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,273 @@
|
|
|
1
|
+
# Softswitch Implementation Comparison: web-a2e vs AppleWin
|
|
2
|
+
|
|
3
|
+
This document compares the softswitch handling between web-a2e and AppleWin (the reference Apple II emulator).
|
|
4
|
+
|
|
5
|
+
## Architecture Overview
|
|
6
|
+
|
|
7
|
+
| Aspect | web-a2e | AppleWin |
|
|
8
|
+
|--------|---------|----------|
|
|
9
|
+
| **Structure** | Single `SoftSwitches` struct with ~30 boolean flags in `types.hpp`, centralized handling in `MMU::readSoftSwitch()` / `writeSoftSwitch()` | Bitmask `g_memmode` with flags (`MF_80STORE`, `MF_AUXREAD`, etc.), distributed across IO handler functions |
|
|
10
|
+
| **Dispatch** | Single large `switch` statement on `(address & 0xFF)` | Function pointer array (`IORead[8]`, `IOWrite[8]`) dispatching to `IORead_C0xx()` / `IOWrite_C0xx()` handlers |
|
|
11
|
+
| **Memory Model** | Direct boolean flags accessed via `switches_.xxx` | Bitfield operations: `SetMemMode(g_memmode | MF_FLAG)` |
|
|
12
|
+
|
|
13
|
+
## I/O Dispatch Mechanism
|
|
14
|
+
|
|
15
|
+
### AppleWin Approach
|
|
16
|
+
|
|
17
|
+
AppleWin uses function pointer arrays split by $10 address ranges:
|
|
18
|
+
|
|
19
|
+
```cpp
|
|
20
|
+
static iofunction IORead_C0xx[8] = {
|
|
21
|
+
IORead_C00x, // Keyboard
|
|
22
|
+
IORead_C01x, // Memory/Video status
|
|
23
|
+
IORead_C02x, // Cassette
|
|
24
|
+
IORead_C03x, // Speaker
|
|
25
|
+
IORead_C04x, // Unused
|
|
26
|
+
IORead_C05x, // Video mode switches
|
|
27
|
+
IORead_C06x, // Joystick
|
|
28
|
+
IORead_C07x, // Joystick/Video
|
|
29
|
+
};
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Each handler function processes 16 addresses:
|
|
33
|
+
|
|
34
|
+
```cpp
|
|
35
|
+
static BYTE __stdcall IORead_C01x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles)
|
|
36
|
+
{
|
|
37
|
+
bool res = false;
|
|
38
|
+
switch (addr & 0xf)
|
|
39
|
+
{
|
|
40
|
+
case 0x0: return KeybReadFlag();
|
|
41
|
+
case 0x1: res = SW_BANK2 ? true : false; break;
|
|
42
|
+
case 0x2: res = SW_HIGHRAM ? true : false; break;
|
|
43
|
+
// ...
|
|
44
|
+
}
|
|
45
|
+
return KeybGetKeycode() | (res ? 0x80 : 0);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### web-a2e Approach
|
|
50
|
+
|
|
51
|
+
web-a2e uses a monolithic switch in `mmu.cpp`:
|
|
52
|
+
|
|
53
|
+
```cpp
|
|
54
|
+
uint8_t MMU::readSoftSwitch(uint16_t address) {
|
|
55
|
+
uint8_t reg = address & 0xFF;
|
|
56
|
+
switch (reg) {
|
|
57
|
+
case 0x00: return keyboardLatch_;
|
|
58
|
+
case 0x10: keyboardLatch_ &= 0x7F; return keyboardLatch_;
|
|
59
|
+
case 0x11: return (switches_.lcram2 ? 0x80 : 0x00) | (getFloatingBusValue() & 0x7F);
|
|
60
|
+
// ... all 256 cases
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Trade-offs:**
|
|
66
|
+
- AppleWin: Better code organization, easier to maintain per-device handlers
|
|
67
|
+
- web-a2e: Simpler dispatch, all logic visible in one place
|
|
68
|
+
|
|
69
|
+
## Status Read Return Values ($C01x)
|
|
70
|
+
|
|
71
|
+
Both implementations return the switch state in bit 7 with data bus noise in bits 0-6.
|
|
72
|
+
|
|
73
|
+
### AppleWin
|
|
74
|
+
|
|
75
|
+
Returns the last keyboard keycode OR'd with the status bit:
|
|
76
|
+
|
|
77
|
+
```cpp
|
|
78
|
+
return KeybGetKeycode() | (res ? 0x80 : 0);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### web-a2e
|
|
82
|
+
|
|
83
|
+
Returns floating bus value OR'd with the status bit:
|
|
84
|
+
|
|
85
|
+
```cpp
|
|
86
|
+
return (switches_.lcram2 ? 0x80 : 0x00) | (getFloatingBusValue() & 0x7F);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Both approaches are correct per "Understanding the Apple IIe" (UTAIIe) - the lower 7 bits represent data bus noise from the previous cycle.
|
|
90
|
+
|
|
91
|
+
## Memory Paging Updates
|
|
92
|
+
|
|
93
|
+
### AppleWin
|
|
94
|
+
|
|
95
|
+
AppleWin maintains shadow memory arrays for fast access:
|
|
96
|
+
|
|
97
|
+
```cpp
|
|
98
|
+
LPBYTE memshadow[256]; // Read pointers per 256-byte page
|
|
99
|
+
LPBYTE memwrite[256]; // Write pointers per 256-byte page
|
|
100
|
+
BYTE memdirty[256]; // Dirty flags for copy-back
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
When a paging mode changes, `UpdatePaging()` copies memory between shadow regions:
|
|
104
|
+
|
|
105
|
+
```cpp
|
|
106
|
+
if ((lastmemmode != g_memmode) || modechanging)
|
|
107
|
+
{
|
|
108
|
+
for (UINT page = 0; page < 256; page++)
|
|
109
|
+
{
|
|
110
|
+
if (oldshadow[page] != memshadow[page])
|
|
111
|
+
{
|
|
112
|
+
if (*(memdirty+page) & 1)
|
|
113
|
+
memcpy(oldshadow[page], mem+(page << 8), 256);
|
|
114
|
+
memcpy(mem+(page << 8), memshadow[page], 256);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### web-a2e
|
|
121
|
+
|
|
122
|
+
web-a2e evaluates switches at memory access time without shadow caching:
|
|
123
|
+
|
|
124
|
+
```cpp
|
|
125
|
+
// In read() method
|
|
126
|
+
if (switches_.store80 && switches_.page2) {
|
|
127
|
+
return auxMemory_[address];
|
|
128
|
+
}
|
|
129
|
+
if (switches_.ramrd) {
|
|
130
|
+
return auxMemory_[address];
|
|
131
|
+
}
|
|
132
|
+
return mainMemory_[address];
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Trade-offs:**
|
|
136
|
+
- AppleWin: Faster memory access after mode change (direct pointer dereference)
|
|
137
|
+
- web-a2e: Simpler implementation, no synchronization overhead, but more conditionals per access
|
|
138
|
+
|
|
139
|
+
## Language Card Handling ($C080-$C08F)
|
|
140
|
+
|
|
141
|
+
### AppleWin
|
|
142
|
+
|
|
143
|
+
Delegates to a separate `LanguageCard` class that manages:
|
|
144
|
+
- Bank selection (bank 1 vs bank 2)
|
|
145
|
+
- Read source (RAM vs ROM)
|
|
146
|
+
- Write enable state with pre-write tracking
|
|
147
|
+
|
|
148
|
+
### web-a2e
|
|
149
|
+
|
|
150
|
+
Handles inline in `handleLanguageCardSwitch()`:
|
|
151
|
+
|
|
152
|
+
```cpp
|
|
153
|
+
uint8_t MMU::handleLanguageCardSwitch(uint8_t reg) {
|
|
154
|
+
bool bank2 = !(reg & 0x08);
|
|
155
|
+
uint8_t op = reg & 0x03;
|
|
156
|
+
bool readRAM = (op == 0 || op == 3);
|
|
157
|
+
|
|
158
|
+
switch (op) {
|
|
159
|
+
case 0: // $C080, $C088: Read RAM, write disabled
|
|
160
|
+
switches_.lcwrite = false;
|
|
161
|
+
switches_.lcprewrite = false;
|
|
162
|
+
break;
|
|
163
|
+
case 1: // $C081, $C089: Read ROM, write enable on second read
|
|
164
|
+
if (switches_.lcprewrite) {
|
|
165
|
+
switches_.lcwrite = true;
|
|
166
|
+
}
|
|
167
|
+
switches_.lcprewrite = true;
|
|
168
|
+
break;
|
|
169
|
+
// ...
|
|
170
|
+
}
|
|
171
|
+
switches_.lcram = readRAM;
|
|
172
|
+
switches_.lcram2 = bank2;
|
|
173
|
+
return getFloatingBusValue();
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Both implementations correctly handle the double-read requirement for write enable.
|
|
178
|
+
|
|
179
|
+
## Annunciators ($C058-$C05F)
|
|
180
|
+
|
|
181
|
+
### AppleWin
|
|
182
|
+
|
|
183
|
+
Has special handling for DHIRES that checks machine type and IOUDIS state:
|
|
184
|
+
|
|
185
|
+
```cpp
|
|
186
|
+
static BYTE __stdcall IOReadWrite_ANx(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles)
|
|
187
|
+
{
|
|
188
|
+
if (IsAppleIIeOrAbove(GetApple2Type()))
|
|
189
|
+
{
|
|
190
|
+
if (!IsAppleIIc(GetApple2Type()) || SW_IOUDIS)
|
|
191
|
+
GetVideo().VideoSetMode(pc, addr, bWrite, d, nExecutedCycles);
|
|
192
|
+
}
|
|
193
|
+
if (IsAppleIIc(GetApple2Type()))
|
|
194
|
+
return 0; // No ANx lines for //c
|
|
195
|
+
return IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles);
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### web-a2e
|
|
200
|
+
|
|
201
|
+
Directly sets annunciator flags without DHIRES coupling:
|
|
202
|
+
|
|
203
|
+
```cpp
|
|
204
|
+
case 0x5E:
|
|
205
|
+
switches_.an3 = false;
|
|
206
|
+
return getFloatingBusValue(); // AN3 OFF = DHIRES enabled
|
|
207
|
+
case 0x5F:
|
|
208
|
+
switches_.an3 = true;
|
|
209
|
+
return getFloatingBusValue(); // AN3 ON = DHIRES disabled
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The DHIRES state is derived from AN3 when needed rather than tracked separately.
|
|
213
|
+
|
|
214
|
+
## Potential Issues in web-a2e
|
|
215
|
+
|
|
216
|
+
### 1. $C000-$C00B Read Behavior
|
|
217
|
+
|
|
218
|
+
web-a2e treats $C000 as the only keyboard read address. AppleWin's `IORead_C00x` returns keyboard data for the entire `$C000-$C00F` range, which matches hardware behavior where the keyboard decoder responds to all 16 addresses.
|
|
219
|
+
|
|
220
|
+
**Current web-a2e code:**
|
|
221
|
+
```cpp
|
|
222
|
+
case 0x00: return keyboardLatch_;
|
|
223
|
+
case 0x01: case 0x02: ... case 0x0F: return getFloatingBusValue();
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Should be:**
|
|
227
|
+
```cpp
|
|
228
|
+
case 0x00: case 0x01: case 0x02: ... case 0x0F:
|
|
229
|
+
return keyboardLatch_;
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### 2. Write-Only Switch Reads ($C000-$C00B)
|
|
233
|
+
|
|
234
|
+
AppleWin returns keyboard data when reading write-only memory switches. web-a2e returns floating bus, which may cause compatibility issues with software that reads these addresses expecting keyboard data.
|
|
235
|
+
|
|
236
|
+
### 3. PAGE2/HIRES and Memory Remapping
|
|
237
|
+
|
|
238
|
+
AppleWin routes $C054-$C057 through `MemSetPaging()` which triggers `UpdatePaging()` to remap memory pointers. web-a2e sets switches but relies on runtime evaluation. This is functionally equivalent but should be verified for timing-sensitive software.
|
|
239
|
+
|
|
240
|
+
### 4. 80STORE/PAGE2 Interaction
|
|
241
|
+
|
|
242
|
+
AppleWin explicitly updates memory mappings when PAGE2 changes and 80STORE is set:
|
|
243
|
+
|
|
244
|
+
```cpp
|
|
245
|
+
if (SW_80STORE)
|
|
246
|
+
{
|
|
247
|
+
for (loop = 0x04; loop < 0x08; loop++)
|
|
248
|
+
{
|
|
249
|
+
memshadow[loop] = SW_PAGE2 ? memaux+(loop << 8) : memmain+(loop << 8);
|
|
250
|
+
memwrite[loop] = mem+(loop << 8);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
web-a2e checks `store80 && page2` at read/write time, which is functionally equivalent but potentially slower for tight loops.
|
|
256
|
+
|
|
257
|
+
## Summary
|
|
258
|
+
|
|
259
|
+
| Feature | web-a2e | AppleWin |
|
|
260
|
+
|---------|---------|----------|
|
|
261
|
+
| Code clarity | Better - all logic in one switch | Distributed across handlers |
|
|
262
|
+
| Performance | Slower - runtime switch evaluation | Faster - pointer tables |
|
|
263
|
+
| Machine support | IIe focused | II, II+, IIe, IIc, IIgs |
|
|
264
|
+
| Memory model | Direct evaluation | Shadow page tables |
|
|
265
|
+
| Maintainability | Easier - single file | Harder - multiple classes |
|
|
266
|
+
|
|
267
|
+
web-a2e's implementation is cleaner and more readable with the struct-based approach, while AppleWin is more optimized for performance with its paged shadow memory and function pointer dispatch. Both correctly implement the core Apple IIe softswitch semantics.
|
|
268
|
+
|
|
269
|
+
## References
|
|
270
|
+
|
|
271
|
+
- AppleWin source: https://github.com/AppleWin/AppleWin/blob/master/source/Memory.cpp
|
|
272
|
+
- "Understanding the Apple IIe" by James Sather (UTAIIe)
|
|
273
|
+
- Apple IIe Technical Reference Manual
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Thunderclock Debug Notes
|
|
2
|
+
|
|
3
|
+
## Current Status
|
|
4
|
+
|
|
5
|
+
- ProDOS **detects** the clock card (MACHID bit 0 is set)
|
|
6
|
+
- But new files show `<NO DATE>` - time data isn't being read correctly
|
|
7
|
+
- The serial protocol implementation likely needs adjustment
|
|
8
|
+
|
|
9
|
+
## Verification Commands (ProDOS BASIC)
|
|
10
|
+
|
|
11
|
+
### Check if clock was detected
|
|
12
|
+
```
|
|
13
|
+
PRINT PEEK(49048)
|
|
14
|
+
```
|
|
15
|
+
- Odd number = clock detected (bit 0 set)
|
|
16
|
+
- Result was **179** - clock IS detected
|
|
17
|
+
|
|
18
|
+
### Check clock driver hook
|
|
19
|
+
```
|
|
20
|
+
PRINT PEEK(49926)
|
|
21
|
+
```
|
|
22
|
+
- 76 ($4C) = JMP, clock driver installed
|
|
23
|
+
- 96 ($60) = RTS, no clock driver
|
|
24
|
+
- Result was **144** ($90) - unexpected value
|
|
25
|
+
|
|
26
|
+
### Test I/O register (slot 5)
|
|
27
|
+
```
|
|
28
|
+
PRINT PEEK(49360)
|
|
29
|
+
```
|
|
30
|
+
- Reads $C0D0 (Thunderclock I/O for slot 5)
|
|
31
|
+
- Result was **0** - card is responding
|
|
32
|
+
|
|
33
|
+
## Manual Serial Protocol Test
|
|
34
|
+
|
|
35
|
+
Run these in order to test if the Thunderclock responds to the serial protocol:
|
|
36
|
+
|
|
37
|
+
### Step 1 - Trigger time read (STROBE + CMD_TIMED)
|
|
38
|
+
```
|
|
39
|
+
POKE 49360, 164
|
|
40
|
+
```
|
|
41
|
+
($A4 = CMD_TIMED $A0 | STROBE $04)
|
|
42
|
+
|
|
43
|
+
### Step 2 - Clock out first data bit
|
|
44
|
+
```
|
|
45
|
+
POKE 49360, 162
|
|
46
|
+
```
|
|
47
|
+
($A2 = CMD_TIMED $A0 | CLOCK $02)
|
|
48
|
+
|
|
49
|
+
### Step 3 - Read the result
|
|
50
|
+
```
|
|
51
|
+
PRINT PEEK(49360)
|
|
52
|
+
```
|
|
53
|
+
- If bit 7 is set: value >= 128
|
|
54
|
+
- If bit 7 is clear: value < 128
|
|
55
|
+
- This tells us if data bits are being shifted out
|
|
56
|
+
|
|
57
|
+
### Step 4 - Clock out more bits and check pattern
|
|
58
|
+
Repeat steps 2-3 multiple times to see if different bits come out:
|
|
59
|
+
```
|
|
60
|
+
POKE 49360, 160: POKE 49360, 162: PRINT PEEK(49360)
|
|
61
|
+
POKE 49360, 160: POKE 49360, 162: PRINT PEEK(49360)
|
|
62
|
+
POKE 49360, 160: POKE 49360, 162: PRINT PEEK(49360)
|
|
63
|
+
```
|
|
64
|
+
(Each line: clear clock, set clock, read bit)
|
|
65
|
+
|
|
66
|
+
## Expected Time Format
|
|
67
|
+
|
|
68
|
+
The Thunderclock outputs 40 bits in this order (MSB first):
|
|
69
|
+
1. Month (4 bits) - 0-11
|
|
70
|
+
2. Day of week (4 bits) - 0-6, Sunday=0
|
|
71
|
+
3. Day of month (8 bits BCD)
|
|
72
|
+
4. Hour (8 bits BCD, 24-hour)
|
|
73
|
+
5. Minute (8 bits BCD)
|
|
74
|
+
6. Second (8 bits BCD)
|
|
75
|
+
|
|
76
|
+
## Memory Addresses Reference
|
|
77
|
+
|
|
78
|
+
| Address | Decimal | Purpose |
|
|
79
|
+
|---------|---------|---------|
|
|
80
|
+
| $C0D0 | 49360 | Slot 5 I/O register |
|
|
81
|
+
| $C500 | 50432 | Slot 5 ROM start |
|
|
82
|
+
| $BF98 | 49048 | ProDOS MACHID |
|
|
83
|
+
| $BF06 | 49926 | ProDOS clock hook |
|
|
84
|
+
|
|
85
|
+
## Files
|
|
86
|
+
|
|
87
|
+
- `src/core/cards/thunderclock_card.cpp` - Serial protocol implementation
|
|
88
|
+
- `src/core/cards/thunderclock_card.hpp` - Card class definition
|
|
89
|
+
- `roms/Thunderclock Plus ROM.bin` - Real Thunderclock ROM (2KB)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
10 HGR : HGR2
|
|
2
|
+
12 SZ = 150 : CX = 140 : CY = 96
|
|
3
|
+
15 SC = 0
|
|
4
|
+
18 ANGLE = 0
|
|
5
|
+
20 DIM PTS(7,2)
|
|
6
|
+
25 DIM RZP(7,2) : DIM RYP(7,2) : DIM RXP(7,2)
|
|
7
|
+
27 DIM PRJ(7,2)
|
|
8
|
+
30 GOSUB 5000
|
|
9
|
+
|
|
10
|
+
100 REM DRAW LOOP
|
|
11
|
+
105 CALL 62450
|
|
12
|
+
110 GOSUB 2000 : REM ROTATE POINTS
|
|
13
|
+
120 GOSUB 6000 : REM LINES
|
|
14
|
+
140 ANGLE = ANGLE + 0.1
|
|
15
|
+
150 GOSUB 1000
|
|
16
|
+
160 GOTO 100
|
|
17
|
+
|
|
18
|
+
500 X1 = PRJ(A,0) * SZ + CX
|
|
19
|
+
510 Y1 = PRJ(A,1) * SZ + CY
|
|
20
|
+
520 X2 = PRJ(B,0) * SZ + CX
|
|
21
|
+
530 Y2 = PRJ(B,1) * SZ + CY
|
|
22
|
+
535 HCOLOR=3
|
|
23
|
+
540 HPLOT X1,Y1 TO X2,Y2
|
|
24
|
+
550 RETURN
|
|
25
|
+
|
|
26
|
+
1000 SC = NOT SC
|
|
27
|
+
1010 POKE 49236 + NOT SC,0
|
|
28
|
+
1020 POKE 230,32 + 32 * SC
|
|
29
|
+
1030 RETURN
|
|
30
|
+
|
|
31
|
+
2000 CS = COS(ANGLE)
|
|
32
|
+
2010 SN = SIN(ANGLE)
|
|
33
|
+
2020 FOR N = 0 TO 7
|
|
34
|
+
2030 RZP(N,0) = -CS * PTS(N,0) + SN * PTS(N,1) + 0 * PTS(N,2)
|
|
35
|
+
2040 RZP(N,1) = SN * PTS(N,0) + CS * PTS(N,1) + 0 * PTS(N,2)
|
|
36
|
+
2050 RZP(N,2) = 0 * PTS(N,0) + 0 * PTS(N,1) + 1 * PTS(N,2)
|
|
37
|
+
2060 RYP(N,0) = CS * RZP(N,0) + 0 * RZP(N,1) + SN * RZP(N,2)
|
|
38
|
+
2070 RYP(N,1) = 0 * RZP(N,0) + 1 * RZP(N,1) + 0 * RZP(N,2)
|
|
39
|
+
2080 RYP(N,2) = -SN * RZP(N,0) + 0 * RZP(N,1) + CS * RZP(N,2)
|
|
40
|
+
2090 RXP(N,0) = RYP(N,0)
|
|
41
|
+
2100 RXP(N,1) = CS * RYP(N,1) - SN * RYP(N,2)
|
|
42
|
+
2110 RXP(N,2) = SN * RYP(N,1) + CS * RYP(N,2)
|
|
43
|
+
2120 Z = 1 / (5 - RXP(N,2))
|
|
44
|
+
2130 PRJ(N,0) = RXP(N,0) * Z
|
|
45
|
+
2140 PRJ(N,1) = RXP(N,1) * Z
|
|
46
|
+
2150 NEXT N
|
|
47
|
+
2160 RETURN
|
|
48
|
+
|
|
49
|
+
5000 PTS(0,0) = -1: PTS(0,1) = -1 : PTS(0,2) = -1
|
|
50
|
+
5010 PTS(1,0) = 1: PTS(1,1) = -1 : PTS(1,2) = -1
|
|
51
|
+
5020 PTS(2,0) = 1: PTS(2,1) = 1 : PTS(2,2) = -1
|
|
52
|
+
5030 PTS(3,0) = -1: PTS(3,1) = 1 : PTS(3,2) = -1
|
|
53
|
+
5040 PTS(4,0) = -1: PTS(4,1) = -1 : PTS(4,2) = 1
|
|
54
|
+
5050 PTS(5,0) = 1: PTS(5,1) = -1 : PTS(5,2) = 1
|
|
55
|
+
5060 PTS(6,0) = 1: PTS(6,1) = 1 : PTS(6,2) = 1
|
|
56
|
+
5070 PTS(7,0) = -1: PTS(7,1) = 1 : PTS(7,2) = 1
|
|
57
|
+
5080 RETURN
|
|
58
|
+
|
|
59
|
+
6000 REM DRAW LINES
|
|
60
|
+
6010 A = 0 : B = 1 : GOSUB 500
|
|
61
|
+
6020 A = 1 : B = 2 : GOSUB 500
|
|
62
|
+
6030 A = 2 : B = 3 : GOSUB 500
|
|
63
|
+
6040 A = 3 : B = 0 : GOSUB 500
|
|
64
|
+
6050 A = 4 : B = 5 : GOSUB 500
|
|
65
|
+
6060 A = 5 : B = 6 : GOSUB 500
|
|
66
|
+
6070 A = 6 : B = 7 : GOSUB 500
|
|
67
|
+
6080 A = 7 : B = 4 : GOSUB 500
|
|
68
|
+
6090 A = 0 : B = 4 : GOSUB 500
|
|
69
|
+
6100 A = 1 : B = 5 : GOSUB 500
|
|
70
|
+
6110 A = 2 : B = 6 : GOSUB 500
|
|
71
|
+
6120 A = 3 : B = 7 : GOSUB 500
|
|
72
|
+
6130 RETURN
|
package/examples/hello.s
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
**********************************
|
|
2
|
+
* *
|
|
3
|
+
* HELLO WORLD - APPLE IIE *
|
|
4
|
+
* 65C02 ASSEMBLY EXAMPLE *
|
|
5
|
+
* *
|
|
6
|
+
**********************************
|
|
7
|
+
|
|
8
|
+
* Apple IIe ROM routines
|
|
9
|
+
COUT EQU $FDED ;Character output routine
|
|
10
|
+
|
|
11
|
+
ORG $0800 ;Standard BASIC program area
|
|
12
|
+
|
|
13
|
+
**********************************
|
|
14
|
+
* Main program entry point
|
|
15
|
+
**********************************
|
|
16
|
+
START LDX #$00 ;Initialize index to 0
|
|
17
|
+
|
|
18
|
+
**********************************
|
|
19
|
+
* Print loop - one char at a time
|
|
20
|
+
**********************************
|
|
21
|
+
LOOP LDA MESSAGE,X ;Load character at MESSAGE+X
|
|
22
|
+
BEQ DONE ;If zero (end of string), we're done
|
|
23
|
+
JSR COUT ;Print the character
|
|
24
|
+
PHX ;Save X on stack (65C02)
|
|
25
|
+
JSR DELAY ;Wait a bit
|
|
26
|
+
PLX ;Restore X from stack (65C02)
|
|
27
|
+
INX ;Increment index
|
|
28
|
+
BNE LOOP ;Continue if X hasn't wrapped
|
|
29
|
+
|
|
30
|
+
**********************************
|
|
31
|
+
* End of program
|
|
32
|
+
**********************************
|
|
33
|
+
DONE RTS ;Return to caller
|
|
34
|
+
|
|
35
|
+
**********************************
|
|
36
|
+
* Delay routine - nested loop
|
|
37
|
+
* Adjust WAIT value for longer/shorter delay
|
|
38
|
+
**********************************
|
|
39
|
+
WAIT EQU $60 ;Delay multiplier (~100ms)
|
|
40
|
+
|
|
41
|
+
DELAY LDY #WAIT ;Outer loop counter
|
|
42
|
+
DLY1 LDX #$FF ;Inner loop counter
|
|
43
|
+
DLY2 DEX ;Decrement inner
|
|
44
|
+
BNE DLY2 ;Loop until X=0
|
|
45
|
+
DEY ;Decrement outer
|
|
46
|
+
BNE DLY1 ;Loop until Y=0
|
|
47
|
+
RTS
|
|
48
|
+
|
|
49
|
+
**********************************
|
|
50
|
+
* Message data
|
|
51
|
+
* Using " delimiter sets high bit automatically
|
|
52
|
+
**********************************
|
|
53
|
+
MESSAGE ASC "HELLO APPLE IIE!"
|
|
54
|
+
DFB $8D ;Carriage return (with high bit)
|
|
55
|
+
DFB $00 ;Null terminator
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
; scroll.s - Smooth horizontal text scroller for Apple IIe
|
|
2
|
+
;
|
|
3
|
+
; Scrolls a message across the top line of the 40-column text screen.
|
|
4
|
+
; The message slides in from the right, crosses the screen, and exits left
|
|
5
|
+
; before looping. Press any key to quit.
|
|
6
|
+
;
|
|
7
|
+
; Assemble with Merlin or ca65:
|
|
8
|
+
; ca65 scroll.s -o scroll.o && ld65 scroll.o -t none -o SCROLL -S 0x0300
|
|
9
|
+
;
|
|
10
|
+
; Run from the monitor: 300G
|
|
11
|
+
; Or BRUN from DOS/ProDOS after saving.
|
|
12
|
+
|
|
13
|
+
ORG $0300
|
|
14
|
+
|
|
15
|
+
; ── Zero page temporaries ──────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
OFFSET EQU $06 ; current scroll offset (2 bytes)
|
|
18
|
+
SRCPTR EQU $08 ; pointer into message buffer (2 bytes)
|
|
19
|
+
|
|
20
|
+
; ── Hardware / ROM addresses ───────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
TXTLINE0 EQU $0400 ; screen RAM for text line 0
|
|
23
|
+
KBDSTROBE EQU $C000 ; keyboard data (bit 7 = key ready)
|
|
24
|
+
KBDCLEAR EQU $C010 ; clear keyboard strobe
|
|
25
|
+
HOME EQU $FC58 ; ROM: clear screen and home cursor
|
|
26
|
+
|
|
27
|
+
; ── Constants ──────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
SCRWIDTH EQU 40 ; visible columns
|
|
30
|
+
SPEED EQU $18 ; delay — lower = faster
|
|
31
|
+
|
|
32
|
+
; ======================================================================
|
|
33
|
+
; Entry — jump over data block so BUFLEN is defined before use
|
|
34
|
+
; ======================================================================
|
|
35
|
+
|
|
36
|
+
JMP START
|
|
37
|
+
|
|
38
|
+
; ======================================================================
|
|
39
|
+
; Message buffer
|
|
40
|
+
;
|
|
41
|
+
; Padded with 40 spaces on each side so the text scrolls cleanly on and
|
|
42
|
+
; off screen. Characters have the high bit set for normal Apple II text
|
|
43
|
+
; (ASCII + $80).
|
|
44
|
+
; ======================================================================
|
|
45
|
+
|
|
46
|
+
BUFFER
|
|
47
|
+
; 40 leading spaces
|
|
48
|
+
HEX A0A0A0A0A0A0A0A0A0A0 ; 10
|
|
49
|
+
HEX A0A0A0A0A0A0A0A0A0A0 ; 20
|
|
50
|
+
HEX A0A0A0A0A0A0A0A0A0A0 ; 30
|
|
51
|
+
HEX A0A0A0A0A0A0A0A0A0A0 ; 40
|
|
52
|
+
|
|
53
|
+
; Message: "HELLO FROM THE APPLE //E EMULATOR! "
|
|
54
|
+
HEX C8C5CCCCCF ; HELLO
|
|
55
|
+
HEX A0 ; (space)
|
|
56
|
+
HEX C6D2CFCD ; FROM
|
|
57
|
+
HEX A0 ; (space)
|
|
58
|
+
HEX D4C8C5 ; THE
|
|
59
|
+
HEX A0 ; (space)
|
|
60
|
+
HEX C1D0D0CCC5 ; APPLE
|
|
61
|
+
HEX A0AFAFC5 ; (space)//E
|
|
62
|
+
HEX A0 ; (space)
|
|
63
|
+
HEX C5CDD5CCC1D4CFD2 ; EMULATOR
|
|
64
|
+
HEX A1 ; !
|
|
65
|
+
HEX A0A0A0 ; (trailing spaces before pad)
|
|
66
|
+
|
|
67
|
+
MSGLEN EQU *-BUFFER-40 ; length of message + trailing spaces
|
|
68
|
+
|
|
69
|
+
; 40 trailing spaces
|
|
70
|
+
HEX A0A0A0A0A0A0A0A0A0A0 ; 10
|
|
71
|
+
HEX A0A0A0A0A0A0A0A0A0A0 ; 20
|
|
72
|
+
HEX A0A0A0A0A0A0A0A0A0A0 ; 30
|
|
73
|
+
HEX A0A0A0A0A0A0A0A0A0A0 ; 40
|
|
74
|
+
|
|
75
|
+
BUFEND
|
|
76
|
+
BUFLEN EQU BUFEND-BUFFER-SCRWIDTH+1 ; number of scroll positions
|
|
77
|
+
|
|
78
|
+
; ======================================================================
|
|
79
|
+
; Main code
|
|
80
|
+
; ======================================================================
|
|
81
|
+
|
|
82
|
+
START JSR HOME ; clear screen
|
|
83
|
+
|
|
84
|
+
LDA #0 ; reset scroll offset
|
|
85
|
+
STA OFFSET
|
|
86
|
+
STA OFFSET+1
|
|
87
|
+
|
|
88
|
+
; ── Main loop ─────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
LOOP LDA OFFSET ; SRCPTR = BUFFER + OFFSET
|
|
91
|
+
CLC
|
|
92
|
+
ADC #<BUFFER
|
|
93
|
+
STA SRCPTR
|
|
94
|
+
LDA OFFSET+1
|
|
95
|
+
ADC #>BUFFER
|
|
96
|
+
STA SRCPTR+1
|
|
97
|
+
|
|
98
|
+
; Copy 40 characters from SRCPTR to screen line 0
|
|
99
|
+
|
|
100
|
+
LDY #SCRWIDTH-1
|
|
101
|
+
COPY LDA (SRCPTR),Y
|
|
102
|
+
STA TXTLINE0,Y
|
|
103
|
+
DEY
|
|
104
|
+
BPL COPY
|
|
105
|
+
|
|
106
|
+
; Advance the offset; wrap when we reach the end of the padded message
|
|
107
|
+
|
|
108
|
+
INC OFFSET
|
|
109
|
+
BNE NOWRAP
|
|
110
|
+
INC OFFSET+1
|
|
111
|
+
NOWRAP
|
|
112
|
+
LDA OFFSET+1 ; compare OFFSET with BUFLEN
|
|
113
|
+
CMP #>BUFLEN
|
|
114
|
+
BCC NODELAY ; high byte less — not at end
|
|
115
|
+
BNE DORESET ; high byte greater — past end
|
|
116
|
+
LDA OFFSET
|
|
117
|
+
CMP #<BUFLEN
|
|
118
|
+
BCC NODELAY ; low byte < BUFLEN — continue
|
|
119
|
+
DORESET LDA #0 ; wrap offset back to zero
|
|
120
|
+
STA OFFSET
|
|
121
|
+
STA OFFSET+1
|
|
122
|
+
JMP LOOP
|
|
123
|
+
NODELAY
|
|
124
|
+
|
|
125
|
+
; ── Frame delay ───────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
LDX #SPEED
|
|
128
|
+
DELAYOUTER LDY #0
|
|
129
|
+
DELAYINNER DEY
|
|
130
|
+
BNE DELAYINNER
|
|
131
|
+
DEX
|
|
132
|
+
BNE DELAYOUTER
|
|
133
|
+
|
|
134
|
+
; ── Check for keypress (quit on any key) ──────────────────────────────
|
|
135
|
+
|
|
136
|
+
LDA KBDSTROBE
|
|
137
|
+
BPL LOOP ; no key — keep scrolling
|
|
138
|
+
STA KBDCLEAR ; clear strobe
|
|
139
|
+
RTS ; return to caller / monitor
|
|
140
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "web-a2e",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Apple //e emulator for the browser",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "npm run build:wasm && vite build",
|
|
9
|
+
"build:wasm": "mkdir -p build && cd build && emcmake cmake .. && emmake make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu)",
|
|
10
|
+
"preview": "vite preview",
|
|
11
|
+
"clean": "rm -rf build dist public/a2e.js public/a2e.wasm",
|
|
12
|
+
"deploy": "rsync -avz --delete dist/ vps-mike:web-a2e/data/",
|
|
13
|
+
"deploy:staging": "rsync -avz --delete dist/ vps-mike:/home/mike/web-a2e-staging/data/"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"vite": "^7.3.1"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|