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,654 @@
1
+ ---
2
+ recipe: difficulty-tables
3
+ toolchain: oscar64
4
+ output_format: PRG
5
+ region: both
6
+ techniques: [difficulty_ramp_tables, pal_ntsc_detection, object_pool, lfsr_random]
7
+ file_formats: [PRG]
8
+ uses_registers: [D011, D012, D020, D021, DC04, DC05, DC06, DC07, DC0D, DC0E, DC0F]
9
+ uses_kernal: []
10
+ ---
11
+
12
+ <!-- doc-type: recipe -->
13
+
14
+ # Oscar64 difficulty tables: a level ramp read from data, region-compensated, timed on both models
15
+
16
+ ## Synopsis
17
+
18
+ A six-row level table (enemy speed in 8.8 pixels per frame, spawn
19
+ interval in frames, active count, coin quota, a hazard flag) drives a
20
+ small falling-enemy game from data. Enemies come out of an eight-slot
21
+ object pool at the row's interval, no more than the row's count alive at
22
+ once; each one that lands leaves a coin, and an autopilot player collects
23
+ the coins. The level advances when the row's quota is collected, which is
24
+ something the player does, not a timer. From level 3 the flag column
25
+ turns on a second hazard, a spike that falls half again as fast. Past the
26
+ last row the last row is held.
27
+
28
+ The table is written for 50 frames a second. `COMPENSATE=1` (the
29
+ default) asks `pal_ntsc_detection` which chip this is and rescales the
30
+ interval by 6/5 and the speed by 5/6 on NTSC, so the ramp takes the same
31
+ real time on both machines. The program counts frames and runs CIA1
32
+ timers A and B as a 32-bit φ2 clock, prints the frame and the millisecond
33
+ at which level 3 began, and grades that millisecond against a compiled-in
34
+ PAL reference: `$02FF` holds `01` and the border goes green if it is
35
+ within `TOL_MS`, else `02` and red. Built with `-dCOMPENSATE=0` the same
36
+ program shows the raw table: the same frame count on both models and an
37
+ NTSC time about five sixths of the PAL one. It implements
38
+ `difficulty_ramp_tables` (`techniques/logic.md`); the pattern is
39
+ `difficulty_ramp` in `../../game-design/enemy-behaviour-and-difficulty.md`.
40
+
41
+ ## Source
42
+
43
+ ```c
44
+ // difficulty-tables.c
45
+ // A level table read from data: enemy speed (8.8 pixels per frame), spawn
46
+ // interval (frames), active count and a hazard flag, one row per level, the
47
+ // last row held. Enemies fall from an object pool; each one that reaches
48
+ // the ground drops a coin, and the level advances when the player has
49
+ // collected the row's coin quota. The table is written for PAL frames;
50
+ // COMPENSATE=1 rescales interval and speed on NTSC so the ramp takes the
51
+ // same real time on both. The frame count and CIA-timed elapsed time at
52
+ // which level 3 begins are printed and graded at $02FF.
53
+ #include <c64/vic.h>
54
+ #include <c64/cia.h>
55
+
56
+ #ifndef COMPENSATE
57
+ #define COMPENSATE 1 // 1: rescale the table on NTSC, 0: run raw
58
+ #endif
59
+ #ifndef REF_MS
60
+ #define REF_MS 22493 // ms to level 3 measured on PAL, compensated build
61
+ #endif
62
+ #ifndef TOL_MS
63
+ #define TOL_MS 500 // tolerance on REF_MS, both regions
64
+ #endif
65
+
66
+ #define SCREEN ((char *)0x0400)
67
+ #define COLOUR ((char *)0xd800)
68
+ #define RESULT (*(volatile char *)0x02ff)
69
+
70
+ #define CODE_PASS 0x01
71
+ #define CODE_FAIL 0x02
72
+
73
+ // --- The level table -----------------------------------------------------
74
+ // Columns: speed in 8.8 pixels per frame, spawn interval in frames, most
75
+ // enemies alive at once, coins to collect, flags. Written for 50 frames a
76
+ // second. FLAG_SPIKE turns on the second hazard type from level 3.
77
+ #define FLAG_SPIKE 0x01
78
+
79
+ struct LevelRow { unsigned speed; char interval; char active; char quota; char flags; };
80
+
81
+ #define LEVELS 6
82
+ static const struct LevelRow level_table[LEVELS] = {
83
+ { 0x0080, 60, 3, 6, 0 }, // 1: 0.50 px/frame
84
+ { 0x00c0, 45, 3, 6, 0 }, // 2: 0.75
85
+ { 0x0100, 40, 4, 6, FLAG_SPIKE }, // 3: 1.00, spikes appear
86
+ { 0x0140, 32, 4, 6, FLAG_SPIKE }, // 4: 1.25
87
+ { 0x0180, 28, 5, 6, FLAG_SPIKE }, // 5: 1.50
88
+ { 0x01c0, 24, 5, 6, FLAG_SPIKE }, // 6: 1.75, held for ever after
89
+ };
90
+
91
+ // The row in use, copied at level start with the region scaler applied.
92
+ static unsigned cur_speed;
93
+ static char cur_interval, cur_active, cur_quota, cur_flags;
94
+ static char level; // 1-based
95
+ static char coins; // collected this level
96
+
97
+ // --- Region ----------------------------------------------------------------
98
+ static char is_ntsc;
99
+
100
+ // Highest $D012 value seen inside one RST8 band: $37 on a 6569, $06 or $05
101
+ // on a 6567. Interrupts are already off when this runs.
102
+ static char last_raster_line(void)
103
+ {
104
+ char top = 0;
105
+ while (vic.ctrl1 & 0x80) ;
106
+ while (!(vic.ctrl1 & 0x80)) ;
107
+ while (vic.ctrl1 & 0x80) {
108
+ char r = vic.raster;
109
+ if (r > top) top = r;
110
+ }
111
+ return top;
112
+ }
113
+
114
+ // Frames written for 50 Hz become frames at 60 Hz: v * 6 / 5.
115
+ static char scale_frames(char v)
116
+ {
117
+ if (COMPENSATE && is_ntsc)
118
+ return (char)(((unsigned)v * 6 + 2) / 5);
119
+ return v;
120
+ }
121
+
122
+ // Pixels per frame written for 50 Hz become pixels per frame at 60 Hz:
123
+ // v * 5 / 6, in 8.8 so the fraction survives.
124
+ static unsigned scale_speed(unsigned v)
125
+ {
126
+ if (COMPENSATE && is_ntsc)
127
+ return (v * 5 + 3) / 6;
128
+ return v;
129
+ }
130
+
131
+ static void level_start(char n)
132
+ {
133
+ char i = n - 1;
134
+ if (i >= LEVELS) i = LEVELS - 1; // hold the last row, never read past
135
+ level = n;
136
+ coins = 0;
137
+ cur_speed = scale_speed(level_table[i].speed);
138
+ cur_interval = scale_frames(level_table[i].interval);
139
+ cur_active = level_table[i].active;
140
+ cur_quota = level_table[i].quota;
141
+ cur_flags = level_table[i].flags;
142
+ }
143
+
144
+ // --- Random ----------------------------------------------------------------
145
+ // 16-bit Galois LFSR. The seed must not be zero or it stays zero for ever.
146
+ #define LFSR_SEED 0xace1
147
+ static unsigned lfsr = LFSR_SEED;
148
+ static char rnd(void)
149
+ {
150
+ char bit = lfsr & 1;
151
+ lfsr >>= 1;
152
+ if (bit) lfsr ^= 0xb400;
153
+ return (char)lfsr;
154
+ }
155
+
156
+ // --- Object pool -----------------------------------------------------------
157
+ #define MAX_OBJ 8
158
+ #define OBJ_FREE 0
159
+ #define OBJ_ENEMY 1
160
+ #define OBJ_SPIKE 2
161
+ #define OBJ_COIN 3
162
+
163
+ #define TOP_Y 40
164
+ #define GROUND_Y 176 // row 22 of the text screen
165
+ #define PLAYER_Y 192 // row 24, the last row
166
+ #define COIN_DY 0x0200 // coins fall at 2 px/frame (scaled)
167
+
168
+ static char obj_state[MAX_OBJ];
169
+ static char obj_x[MAX_OBJ]; // 0..159, playfield pixels
170
+ static unsigned obj_y[MAX_OBJ]; // 8.8
171
+ static unsigned obj_dy[MAX_OBJ]; // 8.8 per frame
172
+
173
+ static char pool_alloc(void)
174
+ {
175
+ for (char i = 0; i < MAX_OBJ; i++)
176
+ if (obj_state[i] == OBJ_FREE) return i;
177
+ return 0xff;
178
+ }
179
+
180
+ static char count_state(char lo, char hi)
181
+ {
182
+ char n = 0;
183
+ for (char i = 0; i < MAX_OBJ; i++)
184
+ if (obj_state[i] >= lo && obj_state[i] <= hi) n++;
185
+ return n;
186
+ }
187
+
188
+ static void spawn(char kind, char x, unsigned y, unsigned dy)
189
+ {
190
+ char s = pool_alloc();
191
+ if (s == 0xff) return;
192
+ obj_state[s] = kind;
193
+ obj_x[s] = x;
194
+ obj_y[s] = y;
195
+ obj_dy[s] = dy;
196
+ }
197
+
198
+ // --- Player and spawner ------------------------------------------------------
199
+ static char player_x = 80;
200
+ static char spawn_timer;
201
+ static char coins_total;
202
+
203
+ static void spawner(void)
204
+ {
205
+ if (spawn_timer) { spawn_timer--; return; }
206
+ if (count_state(OBJ_ENEMY, OBJ_SPIKE) >= cur_active) return;
207
+ spawn_timer = cur_interval;
208
+ char x = (rnd() & 0x7f) + 16; // 16..143 of the 160-wide field
209
+ // Every fourth spawn is a spike once the row's flag allows it: it
210
+ // falls half again as fast and is the level's new problem.
211
+ char kind = OBJ_ENEMY;
212
+ unsigned dy = cur_speed;
213
+ if ((cur_flags & FLAG_SPIKE) && (rnd() & 3) == 0) {
214
+ kind = OBJ_SPIKE;
215
+ dy = cur_speed + (cur_speed >> 1);
216
+ }
217
+ spawn(kind, x, (unsigned)TOP_Y << 8, dy);
218
+ }
219
+
220
+ // The autopilot: walk towards the lowest object at two pixels a frame,
221
+ // since every enemy becomes a coin where it lands, and collect the coin
222
+ // when it reaches the player's row within eight pixels.
223
+ static void player(void)
224
+ {
225
+ char best = 0xff;
226
+ unsigned besty = 0;
227
+ for (char i = 0; i < MAX_OBJ; i++)
228
+ if (obj_state[i] != OBJ_FREE && obj_y[i] >= besty) { besty = obj_y[i]; best = i; }
229
+ if (best == 0xff) return;
230
+ char tx = obj_x[best];
231
+ if (player_x + 2 <= tx) player_x += 2;
232
+ else if (player_x >= tx + 2) player_x -= 2;
233
+ else player_x = tx;
234
+ }
235
+
236
+ static void tick_objects(void)
237
+ {
238
+ for (char i = 0; i < MAX_OBJ; i++) {
239
+ char st = obj_state[i];
240
+ if (st == OBJ_FREE) continue;
241
+ obj_y[i] += obj_dy[i];
242
+ char y = obj_y[i] >> 8;
243
+ if (st == OBJ_COIN) {
244
+ if (y >= PLAYER_Y) {
245
+ char dx = obj_x[i] > player_x ? obj_x[i] - player_x : player_x - obj_x[i];
246
+ if (dx <= 8) { coins++; coins_total++; }
247
+ obj_state[i] = OBJ_FREE;
248
+ }
249
+ } else if (y >= GROUND_Y) {
250
+ // The enemy lands and leaves a coin behind.
251
+ obj_state[i] = OBJ_COIN;
252
+ obj_dy[i] = scale_speed(COIN_DY);
253
+ }
254
+ }
255
+ }
256
+
257
+ // --- Screen ------------------------------------------------------------------
258
+ static const char hex_glyph[16] = {
259
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
260
+ 0x38, 0x39, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06
261
+ };
262
+
263
+ static void put_str(char row, char col, const char *s)
264
+ {
265
+ char *p = SCREEN + row * 40 + col;
266
+ while (*s) { char c = *s++; *p++ = (c >= 0x40) ? c & 0x3f : c; }
267
+ }
268
+
269
+ // Right-aligned unsigned long in a field of width w, space padded.
270
+ static void put_num(char row, char col, unsigned long v, char w)
271
+ {
272
+ char *p = SCREEN + row * 40 + col + w;
273
+ do { *--p = 0x30 + (char)(v % 10); v /= 10; } while (v && p > SCREEN + row * 40 + col);
274
+ while (p > SCREEN + row * 40 + col) *--p = 0x20;
275
+ }
276
+
277
+ static void draw_field(void)
278
+ {
279
+ // Playfield rows 5..24: row = y / 8, column = x / 4. Clamp y so the
280
+ // write can never leave the screen (past 1000 lies the program and,
281
+ // in colour RAM's mirror, the CIA registers).
282
+ for (unsigned i = 5 * 40; i < 1000; i++) SCREEN[i] = 0x20;
283
+ for (char i = 0; i < MAX_OBJ; i++) {
284
+ char st = obj_state[i];
285
+ if (st == OBJ_FREE) continue;
286
+ char y = obj_y[i] >> 8;
287
+ if (y > 199) y = 199;
288
+ unsigned pos = (unsigned)(y >> 3) * 40 + (obj_x[i] >> 2);
289
+ SCREEN[pos] = (st == OBJ_ENEMY) ? 0x57 : (st == OBJ_SPIKE) ? 0x5e : 0x51;
290
+ COLOUR[pos] = (st == OBJ_ENEMY) ? VCOL_LT_RED : (st == OBJ_SPIKE) ? VCOL_YELLOW : VCOL_LT_GREEN;
291
+ }
292
+ unsigned pp = 24 * 40 + (player_x >> 2);
293
+ SCREEN[pp] = 0x5a; COLOUR[pp] = VCOL_WHITE;
294
+ }
295
+
296
+ // --- Time --------------------------------------------------------------------
297
+ // CIA1 timer A counts phi2 from $FFFF; timer B counts A's underflows.
298
+ static void clock_start(void)
299
+ {
300
+ cia1.cra = 0x00; cia1.crb = 0x00;
301
+ cia1.ta = 0xffff; cia1.tb = 0xffff;
302
+ cia1.crb = 0x51; // count timer A underflows
303
+ cia1.cra = 0x11; // count phi2
304
+ }
305
+
306
+ static unsigned long clock_read(void)
307
+ {
308
+ cia1.cra = 0x00; cia1.crb = 0x00;
309
+ char alo = ((volatile char *)0xdc04)[0], ahi = ((volatile char *)0xdc04)[1];
310
+ char blo = ((volatile char *)0xdc06)[0], bhi = ((volatile char *)0xdc06)[1];
311
+ unsigned a = 0xffff - ((unsigned)ahi << 8 | alo);
312
+ unsigned b = 0xffff - ((unsigned)bhi << 8 | blo);
313
+ cia1.cra = 0x01; cia1.crb = 0x41; // restart with LOAD (bit 4) clear: the count continues
314
+ return ((unsigned long)b << 16) | a;
315
+ }
316
+
317
+ // phi2 cycles to milliseconds: 985,248 Hz PAL, 1,022,727 Hz NTSC.
318
+ static unsigned long cycles_to_ms(unsigned long c)
319
+ {
320
+ return c / (is_ntsc ? 1023 : 985);
321
+ }
322
+
323
+ // Cost of one level-start row read with the scaler, CIA1 timer A over
324
+ // 100 calls, before the game clock starts. __noinline keeps the call real.
325
+ __noinline static void row_read_call(void) { level_start(3); }
326
+ __noinline static void spawner_call(void) { spawner(); }
327
+ // The spawner's worst frame: the interval has run out, a slot is free and
328
+ // the count is under the cap, so it counts the pool, rolls the LFSR,
329
+ // allocates and spawns. Slot 0 is freed before each call so every call
330
+ // is a spawning one.
331
+ __noinline static void spawn_frame_call(void)
332
+ {
333
+ spawn_timer = 0; obj_state[0] = OBJ_FREE; cur_active = MAX_OBJ;
334
+ spawner();
335
+ }
336
+ static unsigned cost_of(void (*fn)(void))
337
+ {
338
+ cia1.cra = 0x00;
339
+ cia1.ta = 0xffff;
340
+ cia1.cra = 0x11;
341
+ for (char i = 0; i < 100; i++) fn();
342
+ cia1.cra = 0x00;
343
+ return (0xffff - cia1.ta) / 100;
344
+ }
345
+
346
+ static void wait_frame(void)
347
+ {
348
+ while (vic.raster == 250) ;
349
+ while (vic.raster != 250) ;
350
+ }
351
+
352
+ int main(void)
353
+ {
354
+ __asm { sei }
355
+ cia1.icr = 0x7f; // no CIA interrupts, timer A is ours
356
+ is_ntsc = last_raster_line() != 0x37;
357
+
358
+ for (unsigned i = 0; i < 1000; i++) { SCREEN[i] = 0x20; COLOUR[i] = VCOL_WHITE; }
359
+ vic.color_back = VCOL_BLACK;
360
+ vic.color_border = VCOL_BLACK;
361
+ put_str(0, 0, is_ntsc ? "NTSC" : "PAL ");
362
+ put_str(0, 5, COMPENSATE ? "COMPENSATED " : "UNCOMPENSATED");
363
+ put_str(1, 0, "LEVEL");
364
+ put_str(1, 9, "COINS");
365
+ put_str(1, 18, "FRAMES");
366
+ put_str(2, 0, "L3 AT FRAME");
367
+ put_str(2, 18, "MS");
368
+ put_str(2, 28, "SPAWN");
369
+
370
+ unsigned row_cost = cost_of(row_read_call);
371
+ spawn_timer = 200; // the ordinary frame: count down, return
372
+ unsigned tick_cost = cost_of(spawner_call);
373
+ level_start(3); // spikes on, so the spike branch is in the figure
374
+ unsigned spawn_cost = cost_of(spawn_frame_call);
375
+ for (char i = 0; i < MAX_OBJ; i++) obj_state[i] = OBJ_FREE;
376
+ lfsr = LFSR_SEED; // the harness rolled it; the game starts from the seed
377
+ spawn_timer = 0;
378
+ put_str(4, 24, "ROW READ");
379
+ put_num(4, 33, row_cost, 4);
380
+ put_str(3, 27, "TICK");
381
+ put_num(3, 32, tick_cost, 4);
382
+
383
+ unsigned frames = 0, l3_frames = 0;
384
+ unsigned long l3_ms = 0, l3_cyc = 0;
385
+ char stop = 0; // frames left to run after level 3
386
+
387
+ level_start(1);
388
+ clock_start();
389
+ for (;;) {
390
+ wait_frame();
391
+ frames++;
392
+ spawner();
393
+ player();
394
+ tick_objects();
395
+ if (coins >= cur_quota) {
396
+ level_start(level + 1);
397
+ if (level == 3) {
398
+ l3_frames = frames;
399
+ l3_cyc = clock_read();
400
+ l3_ms = cycles_to_ms(l3_cyc);
401
+ stop = 200;
402
+ }
403
+ }
404
+ if (stop && --stop == 0) break;
405
+ if ((frames & 3) == 0) {
406
+ draw_field();
407
+ put_num(1, 6, level, 2);
408
+ put_num(1, 15, coins, 2);
409
+ put_num(1, 25, frames, 5);
410
+ }
411
+ }
412
+
413
+ // Verdict: the compensated time to level 3 must sit within TOL_MS of
414
+ // REF_MS on either region; uncompensated NTSC is expected 5/6 of it.
415
+ unsigned long want = REF_MS;
416
+ if (!COMPENSATE && is_ntsc) want = (unsigned long)REF_MS * 5 / 6;
417
+ unsigned long diff = l3_ms > want ? l3_ms - want : want - l3_ms;
418
+ char code = diff <= TOL_MS ? CODE_PASS : CODE_FAIL;
419
+
420
+ draw_field();
421
+ put_num(1, 6, level, 2);
422
+ put_num(1, 15, coins, 2);
423
+ put_num(1, 25, frames, 5);
424
+ put_num(2, 12, l3_frames, 5);
425
+ put_num(2, 20, l3_ms, 6);
426
+ put_num(2, 34, spawn_cost, 4);
427
+ put_str(4, 0, "L3 AT CYCLE");
428
+ put_num(4, 12, l3_cyc, 9);
429
+ put_str(3, 0, "WANT");
430
+ put_num(3, 5, want, 6);
431
+ put_str(3, 12, "RESULT");
432
+ SCREEN[3 * 40 + 19] = hex_glyph[code >> 4];
433
+ SCREEN[3 * 40 + 20] = hex_glyph[code & 15];
434
+ put_str(3, 22, code == CODE_PASS ? "PASS" : "FAIL");
435
+ RESULT = code;
436
+ vic.color_border = code == CODE_PASS ? VCOL_GREEN : VCOL_RED;
437
+ for (;;) ;
438
+ return 0;
439
+ }
440
+ ```
441
+
442
+ ## Build
443
+
444
+ ```bash
445
+ oscar64 -tm=c64 -O2 -o=difficulty-tables.prg difficulty-tables.c
446
+ ```
447
+
448
+ Produces `difficulty-tables.prg`, 2,931 bytes including the BASIC stub
449
+ (Oscar64 build 2026-05-19). That is the invocation the pinned run used;
450
+ `npm run check:listings` builds the listing the same way. The other
451
+ build this page measures:
452
+
453
+ ```bash
454
+ oscar64 -tm=c64 -O2 -dCOMPENSATE=0 -o=difficulty-tables-raw.prg difficulty-tables.c # 2,805 bytes
455
+ ```
456
+
457
+ The pinned run, on each model:
458
+
459
+ ```bash
460
+ x64sc -default -warp +sound +autostart-delay-random -autostartprgmode 1 -limitcycles 40000000 -exitscreenshot difficulty-tables.png -autostart difficulty-tables.prg
461
+ ```
462
+
463
+ Add `-model ntsc` for the NTSC picture. The game reaches level 3 at
464
+ about 22 million cycles on either model and stops 200 frames later, so
465
+ the screen is final well inside the limit. `REF_MS` is the PAL figure
466
+ measured below; change the table and it must be measured again.
467
+
468
+ ## Expected output
469
+
470
+ Black screen, green border. Rows 0 to 4 are text; rows 5 to 24 are the
471
+ playfield, enemies as red circles, spikes as yellow chevrons, coins as
472
+ green circles and the player as a white diamond on the bottom row.
473
+ Decoded from the pinned pictures with the character ROM:
474
+
475
+ PAL, `screenshots/difficulty-tables.png`:
476
+
477
+ ```
478
+ PAL COMPENSATED
479
+ LEVEL 3 COINS 3 FRAMES 1327
480
+ L3 AT FRAME 1128 MS 22497 SPAWN 409
481
+ WANT 22493 RESULT 01 PASS TICK 51
482
+ L3 AT CYCLE 22159699 ROW READ 181
483
+ ```
484
+
485
+ NTSC, `screenshots/difficulty-tables-ntsc.png`:
486
+
487
+ ```
488
+ NTSC COMPENSATED
489
+ LEVEL 3 COINS 2 FRAMES 1551
490
+ L3 AT FRAME 1352 MS 22577 SPAWN 412
491
+ WANT 22493 RESULT 01 PASS TICK 53
492
+ L3 AT CYCLE 23097254 ROW READ 267
493
+ ```
494
+
495
+ The `-dCOMPENSATE=0` build was run on both models and not pinned:
496
+
497
+ | Build | Model | Level 3 at frame | Level 3 at φ2 cycle | Milliseconds | WANT | Verdict |
498
+ |---|---|---|---|---|---|---|
499
+ | compensated | PAL | 1128 | 22,159,699 | 22,497 | 22,493 | 01 PASS |
500
+ | compensated | NTSC | 1352 | 23,097,254 | 22,577 | 22,493 | 01 PASS |
501
+ | uncompensated | PAL | 1128 | 22,166,254 | 22,503 | 22,493 | 01 PASS |
502
+ | uncompensated | NTSC | 1128 | 19,281,170 | 18,847 | 18,744 | 01 PASS |
503
+
504
+ Read across the rows. Uncompensated, the frame count is the same on both
505
+ models, because the simulation is deterministic and counts frames, and
506
+ the NTSC run reaches level 3 in 18,847 ms against 22,503 ms on PAL: a
507
+ ratio of 0.838. Compensated, the frame counts differ by the rescaling
508
+ (1352 / 1128 = 1.199) and the printed times agree within 80 ms, 0.4 %
509
+ of the run; with the exact clock rates applied to the cycle column
510
+ (see "The clock" below) the gap is 93 ms. `TOL_MS` is 500, so the
511
+ verdict has room for a different table row rounding and none for the
512
+ uncompensated 3.7 second gap. The cycle figures move by a few thousand
513
+ between builds of this program because the clock starts part way
514
+ through a frame and the first `wait_frame` absorbs the rest of it; the
515
+ frame counts do not move.
516
+
517
+ Three smaller figures are on the screen because the technique page
518
+ quotes them. `ROW READ` is the cost of one `level_start` call, the
519
+ table read plus the scaler: 181 cycles on PAL and 267 on NTSC in the
520
+ compensated build, where the NTSC path runs the multiply and divide;
521
+ 113 and 118 uncompensated. `TICK` is the spawner's cost on an ordinary
522
+ frame, when the interval timer is counting and no spawn happens: 51
523
+ cycles on PAL, 53 on NTSC. `SPAWN` is the spawner's worst frame, when
524
+ the interval has run out and it counts the pool, rolls the LFSR once or
525
+ twice, allocates a slot and fills it: 409 cycles on PAL and 412 on
526
+ NTSC in the compensated build, 410 and 415 uncompensated. All three are
527
+ averages of one hundred calls timed by CIA1 timer A with the screen on,
528
+ so badline stalls are inside them and they wander by a cycle or two
529
+ between runs and models; the `SPAWN` average includes the spike branch
530
+ as often as the LFSR takes it, and about fifteen cycles of the wrapper
531
+ that resets the timer and frees a slot before each call.
532
+
533
+ ## Why this works
534
+
535
+ **The table is the ramp.** `level_table` is the only place a speed, an
536
+ interval, a count or a flag is written. `level_start` copies one row
537
+ into `cur_*` at the start of each level, and the spawner and the movers
538
+ read those. Changing the game's difficulty is editing a row; adding a
539
+ level is adding a row and raising `LEVELS`. The index is clamped to
540
+ `LEVELS - 1` before the read, so level 7 and level 200 play row 6 rather
541
+ than the bytes after it.
542
+
543
+ **Level 3 adds a problem, not just speed.** Levels 1 and 2 differ in
544
+ speed and interval. Level 3 is the first row with `FLAG_SPIKE`, and the
545
+ spawner then makes every fourth spawn a spike at one and a half times
546
+ the row's speed. The autopilot cannot tell them apart; a player can, and
547
+ has a new thing to learn. That is the pattern page's rule of a type or
548
+ hazard column, not only a speed column.
549
+
550
+ **Coins, not seconds.** The level advances when `coins >= cur_quota`,
551
+ and a coin is only counted if the player is within eight pixels of it
552
+ when it reaches the bottom row. A slower player reaches level 3 later.
553
+ The time to level 3 that this page measures is therefore a property of
554
+ the table and the autopilot together, and it is repeatable because the
555
+ LFSR is seeded with a constant.
556
+
557
+ **Two knobs, two scalers.** Frames at 60 Hz must be more numerous to
558
+ last as long: `scale_frames` multiplies by 6/5, with `+2` so the divide
559
+ rounds to nearest. Pixels per frame at 60 Hz must be fewer to cover the
560
+ same distance in the same time: `scale_speed` multiplies by 5/6 in 8.8,
561
+ so a level-1 speed of `$0080` becomes `$006B` rather than zero. Both
562
+ scalers run at level start; the only other call is the coin's fall speed
563
+ when an enemy lands, and the frame loop never reads the table. The
564
+ 6/5 is an approximation: a PAL frame is 19.950 ms and a 6567R8 frame is
565
+ 16.715 ms, a ratio of 0.838, which is why the uncompensated NTSC run
566
+ came out at 0.838 of PAL rather than exactly 5/6, and why the
567
+ compensated runs differ by 80 ms rather than zero.
568
+
569
+ **The clock.** `sei` and `icr = $7F` take CIA1 away from the KERNAL,
570
+ whose interrupt otherwise reloads timer A sixty times a second. Timer A
571
+ then counts φ2 from `$FFFF` and timer B, in mode `$51`, counts timer A's
572
+ underflows, which makes a 32-bit cycle counter. `clock_read` stops both
573
+ before reading so the two halves belong to the same instant, reads the
574
+ four bytes low first, and restarts with `$01` and `$41`, which set START
575
+ and leave bit 4 clear. Bit 4 of either control register is LOAD, a
576
+ strobe that copies the `$FFFF` latch into the counter, so restarting
577
+ with the `$11` and `$51` that `clock_start` uses would zero the clock
578
+ on every read; a first draft of this page did exactly that and called
579
+ it a continuation, and it was caught by reading the clock a second time
580
+ 200 frames later and getting 200 frames rather than the whole run.
581
+ Milliseconds come from dividing by the region's clock in kHz (985 or
582
+ 1023); that is 0.025 % low on PAL and 0.027 % high on NTSC against the
583
+ true 985.248 and 1022.727, about six milliseconds each way at this
584
+ length, and because the two errors run in opposite directions they
585
+ close the gap between the printed times: divided exactly, the pinned
586
+ cycle counts are 22,491.5 ms and 22,584.0 ms, 93 ms apart, where the
587
+ screen says 80. Both are far inside `TOL_MS`. This is what
588
+ `cia_timer_phi2_difference` in `pitfalls/region-timing.md` is about: a
589
+ CIA count is not a time until the region's clock is applied to it.
590
+
591
+ **Why the frame count also tells the time.** Independently of the CIA,
592
+ 1128 PAL frames of 19,656 cycles is 22,171,968 cycles, and the CIA read
593
+ 22,159,699: the difference, 12,269 cycles, is the part of a frame
594
+ between the clock's start and the first `wait_frame`. The two
595
+ instruments agree to within one frame, under 0.07 %, on every row of
596
+ the table above, which is the check that the CIA path is reading the
597
+ right registers.
598
+
599
+ **A bug this page had.** The first build drew the coins on text rows up
600
+ to 28, past the 1,000 cells of the screen. The screen writes landed in
601
+ the program at `$0800` and the colour writes, at `$D800 + 1120` and up,
602
+ landed in the CIA1 register mirror at `$DC60`, where they stopped and
603
+ reloaded the timers. The cycle figures were nonsense and the frame
604
+ counts were fine, which is the symptom to remember: a colour RAM index
605
+ past 1,000 reaches the I/O area. The playfield now ends at row 24 and
606
+ the draw clamps `y` before it indexes.
607
+
608
+ ## Verification
609
+
610
+ Three instruments, run against the committed PNGs (VICE x64sc 3.10,
611
+ windowless build, Oscar64 build 2026-05-19).
612
+
613
+ **Text and border.** A script matched every cell of rows 0 to 4 against
614
+ the glyphs of `chargen-901225-01.bin` and read the border pixel at
615
+ (2, 100). Both pictures give the five lines quoted above and the border
616
+ RGB VICE emits for colour 5 on each model, (98, 213, 50) on PAL and
617
+ (114, 189, 103) on NTSC.
618
+
619
+ **The four figures.** The frame and millisecond at level 3 were read
620
+ from the exit screenshots of four runs, two builds on two models, and
621
+ are the table above. The cycle column was checked against frames times
622
+ the region's frame length in cycles (19,656 PAL, 17,095 NTSC) and agreed
623
+ to within one frame on all four.
624
+
625
+ **Reproducibility.** The pinned command was run twice per model and the
626
+ PNG bytes were identical each time.
627
+
628
+ **The clock continues across a read.** A test build of this listing
629
+ with a second `clock_read` after the 200-frame run-out printed
630
+ 26,070,834 cycles on PAL against 22,159,660 at level 3, a gap of
631
+ 3,911,174, which is 200 frames of 19,656 less the fraction of a frame
632
+ between the read and the loop's exit. The same build with the restart
633
+ changed to `$11` and `$51` printed 3,911,173 for the second read: the
634
+ LOAD strobe had reset the clock at the first one. Neither test build is
635
+ pinned; the figures on the pinned pictures come from one read.
636
+
637
+ The CIA cascade was also checked in isolation before the bug above was
638
+ found: a 30-line program that starts the two timers, waits 100 PAL
639
+ frames and dumps the sixteen CIA1 registers read 29 underflows and a
640
+ timer A remainder of `$E4A5`, 1,959,077 cycles against the 1,965,600 of
641
+ 100 frames; the gap is the start-up before the first `wait_frame`.
642
+
643
+ ## Sources
644
+
645
+ - `../../game-design/enemy-behaviour-and-difficulty.md`, `difficulty_ramp`:
646
+ the table shape, the held last row and the 50/60 coupling this page
647
+ measures.
648
+ - `pal-ntsc-detect.md`: the region read, reused as one function.
649
+ - `object-pool.md`: the parallel-array pool and the CIA1 timer A cost
650
+ loop, both reused in shape.
651
+ - `headless-verify.md`: the `$02FF` result byte and the border verdict.
652
+ - `../../hardware/pal-ntsc-reference.md`: 985,248 Hz and 1,022,727 Hz,
653
+ 312 lines of 63 cycles and 263 of 65 (read for the figures; the frame
654
+ lengths above are arithmetic from them).