web-a2e 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.clangd +5 -0
- package/.mcp.json +12 -0
- package/CLAUDE.md +362 -0
- package/CMakeLists.txt +774 -0
- package/LICENSE +21 -0
- package/README.md +392 -0
- package/build-wasm/generated/roms.cpp +2447 -0
- package/docker-compose.staging.yml +9 -0
- package/docs/basic-rom-disassembly.md +6663 -0
- package/docs/softswitch-comparison.md +273 -0
- package/docs/thunderclock-debug.md +89 -0
- package/examples/cube.bas +72 -0
- package/examples/hello.s +55 -0
- package/examples/scroll.s +140 -0
- package/package.json +18 -0
- package/public/assets/apple-logo-old.png +0 -0
- package/public/assets/apple-logo.png +0 -0
- package/public/assets/drive-closed-light-on.png +0 -0
- package/public/assets/drive-closed.png +0 -0
- package/public/assets/drive-open-light-on.png +0 -0
- package/public/assets/drive-open.png +0 -0
- package/public/audio-worklet.js +82 -0
- package/public/disks/Apple DOS 3.3 January 1983.dsk +0 -0
- package/public/disks/ProDOS 2.4.3.po +0 -0
- package/public/disks/h32mb.2mg +0 -0
- package/public/disks/library.json +26 -0
- package/public/docs/llms/llm-assembler.txt +90 -0
- package/public/docs/llms/llm-basic-program.txt +256 -0
- package/public/docs/llms/llm-disk-drives.txt +72 -0
- package/public/docs/llms/llm-file-explorer.txt +50 -0
- package/public/docs/llms/llm-hard-drives.txt +80 -0
- package/public/docs/llms/llm-main.txt +51 -0
- package/public/docs/llms/llm-slot-configuration.txt +66 -0
- package/public/icons/icon-192.svg +4 -0
- package/public/icons/icon-512.svg +4 -0
- package/public/index.html +661 -0
- package/public/llms.txt +49 -0
- package/public/manifest.json +29 -0
- package/public/shaders/burnin.glsl +22 -0
- package/public/shaders/crt.glsl +706 -0
- package/public/shaders/edge.glsl +109 -0
- package/public/shaders/vertex.glsl +8 -0
- package/public/sw.js +186 -0
- package/roms/341-0027.bin +0 -0
- package/roms/341-0160-A-US-UK.bin +0 -0
- package/roms/341-0160-A.bin +0 -0
- package/roms/342-0273-A-US-UK.bin +0 -0
- package/roms/342-0349-B-C0-FF.bin +0 -0
- package/roms/Apple Mouse Interface Card ROM - 342-0270-C.bin +0 -0
- package/roms/Thunderclock Plus ROM.bin +0 -0
- package/scripts/generate_roms.sh +69 -0
- package/src/bindings/wasm_interface.cpp +1940 -0
- package/src/core/assembler/assembler.cpp +1239 -0
- package/src/core/assembler/assembler.hpp +115 -0
- package/src/core/audio/audio.cpp +160 -0
- package/src/core/audio/audio.hpp +81 -0
- package/src/core/basic/basic_detokenizer.cpp +436 -0
- package/src/core/basic/basic_detokenizer.hpp +41 -0
- package/src/core/basic/basic_tokenizer.cpp +286 -0
- package/src/core/basic/basic_tokenizer.hpp +26 -0
- package/src/core/basic/basic_tokens.hpp +295 -0
- package/src/core/cards/disk2_card.cpp +568 -0
- package/src/core/cards/disk2_card.hpp +316 -0
- package/src/core/cards/expansion_card.hpp +185 -0
- package/src/core/cards/mockingboard/ay8910.cpp +616 -0
- package/src/core/cards/mockingboard/ay8910.hpp +159 -0
- package/src/core/cards/mockingboard/via6522.cpp +530 -0
- package/src/core/cards/mockingboard/via6522.hpp +163 -0
- package/src/core/cards/mockingboard_card.cpp +312 -0
- package/src/core/cards/mockingboard_card.hpp +159 -0
- package/src/core/cards/mouse_card.cpp +654 -0
- package/src/core/cards/mouse_card.hpp +190 -0
- package/src/core/cards/smartport/block_device.cpp +202 -0
- package/src/core/cards/smartport/block_device.hpp +60 -0
- package/src/core/cards/smartport/smartport_card.cpp +603 -0
- package/src/core/cards/smartport/smartport_card.hpp +120 -0
- package/src/core/cards/thunderclock_card.cpp +237 -0
- package/src/core/cards/thunderclock_card.hpp +122 -0
- package/src/core/cpu/cpu6502.cpp +1609 -0
- package/src/core/cpu/cpu6502.hpp +203 -0
- package/src/core/debug/condition_evaluator.cpp +470 -0
- package/src/core/debug/condition_evaluator.hpp +87 -0
- package/src/core/disassembler/disassembler.cpp +552 -0
- package/src/core/disassembler/disassembler.hpp +171 -0
- package/src/core/disk-image/disk_image.hpp +267 -0
- package/src/core/disk-image/dsk_disk_image.cpp +827 -0
- package/src/core/disk-image/dsk_disk_image.hpp +204 -0
- package/src/core/disk-image/gcr_encoding.cpp +147 -0
- package/src/core/disk-image/gcr_encoding.hpp +78 -0
- package/src/core/disk-image/woz_disk_image.cpp +1049 -0
- package/src/core/disk-image/woz_disk_image.hpp +343 -0
- package/src/core/emulator.cpp +2126 -0
- package/src/core/emulator.hpp +434 -0
- package/src/core/filesystem/dos33.cpp +178 -0
- package/src/core/filesystem/dos33.hpp +66 -0
- package/src/core/filesystem/pascal.cpp +262 -0
- package/src/core/filesystem/pascal.hpp +87 -0
- package/src/core/filesystem/prodos.cpp +369 -0
- package/src/core/filesystem/prodos.hpp +119 -0
- package/src/core/input/keyboard.cpp +227 -0
- package/src/core/input/keyboard.hpp +111 -0
- package/src/core/mmu/mmu.cpp +1387 -0
- package/src/core/mmu/mmu.hpp +236 -0
- package/src/core/types.hpp +196 -0
- package/src/core/video/video.cpp +680 -0
- package/src/core/video/video.hpp +156 -0
- package/src/css/assembler-editor.css +1617 -0
- package/src/css/base.css +470 -0
- package/src/css/basic-debugger.css +791 -0
- package/src/css/basic-editor.css +792 -0
- package/src/css/controls.css +783 -0
- package/src/css/cpu-debugger.css +1413 -0
- package/src/css/debug-base.css +160 -0
- package/src/css/debug-windows.css +6455 -0
- package/src/css/disk-drives.css +406 -0
- package/src/css/documentation.css +392 -0
- package/src/css/file-explorer.css +867 -0
- package/src/css/hard-drive.css +180 -0
- package/src/css/layout.css +217 -0
- package/src/css/memory-windows.css +798 -0
- package/src/css/modals.css +510 -0
- package/src/css/monitor.css +425 -0
- package/src/css/release-notes.css +101 -0
- package/src/css/responsive.css +400 -0
- package/src/css/rule-builder.css +340 -0
- package/src/css/save-states.css +201 -0
- package/src/css/settings-windows.css +1231 -0
- package/src/css/window-switcher.css +150 -0
- package/src/js/agent/agent-manager.js +643 -0
- package/src/js/agent/agent-tools.js +293 -0
- package/src/js/agent/agent-version-tools.js +131 -0
- package/src/js/agent/assembler-tools.js +357 -0
- package/src/js/agent/basic-program-tools.js +894 -0
- package/src/js/agent/disk-tools.js +417 -0
- package/src/js/agent/file-explorer-tools.js +269 -0
- package/src/js/agent/index.js +13 -0
- package/src/js/agent/main-tools.js +222 -0
- package/src/js/agent/slot-tools.js +303 -0
- package/src/js/agent/smartport-tools.js +257 -0
- package/src/js/agent/window-tools.js +80 -0
- package/src/js/audio/audio-driver.js +417 -0
- package/src/js/audio/audio-worklet.js +85 -0
- package/src/js/audio/index.js +8 -0
- package/src/js/config/default-layout.js +34 -0
- package/src/js/config/version.js +8 -0
- package/src/js/data/apple2-rom-routines.js +577 -0
- package/src/js/debug/assembler-editor-window.js +2993 -0
- package/src/js/debug/basic-breakpoint-manager.js +529 -0
- package/src/js/debug/basic-program-parser.js +436 -0
- package/src/js/debug/basic-program-window.js +2594 -0
- package/src/js/debug/basic-variable-inspector.js +447 -0
- package/src/js/debug/breakpoint-manager.js +472 -0
- package/src/js/debug/cpu-debugger-window.js +2396 -0
- package/src/js/debug/index.js +22 -0
- package/src/js/debug/label-manager.js +238 -0
- package/src/js/debug/memory-browser-window.js +416 -0
- package/src/js/debug/memory-heat-map-window.js +481 -0
- package/src/js/debug/memory-map-window.js +206 -0
- package/src/js/debug/mockingboard-window.js +882 -0
- package/src/js/debug/mouse-card-window.js +355 -0
- package/src/js/debug/rule-builder-window.js +648 -0
- package/src/js/debug/soft-switch-window.js +458 -0
- package/src/js/debug/stack-viewer-window.js +221 -0
- package/src/js/debug/symbols.js +416 -0
- package/src/js/debug/trace-panel.js +291 -0
- package/src/js/debug/zero-page-watch-window.js +297 -0
- package/src/js/disk-manager/disk-drives-window.js +212 -0
- package/src/js/disk-manager/disk-operations.js +284 -0
- package/src/js/disk-manager/disk-persistence.js +301 -0
- package/src/js/disk-manager/disk-surface-renderer.js +388 -0
- package/src/js/disk-manager/drive-sounds.js +139 -0
- package/src/js/disk-manager/hard-drive-manager.js +481 -0
- package/src/js/disk-manager/hard-drive-persistence.js +187 -0
- package/src/js/disk-manager/hard-drive-window.js +57 -0
- package/src/js/disk-manager/index.js +890 -0
- package/src/js/display/display-settings-window.js +383 -0
- package/src/js/display/index.js +10 -0
- package/src/js/display/screen-window.js +342 -0
- package/src/js/display/webgl-renderer.js +705 -0
- package/src/js/file-explorer/disassembler.js +574 -0
- package/src/js/file-explorer/dos33.js +266 -0
- package/src/js/file-explorer/file-viewer.js +359 -0
- package/src/js/file-explorer/index.js +1261 -0
- package/src/js/file-explorer/prodos.js +549 -0
- package/src/js/file-explorer/utils.js +67 -0
- package/src/js/help/documentation-window.js +1096 -0
- package/src/js/help/index.js +10 -0
- package/src/js/help/release-notes-window.js +85 -0
- package/src/js/help/release-notes.js +612 -0
- package/src/js/input/gamepad-handler.js +176 -0
- package/src/js/input/index.js +12 -0
- package/src/js/input/input-handler.js +396 -0
- package/src/js/input/joystick-window.js +404 -0
- package/src/js/input/mouse-handler.js +99 -0
- package/src/js/input/text-selection.js +462 -0
- package/src/js/main.js +653 -0
- package/src/js/state/index.js +15 -0
- package/src/js/state/save-states-window.js +393 -0
- package/src/js/state/state-manager.js +409 -0
- package/src/js/state/state-persistence.js +218 -0
- package/src/js/ui/confirm.js +43 -0
- package/src/js/ui/disk-drive-positioner.js +347 -0
- package/src/js/ui/reminder-controller.js +129 -0
- package/src/js/ui/slot-configuration-window.js +560 -0
- package/src/js/ui/theme-manager.js +61 -0
- package/src/js/ui/toast.js +44 -0
- package/src/js/ui/ui-controller.js +897 -0
- package/src/js/ui/window-switcher.js +275 -0
- package/src/js/utils/basic-autocomplete.js +832 -0
- package/src/js/utils/basic-highlighting.js +473 -0
- package/src/js/utils/basic-tokenizer.js +153 -0
- package/src/js/utils/basic-tokens.js +117 -0
- package/src/js/utils/constants.js +28 -0
- package/src/js/utils/indexeddb-helper.js +225 -0
- package/src/js/utils/merlin-editor-support.js +905 -0
- package/src/js/utils/merlin-highlighting.js +551 -0
- package/src/js/utils/storage.js +125 -0
- package/src/js/utils/string-utils.js +19 -0
- package/src/js/utils/wasm-memory.js +54 -0
- package/src/js/windows/base-window.js +690 -0
- package/src/js/windows/index.js +9 -0
- package/src/js/windows/window-manager.js +375 -0
- package/tests/catch2/catch.hpp +17976 -0
- package/tests/common/basic_program_builder.cpp +119 -0
- package/tests/common/basic_program_builder.hpp +209 -0
- package/tests/common/disk_image_builder.cpp +444 -0
- package/tests/common/disk_image_builder.hpp +141 -0
- package/tests/common/test_helpers.hpp +118 -0
- package/tests/gcr/gcr-test.cpp +142 -0
- package/tests/integration/check-rom.js +70 -0
- package/tests/integration/compare-boot.js +239 -0
- package/tests/integration/crash-trace.js +102 -0
- package/tests/integration/disk-boot-test.js +264 -0
- package/tests/integration/memory-crash.js +108 -0
- package/tests/integration/nibble-read-test.js +249 -0
- package/tests/integration/phase-test.js +159 -0
- package/tests/integration/test_emulator.cpp +291 -0
- package/tests/integration/test_emulator_basic.cpp +91 -0
- package/tests/integration/test_emulator_debug.cpp +344 -0
- package/tests/integration/test_emulator_disk.cpp +153 -0
- package/tests/integration/test_emulator_state.cpp +163 -0
- package/tests/klaus/6502_functional_test.bin +0 -0
- package/tests/klaus/65C02_extended_opcodes_test.bin +0 -0
- package/tests/klaus/klaus_6502_test.cpp +184 -0
- package/tests/klaus/klaus_65c02_test.cpp +197 -0
- package/tests/thunderclock/thunderclock_mmu_test.cpp +304 -0
- package/tests/thunderclock/thunderclock_test.cpp +550 -0
- package/tests/unit/test_assembler.cpp +521 -0
- package/tests/unit/test_audio.cpp +196 -0
- package/tests/unit/test_ay8910.cpp +311 -0
- package/tests/unit/test_basic_detokenizer.cpp +265 -0
- package/tests/unit/test_basic_tokenizer.cpp +382 -0
- package/tests/unit/test_block_device.cpp +259 -0
- package/tests/unit/test_condition_evaluator.cpp +219 -0
- package/tests/unit/test_cpu6502.cpp +1301 -0
- package/tests/unit/test_cpu_addressing.cpp +361 -0
- package/tests/unit/test_cpu_cycle_counts.cpp +409 -0
- package/tests/unit/test_cpu_decimal.cpp +166 -0
- package/tests/unit/test_cpu_interrupts.cpp +285 -0
- package/tests/unit/test_disassembler.cpp +323 -0
- package/tests/unit/test_disk2_card.cpp +330 -0
- package/tests/unit/test_dos33.cpp +273 -0
- package/tests/unit/test_dsk_disk_image.cpp +315 -0
- package/tests/unit/test_expansion_card.cpp +178 -0
- package/tests/unit/test_gcr_encoding.cpp +232 -0
- package/tests/unit/test_keyboard.cpp +262 -0
- package/tests/unit/test_mmu.cpp +555 -0
- package/tests/unit/test_mmu_slots.cpp +323 -0
- package/tests/unit/test_mockingboard.cpp +352 -0
- package/tests/unit/test_mouse_card.cpp +386 -0
- package/tests/unit/test_pascal.cpp +248 -0
- package/tests/unit/test_prodos.cpp +259 -0
- package/tests/unit/test_smartport_card.cpp +321 -0
- package/tests/unit/test_thunderclock.cpp +354 -0
- package/tests/unit/test_via6522.cpp +323 -0
- package/tests/unit/test_video.cpp +319 -0
- package/tests/unit/test_woz_disk_image.cpp +257 -0
- package/vite.config.js +96 -0
- package/wiki/AI-Agent.md +372 -0
- package/wiki/Architecture-Overview.md +303 -0
- package/wiki/Audio-System.md +449 -0
- package/wiki/CPU-Emulation.md +477 -0
- package/wiki/Debugger.md +516 -0
- package/wiki/Disk-Drives.md +161 -0
- package/wiki/Disk-System-Internals.md +547 -0
- package/wiki/Display-Settings.md +88 -0
- package/wiki/Expansion-Slots.md +187 -0
- package/wiki/File-Explorer.md +259 -0
- package/wiki/Getting-Started.md +156 -0
- package/wiki/Home.md +69 -0
- package/wiki/Input-Devices.md +183 -0
- package/wiki/Keyboard-Shortcuts.md +158 -0
- package/wiki/Memory-System.md +364 -0
- package/wiki/Save-States.md +172 -0
- package/wiki/Video-Rendering.md +658 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* test_disk2_card.cpp - Unit tests for Disk2Card
|
|
3
|
+
*
|
|
4
|
+
* Tests the Disk II controller card implementation including:
|
|
5
|
+
* - Construction with ROM data
|
|
6
|
+
* - ROM read access
|
|
7
|
+
* - Motor control (on/off)
|
|
8
|
+
* - Drive selection
|
|
9
|
+
* - Q6/Q7 latch states
|
|
10
|
+
* - Phase control for head stepping
|
|
11
|
+
* - Disk insert/eject operations
|
|
12
|
+
* - Card metadata (name, preferred slot)
|
|
13
|
+
* - State serialization round-trip
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
#define CATCH_CONFIG_MAIN
|
|
17
|
+
#include "catch.hpp"
|
|
18
|
+
|
|
19
|
+
#include "disk2_card.hpp"
|
|
20
|
+
#include "roms.cpp"
|
|
21
|
+
|
|
22
|
+
#include <cstring>
|
|
23
|
+
#include <vector>
|
|
24
|
+
|
|
25
|
+
using namespace a2e;
|
|
26
|
+
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Construction
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
TEST_CASE("Disk2Card constructor with ROM loads ROM data", "[disk2]") {
|
|
32
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
33
|
+
|
|
34
|
+
// The ROM should be 256 bytes; every byte should match the source data
|
|
35
|
+
for (size_t i = 0; i < roms::ROM_DISK2_SIZE; ++i) {
|
|
36
|
+
REQUIRE(card.readROM(static_cast<uint8_t>(i)) == roms::ROM_DISK2[i]);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
TEST_CASE("Disk2Card default constructor creates valid card", "[disk2]") {
|
|
41
|
+
Disk2Card card;
|
|
42
|
+
// Should still be constructable; ROM content may be zeroed
|
|
43
|
+
REQUIRE(card.getName() != nullptr);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// readROM returns bytes from loaded ROM
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
TEST_CASE("Disk2Card readROM returns ROM bytes after loadROM", "[disk2]") {
|
|
51
|
+
Disk2Card card;
|
|
52
|
+
card.loadROM(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
53
|
+
|
|
54
|
+
REQUIRE(card.readROM(0x00) == roms::ROM_DISK2[0]);
|
|
55
|
+
REQUIRE(card.readROM(0x7F) == roms::ROM_DISK2[0x7F]);
|
|
56
|
+
REQUIRE(card.readROM(0xFF) == roms::ROM_DISK2[0xFF]);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// reset() clears controller state
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
TEST_CASE("Disk2Card reset clears state", "[disk2]") {
|
|
64
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
65
|
+
|
|
66
|
+
// Turn motor on and select drive 2
|
|
67
|
+
card.readIO(0x09); // Motor on
|
|
68
|
+
card.readIO(0x0B); // Drive 2
|
|
69
|
+
|
|
70
|
+
REQUIRE(card.isMotorOn());
|
|
71
|
+
REQUIRE(card.getSelectedDrive() == 1);
|
|
72
|
+
|
|
73
|
+
card.reset();
|
|
74
|
+
|
|
75
|
+
REQUIRE_FALSE(card.isMotorOn());
|
|
76
|
+
REQUIRE(card.getSelectedDrive() == 0);
|
|
77
|
+
REQUIRE(card.getQ6() == false);
|
|
78
|
+
REQUIRE(card.getQ7() == false);
|
|
79
|
+
REQUIRE(card.getPhaseStates() == 0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// Motor on/off via readIO
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
TEST_CASE("Disk2Card motor control via I/O offsets", "[disk2]") {
|
|
87
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
88
|
+
|
|
89
|
+
SECTION("Motor starts off") {
|
|
90
|
+
REQUIRE_FALSE(card.isMotorOn());
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
SECTION("Offset 0x09 turns motor on") {
|
|
94
|
+
card.readIO(0x09);
|
|
95
|
+
REQUIRE(card.isMotorOn());
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
SECTION("Offset 0x08 turns motor off") {
|
|
99
|
+
card.readIO(0x09); // on
|
|
100
|
+
REQUIRE(card.isMotorOn());
|
|
101
|
+
card.readIO(0x08); // off
|
|
102
|
+
// Motor may not turn off immediately (delay), but the off request is latched
|
|
103
|
+
// After enough update cycles, motor will be off. For the immediate test,
|
|
104
|
+
// just verify the toggle was accepted without error.
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Drive select
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
TEST_CASE("Disk2Card drive selection via I/O offsets", "[disk2]") {
|
|
113
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
114
|
+
|
|
115
|
+
SECTION("Default drive is 0 (drive 1)") {
|
|
116
|
+
REQUIRE(card.getSelectedDrive() == 0);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
SECTION("Offset 0x0B selects drive 2 (index 1)") {
|
|
120
|
+
card.readIO(0x0B);
|
|
121
|
+
REQUIRE(card.getSelectedDrive() == 1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
SECTION("Offset 0x0A selects drive 1 (index 0)") {
|
|
125
|
+
card.readIO(0x0B); // select drive 2 first
|
|
126
|
+
card.readIO(0x0A); // back to drive 1
|
|
127
|
+
REQUIRE(card.getSelectedDrive() == 0);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
// Q6/Q7 latches
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
TEST_CASE("Disk2Card Q6 and Q7 latch control", "[disk2]") {
|
|
136
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
137
|
+
|
|
138
|
+
SECTION("Q6 and Q7 start low") {
|
|
139
|
+
REQUIRE(card.getQ6() == false);
|
|
140
|
+
REQUIRE(card.getQ7() == false);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
SECTION("Offset 0x0C sets Q6 low") {
|
|
144
|
+
card.readIO(0x0D); // Q6H first
|
|
145
|
+
REQUIRE(card.getQ6() == true);
|
|
146
|
+
card.readIO(0x0C); // Q6L
|
|
147
|
+
REQUIRE(card.getQ6() == false);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
SECTION("Offset 0x0D sets Q6 high") {
|
|
151
|
+
card.readIO(0x0D);
|
|
152
|
+
REQUIRE(card.getQ6() == true);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
SECTION("Offset 0x0E sets Q7 low") {
|
|
156
|
+
card.readIO(0x0F); // Q7H first
|
|
157
|
+
REQUIRE(card.getQ7() == true);
|
|
158
|
+
card.readIO(0x0E); // Q7L
|
|
159
|
+
REQUIRE(card.getQ7() == false);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
SECTION("Offset 0x0F sets Q7 high") {
|
|
163
|
+
card.readIO(0x0F);
|
|
164
|
+
REQUIRE(card.getQ7() == true);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
// Phase control
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
TEST_CASE("Disk2Card phase control via I/O offsets", "[disk2]") {
|
|
173
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
174
|
+
|
|
175
|
+
SECTION("All phases off initially") {
|
|
176
|
+
REQUIRE(card.getPhaseStates() == 0x00);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
SECTION("Phase 0 on (offset 0x01)") {
|
|
180
|
+
card.readIO(0x01);
|
|
181
|
+
REQUIRE((card.getPhaseStates() & 0x01) != 0);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
SECTION("Phase 0 off (offset 0x00)") {
|
|
185
|
+
card.readIO(0x01); // on
|
|
186
|
+
card.readIO(0x00); // off
|
|
187
|
+
REQUIRE((card.getPhaseStates() & 0x01) == 0);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
SECTION("Phase 1 on (offset 0x03)") {
|
|
191
|
+
card.readIO(0x03);
|
|
192
|
+
REQUIRE((card.getPhaseStates() & 0x02) != 0);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
SECTION("Phase 1 off (offset 0x02)") {
|
|
196
|
+
card.readIO(0x03);
|
|
197
|
+
card.readIO(0x02);
|
|
198
|
+
REQUIRE((card.getPhaseStates() & 0x02) == 0);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
SECTION("Phase 2 on (offset 0x05)") {
|
|
202
|
+
card.readIO(0x05);
|
|
203
|
+
REQUIRE((card.getPhaseStates() & 0x04) != 0);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
SECTION("Phase 2 off (offset 0x04)") {
|
|
207
|
+
card.readIO(0x05);
|
|
208
|
+
card.readIO(0x04);
|
|
209
|
+
REQUIRE((card.getPhaseStates() & 0x04) == 0);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
SECTION("Phase 3 on (offset 0x07)") {
|
|
213
|
+
card.readIO(0x07);
|
|
214
|
+
REQUIRE((card.getPhaseStates() & 0x08) != 0);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
SECTION("Phase 3 off (offset 0x06)") {
|
|
218
|
+
card.readIO(0x07);
|
|
219
|
+
card.readIO(0x06);
|
|
220
|
+
REQUIRE((card.getPhaseStates() & 0x08) == 0);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
SECTION("Multiple phases can be on simultaneously") {
|
|
224
|
+
card.readIO(0x01); // phase 0 on
|
|
225
|
+
card.readIO(0x03); // phase 1 on
|
|
226
|
+
REQUIRE((card.getPhaseStates() & 0x03) == 0x03);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ---------------------------------------------------------------------------
|
|
231
|
+
// Disk insert / eject
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
|
|
234
|
+
TEST_CASE("Disk2Card hasDisk is false by default", "[disk2]") {
|
|
235
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
236
|
+
REQUIRE_FALSE(card.hasDisk(0));
|
|
237
|
+
REQUIRE_FALSE(card.hasDisk(1));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
TEST_CASE("Disk2Card insertDisk with DSK data makes hasDisk true", "[disk2]") {
|
|
241
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
242
|
+
|
|
243
|
+
// Create a minimal valid DSK image (143360 bytes, all zeros)
|
|
244
|
+
std::vector<uint8_t> dskData(143360, 0x00);
|
|
245
|
+
|
|
246
|
+
bool result = card.insertDisk(0, dskData.data(), dskData.size(), "test.dsk");
|
|
247
|
+
REQUIRE(result);
|
|
248
|
+
REQUIRE(card.hasDisk(0));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
TEST_CASE("Disk2Card insertDisk into drive 2", "[disk2]") {
|
|
252
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
253
|
+
|
|
254
|
+
std::vector<uint8_t> dskData(143360, 0x00);
|
|
255
|
+
bool result = card.insertDisk(1, dskData.data(), dskData.size(), "test.dsk");
|
|
256
|
+
REQUIRE(result);
|
|
257
|
+
REQUIRE(card.hasDisk(1));
|
|
258
|
+
REQUIRE_FALSE(card.hasDisk(0)); // drive 1 still empty
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
TEST_CASE("Disk2Card ejectDisk clears the disk", "[disk2]") {
|
|
262
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
263
|
+
|
|
264
|
+
std::vector<uint8_t> dskData(143360, 0x00);
|
|
265
|
+
card.insertDisk(0, dskData.data(), dskData.size(), "test.dsk");
|
|
266
|
+
REQUIRE(card.hasDisk(0));
|
|
267
|
+
|
|
268
|
+
card.ejectDisk(0);
|
|
269
|
+
REQUIRE_FALSE(card.hasDisk(0));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
// Card metadata
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
|
|
276
|
+
TEST_CASE("Disk2Card getName returns Disk II", "[disk2]") {
|
|
277
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
278
|
+
REQUIRE(std::string(card.getName()) == "Disk II");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
TEST_CASE("Disk2Card getPreferredSlot returns 6", "[disk2]") {
|
|
282
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
283
|
+
REQUIRE(card.getPreferredSlot() == 6);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
TEST_CASE("Disk2Card hasROM returns true", "[disk2]") {
|
|
287
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
288
|
+
REQUIRE(card.hasROM());
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
TEST_CASE("Disk2Card hasExpansionROM returns false", "[disk2]") {
|
|
292
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
293
|
+
REQUIRE_FALSE(card.hasExpansionROM());
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ---------------------------------------------------------------------------
|
|
297
|
+
// Serialization round-trip
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
|
|
300
|
+
TEST_CASE("Disk2Card getStateSize is greater than zero", "[disk2]") {
|
|
301
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
302
|
+
REQUIRE(card.getStateSize() > 0);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
TEST_CASE("Disk2Card serialize/deserialize round-trip preserves state", "[disk2]") {
|
|
306
|
+
Disk2Card card1(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
307
|
+
|
|
308
|
+
// Set up some state
|
|
309
|
+
card1.readIO(0x09); // motor on
|
|
310
|
+
card1.readIO(0x0B); // drive 2
|
|
311
|
+
card1.readIO(0x0D); // Q6H
|
|
312
|
+
card1.readIO(0x01); // phase 0 on
|
|
313
|
+
|
|
314
|
+
size_t stateSize = card1.getStateSize();
|
|
315
|
+
std::vector<uint8_t> buffer(stateSize);
|
|
316
|
+
|
|
317
|
+
size_t written = card1.serialize(buffer.data(), buffer.size());
|
|
318
|
+
REQUIRE(written > 0);
|
|
319
|
+
REQUIRE(written <= stateSize);
|
|
320
|
+
|
|
321
|
+
Disk2Card card2(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
322
|
+
size_t consumed = card2.deserialize(buffer.data(), written);
|
|
323
|
+
REQUIRE(consumed > 0);
|
|
324
|
+
|
|
325
|
+
// Verify key state was preserved
|
|
326
|
+
REQUIRE(card2.getSelectedDrive() == card1.getSelectedDrive());
|
|
327
|
+
REQUIRE(card2.getQ6() == card1.getQ6());
|
|
328
|
+
REQUIRE(card2.getQ7() == card1.getQ7());
|
|
329
|
+
REQUIRE(card2.getPhaseStates() == card1.getPhaseStates());
|
|
330
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* test_dos33.cpp - Unit tests for DOS 3.3 filesystem parser
|
|
3
|
+
*
|
|
4
|
+
* Tests the DOS 3.3 filesystem reader including:
|
|
5
|
+
* - Format detection (isDOS33)
|
|
6
|
+
* - Catalog reading
|
|
7
|
+
* - File data retrieval
|
|
8
|
+
* - Binary file header extraction
|
|
9
|
+
* - Multiple files in catalog
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
#define CATCH_CONFIG_MAIN
|
|
13
|
+
#include "catch.hpp"
|
|
14
|
+
|
|
15
|
+
#include "dos33.hpp"
|
|
16
|
+
#include "disk_image_builder.hpp"
|
|
17
|
+
|
|
18
|
+
#include <array>
|
|
19
|
+
#include <cstring>
|
|
20
|
+
#include <vector>
|
|
21
|
+
|
|
22
|
+
using namespace a2e;
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// isDOS33 - Format detection
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
TEST_CASE("isDOS33 returns true for valid DOS 3.3 image", "[dos33][detection]") {
|
|
29
|
+
test::DOS33DiskBuilder builder;
|
|
30
|
+
REQUIRE(DOS33::isDOS33(builder.data(), builder.size()));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
TEST_CASE("isDOS33 returns false for zeroed data", "[dos33][detection]") {
|
|
34
|
+
std::vector<uint8_t> zeroed(143360, 0x00);
|
|
35
|
+
REQUIRE_FALSE(DOS33::isDOS33(zeroed.data(), zeroed.size()));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
TEST_CASE("isDOS33 returns false for too-small data", "[dos33][detection]") {
|
|
39
|
+
std::vector<uint8_t> small(1024, 0x00);
|
|
40
|
+
REQUIRE_FALSE(DOS33::isDOS33(small.data(), small.size()));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
TEST_CASE("isDOS33 returns false for null pointer", "[dos33][detection]") {
|
|
44
|
+
REQUIRE_FALSE(DOS33::isDOS33(nullptr, 0));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// readCatalog - Catalog reading
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
TEST_CASE("readCatalog returns correct file count after addFile", "[dos33][catalog]") {
|
|
52
|
+
test::DOS33DiskBuilder builder;
|
|
53
|
+
|
|
54
|
+
const uint8_t fileData[] = "HELLO WORLD";
|
|
55
|
+
builder.addFile("TESTFILE", 0x00, fileData, sizeof(fileData));
|
|
56
|
+
|
|
57
|
+
DOS33CatalogEntry entries[32];
|
|
58
|
+
int count = DOS33::readCatalog(builder.data(), builder.size(), entries, 32);
|
|
59
|
+
REQUIRE(count == 1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
TEST_CASE("Catalog entry has correct filename", "[dos33][catalog]") {
|
|
63
|
+
test::DOS33DiskBuilder builder;
|
|
64
|
+
|
|
65
|
+
const uint8_t fileData[] = "CONTENT";
|
|
66
|
+
builder.addFile("MYFILE", 0x00, fileData, sizeof(fileData));
|
|
67
|
+
|
|
68
|
+
DOS33CatalogEntry entries[32];
|
|
69
|
+
int count = DOS33::readCatalog(builder.data(), builder.size(), entries, 32);
|
|
70
|
+
REQUIRE(count == 1);
|
|
71
|
+
REQUIRE(std::string(entries[0].filename) == "MYFILE");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
TEST_CASE("Catalog entry has correct file type for text file", "[dos33][catalog]") {
|
|
75
|
+
test::DOS33DiskBuilder builder;
|
|
76
|
+
|
|
77
|
+
const uint8_t fileData[] = "TEXT DATA";
|
|
78
|
+
builder.addFile("README", 0x00, fileData, sizeof(fileData)); // 0x00 = Text
|
|
79
|
+
|
|
80
|
+
DOS33CatalogEntry entries[32];
|
|
81
|
+
int count = DOS33::readCatalog(builder.data(), builder.size(), entries, 32);
|
|
82
|
+
REQUIRE(count == 1);
|
|
83
|
+
CHECK(entries[0].fileType == 0x00);
|
|
84
|
+
CHECK(std::string(entries[0].fileTypeName) == "T");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
TEST_CASE("Catalog entry has correct file type for binary file", "[dos33][catalog]") {
|
|
88
|
+
test::DOS33DiskBuilder builder;
|
|
89
|
+
|
|
90
|
+
const uint8_t fileData[] = {0x00, 0x20, 0x05, 0x00, 0xEA, 0xEA, 0xEA, 0x60, 0x00};
|
|
91
|
+
builder.addFile("BINFILE", 0x04, fileData, sizeof(fileData)); // 0x04 = Binary
|
|
92
|
+
|
|
93
|
+
DOS33CatalogEntry entries[32];
|
|
94
|
+
int count = DOS33::readCatalog(builder.data(), builder.size(), entries, 32);
|
|
95
|
+
REQUIRE(count == 1);
|
|
96
|
+
CHECK(entries[0].fileType == 0x04);
|
|
97
|
+
CHECK(std::string(entries[0].fileTypeName) == "B");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
TEST_CASE("Catalog entry locked status is correctly set", "[dos33][catalog]") {
|
|
101
|
+
test::DOS33DiskBuilder builder;
|
|
102
|
+
|
|
103
|
+
const uint8_t data1[] = "UNLOCKED";
|
|
104
|
+
const uint8_t data2[] = "LOCKED";
|
|
105
|
+
builder.addFile("FREE", 0x00, data1, sizeof(data1), false);
|
|
106
|
+
builder.addFile("PROT", 0x00, data2, sizeof(data2), true);
|
|
107
|
+
|
|
108
|
+
DOS33CatalogEntry entries[32];
|
|
109
|
+
int count = DOS33::readCatalog(builder.data(), builder.size(), entries, 32);
|
|
110
|
+
REQUIRE(count == 2);
|
|
111
|
+
|
|
112
|
+
// Find each entry by name
|
|
113
|
+
bool foundFree = false, foundProt = false;
|
|
114
|
+
for (int i = 0; i < count; i++) {
|
|
115
|
+
if (std::string(entries[i].filename) == "FREE") {
|
|
116
|
+
CHECK_FALSE(entries[i].isLocked);
|
|
117
|
+
foundFree = true;
|
|
118
|
+
}
|
|
119
|
+
if (std::string(entries[i].filename) == "PROT") {
|
|
120
|
+
CHECK(entries[i].isLocked);
|
|
121
|
+
foundProt = true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
REQUIRE(foundFree);
|
|
125
|
+
REQUIRE(foundProt);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
TEST_CASE("readCatalog with empty disk returns zero", "[dos33][catalog]") {
|
|
129
|
+
test::DOS33DiskBuilder builder;
|
|
130
|
+
|
|
131
|
+
DOS33CatalogEntry entries[32];
|
|
132
|
+
int count = DOS33::readCatalog(builder.data(), builder.size(), entries, 32);
|
|
133
|
+
REQUIRE(count == 0);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// Multiple files in catalog
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
TEST_CASE("Multiple files in catalog are all enumerated", "[dos33][catalog]") {
|
|
141
|
+
test::DOS33DiskBuilder builder;
|
|
142
|
+
|
|
143
|
+
const uint8_t data1[] = "FILE ONE DATA";
|
|
144
|
+
const uint8_t data2[] = "FILE TWO DATA";
|
|
145
|
+
const uint8_t data3[] = "FILE THREE DATA";
|
|
146
|
+
|
|
147
|
+
builder.addFile("FILE1", 0x00, data1, sizeof(data1));
|
|
148
|
+
builder.addFile("FILE2", 0x02, data2, sizeof(data2)); // 0x02 = Applesoft
|
|
149
|
+
builder.addFile("FILE3", 0x04, data3, sizeof(data3)); // 0x04 = Binary
|
|
150
|
+
|
|
151
|
+
DOS33CatalogEntry entries[32];
|
|
152
|
+
int count = DOS33::readCatalog(builder.data(), builder.size(), entries, 32);
|
|
153
|
+
REQUIRE(count == 3);
|
|
154
|
+
|
|
155
|
+
// Verify filenames are present (order may vary)
|
|
156
|
+
bool found1 = false, found2 = false, found3 = false;
|
|
157
|
+
for (int i = 0; i < count; i++) {
|
|
158
|
+
std::string name(entries[i].filename);
|
|
159
|
+
if (name == "FILE1") found1 = true;
|
|
160
|
+
if (name == "FILE2") found2 = true;
|
|
161
|
+
if (name == "FILE3") found3 = true;
|
|
162
|
+
}
|
|
163
|
+
CHECK(found1);
|
|
164
|
+
CHECK(found2);
|
|
165
|
+
CHECK(found3);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
TEST_CASE("Catalog entries have non-zero sector count", "[dos33][catalog]") {
|
|
169
|
+
test::DOS33DiskBuilder builder;
|
|
170
|
+
|
|
171
|
+
const uint8_t fileData[] = "SOME DATA CONTENT";
|
|
172
|
+
builder.addFile("HASDATA", 0x00, fileData, sizeof(fileData));
|
|
173
|
+
|
|
174
|
+
DOS33CatalogEntry entries[32];
|
|
175
|
+
int count = DOS33::readCatalog(builder.data(), builder.size(), entries, 32);
|
|
176
|
+
REQUIRE(count == 1);
|
|
177
|
+
CHECK(entries[0].sectorCount > 0);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// readFile - File data retrieval
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
TEST_CASE("readFile retrieves file data correctly", "[dos33][readFile]") {
|
|
185
|
+
test::DOS33DiskBuilder builder;
|
|
186
|
+
|
|
187
|
+
// Create a file with known content
|
|
188
|
+
std::vector<uint8_t> fileData(64);
|
|
189
|
+
for (int i = 0; i < 64; i++) {
|
|
190
|
+
fileData[i] = static_cast<uint8_t>(i);
|
|
191
|
+
}
|
|
192
|
+
builder.addFile("SEQDATA", 0x00, fileData.data(), static_cast<int>(fileData.size()));
|
|
193
|
+
|
|
194
|
+
// Read catalog to get the file's first track/sector
|
|
195
|
+
DOS33CatalogEntry entries[32];
|
|
196
|
+
int count = DOS33::readCatalog(builder.data(), builder.size(), entries, 32);
|
|
197
|
+
REQUIRE(count == 1);
|
|
198
|
+
|
|
199
|
+
// Read the file data
|
|
200
|
+
std::vector<uint8_t> outBuf(4096, 0);
|
|
201
|
+
int bytesRead = DOS33::readFile(builder.data(), builder.size(),
|
|
202
|
+
entries[0].firstTrack, entries[0].firstSector,
|
|
203
|
+
outBuf.data(), static_cast<int>(outBuf.size()));
|
|
204
|
+
REQUIRE(bytesRead > 0);
|
|
205
|
+
|
|
206
|
+
// Verify the data is present in the output
|
|
207
|
+
// The raw file data starts from the first data sector
|
|
208
|
+
bool found = false;
|
|
209
|
+
for (int offset = 0; offset <= bytesRead - 64; offset++) {
|
|
210
|
+
if (memcmp(&outBuf[offset], fileData.data(), 64) == 0) {
|
|
211
|
+
found = true;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
CHECK(found);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
TEST_CASE("readFile returns zero for invalid track/sector", "[dos33][readFile]") {
|
|
219
|
+
test::DOS33DiskBuilder builder;
|
|
220
|
+
|
|
221
|
+
std::vector<uint8_t> outBuf(4096, 0);
|
|
222
|
+
int bytesRead = DOS33::readFile(builder.data(), builder.size(),
|
|
223
|
+
0, 0, outBuf.data(),
|
|
224
|
+
static_cast<int>(outBuf.size()));
|
|
225
|
+
CHECK(bytesRead == 0);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ---------------------------------------------------------------------------
|
|
229
|
+
// getBinaryFileInfo - Binary file header extraction
|
|
230
|
+
// ---------------------------------------------------------------------------
|
|
231
|
+
|
|
232
|
+
TEST_CASE("getBinaryFileInfo extracts address and length from binary header", "[dos33][binaryInfo]") {
|
|
233
|
+
// Binary file format: first 4 bytes are [addrLo, addrHi, lenLo, lenHi]
|
|
234
|
+
uint8_t binaryFile[] = {
|
|
235
|
+
0x00, 0x20, // Load address = $2000
|
|
236
|
+
0x05, 0x00, // Length = 5 bytes
|
|
237
|
+
0xEA, 0xEA, 0xEA, 0xEA, 0x60 // NOP NOP NOP NOP RTS
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
uint16_t address = 0, length = 0;
|
|
241
|
+
bool result = DOS33::getBinaryFileInfo(binaryFile, sizeof(binaryFile),
|
|
242
|
+
&address, &length);
|
|
243
|
+
REQUIRE(result);
|
|
244
|
+
CHECK(address == 0x2000);
|
|
245
|
+
CHECK(length == 5);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
TEST_CASE("getBinaryFileInfo returns false for too-small data", "[dos33][binaryInfo]") {
|
|
249
|
+
uint8_t tinyData[] = {0x00, 0x20};
|
|
250
|
+
|
|
251
|
+
uint16_t address = 0, length = 0;
|
|
252
|
+
bool result = DOS33::getBinaryFileInfo(tinyData, sizeof(tinyData),
|
|
253
|
+
&address, &length);
|
|
254
|
+
CHECK_FALSE(result);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
TEST_CASE("getBinaryFileInfo with various load addresses", "[dos33][binaryInfo]") {
|
|
258
|
+
SECTION("Zero page address") {
|
|
259
|
+
uint8_t data[] = {0x00, 0x00, 0x10, 0x00, 0x00};
|
|
260
|
+
uint16_t addr, len;
|
|
261
|
+
REQUIRE(DOS33::getBinaryFileInfo(data, sizeof(data), &addr, &len));
|
|
262
|
+
CHECK(addr == 0x0000);
|
|
263
|
+
CHECK(len == 0x0010);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
SECTION("High memory address") {
|
|
267
|
+
uint8_t data[] = {0x00, 0xBF, 0x00, 0x01, 0x00};
|
|
268
|
+
uint16_t addr, len;
|
|
269
|
+
REQUIRE(DOS33::getBinaryFileInfo(data, sizeof(data), &addr, &len));
|
|
270
|
+
CHECK(addr == 0xBF00);
|
|
271
|
+
CHECK(len == 0x0100);
|
|
272
|
+
}
|
|
273
|
+
}
|