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,120 @@
1
+ # Rules for an agent working on this C64 program
2
+
3
+ This program runs on a stock Commodore 64, PAL and NTSC. It is built on the
4
+ c64-kb harness (`./harness`, vendored from c64-kb's `templates/_harness`).
5
+ c64-kb is the reference: its pages give the registers, timings, techniques,
6
+ pitfalls and recipes this program is built from. A wrong number becomes a torn
7
+ screen or a crash, so every claim here is measured or says it is not.
8
+
9
+ A new program starts in c64-kb with `npm run new-project -- <starter> <dir>`.
10
+ That copies the starter and the harness, points `.mcp.json` and `local.mk`
11
+ at the c64-kb checkout, proves the copy with `make shot check`, and leaves a
12
+ blank `PLAN.md`.
13
+
14
+ ## This starter: action-puzzle
15
+
16
+ CAVE RUN, a Boulder Dash-style game in Oscar64 C: a cave scan split into
17
+ four row slices a frame, RLE caves, a tune and effects, and a high-score
18
+ table saved to drive 8. README.md has the file map and the measured frame.
19
+ Start a program from it with `npm run new-project -- action-puzzle <dir>`
20
+ in c64-kb. `PLAN.md` here is a plan filled from real tool output;
21
+ new-project keeps it as `PLAN-action-puzzle-example.md` and gives the new
22
+ program a blank one.
23
+
24
+ Rules this starter adds:
25
+
26
+ - The rules live twice: `src/cave.c` and the model in `tools/gen.py`. Change
27
+ both, run `make gen`, then `make shot check modelcheck`. The autopilot
28
+ verdict compares the game with the model; a rule changed on one side only
29
+ turns it red. A red verdict prints one letter per failed test on row 23
30
+ (README, "Checks"). After a change to the scan, also `make selftest-scan`.
31
+ - A slice fits about 20 moving objects on NTSC; the verdict counts dropped
32
+ frames (letter D), so a cave past that fails its own check.
33
+ - Caves and the autopilot script are edited in `tools/gen.py`, never in the
34
+ generated `src/gen_*.h`.
35
+ - Disk calls happen on a static screen, with the sound muted, followed by
36
+ `SEI` and `$DC00 = $FF`. `make disktest` proves the save and the load on a
37
+ true-drive 1541; run it after any change to `hiscore.c` or to what runs
38
+ before the first OPEN (pitfall `first_open_after_reset_hangs_on_pal`).
39
+ - To see the normal game (not the autopilot) headless: `make joy`, then
40
+ `tools/drive.py` (README, "Driving it headless"). VICE's joyport commands
41
+ do not reach `$DC00` in the windowless build.
42
+
43
+ ## Before any code
44
+
45
+ 1. Brief: `npx tsx src/cli.ts game-briefing "<concept>" --archetype <name>`
46
+ (or `demo-briefing`), in the c64-kb checkout. Read what it proposes.
47
+ 2. For each technique you keep: `technique-lookup <name>` and
48
+ `pitfalls-for <name>`. Start from the recipe it names (`recipe-lookup`).
49
+ 3. Fill `PLAN.md`: paste the whole output of `check-compatibility` and of
50
+ `plan-budget` (same techniques) into their sections, list every technique
51
+ in its table, and replace every `FILL:` line.
52
+
53
+ Until `PLAN.md` passes, a hook blocks Edit and Write under `src/`, and every
54
+ make target that builds stops (`make plan-gate` says what is missing). With
55
+ C64KB set, `make` re-runs `check-compatibility` and wants the pasted Verdict.
56
+ The gate cannot see writes made through a shell command, or outputs edited
57
+ into shape by hand: do neither. `make PLAN_GATE=off` overrides it; say why in
58
+ `PLAN.md` if you do.
59
+
60
+ ## The loop
61
+
62
+ ```bash
63
+ make # build build/<name>.prg
64
+ make shot check # autopilot build, headless PAL and NTSC, graded by expect.json
65
+ make selftest # the FORCE_FAULT build must fail the same checks
66
+ make disk # build/<name>.d64
67
+ make claims # every store the program makes, against what it declares
68
+ make run # windowed VICE, for a human
69
+ ```
70
+
71
+ ## Rules
72
+
73
+ - Build before claiming. Code that has not built is not done.
74
+ - Measure screenshots with `check.py` through `expect.json`, never by eye.
75
+ Every visible claim gets a check. Add one before you trust a change.
76
+ - PAL and NTSC: both shots pass, or it is not done.
77
+ - Name the rung for every number: measured here (`make shot`, the meter, the
78
+ VICE monitor), taken from a c64-kb page (say which), arithmetic, or not
79
+ measured. Never call something verified that was not run.
80
+ - Frame cost comes from the meter: CIA2 timer A, printed as
81
+ `F<frames> W<worst> T<typical>` at row 24, columns 20 to 39, in AUTOPILOT
82
+ builds. It records the first `hold` frames: make that the autopilot
83
+ script's play frames. Typical is the median of those frames, the KB's
84
+ `cycles_per_frame_typical`. Keep grading, logging and printing outside the
85
+ bracket; work split between the main loop and IRQs is summed with
86
+ `METER_PAUSE` / `METER_START`.
87
+ - Do not invent a register, routine or address. Look it up:
88
+ `lookup-register`, `lookup-kernal`, `memory-map`.
89
+ - Lint every source you change: `npx tsx src/cli.ts lint <file>`.
90
+ - Commit named paths only. Never `git add -A` or `git add .`. `build/`,
91
+ `shots/` and `local.mk` are never committed.
92
+
93
+ ## Harness facts you will need
94
+
95
+ - `AUTOPILOT=1` builds read a scripted joystick byte instead of `$DC00` and
96
+ grade themselves: `$02FF` = `$01` and a green border on pass, `$02` and red
97
+ on fail. `FORCE_FAULT=1` must turn the verdict red and move something a
98
+ screenshot check can see.
99
+ - Screenshot geometry (c64-kb `docs/runtime/vice-reference.md`): PAL 384 x 272,
100
+ row = raster line - 16; NTSC 384 x 247, row = line - 28; x = VIC X + 8. A
101
+ sprite at Y register y is drawn from line y + 1.
102
+ - `check.py` reads text in the ROM character set unless a check names a
103
+ `charset` file; the meter's cells need 0-9, F, W and T in the set on screen.
104
+ - A KickAssembler part called from C is a raw blob at its own `* =` address,
105
+ `$0880` or above; `build/asm.h` gives C its labels as `ASM_<LABEL>`
106
+ (`ASM_<SCOPE>_<LABEL>` inside a scope). Pass arguments through bytes in the
107
+ blob, not Oscar64's zero page (`$02` up; the top depends on the program:
108
+ `make zp` lists what the build's code touches).
109
+ - The meter takes CIA2 timer A. Do not open RS-232 (device 2) while it runs.
110
+ - The KERNAL serial routines end in `CLI` (ROM bytes `58 60` / `58 18` at
111
+ `$EDAB`, `$EDB5`, `$EDDB`, `$EE82`). After any disk call, `SEI` again if
112
+ the program runs with the KERNAL IRQ off: otherwise that IRQ lands inside
113
+ the meter's brackets and rewrites `$DC00`.
114
+
115
+ ## Calling c64-kb
116
+
117
+ - CLI, from the c64-kb checkout (`C64KB` in `local.mk`):
118
+ `npx tsx src/cli.ts <command>`; `npx tsx src/cli.ts --help` lists them.
119
+ - MCP: `.mcp.json` starts `node <c64-kb>/dist/cli.js serve` (run
120
+ `npm run build` in c64-kb first).
@@ -0,0 +1,125 @@
1
+ # action-puzzle: CAVE RUN, a Boulder Dash-style starter. Harness targets:
2
+ # make build build/action-puzzle.prg (after the PLAN.md gate)
3
+ # make run windowed VICE, joystick in port 2
4
+ # make shot headless PAL and NTSC exit screenshots of the autopilot build
5
+ # make check grade shots/pal.png and shots/ntsc.png against expect.json
6
+ # make selftest prove check fails on the FORCE_FAULT=1 build
7
+ # make disk build/action-puzzle.d64
8
+ # make claims c64-kb's claims-watch over the autopilot build, when available
9
+ # and this starter's own:
10
+ # make gen re-run tools/gen.py (caves, script, model, note table)
11
+ # make disktest the high-score save on a true-drive 1541: save, save over it, cold start, load
12
+ # make modelcheck every visible cave cell of the shots against tools/gen.py's model
13
+ # make joy the normal game reading its port byte from $02FE, for tools/drive.py
14
+ # make clean
15
+ NAME := action-puzzle
16
+ C_MAIN := src/main.c
17
+
18
+ # Pinned after the verdict, which follows the save and its read-back on the
19
+ # shot's fresh disk. Measured with VICE x64sc 3.10: at 18,000,000 cycles the
20
+ # save is still running on both models; at 20,000,000, 24,000,000 and
21
+ # 30,000,000 the picture is byte-identical, PAL and NTSC.
22
+ SHOT_CYCLES_PAL := 24000000
23
+ SHOT_CYCLES_NTSC := 24000000
24
+
25
+ # Every headless shot attaches its own fresh copy of the release D64 (the
26
+ # harness makes it), so the autopilot saves its table and reads it back.
27
+ SHOT_DISK := 1
28
+
29
+ # What the program may write, for make claims (claims-watch's grammar).
30
+ # zero_page $02-$52 is Oscar64's register block, as in hello; $53-$55 is the
31
+ # start of its BC_REG_TMP_SAVED block (MachineTypes.cpp), which this
32
+ # program's larger functions use (claims-watch found 902 stores there on
33
+ # the first run). --kernal names the routines hiscore.c calls through
34
+ # kernalio.h and the KERNAL IRQ, which runs during those calls only.
35
+ # vic_raster_irq: wait_frame sets the compare line and acknowledges the
36
+ # $D019 latch to count dropped frames; the interrupt itself stays disabled.
37
+ CLAIMS_ARGS := --cycles 24000000 --kernal SETLFS,SETNAM,OPEN,CLOSE,CHKIN,CHKOUT,CLRCHN,CHRIN,CHROUT,READST,IRQ --claim 'sid_voice_1, sid_voice_2, sid_voice_3, sid_filter_volume, cia1_port_a, cia1_timer_b, vic_raster_irq, zero_page $$02-$$55' --range 'screen=$$0400-$$07FF,colour=$$D800-$$DBFF,charset=$$2000-$$27FF,result=$$02FF' --harness 'cia2_timer_a, cia2_timer_b, cia2_tod'
38
+
39
+ # The harness sits beside the starter in c64-kb (../_harness) or is vendored
40
+ # into ./harness when the starter is copied out on its own.
41
+ # Proof targets `npm run verify:templates -- --selftest` runs after selftest.
42
+ VERIFY_TARGETS := disktest modelcheck selftest-scan
43
+
44
+ HARNESS ?= $(firstword $(wildcard harness) ../_harness)
45
+ $(if $(wildcard $(HARNESS)/harness.mk),,$(error No harness at '$(HARNESS)'. Copy c64-kb's templates/_harness to ./harness, or make the project with npm run new-project in c64-kb))
46
+ include $(HARNESS)/harness.mk
47
+
48
+ # ---- this starter's own targets ---------------------------------------------------
49
+ # tools/gen.py writes the three src/gen_*.h headers; an edit to it rebuilds them.
50
+ GEN_OUT := src/gen_caves.h src/gen_autopilot.h src/gen_notes.h
51
+ $(GEN_OUT): build/gen.stamp
52
+ build/gen.stamp: tools/gen.py
53
+ @mkdir -p build
54
+ $(PYTHON) tools/gen.py
55
+ @touch $@
56
+
57
+ .PHONY: gen disktest modelcheck
58
+ gen:
59
+ $(PYTHON) tools/gen.py
60
+
61
+ # The disk test, on a true-drive 1541 (DISK_MODEL=pal or ntsc):
62
+ # 1. the autopilot build runs against a fresh copy of the release D64: no
63
+ # file (62), it plays, saves (scratch, then write) and shows SAVED (00);
64
+ # 2. it runs again on the same D64: it loads that file, its score ties row 4
65
+ # and goes in row 5, and the save must replace the file (without the
66
+ # scratch the drive answers 63, FILE EXISTS). Its verdict is red by
67
+ # design: the table it started from is not the default one;
68
+ # 3. a cold VICE boots the release PRG from the D64 with LOAD"*",8,1; it
69
+ # loads the table and shows it on the title screen.
70
+ # tools/disk_check.py grades the three pictures, both files c1541 read back,
71
+ # and the directory (one HISCORE entry).
72
+ DISKTEST_D64 := build/disktest.d64
73
+ DISK_MODEL ?= pal
74
+ # PAL is the -default machine: "-model pal" selects a 6569R3 whose palette
75
+ # differs from the one check.py matches (border green read (94, 214, 56)).
76
+ DISK_VICE = $(TIMEOUT) $(VICE_TIMEOUT) $(X64SC) $(VICE_FLAGS) $(if $(filter ntsc,$(DISK_MODEL)),-model ntsc) -drive8type 1541 -drive8truedrive -drive8wobbleamplitude 0 -drive8wobblefrequency 0
77
+ # $(1) = the picture's name, $(2) = cycles, $(3) = what to autostart, $(4) = extra flags
78
+ define disk_run
79
+ @rm -f shots/$(1).png
80
+ @$(DISK_VICE) -limitcycles $(2) $(4) -exitscreenshot shots/$(1).png -autostart $(3) > shots/$(1).log 2>&1 || true
81
+ @test -s shots/$(1).png || { echo "disktest: no picture from the $(1) run; see shots/$(1).log"; exit 1; }
82
+ endef
83
+ disktest: $(D64) $(PRG_AUTO)
84
+ @mkdir -p shots
85
+ cp $(D64) $(DISKTEST_D64)
86
+ $(call disk_run,disk-save,30000000,$(PRG_AUTO),-8 $(DISKTEST_D64))
87
+ $(C1541) -attach $(DISKTEST_D64) -read "hiscore,s" build/hiscore-1.seq > /dev/null
88
+ $(call disk_run,disk-save2,30000000,$(PRG_AUTO),-8 $(DISKTEST_D64))
89
+ $(C1541) -attach $(DISKTEST_D64) -read "hiscore,s" build/hiscore-2.seq > /dev/null
90
+ $(C1541) -attach $(DISKTEST_D64) -list > build/disktest-dir.txt 2>/dev/null
91
+ $(call disk_run,disk-load,60000000,$(DISKTEST_D64),)
92
+ $(PYTHON) tools/disk_check.py $(HARNESS_DIR) build $(DISK_MODEL)
93
+
94
+ # The normal game with its port byte read from $02FE instead of $DC00, so a
95
+ # VICE monitor can drive it headless (tools/drive.py; README "Driving it headless").
96
+ .PHONY: joy
97
+ joy: build/$(NAME)-joy.prg
98
+ build/$(NAME)-joy.prg: $(C_DEPS) $(METER_DEPS) | plan-gate
99
+ @mkdir -p build
100
+ $(OSCAR64_BUILD) -dJOY_SOURCE=0x02fe -o=$@ $(C_MAIN)
101
+
102
+ # The scanned bit's self-test: the autopilot built with SCAN_FLAG=0 (the
103
+ # cave-scan recipe's double-move bug) must fail check.py and name E in its
104
+ # verdict, the test that the scan meets the living player once a cave frame.
105
+ PRG_NOSCAN := build/$(NAME)-noscan.prg
106
+ $(PRG_NOSCAN): $(C_DEPS) $(METER_DEPS) | plan-gate
107
+ @mkdir -p build
108
+ $(OSCAR64_BUILD) -d$(AUTOPILOT_DEFINE)=1 -dSCAN_FLAG=0 -o=$@ $(C_MAIN)
109
+ shots/noscan-pal.png: $(PRG_NOSCAN) $(PIN_PAL) $(SHOT_DEPS)
110
+ $(call vice_shot,$(PRG_NOSCAN),$(SHOT_CYCLES_PAL),,$@)
111
+ shots/noscan-ntsc.png: $(PRG_NOSCAN) $(PIN_NTSC) $(SHOT_DEPS)
112
+ $(call vice_shot,$(PRG_NOSCAN),$(SHOT_CYCLES_NTSC),-model ntsc,$@)
113
+ .PHONY: selftest-scan
114
+ selftest-scan:
115
+ @rm -f shots/noscan-pal.png shots/noscan-ntsc.png
116
+ @$(MAKE) --no-print-directory shots/noscan-pal.png shots/noscan-ntsc.png
117
+ @$(PYTHON) $(HARNESS_DIR)/check.py expect.json shots/noscan-pal.png shots/noscan-ntsc.png > shots/noscan-check.txt 2>&1; st=$$?; \
118
+ if [ $$st -ne 1 ]; then cat shots/noscan-check.txt; echo "selftest-scan: FAIL, check.py exit $$st on the SCAN_FLAG=0 build"; exit 1; fi
119
+ @$(PYTHON) tools/verdict_codes.py $(HARNESS_DIR) shots/noscan-pal.png pal E
120
+ @$(PYTHON) tools/verdict_codes.py $(HARNESS_DIR) shots/noscan-ntsc.png ntsc E
121
+ @echo "selftest-scan: PASS, the SCAN_FLAG=0 build failed, E among its letters"
122
+
123
+ # Every visible cave cell of both autopilot shots against tools/gen.py's model.
124
+ modelcheck: $(SHOTS)
125
+ $(PYTHON) tools/model_check.py $(HARNESS_DIR) shots/pal.png shots/ntsc.png
@@ -0,0 +1,388 @@
1
+ # Plan: action-puzzle
2
+
3
+ A Boulder Dash-style cave game, planned from the c64-kb tools on 2026-09-23
4
+ (c64-kb 0.14.0, the graph on this branch). The tool output is pasted whole.
5
+
6
+ ## Concept
7
+
8
+ The player digs through earth in a 40 x 22 cave of one-character cells.
9
+ Boulders and gems fall and roll under a cave scan; two enemy kinds follow
10
+ walls. Collecting the cave's quota of gems opens the exit; walking into it
11
+ decodes the next cave from its RLE stream. A timer, a score and three lives;
12
+ at game over a new high score is entered by joystick and the five-row table
13
+ is saved to drive 8 and loaded back at start-up. An original tune plays
14
+ through the game, sound effects on their own voice. PAL and NTSC, the same
15
+ game logic on both (one cave frame every four display frames).
16
+
17
+ ## Briefing
18
+
19
+ Command: `npx tsx src/cli.ts game-briefing "Boulder Dash-style cave game: dig earth, boulders and gems fall and roll under a cave scan, enemies follow walls, enough gems open the exit; RLE-compressed caves decoded between caves; timer, score, high-score table saved to disk" --archetype action-puzzle`
20
+
21
+ Kept from its 13 proposals: cave_scan_engine, high_score_table_insert,
22
+ raster_profile_bars (the harness meter is its CIA half).
23
+
24
+ Dropped, and why: text_mode_overlay_render (no moving overlay: every
25
+ object is a cell), directory_read_and_select (the file name is fixed),
26
+ irq_keyboard_own_scan and keyboard_matrix_scan (joystick only),
27
+ text_window_and_menu (one fixed table box, drawn directly),
28
+ actor_activation_window and nav_area_pathfinding (enemies are cells the scan
29
+ moves by local rules), two_word_parser (no text input),
30
+ drive_code_upload_and_job_queue (KERNAL file calls are enough for a small
31
+ file), jump_arc_table (no jumping).
32
+
33
+ Added, which the briefing did not propose: tile_map_render (its RLE row
34
+ stream is the cave format), charset_animation (gems, enemies and the open
35
+ exit animate by glyph), sid_play_routine_pattern and sfx_engine_beside_music
36
+ (tune and effects), kernal_file_read_seq, kernal_file_write_seq and
37
+ error_channel_check (the table on disk), frame_sync_loop, joystick_edge_detect.
38
+
39
+ The briefing listed 0 pitfalls; pitfalls-for on the techniques kept found
40
+ the ones in the table below.
41
+
42
+ ## Techniques
43
+
44
+ | Technique | Why | Recipe it starts from | Pitfalls read (`pitfalls-for`) |
45
+ |---|---|---|---|
46
+ | cave_scan_engine | falling, rolling, digging, pushing, wall-following enemies, explosions; one scan per cave frame, split into four row slices, one a display frame | oscar64-cave-scan | pal_ntsc_tempo_mismatch |
47
+ | tile_map_render | caves stored as the RLE row stream (run `0x80+n`, literal count, `0` ends a row), decoded between caves | oscar64-level-rle-decoder, oscar64-tile-map-render | badline_cycle_loss, colour_ram_index_past_last_cell_hits_cia1, full_field_redraw_exceeds_vblank |
48
+ | charset_animation | gem sparkle, enemy spin and the open exit by rewriting glyph bytes in the RAM charset (method (a)) | oscar64-charset-animation | charset_blit_overruns_grown_code, vic_bank_visibility_collision, petscii_written_to_screen_ram |
49
+ | sid_play_routine_pattern | an original two-voice tune, one play call a frame | oscar64-sid-music-player | pal_ntsc_tempo_mismatch, sid_write_only_registers |
50
+ | sfx_engine_beside_music | table-driven effects with priorities on voice 3, after the tune's play call | oscar64-sfx-engine | sid_adsr_bug_8580, sid_voice3_disable_silent_bit |
51
+ | high_score_table_insert | a new score into the sorted five-row table; a tie goes below | kickassembler-high-score-insert, oscar64-high-score-persist | petscii_written_to_screen_ram |
52
+ | kernal_file_read_seq | load the table at start-up | oscar64-high-score-persist, oscar64-save-load-seq-file | first_open_after_reset_hangs_on_pal, kernal_assumes_sei_cleared, kernal_io_mapping_dependency, c1541_uppercase_filename_petscii_shift |
53
+ | kernal_file_write_seq | save it after a new entry: scratch, then write | oscar64-high-score-persist | krnio_save_leaves_splat_file, raster_irq_during_serial_io |
54
+ | error_channel_check | the drive's reply decides first run (62), loaded (00), no disk (74) | oscar64-high-score-persist | first_open_after_reset_hangs_on_pal |
55
+ | frame_sync_loop | one pass a frame, polled at raster line 250 | oscar64-frame-sync-loop, templates/hello | badline_cycle_loss, d012_wrap_around, raster_line_count_difference |
56
+ | joystick_edge_detect | fire and name entry act once per press | oscar64-headless-verify (the autopilot) | joystick2_scan_phantom_press, cia1_ddr_cleared_kills_keyboard |
57
+ | raster_profile_bars | the harness frame meter (CIA2 timer A) | oscar64-raster-profile-bars | badline_cycle_loss, vic_bus_takeover_on_dma |
58
+
59
+ ## Compatibility
60
+
61
+ Command: `npx tsx src/cli.ts check-compatibility cave_scan_engine tile_map_render charset_animation sfx_engine_beside_music sid_play_routine_pattern high_score_table_insert kernal_file_write_seq kernal_file_read_seq error_channel_check frame_sync_loop joystick_edge_detect raster_profile_bars`
62
+
63
+ ```text
64
+ # Compatibility: cave_scan_engine + tile_map_render + charset_animation + sfx_engine_beside_music + sid_play_routine_pattern + high_score_table_insert + kernal_file_write_seq + kernal_file_read_seq + error_channel_check + frame_sync_loop + joystick_edge_detect + raster_profile_bars
65
+
66
+ **Verdict:** WARNINGS
67
+
68
+ Checked with 1 implied prerequisite(s): sid_voice_setup.
69
+
70
+ Unit claims are stated for 3 of 12 techniques; a unit conflict cannot be ruled out for: cave_scan_engine, tile_map_render, charset_animation, high_score_table_insert, kernal_file_write_seq, kernal_file_read_seq, error_channel_check, frame_sync_loop, raster_profile_bars, sid_voice_setup (prerequisite). The zero-page bytes and interrupt vectors a recipe chooses are not checked yet (issue #22, step 8).
71
+
72
+ ## unit_shared (soft): sfx_engine_beside_music × sid_play_routine_pattern
73
+ **Shared:** sid_voice_2
74
+ sid_play_routine_pattern owns sid_voice_2; sfx_engine_beside_music writes it under sid_play_routine_pattern's protocol.
75
+ **Resolution:** sfx_engine_beside_music must follow sid_play_routine_pattern's protocol: write after sid_play_routine_pattern's write in the frame, or run inside sid_play_routine_pattern's interrupt chain.
76
+
77
+ ## shared_register (soft): sfx_engine_beside_music × sid_play_routine_pattern
78
+ **Shared:** SIGVOL, SUREL3, ATDCY3, VCREG3, PWHI3, PWLO3, FREHI3, FRELO3, SUREL2, ATDCY2, VCREG2, PWHI2, PWLO2, FREHI2, FRELO2, SUREL1, ATDCY1, VCREG1, PWHI1, PWLO1, FREHI1, FRELO1
79
+ Both techniques touch register(s) SIGVOL, SUREL3, ATDCY3, VCREG3, PWHI3, PWLO3, FREHI3, FRELO3, SUREL2, ATDCY2, VCREG2, PWHI2, PWLO2, FREHI2, FRELO2, SUREL1, ATDCY1, VCREG1, PWHI1, PWLO1, FREHI1, FRELO1. This says they write the same registers, not that they fight: keep each one's writes in its own raster region, or have one of them own the register and the other read a shadow copy.
80
+
81
+ ## shared_kernal (soft): kernal_file_write_seq × kernal_file_read_seq
82
+ **Shared:** CLRCHN, CLOSE, OPEN, SETNAM, SETLFS
83
+ Both techniques call KERNAL routine(s) CLRCHN, CLOSE, OPEN, SETNAM, SETLFS. Concurrent use may clobber KERNAL state.
84
+
85
+ ## shared_kernal (soft): kernal_file_write_seq × error_channel_check
86
+ **Shared:** CLRCHN, CLOSE, OPEN, SETNAM, SETLFS
87
+ Both techniques call KERNAL routine(s) CLRCHN, CLOSE, OPEN, SETNAM, SETLFS. Concurrent use may clobber KERNAL state.
88
+
89
+ ## shared_kernal (soft): kernal_file_read_seq × error_channel_check
90
+ **Shared:** CHRIN, CLRCHN, CHKIN, CLOSE, OPEN, SETNAM, SETLFS, READST
91
+ Both techniques call KERNAL routine(s) CHRIN, CLRCHN, CHKIN, CLOSE, OPEN, SETNAM, SETLFS, READST. Concurrent use may clobber KERNAL state.
92
+
93
+ ## shared_register (soft): frame_sync_loop × raster_profile_bars
94
+ **Shared:** EXTCOL
95
+ Both techniques touch register(s) EXTCOL. This says they write the same registers, not that they fight: keep each one's writes in its own raster region, or have one of them own the register and the other read a shadow copy.
96
+
97
+
98
+ ## Not covered
99
+ - **cave_scan_engine**: the graph has no registers, KERNAL routines or demands for it; the verdict says nothing about it.
100
+ - **tile_map_render**: the graph has no registers, KERNAL routines or demands for it; the verdict says nothing about it.
101
+ - **high_score_table_insert**: the graph has no registers, KERNAL routines or demands for it; the verdict says nothing about it.
102
+
103
+ ## Shared Infrastructure (info)
104
+ - **sid_voice_setup** (prerequisite, not in the set): required by sfx_engine_beside_music, sid_play_routine_pattern; included in the check as implied. Set it up first.
105
+ - **CHRIN** (KernalRoutine) shared via recipe(s): oscar64-platformer-scaffold, kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader, oscar64-high-score-persist
106
+ - **CHROUT** (KernalRoutine) shared via recipe(s): oscar64-platformer-scaffold, kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader, oscar64-high-score-persist
107
+ - **CLRCHN** (KernalRoutine) shared via recipe(s): oscar64-platformer-scaffold, kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader, oscar64-high-score-persist
108
+ - **CHKOUT** (KernalRoutine) shared via recipe(s): oscar64-platformer-scaffold, kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader, oscar64-high-score-persist
109
+ - **CHKIN** (KernalRoutine) shared via recipe(s): oscar64-platformer-scaffold, kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader, oscar64-high-score-persist
110
+ - **SP6COL** (Register) shared via recipe(s): oscar64-platformer-scaffold
111
+ - **SP5COL** (Register) shared via recipe(s): oscar64-platformer-scaffold
112
+ - **SP4COL** (Register) shared via recipe(s): oscar64-platformer-scaffold
113
+ - **SP3COL** (Register) shared via recipe(s): oscar64-platformer-scaffold
114
+ - **SP2COL** (Register) shared via recipe(s): oscar64-platformer-scaffold
115
+ - **SP1COL** (Register) shared via recipe(s): oscar64-platformer-scaffold
116
+ - **CLOSE** (KernalRoutine) shared via recipe(s): oscar64-platformer-scaffold, kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader, oscar64-high-score-persist
117
+ - **OPEN** (KernalRoutine) shared via recipe(s): oscar64-platformer-scaffold, kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader, oscar64-high-score-persist
118
+ - **SETNAM** (KernalRoutine) shared via recipe(s): oscar64-platformer-scaffold, kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader, oscar64-high-score-persist
119
+ - **SETLFS** (KernalRoutine) shared via recipe(s): oscar64-platformer-scaffold, kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader, oscar64-high-score-persist
120
+ - **SP0COL** (Register) shared via recipe(s): oscar64-platformer-scaffold
121
+ - **DC0F** (Register) shared via recipe(s): oscar64-platformer-scaffold, oscar64-cave-scan
122
+ - **DC07** (Register) shared via recipe(s): oscar64-platformer-scaffold, oscar64-cave-scan
123
+ - **DC06** (Register) shared via recipe(s): oscar64-platformer-scaffold, oscar64-cave-scan
124
+ - **DC03** (Register) shared via recipe(s): oscar64-platformer-scaffold
125
+ - **BGCOL0** (Register) shared via recipe(s): oscar64-platformer-scaffold, oscar64-cave-scan, oscar64-falling-blocks
126
+ - **EXTCOL** (Register) shared via recipe(s): oscar64-platformer-scaffold, oscar64-cave-scan, oscar64-directory-reader, oscar64-falling-blocks
127
+ - **IRQMSK** (Register) shared via recipe(s): oscar64-platformer-scaffold, oscar64-cave-scan
128
+ - **VICIRQ** (Register) shared via recipe(s): oscar64-platformer-scaffold, oscar64-cave-scan
129
+ - **SPENA** (Register) shared via recipe(s): oscar64-platformer-scaffold
130
+ - **RASTER** (Register) shared via recipe(s): oscar64-platformer-scaffold, oscar64-cave-scan, oscar64-directory-reader, oscar64-falling-blocks
131
+ - **SCROLY** (Register) shared via recipe(s): oscar64-platformer-scaffold, oscar64-falling-blocks
132
+ - **MSIGX** (Register) shared via recipe(s): oscar64-platformer-scaffold
133
+ - **M0Y** (Register) shared via recipe(s): oscar64-platformer-scaffold
134
+ - **M0X** (Register) shared via recipe(s): oscar64-platformer-scaffold
135
+ - **RANDOM** (Register) shared via recipe(s): oscar64-platformer-scaffold
136
+ - **SIGVOL** (Register) shared via recipe(s): oscar64-platformer-scaffold
137
+ - **SUREL3** (Register) shared via recipe(s): oscar64-platformer-scaffold
138
+ - **ATDCY3** (Register) shared via recipe(s): oscar64-platformer-scaffold
139
+ - **VCREG3** (Register) shared via recipe(s): oscar64-platformer-scaffold
140
+ - **PWHI3** (Register) shared via recipe(s): oscar64-platformer-scaffold
141
+ - **PWLO3** (Register) shared via recipe(s): oscar64-platformer-scaffold
142
+ - **FREHI3** (Register) shared via recipe(s): oscar64-platformer-scaffold
143
+ - **FRELO3** (Register) shared via recipe(s): oscar64-platformer-scaffold
144
+ - **SUREL2** (Register) shared via recipe(s): oscar64-platformer-scaffold
145
+ - **ATDCY2** (Register) shared via recipe(s): oscar64-platformer-scaffold
146
+ - **VCREG2** (Register) shared via recipe(s): oscar64-platformer-scaffold
147
+ - **PWHI2** (Register) shared via recipe(s): oscar64-platformer-scaffold
148
+ - **PWLO2** (Register) shared via recipe(s): oscar64-platformer-scaffold
149
+ - **FREHI2** (Register) shared via recipe(s): oscar64-platformer-scaffold
150
+ - **FRELO2** (Register) shared via recipe(s): oscar64-platformer-scaffold
151
+ - **SUREL1** (Register) shared via recipe(s): oscar64-platformer-scaffold
152
+ - **ATDCY1** (Register) shared via recipe(s): oscar64-platformer-scaffold
153
+ - **VCREG1** (Register) shared via recipe(s): oscar64-platformer-scaffold
154
+ - **PWHI1** (Register) shared via recipe(s): oscar64-platformer-scaffold
155
+ - **PWLO1** (Register) shared via recipe(s): oscar64-platformer-scaffold
156
+ - **FREHI1** (Register) shared via recipe(s): oscar64-platformer-scaffold
157
+ - **FRELO1** (Register) shared via recipe(s): oscar64-platformer-scaffold
158
+ - **DC02** (Register) shared via recipe(s): oscar64-platformer-scaffold
159
+ - **DC00** (Register) shared via recipe(s): oscar64-platformer-scaffold, oscar64-directory-reader, oscar64-falling-blocks
160
+ - **READST** (KernalRoutine) shared via recipe(s): oscar64-platformer-scaffold, kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader, oscar64-high-score-persist
161
+ - **DC0E** (Register) shared via recipe(s): oscar64-cave-scan, oscar64-falling-blocks
162
+ - **DC05** (Register) shared via recipe(s): oscar64-cave-scan, oscar64-falling-blocks
163
+ - **DC04** (Register) shared via recipe(s): oscar64-cave-scan, oscar64-falling-blocks
164
+ - **DD0F** (Register) shared via recipe(s): kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader
165
+ - **DD0E** (Register) shared via recipe(s): kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader
166
+ - **DD07** (Register) shared via recipe(s): kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader
167
+ - **DD06** (Register) shared via recipe(s): kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader
168
+ - **DD05** (Register) shared via recipe(s): kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader
169
+ - **DD04** (Register) shared via recipe(s): kickassembler-file-io-roundtrip, oscar64-save-load-seq-file, oscar64-directory-reader
170
+ - **raster_discipline**: Multiple raster-discipline techniques present. Verify IRQ stack ordering and timing budget.
171
+ ```
172
+
173
+ What the warnings mean for this program:
174
+
175
+ - sid_voice_2 unit and the shared SID registers (sfx x tune): the tune here
176
+ uses voices 1 and 2 only; effects own voice 3 and run after the play call
177
+ in the same frame, so the resolution the tool asks for holds by layout.
178
+ - Shared KERNAL routines between the three file techniques: they run one
179
+ after another on a static screen, never at once.
180
+ - EXTCOL (frame_sync_loop x raster_profile_bars): no border bars; only the
181
+ verdict writes `$D020`.
182
+
183
+ ## Budget
184
+
185
+ Command: `npx tsx src/cli.ts plan-budget cave_scan_engine:play tile_map_render:transition charset_animation:play sfx_engine_beside_music:play sid_play_routine_pattern:play high_score_table_insert:transition kernal_file_write_seq:transition kernal_file_read_seq:init error_channel_check:transition frame_sync_loop:play joystick_edge_detect:play raster_profile_bars:play --region both --screen on`
186
+
187
+ ```text
188
+ # Budget plan: undetermined
189
+
190
+ Techniques: cave_scan_engine, tile_map_render:transition, charset_animation, sfx_engine_beside_music, sid_play_routine_pattern, high_score_table_insert:transition, kernal_file_write_seq:transition, kernal_file_read_seq:init, error_channel_check:transition, frame_sync_loop, joystick_edge_detect, raster_profile_bars
191
+
192
+ ## play (PAL, 19656 cycles a frame): undetermined
193
+
194
+ Range 19599-19807 + 1075 fixed cycles; floor 0; weakest basis measured-vice; IRQ slots 2.
195
+
196
+ Summed:
197
+ - cave_scan_engine: 18559 (measured-vice, on oscar64-cave-scan (one scan, run every fourth frame; slowest game-cave scan, NTSC, screen on))
198
+ - charset_animation: 196 (measured-vice, on oscar64-charset-animation (one glyph a frame, in the vertical blank))
199
+ - sfx_engine_beside_music: 50-258 (measured-vice, on oscar64-sfx-engine (a frame the engine owns the voice))
200
+ - sid_play_routine_pattern: 327 (measured-vice, on oscar64-sfx-engine (the recipe's stub tune; a real player costs several times more))
201
+ - raster_profile_bars: 467 (measured-vice, on oscar64-raster-profile-bars (worst frame, screen blanked))
202
+
203
+ To measure:
204
+ - frame_sync_loop: the Cost line has no cycles_per_frame (nor cycles_per_line with lines_active); measure it on oscar64-frame-sync-loop
205
+ - joystick_edge_detect: no **Cost:** line; measure it on oscar64-attract-replay
206
+
207
+ Notes:
208
+ - Fixed losses 1075 cycles (badlines 25 × 43 = 1075, lines 51-243 every eighth with YSCROLL 3; arithmetic) charged because charset_animation, sfx_engine_beside_music, sid_play_routine_pattern, raster_profile_bars are not stated as measured with the screen on. A stall takes its cycles wherever the code runs, so the charge is exact unless a figure already holds stalls: cave_scan_engine was measured with the screen on and already holds the stalls that fell inside it, so the charge is too high by that much and the over test counts 0.
209
+ - The low end, 19599 + 1075, passes the 19656-cycle frame, but it is not a floor: the figures of cave_scan_engine, charset_animation, sfx_engine_beside_music, sid_play_routine_pattern, raster_profile_bars are a common frame or a real run's worst, and those frames need not fall together. cave_scan_engine, charset_animation, sid_play_routine_pattern, raster_profile_bars have no typical frame, so their low end is a worst frame. The floor, work every frame plus the loss no figure can hold, is 0 and fits. A frame measured whole, with every member running, would settle it.
210
+ - Unknown is not zero: frame_sync_loop, joystick_edge_detect have no cycles figure, so the verdict cannot be fits.
211
+
212
+ ## play (NTSC, 17095 cycles a frame): undetermined
213
+
214
+ Range 1040-1248 + 1075 fixed cycles; floor 1075; weakest basis measured-vice; IRQ slots 2.
215
+
216
+ Summed:
217
+ - charset_animation: 196 (measured-vice, on oscar64-charset-animation (one glyph a frame, in the vertical blank))
218
+ - sfx_engine_beside_music: 50-258 (measured-vice, on oscar64-sfx-engine (a frame the engine owns the voice))
219
+ - sid_play_routine_pattern: 327 (measured-vice, on oscar64-sfx-engine (the recipe's stub tune; a real player costs several times more))
220
+ - raster_profile_bars: 467 (measured-vice, on oscar64-raster-profile-bars (worst frame, screen blanked))
221
+
222
+ Left out:
223
+ - cave_scan_engine: 18559 cycles, above one frame: a multi-frame operation, not summed (measured on oscar64-cave-scan)
224
+
225
+ To measure:
226
+ - frame_sync_loop: the Cost line has no cycles_per_frame (nor cycles_per_line with lines_active); measure it on oscar64-frame-sync-loop
227
+ - joystick_edge_detect: no **Cost:** line; measure it on oscar64-attract-replay
228
+
229
+ Notes:
230
+ - Fixed losses 1075 cycles (badlines 25 × 43 = 1075, lines 51-243 every eighth with YSCROLL 3; arithmetic) charged because charset_animation, sfx_engine_beside_music, sid_play_routine_pattern, raster_profile_bars are not stated as measured with the screen on. A stall takes its cycles wherever the code runs, so the charge is exact: no summed figure already holds a stall.
231
+ - Unknown is not zero: frame_sync_loop, joystick_edge_detect have no cycles figure, so the verdict cannot be fits.
232
+ - Multi-frame: cave_scan_engine (18559) is above one NTSC frame of 17095 and not summed; spread the work over frames or budget it as its own phase.
233
+ - Cycles per frame are the pages' figures, most measured on PAL; the same code takes about the same cycles on NTSC, against a 17,095-cycle frame.
234
+
235
+ ## transition (PAL, 19656 cycles a frame): undetermined
236
+
237
+ Range 749 + 1075 fixed cycles; floor 1075; weakest basis arithmetic; IRQ slots 0.
238
+
239
+ Summed:
240
+ - tile_map_render: 268 (arithmetic, on oscar64-tile-map-render (one column edge, 11 metatiles))
241
+ - high_score_table_insert: 481 (measured-vice, on kickassembler-high-score-insert (worst insert, once a round, screen blanked))
242
+
243
+ To measure:
244
+ - kernal_file_write_seq: no **Cost:** line; measure it on kickassembler-file-io-roundtrip
245
+ - error_channel_check: no **Cost:** line; measure it on kickassembler-dos-error-codes
246
+
247
+ Notes:
248
+ - Fixed losses 1075 cycles (badlines 25 × 43 = 1075, lines 51-243 every eighth with YSCROLL 3; arithmetic) charged because tile_map_render, high_score_table_insert are not stated as measured with the screen on. A stall takes its cycles wherever the code runs, so the charge is exact: no summed figure already holds a stall.
249
+ - Unknown is not zero: kernal_file_write_seq, error_channel_check have no cycles figure, so the verdict cannot be fits.
250
+ - The transition phase is judged against one frame too; a transition that takes several frames drops frames, which may be acceptable there.
251
+
252
+ ## transition (NTSC, 17095 cycles a frame): undetermined
253
+
254
+ Range 749 + 1075 fixed cycles; floor 1075; weakest basis arithmetic; IRQ slots 0.
255
+
256
+ Summed:
257
+ - tile_map_render: 268 (arithmetic, on oscar64-tile-map-render (one column edge, 11 metatiles))
258
+ - high_score_table_insert: 481 (measured-vice, on kickassembler-high-score-insert (worst insert, once a round, screen blanked))
259
+
260
+ To measure:
261
+ - kernal_file_write_seq: no **Cost:** line; measure it on kickassembler-file-io-roundtrip
262
+ - error_channel_check: no **Cost:** line; measure it on kickassembler-dos-error-codes
263
+
264
+ Notes:
265
+ - Fixed losses 1075 cycles (badlines 25 × 43 = 1075, lines 51-243 every eighth with YSCROLL 3; arithmetic) charged because tile_map_render, high_score_table_insert are not stated as measured with the screen on. A stall takes its cycles wherever the code runs, so the charge is exact: no summed figure already holds a stall.
266
+ - Unknown is not zero: kernal_file_write_seq, error_channel_check have no cycles figure, so the verdict cannot be fits.
267
+ - The transition phase is judged against one frame too; a transition that takes several frames drops frames, which may be acceptable there.
268
+ - Cycles per frame are the pages' figures, most measured on PAL; the same code takes about the same cycles on NTSC, against a 17,095-cycle frame.
269
+
270
+ ## init (PAL, 19656 cycles a frame): undetermined
271
+
272
+ Range 0 cycles; floor 0; weakest basis (nothing summed); IRQ slots 0.
273
+
274
+ To measure:
275
+ - kernal_file_read_seq: no **Cost:** line; measure it on kickassembler-file-io-roundtrip
276
+
277
+ Notes:
278
+ - Unknown is not zero: kernal_file_read_seq has no cycles figure, so the verdict cannot be fits.
279
+ - The init phase is judged against one frame too; an init that takes several frames drops frames, which may be acceptable there.
280
+
281
+ ## init (NTSC, 17095 cycles a frame): undetermined
282
+
283
+ Range 0 cycles; floor 0; weakest basis (nothing summed); IRQ slots 0.
284
+
285
+ To measure:
286
+ - kernal_file_read_seq: no **Cost:** line; measure it on kickassembler-file-io-roundtrip
287
+
288
+ Notes:
289
+ - Unknown is not zero: kernal_file_read_seq has no cycles figure, so the verdict cannot be fits.
290
+ - The init phase is judged against one frame too; an init that takes several frames drops frames, which may be acceptable there.
291
+ - Cycles per frame are the pages' figures, most measured on PAL; the same code takes about the same cycles on NTSC, against a 17,095-cycle frame.
292
+
293
+ ## Bytes
294
+
295
+ No member states a byte figure that can be summed.
296
+ - frame_sync_loop: 985 bytes left out, the whole program, not the technique
297
+
298
+ ## Assumptions
299
+
300
+ - Region PAL and NTSC: PAL 19656, NTSC 17095 cycles a frame.
301
+ - Screen on: unless every summed figure says it was measured with the screen on, the 25 badlines × 43 cycles outside any band charge are charged.
302
+ - Low end: each member's cycles_per_frame_typical where the page states one (a common frame, or a real run's worst frame), else its worst frame. It is not a floor. Over is judged on the floor: band and per-line charges, which run every frame, plus the badline loss no summed figure can already hold.
303
+ - Each figure is the technique's own Cost line, measured on the recipe it names: another implementation can cost more or less.
304
+ - Claims, zero page and memory are not judged here; c64_check_compatibility judges claims and zero page.
305
+ ```
306
+
307
+ The frame meter measures the real figure once the loop runs: worst, and
308
+ typical as the median of the autopilot's play frames. Both are recorded in
309
+ README.md with the model and the command.
310
+
311
+ What is unknown, and how the meter settles it: the tool sums a whole cave
312
+ scan (18,559 cycles, the recipe's slowest scan) into one PAL play frame and
313
+ drops it on NTSC as multi-frame. This program splits each scan into four
314
+ slices of five rows, one slice a display frame, so the play frame holds a
315
+ quarter scan plus the redraw of the cells it changed, the tune, the effects
316
+ and the glyph animation. The meter over the autopilot's play frames gives
317
+ the real worst and typical on both models. The RLE decode (the recipe's C
318
+ decoder: 28,193 to 50,447 cycles a room) is a transition on a static
319
+ screen; it is timed once with CIA1 timer B and printed, not metered.
320
+
321
+ ## Memory and screen
322
+
323
+ - `$0801`-`$087F` Oscar64 start-up; code and data `$0880`-`$1FFF`, then
324
+ `$2800`-`$9FFF` for the rest (`#pragma region`, the split the
325
+ bitmap-koala-viewer recipe uses).
326
+ - `$2000`-`$27FF` the RAM character set: the ROM set copied at start-up
327
+ (text, digits and the meter's F, W, T keep their ROM glyphs), codes
328
+ `$40`-`$5F` replaced by the cave's glyphs. `$D018` = `$18`.
329
+ - Screen `$0400`: row 0 the HUD, rows 1-22 the cave (cell x, y at row
330
+ y + 1), row 23 messages, row 24 columns 0-19 the cave name; columns 20-39
331
+ belong to the meter in AUTOPILOT builds.
332
+ - No sprites, no raster IRQ. The KERNAL IRQ is off (`SEI`) except during
333
+ disk calls, and `SEI` again after each one.
334
+ - CIA2 timer A is the meter's; CIA1 timer B times the RLE decode once.
335
+
336
+ ## Autopilot and checks
337
+
338
+ - The script is one string of moves per cave start (L U R D, `-` still),
339
+ keyed to the game's own counter of starts and held on the synthetic
340
+ joystick byte for the four display frames of each cave frame. Start 1
341
+ digs, pushes a boulder, collects cave 1's quota and enters the open exit;
342
+ starts 2 to 4 are cave 2 after each lost life, each ending under a falling
343
+ boulder, so the restart path runs twice and all three lives are played.
344
+ The script then enters a name; the table is saved to the shot's fresh D64
345
+ (`SHOT_DISK := 1`), read back and compared, and shown.
346
+ - `tools/gen.py` holds the caves, packs them, and runs a Python model of
347
+ the same rules over the same script. It writes `src/gen_caves.h` (the RLE
348
+ streams), `src/gen_autopilot.h` (the script and the model's expected
349
+ values) and `src/gen_notes.h` (the note tables).
350
+ - Verdict, after the table is shown, outside the meter, one letter per
351
+ failed test: the fold chained over every cave left, score, gems, cave,
352
+ play frames, cave starts, the table row, the decodes, the scan meeting the
353
+ player once a cave frame, the screen against the cave at every cave end,
354
+ dropped frames (the `$D019` raster latch), and the save's read-back.
355
+ `$02FF` and the border. A watchdog ends a game that runs past the model's
356
+ play frames.
357
+ - FORCE_FAULT flips a byte of the save as it is read back: the verdict turns
358
+ red and the table shows READ BACK BAD. `make selftest-scan` builds with
359
+ SCAN_FLAG=0 and must fail with E.
360
+ - expect.json: verdict, the HUD text, the table box text, cave cells the
361
+ model places, the `same` area over the cave, the meter with the recorded
362
+ frame count.
363
+ - A separate `make disktest` proves the save with true drive emulation: the
364
+ autopilot build writes the table to a fresh D64, then saves again over
365
+ it; a cold VICE boots the release PRG from that D64, which loads the
366
+ table and shows it.
367
+
368
+ ## Decisions and open questions
369
+
370
+ - One cave frame every four display frames on both models, so the game is
371
+ the same on PAL and NTSC and one model run serves both; NTSC plays 20 %
372
+ faster in real time (pal_ntsc_tempo_mismatch). The time counter ticks every
373
+ 12 cave frames. The tune's note lengths are in frames, so it plays 20 %
374
+ faster on NTSC too; the note frequencies come from a PAL or NTSC table.
375
+ - The meter records at most 255 frames; the script is kept to fit (240).
376
+ - Measured (make shot check, 2026-09-23): worst 13,619 / typical 6,529
377
+ cycles on PAL, 13,832 / 6,786 on NTSC; the worst is the slice where 16
378
+ boulders fall and overflow the dirty list. About 440 cycles a moving
379
+ object, so about 20 a slice fit NTSC. The overflow queues rows, redrawn
380
+ two a frame. A first build passed the cell
381
+ pointer to `cell()`; Oscar64 then computed it for every cell before the
382
+ test and the scan loop took 42 cycles a cell (typical frame 11,812).
383
+ Passing row and column made it 17 (counted from the generated code).
384
+ - Settled: first_open_after_reset_hangs_on_pal. `DISK_WAIT` waits 50 frames
385
+ before the first OPEN (main.c). `make disktest` ran on PAL and NTSC with a
386
+ true-drive 1541: the start-up read, two saves (the second replacing the
387
+ file) and a cold boot that loads it, all without a hang. It is a phase
388
+ effect, so any change to the code before the first OPEN needs that test again.