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,975 @@
1
+ ---
2
+ recipe: dig-and-guards
3
+ toolchain: oscar64
4
+ output_format: PRG
5
+ region: both
6
+ techniques: [dig_and_refill]
7
+ file_formats: [PRG]
8
+ uses_registers: [D011, D020, D021, DC06, DC07, DC0F]
9
+ uses_kernal: []
10
+ ---
11
+
12
+ <!-- doc-type: recipe -->
13
+
14
+ # Oscar64 Dig and Guards: holes that refill, a guard that falls in, climbs out and dies
15
+
16
+ ## Synopsis
17
+
18
+ A 28 x 16 cell level with a brick floor, two ladders and a solid bottom.
19
+ A scripted player (autopilot) digs a hole beside it, waits for the guard
20
+ to fall in, walks over the trapped guard's head, runs on and digs a
21
+ second hole. The guard climbs out of the first hole before it refills,
22
+ falls into the second, and is still inside when it refills: it dies and
23
+ respawns on the top row, lands on the brick floor, walks to the nearest
24
+ ladder and climbs down after the player. Every tick folds the map and
25
+ all actor states into a 16-bit checksum, and every 16th tick compares it
26
+ with a value from a Python model of the same rules, compiled in. After
27
+ the script one stress tick runs the technique's worst frame on purpose.
28
+ The screen shows event counts, CIA1 timer B cycles and PASS or FAIL.
29
+ `$02FF` = `01` and a green border on pass, `02` and red on fail. This is
30
+ the `dig_and_refill` technique in `docs/techniques/logic.md`; the
31
+ result-byte contract is `headless-verify.md`.
32
+
33
+ ## Source
34
+
35
+ ```c
36
+ // dig-and-guards.c
37
+ //
38
+ // Dig-and-refill bricks and a chasing guard on a 28 x 16 cell map. The
39
+ // player digs a brick diagonally below-left or below-right when the cell
40
+ // above it is empty. The hole goes into an eight-slot timer list: it
41
+ // shows a refill glyph for its last 16 ticks and is brick again at 0.
42
+ // A guard that falls into a hole is trapped for 30 guard ticks, then
43
+ // climbs out and steps sideways. Anyone standing over a trapped guard is
44
+ // held up by it. A hole that refills on a guard kills it, and the guard
45
+ // respawns on the top row. Guards move on even ticks by a greedy rule:
46
+ // straight at the player along a clear row, up or down a ladder they
47
+ // stand on, else to the nearest column on their row that offers a move
48
+ // toward the player's row, else toward the player's column.
49
+ // The player follows a script (autopilot). A checksum of the map and all
50
+ // actor states is folded every tick and compared every 16 ticks with the
51
+ // values from a Python model of the same rules, compiled in.
52
+ // After the script, one stress tick: four free guards scan their whole
53
+ // row, eight live holes on the ledge (one expires, one starts refilling),
54
+ // and the player tries a dig with the hole list full.
55
+ // Row 0: tick, digs, traps, escapes, kills, walk-over ticks. Row 1: CIA1
56
+ // timer B cycles, worst hole-list update, worst single guard update,
57
+ // worst tick. Row 2: stress tick total, its hole-list update and its
58
+ // worst guard, checkpoint misses, result. Row 3: final checksum, worst
59
+ // player update (move or dig).
60
+ // The screen keeps the scenario's last tick; the stress tick is not drawn.
61
+ // $02FF = 01 and a green border on pass, 02 and red on fail.
62
+ // Build: oscar64 -tm=c64 -O2 -o=dig-and-guards.prg dig-and-guards.c
63
+ #include <c64/vic.h>
64
+ #include <c64/cia.h>
65
+ #include <string.h>
66
+
67
+ #define SCREEN ((char *)0x0400)
68
+ #define COLOUR ((char *)0xd800)
69
+ #define RESULT (*(volatile char *)0x02ff)
70
+ #define CODE_PASS 0x01
71
+ #define CODE_FAIL 0x02
72
+
73
+ #define W 28
74
+ #define H 16
75
+ #define ROW0 5 // screen row of map row 0
76
+ #define COL0 6 // screen column of map column 0
77
+
78
+ enum { T_E, T_B, T_S, T_L, T_HOLE, T_FILL };
79
+
80
+ #define HOLE_T 80 // ticks from dig to brick
81
+ #define REFILL_T 16 // refill glyph from this count
82
+ #define T_TRAP 30 // guard ticks spent in a hole
83
+ #define NH 8
84
+ #define NG 4
85
+ #define RESPAWN_X 13
86
+
87
+ static const char level[H][W + 1] = {
88
+ "............................",
89
+ "............................",
90
+ "............................",
91
+ "###########.................",
92
+ "............................",
93
+ "............................",
94
+ "............................",
95
+ "SS#H######################HS",
96
+ "...H......................H.",
97
+ "...H......................H.",
98
+ "...H......................H.",
99
+ "...H......................H.",
100
+ "...H......................H.",
101
+ "...H......................H.",
102
+ "...H......................H.",
103
+ "SSSSSSSSSSSSSSSSSSSSSSSSSSSS",
104
+ };
105
+
106
+ // Autopilot: action, ticks. Z digs left, X digs right, W waits.
107
+ struct Step { char act, n; };
108
+ static const struct Step script[] = {
109
+ {'X', 1}, {'W', 12}, {'R', 16}, {'Z', 1}, {'W', 82},
110
+ {'R', 2}, {'D', 8}, {'L', 10}, {'W', 36},
111
+ };
112
+ #define NSTEPS (sizeof(script) / sizeof(script[0]))
113
+
114
+ // From dig_model.py (see the page): checksum after every 16th tick,
115
+ // after the last tick (168), and after the stress tick.
116
+ static const unsigned exp_check[] = {
117
+ 0xbfa0, 0x2b2a, 0x8f96, 0x94c2, 0x3c57,
118
+ 0x859c, 0x585a, 0xb6b3, 0x1f4b, 0x3dfa,
119
+ };
120
+ #define NCHECK 10
121
+ #define EXP_TICKS 168
122
+ #define EXP_FINAL 0xefbe
123
+ #define EXP_STRESS 0x9760
124
+
125
+ char map[H][W];
126
+ char *rowp[H]; // row pointers: no multiply by 28
127
+ char hx[NH], hy[NH], ht[NH]; // hole list: cell and timer
128
+ char gx[NG], gy[NG], gst[NG], gcnt[NG], gon[NG]; // st 0 free, 1 trapped, 2 escaping
129
+ char px, py, alive;
130
+ char digs, traps, escs, kills, walk;
131
+
132
+ char dirty_x[24], dirty_y[24], ndirty;
133
+
134
+ static inline void set_tile(char x, char y, char t)
135
+ {
136
+ rowp[y][x] = t;
137
+ dirty_x[ndirty] = x;
138
+ dirty_y[ndirty] = y;
139
+ ndirty++;
140
+ }
141
+
142
+ static inline bool blocked(char t)
143
+ {
144
+ return t == T_B || t == T_S;
145
+ }
146
+
147
+ signed char guard_at(char x, char y)
148
+ {
149
+ for (char i = 0; i < NG; i++)
150
+ if (gon[i] && gx[i] == x && gy[i] == y)
151
+ return i;
152
+ return -1;
153
+ }
154
+
155
+ bool trapped_at(char x, char y)
156
+ {
157
+ signed char i = guard_at(x, y);
158
+ return i >= 0 && gst[i] == 1;
159
+ }
160
+
161
+ bool supported(char x, char y)
162
+ {
163
+ if (rowp[y][x] == T_L)
164
+ return true;
165
+ char b = rowp[y + 1][x];
166
+ if (b == T_B || b == T_S || b == T_L)
167
+ return true;
168
+ return (b == T_HOLE || b == T_FILL) && trapped_at(x, y + 1);
169
+ }
170
+
171
+ // --- digging and the hole list -------------------------------------------
172
+ void dig(signed char dx)
173
+ {
174
+ signed char tx = (signed char)px + dx;
175
+ char ty = py + 1;
176
+ if (tx < 0 || tx >= W)
177
+ return;
178
+ if (rowp[ty][tx] != T_B || rowp[py][tx] != T_E || guard_at(tx, py) >= 0)
179
+ return;
180
+ for (char i = 0; i < NH; i++) {
181
+ if (ht[i] == 0) {
182
+ hx[i] = tx; hy[i] = ty; ht[i] = HOLE_T;
183
+ set_tile(tx, ty, T_HOLE);
184
+ digs++;
185
+ return;
186
+ }
187
+ }
188
+ }
189
+
190
+ void holes_update(void)
191
+ {
192
+ for (char i = 0; i < NH; i++) {
193
+ if (ht[i] == 0)
194
+ continue;
195
+ char t = --ht[i];
196
+ char x = hx[i], y = hy[i];
197
+ if (t == REFILL_T) {
198
+ set_tile(x, y, T_FILL);
199
+ } else if (t == 0) {
200
+ set_tile(x, y, T_B);
201
+ for (char g = 0; g < NG; g++) {
202
+ if (gon[g] && gx[g] == x && gy[g] == y) {
203
+ gx[g] = RESPAWN_X; gy[g] = 0; gst[g] = 0; gcnt[g] = 0;
204
+ kills++;
205
+ }
206
+ }
207
+ if (px == x && py == y)
208
+ alive = 0;
209
+ }
210
+ }
211
+ }
212
+
213
+ // --- player --------------------------------------------------------------
214
+ void player_update(char a)
215
+ {
216
+ char x = px, y = py;
217
+ if (!supported(x, y))
218
+ py++;
219
+ else if (a == 'L' && x > 0 && !blocked(rowp[y][x - 1]))
220
+ px--;
221
+ else if (a == 'R' && x < W - 1 && !blocked(rowp[y][x + 1]))
222
+ px++;
223
+ else if (a == 'U' && rowp[y][x] == T_L && !blocked(rowp[y - 1][x]))
224
+ py--;
225
+ else if (a == 'D' && !blocked(rowp[y + 1][x]) && guard_at(x, y + 1) < 0)
226
+ py++;
227
+ else if (a == 'Z')
228
+ dig(-1);
229
+ else if (a == 'X')
230
+ dig(1);
231
+ char b = rowp[py + 1][px];
232
+ if ((b == T_HOLE || b == T_FILL) && trapped_at(px, py + 1))
233
+ walk++;
234
+ }
235
+
236
+ // --- guards --------------------------------------------------------------
237
+ signed char mdx, mdy;
238
+
239
+ // The guard's row and the rows above and below it, fetched once per
240
+ // decision so the scan does no row arithmetic.
241
+ const char *r_up, *r_at, *r_dn;
242
+ char s_y;
243
+
244
+ static inline bool held(signed char c) // can stand at column c
245
+ {
246
+ if (r_at[c] == T_L)
247
+ return true;
248
+ char b = r_dn[c];
249
+ if (b == T_B || b == T_S || b == T_L)
250
+ return true;
251
+ return (b == T_HOLE || b == T_FILL) && trapped_at(c, s_y + 1);
252
+ }
253
+
254
+ // One cell of the row scan: 0 the scan stops here, 1 this column offers
255
+ // a move toward the player's row, 2 walk on past it.
256
+ char probe(signed char c)
257
+ {
258
+ if (c < 0 || c >= W || blocked(r_at[c]))
259
+ return 0;
260
+ if (py < s_y) {
261
+ if (r_at[c] == T_L && !blocked(r_up[c]))
262
+ return 1;
263
+ } else {
264
+ char b = r_dn[c];
265
+ if (b == T_L || b == T_E)
266
+ return 1;
267
+ }
268
+ return held(c) ? 2 : 0;
269
+ }
270
+
271
+ void decide(char g)
272
+ {
273
+ char x = gx[g], y = gy[g];
274
+ s_y = y;
275
+ r_at = rowp[y];
276
+ r_dn = rowp[y + 1];
277
+ r_up = y ? rowp[y - 1] : r_at;
278
+ mdx = 0; mdy = 0;
279
+ if (y == py && x != px) {
280
+ signed char d = px > x ? 1 : -1;
281
+ signed char c = x;
282
+ for (;;) {
283
+ c += d;
284
+ if (blocked(r_at[c]) || !held(c))
285
+ break;
286
+ if (c == px) { mdx = d; return; }
287
+ }
288
+ }
289
+ if (py < y && r_at[x] == T_L && !blocked(r_up[x])) { mdy = -1; return; }
290
+ if (py > y) {
291
+ char b = r_dn[x];
292
+ if (b == T_L || b == T_E) { mdy = 1; return; }
293
+ }
294
+ if (py != y) {
295
+ signed char cl = x, cr = x;
296
+ bool open_l = true, open_r = true;
297
+ while (open_l || open_r) {
298
+ if (open_l) {
299
+ char r = probe(--cl);
300
+ if (r == 1) { mdx = -1; return; }
301
+ if (r == 0) open_l = false;
302
+ }
303
+ if (open_r) {
304
+ char r = probe(++cr);
305
+ if (r == 1) { mdx = 1; return; }
306
+ if (r == 0) open_r = false;
307
+ }
308
+ }
309
+ }
310
+ if (px != x)
311
+ mdx = px > x ? 1 : -1;
312
+ }
313
+
314
+ bool guard_step(char g, signed char dx, signed char dy)
315
+ {
316
+ signed char nx = (signed char)gx[g] + dx;
317
+ char ny = gy[g] + dy;
318
+ if (nx < 0 || nx >= W || blocked(rowp[ny][nx]) || guard_at(nx, ny) >= 0)
319
+ return false;
320
+ gx[g] = nx; gy[g] = ny;
321
+ return true;
322
+ }
323
+
324
+ void guard_update(char g)
325
+ {
326
+ if (!gon[g])
327
+ return;
328
+ if (gst[g] == 1) {
329
+ if (--gcnt[g] == 0) {
330
+ if (guard_step(g, 0, -1)) { gst[g] = 2; escs++; }
331
+ else gcnt[g] = 1;
332
+ }
333
+ } else if (gst[g] == 2) {
334
+ signed char d = px >= gx[g] ? 1 : -1;
335
+ if (!guard_step(g, d, 0))
336
+ guard_step(g, -d, 0);
337
+ gst[g] = 0;
338
+ } else if (!supported(gx[g], gy[g])) {
339
+ gy[g]++;
340
+ char t = rowp[gy[g]][gx[g]];
341
+ if (t == T_HOLE || t == T_FILL) {
342
+ gst[g] = 1; gcnt[g] = T_TRAP;
343
+ traps++;
344
+ }
345
+ } else {
346
+ decide(g);
347
+ if (mdx || mdy)
348
+ guard_step(g, mdx, mdy);
349
+ }
350
+ if (gx[g] == px && gy[g] == py)
351
+ alive = 0;
352
+ }
353
+
354
+ // --- checksum (outside the timed work) --------------------------------------
355
+ unsigned digest(void)
356
+ {
357
+ unsigned v = 0;
358
+ const char *p = &map[0][0];
359
+ for (unsigned i = 0; i < W * H; i++)
360
+ v += p[i];
361
+ for (char g = 0; g < NG; g++)
362
+ v += gx[g] + (unsigned)gy[g] * 32 + (unsigned)gst[g] * 1024 + gcnt[g]
363
+ + (unsigned)gon[g] * 4096;
364
+ v += px + (unsigned)py * 32 + (unsigned)alive * 1024;
365
+ return v;
366
+ }
367
+
368
+ static inline unsigned rol16(unsigned c)
369
+ {
370
+ return (c << 1) | (c >> 15);
371
+ }
372
+
373
+ // --- CIA1 timer B harness ----------------------------------------------------
374
+ static inline void timer_start(void)
375
+ {
376
+ cia1.crb = 0x00;
377
+ cia1.tb = 0xffff;
378
+ cia1.crb = 0x11;
379
+ }
380
+
381
+ static inline unsigned timer_stop(void)
382
+ {
383
+ cia1.crb = 0x00;
384
+ return 0xffff - cia1.tb;
385
+ }
386
+
387
+ // --- drawing ----------------------------------------------------------------
388
+ static const char tile_char[6] = { 0x20, 0x66, 0xa0, 0x08, 0x20, 0x62 };
389
+ static const char tile_col[6] = { VCOL_BLACK, VCOL_RED, VCOL_LT_GREY,
390
+ VCOL_YELLOW, VCOL_BLACK, VCOL_ORANGE };
391
+
392
+ void draw_cell(char x, char y)
393
+ {
394
+ unsigned o = (unsigned)(ROW0 + y) * 40 + COL0 + x;
395
+ char t = rowp[y][x];
396
+ SCREEN[o] = tile_char[t];
397
+ COLOUR[o] = tile_col[t];
398
+ }
399
+
400
+ void draw_actor(char x, char y, char ch, char col)
401
+ {
402
+ unsigned o = (unsigned)(ROW0 + y) * 40 + COL0 + x;
403
+ SCREEN[o] = ch;
404
+ COLOUR[o] = col;
405
+ }
406
+
407
+ void put_str(char *p, const char *s)
408
+ {
409
+ while (*s)
410
+ *p++ = *s++;
411
+ }
412
+
413
+ void put_dec(char *p, unsigned v, char digits)
414
+ {
415
+ for (char i = digits; i > 0; i--) {
416
+ p[i - 1] = 0x30 + v % 10;
417
+ v /= 10;
418
+ }
419
+ }
420
+
421
+ void put_hex(char *p, unsigned v)
422
+ {
423
+ for (char i = 4; i > 0; i--) {
424
+ char n = v & 15;
425
+ p[i - 1] = n < 10 ? 0x30 + n : n - 9;
426
+ v >>= 4;
427
+ }
428
+ }
429
+
430
+ char ogx[NG], ogy[NG], opx, opy;
431
+
432
+ void redraw(void)
433
+ {
434
+ for (char i = 0; i < ndirty; i++)
435
+ draw_cell(dirty_x[i], dirty_y[i]);
436
+ ndirty = 0;
437
+ draw_cell(opx, opy);
438
+ for (char g = 0; g < NG; g++)
439
+ draw_cell(ogx[g], ogy[g]);
440
+ draw_actor(px, py, 0x53, VCOL_CYAN); // heart: the player
441
+ for (char g = 0; g < NG; g++) {
442
+ if (gon[g])
443
+ draw_actor(gx[g], gy[g], 0x51, gst[g] == 1 ? VCOL_PURPLE : VCOL_WHITE);
444
+ ogx[g] = gx[g]; ogy[g] = gy[g];
445
+ }
446
+ opx = px; opy = py;
447
+ }
448
+
449
+ int main(void)
450
+ {
451
+ __asm { sei }
452
+ vic.color_border = VCOL_BLACK;
453
+ vic.color_back = VCOL_BLACK;
454
+ memset(SCREEN, 0x20, 1000);
455
+ memset(COLOUR, VCOL_WHITE, 1000);
456
+
457
+ for (char y = 0; y < H; y++)
458
+ rowp[y] = map[y];
459
+ for (char y = 0; y < H; y++) {
460
+ for (char x = 0; x < W; x++) {
461
+ char c = level[y][x], t = T_E;
462
+ if (c == '#') t = T_B;
463
+ else if (c == 'S') t = T_S;
464
+ else if (c == 'H') t = T_L;
465
+ rowp[y][x] = t;
466
+ draw_cell(x, y);
467
+ }
468
+ }
469
+ gon[0] = 1; gx[0] = 14; gy[0] = 6;
470
+ px = 8; py = 6; alive = 1;
471
+ opx = px; opy = py;
472
+ redraw();
473
+
474
+ put_str(SCREEN, s"t dig trap esc kill walk");
475
+ put_str(SCREEN + 40, s"holes guard tick");
476
+ put_str(SCREEN + 80, s"st sh sg m r");
477
+ put_str(SCREEN + 120, s"cs pl");
478
+
479
+ unsigned cs = 0, tick = 0, t_hole = 0, t_guard = 0, t_tick = 0, t_play = 0;
480
+ char step = 0, left = script[0].n, miss = 0, ci = 0;
481
+
482
+ while (step < NSTEPS) {
483
+ vic_waitFrame();
484
+ char a = script[step].act;
485
+
486
+ unsigned tt = 0;
487
+ timer_start();
488
+ player_update(a);
489
+ tt = timer_stop();
490
+ if (tt > t_play) t_play = tt;
491
+ timer_start();
492
+ holes_update();
493
+ unsigned t = timer_stop();
494
+ tt += t;
495
+ if (t > t_hole) t_hole = t;
496
+ if ((tick & 1) == 0) {
497
+ for (char g = 0; g < NG; g++) {
498
+ timer_start();
499
+ guard_update(g);
500
+ t = timer_stop();
501
+ tt += t;
502
+ if (t > t_guard) t_guard = t;
503
+ }
504
+ }
505
+ if (tt > t_tick) t_tick = tt;
506
+
507
+ cs = rol16(cs) ^ digest();
508
+ tick++;
509
+ if ((tick & 15) == 0) {
510
+ if (ci >= NCHECK || cs != exp_check[ci])
511
+ miss++;
512
+ ci++;
513
+ }
514
+ redraw();
515
+
516
+ put_dec(SCREEN + 2, tick, 3);
517
+ put_dec(SCREEN + 10, digs, 1);
518
+ put_dec(SCREEN + 16, traps, 1);
519
+ put_dec(SCREEN + 22, escs, 1);
520
+ put_dec(SCREEN + 28, kills, 1);
521
+ put_dec(SCREEN + 34, walk, 3);
522
+ put_dec(SCREEN + 40 + 6, t_hole, 5);
523
+ put_dec(SCREEN + 40 + 18, t_guard, 5);
524
+ put_dec(SCREEN + 40 + 29, t_tick, 5);
525
+
526
+ if (--left == 0) {
527
+ step++;
528
+ if (step < NSTEPS)
529
+ left = script[step].n;
530
+ }
531
+ }
532
+
533
+ char fault = 0;
534
+ if (ci != NCHECK || miss) fault = 1;
535
+ else if (tick != EXP_TICKS || cs != EXP_FINAL) fault = 2;
536
+ else if (digs != 2 || traps != 2 || escs != 1 || kills != 1 || !walk || !alive) fault = 3;
537
+
538
+ // Stress tick: the technique's worst frame, built on purpose. One dig
539
+ // a tick and a fixed HOLE_T mean at most one hole expires and one
540
+ // starts refilling per tick. The holes sit on the ledge, off row 7,
541
+ // so they do not cut the guards' scans short.
542
+ static const char st_t[NH] = { 1, 17, 30, 40, 50, 60, 70, 79 };
543
+ for (char i = 0; i < NH; i++) {
544
+ hx[i] = 3 + i; hy[i] = 3; ht[i] = st_t[i];
545
+ set_tile(3 + i, 3, st_t[i] <= REFILL_T ? T_FILL : T_HOLE);
546
+ }
547
+ for (char g = 0; g < NG; g++) {
548
+ gon[g] = 1; gx[g] = 7 + 5 * g; gy[g] = 6; gst[g] = 0; gcnt[g] = 0;
549
+ }
550
+ px = 1; py = 2; // on the ledge; the dig finds the list full
551
+ vic_waitFrame();
552
+ timer_start();
553
+ player_update('X');
554
+ unsigned s_play = timer_stop();
555
+ timer_start();
556
+ holes_update();
557
+ unsigned s_hole = timer_stop(), s_guard = 0, t_stress = s_play + s_hole;
558
+ for (char g = 0; g < NG; g++) {
559
+ timer_start();
560
+ guard_update(g);
561
+ unsigned t = timer_stop();
562
+ t_stress += t;
563
+ if (t > s_guard) s_guard = t;
564
+ }
565
+ cs = rol16(cs) ^ digest();
566
+ if (!fault && cs != EXP_STRESS) fault = 4;
567
+ ndirty = 0; // the screen keeps the scenario's last tick
568
+
569
+ put_dec(SCREEN + 80 + 3, t_stress, 5);
570
+ put_dec(SCREEN + 80 + 12, s_hole, 5);
571
+ put_dec(SCREEN + 80 + 21, s_guard, 5);
572
+ put_dec(SCREEN + 80 + 29, miss, 2);
573
+ put_hex(SCREEN + 120 + 3, cs);
574
+ put_dec(SCREEN + 120 + 12, t_play, 5);
575
+ char code = fault ? CODE_FAIL : CODE_PASS;
576
+ RESULT = code;
577
+ vic.color_border = fault ? VCOL_RED : VCOL_GREEN;
578
+ SCREEN[80 + 34] = 0x30 + code;
579
+ SCREEN[80 + 35] = 0x30 + fault;
580
+ for (;;)
581
+ ;
582
+ return 0;
583
+ }
584
+ ```
585
+
586
+ ## Build
587
+
588
+ ```bash
589
+ oscar64 -tm=c64 -O2 -o=dig-and-guards.prg dig-and-guards.c
590
+ ```
591
+
592
+ Oscar64 1.32.271 builds it with no warnings; the PRG is 4,629 bytes.
593
+
594
+ The checkpoint checksums, the final checksum and the stress checksum
595
+ come from this script (Python 3). It keeps the same map and rules as
596
+ objects and lists, not the C's arrays, and prints `ticks 168 digs 2
597
+ traps 2 escs 1 kills 1 walk 1 alive 1`, the ten checkpoints, `final
598
+ 0xefbe` and `stress 0x9760`.
599
+
600
+ ```python
601
+ # dig_model.py: the reference for dig-and-guards.c. Same map, same rules,
602
+ # same order of work per tick, written from the page's rule list, not
603
+ # from the C. Prints the checkpoint checksums, the event counts and the
604
+ # final positions the listing compiles in.
605
+ W, H = 28, 16
606
+ E, B, S, L, HO, F = 0, 1, 2, 3, 4, 5
607
+ HOLE_T, REFILL_T, T_TRAP, NH, NG = 80, 16, 30, 8, 4
608
+ RESPAWN_X = 13
609
+ LEVEL = [
610
+ "............................",
611
+ "............................",
612
+ "............................",
613
+ "###########.................",
614
+ "............................",
615
+ "............................",
616
+ "............................",
617
+ "SS#H######################HS",
618
+ "...H......................H.",
619
+ "...H......................H.",
620
+ "...H......................H.",
621
+ "...H......................H.",
622
+ "...H......................H.",
623
+ "...H......................H.",
624
+ "...H......................H.",
625
+ "SSSSSSSSSSSSSSSSSSSSSSSSSSSS",
626
+ ]
627
+ SCRIPT = [("X", 1), ("W", 12), ("R", 16), ("Z", 1), ("W", 82),
628
+ ("R", 2), ("D", 8), ("L", 10), ("W", 36)]
629
+ CHECK_EVERY = 16
630
+
631
+ class G:
632
+ def __init__(s, x, y, on):
633
+ s.x, s.y, s.st, s.cnt, s.on = x, y, 0, 0, on
634
+
635
+ def blocked(t):
636
+ return t == B or t == S
637
+
638
+ class World:
639
+ def __init__(s):
640
+ code = {'.': E, '#': B, 'S': S, 'H': L}
641
+ s.m = [[code[c] for c in row] for row in LEVEL]
642
+ s.hx, s.hy, s.ht = [0] * NH, [0] * NH, [0] * NH
643
+ s.g = [G(14, 6, 1), G(0, 0, 0), G(0, 0, 0), G(0, 0, 0)]
644
+ s.px, s.py, s.alive = 8, 6, 1
645
+ s.digs = s.traps = s.escs = s.kills = s.walk = 0
646
+
647
+ def guard_at(s, x, y):
648
+ for i, g in enumerate(s.g):
649
+ if g.on and g.x == x and g.y == y:
650
+ return i
651
+ return -1
652
+
653
+ def trapped_at(s, x, y):
654
+ i = s.guard_at(x, y)
655
+ return i >= 0 and s.g[i].st == 1
656
+
657
+ def supported(s, x, y):
658
+ if s.m[y][x] == L:
659
+ return True
660
+ b = s.m[y + 1][x]
661
+ if b in (B, S, L):
662
+ return True
663
+ return b in (HO, F) and s.trapped_at(x, y + 1)
664
+
665
+ def dig(s, dx):
666
+ tx, ty = s.px + dx, s.py + 1
667
+ if tx < 0 or tx >= W:
668
+ return
669
+ if s.m[ty][tx] != B or s.m[s.py][tx] != E or s.guard_at(tx, s.py) >= 0:
670
+ return
671
+ for i in range(NH):
672
+ if s.ht[i] == 0:
673
+ s.hx[i], s.hy[i], s.ht[i] = tx, ty, HOLE_T
674
+ s.m[ty][tx] = HO
675
+ s.digs += 1
676
+ return
677
+
678
+ def player(s, a):
679
+ x, y = s.px, s.py
680
+ if not s.supported(x, y):
681
+ s.py += 1
682
+ elif a == 'L' and x > 0 and not blocked(s.m[y][x - 1]):
683
+ s.px -= 1
684
+ elif a == 'R' and x < W - 1 and not blocked(s.m[y][x + 1]):
685
+ s.px += 1
686
+ elif a == 'U' and s.m[y][x] == L and not blocked(s.m[y - 1][x]):
687
+ s.py -= 1
688
+ elif a == 'D' and not blocked(s.m[y + 1][x]) and s.guard_at(x, y + 1) < 0:
689
+ s.py += 1
690
+ elif a == 'Z':
691
+ s.dig(-1)
692
+ elif a == 'X':
693
+ s.dig(1)
694
+ b = s.m[s.py + 1][s.px]
695
+ if b in (HO, F) and s.trapped_at(s.px, s.py + 1):
696
+ s.walk += 1
697
+
698
+ def holes(s):
699
+ for i in range(NH):
700
+ if s.ht[i] == 0:
701
+ continue
702
+ s.ht[i] -= 1
703
+ x, y = s.hx[i], s.hy[i]
704
+ if s.ht[i] == REFILL_T:
705
+ s.m[y][x] = F
706
+ elif s.ht[i] == 0:
707
+ s.m[y][x] = B
708
+ for g in s.g:
709
+ if g.on and g.x == x and g.y == y:
710
+ g.x, g.y, g.st, g.cnt = RESPAWN_X, 0, 0, 0
711
+ s.kills += 1
712
+ if s.px == x and s.py == y:
713
+ s.alive = 0
714
+
715
+ def decide(s, g):
716
+ x, y, m = g.x, g.y, s.m
717
+ if y == s.py and x != s.px:
718
+ d = 1 if s.px > x else -1
719
+ c = x
720
+ while True:
721
+ c += d
722
+ if blocked(m[y][c]) or not s.supported(c, y):
723
+ break
724
+ if c == s.px:
725
+ return d, 0
726
+ if s.py < y and m[y][x] == L and not blocked(m[y - 1][x]):
727
+ return 0, -1
728
+ if s.py > y and m[y + 1][x] in (L, E):
729
+ return 0, 1
730
+ if s.py != y:
731
+ open_l = open_r = True
732
+ for dist in range(1, W):
733
+ for d in (-1, 1):
734
+ if (open_l if d < 0 else open_r) is False:
735
+ continue
736
+ c = x + d * dist
737
+ if c < 0 or c >= W or blocked(m[y][c]):
738
+ if d < 0: open_l = False
739
+ else: open_r = False
740
+ continue
741
+ if s.py < y and m[y][c] == L and not blocked(m[y - 1][c]):
742
+ return d, 0
743
+ if s.py > y and m[y + 1][c] in (L, E):
744
+ return d, 0
745
+ if not s.supported(c, y):
746
+ if d < 0: open_l = False
747
+ else: open_r = False
748
+ if s.px != x:
749
+ return (1 if s.px > x else -1), 0
750
+ return 0, 0
751
+
752
+ def step(s, g, dx, dy):
753
+ nx, ny = g.x + dx, g.y + dy
754
+ if nx < 0 or nx >= W or blocked(s.m[ny][nx]) or s.guard_at(nx, ny) >= 0:
755
+ return False
756
+ g.x, g.y = nx, ny
757
+ return True
758
+
759
+ def guard(s, g):
760
+ if not g.on:
761
+ return
762
+ if g.st == 1:
763
+ g.cnt -= 1
764
+ if g.cnt == 0:
765
+ if s.step(g, 0, -1):
766
+ g.st = 2
767
+ s.escs += 1
768
+ else:
769
+ g.cnt = 1
770
+ elif g.st == 2:
771
+ d = 1 if s.px >= g.x else -1
772
+ if not s.step(g, d, 0):
773
+ s.step(g, -d, 0)
774
+ g.st = 0
775
+ elif not s.supported(g.x, g.y):
776
+ g.y += 1
777
+ if s.m[g.y][g.x] in (HO, F):
778
+ g.st, g.cnt = 1, T_TRAP
779
+ s.traps += 1
780
+ else:
781
+ dx, dy = s.decide(g)
782
+ if dx or dy:
783
+ s.step(g, dx, dy)
784
+ if g.x == s.px and g.y == s.py:
785
+ s.alive = 0
786
+
787
+ def digest(s):
788
+ v = sum(sum(r) for r in s.m)
789
+ for g in s.g:
790
+ v += g.x + g.y * 32 + g.st * 1024 + g.cnt + g.on * 4096
791
+ v += s.px + s.py * 32 + s.alive * 1024
792
+ return v & 0xffff
793
+
794
+ def rol(c):
795
+ return ((c << 1) | (c >> 15)) & 0xffff
796
+
797
+ def run():
798
+ w, cs, tick, checks = World(), 0, 0, []
799
+ for a, n in SCRIPT:
800
+ for _ in range(n):
801
+ w.player(a)
802
+ w.holes()
803
+ if tick & 1 == 0:
804
+ for g in w.g:
805
+ w.guard(g)
806
+ cs = rol(cs) ^ w.digest()
807
+ tick += 1
808
+ if tick % CHECK_EVERY == 0:
809
+ checks.append(cs)
810
+ return w, cs, tick, checks
811
+
812
+ def stress(w, cs):
813
+ # Worst frame: four free guards on row 6 each scan the whole row (the
814
+ # player is above and no column offers a climb); eight live holes on
815
+ # the ledge, off the guards' row, one expiring and one turning to the
816
+ # refill glyph; the player, on the ledge, tries a dig with the list full.
817
+ for i, t in enumerate((1, 17, 30, 40, 50, 60, 70, 79)):
818
+ x, y = 3 + i, 3
819
+ w.m[y][x] = F if t <= REFILL_T else HO
820
+ w.hx[i], w.hy[i], w.ht[i] = x, y, t
821
+ for i, gx in enumerate((7, 12, 17, 22)):
822
+ g = w.g[i]
823
+ g.on, g.x, g.y, g.st, g.cnt = 1, gx, 6, 0, 0
824
+ w.px, w.py = 1, 2
825
+ w.player('X')
826
+ w.holes()
827
+ for g in w.g:
828
+ w.guard(g)
829
+ return rol(cs) ^ w.digest()
830
+
831
+ if __name__ == "__main__":
832
+ w, cs, tick, checks = run()
833
+ print("ticks", tick, "digs", w.digs, "traps", w.traps, "escs", w.escs,
834
+ "kills", w.kills, "walk", w.walk, "alive", w.alive)
835
+ print("player", w.px, w.py, "guard", w.g[0].x, w.g[0].y, w.g[0].st)
836
+ print("checks", ", ".join("0x%04x" % c for c in checks))
837
+ print("final 0x%04x" % cs)
838
+ print("stress 0x%04x" % stress(w, cs))
839
+ ```
840
+
841
+ ## Expected output
842
+
843
+ Black background, green border. Rows 0 to 3 are the HUD. Below them the
844
+ level: an eleven-cell red brick ledge at the top left (used only by the
845
+ stress tick), a red brick floor with grey solid ends, two yellow `H`
846
+ ladders at map columns 3 and 26, and a grey solid bottom. The player, a
847
+ cyan heart, stands on the bottom row at map column 16; the guard, a white
848
+ ball, is on the same row at column 9, walking toward it. Both holes have
849
+ refilled, so the brick floor is whole again. The stress tick is not
850
+ drawn.
851
+
852
+ HUD at 16,000,000 cycles (measured in VICE x64sc 3.10, decoded against
853
+ the character ROM with PIL):
854
+
855
+ | Row | PAL | NTSC |
856
+ |---|---|---|
857
+ | 0 | `T 168 DIG 2 TRAP2 ESC 1 KILL1 WALK001` | same |
858
+ | 1 | `HOLES 00366 GUARD 03516 TICK 04075` | `HOLES 00366 GUARD 03559 TICK 04118` |
859
+ | 2 | `ST 19759 SH 00659 SG 04780 M 00 R 10` | `ST 19716 SH 00659 SG 04761 M 00 R 10` |
860
+ | 3 | `CS 9760 PL 00505` | same |
861
+
862
+ Row 0: 168 ticks, two digs, two traps, one escape, one kill, one tick
863
+ with the player held up by a trapped guard. Row 1, worst over the
864
+ scenario in CIA1 cycles: the hole-list update, one guard's update, and a
865
+ whole tick (player, holes and every guard). Row 2: the stress tick
866
+ (`ST`), its hole-list update (`SH`) and its slowest guard (`SG`), then
867
+ checkpoint misses (`M 00`) and the result, code 1 and fault 0. Row 3:
868
+ the final checksum, equal to the model's stress value `0x9760`, and the
869
+ worst player update (`PL`, a dig). The border is (98, 213, 50) on PAL
870
+ and (114, 189, 103) on NTSC.
871
+
872
+ A copy of the listing with one checkpoint constant changed by one bit
873
+ printed `M 01 R 21` and a red border (175, 60, 88) on PAL, so the
874
+ comparison can fail.
875
+
876
+ The two models differ only where the timed work reaches the visible
877
+ screen. `vic_waitFrame()` returns when the raster reaches line 256, so
878
+ the work starts in the vertical blank: about 104 lines, 6,552 cycles,
879
+ before the first badline on PAL and about 55 lines, 3,575 cycles, on
880
+ NTSC (arithmetic from 63 and 65 cycles a line; rung 3). The 4,075-cycle
881
+ tick fits the PAL gap and not the NTSC one. The stress tick is longer
882
+ than a whole frame on both models, so each run crosses every badline
883
+ once and the two figures end up 43 cycles apart; the CIA counts the
884
+ stolen cycles.
885
+
886
+ Screenshots at 16,000,000 cycles, both after the verdict and each
887
+ identical over two runs: `screenshots/dig-and-guards.png` (PAL) and
888
+ `screenshots/dig-and-guards-ntsc.png` (NTSC). Measured on both with PIL:
889
+ every one of the 448 map cells matches the model's last tick, glyph from
890
+ the character ROM and ink from the palette, with 0 wrong pixels. The
891
+ script:
892
+
893
+ ```python
894
+ # Every map cell on the exit screenshot against the model's last tick:
895
+ # glyph from the character ROM, ink colour from the palette per model.
896
+ from PIL import Image
897
+ import dig_model
898
+ ROM = open('/opt/homebrew/opt/vice/share/vice/C64/chargen-901225-01.bin', 'rb').read()[:2048]
899
+ PAL = {0: (0, 0, 0), 1: (255, 255, 255), 2: (175, 60, 88), 3: (126, 243, 214),
900
+ 4: (170, 64, 245), 7: (255, 255, 70), 8: (183, 99, 30), 15: (205, 205, 205)}
901
+ NTSC = {0: (0, 0, 0), 1: (255, 255, 255), 2: (169, 71, 100), 3: (138, 230, 203),
902
+ 4: (154, 88, 185), 7: (255, 248, 141), 8: (196, 98, 65), 15: (205, 205, 205)}
903
+ GLYPH = {0: 0x20, 1: 0x66, 2: 0xa0, 3: 0x08, 4: 0x20, 5: 0x62}
904
+ INK = {0: 0, 1: 2, 2: 15, 3: 7, 4: 0, 5: 8}
905
+ def check(png, ntsc):
906
+ px = Image.open(png).convert('RGB').load()
907
+ top, pal = (23, NTSC) if ntsc else (35, PAL)
908
+ w, cs, tick, _ = dig_model.run()
909
+ exp = {}
910
+ for y in range(16):
911
+ for x in range(28):
912
+ t = w.m[y][x]
913
+ exp[(x, y)] = (GLYPH[t], INK[t])
914
+ exp[(w.px, w.py)] = (0x53, 3)
915
+ for g in w.g:
916
+ if g.on:
917
+ exp[(g.x, g.y)] = (0x51, 4 if g.st == 1 else 1)
918
+ bad = 0
919
+ for (x, y), (ch, ink) in exp.items():
920
+ X, Y = 32 + 8 * (6 + x), top + 8 * (5 + y)
921
+ want = ROM[(ch & 0x7f) * 8:(ch & 0x7f) * 8 + 8]
922
+ for j in range(8):
923
+ for i in range(8):
924
+ on = (want[j] >> (7 - i)) & 1
925
+ if ch & 0x80: on ^= 1
926
+ c = px[X + i, Y + j]
927
+ if c != (pal[ink] if on else (0, 0, 0)):
928
+ bad += 1
929
+ return bad, len(exp)
930
+ for png, n in (('dig-and-guards.png', 0), ('dig-and-guards-ntsc.png', 1)):
931
+ print(png, 'wrong pixels %d over %d cells' % check(png, n))
932
+ ```
933
+
934
+ ## Why this works
935
+
936
+ The hole list is the whole refill mechanism. A dig writes the hole into
937
+ the map and puts its cell and a timer into a free slot of eight; one
938
+ loop per tick counts every live slot down, swaps the glyph at 16 and
939
+ writes brick back at 0. At 0 it asks which actor stands in the cell: a
940
+ guard there is killed and moved to the top row, the player there dies.
941
+ Nothing else in the program tracks holes, so the map cell and the slot
942
+ can never disagree. The player digs at most once a tick and every hole
943
+ lives 80 ticks, so at most one slot expires and one turns to the refill
944
+ glyph in any tick. Eight live slots with one of each cost 659 cycles in
945
+ the stress tick; two live holes cost at most 366 in the scenario.
946
+
947
+ Standing is one function for everyone. `supported()` says an actor at a
948
+ cell is held up when the cell is a ladder, when the cell below is
949
+ brick, solid or ladder, or when the cell below is a hole holding a
950
+ trapped guard. The last clause is the walk-over rule, and it also keeps
951
+ a second guard from falling into an occupied hole. A guard that is not
952
+ held up falls one cell a guard tick; landing in a hole traps it for 30
953
+ guard ticks, after which it climbs one cell and steps sideways on the
954
+ next guard tick without the fall test, so it does not drop straight
955
+ back in.
956
+
957
+ The guard's rule is greedy and costs a row scan at most. On the player's
958
+ row it walks straight at the player if every cell between can be stood
959
+ on. On a ladder with the player above it climbs; with a ladder or empty
960
+ cell below and the player below it descends. Otherwise it scans its row
961
+ outward, left before right at each distance, for the nearest column
962
+ that offers a move toward the player's row, and stops a side at a wall,
963
+ a map edge or a cell it would fall from. Failing all of that it steps
964
+ toward the player's column. The stress tick puts four free guards on a
965
+ row with no such column and the player above, so each scans all 27
966
+ other columns: 4,780 cycles for the slowest on PAL, about 165 cycles a
967
+ probe (arithmetic over 29 probes). That is the cost of an Oscar64 `-O2`
968
+ function call per cell, not of the rule. With eight live holes and the
969
+ failed dig, the tick takes 19,759 cycles on PAL and 19,716 on NTSC:
970
+ 101% of a PAL frame (19,656 cycles) and 115% of an NTSC one (17,095).
971
+ Four guards scanning full rows overrun a frame in this C. A copy of the
972
+ listing with the fourth guard switched off took 15,085 cycles on PAL
973
+ (77%) and 15,299 on NTSC (89%), so three fit. More guards need their
974
+ decisions staggered over frames; the technique page lists that and
975
+ other ways to bound the scan.