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,286 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* basic_tokenizer.cpp - Applesoft BASIC tokenizer for direct memory insertion
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Mike Daley <michael_daley@icloud.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include "basic_tokenizer.hpp"
|
|
9
|
+
#include "basic_tokens.hpp"
|
|
10
|
+
#include <algorithm>
|
|
11
|
+
#include <cctype>
|
|
12
|
+
#include <cstring>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <vector>
|
|
15
|
+
|
|
16
|
+
namespace a2e {
|
|
17
|
+
|
|
18
|
+
// A keyword entry for greedy longest-match tokenization
|
|
19
|
+
struct KeywordEntry {
|
|
20
|
+
const char* keyword;
|
|
21
|
+
uint8_t token;
|
|
22
|
+
size_t length;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Build sorted keyword list (longest first) for greedy matching
|
|
26
|
+
static std::vector<KeywordEntry> buildKeywordList() {
|
|
27
|
+
std::vector<KeywordEntry> list;
|
|
28
|
+
for (int i = 0; i < APPLESOFT_TOKEN_COUNT; i++) {
|
|
29
|
+
KeywordEntry e;
|
|
30
|
+
e.keyword = APPLESOFT_TOKENS[i];
|
|
31
|
+
e.token = static_cast<uint8_t>(0x80 + i);
|
|
32
|
+
e.length = strlen(APPLESOFT_TOKENS[i]);
|
|
33
|
+
list.push_back(e);
|
|
34
|
+
}
|
|
35
|
+
// Sort by length descending for greedy longest-match
|
|
36
|
+
std::sort(list.begin(), list.end(), [](const KeywordEntry& a, const KeywordEntry& b) {
|
|
37
|
+
return a.length > b.length;
|
|
38
|
+
});
|
|
39
|
+
return list;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Parsed BASIC line
|
|
43
|
+
struct BasicLine {
|
|
44
|
+
int lineNumber;
|
|
45
|
+
std::string content; // uppercase content after line number
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Parse source into lines, extracting line numbers
|
|
49
|
+
static std::vector<BasicLine> parseSource(const char* source) {
|
|
50
|
+
std::vector<BasicLine> lines;
|
|
51
|
+
std::string src(source);
|
|
52
|
+
|
|
53
|
+
size_t pos = 0;
|
|
54
|
+
while (pos < src.size()) {
|
|
55
|
+
// Find end of line
|
|
56
|
+
size_t eol = src.find('\n', pos);
|
|
57
|
+
if (eol == std::string::npos) eol = src.size();
|
|
58
|
+
|
|
59
|
+
std::string rawLine = src.substr(pos, eol - pos);
|
|
60
|
+
pos = eol + 1;
|
|
61
|
+
|
|
62
|
+
// Trim leading/trailing whitespace
|
|
63
|
+
size_t start = rawLine.find_first_not_of(" \t\r");
|
|
64
|
+
if (start == std::string::npos) continue;
|
|
65
|
+
std::string trimmed = rawLine.substr(start);
|
|
66
|
+
|
|
67
|
+
// Extract line number
|
|
68
|
+
if (trimmed.empty() || !isdigit(static_cast<unsigned char>(trimmed[0]))) continue;
|
|
69
|
+
|
|
70
|
+
size_t numEnd = 0;
|
|
71
|
+
while (numEnd < trimmed.size() && isdigit(static_cast<unsigned char>(trimmed[numEnd]))) {
|
|
72
|
+
numEnd++;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
int lineNum = std::stoi(trimmed.substr(0, numEnd));
|
|
76
|
+
if (lineNum < 0 || lineNum > 63999) continue;
|
|
77
|
+
|
|
78
|
+
// Get content after line number, skip leading spaces
|
|
79
|
+
std::string content = trimmed.substr(numEnd);
|
|
80
|
+
size_t contentStart = content.find_first_not_of(" \t");
|
|
81
|
+
if (contentStart != std::string::npos) {
|
|
82
|
+
content = content.substr(contentStart);
|
|
83
|
+
} else {
|
|
84
|
+
content = "";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Skip lines with only a line number and no content
|
|
88
|
+
if (content.empty()) continue;
|
|
89
|
+
|
|
90
|
+
// Convert to uppercase, preserving case inside quoted strings
|
|
91
|
+
std::string upper;
|
|
92
|
+
upper.reserve(content.size());
|
|
93
|
+
bool inQuote = false;
|
|
94
|
+
for (char c : content) {
|
|
95
|
+
if (c == '"') {
|
|
96
|
+
inQuote = !inQuote;
|
|
97
|
+
upper += c;
|
|
98
|
+
} else if (inQuote) {
|
|
99
|
+
upper += c;
|
|
100
|
+
} else {
|
|
101
|
+
upper += static_cast<char>(toupper(static_cast<unsigned char>(c)));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
lines.push_back({lineNum, upper});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Sort by line number
|
|
109
|
+
std::sort(lines.begin(), lines.end(), [](const BasicLine& a, const BasicLine& b) {
|
|
110
|
+
return a.lineNumber < b.lineNumber;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return lines;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Tokenize a single line's content
|
|
117
|
+
static std::vector<uint8_t> tokenizeLine(const std::string& text,
|
|
118
|
+
const std::vector<KeywordEntry>& keywords) {
|
|
119
|
+
std::vector<uint8_t> bytes;
|
|
120
|
+
size_t i = 0;
|
|
121
|
+
bool inRem = false;
|
|
122
|
+
bool inData = false;
|
|
123
|
+
bool inQuote = false;
|
|
124
|
+
|
|
125
|
+
while (i < text.size()) {
|
|
126
|
+
char ch = text[i];
|
|
127
|
+
|
|
128
|
+
// Inside a quoted string - emit as-is until closing quote
|
|
129
|
+
if (inQuote) {
|
|
130
|
+
bytes.push_back(static_cast<uint8_t>(text[i]));
|
|
131
|
+
if (ch == '"') inQuote = false;
|
|
132
|
+
i++;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// After REM token - emit rest of line as raw ASCII
|
|
137
|
+
if (inRem) {
|
|
138
|
+
bytes.push_back(static_cast<uint8_t>(text[i]));
|
|
139
|
+
i++;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// After DATA token - emit as-is until colon
|
|
144
|
+
if (inData) {
|
|
145
|
+
if (ch == ':') {
|
|
146
|
+
inData = false;
|
|
147
|
+
// Fall through to normal processing for the colon
|
|
148
|
+
} else {
|
|
149
|
+
bytes.push_back(static_cast<uint8_t>(text[i]));
|
|
150
|
+
i++;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Opening quote
|
|
156
|
+
if (ch == '"') {
|
|
157
|
+
inQuote = true;
|
|
158
|
+
bytes.push_back(static_cast<uint8_t>(text[i]));
|
|
159
|
+
i++;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ? is shorthand for PRINT
|
|
164
|
+
if (ch == '?') {
|
|
165
|
+
bytes.push_back(0xBA); // PRINT token
|
|
166
|
+
i++;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Skip spaces outside strings (Apple II tokenizer ignores spaces)
|
|
171
|
+
if (ch == ' ') {
|
|
172
|
+
i++;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Try greedy longest-match against keywords
|
|
177
|
+
bool matched = false;
|
|
178
|
+
const char* remaining = text.c_str() + i;
|
|
179
|
+
size_t remainingLen = text.size() - i;
|
|
180
|
+
|
|
181
|
+
for (const auto& kw : keywords) {
|
|
182
|
+
if (kw.length <= remainingLen &&
|
|
183
|
+
strncmp(remaining, kw.keyword, kw.length) == 0) {
|
|
184
|
+
bytes.push_back(kw.token);
|
|
185
|
+
i += kw.length;
|
|
186
|
+
matched = true;
|
|
187
|
+
|
|
188
|
+
if (kw.token == 0xB2) { // REM
|
|
189
|
+
inRem = true;
|
|
190
|
+
} else if (kw.token == 0x83) { // DATA
|
|
191
|
+
inData = true;
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!matched) {
|
|
198
|
+
bytes.push_back(static_cast<uint8_t>(text[i]));
|
|
199
|
+
i++;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return bytes;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
int loadBasicProgram(const char* source, MemReadFn readMem, MemWriteFn writeMem) {
|
|
207
|
+
if (!source) return -1;
|
|
208
|
+
|
|
209
|
+
auto lines = parseSource(source);
|
|
210
|
+
if (lines.empty()) return 0;
|
|
211
|
+
|
|
212
|
+
auto keywords = buildKeywordList();
|
|
213
|
+
|
|
214
|
+
constexpr uint16_t txttab = 0x0801;
|
|
215
|
+
uint16_t addr = txttab;
|
|
216
|
+
|
|
217
|
+
// First pass: tokenize all lines and compute layout
|
|
218
|
+
struct TokenizedLine {
|
|
219
|
+
int lineNumber;
|
|
220
|
+
std::vector<uint8_t> tokens;
|
|
221
|
+
uint16_t lineSize; // 2 (next-ptr) + 2 (line-num) + tokens + 1 (terminator)
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
std::vector<TokenizedLine> tokenizedLines;
|
|
225
|
+
for (const auto& line : lines) {
|
|
226
|
+
auto tokens = tokenizeLine(line.content, keywords);
|
|
227
|
+
uint16_t lineSize = static_cast<uint16_t>(2 + 2 + tokens.size() + 1);
|
|
228
|
+
tokenizedLines.push_back({line.lineNumber, std::move(tokens), lineSize});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Second pass: write into memory
|
|
232
|
+
for (const auto& line : tokenizedLines) {
|
|
233
|
+
uint16_t nextAddr = addr + line.lineSize;
|
|
234
|
+
|
|
235
|
+
// Bounds check
|
|
236
|
+
if (nextAddr >= 0xC000) return -1;
|
|
237
|
+
|
|
238
|
+
// Next-pointer (little-endian)
|
|
239
|
+
writeMem(addr, nextAddr & 0xFF);
|
|
240
|
+
writeMem(addr + 1, (nextAddr >> 8) & 0xFF);
|
|
241
|
+
|
|
242
|
+
// Line number (little-endian)
|
|
243
|
+
writeMem(addr + 2, line.lineNumber & 0xFF);
|
|
244
|
+
writeMem(addr + 3, (line.lineNumber >> 8) & 0xFF);
|
|
245
|
+
|
|
246
|
+
// Token bytes
|
|
247
|
+
for (size_t i = 0; i < line.tokens.size(); i++) {
|
|
248
|
+
writeMem(static_cast<uint16_t>(addr + 4 + i), line.tokens[i]);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Line terminator
|
|
252
|
+
writeMem(static_cast<uint16_t>(addr + 4 + line.tokens.size()), 0x00);
|
|
253
|
+
|
|
254
|
+
addr = nextAddr;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// End-of-program marker
|
|
258
|
+
writeMem(addr, 0x00);
|
|
259
|
+
writeMem(addr + 1, 0x00);
|
|
260
|
+
|
|
261
|
+
uint16_t endAddr = addr + 2;
|
|
262
|
+
|
|
263
|
+
// Read MEMSIZE ($73/$74) from memory
|
|
264
|
+
uint16_t memsize = readMem(0x73) | (readMem(0x74) << 8);
|
|
265
|
+
|
|
266
|
+
// Set zero page pointers
|
|
267
|
+
auto writePtr = [&](uint16_t zpAddr, uint16_t value) {
|
|
268
|
+
writeMem(zpAddr, value & 0xFF);
|
|
269
|
+
writeMem(zpAddr + 1, (value >> 8) & 0xFF);
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
writePtr(0x67, txttab); // TXTTAB - start of program
|
|
273
|
+
writePtr(0x69, endAddr); // VARTAB - start of variable space
|
|
274
|
+
writePtr(0x6B, endAddr); // ARYTAB - start of array space
|
|
275
|
+
writePtr(0x6D, endAddr); // STREND - end of numeric storage
|
|
276
|
+
writePtr(0x6F, memsize); // FRETOP - end of string storage
|
|
277
|
+
writePtr(0xAF, endAddr); // PRGEND - end of program
|
|
278
|
+
|
|
279
|
+
// Set interpreter state for direct mode
|
|
280
|
+
writePtr(0xB8, txttab - 1); // TXTPTR
|
|
281
|
+
writeMem(0x76, 0xFF); // CURLIN+1 high byte = $FF (direct mode)
|
|
282
|
+
|
|
283
|
+
return static_cast<int>(tokenizedLines.size());
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
} // namespace a2e
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* basic_tokenizer.hpp - Applesoft BASIC tokenizer for direct memory insertion
|
|
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 <functional>
|
|
13
|
+
|
|
14
|
+
namespace a2e {
|
|
15
|
+
|
|
16
|
+
// Callback types for reading/writing emulator memory
|
|
17
|
+
using MemReadFn = std::function<uint8_t(uint16_t)>;
|
|
18
|
+
using MemWriteFn = std::function<void(uint16_t, uint8_t)>;
|
|
19
|
+
|
|
20
|
+
// Tokenize BASIC source text and load it into emulator memory.
|
|
21
|
+
// Writes the tokenized program at TXTTAB (0x0801), sets zero page pointers.
|
|
22
|
+
// Uses callbacks to read/write memory (main RAM, bypassing ALTZP).
|
|
23
|
+
// Returns the number of lines loaded, or -1 on error.
|
|
24
|
+
int loadBasicProgram(const char* source, MemReadFn readMem, MemWriteFn writeMem);
|
|
25
|
+
|
|
26
|
+
} // namespace a2e
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* basic_tokens.hpp - Applesoft and Integer BASIC token tables and keyword helpers
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Mike Daley <michael_daley@icloud.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <cstring>
|
|
11
|
+
|
|
12
|
+
namespace a2e {
|
|
13
|
+
|
|
14
|
+
// Applesoft BASIC tokens - index = token byte - 0x80
|
|
15
|
+
static constexpr const char* APPLESOFT_TOKENS[] = {
|
|
16
|
+
"END", // $80
|
|
17
|
+
"FOR", // $81
|
|
18
|
+
"NEXT", // $82
|
|
19
|
+
"DATA", // $83
|
|
20
|
+
"INPUT", // $84
|
|
21
|
+
"DEL", // $85
|
|
22
|
+
"DIM", // $86
|
|
23
|
+
"READ", // $87
|
|
24
|
+
"GR", // $88
|
|
25
|
+
"TEXT", // $89
|
|
26
|
+
"PR#", // $8A
|
|
27
|
+
"IN#", // $8B
|
|
28
|
+
"CALL", // $8C
|
|
29
|
+
"PLOT", // $8D
|
|
30
|
+
"HLIN", // $8E
|
|
31
|
+
"VLIN", // $8F
|
|
32
|
+
"HGR2", // $90
|
|
33
|
+
"HGR", // $91
|
|
34
|
+
"HCOLOR=", // $92
|
|
35
|
+
"HPLOT", // $93
|
|
36
|
+
"DRAW", // $94
|
|
37
|
+
"XDRAW", // $95
|
|
38
|
+
"HTAB", // $96
|
|
39
|
+
"HOME", // $97
|
|
40
|
+
"ROT=", // $98
|
|
41
|
+
"SCALE=", // $99
|
|
42
|
+
"SHLOAD", // $9A
|
|
43
|
+
"TRACE", // $9B
|
|
44
|
+
"NOTRACE", // $9C
|
|
45
|
+
"NORMAL", // $9D
|
|
46
|
+
"INVERSE", // $9E
|
|
47
|
+
"FLASH", // $9F
|
|
48
|
+
"COLOR=", // $A0
|
|
49
|
+
"POP", // $A1
|
|
50
|
+
"VTAB", // $A2
|
|
51
|
+
"HIMEM:", // $A3
|
|
52
|
+
"LOMEM:", // $A4
|
|
53
|
+
"ONERR", // $A5
|
|
54
|
+
"RESUME", // $A6
|
|
55
|
+
"RECALL", // $A7
|
|
56
|
+
"STORE", // $A8
|
|
57
|
+
"SPEED=", // $A9
|
|
58
|
+
"LET", // $AA
|
|
59
|
+
"GOTO", // $AB
|
|
60
|
+
"RUN", // $AC
|
|
61
|
+
"IF", // $AD
|
|
62
|
+
"RESTORE", // $AE
|
|
63
|
+
"&", // $AF
|
|
64
|
+
"GOSUB", // $B0
|
|
65
|
+
"RETURN", // $B1
|
|
66
|
+
"REM", // $B2
|
|
67
|
+
"STOP", // $B3
|
|
68
|
+
"ON", // $B4
|
|
69
|
+
"WAIT", // $B5
|
|
70
|
+
"LOAD", // $B6
|
|
71
|
+
"SAVE", // $B7
|
|
72
|
+
"DEF", // $B8
|
|
73
|
+
"POKE", // $B9
|
|
74
|
+
"PRINT", // $BA
|
|
75
|
+
"CONT", // $BB
|
|
76
|
+
"LIST", // $BC
|
|
77
|
+
"CLEAR", // $BD
|
|
78
|
+
"GET", // $BE
|
|
79
|
+
"NEW", // $BF
|
|
80
|
+
"TAB(", // $C0
|
|
81
|
+
"TO", // $C1
|
|
82
|
+
"FN", // $C2
|
|
83
|
+
"SPC(", // $C3
|
|
84
|
+
"THEN", // $C4
|
|
85
|
+
"AT", // $C5
|
|
86
|
+
"NOT", // $C6
|
|
87
|
+
"STEP", // $C7
|
|
88
|
+
"+", // $C8
|
|
89
|
+
"-", // $C9
|
|
90
|
+
"*", // $CA
|
|
91
|
+
"/", // $CB
|
|
92
|
+
"^", // $CC
|
|
93
|
+
"AND", // $CD
|
|
94
|
+
"OR", // $CE
|
|
95
|
+
">", // $CF
|
|
96
|
+
"=", // $D0
|
|
97
|
+
"<", // $D1
|
|
98
|
+
"SGN", // $D2
|
|
99
|
+
"INT", // $D3
|
|
100
|
+
"ABS", // $D4
|
|
101
|
+
"USR", // $D5
|
|
102
|
+
"FRE", // $D6
|
|
103
|
+
"SCRN(", // $D7
|
|
104
|
+
"PDL", // $D8
|
|
105
|
+
"POS", // $D9
|
|
106
|
+
"SQR", // $DA
|
|
107
|
+
"RND", // $DB
|
|
108
|
+
"LOG", // $DC
|
|
109
|
+
"EXP", // $DD
|
|
110
|
+
"COS", // $DE
|
|
111
|
+
"SIN", // $DF
|
|
112
|
+
"TAN", // $E0
|
|
113
|
+
"ATN", // $E1
|
|
114
|
+
"PEEK", // $E2
|
|
115
|
+
"LEN", // $E3
|
|
116
|
+
"STR$", // $E4
|
|
117
|
+
"VAL", // $E5
|
|
118
|
+
"ASC", // $E6
|
|
119
|
+
"CHR$", // $E7
|
|
120
|
+
"LEFT$", // $E8
|
|
121
|
+
"RIGHT$", // $E9
|
|
122
|
+
"MID$", // $EA
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
static constexpr int APPLESOFT_TOKEN_COUNT = sizeof(APPLESOFT_TOKENS) / sizeof(APPLESOFT_TOKENS[0]);
|
|
126
|
+
|
|
127
|
+
// Integer BASIC tokens ($00-$7F)
|
|
128
|
+
static constexpr const char* INTEGER_BASIC_TOKENS[] = {
|
|
129
|
+
" HIMEM: ", // $00
|
|
130
|
+
nullptr, // $01 (end of line)
|
|
131
|
+
"_", // $02
|
|
132
|
+
":", // $03
|
|
133
|
+
" LOAD ", // $04
|
|
134
|
+
" SAVE ", // $05
|
|
135
|
+
" CON ", // $06
|
|
136
|
+
" RUN ", // $07
|
|
137
|
+
" RUN ", // $08
|
|
138
|
+
" DEL ", // $09
|
|
139
|
+
",", // $0A
|
|
140
|
+
" NEW ", // $0B
|
|
141
|
+
" CLR ", // $0C
|
|
142
|
+
" AUTO ", // $0D
|
|
143
|
+
",", // $0E
|
|
144
|
+
" MAN ", // $0F
|
|
145
|
+
" HIMEM: ", // $10
|
|
146
|
+
" LOMEM: ", // $11
|
|
147
|
+
"+", // $12
|
|
148
|
+
"-", // $13
|
|
149
|
+
"*", // $14
|
|
150
|
+
"/", // $15
|
|
151
|
+
"=", // $16
|
|
152
|
+
"#", // $17
|
|
153
|
+
">=", // $18
|
|
154
|
+
">", // $19
|
|
155
|
+
"<=", // $1A
|
|
156
|
+
"<>", // $1B
|
|
157
|
+
"<", // $1C
|
|
158
|
+
" AND ", // $1D
|
|
159
|
+
" OR ", // $1E
|
|
160
|
+
" MOD ", // $1F
|
|
161
|
+
"^", // $20
|
|
162
|
+
"+", // $21
|
|
163
|
+
"(", // $22
|
|
164
|
+
",", // $23
|
|
165
|
+
" THEN ", // $24
|
|
166
|
+
" THEN ", // $25
|
|
167
|
+
",", // $26
|
|
168
|
+
",", // $27
|
|
169
|
+
"\"", // $28 (start quote)
|
|
170
|
+
"\"", // $29 (end quote)
|
|
171
|
+
"(", // $2A
|
|
172
|
+
"!", // $2B
|
|
173
|
+
"!", // $2C
|
|
174
|
+
"(", // $2D
|
|
175
|
+
"PEEK", // $2E
|
|
176
|
+
"RND", // $2F
|
|
177
|
+
"SGN", // $30
|
|
178
|
+
"ABS", // $31
|
|
179
|
+
"PDL", // $32
|
|
180
|
+
"RNDX", // $33
|
|
181
|
+
"(", // $34
|
|
182
|
+
"+", // $35
|
|
183
|
+
"-", // $36
|
|
184
|
+
" NOT ", // $37
|
|
185
|
+
"(", // $38
|
|
186
|
+
"=", // $39
|
|
187
|
+
"#", // $3A
|
|
188
|
+
"LEN(", // $3B
|
|
189
|
+
"ASC(", // $3C
|
|
190
|
+
"SCRN(", // $3D
|
|
191
|
+
",", // $3E
|
|
192
|
+
"(", // $3F
|
|
193
|
+
"$", // $40
|
|
194
|
+
"$", // $41
|
|
195
|
+
"(", // $42
|
|
196
|
+
",", // $43
|
|
197
|
+
",", // $44
|
|
198
|
+
";", // $45
|
|
199
|
+
";", // $46
|
|
200
|
+
";", // $47
|
|
201
|
+
",", // $48
|
|
202
|
+
",", // $49
|
|
203
|
+
",", // $4A
|
|
204
|
+
" TEXT ", // $4B
|
|
205
|
+
" GR ", // $4C
|
|
206
|
+
" CALL ", // $4D
|
|
207
|
+
" DIM ", // $4E
|
|
208
|
+
" DIM ", // $4F
|
|
209
|
+
" TAB ", // $50
|
|
210
|
+
" END ", // $51
|
|
211
|
+
" INPUT ", // $52
|
|
212
|
+
" INPUT ", // $53
|
|
213
|
+
" INPUT ", // $54
|
|
214
|
+
" FOR ", // $55
|
|
215
|
+
"=", // $56
|
|
216
|
+
" TO ", // $57
|
|
217
|
+
" STEP ", // $58
|
|
218
|
+
" NEXT ", // $59
|
|
219
|
+
",", // $5A
|
|
220
|
+
" RETURN ", // $5B
|
|
221
|
+
" GOSUB ", // $5C
|
|
222
|
+
" REM ", // $5D
|
|
223
|
+
" LET ", // $5E
|
|
224
|
+
" GOTO ", // $5F
|
|
225
|
+
" IF ", // $60
|
|
226
|
+
" PRINT ", // $61
|
|
227
|
+
" PRINT ", // $62
|
|
228
|
+
" PRINT ", // $63
|
|
229
|
+
" POKE ", // $64
|
|
230
|
+
",", // $65
|
|
231
|
+
" COLOR= ", // $66
|
|
232
|
+
" PLOT ", // $67
|
|
233
|
+
",", // $68
|
|
234
|
+
" HLIN ", // $69
|
|
235
|
+
",", // $6A
|
|
236
|
+
" AT ", // $6B
|
|
237
|
+
" VLIN ", // $6C
|
|
238
|
+
",", // $6D
|
|
239
|
+
" AT ", // $6E
|
|
240
|
+
" VTAB ", // $6F
|
|
241
|
+
"=", // $70
|
|
242
|
+
"=", // $71
|
|
243
|
+
")", // $72
|
|
244
|
+
")", // $73
|
|
245
|
+
" LIST ", // $74
|
|
246
|
+
",", // $75
|
|
247
|
+
" LIST ", // $76
|
|
248
|
+
" POP ", // $77
|
|
249
|
+
" NODSP ", // $78
|
|
250
|
+
" DSP ", // $79
|
|
251
|
+
" NOTRACE ", // $7A
|
|
252
|
+
" DSP ", // $7B
|
|
253
|
+
" DSP ", // $7C
|
|
254
|
+
" TRACE ", // $7D
|
|
255
|
+
" PR# ", // $7E
|
|
256
|
+
" IN# ", // $7F
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
static constexpr int INTEGER_BASIC_TOKEN_COUNT = 0x80;
|
|
260
|
+
|
|
261
|
+
// Keywords that need space before them (Applesoft)
|
|
262
|
+
inline bool needsSpaceBefore(const char* token) {
|
|
263
|
+
// Check common keywords
|
|
264
|
+
const char* keywords[] = {
|
|
265
|
+
"FOR", "NEXT", "DATA", "INPUT", "DIM", "READ", "GR", "TEXT",
|
|
266
|
+
"CALL", "PLOT", "HLIN", "VLIN", "HGR2", "HGR", "HPLOT",
|
|
267
|
+
"DRAW", "XDRAW", "HTAB", "HOME", "SHLOAD", "TRACE", "NOTRACE",
|
|
268
|
+
"NORMAL", "INVERSE", "FLASH", "POP", "VTAB", "ONERR", "RESUME",
|
|
269
|
+
"RECALL", "STORE", "LET", "GOTO", "RUN", "IF", "RESTORE",
|
|
270
|
+
"GOSUB", "RETURN", "REM", "STOP", "ON", "WAIT", "LOAD", "SAVE",
|
|
271
|
+
"DEF", "POKE", "PRINT", "CONT", "LIST", "CLEAR", "GET", "NEW",
|
|
272
|
+
"TO", "FN", "THEN", "AT", "NOT", "STEP", "AND", "OR", "END",
|
|
273
|
+
};
|
|
274
|
+
for (const char* kw : keywords) {
|
|
275
|
+
if (strcmp(token, kw) == 0) return true;
|
|
276
|
+
}
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Keywords that need space after them (Applesoft)
|
|
281
|
+
inline bool needsSpaceAfter(const char* token) {
|
|
282
|
+
const char* keywords[] = {
|
|
283
|
+
"GOTO", "GOSUB", "THEN", "TO", "STEP", "AND", "OR", "NOT",
|
|
284
|
+
"IF", "ON", "LET", "FOR", "NEXT", "PRINT", "INPUT", "READ",
|
|
285
|
+
"DATA", "DIM", "DEF", "POKE", "CALL", "PLOT", "HLIN", "VLIN",
|
|
286
|
+
"HPLOT", "DRAW", "XDRAW", "HTAB", "VTAB", "ONERR", "WAIT",
|
|
287
|
+
"GET", "AT", "FN",
|
|
288
|
+
};
|
|
289
|
+
for (const char* kw : keywords) {
|
|
290
|
+
if (strcmp(token, kw) == 0) return true;
|
|
291
|
+
}
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
} // namespace a2e
|