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,592 @@
|
|
|
1
|
+
---
|
|
2
|
+
tool: tape-mastering
|
|
3
|
+
tool_kind: reference-catalog
|
|
4
|
+
maintainer: bdgscotland
|
|
5
|
+
license: BSD-3-Clause
|
|
6
|
+
home_url: https://github.com/bdgscotland/c64-kb
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<!-- doc-type: toolchain-reference -->
|
|
10
|
+
|
|
11
|
+
# Mastering a tape image and proving it loads
|
|
12
|
+
|
|
13
|
+
## Tool
|
|
14
|
+
|
|
15
|
+
This page is a procedure, not a program. It takes a PRG that one of the
|
|
16
|
+
toolchains built and turns it into a TAP image a user can attach as a
|
|
17
|
+
cassette and start with `LOAD` and `RUN`, then proves the image loads by
|
|
18
|
+
running it in headless VICE and looking at the screen. Two routes are
|
|
19
|
+
shown: the KERNAL's own format, which any C64 reads with no software of
|
|
20
|
+
its own, and a turbo block placed behind a small KERNAL-format loader,
|
|
21
|
+
which is how a commercial tape got its speed. Every command and every
|
|
22
|
+
figure here was run on this machine with the windowless x64sc build of
|
|
23
|
+
VICE 3.10 (`-warp`, PAL unless the line says NTSC) on two payloads: a
|
|
24
|
+
202-byte KickAssembler test program that sets the border green and
|
|
25
|
+
prints one line, and the 1,183-byte loader from
|
|
26
|
+
[kickassembler/tape-turbo-loader](../recipes/kickassembler/tape-turbo-loader.md).
|
|
27
|
+
The pulse lengths and the block layout come from the "KERNAL bit
|
|
28
|
+
encoding" subsection of [c64-file-formats](../formats/c64-file-formats.md),
|
|
29
|
+
which measured them on a TAP the KERNAL itself recorded; the writer here
|
|
30
|
+
was checked against that recording with the same decoder. No third-party
|
|
31
|
+
mastering tool was used: a search for a licence-clear `prg2tap` source
|
|
32
|
+
could not be run inside this session's budget, none is installed on this
|
|
33
|
+
machine, and the writers below are short enough to own.
|
|
34
|
+
|
|
35
|
+
**Targets:** 6510
|
|
36
|
+
|
|
37
|
+
## What a LOAD from tape needs
|
|
38
|
+
|
|
39
|
+
The KERNAL's tape reader wants, in this order, and it wants each of the
|
|
40
|
+
two blocks twice:
|
|
41
|
+
|
|
42
|
+
1. **A leader of short pulses.** The reader locks its timing to them. The
|
|
43
|
+
KERNAL writes about ten seconds of leader before the first block on a
|
|
44
|
+
tape and about two seconds before every later block; the writer below
|
|
45
|
+
uses the same counts, 27,136 and 5,376 shorts. Shorter leaders were
|
|
46
|
+
not tried here.
|
|
47
|
+
2. **A header block of 192 bytes.** Byte 0 is the file type: `$01` for a
|
|
48
|
+
relocatable program, which a plain `LOAD` puts at the start of BASIC
|
|
49
|
+
and `LOAD"",1,1` puts at the header's address, and `$03` for one that
|
|
50
|
+
always goes to the header's address. Only the plain `LOAD` of a type
|
|
51
|
+
1 file was run here; the other two paths are not measured here. A
|
|
52
|
+
PRG with a BASIC stub at `$0801` wants type 1. Bytes 1 and 2 are
|
|
53
|
+
the start address and bytes 3 and 4 the end address, low byte first,
|
|
54
|
+
the end exclusive. Bytes 5 to 20 are the file name in PETSCII, padded
|
|
55
|
+
with `$20` to sixteen characters; the rest of the block is `$20`
|
|
56
|
+
here. The name is what `FOUND` prints and what `LOAD"NAME"` matches;
|
|
57
|
+
a plain `LOAD` takes the first header it finds.
|
|
58
|
+
3. **A data block** of end minus start bytes, the PRG with its two
|
|
59
|
+
load-address bytes stripped.
|
|
60
|
+
4. **Two copies of each block.** Each copy opens with a nine-byte
|
|
61
|
+
countdown, `$89` down to `$81` for the first and `$09` down to `$01`
|
|
62
|
+
for the second, and closes with a one-byte XOR checksum of its data
|
|
63
|
+
and the end-of-block marker. The two copies are separated by a short
|
|
64
|
+
gap of leader pulses.
|
|
65
|
+
|
|
66
|
+
Every byte is twenty pulses: a byte marker, eight data bits least
|
|
67
|
+
significant first, and an odd parity bit, each bit a pair of pulses.
|
|
68
|
+
Those rules are the formats page's measurement and are restated in the
|
|
69
|
+
pitfall
|
|
70
|
+
[tape_bit_is_a_pulse_pair_not_a_pulse](../pitfalls/loader.md); this page
|
|
71
|
+
does not repeat the figures, it uses them.
|
|
72
|
+
|
|
73
|
+
## The KERNAL-format writer
|
|
74
|
+
|
|
75
|
+
Save this beside the PRG as `kernal_tap.py`. It is not a listing the
|
|
76
|
+
gate builds; it is the mastering step.
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
#!/usr/bin/env python3
|
|
80
|
+
"""Write a TAP v1 file that a plain LOAD from tape reads: a PRG in the
|
|
81
|
+
KERNAL's own pulse-pair format, header block and data block, each
|
|
82
|
+
written twice with the countdowns.
|
|
83
|
+
|
|
84
|
+
usage: kernal_tap.py in.prg out.tap [NAME] [--type 3] [--leader N] [--append other.tap]
|
|
85
|
+
|
|
86
|
+
--append copies the pulses of another TAP after the data block, so a
|
|
87
|
+
turbo block can follow a loader written in the KERNAL format.
|
|
88
|
+
"""
|
|
89
|
+
import struct
|
|
90
|
+
import sys
|
|
91
|
+
|
|
92
|
+
SHORT, MEDIUM, LONG = 0x2F, 0x43, 0x58 # TAP units of 8 cycles: 376, 536, 704 cycles
|
|
93
|
+
LEADER_HEADER = 27136 # short pulses before the header block, about 10 s
|
|
94
|
+
LEADER_DATA = 5376 # short pulses before the data block, about 2 s
|
|
95
|
+
GAP = 79 # short pulses between the two copies of a block
|
|
96
|
+
TRAILER = 78 # short pulses after the second copy
|
|
97
|
+
SILENCE = 327680 # cycles of silence between header and data block
|
|
98
|
+
NAME_LEN = 16
|
|
99
|
+
HEADER_LEN = 192
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def bit_pair(bit):
|
|
103
|
+
return (MEDIUM, SHORT) if bit else (SHORT, MEDIUM)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def byte_pulses(value):
|
|
107
|
+
out = [LONG, MEDIUM] # byte marker
|
|
108
|
+
ones = 0
|
|
109
|
+
for k in range(8):
|
|
110
|
+
bit = (value >> k) & 1 # least significant bit first
|
|
111
|
+
ones += bit
|
|
112
|
+
out += bit_pair(bit)
|
|
113
|
+
out += bit_pair(1 - (ones & 1)) # odd parity over nine bits
|
|
114
|
+
return out
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def block_pulses(data, leader):
|
|
118
|
+
"""One block: leader, then two copies, each countdown + data + checksum
|
|
119
|
+
+ end-of-block marker, separated by a short gap."""
|
|
120
|
+
check = 0
|
|
121
|
+
for b in data:
|
|
122
|
+
check ^= b
|
|
123
|
+
out = [SHORT] * leader
|
|
124
|
+
for first_copy in (True, False):
|
|
125
|
+
base = 0x80 if first_copy else 0x00
|
|
126
|
+
for n in range(9, 0, -1):
|
|
127
|
+
out += byte_pulses(base + n)
|
|
128
|
+
for b in data:
|
|
129
|
+
out += byte_pulses(b)
|
|
130
|
+
out += byte_pulses(check)
|
|
131
|
+
out += [LONG, SHORT] # end-of-block marker
|
|
132
|
+
out += [SHORT] * (GAP if first_copy else TRAILER)
|
|
133
|
+
return out
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def silence(cycles):
|
|
137
|
+
return [0, cycles & 0xFF, (cycles >> 8) & 0xFF, (cycles >> 16) & 0xFF]
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def make_tap(prg, name, file_type=1, leader=LEADER_HEADER):
|
|
141
|
+
start = prg[0] | (prg[1] << 8)
|
|
142
|
+
body = prg[2:]
|
|
143
|
+
end = start + len(body) # exclusive
|
|
144
|
+
padded = name.upper().encode("ascii")[:NAME_LEN].ljust(NAME_LEN, b" ")
|
|
145
|
+
header = bytes([file_type, start & 0xFF, start >> 8, end & 0xFF, end >> 8])
|
|
146
|
+
header += padded
|
|
147
|
+
header += b" " * (HEADER_LEN - len(header))
|
|
148
|
+
first = block_pulses(header, leader)
|
|
149
|
+
second = block_pulses(body, LEADER_DATA)
|
|
150
|
+
cycles = sum(p * 8 for p in first + second) + SILENCE
|
|
151
|
+
return first + silence(SILENCE) + second, cycles
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def tap_bytes(pulses):
|
|
155
|
+
body = bytes(pulses)
|
|
156
|
+
return b"C64-TAPE-RAW" + bytes([1, 0, 0, 0]) + struct.pack("<I", len(body)) + body
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def main(argv):
|
|
160
|
+
src, dst = argv[1], argv[2]
|
|
161
|
+
name = argv[3] if len(argv) > 3 and not argv[3].startswith("--") else "PROGRAM"
|
|
162
|
+
file_type = 1
|
|
163
|
+
leader = LEADER_HEADER
|
|
164
|
+
if "--type" in argv:
|
|
165
|
+
file_type = int(argv[argv.index("--type") + 1])
|
|
166
|
+
if "--leader" in argv:
|
|
167
|
+
leader = int(argv[argv.index("--leader") + 1])
|
|
168
|
+
prg = open(src, "rb").read()
|
|
169
|
+
pulses, cycles = make_tap(prg, name, file_type, leader)
|
|
170
|
+
if "--append" in argv:
|
|
171
|
+
tail = open(argv[argv.index("--append") + 1], "rb").read()
|
|
172
|
+
assert tail[:12] == b"C64-TAPE-RAW" and tail[12] in (0, 1)
|
|
173
|
+
pulses += list(tail[20:20 + struct.unpack("<I", tail[16:20])[0]])
|
|
174
|
+
out = tap_bytes(pulses)
|
|
175
|
+
open(dst, "wb").write(out)
|
|
176
|
+
print(f"wrote {dst}: {len(prg) - 2} payload bytes, {len(out)} TAP bytes, "
|
|
177
|
+
f"{cycles} cycles of tape ({cycles / 985248:.2f} s PAL)")
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
if __name__ == "__main__":
|
|
181
|
+
main(sys.argv)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Three choices in it are worth knowing. The pulse values `$2F`, `$43`
|
|
185
|
+
and `$58` are the most common bytes in the KERNAL's own recording, so a
|
|
186
|
+
reader that copes with the KERNAL's tapes copes with these. The
|
|
187
|
+
0.33 s silence between the two blocks is a 24-bit TAP v1 entry, the
|
|
188
|
+
form VICE itself recorded at that point. The file name goes in as
|
|
189
|
+
upper-case ASCII, which is unshifted PETSCII and shows as capitals in
|
|
190
|
+
the C64's default font; the same shift trap that
|
|
191
|
+
[release-disk](release-disk.md) records for `c1541` applies to any name
|
|
192
|
+
typed in lower case in a shell, and this script upper-cases for you.
|
|
193
|
+
|
|
194
|
+
On the test program:
|
|
195
|
+
|
|
196
|
+
```text
|
|
197
|
+
python3 kernal_tap.py hello.prg hello.tap HELLO
|
|
198
|
+
wrote hello.tap: 202 payload bytes, 49418 TAP bytes, 20497520 cycles of tape (20.80 s PAL)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
A 202-byte program is 49,418 bytes of TAP. The ratio is fixed by the
|
|
202
|
+
format: every payload byte is twenty pulses in each of two copies, and
|
|
203
|
+
the leaders are constant. For a 1,183-byte payload the same command
|
|
204
|
+
printed `88658 TAP bytes` and `39.62 s`.
|
|
205
|
+
|
|
206
|
+
## Proving it loads in VICE
|
|
207
|
+
|
|
208
|
+
The run does not autostart the PRG; that would prove the program and
|
|
209
|
+
say nothing about the tape. It attaches the TAP, presses PLAY from the
|
|
210
|
+
monitor command file, and types `LOAD` and `RUN` into the keyboard
|
|
211
|
+
buffer:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
printf 'tapectrl 1\n' > play.mon
|
|
215
|
+
x64sc -default -warp +sound +autostart-delay-random \
|
|
216
|
+
-limitcycles 40000000 +dsresetwithcpu \
|
|
217
|
+
-dstapewobbleamp 0 -dstapewobblefreq 0 -dsspeedtuning 0 -dstapeerror 0 \
|
|
218
|
+
-1 hello.tap -moncommands play.mon -keybuf 'load\nrun\n' \
|
|
219
|
+
-exitscreenshot hello-pal.png
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Add `-model ntsc` for the NTSC run. `-1` attaches the TAP, `tapectrl 1`
|
|
223
|
+
presses PLAY at power-on, and `+dsresetwithcpu` keeps that press through
|
|
224
|
+
the reset; without it the KERNAL prints `PRESS PLAY ON TAPE` and waits.
|
|
225
|
+
The four `-ds` options switch off VICE's tape speed error and wobble so
|
|
226
|
+
that two runs give identical bytes (see Limits). Because PLAY is
|
|
227
|
+
already down, the KERNAL skips its prompt, prints `SEARCHING`, blanks
|
|
228
|
+
the screen while the tape runs, shows `FOUND HELLO` with the screen
|
|
229
|
+
back on, pauses, blanks again for `LOADING`, and returns to BASIC,
|
|
230
|
+
where the buffered `RUN` starts the program. The exit screenshot is the
|
|
231
|
+
program's own output on a green border, `TAPE MASTER OK JIFFIES 770`,
|
|
232
|
+
followed by `READY.`; the pictures are
|
|
233
|
+
`../figures/tape-kernal-master-pal.png` and
|
|
234
|
+
`../figures/tape-kernal-master-ntsc.png`, each byte-identical on two
|
|
235
|
+
runs, every character cell decoded against the character ROM.
|
|
236
|
+
|
|
237
|
+
**Timing was measured by bisecting `-limitcycles`**, not from the
|
|
238
|
+
jiffy clock. The program stores `$A0`-`$A2` as its first act, and both
|
|
239
|
+
models printed `770` for a load that took over thirty seconds of
|
|
240
|
+
machine time: the KERNAL's tape interrupt does not advance the jiffy
|
|
241
|
+
clock, so `TI` measures only the boot and the pause at `FOUND`. Each
|
|
242
|
+
row below is the smallest cycle count, to within 50,000, at which the
|
|
243
|
+
exit screenshot showed the stage, found by nine or ten runs per row:
|
|
244
|
+
|
|
245
|
+
| Stage (PAL, 202-byte payload) | Cycles from power-on | Frames (19,656 cycles) |
|
|
246
|
+
|------|------|------|
|
|
247
|
+
| `LOAD` typed, screen blanks, tape starts | 2,160,000 to 2,200,000 | 110 |
|
|
248
|
+
| `FOUND HELLO` on screen (both header copies read) | 16,150,000 to 16,200,000 | 822 |
|
|
249
|
+
| Screen blanks again (`FOUND` pause over) | 28,650,000 to 28,690,000 | 1,458 |
|
|
250
|
+
| Program running, border green | 35,000,000 to 35,040,000 | 1,782 |
|
|
251
|
+
|
|
252
|
+
From `LOAD` to the program's first instruction is 32.8 million cycles,
|
|
253
|
+
1,671 PAL frames, 33.3 s. The tape itself holds 20.5 million cycles of
|
|
254
|
+
pulses; the other 12.5 million, 12.7 s, is the KERNAL's pause at
|
|
255
|
+
`FOUND`, during which the motor is stopped. The rule that ends that
|
|
256
|
+
pause was not measured here; a real user shortens it with a key. On
|
|
257
|
+
NTSC the same image reached the green border between 35,470,000 and
|
|
258
|
+
35,510,000 cycles, 2,076 NTSC frames of 17,095 cycles, 34.7 s: the TAP
|
|
259
|
+
stores cycles, so the tape costs the same count on either model and the
|
|
260
|
+
faster clock finishes sooner in wall time.
|
|
261
|
+
|
|
262
|
+
**Cost per byte.** A second image with the payload padded to 500 bytes
|
|
263
|
+
reached the green border between 40,630,000 and 40,670,000 cycles PAL.
|
|
264
|
+
The 298 extra bytes cost 5.63 million cycles, 18,890 per byte, which is
|
|
265
|
+
two copies of the 9,448 cycles a byte's twenty pulses sum to at the
|
|
266
|
+
writer's pulse values (704 + 536 for the marker, 912 for each of nine
|
|
267
|
+
bit pairs). A KERNAL-format program therefore loads at about 52 bytes
|
|
268
|
+
per second once the leaders and the pause are paid.
|
|
269
|
+
|
|
270
|
+
## The turbo route: a KERNAL-format stub in front of a turbo block
|
|
271
|
+
|
|
272
|
+
A turbo tape is a KERNAL-format program that is itself a loader,
|
|
273
|
+
followed on the same tape by a block in the loader's own encoding. The
|
|
274
|
+
user types `LOAD` and `RUN` as before; the KERNAL loads the stub, the
|
|
275
|
+
stub turns the motor back on and reads the fast block. The loader from
|
|
276
|
+
[kickassembler/tape-turbo-loader](../recipes/kickassembler/tape-turbo-loader.md)
|
|
277
|
+
serves unchanged: it waits for PLAY, drives the motor through bit 5 of
|
|
278
|
+
`$01`, and reads a one-pulse-per-bit block whose TAP its own
|
|
279
|
+
`make_tap.py` writes. Master the pair with `--append`:
|
|
280
|
+
|
|
281
|
+
```text
|
|
282
|
+
java -jar $KICKASS_JAR tape-turbo-loader.asm -o turbo.prg
|
|
283
|
+
python3 make_tap.py turbo.tap
|
|
284
|
+
python3 kernal_tap.py turbo.prg turbo-master.tap TURBO --append turbo.tap
|
|
285
|
+
wrote turbo-master.tap: 1183 payload bytes, 107690 TAP bytes, 39034496 cycles of tape (39.62 s PAL)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
The cycle figure is the KERNAL part only; the appended 19,032 turbo
|
|
289
|
+
pulses add 7.68 million. Run it with the command above, `-1
|
|
290
|
+
turbo-master.tap` and `-limitcycles 80000000`. The exit screenshot is
|
|
291
|
+
the recipe's own report on a green border:
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
TAPE TURBO LOADER
|
|
295
|
+
LEADIN 11995 LEN 500 CHK 40/40
|
|
296
|
+
CYCLES 1540487 B/S 321 PAL
|
|
297
|
+
BIT0 248-264 BIT1 506-520
|
|
298
|
+
OK
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
The pictures are `../figures/tape-turbo-master-pal.png` and
|
|
302
|
+
`../figures/tape-turbo-master-ntsc.png` (NTSC reads `CYCLES 1540493
|
|
303
|
+
B/S 334 NTSC`), byte-identical on two runs each. `LEADIN 11995` is the
|
|
304
|
+
detail that matters: the KERNAL stops the motor within a handful of
|
|
305
|
+
pulses of the end of its second copy, and the stub restarts it, so the
|
|
306
|
+
loader saw 11,995 of the 12,000 lead-in pulses the recipe writes for a
|
|
307
|
+
tape that runs from power-on. Behind a stub the lead-in can be short.
|
|
308
|
+
With `LEADIN = 1000` in `make_tap.py` the same run printed `LEADIN
|
|
309
|
+
995`, the same cycle count and `OK`; the recipe's `LEADIN_MIN` of 64 is
|
|
310
|
+
the floor, and 1,000 leaves a margin of 0.5 s of tape.
|
|
311
|
+
|
|
312
|
+
**Whole-image time.** The green border appeared between 61,480,000 and
|
|
313
|
+
61,520,000 cycles PAL (3,129 frames, 62.4 s from power-on) and between
|
|
314
|
+
61,990,000 and 62,030,000 on NTSC (3,627 NTSC frames, 60.6 s). That is
|
|
315
|
+
the stub's 39.0 million cycles of KERNAL-format tape, the boot, the
|
|
316
|
+
`FOUND` pause, and 7.68 million for the turbo lead-in and block.
|
|
317
|
+
|
|
318
|
+
**Time saved for the same payload size.** The turbo block carries 500
|
|
319
|
+
bytes in 1,540,487 cycles, measured by the loader's own CIA 2 stopwatch,
|
|
320
|
+
1.56 s, about 3,060 cycles per byte. The same 500 bytes as a KERNAL data
|
|
321
|
+
block are 11.72 million cycles by the writer's arithmetic (two copies of
|
|
322
|
+
510 bytes at 9,448, the 5,376-short leader, the gaps and markers), and
|
|
323
|
+
the per-byte cost above confirms the arithmetic to within three cycles a
|
|
324
|
+
byte. The block loads 7.6 times faster and saves 10.2 s per 500 bytes;
|
|
325
|
+
with a 1,000-pulse lead-in in front of it the whole turbo section is
|
|
326
|
+
2.05 million cycles against 11.72 million, 5.7 times faster. Against
|
|
327
|
+
that, the stub costs what any KERNAL-format program costs: this one,
|
|
328
|
+
1,183 bytes of loader with its statistics printing, is 22.4 million
|
|
329
|
+
cycles of tape, and by the same arithmetic the turbo route pays back
|
|
330
|
+
once the payload passes about 1,400 bytes. A production loader is a few
|
|
331
|
+
hundred bytes and pays back sooner; none was written here.
|
|
332
|
+
|
|
333
|
+
### .TAP — Tape image carrying a KERNAL-format program
|
|
334
|
+
|
|
335
|
+
**Produced by:** tape-mastering
|
|
336
|
+
**Consumed by:** vice
|
|
337
|
+
|
|
338
|
+
The container is on [c64-file-formats](../formats/c64-file-formats.md)
|
|
339
|
+
under ".TAP"; this page adds only the block order, the two copies and
|
|
340
|
+
the stub-then-turbo layout.
|
|
341
|
+
|
|
342
|
+
## Checks a mastering step should run
|
|
343
|
+
|
|
344
|
+
Decode the image back before anyone loads it. This reader classifies
|
|
345
|
+
each pulse by the thresholds the formats page found, walks the pulse
|
|
346
|
+
pairs into bytes, checks parity, the countdowns and the XOR, compares
|
|
347
|
+
the data block with the PRG, and reports what follows the last KERNAL
|
|
348
|
+
block, which is where a turbo block sits. Save it as `tapcheck.py`:
|
|
349
|
+
|
|
350
|
+
```text
|
|
351
|
+
#!/usr/bin/env python3
|
|
352
|
+
"""Decode the KERNAL-format blocks in a TAP and check them against a PRG.
|
|
353
|
+
|
|
354
|
+
usage: tapcheck.py file.tap [file.prg]
|
|
355
|
+
|
|
356
|
+
Prints the pulse histogram by class, every block found (countdown copy,
|
|
357
|
+
byte count, checksum, parity failures), the header fields, and whether
|
|
358
|
+
the data block equals the PRG. Pulses after the last KERNAL block are
|
|
359
|
+
reported as a tail, which is where a turbo block sits.
|
|
360
|
+
"""
|
|
361
|
+
import sys
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def read_tap(path):
|
|
365
|
+
d = open(path, "rb").read()
|
|
366
|
+
assert d[:12] == b"C64-TAPE-RAW", "not a TAP"
|
|
367
|
+
version = d[12]
|
|
368
|
+
n = int.from_bytes(d[16:20], "little")
|
|
369
|
+
body = d[20:20 + n]
|
|
370
|
+
pulses = [] # cycles per pulse; a pause is one long entry
|
|
371
|
+
i = 0
|
|
372
|
+
while i < len(body):
|
|
373
|
+
b = body[i]
|
|
374
|
+
if b == 0:
|
|
375
|
+
if version == 0:
|
|
376
|
+
pulses.append(256 * 8)
|
|
377
|
+
i += 1
|
|
378
|
+
else:
|
|
379
|
+
pulses.append(int.from_bytes(body[i + 1:i + 4], "little"))
|
|
380
|
+
i += 4
|
|
381
|
+
else:
|
|
382
|
+
pulses.append(b * 8)
|
|
383
|
+
i += 1
|
|
384
|
+
return version, n, pulses
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def classify(cycles):
|
|
388
|
+
if cycles < 0x3A * 8:
|
|
389
|
+
return "S"
|
|
390
|
+
if cycles < 0x4C * 8:
|
|
391
|
+
return "M"
|
|
392
|
+
if cycles < 0x80 * 8:
|
|
393
|
+
return "L"
|
|
394
|
+
return "Z" # pause or a pulse no KERNAL routine wrote
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def decode_block(sym, pos):
|
|
398
|
+
"""Decode bytes from a byte marker at pos until the end-of-block marker.
|
|
399
|
+
Returns (bytes, parity_failures, end_pos, clean_end)."""
|
|
400
|
+
out, bad = [], 0
|
|
401
|
+
while pos + 1 < len(sym):
|
|
402
|
+
if sym[pos:pos + 2] == "LS":
|
|
403
|
+
return bytes(out), bad, pos + 2, True
|
|
404
|
+
if sym[pos:pos + 2] != "LM":
|
|
405
|
+
return bytes(out), bad, pos, False
|
|
406
|
+
pos += 2
|
|
407
|
+
bits = []
|
|
408
|
+
for _ in range(9):
|
|
409
|
+
pair = sym[pos:pos + 2]
|
|
410
|
+
pos += 2
|
|
411
|
+
if pair == "SM":
|
|
412
|
+
bits.append(0)
|
|
413
|
+
elif pair == "MS":
|
|
414
|
+
bits.append(1)
|
|
415
|
+
else:
|
|
416
|
+
return bytes(out), bad, pos, False
|
|
417
|
+
value = sum(bit << k for k, bit in enumerate(bits[:8]))
|
|
418
|
+
if sum(bits) % 2 == 0:
|
|
419
|
+
bad += 1
|
|
420
|
+
out.append(value)
|
|
421
|
+
return bytes(out), bad, pos, False
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def main(argv):
|
|
425
|
+
version, n, pulses = read_tap(argv[1])
|
|
426
|
+
prg = open(argv[2], "rb").read() if len(argv) > 2 else None
|
|
427
|
+
sym = "".join(classify(c) for c in pulses)
|
|
428
|
+
total = sum(pulses)
|
|
429
|
+
print(f"{argv[1]}: TAP v{version}, {n} data bytes, {len(pulses)} entries, "
|
|
430
|
+
f"{total} cycles ({total / 985248:.2f} s PAL), "
|
|
431
|
+
f"{len(pulses) * 985248 / total:.0f} pulses/s")
|
|
432
|
+
for k in "SMLZ":
|
|
433
|
+
print(f" {k}: {sym.count(k)}")
|
|
434
|
+
blocks = []
|
|
435
|
+
pos = last_end = 0
|
|
436
|
+
while True:
|
|
437
|
+
pos = sym.find("LM", pos)
|
|
438
|
+
if pos < 0:
|
|
439
|
+
break
|
|
440
|
+
data, bad, end, clean = decode_block(sym, pos)
|
|
441
|
+
if len(data) < 10 or not clean:
|
|
442
|
+
pos += 1
|
|
443
|
+
continue
|
|
444
|
+
countdown, payload, check = data[:9], data[9:-1], data[-1]
|
|
445
|
+
copy = "first" if countdown[0] == 0x89 else "second" if countdown[0] == 0x09 else "?"
|
|
446
|
+
xor = 0
|
|
447
|
+
for b in payload:
|
|
448
|
+
xor ^= b
|
|
449
|
+
print(f" block at entry {pos}: {copy} copy, {len(payload)} bytes, "
|
|
450
|
+
f"checksum ${check:02X} {'ok' if xor == check else 'BAD'}, "
|
|
451
|
+
f"parity failures {bad}")
|
|
452
|
+
blocks.append((copy, payload))
|
|
453
|
+
pos = last_end = end
|
|
454
|
+
tail = len(sym) - last_end
|
|
455
|
+
firsts = [p for c, p in blocks if c == "first"]
|
|
456
|
+
if len(firsts) >= 1 and len(firsts[0]) == 192:
|
|
457
|
+
h = firsts[0]
|
|
458
|
+
start = h[1] | (h[2] << 8)
|
|
459
|
+
end = h[3] | (h[4] << 8)
|
|
460
|
+
name = h[5:21].decode("ascii", "replace")
|
|
461
|
+
print(f" header: type {h[0]}, ${start:04X}-${end:04X} exclusive, name '{name}'")
|
|
462
|
+
if len(firsts) >= 2:
|
|
463
|
+
body = firsts[1]
|
|
464
|
+
print(f" data block: {len(body)} bytes, "
|
|
465
|
+
f"{'length matches header' if len(body) == end - start else 'LENGTH MISMATCH'}")
|
|
466
|
+
if prg is not None:
|
|
467
|
+
same = prg[:2] == bytes([start & 0xFF, start >> 8]) and prg[2:] == body
|
|
468
|
+
print(f" against {argv[2]}: {'identical' if same else 'DIFFERENT'}")
|
|
469
|
+
seconds = [p for c, p in blocks if c == "second"]
|
|
470
|
+
if len(firsts) == len(seconds) and all(a == b for a, b in zip(firsts, seconds)):
|
|
471
|
+
print(" both copies of every block agree")
|
|
472
|
+
print(f" tail after the last KERNAL block: {tail} entries")
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
if __name__ == "__main__":
|
|
476
|
+
main(sys.argv)
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
On the two images above it printed, in part:
|
|
480
|
+
|
|
481
|
+
```text
|
|
482
|
+
hello.tap: TAP v1, 49398 data bytes, 49395 entries, 20497520 cycles (20.80 s PAL), 2374 pulses/s
|
|
483
|
+
block at entry 27136: first copy, 192 bytes, checksum $89 ok, parity failures 0
|
|
484
|
+
block at entry 31257: second copy, 192 bytes, checksum $89 ok, parity failures 0
|
|
485
|
+
block at entry 40754: first copy, 202 bytes, checksum $23 ok, parity failures 0
|
|
486
|
+
block at entry 45075: second copy, 202 bytes, checksum $23 ok, parity failures 0
|
|
487
|
+
header: type 1, $0801-$08CB exclusive, name 'HELLO '
|
|
488
|
+
data block: 202 bytes, length matches header
|
|
489
|
+
against hello.prg: identical
|
|
490
|
+
both copies of every block agree
|
|
491
|
+
tail after the last KERNAL block: 78 entries
|
|
492
|
+
|
|
493
|
+
turbo-master.tap: TAP v1, 107670 data bytes, 107667 entries, 48258176 cycles (48.98 s PAL), 2198 pulses/s
|
|
494
|
+
header: type 1, $0801-$0CA0 exclusive, name 'TURBO '
|
|
495
|
+
against turbo.prg: identical
|
|
496
|
+
tail after the last KERNAL block: 19110 entries
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
The same reader, run on the TAP that VICE recorded from the KERNAL's
|
|
500
|
+
own `SAVE` for the formats page, found the header block with checksum
|
|
501
|
+
`$59`, a 12-byte data block with checksum `$E6`, `type 1,
|
|
502
|
+
$0801-$080D exclusive, name 'T'`, both copies agreeing and no parity
|
|
503
|
+
failure. A writer and a reader that both agree with the KERNAL's tape
|
|
504
|
+
is the check this page rests on; a reader that only agrees with its own
|
|
505
|
+
writer proves nothing.
|
|
506
|
+
|
|
507
|
+
What to look at in the output:
|
|
508
|
+
|
|
509
|
+
- **`against x.prg: identical`** and **`length matches header`**. A
|
|
510
|
+
mismatch between header and data lengths is the commonest way to make
|
|
511
|
+
a tape that `FOUND`s and then fails.
|
|
512
|
+
- **Zero parity failures and every checksum `ok`** in all four blocks.
|
|
513
|
+
- **Pulses per second.** A KERNAL-format stream cannot exceed 2,620,
|
|
514
|
+
which is a tape of nothing but short pulses; the images here read
|
|
515
|
+
2,374 and, with the turbo tail included, 2,198. A figure above 2,700
|
|
516
|
+
means a pulse the KERNAL never writes, and a figure far below means a
|
|
517
|
+
long pause or a leader of something other than shorts.
|
|
518
|
+
- **The tail.** For a plain image it is the 78-pulse trailer. For a
|
|
519
|
+
turbo image it is the trailer plus the turbo block, 19,110 here; if
|
|
520
|
+
it is only 78 the `--append` was left off.
|
|
521
|
+
- **The file's MD5**, recorded before anyone loads it: `hello.tap`
|
|
522
|
+
is `4984f15a118db310f84ddaa0f95eb658` and `turbo-master.tap` is
|
|
523
|
+
`610745f60cb5c925378dc1ec97d574d8`; both writers are deterministic.
|
|
524
|
+
|
|
525
|
+
## Limits
|
|
526
|
+
|
|
527
|
+
- **VICE models a speed error and a wobble, and this page turned them
|
|
528
|
+
off.** With the four `-ds` options at their defaults the turbo recipe
|
|
529
|
+
measured its pulses spreading from 16 cycles to 52 on either bit and
|
|
530
|
+
the block taking 0.5 % longer; it still loaded. The same defaults were
|
|
531
|
+
not run against the KERNAL-format image here, and the KERNAL reader's
|
|
532
|
+
own tolerance was not measured.
|
|
533
|
+
- **A real Datasette is not measured here.** Its motor speed differs
|
|
534
|
+
from the nominal by an amount that varies by unit and by tape
|
|
535
|
+
position, and a TAP written at the KERNAL's exact pulse values says
|
|
536
|
+
nothing about how far off a deck can be before the reader loses a
|
|
537
|
+
bit. The KERNAL's two copies exist for that case.
|
|
538
|
+
- **Only the plain-`LOAD` path was run.** `LOAD"NAME"`, `LOAD"",1,1`
|
|
539
|
+
with a type 3 header, `VERIFY`, and a tape with more than one program
|
|
540
|
+
on it were not tried.
|
|
541
|
+
- **Timing granularity.** Every stage figure is a bracket of 50,000
|
|
542
|
+
cycles, about two and a half frames, from a bisection on the exit
|
|
543
|
+
screenshot; the program's own jiffy count is not a load timer.
|
|
544
|
+
- **No hardware tool.** Nothing here writes a physical cassette; the
|
|
545
|
+
TAP is the release artefact and the player's own TAP-to-audio step is
|
|
546
|
+
outside this page.
|
|
547
|
+
|
|
548
|
+
## Checklist
|
|
549
|
+
|
|
550
|
+
- The PRG's first two bytes are the load address; `01 08` wants header
|
|
551
|
+
type 1 and a name. Check with `xxd -l 2 game.prg`.
|
|
552
|
+
- Write the KERNAL image with `kernal_tap.py`; the cycle figure it
|
|
553
|
+
prints is the tape's own running time, not the load time.
|
|
554
|
+
- Decode it back with `tapcheck.py` and read `identical`, `length
|
|
555
|
+
matches header`, zero parity failures, both copies agreeing, and a
|
|
556
|
+
pulse rate in the KERNAL's range.
|
|
557
|
+
- Prove the load headless: `-1 image.tap`, `-moncommands play.mon`
|
|
558
|
+
holding `tapectrl 1`, `+dsresetwithcpu`, `-keybuf 'load\nrun\n'`, no
|
|
559
|
+
`-autostart`, and a cycle limit above the tape's running time plus
|
|
560
|
+
the boot and the pause at `FOUND`: about 15 million cycles more than
|
|
561
|
+
the writer's figure was enough for both images here.
|
|
562
|
+
- Look at the screenshot on both models.
|
|
563
|
+
- For a turbo release: the stub is a KERNAL-format program, so master
|
|
564
|
+
it with `kernal_tap.py` and append the turbo TAP; check the tail in
|
|
565
|
+
`tapcheck.py`'s output; shorten the turbo lead-in, since the motor
|
|
566
|
+
restarts within a few pulses of the stub's end.
|
|
567
|
+
- Record the TAP's MD5 in the release notes.
|
|
568
|
+
|
|
569
|
+
## Not measured here
|
|
570
|
+
|
|
571
|
+
- The shortest leader the KERNAL reader accepts before the header
|
|
572
|
+
block; 27,136 shorts is the KERNAL's own figure and the only one run.
|
|
573
|
+
- How the KERNAL uses the second copy of a block, and what it does with
|
|
574
|
+
a tape that carries only one.
|
|
575
|
+
- The rule that ends the pause at `FOUND` and whether a key press
|
|
576
|
+
shortens it in this KERNAL revision.
|
|
577
|
+
- Loading with VICE's speed error and wobble at their defaults, and on
|
|
578
|
+
a real Datasette.
|
|
579
|
+
- Whether `-autostart image.tap` reproduces the `LOAD` path; it was not
|
|
580
|
+
used, so the keyboard route above is the one the figures describe.
|
|
581
|
+
|
|
582
|
+
## See also
|
|
583
|
+
|
|
584
|
+
- [c64-file-formats](../formats/c64-file-formats.md), ".TAP" and its
|
|
585
|
+
"KERNAL bit encoding" subsection
|
|
586
|
+
- [release-disk](release-disk.md), the disk counterpart of this page
|
|
587
|
+
- [vice-reference](../runtime/vice-reference.md), the monitor and
|
|
588
|
+
`-moncommands`
|
|
589
|
+
- Pitfall: [tape_bit_is_a_pulse_pair_not_a_pulse](../pitfalls/loader.md)
|
|
590
|
+
- Recipe: [kickassembler/tape-turbo-loader](../recipes/kickassembler/tape-turbo-loader.md),
|
|
591
|
+
whose `make_tap.py` writes the turbo block and whose listing is the
|
|
592
|
+
stub used here
|