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,259 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* test_block_device.cpp - Unit tests for SmartPort BlockDevice
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
#define CATCH_CONFIG_MAIN
|
|
6
|
+
#include "catch.hpp"
|
|
7
|
+
|
|
8
|
+
#include "block_device.hpp"
|
|
9
|
+
|
|
10
|
+
#include <cstring>
|
|
11
|
+
#include <vector>
|
|
12
|
+
|
|
13
|
+
using namespace a2e;
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Default construction
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
TEST_CASE("BlockDevice default constructed is not loaded", "[blockdev][ctor]") {
|
|
20
|
+
BlockDevice dev;
|
|
21
|
+
CHECK(dev.isLoaded() == false);
|
|
22
|
+
CHECK(dev.getTotalBlocks() == 0);
|
|
23
|
+
CHECK(dev.isModified() == false);
|
|
24
|
+
CHECK(dev.isWriteProtected() == false);
|
|
25
|
+
CHECK(dev.getFilename().empty());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// load with valid HDV data
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
TEST_CASE("BlockDevice load with raw block data", "[blockdev][load]") {
|
|
33
|
+
BlockDevice dev;
|
|
34
|
+
|
|
35
|
+
// Create a minimal HDV image: 4 blocks = 2048 bytes
|
|
36
|
+
const size_t numBlocks = 4;
|
|
37
|
+
const size_t dataSize = numBlocks * BlockDevice::BLOCK_SIZE;
|
|
38
|
+
std::vector<uint8_t> data(dataSize, 0x00);
|
|
39
|
+
|
|
40
|
+
// Fill first block with a pattern
|
|
41
|
+
for (size_t i = 0; i < BlockDevice::BLOCK_SIZE; i++) {
|
|
42
|
+
data[i] = static_cast<uint8_t>(i & 0xFF);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
bool loaded = dev.load(data.data(), data.size(), "test.hdv");
|
|
46
|
+
REQUIRE(loaded == true);
|
|
47
|
+
|
|
48
|
+
CHECK(dev.isLoaded() == true);
|
|
49
|
+
CHECK(dev.getTotalBlocks() == numBlocks);
|
|
50
|
+
CHECK(dev.getFilename() == "test.hdv");
|
|
51
|
+
CHECK(dev.isModified() == false);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
TEST_CASE("BlockDevice load with zero-size data fails", "[blockdev][load]") {
|
|
55
|
+
BlockDevice dev;
|
|
56
|
+
bool loaded = dev.load(nullptr, 0, "empty.hdv");
|
|
57
|
+
CHECK(loaded == false);
|
|
58
|
+
CHECK(dev.isLoaded() == false);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
TEST_CASE("BlockDevice load with non-block-aligned size", "[blockdev][load]") {
|
|
62
|
+
BlockDevice dev;
|
|
63
|
+
// 1000 bytes is not a multiple of 512
|
|
64
|
+
std::vector<uint8_t> data(1000, 0x00);
|
|
65
|
+
bool loaded = dev.load(data.data(), data.size(), "weird.hdv");
|
|
66
|
+
// Implementation may reject or truncate - just verify no crash
|
|
67
|
+
// If it loads, totalBlocks should reflect the truncated count
|
|
68
|
+
if (loaded) {
|
|
69
|
+
CHECK(dev.getTotalBlocks() == 1); // 1000/512 = 1 full block
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// readBlock
|
|
75
|
+
// ============================================================================
|
|
76
|
+
|
|
77
|
+
TEST_CASE("BlockDevice readBlock reads correct data", "[blockdev][readBlock]") {
|
|
78
|
+
BlockDevice dev;
|
|
79
|
+
|
|
80
|
+
const size_t numBlocks = 8;
|
|
81
|
+
std::vector<uint8_t> data(numBlocks * BlockDevice::BLOCK_SIZE, 0x00);
|
|
82
|
+
|
|
83
|
+
// Put distinctive pattern in block 3
|
|
84
|
+
for (size_t i = 0; i < BlockDevice::BLOCK_SIZE; i++) {
|
|
85
|
+
data[3 * BlockDevice::BLOCK_SIZE + i] = static_cast<uint8_t>(0xAA);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
REQUIRE(dev.load(data.data(), data.size(), "test.hdv"));
|
|
89
|
+
|
|
90
|
+
uint8_t buffer[BlockDevice::BLOCK_SIZE];
|
|
91
|
+
|
|
92
|
+
// Read block 3
|
|
93
|
+
bool ok = dev.readBlock(3, buffer);
|
|
94
|
+
REQUIRE(ok);
|
|
95
|
+
for (size_t i = 0; i < BlockDevice::BLOCK_SIZE; i++) {
|
|
96
|
+
CHECK(buffer[i] == 0xAA);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Read block 0 (should be all zeros)
|
|
100
|
+
ok = dev.readBlock(0, buffer);
|
|
101
|
+
REQUIRE(ok);
|
|
102
|
+
for (size_t i = 0; i < BlockDevice::BLOCK_SIZE; i++) {
|
|
103
|
+
CHECK(buffer[i] == 0x00);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
TEST_CASE("BlockDevice readBlock out of range fails", "[blockdev][readBlock]") {
|
|
108
|
+
BlockDevice dev;
|
|
109
|
+
|
|
110
|
+
std::vector<uint8_t> data(4 * BlockDevice::BLOCK_SIZE, 0x00);
|
|
111
|
+
REQUIRE(dev.load(data.data(), data.size(), "test.hdv"));
|
|
112
|
+
|
|
113
|
+
uint8_t buffer[BlockDevice::BLOCK_SIZE];
|
|
114
|
+
bool ok = dev.readBlock(100, buffer); // Way out of range
|
|
115
|
+
CHECK(ok == false);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ============================================================================
|
|
119
|
+
// writeBlock
|
|
120
|
+
// ============================================================================
|
|
121
|
+
|
|
122
|
+
TEST_CASE("BlockDevice writeBlock writes and reads back", "[blockdev][writeBlock]") {
|
|
123
|
+
BlockDevice dev;
|
|
124
|
+
|
|
125
|
+
std::vector<uint8_t> data(8 * BlockDevice::BLOCK_SIZE, 0x00);
|
|
126
|
+
REQUIRE(dev.load(data.data(), data.size(), "test.hdv"));
|
|
127
|
+
|
|
128
|
+
uint8_t writeData[BlockDevice::BLOCK_SIZE];
|
|
129
|
+
memset(writeData, 0x55, sizeof(writeData));
|
|
130
|
+
|
|
131
|
+
bool ok = dev.writeBlock(2, writeData);
|
|
132
|
+
REQUIRE(ok);
|
|
133
|
+
|
|
134
|
+
// Read it back
|
|
135
|
+
uint8_t readBuf[BlockDevice::BLOCK_SIZE];
|
|
136
|
+
ok = dev.readBlock(2, readBuf);
|
|
137
|
+
REQUIRE(ok);
|
|
138
|
+
|
|
139
|
+
CHECK(memcmp(writeData, readBuf, BlockDevice::BLOCK_SIZE) == 0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
TEST_CASE("BlockDevice writeBlock out of range fails", "[blockdev][writeBlock]") {
|
|
143
|
+
BlockDevice dev;
|
|
144
|
+
|
|
145
|
+
std::vector<uint8_t> data(4 * BlockDevice::BLOCK_SIZE, 0x00);
|
|
146
|
+
REQUIRE(dev.load(data.data(), data.size(), "test.hdv"));
|
|
147
|
+
|
|
148
|
+
uint8_t writeData[BlockDevice::BLOCK_SIZE];
|
|
149
|
+
memset(writeData, 0xFF, sizeof(writeData));
|
|
150
|
+
|
|
151
|
+
bool ok = dev.writeBlock(100, writeData);
|
|
152
|
+
CHECK(ok == false);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// isModified after writeBlock
|
|
157
|
+
// ============================================================================
|
|
158
|
+
|
|
159
|
+
TEST_CASE("BlockDevice isModified is true after writeBlock", "[blockdev][modified]") {
|
|
160
|
+
BlockDevice dev;
|
|
161
|
+
|
|
162
|
+
std::vector<uint8_t> data(4 * BlockDevice::BLOCK_SIZE, 0x00);
|
|
163
|
+
REQUIRE(dev.load(data.data(), data.size(), "test.hdv"));
|
|
164
|
+
|
|
165
|
+
CHECK(dev.isModified() == false);
|
|
166
|
+
|
|
167
|
+
uint8_t writeData[BlockDevice::BLOCK_SIZE];
|
|
168
|
+
memset(writeData, 0x42, sizeof(writeData));
|
|
169
|
+
dev.writeBlock(0, writeData);
|
|
170
|
+
|
|
171
|
+
CHECK(dev.isModified() == true);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ============================================================================
|
|
175
|
+
// eject
|
|
176
|
+
// ============================================================================
|
|
177
|
+
|
|
178
|
+
TEST_CASE("BlockDevice eject clears state", "[blockdev][eject]") {
|
|
179
|
+
BlockDevice dev;
|
|
180
|
+
|
|
181
|
+
std::vector<uint8_t> data(8 * BlockDevice::BLOCK_SIZE, 0x00);
|
|
182
|
+
REQUIRE(dev.load(data.data(), data.size(), "test.hdv"));
|
|
183
|
+
|
|
184
|
+
CHECK(dev.isLoaded() == true);
|
|
185
|
+
|
|
186
|
+
dev.eject();
|
|
187
|
+
|
|
188
|
+
CHECK(dev.isLoaded() == false);
|
|
189
|
+
CHECK(dev.getTotalBlocks() == 0);
|
|
190
|
+
CHECK(dev.isModified() == false);
|
|
191
|
+
CHECK(dev.getFilename().empty());
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// exportData
|
|
196
|
+
// ============================================================================
|
|
197
|
+
|
|
198
|
+
TEST_CASE("BlockDevice exportData returns valid pointer and size", "[blockdev][export]") {
|
|
199
|
+
BlockDevice dev;
|
|
200
|
+
|
|
201
|
+
const size_t numBlocks = 4;
|
|
202
|
+
std::vector<uint8_t> data(numBlocks * BlockDevice::BLOCK_SIZE, 0xAB);
|
|
203
|
+
REQUIRE(dev.load(data.data(), data.size(), "test.hdv"));
|
|
204
|
+
|
|
205
|
+
size_t exportSize = 0;
|
|
206
|
+
const uint8_t* exported = dev.exportData(&exportSize);
|
|
207
|
+
|
|
208
|
+
REQUIRE(exported != nullptr);
|
|
209
|
+
REQUIRE(exportSize > 0);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
TEST_CASE("BlockDevice exportData on unloaded device returns null", "[blockdev][export]") {
|
|
213
|
+
BlockDevice dev;
|
|
214
|
+
size_t exportSize = 0;
|
|
215
|
+
const uint8_t* exported = dev.exportData(&exportSize);
|
|
216
|
+
CHECK(exported == nullptr);
|
|
217
|
+
CHECK(exportSize == 0);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ============================================================================
|
|
221
|
+
// getFilename after load
|
|
222
|
+
// ============================================================================
|
|
223
|
+
|
|
224
|
+
TEST_CASE("BlockDevice getFilename returns loaded filename", "[blockdev][filename]") {
|
|
225
|
+
BlockDevice dev;
|
|
226
|
+
|
|
227
|
+
std::vector<uint8_t> data(2 * BlockDevice::BLOCK_SIZE, 0x00);
|
|
228
|
+
|
|
229
|
+
dev.load(data.data(), data.size(), "mydisk.po");
|
|
230
|
+
CHECK(dev.getFilename() == "mydisk.po");
|
|
231
|
+
|
|
232
|
+
dev.load(data.data(), data.size(), "another.2mg");
|
|
233
|
+
CHECK(dev.getFilename() == "another.2mg");
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
TEST_CASE("BlockDevice getFilename empty when not loaded", "[blockdev][filename]") {
|
|
237
|
+
BlockDevice dev;
|
|
238
|
+
CHECK(dev.getFilename().empty());
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ============================================================================
|
|
242
|
+
// Multiple load/eject cycles
|
|
243
|
+
// ============================================================================
|
|
244
|
+
|
|
245
|
+
TEST_CASE("BlockDevice can be reloaded after eject", "[blockdev][lifecycle]") {
|
|
246
|
+
BlockDevice dev;
|
|
247
|
+
|
|
248
|
+
std::vector<uint8_t> data1(4 * BlockDevice::BLOCK_SIZE, 0x11);
|
|
249
|
+
REQUIRE(dev.load(data1.data(), data1.size(), "first.hdv"));
|
|
250
|
+
CHECK(dev.getTotalBlocks() == 4);
|
|
251
|
+
|
|
252
|
+
dev.eject();
|
|
253
|
+
CHECK(dev.isLoaded() == false);
|
|
254
|
+
|
|
255
|
+
std::vector<uint8_t> data2(8 * BlockDevice::BLOCK_SIZE, 0x22);
|
|
256
|
+
REQUIRE(dev.load(data2.data(), data2.size(), "second.hdv"));
|
|
257
|
+
CHECK(dev.getTotalBlocks() == 8);
|
|
258
|
+
CHECK(dev.getFilename() == "second.hdv");
|
|
259
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* test_condition_evaluator.cpp - Unit tests for ConditionEvaluator
|
|
3
|
+
*
|
|
4
|
+
* This requires an Emulator instance since evaluate() and evaluateNumeric()
|
|
5
|
+
* take a const Emulator& parameter.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#define CATCH_CONFIG_MAIN
|
|
9
|
+
#include "catch.hpp"
|
|
10
|
+
|
|
11
|
+
#include "condition_evaluator.hpp"
|
|
12
|
+
#include "emulator.hpp"
|
|
13
|
+
|
|
14
|
+
using namespace a2e;
|
|
15
|
+
|
|
16
|
+
// Helper to create and initialize an emulator for testing.
|
|
17
|
+
// The emulator init() loads ROM and resets the CPU.
|
|
18
|
+
static Emulator& getEmulator() {
|
|
19
|
+
static Emulator emu;
|
|
20
|
+
static bool initialized = false;
|
|
21
|
+
if (!initialized) {
|
|
22
|
+
emu.init();
|
|
23
|
+
initialized = true;
|
|
24
|
+
}
|
|
25
|
+
return emu;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Simple numeric expression
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
TEST_CASE("ConditionEvaluator numeric literal 42 evaluates to 42", "[condeval][numeric]") {
|
|
33
|
+
auto& emu = getEmulator();
|
|
34
|
+
int32_t result = ConditionEvaluator::evaluateNumeric("42", emu);
|
|
35
|
+
CHECK(result == 42);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
TEST_CASE("ConditionEvaluator numeric literal 0 evaluates to 0", "[condeval][numeric]") {
|
|
39
|
+
auto& emu = getEmulator();
|
|
40
|
+
int32_t result = ConditionEvaluator::evaluateNumeric("0", emu);
|
|
41
|
+
CHECK(result == 0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
TEST_CASE("ConditionEvaluator numeric literal 255 evaluates to 255", "[condeval][numeric]") {
|
|
45
|
+
auto& emu = getEmulator();
|
|
46
|
+
int32_t result = ConditionEvaluator::evaluateNumeric("255", emu);
|
|
47
|
+
CHECK(result == 255);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Hex literal
|
|
52
|
+
// ============================================================================
|
|
53
|
+
|
|
54
|
+
TEST_CASE("ConditionEvaluator hex literal $FF evaluates to 255", "[condeval][hex]") {
|
|
55
|
+
auto& emu = getEmulator();
|
|
56
|
+
int32_t result = ConditionEvaluator::evaluateNumeric("$FF", emu);
|
|
57
|
+
CHECK(result == 255);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
TEST_CASE("ConditionEvaluator hex literal $00 evaluates to 0", "[condeval][hex]") {
|
|
61
|
+
auto& emu = getEmulator();
|
|
62
|
+
int32_t result = ConditionEvaluator::evaluateNumeric("$00", emu);
|
|
63
|
+
CHECK(result == 0);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
TEST_CASE("ConditionEvaluator hex literal $FFFF evaluates to 65535", "[condeval][hex]") {
|
|
67
|
+
auto& emu = getEmulator();
|
|
68
|
+
int32_t result = ConditionEvaluator::evaluateNumeric("$FFFF", emu);
|
|
69
|
+
CHECK(result == 65535);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ============================================================================
|
|
73
|
+
// Register comparison
|
|
74
|
+
// ============================================================================
|
|
75
|
+
|
|
76
|
+
TEST_CASE("ConditionEvaluator A register comparison", "[condeval][register]") {
|
|
77
|
+
auto& emu = getEmulator();
|
|
78
|
+
|
|
79
|
+
// After init(), A register has some value (typically 0 after cold reset).
|
|
80
|
+
// Set A to a known value via the emulator's CPU setter.
|
|
81
|
+
emu.setA(0);
|
|
82
|
+
|
|
83
|
+
bool result = ConditionEvaluator::evaluate("A == 0", emu);
|
|
84
|
+
CHECK(result == true);
|
|
85
|
+
|
|
86
|
+
// Negative test
|
|
87
|
+
result = ConditionEvaluator::evaluate("A == 1", emu);
|
|
88
|
+
CHECK(result == false);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
TEST_CASE("ConditionEvaluator X register comparison", "[condeval][register]") {
|
|
92
|
+
auto& emu = getEmulator();
|
|
93
|
+
emu.setX(0x42);
|
|
94
|
+
|
|
95
|
+
bool result = ConditionEvaluator::evaluate("X == $42", emu);
|
|
96
|
+
CHECK(result == true);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
TEST_CASE("ConditionEvaluator Y register comparison", "[condeval][register]") {
|
|
100
|
+
auto& emu = getEmulator();
|
|
101
|
+
emu.setY(0x10);
|
|
102
|
+
|
|
103
|
+
bool result = ConditionEvaluator::evaluate("Y == $10", emu);
|
|
104
|
+
CHECK(result == true);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Invalid expression returns error
|
|
109
|
+
// ============================================================================
|
|
110
|
+
|
|
111
|
+
TEST_CASE("ConditionEvaluator unknown identifier sets error", "[condeval][error]") {
|
|
112
|
+
auto& emu = getEmulator();
|
|
113
|
+
|
|
114
|
+
// An expression with an unknown identifier triggers an error
|
|
115
|
+
ConditionEvaluator::evaluate("FOOBAR == 1", emu);
|
|
116
|
+
const char* err = ConditionEvaluator::getLastError();
|
|
117
|
+
CHECK(strlen(err) > 0);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
TEST_CASE("ConditionEvaluator valid expression clears error", "[condeval][error]") {
|
|
121
|
+
auto& emu = getEmulator();
|
|
122
|
+
|
|
123
|
+
// First trigger an error with an unknown identifier
|
|
124
|
+
ConditionEvaluator::evaluate("FOOBAR == 1", emu);
|
|
125
|
+
CHECK(strlen(ConditionEvaluator::getLastError()) > 0);
|
|
126
|
+
|
|
127
|
+
// Now evaluate a valid expression
|
|
128
|
+
ConditionEvaluator::evaluate("42 == 42", emu);
|
|
129
|
+
// The error should be cleared (empty string)
|
|
130
|
+
CHECK(strlen(ConditionEvaluator::getLastError()) == 0);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ============================================================================
|
|
134
|
+
// Arithmetic expression
|
|
135
|
+
// ============================================================================
|
|
136
|
+
|
|
137
|
+
TEST_CASE("ConditionEvaluator arithmetic $10 + $20 = $30", "[condeval][arithmetic]") {
|
|
138
|
+
auto& emu = getEmulator();
|
|
139
|
+
int32_t result = ConditionEvaluator::evaluateNumeric("$10 + $20", emu);
|
|
140
|
+
CHECK(result == 0x30);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
TEST_CASE("ConditionEvaluator arithmetic subtraction", "[condeval][arithmetic]") {
|
|
144
|
+
auto& emu = getEmulator();
|
|
145
|
+
int32_t result = ConditionEvaluator::evaluateNumeric("100 - 30", emu);
|
|
146
|
+
CHECK(result == 70);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
TEST_CASE("ConditionEvaluator arithmetic multiplication", "[condeval][arithmetic]") {
|
|
150
|
+
auto& emu = getEmulator();
|
|
151
|
+
int32_t result = ConditionEvaluator::evaluateNumeric("6 * 7", emu);
|
|
152
|
+
CHECK(result == 42);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// Boolean operations
|
|
157
|
+
// ============================================================================
|
|
158
|
+
|
|
159
|
+
TEST_CASE("ConditionEvaluator equality true", "[condeval][comparison]") {
|
|
160
|
+
auto& emu = getEmulator();
|
|
161
|
+
bool result = ConditionEvaluator::evaluate("42 == 42", emu);
|
|
162
|
+
CHECK(result == true);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
TEST_CASE("ConditionEvaluator inequality", "[condeval][comparison]") {
|
|
166
|
+
auto& emu = getEmulator();
|
|
167
|
+
bool result = ConditionEvaluator::evaluate("1 != 2", emu);
|
|
168
|
+
CHECK(result == true);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
TEST_CASE("ConditionEvaluator less than", "[condeval][comparison]") {
|
|
172
|
+
auto& emu = getEmulator();
|
|
173
|
+
bool result = ConditionEvaluator::evaluate("1 < 2", emu);
|
|
174
|
+
CHECK(result == true);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
TEST_CASE("ConditionEvaluator greater than", "[condeval][comparison]") {
|
|
178
|
+
auto& emu = getEmulator();
|
|
179
|
+
bool result = ConditionEvaluator::evaluate("10 > 5", emu);
|
|
180
|
+
CHECK(result == true);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ============================================================================
|
|
184
|
+
// Parenthesized expressions
|
|
185
|
+
// ============================================================================
|
|
186
|
+
|
|
187
|
+
TEST_CASE("ConditionEvaluator parenthesized expression", "[condeval][paren]") {
|
|
188
|
+
auto& emu = getEmulator();
|
|
189
|
+
|
|
190
|
+
// Parenthesized expressions are evaluated through parseOr which returns
|
|
191
|
+
// a boolean (truthy) value. So (2 + 3) becomes true (1), and 1 * 4 = 4.
|
|
192
|
+
// This is by design: the evaluator is a condition evaluator, not a
|
|
193
|
+
// general-purpose calculator. Parentheses are for grouping comparisons.
|
|
194
|
+
int32_t result = ConditionEvaluator::evaluateNumeric("(2 + 3) * 4", emu);
|
|
195
|
+
CHECK(result == 4);
|
|
196
|
+
|
|
197
|
+
// Verify parenthesized comparisons work as expected
|
|
198
|
+
bool cmpResult = ConditionEvaluator::evaluate("(A == A) && (1 < 2)", emu);
|
|
199
|
+
CHECK(cmpResult == true);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ============================================================================
|
|
203
|
+
// Register numeric read
|
|
204
|
+
// ============================================================================
|
|
205
|
+
|
|
206
|
+
TEST_CASE("ConditionEvaluator reads register A as numeric", "[condeval][register_num]") {
|
|
207
|
+
auto& emu = getEmulator();
|
|
208
|
+
emu.setA(0xAB);
|
|
209
|
+
int32_t result = ConditionEvaluator::evaluateNumeric("A", emu);
|
|
210
|
+
CHECK(result == 0xAB);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
TEST_CASE("ConditionEvaluator reads SP register", "[condeval][register_num]") {
|
|
214
|
+
auto& emu = getEmulator();
|
|
215
|
+
// SP should be a valid 8-bit value
|
|
216
|
+
int32_t result = ConditionEvaluator::evaluateNumeric("SP", emu);
|
|
217
|
+
CHECK(result >= 0);
|
|
218
|
+
CHECK(result <= 0xFF);
|
|
219
|
+
}
|