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,772 @@
1
+ ---
2
+ recipe: high-score-persist
3
+ toolchain: oscar64
4
+ output_format: PRG
5
+ region: both
6
+ techniques: [kernal_file_write_seq, kernal_file_read_seq, error_channel_check]
7
+ file_formats: [PRG]
8
+ uses_registers: []
9
+ uses_kernal: [SETLFS, SETNAM, OPEN, CLOSE, CHKIN, CHKOUT, CLRCHN, CHRIN, CHROUT, READST]
10
+ ---
11
+
12
+ <!-- doc-type: recipe -->
13
+
14
+ # Oscar64 High-Score File: First Run, Replace, Version Check
15
+
16
+ ## Synopsis
17
+
18
+ The save-file policy a game needs around the file calls, run end to
19
+ end against drive 8 through Oscar64's `kernalio.h`. On start the
20
+ program tries to read the sequential file `HISCORE`. That OPEN sends a
21
+ name down the bus, so it is also the device test: when no drive answers
22
+ it returns false with ST bit 7 set and the bus is left alone for the
23
+ rest of the run. A drive reply of
24
+ `62` is the first run: it shows the default table and writes it. A
25
+ reply of `00` with a record of the right size, magic and version is a
26
+ load. A record of the wrong size or version is rejected, scratched and
27
+ replaced. Any other reply, `74` with no disk in the drive for one,
28
+ turns saving off for the session and the defaults are used. It then
29
+ enters a new score, replaces the file by scratch-then-write, reads it
30
+ back and prints it, with the drive's reply and `krnio_status()` after
31
+ every step and a 16-bit checksum of the record that came back matched
32
+ against the value Python computed. The verifier gives every run a
33
+ freshly formatted disk, so one pinned run exercises the first-run path,
34
+ the first write, the scratch, the rewrite and the read-back in
35
+ sequence. The other paths were run once each and are described below.
36
+ Use it as the skeleton for a high-score file or a save slot.
37
+
38
+ ## Source
39
+
40
+ ```c
41
+ // high-score-persist.c
42
+ // The save-game policy in one run against drive 8, through Oscar64's
43
+ // kernalio.h. On start, try to read the SEQ file HISCORE. That OPEN
44
+ // sends a name down the bus, so it is also the device test: if no drive
45
+ // answers it returns false with ST bit 7 (KRNIO_NODEVICE) and the bus
46
+ // is not touched again. A drive reply of 62 means first run: show the
47
+ // default table and write it. A reply of 00 with a record of the right
48
+ // size and version means loaded. Any other reply means saving is off
49
+ // for this session and the defaults are used. Then a new high score is
50
+ // entered, the file is scratched and rewritten, and read back. The
51
+ // drive reply and krnio_status() are printed at every step, and a
52
+ // 16-bit checksum of the record that came back is shown with PASS or
53
+ // FAIL against the value Python computed.
54
+ #include <stdio.h>
55
+ #include <string.h>
56
+ #include <c64/kernalio.h>
57
+
58
+ #define DRIVE 8
59
+ #define VERSION 1
60
+ #define ENTRIES 3
61
+ #define EXPECT_CHK 0xF1F6 // python: same fold over the 21 bytes
62
+
63
+ typedef struct {
64
+ char name[4]; // three letters and a terminating zero
65
+ unsigned score; // 16 bits, stored low byte first
66
+ } Score;
67
+
68
+ typedef struct {
69
+ char magic[2]; // 'H','S'
70
+ char version; // bump when the layout changes
71
+ Score entry[ENTRIES];
72
+ } Table; // 21 bytes: no padding in Oscar64
73
+
74
+ static const Table defaults = {
75
+ { 'H', 'S' }, VERSION,
76
+ {{ "ABC", 5000 }, { "DEF", 2500 }, { "GHI", 1000 }}
77
+ };
78
+
79
+ static Table table; // the live table
80
+ static char back[64]; // read buffer, bigger than the file
81
+ static char reply[40]; // one drive status line
82
+ static char code; // first two digits of the last reply
83
+ static bool saving; // false: drive 8 is not usable
84
+
85
+ static void put_hex2(char v)
86
+ {
87
+ char d = v >> 4;
88
+ putchar(d < 10 ? '0' + d : 'A' + d - 10);
89
+ d = v & 15;
90
+ putchar(d < 10 ? '0' + d : 'A' + d - 10);
91
+ }
92
+
93
+ // Open the command channel with cmd as the name ("" to read the status,
94
+ // or a DOS command such as "S0:HISCORE"), read one status line from the
95
+ // same open channel, close it. The reply to a command is only there
96
+ // until the channel that sent it is closed. Sets code to the two-digit
97
+ // number at the head of the line. Never called once the device is known
98
+ // to be absent: an empty name sends nothing on the bus, so this OPEN
99
+ // succeeds whether or not a drive exists, and the CHKIN inside
100
+ // krnio_gets would then hang with no timeout.
101
+ static void drive_reply(const char *cmd)
102
+ {
103
+ reply[0] = 0;
104
+ code = 99;
105
+ krnio_setnam(cmd);
106
+ if (krnio_open(15, DRIVE, 15)) {
107
+ int n = krnio_gets(15, reply, sizeof(reply));
108
+ if (n > 0 && reply[n - 1] == 13)
109
+ reply[n - 1] = 0;
110
+ if (n >= 2)
111
+ code = (reply[0] - '0') * 10 + (reply[1] - '0');
112
+ krnio_close(15);
113
+ }
114
+ printf("DRIVE: %s\n", reply);
115
+ }
116
+
117
+ // chk = ((chk ^ b) * 5 + 1) & 0xffff over n bytes
118
+ static unsigned fold(const char *p, int n)
119
+ {
120
+ unsigned c = 0;
121
+ for (int i = 0; i < n; i++)
122
+ c = (c ^ p[i]) * 5 + 1;
123
+ return c;
124
+ }
125
+
126
+ // Write the live table as HISCORE,S,W. Returns true when the drive
127
+ // answered 00 after the close.
128
+ static bool save_table(void)
129
+ {
130
+ printf("WRITE HISCORE,S,W %d BYTES\n", (int)sizeof(Table));
131
+ krnio_setnam("HISCORE,S,W");
132
+ bool ok = krnio_open(2, DRIVE, 2);
133
+ printf("OPEN %d ST=", ok); put_hex2(krnio_status());
134
+ int n = krnio_write(2, (const char *)&table, sizeof(Table));
135
+ printf(" WRITE %d ST=", n); put_hex2(krnio_status());
136
+ krnio_close(2);
137
+ printf(" CLOSE ST="); put_hex2(krnio_status());
138
+ putchar('\n');
139
+ drive_reply("");
140
+ return code == 0;
141
+ }
142
+
143
+ // Try to read HISCORE,S,R into back[]. Returns the byte count (0 when
144
+ // the file is missing or the device did not answer). This OPEN sends
145
+ // the name down the bus, so it is the one call that can see an absent
146
+ // drive: it then returns false with KRNIO_NODEVICE in the status.
147
+ static int load_table(void)
148
+ {
149
+ printf("READ HISCORE,S,R\n");
150
+ krnio_setnam("HISCORE,S,R");
151
+ bool ok = krnio_open(2, DRIVE, 2);
152
+ printf("OPEN %d ST=", ok); put_hex2(krnio_status());
153
+ if (!ok && (krnio_status() & KRNIO_NODEVICE)) {
154
+ // nothing answered: do not CHKIN, it hangs with no timeout on a
155
+ // device that does not answer, and do not ask for a reply
156
+ krnio_close(2);
157
+ printf("\nDRIVE: NO DEVICE\n");
158
+ code = 99;
159
+ saving = false;
160
+ return 0;
161
+ }
162
+ int n = 0;
163
+ if (ok) {
164
+ n = krnio_read(2, back, sizeof(back));
165
+ printf(" READ %d ST=", n); put_hex2(krnio_pstatus[2]);
166
+ }
167
+ putchar('\n');
168
+ krnio_close(2);
169
+ drive_reply("");
170
+ return n;
171
+ }
172
+
173
+ // Replace the file on disk: scratch it, then write. Writing HISCORE,S,W
174
+ // over an existing name is refused with 63, FILE EXISTS, and every byte
175
+ // written afterwards comes back with ST=$80, KRNIO_NODEVICE: the drive
176
+ // does not acknowledge bytes on a channel it never opened.
177
+ static bool rewrite_table(void)
178
+ {
179
+ printf("SCRATCH S0:HISCORE\n");
180
+ drive_reply("S0:HISCORE");
181
+ return save_table();
182
+ }
183
+
184
+ static void show_table(const char *label)
185
+ {
186
+ printf("%s", label);
187
+ for (char i = 0; i < ENTRIES; i++)
188
+ printf(" %s %u", table.entry[i].name, table.entry[i].score);
189
+ putchar('\n');
190
+ }
191
+
192
+ // Insert a score in rank order, dropping the last entry.
193
+ static void enter_score(const char *name, unsigned score)
194
+ {
195
+ char i = ENTRIES;
196
+ while (i > 0 && table.entry[i - 1].score < score) {
197
+ if (i < ENTRIES)
198
+ table.entry[i] = table.entry[i - 1];
199
+ i--;
200
+ }
201
+ if (i < ENTRIES) {
202
+ strcpy(table.entry[i].name, name);
203
+ table.entry[i].score = score;
204
+ }
205
+ }
206
+
207
+ int main(void)
208
+ {
209
+ printf("%cHIGH SCORE PERSIST (KERNALIO.H)\n", 147);
210
+
211
+ // 1. try to load; the named OPEN inside is also the device test
212
+ table = defaults;
213
+ saving = true;
214
+ int n = load_table();
215
+ const Table *got = (const Table *)back;
216
+
217
+ // 2. decide what state the save is in
218
+ if (code == 0 && n == sizeof(Table) &&
219
+ got->magic[0] == 'H' && got->magic[1] == 'S' &&
220
+ got->version == VERSION) {
221
+ table = *got;
222
+ show_table("LOADED:");
223
+ } else if (code == 0 && n > 0) {
224
+ // right name, wrong layout: reject it and start again
225
+ printf("OLD FORMAT %d BYTES V%d: RESET\n", n, got->version);
226
+ saving = rewrite_table();
227
+ show_table("DEFAULTS:");
228
+ } else if (code == 62) {
229
+ printf("FIRST RUN\n");
230
+ saving = save_table();
231
+ show_table("DEFAULTS:");
232
+ } else {
233
+ // 74 with no disk, 99 with no device, anything else unexpected
234
+ printf("SAVING OFF (%d)\n", code);
235
+ saving = false;
236
+ show_table("DEFAULTS:");
237
+ }
238
+
239
+ // 3. a new high score, then rewrite: scratch first, then write
240
+ enter_score("YOU", 4000);
241
+ show_table("NEW:");
242
+ if (saving)
243
+ saving = rewrite_table();
244
+
245
+ // 4. read back what is on the disk now and check it
246
+ if (saving) {
247
+ n = load_table();
248
+ got = (const Table *)back;
249
+ printf("BACK V%d:", got->version);
250
+ for (char i = 0; i < ENTRIES; i++)
251
+ printf(" %s %u", got->entry[i].name, got->entry[i].score);
252
+ putchar('\n');
253
+ } else {
254
+ printf("NOT SAVED\n");
255
+ memcpy(back, &table, sizeof(Table));
256
+ n = sizeof(Table);
257
+ }
258
+ unsigned chk = fold(back, n);
259
+ printf("CHK ");
260
+ put_hex2(chk >> 8);
261
+ put_hex2(chk & 0xff);
262
+ printf(chk == EXPECT_CHK ? " PASS\n" : " FAIL\n");
263
+ return 0;
264
+ }
265
+ ```
266
+
267
+ ## Build
268
+
269
+ ```bash
270
+ oscar64 -tm=c64 -O2 -o=high-score-persist.prg high-score-persist.c
271
+ ```
272
+
273
+ Produces `high-score-persist.prg`. `kernalio.h` carries
274
+ `#pragma compile("kernalio.c")`, so the wrapper source is compiled in
275
+ without a second file on the command line. The run needs a disk in
276
+ drive 8:
277
+
278
+ ```bash
279
+ c1541 -format "test,01" d64 test.d64
280
+ x64sc -default -warp +sound +autostart-delay-random -autostartprgmode 1 \
281
+ -limitcycles 24000000 -8 test.d64 \
282
+ -drive8wobbleamplitude 0 -drive8wobblefrequency 0 \
283
+ -exitscreenshot high-score-persist.png -autostart high-score-persist.prg
284
+ ```
285
+
286
+ Putting a finished game on a D64 that boots from `LOAD"*",8,1`, and what
287
+ its own score file does to that image, is
288
+ [release-disk](../../toolchains/release-disk.md).
289
+
290
+ ## Expected output
291
+
292
+ ```
293
+ HIGH SCORE PERSIST (KERNALIO.H)
294
+ READ HISCORE,S,R
295
+ OPEN 1 ST=00 READ 0 ST=42
296
+ DRIVE: 62, FILE NOT FOUND,00,00
297
+ FIRST RUN
298
+ WRITE HISCORE,S,W 21 BYTES
299
+ OPEN 1 ST=00 WRITE 21 ST=00 CLOSE ST=00
300
+ DRIVE: 00, OK,00,00
301
+ DEFAULTS: ABC 5000 DEF 2500 GHI 1000
302
+ NEW: ABC 5000 YOU 4000 DEF 2500
303
+ SCRATCH S0:HISCORE
304
+ DRIVE: 01, FILES SCRATCHED,01,00
305
+ WRITE HISCORE,S,W 21 BYTES
306
+ OPEN 1 ST=00 WRITE 21 ST=00 CLOSE ST=00
307
+ DRIVE: 00, OK,00,00
308
+ READ HISCORE,S,R
309
+ OPEN 1 ST=00 READ 21 ST=40
310
+ DRIVE: 00, OK,00,00
311
+ BACK V1: ABC 5000 YOU 4000 DEF 2500
312
+ CHK F1F6 PASS
313
+
314
+ READY.
315
+ ```
316
+
317
+ Light blue text on blue, the ordinary power-on screen. The text is the
318
+ same on PAL and NTSC. Screenshots from the VICE runs this page
319
+ describes: `screenshots/high-score-persist.png` (PAL) and
320
+ `screenshots/high-score-persist-ntsc.png` (NTSC), both from the pinned
321
+ command above with a disk formatted as `TEST,01` immediately before the
322
+ run. A second PAL run on another fresh disk gave a pixel-identical PNG.
323
+ `c1541 -list` of the disk afterwards shows one file, `"hiscore" seq`,
324
+ and 663 blocks free.
325
+
326
+ Read the lines in pairs. `ST=` is `krnio_status()`, the KERNAL's status
327
+ byte (READST, `$FFB7`); after the read it is `krnio_pstatus[2]`, the
328
+ value the wrapper kept for that logical file. `DRIVE:` is the text the
329
+ 1541 put on its command channel.
330
+
331
+ - The first call is the OPEN for read, and it is the device test as
332
+ well: it sends the name `HISCORE,S,R` down the bus, so a bus with no
333
+ drive on it returns `OPEN 0 ST=80` (measured below). The 1541's
334
+ power-on banner, `73,CBM DOS V2.6 1541,00,00`, does not appear in
335
+ this run: the error the OPEN raises replaces it before the channel is
336
+ first read. A probe that reads the channel before any other command
337
+ gets the `73` line first (measured in the same VICE build).
338
+ - First run: the OPEN for read succeeds (`OPEN 1 ST=00`), the read
339
+ returns 0 bytes with ST `$42` (EOF and timeout bits) and the drive
340
+ says `62, FILE NOT FOUND`. The program takes that as "no save yet",
341
+ writes the defaults (21 bytes, ST `00` through open, write and close,
342
+ drive `00, OK`) and shows them.
343
+ - New score: `YOU 4000` is inserted in rank order and `GHI 1000` drops
344
+ off the end.
345
+ - Replace: `S0:HISCORE` on the command channel answers `01, FILES
346
+ SCRATCHED,01,00`, read from the same open channel. The rewrite then
347
+ opens cleanly. An earlier build of this listing wrote over the
348
+ existing name without the scratch: the drive answered `63, FILE
349
+ EXISTS,00,00`, `krnio_write` still returned 21 but ST was `$80` after
350
+ the write and the close, and nothing reached the disk. That `$80` is
351
+ the device-not-present bit (`KRNIO_NODEVICE`), not a timeout: the
352
+ drive did not acknowledge bytes on a channel it never opened.
353
+ Reading the
354
+ scratch reply after closing and reopening channel 15 gave `00, OK`
355
+ instead of the `01` line; the reply to a command lasts only while the
356
+ channel that sent it is open.
357
+ - Read-back: 21 bytes with ST `$40` (EOF), drive `00, OK`, and the
358
+ record decodes to the table that was written. `CHK F1F6` is the fold
359
+ `chk = ((chk ^ byte) * 5 + 1) & 0xFFFF` over those 21 bytes; Python
360
+ gives `0xF1F6` for the same bytes, so the line reads `PASS`.
361
+
362
+ ### The other paths, run once each (not pinned)
363
+
364
+ **Second run, same disk, file present.** The same command run again on
365
+ the disk the pinned run left behind. The read returns 21 bytes with ST
366
+ `$40` and the drive says `00, OK`; the program prints `LOADED: ABC 5000
367
+ YOU 4000 DEF 2500`. The new score is entered again, so the table becomes
368
+ `ABC 5000 YOU 4000 YOU 4000` (a tie is placed below the entry already
369
+ there), the scratch answers `01, FILES SCRATCHED,01,00`, the rewrite and
370
+ read-back succeed, and the last line reads `CHK B91C FAIL`. That FAIL
371
+ is expected: `EXPECT_CHK` is the first-run table's value, and Python's
372
+ fold of the second-run table is `0xB91C`, so the disk holds exactly
373
+ what was written. The screen ends at row 15 with `READY.` on row 17.
374
+
375
+ **No disk attached.** The same command with no `-8` argument. VICE
376
+ `-default` still emulates a 1541 on device 8, so the drive answers: the
377
+ OPEN for read succeeds with ST `00`, the read returns 0 bytes with ST
378
+ `$42`, and the error channel says `74,DRIVE NOT READY,00,00`. The
379
+ program prints `SAVING OFF (74)`, shows the defaults, enters the new
380
+ score, prints `NOT SAVED`, and the checksum line reads `CHK F1F6 PASS`
381
+ because it is then folded over the in-memory table, which is the same
382
+ 21 bytes. Nine lines of text on rows 0 to 8, `READY.` on row 10. This
383
+ measures a drive with no disk, not an absent drive.
384
+
385
+ **No device on the bus.** The same command with no `-8` and
386
+ `-drive8type 0` added, so that nothing is emulated on device 8. The
387
+ OPEN for read comes back as `OPEN 0 ST=80`: OPEN returned C=1,
388
+ and ST holds bit 7, `KRNIO_NODEVICE`, which the KERNAL sets when no
389
+ device pulls DATA low in answer to the LISTEN
390
+ (`../../hardware/kernal-routines-reference.md`, CHKOUT entry). The
391
+ program prints `DRIVE: NO DEVICE`, then `SAVING OFF (99)`, the defaults,
392
+ the new score, `NOT SAVED` and `CHK F1F6 PASS`: the same nine lines as
393
+ the no-disk case with the two drive lines changed, `READY.` on row 10.
394
+ The same screen comes from `+drive8truedrive` with no `-8` and the
395
+ drive type left at its default.
396
+
397
+ Two things about that path are worth knowing, both measured in this
398
+ VICE build with `-drive8type 0`. First, an OPEN of channel 15 with an
399
+ empty name cannot detect an absent drive: the KERNAL sends nothing on
400
+ the bus when the filename length is zero, so that OPEN returned 1 with
401
+ ST `00` with no drive present, while OPEN of `I0` on channel 15 and of
402
+ `HISCORE,S,R` on channel 2 both returned 0 with ST `$80`. Second, the
403
+ first CHKIN on a channel whose device does not answer hangs with no
404
+ timeout (`../../hardware/kernal-routines-reference.md`, CHKIN entry), and
405
+ `krnio_gets` starts with CHKIN. An earlier build of this listing read
406
+ the banner with an empty-name OPEN before anything else and then hung
407
+ inside `krnio_gets` with the title line alone on screen after 24,000,000
408
+ cycles. That is why `drive_reply` is only ever called after a named OPEN
409
+ has succeeded, and why the named OPEN of the save file comes first.
410
+
411
+ **Old format on the disk.** A 15-byte `HISCORE` with version byte 0 was
412
+ written to a fresh disk with `c1541 -write old.bin hiscore,s`. The read
413
+ returned 15 bytes with ST `$40` and drive `00, OK`; the program printed
414
+ `OLD FORMAT 15 BYTES V0: RESET`, scratched (`01, FILES SCRATCHED,01,00`),
415
+ wrote the defaults, then entered the new score, scratched and wrote
416
+ again, and read back `BACK V1: ABC 5000 YOU 4000 DEF 2500` with
417
+ `CHK F1F6 PASS`.
418
+
419
+ ## Why this works
420
+
421
+ The policy turns on the first two digits of the drive's reply, not on
422
+ the KERNAL's status byte alone. After a failed OPEN for read the
423
+ KERNAL only knows that the read timed out (ST `$42`); it is the error
424
+ channel that separates `62` (no file: write the defaults) from `74` (no
425
+ disk: leave the disk alone) from `00` (a file came back: check it).
426
+ The one case the status byte does settle on its own is an absent drive:
427
+ OPEN of a named file returns false with `KRNIO_NODEVICE` set, and the
428
+ program never touches the bus again. `drive_reply` opens logical file
429
+ 15 on secondary 15 with the string it is given, an empty name to read
430
+ the status or a DOS command such as `S0:HISCORE`, reads one line with
431
+ `krnio_gets`, strips the CR and closes; `code` keeps the two digits so
432
+ the caller can branch on them. The reply is read on the same open
433
+ channel in both uses because the reply to a command is gone once
434
+ channel 15 is closed.
435
+
436
+ A 21-byte record with a magic pair, a version byte and three fixed
437
+ six-byte entries lets `load_table` accept or reject a file with three
438
+ comparisons: the byte count, the magic and the version. A record that
439
+ grows in a later release changes the version byte, and the old file is
440
+ then rejected and rewritten rather than read as garbage. Oscar64 packs
441
+ the struct with no padding, so `sizeof(Table)` is the file size on disk
442
+ and the record can be written and read as raw bytes.
443
+
444
+ Replacing the file is scratch, then write. OPEN of `HISCORE,S,W` over an
445
+ existing name is refused by the DOS with `63, FILE EXISTS` and every
446
+ byte written afterwards comes back with ST `$80`, the device-not-present
447
+ bit (`KRNIO_NODEVICE`): the drive does not acknowledge bytes on a
448
+ channel it never opened. The earlier build of this listing measured
449
+ that, and the disk was unchanged afterwards. `@0:HISCORE,S,W` would ask
450
+ the DOS to replace in place; `../../techniques/file-io.md` says why this
451
+ KB does not lean on that command. A scratch of a name that is not there
452
+ costs one command-channel round trip and does no harm: `S0:NOSUCH` on a
453
+ disk without that file answers `01, FILES SCRATCHED,00,00`, count `00`,
454
+ and the directory is unchanged (measured in VICE). Every call here is a
455
+ KERNAL call, so the ROM must be banked in from the first OPEN to the
456
+ last CLOSE; the write of a 21-byte file and its close must also be
457
+ allowed to finish before the machine is reset or the emulator exits, or
458
+ the directory holds a splat file (`../../pitfalls/kernal-and-io.md`,
459
+ `krnio_save_leaves_splat_file`). The bare write-and-read sequence
460
+ without the policy is `save-load-seq-file.md` in this directory and
461
+ `../kickassembler/file-io-roundtrip.md` in assembly.
462
+
463
+ ## A raster IRQ during file I/O
464
+
465
+ The listing above runs with the KERNAL's own IRQ only. A game that
466
+ saves from a screen with a `rasterirq.h` split on it needs to know what
467
+ the file calls do to that split. Measured on 2026-09-22 in VICE x64sc
468
+ 3.10, true-drive 1541, fresh `TEST,01` disk, PAL and `-model ntsc`, with
469
+ the program quoted at the end of this section: a border split at rows
470
+ 100 (white) and 200 (light blue), a `rirq_call` handler on the row-100
471
+ slot that counts its entries and keeps the lowest and highest `$D012` it
472
+ saw on entry, and a 2,048-byte sequential file written and read back
473
+ with a 16-bit fold checked against the buffer that went out. Time is
474
+ CIA2 timers A and B cascaded into one 32-bit cycle counter, which the
475
+ KERNAL never touches; frames are those cycles over 19,656 or 17,095,
476
+ arithmetic.
477
+
478
+ | Build | Model | Write | Handler entries during it | `$D012` at entry | Read | Handler entries during it | `$D012` at entry |
479
+ |---|---|---|---|---|---|---|---|
480
+ | armed throughout | PAL | 9,359,321 cycles, 476 frames | 145 | 101 to 240 | 5,656,966 cycles, 288 frames | 226 | 101 to 234 |
481
+ | armed throughout | NTSC | 9,702,621 cycles, 568 frames | 169 | 101 to 241 | 5,991,068 cycles, 350 frames | 268 | 101 to 242 |
482
+ | `rirq_stop()` / `rirq_start()` around each call | PAL | 9,359,424 cycles, 476 frames | 144 | 101 to 238 | 5,659,833 cycles, 288 frames | 226 | 101 to 233 |
483
+ | `rirq_stop()` / `rirq_start()` around each call | NTSC | 9,702,713 cycles, 568 frames | 168 | 101 to 240 | 5,983,652 cycles, 350 frames | 270 | 101 to 247 |
484
+ | `$D01A = 0` around each call | PAL | 9,158,718 cycles, 466 frames | 0 | none | 5,553,710 cycles, 283 frames | 0 | none |
485
+ | `$D01A = 0` around each call | NTSC | 9,498,969 cycles, 556 frames | 0 | none | 5,765,998 cycles, 337 frames | 0 | none |
486
+
487
+ Every row: `OPEN=1`, 2,048 bytes written with ST `00` after the close,
488
+ 2,048 bytes read with ST `40`, `CHK F000/F000 PASS`, `DRIVE: 00, OK,00,00`.
489
+ Idle before and after, the handler entered on line 101 in every frame
490
+ (50 of 50 before; 48 or 49 of the 50 `rirq_wait()` calls after, the
491
+ first call returning early on the count already in flight). The file is
492
+ never at risk; the picture and the frame count are.
493
+
494
+ Read the armed rows against the idle line. In 476 PAL frames of writing
495
+ the row-100 handler ran 145 times, and when it ran it entered anywhere
496
+ from line 101 to line 240. The KERNAL's serial primitives run every
497
+ byte with the I flag set and hold it while they wait for the drive, with
498
+ no timeout on those waits (`$ED50` to `$ED5D`, `$EDD6`, `$EE1B`; ROM
499
+ bytes, rung 1). Under the monitor one such wait, the TALK that follows a
500
+ directory lookup, held the flag for 1,512,351 cycles. A raster match
501
+ inside the window is delivered at the `CLI`, late; a second match in the
502
+ same window is lost. `rirq_stop()` is `sei` alone (`rasterirq.c`), and
503
+ the first primitive's `CLI` cancels it, which is why its rows repeat the
504
+ armed rows to within a frame. Clearing `$D01A` is the stop that holds:
505
+ no entries, and the transfer two per cent shorter.
506
+
507
+ The exit screenshot at 8,000,000 cycles lands inside the write. Armed
508
+ on PAL, the left border is white on lines 240 to 242 only: the row-100
509
+ slot was delivered around line 240 and the row-200 slot ran straight
510
+ after it. Armed on NTSC, white on lines 104 to 201 against 101 to 200
511
+ idle. With `$D01A` cleared, one colour for the whole frame, white on PAL
512
+ and light blue on NTSC, whichever the border held when the register
513
+ was cleared. In every split the right border changes one row above the
514
+ left, the store landing mid-line. These pictures are not pinned; the
515
+ recipe's own run is.
516
+
517
+ Recommendation: save and load on a static screen. Set the border and
518
+ the screen to what you want held, clear `$D01A`, make the calls, then
519
+ write 1 to `$D019` and set `$D01A` again; `rasterirq.h` resumes on its
520
+ next row. Do not use `rirq_stop()` for this. If the IRQ must stay armed,
521
+ do not count frames or drive music from it across the calls; the
522
+ pitfall is `../../pitfalls/kernal-and-io.md`,
523
+ `raster_irq_during_serial_io`. The transfer rate, about 216 bytes per
524
+ second writing and 355 reading on either model, is the KERNAL's.
525
+
526
+ The test program, built three ways with `-dMODE=0`, `1` and `2`. It is
527
+ not a recipe listing; the numbers above are its output, read from the
528
+ exit screenshots of runs at 40,000,000 cycles:
529
+
530
+ ```text
531
+ // iotest.c -- a rirq border split kept armed while 2 KB goes to a
532
+ // SEQ file on drive 8 and comes back. Build with -dMODE=0 (armed
533
+ // throughout), -dMODE=1 (rirq_stop()/rirq_start() around each file
534
+ // call) or -dMODE=2 ($D01A cleared around each file call).
535
+ // Prints, per phase: KERNAL status, CIA2 cycles, frames from that,
536
+ // raster IRQs counted by the handler, and the min/max $D012 seen at
537
+ // handler entry for the split slot (row SPLIT_ROW).
538
+ #include <stdio.h>
539
+ #include <string.h>
540
+ #include <c64/vic.h>
541
+ #include <c64/cia.h>
542
+ #include <c64/rasterirq.h>
543
+ #include <c64/kernalio.h>
544
+
545
+ #ifndef MODE
546
+ #define MODE 0
547
+ #endif
548
+ #define DRIVE 8
549
+ #define SIZE 2048
550
+ #define SPLIT_ROW 100
551
+ #define RESTORE_ROW 200
552
+
553
+ static RIRQCode split, restore;
554
+ static char buf[SIZE];
555
+ static char back[SIZE];
556
+ static char reply[40];
557
+
558
+ static volatile unsigned irqs; // split-slot handler entries
559
+ static volatile char rmin, rmax; // $D012 at handler entry
560
+
561
+ __interrupt void on_split(void)
562
+ {
563
+ char r = vic.raster;
564
+ if (r < rmin) rmin = r;
565
+ if (r > rmax) rmax = r;
566
+ irqs++;
567
+ }
568
+
569
+ static void stats_reset(void)
570
+ {
571
+ irqs = 0; rmin = 255; rmax = 0;
572
+ }
573
+
574
+ // CIA2 timer A counts cycles, timer B counts A underflows: a 32-bit
575
+ // down counter the KERNAL never touches (its serial timeout is CIA1 B).
576
+ static void clock_init(void)
577
+ {
578
+ cia2.cra = 0x00; cia2.crb = 0x00;
579
+ cia2.ta = 0xffff; cia2.tb = 0xffff;
580
+ cia2.crb = 0x51; // count TA underflows, force load, start
581
+ cia2.cra = 0x11; // cycles, force load, start
582
+ }
583
+
584
+ static unsigned long clock_now(void)
585
+ {
586
+ unsigned hi, lo, hi2;
587
+ do {
588
+ hi = cia2.tb; lo = cia2.ta; hi2 = cia2.tb;
589
+ } while (hi != hi2);
590
+ return 0xffffffffUL - (((unsigned long)hi << 16) | lo);
591
+ }
592
+
593
+ static unsigned fold(const char *p, int n)
594
+ {
595
+ unsigned c = 0;
596
+ for (int i = 0; i < n; i++)
597
+ c = (c ^ p[i]) * 5 + 1;
598
+ return c;
599
+ }
600
+
601
+ static unsigned long period; // cycles per frame for this model
602
+
603
+ static void report(const char *tag, char st, unsigned long cyc)
604
+ {
605
+ printf("%s ST=%02X CYC=%lu F=%lu IRQ=%u R=%u..%u\n",
606
+ tag, st, cyc, cyc / period, irqs, rmin, rmax);
607
+ }
608
+
609
+ static void io_begin(void)
610
+ {
611
+ #if MODE == 1
612
+ rirq_stop();
613
+ #elif MODE == 2
614
+ vic.intr_enable = 0;
615
+ #endif
616
+ }
617
+
618
+ static void io_end(void)
619
+ {
620
+ #if MODE == 1
621
+ rirq_start();
622
+ #elif MODE == 2
623
+ vic.intr_ctrl = 1;
624
+ vic.intr_enable = 1;
625
+ #endif
626
+ }
627
+
628
+ static void drive_reply(void)
629
+ {
630
+ reply[0] = 0;
631
+ krnio_setnam("");
632
+ io_begin();
633
+ if (krnio_open(15, DRIVE, 15)) {
634
+ int n = krnio_gets(15, reply, sizeof(reply));
635
+ if (n > 0 && reply[n - 1] == 13) reply[n - 1] = 0;
636
+ krnio_close(15);
637
+ }
638
+ io_end();
639
+ printf("DRIVE: %s\n", reply);
640
+ }
641
+
642
+ int main(void)
643
+ {
644
+ printf("%cIOTEST MODE %d\n", 147, MODE);
645
+
646
+ // model: the highest raster line seen in one frame
647
+ char top = 0;
648
+ for (unsigned i = 0; i < 20000; i++) {
649
+ char r = vic.raster;
650
+ if ((vic.ctrl1 & 0x80) && r > top) top = r;
651
+ }
652
+ bool pal = top >= 0x37; // 0x137 = 311 on PAL; NTSC tops at 0x106
653
+ period = pal ? 19656UL : 17095UL;
654
+ printf("%s\n", pal ? "PAL" : "NTSC");
655
+
656
+ for (int i = 0; i < SIZE; i++) buf[i] = (char)(i * 7 + 3);
657
+ unsigned want = fold(buf, SIZE);
658
+
659
+ rirq_init(true);
660
+ rirq_build(&split, 2);
661
+ rirq_write(&split, 0, &vic.color_border, VCOL_WHITE);
662
+ rirq_call(&split, 1, on_split);
663
+ rirq_set(0, SPLIT_ROW, &split);
664
+ rirq_build(&restore, 1);
665
+ rirq_write(&restore, 0, &vic.color_border, VCOL_LT_BLUE);
666
+ rirq_set(1, RESTORE_ROW, &restore);
667
+ rirq_sort();
668
+ rirq_start();
669
+ clock_init();
670
+
671
+ // idle baseline: 50 frames of the split with the bus quiet
672
+ stats_reset();
673
+ unsigned long t0 = clock_now();
674
+ for (char i = 0; i < 50; i++) rirq_wait();
675
+ report("IDLE", 0, clock_now() - t0);
676
+
677
+ // write
678
+ stats_reset();
679
+ krnio_setnam("IOTEST,S,W");
680
+ t0 = clock_now();
681
+ io_begin();
682
+ bool ok = krnio_open(2, DRIVE, 2);
683
+ int n = ok ? krnio_write(2, buf, SIZE) : 0;
684
+ krnio_close(2);
685
+ char st = krnio_status();
686
+ io_end();
687
+ report("WRITE", st, clock_now() - t0);
688
+ printf(" OPEN=%d N=%d\n", ok, n);
689
+
690
+ // read back
691
+ stats_reset();
692
+ krnio_setnam("IOTEST,S,R");
693
+ t0 = clock_now();
694
+ io_begin();
695
+ ok = krnio_open(2, DRIVE, 2);
696
+ n = ok ? krnio_read(2, back, SIZE) : 0;
697
+ st = krnio_pstatus[2];
698
+ krnio_close(2);
699
+ io_end();
700
+ report("READ", st, clock_now() - t0);
701
+ unsigned got = fold(back, n);
702
+ printf(" OPEN=%d N=%d CHK=%04X/%04X %s\n", ok, n, got, want,
703
+ (n == SIZE && got == want) ? "PASS" : "FAIL");
704
+
705
+ drive_reply();
706
+
707
+ // idle again: does the split come back the same
708
+ stats_reset();
709
+ t0 = clock_now();
710
+ for (char i = 0; i < 50; i++) rirq_wait();
711
+ report("AFTER", 0, clock_now() - t0);
712
+ printf("DONE\n");
713
+ for (;;) ;
714
+ return 0;
715
+ }
716
+ ```
717
+
718
+ One defect in it, left as run: the model check at the top read `PAL` on
719
+ NTSC in the `MODE=1` and `MODE=2` builds, so their printed `F=` values
720
+ divided by 19,656. The frames in the table were recomputed from the
721
+ printed cycle counts, which do not depend on it. The `MODE=0` NTSC build
722
+ detected NTSC and its printed frames agree with the table.
723
+
724
+ ## A start-up hang seen in VICE, located but not explained
725
+
726
+ A game built from this recipe, whose first act after its own set-up was
727
+ `krnio_open` of `HISCORE,S,R` on a fresh disk, hung in that OPEN's
728
+ read-back on PAL every time and not on NTSC (VICE x64sc 3.10,
729
+ `-autostart` with `-8`, true drive, `+autostart-delay-random`; three
730
+ plain PAL runs hung, one plain NTSC run and one under the monitor did
731
+ not, and the game's own author had seen the same split before the
732
+ delay went in). Fifty `vic_waitFrame()` calls
733
+ before the OPEN, or an unrelated change of code size, removed it. What
734
+ follows is what the remote monitor showed on 2026-09-22, rung 1. The
735
+ figures come from three monitor sessions with different breakpoint
736
+ sets; they agree to the cycle at OPEN and drift apart by about 70,000
737
+ cycles after the first drive-side stop, so read each bullet as its own
738
+ run:
739
+
740
+ - The drive was not resetting. Its idle loop at `$EBE7` was first
741
+ reached 1,002,456 drive cycles after reset; the program's OPEN
742
+ entered `$F34A` at C64 cycle 3,173,695, about two seconds later.
743
+ - OPEN itself completed. LISTEN, the eleven name bytes and UNLISTEN
744
+ each went out in about 1,150 cycles. The TALK byte for the read
745
+ entered `$ED40` at 3,196,393 and left through `$EDAB` at 4,708,744:
746
+ the drive served ATN (`$E85B`) only when its directory search had
747
+ finished, and the C64 stayed inside `$ED40`'s `SEI` bracket for those
748
+ 1,512,351 cycles. By the ROM flow the wait is the DATA loop at
749
+ `$ED5A`, not stopped on here. TKSA (`$62`) followed at 4,709,842 to
750
+ 4,710,959.
751
+ - The spin is `$EDD6`, the loop after TKSA that waits for the drive to
752
+ pull CLK low and become the talker. It has no timeout. `$DD00` read
753
+ `$67` on every pass: DATA held low by the C64, CLK released by both
754
+ sides. The drive's port `$1800` read `$01` (DATA OUT set, CLK OUT
755
+ clear), its secondary-address cell `$84` held `$62`, its ATN-pending
756
+ flag `$7C` was 0. The drive had taken TALK and TKSA and stayed a
757
+ listener.
758
+ - Reading the drive CPU's registers from the monitor at each C64 stop
759
+ made the same run pass: the drive went through `$E8E5`, `$E8F4` and
760
+ `$E909`, the C64 reached ACPTR at `$EE13`, and the program went on
761
+ to its next OPEN. Memory dumps alone did not change the outcome. Two
762
+ runs each way.
763
+
764
+ What was measured is consistent with a race at the TALK turnaround
765
+ between the C64 releasing ATN and CLK and the drive sampling them. Why
766
+ the drive misses the release, and whether a real drive can, is not
767
+ measured here: the drive's state in the hanging case could only be read
768
+ by an act that changed it. What a program can do is what the
769
+ game did: give the bus a moment before the first OPEN, and check the
770
+ status and the error channel after it rather than assuming the read
771
+ returns. A wait of fifty frames was enough in every run tried; the
772
+ smallest wait that suffices was not measured.