c64-kb 0.17.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/CHANGELOG.md +2082 -0
- package/LICENSE +29 -0
- package/README.md +559 -0
- package/VERSION +25 -0
- package/dist/cli/setup.js +66 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli-memorize-audit.js +97 -0
- package/dist/cli-memorize-audit.js.map +1 -0
- package/dist/cli.js +335 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +83 -0
- package/dist/config.js.map +1 -0
- package/dist/context.js +53 -0
- package/dist/context.js.map +1 -0
- package/dist/domain/budget-notes.js +104 -0
- package/dist/domain/budget-notes.js.map +1 -0
- package/dist/domain/budget.js +417 -0
- package/dist/domain/budget.js.map +1 -0
- package/dist/domain/game-design.js +77 -0
- package/dist/domain/game-design.js.map +1 -0
- package/dist/domain/timing.js +39 -0
- package/dist/domain/timing.js.map +1 -0
- package/dist/graph/apply.js +132 -0
- package/dist/graph/apply.js.map +1 -0
- package/dist/graph/claims.js +231 -0
- package/dist/graph/claims.js.map +1 -0
- package/dist/graph/extract/archetype.js +118 -0
- package/dist/graph/extract/archetype.js.map +1 -0
- package/dist/graph/extract/common.js +69 -0
- package/dist/graph/extract/common.js.map +1 -0
- package/dist/graph/extract/failure.js +37 -0
- package/dist/graph/extract/failure.js.map +1 -0
- package/dist/graph/extract/game-design.js +189 -0
- package/dist/graph/extract/game-design.js.map +1 -0
- package/dist/graph/extract/hardware.js +124 -0
- package/dist/graph/extract/hardware.js.map +1 -0
- package/dist/graph/extract/pitfall.js +62 -0
- package/dist/graph/extract/pitfall.js.map +1 -0
- package/dist/graph/extract/raster-band.js +56 -0
- package/dist/graph/extract/raster-band.js.map +1 -0
- package/dist/graph/extract/recipe.js +71 -0
- package/dist/graph/extract/recipe.js.map +1 -0
- package/dist/graph/extract/technique-entities.js +180 -0
- package/dist/graph/extract/technique-entities.js.map +1 -0
- package/dist/graph/extract/technique.js +179 -0
- package/dist/graph/extract/technique.js.map +1 -0
- package/dist/graph/extract/toolchain.js +67 -0
- package/dist/graph/extract/toolchain.js.map +1 -0
- package/dist/graph/extract/types.js +3 -0
- package/dist/graph/extract/types.js.map +1 -0
- package/dist/graph/extract/vocabulary.js +65 -0
- package/dist/graph/extract/vocabulary.js.map +1 -0
- package/dist/graph/extract.js +43 -0
- package/dist/graph/extract.js.map +1 -0
- package/dist/graph/kernal-clobbers.js +64 -0
- package/dist/graph/kernal-clobbers.js.map +1 -0
- package/dist/graph/machine-variants.js +208 -0
- package/dist/graph/machine-variants.js.map +1 -0
- package/dist/ingest/files.js +99 -0
- package/dist/ingest/files.js.map +1 -0
- package/dist/ingest/passes.js +104 -0
- package/dist/ingest/passes.js.map +1 -0
- package/dist/ingest/points.js +50 -0
- package/dist/ingest/points.js.map +1 -0
- package/dist/ingest/report.js +140 -0
- package/dist/ingest/report.js.map +1 -0
- package/dist/ingest/tally.js +81 -0
- package/dist/ingest/tally.js.map +1 -0
- package/dist/ingest/verified-on.js +43 -0
- package/dist/ingest/verified-on.js.map +1 -0
- package/dist/ingest.js +135 -0
- package/dist/ingest.js.map +1 -0
- package/dist/schemas/cost-basis.js +5 -0
- package/dist/schemas/cost-basis.js.map +1 -0
- package/dist/schemas/machine-variant.js +25 -0
- package/dist/schemas/machine-variant.js.map +1 -0
- package/dist/schemas/plan-budget.js +121 -0
- package/dist/schemas/plan-budget.js.map +1 -0
- package/dist/schemas/tool-outputs.js +526 -0
- package/dist/schemas/tool-outputs.js.map +1 -0
- package/dist/server/define-tool.js +52 -0
- package/dist/server/define-tool.js.map +1 -0
- package/dist/server/defined-only.js +9 -0
- package/dist/server/defined-only.js.map +1 -0
- package/dist/server/tools-briefings.js +70 -0
- package/dist/server/tools-briefings.js.map +1 -0
- package/dist/server/tools-lookup.js +180 -0
- package/dist/server/tools-lookup.js.map +1 -0
- package/dist/server/tools-maintenance.js +116 -0
- package/dist/server/tools-maintenance.js.map +1 -0
- package/dist/server/tools-pitfalls.js +89 -0
- package/dist/server/tools-pitfalls.js.map +1 -0
- package/dist/server/tools-recipes.js +296 -0
- package/dist/server/tools-recipes.js.map +1 -0
- package/dist/server/tools-runtime.js +48 -0
- package/dist/server/tools-runtime.js.map +1 -0
- package/dist/server/tools.js +43 -0
- package/dist/server/tools.js.map +1 -0
- package/dist/server.js +95 -0
- package/dist/server.js.map +1 -0
- package/dist/services/analytics/schema.js +78 -0
- package/dist/services/analytics/schema.js.map +1 -0
- package/dist/services/analytics/statements.js +99 -0
- package/dist/services/analytics/statements.js.map +1 -0
- package/dist/services/analytics.js +195 -0
- package/dist/services/analytics.js.map +1 -0
- package/dist/services/bm25.js +158 -0
- package/dist/services/bm25.js.map +1 -0
- package/dist/services/chunker.js +81 -0
- package/dist/services/chunker.js.map +1 -0
- package/dist/services/embeddings.js +58 -0
- package/dist/services/embeddings.js.map +1 -0
- package/dist/services/falkor/base.js +85 -0
- package/dist/services/falkor/base.js.map +1 -0
- package/dist/services/falkor/links.js +361 -0
- package/dist/services/falkor/links.js.map +1 -0
- package/dist/services/falkor/nodes.js +183 -0
- package/dist/services/falkor/nodes.js.map +1 -0
- package/dist/services/falkor/params.js +45 -0
- package/dist/services/falkor/params.js.map +1 -0
- package/dist/services/falkor/schema.js +77 -0
- package/dist/services/falkor/schema.js.map +1 -0
- package/dist/services/falkor.js +100 -0
- package/dist/services/falkor.js.map +1 -0
- package/dist/services/qdrant.js +238 -0
- package/dist/services/qdrant.js.map +1 -0
- package/dist/services/versions.js +73 -0
- package/dist/services/versions.js.map +1 -0
- package/dist/services/vice-bin.js +51 -0
- package/dist/services/vice-bin.js.map +1 -0
- package/dist/tools/briefings/archetype.js +174 -0
- package/dist/tools/briefings/archetype.js.map +1 -0
- package/dist/tools/briefings/budget.js +177 -0
- package/dist/tools/briefings/budget.js.map +1 -0
- package/dist/tools/briefings/build-order.js +135 -0
- package/dist/tools/briefings/build-order.js.map +1 -0
- package/dist/tools/briefings/build.js +185 -0
- package/dist/tools/briefings/build.js.map +1 -0
- package/dist/tools/briefings/discovery.js +290 -0
- package/dist/tools/briefings/discovery.js.map +1 -0
- package/dist/tools/briefings/plan-pitfalls.js +69 -0
- package/dist/tools/briefings/plan-pitfalls.js.map +1 -0
- package/dist/tools/briefings/render.js +151 -0
- package/dist/tools/briefings/render.js.map +1 -0
- package/dist/tools/briefings/rows.js +41 -0
- package/dist/tools/briefings/rows.js.map +1 -0
- package/dist/tools/briefings/toolchain.js +63 -0
- package/dist/tools/briefings/toolchain.js.map +1 -0
- package/dist/tools/briefings/why-proposed.js +69 -0
- package/dist/tools/briefings/why-proposed.js.map +1 -0
- package/dist/tools/briefings.js +44 -0
- package/dist/tools/briefings.js.map +1 -0
- package/dist/tools/feedback.js +97 -0
- package/dist/tools/feedback.js.map +1 -0
- package/dist/tools/hydrate.js +75 -0
- package/dist/tools/hydrate.js.map +1 -0
- package/dist/tools/intelligence.js +67 -0
- package/dist/tools/intelligence.js.map +1 -0
- package/dist/tools/lint/asm-decimal.js +86 -0
- package/dist/tools/lint/asm-decimal.js.map +1 -0
- package/dist/tools/lint/asm-rules.js +221 -0
- package/dist/tools/lint/asm-rules.js.map +1 -0
- package/dist/tools/lint/asm-shared.js +3 -0
- package/dist/tools/lint/asm-shared.js.map +1 -0
- package/dist/tools/lint/c-rules.js +215 -0
- package/dist/tools/lint/c-rules.js.map +1 -0
- package/dist/tools/lint/text.js +122 -0
- package/dist/tools/lint/text.js.map +1 -0
- package/dist/tools/lint/types.js +40 -0
- package/dist/tools/lint/types.js.map +1 -0
- package/dist/tools/lint.js +76 -0
- package/dist/tools/lint.js.map +1 -0
- package/dist/tools/memorization-mcp.js +111 -0
- package/dist/tools/memorization-mcp.js.map +1 -0
- package/dist/tools/pitfalls/bm25.js +53 -0
- package/dist/tools/pitfalls/bm25.js.map +1 -0
- package/dist/tools/pitfalls/format.js +58 -0
- package/dist/tools/pitfalls/format.js.map +1 -0
- package/dist/tools/pitfalls/graph.js +124 -0
- package/dist/tools/pitfalls/graph.js.map +1 -0
- package/dist/tools/pitfalls.js +134 -0
- package/dist/tools/pitfalls.js.map +1 -0
- package/dist/tools/prompts.js +81 -0
- package/dist/tools/prompts.js.map +1 -0
- package/dist/tools/query/compatibility/closure.js +62 -0
- package/dist/tools/query/compatibility/closure.js.map +1 -0
- package/dist/tools/query/compatibility/facts.js +28 -0
- package/dist/tools/query/compatibility/facts.js.map +1 -0
- package/dist/tools/query/compatibility/fetch.js +164 -0
- package/dist/tools/query/compatibility/fetch.js.map +1 -0
- package/dist/tools/query/compatibility/hard-rules.js +159 -0
- package/dist/tools/query/compatibility/hard-rules.js.map +1 -0
- package/dist/tools/query/compatibility/index.js +27 -0
- package/dist/tools/query/compatibility/index.js.map +1 -0
- package/dist/tools/query/compatibility/kernal-zp-rule.js +90 -0
- package/dist/tools/query/compatibility/kernal-zp-rule.js.map +1 -0
- package/dist/tools/query/compatibility/render.js +106 -0
- package/dist/tools/query/compatibility/render.js.map +1 -0
- package/dist/tools/query/compatibility/rules.js +337 -0
- package/dist/tools/query/compatibility/rules.js.map +1 -0
- package/dist/tools/query/compatibility/unit-rules.js +253 -0
- package/dist/tools/query/compatibility/unit-rules.js.map +1 -0
- package/dist/tools/query/game-design.js +83 -0
- package/dist/tools/query/game-design.js.map +1 -0
- package/dist/tools/query/lookups.js +220 -0
- package/dist/tools/query/lookups.js.map +1 -0
- package/dist/tools/query/plan-budget.js +266 -0
- package/dist/tools/query/plan-budget.js.map +1 -0
- package/dist/tools/query/recipes.js +214 -0
- package/dist/tools/query/recipes.js.map +1 -0
- package/dist/tools/query/retrieval.js +128 -0
- package/dist/tools/query/retrieval.js.map +1 -0
- package/dist/tools/query/shared.js +121 -0
- package/dist/tools/query/shared.js.map +1 -0
- package/dist/tools/query/techniques.js +331 -0
- package/dist/tools/query/techniques.js.map +1 -0
- package/dist/tools/query/timing.js +106 -0
- package/dist/tools/query/timing.js.map +1 -0
- package/dist/tools/query/types.js +11 -0
- package/dist/tools/query/types.js.map +1 -0
- package/dist/tools/query.js +21 -0
- package/dist/tools/query.js.map +1 -0
- package/dist/tools/resources.js +186 -0
- package/dist/tools/resources.js.map +1 -0
- package/dist/tools/run-game.js +249 -0
- package/dist/tools/run-game.js.map +1 -0
- package/dist/tools/selfimprovement.js +367 -0
- package/dist/tools/selfimprovement.js.map +1 -0
- package/docker-compose.yml +35 -0
- package/docs/ARCHITECTURE.md +164 -0
- package/docs/CONVENTIONS-archetypes.md +158 -0
- package/docs/CONVENTIONS-failures.md +46 -0
- package/docs/CONVENTIONS-game-designs.md +75 -0
- package/docs/CONVENTIONS-hardware-reference.md +184 -0
- package/docs/CONVENTIONS-pitfalls.md +91 -0
- package/docs/CONVENTIONS-recipes.md +106 -0
- package/docs/CONVENTIONS-techniques.md +309 -0
- package/docs/CONVENTIONS-toolchain-reference.md +89 -0
- package/docs/ONTOLOGY.md +655 -0
- package/docs/art/art-production-reference.md +157 -0
- package/docs/art/asset-pipelines.md +586 -0
- package/docs/c64-failure-patterns.md +693 -0
- package/docs/demo-design/demo-composition.md +349 -0
- package/docs/demo-design/demo-design-philosophy.md +320 -0
- package/docs/demo-design/intro-cracktro-patterns.md +435 -0
- package/docs/figures/ctm-spd-embed-probe.png +0 -0
- package/docs/figures/dot-flag-mid-5000000.png +0 -0
- package/docs/figures/dot-flag-no-erase-12000000.png +0 -0
- package/docs/figures/edges.json +1 -0
- package/docs/figures/fig6-architecture.png +0 -0
- package/docs/figures/fig7-ontology.png +0 -0
- package/docs/figures/screen-dissolve-mid-frame25.png +0 -0
- package/docs/figures/screen-dissolve-wipe-frame25.png +0 -0
- package/docs/figures/spindle-two-parts-part1.png +0 -0
- package/docs/figures/spindle-two-parts-part2.png +0 -0
- package/docs/figures/starters/action-puzzle.png +0 -0
- package/docs/figures/starters/adventure.png +0 -0
- package/docs/figures/starters/beat-em-up.png +0 -0
- package/docs/figures/starters/demo.png +0 -0
- package/docs/figures/starters/platformer.png +0 -0
- package/docs/figures/starters/shmup-vertical.png +0 -0
- package/docs/figures/tape-kernal-master-ntsc.png +0 -0
- package/docs/figures/tape-kernal-master-pal.png +0 -0
- package/docs/figures/tape-turbo-loader-ntsc.png +0 -0
- package/docs/figures/tape-turbo-loader-pal.png +0 -0
- package/docs/figures/tape-turbo-master-ntsc.png +0 -0
- package/docs/figures/tape-turbo-master-pal.png +0 -0
- package/docs/figures/tech-tech-pad2-12000000.png +0 -0
- package/docs/figures/tech-tech-pad4-12000000.png +0 -0
- package/docs/figures/tech-tech-seven-12000000.png +0 -0
- package/docs/figures/twister-mid-5000000.png +0 -0
- package/docs/figures/twister-straight-12000000.png +0 -0
- package/docs/figures/vector-balls-nosort-ntsc.png +0 -0
- package/docs/figures/vector-balls-nosort.png +0 -0
- package/docs/formats/c64-file-formats.md +1212 -0
- package/docs/formats/iec-disk-reference.md +754 -0
- package/docs/game-design/c64-game-archetypes.md +273 -0
- package/docs/game-design/designs/falling-blocks.md +54 -0
- package/docs/game-design/designs/platformer-scaffold.md +48 -0
- package/docs/game-design/designs/simple-shmup.md +27 -0
- package/docs/game-design/enemy-behaviour-and-difficulty.md +349 -0
- package/docs/game-design/game-design-patterns.md +1189 -0
- package/docs/game-design/game-structure.md +354 -0
- package/docs/game-design/production-planning.md +349 -0
- package/docs/game-design/reference-game-sources.md +90 -0
- package/docs/hardware/6502-illegal-opcodes.md +1353 -0
- package/docs/hardware/6510-cpu-reference.md +2112 -0
- package/docs/hardware/c64-memory-map.md +3184 -0
- package/docs/hardware/c64-registers-reference.md +996 -0
- package/docs/hardware/cia-reference.md +1281 -0
- package/docs/hardware/kernal-routines-reference.md +1836 -0
- package/docs/hardware/pal-ntsc-reference.md +813 -0
- package/docs/hardware/sid-reference.md +1137 -0
- package/docs/hardware/vic-ii-reference.md +1714 -0
- package/docs/music/music-production-reference.md +403 -0
- package/docs/pitfalls/banking.md +1067 -0
- package/docs/pitfalls/cia.md +1147 -0
- package/docs/pitfalls/cpu.md +874 -0
- package/docs/pitfalls/input.md +282 -0
- package/docs/pitfalls/kernal-and-io.md +1615 -0
- package/docs/pitfalls/loader.md +1191 -0
- package/docs/pitfalls/logic.md +94 -0
- package/docs/pitfalls/maths.md +187 -0
- package/docs/pitfalls/raster-and-badline.md +721 -0
- package/docs/pitfalls/region-timing.md +660 -0
- package/docs/pitfalls/scroll.md +207 -0
- package/docs/pitfalls/sid.md +738 -0
- package/docs/pitfalls/sprite.md +979 -0
- package/docs/pitfalls/text-mode-render.md +1172 -0
- package/docs/recipes/cc65/cartridge-8k.md +398 -0
- package/docs/recipes/cc65/hello-world-conio.md +76 -0
- package/docs/recipes/cc65/memory-layout.md +312 -0
- package/docs/recipes/cc65/screenshots/README.md +14 -0
- package/docs/recipes/cc65/screenshots/cartridge-8k-ntsc.png +0 -0
- package/docs/recipes/cc65/screenshots/cartridge-8k.png +0 -0
- package/docs/recipes/cc65/screenshots/hello-world-conio.png +0 -0
- package/docs/recipes/cc65/screenshots/memory-layout-ntsc.png +0 -0
- package/docs/recipes/cc65/screenshots/memory-layout.png +0 -0
- package/docs/recipes/kickassembler/basic-float-calls.md +578 -0
- package/docs/recipes/kickassembler/basic-wedge.md +398 -0
- package/docs/recipes/kickassembler/big-font-scroller.md +620 -0
- package/docs/recipes/kickassembler/charset-copy-rom-to-ram.md +623 -0
- package/docs/recipes/kickassembler/cia-revision-detect.md +366 -0
- package/docs/recipes/kickassembler/colour-cycling.md +343 -0
- package/docs/recipes/kickassembler/colour-fade.md +303 -0
- package/docs/recipes/kickassembler/compare-16bit-signed.md +1007 -0
- package/docs/recipes/kickassembler/cracktro-template.md +526 -0
- package/docs/recipes/kickassembler/crt-banked.md +276 -0
- package/docs/recipes/kickassembler/dos-error-codes.md +466 -0
- package/docs/recipes/kickassembler/dot-flag.md +576 -0
- package/docs/recipes/kickassembler/drive-job-queue.md +667 -0
- package/docs/recipes/kickassembler/drive-via-probe.md +690 -0
- package/docs/recipes/kickassembler/dycp-scroller.md +568 -0
- package/docs/recipes/kickassembler/dypp-sprite-scroller.md +681 -0
- package/docs/recipes/kickassembler/dysp.md +670 -0
- package/docs/recipes/kickassembler/easyflash-save.md +800 -0
- package/docs/recipes/kickassembler/eight-way-scroll.md +1484 -0
- package/docs/recipes/kickassembler/file-io-roundtrip.md +546 -0
- package/docs/recipes/kickassembler/fire-effect.md +420 -0
- package/docs/recipes/kickassembler/fld.md +414 -0
- package/docs/recipes/kickassembler/fli-image.md +336 -0
- package/docs/recipes/kickassembler/four-player-read.md +672 -0
- package/docs/recipes/kickassembler/headless-verify.md +228 -0
- package/docs/recipes/kickassembler/hello-world.md +98 -0
- package/docs/recipes/kickassembler/high-score-insert.md +660 -0
- package/docs/recipes/kickassembler/hires-plot-line.md +723 -0
- package/docs/recipes/kickassembler/irq-chain.md +316 -0
- package/docs/recipes/kickassembler/irq-owns-port.md +668 -0
- package/docs/recipes/kickassembler/isometric-room.md +1145 -0
- package/docs/recipes/kickassembler/light-pen-read.md +926 -0
- package/docs/recipes/kickassembler/logic-rate-decoupling.md +670 -0
- package/docs/recipes/kickassembler/mci-interlace.md +543 -0
- package/docs/recipes/kickassembler/memory-layout.md +277 -0
- package/docs/recipes/kickassembler/mouse-1351-read.md +801 -0
- package/docs/recipes/kickassembler/nmi-timer-tick.md +562 -0
- package/docs/recipes/kickassembler/own-keyscan.md +881 -0
- package/docs/recipes/kickassembler/paddle-read.md +751 -0
- package/docs/recipes/kickassembler/palette-cells.md +219 -0
- package/docs/recipes/kickassembler/pseudo-3d-road.md +930 -0
- package/docs/recipes/kickassembler/pwm-digi.md +463 -0
- package/docs/recipes/kickassembler/raster-bars.md +264 -0
- package/docs/recipes/kickassembler/reu-dma.md +1019 -0
- package/docs/recipes/kickassembler/runtime-relocation.md +743 -0
- package/docs/recipes/kickassembler/screen-dissolve.md +545 -0
- package/docs/recipes/kickassembler/screen-wipe.md +408 -0
- package/docs/recipes/kickassembler/screenshots/README.md +72 -0
- package/docs/recipes/kickassembler/screenshots/basic-float-calls-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/basic-float-calls.png +0 -0
- package/docs/recipes/kickassembler/screenshots/basic-wedge-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/basic-wedge.png +0 -0
- package/docs/recipes/kickassembler/screenshots/big-font-scroller-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/big-font-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/charset-copy-rom-to-ram-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/charset-copy-rom-to-ram.png +0 -0
- package/docs/recipes/kickassembler/screenshots/cia-revision-detect-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/cia-revision-detect.png +0 -0
- package/docs/recipes/kickassembler/screenshots/colour-cycling-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/colour-cycling.png +0 -0
- package/docs/recipes/kickassembler/screenshots/colour-fade-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/colour-fade.png +0 -0
- package/docs/recipes/kickassembler/screenshots/compare-16bit-signed-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/compare-16bit-signed.png +0 -0
- package/docs/recipes/kickassembler/screenshots/cracktro-template.png +0 -0
- package/docs/recipes/kickassembler/screenshots/crt-banked-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/crt-banked.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dos-error-codes-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dos-error-codes.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dot-flag-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dot-flag.png +0 -0
- package/docs/recipes/kickassembler/screenshots/drive-job-queue-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/drive-job-queue.png +0 -0
- package/docs/recipes/kickassembler/screenshots/drive-via-probe-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/drive-via-probe.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dycp-scroller-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dycp-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dypp-sprite-scroller-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dypp-sprite-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dysp-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dysp.png +0 -0
- package/docs/recipes/kickassembler/screenshots/easyflash-save-ntsc-run2.png +0 -0
- package/docs/recipes/kickassembler/screenshots/easyflash-save-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/easyflash-save-run2.png +0 -0
- package/docs/recipes/kickassembler/screenshots/easyflash-save.png +0 -0
- package/docs/recipes/kickassembler/screenshots/eight-way-scroll-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/eight-way-scroll.png +0 -0
- package/docs/recipes/kickassembler/screenshots/file-io-roundtrip-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/file-io-roundtrip.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fire-effect-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fire-effect.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fld-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fld.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fli-image.png +0 -0
- package/docs/recipes/kickassembler/screenshots/four-player-read-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/four-player-read.png +0 -0
- package/docs/recipes/kickassembler/screenshots/headless-verify-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/headless-verify.png +0 -0
- package/docs/recipes/kickassembler/screenshots/hello-world.png +0 -0
- package/docs/recipes/kickassembler/screenshots/high-score-insert-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/high-score-insert.png +0 -0
- package/docs/recipes/kickassembler/screenshots/hires-plot-line-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/hires-plot-line.png +0 -0
- package/docs/recipes/kickassembler/screenshots/irq-chain-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/irq-chain.png +0 -0
- package/docs/recipes/kickassembler/screenshots/irq-owns-port-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/irq-owns-port.png +0 -0
- package/docs/recipes/kickassembler/screenshots/isometric-room-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/isometric-room.png +0 -0
- package/docs/recipes/kickassembler/screenshots/light-pen-read-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/light-pen-read.png +0 -0
- package/docs/recipes/kickassembler/screenshots/logic-rate-decoupling-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/logic-rate-decoupling.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mci-interlace-average.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mci-interlace-frame-b.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mci-interlace-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mci-interlace.png +0 -0
- package/docs/recipes/kickassembler/screenshots/memory-layout-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/memory-layout.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mouse-1351-read-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mouse-1351-read.png +0 -0
- package/docs/recipes/kickassembler/screenshots/nmi-timer-tick-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/nmi-timer-tick.png +0 -0
- package/docs/recipes/kickassembler/screenshots/own-keyscan-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/own-keyscan.png +0 -0
- package/docs/recipes/kickassembler/screenshots/paddle-read-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/paddle-read.png +0 -0
- package/docs/recipes/kickassembler/screenshots/palette-cells-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/palette-cells.png +0 -0
- package/docs/recipes/kickassembler/screenshots/pseudo-3d-road-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/pseudo-3d-road.png +0 -0
- package/docs/recipes/kickassembler/screenshots/pwm-digi-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/pwm-digi.png +0 -0
- package/docs/recipes/kickassembler/screenshots/raster-bars.png +0 -0
- package/docs/recipes/kickassembler/screenshots/reu-dma-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/reu-dma.png +0 -0
- package/docs/recipes/kickassembler/screenshots/runtime-relocation-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/runtime-relocation.png +0 -0
- package/docs/recipes/kickassembler/screenshots/screen-dissolve-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/screen-dissolve.png +0 -0
- package/docs/recipes/kickassembler/screenshots/screen-wipe-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/screen-wipe.png +0 -0
- package/docs/recipes/kickassembler/screenshots/scroll-panel-split-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/scroll-panel-split.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sfx-in-player-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sfx-in-player.png +0 -0
- package/docs/recipes/kickassembler/screenshots/shadebobs-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/shadebobs.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sideborder-open.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sine-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sine-table-runtime-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sine-table-runtime.png +0 -0
- package/docs/recipes/kickassembler/screenshots/software-sprite-preshifted-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/software-sprite-preshifted.png +0 -0
- package/docs/recipes/kickassembler/screenshots/speedcode-generator-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/speedcode-generator.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-border-scroller-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-border-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-cache-flip-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-cache-flip.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-multiplex-24.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-multiplex-game-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-multiplex-game.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-priority-classes-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-priority-classes.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-sine-chain-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-sine-chain.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-stretcher-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-stretcher.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprites-only-screen-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprites-only-screen.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sqrt-atan2-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sqrt-atan2.png +0 -0
- package/docs/recipes/kickassembler/screenshots/stable-raster-irq-control.png +0 -0
- package/docs/recipes/kickassembler/screenshots/stable-raster-irq.png +0 -0
- package/docs/recipes/kickassembler/screenshots/tech-tech-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/tech-tech.png +0 -0
- package/docs/recipes/kickassembler/screenshots/tod-alarm-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/tod-alarm.png +0 -0
- package/docs/recipes/kickassembler/screenshots/topbottom-border-open.png +0 -0
- package/docs/recipes/kickassembler/screenshots/twister-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/twister.png +0 -0
- package/docs/recipes/kickassembler/screenshots/unit-test-driver-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/unit-test-driver.png +0 -0
- package/docs/recipes/kickassembler/screenshots/vector-balls-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/vector-balls.png +0 -0
- package/docs/recipes/kickassembler/screenshots/wireframe-ships-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/wireframe-ships.png +0 -0
- package/docs/recipes/kickassembler/scroll-panel-split.md +381 -0
- package/docs/recipes/kickassembler/sfx-in-player.md +907 -0
- package/docs/recipes/kickassembler/shadebobs.md +522 -0
- package/docs/recipes/kickassembler/sideborder-open.md +338 -0
- package/docs/recipes/kickassembler/sine-scroller.md +321 -0
- package/docs/recipes/kickassembler/sine-table-runtime.md +815 -0
- package/docs/recipes/kickassembler/software-sprite-preshifted.md +725 -0
- package/docs/recipes/kickassembler/speedcode-generator.md +606 -0
- package/docs/recipes/kickassembler/sprite-border-scroller.md +696 -0
- package/docs/recipes/kickassembler/sprite-cache-flip.md +1071 -0
- package/docs/recipes/kickassembler/sprite-multiplex-24.md +326 -0
- package/docs/recipes/kickassembler/sprite-multiplex-game.md +986 -0
- package/docs/recipes/kickassembler/sprite-priority-classes.md +463 -0
- package/docs/recipes/kickassembler/sprite-sine-chain.md +338 -0
- package/docs/recipes/kickassembler/sprite-stretcher.md +501 -0
- package/docs/recipes/kickassembler/sprites-only-screen.md +550 -0
- package/docs/recipes/kickassembler/sqrt-atan2.md +1014 -0
- package/docs/recipes/kickassembler/stable-raster-irq.md +360 -0
- package/docs/recipes/kickassembler/tape-turbo-loader.md +875 -0
- package/docs/recipes/kickassembler/tech-tech.md +679 -0
- package/docs/recipes/kickassembler/tod-alarm.md +754 -0
- package/docs/recipes/kickassembler/topbottom-border-open.md +407 -0
- package/docs/recipes/kickassembler/twister.md +518 -0
- package/docs/recipes/kickassembler/unit-test-driver.md +314 -0
- package/docs/recipes/kickassembler/vector-balls.md +545 -0
- package/docs/recipes/kickassembler/wireframe-ships.md +2324 -0
- package/docs/recipes/oscar64/actor-activation-window.md +807 -0
- package/docs/recipes/oscar64/adventure-engine.md +1094 -0
- package/docs/recipes/oscar64/attract-replay.md +465 -0
- package/docs/recipes/oscar64/beat-em-up-lanes.md +563 -0
- package/docs/recipes/oscar64/bfs-distance-map.md +723 -0
- package/docs/recipes/oscar64/bitmap-koala-viewer.md +355 -0
- package/docs/recipes/oscar64/car-contact.md +950 -0
- package/docs/recipes/oscar64/cave-scan.md +609 -0
- package/docs/recipes/oscar64/char-bullets.md +477 -0
- package/docs/recipes/oscar64/charset-animation.md +383 -0
- package/docs/recipes/oscar64/charset-parallax.md +401 -0
- package/docs/recipes/oscar64/destructible-terrain.md +1099 -0
- package/docs/recipes/oscar64/difficulty-tables.md +654 -0
- package/docs/recipes/oscar64/dig-and-guards.md +975 -0
- package/docs/recipes/oscar64/directory-reader.md +535 -0
- package/docs/recipes/oscar64/divide-check.md +581 -0
- package/docs/recipes/oscar64/double-buffer-nomirror.md +205 -0
- package/docs/recipes/oscar64/double-buffer.md +255 -0
- package/docs/recipes/oscar64/falling-blocks.md +874 -0
- package/docs/recipes/oscar64/fixed-point-jump-velocity.md +289 -0
- package/docs/recipes/oscar64/fixed-point-jump.md +210 -0
- package/docs/recipes/oscar64/flip-screen-rooms.md +793 -0
- package/docs/recipes/oscar64/frame-sync-loop-overrun.md +214 -0
- package/docs/recipes/oscar64/frame-sync-loop.md +254 -0
- package/docs/recipes/oscar64/game-tree-search.md +743 -0
- package/docs/recipes/oscar64/ghost-targeting.md +867 -0
- package/docs/recipes/oscar64/headless-verify.md +345 -0
- package/docs/recipes/oscar64/hello-world.md +83 -0
- package/docs/recipes/oscar64/high-score-persist.md +772 -0
- package/docs/recipes/oscar64/iffl-kernal-skip.md +421 -0
- package/docs/recipes/oscar64/joystick-input.md +358 -0
- package/docs/recipes/oscar64/lane-pursuit.md +1028 -0
- package/docs/recipes/oscar64/level-rle-decoder.md +620 -0
- package/docs/recipes/oscar64/lfsr-random-seed2.md +338 -0
- package/docs/recipes/oscar64/lfsr-random.md +403 -0
- package/docs/recipes/oscar64/load-asset-runtime.md +492 -0
- package/docs/recipes/oscar64/memory-layout.md +232 -0
- package/docs/recipes/oscar64/mixed-fighters.md +781 -0
- package/docs/recipes/oscar64/multi-sprite-object.md +533 -0
- package/docs/recipes/oscar64/nav-area-pathfinding.md +881 -0
- package/docs/recipes/oscar64/object-pool.md +572 -0
- package/docs/recipes/oscar64/pal-ntsc-detect.md +257 -0
- package/docs/recipes/oscar64/password-state.md +532 -0
- package/docs/recipes/oscar64/per-frame-hitbox.md +651 -0
- package/docs/recipes/oscar64/petscii-screen-codes.md +428 -0
- package/docs/recipes/oscar64/platformer-scaffold.md +1467 -0
- package/docs/recipes/oscar64/print-number.md +367 -0
- package/docs/recipes/oscar64/raster-bars.md +239 -0
- package/docs/recipes/oscar64/raster-profile-bars.md +404 -0
- package/docs/recipes/oscar64/rel-side-sectors.md +391 -0
- package/docs/recipes/oscar64/relative-file-records.md +378 -0
- package/docs/recipes/oscar64/save-load-seq-file.md +365 -0
- package/docs/recipes/oscar64/screenshots/README.md +99 -0
- package/docs/recipes/oscar64/screenshots/actor-activation-window-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/actor-activation-window.png +0 -0
- package/docs/recipes/oscar64/screenshots/adventure-engine-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/adventure-engine.png +0 -0
- package/docs/recipes/oscar64/screenshots/attract-replay-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/attract-replay.png +0 -0
- package/docs/recipes/oscar64/screenshots/beat-em-up-lanes-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/beat-em-up-lanes.png +0 -0
- package/docs/recipes/oscar64/screenshots/bfs-distance-map-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/bfs-distance-map.png +0 -0
- package/docs/recipes/oscar64/screenshots/bitmap-koala-viewer.png +0 -0
- package/docs/recipes/oscar64/screenshots/car-contact-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/car-contact.png +0 -0
- package/docs/recipes/oscar64/screenshots/cave-scan-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/cave-scan.png +0 -0
- package/docs/recipes/oscar64/screenshots/char-bullets-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/char-bullets.png +0 -0
- package/docs/recipes/oscar64/screenshots/charset-animation-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/charset-animation.png +0 -0
- package/docs/recipes/oscar64/screenshots/charset-parallax-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/charset-parallax.png +0 -0
- package/docs/recipes/oscar64/screenshots/destructible-terrain-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/destructible-terrain.png +0 -0
- package/docs/recipes/oscar64/screenshots/difficulty-tables-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/difficulty-tables.png +0 -0
- package/docs/recipes/oscar64/screenshots/dig-and-guards-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/dig-and-guards.png +0 -0
- package/docs/recipes/oscar64/screenshots/directory-reader-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/directory-reader.png +0 -0
- package/docs/recipes/oscar64/screenshots/divide-check-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/divide-check.png +0 -0
- package/docs/recipes/oscar64/screenshots/double-buffer-nomirror.png +0 -0
- package/docs/recipes/oscar64/screenshots/double-buffer-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/double-buffer.png +0 -0
- package/docs/recipes/oscar64/screenshots/falling-blocks-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/falling-blocks.png +0 -0
- package/docs/recipes/oscar64/screenshots/fixed-point-jump-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/fixed-point-jump-velocity-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/fixed-point-jump-velocity.png +0 -0
- package/docs/recipes/oscar64/screenshots/fixed-point-jump.png +0 -0
- package/docs/recipes/oscar64/screenshots/flip-screen-rooms-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/flip-screen-rooms.png +0 -0
- package/docs/recipes/oscar64/screenshots/frame-sync-loop-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/frame-sync-loop-overrun.png +0 -0
- package/docs/recipes/oscar64/screenshots/frame-sync-loop.png +0 -0
- package/docs/recipes/oscar64/screenshots/game-tree-search-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/game-tree-search.png +0 -0
- package/docs/recipes/oscar64/screenshots/ghost-targeting-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/ghost-targeting.png +0 -0
- package/docs/recipes/oscar64/screenshots/headless-verify-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/headless-verify.png +0 -0
- package/docs/recipes/oscar64/screenshots/hello-world.png +0 -0
- package/docs/recipes/oscar64/screenshots/high-score-persist-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/high-score-persist.png +0 -0
- package/docs/recipes/oscar64/screenshots/iffl-kernal-skip-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/iffl-kernal-skip.png +0 -0
- package/docs/recipes/oscar64/screenshots/joystick-input-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/joystick-input.png +0 -0
- package/docs/recipes/oscar64/screenshots/lane-pursuit-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/lane-pursuit.png +0 -0
- package/docs/recipes/oscar64/screenshots/level-rle-decoder-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/level-rle-decoder.png +0 -0
- package/docs/recipes/oscar64/screenshots/lfsr-random-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/lfsr-random-seed2.png +0 -0
- package/docs/recipes/oscar64/screenshots/lfsr-random.png +0 -0
- package/docs/recipes/oscar64/screenshots/load-asset-runtime-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/load-asset-runtime.png +0 -0
- package/docs/recipes/oscar64/screenshots/memory-layout-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/memory-layout.png +0 -0
- package/docs/recipes/oscar64/screenshots/mixed-fighters-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/mixed-fighters.png +0 -0
- package/docs/recipes/oscar64/screenshots/multi-sprite-object-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/multi-sprite-object.png +0 -0
- package/docs/recipes/oscar64/screenshots/nav-area-pathfinding-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/nav-area-pathfinding.png +0 -0
- package/docs/recipes/oscar64/screenshots/object-pool-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/object-pool.png +0 -0
- package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-oldntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-pal.png +0 -0
- package/docs/recipes/oscar64/screenshots/password-state-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/password-state.png +0 -0
- package/docs/recipes/oscar64/screenshots/per-frame-hitbox-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/per-frame-hitbox.png +0 -0
- package/docs/recipes/oscar64/screenshots/petscii-screen-codes-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/petscii-screen-codes.png +0 -0
- package/docs/recipes/oscar64/screenshots/platformer-scaffold-d64-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/platformer-scaffold-d64.png +0 -0
- package/docs/recipes/oscar64/screenshots/platformer-scaffold-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/platformer-scaffold.png +0 -0
- package/docs/recipes/oscar64/screenshots/print-number-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/print-number.png +0 -0
- package/docs/recipes/oscar64/screenshots/raster-bars.png +0 -0
- package/docs/recipes/oscar64/screenshots/raster-profile-bars-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/raster-profile-bars.png +0 -0
- package/docs/recipes/oscar64/screenshots/rel-side-sectors-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/rel-side-sectors.png +0 -0
- package/docs/recipes/oscar64/screenshots/relative-file-records-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/relative-file-records.png +0 -0
- package/docs/recipes/oscar64/screenshots/save-load-seq-file-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/save-load-seq-file.png +0 -0
- package/docs/recipes/oscar64/screenshots/seeded-level-fill-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/seeded-level-fill.png +0 -0
- package/docs/recipes/oscar64/screenshots/sfx-engine-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/sfx-engine.png +0 -0
- package/docs/recipes/oscar64/screenshots/sid-music-player.png +0 -0
- package/docs/recipes/oscar64/screenshots/simple-shmup.png +0 -0
- package/docs/recipes/oscar64/screenshots/slope-collision-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/slope-collision.png +0 -0
- package/docs/recipes/oscar64/screenshots/soft-scroll-h.png +0 -0
- package/docs/recipes/oscar64/screenshots/sprite-animation-table-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/sprite-animation-table.png +0 -0
- package/docs/recipes/oscar64/screenshots/sprite-multiplex-8.png +0 -0
- package/docs/recipes/oscar64/screenshots/stable-raster-irq.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-input-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-input.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield-gameover.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield-notrail-control.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield-ungated-control.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-window-menu-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-window-menu.png +0 -0
- package/docs/recipes/oscar64/screenshots/tile-grid-collision-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/tile-grid-collision.png +0 -0
- package/docs/recipes/oscar64/screenshots/tile-map-render-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/tile-map-render.png +0 -0
- package/docs/recipes/oscar64/screenshots/two-player-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/two-player.png +0 -0
- package/docs/recipes/oscar64/screenshots/two-word-parser-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/two-word-parser.png +0 -0
- package/docs/recipes/oscar64/screenshots/vehicle-control-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/vehicle-control.png +0 -0
- package/docs/recipes/oscar64/screenshots/wave-director-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/wave-director.png +0 -0
- package/docs/recipes/oscar64/seeded-level-fill.md +364 -0
- package/docs/recipes/oscar64/sfx-engine.md +514 -0
- package/docs/recipes/oscar64/sid-music-player.md +441 -0
- package/docs/recipes/oscar64/simple-shmup.md +1000 -0
- package/docs/recipes/oscar64/slope-collision.md +610 -0
- package/docs/recipes/oscar64/soft-scroll-h.md +261 -0
- package/docs/recipes/oscar64/sprite-animation-table.md +671 -0
- package/docs/recipes/oscar64/sprite-multiplex-8.md +314 -0
- package/docs/recipes/oscar64/stable-raster-irq.md +236 -0
- package/docs/recipes/oscar64/text-input.md +274 -0
- package/docs/recipes/oscar64/text-overlay-playfield.md +617 -0
- package/docs/recipes/oscar64/text-window-menu.md +551 -0
- package/docs/recipes/oscar64/tile-grid-collision.md +563 -0
- package/docs/recipes/oscar64/tile-map-render.md +395 -0
- package/docs/recipes/oscar64/two-player.md +528 -0
- package/docs/recipes/oscar64/two-word-parser.md +510 -0
- package/docs/recipes/oscar64/vehicle-control.md +771 -0
- package/docs/recipes/oscar64/wave-director.md +787 -0
- package/docs/recipes/runs.json +1606 -0
- package/docs/runtime/sim6502-reference.md +466 -0
- package/docs/runtime/vice-mcp-reference.md +351 -0
- package/docs/runtime/vice-reference.md +1393 -0
- package/docs/techniques/bitmap-modes.md +685 -0
- package/docs/techniques/cpu-cycle-tricks.md +1570 -0
- package/docs/techniques/effects-vector-3d.md +1431 -0
- package/docs/techniques/file-io.md +1427 -0
- package/docs/techniques/input.md +1072 -0
- package/docs/techniques/loaders-packers.md +768 -0
- package/docs/techniques/logic.md +3091 -0
- package/docs/techniques/maths.md +1524 -0
- package/docs/techniques/memory-banking.md +1686 -0
- package/docs/techniques/music-sid.md +1144 -0
- package/docs/techniques/raster.md +1543 -0
- package/docs/techniques/scroll.md +1292 -0
- package/docs/techniques/sprite.md +2566 -0
- package/docs/techniques/text-mode-render.md +717 -0
- package/docs/techniques/text.md +1095 -0
- package/docs/techniques/transitions.md +405 -0
- package/docs/toolchains/cartconv-reference.md +262 -0
- package/docs/toolchains/cc1541-reference.md +316 -0
- package/docs/toolchains/cc65-reference.md +667 -0
- package/docs/toolchains/error-sources/cc65/README.md +1 -0
- package/docs/toolchains/error-sources/cc65/missing-setcpu.s +4 -0
- package/docs/toolchains/error-sources/cc65/range-error.s +4 -0
- package/docs/toolchains/error-sources/cc65/segment-overflow.c +6 -0
- package/docs/toolchains/error-sources/cc65/unresolved-external.c +6 -0
- package/docs/toolchains/error-sources/kickassembler/README.md +1 -0
- package/docs/toolchains/error-sources/kickassembler/branch-out-of-range.asm +4 -0
- package/docs/toolchains/error-sources/kickassembler/failed-assert.asm +3 -0
- package/docs/toolchains/error-sources/kickassembler/flat-lib.asm +4 -0
- package/docs/toolchains/error-sources/kickassembler/import-source-no-namespace.asm +5 -0
- package/docs/toolchains/error-sources/kickassembler/macro-without-parentheses.asm +6 -0
- package/docs/toolchains/error-sources/kickassembler/memory-block-overlap.asm +4 -0
- package/docs/toolchains/error-sources/kickassembler/symbol-clash-lib.asm +5 -0
- package/docs/toolchains/error-sources/kickassembler/symbol-clash-main.asm +6 -0
- package/docs/toolchains/error-sources/kickassembler/unknown-symbol.asm +4 -0
- package/docs/toolchains/error-sources/oscar64/README.md +1 -0
- package/docs/toolchains/error-sources/oscar64/asm-addressing-mode.c +7 -0
- package/docs/toolchains/error-sources/oscar64/region-overflow.c +6 -0
- package/docs/toolchains/error-sources/oscar64/undefined-extern-var.c +5 -0
- package/docs/toolchains/error-sources/oscar64/undefined-extern.c +6 -0
- package/docs/toolchains/error-sources/oscar64/unknown-vic-field.c +6 -0
- package/docs/toolchains/error-sources/oscar64/void-main.c +3 -0
- package/docs/toolchains/kickassembler-reference.md +1020 -0
- package/docs/toolchains/memory-layout-planning.md +362 -0
- package/docs/toolchains/oscar64-headers-reference.md +583 -0
- package/docs/toolchains/oscar64-reference.md +892 -0
- package/docs/toolchains/petcat-reference.md +290 -0
- package/docs/toolchains/png2prg.md +188 -0
- package/docs/toolchains/release-disk.md +314 -0
- package/docs/toolchains/sidreloc.md +236 -0
- package/docs/toolchains/spindle-reference.md +487 -0
- package/docs/toolchains/tape-mastering.md +592 -0
- package/docs/toolchains/unit-testing-6502.md +281 -0
- package/docs/workflow/agent-harness.md +478 -0
- package/package.json +105 -0
- package/templates/_harness/CLAUDE.md.template +91 -0
- package/templates/_harness/PLAN.md.template +72 -0
- package/templates/_harness/check.py +442 -0
- package/templates/_harness/claude/settings.json +17 -0
- package/templates/_harness/gen-asm-header.py +96 -0
- package/templates/_harness/harness.mk +273 -0
- package/templates/_harness/hooks/plan-gate.py +185 -0
- package/templates/_harness/meter/frame_meter.asm +311 -0
- package/templates/_harness/meter/frame_meter.c +155 -0
- package/templates/_harness/meter/frame_meter.h +80 -0
- package/templates/_harness/zp-used.py +104 -0
- package/templates/action-puzzle/.claude/settings.json +17 -0
- package/templates/action-puzzle/.mcp.json +10 -0
- package/templates/action-puzzle/CLAUDE.md +120 -0
- package/templates/action-puzzle/Makefile +125 -0
- package/templates/action-puzzle/PLAN.md +388 -0
- package/templates/action-puzzle/README.md +240 -0
- package/templates/action-puzzle/expect.json +25 -0
- package/templates/action-puzzle/src/cave.c +280 -0
- package/templates/action-puzzle/src/cave.h +80 -0
- package/templates/action-puzzle/src/gen_autopilot.h +20 -0
- package/templates/action-puzzle/src/gen_caves.h +59 -0
- package/templates/action-puzzle/src/gen_notes.h +16 -0
- package/templates/action-puzzle/src/hiscore.c +152 -0
- package/templates/action-puzzle/src/hiscore.h +41 -0
- package/templates/action-puzzle/src/level.c +64 -0
- package/templates/action-puzzle/src/level.h +24 -0
- package/templates/action-puzzle/src/main.c +599 -0
- package/templates/action-puzzle/src/render.c +239 -0
- package/templates/action-puzzle/src/render.h +31 -0
- package/templates/action-puzzle/src/sound.c +158 -0
- package/templates/action-puzzle/src/sound.h +22 -0
- package/templates/action-puzzle/tools/disk_check.py +77 -0
- package/templates/action-puzzle/tools/drive.py +106 -0
- package/templates/action-puzzle/tools/gen.py +454 -0
- package/templates/action-puzzle/tools/model_check.py +49 -0
- package/templates/action-puzzle/tools/verdict_codes.py +24 -0
- package/templates/adventure/.claude/settings.json +17 -0
- package/templates/adventure/.mcp.json +10 -0
- package/templates/adventure/CLAUDE.md +118 -0
- package/templates/adventure/Makefile +113 -0
- package/templates/adventure/PLAN.md +396 -0
- package/templates/adventure/README.md +277 -0
- package/templates/adventure/expect.json +25 -0
- package/templates/adventure/src/engine.c +543 -0
- package/templates/adventure/src/engine.h +48 -0
- package/templates/adventure/src/gen_script.h +141 -0
- package/templates/adventure/src/gen_world.h +511 -0
- package/templates/adventure/src/main.c +439 -0
- package/templates/adventure/src/picture.c +83 -0
- package/templates/adventure/src/picture.h +16 -0
- package/templates/adventure/src/save.c +159 -0
- package/templates/adventure/src/save.h +25 -0
- package/templates/adventure/src/sound.c +71 -0
- package/templates/adventure/src/sound.h +16 -0
- package/templates/adventure/src/text.c +349 -0
- package/templates/adventure/src/text.h +40 -0
- package/templates/adventure/tools/disk_check.py +128 -0
- package/templates/adventure/tools/drive.py +119 -0
- package/templates/adventure/tools/gen.py +1033 -0
- package/templates/adventure/tools/world.py +356 -0
- package/templates/beat-em-up/.claude/settings.json +17 -0
- package/templates/beat-em-up/.mcp.json +10 -0
- package/templates/beat-em-up/CLAUDE.md +136 -0
- package/templates/beat-em-up/Makefile +96 -0
- package/templates/beat-em-up/PLAN.md +502 -0
- package/templates/beat-em-up/README.md +315 -0
- package/templates/beat-em-up/expect-gameover.json +8 -0
- package/templates/beat-em-up/expect.json +29 -0
- package/templates/beat-em-up/src/anim.c +70 -0
- package/templates/beat-em-up/src/art.c +344 -0
- package/templates/beat-em-up/src/autopilot.h +154 -0
- package/templates/beat-em-up/src/brute.c +273 -0
- package/templates/beat-em-up/src/enemy.c +281 -0
- package/templates/beat-em-up/src/engine.asm +512 -0
- package/templates/beat-em-up/src/fighter.c +384 -0
- package/templates/beat-em-up/src/game.h +269 -0
- package/templates/beat-em-up/src/hud.c +135 -0
- package/templates/beat-em-up/src/main.c +341 -0
- package/templates/beat-em-up/src/sound.c +44 -0
- package/templates/beat-em-up/src/street.c +93 -0
- package/templates/beat-em-up/src/verdict.h +523 -0
- package/templates/beat-em-up/src/view.c +362 -0
- package/templates/beat-em-up/tools/drive.py +105 -0
- package/templates/beat-em-up/tools/flickercheck.py +273 -0
- package/templates/demo/.claude/settings.json +17 -0
- package/templates/demo/.mcp.json +10 -0
- package/templates/demo/CLAUDE.md +129 -0
- package/templates/demo/Makefile +66 -0
- package/templates/demo/PLAN.md +401 -0
- package/templates/demo/README.md +322 -0
- package/templates/demo/expect.json +945 -0
- package/templates/demo/src/config.asm +80 -0
- package/templates/demo/src/framework.asm +390 -0
- package/templates/demo/src/main.asm +139 -0
- package/templates/demo/src/music.asm +197 -0
- package/templates/demo/src/part_main.asm +279 -0
- package/templates/demo/src/part_title.asm +79 -0
- package/templates/demo/src/parts.asm +43 -0
- package/templates/demo/src/tables.asm +73 -0
- package/templates/demo/src/verdict.asm +179 -0
- package/templates/demo/tools/audio.py +80 -0
- package/templates/demo/tools/gen_expect.py +167 -0
- package/templates/demo/tools/probe.py +95 -0
- package/templates/hello/.claude/settings.json +17 -0
- package/templates/hello/.mcp.json +10 -0
- package/templates/hello/CLAUDE.md +100 -0
- package/templates/hello/Makefile +38 -0
- package/templates/hello/PLAN.md +200 -0
- package/templates/hello/expect.json +20 -0
- package/templates/hello/src/main.c +161 -0
- package/templates/hello/src/sprite.asm +27 -0
- package/templates/hello-kick/.claude/settings.json +17 -0
- package/templates/hello-kick/.mcp.json +10 -0
- package/templates/hello-kick/CLAUDE.md +100 -0
- package/templates/hello-kick/Makefile +17 -0
- package/templates/hello-kick/PLAN.md +199 -0
- package/templates/hello-kick/expect.json +20 -0
- package/templates/hello-kick/src/main.asm +233 -0
- package/templates/platformer/.claude/settings.json +17 -0
- package/templates/platformer/.mcp.json +10 -0
- package/templates/platformer/CLAUDE.md +125 -0
- package/templates/platformer/Makefile +86 -0
- package/templates/platformer/PLAN.md +360 -0
- package/templates/platformer/README.md +277 -0
- package/templates/platformer/expect.json +32 -0
- package/templates/platformer/src/actors.c +289 -0
- package/templates/platformer/src/anim.c +51 -0
- package/templates/platformer/src/art.c +212 -0
- package/templates/platformer/src/autopilot.h +30 -0
- package/templates/platformer/src/engine.asm +253 -0
- package/templates/platformer/src/game.h +248 -0
- package/templates/platformer/src/hud.c +90 -0
- package/templates/platformer/src/level.c +184 -0
- package/templates/platformer/src/main.c +306 -0
- package/templates/platformer/src/player.c +268 -0
- package/templates/platformer/src/sound.c +42 -0
- package/templates/platformer/src/verdict.h +206 -0
- package/templates/platformer/src/view.c +331 -0
- package/templates/platformer/stage-expect.json +6 -0
- package/templates/platformer/tools/drive.py +112 -0
- package/templates/platformer/tools/tearcheck.py +229 -0
- package/templates/shmup-vertical/.claude/settings.json +17 -0
- package/templates/shmup-vertical/.mcp.json +10 -0
- package/templates/shmup-vertical/CLAUDE.md +137 -0
- package/templates/shmup-vertical/Makefile +131 -0
- package/templates/shmup-vertical/PLAN.md +569 -0
- package/templates/shmup-vertical/README.md +189 -0
- package/templates/shmup-vertical/expect.json +38 -0
- package/templates/shmup-vertical/src/bullets.c +158 -0
- package/templates/shmup-vertical/src/bullets.h +35 -0
- package/templates/shmup-vertical/src/display.c +239 -0
- package/templates/shmup-vertical/src/display.h +40 -0
- package/templates/shmup-vertical/src/game.h +85 -0
- package/templates/shmup-vertical/src/glyph.asm +358 -0
- package/templates/shmup-vertical/src/hiscore.c +141 -0
- package/templates/shmup-vertical/src/hiscore.h +16 -0
- package/templates/shmup-vertical/src/hit.asm +180 -0
- package/templates/shmup-vertical/src/hitbox.c +59 -0
- package/templates/shmup-vertical/src/hitbox.h +25 -0
- package/templates/shmup-vertical/src/kernel.asm +330 -0
- package/templates/shmup-vertical/src/level.c +174 -0
- package/templates/shmup-vertical/src/level.h +27 -0
- package/templates/shmup-vertical/src/main.c +824 -0
- package/templates/shmup-vertical/src/mux.asm +321 -0
- package/templates/shmup-vertical/src/sound.asm +255 -0
- package/templates/shmup-vertical/src/step.asm +169 -0
- package/templates/shmup-vertical/src/waves.c +195 -0
- package/templates/shmup-vertical/src/waves.h +33 -0
- package/templates/shmup-vertical/stage-expect.json +6 -0
- package/templates/shmup-vertical/tools/drive.py +152 -0
- package/templates/shmup-vertical/tools/joytest.py +159 -0
- package/templates/shmup-vertical/tools/meter.py +15 -0
- package/templates/shmup-vertical/tools/phases.py +73 -0
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
<!-- doc-type: format-reference -->
|
|
2
|
+
|
|
3
|
+
# IEC Bus and 1541 Disk Drive Reference
|
|
4
|
+
|
|
5
|
+
This document covers the Commodore IEC serial bus protocol, the 1541 floppy drive's command interface, and the practical concerns of writing C64 code that talks to disk drives. The IEC bus is the physical transport underlying KERNAL disk I/O (LOAD, SAVE, OPEN, CLOSE, CHKIN, CHKOUT) and any custom fast-loader that bypasses the KERNAL. For the KERNAL jump-table entries that drive IEC communication from the C64 side, see `../hardware/kernal-routines-reference.md`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
The IEC bus (serial IEEE-488 bus) is Commodore's cost-reduced adaptation of the IEEE-488/HP-IB parallel bus used on PET computers. Commodore replaced the 16-wire parallel bus with a 3-wire serial bus to save component costs, at a severe performance penalty: the standard 1541 protocol delivers approximately 300–400 bytes/second, against the PET's ~4 KB/second. The bus connects the C64 to disk drives (1541, 1571, 1581), printers (MPS-801, MPS-803), and other peripherals via a 6-pin DIN connector on the back of the computer.
|
|
12
|
+
|
|
13
|
+
The C64 works the IEC bus through five lines of CIA2 port A: three outputs (ATN, CLK, DATA) and two inputs (CLK, DATA). The same CIA2 chip (`$DD00`–`$DD0F`) that controls the VIC-II bank-switching (`$DD00` bits 0–1) also owns the IEC bus lines. This dual use means that code which manipulates CIA2 for RS-232 or custom bit-banging must be aware of IEC bus contention.
|
|
14
|
+
|
|
15
|
+
All IEC communication from the C64 side goes through KERNAL routines. User programs should always call the KERNAL jump table (`$FF81`–`$FFF5`), never poke CIA2 directly for IEC purposes — the internal routine addresses changed between KERNAL revisions and are private implementation details.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Pin Map and Electrical Characteristics
|
|
20
|
+
|
|
21
|
+
The IEC bus is open-collector. For each line, every device has a driver that can pull the line to ground and do nothing else, and a pull-up lets the line float to +5 V when no driver is on. A line is therefore **asserted ("true") when it is low** and **released ("false") when it is high**, and any one device can hold it low against all the others — which is what the handshakes rely on: a listener that is not ready keeps DATA low and the talker cannot proceed.
|
|
22
|
+
|
|
23
|
+
**DIN-6 connector pinout:**
|
|
24
|
+
|
|
25
|
+
| Pin | Signal | Direction | Description |
|
|
26
|
+
|-----|--------|-----------|-------------|
|
|
27
|
+
| 1 | SRQ | Bidirectional | Service Request (unused by 1541; used by some fast loaders for handshake) |
|
|
28
|
+
| 2 | GND | — | Signal ground |
|
|
29
|
+
| 3 | ATN | C64 → drives | Attention: C64 claims bus and sends command bytes |
|
|
30
|
+
| 4 | CLK | Bidirectional | Clock line: transmitter controls timing |
|
|
31
|
+
| 5 | DATA | Bidirectional | Serial data |
|
|
32
|
+
| 6 | RESET | C64 → drives | Reset line (tied to C64 reset; resets all devices on power-cycle) |
|
|
33
|
+
|
|
34
|
+
**CIA2 port A (`$DD00`) bit map**, read from the KERNAL 901227-03 image (rung 1; the instructions are in the next table):
|
|
35
|
+
|
|
36
|
+
| Bit | Direction (DDR default) | Signal | Sense |
|
|
37
|
+
|-----|-------------------------|--------|-------|
|
|
38
|
+
| 7 | input | DATA IN | 1 = DATA is high (released); 0 = some device, this C64 included, is holding DATA low |
|
|
39
|
+
| 6 | input | CLK IN | 1 = CLK is high (released); 0 = CLK is held low |
|
|
40
|
+
| 5 | output | DATA OUT | 1 = this C64 pulls DATA low; 0 = released |
|
|
41
|
+
| 4 | output | CLK OUT | 1 = this C64 pulls CLK low; 0 = released |
|
|
42
|
+
| 3 | output | ATN OUT | 1 = this C64 pulls ATN low; 0 = released |
|
|
43
|
+
| 2 | output | RS-232 TXD | user port, not IEC — the KERNAL's RS-232 transmitter writes it |
|
|
44
|
+
| 1–0 | output | VIC-II bank select | not IEC — see `../hardware/cia-reference.md` |
|
|
45
|
+
|
|
46
|
+
The two directions have opposite senses and both matter. **Writing 1 to an output bit pulls its line low** — the port reaches the bus through inverting open-collector drivers (the 7406 on the C64 schematic, rung 4) — so releasing a line means *clearing* its bit, and the KERNAL's routine for "clock high" is an `AND`. **Reading 1 on an input bit means the line is high**, i.e. released; a line that anything is holding low reads 0. The KERNAL's device-present test is exactly that: assert ATN, release our own DATA, and if DATA IN still reads 1 nobody is holding it, so nobody is there.
|
|
47
|
+
|
|
48
|
+
The evidence, all rung 1 from the ROM bytes (the labels are the KERNAL source's names, rung 4):
|
|
49
|
+
|
|
50
|
+
| Address | Label | Instructions | Establishes |
|
|
51
|
+
|---|---|---|---|
|
|
52
|
+
| `$EE85` | CLKHI | `LDA $DD00 / AND #$EF / STA $DD00` | CLK OUT is bit 4; release by clearing |
|
|
53
|
+
| `$EE8E` | CLKLO | `LDA $DD00 / ORA #$10 / STA $DD00` | assert by setting |
|
|
54
|
+
| `$EE97` | DATAHI | `LDA $DD00 / AND #$DF / STA $DD00` | DATA OUT is bit 5 |
|
|
55
|
+
| `$EEA0` | DATALO | `LDA $DD00 / ORA #$20 / STA $DD00` | |
|
|
56
|
+
| `$ED2E` | in LISTEN/TALK | `LDA $DD00 / ORA #$08 / STA $DD00` | ATN OUT is bit 3, asserted by setting |
|
|
57
|
+
| `$EDBE` | SCATN | `LDA $DD00 / AND #$F7 / STA $DD00` | released by clearing |
|
|
58
|
+
| `$EEA9` | DEBPIA | `LDA $DD00 / CMP $DD00 / BNE $EEA9 / ASL A / RTS` | debounced read: DATA IN (bit 7) lands in the carry, CLK IN (bit 6) in the sign flag |
|
|
59
|
+
| `$ED41`–`$ED47` | in ISOUR | `JSR DATAHI / JSR DEBPIA / BCS $EDAD` | carry set — DATA IN reads 1 — with ATN asserted goes to `$EDAD`, `LDA #$80`, the device-not-present status; a present drive holds DATA low, and low reads 0 |
|
|
60
|
+
| `$FE7B` | RS-232 transmit | `LDA $DD00 / AND #$FB / ORA $B5 / STA $DD00` | bit 2 is the RS-232 TXD bit |
|
|
61
|
+
|
|
62
|
+
Measured too, in VICE x64sc 3.10 with `-drive8truedrive` (rung 1): once the C64 has released its own lines — the probe stored `$07` before its first read; see the reset state below for why that matters — `$DD00` reads `$C7` (bits 6–7 both 1); write bit 4 and it reads `$97` (CLK IN fell to 0); write bit 5 instead and it reads `$67` (DATA IN fell to 0); after `JSR $FFB1` (LISTEN 8) with a 1541 attached it reads `$1F` — ATN and CLK held by the C64, DATA held by the drive, all three inputs 0 — and READST is `$00`, while with no drive attached the same call leaves `$C7` and READST `$80`.
|
|
63
|
+
|
|
64
|
+
**Data direction and reset state.** IOINIT (`$FDA3`) sets the port up at `$FDCB`–`$FDD4`: `LDA #$07 / STA $DD00`, then `LDA #$3F / STA $DD02` (rung 1). The DDR default is therefore `$3F` — bits 0–5 output, 6–7 input — and user code has no reason to change it. The `$07` is not where the port ends up, though. IOINIT's last instruction, at `$FDF6`, is `JMP $FF6E`, and `$FF6E`–`$FF7F` — `LDA #$81 / STA $DC0D / LDA $DC0E / AND #$80 / ORA #$11 / STA $DC0E / JMP $EE8E` — finishes with the only `JMP CLKLO` in the KERNAL (rung 1). A freshly reset C64 therefore parks CLK asserted: the port register is `$17` and `$DD00` reads `$97`. Measured in VICE x64sc 3.10 (rung 1): a program whose first instruction is `LDA $DD00` shows `$97`, and `$C7` only after it stores `$07`; the readings are identical with `-drive8truedrive` and a 1541 attached and with no drive, so nothing on the emulated bus was pulling. `$C7` is the bus after the C64 has released its lines — a `$07` store does that, and so does the `JSR CLKHI / JMP DATAHI` pair at `$EE0D`–`$EE12` that the KERNAL's UNLSN and UNTLK both end with.
|
|
65
|
+
|
|
66
|
+
**One register, both directions.** The C64 reads the bus and drives its own lines through the same byte, which is why every KERNAL primitive above is a read-modify-write: a bare `STA $DD00` would also rewrite the VIC bank and TXD. Custom IEC code must mask the same way, and anything that changes the VIC bank while the bus is busy — a raster interrupt switching banks under a loader, say — must carry bits 3–5 through unchanged or it will drop CLK or DATA in the middle of a byte.
|
|
67
|
+
|
|
68
|
+
Port B (`$DD01`) and its DDR (`$DD03`) carry no IEC signal. They are the user port's data lines, which is where parallel-cable loaders put their eight data bits.
|
|
69
|
+
|
|
70
|
+
**Correction (2026-09-21).** The table this replaces had every IEC row wrong — CLK IN on bit 7, DATA IN on bit 6, ATN on bit 4, CLK OUT on bit 3, DATA OUT on bit 2, and "bits 2, 3, 4 output" for the DDR — and said the inputs were "ORed with the output lines inside the CIA". A loader written from it would have toggled the RS-232 TXD line as DATA and sampled DATA as CLK. `../hardware/cia-reference.md` has the bit numbers right but gives the input sense the other way round ("reading `1` from bit 6 means the bus is being held low"); the ISOUR test and the VICE readings above both say 1 is released. A draft of this section written the same day said IOINIT's `$07` store left all three IEC lines released and called `$C7` the idle reading without saying whose idle; IOINIT's last instruction asserts CLK, and `$C7` is what the C64 sees after it has released its own lines.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Bus Protocol
|
|
75
|
+
|
|
76
|
+
### Roles: Controller, Talker, Listener
|
|
77
|
+
|
|
78
|
+
The IEC bus has one **controller** (always the C64), one **talker** (the device sending data), and one or more **listeners** (devices receiving data). During a LOAD, the drive is the talker and the C64 is the listener. During a SAVE, the C64 is the talker and the drive is the listener. A printer is always a listener.
|
|
79
|
+
|
|
80
|
+
### ATN Phase (Command Phase)
|
|
81
|
+
|
|
82
|
+
The C64 initiates bus activity by pulling ATN low. While ATN is asserted, all devices on the bus treat incoming bytes as command bytes (not data). The C64 sends a 1-byte command:
|
|
83
|
+
|
|
84
|
+
- **Bits 7–5:** Command type:
|
|
85
|
+
- `$20` (001xxxxx) — LISTEN: address a device as a listener
|
|
86
|
+
- `$40` (010xxxxx) — TALK: address a device as a talker
|
|
87
|
+
- `$60` (011xxxxx) — OPEN CHANNEL / SECONDARY ADDRESS: follows LISTEN or TALK
|
|
88
|
+
- `$E0` (111xxxxx) — CLOSE
|
|
89
|
+
- `$F0` (111xxxxx) — OPEN (also a secondary address command)
|
|
90
|
+
- **Bits 4–0:** Device address (0–30; C64 itself is address 0, drives default to 8–11, printers to 4–7)
|
|
91
|
+
|
|
92
|
+
After sending LISTEN or TALK + secondary address, the C64 releases ATN. The addressed device becomes active; all others go passive.
|
|
93
|
+
|
|
94
|
+
### Serial Frame (Data Phase)
|
|
95
|
+
|
|
96
|
+
Each byte is transferred serially, bit-by-bit, using a two-line handshake on CLK and DATA:
|
|
97
|
+
|
|
98
|
+
1. Talker asserts CLK (signals it is about to send a bit)
|
|
99
|
+
2. Listener releases DATA (signals ready to receive)
|
|
100
|
+
3. Talker toggles CLK while holding DATA stable at the bit value
|
|
101
|
+
4. Listener reads DATA on the CLK edge
|
|
102
|
+
5. Repeat for all 8 bits (LSB first)
|
|
103
|
+
6. Listener holds DATA low (ACK) briefly after the last bit
|
|
104
|
+
|
|
105
|
+
This handshake means transmission speed is limited by the slowest device on the bus. The 1541 introduces significant overhead because it processes bytes in its own 6502 CPU, handling each bit-transfer in a software loop at 1 MHz. The result is the notorious ~300 byte/sec standard-load throughput.
|
|
106
|
+
|
|
107
|
+
### EOI (End Or Identify)
|
|
108
|
+
|
|
109
|
+
The talker signals the last byte in a data stream via EOI (End or Identify). After the receiver is ready (DATA released), the talker leaves CLK *released* and does nothing; a normal byte would have CLK pulled low again within a couple of hundred microseconds. The listener recognises the long gap as EOI and acknowledges by pulling DATA low briefly, then releasing it; only then does the talker pull CLK low and clock the final byte out. The lengths, as the KERNAL produces and accepts them, are in "IEC bit timing, measured" below.
|
|
110
|
+
|
|
111
|
+
**Correction (2026-09-23).** This paragraph said the talker "holds CLK low for more than ~200 µs" to signal EOI. It is the other way round: CLK is high (released) throughout the EOI gap, and it is the *absence* of the CLK-low edge that the listener times. The trace below shows the C64, as talker, releasing CLK at `$EE8A` and not touching it again until after the drive's DATA pulse; as listener it times the gap with CIA1 timer B and answers after 539 cycles, not 200 µs.
|
|
112
|
+
|
|
113
|
+
The KERNAL READST routine (`$FFB7`) returns a status byte where bit 6 indicates EOI was received on the last IECIN call.
|
|
114
|
+
|
|
115
|
+
### UNLISTEN / UNTALK
|
|
116
|
+
|
|
117
|
+
After data transfer is complete, the C64 asserts ATN and sends the UNLISTEN (`$3F`) or UNTALK (`$5F`) command to release the addressed devices.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## IEC bit timing, measured
|
|
122
|
+
|
|
123
|
+
The prose above says what the handshake does; this section says how long each part takes when the 901227-03 KERNAL does it, read off a cycle-stamped trace of every `$DD00` access in VICE x64sc 3.10 with `-drive8truedrive -drive8type 1541` and a freshly formatted disk (rung 1). Two probes were traced: one that does `CHKOUT` on channel 15, sends `M-R $00 $00 $04` and reads the four bytes back, and one that only reads the status line, which is the shortest way to get an EOI *from* the drive. The body of the first, without its BASIC stub:
|
|
124
|
+
|
|
125
|
+
```kick
|
|
126
|
+
iec_timing:
|
|
127
|
+
lda #15
|
|
128
|
+
ldx #8
|
|
129
|
+
ldy #15
|
|
130
|
+
jsr $ffba // SETLFS 15,8,15
|
|
131
|
+
lda #0
|
|
132
|
+
jsr $ffbd // SETNAM "": with no name, OPEN sends nothing on the bus
|
|
133
|
+
jsr $ffc0 // OPEN
|
|
134
|
+
ldx #15
|
|
135
|
+
jsr $ffc9 // CHKOUT 15: LISTEN 8, secondary $6F, under ATN
|
|
136
|
+
ldx #0
|
|
137
|
+
send:
|
|
138
|
+
lda cmd,x
|
|
139
|
+
jsr $ffd2 // CHROUT: the KERNAL sends the previous byte, keeps this one
|
|
140
|
+
inx
|
|
141
|
+
cpx #6
|
|
142
|
+
bne send
|
|
143
|
+
jsr $ffcc // CLRCHN: last byte goes out with EOI, then UNLISTEN
|
|
144
|
+
ldx #15
|
|
145
|
+
jsr $ffc6 // CHKIN 15: TALK 8, secondary $6F, then the turnaround
|
|
146
|
+
ldx #0
|
|
147
|
+
recv:
|
|
148
|
+
jsr $ffcf // CHRIN: one byte from the drive
|
|
149
|
+
sta buf,x
|
|
150
|
+
inx
|
|
151
|
+
cpx #4
|
|
152
|
+
bne recv
|
|
153
|
+
jsr $ffcc // CLRCHN: UNTALK
|
|
154
|
+
lda #15
|
|
155
|
+
jmp $ffc3 // CLOSE 15: LISTEN 8, secondary $EF, UNLISTEN
|
|
156
|
+
cmd:
|
|
157
|
+
.text "M-R"
|
|
158
|
+
.byte $00, $00, $04
|
|
159
|
+
buf:
|
|
160
|
+
.fill 4, 0
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The monitor file, given to `-moncommands`. `trace` does not stop the machine (`watch` would, and the run would hang; see `../runtime/vice-reference.md`); `command 2` dumps the port after every read so the log carries the value the load returned, which the register line does not show:
|
|
164
|
+
|
|
165
|
+
```text
|
|
166
|
+
logname "/tmp/iec.log"
|
|
167
|
+
log on
|
|
168
|
+
trace store dd00
|
|
169
|
+
trace load dd00
|
|
170
|
+
command 2 "m dd00 dd00"
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The run: `x64sc -default -warp +sound +autostart-delay-random -autostartprgmode 1 -limitcycles 4500000 -8 disk.d64 -drive8truedrive -drive8type 1541 -drive8wobbleamplitude 0 -drive8wobblefrequency 0 -moncommands trace.mon -exitscreenshot out.png -autostart iec.prg`, once PAL and once with `-model ntsc`. Each log was about 8,400 lines; a Python script folds it into one line per bus event, with the port bits named. The parse is two regular expressions:
|
|
174
|
+
|
|
175
|
+
```text
|
|
176
|
+
import re, sys
|
|
177
|
+
ev, pend = [], None
|
|
178
|
+
for ln in open(sys.argv[1], encoding='latin-1'): # the dump line carries a PETSCII byte
|
|
179
|
+
m = re.match(r'\.C:([0-9a-f]{4})\s+.{12}(\w+)\s+\$DD00\s+- A:([0-9A-F]{2}).*?(\d+)\s*$', ln)
|
|
180
|
+
if m: # ".C:ee93 8D 00 DD STA $DD00 - A:1F ... 3011202"
|
|
181
|
+
pc, op, a, cyc = m.group(1), m.group(2), int(m.group(3), 16), int(m.group(4))
|
|
182
|
+
if op == 'STA': ev.append([cyc, pc, 'W', a]); pend = None
|
|
183
|
+
else: pend = [cyc, pc, 'R', None]; ev.append(pend)
|
|
184
|
+
continue
|
|
185
|
+
m = re.match(r'>C:dd00\s+([0-9a-f]{2})', ln) # the value the load saw
|
|
186
|
+
if m and pend: pend[3] = int(m.group(1), 16); pend = None
|
|
187
|
+
for cyc, pc, k, v in ev:
|
|
188
|
+
if v is None: continue
|
|
189
|
+
print('%8d %s %s %02x ATN%d CLKo%d DATo%d | CLKi%d DATi%d' % (cyc, pc, k, v,
|
|
190
|
+
(v>>3)&1, (v>>4)&1, (v>>5)&1, (v>>6)&1, (v>>7)&1))
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
A second script grouped those events into bytes; its figures are the table. The first excerpt is the start of `CHKOUT`'s LISTEN (PAL, cycles are the monitor's stopwatch column; `W` is a store, `R` a load, and the output bits are the C64's own drivers, 1 = pulling low):
|
|
194
|
+
|
|
195
|
+
```text
|
|
196
|
+
3011184 ed33 W 9f ATN1 CLKo1 DATo0 | CLKi0 DATi1 ATN asserted
|
|
197
|
+
3011196 ee8e R 1f ATN1 CLKo1 DATo0 | CLKi0 DATi0 DATA already low, 12 cycles later
|
|
198
|
+
3011224 ee9c W 1f ATN1 CLKo1 DATo0 | CLKi0 DATi0 DATAHI: C64 releases its own DATA
|
|
199
|
+
3012265 ee97 R 1f ATN1 CLKo1 DATo0 | CLKi0 DATi0 1,041 cycles later: the 1 ms wait is over
|
|
200
|
+
3012287 eea9 R 1f ATN1 CLKo1 DATo0 | CLKi0 DATi0 DEBPIA: DATA low, so a device is present
|
|
201
|
+
3012319 ee8a W 0f ATN1 CLKo0 DATo0 | CLKi0 DATi0 CLKHI: talker ready to send
|
|
202
|
+
3012422 eea9 R cf ATN1 CLKo0 DATo0 | CLKi1 DATi1 drive releases DATA: listener ready
|
|
203
|
+
3012454 ee93 W df ATN1 CLKo1 DATo0 | CLKi1 DATi1 CLKLO: byte starts
|
|
204
|
+
3012502 eea5 W bf ATN1 CLKo1 DATo1 | CLKi0 DATi1 DATALO: bit 0 of $28 is 0
|
|
205
|
+
3012527 ee8a W 2f ATN1 CLKo0 DATo1 | CLKi0 DATi0 CLKHI: bit valid
|
|
206
|
+
3012553 ed8b W 5f ATN1 CLKo1 DATo0 | CLKi1 DATi0 CLK low again, DATA released: cell over
|
|
207
|
+
3012666 ee8a W 2f ATN1 CLKo0 DATo1 | CLKi0 DATi0 next bit valid, 139 cycles after the last (a badline)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The second is the drive's EOI on the status line's closing `$0D`, seen by the C64 as listener:
|
|
211
|
+
|
|
212
|
+
```text
|
|
213
|
+
3070659 eea9 R 67 ATN0 CLKo0 DATo1 | CLKi1 DATi0 drive releases CLK: talker ready
|
|
214
|
+
3070703 ee9c W 47 ATN0 CLKo0 DATo0 | CLKi1 DATi0 DATAHI: listener ready, timer B running
|
|
215
|
+
3071242 eea5 W e7 ATN0 CLKo0 DATo1 | CLKi1 DATi1 539 cycles, no CLK edge: DATALO, the EOI acknowledge
|
|
216
|
+
3071326 ee9c W 07 ATN0 CLKo0 DATo0 | CLKi0 DATi0 DATAHI: acknowledge over after 84 cycles
|
|
217
|
+
3071354 eea9 R 87 ATN0 CLKo0 DATo0 | CLKi0 DATi1 drive pulls CLK low 28 cycles later: byte starts
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**The figures.** Cycles are what was measured; microseconds are those cycles at 985,248 Hz (PAL) and 1,022,727 Hz (NTSC). Where the NTSC column is a cycle count it came from the NTSC run; the C64's own base intervals came out identical in cycles on both models: they are instruction counts, plus whatever VIC-II stalls the screen state adds, and the stalls land in different places on the two models, so the long variants quoted are the PAL run's unless the row says otherwise. "Whose" says which side sets the interval: the C64's are the KERNAL's own and would be the same on any drive; the drive's are VICE's 1541 answering at VICE's 1541 timing, quoted as the range seen over fifteen sent and thirty-one received bytes, and a real drive, a 1571 or an SD2IEC will differ there.
|
|
221
|
+
|
|
222
|
+
| Phase | Whose | Cycles | PAL µs | NTSC µs | Where in the KERNAL |
|
|
223
|
+
|---|---|---|---|---|---|
|
|
224
|
+
| ATN asserted to first look at DATA (the device-present test) | C64 | 1,103 with the screen on (two badlines); 1,017 by instruction count | 1,119.5 | 1,078.5 | `STA $DD00` at `$ED33`; DATAHI at `$EE9C` 40 cycles later; the 1 ms loop is `LDX #$B8` at `$EEB4`, 955 cycles by count from the DATAHI store to the `$EE97` load before the next one, 1,041 measured on both models, the 86 being two badline stalls of 43; the test read at `$EEA9` |
|
|
225
|
+
| Drive's DATA response to ATN | drive | ≤ 12 | ≤ 12.2 | ≤ 11.7 | already low at the first read after the ATN store, `$EE8E` |
|
|
226
|
+
| Talker CLK release to listener DATA release (drive ready for a byte) | drive | 49 to 859 | 50 to 872 | 48 to 840 | wait loop at `$EEA9`; the 859 was the drive's `UNLISTEN` after `CLOSE`, the rest 49 to 373 |
|
|
227
|
+
| Listener ready to CLK asserted (byte starts) | C64 | 32; 43 to 98 when the `$EEA9` poll catches the edge late or a badline lands in the gap | 32.5 | 31.3 | `$EE93`; 32 in twelve of the fifteen sent bytes, 43, 75 and 98 in the other three on PAL, 39 and 43 on NTSC |
|
|
228
|
+
| CLK asserted to first bit's CLK release | C64 | 71 to 73 | 72.1 to 74.1 | 69.4 to 71.4 | first pass of the loop at `$ED66` |
|
|
229
|
+
| Bit cell, C64 sending: CLK released | C64 | 26, or 69 with a badline in the released half | 26.4, or 70.0 | 25.4, or 67.5 | `$EE8A` to `$ED8B`; three of the 120 cells in each run were 69 |
|
|
230
|
+
| Bit cell, C64 sending: period | C64 | 94 to 96, or 136 to 139 | 95.4 to 97.4, or 138.0 to 141.1 | 91.9 to 93.9, or 133.0 to 135.9 | `$EE8A` to the next `$EE8A`; the long cells are the ones a badline landed in |
|
|
231
|
+
| Last bit to listener acknowledge (DATA low) | drive | 76 to 80, once 111, once 154 | 77 to 81 | 74 to 78 | wait loop at `$EEA9` after `$ED8B` |
|
|
232
|
+
| EOI, C64 sending: listener ready to the drive's DATA pulse | drive | 609 to 613 | 618 | 599 | the C64 idles at `$EEA9` with CLK released |
|
|
233
|
+
| EOI, C64 sending: the drive's DATA pulse | drive | 80 to 87 | 81 | 85 | then `$EE93`, CLK asserted, 32 cycles after it ends |
|
|
234
|
+
| TALK turnaround: ATN release to C64 CLK release | C64 | 22 | 22.3 | 21.5 | `$EDC3` then `$EE8A` |
|
|
235
|
+
| TALK turnaround: C64 CLK release to drive CLK assert | drive | 70 to 86 | 87 | 68 | wait at `$EEA9` |
|
|
236
|
+
| Receive: drive CLK release to C64 DATA release (listener ready) | C64 | 44 | 44.7 | 43.0 | `$EE9C`, inside ACPTR |
|
|
237
|
+
| Receive: listener ready to drive CLK assert (byte starts) | drive | 63 to 106 | 64 to 108 | 62 to 104 | wait at `$EEA9` |
|
|
238
|
+
| Receive: bit period, drive sending | drive | 157 to 237, mostly 185 to 196 | 188 to 199 | 181 to 192 | CLK-high samples at `$EE5A`/`$EE5D`, CLK-low waits at `$EE67`/`$EE6A` |
|
|
239
|
+
| Receive: last bit to C64 acknowledge (DATA low) | both | 78 to 136 | 79 to 138 | 76 to 133 | `$EEA5` after the loop |
|
|
240
|
+
| EOI, C64 receiving: listener ready to acknowledge | C64 | 539 | 547.1 | 527.0 | timer B armed before `$EE9C`; `$EEA5` when it expires |
|
|
241
|
+
| EOI, C64 receiving: acknowledge pulse | C64 | 84 | 85.3 | 82.1 | `$EEA5` to `$EE9C`, with CLKHI at `$EE8A` between |
|
|
242
|
+
| UNTALK: ATN asserted from a different place | C64 | | | | `STA $DD00` at `$EDF8`, not `$ED33` |
|
|
243
|
+
|
|
244
|
+
Three things the table settles that the prose could not:
|
|
245
|
+
|
|
246
|
+
- **The C64's bit cell is not constant.** The send loop is straight-line code, so its cell is 94 to 96 cycles, but a VIC-II badline steals about 40 cycles from whichever cell it lands in, and one or two of every eight bits were 136 to 139 cycles in every byte sent with the screen on. The drive tolerates it because every bit is handshaken; a loader with cycle-counted loops on the drive side does not, which is why fast loaders blank the screen or sit in the border. Pitfalls `gcr_timing_assumes_stock_drive`, `fastloader_dd00_write_corrupts_resident` and `raster_irq_during_serial_io` are the three places this bites.
|
|
247
|
+
- **The EOI window the C64 applies as listener is 539 cycles, not 256.** ACPTR writes `1` to CIA1 timer B's high byte and force-loads the timer; it never writes the low byte, whose latch is left at whatever the last user set, so the count that ran here was `$01FF`, not `$0100`. That is arithmetic from the trace (539 = 511 plus the loop's overhead), not a measurement of the latch (not measured here). The send-side timeout in "Drive-Not-Ready and Timeout Errors" below writes `4` the same way, so "about 1,024 cycles" is the nominal count and the one that runs is likely `$04FF`, 1,279 cycles; a run in which it fires was not produced (see the caveat).
|
|
248
|
+
- **The drive answers ATN by hardware, not by code.** DATA was low 12 cycles after the ATN store, in both runs, before the drive's CPU could have taken an interrupt. The 1541 gates DATA from ATN in logic, and the KERNAL's 1 ms wait before it looks is for the drive's *software* to catch up, not for the line.
|
|
249
|
+
|
|
250
|
+
**What this measures and what it does not.** The drive rows are VICE's 1541 and nothing else: they are the reply times of an emulated 6502 running the 325302-01+901229-05 ROM under VICE's drive timing, and the real spread across drives, ROM revisions and third-party devices is not in them. The device-not-present path was traced too, in a run with no disk mounted and `-drive8type 0 +drive8truedrive +busdevice8` in place of the drive flags. An earlier version of this paragraph said that run could not be produced and that something still answered ATN; it had been read from a log holding five runs appended (`log on` appends to an existing file, so a log shared between runs must be split per run before it is parsed), and only the first run in it, made with a disk mounted, had been looked at. With nothing on the bus the trace matches the drive run to the cycle up to the test: ATN store at `$ED33`, DATAHI at +40, the second DATAHI at +1,087, the `$EEA9` read at +1,103. There it returns DATA high, the `BCS` at `$ED47` takes the `$EDAD` path, and ATN is released at `$EDC3` 65 cycles after the test read, so the 1,103 window is confirmed from the refusal itself. Not one byte is sent, which is why the send-side timer at `$ED92` was still not seen to fire: that needs a device that answers ATN and then never acknowledges. The `CHKIN` that followed put TALK on the bus at cycle 3,016,512, released ATN at 3,017,723, ran the talk turnaround regardless, and then waited at `$EEA9` for a CLK edge from cycle 3,019,237 to the 4,500,000 limit: the hang "Drive-Not-Ready and Timeout Errors" below describes, with no timeout. The monitor's drive-side breakpoints (`dev 8`, or the `8:` address prefix) were not tried; everything here is the C64's view of the bus.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Drive Commands
|
|
255
|
+
|
|
256
|
+
### High-Level KERNAL Interface
|
|
257
|
+
|
|
258
|
+
The standard way for C64 programs to communicate with a disk drive is via the KERNAL file I/O layer. The typical sequence for loading a file:
|
|
259
|
+
|
|
260
|
+
```asm
|
|
261
|
+
; Set logical file number, device, secondary address (channel)
|
|
262
|
+
; LFN=1, device=8, SA=0 (load to original address)
|
|
263
|
+
LDA #1
|
|
264
|
+
LDX #8
|
|
265
|
+
LDY #0
|
|
266
|
+
JSR $FFBA ; SETLFS
|
|
267
|
+
|
|
268
|
+
; Set filename
|
|
269
|
+
LDA #<filename_len
|
|
270
|
+
LDX #<filename_addr
|
|
271
|
+
LDY #>filename_addr
|
|
272
|
+
JSR $FFBD ; SETNAM
|
|
273
|
+
|
|
274
|
+
; LOAD: A=0 (load), A=1 (verify)
|
|
275
|
+
LDA #0
|
|
276
|
+
LDX #<dest_addr_lo
|
|
277
|
+
LDY #<dest_addr_hi
|
|
278
|
+
JSR $FFD5 ; LOAD
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
For arbitrary file access (read/write):
|
|
282
|
+
|
|
283
|
+
```asm
|
|
284
|
+
JSR $FFC0 ; OPEN — opens the file (uses LFN/device/SA from SETLFS/SETNAM)
|
|
285
|
+
JSR $FFC6 ; CHKIN — redirect character input from file
|
|
286
|
+
JSR $FFCF ; CHRIN — read one byte
|
|
287
|
+
JSR $FFC9 ; CHKOUT — redirect character output to file
|
|
288
|
+
JSR $FFD2 ; CHROUT — write one byte
|
|
289
|
+
JSR $FFCC ; CLRCHN — restore default I/O channels
|
|
290
|
+
JSR $FFC3 ; CLOSE — close the file
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
See `../hardware/kernal-routines-reference.md` for the full register contracts, error-status handling, and secondary address conventions for each jump-table entry.
|
|
294
|
+
|
|
295
|
+
### Secondary Addresses and Channels
|
|
296
|
+
|
|
297
|
+
The secondary address (SA) passed to SETLFS encodes the channel number and access type:
|
|
298
|
+
|
|
299
|
+
| Secondary address | Meaning |
|
|
300
|
+
|------------------|---------|
|
|
301
|
+
| 0 | LOAD (drive sends file to C64 memory) |
|
|
302
|
+
| 1 | SAVE (C64 sends file to drive) |
|
|
303
|
+
| 2–14 | Arbitrary I/O channels |
|
|
304
|
+
| 15 | Command/status channel |
|
|
305
|
+
|
|
306
|
+
The **command channel** (SA=15, device=8) is a special bidirectional channel for sending DOS commands to the drive and reading the drive status. To scratch a file:
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
OPEN 1,8,15,"S0:FILENAME"
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Common drive commands sent to the command channel:
|
|
313
|
+
|
|
314
|
+
| Command | Example | Effect |
|
|
315
|
+
|---------|---------|--------|
|
|
316
|
+
| SCRATCH | `S0:FILENAME` | Delete file |
|
|
317
|
+
| RENAME | `R0:NEW=OLD` | Rename file |
|
|
318
|
+
| COPY | `C0:DST=0:SRC` | Copy file |
|
|
319
|
+
| VALIDATE | `V0` | Rebuild BAM (like CHKDSK) |
|
|
320
|
+
| INITIALIZE | `I0` | Force drive to re-read BAM |
|
|
321
|
+
| NEW | `N0:NAME,ID` | Format disk |
|
|
322
|
+
| BLOCK-READ | `B-R chn drv trk sec` | Read arbitrary sector |
|
|
323
|
+
| BLOCK-WRITE | `B-W chn drv trk sec` | Write arbitrary sector |
|
|
324
|
+
| MEMORY-READ | `M-R addrlo addrhi len` | Read drive RAM/ROM |
|
|
325
|
+
| MEMORY-WRITE | `M-W addrlo addrhi len data` | Write drive RAM |
|
|
326
|
+
| MEMORY-EXECUTE | `M-E addrlo addrhi` | Execute code in drive RAM |
|
|
327
|
+
|
|
328
|
+
Reading from the command channel after any operation returns the drive status string: a 2-digit error code, message text, track number, sector number, and newline. Error code `00` means no error.
|
|
329
|
+
|
|
330
|
+
### Sequential vs Random Access Files
|
|
331
|
+
|
|
332
|
+
**Sequential files** (PRG, SEQ, USR types) are stored as a linked chain of 256-byte sectors (254 bytes of data per sector; first 2 bytes are next-track/next-sector pointers). Reading is strictly forward.
|
|
333
|
+
|
|
334
|
+
**Random access files** (REL type) allow seeking to arbitrary records. They use a fixed record length declared at file-open time and maintain side-sectors — dedicated bookkeeping sectors that map logical record numbers to physical track/sector locations. REL files are rarely used in demo/game code but common in productivity applications. The on-disk layout (directory entry bytes, side-sector fields, record padding, the P command's byte order) is decoded from images the 1541 wrote in `c64-file-formats.md`, ".D64", under "REL file".
|
|
335
|
+
|
|
336
|
+
### The 1541 DOS Error Codes
|
|
337
|
+
|
|
338
|
+
The status line is `cc,message,tt,ss` followed by a CR: a two-digit code, the text, then a track and a sector in decimal. The 1541 ROM assembles it at `$E6C7` into the buffer at `$02D5`: two BCD digits from the code, a comma, the text looked up in the table below, a comma, the track, a comma, the sector (rung 1: the `dos1541-325302-01+901229-05` image in `/opt/homebrew/opt/vice/share/vice/DRIVES/`, bytes `$E6C7`–`$E705`). Every code the ROM can put in that line is in this table. The message column is the text field exactly as it comes back, including the leading space some messages have; the "Provoked" column says whether the recipe `../recipes/kickassembler/dos-error-codes.md` produced that reply in VICE x64sc 3.10 (rung 1) or whether the text is only read from the ROM.
|
|
339
|
+
|
|
340
|
+
| Code | Message field | Cause | Class | Provoked |
|
|
341
|
+
|---|---|---|---|---|
|
|
342
|
+
| 00 | ` OK` | no error; the line reads `00, OK,00,00` | none | yes |
|
|
343
|
+
| 01 | ` FILES SCRATCHED` | the reply to SCRATCH; the track field is the number of files removed, so `01, FILES SCRATCHED,00,00` means nothing matched | none | yes (`S0:T` after writing T: `01, FILES SCRATCHED,01,00`) |
|
|
344
|
+
| 20 | `READ ERROR` | block header not found (job code 2) | media | ROM text only |
|
|
345
|
+
| 21 | `READ ERROR` | no sync found (job code 3): unformatted track, no disk, or the drive did not come up to speed | media | ROM text only |
|
|
346
|
+
| 22 | `READ ERROR` | data block not found after the header (job code 4) | media | ROM text only |
|
|
347
|
+
| 23 | `READ ERROR` | checksum error in the data block (job code 5) | media | ROM text only |
|
|
348
|
+
| 24 | `READ ERROR` | job code 6, and also job code 0 (the ROM maps both here); the 1541 manual calls it a byte-decoding error (rung 4) | media | ROM text only |
|
|
349
|
+
| 25 | ` WRITE ERROR` | write-verify mismatch (job code 7) | retry, then media | ROM text only |
|
|
350
|
+
| 26 | ` WRITE PROTECT ON` | write attempted with the notch covered (job code 8) | user error | yes, in a side run with `-attach8ro`: `26, WRITE PROTECT ON,18,00` |
|
|
351
|
+
| 27 | `READ ERROR` | checksum error in the block header (job code 9) | media | ROM text only |
|
|
352
|
+
| 28 | ` WRITE ERROR` | no sync after the data block was written, a long data block (job code 10) | media | ROM text only |
|
|
353
|
+
| 29 | ` DISK ID MISMATCH` | the sector header's ID is not the one in the BAM (job code 11): a disk was changed without INITIALIZE, or a disk was formatted over | user error | ROM text only |
|
|
354
|
+
| 30 | `SYNTAX ERROR` | the command parser could not make sense of the string (issued at `$C263`, `$C923`, `$CC2B`, `$D837`) | program bug | yes (`R0:A`, a RENAME with no `=`) |
|
|
355
|
+
| 31 | `SYNTAX ERROR` | the command letter does not exist, or the letter after `M-` or `B-` is not one the DOS has (`$C175`, `$C8C1`, `$CB4B`, `$CC26`) | program bug | yes (`XYZ`) |
|
|
356
|
+
| 32 | `SYNTAX ERROR` | the command string is too long (`$C2D7`) | program bug | ROM text only |
|
|
357
|
+
| 33 | `SYNTAX ERROR` | a wildcard in a name where none is allowed (`$D8F0`, `$EE14`) | program bug | yes (`T*,S,W`) |
|
|
358
|
+
| 34 | `SYNTAX ERROR` | no file name after the command (`$C1F3`) | program bug | yes (`N` alone) |
|
|
359
|
+
| 39 | ` FILE NOT FOUND` | raised at one site only, `$E7C0`; that it is the `&` utility-loader command's not-found case is rung 4 | user error | ROM text only |
|
|
360
|
+
| 50 | ` RECORD NOT PRESENT` | REL file: positioned past the last record; the DOS also reports it when a write extends the file (`$D9BE`, `$E169`, `$E449`) | program bug, or expected when extending | ROM text only |
|
|
361
|
+
| 51 | `OVERFLOW IN RECORD` | REL file: more bytes written than the record length (`$E297`) | program bug | ROM text only |
|
|
362
|
+
| 52 | ` FILE TOO LARGE` | REL file: the record position would need more blocks than the disk has (`$E363`) | program bug | ROM text only |
|
|
363
|
+
| 60 | ` WRITE FILE OPEN` | opening a file that is still open for write, an unclosed entry (`$D957`) | program bug | ROM text only |
|
|
364
|
+
| 61 | ` FILE NOT OPEN` | a data channel used with no file open on it (`$CFF8`) | program bug | ROM text only |
|
|
365
|
+
| 62 | ` FILE NOT FOUND` | the name is not in the directory (`$CAE1`, `$D945`); OPEN on the C64 side still returns C=0 | user error | yes (`NOFILE,S,R`) |
|
|
366
|
+
| 63 | ` FILE EXISTS` | open for write on a name that exists, without `@` (`$CAEF`, `$D8EB`) | user error | yes (`T,S,W` a second time) |
|
|
367
|
+
| 64 | ` FILE TYPE MISMATCH` | the type in the open string is not the entry's type (`$C982`, `$D965`, `$E223`) | program bug | yes (`T,P,R` on a SEQ file) |
|
|
368
|
+
| 65 | `NO BLOCK` | B-A on a block already allocated; the track and sector fields give the next free block, or `00,00` if none (`$CD31`) | program bug | ROM text only |
|
|
369
|
+
| 66 | `ILLEGAL TRACK OR SECTOR` | a block command named a track or sector that does not exist; the fields echo the request (`$D54D`) | program bug | yes (`B-R 2 0 40 0`: `66,ILLEGAL TRACK OR SECTOR,40,00`) |
|
|
370
|
+
| 67 | `ILLEGAL TRACK OR SECTOR` | a file chain or the BAM points at a block that does not exist (`$DC01`, `$E202`, `$F1DA`); same text as 66 | media | ROM text only |
|
|
371
|
+
| 70 | `NO CHANNEL` | no drive buffer free: the 1541 lends four to data channels (seven sites, among them `$CBA0`, `$D212`, `$E214`) | program bug | yes (a fifth `#` open) |
|
|
372
|
+
| 71 | `DIR ERROR` | the BAM disagrees with itself while allocating (`$F1F5`, `$F246`); VALIDATE rebuilds it | media | ROM text only |
|
|
373
|
+
| 72 | ` DISK FULL` | no free block, or no free directory entry (`$F15A`) | user error | ROM text only |
|
|
374
|
+
| 73 | `CBM DOS V2.6 1541` | the power-on message, put there at reset (`$EBD5`); also raised as an error at `$D575` when the BAM's DOS-version byte is not the ROM's `$41` and a write was attempted | none on the first read; media after an access | yes (first read) |
|
|
375
|
+
| 74 | `DRIVE NOT READY` | no disk, or the drive could not read it (job code 15, and `$C41B`) | user error | yes, in a side run with no image attached: `74,DRIVE NOT READY,00,00` |
|
|
376
|
+
|
|
377
|
+
The provoked lines are the twelve on the recipe's screenshot plus the two side runs. The side runs used the same PRG; `-attach8ro` before `-8 disk.d64` attaches the image read-only and the write step answered 26 (an `-attach8rw` option also exists), and a run with no `-8` at all answered 74 on the first OPEN. For codes marked "ROM text only" the message field is assembled from the table below by the same routine, so the text is rung 1; the cause column for those rows is from the 1541 manual and the ROM's call sites, and the track and sector fields were not measured.
|
|
378
|
+
|
|
379
|
+
The class column is for an agent deciding what to do with the line: **retry** means try the operation again once, **media** means the disk or drive is at fault and no retry will help, **user error** means the program is fine and the person needs to act (insert a disk, free space, remove a file), **program bug** means the command string or call sequence is wrong. Codes 20 to 29 all carry the failing track and sector.
|
|
380
|
+
|
|
381
|
+
**How the text is stored.** The message table runs from `$E4FC` to `$E5D4`, and a word table from `$E5D5` to `$E609` follows it (rung 1). An entry is one or more BCD code bytes followed by the text; the first and the last byte of a text carry bit 7 set, which is how the reader finds the ends, and several codes share one text (`20 21 22 23 24 27` precede `READ ERROR`, `25 28` precede `WRITE ERROR`, `30`–`34` precede `SYNTAX ERROR`, `39 62` precede `FILE NOT FOUND`, `66 67` precede `ILLEGAL TRACK OR SECTOR`). Nine words are stored once and referred to by a byte below `$10`:
|
|
382
|
+
|
|
383
|
+
| Token | Word | At |
|
|
384
|
+
|---|---|---|
|
|
385
|
+
| `$03` | FILE | `$E5E1` |
|
|
386
|
+
| `$04` | OPEN | `$E5E6` |
|
|
387
|
+
| `$05` | MISMATCH | `$E5EB` |
|
|
388
|
+
| `$06` | NOT | `$E5F4` |
|
|
389
|
+
| `$07` | FOUND | `$E5F8` |
|
|
390
|
+
| `$08` | DISK | `$E5FE` |
|
|
391
|
+
| `$09` | ERROR | `$E5D5` |
|
|
392
|
+
| `$0A` | WRITE | `$E5DB` |
|
|
393
|
+
| `$0B` | RECORD | `$E603` |
|
|
394
|
+
|
|
395
|
+
The lookup at `$E706` scans from `$E4FC` for a byte equal to the code, skips to the text, and copies it byte by byte; a byte below `$20` is a token, and the copier at `$E754` writes a space and then looks the token up through the same routine (the scan runs on past `$E5D5` into the word table). That space is why `62` reads `62, FILE NOT FOUND` with a space after the comma while `31` reads `31,SYNTAX ERROR` without one: a message that starts with a token gets the token's space, a message that starts with a literal letter does not, and `00` has its space stored as a literal `$A0`. Entry 62 is the three bytes `83 06 87` at `$E58F`, tokens FILE, NOT, FOUND with the end bits on the first and last. A code that is not in the table at all comes back with an empty text field; the scan stops at `$E60A`.
|
|
396
|
+
|
|
397
|
+
**D64 error bytes.** The per-sector error byte a `.d64` image can carry (683 bytes after the sector data, see `c64-file-formats.md`) is not the DOS number. It is the drive's job return code, the value the sector routines hand back, and the conversion to a DOS number is at `$E60A`–`$E62C` (rung 1): the code is masked to its low four bits; 0 becomes 24, 15 becomes 74, and anything else is ORed with `$20` and decremented twice, which reads as a decimal code because the results stay below `$2A`.
|
|
398
|
+
|
|
399
|
+
| Byte in the image | DOS code | Message |
|
|
400
|
+
|---|---|---|
|
|
401
|
+
| `$01` | none | sector read cleanly |
|
|
402
|
+
| `$02` | 20 | READ ERROR, header not found |
|
|
403
|
+
| `$03` | 21 | READ ERROR, no sync |
|
|
404
|
+
| `$04` | 22 | READ ERROR, data block not found |
|
|
405
|
+
| `$05` | 23 | READ ERROR, data checksum |
|
|
406
|
+
| `$06` | 24 | READ ERROR |
|
|
407
|
+
| `$07` | 25 | WRITE ERROR, verify |
|
|
408
|
+
| `$08` | 26 | WRITE PROTECT ON |
|
|
409
|
+
| `$09` | 27 | READ ERROR, header checksum |
|
|
410
|
+
| `$0A` | 28 | WRITE ERROR |
|
|
411
|
+
| `$0B` | 29 | DISK ID MISMATCH |
|
|
412
|
+
| `$0F` | 74 | DRIVE NOT READY |
|
|
413
|
+
|
|
414
|
+
A `$00` byte is treated as no error by image tools. So a D64 can carry 20 to 29 and 74 and nothing else; the 3x, 5x, 6x and 7x codes are conditions of a command or a file, not of a sector, and no image byte produces them. That the image's byte is the job code is the D64 format's convention (rung 4, from the format's documentation); the arithmetic from job code to DOS number is the ROM's. Which of these bytes VICE's 1541 emulation reproduces on a read is measured in `../pitfalls/loader.md` (`d64_error_byte_is_a_controller_code`): VICE 3.10 honours `$02`, `$03`, `$04`, `$05`, `$09` and `$0B` and ignores `$07`, `$08` and `$0F`, and a single sector flagged `$03` or `$0B` reports 20 rather than 21 or 29 unless the whole track carries the code.
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## Identifying the drive over the command channel
|
|
419
|
+
|
|
420
|
+
`M-R` (memory read) on the command channel returns raw bytes from the drive's own address space, and each DOS keeps its power-on message in ROM at a fixed place. Four bytes therefore say which firmware is answering — and the same four bytes are the honest form of the "is this a real 1541?" test that a GCR fast loader wants before it uploads drive code (`../pitfalls/loader.md`, `gcr_timing_assumes_stock_drive`).
|
|
421
|
+
|
|
422
|
+
**Where the string is** (rung 1: the bytes of the drive ROM images VICE 3.10 ships in `/opt/homebrew/opt/vice/share/vice/DRIVES/`). In the 1541 the error-message table holds entry 73 at `$E5B6`: the number byte `$73`, then `CBM DOS V2.6 1541` from `$E5B7` to `$E5C7`, with bit 7 set on the first text byte (`$C3`) and on the last (`$B1`) — that is how the table marks a message's ends. The same entry in the other images:
|
|
423
|
+
|
|
424
|
+
| VICE image | Drive | Text at `$E5BF` | Bytes at `$E5C4`–`$E5C7` | Byte at `$E5C3` |
|
|
425
|
+
|---|---|---|---|---|
|
|
426
|
+
| `dos1541-325302-01+901229-05` | 1541 | `V2.6 1541` | `31 35 34 B1` — "1541" | `$20` |
|
|
427
|
+
| `dos1541ii-251968-03` | 1541-II | `V2.6 1541` | `31 35 34 B1` — identical | `$20` |
|
|
428
|
+
| `dos1540-325302+3-01` | 1540 | `V2.6 V170` | `56 31 37 B0` — "V170" | `$20` |
|
|
429
|
+
| `dos1571-310654-05` | 1571 | `V3.0 1571` | `31 35 37 B1` — "1571" | `$20` |
|
|
430
|
+
| `dos1571cr-318047-01` | 1571 (C128DCR) | `V3.1 1571` | `31 35 37 B1` — "1571" | `$20` |
|
|
431
|
+
| `dos1570-315090-01` | 1570 | `V3.0 1570` | `31 35 37 B0` — "1570" | `$20` |
|
|
432
|
+
| `dos1581-318045-02` | 1581 | `FF FF FF FF …` | `FF FF FF FF` | `$FF` |
|
|
433
|
+
|
|
434
|
+
The 1581's ROM (32 KB from `$8000`) has nothing at that address. Its message is `COPYRIGHT CBM DOS V10 1581`: entry 73 at `$A6D0`, text from `$A6D1`, with "1581" at `$A6E7`–`$A6EA` (`31 35 38 B1`). The 1551 image (`dos1551-318008-01`) is a different bus altogether — the Plus/4's parallel port, not IEC (rung 4) — and is laid out differently again.
|
|
435
|
+
|
|
436
|
+
**The command.** From the 1541's handler (MEMRD, `$CB20`, rung 1 from the bytes): the letter after `M-` is compared with `R`, `W` and `E` — anything else is error 31 — the address is taken from `$0203` (low byte) and `$0204` (high byte), and if the command is at least six bytes long the byte at `$0205` is the count; a shorter command, or a count of 1, returns one byte. So the full form is the six bytes `M-R` `lo` `hi` `count`, sent as the "filename" of an OPEN on secondary address 15 (or with `PRINT#`; the CR it appends is ignored). The drive then delivers `count` bytes on channel 15, the last with EOI; read them with CHRIN after CHKIN 15 (or `GET#`, one per call — `INPUT#` splits its input at a CR, a comma or a colon, so it is the wrong tool for raw bytes).
|
|
437
|
+
|
|
438
|
+
**A fragment that shows the answer.** Opens the command channel, asks for the four bytes at `$E5C4`, strips the end-marker bit and puts them at the top left of the screen (digits have the same code in PETSCII and in screen code):
|
|
439
|
+
|
|
440
|
+
```kick
|
|
441
|
+
show_drive_id:
|
|
442
|
+
lda #15
|
|
443
|
+
ldx #8
|
|
444
|
+
ldy #15
|
|
445
|
+
jsr $ffba // SETLFS: logical file 15, device 8, channel 15
|
|
446
|
+
lda #cmd_end-cmd
|
|
447
|
+
ldx #<cmd
|
|
448
|
+
ldy #>cmd
|
|
449
|
+
jsr $ffbd // SETNAM: the command text travels as the "filename"
|
|
450
|
+
jsr $ffc0 // OPEN sends it
|
|
451
|
+
bcs fail
|
|
452
|
+
ldx #15
|
|
453
|
+
jsr $ffc6 // CHKIN: channel 15 becomes the input channel
|
|
454
|
+
ldy #0
|
|
455
|
+
loop:
|
|
456
|
+
jsr $ffcf // CHRIN: one reply byte per call
|
|
457
|
+
and #$7f // the string's last byte carries bit 7 as its end marker
|
|
458
|
+
sta $0400,y
|
|
459
|
+
lda #1
|
|
460
|
+
sta $d800,y
|
|
461
|
+
iny
|
|
462
|
+
cpy #4
|
|
463
|
+
bne loop
|
|
464
|
+
jsr $ffcc // CLRCHN
|
|
465
|
+
lda #15
|
|
466
|
+
jsr $ffc3 // CLOSE
|
|
467
|
+
fail:
|
|
468
|
+
rts
|
|
469
|
+
cmd:
|
|
470
|
+
.byte $4d, $2d, $52 // "M-R" in PETSCII
|
|
471
|
+
.byte $c4, $e5 // address $E5C4, low byte first
|
|
472
|
+
.byte $04 // byte count
|
|
473
|
+
cmd_end:
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Run in VICE x64sc 3.10 (PAL) with `-drive8truedrive` and a freshly formatted `.d64` attached (rung 1): `-drive8type 1541`, `1542` (the 1541-II) and the default drive each put `1541` on the screen; `-drive8type 1571` puts `1571`; `-drive8type 1581` with a `.d81` puts four `$7F` glyphs, the `$FF` bytes with bit 7 stripped. Each glyph was matched against the character ROM's bitmap with zero differing pixels. With true drive emulation off the row stayed blank — the answer comes from the emulated drive's ROM, not from a VICE shortcut.
|
|
477
|
+
|
|
478
|
+
**`$41` is not a ROM byte.** The `$41` ("A") that detection routines sometimes go looking for is the DOS-version marker at offset 2 of the BAM sector, track 18 sector 0, written to the disk when it is formatted (rung 1: `c1541 -format test,01 d64 test.d64`, then byte 357 × 256 + 2 of the file reads `$41`). The 1541 ROM holds that constant at `$FED5` (VERNUM in the listing) and compares the BAM's byte against it. Testing for `$41` at `$E5C3` confuses the two: `$E5C3` is `$20`, the space between `V2.6` and `1541`, in every 1541-family image above, so such a test never passes.
|
|
479
|
+
|
|
480
|
+
One thing observed and not explained: in VICE, asserting ATN by hand with CLK left released did not get DATA pulled by the emulated 1541 within 330 ms, while ATN together with CLK — which is what the KERNAL does — did. Do as the KERNAL does; the mechanism was not chased.
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## 1541 Drive ROM
|
|
485
|
+
|
|
486
|
+
The 1541 has its own 6502 at 1 MHz, 2 KiB of RAM at `$0000`–`$07FF` (rung 4) and 16 KiB of ROM at `$C000`–`$FFFF` (rung 1: the image is 16,384 bytes and its reset vector at `$FFFC` reads `$EAA0`). The firmware — CBM DOS 2.6 — runs the file system by itself; the C64 only ever talks to it over the bus.
|
|
487
|
+
|
|
488
|
+
Entry points, for reading a disassembly or for code uploaded with `M-W` and started with `M-E`. The bytes are rung 1 from the `dos1541-325302-01+901229-05` image and are identical in `dos1541ii-251968-03` and, except where the table says otherwise, in the 1540 image; the names are those of the g3sl.github.io listing, which takes them from *Inside Commodore DOS* (rung 4).
|
|
489
|
+
|
|
490
|
+
| Address | First bytes | Name | What it is |
|
|
491
|
+
|---|---|---|---|
|
|
492
|
+
| `$C100` | `78 A9 F7 2D 00 1C 48` | SETLDS | turn on the drive-active LED |
|
|
493
|
+
| `$CB20` | `B1 6F 85 85 AD 74 02 C9 06` | MEMRD | the `M-R` handler described above |
|
|
494
|
+
| `$D042` | `20 D1 F0 20 13 D3 20 0E D0` | INITDR | initialise the drive: read the BAM |
|
|
495
|
+
| `$E5B6` | `73 C3 42 4D 20 44 4F 53` | — | error table, entry 73: `CBM DOS V2.6 1541` (the 1540 has `V170` at `$E5C4`) |
|
|
496
|
+
| `$E85B` | `78 A9 00 85 7C 85 79 85 7A` | ATNSRV | ATN service: where the drive goes when the C64 asserts ATN |
|
|
497
|
+
| `$E909` | `78 20 EB D0 B0 06` | TALK | send bytes on the bus as talker |
|
|
498
|
+
| `$E9C9` | `A9 08 85 98 20 59 EA 20 C0 E9` | ACPTR | receive one byte from the bus |
|
|
499
|
+
| `$EBE7` | `58 AD 00 18 29 E5 8D 00 18` | IDLE | the idle loop; on entry it clears the DATA OUT, CLK OUT and ATNA bits of `$1800` |
|
|
500
|
+
| `$F4CA` | `C9 00 F0 03 4C 6E F5` | READ | job dispatch for a read job; not a sector read itself, and it hands anything else to WRIGHT |
|
|
501
|
+
| `$F56E` | `C9 10 F0 03 4C 91 F6` | WRIGHT | job dispatch for a write job |
|
|
502
|
+
| `$F5E9` | `A9 00 A8 51 30 C8 D0 FB 60` | CHKBLK | EOR checksum over the 256-byte data block |
|
|
503
|
+
| `$F78F` | `A9 00 85 30 85 2E 85 36 A9 BB` | BINGCR | convert the buffer to its GCR image |
|
|
504
|
+
| `$FED1` | `11 12 13 15` | — | sectors per track by zone index 0–3: 17, 18, 19, 21 |
|
|
505
|
+
| `$FED5` | `41` | VERNUM | the DOS-version byte the formatter writes into the BAM |
|
|
506
|
+
| `$FED7` | `24 1F 19 12` | MAXTRK | zone boundaries: 36, 31, 25, 18 |
|
|
507
|
+
|
|
508
|
+
The listing itself is at `https://g3sl.github.io/c1541rom.html`; it annotates the 325302-01 + 901229-01 pair, and the instruction it shows at each address above agrees with the -05 bytes quoted. Most fast loaders call none of this: they upload their own drive code with `M-W` and start it with `M-E`.
|
|
509
|
+
|
|
510
|
+
**Correction (2026-09-21).** The previous table was wrong in every row: `$C100` was called the "main idle/command loop" (it is the LED routine; the idle loop is `$EBE7`), `$E505` "bump head to track 1" (it is a data byte in the error-message table: `$E500`–`$E505` hold the numbers `20 21 22 23 24 27` that share one text, `$E505` is the last of them, `$27`, and `READ` starts at `$E506` with `$D2`), `$F5E9` "transmit byte via IEC" (the block checksum) and `$F78F` "receive byte via IEC" (the GCR conversion); `$D486` is OPNIWR, open an internal write channel, not the formatter, and `$C8B6` and `$C83C` are directory-entry deletion inside the scratch code, not sector read and write. It also pointed at `pagetable.com/c64ref/1541/`, which could not be reached from here to confirm it exists; the g3sl listing above was read.
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
## Custom Code on the 1541
|
|
515
|
+
|
|
516
|
+
### Drive RAM and the Parallel Trick
|
|
517
|
+
|
|
518
|
+
The 1541 has 2 KiB of general-purpose RAM. Because the drive's 6502 operates independently of the C64's 6510, both CPUs can coordinate via the IEC bus for synchronization, enabling **parallel loading**: data is transferred over all 8 bits of the user port (Centronics-style) simultaneously rather than serially over the 1-bit IEC DATA line. Combined with bit-banging on the drive side, this achieves 10–25 KB/sec — 30–80x faster than the standard KERNAL loader.
|
|
519
|
+
|
|
520
|
+
The technique requires custom code running on the drive CPU. The C64 uploads the drive-side routine via the command channel's `M-W` (Memory Write) command, then starts it with `M-E` (Memory Execute). Once the drive routine is running, both sides enter a tight handshake loop using the user-port lines for data and the IEC bus for control.
|
|
521
|
+
|
|
522
|
+
### 1541 job queue and buffers
|
|
523
|
+
|
|
524
|
+
The addresses uploaded code uses to ask the controller for a sector. The rows marked "run" were exercised by `../recipes/kickassembler/drive-job-queue.md` in VICE x64sc 3.10 with true drive emulation of a 1541 (rung 1); the rest carry the names and meanings of the g3sl.github.io ROM listing (rung 4) and were not run here.
|
|
525
|
+
|
|
526
|
+
| Address | Name | Meaning | Status |
|
|
527
|
+
|---|---|---|---|
|
|
528
|
+
| `$00`–`$05` | JOBS | one job byte per buffer 0–5; bit 7 set means pending, the controller replaces it with a result code | `$01` run |
|
|
529
|
+
| `$06`–`$11` | HDRS | track and sector for each job, two bytes per buffer: `$06`/`$07` buffer 0, `$08`/`$09` buffer 1, up to `$10`/`$11` buffer 5 | `$08`/`$09` run: read back `12 00` |
|
|
530
|
+
| `$12`–`$13` | DSKID | the master disk ID the controller compares each header against; set by a seek job and by `I` | run: `00 00` until a seek, then `30 31` |
|
|
531
|
+
| `$16`–`$1A` | HEADER | the last header read: ID, ID, track, sector, checksum | not run |
|
|
532
|
+
| `$0300`–`$06FF` | buffers 0–3 | data buffers lent to channels | `$0400` and `$0600` run |
|
|
533
|
+
| `$0700`–`$07FF` | buffer 4 | the BAM | not run |
|
|
534
|
+
|
|
535
|
+
| Job code | Meaning | Status |
|
|
536
|
+
|---|---|---|
|
|
537
|
+
| `$80` | read the sector into the buffer | run |
|
|
538
|
+
| `$90` | write the buffer to the sector | not run |
|
|
539
|
+
| `$A0` | verify | not run |
|
|
540
|
+
| `$B0` | seek: find any header on the track, keep its ID | run |
|
|
541
|
+
| `$C0` | bump the head to track 1 | not run |
|
|
542
|
+
| `$D0` | jump to code in the buffer | not run |
|
|
543
|
+
| `$E0` | execute code in the buffer once the motor is up to speed | not run |
|
|
544
|
+
|
|
545
|
+
| Result | Meaning | Status |
|
|
546
|
+
|---|---|---|
|
|
547
|
+
| `$01` | done | seen: seek and read |
|
|
548
|
+
| `$02` | header not found | not seen |
|
|
549
|
+
| `$03` | no sync | seen: read of track 40 on a 35-track image |
|
|
550
|
+
| `$04` | data block not found | not seen |
|
|
551
|
+
| `$05` | data checksum error | not seen |
|
|
552
|
+
| `$07` | verify error | not seen |
|
|
553
|
+
| `$08` | write protect | not seen |
|
|
554
|
+
| `$09` | header checksum error | not seen |
|
|
555
|
+
| `$0A` | data block too long | not seen |
|
|
556
|
+
| `$0B` | ID mismatch | seen: read before any seek or `I` |
|
|
557
|
+
| `$10` | byte decoding error | not seen |
|
|
558
|
+
|
|
559
|
+
The result codes are the same numbers a `.d64` error block carries; `../pitfalls/loader.md`, `d64_error_byte_is_a_controller_code`, maps them to the error-channel numbers. Two more things measured by the same recipe: a job that fails leaves the error channel at `00, OK,00,00`, and a bare read of channel 15 after an `M-R` has been consumed returns one CR. `M-E` returns to the idle loop on the routine's RTS, and the host's next command waits until it does.
|
|
560
|
+
|
|
561
|
+
### Notable Fastloaders
|
|
562
|
+
|
|
563
|
+
Several widely-used fastloaders from the demoscene implement this approach:
|
|
564
|
+
|
|
565
|
+
- **Krill's Loader** — widely used in modern demos; open source; supports 1541/1571/1581/SD2IEC; PAL and NTSC safe via CIA-timer calibration
|
|
566
|
+
- **Spindle** — DreamLoad-compatible, optimized for original 1541 hardware
|
|
567
|
+
- **DreamLoad** — older but common in late-1990s/early-2000s releases
|
|
568
|
+
- **Kung Fu Flash loader** — targets flash-cart hardware with direct SD access
|
|
569
|
+
|
|
570
|
+
From the KB's toolchain perspective, a fastloader is an assembly module linked into the PRG (Oscar64: inline asm or an external `.asm` included via the linker; KickAssembler: `import binary` or included source). The drive-side routine is a binary blob uploaded at runtime. Oscar64 or KickAssembler produce the drive-side stub as a `.BIN` and the loader includes it as a `char[]` array or embedded resource.
|
|
571
|
+
|
|
572
|
+
### Timing Considerations
|
|
573
|
+
|
|
574
|
+
The 1541's 6502 runs at 1 MHz from a 16 MHz crystal divided by sixteen — a fixed oscillator that has nothing to do with the disk. What changes with the track is the bit-cell clock: a programmable counter divides the same 16 MHz by 13, 14, 15 or 16 under two "density" bits, so the longer outer tracks are written denser in time and hold more sectors (rung 4: Ruud Baltissen's page on the 1541 board, which names a 74177 as the ÷16 stage and a 74LS193 as the counter the VIA's PB5/PB6 "density bits" preload, with divisors 13, 14, 15 and 16 for tracks 1–17, 18–24, 25–30 and 31–35 — not measured here). The ROM's half of that is rung 1: at `$F33C`–`$F358` the DOS compares the track against the boundary table at `$FED7` (36, 31, 25, 18), ending with a zone index of 3 for tracks 1–17 down to 0 for tracks 31–35, fetches the matching sectors-per-track from `$FED1` (21, 19, 18, 17 for indices 3 to 0), shifts the index left five places and writes it into bits 5–6 of `$1C00` with `LDA $1C00 / AND #$9F / ORA $44 / STA $1C00`. Index 3 is the fastest bit clock, index 0 the slowest.
|
|
575
|
+
|
|
576
|
+
Drives still vary — crystals have tolerances and spindles do not all turn at exactly 300 rpm — and a fast loader with cycle-counted loops on both ends has to leave room for that; the KERNAL protocol is immune because every bit is handshaken. The PAL/NTSC difference is on the C64 side only: 985,248 Hz against 1,022,727 Hz, while the drive is 1 MHz in both regions, so a loader that counts C64 cycles against drive cycles must know which C64 it is on. The `c64_pal_ntsc_diff` tool in this KB has the numbers.
|
|
577
|
+
|
|
578
|
+
**Correction (2026-09-21).** The earlier text said the drive CPU's clock was "derived from the disk rotation rate, synchronous with the GCR bit cells" and that "PAL C64 drives run at 985,248 Hz". Neither is so: the CPU clock is the crystal, only the bit clock is switched, and it is switched by track zone rather than by anything measured off the disk; the two frequencies quoted are the C64's, not the drive's.
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
## 1541 VIA registers, measured
|
|
583
|
+
|
|
584
|
+
The drive has two 6522 VIAs: VIA1 at `$1800`, whose port B is the serial bus, and VIA2 at `$1C00`, whose port B drives the mechanism and whose port A is the byte under the head. Every value below was read in VICE x64sc 3.10 with true drive emulation of a 1541 by `../recipes/kickassembler/drive-via-probe.md` (rung 1), on a disk formatted `TEST,01`, in these states:
|
|
585
|
+
|
|
586
|
+
- **rest**: after power-up, before any job; the host's `M-R` is the first command after the bare open of channel 15.
|
|
587
|
+
- **job**: the drive's own copy of the port, taken by uploaded code the instant a seek job's code byte came back below `$80`, motor still on. Five seeks: track 18 requested twice, then 1, 25, 31.
|
|
588
|
+
- **host**: the host's `M-R` of the same port after `M-E` returned, a few milliseconds later.
|
|
589
|
+
- **idle**: `M-R` after five seconds of C64 time with no command.
|
|
590
|
+
- **init**: `M-R` after `OPEN 2,8,2,"#"` and again after `CLOSE 2`; the open made the DOS initialise the disk.
|
|
591
|
+
- **trace**: a `-moncommands` file with `trace store 8:1c00` and no attached command, logging every write to `$1C00` over the PAL run with the writer's address and the byte.
|
|
592
|
+
|
|
593
|
+
Bit names are the DOS ROM listing's (g3sl.github.io, from *Inside Commodore DOS*, rung 4); the values are rung 1. The same picture came out byte-identical on two runs per model; PAL and NTSC differed only where the table says.
|
|
594
|
+
|
|
595
|
+
**VIA1, `$1800`, serial bus.**
|
|
596
|
+
|
|
597
|
+
| Address | Bit | Name | Read here |
|
|
598
|
+
|---|---|---|---|
|
|
599
|
+
| `$1800` | 0 | DATA IN | rest `1` both models; job `1` (all five); host `1` |
|
|
600
|
+
| `$1800` | 1 | DATA OUT | `0` in every state (output; DDR bit set) |
|
|
601
|
+
| `$1800` | 2 | CLK IN | rest `1` PAL, `0` NTSC; job `0` (all five); host `1` |
|
|
602
|
+
| `$1800` | 3 | CLK OUT | `0` in every state (output) |
|
|
603
|
+
| `$1800` | 4 | ATNA, attention acknowledge | `0` in every state (output). Set by a drive program it reads back as `1` (`11`, `90`, `13`, `93` in the runs behind `pitfalls/loader.md#atn_assert_drives_data_low_via_atna`) |
|
|
604
|
+
| `$1800` | 5, 6 | device number jumpers | `0 0` in every state: device 8. Another number: not measured here (x64sc 3.10 has no option to move drive 8) |
|
|
605
|
+
| `$1800` | 7 | ATN IN | `1` in every state of this run. Measured later with a drive program of its own (`pitfalls/loader.md#atn_assert_drives_data_low_via_atna`): `0` with ATN released, `1` with ATN asserted, PAL and NTSC, so `1` here is the asserted level, and the `M-R` reads behind this table were taken while the host still held ATN for the command (an inference from the DOS command flow, not traced). An earlier version of this row said ATN was released in all of them |
|
|
606
|
+
| `$1800` | all | port B | rest `85` PAL, `81` NTSC; job `81`; host `85`. Bits 0 and 2 are one instant of the bus handshake and their level-to-bit polarity is not established by this run |
|
|
607
|
+
| `$1801` | all | port A, unused | `00` |
|
|
608
|
+
| `$1802` | all | DDRB | `1A`: bits 1, 3, 4 outputs, the rest inputs |
|
|
609
|
+
| `$1803` | all | DDRA | `FF` |
|
|
610
|
+
| `$1804`-`$1805` | all | timer 1 counter | PAL `A1 00`, NTSC `54 00`: free-running, differs run to run when the program changes |
|
|
611
|
+
| `$1806`-`$1807` | all | timer 1 latch | `FF 01` |
|
|
612
|
+
| `$1808`-`$1809` | all | timer 2 counter | PAL `4C AA`, NTSC `F7 B3` |
|
|
613
|
+
| `$180A` | all | shift register | `00` |
|
|
614
|
+
| `$180B` | all | ACR | `00` |
|
|
615
|
+
| `$180C` | all | PCR | `01`: CA1 (ATN) interrupts on a positive edge |
|
|
616
|
+
| `$180D` | all | IFR | `00` |
|
|
617
|
+
| `$180E` | all | IER | `82`: CA1 enabled, so ATN raises the drive's IRQ |
|
|
618
|
+
| `$180F` | all | port A without handshake | `00` |
|
|
619
|
+
|
|
620
|
+
**VIA2, `$1C00`, disk controller.**
|
|
621
|
+
|
|
622
|
+
| Address | Bit | Name | Read here |
|
|
623
|
+
|---|---|---|---|
|
|
624
|
+
| `$1C00` | 0, 1 | stepper motor phase | rest `00`; job `00` for the first track 18 request, `10` for the second, `00` for 1, 25, 31; init `10`. The trace shows `$FA75` writing the low two bits down through `3 2 1 0 3 2 ...` at one write per half-step, 122 writes over the run's five moves (96 for the four seeks that stepped, 26 for the initialise), one write every 14.8 thousand drive cycles |
|
|
625
|
+
| `$1C00` | 2 | motor on | rest `0`; job `1` (all five); host `1`; idle `0`; init `1` after the open, `1` after the close. The trace's motor-on writes came from `$F987`, motor-off from `$F9ED` |
|
|
626
|
+
| `$1C00` | 3 | drive LED | `0` in every `M-R` and every job snapshot. On in the trace only: `$EC98` in the idle loop wrote `DE` twice, 1,108 cycles apart, during the initialise; no other write in the run had bit 3 set. A named-file open, which the ROM's `$C100` path serves: not measured here |
|
|
627
|
+
| `$1C00` | 4 | write-protect sense | `1` with the image attached normally, `0` with `-attach8ro` (rest `F0` against `E0`). Zero-page `$1E` (LWPT) followed it: `10` against `00` |
|
|
628
|
+
| `$1C00` | 5, 6 | density (bit-clock select) | rest `11`; job `10` for track 18, `11` for 1, `01` for 25, `00` for 31; this is the zone index of the Timing section above, now rung 1. Written by `$F35C` in the trace, from the requested track: the first job wrote `D4` for track 18 while the head was still on track 19 (see `$22` below) |
|
|
629
|
+
| `$1C00` | 7 | SYNC detected, inverted | `1` in every read but one: the NTSC host read after the track 1 seek was `74`, a sync mark under the head at that instant |
|
|
630
|
+
| `$1C00` | all | port B | rest `F0` (read-write image) or `E0` (read-only); job `D4 D6 F4 B4 94`; host the same except the NTSC `74`; idle `90`; init `D6` before and after the close; trace also `F7` from `$EB2A` at reset and `60` from `$F260` |
|
|
631
|
+
| `$1C01` | all | port A, the byte from the head | rest `54`. Read by uploaded code with byte-ready after each seek: `A5 4A 94 29`, `52 A5 4A 94`, `52 94 29 52`; GCR of long runs, as in a gap. Byte-ready only arrives with CA2 of `$1C0C` high (SOE); at rest it is low and the read loop times out |
|
|
632
|
+
| `$1C02` | all | DDRB | `6F`: bits 4 and 7 inputs, the rest outputs |
|
|
633
|
+
| `$1C03` | all | DDRA | `00`: read mode |
|
|
634
|
+
| `$1C04`-`$1C05` | all | timer 1 counter | PAL `85 18`, NTSC `D8 20`: free-running |
|
|
635
|
+
| `$1C06`-`$1C07` | all | timer 1 latch | `00 3A`: the controller's interrupt interval is `$3A00` = 14,848 drive cycles, which matches the stepper write spacing above |
|
|
636
|
+
| `$1C08`-`$1C09` | all | timer 2 counter | PAL `66 9B`, NTSC `70 A8` |
|
|
637
|
+
| `$1C0A` | all | shift register | `00` |
|
|
638
|
+
| `$1C0B` | all | ACR | `41`: timer 1 free-running, port A input latching on |
|
|
639
|
+
| `$1C0C` | all | PCR | `EC`: CA1 negative edge, CA2 output low (SOE off), CB1 negative edge, CB2 output high (read mode). The probe writes `EE` while it reads the head and puts `EC` back |
|
|
640
|
+
| `$1C0D` | all | IFR | `00` at rest |
|
|
641
|
+
| `$1C0E` | all | IER | `C0`: timer 1 enabled; that interrupt is the disk controller |
|
|
642
|
+
| `$1C0F` | all | port A without handshake | `54`, the same byte as `$1C01` |
|
|
643
|
+
|
|
644
|
+
Two things the seeks showed about the controller rather than the VIA. The zero-page track byte `$22` is `00` at rest, and the first job after power-up does not step: the request for track 18 found a header on track 19 (the header image at `$16`-`$1A` read `30 31 13 02 10`, ID `01`, track 19) and left `$22` at `13`; only the second request for track 18 moved the head, two half-steps. And a job that fails to step still sets the density bits for the track it was asked for. Rung 1 for VICE's 1541; whether the initial head position of 19 is the emulator's or a real drive's power-on position is not established here.
|
|
645
|
+
|
|
646
|
+
Related pitfall: `../pitfalls/loader.md`, `gcr_timing_assumes_stock_drive`, for what happens when uploaded code assumes these bit clocks on a drive that is not a stock 1541.
|
|
647
|
+
|
|
648
|
+
## 1541 memory map
|
|
649
|
+
|
|
650
|
+
The 6502 in the drive sees 2 KiB of RAM, two VIAs and 16 KiB of ROM. Names and meanings are the g3sl.github.io ROM listing's (rung 4); "run" marks a location read or exercised here or by `../recipes/kickassembler/drive-job-queue.md` (rung 1); the ROM start is rung 1 from the image.
|
|
651
|
+
|
|
652
|
+
| Address | Name | What the DOS keeps there | Status |
|
|
653
|
+
|---|---|---|---|
|
|
654
|
+
| `$00`-`$05` | JOBS | job code per buffer, result code when done | run |
|
|
655
|
+
| `$06`-`$11` | HDRS | track and sector per buffer | run |
|
|
656
|
+
| `$12`-`$13` | DSKID | master disk ID | run |
|
|
657
|
+
| `$16`-`$1A` | HEADER | last header read: ID, ID, track, sector, checksum | run: `30 31 12 03 10` after the seek to 18 (PAL) |
|
|
658
|
+
| `$1C` | WPSW | write-protect switch changed | run: `01` at rest |
|
|
659
|
+
| `$1E` | LWPT | last write-protect state | run: `10` read-write, `00` read-only |
|
|
660
|
+
| `$20` | DRVST | drive status | run: `30` after a job (an earlier build of the probe printed it) |
|
|
661
|
+
| `$22` | DRVTRK | track under the head | run: `00` at rest, then the track of the last job |
|
|
662
|
+
| `$30`-`$31` | BUFPNT | pointer to the active buffer | listing |
|
|
663
|
+
| `$3E` | CDRIVE | active drive, `$FF` when idle | listing |
|
|
664
|
+
| `$3F`, `$41` | JOBN, NXTJOB | last and next job slot | listing |
|
|
665
|
+
| `$44` | WORK | scratch; the zone index during the density write | listing, ROM |
|
|
666
|
+
| `$48` | ACLTIM | head acceleration timer | listing |
|
|
667
|
+
| `$4A` | STEPS | half-steps left to move | listing |
|
|
668
|
+
| `$62`-`$63` | NXTST | pointer to the stepping routine, `$FA05` when not stepping | listing |
|
|
669
|
+
| `$6F`-`$74` | T0-T4 | temporaries | listing |
|
|
670
|
+
| `$7F` | DRVNUM | drive number, `0` | listing |
|
|
671
|
+
| `$80`-`$81` | TRACK, SECTOR | the track and sector of the current file operation | listing |
|
|
672
|
+
| `$82`-`$84` | LINDX, SA, ORGSA | current channel index and secondary address | listing |
|
|
673
|
+
| `$99`-`$A6` | BUFTAB | pointers into buffers 0 to 4, the command buffer and the error buffer | listing |
|
|
674
|
+
| `$F9` | JOBNUM | current job number | listing |
|
|
675
|
+
| `$0100`-`$01FF` | stack | the 6502 stack; the trace showed SP at `$43`-`$45` | run |
|
|
676
|
+
| `$0200`-`$0229` | CMDBUF | the command as received on channel 15 | listing |
|
|
677
|
+
| `$022A` | CMDNUM | command code | listing |
|
|
678
|
+
| `$022B`-`$023D` | LINTAB | secondary address to channel table | listing |
|
|
679
|
+
| `$023E`-`$0243` | CHNDAT | last data byte per channel | listing |
|
|
680
|
+
| `$0274` | CMDSIZ | command length | listing |
|
|
681
|
+
| `$027A`-`$027F` | FILTBL | filename pointers | listing |
|
|
682
|
+
| `$02B1`-`$02D4` | NAMBUF | directory name buffer | listing |
|
|
683
|
+
| `$02D5`-`$02F8` | ERRBUF | the error channel text | listing |
|
|
684
|
+
| `$02FA`-`$02FD` | NDBL, NDBK | blocks free | listing |
|
|
685
|
+
| `$02FE` | PHASE | stepper phase | listing |
|
|
686
|
+
| `$0300`-`$03FF` | buffer 0 | data buffer lent to a channel | listing |
|
|
687
|
+
| `$0400`-`$04FF` | buffer 1 | data buffer; read into by both recipes | run |
|
|
688
|
+
| `$0500`-`$05FF` | buffer 2 | data buffer | listing |
|
|
689
|
+
| `$0600`-`$06FF` | buffer 3 | data buffer; both recipes upload code here with only channel 15 open | run |
|
|
690
|
+
| `$0700`-`$07FF` | buffer 4 | the BAM once a disk is initialised | listing |
|
|
691
|
+
| `$0800`-`$17FF` | — | no RAM in a stock 1541; what a read returns here: not measured | — |
|
|
692
|
+
| `$1800`-`$180F` | VIA1 | serial bus; table above | run |
|
|
693
|
+
| `$1C00`-`$1C0F` | VIA2 | disk controller; table above | run |
|
|
694
|
+
| `$C000`-`$FFFF` | ROM | CBM DOS 2.6; entry points in the section "1541 Drive ROM" | run |
|
|
695
|
+
|
|
696
|
+
Which buffers uploaded code may take: with only channel 15 open, buffers 1 and 3 were free in both recipes and nothing overwrote them between commands (rung 1 for that situation). A file channel takes a buffer from this pool, and the BAM takes buffer 4 once a disk is initialised (listing, rung 4); a loader that opens files while its code is resident must check the channel-to-buffer table before choosing, and that check is not measured here. The mirror addresses between `$0800` and `$17FF` and the register repeats within `$1800`-`$1BFF` and `$1C00`-`$1FFF` are not measured here.
|
|
697
|
+
|
|
698
|
+
---
|
|
699
|
+
|
|
700
|
+
## SD2IEC and Ultimate II+
|
|
701
|
+
|
|
702
|
+
These modern IEC-compatible peripherals are flagged here for completeness but are **out of scope** for this KB's primary hardware target (stock C64 PAL/NTSC).
|
|
703
|
+
|
|
704
|
+
**SD2IEC** is a microcontroller-based IEC device that reads/writes SD cards. It emulates the 1541 command set well enough for most purposes but has no drive CPU — it cannot execute drive-side code, making all `M-W`/`M-E` based fastloaders non-functional. SD2IEC supports a subset of fastloaders via native acceleration modes (Krill's Loader, for example, has an SD2IEC-compatible codepath).
|
|
705
|
+
|
|
706
|
+
**Ultimate II+** (Gideon's Logic) is an FPGA cartridge that includes an accurate 1541 emulation with real drive CPU, plus fast IEC (via FBI fast loader built into the cartridge firmware). It is the gold standard for hardware-accurate fast loading on modern C64 setups but represents cartridge-extended hardware outside the stock scope.
|
|
707
|
+
|
|
708
|
+
Both devices handle `.D64`, `.D71`, `.D81`, `.T64`, and `.PRG` files from SD cards, making them the most common way demosceners develop on real hardware today.
|
|
709
|
+
|
|
710
|
+
---
|
|
711
|
+
|
|
712
|
+
## Pitfalls
|
|
713
|
+
|
|
714
|
+
### Standard IEC Load Speed (~300 bytes/sec)
|
|
715
|
+
|
|
716
|
+
The headline limitation of the IEC bus is its throughput. A full 35-track 1541 disk (664 KB usable) takes over 30 minutes to read entirely via the KERNAL LOAD. A typical 50 KB program takes about 2.5 minutes. This is universally considered unacceptable for released software; virtually every released demo and game uses a custom fastloader. Plan for fastloader integration from the start of any project targeting real hardware.
|
|
717
|
+
|
|
718
|
+
### VICE Timing Differences with Real Hardware
|
|
719
|
+
|
|
720
|
+
VICE's 1541 emulation is accurate for correctness but the default configuration does not emulate the real-time IEC bus timing precisely. Programs that rely on cycle-counted timing in IEC routines (including some fastloaders) may work correctly in VICE but fail on real hardware, or vice versa. Use VICE's `-drive8truedrive` option (and `-drivesound` if you want to hear the head) to enable the more accurate (but slower) true-drive emulation during testing; an earlier version of this sentence spelled them `--drivesound 1 --drive8truedrive 1`, which x64sc 3.10 refuses — its options take a single dash and no argument.
|
|
721
|
+
|
|
722
|
+
The `c64_pal_ntsc_diff` MCP tool and `../hardware/pal-ntsc-reference.md` detail the clock-rate differences that affect CIA-timer-based IEC routines.
|
|
723
|
+
|
|
724
|
+
### CIA2 Contention: IEC Bus vs RS-232
|
|
725
|
+
|
|
726
|
+
CIA2 Port B (`$DD01`) is the user port's eight data lines (RS-232 or a parallel cable); Port A (`$DD00`) holds the IEC outputs in bits 3–5, the RS-232 TXD line in bit 2 and the VIC bank in bits 0–1. A parallel fast loader drives Port B freely — nothing there touches the bus — but every write to Port A must be a masked read-modify-write, and the bug to look for is a loader, or an interrupt handler switching VIC banks, that stores a whole byte to `$DD00` and thereby releases or asserts ATN, CLK or DATA. An earlier version of this paragraph had the IEC outputs at bits 2–4 and the fault on Port B; both were wrong.
|
|
727
|
+
|
|
728
|
+
Similarly, code that uses CIA2 for RS-232 (via the user port ACIA emulation) must ensure IEC I/O is not simultaneously active. The KERNAL does not serialize these; user code must guard access.
|
|
729
|
+
|
|
730
|
+
### Drive-Not-Ready and Timeout Errors
|
|
731
|
+
|
|
732
|
+
The KERNAL has one timeout on the bus, and it is short and narrow. The byte-send routine arms CIA1 timer B for about 1,024 cycles (it writes 4 to the timer's high byte at `$ED92`) while it waits for a listener to acknowledge; if nothing answers it sets the READST bit and returns. An absent or unpowered drive is caught before that, and not by the timer: `LISTEN` and `TALK` look at DATA about 1,100 cycles after asserting ATN, and if no device is holding it low they take the device-not-present path at `$EDAD` without sending a byte (measured in the no-drive run under "IEC bit timing, measured" above; an earlier version of this paragraph credited the timer with that detection). The talk turnaround and the byte-receive waits have no timeout at all: a drive that has accepted TALK and never pulls CLK leaves the CPU waiting for ever, which is the start-up hang measured in VICE and described on `recipes/oscar64/high-score-persist.md`. An earlier version of this page said "approximately 64 ms" and implied every stall returns; neither is so. Check READST after every OPEN/CHKIN/CHKOUT for the errors the KERNAL can report, and do not rely on it to return from a stalled transfer.
|
|
733
|
+
|
|
734
|
+
### Directory Track Corruption
|
|
735
|
+
|
|
736
|
+
Track 18 is the most-written track on a 1541. The directory and BAM are updated on every file write or scratch. Repeated use without a VALIDATE (`V0` command channel) command can produce a corrupted BAM — sectors marked allocated that are actually free, or vice versa. VALIDATE rebuilds the BAM by walking every file chain and reconstructing the bitmap. Running VALIDATE on a disk with active writes will abort any open file writes. The c1541 utility (bundled with VICE) can perform offline BAM repair.
|
|
737
|
+
|
|
738
|
+
---
|
|
739
|
+
|
|
740
|
+
## See Also
|
|
741
|
+
|
|
742
|
+
- `../runtime/vice-reference.md` — VICE emulator configuration for IEC/drive emulation
|
|
743
|
+
- `c64-file-formats.md` — Disk image formats (.D64, .D71, .D81, .G64)
|
|
744
|
+
- `../hardware/kernal-routines-reference.md` — KERNAL jump table: SETLFS, SETNAM, OPEN, CLOSE, CHKIN, CHKOUT, LOAD, SAVE, LISTEN, TALK, IECIN, IECOUT, READST, UNLSN, UNTLK
|
|
745
|
+
|
|
746
|
+
---
|
|
747
|
+
|
|
748
|
+
## Sources
|
|
749
|
+
|
|
750
|
+
- KERNAL ROM 901227-03 and the drive ROM images (1540 325302+3-01, 1541 325302-01+901229-05, 1541-II 251968-03, 1570 315090-01, 1571 310654-05, 1571CR 318047-01, 1581 318045-02) as shipped with VICE 3.10 in `/opt/homebrew/opt/vice/share/vice/C64/` and `…/DRIVES/` — every byte, address and instruction quoted above was read from these (rung 1).
|
|
751
|
+
- VICE x64sc 3.10, `-drive8truedrive`, drive types 1541, 1541-II, 1571 and 1581, and `c1541` — the `$DD00` readings, the reset-state reading, the `M-R` runs and the BAM byte (rung 1).
|
|
752
|
+
- *C1541 ROM disassembly with comments*, `https://g3sl.github.io/c1541rom.html` — the routine names SETLDS, MEMRD, INITDR, ATNSRV, TALK, ACPTR, IDLE, READ, WRIGHT, CHKBLK, BINGCR, VERNUM, MAXTRK and OPNIWR; the page credits its labels and comments to *Inside Commodore DOS* by Richard Immers and Gerald G. Neufeld, 1984 (rung 4).
|
|
753
|
+
- Ruud Baltissen, *1541: Transferring data*, `http://baltissen.org/newhtm/1541a.htm`, read directly — the 16 MHz clock, the 74177 ÷16 stage for the 6502, the 74LS193 counter preloaded from the VIA's PB5/PB6 density bits, and the divisors 13–16 by track zone (rung 4). An earlier draft of this list also cited the Commodore 1540/1541 Service Manual for those figures; it was not consulted, and the figures came from this page.
|
|
754
|
+
- The KERNAL labels CLKHI, CLKLO, DATAHI, DATALO, DEBPIA, SCATN, ISOUR and IOINIT are the names used in the published KERNAL source listings (rung 4).
|