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,1099 @@
|
|
|
1
|
+
---
|
|
2
|
+
recipe: destructible-terrain
|
|
3
|
+
toolchain: oscar64
|
|
4
|
+
output_format: PRG
|
|
5
|
+
region: both
|
|
6
|
+
techniques: [destructible_char_terrain, creature_state_machine]
|
|
7
|
+
file_formats: [PRG]
|
|
8
|
+
uses_registers: [D011, D012, D016, D018, D020, D021, D022, D023, DC06, DC07, DC0F, DD06, DD07, DD0F]
|
|
9
|
+
uses_kernal: []
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<!-- doc-type: recipe -->
|
|
13
|
+
|
|
14
|
+
# Oscar64 destructible terrain: 24 creatures dig, build and block in character terrain with private glyphs, checked against a Python model
|
|
15
|
+
|
|
16
|
+
## Synopsis
|
|
17
|
+
|
|
18
|
+
A 40 by 16 cell level in multicolour characters, 160 by 128 pixels.
|
|
19
|
+
Almost every cell shows one of three shared glyphs: empty, earth, or a
|
|
20
|
+
2-pixel step. When a creature digs or builds, the edited cell gets a
|
|
21
|
+
private glyph from a pool of 128 codes ($80 to $FF), copied from its
|
|
22
|
+
shared glyph and then changed pixel by pixel. A private glyph that
|
|
23
|
+
becomes equal to a shared glyph again goes back to the pool. 24
|
|
24
|
+
creatures drop from a hatch, one every 8 ticks, and run a state machine
|
|
25
|
+
(walk, fall, dig, build, block, dead) on single-pixel probes of the
|
|
26
|
+
glyph bytes. Four roles are given at fixed ticks: a blocker, two
|
|
27
|
+
diggers side by side and a builder. After 360 ticks the program folds
|
|
28
|
+
the level's glyph bitmap and every creature's position, state and
|
|
29
|
+
direction into Fletcher-16 checksums and compares them, the pool counts
|
|
30
|
+
and the death count with values from a Python model of the same rules,
|
|
31
|
+
compiled in. Then five worst-frame subjects run from a snapshot: the
|
|
32
|
+
terrain edits of 24 dig steps alone, the same edits timed one dig step
|
|
33
|
+
at a time, a whole tick with all 24 creatures on a dig step, one with
|
|
34
|
+
all 24 laying a brick, and the updates alone of all 24 walking into a
|
|
35
|
+
2-pixel wall and climbing it. Each is checked against the model too. The snapshot is put back and the screen
|
|
36
|
+
shows the scenario. `PASS`, a green border and 01 in `$02FF`; `FAIL`,
|
|
37
|
+
red and 02. The techniques are `destructible_char_terrain` in
|
|
38
|
+
`techniques/text-mode-render.md` and `creature_state_machine` in
|
|
39
|
+
`techniques/logic.md`.
|
|
40
|
+
|
|
41
|
+
## Source
|
|
42
|
+
|
|
43
|
+
```c
|
|
44
|
+
// destructible-terrain.c -- terrain drawn in multicolour characters that
|
|
45
|
+
// creatures dig and build into, pixel by pixel. Most cells use one of
|
|
46
|
+
// three shared glyphs; an edited cell gets a private glyph from a pool of
|
|
47
|
+
// 128 codes ($80-$FF) and gives it back when it matches a shared glyph
|
|
48
|
+
// again. 24 creatures run a small state machine (walk, fall, dig, build,
|
|
49
|
+
// block, dead) on per-pixel probes of the glyph bytes, and are drawn as
|
|
50
|
+
// merged glyphs (save the code under, copy its glyph, OR the body in).
|
|
51
|
+
// Roles are given at fixed ticks. After 360 ticks the terrain pixels and
|
|
52
|
+
// the creature states are checked against a Python model, then five
|
|
53
|
+
// worst-frame subjects run from a snapshot and are timed and checked.
|
|
54
|
+
// $02FF = 01 and a green border on pass, 02 and red on fail.
|
|
55
|
+
// Build: oscar64 -tm=c64 -O2 -o=destructible-terrain.prg destructible-terrain.c
|
|
56
|
+
#include <c64/vic.h>
|
|
57
|
+
#include <c64/cia.h>
|
|
58
|
+
#include <c64/memmap.h>
|
|
59
|
+
#include <string.h>
|
|
60
|
+
|
|
61
|
+
// The charset is at $3800-$3FFF; code and data go above it.
|
|
62
|
+
#pragma region( main, 0x4000, 0xa000, , , {code, data, bss, heap, stack} )
|
|
63
|
+
|
|
64
|
+
#define SCREEN ((char *)0x0400)
|
|
65
|
+
#define COLOUR ((char *)0xd800)
|
|
66
|
+
#define CHAR_ROM ((char *)0xd000)
|
|
67
|
+
#define FONT ((char *)0x3800) // $D018 = $1E: screen $0400, chars $3800
|
|
68
|
+
#define RESULT (*(volatile char *)0x02ff)
|
|
69
|
+
|
|
70
|
+
#define LW 40 // level: 40 x 16 cells at screen rows 9-24
|
|
71
|
+
#define LH 16
|
|
72
|
+
#define LY0 9
|
|
73
|
+
#define PW 160 // 160 x 128 multicolour pixels
|
|
74
|
+
#define PH 128
|
|
75
|
+
|
|
76
|
+
#define G_EMPTY 0x40 // shared glyphs
|
|
77
|
+
#define G_EARTH 0x41
|
|
78
|
+
#define G_STEP2 0x42
|
|
79
|
+
#define CR_BASE 0x50 // creature glyphs $50-$7F, two each
|
|
80
|
+
#define POOL0 0x80 // private terrain glyphs $80-$FF
|
|
81
|
+
#define POOLN 128
|
|
82
|
+
|
|
83
|
+
#define NC 24 // creatures
|
|
84
|
+
#define HX 22 // hatch: feet at (22, 12)
|
|
85
|
+
#define HY 12
|
|
86
|
+
#define REL 8 // one released every 8 ticks
|
|
87
|
+
#define FATAL 32 // a fall longer than this kills
|
|
88
|
+
#define BRICKS 12
|
|
89
|
+
#define TICKS 360
|
|
90
|
+
|
|
91
|
+
enum { WALK, FALL, DIG, BUILD, BLOCK, DEAD };
|
|
92
|
+
|
|
93
|
+
// From terrain_model.py (see Build).
|
|
94
|
+
#define EXP_TERRAIN 0xcb64
|
|
95
|
+
#define EXP_CREATURES 0x92cb
|
|
96
|
+
#define EXP_USED 7
|
|
97
|
+
#define EXP_PEAK 7
|
|
98
|
+
#define EXP_FREED 2
|
|
99
|
+
#define EXP_DEAD 5
|
|
100
|
+
#define EXP_S_TERRAIN 0x627f
|
|
101
|
+
#define EXP_S_CREATURES 0x2a72
|
|
102
|
+
#define EXP_S_USED 43
|
|
103
|
+
#define EXP_B_TERRAIN 0xaa64
|
|
104
|
+
#define EXP_B_CREATURES 0x2a72
|
|
105
|
+
#define EXP_B_USED 43
|
|
106
|
+
#define EXP_W_TERRAIN 0x7d97
|
|
107
|
+
#define EXP_W_CREATURES 0x0012
|
|
108
|
+
#define EXP_W_USED 31
|
|
109
|
+
|
|
110
|
+
static const char level[LH][LW + 1] = {
|
|
111
|
+
"........................................",
|
|
112
|
+
"........................................",
|
|
113
|
+
"........................................",
|
|
114
|
+
"........................................",
|
|
115
|
+
"#...........s...........................",
|
|
116
|
+
"#######################.................",
|
|
117
|
+
"#######################.................",
|
|
118
|
+
"#.......................................",
|
|
119
|
+
"#...............................########",
|
|
120
|
+
"###############################.........",
|
|
121
|
+
"#......................................#",
|
|
122
|
+
"#......................................#",
|
|
123
|
+
"#......................................#",
|
|
124
|
+
"#......................................#",
|
|
125
|
+
"#......................................#",
|
|
126
|
+
"########################################",
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
static const char earth_rows[8] = { 0x56, 0x65, 0x59, 0x95, 0x56, 0x65, 0x59, 0x95 };
|
|
130
|
+
static const char step2_rows[8] = { 0, 0, 0, 0, 0, 0, 0xaa, 0xaa };
|
|
131
|
+
static const char pmask[4] = { 0xc0, 0x30, 0x0c, 0x03 }; // pixel x & 3
|
|
132
|
+
|
|
133
|
+
static char * rowp[LH]; // level row start in screen RAM
|
|
134
|
+
|
|
135
|
+
// Creatures.
|
|
136
|
+
static char cx[NC], cy[NC], cst[NC], cfall[NC], ct[NC], cbr[NC];
|
|
137
|
+
static signed char cdir[NC];
|
|
138
|
+
static char ncr; // released so far
|
|
139
|
+
static char under[2 * NC]; // screen code under each drawn cell
|
|
140
|
+
static char * ucell[2 * NC]; // and where it was
|
|
141
|
+
static char ncell[NC]; // cells drawn: 0, 1 or 2
|
|
142
|
+
|
|
143
|
+
// Private glyph pool: a stack of free codes.
|
|
144
|
+
static char freel[POOLN], nfree, used, peak, freed;
|
|
145
|
+
static unsigned refused;
|
|
146
|
+
|
|
147
|
+
static char blk_x[4], blk_y[4], nblk;
|
|
148
|
+
|
|
149
|
+
// Scripted roles: tick, creature, role.
|
|
150
|
+
static const unsigned sc_tick[4] = { 70, 120, 157, 246 };
|
|
151
|
+
static const char sc_who[4] = { 0, 3, 8, 4 };
|
|
152
|
+
static const char sc_role[4] = { BLOCK, DIG, DIG, BUILD };
|
|
153
|
+
static char ignored;
|
|
154
|
+
|
|
155
|
+
static unsigned tmax[4]; // worst single update per state
|
|
156
|
+
static unsigned t_tick, t_erase, t_draw, max_tick, max_erase, max_draw;
|
|
157
|
+
|
|
158
|
+
static void timer_start(void)
|
|
159
|
+
{
|
|
160
|
+
cia1.crb = 0x00;
|
|
161
|
+
cia1.tb = 0xffff;
|
|
162
|
+
cia1.crb = 0x11; // force load, start, count phi2
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
static unsigned timer_stop(void)
|
|
166
|
+
{
|
|
167
|
+
cia1.crb = 0x00;
|
|
168
|
+
return 0xffff - cia1.tb;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
static void put_str(char * dst, const char * s)
|
|
172
|
+
{
|
|
173
|
+
while (*s) {
|
|
174
|
+
char c = *s++;
|
|
175
|
+
if (c >= 'A' && c <= 'Z') c -= 64; // ASCII letter to screen code
|
|
176
|
+
*dst++ = c;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
static void put_dec(char * dst, unsigned v, char n)
|
|
181
|
+
{
|
|
182
|
+
for (char i = n; i > 0; i--) {
|
|
183
|
+
dst[i - 1] = '0' + (char)(v % 10);
|
|
184
|
+
v /= 10;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
static void put_hex(char * dst, unsigned v)
|
|
189
|
+
{
|
|
190
|
+
for (char i = 4; i > 0; i--) {
|
|
191
|
+
char d = v & 15;
|
|
192
|
+
dst[i - 1] = d < 10 ? '0' + d : d - 9;
|
|
193
|
+
v >>= 4;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Is the pixel at (x, y) terrain? Outside the level counts as solid;
|
|
198
|
+
// x - 1 at 0 and y - 1 at 0 wrap to 255 and land here too.
|
|
199
|
+
static inline char solid(char x, char y)
|
|
200
|
+
{
|
|
201
|
+
if (x >= PW || y >= PH) return 1;
|
|
202
|
+
char code = rowp[y >> 3][x >> 2];
|
|
203
|
+
return FONT[(unsigned)code * 8 + (y & 7)] & pmask[x & 3];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Timing subjects for the probe. Written inline as a loop storing to a
|
|
207
|
+
// local volatile, it left the timer window empty in the -O2 listing;
|
|
208
|
+
// these write a global and are kept out of line.
|
|
209
|
+
static char probe_out[PW];
|
|
210
|
+
__noinline void probe_row(char y)
|
|
211
|
+
{
|
|
212
|
+
for (char x = 0; x < PW; x++) probe_out[x] = solid(x, y);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
__noinline void empty_row(char y)
|
|
216
|
+
{
|
|
217
|
+
for (char x = 0; x < PW; x++) probe_out[x] = x + y;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
static bool same8(const char * a, const char * b)
|
|
221
|
+
{
|
|
222
|
+
for (char i = 0; i < 8; i++)
|
|
223
|
+
if (a[i] != b[i]) return false;
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Set the pixel at (x, y) to pair v (0 = empty, 2 = brick). A shared
|
|
228
|
+
// cell first gets a private copy of its glyph; a private glyph that ends
|
|
229
|
+
// up equal to a shared one is given back.
|
|
230
|
+
static void setpix(char x, char y, char v)
|
|
231
|
+
{
|
|
232
|
+
if (x >= PW || y >= PH) return;
|
|
233
|
+
char * cell = rowp[y >> 3] + (x >> 2);
|
|
234
|
+
char code = *cell;
|
|
235
|
+
char m = pmask[x & 3];
|
|
236
|
+
char want = v ? (0xaa & m) : 0;
|
|
237
|
+
char * g = FONT + (unsigned)code * 8;
|
|
238
|
+
char r = y & 7;
|
|
239
|
+
if ((g[r] & m) == want) return;
|
|
240
|
+
if (code < POOL0) {
|
|
241
|
+
if (nfree == 0) { refused++; return; }
|
|
242
|
+
char p = freel[--nfree];
|
|
243
|
+
char * n = FONT + (unsigned)p * 8;
|
|
244
|
+
n[0] = g[0]; n[1] = g[1]; n[2] = g[2]; n[3] = g[3];
|
|
245
|
+
n[4] = g[4]; n[5] = g[5]; n[6] = g[6]; n[7] = g[7];
|
|
246
|
+
*cell = p; code = p; g = n;
|
|
247
|
+
used++;
|
|
248
|
+
if (used > peak) peak = used;
|
|
249
|
+
}
|
|
250
|
+
g[r] = (g[r] & ~m) | want;
|
|
251
|
+
for (char s = G_EMPTY; s <= G_STEP2; s++) {
|
|
252
|
+
if (same8(g, FONT + (unsigned)s * 8)) {
|
|
253
|
+
*cell = s;
|
|
254
|
+
freel[nfree++] = code;
|
|
255
|
+
used--;
|
|
256
|
+
freed++;
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
static bool blocked(char nx, char y)
|
|
263
|
+
{
|
|
264
|
+
for (char k = 0; k < nblk; k++) {
|
|
265
|
+
char d = blk_y[k] > y ? blk_y[k] - y : y - blk_y[k];
|
|
266
|
+
if (blk_x[k] == nx && d < 4) return true;
|
|
267
|
+
}
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
static void update(char i)
|
|
272
|
+
{
|
|
273
|
+
char x = cx[i], y = cy[i];
|
|
274
|
+
switch (cst[i]) {
|
|
275
|
+
case WALK: {
|
|
276
|
+
char nx = x + cdir[i];
|
|
277
|
+
if (nx >= PW || blocked(nx, y)) { cdir[i] = -cdir[i]; return; }
|
|
278
|
+
char ny = y;
|
|
279
|
+
if (solid(nx, ny)) {
|
|
280
|
+
if (!solid(nx, ny - 1)) ny--;
|
|
281
|
+
else if (!solid(nx, ny - 2)) ny -= 2;
|
|
282
|
+
else { cdir[i] = -cdir[i]; return; }
|
|
283
|
+
}
|
|
284
|
+
cx[i] = nx; cy[i] = ny;
|
|
285
|
+
if (!solid(nx, ny + 1)) { cst[i] = FALL; cfall[i] = 0; }
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
case FALL:
|
|
289
|
+
if (solid(x, y + 1)) cst[i] = cfall[i] > FATAL ? DEAD : WALK;
|
|
290
|
+
else if (y + 1 >= PH - 1) cst[i] = DEAD;
|
|
291
|
+
else { cy[i] = y + 1; cfall[i]++; }
|
|
292
|
+
break;
|
|
293
|
+
case DIG:
|
|
294
|
+
if (++ct[i] < 2) return;
|
|
295
|
+
ct[i] = 0;
|
|
296
|
+
if (!(solid(x - 1, y + 1) || solid(x, y + 1) || solid(x + 1, y + 1))) {
|
|
297
|
+
cst[i] = FALL; cfall[i] = 0; return;
|
|
298
|
+
}
|
|
299
|
+
setpix(x - 1, y + 1, 0);
|
|
300
|
+
setpix(x, y + 1, 0);
|
|
301
|
+
setpix(x + 1, y + 1, 0);
|
|
302
|
+
cy[i] = y + 1;
|
|
303
|
+
break;
|
|
304
|
+
case BUILD: {
|
|
305
|
+
if (++ct[i] < 4) return;
|
|
306
|
+
ct[i] = 0;
|
|
307
|
+
char d = cdir[i];
|
|
308
|
+
if (cbr[i] == 0 || solid(x + d, y - 1)) { cst[i] = WALK; return; }
|
|
309
|
+
setpix(x + d, y, 2);
|
|
310
|
+
setpix(x + 2 * d, y, 2);
|
|
311
|
+
cx[i] = x + d; cy[i] = y - 1;
|
|
312
|
+
cbr[i]--;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
default: // BLOCK and DEAD do nothing
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Merge one cell of creature i's body: rows y0..y1 (same cell) at x.
|
|
321
|
+
static inline void draw_cell(char k, char x, char y0, char y1)
|
|
322
|
+
{
|
|
323
|
+
char * cell = rowp[y0 >> 3] + (x >> 2);
|
|
324
|
+
char u = *cell;
|
|
325
|
+
under[k] = u;
|
|
326
|
+
ucell[k] = cell;
|
|
327
|
+
const char * s = FONT + (unsigned)u * 8;
|
|
328
|
+
char * d = FONT + (unsigned)(CR_BASE + k) * 8;
|
|
329
|
+
d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; d[3] = s[3];
|
|
330
|
+
d[4] = s[4]; d[5] = s[5]; d[6] = s[6]; d[7] = s[7];
|
|
331
|
+
char m = pmask[x & 3];
|
|
332
|
+
for (char r = y0 & 7; r <= (y1 & 7); r++) d[r] |= m; // pair 11: colour RAM
|
|
333
|
+
*cell = CR_BASE + k;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
static void draw_all(void)
|
|
337
|
+
{
|
|
338
|
+
for (char i = 0; i < ncr; i++) {
|
|
339
|
+
if (cst[i] == DEAD) { ncell[i] = 0; continue; }
|
|
340
|
+
char x = cx[i], y = cy[i], top = y - 3;
|
|
341
|
+
if ((top >> 3) == (y >> 3)) {
|
|
342
|
+
draw_cell(2 * i, x, top, y);
|
|
343
|
+
ncell[i] = 1;
|
|
344
|
+
} else {
|
|
345
|
+
draw_cell(2 * i, x, top, top | 7);
|
|
346
|
+
draw_cell(2 * i + 1, x, y & 0xf8, y);
|
|
347
|
+
ncell[i] = 2;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Restore in reverse draw order, so stacked creatures unwind.
|
|
353
|
+
static void erase_all(void)
|
|
354
|
+
{
|
|
355
|
+
char i = ncr;
|
|
356
|
+
while (i) {
|
|
357
|
+
i--;
|
|
358
|
+
if (ncell[i] == 2) *ucell[2 * i + 1] = under[2 * i + 1];
|
|
359
|
+
if (ncell[i]) *ucell[2 * i] = under[2 * i];
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
static void tick(unsigned t)
|
|
364
|
+
{
|
|
365
|
+
if ((t & (REL - 1)) == 0 && ncr < NC) {
|
|
366
|
+
char i = ncr++;
|
|
367
|
+
cx[i] = HX; cy[i] = HY; cdir[i] = 1; cst[i] = FALL;
|
|
368
|
+
cfall[i] = 0; ct[i] = 0; cbr[i] = 0; ncell[i] = 0;
|
|
369
|
+
}
|
|
370
|
+
for (char k = 0; k < 4; k++) {
|
|
371
|
+
if (sc_tick[k] == t) {
|
|
372
|
+
char i = sc_who[k];
|
|
373
|
+
if (i < ncr && cst[i] == WALK) {
|
|
374
|
+
cst[i] = sc_role[k]; ct[i] = 0;
|
|
375
|
+
if (sc_role[k] == BUILD) cbr[i] = BRICKS;
|
|
376
|
+
if (sc_role[k] == BLOCK) { blk_x[nblk] = cx[i]; blk_y[nblk] = cy[i]; nblk++; }
|
|
377
|
+
} else ignored++;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
for (char i = 0; i < ncr; i++) {
|
|
381
|
+
char s = cst[i];
|
|
382
|
+
timer_start();
|
|
383
|
+
update(i);
|
|
384
|
+
unsigned c = timer_stop();
|
|
385
|
+
if (s < BLOCK && c > tmax[s]) tmax[s] = c;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
static char fa, fb; // Fletcher-16 sums
|
|
390
|
+
static void fl_add(char v)
|
|
391
|
+
{
|
|
392
|
+
unsigned a = fa + v;
|
|
393
|
+
if (a >= 255) a -= 255;
|
|
394
|
+
fa = a;
|
|
395
|
+
unsigned b = fb + fa;
|
|
396
|
+
if (b >= 255) b -= 255;
|
|
397
|
+
fb = b;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
static unsigned terrain_cs(void)
|
|
401
|
+
{
|
|
402
|
+
fa = fb = 0;
|
|
403
|
+
for (char r = 0; r < LH; r++)
|
|
404
|
+
for (char c = 0; c < LW; c++) {
|
|
405
|
+
const char * g = FONT + (unsigned)rowp[r][c] * 8;
|
|
406
|
+
for (char k = 0; k < 8; k++) fl_add(g[k]);
|
|
407
|
+
}
|
|
408
|
+
return (unsigned)fb << 8 | fa;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
static unsigned creature_cs(void)
|
|
412
|
+
{
|
|
413
|
+
fa = fb = 0;
|
|
414
|
+
for (char i = 0; i < ncr; i++) {
|
|
415
|
+
fl_add(cx[i]); fl_add(cy[i]); fl_add(cst[i]); fl_add((char)cdir[i]);
|
|
416
|
+
}
|
|
417
|
+
return (unsigned)fb << 8 | fa;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Snapshot for the stress tick, so the screen shows the scenario after it.
|
|
421
|
+
static char save_scr[LH * LW], save_pool[POOLN * 8];
|
|
422
|
+
static char save_x[NC], save_y[NC], save_st[NC], save_nf, save_used;
|
|
423
|
+
static char save_free[POOLN];
|
|
424
|
+
|
|
425
|
+
static void snapshot(void)
|
|
426
|
+
{
|
|
427
|
+
for (char r = 0; r < LH; r++) memcpy(save_scr + r * LW, rowp[r], LW);
|
|
428
|
+
memcpy(save_pool, FONT + POOL0 * 8, POOLN * 8);
|
|
429
|
+
memcpy(save_free, freel, POOLN);
|
|
430
|
+
memcpy(save_x, cx, NC); memcpy(save_y, cy, NC); memcpy(save_st, cst, NC);
|
|
431
|
+
save_nf = nfree; save_used = used;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
static void restore(void)
|
|
435
|
+
{
|
|
436
|
+
for (char r = 0; r < LH; r++) memcpy(rowp[r], save_scr + r * LW, LW);
|
|
437
|
+
memcpy(FONT + POOL0 * 8, save_pool, POOLN * 8);
|
|
438
|
+
memcpy(freel, save_free, POOLN);
|
|
439
|
+
memcpy(cx, save_x, NC); memcpy(cy, save_y, NC); memcpy(cst, save_st, NC);
|
|
440
|
+
nfree = save_nf; used = save_used;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
static unsigned s_tick, s_erase, s_upd, s_draw;
|
|
444
|
+
|
|
445
|
+
// One whole tick with every creature about to act in state st.
|
|
446
|
+
static void stress(char st)
|
|
447
|
+
{
|
|
448
|
+
for (char i = 0; i < NC; i++) {
|
|
449
|
+
cx[i] = 4 + 6 * i; cy[i] = 119; cdir[i] = 1; cst[i] = st;
|
|
450
|
+
ct[i] = st == DIG ? 1 : 3; cfall[i] = 0; cbr[i] = BRICKS;
|
|
451
|
+
}
|
|
452
|
+
draw_all();
|
|
453
|
+
vic_waitFrame();
|
|
454
|
+
cia2.crb = 0x00;
|
|
455
|
+
cia2.tb = 0xffff;
|
|
456
|
+
cia2.crb = 0x11;
|
|
457
|
+
timer_start();
|
|
458
|
+
erase_all();
|
|
459
|
+
s_erase = timer_stop();
|
|
460
|
+
timer_start();
|
|
461
|
+
for (char i = 0; i < NC; i++) update(i);
|
|
462
|
+
s_upd = timer_stop();
|
|
463
|
+
timer_start();
|
|
464
|
+
draw_all();
|
|
465
|
+
s_draw = timer_stop();
|
|
466
|
+
cia2.crb = 0x00;
|
|
467
|
+
s_tick = 0xffff - cia2.tb;
|
|
468
|
+
erase_all();
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// The creatures' own logic at its worst: every creature walks into a
|
|
472
|
+
// 2-pixel brick wall and climbs it (four probes and a blocker check).
|
|
473
|
+
// The bricks are laid before the timer starts; only the updates are timed.
|
|
474
|
+
static unsigned w_upd;
|
|
475
|
+
static void stress_walk(void)
|
|
476
|
+
{
|
|
477
|
+
for (char i = 0; i < NC; i++) {
|
|
478
|
+
cx[i] = 4 + 6 * i; cy[i] = 119; cdir[i] = 1; cst[i] = WALK;
|
|
479
|
+
ct[i] = 0; cfall[i] = 0;
|
|
480
|
+
}
|
|
481
|
+
for (char i = 0; i < NC; i++) {
|
|
482
|
+
setpix(5 + 6 * i, 119, 2); setpix(5 + 6 * i, 118, 2);
|
|
483
|
+
}
|
|
484
|
+
vic_waitFrame();
|
|
485
|
+
timer_start();
|
|
486
|
+
for (char i = 0; i < NC; i++) update(i);
|
|
487
|
+
w_upd = timer_stop();
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
int main(void)
|
|
491
|
+
{
|
|
492
|
+
__asm { sei }
|
|
493
|
+
|
|
494
|
+
mmap_set(MMAP_CHAR_ROM);
|
|
495
|
+
memcpy(FONT, CHAR_ROM, 512); // codes $00-$3F for the HUD
|
|
496
|
+
mmap_set(MMAP_ROM);
|
|
497
|
+
memset(FONT + G_EMPTY * 8, 0, 8);
|
|
498
|
+
memcpy(FONT + G_EARTH * 8, earth_rows, 8);
|
|
499
|
+
memcpy(FONT + G_STEP2 * 8, step2_rows, 8);
|
|
500
|
+
for (char i = 0; i < POOLN; i++) freel[i] = 0xff - i;
|
|
501
|
+
nfree = POOLN;
|
|
502
|
+
|
|
503
|
+
memset(SCREEN, 0x20, 1000);
|
|
504
|
+
memset(COLOUR, VCOL_WHITE, 1000);
|
|
505
|
+
for (char r = 0; r < LH; r++) {
|
|
506
|
+
char * p = SCREEN + 40 * (LY0 + r);
|
|
507
|
+
rowp[r] = p;
|
|
508
|
+
for (char c = 0; c < LW; c++) {
|
|
509
|
+
char ch = level[r][c];
|
|
510
|
+
p[c] = ch == '#' ? G_EARTH : ch == 's' ? G_STEP2 : G_EMPTY;
|
|
511
|
+
p[c + 0xd400] = 8 | VCOL_WHITE; // multicolour cell, pair 11 white
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
vic.color_back = VCOL_BLACK;
|
|
515
|
+
vic.color_back1 = VCOL_BROWN; // pair 01
|
|
516
|
+
vic.color_back2 = VCOL_ORANGE; // pair 10: earth texture and bricks
|
|
517
|
+
vic.color_border = VCOL_BLACK;
|
|
518
|
+
vic.ctrl2 |= 0x10; // multicolour text
|
|
519
|
+
vic.memptr = 0x1e;
|
|
520
|
+
|
|
521
|
+
// Probes: 160 along one pixel row, and the same loop with no probe,
|
|
522
|
+
// timed with the display off so no badline steals cycles.
|
|
523
|
+
vic.ctrl1 &= ~0x10;
|
|
524
|
+
vic_waitFrame(); vic_waitFrame();
|
|
525
|
+
timer_start();
|
|
526
|
+
probe_row(40);
|
|
527
|
+
unsigned t_probe = timer_stop();
|
|
528
|
+
timer_start();
|
|
529
|
+
empty_row(40);
|
|
530
|
+
unsigned t_empty = timer_stop();
|
|
531
|
+
vic.ctrl1 |= 0x10;
|
|
532
|
+
|
|
533
|
+
for (unsigned t = 0; t < TICKS; t++) {
|
|
534
|
+
vic_waitFrame();
|
|
535
|
+
cia2.crb = 0x00; // whole tick, CIA2 timer B
|
|
536
|
+
cia2.tb = 0xffff;
|
|
537
|
+
cia2.crb = 0x11;
|
|
538
|
+
timer_start();
|
|
539
|
+
erase_all();
|
|
540
|
+
t_erase = timer_stop();
|
|
541
|
+
tick(t);
|
|
542
|
+
timer_start();
|
|
543
|
+
draw_all();
|
|
544
|
+
t_draw = timer_stop();
|
|
545
|
+
cia2.crb = 0x00;
|
|
546
|
+
t_tick = 0xffff - cia2.tb;
|
|
547
|
+
if (t_tick > max_tick) max_tick = t_tick;
|
|
548
|
+
if (t_erase > max_erase) max_erase = t_erase;
|
|
549
|
+
if (t_draw > max_draw) max_draw = t_draw;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
erase_all();
|
|
553
|
+
unsigned tcs = terrain_cs(), ccs = creature_cs();
|
|
554
|
+
char dead = 0;
|
|
555
|
+
for (char i = 0; i < ncr; i++) if (cst[i] == DEAD) dead++;
|
|
556
|
+
char fault = 0;
|
|
557
|
+
if (tcs != EXP_TERRAIN) fault |= 1;
|
|
558
|
+
if (ccs != EXP_CREATURES) fault |= 2;
|
|
559
|
+
if (used != EXP_USED || peak != EXP_PEAK || freed != EXP_FREED) fault |= 4;
|
|
560
|
+
char was_freed = freed;
|
|
561
|
+
if (dead != EXP_DEAD || ignored || refused) fault |= 8;
|
|
562
|
+
|
|
563
|
+
// Worst frames, each from a snapshot of the scenario: the terrain
|
|
564
|
+
// edits of 24 dig steps alone, then a whole tick with every creature
|
|
565
|
+
// on a dig step, then one with every creature laying a brick, then
|
|
566
|
+
// the updates alone of every creature climbing a 2-pixel wall. Each
|
|
567
|
+
// creature stands on the floor in its own column on shared earth.
|
|
568
|
+
snapshot();
|
|
569
|
+
char was_peak = peak;
|
|
570
|
+
timer_start();
|
|
571
|
+
for (char i = 0; i < NC; i++) {
|
|
572
|
+
char x = 4 + 6 * i;
|
|
573
|
+
setpix(x - 1, 120, 0); setpix(x, 120, 0); setpix(x + 1, 120, 0);
|
|
574
|
+
}
|
|
575
|
+
unsigned s_edit = timer_stop();
|
|
576
|
+
if (terrain_cs() != EXP_S_TERRAIN || used != EXP_S_USED) fault |= 16;
|
|
577
|
+
restore();
|
|
578
|
+
unsigned s_step = 0; // the same edits, one dig step at a time
|
|
579
|
+
for (char i = 0; i < NC; i++) {
|
|
580
|
+
char x = 4 + 6 * i;
|
|
581
|
+
timer_start();
|
|
582
|
+
setpix(x - 1, 120, 0); setpix(x, 120, 0); setpix(x + 1, 120, 0);
|
|
583
|
+
unsigned c = timer_stop();
|
|
584
|
+
if (c > s_step) s_step = c;
|
|
585
|
+
}
|
|
586
|
+
if (terrain_cs() != EXP_S_TERRAIN || used != EXP_S_USED) fault |= 16;
|
|
587
|
+
restore();
|
|
588
|
+
stress(DIG);
|
|
589
|
+
if (terrain_cs() != EXP_S_TERRAIN || creature_cs() != EXP_S_CREATURES
|
|
590
|
+
|| used != EXP_S_USED) fault |= 16;
|
|
591
|
+
unsigned d_tick = s_tick, d_erase = s_erase, d_upd = s_upd, d_draw = s_draw;
|
|
592
|
+
char d_used = used;
|
|
593
|
+
unsigned d_cs = terrain_cs();
|
|
594
|
+
restore();
|
|
595
|
+
stress(BUILD);
|
|
596
|
+
if (terrain_cs() != EXP_B_TERRAIN || creature_cs() != EXP_B_CREATURES
|
|
597
|
+
|| used != EXP_B_USED) fault |= 32;
|
|
598
|
+
char b_used = used;
|
|
599
|
+
restore();
|
|
600
|
+
stress_walk();
|
|
601
|
+
if (terrain_cs() != EXP_W_TERRAIN || creature_cs() != EXP_W_CREATURES
|
|
602
|
+
|| used != EXP_W_USED) fault |= 64;
|
|
603
|
+
restore();
|
|
604
|
+
draw_all(); // the scenario, as the model left it
|
|
605
|
+
|
|
606
|
+
put_str(SCREEN, "TICK 00000 PASS POOL 000 PEAK 000");
|
|
607
|
+
put_str(SCREEN + 40, "ALIVE 00 DEAD 00 REFUSED 000 CODE 00");
|
|
608
|
+
put_str(SCREEN + 80, "TERRAIN 0000 CREATURES 0000 FREED 000");
|
|
609
|
+
put_str(SCREEN + 120, "WALK 00000 FALL 00000 PROBE 00000");
|
|
610
|
+
put_str(SCREEN + 160, "DIG 00000 BUILD 00000 EMPTY 00000");
|
|
611
|
+
put_str(SCREEN + 200, "TICK MAX 00000 ERASE 00000 DRAW 00000");
|
|
612
|
+
put_str(SCREEN + 240, "DIGS 00000 E 00000 U 00000 D 00000");
|
|
613
|
+
put_str(SCREEN + 280, "BRICKS 00000 U 00000 EDIT 00000 W 00000");
|
|
614
|
+
put_str(SCREEN + 320, "POOL D 000 B 000 TERRAIN 0000 STEP 00000");
|
|
615
|
+
put_dec(SCREEN + 5, TICKS, 5);
|
|
616
|
+
if (fault) put_str(SCREEN + 11, "FAIL");
|
|
617
|
+
put_dec(SCREEN + 22, used, 3);
|
|
618
|
+
put_dec(SCREEN + 31, was_peak, 3);
|
|
619
|
+
put_dec(SCREEN + 46, NC - dead, 2);
|
|
620
|
+
put_dec(SCREEN + 54, dead, 2);
|
|
621
|
+
put_dec(SCREEN + 65, refused, 3);
|
|
622
|
+
put_dec(SCREEN + 74, fault, 2);
|
|
623
|
+
put_hex(SCREEN + 88, tcs);
|
|
624
|
+
put_hex(SCREEN + 103, ccs);
|
|
625
|
+
put_dec(SCREEN + 114, was_freed, 3);
|
|
626
|
+
put_dec(SCREEN + 125, tmax[WALK], 5);
|
|
627
|
+
put_dec(SCREEN + 136, tmax[FALL], 5);
|
|
628
|
+
put_dec(SCREEN + 148, t_probe, 5);
|
|
629
|
+
put_dec(SCREEN + 165, tmax[DIG], 5);
|
|
630
|
+
put_dec(SCREEN + 177, tmax[BUILD], 5);
|
|
631
|
+
put_dec(SCREEN + 189, t_empty, 5);
|
|
632
|
+
put_dec(SCREEN + 209, max_tick, 5);
|
|
633
|
+
put_dec(SCREEN + 221, max_erase, 5);
|
|
634
|
+
put_dec(SCREEN + 232, max_draw, 5);
|
|
635
|
+
put_dec(SCREEN + 245, d_tick, 5);
|
|
636
|
+
put_dec(SCREEN + 253, d_erase, 5);
|
|
637
|
+
put_dec(SCREEN + 261, d_upd, 5);
|
|
638
|
+
put_dec(SCREEN + 269, d_draw, 5);
|
|
639
|
+
put_dec(SCREEN + 287, s_tick, 5);
|
|
640
|
+
put_dec(SCREEN + 295, s_upd, 5);
|
|
641
|
+
put_dec(SCREEN + 306, s_edit, 5);
|
|
642
|
+
put_dec(SCREEN + 314, w_upd, 5);
|
|
643
|
+
put_dec(SCREEN + 327, d_used, 3);
|
|
644
|
+
put_dec(SCREEN + 333, b_used, 3);
|
|
645
|
+
put_hex(SCREEN + 345, d_cs);
|
|
646
|
+
put_dec(SCREEN + 355, s_step, 5);
|
|
647
|
+
|
|
648
|
+
RESULT = fault ? 2 : 1;
|
|
649
|
+
vic.color_border = fault ? VCOL_RED : VCOL_GREEN;
|
|
650
|
+
for (;;) ;
|
|
651
|
+
return 0;
|
|
652
|
+
}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
## Build
|
|
656
|
+
|
|
657
|
+
```bash
|
|
658
|
+
oscar64 -tm=c64 -O2 -o=destructible-terrain.prg destructible-terrain.c
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
Oscar64 1.32.271 builds it with no warnings; the PRG is 20,540 bytes.
|
|
662
|
+
Most of that is the gap between the BASIC start and $4000: the
|
|
663
|
+
`#pragma region` puts code and data above the charset at $3800, so
|
|
664
|
+
nothing can grow into it (`charset_blit_overruns_grown_code`).
|
|
665
|
+
|
|
666
|
+
The `EXP_` constants come from this script (Python 3). It keeps the
|
|
667
|
+
level as a 160 by 128 array of pixel pairs and the creatures as
|
|
668
|
+
objects, with no glyphs, codes or pool. A cell counts as holding a
|
|
669
|
+
private glyph when its pixels match none of the three shared glyphs;
|
|
670
|
+
that is the pool count the C must reach by allocating and freeing. It
|
|
671
|
+
prints the `#define` block in the listing.
|
|
672
|
+
|
|
673
|
+
```python
|
|
674
|
+
# terrain_model.py: the reference for destructible-terrain.c. Same level,
|
|
675
|
+
# same rules, same order of work per tick, kept as a pixel array and
|
|
676
|
+
# objects rather than glyphs and screen codes. Prints the checksums and
|
|
677
|
+
# counts the listing compiles in.
|
|
678
|
+
W, H = 160, 128 # multicolour pixels: 40 x 16 cells of 4 x 8
|
|
679
|
+
EMPTY, EARTH, STEP2 = 0, 1, 2
|
|
680
|
+
EARTH_ROWS = [0x56, 0x65, 0x59, 0x95, 0x56, 0x65, 0x59, 0x95]
|
|
681
|
+
STEP2_ROWS = [0, 0, 0, 0, 0, 0, 0xAA, 0xAA]
|
|
682
|
+
GLYPHS = {EMPTY: [0] * 8, EARTH: EARTH_ROWS, STEP2: STEP2_ROWS}
|
|
683
|
+
LEVEL = [
|
|
684
|
+
"........................................",
|
|
685
|
+
"........................................",
|
|
686
|
+
"........................................",
|
|
687
|
+
"........................................",
|
|
688
|
+
"#...........s...........................",
|
|
689
|
+
"#######################.................",
|
|
690
|
+
"#######################.................",
|
|
691
|
+
"#.......................................",
|
|
692
|
+
"#...............................########",
|
|
693
|
+
"###############################.........",
|
|
694
|
+
"#......................................#",
|
|
695
|
+
"#......................................#",
|
|
696
|
+
"#......................................#",
|
|
697
|
+
"#......................................#",
|
|
698
|
+
"#......................................#",
|
|
699
|
+
"########################################",
|
|
700
|
+
]
|
|
701
|
+
N, HX, HY, REL_EVERY = 24, 22, 12, 8
|
|
702
|
+
FATAL, BRICKS, TICKS = 32, 12, 360
|
|
703
|
+
WALK, FALL, DIG, BUILD, BLOCK, DEAD = 0, 1, 2, 3, 4, 5
|
|
704
|
+
SCRIPT = [(70, 0, BLOCK), (120, 3, DIG), (157, 8, DIG), (246, 4, BUILD)]
|
|
705
|
+
POOL = 128
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
def pair(byte, i):
|
|
709
|
+
return (byte >> (6 - 2 * i)) & 3
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
class World:
|
|
713
|
+
def __init__(self):
|
|
714
|
+
self.px = [[0] * W for _ in range(H)]
|
|
715
|
+
for cy, row in enumerate(LEVEL):
|
|
716
|
+
for cx, ch in enumerate(row):
|
|
717
|
+
g = GLYPHS[{'.': EMPTY, '#': EARTH, 's': STEP2}[ch]]
|
|
718
|
+
for r in range(8):
|
|
719
|
+
for i in range(4):
|
|
720
|
+
self.px[cy * 8 + r][cx * 4 + i] = pair(g[r], i)
|
|
721
|
+
self.cr = []
|
|
722
|
+
self.refused = 0
|
|
723
|
+
self.ignored = 0
|
|
724
|
+
self.peak = 0
|
|
725
|
+
self.freed = 0
|
|
726
|
+
|
|
727
|
+
def solid(self, x, y):
|
|
728
|
+
if x < 0 or x >= W or y < 0 or y >= H:
|
|
729
|
+
return True
|
|
730
|
+
return self.px[y][x] != 0
|
|
731
|
+
|
|
732
|
+
def cell_private(self, cx, cy):
|
|
733
|
+
rows = []
|
|
734
|
+
for r in range(8):
|
|
735
|
+
b = 0
|
|
736
|
+
for i in range(4):
|
|
737
|
+
b = (b << 2) | self.px[cy * 8 + r][cx * 4 + i]
|
|
738
|
+
rows.append(b)
|
|
739
|
+
return rows not in GLYPHS.values()
|
|
740
|
+
|
|
741
|
+
def used(self):
|
|
742
|
+
return sum(self.cell_private(cx, cy) for cy in range(16) for cx in range(40))
|
|
743
|
+
|
|
744
|
+
def setpix(self, x, y, v):
|
|
745
|
+
if x < 0 or x >= W or y < 0 or y >= H or self.px[y][x] == v:
|
|
746
|
+
return
|
|
747
|
+
cx, cy = x >> 2, y >> 3
|
|
748
|
+
if not self.cell_private(cx, cy) and self.used() >= POOL:
|
|
749
|
+
self.refused += 1
|
|
750
|
+
return
|
|
751
|
+
was = self.cell_private(cx, cy)
|
|
752
|
+
self.px[y][x] = v
|
|
753
|
+
if was and not self.cell_private(cx, cy):
|
|
754
|
+
self.freed += 1
|
|
755
|
+
self.peak = max(self.peak, self.used())
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
class Cr:
|
|
759
|
+
def __init__(self):
|
|
760
|
+
self.x, self.y, self.dir, self.st = HX, HY, 1, FALL
|
|
761
|
+
self.fall, self.t, self.bricks = 0, 0, 0
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
def blocked(w, me, nx, y):
|
|
765
|
+
for c in w.cr:
|
|
766
|
+
if c is not me and c.st == BLOCK and c.x == nx and abs(c.y - y) < 4:
|
|
767
|
+
return True
|
|
768
|
+
return False
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
def update(w, c):
|
|
772
|
+
if c.st == WALK:
|
|
773
|
+
nx = c.x + c.dir
|
|
774
|
+
if nx < 0 or nx >= W or blocked(w, c, nx, c.y):
|
|
775
|
+
c.dir = -c.dir
|
|
776
|
+
return
|
|
777
|
+
ny = c.y
|
|
778
|
+
if w.solid(nx, ny):
|
|
779
|
+
if not w.solid(nx, ny - 1):
|
|
780
|
+
ny -= 1
|
|
781
|
+
elif not w.solid(nx, ny - 2):
|
|
782
|
+
ny -= 2
|
|
783
|
+
else:
|
|
784
|
+
c.dir = -c.dir
|
|
785
|
+
return
|
|
786
|
+
c.x, c.y = nx, ny
|
|
787
|
+
if not w.solid(c.x, c.y + 1):
|
|
788
|
+
c.st, c.fall = FALL, 0
|
|
789
|
+
elif c.st == FALL:
|
|
790
|
+
if w.solid(c.x, c.y + 1):
|
|
791
|
+
c.st = DEAD if c.fall > FATAL else WALK
|
|
792
|
+
elif c.y + 1 >= H - 1:
|
|
793
|
+
c.st = DEAD
|
|
794
|
+
else:
|
|
795
|
+
c.y += 1
|
|
796
|
+
c.fall += 1
|
|
797
|
+
elif c.st == DIG:
|
|
798
|
+
c.t += 1
|
|
799
|
+
if c.t < 2:
|
|
800
|
+
return
|
|
801
|
+
c.t = 0
|
|
802
|
+
if not (w.solid(c.x - 1, c.y + 1) or w.solid(c.x, c.y + 1)
|
|
803
|
+
or w.solid(c.x + 1, c.y + 1)):
|
|
804
|
+
c.st, c.fall = FALL, 0
|
|
805
|
+
return
|
|
806
|
+
for dx in (-1, 0, 1):
|
|
807
|
+
w.setpix(c.x + dx, c.y + 1, 0)
|
|
808
|
+
c.y += 1
|
|
809
|
+
elif c.st == BUILD:
|
|
810
|
+
c.t += 1
|
|
811
|
+
if c.t < 4:
|
|
812
|
+
return
|
|
813
|
+
c.t = 0
|
|
814
|
+
if c.bricks == 0 or w.solid(c.x + c.dir, c.y - 1):
|
|
815
|
+
c.st = WALK
|
|
816
|
+
return
|
|
817
|
+
w.setpix(c.x + c.dir, c.y, 2)
|
|
818
|
+
w.setpix(c.x + 2 * c.dir, c.y, 2)
|
|
819
|
+
c.x += c.dir
|
|
820
|
+
c.y -= 1
|
|
821
|
+
c.bricks -= 1
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
class Fletcher:
|
|
825
|
+
# Fletcher-16: two running sums modulo 255, so order counts.
|
|
826
|
+
def __init__(self):
|
|
827
|
+
self.a = self.b = 0
|
|
828
|
+
|
|
829
|
+
def add(self, v):
|
|
830
|
+
self.a = (self.a + v) % 255
|
|
831
|
+
self.b = (self.b + self.a) % 255
|
|
832
|
+
|
|
833
|
+
def value(self):
|
|
834
|
+
return self.b << 8 | self.a
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
def terrain_cs(w):
|
|
838
|
+
cs = Fletcher()
|
|
839
|
+
for cy in range(16):
|
|
840
|
+
for cx in range(40):
|
|
841
|
+
for r in range(8):
|
|
842
|
+
b = 0
|
|
843
|
+
for i in range(4):
|
|
844
|
+
b = (b << 2) | w.px[cy * 8 + r][cx * 4 + i]
|
|
845
|
+
cs.add(b)
|
|
846
|
+
return cs.value()
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
def creature_cs(w):
|
|
850
|
+
cs = Fletcher()
|
|
851
|
+
for c in w.cr:
|
|
852
|
+
for v in (c.x, c.y, c.st, c.dir & 0xFF):
|
|
853
|
+
cs.add(v)
|
|
854
|
+
return cs.value()
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
def tick(w, t):
|
|
858
|
+
if t % REL_EVERY == 0 and len(w.cr) < N:
|
|
859
|
+
w.cr.append(Cr())
|
|
860
|
+
for (st, i, role) in SCRIPT:
|
|
861
|
+
if st == t:
|
|
862
|
+
if i < len(w.cr) and w.cr[i].st == WALK:
|
|
863
|
+
w.cr[i].st, w.cr[i].t = role, 0
|
|
864
|
+
if role == BUILD:
|
|
865
|
+
w.cr[i].bricks = BRICKS
|
|
866
|
+
else:
|
|
867
|
+
w.ignored += 1
|
|
868
|
+
for c in w.cr:
|
|
869
|
+
update(w, c)
|
|
870
|
+
|
|
871
|
+
|
|
872
|
+
def run():
|
|
873
|
+
w = World()
|
|
874
|
+
for t in range(TICKS):
|
|
875
|
+
tick(w, t)
|
|
876
|
+
return w
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
def stress(w, st):
|
|
880
|
+
# Worst frame: every creature on the floor in its own column, about
|
|
881
|
+
# to take a dig step (st = DIG) or lay a brick (st = BUILD).
|
|
882
|
+
for i, c in enumerate(w.cr):
|
|
883
|
+
c.x, c.y, c.dir, c.st, c.fall = 4 + 6 * i, 119, 1, st, 0
|
|
884
|
+
c.t, c.bricks = (1 if st == DIG else 3), BRICKS
|
|
885
|
+
for c in w.cr:
|
|
886
|
+
update(w, c)
|
|
887
|
+
return terrain_cs(w), creature_cs(w), w.used()
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
def stress_walk(w):
|
|
891
|
+
# Worst walk: every creature walking into a 2-pixel brick wall it can
|
|
892
|
+
# climb, each in its own column. The bricks are laid before the tick.
|
|
893
|
+
for i, c in enumerate(w.cr):
|
|
894
|
+
c.x, c.y, c.dir, c.st, c.fall, c.t = 4 + 6 * i, 119, 1, WALK, 0, 0
|
|
895
|
+
for i in range(len(w.cr)):
|
|
896
|
+
w.setpix(5 + 6 * i, 119, 2)
|
|
897
|
+
w.setpix(5 + 6 * i, 118, 2)
|
|
898
|
+
for c in w.cr:
|
|
899
|
+
update(w, c)
|
|
900
|
+
return terrain_cs(w), creature_cs(w), w.used()
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
if __name__ == '__main__':
|
|
904
|
+
import copy
|
|
905
|
+
w = run()
|
|
906
|
+
st = [c.st for c in w.cr]
|
|
907
|
+
print("// walk %d fall %d dig %d build %d block %d dead %d" %
|
|
908
|
+
tuple(st.count(s) for s in range(6)))
|
|
909
|
+
print("// refused %d ignored %d" % (w.refused, w.ignored))
|
|
910
|
+
print("#define EXP_TERRAIN 0x%04x" % terrain_cs(w))
|
|
911
|
+
print("#define EXP_CREATURES 0x%04x" % creature_cs(w))
|
|
912
|
+
print("#define EXP_USED %d" % w.used())
|
|
913
|
+
print("#define EXP_PEAK %d" % w.peak)
|
|
914
|
+
print("#define EXP_FREED %d" % w.freed)
|
|
915
|
+
print("#define EXP_DEAD %d" % st.count(DEAD))
|
|
916
|
+
for tag, st in (("S", DIG), ("B", BUILD)):
|
|
917
|
+
t, c, u = stress(copy.deepcopy(w), st)
|
|
918
|
+
print("#define EXP_%s_TERRAIN 0x%04x" % (tag, t))
|
|
919
|
+
print("#define EXP_%s_CREATURES 0x%04x" % (tag, c))
|
|
920
|
+
print("#define EXP_%s_USED %d" % (tag, u))
|
|
921
|
+
t, c, u = stress_walk(copy.deepcopy(w))
|
|
922
|
+
print("#define EXP_W_TERRAIN 0x%04x" % t)
|
|
923
|
+
print("#define EXP_W_CREATURES 0x%04x" % c)
|
|
924
|
+
print("#define EXP_W_USED %d" % u)
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
## Expected output
|
|
928
|
+
|
|
929
|
+
Black background, green border. Rows 0 to 8 are a white HUD. Below it
|
|
930
|
+
the level in brown and orange: a left wall, the upper platform (cell
|
|
931
|
+
rows 5 and 6) with a 2-pixel step on it and a 6-pixel-wide shaft dug
|
|
932
|
+
through it, the lower floor (cell row 9) ending at pixel 123, a
|
|
933
|
+
twelve-brick orange staircase rising right from it to the ledge on the
|
|
934
|
+
right (cell row 8), a right wall and the bottom floor. Creatures are
|
|
935
|
+
white columns 1 multicolour pixel wide and 4 tall: the blocker on the
|
|
936
|
+
upper platform at pixel 61, two on the staircase and sixteen on the
|
|
937
|
+
ledge. The five dead are not drawn.
|
|
938
|
+
|
|
939
|
+
HUD at 20,000,000 cycles (measured in VICE x64sc 3.10, decoded with PIL
|
|
940
|
+
against the character ROM):
|
|
941
|
+
|
|
942
|
+
| Row | PAL | NTSC |
|
|
943
|
+
|---|---|---|
|
|
944
|
+
| 0 | `TICK 00360 PASS POOL 007 PEAK 007` | same |
|
|
945
|
+
| 1 | `ALIVE 19 DEAD 05 REFUSED 000 CODE 00` | same |
|
|
946
|
+
| 2 | `TERRAIN CB64 CREATURES 92CB FREED 002` | same |
|
|
947
|
+
| 3 | `WALK 00562 FALL 00241 PROBE 10912` | `WALK 00648 FALL 00241 PROBE 10912` |
|
|
948
|
+
| 4 | `DIG 01403 BUILD 01577 EMPTY 02580` | `DIG 01532 BUILD 01706 EMPTY 02580` |
|
|
949
|
+
| 5 | `TICK MAX 27280 ERASE 02033 DRAW 15024` | `TICK MAX 27802 ERASE 02033 DRAW 15234` |
|
|
950
|
+
| 6 | `DIGS 57299 E 01633 U 36431 D 19104` | `DIGS 57606 E 01633 U 36523 D 19319` |
|
|
951
|
+
| 7 | `BRICKS 56357 U 42071 EDIT 32037 W 14209` | `BRICKS 56701 U 42631 EDIT 32555 W 14465` |
|
|
952
|
+
| 8 | `POOL D 043 B 043 TERRAIN 627F STEP 01450` | same |
|
|
953
|
+
|
|
954
|
+
Every figure is in CPU cycles from a CIA timer, and every checked value
|
|
955
|
+
matches the model on both models.
|
|
956
|
+
|
|
957
|
+
| Field | What it is |
|
|
958
|
+
|---|---|
|
|
959
|
+
| POOL, PEAK, FREED | private glyphs in use after 360 ticks, the most in use at once, and how many went back to the pool |
|
|
960
|
+
| REFUSED, CODE | pool-full refusals (0); the fault bits, 0 on pass |
|
|
961
|
+
| TERRAIN, CREATURES | the two checksums after 360 ticks, equal to the model's |
|
|
962
|
+
| WALK, FALL, DIG, BUILD | the slowest single creature update seen in that state over the 360 ticks |
|
|
963
|
+
| PROBE, EMPTY | 160 probes along one pixel row, and the same loop with no probe, display off: (10,912 − 2,580) / 160 = 52 cycles a probe |
|
|
964
|
+
| TICK MAX, ERASE, DRAW | the slowest whole tick (restore, release, roles, 24 updates, draw), and the slowest restore and draw. TICK MAX includes the per-creature timer reads |
|
|
965
|
+
| DIGS | a whole tick with all 24 creatures on a dig step: restore (E), updates (U) and draw (D). Each digger clears 3 pixels and lands in two cells |
|
|
966
|
+
| BRICKS | a whole tick with all 24 laying a brick, and its updates (U) |
|
|
967
|
+
| EDIT | the 72 pixel edits of 24 dig steps alone, 36 of which take a new private glyph |
|
|
968
|
+
| W | the 24 updates alone, every creature walking into a 2-pixel brick wall and climbing it: four probes and a blocker check each, the most any walk makes. The bricks are laid before the timer starts. Without the per-creature timer reads it is 603 a creature on NTSC, under the 648 of WALK, which includes them |
|
|
969
|
+
| POOL D, B | glyphs in use after the dig and brick subjects; the terrain checksum after the digs |
|
|
970
|
+
| STEP | the dearest single dig step's terrain work, the same 72 edits timed one step (3 edits) at a time, including the timer calls. Half the steps span two cells and allocate twice, half allocate once |
|
|
971
|
+
|
|
972
|
+
The scenario ran as the model predicts. The blocker stands from tick 70.
|
|
973
|
+
The first digger goes through the upper platform in 16 steps and falls
|
|
974
|
+
16 pixels; the second, beside it from tick 157, widens the shaft so two
|
|
975
|
+
cells become empty again and are freed (FREED 002). The builder lays
|
|
976
|
+
its twelve bricks from tick 246. Before it finishes, three creatures
|
|
977
|
+
walk off the end of the lower floor and two off the top of the
|
|
978
|
+
unfinished staircase; they fall 48 to 57 pixels, more than the fatal
|
|
979
|
+
32, and die.
|
|
980
|
+
|
|
981
|
+
Two pinned runs per model gave byte-identical PNGs,
|
|
982
|
+
`screenshots/destructible-terrain.png` (PAL) and
|
|
983
|
+
`screenshots/destructible-terrain-ntsc.png` (NTSC). Measured on both with
|
|
984
|
+
this script: all 40,960 screen pixels of the level (160 by 128
|
|
985
|
+
multicolour pixels, two screen pixels each) equal the model's terrain
|
|
986
|
+
after tick 360 with the creature bodies drawn in white; 0 wrong. The
|
|
987
|
+
border is (98, 213, 50) on PAL and (114, 189, 103) on NTSC. The same
|
|
988
|
+
script run against the model at 359 ticks reports 56 wrong pixels, so
|
|
989
|
+
it can fail.
|
|
990
|
+
|
|
991
|
+
```python
|
|
992
|
+
# Every level pixel on the exit screenshot against the model after 360
|
|
993
|
+
# ticks: terrain pairs 00/01/10 as black/brown/orange, creature bodies
|
|
994
|
+
# (4 pixels tall, pair 11) white. One multicolour pixel is 2 screen pixels.
|
|
995
|
+
from PIL import Image
|
|
996
|
+
import terrain_model as m
|
|
997
|
+
COL = {'pal': {0: (0, 0, 0), 1: (119, 83, 0), 2: (183, 99, 30), 3: (255, 255, 255)},
|
|
998
|
+
'ntsc': {0: (0, 0, 0), 1: (151, 64, 0), 2: (196, 98, 65), 3: (255, 255, 255)}}
|
|
999
|
+
w = m.run()
|
|
1000
|
+
exp = [row[:] for row in w.px]
|
|
1001
|
+
for c in w.cr:
|
|
1002
|
+
if c.st != m.DEAD:
|
|
1003
|
+
for k in range(4):
|
|
1004
|
+
exp[c.y - k][c.x] = 3
|
|
1005
|
+
def check(png, model):
|
|
1006
|
+
px = Image.open(png).convert('RGB').load()
|
|
1007
|
+
top = (23 if model == 'ntsc' else 35) + 8 * 9
|
|
1008
|
+
bad = 0
|
|
1009
|
+
for y in range(m.H):
|
|
1010
|
+
for x in range(m.W):
|
|
1011
|
+
want = COL[model][exp[y][x]]
|
|
1012
|
+
for i in (0, 1):
|
|
1013
|
+
if px[32 + 2 * x + i, top + y] != want:
|
|
1014
|
+
bad += 1
|
|
1015
|
+
border = px[2, 100]
|
|
1016
|
+
return bad, border
|
|
1017
|
+
for png, model in (('pal1.png', 'pal'), ('ntsc1.png', 'ntsc')):
|
|
1018
|
+
bad, border = check(png, model)
|
|
1019
|
+
print(png, 'wrong pixels %d of %d, border %s' % (bad, 2 * m.W * m.H, border))
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
The listing can fail too. A copy with `EXP_TERRAIN` changed by one bit
|
|
1023
|
+
printed `FAIL`, `CODE 01` and a red border (175, 60, 88) on PAL.
|
|
1024
|
+
|
|
1025
|
+
**Pool exhaustion.** A copy built with a 32-code pool (`POOLN 32`) ran
|
|
1026
|
+
the scenario unchanged (it needs at most 7) and refused edits once the
|
|
1027
|
+
pool filled at 32: `REFUSED 083`, which is 23 in each of the EDIT, STEP
|
|
1028
|
+
and dig subjects and 14 in the brick subject, the counts the model
|
|
1029
|
+
gives with `POOL = 32`. Its terrain checksum after the digs, `E71F`,
|
|
1030
|
+
equals the model's, so the refusal rule matches. The climb subject
|
|
1031
|
+
needs 31 codes and passes. That copy prints `FAIL` with `CODE 48`
|
|
1032
|
+
(fault bits 16 and 32), because its compiled-in constants are the
|
|
1033
|
+
128-code ones.
|
|
1034
|
+
|
|
1035
|
+
## Why this works
|
|
1036
|
+
|
|
1037
|
+
**Private glyphs.** The VIC-II draws a cell from the 8 bytes of its
|
|
1038
|
+
code's glyph, so changing a glyph byte changes every cell that shows
|
|
1039
|
+
that code. Earth is shared by hundreds of cells, so an edit to it must
|
|
1040
|
+
not touch the shared glyph. `setpix()` first checks whether the pixel
|
|
1041
|
+
already has the wanted value; if it does, nothing is allocated. If the
|
|
1042
|
+
cell shows a shared code, it pops a free code, copies the 8 bytes and
|
|
1043
|
+
writes the new code into the cell, and only then edits the byte. After
|
|
1044
|
+
every edit it compares the private glyph with the three shared glyphs;
|
|
1045
|
+
on a match the cell gets the shared code back and the private code goes
|
|
1046
|
+
back on the free stack. One cell owns each private glyph, so no
|
|
1047
|
+
reference count is needed.
|
|
1048
|
+
|
|
1049
|
+
**Probes.** `solid(x, y)` turns a pixel into a cell (`x >> 2`,
|
|
1050
|
+
`y >> 3`), reads the screen code there, and tests the pixel pair at row
|
|
1051
|
+
`y & 7` of that code's glyph with a mask from `x & 3`. Any non-zero pair
|
|
1052
|
+
is terrain. It reads the terrain as the VIC shows it, so a creature
|
|
1053
|
+
never walks through a pixel that is drawn. Outside the level counts as
|
|
1054
|
+
solid; with `char` coordinates, `x - 1` at 0 is 255, which the same
|
|
1055
|
+
test catches. Creatures are not in the glyphs when probes run: every
|
|
1056
|
+
tick restores all creature cells first, updates, then draws.
|
|
1057
|
+
|
|
1058
|
+
**Creatures as merged glyphs.** Each creature owns two codes, $50 + 2i
|
|
1059
|
+
and $51 + 2i, for the one or two cells its 4-pixel body covers. The draw
|
|
1060
|
+
saves the code under each cell, copies that glyph into the creature's
|
|
1061
|
+
code and ORs pair 11 into the body rows. Pair 11 takes the colour from
|
|
1062
|
+
colour RAM, white here, so the body keeps its colour over any terrain.
|
|
1063
|
+
The restore runs in reverse order so that stacked creatures unwind, as
|
|
1064
|
+
in `char_bullets`. Hardware sprites would not do: the sixteen creatures
|
|
1065
|
+
on the ledge share the same raster lines.
|
|
1066
|
+
|
|
1067
|
+
**The state machine.** A walker moves one pixel; if the pixel ahead at
|
|
1068
|
+
foot level is solid it climbs 1 or 2 pixels when there is room, and
|
|
1069
|
+
otherwise turns. A walker that would step onto a blocker's column
|
|
1070
|
+
within 3 pixels of its height turns. With no ground under it, it starts
|
|
1071
|
+
to fall. A faller counts pixels and dies on landing after more than 32.
|
|
1072
|
+
A digger clears 3 pixels below its feet every second tick and moves
|
|
1073
|
+
down one; with nothing left under it, it falls. A builder lays a
|
|
1074
|
+
2-pixel brick level with its feet every fourth tick, steps up one
|
|
1075
|
+
pixel onto it, and stops after 12 bricks or when the pixel ahead of it,
|
|
1076
|
+
one above its feet, is solid. The model is written from this list, not from the C.
|
|
1077
|
+
|
|
1078
|
+
**Checks.** The terrain checksum reads the glyph bytes through the
|
|
1079
|
+
screen codes, so it checks the pool bookkeeping as well as the pixels:
|
|
1080
|
+
a cell left on a stale code, or a private glyph freed while still on
|
|
1081
|
+
screen, changes it. A position checksum over the creatures, the pool
|
|
1082
|
+
counts and the death count complete the verdict. The screenshot
|
|
1083
|
+
comparison checks what the VIC shows. `EXP_S_CREATURES` and
|
|
1084
|
+
`EXP_B_CREATURES` are both `0x2a72` by a coincidence of the checksum.
|
|
1085
|
+
The diggers end at (x, 120) in state dig (2), the builders at
|
|
1086
|
+
(x + 1, 118) in state build (3): x up 1, y down 2, state up 1. Fletcher-16
|
|
1087
|
+
weights consecutive bytes w, w − 1, w − 2, and
|
|
1088
|
+
w − 2(w − 1) + (w − 2) = 0, so neither sum moves (arithmetic). Each
|
|
1089
|
+
subject is also checked with its own terrain checksum, and those
|
|
1090
|
+
differ.
|
|
1091
|
+
|
|
1092
|
+
**Cost.** In this C, on NTSC, a creature update costs 241 cycles
|
|
1093
|
+
falling, up to 648 walking, up to 1,532 on a dig step and up to 1,706
|
|
1094
|
+
on a brick, and drawing one costs about 805 (19,319 / 24, two cells
|
|
1095
|
+
each). The dig subject, 57,606 cycles, is more than three NTSC frames
|
|
1096
|
+
of 17,095 (3.4). The scenario's slowest tick, 27,802, is more than one.
|
|
1097
|
+
The timed loop starts on line 256, so the run is not one tick per
|
|
1098
|
+
frame; the checks do not depend on that. `techniques/logic.md`, `creature_state_machine`,
|
|
1099
|
+
works out how many creatures fit a frame.
|