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.
Files changed (970) hide show
  1. package/CHANGELOG.md +2082 -0
  2. package/LICENSE +29 -0
  3. package/README.md +559 -0
  4. package/VERSION +25 -0
  5. package/dist/cli/setup.js +66 -0
  6. package/dist/cli/setup.js.map +1 -0
  7. package/dist/cli-memorize-audit.js +97 -0
  8. package/dist/cli-memorize-audit.js.map +1 -0
  9. package/dist/cli.js +335 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/config.js +83 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/context.js +53 -0
  14. package/dist/context.js.map +1 -0
  15. package/dist/domain/budget-notes.js +104 -0
  16. package/dist/domain/budget-notes.js.map +1 -0
  17. package/dist/domain/budget.js +417 -0
  18. package/dist/domain/budget.js.map +1 -0
  19. package/dist/domain/game-design.js +77 -0
  20. package/dist/domain/game-design.js.map +1 -0
  21. package/dist/domain/timing.js +39 -0
  22. package/dist/domain/timing.js.map +1 -0
  23. package/dist/graph/apply.js +132 -0
  24. package/dist/graph/apply.js.map +1 -0
  25. package/dist/graph/claims.js +231 -0
  26. package/dist/graph/claims.js.map +1 -0
  27. package/dist/graph/extract/archetype.js +118 -0
  28. package/dist/graph/extract/archetype.js.map +1 -0
  29. package/dist/graph/extract/common.js +69 -0
  30. package/dist/graph/extract/common.js.map +1 -0
  31. package/dist/graph/extract/failure.js +37 -0
  32. package/dist/graph/extract/failure.js.map +1 -0
  33. package/dist/graph/extract/game-design.js +189 -0
  34. package/dist/graph/extract/game-design.js.map +1 -0
  35. package/dist/graph/extract/hardware.js +124 -0
  36. package/dist/graph/extract/hardware.js.map +1 -0
  37. package/dist/graph/extract/pitfall.js +62 -0
  38. package/dist/graph/extract/pitfall.js.map +1 -0
  39. package/dist/graph/extract/raster-band.js +56 -0
  40. package/dist/graph/extract/raster-band.js.map +1 -0
  41. package/dist/graph/extract/recipe.js +71 -0
  42. package/dist/graph/extract/recipe.js.map +1 -0
  43. package/dist/graph/extract/technique-entities.js +180 -0
  44. package/dist/graph/extract/technique-entities.js.map +1 -0
  45. package/dist/graph/extract/technique.js +179 -0
  46. package/dist/graph/extract/technique.js.map +1 -0
  47. package/dist/graph/extract/toolchain.js +67 -0
  48. package/dist/graph/extract/toolchain.js.map +1 -0
  49. package/dist/graph/extract/types.js +3 -0
  50. package/dist/graph/extract/types.js.map +1 -0
  51. package/dist/graph/extract/vocabulary.js +65 -0
  52. package/dist/graph/extract/vocabulary.js.map +1 -0
  53. package/dist/graph/extract.js +43 -0
  54. package/dist/graph/extract.js.map +1 -0
  55. package/dist/graph/kernal-clobbers.js +64 -0
  56. package/dist/graph/kernal-clobbers.js.map +1 -0
  57. package/dist/graph/machine-variants.js +208 -0
  58. package/dist/graph/machine-variants.js.map +1 -0
  59. package/dist/ingest/files.js +99 -0
  60. package/dist/ingest/files.js.map +1 -0
  61. package/dist/ingest/passes.js +104 -0
  62. package/dist/ingest/passes.js.map +1 -0
  63. package/dist/ingest/points.js +50 -0
  64. package/dist/ingest/points.js.map +1 -0
  65. package/dist/ingest/report.js +140 -0
  66. package/dist/ingest/report.js.map +1 -0
  67. package/dist/ingest/tally.js +81 -0
  68. package/dist/ingest/tally.js.map +1 -0
  69. package/dist/ingest/verified-on.js +43 -0
  70. package/dist/ingest/verified-on.js.map +1 -0
  71. package/dist/ingest.js +135 -0
  72. package/dist/ingest.js.map +1 -0
  73. package/dist/schemas/cost-basis.js +5 -0
  74. package/dist/schemas/cost-basis.js.map +1 -0
  75. package/dist/schemas/machine-variant.js +25 -0
  76. package/dist/schemas/machine-variant.js.map +1 -0
  77. package/dist/schemas/plan-budget.js +121 -0
  78. package/dist/schemas/plan-budget.js.map +1 -0
  79. package/dist/schemas/tool-outputs.js +526 -0
  80. package/dist/schemas/tool-outputs.js.map +1 -0
  81. package/dist/server/define-tool.js +52 -0
  82. package/dist/server/define-tool.js.map +1 -0
  83. package/dist/server/defined-only.js +9 -0
  84. package/dist/server/defined-only.js.map +1 -0
  85. package/dist/server/tools-briefings.js +70 -0
  86. package/dist/server/tools-briefings.js.map +1 -0
  87. package/dist/server/tools-lookup.js +180 -0
  88. package/dist/server/tools-lookup.js.map +1 -0
  89. package/dist/server/tools-maintenance.js +116 -0
  90. package/dist/server/tools-maintenance.js.map +1 -0
  91. package/dist/server/tools-pitfalls.js +89 -0
  92. package/dist/server/tools-pitfalls.js.map +1 -0
  93. package/dist/server/tools-recipes.js +296 -0
  94. package/dist/server/tools-recipes.js.map +1 -0
  95. package/dist/server/tools-runtime.js +48 -0
  96. package/dist/server/tools-runtime.js.map +1 -0
  97. package/dist/server/tools.js +43 -0
  98. package/dist/server/tools.js.map +1 -0
  99. package/dist/server.js +95 -0
  100. package/dist/server.js.map +1 -0
  101. package/dist/services/analytics/schema.js +78 -0
  102. package/dist/services/analytics/schema.js.map +1 -0
  103. package/dist/services/analytics/statements.js +99 -0
  104. package/dist/services/analytics/statements.js.map +1 -0
  105. package/dist/services/analytics.js +195 -0
  106. package/dist/services/analytics.js.map +1 -0
  107. package/dist/services/bm25.js +158 -0
  108. package/dist/services/bm25.js.map +1 -0
  109. package/dist/services/chunker.js +81 -0
  110. package/dist/services/chunker.js.map +1 -0
  111. package/dist/services/embeddings.js +58 -0
  112. package/dist/services/embeddings.js.map +1 -0
  113. package/dist/services/falkor/base.js +85 -0
  114. package/dist/services/falkor/base.js.map +1 -0
  115. package/dist/services/falkor/links.js +361 -0
  116. package/dist/services/falkor/links.js.map +1 -0
  117. package/dist/services/falkor/nodes.js +183 -0
  118. package/dist/services/falkor/nodes.js.map +1 -0
  119. package/dist/services/falkor/params.js +45 -0
  120. package/dist/services/falkor/params.js.map +1 -0
  121. package/dist/services/falkor/schema.js +77 -0
  122. package/dist/services/falkor/schema.js.map +1 -0
  123. package/dist/services/falkor.js +100 -0
  124. package/dist/services/falkor.js.map +1 -0
  125. package/dist/services/qdrant.js +238 -0
  126. package/dist/services/qdrant.js.map +1 -0
  127. package/dist/services/versions.js +73 -0
  128. package/dist/services/versions.js.map +1 -0
  129. package/dist/services/vice-bin.js +51 -0
  130. package/dist/services/vice-bin.js.map +1 -0
  131. package/dist/tools/briefings/archetype.js +174 -0
  132. package/dist/tools/briefings/archetype.js.map +1 -0
  133. package/dist/tools/briefings/budget.js +177 -0
  134. package/dist/tools/briefings/budget.js.map +1 -0
  135. package/dist/tools/briefings/build-order.js +135 -0
  136. package/dist/tools/briefings/build-order.js.map +1 -0
  137. package/dist/tools/briefings/build.js +185 -0
  138. package/dist/tools/briefings/build.js.map +1 -0
  139. package/dist/tools/briefings/discovery.js +290 -0
  140. package/dist/tools/briefings/discovery.js.map +1 -0
  141. package/dist/tools/briefings/plan-pitfalls.js +69 -0
  142. package/dist/tools/briefings/plan-pitfalls.js.map +1 -0
  143. package/dist/tools/briefings/render.js +151 -0
  144. package/dist/tools/briefings/render.js.map +1 -0
  145. package/dist/tools/briefings/rows.js +41 -0
  146. package/dist/tools/briefings/rows.js.map +1 -0
  147. package/dist/tools/briefings/toolchain.js +63 -0
  148. package/dist/tools/briefings/toolchain.js.map +1 -0
  149. package/dist/tools/briefings/why-proposed.js +69 -0
  150. package/dist/tools/briefings/why-proposed.js.map +1 -0
  151. package/dist/tools/briefings.js +44 -0
  152. package/dist/tools/briefings.js.map +1 -0
  153. package/dist/tools/feedback.js +97 -0
  154. package/dist/tools/feedback.js.map +1 -0
  155. package/dist/tools/hydrate.js +75 -0
  156. package/dist/tools/hydrate.js.map +1 -0
  157. package/dist/tools/intelligence.js +67 -0
  158. package/dist/tools/intelligence.js.map +1 -0
  159. package/dist/tools/lint/asm-decimal.js +86 -0
  160. package/dist/tools/lint/asm-decimal.js.map +1 -0
  161. package/dist/tools/lint/asm-rules.js +221 -0
  162. package/dist/tools/lint/asm-rules.js.map +1 -0
  163. package/dist/tools/lint/asm-shared.js +3 -0
  164. package/dist/tools/lint/asm-shared.js.map +1 -0
  165. package/dist/tools/lint/c-rules.js +215 -0
  166. package/dist/tools/lint/c-rules.js.map +1 -0
  167. package/dist/tools/lint/text.js +122 -0
  168. package/dist/tools/lint/text.js.map +1 -0
  169. package/dist/tools/lint/types.js +40 -0
  170. package/dist/tools/lint/types.js.map +1 -0
  171. package/dist/tools/lint.js +76 -0
  172. package/dist/tools/lint.js.map +1 -0
  173. package/dist/tools/memorization-mcp.js +111 -0
  174. package/dist/tools/memorization-mcp.js.map +1 -0
  175. package/dist/tools/pitfalls/bm25.js +53 -0
  176. package/dist/tools/pitfalls/bm25.js.map +1 -0
  177. package/dist/tools/pitfalls/format.js +58 -0
  178. package/dist/tools/pitfalls/format.js.map +1 -0
  179. package/dist/tools/pitfalls/graph.js +124 -0
  180. package/dist/tools/pitfalls/graph.js.map +1 -0
  181. package/dist/tools/pitfalls.js +134 -0
  182. package/dist/tools/pitfalls.js.map +1 -0
  183. package/dist/tools/prompts.js +81 -0
  184. package/dist/tools/prompts.js.map +1 -0
  185. package/dist/tools/query/compatibility/closure.js +62 -0
  186. package/dist/tools/query/compatibility/closure.js.map +1 -0
  187. package/dist/tools/query/compatibility/facts.js +28 -0
  188. package/dist/tools/query/compatibility/facts.js.map +1 -0
  189. package/dist/tools/query/compatibility/fetch.js +164 -0
  190. package/dist/tools/query/compatibility/fetch.js.map +1 -0
  191. package/dist/tools/query/compatibility/hard-rules.js +159 -0
  192. package/dist/tools/query/compatibility/hard-rules.js.map +1 -0
  193. package/dist/tools/query/compatibility/index.js +27 -0
  194. package/dist/tools/query/compatibility/index.js.map +1 -0
  195. package/dist/tools/query/compatibility/kernal-zp-rule.js +90 -0
  196. package/dist/tools/query/compatibility/kernal-zp-rule.js.map +1 -0
  197. package/dist/tools/query/compatibility/render.js +106 -0
  198. package/dist/tools/query/compatibility/render.js.map +1 -0
  199. package/dist/tools/query/compatibility/rules.js +337 -0
  200. package/dist/tools/query/compatibility/rules.js.map +1 -0
  201. package/dist/tools/query/compatibility/unit-rules.js +253 -0
  202. package/dist/tools/query/compatibility/unit-rules.js.map +1 -0
  203. package/dist/tools/query/game-design.js +83 -0
  204. package/dist/tools/query/game-design.js.map +1 -0
  205. package/dist/tools/query/lookups.js +220 -0
  206. package/dist/tools/query/lookups.js.map +1 -0
  207. package/dist/tools/query/plan-budget.js +266 -0
  208. package/dist/tools/query/plan-budget.js.map +1 -0
  209. package/dist/tools/query/recipes.js +214 -0
  210. package/dist/tools/query/recipes.js.map +1 -0
  211. package/dist/tools/query/retrieval.js +128 -0
  212. package/dist/tools/query/retrieval.js.map +1 -0
  213. package/dist/tools/query/shared.js +121 -0
  214. package/dist/tools/query/shared.js.map +1 -0
  215. package/dist/tools/query/techniques.js +331 -0
  216. package/dist/tools/query/techniques.js.map +1 -0
  217. package/dist/tools/query/timing.js +106 -0
  218. package/dist/tools/query/timing.js.map +1 -0
  219. package/dist/tools/query/types.js +11 -0
  220. package/dist/tools/query/types.js.map +1 -0
  221. package/dist/tools/query.js +21 -0
  222. package/dist/tools/query.js.map +1 -0
  223. package/dist/tools/resources.js +186 -0
  224. package/dist/tools/resources.js.map +1 -0
  225. package/dist/tools/run-game.js +249 -0
  226. package/dist/tools/run-game.js.map +1 -0
  227. package/dist/tools/selfimprovement.js +367 -0
  228. package/dist/tools/selfimprovement.js.map +1 -0
  229. package/docker-compose.yml +35 -0
  230. package/docs/ARCHITECTURE.md +164 -0
  231. package/docs/CONVENTIONS-archetypes.md +158 -0
  232. package/docs/CONVENTIONS-failures.md +46 -0
  233. package/docs/CONVENTIONS-game-designs.md +75 -0
  234. package/docs/CONVENTIONS-hardware-reference.md +184 -0
  235. package/docs/CONVENTIONS-pitfalls.md +91 -0
  236. package/docs/CONVENTIONS-recipes.md +106 -0
  237. package/docs/CONVENTIONS-techniques.md +309 -0
  238. package/docs/CONVENTIONS-toolchain-reference.md +89 -0
  239. package/docs/ONTOLOGY.md +655 -0
  240. package/docs/art/art-production-reference.md +157 -0
  241. package/docs/art/asset-pipelines.md +586 -0
  242. package/docs/c64-failure-patterns.md +693 -0
  243. package/docs/demo-design/demo-composition.md +349 -0
  244. package/docs/demo-design/demo-design-philosophy.md +320 -0
  245. package/docs/demo-design/intro-cracktro-patterns.md +435 -0
  246. package/docs/figures/ctm-spd-embed-probe.png +0 -0
  247. package/docs/figures/dot-flag-mid-5000000.png +0 -0
  248. package/docs/figures/dot-flag-no-erase-12000000.png +0 -0
  249. package/docs/figures/edges.json +1 -0
  250. package/docs/figures/fig6-architecture.png +0 -0
  251. package/docs/figures/fig7-ontology.png +0 -0
  252. package/docs/figures/screen-dissolve-mid-frame25.png +0 -0
  253. package/docs/figures/screen-dissolve-wipe-frame25.png +0 -0
  254. package/docs/figures/spindle-two-parts-part1.png +0 -0
  255. package/docs/figures/spindle-two-parts-part2.png +0 -0
  256. package/docs/figures/starters/action-puzzle.png +0 -0
  257. package/docs/figures/starters/adventure.png +0 -0
  258. package/docs/figures/starters/beat-em-up.png +0 -0
  259. package/docs/figures/starters/demo.png +0 -0
  260. package/docs/figures/starters/platformer.png +0 -0
  261. package/docs/figures/starters/shmup-vertical.png +0 -0
  262. package/docs/figures/tape-kernal-master-ntsc.png +0 -0
  263. package/docs/figures/tape-kernal-master-pal.png +0 -0
  264. package/docs/figures/tape-turbo-loader-ntsc.png +0 -0
  265. package/docs/figures/tape-turbo-loader-pal.png +0 -0
  266. package/docs/figures/tape-turbo-master-ntsc.png +0 -0
  267. package/docs/figures/tape-turbo-master-pal.png +0 -0
  268. package/docs/figures/tech-tech-pad2-12000000.png +0 -0
  269. package/docs/figures/tech-tech-pad4-12000000.png +0 -0
  270. package/docs/figures/tech-tech-seven-12000000.png +0 -0
  271. package/docs/figures/twister-mid-5000000.png +0 -0
  272. package/docs/figures/twister-straight-12000000.png +0 -0
  273. package/docs/figures/vector-balls-nosort-ntsc.png +0 -0
  274. package/docs/figures/vector-balls-nosort.png +0 -0
  275. package/docs/formats/c64-file-formats.md +1212 -0
  276. package/docs/formats/iec-disk-reference.md +754 -0
  277. package/docs/game-design/c64-game-archetypes.md +273 -0
  278. package/docs/game-design/designs/falling-blocks.md +54 -0
  279. package/docs/game-design/designs/platformer-scaffold.md +48 -0
  280. package/docs/game-design/designs/simple-shmup.md +27 -0
  281. package/docs/game-design/enemy-behaviour-and-difficulty.md +349 -0
  282. package/docs/game-design/game-design-patterns.md +1189 -0
  283. package/docs/game-design/game-structure.md +354 -0
  284. package/docs/game-design/production-planning.md +349 -0
  285. package/docs/game-design/reference-game-sources.md +90 -0
  286. package/docs/hardware/6502-illegal-opcodes.md +1353 -0
  287. package/docs/hardware/6510-cpu-reference.md +2112 -0
  288. package/docs/hardware/c64-memory-map.md +3184 -0
  289. package/docs/hardware/c64-registers-reference.md +996 -0
  290. package/docs/hardware/cia-reference.md +1281 -0
  291. package/docs/hardware/kernal-routines-reference.md +1836 -0
  292. package/docs/hardware/pal-ntsc-reference.md +813 -0
  293. package/docs/hardware/sid-reference.md +1137 -0
  294. package/docs/hardware/vic-ii-reference.md +1714 -0
  295. package/docs/music/music-production-reference.md +403 -0
  296. package/docs/pitfalls/banking.md +1067 -0
  297. package/docs/pitfalls/cia.md +1147 -0
  298. package/docs/pitfalls/cpu.md +874 -0
  299. package/docs/pitfalls/input.md +282 -0
  300. package/docs/pitfalls/kernal-and-io.md +1615 -0
  301. package/docs/pitfalls/loader.md +1191 -0
  302. package/docs/pitfalls/logic.md +94 -0
  303. package/docs/pitfalls/maths.md +187 -0
  304. package/docs/pitfalls/raster-and-badline.md +721 -0
  305. package/docs/pitfalls/region-timing.md +660 -0
  306. package/docs/pitfalls/scroll.md +207 -0
  307. package/docs/pitfalls/sid.md +738 -0
  308. package/docs/pitfalls/sprite.md +979 -0
  309. package/docs/pitfalls/text-mode-render.md +1172 -0
  310. package/docs/recipes/cc65/cartridge-8k.md +398 -0
  311. package/docs/recipes/cc65/hello-world-conio.md +76 -0
  312. package/docs/recipes/cc65/memory-layout.md +312 -0
  313. package/docs/recipes/cc65/screenshots/README.md +14 -0
  314. package/docs/recipes/cc65/screenshots/cartridge-8k-ntsc.png +0 -0
  315. package/docs/recipes/cc65/screenshots/cartridge-8k.png +0 -0
  316. package/docs/recipes/cc65/screenshots/hello-world-conio.png +0 -0
  317. package/docs/recipes/cc65/screenshots/memory-layout-ntsc.png +0 -0
  318. package/docs/recipes/cc65/screenshots/memory-layout.png +0 -0
  319. package/docs/recipes/kickassembler/basic-float-calls.md +578 -0
  320. package/docs/recipes/kickassembler/basic-wedge.md +398 -0
  321. package/docs/recipes/kickassembler/big-font-scroller.md +620 -0
  322. package/docs/recipes/kickassembler/charset-copy-rom-to-ram.md +623 -0
  323. package/docs/recipes/kickassembler/cia-revision-detect.md +366 -0
  324. package/docs/recipes/kickassembler/colour-cycling.md +343 -0
  325. package/docs/recipes/kickassembler/colour-fade.md +303 -0
  326. package/docs/recipes/kickassembler/compare-16bit-signed.md +1007 -0
  327. package/docs/recipes/kickassembler/cracktro-template.md +526 -0
  328. package/docs/recipes/kickassembler/crt-banked.md +276 -0
  329. package/docs/recipes/kickassembler/dos-error-codes.md +466 -0
  330. package/docs/recipes/kickassembler/dot-flag.md +576 -0
  331. package/docs/recipes/kickassembler/drive-job-queue.md +667 -0
  332. package/docs/recipes/kickassembler/drive-via-probe.md +690 -0
  333. package/docs/recipes/kickassembler/dycp-scroller.md +568 -0
  334. package/docs/recipes/kickassembler/dypp-sprite-scroller.md +681 -0
  335. package/docs/recipes/kickassembler/dysp.md +670 -0
  336. package/docs/recipes/kickassembler/easyflash-save.md +800 -0
  337. package/docs/recipes/kickassembler/eight-way-scroll.md +1484 -0
  338. package/docs/recipes/kickassembler/file-io-roundtrip.md +546 -0
  339. package/docs/recipes/kickassembler/fire-effect.md +420 -0
  340. package/docs/recipes/kickassembler/fld.md +414 -0
  341. package/docs/recipes/kickassembler/fli-image.md +336 -0
  342. package/docs/recipes/kickassembler/four-player-read.md +672 -0
  343. package/docs/recipes/kickassembler/headless-verify.md +228 -0
  344. package/docs/recipes/kickassembler/hello-world.md +98 -0
  345. package/docs/recipes/kickassembler/high-score-insert.md +660 -0
  346. package/docs/recipes/kickassembler/hires-plot-line.md +723 -0
  347. package/docs/recipes/kickassembler/irq-chain.md +316 -0
  348. package/docs/recipes/kickassembler/irq-owns-port.md +668 -0
  349. package/docs/recipes/kickassembler/isometric-room.md +1145 -0
  350. package/docs/recipes/kickassembler/light-pen-read.md +926 -0
  351. package/docs/recipes/kickassembler/logic-rate-decoupling.md +670 -0
  352. package/docs/recipes/kickassembler/mci-interlace.md +543 -0
  353. package/docs/recipes/kickassembler/memory-layout.md +277 -0
  354. package/docs/recipes/kickassembler/mouse-1351-read.md +801 -0
  355. package/docs/recipes/kickassembler/nmi-timer-tick.md +562 -0
  356. package/docs/recipes/kickassembler/own-keyscan.md +881 -0
  357. package/docs/recipes/kickassembler/paddle-read.md +751 -0
  358. package/docs/recipes/kickassembler/palette-cells.md +219 -0
  359. package/docs/recipes/kickassembler/pseudo-3d-road.md +930 -0
  360. package/docs/recipes/kickassembler/pwm-digi.md +463 -0
  361. package/docs/recipes/kickassembler/raster-bars.md +264 -0
  362. package/docs/recipes/kickassembler/reu-dma.md +1019 -0
  363. package/docs/recipes/kickassembler/runtime-relocation.md +743 -0
  364. package/docs/recipes/kickassembler/screen-dissolve.md +545 -0
  365. package/docs/recipes/kickassembler/screen-wipe.md +408 -0
  366. package/docs/recipes/kickassembler/screenshots/README.md +72 -0
  367. package/docs/recipes/kickassembler/screenshots/basic-float-calls-ntsc.png +0 -0
  368. package/docs/recipes/kickassembler/screenshots/basic-float-calls.png +0 -0
  369. package/docs/recipes/kickassembler/screenshots/basic-wedge-ntsc.png +0 -0
  370. package/docs/recipes/kickassembler/screenshots/basic-wedge.png +0 -0
  371. package/docs/recipes/kickassembler/screenshots/big-font-scroller-ntsc.png +0 -0
  372. package/docs/recipes/kickassembler/screenshots/big-font-scroller.png +0 -0
  373. package/docs/recipes/kickassembler/screenshots/charset-copy-rom-to-ram-ntsc.png +0 -0
  374. package/docs/recipes/kickassembler/screenshots/charset-copy-rom-to-ram.png +0 -0
  375. package/docs/recipes/kickassembler/screenshots/cia-revision-detect-ntsc.png +0 -0
  376. package/docs/recipes/kickassembler/screenshots/cia-revision-detect.png +0 -0
  377. package/docs/recipes/kickassembler/screenshots/colour-cycling-ntsc.png +0 -0
  378. package/docs/recipes/kickassembler/screenshots/colour-cycling.png +0 -0
  379. package/docs/recipes/kickassembler/screenshots/colour-fade-ntsc.png +0 -0
  380. package/docs/recipes/kickassembler/screenshots/colour-fade.png +0 -0
  381. package/docs/recipes/kickassembler/screenshots/compare-16bit-signed-ntsc.png +0 -0
  382. package/docs/recipes/kickassembler/screenshots/compare-16bit-signed.png +0 -0
  383. package/docs/recipes/kickassembler/screenshots/cracktro-template.png +0 -0
  384. package/docs/recipes/kickassembler/screenshots/crt-banked-ntsc.png +0 -0
  385. package/docs/recipes/kickassembler/screenshots/crt-banked.png +0 -0
  386. package/docs/recipes/kickassembler/screenshots/dos-error-codes-ntsc.png +0 -0
  387. package/docs/recipes/kickassembler/screenshots/dos-error-codes.png +0 -0
  388. package/docs/recipes/kickassembler/screenshots/dot-flag-ntsc.png +0 -0
  389. package/docs/recipes/kickassembler/screenshots/dot-flag.png +0 -0
  390. package/docs/recipes/kickassembler/screenshots/drive-job-queue-ntsc.png +0 -0
  391. package/docs/recipes/kickassembler/screenshots/drive-job-queue.png +0 -0
  392. package/docs/recipes/kickassembler/screenshots/drive-via-probe-ntsc.png +0 -0
  393. package/docs/recipes/kickassembler/screenshots/drive-via-probe.png +0 -0
  394. package/docs/recipes/kickassembler/screenshots/dycp-scroller-ntsc.png +0 -0
  395. package/docs/recipes/kickassembler/screenshots/dycp-scroller.png +0 -0
  396. package/docs/recipes/kickassembler/screenshots/dypp-sprite-scroller-ntsc.png +0 -0
  397. package/docs/recipes/kickassembler/screenshots/dypp-sprite-scroller.png +0 -0
  398. package/docs/recipes/kickassembler/screenshots/dysp-ntsc.png +0 -0
  399. package/docs/recipes/kickassembler/screenshots/dysp.png +0 -0
  400. package/docs/recipes/kickassembler/screenshots/easyflash-save-ntsc-run2.png +0 -0
  401. package/docs/recipes/kickassembler/screenshots/easyflash-save-ntsc.png +0 -0
  402. package/docs/recipes/kickassembler/screenshots/easyflash-save-run2.png +0 -0
  403. package/docs/recipes/kickassembler/screenshots/easyflash-save.png +0 -0
  404. package/docs/recipes/kickassembler/screenshots/eight-way-scroll-ntsc.png +0 -0
  405. package/docs/recipes/kickassembler/screenshots/eight-way-scroll.png +0 -0
  406. package/docs/recipes/kickassembler/screenshots/file-io-roundtrip-ntsc.png +0 -0
  407. package/docs/recipes/kickassembler/screenshots/file-io-roundtrip.png +0 -0
  408. package/docs/recipes/kickassembler/screenshots/fire-effect-ntsc.png +0 -0
  409. package/docs/recipes/kickassembler/screenshots/fire-effect.png +0 -0
  410. package/docs/recipes/kickassembler/screenshots/fld-ntsc.png +0 -0
  411. package/docs/recipes/kickassembler/screenshots/fld.png +0 -0
  412. package/docs/recipes/kickassembler/screenshots/fli-image.png +0 -0
  413. package/docs/recipes/kickassembler/screenshots/four-player-read-ntsc.png +0 -0
  414. package/docs/recipes/kickassembler/screenshots/four-player-read.png +0 -0
  415. package/docs/recipes/kickassembler/screenshots/headless-verify-ntsc.png +0 -0
  416. package/docs/recipes/kickassembler/screenshots/headless-verify.png +0 -0
  417. package/docs/recipes/kickassembler/screenshots/hello-world.png +0 -0
  418. package/docs/recipes/kickassembler/screenshots/high-score-insert-ntsc.png +0 -0
  419. package/docs/recipes/kickassembler/screenshots/high-score-insert.png +0 -0
  420. package/docs/recipes/kickassembler/screenshots/hires-plot-line-ntsc.png +0 -0
  421. package/docs/recipes/kickassembler/screenshots/hires-plot-line.png +0 -0
  422. package/docs/recipes/kickassembler/screenshots/irq-chain-ntsc.png +0 -0
  423. package/docs/recipes/kickassembler/screenshots/irq-chain.png +0 -0
  424. package/docs/recipes/kickassembler/screenshots/irq-owns-port-ntsc.png +0 -0
  425. package/docs/recipes/kickassembler/screenshots/irq-owns-port.png +0 -0
  426. package/docs/recipes/kickassembler/screenshots/isometric-room-ntsc.png +0 -0
  427. package/docs/recipes/kickassembler/screenshots/isometric-room.png +0 -0
  428. package/docs/recipes/kickassembler/screenshots/light-pen-read-ntsc.png +0 -0
  429. package/docs/recipes/kickassembler/screenshots/light-pen-read.png +0 -0
  430. package/docs/recipes/kickassembler/screenshots/logic-rate-decoupling-ntsc.png +0 -0
  431. package/docs/recipes/kickassembler/screenshots/logic-rate-decoupling.png +0 -0
  432. package/docs/recipes/kickassembler/screenshots/mci-interlace-average.png +0 -0
  433. package/docs/recipes/kickassembler/screenshots/mci-interlace-frame-b.png +0 -0
  434. package/docs/recipes/kickassembler/screenshots/mci-interlace-ntsc.png +0 -0
  435. package/docs/recipes/kickassembler/screenshots/mci-interlace.png +0 -0
  436. package/docs/recipes/kickassembler/screenshots/memory-layout-ntsc.png +0 -0
  437. package/docs/recipes/kickassembler/screenshots/memory-layout.png +0 -0
  438. package/docs/recipes/kickassembler/screenshots/mouse-1351-read-ntsc.png +0 -0
  439. package/docs/recipes/kickassembler/screenshots/mouse-1351-read.png +0 -0
  440. package/docs/recipes/kickassembler/screenshots/nmi-timer-tick-ntsc.png +0 -0
  441. package/docs/recipes/kickassembler/screenshots/nmi-timer-tick.png +0 -0
  442. package/docs/recipes/kickassembler/screenshots/own-keyscan-ntsc.png +0 -0
  443. package/docs/recipes/kickassembler/screenshots/own-keyscan.png +0 -0
  444. package/docs/recipes/kickassembler/screenshots/paddle-read-ntsc.png +0 -0
  445. package/docs/recipes/kickassembler/screenshots/paddle-read.png +0 -0
  446. package/docs/recipes/kickassembler/screenshots/palette-cells-ntsc.png +0 -0
  447. package/docs/recipes/kickassembler/screenshots/palette-cells.png +0 -0
  448. package/docs/recipes/kickassembler/screenshots/pseudo-3d-road-ntsc.png +0 -0
  449. package/docs/recipes/kickassembler/screenshots/pseudo-3d-road.png +0 -0
  450. package/docs/recipes/kickassembler/screenshots/pwm-digi-ntsc.png +0 -0
  451. package/docs/recipes/kickassembler/screenshots/pwm-digi.png +0 -0
  452. package/docs/recipes/kickassembler/screenshots/raster-bars.png +0 -0
  453. package/docs/recipes/kickassembler/screenshots/reu-dma-ntsc.png +0 -0
  454. package/docs/recipes/kickassembler/screenshots/reu-dma.png +0 -0
  455. package/docs/recipes/kickassembler/screenshots/runtime-relocation-ntsc.png +0 -0
  456. package/docs/recipes/kickassembler/screenshots/runtime-relocation.png +0 -0
  457. package/docs/recipes/kickassembler/screenshots/screen-dissolve-ntsc.png +0 -0
  458. package/docs/recipes/kickassembler/screenshots/screen-dissolve.png +0 -0
  459. package/docs/recipes/kickassembler/screenshots/screen-wipe-ntsc.png +0 -0
  460. package/docs/recipes/kickassembler/screenshots/screen-wipe.png +0 -0
  461. package/docs/recipes/kickassembler/screenshots/scroll-panel-split-ntsc.png +0 -0
  462. package/docs/recipes/kickassembler/screenshots/scroll-panel-split.png +0 -0
  463. package/docs/recipes/kickassembler/screenshots/sfx-in-player-ntsc.png +0 -0
  464. package/docs/recipes/kickassembler/screenshots/sfx-in-player.png +0 -0
  465. package/docs/recipes/kickassembler/screenshots/shadebobs-ntsc.png +0 -0
  466. package/docs/recipes/kickassembler/screenshots/shadebobs.png +0 -0
  467. package/docs/recipes/kickassembler/screenshots/sideborder-open.png +0 -0
  468. package/docs/recipes/kickassembler/screenshots/sine-scroller.png +0 -0
  469. package/docs/recipes/kickassembler/screenshots/sine-table-runtime-ntsc.png +0 -0
  470. package/docs/recipes/kickassembler/screenshots/sine-table-runtime.png +0 -0
  471. package/docs/recipes/kickassembler/screenshots/software-sprite-preshifted-ntsc.png +0 -0
  472. package/docs/recipes/kickassembler/screenshots/software-sprite-preshifted.png +0 -0
  473. package/docs/recipes/kickassembler/screenshots/speedcode-generator-ntsc.png +0 -0
  474. package/docs/recipes/kickassembler/screenshots/speedcode-generator.png +0 -0
  475. package/docs/recipes/kickassembler/screenshots/sprite-border-scroller-ntsc.png +0 -0
  476. package/docs/recipes/kickassembler/screenshots/sprite-border-scroller.png +0 -0
  477. package/docs/recipes/kickassembler/screenshots/sprite-cache-flip-ntsc.png +0 -0
  478. package/docs/recipes/kickassembler/screenshots/sprite-cache-flip.png +0 -0
  479. package/docs/recipes/kickassembler/screenshots/sprite-multiplex-24.png +0 -0
  480. package/docs/recipes/kickassembler/screenshots/sprite-multiplex-game-ntsc.png +0 -0
  481. package/docs/recipes/kickassembler/screenshots/sprite-multiplex-game.png +0 -0
  482. package/docs/recipes/kickassembler/screenshots/sprite-priority-classes-ntsc.png +0 -0
  483. package/docs/recipes/kickassembler/screenshots/sprite-priority-classes.png +0 -0
  484. package/docs/recipes/kickassembler/screenshots/sprite-sine-chain-ntsc.png +0 -0
  485. package/docs/recipes/kickassembler/screenshots/sprite-sine-chain.png +0 -0
  486. package/docs/recipes/kickassembler/screenshots/sprite-stretcher-ntsc.png +0 -0
  487. package/docs/recipes/kickassembler/screenshots/sprite-stretcher.png +0 -0
  488. package/docs/recipes/kickassembler/screenshots/sprites-only-screen-ntsc.png +0 -0
  489. package/docs/recipes/kickassembler/screenshots/sprites-only-screen.png +0 -0
  490. package/docs/recipes/kickassembler/screenshots/sqrt-atan2-ntsc.png +0 -0
  491. package/docs/recipes/kickassembler/screenshots/sqrt-atan2.png +0 -0
  492. package/docs/recipes/kickassembler/screenshots/stable-raster-irq-control.png +0 -0
  493. package/docs/recipes/kickassembler/screenshots/stable-raster-irq.png +0 -0
  494. package/docs/recipes/kickassembler/screenshots/tech-tech-ntsc.png +0 -0
  495. package/docs/recipes/kickassembler/screenshots/tech-tech.png +0 -0
  496. package/docs/recipes/kickassembler/screenshots/tod-alarm-ntsc.png +0 -0
  497. package/docs/recipes/kickassembler/screenshots/tod-alarm.png +0 -0
  498. package/docs/recipes/kickassembler/screenshots/topbottom-border-open.png +0 -0
  499. package/docs/recipes/kickassembler/screenshots/twister-ntsc.png +0 -0
  500. package/docs/recipes/kickassembler/screenshots/twister.png +0 -0
  501. package/docs/recipes/kickassembler/screenshots/unit-test-driver-ntsc.png +0 -0
  502. package/docs/recipes/kickassembler/screenshots/unit-test-driver.png +0 -0
  503. package/docs/recipes/kickassembler/screenshots/vector-balls-ntsc.png +0 -0
  504. package/docs/recipes/kickassembler/screenshots/vector-balls.png +0 -0
  505. package/docs/recipes/kickassembler/screenshots/wireframe-ships-ntsc.png +0 -0
  506. package/docs/recipes/kickassembler/screenshots/wireframe-ships.png +0 -0
  507. package/docs/recipes/kickassembler/scroll-panel-split.md +381 -0
  508. package/docs/recipes/kickassembler/sfx-in-player.md +907 -0
  509. package/docs/recipes/kickassembler/shadebobs.md +522 -0
  510. package/docs/recipes/kickassembler/sideborder-open.md +338 -0
  511. package/docs/recipes/kickassembler/sine-scroller.md +321 -0
  512. package/docs/recipes/kickassembler/sine-table-runtime.md +815 -0
  513. package/docs/recipes/kickassembler/software-sprite-preshifted.md +725 -0
  514. package/docs/recipes/kickassembler/speedcode-generator.md +606 -0
  515. package/docs/recipes/kickassembler/sprite-border-scroller.md +696 -0
  516. package/docs/recipes/kickassembler/sprite-cache-flip.md +1071 -0
  517. package/docs/recipes/kickassembler/sprite-multiplex-24.md +326 -0
  518. package/docs/recipes/kickassembler/sprite-multiplex-game.md +986 -0
  519. package/docs/recipes/kickassembler/sprite-priority-classes.md +463 -0
  520. package/docs/recipes/kickassembler/sprite-sine-chain.md +338 -0
  521. package/docs/recipes/kickassembler/sprite-stretcher.md +501 -0
  522. package/docs/recipes/kickassembler/sprites-only-screen.md +550 -0
  523. package/docs/recipes/kickassembler/sqrt-atan2.md +1014 -0
  524. package/docs/recipes/kickassembler/stable-raster-irq.md +360 -0
  525. package/docs/recipes/kickassembler/tape-turbo-loader.md +875 -0
  526. package/docs/recipes/kickassembler/tech-tech.md +679 -0
  527. package/docs/recipes/kickassembler/tod-alarm.md +754 -0
  528. package/docs/recipes/kickassembler/topbottom-border-open.md +407 -0
  529. package/docs/recipes/kickassembler/twister.md +518 -0
  530. package/docs/recipes/kickassembler/unit-test-driver.md +314 -0
  531. package/docs/recipes/kickassembler/vector-balls.md +545 -0
  532. package/docs/recipes/kickassembler/wireframe-ships.md +2324 -0
  533. package/docs/recipes/oscar64/actor-activation-window.md +807 -0
  534. package/docs/recipes/oscar64/adventure-engine.md +1094 -0
  535. package/docs/recipes/oscar64/attract-replay.md +465 -0
  536. package/docs/recipes/oscar64/beat-em-up-lanes.md +563 -0
  537. package/docs/recipes/oscar64/bfs-distance-map.md +723 -0
  538. package/docs/recipes/oscar64/bitmap-koala-viewer.md +355 -0
  539. package/docs/recipes/oscar64/car-contact.md +950 -0
  540. package/docs/recipes/oscar64/cave-scan.md +609 -0
  541. package/docs/recipes/oscar64/char-bullets.md +477 -0
  542. package/docs/recipes/oscar64/charset-animation.md +383 -0
  543. package/docs/recipes/oscar64/charset-parallax.md +401 -0
  544. package/docs/recipes/oscar64/destructible-terrain.md +1099 -0
  545. package/docs/recipes/oscar64/difficulty-tables.md +654 -0
  546. package/docs/recipes/oscar64/dig-and-guards.md +975 -0
  547. package/docs/recipes/oscar64/directory-reader.md +535 -0
  548. package/docs/recipes/oscar64/divide-check.md +581 -0
  549. package/docs/recipes/oscar64/double-buffer-nomirror.md +205 -0
  550. package/docs/recipes/oscar64/double-buffer.md +255 -0
  551. package/docs/recipes/oscar64/falling-blocks.md +874 -0
  552. package/docs/recipes/oscar64/fixed-point-jump-velocity.md +289 -0
  553. package/docs/recipes/oscar64/fixed-point-jump.md +210 -0
  554. package/docs/recipes/oscar64/flip-screen-rooms.md +793 -0
  555. package/docs/recipes/oscar64/frame-sync-loop-overrun.md +214 -0
  556. package/docs/recipes/oscar64/frame-sync-loop.md +254 -0
  557. package/docs/recipes/oscar64/game-tree-search.md +743 -0
  558. package/docs/recipes/oscar64/ghost-targeting.md +867 -0
  559. package/docs/recipes/oscar64/headless-verify.md +345 -0
  560. package/docs/recipes/oscar64/hello-world.md +83 -0
  561. package/docs/recipes/oscar64/high-score-persist.md +772 -0
  562. package/docs/recipes/oscar64/iffl-kernal-skip.md +421 -0
  563. package/docs/recipes/oscar64/joystick-input.md +358 -0
  564. package/docs/recipes/oscar64/lane-pursuit.md +1028 -0
  565. package/docs/recipes/oscar64/level-rle-decoder.md +620 -0
  566. package/docs/recipes/oscar64/lfsr-random-seed2.md +338 -0
  567. package/docs/recipes/oscar64/lfsr-random.md +403 -0
  568. package/docs/recipes/oscar64/load-asset-runtime.md +492 -0
  569. package/docs/recipes/oscar64/memory-layout.md +232 -0
  570. package/docs/recipes/oscar64/mixed-fighters.md +781 -0
  571. package/docs/recipes/oscar64/multi-sprite-object.md +533 -0
  572. package/docs/recipes/oscar64/nav-area-pathfinding.md +881 -0
  573. package/docs/recipes/oscar64/object-pool.md +572 -0
  574. package/docs/recipes/oscar64/pal-ntsc-detect.md +257 -0
  575. package/docs/recipes/oscar64/password-state.md +532 -0
  576. package/docs/recipes/oscar64/per-frame-hitbox.md +651 -0
  577. package/docs/recipes/oscar64/petscii-screen-codes.md +428 -0
  578. package/docs/recipes/oscar64/platformer-scaffold.md +1467 -0
  579. package/docs/recipes/oscar64/print-number.md +367 -0
  580. package/docs/recipes/oscar64/raster-bars.md +239 -0
  581. package/docs/recipes/oscar64/raster-profile-bars.md +404 -0
  582. package/docs/recipes/oscar64/rel-side-sectors.md +391 -0
  583. package/docs/recipes/oscar64/relative-file-records.md +378 -0
  584. package/docs/recipes/oscar64/save-load-seq-file.md +365 -0
  585. package/docs/recipes/oscar64/screenshots/README.md +99 -0
  586. package/docs/recipes/oscar64/screenshots/actor-activation-window-ntsc.png +0 -0
  587. package/docs/recipes/oscar64/screenshots/actor-activation-window.png +0 -0
  588. package/docs/recipes/oscar64/screenshots/adventure-engine-ntsc.png +0 -0
  589. package/docs/recipes/oscar64/screenshots/adventure-engine.png +0 -0
  590. package/docs/recipes/oscar64/screenshots/attract-replay-ntsc.png +0 -0
  591. package/docs/recipes/oscar64/screenshots/attract-replay.png +0 -0
  592. package/docs/recipes/oscar64/screenshots/beat-em-up-lanes-ntsc.png +0 -0
  593. package/docs/recipes/oscar64/screenshots/beat-em-up-lanes.png +0 -0
  594. package/docs/recipes/oscar64/screenshots/bfs-distance-map-ntsc.png +0 -0
  595. package/docs/recipes/oscar64/screenshots/bfs-distance-map.png +0 -0
  596. package/docs/recipes/oscar64/screenshots/bitmap-koala-viewer.png +0 -0
  597. package/docs/recipes/oscar64/screenshots/car-contact-ntsc.png +0 -0
  598. package/docs/recipes/oscar64/screenshots/car-contact.png +0 -0
  599. package/docs/recipes/oscar64/screenshots/cave-scan-ntsc.png +0 -0
  600. package/docs/recipes/oscar64/screenshots/cave-scan.png +0 -0
  601. package/docs/recipes/oscar64/screenshots/char-bullets-ntsc.png +0 -0
  602. package/docs/recipes/oscar64/screenshots/char-bullets.png +0 -0
  603. package/docs/recipes/oscar64/screenshots/charset-animation-ntsc.png +0 -0
  604. package/docs/recipes/oscar64/screenshots/charset-animation.png +0 -0
  605. package/docs/recipes/oscar64/screenshots/charset-parallax-ntsc.png +0 -0
  606. package/docs/recipes/oscar64/screenshots/charset-parallax.png +0 -0
  607. package/docs/recipes/oscar64/screenshots/destructible-terrain-ntsc.png +0 -0
  608. package/docs/recipes/oscar64/screenshots/destructible-terrain.png +0 -0
  609. package/docs/recipes/oscar64/screenshots/difficulty-tables-ntsc.png +0 -0
  610. package/docs/recipes/oscar64/screenshots/difficulty-tables.png +0 -0
  611. package/docs/recipes/oscar64/screenshots/dig-and-guards-ntsc.png +0 -0
  612. package/docs/recipes/oscar64/screenshots/dig-and-guards.png +0 -0
  613. package/docs/recipes/oscar64/screenshots/directory-reader-ntsc.png +0 -0
  614. package/docs/recipes/oscar64/screenshots/directory-reader.png +0 -0
  615. package/docs/recipes/oscar64/screenshots/divide-check-ntsc.png +0 -0
  616. package/docs/recipes/oscar64/screenshots/divide-check.png +0 -0
  617. package/docs/recipes/oscar64/screenshots/double-buffer-nomirror.png +0 -0
  618. package/docs/recipes/oscar64/screenshots/double-buffer-ntsc.png +0 -0
  619. package/docs/recipes/oscar64/screenshots/double-buffer.png +0 -0
  620. package/docs/recipes/oscar64/screenshots/falling-blocks-ntsc.png +0 -0
  621. package/docs/recipes/oscar64/screenshots/falling-blocks.png +0 -0
  622. package/docs/recipes/oscar64/screenshots/fixed-point-jump-ntsc.png +0 -0
  623. package/docs/recipes/oscar64/screenshots/fixed-point-jump-velocity-ntsc.png +0 -0
  624. package/docs/recipes/oscar64/screenshots/fixed-point-jump-velocity.png +0 -0
  625. package/docs/recipes/oscar64/screenshots/fixed-point-jump.png +0 -0
  626. package/docs/recipes/oscar64/screenshots/flip-screen-rooms-ntsc.png +0 -0
  627. package/docs/recipes/oscar64/screenshots/flip-screen-rooms.png +0 -0
  628. package/docs/recipes/oscar64/screenshots/frame-sync-loop-ntsc.png +0 -0
  629. package/docs/recipes/oscar64/screenshots/frame-sync-loop-overrun.png +0 -0
  630. package/docs/recipes/oscar64/screenshots/frame-sync-loop.png +0 -0
  631. package/docs/recipes/oscar64/screenshots/game-tree-search-ntsc.png +0 -0
  632. package/docs/recipes/oscar64/screenshots/game-tree-search.png +0 -0
  633. package/docs/recipes/oscar64/screenshots/ghost-targeting-ntsc.png +0 -0
  634. package/docs/recipes/oscar64/screenshots/ghost-targeting.png +0 -0
  635. package/docs/recipes/oscar64/screenshots/headless-verify-ntsc.png +0 -0
  636. package/docs/recipes/oscar64/screenshots/headless-verify.png +0 -0
  637. package/docs/recipes/oscar64/screenshots/hello-world.png +0 -0
  638. package/docs/recipes/oscar64/screenshots/high-score-persist-ntsc.png +0 -0
  639. package/docs/recipes/oscar64/screenshots/high-score-persist.png +0 -0
  640. package/docs/recipes/oscar64/screenshots/iffl-kernal-skip-ntsc.png +0 -0
  641. package/docs/recipes/oscar64/screenshots/iffl-kernal-skip.png +0 -0
  642. package/docs/recipes/oscar64/screenshots/joystick-input-ntsc.png +0 -0
  643. package/docs/recipes/oscar64/screenshots/joystick-input.png +0 -0
  644. package/docs/recipes/oscar64/screenshots/lane-pursuit-ntsc.png +0 -0
  645. package/docs/recipes/oscar64/screenshots/lane-pursuit.png +0 -0
  646. package/docs/recipes/oscar64/screenshots/level-rle-decoder-ntsc.png +0 -0
  647. package/docs/recipes/oscar64/screenshots/level-rle-decoder.png +0 -0
  648. package/docs/recipes/oscar64/screenshots/lfsr-random-ntsc.png +0 -0
  649. package/docs/recipes/oscar64/screenshots/lfsr-random-seed2.png +0 -0
  650. package/docs/recipes/oscar64/screenshots/lfsr-random.png +0 -0
  651. package/docs/recipes/oscar64/screenshots/load-asset-runtime-ntsc.png +0 -0
  652. package/docs/recipes/oscar64/screenshots/load-asset-runtime.png +0 -0
  653. package/docs/recipes/oscar64/screenshots/memory-layout-ntsc.png +0 -0
  654. package/docs/recipes/oscar64/screenshots/memory-layout.png +0 -0
  655. package/docs/recipes/oscar64/screenshots/mixed-fighters-ntsc.png +0 -0
  656. package/docs/recipes/oscar64/screenshots/mixed-fighters.png +0 -0
  657. package/docs/recipes/oscar64/screenshots/multi-sprite-object-ntsc.png +0 -0
  658. package/docs/recipes/oscar64/screenshots/multi-sprite-object.png +0 -0
  659. package/docs/recipes/oscar64/screenshots/nav-area-pathfinding-ntsc.png +0 -0
  660. package/docs/recipes/oscar64/screenshots/nav-area-pathfinding.png +0 -0
  661. package/docs/recipes/oscar64/screenshots/object-pool-ntsc.png +0 -0
  662. package/docs/recipes/oscar64/screenshots/object-pool.png +0 -0
  663. package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-ntsc.png +0 -0
  664. package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-oldntsc.png +0 -0
  665. package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-pal.png +0 -0
  666. package/docs/recipes/oscar64/screenshots/password-state-ntsc.png +0 -0
  667. package/docs/recipes/oscar64/screenshots/password-state.png +0 -0
  668. package/docs/recipes/oscar64/screenshots/per-frame-hitbox-ntsc.png +0 -0
  669. package/docs/recipes/oscar64/screenshots/per-frame-hitbox.png +0 -0
  670. package/docs/recipes/oscar64/screenshots/petscii-screen-codes-ntsc.png +0 -0
  671. package/docs/recipes/oscar64/screenshots/petscii-screen-codes.png +0 -0
  672. package/docs/recipes/oscar64/screenshots/platformer-scaffold-d64-ntsc.png +0 -0
  673. package/docs/recipes/oscar64/screenshots/platformer-scaffold-d64.png +0 -0
  674. package/docs/recipes/oscar64/screenshots/platformer-scaffold-ntsc.png +0 -0
  675. package/docs/recipes/oscar64/screenshots/platformer-scaffold.png +0 -0
  676. package/docs/recipes/oscar64/screenshots/print-number-ntsc.png +0 -0
  677. package/docs/recipes/oscar64/screenshots/print-number.png +0 -0
  678. package/docs/recipes/oscar64/screenshots/raster-bars.png +0 -0
  679. package/docs/recipes/oscar64/screenshots/raster-profile-bars-ntsc.png +0 -0
  680. package/docs/recipes/oscar64/screenshots/raster-profile-bars.png +0 -0
  681. package/docs/recipes/oscar64/screenshots/rel-side-sectors-ntsc.png +0 -0
  682. package/docs/recipes/oscar64/screenshots/rel-side-sectors.png +0 -0
  683. package/docs/recipes/oscar64/screenshots/relative-file-records-ntsc.png +0 -0
  684. package/docs/recipes/oscar64/screenshots/relative-file-records.png +0 -0
  685. package/docs/recipes/oscar64/screenshots/save-load-seq-file-ntsc.png +0 -0
  686. package/docs/recipes/oscar64/screenshots/save-load-seq-file.png +0 -0
  687. package/docs/recipes/oscar64/screenshots/seeded-level-fill-ntsc.png +0 -0
  688. package/docs/recipes/oscar64/screenshots/seeded-level-fill.png +0 -0
  689. package/docs/recipes/oscar64/screenshots/sfx-engine-ntsc.png +0 -0
  690. package/docs/recipes/oscar64/screenshots/sfx-engine.png +0 -0
  691. package/docs/recipes/oscar64/screenshots/sid-music-player.png +0 -0
  692. package/docs/recipes/oscar64/screenshots/simple-shmup.png +0 -0
  693. package/docs/recipes/oscar64/screenshots/slope-collision-ntsc.png +0 -0
  694. package/docs/recipes/oscar64/screenshots/slope-collision.png +0 -0
  695. package/docs/recipes/oscar64/screenshots/soft-scroll-h.png +0 -0
  696. package/docs/recipes/oscar64/screenshots/sprite-animation-table-ntsc.png +0 -0
  697. package/docs/recipes/oscar64/screenshots/sprite-animation-table.png +0 -0
  698. package/docs/recipes/oscar64/screenshots/sprite-multiplex-8.png +0 -0
  699. package/docs/recipes/oscar64/screenshots/stable-raster-irq.png +0 -0
  700. package/docs/recipes/oscar64/screenshots/text-input-ntsc.png +0 -0
  701. package/docs/recipes/oscar64/screenshots/text-input.png +0 -0
  702. package/docs/recipes/oscar64/screenshots/text-overlay-playfield-gameover.png +0 -0
  703. package/docs/recipes/oscar64/screenshots/text-overlay-playfield-notrail-control.png +0 -0
  704. package/docs/recipes/oscar64/screenshots/text-overlay-playfield-ntsc.png +0 -0
  705. package/docs/recipes/oscar64/screenshots/text-overlay-playfield-ungated-control.png +0 -0
  706. package/docs/recipes/oscar64/screenshots/text-overlay-playfield.png +0 -0
  707. package/docs/recipes/oscar64/screenshots/text-window-menu-ntsc.png +0 -0
  708. package/docs/recipes/oscar64/screenshots/text-window-menu.png +0 -0
  709. package/docs/recipes/oscar64/screenshots/tile-grid-collision-ntsc.png +0 -0
  710. package/docs/recipes/oscar64/screenshots/tile-grid-collision.png +0 -0
  711. package/docs/recipes/oscar64/screenshots/tile-map-render-ntsc.png +0 -0
  712. package/docs/recipes/oscar64/screenshots/tile-map-render.png +0 -0
  713. package/docs/recipes/oscar64/screenshots/two-player-ntsc.png +0 -0
  714. package/docs/recipes/oscar64/screenshots/two-player.png +0 -0
  715. package/docs/recipes/oscar64/screenshots/two-word-parser-ntsc.png +0 -0
  716. package/docs/recipes/oscar64/screenshots/two-word-parser.png +0 -0
  717. package/docs/recipes/oscar64/screenshots/vehicle-control-ntsc.png +0 -0
  718. package/docs/recipes/oscar64/screenshots/vehicle-control.png +0 -0
  719. package/docs/recipes/oscar64/screenshots/wave-director-ntsc.png +0 -0
  720. package/docs/recipes/oscar64/screenshots/wave-director.png +0 -0
  721. package/docs/recipes/oscar64/seeded-level-fill.md +364 -0
  722. package/docs/recipes/oscar64/sfx-engine.md +514 -0
  723. package/docs/recipes/oscar64/sid-music-player.md +441 -0
  724. package/docs/recipes/oscar64/simple-shmup.md +1000 -0
  725. package/docs/recipes/oscar64/slope-collision.md +610 -0
  726. package/docs/recipes/oscar64/soft-scroll-h.md +261 -0
  727. package/docs/recipes/oscar64/sprite-animation-table.md +671 -0
  728. package/docs/recipes/oscar64/sprite-multiplex-8.md +314 -0
  729. package/docs/recipes/oscar64/stable-raster-irq.md +236 -0
  730. package/docs/recipes/oscar64/text-input.md +274 -0
  731. package/docs/recipes/oscar64/text-overlay-playfield.md +617 -0
  732. package/docs/recipes/oscar64/text-window-menu.md +551 -0
  733. package/docs/recipes/oscar64/tile-grid-collision.md +563 -0
  734. package/docs/recipes/oscar64/tile-map-render.md +395 -0
  735. package/docs/recipes/oscar64/two-player.md +528 -0
  736. package/docs/recipes/oscar64/two-word-parser.md +510 -0
  737. package/docs/recipes/oscar64/vehicle-control.md +771 -0
  738. package/docs/recipes/oscar64/wave-director.md +787 -0
  739. package/docs/recipes/runs.json +1606 -0
  740. package/docs/runtime/sim6502-reference.md +466 -0
  741. package/docs/runtime/vice-mcp-reference.md +351 -0
  742. package/docs/runtime/vice-reference.md +1393 -0
  743. package/docs/techniques/bitmap-modes.md +685 -0
  744. package/docs/techniques/cpu-cycle-tricks.md +1570 -0
  745. package/docs/techniques/effects-vector-3d.md +1431 -0
  746. package/docs/techniques/file-io.md +1427 -0
  747. package/docs/techniques/input.md +1072 -0
  748. package/docs/techniques/loaders-packers.md +768 -0
  749. package/docs/techniques/logic.md +3091 -0
  750. package/docs/techniques/maths.md +1524 -0
  751. package/docs/techniques/memory-banking.md +1686 -0
  752. package/docs/techniques/music-sid.md +1144 -0
  753. package/docs/techniques/raster.md +1543 -0
  754. package/docs/techniques/scroll.md +1292 -0
  755. package/docs/techniques/sprite.md +2566 -0
  756. package/docs/techniques/text-mode-render.md +717 -0
  757. package/docs/techniques/text.md +1095 -0
  758. package/docs/techniques/transitions.md +405 -0
  759. package/docs/toolchains/cartconv-reference.md +262 -0
  760. package/docs/toolchains/cc1541-reference.md +316 -0
  761. package/docs/toolchains/cc65-reference.md +667 -0
  762. package/docs/toolchains/error-sources/cc65/README.md +1 -0
  763. package/docs/toolchains/error-sources/cc65/missing-setcpu.s +4 -0
  764. package/docs/toolchains/error-sources/cc65/range-error.s +4 -0
  765. package/docs/toolchains/error-sources/cc65/segment-overflow.c +6 -0
  766. package/docs/toolchains/error-sources/cc65/unresolved-external.c +6 -0
  767. package/docs/toolchains/error-sources/kickassembler/README.md +1 -0
  768. package/docs/toolchains/error-sources/kickassembler/branch-out-of-range.asm +4 -0
  769. package/docs/toolchains/error-sources/kickassembler/failed-assert.asm +3 -0
  770. package/docs/toolchains/error-sources/kickassembler/flat-lib.asm +4 -0
  771. package/docs/toolchains/error-sources/kickassembler/import-source-no-namespace.asm +5 -0
  772. package/docs/toolchains/error-sources/kickassembler/macro-without-parentheses.asm +6 -0
  773. package/docs/toolchains/error-sources/kickassembler/memory-block-overlap.asm +4 -0
  774. package/docs/toolchains/error-sources/kickassembler/symbol-clash-lib.asm +5 -0
  775. package/docs/toolchains/error-sources/kickassembler/symbol-clash-main.asm +6 -0
  776. package/docs/toolchains/error-sources/kickassembler/unknown-symbol.asm +4 -0
  777. package/docs/toolchains/error-sources/oscar64/README.md +1 -0
  778. package/docs/toolchains/error-sources/oscar64/asm-addressing-mode.c +7 -0
  779. package/docs/toolchains/error-sources/oscar64/region-overflow.c +6 -0
  780. package/docs/toolchains/error-sources/oscar64/undefined-extern-var.c +5 -0
  781. package/docs/toolchains/error-sources/oscar64/undefined-extern.c +6 -0
  782. package/docs/toolchains/error-sources/oscar64/unknown-vic-field.c +6 -0
  783. package/docs/toolchains/error-sources/oscar64/void-main.c +3 -0
  784. package/docs/toolchains/kickassembler-reference.md +1020 -0
  785. package/docs/toolchains/memory-layout-planning.md +362 -0
  786. package/docs/toolchains/oscar64-headers-reference.md +583 -0
  787. package/docs/toolchains/oscar64-reference.md +892 -0
  788. package/docs/toolchains/petcat-reference.md +290 -0
  789. package/docs/toolchains/png2prg.md +188 -0
  790. package/docs/toolchains/release-disk.md +314 -0
  791. package/docs/toolchains/sidreloc.md +236 -0
  792. package/docs/toolchains/spindle-reference.md +487 -0
  793. package/docs/toolchains/tape-mastering.md +592 -0
  794. package/docs/toolchains/unit-testing-6502.md +281 -0
  795. package/docs/workflow/agent-harness.md +478 -0
  796. package/package.json +105 -0
  797. package/templates/_harness/CLAUDE.md.template +91 -0
  798. package/templates/_harness/PLAN.md.template +72 -0
  799. package/templates/_harness/check.py +442 -0
  800. package/templates/_harness/claude/settings.json +17 -0
  801. package/templates/_harness/gen-asm-header.py +96 -0
  802. package/templates/_harness/harness.mk +273 -0
  803. package/templates/_harness/hooks/plan-gate.py +185 -0
  804. package/templates/_harness/meter/frame_meter.asm +311 -0
  805. package/templates/_harness/meter/frame_meter.c +155 -0
  806. package/templates/_harness/meter/frame_meter.h +80 -0
  807. package/templates/_harness/zp-used.py +104 -0
  808. package/templates/action-puzzle/.claude/settings.json +17 -0
  809. package/templates/action-puzzle/.mcp.json +10 -0
  810. package/templates/action-puzzle/CLAUDE.md +120 -0
  811. package/templates/action-puzzle/Makefile +125 -0
  812. package/templates/action-puzzle/PLAN.md +388 -0
  813. package/templates/action-puzzle/README.md +240 -0
  814. package/templates/action-puzzle/expect.json +25 -0
  815. package/templates/action-puzzle/src/cave.c +280 -0
  816. package/templates/action-puzzle/src/cave.h +80 -0
  817. package/templates/action-puzzle/src/gen_autopilot.h +20 -0
  818. package/templates/action-puzzle/src/gen_caves.h +59 -0
  819. package/templates/action-puzzle/src/gen_notes.h +16 -0
  820. package/templates/action-puzzle/src/hiscore.c +152 -0
  821. package/templates/action-puzzle/src/hiscore.h +41 -0
  822. package/templates/action-puzzle/src/level.c +64 -0
  823. package/templates/action-puzzle/src/level.h +24 -0
  824. package/templates/action-puzzle/src/main.c +599 -0
  825. package/templates/action-puzzle/src/render.c +239 -0
  826. package/templates/action-puzzle/src/render.h +31 -0
  827. package/templates/action-puzzle/src/sound.c +158 -0
  828. package/templates/action-puzzle/src/sound.h +22 -0
  829. package/templates/action-puzzle/tools/disk_check.py +77 -0
  830. package/templates/action-puzzle/tools/drive.py +106 -0
  831. package/templates/action-puzzle/tools/gen.py +454 -0
  832. package/templates/action-puzzle/tools/model_check.py +49 -0
  833. package/templates/action-puzzle/tools/verdict_codes.py +24 -0
  834. package/templates/adventure/.claude/settings.json +17 -0
  835. package/templates/adventure/.mcp.json +10 -0
  836. package/templates/adventure/CLAUDE.md +118 -0
  837. package/templates/adventure/Makefile +113 -0
  838. package/templates/adventure/PLAN.md +396 -0
  839. package/templates/adventure/README.md +277 -0
  840. package/templates/adventure/expect.json +25 -0
  841. package/templates/adventure/src/engine.c +543 -0
  842. package/templates/adventure/src/engine.h +48 -0
  843. package/templates/adventure/src/gen_script.h +141 -0
  844. package/templates/adventure/src/gen_world.h +511 -0
  845. package/templates/adventure/src/main.c +439 -0
  846. package/templates/adventure/src/picture.c +83 -0
  847. package/templates/adventure/src/picture.h +16 -0
  848. package/templates/adventure/src/save.c +159 -0
  849. package/templates/adventure/src/save.h +25 -0
  850. package/templates/adventure/src/sound.c +71 -0
  851. package/templates/adventure/src/sound.h +16 -0
  852. package/templates/adventure/src/text.c +349 -0
  853. package/templates/adventure/src/text.h +40 -0
  854. package/templates/adventure/tools/disk_check.py +128 -0
  855. package/templates/adventure/tools/drive.py +119 -0
  856. package/templates/adventure/tools/gen.py +1033 -0
  857. package/templates/adventure/tools/world.py +356 -0
  858. package/templates/beat-em-up/.claude/settings.json +17 -0
  859. package/templates/beat-em-up/.mcp.json +10 -0
  860. package/templates/beat-em-up/CLAUDE.md +136 -0
  861. package/templates/beat-em-up/Makefile +96 -0
  862. package/templates/beat-em-up/PLAN.md +502 -0
  863. package/templates/beat-em-up/README.md +315 -0
  864. package/templates/beat-em-up/expect-gameover.json +8 -0
  865. package/templates/beat-em-up/expect.json +29 -0
  866. package/templates/beat-em-up/src/anim.c +70 -0
  867. package/templates/beat-em-up/src/art.c +344 -0
  868. package/templates/beat-em-up/src/autopilot.h +154 -0
  869. package/templates/beat-em-up/src/brute.c +273 -0
  870. package/templates/beat-em-up/src/enemy.c +281 -0
  871. package/templates/beat-em-up/src/engine.asm +512 -0
  872. package/templates/beat-em-up/src/fighter.c +384 -0
  873. package/templates/beat-em-up/src/game.h +269 -0
  874. package/templates/beat-em-up/src/hud.c +135 -0
  875. package/templates/beat-em-up/src/main.c +341 -0
  876. package/templates/beat-em-up/src/sound.c +44 -0
  877. package/templates/beat-em-up/src/street.c +93 -0
  878. package/templates/beat-em-up/src/verdict.h +523 -0
  879. package/templates/beat-em-up/src/view.c +362 -0
  880. package/templates/beat-em-up/tools/drive.py +105 -0
  881. package/templates/beat-em-up/tools/flickercheck.py +273 -0
  882. package/templates/demo/.claude/settings.json +17 -0
  883. package/templates/demo/.mcp.json +10 -0
  884. package/templates/demo/CLAUDE.md +129 -0
  885. package/templates/demo/Makefile +66 -0
  886. package/templates/demo/PLAN.md +401 -0
  887. package/templates/demo/README.md +322 -0
  888. package/templates/demo/expect.json +945 -0
  889. package/templates/demo/src/config.asm +80 -0
  890. package/templates/demo/src/framework.asm +390 -0
  891. package/templates/demo/src/main.asm +139 -0
  892. package/templates/demo/src/music.asm +197 -0
  893. package/templates/demo/src/part_main.asm +279 -0
  894. package/templates/demo/src/part_title.asm +79 -0
  895. package/templates/demo/src/parts.asm +43 -0
  896. package/templates/demo/src/tables.asm +73 -0
  897. package/templates/demo/src/verdict.asm +179 -0
  898. package/templates/demo/tools/audio.py +80 -0
  899. package/templates/demo/tools/gen_expect.py +167 -0
  900. package/templates/demo/tools/probe.py +95 -0
  901. package/templates/hello/.claude/settings.json +17 -0
  902. package/templates/hello/.mcp.json +10 -0
  903. package/templates/hello/CLAUDE.md +100 -0
  904. package/templates/hello/Makefile +38 -0
  905. package/templates/hello/PLAN.md +200 -0
  906. package/templates/hello/expect.json +20 -0
  907. package/templates/hello/src/main.c +161 -0
  908. package/templates/hello/src/sprite.asm +27 -0
  909. package/templates/hello-kick/.claude/settings.json +17 -0
  910. package/templates/hello-kick/.mcp.json +10 -0
  911. package/templates/hello-kick/CLAUDE.md +100 -0
  912. package/templates/hello-kick/Makefile +17 -0
  913. package/templates/hello-kick/PLAN.md +199 -0
  914. package/templates/hello-kick/expect.json +20 -0
  915. package/templates/hello-kick/src/main.asm +233 -0
  916. package/templates/platformer/.claude/settings.json +17 -0
  917. package/templates/platformer/.mcp.json +10 -0
  918. package/templates/platformer/CLAUDE.md +125 -0
  919. package/templates/platformer/Makefile +86 -0
  920. package/templates/platformer/PLAN.md +360 -0
  921. package/templates/platformer/README.md +277 -0
  922. package/templates/platformer/expect.json +32 -0
  923. package/templates/platformer/src/actors.c +289 -0
  924. package/templates/platformer/src/anim.c +51 -0
  925. package/templates/platformer/src/art.c +212 -0
  926. package/templates/platformer/src/autopilot.h +30 -0
  927. package/templates/platformer/src/engine.asm +253 -0
  928. package/templates/platformer/src/game.h +248 -0
  929. package/templates/platformer/src/hud.c +90 -0
  930. package/templates/platformer/src/level.c +184 -0
  931. package/templates/platformer/src/main.c +306 -0
  932. package/templates/platformer/src/player.c +268 -0
  933. package/templates/platformer/src/sound.c +42 -0
  934. package/templates/platformer/src/verdict.h +206 -0
  935. package/templates/platformer/src/view.c +331 -0
  936. package/templates/platformer/stage-expect.json +6 -0
  937. package/templates/platformer/tools/drive.py +112 -0
  938. package/templates/platformer/tools/tearcheck.py +229 -0
  939. package/templates/shmup-vertical/.claude/settings.json +17 -0
  940. package/templates/shmup-vertical/.mcp.json +10 -0
  941. package/templates/shmup-vertical/CLAUDE.md +137 -0
  942. package/templates/shmup-vertical/Makefile +131 -0
  943. package/templates/shmup-vertical/PLAN.md +569 -0
  944. package/templates/shmup-vertical/README.md +189 -0
  945. package/templates/shmup-vertical/expect.json +38 -0
  946. package/templates/shmup-vertical/src/bullets.c +158 -0
  947. package/templates/shmup-vertical/src/bullets.h +35 -0
  948. package/templates/shmup-vertical/src/display.c +239 -0
  949. package/templates/shmup-vertical/src/display.h +40 -0
  950. package/templates/shmup-vertical/src/game.h +85 -0
  951. package/templates/shmup-vertical/src/glyph.asm +358 -0
  952. package/templates/shmup-vertical/src/hiscore.c +141 -0
  953. package/templates/shmup-vertical/src/hiscore.h +16 -0
  954. package/templates/shmup-vertical/src/hit.asm +180 -0
  955. package/templates/shmup-vertical/src/hitbox.c +59 -0
  956. package/templates/shmup-vertical/src/hitbox.h +25 -0
  957. package/templates/shmup-vertical/src/kernel.asm +330 -0
  958. package/templates/shmup-vertical/src/level.c +174 -0
  959. package/templates/shmup-vertical/src/level.h +27 -0
  960. package/templates/shmup-vertical/src/main.c +824 -0
  961. package/templates/shmup-vertical/src/mux.asm +321 -0
  962. package/templates/shmup-vertical/src/sound.asm +255 -0
  963. package/templates/shmup-vertical/src/step.asm +169 -0
  964. package/templates/shmup-vertical/src/waves.c +195 -0
  965. package/templates/shmup-vertical/src/waves.h +33 -0
  966. package/templates/shmup-vertical/stage-expect.json +6 -0
  967. package/templates/shmup-vertical/tools/drive.py +152 -0
  968. package/templates/shmup-vertical/tools/joytest.py +159 -0
  969. package/templates/shmup-vertical/tools/meter.py +15 -0
  970. 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.