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,1072 @@
1
+ ---
2
+ category: input
3
+ chip: CIA
4
+ ---
5
+
6
+ <!-- doc-type: technique-reference -->
7
+
8
+ # Input Techniques
9
+
10
+ Reading the joystick ports, the paddles and the keyboard matrix directly
11
+ from CIA1 and the SID, without the KERNAL's once-per-jiffy scanner. The
12
+ techniques share one fact: CIA1 port A (`$DC00`) is control port 2 and the keyboard's
13
+ column drive, CIA1 port B (`$DC01`) is control port 1 and the keyboard's
14
+ row sense, and every switch on either port reads as a 0 bit when closed.
15
+ The wiring is in `hardware/cia-reference.md`; the ways it bites are in
16
+ `pitfalls/input.md`. Every number below that came from an instrument says
17
+ so; the rest is marked as arithmetic or as not measured here.
18
+
19
+ ---
20
+
21
+ ## joystick_edge_detect — Joystick press events by previous-frame XOR
22
+
23
+ **Complexity:** low
24
+ **Region:** both
25
+ **Uses registers:** DC00, DC01
26
+ **Claims:** cia1_port_a (reads), cia1_port_b (reads)
27
+ **Claims basis:** derived-listing
28
+
29
+ ### Why
30
+
31
+ A port byte is a level: bit 4 low means the fire button is down right
32
+ now. A game usually wants an event: the frame the button went down, so
33
+ one press fires one shot, or the frame it came up. Reading the level
34
+ every frame and acting on it gives one shot per frame for as long as the
35
+ button is held.
36
+
37
+ ### How
38
+
39
+ Keep last frame's port byte. Once per frame read the port again and
40
+ compare the two:
41
+
42
+ ```
43
+ pressed = ~cur & $1F ; lines that are low now
44
+ new = pressed & prev ; low now, high last frame
45
+ held = pressed & ~prev & $1F ; low now, low last frame
46
+ released = cur & ~prev & $1F ; high now, low last frame
47
+ prev = cur
48
+ ```
49
+
50
+ `cur ^ prev` is the set of lines that changed; masking that with `prev`
51
+ picks the ones that changed from high to low, which is the same set as
52
+ `new` above. Bit 0 is up, 1 down, 2 left, 3 right, 4 fire, on both ports
53
+ (`hardware/cia-reference.md`, and Oscar64's `joystick.c` tests the same
54
+ five bits). Bits 5 to 7 are keyboard lines and paddle plumbing; mask them
55
+ off or a held key turns into a phantom event.
56
+
57
+ Port 2 is `$DC00`, port 1 is `$DC01`. The same code serves both; only the
58
+ address differs. The first `prev` should be `$FF` (nothing pressed), so a
59
+ button already down when the program starts counts as a new press on
60
+ frame 0 rather than being missed.
61
+
62
+ Oscar64's library path is `joy_poll(n)` from `<c64/joystick.h>`, which
63
+ reads `$DC00 + n` (so `joy_poll(0)` is control port 2) and leaves
64
+ `joyx[n]`, `joyy[n]` as signed -1, 0 or 1 and `joyb[n]` as a bool (read
65
+ from the header and `joystick.c`). It reports levels, not edges; keep the
66
+ previous `joyb[n]` yourself if you use it. The bare-register path is one
67
+ `LDA $DC00` per frame and the four lines above.
68
+
69
+ ### Why it works
70
+
71
+ The five switch lines have pull-ups inside the CIA; a closed switch grounds
72
+ its line and the port read returns 0 in that bit. Nothing latches: the byte
73
+ is the state of the switches at the cycle of the read. The comparison with
74
+ the previous frame is the only memory in the system, which is why the
75
+ program has to hold it.
76
+
77
+ Timing the read to the frame matters. The read is instantaneous; the
78
+ comparison assumes one read per frame. A read on a raster line the game
79
+ picks (the recipe waits for line 250) gives a steady 50 or 60 samples a
80
+ second on PAL or NTSC. If the KERNAL IRQ is still running, a main-loop read
81
+ of `$DC00` sees `$7F` at rest, not `$FF`: the KERNAL leaves column 7
82
+ driven low between scans (measured in `pitfalls/input.md`). With the
83
+ mask applied it makes no difference to the five joystick bits.
84
+
85
+ ### Variations
86
+
87
+ **Released events.** `released` above is free; use it for a charge-and-let-go
88
+ shot or to end a jump early.
89
+
90
+ **Both ports in one pass.** Read `$DC00` and `$DC01` back to back into two
91
+ bytes and run the four lines twice. For port 1 write `$FF` to `$DC00`
92
+ first so no keyboard column is selected; otherwise a key held in the
93
+ selected column reads as a port 1 direction (see `keyboard_matrix_scan`).
94
+
95
+ **Diagonal cleanup.** Up and down cannot both be low on a working stick,
96
+ but a cheap one can bounce through it. Oscar64's `joy_poll` resolves the
97
+ conflict by testing up before down and left before right; do the same or
98
+ mask the pair to zero.
99
+
100
+ ### Exhaustive check
101
+
102
+ The recipe below runs the four lines over every (prev, cur) pair, all
103
+ 65,536 of them, and folds `new | held << 5 | released << 10` into a 16-bit
104
+ checksum. The 6502 shows `1800 PASS`; the same fold in Python gives `1800`
105
+ (measured in VICE x64sc 3.10, rung 1).
106
+
107
+ ### Recipes
108
+
109
+ - `recipes/oscar64/joystick-input.md`
110
+
111
+ ---
112
+
113
+ ## joystick_autorepeat — Delayed auto-repeat from per-direction age counters
114
+
115
+ **Complexity:** low
116
+ **Region:** both
117
+ **Uses registers:** DC00, DC01
118
+ **Requires:** joystick_edge_detect
119
+ **Claims:** cia1_port_a (reads), cia1_port_b (reads)
120
+ **Claims basis:** derived-listing
121
+
122
+ ### Why
123
+
124
+ A falling-block or menu game wants a held direction to move once at
125
+ once, pause, then step at a steady rate. Acting on the level moves every
126
+ frame, which is too fast to steer; acting only on the press event needs a
127
+ tap per cell, which is too slow. The KERNAL keyboard queue has its own
128
+ repeat but no joystick equivalent and no way to tune the delay per
129
+ direction, which is why `game-design/c64-game-archetypes.md` says
130
+ action-puzzle games keep their own age counters.
131
+
132
+ ### How
133
+
134
+ One byte per direction counts frames held. Each frame:
135
+
136
+ ```
137
+ if line is high: age = 0, no fire
138
+ else: age = age + 1
139
+ if age == DELAY + RATE: age = DELAY
140
+ fire if age == 1 or age == DELAY
141
+ ```
142
+
143
+ `DELAY` is the number of frames before the first repeat and `RATE` the
144
+ frames between repeats after that. Resetting `age` to `DELAY` instead of
145
+ letting it climb keeps the counter in a byte and avoids a modulo. The
146
+ recipe uses `DELAY` 20 and `RATE` 4: on PAL that is a first repeat after
147
+ 0.4 s and then 12.5 steps a second, on NTSC 0.33 s and 15 a second
148
+ (arithmetic from 50 and 60 frames a second, rung 3). A 100-frame hold
149
+ fires 22 times: once at frame 1, once at frame 20, then every four frames
150
+ (arithmetic, and the same number from the Python model).
151
+
152
+ The first-frame fire is the press event from `joystick_edge_detect`
153
+ expressed through the counter: `age == 1` is true only on the frame after
154
+ the line went low. Games that already consume press events elsewhere can
155
+ drop that term and fire only on `age == DELAY`.
156
+
157
+ Frames, not jiffies. The counters advance once per frame the game draws,
158
+ so the feel tracks the region: the same `DELAY` is a fifth slower on PAL.
159
+ A region-aware program scales the two constants after
160
+ `pal_ntsc_detection` (`techniques/raster.md`) or accepts the difference.
161
+
162
+ ### Why it works
163
+
164
+ The CIA gives no press history; the age counter is the history. Because it
165
+ is per direction, a player rolling from left to down keeps a fresh counter
166
+ for down while left's counter clears, so the first step in the new
167
+ direction is immediate. A single shared counter would inherit the old
168
+ direction's age and skip the initial delay.
169
+
170
+ ### Variations
171
+
172
+ **Count-down timer.** Load a timer with `DELAY` on the press, decrement
173
+ while held, fire and reload with `RATE` when it hits zero. Same behaviour,
174
+ one compare fewer per frame, but the count of frames held is lost.
175
+
176
+ **Accelerating repeat.** Shrink `RATE` each time it fires, with a floor, for
177
+ a cursor that speeds up while held.
178
+
179
+ **Soft drop.** For a falling piece, hold down usually bypasses the delay
180
+ and moves every `RATE` frames from the first; use `DELAY` = 1 for that
181
+ direction only.
182
+
183
+ ### Exhaustive check
184
+
185
+ The recipe runs the step over every (age, pressed) pair, 256 by 2, and
186
+ folds `age' | fire << 8` into the same 16-bit checksum. The 6502 shows
187
+ `D1CC PASS`; Python gives `D1CC` (measured in VICE x64sc 3.10, rung 1).
188
+
189
+ ### Recipes
190
+
191
+ - `recipes/oscar64/joystick-input.md`
192
+
193
+ ---
194
+
195
+ ## keyboard_matrix_scan — Read the keyboard matrix from the main loop, without the KERNAL
196
+
197
+ **Complexity:** medium
198
+ **Region:** both
199
+ **Uses registers:** DC00, DC01, DC02, DC03
200
+ **Claims:** cia1_port_a (owns), cia1_port_b (reads)
201
+ **Claims basis:** derived-listing
202
+
203
+ ### Why
204
+
205
+ `GETIN` hands out one PETSCII character per call from a ten-byte queue.
206
+ It cannot say whether a key is still down, cannot see two keys at once
207
+ outside the shift and control modifiers, and runs on the jiffy IRQ's
208
+ schedule. A game that steers with keys needs the matrix itself: which of
209
+ the 64 switches is closed this frame.
210
+
211
+ ### How
212
+
213
+ The keyboard is 8 columns by 8 rows. Columns hang on port A, rows on port
214
+ B. Set the direction registers once, then walk one low bit across the
215
+ columns and read the rows after each write:
216
+
217
+ ```
218
+ $DC02 = $FF ; port A all output (column drive)
219
+ $DC03 = $00 ; port B all input (row sense)
220
+ col = $FE
221
+ for i in 0..7:
222
+ $DC00 = col ; drive column i low, the other seven high
223
+ rows[i] = $DC01 ; bit r is 0 if key (column i, row r) is down
224
+ col = col << 1 | 1
225
+ $DC00 = $FF ; leave no column selected
226
+ ```
227
+
228
+ Key (column c, row r) is down when bit r of `rows[c]` is 0. The layout
229
+ table is in `hardware/cia-reference.md`; SPACE is column 7, row 4, so
230
+ `rows[7] & $10` is 0 while SPACE is held. Keep last frame's eight bytes
231
+ and the XOR trick from `joystick_edge_detect` gives press and release
232
+ events per key; an age counter per key gives auto-repeat.
233
+
234
+ The Oscar64 library does exactly this in `keyb_poll()` from
235
+ `<c64/keyboard.h>`: `keyb_matrix[8]` holds the eight row bytes as read,
236
+ `key_pressed(KSCAN_x)` tests bit `code & 7` of `keyb_matrix[code >> 3]`,
237
+ so the scan code is `column * 8 + row` and `KSCAN_SPACE` is 60. It also
238
+ keeps the previous scan and reports the newest press in `keyb_key` with
239
+ `KSCAN_QUAL_DOWN` (`$80`) set. All from the header and `keyboard.c`,
240
+ build 2026-05-19. The library's scan first tests `$DC01` with every
241
+ column driven low and returns early if it reads `$FF`, which is the
242
+ KERNAL's own no-key shortcut.
243
+
244
+ ### Why it works
245
+
246
+ There are no diodes in the matrix: a closed switch is a wire from its
247
+ column line to its row line. With one column pulled low and the rows
248
+ floating high through their pull-ups, only switches in that column can pull
249
+ a row down. Eight passes cover the 64 switches. Leaving all columns high
250
+ afterwards, or all low, is a choice; the KERNAL leaves `$7F` (column 7
251
+ low), which is why the STOP key can be tested with a single read of
252
+ `$DC01` bit 7 (`hardware/c64-memory-map.md`).
253
+
254
+ **Ghosting.** Because a switch is just a wire, three closed keys at three
255
+ corners of a rectangle in the matrix connect the fourth corner's column to
256
+ its row through the other three, and the scan reads four keys. Two keys
257
+ in the same row or the same column never ghost; three keys sharing no
258
+ row or column never ghost. From the wiring (`hardware/cia-reference.md`),
259
+ not measured here: a headless VICE run cannot hold three keys.
260
+
261
+ **Why a main-loop scan and a joystick in port 1 interfere.** Port 1's five
262
+ switches sit on the row lines PB0 to PB4. A held direction grounds its
263
+ row regardless of which column is driven, so the scan reads that row as
264
+ pressed in all eight columns: up (PB0) looks like DEL, 3, 5, 7, 9, +,
265
+ pound and 1 held together. In the other direction, while the scan has
266
+ column c driven low, any key in column c pulls its row down, and a read of
267
+ `$DC01` taken as joystick 1 at that moment sees a direction. The KERNAL's
268
+ version of this, with column 7 left selected, is measured in
269
+ `pitfalls/input.md`: 1, left-arrow, CTRL, 2 and SPACE read as up, down,
270
+ left, right and fire on port 1. The fix is ordering: read port 1 only
271
+ with `$DC00` = `$FF`, and read the matrix only when the game does not also
272
+ expect port 1 input, or accept that a port 1 stick is a set of keys. Port
273
+ 2 sits on the column lines PA0 to PA4; a held port 2 direction grounds a
274
+ column the scan did not select, so keys in that column show up in every
275
+ pass (from the wiring, not measured here). Games that use port 2 and the
276
+ keyboard together therefore see keyboard corruption only while the stick
277
+ is held, and only for keys in five of the eight columns.
278
+
279
+ **Why the KERNAL IRQ must be out of the way.** SCNKEY writes `$DC00` on
280
+ its own schedule inside the jiffy IRQ. If it fires between your column
281
+ write and your row read, the row byte belongs to whichever column SCNKEY
282
+ left selected, and its final `$7F` clobbers your walk. Either take over
283
+ the IRQ, or bracket the eight-column loop with `SEI`/`CLI`, or (the
284
+ recipe's choice) run with interrupts off and sync to a raster line
285
+ instead of the jiffy. The pitfall page's measurements put the main-loop
286
+ hazard on `$DC00` only, but a matrix scan writes `$DC00`, so it is exposed
287
+ where a plain joystick poll is not.
288
+
289
+ **Debouncing is not needed.** A mechanical contact bounces for a few
290
+ milliseconds when it closes (rung 4, not measured here); a scan once per
291
+ frame samples every 20 ms on PAL and 16.7 ms on NTSC (arithmetic from the
292
+ frame rates). The sample period is the debounce: the read lands inside a
293
+ bounce rarely, and when it does the worst case is one press event that
294
+ arrives a frame late, or one extra release-and-press pair, which a
295
+ per-key age counter absorbs since the counter resets to 0 and fires
296
+ again on the next frame. The KERNAL's SCNKEY does no bounce filtering
297
+ either; it keeps the last scan's matrix code in `$C5`
298
+ (`hardware/c64-memory-map.md`) and decides new-versus-repeat against it,
299
+ which is the same previous-scan comparison, not a timing filter (from the
300
+ ROM's variable layout; the comparison itself is not measured here). A scan
301
+ that runs many times per frame, for instance inside a tight wait loop,
302
+ does need a filter, because it can see both edges of a bounce.
303
+
304
+ ### Variations
305
+
306
+ **Only the keys you use.** Drive just the columns holding the game's keys
307
+ and skip the rest; a WASD plus SPACE game needs columns 1, 2 and 7.
308
+
309
+ **Shift handling.** Left shift is column 1 row 7, right shift column 6
310
+ row 4 (`hardware/cia-reference.md`; Oscar64's `key_shift()` tests the
311
+ same two bits). Read them as ordinary keys and use them as modifiers in
312
+ software.
313
+
314
+ **No-key shortcut.** Drive all eight columns low (`$DC00` = `$00`) and read
315
+ `$DC01` once; `$FF` means no key and no port 1 input, and the eight-pass
316
+ scan can be skipped that frame.
317
+
318
+ ### Recipes
319
+
320
+ - `recipes/oscar64/joystick-input.md`
321
+
322
+ ---
323
+
324
+ ## paddle_read — Read the paddles through the SID's POTX/POTY with the CIA1 port select
325
+
326
+ **Complexity:** medium
327
+ **Region:** both
328
+ **Uses registers:** D419, D41A, DC00, DC01, DC02
329
+ **Cost:** cycles_per_frame=1152, zp_bytes=0, irq_slots=0
330
+ **Cost basis:** arithmetic
331
+ **Cost measured on:** kickassembler-paddle-read (two ports in one frame)
332
+
333
+ ### Why
334
+
335
+ A paddle is a potentiometer, and the C64 reads it through the SID, not
336
+ the CIA. `$D419` (POTX) and `$D41A` (POTY) hold an 8-bit conversion of
337
+ the voltage on the two pot pins of one control port. Which port is a
338
+ choice the program makes through CIA1 port A: bit 6 of `$DC00` set
339
+ selects control port 1, bit 7 set selects port 2. Both ports carry two
340
+ pots, so a four-paddle game reads all four values through the same two
341
+ registers by switching the select and reading twice. The registers are
342
+ read-only; there is nothing to set up on the SID side.
343
+
344
+ ### How
345
+
346
+ The direction register `$DC02` must be `$FF`, which is how IOINIT
347
+ leaves it; the select bits are outputs. Then, per port:
348
+
349
+ ```text
350
+ $DC00 = $40 ; bit 6: control port 1 (or $80, bit 7: port 2)
351
+ wait ; a conversion has to start and finish after the switch
352
+ x1 = $D419 ; paddle 1 (port 1 pin 9)
353
+ y1 = $D41A ; paddle 2 (port 1 pin 5)
354
+ $DC00 = $80
355
+ wait
356
+ x2 = $D419
357
+ y2 = $D41A
358
+ ```
359
+
360
+ The wait is the whole difficulty. The SID converts on its own clock, one
361
+ conversion every 512 cycles, and the switch does not restart it. A read
362
+ taken before a conversion that began after the switch has completed
363
+ returns the old port's value or a byte from the middle of a conversion.
364
+ Measured in VICE x64sc 3.10 with a 1351 as the pot source, sampling
365
+ `$D419` every 8 cycles after the select write at eight phases 65 cycles
366
+ apart: at six phases the settled value was present within 480 cycles
367
+ (from cycle 476 at phase 0, on PAL and NTSC), at two phases it needed
368
+ more than 512 and was present within 544; nothing settled between 481
369
+ and 512. So in VICE 512 is the conversion period, not a safe delay from
370
+ the write. Wait at least 576 cycles, which is the measured worst case of
371
+ 544 plus 32 of margin; that figure is measured in VICE, and the hardware
372
+ worst case after a mid-conversion switch is not measured here. Or use
373
+ the schedule most games use, select on one frame and read on the next:
374
+ a frame is 19,656 cycles on PAL and 17,095 on NTSC (arithmetic: 312
375
+ lines of 63 and 263 lines of 65), and both ports fit in two frames.
376
+
377
+ Reading both ports means alternating the select and paying the wait
378
+ each time. The cost line is two waits of 576 cycles, the recommended
379
+ wait, for a two-port read done in one frame; the reads and writes
380
+ themselves are under 30 cycles. A program that selects at the end of
381
+ its frame and reads at the start of the next spends none of that
382
+ waiting.
383
+
384
+ The paddle buttons are not on the SID. They are read on the port's
385
+ LEFT and RIGHT switch lines (bits 2 and 3 of the joystick byte: `$DC00`
386
+ for port 2, `$DC01` for port 1, low when pressed). That the paddle
387
+ buttons ride those two lines is not measured here, since a headless
388
+ VICE run cannot press one, and is not stated on
389
+ `hardware/cia-reference.md`, which places the paddle switches somewhere
390
+ in PA0-PA4. The recipe shows both bytes.
391
+
392
+ ### Why it works
393
+
394
+ The two select bits drive the SID's analogue multiplexer directly from
395
+ the CIA pins; the SID sees one port's pots at a time and converts what
396
+ it sees. The usual account of the SID's converter, not measured here,
397
+ is that each conversion discharges a capacitor and then counts cycles
398
+ until the pot charges it again, which would make the result a cycle
399
+ count in 0 to 255 and the period 512 cycles, half to discharge and half
400
+ to count. The two-bucket settle pattern measured above is VICE's model
401
+ of that converter, and this page does not claim it for the chip. What
402
+ the measurement does show is that the register holds the last finished
403
+ conversion, whichever port that was for, until the next one completes.
404
+ Selecting neither port reads
405
+ `$FF`, as does an empty port, since the pin floats high (measured in
406
+ VICE; the same reading a paddle at one end of its travel gives, so a
407
+ game cannot tell an empty port from a paddle at full scale).
408
+
409
+ The keyboard scan interferes because it owns the same bits. SCNKEY, in
410
+ the jiffy IRQ, writes `$DC00` to drive the columns and leaves `$7F`
411
+ there when it is done: bit 6 set, bit 7 clear, control port 1
412
+ selected. A program that selected port 2 and is waiting for the
413
+ conversion has, after the interrupt, port 1 selected and does not know
414
+ it. Measured in VICE: 2,000 passes of select port 2, wait 530 cycles,
415
+ read, with the KERNAL IRQ live, returned something other than port 2's
416
+ value 53 to 59 times per run, about one per jiffy interrupt, and the
417
+ wrong values were port 1's or a mid-conversion byte. The same 2,000
418
+ passes under `SEI` were wrong 0 times. Three ways to time the read
419
+ against the scan: bracket select, wait and read with `SEI`/`CLI`; own
420
+ the IRQ, so no scan runs; or select and read at fixed raster lines and
421
+ keep the KERNAL scan's line, which is wherever the jiffy IRQ lands, out
422
+ of the window. Re-selecting immediately before the read, as
423
+ `hardware/sid-reference.md` suggests, is not enough on its own: the
424
+ conversion still needs its 512 cycles after that write.
425
+
426
+ The DDR matters for the same reason it matters for the keyboard:
427
+ `cia1_ddr_cleared_kills_keyboard` in `pitfalls/input.md`. With `$DC02`
428
+ cleared, the select bits are inputs, the write reaches no pin, and
429
+ both select bits read high: both ports selected, a case the SID page
430
+ does not define and this page does not measure (the recipe's `$C0` row
431
+ shows only what VICE does with it).
432
+
433
+ ### Variations
434
+
435
+ **One port, no switching.** A single-port paddle game writes the select
436
+ once and never touches `$DC00` again, and then only the KERNAL scan can
437
+ disturb it. With the KERNAL IRQ off it can read `$D419` whenever it
438
+ likes.
439
+
440
+ **Interleaved frames.** Select port 1 on even frames and read it on odd
441
+ frames, port 2 the other way. Every paddle updates at half the frame
442
+ rate, which is enough for a bat, and no frame carries a wait.
443
+
444
+ **Position sweeps.** Not measured here: the host mouse that drives
445
+ VICE's paddles cannot be moved headlessly, so the value's range and
446
+ linearity against the knob are from `hardware/sid-reference.md`, which
447
+ gives the usable span as about `$00` to `$DF`.
448
+
449
+ ### Cycle budget
450
+
451
+ The reads and writes are 4 cycles each. The whole cost is the wait
452
+ between a select and its first trustworthy read: 544 cycles at the
453
+ worst phase in the runs here, 576 with 32 of margin, which is just over
454
+ nine PAL raster lines. The Cost line states 1,152, two waits of 576, as
455
+ the worst frame of a two-port read done inside one frame.
456
+
457
+ ### Recipes
458
+
459
+ - `recipes/kickassembler/paddle-read.md`
460
+
461
+ ---
462
+
463
+ ## mouse_1351_read — Read a 1351 mouse's position counter from POTX/POTY once per frame
464
+
465
+ **Complexity:** medium
466
+ **Region:** both
467
+ **Uses registers:** D419, D41A, DC00, DC01, DC02
468
+ **Cost:** cycles_per_frame=104, zp_bytes=0, irq_slots=0
469
+ **Cost basis:** measured-vice
470
+ **Cost measured on:** kickassembler-mouse-1351-read (one call, worst case)
471
+
472
+ ### Why
473
+
474
+ The Commodore 1351 in its proportional mode is not a joystick and not a
475
+ paddle, though it uses both sets of lines. Its movement arrives through
476
+ the SID's pot registers, `$D419` and `$D41A`, as a position counter the
477
+ mouse keeps itself; its two buttons arrive on the control port's switch
478
+ lines. A program that reads it as a paddle sees a value that wanders
479
+ and wraps; a program that reads it as a joystick sees nothing at all
480
+ unless the mouse was powered up in its joystick mode. The 1350, and the
481
+ 1351 with the right button held at power-up, report movement as
482
+ joystick direction pulses on the switch lines instead; nothing below
483
+ applies to that mode.
484
+
485
+ ### How
486
+
487
+ Select the mouse's port through CIA1 port A, bit 6 of `$DC00` for
488
+ control port 1 or bit 7 for port 2, with `$DC02` at `$FF` so the write
489
+ reaches the pins; that is the same select as `paddle_read`, and the
490
+ same 512-cycle conversion follows it. Then, once per frame:
491
+
492
+ ```text
493
+ x = ($D419 >> 1) & $3F ; bits 1 to 6 are the counter, bit 0 is noise
494
+ dx = (x - prev_x) & $3F ; the difference modulo 64
495
+ if dx >= 32: dx = dx - 64 ; 0..31 forward, 32..63 back
496
+ prev_x = x
497
+ ```
498
+
499
+ and the same for `$D41A`. The button byte is the port's joystick byte,
500
+ `$DC01` for port 1 or `$DC00` for port 2: the left button is on the
501
+ FIRE line, bit 4, and the right button on the UP line, bit 0, both low
502
+ when pressed.
503
+
504
+ The register format is from the 1351's documentation and is not
505
+ measured against a real mouse here. What is measured, in VICE x64sc
506
+ 3.10 with its 1351 on port 1 (`recipes/kickassembler/mouse-1351-read.md`):
507
+ over 250 frames at rest the masked counter read 32 on every frame on
508
+ both axes and both models, while the raw byte alternated between `$40`
509
+ and `$41`, bit 0 set in 128, 112 and 112 of the 250 frames across three
510
+ runs. Bit 0 is noise from one frame to the next, not a constant offset,
511
+ and any code that compares raw pot bytes will see movement that is not
512
+ there. Shift it off before anything else.
513
+
514
+ ### Why it works
515
+
516
+ The mouse counts its own quadrature transitions in a 6-bit counter per
517
+ axis and encodes the counter as a voltage on the pot line, timed against
518
+ the SID's conversion cycle, so that the byte the SID finishes every 512
519
+ cycles carries the counter in bits 1 to 6. The period is the SID's, not
520
+ the mouse's, and it is the figure `paddle_read` measured: 512 cycles a
521
+ conversion, about 520 microseconds on PAL and 500 on NTSC (arithmetic
522
+ from the clock rates). A frame is 38 conversions on PAL and 33 on
523
+ NTSC (arithmetic), so the register has been refreshed many times over
524
+ before the next read, and one read per frame loses nothing so long as
525
+ the counter has moved fewer than 32 steps in between. Whether a hand
526
+ can move a 1351 faster than that in one frame, a fiftieth of a second
527
+ on PAL and a sixtieth on NTSC, is not measured here; the delta arithmetic is exact below that speed and
528
+ wrong above it, with no way to tell from the data.
529
+
530
+ The modulo-64 subtraction is what makes the wrap invisible. 63 to 0 is
531
+ `(0 - 63) & $3F` = 1, a step forward; 0 to 63 is `(63 - 0) & $3F` = 63,
532
+ read as -1. The two half-turn cases, a difference of exactly 32, are
533
+ ambiguous by construction and the recipe's convention reads them as
534
+ -32. The recipe runs ten such pairs, including both wraps and both
535
+ half-turns, against expected values and prints `OK` for each.
536
+
537
+ The select bits are the ones `paddle_read` describes, with the same
538
+ consequence: the KERNAL's SCNKEY drives `$DC00` from the jiffy IRQ and
539
+ changes bit 6 as it walks the columns, so a conversion started during
540
+ the scan converts the wrong source. `paddle_read` measured about one
541
+ bad read per jiffy interrupt with the KERNAL IRQ live and none under
542
+ `SEI`; the mouse read is the same read on the same register and the
543
+ same three remedies apply, `SEI` around the read, owning the IRQ, or a
544
+ read on a raster line at least 576 cycles after the scan's last column
545
+ write. The recipe reads at raster line 250 with interrupts off. Because
546
+ both ports' pots go through the same two SID registers, a mouse and a
547
+ pair of paddles on the other port share the select and cannot be read
548
+ in the same conversion; `paddle_read`'s interleaved-frame schedule
549
+ handles the pair.
550
+
551
+ Both button lines are joystick lines, so `joystick2_scan_phantom_press`
552
+ and `cia1_ddr_cleared_kills_keyboard` in `pitfalls/input.md` apply to a
553
+ mouse in port 2 exactly as to a stick: read `$DC00` from the main loop
554
+ or from a handler that cannot interrupt the scan, and never clear
555
+ `$DC02` to do it. A mouse in port 1 has the keyboard problem the other
556
+ way round, and the select makes it worse: `$40` in `$DC00` sets bit 6
557
+ and clears the other seven, so keyboard columns 0 to 5 and 7 are driven
558
+ low for as long as port 1 is selected, and any held key in those
559
+ columns pulls its row line low on `$DC01`, where the mouse's right
560
+ button is row 0 (UP, PB0) and its left button row 4 (FIRE, PB4). That
561
+ is the wiring `keyboard_matrix_scan` describes for port 1 sticks, not
562
+ measured here with a mouse. A game that reads the keyboard as well as a
563
+ port 1 mouse selects with `$7F`, the value SCNKEY itself leaves behind:
564
+ bit 6 still set, only column 7 driven, so only that column's keys can
565
+ alias the mouse lines (1, left-arrow, CTRL, 2 and SPACE, measured for
566
+ port 1 in `pitfalls/input.md`). The recipe uses `$40` with no keyboard
567
+ in play.
568
+
569
+ ### Variations
570
+
571
+ **Accumulated position.** Add each frame's `dx` and `dy` to a 16-bit
572
+ pointer position and clamp to the screen; the pointer sprite's
573
+ coordinate comes from that sum, never from the counter.
574
+
575
+ **Acceleration.** Scale `dx` by its own magnitude, doubling a delta of
576
+ 8 or more, so slow moves are precise and fast ones cross the screen.
577
+ The threshold is a matter of feel and is not measured here.
578
+
579
+ **Half-rate reads.** Reading every other frame halves the cost and
580
+ halves the speed at which the 32-step ambiguity is reached; a game that
581
+ reads at half rate should say so in its own budget.
582
+
583
+ ### Cycle budget
584
+
585
+ One call of the recipe's `read_mouse`, both axes with their deltas and
586
+ including `jsr` and `rts`, costs 102 to 104 cycles by the sign of each
587
+ axis's delta: a delta of 0 to +31 takes a `bcc` (3 cycles), a negative
588
+ delta falls through it and runs the `ora` sign-extend (2 plus 2), one
589
+ cycle more per axis. 102 is both non-negative, 103 one of each, 104
590
+ both negative. The worst case measured 104 cycles by CIA2 timer A
591
+ difference on raster line 250, on both models, with both previous
592
+ counters set to force the negative path (measured in VICE x64sc 3.10).
593
+ The Cost line states that figure. There is no wait in the per-frame
594
+ cost because the
595
+ select is written once and never changed; a program that switches the
596
+ select to read paddles on the other port pays `paddle_read`'s settle.
597
+
598
+ ### Recipes
599
+
600
+ - `recipes/kickassembler/mouse-1351-read.md`
601
+
602
+ ## attract_mode_input_replay — The title screen plays the game from a recorded input stream
603
+
604
+ **Complexity:** low
605
+ **Region:** both
606
+ **Uses registers:** DC00, D012
607
+ **Requires:** joystick_edge_detect, lfsr_random, frame_sync_loop
608
+ **Cost:** cycles_per_frame=68
609
+ **Cost basis:** measured-vice
610
+ **Cost measured on:** oscar64-attract-replay (longest step)
611
+
612
+ ### Why
613
+
614
+ A title screen that idles is a still frame. The arcade answer is the
615
+ attract mode: after a while the game plays itself, and anyone watching
616
+ sees what the game is. The cheap way to get one is not a second code
617
+ path that moves the player about; it is the game itself, with its input
618
+ coming from somewhere other than the joystick port. The player code
619
+ reads one input byte. During play the port fills it. During the demo a
620
+ recording fills it. Nothing else changes, so the demo does exactly what
621
+ the game does, and a change to the game's rules is a change to the demo
622
+ for free.
623
+
624
+ ### How
625
+
626
+ Route every input read through one byte. `joystick_edge_detect` already
627
+ keeps last frame's port byte; put this frame's byte beside it and have
628
+ the game read the pair, never `$DC00` directly. The main loop is the
629
+ only place that writes it: from the port on the title and in play, from
630
+ the recording during the demo.
631
+
632
+ Store the recording as run-length pairs: the port byte exactly as
633
+ `$DC00` gives it, active low, and the number of frames it was held. A
634
+ joystick changes a few times a second, so a 285-frame demo is eight
635
+ pairs. A count of zero ends the stream. Beside the stream keep the seed
636
+ the random generator had when the recording was made, and the state the
637
+ recording ends in, here the player's X and Y on its last frame, so the
638
+ program can check its own replay.
639
+
640
+ Count idle frames on the title: a byte that goes up once a frame while
641
+ the port reads nothing and back to zero on any bit. When it reaches the
642
+ attract delay, start the demo: reset the playfield the way a new game
643
+ would, set the LFSR to the recorded seed, and point the stream at its
644
+ first pair. Each demo frame, if the current pair has frames left, hold
645
+ its byte; otherwise load the next pair; when the next pair's count is
646
+ zero the demo is over and the title comes back.
647
+
648
+ Keep reading the port during the demo. Any bit low is a real press: end
649
+ the demo that frame, before the stream is consulted, and let the title
650
+ take over. The player then starts a game from the title as usual.
651
+
652
+ ### Why it works
653
+
654
+ The replay is exact because the game is deterministic from its seed.
655
+ Its state on any frame is a function of the state before it and the
656
+ input byte, and the only source of variation is the random generator.
657
+ Reseed the generator, feed the same bytes in the same frames, and every
658
+ intermediate state is the one the recording saw, so the last frame lands
659
+ where the table says. The recipe shows the other side: built with the
660
+ reseed left out, the same 285 bytes end 210 pixels from the recorded
661
+ position, because the title screen had moved the generator on. The
662
+ frame is the unit that makes "the same frames" true on both regions: a
663
+ `frame_sync_loop` runs one step per frame, so a count of 50 is 50 steps
664
+ on PAL and on NTSC, in less wall time on NTSC and with the same result.
665
+
666
+ The seed must not be zero. An LFSR at zero stays at zero, so the reseed
667
+ maps zero to one; a recording tool should never store zero, and the
668
+ guard costs one compare.
669
+
670
+ ### Variations
671
+
672
+ Öörni's control override, named on `game-design/game-structure.md`, is
673
+ the same seam used the other way: enemies and cutscene actors read a
674
+ virtual joystick byte that the AI or the script writes, and a
675
+ conversation freezes the player by writing zero. A recorded human run is
676
+ the natural source for the stream: log the byte and the frame count
677
+ through the same variable during a real game and dump the pairs. The
678
+ verdict constants are then whatever the recording ended on, which is how
679
+ a shipped game can check its own attract mode after a rules change. A
680
+ longer attract can chain several recordings, or a recorded run and a
681
+ scripted one, through the same byte.
682
+
683
+ ### Cycle budget
684
+
685
+ The replay step, the code that decides whether to hold the current pair
686
+ or load the next one and writes the input byte, was timed with CIA1
687
+ timer B, interrupts held off for the timed window, over a 285-frame
688
+ demo. The longest step was 68 cycles on PAL and NTSC and in all three
689
+ builds, and that figure includes the timer's own start and stop stores.
690
+ The Cost line states it; the game step it feeds is the game's own cost,
691
+ not the technique's. The idle counter on the title is one increment and
692
+ one compare a frame. Measured in VICE x64sc 3.10 on the recipe's
693
+ listing.
694
+
695
+ ### Recipes
696
+
697
+ - `recipes/oscar64/attract-replay.md`
698
+
699
+ ---
700
+
701
+ ## four_player_read — Read joysticks 3 and 4 through a user-port 4-player adapter
702
+
703
+ **Complexity:** low
704
+ **Region:** both
705
+ **Uses registers:** DC00, DC01, DD01, DD03
706
+ **Cost:** cycles_per_frame=62, irq_slots=0
707
+ **Cost basis:** measured-vice
708
+ **Cost measured on:** kickassembler-four-player-read (read inlined, once a frame)
709
+
710
+ ### Why
711
+
712
+ The C64 has two control ports. Party and sports games for four players
713
+ add two more joysticks through an adapter on the user port, read through
714
+ CIA2 port B (`$DD01`). The common design, sold by Protovision and
715
+ emulated by VICE as the "CGA userport joy adapter", has seven port-B
716
+ lines (six inputs and a select) for ten switches, so it multiplexes the directions and gives each
717
+ fire button its own line. A game has to know which bit is which, and
718
+ that the adapter types are not interchangeable.
719
+
720
+ ### How
721
+
722
+ Set PB7 as an output once. It is the adapter's select line; PB0 to PB6
723
+ stay inputs:
724
+
725
+ ```text
726
+ $DD03 = $80 ; DDR B: PB7 out, PB0-PB6 in
727
+ ```
728
+
729
+ Then, each frame:
730
+
731
+ ```text
732
+ j1 = $DC01 & $1F ; port 1
733
+ j2 = $DC00 & $1F ; port 2 ($DC00 must drive no keyboard column)
734
+ $DD01 = $80 ; PB7 = 1: joystick 3's directions on PB0-PB3
735
+ j3 = $DD01 & $1F ; PB0-PB3 directions, PB4 = fire 3
736
+ $DD01 = $00 ; PB7 = 0: joystick 4's directions on PB0-PB3
737
+ v = $DD01
738
+ j4 = (v & $0F) | ((v & $20) >> 1) ; PB5 = fire 4, moved to bit 4
739
+ ```
740
+
741
+ All four bytes then have the `$DC00` layout: bit 0 up, 1 down, 2 left,
742
+ 3 right, 4 fire, 0 when pressed. `joystick_edge_detect` and the rest of
743
+ a game's input code work on them unchanged.
744
+
745
+ Fire 3 on PB4 and fire 4 on PB5 are present under either select. Only
746
+ the directions go through the select. Joystick 4's fire therefore is not
747
+ bit 4 of its read; forgetting the shift reads joystick 4 as never firing.
748
+
749
+ ### Why it works
750
+
751
+ Writing `$DD01` with `$DD03` at `$80` drives only PB7; the input bits
752
+ ignore the write. PB7 drives a 74LS257 multiplexer (VICE's pin table)
753
+ that routes one joystick's four direction lines to PB0-PB3. Protovision
754
+ and the icomp wiki both give PB7 = 1 for joystick 3 and PB7 = 0 for
755
+ joystick 4, with fire 4 on PB5. VICE 3.10's source agrees
756
+ (`src/userport/userport_joystick.c`): PB7 high selects joystick 3's
757
+ directions, and the read carries joystick 3's fire on PB4 and joystick
758
+ 4's on PB5 whatever the select.
759
+
760
+ What was run: the recipe, in VICE x64sc 3.10 with the CGA adapter and
761
+ autofire on joysticks 3 and 4 at 5 and 7 presses a second, counted 11
762
+ and 15 presses in 100 PAL frames, 9 and 12 in 100 NTSC frames, and
763
+ found PB4 and PB5 equal under both selects on every frame. Directions on
764
+ joysticks 3 and 4 were not injected: VICE has no headless way to move
765
+ them. The select's effect on PB0-PB3 is therefore from the two sources
766
+ and the VICE source, not measured here.
767
+
768
+ VICE switches the multiplexer at the store, so a load 4 cycles later
769
+ sees the new joystick. No settle time on hardware is measured here.
770
+
771
+ ### Variations
772
+
773
+ **Kingsoft adapter.** A different wiring with no select line. In VICE's
774
+ source (`src/userport/userport_hks_joystick.c`, not exercised here
775
+ beyond one control run) joystick 4's directions are on PB0-PB3 in the
776
+ order right, left, down, up; joystick 3's fire is PB4, its right, left
777
+ and down are PB5-PB7 and its up is PA2, bit 2 of `$DD00`; joystick 4's
778
+ fire arrives on CIA2's serial data pin SP2, clocked from CIA1's CNT1. The
779
+ control run in the recipe read a Kingsoft adapter with the CGA code:
780
+ joystick 3's fire counted, joystick 4's never did. VICE also emulates
781
+ HIT and StarByte adapters with other wirings. A game that supports more
782
+ than one adapter needs a menu choice or a per-type read.
783
+
784
+ **Detecting the adapter.** With no adapter, PB0-PB6 read high under
785
+ both selects (measured in VICE, the recipe's control run with no
786
+ user-port device), which is also what a connected adapter with every
787
+ joystick idle returns.
788
+ There is no reliable detect; ask the player.
789
+
790
+ **Two reads a frame for responsiveness.** The read is 62 cycles, so a
791
+ game that polls twice a frame, for example once in the IRQ and once in
792
+ the main loop, spends 124.
793
+
794
+ ### Conflicts on the user port
795
+
796
+ The adapter uses PB0-PB5 and PB7, and its DDR setting owns port B, so it cannot share the port with anything else
797
+ that uses port B. `hardware/cia-reference.md` lists the KERNAL RS-232
798
+ driver (RXD sampled on `$DD01` bit 0 from its NMI) and user-port parallel
799
+ cables for 1541 fastloaders. Either one active at the same time would
800
+ corrupt the other's reads or writes (not measured here), and the adapter's `$DD03` setting is lost
801
+ if the other code rewrites the DDR. The IEC-bus loaders this repository
802
+ covers, Krill and Sparkle, are described in `pitfalls/loader.md` as
803
+ driving `$DD00` only; no `$DD01` use is stated for them, and none was
804
+ measured. A parallel-cable build of a loader is a different case and is
805
+ not covered here.
806
+
807
+ Ports 1 and 2 share `$DC00` and `$DC01` with the keyboard. The KERNAL
808
+ scan rewrites `$DC00`; `pitfalls/input.md` has what that does to a
809
+ port 2 read.
810
+
811
+ ### Cycle budget
812
+
813
+ The read is 22 instructions and no branch: 62 cycles every frame,
814
+ measured by CIA1 timer A in VICE on PAL and NTSC, and the same from the
815
+ instruction table. As a subroutine it is 74 with the `jsr` and `rts`
816
+ (arithmetic). The Cost line states 62, the read inlined. The one-off
817
+ `$DD03` write is not part of the frame.
818
+
819
+ ### Sources
820
+
821
+ - Protovision, 4-player adapter build page:
822
+ https://www.protovision.games/hardw/build4player.php?language=en
823
+ (PB7 select, `$DD03` = `$80`, fire 4 on bit 5)
824
+ - icomp wiki, "4 Player Adapter": https://wiki.icomp.de/wiki/4_Player_Adapter
825
+ (DDR `%10000000`, `$80` selects joystick 3, `$00` joystick 4, fire 4 on PB5)
826
+ - VICE 3.10 source, `src/userport/userport_joystick.c` (CGA adapter) and
827
+ `src/userport/userport_hks_joystick.c` (HIT, Kingsoft, StarByte)
828
+
829
+ ### Recipes
830
+
831
+ - `recipes/kickassembler/four-player-read.md`
832
+
833
+ ---
834
+
835
+ ## irq_keyboard_own_scan — Scan the keyboard from your own IRQ, with per-key age counters and the KERNAL's exit points
836
+
837
+ **Complexity:** medium
838
+ **Region:** both
839
+ **Uses registers:** DC00, DC01, DC02, DC03
840
+ **Requires:** keyboard_matrix_scan
841
+ **Cost:** cycles_per_frame=945, irq_slots=1, zp_bytes=1
842
+ **Cost basis:** measured-vice
843
+
844
+ ### Why
845
+
846
+ A game that takes the IRQ vector and masks CIA1 has switched off
847
+ SCNKEY, so `GETIN`, the buffer at `$0277` and the STOP key all go
848
+ quiet; the fix a platformer built for a blind test needed on 2026-09-22
849
+ (`pitfalls/input.md`, `cia1_ddr_cleared_kills_keyboard`) is the general
850
+ one. Scan the matrix yourself, once a frame, from the same IRQ that
851
+ drives the game. Done there rather than in the main loop, the scan
852
+ cannot be torn by the KERNAL's column writes, runs at a fixed point in
853
+ the frame, and gives every key a frame-accurate history: pressed this
854
+ frame, held N frames, released this frame.
855
+
856
+ ### How
857
+
858
+ 1. Mask CIA1 (`$DC0D` = `$7F`, then read it) and take `$0314`, or
859
+ `$FFFE` with the KERNAL out. The direction registers stay as IOINIT
860
+ left them, `$DC02` = `$FF` and `$DC03` = `$00`; write them anyway,
861
+ because a loader or a previous program may not have.
862
+ 2. In the IRQ, walk a single zero bit across `$DC00` from `$FE` to
863
+ `$7F` and read `$DC01` after each store into an eight-byte image,
864
+ one byte a column, bit r clear for a closed switch on row r. The
865
+ layout is in `hardware/cia-reference.md`.
866
+ 3. Write `$FF` back to `$DC00`. A joystick 2 poll later in the frame,
867
+ or in the same handler, then reads only the port's own switches. The
868
+ KERNAL leaves `$7F` and a game that copies that habit sees column 7's
869
+ keys on port 2.
870
+ 4. Edges: `prev EOR cur` is the set of changed bits; `AND prev` keeps
871
+ the presses, `AND cur` the releases. Then `prev` = `cur`.
872
+ 5. Ages: for each key the game uses, a byte that is 0 while the key is
873
+ up and counts up from 1 while it is down, saturating at 255. Age 1 is
874
+ the press event; age N is "held N frames"; a repeat rule on the age
875
+ (first at REPEAT_AT, then every second frame) is `joystick_autorepeat`
876
+ applied to a key.
877
+ 6. Leave through the KERNAL's exit that matches what you kept. Read from
878
+ the ROM image: `$EA81` is `PLA TAY PLA TAX PLA RTI`, the bare exit;
879
+ `$EA7E` is `LDA $DC0D` then the same, a bare exit that also
880
+ acknowledges CIA1; `$EA7B` is `JSR $EA87`, the SCNKEY call, then the
881
+ two above; `$EA31` is the whole service routine, jiffy clock and
882
+ cursor blink and tape motor and scan. A handler that keeps the KERNAL
883
+ jiffy IRQ alive and adds its own scan exits through `$EA7E`, or does
884
+ the acknowledge itself and uses `$EA81`; jumping to `$EA7B` runs the
885
+ KERNAL scan too and defeats the point.
886
+
887
+ ### Why it works
888
+
889
+ There are no diodes in the matrix, so one column pulled low lets only
890
+ that column's closed switches pull rows down; eight passes see all 64
891
+ switches (`keyboard_matrix_scan`). SCNKEY can no longer interleave its
892
+ own column writes with yours because it is not running: it is only
893
+ ever called from the handler at `$EA31`, and masking CIA1 stops that
894
+ handler. Everything the KERNAL did with the result, the decode to
895
+ PETSCII, the modifier tables, the ten-byte buffer, the repeat delay
896
+ in `$028B/$028C`, is now the game's to do or to skip; a game that
897
+ steers with keys skips all of it and reads the image directly.
898
+
899
+ The age counter carries the whole per-key state in one byte. It is
900
+ also the debounce, for the reason `keyboard_matrix_scan` gives: at one
901
+ sample per frame, a bounce costs at worst one extra release-and-press
902
+ pair, which shows as a second age 1.
903
+
904
+ ### Variations
905
+
906
+ **Ghosting with three keys.** Three closed switches at three corners of
907
+ a rectangle in the matrix make the fourth corner read closed. A
908
+ key-steered game picks its keys so no three of them form three corners
909
+ of a rectangle: SPACE, Z, C, B are all row 4 and can never ghost with
910
+ each other, but Z (column 1, row 4) with W (column 1, row 1) and S
911
+ (column 1, row 5) is fine while Z, W and R (column 2, row 1) ghosts C
912
+ (column 2, row 4) on. From the wiring, not measured: a headless run
913
+ cannot hold three keys.
914
+
915
+ **The shift and control lines.** Left shift is column 1 row 7, right
916
+ shift column 6 row 4, CTRL column 7 row 2, C= column 7 row 5. They are
917
+ ordinary switches to the scan; a game reads them as modifiers by
918
+ testing those bits alongside the key. SHIFT LOCK is left shift held
919
+ mechanically.
920
+
921
+ **A keyboard-driven menu.** Ages give a menu its repeat for free: move
922
+ the cursor when a direction key's age is 1, again when it is
923
+ REPEAT_AT, then every second frame. The press set gives one event per
924
+ key per press for the confirm key, whatever the frame rate.
925
+
926
+ **Keeping the KERNAL IRQ.** If the game needs the jiffy clock or the
927
+ KERNAL's STOP handling, leave `$DC0D` alone, scan in your own raster
928
+ IRQ and exit through `$EA81` after acknowledging `$D019`; the KERNAL's
929
+ own SCNKEY still runs on the CIA timer and still leaves `$7F` on
930
+ `$DC00`. A joystick 2 read then has to tolerate `$7F`, or write `$FF`
931
+ first, and the two scans' column writes can interleave if the raster
932
+ IRQ is allowed to pre-empt the timer one (`pitfalls/input.md`,
933
+ `joystick2_scan_phantom_press`, and `keyboard_matrix_scan`, "Why the
934
+ KERNAL IRQ must be out of the way").
935
+
936
+ ### Cycle budget
937
+
938
+ Measured in VICE x64sc 3.10 with CIA1 Timer A in the recipe's
939
+ handler, PAL and NTSC alike: the scan alone, eight columns with the
940
+ `$FF` restore, is 252 cycles; the scan plus the `$DC00` read-back, the
941
+ edge pass over eight columns and the ageing of four keys is 781 with
942
+ the matrix empty and 945 with all four watched keys held (41 cycles a
943
+ held key, the longer ageing branch). The Cost line carries the 945,
944
+ the worst frame. On PAL that is under 5 % of a 19,656-cycle frame,
945
+ about fifteen raster lines; it runs from line 250 in the recipe, below
946
+ the display, where no badline can stretch it.
947
+
948
+ ### Recipes
949
+
950
+ - `recipes/kickassembler/own-keyscan.md`
951
+
952
+ ## light_pen_read — Read the light pen's latched beam position from LPX/LPY once per frame
953
+
954
+ **Complexity:** low
955
+ **Region:** both
956
+ **Uses registers:** D013, D014, D019, D01A, DC01
957
+ **Cost:** cycles_per_frame=94, zp_bytes=0, irq_slots=1
958
+ **Cost basis:** measured-vice
959
+
960
+ ### Why
961
+
962
+ A light pen points at the screen and the machine has to say which cell
963
+ or pixel it is over. The pen has no position of its own: it fires a
964
+ pulse when the beam passes under it, and the VIC turns that pulse into a
965
+ coordinate by copying its own counters into `$D013` and `$D014`. A
966
+ program that reads those two registers at the right time, once a frame,
967
+ knows where the pen is; a program that reads them at the wrong time or
968
+ skips the correction reads a point a few cells to the right of the pen,
969
+ or the same stale point every frame.
970
+
971
+ ### How
972
+
973
+ Enable the pen interrupt, bit 3 of `$D01A`, with the pen in control
974
+ port 1. When the pen's pulse pulls the LP pin low the VIC latches the
975
+ beam's X coordinate divided by two into `$D013` (LPX) and the low eight
976
+ bits of the raster line into `$D014` (LPY), and sets bit 3 of `$D019`.
977
+ The handler acknowledges by writing bit 3 back to `$D019`, copies the
978
+ two registers into program variables and counts the interrupt. The
979
+ main loop converts, once per frame:
980
+
981
+ ```text
982
+ x = (LPX - PEN_DELAY) * 2 ; VIC X coordinate of the beam
983
+ col = (x - 24) / 8 ; text area starts at X = 24
984
+ row = (LPY - 51) / 8 ; text area starts at line 51
985
+ ```
986
+
987
+ and treats anything outside 0 to 39 or 0 to 24 as "not on the text
988
+ area". Whether the pen is pressed to the screen, on pens that have a tip
989
+ switch, comes from the port's switch lines on `$DC01`: bit 4 is the
990
+ FIRE line, which is the pen's own trigger line. A program that only wants
991
+ to know whether the pen triggered this frame can poll bit 3 of `$D019`
992
+ instead of taking the interrupt, and must still write it back to clear
993
+ it.
994
+
995
+ `PEN_DELAY` is the pen's own latency in LPX units. A pen reports the
996
+ beam a little after the beam lit the phosphor under it, and the latch
997
+ takes the counter at the moment of the pulse, so LPX reads a constant
998
+ too large; the constant depends on the pen and the display, so the
999
+ program calibrates it once by asking for a touch on a known cell and
1000
+ keeping the difference. This is the standard treatment (rung 5, not
1001
+ measured here: no pen was available). Both figures and the reading
1002
+ pattern are measured in `recipes/kickassembler/light-pen-read.md`.
1003
+
1004
+ ### Why it works
1005
+
1006
+ The latch fires once per frame: the first LP edge after the vertical
1007
+ blank copies the counters, later edges in the same frame are ignored,
1008
+ and the latch is reset at the start of the next frame
1009
+ (`hardware/vic-ii-reference.md`, "Light pen latch"). One read per frame
1010
+ therefore sees every position the pen reported, and reading twice in a
1011
+ frame sees the same value twice. The X counter is halved to fit eight
1012
+ bits, which is why LPX is in two-pixel units and a pen cannot resolve a
1013
+ single pixel horizontally; LPY is a raster line, so vertical resolution
1014
+ is one line and the text row needs only the divide by eight.
1015
+
1016
+ The LP pin is CIA1 PB4, control port 1's FIRE line. That is why the pen
1017
+ lives in port 1 and why a joystick fire press in port 1 latches a
1018
+ position too: with no pen attached `$D013` and `$D014` hold whatever the
1019
+ last fire press latched. It is also why the port-1 keyboard hazard
1020
+ applies. The KERNAL's SCNKEY drives the keyboard columns from `$DC00`
1021
+ in the jiffy IRQ, and a held key in a driven column pulls its row line
1022
+ low on `$DC01`; the pen's line is row 4 (`joystick2_scan_phantom_press`
1023
+ in `pitfalls/input.md`, Fix C, covers the port-1 form). A held SPACE or
1024
+ another row-4 key can therefore read as the pen's switch, and the scan
1025
+ itself cannot pull the LP pin, which is an input to the VIC, so it
1026
+ cannot fake a latch; the recipe turns the CIA1 interrupt off anyway.
1027
+
1028
+ Measured in VICE x64sc 3.10 with the recipe's pen (`-controlport1device
1029
+ 11`), PAL and NTSC: over 300 frames the pen interrupt fired zero times,
1030
+ `$D013` and `$D014` read `$00` on every frame, bit 3 of `$D019` was
1031
+ clear at the end and `$DC01` read `$FF`. VICE's pen follows the host
1032
+ mouse and a headless run has none to move, so the emulated pen never
1033
+ triggers; what a triggering pen latches, and the frame-to-frame spread
1034
+ of the latch, is not measured here. The recipe's synthetic table checks
1035
+ the conversion on both corners of the text area, one step outside each,
1036
+ and the all-zero resting value, which converts to "outside".
1037
+
1038
+ ### Variations
1039
+
1040
+ **A menu by pen.** Draw the choices as text cells, convert the latch to
1041
+ a row each frame, highlight that row, and take the tip switch on `$DC01`
1042
+ bit 4, or a second's dwell counted in frames, as the selection. The
1043
+ conversion is the recipe's `lpy_to_row`; the pen's vertical reading is
1044
+ the steadier axis, so a menu of rows needs no calibration in X.
1045
+
1046
+ **Polled instead of interrupt-driven.** Read bit 3 of `$D019` once a
1047
+ frame from the main loop, and read the two registers when it is set;
1048
+ write the bit back to clear it. The cost falls to a handful of cycles
1049
+ and no interrupt slot, at the price of reading the latch up to a frame
1050
+ late, which a menu does not notice.
1051
+
1052
+ **Averaging.** A real pen's LPX can differ by a unit or two from one
1053
+ frame to the next (rung 5, not measured here). Averaging the last four
1054
+ frames' values before the conversion steadies a cursor.
1055
+
1056
+ ### Cycle budget
1057
+
1058
+ The interrupt, from the 6510 taking it to the `rti`, costs 94 cycles on
1059
+ both models: 7 for the interrupt sequence, 29 for the KERNAL's
1060
+ dispatcher from `$FF48` to the `$0314` vector, 36 for the handler's nine
1061
+ instructions and 22 for the exit through `$EA81` (measured in VICE x64sc
1062
+ 3.10 by CIA2 timer A difference around a fixed loop with and without the
1063
+ interrupt, the same handler entered from a raster interrupt because the
1064
+ pen line cannot be pulled low by software; the four terms are
1065
+ arithmetic that agrees with it). The Cost line states the 94, one
1066
+ interrupt a frame. The conversion in the main loop is about 26 cycles for the
1067
+ column and 20 for the row by the instruction table (arithmetic), and is
1068
+ not on the line.
1069
+
1070
+ ### Recipes
1071
+
1072
+ - `recipes/kickassembler/light-pen-read.md`