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
package/wiki/AI-Agent.md
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# AI Agent
|
|
2
|
+
|
|
3
|
+
The AI Agent integration allows LLMs like Claude to control the emulator through natural language commands. The agent can show/hide windows, manage disks, read/write BASIC programs, and inspect emulator state in real time using the AG-UI protocol over an MCP server.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Connection Status](#connection-status)
|
|
10
|
+
- [Setting Up the MCP Server](#setting-up-the-mcp-server)
|
|
11
|
+
- [Example Prompts](#example-prompts)
|
|
12
|
+
- [Window Management](#window-management)
|
|
13
|
+
- [Disk Management](#disk-management)
|
|
14
|
+
- [SmartPort Hard Drives](#smartport-hard-drives)
|
|
15
|
+
- [Slot Configuration](#slot-configuration)
|
|
16
|
+
- [BASIC Programs](#basic-programs)
|
|
17
|
+
- [Assembly Programs](#assembly-programs)
|
|
18
|
+
- [Memory Operations](#memory-operations)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Connection Status
|
|
23
|
+
|
|
24
|
+
The agent connection status is shown by a sparkle icon in the toolbar header:
|
|
25
|
+
|
|
26
|
+
| Icon | Status | Description |
|
|
27
|
+
|------|--------|-------------|
|
|
28
|
+
| <svg viewBox="0 0 24 24" width="20" height="20" fill="#6e7681"><path d="M12 2l1.5 4.5L18 8l-4.5 1.5L12 14l-1.5-4.5L6 8l4.5-1.5L12 2zM6 16l.75 2.25L9 19l-2.25.75L6 22l-.75-2.25L3 19l2.25-.75L6 16zM18 16l.75 2.25L21 19l-2.25.75L18 22l-.75-2.25L15 19l2.25-.75L18 16z"/></svg> | **Disconnected** | MCP server is not running or not reachable |
|
|
29
|
+
| <svg viewBox="0 0 24 24" width="20" height="20" fill="#FDBE34"><path d="M12 2l1.5 4.5L18 8l-4.5 1.5L12 14l-1.5-4.5L6 8l4.5-1.5L12 2zM6 16l.75 2.25L9 19l-2.25.75L6 22l-.75-2.25L3 19l2.25-.75L6 16zM18 16l.75 2.25L21 19l-2.25.75L18 22l-.75-2.25L15 19l2.25-.75L18 16z"/></svg> | **Connected** | Agent is connected and ready to receive commands |
|
|
30
|
+
| <svg viewBox="0 0 24 24" width="20" height="20" fill="#E5504F"><path d="M12 2l1.5 4.5L18 8l-4.5 1.5L12 14l-1.5-4.5L6 8l4.5-1.5L12 2zM6 16l.75 2.25L9 19l-2.25.75L6 22l-.75-2.25L3 19l2.25-.75L6 16zM18 16l.75 2.25L21 19l-2.25.75L18 22l-.75-2.25L15 19l2.25-.75L18 16z"/></svg> | **Interrupted** | Connection error or server unavailable |
|
|
31
|
+
|
|
32
|
+
Click the sparkle icon to open the agent connection panel and view detailed status information.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Setting Up the MCP Server
|
|
37
|
+
|
|
38
|
+
The AI Agent uses the Model Context Protocol (MCP) to communicate with LLM clients like Claude Code. Configure your MCP client to connect to the emulator's agent server.
|
|
39
|
+
|
|
40
|
+
### Configuration
|
|
41
|
+
|
|
42
|
+
Add the following to your MCP configuration file (e.g., `~/.claude.json` or `.mcp.json` in your project):
|
|
43
|
+
|
|
44
|
+
**Using bunx (recommended):**
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"appleii-agent": {
|
|
50
|
+
"type": "stdio",
|
|
51
|
+
"command": "bunx",
|
|
52
|
+
"args": [
|
|
53
|
+
"-y",
|
|
54
|
+
"@retrotech71/appleii-agent"
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Using npx:**
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"mcpServers": {
|
|
66
|
+
"appleii-agent": {
|
|
67
|
+
"type": "stdio",
|
|
68
|
+
"command": "npx",
|
|
69
|
+
"args": [
|
|
70
|
+
"-y",
|
|
71
|
+
"@retrotech71/appleii-agent"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
No installation required — the agent is downloaded and run automatically on first use. `bunx` ([Bun](https://bun.sh)) is recommended as it is more likely to work out of the box; `npx` (Node.js) may experience timeout issues on first run while downloading the package.
|
|
79
|
+
|
|
80
|
+
### How It Works
|
|
81
|
+
|
|
82
|
+
The MCP server starts automatically when your MCP client (e.g., Claude Code) connects. It listens on `http://localhost:3033` and uses the AG-UI protocol to communicate with the emulator frontend running in your browser.
|
|
83
|
+
|
|
84
|
+
### Port Conflict Management
|
|
85
|
+
|
|
86
|
+
The MCP server includes graceful port conflict handling when multiple instances attempt to use port 3033:
|
|
87
|
+
|
|
88
|
+
- **Automatic Detection:** When port 3033 is already in use, the MCP server stays alive without failing
|
|
89
|
+
- **Status Reporting:** The `server_control` tool reports port conflicts and provides guidance
|
|
90
|
+
- **Port Reclamation:** Any instance can take over the port by shutting down the other instance and starting itself
|
|
91
|
+
- **Two-Step Process:**
|
|
92
|
+
1. Call `shutdown_remote_server` to stop the other instance
|
|
93
|
+
2. Call `server_control` with action `start` to start this instance
|
|
94
|
+
|
|
95
|
+
**Example workflow to reclaim port 3033:**
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
Check the status → port shows as in use
|
|
99
|
+
Shutdown the remote server on port 3033
|
|
100
|
+
Start this server on port 3033
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
This allows multiple Claude Code sessions or MCP instances to coordinate gracefully without manual process management.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Example Prompts
|
|
108
|
+
|
|
109
|
+
### Window Management
|
|
110
|
+
|
|
111
|
+
**Show a window:**
|
|
112
|
+
```
|
|
113
|
+
Show the CPU debugger window
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Hide a window:**
|
|
117
|
+
```
|
|
118
|
+
Hide the disk drives window
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Focus a window:**
|
|
122
|
+
```
|
|
123
|
+
Bring the BASIC program window to the front
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Disk Management
|
|
127
|
+
|
|
128
|
+
**Insert a disk from the filesystem:**
|
|
129
|
+
```
|
|
130
|
+
Load ~/Documents/Apple_II/ProDOS_2_4_2.dsk into drive 1
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**List recent disks:**
|
|
134
|
+
```
|
|
135
|
+
What disks are in the recent list for drive 1?
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Load from recent disks:**
|
|
139
|
+
```
|
|
140
|
+
Insert the disk named "Zork_1.dsk" from recent disks into drive 2
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Eject a disk:**
|
|
144
|
+
```
|
|
145
|
+
Eject the disk from drive 1
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Clear recent disks:**
|
|
149
|
+
```
|
|
150
|
+
Clear all recent disks for drive 1
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### SmartPort Hard Drives
|
|
154
|
+
|
|
155
|
+
**Insert an image from the filesystem:**
|
|
156
|
+
```
|
|
157
|
+
Load ~/Images/Total_Replay.hdv into SmartPort device 1
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**List recent images:**
|
|
161
|
+
```
|
|
162
|
+
What images are in the recent list for SmartPort device 1?
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Load from recent images:**
|
|
166
|
+
```
|
|
167
|
+
Insert Apple Pascal from recent SmartPort images
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Clear recent images:**
|
|
171
|
+
```
|
|
172
|
+
Clear the recent images list for SmartPort device 1
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Eject an image:**
|
|
176
|
+
```
|
|
177
|
+
Eject the SmartPort image from device 1
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Slot Configuration
|
|
181
|
+
|
|
182
|
+
**List all slots:**
|
|
183
|
+
```
|
|
184
|
+
Show me the current expansion slot configuration
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Install a card:**
|
|
188
|
+
```
|
|
189
|
+
Install the Mockingboard in slot 4
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Remove a card:**
|
|
193
|
+
```
|
|
194
|
+
Remove the card from slot 5
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Move a card:**
|
|
198
|
+
```
|
|
199
|
+
Move the SmartPort card from slot 7 to slot 5
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### BASIC Programs
|
|
203
|
+
|
|
204
|
+
**Read BASIC program from memory:**
|
|
205
|
+
```
|
|
206
|
+
Load the BASIC program from memory and show it in the editor
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Write BASIC program to memory:**
|
|
210
|
+
```
|
|
211
|
+
Write this BASIC program to emulator memory:
|
|
212
|
+
10 PRINT "HELLO, WORLD!"
|
|
213
|
+
20 GOTO 10
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Get program listing:**
|
|
217
|
+
```
|
|
218
|
+
What BASIC program is currently in memory?
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Save program to file:**
|
|
222
|
+
```
|
|
223
|
+
Save the BASIC program from the editor to ~/Documents/myprogram.bas
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Save program from memory to file:**
|
|
227
|
+
```
|
|
228
|
+
Save the BASIC program from memory to ~/Documents/myprogram.bas
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Set a breakpoint:**
|
|
232
|
+
```
|
|
233
|
+
Set a breakpoint on BASIC line 20
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Set a statement-level breakpoint:**
|
|
237
|
+
```
|
|
238
|
+
Set a breakpoint on line 10, second statement
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**List all breakpoints:**
|
|
242
|
+
```
|
|
243
|
+
Show me all BASIC breakpoints
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Remove a breakpoint:**
|
|
247
|
+
```
|
|
248
|
+
Remove the breakpoint from line 20
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Step to next line:**
|
|
252
|
+
```
|
|
253
|
+
Step to the next BASIC line
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Get current line number:**
|
|
257
|
+
```
|
|
258
|
+
What line is the BASIC program stopped at?
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Inspect variables:**
|
|
262
|
+
```
|
|
263
|
+
Show me all BASIC variables
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**Get specific variable value:**
|
|
267
|
+
```
|
|
268
|
+
What's the value of variable X?
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**Set a variable:**
|
|
272
|
+
```
|
|
273
|
+
Set variable X to 42
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Set a string variable:**
|
|
277
|
+
```
|
|
278
|
+
Set A$ to "HELLO WORLD"
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Debug workflow example:**
|
|
282
|
+
```
|
|
283
|
+
Write this program to memory:
|
|
284
|
+
10 X=1:Y=2:PRINT X+Y
|
|
285
|
+
20 PRINT "LINE 20"
|
|
286
|
+
30 END
|
|
287
|
+
|
|
288
|
+
Set a breakpoint on line 10, second statement
|
|
289
|
+
Run the program
|
|
290
|
+
Step to the next line
|
|
291
|
+
Show me all variables
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Assembly Programs
|
|
295
|
+
|
|
296
|
+
**Get assembly status:**
|
|
297
|
+
```
|
|
298
|
+
What's the status of the assembler?
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Execute assembled program:**
|
|
302
|
+
```
|
|
303
|
+
Run the assembled program
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Execute at specific address:**
|
|
307
|
+
```
|
|
308
|
+
Execute the code at $0800
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Execute with return to BASIC:**
|
|
312
|
+
```
|
|
313
|
+
Execute $0800 and return to BASIC
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Execute with return to monitor:**
|
|
317
|
+
```
|
|
318
|
+
Run $0800 and return to monitor
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Set PC without executing:**
|
|
322
|
+
```
|
|
323
|
+
Set PC to $0800 but don't execute yet
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Memory Operations
|
|
327
|
+
|
|
328
|
+
**Load binary file to memory:**
|
|
329
|
+
```
|
|
330
|
+
Load the file ~/program.bin into memory at address $2000
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Save memory range to file:**
|
|
334
|
+
```
|
|
335
|
+
Save 256 bytes from memory address $0800 to ~/output.bin
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Save memory region:**
|
|
339
|
+
```
|
|
340
|
+
Read 1024 bytes starting at $4000 and save them to ~/dump.bin
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Emulator Control
|
|
344
|
+
|
|
345
|
+
**Power on:**
|
|
346
|
+
```
|
|
347
|
+
Turn on the emulator
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
**Power off:**
|
|
351
|
+
```
|
|
352
|
+
Turn off the emulator
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Reboot (cold reset):**
|
|
356
|
+
```
|
|
357
|
+
Reboot the emulator
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
**Warm reset:**
|
|
361
|
+
```
|
|
362
|
+
Send Ctrl+Reset to the emulator
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
**Break program:**
|
|
366
|
+
```
|
|
367
|
+
Send Ctrl+C to the emulator
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
See also: [[Debugger]], [[Disk-Drives]], [[Getting-Started]]
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# Architecture Overview
|
|
2
|
+
|
|
3
|
+
This page describes the internal architecture of the Apple //e emulator, covering the two-layer design, audio-driven timing model, WebAssembly interface, and build system.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Two-Layer Design](#two-layer-design)
|
|
10
|
+
- [Component Wiring](#component-wiring)
|
|
11
|
+
- [Audio-Driven Timing](#audio-driven-timing)
|
|
12
|
+
- [Frame Synchronization](#frame-synchronization)
|
|
13
|
+
- [WASM Interface](#wasm-interface)
|
|
14
|
+
- [Rendering Pipeline](#rendering-pipeline)
|
|
15
|
+
- [Build System](#build-system)
|
|
16
|
+
- [Key Constants](#key-constants)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Two-Layer Design
|
|
21
|
+
|
|
22
|
+
The emulator is split into two distinct layers:
|
|
23
|
+
|
|
24
|
+
**C++ Core** (`src/core/`) -- Pure emulation logic compiled to WebAssembly. This layer has no browser dependencies and contains:
|
|
25
|
+
|
|
26
|
+
| Component | File | Responsibility |
|
|
27
|
+
|-----------|------|----------------|
|
|
28
|
+
| CPU | `cpu/cpu6502.cpp` | Cycle-accurate 65C02 processor |
|
|
29
|
+
| MMU | `mmu/mmu.cpp` | 128KB memory, soft switches, expansion slots |
|
|
30
|
+
| Video | `video/video.cpp` | Per-scanline rendering of all 6 video modes |
|
|
31
|
+
| Audio | `audio/audio.cpp` | Speaker toggle tracking and sample generation |
|
|
32
|
+
| Disk | `disk-image/` | DSK/DO/PO/NIB/WOZ format support with GCR encoding |
|
|
33
|
+
| Input | `input/keyboard.cpp` | Browser keycode to Apple II keycode translation |
|
|
34
|
+
| Cards | `cards/` | Pluggable expansion card system (Disk II, Mockingboard, Thunderclock, Mouse) |
|
|
35
|
+
| Emulator | `emulator.cpp` | Core coordinator, state serialization |
|
|
36
|
+
|
|
37
|
+
**JavaScript Layer** (`src/js/`) -- Browser integration using vanilla ES6 modules (no frameworks):
|
|
38
|
+
|
|
39
|
+
| Component | Directory | Responsibility |
|
|
40
|
+
|-----------|-----------|----------------|
|
|
41
|
+
| Main | `main.js` | `AppleIIeEmulator` class, initialization, render loop |
|
|
42
|
+
| Audio | `audio/` | Web Audio API driver, AudioWorklet processor |
|
|
43
|
+
| Display | `display/` | WebGL renderer, CRT shader effects |
|
|
44
|
+
| Disk Manager | `disk-manager/` | Disk drive UI, persistence, surface rendering |
|
|
45
|
+
| File Explorer | `file-explorer/` | DOS 3.3 and ProDOS disk browser |
|
|
46
|
+
| Debug | `debug/` | CPU debugger, memory browser, soft switch monitor, etc. |
|
|
47
|
+
| State | `state/` | Save state manager (autosave + 5 manual slots) |
|
|
48
|
+
| Input | `input/` | Keyboard handling, text selection, joystick, mouse |
|
|
49
|
+
| Windows | `windows/` | Base window class and window manager |
|
|
50
|
+
|
|
51
|
+
The C++ core is compiled to WebAssembly using Emscripten and exposed to JavaScript through a flat C function interface defined in `src/bindings/wasm_interface.cpp`.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Component Wiring
|
|
56
|
+
|
|
57
|
+
The `Emulator` class (in `emulator.cpp`) acts as the central coordinator. During construction, it creates all subsystems and wires them together using callbacks:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
Emulator
|
|
61
|
+
|
|
|
62
|
+
+-- CPU6502 (read/write callbacks -> MMU)
|
|
63
|
+
|
|
|
64
|
+
+-- MMU (keyboard, speaker, button, cycle callbacks -> Emulator)
|
|
65
|
+
| |
|
|
66
|
+
| +-- ExpansionCard slots[1..7]
|
|
67
|
+
| +-- Slot 3: 80-column (built-in)
|
|
68
|
+
| +-- Slot 4: MockingboardCard
|
|
69
|
+
| +-- Slot 5: ThunderclockCard
|
|
70
|
+
| +-- Slot 6: Disk2Card
|
|
71
|
+
|
|
|
72
|
+
+-- Video (cycle callback -> CPU, switch callback -> MMU)
|
|
73
|
+
|
|
|
74
|
+
+-- Audio (Mockingboard pointer for stereo mixing)
|
|
75
|
+
|
|
|
76
|
+
+-- Keyboard (key callback -> Emulator)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The CPU does not access memory directly. Instead, it calls lambda functions provided at construction time that route through the MMU:
|
|
80
|
+
|
|
81
|
+
```cpp
|
|
82
|
+
cpu_ = std::make_unique<CPU6502>(
|
|
83
|
+
[this](uint16_t addr) { return cpuRead(addr); },
|
|
84
|
+
[this](uint16_t addr, uint8_t val) { cpuWrite(addr, val); },
|
|
85
|
+
CPUVariant::CMOS_65C02);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The MMU in turn delegates I/O space reads/writes to expansion cards based on the address range, and invokes speaker, keyboard, and button callbacks to communicate back to the Emulator.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Audio-Driven Timing
|
|
93
|
+
|
|
94
|
+
The emulator uses the Web Audio API as its primary timing source rather than `requestAnimationFrame` or `setInterval`. This approach provides several advantages:
|
|
95
|
+
|
|
96
|
+
1. **Precise timing** -- The audio callback runs at exactly 48,000 Hz, providing consistent sample-level timing.
|
|
97
|
+
2. **Background tab support** -- Web Audio continues to fire in background tabs, keeping emulation running when the tab is not visible.
|
|
98
|
+
3. **Synchronized audio** -- Speaker clicks and Mockingboard output are generated in lockstep with CPU execution, preventing drift.
|
|
99
|
+
|
|
100
|
+
### How It Works
|
|
101
|
+
|
|
102
|
+
The timing chain flows as follows:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
AudioWorklet (48kHz)
|
|
106
|
+
--> requests 1600 sample frames from main thread
|
|
107
|
+
--> AudioDriver.generateSamples(count)
|
|
108
|
+
--> WASM _generateStereoAudioSamples(buffer, count)
|
|
109
|
+
--> Emulator::runCycles(count * CYCLES_PER_SAMPLE * speedMultiplier)
|
|
110
|
+
--> CPU executes instructions
|
|
111
|
+
--> Video renders scanlines progressively
|
|
112
|
+
--> Disk controller updates per instruction
|
|
113
|
+
--> Mockingboard timers tick
|
|
114
|
+
--> Audio::generateStereoSamples() produces speaker + Mockingboard output
|
|
115
|
+
--> JS copies samples from WASM heap
|
|
116
|
+
--> Samples sent back to AudioWorklet via postMessage
|
|
117
|
+
--> AudioWorklet deinterleaves into L/R channels for output
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The AudioWorklet processor (`audio-worklet.js`) runs on a separate thread. It processes 128 samples at a time in its `process()` method, consuming from an internal buffer. When the buffer drops below 1,600 frames, it sends a `requestSamples` message to the main thread. The main thread responds by running the emulator for the required number of cycles and returning the generated samples.
|
|
121
|
+
|
|
122
|
+
Each audio sample requires approximately 21.3 CPU cycles (`1,023,000 / 48,000`). The WASM function `generateStereoAudioSamples` runs the emulator for `sampleCount * CYCLES_PER_SAMPLE * speedMultiplier` cycles, then generates interleaved stereo samples (speaker centered on both channels, Mockingboard PSG1 on left, PSG2 on right).
|
|
123
|
+
|
|
124
|
+
### Fallback Timing
|
|
125
|
+
|
|
126
|
+
When Web Audio is unavailable (suspended by autoplay policy, no audio hardware), the `AudioDriver` falls back to a `setInterval` at 60 Hz, running approximately 17,050 cycles per tick. Audio resumes automatically on the first user interaction.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Frame Synchronization
|
|
131
|
+
|
|
132
|
+
Frame boundaries are detected inside the emulator's main execution loop. After each instruction, the emulator checks whether `CYCLES_PER_FRAME` (17,030) cycles have elapsed since the last frame:
|
|
133
|
+
|
|
134
|
+
```cpp
|
|
135
|
+
if (currentCycle - lastFrameCycle_ >= CYCLES_PER_FRAME) {
|
|
136
|
+
lastFrameCycle_ += CYCLES_PER_FRAME; // Aligned increment, no drift
|
|
137
|
+
video_->renderFrame();
|
|
138
|
+
video_->beginNewFrame(lastFrameCycle_);
|
|
139
|
+
frameReady_ = true;
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The frame boundary is advanced by exactly `CYCLES_PER_FRAME` rather than set to the current cycle count. This prevents drift and keeps the VBL detection at `$C019` synchronized with raster effects.
|
|
144
|
+
|
|
145
|
+
On the JavaScript side, `consumeFrameSamples()` tracks how many audio samples have been generated. At 48,000 Hz / 60 Hz = 800 samples per frame, this provides frame-level synchronization. When one or more frames' worth of samples are generated, `AudioDriver` triggers `onFrameReady`, which calls `renderFrame()` to upload the framebuffer to the WebGL texture.
|
|
146
|
+
|
|
147
|
+
The `requestAnimationFrame` render loop handles display updates for the non-audio path: debug window updates, beam crosshair overlays, drive LED animations, and forced re-renders when the CPU is paused.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## WASM Interface
|
|
152
|
+
|
|
153
|
+
The C++ core is exposed to JavaScript through a single global `Emulator` instance accessed via flat C functions in `wasm_interface.cpp`. A single static pointer `g_emulator` holds the instance:
|
|
154
|
+
|
|
155
|
+
```cpp
|
|
156
|
+
static a2e::Emulator *g_emulator = nullptr;
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Memory Management
|
|
160
|
+
|
|
161
|
+
JavaScript uses Emscripten's `_malloc` / `_free` for WASM heap allocation and `HEAPU8` / `HEAPF32` for direct memory access. String conversion uses `stringToUTF8()` and `UTF8ToString()`. Example pattern for audio:
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
const bufferPtr = wasmModule._malloc(count * 2 * 4); // stereo floats
|
|
165
|
+
wasmModule._generateStereoAudioSamples(bufferPtr, count);
|
|
166
|
+
for (let i = 0; i < count * 2; i++) {
|
|
167
|
+
samples[i] = wasmModule.HEAPF32[(bufferPtr >> 2) + i];
|
|
168
|
+
}
|
|
169
|
+
wasmModule._free(bufferPtr);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Exported Functions
|
|
173
|
+
|
|
174
|
+
All WASM exports are listed explicitly in `CMakeLists.txt` under `EXPORTED_FUNCTIONS`. To add a new export:
|
|
175
|
+
|
|
176
|
+
1. Define the `extern "C"` function in `wasm_interface.cpp` with `EMSCRIPTEN_KEEPALIVE`
|
|
177
|
+
2. Add the mangled name (prefixed with `_`) to the `EXPORTED_FUNCTIONS` list in `CMakeLists.txt`
|
|
178
|
+
3. Rebuild WASM with `npm run build:wasm`
|
|
179
|
+
|
|
180
|
+
The exported API covers:
|
|
181
|
+
|
|
182
|
+
| Category | Examples |
|
|
183
|
+
|----------|----------|
|
|
184
|
+
| Lifecycle | `_init`, `_reset`, `_warmReset` |
|
|
185
|
+
| Execution | `_runCycles`, `_generateStereoAudioSamples`, `_stepInstruction` |
|
|
186
|
+
| CPU State | `_getPC`, `_getA`, `_getX`, `_getY`, `_getSP`, `_getP`, `_getTotalCycles` |
|
|
187
|
+
| Memory | `_readMemory`, `_writeMemory`, `_peekMemory`, `_readMainRAM` |
|
|
188
|
+
| Video | `_getFramebuffer`, `_getFramebufferSize`, `_isFrameReady`, `_forceRenderFrame` |
|
|
189
|
+
| Audio | `_setAudioVolume`, `_setAudioMuted` |
|
|
190
|
+
| Disk | `_insertDisk`, `_ejectDisk`, `_getDiskData`, `_isDiskInserted` |
|
|
191
|
+
| Debug | `_addBreakpoint`, `_stepOver`, `_stepOut`, `_addWatchpoint`, `_setTraceEnabled` |
|
|
192
|
+
| Expansion | `_getSlotCard`, `_setSlotCard`, `_isSlotEmpty` |
|
|
193
|
+
| Filesystem | `_isDOS33Format`, `_isProDOSFormat`, `_getDOS33Catalog`, `_getProDOSCatalog` |
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Rendering Pipeline
|
|
198
|
+
|
|
199
|
+
The rendering pipeline has two stages:
|
|
200
|
+
|
|
201
|
+
**C++ Video Rendering** -- The `Video` class renders into a 560x384 RGBA framebuffer (280x192 doubled). Rendering is progressive: `renderUpToCycle()` is called after each CPU instruction to render scanlines up to the current beam position. At frame boundaries, `renderFrame()` finalizes the current frame using a change log that records video switch changes at specific cycles.
|
|
202
|
+
|
|
203
|
+
**WebGL Display** -- The JavaScript `WebGLRenderer` uploads the framebuffer as a texture and applies CRT shader effects (scanlines, curvature, bloom, phosphor glow). The display pipeline:
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
WASM Framebuffer (560x384 RGBA)
|
|
207
|
+
--> JS reads via HEAPU8[fbPtr..fbPtr+fbSize]
|
|
208
|
+
--> WebGLRenderer.updateTexture(framebuffer)
|
|
209
|
+
--> Fragment shader applies CRT effects
|
|
210
|
+
--> Canvas displays final output
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Build System
|
|
216
|
+
|
|
217
|
+
The project uses CMake for C++ compilation and Vite for JavaScript bundling.
|
|
218
|
+
|
|
219
|
+
### WASM Build
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
npm run build:wasm
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
This invokes Emscripten's `emcmake cmake` and `emmake make` to compile the C++ core to WebAssembly. Key Emscripten settings:
|
|
226
|
+
|
|
227
|
+
| Setting | Value | Purpose |
|
|
228
|
+
|---------|-------|---------|
|
|
229
|
+
| `WASM` | 1 | Output WebAssembly |
|
|
230
|
+
| `MODULARIZE` | 1 | Wrap in factory function |
|
|
231
|
+
| `EXPORT_NAME` | `createA2EModule` | Global factory name |
|
|
232
|
+
| `ALLOW_MEMORY_GROWTH` | 1 | Dynamic heap expansion |
|
|
233
|
+
| `INITIAL_MEMORY` | 32 MB | Starting heap size |
|
|
234
|
+
| `MAXIMUM_MEMORY` | 64 MB | Maximum heap size |
|
|
235
|
+
| `NO_EXIT_RUNTIME` | 1 | Keep runtime alive |
|
|
236
|
+
| `ASYNCIFY` | 0 | Disabled (not needed) |
|
|
237
|
+
| Optimization | `-O3 -flto` | Full optimization with LTO |
|
|
238
|
+
|
|
239
|
+
Output files (`a2e.js` and `a2e.wasm`) are copied to `public/` after compilation.
|
|
240
|
+
|
|
241
|
+
### ROM Embedding
|
|
242
|
+
|
|
243
|
+
ROM files are embedded at compile time. A shell script (`scripts/generate_roms.sh`) converts binary ROM files into C arrays in `generated/roms.cpp`, which is `#include`-ed by `emulator.cpp`. The embedded ROMs are:
|
|
244
|
+
|
|
245
|
+
| ROM File | Size | Purpose |
|
|
246
|
+
|----------|------|---------|
|
|
247
|
+
| `342-0349-B-C0-FF.bin` | 16 KB | System ROM ($C000-$FFFF) |
|
|
248
|
+
| `342-0273-A-US-UK.bin` | 4 KB | Character ROM (US/UK) |
|
|
249
|
+
| `341-0027.bin` | 256 bytes | Disk II controller ROM |
|
|
250
|
+
| `Thunderclock Plus ROM.bin` | 2 KB | Thunderclock card ROM |
|
|
251
|
+
| `Apple Mouse Interface Card ROM` | 2 KB | Mouse card ROM |
|
|
252
|
+
|
|
253
|
+
### JavaScript Build
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
npm run dev # Vite dev server at localhost:3000 with hot reload
|
|
257
|
+
npm run build # Production build (WASM + Vite bundle) to dist/
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
The Vite build handles ES6 module bundling, CSS processing, and asset optimization. The audio worklet (`audio-worklet.js`) is loaded separately since worklets cannot be bundled.
|
|
261
|
+
|
|
262
|
+
### Native Build (Testing)
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
mkdir -p build-native && cd build-native
|
|
266
|
+
cmake ..
|
|
267
|
+
make -j$(sysctl -n hw.ncpu)
|
|
268
|
+
ctest --verbose
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The native build compiles test executables for CPU compliance (Klaus Dormann), Thunderclock card behavior, and GCR encoding. The emulator itself does not have a native runtime target.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Key Constants
|
|
276
|
+
|
|
277
|
+
Defined in `src/core/types.hpp`:
|
|
278
|
+
|
|
279
|
+
| Constant | Value | Description |
|
|
280
|
+
|----------|-------|-------------|
|
|
281
|
+
| `CPU_CLOCK_HZ` | 1,023,000 | CPU clock frequency (1.023 MHz) |
|
|
282
|
+
| `AUDIO_SAMPLE_RATE` | 48,000 | Audio output sample rate |
|
|
283
|
+
| `CYCLES_PER_SAMPLE` | ~21.3125 | CPU cycles per audio sample |
|
|
284
|
+
| `CYCLES_PER_SCANLINE` | 65 | CPU cycles per horizontal scanline |
|
|
285
|
+
| `SCANLINES_PER_FRAME` | 262 | Total scanlines per frame (192 visible + 70 VBL) |
|
|
286
|
+
| `CYCLES_PER_FRAME` | 17,030 | CPU cycles per video frame (65 x 262) |
|
|
287
|
+
| `SCREEN_WIDTH` | 560 | Framebuffer width (280 x 2) |
|
|
288
|
+
| `SCREEN_HEIGHT` | 384 | Framebuffer height (192 x 2) |
|
|
289
|
+
| `FRAMEBUFFER_SIZE` | 860,160 | Framebuffer byte size (560 x 384 x 4 RGBA) |
|
|
290
|
+
| `MAIN_RAM_SIZE` | 65,536 | Main RAM (64 KB) |
|
|
291
|
+
| `AUX_RAM_SIZE` | 65,536 | Auxiliary RAM (64 KB) |
|
|
292
|
+
| `ROM_SIZE` | 16,384 | System ROM (16 KB) |
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## See Also
|
|
297
|
+
|
|
298
|
+
- [[CPU-Emulation]] -- 65C02 processor details
|
|
299
|
+
- [[Memory-System]] -- MMU, bank switching, soft switches
|
|
300
|
+
- [[Video-Rendering]] -- Per-scanline rendering and video modes
|
|
301
|
+
- [[Audio-System]] -- Speaker and Mockingboard audio
|
|
302
|
+
- [[Expansion-Slots]] -- Card architecture and slot memory map
|
|
303
|
+
- [[Save-States]] -- Binary state serialization format
|