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,1609 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* cpu6502.cpp - Cycle-accurate 65C02 CPU emulation implementation
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Mike Daley <michael_daley@icloud.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include "cpu6502.hpp"
|
|
9
|
+
#include <iomanip>
|
|
10
|
+
#include <sstream>
|
|
11
|
+
|
|
12
|
+
namespace a2e {
|
|
13
|
+
|
|
14
|
+
CPU6502::CPU6502(ReadCallback read, WriteCallback write, CPUVariant variant)
|
|
15
|
+
: read_(std::move(read)), write_(std::move(write)), variant_(variant) {}
|
|
16
|
+
|
|
17
|
+
void CPU6502::reset() {
|
|
18
|
+
a_ = 0;
|
|
19
|
+
x_ = 0;
|
|
20
|
+
y_ = 0;
|
|
21
|
+
sp_ = 0xFD;
|
|
22
|
+
p_ = 0x24; // I flag set, U always set
|
|
23
|
+
|
|
24
|
+
// Read reset vector
|
|
25
|
+
uint16_t lo = read_(0xFFFC);
|
|
26
|
+
uint16_t hi = read_(0xFFFD);
|
|
27
|
+
pc_ = lo | (hi << 8);
|
|
28
|
+
|
|
29
|
+
cycleCount_ = 0;
|
|
30
|
+
totalCycles_ += 7; // Reset takes 7 cycles
|
|
31
|
+
|
|
32
|
+
irqPending_ = false;
|
|
33
|
+
nmiPending_ = false;
|
|
34
|
+
nmiEdge_ = false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
void CPU6502::executeInstruction() {
|
|
38
|
+
// Note: VIA IRQs are handled via edge-triggered callback in checkIRQ()
|
|
39
|
+
// The callback sets irqPending_ when interrupt first becomes active
|
|
40
|
+
// Level-triggered re-assertion after RTI is handled by the VIA callback
|
|
41
|
+
// mechanism - no polling needed here
|
|
42
|
+
|
|
43
|
+
// Handle pending interrupts
|
|
44
|
+
if (nmiPending_) {
|
|
45
|
+
nmiPending_ = false;
|
|
46
|
+
pushWord(pc_);
|
|
47
|
+
push((p_ & ~FLAG_B) | FLAG_U);
|
|
48
|
+
setFlag(FLAG_I, true);
|
|
49
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
50
|
+
setFlag(FLAG_D, false); // 65C02 clears D on NMI
|
|
51
|
+
}
|
|
52
|
+
pc_ = read_(0xFFFA) | (read_(0xFFFB) << 8);
|
|
53
|
+
totalCycles_ += 7;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (irqPending_ && !getFlag(FLAG_I)) {
|
|
58
|
+
irqPending_ = false;
|
|
59
|
+
pushWord(pc_);
|
|
60
|
+
push((p_ & ~FLAG_B) | FLAG_U);
|
|
61
|
+
setFlag(FLAG_I, true);
|
|
62
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
63
|
+
setFlag(FLAG_D, false); // 65C02 clears D on IRQ
|
|
64
|
+
}
|
|
65
|
+
pc_ = read_(0xFFFE) | (read_(0xFFFF) << 8);
|
|
66
|
+
totalCycles_ += 7;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Fetch and execute opcode
|
|
71
|
+
pageCrossed_ = false;
|
|
72
|
+
uint8_t opcode = fetch();
|
|
73
|
+
cycleCount_ = CYCLE_TABLE[opcode];
|
|
74
|
+
|
|
75
|
+
executeOpcode(opcode);
|
|
76
|
+
|
|
77
|
+
if (pageCrossed_) {
|
|
78
|
+
// Some instructions take an extra cycle on page crossing
|
|
79
|
+
// This is handled per-instruction in executeOpcode
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
totalCycles_ += cycleCount_;
|
|
83
|
+
cycleCount_ = 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
void CPU6502::step() {
|
|
87
|
+
// For cycle-accurate emulation, we execute one instruction at a time
|
|
88
|
+
// and track the cycles. A more sophisticated implementation would
|
|
89
|
+
// break down each instruction into individual cycle steps.
|
|
90
|
+
if (cycleCount_ == 0) {
|
|
91
|
+
executeInstruction();
|
|
92
|
+
} else {
|
|
93
|
+
cycleCount_--;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
void CPU6502::irq() { irqPending_ = true; }
|
|
98
|
+
|
|
99
|
+
void CPU6502::nmi() {
|
|
100
|
+
// NMI is edge-triggered
|
|
101
|
+
if (!nmiEdge_) {
|
|
102
|
+
nmiPending_ = true;
|
|
103
|
+
nmiEdge_ = true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
uint8_t CPU6502::read(uint16_t address) { return read_(address); }
|
|
108
|
+
|
|
109
|
+
void CPU6502::write(uint16_t address, uint8_t value) { write_(address, value); }
|
|
110
|
+
|
|
111
|
+
uint8_t CPU6502::fetch() { return read(pc_++); }
|
|
112
|
+
|
|
113
|
+
uint16_t CPU6502::fetchWord() {
|
|
114
|
+
uint8_t lo = fetch();
|
|
115
|
+
uint8_t hi = fetch();
|
|
116
|
+
return lo | (hi << 8);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
void CPU6502::push(uint8_t value) {
|
|
120
|
+
write(0x0100 | sp_, value);
|
|
121
|
+
sp_--;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
uint8_t CPU6502::pop() {
|
|
125
|
+
sp_++;
|
|
126
|
+
return read(0x0100 | sp_);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
void CPU6502::pushWord(uint16_t value) {
|
|
130
|
+
push(value >> 8);
|
|
131
|
+
push(value & 0xFF);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
uint16_t CPU6502::popWord() {
|
|
135
|
+
uint8_t lo = pop();
|
|
136
|
+
uint8_t hi = pop();
|
|
137
|
+
return lo | (hi << 8);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
void CPU6502::updateNZ(uint8_t value) {
|
|
141
|
+
setFlag(FLAG_Z, value == 0);
|
|
142
|
+
setFlag(FLAG_N, (value & 0x80) != 0);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
void CPU6502::compare(uint8_t reg, uint8_t value) {
|
|
146
|
+
uint16_t result = reg - value;
|
|
147
|
+
setFlag(FLAG_C, reg >= value);
|
|
148
|
+
setFlag(FLAG_Z, reg == value);
|
|
149
|
+
setFlag(FLAG_N, (result & 0x80) != 0);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Addressing modes
|
|
153
|
+
uint16_t CPU6502::addrImmediate() { return pc_++; }
|
|
154
|
+
|
|
155
|
+
uint16_t CPU6502::addrZeroPage() { return fetch(); }
|
|
156
|
+
|
|
157
|
+
uint16_t CPU6502::addrZeroPageX() { return (fetch() + x_) & 0xFF; }
|
|
158
|
+
|
|
159
|
+
uint16_t CPU6502::addrZeroPageY() { return (fetch() + y_) & 0xFF; }
|
|
160
|
+
|
|
161
|
+
uint16_t CPU6502::addrAbsolute() { return fetchWord(); }
|
|
162
|
+
|
|
163
|
+
uint16_t CPU6502::addrAbsoluteX(bool checkPage) {
|
|
164
|
+
uint16_t base = fetchWord();
|
|
165
|
+
uint16_t addr = base + x_;
|
|
166
|
+
if (checkPage && ((base & 0xFF00) != (addr & 0xFF00))) {
|
|
167
|
+
pageCrossed_ = true;
|
|
168
|
+
cycleCount_++;
|
|
169
|
+
}
|
|
170
|
+
return addr;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
uint16_t CPU6502::addrAbsoluteY(bool checkPage) {
|
|
174
|
+
uint16_t base = fetchWord();
|
|
175
|
+
uint16_t addr = base + y_;
|
|
176
|
+
if (checkPage && ((base & 0xFF00) != (addr & 0xFF00))) {
|
|
177
|
+
pageCrossed_ = true;
|
|
178
|
+
cycleCount_++;
|
|
179
|
+
}
|
|
180
|
+
return addr;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
uint16_t CPU6502::addrIndirect() {
|
|
184
|
+
uint16_t ptr = fetchWord();
|
|
185
|
+
// 6502 bug: if low byte is 0xFF, high byte comes from same page
|
|
186
|
+
if (variant_ == CPUVariant::NMOS_6502 && (ptr & 0xFF) == 0xFF) {
|
|
187
|
+
return read(ptr) | (read(ptr & 0xFF00) << 8);
|
|
188
|
+
}
|
|
189
|
+
return read(ptr) | (read(ptr + 1) << 8);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
uint16_t CPU6502::addrIndexedIndirect() {
|
|
193
|
+
uint8_t zp = (fetch() + x_) & 0xFF;
|
|
194
|
+
return read(zp) | (read((zp + 1) & 0xFF) << 8);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
uint16_t CPU6502::addrIndirectIndexed(bool checkPage) {
|
|
198
|
+
uint8_t zp = fetch();
|
|
199
|
+
uint16_t base = read(zp) | (read((zp + 1) & 0xFF) << 8);
|
|
200
|
+
uint16_t addr = base + y_;
|
|
201
|
+
if (checkPage && ((base & 0xFF00) != (addr & 0xFF00))) {
|
|
202
|
+
pageCrossed_ = true;
|
|
203
|
+
cycleCount_++;
|
|
204
|
+
}
|
|
205
|
+
return addr;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
uint16_t CPU6502::addrIndirectZP() {
|
|
209
|
+
uint8_t zp = fetch();
|
|
210
|
+
return read(zp) | (read((zp + 1) & 0xFF) << 8);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ALU operations
|
|
214
|
+
void CPU6502::opADC(uint8_t value) {
|
|
215
|
+
if (getFlag(FLAG_D)) {
|
|
216
|
+
// Decimal mode - 65C02 takes an extra cycle
|
|
217
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
218
|
+
cycleCount_++;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
uint8_t carry = getFlag(FLAG_C) ? 1 : 0;
|
|
222
|
+
|
|
223
|
+
// V flag is computed from binary result (same for both variants)
|
|
224
|
+
uint16_t binResult = a_ + value + carry;
|
|
225
|
+
setFlag(FLAG_V, ~(a_ ^ value) & (a_ ^ binResult) & 0x80);
|
|
226
|
+
|
|
227
|
+
// Add low nibbles
|
|
228
|
+
int lo = (a_ & 0x0F) + (value & 0x0F) + carry;
|
|
229
|
+
|
|
230
|
+
// Add high nibbles
|
|
231
|
+
int hi = (a_ >> 4) + (value >> 4);
|
|
232
|
+
|
|
233
|
+
// BCD adjust low nibble
|
|
234
|
+
if (lo > 9) {
|
|
235
|
+
lo += 6;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Carry from low to high (handles overflow from correction)
|
|
239
|
+
hi += (lo >> 4);
|
|
240
|
+
lo &= 0x0F;
|
|
241
|
+
|
|
242
|
+
// BCD adjust high nibble and set carry
|
|
243
|
+
if (hi > 9) {
|
|
244
|
+
hi += 6;
|
|
245
|
+
setFlag(FLAG_C, true);
|
|
246
|
+
} else {
|
|
247
|
+
setFlag(FLAG_C, false);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
a_ = static_cast<uint8_t>(((hi & 0x0F) << 4) | lo);
|
|
251
|
+
|
|
252
|
+
// 65C02: N and Z are set from final BCD result (valid flags)
|
|
253
|
+
// NMOS 6502: N and Z are set but may not be valid
|
|
254
|
+
updateNZ(a_);
|
|
255
|
+
} else {
|
|
256
|
+
uint16_t result = a_ + value + (getFlag(FLAG_C) ? 1 : 0);
|
|
257
|
+
setFlag(FLAG_C, result > 0xFF);
|
|
258
|
+
setFlag(FLAG_V, ~(a_ ^ value) & (a_ ^ result) & 0x80);
|
|
259
|
+
a_ = result & 0xFF;
|
|
260
|
+
updateNZ(a_);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
void CPU6502::opSBC(uint8_t value) {
|
|
265
|
+
if (getFlag(FLAG_D)) {
|
|
266
|
+
// Decimal mode - 65C02 takes an extra cycle
|
|
267
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
268
|
+
cycleCount_++;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
uint8_t borrow = getFlag(FLAG_C) ? 0 : 1;
|
|
272
|
+
|
|
273
|
+
// V and C flags computed from binary result
|
|
274
|
+
uint16_t binResult = a_ - value - borrow;
|
|
275
|
+
setFlag(FLAG_C, binResult < 0x100);
|
|
276
|
+
setFlag(FLAG_V, (a_ ^ value) & (a_ ^ binResult) & 0x80);
|
|
277
|
+
|
|
278
|
+
// Subtract low nibbles
|
|
279
|
+
int lo = (a_ & 0x0F) - (value & 0x0F) - borrow;
|
|
280
|
+
|
|
281
|
+
// Subtract high nibbles
|
|
282
|
+
int hi = (a_ >> 4) - (value >> 4);
|
|
283
|
+
|
|
284
|
+
// BCD adjust low nibble (borrow if negative)
|
|
285
|
+
if (lo < 0) {
|
|
286
|
+
lo -= 6;
|
|
287
|
+
hi--;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// BCD adjust high nibble
|
|
291
|
+
if (hi < 0) {
|
|
292
|
+
hi -= 6;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
a_ = static_cast<uint8_t>(((hi & 0x0F) << 4) | (lo & 0x0F));
|
|
296
|
+
|
|
297
|
+
// N and Z from final BCD result
|
|
298
|
+
updateNZ(a_);
|
|
299
|
+
} else {
|
|
300
|
+
uint16_t result = a_ - value - (getFlag(FLAG_C) ? 0 : 1);
|
|
301
|
+
setFlag(FLAG_C, result < 0x100);
|
|
302
|
+
setFlag(FLAG_V, (a_ ^ value) & (a_ ^ result) & 0x80);
|
|
303
|
+
a_ = result & 0xFF;
|
|
304
|
+
updateNZ(a_);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
void CPU6502::opAND(uint8_t value) {
|
|
309
|
+
a_ &= value;
|
|
310
|
+
updateNZ(a_);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
void CPU6502::opORA(uint8_t value) {
|
|
314
|
+
a_ |= value;
|
|
315
|
+
updateNZ(a_);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
void CPU6502::opEOR(uint8_t value) {
|
|
319
|
+
a_ ^= value;
|
|
320
|
+
updateNZ(a_);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
void CPU6502::opCMP(uint8_t value) { compare(a_, value); }
|
|
324
|
+
|
|
325
|
+
void CPU6502::opCPX(uint8_t value) { compare(x_, value); }
|
|
326
|
+
|
|
327
|
+
void CPU6502::opCPY(uint8_t value) { compare(y_, value); }
|
|
328
|
+
|
|
329
|
+
void CPU6502::opBIT(uint8_t value) {
|
|
330
|
+
setFlag(FLAG_Z, (a_ & value) == 0);
|
|
331
|
+
setFlag(FLAG_N, (value & 0x80) != 0);
|
|
332
|
+
setFlag(FLAG_V, (value & 0x40) != 0);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
void CPU6502::opASL_A() {
|
|
336
|
+
setFlag(FLAG_C, (a_ & 0x80) != 0);
|
|
337
|
+
a_ <<= 1;
|
|
338
|
+
updateNZ(a_);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
uint8_t CPU6502::opASL(uint8_t value) {
|
|
342
|
+
setFlag(FLAG_C, (value & 0x80) != 0);
|
|
343
|
+
value <<= 1;
|
|
344
|
+
updateNZ(value);
|
|
345
|
+
return value;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
void CPU6502::opLSR_A() {
|
|
349
|
+
setFlag(FLAG_C, (a_ & 0x01) != 0);
|
|
350
|
+
a_ >>= 1;
|
|
351
|
+
updateNZ(a_);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
uint8_t CPU6502::opLSR(uint8_t value) {
|
|
355
|
+
setFlag(FLAG_C, (value & 0x01) != 0);
|
|
356
|
+
value >>= 1;
|
|
357
|
+
updateNZ(value);
|
|
358
|
+
return value;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
void CPU6502::opROL_A() {
|
|
362
|
+
uint8_t carry = getFlag(FLAG_C) ? 1 : 0;
|
|
363
|
+
setFlag(FLAG_C, (a_ & 0x80) != 0);
|
|
364
|
+
a_ = (a_ << 1) | carry;
|
|
365
|
+
updateNZ(a_);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
uint8_t CPU6502::opROL(uint8_t value) {
|
|
369
|
+
uint8_t carry = getFlag(FLAG_C) ? 1 : 0;
|
|
370
|
+
setFlag(FLAG_C, (value & 0x80) != 0);
|
|
371
|
+
value = (value << 1) | carry;
|
|
372
|
+
updateNZ(value);
|
|
373
|
+
return value;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
void CPU6502::opROR_A() {
|
|
377
|
+
uint8_t carry = getFlag(FLAG_C) ? 0x80 : 0;
|
|
378
|
+
setFlag(FLAG_C, (a_ & 0x01) != 0);
|
|
379
|
+
a_ = (a_ >> 1) | carry;
|
|
380
|
+
updateNZ(a_);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
uint8_t CPU6502::opROR(uint8_t value) {
|
|
384
|
+
uint8_t carry = getFlag(FLAG_C) ? 0x80 : 0;
|
|
385
|
+
setFlag(FLAG_C, (value & 0x01) != 0);
|
|
386
|
+
value = (value >> 1) | carry;
|
|
387
|
+
updateNZ(value);
|
|
388
|
+
return value;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
uint8_t CPU6502::opINC(uint8_t value) {
|
|
392
|
+
value++;
|
|
393
|
+
updateNZ(value);
|
|
394
|
+
return value;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
uint8_t CPU6502::opDEC(uint8_t value) {
|
|
398
|
+
value--;
|
|
399
|
+
updateNZ(value);
|
|
400
|
+
return value;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
void CPU6502::branch(bool condition) {
|
|
404
|
+
int8_t offset = static_cast<int8_t>(fetch());
|
|
405
|
+
if (condition) {
|
|
406
|
+
uint16_t oldPC = pc_;
|
|
407
|
+
pc_ += offset;
|
|
408
|
+
cycleCount_++;
|
|
409
|
+
if ((oldPC & 0xFF00) != (pc_ & 0xFF00)) {
|
|
410
|
+
cycleCount_++; // Page crossing penalty
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
void CPU6502::executeOpcode(uint8_t opcode) {
|
|
416
|
+
uint16_t addr;
|
|
417
|
+
uint8_t value;
|
|
418
|
+
|
|
419
|
+
switch (opcode) {
|
|
420
|
+
// LDA
|
|
421
|
+
case 0xA9:
|
|
422
|
+
a_ = read(addrImmediate());
|
|
423
|
+
updateNZ(a_);
|
|
424
|
+
break;
|
|
425
|
+
case 0xA5:
|
|
426
|
+
a_ = read(addrZeroPage());
|
|
427
|
+
updateNZ(a_);
|
|
428
|
+
break;
|
|
429
|
+
case 0xB5:
|
|
430
|
+
a_ = read(addrZeroPageX());
|
|
431
|
+
updateNZ(a_);
|
|
432
|
+
break;
|
|
433
|
+
case 0xAD:
|
|
434
|
+
a_ = read(addrAbsolute());
|
|
435
|
+
updateNZ(a_);
|
|
436
|
+
break;
|
|
437
|
+
case 0xBD:
|
|
438
|
+
a_ = read(addrAbsoluteX());
|
|
439
|
+
updateNZ(a_);
|
|
440
|
+
break;
|
|
441
|
+
case 0xB9:
|
|
442
|
+
a_ = read(addrAbsoluteY());
|
|
443
|
+
updateNZ(a_);
|
|
444
|
+
break;
|
|
445
|
+
case 0xA1:
|
|
446
|
+
a_ = read(addrIndexedIndirect());
|
|
447
|
+
updateNZ(a_);
|
|
448
|
+
break;
|
|
449
|
+
case 0xB1:
|
|
450
|
+
a_ = read(addrIndirectIndexed());
|
|
451
|
+
updateNZ(a_);
|
|
452
|
+
break;
|
|
453
|
+
case 0xB2:
|
|
454
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
455
|
+
a_ = read(addrIndirectZP());
|
|
456
|
+
updateNZ(a_);
|
|
457
|
+
}
|
|
458
|
+
break; // LDA (zp) - 65C02
|
|
459
|
+
|
|
460
|
+
// LDX
|
|
461
|
+
case 0xA2:
|
|
462
|
+
x_ = read(addrImmediate());
|
|
463
|
+
updateNZ(x_);
|
|
464
|
+
break;
|
|
465
|
+
case 0xA6:
|
|
466
|
+
x_ = read(addrZeroPage());
|
|
467
|
+
updateNZ(x_);
|
|
468
|
+
break;
|
|
469
|
+
case 0xB6:
|
|
470
|
+
x_ = read(addrZeroPageY());
|
|
471
|
+
updateNZ(x_);
|
|
472
|
+
break;
|
|
473
|
+
case 0xAE:
|
|
474
|
+
x_ = read(addrAbsolute());
|
|
475
|
+
updateNZ(x_);
|
|
476
|
+
break;
|
|
477
|
+
case 0xBE:
|
|
478
|
+
x_ = read(addrAbsoluteY());
|
|
479
|
+
updateNZ(x_);
|
|
480
|
+
break;
|
|
481
|
+
|
|
482
|
+
// LDY
|
|
483
|
+
case 0xA0:
|
|
484
|
+
y_ = read(addrImmediate());
|
|
485
|
+
updateNZ(y_);
|
|
486
|
+
break;
|
|
487
|
+
case 0xA4:
|
|
488
|
+
y_ = read(addrZeroPage());
|
|
489
|
+
updateNZ(y_);
|
|
490
|
+
break;
|
|
491
|
+
case 0xB4:
|
|
492
|
+
y_ = read(addrZeroPageX());
|
|
493
|
+
updateNZ(y_);
|
|
494
|
+
break;
|
|
495
|
+
case 0xAC:
|
|
496
|
+
y_ = read(addrAbsolute());
|
|
497
|
+
updateNZ(y_);
|
|
498
|
+
break;
|
|
499
|
+
case 0xBC:
|
|
500
|
+
y_ = read(addrAbsoluteX());
|
|
501
|
+
updateNZ(y_);
|
|
502
|
+
break;
|
|
503
|
+
|
|
504
|
+
// STA
|
|
505
|
+
case 0x85:
|
|
506
|
+
write(addrZeroPage(), a_);
|
|
507
|
+
break;
|
|
508
|
+
case 0x95:
|
|
509
|
+
write(addrZeroPageX(), a_);
|
|
510
|
+
break;
|
|
511
|
+
case 0x8D:
|
|
512
|
+
write(addrAbsolute(), a_);
|
|
513
|
+
break;
|
|
514
|
+
case 0x9D:
|
|
515
|
+
write(addrAbsoluteX(false), a_);
|
|
516
|
+
break;
|
|
517
|
+
case 0x99:
|
|
518
|
+
write(addrAbsoluteY(false), a_);
|
|
519
|
+
break;
|
|
520
|
+
case 0x81:
|
|
521
|
+
write(addrIndexedIndirect(), a_);
|
|
522
|
+
break;
|
|
523
|
+
case 0x91:
|
|
524
|
+
write(addrIndirectIndexed(false), a_);
|
|
525
|
+
break;
|
|
526
|
+
case 0x92:
|
|
527
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
528
|
+
write(addrIndirectZP(), a_);
|
|
529
|
+
break; // STA (zp) - 65C02
|
|
530
|
+
|
|
531
|
+
// STX
|
|
532
|
+
case 0x86:
|
|
533
|
+
write(addrZeroPage(), x_);
|
|
534
|
+
break;
|
|
535
|
+
case 0x96:
|
|
536
|
+
write(addrZeroPageY(), x_);
|
|
537
|
+
break;
|
|
538
|
+
case 0x8E:
|
|
539
|
+
write(addrAbsolute(), x_);
|
|
540
|
+
break;
|
|
541
|
+
|
|
542
|
+
// STY
|
|
543
|
+
case 0x84:
|
|
544
|
+
write(addrZeroPage(), y_);
|
|
545
|
+
break;
|
|
546
|
+
case 0x94:
|
|
547
|
+
write(addrZeroPageX(), y_);
|
|
548
|
+
break;
|
|
549
|
+
case 0x8C:
|
|
550
|
+
write(addrAbsolute(), y_);
|
|
551
|
+
break;
|
|
552
|
+
|
|
553
|
+
// STZ (65C02)
|
|
554
|
+
case 0x64:
|
|
555
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
556
|
+
write(addrZeroPage(), 0);
|
|
557
|
+
break;
|
|
558
|
+
case 0x74:
|
|
559
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
560
|
+
write(addrZeroPageX(), 0);
|
|
561
|
+
break;
|
|
562
|
+
case 0x9C:
|
|
563
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
564
|
+
write(addrAbsolute(), 0);
|
|
565
|
+
break;
|
|
566
|
+
case 0x9E:
|
|
567
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
568
|
+
write(addrAbsoluteX(false), 0);
|
|
569
|
+
break;
|
|
570
|
+
|
|
571
|
+
// Transfer
|
|
572
|
+
case 0xAA:
|
|
573
|
+
x_ = a_;
|
|
574
|
+
updateNZ(x_);
|
|
575
|
+
break; // TAX
|
|
576
|
+
case 0xA8:
|
|
577
|
+
y_ = a_;
|
|
578
|
+
updateNZ(y_);
|
|
579
|
+
break; // TAY
|
|
580
|
+
case 0xBA:
|
|
581
|
+
x_ = sp_;
|
|
582
|
+
updateNZ(x_);
|
|
583
|
+
break; // TSX
|
|
584
|
+
case 0x8A:
|
|
585
|
+
a_ = x_;
|
|
586
|
+
updateNZ(a_);
|
|
587
|
+
break; // TXA
|
|
588
|
+
case 0x9A:
|
|
589
|
+
sp_ = x_;
|
|
590
|
+
break; // TXS
|
|
591
|
+
case 0x98:
|
|
592
|
+
a_ = y_;
|
|
593
|
+
updateNZ(a_);
|
|
594
|
+
break; // TYA
|
|
595
|
+
|
|
596
|
+
// Stack
|
|
597
|
+
case 0x48:
|
|
598
|
+
push(a_);
|
|
599
|
+
break; // PHA
|
|
600
|
+
case 0x68:
|
|
601
|
+
a_ = pop();
|
|
602
|
+
updateNZ(a_);
|
|
603
|
+
break; // PLA
|
|
604
|
+
case 0x08:
|
|
605
|
+
push(p_ | FLAG_B | FLAG_U);
|
|
606
|
+
break; // PHP
|
|
607
|
+
case 0x28:
|
|
608
|
+
p_ = (pop() & ~FLAG_B) | FLAG_U;
|
|
609
|
+
break; // PLP
|
|
610
|
+
case 0xDA:
|
|
611
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
612
|
+
push(x_);
|
|
613
|
+
break; // PHX (65C02)
|
|
614
|
+
case 0xFA:
|
|
615
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
616
|
+
x_ = pop();
|
|
617
|
+
updateNZ(x_);
|
|
618
|
+
}
|
|
619
|
+
break; // PLX (65C02)
|
|
620
|
+
case 0x5A:
|
|
621
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
622
|
+
push(y_);
|
|
623
|
+
break; // PHY (65C02)
|
|
624
|
+
case 0x7A:
|
|
625
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
626
|
+
y_ = pop();
|
|
627
|
+
updateNZ(y_);
|
|
628
|
+
}
|
|
629
|
+
break; // PLY (65C02)
|
|
630
|
+
|
|
631
|
+
// ADC
|
|
632
|
+
case 0x69:
|
|
633
|
+
opADC(read(addrImmediate()));
|
|
634
|
+
break;
|
|
635
|
+
case 0x65:
|
|
636
|
+
opADC(read(addrZeroPage()));
|
|
637
|
+
break;
|
|
638
|
+
case 0x75:
|
|
639
|
+
opADC(read(addrZeroPageX()));
|
|
640
|
+
break;
|
|
641
|
+
case 0x6D:
|
|
642
|
+
opADC(read(addrAbsolute()));
|
|
643
|
+
break;
|
|
644
|
+
case 0x7D:
|
|
645
|
+
opADC(read(addrAbsoluteX()));
|
|
646
|
+
break;
|
|
647
|
+
case 0x79:
|
|
648
|
+
opADC(read(addrAbsoluteY()));
|
|
649
|
+
break;
|
|
650
|
+
case 0x61:
|
|
651
|
+
opADC(read(addrIndexedIndirect()));
|
|
652
|
+
break;
|
|
653
|
+
case 0x71:
|
|
654
|
+
opADC(read(addrIndirectIndexed()));
|
|
655
|
+
break;
|
|
656
|
+
case 0x72:
|
|
657
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
658
|
+
opADC(read(addrIndirectZP()));
|
|
659
|
+
break; // (65C02)
|
|
660
|
+
|
|
661
|
+
// SBC
|
|
662
|
+
case 0xE9:
|
|
663
|
+
opSBC(read(addrImmediate()));
|
|
664
|
+
break;
|
|
665
|
+
case 0xE5:
|
|
666
|
+
opSBC(read(addrZeroPage()));
|
|
667
|
+
break;
|
|
668
|
+
case 0xF5:
|
|
669
|
+
opSBC(read(addrZeroPageX()));
|
|
670
|
+
break;
|
|
671
|
+
case 0xED:
|
|
672
|
+
opSBC(read(addrAbsolute()));
|
|
673
|
+
break;
|
|
674
|
+
case 0xFD:
|
|
675
|
+
opSBC(read(addrAbsoluteX()));
|
|
676
|
+
break;
|
|
677
|
+
case 0xF9:
|
|
678
|
+
opSBC(read(addrAbsoluteY()));
|
|
679
|
+
break;
|
|
680
|
+
case 0xE1:
|
|
681
|
+
opSBC(read(addrIndexedIndirect()));
|
|
682
|
+
break;
|
|
683
|
+
case 0xF1:
|
|
684
|
+
opSBC(read(addrIndirectIndexed()));
|
|
685
|
+
break;
|
|
686
|
+
case 0xF2:
|
|
687
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
688
|
+
opSBC(read(addrIndirectZP()));
|
|
689
|
+
break; // (65C02)
|
|
690
|
+
|
|
691
|
+
// AND
|
|
692
|
+
case 0x29:
|
|
693
|
+
opAND(read(addrImmediate()));
|
|
694
|
+
break;
|
|
695
|
+
case 0x25:
|
|
696
|
+
opAND(read(addrZeroPage()));
|
|
697
|
+
break;
|
|
698
|
+
case 0x35:
|
|
699
|
+
opAND(read(addrZeroPageX()));
|
|
700
|
+
break;
|
|
701
|
+
case 0x2D:
|
|
702
|
+
opAND(read(addrAbsolute()));
|
|
703
|
+
break;
|
|
704
|
+
case 0x3D:
|
|
705
|
+
opAND(read(addrAbsoluteX()));
|
|
706
|
+
break;
|
|
707
|
+
case 0x39:
|
|
708
|
+
opAND(read(addrAbsoluteY()));
|
|
709
|
+
break;
|
|
710
|
+
case 0x21:
|
|
711
|
+
opAND(read(addrIndexedIndirect()));
|
|
712
|
+
break;
|
|
713
|
+
case 0x31:
|
|
714
|
+
opAND(read(addrIndirectIndexed()));
|
|
715
|
+
break;
|
|
716
|
+
case 0x32:
|
|
717
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
718
|
+
opAND(read(addrIndirectZP()));
|
|
719
|
+
break; // (65C02)
|
|
720
|
+
|
|
721
|
+
// ORA
|
|
722
|
+
case 0x09:
|
|
723
|
+
opORA(read(addrImmediate()));
|
|
724
|
+
break;
|
|
725
|
+
case 0x05:
|
|
726
|
+
opORA(read(addrZeroPage()));
|
|
727
|
+
break;
|
|
728
|
+
case 0x15:
|
|
729
|
+
opORA(read(addrZeroPageX()));
|
|
730
|
+
break;
|
|
731
|
+
case 0x0D:
|
|
732
|
+
opORA(read(addrAbsolute()));
|
|
733
|
+
break;
|
|
734
|
+
case 0x1D:
|
|
735
|
+
opORA(read(addrAbsoluteX()));
|
|
736
|
+
break;
|
|
737
|
+
case 0x19:
|
|
738
|
+
opORA(read(addrAbsoluteY()));
|
|
739
|
+
break;
|
|
740
|
+
case 0x01:
|
|
741
|
+
opORA(read(addrIndexedIndirect()));
|
|
742
|
+
break;
|
|
743
|
+
case 0x11:
|
|
744
|
+
opORA(read(addrIndirectIndexed()));
|
|
745
|
+
break;
|
|
746
|
+
case 0x12:
|
|
747
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
748
|
+
opORA(read(addrIndirectZP()));
|
|
749
|
+
break; // (65C02)
|
|
750
|
+
|
|
751
|
+
// EOR
|
|
752
|
+
case 0x49:
|
|
753
|
+
opEOR(read(addrImmediate()));
|
|
754
|
+
break;
|
|
755
|
+
case 0x45:
|
|
756
|
+
opEOR(read(addrZeroPage()));
|
|
757
|
+
break;
|
|
758
|
+
case 0x55:
|
|
759
|
+
opEOR(read(addrZeroPageX()));
|
|
760
|
+
break;
|
|
761
|
+
case 0x4D:
|
|
762
|
+
opEOR(read(addrAbsolute()));
|
|
763
|
+
break;
|
|
764
|
+
case 0x5D:
|
|
765
|
+
opEOR(read(addrAbsoluteX()));
|
|
766
|
+
break;
|
|
767
|
+
case 0x59:
|
|
768
|
+
opEOR(read(addrAbsoluteY()));
|
|
769
|
+
break;
|
|
770
|
+
case 0x41:
|
|
771
|
+
opEOR(read(addrIndexedIndirect()));
|
|
772
|
+
break;
|
|
773
|
+
case 0x51:
|
|
774
|
+
opEOR(read(addrIndirectIndexed()));
|
|
775
|
+
break;
|
|
776
|
+
case 0x52:
|
|
777
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
778
|
+
opEOR(read(addrIndirectZP()));
|
|
779
|
+
break; // (65C02)
|
|
780
|
+
|
|
781
|
+
// CMP
|
|
782
|
+
case 0xC9:
|
|
783
|
+
opCMP(read(addrImmediate()));
|
|
784
|
+
break;
|
|
785
|
+
case 0xC5:
|
|
786
|
+
opCMP(read(addrZeroPage()));
|
|
787
|
+
break;
|
|
788
|
+
case 0xD5:
|
|
789
|
+
opCMP(read(addrZeroPageX()));
|
|
790
|
+
break;
|
|
791
|
+
case 0xCD:
|
|
792
|
+
opCMP(read(addrAbsolute()));
|
|
793
|
+
break;
|
|
794
|
+
case 0xDD:
|
|
795
|
+
opCMP(read(addrAbsoluteX()));
|
|
796
|
+
break;
|
|
797
|
+
case 0xD9:
|
|
798
|
+
opCMP(read(addrAbsoluteY()));
|
|
799
|
+
break;
|
|
800
|
+
case 0xC1:
|
|
801
|
+
opCMP(read(addrIndexedIndirect()));
|
|
802
|
+
break;
|
|
803
|
+
case 0xD1:
|
|
804
|
+
opCMP(read(addrIndirectIndexed()));
|
|
805
|
+
break;
|
|
806
|
+
case 0xD2:
|
|
807
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
808
|
+
opCMP(read(addrIndirectZP()));
|
|
809
|
+
break; // (65C02)
|
|
810
|
+
|
|
811
|
+
// CPX
|
|
812
|
+
case 0xE0:
|
|
813
|
+
opCPX(read(addrImmediate()));
|
|
814
|
+
break;
|
|
815
|
+
case 0xE4:
|
|
816
|
+
opCPX(read(addrZeroPage()));
|
|
817
|
+
break;
|
|
818
|
+
case 0xEC:
|
|
819
|
+
opCPX(read(addrAbsolute()));
|
|
820
|
+
break;
|
|
821
|
+
|
|
822
|
+
// CPY
|
|
823
|
+
case 0xC0:
|
|
824
|
+
opCPY(read(addrImmediate()));
|
|
825
|
+
break;
|
|
826
|
+
case 0xC4:
|
|
827
|
+
opCPY(read(addrZeroPage()));
|
|
828
|
+
break;
|
|
829
|
+
case 0xCC:
|
|
830
|
+
opCPY(read(addrAbsolute()));
|
|
831
|
+
break;
|
|
832
|
+
|
|
833
|
+
// BIT
|
|
834
|
+
case 0x24:
|
|
835
|
+
opBIT(read(addrZeroPage()));
|
|
836
|
+
break;
|
|
837
|
+
case 0x2C:
|
|
838
|
+
opBIT(read(addrAbsolute()));
|
|
839
|
+
break;
|
|
840
|
+
case 0x89:
|
|
841
|
+
if (variant_ == CPUVariant::CMOS_65C02) { // BIT immediate (65C02)
|
|
842
|
+
value = read(addrImmediate());
|
|
843
|
+
setFlag(FLAG_Z, (a_ & value) == 0);
|
|
844
|
+
}
|
|
845
|
+
break;
|
|
846
|
+
case 0x34:
|
|
847
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
848
|
+
opBIT(read(addrZeroPageX()));
|
|
849
|
+
break;
|
|
850
|
+
case 0x3C:
|
|
851
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
852
|
+
opBIT(read(addrAbsoluteX()));
|
|
853
|
+
break;
|
|
854
|
+
|
|
855
|
+
// ASL - Read-modify-write
|
|
856
|
+
// NMOS 6502: read, write-old, write-new (double write)
|
|
857
|
+
// CMOS 65C02: read, read-dummy, write-new (double read)
|
|
858
|
+
case 0x0A:
|
|
859
|
+
opASL_A();
|
|
860
|
+
break;
|
|
861
|
+
case 0x06:
|
|
862
|
+
addr = addrZeroPage();
|
|
863
|
+
value = read(addr);
|
|
864
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
865
|
+
read(addr); // 65C02: dummy read
|
|
866
|
+
else
|
|
867
|
+
write(addr, value); // NMOS: dummy write of old value
|
|
868
|
+
write(addr, opASL(value));
|
|
869
|
+
break;
|
|
870
|
+
case 0x16:
|
|
871
|
+
addr = addrZeroPageX();
|
|
872
|
+
value = read(addr);
|
|
873
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
874
|
+
read(addr);
|
|
875
|
+
else
|
|
876
|
+
write(addr, value);
|
|
877
|
+
write(addr, opASL(value));
|
|
878
|
+
break;
|
|
879
|
+
case 0x0E:
|
|
880
|
+
addr = addrAbsolute();
|
|
881
|
+
value = read(addr);
|
|
882
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
883
|
+
read(addr);
|
|
884
|
+
else
|
|
885
|
+
write(addr, value);
|
|
886
|
+
write(addr, opASL(value));
|
|
887
|
+
break;
|
|
888
|
+
case 0x1E:
|
|
889
|
+
addr = addrAbsoluteX(false);
|
|
890
|
+
value = read(addr);
|
|
891
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
892
|
+
read(addr);
|
|
893
|
+
else
|
|
894
|
+
write(addr, value);
|
|
895
|
+
write(addr, opASL(value));
|
|
896
|
+
break;
|
|
897
|
+
|
|
898
|
+
// LSR - Read-modify-write
|
|
899
|
+
case 0x4A:
|
|
900
|
+
opLSR_A();
|
|
901
|
+
break;
|
|
902
|
+
case 0x46:
|
|
903
|
+
addr = addrZeroPage();
|
|
904
|
+
value = read(addr);
|
|
905
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
906
|
+
read(addr);
|
|
907
|
+
else
|
|
908
|
+
write(addr, value);
|
|
909
|
+
write(addr, opLSR(value));
|
|
910
|
+
break;
|
|
911
|
+
case 0x56:
|
|
912
|
+
addr = addrZeroPageX();
|
|
913
|
+
value = read(addr);
|
|
914
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
915
|
+
read(addr);
|
|
916
|
+
else
|
|
917
|
+
write(addr, value);
|
|
918
|
+
write(addr, opLSR(value));
|
|
919
|
+
break;
|
|
920
|
+
case 0x4E:
|
|
921
|
+
addr = addrAbsolute();
|
|
922
|
+
value = read(addr);
|
|
923
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
924
|
+
read(addr);
|
|
925
|
+
else
|
|
926
|
+
write(addr, value);
|
|
927
|
+
write(addr, opLSR(value));
|
|
928
|
+
break;
|
|
929
|
+
case 0x5E:
|
|
930
|
+
addr = addrAbsoluteX(false);
|
|
931
|
+
value = read(addr);
|
|
932
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
933
|
+
read(addr);
|
|
934
|
+
else
|
|
935
|
+
write(addr, value);
|
|
936
|
+
write(addr, opLSR(value));
|
|
937
|
+
break;
|
|
938
|
+
|
|
939
|
+
// ROL - Read-modify-write
|
|
940
|
+
case 0x2A:
|
|
941
|
+
opROL_A();
|
|
942
|
+
break;
|
|
943
|
+
case 0x26:
|
|
944
|
+
addr = addrZeroPage();
|
|
945
|
+
value = read(addr);
|
|
946
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
947
|
+
read(addr);
|
|
948
|
+
else
|
|
949
|
+
write(addr, value);
|
|
950
|
+
write(addr, opROL(value));
|
|
951
|
+
break;
|
|
952
|
+
case 0x36:
|
|
953
|
+
addr = addrZeroPageX();
|
|
954
|
+
value = read(addr);
|
|
955
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
956
|
+
read(addr);
|
|
957
|
+
else
|
|
958
|
+
write(addr, value);
|
|
959
|
+
write(addr, opROL(value));
|
|
960
|
+
break;
|
|
961
|
+
case 0x2E:
|
|
962
|
+
addr = addrAbsolute();
|
|
963
|
+
value = read(addr);
|
|
964
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
965
|
+
read(addr);
|
|
966
|
+
else
|
|
967
|
+
write(addr, value);
|
|
968
|
+
write(addr, opROL(value));
|
|
969
|
+
break;
|
|
970
|
+
case 0x3E:
|
|
971
|
+
addr = addrAbsoluteX(false);
|
|
972
|
+
value = read(addr);
|
|
973
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
974
|
+
read(addr);
|
|
975
|
+
else
|
|
976
|
+
write(addr, value);
|
|
977
|
+
write(addr, opROL(value));
|
|
978
|
+
break;
|
|
979
|
+
|
|
980
|
+
// ROR - Read-modify-write
|
|
981
|
+
case 0x6A:
|
|
982
|
+
opROR_A();
|
|
983
|
+
break;
|
|
984
|
+
case 0x66:
|
|
985
|
+
addr = addrZeroPage();
|
|
986
|
+
value = read(addr);
|
|
987
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
988
|
+
read(addr);
|
|
989
|
+
else
|
|
990
|
+
write(addr, value);
|
|
991
|
+
write(addr, opROR(value));
|
|
992
|
+
break;
|
|
993
|
+
case 0x76:
|
|
994
|
+
addr = addrZeroPageX();
|
|
995
|
+
value = read(addr);
|
|
996
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
997
|
+
read(addr);
|
|
998
|
+
else
|
|
999
|
+
write(addr, value);
|
|
1000
|
+
write(addr, opROR(value));
|
|
1001
|
+
break;
|
|
1002
|
+
case 0x6E:
|
|
1003
|
+
addr = addrAbsolute();
|
|
1004
|
+
value = read(addr);
|
|
1005
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1006
|
+
read(addr);
|
|
1007
|
+
else
|
|
1008
|
+
write(addr, value);
|
|
1009
|
+
write(addr, opROR(value));
|
|
1010
|
+
break;
|
|
1011
|
+
case 0x7E:
|
|
1012
|
+
addr = addrAbsoluteX(false);
|
|
1013
|
+
value = read(addr);
|
|
1014
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1015
|
+
read(addr);
|
|
1016
|
+
else
|
|
1017
|
+
write(addr, value);
|
|
1018
|
+
write(addr, opROR(value));
|
|
1019
|
+
break;
|
|
1020
|
+
|
|
1021
|
+
// INC - Read-modify-write
|
|
1022
|
+
case 0xE6:
|
|
1023
|
+
addr = addrZeroPage();
|
|
1024
|
+
value = read(addr);
|
|
1025
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1026
|
+
read(addr);
|
|
1027
|
+
else
|
|
1028
|
+
write(addr, value);
|
|
1029
|
+
write(addr, opINC(value));
|
|
1030
|
+
break;
|
|
1031
|
+
case 0xF6:
|
|
1032
|
+
addr = addrZeroPageX();
|
|
1033
|
+
value = read(addr);
|
|
1034
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1035
|
+
read(addr);
|
|
1036
|
+
else
|
|
1037
|
+
write(addr, value);
|
|
1038
|
+
write(addr, opINC(value));
|
|
1039
|
+
break;
|
|
1040
|
+
case 0xEE:
|
|
1041
|
+
addr = addrAbsolute();
|
|
1042
|
+
value = read(addr);
|
|
1043
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1044
|
+
read(addr);
|
|
1045
|
+
else
|
|
1046
|
+
write(addr, value);
|
|
1047
|
+
write(addr, opINC(value));
|
|
1048
|
+
break;
|
|
1049
|
+
case 0xFE:
|
|
1050
|
+
addr = addrAbsoluteX(false);
|
|
1051
|
+
value = read(addr);
|
|
1052
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1053
|
+
read(addr);
|
|
1054
|
+
else
|
|
1055
|
+
write(addr, value);
|
|
1056
|
+
write(addr, opINC(value));
|
|
1057
|
+
break;
|
|
1058
|
+
case 0x1A:
|
|
1059
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
1060
|
+
a_ = opINC(a_);
|
|
1061
|
+
}
|
|
1062
|
+
break; // INC A (65C02)
|
|
1063
|
+
|
|
1064
|
+
// DEC - Read-modify-write
|
|
1065
|
+
case 0xC6:
|
|
1066
|
+
addr = addrZeroPage();
|
|
1067
|
+
value = read(addr);
|
|
1068
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1069
|
+
read(addr);
|
|
1070
|
+
else
|
|
1071
|
+
write(addr, value);
|
|
1072
|
+
write(addr, opDEC(value));
|
|
1073
|
+
break;
|
|
1074
|
+
case 0xD6:
|
|
1075
|
+
addr = addrZeroPageX();
|
|
1076
|
+
value = read(addr);
|
|
1077
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1078
|
+
read(addr);
|
|
1079
|
+
else
|
|
1080
|
+
write(addr, value);
|
|
1081
|
+
write(addr, opDEC(value));
|
|
1082
|
+
break;
|
|
1083
|
+
case 0xCE:
|
|
1084
|
+
addr = addrAbsolute();
|
|
1085
|
+
value = read(addr);
|
|
1086
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1087
|
+
read(addr);
|
|
1088
|
+
else
|
|
1089
|
+
write(addr, value);
|
|
1090
|
+
write(addr, opDEC(value));
|
|
1091
|
+
break;
|
|
1092
|
+
case 0xDE:
|
|
1093
|
+
addr = addrAbsoluteX(false);
|
|
1094
|
+
value = read(addr);
|
|
1095
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1096
|
+
read(addr);
|
|
1097
|
+
else
|
|
1098
|
+
write(addr, value);
|
|
1099
|
+
write(addr, opDEC(value));
|
|
1100
|
+
break;
|
|
1101
|
+
case 0x3A:
|
|
1102
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
1103
|
+
a_ = opDEC(a_);
|
|
1104
|
+
}
|
|
1105
|
+
break; // DEC A (65C02)
|
|
1106
|
+
|
|
1107
|
+
// INX/INY/DEX/DEY
|
|
1108
|
+
case 0xE8:
|
|
1109
|
+
x_++;
|
|
1110
|
+
updateNZ(x_);
|
|
1111
|
+
break; // INX
|
|
1112
|
+
case 0xC8:
|
|
1113
|
+
y_++;
|
|
1114
|
+
updateNZ(y_);
|
|
1115
|
+
break; // INY
|
|
1116
|
+
case 0xCA:
|
|
1117
|
+
x_--;
|
|
1118
|
+
updateNZ(x_);
|
|
1119
|
+
break; // DEX
|
|
1120
|
+
case 0x88:
|
|
1121
|
+
y_--;
|
|
1122
|
+
updateNZ(y_);
|
|
1123
|
+
break; // DEY
|
|
1124
|
+
|
|
1125
|
+
// Branches
|
|
1126
|
+
case 0x10:
|
|
1127
|
+
branch(!getFlag(FLAG_N));
|
|
1128
|
+
break; // BPL
|
|
1129
|
+
case 0x30:
|
|
1130
|
+
branch(getFlag(FLAG_N));
|
|
1131
|
+
break; // BMI
|
|
1132
|
+
case 0x50:
|
|
1133
|
+
branch(!getFlag(FLAG_V));
|
|
1134
|
+
break; // BVC
|
|
1135
|
+
case 0x70:
|
|
1136
|
+
branch(getFlag(FLAG_V));
|
|
1137
|
+
break; // BVS
|
|
1138
|
+
case 0x90:
|
|
1139
|
+
branch(!getFlag(FLAG_C));
|
|
1140
|
+
break; // BCC
|
|
1141
|
+
case 0xB0:
|
|
1142
|
+
branch(getFlag(FLAG_C));
|
|
1143
|
+
break; // BCS
|
|
1144
|
+
case 0xD0:
|
|
1145
|
+
branch(!getFlag(FLAG_Z));
|
|
1146
|
+
break; // BNE
|
|
1147
|
+
case 0xF0:
|
|
1148
|
+
branch(getFlag(FLAG_Z));
|
|
1149
|
+
break; // BEQ
|
|
1150
|
+
case 0x80:
|
|
1151
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1152
|
+
branch(true);
|
|
1153
|
+
break; // BRA (65C02)
|
|
1154
|
+
|
|
1155
|
+
// JMP
|
|
1156
|
+
case 0x4C:
|
|
1157
|
+
pc_ = addrAbsolute();
|
|
1158
|
+
break;
|
|
1159
|
+
case 0x6C:
|
|
1160
|
+
pc_ = addrIndirect();
|
|
1161
|
+
break;
|
|
1162
|
+
case 0x7C:
|
|
1163
|
+
if (variant_ == CPUVariant::CMOS_65C02) { // JMP (abs,X) - 65C02
|
|
1164
|
+
uint16_t base = fetchWord();
|
|
1165
|
+
pc_ = read(base + x_) | (read(base + x_ + 1) << 8);
|
|
1166
|
+
}
|
|
1167
|
+
break;
|
|
1168
|
+
|
|
1169
|
+
// JSR/RTS/RTI
|
|
1170
|
+
case 0x20:
|
|
1171
|
+
addr = fetchWord();
|
|
1172
|
+
pushWord(pc_ - 1);
|
|
1173
|
+
pc_ = addr;
|
|
1174
|
+
break;
|
|
1175
|
+
case 0x60:
|
|
1176
|
+
pc_ = popWord() + 1;
|
|
1177
|
+
break;
|
|
1178
|
+
case 0x40:
|
|
1179
|
+
p_ = (pop() & ~FLAG_B) | FLAG_U;
|
|
1180
|
+
pc_ = popWord();
|
|
1181
|
+
break;
|
|
1182
|
+
|
|
1183
|
+
// Flag operations
|
|
1184
|
+
case 0x18:
|
|
1185
|
+
setFlag(FLAG_C, false);
|
|
1186
|
+
break; // CLC
|
|
1187
|
+
case 0x38:
|
|
1188
|
+
setFlag(FLAG_C, true);
|
|
1189
|
+
break; // SEC
|
|
1190
|
+
case 0x58:
|
|
1191
|
+
setFlag(FLAG_I, false);
|
|
1192
|
+
break; // CLI
|
|
1193
|
+
case 0x78:
|
|
1194
|
+
setFlag(FLAG_I, true);
|
|
1195
|
+
break; // SEI
|
|
1196
|
+
case 0xB8:
|
|
1197
|
+
setFlag(FLAG_V, false);
|
|
1198
|
+
break; // CLV
|
|
1199
|
+
case 0xD8:
|
|
1200
|
+
setFlag(FLAG_D, false);
|
|
1201
|
+
break; // CLD
|
|
1202
|
+
case 0xF8:
|
|
1203
|
+
setFlag(FLAG_D, true);
|
|
1204
|
+
break; // SED
|
|
1205
|
+
|
|
1206
|
+
// BRK
|
|
1207
|
+
case 0x00:
|
|
1208
|
+
pc_++;
|
|
1209
|
+
pushWord(pc_);
|
|
1210
|
+
push(p_ | FLAG_B | FLAG_U);
|
|
1211
|
+
setFlag(FLAG_I, true);
|
|
1212
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
1213
|
+
setFlag(FLAG_D, false); // 65C02 clears D on BRK
|
|
1214
|
+
}
|
|
1215
|
+
pc_ = read(0xFFFE) | (read(0xFFFF) << 8);
|
|
1216
|
+
break;
|
|
1217
|
+
|
|
1218
|
+
// NOP
|
|
1219
|
+
case 0xEA:
|
|
1220
|
+
break;
|
|
1221
|
+
|
|
1222
|
+
// 65C02 NOPs with operands
|
|
1223
|
+
case 0x02:
|
|
1224
|
+
case 0x22:
|
|
1225
|
+
case 0x42:
|
|
1226
|
+
case 0x62:
|
|
1227
|
+
case 0x82:
|
|
1228
|
+
case 0xC2:
|
|
1229
|
+
case 0xE2:
|
|
1230
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1231
|
+
pc_++;
|
|
1232
|
+
break;
|
|
1233
|
+
case 0x44:
|
|
1234
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1235
|
+
pc_++;
|
|
1236
|
+
break;
|
|
1237
|
+
case 0x54:
|
|
1238
|
+
case 0xD4:
|
|
1239
|
+
case 0xF4:
|
|
1240
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1241
|
+
pc_++;
|
|
1242
|
+
break;
|
|
1243
|
+
case 0x5C:
|
|
1244
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1245
|
+
pc_ += 2;
|
|
1246
|
+
break;
|
|
1247
|
+
case 0xDC:
|
|
1248
|
+
case 0xFC:
|
|
1249
|
+
if (variant_ == CPUVariant::CMOS_65C02)
|
|
1250
|
+
pc_ += 2;
|
|
1251
|
+
break;
|
|
1252
|
+
|
|
1253
|
+
// TRB/TSB (65C02)
|
|
1254
|
+
case 0x14:
|
|
1255
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
1256
|
+
addr = addrZeroPage();
|
|
1257
|
+
value = read(addr);
|
|
1258
|
+
setFlag(FLAG_Z, (a_ & value) == 0);
|
|
1259
|
+
write(addr, value & ~a_);
|
|
1260
|
+
}
|
|
1261
|
+
break;
|
|
1262
|
+
case 0x1C:
|
|
1263
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
1264
|
+
addr = addrAbsolute();
|
|
1265
|
+
value = read(addr);
|
|
1266
|
+
setFlag(FLAG_Z, (a_ & value) == 0);
|
|
1267
|
+
write(addr, value & ~a_);
|
|
1268
|
+
}
|
|
1269
|
+
break;
|
|
1270
|
+
case 0x04:
|
|
1271
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
1272
|
+
addr = addrZeroPage();
|
|
1273
|
+
value = read(addr);
|
|
1274
|
+
setFlag(FLAG_Z, (a_ & value) == 0);
|
|
1275
|
+
write(addr, value | a_);
|
|
1276
|
+
}
|
|
1277
|
+
break;
|
|
1278
|
+
case 0x0C:
|
|
1279
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
1280
|
+
addr = addrAbsolute();
|
|
1281
|
+
value = read(addr);
|
|
1282
|
+
setFlag(FLAG_Z, (a_ & value) == 0);
|
|
1283
|
+
write(addr, value | a_);
|
|
1284
|
+
}
|
|
1285
|
+
break;
|
|
1286
|
+
|
|
1287
|
+
// BBR/BBS (65C02 - Rockwell/WDC)
|
|
1288
|
+
case 0x0F:
|
|
1289
|
+
case 0x1F:
|
|
1290
|
+
case 0x2F:
|
|
1291
|
+
case 0x3F:
|
|
1292
|
+
case 0x4F:
|
|
1293
|
+
case 0x5F:
|
|
1294
|
+
case 0x6F:
|
|
1295
|
+
case 0x7F:
|
|
1296
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
1297
|
+
addr = addrZeroPage();
|
|
1298
|
+
value = read(addr);
|
|
1299
|
+
int8_t offset = static_cast<int8_t>(fetch());
|
|
1300
|
+
int bit = (opcode >> 4) & 7;
|
|
1301
|
+
if (!(value & (1 << bit))) {
|
|
1302
|
+
pc_ += offset;
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
break;
|
|
1306
|
+
case 0x8F:
|
|
1307
|
+
case 0x9F:
|
|
1308
|
+
case 0xAF:
|
|
1309
|
+
case 0xBF:
|
|
1310
|
+
case 0xCF:
|
|
1311
|
+
case 0xDF:
|
|
1312
|
+
case 0xEF:
|
|
1313
|
+
case 0xFF:
|
|
1314
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
1315
|
+
addr = addrZeroPage();
|
|
1316
|
+
value = read(addr);
|
|
1317
|
+
int8_t offset = static_cast<int8_t>(fetch());
|
|
1318
|
+
int bit = (opcode >> 4) & 7;
|
|
1319
|
+
if (value & (1 << bit)) {
|
|
1320
|
+
pc_ += offset;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
break;
|
|
1324
|
+
|
|
1325
|
+
// RMB/SMB (65C02 - Rockwell/WDC)
|
|
1326
|
+
case 0x07:
|
|
1327
|
+
case 0x17:
|
|
1328
|
+
case 0x27:
|
|
1329
|
+
case 0x37:
|
|
1330
|
+
case 0x47:
|
|
1331
|
+
case 0x57:
|
|
1332
|
+
case 0x67:
|
|
1333
|
+
case 0x77:
|
|
1334
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
1335
|
+
addr = addrZeroPage();
|
|
1336
|
+
value = read(addr);
|
|
1337
|
+
int bit = (opcode >> 4) & 7;
|
|
1338
|
+
write(addr, value & ~(1 << bit));
|
|
1339
|
+
}
|
|
1340
|
+
break;
|
|
1341
|
+
case 0x87:
|
|
1342
|
+
case 0x97:
|
|
1343
|
+
case 0xA7:
|
|
1344
|
+
case 0xB7:
|
|
1345
|
+
case 0xC7:
|
|
1346
|
+
case 0xD7:
|
|
1347
|
+
case 0xE7:
|
|
1348
|
+
case 0xF7:
|
|
1349
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
1350
|
+
addr = addrZeroPage();
|
|
1351
|
+
value = read(addr);
|
|
1352
|
+
int bit = (opcode >> 4) & 7;
|
|
1353
|
+
write(addr, value | (1 << bit));
|
|
1354
|
+
}
|
|
1355
|
+
break;
|
|
1356
|
+
|
|
1357
|
+
// WAI/STP (65C02 - WDC)
|
|
1358
|
+
case 0xCB: // WAI - Wait for interrupt
|
|
1359
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
1360
|
+
// Wait until interrupt - simplified: just continue
|
|
1361
|
+
}
|
|
1362
|
+
break;
|
|
1363
|
+
case 0xDB: // STP - Stop processor
|
|
1364
|
+
if (variant_ == CPUVariant::CMOS_65C02) {
|
|
1365
|
+
// Stop until reset - simplified: do nothing
|
|
1366
|
+
}
|
|
1367
|
+
break;
|
|
1368
|
+
|
|
1369
|
+
default:
|
|
1370
|
+
// Unknown opcode - treat as NOP
|
|
1371
|
+
break;
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
std::string CPU6502::disassembleAt(uint16_t address) const {
|
|
1376
|
+
// Addressing mode enumeration
|
|
1377
|
+
enum AddrMode {
|
|
1378
|
+
IMP, // Implied
|
|
1379
|
+
ACC, // Accumulator
|
|
1380
|
+
IMM, // Immediate #$nn
|
|
1381
|
+
ZP, // Zero Page $nn
|
|
1382
|
+
ZPX, // Zero Page,X $nn,X
|
|
1383
|
+
ZPY, // Zero Page,Y $nn,Y
|
|
1384
|
+
ABS, // Absolute $nnnn
|
|
1385
|
+
ABX, // Absolute,X $nnnn,X
|
|
1386
|
+
ABY, // Absolute,Y $nnnn,Y
|
|
1387
|
+
IND, // Indirect ($nnnn)
|
|
1388
|
+
IZX, // Indexed Indirect ($nn,X)
|
|
1389
|
+
IZY, // Indirect Indexed ($nn),Y
|
|
1390
|
+
REL, // Relative (branches)
|
|
1391
|
+
ZPI, // Zero Page Indirect ($nn) - 65C02
|
|
1392
|
+
AIX, // Absolute Indexed Indirect ($nnnn,X) - 65C02
|
|
1393
|
+
ZPR // Zero Page Relative (BBR/BBS) - 65C02
|
|
1394
|
+
};
|
|
1395
|
+
|
|
1396
|
+
// Opcode info: mnemonic and addressing mode
|
|
1397
|
+
struct OpcodeInfo {
|
|
1398
|
+
const char *mnemonic;
|
|
1399
|
+
AddrMode mode;
|
|
1400
|
+
};
|
|
1401
|
+
|
|
1402
|
+
static const OpcodeInfo opcodes[256] = {
|
|
1403
|
+
// 0x00-0x0F
|
|
1404
|
+
{"BRK", IMP}, {"ORA", IZX}, {"???", IMP}, {"???", IMP},
|
|
1405
|
+
{"TSB", ZP}, {"ORA", ZP}, {"ASL", ZP}, {"RMB0", ZP},
|
|
1406
|
+
{"PHP", IMP}, {"ORA", IMM}, {"ASL", ACC}, {"???", IMP},
|
|
1407
|
+
{"TSB", ABS}, {"ORA", ABS}, {"ASL", ABS}, {"BBR0", ZPR},
|
|
1408
|
+
// 0x10-0x1F
|
|
1409
|
+
{"BPL", REL}, {"ORA", IZY}, {"ORA", ZPI}, {"???", IMP},
|
|
1410
|
+
{"TRB", ZP}, {"ORA", ZPX}, {"ASL", ZPX}, {"RMB1", ZP},
|
|
1411
|
+
{"CLC", IMP}, {"ORA", ABY}, {"INC", ACC}, {"???", IMP},
|
|
1412
|
+
{"TRB", ABS}, {"ORA", ABX}, {"ASL", ABX}, {"BBR1", ZPR},
|
|
1413
|
+
// 0x20-0x2F
|
|
1414
|
+
{"JSR", ABS}, {"AND", IZX}, {"???", IMP}, {"???", IMP},
|
|
1415
|
+
{"BIT", ZP}, {"AND", ZP}, {"ROL", ZP}, {"RMB2", ZP},
|
|
1416
|
+
{"PLP", IMP}, {"AND", IMM}, {"ROL", ACC}, {"???", IMP},
|
|
1417
|
+
{"BIT", ABS}, {"AND", ABS}, {"ROL", ABS}, {"BBR2", ZPR},
|
|
1418
|
+
// 0x30-0x3F
|
|
1419
|
+
{"BMI", REL}, {"AND", IZY}, {"AND", ZPI}, {"???", IMP},
|
|
1420
|
+
{"BIT", ZPX}, {"AND", ZPX}, {"ROL", ZPX}, {"RMB3", ZP},
|
|
1421
|
+
{"SEC", IMP}, {"AND", ABY}, {"DEC", ACC}, {"???", IMP},
|
|
1422
|
+
{"BIT", ABX}, {"AND", ABX}, {"ROL", ABX}, {"BBR3", ZPR},
|
|
1423
|
+
// 0x40-0x4F
|
|
1424
|
+
{"RTI", IMP}, {"EOR", IZX}, {"???", IMP}, {"???", IMP},
|
|
1425
|
+
{"???", IMP}, {"EOR", ZP}, {"LSR", ZP}, {"RMB4", ZP},
|
|
1426
|
+
{"PHA", IMP}, {"EOR", IMM}, {"LSR", ACC}, {"???", IMP},
|
|
1427
|
+
{"JMP", ABS}, {"EOR", ABS}, {"LSR", ABS}, {"BBR4", ZPR},
|
|
1428
|
+
// 0x50-0x5F
|
|
1429
|
+
{"BVC", REL}, {"EOR", IZY}, {"EOR", ZPI}, {"???", IMP},
|
|
1430
|
+
{"???", IMP}, {"EOR", ZPX}, {"LSR", ZPX}, {"RMB5", ZP},
|
|
1431
|
+
{"CLI", IMP}, {"EOR", ABY}, {"PHY", IMP}, {"???", IMP},
|
|
1432
|
+
{"???", IMP}, {"EOR", ABX}, {"LSR", ABX}, {"BBR5", ZPR},
|
|
1433
|
+
// 0x60-0x6F
|
|
1434
|
+
{"RTS", IMP}, {"ADC", IZX}, {"???", IMP}, {"???", IMP},
|
|
1435
|
+
{"STZ", ZP}, {"ADC", ZP}, {"ROR", ZP}, {"RMB6", ZP},
|
|
1436
|
+
{"PLA", IMP}, {"ADC", IMM}, {"ROR", ACC}, {"???", IMP},
|
|
1437
|
+
{"JMP", IND}, {"ADC", ABS}, {"ROR", ABS}, {"BBR6", ZPR},
|
|
1438
|
+
// 0x70-0x7F
|
|
1439
|
+
{"BVS", REL}, {"ADC", IZY}, {"ADC", ZPI}, {"???", IMP},
|
|
1440
|
+
{"STZ", ZPX}, {"ADC", ZPX}, {"ROR", ZPX}, {"RMB7", ZP},
|
|
1441
|
+
{"SEI", IMP}, {"ADC", ABY}, {"PLY", IMP}, {"???", IMP},
|
|
1442
|
+
{"JMP", AIX}, {"ADC", ABX}, {"ROR", ABX}, {"BBR7", ZPR},
|
|
1443
|
+
// 0x80-0x8F
|
|
1444
|
+
{"BRA", REL}, {"STA", IZX}, {"???", IMP}, {"???", IMP},
|
|
1445
|
+
{"STY", ZP}, {"STA", ZP}, {"STX", ZP}, {"SMB0", ZP},
|
|
1446
|
+
{"DEY", IMP}, {"BIT", IMM}, {"TXA", IMP}, {"???", IMP},
|
|
1447
|
+
{"STY", ABS}, {"STA", ABS}, {"STX", ABS}, {"BBS0", ZPR},
|
|
1448
|
+
// 0x90-0x9F
|
|
1449
|
+
{"BCC", REL}, {"STA", IZY}, {"STA", ZPI}, {"???", IMP},
|
|
1450
|
+
{"STY", ZPX}, {"STA", ZPX}, {"STX", ZPY}, {"SMB1", ZP},
|
|
1451
|
+
{"TYA", IMP}, {"STA", ABY}, {"TXS", IMP}, {"???", IMP},
|
|
1452
|
+
{"STZ", ABS}, {"STA", ABX}, {"STZ", ABX}, {"BBS1", ZPR},
|
|
1453
|
+
// 0xA0-0xAF
|
|
1454
|
+
{"LDY", IMM}, {"LDA", IZX}, {"LDX", IMM}, {"???", IMP},
|
|
1455
|
+
{"LDY", ZP}, {"LDA", ZP}, {"LDX", ZP}, {"SMB2", ZP},
|
|
1456
|
+
{"TAY", IMP}, {"LDA", IMM}, {"TAX", IMP}, {"???", IMP},
|
|
1457
|
+
{"LDY", ABS}, {"LDA", ABS}, {"LDX", ABS}, {"BBS2", ZPR},
|
|
1458
|
+
// 0xB0-0xBF
|
|
1459
|
+
{"BCS", REL}, {"LDA", IZY}, {"LDA", ZPI}, {"???", IMP},
|
|
1460
|
+
{"LDY", ZPX}, {"LDA", ZPX}, {"LDX", ZPY}, {"SMB3", ZP},
|
|
1461
|
+
{"CLV", IMP}, {"LDA", ABY}, {"TSX", IMP}, {"???", IMP},
|
|
1462
|
+
{"LDY", ABX}, {"LDA", ABX}, {"LDX", ABY}, {"BBS3", ZPR},
|
|
1463
|
+
// 0xC0-0xCF
|
|
1464
|
+
{"CPY", IMM}, {"CMP", IZX}, {"???", IMP}, {"???", IMP},
|
|
1465
|
+
{"CPY", ZP}, {"CMP", ZP}, {"DEC", ZP}, {"SMB4", ZP},
|
|
1466
|
+
{"INY", IMP}, {"CMP", IMM}, {"DEX", IMP}, {"WAI", IMP},
|
|
1467
|
+
{"CPY", ABS}, {"CMP", ABS}, {"DEC", ABS}, {"BBS4", ZPR},
|
|
1468
|
+
// 0xD0-0xDF
|
|
1469
|
+
{"BNE", REL}, {"CMP", IZY}, {"CMP", ZPI}, {"???", IMP},
|
|
1470
|
+
{"???", IMP}, {"CMP", ZPX}, {"DEC", ZPX}, {"SMB5", ZP},
|
|
1471
|
+
{"CLD", IMP}, {"CMP", ABY}, {"PHX", IMP}, {"STP", IMP},
|
|
1472
|
+
{"???", IMP}, {"CMP", ABX}, {"DEC", ABX}, {"BBS5", ZPR},
|
|
1473
|
+
// 0xE0-0xEF
|
|
1474
|
+
{"CPX", IMM}, {"SBC", IZX}, {"???", IMP}, {"???", IMP},
|
|
1475
|
+
{"CPX", ZP}, {"SBC", ZP}, {"INC", ZP}, {"SMB6", ZP},
|
|
1476
|
+
{"INX", IMP}, {"SBC", IMM}, {"NOP", IMP}, {"???", IMP},
|
|
1477
|
+
{"CPX", ABS}, {"SBC", ABS}, {"INC", ABS}, {"BBS6", ZPR},
|
|
1478
|
+
// 0xF0-0xFF
|
|
1479
|
+
{"BEQ", REL}, {"SBC", IZY}, {"SBC", ZPI}, {"???", IMP},
|
|
1480
|
+
{"???", IMP}, {"SBC", ZPX}, {"INC", ZPX}, {"SMB7", ZP},
|
|
1481
|
+
{"SED", IMP}, {"SBC", ABY}, {"PLX", IMP}, {"???", IMP},
|
|
1482
|
+
{"???", IMP}, {"SBC", ABX}, {"INC", ABX}, {"BBS7", ZPR}
|
|
1483
|
+
};
|
|
1484
|
+
|
|
1485
|
+
std::ostringstream ss;
|
|
1486
|
+
ss << std::hex << std::uppercase << std::setfill('0');
|
|
1487
|
+
|
|
1488
|
+
uint8_t opcode = read_(address);
|
|
1489
|
+
const OpcodeInfo &info = opcodes[opcode];
|
|
1490
|
+
|
|
1491
|
+
// Read operand bytes based on addressing mode
|
|
1492
|
+
uint8_t lo = 0, hi = 0;
|
|
1493
|
+
int instrLen = 1;
|
|
1494
|
+
|
|
1495
|
+
switch (info.mode) {
|
|
1496
|
+
case IMP:
|
|
1497
|
+
case ACC:
|
|
1498
|
+
instrLen = 1;
|
|
1499
|
+
break;
|
|
1500
|
+
case IMM:
|
|
1501
|
+
case ZP:
|
|
1502
|
+
case ZPX:
|
|
1503
|
+
case ZPY:
|
|
1504
|
+
case IZX:
|
|
1505
|
+
case IZY:
|
|
1506
|
+
case ZPI:
|
|
1507
|
+
case REL:
|
|
1508
|
+
lo = read_(address + 1);
|
|
1509
|
+
instrLen = 2;
|
|
1510
|
+
break;
|
|
1511
|
+
case ABS:
|
|
1512
|
+
case ABX:
|
|
1513
|
+
case ABY:
|
|
1514
|
+
case IND:
|
|
1515
|
+
case AIX:
|
|
1516
|
+
lo = read_(address + 1);
|
|
1517
|
+
hi = read_(address + 2);
|
|
1518
|
+
instrLen = 3;
|
|
1519
|
+
break;
|
|
1520
|
+
case ZPR:
|
|
1521
|
+
lo = read_(address + 1);
|
|
1522
|
+
hi = read_(address + 2);
|
|
1523
|
+
instrLen = 3;
|
|
1524
|
+
break;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
// Format: "AAAA: BB BB BB MMM OPERAND"
|
|
1528
|
+
ss << std::setw(4) << address << ": ";
|
|
1529
|
+
|
|
1530
|
+
// Output instruction bytes
|
|
1531
|
+
ss << std::setw(2) << static_cast<int>(opcode);
|
|
1532
|
+
if (instrLen >= 2) {
|
|
1533
|
+
ss << " " << std::setw(2) << static_cast<int>(lo);
|
|
1534
|
+
} else {
|
|
1535
|
+
ss << " ";
|
|
1536
|
+
}
|
|
1537
|
+
if (instrLen >= 3) {
|
|
1538
|
+
ss << " " << std::setw(2) << static_cast<int>(hi);
|
|
1539
|
+
} else {
|
|
1540
|
+
ss << " ";
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
// Mnemonic
|
|
1544
|
+
ss << " " << info.mnemonic;
|
|
1545
|
+
|
|
1546
|
+
// Format operand based on addressing mode
|
|
1547
|
+
switch (info.mode) {
|
|
1548
|
+
case IMP:
|
|
1549
|
+
break;
|
|
1550
|
+
case ACC:
|
|
1551
|
+
ss << " A";
|
|
1552
|
+
break;
|
|
1553
|
+
case IMM:
|
|
1554
|
+
ss << " #$" << std::setw(2) << static_cast<int>(lo);
|
|
1555
|
+
break;
|
|
1556
|
+
case ZP:
|
|
1557
|
+
ss << " $" << std::setw(2) << static_cast<int>(lo);
|
|
1558
|
+
break;
|
|
1559
|
+
case ZPX:
|
|
1560
|
+
ss << " $" << std::setw(2) << static_cast<int>(lo) << ",X";
|
|
1561
|
+
break;
|
|
1562
|
+
case ZPY:
|
|
1563
|
+
ss << " $" << std::setw(2) << static_cast<int>(lo) << ",Y";
|
|
1564
|
+
break;
|
|
1565
|
+
case ABS:
|
|
1566
|
+
ss << " $" << std::setw(4) << static_cast<int>((hi << 8) | lo);
|
|
1567
|
+
break;
|
|
1568
|
+
case ABX:
|
|
1569
|
+
ss << " $" << std::setw(4) << static_cast<int>((hi << 8) | lo) << ",X";
|
|
1570
|
+
break;
|
|
1571
|
+
case ABY:
|
|
1572
|
+
ss << " $" << std::setw(4) << static_cast<int>((hi << 8) | lo) << ",Y";
|
|
1573
|
+
break;
|
|
1574
|
+
case IND:
|
|
1575
|
+
ss << " ($" << std::setw(4) << static_cast<int>((hi << 8) | lo) << ")";
|
|
1576
|
+
break;
|
|
1577
|
+
case IZX:
|
|
1578
|
+
ss << " ($" << std::setw(2) << static_cast<int>(lo) << ",X)";
|
|
1579
|
+
break;
|
|
1580
|
+
case IZY:
|
|
1581
|
+
ss << " ($" << std::setw(2) << static_cast<int>(lo) << "),Y";
|
|
1582
|
+
break;
|
|
1583
|
+
case ZPI:
|
|
1584
|
+
ss << " ($" << std::setw(2) << static_cast<int>(lo) << ")";
|
|
1585
|
+
break;
|
|
1586
|
+
case AIX:
|
|
1587
|
+
ss << " ($" << std::setw(4) << static_cast<int>((hi << 8) | lo) << ",X)";
|
|
1588
|
+
break;
|
|
1589
|
+
case REL: {
|
|
1590
|
+
// Calculate branch target
|
|
1591
|
+
int8_t offset = static_cast<int8_t>(lo);
|
|
1592
|
+
uint16_t target = address + 2 + offset;
|
|
1593
|
+
ss << " $" << std::setw(4) << static_cast<int>(target);
|
|
1594
|
+
break;
|
|
1595
|
+
}
|
|
1596
|
+
case ZPR: {
|
|
1597
|
+
// BBR/BBS: zero page address and relative branch
|
|
1598
|
+
int8_t offset = static_cast<int8_t>(hi);
|
|
1599
|
+
uint16_t target = address + 3 + offset;
|
|
1600
|
+
ss << " $" << std::setw(2) << static_cast<int>(lo) << ",$"
|
|
1601
|
+
<< std::setw(4) << static_cast<int>(target);
|
|
1602
|
+
break;
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
return ss.str();
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
} // namespace a2e
|