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,547 @@
|
|
|
1
|
+
# Disk System Internals
|
|
2
|
+
|
|
3
|
+
This page covers the low-level implementation of the Disk II controller card and disk image format support. For user-facing disk drive operations, see [[Disk Drives]].
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Architecture Overview](#architecture-overview)
|
|
10
|
+
- [Disk II Controller Card](#disk-ii-controller-card)
|
|
11
|
+
- [Slot Assignment and Memory Map](#slot-assignment-and-memory-map)
|
|
12
|
+
- [Soft Switches](#soft-switches)
|
|
13
|
+
- [Logic State Sequencer](#logic-state-sequencer)
|
|
14
|
+
- [P6 ROM](#p6-rom)
|
|
15
|
+
- [Motor Control](#motor-control)
|
|
16
|
+
- [Stepper Motor and Head Positioning](#stepper-motor-and-head-positioning)
|
|
17
|
+
- [Disk Image Abstraction](#disk-image-abstraction)
|
|
18
|
+
- [DSK Format](#dsk-format)
|
|
19
|
+
- [File Layout](#file-layout)
|
|
20
|
+
- [Format Detection](#format-detection)
|
|
21
|
+
- [Sector Ordering](#sector-ordering)
|
|
22
|
+
- [Nibblization](#nibblization)
|
|
23
|
+
- [Denibblization](#denibblization)
|
|
24
|
+
- [Bit Stream Cache](#bit-stream-cache)
|
|
25
|
+
- [Disk Rotation Timing](#disk-rotation-timing)
|
|
26
|
+
- [WOZ Format](#woz-format)
|
|
27
|
+
- [File Structure](#file-structure)
|
|
28
|
+
- [INFO Chunk](#info-chunk)
|
|
29
|
+
- [TMAP Chunk](#tmap-chunk)
|
|
30
|
+
- [TRKS Chunk](#trks-chunk)
|
|
31
|
+
- [Quarter-Track Support](#quarter-track-support)
|
|
32
|
+
- [Bit-Level Access](#bit-level-access)
|
|
33
|
+
- [Sector Decoding](#sector-decoding)
|
|
34
|
+
- [Export and Saving](#export-and-saving)
|
|
35
|
+
- [GCR Encoding](#gcr-encoding)
|
|
36
|
+
- [6-and-2 Encoding](#6-and-2-encoding)
|
|
37
|
+
- [4-and-4 Encoding](#4-and-4-encoding)
|
|
38
|
+
- [Sector Structure](#sector-structure)
|
|
39
|
+
- [Self-Sync Bytes](#self-sync-bytes)
|
|
40
|
+
- [Write Support](#write-support)
|
|
41
|
+
- [State Serialization](#state-serialization)
|
|
42
|
+
- [Source Files](#source-files)
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Architecture Overview
|
|
47
|
+
|
|
48
|
+
The disk subsystem has three layers:
|
|
49
|
+
|
|
50
|
+
| Layer | Class | Responsibility |
|
|
51
|
+
|-------|-------|----------------|
|
|
52
|
+
| Controller | `Disk2Card` | Soft switches, Logic State Sequencer, motor/drive control |
|
|
53
|
+
| Image abstraction | `DiskImage` | Abstract interface for head positioning and data access |
|
|
54
|
+
| Format implementations | `DskDiskImage`, `WozDiskImage` | Format-specific loading, nibblization, bit-level storage |
|
|
55
|
+
|
|
56
|
+
The controller only communicates through the `DiskImage` interface using phase signals (for head movement) and nibble/bit read/write operations. Format-specific details are hidden behind this abstraction.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Disk II Controller Card
|
|
61
|
+
|
|
62
|
+
### Slot Assignment and Memory Map
|
|
63
|
+
|
|
64
|
+
The Disk II occupies slot 6 by default:
|
|
65
|
+
|
|
66
|
+
| Address Range | Function |
|
|
67
|
+
|---------------|----------|
|
|
68
|
+
| `$C0E0-$C0EF` | 16 soft switches for disk control |
|
|
69
|
+
| `$C600-$C6FF` | 256-byte bootstrap ROM (P5A, 341-0027) |
|
|
70
|
+
|
|
71
|
+
The card does not use expansion ROM (`$C800-$CFFF`).
|
|
72
|
+
|
|
73
|
+
### Soft Switches
|
|
74
|
+
|
|
75
|
+
The 16 soft switch addresses control all disk operations. Each switch is triggered by any access (read or write) to its address:
|
|
76
|
+
|
|
77
|
+
| Offset | Even (Off/Low) | Odd (On/High) |
|
|
78
|
+
|--------|----------------|---------------|
|
|
79
|
+
| `$00-$01` | Phase 0 off | Phase 0 on |
|
|
80
|
+
| `$02-$03` | Phase 1 off | Phase 1 on |
|
|
81
|
+
| `$04-$05` | Phase 2 off | Phase 2 on |
|
|
82
|
+
| `$06-$07` | Phase 3 off | Phase 3 on |
|
|
83
|
+
| `$08-$09` | Motor off | Motor on |
|
|
84
|
+
| `$0A-$0B` | Select Drive 1 | Select Drive 2 |
|
|
85
|
+
| `$0C-$0D` | Q6 Low (read data) | Q6 High (WP sense / write load) |
|
|
86
|
+
| `$0E-$0F` | Q7 Low (read mode) | Q7 High (write mode) |
|
|
87
|
+
|
|
88
|
+
The Q6/Q7 combination determines the controller mode:
|
|
89
|
+
|
|
90
|
+
| Q7 | Q6 | Mode | Operation |
|
|
91
|
+
|----|----|------|-----------|
|
|
92
|
+
| 0 | 0 | Read | Returns data register (shift register output) |
|
|
93
|
+
| 0 | 1 | Sense | Returns write-protect status in bit 7 |
|
|
94
|
+
| 1 | 0 | Write | Writes data register to disk |
|
|
95
|
+
| 1 | 1 | Load | Loads CPU bus data into data register |
|
|
96
|
+
|
|
97
|
+
### Logic State Sequencer
|
|
98
|
+
|
|
99
|
+
The heart of the Disk II controller is the Logic State Sequencer (LSS), implemented by the P6 ROM (341-0028). The LSS is a state machine that converts between the serial bit stream on the disk and bytes accessible by the CPU.
|
|
100
|
+
|
|
101
|
+
**Timing:** The LSS runs at 2x the CPU clock rate (approximately 2.046 MHz). An 8-phase clock divides each 4-cycle bit cell into 8 ticks. Disk read/write occurs only at phase 4 of this clock.
|
|
102
|
+
|
|
103
|
+
**State machine operation:**
|
|
104
|
+
|
|
105
|
+
1. At each tick, a P6 ROM address is computed from:
|
|
106
|
+
- Sequencer state (4 bits, high nibble)
|
|
107
|
+
- Q7 and Q6 mode bits
|
|
108
|
+
- Data register bit 7 (QA feedback)
|
|
109
|
+
- Read pulse from disk (1 = flux transition, inverted for address)
|
|
110
|
+
|
|
111
|
+
2. The ROM output encodes the next state (high nibble) and an action code (low nibble)
|
|
112
|
+
|
|
113
|
+
3. Actions on the data register:
|
|
114
|
+
|
|
115
|
+
| Action Code | Operation | Description |
|
|
116
|
+
|-------------|-----------|-------------|
|
|
117
|
+
| `$0-$7` | CLR | Clear data register to `$00` |
|
|
118
|
+
| `$8`, `$C` | NOP | No operation |
|
|
119
|
+
| `$9` | SL0 | Shift left, insert 0 |
|
|
120
|
+
| `$A`, `$E` | SR+WP | Shift right, insert write-protect bit |
|
|
121
|
+
| `$B`, `$F` | LOAD | Load from CPU bus |
|
|
122
|
+
| `$D` | SL1 | Shift left, insert 1 |
|
|
123
|
+
|
|
124
|
+
4. In write mode (Q7=1), at phase 4 the sequencer outputs bit 3 of the next state to the disk head.
|
|
125
|
+
|
|
126
|
+
**Catch-up mechanism:** Rather than running the LSS every CPU cycle, the controller records the last cycle it was clocked and "catches up" when a soft switch is accessed. This is capped at approximately one disk revolution (~53,000 bits x 8 ticks) to prevent excessive computation after long idle periods.
|
|
127
|
+
|
|
128
|
+
### P6 ROM
|
|
129
|
+
|
|
130
|
+
The P6 ROM (341-0028) is a 256x4-bit PROM stored in the emulator in de-scrambled BAPD (Beneath Apple ProDOS) logical format. The ROM address is computed as:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
address = (state << 4) | (Q7 << 3) | (Q6 << 2) | (QA << 1) | inverted_pulse
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The ROM CRC32 (source format) is `b72a2c70`.
|
|
137
|
+
|
|
138
|
+
### Motor Control
|
|
139
|
+
|
|
140
|
+
The disk motor has a delayed-off behavior matching real hardware:
|
|
141
|
+
|
|
142
|
+
- **Motor on:** Immediate; sets `motorOn_` flag and resets the LSS cycle counter
|
|
143
|
+
- **Motor off:** Delayed by approximately 1 second (1,023,000 CPU cycles). The `motorOffCycle_` timestamp is recorded when the off switch is hit, and `isMotorOn()` checks elapsed time lazily
|
|
144
|
+
|
|
145
|
+
This delay allows software to briefly turn the motor "off" during seeks without actually stopping the disk, which was a common technique.
|
|
146
|
+
|
|
147
|
+
### Stepper Motor and Head Positioning
|
|
148
|
+
|
|
149
|
+
The Disk II uses a 4-phase stepper motor for head positioning. The head moves in half-track (2 quarter-track) increments:
|
|
150
|
+
|
|
151
|
+
1. The controller manages phase magnet states as a 4-bit field (bits 0-3 for phases 0-3)
|
|
152
|
+
2. Stepping occurs when the **current phase is turned OFF** and an adjacent phase is ON
|
|
153
|
+
3. If the next phase (clockwise) is on, the head steps **inward** (higher track numbers)
|
|
154
|
+
4. If the previous phase (counter-clockwise) is on, the head steps **outward** (toward track 0)
|
|
155
|
+
5. If both or neither adjacent phases are on, no stepping occurs
|
|
156
|
+
|
|
157
|
+
Position tracking uses quarter-tracks (0-139 for DSK, 0-159 for WOZ). The visible track number is `quarter_track / 4`.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Disk Image Abstraction
|
|
162
|
+
|
|
163
|
+
The `DiskImage` abstract base class defines the interface between the controller and format-specific implementations:
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
DiskImage (abstract)
|
|
167
|
+
+-- DskDiskImage (DSK/DO/PO raw sector format)
|
|
168
|
+
+-- WozDiskImage (WOZ 1.0/2.0 bit-accurate format)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Key interface methods:
|
|
172
|
+
|
|
173
|
+
| Method | Description |
|
|
174
|
+
|--------|-------------|
|
|
175
|
+
| `load()` | Load disk image from raw data |
|
|
176
|
+
| `setPhase()` | Notify of phase magnet state change |
|
|
177
|
+
| `advanceBitPosition()` | Advance disk rotation based on CPU cycles |
|
|
178
|
+
| `readNibble()` / `writeNibble()` | Nibble-level access |
|
|
179
|
+
| `readBit()` / `writeBit()` | Bit-level access (for LSS) |
|
|
180
|
+
| `getSectorData()` | Get decoded sector data for file explorer |
|
|
181
|
+
| `exportData()` | Export in native format for saving |
|
|
182
|
+
|
|
183
|
+
The disk image manages head positioning internally based on stepper motor phase changes. The controller only knows about phases (0-3), not tracks.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## DSK Format
|
|
188
|
+
|
|
189
|
+
### File Layout
|
|
190
|
+
|
|
191
|
+
DSK is a raw sector image format storing exactly 143,360 bytes (140 KB):
|
|
192
|
+
|
|
193
|
+
| Parameter | Value |
|
|
194
|
+
|-----------|-------|
|
|
195
|
+
| Tracks | 35 (0-34) |
|
|
196
|
+
| Sectors per track | 16 |
|
|
197
|
+
| Bytes per sector | 256 |
|
|
198
|
+
| Nibbles per track | 6,656 |
|
|
199
|
+
| Total size | 143,360 bytes |
|
|
200
|
+
|
|
201
|
+
### Format Detection
|
|
202
|
+
|
|
203
|
+
Format detection uses content-based heuristics rather than relying solely on file extensions:
|
|
204
|
+
|
|
205
|
+
1. **ProDOS check:** Examine block 2 (offset 1024) for a volume directory header. A valid ProDOS disk has storage type `$Fx` with a valid name length and ASCII characters. If found, the format is PO (ProDOS order).
|
|
206
|
+
|
|
207
|
+
2. **DOS 3.3 check:** Examine the VTOC at track 17, sector 0 (offset 69,632). A valid DOS 3.3 disk has a catalog track of `$11-$14`, catalog sector of `$00-$0F`, and DOS version `$03`.
|
|
208
|
+
|
|
209
|
+
3. **Extension fallback:** If content detection fails, `.po` files are treated as ProDOS order; all others default to DOS order.
|
|
210
|
+
|
|
211
|
+
### Sector Ordering
|
|
212
|
+
|
|
213
|
+
Two sector ordering schemes are supported, each with its own logical-to-physical mapping:
|
|
214
|
+
|
|
215
|
+
**DOS 3.3 order (DSK/DO):**
|
|
216
|
+
|
|
217
|
+
| Logical | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|
|
218
|
+
|---------|---|---|---|---|---|---|---|---|---|---|----|----|----|----|----|-----|
|
|
219
|
+
| Physical | 0 | 13 | 11 | 9 | 7 | 5 | 3 | 1 | 14 | 12 | 10 | 8 | 6 | 4 | 2 | 15 |
|
|
220
|
+
|
|
221
|
+
**ProDOS order (PO):**
|
|
222
|
+
|
|
223
|
+
| Logical | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|
|
224
|
+
|---------|---|---|---|---|---|---|---|---|---|---|----|----|----|----|----|-----|
|
|
225
|
+
| Physical | 0 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 1 | 3 | 5 | 7 | 9 | 11 | 13 | 15 |
|
|
226
|
+
|
|
227
|
+
### Nibblization
|
|
228
|
+
|
|
229
|
+
When a track is first accessed, the raw sector data is converted to a GCR-encoded nibble stream on demand. Each track contains 16 sectors laid out sequentially:
|
|
230
|
+
|
|
231
|
+
1. **Gap 1** (first sector) or **Gap 3** (between sectors): Self-sync bytes
|
|
232
|
+
- First sector: 128 sync bytes
|
|
233
|
+
- Subsequent sectors: 40 bytes (track 0) or 38 bytes (other tracks)
|
|
234
|
+
2. **Address field:** Prologue + 4-and-4 encoded volume/track/sector/checksum + epilogue
|
|
235
|
+
3. **Gap 2:** 5 sync bytes
|
|
236
|
+
4. **Data field:** Prologue + 343 6-and-2 encoded nibbles + epilogue
|
|
237
|
+
5. **Gap 3 end:** 1 sync byte
|
|
238
|
+
|
|
239
|
+
The nibble track is padded or truncated to exactly 6,656 nibbles. Each nibble also carries a sync flag indicating whether it is a 10-bit self-sync byte or a standard 8-bit data byte.
|
|
240
|
+
|
|
241
|
+
### Denibblization
|
|
242
|
+
|
|
243
|
+
When a modified nibble track needs to be converted back to sector data (for saving), the denibblizer:
|
|
244
|
+
|
|
245
|
+
1. Creates an extended buffer with wrap-around (500 extra nibbles) to handle sectors that span the track boundary
|
|
246
|
+
2. Searches for address field prologues (`$D5 $AA $96`)
|
|
247
|
+
3. Decodes the 4-and-4 encoded address (volume, track, sector, checksum)
|
|
248
|
+
4. Verifies the address checksum and track number
|
|
249
|
+
5. Finds the data field prologue (`$D5 $AA $AD`) within 50 nibbles
|
|
250
|
+
6. Decodes the 343 6-and-2 encoded nibbles back to 256 bytes
|
|
251
|
+
7. Writes the decoded data to the sector data array using the appropriate sector mapping
|
|
252
|
+
|
|
253
|
+
### Bit Stream Cache
|
|
254
|
+
|
|
255
|
+
For LSS-level access, each nibble track can be converted to a packed bit stream. The conversion respects the sync flag:
|
|
256
|
+
|
|
257
|
+
- **Data bytes:** 8 bits per nibble (MSB first)
|
|
258
|
+
- **Sync bytes:** 10 bits per nibble (8 data bits + 2 extra zero bits)
|
|
259
|
+
|
|
260
|
+
The total bit count varies per track based on the number of sync bytes. Dirty bit tracks are converted back to nibble tracks before denibblization.
|
|
261
|
+
|
|
262
|
+
### Disk Rotation Timing
|
|
263
|
+
|
|
264
|
+
The disk spins at approximately 300 RPM (5 revolutions per second):
|
|
265
|
+
|
|
266
|
+
| Parameter | Value |
|
|
267
|
+
|-----------|-------|
|
|
268
|
+
| CPU clock | 1,023,000 Hz |
|
|
269
|
+
| Cycles per revolution | ~204,600 |
|
|
270
|
+
| Nibbles per track | 6,656 |
|
|
271
|
+
| Cycles per nibble | ~31 (giving ~297 RPM, within spec) |
|
|
272
|
+
|
|
273
|
+
The `advanceBitPosition()` method advances the nibble position based on elapsed CPU cycles to simulate continuous disk rotation while the motor is on.
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## WOZ Format
|
|
278
|
+
|
|
279
|
+
WOZ is a bit-accurate disk image format that captures exact magnetic flux transitions. It preserves copy-protection schemes and timing variations that sector-based formats cannot represent.
|
|
280
|
+
|
|
281
|
+
### File Structure
|
|
282
|
+
|
|
283
|
+
A WOZ file consists of a header followed by a series of chunks:
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
WOZ Header (12 bytes)
|
|
287
|
+
+-- INFO chunk (required)
|
|
288
|
+
+-- TMAP chunk (required)
|
|
289
|
+
+-- TRKS chunk (required)
|
|
290
|
+
+-- META chunk (optional, skipped)
|
|
291
|
+
+-- WRIT chunk (optional, skipped)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**Header format (12 bytes):**
|
|
295
|
+
|
|
296
|
+
| Offset | Size | Field | Description |
|
|
297
|
+
|--------|------|-------|-------------|
|
|
298
|
+
| 0 | 4 | Signature | `WOZ1` (`$57 $4F $5A $31`) or `WOZ2` (`$57 $4F $5A $32`) |
|
|
299
|
+
| 4 | 1 | High bits | `$FF` (high bit test) |
|
|
300
|
+
| 5 | 3 | LF/CR/LF | `$0A $0D $0A` (line ending test) |
|
|
301
|
+
| 8 | 4 | CRC32 | CRC of all data after this field |
|
|
302
|
+
|
|
303
|
+
**Chunk header (8 bytes):**
|
|
304
|
+
|
|
305
|
+
| Offset | Size | Field |
|
|
306
|
+
|--------|------|-------|
|
|
307
|
+
| 0 | 4 | Chunk ID (4 ASCII characters) |
|
|
308
|
+
| 4 | 4 | Data size (not including header) |
|
|
309
|
+
|
|
310
|
+
### INFO Chunk
|
|
311
|
+
|
|
312
|
+
The INFO chunk (60 bytes in WOZ2) contains disk metadata:
|
|
313
|
+
|
|
314
|
+
| Offset | Size | Field | Description |
|
|
315
|
+
|--------|------|-------|-------------|
|
|
316
|
+
| 0 | 1 | Version | 1 or 2 |
|
|
317
|
+
| 1 | 1 | Disk type | 1 = 5.25", 2 = 3.5" |
|
|
318
|
+
| 2 | 1 | Write protected | 0 or 1 |
|
|
319
|
+
| 3 | 1 | Synchronized | Tracks are cross-track synchronized |
|
|
320
|
+
| 4 | 1 | Cleaned | MC3470 fake bits removed |
|
|
321
|
+
| 5 | 32 | Creator | Software that created the image |
|
|
322
|
+
| 37 | 1 | Disk sides | 1 or 2 |
|
|
323
|
+
| 38 | 1 | Boot sector format | 0=unknown, 1=16-sector, 2=13-sector, 3=both |
|
|
324
|
+
| 39 | 1 | Optimal bit timing | In 125 ns units (default 32 = 4 us) |
|
|
325
|
+
| 40 | 2 | Compatible hardware | Bit field |
|
|
326
|
+
| 42 | 2 | Required RAM | Minimum RAM in KB |
|
|
327
|
+
| 44 | 2 | Largest track | Block count of largest track |
|
|
328
|
+
|
|
329
|
+
### TMAP Chunk
|
|
330
|
+
|
|
331
|
+
The TMAP (Track Map) chunk is a 160-byte array that maps quarter-track positions (0-159) to track data indices. A value of `$FF` indicates no track data at that position. Multiple quarter-tracks can reference the same track data index, enabling half-track data sharing.
|
|
332
|
+
|
|
333
|
+
### TRKS Chunk
|
|
334
|
+
|
|
335
|
+
**WOZ1 format:** Each track entry is a fixed 6,656 bytes:
|
|
336
|
+
|
|
337
|
+
| Offset | Size | Field |
|
|
338
|
+
|--------|------|-------|
|
|
339
|
+
| 0 | 6,646 | Bitstream data |
|
|
340
|
+
| 6,646 | 2 | Bytes used |
|
|
341
|
+
| 6,648 | 2 | Bit count |
|
|
342
|
+
| 6,650 | 2 | Splice point |
|
|
343
|
+
| 6,652 | 1 | Splice nibble |
|
|
344
|
+
| 6,653 | 1 | Splice bit count |
|
|
345
|
+
| 6,654 | 2 | Reserved |
|
|
346
|
+
|
|
347
|
+
**WOZ2 format:** A table of 160 track entries (8 bytes each), followed by track bit data at 512-byte block offsets:
|
|
348
|
+
|
|
349
|
+
| Offset | Size | Field |
|
|
350
|
+
|--------|------|-------|
|
|
351
|
+
| 0 | 2 | Starting block (512-byte blocks from file start) |
|
|
352
|
+
| 2 | 2 | Block count |
|
|
353
|
+
| 4 | 4 | Bit count |
|
|
354
|
+
|
|
355
|
+
### Quarter-Track Support
|
|
356
|
+
|
|
357
|
+
WOZ supports 160 quarter-track positions (40 possible track positions x 4 quarter-tracks each). The TMAP provides the indirection layer:
|
|
358
|
+
|
|
359
|
+
- Standard tracks are at quarter-track positions 0, 4, 8, ..., 136
|
|
360
|
+
- Half-tracks (used by some copy protection) can exist at positions 2, 6, 10, ...
|
|
361
|
+
- Quarter-tracks provide even finer granularity
|
|
362
|
+
|
|
363
|
+
The stepper motor physics are identical to the DSK implementation but with a wider range (0-159 vs 0-139).
|
|
364
|
+
|
|
365
|
+
### Bit-Level Access
|
|
366
|
+
|
|
367
|
+
WOZ stores raw bit data in packed MSB-first format. Reading a nibble from the bit stream follows the Disk II hardware behavior:
|
|
368
|
+
|
|
369
|
+
1. Read bits sequentially
|
|
370
|
+
2. Skip zero bits until a 1-bit is found (sync region)
|
|
371
|
+
3. Shift bits into an accumulator
|
|
372
|
+
4. When bit 7 of the accumulator is set, the nibble is complete
|
|
373
|
+
|
|
374
|
+
A safety limit of 64 bits prevents infinite loops on corrupted data.
|
|
375
|
+
|
|
376
|
+
**Bit timing for WOZ:**
|
|
377
|
+
|
|
378
|
+
| Parameter | Value |
|
|
379
|
+
|-----------|-------|
|
|
380
|
+
| Cycles per bit | 4 (from optimal bit timing of 32 x 125 ns = 4 us) |
|
|
381
|
+
| Bits per revolution | ~51,200 (standard track) |
|
|
382
|
+
|
|
383
|
+
### Sector Decoding
|
|
384
|
+
|
|
385
|
+
WOZ images can be decoded to sector data for the [[File Explorer]]. This enables browsing DOS 3.3 and ProDOS files on WOZ disks:
|
|
386
|
+
|
|
387
|
+
1. For each track, read nibbles from the bit stream (scanning through twice to handle wrap-around)
|
|
388
|
+
2. Search for address field prologues (`$D5 $AA $96`)
|
|
389
|
+
3. Decode 4-and-4 address fields and verify checksums
|
|
390
|
+
4. Find data field prologues (`$D5 $AA $AD`)
|
|
391
|
+
5. Decode 6-and-2 data fields
|
|
392
|
+
|
|
393
|
+
Decoding is considered successful if at least 50% of sectors (280 of 560) are recovered. This tolerance allows copy-protected disks with intentionally bad sectors to still have their readable content browsed.
|
|
394
|
+
|
|
395
|
+
### Export and Saving
|
|
396
|
+
|
|
397
|
+
WOZ images are exported in WOZ2 format with the following structure:
|
|
398
|
+
|
|
399
|
+
1. 12-byte WOZ2 header
|
|
400
|
+
2. INFO chunk (68 bytes total with header)
|
|
401
|
+
3. TMAP chunk (168 bytes total)
|
|
402
|
+
4. TRKS chunk with 160-entry track table (1288 bytes)
|
|
403
|
+
5. Track bit data starting at block 3 (offset 1536)
|
|
404
|
+
|
|
405
|
+
Blank/unformatted disks can be created with 35 tracks of 51,200 bits each, filled with sync bytes (`$FF`).
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## GCR Encoding
|
|
410
|
+
|
|
411
|
+
Group Coded Recording (GCR) is the encoding scheme used by the Disk II to store data on floppy disks. It ensures that the encoded bit stream maintains the timing constraints required by the hardware: every byte must have its high bit set and must not contain more than two consecutive zero bits.
|
|
412
|
+
|
|
413
|
+
### 6-and-2 Encoding
|
|
414
|
+
|
|
415
|
+
The primary data encoding scheme converts 256 data bytes into 343 disk nibbles (342 encoded + 1 checksum):
|
|
416
|
+
|
|
417
|
+
**Step 1: Build auxiliary buffer (86 bytes)**
|
|
418
|
+
|
|
419
|
+
For each of 86 positions, extract bits 0-1 from up to three data bytes:
|
|
420
|
+
- `buffer[i]` bits 0-1 from `data[i]` (with bit swap)
|
|
421
|
+
- `buffer[i]` bits 2-3 from `data[i+86]` (with bit swap)
|
|
422
|
+
- `buffer[i]` bits 4-5 from `data[i+172]` (with bit swap)
|
|
423
|
+
|
|
424
|
+
**Step 2: Build primary buffer (256 bytes)**
|
|
425
|
+
|
|
426
|
+
Store bits 2-7 of each data byte (shifted right by 2):
|
|
427
|
+
```
|
|
428
|
+
buffer[86+i] = data[i] >> 2
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**Step 3: XOR differential encoding**
|
|
432
|
+
|
|
433
|
+
Each buffer value is XORed with the previous value before encoding:
|
|
434
|
+
```
|
|
435
|
+
encoded[i] = ENCODE_6_AND_2[buffer[i] XOR prev]
|
|
436
|
+
prev = buffer[i]
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
**Step 4: Checksum**
|
|
440
|
+
|
|
441
|
+
The final (343rd) nibble encodes the last pre-XOR buffer value, serving as a checksum.
|
|
442
|
+
|
|
443
|
+
The 6-and-2 lookup table maps 64 possible 6-bit values to 64 valid disk nibbles (values `$96-$FF` with high bit set and no adjacent zeros).
|
|
444
|
+
|
|
445
|
+
### 4-and-4 Encoding
|
|
446
|
+
|
|
447
|
+
Address field values (volume, track, sector, checksum) use a simpler encoding that splits each byte into odd and even bits:
|
|
448
|
+
|
|
449
|
+
```
|
|
450
|
+
odd = 0xAA | (value >> 1) & 0x55 // bits 7,5,3,1 -> positions 6,4,2,0
|
|
451
|
+
even = 0xAA | value & 0x55 // bits 6,4,2,0 -> positions 6,4,2,0
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Each byte becomes two bytes, both guaranteed to have their high bit set.
|
|
455
|
+
|
|
456
|
+
### Sector Structure
|
|
457
|
+
|
|
458
|
+
A complete sector on disk consists of:
|
|
459
|
+
|
|
460
|
+
```
|
|
461
|
+
[Gap: 14+ sync bytes]
|
|
462
|
+
[Address Prologue: D5 AA 96]
|
|
463
|
+
[Volume odd] [Volume even]
|
|
464
|
+
[Track odd] [Track even]
|
|
465
|
+
[Sector odd] [Sector even]
|
|
466
|
+
[Checksum odd] [Checksum even] (checksum = volume XOR track XOR sector)
|
|
467
|
+
[Address Epilogue: DE AA EB]
|
|
468
|
+
[Gap: 6 sync bytes]
|
|
469
|
+
[Data Prologue: D5 AA AD]
|
|
470
|
+
[343 encoded data nibbles]
|
|
471
|
+
[Data Epilogue: DE AA EB]
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
**Marker bytes:**
|
|
475
|
+
|
|
476
|
+
| Marker | Bytes | Purpose |
|
|
477
|
+
|--------|-------|---------|
|
|
478
|
+
| Address prologue | `$D5 $AA $96` | Start of address field |
|
|
479
|
+
| Address epilogue | `$DE $AA $EB` | End of address field |
|
|
480
|
+
| Data prologue | `$D5 $AA $AD` | Start of data field |
|
|
481
|
+
| Data epilogue | `$DE $AA $EB` | End of data field |
|
|
482
|
+
| Sync byte | `$FF` | Self-synchronizing (10 bits on disk) |
|
|
483
|
+
|
|
484
|
+
### Self-Sync Bytes
|
|
485
|
+
|
|
486
|
+
Self-sync bytes are `$FF` values that occupy 10 bits on disk instead of the standard 8 bits. The extra two zero bits allow the read hardware to regain byte synchronization. When the controller encounters a stream of 10-bit `$FF` values, the shift register naturally aligns to byte boundaries because the `$FF` pattern fills all 8 bits with ones regardless of the starting alignment.
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## Write Support
|
|
491
|
+
|
|
492
|
+
Both DSK and WOZ formats support write operations:
|
|
493
|
+
|
|
494
|
+
**DSK writes:**
|
|
495
|
+
1. Nibbles are written directly to the nibble track cache at the current position
|
|
496
|
+
2. The track is marked dirty
|
|
497
|
+
3. On save, dirty tracks are denibblized back to sector data
|
|
498
|
+
4. For bit-level writes (LSS), bits are written to the bit track cache and later converted back through bit-track -> nibble-track -> sector-data
|
|
499
|
+
|
|
500
|
+
**WOZ writes:**
|
|
501
|
+
1. Bits are written directly to the track's packed bit array
|
|
502
|
+
2. For nibble writes, 8 bits are written MSB-first
|
|
503
|
+
3. The modified flag is set for the entire image
|
|
504
|
+
|
|
505
|
+
Write-protected disks reject all write operations. The write-protect status comes from the WOZ INFO chunk or the `write_protected_` flag for DSK images.
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
## State Serialization
|
|
510
|
+
|
|
511
|
+
The Disk II controller state (32 bytes maximum) includes:
|
|
512
|
+
|
|
513
|
+
| Field | Size | Description |
|
|
514
|
+
|-------|------|-------------|
|
|
515
|
+
| Motor on | 1 byte | Motor running flag |
|
|
516
|
+
| Selected drive | 1 byte | 0 or 1 |
|
|
517
|
+
| Q6 | 1 byte | Q6 latch state |
|
|
518
|
+
| Q7 | 1 byte | Q7 latch state |
|
|
519
|
+
| Phase states | 1 byte | 4-bit phase magnet field |
|
|
520
|
+
| Data register | 1 byte | LSS shift register value |
|
|
521
|
+
| Drive 0 quarter-track | 1 byte | Head position for drive 0 |
|
|
522
|
+
| Drive 1 quarter-track | 1 byte | Head position for drive 1 |
|
|
523
|
+
| Sequencer state | 1 byte | 4-bit LSS state |
|
|
524
|
+
| Bus data | 1 byte | Last CPU bus value |
|
|
525
|
+
| LSS clock | 1 byte | 8-phase clock position |
|
|
526
|
+
|
|
527
|
+
Disk image data (sector data and modifications) is saved separately as part of the full emulator state. See [[Save States]] for details.
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
## Source Files
|
|
532
|
+
|
|
533
|
+
| File | Description |
|
|
534
|
+
|------|-------------|
|
|
535
|
+
| `src/core/cards/disk2_card.hpp` | Disk II controller card interface |
|
|
536
|
+
| `src/core/cards/disk2_card.cpp` | Controller implementation, LSS, P6 ROM |
|
|
537
|
+
| `src/core/disk-image/disk_image.hpp` | Abstract disk image base class |
|
|
538
|
+
| `src/core/disk-image/dsk_disk_image.hpp` | DSK format class declaration |
|
|
539
|
+
| `src/core/disk-image/dsk_disk_image.cpp` | DSK nibblization, denibblization, stepper |
|
|
540
|
+
| `src/core/disk-image/woz_disk_image.hpp` | WOZ format class declaration |
|
|
541
|
+
| `src/core/disk-image/woz_disk_image.cpp` | WOZ parsing, bit-level access, sector decoding |
|
|
542
|
+
| `src/core/disk-image/gcr_encoding.hpp` | GCR encoding tables and function declarations |
|
|
543
|
+
| `src/core/disk-image/gcr_encoding.cpp` | 6-and-2 and 4-and-4 encoding implementation |
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
See also: [[Disk Drives]] | [[File Explorer]] | [[Expansion Slots]] | [[Architecture Overview]]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Display Settings
|
|
2
|
+
|
|
3
|
+
The Display Settings window controls all visual effects applied to the emulator screen. Open it from **View > Display** in the toolbar.
|
|
4
|
+
|
|
5
|
+
Every setting is a percentage slider (0-100%) unless otherwise noted. All settings are saved to localStorage and restored automatically on reload. Click **Reset to Defaults** at the bottom of the window to return every setting to its initial value.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [CRT Effects](#crt-effects)
|
|
12
|
+
- [Analog Effects](#analog-effects)
|
|
13
|
+
- [Image Controls](#image-controls)
|
|
14
|
+
- [Rendering Options](#rendering-options)
|
|
15
|
+
- [NTSC Effects](#ntsc-effects)
|
|
16
|
+
- [How the Shader Pipeline Works](#how-the-shader-pipeline-works)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## CRT Effects
|
|
21
|
+
|
|
22
|
+
These settings simulate the physical characteristics of a cathode-ray tube monitor.
|
|
23
|
+
|
|
24
|
+
| Setting | Description |
|
|
25
|
+
|---------|-------------|
|
|
26
|
+
| **Screen Curvature** | Barrel distortion that bows the image outward, mimicking the curved glass of a CRT. At 0% the screen is perfectly flat. |
|
|
27
|
+
| **Screen Border** | Adds a dark border (overscan area) around the display content, simulating the bezel masking seen on real monitors. |
|
|
28
|
+
| **Scanlines** | Horizontal dark lines between each row of pixels. Reproduces the visible gap between phosphor rows on a real CRT. |
|
|
29
|
+
| **Shadow Mask** | A repeating RGB dot pattern overlaid on the image, simulating the aperture grille or shadow mask that separates red, green, and blue phosphor dots. |
|
|
30
|
+
| **Phosphor Glow** | Bloom effect around bright pixels. Simulates the way phosphor coating bleeds light outward on a real tube. |
|
|
31
|
+
| **Vignette** | Darkens the corners and edges of the screen, reproducing the natural brightness falloff at the periphery of a CRT. |
|
|
32
|
+
| **RGB Offset** | Chromatic aberration -- shifts the red, green, and blue colour channels slightly apart, simulating convergence errors in the electron guns. |
|
|
33
|
+
| **Flicker** | Random frame-to-frame brightness variation, reproducing the subtle luminance instability of analog displays. |
|
|
34
|
+
|
|
35
|
+
## Analog Effects
|
|
36
|
+
|
|
37
|
+
These settings simulate signal-path imperfections and environmental characteristics of vintage displays.
|
|
38
|
+
|
|
39
|
+
| Setting | Description |
|
|
40
|
+
|---------|-------------|
|
|
41
|
+
| **Static Noise** | Random grain overlaid on the image, like a slightly noisy video signal. |
|
|
42
|
+
| **Jitter** | Random per-pixel horizontal displacement, simulating timing instability in the video signal. |
|
|
43
|
+
| **Horizontal Sync** | Periodic horizontal distortion bands that slide across the screen, mimicking sync issues in the composite video signal. |
|
|
44
|
+
| **Glowing Line** | A bright horizontal scan beam that sweeps down the screen, reproducing the visible raster scan of a real CRT's electron beam. |
|
|
45
|
+
| **Ambient Light** | Simulates room light reflecting off the glass surface of the monitor. Adds a subtle lightening to the screen. |
|
|
46
|
+
| **Burn In** | Phosphor persistence -- bright areas leave a fading afterimage. The emulator maintains a separate framebuffer that accumulates and decays burn-in over time. |
|
|
47
|
+
|
|
48
|
+
## Image Controls
|
|
49
|
+
|
|
50
|
+
Basic picture adjustments that apply on top of all CRT and analog effects.
|
|
51
|
+
|
|
52
|
+
| Setting | Default | Description |
|
|
53
|
+
|---------|---------|-------------|
|
|
54
|
+
| **Brightness** | 100% | Overall luminance. Maps to a 0.5-1.5 multiplier in the shader. |
|
|
55
|
+
| **Contrast** | 100% | Difference between dark and light areas. Also maps to a 0.5-1.5 range. |
|
|
56
|
+
| **Saturation** | 100% | Colour intensity. At 0% the image is greyscale; at 200% colours are vivid. |
|
|
57
|
+
|
|
58
|
+
## Rendering Options
|
|
59
|
+
|
|
60
|
+
| Setting | Description |
|
|
61
|
+
|---------|-------------|
|
|
62
|
+
| **Display Mode** | Dropdown with four choices: **Color** (full NTSC colour), **Green** (green phosphor monochrome), **Amber** (amber phosphor), or **White** (white phosphor). Monochrome modes bypass NTSC colour artifacts and render a single-channel image tinted to match the selected phosphor colour. |
|
|
63
|
+
| **Sharp Pixels** | Toggle. When enabled, the WebGL texture uses nearest-neighbour filtering instead of bilinear interpolation, producing crisp pixel edges with no smoothing. When disabled (default), pixels are softly blended. |
|
|
64
|
+
|
|
65
|
+
## NTSC Effects
|
|
66
|
+
|
|
67
|
+
These shader-based effects simulate characteristics of the NTSC composite video encoding used by the Apple IIe.
|
|
68
|
+
|
|
69
|
+
| Setting | Description |
|
|
70
|
+
|---------|-------------|
|
|
71
|
+
| **Color Bleed** | Vertical inter-scanline colour blending. Simulates the way CRT phosphor rows overlap slightly, causing colour from one scanline to bleed into its neighbours. |
|
|
72
|
+
| **NTSC Fringing** | Colour fringing at sharp horizontal edges. Reproduces the magenta/cyan fringes caused by NTSC chroma bandwidth limiting, visible on real hardware at transitions between black and white pixels. |
|
|
73
|
+
|
|
74
|
+
## How the Shader Pipeline Works
|
|
75
|
+
|
|
76
|
+
The display passes through a multi-stage WebGL shader pipeline:
|
|
77
|
+
|
|
78
|
+
1. **Source texture** -- The C++ video renderer produces a 560x384 RGBA framebuffer (double the native 280x192 resolution). This is uploaded to a WebGL texture each frame.
|
|
79
|
+
|
|
80
|
+
2. **CRT fragment shader** (`crt.glsl`) -- Applies curvature, scanlines, shadow mask, phosphor glow, vignette, chromatic aberration, flicker, static noise, jitter, horizontal sync, glowing line, ambient light, colour bleed, NTSC fringing, monochrome tinting, screen border, and rounded corners in a single pass.
|
|
81
|
+
|
|
82
|
+
3. **Burn-in pass** (`burnin.glsl`) -- A separate framebuffer accumulates bright pixel values over time and decays them, creating the phosphor persistence effect. This texture is blended back into the main image during the CRT pass.
|
|
83
|
+
|
|
84
|
+
4. **Edge overlay pass** (`edge.glsl`) -- Draws a subtle highlight around the screen border to simulate light catching the edge of the CRT glass.
|
|
85
|
+
|
|
86
|
+
All shader parameters are set as WebGL uniforms and update in real time as you drag the sliders. An animated `time` uniform drives effects like static noise, flicker, jitter, and the glowing scan line.
|
|
87
|
+
|
|
88
|
+
Settings are stored in `localStorage` under the key `a2e-display-settings` as a JSON object. They persist across sessions and are independent of save states.
|