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,190 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* mouse_card.hpp - Apple Mouse Interface card
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Mike Daley <michael_daley@icloud.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include "expansion_card.hpp"
|
|
11
|
+
#include <cstdint>
|
|
12
|
+
|
|
13
|
+
namespace a2e {
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* MouseCard - Apple Mouse Interface Card emulation
|
|
17
|
+
*
|
|
18
|
+
* Emulates the Apple Mouse Interface Card (342-0270-C ROM) using the
|
|
19
|
+
* AppleWin-style PIA command protocol. The real ROM firmware executes
|
|
20
|
+
* as native 6502 code on the CPU while our C++ code emulates the
|
|
21
|
+
* "MCU side" of the MC6821 PIA — receiving commands and providing
|
|
22
|
+
* response data through Port A/B transitions.
|
|
23
|
+
*
|
|
24
|
+
* Protocol overview:
|
|
25
|
+
* 6502 CPU <-> ROM firmware (runs natively) <-> MC6821 PIA <-> Our "MCU" code
|
|
26
|
+
*
|
|
27
|
+
* The firmware writes command bytes to PIA Port A and toggles Port B
|
|
28
|
+
* control signals. Our code detects Port B transitions, buffers the
|
|
29
|
+
* command/data, and places response data on Port A input pins for the
|
|
30
|
+
* firmware to read back.
|
|
31
|
+
*
|
|
32
|
+
* Mouse state is communicated through "screen holes" by the firmware
|
|
33
|
+
* itself — we only provide position/status data via the PIA when asked.
|
|
34
|
+
*
|
|
35
|
+
* The 6821 PIA provides 4 registers at I/O offsets 0-3 and the
|
|
36
|
+
* real ROM is served for card identification and firmware execution.
|
|
37
|
+
* Port B bits 1-3 select the 256-byte ROM page (2KB total).
|
|
38
|
+
*
|
|
39
|
+
* VBL (Vertical Blank) interrupt generation:
|
|
40
|
+
* When the mouse mode has bit 3 set (VBL interrupt enable), an IRQ
|
|
41
|
+
* is generated at the start of each vertical blanking period.
|
|
42
|
+
*
|
|
43
|
+
* Available in slots 2, 4, and 7.
|
|
44
|
+
*/
|
|
45
|
+
class MouseCard : public ExpansionCard {
|
|
46
|
+
public:
|
|
47
|
+
MouseCard();
|
|
48
|
+
~MouseCard() override = default;
|
|
49
|
+
|
|
50
|
+
// Delete copy
|
|
51
|
+
MouseCard(const MouseCard&) = delete;
|
|
52
|
+
MouseCard& operator=(const MouseCard&) = delete;
|
|
53
|
+
|
|
54
|
+
// Allow move
|
|
55
|
+
MouseCard(MouseCard&&) = default;
|
|
56
|
+
MouseCard& operator=(MouseCard&&) = default;
|
|
57
|
+
|
|
58
|
+
// ===== ExpansionCard Interface =====
|
|
59
|
+
|
|
60
|
+
// I/O space ($C0n0-$C0nF)
|
|
61
|
+
uint8_t readIO(uint8_t offset) override;
|
|
62
|
+
void writeIO(uint8_t offset, uint8_t value) override;
|
|
63
|
+
uint8_t peekIO(uint8_t offset) const override;
|
|
64
|
+
|
|
65
|
+
// ROM space ($Cn00-$CnFF)
|
|
66
|
+
uint8_t readROM(uint8_t offset) override;
|
|
67
|
+
bool hasROM() const override { return true; }
|
|
68
|
+
|
|
69
|
+
// No expansion ROM - the mouse card uses banked slot ROM instead
|
|
70
|
+
bool hasExpansionROM() const override { return false; }
|
|
71
|
+
uint8_t readExpansionROM(uint16_t offset) override;
|
|
72
|
+
|
|
73
|
+
void reset() override;
|
|
74
|
+
void update(int cycles) override;
|
|
75
|
+
|
|
76
|
+
const char* getName() const override { return "Mouse"; }
|
|
77
|
+
uint8_t getPreferredSlot() const override { return 4; }
|
|
78
|
+
|
|
79
|
+
// IRQ support
|
|
80
|
+
void setIRQCallback(IRQCallback callback) override { irqCallback_ = callback; }
|
|
81
|
+
void setCycleCallback(CycleCallback callback) override { cycleCallback_ = callback; }
|
|
82
|
+
bool isIRQActive() const override { return irqActive_; }
|
|
83
|
+
|
|
84
|
+
// State serialization
|
|
85
|
+
static constexpr size_t STATE_SIZE = 36;
|
|
86
|
+
size_t getStateSize() const override { return STATE_SIZE; }
|
|
87
|
+
size_t serialize(uint8_t* buffer, size_t maxSize) const override;
|
|
88
|
+
size_t deserialize(const uint8_t* buffer, size_t size) override;
|
|
89
|
+
|
|
90
|
+
// Mouse input from browser
|
|
91
|
+
void setSlotNumber(uint8_t slot) { slotNum_ = slot; }
|
|
92
|
+
void addDelta(int dx, int dy);
|
|
93
|
+
void setMouseButton(bool pressed);
|
|
94
|
+
|
|
95
|
+
// Debug accessors
|
|
96
|
+
uint8_t getSlotNumber() const { return slotNum_; }
|
|
97
|
+
int16_t getMouseX() const { return mouseX_; }
|
|
98
|
+
int16_t getMouseY() const { return mouseY_; }
|
|
99
|
+
bool getMouseButton() const { return mouseButton_; }
|
|
100
|
+
bool getMoved() const { return moved_; }
|
|
101
|
+
bool getButtonChanged() const { return buttonChanged_; }
|
|
102
|
+
int16_t getClampMinX() const { return clampMinX_; }
|
|
103
|
+
int16_t getClampMaxX() const { return clampMaxX_; }
|
|
104
|
+
int16_t getClampMinY() const { return clampMinY_; }
|
|
105
|
+
int16_t getClampMaxY() const { return clampMaxY_; }
|
|
106
|
+
uint8_t getDDRA() const { return ddra_; }
|
|
107
|
+
uint8_t getDDRB() const { return ddrb_; }
|
|
108
|
+
uint8_t getORA() const { return ora_; }
|
|
109
|
+
uint8_t getORB() const { return orb_; }
|
|
110
|
+
uint8_t getIRA() const { return ira_; }
|
|
111
|
+
uint8_t getIRB() const { return irb_; }
|
|
112
|
+
uint8_t getCRA() const { return cra_; }
|
|
113
|
+
uint8_t getCRB() const { return crb_; }
|
|
114
|
+
bool getVBLInterruptPending() const { return vblInterruptPending_; }
|
|
115
|
+
bool getMoveInterruptPending() const { return moveInterruptPending_; }
|
|
116
|
+
bool getButtonInterruptPending() const { return buttonInterruptPending_; }
|
|
117
|
+
uint8_t getLastCommand() const { return lastCommand_; }
|
|
118
|
+
uint8_t getResponseState() const { return byState_; }
|
|
119
|
+
bool getWasInVBL() const { return wasInVBL_; }
|
|
120
|
+
uint8_t getMode() const { return mode_; }
|
|
121
|
+
|
|
122
|
+
private:
|
|
123
|
+
// ROM data
|
|
124
|
+
const uint8_t* rom_;
|
|
125
|
+
size_t romSize_;
|
|
126
|
+
|
|
127
|
+
// Slot number (needed for screen hole addressing)
|
|
128
|
+
uint8_t slotNum_ = 4;
|
|
129
|
+
|
|
130
|
+
// 6821 PIA registers
|
|
131
|
+
uint8_t ddra_ = 0; // Data Direction Register A (1=output, 0=input)
|
|
132
|
+
uint8_t ddrb_ = 0; // Data Direction Register B
|
|
133
|
+
uint8_t ora_ = 0; // Output Register A
|
|
134
|
+
uint8_t orb_ = 0; // Output Register B
|
|
135
|
+
uint8_t ira_ = 0; // Input Register A (peripheral side)
|
|
136
|
+
uint8_t irb_ = 0; // Input Register B (peripheral side)
|
|
137
|
+
uint8_t cra_ = 0; // Control Register A
|
|
138
|
+
uint8_t crb_ = 0; // Control Register B
|
|
139
|
+
|
|
140
|
+
// Mouse state
|
|
141
|
+
int16_t mouseX_ = 0;
|
|
142
|
+
int16_t mouseY_ = 0;
|
|
143
|
+
bool mouseButton_ = false;
|
|
144
|
+
bool lastButton_ = false;
|
|
145
|
+
bool moved_ = false;
|
|
146
|
+
bool buttonChanged_ = false;
|
|
147
|
+
|
|
148
|
+
// Mouse mode (set by SetMouse command)
|
|
149
|
+
uint8_t mode_ = 0;
|
|
150
|
+
|
|
151
|
+
// Mouse clamping
|
|
152
|
+
int16_t clampMinX_ = 0;
|
|
153
|
+
int16_t clampMaxX_ = 1023;
|
|
154
|
+
int16_t clampMinY_ = 0;
|
|
155
|
+
int16_t clampMaxY_ = 1023;
|
|
156
|
+
|
|
157
|
+
// Interrupt state
|
|
158
|
+
bool irqActive_ = false;
|
|
159
|
+
bool vblInterruptPending_ = false;
|
|
160
|
+
bool moveInterruptPending_ = false;
|
|
161
|
+
bool buttonInterruptPending_ = false;
|
|
162
|
+
|
|
163
|
+
// VBL tracking
|
|
164
|
+
bool wasInVBL_ = false;
|
|
165
|
+
|
|
166
|
+
// IRQ/cycle callbacks
|
|
167
|
+
IRQCallback irqCallback_;
|
|
168
|
+
CycleCallback cycleCallback_;
|
|
169
|
+
|
|
170
|
+
// PIA command protocol state machine (AppleWin-style)
|
|
171
|
+
uint8_t byState_ = 0; // Protocol state (0=idle, 1=writing, 2=reading)
|
|
172
|
+
uint8_t byBuff_[8] = {}; // Command/response data buffer
|
|
173
|
+
uint8_t nBuffPos_ = 0; // Current buffer position
|
|
174
|
+
uint8_t nDataLen_ = 0; // Expected data length for current operation
|
|
175
|
+
uint8_t by6821B_ = 0; // Previous Port B value for transition detection
|
|
176
|
+
uint8_t lastCommand_ = 0; // Last command byte (for debug display)
|
|
177
|
+
|
|
178
|
+
// Position snapshot (set on ReadMouse, served to firmware)
|
|
179
|
+
int16_t snapX_ = 0;
|
|
180
|
+
int16_t snapY_ = 0;
|
|
181
|
+
|
|
182
|
+
void updateIRQState();
|
|
183
|
+
|
|
184
|
+
// PIA command protocol handlers
|
|
185
|
+
void on6821_B(uint8_t byData); // Port B transition handler
|
|
186
|
+
void onCommand(); // Command dispatch
|
|
187
|
+
void onWrite(); // Multi-byte write dispatch
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
} // namespace a2e
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* block_device.cpp - Block-oriented storage device for SmartPort
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Mike Daley <michael_daley@icloud.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include "block_device.hpp"
|
|
9
|
+
#include <cstring>
|
|
10
|
+
#include <algorithm>
|
|
11
|
+
|
|
12
|
+
namespace a2e {
|
|
13
|
+
|
|
14
|
+
bool BlockDevice::load(const uint8_t* data, size_t size, const std::string& filename) {
|
|
15
|
+
if (!data || size == 0) return false;
|
|
16
|
+
|
|
17
|
+
filename_ = filename;
|
|
18
|
+
modified_ = false;
|
|
19
|
+
writeProtected_ = false;
|
|
20
|
+
dataOffset_ = 0;
|
|
21
|
+
|
|
22
|
+
// Check for 2IMG format (64-byte header with "2IMG" magic)
|
|
23
|
+
if (size > 64 && data[0] == '2' && data[1] == 'I' && data[2] == 'M' && data[3] == 'G') {
|
|
24
|
+
// Parse 2IMG header
|
|
25
|
+
uint32_t headerSize = data[8] | (data[9] << 8) | (data[10] << 16) | (data[11] << 24);
|
|
26
|
+
uint32_t dataOffset = data[24] | (data[25] << 8) | (data[26] << 16) | (data[27] << 24);
|
|
27
|
+
uint32_t dataLength = data[28] | (data[29] << 8) | (data[30] << 16) | (data[31] << 24);
|
|
28
|
+
uint32_t flags = data[16] | (data[17] << 8) | (data[18] << 16) | (data[19] << 24);
|
|
29
|
+
|
|
30
|
+
// Validate
|
|
31
|
+
if (headerSize < 64 || dataOffset >= size) return false;
|
|
32
|
+
if (dataOffset + dataLength > size) return false;
|
|
33
|
+
|
|
34
|
+
// Image format in header byte 12: 0=DOS order, 1=ProDOS order, 2=nibble
|
|
35
|
+
uint32_t imageFormat = data[12] | (data[13] << 8) | (data[14] << 16) | (data[15] << 24);
|
|
36
|
+
if (imageFormat == 2) return false; // nibble format not supported as block device
|
|
37
|
+
|
|
38
|
+
writeProtected_ = (flags & 0x80000000) != 0;
|
|
39
|
+
|
|
40
|
+
// Block count from header
|
|
41
|
+
uint32_t blockCount = data[20] | (data[21] << 8) | (data[22] << 16) | (data[23] << 24);
|
|
42
|
+
if (blockCount == 0 && dataLength > 0) {
|
|
43
|
+
blockCount = dataLength / BLOCK_SIZE;
|
|
44
|
+
}
|
|
45
|
+
if (blockCount > MAX_BLOCKS) blockCount = MAX_BLOCKS;
|
|
46
|
+
|
|
47
|
+
dataOffset_ = dataOffset;
|
|
48
|
+
totalBlocks_ = static_cast<uint16_t>(blockCount);
|
|
49
|
+
|
|
50
|
+
// Store the entire file (header + data) so we can export it back
|
|
51
|
+
data_.assign(data, data + size);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Raw ProDOS order (.hdv, .po) - just raw blocks
|
|
56
|
+
if (size < BLOCK_SIZE) return false;
|
|
57
|
+
|
|
58
|
+
uint32_t blockCount = size / BLOCK_SIZE;
|
|
59
|
+
if (blockCount > MAX_BLOCKS) blockCount = MAX_BLOCKS;
|
|
60
|
+
|
|
61
|
+
totalBlocks_ = static_cast<uint16_t>(blockCount);
|
|
62
|
+
dataOffset_ = 0;
|
|
63
|
+
data_.assign(data, data + size);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
bool BlockDevice::readBlock(uint16_t blockNum, uint8_t* buffer) const {
|
|
68
|
+
if (!buffer || blockNum >= totalBlocks_) return false;
|
|
69
|
+
|
|
70
|
+
size_t offset = dataOffset_ + static_cast<size_t>(blockNum) * BLOCK_SIZE;
|
|
71
|
+
if (offset + BLOCK_SIZE > data_.size()) return false;
|
|
72
|
+
|
|
73
|
+
std::memcpy(buffer, data_.data() + offset, BLOCK_SIZE);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
bool BlockDevice::writeBlock(uint16_t blockNum, const uint8_t* buffer) {
|
|
78
|
+
if (!buffer || blockNum >= totalBlocks_ || writeProtected_) return false;
|
|
79
|
+
|
|
80
|
+
size_t offset = dataOffset_ + static_cast<size_t>(blockNum) * BLOCK_SIZE;
|
|
81
|
+
if (offset + BLOCK_SIZE > data_.size()) return false;
|
|
82
|
+
|
|
83
|
+
std::memcpy(data_.data() + offset, buffer, BLOCK_SIZE);
|
|
84
|
+
modified_ = true;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const uint8_t* BlockDevice::exportData(size_t* size) const {
|
|
89
|
+
if (data_.empty()) {
|
|
90
|
+
if (size) *size = 0;
|
|
91
|
+
return nullptr;
|
|
92
|
+
}
|
|
93
|
+
if (size) *size = data_.size();
|
|
94
|
+
return data_.data();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const uint8_t* BlockDevice::getBlockData(size_t* size) const {
|
|
98
|
+
if (data_.empty()) {
|
|
99
|
+
if (size) *size = 0;
|
|
100
|
+
return nullptr;
|
|
101
|
+
}
|
|
102
|
+
if (size) *size = data_.size() - dataOffset_;
|
|
103
|
+
return data_.data() + dataOffset_;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
size_t BlockDevice::getStateSize() const {
|
|
107
|
+
// flags(1) + totalBlocks(2) + dataOffset(4) + filenameLen(2) + filename + dataSize(4) + data
|
|
108
|
+
return 1 + 2 + 4 + 2 + filename_.size() + 4 + data_.size();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
size_t BlockDevice::serialize(uint8_t* buffer, size_t maxSize) const {
|
|
112
|
+
size_t needed = getStateSize();
|
|
113
|
+
if (maxSize < needed) return 0;
|
|
114
|
+
|
|
115
|
+
size_t offset = 0;
|
|
116
|
+
|
|
117
|
+
// Flags: bit 0 = loaded, bit 1 = writeProtected, bit 2 = modified
|
|
118
|
+
uint8_t flags = 0;
|
|
119
|
+
if (!data_.empty()) flags |= 0x01;
|
|
120
|
+
if (writeProtected_) flags |= 0x02;
|
|
121
|
+
if (modified_) flags |= 0x04;
|
|
122
|
+
buffer[offset++] = flags;
|
|
123
|
+
|
|
124
|
+
// Total blocks (LE16)
|
|
125
|
+
buffer[offset++] = totalBlocks_ & 0xFF;
|
|
126
|
+
buffer[offset++] = (totalBlocks_ >> 8) & 0xFF;
|
|
127
|
+
|
|
128
|
+
// Data offset (LE32)
|
|
129
|
+
uint32_t doff = static_cast<uint32_t>(dataOffset_);
|
|
130
|
+
buffer[offset++] = doff & 0xFF;
|
|
131
|
+
buffer[offset++] = (doff >> 8) & 0xFF;
|
|
132
|
+
buffer[offset++] = (doff >> 16) & 0xFF;
|
|
133
|
+
buffer[offset++] = (doff >> 24) & 0xFF;
|
|
134
|
+
|
|
135
|
+
// Filename
|
|
136
|
+
uint16_t fnLen = static_cast<uint16_t>(filename_.size());
|
|
137
|
+
buffer[offset++] = fnLen & 0xFF;
|
|
138
|
+
buffer[offset++] = (fnLen >> 8) & 0xFF;
|
|
139
|
+
if (fnLen > 0) {
|
|
140
|
+
std::memcpy(buffer + offset, filename_.data(), fnLen);
|
|
141
|
+
offset += fnLen;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Data
|
|
145
|
+
uint32_t dataSize = static_cast<uint32_t>(data_.size());
|
|
146
|
+
buffer[offset++] = dataSize & 0xFF;
|
|
147
|
+
buffer[offset++] = (dataSize >> 8) & 0xFF;
|
|
148
|
+
buffer[offset++] = (dataSize >> 16) & 0xFF;
|
|
149
|
+
buffer[offset++] = (dataSize >> 24) & 0xFF;
|
|
150
|
+
if (dataSize > 0) {
|
|
151
|
+
std::memcpy(buffer + offset, data_.data(), dataSize);
|
|
152
|
+
offset += dataSize;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return offset;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
size_t BlockDevice::deserialize(const uint8_t* buffer, size_t size) {
|
|
159
|
+
if (size < 13) return 0; // minimum: flags + blocks + offset + fnLen + dataSize
|
|
160
|
+
|
|
161
|
+
size_t offset = 0;
|
|
162
|
+
|
|
163
|
+
uint8_t flags = buffer[offset++];
|
|
164
|
+
bool loaded = (flags & 0x01) != 0;
|
|
165
|
+
writeProtected_ = (flags & 0x02) != 0;
|
|
166
|
+
modified_ = (flags & 0x04) != 0;
|
|
167
|
+
|
|
168
|
+
totalBlocks_ = buffer[offset] | (buffer[offset + 1] << 8);
|
|
169
|
+
offset += 2;
|
|
170
|
+
|
|
171
|
+
dataOffset_ = buffer[offset] | (buffer[offset + 1] << 8) |
|
|
172
|
+
(buffer[offset + 2] << 16) | (buffer[offset + 3] << 24);
|
|
173
|
+
offset += 4;
|
|
174
|
+
|
|
175
|
+
uint16_t fnLen = buffer[offset] | (buffer[offset + 1] << 8);
|
|
176
|
+
offset += 2;
|
|
177
|
+
if (offset + fnLen > size) return 0;
|
|
178
|
+
if (fnLen > 0) {
|
|
179
|
+
filename_.assign(reinterpret_cast<const char*>(buffer + offset), fnLen);
|
|
180
|
+
offset += fnLen;
|
|
181
|
+
} else {
|
|
182
|
+
filename_.clear();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (offset + 4 > size) return 0;
|
|
186
|
+
uint32_t dataSize = buffer[offset] | (buffer[offset + 1] << 8) |
|
|
187
|
+
(buffer[offset + 2] << 16) | (buffer[offset + 3] << 24);
|
|
188
|
+
offset += 4;
|
|
189
|
+
|
|
190
|
+
if (loaded && dataSize > 0) {
|
|
191
|
+
if (offset + dataSize > size) return 0;
|
|
192
|
+
data_.assign(buffer + offset, buffer + offset + dataSize);
|
|
193
|
+
offset += dataSize;
|
|
194
|
+
} else {
|
|
195
|
+
data_.clear();
|
|
196
|
+
totalBlocks_ = 0;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return offset;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
} // namespace a2e
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* block_device.hpp - Block-oriented storage device for SmartPort
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Mike Daley <michael_daley@icloud.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <cstdint>
|
|
11
|
+
#include <cstddef>
|
|
12
|
+
#include <string>
|
|
13
|
+
#include <vector>
|
|
14
|
+
|
|
15
|
+
namespace a2e {
|
|
16
|
+
|
|
17
|
+
class BlockDevice {
|
|
18
|
+
public:
|
|
19
|
+
static constexpr size_t BLOCK_SIZE = 512;
|
|
20
|
+
static constexpr size_t MAX_BLOCKS = 65535;
|
|
21
|
+
|
|
22
|
+
BlockDevice() = default;
|
|
23
|
+
~BlockDevice() = default;
|
|
24
|
+
|
|
25
|
+
bool load(const uint8_t* data, size_t size, const std::string& filename);
|
|
26
|
+
bool readBlock(uint16_t blockNum, uint8_t* buffer) const;
|
|
27
|
+
bool writeBlock(uint16_t blockNum, const uint8_t* buffer);
|
|
28
|
+
|
|
29
|
+
uint16_t getTotalBlocks() const { return totalBlocks_; }
|
|
30
|
+
bool isWriteProtected() const { return writeProtected_; }
|
|
31
|
+
bool isModified() const { return modified_; }
|
|
32
|
+
bool isLoaded() const { return !data_.empty(); }
|
|
33
|
+
const std::string& getFilename() const { return filename_; }
|
|
34
|
+
|
|
35
|
+
const uint8_t* exportData(size_t* size) const;
|
|
36
|
+
const uint8_t* getBlockData(size_t* size) const;
|
|
37
|
+
|
|
38
|
+
size_t getStateSize() const;
|
|
39
|
+
size_t serialize(uint8_t* buffer, size_t maxSize) const;
|
|
40
|
+
size_t deserialize(const uint8_t* buffer, size_t size);
|
|
41
|
+
|
|
42
|
+
void eject() {
|
|
43
|
+
data_.clear();
|
|
44
|
+
totalBlocks_ = 0;
|
|
45
|
+
modified_ = false;
|
|
46
|
+
writeProtected_ = false;
|
|
47
|
+
filename_.clear();
|
|
48
|
+
dataOffset_ = 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private:
|
|
52
|
+
std::vector<uint8_t> data_;
|
|
53
|
+
uint16_t totalBlocks_ = 0;
|
|
54
|
+
bool writeProtected_ = false;
|
|
55
|
+
bool modified_ = false;
|
|
56
|
+
std::string filename_;
|
|
57
|
+
size_t dataOffset_ = 0; // offset to block data (non-zero for 2IMG)
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
} // namespace a2e
|