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,1094 @@
|
|
|
1
|
+
---
|
|
2
|
+
recipe: adventure-engine
|
|
3
|
+
toolchain: oscar64
|
|
4
|
+
output_format: PRG
|
|
5
|
+
region: both
|
|
6
|
+
techniques: [adventure_database_engine]
|
|
7
|
+
file_formats: [PRG]
|
|
8
|
+
uses_registers: [D011, D020, D021, DD04, DD05, DD06, DD07, DD0E, DD0F]
|
|
9
|
+
uses_kernal: [GETIN]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<!-- doc-type: recipe -->
|
|
13
|
+
|
|
14
|
+
# Oscar64 Adventure Engine: a two-word parser, an action table and packed text, played on autopilot
|
|
15
|
+
|
|
16
|
+
## Synopsis
|
|
17
|
+
|
|
18
|
+
A table-driven text adventure engine in the Scott Adams style and an
|
|
19
|
+
original seven-room game for it: a beach, a cliff path, a locked
|
|
20
|
+
lighthouse door, a dark hall, stair and cellar, and a beacon to light.
|
|
21
|
+
Nine items, among them a key hidden in a net, a lamp in three states and
|
|
22
|
+
a can of oil. The lamp lasts seven turns; an occurrence warns at three
|
|
23
|
+
and puts it out at zero. Words count on their first four letters and
|
|
24
|
+
synonyms share an id. The action table is scanned in order and the first
|
|
25
|
+
entry whose conditions hold runs; occurrences run every turn. Messages
|
|
26
|
+
are packed 5 bits a character, three to a 16-bit word. A script of 19
|
|
27
|
+
commands is typed one key per frame into the KERNAL keyboard queue and
|
|
28
|
+
read back through GETIN. It includes an unknown word, a locked door
|
|
29
|
+
tried before it is opened, and the synonyms `TAKE` and `LANTERN`, and it
|
|
30
|
+
wins. The program then compares the room, the items carried, the flags,
|
|
31
|
+
the turns, the score, a fold of every character printed and a fold of
|
|
32
|
+
the packed text with a Python model, prints PASS or FAIL, and prints the
|
|
33
|
+
cycles of parse, turn and print. It implements
|
|
34
|
+
`adventure_database_engine` (`techniques/text.md`). The game and its
|
|
35
|
+
text are original, not an Adams game's data.
|
|
36
|
+
|
|
37
|
+
## Source
|
|
38
|
+
|
|
39
|
+
```c
|
|
40
|
+
// adventure-engine.c
|
|
41
|
+
// A table-driven two-word adventure engine in the Scott Adams style, with
|
|
42
|
+
// an original seven-room game. Vocabulary words are compared on their first
|
|
43
|
+
// WL letters and synonyms share an id; the action table is scanned in order
|
|
44
|
+
// and the first entry whose verb, noun and conditions all hold runs its
|
|
45
|
+
// commands; entries with verb 0 are occurrences, run after every turn.
|
|
46
|
+
// Messages are packed 5 bits a character, three to a 16-bit word, and
|
|
47
|
+
// decoded into an output buffer that a word-wrapping terminal prints.
|
|
48
|
+
// A script is typed on autopilot through the KERNAL keyboard queue. At the
|
|
49
|
+
// end the final state and a fold of every printed character are compared
|
|
50
|
+
// with a Python model of the same engine and data; PASS or FAIL and the
|
|
51
|
+
// cycle costs are printed.
|
|
52
|
+
#include <c64/vic.h>
|
|
53
|
+
#include <c64/cia.h>
|
|
54
|
+
#include <conio.h>
|
|
55
|
+
|
|
56
|
+
#define SCREEN ((char *)0x0400)
|
|
57
|
+
#define COLOUR ((char *)0xd800)
|
|
58
|
+
#define TROWS 21 // terminal rows 0..20; report rows 21..24
|
|
59
|
+
#define WL 4 // significant letters per word
|
|
60
|
+
#define CARRIED 255
|
|
61
|
+
#define MAXLOAD 4
|
|
62
|
+
#define LAMPTIME 7
|
|
63
|
+
#define NITEM 10 // items 1..9, 0 unused
|
|
64
|
+
#define LIGHT 3 // the lit lamp
|
|
65
|
+
|
|
66
|
+
// Python model: final state and fold of the printed text.
|
|
67
|
+
#define EXP_ROOM 6
|
|
68
|
+
#define EXP_TURNS 18
|
|
69
|
+
#define EXP_SCORE 100
|
|
70
|
+
#define EXP_INV 0x0012
|
|
71
|
+
#define EXP_FLAGS 0x01
|
|
72
|
+
#define EXP_TEXT 0x7a7a
|
|
73
|
+
#define EXP_PACK 0x0774 // fold of the packed words, low byte first
|
|
74
|
+
|
|
75
|
+
// ---- messages: plain text here, packed at start-up by pack_all() ----------
|
|
76
|
+
static const char * const msgs[] = {
|
|
77
|
+
"",
|
|
78
|
+
"YOU ARE ON A PEBBLE BEACH. A PATH CLIMBS NORTH.",
|
|
79
|
+
"YOU ARE ON A CLIFF PATH. A LIGHTHOUSE STANDS TO THE NORTH AND A HUT TO THE EAST.",
|
|
80
|
+
"YOU ARE IN THE LIGHTHOUSE HALL. STAIRS GO UP AND DOWN.",
|
|
81
|
+
"YOU ARE IN A FISHER'S HUT.",
|
|
82
|
+
"YOU ARE ON A SPIRAL STAIR.",
|
|
83
|
+
"YOU ARE IN THE LAMP ROOM. THE SEA BELOW IS BLACK.",
|
|
84
|
+
"YOU ARE IN A DAMP CELLAR.",
|
|
85
|
+
"A BRASS KEY", "AN UNLIT LAMP", "A LIT LAMP", "A DEAD LAMP",
|
|
86
|
+
"A FISHING NET", "A CAN OF OIL", "A LOCKED DOOR", "AN OPEN DOOR",
|
|
87
|
+
"THE GREAT BEACON",
|
|
88
|
+
"IT IS TOO DARK TO SEE.", // 17
|
|
89
|
+
"YOU CAN SEE ", "EXITS", "YOU ARE CARRYING ", "NOTHING",
|
|
90
|
+
"I DON'T KNOW HOW TO ", "I DON'T KNOW WHAT A ", " IS.",
|
|
91
|
+
"YOU CAN'T GO THAT WAY.", "OK.", "YOU CARRY TOO MUCH.",
|
|
92
|
+
"YOU ALREADY HAVE IT.", "I DON'T SEE IT HERE.", "YOU DON'T HAVE IT.",
|
|
93
|
+
"YOU CAN'T DO THAT.", // 31
|
|
94
|
+
"THE DOOR IS LOCKED.",
|
|
95
|
+
"THE KEY TURNS AND THE DOOR SWINGS OPEN.",
|
|
96
|
+
"YOU HAVE NO KEY.",
|
|
97
|
+
"TANGLED IN THE NET IS A BRASS KEY.",
|
|
98
|
+
"YOU FIND NOTHING MORE.",
|
|
99
|
+
"THE LAMP FLICKERS INTO LIFE.",
|
|
100
|
+
"THE LAMP IS SPENT.",
|
|
101
|
+
"YOU POUR IN THE OIL AND LIGHT THE BEACON. ITS BEAM SWEEPS THE SEA.",
|
|
102
|
+
"THE BEACON HAS NO OIL.",
|
|
103
|
+
"YOU FALL DOWN THE STAIRS IN THE DARK.",
|
|
104
|
+
"YOUR LAMP IS GROWING DIM.",
|
|
105
|
+
"YOUR LAMP GOES OUT.", // 43
|
|
106
|
+
"YOU HAVE SCORED ", " OUT OF ", " TURNS.", "YOU HAVE WON!",
|
|
107
|
+
"YOU ARE DEAD.", "WHICH WAY.", " IN " // 50
|
|
108
|
+
};
|
|
109
|
+
#define NMSG (sizeof(msgs) / sizeof(msgs[0]))
|
|
110
|
+
|
|
111
|
+
// 5-bit alphabet: codes 0-30 as screen codes, 31 pads the last word.
|
|
112
|
+
static const char alpha[32] = {
|
|
113
|
+
0x20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
|
114
|
+
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 0x2e, 0x2c, 0x27, 0x21, 0 };
|
|
115
|
+
|
|
116
|
+
static unsigned text[400]; // packed words
|
|
117
|
+
static unsigned msg_at[NMSG]; // first word of each message
|
|
118
|
+
static unsigned nchars, nwords;
|
|
119
|
+
|
|
120
|
+
static char code_of(char c) // ASCII upper case to a 5-bit code
|
|
121
|
+
{
|
|
122
|
+
if (c >= 'A' && c <= 'Z') return c - 'A' + 1;
|
|
123
|
+
if (c == '.') return 27;
|
|
124
|
+
if (c == ',') return 28;
|
|
125
|
+
if (c == '\'') return 29;
|
|
126
|
+
if (c == '!') return 30;
|
|
127
|
+
return 0; // space
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static void pack_all(void)
|
|
131
|
+
{
|
|
132
|
+
for (char m = 1; m < NMSG; m++) {
|
|
133
|
+
const char *s = msgs[m];
|
|
134
|
+
msg_at[m] = nwords;
|
|
135
|
+
char k = 0, c[3];
|
|
136
|
+
while (*s) {
|
|
137
|
+
c[k++] = code_of(*s++); nchars++;
|
|
138
|
+
if (k == 3 || !*s) {
|
|
139
|
+
while (k < 3) c[k++] = 31;
|
|
140
|
+
text[nwords++] = ((unsigned)c[0] << 10) | ((unsigned)c[1] << 5) | c[2];
|
|
141
|
+
k = 0;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
text[nwords - 1] |= 0x8000; // bit 15 ends the message
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---- the world ------------------------------------------------------------
|
|
149
|
+
static const char room_desc[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
|
|
150
|
+
static const char exits[8][6] = { // N S E W U D
|
|
151
|
+
{0,0,0,0,0,0}, {2,0,0,0,0,0}, {0,1,4,0,0,0}, {0,2,0,0,5,7},
|
|
152
|
+
{0,0,0,2,0,0}, {0,0,0,0,6,3}, {0,0,0,0,0,5}, {0,0,0,0,3,0} };
|
|
153
|
+
static const char dark_room[8] = { 0, 0, 0, 1, 0, 1, 0, 1 };
|
|
154
|
+
static const char dir_sc[6] = { 14, 19, 5, 23, 21, 4 }; // N S E W U D
|
|
155
|
+
|
|
156
|
+
// Items: description message, noun id (0: cannot be carried), start room.
|
|
157
|
+
// The lamp's states are adjacent, so SWAP i trades items i and i + 1.
|
|
158
|
+
static const char item_desc[NITEM] = { 0, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
|
|
159
|
+
static const char item_noun[NITEM] = { 0, 7, 8, 8, 8, 9, 10, 0, 0, 0 };
|
|
160
|
+
static const char item_start[NITEM] = { 0, 0, 4, 0, 0, 4, 7, 2, 0, 6 };
|
|
161
|
+
|
|
162
|
+
// Vocabulary: first WL letters, space padded, and the word's id.
|
|
163
|
+
#define NVERB 15
|
|
164
|
+
static const char verb_w[NVERB][WL] = {
|
|
165
|
+
{'G','O',' ',' '}, {'W','A','L','K'}, {'G','E','T',' '}, {'T','A','K','E'},
|
|
166
|
+
{'D','R','O','P'}, {'L','O','O','K'}, {'L',' ',' ',' '}, {'I','N','V','E'},
|
|
167
|
+
{'I',' ',' ',' '}, {'U','N','L','O'}, {'O','P','E','N'}, {'L','I','G','H'},
|
|
168
|
+
{'S','E','A','R'}, {'E','X','A','M'}, {'S','C','O','R'} };
|
|
169
|
+
static const char verb_id[NVERB] = { 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6, 7, 8, 8, 9 };
|
|
170
|
+
#define NNOUN 20
|
|
171
|
+
static const char noun_w[NNOUN][WL] = {
|
|
172
|
+
{'N','O','R','T'}, {'N',' ',' ',' '}, {'S','O','U','T'}, {'S',' ',' ',' '},
|
|
173
|
+
{'E','A','S','T'}, {'E',' ',' ',' '}, {'W','E','S','T'}, {'W',' ',' ',' '},
|
|
174
|
+
{'U','P',' ',' '}, {'U',' ',' ',' '}, {'D','O','W','N'}, {'D',' ',' ',' '},
|
|
175
|
+
{'K','E','Y',' '}, {'L','A','M','P'}, {'L','A','N','T'}, {'N','E','T',' '},
|
|
176
|
+
{'O','I','L',' '}, {'C','A','N',' '}, {'D','O','O','R'}, {'B','E','A','C'} };
|
|
177
|
+
static const char noun_id[NNOUN] = { 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12 };
|
|
178
|
+
|
|
179
|
+
// Conditions (op, arg) and commands (op, arg); op 0 ends a list.
|
|
180
|
+
enum { C_AT = 1, C_CARRIED, C_HERE, C_ACCESS, C_EXISTS, C_FLAG, C_NOTFLAG, C_CTREQ, C_DARK };
|
|
181
|
+
enum { K_MSG = 1, K_GOTO, K_LOOK, K_INV, K_SCORE, K_SWAP, K_PLACE, K_DESTROY,
|
|
182
|
+
K_SETF, K_SETC, K_DECC, K_ADDSC, K_DIE, K_WIN };
|
|
183
|
+
|
|
184
|
+
// verb, noun (0: any), four conditions, four commands. Verb 0: occurrence.
|
|
185
|
+
#define NACT 17
|
|
186
|
+
static const char act[NACT][18] = {
|
|
187
|
+
{4, 0, 0,0, 0,0, 0,0, 0,0, K_LOOK,0, 0,0, 0,0, 0,0},
|
|
188
|
+
{5, 0, 0,0, 0,0, 0,0, 0,0, K_INV,0, 0,0, 0,0, 0,0},
|
|
189
|
+
{9, 0, 0,0, 0,0, 0,0, 0,0, K_SCORE,0, 0,0, 0,0, 0,0},
|
|
190
|
+
{1, 1, C_AT,2, C_HERE,8, 0,0, 0,0, K_GOTO,3, K_LOOK,0, 0,0, 0,0},
|
|
191
|
+
{1, 1, C_AT,2, 0,0, 0,0, 0,0, K_MSG,32, 0,0, 0,0, 0,0},
|
|
192
|
+
{1, 6, C_AT,5, C_DARK,0, 0,0, 0,0, K_MSG,41, K_DIE,0, 0,0, 0,0},
|
|
193
|
+
{6, 11, C_AT,2, C_HERE,7, C_CARRIED,1, 0,0, K_SWAP,7, K_ADDSC,20, K_MSG,33, 0,0},
|
|
194
|
+
{6, 11, C_AT,2, C_HERE,7, 0,0, 0,0, K_MSG,34, 0,0, 0,0, 0,0},
|
|
195
|
+
{8, 9, C_ACCESS,5, C_NOTFLAG,0, 0,0, 0,0, K_SETF,0, K_PLACE,1, K_ADDSC,20, K_MSG,35},
|
|
196
|
+
{8, 9, C_ACCESS,5, 0,0, 0,0, 0,0, K_MSG,36, 0,0, 0,0, 0,0},
|
|
197
|
+
{7, 8, C_ACCESS,2, 0,0, 0,0, 0,0, K_SWAP,2, K_SETC,LAMPTIME, K_MSG,37, 0,0},
|
|
198
|
+
{7, 8, C_ACCESS,4, 0,0, 0,0, 0,0, K_MSG,38, 0,0, 0,0, 0,0},
|
|
199
|
+
{7, 12, C_AT,6, C_CARRIED,6, 0,0, 0,0, K_DESTROY,6, K_ADDSC,60, K_MSG,39, K_WIN,0},
|
|
200
|
+
{7, 12, C_AT,6, 0,0, 0,0, 0,0, K_MSG,40, 0,0, 0,0, 0,0},
|
|
201
|
+
{0, 0, C_EXISTS,3, 0,0, 0,0, 0,0, K_DECC,0, 0,0, 0,0, 0,0},
|
|
202
|
+
{0, 0, C_EXISTS,3, C_CTREQ,3, 0,0, 0,0, K_MSG,42, 0,0, 0,0, 0,0},
|
|
203
|
+
{0, 0, C_EXISTS,3, C_CTREQ,0, 0,0, 0,0, K_SWAP,3, K_MSG,43, 0,0, 0,0} };
|
|
204
|
+
|
|
205
|
+
// The autopilot: typed one key per frame into the KERNAL queue. 13 = RETURN.
|
|
206
|
+
static const char script[] =
|
|
207
|
+
"XYZZY\rN\rGO NORTH\rE\rTAKE LANTERN\rEXAMINE NET\rGET KEY\rW\r"
|
|
208
|
+
"UNLOCK DOOR\rN\rLIGHT LAMP\rLOOK\rD\rGET OIL\rI\rU\rU\rU\rLIGHT BEACON\r";
|
|
209
|
+
|
|
210
|
+
// ---- game state -------------------------------------------------------------
|
|
211
|
+
static char room, loc[NITEM], flags, ctr, score, turns, over;
|
|
212
|
+
|
|
213
|
+
// ---- output buffer: screen codes, 0xff = new line ---------------------------
|
|
214
|
+
static char ob[256];
|
|
215
|
+
static char on;
|
|
216
|
+
|
|
217
|
+
static void o_put(char c) { if (on < 255) ob[on++] = c; }
|
|
218
|
+
static void o_nl(void) { o_put(0xff); }
|
|
219
|
+
|
|
220
|
+
static void o_msg(char m) // decode a packed message
|
|
221
|
+
{
|
|
222
|
+
const unsigned *p = text + msg_at[m];
|
|
223
|
+
unsigned w;
|
|
224
|
+
do {
|
|
225
|
+
w = *p++;
|
|
226
|
+
char c = (w >> 10) & 31;
|
|
227
|
+
if (c < 31) o_put(alpha[c]);
|
|
228
|
+
c = (w >> 5) & 31;
|
|
229
|
+
if (c < 31) o_put(alpha[c]);
|
|
230
|
+
c = w & 31;
|
|
231
|
+
if (c < 31) o_put(alpha[c]);
|
|
232
|
+
} while (!(w & 0x8000));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
static char sc_of(char k) // PETSCII upper case to screen code
|
|
236
|
+
{
|
|
237
|
+
return (k >= 0x41 && k <= 0x5a) ? k - 0x40 : k;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
static void o_num(char n)
|
|
241
|
+
{
|
|
242
|
+
if (n >= 100) { o_put(0x30 + n / 100); n %= 100; o_put(0x30 + n / 10); }
|
|
243
|
+
else if (n >= 10) o_put(0x30 + n / 10);
|
|
244
|
+
o_put(0x30 + n % 10);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ---- terminal: rows 0..20, word wrap at 40, scroll on new line --------------
|
|
248
|
+
static char col;
|
|
249
|
+
static unsigned tchk;
|
|
250
|
+
|
|
251
|
+
static void fold(char v) { tchk = ((tchk ^ v) * 5 + 1); }
|
|
252
|
+
|
|
253
|
+
static void t_nl(void)
|
|
254
|
+
{
|
|
255
|
+
char *p = SCREEN;
|
|
256
|
+
for (char r = 0; r < TROWS - 1; r++, p += 40)
|
|
257
|
+
for (char i = 0; i < 40; i++) p[i] = p[i + 40];
|
|
258
|
+
for (char i = 0; i < 40; i++) p[i] = 0x20;
|
|
259
|
+
col = 0;
|
|
260
|
+
fold(0xff);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
static void t_put(char c)
|
|
264
|
+
{
|
|
265
|
+
if (col == 40) t_nl();
|
|
266
|
+
SCREEN[40 * (TROWS - 1) + col++] = c;
|
|
267
|
+
fold(c);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
static void t_text(char i, char n) // ob[i..n) as words
|
|
271
|
+
{
|
|
272
|
+
while (i < n) {
|
|
273
|
+
char j = i;
|
|
274
|
+
while (j < n && ob[j] != 0x20) j++;
|
|
275
|
+
if (col && col + (j - i) > 40) t_nl();
|
|
276
|
+
for (char k = i; k < j; k++) t_put(ob[k]);
|
|
277
|
+
if (j < n && col < 40) t_put(0x20);
|
|
278
|
+
i = j + 1;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
static void flush(void)
|
|
283
|
+
{
|
|
284
|
+
char s = 0;
|
|
285
|
+
for (char i = 0; i < on; i++)
|
|
286
|
+
if (ob[i] == 0xff) { t_text(s, i); t_nl(); s = i + 1; }
|
|
287
|
+
if (s < on) t_text(s, on);
|
|
288
|
+
on = 0;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// ---- the engine -------------------------------------------------------------
|
|
292
|
+
static bool lit(void)
|
|
293
|
+
{
|
|
294
|
+
return !dark_room[room] || loc[LIGHT] == CARRIED || loc[LIGHT] == room;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
static void list_items(char where)
|
|
298
|
+
{
|
|
299
|
+
char k = 0;
|
|
300
|
+
for (char i = 1; i < NITEM; i++)
|
|
301
|
+
if (loc[i] == where) {
|
|
302
|
+
if (k++) { o_put(0x2c); o_put(0x20); }
|
|
303
|
+
o_msg(item_desc[i]);
|
|
304
|
+
}
|
|
305
|
+
if (where == CARRIED && !k) o_msg(21);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
static void look(void)
|
|
309
|
+
{
|
|
310
|
+
if (!lit()) { o_msg(17); o_nl(); return; }
|
|
311
|
+
o_msg(room_desc[room]); o_nl();
|
|
312
|
+
char k = 0;
|
|
313
|
+
for (char i = 1; i < NITEM; i++) if (loc[i] == room) k = 1;
|
|
314
|
+
if (k) { o_msg(18); list_items(room); o_put(0x2e); o_nl(); }
|
|
315
|
+
o_msg(19);
|
|
316
|
+
for (char d = 0; d < 6; d++)
|
|
317
|
+
if (exits[room][d]) { o_put(0x20); o_put(dir_sc[d]); }
|
|
318
|
+
o_put(0x2e); o_nl();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
static void say_score(void)
|
|
322
|
+
{
|
|
323
|
+
o_msg(44); o_num(score); o_msg(45); o_num(100); o_msg(50);
|
|
324
|
+
o_num(turns); o_msg(46); o_nl();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
static bool cond(char op, char a)
|
|
328
|
+
{
|
|
329
|
+
switch (op) {
|
|
330
|
+
case C_AT: return room == a;
|
|
331
|
+
case C_CARRIED: return loc[a] == CARRIED;
|
|
332
|
+
case C_HERE: return loc[a] == room;
|
|
333
|
+
case C_ACCESS: return loc[a] == CARRIED || loc[a] == room;
|
|
334
|
+
case C_EXISTS: return loc[a] != 0;
|
|
335
|
+
case C_FLAG: return (flags >> a) & 1;
|
|
336
|
+
case C_NOTFLAG: return !((flags >> a) & 1);
|
|
337
|
+
case C_CTREQ: return ctr == a;
|
|
338
|
+
case C_DARK: return !lit();
|
|
339
|
+
}
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
static void cmd(char op, char a)
|
|
344
|
+
{
|
|
345
|
+
char t;
|
|
346
|
+
switch (op) {
|
|
347
|
+
case K_MSG: o_msg(a); o_nl(); break;
|
|
348
|
+
case K_GOTO: room = a; break;
|
|
349
|
+
case K_LOOK: look(); break;
|
|
350
|
+
case K_INV: o_msg(20); list_items(CARRIED); o_put(0x2e); o_nl(); break;
|
|
351
|
+
case K_SCORE: say_score(); break;
|
|
352
|
+
case K_SWAP: t = loc[a]; loc[a] = loc[a + 1]; loc[a + 1] = t; break;
|
|
353
|
+
case K_PLACE: loc[a] = room; break;
|
|
354
|
+
case K_DESTROY: loc[a] = 0; break;
|
|
355
|
+
case K_SETF: flags |= 1 << a; break;
|
|
356
|
+
case K_SETC: ctr = a; break;
|
|
357
|
+
case K_DECC: if (ctr) ctr--; break;
|
|
358
|
+
case K_ADDSC: score += a; break;
|
|
359
|
+
case K_DIE: o_msg(48); o_nl(); over = 2; break;
|
|
360
|
+
case K_WIN: o_msg(47); o_nl(); say_score(); over = 1; break;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
static bool run_entry(const char *e) // conditions all true: run the commands
|
|
365
|
+
{
|
|
366
|
+
for (char i = 2; i < 10 && e[i]; i += 2)
|
|
367
|
+
if (!cond(e[i], e[i + 1])) return false;
|
|
368
|
+
for (char i = 10; i < 18 && e[i]; i += 2)
|
|
369
|
+
cmd(e[i], e[i + 1]);
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
static bool scan(char v, char n) // first matching action only
|
|
374
|
+
{
|
|
375
|
+
for (char a = 0; a < NACT; a++) {
|
|
376
|
+
const char *e = act[a];
|
|
377
|
+
if (e[0] == v && v && (e[1] == 0 || e[1] == n) && run_entry(e))
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
static void builtin(char v, char n)
|
|
384
|
+
{
|
|
385
|
+
if (v == 1) { // GO
|
|
386
|
+
if (n < 1 || n > 6) { o_msg(49); o_nl(); return; }
|
|
387
|
+
char r = exits[room][n - 1];
|
|
388
|
+
if (!r) { o_msg(25); o_nl(); return; }
|
|
389
|
+
room = r; look(); return;
|
|
390
|
+
}
|
|
391
|
+
if ((v == 2 || v == 3) && n) { // GET, DROP
|
|
392
|
+
char held = 0;
|
|
393
|
+
for (char i = 1; i < NITEM; i++) if (loc[i] == CARRIED) held++;
|
|
394
|
+
for (char i = 1; i < NITEM; i++) {
|
|
395
|
+
if (item_noun[i] != n) continue;
|
|
396
|
+
if (v == 2 && loc[i] == CARRIED) { o_msg(28); o_nl(); return; }
|
|
397
|
+
if (v == 2 && loc[i] == room) {
|
|
398
|
+
if (!lit()) o_msg(17);
|
|
399
|
+
else if (held >= MAXLOAD) o_msg(27);
|
|
400
|
+
else { loc[i] = CARRIED; o_msg(26); }
|
|
401
|
+
o_nl(); return;
|
|
402
|
+
}
|
|
403
|
+
if (v == 3 && loc[i] == CARRIED) { loc[i] = room; o_msg(26); o_nl(); return; }
|
|
404
|
+
}
|
|
405
|
+
o_msg(v == 2 ? 29 : 30); o_nl(); return;
|
|
406
|
+
}
|
|
407
|
+
o_msg(31); o_nl();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
static void turn(char v, char n)
|
|
411
|
+
{
|
|
412
|
+
turns++;
|
|
413
|
+
if (!scan(v, n)) builtin(v, n);
|
|
414
|
+
if (!over)
|
|
415
|
+
for (char a = 0; a < NACT; a++)
|
|
416
|
+
if (act[a][0] == 0) run_entry(act[a]);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// ---- the parser -------------------------------------------------------------
|
|
420
|
+
static char line[40], len;
|
|
421
|
+
static char w_at[2], w_len[2], nw;
|
|
422
|
+
|
|
423
|
+
static char find(const char *tab, const char *ids, char count, char w)
|
|
424
|
+
{
|
|
425
|
+
char key[WL];
|
|
426
|
+
for (char k = 0; k < WL; k++)
|
|
427
|
+
key[k] = k < w_len[w] ? line[w_at[w] + k] : ' ';
|
|
428
|
+
for (char t = 0; t < count; t++, tab += WL) {
|
|
429
|
+
char k = 0;
|
|
430
|
+
while (k < WL && tab[k] == key[k]) k++;
|
|
431
|
+
if (k == WL) return ids[t];
|
|
432
|
+
}
|
|
433
|
+
return 0;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
static void o_word(char w)
|
|
437
|
+
{
|
|
438
|
+
for (char k = 0; k < w_len[w] && k < 8; k++) o_put(sc_of(line[w_at[w] + k]));
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
static char pv, pn; // parsed verb and noun
|
|
442
|
+
|
|
443
|
+
static bool parse(void)
|
|
444
|
+
{
|
|
445
|
+
nw = 0;
|
|
446
|
+
char i = 0;
|
|
447
|
+
while (nw < 2) {
|
|
448
|
+
while (i < len && line[i] == ' ') i++;
|
|
449
|
+
if (i >= len) break;
|
|
450
|
+
w_at[nw] = i;
|
|
451
|
+
while (i < len && line[i] != ' ') i++;
|
|
452
|
+
w_len[nw] = i - w_at[nw];
|
|
453
|
+
nw++;
|
|
454
|
+
}
|
|
455
|
+
if (!nw) return false;
|
|
456
|
+
pv = find(verb_w[0], verb_id, NVERB, 0);
|
|
457
|
+
pn = nw > 1 ? find(noun_w[0], noun_id, NNOUN, 1) : 0;
|
|
458
|
+
if (!pv) {
|
|
459
|
+
char d = find(noun_w[0], noun_id, NNOUN, 0);
|
|
460
|
+
if (d >= 1 && d <= 6 && nw == 1) { pv = 1; pn = d; return true; }
|
|
461
|
+
o_msg(22); o_word(0); o_put(0x2e); o_nl(); return false;
|
|
462
|
+
}
|
|
463
|
+
if (nw > 1 && !pn) { o_msg(23); o_word(1); o_msg(24); o_nl(); return false; }
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// ---- timing: CIA2 timer A counts phi2, timer B counts A's underflows --------
|
|
468
|
+
static void t_start(void)
|
|
469
|
+
{
|
|
470
|
+
cia2.cra = 0; cia2.crb = 0;
|
|
471
|
+
cia2.ta = 0xffff; cia2.tb = 0xffff;
|
|
472
|
+
cia2.crb = 0x51;
|
|
473
|
+
cia2.cra = 0x11;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
static unsigned long t_stop(void)
|
|
477
|
+
{
|
|
478
|
+
cia2.cra = 0; cia2.crb = 0;
|
|
479
|
+
return ((unsigned long)(0xffff - cia2.tb) << 16) + (0xffff - cia2.ta);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// ---- report -----------------------------------------------------------------
|
|
483
|
+
static void put_text(char row, char c, const char *s)
|
|
484
|
+
{
|
|
485
|
+
char *p = SCREEN + 40 * row + c;
|
|
486
|
+
while (*s) *p++ = sc_of(*s++);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
static void put_dec(char row, char c, unsigned long v, char digits)
|
|
490
|
+
{
|
|
491
|
+
char *p = SCREEN + 40 * row + c + digits;
|
|
492
|
+
while (digits--) { *--p = 0x30 + v % 10; v /= 10; }
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
static void put_hex(char row, char c, unsigned v, char digits)
|
|
496
|
+
{
|
|
497
|
+
char *p = SCREEN + 40 * row + c + digits;
|
|
498
|
+
while (digits--) { char h = v & 15; *--p = h < 10 ? 0x30 + h : h - 9; v >>= 4; }
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
int main(void)
|
|
502
|
+
{
|
|
503
|
+
vic.color_border = VCOL_BLACK;
|
|
504
|
+
vic.color_back = VCOL_BLACK;
|
|
505
|
+
for (unsigned i = 0; i < 1000; i++) { SCREEN[i] = 0x20; COLOUR[i] = VCOL_LT_GREEN; }
|
|
506
|
+
|
|
507
|
+
pack_all();
|
|
508
|
+
unsigned pchk = 0;
|
|
509
|
+
for (unsigned i = 0; i < nwords; i++) {
|
|
510
|
+
pchk = ((pchk ^ (text[i] & 0xff)) * 5 + 1);
|
|
511
|
+
pchk = ((pchk ^ (text[i] >> 8)) * 5 + 1);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// A command that matches no entry walks the whole table: time it alone.
|
|
515
|
+
__asm { sei }
|
|
516
|
+
t_start(); unsigned long c_null = t_stop();
|
|
517
|
+
t_start(); scan(99, 0); unsigned long c_miss = t_stop() - c_null;
|
|
518
|
+
__asm { cli }
|
|
519
|
+
|
|
520
|
+
room = 1;
|
|
521
|
+
for (char i = 0; i < NITEM; i++) loc[i] = item_start[i];
|
|
522
|
+
look(); flush();
|
|
523
|
+
t_put(0x3e); // prompt
|
|
524
|
+
|
|
525
|
+
unsigned long pmin = 0xffffff, pmax = 0, tmin = 0xffffff, tmax = 0, fmax = 0, cmax = 0;
|
|
526
|
+
unsigned si = 0;
|
|
527
|
+
while (!over) {
|
|
528
|
+
vic_waitFrame();
|
|
529
|
+
if (script[si] && *(volatile char *)0xc6 == 0) { // queue empty: type one key
|
|
530
|
+
__asm { sei }
|
|
531
|
+
*(volatile char *)0x0277 = script[si++];
|
|
532
|
+
*(volatile char *)0xc6 = 1;
|
|
533
|
+
__asm { cli }
|
|
534
|
+
}
|
|
535
|
+
char k = getchx(); // GETIN
|
|
536
|
+
if (k == 0x0d || k == 0x0a) { // RETURN ($0A from getchx)
|
|
537
|
+
t_nl();
|
|
538
|
+
__asm { sei }
|
|
539
|
+
t_start();
|
|
540
|
+
bool ok = parse();
|
|
541
|
+
unsigned long c = t_stop() - c_null;
|
|
542
|
+
if (c < pmin) pmin = c;
|
|
543
|
+
if (c > pmax) pmax = c;
|
|
544
|
+
unsigned long both = c;
|
|
545
|
+
if (ok) {
|
|
546
|
+
t_start();
|
|
547
|
+
turn(pv, pn);
|
|
548
|
+
c = t_stop() - c_null;
|
|
549
|
+
if (c < tmin) tmin = c;
|
|
550
|
+
if (c > tmax) tmax = c;
|
|
551
|
+
both += c;
|
|
552
|
+
}
|
|
553
|
+
if (both > cmax) cmax = both; // parse + turn of one command
|
|
554
|
+
t_start();
|
|
555
|
+
flush();
|
|
556
|
+
c = t_stop() - c_null;
|
|
557
|
+
if (c > fmax) fmax = c;
|
|
558
|
+
__asm { cli }
|
|
559
|
+
len = 0;
|
|
560
|
+
if (!over) t_put(0x3e);
|
|
561
|
+
} else if (k == 0x14) { // DEL
|
|
562
|
+
if (len) { len--; col--; SCREEN[40 * (TROWS - 1) + col] = 0x20; }
|
|
563
|
+
} else if (k >= 0x20 && k <= 0x5a && len < 30 && col < 40) {
|
|
564
|
+
line[len++] = k;
|
|
565
|
+
t_put(sc_of(k)); // echo as a screen code
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
unsigned inv = 0;
|
|
570
|
+
for (char i = 1; i < NITEM; i++) if (loc[i] == CARRIED) inv |= 1 << i;
|
|
571
|
+
bool pass = room == EXP_ROOM && turns == EXP_TURNS && score == EXP_SCORE &&
|
|
572
|
+
inv == EXP_INV && flags == EXP_FLAGS && tchk == EXP_TEXT &&
|
|
573
|
+
pchk == EXP_PACK && over == 1;
|
|
574
|
+
|
|
575
|
+
put_text(21, 0, "ROOM TURNS SCORE INV F");
|
|
576
|
+
put_dec(21, 5, room, 1); put_dec(21, 13, turns, 2); put_dec(21, 22, score, 3);
|
|
577
|
+
put_hex(21, 30, inv, 4); put_hex(21, 37, flags, 2);
|
|
578
|
+
put_text(22, 0, "TEXT / PACK / ");
|
|
579
|
+
put_hex(22, 5, tchk, 4); put_hex(22, 10, EXP_TEXT, 4);
|
|
580
|
+
put_hex(22, 20, pchk, 4); put_hex(22, 25, EXP_PACK, 4);
|
|
581
|
+
put_dec(22, 30, nwords * 2, 4); put_text(22, 34, "/"); put_dec(22, 35, nchars, 4);
|
|
582
|
+
put_text(23, 0, "PARSE - TURN -");
|
|
583
|
+
put_dec(23, 6, pmin, 5); put_dec(23, 12, pmax, 5);
|
|
584
|
+
put_dec(23, 23, tmin, 6); put_dec(23, 30, tmax, 6);
|
|
585
|
+
put_text(24, 0, "PRINT MISS CMD");
|
|
586
|
+
put_dec(24, 6, fmax, 6); put_dec(24, 18, c_miss, 5); put_dec(24, 28, cmax, 6);
|
|
587
|
+
put_text(24, 36, pass ? "PASS" : "FAIL");
|
|
588
|
+
vic.color_border = pass ? VCOL_GREEN : VCOL_RED;
|
|
589
|
+
for (;;) ;
|
|
590
|
+
return 0;
|
|
591
|
+
}
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
## Build
|
|
595
|
+
|
|
596
|
+
```bash
|
|
597
|
+
oscar64 -tm=c64 -O2 -o=adventure-engine.prg adventure-engine.c
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
Run headless (PAL; add `-model ntsc` for NTSC):
|
|
601
|
+
|
|
602
|
+
```bash
|
|
603
|
+
timeout 180 x64sc -default -warp +sound +autostart-delay-random -autostartprgmode 1 -limitcycles 10000000 -exitscreenshot adventure-engine.png -autostart adventure-engine.prg
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
The report is on screen between 7,000,000 and 7,500,000 cycles on PAL
|
|
607
|
+
and between 6,000,000 and 7,000,000 on NTSC (measured by bisecting
|
|
608
|
+
`-limitcycles`); 10,000,000 leaves a margin.
|
|
609
|
+
|
|
610
|
+
## Expected output
|
|
611
|
+
|
|
612
|
+
`screenshots/adventure-engine.png` (PAL) and
|
|
613
|
+
`screenshots/adventure-engine-ntsc.png` (NTSC). Black screen, light green
|
|
614
|
+
text, green border. Rows 0 to 20 are the terminal's last 21 lines:
|
|
615
|
+
|
|
616
|
+
```text
|
|
617
|
+
LAMP, A CAN OF OIL.
|
|
618
|
+
>U
|
|
619
|
+
YOU ARE IN THE LIGHTHOUSE HALL. STAIRS
|
|
620
|
+
GO UP AND DOWN.
|
|
621
|
+
EXITS S U D.
|
|
622
|
+
>U
|
|
623
|
+
YOU ARE ON A SPIRAL STAIR.
|
|
624
|
+
EXITS U D.
|
|
625
|
+
YOUR LAMP GOES OUT.
|
|
626
|
+
>U
|
|
627
|
+
YOU ARE IN THE LAMP ROOM. THE SEA BELOW
|
|
628
|
+
IS BLACK.
|
|
629
|
+
YOU CAN SEE THE GREAT BEACON.
|
|
630
|
+
EXITS D.
|
|
631
|
+
>LIGHT BEACON
|
|
632
|
+
YOU POUR IN THE OIL AND LIGHT THE
|
|
633
|
+
BEACON. ITS BEAM SWEEPS THE SEA.
|
|
634
|
+
YOU HAVE WON!
|
|
635
|
+
YOU HAVE SCORED 100 OUT OF 100 IN 18
|
|
636
|
+
TURNS.
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
Rows 21 to 24, PAL:
|
|
640
|
+
|
|
641
|
+
```text
|
|
642
|
+
ROOM 6 TURNS 18 SCORE 100 INV 0012 F 01
|
|
643
|
+
TEXT 7A7A/7A7A PACK 0774/0774 0752/1061
|
|
644
|
+
PARSE 00722-03944 TURN 002888-012734
|
|
645
|
+
PRINT 109866 MISS 01695 CMD 014908 PASS
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
NTSC reads `PARSE 00636-03944 TURN 003017-013161` and
|
|
649
|
+
`PRINT 110762 MISS 01609 CMD 015206`; the rest is the same.
|
|
650
|
+
|
|
651
|
+
- `ROOM 6`: the lamp room. `TURNS 18`: 19 commands, less the unknown
|
|
652
|
+
word, which costs no turn. `SCORE 100`: 20 for finding the key, 20
|
|
653
|
+
for the door, 60 for the beacon. `INV 0012`: bits 1 and 4, the key
|
|
654
|
+
and the dead lamp; the oil was poured into the beacon. `F 01`: flag 0,
|
|
655
|
+
the net has been searched.
|
|
656
|
+
- `TEXT` is the fold of every screen code the terminal printed, prompts,
|
|
657
|
+
echoes and new lines included, then the model's value. `PACK` is the
|
|
658
|
+
fold of the packed words the program built at start-up, then the
|
|
659
|
+
model's. `0752/1061` is the packed size in bytes and the characters it
|
|
660
|
+
holds: a ratio of 0.709.
|
|
661
|
+
- `PARSE` and `TURN` are the fewest and most cycles one parse and one
|
|
662
|
+
turn took, CIA2 timer A cascaded into timer B, interrupts off,
|
|
663
|
+
display on. A turn includes the occurrences and decoding its messages
|
|
664
|
+
into the output buffer. `CMD` is the most that parse and turn took
|
|
665
|
+
together for one command.
|
|
666
|
+
- `PRINT` is the most cycles the terminal took to print one command's
|
|
667
|
+
output. `MISS` is one scan of the 17-entry table with a verb that
|
|
668
|
+
matches nothing.
|
|
669
|
+
|
|
670
|
+
The whole transcript, from the model, starts:
|
|
671
|
+
|
|
672
|
+
```text
|
|
673
|
+
YOU ARE ON A PEBBLE BEACH. A PATH CLIMBS
|
|
674
|
+
NORTH.
|
|
675
|
+
EXITS N.
|
|
676
|
+
>XYZZY
|
|
677
|
+
I DON'T KNOW HOW TO XYZZY.
|
|
678
|
+
>N
|
|
679
|
+
YOU ARE ON A CLIFF PATH. A LIGHTHOUSE
|
|
680
|
+
STANDS TO THE NORTH AND A HUT TO THE
|
|
681
|
+
EAST.
|
|
682
|
+
YOU CAN SEE A LOCKED DOOR.
|
|
683
|
+
EXITS S E.
|
|
684
|
+
>GO NORTH
|
|
685
|
+
THE DOOR IS LOCKED.
|
|
686
|
+
>E
|
|
687
|
+
YOU ARE IN A FISHER'S HUT.
|
|
688
|
+
YOU CAN SEE AN UNLIT LAMP, A FISHING
|
|
689
|
+
NET.
|
|
690
|
+
EXITS W.
|
|
691
|
+
>TAKE LANTERN
|
|
692
|
+
OK.
|
|
693
|
+
>EXAMINE NET
|
|
694
|
+
TANGLED IN THE NET IS A BRASS KEY.
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
and goes on through `UNLOCK DOOR`, a hall too dark to see, `LIGHT
|
|
698
|
+
LAMP`, the cellar, `GET OIL` with "YOUR LAMP IS GROWING DIM." and an
|
|
699
|
+
inventory, to the screen above.
|
|
700
|
+
|
|
701
|
+
The model, the engine and the game written again in Python:
|
|
702
|
+
|
|
703
|
+
```python
|
|
704
|
+
# Python model of adventure-engine.c: the same data, rules, packing and
|
|
705
|
+
# 21-row terminal. g.term holds the last screen of the terminal.
|
|
706
|
+
|
|
707
|
+
WL = 4 # significant letters per word
|
|
708
|
+
CARRIED, MAXLOAD, LAMPTIME = 255, 4, 7
|
|
709
|
+
|
|
710
|
+
MSGS = [
|
|
711
|
+
"", # 0 unused
|
|
712
|
+
"YOU ARE ON A PEBBLE BEACH. A PATH CLIMBS NORTH.", # 1
|
|
713
|
+
"YOU ARE ON A CLIFF PATH. A LIGHTHOUSE STANDS TO THE NORTH AND A HUT TO THE EAST.", # 2
|
|
714
|
+
"YOU ARE IN THE LIGHTHOUSE HALL. STAIRS GO UP AND DOWN.", # 3
|
|
715
|
+
"YOU ARE IN A FISHER'S HUT.", # 4
|
|
716
|
+
"YOU ARE ON A SPIRAL STAIR.", # 5
|
|
717
|
+
"YOU ARE IN THE LAMP ROOM. THE SEA BELOW IS BLACK.", # 6
|
|
718
|
+
"YOU ARE IN A DAMP CELLAR.", # 7
|
|
719
|
+
"A BRASS KEY", # 8
|
|
720
|
+
"AN UNLIT LAMP", # 9
|
|
721
|
+
"A LIT LAMP", # 10
|
|
722
|
+
"A DEAD LAMP", # 11
|
|
723
|
+
"A FISHING NET", # 12
|
|
724
|
+
"A CAN OF OIL", # 13
|
|
725
|
+
"A LOCKED DOOR", # 14
|
|
726
|
+
"AN OPEN DOOR", # 15
|
|
727
|
+
"THE GREAT BEACON", # 16
|
|
728
|
+
"IT IS TOO DARK TO SEE.", # 17
|
|
729
|
+
"YOU CAN SEE ", # 18
|
|
730
|
+
"EXITS", # 19
|
|
731
|
+
"YOU ARE CARRYING ", # 20
|
|
732
|
+
"NOTHING", # 21
|
|
733
|
+
"I DON'T KNOW HOW TO ", # 22
|
|
734
|
+
"I DON'T KNOW WHAT A ", # 23
|
|
735
|
+
" IS.", # 24
|
|
736
|
+
"YOU CAN'T GO THAT WAY.", # 25
|
|
737
|
+
"OK.", # 26
|
|
738
|
+
"YOU CARRY TOO MUCH.", # 27
|
|
739
|
+
"YOU ALREADY HAVE IT.", # 28
|
|
740
|
+
"I DON'T SEE IT HERE.", # 29
|
|
741
|
+
"YOU DON'T HAVE IT.", # 30
|
|
742
|
+
"YOU CAN'T DO THAT.", # 31
|
|
743
|
+
"THE DOOR IS LOCKED.", # 32
|
|
744
|
+
"THE KEY TURNS AND THE DOOR SWINGS OPEN.", # 33
|
|
745
|
+
"YOU HAVE NO KEY.", # 34
|
|
746
|
+
"TANGLED IN THE NET IS A BRASS KEY.", # 35
|
|
747
|
+
"YOU FIND NOTHING MORE.", # 36
|
|
748
|
+
"THE LAMP FLICKERS INTO LIFE.", # 37
|
|
749
|
+
"THE LAMP IS SPENT.", # 38
|
|
750
|
+
"YOU POUR IN THE OIL AND LIGHT THE BEACON. ITS BEAM SWEEPS THE SEA.", # 39
|
|
751
|
+
"THE BEACON HAS NO OIL.", # 40
|
|
752
|
+
"YOU FALL DOWN THE STAIRS IN THE DARK.", # 41
|
|
753
|
+
"YOUR LAMP IS GROWING DIM.", # 42
|
|
754
|
+
"YOUR LAMP GOES OUT.", # 43
|
|
755
|
+
"YOU HAVE SCORED ", # 44
|
|
756
|
+
" OUT OF ", # 45
|
|
757
|
+
" TURNS.", # 46
|
|
758
|
+
"YOU HAVE WON!", # 47
|
|
759
|
+
"YOU ARE DEAD.", # 48
|
|
760
|
+
"WHICH WAY.", # 49
|
|
761
|
+
" IN ", # 50
|
|
762
|
+
]
|
|
763
|
+
|
|
764
|
+
# Rooms 1-7. Exits N S E W U D; 0 = none. The path's north exit is an action.
|
|
765
|
+
ROOM_DESC = [0, 1, 2, 3, 4, 5, 6, 7]
|
|
766
|
+
EXITS = [[0] * 6,
|
|
767
|
+
[2, 0, 0, 0, 0, 0], [0, 1, 4, 0, 0, 0], [0, 2, 0, 0, 5, 7],
|
|
768
|
+
[0, 0, 0, 2, 0, 0], [0, 0, 0, 0, 6, 3], [0, 0, 0, 0, 0, 5],
|
|
769
|
+
[0, 0, 0, 0, 3, 0]]
|
|
770
|
+
DARK = {3, 5, 7}
|
|
771
|
+
|
|
772
|
+
# Items 1-9: description, noun (0 = cannot be carried), start room.
|
|
773
|
+
# Lamp states are adjacent (2 unlit, 3 lit, 4 dead) so SWAP i trades i, i+1.
|
|
774
|
+
ITEMS = [(0, 0, 0),
|
|
775
|
+
(8, 7, 0), (9, 8, 4), (10, 8, 0), (11, 8, 0), (12, 9, 4),
|
|
776
|
+
(13, 10, 7), (14, 0, 2), (15, 0, 0), (16, 0, 6)]
|
|
777
|
+
LIGHT = 3 # the lit lamp
|
|
778
|
+
|
|
779
|
+
# Vocabulary: word, id. Synonyms share an id.
|
|
780
|
+
VERBS = [("GO", 1), ("WALK", 1), ("GET", 2), ("TAKE", 2), ("DROP", 3),
|
|
781
|
+
("LOOK", 4), ("L", 4), ("INVENTORY", 5), ("I", 5), ("UNLOCK", 6),
|
|
782
|
+
("OPEN", 6), ("LIGHT", 7), ("SEARCH", 8), ("EXAMINE", 8), ("SCORE", 9)]
|
|
783
|
+
NOUNS = [("NORTH", 1), ("N", 1), ("SOUTH", 2), ("S", 2), ("EAST", 3), ("E", 3),
|
|
784
|
+
("WEST", 4), ("W", 4), ("UP", 5), ("U", 5), ("DOWN", 6), ("D", 6),
|
|
785
|
+
("KEY", 7), ("LAMP", 8), ("LANTERN", 8), ("NET", 9), ("OIL", 10),
|
|
786
|
+
("CAN", 10), ("DOOR", 11), ("BEACON", 12)]
|
|
787
|
+
|
|
788
|
+
# Condition and command opcodes; each is one byte of op and one of argument.
|
|
789
|
+
C_AT, C_CARRIED, C_HERE, C_ACCESS, C_EXISTS, C_FLAG, C_NOTFLAG, C_CTREQ, C_DARK = range(1, 10)
|
|
790
|
+
K_MSG, K_GOTO, K_LOOK, K_INV, K_SCORE, K_SWAP, K_PLACE, K_DESTROY, K_SETF, \
|
|
791
|
+
K_SETC, K_DECC, K_ADDSC, K_DIE, K_WIN = range(1, 15)
|
|
792
|
+
|
|
793
|
+
# (verb, noun, [conditions], [commands]); verb 0 = occurrence, run every turn.
|
|
794
|
+
ACTIONS = [
|
|
795
|
+
(4, 0, [], [(K_LOOK, 0)]),
|
|
796
|
+
(5, 0, [], [(K_INV, 0)]),
|
|
797
|
+
(9, 0, [], [(K_SCORE, 0)]),
|
|
798
|
+
(1, 1, [(C_AT, 2), (C_HERE, 8)], [(K_GOTO, 3), (K_LOOK, 0)]),
|
|
799
|
+
(1, 1, [(C_AT, 2)], [(K_MSG, 32)]),
|
|
800
|
+
(1, 6, [(C_AT, 5), (C_DARK, 0)], [(K_MSG, 41), (K_DIE, 0)]),
|
|
801
|
+
(6, 11, [(C_AT, 2), (C_HERE, 7), (C_CARRIED, 1)], [(K_SWAP, 7), (K_ADDSC, 20), (K_MSG, 33)]),
|
|
802
|
+
(6, 11, [(C_AT, 2), (C_HERE, 7)], [(K_MSG, 34)]),
|
|
803
|
+
(8, 9, [(C_ACCESS, 5), (C_NOTFLAG, 0)], [(K_SETF, 0), (K_PLACE, 1), (K_ADDSC, 20), (K_MSG, 35)]),
|
|
804
|
+
(8, 9, [(C_ACCESS, 5)], [(K_MSG, 36)]),
|
|
805
|
+
(7, 8, [(C_ACCESS, 2)], [(K_SWAP, 2), (K_SETC, LAMPTIME), (K_MSG, 37)]),
|
|
806
|
+
(7, 8, [(C_ACCESS, 4)], [(K_MSG, 38)]),
|
|
807
|
+
(7, 12, [(C_AT, 6), (C_CARRIED, 6)], [(K_DESTROY, 6), (K_ADDSC, 60), (K_MSG, 39), (K_WIN, 0)]),
|
|
808
|
+
(7, 12, [(C_AT, 6)], [(K_MSG, 40)]),
|
|
809
|
+
(0, 0, [(C_EXISTS, 3)], [(K_DECC, 0)]),
|
|
810
|
+
(0, 0, [(C_EXISTS, 3), (C_CTREQ, 3)], [(K_MSG, 42)]),
|
|
811
|
+
(0, 0, [(C_EXISTS, 3), (C_CTREQ, 0)], [(K_SWAP, 3), (K_MSG, 43)]),
|
|
812
|
+
]
|
|
813
|
+
|
|
814
|
+
SCRIPT = ["XYZZY", "N", "GO NORTH", "E", "TAKE LANTERN", "EXAMINE NET",
|
|
815
|
+
"GET KEY", "W", "UNLOCK DOOR", "N", "LIGHT LAMP", "LOOK", "D",
|
|
816
|
+
"GET OIL", "I", "U", "U", "U", "LIGHT BEACON"]
|
|
817
|
+
|
|
818
|
+
# ---- text packing: 5-bit codes, three to a 16-bit word, bit 15 ends a message
|
|
819
|
+
ALPHA = " ABCDEFGHIJKLMNOPQRSTUVWXYZ.,'!" # codes 0-30; 31 pads
|
|
820
|
+
def pack(s):
|
|
821
|
+
codes = [ALPHA.index(ch) for ch in s]
|
|
822
|
+
while len(codes) % 3: codes.append(31)
|
|
823
|
+
words = [(codes[i] << 10) | (codes[i + 1] << 5) | codes[i + 2]
|
|
824
|
+
for i in range(0, len(codes), 3)]
|
|
825
|
+
words[-1] |= 0x8000
|
|
826
|
+
return words
|
|
827
|
+
|
|
828
|
+
def screen_code(ch): # ASCII/PETSCII upper case -> screen code
|
|
829
|
+
o = ord(ch)
|
|
830
|
+
return o - 0x40 if 0x41 <= o <= 0x5a else o
|
|
831
|
+
|
|
832
|
+
# ---- the engine
|
|
833
|
+
class Game:
|
|
834
|
+
def __init__(s):
|
|
835
|
+
s.room, s.loc = 1, [it[2] for it in ITEMS]
|
|
836
|
+
s.flags, s.ctr, s.score, s.turns, s.over = 0, 0, 0, 0, 0
|
|
837
|
+
s.out = [] # screen codes, 0xFF = newline
|
|
838
|
+
s.term = [[0x20] * 40 for _ in range(21)]
|
|
839
|
+
s.col, s.chk = 0, 0
|
|
840
|
+
|
|
841
|
+
# output buffer
|
|
842
|
+
def o_msg(s, m): s.out += [screen_code(c) for c in MSGS[m]]
|
|
843
|
+
def o_nl(s): s.out.append(0xFF)
|
|
844
|
+
def o_word(s, w): s.out += [screen_code(c) for c in w[:8]]
|
|
845
|
+
def o_num(s, n):
|
|
846
|
+
s.out += [0x30 + int(d) for d in str(n)]
|
|
847
|
+
|
|
848
|
+
def light(s):
|
|
849
|
+
return s.room not in DARK or s.loc[LIGHT] in (CARRIED, s.room)
|
|
850
|
+
|
|
851
|
+
def look(s):
|
|
852
|
+
if not s.light(): s.o_msg(17); s.o_nl(); return
|
|
853
|
+
s.o_msg(ROOM_DESC[s.room]); s.o_nl()
|
|
854
|
+
seen = [i for i in range(1, len(ITEMS)) if s.loc[i] == s.room]
|
|
855
|
+
if seen:
|
|
856
|
+
s.o_msg(18)
|
|
857
|
+
for k, i in enumerate(seen):
|
|
858
|
+
if k: s.out += [0x2C, 0x20]
|
|
859
|
+
s.o_msg(ITEMS[i][0])
|
|
860
|
+
s.out.append(0x2E); s.o_nl()
|
|
861
|
+
s.o_msg(19)
|
|
862
|
+
for d in range(6):
|
|
863
|
+
if EXITS[s.room][d]: s.out += [0x20, screen_code("NSEWUD"[d])]
|
|
864
|
+
s.out.append(0x2E); s.o_nl()
|
|
865
|
+
|
|
866
|
+
def inv(s):
|
|
867
|
+
s.o_msg(20)
|
|
868
|
+
held = [i for i in range(1, len(ITEMS)) if s.loc[i] == CARRIED]
|
|
869
|
+
if not held: s.o_msg(21)
|
|
870
|
+
for k, i in enumerate(held):
|
|
871
|
+
if k: s.out += [0x2C, 0x20]
|
|
872
|
+
s.o_msg(ITEMS[i][0])
|
|
873
|
+
s.out.append(0x2E); s.o_nl()
|
|
874
|
+
|
|
875
|
+
def cond(s, op, a):
|
|
876
|
+
if op == C_AT: return s.room == a
|
|
877
|
+
if op == C_CARRIED: return s.loc[a] == CARRIED
|
|
878
|
+
if op == C_HERE: return s.loc[a] == s.room
|
|
879
|
+
if op == C_ACCESS: return s.loc[a] in (CARRIED, s.room)
|
|
880
|
+
if op == C_EXISTS: return s.loc[a] != 0
|
|
881
|
+
if op == C_FLAG: return bool(s.flags >> a & 1)
|
|
882
|
+
if op == C_NOTFLAG: return not (s.flags >> a & 1)
|
|
883
|
+
if op == C_CTREQ: return s.ctr == a
|
|
884
|
+
if op == C_DARK: return not s.light()
|
|
885
|
+
|
|
886
|
+
def cmd(s, op, a):
|
|
887
|
+
if op == K_MSG: s.o_msg(a); s.o_nl()
|
|
888
|
+
elif op == K_GOTO: s.room = a
|
|
889
|
+
elif op == K_LOOK: s.look()
|
|
890
|
+
elif op == K_INV: s.inv()
|
|
891
|
+
elif op == K_SCORE: s.say_score()
|
|
892
|
+
elif op == K_SWAP: s.loc[a], s.loc[a + 1] = s.loc[a + 1], s.loc[a]
|
|
893
|
+
elif op == K_PLACE: s.loc[a] = s.room
|
|
894
|
+
elif op == K_DESTROY: s.loc[a] = 0
|
|
895
|
+
elif op == K_SETF: s.flags |= 1 << a
|
|
896
|
+
elif op == K_SETC: s.ctr = a
|
|
897
|
+
elif op == K_DECC:
|
|
898
|
+
if s.ctr: s.ctr -= 1
|
|
899
|
+
elif op == K_ADDSC: s.score += a
|
|
900
|
+
elif op == K_DIE: s.o_msg(48); s.o_nl(); s.over = 2
|
|
901
|
+
elif op == K_WIN: s.o_msg(47); s.o_nl(); s.say_score(); s.over = 1
|
|
902
|
+
|
|
903
|
+
def say_score(s):
|
|
904
|
+
s.o_msg(44); s.o_num(s.score); s.o_msg(45); s.o_num(100); s.o_msg(50); s.o_num(s.turns); s.o_msg(46); s.o_nl()
|
|
905
|
+
|
|
906
|
+
def run_entry(s, a):
|
|
907
|
+
for op, arg in a[2]:
|
|
908
|
+
if not s.cond(op, arg): return False
|
|
909
|
+
for op, arg in a[3]:
|
|
910
|
+
s.cmd(op, arg)
|
|
911
|
+
return True
|
|
912
|
+
|
|
913
|
+
# parse: two words, truncated to WL letters, synonyms share an id
|
|
914
|
+
def parse(s, line):
|
|
915
|
+
words = line.split()
|
|
916
|
+
def find(table, w):
|
|
917
|
+
for t, i in table:
|
|
918
|
+
if t[:WL] == w[:WL]: return i
|
|
919
|
+
return 0
|
|
920
|
+
if not words: return None
|
|
921
|
+
v = find(VERBS, words[0])
|
|
922
|
+
n = find(NOUNS, words[1]) if len(words) > 1 else 0
|
|
923
|
+
if not v:
|
|
924
|
+
d = find(NOUNS, words[0])
|
|
925
|
+
if 1 <= d <= 6 and len(words) == 1: return (1, d)
|
|
926
|
+
s.o_msg(22); s.o_word(words[0]); s.out.append(0x2E); s.o_nl(); return None
|
|
927
|
+
if len(words) > 1 and not n:
|
|
928
|
+
s.o_msg(23); s.o_word(words[1]); s.o_msg(24); s.o_nl(); return None
|
|
929
|
+
return (v, n)
|
|
930
|
+
|
|
931
|
+
def turn(s, v, n):
|
|
932
|
+
s.turns += 1
|
|
933
|
+
for a in ACTIONS:
|
|
934
|
+
if a[0] == v and (a[1] == 0 or a[1] == n) and a[0] and s.run_entry(a):
|
|
935
|
+
break
|
|
936
|
+
else:
|
|
937
|
+
s.builtin(v, n)
|
|
938
|
+
if not s.over:
|
|
939
|
+
for a in ACTIONS:
|
|
940
|
+
if a[0] == 0: s.run_entry(a)
|
|
941
|
+
|
|
942
|
+
def builtin(s, v, n):
|
|
943
|
+
if v == 1:
|
|
944
|
+
if not 1 <= n <= 6: s.o_msg(49); s.o_nl(); return
|
|
945
|
+
r = EXITS[s.room][n - 1]
|
|
946
|
+
if not r: s.o_msg(25); s.o_nl(); return
|
|
947
|
+
s.room = r; s.look(); return
|
|
948
|
+
if v in (2, 3) and n:
|
|
949
|
+
held = sum(1 for l in s.loc if l == CARRIED)
|
|
950
|
+
for i in range(1, len(ITEMS)):
|
|
951
|
+
if ITEMS[i][1] != n: continue
|
|
952
|
+
if v == 2 and s.loc[i] == CARRIED: s.o_msg(28); s.o_nl(); return
|
|
953
|
+
if v == 2 and s.loc[i] == s.room:
|
|
954
|
+
if not s.light(): s.o_msg(17)
|
|
955
|
+
elif held >= MAXLOAD: s.o_msg(27)
|
|
956
|
+
else: s.loc[i] = CARRIED; s.o_msg(26)
|
|
957
|
+
s.o_nl(); return
|
|
958
|
+
if v == 3 and s.loc[i] == CARRIED:
|
|
959
|
+
s.loc[i] = s.room; s.o_msg(26); s.o_nl(); return
|
|
960
|
+
s.o_msg(29 if v == 2 else 30); s.o_nl(); return
|
|
961
|
+
s.o_msg(31); s.o_nl()
|
|
962
|
+
|
|
963
|
+
# terminal: 21 rows, word wrap at 40, scroll up on newline
|
|
964
|
+
def t_nl(s):
|
|
965
|
+
s.term = s.term[1:] + [[0x20] * 40]; s.col = 0
|
|
966
|
+
s.fold(0xFF)
|
|
967
|
+
def t_put(s, c):
|
|
968
|
+
if s.col == 40: s.t_nl()
|
|
969
|
+
s.term[20][s.col] = c; s.col += 1; s.fold(c)
|
|
970
|
+
def fold(s, v): s.chk = ((s.chk ^ v) * 5 + 1) & 0xFFFF
|
|
971
|
+
def flush(s):
|
|
972
|
+
seg = []
|
|
973
|
+
for c in s.out:
|
|
974
|
+
if c == 0xFF: s.t_text(seg); s.t_nl(); seg = []
|
|
975
|
+
else: seg.append(c)
|
|
976
|
+
if seg: s.t_text(seg)
|
|
977
|
+
s.out = []
|
|
978
|
+
def t_text(s, seg):
|
|
979
|
+
i, n = 0, len(seg)
|
|
980
|
+
while i < n:
|
|
981
|
+
j = i
|
|
982
|
+
while j < n and seg[j] != 0x20: j += 1
|
|
983
|
+
if s.col and s.col + (j - i) > 40: s.t_nl()
|
|
984
|
+
for k in range(i, j): s.t_put(seg[k])
|
|
985
|
+
if j < n and s.col < 40: s.t_put(0x20)
|
|
986
|
+
i = j + 1
|
|
987
|
+
|
|
988
|
+
def play(s):
|
|
989
|
+
s.look(); s.flush()
|
|
990
|
+
for line in SCRIPT:
|
|
991
|
+
if s.over: break
|
|
992
|
+
s.t_put(0x3E) # prompt '>'
|
|
993
|
+
for ch in line: s.t_put(screen_code(ch))
|
|
994
|
+
s.t_nl()
|
|
995
|
+
p = s.parse(line)
|
|
996
|
+
if p: s.turn(*p)
|
|
997
|
+
s.flush()
|
|
998
|
+
|
|
999
|
+
if __name__ == "__main__":
|
|
1000
|
+
for tab in (VERBS, NOUNS): # truncation must not merge two ids
|
|
1001
|
+
seen = {}
|
|
1002
|
+
for w, i in tab:
|
|
1003
|
+
assert seen.setdefault(w[:WL], i) == i, w
|
|
1004
|
+
g = Game(); g.play()
|
|
1005
|
+
inv = sum(1 << i for i in range(1, len(ITEMS)) if g.loc[i] == CARRIED)
|
|
1006
|
+
pk = 0
|
|
1007
|
+
for w in [w for m in MSGS[1:] for w in pack(m)]:
|
|
1008
|
+
pk = ((pk ^ (w & 0xFF)) * 5 + 1) & 0xFFFF
|
|
1009
|
+
pk = ((pk ^ (w >> 8)) * 5 + 1) & 0xFFFF
|
|
1010
|
+
chars = sum(len(m) for m in MSGS[1:])
|
|
1011
|
+
nbytes = 2 * sum(len(pack(m)) for m in MSGS[1:])
|
|
1012
|
+
print(g.room, g.turns, g.score, g.over, "%04X %02X %04X %04X" % (inv, g.flags, g.chk, pk),
|
|
1013
|
+
nbytes, chars) # 6 18 100 1 0012 01 7A7A 0774 752 1061
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
## Why this works
|
|
1017
|
+
|
|
1018
|
+
The game is the tables; the code is the same for any game. `scan()`
|
|
1019
|
+
walks the action table from the top and stops at the first entry whose
|
|
1020
|
+
verb and noun match and whose conditions all hold, so the order of
|
|
1021
|
+
entries carries the logic. The two `UNLOCK DOOR` entries are the
|
|
1022
|
+
pattern: the first needs the key and opens the door, the second catches
|
|
1023
|
+
the same command without the key and says so. The locked door is not in
|
|
1024
|
+
the exit table at all. `GO NORTH` at the path matches an action that
|
|
1025
|
+
needs the open door and moves the player; the entry after it prints
|
|
1026
|
+
"THE DOOR IS LOCKED". The script tries it once before the key is found.
|
|
1027
|
+
|
|
1028
|
+
`SWAP i` trades the locations of items `i` and `i + 1`. The lamp's three
|
|
1029
|
+
states are items 2, 3 and 4, so lighting it is one swap and its going
|
|
1030
|
+
out is another, and whichever state is carried stays carried. The three
|
|
1031
|
+
occurrences are the lamp's clock: each turn the lit lamp exists they
|
|
1032
|
+
decrease the counter, print a warning at 3 and swap in the dead lamp at
|
|
1033
|
+
0. In the script the warning comes in the cellar and the lamp goes out
|
|
1034
|
+
on the stair, one move short of the lamp room, which is not dark.
|
|
1035
|
+
|
|
1036
|
+
The parser works on PETSCII, the encoding GETIN delivers, because the
|
|
1037
|
+
vocabulary is stored as upper-case ASCII, which is the same bytes for
|
|
1038
|
+
`$20` to `$5A`. Only the echo is converted to screen codes, by
|
|
1039
|
+
`sc_of()`. Messages never exist as PETSCII at run time: the 5-bit
|
|
1040
|
+
alphabet decodes straight to screen codes. Oscar64's `getchx()` turns
|
|
1041
|
+
RETURN into `$0A`, so the loop accepts `$0D` and `$0A`
|
|
1042
|
+
(`pitfalls/kernal-and-io.md`, `getchx_petscii_remaps_return`).
|
|
1043
|
+
|
|
1044
|
+
The autopilot writes one byte to the queue at `$0277` and sets the count
|
|
1045
|
+
at `$C6` to 1, with interrupts off, only when the count is zero. GETIN
|
|
1046
|
+
then takes the key exactly as if it had been typed, so the line editor,
|
|
1047
|
+
the echo and the parser see nothing different.
|
|
1048
|
+
|
|
1049
|
+
The timings use CIA2 because CIA1 timer A drives the KERNAL interrupt
|
|
1050
|
+
that fills the keyboard queue. Interrupts are off while a parse, a turn
|
|
1051
|
+
or a print is timed, so the figures are the engine's alone; the queue
|
|
1052
|
+
is filled between commands. Printing is the slow part, over five frames
|
|
1053
|
+
for the longest output. Almost all of it is the scroll: the debugging
|
|
1054
|
+
build below showed about 17,400 cycles for a command that prints one
|
|
1055
|
+
line, which is one 800-byte scroll in C.
|
|
1056
|
+
|
|
1057
|
+
**Which command is the worst.** A build that kept every command's
|
|
1058
|
+
figures (not pinned; its code layout differs, so its parse and turn are within
|
|
1059
|
+
about 90 cycles of the pinned ones; print and the miss scan differ by up
|
|
1060
|
+
to about 4,000) showed the parse maximum on `XYZZY`
|
|
1061
|
+
(both tables scanned, then an error message decoded), the turn maximum
|
|
1062
|
+
and the `CMD` maximum on `LIGHT BEACON` (the win: two long messages and
|
|
1063
|
+
the score line), and turns of 7,800 to 12,000 cycles on a move into a room whose items
|
|
1064
|
+
are listed, depending on how much text is decoded (debug build).
|
|
1065
|
+
|
|
1066
|
+
## Verification
|
|
1067
|
+
|
|
1068
|
+
VICE x64sc 3.10, windowless build, Oscar64 as installed on 2026-09-23.
|
|
1069
|
+
|
|
1070
|
+
**The terminal against the model.** A script read the 840 cells of rows
|
|
1071
|
+
0 to 20 in both pictures, matched each 8x8 cell against
|
|
1072
|
+
`chargen-901225-01.bin`, and compared the screen codes with the model's
|
|
1073
|
+
`g.term`. 0 mismatches and 0 undecoded cells on PAL and on NTSC.
|
|
1074
|
+
|
|
1075
|
+
**Text and border.** Rows 21 to 24 were decoded the same way and read
|
|
1076
|
+
as quoted above. The pictures hold three colours: black, the border
|
|
1077
|
+
(98, 213, 50) on PAL and (114, 189, 103) on NTSC, colour 5, and the
|
|
1078
|
+
text (183, 255, 134) and (198, 255, 186), colour 13
|
|
1079
|
+
(`runtime/vice-reference.md`, "The default palette").
|
|
1080
|
+
|
|
1081
|
+
**Reproducibility.** The pinned command was run twice per model; the
|
|
1082
|
+
PNG bytes were identical each time.
|
|
1083
|
+
|
|
1084
|
+
**Lint.** `npx tsx src/cli.ts lint adventure-engine.c`: no findings.
|
|
1085
|
+
|
|
1086
|
+
## Sources
|
|
1087
|
+
|
|
1088
|
+
- https://www.miketaylor.org.uk/tech/advent/sac/Manual.html (the
|
|
1089
|
+
Scott Adams Adventure Compiler (sac) manual): the database model this engine follows, including
|
|
1090
|
+
the condition and result names, occurrences, first-match evaluation
|
|
1091
|
+
and darkness. The game here is original.
|
|
1092
|
+
- `text-input.md`: the GETIN key path and the RETURN test, reused in
|
|
1093
|
+
shape. `cave-scan.md` and `print-number.md`: the cascaded timer
|
|
1094
|
+
harness, here on CIA2, and the fold.
|