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,203 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* cpu6502.hpp - Cycle-accurate 65C02 CPU emulation
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Mike Daley <michael_daley@icloud.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <array>
|
|
11
|
+
#include <cstdint>
|
|
12
|
+
#include <functional>
|
|
13
|
+
|
|
14
|
+
namespace a2e {
|
|
15
|
+
|
|
16
|
+
// CPU variant types
|
|
17
|
+
enum class CPUVariant { NMOS_6502, CMOS_65C02 };
|
|
18
|
+
|
|
19
|
+
// Status flag bits
|
|
20
|
+
enum StatusFlag : uint8_t {
|
|
21
|
+
FLAG_C = 0x01, // Carry
|
|
22
|
+
FLAG_Z = 0x02, // Zero
|
|
23
|
+
FLAG_I = 0x04, // Interrupt disable
|
|
24
|
+
FLAG_D = 0x08, // Decimal mode
|
|
25
|
+
FLAG_B = 0x10, // Break
|
|
26
|
+
FLAG_U = 0x20, // Unused (always 1)
|
|
27
|
+
FLAG_V = 0x40, // Overflow
|
|
28
|
+
FLAG_N = 0x80 // Negative
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
class CPU6502 {
|
|
32
|
+
public:
|
|
33
|
+
using ReadCallback = std::function<uint8_t(uint16_t)>;
|
|
34
|
+
using WriteCallback = std::function<void(uint16_t, uint8_t)>;
|
|
35
|
+
using IRQStatusCallback = std::function<bool()>;
|
|
36
|
+
|
|
37
|
+
CPU6502(ReadCallback read, WriteCallback write,
|
|
38
|
+
CPUVariant variant = CPUVariant::CMOS_65C02);
|
|
39
|
+
|
|
40
|
+
// Execution
|
|
41
|
+
void reset();
|
|
42
|
+
void executeInstruction();
|
|
43
|
+
void step(); // Single cycle step
|
|
44
|
+
bool isInstructionComplete() const { return cycleCount_ == 0; }
|
|
45
|
+
|
|
46
|
+
// Interrupts
|
|
47
|
+
void irq();
|
|
48
|
+
void nmi();
|
|
49
|
+
|
|
50
|
+
// Set callback for polling IRQ status (level-triggered IRQs like VIA)
|
|
51
|
+
void setIRQStatusCallback(IRQStatusCallback cb) { irqStatusCallback_ = std::move(cb); }
|
|
52
|
+
|
|
53
|
+
// Register access
|
|
54
|
+
uint8_t getA() const { return a_; }
|
|
55
|
+
uint8_t getX() const { return x_; }
|
|
56
|
+
uint8_t getY() const { return y_; }
|
|
57
|
+
uint8_t getSP() const { return sp_; }
|
|
58
|
+
uint16_t getPC() const { return pc_; }
|
|
59
|
+
uint8_t getP() const { return p_; }
|
|
60
|
+
|
|
61
|
+
void setA(uint8_t v) { a_ = v; }
|
|
62
|
+
void setX(uint8_t v) { x_ = v; }
|
|
63
|
+
void setY(uint8_t v) { y_ = v; }
|
|
64
|
+
void setSP(uint8_t v) { sp_ = v; }
|
|
65
|
+
void setPC(uint16_t v) { pc_ = v; }
|
|
66
|
+
void setP(uint8_t v) { p_ = v; }
|
|
67
|
+
|
|
68
|
+
// Flag access
|
|
69
|
+
bool getFlag(StatusFlag flag) const { return (p_ & flag) != 0; }
|
|
70
|
+
void setFlag(StatusFlag flag, bool value) {
|
|
71
|
+
if (value)
|
|
72
|
+
p_ |= flag;
|
|
73
|
+
else
|
|
74
|
+
p_ &= ~flag;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Cycle counting
|
|
78
|
+
// During instruction execution (cycleCount_ > 0), return the cycle of
|
|
79
|
+
// the last bus access — this matches when the 6502 performs the effective
|
|
80
|
+
// memory read/write that triggers soft switch callbacks.
|
|
81
|
+
// Between instructions (cycleCount_ == 0), return the plain total.
|
|
82
|
+
uint64_t getTotalCycles() const {
|
|
83
|
+
return cycleCount_ > 0 ? totalCycles_ + cycleCount_ - 1 : totalCycles_;
|
|
84
|
+
}
|
|
85
|
+
void resetCycleCount() { totalCycles_ = 0; }
|
|
86
|
+
void setTotalCycles(uint64_t cycles) { totalCycles_ = cycles; }
|
|
87
|
+
|
|
88
|
+
// Interrupt state access
|
|
89
|
+
bool isIRQPending() const { return irqPending_; }
|
|
90
|
+
bool isNMIPending() const { return nmiPending_; }
|
|
91
|
+
bool isNMIEdge() const { return nmiEdge_; }
|
|
92
|
+
|
|
93
|
+
// Debugging
|
|
94
|
+
std::string disassembleAt(uint16_t address) const;
|
|
95
|
+
uint8_t peekMemory(uint16_t address) const { return read_(address); }
|
|
96
|
+
|
|
97
|
+
private:
|
|
98
|
+
// Memory access
|
|
99
|
+
uint8_t read(uint16_t address);
|
|
100
|
+
void write(uint16_t address, uint8_t value);
|
|
101
|
+
uint8_t fetch();
|
|
102
|
+
uint16_t fetchWord();
|
|
103
|
+
|
|
104
|
+
// Stack operations
|
|
105
|
+
void push(uint8_t value);
|
|
106
|
+
uint8_t pop();
|
|
107
|
+
void pushWord(uint16_t value);
|
|
108
|
+
uint16_t popWord();
|
|
109
|
+
|
|
110
|
+
// Flag helpers
|
|
111
|
+
void updateNZ(uint8_t value);
|
|
112
|
+
void compare(uint8_t reg, uint8_t value);
|
|
113
|
+
|
|
114
|
+
// Addressing modes - return effective address
|
|
115
|
+
uint16_t addrImmediate();
|
|
116
|
+
uint16_t addrZeroPage();
|
|
117
|
+
uint16_t addrZeroPageX();
|
|
118
|
+
uint16_t addrZeroPageY();
|
|
119
|
+
uint16_t addrAbsolute();
|
|
120
|
+
uint16_t addrAbsoluteX(bool checkPage = true);
|
|
121
|
+
uint16_t addrAbsoluteY(bool checkPage = true);
|
|
122
|
+
uint16_t addrIndirect();
|
|
123
|
+
uint16_t addrIndexedIndirect(); // (zp,X)
|
|
124
|
+
uint16_t addrIndirectIndexed(bool checkPage = true); // (zp),Y
|
|
125
|
+
uint16_t addrIndirectZP(); // 65C02: (zp)
|
|
126
|
+
|
|
127
|
+
// Instruction implementations
|
|
128
|
+
void opADC(uint8_t value);
|
|
129
|
+
void opSBC(uint8_t value);
|
|
130
|
+
void opAND(uint8_t value);
|
|
131
|
+
void opORA(uint8_t value);
|
|
132
|
+
void opEOR(uint8_t value);
|
|
133
|
+
void opCMP(uint8_t value);
|
|
134
|
+
void opCPX(uint8_t value);
|
|
135
|
+
void opCPY(uint8_t value);
|
|
136
|
+
void opBIT(uint8_t value);
|
|
137
|
+
void opASL_A();
|
|
138
|
+
uint8_t opASL(uint8_t value);
|
|
139
|
+
void opLSR_A();
|
|
140
|
+
uint8_t opLSR(uint8_t value);
|
|
141
|
+
void opROL_A();
|
|
142
|
+
uint8_t opROL(uint8_t value);
|
|
143
|
+
void opROR_A();
|
|
144
|
+
uint8_t opROR(uint8_t value);
|
|
145
|
+
uint8_t opINC(uint8_t value);
|
|
146
|
+
uint8_t opDEC(uint8_t value);
|
|
147
|
+
|
|
148
|
+
// Branch helper
|
|
149
|
+
void branch(bool condition);
|
|
150
|
+
|
|
151
|
+
// Execute the current opcode
|
|
152
|
+
void executeOpcode(uint8_t opcode);
|
|
153
|
+
|
|
154
|
+
// Registers
|
|
155
|
+
uint8_t a_ = 0; // Accumulator
|
|
156
|
+
uint8_t x_ = 0; // X index
|
|
157
|
+
uint8_t y_ = 0; // Y index
|
|
158
|
+
uint8_t sp_ = 0xFF; // Stack pointer
|
|
159
|
+
uint16_t pc_ = 0; // Program counter
|
|
160
|
+
uint8_t p_ = 0x24; // Status register (U always set, I set on reset)
|
|
161
|
+
|
|
162
|
+
// Cycle tracking
|
|
163
|
+
int cycleCount_ = 0;
|
|
164
|
+
uint64_t totalCycles_ = 0;
|
|
165
|
+
bool pageCrossed_ = false;
|
|
166
|
+
|
|
167
|
+
// Interrupt state
|
|
168
|
+
bool irqPending_ = false;
|
|
169
|
+
bool nmiPending_ = false;
|
|
170
|
+
bool nmiEdge_ = false;
|
|
171
|
+
|
|
172
|
+
// Memory callbacks
|
|
173
|
+
ReadCallback read_;
|
|
174
|
+
WriteCallback write_;
|
|
175
|
+
|
|
176
|
+
// IRQ status callback for level-triggered IRQs (VIA)
|
|
177
|
+
IRQStatusCallback irqStatusCallback_;
|
|
178
|
+
|
|
179
|
+
// CPU variant
|
|
180
|
+
CPUVariant variant_;
|
|
181
|
+
|
|
182
|
+
// Opcode table for cycle counts
|
|
183
|
+
static constexpr std::array<uint8_t, 256> CYCLE_TABLE = {{
|
|
184
|
+
7, 6, 2, 2, 5, 3, 5, 5, 3, 2, 2, 2, 6, 4, 6, 5, // 00-0F
|
|
185
|
+
2, 5, 5, 2, 5, 4, 6, 5, 2, 4, 2, 2, 6, 4, 6, 5, // 10-1F
|
|
186
|
+
6, 6, 2, 2, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 5, // 20-2F
|
|
187
|
+
2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 2, 2, 4, 4, 6, 5, // 30-3F
|
|
188
|
+
6, 6, 2, 2, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 5, // 40-4F
|
|
189
|
+
2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 3, 2, 8, 4, 6, 5, // 50-5F
|
|
190
|
+
6, 6, 2, 2, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 5, // 60-6F
|
|
191
|
+
2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 4, 2, 6, 4, 6, 5, // 70-7F
|
|
192
|
+
3, 6, 2, 2, 3, 3, 3, 5, 2, 2, 2, 2, 4, 4, 4, 5, // 80-8F
|
|
193
|
+
2, 6, 5, 2, 4, 4, 4, 5, 2, 5, 2, 2, 4, 5, 5, 5, // 90-9F
|
|
194
|
+
2, 6, 2, 2, 3, 3, 3, 5, 2, 2, 2, 2, 4, 4, 4, 5, // A0-AF
|
|
195
|
+
2, 5, 5, 2, 4, 4, 4, 5, 2, 4, 2, 2, 4, 4, 4, 5, // B0-BF
|
|
196
|
+
2, 6, 2, 2, 3, 3, 5, 5, 2, 2, 2, 3, 4, 4, 6, 5, // C0-CF
|
|
197
|
+
2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 3, 3, 4, 4, 7, 5, // D0-DF
|
|
198
|
+
2, 6, 2, 2, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 5, // E0-EF
|
|
199
|
+
2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 4, 2, 4, 4, 7, 5 // F0-FF
|
|
200
|
+
}};
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
} // namespace a2e
|
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* condition_evaluator.cpp - Expression parser for debugger breakpoint conditions and watches
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Mike Daley <michael_daley@icloud.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include "condition_evaluator.hpp"
|
|
9
|
+
#include "../emulator.hpp"
|
|
10
|
+
#include <cctype>
|
|
11
|
+
#include <cmath>
|
|
12
|
+
#include <cstdio>
|
|
13
|
+
|
|
14
|
+
namespace a2e {
|
|
15
|
+
|
|
16
|
+
// Decode 5-byte Applesoft float to int32_t (truncated)
|
|
17
|
+
static int32_t decodeApplesoftFloat(const Emulator& emu, uint16_t addr) {
|
|
18
|
+
uint8_t exp = emu.peekMemory(addr);
|
|
19
|
+
if (exp == 0) return 0;
|
|
20
|
+
|
|
21
|
+
int sign = (emu.peekMemory(addr + 1) & 0x80) ? -1 : 1;
|
|
22
|
+
double mantissa = 1.0;
|
|
23
|
+
mantissa += (emu.peekMemory(addr + 1) & 0x7F) / 128.0;
|
|
24
|
+
mantissa += emu.peekMemory(addr + 2) / 32768.0;
|
|
25
|
+
mantissa += emu.peekMemory(addr + 3) / 8388608.0;
|
|
26
|
+
mantissa += emu.peekMemory(addr + 4) / 2147483648.0;
|
|
27
|
+
|
|
28
|
+
int actualExp = exp - 129;
|
|
29
|
+
double value = sign * mantissa * pow(2.0, actualExp);
|
|
30
|
+
return static_cast<int32_t>(value);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Read a BASIC simple variable value by its encoded name bytes
|
|
34
|
+
// Walks VARTAB to ARYTAB looking for matching name
|
|
35
|
+
static int32_t readBasicVariable(const Emulator& emu, uint8_t nameB1, uint8_t nameB2) {
|
|
36
|
+
uint16_t vartab = emu.peekMemory(0x69) | (emu.peekMemory(0x6A) << 8);
|
|
37
|
+
uint16_t arytab = emu.peekMemory(0x6B) | (emu.peekMemory(0x6C) << 8);
|
|
38
|
+
|
|
39
|
+
if (vartab == 0 || arytab == 0 || vartab >= arytab) return 0;
|
|
40
|
+
|
|
41
|
+
uint16_t addr = vartab;
|
|
42
|
+
while (addr < arytab) {
|
|
43
|
+
uint8_t b1 = emu.peekMemory(addr);
|
|
44
|
+
uint8_t b2 = emu.peekMemory(addr + 1);
|
|
45
|
+
|
|
46
|
+
if (b1 == nameB1 && b2 == nameB2) {
|
|
47
|
+
// Found it - determine type from high bits
|
|
48
|
+
bool isInteger = (b1 & 0x80) && (b2 & 0x80);
|
|
49
|
+
if (isInteger) {
|
|
50
|
+
uint8_t high = emu.peekMemory(addr + 2);
|
|
51
|
+
uint8_t low = emu.peekMemory(addr + 3);
|
|
52
|
+
int16_t val = static_cast<int16_t>((high << 8) | low);
|
|
53
|
+
return static_cast<int32_t>(val);
|
|
54
|
+
}
|
|
55
|
+
// Real number (5-byte float)
|
|
56
|
+
return decodeApplesoftFloat(emu, addr + 2);
|
|
57
|
+
}
|
|
58
|
+
addr += 7; // Each simple var is 7 bytes
|
|
59
|
+
}
|
|
60
|
+
return 0; // Variable not found
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Read a BASIC array element by encoded name bytes and flat index
|
|
64
|
+
static int32_t readBasicArrayElement(const Emulator& emu, uint8_t nameB1, uint8_t nameB2, int32_t flatIndex) {
|
|
65
|
+
uint16_t arytab = emu.peekMemory(0x6B) | (emu.peekMemory(0x6C) << 8);
|
|
66
|
+
uint16_t strend = emu.peekMemory(0x6D) | (emu.peekMemory(0x6E) << 8);
|
|
67
|
+
|
|
68
|
+
if (arytab == 0 || strend == 0 || arytab >= strend) return 0;
|
|
69
|
+
|
|
70
|
+
uint16_t addr = arytab;
|
|
71
|
+
while (addr < strend) {
|
|
72
|
+
uint8_t b1 = emu.peekMemory(addr);
|
|
73
|
+
uint8_t b2 = emu.peekMemory(addr + 1);
|
|
74
|
+
uint16_t totalSize = emu.peekMemory(addr + 2) | (emu.peekMemory(addr + 3) << 8);
|
|
75
|
+
|
|
76
|
+
if (b1 == nameB1 && b2 == nameB2) {
|
|
77
|
+
uint8_t numDims = emu.peekMemory(addr + 4);
|
|
78
|
+
uint16_t dataStart = addr + 5 + numDims * 2;
|
|
79
|
+
|
|
80
|
+
bool isInteger = (b1 & 0x80) && (b2 & 0x80);
|
|
81
|
+
int elementSize = isInteger ? 2 : 5;
|
|
82
|
+
|
|
83
|
+
uint16_t elemAddr = dataStart + flatIndex * elementSize;
|
|
84
|
+
if (isInteger) {
|
|
85
|
+
uint8_t high = emu.peekMemory(elemAddr);
|
|
86
|
+
uint8_t low = emu.peekMemory(elemAddr + 1);
|
|
87
|
+
int16_t val = static_cast<int16_t>((high << 8) | low);
|
|
88
|
+
return static_cast<int32_t>(val);
|
|
89
|
+
}
|
|
90
|
+
return decodeApplesoftFloat(emu, elemAddr);
|
|
91
|
+
}
|
|
92
|
+
addr += totalSize;
|
|
93
|
+
}
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Read a BASIC 2D array element by encoded name bytes and two indices
|
|
98
|
+
// Applesoft stores dimensions in reverse order: DIM A(M,N) stores [N+1, M+1]
|
|
99
|
+
// Element A(i,j) flat index = i * dim2size + j (dim2size is the FIRST stored dimension)
|
|
100
|
+
static int32_t readBasicArrayElement2D(const Emulator& emu, uint8_t nameB1, uint8_t nameB2, int32_t idx1, int32_t idx2) {
|
|
101
|
+
uint16_t arytab = emu.peekMemory(0x6B) | (emu.peekMemory(0x6C) << 8);
|
|
102
|
+
uint16_t strend = emu.peekMemory(0x6D) | (emu.peekMemory(0x6E) << 8);
|
|
103
|
+
|
|
104
|
+
if (arytab == 0 || strend == 0 || arytab >= strend) return 0;
|
|
105
|
+
|
|
106
|
+
uint16_t addr = arytab;
|
|
107
|
+
while (addr < strend) {
|
|
108
|
+
uint8_t b1 = emu.peekMemory(addr);
|
|
109
|
+
uint8_t b2 = emu.peekMemory(addr + 1);
|
|
110
|
+
uint16_t totalSize = emu.peekMemory(addr + 2) | (emu.peekMemory(addr + 3) << 8);
|
|
111
|
+
|
|
112
|
+
if (b1 == nameB1 && b2 == nameB2) {
|
|
113
|
+
uint8_t numDims = emu.peekMemory(addr + 4);
|
|
114
|
+
if (numDims < 2) return 0;
|
|
115
|
+
|
|
116
|
+
// Applesoft stores arrays in column-major order with reversed dimensions.
|
|
117
|
+
// For DIM A(M,N): dims[0]=N+1, dims[1]=M+1
|
|
118
|
+
// Flat index for A(i,j) = j * dims[1] + i
|
|
119
|
+
uint16_t dim1Size = (emu.peekMemory(addr + 7) << 8) | emu.peekMemory(addr + 8);
|
|
120
|
+
|
|
121
|
+
int32_t flatIndex = idx2 * dim1Size + idx1;
|
|
122
|
+
uint16_t dataStart = addr + 5 + numDims * 2;
|
|
123
|
+
|
|
124
|
+
bool isInteger = (b1 & 0x80) && (b2 & 0x80);
|
|
125
|
+
int elementSize = isInteger ? 2 : 5;
|
|
126
|
+
|
|
127
|
+
uint16_t elemAddr = dataStart + flatIndex * elementSize;
|
|
128
|
+
if (isInteger) {
|
|
129
|
+
uint8_t high = emu.peekMemory(elemAddr);
|
|
130
|
+
uint8_t low = emu.peekMemory(elemAddr + 1);
|
|
131
|
+
int16_t val = static_cast<int16_t>((high << 8) | low);
|
|
132
|
+
return static_cast<int32_t>(val);
|
|
133
|
+
}
|
|
134
|
+
return decodeApplesoftFloat(emu, elemAddr);
|
|
135
|
+
}
|
|
136
|
+
addr += totalSize;
|
|
137
|
+
}
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
char ConditionEvaluator::errorBuf_[128] = "";
|
|
142
|
+
|
|
143
|
+
int ConditionEvaluator::tokenize(const char* expr, Token* tokens, int maxTokens) {
|
|
144
|
+
int count = 0;
|
|
145
|
+
int i = 0;
|
|
146
|
+
int len = static_cast<int>(strlen(expr));
|
|
147
|
+
|
|
148
|
+
while (i < len && count < maxTokens - 1) {
|
|
149
|
+
// Skip whitespace
|
|
150
|
+
if (expr[i] == ' ' || expr[i] == '\t') {
|
|
151
|
+
i++;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Two-char operators
|
|
156
|
+
if (i + 1 < len) {
|
|
157
|
+
char c0 = expr[i], c1 = expr[i + 1];
|
|
158
|
+
if ((c0 == '=' && c1 == '=') || (c0 == '!' && c1 == '=') ||
|
|
159
|
+
(c0 == '>' && c1 == '=') || (c0 == '<' && c1 == '=') ||
|
|
160
|
+
(c0 == '&' && c1 == '&') || (c0 == '|' && c1 == '|')) {
|
|
161
|
+
tokens[count].type = TOK_OP2;
|
|
162
|
+
tokens[count].strVal[0] = c0;
|
|
163
|
+
tokens[count].strVal[1] = c1;
|
|
164
|
+
tokens[count].strVal[2] = '\0';
|
|
165
|
+
count++;
|
|
166
|
+
i += 2;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Single-char operators (including comma for function args)
|
|
172
|
+
char ch = expr[i];
|
|
173
|
+
if (ch == '<' || ch == '>' || ch == '(' || ch == ')' ||
|
|
174
|
+
ch == '+' || ch == '-' || ch == '*' || ch == ',') {
|
|
175
|
+
tokens[count].type = TOK_OP1;
|
|
176
|
+
tokens[count].strVal[0] = ch;
|
|
177
|
+
tokens[count].strVal[1] = '\0';
|
|
178
|
+
count++;
|
|
179
|
+
i++;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Hex literal: #$XX or $XXXX
|
|
184
|
+
if (ch == '#' && i + 1 < len && expr[i + 1] == '$') {
|
|
185
|
+
i += 2;
|
|
186
|
+
int32_t val = 0;
|
|
187
|
+
while (i < len && isxdigit(static_cast<unsigned char>(expr[i]))) {
|
|
188
|
+
int digit;
|
|
189
|
+
char c = expr[i];
|
|
190
|
+
if (c >= '0' && c <= '9') digit = c - '0';
|
|
191
|
+
else if (c >= 'a' && c <= 'f') digit = c - 'a' + 10;
|
|
192
|
+
else digit = c - 'A' + 10;
|
|
193
|
+
val = (val << 4) | digit;
|
|
194
|
+
i++;
|
|
195
|
+
}
|
|
196
|
+
tokens[count].type = TOK_NUM;
|
|
197
|
+
tokens[count].numVal = val;
|
|
198
|
+
count++;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (ch == '$') {
|
|
203
|
+
i++;
|
|
204
|
+
int32_t val = 0;
|
|
205
|
+
while (i < len && isxdigit(static_cast<unsigned char>(expr[i]))) {
|
|
206
|
+
int digit;
|
|
207
|
+
char c = expr[i];
|
|
208
|
+
if (c >= '0' && c <= '9') digit = c - '0';
|
|
209
|
+
else if (c >= 'a' && c <= 'f') digit = c - 'a' + 10;
|
|
210
|
+
else digit = c - 'A' + 10;
|
|
211
|
+
val = (val << 4) | digit;
|
|
212
|
+
i++;
|
|
213
|
+
}
|
|
214
|
+
tokens[count].type = TOK_NUM;
|
|
215
|
+
tokens[count].numVal = val;
|
|
216
|
+
count++;
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Decimal literal
|
|
221
|
+
if (isdigit(static_cast<unsigned char>(ch))) {
|
|
222
|
+
int32_t val = 0;
|
|
223
|
+
while (i < len && isdigit(static_cast<unsigned char>(expr[i]))) {
|
|
224
|
+
val = val * 10 + (expr[i] - '0');
|
|
225
|
+
i++;
|
|
226
|
+
}
|
|
227
|
+
tokens[count].type = TOK_NUM;
|
|
228
|
+
tokens[count].numVal = val;
|
|
229
|
+
count++;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Identifiers
|
|
234
|
+
if (isalpha(static_cast<unsigned char>(ch)) || ch == '_') {
|
|
235
|
+
int start = i;
|
|
236
|
+
while (i < len && (isalnum(static_cast<unsigned char>(expr[i])) || expr[i] == '_')) {
|
|
237
|
+
i++;
|
|
238
|
+
}
|
|
239
|
+
int idLen = i - start;
|
|
240
|
+
if (idLen > 7) idLen = 7; // Truncate to fit strVal
|
|
241
|
+
tokens[count].type = TOK_ID;
|
|
242
|
+
for (int j = 0; j < idLen; j++) {
|
|
243
|
+
tokens[count].strVal[j] = toupper(static_cast<unsigned char>(expr[start + j]));
|
|
244
|
+
}
|
|
245
|
+
tokens[count].strVal[idLen] = '\0';
|
|
246
|
+
count++;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Unknown char, skip
|
|
251
|
+
i++;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// End sentinel
|
|
255
|
+
tokens[count].type = TOK_END;
|
|
256
|
+
return count;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
bool ConditionEvaluator::evaluate(const char* expr, const Emulator& emu) {
|
|
260
|
+
errorBuf_[0] = '\0';
|
|
261
|
+
ParseState s;
|
|
262
|
+
s.count = tokenize(expr, s.tokens, MAX_TOKENS);
|
|
263
|
+
s.pos = 0;
|
|
264
|
+
s.emu = &emu;
|
|
265
|
+
|
|
266
|
+
bool result = parseOr(s);
|
|
267
|
+
return result;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
int32_t ConditionEvaluator::evaluateNumeric(const char* expr, const Emulator& emu) {
|
|
271
|
+
errorBuf_[0] = '\0';
|
|
272
|
+
ParseState s;
|
|
273
|
+
s.count = tokenize(expr, s.tokens, MAX_TOKENS);
|
|
274
|
+
s.pos = 0;
|
|
275
|
+
s.emu = &emu;
|
|
276
|
+
|
|
277
|
+
return parseExpr(s);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const char* ConditionEvaluator::getLastError() {
|
|
281
|
+
return errorBuf_;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
bool ConditionEvaluator::parseOr(ParseState& s) {
|
|
285
|
+
bool left = parseAnd(s);
|
|
286
|
+
while (s.pos < s.count && s.tokens[s.pos].type == TOK_OP2 &&
|
|
287
|
+
s.tokens[s.pos].strVal[0] == '|') {
|
|
288
|
+
s.pos++;
|
|
289
|
+
bool right = parseAnd(s);
|
|
290
|
+
left = left || right;
|
|
291
|
+
}
|
|
292
|
+
return left;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
bool ConditionEvaluator::parseAnd(ParseState& s) {
|
|
296
|
+
bool left = parseComparison(s);
|
|
297
|
+
while (s.pos < s.count && s.tokens[s.pos].type == TOK_OP2 &&
|
|
298
|
+
s.tokens[s.pos].strVal[0] == '&') {
|
|
299
|
+
s.pos++;
|
|
300
|
+
bool right = parseComparison(s);
|
|
301
|
+
left = left && right;
|
|
302
|
+
}
|
|
303
|
+
return left;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
bool ConditionEvaluator::parseComparison(ParseState& s) {
|
|
307
|
+
int32_t left = parseExpr(s);
|
|
308
|
+
|
|
309
|
+
if (s.pos < s.count) {
|
|
310
|
+
const Token& tok = s.tokens[s.pos];
|
|
311
|
+
if (tok.type == TOK_OP2 || tok.type == TOK_OP1) {
|
|
312
|
+
const char* op = tok.strVal;
|
|
313
|
+
if (strcmp(op, "==") == 0) { s.pos++; return left == parseExpr(s); }
|
|
314
|
+
if (strcmp(op, "!=") == 0) { s.pos++; return left != parseExpr(s); }
|
|
315
|
+
if (strcmp(op, ">=") == 0) { s.pos++; return left >= parseExpr(s); }
|
|
316
|
+
if (strcmp(op, "<=") == 0) { s.pos++; return left <= parseExpr(s); }
|
|
317
|
+
if (strcmp(op, ">") == 0) { s.pos++; return left > parseExpr(s); }
|
|
318
|
+
if (strcmp(op, "<") == 0) { s.pos++; return left < parseExpr(s); }
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return left != 0; // Truthy if no comparison
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
int32_t ConditionEvaluator::parseExpr(ParseState& s) {
|
|
326
|
+
int32_t val = parseAtom(s);
|
|
327
|
+
|
|
328
|
+
while (s.pos < s.count) {
|
|
329
|
+
const Token& tok = s.tokens[s.pos];
|
|
330
|
+
if (tok.type == TOK_OP1) {
|
|
331
|
+
char op = tok.strVal[0];
|
|
332
|
+
if (op == '+') { s.pos++; val += parseAtom(s); }
|
|
333
|
+
else if (op == '-') { s.pos++; val -= parseAtom(s); }
|
|
334
|
+
else if (op == '*') { s.pos++; val *= parseAtom(s); }
|
|
335
|
+
else break;
|
|
336
|
+
} else break;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return val;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
int32_t ConditionEvaluator::parseAtom(ParseState& s) {
|
|
343
|
+
if (s.pos >= s.count) return 0;
|
|
344
|
+
|
|
345
|
+
const Token& t = s.tokens[s.pos];
|
|
346
|
+
|
|
347
|
+
// Number literal
|
|
348
|
+
if (t.type == TOK_NUM) {
|
|
349
|
+
s.pos++;
|
|
350
|
+
return t.numVal;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Parenthesized expression
|
|
354
|
+
if (t.type == TOK_OP1 && t.strVal[0] == '(') {
|
|
355
|
+
s.pos++;
|
|
356
|
+
int32_t val = static_cast<int32_t>(parseOr(s));
|
|
357
|
+
if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 &&
|
|
358
|
+
s.tokens[s.pos].strVal[0] == ')') {
|
|
359
|
+
s.pos++;
|
|
360
|
+
}
|
|
361
|
+
return val;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Identifier
|
|
365
|
+
if (t.type == TOK_ID) {
|
|
366
|
+
s.pos++;
|
|
367
|
+
const char* id = t.strVal;
|
|
368
|
+
const Emulator& emu = *s.emu;
|
|
369
|
+
|
|
370
|
+
// BV(b1,b2) - read BASIC simple variable value by encoded name bytes
|
|
371
|
+
if (strcmp(id, "BV") == 0 && s.pos < s.count &&
|
|
372
|
+
s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == '(') {
|
|
373
|
+
s.pos++; // skip '('
|
|
374
|
+
int32_t b1 = parseExpr(s);
|
|
375
|
+
if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == ',') s.pos++;
|
|
376
|
+
int32_t b2 = parseExpr(s);
|
|
377
|
+
if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 &&
|
|
378
|
+
s.tokens[s.pos].strVal[0] == ')') {
|
|
379
|
+
s.pos++;
|
|
380
|
+
}
|
|
381
|
+
return readBasicVariable(emu, static_cast<uint8_t>(b1), static_cast<uint8_t>(b2));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// BA(b1,b2,idx) - read BASIC array element by encoded name bytes and flat index
|
|
385
|
+
if (strcmp(id, "BA") == 0 && s.pos < s.count &&
|
|
386
|
+
s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == '(') {
|
|
387
|
+
s.pos++; // skip '('
|
|
388
|
+
int32_t b1 = parseExpr(s);
|
|
389
|
+
if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == ',') s.pos++;
|
|
390
|
+
int32_t b2 = parseExpr(s);
|
|
391
|
+
if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == ',') s.pos++;
|
|
392
|
+
int32_t idx = parseExpr(s);
|
|
393
|
+
if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 &&
|
|
394
|
+
s.tokens[s.pos].strVal[0] == ')') {
|
|
395
|
+
s.pos++;
|
|
396
|
+
}
|
|
397
|
+
return readBasicArrayElement(emu, static_cast<uint8_t>(b1), static_cast<uint8_t>(b2), idx);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// BA2(b1,b2,i1,i2) - read BASIC 2D array element
|
|
401
|
+
if (strcmp(id, "BA2") == 0 && s.pos < s.count &&
|
|
402
|
+
s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == '(') {
|
|
403
|
+
s.pos++; // skip '('
|
|
404
|
+
int32_t b1 = parseExpr(s);
|
|
405
|
+
if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == ',') s.pos++;
|
|
406
|
+
int32_t b2 = parseExpr(s);
|
|
407
|
+
if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == ',') s.pos++;
|
|
408
|
+
int32_t i1 = parseExpr(s);
|
|
409
|
+
if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == ',') s.pos++;
|
|
410
|
+
int32_t i2 = parseExpr(s);
|
|
411
|
+
if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 &&
|
|
412
|
+
s.tokens[s.pos].strVal[0] == ')') {
|
|
413
|
+
s.pos++;
|
|
414
|
+
}
|
|
415
|
+
return readBasicArrayElement2D(emu, static_cast<uint8_t>(b1), static_cast<uint8_t>(b2), i1, i2);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// PEEK(addr) - read byte
|
|
419
|
+
if (strcmp(id, "PEEK") == 0 && s.pos < s.count &&
|
|
420
|
+
s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == '(') {
|
|
421
|
+
s.pos++;
|
|
422
|
+
int32_t addr = parseExpr(s);
|
|
423
|
+
if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 &&
|
|
424
|
+
s.tokens[s.pos].strVal[0] == ')') {
|
|
425
|
+
s.pos++;
|
|
426
|
+
}
|
|
427
|
+
return emu.peekMemory(static_cast<uint16_t>(addr & 0xFFFF));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// DEEK(addr) - read 16-bit word (little-endian)
|
|
431
|
+
if (strcmp(id, "DEEK") == 0 && s.pos < s.count &&
|
|
432
|
+
s.tokens[s.pos].type == TOK_OP1 && s.tokens[s.pos].strVal[0] == '(') {
|
|
433
|
+
s.pos++;
|
|
434
|
+
int32_t addr = parseExpr(s);
|
|
435
|
+
if (s.pos < s.count && s.tokens[s.pos].type == TOK_OP1 &&
|
|
436
|
+
s.tokens[s.pos].strVal[0] == ')') {
|
|
437
|
+
s.pos++;
|
|
438
|
+
}
|
|
439
|
+
uint8_t lo = emu.peekMemory(static_cast<uint16_t>(addr & 0xFFFF));
|
|
440
|
+
uint8_t hi = emu.peekMemory(static_cast<uint16_t>((addr + 1) & 0xFFFF));
|
|
441
|
+
return (hi << 8) | lo;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Registers
|
|
445
|
+
if (strcmp(id, "A") == 0) return emu.getA();
|
|
446
|
+
if (strcmp(id, "X") == 0) return emu.getX();
|
|
447
|
+
if (strcmp(id, "Y") == 0) return emu.getY();
|
|
448
|
+
if (strcmp(id, "SP") == 0) return emu.getSP();
|
|
449
|
+
if (strcmp(id, "PC") == 0) return emu.getPC();
|
|
450
|
+
if (strcmp(id, "P") == 0) return emu.getP();
|
|
451
|
+
|
|
452
|
+
// Individual flags
|
|
453
|
+
if (strcmp(id, "C") == 0) return (emu.getP() & 0x01) ? 1 : 0;
|
|
454
|
+
if (strcmp(id, "Z") == 0) return (emu.getP() & 0x02) ? 1 : 0;
|
|
455
|
+
if (strcmp(id, "I") == 0) return (emu.getP() & 0x04) ? 1 : 0;
|
|
456
|
+
if (strcmp(id, "D") == 0) return (emu.getP() & 0x08) ? 1 : 0;
|
|
457
|
+
if (strcmp(id, "B") == 0) return (emu.getP() & 0x10) ? 1 : 0;
|
|
458
|
+
if (strcmp(id, "V") == 0) return (emu.getP() & 0x40) ? 1 : 0;
|
|
459
|
+
if (strcmp(id, "N") == 0) return (emu.getP() & 0x80) ? 1 : 0;
|
|
460
|
+
|
|
461
|
+
snprintf(errorBuf_, sizeof(errorBuf_), "Unknown identifier: %s", id);
|
|
462
|
+
return 0;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Fallback
|
|
466
|
+
s.pos++;
|
|
467
|
+
return 0;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
} // namespace a2e
|