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,603 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* smartport_card.cpp - SmartPort expansion card for ProDOS block devices
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Mike Daley <michael_daley@icloud.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include "smartport_card.hpp"
|
|
9
|
+
#include <cstring>
|
|
10
|
+
|
|
11
|
+
namespace a2e {
|
|
12
|
+
|
|
13
|
+
const std::string SmartPortCard::emptyString_;
|
|
14
|
+
|
|
15
|
+
// ProDOS block device entry point offset in slot ROM
|
|
16
|
+
static constexpr uint8_t PRODOS_ENTRY = 0x10;
|
|
17
|
+
// SmartPort entry = ProDOS entry + 3
|
|
18
|
+
static constexpr uint8_t SMARTPORT_ENTRY = 0x13;
|
|
19
|
+
// Boot trigger offset in I/O space
|
|
20
|
+
static constexpr uint8_t BOOT_IO_OFFSET = 0x00;
|
|
21
|
+
|
|
22
|
+
// SmartPort error codes
|
|
23
|
+
static constexpr uint8_t SP_OK = 0x00;
|
|
24
|
+
static constexpr uint8_t SP_IO_ERROR = 0x27;
|
|
25
|
+
static constexpr uint8_t SP_NO_DEVICE = 0x28;
|
|
26
|
+
static constexpr uint8_t SP_WRITE_PROTECTED = 0x2B;
|
|
27
|
+
|
|
28
|
+
SmartPortCard::SmartPortCard() {
|
|
29
|
+
rom_.fill(0);
|
|
30
|
+
buildROM();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
void SmartPortCard::setSlotNumber(uint8_t slot) {
|
|
34
|
+
if (slot < 1 || slot > 7) return;
|
|
35
|
+
slotNum_ = slot;
|
|
36
|
+
buildROM();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
void SmartPortCard::buildROM() {
|
|
40
|
+
rom_.fill(0);
|
|
41
|
+
|
|
42
|
+
// Signature bytes for ProDOS device discovery.
|
|
43
|
+
// ProDOS checks odd bytes at $Cn01, $Cn03, $Cn05, $Cn07.
|
|
44
|
+
// We embed them as LDA# operands so they occupy the right offsets.
|
|
45
|
+
|
|
46
|
+
// $00: LDA #$20 -> $Cn01 = $20 (ProDOS signature)
|
|
47
|
+
rom_[0x00] = 0xA9; rom_[0x01] = 0x20;
|
|
48
|
+
// $02: LDA #$00 -> $Cn03 = $00 (block device)
|
|
49
|
+
rom_[0x02] = 0xA9; rom_[0x03] = 0x00;
|
|
50
|
+
// $04: LDA #$03 -> $Cn05 = $03 (identifies as SmartPort-capable)
|
|
51
|
+
rom_[0x04] = 0xA9; rom_[0x05] = 0x03;
|
|
52
|
+
// $06: LDA #$00 -> $Cn07 = $00 (SmartPort present)
|
|
53
|
+
rom_[0x06] = 0xA9; rom_[0x07] = 0x00;
|
|
54
|
+
|
|
55
|
+
// Boot code at $08: set X to slot*16, trigger I/O boot, JMP $0801
|
|
56
|
+
// This path is used by PR#n from BASIC (execution falls through from $Cn00)
|
|
57
|
+
uint8_t slotOffset = slotNum_ << 4; // slot * 16
|
|
58
|
+
uint8_t ioAddr = 0x80 + slotOffset; // $C0n0 base
|
|
59
|
+
|
|
60
|
+
// LDX #$n0 (set X to slot*16, needed by ProDOS boot block)
|
|
61
|
+
rom_[0x08] = 0xA2;
|
|
62
|
+
rom_[0x09] = slotOffset;
|
|
63
|
+
// STX $C0n0 (trigger I/O trap for boot block load)
|
|
64
|
+
rom_[0x0A] = 0x8E;
|
|
65
|
+
rom_[0x0B] = ioAddr;
|
|
66
|
+
rom_[0x0C] = 0xC0;
|
|
67
|
+
// RTS - if boot succeeded, writeIO pushed $0800 on stack so RTS goes to $0801.
|
|
68
|
+
// If no disk loaded, RTS returns to the autostart ROM caller which scans the next slot.
|
|
69
|
+
rom_[0x0D] = 0x60;
|
|
70
|
+
|
|
71
|
+
// ProDOS block device entry at $10: SEC + RTS (trapped by readROM)
|
|
72
|
+
rom_[PRODOS_ENTRY] = 0x38; // SEC
|
|
73
|
+
rom_[PRODOS_ENTRY + 1] = 0x60; // RTS
|
|
74
|
+
|
|
75
|
+
// Padding byte at $12
|
|
76
|
+
rom_[0x12] = 0xEA; // NOP
|
|
77
|
+
|
|
78
|
+
// SmartPort entry at $13: SEC + RTS (trapped by readROM)
|
|
79
|
+
rom_[SMARTPORT_ENTRY] = 0x38; // SEC
|
|
80
|
+
rom_[SMARTPORT_ENTRY + 1] = 0x60; // RTS
|
|
81
|
+
|
|
82
|
+
// $FF: ProDOS entry point offset (used by both autostart ROM boot and ProDOS driver)
|
|
83
|
+
// The readROM trap at $Cn10 distinguishes boot vs ProDOS calls via the booted_ flag.
|
|
84
|
+
rom_[0xFF] = PRODOS_ENTRY;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
bool SmartPortCard::hasAnyDevice() const {
|
|
88
|
+
for (int i = 0; i < MAX_DEVICES; i++) {
|
|
89
|
+
if (devices_[i].isLoaded()) return true;
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
void SmartPortCard::reset() {
|
|
95
|
+
booted_ = false;
|
|
96
|
+
activity_ = false;
|
|
97
|
+
activityWrite_ = false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
uint8_t SmartPortCard::readIO(uint8_t offset) {
|
|
101
|
+
(void)offset;
|
|
102
|
+
return 0xFF;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
void SmartPortCard::writeIO(uint8_t offset, uint8_t value) {
|
|
106
|
+
if (offset == BOOT_IO_OFFSET) {
|
|
107
|
+
// Boot trap: load block 0 of device 0 into $0800
|
|
108
|
+
if (!devices_[0].isLoaded() || !memWrite_ || !getSP_ || !setSP_) {
|
|
109
|
+
// Mark as booted so subsequent ProDOS calls to $Cn10 are handled
|
|
110
|
+
// as block device driver calls rather than triggering another boot.
|
|
111
|
+
booted_ = true;
|
|
112
|
+
// No disk loaded. Scan lower slots for the next bootable device,
|
|
113
|
+
// mimicking the autostart ROM's 7→1 scan that was interrupted.
|
|
114
|
+
if (setPC_ && memRead_) {
|
|
115
|
+
for (int slot = slotNum_ - 1; slot >= 1; slot--) {
|
|
116
|
+
uint16_t base = 0xC000 | (slot << 8);
|
|
117
|
+
uint8_t sig = memRead_(base + 1); // $Cn01
|
|
118
|
+
if (sig == 0x20) {
|
|
119
|
+
// ProDOS-compatible device found (Disk II, SmartPort, etc.)
|
|
120
|
+
uint8_t entry = memRead_(base + 0xFF);
|
|
121
|
+
setPC_(base + (entry ? entry : 0));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// No bootable slot found — fall through to Applesoft prompt
|
|
126
|
+
setPC_(0xE003);
|
|
127
|
+
}
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
uint8_t blockBuf[BlockDevice::BLOCK_SIZE];
|
|
132
|
+
if (devices_[0].readBlock(0, blockBuf)) {
|
|
133
|
+
for (size_t i = 0; i < BlockDevice::BLOCK_SIZE; i++) {
|
|
134
|
+
memWrite_(static_cast<uint16_t>(0x0800 + i), blockBuf[i]);
|
|
135
|
+
}
|
|
136
|
+
activity_ = true;
|
|
137
|
+
activityWrite_ = false;
|
|
138
|
+
|
|
139
|
+
// Set X to slot*16 (ProDOS boot block expects this)
|
|
140
|
+
if (setX_) setX_(slotNum_ << 4);
|
|
141
|
+
|
|
142
|
+
// Push $0800 onto the stack so the RTS in boot ROM goes to $0801
|
|
143
|
+
uint8_t sp = getSP_();
|
|
144
|
+
memWrite_(0x0100 + sp, 0x08); // high byte
|
|
145
|
+
sp = static_cast<uint8_t>(sp - 1);
|
|
146
|
+
memWrite_(0x0100 + sp, 0x00); // low byte
|
|
147
|
+
sp = static_cast<uint8_t>(sp - 1);
|
|
148
|
+
setSP_(sp);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
uint8_t SmartPortCard::readROM(uint8_t offset) {
|
|
154
|
+
// When no devices are loaded, hide the ROM so ProDOS doesn't detect this slot
|
|
155
|
+
if (!hasAnyDevice()) return 0;
|
|
156
|
+
|
|
157
|
+
// Check if the CPU is executing at this ROM address (not just reading data).
|
|
158
|
+
// The CPU's fetch() does read(pc_++) so by the time the read callback fires,
|
|
159
|
+
// PC has already been incremented by 1. We account for this by comparing
|
|
160
|
+
// against expectedPC + 1.
|
|
161
|
+
uint16_t expectedPC = (0xC000 | (static_cast<uint16_t>(slotNum_) << 8)) + offset;
|
|
162
|
+
|
|
163
|
+
if (getPC_ && getPC_() == static_cast<uint16_t>(expectedPC + 1)) {
|
|
164
|
+
if (offset == PRODOS_ENTRY) {
|
|
165
|
+
if (!booted_) {
|
|
166
|
+
// First call to entry point = boot (from autostart ROM or PR#n fallthrough)
|
|
167
|
+
if (!handleBoot()) {
|
|
168
|
+
// No disk loaded. Scan lower slots for the next bootable
|
|
169
|
+
// device, continuing the autostart ROM's 7→1 scan.
|
|
170
|
+
if (setPC_ && memRead_) {
|
|
171
|
+
for (int slot = slotNum_ - 1; slot >= 1; slot--) {
|
|
172
|
+
uint16_t base = 0xC000 | (slot << 8);
|
|
173
|
+
uint8_t sig = memRead_(base + 1);
|
|
174
|
+
if (sig == 0x20) {
|
|
175
|
+
uint8_t entry = memRead_(base + 0xFF);
|
|
176
|
+
setPC_(base + (entry ? entry : 0));
|
|
177
|
+
return 0xEA; // NOP
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
setPC_(0xE003); // No bootable slot — Applesoft prompt
|
|
181
|
+
}
|
|
182
|
+
return 0xEA; // NOP (harmless; CPU continues from new PC)
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
// Subsequent calls = ProDOS block driver calls
|
|
186
|
+
handleProDOSBlock();
|
|
187
|
+
}
|
|
188
|
+
return 0x60; // RTS
|
|
189
|
+
}
|
|
190
|
+
if (offset == SMARTPORT_ENTRY) {
|
|
191
|
+
handleSmartPort();
|
|
192
|
+
return 0x60; // RTS
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return rom_[offset];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
bool SmartPortCard::handleBoot() {
|
|
200
|
+
// Called when the autostart ROM or PR#n reaches the entry point for the first time.
|
|
201
|
+
// Load block 0 of device 0 into $0800, set X to slot*16, and arrange
|
|
202
|
+
// for the CPU to jump to $0801 (ProDOS boot block entry) via RTS.
|
|
203
|
+
booted_ = true;
|
|
204
|
+
|
|
205
|
+
if (!devices_[0].isLoaded() || !memWrite_ || !getSP_ || !setSP_) return false;
|
|
206
|
+
|
|
207
|
+
// Load block 0 into $0800
|
|
208
|
+
uint8_t blockBuf[BlockDevice::BLOCK_SIZE];
|
|
209
|
+
if (!devices_[0].readBlock(0, blockBuf)) return false;
|
|
210
|
+
|
|
211
|
+
for (size_t i = 0; i < BlockDevice::BLOCK_SIZE; i++) {
|
|
212
|
+
memWrite_(static_cast<uint16_t>(0x0800 + i), blockBuf[i]);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
activity_ = true;
|
|
216
|
+
activityWrite_ = false;
|
|
217
|
+
|
|
218
|
+
// Set X to slot*16 (ProDOS boot block expects this)
|
|
219
|
+
if (setX_) setX_(slotNum_ << 4);
|
|
220
|
+
|
|
221
|
+
// Push $0800 onto the stack so RTS goes to $0801 (RTS adds 1 to popped address)
|
|
222
|
+
uint8_t sp = getSP_();
|
|
223
|
+
memWrite_(0x0100 + sp, 0x08); // high byte
|
|
224
|
+
sp = static_cast<uint8_t>(sp - 1);
|
|
225
|
+
memWrite_(0x0100 + sp, 0x00); // low byte
|
|
226
|
+
sp = static_cast<uint8_t>(sp - 1);
|
|
227
|
+
setSP_(sp);
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
void SmartPortCard::setErrorResult(uint8_t errorCode) {
|
|
232
|
+
if (!setA_ || !getP_ || !setP_) return;
|
|
233
|
+
|
|
234
|
+
setA_(errorCode);
|
|
235
|
+
|
|
236
|
+
uint8_t p = getP_();
|
|
237
|
+
if (errorCode != SP_OK) {
|
|
238
|
+
p |= 0x01; // Set carry (error)
|
|
239
|
+
} else {
|
|
240
|
+
p &= ~0x01; // Clear carry (success)
|
|
241
|
+
}
|
|
242
|
+
setP_(p);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
void SmartPortCard::handleProDOSBlock() {
|
|
246
|
+
// ProDOS block call convention:
|
|
247
|
+
// $42 = command (0=STATUS, 1=READ, 2=WRITE, 3=FORMAT)
|
|
248
|
+
// $43 = unit number (bit 7: 0=device 0, 1=device 1; bits 4-6: slot)
|
|
249
|
+
// $44-$45 = buffer pointer (lo/hi)
|
|
250
|
+
// $46-$47 = block number (lo/hi)
|
|
251
|
+
if (!memRead_ || !memWrite_ || !setA_ || !setP_) return;
|
|
252
|
+
|
|
253
|
+
uint8_t command = memRead_(0x42);
|
|
254
|
+
uint8_t unitNum = memRead_(0x43);
|
|
255
|
+
uint16_t bufPtr = memRead_(0x44) | (memRead_(0x45) << 8);
|
|
256
|
+
uint16_t blockNum = memRead_(0x46) | (memRead_(0x47) << 8);
|
|
257
|
+
|
|
258
|
+
// Device index from unit number bit 7
|
|
259
|
+
int device = (unitNum & 0x80) ? 1 : 0;
|
|
260
|
+
|
|
261
|
+
activity_ = true;
|
|
262
|
+
activityWrite_ = (command == 2);
|
|
263
|
+
|
|
264
|
+
switch (command) {
|
|
265
|
+
case 0: { // STATUS
|
|
266
|
+
if (!devices_[device].isLoaded()) {
|
|
267
|
+
setErrorResult(SP_NO_DEVICE);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
// ProDOS expects block count in X (lo) and Y (hi) registers
|
|
271
|
+
uint16_t blocks = devices_[device].getTotalBlocks();
|
|
272
|
+
if (setX_) setX_(blocks & 0xFF);
|
|
273
|
+
if (setY_) setY_((blocks >> 8) & 0xFF);
|
|
274
|
+
setErrorResult(SP_OK);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
case 1: { // READ
|
|
279
|
+
if (!devices_[device].isLoaded()) {
|
|
280
|
+
setErrorResult(SP_NO_DEVICE);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
uint8_t blockBuf[BlockDevice::BLOCK_SIZE];
|
|
284
|
+
if (!devices_[device].readBlock(blockNum, blockBuf)) {
|
|
285
|
+
setErrorResult(SP_IO_ERROR);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
for (size_t i = 0; i < BlockDevice::BLOCK_SIZE; i++) {
|
|
289
|
+
memWrite_(static_cast<uint16_t>(bufPtr + i), blockBuf[i]);
|
|
290
|
+
}
|
|
291
|
+
setErrorResult(SP_OK);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
case 2: { // WRITE
|
|
296
|
+
if (!devices_[device].isLoaded()) {
|
|
297
|
+
setErrorResult(SP_NO_DEVICE);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if (devices_[device].isWriteProtected()) {
|
|
301
|
+
setErrorResult(SP_WRITE_PROTECTED);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
uint8_t blockBuf[BlockDevice::BLOCK_SIZE];
|
|
305
|
+
for (size_t i = 0; i < BlockDevice::BLOCK_SIZE; i++) {
|
|
306
|
+
blockBuf[i] = memRead_(static_cast<uint16_t>(bufPtr + i));
|
|
307
|
+
}
|
|
308
|
+
if (!devices_[device].writeBlock(blockNum, blockBuf)) {
|
|
309
|
+
setErrorResult(SP_IO_ERROR);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
setErrorResult(SP_OK);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
case 3: { // FORMAT
|
|
317
|
+
// We don't actually format - just return OK if device is present
|
|
318
|
+
if (!devices_[device].isLoaded()) {
|
|
319
|
+
setErrorResult(SP_NO_DEVICE);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
setErrorResult(SP_OK);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
default:
|
|
327
|
+
setErrorResult(SP_IO_ERROR);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
void SmartPortCard::handleSmartPort() {
|
|
333
|
+
// SmartPort call convention:
|
|
334
|
+
// After JSR $Cn13, the inline bytes are:
|
|
335
|
+
// +0: command byte
|
|
336
|
+
// +1,+2: parameter list pointer (lo/hi)
|
|
337
|
+
// We need to read these from after the JSR instruction,
|
|
338
|
+
// then adjust the return address on the stack by +3.
|
|
339
|
+
if (!memRead_ || !memWrite_ || !setA_ || !setP_ || !getSP_ || !setSP_) return;
|
|
340
|
+
|
|
341
|
+
uint8_t sp = getSP_();
|
|
342
|
+
|
|
343
|
+
// Read return address from stack (points to byte before inline params)
|
|
344
|
+
uint8_t retLo = memRead_(0x0100 + ((sp + 1) & 0xFF));
|
|
345
|
+
uint8_t retHi = memRead_(0x0100 + ((sp + 2) & 0xFF));
|
|
346
|
+
uint16_t retAddr = (retHi << 8) | retLo;
|
|
347
|
+
|
|
348
|
+
// Inline params start at retAddr + 1
|
|
349
|
+
uint16_t inlineAddr = retAddr + 1;
|
|
350
|
+
uint8_t command = memRead_(inlineAddr);
|
|
351
|
+
uint16_t paramPtr = memRead_(inlineAddr + 1) | (memRead_(inlineAddr + 2) << 8);
|
|
352
|
+
|
|
353
|
+
// Adjust return address past the 3 inline bytes
|
|
354
|
+
uint16_t newRet = retAddr + 3;
|
|
355
|
+
memWrite_(0x0100 + ((sp + 1) & 0xFF), newRet & 0xFF);
|
|
356
|
+
memWrite_(0x0100 + ((sp + 2) & 0xFF), (newRet >> 8) & 0xFF);
|
|
357
|
+
|
|
358
|
+
activity_ = true;
|
|
359
|
+
activityWrite_ = (command == 0x02);
|
|
360
|
+
|
|
361
|
+
switch (command) {
|
|
362
|
+
case 0x00: { // STATUS
|
|
363
|
+
uint8_t paramCount = memRead_(paramPtr);
|
|
364
|
+
uint8_t unitNum = memRead_(paramPtr + 1);
|
|
365
|
+
uint16_t statusBuf = memRead_(paramPtr + 2) | (memRead_(paramPtr + 3) << 8);
|
|
366
|
+
uint8_t statusCode = memRead_(paramPtr + 4);
|
|
367
|
+
(void)paramCount;
|
|
368
|
+
|
|
369
|
+
if (unitNum == 0) {
|
|
370
|
+
// Unit 0 STATUS: return number of devices
|
|
371
|
+
int count = 0;
|
|
372
|
+
for (int i = 0; i < MAX_DEVICES; i++) {
|
|
373
|
+
if (devices_[i].isLoaded()) count = i + 1;
|
|
374
|
+
}
|
|
375
|
+
// Write device count to status buffer
|
|
376
|
+
memWrite_(statusBuf, static_cast<uint8_t>(count));
|
|
377
|
+
setErrorResult(SP_OK);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
int device = unitNum - 1;
|
|
382
|
+
if (device < 0 || device >= MAX_DEVICES || !devices_[device].isLoaded()) {
|
|
383
|
+
setErrorResult(SP_NO_DEVICE);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (statusCode == 0x00) {
|
|
388
|
+
// General status: 4 bytes
|
|
389
|
+
uint8_t statusByte = 0xF8; // block device, read/write, online, format capable
|
|
390
|
+
if (devices_[device].isWriteProtected()) {
|
|
391
|
+
statusByte |= 0x04; // write protected
|
|
392
|
+
}
|
|
393
|
+
memWrite_(statusBuf, statusByte);
|
|
394
|
+
uint16_t blocks = devices_[device].getTotalBlocks();
|
|
395
|
+
memWrite_(statusBuf + 1, blocks & 0xFF);
|
|
396
|
+
memWrite_(statusBuf + 2, (blocks >> 8) & 0xFF);
|
|
397
|
+
memWrite_(statusBuf + 3, 0x00); // blocks high byte (always 0 for 16-bit)
|
|
398
|
+
}
|
|
399
|
+
setErrorResult(SP_OK);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
case 0x01: { // READ BLOCK
|
|
404
|
+
uint8_t unitNum = memRead_(paramPtr + 1);
|
|
405
|
+
uint16_t dataBuf = memRead_(paramPtr + 2) | (memRead_(paramPtr + 3) << 8);
|
|
406
|
+
uint16_t blockNum = memRead_(paramPtr + 4) | (memRead_(paramPtr + 5) << 8);
|
|
407
|
+
|
|
408
|
+
int device = unitNum - 1;
|
|
409
|
+
if (device < 0 || device >= MAX_DEVICES || !devices_[device].isLoaded()) {
|
|
410
|
+
setErrorResult(SP_NO_DEVICE);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
uint8_t blockBuf[BlockDevice::BLOCK_SIZE];
|
|
415
|
+
if (!devices_[device].readBlock(blockNum, blockBuf)) {
|
|
416
|
+
setErrorResult(SP_IO_ERROR);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
for (size_t i = 0; i < BlockDevice::BLOCK_SIZE; i++) {
|
|
420
|
+
memWrite_(static_cast<uint16_t>(dataBuf + i), blockBuf[i]);
|
|
421
|
+
}
|
|
422
|
+
setErrorResult(SP_OK);
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
case 0x02: { // WRITE BLOCK
|
|
427
|
+
uint8_t unitNum = memRead_(paramPtr + 1);
|
|
428
|
+
uint16_t dataBuf = memRead_(paramPtr + 2) | (memRead_(paramPtr + 3) << 8);
|
|
429
|
+
uint16_t blockNum = memRead_(paramPtr + 4) | (memRead_(paramPtr + 5) << 8);
|
|
430
|
+
|
|
431
|
+
int device = unitNum - 1;
|
|
432
|
+
if (device < 0 || device >= MAX_DEVICES || !devices_[device].isLoaded()) {
|
|
433
|
+
setErrorResult(SP_NO_DEVICE);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
if (devices_[device].isWriteProtected()) {
|
|
437
|
+
setErrorResult(SP_WRITE_PROTECTED);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
uint8_t blockBuf[BlockDevice::BLOCK_SIZE];
|
|
442
|
+
for (size_t i = 0; i < BlockDevice::BLOCK_SIZE; i++) {
|
|
443
|
+
blockBuf[i] = memRead_(static_cast<uint16_t>(dataBuf + i));
|
|
444
|
+
}
|
|
445
|
+
if (!devices_[device].writeBlock(blockNum, blockBuf)) {
|
|
446
|
+
setErrorResult(SP_IO_ERROR);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
setErrorResult(SP_OK);
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
case 0x03: { // FORMAT
|
|
454
|
+
uint8_t unitNum = memRead_(paramPtr + 1);
|
|
455
|
+
int device = unitNum - 1;
|
|
456
|
+
if (device < 0 || device >= MAX_DEVICES || !devices_[device].isLoaded()) {
|
|
457
|
+
setErrorResult(SP_NO_DEVICE);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
setErrorResult(SP_OK);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
case 0x04: // CONTROL
|
|
465
|
+
case 0x05: // INIT
|
|
466
|
+
setErrorResult(SP_OK);
|
|
467
|
+
return;
|
|
468
|
+
|
|
469
|
+
default:
|
|
470
|
+
setErrorResult(SP_IO_ERROR);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Device management
|
|
476
|
+
|
|
477
|
+
bool SmartPortCard::insertImage(int device, const uint8_t* data, size_t size, const std::string& filename) {
|
|
478
|
+
if (device < 0 || device >= MAX_DEVICES) return false;
|
|
479
|
+
return devices_[device].load(data, size, filename);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
void SmartPortCard::ejectImage(int device) {
|
|
483
|
+
if (device >= 0 && device < MAX_DEVICES) {
|
|
484
|
+
devices_[device].eject();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
bool SmartPortCard::isImageInserted(int device) const {
|
|
489
|
+
if (device < 0 || device >= MAX_DEVICES) return false;
|
|
490
|
+
return devices_[device].isLoaded();
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const std::string& SmartPortCard::getImageFilename(int device) const {
|
|
494
|
+
if (device < 0 || device >= MAX_DEVICES) return emptyString_;
|
|
495
|
+
return devices_[device].getFilename();
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
bool SmartPortCard::isImageModified(int device) const {
|
|
499
|
+
if (device < 0 || device >= MAX_DEVICES) return false;
|
|
500
|
+
return devices_[device].isModified();
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const uint8_t* SmartPortCard::exportImageData(int device, size_t* size) const {
|
|
504
|
+
if (device < 0 || device >= MAX_DEVICES) {
|
|
505
|
+
if (size) *size = 0;
|
|
506
|
+
return nullptr;
|
|
507
|
+
}
|
|
508
|
+
return devices_[device].exportData(size);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const uint8_t* SmartPortCard::getBlockData(int device, size_t* size) const {
|
|
512
|
+
if (device < 0 || device >= MAX_DEVICES) {
|
|
513
|
+
if (size) *size = 0;
|
|
514
|
+
return nullptr;
|
|
515
|
+
}
|
|
516
|
+
return devices_[device].getBlockData(size);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
BlockDevice* SmartPortCard::getDevice(int device) {
|
|
520
|
+
if (device < 0 || device >= MAX_DEVICES) return nullptr;
|
|
521
|
+
return &devices_[device];
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const BlockDevice* SmartPortCard::getDevice(int device) const {
|
|
525
|
+
if (device < 0 || device >= MAX_DEVICES) return nullptr;
|
|
526
|
+
return &devices_[device];
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// State serialization
|
|
530
|
+
|
|
531
|
+
size_t SmartPortCard::getStateSize() const {
|
|
532
|
+
// slotNum(1) + per-device state
|
|
533
|
+
size_t total = 1;
|
|
534
|
+
for (int i = 0; i < MAX_DEVICES; i++) {
|
|
535
|
+
total += 4; // device state size prefix (LE32)
|
|
536
|
+
if (devices_[i].isLoaded()) {
|
|
537
|
+
total += devices_[i].getStateSize();
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return total;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
size_t SmartPortCard::serialize(uint8_t* buffer, size_t maxSize) const {
|
|
544
|
+
if (maxSize < 1) return 0;
|
|
545
|
+
|
|
546
|
+
size_t offset = 0;
|
|
547
|
+
buffer[offset++] = slotNum_;
|
|
548
|
+
|
|
549
|
+
for (int i = 0; i < MAX_DEVICES; i++) {
|
|
550
|
+
if (devices_[i].isLoaded()) {
|
|
551
|
+
size_t devStateSize = devices_[i].getStateSize();
|
|
552
|
+
if (offset + 4 + devStateSize > maxSize) return 0;
|
|
553
|
+
|
|
554
|
+
// Write device state size
|
|
555
|
+
uint32_t sz = static_cast<uint32_t>(devStateSize);
|
|
556
|
+
buffer[offset++] = sz & 0xFF;
|
|
557
|
+
buffer[offset++] = (sz >> 8) & 0xFF;
|
|
558
|
+
buffer[offset++] = (sz >> 16) & 0xFF;
|
|
559
|
+
buffer[offset++] = (sz >> 24) & 0xFF;
|
|
560
|
+
|
|
561
|
+
size_t written = devices_[i].serialize(buffer + offset, maxSize - offset);
|
|
562
|
+
if (written == 0) return 0;
|
|
563
|
+
offset += written;
|
|
564
|
+
} else {
|
|
565
|
+
if (offset + 4 > maxSize) return 0;
|
|
566
|
+
buffer[offset++] = 0;
|
|
567
|
+
buffer[offset++] = 0;
|
|
568
|
+
buffer[offset++] = 0;
|
|
569
|
+
buffer[offset++] = 0;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return offset;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
size_t SmartPortCard::deserialize(const uint8_t* buffer, size_t size) {
|
|
577
|
+
if (size < 1) return 0;
|
|
578
|
+
|
|
579
|
+
size_t offset = 0;
|
|
580
|
+
slotNum_ = buffer[offset++];
|
|
581
|
+
buildROM();
|
|
582
|
+
|
|
583
|
+
for (int i = 0; i < MAX_DEVICES; i++) {
|
|
584
|
+
if (offset + 4 > size) return 0;
|
|
585
|
+
|
|
586
|
+
uint32_t devStateSize = buffer[offset] | (buffer[offset + 1] << 8) |
|
|
587
|
+
(buffer[offset + 2] << 16) | (buffer[offset + 3] << 24);
|
|
588
|
+
offset += 4;
|
|
589
|
+
|
|
590
|
+
if (devStateSize > 0) {
|
|
591
|
+
if (offset + devStateSize > size) return 0;
|
|
592
|
+
size_t read = devices_[i].deserialize(buffer + offset, devStateSize);
|
|
593
|
+
if (read == 0) return 0;
|
|
594
|
+
offset += read;
|
|
595
|
+
} else {
|
|
596
|
+
devices_[i].eject();
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return offset;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
} // namespace a2e
|