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,319 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* test_video.cpp - Unit tests for Video output generation
|
|
3
|
+
*
|
|
4
|
+
* Tests the video subsystem including mode detection, framebuffer
|
|
5
|
+
* management, display options, page switching, text rendering,
|
|
6
|
+
* and dirty flag management.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#define CATCH_CONFIG_MAIN
|
|
10
|
+
#include "catch.hpp"
|
|
11
|
+
|
|
12
|
+
#include "video/video.hpp"
|
|
13
|
+
#include "mmu/mmu.hpp"
|
|
14
|
+
#include "roms.cpp"
|
|
15
|
+
|
|
16
|
+
using namespace a2e;
|
|
17
|
+
|
|
18
|
+
// Helper: create an MMU with ROMs loaded and a Video instance
|
|
19
|
+
struct VideoTestFixture {
|
|
20
|
+
MMU mmu;
|
|
21
|
+
std::unique_ptr<Video> video;
|
|
22
|
+
|
|
23
|
+
VideoTestFixture() {
|
|
24
|
+
mmu.loadROM(roms::ROM_SYSTEM, roms::ROM_SYSTEM_SIZE,
|
|
25
|
+
roms::ROM_CHAR, roms::ROM_CHAR_SIZE);
|
|
26
|
+
video = std::make_unique<Video>(mmu);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Default state
|
|
32
|
+
// ============================================================================
|
|
33
|
+
|
|
34
|
+
TEST_CASE("Video default mode is TEXT_40", "[video][mode]") {
|
|
35
|
+
VideoTestFixture f;
|
|
36
|
+
|
|
37
|
+
// Default soft switches: text=true, col80=false
|
|
38
|
+
CHECK(f.video->getCurrentMode() == VideoMode::TEXT_40);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// Framebuffer
|
|
43
|
+
// ============================================================================
|
|
44
|
+
|
|
45
|
+
TEST_CASE("Video getFramebufferSize returns 860160", "[video][framebuffer]") {
|
|
46
|
+
// 560 * 384 * 4 (RGBA) = 860160
|
|
47
|
+
CHECK(Video::getFramebufferSize() == 860160);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
TEST_CASE("Video getFramebuffer returns non-null pointer", "[video][framebuffer]") {
|
|
51
|
+
VideoTestFixture f;
|
|
52
|
+
|
|
53
|
+
CHECK(f.video->getFramebuffer() != nullptr);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
TEST_CASE("Video const getFramebuffer returns non-null pointer", "[video][framebuffer]") {
|
|
57
|
+
VideoTestFixture f;
|
|
58
|
+
|
|
59
|
+
const Video& constVideo = *f.video;
|
|
60
|
+
CHECK(constVideo.getFramebuffer() != nullptr);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// forceRenderFrame
|
|
65
|
+
// ============================================================================
|
|
66
|
+
|
|
67
|
+
TEST_CASE("Video forceRenderFrame does not crash", "[video][render]") {
|
|
68
|
+
VideoTestFixture f;
|
|
69
|
+
|
|
70
|
+
// Should complete without error
|
|
71
|
+
f.video->forceRenderFrame();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
TEST_CASE("Video forceRenderFrame sets frame dirty", "[video][render]") {
|
|
75
|
+
VideoTestFixture f;
|
|
76
|
+
|
|
77
|
+
f.video->clearFrameDirty();
|
|
78
|
+
CHECK_FALSE(f.video->isFrameDirty());
|
|
79
|
+
|
|
80
|
+
f.video->forceRenderFrame();
|
|
81
|
+
// After rendering, the dirty flag should reflect the render occurred
|
|
82
|
+
// (forceRenderFrame renders regardless of dirty state)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// Mode detection via soft switch toggling
|
|
87
|
+
// ============================================================================
|
|
88
|
+
|
|
89
|
+
TEST_CASE("Video mode: text=true, col80=false -> TEXT_40", "[video][mode]") {
|
|
90
|
+
VideoTestFixture f;
|
|
91
|
+
|
|
92
|
+
// Ensure text mode
|
|
93
|
+
f.mmu.write(0xC051, 0); // TEXT on (write also toggles)
|
|
94
|
+
f.mmu.write(0xC00C, 0); // 80COL off
|
|
95
|
+
|
|
96
|
+
CHECK(f.video->getCurrentMode() == VideoMode::TEXT_40);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
TEST_CASE("Video mode: text=true, col80=true -> TEXT_80", "[video][mode]") {
|
|
100
|
+
VideoTestFixture f;
|
|
101
|
+
|
|
102
|
+
f.mmu.write(0xC051, 0); // TEXT on
|
|
103
|
+
f.mmu.write(0xC00D, 0); // 80COL on
|
|
104
|
+
|
|
105
|
+
CHECK(f.video->getCurrentMode() == VideoMode::TEXT_80);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
TEST_CASE("Video mode: text=false, hires=false -> LORES", "[video][mode]") {
|
|
109
|
+
VideoTestFixture f;
|
|
110
|
+
|
|
111
|
+
f.mmu.read(0xC050); // TEXT off (graphics mode)
|
|
112
|
+
f.mmu.read(0xC056); // HIRES off
|
|
113
|
+
|
|
114
|
+
CHECK(f.video->getCurrentMode() == VideoMode::LORES);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
TEST_CASE("Video mode: text=false, hires=true -> HIRES", "[video][mode]") {
|
|
118
|
+
VideoTestFixture f;
|
|
119
|
+
|
|
120
|
+
f.mmu.read(0xC050); // TEXT off (graphics mode)
|
|
121
|
+
f.mmu.read(0xC057); // HIRES on
|
|
122
|
+
|
|
123
|
+
CHECK(f.video->getCurrentMode() == VideoMode::HIRES);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
TEST_CASE("Video mode: DOUBLE_LORES requires AN3 off + 80COL", "[video][mode]") {
|
|
127
|
+
VideoTestFixture f;
|
|
128
|
+
|
|
129
|
+
f.mmu.read(0xC050); // TEXT off (graphics)
|
|
130
|
+
f.mmu.read(0xC056); // HIRES off
|
|
131
|
+
f.mmu.write(0xC00D, 0); // 80COL on
|
|
132
|
+
f.mmu.read(0xC05E); // AN3 off
|
|
133
|
+
|
|
134
|
+
CHECK(f.video->getCurrentMode() == VideoMode::DOUBLE_LORES);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
TEST_CASE("Video mode: DOUBLE_HIRES requires AN3 off + 80COL + HIRES", "[video][mode]") {
|
|
138
|
+
VideoTestFixture f;
|
|
139
|
+
|
|
140
|
+
f.mmu.read(0xC050); // TEXT off (graphics)
|
|
141
|
+
f.mmu.read(0xC057); // HIRES on
|
|
142
|
+
f.mmu.write(0xC00D, 0); // 80COL on
|
|
143
|
+
f.mmu.read(0xC05E); // AN3 off
|
|
144
|
+
|
|
145
|
+
CHECK(f.video->getCurrentMode() == VideoMode::DOUBLE_HIRES);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ============================================================================
|
|
149
|
+
// Display options: monochrome
|
|
150
|
+
// ============================================================================
|
|
151
|
+
|
|
152
|
+
TEST_CASE("Video setMonochrome/isMonochrome toggle", "[video][options]") {
|
|
153
|
+
VideoTestFixture f;
|
|
154
|
+
|
|
155
|
+
CHECK_FALSE(f.video->isMonochrome());
|
|
156
|
+
|
|
157
|
+
f.video->setMonochrome(true);
|
|
158
|
+
CHECK(f.video->isMonochrome());
|
|
159
|
+
|
|
160
|
+
f.video->setMonochrome(false);
|
|
161
|
+
CHECK_FALSE(f.video->isMonochrome());
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ============================================================================
|
|
165
|
+
// Display options: green phosphor
|
|
166
|
+
// ============================================================================
|
|
167
|
+
|
|
168
|
+
TEST_CASE("Video setGreenPhosphor/isGreenPhosphor toggle", "[video][options]") {
|
|
169
|
+
VideoTestFixture f;
|
|
170
|
+
|
|
171
|
+
CHECK_FALSE(f.video->isGreenPhosphor());
|
|
172
|
+
|
|
173
|
+
f.video->setGreenPhosphor(true);
|
|
174
|
+
CHECK(f.video->isGreenPhosphor());
|
|
175
|
+
|
|
176
|
+
f.video->setGreenPhosphor(false);
|
|
177
|
+
CHECK_FALSE(f.video->isGreenPhosphor());
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ============================================================================
|
|
181
|
+
// Display options: UK character set
|
|
182
|
+
// ============================================================================
|
|
183
|
+
|
|
184
|
+
TEST_CASE("Video setUKCharacterSet/isUKCharacterSet toggle", "[video][options]") {
|
|
185
|
+
VideoTestFixture f;
|
|
186
|
+
|
|
187
|
+
CHECK_FALSE(f.video->isUKCharacterSet());
|
|
188
|
+
|
|
189
|
+
f.video->setUKCharacterSet(true);
|
|
190
|
+
CHECK(f.video->isUKCharacterSet());
|
|
191
|
+
|
|
192
|
+
f.video->setUKCharacterSet(false);
|
|
193
|
+
CHECK_FALSE(f.video->isUKCharacterSet());
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ============================================================================
|
|
197
|
+
// Page switching
|
|
198
|
+
// ============================================================================
|
|
199
|
+
|
|
200
|
+
TEST_CASE("Video page switching via $C055/$C054", "[video][page]") {
|
|
201
|
+
VideoTestFixture f;
|
|
202
|
+
|
|
203
|
+
// Default: page1
|
|
204
|
+
CHECK_FALSE(f.mmu.getSoftSwitches().page2);
|
|
205
|
+
|
|
206
|
+
// Switch to page 2
|
|
207
|
+
f.mmu.write(0xC055, 0);
|
|
208
|
+
CHECK(f.mmu.getSoftSwitches().page2);
|
|
209
|
+
|
|
210
|
+
// Switch back to page 1
|
|
211
|
+
f.mmu.write(0xC054, 0);
|
|
212
|
+
CHECK_FALSE(f.mmu.getSoftSwitches().page2);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ============================================================================
|
|
216
|
+
// Text rendering
|
|
217
|
+
// ============================================================================
|
|
218
|
+
|
|
219
|
+
TEST_CASE("Video text rendering: writing ASCII to $0400 produces non-zero pixels", "[video][render]") {
|
|
220
|
+
VideoTestFixture f;
|
|
221
|
+
|
|
222
|
+
// Fill text page 1 with a visible character (inverse '@' = 0x00, normal 'A' = 0xC1)
|
|
223
|
+
// Use normal 'A' character (0xC1 in Apple II encoding)
|
|
224
|
+
for (int i = 0; i < 40; ++i) {
|
|
225
|
+
f.mmu.write(0x0400 + i, 0xC1); // 'A' in normal video
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Render the frame
|
|
229
|
+
f.video->forceRenderFrame();
|
|
230
|
+
|
|
231
|
+
// Check that framebuffer has some non-zero pixels in the first row area
|
|
232
|
+
const uint8_t* fb = f.video->getFramebuffer();
|
|
233
|
+
bool hasNonZero = false;
|
|
234
|
+
// Check the first few scanlines (each text row is 16 pixels tall in 560x384)
|
|
235
|
+
// 384 / 24 = 16 pixels per text row
|
|
236
|
+
for (size_t i = 0; i < 560 * 16 * 4; ++i) {
|
|
237
|
+
if (fb[i] != 0) {
|
|
238
|
+
hasNonZero = true;
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
CHECK(hasNonZero);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
TEST_CASE("Video text rendering: blank screen has predictable output", "[video][render]") {
|
|
247
|
+
VideoTestFixture f;
|
|
248
|
+
|
|
249
|
+
// Text page is all zeros (inverse '@' on Apple IIe)
|
|
250
|
+
// Even with all zeros, the character ROM should produce some pixel output
|
|
251
|
+
f.video->forceRenderFrame();
|
|
252
|
+
|
|
253
|
+
const uint8_t* fb = f.video->getFramebuffer();
|
|
254
|
+
// Framebuffer should exist and have been written to
|
|
255
|
+
CHECK(fb != nullptr);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ============================================================================
|
|
259
|
+
// Frame dirty flag
|
|
260
|
+
// ============================================================================
|
|
261
|
+
|
|
262
|
+
TEST_CASE("Video isFrameDirty/clearFrameDirty/setFrameDirty", "[video][dirty]") {
|
|
263
|
+
VideoTestFixture f;
|
|
264
|
+
|
|
265
|
+
// Initially dirty
|
|
266
|
+
CHECK(f.video->isFrameDirty());
|
|
267
|
+
|
|
268
|
+
f.video->clearFrameDirty();
|
|
269
|
+
CHECK_FALSE(f.video->isFrameDirty());
|
|
270
|
+
|
|
271
|
+
f.video->setFrameDirty();
|
|
272
|
+
CHECK(f.video->isFrameDirty());
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ============================================================================
|
|
276
|
+
// Mixed mode
|
|
277
|
+
// ============================================================================
|
|
278
|
+
|
|
279
|
+
TEST_CASE("Video mixed mode: $C053 sets mixed, $C052 clears", "[video][mode]") {
|
|
280
|
+
VideoTestFixture f;
|
|
281
|
+
|
|
282
|
+
// Default: mixed off
|
|
283
|
+
CHECK_FALSE(f.mmu.getSoftSwitches().mixed);
|
|
284
|
+
|
|
285
|
+
f.mmu.read(0xC053); // MIXSET
|
|
286
|
+
CHECK(f.mmu.getSoftSwitches().mixed);
|
|
287
|
+
|
|
288
|
+
f.mmu.read(0xC052); // MIXCLR
|
|
289
|
+
CHECK_FALSE(f.mmu.getSoftSwitches().mixed);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// ============================================================================
|
|
293
|
+
// Mode transitions
|
|
294
|
+
// ============================================================================
|
|
295
|
+
|
|
296
|
+
TEST_CASE("Video mode transitions work correctly", "[video][mode]") {
|
|
297
|
+
VideoTestFixture f;
|
|
298
|
+
|
|
299
|
+
// Start in TEXT_40
|
|
300
|
+
CHECK(f.video->getCurrentMode() == VideoMode::TEXT_40);
|
|
301
|
+
|
|
302
|
+
// Switch to HIRES
|
|
303
|
+
f.mmu.read(0xC050); // TEXT off
|
|
304
|
+
f.mmu.read(0xC057); // HIRES on
|
|
305
|
+
CHECK(f.video->getCurrentMode() == VideoMode::HIRES);
|
|
306
|
+
|
|
307
|
+
// Switch to LORES
|
|
308
|
+
f.mmu.read(0xC056); // HIRES off
|
|
309
|
+
CHECK(f.video->getCurrentMode() == VideoMode::LORES);
|
|
310
|
+
|
|
311
|
+
// Switch to TEXT_80
|
|
312
|
+
f.mmu.read(0xC051); // TEXT on
|
|
313
|
+
f.mmu.write(0xC00D, 0); // 80COL on
|
|
314
|
+
CHECK(f.video->getCurrentMode() == VideoMode::TEXT_80);
|
|
315
|
+
|
|
316
|
+
// Back to TEXT_40
|
|
317
|
+
f.mmu.write(0xC00C, 0); // 80COL off
|
|
318
|
+
CHECK(f.video->getCurrentMode() == VideoMode::TEXT_40);
|
|
319
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* test_woz_disk_image.cpp - Unit tests for WozDiskImage
|
|
3
|
+
*
|
|
4
|
+
* Tests the WOZ 1.0/2.0 bit-accurate disk image format including:
|
|
5
|
+
* - Loading with invalid data
|
|
6
|
+
* - Creating blank WOZ2 images
|
|
7
|
+
* - Bit read/write operations
|
|
8
|
+
* - Track count and head positioning
|
|
9
|
+
* - Format and metadata queries
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
#define CATCH_CONFIG_MAIN
|
|
13
|
+
#include "catch.hpp"
|
|
14
|
+
|
|
15
|
+
#include "woz_disk_image.hpp"
|
|
16
|
+
|
|
17
|
+
#include <cstring>
|
|
18
|
+
#include <vector>
|
|
19
|
+
|
|
20
|
+
using namespace a2e;
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Loading with invalid data
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
TEST_CASE("WozDiskImage load with empty data returns false", "[woz]") {
|
|
27
|
+
WozDiskImage img;
|
|
28
|
+
bool ok = img.load(nullptr, 0, "empty.woz");
|
|
29
|
+
REQUIRE_FALSE(ok);
|
|
30
|
+
REQUIRE_FALSE(img.isLoaded());
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
TEST_CASE("WozDiskImage load with too-small data returns false", "[woz]") {
|
|
34
|
+
WozDiskImage img;
|
|
35
|
+
std::vector<uint8_t> tiny(10, 0x00);
|
|
36
|
+
bool ok = img.load(tiny.data(), tiny.size(), "tiny.woz");
|
|
37
|
+
REQUIRE_FALSE(ok);
|
|
38
|
+
REQUIRE_FALSE(img.isLoaded());
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
TEST_CASE("WozDiskImage load with random garbage returns false", "[woz]") {
|
|
42
|
+
WozDiskImage img;
|
|
43
|
+
std::vector<uint8_t> garbage(8192, 0xAA);
|
|
44
|
+
bool ok = img.load(garbage.data(), garbage.size(), "garbage.woz");
|
|
45
|
+
REQUIRE_FALSE(ok);
|
|
46
|
+
REQUIRE_FALSE(img.isLoaded());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
TEST_CASE("WozDiskImage load with wrong signature returns false", "[woz]") {
|
|
50
|
+
WozDiskImage img;
|
|
51
|
+
// Build a buffer that looks like a file header but has wrong signature
|
|
52
|
+
std::vector<uint8_t> badSig(256, 0x00);
|
|
53
|
+
badSig[0] = 'W'; badSig[1] = 'O'; badSig[2] = 'Z'; badSig[3] = '9';
|
|
54
|
+
badSig[4] = 0xFF;
|
|
55
|
+
badSig[5] = 0x0A; badSig[6] = 0x0D; badSig[7] = 0x0A;
|
|
56
|
+
|
|
57
|
+
bool ok = img.load(badSig.data(), badSig.size(), "badsig.woz");
|
|
58
|
+
REQUIRE_FALSE(ok);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Creating blank images
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
TEST_CASE("WozDiskImage createBlank creates a valid image", "[woz]") {
|
|
66
|
+
WozDiskImage img;
|
|
67
|
+
img.createBlank();
|
|
68
|
+
|
|
69
|
+
REQUIRE(img.isLoaded());
|
|
70
|
+
REQUIRE(img.getFormat() == DiskImage::Format::WOZ2);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
TEST_CASE("WozDiskImage createBlank has 35 tracks", "[woz]") {
|
|
74
|
+
WozDiskImage img;
|
|
75
|
+
img.createBlank();
|
|
76
|
+
|
|
77
|
+
REQUIRE(img.getTrackCount() == 35);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
TEST_CASE("WozDiskImage createBlank starts at track 0", "[woz]") {
|
|
81
|
+
WozDiskImage img;
|
|
82
|
+
img.createBlank();
|
|
83
|
+
|
|
84
|
+
REQUIRE(img.getTrack() == 0);
|
|
85
|
+
REQUIRE(img.getQuarterTrack() == 0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
TEST_CASE("WozDiskImage createBlank reports 5.25 inch disk type", "[woz]") {
|
|
89
|
+
WozDiskImage img;
|
|
90
|
+
img.createBlank();
|
|
91
|
+
|
|
92
|
+
REQUIRE(img.getDiskType() == 1); // 1 = 5.25"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
TEST_CASE("WozDiskImage createBlank has valid disk type string", "[woz]") {
|
|
96
|
+
WozDiskImage img;
|
|
97
|
+
img.createBlank();
|
|
98
|
+
|
|
99
|
+
std::string typeStr = img.getDiskTypeString();
|
|
100
|
+
REQUIRE_FALSE(typeStr.empty());
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
TEST_CASE("WozDiskImage createBlank has default bit timing", "[woz]") {
|
|
104
|
+
WozDiskImage img;
|
|
105
|
+
img.createBlank();
|
|
106
|
+
|
|
107
|
+
// Default optimal bit timing is 32 (= 4 microseconds per bit)
|
|
108
|
+
REQUIRE(img.getOptimalBitTiming() == 32);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
TEST_CASE("WozDiskImage createBlank is not write-protected", "[woz]") {
|
|
112
|
+
WozDiskImage img;
|
|
113
|
+
img.createBlank();
|
|
114
|
+
|
|
115
|
+
REQUIRE_FALSE(img.isWriteProtected());
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
TEST_CASE("WozDiskImage createBlank is marked as modified", "[woz]") {
|
|
119
|
+
WozDiskImage img;
|
|
120
|
+
img.createBlank();
|
|
121
|
+
|
|
122
|
+
// createBlank marks the image as modified so it will be saved
|
|
123
|
+
REQUIRE(img.isModified());
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
TEST_CASE("WozDiskImage createBlank hasData returns true", "[woz]") {
|
|
127
|
+
WozDiskImage img;
|
|
128
|
+
img.createBlank();
|
|
129
|
+
|
|
130
|
+
REQUIRE(img.hasData());
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// Bit read/write operations
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
TEST_CASE("WozDiskImage readBit on blank image returns 0 or 1", "[woz]") {
|
|
138
|
+
WozDiskImage img;
|
|
139
|
+
img.createBlank();
|
|
140
|
+
|
|
141
|
+
// Blank disk is filled with sync bytes, so bits should be valid
|
|
142
|
+
uint8_t bit = img.readBit();
|
|
143
|
+
REQUIRE((bit == 0 || bit == 1));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
TEST_CASE("WozDiskImage readBit advances position", "[woz]") {
|
|
147
|
+
WozDiskImage img;
|
|
148
|
+
img.createBlank();
|
|
149
|
+
|
|
150
|
+
size_t pos1 = img.getCurrentNibblePosition();
|
|
151
|
+
// Read enough bits to shift nibble position
|
|
152
|
+
for (int i = 0; i < 8; ++i) {
|
|
153
|
+
img.readBit();
|
|
154
|
+
}
|
|
155
|
+
size_t pos2 = img.getCurrentNibblePosition();
|
|
156
|
+
REQUIRE(pos2 > pos1);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
TEST_CASE("WozDiskImage writeBit modifies the image", "[woz]") {
|
|
160
|
+
WozDiskImage img;
|
|
161
|
+
img.createBlank();
|
|
162
|
+
|
|
163
|
+
// Write some bits
|
|
164
|
+
img.writeBit(1);
|
|
165
|
+
img.writeBit(0);
|
|
166
|
+
img.writeBit(1);
|
|
167
|
+
img.writeBit(1);
|
|
168
|
+
|
|
169
|
+
REQUIRE(img.isModified());
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
TEST_CASE("WozDiskImage readNibble on blank image returns sync-like nibble", "[woz]") {
|
|
173
|
+
WozDiskImage img;
|
|
174
|
+
img.createBlank();
|
|
175
|
+
|
|
176
|
+
// Blank disk is filled with sync bytes (0xFF typically)
|
|
177
|
+
uint8_t nibble = img.readNibble();
|
|
178
|
+
// Valid nibbles have bit 7 set
|
|
179
|
+
REQUIRE((nibble & 0x80) != 0);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
// Head positioning
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
TEST_CASE("WozDiskImage setQuarterTrack changes position", "[woz]") {
|
|
187
|
+
WozDiskImage img;
|
|
188
|
+
img.createBlank();
|
|
189
|
+
|
|
190
|
+
img.setQuarterTrack(12);
|
|
191
|
+
REQUIRE(img.getQuarterTrack() == 12);
|
|
192
|
+
REQUIRE(img.getTrack() == 3);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
TEST_CASE("WozDiskImage setPhase stepping moves head", "[woz]") {
|
|
196
|
+
WozDiskImage img;
|
|
197
|
+
img.createBlank();
|
|
198
|
+
|
|
199
|
+
REQUIRE(img.getQuarterTrack() == 0);
|
|
200
|
+
|
|
201
|
+
// Step outward: phase 0 on, phase 1 on, phase 0 off
|
|
202
|
+
img.setPhase(0, true);
|
|
203
|
+
img.setPhase(1, true);
|
|
204
|
+
img.setPhase(0, false);
|
|
205
|
+
|
|
206
|
+
REQUIRE(img.getQuarterTrack() > 0);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
// Reset state
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
|
|
213
|
+
TEST_CASE("WozDiskImage resetState resets head position", "[woz]") {
|
|
214
|
+
WozDiskImage img;
|
|
215
|
+
img.createBlank();
|
|
216
|
+
|
|
217
|
+
img.setQuarterTrack(20);
|
|
218
|
+
REQUIRE(img.getQuarterTrack() == 20);
|
|
219
|
+
|
|
220
|
+
img.resetState();
|
|
221
|
+
REQUIRE(img.getQuarterTrack() == 0);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ---------------------------------------------------------------------------
|
|
225
|
+
// Format name
|
|
226
|
+
// ---------------------------------------------------------------------------
|
|
227
|
+
|
|
228
|
+
TEST_CASE("WozDiskImage getFormatName returns non-empty string", "[woz]") {
|
|
229
|
+
WozDiskImage img;
|
|
230
|
+
img.createBlank();
|
|
231
|
+
|
|
232
|
+
std::string name = img.getFormatName();
|
|
233
|
+
REQUIRE_FALSE(name.empty());
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
// Move semantics
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
|
|
240
|
+
TEST_CASE("WozDiskImage is move-constructible", "[woz]") {
|
|
241
|
+
WozDiskImage img1;
|
|
242
|
+
img1.createBlank();
|
|
243
|
+
REQUIRE(img1.isLoaded());
|
|
244
|
+
|
|
245
|
+
WozDiskImage img2(std::move(img1));
|
|
246
|
+
REQUIRE(img2.isLoaded());
|
|
247
|
+
REQUIRE(img2.getTrackCount() == 35);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
TEST_CASE("WozDiskImage is move-assignable", "[woz]") {
|
|
251
|
+
WozDiskImage img1;
|
|
252
|
+
img1.createBlank();
|
|
253
|
+
|
|
254
|
+
WozDiskImage img2;
|
|
255
|
+
img2 = std::move(img1);
|
|
256
|
+
REQUIRE(img2.isLoaded());
|
|
257
|
+
}
|
package/vite.config.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { defineConfig } from "vite";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
import { copyFileSync, mkdirSync } from "fs";
|
|
4
|
+
|
|
5
|
+
// Plugin to copy audio worklet file (can't be bundled)
|
|
6
|
+
const copyAudioWorklet = () => ({
|
|
7
|
+
name: "copy-audio-worklet",
|
|
8
|
+
writeBundle() {
|
|
9
|
+
mkdirSync(resolve(__dirname, "dist"), { recursive: true });
|
|
10
|
+
copyFileSync(
|
|
11
|
+
resolve(__dirname, "src/js/audio/audio-worklet.js"),
|
|
12
|
+
resolve(__dirname, "dist/audio-worklet.js"),
|
|
13
|
+
);
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export default defineConfig({
|
|
18
|
+
root: "public",
|
|
19
|
+
publicDir: "../public",
|
|
20
|
+
|
|
21
|
+
server: {
|
|
22
|
+
port: 3000,
|
|
23
|
+
open: true,
|
|
24
|
+
headers: {
|
|
25
|
+
// Required for SharedArrayBuffer (if needed for AudioWorklet)
|
|
26
|
+
"Cross-Origin-Opener-Policy": "same-origin",
|
|
27
|
+
"Cross-Origin-Embedder-Policy": "require-corp",
|
|
28
|
+
"Cache-Control": "no-store",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
build: {
|
|
33
|
+
outDir: "../dist",
|
|
34
|
+
emptyOutDir: true,
|
|
35
|
+
rollupOptions: {
|
|
36
|
+
input: {
|
|
37
|
+
main: resolve(__dirname, "public/index.html"),
|
|
38
|
+
},
|
|
39
|
+
output: {
|
|
40
|
+
manualChunks: {
|
|
41
|
+
debug: [
|
|
42
|
+
"/src/js/debug/cpu-debugger-window.js",
|
|
43
|
+
"/src/js/debug/memory-browser-window.js",
|
|
44
|
+
"/src/js/debug/memory-heat-map-window.js",
|
|
45
|
+
"/src/js/debug/memory-map-window.js",
|
|
46
|
+
"/src/js/debug/stack-viewer-window.js",
|
|
47
|
+
"/src/js/debug/zero-page-watch-window.js",
|
|
48
|
+
"/src/js/debug/soft-switch-window.js",
|
|
49
|
+
"/src/js/debug/mockingboard-window.js",
|
|
50
|
+
"/src/js/debug/mouse-card-window.js",
|
|
51
|
+
"/src/js/debug/basic-program-window.js",
|
|
52
|
+
"/src/js/debug/rule-builder-window.js",
|
|
53
|
+
"/src/js/debug/assembler-editor-window.js",
|
|
54
|
+
],
|
|
55
|
+
display: [
|
|
56
|
+
"/src/js/display/index.js",
|
|
57
|
+
"/src/js/display/webgl-renderer.js",
|
|
58
|
+
"/src/js/display/display-settings-window.js",
|
|
59
|
+
"/src/js/display/screen-window.js",
|
|
60
|
+
],
|
|
61
|
+
"disk-manager": [
|
|
62
|
+
"/src/js/disk-manager/index.js",
|
|
63
|
+
"/src/js/disk-manager/disk-operations.js",
|
|
64
|
+
"/src/js/disk-manager/disk-persistence.js",
|
|
65
|
+
"/src/js/disk-manager/disk-surface-renderer.js",
|
|
66
|
+
"/src/js/disk-manager/disk-drives-window.js",
|
|
67
|
+
"/src/js/disk-manager/drive-sounds.js",
|
|
68
|
+
],
|
|
69
|
+
"file-explorer": [
|
|
70
|
+
"/src/js/file-explorer/index.js",
|
|
71
|
+
"/src/js/file-explorer/dos33.js",
|
|
72
|
+
"/src/js/file-explorer/prodos.js",
|
|
73
|
+
"/src/js/file-explorer/disassembler.js",
|
|
74
|
+
"/src/js/file-explorer/file-viewer.js",
|
|
75
|
+
"/src/js/file-explorer/utils.js",
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
resolve: {
|
|
83
|
+
alias: {
|
|
84
|
+
"/src": resolve(__dirname, "src"),
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
// Handle WASM files
|
|
89
|
+
assetsInclude: ["**/*.wasm"],
|
|
90
|
+
|
|
91
|
+
optimizeDeps: {
|
|
92
|
+
exclude: ["a2e.js"],
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
plugins: [copyAudioWorklet()],
|
|
96
|
+
});
|