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,344 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* test_emulator_debug.cpp - Integration tests for Emulator debug features
|
|
3
|
+
*
|
|
4
|
+
* Tests breakpoints, watchpoints, beam breakpoints, trace logging,
|
|
5
|
+
* cycle profiling, step over, step out, and related debug state.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#define CATCH_CONFIG_MAIN
|
|
9
|
+
#include "catch.hpp"
|
|
10
|
+
|
|
11
|
+
#include "emulator.hpp"
|
|
12
|
+
|
|
13
|
+
#include <memory>
|
|
14
|
+
|
|
15
|
+
using namespace a2e;
|
|
16
|
+
|
|
17
|
+
// Helper: create an initialized emulator ready for testing
|
|
18
|
+
static std::unique_ptr<Emulator> makeEmulator() {
|
|
19
|
+
auto emu = std::make_unique<Emulator>();
|
|
20
|
+
emu->init();
|
|
21
|
+
return emu;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Breakpoints
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
TEST_CASE("Breakpoint triggers when PC reaches address", "[emulator][debug][breakpoint]") {
|
|
29
|
+
Emulator emu;
|
|
30
|
+
emu.init();
|
|
31
|
+
|
|
32
|
+
// Write a JMP $0400 loop at $0400: 4C 00 04
|
|
33
|
+
emu.writeMemory(0x0400, 0x4C); // JMP
|
|
34
|
+
emu.writeMemory(0x0401, 0x00); // low byte
|
|
35
|
+
emu.writeMemory(0x0402, 0x04); // high byte
|
|
36
|
+
|
|
37
|
+
// Set PC to $0400 and add breakpoint there
|
|
38
|
+
emu.setPC(0x0400);
|
|
39
|
+
emu.addBreakpoint(0x0400);
|
|
40
|
+
|
|
41
|
+
// Need to unpause since breakpoints pause on hit
|
|
42
|
+
// The first execution should immediately hit the breakpoint
|
|
43
|
+
emu.runCycles(100);
|
|
44
|
+
|
|
45
|
+
REQUIRE(emu.isBreakpointHit());
|
|
46
|
+
REQUIRE(emu.getBreakpointAddress() == 0x0400);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
TEST_CASE("removeBreakpoint prevents breakpoint from triggering", "[emulator][debug][breakpoint]") {
|
|
50
|
+
Emulator emu;
|
|
51
|
+
emu.init();
|
|
52
|
+
|
|
53
|
+
// Write NOP sled at $0400
|
|
54
|
+
for (int i = 0; i < 16; i++) {
|
|
55
|
+
emu.writeMemory(0x0400 + i, 0xEA); // NOP
|
|
56
|
+
}
|
|
57
|
+
// End with JMP $0400
|
|
58
|
+
emu.writeMemory(0x0410, 0x4C);
|
|
59
|
+
emu.writeMemory(0x0411, 0x00);
|
|
60
|
+
emu.writeMemory(0x0412, 0x04);
|
|
61
|
+
|
|
62
|
+
emu.setPC(0x0400);
|
|
63
|
+
emu.addBreakpoint(0x0408);
|
|
64
|
+
|
|
65
|
+
// Run - breakpoint should hit at $0408
|
|
66
|
+
emu.runCycles(100);
|
|
67
|
+
REQUIRE(emu.isBreakpointHit());
|
|
68
|
+
REQUIRE(emu.getBreakpointAddress() == 0x0408);
|
|
69
|
+
|
|
70
|
+
// Remove it and resume
|
|
71
|
+
emu.removeBreakpoint(0x0408);
|
|
72
|
+
emu.setPaused(false);
|
|
73
|
+
emu.runCycles(1000);
|
|
74
|
+
|
|
75
|
+
// Should NOT hit a breakpoint now (will just loop)
|
|
76
|
+
REQUIRE_FALSE(emu.isBreakpointHit());
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
TEST_CASE("enableBreakpoint(false) prevents breakpoint from triggering", "[emulator][debug][breakpoint]") {
|
|
80
|
+
Emulator emu;
|
|
81
|
+
emu.init();
|
|
82
|
+
|
|
83
|
+
// NOP sled + JMP loop
|
|
84
|
+
for (int i = 0; i < 16; i++) {
|
|
85
|
+
emu.writeMemory(0x0400 + i, 0xEA);
|
|
86
|
+
}
|
|
87
|
+
emu.writeMemory(0x0410, 0x4C);
|
|
88
|
+
emu.writeMemory(0x0411, 0x00);
|
|
89
|
+
emu.writeMemory(0x0412, 0x04);
|
|
90
|
+
|
|
91
|
+
emu.setPC(0x0400);
|
|
92
|
+
emu.addBreakpoint(0x0408);
|
|
93
|
+
emu.enableBreakpoint(0x0408, false); // disable it
|
|
94
|
+
|
|
95
|
+
emu.runCycles(1000);
|
|
96
|
+
REQUIRE_FALSE(emu.isBreakpointHit());
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
TEST_CASE("getBreakpointAddress returns correct address on hit", "[emulator][debug][breakpoint]") {
|
|
100
|
+
Emulator emu;
|
|
101
|
+
emu.init();
|
|
102
|
+
|
|
103
|
+
emu.writeMemory(0x0500, 0x4C);
|
|
104
|
+
emu.writeMemory(0x0501, 0x00);
|
|
105
|
+
emu.writeMemory(0x0502, 0x05);
|
|
106
|
+
|
|
107
|
+
emu.setPC(0x0500);
|
|
108
|
+
emu.addBreakpoint(0x0500);
|
|
109
|
+
emu.runCycles(100);
|
|
110
|
+
|
|
111
|
+
REQUIRE(emu.isBreakpointHit());
|
|
112
|
+
REQUIRE(emu.getBreakpointAddress() == 0x0500);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// Watchpoints
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
TEST_CASE("Watchpoint triggers on write to watched address", "[emulator][debug][watchpoint]") {
|
|
120
|
+
Emulator emu;
|
|
121
|
+
emu.init();
|
|
122
|
+
|
|
123
|
+
// Add a write watchpoint on $0400
|
|
124
|
+
emu.addWatchpoint(0x0400, 0x0400, Emulator::WP_WRITE);
|
|
125
|
+
|
|
126
|
+
// Write a small program at $0300 that writes to $0400 then loops:
|
|
127
|
+
// LDA #$42 ; A9 42
|
|
128
|
+
// STA $0400 ; 8D 00 04
|
|
129
|
+
// JMP $0305 ; 4C 05 03
|
|
130
|
+
emu.writeMemory(0x0300, 0xA9);
|
|
131
|
+
emu.writeMemory(0x0301, 0x42);
|
|
132
|
+
emu.writeMemory(0x0302, 0x8D);
|
|
133
|
+
emu.writeMemory(0x0303, 0x00);
|
|
134
|
+
emu.writeMemory(0x0304, 0x04);
|
|
135
|
+
emu.writeMemory(0x0305, 0x4C);
|
|
136
|
+
emu.writeMemory(0x0306, 0x05);
|
|
137
|
+
emu.writeMemory(0x0307, 0x03);
|
|
138
|
+
|
|
139
|
+
emu.setPC(0x0300);
|
|
140
|
+
emu.runCycles(100);
|
|
141
|
+
|
|
142
|
+
REQUIRE(emu.isWatchpointHit());
|
|
143
|
+
REQUIRE(emu.getWatchpointAddress() == 0x0400);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
TEST_CASE("clearWatchpoints removes all watchpoints", "[emulator][debug][watchpoint]") {
|
|
147
|
+
Emulator emu;
|
|
148
|
+
emu.init();
|
|
149
|
+
|
|
150
|
+
emu.addWatchpoint(0x0400, 0x0400, Emulator::WP_WRITE);
|
|
151
|
+
emu.clearWatchpoints();
|
|
152
|
+
|
|
153
|
+
// Write to $0400 via a small program
|
|
154
|
+
emu.writeMemory(0x0300, 0xA9); // LDA #$42
|
|
155
|
+
emu.writeMemory(0x0301, 0x42);
|
|
156
|
+
emu.writeMemory(0x0302, 0x8D); // STA $0400
|
|
157
|
+
emu.writeMemory(0x0303, 0x00);
|
|
158
|
+
emu.writeMemory(0x0304, 0x04);
|
|
159
|
+
emu.writeMemory(0x0305, 0x4C); // JMP $0305
|
|
160
|
+
emu.writeMemory(0x0306, 0x05);
|
|
161
|
+
emu.writeMemory(0x0307, 0x03);
|
|
162
|
+
|
|
163
|
+
emu.setPC(0x0300);
|
|
164
|
+
emu.runCycles(100);
|
|
165
|
+
|
|
166
|
+
REQUIRE_FALSE(emu.isWatchpointHit());
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
// Beam breakpoints
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
TEST_CASE("Beam breakpoint triggers at specified scanline", "[emulator][debug][beam]") {
|
|
174
|
+
Emulator emu;
|
|
175
|
+
emu.init();
|
|
176
|
+
|
|
177
|
+
// Add beam breakpoint at scanline 100, any hPos (-1 = wildcard)
|
|
178
|
+
int32_t id = emu.addBeamBreakpoint(100, -1);
|
|
179
|
+
REQUIRE(id >= 0);
|
|
180
|
+
|
|
181
|
+
// Run enough cycles for the beam to reach scanline 100
|
|
182
|
+
// 100 scanlines * 65 cycles/scanline = 6500 cycles minimum
|
|
183
|
+
emu.runCycles(17030); // One full frame
|
|
184
|
+
|
|
185
|
+
REQUIRE(emu.isBeamBreakpointHit());
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
TEST_CASE("removeBeamBreakpoint prevents beam break from triggering", "[emulator][debug][beam]") {
|
|
189
|
+
Emulator emu;
|
|
190
|
+
emu.init();
|
|
191
|
+
|
|
192
|
+
int32_t id = emu.addBeamBreakpoint(100, -1);
|
|
193
|
+
emu.removeBeamBreakpoint(id);
|
|
194
|
+
|
|
195
|
+
emu.runCycles(17030);
|
|
196
|
+
|
|
197
|
+
REQUIRE_FALSE(emu.isBeamBreakpointHit());
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
TEST_CASE("clearAllBeamBreakpoints clears all beam breaks", "[emulator][debug][beam]") {
|
|
201
|
+
Emulator emu;
|
|
202
|
+
emu.init();
|
|
203
|
+
|
|
204
|
+
emu.addBeamBreakpoint(50, -1);
|
|
205
|
+
emu.addBeamBreakpoint(100, -1);
|
|
206
|
+
emu.clearAllBeamBreakpoints();
|
|
207
|
+
|
|
208
|
+
emu.runCycles(17030);
|
|
209
|
+
|
|
210
|
+
REQUIRE_FALSE(emu.isBeamBreakpointHit());
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
// Trace log
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
|
|
217
|
+
TEST_CASE("Trace records entries when enabled", "[emulator][debug][trace]") {
|
|
218
|
+
Emulator emu;
|
|
219
|
+
emu.init();
|
|
220
|
+
|
|
221
|
+
emu.setTraceEnabled(true);
|
|
222
|
+
emu.runCycles(200);
|
|
223
|
+
|
|
224
|
+
REQUIRE(emu.getTraceCount() > 0);
|
|
225
|
+
REQUIRE(emu.getTraceBuffer() != nullptr);
|
|
226
|
+
REQUIRE(emu.getTraceCapacity() > 0);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
TEST_CASE("clearTrace resets trace count to zero", "[emulator][debug][trace]") {
|
|
230
|
+
Emulator emu;
|
|
231
|
+
emu.init();
|
|
232
|
+
|
|
233
|
+
emu.setTraceEnabled(true);
|
|
234
|
+
emu.runCycles(200);
|
|
235
|
+
REQUIRE(emu.getTraceCount() > 0);
|
|
236
|
+
|
|
237
|
+
emu.clearTrace();
|
|
238
|
+
REQUIRE(emu.getTraceCount() == 0);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
// Cycle profiling
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
|
|
245
|
+
TEST_CASE("Profiling records cycles when enabled", "[emulator][debug][profile]") {
|
|
246
|
+
Emulator emu;
|
|
247
|
+
emu.init();
|
|
248
|
+
|
|
249
|
+
emu.setProfileEnabled(true);
|
|
250
|
+
emu.clearProfile();
|
|
251
|
+
emu.runCycles(1000);
|
|
252
|
+
|
|
253
|
+
// Check that at least one address has non-zero cycle counts
|
|
254
|
+
const uint32_t* profile = emu.getProfileCycles();
|
|
255
|
+
REQUIRE(profile != nullptr);
|
|
256
|
+
|
|
257
|
+
bool hasNonZero = false;
|
|
258
|
+
for (int i = 0; i < 65536; i++) {
|
|
259
|
+
if (profile[i] > 0) {
|
|
260
|
+
hasNonZero = true;
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
REQUIRE(hasNonZero);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
TEST_CASE("clearProfile resets all profile cycle counts", "[emulator][debug][profile]") {
|
|
268
|
+
Emulator emu;
|
|
269
|
+
emu.init();
|
|
270
|
+
|
|
271
|
+
emu.setProfileEnabled(true);
|
|
272
|
+
emu.runCycles(1000);
|
|
273
|
+
emu.clearProfile();
|
|
274
|
+
|
|
275
|
+
const uint32_t* profile = emu.getProfileCycles();
|
|
276
|
+
bool allZero = true;
|
|
277
|
+
for (int i = 0; i < 65536; i++) {
|
|
278
|
+
if (profile[i] != 0) {
|
|
279
|
+
allZero = false;
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
REQUIRE(allZero);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
// Step Over
|
|
288
|
+
// ---------------------------------------------------------------------------
|
|
289
|
+
|
|
290
|
+
TEST_CASE("stepOver on JSR sets temp breakpoint after JSR", "[emulator][debug][stepover]") {
|
|
291
|
+
Emulator emu;
|
|
292
|
+
emu.init();
|
|
293
|
+
|
|
294
|
+
// Write JSR $0500 at $0400
|
|
295
|
+
emu.writeMemory(0x0400, 0x20); // JSR
|
|
296
|
+
emu.writeMemory(0x0401, 0x00); // low byte
|
|
297
|
+
emu.writeMemory(0x0402, 0x05); // high byte
|
|
298
|
+
// Write NOP at $0403
|
|
299
|
+
emu.writeMemory(0x0403, 0xEA);
|
|
300
|
+
|
|
301
|
+
// Write RTS at $0500 so the subroutine returns
|
|
302
|
+
emu.writeMemory(0x0500, 0x60); // RTS
|
|
303
|
+
|
|
304
|
+
emu.setPC(0x0400);
|
|
305
|
+
emu.setPaused(true);
|
|
306
|
+
|
|
307
|
+
uint16_t tempBp = emu.stepOver();
|
|
308
|
+
|
|
309
|
+
// stepOver on JSR should set temp breakpoint at PC+3 = $0403
|
|
310
|
+
REQUIRE(tempBp == 0x0403);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ---------------------------------------------------------------------------
|
|
314
|
+
// Step Out
|
|
315
|
+
// ---------------------------------------------------------------------------
|
|
316
|
+
|
|
317
|
+
TEST_CASE("stepOut sets temp breakpoint at return address from stack", "[emulator][debug][stepout]") {
|
|
318
|
+
Emulator emu;
|
|
319
|
+
emu.init();
|
|
320
|
+
|
|
321
|
+
// Simulate being inside a subroutine:
|
|
322
|
+
// Push a fake return address (e.g., $04FF, since RTS adds 1 -> $0500)
|
|
323
|
+
// onto the stack.
|
|
324
|
+
uint8_t sp = emu.getSP();
|
|
325
|
+
|
|
326
|
+
// Push high byte then low byte (as JSR does)
|
|
327
|
+
emu.writeMemory(0x0100 + sp, 0x04); // PCH
|
|
328
|
+
sp--;
|
|
329
|
+
emu.writeMemory(0x0100 + sp, 0xFF); // PCL
|
|
330
|
+
sp--;
|
|
331
|
+
emu.setSP(sp);
|
|
332
|
+
|
|
333
|
+
// Write a NOP loop at current PC
|
|
334
|
+
uint16_t pc = emu.getPC();
|
|
335
|
+
emu.writeMemory(pc, 0xEA); // NOP
|
|
336
|
+
emu.writeMemory(pc + 1, 0xEA); // NOP
|
|
337
|
+
|
|
338
|
+
emu.setPaused(true);
|
|
339
|
+
|
|
340
|
+
uint16_t tempBp = emu.stepOut();
|
|
341
|
+
|
|
342
|
+
// stepOut reads stack: (PCL,PCH) = ($FF,$04) -> $04FF + 1 = $0500
|
|
343
|
+
REQUIRE(tempBp == 0x0500);
|
|
344
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* test_emulator_disk.cpp - Integration tests for Emulator disk operations
|
|
3
|
+
*
|
|
4
|
+
* Tests disk insert, eject, blank disk creation, two-drive support,
|
|
5
|
+
* filename tracking, SmartPort, and error handling through the
|
|
6
|
+
* Emulator facade.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#define CATCH_CONFIG_MAIN
|
|
10
|
+
#include "catch.hpp"
|
|
11
|
+
|
|
12
|
+
#include "emulator.hpp"
|
|
13
|
+
|
|
14
|
+
#include <cstring>
|
|
15
|
+
#include <vector>
|
|
16
|
+
|
|
17
|
+
using namespace a2e;
|
|
18
|
+
|
|
19
|
+
// Standard DSK image size: 35 tracks * 16 sectors * 256 bytes
|
|
20
|
+
static constexpr size_t DSK_SIZE = 143360;
|
|
21
|
+
|
|
22
|
+
// Helper: create a valid-sized DSK image filled with a byte value
|
|
23
|
+
static std::vector<uint8_t> makeDskImage(uint8_t fill = 0x00) {
|
|
24
|
+
return std::vector<uint8_t>(DSK_SIZE, fill);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Insert disk
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
TEST_CASE("Emulator insertDisk succeeds with valid DSK data", "[emulator][disk]") {
|
|
32
|
+
Emulator emu;
|
|
33
|
+
emu.init();
|
|
34
|
+
|
|
35
|
+
auto img = makeDskImage();
|
|
36
|
+
bool result = emu.insertDisk(0, img.data(), img.size(), "test.dsk");
|
|
37
|
+
REQUIRE(result);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
TEST_CASE("Emulator getDiskFilename returns inserted filename", "[emulator][disk]") {
|
|
41
|
+
Emulator emu;
|
|
42
|
+
emu.init();
|
|
43
|
+
|
|
44
|
+
auto img = makeDskImage();
|
|
45
|
+
emu.insertDisk(0, img.data(), img.size(), "test.dsk");
|
|
46
|
+
|
|
47
|
+
const char* name = emu.getDiskFilename(0);
|
|
48
|
+
REQUIRE(name != nullptr);
|
|
49
|
+
REQUIRE(std::string(name) == "test.dsk");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Eject disk
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
TEST_CASE("Emulator ejectDisk clears the drive", "[emulator][disk]") {
|
|
57
|
+
Emulator emu;
|
|
58
|
+
emu.init();
|
|
59
|
+
|
|
60
|
+
auto img = makeDskImage();
|
|
61
|
+
emu.insertDisk(0, img.data(), img.size(), "test.dsk");
|
|
62
|
+
emu.ejectDisk(0);
|
|
63
|
+
|
|
64
|
+
// After ejecting, getDiskData should return nullptr
|
|
65
|
+
size_t dataSize = 0;
|
|
66
|
+
const uint8_t* data = emu.getDiskData(0, &dataSize);
|
|
67
|
+
REQUIRE(data == nullptr);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Blank disk
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
TEST_CASE("Emulator insertBlankDisk succeeds", "[emulator][disk]") {
|
|
75
|
+
Emulator emu;
|
|
76
|
+
emu.init();
|
|
77
|
+
|
|
78
|
+
bool result = emu.insertBlankDisk(0);
|
|
79
|
+
REQUIRE(result);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// Two drives
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
TEST_CASE("Emulator supports two drives simultaneously", "[emulator][disk]") {
|
|
87
|
+
Emulator emu;
|
|
88
|
+
emu.init();
|
|
89
|
+
|
|
90
|
+
auto img0 = makeDskImage(0x00);
|
|
91
|
+
auto img1 = makeDskImage(0xFF);
|
|
92
|
+
|
|
93
|
+
bool r0 = emu.insertDisk(0, img0.data(), img0.size(), "disk1.dsk");
|
|
94
|
+
bool r1 = emu.insertDisk(1, img1.data(), img1.size(), "disk2.dsk");
|
|
95
|
+
|
|
96
|
+
REQUIRE(r0);
|
|
97
|
+
REQUIRE(r1);
|
|
98
|
+
|
|
99
|
+
REQUIRE(std::string(emu.getDiskFilename(0)) == "disk1.dsk");
|
|
100
|
+
REQUIRE(std::string(emu.getDiskFilename(1)) == "disk2.dsk");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// getDisk reference
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
TEST_CASE("Emulator getDisk returns a valid Disk2Card reference", "[emulator][disk]") {
|
|
108
|
+
Emulator emu;
|
|
109
|
+
emu.init();
|
|
110
|
+
|
|
111
|
+
Disk2Card& disk = emu.getDisk();
|
|
112
|
+
REQUIRE(std::string(disk.getName()) == "Disk II");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// SmartPort
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
TEST_CASE("Emulator isSmartPortCardInstalled reflects slot configuration", "[emulator][disk][smartport]") {
|
|
120
|
+
Emulator emu;
|
|
121
|
+
emu.init();
|
|
122
|
+
|
|
123
|
+
// By default, SmartPort is not installed (no card in slot 7)
|
|
124
|
+
// The result depends on default configuration
|
|
125
|
+
// Just verify the method is callable without crashing
|
|
126
|
+
bool installed = emu.isSmartPortCardInstalled();
|
|
127
|
+
|
|
128
|
+
if (installed) {
|
|
129
|
+
// If SmartPort is installed, insertSmartPortImage should be callable
|
|
130
|
+
// (actual success depends on data validity)
|
|
131
|
+
std::vector<uint8_t> hdvData(512 * 280, 0x00); // Minimal ProDOS volume
|
|
132
|
+
// This may or may not succeed depending on image validation
|
|
133
|
+
emu.insertSmartPortImage(0, hdvData.data(), hdvData.size(), "test.hdv");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Either way, no crash
|
|
137
|
+
REQUIRE(true);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Invalid data
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
TEST_CASE("Emulator insertDisk with invalid size returns false", "[emulator][disk]") {
|
|
145
|
+
Emulator emu;
|
|
146
|
+
emu.init();
|
|
147
|
+
|
|
148
|
+
// A DSK image must be exactly 143360 bytes (or a valid NIB/WOZ size)
|
|
149
|
+
// An arbitrary size should be rejected
|
|
150
|
+
std::vector<uint8_t> badData(1000, 0x00);
|
|
151
|
+
bool result = emu.insertDisk(0, badData.data(), badData.size(), "bad.dsk");
|
|
152
|
+
REQUIRE_FALSE(result);
|
|
153
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* test_emulator_state.cpp - Integration tests for Emulator state serialization
|
|
3
|
+
*
|
|
4
|
+
* Tests exportState, importState, round-trip fidelity, CPU state preservation,
|
|
5
|
+
* and error handling for invalid data.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#define CATCH_CONFIG_MAIN
|
|
9
|
+
#include "catch.hpp"
|
|
10
|
+
|
|
11
|
+
#include "emulator.hpp"
|
|
12
|
+
|
|
13
|
+
#include <cstring>
|
|
14
|
+
#include <vector>
|
|
15
|
+
|
|
16
|
+
using namespace a2e;
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Export state
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
TEST_CASE("Emulator exportState returns non-null data with size > 0", "[emulator][state]") {
|
|
23
|
+
Emulator emu;
|
|
24
|
+
emu.init();
|
|
25
|
+
|
|
26
|
+
size_t size = 0;
|
|
27
|
+
const uint8_t* data = emu.exportState(&size);
|
|
28
|
+
|
|
29
|
+
REQUIRE(data != nullptr);
|
|
30
|
+
REQUIRE(size > 0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
TEST_CASE("Emulator exportState size is reasonable", "[emulator][state]") {
|
|
34
|
+
Emulator emu;
|
|
35
|
+
emu.init();
|
|
36
|
+
|
|
37
|
+
size_t size = 0;
|
|
38
|
+
emu.exportState(&size);
|
|
39
|
+
|
|
40
|
+
// State includes 128KB RAM (main + aux), 16KB LC RAM, CPU state,
|
|
41
|
+
// soft switches, disk state, etc. Should be at least 128KB.
|
|
42
|
+
REQUIRE(size >= 128 * 1024);
|
|
43
|
+
// But not unreasonably large (under 2MB)
|
|
44
|
+
REQUIRE(size < 2 * 1024 * 1024);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// Import state
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
TEST_CASE("Emulator importState accepts previously exported data", "[emulator][state]") {
|
|
52
|
+
Emulator emu;
|
|
53
|
+
emu.init();
|
|
54
|
+
|
|
55
|
+
size_t size = 0;
|
|
56
|
+
const uint8_t* data = emu.exportState(&size);
|
|
57
|
+
REQUIRE(data != nullptr);
|
|
58
|
+
|
|
59
|
+
// Copy the exported data since import may reset internal buffers
|
|
60
|
+
std::vector<uint8_t> stateCopy(data, data + size);
|
|
61
|
+
|
|
62
|
+
bool result = emu.importState(stateCopy.data(), stateCopy.size());
|
|
63
|
+
REQUIRE(result);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Round-trip: memory preserved
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
TEST_CASE("Emulator state round-trip preserves memory", "[emulator][state]") {
|
|
71
|
+
Emulator emu;
|
|
72
|
+
emu.init();
|
|
73
|
+
|
|
74
|
+
// Write known values to several RAM locations
|
|
75
|
+
emu.writeMemory(0x0300, 0xDE);
|
|
76
|
+
emu.writeMemory(0x0301, 0xAD);
|
|
77
|
+
emu.writeMemory(0x0302, 0xBE);
|
|
78
|
+
emu.writeMemory(0x0303, 0xEF);
|
|
79
|
+
|
|
80
|
+
// Export
|
|
81
|
+
size_t size = 0;
|
|
82
|
+
const uint8_t* data = emu.exportState(&size);
|
|
83
|
+
std::vector<uint8_t> stateCopy(data, data + size);
|
|
84
|
+
|
|
85
|
+
// Reset clears memory
|
|
86
|
+
emu.reset();
|
|
87
|
+
// Verify memory was cleared (reset re-initializes)
|
|
88
|
+
// Note: after reset, ROM re-initializes; RAM at $0300 should be cleared
|
|
89
|
+
REQUIRE(emu.readMemory(0x0300) != 0xDE);
|
|
90
|
+
|
|
91
|
+
// Import previously saved state
|
|
92
|
+
bool result = emu.importState(stateCopy.data(), stateCopy.size());
|
|
93
|
+
REQUIRE(result);
|
|
94
|
+
|
|
95
|
+
// Memory should be restored
|
|
96
|
+
REQUIRE(emu.readMemory(0x0300) == 0xDE);
|
|
97
|
+
REQUIRE(emu.readMemory(0x0301) == 0xAD);
|
|
98
|
+
REQUIRE(emu.readMemory(0x0302) == 0xBE);
|
|
99
|
+
REQUIRE(emu.readMemory(0x0303) == 0xEF);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
// Round-trip: CPU state preserved
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
|
|
106
|
+
TEST_CASE("Emulator state round-trip preserves CPU registers", "[emulator][state]") {
|
|
107
|
+
Emulator emu;
|
|
108
|
+
emu.init();
|
|
109
|
+
|
|
110
|
+
// Set specific CPU register values
|
|
111
|
+
emu.setA(0x42);
|
|
112
|
+
emu.setX(0x13);
|
|
113
|
+
emu.setY(0x77);
|
|
114
|
+
|
|
115
|
+
// Export
|
|
116
|
+
size_t size = 0;
|
|
117
|
+
const uint8_t* data = emu.exportState(&size);
|
|
118
|
+
std::vector<uint8_t> stateCopy(data, data + size);
|
|
119
|
+
|
|
120
|
+
// Reset changes registers
|
|
121
|
+
emu.reset();
|
|
122
|
+
REQUIRE(emu.getA() != 0x42);
|
|
123
|
+
|
|
124
|
+
// Import
|
|
125
|
+
bool result = emu.importState(stateCopy.data(), stateCopy.size());
|
|
126
|
+
REQUIRE(result);
|
|
127
|
+
|
|
128
|
+
REQUIRE(emu.getA() == 0x42);
|
|
129
|
+
REQUIRE(emu.getX() == 0x13);
|
|
130
|
+
REQUIRE(emu.getY() == 0x77);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// Invalid data
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
TEST_CASE("Emulator importState rejects garbage data", "[emulator][state]") {
|
|
138
|
+
Emulator emu;
|
|
139
|
+
emu.init();
|
|
140
|
+
|
|
141
|
+
// Create garbage data that does not have a valid magic header
|
|
142
|
+
std::vector<uint8_t> garbage(1024, 0xFF);
|
|
143
|
+
bool result = emu.importState(garbage.data(), garbage.size());
|
|
144
|
+
REQUIRE_FALSE(result);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
TEST_CASE("Emulator importState rejects too-small data", "[emulator][state]") {
|
|
148
|
+
Emulator emu;
|
|
149
|
+
emu.init();
|
|
150
|
+
|
|
151
|
+
// Data smaller than the minimum header (8 bytes for magic + version)
|
|
152
|
+
std::vector<uint8_t> tooSmall(4, 0x00);
|
|
153
|
+
bool result = emu.importState(tooSmall.data(), tooSmall.size());
|
|
154
|
+
REQUIRE_FALSE(result);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
TEST_CASE("Emulator importState rejects empty data", "[emulator][state]") {
|
|
158
|
+
Emulator emu;
|
|
159
|
+
emu.init();
|
|
160
|
+
|
|
161
|
+
bool result = emu.importState(nullptr, 0);
|
|
162
|
+
REQUIRE_FALSE(result);
|
|
163
|
+
}
|
|
Binary file
|
|
Binary file
|