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,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless Disk Boot Test
|
|
3
|
+
*
|
|
4
|
+
* This test loads the DOS 3.3 disk image and runs the emulator for enough
|
|
5
|
+
* cycles to boot, then checks if "DOS" appears in screen memory.
|
|
6
|
+
*
|
|
7
|
+
* Apple II text screen memory is at $0400-$07FF
|
|
8
|
+
* The screen is 40x24 characters, but the memory is not linear.
|
|
9
|
+
*
|
|
10
|
+
* Run with: node test/disk-boot-test.js
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { readFileSync } from 'fs';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
import { dirname, join, resolve } from 'path';
|
|
16
|
+
import { createContext, runInContext } from 'vm';
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = dirname(__filename);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Load the WASM module using vm context (needed because of ES module compatibility)
|
|
23
|
+
*/
|
|
24
|
+
async function loadWasmModule() {
|
|
25
|
+
const wasmJsPath = join(__dirname, '..', 'public', 'a2e.js');
|
|
26
|
+
const wasmDir = dirname(wasmJsPath);
|
|
27
|
+
|
|
28
|
+
const code = readFileSync(wasmJsPath, 'utf8');
|
|
29
|
+
|
|
30
|
+
// Create a context with the necessary globals for the WASM module
|
|
31
|
+
const context = {
|
|
32
|
+
module: { exports: {} },
|
|
33
|
+
exports: {},
|
|
34
|
+
globalThis: global,
|
|
35
|
+
console: console,
|
|
36
|
+
require: (await import('module')).createRequire(import.meta.url),
|
|
37
|
+
__dirname: wasmDir,
|
|
38
|
+
__filename: wasmJsPath,
|
|
39
|
+
process: process,
|
|
40
|
+
WebAssembly: WebAssembly,
|
|
41
|
+
URL: URL,
|
|
42
|
+
TextDecoder: TextDecoder,
|
|
43
|
+
setTimeout: setTimeout,
|
|
44
|
+
clearTimeout: clearTimeout,
|
|
45
|
+
performance: { now: () => Date.now() }
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
createContext(context);
|
|
49
|
+
runInContext(code, context);
|
|
50
|
+
|
|
51
|
+
return context.module.exports;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function runTest() {
|
|
55
|
+
console.log('Loading WASM module...');
|
|
56
|
+
|
|
57
|
+
const createA2EModule = await loadWasmModule();
|
|
58
|
+
|
|
59
|
+
if (typeof createA2EModule !== 'function') {
|
|
60
|
+
console.error('Failed to load WASM module factory');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Create the module instance
|
|
65
|
+
const Module = await createA2EModule();
|
|
66
|
+
|
|
67
|
+
console.log('Initializing emulator...');
|
|
68
|
+
Module._init();
|
|
69
|
+
|
|
70
|
+
// Load the disk image
|
|
71
|
+
const diskPath = join(__dirname, '..', 'public', 'Apple DOS 3.3 August 1980.dsk');
|
|
72
|
+
console.log(`Loading disk image: ${diskPath}`);
|
|
73
|
+
|
|
74
|
+
const diskData = readFileSync(diskPath);
|
|
75
|
+
console.log(`Disk size: ${diskData.length} bytes`);
|
|
76
|
+
|
|
77
|
+
// Allocate memory for disk data
|
|
78
|
+
const diskPtr = Module._malloc(diskData.length);
|
|
79
|
+
Module.HEAPU8.set(diskData, diskPtr);
|
|
80
|
+
|
|
81
|
+
// Allocate memory for filename
|
|
82
|
+
const filename = 'Apple DOS 3.3 August 1980.dsk';
|
|
83
|
+
const filenamePtr = Module._malloc(filename.length + 1);
|
|
84
|
+
Module.stringToUTF8(filename, filenamePtr, filename.length + 1);
|
|
85
|
+
|
|
86
|
+
// Insert the disk
|
|
87
|
+
const inserted = Module._insertDisk(0, diskPtr, diskData.length, filenamePtr);
|
|
88
|
+
console.log(`Disk inserted: ${inserted}`);
|
|
89
|
+
|
|
90
|
+
// Free the filename memory
|
|
91
|
+
Module._free(filenamePtr);
|
|
92
|
+
|
|
93
|
+
if (!inserted) {
|
|
94
|
+
console.error('Failed to insert disk!');
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Run emulation for enough cycles to boot
|
|
99
|
+
// DOS 3.3 boot takes about 2-3 seconds at 1.023 MHz
|
|
100
|
+
// That's roughly 2-3 million cycles
|
|
101
|
+
const CYCLES_PER_BATCH = 100000;
|
|
102
|
+
const MAX_BATCHES = 200; // ~20 million cycles (about 20 seconds of emulation)
|
|
103
|
+
|
|
104
|
+
console.log('Running emulation...');
|
|
105
|
+
|
|
106
|
+
let lastTrack = -1;
|
|
107
|
+
let lastByte = 0;
|
|
108
|
+
let readCount = 0;
|
|
109
|
+
|
|
110
|
+
for (let batch = 0; batch < MAX_BATCHES; batch++) {
|
|
111
|
+
Module._runCycles(CYCLES_PER_BATCH);
|
|
112
|
+
|
|
113
|
+
// Check disk state
|
|
114
|
+
const track = Module._getDiskTrack(0);
|
|
115
|
+
const motorOn = Module._getDiskMotorOn(0);
|
|
116
|
+
const lastDiskByte = Module._getLastDiskByte();
|
|
117
|
+
|
|
118
|
+
if (track !== lastTrack) {
|
|
119
|
+
console.log(`\n Track changed: ${lastTrack} -> ${track}`);
|
|
120
|
+
lastTrack = track;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (lastDiskByte !== lastByte) {
|
|
124
|
+
readCount++;
|
|
125
|
+
if (readCount <= 20) {
|
|
126
|
+
console.log(` Disk byte: $${lastDiskByte.toString(16).toUpperCase().padStart(2, '0')} (track ${track}, motor: ${motorOn})`);
|
|
127
|
+
}
|
|
128
|
+
lastByte = lastDiskByte;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check for DOS in screen memory every batch
|
|
132
|
+
const found = checkForDOS(Module);
|
|
133
|
+
if (found) {
|
|
134
|
+
const totalCycles = Module._getTotalCycles();
|
|
135
|
+
console.log(`\nSUCCESS: Found "DOS" in screen memory after ${totalCycles} cycles!`);
|
|
136
|
+
dumpScreenMemory(Module);
|
|
137
|
+
Module._free(diskPtr);
|
|
138
|
+
process.exit(0);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Progress indicator
|
|
142
|
+
if (batch % 10 === 0) {
|
|
143
|
+
const totalCycles = Module._getTotalCycles();
|
|
144
|
+
const pc = Module._getPC();
|
|
145
|
+
process.stdout.write(`\rCycles: ${totalCycles}, PC: $${pc.toString(16).toUpperCase().padStart(4, '0')}, Track: ${track}, Motor: ${motorOn}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.log('\n\nFAILED: Did not find "DOS" in screen memory');
|
|
150
|
+
console.log('\nScreen memory dump:');
|
|
151
|
+
dumpScreenMemory(Module);
|
|
152
|
+
|
|
153
|
+
// Also dump some debug info
|
|
154
|
+
console.log('\nDebug info:');
|
|
155
|
+
console.log(`PC: $${Module._getPC().toString(16).toUpperCase().padStart(4, '0')}`);
|
|
156
|
+
console.log(`A: $${Module._getA().toString(16).toUpperCase().padStart(2, '0')}`);
|
|
157
|
+
console.log(`X: $${Module._getX().toString(16).toUpperCase().padStart(2, '0')}`);
|
|
158
|
+
console.log(`Y: $${Module._getY().toString(16).toUpperCase().padStart(2, '0')}`);
|
|
159
|
+
console.log(`SP: $${Module._getSP().toString(16).toUpperCase().padStart(2, '0')}`);
|
|
160
|
+
console.log(`Total cycles: ${Module._getTotalCycles()}`);
|
|
161
|
+
|
|
162
|
+
// Dump zero page
|
|
163
|
+
console.log('\nZero page ($00-$3F):');
|
|
164
|
+
let zpLine = '';
|
|
165
|
+
for (let i = 0; i < 0x40; i++) {
|
|
166
|
+
if (i % 16 === 0) {
|
|
167
|
+
if (zpLine) console.log(zpLine);
|
|
168
|
+
zpLine = `$${i.toString(16).toUpperCase().padStart(2, '0')}: `;
|
|
169
|
+
}
|
|
170
|
+
zpLine += Module._readMemory(i).toString(16).toUpperCase().padStart(2, '0') + ' ';
|
|
171
|
+
}
|
|
172
|
+
console.log(zpLine);
|
|
173
|
+
|
|
174
|
+
// Dump boot sector area ($0800-$08FF)
|
|
175
|
+
console.log('\nBoot sector memory ($0800-$08FF):');
|
|
176
|
+
for (let row = 0; row < 16; row++) {
|
|
177
|
+
const addr = 0x0800 + row * 16;
|
|
178
|
+
let line = `$${addr.toString(16).toUpperCase().padStart(4, '0')}: `;
|
|
179
|
+
let ascii = '';
|
|
180
|
+
for (let col = 0; col < 16; col++) {
|
|
181
|
+
const byte = Module._readMemory(addr + col);
|
|
182
|
+
line += byte.toString(16).toUpperCase().padStart(2, '0') + ' ';
|
|
183
|
+
ascii += (byte >= 0x20 && byte < 0x7F) ? String.fromCharCode(byte) : '.';
|
|
184
|
+
}
|
|
185
|
+
console.log(line + ' ' + ascii);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
Module._free(diskPtr);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Check if "DOS" appears in screen memory
|
|
194
|
+
* Apple II screen memory is at $0400-$07FF
|
|
195
|
+
*/
|
|
196
|
+
function checkForDOS(Module) {
|
|
197
|
+
// Apple II character codes for "DOS" (high bit may be set)
|
|
198
|
+
// D = 0x44 or 0xC4, O = 0x4F or 0xCF, S = 0x53 or 0xD3
|
|
199
|
+
|
|
200
|
+
// Scan all of screen memory
|
|
201
|
+
for (let addr = 0x0400; addr < 0x0800 - 2; addr++) {
|
|
202
|
+
const c1 = Module._readMemory(addr) & 0x7F;
|
|
203
|
+
const c2 = Module._readMemory(addr + 1) & 0x7F;
|
|
204
|
+
const c3 = Module._readMemory(addr + 2) & 0x7F;
|
|
205
|
+
|
|
206
|
+
if (c1 === 0x44 && c2 === 0x4F && c3 === 0x53) { // "DOS"
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Dump screen memory as text
|
|
216
|
+
*/
|
|
217
|
+
function dumpScreenMemory(Module) {
|
|
218
|
+
// Apple II screen memory layout is complex
|
|
219
|
+
// Each row is split into 3 groups of 8 rows
|
|
220
|
+
const rowBases = [
|
|
221
|
+
0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
|
|
222
|
+
0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
|
|
223
|
+
0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
console.log('┌────────────────────────────────────────┐');
|
|
227
|
+
|
|
228
|
+
for (let row = 0; row < 24; row++) {
|
|
229
|
+
let line = '│';
|
|
230
|
+
const base = rowBases[row];
|
|
231
|
+
|
|
232
|
+
for (let col = 0; col < 40; col++) {
|
|
233
|
+
const char = Module._readMemory(base + col);
|
|
234
|
+
const ascii = appleCharToAscii(char);
|
|
235
|
+
line += ascii;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
console.log(line + '│');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
console.log('└────────────────────────────────────────┘');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Convert Apple II character code to ASCII
|
|
246
|
+
*/
|
|
247
|
+
function appleCharToAscii(char) {
|
|
248
|
+
const c = char & 0x7F;
|
|
249
|
+
|
|
250
|
+
if (c >= 0x20 && c < 0x60) {
|
|
251
|
+
return String.fromCharCode(c);
|
|
252
|
+
} else if (c >= 0x00 && c < 0x20) {
|
|
253
|
+
return String.fromCharCode(c + 0x40);
|
|
254
|
+
} else if (c >= 0x60) {
|
|
255
|
+
return String.fromCharCode(c);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return ' ';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
runTest().catch(err => {
|
|
262
|
+
console.error('Test error:', err);
|
|
263
|
+
process.exit(1);
|
|
264
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
import { createContext, runInContext } from 'vm';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
|
|
9
|
+
async function loadWasmModule() {
|
|
10
|
+
const wasmJsPath = join(__dirname, '..', 'public', 'a2e.js');
|
|
11
|
+
const wasmDir = dirname(wasmJsPath);
|
|
12
|
+
const code = readFileSync(wasmJsPath, 'utf8');
|
|
13
|
+
const context = {
|
|
14
|
+
module: { exports: {} },
|
|
15
|
+
exports: {},
|
|
16
|
+
globalThis: global,
|
|
17
|
+
console: console,
|
|
18
|
+
require: (await import('module')).createRequire(import.meta.url),
|
|
19
|
+
__dirname: wasmDir,
|
|
20
|
+
__filename: wasmJsPath,
|
|
21
|
+
process: process,
|
|
22
|
+
WebAssembly: WebAssembly,
|
|
23
|
+
URL: URL,
|
|
24
|
+
TextDecoder: TextDecoder,
|
|
25
|
+
setTimeout: setTimeout,
|
|
26
|
+
clearTimeout: clearTimeout,
|
|
27
|
+
performance: { now: () => Date.now() }
|
|
28
|
+
};
|
|
29
|
+
createContext(context);
|
|
30
|
+
runInContext(code, context);
|
|
31
|
+
return context.module.exports;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function runTest() {
|
|
35
|
+
const createA2EModule = await loadWasmModule();
|
|
36
|
+
const Module = await createA2EModule();
|
|
37
|
+
Module._init();
|
|
38
|
+
|
|
39
|
+
const diskPath = join(__dirname, '..', 'public', 'Apple DOS 3.3 August 1980.dsk');
|
|
40
|
+
const diskData = readFileSync(diskPath);
|
|
41
|
+
const diskPtr = Module._malloc(diskData.length);
|
|
42
|
+
Module.HEAPU8.set(diskData, diskPtr);
|
|
43
|
+
const filename = 'test.dsk';
|
|
44
|
+
const filenamePtr = Module._malloc(filename.length + 1);
|
|
45
|
+
Module.stringToUTF8(filename, filenamePtr, filename.length + 1);
|
|
46
|
+
Module._insertDisk(0, diskPtr, diskData.length, filenamePtr);
|
|
47
|
+
Module._free(filenamePtr);
|
|
48
|
+
|
|
49
|
+
console.log('=== MEMORY STATE TEST ===\n');
|
|
50
|
+
|
|
51
|
+
// Check ROM at startup
|
|
52
|
+
console.log('At startup:');
|
|
53
|
+
console.log(' $FE89 = $' + Module._readMemory(0xFE89).toString(16).toUpperCase());
|
|
54
|
+
console.log(' $FE8A = $' + Module._readMemory(0xFE8A).toString(16).toUpperCase());
|
|
55
|
+
console.log(' $FE8B = $' + Module._readMemory(0xFE8B).toString(16).toUpperCase());
|
|
56
|
+
console.log(' Soft switches: 0x' + Module._getSoftSwitchState().toString(16).toUpperCase());
|
|
57
|
+
|
|
58
|
+
// Run to near crash point
|
|
59
|
+
console.log('\nRunning to 4,050,000 cycles...');
|
|
60
|
+
while (Module._getTotalCycles() < 4050000) {
|
|
61
|
+
Module._runCycles(1000);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Check memory state just before crash
|
|
65
|
+
console.log('\nAt cycle ' + Module._getTotalCycles() + ':');
|
|
66
|
+
console.log(' $FE89 = $' + Module._readMemory(0xFE89).toString(16).toUpperCase());
|
|
67
|
+
console.log(' $FE8A = $' + Module._readMemory(0xFE8A).toString(16).toUpperCase());
|
|
68
|
+
console.log(' $FE8B = $' + Module._readMemory(0xFE8B).toString(16).toUpperCase());
|
|
69
|
+
console.log(' Soft switches: 0x' + Module._getSoftSwitchState().toString(16).toUpperCase());
|
|
70
|
+
|
|
71
|
+
// Step until crash
|
|
72
|
+
for (let i = 0; i < 500000; i++) {
|
|
73
|
+
const pc = Module._getPC();
|
|
74
|
+
|
|
75
|
+
// Check memory periodically
|
|
76
|
+
if (i % 10000 === 0) {
|
|
77
|
+
const fe89 = Module._readMemory(0xFE89);
|
|
78
|
+
if (fe89 !== 0xA9) {
|
|
79
|
+
console.log('\n*** $FE89 changed from $A9 to $' + fe89.toString(16).toUpperCase() + ' at cycle ' + Module._getTotalCycles() + ' ***');
|
|
80
|
+
console.log(' PC = $' + pc.toString(16).toUpperCase());
|
|
81
|
+
console.log(' Soft switches: 0x' + Module._getSoftSwitchState().toString(16).toUpperCase());
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
Module._stepInstruction();
|
|
86
|
+
|
|
87
|
+
const newPC = Module._getPC();
|
|
88
|
+
if (newPC === 0) {
|
|
89
|
+
console.log('\n*** CRASH at cycle ' + Module._getTotalCycles() + ' ***');
|
|
90
|
+
console.log(' Last PC was $' + pc.toString(16).toUpperCase());
|
|
91
|
+
console.log(' $FE89 at crash: $' + Module._readMemory(0xFE89).toString(16).toUpperCase());
|
|
92
|
+
console.log(' Soft switches at crash: 0x' + Module._getSoftSwitchState().toString(16).toUpperCase());
|
|
93
|
+
|
|
94
|
+
// Check what JSR target would be
|
|
95
|
+
if (pc >= 0x3740 && pc <= 0x3750) {
|
|
96
|
+
console.log(' Memory at $3744-$3746: $' +
|
|
97
|
+
Module._readMemory(0x3744).toString(16).padStart(2,'0') + ' ' +
|
|
98
|
+
Module._readMemory(0x3745).toString(16).padStart(2,'0') + ' ' +
|
|
99
|
+
Module._readMemory(0x3746).toString(16).padStart(2,'0'));
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
Module._free(diskPtr);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
runTest().catch(err => { console.error(err); process.exit(1); });
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nibble Read Test - Track actual disk reads during boot
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { readFileSync } from 'fs';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import { dirname, join } from 'path';
|
|
8
|
+
import { createContext, runInContext } from 'vm';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
|
|
13
|
+
async function loadWasmModule() {
|
|
14
|
+
const wasmJsPath = join(__dirname, '..', 'public', 'a2e.js');
|
|
15
|
+
const wasmDir = dirname(wasmJsPath);
|
|
16
|
+
const code = readFileSync(wasmJsPath, 'utf8');
|
|
17
|
+
|
|
18
|
+
const context = {
|
|
19
|
+
module: { exports: {} },
|
|
20
|
+
exports: {},
|
|
21
|
+
globalThis: global,
|
|
22
|
+
console: console,
|
|
23
|
+
require: (await import('module')).createRequire(import.meta.url),
|
|
24
|
+
__dirname: wasmDir,
|
|
25
|
+
__filename: wasmJsPath,
|
|
26
|
+
process: process,
|
|
27
|
+
WebAssembly: WebAssembly,
|
|
28
|
+
URL: URL,
|
|
29
|
+
TextDecoder: TextDecoder,
|
|
30
|
+
setTimeout: setTimeout,
|
|
31
|
+
clearTimeout: clearTimeout,
|
|
32
|
+
performance: { now: () => Date.now() }
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
createContext(context);
|
|
36
|
+
runInContext(code, context);
|
|
37
|
+
return context.module.exports;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function runTest() {
|
|
41
|
+
console.log('Loading WASM module...');
|
|
42
|
+
const createA2EModule = await loadWasmModule();
|
|
43
|
+
const Module = await createA2EModule();
|
|
44
|
+
|
|
45
|
+
console.log('Initializing emulator...');
|
|
46
|
+
Module._init();
|
|
47
|
+
|
|
48
|
+
// Load disk
|
|
49
|
+
const diskPath = join(__dirname, '..', 'public', 'Apple DOS 3.3 August 1980.dsk');
|
|
50
|
+
const diskData = readFileSync(diskPath);
|
|
51
|
+
const diskPtr = Module._malloc(diskData.length);
|
|
52
|
+
Module.HEAPU8.set(diskData, diskPtr);
|
|
53
|
+
const filename = 'test.dsk';
|
|
54
|
+
const filenamePtr = Module._malloc(filename.length + 1);
|
|
55
|
+
Module.stringToUTF8(filename, filenamePtr, filename.length + 1);
|
|
56
|
+
Module._insertDisk(0, diskPtr, diskData.length, filenamePtr);
|
|
57
|
+
Module._free(filenamePtr);
|
|
58
|
+
|
|
59
|
+
// Get track 0 nibbles for comparison
|
|
60
|
+
const track0Nibbles = [];
|
|
61
|
+
const count = Module._getTrackNibbleCount(0, 0);
|
|
62
|
+
for (let i = 0; i < count; i++) {
|
|
63
|
+
track0Nibbles.push(Module._getTrackNibble(0, 0, i));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log('Track 0 has ' + count + ' nibbles');
|
|
67
|
+
|
|
68
|
+
// Find first sync run and address prologue
|
|
69
|
+
let syncEnd = 0;
|
|
70
|
+
for (let i = 0; i < count; i++) {
|
|
71
|
+
if (track0Nibbles[i] !== 0xFF) {
|
|
72
|
+
syncEnd = i;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
console.log('Sync bytes end at position ' + syncEnd);
|
|
77
|
+
|
|
78
|
+
let nibblesStr = '';
|
|
79
|
+
for (let i = syncEnd; i < syncEnd + 10; i++) {
|
|
80
|
+
nibblesStr += track0Nibbles[i].toString(16).toUpperCase().padStart(2, '0') + ' ';
|
|
81
|
+
}
|
|
82
|
+
console.log('Nibbles at sync end: ' + nibblesStr);
|
|
83
|
+
|
|
84
|
+
// Run to disk boot ROM
|
|
85
|
+
console.log('\nRunning to disk boot ROM...');
|
|
86
|
+
for (let i = 0; i < 2000; i++) {
|
|
87
|
+
Module._runCycles(1000);
|
|
88
|
+
const pc = Module._getPC();
|
|
89
|
+
if (pc >= 0xC600 && pc < 0xC700) {
|
|
90
|
+
console.log('Entered disk boot ROM at $' + pc.toString(16).toUpperCase() + ' after ' + Module._getTotalCycles() + ' cycles');
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Track nibble reads during boot until screen text appears
|
|
96
|
+
console.log('\nTracking disk reads during boot (waiting for screen text)...');
|
|
97
|
+
let lastLatch = 0;
|
|
98
|
+
let lastPos = Module._getCurrentNibblePosition(0);
|
|
99
|
+
let lastTrack = Module._getDiskTrack(0);
|
|
100
|
+
const readLog = [];
|
|
101
|
+
let crashPC = 0;
|
|
102
|
+
let screenTextFound = false;
|
|
103
|
+
|
|
104
|
+
// Helper to check if screen has "DOS" text (indicates disk boot success)
|
|
105
|
+
function hasDOSText() {
|
|
106
|
+
// Look for "DOS" in screen memory
|
|
107
|
+
for (let addr = 0x0400; addr < 0x0800 - 2; addr++) {
|
|
108
|
+
const c1 = Module._readMemory(addr) & 0x7F;
|
|
109
|
+
const c2 = Module._readMemory(addr + 1) & 0x7F;
|
|
110
|
+
const c3 = Module._readMemory(addr + 2) & 0x7F;
|
|
111
|
+
if (c1 === 0x44 && c2 === 0x4F && c3 === 0x53) { // "DOS"
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Helper to dump screen contents
|
|
119
|
+
function dumpScreen() {
|
|
120
|
+
const rowBases = [
|
|
121
|
+
0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
|
|
122
|
+
0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
|
|
123
|
+
0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
console.log('Screen contents:');
|
|
127
|
+
for (let row = 0; row < 24; row++) {
|
|
128
|
+
let line = '';
|
|
129
|
+
const base = rowBases[row];
|
|
130
|
+
for (let col = 0; col < 40; col++) {
|
|
131
|
+
const c = Module._readMemory(base + col) & 0x7F;
|
|
132
|
+
if (c >= 0x20 && c < 0x7F) {
|
|
133
|
+
line += String.fromCharCode(c);
|
|
134
|
+
} else if (c >= 0x00 && c < 0x20) {
|
|
135
|
+
line += String.fromCharCode(c + 0x40);
|
|
136
|
+
} else {
|
|
137
|
+
line += ' ';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
console.log('|' + line + '|');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const MAX_BATCHES = 50000; // Run for much longer
|
|
145
|
+
for (let batch = 0; batch < MAX_BATCHES; batch++) {
|
|
146
|
+
const pcBefore = Module._getPC();
|
|
147
|
+
Module._runCycles(100);
|
|
148
|
+
const pcAfter = Module._getPC();
|
|
149
|
+
|
|
150
|
+
const pos = Module._getCurrentNibblePosition(0);
|
|
151
|
+
const latch = Module._getLastDiskByte(0);
|
|
152
|
+
const track = Module._getDiskTrack(0);
|
|
153
|
+
const motor = Module._getDiskMotorOn(0);
|
|
154
|
+
|
|
155
|
+
// Detect crash
|
|
156
|
+
if (pcAfter === 0 && pcBefore !== 0 && pcBefore !== 0xFFFC) {
|
|
157
|
+
console.log('\n*** CRASH DETECTED at cycle ' + Module._getTotalCycles() + ' ***');
|
|
158
|
+
console.log('Previous PC: $' + pcBefore.toString(16).toUpperCase());
|
|
159
|
+
console.log('Current Track: ' + track + ', Motor: ' + motor);
|
|
160
|
+
dumpScreen();
|
|
161
|
+
crashPC = pcBefore;
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Track nibble reads
|
|
166
|
+
if (latch !== lastLatch && motor) {
|
|
167
|
+
if (readLog.length < 500) {
|
|
168
|
+
// Track 0 comparison
|
|
169
|
+
let expectedNibble = 0;
|
|
170
|
+
if (track === 0) {
|
|
171
|
+
// Get what nibble should be at the PREVIOUS position (since readNibble advances)
|
|
172
|
+
const readPos = (pos - 1 + count) % count;
|
|
173
|
+
expectedNibble = track0Nibbles[readPos] | 0x80;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
readLog.push({
|
|
177
|
+
cycle: Module._getTotalCycles(),
|
|
178
|
+
track: track,
|
|
179
|
+
pos: pos,
|
|
180
|
+
readPos: (pos - 1 + count) % count,
|
|
181
|
+
latch: latch,
|
|
182
|
+
expected: expectedNibble
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
lastLatch = latch;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
lastPos = pos;
|
|
189
|
+
lastTrack = track;
|
|
190
|
+
|
|
191
|
+
// Check for DOS text every 1000 batches
|
|
192
|
+
if (batch % 1000 === 0) {
|
|
193
|
+
if (hasDOSText()) {
|
|
194
|
+
console.log('\nDOS text detected after ' + Module._getTotalCycles() + ' cycles!');
|
|
195
|
+
dumpScreen();
|
|
196
|
+
screenTextFound = true;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
// Progress indicator
|
|
200
|
+
process.stdout.write('\rCycles: ' + Module._getTotalCycles() + ', Track: ' + track);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (!screenTextFound && crashPC === 0) {
|
|
205
|
+
console.log('\nTimeout: No screen text after ' + Module._getTotalCycles() + ' cycles');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
console.log('\nCaptured ' + readLog.length + ' disk byte changes');
|
|
209
|
+
|
|
210
|
+
// Show first 30 reads
|
|
211
|
+
console.log('\nFirst 30 disk byte changes:');
|
|
212
|
+
for (let i = 0; i < Math.min(30, readLog.length); i++) {
|
|
213
|
+
const r = readLog[i];
|
|
214
|
+
let status = '';
|
|
215
|
+
if (r.track === 0 && r.expected !== 0) {
|
|
216
|
+
status = r.latch === r.expected ? ' OK' : ' MISMATCH (expected $' + r.expected.toString(16).toUpperCase() + ')';
|
|
217
|
+
}
|
|
218
|
+
console.log(' cycle=' + r.cycle.toString().padStart(8) + ' track=' + r.track + ' pos=' + r.readPos.toString().padStart(4) + ' latch=$' + r.latch.toString(16).toUpperCase().padStart(2, '0') + status);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Check for mismatches on track 0
|
|
222
|
+
const track0Reads = readLog.filter(r => r.track === 0 && r.expected !== 0);
|
|
223
|
+
const mismatches = track0Reads.filter(r => r.latch !== r.expected);
|
|
224
|
+
|
|
225
|
+
if (mismatches.length > 0) {
|
|
226
|
+
console.log('\n*** Found ' + mismatches.length + ' mismatches on track 0! ***');
|
|
227
|
+
for (let i = 0; i < Math.min(10, mismatches.length); i++) {
|
|
228
|
+
const m = mismatches[i];
|
|
229
|
+
console.log(' pos=' + m.readPos + ': expected=$' + m.expected.toString(16).toUpperCase() + ' actual=$' + m.latch.toString(16).toUpperCase());
|
|
230
|
+
}
|
|
231
|
+
} else if (track0Reads.length > 0) {
|
|
232
|
+
console.log('\nAll ' + track0Reads.length + ' track 0 nibble reads matched!');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Look for patterns in the read positions
|
|
236
|
+
console.log('\nNibble position progression (first 50):');
|
|
237
|
+
const positions = [];
|
|
238
|
+
for (let i = 0; i < Math.min(50, readLog.length); i++) {
|
|
239
|
+
positions.push(readLog[i].readPos);
|
|
240
|
+
}
|
|
241
|
+
console.log(positions.join(' -> '));
|
|
242
|
+
|
|
243
|
+
Module._free(diskPtr);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
runTest().catch(err => {
|
|
247
|
+
console.error('Error:', err);
|
|
248
|
+
process.exit(1);
|
|
249
|
+
});
|