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,874 @@
1
+ ---
2
+ recipe: falling-blocks
3
+ toolchain: oscar64
4
+ output_format: PRG
5
+ region: both
6
+ techniques: [falling_block_rules, joystick_edge_detect, joystick_autorepeat, lfsr_random, text_mode_overlay_render, frame_sync_loop, pal_ntsc_detection]
7
+ file_formats: [PRG]
8
+ uses_registers: [D011, D012, D020, D021, DC00, DC04, DC05, DC0E]
9
+ uses_kernal: []
10
+ scaffolds: [action_puzzle]
11
+ ---
12
+
13
+ <!-- doc-type: recipe -->
14
+
15
+ # Oscar64 Falling Blocks: a playable game with its rules checked against a model
16
+
17
+ ## Synopsis
18
+
19
+ A falling-block game in character mode, written in C: seven pieces,
20
+ clockwise rotation with a three-try kick, gravity and delayed auto-shift
21
+ from the NES Tetris tables for the region the program finds, soft drop,
22
+ lock on landing, line clear with collapse, scoring by lines at once, a
23
+ level every 10 lines, a 7-bag randomiser on a 16-bit LFSR, and game over
24
+ when a piece cannot spawn. The rules are `falling_block_rules` in
25
+ `techniques/logic.md`. The default build (`AUTOPILOT=1`) starts from a
26
+ fixed garbage board and plays five scripted pieces through the same
27
+ input path a joystick uses: a single, a double, a triple, a four-line
28
+ clear that takes the level to 1, DAS moves to both walls, and a J that
29
+ kicks one cell left when rotated against the right wall. It then checks
30
+ the board, score, lines, level, clear counts, kick count, ten bags and a
31
+ blocked spawn against values from the Python model below, checks every
32
+ well cell on screen against the board, runs a probe that moves a piece
33
+ on the frame it locks, prints PASS or FAIL, and prints the cycles of its
34
+ dearest frames and of a worst-frame subject, rules and render timed
35
+ apart. `-dAUTOPILOT=0` builds the game for a player: joystick in port
36
+ 2, fire rotates, down soft-drops, fire starts and restarts. Use it as
37
+ the starting point for any Tetris-like game; the autopilot pattern is
38
+ `headless-verify.md`.
39
+
40
+ ## Source
41
+
42
+ ```c
43
+ // falling-blocks.c
44
+ // A falling-block game in character mode: seven pieces, a clockwise
45
+ // rotation with a three-try kick, gravity from a frames-per-row table
46
+ // chosen by region, delayed auto-shift, soft drop, lock on landing, line
47
+ // clear and collapse, scoring by lines at once, a level every 10 lines,
48
+ // and a 7-bag randomiser on a 16-bit LFSR.
49
+ // AUTOPILOT=1 (default) starts from a fixed garbage board, plays five
50
+ // scripted pieces through the same input path a joystick uses, and
51
+ // checks the board, score, lines, level, clears, kick, bag and spawn
52
+ // test against a Python model, and the screen against the board.
53
+ // AUTOPILOT=0 is the game: joystick port 2, fire rotates, down
54
+ // soft-drops, fire starts and restarts.
55
+ // Every game frame is timed with CIA1 timer A (KERNAL IRQ off).
56
+ #include <c64/vic.h>
57
+ #include <c64/cia.h>
58
+
59
+ #ifndef AUTOPILOT
60
+ #define AUTOPILOT 1
61
+ #endif
62
+
63
+ #define W 10
64
+ #define H 20
65
+ #define BX 15 // board's left column on screen
66
+ #define BY 2 // board's top row on screen
67
+ #define SCR ((char *)0x0400)
68
+ #define CRAM ((char *)0xd800)
69
+ #define RESULT (*(volatile char *)0x02ff)
70
+
71
+ #define J_DOWN 0x02
72
+ #define J_LEFT 0x04
73
+ #define J_RIGHT 0x08
74
+ #define J_FIRE 0x10
75
+
76
+ // --- rules data ---------------------------------------------------------
77
+ // Cells of each piece in a 4x4 box, clockwise rotations 0-3.
78
+ // Order I O T S Z J L; I, S, Z repeat their two states, O its one.
79
+ static const char cx[7][4][4] = {
80
+ {{0,1,2,3},{2,2,2,2},{0,1,2,3},{2,2,2,2}},
81
+ {{1,2,1,2},{1,2,1,2},{1,2,1,2},{1,2,1,2}},
82
+ {{0,1,2,1},{1,0,1,1},{1,0,1,2},{1,1,2,1}},
83
+ {{1,2,0,1},{1,1,2,2},{1,2,0,1},{1,1,2,2}},
84
+ {{0,1,1,2},{2,1,2,1},{0,1,1,2},{2,1,2,1}},
85
+ {{0,1,2,2},{1,1,0,1},{0,0,1,2},{1,2,1,1}},
86
+ {{0,1,2,0},{0,1,1,1},{2,0,1,2},{1,1,1,2}}
87
+ };
88
+ static const char cy[7][4][4] = {
89
+ {{1,1,1,1},{0,1,2,3},{1,1,1,1},{0,1,2,3}},
90
+ {{0,0,1,1},{0,0,1,1},{0,0,1,1},{0,0,1,1}},
91
+ {{1,1,1,2},{0,1,1,2},{0,1,1,1},{0,1,1,2}},
92
+ {{1,1,2,2},{0,1,1,2},{1,1,2,2},{0,1,1,2}},
93
+ {{1,1,2,2},{0,1,1,2},{1,1,2,2},{0,1,1,2}},
94
+ {{1,1,1,2},{0,1,2,2},{0,1,1,1},{0,0,1,2}},
95
+ {{1,1,1,2},{0,0,1,2},{0,1,1,1},{0,1,2,2}}
96
+ };
97
+ static const signed char kick[3] = { 0, 1, -1 };
98
+ static const unsigned points[4] = { 40, 100, 300, 1200 };
99
+ // NES Tetris frames per row by level, 0-29 (tetris.wiki); 29+ holds.
100
+ static const char grav_ntsc[30] = { 48,43,38,33,28,23,18,13,8,6, 5,5,5,4,4,4,3,3,3,
101
+ 2,2,2,2,2,2,2,2,2,2, 1 };
102
+ static const char grav_pal[30] = { 36,32,29,25,22,18,15,11,7,5, 4,4,4,3,3,3,2,2,2,
103
+ 1,1,1,1,1,1,1,1,1,1, 1 };
104
+ static const char colours[9] = { 0, 3, 7, 4, 5, 2, 14, 8, 11 };
105
+
106
+ // --- state --------------------------------------------------------------
107
+ static char board[H * W]; // 0 empty, 1-7 piece, 8 garbage
108
+ static char rowfill[H]; // filled cells per row
109
+ static char rowoff[H]; // y * 10
110
+ static unsigned scroff[H]; // screen offset of board row y
111
+ static char piece, rot, nextp;
112
+ static signed char px, py, ox, oy;
113
+ static char orot;
114
+ static char fall, das_l, das_r, prev_joy = 0xff;
115
+ static char level, lines, next_at, pal;
116
+ static const char *grav;
117
+ static char das_delay, das_rate;
118
+ static unsigned long score;
119
+ static char clears[4], kicks, dirty;
120
+ static char ev_lines; // lines cleared this frame, 0 if none
121
+ static char locked; // a piece locked this frame
122
+ static char lk_p, lk_r; // where it locked
123
+ static signed char lk_x, lk_y;
124
+ static char top; // highest row holding a cell, H if none
125
+ static char redraw_a, redraw_b; // board rows to redraw when dirty
126
+
127
+ // --- 7-bag on a 16-bit Galois LFSR, taps $B400 ------------------------
128
+ static unsigned lfsr = 0xace1;
129
+ static char bag[7], bag_n;
130
+
131
+ static unsigned rnd(void)
132
+ {
133
+ char lsb = lfsr & 1;
134
+ lfsr >>= 1;
135
+ if (lsb) lfsr ^= 0xb400;
136
+ return lfsr;
137
+ }
138
+
139
+ static char bag_next(void)
140
+ {
141
+ if (bag_n == 0) {
142
+ for (char i = 0; i < 7; i++) bag[i] = i;
143
+ for (char i = 6; i > 0; i--) { // Fisher-Yates
144
+ char j = rnd() % (i + 1);
145
+ char t = bag[i]; bag[i] = bag[j]; bag[j] = t;
146
+ }
147
+ bag_n = 7;
148
+ }
149
+ return bag[7 - bag_n--];
150
+ }
151
+
152
+ static void draw_piece(char p, char r, signed char x, signed char y, char v);
153
+
154
+ // --- the rules ----------------------------------------------------------
155
+ static char fits(char p, char r, signed char x, signed char y)
156
+ {
157
+ const char *xs = cx[p][r], *ys = cy[p][r];
158
+ for (char i = 0; i < 4; i++) {
159
+ char bx = (char)(x + xs[i]), by = (char)(y + ys[i]);
160
+ if (bx >= W || by >= H) return 0; // unsigned: catches < 0
161
+ if (board[rowoff[by] + bx]) return 0;
162
+ }
163
+ return 1;
164
+ }
165
+
166
+ static char spawn(char p)
167
+ {
168
+ piece = p; rot = 0; px = 3; py = 0; fall = 0;
169
+ return fits(piece, rot, px, py);
170
+ }
171
+
172
+ static void rotate(void)
173
+ {
174
+ char nr = (rot + 1) & 3;
175
+ for (char k = 0; k < 3; k++)
176
+ if (fits(piece, nr, px + kick[k], py)) {
177
+ rot = nr; px += kick[k];
178
+ if (k) kicks++;
179
+ return;
180
+ }
181
+ }
182
+
183
+ static void clear_lines(void)
184
+ {
185
+ char n = 0;
186
+ signed char low = -1;
187
+ for (char i = 0; i < 4; i++) { // only the rows the piece touched
188
+ char y = py + cy[piece][rot][i];
189
+ if (rowfill[y] == W) {
190
+ if ((signed char)y > low) low = y;
191
+ }
192
+ }
193
+ if (low < 0) return;
194
+ signed char dst = low, t = top; // rows above top are empty
195
+ for (signed char src = low; src >= t; src--) { // collapse, lowest full row up
196
+ if (rowfill[src] == W) { n++; continue; }
197
+ if (dst != src) {
198
+ char *d = board + rowoff[dst], *s = board + rowoff[src];
199
+ #pragma unroll(full)
200
+ for (char x = 0; x < W; x++) d[x] = s[x];
201
+ rowfill[dst] = rowfill[src];
202
+ }
203
+ dst--;
204
+ }
205
+ for (; dst >= t; dst--) { // the n rows vacated at the top
206
+ char *d = board + rowoff[dst];
207
+ #pragma unroll(full)
208
+ for (char x = 0; x < W; x++) d[x] = 0;
209
+ rowfill[dst] = 0;
210
+ }
211
+ redraw_a = top; redraw_b = low; // only these rows changed
212
+ top += n;
213
+ clears[n - 1]++;
214
+ lines += n;
215
+ if (lines >= next_at) { level++; next_at += 10; }
216
+ for (char l = 0; l <= level; l++) score += points[n - 1]; // level after the clear, as NES
217
+ ev_lines = n;
218
+ dirty = 1;
219
+ }
220
+
221
+ static void lock_piece(void)
222
+ {
223
+ for (char i = 0; i < 4; i++) {
224
+ char y = py + cy[piece][rot][i];
225
+ char x = px + cx[piece][rot][i]; // in a char first: px can be -2
226
+ board[rowoff[y] + x] = piece + 1;
227
+ rowfill[y]++;
228
+ if (y < top) top = y;
229
+ }
230
+ lk_p = piece; lk_r = rot; lk_x = px; lk_y = py; // for render: spawn moves piece
231
+ clear_lines();
232
+ locked = 1;
233
+ }
234
+
235
+ // DAS: first step on the press, the second DELAY frames later, then every RATE.
236
+ static char das(char *age, char held)
237
+ {
238
+ if (!held) { *age = 0; return 0; }
239
+ char a = *age + 1;
240
+ if (a == das_delay + 1 + das_rate) a = das_delay + 1;
241
+ *age = a;
242
+ return a == 1 || a == das_delay + 1;
243
+ }
244
+
245
+ // One game frame. joy is active low, as $DC00 reads it.
246
+ static void game_step(char joy)
247
+ {
248
+ char pressed = ~joy;
249
+ ev_lines = 0; locked = 0;
250
+ if ((pressed & J_FIRE) && (prev_joy & J_FIRE)) rotate();
251
+ prev_joy = joy;
252
+ if (das(&das_l, pressed & J_LEFT) && fits(piece, rot, px - 1, py)) px--;
253
+ if (das(&das_r, pressed & J_RIGHT) && fits(piece, rot, px + 1, py)) px++;
254
+ char lim = grav[level < 29 ? level : 29];
255
+ if ((pressed & J_DOWN) && lim > 2) lim = 2; // soft drop, 1/2 G
256
+ if (++fall >= lim) {
257
+ fall = 0;
258
+ if (fits(piece, rot, px, py + 1)) py++;
259
+ else lock_piece(); // no lock delay
260
+ }
261
+ }
262
+
263
+ // --- drawing ------------------------------------------------------------
264
+ static void cell(char x, char y, char v)
265
+ {
266
+ unsigned o = scroff[y] + x;
267
+ SCR[o] = v ? 160 : 32;
268
+ CRAM[o] = colours[v];
269
+ }
270
+
271
+ static void draw_piece(char p, char r, signed char x, signed char y, char v)
272
+ {
273
+ for (char i = 0; i < 4; i++)
274
+ cell(x + cx[p][r][i], y + cy[p][r][i], v);
275
+ }
276
+
277
+ static const char glyph[9] = { 32, 160, 160, 160, 160, 160, 160, 160, 160 };
278
+
279
+ static void redraw_rows(char a, char b)
280
+ {
281
+ for (char y = a; y <= b; y++) {
282
+ char *s = SCR + scroff[y], *c = CRAM + scroff[y];
283
+ const char *bp = board + rowoff[y];
284
+ #pragma unroll(full)
285
+ for (char x = 0; x < W; x++) {
286
+ char v = bp[x];
287
+ s[x] = glyph[v];
288
+ c[x] = colours[v];
289
+ }
290
+ }
291
+ }
292
+
293
+ // Erase the piece where it was drawn and draw it where it is. On a lock
294
+ // frame that is the locked piece, which may have moved or rotated in the
295
+ // same frame; then redraw the rows a clear changed, then the new piece.
296
+ static void render(void)
297
+ {
298
+ if (locked) {
299
+ draw_piece(lk_p, orot, ox, oy, 0);
300
+ draw_piece(lk_p, lk_r, lk_x, lk_y, lk_p + 1);
301
+ } else draw_piece(piece, orot, ox, oy, 0);
302
+ if (dirty) { redraw_rows(redraw_a, redraw_b); dirty = 0; }
303
+ draw_piece(piece, rot, px, py, piece + 1);
304
+ ox = px; oy = py; orot = rot;
305
+ }
306
+
307
+ // --- text, timing, frame --------------------------------------------------
308
+ static void put_text(char col, char row, const char *s)
309
+ {
310
+ char *d = SCR + 40 * row + col;
311
+ char *c = CRAM + 40 * row + col;
312
+ while (*s) {
313
+ char ch = *s++;
314
+ *d++ = (ch >= 'A' && ch <= 'Z') ? ch - 64 : ch;
315
+ *c++ = 1;
316
+ }
317
+ }
318
+
319
+ static void put_num(char col, char row, unsigned long v, char digits)
320
+ {
321
+ char *d = SCR + 40 * row + col + digits;
322
+ char *c = CRAM + 40 * row + col;
323
+ for (char i = 0; i < digits; i++) { *--d = '0' + (char)(v % 10); v /= 10; c[i] = 1; }
324
+ }
325
+
326
+ static void put_hex(char col, char row, unsigned v)
327
+ {
328
+ static const char hx[] = "0123456789ABCDEF";
329
+ char s[5];
330
+ for (char i = 0; i < 4; i++) { s[3 - i] = hx[v & 15]; v >>= 4; }
331
+ s[4] = 0;
332
+ put_text(col, row, s);
333
+ }
334
+
335
+ static void t_start(void) { cia1.cra = 0x00; cia1.ta = 0xffff; cia1.cra = 0x11; }
336
+ static unsigned t_stop(void) { cia1.cra = 0x00; return 0xffff - cia1.ta; }
337
+
338
+ static void wait_frame(void)
339
+ {
340
+ while (vic.raster == 250) ;
341
+ while (vic.raster != 250) ;
342
+ }
343
+
344
+ static char detect_pal(void) // does raster line 280 exist?
345
+ {
346
+ for (unsigned n = 0; n < 20000; n++)
347
+ if ((vic.ctrl1 & 0x80) && vic.raster == 280 - 256) return 1;
348
+ return 0;
349
+ }
350
+
351
+ static void hud(void)
352
+ {
353
+ put_text(27, 3, "SCORE"); put_num(27, 4, score, 6);
354
+ put_text(27, 6, "LINES"); put_num(27, 7, lines, 3);
355
+ put_text(27, 9, "LEVEL"); put_num(27, 10, level, 2);
356
+ }
357
+
358
+ static void setup_screen(void)
359
+ {
360
+ vic.color_border = 12; vic.color_back = 0;
361
+ for (unsigned i = 0; i < 1000; i++) { SCR[i] = 32; CRAM[i] = 1; }
362
+ for (char y = 0; y < H; y++) {
363
+ rowoff[y] = y * W;
364
+ scroff[y] = (BY + y) * 40 + BX;
365
+ SCR[scroff[y] - 1] = 160; CRAM[scroff[y] - 1] = 12; // walls
366
+ SCR[scroff[y] + W] = 160; CRAM[scroff[y] + W] = 12;
367
+ }
368
+ for (char x = 0; x < W + 2; x++) {
369
+ SCR[(BY + H) * 40 + BX - 1 + x] = 160; CRAM[(BY + H) * 40 + BX - 1 + x] = 12;
370
+ }
371
+ }
372
+
373
+ static void new_game(void)
374
+ {
375
+ for (char i = 0; i < H * W; i++) board[i] = 0;
376
+ for (char y = 0; y < H; y++) rowfill[y] = 0;
377
+ score = 0; lines = 0; level = 0; next_at = 10; kicks = 0;
378
+ for (char i = 0; i < 4; i++) clears[i] = 0;
379
+ grav = pal ? grav_pal : grav_ntsc;
380
+ das_delay = pal ? 12 : 16; // NES PAL / NTSC (tetris.wiki)
381
+ das_rate = pal ? 4 : 6;
382
+ das_l = das_r = 0; bag_n = 0; dirty = 0; top = H;
383
+ }
384
+
385
+ // --- autopilot: a planner that presses the same bits a player would ------
386
+ #if AUTOPILOT
387
+ struct Plan { char p, r1; signed char x; char r2; };
388
+ static const struct Plan plan[5] = {
389
+ { 0, 0, 6, 0 }, // I flat, three right: single
390
+ { 1, 0, 1, 0 }, // O, two left: double
391
+ { 0, 1, -2, 1 }, // I upright, five left to the wall: triple
392
+ { 0, 1, 7, 1 }, // I upright, four right into the well: four lines, level 1
393
+ { 5, 1, 8, 2 }, // J, five right to the wall, rotate: kicked one left
394
+ };
395
+ static const char *const garbage[11] = { // rows 9-19
396
+ "######....", "##..######", "##..######", ".#########", ".#########",
397
+ ".#########", "#########.", "#########.", "#########.", "#########.",
398
+ "#########."
399
+ };
400
+ #define EXPECT_BOARD 0xecb5 // from the Python model on the page
401
+ #define EXPECT_SCORE 2840
402
+ #define EXPECT_BAG 0x808a
403
+ static char ap_n, ap_phase, ap_tog;
404
+
405
+ static char autopilot(void)
406
+ {
407
+ const struct Plan *p = plan + ap_n;
408
+ char out = 0xff;
409
+ if (ap_phase == 0 || ap_phase == 2) {
410
+ char want = ap_phase ? p->r2 : p->r1;
411
+ if (rot != want) { ap_tog ^= 1; if (ap_tog) out &= ~J_FIRE; }
412
+ else { ap_phase++; ap_tog = 0; }
413
+ }
414
+ if (ap_phase == 1) {
415
+ if (px < p->x) out &= ~J_RIGHT;
416
+ else if (px > p->x) out &= ~J_LEFT;
417
+ else ap_phase = 2;
418
+ }
419
+ if (ap_phase == 3) out &= ~J_DOWN;
420
+ return out;
421
+ }
422
+
423
+ static void load_garbage(void)
424
+ {
425
+ for (char r = 0; r < 11; r++)
426
+ for (char x = 0; x < W; x++)
427
+ if (garbage[r][x] == '#') { board[rowoff[9 + r] + x] = 8; rowfill[9 + r]++; }
428
+ top = 9;
429
+ }
430
+
431
+ static unsigned fold(unsigned c, char v) { return (c ^ v) * 5 + 1; }
432
+
433
+ // Well cells on screen that differ from the board, glyph or colour, with
434
+ // the live piece written into the board for the comparison when live.
435
+ static char screen_bad(char live)
436
+ {
437
+ char bad = 0;
438
+ if (live) for (char i = 0; i < 4; i++) {
439
+ char y = py + cy[piece][rot][i], x = px + cx[piece][rot][i];
440
+ board[rowoff[y] + x] = piece + 1;
441
+ }
442
+ for (char y = 0; y < H; y++) {
443
+ const char *s = SCR + scroff[y], *c = CRAM + scroff[y];
444
+ const char *bp = board + rowoff[y];
445
+ for (char x = 0; x < W; x++) {
446
+ char v = bp[x];
447
+ if (s[x] != glyph[v] || (c[x] & 15) != colours[v]) bad++;
448
+ }
449
+ }
450
+ if (live) for (char i = 0; i < 4; i++) {
451
+ char y = py + cy[piece][rot][i], x = px + cx[piece][rot][i];
452
+ board[rowoff[y] + x] = 0;
453
+ }
454
+ return bad;
455
+ }
456
+
457
+ // Worst-frame subject: a full-height stack, every row open in column 9,
458
+ // an upright I locking into the bottom four with fire, left and right
459
+ // held, so the three rotation tries and both moves are tested and fail.
460
+ // Lock, four-line clear, collapse of sixteen rows and spawn are timed as
461
+ // the rules; the redraw of all twenty rows is timed apart as the render.
462
+ static unsigned subj_rules, subj_render;
463
+ static char worst_subject(void)
464
+ {
465
+ new_game();
466
+ for (char y = 0; y < H; y++) {
467
+ for (char x = 0; x < W - 1; x++) board[rowoff[y] + x] = 8;
468
+ rowfill[y] = W - 1;
469
+ }
470
+ top = 0;
471
+ piece = 0; rot = 1; px = 7; py = 16; ox = px; oy = py; orot = rot;
472
+ fall = grav[0] - 1; // gravity runs out this frame
473
+ prev_joy = 0xff;
474
+ wait_frame();
475
+ t_start();
476
+ game_step(0xff & ~(J_FIRE | J_LEFT | J_RIGHT)); // all refused, then lock
477
+ spawn(0);
478
+ subj_rules = t_stop();
479
+ t_start();
480
+ render();
481
+ subj_render = t_stop();
482
+ return ev_lines == 4 && top == 4 && screen_bad(1) == 0;
483
+ }
484
+
485
+ // Lock-frame move: an upright I in column 8 is held right on the frame
486
+ // gravity locks it, so it moves to column 9 and locks there, completing
487
+ // row 16. Rows 17-19 are below the cleared row and are not redrawn; the
488
+ // cells drawn in column 8 must still be erased.
489
+ static char lock_move_probe(void)
490
+ {
491
+ new_game();
492
+ for (char y = 16; y < H; y++) {
493
+ char n = y == 16 ? 9 : 8;
494
+ for (char x = 0; x < n; x++) board[rowoff[y] + x] = 8;
495
+ rowfill[y] = n;
496
+ }
497
+ top = 16;
498
+ redraw_rows(0, H - 1);
499
+ piece = 0; rot = 1; px = 6; py = 16; ox = px; oy = py; orot = rot;
500
+ locked = 0;
501
+ render(); // drawn in column 8
502
+ fall = grav[0] - 1; prev_joy = 0xff;
503
+ game_step(0xff & ~J_RIGHT); // moves to column 9, then locks
504
+ spawn(0);
505
+ render();
506
+ return locked && ev_lines == 1 && screen_bad(1) == 0;
507
+ }
508
+ #endif
509
+
510
+ int main(void)
511
+ {
512
+ __asm { sei } // KERNAL IRQ off: timer A is ours
513
+ pal = detect_pal();
514
+ setup_screen();
515
+ unsigned worst_lock = 0, worst_move = 0, four = 0;
516
+
517
+ #if AUTOPILOT
518
+ // Randomiser check: ten bags from seed $ACE1, each a permutation.
519
+ unsigned bchk = 0; char bag_ok = 1;
520
+ for (char b = 0; b < 10; b++) {
521
+ char seen = 0;
522
+ for (char i = 0; i < 7; i++) { char v = bag_next(); seen |= 1 << v; bchk = fold(bchk, v); }
523
+ if (seen != 0x7f) bag_ok = 0;
524
+ }
525
+ char subject_ok = worst_subject();
526
+ char probe_ok = lock_move_probe();
527
+ new_game();
528
+ load_garbage();
529
+ spawn(plan[0].p);
530
+ #else
531
+ for (;;) {
532
+ new_game();
533
+ put_text(1, 12, "FIRE TO PLAY");
534
+ for (;;) { // seed from the frames the player took
535
+ wait_frame(); rnd();
536
+ cia1.pra = 0xff;
537
+ if (!(cia1.pra & J_FIRE)) break;
538
+ }
539
+ put_text(1, 12, " ");
540
+ prev_joy = 0; // the start press is not a rotate
541
+ spawn(bag_next()); nextp = bag_next();
542
+ #endif
543
+ redraw_rows(0, H - 1); hud();
544
+ for (;;) {
545
+ wait_frame();
546
+ #if AUTOPILOT
547
+ char joy = autopilot();
548
+ #else
549
+ cia1.pra = 0xff;
550
+ char joy = cia1.pra;
551
+ #endif
552
+ t_start();
553
+ game_step(joy);
554
+ char next_ok = 1;
555
+ if (locked) {
556
+ #if AUTOPILOT
557
+ ap_n++; ap_phase = 0; ap_tog = 0;
558
+ if (ap_n < 5) next_ok = spawn(plan[ap_n].p);
559
+ #else
560
+ next_ok = spawn(nextp); nextp = bag_next();
561
+ #endif
562
+ }
563
+ render();
564
+ unsigned t = t_stop();
565
+ if (locked) { if (t > worst_lock) worst_lock = t; }
566
+ else if (t > worst_move) worst_move = t;
567
+ if (ev_lines == 4) four = t;
568
+ if (locked) hud();
569
+ if (ev_lines) {
570
+ static const char *const names[4] = { "SINGLE", "DOUBLE", "TRIPLE", "FOUR " };
571
+ put_text(1, 3 + ev_lines, names[ev_lines - 1]);
572
+ }
573
+ if (kicks) put_text(1, 9, "KICK");
574
+ if (level) put_text(1, 10, "LEVEL UP");
575
+ #if AUTOPILOT
576
+ if (ap_n == 5) break;
577
+ #endif
578
+ if (!next_ok) break;
579
+ }
580
+ #if AUTOPILOT
581
+ // Spawn test: a cell under the spawn box refuses the next piece.
582
+ board[rowoff[1] + 4] = 8;
583
+ char spawn_ok = !spawn(0);
584
+ board[rowoff[1] + 4] = 0;
585
+ unsigned chk = 0;
586
+ for (char i = 0; i < H * W; i++) chk = fold(chk, board[i]);
587
+ char pass = chk == EXPECT_BOARD && score == EXPECT_SCORE && lines == 10
588
+ && level == 1 && clears[0] == 1 && clears[1] == 1 && clears[2] == 1
589
+ && clears[3] == 1 && kicks == 1 && bchk == EXPECT_BAG && bag_ok
590
+ && spawn_ok && subject_ok && probe_ok && screen_bad(0) == 0;
591
+ put_text(1, 13, "BOARD"); put_hex(7, 13, chk);
592
+ put_text(1, 14, "BAG"); put_hex(7, 14, bchk);
593
+ put_text(27, 13, pal ? "PAL" : "NTSC");
594
+ put_text(27, 15, "LOCK MAX"); put_num(27, 16, worst_lock, 5);
595
+ put_text(27, 17, "FOUR"); put_num(27, 18, four, 5);
596
+ put_text(27, 19, "MOVE MAX"); put_num(27, 20, worst_move, 5);
597
+ put_text(27, 21, "RULES"); put_num(27, 22, subj_rules, 5);
598
+ put_text(27, 23, "RENDER"); put_num(27, 24, subj_render, 5);
599
+ put_text(1, 16, pass ? "PASS" : "FAIL");
600
+ RESULT = pass ? 1 : 2;
601
+ vic.color_border = pass ? 5 : 2;
602
+ for (;;) ;
603
+ #else
604
+ put_text(1, 12, "GAME OVER");
605
+ for (char f = 0; f < 100; f++) wait_frame();
606
+ put_text(1, 12, " ");
607
+ for (char i = 1; i < 11; i++) put_text(1, i, " ");
608
+ }
609
+ #endif
610
+ return 0;
611
+ }
612
+ ```
613
+
614
+ ## Build
615
+
616
+ ```bash
617
+ oscar64 -tm=c64 -O2 -n -o=falling-blocks.prg falling-blocks.c # 5,348 bytes, autopilot
618
+ oscar64 -tm=c64 -O2 -dAUTOPILOT=0 -o=falling-blocks-play.prg falling-blocks.c # 3,697 bytes, the game
619
+ ```
620
+
621
+ The expected values in the listing come from this model of the same
622
+ rules at the level of placements (rotate, move, rotate, drop). The
623
+ autopilot aims at a rotation and a column, so the result does not
624
+ depend on how many frames gravity or DAS take, and the same values hold
625
+ on PAL and NTSC.
626
+
627
+ ```python
628
+ W, H = 10, 20
629
+ # Cells of each piece in a 4x4 box, (x, y), per rotation 0-3, clockwise.
630
+ # Order I O T S Z J L; I, S, Z repeat their two states, O its one.
631
+ P = {
632
+ 0: [[(0,1),(1,1),(2,1),(3,1)], [(2,0),(2,1),(2,2),(2,3)]] * 2,
633
+ 1: [[(1,0),(2,0),(1,1),(2,1)]] * 4,
634
+ 2: [[(0,1),(1,1),(2,1),(1,2)], [(1,0),(0,1),(1,1),(1,2)],
635
+ [(1,0),(0,1),(1,1),(2,1)], [(1,0),(1,1),(2,1),(1,2)]],
636
+ 3: [[(1,1),(2,1),(0,2),(1,2)], [(1,0),(1,1),(2,1),(2,2)]] * 2,
637
+ 4: [[(0,1),(1,1),(1,2),(2,2)], [(2,0),(1,1),(2,1),(1,2)]] * 2,
638
+ 5: [[(0,1),(1,1),(2,1),(2,2)], [(1,0),(1,1),(0,2),(1,2)],
639
+ [(0,0),(0,1),(1,1),(2,1)], [(1,0),(2,0),(1,1),(1,2)]],
640
+ 6: [[(0,1),(1,1),(2,1),(0,2)], [(0,0),(1,0),(1,1),(1,2)],
641
+ [(2,0),(0,1),(1,1),(2,1)], [(1,0),(1,1),(1,2),(2,2)]],
642
+ }
643
+ KICKS = (0, 1, -1)
644
+ POINTS = (40, 100, 300, 1200)
645
+ GARBAGE = {9: "######....", 10: "##..######", 11: "##..######",
646
+ 12: ".#########", 13: ".#########", 14: ".#########",
647
+ 15: "#########.", 16: "#########.", 17: "#########.",
648
+ 18: "#########.", 19: "#########."}
649
+ PLAN = [(0, 0, 6, 0), (1, 0, 1, 0), (0, 1, -2, 1), (0, 1, 7, 1), (5, 1, 8, 2)]
650
+
651
+ board = [[8 if GARBAGE.get(y, "." * W)[x] == "#" else 0 for x in range(W)]
652
+ for y in range(H)]
653
+
654
+ def fits(p, r, x, y):
655
+ for cx, cy in P[p][r]:
656
+ if not (0 <= x + cx < W and 0 <= y + cy < H) or board[y + cy][x + cx]:
657
+ return False
658
+ return True
659
+
660
+ score = lines = level = kicks = 0
661
+ clears = [0, 0, 0, 0]
662
+ for p, r1, tx, r2 in PLAN:
663
+ r, x, y = 0, 3, 0
664
+ assert fits(p, r, x, y), "game over"
665
+ def rotate_to(want):
666
+ global r, x, kicks
667
+ while r != want:
668
+ nr = (r + 1) & 3
669
+ for k in KICKS:
670
+ if fits(p, nr, x + k, y):
671
+ r, x = nr, x + k
672
+ kicks += k != 0
673
+ break
674
+ else:
675
+ raise SystemExit("rotation refused")
676
+ rotate_to(r1)
677
+ while x != tx:
678
+ step = 1 if tx > x else -1
679
+ assert fits(p, r, x + step, y), "move blocked"
680
+ x += step
681
+ rotate_to(r2)
682
+ while fits(p, r, x, y + 1):
683
+ y += 1
684
+ for cx, cy in P[p][r]:
685
+ board[y + cy][x + cx] = p + 1
686
+ full = [row for row in board if all(row)]
687
+ n = len(full)
688
+ if n:
689
+ board = [[0] * W for _ in range(n)] + [row for row in board if not all(row)]
690
+ lines += n
691
+ clears[n - 1] += 1
692
+ if lines >= (level + 1) * 10:
693
+ level += 1
694
+ score += POINTS[n - 1] * (level + 1) # level after the clear, as NES
695
+
696
+ chk = 0
697
+ for row in board:
698
+ for v in row:
699
+ chk = ((chk ^ v) * 5 + 1) & 0xFFFF
700
+ print("BOARD %04X SCORE %d LINES %d LEVEL %d CLEARS %s KICKS %d"
701
+ % (chk, score, lines, level, clears, kicks))
702
+ for row in board[15:]:
703
+ print("".join(".IOTSZJLG"[v] for v in row))
704
+
705
+ # 7-bag from a 16-bit Galois LFSR, taps $B400, seed $ACE1: ten bags.
706
+ s = 0xACE1
707
+ def rnd():
708
+ global s
709
+ lsb = s & 1
710
+ s >>= 1
711
+ if lsb:
712
+ s ^= 0xB400
713
+ return s
714
+ seq, bchk = [], 0
715
+ for _ in range(10):
716
+ bag = list(range(7))
717
+ for i in range(6, 0, -1):
718
+ j = rnd() % (i + 1)
719
+ bag[i], bag[j] = bag[j], bag[i]
720
+ seq += bag
721
+ for v in seq:
722
+ bchk = ((bchk ^ v) * 5 + 1) & 0xFFFF
723
+ print("BAG %04X" % bchk, "".join("IOTSZJL"[v] for v in seq[:14]))
724
+ ```
725
+
726
+ It prints:
727
+
728
+ ```text
729
+ BOARD ECB5 SCORE 2840 LINES 10 LEVEL 1 CLEARS [1, 1, 1, 1] KICKS 1
730
+ ..........
731
+ ..........
732
+ .......J..
733
+ I......JJJ
734
+ GGGGGGGGG.
735
+ BAG 808A ILSJTZOSZTOJLI
736
+ ```
737
+
738
+ ## Expected output
739
+
740
+ Pinned run: `x64sc -default -warp +sound +autostart-delay-random
741
+ -autostartprgmode 1 -limitcycles 12500000 [-model ntsc] -exitscreenshot
742
+ out.png -autostart falling-blocks.prg`. Screenshots
743
+ `screenshots/falling-blocks.png` (PAL) and
744
+ `screenshots/falling-blocks-ntsc.png` (NTSC). The exit screenshot is
745
+ already the final one at 8,250,000 cycles on PAL and 8,750,000 on NTSC,
746
+ and differs at 8,000,000 and 8,500,000; after that the program holds the
747
+ result screen.
748
+
749
+ Border green (PAL RGB 98, 213, 50; NTSC 114, 189, 103), background
750
+ black. Screen rows 2-24 of the PAL shot, decoded cell by cell against
751
+ the character ROM (`#` is a reverse-space cell: walls, garbage and
752
+ pieces):
753
+
754
+ ```text
755
+ # #
756
+ # # SCORE
757
+ SINGLE # # 002840
758
+ DOUBLE # #
759
+ TRIPLE # # LINES
760
+ FOUR # # 010
761
+ # #
762
+ KICK # # LEVEL
763
+ LEVEL UP # # 01
764
+ # #
765
+ # #
766
+ BOARD ECB5 # # PAL
767
+ BAG 808A # #
768
+ # # LOCK MAX
769
+ PASS # # 06276
770
+ # # FOUR
771
+ # # 06276
772
+ # # # MOVE MAX
773
+ ## #### 02113
774
+ ########## # RULES
775
+ ############ 05717
776
+ RENDER
777
+ 09311
778
+ ```
779
+
780
+ On NTSC the same, with `NTSC` on row 13 and 06491, 06491, 02113, 05888
781
+ and 09394 in the five figures.
782
+
783
+ The well is screen columns 15-24, rows 2-21, with medium grey (colour
784
+ 12) walls in columns 14 and 25 and a floor on row 22. All 200 board
785
+ cells were measured with PIL on both shots against the model's board,
786
+ each 8x8 cell required to be one colour: empty cells black, row 19
787
+ columns 0-8 dark grey (colour 11, garbage) with column 9 black, row 18
788
+ column 0 cyan (colour 3, the I left by the triple), row 18 columns 7-9
789
+ and row 17 column 7 light blue (colour 14, the J). No cell differed on
790
+ either model. Each model was run twice and gave byte-identical PNGs.
791
+
792
+ ## Why this works
793
+
794
+ `game_step` is the whole rule set and takes one joystick byte, active
795
+ low as `$DC00` reads it. The autopilot builds that byte from a plan of
796
+ (piece, first rotation, column, second rotation): it taps fire every
797
+ other frame until the rotation matches, holds left or right until the
798
+ column matches, taps fire again, then holds down until the piece locks.
799
+ Holding a direction is what drives `das`, so the moves across the board
800
+ exercise the delay and repeat exactly as a held stick would. The J is
801
+ moved to the right wall in its upright state and rotated there; its flat
802
+ state does not fit in place or one cell right, so the kick table's third
803
+ entry moves it one left, and the program counts that kick.
804
+
805
+ The board rows start at 9 with garbage built so that each piece clears
806
+ the rows above the next well first: the flat I completes row 9, the O
807
+ rows 10-11, the upright I at the left wall rows 12-14, and the upright I
808
+ in column 9 rows 16-19, with row 15 dropping into row 19 afterwards. The
809
+ four clears total 10 lines, so the level goes to 1 on the four-line
810
+ clear. As on the NES, a clear is scored at the level after it:
811
+ 40 + 100 + 300 at level 0 and 1200 x 2 at level 1, 2840.
812
+
813
+ Every game frame is timed with CIA1 timer A from `t_start` to `t_stop`,
814
+ the method of `print-number.md`, with the KERNAL IRQ off (`sei`) so the
815
+ jiffy handler neither runs inside the timed code nor reloads the timer.
816
+ The screen stays on and each frame starts at raster line 250, so badline
817
+ stalls are in the figures. HUD printing runs after `t_stop` and is not
818
+ counted. `worst_subject` builds a stack the game cannot reach, twenty
819
+ rows high with column 9 open, and times the frame in which an upright I
820
+ locks into the bottom four with fire, left and right all held: three
821
+ rotation tries and both moves are tested and refused, then the lock, a
822
+ four-line clear, sixteen rows collapsed and a spawn. Holding left and
823
+ right together cannot happen on a real stick, so this is an upper bound.
824
+ That rules part took 5,717 cycles on PAL and 5,888 on NTSC. The render
825
+ of the same frame, timed apart, erases and redraws the locked piece,
826
+ redraws all twenty rows and draws the new piece: 9,311 on PAL and 9,394
827
+ on NTSC. That is `text_mode_overlay_render`'s work, not the rules'. The
828
+ scripted game's own four-line clear, rules and render together with six
829
+ rows to redraw, took 6,276 on PAL and 6,491 on NTSC and was its dearest
830
+ frame. The whole subject frame, 15,028 cycles on PAL and 15,282 on NTSC,
831
+ runs about 240 and 235 raster lines from line 250 (divided by 63 and 65,
832
+ arithmetic), well past the start of the next frame's display, so the
833
+ collapse frame can tear the field once (`full_field_redraw_exceeds_vblank`
834
+ in `pitfalls/text-mode-render.md`). A game that cares redraws only the
835
+ rows that moved over two frames, or builds the next field in a second
836
+ screen (`screen_double_buffer_d018`).
837
+
838
+ `render` erases the piece where it was last drawn and draws it where it
839
+ is on every frame, including the frame it locks. A lock frame can also
840
+ carry a move or a rotation, and a clear redraws only the rows from the
841
+ old stack top down to the lowest cleared row, so rows below that row
842
+ keep whatever the erase left. `lock_piece` records where the piece
843
+ locked (`lk_p`, `lk_r`, `lk_x`, `lk_y`) because the spawn that follows
844
+ overwrites `piece` and `px`. `lock_move_probe` builds that case: an
845
+ upright I drawn in column 8, held right on the frame gravity locks it,
846
+ completing row 16 while its lower three cells sit in rows 17-19. It then
847
+ requires every well cell on screen, glyph and colour, to match the board
848
+ with the new piece on it (`screen_bad`). With the erase skipped on a
849
+ lock that clears lines, the probe fails and the program prints FAIL
850
+ (measured in VICE). The same screen check runs on the worst-frame
851
+ subject and on the final board.
852
+
853
+ `detect_pal` polls for raster line 280, which exists only on PAL, and
854
+ selects the NES PAL gravity and DAS tables there. The rules then run at
855
+ the speed the NES tables intend on both regions; the scripted result is
856
+ the same on both because the autopilot aims at positions, not frames.
857
+
858
+ The lock writes `char x = px + cx[...]` before indexing the board.
859
+ Summing `rowoff[y] + px + cx[...]` inline instead loses a cell locked at
860
+ column 0 with `px` = -2 (board fold `04AC` instead of `ECB5`, the
861
+ model's board less that cell). Oscar64 1.32.271 at `-O2` hoists
862
+ `board + px` out of the loop as a 16-bit pointer and zero-extends the
863
+ signed `px` (`CLC`, `LDA #<board`, `ADC px`, `LDA #>board`, `ADC #$00`),
864
+ so `px` = -2 (`$FE`) lands 256 bytes above the cell. A 20-line test
865
+ reproduces it (border red, none of the four cells in the board);
866
+ there is no diagnostic. Computing the column in a `char` first, as the
867
+ listing does, compiles correctly.
868
+
869
+ The 7-bag check deals ten bags from seed `$ACE1`, requires each to hold
870
+ all seven pieces, and folds the 70 pieces into `808A`, the model's
871
+ value. The spawn check puts one cell under the spawn box, requires
872
+ `spawn` to refuse, and removes it. In the joystick build the bag is
873
+ seeded by stepping the LFSR once per frame until fire is pressed, the
874
+ player-input seed of `lfsr_random`.