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,315 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* test_dsk_disk_image.cpp - Unit tests for DskDiskImage
|
|
3
|
+
*
|
|
4
|
+
* Tests the DSK/DO/PO disk image format implementation including:
|
|
5
|
+
* - Loading with .dsk and .po filenames
|
|
6
|
+
* - Format detection from filename
|
|
7
|
+
* - Track count and geometry
|
|
8
|
+
* - Initial track position
|
|
9
|
+
* - Head positioning via setPhase / getQuarterTrack
|
|
10
|
+
* - Nibble reading after load
|
|
11
|
+
* - Sector data access
|
|
12
|
+
* - Modification tracking
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
#define CATCH_CONFIG_MAIN
|
|
16
|
+
#include "catch.hpp"
|
|
17
|
+
|
|
18
|
+
#include "dsk_disk_image.hpp"
|
|
19
|
+
|
|
20
|
+
#include <cstring>
|
|
21
|
+
#include <vector>
|
|
22
|
+
|
|
23
|
+
using namespace a2e;
|
|
24
|
+
|
|
25
|
+
// Helper: create a zero-filled DSK image (143360 bytes)
|
|
26
|
+
static std::vector<uint8_t> createBlankDSK() {
|
|
27
|
+
return std::vector<uint8_t>(DskDiskImage::DISK_SIZE, 0x00);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Loading
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
TEST_CASE("DskDiskImage load with valid .dsk data succeeds", "[dsk]") {
|
|
35
|
+
DskDiskImage img;
|
|
36
|
+
auto data = createBlankDSK();
|
|
37
|
+
|
|
38
|
+
bool ok = img.load(data.data(), data.size(), "test.dsk");
|
|
39
|
+
REQUIRE(ok);
|
|
40
|
+
REQUIRE(img.isLoaded());
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
TEST_CASE("DskDiskImage load with .dsk filename sets DSK format", "[dsk]") {
|
|
44
|
+
DskDiskImage img;
|
|
45
|
+
auto data = createBlankDSK();
|
|
46
|
+
|
|
47
|
+
img.load(data.data(), data.size(), "test.dsk");
|
|
48
|
+
// .dsk files are typically treated as DOS-order (DSK or DO)
|
|
49
|
+
DiskImage::Format fmt = img.getFormat();
|
|
50
|
+
REQUIRE((fmt == DiskImage::Format::DSK || fmt == DiskImage::Format::DO));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
TEST_CASE("DskDiskImage load with .po filename sets PO format", "[dsk]") {
|
|
54
|
+
DskDiskImage img;
|
|
55
|
+
auto data = createBlankDSK();
|
|
56
|
+
|
|
57
|
+
img.load(data.data(), data.size(), "test.po");
|
|
58
|
+
REQUIRE(img.getFormat() == DiskImage::Format::PO);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
TEST_CASE("DskDiskImage load with .do filename sets DO/DSK format", "[dsk]") {
|
|
62
|
+
DskDiskImage img;
|
|
63
|
+
auto data = createBlankDSK();
|
|
64
|
+
|
|
65
|
+
img.load(data.data(), data.size(), "test.do");
|
|
66
|
+
DiskImage::Format fmt = img.getFormat();
|
|
67
|
+
REQUIRE((fmt == DiskImage::Format::DSK || fmt == DiskImage::Format::DO));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
TEST_CASE("DskDiskImage load with wrong size fails", "[dsk]") {
|
|
71
|
+
DskDiskImage img;
|
|
72
|
+
std::vector<uint8_t> tooSmall(1000, 0x00);
|
|
73
|
+
|
|
74
|
+
bool ok = img.load(tooSmall.data(), tooSmall.size(), "bad.dsk");
|
|
75
|
+
REQUIRE_FALSE(ok);
|
|
76
|
+
REQUIRE_FALSE(img.isLoaded());
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
TEST_CASE("DskDiskImage load with nullptr fails gracefully", "[dsk]") {
|
|
80
|
+
DskDiskImage img;
|
|
81
|
+
bool ok = img.load(nullptr, 0, "empty.dsk");
|
|
82
|
+
REQUIRE_FALSE(ok);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Geometry
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
TEST_CASE("DskDiskImage getTrackCount returns 35", "[dsk]") {
|
|
90
|
+
DskDiskImage img;
|
|
91
|
+
auto data = createBlankDSK();
|
|
92
|
+
img.load(data.data(), data.size(), "test.dsk");
|
|
93
|
+
|
|
94
|
+
REQUIRE(img.getTrackCount() == 35);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
TEST_CASE("DskDiskImage initial track is 0", "[dsk]") {
|
|
98
|
+
DskDiskImage img;
|
|
99
|
+
auto data = createBlankDSK();
|
|
100
|
+
img.load(data.data(), data.size(), "test.dsk");
|
|
101
|
+
|
|
102
|
+
REQUIRE(img.getTrack() == 0);
|
|
103
|
+
REQUIRE(img.getQuarterTrack() == 0);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// Head positioning
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
TEST_CASE("DskDiskImage setQuarterTrack changes position", "[dsk]") {
|
|
111
|
+
DskDiskImage img;
|
|
112
|
+
auto data = createBlankDSK();
|
|
113
|
+
img.load(data.data(), data.size(), "test.dsk");
|
|
114
|
+
|
|
115
|
+
img.setQuarterTrack(8); // track 2
|
|
116
|
+
REQUIRE(img.getQuarterTrack() == 8);
|
|
117
|
+
REQUIRE(img.getTrack() == 2);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
TEST_CASE("DskDiskImage setQuarterTrack clamps to valid range", "[dsk]") {
|
|
121
|
+
DskDiskImage img;
|
|
122
|
+
auto data = createBlankDSK();
|
|
123
|
+
img.load(data.data(), data.size(), "test.dsk");
|
|
124
|
+
|
|
125
|
+
// Clamp high
|
|
126
|
+
img.setQuarterTrack(1000);
|
|
127
|
+
REQUIRE(img.getQuarterTrack() <= (34 * 4));
|
|
128
|
+
|
|
129
|
+
// Clamp low
|
|
130
|
+
img.setQuarterTrack(-10);
|
|
131
|
+
REQUIRE(img.getQuarterTrack() >= 0);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
TEST_CASE("DskDiskImage setPhase stepping moves head", "[dsk]") {
|
|
135
|
+
DskDiskImage img;
|
|
136
|
+
auto data = createBlankDSK();
|
|
137
|
+
img.load(data.data(), data.size(), "test.dsk");
|
|
138
|
+
|
|
139
|
+
REQUIRE(img.getQuarterTrack() == 0);
|
|
140
|
+
|
|
141
|
+
// Simulate stepping from track 0 toward track 1:
|
|
142
|
+
// Phase sequence: activate phase 1 while phase 0 was active, then deactivate phase 0
|
|
143
|
+
img.setPhase(0, true); // phase 0 on (already at track 0)
|
|
144
|
+
img.setPhase(1, true); // phase 1 on
|
|
145
|
+
img.setPhase(0, false); // phase 0 off - head should step toward phase 1
|
|
146
|
+
|
|
147
|
+
// After stepping, quarter track should have increased
|
|
148
|
+
REQUIRE(img.getQuarterTrack() > 0);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Nibble reading
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
TEST_CASE("DskDiskImage readNibble returns bytes with bit 7 set after load", "[dsk]") {
|
|
156
|
+
DskDiskImage img;
|
|
157
|
+
auto data = createBlankDSK();
|
|
158
|
+
img.load(data.data(), data.size(), "test.dsk");
|
|
159
|
+
|
|
160
|
+
// Read several nibbles; valid GCR nibbles have bit 7 set
|
|
161
|
+
int validCount = 0;
|
|
162
|
+
for (int i = 0; i < 100; ++i) {
|
|
163
|
+
uint8_t nibble = img.readNibble();
|
|
164
|
+
if (nibble & 0x80) {
|
|
165
|
+
++validCount;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Most nibbles from a freshly nibblized track should be valid
|
|
169
|
+
REQUIRE(validCount > 50);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// Sector data access
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
TEST_CASE("DskDiskImage getSectorData returns correct size", "[dsk]") {
|
|
177
|
+
DskDiskImage img;
|
|
178
|
+
auto data = createBlankDSK();
|
|
179
|
+
img.load(data.data(), data.size(), "test.dsk");
|
|
180
|
+
|
|
181
|
+
size_t size = 0;
|
|
182
|
+
const uint8_t* sectorData = img.getSectorData(&size);
|
|
183
|
+
REQUIRE(sectorData != nullptr);
|
|
184
|
+
REQUIRE(size == 143360);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
TEST_CASE("DskDiskImage getSectorData matches loaded data", "[dsk]") {
|
|
188
|
+
DskDiskImage img;
|
|
189
|
+
auto data = createBlankDSK();
|
|
190
|
+
// Write a known pattern
|
|
191
|
+
data[0] = 0xAB;
|
|
192
|
+
data[1] = 0xCD;
|
|
193
|
+
data[143359] = 0xEF;
|
|
194
|
+
img.load(data.data(), data.size(), "test.dsk");
|
|
195
|
+
|
|
196
|
+
size_t size = 0;
|
|
197
|
+
const uint8_t* sectorData = img.getSectorData(&size);
|
|
198
|
+
REQUIRE(sectorData != nullptr);
|
|
199
|
+
REQUIRE(sectorData[0] == 0xAB);
|
|
200
|
+
REQUIRE(sectorData[1] == 0xCD);
|
|
201
|
+
REQUIRE(sectorData[143359] == 0xEF);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
// Modification tracking
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
|
|
208
|
+
TEST_CASE("DskDiskImage isModified is initially false", "[dsk]") {
|
|
209
|
+
DskDiskImage img;
|
|
210
|
+
auto data = createBlankDSK();
|
|
211
|
+
img.load(data.data(), data.size(), "test.dsk");
|
|
212
|
+
|
|
213
|
+
REQUIRE_FALSE(img.isModified());
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
// Write protection
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
TEST_CASE("DskDiskImage isWriteProtected is initially false", "[dsk]") {
|
|
221
|
+
DskDiskImage img;
|
|
222
|
+
auto data = createBlankDSK();
|
|
223
|
+
img.load(data.data(), data.size(), "test.dsk");
|
|
224
|
+
|
|
225
|
+
REQUIRE_FALSE(img.isWriteProtected());
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ---------------------------------------------------------------------------
|
|
229
|
+
// Format name
|
|
230
|
+
// ---------------------------------------------------------------------------
|
|
231
|
+
|
|
232
|
+
TEST_CASE("DskDiskImage getFormatName returns non-empty string", "[dsk]") {
|
|
233
|
+
DskDiskImage img;
|
|
234
|
+
auto data = createBlankDSK();
|
|
235
|
+
img.load(data.data(), data.size(), "test.dsk");
|
|
236
|
+
|
|
237
|
+
std::string name = img.getFormatName();
|
|
238
|
+
REQUIRE_FALSE(name.empty());
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
// Volume number
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
|
|
245
|
+
TEST_CASE("DskDiskImage default volume number is 254", "[dsk]") {
|
|
246
|
+
DskDiskImage img;
|
|
247
|
+
REQUIRE(img.getVolumeNumber() == 254);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
TEST_CASE("DskDiskImage setVolumeNumber changes volume", "[dsk]") {
|
|
251
|
+
DskDiskImage img;
|
|
252
|
+
img.setVolumeNumber(100);
|
|
253
|
+
REQUIRE(img.getVolumeNumber() == 100);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ---------------------------------------------------------------------------
|
|
257
|
+
// Reset state
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
|
|
260
|
+
TEST_CASE("DskDiskImage resetState resets head position", "[dsk]") {
|
|
261
|
+
DskDiskImage img;
|
|
262
|
+
auto data = createBlankDSK();
|
|
263
|
+
img.load(data.data(), data.size(), "test.dsk");
|
|
264
|
+
|
|
265
|
+
img.setQuarterTrack(20);
|
|
266
|
+
REQUIRE(img.getQuarterTrack() == 20);
|
|
267
|
+
|
|
268
|
+
img.resetState();
|
|
269
|
+
REQUIRE(img.getQuarterTrack() == 0);
|
|
270
|
+
REQUIRE(img.getTrack() == 0);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
// hasData
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
|
|
277
|
+
TEST_CASE("DskDiskImage hasData after load", "[dsk]") {
|
|
278
|
+
DskDiskImage img;
|
|
279
|
+
auto data = createBlankDSK();
|
|
280
|
+
img.load(data.data(), data.size(), "test.dsk");
|
|
281
|
+
|
|
282
|
+
REQUIRE(img.hasData());
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
// Filename tracking
|
|
287
|
+
// ---------------------------------------------------------------------------
|
|
288
|
+
|
|
289
|
+
TEST_CASE("DskDiskImage stores filename on load", "[dsk]") {
|
|
290
|
+
DskDiskImage img;
|
|
291
|
+
auto data = createBlankDSK();
|
|
292
|
+
img.load(data.data(), data.size(), "my_disk.dsk");
|
|
293
|
+
|
|
294
|
+
REQUIRE(img.getFilename() == "my_disk.dsk");
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// ---------------------------------------------------------------------------
|
|
298
|
+
// ProDOS order detection
|
|
299
|
+
// ---------------------------------------------------------------------------
|
|
300
|
+
|
|
301
|
+
TEST_CASE("DskDiskImage isProDOSOrder for .po file", "[dsk]") {
|
|
302
|
+
DskDiskImage img;
|
|
303
|
+
auto data = createBlankDSK();
|
|
304
|
+
img.load(data.data(), data.size(), "prodos.po");
|
|
305
|
+
|
|
306
|
+
REQUIRE(img.isProDOSOrder());
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
TEST_CASE("DskDiskImage isProDOSOrder false for .dsk file", "[dsk]") {
|
|
310
|
+
DskDiskImage img;
|
|
311
|
+
auto data = createBlankDSK();
|
|
312
|
+
img.load(data.data(), data.size(), "dos33.dsk");
|
|
313
|
+
|
|
314
|
+
REQUIRE_FALSE(img.isProDOSOrder());
|
|
315
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* test_expansion_card.cpp - Unit tests for expansion card interface
|
|
3
|
+
*
|
|
4
|
+
* Tests the common ExpansionCard interface across all card types:
|
|
5
|
+
* Disk2Card, MockingboardCard, ThunderclockCard, MouseCard, SmartPortCard.
|
|
6
|
+
* Verifies getName, getPreferredSlot, hasROM, hasExpansionROM, reset,
|
|
7
|
+
* and getStateSize for each card type.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#define CATCH_CONFIG_MAIN
|
|
11
|
+
#include "catch.hpp"
|
|
12
|
+
|
|
13
|
+
#include "expansion_card.hpp"
|
|
14
|
+
#include "cards/disk2_card.hpp"
|
|
15
|
+
#include "cards/mockingboard_card.hpp"
|
|
16
|
+
#include "cards/thunderclock_card.hpp"
|
|
17
|
+
#include "cards/mouse_card.hpp"
|
|
18
|
+
#include "cards/smartport/smartport_card.hpp"
|
|
19
|
+
#include "roms.cpp"
|
|
20
|
+
|
|
21
|
+
#include <string>
|
|
22
|
+
|
|
23
|
+
using namespace a2e;
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// getName - each card returns a non-null, non-empty name
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
TEST_CASE("Disk2Card getName returns non-null", "[expansion][disk2]") {
|
|
30
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
31
|
+
REQUIRE(card.getName() != nullptr);
|
|
32
|
+
REQUIRE(std::string(card.getName()).length() > 0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
TEST_CASE("MockingboardCard getName returns non-null", "[expansion][mockingboard]") {
|
|
36
|
+
MockingboardCard card;
|
|
37
|
+
REQUIRE(card.getName() != nullptr);
|
|
38
|
+
REQUIRE(std::string(card.getName()).length() > 0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
TEST_CASE("ThunderclockCard getName returns non-null", "[expansion][thunderclock]") {
|
|
42
|
+
ThunderclockCard card;
|
|
43
|
+
REQUIRE(card.getName() != nullptr);
|
|
44
|
+
REQUIRE(std::string(card.getName()).length() > 0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
TEST_CASE("MouseCard getName returns non-null", "[expansion][mouse]") {
|
|
48
|
+
MouseCard card;
|
|
49
|
+
REQUIRE(card.getName() != nullptr);
|
|
50
|
+
REQUIRE(std::string(card.getName()).length() > 0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
TEST_CASE("SmartPortCard getName returns non-null", "[expansion][smartport]") {
|
|
54
|
+
SmartPortCard card;
|
|
55
|
+
REQUIRE(card.getName() != nullptr);
|
|
56
|
+
REQUIRE(std::string(card.getName()).length() > 0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// getPreferredSlot - each card returns a valid slot number
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
TEST_CASE("Disk2Card getPreferredSlot is 6", "[expansion][disk2]") {
|
|
64
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
65
|
+
REQUIRE(card.getPreferredSlot() == 6);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
TEST_CASE("MockingboardCard getPreferredSlot is 4", "[expansion][mockingboard]") {
|
|
69
|
+
MockingboardCard card;
|
|
70
|
+
REQUIRE(card.getPreferredSlot() == 4);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
TEST_CASE("ThunderclockCard getPreferredSlot is 5", "[expansion][thunderclock]") {
|
|
74
|
+
ThunderclockCard card;
|
|
75
|
+
REQUIRE(card.getPreferredSlot() == 5);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
TEST_CASE("MouseCard getPreferredSlot is 4", "[expansion][mouse]") {
|
|
79
|
+
MouseCard card;
|
|
80
|
+
REQUIRE(card.getPreferredSlot() == 4);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
TEST_CASE("SmartPortCard getPreferredSlot is 7", "[expansion][smartport]") {
|
|
84
|
+
SmartPortCard card;
|
|
85
|
+
REQUIRE(card.getPreferredSlot() == 7);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// hasROM / hasExpansionROM
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
TEST_CASE("Disk2Card hasROM is true, hasExpansionROM is false", "[expansion][disk2]") {
|
|
93
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
94
|
+
REQUIRE(card.hasROM());
|
|
95
|
+
REQUIRE_FALSE(card.hasExpansionROM());
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
TEST_CASE("MockingboardCard hasROM is true, hasExpansionROM is false", "[expansion][mockingboard]") {
|
|
99
|
+
MockingboardCard card;
|
|
100
|
+
REQUIRE(card.hasROM());
|
|
101
|
+
REQUIRE_FALSE(card.hasExpansionROM());
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
TEST_CASE("ThunderclockCard hasROM is true, hasExpansionROM is true", "[expansion][thunderclock]") {
|
|
105
|
+
ThunderclockCard card;
|
|
106
|
+
REQUIRE(card.hasROM());
|
|
107
|
+
REQUIRE(card.hasExpansionROM());
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
TEST_CASE("MouseCard hasROM is true", "[expansion][mouse]") {
|
|
111
|
+
MouseCard card;
|
|
112
|
+
REQUIRE(card.hasROM());
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// reset() - should not crash for any card type
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
TEST_CASE("Disk2Card reset does not crash", "[expansion][disk2]") {
|
|
120
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
121
|
+
REQUIRE_NOTHROW(card.reset());
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
TEST_CASE("MockingboardCard reset does not crash", "[expansion][mockingboard]") {
|
|
125
|
+
MockingboardCard card;
|
|
126
|
+
REQUIRE_NOTHROW(card.reset());
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
TEST_CASE("ThunderclockCard reset does not crash", "[expansion][thunderclock]") {
|
|
130
|
+
ThunderclockCard card;
|
|
131
|
+
REQUIRE_NOTHROW(card.reset());
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
TEST_CASE("MouseCard reset does not crash", "[expansion][mouse]") {
|
|
135
|
+
MouseCard card;
|
|
136
|
+
REQUIRE_NOTHROW(card.reset());
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
TEST_CASE("SmartPortCard reset does not crash", "[expansion][smartport]") {
|
|
140
|
+
SmartPortCard card;
|
|
141
|
+
REQUIRE_NOTHROW(card.reset());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
// getStateSize - returns reasonable values
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
TEST_CASE("Disk2Card getStateSize is greater than zero", "[expansion][disk2]") {
|
|
149
|
+
Disk2Card card(roms::ROM_DISK2, roms::ROM_DISK2_SIZE);
|
|
150
|
+
REQUIRE(card.getStateSize() > 0);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
TEST_CASE("MockingboardCard getStateSize equals STATE_SIZE constant", "[expansion][mockingboard]") {
|
|
154
|
+
MockingboardCard card;
|
|
155
|
+
REQUIRE(card.getStateSize() == MockingboardCard::STATE_SIZE);
|
|
156
|
+
REQUIRE(card.getStateSize() > 0);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
TEST_CASE("ThunderclockCard getStateSize equals STATE_SIZE constant", "[expansion][thunderclock]") {
|
|
160
|
+
ThunderclockCard card;
|
|
161
|
+
REQUIRE(card.getStateSize() == ThunderclockCard::STATE_SIZE);
|
|
162
|
+
REQUIRE(card.getStateSize() > 0);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
TEST_CASE("MouseCard getStateSize equals STATE_SIZE constant", "[expansion][mouse]") {
|
|
166
|
+
MouseCard card;
|
|
167
|
+
REQUIRE(card.getStateSize() == MouseCard::STATE_SIZE);
|
|
168
|
+
REQUIRE(card.getStateSize() > 0);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
TEST_CASE("SmartPortCard getStateSize returns a value", "[expansion][smartport]") {
|
|
172
|
+
SmartPortCard card;
|
|
173
|
+
// SmartPort state size may be 0 when no devices are loaded, or > 0
|
|
174
|
+
// Just verify it does not crash
|
|
175
|
+
size_t sz = card.getStateSize();
|
|
176
|
+
(void)sz;
|
|
177
|
+
REQUIRE(true);
|
|
178
|
+
}
|