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