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,1033 @@
1
+ #!/usr/bin/env python3
2
+ """gen.py: pack tools/world.py into src/gen_world.h and src/gen_script.h.
3
+
4
+ python3 tools/gen.py write both headers, print the packing figures
5
+ python3 tools/gen.py --print also print the model's transcript of the full script
6
+
7
+ The text: every string becomes a message. Messages are packed as bytes:
8
+ $01-$3F a screen code, $40-$FF a pair of codes (byte-pair packing, below),
9
+ $00 the end.
10
+ The engine: a Python copy of src/engine.c and src/text.c (parser, action
11
+ table, built-in verbs, the output tokens, the line printer). It plays the
12
+ autopilot script and writes what the game must end with, the fold of every
13
+ printed line and the number of metered frames, per build variant.
14
+ """
15
+ import math
16
+ import os
17
+ import re
18
+ import sys
19
+ import zlib
20
+
21
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
22
+ import world as W # noqa: E402
23
+
24
+ ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
25
+ WL = 4 # letters a word is matched on
26
+ CARRIED = 0xFF
27
+ WIDTH = 40 # the text window's columns
28
+ KEYS_PER_FRAME = 10 # the KERNAL keyboard queue's size ($0289)
29
+ TOK_NL, TOK_NUM, TOK_CHR, TOK_STYLE = 0xF0, 0xF1, 0xF2, 0xF3
30
+ STYLE_TEXT, STYLE_ECHO = 0x00, 0x80 # the typed command prints in reverse video
31
+
32
+ # ---- ids -----------------------------------------------------------------------
33
+ ROOM_KEYS = list(W.ROOMS)
34
+ ROOM_ID = {k: i + 1 for i, k in enumerate(ROOM_KEYS)}
35
+ ITEM_KEYS = list(W.ITEMS)
36
+ ITEM_ID = {k: i + 1 for i, k in enumerate(ITEM_KEYS)}
37
+ NITEM = len(ITEM_KEYS) + 1
38
+ VERB_ID = {k: i + 1 for i, k in enumerate(W.VERBS)}
39
+ NOUN_KEYS = list(W.NOUNS)
40
+ assert NOUN_KEYS[:6] == W.DIRECTIONS
41
+ NOUN_ID = {k: i + 1 for i, k in enumerate(NOUN_KEYS)}
42
+ FLAG_BIT = {k: i for i, k in enumerate(W.FLAGS)}
43
+ PIC_KEYS = list(W.PICTURES)
44
+ PIC_ID = {k: i for i, k in enumerate(PIC_KEYS)}
45
+
46
+ COND = {"AT": 1, "CARRIED": 2, "HERE": 3, "REACH": 4, "FLAG": 5, "NOTFLAG": 6, "DARK": 7}
47
+ CMD = {"MSG": 1, "GOTO": 2, "LOOK": 3, "SETF": 4, "SWAP": 5, "CARRY": 6, "DESTROY": 7,
48
+ "SCORE": 8, "WIN": 9}
49
+
50
+ # ---- messages -----------------------------------------------------------------------
51
+ MSGS = [""] # id 0 unused
52
+ MSG_ID = {}
53
+
54
+
55
+ def msg(text):
56
+ if text not in MSG_ID:
57
+ MSG_ID[text] = len(MSGS)
58
+ MSGS.append(text)
59
+ return MSG_ID[text]
60
+
61
+
62
+ ENGINE_MSG = {k: msg(v) for k, v in W.MESSAGES.items()}
63
+ for k, v in [("COMMA", ", "), ("DOT", "."), ("COLON", ": "),
64
+ ("CLOSEP", ")."), ("SCORELBL", "SCORE"), ("TURNSLBL", "TURNS")]:
65
+ ENGINE_MSG[k] = msg(v)
66
+ for d in W.DIRECTIONS:
67
+ ENGINE_MSG["DIR" + d] = msg(" " + d)
68
+ ROOM_NAME = {k: msg(v[0]) for k, v in W.ROOMS.items()}
69
+ ROOM_DESC = {k: msg(v[1]) for k, v in W.ROOMS.items()}
70
+ ITEM_DESC = {k: msg(v[0]) for k, v in W.ITEMS.items()}
71
+ ITEM_EXAM = {k: (msg(v[4]) if v[4] else 0) for k, v in W.ITEMS.items()}
72
+ ITEM_INSIDE = {k: msg(W.INSIDE[k]) if k in W.INSIDE else 0 for k in W.ITEMS}
73
+
74
+
75
+ def compile_actions():
76
+ rows = []
77
+ for verb, noun, conds, cmds in W.ACTIONS:
78
+ assert len(conds) <= 4 and len(cmds) <= 4, (verb, noun)
79
+ row = [VERB_ID[verb] if verb else 0, NOUN_ID[noun] if noun else 0]
80
+ for c in conds + [None] * (4 - len(conds)):
81
+ if c is None:
82
+ row += [0, 0]
83
+ continue
84
+ op, _, arg = c.partition(" ")
85
+ row += [COND[op], cond_arg(op, arg)]
86
+ for c in cmds + [None] * (4 - len(cmds)):
87
+ if c is None:
88
+ row += [0, 0]
89
+ continue
90
+ op, _, arg = c.partition(" ")
91
+ row += [CMD[op], cmd_arg(op, arg)]
92
+ rows.append(row)
93
+ return rows
94
+
95
+
96
+ def cond_arg(op, arg):
97
+ if op == "AT":
98
+ return ROOM_ID[arg]
99
+ if op in ("CARRIED", "HERE", "REACH"):
100
+ return ITEM_ID[arg]
101
+ if op in ("FLAG", "NOTFLAG"):
102
+ return FLAG_BIT[arg]
103
+ return 0
104
+
105
+
106
+ def cmd_arg(op, arg):
107
+ if op == "MSG":
108
+ return msg(arg)
109
+ if op == "GOTO":
110
+ return ROOM_ID[arg]
111
+ if op in ("SWAP", "CARRY", "DESTROY"):
112
+ return ITEM_ID[arg]
113
+ if op == "SETF":
114
+ return FLAG_BIT[arg]
115
+ if op == "SCORE":
116
+ return int(arg)
117
+ return 0
118
+
119
+
120
+ ACTS = compile_actions()
121
+ MAXTURNS = 999 # the status bar's three digits
122
+ NEEDS_NOUN = {VERB_ID[k] for k in ("GET", "DROP", "OPEN", "CLOSE", "UNLOCK", "LIGHT", "WIND", "PUT")}
123
+
124
+ # The limits the C code assumes.
125
+ assert len(MSGS) < TOK_NL, f"{len(MSGS)} messages: ids must stay below $F0"
126
+ assert len(W.FLAGS) <= 16, "flags are one 16-bit word"
127
+ assert len(W.ITEMS) < 127 and len(W.ROOMS) < 127, "bit 7 of a place marks a container"
128
+ assert len(ACTS) <= 255, "the action index is a char"
129
+ assert sum(int(c.split()[1]) for a in W.ACTIONS for c in a[3] if c.startswith("SCORE")) <= 255 \
130
+ and W.MAXSCORE <= 255, "the score is a char"
131
+ for _t in MSGS:
132
+ assert all(len(w) <= 40 for w in _t.split(" ")), f"a word over 40 letters: {_t}"
133
+
134
+ # The save record's version: a hash of what gives rooms, items and flags their
135
+ # numbers. Any change to them makes an old save refused, not misread.
136
+ WORLD_VERSION = zlib.crc32(repr((list(W.ROOMS), list(W.ITEMS), W.FLAGS)).encode()) & 0xFFFF
137
+
138
+ # ---- text packing -------------------------------------------------------------------------
139
+
140
+
141
+ def sc(ch):
142
+ """ASCII upper case to a screen code."""
143
+ o = ord(ch)
144
+ if 0x41 <= o <= 0x5A:
145
+ return o - 0x40
146
+ assert 0x20 <= o <= 0x3F, repr(ch)
147
+ return o
148
+
149
+
150
+ def pack_text():
151
+ """Byte-pair packing: each round the commonest adjacent pair of symbols
152
+ becomes a new code ($40 up), until 192 codes are used or no pair repeats
153
+ three times. A code costs two table bytes; the decoder expands it on a
154
+ small stack."""
155
+ msgs = [[sc(c) for c in m] for m in MSGS[1:]]
156
+ pairs = []
157
+ while len(pairs) < 192:
158
+ count = {}
159
+ for m in msgs:
160
+ i = 0
161
+ while i < len(m) - 1:
162
+ p = (m[i], m[i + 1])
163
+ count[p] = count.get(p, 0) + 1
164
+ i += 2 if i + 2 < len(m) and m[i + 2] == m[i] == m[i + 1] else 1
165
+ if not count:
166
+ break
167
+ best = min(count, key=lambda p: (-count[p], p))
168
+ if count[best] < 3:
169
+ break
170
+ code = 0x40 + len(pairs)
171
+ pairs.append(best)
172
+ for m in msgs:
173
+ i, out = 0, []
174
+ while i < len(m):
175
+ if i < len(m) - 1 and (m[i], m[i + 1]) == best:
176
+ out.append(code)
177
+ i += 2
178
+ else:
179
+ out.append(m[i])
180
+ i += 1
181
+ m[:] = out
182
+ stream, offs = bytearray(), [0]
183
+ for m in msgs:
184
+ offs.append(len(stream))
185
+ stream += bytes(m) + b"\0"
186
+ return stream, offs, pairs
187
+
188
+
189
+ STREAM, MSG_OFFS, PAIRS = pack_text()
190
+
191
+
192
+ def depth(b):
193
+ return 1 if b < 0x40 else 1 + max(depth(PAIRS[b - 0x40][0]), depth(PAIRS[b - 0x40][1]))
194
+
195
+
196
+ PAIR_DEPTH = max([depth(0x40 + i) for i in range(len(PAIRS))] + [1])
197
+ assert PAIR_DEPTH < 16, f"pair codes nest {PAIR_DEPTH} deep; text.c's stack holds 16"
198
+
199
+
200
+ def unpack(m):
201
+ """The C decoder's output for message m, as a string (checks the packer)."""
202
+ out, p = [], MSG_OFFS[m]
203
+ while STREAM[p]:
204
+ stack = [STREAM[p]]
205
+ p += 1
206
+ while stack:
207
+ b = stack.pop()
208
+ if b < 0x40:
209
+ out.append(b)
210
+ else:
211
+ stack += [PAIRS[b - 0x40][1], PAIRS[b - 0x40][0]]
212
+ return "".join(chr(c + 0x40) if 1 <= c <= 26 else chr(c) for c in out)
213
+
214
+
215
+ for _m in range(1, len(MSGS)):
216
+ assert unpack(_m) == MSGS[_m], (_m, unpack(_m), MSGS[_m])
217
+
218
+ # ---- the engine model ---------------------------------------------------------------------
219
+
220
+
221
+ def stem(word):
222
+ return (word[:WL] + " " * WL)[:WL]
223
+
224
+
225
+ def word_table(words):
226
+ """(stem, id) rows sorted by stem, so the parser starts at the first row
227
+ with the word's first letter (first_letter() below)."""
228
+ rows = sorted({(stem(w), i) for w, i in words})
229
+ stems = [s for s, _ in rows]
230
+ assert len(stems) == len(set(stems)), f"one stem, two meanings: {rows}"
231
+ assert all("A" <= s[0] <= "Z" for s in stems), stems
232
+ return rows
233
+
234
+
235
+ def first_letter(rows):
236
+ return [next((k for k, (s, _) in enumerate(rows) if s[0] >= chr(65 + c)), len(rows))
237
+ for c in range(26)] + [len(rows)]
238
+
239
+
240
+ VERB_TAB = word_table((w, VERB_ID[k]) for k, ws in W.VERBS.items() for w in ws)
241
+ NOUN_TAB = word_table((w, NOUN_ID[k]) for k, ws in W.NOUNS.items() for w in ws)
242
+ NOISE_TAB = [s for s, _ in word_table((w, 1) for w in W.NOISE)]
243
+ ITEM_NOUN = [0] + [NOUN_ID[W.ITEMS[k][1]] for k in ITEM_KEYS]
244
+ ITEM_FLAGS = [""] + [W.ITEMS[k][3] for k in ITEM_KEYS]
245
+ EXITS = [[0] * 6] + [[ROOM_ID.get(W.ROOMS[k][2].get(d), 0) for d in W.DIRECTIONS]
246
+ for k in ROOM_KEYS]
247
+ DARK = [False] + [W.ROOMS[k][3] for k in ROOM_KEYS]
248
+ LIGHT = ITEM_ID[W.LIGHT_ITEM]
249
+
250
+
251
+ def start_loc(s):
252
+ if s is None:
253
+ return 0
254
+ if s == "CARRIED":
255
+ return CARRIED
256
+ if s.startswith("IN:"):
257
+ return 0x80 | ITEM_ID[s[3:]]
258
+ return ROOM_ID[s]
259
+
260
+
261
+ class Game:
262
+ def __init__(self):
263
+ self.room = ROOM_ID[W.START]
264
+ self.loc = [0] + [start_loc(W.ITEMS[k][2]) for k in ITEM_KEYS]
265
+ self.opened = [0] + [1 if "O" in W.ITEMS[k][3] else 0 for k in ITEM_KEYS]
266
+ self.flags = self.score = self.turns = self.over = 0
267
+ self.out = []
268
+ self.disk = None # "SAVE" or "LOAD" after a turn that asked for one
269
+
270
+ # -- output tokens
271
+ def m(self, key):
272
+ self.out.append(ENGINE_MSG[key] if isinstance(key, str) else key)
273
+
274
+ def nl(self):
275
+ self.out.append(TOK_NL)
276
+
277
+ def num(self, v):
278
+ self.out += [TOK_NUM, v & 0xFF, v >> 8]
279
+
280
+ def chars(self, text):
281
+ for c in text:
282
+ self.out += [TOK_CHR, sc(c)]
283
+
284
+ # -- the world
285
+ def reach_raw(self, i):
286
+ lc = self.loc[i]
287
+ if lc in (CARRIED, self.room):
288
+ return True
289
+ if lc & 0x80 and lc != CARRIED:
290
+ c = lc & 0x7F
291
+ return bool(self.opened[c]) and self.loc[c] in (CARRIED, self.room)
292
+ return False
293
+
294
+ def lit(self):
295
+ return not DARK[self.room] or self.reach_raw(LIGHT)
296
+
297
+ def scope(self, i):
298
+ return self.loc[i] == CARRIED or (self.lit() and self.reach_raw(i))
299
+
300
+ def picture(self):
301
+ return PIC_ID[W.DARK_PICTURE] if not self.lit() else PIC_ID[W.ROOMS[ROOM_KEYS[self.room - 1]][4]]
302
+
303
+ def listing(self, where, skip_scenery):
304
+ k = 0
305
+ for i in range(1, NITEM):
306
+ if self.loc[i] == where and not (skip_scenery and "S" in ITEM_FLAGS[i]):
307
+ if k:
308
+ self.m("COMMA")
309
+ self.m(ITEM_DESC[ITEM_KEYS[i - 1]])
310
+ k += 1
311
+ return k
312
+
313
+ def has_listed(self, where):
314
+ return any(self.loc[i] == where and "S" not in ITEM_FLAGS[i] for i in range(1, NITEM))
315
+
316
+ def contents(self, c):
317
+ if self.has_listed(0x80 | c):
318
+ self.m(ITEM_INSIDE[ITEM_KEYS[c - 1]])
319
+ self.m("COLON")
320
+ self.listing(0x80 | c, True)
321
+ self.m("DOT")
322
+ else:
323
+ self.m("EMPTY")
324
+ self.nl()
325
+
326
+ def look(self):
327
+ if not self.lit():
328
+ self.m("DARK")
329
+ self.nl()
330
+ return
331
+ self.m(ROOM_DESC[ROOM_KEYS[self.room - 1]])
332
+ self.nl()
333
+ if self.has_listed(self.room):
334
+ self.m("SEE")
335
+ self.listing(self.room, True)
336
+ self.m("DOT")
337
+ self.nl()
338
+ for c in range(1, NITEM):
339
+ if "C" in ITEM_FLAGS[c] and self.loc[c] == self.room and self.opened[c] \
340
+ and self.has_listed(0x80 | c):
341
+ self.contents(c)
342
+ dirs = [d for d in range(6) if EXITS[self.room][d]]
343
+ if dirs:
344
+ self.m("EXITS")
345
+ for d in dirs:
346
+ self.m("DIR" + W.DIRECTIONS[d])
347
+ self.m("DOT")
348
+ else:
349
+ self.m("NOEXITS")
350
+ self.nl()
351
+
352
+ def score_line(self):
353
+ self.m("SCORED")
354
+ self.num(self.score)
355
+ self.m("OUTOF")
356
+ self.num(W.MAXSCORE)
357
+ self.m("IN")
358
+ self.num(self.turns)
359
+ self.m("TURN" if self.turns == 1 else "TURNS")
360
+ self.nl()
361
+
362
+ def finish(self, how):
363
+ self.m(how)
364
+ self.nl()
365
+ self.score_line()
366
+ self.m("RANK0" if self.score == W.MAXSCORE else "RANK1")
367
+ self.nl()
368
+ self.m("AGAIN")
369
+ self.nl()
370
+ self.over = 1 if how == "WON" else 2
371
+
372
+ # -- the action table
373
+ def cond(self, op, a):
374
+ if op == 1:
375
+ return self.room == a
376
+ if op == 2:
377
+ return self.loc[a] == CARRIED
378
+ if op == 3:
379
+ return self.loc[a] == self.room
380
+ if op == 4:
381
+ return self.scope(a)
382
+ if op == 5:
383
+ return bool(self.flags >> a & 1)
384
+ if op == 6:
385
+ return not self.flags >> a & 1
386
+ if op == 7:
387
+ return not self.lit()
388
+ return True
389
+
390
+ def cmd(self, op, a):
391
+ if op == 1:
392
+ self.m(a)
393
+ self.nl()
394
+ elif op == 2:
395
+ self.room = a
396
+ elif op == 3:
397
+ self.look()
398
+ elif op == 4:
399
+ self.flags |= 1 << a
400
+ elif op == 5:
401
+ self.loc[a], self.loc[a + 1] = self.loc[a + 1], self.loc[a]
402
+ elif op == 6:
403
+ self.loc[a] = CARRIED
404
+ elif op == 7:
405
+ self.loc[a] = 0
406
+ elif op == 8:
407
+ self.score += a
408
+ elif op == 9:
409
+ self.finish("WON")
410
+
411
+ def run_row(self, row):
412
+ for k in range(2, 10, 2):
413
+ if row[k] and not self.cond(row[k], row[k + 1]):
414
+ return False
415
+ for k in range(10, 18, 2):
416
+ if row[k]:
417
+ self.cmd(row[k], row[k + 1])
418
+ return True
419
+
420
+ def scan(self, v, n):
421
+ for row in ACTS:
422
+ if row[0] == v and (row[1] == 0 or row[1] == n) and self.run_row(row):
423
+ return True
424
+ return False
425
+
426
+ # -- built-in verbs
427
+ def find(self, n, test):
428
+ for i in range(1, NITEM):
429
+ if ITEM_NOUN[i] == n and test(i):
430
+ return i
431
+ return 0
432
+
433
+ def not_found(self):
434
+ self.m("NOTHERE" if self.lit() else "DARK")
435
+ self.nl()
436
+
437
+ def builtin(self, v, n):
438
+ V = {k: VERB_ID[k] for k in W.VERBS}
439
+ if not n and v in NEEDS_NOUN:
440
+ self.m("WHAT")
441
+ self.nl()
442
+ return
443
+ if v == V["GO"]:
444
+ if not 1 <= n <= 6:
445
+ self.m("WHICHWAY")
446
+ self.nl()
447
+ elif not EXITS[self.room][n - 1]:
448
+ self.m("NOWAY")
449
+ self.nl()
450
+ else:
451
+ self.room = EXITS[self.room][n - 1]
452
+ self.look()
453
+ elif v == V["GET"]:
454
+ i = self.find(n, lambda i: self.loc[i] != CARRIED and self.scope(i))
455
+ if not n:
456
+ self.m("WHAT")
457
+ elif i:
458
+ held = sum(1 for j in range(1, NITEM) if self.loc[j] == CARRIED)
459
+ if "P" not in ITEM_FLAGS[i]:
460
+ self.m("FIXED")
461
+ elif held >= W.MAXLOAD:
462
+ self.m("FULL")
463
+ else:
464
+ self.loc[i] = CARRIED
465
+ self.m("TAKEN")
466
+ elif self.find(n, lambda i: self.loc[i] == CARRIED):
467
+ self.m("HAVEIT")
468
+ else:
469
+ self.not_found()
470
+ return
471
+ self.nl()
472
+ elif v == V["DROP"]:
473
+ i = self.find(n, lambda i: self.loc[i] == CARRIED)
474
+ if i:
475
+ self.loc[i] = self.room
476
+ self.m("DROPPED")
477
+ else:
478
+ self.m("NOTHAVE")
479
+ self.nl()
480
+ elif v == V["LOOK"]:
481
+ if not n:
482
+ self.look()
483
+ return
484
+ i = self.find(n, self.scope)
485
+ if not i:
486
+ self.not_found()
487
+ return
488
+ exam = ITEM_EXAM[ITEM_KEYS[i - 1]]
489
+ if exam:
490
+ self.m(exam)
491
+ self.nl()
492
+ if "C" in ITEM_FLAGS[i]:
493
+ if self.opened[i]:
494
+ self.contents(i)
495
+ else:
496
+ self.m("CLOSED")
497
+ self.nl()
498
+ elif not exam:
499
+ self.m("NOTHINGSPECIAL")
500
+ self.nl()
501
+ elif v == V["INV"]:
502
+ self.m("CARRYING")
503
+ if not self.listing(CARRIED, False):
504
+ self.m("NOTHING")
505
+ self.m("DOT")
506
+ self.nl()
507
+ elif v in (V["OPEN"], V["CLOSE"]):
508
+ i = self.find(n, self.scope)
509
+ if not i:
510
+ self.not_found()
511
+ return
512
+ if "C" not in ITEM_FLAGS[i]:
513
+ self.m("NOTOPEN" if v == V["OPEN"] else "CANT")
514
+ self.nl()
515
+ elif v == V["OPEN"] and self.opened[i]:
516
+ self.m("ISOPEN")
517
+ self.nl()
518
+ elif v == V["OPEN"]:
519
+ self.opened[i] = 1
520
+ self.m("OPENED")
521
+ self.nl()
522
+ self.contents(i)
523
+ elif self.opened[i]:
524
+ self.opened[i] = 0
525
+ self.m("CLOSEDOK")
526
+ self.nl()
527
+ else:
528
+ self.m("ISSHUT")
529
+ self.nl()
530
+ elif v == V["SCORE"]:
531
+ self.score_line()
532
+ elif v == V["QUIT"]:
533
+ self.finish("QUITS")
534
+ elif v == V["HELP"]:
535
+ self.m("HELP")
536
+ self.nl()
537
+ else:
538
+ self.m("CANT")
539
+ self.nl()
540
+
541
+ # -- the parser and one command
542
+ def parse(self, line):
543
+ words = [w for w in line.split(" ") if w and stem(w) not in NOISE_TAB]
544
+ if not words:
545
+ self.m("PARDON")
546
+ self.nl()
547
+ return None
548
+ noun = lambda w: next((i for s, i in NOUN_TAB if s == stem(w)), 0)
549
+ v = next((i for s, i in VERB_TAB if s == stem(words[0])), 0)
550
+ k = 1
551
+ n = noun(words[1]) if len(words) > 1 else 0
552
+ if v and v != VERB_ID["GO"] and 1 <= n <= 6 and len(words) > 2:
553
+ k, n = 2, noun(words[2]) # PICK UP LAMP: the UP is not a direction
554
+ if not v:
555
+ d = noun(words[0])
556
+ if 1 <= d <= 6 and len(words) == 1:
557
+ return VERB_ID["GO"], d
558
+ if d and len(words) == 1:
559
+ self.m("WITHIT") # LAMP alone
560
+ self.nl()
561
+ return None
562
+ self.unknown(words[0])
563
+ return None
564
+ if len(words) > k and not n:
565
+ self.unknown(words[k])
566
+ return None
567
+ return v, n
568
+
569
+ def unknown(self, word):
570
+ self.m("UNKNOWN")
571
+ self.chars(word[:12])
572
+ self.m("DOT")
573
+ self.nl()
574
+
575
+ def command(self, line):
576
+ self.out += [TOK_STYLE, STYLE_ECHO]
577
+ self.chars(">" + line)
578
+ self.out += [TOK_NL, TOK_STYLE, STYLE_TEXT]
579
+ p = self.parse(line)
580
+ if not p:
581
+ return
582
+ v, n = p
583
+ if v in (VERB_ID["SAVE"], VERB_ID["LOAD"]):
584
+ self.disk = "SAVE" if v == VERB_ID["SAVE"] else "LOAD"
585
+ return
586
+ if self.turns < MAXTURNS: # the status bar has three digits
587
+ self.turns += 1
588
+ if not self.scan(v, n):
589
+ self.builtin(v, n)
590
+ if not self.over:
591
+ for row in ACTS:
592
+ if row[0] == 0:
593
+ self.run_row(row)
594
+
595
+ # -- the save record (src/save.c)
596
+ def record(self):
597
+ b = [ord("S"), ord("W"), WORLD_VERSION & 0xFF, WORLD_VERSION >> 8, self.room, self.score,
598
+ self.turns & 0xFF, self.turns >> 8, self.flags & 0xFF, self.flags >> 8] + \
599
+ self.loc[1:] + self.opened[1:]
600
+ return bytes(b + [fold8(b)])
601
+
602
+ def restore(self, rec):
603
+ n = NITEM - 1
604
+ self.room, self.score = rec[4], rec[5]
605
+ self.turns = rec[6] | rec[7] << 8
606
+ self.flags = rec[8] | rec[9] << 8
607
+ self.loc = [0] + list(rec[10:10 + n])
608
+ self.opened = [0] + list(rec[10 + n:10 + 2 * n])
609
+
610
+ def disk_reply(self, key, code):
611
+ self.m(key)
612
+ self.chars("(%02d" % code)
613
+ self.m("CLOSEP")
614
+ self.nl()
615
+
616
+
617
+ def fold8(bs):
618
+ c = 0
619
+ for b in bs:
620
+ c = ((c ^ b) * 3 + 1) & 0xFF
621
+ return c
622
+
623
+
624
+ def fold16(c, v):
625
+ return ((c ^ v) * 5 + 1) & 0xFFFF
626
+
627
+ # ---- the printer model: one window line per frame --------------------------------------
628
+
629
+
630
+ class Printer:
631
+ def __init__(self, toks):
632
+ self.toks, self.t = toks, 0
633
+ self.src = [] # characters of the token being read (screen codes)
634
+ self.pend = None
635
+ self.back = None # a new line read one character too early
636
+ self.style = STYLE_TEXT
637
+
638
+ def char(self):
639
+ """Next screen code, 'NL' or None at the end; colour tokens act at once."""
640
+ if self.back:
641
+ self.back = None
642
+ return "NL"
643
+ while not self.src:
644
+ if self.t >= len(self.toks):
645
+ return None
646
+ k = self.toks[self.t]
647
+ self.t += 1
648
+ if k == TOK_NL:
649
+ return "NL"
650
+ if k == TOK_STYLE:
651
+ self.style = self.toks[self.t]
652
+ self.t += 1
653
+ elif k == TOK_CHR:
654
+ self.src = [self.toks[self.t]]
655
+ self.t += 1
656
+ elif k == TOK_NUM:
657
+ v = self.toks[self.t] | self.toks[self.t + 1] << 8
658
+ self.t += 2
659
+ self.src = [ord(c) for c in str(v)]
660
+ else:
661
+ self.src = [sc(c) for c in MSGS[k]]
662
+ return self.src.pop(0)
663
+
664
+ def item(self):
665
+ """A word (list of screen codes), 'NL', or None at the end."""
666
+ c = self.char()
667
+ while c == 0x20:
668
+ c = self.char()
669
+ if c in ("NL", None):
670
+ return c
671
+ w = [c]
672
+ while True:
673
+ c = self.char()
674
+ if c == 0x20:
675
+ return w
676
+ if c in ("NL", None):
677
+ self.back = c == "NL"
678
+ return w
679
+ w.append(c)
680
+
681
+ def line(self):
682
+ """The next line's 40 screen codes, or None when done."""
683
+ if self.pend is None:
684
+ self.pend = self.item()
685
+ if self.pend is None:
686
+ return None
687
+ row, col = [0x20] * WIDTH, 0
688
+ while True:
689
+ if self.pend is None:
690
+ self.pend = self.item()
691
+ p = self.pend
692
+ if p is None:
693
+ break
694
+ if p == "NL":
695
+ self.pend = None
696
+ break
697
+ need = len(p) + (1 if col else 0)
698
+ if col + need > WIDTH:
699
+ if col:
700
+ break
701
+ p, self.pend = p[:WIDTH], p[WIDTH:]
702
+ row[:len(p)] = p
703
+ col = WIDTH
704
+ break
705
+ if col:
706
+ col += 1
707
+ row[col:col + len(p)] = p
708
+ col += len(p)
709
+ self.pend = None
710
+ return [c | self.style if i < col else c for i, c in enumerate(row)]
711
+
712
+
713
+ def play(script, disk_saved=None, stop_after=None):
714
+ """Play commands through the model. Returns (game, lines, frames, codes, saved)."""
715
+ g = Game()
716
+ g.out += [TOK_STYLE, STYLE_TEXT]
717
+ g.m("INTRO")
718
+ g.nl()
719
+ g.nl()
720
+ g.look()
721
+ frames, lines, shown = 1, [], g.picture() # the first frame draws the picture
722
+ g.seen = {shown}
723
+ pr = Printer(g.out)
724
+ style = STYLE_TEXT
725
+ codes, saved = {}, disk_saved
726
+
727
+ def drain():
728
+ nonlocal frames, style, pr
729
+ while True:
730
+ pr.style = style
731
+ r = pr.line()
732
+ style = pr.style
733
+ if r is None:
734
+ break
735
+ lines.append(r)
736
+ frames += 1
737
+
738
+ drain()
739
+ for cmd in script:
740
+ g.out = []
741
+ frames += math.ceil((len(cmd) + 1) / KEYS_PER_FRAME)
742
+ g.command(cmd)
743
+ assert len(g.out) < 250, f"{cmd}: {len(g.out)} output bytes; engine.c keeps 250"
744
+ if g.picture() != shown:
745
+ shown = g.picture()
746
+ g.seen.add(shown)
747
+ frames += 1
748
+ pr = Printer(g.out)
749
+ drain()
750
+ if g.disk:
751
+ g.out = []
752
+ if g.disk == "SAVE":
753
+ saved = g.record()
754
+ g.disk_reply("SAVED", 0)
755
+ codes["save"] = 0
756
+ elif saved:
757
+ g.restore(saved)
758
+ g.disk_reply("LOADED", 0)
759
+ g.look()
760
+ codes["load"] = 0
761
+ else:
762
+ g.disk_reply("NOSAVE", 62)
763
+ codes["load"] = 62
764
+ g.disk = None
765
+ if g.picture() != shown:
766
+ shown = g.picture()
767
+ frames += 1
768
+ pr = Printer(g.out)
769
+ drain()
770
+ if g.over or cmd == stop_after:
771
+ break
772
+ return g, lines, frames, codes, saved
773
+
774
+
775
+ def text_fold(lines):
776
+ f = 0
777
+ for row in lines:
778
+ for c in row:
779
+ f = fold16(f, c)
780
+ return f
781
+
782
+
783
+ def loc_fold(g):
784
+ f = 0
785
+ for b in g.loc[1:] + g.opened[1:]:
786
+ f = fold16(f, b)
787
+ return f
788
+
789
+
790
+ def row_text(row):
791
+ return "".join(chr((c & 0x7F) + 0x40) if 1 <= c & 0x7F <= 26 else chr(c & 0x7F) for c in row)
792
+
793
+ # ---- pictures ---------------------------------------------------------------------------------
794
+
795
+
796
+ class Pic:
797
+ def __init__(self):
798
+ self.cells = [[("sky", 0)] * 40 for _ in range(7)]
799
+
800
+ def put(self, x, y, glyph, colour=0):
801
+ assert glyph in W.GLYPHS, glyph
802
+ self.cells[y][x] = (glyph, colour if "c" in "".join(W.GLYPHS[glyph]) else 0)
803
+
804
+ def fill(self, x0, y0, x1, y1, glyph, colour=0):
805
+ for y in range(y0, y1 + 1):
806
+ for x in range(x0, x1 + 1):
807
+ self.put(x, y, glyph, colour)
808
+
809
+
810
+ GLYPH_KEYS = list(W.GLYPHS)
811
+ assert len(GLYPH_KEYS) <= 64
812
+
813
+
814
+ def glyph_bytes(rows):
815
+ val = {".": 0, "a": 1, "b": 2, "c": 3}
816
+ out = []
817
+ for r in rows:
818
+ assert len(r) == 4, r
819
+ out.append(val[r[0]] << 6 | val[r[1]] << 4 | val[r[2]] << 2 | val[r[3]])
820
+ return out
821
+
822
+
823
+ def build_pictures():
824
+ tiles, tile_id, streams, raw = [], {}, [], 0
825
+ for key in PIC_KEYS:
826
+ p = Pic()
827
+ W.PICTURES[key](p)
828
+ cells = [c for row in p.cells for c in row]
829
+ raw += 2 * len(cells)
830
+ ids = []
831
+ for c in cells:
832
+ if c not in tile_id:
833
+ tile_id[c] = len(tiles)
834
+ tiles.append(c)
835
+ ids.append(tile_id[c])
836
+ s = bytearray()
837
+ for row in range(7): # nothing crosses the end of a row
838
+ i, end, lit = 40 * row, 40 * row + 40, []
839
+ while i < end:
840
+ j = i
841
+ while j < end and ids[j] == ids[i]:
842
+ j += 1
843
+ if j - i >= 3: # a run: its length, then the tile
844
+ if lit:
845
+ s += bytes([0x80 | len(lit)] + lit)
846
+ lit = []
847
+ s += bytes([j - i, ids[i]])
848
+ else: # short: into a literal, $80 + count, tiles
849
+ lit += ids[i:j]
850
+ i = j
851
+ if lit:
852
+ s += bytes([0x80 | len(lit)] + lit)
853
+ streams.append(s)
854
+ assert len(tiles) <= 255
855
+ return tiles, streams, raw
856
+
857
+ # ---- sound ----------------------------------------------------------------------------------------
858
+ NOTES = {"C5": 523.25, "E5": 659.26, "G5": 783.99, "C6": 1046.50, "A4": 440.0}
859
+ PAL_CLOCK, NTSC_CLOCK = 985248, 1022727
860
+
861
+
862
+ def freq(hz, clock):
863
+ return round(hz * 16777216 / clock)
864
+
865
+ # ---- C output ---------------------------------------------------------------------------------------
866
+
867
+
868
+ def carr(ctype, name, values, per=16, fmt="0x%02x"):
869
+ body = ",\n ".join(", ".join(fmt % v for v in values[i:i + per])
870
+ for i in range(0, len(values), per))
871
+ return f"static const {ctype} {name}[{len(values)}] = {{\n {body}\n}};\n"
872
+
873
+
874
+ def cname(k):
875
+ return re.sub(r"[^A-Z0-9]", "_", k.upper())
876
+
877
+
878
+ def write_world(tiles, streams):
879
+ o = ["// gen_world.h: written by tools/gen.py from tools/world.py. Do not edit.",
880
+ "#ifndef GEN_WORLD_H", "#define GEN_WORLD_H", ""]
881
+ o.append(f"#define NROOM {len(ROOM_KEYS) + 1}")
882
+ o.append(f"#define NITEM {NITEM}")
883
+ o.append(f"#define NACT {len(ACTS)}")
884
+ o.append(f"#define NVERBW {len(VERB_TAB)}")
885
+ o.append(f"#define NNOUNW {len(NOUN_TAB)}")
886
+ o.append(f"#define NNOISE {len(NOISE_TAB)}")
887
+ o.append(f"#define MAXLOAD {W.MAXLOAD}")
888
+ o.append(f"#define MAXSCORE {W.MAXSCORE}")
889
+ o.append(f"#define START_ROOM {ROOM_ID[W.START]}")
890
+ o.append(f"#define LIGHT_ITEM {LIGHT}")
891
+ o.append(f"#define DARK_PIC {PIC_ID[W.DARK_PICTURE]}")
892
+ o.append(f"#define TITLE_PIC {PIC_ID[W.TITLE_PICTURE]}")
893
+ o.append(f"#define NPIC {len(PIC_KEYS)}")
894
+ o.append(f"#define MAXTURNS {MAXTURNS}")
895
+ o.append(f"#define WORLD_VERSION 0x{WORLD_VERSION:04x} // a hash of rooms, items, flags")
896
+ o.append(f"#define NTILE {len(tiles)}")
897
+ o.append(f"#define NGLYPH {len(GLYPH_KEYS)}")
898
+ for k, v in VERB_ID.items():
899
+ o.append(f"#define V_{k} {v}")
900
+ for k in W.DIRECTIONS:
901
+ o.append(f"#define N_{k} {NOUN_ID[k]}")
902
+ for k, v in ENGINE_MSG.items():
903
+ o.append(f"#define M_{k} {v}")
904
+ for k, v in ITEM_ID.items():
905
+ o.append(f"#define I_{cname(k)} {v}")
906
+ o.append("#define IF_PORTABLE 1\n#define IF_CONTAINER 2\n#define IF_OPEN 4\n#define IF_SCENERY 8")
907
+ o.append("")
908
+ o.append("// Packed text: see tools/gen.py. msg_at[m] is message m's first byte.")
909
+ o.append(carr("char", "text_bytes", list(STREAM)))
910
+ o.append(carr("unsigned", "msg_at", [0] + MSG_OFFS[1:], 12, "%d"))
911
+ o.append(carr("char", "pair_left", [a for a, _ in PAIRS]))
912
+ o.append(carr("char", "pair_right", [b for _, b in PAIRS]))
913
+ o.append("// Rooms: name, description, exits N S E W U D, dark, picture.")
914
+ o.append(carr("char", "room_name", [0] + [ROOM_NAME[k] for k in ROOM_KEYS], 16, "%d"))
915
+ o.append(carr("char", "room_desc", [0] + [ROOM_DESC[k] for k in ROOM_KEYS], 16, "%d"))
916
+ o.append(carr("char", "room_exits", [x for e in EXITS for x in e], 6, "%d"))
917
+ o.append(carr("char", "room_dark", [int(d) for d in DARK], 16, "%d"))
918
+ o.append(carr("char", "room_pic", [0] + [PIC_ID[W.ROOMS[k][4]] for k in ROOM_KEYS], 16, "%d"))
919
+ o.append("// Items: description, noun, start location, flags, examine text, container phrase.")
920
+ o.append(carr("char", "item_desc", [0] + [ITEM_DESC[k] for k in ITEM_KEYS], 16, "%d"))
921
+ o.append(carr("char", "item_noun", ITEM_NOUN, 16, "%d"))
922
+ o.append(carr("char", "item_start", [0] + [start_loc(W.ITEMS[k][2]) for k in ITEM_KEYS]))
923
+ fl = [0] + [("P" in f) | ("C" in f) << 1 | ("O" in f) << 2 | ("S" in f) << 3
924
+ for f in (W.ITEMS[k][3] for k in ITEM_KEYS)]
925
+ o.append(carr("char", "item_flags", fl, 16, "%d"))
926
+ o.append(carr("char", "item_exam", [0] + [ITEM_EXAM[k] for k in ITEM_KEYS], 16, "%d"))
927
+ o.append(carr("char", "item_inside", [0] + [ITEM_INSIDE[k] for k in ITEM_KEYS], 16, "%d"))
928
+ o.append("// Words: four letters, space padded, then the id.")
929
+ o.append(carr("char", "verb_words", [ord(c) for s, _ in VERB_TAB for c in s], 16, "%d"))
930
+ o.append(carr("char", "verb_ids", [i for _, i in VERB_TAB], 16, "%d"))
931
+ o.append(carr("char", "noun_words", [ord(c) for s, _ in NOUN_TAB for c in s], 16, "%d"))
932
+ o.append(carr("char", "noun_ids", [i for _, i in NOUN_TAB], 16, "%d"))
933
+ o.append(carr("char", "noise_words", [ord(c) for s in NOISE_TAB for c in s], 16, "%d"))
934
+ o.append("// The first row of each table whose stem starts with A, B, ... Z, then the count.")
935
+ o.append(carr("char", "verb_first", first_letter(VERB_TAB), 27, "%d"))
936
+ o.append(carr("char", "noun_first", first_letter(NOUN_TAB), 27, "%d"))
937
+ o.append(carr("char", "noise_first", first_letter([(s, 1) for s in NOISE_TAB]), 27, "%d"))
938
+ o.append("// Actions: verb, noun (0 any), four (condition, arg), four (command, arg).")
939
+ o.append(carr("char", "act", [x for r in ACTS for x in r], 18, "%d"))
940
+ o.append("// Pictures: glyph bytes for codes 64 up, tiles (character, colour RAM), runs and literals.")
941
+ o.append(carr("char", "glyph_bytes", [b for k in GLYPH_KEYS for b in glyph_bytes(W.GLYPHS[k])], 8))
942
+ o.append(carr("char", "tile_char", [64 + GLYPH_KEYS.index(g) for g, _ in tiles]))
943
+ o.append(carr("char", "tile_colour", [8 | c for _, c in tiles]))
944
+ pic_at, allp = [], bytearray()
945
+ for s in streams:
946
+ pic_at.append(len(allp))
947
+ allp += s
948
+ o.append(carr("char", "pic_bytes", list(allp)))
949
+ o.append(carr("unsigned", "pic_at", pic_at, 12, "%d"))
950
+ o.append("// Note frequencies, PAL then NTSC: C5 E5 G5 C6 A4.")
951
+ o.append(carr("unsigned", "note_pal", [freq(h, PAL_CLOCK) for h in NOTES.values()], 8, "%d"))
952
+ o.append(carr("unsigned", "note_ntsc", [freq(h, NTSC_CLOCK) for h in NOTES.values()], 8, "%d"))
953
+ o.append("#endif\n")
954
+ open(os.path.join(ROOT, "src", "gen_world.h"), "w").write("\n".join(o))
955
+ return len(allp)
956
+
957
+
958
+ def cstr(s):
959
+ return '"' + s.replace('"', '\\"') + '"'
960
+
961
+
962
+ def write_script(variants):
963
+ o = ["// gen_script.h: written by tools/gen.py (the autopilot script and what the",
964
+ "// model says each build must end with). Do not edit.",
965
+ "#ifndef GEN_SCRIPT_H", "#define GEN_SCRIPT_H", ""]
966
+ for i, (cond, script, g, lines, frames, codes) in enumerate(variants):
967
+ o.append(("#if " if i == 0 else "#elif ") + cond)
968
+ o.append("static const char *const script[] = {\n " +
969
+ ",\n ".join(cstr(s) for s in script) + ",\n 0\n};")
970
+ o.append(f"#define EXPECT_ROOM {g.room}")
971
+ o.append(f"#define EXPECT_SCORE {g.score}")
972
+ o.append(f"#define EXPECT_TURNS {g.turns}")
973
+ o.append(f"#define EXPECT_FLAGS 0x{g.flags:04x}")
974
+ o.append(f"#define EXPECT_OVER {g.over}")
975
+ o.append(f"#define EXPECT_LOCFOLD 0x{loc_fold(g):04x}")
976
+ o.append(f"#define EXPECT_TEXTFOLD 0x{text_fold(lines):04x}")
977
+ o.append(f"#define EXPECT_FRAMES {frames}")
978
+ o.append(f"#define METER_HOLD {min(frames, 255)} // the meter records at most 255")
979
+ o.append(f"#define EXPECT_LINES {len(lines)}")
980
+ o.append(f"#define EXPECT_SAVE_CODE {codes.get('save', 0xff)}")
981
+ o.append(f"#define EXPECT_LOAD_CODE {codes.get('load', 0xff)}")
982
+ o.append("#endif\n#endif\n")
983
+ open(os.path.join(ROOT, "src", "gen_script.h"), "w").write("\n".join(o))
984
+
985
+
986
+ def main():
987
+ tiles, streams, pic_raw = build_pictures()
988
+ pic_packed = write_world(tiles, streams)
989
+ split = W.SCRIPT.index("SAVE")
990
+ load_at = W.SCRIPT.index("LOAD")
991
+ full = play(W.SCRIPT)
992
+ # Every picture must be drawn under the meter: the library's, never drawn
993
+ # by the first script, was over the frame (found in review).
994
+ missing = sorted(k for k in PIC_KEYS if PIC_ID[k] not in full[0].seen)
995
+ assert not missing, f"the script never shows the pictures {missing}: visit them"
996
+ part1 = play(W.SCRIPT[:split + 1], stop_after="SAVE")
997
+ part2 = play(W.SCRIPT[load_at:], disk_saved=part1[4])
998
+ variants = [
999
+ ("DISKTEST == 1", W.SCRIPT[:split + 1]) + part1[:4],
1000
+ ("DISKTEST == 2", W.SCRIPT[load_at:]) + part2[:4],
1001
+ ("1", W.SCRIPT) + full[:4],
1002
+ ]
1003
+ write_script(variants)
1004
+ for v in variants:
1005
+ if v[4] > 255:
1006
+ print(f"gen: WARNING: {v[0]}: {v[4]} play frames. The meter records the first 255, "
1007
+ f"so the rest are not metered and check's frame count will not match. "
1008
+ f"Shorten the script (README, Extending it).", file=sys.stderr)
1009
+ os.makedirs(os.path.join(ROOT, "build"), exist_ok=True)
1010
+ open(os.path.join(ROOT, "build", "save-expected.bin"), "wb").write(part1[4])
1011
+
1012
+ plain = sum(len(m) + 1 for m in MSGS[1:]) + 2 * (len(MSGS) - 1)
1013
+ packed = len(STREAM) + 2 * len(PAIRS) + 2 * (len(MSGS) - 1)
1014
+ five = sum(2 * math.ceil(len(m) / 3) for m in MSGS[1:]) + 2 * (len(MSGS) - 1)
1015
+ print(f"gen: {len(MSGS) - 1} messages, {sum(len(m) for m in MSGS[1:])} characters")
1016
+ print(f"gen: text as C strings with a pointer table {plain} bytes; packed {packed} "
1017
+ f"(stream {len(STREAM)}, {len(PAIRS)} pair codes {2 * len(PAIRS)}, "
1018
+ f"pointers {2 * (len(MSGS) - 1)}): "
1019
+ f"{100 * (plain - packed) / plain:.1f} % smaller; 5-bit packing would be {five}")
1020
+ print(f"gen: pictures {len(PIC_KEYS)} x 280 cells, {pic_raw} bytes raw, {pic_packed} packed, "
1021
+ f"{len(tiles)} tiles over {len(GLYPH_KEYS)} glyphs")
1022
+ for v in variants:
1023
+ g = v[2]
1024
+ print(f"gen: {v[0]}: {len(v[1])} commands, room {g.room}, score {g.score}, "
1025
+ f"turns {g.turns}, over {g.over}, {len(v[3])} lines, {v[4]} play frames, "
1026
+ f"text fold 0x{text_fold(v[3]):04x}, codes {v[5]}")
1027
+ if "--print" in sys.argv:
1028
+ for row in full[1]:
1029
+ print(f" |{row_text(row)}|")
1030
+
1031
+
1032
+ if __name__ == "__main__":
1033
+ main()