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,771 @@
1
+ ---
2
+ recipe: vehicle-control
3
+ toolchain: oscar64
4
+ output_format: PRG
5
+ region: both
6
+ techniques: [vehicle_control, fixed_point_8_8, tile_grid_collision, soft_scroll_v, screen_double_buffer_d018, ecm_mode]
7
+ file_formats: [PRG]
8
+ uses_registers: [D000, D001, D010, D011, D015, D018, D020, D021, D022, D023, D024, D027, DC06, DC07, DC0F, DD04, DD05, DD0E]
9
+ uses_kernal: []
10
+ ---
11
+
12
+ <!-- doc-type: recipe -->
13
+
14
+ # Oscar64 Vehicle Control: a Spy Hunter style car on a scrolling road
15
+
16
+ ## Synopsis
17
+
18
+ A car sprite at a fixed screen height drives up a road that scrolls down
19
+ the screen. The throttle sets the forward speed in 8.8 pixels a frame,
20
+ and that speed is the scroll speed: up to 4 pixels a frame on the road.
21
+ Steering changes a lateral velocity that grip bleeds away when the stick
22
+ is centred. Four wheel probes read the tile map every frame: road, verge
23
+ (slows the car to 1.5 pixels a frame), water (boat handling, less grip)
24
+ and rock (a crash, a 50-frame stop, a restart on the road's centre line).
25
+ A 508-frame script drives it on autopilot: accelerate, onto the left
26
+ verge and back, a slide left and a brake into rock at the narrowing, a
27
+ restart, a coast, then across a water stretch.
28
+ Every frame folds the car's x, speed, distance and surface into a
29
+ checksum that the Python model below computes on the host. Rows 1 to 3
30
+ show the verdict and the CIA1 timer B cost of the control update and of
31
+ the redraw. `$02FF` = `01` and a green border when the checksum, the end
32
+ state, the surfaces seen and the single crash all match; `02` and red
33
+ otherwise. This is `vehicle_control` in `docs/techniques/logic.md`; the
34
+ result-byte contract is `headless-verify.md`.
35
+
36
+ ## Source
37
+
38
+ ```c
39
+ // vehicle-control.c
40
+ // A Spy Hunter style car on a vertically scrolling road. The throttle sets
41
+ // the forward speed in 8.8 pixels a frame, and that speed is the scroll
42
+ // speed; steering changes a lateral velocity that grip bleeds away; four
43
+ // wheel probes read the tile map for road, verge, water and rock. A script
44
+ // drives it: accelerate, onto the verge and back, a slide left and a brake
45
+ // into rock at the narrowing (one crash), coast, across a water stretch as
46
+ // a boat. Every frame folds x, speed, distance and surface into a checksum
47
+ // that a Python model of the same rules computed on the host. Rows 1 to 3 show the
48
+ // verdict and the CIA1 timer B cost of car_update and of the redraw.
49
+ #include <c64/vic.h>
50
+ #include <c64/cia.h>
51
+
52
+ #define Screen0 ((char *)0x0400)
53
+ #define Screen1 ((char *)0x3c00)
54
+ #define Color ((char *)0xd800)
55
+ #define SprData ((char *)0x0340) // sprite block 13
56
+ #define Map ((char *)0x5000) // MAP_H x 40 tile codes, 8,800 bytes
57
+ #define RESULT (*(volatile char *)0x02ff)
58
+
59
+ #define CODE_PASS 0x01
60
+ #define CODE_FAIL 0x02
61
+
62
+ #define MAP_H 220
63
+ #define BASE (MAP_H - 25) // top map row at distance 0
64
+ #define CAR_Y 150 // car top, pixels below line 48
65
+ #define WHEEL_L 1 // wheel columns in the 16-px body
66
+ #define WHEEL_R 14
67
+ #define WHEEL_F 3 // wheel rows below the car top
68
+ #define WHEEL_B 17
69
+
70
+ // Tile code bits 7-6 are the surface class and, in ECM, the background
71
+ // register: road $D021, verge $D022, water $D023, rock $D024.
72
+ #define C_ROAD 0
73
+ #define C_VERGE 1
74
+ #define C_WATER 2
75
+ #define C_CRASH 3
76
+
77
+ #define IN_UP 0x01
78
+ #define IN_DOWN 0x02
79
+ #define IN_LEFT 0x04
80
+ #define IN_RIGHT 0x08
81
+
82
+ // 8.8 constants, pixels a frame
83
+ #define ACCEL 0x000c
84
+ #define BRAKE 0x0020
85
+ #define COAST 0x0002
86
+ #define OVERDRAG 0x0010
87
+ #define STEER_MIN 0x0040
88
+ #define VXMAX 0x0180
89
+ #define CRASH_FRAMES 50
90
+
91
+ static const unsigned vmax[4] = {0x0400, 0x0180, 0x0300, 0};
92
+ static const int steer[4] = {0x0014, 0x0014, 0x0008, 0};
93
+ static const int grip[4] = {0x0010, 0x0010, 0x0003, 0};
94
+
95
+ // Python model (the page's "Model"): the fold after the whole script,
96
+ // and the state at the end.
97
+ #define EXPECT_CHK 0x2b51
98
+ #define EXPECT_X 0x91fe
99
+ #define EXPECT_SPEED 0x0300
100
+ #define EXPECT_DPIX 1210
101
+ #define EXPECT_SEEN 0x17 // road, verge, water, crash
102
+
103
+ // Road segments in driving order, from the bottom of the map up.
104
+ struct Seg { char rows, left, right, verge, water; };
105
+ static const struct Seg segs[] = {
106
+ {60, 12, 28, 3, 0}, // wide road
107
+ { 6, 13, 27, 3, 0},
108
+ { 6, 14, 26, 3, 0},
109
+ {30, 16, 24, 2, 0}, // the narrowing
110
+ { 8, 13, 27, 3, 0},
111
+ {50, 10, 30, 0, 1}, // water between rock banks
112
+ {60, 12, 28, 3, 0},
113
+ };
114
+ #define NSEGS (sizeof(segs) / sizeof(segs[0]))
115
+
116
+ struct Step { char frames, input; };
117
+ static const struct Step script[] = {
118
+ {60, IN_UP}, {40, IN_UP | IN_LEFT}, {40, IN_UP}, {40, IN_UP | IN_RIGHT},
119
+ {30, IN_UP}, {30, IN_UP | IN_LEFT}, {60, IN_DOWN}, {60, IN_UP},
120
+ {20, 0}, {60, IN_UP}, {14, IN_UP | IN_LEFT}, {30, IN_UP},
121
+ {14, IN_UP | IN_RIGHT}, {10, IN_UP},
122
+ };
123
+ #define STEPS (sizeof(script) / sizeof(script[0]))
124
+
125
+ static unsigned rowoff[MAP_H]; // row * 40
126
+ static char centre[MAP_H]; // road centre column per row
127
+
128
+ // --- the car ---------------------------------------------------------------
129
+ unsigned x_fp; // left edge, 8.8 pixels
130
+ int vx; // lateral velocity, 8.8
131
+ unsigned speed; // forward = scroll speed, 8.8
132
+ char dfrac; // distance, fraction byte
133
+ unsigned dpix; // distance, whole pixels
134
+ char surface; // class under the wheels last frame
135
+ char crash_t; // frames left in a crash
136
+ char crashes;
137
+
138
+ static char tile_class(const char *row, char px)
139
+ {
140
+ return row[px >> 3] >> 6;
141
+ }
142
+
143
+ void car_update(char in)
144
+ {
145
+ if (crash_t) { // crashed: no input, no motion
146
+ if (--crash_t == 0) {
147
+ unsigned w = BASE * 8 + CAR_Y + WHEEL_F - dpix;
148
+ x_fp = (unsigned)(centre[w >> 3] * 8 - 8) << 8;
149
+ surface = C_ROAD;
150
+ }
151
+ return;
152
+ }
153
+
154
+ // 1. speed from the throttle, limited by the surface; no throttle at or
155
+ // over the limit, so the over-limit drag slows a car with up held
156
+ unsigned vm = vmax[surface];
157
+ if (in & IN_UP) {
158
+ if (speed < vm)
159
+ speed += ACCEL;
160
+ } else if (in & IN_DOWN)
161
+ speed = speed > BRAKE ? speed - BRAKE : 0;
162
+ else
163
+ speed = speed > COAST ? speed - COAST : 0;
164
+ if (speed > vm)
165
+ speed = speed - OVERDRAG > vm ? speed - OVERDRAG : vm;
166
+
167
+ // 2. scroll accumulator: distance += speed (16.8)
168
+ unsigned t = dfrac + (speed & 0xff);
169
+ dfrac = (char)t;
170
+ dpix += (speed >> 8) + (t >> 8);
171
+
172
+ // 3. lateral velocity with grip, then position
173
+ int g = grip[surface];
174
+ if (speed >= STEER_MIN && (in & IN_LEFT))
175
+ vx -= steer[surface];
176
+ else if (speed >= STEER_MIN && (in & IN_RIGHT))
177
+ vx += steer[surface];
178
+ else if (vx > 0)
179
+ vx = vx > g ? vx - g : 0;
180
+ else if (vx < 0)
181
+ vx = -vx > g ? vx + g : 0;
182
+ if (vx > VXMAX)
183
+ vx = VXMAX;
184
+ else if (vx < -VXMAX)
185
+ vx = -VXMAX;
186
+ x_fp += vx;
187
+
188
+ // 4. surface probe: four wheels, the worst class wins
189
+ char px = x_fp >> 8;
190
+ unsigned w = BASE * 8 + CAR_Y - dpix;
191
+ const char *rf = Map + rowoff[(w + WHEEL_F) >> 3];
192
+ const char *rb = Map + rowoff[(w + WHEEL_B) >> 3];
193
+ char c = tile_class(rf, px + WHEEL_L), k;
194
+ k = tile_class(rf, px + WHEEL_R); if (k > c) c = k;
195
+ k = tile_class(rb, px + WHEEL_L); if (k > c) c = k;
196
+ k = tile_class(rb, px + WHEEL_R); if (k > c) c = k;
197
+
198
+ // 5. response
199
+ if (c == C_CRASH) {
200
+ crash_t = CRASH_FRAMES;
201
+ speed = 0;
202
+ vx = 0;
203
+ crashes++;
204
+ } else
205
+ surface = c;
206
+ }
207
+
208
+ // --- the redraw (belongs to the scroll, not to the car) ---------------------
209
+ // Copies 250 * blocks bytes: the whole matrix is 4 blocks, half is 2.
210
+ static void draw(char *dst, const char *src, char blocks)
211
+ {
212
+ for (char b = 0; b < blocks; b++) {
213
+ for (char i = 0; i < 250; i++)
214
+ dst[i] = src[i];
215
+ dst += 250;
216
+ src += 250;
217
+ }
218
+ }
219
+
220
+ static void build_map(void)
221
+ {
222
+ char r = MAP_H - 1;
223
+ for (char s = 0; s < NSEGS; s++) {
224
+ const struct Seg *g = segs + s;
225
+ char mid = (g->left + g->right) >> 1;
226
+ for (char n = 0; n < g->rows; n++) {
227
+ char *m = Map + (unsigned)r * 40;
228
+ for (char c = 0; c < 40; c++) {
229
+ char t;
230
+ if (g->water)
231
+ t = (c >= g->left && c < g->right) ? 0xa0 : 0xe0;
232
+ else if (c + g->verge < g->left || c >= g->right + g->verge)
233
+ t = 0xe0;
234
+ else if (c < g->left || c >= g->right)
235
+ t = 0x60;
236
+ else if (c == mid && (r & 3) < 2)
237
+ t = 0x3a; // ':' lane mark, road class
238
+ else
239
+ t = 0x20;
240
+ m[c] = t;
241
+ }
242
+ centre[r] = mid;
243
+ r--;
244
+ }
245
+ }
246
+ for (char i = 0; i < MAP_H; i++)
247
+ rowoff[i] = (unsigned)i * 40;
248
+ }
249
+
250
+ // --- CIA1 timer B harness (as tile-grid-collision.md) -----------------------
251
+ static inline void timer_start(void)
252
+ {
253
+ cia1.crb = 0x00;
254
+ cia1.tb = 0xffff;
255
+ cia1.crb = 0x11;
256
+ }
257
+
258
+ static inline unsigned timer_stop(void)
259
+ {
260
+ cia1.crb = 0x00;
261
+ return 0xffff - cia1.tb;
262
+ }
263
+
264
+ static unsigned fold(unsigned chk, unsigned v)
265
+ {
266
+ chk = (chk << 1) | (chk >> 15);
267
+ return (chk ^ v) + 13;
268
+ }
269
+
270
+ void put_str(char *p, const char *s)
271
+ {
272
+ while (*s)
273
+ *p++ = *s++;
274
+ }
275
+
276
+ void put_dec(char *p, unsigned v, char digits)
277
+ {
278
+ for (char i = digits; i > 0; i--) {
279
+ p[i - 1] = 0x30 + v % 10;
280
+ v /= 10;
281
+ }
282
+ }
283
+
284
+ void put_hex4(char *p, unsigned v)
285
+ {
286
+ for (char i = 4; i > 0; i--) {
287
+ char d = v & 15;
288
+ p[i - 1] = d < 10 ? 0x30 + d : d - 9;
289
+ v >>= 4;
290
+ }
291
+ }
292
+
293
+ static char car_colour(void)
294
+ {
295
+ return crash_t ? VCOL_WHITE : surface == C_WATER ? VCOL_YELLOW : VCOL_RED;
296
+ }
297
+
298
+ int main(void)
299
+ {
300
+ __asm { sei }
301
+ vic.color_border = VCOL_BLACK;
302
+ vic.color_back = VCOL_DARK_GREY; // road
303
+ vic.color_back1 = VCOL_GREEN; // verge
304
+ vic.color_back2 = VCOL_BLUE; // water
305
+ vic.color_back3 = VCOL_BROWN; // rock
306
+ for (unsigned i = 0; i < 1000; i++)
307
+ Color[i] = VCOL_WHITE;
308
+ build_map();
309
+
310
+ for (char i = 0; i < 63; i++)
311
+ SprData[i] = (i % 3 == 2) ? 0x00 : 0xff; // 16 x 21 block
312
+ Screen0[0x3f8] = 13;
313
+ Screen1[0x3f8] = 13;
314
+ vic.spr_enable = 0x01;
315
+ vic.spr_pos[0].y = 47 + CAR_Y; // drawn from line 48 + CAR_Y
316
+
317
+ x_fp = (unsigned)(centre[MAP_H - 1] * 8 - 8) << 8;
318
+ char top = BASE, shown = BASE, target = BASE - 1, half = 0, refused = 0;
319
+ draw(Screen0, Map + rowoff[BASE], 4);
320
+ char *back = Screen1;
321
+ char d018 = 0x14, d011 = 0x50; // screen $0400, chars $1000; ECM, DEN, RSEL 0
322
+ unsigned sx = (x_fp >> 8) + 24;
323
+ char scol = car_colour();
324
+
325
+ unsigned chk = 0, t_max = 0, t_at = 0, n_drive = 0, c_max = 0, f_max = 0;
326
+ unsigned long t_sum = 0;
327
+ char seen = 0;
328
+ unsigned frames = 0;
329
+ cia2.cra = 0x00;
330
+
331
+ for (char s = 0; s < STEPS; s++) {
332
+ for (char n = 0; n < script[s].frames; n++) {
333
+ vic_waitFrame(); // line 256: below the display
334
+ cia2.ta = 0xffff;
335
+ cia2.cra = 0x11; // whole-frame work timer
336
+ vic.memptr = d018;
337
+ vic.ctrl1 = d011;
338
+ vic.spr_pos[0].x = (char)sx;
339
+ vic.spr_msbx = sx >> 8;
340
+ vic.spr_color[0] = scol;
341
+
342
+ timer_start();
343
+ car_update(script[s].input);
344
+ unsigned t = timer_stop();
345
+ if (t > t_max) {
346
+ t_max = t;
347
+ t_at = frames;
348
+ }
349
+ if (!crash_t) { // typical: frames the car drives
350
+ t_sum += t;
351
+ n_drive++;
352
+ }
353
+
354
+ char st = surface | (crash_t ? 0x10 : 0);
355
+ seen |= crash_t ? 0x10 : 1 << surface;
356
+ chk = fold(chk, x_fp);
357
+ chk = fold(chk, speed);
358
+ chk = fold(chk, dpix);
359
+ chk = fold(chk, st);
360
+
361
+ // Scroll: the top row and YSCROLL follow the distance. The
362
+ // hidden matrix is drawn for the next top row (one map row
363
+ // further ahead), half a matrix a frame, and flipped in when
364
+ // the top row reaches it.
365
+ top = BASE - (dpix >> 3);
366
+ if (top != shown) {
367
+ if (top == target && half == 2) {
368
+ d018 = (back == Screen1) ? 0xf4 : 0x14;
369
+ back = (back == Screen1) ? Screen0 : Screen1;
370
+ shown = top;
371
+ target = top - 1;
372
+ half = 0;
373
+ } else
374
+ refused++; // would show a wrong row
375
+ }
376
+ if (half < 2) {
377
+ timer_start();
378
+ draw(back + half * 500, Map + rowoff[target] + half * 500, 2);
379
+ unsigned tc = timer_stop();
380
+ if (tc > c_max) c_max = tc;
381
+ half++;
382
+ }
383
+ d011 = 0x50 | (dpix & 7);
384
+ sx = (x_fp >> 8) + 24;
385
+ scol = car_colour();
386
+ frames++;
387
+
388
+ cia2.cra = 0x00;
389
+ unsigned tf = 0xffff - cia2.ta;
390
+ if (tf > f_max) f_max = tf;
391
+ }
392
+ }
393
+
394
+ vic_waitFrame(); // show the last state
395
+ vic.memptr = d018;
396
+ vic.ctrl1 = d011;
397
+ vic.spr_pos[0].x = (char)sx;
398
+ vic.spr_msbx = sx >> 8;
399
+ vic.spr_color[0] = scol;
400
+
401
+ bool ok = chk == EXPECT_CHK && x_fp == EXPECT_X && speed == EXPECT_SPEED &&
402
+ dpix == EXPECT_DPIX && seen == EXPECT_SEEN && crashes == 1 &&
403
+ refused == 0;
404
+ char *scr = (d018 == 0x14) ? Screen0 : Screen1;
405
+ put_str(scr + 40, ok ? s"pass chk " : s"fail chk ");
406
+ put_hex4(scr + 49, chk);
407
+ put_str(scr + 54, s"frames ");
408
+ put_dec(scr + 61, frames, 3);
409
+ put_str(scr + 80, s"ctl max avg");
410
+ put_dec(scr + 88, t_max, 5);
411
+ put_dec(scr + 98, (unsigned)(t_sum / n_drive), 5);
412
+ put_str(scr + 104, s"at");
413
+ put_dec(scr + 107, t_at, 3);
414
+ put_str(scr + 120, s"cpy max frm");
415
+ put_dec(scr + 128, c_max, 5);
416
+ put_dec(scr + 138, f_max, 5);
417
+ RESULT = ok ? CODE_PASS : CODE_FAIL;
418
+ vic.color_border = ok ? VCOL_GREEN : VCOL_RED;
419
+ for (;;)
420
+ ;
421
+ return 0;
422
+ }
423
+ ```
424
+
425
+ ## Build
426
+
427
+ ```bash
428
+ oscar64 -tm=c64 -O2 -o=vehicle-control.prg vehicle-control.c
429
+ ```
430
+
431
+ Produces `vehicle-control.prg`, 3,165 bytes (Oscar64 as installed on
432
+ 2026-09-23). Then run headless in VICE (PAL):
433
+
434
+ ```bash
435
+ GSETTINGS_SCHEMA_DIR=/opt/homebrew/share/glib-2.0/schemas x64sc -default -warp +sound \
436
+ +autostart-delay-random -autostartprgmode 1 -limitcycles 16000000 \
437
+ -exitscreenshot vehicle-control.png -autostart vehicle-control.prg
438
+ ```
439
+
440
+ Add `-model ntsc` for the NTSC picture. The script is 508 frames on both
441
+ models; at 16,000,000 cycles the run is over on both and the picture is
442
+ the last frame.
443
+
444
+ ## Expected output
445
+
446
+ The screen is in extended colour mode (ECM), so bits 7 and 6 of each
447
+ tile code pick the cell's background register and are also the surface
448
+ class the probes read. Colour RAM is white everywhere and never changes.
449
+
450
+ | Tile code | Class | Background | Colour |
451
+ |---|---|---|---|
452
+ | `$20`, `$3A` (`:` lane mark) | road | `$D021` | dark grey, 11 |
453
+ | `$60` | verge | `$D022` | green, 5 |
454
+ | `$A0` | water | `$D023` | blue, 6 |
455
+ | `$E0` | rock | `$D024` | brown, 9 |
456
+
457
+ The map is 220 rows of 40 tiles, built at start from seven segments and
458
+ driven from the bottom up: 60 rows of road 16 tiles wide with 3-tile
459
+ verges, two 6-row steps in, 30 rows 8 tiles wide with 2-tile verges (the
460
+ narrowing), 8 rows at 14 wide, 50 rows of water 20 tiles wide between
461
+ rock banks, and 60 rows of road. The car is a 16 x 21 block: red on the
462
+ road and verge, yellow on water, white while crashed.
463
+
464
+ The script, as the model and the program both run it:
465
+
466
+ | Frames | Input | What happens |
467
+ |---|---|---|
468
+ | 0 to 59 | up | accelerates from 0 to 2.81 pixels a frame (`$02D0`), straight |
469
+ | 60 to 99 | up, left | reaches the road limit, 4.0 (`$0400`); slides left |
470
+ | 100 to 139 | up | onto the left verge at frame 108, x 94. Up adds nothing at or over the limit, so the over-limit drag takes 0.06 (`$0010`) a frame off: 4.0 at frame 108, 1.5 (`$0180`) at frame 148 |
471
+ | 140 to 179 | up, right | back onto the road at frame 152, x 95, and across it |
472
+ | 180 to 209 | up | grip takes the slide out; back to 4.0 at x 152 |
473
+ | 210 to 239 | up, left | slides left at 4.0; lateral speed at its cap, 1.5, from frame 229; onto the narrowing's left verge at frame 235, x 126 |
474
+ | 240 to 246 | down | brake: 0.13 (`$0020`) plus the over-limit drag a frame; the stick is centred, so grip slows the slide left |
475
+ | 247 | down | the left wheels reach rock at x 110 with the speed still over the verge limit: crash, speed 0, the one crash of the run |
476
+ | 248 to 297 | (ignored) | crashed; the road stands still; at frame 297 the timer ends and the car restarts at x 152, on the narrowing's centre line |
477
+ | 298 to 299 | down | brake at speed 0: the speed stays 0 |
478
+ | 300 to 359 | up | accelerates to 2.81 (`$02D0`) |
479
+ | 360 to 379 | none | coast: 0.008 (`$0002`) a frame off, to `$02A8` |
480
+ | 380 to 507 | up, then left, up, right, up | the front wheels reach water at frame 382; boat: limit 3.0 (`$0300`), steering 0.03 a frame, grip 0.01 a frame, so the drift outlasts the input |
481
+
482
+ The end state, from the model: x `$91FE` (pixel 145), speed `$0300`,
483
+ distance 1,210 pixels, so the top row is map row 44 and YSCROLL is 2,
484
+ and the checksum is `$2B51`.
485
+
486
+ Measured in VICE x64sc 3.10 at 16,000,000 cycles, decoded against the
487
+ char ROM: rows 1 to 3 read
488
+
489
+ ```text
490
+ PASS CHK 2B51 FRAMES 508
491
+ CTL MAX 00655 AVG 00557 AT 247
492
+ CPY MAX 10110 FRM 11393
493
+ ```
494
+
495
+ on PAL; NTSC is the same except `CPY MAX 10368 FRM 11640`. The border
496
+ is colour 5 on both. `CTL MAX` is the worst frame of `car_update`, 655
497
+ cycles, at frame 247: the crash frame, which runs the brake, the verge
498
+ over-limit drag, the grip step on a slide to the left, the four probes
499
+ and the crash response. The script puts that frame there on purpose: it
500
+ is the longest path through `car_update`. `AVG` is the mean over the 458
501
+ frames the car was not crashed, 557. Both are the same on the two
502
+ models because the update runs from line 256, before the first badline,
503
+ on both. A build whose code is laid out differently can move them by a
504
+ cycle (656 and 558 in a variant with a different redraw call). `CPY MAX`
505
+ is the worst half-matrix redraw, which runs with the display on, and
506
+ `FRM` the worst frame's whole work from line 256 to the end of the loop
507
+ body; 11,640 is 68% of an NTSC frame (17,095 cycles), so no frame
508
+ overran.
509
+
510
+ `screenshots/vehicle-control.png` (PAL) and
511
+ `screenshots/vehicle-control-ntsc.png` (NTSC) are the exit screenshots
512
+ at 16,000,000 cycles. Measured on the PNGs: the car's 336 yellow pixels
513
+ are a 16 x 21 block at PNG x 177 to 192, which is car x 145, from raster
514
+ line 198 (PNG row 182 on PAL, 170 on NTSC). Every other pixel of the
515
+ display window outside rows 1 to 3, 53,424 of them, matches the colour
516
+ predicted from the model's map at distance 1,210 and YSCROLL 2, with the
517
+ `:` glyphs taken from `chargen-901225-01.bin`: 0 mismatches on each
518
+ model. The picture shows the end of the water stretch: road with its
519
+ lane mark and verges above, the rock bank, and the boat in the water.
520
+
521
+ ### Model
522
+
523
+ The Python the checksum came from. It builds the same map and runs the
524
+ same rules; `run()` returns the end state and the fold.
525
+
526
+ ```python
527
+ # Model of vehicle-control.c: same map, same rules, same fold.
528
+ import sys
529
+
530
+ MAP_H, W = 220, 40
531
+ BASE = MAP_H - 25
532
+ CAR_Y = 150 # car top, text-area pixel row (line 48 + CAR_Y)
533
+ WHEEL_L, WHEEL_R = 1, 14 # wheel columns in the 16-px body
534
+ WHEEL_F, WHEEL_B = 3, 17 # wheel rows below the car top
535
+ C_ROAD, C_VERGE, C_WATER, C_CRASH = 0, 1, 2, 3
536
+ UP, DOWN, LEFT, RIGHT = 1, 2, 4, 8
537
+
538
+ ACCEL, BRAKE, COAST, OVERDRAG = 0x000C, 0x0020, 0x0002, 0x0010
539
+ STEER_MIN, VXMAX = 0x0040, 0x0180
540
+ VMAX = [0x0400, 0x0180, 0x0300, 0]
541
+ STEER = [0x0014, 0x0014, 0x0008, 0]
542
+ GRIP = [0x0010, 0x0010, 0x0003, 0]
543
+ CRASH_FRAMES = 50
544
+
545
+ # Segments in driving order from the bottom of the map:
546
+ # rows, road left col, road right col (exclusive), verge width, kind (0 road, 1 water)
547
+ SEGS = [
548
+ (60, 12, 28, 3, 0),
549
+ (6, 13, 27, 3, 0),
550
+ (6, 14, 26, 3, 0),
551
+ (30, 16, 24, 2, 0),
552
+ (8, 13, 27, 3, 0),
553
+ (50, 10, 30, 0, 1),
554
+ (60, 12, 28, 3, 0),
555
+ ]
556
+
557
+ SCRIPT = [
558
+ (60, UP),
559
+ (40, UP | LEFT),
560
+ (40, UP),
561
+ (40, UP | RIGHT),
562
+ (30, UP),
563
+ (30, UP | LEFT),
564
+ (60, DOWN),
565
+ (60, UP),
566
+ (20, 0),
567
+ (60, UP),
568
+ (14, UP | LEFT),
569
+ (30, UP),
570
+ (14, UP | RIGHT),
571
+ (10, UP),
572
+ ]
573
+
574
+
575
+ def build():
576
+ m = bytearray(MAP_H * W)
577
+ centre = bytearray(MAP_H)
578
+ r = MAP_H - 1
579
+ for rows, rl, rr, v, kind in SEGS:
580
+ for _ in range(rows):
581
+ for c in range(W):
582
+ if kind:
583
+ t = 0xA0 if rl <= c < rr else 0xE0
584
+ elif c < rl - v or c >= rr + v:
585
+ t = 0xE0
586
+ elif c < rl or c >= rr:
587
+ t = 0x60
588
+ elif c == (rl + rr) >> 1 and (r & 3) < 2:
589
+ t = 0x3A
590
+ else:
591
+ t = 0x20
592
+ m[r * W + c] = t
593
+ centre[r] = (rl + rr) >> 1
594
+ r -= 1
595
+ assert r == -1
596
+ return m, centre
597
+
598
+
599
+ MAP, CENTRE = build()
600
+
601
+
602
+ class Car:
603
+ def __init__(s):
604
+ s.x = (CENTRE[MAP_H - 1] * 8 - 8) << 8
605
+ s.vx = 0
606
+ s.speed = 0
607
+ s.dfrac = 0
608
+ s.dpix = 0
609
+ s.surface = C_ROAD
610
+ s.crash_t = 0
611
+ s.crashes = 0
612
+
613
+ def cls(s, px, yy):
614
+ w = BASE * 8 + yy - s.dpix
615
+ return MAP[(w >> 3) * W + (px >> 3)] >> 6
616
+
617
+ def update(s, inp):
618
+ if s.crash_t:
619
+ s.crash_t -= 1
620
+ if s.crash_t == 0:
621
+ row = (BASE * 8 + CAR_Y + WHEEL_F - s.dpix) >> 3
622
+ s.x = (CENTRE[row] * 8 - 8) << 8
623
+ s.surface = C_ROAD
624
+ return
625
+ # speed
626
+ vm = VMAX[s.surface]
627
+ if inp & UP:
628
+ if s.speed < vm:
629
+ s.speed += ACCEL
630
+ elif inp & DOWN:
631
+ s.speed = s.speed - BRAKE if s.speed > BRAKE else 0
632
+ else:
633
+ s.speed = s.speed - COAST if s.speed > COAST else 0
634
+ if s.speed > vm:
635
+ s.speed = s.speed - OVERDRAG if s.speed - OVERDRAG > vm else vm
636
+ # scroll accumulator
637
+ t = s.dfrac + (s.speed & 0xFF)
638
+ s.dfrac = t & 0xFF
639
+ s.dpix += (s.speed >> 8) + (t >> 8)
640
+ # lateral
641
+ su = s.surface
642
+ if s.speed >= STEER_MIN and inp & LEFT:
643
+ s.vx -= STEER[su]
644
+ elif s.speed >= STEER_MIN and inp & RIGHT:
645
+ s.vx += STEER[su]
646
+ elif s.vx > 0:
647
+ s.vx = s.vx - GRIP[su] if s.vx > GRIP[su] else 0
648
+ elif s.vx < 0:
649
+ s.vx = s.vx + GRIP[su] if -s.vx > GRIP[su] else 0
650
+ if s.vx > VXMAX:
651
+ s.vx = VXMAX
652
+ if s.vx < -VXMAX:
653
+ s.vx = -VXMAX
654
+ s.x = (s.x + s.vx) & 0xFFFF
655
+ # surface probe: four wheels, worst class wins
656
+ px = s.x >> 8
657
+ c = max(s.cls(px + WHEEL_L, CAR_Y + WHEEL_F), s.cls(px + WHEEL_R, CAR_Y + WHEEL_F),
658
+ s.cls(px + WHEEL_L, CAR_Y + WHEEL_B), s.cls(px + WHEEL_R, CAR_Y + WHEEL_B))
659
+ # response
660
+ if c == C_CRASH:
661
+ s.crash_t = CRASH_FRAMES
662
+ s.speed = 0
663
+ s.vx = 0
664
+ s.crashes += 1
665
+ else:
666
+ s.surface = c
667
+
668
+
669
+ def rol_fold(chk, v):
670
+ chk = ((chk << 1) | (chk >> 15)) & 0xFFFF
671
+ return ((chk ^ v) + 13) & 0xFFFF
672
+
673
+
674
+ def run(verbose=False):
675
+ car = Car()
676
+ chk = 0
677
+ f = 0
678
+ seen = 0
679
+ for n, inp in SCRIPT:
680
+ for _ in range(n):
681
+ car.update(inp)
682
+ st = car.surface | (0x10 if car.crash_t else 0)
683
+ seen |= 1 << (4 if car.crash_t else car.surface)
684
+ for v in (car.x, car.speed, car.dpix, st):
685
+ chk = rol_fold(chk, v)
686
+ if verbose:
687
+ print(f, hex(inp), 'x', car.x >> 8, hex(car.x & 255), 'sp', hex(car.speed), 'vx', car.vx,
688
+ 'd', car.dpix, 'surf', car.surface, 'crash', car.crash_t)
689
+ f += 1
690
+ return car, chk, f, seen
691
+
692
+
693
+ if __name__ == '__main__':
694
+ car, chk, f, seen = run('-v' in sys.argv)
695
+ top = BASE - (car.dpix >> 3)
696
+ print('frames', f, 'chk %04X' % chk, 'x %04X' % car.x, 'speed %04X' % car.speed, 'dpix', car.dpix,
697
+ 'top', top, 'yscroll', car.dpix & 7, 'crashes', car.crashes, 'seen %02X' % seen)
698
+ ```
699
+
700
+ ## Why this works
701
+
702
+ The distance is a 16.8 number: a whole-pixel word `dpix` and a fraction
703
+ byte. Each frame adds the speed, and the carry out of the fraction byte
704
+ moves into the pixel word, so 2.81 pixels a frame advances 2 or 3 pixels
705
+ and never drifts. The scroll follows from the distance and nothing else:
706
+ the top map row on screen is `BASE - (dpix >> 3)` and YSCROLL is
707
+ `dpix & 7`, so the probes and the picture cannot disagree. The screen
708
+ pixel `yy` lines below line 48 shows map pixel row
709
+ `BASE * 8 + yy - dpix`, which is what the probes index. With RSEL 0 the
710
+ 25-row matrix covers the 24-row window at every YSCROLL.
711
+
712
+ The probes are two map rows, looked up once through a row-offset table,
713
+ and two columns in each, the wheel points of a 16-pixel body. The worst
714
+ class of the four wins (rock over water over verge over road), so one
715
+ wheel on the verge slows the car and one wheel on rock crashes it. The
716
+ class is the tile code shifted right six times; in ECM the same two bits
717
+ pick the background colour, so the drawing and the rule come from one
718
+ byte. A lateral speed of at most 1.5 pixels a frame and a forward speed
719
+ of at most 4 are both under a tile, so no probe steps over a tile
720
+ between frames.
721
+
722
+ Up adds the acceleration only below the surface's limit. Over it, the
723
+ over-limit drag is the only change, so a car that runs onto the verge
724
+ with the throttle held slows by 0.06 a frame, from 4.0 to 1.5 in 40
725
+ frames. If up still added its 0.05 the net fall would be 0.016 a frame,
726
+ 160 frames to reach 1.5 (arithmetic), and the verge would barely slow a
727
+ car at full throttle.
728
+
729
+ The sprite's Y register is set to `47 + CAR_Y` because a sprite at Y
730
+ register n is drawn from raster line n + 1: the first build used
731
+ `48 + CAR_Y` and the PNG check found the car one line below the model's
732
+ probe rows, 16 mismatched pixels on each model.
733
+
734
+ The redraw is the scroll's work, not the car's. A crossing needs the
735
+ whole matrix rewritten one row lower. At up to 4 pixels a frame
736
+ crossings are at least two frames apart, so the hidden matrix is drawn
737
+ for the next top row half a matrix a frame, 10,110 cycles on PAL and
738
+ 10,368 on NTSC, and `$D018` flips to it on the first frame the top row
739
+ equals it. A variant of this listing that copies the whole matrix in
740
+ one frame measured 20,330 cycles on PAL and 20,414 on NTSC, more than a
741
+ frame on either (19,656 and 17,095). `refused`
742
+ counts crossings that arrive before the hidden matrix is ready; it is 0,
743
+ and the verdict requires that. Colour RAM needs no move because ECM
744
+ takes the colour from the tile code.
745
+
746
+ The speed is per frame, so on NTSC the road passes 20% faster in real
747
+ time for the same script; the checksum is the same on both models
748
+ because the logic runs once per frame on both.
749
+
750
+ ## Verification
751
+
752
+ VICE x64sc 3.10, windowless build, Oscar64 as installed on 2026-09-23.
753
+
754
+ **Every pixel against the model.** A script predicted each pixel of the
755
+ display window (lines 55 to 246, 320 columns) from the model's map, the
756
+ end distance and YSCROLL, and the char ROM for the lane-mark glyph,
757
+ skipping the HUD rows and the sprite's box: 53,424 pixels, 0 mismatches
758
+ on PAL and on NTSC. The sprite's yellow pixels were located separately
759
+ and give car x 145, the model's value.
760
+
761
+ **Text and border.** Rows 1 to 3 were decoded against
762
+ `chargen-901225-01.bin` and read as quoted above; the border pixel is
763
+ colour 5 on each model.
764
+
765
+ **Reproducibility.** The pinned command was run twice per model; the
766
+ PNG bytes were identical each time.
767
+
768
+ ## Sources
769
+
770
+ None. The map, the rules, the constants and the code are original; the
771
+ game style is named only as the brief for the design.