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,1147 @@
1
+ ---
2
+ category: cia
3
+ ---
4
+
5
+ <!-- doc-type: pitfall-reference -->
6
+
7
+ # CIA Pitfalls
8
+
9
+ Pitfalls in the two 6526 CIAs that are not about reading the keyboard or
10
+ joysticks — those are in `pitfalls/input.md`. The entries here are about
11
+ the chip's own sequencing rules: registers whose *order* of access is
12
+ part of their contract, so that code which touches them in the wrong
13
+ order gets a wrong answer without any error to show for it.
14
+
15
+ ---
16
+
17
+ ## tod_read_order_latch — Read TOD hours first and tenths last; write hours first and tenths last
18
+
19
+ **Severity:** high
20
+ **Region:** both
21
+ **Triggered by registers:** DC08, DC0B, DC0F
22
+ **Triggered by techniques:** tod_alarm_interrupt
23
+
24
+ ### Symptom
25
+
26
+ Four different failures, one cause. Which one you get depends on which
27
+ order your code touched `$DC08`-`$DC0B` in.
28
+
29
+ 1. **A tenths digit that lags.** A clock display polled once a second
30
+ shows seconds, minutes and hours moving while the tenths digit is
31
+ always one poll stale, or sits on a value that makes no sense next to
32
+ the seconds. The code reads tenths first and hours last.
33
+ 2. **A frozen clock.** Every read of the four TOD registers returns the
34
+ same time, forever, although the game has been running for minutes.
35
+ Somewhere the code read `$DC0B` — often a lone peek at the hours or
36
+ the AM/PM bit — and never read `$DC08` afterwards.
37
+ 3. **A clock that never starts.** The program sets the time and the
38
+ registers read back exactly what was written, an hour later. The
39
+ code wrote tenths first and hours last.
40
+ 4. **A time that will not set, and an alarm interrupt later.** The
41
+ program follows "set `$DC0F` bit 7, then write hours..tenths"; the
42
+ clock keeps its old time and keeps running, and some time later ICR
43
+ bit 2 sets. The writes armed the alarm, not the clock.
44
+
45
+ Until 2026-09-21 two pages in this repository led straight into these:
46
+ `hardware/cia-reference.md` had the first three the wrong way round (a
47
+ read of tenths latched, a read of hours released, a write of hours
48
+ started the clock), and the CIA1 quick-lookup row in
49
+ `hardware/c64-registers-reference.md` prescribed the fourth (set the
50
+ clock with bit 7 of `$DC0F` *set*). Both are corrected; the measurement
51
+ that corrected them is below.
52
+
53
+ ### Mechanism
54
+
55
+ The 6526's time-of-day clock is four BCD registers — tenths (`$DC08`),
56
+ seconds (`$DC09`), minutes (`$DC0A`), hours with the AM/PM flag in bit 7
57
+ (`$DC0B`) — counting a 50 Hz or 60 Hz input on the chip's TOD pin. Two
58
+ sequencing rules are built into the silicon, and both key on the
59
+ *hours* register at one end and the *tenths* register at the other:
60
+
61
+ - **Reading.** A read of `$DC0B` latches all four registers: from that
62
+ moment every read of `$DC08`-`$DC0B` returns the values as they stood
63
+ at the hours read. The latch is released by a read of `$DC08`. The
64
+ counter keeps running underneath the latch the whole time; only what
65
+ you *see* is frozen. This exists so that a four-byte read can never
66
+ straddle a carry (59.9 to 00.0) — provided it starts at hours and
67
+ ends at tenths.
68
+ - **Writing.** A write to `$DC0B` stops the clock. It does not run
69
+ again until `$DC08` is written. This exists so that a four-byte
70
+ *write* cannot be overtaken by a tick halfway through — provided it
71
+ starts at hours and ends at tenths. Written the other way round, the
72
+ clock is stopped by the last write and stays stopped.
73
+ - **Clock or alarm.** Bit 7 of `$DC0F` (CRB, ALARM) chooses what a TOD
74
+ *write* lands in: 0 = the clock, 1 = the alarm. Reads always return
75
+ the clock, whatever bit 7 says (the datasheet's statement; the read
76
+ side of that was not measured here). The alarm registers sit at the
77
+ same four addresses, so "set the clock with bit 7 = 1" silently
78
+ programs the alarm and leaves the clock alone — neither set nor
79
+ stopped, in VICE (rows E, G and H below). One caveat on the "nor
80
+ stopped": the datasheet's "stopped whenever a write to the Hours
81
+ register occurs" is not conditioned on bit 7, so a 6526 may pause
82
+ the clock on an alarm-side hours write until the alarm-side tenths
83
+ write that follows it a few microseconds later. That is not
84
+ measurable here and makes no difference to a four-write sequence;
85
+ it would matter only to code that writes the alarm's hours alone.
86
+ - **Rate.** Bit 7 of `$DC0E` (CRA, TODIN) tells the chip whether the
87
+ TOD pin carries 50 Hz (1) or 60 Hz (0), i.e. whether to divide by 5
88
+ or by 6 to make a tenth. The KERNAL does not set this for you:
89
+ IOINIT writes `$08` to `$DC0E` (`LDA #$08` at `$FDAE`, `STA $DC0E`
90
+ at `$FDB0`, in KERNAL 901227-03) and the same `$08` to `$DD0E` at
91
+ `$FDB3`, which leaves bit 7 clear on both CIAs, and the
92
+ region-dependent timer setup at `$FF6E` reads `$DC0E` back, masks
93
+ with `#$80` and ORs in `$11` — it preserves whatever bit 7 was, it
94
+ never sets it. On a PAL machine the TOD therefore counts a 50 Hz
95
+ input with the 60 Hz divider, five-sixths of true speed, until a
96
+ program sets the bit. (ROM bytes read from the 901227-03 image,
97
+ rung 1 — an earlier version of this page placed the `LDA`/`STA` pair
98
+ at `$FDB0` alone; the five-sixths is arithmetic from the datasheet's
99
+ divider, and the VICE run in the measurement section below confirms
100
+ the ratio.)
101
+
102
+ Everything in the first three bullets is what the MOS 6526 datasheet
103
+ says in its "Time of Day Clock" section, and it is what VICE x64sc 3.10
104
+ does when measured (rung 1, below). It is not what two pages in this
105
+ repository said, which is why this entry exists: a reader who trusted
106
+ them wrote code with the read latch and the write halt on the wrong
107
+ registers.
108
+
109
+ One consequence worth stating because it follows from the rules rather
110
+ than from any measurement: the latch is chip state, not per-caller
111
+ state. If an interrupt handler reads `$DC08` while the main loop is
112
+ between its hours read and its tenths read, the main loop's latch is
113
+ gone and its remaining reads are live — a torn time with the correct
114
+ order on both sides. Keep all TOD reads in one context, or bracket the
115
+ four reads with `SEI`/`CLI` when an IRQ handler also reads the clock.
116
+ `SEI` does not hold off an NMI, so an NMI handler (RESTORE, or anything
117
+ raised through CIA2) must leave `$DC08`-`$DC0B` alone altogether.
118
+
119
+ ### Fix
120
+
121
+ - **Read** in the order `$DC0B`, `$DC0A`, `$DC09`, `$DC08` — hours
122
+ first, tenths last. If you only want one register and it is hours,
123
+ read `$DC08` afterwards anyway to drop the latch. Any other single
124
+ register can be read on its own.
125
+ - **Write** in the order `$DC0B`, `$DC0A`, `$DC09`, `$DC08` with
126
+ `$DC0F` bit 7 = 0. The clock is stopped from the first write to the
127
+ last and starts at exactly the time you set. To program the alarm,
128
+ do the same four writes with bit 7 = 1, then put bit 7 back to 0
129
+ before any code that expects to set the clock.
130
+ - **Set the rate** yourself: `$DC0E` bit 7 = 1 on a PAL machine (50 Hz
131
+ mains), 0 on NTSC (60 Hz). The KERNAL leaves it 0 on both.
132
+ - Values are BCD; the hours register carries AM/PM in bit 7.
133
+
134
+ ### Worked example
135
+
136
+ The first two routines are the orders an earlier version of the two
137
+ hardware pages implied; the last two are the correct ones. This fence
138
+ is assembled by `npm run check:listings`.
139
+
140
+ ```kick
141
+ // WRONG — tenths first "to latch", hours last "to release" (the order
142
+ // cia-reference.md gave before 2026-09-21). On the first pass nothing
143
+ // is latched while these four reads happen, so a carry between them
144
+ // tears the time. The hours read at the end DOES latch, so on every
145
+ // later pass the first read returns the tenths as they stood at the
146
+ // previous pass's hours read and then releases; only the last three
147
+ // reads of each pass are live.
148
+ read_tod_wrong:
149
+ lda $dc08
150
+ sta tod_tenths
151
+ lda $dc09
152
+ sta tod_secs
153
+ lda $dc0a
154
+ sta tod_mins
155
+ lda $dc0b // latches all four here; nothing releases it
156
+ sta tod_hours
157
+ rts
158
+
159
+ // WRONG — "$DC0F bit 7 = 1, then write hours..tenths" (the quick-lookup
160
+ // row before 2026-09-21). Bit 7 = 1 routes the writes to the ALARM; the
161
+ // clock is neither set nor stopped (rows E and G in the measurement).
162
+ set_tod_wrong:
163
+ lda $dc0f
164
+ ora #$80
165
+ sta $dc0f
166
+ lda new_hours
167
+ sta $dc0b
168
+ lda new_mins
169
+ sta $dc0a
170
+ lda new_secs
171
+ sta $dc09
172
+ lda new_tenths
173
+ sta $dc08 // the alarm is armed for new_*; the clock never changed
174
+ rts
175
+
176
+ // RIGHT — hours first (latches all four), tenths last (releases).
177
+ read_tod:
178
+ lda $dc0b
179
+ sta tod_hours
180
+ lda $dc0a
181
+ sta tod_mins
182
+ lda $dc09
183
+ sta tod_secs
184
+ lda $dc08 // releases the latch; the counter never stopped
185
+ sta tod_tenths
186
+ rts
187
+
188
+ // RIGHT — mains rate in CRA bit 7, clock (not alarm) in CRB bit 7,
189
+ // hours first (stops the clock), tenths last (starts it at the set time).
190
+ set_tod:
191
+ lda $dc0e
192
+ ora #$80 // TODIN = 1: 50 Hz mains (PAL). NTSC: and #$7f
193
+ sta $dc0e
194
+ lda $dc0f
195
+ and #$7f // ALARM = 0: the four writes below reach the clock
196
+ sta $dc0f
197
+ lda new_hours // BCD, bit 7 = PM
198
+ sta $dc0b // clock stops
199
+ lda new_mins
200
+ sta $dc0a
201
+ lda new_secs
202
+ sta $dc09
203
+ lda new_tenths
204
+ sta $dc08 // clock starts
205
+ rts
206
+
207
+ tod_hours: .byte 0
208
+ tod_mins: .byte 0
209
+ tod_secs: .byte 0
210
+ tod_tenths: .byte 0
211
+ new_hours: .byte $09 // 09:41:23.5 AM
212
+ new_mins: .byte $41
213
+ new_secs: .byte $23
214
+ new_tenths: .byte $05
215
+ ```
216
+
217
+ ### How this was measured
218
+
219
+ Instrument: VICE x64sc 3.10 (`*** VICE Version 3.10 ***` in its own
220
+ startup log; the Homebrew cellar path says the same), headless, `-warp`,
221
+ `-autostartprgmode 1`, `-limitcycles 20000000` (16,000,000 until three
222
+ rows were added on 2026-09-22; the probe now waits about 12 s of C64
223
+ time after boot), `-exitscreenshot`. PAL
224
+ (VICE's default C64C: 8565, 8580, CIA 8521) with CRA bit 7 = 1; NTSC (`-model ntsc`, 6567R8) with CRA bit
225
+ 7 = 0. Nothing here was run on a 6526 on a bench. An earlier version said
226
+ the PAL run was a 6569; `x64sc -default` is the C64C, whose CIAs are 8521s,
227
+ while the NTSC run's are 6526s. VICE derives the TOD
228
+ tick from emulated cycles, not from the host clock — the probe runs
229
+ under `-warp` and the clock still advances 1.5 s in 75 PAL frames, and
230
+ `src/core/ciacore.c` in the VICE tree schedules the tick as an alarm
231
+ `todticks` cycles apart — so the readings below are about the chip
232
+ model VICE implements, at emulated speed.
233
+
234
+ The probe (listing below, exactly as run) clears the screen, sets the
235
+ clock to 00:00:00.0 in the correct order, waits a known number of
236
+ frames by watching `$D012`, and stores a reverse-space (`$A0`) on a
237
+ fixed screen row at the column equal to the tenths value it read. One
238
+ test per row, so the screenshot *is* the result. Decoded with PIL,
239
+ which first finds the display area as the run of non-border pixels
240
+ (border red, background blue): the PAL screenshot is 384×272 with the
241
+ display at y 35-234, so screen row 0 is at PNG y = 35, and the NTSC one
242
+ 384×247 with the display at y 23-222, row 0 at y = 23; the display
243
+ spans x 32-351 in both, so screen column c is x = 32 + 8c (this geometry,
244
+ the palette values per model and a decode snippet are now collected in
245
+ `../runtime/vice-reference.md`, "Reading the exit screenshot"). The decoder
246
+ then demands exactly one lit cell per test row, all 64 of its pixels
247
+ lit, and no lit cell anywhere else on the screen; every run below
248
+ passed that. The runs (2026-09-22, after rows G, H and I were added;
249
+ the first seven rows re-measured and reading what the page's first
250
+ version had):
251
+
252
+ | Row | Test | PAL 1 | PAL 2 | PAL 3 | NTSC 1 | NTSC 2 | Reading |
253
+ |-----|------|------:|------:|------:|-------:|-------:|---------|
254
+ | 6 | **I** hours written (clock stopped at 0); tenths *read* at ~0.3 s; tenths read again ~1.5 s later | 0 | 0 | 0 | 0 | 0 | still 0: **reading tenths does not restart a stopped clock**, only the tenths write does |
255
+ | 8 | **H** running from 0; at ~0.3 s CRB bit 7 = 1, hours written alone, bit 7 = 0; tenths read ~1.5 s later | 8 | 8 | 8 | 8 | 8 | ~1.8 s elapsed: **an alarm-side hours write did not stop the clock** (a stop would read 2 or 3) |
256
+ | 10 | **G** as E, then a further ~0.3 s before the tenths read | 8 | 8 | 8 | 8 | 8 | ~1.8 s elapsed: **the clock kept running after the alarm-routed writes** (a stop would read 4 or 5) |
257
+ | 12 | **A** hours read at ~0.3 s, tenths read at ~1.5 s | 3 | 3 | 3 | 3 | 3 | tenths froze at the hours read: **hours read latches** |
258
+ | 14 | **B** tenths written, *then* hours; tenths read ~1.5 s later | 0 | 0 | 0 | 0 | 0 | never moved: **hours write stops the clock** |
259
+ | 16 | **C** live tenths at ~1.5 s (control) | 5 | 5 | 5 | 5 | 5 | clock runs at the right rate |
260
+ | 18 | **D** live tenths at ~0.3 s (control) | 2 | 2 | 3 | 2 | 3 | |
261
+ | 20 | **E** CRB bit 7 = 1, then 00:00:00.0 written hours..tenths at ~1.5 s; tenths read at once | 5 | 5 | 5 | 5 | 5 | not 0: **bit 7 = 1 writes the alarm, the clock was not set** |
262
+ | 22 | **F1** hours read at once after start; first tenths read at ~0.66 s | 0 | 0 | 0 | 0 | 0 | the latched value |
263
+ | 24 | **F2** second tenths read, back to back with F1 | 6 | 6 | 6 | 6 | 6 | live: **the tenths read released the latch** |
264
+
265
+ The seven tests (A, B, E, F1, G, H, I) gave the same digit on every
266
+ run. The two controls vary by one between runs, and the cause was
267
+ tested rather than guessed: VICE's autostart delay is randomised by
268
+ default, and two PAL runs with `+autostart-delay-random` came out
269
+ identical (C = 5, D = 2, every test digit as above; the page's first
270
+ version made the same check on its seven-row probe and read C = 5,
271
+ D = 3 twice). The program therefore starts at a different point
272
+ relative to the emulated tick from run to run, a 75-frame wait is
273
+ 1.48-1.50 s depending on where in a frame it began, and a live reading
274
+ taken near 1.5 s or 0.3 s lands either side of a digit boundary. The
275
+ tests do not sit near a boundary; the controls do. Which control
276
+ wobbles is itself run-dependent: the first version's five runs read
277
+ C = 4/5/4/5/5 with D = 3/2/3/2/2, the five above read C = 5 every time
278
+ with D = 2/2/3/2/3. A decoder that demands C = 5 or D = 2 exactly will
279
+ fail on some runs and prove nothing when it does. (This also accounts
280
+ for an earlier run of the same design, before this page, reading C = 5
281
+ where the next read 4.)
282
+
283
+ Row B also shows what the earlier text would have predicted wrongly: if
284
+ the hours write started the clock, B would read 4 or 5 like C. Row A
285
+ shows the other half: if the tenths read latched and the hours read
286
+ released, A would read 4 or 5 too. Row I settles a third reading that
287
+ is in circulation (C64-Wiki's `$DC0B` entry has a stopped clock wait
288
+ for a *read* of the tenths register): the tenths read at 0.3 s did not
289
+ restart it, the clock still stood at 0 a second and a half later, and
290
+ only the tenths write in `reset_tod` set it going again — the
291
+ datasheet's rule, in VICE.
292
+
293
+ One more PAL run with CRA bit 7 left at 0 — the state the KERNAL leaves
294
+ it in; the listing's PAL branch with its `ora #$80` changed to
295
+ `and #$7f` — reads C = 2, D = 2, E = 2, A = 2, F2 = 5, G = 5 and H = 5,
296
+ with B, I and F1 still 0: 1.5 s of waiting advanced the clock about
297
+ 1.25 s and 1.8 s about 1.5 s, the five-sixths ratio above (rung 1 for
298
+ the emulator's divider; the datasheet describes the same divide-by-6).
299
+
300
+ ```kick
301
+ // TOD semantics probe, extended from tod3 (same encoding: a reverse-space at
302
+ // screen column = value read, one test per row). Original code for c64-kb.
303
+ //
304
+ // row 6: I hours written (clock stopped); tenths READ at ~0.3 s; tenths
305
+ // read again ~1.5 s later restart-by-read test
306
+ // row 8: H clock running from 0; at ~0.3 s CRB bit 7 = 1, hours written
307
+ // (alarm side, nothing after it), bit 7 = 0; tenths read ~1.5 s
308
+ // later alarm-hours halt test
309
+ // row 10: G as E, then a further ~0.3 s wait before the tenths read
310
+ // still-running test
311
+ // row 12: A hours read at ~0.3 s, tenths read at ~1.5 s latch test
312
+ // row 14: B tenths then hours written, tenths read ~1.5 s later halt test
313
+ // row 16: C live tenths at ~1.5 s control
314
+ // row 18: D live tenths at ~0.3 s control
315
+ // row 20: E CRB bit 7 = 1, then 00:00:00.0 written hours..tenths while
316
+ // the clock reads ~1.5 s; tenths read at once alarm-select test
317
+ // row 22: F1 hours read right after start; first tenths read at ~0.66 s
318
+ // row 24: F2 second tenths read, back to back with F1 latch release test
319
+ //
320
+ // Border red, background blue, so the decoder can read the display area's
321
+ // top edge (text row 0) off the same PNG instead of assuming it; the screen
322
+ // is cleared first so a lit cell can only be a result.
323
+ //
324
+ // Build PAL: java -jar KickAss.jar tod6.asm -o tod6_pal.prg
325
+ // Build NTSC: java -jar KickAss.jar tod6.asm -define NTSC -o tod6_ntsc.prg
326
+ // (NTSC: CRA bit 7 = 0 so the TOD divides 60 Hz; frame counts rescaled
327
+ // to the 6567R8's 17,095-cycle frame so the seconds waited stay the same)
328
+
329
+ #if NTSC
330
+ .const F03 = 18 // ~0.30 s
331
+ .const F06 = 40 // ~0.67 s
332
+ .const F12 = 72 // ~1.20 s
333
+ .const F15 = 90 // ~1.50 s
334
+ #else
335
+ .const F03 = 15 // ~0.30 s
336
+ .const F06 = 33 // ~0.66 s
337
+ .const F12 = 60 // ~1.20 s
338
+ .const F15 = 75 // ~1.50 s
339
+ #endif
340
+
341
+ .label col = $02
342
+ .label lo = $03
343
+ .label hi = $04
344
+
345
+ * = $0801
346
+ .byte $0b,$08,$0a,$00,$9e,$32,$30,$36,$31,$00,$00,$00 // 10 SYS2061
347
+ * = $080d
348
+ sei
349
+ lda #$02
350
+ sta $d020 // border red
351
+ lda #$06
352
+ sta $d021 // background blue
353
+ ldx #$00
354
+ lda #$20
355
+ cls: sta $0400,x // clear the screen: the boot banner and the typed RUN
356
+ sta $0500,x // would otherwise sit on rows 1, 3, 5 and 6
357
+ sta $0600,x
358
+ sta $0700,x
359
+ inx
360
+ bne cls
361
+ #if NTSC
362
+ lda $dc0e
363
+ and #$7f // TODIN = 0: 60 Hz
364
+ sta $dc0e
365
+ #else
366
+ lda $dc0e
367
+ ora #$80 // TODIN = 1: 50 Hz
368
+ sta $dc0e
369
+ #endif
370
+ lda $dc0f
371
+ and #$7f // ALARM = 0: TOD writes go to the clock
372
+ sta $dc0f
373
+
374
+ // D: live tenths at ~0.3 s (control)
375
+ jsr reset_tod
376
+ ldx #F03
377
+ jsr waitx
378
+ lda $dc08
379
+ ldy #18
380
+ jsr plot
381
+
382
+ // A: latch test — hours read at ~0.3 s, tenths read at ~1.5 s
383
+ jsr reset_tod
384
+ ldx #F03
385
+ jsr waitx
386
+ lda $dc0b // hours read: datasheet says this latches all four
387
+ ldx #F12
388
+ jsr waitx // now ~1.5 s
389
+ lda $dc08
390
+ ldy #12
391
+ jsr plot
392
+ lda $dc08 // release, in case the latch is still held
393
+
394
+ // C: live tenths at ~1.5 s (control)
395
+ jsr reset_tod
396
+ ldx #F15
397
+ jsr waitx
398
+ lda $dc08
399
+ ldy #16
400
+ jsr plot
401
+
402
+ // B: halt test — clock running, then hours written LAST
403
+ jsr reset_tod // ends with the tenths write: running
404
+ lda #$00
405
+ sta $dc0b // hours written after tenths
406
+ ldx #F15
407
+ jsr waitx
408
+ lda $dc08
409
+ ldy #14
410
+ jsr plot
411
+
412
+ // E: alarm-select test — the registers page's "set the clock" row
413
+ jsr reset_tod // running from 00:00:00.0
414
+ ldx #F15
415
+ jsr waitx // clock reads ~1.5 s
416
+ lda $dc0f
417
+ ora #$80 // ALARM = 1
418
+ sta $dc0f
419
+ lda #$00
420
+ sta $dc0b // "set the clock to 00:00:00.0"
421
+ sta $dc0a
422
+ sta $dc09
423
+ sta $dc08
424
+ lda $dc0f
425
+ and #$7f // ALARM = 0 again
426
+ sta $dc0f
427
+ lda $dc08 // if the clock had been set this reads 0
428
+ ldy #20
429
+ jsr plot
430
+
431
+ // G: still-running test — E again, then wait before reading
432
+ jsr reset_tod // running from 00:00:00.0
433
+ ldx #F15
434
+ jsr waitx // clock reads ~1.5 s
435
+ lda $dc0f
436
+ ora #$80 // ALARM = 1
437
+ sta $dc0f
438
+ lda #$00
439
+ sta $dc0b
440
+ sta $dc0a
441
+ sta $dc09
442
+ sta $dc08
443
+ lda $dc0f
444
+ and #$7f // ALARM = 0 again
445
+ sta $dc0f
446
+ ldx #F03
447
+ jsr waitx // a further ~0.3 s
448
+ lda $dc08 // still running: ~1.8 s -> 8 or 7; stopped by the writes: 4 or 5
449
+ ldy #10
450
+ jsr plot
451
+
452
+ // H: alarm-hours halt test — one alarm-side hours write, nothing after it
453
+ jsr reset_tod // running from 00:00:00.0
454
+ ldx #F03
455
+ jsr waitx // ~0.3 s
456
+ lda $dc0f
457
+ ora #$80 // ALARM = 1
458
+ sta $dc0f
459
+ lda #$00
460
+ sta $dc0b // hours, alarm side; no tenths write follows
461
+ lda $dc0f
462
+ and #$7f // ALARM = 0 again
463
+ sta $dc0f
464
+ ldx #F15
465
+ jsr waitx // ~1.5 s more
466
+ lda $dc08 // still running: ~1.8 s -> 8 or 7; stopped at the write: 2 or 3
467
+ ldy #8
468
+ jsr plot
469
+
470
+ // I: restart-by-read test — does reading tenths restart a write-stopped clock?
471
+ jsr reset_tod // running from 00:00:00.0
472
+ lda #$00
473
+ sta $dc0b // hours written: clock stops at 0
474
+ ldx #F03
475
+ jsr waitx // ~0.3 s
476
+ lda $dc08 // tenths read while stopped (reads 0 either way)
477
+ ldx #F15
478
+ jsr waitx // ~1.5 s more
479
+ lda $dc08 // 0 if only a tenths WRITE restarts it; 4 or 5 if that read did
480
+ ldy #6
481
+ jsr plot
482
+
483
+ // F: latch then two back-to-back tenths reads
484
+ jsr reset_tod // running from 00:00:00.0
485
+ lda $dc0b // hours read at once: latched tenths = 0
486
+ ldx #F06
487
+ jsr waitx // ~0.66 s
488
+ lda $dc08 // first tenths read: the latched value
489
+ and #$0f
490
+ pha
491
+ lda $dc08 // second tenths read: live, latch released
492
+ ldy #24
493
+ jsr plot
494
+ pla
495
+ ldy #22
496
+ jsr plot
497
+ loop: jmp loop
498
+
499
+ // A = raw register value, Y = screen row (0-24). Reverse-space at column (A & 15).
500
+ plot: and #$0f
501
+ sta col
502
+ lda #<$0400
503
+ sta lo
504
+ lda #>$0400
505
+ sta hi
506
+ tya
507
+ beq pdone
508
+ tax
509
+ padd: lda lo
510
+ clc
511
+ adc #40
512
+ sta lo
513
+ bcc pnext
514
+ inc hi
515
+ pnext: dex
516
+ bne padd
517
+ pdone: ldy col
518
+ lda #$a0
519
+ sta (lo),y
520
+ rts
521
+
522
+ reset_tod:
523
+ lda #$00
524
+ sta $dc0b // hours first: stops the clock
525
+ sta $dc0a
526
+ sta $dc09
527
+ sta $dc08 // tenths last: starts it
528
+ rts
529
+
530
+ // wait X frames, synchronising to raster line $FF each time
531
+ waitx:
532
+ w1: lda #$ff
533
+ w2: cmp $d012
534
+ bne w2
535
+ w3: cmp $d012
536
+ beq w3
537
+ dex
538
+ bne w1
539
+ rts
540
+ ```
541
+
542
+ Run with
543
+ `x64sc -default -warp +sound -autostartprgmode 1 -limitcycles 20000000 -exitscreenshot out.png -autostart tod6_pal.prg`
544
+ (add `-model ntsc` and build with `-define NTSC` for the NTSC column),
545
+ then find the single lit eight-pixel cell on each of rows 6, 8, 10, 12,
546
+ 14, 16, 18, 20, 22 and 24 and read its column. The NTSC frame counts
547
+ are for the 6567R8 (65 × 263 = 17,095 cycles a frame, 59.83 Hz); on a
548
+ 6567R56A (64 × 262 = 16,768 cycles, 60.99 Hz) the same seconds would
549
+ need 18, 41, 73 and 91 frames — arithmetic from the settled line
550
+ counts, rung 3, not run.
551
+
552
+ ### Cross-references
553
+
554
+ - `hardware/cia-reference.md` — `$DC08`, `$DC0B` and `$DC0F`
555
+ (corrected 2026-09-21 to match this entry), `$DC0E` bit 7 (TODIN),
556
+ and the "TOD latch on read" bullet in its Pitfalls list.
557
+ - `hardware/c64-registers-reference.md` — the CIA1 quick-lookup rows
558
+ "Read the TOD clock" and "Set the TOD clock" (the second corrected
559
+ 2026-09-21), and the "TOD clock latching" bullet in its Pitfalls,
560
+ which had the read side right all along.
561
+ - Register `DC0E` — TODIN, bit 7, which the KERNAL leaves clear; the
562
+ same is true of CIA2's `DD0E` (its `hardware/cia-reference.md` entry
563
+ said otherwise until 2026-09-22).
564
+ - `pitfalls/input.md` — the other CIA1 pitfall family (shared pins).
565
+ - `recipes/kickassembler/tod-alarm.md` — sets the clock and the alarm
566
+ in this order, reads the four registers in the handler in this order,
567
+ sets TODIN from the detected model, and quotes the drift with the bit
568
+ wrong (this bullet said no recipe used the TOD clock until
569
+ 2026-09-23). Technique: `tod_alarm_interrupt`,
570
+ `techniques/cpu-cycle-tricks.md`.
571
+
572
+ ### Sources
573
+
574
+ - *6526 Complex Interface Adapter (CIA)* datasheet, Commodore
575
+ Semiconductor Group / MOS Technology — "Time of Day Clock (TOD)" and
576
+ "Control Registers" sections. Read in the re-typeset copy
577
+ `mos_6526_cia_recreated.pdf` at 6502.org
578
+ (https://6502.org/documents/datasheets/mos/mos_6526_cia_recreated.pdf);
579
+ that copy carries no date. Paraphrased, not quoted. The server
580
+ answers a bare `curl` with 404 and a browser user-agent string with
581
+ 200 (checked 2026-09-22; the same file either way), so a link checker
582
+ will call this URL dead when it is not.
583
+ - VICE 3.10, `x64sc` — the instrument for every measured digit above;
584
+ and `vice/src/core/ciacore.c` (VICE-Team `svn-mirror` on GitHub),
585
+ read to learn how the emulator schedules TOD ticks, and re-read on
586
+ 2026-09-22 for the alarm case: its store routine sets the stopped
587
+ flag on an hours write, and clears it on a tenths write, only inside
588
+ the branch taken when CRB bit 7 is clear, which is why rows G and H
589
+ come out as they do. That reading is about the instrument, not about
590
+ a chip.
591
+ - C64 KERNAL ROM 901227-03 (image shipped with VICE) — bytes at
592
+ `$FDA3-$FDBC` and `$FF6E-$FF7C` for what the KERNAL writes to `$DC0E`,
593
+ and `$F02E-$F030`, `$F047-$F049` and `$FDB3` for the three stores to
594
+ `$DD0E`.
595
+ - C64-Wiki, "CIA" (https://www.c64-wiki.com/wiki/CIA) — consulted for
596
+ its register-bit wording; it agrees with the datasheet on CRB bit 7
597
+ and on the hours-write stop, but its `$DC0B` text has the stopped
598
+ clock restart on a *read* of the tenths register where the datasheet
599
+ says a write. Row I above measured that difference in VICE and the
600
+ datasheet is what the emulator does; do not "correct" this page
601
+ toward the wiki on that point.
602
+
603
+ ---
604
+
605
+ ## kernal_nmi_handler_runs_stop_check — The KERNAL NMI handler warm-starts BASIC on RUN/STOP+RESTORE, and a handler that skips the $DD0D read locks every NMI out
606
+
607
+ **Severity:** high
608
+ **Region:** both
609
+ **Triggered by registers:** DD0D
610
+ **Triggered by techniques:** nmi_handler_and_restore_key
611
+ **Mitigated by techniques:** nmi_handler_and_restore_key
612
+
613
+ ### Symptom
614
+
615
+ Two failures with opposite causes.
616
+
617
+ 1. **The game is gone.** The player presses RUN/STOP+RESTORE, by
618
+ accident or on purpose, and the screen clears to `READY.` with the
619
+ program's code still in memory. The program never touched `$0318`.
620
+ Its interrupt vector, its `$01` setting and its VIC mode were all put
621
+ back to the KERNAL's defaults on the way.
622
+ 2. **The tick stopped.** A program with its own NMI handler, driven by a
623
+ CIA2 timer, plays or counts exactly once and then never again. The
624
+ timer is still running; `$DD0D` reads `$81` if anything reads it at
625
+ all. RESTORE does nothing either. The handler ends in `RTI` without
626
+ having read `$DD0D`.
627
+
628
+ ### Mechanism
629
+
630
+ This entry sits in the CIA pitfalls because both halves turn on one
631
+ register access, the read of `$DD0D`, and on what CIA2's interrupt
632
+ output does around it. The KERNAL's dispatch through the vector and the
633
+ per-board wiring of the key are in `pitfalls/kernal-and-io.md`,
634
+ `restore_nmi_not_maskable`; this entry is the short form for a program
635
+ that is installing a handler. The technique is on both lines above
636
+ because the pitfall arises inside a naive version of it (no vector
637
+ taken, or a handler with no `$DD0D` read) and the correct version cures
638
+ it.
639
+
640
+ **The STOP check.** The KERNAL's handler at `$FE47` (the default
641
+ `$0318` target) reads `$DD0D` and branches on bit 7. Set means a CIA2
642
+ source: it runs the RS-232 code. Clear means the NMI came from
643
+ somewhere else, and the only somewhere else is the RESTORE key. It then
644
+ samples the keyboard row that holds RUN/STOP through `$F6BC` and tests
645
+ it through `$FFE1`; if the key is down it falls into `$FE66`: RESTOR,
646
+ IOINIT, CINT and `JMP ($A002)`, the BASIC warm start. There is no flag a
647
+ program can set to opt out; the decision is made from the CIA2 flag
648
+ being absent, which is exactly the state a RESTORE press produces. This
649
+ is from the ROM bytes of `kernal-901227-03.bin`, read for
650
+ `restore_nmi_not_maskable`; the warm-start branch itself needs RUN/STOP
651
+ held and was not run headless.
652
+
653
+ **The lock.** CIA2 drives the 6510's `/NMI` pin, and holds it low while
654
+ any enabled flag in its interrupt control register is set. A read of
655
+ `$DD0D` clears every flag and lets the pin rise. The 6510 takes an NMI
656
+ on the falling edge of the pin and not on its level, so a handler that
657
+ returns without the read leaves `/NMI` low, and no later event on the
658
+ pin, timer or key, can make an edge. Measured in VICE x64sc 3.10 on PAL
659
+ and NTSC: CIA2 Timer A ticking every 10,000 cycles for 1,005,000 cycles
660
+ entered a handler that reads `$DD0D` 100 times and a handler that does
661
+ not exactly once; a single `LDA $DD0D` from the main loop then bought
662
+ exactly one more entry before the second handler locked the line again
663
+ (`recipes/kickassembler/nmi-timer-tick.md`).
664
+
665
+ ### Fix
666
+
667
+ For the first symptom, install a handler at `$0318` before the game
668
+ starts. One `RTI` is a complete handler; `$FE43` runs only `SEI` before
669
+ the vector and pushes nothing. Reinstall it after anything that calls
670
+ RESTOR (`$FF8A`) or VECTOR (`$FF8D`) with an old table, since both put
671
+ `$FE47` back.
672
+
673
+ For the second, read `$DD0D` in every handler that services a CIA2
674
+ source. `BIT $DD0D` costs 4 cycles and touches no register. Do the read
675
+ only when the handler is meant to consume the event: a RESTORE-only
676
+ stub that also reads `$DD0D` discards a timer or RS-232 flag that
677
+ arrived in the same instant.
678
+
679
+ A program that wants both, a live CIA2 tick and a dead RESTORE key,
680
+ takes the vector, acknowledges in the handler, and either ignores the
681
+ extra entry a press produces or tests bit 7 of `$DD0D` before acting.
682
+
683
+ ### Worked example
684
+
685
+ The handler that locks. It counts one tick and then nothing, and the
686
+ key is dead with it:
687
+
688
+ ```asm
689
+ // LOCKS AFTER ONE TICK: no read of $DD0D, so /NMI never rises again.
690
+ nmi_bad:
691
+ inc tick_count
692
+ rti
693
+
694
+ tick_count:
695
+ .byte $00
696
+ ```
697
+
698
+ The same handler with the acknowledge. Every tick is taken, and a
699
+ RESTORE press is one extra entry the counter also sees:
700
+
701
+ ```asm
702
+ // Correct: the read clears the CIA2 flag and re-arms the edge.
703
+ nmi_good:
704
+ inc tick_count
705
+ bit $dd0d
706
+ rti
707
+
708
+ tick_count:
709
+ .byte $00
710
+ ```
711
+
712
+ For a program with no CIA2 use, the stub that disarms the key; it costs
713
+ 20 cycles per press and nothing between presses (measured in VICE x64sc
714
+ 3.10, `recipes/kickassembler/nmi-timer-tick.md`):
715
+
716
+ ```asm
717
+ install_stub:
718
+ sei
719
+ lda #<nmi_stub
720
+ sta $0318
721
+ lda #>nmi_stub
722
+ sta $0319
723
+ cli
724
+ rts
725
+
726
+ nmi_stub:
727
+ rti
728
+ ```
729
+
730
+ ### Cross-references
731
+
732
+ - Technique `nmi_handler_and_restore_key` (`techniques/cpu-cycle-tricks.md`),
733
+ the entry this pitfall guards and the one that fixes it: the vector,
734
+ the acknowledge and the tick, with the measurements.
735
+ - Pitfall `restore_nmi_not_maskable` (`pitfalls/kernal-and-io.md`), the
736
+ long form: the wiring of the key, the KERNAL path byte by byte, the
737
+ 189-cycle cost of a press with the KERNAL handler in place, and RESTOR
738
+ and VECTOR undoing the vector.
739
+ - `hardware/cia-reference.md`, `$DD0D` and "NMI vector (CIA2 +
740
+ RESTORE)".
741
+ - `hardware/c64-memory-map.md`, `$0318-$0319` and `$FE43-$FF42`.
742
+ - Recipe `recipes/kickassembler/nmi-timer-tick.md`.
743
+
744
+ ### Sources
745
+
746
+ - Commodore 64 KERNAL ROM 901227-03 (`kernal-901227-03.bin` as shipped
747
+ with VICE), as read for `restore_nmi_not_maskable`; not re-read for
748
+ this entry.
749
+ - VICE x64sc 3.10, the pinned runs of `nmi-timer-tick.prg` on PAL and
750
+ NTSC, screen cells decoded against `chargen-901225-01.bin`.
751
+ - C64-Wiki, "RESTORE (Key)", https://www.c64-wiki.com/wiki/RESTORE_(Key),
752
+ consulted for the name of the key's connection to the CPU only.
753
+
754
+ ---
755
+
756
+ ## cia_revision_irq_one_cycle_late — An old 6526 raises its timer interrupt one cycle later than a 6526A or 8521
757
+
758
+ **Severity:** medium
759
+ **Region:** both
760
+ **Triggered by registers:** DC04, DC05, DC0D, DC0E, DD0D
761
+ **Triggered by techniques:** double_irq
762
+
763
+ ### Symptom
764
+
765
+ Code whose timing is set by a CIA timer interrupt is right on one
766
+ machine and one cycle out on another, with nothing else changed.
767
+
768
+ - A raster routine that uses a CIA timer to place its second interrupt
769
+ a counted number of cycles after the first (the "CIA timer second
770
+ stage" of `double_irq`) lands one cycle late on an older board: a
771
+ colour split moves eight pixels right, or a routine that was stable
772
+ jitters by one cycle on every frame.
773
+ - A loader whose receive loop is tuned so that a timer interrupt or a
774
+ timer-polled read falls in a fixed bit cell misses the cell on the
775
+ other revision and reads a wrong bit, so the same disk that loads on
776
+ the development machine fails on another.
777
+
778
+ Which machine is "right" is whichever the code was tuned on. Two
779
+ revisions of the chip shipped in the C64 and both are common.
780
+
781
+ ### Mechanism
782
+
783
+ The C64 carried two CIA revisions over its life: the original 6526,
784
+ and the later 6526A, which the 8521 in the C64C is an HMOS version of
785
+ (board history from the C64-Wiki CIA page, which says only that later
786
+ boards "may also use the 6526A or the 8521"; not verified here). On a
787
+ timer underflow every revision sets the timer's bit in the interrupt
788
+ control register in the same cycle. What differs is when `/IRQ` (or
789
+ `/NMI` for CIA2) follows: the old 6526 pulls the pin one cycle after
790
+ the flag, the newer parts in the same cycle. The 6510 samples the
791
+ interrupt line once per instruction, so a one-cycle change on the pin
792
+ either changes nothing or moves the handler's entry by the length of
793
+ the instruction that was running.
794
+
795
+ VICE 3.10 models this. `x64sc -help` lists `-ciamodel <0|1>` (both
796
+ chips), `-cia1model` and `-cia2model`, with 0 the "old 6526" and 1 the
797
+ "new 8521"; a run with no flag behaved as model 1 here. In the
798
+ emulator's source (`src/core/ciacore.c`, `cia_run_ifr_cycle`, read at
799
+ the VICE-Team GitHub mirror) the old-model branch always schedules the
800
+ interrupt raise one cycle on, and the new-model branch raises it in
801
+ the same cycle unless the ICR was read in the cycle before. That
802
+ source is the named authority for the real-hardware claim; no 6526 was
803
+ put on a bench for this entry, and the one-cycle figure for hardware
804
+ stands at rung 4.
805
+
806
+ Measured in VICE x64sc 3.10, the difference is exactly what the model
807
+ says. A probe starts CIA1 Timer B free-running from `$FFFF` and, four
808
+ cycles later, Timer A one-shot from latch `$40 + k` for eight phases
809
+ `k`, then executes 400 cycles of 2-cycle `NOP`s. The handler's first
810
+ instruction is `lda $DC06`. The screen shows `E - N`, where `E` is
811
+ `$FFFF` less the Timer B value read and `N` the Timer A latch; the
812
+ display is blanked (`$D011` bit 4 clear) for the whole test so no
813
+ badline can move anything, and the KERNAL is banked out so `$FFFE`
814
+ reaches the handler directly. Same figures on PAL and NTSC:
815
+
816
+ | `-ciamodel` | k = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
817
+ |---|---|---|---|---|---|---|---|---|
818
+ | 0, old 6526 | $12 | $11 | $12 | $11 | $12 | $11 | $12 | $11 |
819
+ | 1, new 8521 | $10 | $11 | $10 | $11 | $10 | $11 | $10 | $11 |
820
+ | none (default) | $10 | $11 | $10 | $11 | $10 | $11 | $10 | $11 |
821
+
822
+ `-cia1model 0 -cia2model 1` reads as the old row and `-cia1model 1
823
+ -cia2model 0` as the new row, so the flag for the chip under test is
824
+ the one that matters. At every even phase the old part's handler runs
825
+ two cycles later than the new part's, at every odd phase at the same
826
+ cycle; over the two phases the mean difference is one cycle, and the
827
+ alternation is the `NOP` boundary, not the chip. A control read of Timer B eight cycles after
828
+ its start write returned `$FFFA` on both models, so the timers' own
829
+ start delay does not differ between them. Subtracting the fixed parts
830
+ of the path (Timer A's four-cycle later start, the `N + 1` counts to
831
+ underflow, the three cycles from the handler's opcode fetch to the
832
+ `lda`'s read) puts the new part's underflow-to-handler-fetch at 8 or 9
833
+ cycles and the old part's at 9 or 10; that subtraction is arithmetic on
834
+ the timer pipeline, rung 3, and the difference between the rows is the
835
+ measurement.
836
+
837
+ Not measured here: CIA2 and `/NMI` (the source has one path for both
838
+ chips, so the same one-cycle rule is expected, rung 4), the Timer B
839
+ bug VICE also gates on the old model (an ICR read in the cycle before
840
+ a Timer B underflow loses the flag), and any real chip.
841
+
842
+ ### Fix
843
+
844
+ Two remedies; use both.
845
+
846
+ 1. **Detect the revision once at start and branch on it.** Run the
847
+ two-phase form of the probe with interrupts otherwise off, before
848
+ any timing-critical code is installed, and store a byte: `00` old
849
+ 6526, `01` 6526A or 8521. `recipes/kickassembler/cia-revision-detect.md`
850
+ is that routine, verdict at `$02FE`. A timer-timed second stage then
851
+ loads a latch one count shorter on the old part, or the loader picks
852
+ the delay table for the part it found. The detection is a short
853
+ routine and costs a little over two raster frames.
854
+ 2. **Do not put a bit cell or a pixel on a one-cycle edge.** Where a
855
+ timer interrupt sets the phase, follow it with a raster-synchronised
856
+ entry (`stable_raster_irq`) or a self-timing sequence so the CIA's
857
+ delay is absorbed; where a loader polls a timer, centre the read in
858
+ the bit cell rather than at its edge. A routine that tolerates one
859
+ cycle either way is right on both revisions without detection.
860
+
861
+ ### Worked example
862
+
863
+ The bad pattern: a second-stage interrupt whose latch was tuned on one
864
+ machine.
865
+
866
+ ```text
867
+ ; first IRQ, raster-triggered, jitter up to 7 cycles
868
+ lda #<DELAY ; a value found by trial on the author's C64
869
+ sta $dc04
870
+ lda #>DELAY
871
+ sta $dc05
872
+ lda #$19 ; one-shot, load, start
873
+ sta $dc0e
874
+ ; ... second handler writes $D020 at "the" cycle
875
+ ```
876
+
877
+ On the other revision the write lands one cycle away from where it was
878
+ tuned, and because the 6510 quantises the entry to an instruction
879
+ boundary the visible error is 0 or 2 cycles depending on which
880
+ instruction the interrupt met.
881
+
882
+ The fix, in outline (the full routine is the recipe):
883
+
884
+ ```text
885
+ ; at start-up, interrupts masked, display blanked
886
+ ; run the probe at latch $40 and $41; E-N pairs:
887
+ ; $12,$11 -> old 6526 -> revision = 0
888
+ ; $10,$11 -> 6526A / 8521 -> revision = 1
889
+ ; later:
890
+ lda revision
891
+ beq old_part
892
+ lda #<DELAY
893
+ bne set_latch
894
+ old_part:
895
+ lda #<DELAY-1 ; the old part raises one cycle later
896
+ set_latch:
897
+ sta $dc04
898
+ ```
899
+
900
+ Per-model runs of the recipe (VICE x64sc 3.10, `-ciamodel 0` and `1`,
901
+ PAL and NTSC, four runs): row 2 of the screen `12 11` and verdict `OLD
902
+ 6526` under model 0, `10 11` and `NEW 8521 OR 6526A` under model 1,
903
+ `$02FF` = `01` and a green border in all four.
904
+
905
+ ### Cross-references
906
+
907
+ - Recipe `recipes/kickassembler/cia-revision-detect.md`, the detection
908
+ routine, pinned under `-ciamodel 0`.
909
+ - Technique `double_irq` (`techniques/raster.md`), whose "CIA timer
910
+ second stage" is the raster case above; `stable_raster_irq` in the
911
+ same file is the raster-synchronised entry that absorbs the delay.
912
+ - Techniques `krill_loader_integration` and `sparkle_irq_loader`
913
+ (`techniques/loaders-packers.md`): the loader case. Their pages
914
+ describe finished loaders and nothing in their text turns on a
915
+ cycle-exact timer interrupt, so they are not on the Triggered-by
916
+ line; the pitfall is for a loader written from scratch with a timer
917
+ in its bit loop. Whether either loader detects the revision was not
918
+ checked here.
919
+ - `hardware/cia-reference.md`, "IRQ / NMI on underflow" and the
920
+ "CIA chip revision differences" bullet under its Pitfalls list, which
921
+ names the revisions without a figure; this entry supplies the one
922
+ figure that was measured.
923
+ - Pitfall `raster_irq_first_line_jitter` (`pitfalls/raster-and-badline.md`)
924
+ for the instruction-boundary quantisation that turns one cycle into
925
+ 0 or 2.
926
+
927
+ ### Sources
928
+
929
+ - VICE x64sc 3.10, the eight-phase probe and the recipe, run under
930
+ `-ciamodel 0`, `-ciamodel 1`, no flag, and the `-cia1model` /
931
+ `-cia2model` pairs, PAL and NTSC, screen cells decoded against
932
+ `chargen-901225-01.bin`.
933
+ - VICE source, `vice/src/core/ciacore.c` at
934
+ https://github.com/VICE-Team/svn-mirror, function `cia_run_ifr_cycle`,
935
+ read for the model branches only.
936
+ - `x64sc -help`, VICE 3.10, for the option names and values.
937
+ - C64-Wiki, "CIA", https://www.c64-wiki.com/wiki/CIA, for the sentence
938
+ on which revisions later boards carried; it gives no timing figure.
939
+
940
+ ---
941
+
942
+ ## cia_icr_read_clears_all_flags — One read of $DC0D or $DD0D clears every pending flag, not just the one you tested
943
+
944
+ **Severity:** high
945
+ **Region:** both
946
+ **Triggered by registers:** DC0D, DD0D
947
+ **Triggered by techniques:** tape_turbo_loader, nmi_handler_and_restore_key, irq_chain_table, tod_alarm_interrupt, irq_keyboard_own_scan, irq_owns_processor_port
948
+
949
+ ### Symptom
950
+
951
+ Two routines share one CIA. Each reads the interrupt control register
952
+ to look for its own event, and one of them never sees it.
953
+
954
+ - A tape or serial routine spins on bit 4 of `$DC0D` waiting for a
955
+ FLAG edge. A timer on the same chip underflows while it spins. The
956
+ routine that later checks the timer bit finds it clear, and its
957
+ timeout, its tick or its bit-cell clock is simply gone.
958
+ - The other way round: a timer poll runs first and a FLAG edge that
959
+ arrived during it is consumed by the timer poll. The FLAG waiter
960
+ then waits for an edge that has already been and gone.
961
+ - On CIA2, a read of `$DD0D` from the main program while a Timer A
962
+ underflow is arriving leaves the NMI handler with nothing to
963
+ dispatch on, and at one phase the NMI itself is not raised: the read
964
+ returns `$01`, the handler never runs, and the tick is lost.
965
+
966
+ Nothing errors. The register just reads `$00` the second time.
967
+
968
+ ### Mechanism
969
+
970
+ The 6526's interrupt control register at offset `$0D` is two registers
971
+ at one address. A write sets or clears mask bits. A read returns the
972
+ five event flags in bits 0..4 (Timer A, Timer B, TOD alarm, serial,
973
+ FLAG) with bit 7 set if any flagged event is also enabled in the mask,
974
+ and the read clears all of them together. There is no way to read one
975
+ flag and leave the others standing. The data sheet's word for this is
976
+ that the register is cleared on read; the consequence is that whoever
977
+ reads it first owns every event that had arrived by then.
978
+
979
+ Measured in VICE x64sc 3.10, PAL and NTSC, default CIA model and
980
+ `-ciamodel 0`, same bytes in all three runs unless a row says otherwise.
981
+ Interrupts held off with `SEI` for the CIA1 tests so the flags could
982
+ be read rather than taken; mask bit 7 with bit 0 (`$81`) written so
983
+ the pending bit would show.
984
+
985
+ 1. **One read takes both.** CIA1 Timer A one-shot from latch `$0060`,
986
+ then twenty back-to-back reads of `$DC0D` stored to RAM, 16 cycles
987
+ apart. Reads one to six returned `$00`; the seventh returned `$81`
988
+ (bit 7 pending, bit 0 Timer A, bit 4 FLAG clear); reads eight to
989
+ twenty returned `$00`. The underflow was seen once, in a read that
990
+ was looking for FLAG, and never again.
991
+ 2. **The losing pattern.** The same timer, then a FLAG poll of twenty
992
+ reads testing only bit 4, then a "has the timer fired" read. The
993
+ check read returned `$00` (bit 0 clear) and a further read straight
994
+ after it `$00`; the control, the same timer with a delay loop that
995
+ reads `$D020` instead of the ICR, returned `$81`. The poll took the
996
+ underflow on its way past and left nothing for the check.
997
+ 3. **The fix pattern.** The same poll, but each read is ORed into a
998
+ byte in RAM before bit 4 is tested. After the poll that byte was
999
+ `$81`; the timer check, made on the copy, saw `$01`; a fresh read of
1000
+ `$DC0D` afterwards returned `$00`, so the copy was the only place
1001
+ the event still existed.
1002
+ 4. **CIA2, NMI masked.** Timer A of CIA2 one-shot from `$0060` with
1003
+ `$7F` written to `$DD0D` first. After the underflow `$DD0D` read
1004
+ `$01` (flag set, bit 7 clear because nothing was enabled) and the
1005
+ next read `$00`. Masking an interrupt does not stop its flag from
1006
+ being set, and does not stop a read from clearing it.
1007
+ 5. **CIA2, NMI enabled, handler reads once.** Vector at `$0318`,
1008
+ handler `LDA $DD0D` into a byte. One NMI counted, the handler's
1009
+ copy `$81`, and a main-program read after it `$00`.
1010
+ 6. **Two timers, one read.** CIA1 Timer A and Timer B one-shot from
1011
+ `$0020` together, mask `$83`. The first read of `$DC0D` returned
1012
+ `$83`; the second `$00`.
1013
+ 7. **A stray read racing the NMI.** CIA2 Timer A one-shot from latch
1014
+ `$60 + k` for eight phases k while the main program reads `$DD0D`
1015
+ in a 16-cycle loop and the handler of test 5 is installed. PAL,
1016
+ default model: phases 0..3, the main loop read `$81` and the
1017
+ handler read `$00`; phases 4..7, the handler read `$81` and the
1018
+ main loop `$00`; one NMI counted at every phase. NTSC default model
1019
+ and PAL `-ciamodel 0`: the same, except that at phase 3 the main
1020
+ loop read `$01`, the handler read `$00` and **no NMI was counted**.
1021
+ A read that lands in the cycle the chip would raise the line in
1022
+ takes the flag before the line follows it, and bit 7 never sets.
1023
+ Which phase does that depends on where the loop's reads fall
1024
+ against the timer, so treat the phase number as this probe's and
1025
+ the loss as the finding.
1026
+
1027
+ Not measured here: any real 6526 or 8521; a FLAG edge itself (no
1028
+ cassette or serial input was driven, so bit 4 was never set and the
1029
+ lost-FLAG case is the mirror of the lost-timer case, rung 3).
1030
+
1031
+ ### Fix
1032
+
1033
+ One reader per interrupt control register, and one read per event.
1034
+
1035
+ 1. **Read once into RAM and test the copy.** Whoever polls reads the
1036
+ ICR into a byte, ORs it into a pending byte, and every routine on
1037
+ that chip tests and clears its own bit in the pending byte, never
1038
+ the register. Test 3 above is that pattern; the timer bit survived
1039
+ the FLAG poll in it.
1040
+ 2. **Give the register one owner.** If the chip has an interrupt
1041
+ handler, the handler is the only code that reads its ICR. It reads
1042
+ once, stores the byte, and dispatches on the copy. Main-program
1043
+ code that wants to know about a CIA event reads the handler's copy,
1044
+ not the chip. Test 7 is what a second reader costs: at some phases
1045
+ the handler sees nothing, and at one the interrupt itself is gone.
1046
+ 3. **Do not read an ICR you do not own to "be safe".** A stray
1047
+ `LDA $DC0D` in a loader's clean-up, or a `BIT $DD0D` added to a
1048
+ routine that is not the NMI handler, acknowledges events that
1049
+ belong to someone else. The KERNAL's own IRQ exit at `$EA7E` reads
1050
+ `$DC0D`; a raster handler that falls through to it while a CIA1
1051
+ timer is in use hands the timer's flag to the KERNAL.
1052
+
1053
+ Setting up is still a read: `$7F` to the ICR and one read to clear
1054
+ whatever was standing is right at install time, when nothing owns the
1055
+ chip yet. The rule is about steady state.
1056
+
1057
+ ### Worked example
1058
+
1059
+ The losing pattern, two routines and one register:
1060
+
1061
+ ```asm
1062
+ // LOSES THE TIMER: the FLAG poll's read cleared bit 0 on the way past.
1063
+ wait_flag:
1064
+ lda $dc0d
1065
+ and #$10
1066
+ beq wait_flag // measured: an underflow during this loop is gone
1067
+ rts
1068
+
1069
+ check_timer:
1070
+ lda $dc0d // read here: $00 after the poll, $81 without it
1071
+ and #$01
1072
+ rts
1073
+ ```
1074
+
1075
+ The fix, one read kept in RAM:
1076
+
1077
+ ```asm
1078
+ // Correct: every read is banked into a pending byte; routines test the copy.
1079
+ poll_icr:
1080
+ lda $dc0d // the only read of $DC0D outside install
1081
+ ora pending
1082
+ sta pending
1083
+ rts
1084
+
1085
+ wait_flag_ok:
1086
+ jsr poll_icr
1087
+ lda pending
1088
+ and #$10
1089
+ beq wait_flag_ok
1090
+ lda pending
1091
+ and #$ef // consume FLAG, leave the timer bit
1092
+ sta pending
1093
+ rts
1094
+
1095
+ check_timer_ok:
1096
+ lda pending // measured: $81 after the poll, so bit 0 is here
1097
+ and #$01
1098
+ beq no_tick
1099
+ lda pending
1100
+ and #$fe
1101
+ sta pending
1102
+ no_tick:
1103
+ rts
1104
+
1105
+ pending:
1106
+ .byte $00
1107
+ ```
1108
+
1109
+ For a chip with a handler, the handler is `poll_icr`: it reads once,
1110
+ stores, and dispatches on the stored byte; nothing else touches the
1111
+ register. On CIA2 that read is also the acknowledge that lets `/NMI`
1112
+ rise again (`kernal_nmi_handler_runs_stop_check`).
1113
+
1114
+ ### Cross-references
1115
+
1116
+ - Pitfall `cia_revision_irq_one_cycle_late` (this page): both models
1117
+ have a one-cycle window in which a read takes the flag before the
1118
+ line follows it, the old part because its raise is a cycle later,
1119
+ the new part when the read lands in the cycle before the raise.
1120
+ Which phase of test 7 falls in that window is a matter of
1121
+ alignment, which is why NTSC default and PAL `-ciamodel 0` lost
1122
+ phase 3 and PAL default lost none of the eight tried.
1123
+ - Pitfall `kernal_nmi_handler_runs_stop_check` (this page) and
1124
+ `restore_nmi_not_maskable` (`pitfalls/kernal-and-io.md`): the
1125
+ opposite failure, a handler that never reads `$DD0D`. The two rules
1126
+ together: exactly one read, in the handler.
1127
+ - Pitfall `tape_bit_is_a_pulse_pair_not_a_pulse` (`pitfalls/loader.md`)
1128
+ and technique `tape_turbo_loader` (`techniques/file-io.md`): the
1129
+ loader whose FLAG spin on bit 4 met this. Its Timer B is read from
1130
+ `$DC06`/`$DC07`, not from the ICR, which is why it survives; a
1131
+ variant that timed out on the Timer B flag would not.
1132
+ - Technique `frame_sync_loop` (`techniques/raster.md`) is not on the
1133
+ Triggered-by line: its text waits on `$D012`, and no ICR read
1134
+ appears in it.
1135
+ - `hardware/cia-reference.md`, the interrupt control register.
1136
+
1137
+ ### Sources
1138
+
1139
+ - VICE x64sc 3.10 (windowless build), the seven-test probe above,
1140
+ run PAL default model, NTSC default model, PAL `-ciamodel 0`, screen
1141
+ cells decoded against `chargen-901225-01.bin`. Tests 1 to 4 were
1142
+ repeated in a smaller probe with the results dumped from RAM by a
1143
+ monitor tracepoint, same three runs; the test 2 bytes are that
1144
+ probe's.
1145
+ - MOS 6526 data sheet, interrupt control register: flags cleared on
1146
+ read, bit 7 the pending summary; the bit assignments above are the
1147
+ sheet's.