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,1189 @@
|
|
|
1
|
+
<!-- doc-type: reference -->
|
|
2
|
+
|
|
3
|
+
# C64 Game Design Patterns
|
|
4
|
+
|
|
5
|
+
Recurring mechanical patterns that C64 games use, described so that an AI
|
|
6
|
+
agent can match the right pattern to a brief. Each section names the
|
|
7
|
+
tradeoffs in C64 terms — cycle budgets, RAM budgets, and hardware
|
|
8
|
+
constraints — so the choice is mechanical, not aesthetic.
|
|
9
|
+
|
|
10
|
+
Cross-references: `../techniques/sprite.md` for multiplexer mechanics and
|
|
11
|
+
hardware sprite capabilities; `../techniques/scroll.md` for soft-scroll and
|
|
12
|
+
parallax; `./c64-game-archetypes.md` for which archetype pairs with which
|
|
13
|
+
patterns.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Collision systems
|
|
18
|
+
|
|
19
|
+
Collision is the most expensive per-frame logic in an action game. C64
|
|
20
|
+
games use up to four distinct systems, sometimes in combination.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
### Hardware sprite-sprite collision
|
|
25
|
+
|
|
26
|
+
Register $D01E reads back a bitmask of which sprites have overlapped
|
|
27
|
+
another sprite during the last frame. Reading the register clears it. The
|
|
28
|
+
chip sets bits automatically with zero CPU cost.
|
|
29
|
+
|
|
30
|
+
Constraints:
|
|
31
|
+
|
|
32
|
+
- **Lossy.** Only one bit per sprite pair; no record of which pair or when.
|
|
33
|
+
If two separate collisions involve the same sprite in one frame, the
|
|
34
|
+
register cannot distinguish them.
|
|
35
|
+
- **Position-after-the-fact.** The register reflects the state at read time,
|
|
36
|
+
not at the frame-render cycle where the pixels overlapped. If you read at
|
|
37
|
+
the top of the game loop, the collision happened somewhere in the previous
|
|
38
|
+
frame.
|
|
39
|
+
- **Multiplexed sprites break it.** When a hardware sprite is reused to
|
|
40
|
+
represent two logical actors via a multiplexer, $D01E reports hardware
|
|
41
|
+
sprite collisions, not logical actor collisions. A read after the first
|
|
42
|
+
multiplex band clears the register before the second band fires.
|
|
43
|
+
|
|
44
|
+
Use $D01E for: shmups where the player sprite never multiplexes and enemy
|
|
45
|
+
bullets are always in a known hardware sprite slot. The lossy single-bit
|
|
46
|
+
result is acceptable because any hit in the frame ends the player life
|
|
47
|
+
regardless of which bullet caused it.
|
|
48
|
+
|
|
49
|
+
```asm
|
|
50
|
+
lda $D01E ; read and clear sprite-sprite register
|
|
51
|
+
and #%00000001 ; test bit 0 (player sprite)
|
|
52
|
+
bne .hit ; branch if player overlapped any sprite
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### Hardware sprite-background collision
|
|
58
|
+
|
|
59
|
+
Register $D01F records which sprites overlapped a non-zero (non-space)
|
|
60
|
+
foreground character cell during rendering. Same single-bit-per-sprite
|
|
61
|
+
design, same read-clears semantics.
|
|
62
|
+
|
|
63
|
+
Constraints: identical to $D01E plus one more — the collision fires on
|
|
64
|
+
any foreground pixel, including decorative tiles. If your map has purely
|
|
65
|
+
decorative "solid-looking" character cells that are not walls, you must
|
|
66
|
+
arrange the character set so decorative tiles appear as background-colored
|
|
67
|
+
pixels at the ROM/RAM level (multicolor tricks) or use a separate solid
|
|
68
|
+
map and ignore $D01F entirely.
|
|
69
|
+
|
|
70
|
+
Use $D01F for: shmups where the terrain is a single-color foreground
|
|
71
|
+
charset and any contact with it is fatal or obstructing.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
### Software bounding-box collision
|
|
76
|
+
|
|
77
|
+
Compare each actor's X, Y extents against every other actor's extents in
|
|
78
|
+
pure 6510 arithmetic. Typical cost is 28–35 cycles per pair.
|
|
79
|
+
|
|
80
|
+
```asm
|
|
81
|
+
; Actor A at (ax, ay), width aw, height ah
|
|
82
|
+
; Actor B at (bx, by), width bw, height bh
|
|
83
|
+
; Returns carry set on overlap
|
|
84
|
+
|
|
85
|
+
.check_bbox:
|
|
86
|
+
lda ax
|
|
87
|
+
clc
|
|
88
|
+
adc aw
|
|
89
|
+
cmp bx ; A_right < B_left?
|
|
90
|
+
bcc .no_hit
|
|
91
|
+
lda bx
|
|
92
|
+
clc
|
|
93
|
+
adc bw
|
|
94
|
+
cmp ax ; B_right < A_left?
|
|
95
|
+
bcc .no_hit
|
|
96
|
+
lda ay
|
|
97
|
+
clc
|
|
98
|
+
adc ah
|
|
99
|
+
cmp by ; A_bottom < B_top?
|
|
100
|
+
bcc .no_hit
|
|
101
|
+
lda by
|
|
102
|
+
clc
|
|
103
|
+
adc bh
|
|
104
|
+
cmp ay ; B_bottom < A_top?
|
|
105
|
+
bcc .no_hit
|
|
106
|
+
sec ; overlap
|
|
107
|
+
rts
|
|
108
|
+
.no_hit:
|
|
109
|
+
clc
|
|
110
|
+
rts
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
At 8 actors, that is 28 pair-checks worst case (n*(n-1)/2). At 35 cycles
|
|
114
|
+
per check, 28 pairs cost ~980 cycles — under 1 % of a PAL frame. At 16
|
|
115
|
+
actors the cost is ~3920 cycles, still well within budget if bounded-box
|
|
116
|
+
is the only collision layer.
|
|
117
|
+
|
|
118
|
+
Use software bounding-box for: platformers where you need sub-tile accuracy
|
|
119
|
+
for landing/hitting platforms, and for adventure games where the player
|
|
120
|
+
rectangle must exactly enter a doorway hitbox.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
### Tilemap-grid collision
|
|
125
|
+
|
|
126
|
+
For terrain that never moves, test the map grid directly rather than every
|
|
127
|
+
possible object pair. Convert the actor's bounding corners to tile
|
|
128
|
+
coordinates and read the tile index from screen RAM or a parallel solid-bit
|
|
129
|
+
array.
|
|
130
|
+
|
|
131
|
+
```asm
|
|
132
|
+
; Convert pixel (px, py) to tile (tx, ty):
|
|
133
|
+
; tx = px / 8 (LSR three times for 8-pixel tiles)
|
|
134
|
+
; ty = py / 8 likewise
|
|
135
|
+
; Screen RAM address = $0400 + ty*40 + tx
|
|
136
|
+
|
|
137
|
+
.solid_check:
|
|
138
|
+
lda py
|
|
139
|
+
lsr
|
|
140
|
+
lsr
|
|
141
|
+
lsr ; ty = py >> 3
|
|
142
|
+
tax
|
|
143
|
+
lda row_lo, x ; precomputed: row_lo[ty] = ($0400 + ty*40) lo
|
|
144
|
+
sta ptr
|
|
145
|
+
lda row_hi, x
|
|
146
|
+
sta ptr+1
|
|
147
|
+
lda px
|
|
148
|
+
lsr
|
|
149
|
+
lsr
|
|
150
|
+
lsr ; tx = px >> 3
|
|
151
|
+
tay
|
|
152
|
+
lda (ptr), y ; read tile index from screen RAM
|
|
153
|
+
cmp #solid_min ; solid tiles are >= solid_min
|
|
154
|
+
bcs .is_solid
|
|
155
|
+
rts
|
|
156
|
+
.is_solid:
|
|
157
|
+
; handle blocked movement
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
A parallel solid-bit array (one bit per tile, 125 bytes for a 40×25 grid)
|
|
161
|
+
avoids comparing character codes and survives charset reassignments. Allocate
|
|
162
|
+
it in zero page or page 2 for fast access.
|
|
163
|
+
|
|
164
|
+
Tilemap-grid collision costs 20–40 cycles per corner tested. A four-corner
|
|
165
|
+
player check is 80–160 cycles — negligible for one player.
|
|
166
|
+
|
|
167
|
+
Use tilemap-grid for: all games where the terrain is tile-based (platformers,
|
|
168
|
+
adventure games, top-down mazes). Combine with software bounding-box for
|
|
169
|
+
actor-actor hits and hardware registers only where the constraints above are
|
|
170
|
+
acceptable.
|
|
171
|
+
|
|
172
|
+
**Decision table:**
|
|
173
|
+
|
|
174
|
+
| Game type | Primary collision | Secondary |
|
|
175
|
+
|-----------------|-----------------------------|-------------------|
|
|
176
|
+
| Shmup | $D01E + $D01F | — |
|
|
177
|
+
| Platformer | Tilemap-grid (terrain) | SW bbox (actors) |
|
|
178
|
+
| Adventure | Tilemap-grid only | — |
|
|
179
|
+
| Maze/Pac-style | Tilemap-grid | — |
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Map representations
|
|
184
|
+
|
|
185
|
+
The right map format depends on whether the game fits on one screen,
|
|
186
|
+
how far it scrolls, and whether ROM or disk space is the binding constraint.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### Screen-aligned tilemap
|
|
191
|
+
|
|
192
|
+
When the entire playfield fits within the 40×25 character grid, screen RAM
|
|
193
|
+
at $0400 is the map. Color RAM at $D800 holds per-tile colors. The map is
|
|
194
|
+
already in the VIC-II's native format; no copy loop is needed.
|
|
195
|
+
|
|
196
|
+
Cost: 1000 bytes screen RAM + 1000 bytes color RAM = 2 KB for the visible
|
|
197
|
+
state. Additional pages are a direct copy-to-screen-RAM operation (1000
|
|
198
|
+
LDA/STA pairs, ~6000 cycles, done in the vertical blank).
|
|
199
|
+
|
|
200
|
+
Use for: single-screen games (Pac-style mazes, single-room puzzles, fixed
|
|
201
|
+
arena shooters).
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
### Scrolling tilemap
|
|
206
|
+
|
|
207
|
+
Horizontal or vertical scrolling games maintain a map buffer wider or taller
|
|
208
|
+
than the visible area. The classic layout for a horizontally scrolling game:
|
|
209
|
+
|
|
210
|
+
- Map buffer: 48 columns × 25 rows of tile indices (1200 bytes for the
|
|
211
|
+
logical map slice in RAM).
|
|
212
|
+
- A `scroll_col_offset` byte tracks which logical column is at the left
|
|
213
|
+
edge of the visible 40 columns.
|
|
214
|
+
- On each 8-pixel scroll step, copy the new column of tiles into screen RAM
|
|
215
|
+
and color RAM, then update `scroll_col_offset`.
|
|
216
|
+
|
|
217
|
+
The soft-scroll register ($D016 XSCROLL) handles the 0–7 pixel fine phase.
|
|
218
|
+
When XSCROLL wraps, the column copy fires. This is the canonical
|
|
219
|
+
soft-scroll pattern described in `../techniques/scroll.md`. Decoding the
|
|
220
|
+
map into that column (metatiles to characters and colour RAM, the RLE
|
|
221
|
+
decoder, the CharPad import path) is `tile_map_render` on the same page,
|
|
222
|
+
with the recipe `../recipes/oscar64/tile-map-render.md`.
|
|
223
|
+
|
|
224
|
+
Vertical scrolling uses $D011 YSCROLL the same way, with a row copy at the
|
|
225
|
+
8-pixel boundary.
|
|
226
|
+
|
|
227
|
+
RAM cost for the buffer slice: 1–2 KB depending on scroll depth. For large
|
|
228
|
+
worlds, the full map lives on disk or in a compressed block and is paged in
|
|
229
|
+
as the player approaches a boundary.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### Compressed maps
|
|
234
|
+
|
|
235
|
+
A 128-room dungeon at 40×25 tiles per room is 128 KB uncompressed — well
|
|
236
|
+
above the 64 KB address space. Compression is mandatory.
|
|
237
|
+
|
|
238
|
+
**RLE:** Consecutive identical tile runs are stored as (count, tile). The
|
|
239
|
+
ratio depends on the room: three 40×22 rooms measured in
|
|
240
|
+
`recipes/oscar64/level-rle-decoder.md` pack 4.13:1 (a platform room),
|
|
241
|
+
1.40:1 (a maze, the worst of the three) and 10.73:1 (a nearly empty
|
|
242
|
+
room). The decoder in that recipe is 80 bytes of 6510 code by hand, or
|
|
243
|
+
149 bytes compiled from C at `-O2`, and decodes one 880-byte room in
|
|
244
|
+
16,470 to 27,673 cycles by hand or 28,193 to 50,447 cycles from C
|
|
245
|
+
(measured in VICE x64sc 3.10). An earlier version of this paragraph said
|
|
246
|
+
3:1 to 5:1, a 20 to 30 byte decoder and a few hundred cycles per screen;
|
|
247
|
+
none of the three had been measured, and neither the size nor the
|
|
248
|
+
cycle figure matches what was measured for this format.
|
|
249
|
+
|
|
250
|
+
**Dictionary / token substitution:** Repeated 2×2 or 4×4 tile blocks are
|
|
251
|
+
assigned token IDs. The map stores tokens; the decoder expands them into
|
|
252
|
+
screen RAM. Achieves 8:1 or better on repeating tilesets (castle brickwork,
|
|
253
|
+
cave walls). Costs more decoder RAM but compresses aggressively.
|
|
254
|
+
|
|
255
|
+
For disk-based games, the decompressor runs during the load of each level.
|
|
256
|
+
For cartridge or single-load games, all levels must decompress from a
|
|
257
|
+
banked ROM into the map buffer on demand.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Enemy / actor state machines
|
|
262
|
+
|
|
263
|
+
All moving non-player objects are actors. The C64 enforces an implicit actor
|
|
264
|
+
limit through the sprite system: eight hardware sprites per frame. Multiplexed
|
|
265
|
+
setups handle 16–32 logical sprites; software-rendered actors (character
|
|
266
|
+
graphics) can go higher but at steep cycle cost.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
### Slot-based actor table
|
|
271
|
+
|
|
272
|
+
Allocate a fixed array of N actor slots. Each slot holds:
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
actor_x: .byte 0 ; pixel X (0–255 or 16-bit for wide worlds)
|
|
276
|
+
actor_y: .byte 0 ; pixel Y
|
|
277
|
+
actor_state: .byte 0 ; state machine index (0 = inactive)
|
|
278
|
+
actor_type: .byte 0 ; type index (indexes into behavior tables)
|
|
279
|
+
actor_timer: .byte 0 ; general-purpose countdown
|
|
280
|
+
actor_dx: .byte 0 ; signed velocity X
|
|
281
|
+
actor_dy: .byte 0 ; signed velocity Y
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Seven bytes per actor. Eight actors: 56 bytes. Sixteen actors: 112 bytes.
|
|
285
|
+
All actor arrays live in the same zero page / page 2 / $C000 block so
|
|
286
|
+
indexed addressing is fast.
|
|
287
|
+
|
|
288
|
+
The sprite multiplexer (see `../techniques/sprite.md`) draws the active
|
|
289
|
+
slots. Slots with `actor_state = 0` are skipped by both the update loop
|
|
290
|
+
and the multiplexer.
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
### Per-actor state byte and handler jump table
|
|
295
|
+
|
|
296
|
+
Each actor type has a jump table of state-handler addresses indexed by
|
|
297
|
+
`actor_state`. The game loop iterates slots, reads the type, loads the
|
|
298
|
+
handler pointer for the current state, and JMPs indirectly.
|
|
299
|
+
|
|
300
|
+
```asm
|
|
301
|
+
.update_actors:
|
|
302
|
+
ldx #0
|
|
303
|
+
.next_slot:
|
|
304
|
+
lda actor_state, x
|
|
305
|
+
beq .skip ; state 0 = inactive
|
|
306
|
+
ldy actor_type, x
|
|
307
|
+
lda handler_lo, y ; handler table indexed by type
|
|
308
|
+
sta jmp_ptr
|
|
309
|
+
lda handler_hi, y
|
|
310
|
+
sta jmp_ptr+1
|
|
311
|
+
jmp (jmp_ptr) ; handler receives X = slot index
|
|
312
|
+
.skip:
|
|
313
|
+
inx
|
|
314
|
+
cpx #MAX_ACTORS
|
|
315
|
+
bne .next_slot
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Each handler reads `actor_state, x`, branches on it to the correct
|
|
319
|
+
sub-handler, updates position/timer, and transitions state by writing a
|
|
320
|
+
new value to `actor_state, x`. Transitions to state 0 return the slot to
|
|
321
|
+
the free pool.
|
|
322
|
+
|
|
323
|
+
Cost: one indirect JMP per actor per frame (~6 cycles) plus the handler
|
|
324
|
+
body. A simple patrol enemy handler (move, wall-check, reverse) runs in
|
|
325
|
+
~80 cycles.
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
### Path-based vs reactive AI
|
|
330
|
+
|
|
331
|
+
**Path-based:** Actor follows a pre-baked waypoint list stored in ROM.
|
|
332
|
+
State byte indexes the current waypoint. On each update, move toward
|
|
333
|
+
waypoint[state]; when within 2 pixels, increment state to the next
|
|
334
|
+
waypoint; wrap at the end. Zero decision logic, predictable, suitable for
|
|
335
|
+
fixed patrol routes in platformers and maze games.
|
|
336
|
+
|
|
337
|
+
```asm
|
|
338
|
+
; Simplified path follower
|
|
339
|
+
.path_step:
|
|
340
|
+
ldy actor_timer, x ; timer = waypoint index
|
|
341
|
+
lda waypoint_x, y
|
|
342
|
+
sec
|
|
343
|
+
sbc actor_x, x
|
|
344
|
+
beq .match_x
|
|
345
|
+
bmi .move_left
|
|
346
|
+
.move_right:
|
|
347
|
+
inc actor_x, x
|
|
348
|
+
jmp .check_y
|
|
349
|
+
.move_left:
|
|
350
|
+
dec actor_x, x
|
|
351
|
+
jmp .check_y
|
|
352
|
+
.match_x:
|
|
353
|
+
; advance waypoint only when X matches
|
|
354
|
+
inc actor_timer, x
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**Reactive AI:** Actor computes direction from its position to the player
|
|
358
|
+
each frame. Subtract player_x from actor_x to get dx; subtract player_y
|
|
359
|
+
from actor_y to get dy; adjust velocity by sign(dx) and sign(dy). The sign
|
|
360
|
+
extraction is a single BPL/BMI branch. Predictable pursuit; pair with a
|
|
361
|
+
short random timer that pauses the actor for variety.
|
|
362
|
+
|
|
363
|
+
Reactive AI costs ~40 cycles per actor for the subtraction and sign-branch.
|
|
364
|
+
Path-based costs ~20 cycles per actor. Use path-based for turrets and fixed
|
|
365
|
+
patrol routes; use reactive for chasers and bosses.
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
### Object pool: slot table, spawn and despawn
|
|
370
|
+
|
|
371
|
+
The slot table above is an object pool. The parts that recur in every
|
|
372
|
+
game are the allocator, the spawner that feeds it, and the tick that
|
|
373
|
+
walks only the live slots. All figures below are cycles measured with
|
|
374
|
+
CIA1 timer A in VICE x64sc 3.10, Oscar64 -O2, eight slots, net of the
|
|
375
|
+
call and loop around them (rung 1; the listing is
|
|
376
|
+
`../recipes/oscar64/object-pool.md`).
|
|
377
|
+
|
|
378
|
+
**Parallel arrays, not structs.** One array per attribute, indexed by
|
|
379
|
+
slot number, so every access is `lda obj_x,y` with the slot in a register.
|
|
380
|
+
A struct array needs a multiply or a stride-added pointer per field.
|
|
381
|
+
Oscar64 compiles the C below to the same absolute-indexed code.
|
|
382
|
+
|
|
383
|
+
**Find a free slot by scanning for state 0.** Cost grows with the slot
|
|
384
|
+
number found: 27 cycles when slot 0 is free, 64 when slot 3 is the first
|
|
385
|
+
free one, 124 for a full pool that refuses. About 12 cycles per slot
|
|
386
|
+
examined, so a 16-slot pool costs up to about 220 on a refusal (rung 3,
|
|
387
|
+
from the measured eight-slot figures). Refusals are the normal case in a
|
|
388
|
+
busy frame, so the full-pool number is the one to budget.
|
|
389
|
+
|
|
390
|
+
**Free-list alternative.** Keep a stack of free slot numbers; allocate by
|
|
391
|
+
popping, free by pushing. A pop and a push together measured 44 cycles,
|
|
392
|
+
constant whatever the pool size. The trade-offs: eight bytes plus a top
|
|
393
|
+
index; every free must go through the push, so a handler that clears
|
|
394
|
+
`obj_state` directly leaks the slot for good; and slots come back in
|
|
395
|
+
free order, not lowest first, which changes draw order on a multiplexer
|
|
396
|
+
that walks slots in index order. Use it for pools of 16 or more where the
|
|
397
|
+
scan's worst case matters; the scan is fine at eight.
|
|
398
|
+
|
|
399
|
+
**Spawn from a wave table.** Five bytes per entry, in frame order,
|
|
400
|
+
terminated by a frame byte of $ff:
|
|
401
|
+
|
|
402
|
+
| byte | meaning |
|
|
403
|
+
|---|---|
|
|
404
|
+
| 0 | frame on which the entry fires |
|
|
405
|
+
| 1 | type (indexes the per-type tables) |
|
|
406
|
+
| 2 | x of the first object |
|
|
407
|
+
| 3 | y |
|
|
408
|
+
| 4 | count; objects are placed 16 pixels apart in x |
|
|
409
|
+
|
|
410
|
+
The spawner keeps one position into the table and fires every entry
|
|
411
|
+
whose frame byte equals the current frame. A spawn into a full pool is
|
|
412
|
+
dropped, not queued: the wave loses that object and the game goes on.
|
|
413
|
+
The game skeleton recipe in issue #1 reuses this layout. A spawn that
|
|
414
|
+
fills slot 0 and frees it again measured 146 cycles, of which 27 is the
|
|
415
|
+
scan; the rest is six stores and, in the listing, two counters and a log
|
|
416
|
+
store that a game would not have.
|
|
417
|
+
|
|
418
|
+
**Despawn.** Writing 0 to `obj_state` frees the slot; the other fields
|
|
419
|
+
are left stale and overwritten by the next spawn. Two paths free a slot
|
|
420
|
+
inside the tick: y at or past the bottom of the window (the constant is
|
|
421
|
+
the game's choice; 250 in the listing) and a per-slot countdown timer
|
|
422
|
+
reaching 0. A hit does not free the slot: it sets a dying state and a
|
|
423
|
+
short timer, so the explosion frames play from the same slot and the
|
|
424
|
+
timer does the freeing. Timer 0 means no timer, for bosses and scenery.
|
|
425
|
+
|
|
426
|
+
**Iterate only active slots.** The tick tests `obj_state` and skips free
|
|
427
|
+
slots. Eight active slots: 380 cycles for the move, off-screen test and
|
|
428
|
+
timer, 47.5 per slot. Eight free slots: 106 cycles, 13.25 per slot
|
|
429
|
+
skipped. A 16-slot pool with 8 active is therefore about 486 cycles a
|
|
430
|
+
frame before any per-type behaviour runs (rung 3).
|
|
431
|
+
|
|
432
|
+
```c
|
|
433
|
+
// Object pool: parallel arrays, scan allocator, wave table, per-frame tick.
|
|
434
|
+
#define MAX_OBJ 8
|
|
435
|
+
#define NO_SLOT 0xff
|
|
436
|
+
#define OFF_BOTTOM 250
|
|
437
|
+
|
|
438
|
+
char obj_state[MAX_OBJ]; // 0 = free; anything else is active
|
|
439
|
+
char obj_type[MAX_OBJ];
|
|
440
|
+
char obj_x[MAX_OBJ];
|
|
441
|
+
char obj_y[MAX_OBJ];
|
|
442
|
+
char obj_dy[MAX_OBJ];
|
|
443
|
+
char obj_timer[MAX_OBJ]; // frames left; 0 = no timer
|
|
444
|
+
|
|
445
|
+
static const char type_life[4] = { 0, 12, 12, 40 };
|
|
446
|
+
static const char type_dy[4] = { 0, 1, 1, 2 };
|
|
447
|
+
|
|
448
|
+
// frame, type, x, y, count; $ff ends the table. Entries in frame order.
|
|
449
|
+
static const char wave_table[] = {
|
|
450
|
+
0, 1, 24, 50, 8,
|
|
451
|
+
1, 3, 100, 70, 3,
|
|
452
|
+
0xff };
|
|
453
|
+
static char wave_pos;
|
|
454
|
+
|
|
455
|
+
char pool_alloc(void)
|
|
456
|
+
{
|
|
457
|
+
for (char i = 0; i < MAX_OBJ; i++)
|
|
458
|
+
if (obj_state[i] == 0)
|
|
459
|
+
return i;
|
|
460
|
+
return NO_SLOT;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
char spawn(char type, char x, char y)
|
|
464
|
+
{
|
|
465
|
+
char s = pool_alloc();
|
|
466
|
+
if (s == NO_SLOT)
|
|
467
|
+
return NO_SLOT; // pool full: the wave loses this one
|
|
468
|
+
obj_type[s] = type;
|
|
469
|
+
obj_x[s] = x;
|
|
470
|
+
obj_y[s] = y;
|
|
471
|
+
obj_dy[s] = type_dy[type];
|
|
472
|
+
obj_timer[s] = type_life[type];
|
|
473
|
+
obj_state[s] = 1; // last: the slot is visible once set
|
|
474
|
+
return s;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
void wave_step(char frame)
|
|
478
|
+
{
|
|
479
|
+
while (wave_table[wave_pos] == frame)
|
|
480
|
+
{
|
|
481
|
+
char type = wave_table[wave_pos + 1];
|
|
482
|
+
char x = wave_table[wave_pos + 2];
|
|
483
|
+
char y = wave_table[wave_pos + 3];
|
|
484
|
+
char count = wave_table[wave_pos + 4];
|
|
485
|
+
for (char k = 0; k < count; k++)
|
|
486
|
+
spawn(type, x + (k << 4), y);
|
|
487
|
+
wave_pos += 5;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
void update_all(void)
|
|
492
|
+
{
|
|
493
|
+
for (char i = 0; i < MAX_OBJ; i++)
|
|
494
|
+
{
|
|
495
|
+
if (obj_state[i] == 0)
|
|
496
|
+
continue; // free slot: skip
|
|
497
|
+
char y = obj_y[i] + obj_dy[i];
|
|
498
|
+
obj_y[i] = y;
|
|
499
|
+
if (y >= OFF_BOTTOM)
|
|
500
|
+
obj_state[i] = 0; // left the screen
|
|
501
|
+
else if (obj_timer[i] != 0 && --obj_timer[i] == 0)
|
|
502
|
+
obj_state[i] = 0; // timer ran out
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
The fragment compiles with Oscar64 -O2 as written once a `main` calls
|
|
508
|
+
`wave_step` and `update_all` once a frame; the listing gate does not
|
|
509
|
+
build C fragments on this page. In the recipe's build the compiler emits
|
|
510
|
+
`update_all` as one absolute-indexed loop with the timer decrement
|
|
511
|
+
folded into an `sbc #0` that borrows from the off-screen compare, which
|
|
512
|
+
is the code a hand assembler would write (read from Oscar64's `.asm`
|
|
513
|
+
output for the recipe listing, not from this fragment). `../recipes/oscar64/simple-shmup.md` spawns
|
|
514
|
+
its four enemies inline into a fixed `enemies[]` array with an `active`
|
|
515
|
+
flag; the pool is what that becomes once counts vary per wave.
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
## Game loop patterns
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
### Single-buffer IRQ-driven scroll
|
|
524
|
+
|
|
525
|
+
The dominant C64 pattern. The game runs one logical screen buffer. A raster
|
|
526
|
+
IRQ fires at the bottom of the playfield to swap sprite pointers and perform
|
|
527
|
+
any split-screen HUD work. Scroll column copies happen in the vertical blank
|
|
528
|
+
IRQ. Everything is single-buffered: the CPU writes to screen RAM while the
|
|
529
|
+
VIC-II reads from it, which is safe because writes and reads contend only
|
|
530
|
+
during the 8 raster lines of badlines, producing at most one garbled
|
|
531
|
+
character per badline — the classic "scroll tear" that well-timed VBlank
|
|
532
|
+
copies avoid.
|
|
533
|
+
|
|
534
|
+
The frame structure:
|
|
535
|
+
|
|
536
|
+
```
|
|
537
|
+
VBlank IRQ fires (line 0 / line 300 PAL):
|
|
538
|
+
- Copy new scroll column to screen RAM
|
|
539
|
+
- Update XSCROLL / YSCROLL register
|
|
540
|
+
- Update sprite positions from actor table
|
|
541
|
+
- Acknowledge $D019, re-arm for HUD split
|
|
542
|
+
|
|
543
|
+
[Game logic runs in main loop during active display]
|
|
544
|
+
|
|
545
|
+
Raster IRQ fires at HUD split line:
|
|
546
|
+
- Write HUD sprite data
|
|
547
|
+
- Possibly change border color for debug
|
|
548
|
+
- Acknowledge $D019, re-arm for next VBlank
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
The main loop processes input, advances actor states, resolves collision,
|
|
552
|
+
and updates score. On PAL (50 Hz) the loop has ~19 700 cycles of CPU time
|
|
553
|
+
per frame after IRQ overhead. The loop's wait-for-frame, frame counter,
|
|
554
|
+
dropped-frame detection and border-colour budget bar are the technique
|
|
555
|
+
`frame_sync_loop` in `techniques/raster.md` (recipe
|
|
556
|
+
`recipes/oscar64/frame-sync-loop.md`); reading the controls as press
|
|
557
|
+
events with auto-repeat is `techniques/input.md`; sub-pixel movement and
|
|
558
|
+
the jump arc are `techniques/maths.md`.
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
### Double-buffer
|
|
563
|
+
|
|
564
|
+
Two full screen RAM areas (1 KB each: 1,000 cells plus the sprite pointer
|
|
565
|
+
block in the last eight bytes of the page) alternate: the CPU draws into
|
|
566
|
+
the back buffer while the VIC-II displays the front buffer. At VBlank,
|
|
567
|
+
swap the VIC-II base address ($D018) to flip. The layout, the sprite
|
|
568
|
+
pointer block that moves with the page, and the frame-parity discipline
|
|
569
|
+
are `screen_double_buffer_d018` in `../techniques/memory-banking.md`, with
|
|
570
|
+
the recipe `../recipes/oscar64/double-buffer.md` and a companion that shows
|
|
571
|
+
the corrupted sprite you get without the pointer mirror.
|
|
572
|
+
|
|
573
|
+
Memory cost: 1 KB for the second screen RAM (an earlier version of this
|
|
574
|
+
section said 2 KB). Color RAM ($D800–$DBFF) cannot be double-buffered
|
|
575
|
+
(only one color RAM exists in hardware), so double-buffering only
|
|
576
|
+
eliminates screen-RAM tearing, not color-RAM tearing.
|
|
577
|
+
|
|
578
|
+
Use double-buffer only when the game redraws most of the screen each frame
|
|
579
|
+
(3D wireframe, full-screen bitmap effects) and tearing is visually
|
|
580
|
+
unacceptable. Standard tile-scroll games should prefer single-buffer plus
|
|
581
|
+
carefully timed column copies.
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
### 60 Hz vs 50 Hz update rate and cross-region releases
|
|
586
|
+
|
|
587
|
+
PAL runs at 50 frames/second; NTSC runs at ~60 frames/second. A game loop
|
|
588
|
+
tied to the frame rate runs 20 % faster on NTSC. Three strategies:
|
|
589
|
+
|
|
590
|
+
1. **PAL-only:** Ignore NTSC. Ship for the European market. Fastest to
|
|
591
|
+
implement; widely acceptable for 1980s-style games.
|
|
592
|
+
|
|
593
|
+
2. **Dual-rate logic:** Detect region via raster line count ($D011 bit 7
|
|
594
|
+
cycle comparison, or count raster lines in VBlank and compare to 312
|
|
595
|
+
vs 263). Run game logic at the detected frame rate. Physics values
|
|
596
|
+
(gravity, speeds) must be tuned separately per region, stored in two
|
|
597
|
+
ROM tables selected at startup.
|
|
598
|
+
|
|
599
|
+
3. **Fixed-timestep decoupled from frame rate:** Run game logic at a fixed
|
|
600
|
+
50 Hz tick using a CIA timer (CIA1 Timer A), fire an NMI or flag a
|
|
601
|
+
semaphore, and render at the display frame rate. Correct but complex;
|
|
602
|
+
the extra timer interrupt costs ~35 cycles per frame of overhead. Used
|
|
603
|
+
by some later commercial titles.
|
|
604
|
+
|
|
605
|
+
For most games: PAL-only or dual-rate with two physics tables is the
|
|
606
|
+
practical choice. See `../hardware/pal-ntsc-reference.md` for region
|
|
607
|
+
detection details and the `c64_pal_ntsc_diff` tool for per-topic diffs.
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## Memory layouts for game state
|
|
612
|
+
|
|
613
|
+
---
|
|
614
|
+
|
|
615
|
+
### The 38911-byte basic-free area
|
|
616
|
+
|
|
617
|
+
BASIC ROM occupies $A000–$BFFF. Switching it out (bit 0 of $0001) frees
|
|
618
|
+
8 KB for code or data but loses BASIC. All action games disable BASIC. With
|
|
619
|
+
BASIC and KERNAL both out ($0001 = %00110101), the full $A000–$FFFF range
|
|
620
|
+
(minus I/O at $D000–$DFFF) is available.
|
|
621
|
+
|
|
622
|
+
Practical RAM budget for a game without KERNAL:
|
|
623
|
+
|
|
624
|
+
```
|
|
625
|
+
$0002–$00FF Zero page (254 bytes) — fast scratch, counters, pointers
|
|
626
|
+
$0100–$01FF Stack (256 bytes, fixed)
|
|
627
|
+
$0200–$02FF Page 2 (256 bytes) — actor tables, IRQ flag bytes
|
|
628
|
+
$0300–$03FF Page 3 (256 bytes) — jump vectors; avoid overwriting $0314/$0315
|
|
629
|
+
$0400–$07FF Screen RAM (1000 bytes used; 96 bytes spare)
|
|
630
|
+
$0800–$9FFF ~38 KB — code, data, charset, music, level data
|
|
631
|
+
$A000–$BFFF 8 KB free when BASIC out — level data, extra code
|
|
632
|
+
$C000–$CFFF 4 KB — canonical "game state" block (see below)
|
|
633
|
+
$D000–$DFFF I/O (VIC-II, SID, CIA, Color RAM) — never overwrite without banking
|
|
634
|
+
$E000–$FFFF 8 KB free when KERNAL out — fast data, but lose KERNAL routines
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
### $C000–$CFFF as the canonical game state block
|
|
640
|
+
|
|
641
|
+
The 4 KB at $C000 is above the typical code-and-data area but below the I/O
|
|
642
|
+
window. It is not shadowed by any ROM. Convention in C64 games:
|
|
643
|
+
|
|
644
|
+
- `$C000–$C0FF`: Current-level descriptor (tileset index, music track,
|
|
645
|
+
enemy spawn table pointer, starting player position).
|
|
646
|
+
- `$C100–$C1FF`: Persistent game state (lives, score BCD, continues,
|
|
647
|
+
unlocked levels, inventory flags).
|
|
648
|
+
- `$C200–$C2FF`: Per-frame scratch (collision result bits, input debounce
|
|
649
|
+
state, IRQ semaphores).
|
|
650
|
+
- `$C300–$CFFF`: Overflow for large actor tables or a secondary map buffer
|
|
651
|
+
when the main buffer at $0800 is insufficient.
|
|
652
|
+
|
|
653
|
+
This layout is a convention, not a hardware requirement. Its value is that
|
|
654
|
+
the MCP tools in this KB assume it when generating code, so briefings and
|
|
655
|
+
recipes stay consistent.
|
|
656
|
+
|
|
657
|
+
---
|
|
658
|
+
|
|
659
|
+
### Save state mechanisms
|
|
660
|
+
|
|
661
|
+
The C64 has no battery-backed RAM in stock hardware. Three historical
|
|
662
|
+
approaches:
|
|
663
|
+
|
|
664
|
+
**None.** Most arcade-style games. Progress resets on power-off. The entire
|
|
665
|
+
game fits in one session. Simplest; zero save-system code.
|
|
666
|
+
|
|
667
|
+
**Cassette write.** Use the KERNAL `SAVE` routine (or direct serial/tape
|
|
668
|
+
protocol) to write the $C100–$C1FF persistent state block to cassette.
|
|
669
|
+
Load it back with `LOAD`. Cassette save of 256 bytes takes ~10 seconds on
|
|
670
|
+
a stock 1530 datasette. Acceptable for turn-based or RPG games where the
|
|
671
|
+
player expects a save ritual.
|
|
672
|
+
|
|
673
|
+
**Disk write.** Open a sequential or relative file on the 1541 via the
|
|
674
|
+
KERNAL `OPEN`/`PRINT#`/`CLOSE` sequence and write the persistent state
|
|
675
|
+
block. A 256-byte block writes in under 1 second. Disk save is the standard
|
|
676
|
+
for C64 RPGs and adventure games. Requires the KERNAL to remain available
|
|
677
|
+
(do not page it out) during the save, or rebank it in for the save routine.
|
|
678
|
+
The call sequences, the error-channel check and a measured write-and-read
|
|
679
|
+
round-trip are `kernal_file_write_seq`, `kernal_file_read_seq` and
|
|
680
|
+
`error_channel_check` in `../techniques/file-io.md`.
|
|
681
|
+
|
|
682
|
+
### Save-file policy: first run, replace, version, missing drive
|
|
683
|
+
|
|
684
|
+
The file calls are the small part. What a game has to decide around
|
|
685
|
+
them is below; every figure is from
|
|
686
|
+
`../recipes/oscar64/high-score-persist.md`, measured in VICE x64sc 3.10
|
|
687
|
+
with its 1541 emulation and a disk formatted by c1541, unless marked.
|
|
688
|
+
The bare call sequences are in `../recipes/oscar64/save-load-seq-file.md`
|
|
689
|
+
and `../recipes/kickassembler/file-io-roundtrip.md` and are not repeated
|
|
690
|
+
here.
|
|
691
|
+
|
|
692
|
+
**Allow for the banner.** A 1541 answers its first status read after
|
|
693
|
+
reset with `73,CBM DOS V2.6 1541,00,00` (measured with a probe that
|
|
694
|
+
reads the channel before any other command). A check that treats any
|
|
695
|
+
non-zero code as failure refuses a healthy drive. The recipe's first
|
|
696
|
+
call is the OPEN of the save file, and the `62` or `00` that raises
|
|
697
|
+
replaces the banner before the channel is read; a game that reads the
|
|
698
|
+
channel first must accept `73` as healthy. The `2.6` in that line is
|
|
699
|
+
the DOS version the replies below come from.
|
|
700
|
+
|
|
701
|
+
**First run.** Try to read the file before writing anything. On a disk
|
|
702
|
+
that has never held it, OPEN for read succeeds (ST `00`), the read
|
|
703
|
+
returns 0 bytes with ST `$42`, and the error channel says `62, FILE NOT
|
|
704
|
+
FOUND,00,00`. That is the first-run signal, not a fault: show the
|
|
705
|
+
default table and write it at once, so the next run finds the file.
|
|
706
|
+
|
|
707
|
+
**Replace by scratch, then write.** OPEN of `NAME,S,W` on a name that
|
|
708
|
+
exists is refused with `63, FILE EXISTS,00,00`, and every byte written
|
|
709
|
+
into that channel afterwards comes back with ST `$80`, the
|
|
710
|
+
device-not-present bit (`KRNIO_NODEVICE` in Oscar64's `kernalio.h`): the
|
|
711
|
+
drive did not acknowledge bytes on a channel it never opened, and the
|
|
712
|
+
disk is unchanged. `@0:NAME,S,W` asks
|
|
713
|
+
DOS 2.6 for save-with-replace, a command with a long-reported
|
|
714
|
+
corruption defect that this KB has not measured (rung 4) and does not
|
|
715
|
+
recommend relying on (`../techniques/file-io.md`,
|
|
716
|
+
`kernal_file_write_seq`). Send `S0:NAME` on the command channel
|
|
717
|
+
instead, read the reply on the same open channel (`01, FILES
|
|
718
|
+
SCRATCHED,01,00`; closing channel 15 and reopening it reads `00, OK`
|
|
719
|
+
instead), then OPEN for write. Scratching a name that is not there
|
|
720
|
+
answers `01, FILES SCRATCHED,00,00` (count `00`) and changes nothing
|
|
721
|
+
(measured in VICE). Either way the close must be allowed to finish: a
|
|
722
|
+
run cut
|
|
723
|
+
before it leaves a splat file (`../pitfalls/kernal-and-io.md`,
|
|
724
|
+
`krnio_save_leaves_splat_file`).
|
|
725
|
+
|
|
726
|
+
**Version byte and a fixed-size record.** Put a magic pair and a
|
|
727
|
+
version byte at the head of the record and keep the record one fixed
|
|
728
|
+
size. On read, accept it only when the byte count, the magic and the
|
|
729
|
+
version all match; otherwise say so on screen, scratch it and write the
|
|
730
|
+
defaults. Run against a 15-byte version-0 file planted with c1541, the
|
|
731
|
+
recipe printed `OLD FORMAT 15 BYTES V0: RESET`, replaced it and read a
|
|
732
|
+
good record back. An old save is then readable or cleanly replaced,
|
|
733
|
+
never half-parsed.
|
|
734
|
+
|
|
735
|
+
**When drive 8 does not answer.** Two cases, and they look different.
|
|
736
|
+
A drive with no disk (VICE `-default` with no `-8`, which still
|
|
737
|
+
emulates a 1541) lets the OPEN for read succeed, returns 0 bytes with
|
|
738
|
+
ST `$42`, and says `74,DRIVE NOT READY,00,00`. Treat any first-read
|
|
739
|
+
code other than `00` or `62` as saving off for the session: show the
|
|
740
|
+
defaults, say that scores will not be saved, keep playing, and do not
|
|
741
|
+
attempt the write. No device on the bus at all (VICE with
|
|
742
|
+
`-drive8type 0`) is caught one call earlier: an OPEN that sends a
|
|
743
|
+
filename or command returns C=1 with ST bit 7 set (`KRNIO_NODEVICE`;
|
|
744
|
+
the KERNAL's code for it is 5, device not present), because nothing
|
|
745
|
+
pulled DATA low in answer to the
|
|
746
|
+
LISTEN (`../hardware/kernal-routines-reference.md`, CHKOUT entry; the
|
|
747
|
+
recipe prints `OPEN 0 ST=80` and `NO DEVICE`). An OPEN of channel 15
|
|
748
|
+
with an empty name cannot see this: the KERNAL sends nothing on the bus
|
|
749
|
+
when the filename length is zero, so it returns success with ST `00`
|
|
750
|
+
whether or not a drive exists (measured: `1 ST=00` with no drive, where
|
|
751
|
+
`I0` on the same channel gave `0 ST=80`). The first CHKIN on such a
|
|
752
|
+
channel then hangs with no timeout (same page, CHKIN entry), and a
|
|
753
|
+
status read through `krnio_gets` starts with CHKIN; an earlier build of
|
|
754
|
+
the recipe did exactly that and sat with its title line alone on screen
|
|
755
|
+
after 24,000,000 cycles. So the OPEN whose result decides whether the
|
|
756
|
+
drive exists must be one that sends bytes, the save file's own OPEN for
|
|
757
|
+
read is the natural one, and no status read may run before it has
|
|
758
|
+
succeeded.
|
|
759
|
+
|
|
760
|
+
**KERNAL banked in.** Every call in the sequence is a KERNAL call and
|
|
761
|
+
the serial code drives CIA2 directly, so the ROM at $E000-$FFFF and
|
|
762
|
+
the I/O area at $D000-$DFFF must both be mapped from the first OPEN to
|
|
763
|
+
the last CLOSE. A game that runs with the KERNAL out banks it back in
|
|
764
|
+
around the save routine and keeps its own interrupts off, or its
|
|
765
|
+
handler reachable through the ROM's $0314 vector, while the ROM is in.
|
|
766
|
+
|
|
767
|
+
---
|
|
768
|
+
|
|
769
|
+
## Scoring, lives, and HUD
|
|
770
|
+
|
|
771
|
+
---
|
|
772
|
+
|
|
773
|
+
### HUD via raster-IRQ split
|
|
774
|
+
|
|
775
|
+
The HUD (score, lives, timer) is most commonly placed above or below the
|
|
776
|
+
playfield using a raster-IRQ color and charset switch. The playfield
|
|
777
|
+
uses one charset; at the HUD split line the IRQ switches $D018 to point
|
|
778
|
+
to a second charset block where the score digits are custom-drawn,
|
|
779
|
+
restoring the playfield charset on the line where the playfield resumes.
|
|
780
|
+
|
|
781
|
+
Top-of-screen HUD (most common in platformers):
|
|
782
|
+
|
|
783
|
+
```
|
|
784
|
+
Raster IRQ at line 50 (start of playfield):
|
|
785
|
+
- Restore playfield $D018 charset pointer
|
|
786
|
+
- Restore playfield sprite colors
|
|
787
|
+
- Acknowledge, re-arm for VBlank
|
|
788
|
+
|
|
789
|
+
VBlank: write updated score digits to HUD screen RAM rows ($0400–$04A7)
|
|
790
|
+
before the beam returns
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
Bottom-of-screen HUD (common in shmups):
|
|
794
|
+
|
|
795
|
+
```
|
|
796
|
+
Raster IRQ at line 216 (after last playfield row):
|
|
797
|
+
- Switch $D018 to HUD charset
|
|
798
|
+
- Change sprite priority/colors for HUD icons
|
|
799
|
+
- Acknowledge, re-arm for VBlank
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
Both HUD areas use the same physical screen RAM ($0400–$07E7). The split
|
|
803
|
+
just changes which charset the VIC-II uses to render the character codes in
|
|
804
|
+
those rows. Sprites can cross the split freely; only the character rendering
|
|
805
|
+
changes.
|
|
806
|
+
|
|
807
|
+
---
|
|
808
|
+
|
|
809
|
+
### Score update routines
|
|
810
|
+
|
|
811
|
+
Score is stored as BCD (binary-coded decimal) because display requires
|
|
812
|
+
per-digit output and BCD avoids a full binary-to-decimal conversion every
|
|
813
|
+
frame.
|
|
814
|
+
|
|
815
|
+
**Per-digit increment (simple):** Six bytes for a six-digit score. Each
|
|
816
|
+
byte holds two BCD digits (packed: high nibble = tens, low nibble = units).
|
|
817
|
+
Use the 6510 ADC instruction with the D (decimal) flag set (SED before ADC,
|
|
818
|
+
CLD after). Carry propagates automatically through packed BCD.
|
|
819
|
+
|
|
820
|
+
```asm
|
|
821
|
+
sed ; set decimal mode
|
|
822
|
+
clc
|
|
823
|
+
lda score_lo
|
|
824
|
+
adc points_lo ; add points, BCD carry propagates
|
|
825
|
+
sta score_lo
|
|
826
|
+
lda score_hi
|
|
827
|
+
adc points_hi
|
|
828
|
+
sta score_hi
|
|
829
|
+
cld ; clear decimal mode immediately
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
**Display:** Each score byte maps to two PETSCII or custom-charset digit
|
|
833
|
+
characters. Extract high nibble with LSR×4, add character-set base offset,
|
|
834
|
+
write to screen RAM. Extract low nibble with AND #$0F, add offset, write.
|
|
835
|
+
|
|
836
|
+
```asm
|
|
837
|
+
lda score_hi
|
|
838
|
+
lsr
|
|
839
|
+
lsr
|
|
840
|
+
lsr
|
|
841
|
+
lsr ; high nibble
|
|
842
|
+
clc
|
|
843
|
+
adc #'0' ; PETSCII digit base (or custom charset offset)
|
|
844
|
+
sta $0402 ; write to HUD position
|
|
845
|
+
lda score_hi
|
|
846
|
+
and #$0F ; low nibble
|
|
847
|
+
clc
|
|
848
|
+
adc #'0'
|
|
849
|
+
sta $0403
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
For custom charsets, replace `#'0'` with the base character index of your
|
|
853
|
+
digit glyphs.
|
|
854
|
+
|
|
855
|
+
**Full-number convert (BCD to screen RAM):** If points are awarded in
|
|
856
|
+
binary (e.g. physics-derived values), convert to BCD using the double-dabble
|
|
857
|
+
algorithm before storing. Double-dabble on a 16-bit value costs 875
|
|
858
|
+
cycles in assembly with decimal mode, including the unpack to five screen
|
|
859
|
+
codes (measured in VICE x64sc 3.10 with CIA1 timer A; see "Printing
|
|
860
|
+
numbers" below). An earlier version of this page said ~300 cycles, which
|
|
861
|
+
was not measured. Run it in VBlank or on score-change events, not every
|
|
862
|
+
frame.
|
|
863
|
+
|
|
864
|
+
---
|
|
865
|
+
|
|
866
|
+
### Printing numbers
|
|
867
|
+
|
|
868
|
+
A binary value (a timer, a coordinate, a physics-derived score) has to
|
|
869
|
+
become digits on the screen. That is two jobs: converting binary to
|
|
870
|
+
decimal digits, and writing those digits into screen RAM.
|
|
871
|
+
|
|
872
|
+
**Screen codes, no KERNAL.** Digits `0`-`9` are screen codes `$30`-`$39`,
|
|
873
|
+
the same values as their PETSCII codes, so `digit + $30` is right whether
|
|
874
|
+
you think in screen codes or PETSCII. Space is `$20` in both. Letters are
|
|
875
|
+
not the same: `A` is screen code `$01` and PETSCII `$41`, so the hex
|
|
876
|
+
digits `A`-`F` are `$01`-`$06` on screen. Store the byte at
|
|
877
|
+
`$0400 + 40 * row + column` and set colour RAM at `$D800` once at start.
|
|
878
|
+
CHROUT is not needed for a HUD: it costs a cursor, a colour write and a
|
|
879
|
+
scroll check per character.
|
|
880
|
+
|
|
881
|
+
**Subtract-powers.** For each power of ten from 10000 down to 10, count
|
|
882
|
+
how many times it can be subtracted from the value; the count is the
|
|
883
|
+
digit, and what is left after 10 is the units. A 16-bit value needs four
|
|
884
|
+
powers, an 8-bit value two (100 and 10). The cost is proportional to the
|
|
885
|
+
digit sum: 65535 takes 19 subtractions, 59999 takes 32 (the 16-bit worst
|
|
886
|
+
case), 10000 takes one.
|
|
887
|
+
|
|
888
|
+
**Double-dabble.** Shift the value out from the top bit into a BCD
|
|
889
|
+
accumulator, doubling the accumulator each time. Before each doubling,
|
|
890
|
+
any BCD nibble of 5 or more has 3 added so the doubling carries a ten out
|
|
891
|
+
of it. On the 6502 the whole adjust step disappears: in decimal mode
|
|
892
|
+
(`SED`) `adc` of a byte to itself doubles a packed BCD pair correctly.
|
|
893
|
+
Sixteen passes of `asl / rol` on the value and three `adc` on the
|
|
894
|
+
accumulator give five BCD digits in a fixed time, with no tables. C has
|
|
895
|
+
no decimal mode, so a C double-dabble has to test every nibble and is the
|
|
896
|
+
slowest route measured below.
|
|
897
|
+
|
|
898
|
+
**Zero suppression and right alignment.** Print into a field of fixed
|
|
899
|
+
width. Keep a `lead` flag set until the first non-zero digit; while it is
|
|
900
|
+
set, a zero digit writes `$20` instead of `$30`. The last digit always
|
|
901
|
+
prints, so 0 shows as `0`. Because the field is fixed and the leading
|
|
902
|
+
cells are spaces, the value is right-aligned with no second pass. An
|
|
903
|
+
arcade score with leading zeros is the same routine without the `lead`
|
|
904
|
+
test.
|
|
905
|
+
|
|
906
|
+
**Hexadecimal for debugging.** High nibble by four `lsr`, low nibble by
|
|
907
|
+
`and #$0F`, each through a 16-entry screen-code table
|
|
908
|
+
(`$30`-`$39`, `$01`-`$06`). Two cells per byte, 74 cycles from C
|
|
909
|
+
(measured below), and it never lies about the byte the way a decimal
|
|
910
|
+
routine with a bug can.
|
|
911
|
+
|
|
912
|
+
**Measured cost of each route.** VICE x64sc 3.10, CIA1 timer A
|
|
913
|
+
force-loaded from `$FFFF`, display blanked and the timing started at the
|
|
914
|
+
next frame so no badline steals cycles, empty call subtracted: the
|
|
915
|
+
figures are the body of the routine without `JSR` and `RTS` (rung 1).
|
|
916
|
+
PAL and NTSC gave the same figures. The C listings are in
|
|
917
|
+
`recipes/oscar64/print-number.md` and both KickAssembler routines are
|
|
918
|
+
below; the same source assembled in a harness with the timer gave these
|
|
919
|
+
numbers.
|
|
920
|
+
|
|
921
|
+
| Route | Value | Cycles |
|
|
922
|
+
|---|---|---|
|
|
923
|
+
| KickAssembler subtract-powers (listing below) | 65535 | 879 |
|
|
924
|
+
| KickAssembler subtract-powers | 59999, worst case | 1,243 |
|
|
925
|
+
| KickAssembler double-dabble, decimal mode (listing below) | 65535 and 59999 | 875 |
|
|
926
|
+
| Oscar64 C subtract-powers, `fmt_dec_sub` | 65535 | 957 |
|
|
927
|
+
| Oscar64 C subtract-powers, `fmt_dec_sub` | 59999, worst case | 1,361 |
|
|
928
|
+
| Oscar64 C double-dabble, nibble adjust, `fmt_dec_dab` | 65535 | 2,537 |
|
|
929
|
+
| Oscar64 C hex byte, `fmt_hex8` | $FF | 74 |
|
|
930
|
+
|
|
931
|
+
The last few cycles depend on code placement: the same double-dabble
|
|
932
|
+
read 860 in an earlier build where its inner loop did not cross a page,
|
|
933
|
+
and 875 after a 12-byte insertion moved it (fifteen taken branches at
|
|
934
|
+
one extra cycle each, rung 3). A first version of the harness that
|
|
935
|
+
blanked the display and timed at once read 9 per cent higher on NTSC,
|
|
936
|
+
because the VIC-II samples the DEN bit once per frame, on line `$30`
|
|
937
|
+
(VIC-II documentation, not measured here; what was measured is that the
|
|
938
|
+
wait closes the 9 per cent NTSC gap). Both decimal routes were checked
|
|
939
|
+
against Python over
|
|
940
|
+
all 65,536 values (the `PASS` lines in the recipe).
|
|
941
|
+
|
|
942
|
+
**Which to use where.**
|
|
943
|
+
|
|
944
|
+
- Score kept in BCD (the routines above): no conversion at all. Unpack
|
|
945
|
+
nibbles, add `$30`, write. Cheapest by far.
|
|
946
|
+
- 16-bit binary in assembly: double-dabble with `SED` (`dab_u16` below).
|
|
947
|
+
Fixed 875 cycles, no tables, 106 bytes with the unpack (the
|
|
948
|
+
subtract-powers routine below is 90 with its tables; both from the
|
|
949
|
+
assembler's symbol file, rung 1).
|
|
950
|
+
Subtract-powers wins only when
|
|
951
|
+
values are usually small, since its cost falls with the digit sum
|
|
952
|
+
(rung 3, not measured for small values here).
|
|
953
|
+
- 16-bit binary from Oscar64 C: subtract-powers. The C double-dabble is
|
|
954
|
+
2.6 times slower. If the fixed cost matters, call the assembly
|
|
955
|
+
double-dabble through `__asm`.
|
|
956
|
+
- 8-bit binary: subtract-powers with two powers, or a 256-entry table of
|
|
957
|
+
packed BCD if the 256 bytes are spare (not measured here).
|
|
958
|
+
- Budget: 1,000 cycles is about 16 PAL raster lines (63 cycles per line,
|
|
959
|
+
rung 3). Fine once per frame in VBlank for one or two fields, and fine
|
|
960
|
+
on a score-change event. Do not convert inside a raster IRQ with a
|
|
961
|
+
deadline, and do not convert per actor per frame.
|
|
962
|
+
|
|
963
|
+
**KickAssembler routine.** `num` holds the value and is destroyed; `dst`
|
|
964
|
+
is a zero-page pointer to the first of five cells. The demo below prints
|
|
965
|
+
65535 at the top left of the screen and returns to BASIC. This is the
|
|
966
|
+
exact text that measured 879 and 1,243 cycles above.
|
|
967
|
+
|
|
968
|
+
```asm
|
|
969
|
+
// print_u16.asm -- 16-bit binary to five screen codes, right-aligned,
|
|
970
|
+
// leading zeros as spaces, written straight to screen RAM.
|
|
971
|
+
BasicUpstart2(start)
|
|
972
|
+
|
|
973
|
+
.label dst = $fb // zero page pointer to the 5-cell field
|
|
974
|
+
.label num = $fd // 16-bit value, destroyed by the routine
|
|
975
|
+
.label digit = $02
|
|
976
|
+
.label lead = $03
|
|
977
|
+
|
|
978
|
+
* = $0810
|
|
979
|
+
start:
|
|
980
|
+
lda #<65535
|
|
981
|
+
sta num
|
|
982
|
+
lda #>65535
|
|
983
|
+
sta num+1
|
|
984
|
+
lda #<$0400 // top left of the default screen
|
|
985
|
+
sta dst
|
|
986
|
+
lda #>$0400
|
|
987
|
+
sta dst+1
|
|
988
|
+
jsr print_u16
|
|
989
|
+
rts
|
|
990
|
+
|
|
991
|
+
// --- subtract-powers ------------------------------------------------------
|
|
992
|
+
// In: num (16-bit, destroyed), dst -> 5 cells. Out: value right-aligned,
|
|
993
|
+
// leading zeros as spaces. Uses digit, lead, A, X, Y.
|
|
994
|
+
print_u16:
|
|
995
|
+
lda #$01
|
|
996
|
+
sta lead
|
|
997
|
+
ldy #$00
|
|
998
|
+
ldx #$00
|
|
999
|
+
pu_loop:
|
|
1000
|
+
lda #$2f // one below '0': the first pass always increments
|
|
1001
|
+
sta digit
|
|
1002
|
+
sec
|
|
1003
|
+
pu_sub:
|
|
1004
|
+
inc digit
|
|
1005
|
+
lda num
|
|
1006
|
+
sbc pow_lo,x
|
|
1007
|
+
sta num
|
|
1008
|
+
lda num+1
|
|
1009
|
+
sbc pow_hi,x
|
|
1010
|
+
sta num+1
|
|
1011
|
+
bcs pu_sub // still non-negative: subtract again
|
|
1012
|
+
lda num // overshot by one power: add it back (C is clear)
|
|
1013
|
+
adc pow_lo,x
|
|
1014
|
+
sta num
|
|
1015
|
+
lda num+1
|
|
1016
|
+
adc pow_hi,x
|
|
1017
|
+
sta num+1
|
|
1018
|
+
pu_done:
|
|
1019
|
+
lda digit
|
|
1020
|
+
cmp #$30
|
|
1021
|
+
bne pu_emit // a real digit
|
|
1022
|
+
lda lead
|
|
1023
|
+
beq pu_zero // a zero after a real digit
|
|
1024
|
+
lda #$20 // a leading zero: space
|
|
1025
|
+
sta (dst),y
|
|
1026
|
+
bne pu_next // always
|
|
1027
|
+
pu_zero:
|
|
1028
|
+
lda #$30
|
|
1029
|
+
pu_emit:
|
|
1030
|
+
sta (dst),y
|
|
1031
|
+
lda #$00
|
|
1032
|
+
sta lead
|
|
1033
|
+
pu_next:
|
|
1034
|
+
iny
|
|
1035
|
+
inx
|
|
1036
|
+
cpx #$04
|
|
1037
|
+
bne pu_loop
|
|
1038
|
+
lda num // 0..9 remains
|
|
1039
|
+
ora #$30
|
|
1040
|
+
sta (dst),y
|
|
1041
|
+
rts
|
|
1042
|
+
|
|
1043
|
+
pow_lo: .byte <10000, <1000, <100, <10
|
|
1044
|
+
pow_hi: .byte >10000, >1000, >100, >10
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
**KickAssembler double-dabble.** The same `num` and `dst`, plus three
|
|
1048
|
+
zero-page bytes at `bcd` for the packed accumulator. The demo prints
|
|
1049
|
+
65535 at the top left and returns to BASIC (run in VICE, rung 1). This
|
|
1050
|
+
is the exact text that measured 875 cycles above, and the `db_loop`
|
|
1051
|
+
whose page crossing moved the figure from 860.
|
|
1052
|
+
|
|
1053
|
+
```asm
|
|
1054
|
+
// dab_u16.asm -- 16-bit binary to five screen codes by double-dabble in
|
|
1055
|
+
// decimal mode, right-aligned, leading zeros as spaces, written straight
|
|
1056
|
+
// to screen RAM. This is the text that measured 875 cycles.
|
|
1057
|
+
BasicUpstart2(start)
|
|
1058
|
+
|
|
1059
|
+
.label dst = $fb // zero page pointer to the 5-cell field
|
|
1060
|
+
.label num = $fd // 16-bit value, destroyed by the routine
|
|
1061
|
+
.label lead = $03
|
|
1062
|
+
.label bcd = $04 // 3 bytes packed BCD
|
|
1063
|
+
|
|
1064
|
+
* = $0810
|
|
1065
|
+
start:
|
|
1066
|
+
lda #<65535
|
|
1067
|
+
sta num
|
|
1068
|
+
lda #>65535
|
|
1069
|
+
sta num+1
|
|
1070
|
+
lda #<$0400 // top left of the default screen
|
|
1071
|
+
sta dst
|
|
1072
|
+
lda #>$0400
|
|
1073
|
+
sta dst+1
|
|
1074
|
+
jsr dab_u16
|
|
1075
|
+
rts
|
|
1076
|
+
|
|
1077
|
+
// --- double-dabble with decimal mode -------------------------------------
|
|
1078
|
+
// In: num (16-bit, destroyed), dst -> 5 cells. Doubles a 3-byte packed BCD
|
|
1079
|
+
// accumulator in decimal mode 16 times, shifting one bit of num in each
|
|
1080
|
+
// time, then unpacks with the same zero suppression.
|
|
1081
|
+
dab_u16:
|
|
1082
|
+
lda #$00
|
|
1083
|
+
sta bcd
|
|
1084
|
+
sta bcd+1
|
|
1085
|
+
sta bcd+2
|
|
1086
|
+
ldx #$10
|
|
1087
|
+
sed
|
|
1088
|
+
db_loop:
|
|
1089
|
+
asl num
|
|
1090
|
+
rol num+1 // C = next bit, MSB first
|
|
1091
|
+
lda bcd
|
|
1092
|
+
adc bcd
|
|
1093
|
+
sta bcd
|
|
1094
|
+
lda bcd+1
|
|
1095
|
+
adc bcd+1
|
|
1096
|
+
sta bcd+1
|
|
1097
|
+
lda bcd+2
|
|
1098
|
+
adc bcd+2
|
|
1099
|
+
sta bcd+2
|
|
1100
|
+
dex
|
|
1101
|
+
bne db_loop
|
|
1102
|
+
cld
|
|
1103
|
+
ldy #$00
|
|
1104
|
+
lda #$01
|
|
1105
|
+
sta lead
|
|
1106
|
+
lda bcd+2
|
|
1107
|
+
and #$0f
|
|
1108
|
+
jsr db_digit
|
|
1109
|
+
lda bcd+1
|
|
1110
|
+
lsr
|
|
1111
|
+
lsr
|
|
1112
|
+
lsr
|
|
1113
|
+
lsr
|
|
1114
|
+
jsr db_digit
|
|
1115
|
+
lda bcd+1
|
|
1116
|
+
and #$0f
|
|
1117
|
+
jsr db_digit
|
|
1118
|
+
lda bcd
|
|
1119
|
+
lsr
|
|
1120
|
+
lsr
|
|
1121
|
+
lsr
|
|
1122
|
+
lsr
|
|
1123
|
+
jsr db_digit
|
|
1124
|
+
lda bcd
|
|
1125
|
+
and #$0f
|
|
1126
|
+
ora #$30
|
|
1127
|
+
sta (dst),y
|
|
1128
|
+
rts
|
|
1129
|
+
db_digit: // A = 0..9
|
|
1130
|
+
bne db_nz
|
|
1131
|
+
ldx lead
|
|
1132
|
+
beq db_nz // zero after a real digit prints
|
|
1133
|
+
lda #$20
|
|
1134
|
+
sta (dst),y
|
|
1135
|
+
iny
|
|
1136
|
+
rts
|
|
1137
|
+
db_nz:
|
|
1138
|
+
ora #$30
|
|
1139
|
+
sta (dst),y
|
|
1140
|
+
iny
|
|
1141
|
+
lda #$00
|
|
1142
|
+
sta lead
|
|
1143
|
+
rts
|
|
1144
|
+
```
|
|
1145
|
+
|
|
1146
|
+
**Oscar64 routine.** `fmt_dec_sub` from `recipes/oscar64/print-number.md`,
|
|
1147
|
+
which measured 957 and 1,361 cycles above. The recipe page carries the
|
|
1148
|
+
whole program, the exhaustive check and the timer harness; this is the
|
|
1149
|
+
routine on its own (not built by the gate on this page; it is the text
|
|
1150
|
+
the recipe built).
|
|
1151
|
+
|
|
1152
|
+
```c
|
|
1153
|
+
// Subtract-powers. Writes five screen codes to dst: the value right-aligned,
|
|
1154
|
+
// leading zeros as spaces, so 0 becomes " 0" and 65535 fills the field.
|
|
1155
|
+
__noinline void fmt_dec_sub(unsigned v, char *dst)
|
|
1156
|
+
{
|
|
1157
|
+
static const unsigned pow10[4] = { 10000, 1000, 100, 10 };
|
|
1158
|
+
char lead = 1;
|
|
1159
|
+
for (char i = 0; i < 4; i++)
|
|
1160
|
+
{
|
|
1161
|
+
unsigned p = pow10[i];
|
|
1162
|
+
char d = 0x30;
|
|
1163
|
+
while (v >= p) { v -= p; d++; }
|
|
1164
|
+
if (d != 0x30 || !lead) { dst[i] = d; lead = 0; }
|
|
1165
|
+
else dst[i] = 0x20;
|
|
1166
|
+
}
|
|
1167
|
+
dst[4] = 0x30 + v; // v is 0..9 here
|
|
1168
|
+
}
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
Oscar64 -O2 keeps `v` in zero page and the digit count in a zero-page
|
|
1172
|
+
temporary, and the inner loop is a compare-and-subtract of about 32
|
|
1173
|
+
cycles per step (read from the compiler's `.asm` output, rung 1 for the
|
|
1174
|
+
shape, rung 3 for the per-step figure). `__noinline` is documented in the
|
|
1175
|
+
Oscar64 README; without it the compiler may inline the call at -O2.
|
|
1176
|
+
|
|
1177
|
+
---
|
|
1178
|
+
|
|
1179
|
+
## Cross-references
|
|
1180
|
+
|
|
1181
|
+
- `../techniques/sprite.md` — sprite multiplex mechanics, hardware sprite
|
|
1182
|
+
DMA, collision register details, sprite-background priority.
|
|
1183
|
+
- `../techniques/scroll.md` — soft-scroll register usage, column copy
|
|
1184
|
+
timing, parallax layer technique.
|
|
1185
|
+
- `./c64-game-archetypes.md` — which archetype (shmup, platformer,
|
|
1186
|
+
adventure, maze, racer) pairs with which patterns from this document,
|
|
1187
|
+
with recommended default pattern combinations per archetype.
|
|
1188
|
+
- `./game-structure.md`: the state layer above these patterns (game state machine, level end conditions, level transition, front end and attract) with per-pattern checks a harness can run.
|
|
1189
|
+
- `./enemy-behaviour-and-difficulty.md`: the design layer above the actor state machines in this document (Öörni's interaction patterns, enemy personality by target, attack pattern tables, the difficulty ramp and a playtest protocol), each with checks a harness can run.
|