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,1028 @@
|
|
|
1
|
+
---
|
|
2
|
+
recipe: lane-pursuit
|
|
3
|
+
toolchain: oscar64
|
|
4
|
+
output_format: PRG
|
|
5
|
+
region: both
|
|
6
|
+
techniques: [lane_pursuit_ai]
|
|
7
|
+
file_formats: [PRG]
|
|
8
|
+
uses_registers: [D000, D001, D002, D003, D004, D005, D006, D007, D008, D009, D010, D011, D015, D020, D021, D022, D023, D027, D028, D029, D02A, D02B, DC06, DC07, DC0F]
|
|
9
|
+
uses_kernal: []
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<!-- doc-type: recipe -->
|
|
13
|
+
|
|
14
|
+
# Oscar64 Lane Pursuit: road cars that pull alongside, ram and avoid the edges, checked against a model
|
|
15
|
+
|
|
16
|
+
## Synopsis
|
|
17
|
+
|
|
18
|
+
A road-game pursuit AI on an original scrolling road. The road narrows
|
|
19
|
+
from the left, widens again, and is then split in two by a water island,
|
|
20
|
+
the fork-like obstacle. The player car (yellow, sprite 0) drives on
|
|
21
|
+
autopilot, weaving, and takes the left channel at the island. Four
|
|
22
|
+
pursuit cars (sprites 1 to 4) enter from behind and ahead on a spawn
|
|
23
|
+
table and run one state machine: approach a slot beside the player, hold
|
|
24
|
+
it, ram with a lead on the player's sideways velocity, back off, and
|
|
25
|
+
leave when shot. Each car steers with a capped proportional rule in 8.8
|
|
26
|
+
fixed point, keeps a spacing box from the other pursuers, and probes the
|
|
27
|
+
map ahead of its nose so it never drives onto the verge or the water.
|
|
28
|
+
A "frame" on this page is one logic step. Pass 1 runs 780 of them on
|
|
29
|
+
screen, one every second video frame (measured below), and folds every
|
|
30
|
+
car's state and x into a checksum each step; every 78 steps it is
|
|
31
|
+
compared with a Python model of the same rules, compiled in. Then, with
|
|
32
|
+
the display off, pass 2 reruns the 780 steps under CIA1 timer B, and a
|
|
33
|
+
built worst frame is timed.
|
|
34
|
+
`$02FF` = `01` and a green border on pass, `02` and red on fail. This is
|
|
35
|
+
`lane_pursuit_ai` in `docs/techniques/logic.md`; the result-byte contract
|
|
36
|
+
is `headless-verify.md`.
|
|
37
|
+
|
|
38
|
+
## Source
|
|
39
|
+
|
|
40
|
+
```c
|
|
41
|
+
// lane-pursuit.c
|
|
42
|
+
//
|
|
43
|
+
// Lane-pursuit AI for a vertical road game. A "frame" below is one logic
|
|
44
|
+
// step. A road scrolls down the screen at 2 pixels a step: a wide
|
|
45
|
+
// stretch, a narrowing from the left, and a water island that splits the
|
|
46
|
+
// road in two. The player car (sprite 0) is on autopilot: it steers
|
|
47
|
+
// toward the centre of the leftmost road run 32 pixels ahead, plus a
|
|
48
|
+
// triangle weave. Four pursuit cars (sprites 1 to 4)
|
|
49
|
+
// enter from behind or ahead on a spawn table and run one state machine:
|
|
50
|
+
// APPROACH to a slot 24 pixels beside the player (one car per side),
|
|
51
|
+
// ALONG for 40 frames, RAM toward the player's position led by 8 frames of
|
|
52
|
+
// its lateral velocity, BACK off 40 pixels behind, and LEAVE when shot
|
|
53
|
+
// (car 3 at frame 330), freeing the slot 150 pixels behind. Steering is a
|
|
54
|
+
// proportional rule in 8.8 with a speed cap and an acceleration cap;
|
|
55
|
+
// forward speed is held relative to the player's the same way. Each car
|
|
56
|
+
// keeps a spacing box from the other pursuers, probes the map row 32
|
|
57
|
+
// pixels ahead of its nose across the span it will sweep, and when that is
|
|
58
|
+
// blocked steers 32 pixels toward the side with more road; otherwise a side
|
|
59
|
+
// probe 12 pixels beyond its body stops it steering into an edge.
|
|
60
|
+
// Pass 1 runs 780 frames on screen, one every second video frame because a
|
|
61
|
+
// step and its redraw overrun one; each frame every car's state and x are
|
|
62
|
+
// folded into a checksum, checked every 78 frames against a Python model of
|
|
63
|
+
// the same rules compiled in below. Then, with the display and sprites
|
|
64
|
+
// off, pass 2 reruns the 780 frames under CIA1 timer B, and a built worst
|
|
65
|
+
// frame is timed.
|
|
66
|
+
// Row 0: frame, checksum, checkpoints matched, result, leaves. Row 1:
|
|
67
|
+
// contacts, rams, avoid frames, hold frames, spacing frames. Row 23: CIA1
|
|
68
|
+
// cycles per car update, mean and worst of pass 2, built worst car, the
|
|
69
|
+
// edge probes on their own. Row 24: worst frame of pass 2, built worst
|
|
70
|
+
// frame, empty timer pair.
|
|
71
|
+
// $02FF = 01 and a green border on pass, 02 and red on fail.
|
|
72
|
+
// Build: oscar64 -tm=c64 -O2 -o=lane-pursuit.prg lane-pursuit.c
|
|
73
|
+
|
|
74
|
+
#include <c64/vic.h>
|
|
75
|
+
#include <c64/cia.h>
|
|
76
|
+
#include <string.h>
|
|
77
|
+
|
|
78
|
+
#define SCREEN ((char *)0x0400)
|
|
79
|
+
#define COLOUR ((char *)0xd800)
|
|
80
|
+
#define SPRPTR ((char *)0x07f8)
|
|
81
|
+
#define SPRDATA ((char *)0x0340) // blocks 13 and 14, cassette buffer
|
|
82
|
+
#define RESULT (*(volatile char *)0x02ff)
|
|
83
|
+
#define CODE_PASS 0x01
|
|
84
|
+
#define CODE_FAIL 0x02
|
|
85
|
+
|
|
86
|
+
#define NCAR 4
|
|
87
|
+
#define FRAMES 780
|
|
88
|
+
#define CKSTEP 78
|
|
89
|
+
#define NCK 10
|
|
90
|
+
#define MW 32 // map tiles across
|
|
91
|
+
#define MH 64 // map rows per loop
|
|
92
|
+
#define ROAD 0x20 // ECM: space on background 0
|
|
93
|
+
#define VERGE 0x60 // space on background 1
|
|
94
|
+
#define WATER 0xa0 // space on background 2
|
|
95
|
+
#define PSPD 32 // player speed, 1/16 px a frame
|
|
96
|
+
#define CW 12 // car body, pixels
|
|
97
|
+
#define CH 16
|
|
98
|
+
#define LOOK 32 // look-ahead beyond the nose, px
|
|
99
|
+
#define MARGIN 12 // side probe beyond the body, px
|
|
100
|
+
#define SEP_X 20 // spacing box between pursuers, px
|
|
101
|
+
#define SEP_Y 32
|
|
102
|
+
#define SIDE_DX 24
|
|
103
|
+
#define BACK_DX 40
|
|
104
|
+
#define BACK_DY 40
|
|
105
|
+
#define LEAD 8 // frames of player velocity led
|
|
106
|
+
#define T_ALONG 40
|
|
107
|
+
#define T_RAM 24
|
|
108
|
+
#define T_BACK 50
|
|
109
|
+
#define VAVOID 512 // lateral caps while avoiding, 8.8
|
|
110
|
+
#define AAVOID 128
|
|
111
|
+
#define RSMAX 24 // relative forward caps, 1/16 px
|
|
112
|
+
#define RSLEAVE 48
|
|
113
|
+
#define FACC 2
|
|
114
|
+
#define NONE 0xff
|
|
115
|
+
|
|
116
|
+
#define APPROACH 1
|
|
117
|
+
#define ALONG 2
|
|
118
|
+
#define RAM 3
|
|
119
|
+
#define BACK 4
|
|
120
|
+
#define LEAVE 5
|
|
121
|
+
|
|
122
|
+
#define COL0 4 // playfield: text columns 4 to 35
|
|
123
|
+
#define PROW 14 // text row the player's tile is on
|
|
124
|
+
|
|
125
|
+
// The Python model's checksum every 78 frames and its event counts.
|
|
126
|
+
const unsigned exp_ck[NCK] = {
|
|
127
|
+
0xf84d, 0x45ab, 0x6a75, 0xd437, 0x8f47, 0xebe2, 0x37fc, 0x8460, 0xd465, 0xba59
|
|
128
|
+
};
|
|
129
|
+
#define EXP_CONTACTS 3
|
|
130
|
+
#define EXP_RAMS 7
|
|
131
|
+
#define EXP_AVOIDS 60
|
|
132
|
+
#define EXP_HOLDS 318
|
|
133
|
+
#define EXP_SEPS 865
|
|
134
|
+
#define EXP_LEAVES 1
|
|
135
|
+
|
|
136
|
+
const int vmax_t[6] = {0, 256, 256, 768, 256, 256}; // lateral speed cap
|
|
137
|
+
const int acc_t[6] = {0, 32, 32, 96, 32, 32}; // lateral accel cap
|
|
138
|
+
|
|
139
|
+
// Spawn table: frame, slot, x, offset in px (+ ahead, - behind), shot frame.
|
|
140
|
+
#define NSPAWN 5
|
|
141
|
+
const unsigned sp_f[NSPAWN] = {8, 8, 40, 90, 420};
|
|
142
|
+
const char sp_k[NSPAWN] = {0, 1, 2, 3, 3};
|
|
143
|
+
const char sp_x[NSPAWN] = {56, 184, 176, 64, 176};
|
|
144
|
+
const signed char sp_dy[NSPAWN] = {-72, 104, -72, 104, -72};
|
|
145
|
+
const unsigned sp_shot[NSPAWN] = {0, 0, 0, 330, 0};
|
|
146
|
+
|
|
147
|
+
char map[MH][MW];
|
|
148
|
+
char cen[MH]; // centre of leftmost road run, px
|
|
149
|
+
|
|
150
|
+
// Player and cars. y is world position in 1/16 px (up is +), x is 8.8.
|
|
151
|
+
unsigned P;
|
|
152
|
+
int ppx;
|
|
153
|
+
signed char pvx;
|
|
154
|
+
char st[NCAR], tm[NCAR], owner[2];
|
|
155
|
+
signed char side[NCAR], rs[NCAR];
|
|
156
|
+
unsigned x[NCAR], y[NCAR], shot[NCAR];
|
|
157
|
+
int vx[NCAR];
|
|
158
|
+
char sp;
|
|
159
|
+
unsigned contacts, rams, avoids, holds, seps, leaves;
|
|
160
|
+
|
|
161
|
+
static inline int iabs(int v) { return v < 0 ? -v : v; }
|
|
162
|
+
static inline int clampi(int v, int a, int b) { return v < a ? a : (v > b ? b : v); }
|
|
163
|
+
static inline int sm(int e, char s) { return e >= 0 ? e >> s : -((-e) >> s); }
|
|
164
|
+
static inline char tile(unsigned y16, int px) { return map[(y16 >> 7) & 63][(px >> 3) & 31]; }
|
|
165
|
+
|
|
166
|
+
void build_map(void)
|
|
167
|
+
{
|
|
168
|
+
for (char r = 0; r < MH; r++) {
|
|
169
|
+
char L = 6, R = 26, i0 = 0, i1 = 0;
|
|
170
|
+
if (r >= 16 && r <= 23) L = 6 + (r - 15);
|
|
171
|
+
else if (r >= 24 && r <= 31) L = 14;
|
|
172
|
+
else if (r >= 32 && r <= 35) L = 14 - 2 * (r - 31);
|
|
173
|
+
if (r >= 38 && r <= 49) { i0 = 14; i1 = 18; }
|
|
174
|
+
for (char c = 0; c < MW; c++) {
|
|
175
|
+
char t = ROAD;
|
|
176
|
+
if (c < L || c >= R) t = VERGE;
|
|
177
|
+
else if (c >= i0 && c < i1) t = WATER;
|
|
178
|
+
map[r][c] = t;
|
|
179
|
+
}
|
|
180
|
+
char e = L;
|
|
181
|
+
while (e < MW && map[r][e] == ROAD)
|
|
182
|
+
e++;
|
|
183
|
+
cen[r] = (L + e) * 4;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
void init(void)
|
|
188
|
+
{
|
|
189
|
+
P = 1024 * 16;
|
|
190
|
+
ppx = cen[(P >> 7) & 63] - 6;
|
|
191
|
+
pvx = 0;
|
|
192
|
+
for (char i = 0; i < NCAR; i++) {
|
|
193
|
+
st[i] = 0; x[i] = 0; vx[i] = 0; y[i] = 0; rs[i] = 0;
|
|
194
|
+
side[i] = 1; tm[i] = 0; shot[i] = 0;
|
|
195
|
+
}
|
|
196
|
+
contacts = rams = avoids = holds = seps = leaves = 0;
|
|
197
|
+
sp = 0;
|
|
198
|
+
owner[0] = owner[1] = NONE;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
static inline int tri(unsigned f)
|
|
202
|
+
{
|
|
203
|
+
char t = f & 63;
|
|
204
|
+
return t < 32 ? (int)t - 16 : 48 - (int)t;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// The autopilot stands in for the joystick; it is not the technique.
|
|
208
|
+
void player_step(unsigned f)
|
|
209
|
+
{
|
|
210
|
+
P += PSPD;
|
|
211
|
+
int tp = cen[((P + 32 * 16) >> 7) & 63] + tri(f) - 6;
|
|
212
|
+
pvx = clampi(tp - ppx, -2, 2);
|
|
213
|
+
ppx += pvx;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
void spawn(unsigned f)
|
|
217
|
+
{
|
|
218
|
+
while (sp < NSPAWN && sp_f[sp] == f) {
|
|
219
|
+
char k = sp_k[sp];
|
|
220
|
+
st[k] = APPROACH;
|
|
221
|
+
x[k] = (unsigned)sp_x[sp] << 8;
|
|
222
|
+
vx[k] = 0;
|
|
223
|
+
y[k] = P + sp_dy[sp] * 16;
|
|
224
|
+
rs[k] = 0;
|
|
225
|
+
shot[k] = sp_shot[sp];
|
|
226
|
+
sp++;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// True when any tile from pixel a to pixel b on the row of y16 is not road,
|
|
231
|
+
// probing every 8 pixels and at b, so no tile between is skipped.
|
|
232
|
+
bool blocked(unsigned y16, int a, int b)
|
|
233
|
+
{
|
|
234
|
+
const char *row = map[(y16 >> 7) & 63];
|
|
235
|
+
while (a < b) {
|
|
236
|
+
if (row[(a >> 3) & 31] != ROAD)
|
|
237
|
+
return true;
|
|
238
|
+
a += 8;
|
|
239
|
+
}
|
|
240
|
+
return row[(b >> 3) & 31] != ROAD;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
char room(char r, signed char c, signed char d)
|
|
244
|
+
{
|
|
245
|
+
char n = 0;
|
|
246
|
+
for (char i = 1; i <= 8; i++) {
|
|
247
|
+
c += d;
|
|
248
|
+
if (c >= 0 && c < MW && map[r][c] == ROAD)
|
|
249
|
+
n++;
|
|
250
|
+
}
|
|
251
|
+
return n;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
static inline void release(char i)
|
|
255
|
+
{
|
|
256
|
+
if (owner[0] == i) owner[0] = NONE;
|
|
257
|
+
if (owner[1] == i) owner[1] = NONE;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// One pursuit car's update for frame f: state, spacing, edge probes,
|
|
261
|
+
// lateral and forward controllers.
|
|
262
|
+
void car_step(char i, unsigned f)
|
|
263
|
+
{
|
|
264
|
+
char s = st[i];
|
|
265
|
+
if (!s)
|
|
266
|
+
return;
|
|
267
|
+
if (shot[i] == f && s != LEAVE) {
|
|
268
|
+
s = LEAVE;
|
|
269
|
+
leaves++;
|
|
270
|
+
release(i);
|
|
271
|
+
}
|
|
272
|
+
int px = x[i] >> 8;
|
|
273
|
+
int rel = (int)(y[i] - P);
|
|
274
|
+
int dx = px - ppx;
|
|
275
|
+
int tx, rt = 0;
|
|
276
|
+
|
|
277
|
+
switch (s) {
|
|
278
|
+
case APPROACH: {
|
|
279
|
+
char sd = dx >= 0 ? 1 : 0;
|
|
280
|
+
if (owner[sd] != NONE && owner[sd] != i)
|
|
281
|
+
sd ^= 1;
|
|
282
|
+
if (owner[sd] == NONE || owner[sd] == i) {
|
|
283
|
+
owner[sd] = i;
|
|
284
|
+
side[i] = sd ? 1 : -1;
|
|
285
|
+
tx = side[i] > 0 ? ppx + SIDE_DX : ppx - SIDE_DX;
|
|
286
|
+
if (iabs(rel) < 8 * 16 && iabs(px - tx) < 6) {
|
|
287
|
+
s = ALONG;
|
|
288
|
+
tm[i] = T_ALONG;
|
|
289
|
+
}
|
|
290
|
+
} else { // both sides taken: queue behind
|
|
291
|
+
tx = px;
|
|
292
|
+
rt = -BACK_DY * 16;
|
|
293
|
+
}
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
case ALONG:
|
|
297
|
+
tx = side[i] > 0 ? ppx + SIDE_DX : ppx - SIDE_DX;
|
|
298
|
+
if (--tm[i] == 0) {
|
|
299
|
+
s = RAM;
|
|
300
|
+
tm[i] = T_RAM;
|
|
301
|
+
rams++;
|
|
302
|
+
}
|
|
303
|
+
break;
|
|
304
|
+
case RAM:
|
|
305
|
+
tx = ppx + (pvx << 3); // LEAD = 8 frames
|
|
306
|
+
tm[i]--;
|
|
307
|
+
if (iabs(dx) < 14 && iabs(rel) < 16 * 16) {
|
|
308
|
+
contacts++;
|
|
309
|
+
s = BACK;
|
|
310
|
+
tm[i] = T_BACK;
|
|
311
|
+
} else if (tm[i] == 0) {
|
|
312
|
+
s = BACK;
|
|
313
|
+
tm[i] = T_BACK;
|
|
314
|
+
}
|
|
315
|
+
if (s == BACK)
|
|
316
|
+
release(i);
|
|
317
|
+
break;
|
|
318
|
+
case BACK:
|
|
319
|
+
tx = side[i] > 0 ? ppx + BACK_DX : ppx - BACK_DX;
|
|
320
|
+
rt = -BACK_DY * 16;
|
|
321
|
+
if (--tm[i] == 0)
|
|
322
|
+
s = APPROACH;
|
|
323
|
+
break;
|
|
324
|
+
default:
|
|
325
|
+
tx = px;
|
|
326
|
+
rt = -200 * 16;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Spacing: steer and hold back from the first pursuer too close.
|
|
330
|
+
for (char j = 0; j < NCAR; j++) {
|
|
331
|
+
if (j == i || !st[j])
|
|
332
|
+
continue;
|
|
333
|
+
int ddx = (int)(x[j] >> 8) - px;
|
|
334
|
+
int ddy = (int)(y[j] - y[i]);
|
|
335
|
+
if (iabs(ddx) < SEP_X && iabs(ddy) < SEP_Y * 16) {
|
|
336
|
+
tx = (ddx > 0 || (ddx == 0 && i < j)) ? px - SEP_X : px + SEP_X;
|
|
337
|
+
if (ddy > 0)
|
|
338
|
+
rt = rel + ddy - SEP_Y * 16;
|
|
339
|
+
seps++;
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Edges: probe the row LOOK pixels past the nose across the swept span.
|
|
345
|
+
int vmax = vmax_t[s], acc = acc_t[s];
|
|
346
|
+
unsigned ahead = y[i] + (CH - 1 + LOOK) * 16;
|
|
347
|
+
int qx = clampi(px + sm(vx[i], 4), 0, 255 - CW);
|
|
348
|
+
int a = px < qx ? px : qx, b = (px > qx ? px : qx) + CW - 1;
|
|
349
|
+
if (blocked(ahead, a, b)) {
|
|
350
|
+
char r = (ahead >> 7) & 63;
|
|
351
|
+
signed char c = (qx + 6) >> 3;
|
|
352
|
+
tx = room(r, c, -1) > room(r, c, 1) ? px - 32 : px + 32;
|
|
353
|
+
vmax = VAVOID;
|
|
354
|
+
acc = AAVOID;
|
|
355
|
+
avoids++;
|
|
356
|
+
} else { // side probe toward the target
|
|
357
|
+
int sx = px;
|
|
358
|
+
if (tx > px) sx = px + CW - 1 + MARGIN;
|
|
359
|
+
else if (tx < px) sx = px - MARGIN;
|
|
360
|
+
if (sx >= 0 && sx <= 255 &&
|
|
361
|
+
(tile(y[i] + (CH - 1 + 8) * 16, sx) != ROAD || tile(y[i], sx) != ROAD)) {
|
|
362
|
+
tx = px;
|
|
363
|
+
acc = AAVOID;
|
|
364
|
+
holds++;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
tx = clampi(tx, 8, 236);
|
|
368
|
+
|
|
369
|
+
// Lateral: proportional, speed cap, acceleration cap, all 8.8.
|
|
370
|
+
int want = clampi((tx - px) << 4, -vmax, vmax);
|
|
371
|
+
int v = vx[i];
|
|
372
|
+
if (want > v + acc) v += acc;
|
|
373
|
+
else if (want < v - acc) v -= acc;
|
|
374
|
+
else v = want;
|
|
375
|
+
vx[i] = v;
|
|
376
|
+
x[i] += v;
|
|
377
|
+
|
|
378
|
+
// Forward: speed relative to the player's, toward the wanted offset.
|
|
379
|
+
int cap = s == LEAVE ? RSLEAVE : RSMAX;
|
|
380
|
+
want = clampi(sm(rt - rel, 3), -cap, cap);
|
|
381
|
+
int r = rs[i];
|
|
382
|
+
if (want > r + FACC) r += FACC;
|
|
383
|
+
else if (want < r - FACC) r -= FACC;
|
|
384
|
+
else r = want;
|
|
385
|
+
rs[i] = r;
|
|
386
|
+
y[i] += PSPD + r;
|
|
387
|
+
if (s == LEAVE && (int)(y[i] - P) < -150 * 16)
|
|
388
|
+
s = 0;
|
|
389
|
+
st[i] = s;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// --- CIA1 timer B ------------------------------------------------------------
|
|
393
|
+
static inline void timer_start(void)
|
|
394
|
+
{
|
|
395
|
+
cia1.crb = 0x00;
|
|
396
|
+
cia1.tb = 0xffff;
|
|
397
|
+
cia1.crb = 0x11;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
static inline unsigned timer_stop(void)
|
|
401
|
+
{
|
|
402
|
+
cia1.crb = 0x00;
|
|
403
|
+
return 0xffff - cia1.tb;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Built worst frame: the table's two frame-8 spawns land, then all four cars
|
|
407
|
+
// are in RAM with the timer running out (car 0 in contact with the player),
|
|
408
|
+
// a full spacing scan, moving right at the RAM speed cap so the ahead probe
|
|
409
|
+
// sweeps 60 pixels and finds the verge only at its last point, and both
|
|
410
|
+
// room scans run. Set-up is outside the timer.
|
|
411
|
+
unsigned worst_frame(void)
|
|
412
|
+
{
|
|
413
|
+
init();
|
|
414
|
+
timer_start();
|
|
415
|
+
spawn(sp_f[0]);
|
|
416
|
+
unsigned t = timer_stop();
|
|
417
|
+
P = 0x4000;
|
|
418
|
+
ppx = 145; pvx = 2;
|
|
419
|
+
for (char i = 0; i < NCAR; i++) {
|
|
420
|
+
st[i] = RAM; tm[i] = 1; side[i] = 1;
|
|
421
|
+
x[i] = 150 << 8; vx[i] = 768; rs[i] = 0;
|
|
422
|
+
y[i] = P + 128 + i * (SEP_Y * 16 + 64);
|
|
423
|
+
shot[i] = 0;
|
|
424
|
+
}
|
|
425
|
+
owner[1] = 0;
|
|
426
|
+
timer_start();
|
|
427
|
+
for (char i = 0; i < NCAR; i++)
|
|
428
|
+
car_step(i, 1);
|
|
429
|
+
return t + timer_stop();
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// --- screen ------------------------------------------------------------------
|
|
433
|
+
void put_str(char *p, const char *s)
|
|
434
|
+
{
|
|
435
|
+
while (*s)
|
|
436
|
+
*p++ = *s++;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
void put_dec(char *p, unsigned v, char digits)
|
|
440
|
+
{
|
|
441
|
+
for (char i = digits; i > 0; i--) {
|
|
442
|
+
p[i - 1] = 0x30 + (char)(v % 10);
|
|
443
|
+
v /= 10;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
void put_hex(char *p, unsigned v)
|
|
448
|
+
{
|
|
449
|
+
for (char i = 4; i > 0; i--) {
|
|
450
|
+
char n = v & 15;
|
|
451
|
+
p[i - 1] = n < 10 ? 0x30 + n : n - 9;
|
|
452
|
+
v >>= 4;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Text row r (2 to 22) shows map row prow + PROW - r.
|
|
457
|
+
void draw_road(void)
|
|
458
|
+
{
|
|
459
|
+
char prow = (P >> 7) & 63;
|
|
460
|
+
for (char r = 2; r <= 22; r++)
|
|
461
|
+
memcpy(SCREEN + r * 40 + COL0, map[(prow + PROW - r) & 63], MW);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// A world y shows at sprite Y 42 + 8 PROW - (y - P floored to a tile), px,
|
|
465
|
+
// so a car's rear row sits on the text row of its tile.
|
|
466
|
+
void place(char n, int px, unsigned wy)
|
|
467
|
+
{
|
|
468
|
+
int d = (int)(((wy - (P & 0xff80)) + 0x4000) >> 4) - 0x400;
|
|
469
|
+
int sy = 42 + 8 * PROW - d;
|
|
470
|
+
unsigned sx = 24 + 8 * COL0 + px;
|
|
471
|
+
char bit = 1 << n;
|
|
472
|
+
if (sy < 50 || sy > 220) {
|
|
473
|
+
vic.spr_enable &= ~bit;
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
vic.spr_pos[n].x = sx;
|
|
477
|
+
vic.spr_pos[n].y = sy;
|
|
478
|
+
if (sx & 0x100) vic.spr_msbx |= bit;
|
|
479
|
+
else vic.spr_msbx &= ~bit;
|
|
480
|
+
vic.spr_enable |= bit;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
void place_all(void)
|
|
484
|
+
{
|
|
485
|
+
place(0, ppx, P);
|
|
486
|
+
for (char i = 0; i < NCAR; i++)
|
|
487
|
+
if (st[i]) place(i + 1, x[i] >> 8, y[i]);
|
|
488
|
+
else vic.spr_enable &= ~(2 << i);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
int main(void)
|
|
492
|
+
{
|
|
493
|
+
__asm { sei }
|
|
494
|
+
vic.color_border = VCOL_BLACK;
|
|
495
|
+
vic.color_back = VCOL_DARK_GREY; // road
|
|
496
|
+
vic.color_back1 = VCOL_GREEN; // verge
|
|
497
|
+
vic.color_back2 = VCOL_BLUE; // water
|
|
498
|
+
vic.ctrl1 |= VIC_CTRL1_ECM;
|
|
499
|
+
memset(SCREEN, ROAD, 1000);
|
|
500
|
+
memset(COLOUR, VCOL_WHITE, 1000);
|
|
501
|
+
build_map();
|
|
502
|
+
|
|
503
|
+
// Sprite shapes: block 13 a 12 x 16 outline (a car), block 14 the same.
|
|
504
|
+
memset(SPRDATA, 0, 128);
|
|
505
|
+
for (char b = 0; b < 2; b++) {
|
|
506
|
+
char *d = SPRDATA + 64 * b;
|
|
507
|
+
d[0] = 0xff; d[1] = 0xf0;
|
|
508
|
+
d[45] = 0xff; d[46] = 0xf0;
|
|
509
|
+
for (char r = 1; r < 15; r++) {
|
|
510
|
+
d[r * 3] = 0x80;
|
|
511
|
+
d[r * 3 + 1] = 0x10;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
SPRPTR[0] = 14;
|
|
515
|
+
for (char s = 1; s <= NCAR; s++)
|
|
516
|
+
SPRPTR[s] = 13;
|
|
517
|
+
vic.spr_color[0] = VCOL_YELLOW;
|
|
518
|
+
vic.spr_color[1] = VCOL_RED;
|
|
519
|
+
vic.spr_color[2] = VCOL_PURPLE;
|
|
520
|
+
vic.spr_color[3] = VCOL_CYAN;
|
|
521
|
+
vic.spr_color[4] = VCOL_ORANGE;
|
|
522
|
+
vic.spr_msbx = 0;
|
|
523
|
+
vic.spr_enable = 0;
|
|
524
|
+
|
|
525
|
+
// Pass 1, on screen: state and x of every car into the checksum each
|
|
526
|
+
// frame, checked every 78 frames.
|
|
527
|
+
init();
|
|
528
|
+
put_str(SCREEN, s"f ck ok r");
|
|
529
|
+
unsigned ck = 0;
|
|
530
|
+
char ok = 0;
|
|
531
|
+
for (unsigned f = 1; f <= FRAMES; f++) {
|
|
532
|
+
vic_waitFrame();
|
|
533
|
+
player_step(f);
|
|
534
|
+
spawn(f);
|
|
535
|
+
for (char i = 0; i < NCAR; i++)
|
|
536
|
+
car_step(i, f);
|
|
537
|
+
for (char i = 0; i < NCAR; i++) { // the per-frame log
|
|
538
|
+
ck = ck * 33 + st[i];
|
|
539
|
+
ck = ck * 33 + (char)(x[i] >> 8);
|
|
540
|
+
}
|
|
541
|
+
if (f % CKSTEP == 0 && ck == exp_ck[f / CKSTEP - 1])
|
|
542
|
+
ok++;
|
|
543
|
+
draw_road();
|
|
544
|
+
place_all();
|
|
545
|
+
put_dec(SCREEN + 2, f, 4);
|
|
546
|
+
put_hex(SCREEN + 10, ck);
|
|
547
|
+
}
|
|
548
|
+
unsigned c1 = contacts, r1 = rams, a1 = avoids, h1 = holds, s1 = seps, l1 = leaves;
|
|
549
|
+
unsigned end_x[NCAR], end_y[NCAR];
|
|
550
|
+
for (char i = 0; i < NCAR; i++) {
|
|
551
|
+
end_x[i] = x[i];
|
|
552
|
+
end_y[i] = y[i];
|
|
553
|
+
}
|
|
554
|
+
int end_p = ppx;
|
|
555
|
+
|
|
556
|
+
// Display off (no badlines) and sprites off (no sprite DMA).
|
|
557
|
+
char en = vic.spr_enable;
|
|
558
|
+
vic.spr_enable = 0;
|
|
559
|
+
vic.ctrl1 &= ~VIC_CTRL1_DEN;
|
|
560
|
+
vic_waitFrame();
|
|
561
|
+
|
|
562
|
+
unsigned t_empty;
|
|
563
|
+
timer_start();
|
|
564
|
+
t_empty = timer_stop();
|
|
565
|
+
|
|
566
|
+
unsigned t_built = worst_frame();
|
|
567
|
+
// One car of the built frame on its own: car 0, in contact, with the
|
|
568
|
+
// others far off.
|
|
569
|
+
init();
|
|
570
|
+
P = 0x4000; ppx = 145; pvx = 2;
|
|
571
|
+
st[0] = RAM; tm[0] = 1; x[0] = 150 << 8; vx[0] = 768;
|
|
572
|
+
y[0] = P + 128;
|
|
573
|
+
for (char i = 1; i < NCAR; i++) { // three distant cars: full scan
|
|
574
|
+
st[i] = BACK; tm[i] = 9; x[i] = 60 << 8; y[i] = P - 64 * 16 * i;
|
|
575
|
+
}
|
|
576
|
+
timer_start();
|
|
577
|
+
car_step(0, 1);
|
|
578
|
+
unsigned t_bcar = timer_stop();
|
|
579
|
+
// The edge probes alone: a 52-pixel span on a clear row, so every
|
|
580
|
+
// probe runs and none hits, then both room scans.
|
|
581
|
+
timer_start();
|
|
582
|
+
blocked(0, 150, 209 - 8);
|
|
583
|
+
room(0, 20, -1);
|
|
584
|
+
room(0, 20, 1);
|
|
585
|
+
unsigned t_probe = timer_stop();
|
|
586
|
+
|
|
587
|
+
// Pass 2, timed: the same 780 frames back to back, each car update
|
|
588
|
+
// timed on its own. The frame figure is the spawn and the four cars.
|
|
589
|
+
init();
|
|
590
|
+
unsigned t_car = 0, t_frame = 0, n_upd = 0;
|
|
591
|
+
unsigned long t_sum = 0;
|
|
592
|
+
for (unsigned f = 1; f <= FRAMES; f++) {
|
|
593
|
+
player_step(f);
|
|
594
|
+
timer_start();
|
|
595
|
+
spawn(f);
|
|
596
|
+
unsigned tf = timer_stop();
|
|
597
|
+
for (char i = 0; i < NCAR; i++) {
|
|
598
|
+
char live = st[i];
|
|
599
|
+
timer_start();
|
|
600
|
+
car_step(i, f);
|
|
601
|
+
unsigned t = timer_stop();
|
|
602
|
+
tf += t;
|
|
603
|
+
if (live) {
|
|
604
|
+
t_sum += t;
|
|
605
|
+
n_upd++;
|
|
606
|
+
if (t > t_car) t_car = t;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
if (tf > t_frame) t_frame = tf;
|
|
610
|
+
}
|
|
611
|
+
char same = contacts == c1 && rams == r1 && avoids == a1 && ppx == end_p;
|
|
612
|
+
for (char i = 0; i < NCAR; i++)
|
|
613
|
+
if (x[i] != end_x[i] || y[i] != end_y[i])
|
|
614
|
+
same = 0;
|
|
615
|
+
unsigned t_mean = (unsigned)(t_sum / n_upd);
|
|
616
|
+
|
|
617
|
+
// Pass 2 ends where pass 1 ended: that is the picture.
|
|
618
|
+
draw_road();
|
|
619
|
+
vic.ctrl1 |= VIC_CTRL1_DEN;
|
|
620
|
+
vic.spr_enable = en;
|
|
621
|
+
place_all();
|
|
622
|
+
|
|
623
|
+
put_str(SCREEN + 40, s"con ram avd hld sep");
|
|
624
|
+
put_dec(SCREEN + 44, c1, 2);
|
|
625
|
+
put_dec(SCREEN + 51, r1, 2);
|
|
626
|
+
put_dec(SCREEN + 58, a1, 3);
|
|
627
|
+
put_dec(SCREEN + 66, h1, 3);
|
|
628
|
+
put_dec(SCREEN + 74, s1, 3);
|
|
629
|
+
put_str(SCREEN + 28, s"lv");
|
|
630
|
+
put_dec(SCREEN + 31, l1, 1);
|
|
631
|
+
put_str(SCREEN + 920, s"car mean wst blt ");
|
|
632
|
+
put_dec(SCREEN + 929, t_mean, 4);
|
|
633
|
+
put_dec(SCREEN + 938, t_car, 4);
|
|
634
|
+
put_dec(SCREEN + 947, t_bcar, 4);
|
|
635
|
+
put_str(SCREEN + 952, s"prb");
|
|
636
|
+
put_dec(SCREEN + 956, t_probe, 4);
|
|
637
|
+
put_str(SCREEN + 960, s"frame wst blt tmr ");
|
|
638
|
+
put_dec(SCREEN + 970, t_frame, 5);
|
|
639
|
+
put_dec(SCREEN + 980, t_built, 5);
|
|
640
|
+
put_dec(SCREEN + 990, t_empty, 2);
|
|
641
|
+
|
|
642
|
+
char fault = 0;
|
|
643
|
+
if (ok != NCK) fault = 1;
|
|
644
|
+
else if (c1 != EXP_CONTACTS || r1 != EXP_RAMS || a1 != EXP_AVOIDS ||
|
|
645
|
+
h1 != EXP_HOLDS || s1 != EXP_SEPS || l1 != EXP_LEAVES) fault = 2;
|
|
646
|
+
else if (!same) fault = 3;
|
|
647
|
+
put_dec(SCREEN + 19, ok, 2);
|
|
648
|
+
char code = fault ? CODE_FAIL : CODE_PASS;
|
|
649
|
+
RESULT = code;
|
|
650
|
+
vic.color_border = fault ? VCOL_RED : VCOL_GREEN;
|
|
651
|
+
SCREEN[24] = 0x30 + code;
|
|
652
|
+
SCREEN[25] = 0x30 + fault;
|
|
653
|
+
for (;;)
|
|
654
|
+
;
|
|
655
|
+
return 0;
|
|
656
|
+
}
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
## Build
|
|
660
|
+
|
|
661
|
+
```bash
|
|
662
|
+
oscar64 -tm=c64 -O2 -o=lane-pursuit.prg lane-pursuit.c
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
Oscar64 1.32.271 builds it with no warnings; the PRG is 6,009 bytes.
|
|
666
|
+
|
|
667
|
+
The checksums and the event counts come from this script (Python 3). It
|
|
668
|
+
restates the rules independently of the C: the same road, autopilot,
|
|
669
|
+
spawn table, states, spacing, probes and controllers, in the same integer
|
|
670
|
+
arithmetic. It prints the ten `exp_ck` values and
|
|
671
|
+
`contacts 3 rams 7 avoids 60 leaves 1 freed 1 offroad 0 holds 318 seps 865`.
|
|
672
|
+
`offroad` counts car-frames with any tile under a car's 12 x 16 body not
|
|
673
|
+
road; the listing does not compute it, and the model's 0 is the claim
|
|
674
|
+
that the probes keep every car on the road.
|
|
675
|
+
|
|
676
|
+
```python
|
|
677
|
+
# lane_model.py: the reference for lane-pursuit.c. Same road, autopilot,
|
|
678
|
+
# spawn table and pursuit rules, written again in Python with the same
|
|
679
|
+
# integer arithmetic. Prints the checkpoint checksums and the event
|
|
680
|
+
# counts the listing compiles in.
|
|
681
|
+
|
|
682
|
+
NCAR, FRAMES, CKSTEP = 4, 780, 78
|
|
683
|
+
MW, MH = 32, 64 # map tiles across, rows per loop
|
|
684
|
+
ROAD, VERGE, WATER = 0x20, 0x60, 0xA0 # ECM screen codes: bg0, bg1, bg2
|
|
685
|
+
PSPD = 32 # player speed, 1/16 px a frame (2 px)
|
|
686
|
+
CW, CH = 12, 16 # car body in pixels
|
|
687
|
+
LOOK = 32 # look-ahead in pixels
|
|
688
|
+
APPROACH, ALONG, RAM, BACK, LEAVE = 1, 2, 3, 4, 5
|
|
689
|
+
VMAX = [0, 256, 256, 768, 256, 256] # lateral speed cap per state, 8.8
|
|
690
|
+
ACC = [0, 32, 32, 96, 32, 32] # lateral acceleration cap, 8.8
|
|
691
|
+
VAVOID, AAVOID = 512, 128 # caps while avoiding
|
|
692
|
+
RSMAX, RSLEAVE, FACC = 24, 48, 2 # relative forward speed caps, 1/16 px
|
|
693
|
+
SIDE_DX, BACK_DX, BACK_DY = 24, 40, 40
|
|
694
|
+
LEAD = 8 # frames of player velocity to lead
|
|
695
|
+
T_ALONG, T_RAM, T_BACK = 40, 24, 50
|
|
696
|
+
SEP_X, SEP_Y = 20, 32 # spacing box between pursuers, px
|
|
697
|
+
MARGIN = 12 # side probe beyond the body, px
|
|
698
|
+
|
|
699
|
+
def build_map():
|
|
700
|
+
m = []
|
|
701
|
+
for r in range(MH):
|
|
702
|
+
L, R, i0, i1 = 6, 26, 0, 0
|
|
703
|
+
if 16 <= r <= 23: L = 6 + (r - 15)
|
|
704
|
+
elif 24 <= r <= 31: L = 14
|
|
705
|
+
elif 32 <= r <= 35: L = 14 - 2 * (r - 31)
|
|
706
|
+
if 38 <= r <= 49: i0, i1 = 14, 18
|
|
707
|
+
row = []
|
|
708
|
+
for c in range(MW):
|
|
709
|
+
if c < L or c >= R: row.append(VERGE)
|
|
710
|
+
elif i0 <= c < i1: row.append(WATER)
|
|
711
|
+
else: row.append(ROAD)
|
|
712
|
+
m.append(row)
|
|
713
|
+
cen = []
|
|
714
|
+
for r in range(MH):
|
|
715
|
+
L = min(c for c in range(MW) if m[r][c] == ROAD)
|
|
716
|
+
R = L
|
|
717
|
+
while R < MW and m[r][R] == ROAD: R += 1
|
|
718
|
+
cen.append((L + R) * 4) # centre of the leftmost road run, px
|
|
719
|
+
return m, cen
|
|
720
|
+
|
|
721
|
+
MAP, CEN = build_map()
|
|
722
|
+
# spawn table: frame, slot, x px, offset px (+ ahead, - behind), shot frame
|
|
723
|
+
SPAWNS = [(8, 0, 56, -72, 0), (8, 1, 184, 104, 0), (40, 2, 176, -72, 0),
|
|
724
|
+
(90, 3, 64, 104, 330), (420, 3, 176, -72, 0)]
|
|
725
|
+
|
|
726
|
+
def s16(v): return ((v + 0x8000) & 0xFFFF) - 0x8000
|
|
727
|
+
def sm(e, s): return e >> s if e >= 0 else -((-e) >> s)
|
|
728
|
+
def clamp(v, a, b): return a if v < a else b if v > b else v
|
|
729
|
+
def tile(y16, px): return MAP[(y16 >> 7) & 63][(px >> 3) & 31]
|
|
730
|
+
|
|
731
|
+
class S: pass
|
|
732
|
+
|
|
733
|
+
def init():
|
|
734
|
+
s = S()
|
|
735
|
+
s.P = 1024 * 16; s.ppx = CEN[(s.P >> 7) & 63] - 6; s.pvx = 0
|
|
736
|
+
s.st = [0] * NCAR; s.x = [0] * NCAR; s.vx = [0] * NCAR; s.y = [0] * NCAR
|
|
737
|
+
s.rs = [0] * NCAR; s.side = [1] * NCAR; s.tm = [0] * NCAR; s.shot = [0] * NCAR
|
|
738
|
+
s.contacts = s.rams = s.avoids = s.leaves = s.offroad = s.freed = s.holds = s.seps = 0
|
|
739
|
+
s.sp = 0; s.owner = [0xFF, 0xFF]
|
|
740
|
+
return s
|
|
741
|
+
|
|
742
|
+
def tri(f):
|
|
743
|
+
t = f & 63
|
|
744
|
+
return t - 16 if t < 32 else 48 - t
|
|
745
|
+
|
|
746
|
+
def player_step(s, f):
|
|
747
|
+
s.P = (s.P + PSPD) & 0xFFFF
|
|
748
|
+
tp = CEN[((s.P + 32 * 16) >> 7) & 63] + tri(f) - 6
|
|
749
|
+
s.pvx = clamp(tp - s.ppx, -2, 2)
|
|
750
|
+
s.ppx += s.pvx
|
|
751
|
+
|
|
752
|
+
def spawn(s, f):
|
|
753
|
+
while s.sp < len(SPAWNS) and SPAWNS[s.sp][0] == f:
|
|
754
|
+
_, k, x, dy, shot = SPAWNS[s.sp]
|
|
755
|
+
s.st[k] = APPROACH; s.x[k] = x << 8; s.vx[k] = 0
|
|
756
|
+
s.y[k] = (s.P + dy * 16) & 0xFFFF; s.rs[k] = 0; s.shot[k] = shot
|
|
757
|
+
s.sp += 1
|
|
758
|
+
|
|
759
|
+
def room(row, c, d):
|
|
760
|
+
n = 0
|
|
761
|
+
for i in range(1, 9):
|
|
762
|
+
cc = c + d * i
|
|
763
|
+
if 0 <= cc < MW and MAP[row][cc] == ROAD: n += 1
|
|
764
|
+
return n
|
|
765
|
+
|
|
766
|
+
def blocked(y16, a, b): # probe a..b every 8 px and at b
|
|
767
|
+
while a < b:
|
|
768
|
+
if tile(y16, a) != ROAD: return True
|
|
769
|
+
a += 8
|
|
770
|
+
return tile(y16, b) != ROAD
|
|
771
|
+
|
|
772
|
+
def release(s, i):
|
|
773
|
+
for k in (0, 1):
|
|
774
|
+
if s.owner[k] == i: s.owner[k] = 0xFF
|
|
775
|
+
|
|
776
|
+
def car_step(s, i, f):
|
|
777
|
+
st = s.st[i]
|
|
778
|
+
if st == 0: return
|
|
779
|
+
if s.shot[i] == f and st != LEAVE:
|
|
780
|
+
st = LEAVE; s.leaves += 1; release(s, i)
|
|
781
|
+
px = s.x[i] >> 8
|
|
782
|
+
rel = s16(s.y[i] - s.P)
|
|
783
|
+
dx = px - s.ppx
|
|
784
|
+
# --- state transitions and pursuit targets ---
|
|
785
|
+
if st == APPROACH:
|
|
786
|
+
sd = 1 if dx >= 0 else 0
|
|
787
|
+
if s.owner[sd] != 0xFF and s.owner[sd] != i: sd ^= 1
|
|
788
|
+
if s.owner[sd] == 0xFF or s.owner[sd] == i:
|
|
789
|
+
s.owner[sd] = i; s.side[i] = 2 * sd - 1
|
|
790
|
+
tx = s.ppx + s.side[i] * SIDE_DX; rt = 0
|
|
791
|
+
if abs(rel) < 8 * 16 and abs(px - tx) < 6:
|
|
792
|
+
st = ALONG; s.tm[i] = T_ALONG
|
|
793
|
+
else: # both sides taken: queue behind
|
|
794
|
+
tx = px; rt = -BACK_DY * 16
|
|
795
|
+
elif st == ALONG:
|
|
796
|
+
tx = s.ppx + s.side[i] * SIDE_DX; rt = 0
|
|
797
|
+
s.tm[i] -= 1
|
|
798
|
+
if s.tm[i] == 0:
|
|
799
|
+
st = RAM; s.tm[i] = T_RAM; s.rams += 1
|
|
800
|
+
elif st == RAM:
|
|
801
|
+
tx = s.ppx + s.pvx * LEAD; rt = 0
|
|
802
|
+
s.tm[i] -= 1
|
|
803
|
+
if abs(dx) < 14 and abs(rel) < 16 * 16:
|
|
804
|
+
s.contacts += 1; st = BACK; s.tm[i] = T_BACK
|
|
805
|
+
elif s.tm[i] == 0:
|
|
806
|
+
st = BACK; s.tm[i] = T_BACK
|
|
807
|
+
if st == BACK: release(s, i)
|
|
808
|
+
elif st == BACK:
|
|
809
|
+
tx = s.ppx + s.side[i] * BACK_DX; rt = -BACK_DY * 16
|
|
810
|
+
s.tm[i] -= 1
|
|
811
|
+
if s.tm[i] == 0: st = APPROACH
|
|
812
|
+
else:
|
|
813
|
+
tx = px; rt = -200 * 16
|
|
814
|
+
# --- spacing: steer and hold back from the first pursuer too close ---
|
|
815
|
+
for j in range(NCAR):
|
|
816
|
+
if j == i or s.st[j] == 0: continue
|
|
817
|
+
ddx = (s.x[j] >> 8) - px
|
|
818
|
+
ddy = s16(s.y[j] - s.y[i])
|
|
819
|
+
if abs(ddx) < SEP_X and abs(ddy) < SEP_Y * 16:
|
|
820
|
+
tx = px - SEP_X if ddx > 0 or (ddx == 0 and i < j) else px + SEP_X
|
|
821
|
+
if ddy > 0: rt = rel + ddy - SEP_Y * 16
|
|
822
|
+
s.seps += 1
|
|
823
|
+
break
|
|
824
|
+
# --- edge avoidance: probe the row LOOK pixels ahead ---
|
|
825
|
+
vmax, acc = VMAX[st], ACC[st]
|
|
826
|
+
ahead = (s.y[i] + (CH - 1 + LOOK) * 16) & 0xFFFF
|
|
827
|
+
qx = clamp(px + sm(s.vx[i], 4), 0, 255 - CW) # where the car will be
|
|
828
|
+
if blocked(ahead, min(px, qx), max(px, qx) + CW - 1):
|
|
829
|
+
row = (ahead >> 7) & 63; c = (qx + 6) >> 3
|
|
830
|
+
tx = px - 32 if room(row, c, -1) > room(row, c, 1) else px + 32
|
|
831
|
+
vmax, acc = VAVOID, AAVOID; s.avoids += 1
|
|
832
|
+
else: # side probe: next row, toward tx
|
|
833
|
+
near = (s.y[i] + (CH - 1 + 8) * 16) & 0xFFFF
|
|
834
|
+
if tx > px: sx = px + CW - 1 + MARGIN
|
|
835
|
+
elif tx < px: sx = px - MARGIN
|
|
836
|
+
else: sx = px
|
|
837
|
+
if 0 <= sx <= 255 and (tile(near, sx) != ROAD or tile(s.y[i], sx) != ROAD):
|
|
838
|
+
tx = px; acc = AAVOID; s.holds += 1
|
|
839
|
+
tx = clamp(tx, 8, 236)
|
|
840
|
+
# --- lateral: proportional with a speed cap and an acceleration cap ---
|
|
841
|
+
want = clamp((tx - px) * 16, -vmax, vmax)
|
|
842
|
+
v = s.vx[i]
|
|
843
|
+
if want > v + acc: v += acc
|
|
844
|
+
elif want < v - acc: v -= acc
|
|
845
|
+
else: v = want
|
|
846
|
+
s.vx[i] = v; s.x[i] = (s.x[i] + v) & 0xFFFF
|
|
847
|
+
# --- forward: relative speed toward the wanted offset ---
|
|
848
|
+
cap = RSLEAVE if st == LEAVE else RSMAX
|
|
849
|
+
want = clamp(sm(rt - rel, 3), -cap, cap)
|
|
850
|
+
r = s.rs[i]
|
|
851
|
+
if want > r + FACC: r += FACC
|
|
852
|
+
elif want < r - FACC: r -= FACC
|
|
853
|
+
else: r = want
|
|
854
|
+
s.rs[i] = r; s.y[i] = (s.y[i] + PSPD + r) & 0xFFFF
|
|
855
|
+
if st == LEAVE and s16(s.y[i] - s.P) < -150 * 16:
|
|
856
|
+
st = 0; s.freed += 1
|
|
857
|
+
s.st[i] = st
|
|
858
|
+
if st:
|
|
859
|
+
px = s.x[i] >> 8
|
|
860
|
+
top = (s.y[i] + (CH - 1) * 16) & 0xFFFF
|
|
861
|
+
if blocked(s.y[i], px, px + CW - 1) or blocked(top, px, px + CW - 1):
|
|
862
|
+
s.offroad += 1
|
|
863
|
+
|
|
864
|
+
def run(trace=False):
|
|
865
|
+
s = init(); ck = 0; cks = []; hist = set()
|
|
866
|
+
for f in range(1, FRAMES + 1):
|
|
867
|
+
player_step(s, f)
|
|
868
|
+
spawn(s, f)
|
|
869
|
+
for i in range(NCAR): car_step(s, i, f)
|
|
870
|
+
for i in range(NCAR):
|
|
871
|
+
ck = (ck * 33 + s.st[i]) & 0xFFFF
|
|
872
|
+
ck = (ck * 33 + (s.x[i] >> 8)) & 0xFFFF
|
|
873
|
+
hist.add((i, s.st[i]))
|
|
874
|
+
if f % CKSTEP == 0: cks.append(ck)
|
|
875
|
+
if trace and f % 10 == 0:
|
|
876
|
+
print(f, s.ppx, s.pvx, [(s.st[i], s.x[i] >> 8, s16(s.y[i] - s.P) // 16) for i in range(NCAR)])
|
|
877
|
+
return s, cks, hist
|
|
878
|
+
|
|
879
|
+
if __name__ == '__main__':
|
|
880
|
+
import sys
|
|
881
|
+
s, cks, hist = run('-t' in sys.argv)
|
|
882
|
+
print('exp_ck', ', '.join('0x%04x' % c for c in cks))
|
|
883
|
+
print('contacts', s.contacts, 'rams', s.rams, 'avoids', s.avoids,
|
|
884
|
+
'leaves', s.leaves, 'freed', s.freed, 'offroad', s.offroad, 'holds', s.holds, 'seps', s.seps)
|
|
885
|
+
print('end', s.ppx, [(s.st[i], s.x[i] >> 8, s16(s.y[i] - s.P)) for i in range(NCAR)])
|
|
886
|
+
print('states seen', sorted(hist))
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
## Expected output
|
|
890
|
+
|
|
891
|
+
A grey road on green verges, with the HUD in white on the grey. Five
|
|
892
|
+
12 x 16 outline sprites: the player in yellow, cars 0 to 3 in red,
|
|
893
|
+
purple, cyan and orange. The border is green: (98, 213, 50) on PAL,
|
|
894
|
+
(114, 189, 103) on NTSC.
|
|
895
|
+
|
|
896
|
+
HUD at 45,000,000 cycles (measured in VICE x64sc 3.10, decoded against
|
|
897
|
+
the character ROM), identical on PAL and NTSC:
|
|
898
|
+
|
|
899
|
+
| Row | Text |
|
|
900
|
+
|---|---|
|
|
901
|
+
| 0 | `F 0780 CK BA59 OK 10 R 10 LV 1` |
|
|
902
|
+
| 1 | `CON 03 RAM 07 AVD 060 HLD 318 SEP 865` |
|
|
903
|
+
| 23 | `CAR MEAN 1981 WST 3162 BLT 3361 PRB 1649` |
|
|
904
|
+
| 24 | `FRAME WST 09871 BLT 14204 TMR 05` |
|
|
905
|
+
|
|
906
|
+
`F` is the frame, `CK` the final checksum, `OK 10` the checkpoints that
|
|
907
|
+
matched, `R 10` result code 1 and fault 0, `LV 1` the cars that left when
|
|
908
|
+
shot. Row 1 is pass 1's event counts, each equal to the model's: `CON`
|
|
909
|
+
rams that made contact, `RAM` rams started, `AVD` car-frames steering
|
|
910
|
+
away from a blocked probe, `HLD` car-frames held by the side probe, `SEP`
|
|
911
|
+
car-frames steered by the spacing box. Rows 23 and 24 are CIA1 cycles:
|
|
912
|
+
`CAR MEAN` the mean of pass 2's live car updates, `WST` the worst one,
|
|
913
|
+
`BLT` one car on the built worst path (in contact with the player) with
|
|
914
|
+
three others to scan, `PRB` the edge probes alone (a 52-pixel span of
|
|
915
|
+
eight probes and both 8-tile room scans); `FRAME WST` pass 2's worst
|
|
916
|
+
frame (spawn and four car updates), `BLT` the built worst frame (the
|
|
917
|
+
table's two frame-8 spawns, then four cars on the built path, car 0 in
|
|
918
|
+
contact), `TMR` the empty timer pair. Fault
|
|
919
|
+
1 is a checkpoint mismatch, 2 an event count that is not the model's, 3
|
|
920
|
+
pass 2 ending in another state than pass 1.
|
|
921
|
+
|
|
922
|
+
The timed figures are the same on both models because the display and
|
|
923
|
+
sprites are off while they run: no badlines, no sprite DMA. With the
|
|
924
|
+
display on, pass 1 does not keep one step per video frame. The frame
|
|
925
|
+
counter reads `0433` at 20,000,000 cycles and `0687` at 30,000,000 on
|
|
926
|
+
PAL: 254 steps in 10,000,000 cycles, 39,370 cycles a step, 2.00 PAL
|
|
927
|
+
frames. NTSC reads `0197` at 10,000,000 and `0490` at 20,000,000: 293
|
|
928
|
+
steps, 34,130 cycles a step, 2.00 NTSC frames (VICE x64sc 3.10). The
|
|
929
|
+
step, the redraw of 21 text rows and the sprite placement overrun one
|
|
930
|
+
frame, so each step waits for the next one. On screen the road
|
|
931
|
+
therefore moves 1 pixel a video frame, not 2. This is the AI plus a
|
|
932
|
+
naive redraw not fitting a frame with badlines and sprite DMA.
|
|
933
|
+
|
|
934
|
+
What the run exercises, from the model: seven rams, at frames 98, 144,
|
|
935
|
+
248, 414, 558, 667 and 770; three of them make contact (cars 0, 2 and 1,
|
|
936
|
+
at frames 109, 263 and 572). Car 0, spawned behind on the left, is the
|
|
937
|
+
first to meet an edge: at frame 66 the road starts to narrow from the
|
|
938
|
+
left under its look-ahead, and it steers right. Every car avoids at
|
|
939
|
+
least once (28, 15, 6 and 11 car-frames). Car 3 is shot at frame 330,
|
|
940
|
+
drops back and frees its slot at frame 392; the table respawns slot 3
|
|
941
|
+
behind the player at frame 420. No car-frame has a car off the road.
|
|
942
|
+
|
|
943
|
+
At the island the pursuers usually end in the other channel from the
|
|
944
|
+
player. Counting car-frames with the player's and a pursuer's rear rows
|
|
945
|
+
both beside the island, 375 of 452 have the pursuer across the water.
|
|
946
|
+
Cars 1 and 2 are on the player's right when the autopilot dives into the
|
|
947
|
+
left channel, 75 pixels or more from it. The technique page's "Why it
|
|
948
|
+
works" gives the cause and the fixes that were tried in the model.
|
|
949
|
+
|
|
950
|
+
Screenshots at 45,000,000 cycles, both after the verdict and each
|
|
951
|
+
identical over two runs (PAL is still in pass 2 at 40,000,000 and shows the verdict at 42,000,000):
|
|
952
|
+
`screenshots/lane-pursuit.png` (PAL) and
|
|
953
|
+
`screenshots/lane-pursuit-ntsc.png` (NTSC). Measured on both with PIL:
|
|
954
|
+
each sprite is a 12 x 16 outline of 52 pixels at the position the model
|
|
955
|
+
ends on (the player at x 118, car 3 in RAM at 132 one pixel ahead of it,
|
|
956
|
+
cars 0 to 2 approaching 38 to 40 pixels behind at 185, 164 and 143); the
|
|
957
|
+
140 pixels inside every outline are road grey; and the centre pixel of
|
|
958
|
+
each of the 672 playfield tiles has the colour of the model's map row
|
|
959
|
+
(no water is in view at the end). The script, with the model saved as
|
|
960
|
+
`lane_model.py`:
|
|
961
|
+
|
|
962
|
+
```python
|
|
963
|
+
# Measures the exit screenshots of lane-pursuit.prg against lane_model.py.
|
|
964
|
+
from PIL import Image
|
|
965
|
+
import lane_model as m
|
|
966
|
+
PAL = {'row': 16, 'road': (98, 98, 98), 'verge': (98, 213, 50),
|
|
967
|
+
'cars': [(255, 255, 70), (175, 60, 88), (170, 64, 245), (126, 243, 214), (183, 99, 30)]}
|
|
968
|
+
NTSC = {'row': 28, 'road': (98, 98, 98), 'verge': (114, 189, 103),
|
|
969
|
+
'cars': [(255, 248, 141), (169, 71, 100), (154, 88, 185), (138, 230, 203), (196, 98, 65)]}
|
|
970
|
+
s, _, _ = m.run()
|
|
971
|
+
base = s.P & 0xFF80
|
|
972
|
+
def spot(wy, px): # screenshot x, first row of a sprite
|
|
973
|
+
d = ((wy - base + 0x4000) & 0xFFFF) // 16 - 0x400
|
|
974
|
+
return px + 64, 42 + 8 * 14 - d + 1
|
|
975
|
+
want = [spot(s.P, s.ppx)] + [spot(s.y[i], s.x[i] >> 8) for i in range(4)]
|
|
976
|
+
def check(png, g):
|
|
977
|
+
im = Image.open(png).convert('RGB'); px = im.load(); W, H = im.size
|
|
978
|
+
for k, c in enumerate(g['cars']):
|
|
979
|
+
pts = [(x, y) for y in range(H) for x in range(W) if px[x, y] == c]
|
|
980
|
+
x0, y0 = min(p[0] for p in pts), min(p[1] for p in pts)
|
|
981
|
+
x1, y1 = max(p[0] for p in pts), max(p[1] for p in pts)
|
|
982
|
+
wx, wy = want[k]
|
|
983
|
+
assert (x0, y0 + g['row']) == (wx, wy), (png, k, x0, y0, want[k])
|
|
984
|
+
assert (x1 - x0, y1 - y0, len(pts)) == (11, 15, 52), (png, k)
|
|
985
|
+
inner = {px[x, y] for x in range(x0 + 1, x1) for y in range(y0 + 1, y1)}
|
|
986
|
+
assert inner == {g['road']}, (png, k, inner)
|
|
987
|
+
# road rows 2..22: the centre pixel of every tile against the model map
|
|
988
|
+
prow = (s.P >> 7) & 63; bad = n = 0
|
|
989
|
+
for r in range(2, 23):
|
|
990
|
+
for c in range(32):
|
|
991
|
+
p = px[64 + 8 * c + 3, 51 + 8 * r + 3 - g['row']]
|
|
992
|
+
if p in g['cars']: continue
|
|
993
|
+
t = m.MAP[(prow + 14 - r) & 63][c]
|
|
994
|
+
n += 1
|
|
995
|
+
bad += p != (g['road'] if t == m.ROAD else g['verge'])
|
|
996
|
+
print(png, 'tiles sampled', n)
|
|
997
|
+
return bad
|
|
998
|
+
for png, g in (('pal1.png', PAL), ('ntsc1.png', NTSC)):
|
|
999
|
+
print(png, 'tile mismatches', check(png, g))
|
|
1000
|
+
print('model end: player', s.ppx, 'cars', [(s.st[i], s.x[i] >> 8) for i in range(4)])
|
|
1001
|
+
```
|
|
1002
|
+
|
|
1003
|
+
## Why this works
|
|
1004
|
+
|
|
1005
|
+
Every quantity the controllers touch is an integer: lateral position in
|
|
1006
|
+
8.8, lateral speed in 8.8, world y and forward speed in 1/16 pixel. The
|
|
1007
|
+
one rounding the rules need, `sm`, shifts the magnitude and restores the
|
|
1008
|
+
sign, so C and Python agree on negative values without relying on how
|
|
1009
|
+
either shifts a negative number. That is why the model can be exact and
|
|
1010
|
+
the checksum can hold for 780 frames of four interacting cars.
|
|
1011
|
+
|
|
1012
|
+
The swept probe is what keeps a car off the island. A car probes the row
|
|
1013
|
+
32 pixels past its nose, not at the x it has now but across the span
|
|
1014
|
+
from now to where its lateral speed puts it 16 frames later, one probe
|
|
1015
|
+
every 8 pixels so no tile between two probes is skipped. An early draft
|
|
1016
|
+
of the model, which probed at the car's current x only, 24 pixels ahead
|
|
1017
|
+
and with no side probe, counted 226 off-road car-frames: ramming cars
|
|
1018
|
+
slid onto the island. A later draft that measured the look-ahead from
|
|
1019
|
+
the car's rear rather than its nose counted 10. The side probe covers the other
|
|
1020
|
+
case: a target beside the player that lies on the verge or the water.
|
|
1021
|
+
It holds the lateral target rather than steering away, so a car beside
|
|
1022
|
+
a narrowing edge waits instead of weaving.
|
|
1023
|
+
|
|
1024
|
+
What the listing leaves out: contact between cars and between a car and
|
|
1025
|
+
the player is a count, not a push (`car_contact_response` is the page
|
|
1026
|
+
for that); there is no shooting, only a scripted shot; the scroll is
|
|
1027
|
+
coarse, one text row every four steps (eight video frames in pass 1). The pursuit code does not change
|
|
1028
|
+
when those are added.
|