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,1836 @@
1
+ # KERNAL Routines Reference
2
+
3
+ ## Overview
4
+
5
+ The Commodore 64 KERNAL is the 8 KiB ROM at `$E000-$FFFF` that provides the
6
+ machine's operating-system layer: character I/O, file I/O over the IEC serial
7
+ bus, the screen editor, the 60/50 Hz IRQ jiffy clock, the STOP-key check, and
8
+ the cold-start / warm-start sequences. User programs do not call KERNAL code at
9
+ its real ROM addresses — those moved between Commodore machines (VIC-20, PET,
10
+ C64, C128, Plus/4). Instead, the KERNAL exposes a **jump table** in the last
11
+ 128 bytes of the address space, from `$FF81` upward, where each entry is a
12
+ 3-byte `JMP abs` instruction to the real routine. Commodore promised this
13
+ table would be source-compatible across machines; in practice the shared
14
+ part is narrower than the whole table. The block from `$FFC0` (OPEN) to
15
+ `$FFEA` (UDTIM) sits at the same positions on every machine from the PET
16
+ onward; BASIC 4 PETs also carry `$FF93-$FFBD`; the VIC-20's table starts at
17
+ `$FF8A` (RESTOR) and holds 36 of the 39 entries slot-for-slot with the C64's,
18
+ with `$FF7F-$FF84` occupied by its IRQ/BRK `JMP ($0316)`/`JMP ($0314)` tails;
19
+ SCREEN/PLOT/IOBASE at `$FFED-$FFF3` first appear on the VIC-20 and
20
+ CINT/IOINIT/RAMTAS at `$FF81-$FF87` first on the C64. The Plus/4 and C128
21
+ keep all 39 entries at the C64 positions. (An earlier version of this page
22
+ said every Commodore 8-bit shared the table from `$FF81`; the PET and VIC-20
23
+ ROMs do not. Read from the VICE ROM images: PET 901439-04-07 / 901465-03 /
24
+ 901465-22, VIC-20 901486-07, C64 901227-03, Plus/4 318004-05, C128
25
+ 318020-05.)
26
+
27
+ There are **39 jump-table entries** spanning `$FF81-$FFF3`. The portion from
28
+ `$FFC0-$FFF5` is the historically-documented "user jump table" that the
29
+ *Commodore 64 Programmer's Reference Guide* and the *KERNAL Reference Manual*
30
+ encourage application programmers to call directly. The entries from
31
+ `$FF81-$FFBD` are lower-level — most are used internally by the higher-level
32
+ routines, but a handful (SETLFS, SETNAM, READST, MEMTOP, MEMBOT) are essential
33
+ for application code as well.
34
+
35
+ Every routine in this document is documented at its **jump-table address**,
36
+ not its real ROM address. Calling code should always `JSR $FFD2` (CHROUT),
37
+ never `JSR $F1CA` (CHROUT's own body, reached through the `$0326` vector) or
38
+ `JSR $E716` (the screen-output routine CHROUT dispatches to when the output
39
+ device is 3; device 4 and up goes to IECOUT at `$EDDD`). An earlier version
40
+ of this page named `$E716` as CHROUT's ROM address; it is the screen editor's
41
+ print routine — `$0326` defaults to `$F1CA` in every 901227 ROM (read from
42
+ the ROM images). The jump-table addresses are the stable contract; the
43
+ ROM-internal addresses are private implementation detail and can move
44
+ between KERNAL revisions and do move between Commodore machines. (The three
45
+ C64 revisions 901227-01, -02 and -03 differ in 277 bytes of in-place patches
46
+ spread over several dozen byte ranges — e.g. `$E4AC-$E4FF`, `$F428-$F44C`,
47
+ `$FF5B-$FF80` — and no entry address moved; `$F1CA`, `$E716` and `$F49E` are
48
+ at the same addresses in all three, so the examples here illustrate the
49
+ contract, not a case where it bit. Other Commodore machines put every
50
+ routine elsewhere.)
51
+
52
+ The 39 routines group naturally into eight categories:
53
+
54
+ 1. **Initialization** — CINT, IOINIT, RAMTAS, RESTOR, VECTOR. These run at
55
+ power-on and reset; an application normally calls them only to recover
56
+ after corrupting state.
57
+ 2. **High-level file I/O** — SETLFS, SETNAM, OPEN, CLOSE, CHKIN, CHKOUT,
58
+ CLRCHN, CLALL, LOAD, SAVE. The supported way to talk to disk, tape, and
59
+ printer.
60
+ 3. **Character I/O** — CHRIN, CHROUT, GETIN. The screen editor and keyboard
61
+ queue funnel through these.
62
+ 4. **Low-level IEC bus** — LISTEN, TALK, SECOND, TKSA, IECIN, IECOUT, UNLSN,
63
+ UNTLK, READST. Direct serial-bus protocol for fast loaders and bus
64
+ custom code.
65
+ 5. **Screen + cursor** — PLOT, SCREEN.
66
+ 6. **Time + STOP key** — RDTIM, SETTIM, UDTIM, STOP.
67
+ 7. **Memory** — MEMTOP, MEMBOT (BASIC top-of-memory and bottom-of-memory
68
+ pointers).
69
+ 8. **Misc** — SETMSG (KERNAL message control), SETTMO (IEEE timeout — no-op
70
+ on C64), SCNKEY (keyboard scan), IOBASE (I/O base address for
71
+ self-relocating code).
72
+
73
+ The KERNAL also exposes a set of **RAM vectors** at `$0314-$0333`
74
+ (IRQ, BRK, NMI, OPEN, CLOSE, CHKIN, CHKOUT, CLRCHN, CHRIN, CHROUT, STOP,
75
+ GETIN, CLALL, USRCMD, LOAD, SAVE) that the jump-table routines indirect
76
+ through. Patching these vectors is how programs hook character I/O (e.g.
77
+ to capture screen output for printer redirection) or replace the IRQ
78
+ handler (e.g. to drive a player routine). The VECTOR jump-table entry
79
+ (`$FF8D`) reads or writes all of them in one call. See [Vectors](#vectors)
80
+ below.
81
+
82
+ ### KERNAL ROM revisions
83
+
84
+ Three production-run KERNAL ROMs shipped in the C64 lifetime, identified
85
+ by the Commodore part number printed on the ROM chip:
86
+
87
+ - **901227-01** — the original KERNAL. No PAL/NTSC detection: CINT's
88
+ entry is `JMP $E518` and IOINIT hard-codes the jiffy timer at `$411B`;
89
+ the screen clear fills colour RAM with white (`LDA #$01` at `$EA0B`);
90
+ after the tape FOUND message it waits for any key (`$F761`).
91
+ `$FF5B-$FF80` is unused filler.
92
+ - **901227-02** — adds PAL/NTSC detection: CINT's entry becomes a
93
+ wrapper at `$FF5B` that runs the old `$E518`, watches for raster line
94
+ 311 (the VIC init table now sets `$D011`/`$D012` to `$9B`/`$37`),
95
+ stores the result in `$02A6` and lets IOINIT pick a jiffy timer of
96
+ `$4025` (PAL) or `$4295` (NTSC); RS-232 gains a PAL baud table at
97
+ `$E4EC` and region-aware OPEN/NMI code; BASIC's CHKOUT wrapper
98
+ (`$E118`) preserves A via a patch at `$E4AD`; the screen clear fills
99
+ colour RAM with the background colour (`$E4DA` = `LDA $D021`); the wait
100
+ after the tape FOUND message becomes a timed wait on the jiffy clock
101
+ that a keypress also ends (`$E4E0`). This is the only cassette change;
102
+ no tape timing constant differs (`$F767-$FCFB` identical).
103
+ - **901227-03** — the most common ROM, shipped in the bulk of C64
104
+ units sold from mid-1983 onward, and the source of all addresses in
105
+ this doc and the source most other documentation cites by default.
106
+ The screen clear fills colour RAM with the current text colour
107
+ (`$E4DA` = `LDA $0286`), plus small screen-editor (`$E57C-$E599`,
108
+ `$E621`) and RS-232 (`$EF94` -> `$E4D3`) patches.
109
+
110
+ Identify the revision from the byte at `$FF80`: `PEEK(65408)` returns
111
+ 170 (-01), 0 (-02) or 3 (-03).
112
+
113
+ All three expose **the same 39-entry jump table at the same
114
+ addresses**, and the differences between them are in-place patches:
115
+ -01 and -03 differ in 277 bytes (-01/-02 227, -02/-03 57; `cmp` on the
116
+ VICE ROM images). No routine's entry address moved — 38 of 39
117
+ jump-table targets and all sixteen default RAM vectors are identical,
118
+ and the one changed entry (CINT) points at a wrapper that still calls
119
+ `$E518`. The IEC bus routines, RAMTAS, LOAD, CHROUT's body (`$F1CA`)
120
+ and the screen-output routine (`$E716`) are byte-identical in all
121
+ three, so `JSR $F1CA`, `JSR $E716` or `JSR $F49E` would in fact work on
122
+ every 901227 revision. Use the jump table anyway: it is the contract
123
+ Commodore kept across machines (the VIC-20, C128 and Plus/4 internals
124
+ are elsewhere), and it survives the RAM-vector hooks that direct calls
125
+ bypass. An earlier version of this section said -01 had a slower IEC
126
+ protocol, that -02 fixed a RAM-test bug, that internal addresses moved
127
+ between revisions and that `JSR $E716` was "silently broken" on another
128
+ revision; none of that is in the ROM bytes.
129
+
130
+ The C128 in C64 mode maps a plain C64 KERNAL image, not the C128
131
+ KERNAL. An earlier revision of this page named 318020-05 here; that
132
+ part is the C128-mode KERNAL/editor ROM, a different program (its jump
133
+ table points into `$C000`/`$E1xx` and its reset vector is `$FF3D`).
134
+ VICE x128 loads `kernal64-901227-03.bin` for C64 mode, byte-identical
135
+ to the C64's 901227-03 — measured by dumping the `c64rom` bank from
136
+ x128 3.10 in `-go64` mode. VICE also ships two variants for that slot,
137
+ `kernal64-325179-01` and `-325182-01`, differing from 901227-03 in 54
138
+ and 24 bytes (table bytes and one small patch; none in the
139
+ `$FF81-$FFF4` jump table). The part number on a real C128's C64-ROM
140
+ chip (usually given as 251913-01, a combined BASIC+KERNAL mask) is from
141
+ documentation, not measured here. Either way the 39-entry jump table is
142
+ at the same addresses with identical semantics — programs that use only
143
+ the jump table run unchanged on a C128 in C64 mode.
144
+
145
+ ### Zero page each routine writes
146
+
147
+ Each routine below carries `**Clobbers zero page:**` lines. They matter to
148
+ any program that keeps its own variables in zero page and still calls the
149
+ KERNAL.
150
+
151
+ - `(may; ROM walk from $FFxx, power-on vectors)` is an upper bound:
152
+ every byte `$00-$FF` that code reachable from the jump-table slot can
153
+ store to (STA, STX, STY, INC, DEC and the shifts; an indexed store
154
+ counts the index range read off the ROM at that site). Error paths
155
+ count. So does a tape IRQ handler the routine installs at `$0314`
156
+ while it runs (the four in the table at `$FD9B`).
157
+ Vectors are followed through their power-on values: RESTOR's table at
158
+ `$FD30`, and `$028F` → `$EB48`, which CINT sets. A program that repoints
159
+ a vector, such as `$0326` for CHROUT, changes what the routine runs, and
160
+ the line no longer applies. "Also stores through (`$AC`)" means the
161
+ routine writes where that pointer points, not to `$AC` itself.
162
+ `scripts/kernal-zp-walk.ts` writes these lines from the 901227-03 image,
163
+ and `npm test` fails if the page and the walk disagree.
164
+ - `(must; VICE x64sc store trace, <call>)` is what that one call wrote,
165
+ measured by `scripts/kernal-zp-trace.ts` with every interrupt source
166
+ masked. It is a lower bound for that call only. Every traced byte lies
167
+ inside the may set; the walk's check enforces it.
168
+
169
+ The largest routines (OPEN, CLOSE, CHKIN, CHKOUT, CHRIN, CHROUT, GETIN,
170
+ LOAD, SAVE) may write `$D9-$F2`, the screen editor's line-link table. The
171
+ traced CHROUT, printing enough lines to scroll the screen, wrote all of
172
+ `$D9-$F4` and `$AC-$AF`. A loader or a routine that keeps state in
173
+ `$E0-$EF` and also prints through CHROUT loses it.
174
+
175
+ The IRQ and NMI services are not jump-table routines. With interrupts
176
+ enabled they run between any two instructions of a program, so their
177
+ writes apply everywhere. The NMI walk reaches the RUN/STOP-RESTORE warm
178
+ start, which leaves through `JMP ($A002)` into BASIC (or `JMP ($8002)`
179
+ into a cartridge) and does not return.
180
+
181
+ <!-- kernal-zp-walk: services (generated by scripts/kernal-zp-walk.ts --write) -->
182
+ | Entry | May write |
183
+ |---|---|
184
+ | IRQ `$FF48`, through `$0314` to `$EA31` | $01, $91, $A0-$A2, $C0, $C5-$C6, $CB, $CD-$CF, $F3-$F6 (may; ROM walk from $FF48, power-on vectors; also stores through ($D1), ($F3)) |
185
+ | NMI `$FE43`, through `$0318` to `$FE47` | $00-$01, $90-$91, $94-$95, $99-$9A, $A3, $A5, $A7-$AB, $B4-$B6, $BD, $C3-$C4, $C6, $CC-$CD, $CF, $D1-$D3, $D5-$D6, $D9-$F4 (may; ROM walk from $FE43, power-on vectors; not followed: JMP ($8002), JMP ($A002); also stores through ($C3), ($D1), ($F3), ($F7)) |
186
+
187
+ No jump-table routine but RAMTAS, and neither service, may write $02-$8F, $B2-$B3, $FB-$FF.
188
+ <!-- /kernal-zp-walk -->
189
+
190
+ The walk covers the KERNAL, not BASIC, which uses much of `$02-$8F`.
191
+
192
+ ### Sources
193
+
194
+ - *Commodore 64 Programmer's Reference Guide* (1982), Appendix B — KERNAL ROM machine language subroutines
195
+ - *Commodore 64 KERNAL Reference Manual* (1983)
196
+ - C64-Wiki: KERNAL Jump Table — https://www.c64-wiki.com/wiki/KERNAL_Jump_Table
197
+ - sta.c64.org: CBM 64 KERNAL routines — https://sta.c64.org/cbm64krnfunc.html
198
+ - pagetable.com: annotated C64 KERNAL disassembly — https://www.pagetable.com/c64ref/c64disasm/
199
+ - Mapping the Commodore 64 (Sheldon Leemon), KERNAL section — https://www.zimmers.net/anonftp/pub/cbm/c64/manuals/mapping-c64.txt
200
+
201
+ ## Quick reference
202
+
203
+ The full 39-entry jump table, in address order. Each entry is a 3-byte
204
+ `JMP abs` instruction.
205
+
206
+ | Addr | Name | One-line summary |
207
+ |---------|---------|--------------------------------------------------------------------|
208
+ | `$FF81` | CINT | Initialize screen editor + VIC-II (cold-screen setup) |
209
+ | `$FF84` | IOINIT | Initialize CIA1/CIA2/SID; set up jiffy IRQ |
210
+ | `$FF87` | RAMTAS | RAM test, set top/bottom of memory, clear `$0002-$00FF` and `$0200-$03FF` (not the stack page) |
211
+ | `$FF8A` | RESTOR | Restore RAM vectors at `$0314-$0333` to KERNAL defaults |
212
+ | `$FF8D` | VECTOR | Read or write all RAM vectors as a block |
213
+ | `$FF90` | SETMSG | Control KERNAL error / control-message verbosity |
214
+ | `$FF93` | SECOND | Send secondary address after LISTEN |
215
+ | `$FF96` | TKSA | Send secondary address after TALK |
216
+ | `$FF99` | MEMTOP | Read or set top of RAM (used by BASIC + KERNAL buffers) |
217
+ | `$FF9C` | MEMBOT | Read or set bottom of RAM (start of BASIC text area) |
218
+ | `$FF9F` | SCNKEY | Scan the keyboard matrix, push key into queue |
219
+ | `$FFA2` | SETTMO | Set IEEE-488 timeout flag (no effect on C64) |
220
+ | `$FFA5` | IECIN | Receive one byte from serial bus (alias ACPTR) |
221
+ | `$FFA8` | IECOUT | Send one byte to serial bus (alias CIOUT) |
222
+ | `$FFAB` | UNTLK | Send UNTALK on serial bus |
223
+ | `$FFAE` | UNLSN | Send UNLISTEN on serial bus |
224
+ | `$FFB1` | LISTEN | Send LISTEN command to a device on the serial bus |
225
+ | `$FFB4` | TALK | Send TALK command to a device on the serial bus |
226
+ | `$FFB7` | READST | Read the KERNAL serial-bus status byte |
227
+ | `$FFBA` | SETLFS | Set logical file number, device, and secondary address |
228
+ | `$FFBD` | SETNAM | Set filename length and pointer |
229
+ | `$FFC0` | OPEN | Open a logical file (uses SETLFS+SETNAM state) |
230
+ | `$FFC3` | CLOSE | Close a logical file |
231
+ | `$FFC6` | CHKIN | Redirect input from a previously opened logical file |
232
+ | `$FFC9` | CHKOUT | Redirect output to a previously opened logical file |
233
+ | `$FFCC` | CLRCHN | Reset default I/O channels (input=keyboard, output=screen) |
234
+ | `$FFCF` | CHRIN | Read one character from current input channel |
235
+ | `$FFD2` | CHROUT | Write one character to current output channel |
236
+ | `$FFD5` | LOAD | Load or verify a file from device into memory |
237
+ | `$FFD8` | SAVE | Save memory range to device |
238
+ | `$FFDB` | SETTIM | Set the 24-bit jiffy clock |
239
+ | `$FFDE` | RDTIM | Read the 24-bit jiffy clock |
240
+ | `$FFE1` | STOP | Test RUN/STOP key, return Z=1 if pressed |
241
+ | `$FFE4` | GETIN | Get one character from keyboard queue (non-blocking) |
242
+ | `$FFE7` | CLALL | Close all open files (calls CLRCHN internally) |
243
+ | `$FFEA` | UDTIM | Update the jiffy clock; called from IRQ handler at `$EA31` |
244
+ | `$FFED` | SCREEN | Return screen dimensions (40 columns, 25 rows on stock C64) |
245
+ | `$FFF0` | PLOT | Read or set cursor row/column |
246
+ | `$FFF3` | IOBASE | Return base address of I/O block (always `$DC00` on C64) |
247
+
248
+ Three categorical points are non-obvious from the names:
249
+
250
+ - **`IECIN`/`IECOUT`** are documented in the original Commodore 64
251
+ *Programmer's Reference Guide* under the older PET-era names `ACPTR`
252
+ (accept byte from serial bus) and `CIOUT` (Commodore IEEE/IEC output).
253
+ Most modern documentation uses the IEC-prefixed names.
254
+ - **`CHRIN`/`CHROUT`** are also documented as `BASIN`/`BSOUT` in some
255
+ Commodore manuals. Same routine, different name.
256
+ - **`SETTMO`** (`$FFA2`) was meaningful on the PET's IEEE-488 bus; on
257
+ the C64's IEC serial bus the timeout flag is hard-wired and the
258
+ routine does nothing visible.
259
+
260
+ ## File I/O routines
261
+
262
+ The high-level file I/O surface is a stateful three-step protocol:
263
+
264
+ 1. **Configure** the next file operation with SETLFS (logical file, device,
265
+ secondary address) and SETNAM (filename buffer pointer, length).
266
+ 2. **Open** the file with OPEN, LOAD, or SAVE.
267
+ 3. **Use** it: redirect input with CHKIN, redirect output with CHKOUT, read
268
+ with CHRIN / GETIN, write with CHROUT. Reset to defaults with CLRCHN.
269
+ 4. **Close** with CLOSE or CLALL.
270
+
271
+ This is the same protocol the BASIC `OPEN`, `PRINT#`, `INPUT#`, and `CLOSE`
272
+ statements drive. A machine-language program that wants to open and read
273
+ a sequential file from drive 8 does:
274
+
275
+ ```asm
276
+ ; OPEN 2,8,2,"FILE,S,R"
277
+ lda #2 ; logical file number
278
+ ldx #8 ; device 8 (first disk drive)
279
+ ldy #2 ; secondary address = 2 (channel)
280
+ jsr $FFBA ; SETLFS
281
+ lda #fname_end-fname
282
+ ldx #<fname
283
+ ldy #>fname
284
+ jsr $FFBD ; SETNAM
285
+ jsr $FFC0 ; OPEN
286
+ bcs open_error ; C=1 on failure, A = error code
287
+
288
+ ldx #2 ; logical file from above
289
+ jsr $FFC6 ; CHKIN (redirect input)
290
+ read_loop:
291
+ jsr $FFCF ; CHRIN
292
+ ; ... process A ...
293
+ jsr $FFB7 ; READST
294
+ beq read_loop ; ST=0 means more bytes available
295
+
296
+ lda #2
297
+ jsr $FFC3 ; CLOSE
298
+ jsr $FFCC ; CLRCHN
299
+ rts
300
+
301
+ fname: .byte "FILE,S,R"
302
+ fname_end:
303
+ ```
304
+
305
+ Listings in this document are generic 6502 assembler syntax — `;`
306
+ comments and `.byte "…"` strings, as ca65 accepts them — not
307
+ KickAssembler fragments, and `npm run check:listings` does not build
308
+ them (it assembles only `asm` fences written in KickAssembler syntax
309
+ with no `;` lines). In KickAssembler use `//` comments and `.text "…"`;
310
+ Kick's default `.text` encoding is screen codes, which for upper-case
311
+ letters, digits and punctuation coincide with the unshifted PETSCII a
312
+ drive expects (`FILE,S,R` -> `46 49 4C 45 2C 53 2C 52`; `petscii_mixed`
313
+ would give `C6 C9 CC C5 …`). Under ca65 `-t c64` the same literal
314
+ assembles to shifted PETSCII `C6 C9 CC C5 …`, so set the filename bytes
315
+ deliberately in whichever assembler you use (both byte sequences
316
+ measured with KickAssembler 5.25 and ca65 2.19).
317
+
318
+ Three rules trip up first-time KERNAL programmers:
319
+
320
+ - **SETLFS + SETNAM state survives across calls** until you overwrite it.
321
+ If you call SETLFS once with secondary=2 and then call OPEN a second
322
+ time without calling SETLFS again, the second OPEN will reuse
323
+ secondary=2.
324
+ - **CHKIN/CHKOUT require a prior successful OPEN.** They return C=1 +
325
+ error code 3 ("file not open") if the logical file number doesn't
326
+ refer to an open file.
327
+ - **CLRCHN must be called before CLOSE.** If you `CLOSE` while CHKIN
328
+ has redirected input to that file, the next CHRIN reads from a
329
+ closed file and returns garbage. Always: `CLRCHN`, then `CLOSE`.
330
+
331
+ ### $FFBA — SETLFS — Set logical file parameters
332
+
333
+ **Input:** A = logical file number (1-255), X = device number (0-31), Y = secondary address (0-31 or `$FF`)
334
+ **Output:** None
335
+ **Affects:** None (parameters stored in zero-page workspace `$B8`, `$BA`, `$B9`)
336
+ **Clobbers zero page:** $B8-$BA (may; ROM walk from $FFBA, power-on vectors)
337
+ **Clobbers zero page:** $B8-$BA (must; VICE x64sc store trace, SETLFS 2,8,2)
338
+ **Pairs with:** SETNAM, OPEN, LOAD, SAVE
339
+ **Description:** Stores the three file parameters that the next OPEN, LOAD,
340
+ or SAVE will use. The logical file number (A) is a program-chosen tag in the
341
+ range 1-255; it appears in subsequent CHKIN/CHKOUT/CLOSE calls to identify
342
+ this file. The KERNAL stores the number in `$B8` and never interprets
343
+ it. The familiar rule that file numbers 128-255 get a linefeed after
344
+ every carriage return is BASIC's, not the KERNAL's: PRINT#, CMD and any
345
+ PRINT while CMD is active send `$0A` after `$0D` when bit 7 of the
346
+ current channel byte `$13` is set (BASIC ROM `$AAD7`, `BIT $13`). CHROUT
347
+ adds no byte of its own whatever the file number — measured in VICE
348
+ x64sc: `SETLFS` 200 followed by `CHROUT $0D` delivers `$0D` alone, while
349
+ BASIC's `PRINT#200` hands CHROUT `$0D,$0A` and `PRINT#100` hands `$0D`.
350
+ (An earlier revision placed this rule under SETLFS as if the KERNAL
351
+ applied it.)
352
+ The device number (X) selects the bus device: 0=keyboard, 1=tape (Datasette),
353
+ 2=RS-232 (user port), 3=screen, 4-7=printer/plotter, 8-30=IEC disk/printer
354
+ units. Secondary address (Y) is device-specific: for the 1541 disk drive,
355
+ 0=load PRG, 1=save PRG, 2-14=open named channel, 15=command channel. `$FF`
356
+ (any value with bit 7 set) means "send no secondary address" on the
357
+ serial bus: OPEN, CHKIN, CHKOUT and CLOSE then skip the
358
+ secondary-address byte, and OPEN sends nothing at all — not even the
359
+ filename — so it suits an unnamed channel such as a printer, not a named
360
+ disk file. It is what BASIC's OPEN supplies for device 3 and above when
361
+ the third parameter is omitted; for tape and RS-232 BASIC defaults to 0.
362
+ Tape reads the secondary address: for OPEN, 0 = read, 1 = write, 2 =
363
+ write followed by an end-of-tape marker at CLOSE (measured in VICE
364
+ x64sc: SA `$FF` on device 1 takes the write path and prompts PRESS
365
+ RECORD & PLAY ON TAPE); for SAVE, bit 0 = absolute-address header, bit 1
366
+ = write an end-of-tape marker after the data; for LOAD, see `$FFD5`
367
+ below. An earlier revision said `$FF` was "used by tape"; it is not.
368
+
369
+ ### $FFBD — SETNAM — Set filename
370
+
371
+ **Input:** A = filename length (0-16, longer accepted but truncated by device), X = low byte of filename pointer, Y = high byte of filename pointer
372
+ **Output:** None
373
+ **Affects:** None (length stored at `$B7`, pointer at `$BB`/`$BC`)
374
+ **Clobbers zero page:** $B7, $BB-$BC (may; ROM walk from $FFBD, power-on vectors)
375
+ **Clobbers zero page:** $B7, $BB-$BC (must; VICE x64sc store trace, SETNAM with a 4-byte name)
376
+ **Pairs with:** SETLFS, OPEN, LOAD, SAVE
377
+ **Description:** Records the filename for the next OPEN/LOAD/SAVE. The
378
+ filename is *not* copied; only its address is stored, so the buffer must
379
+ remain valid until OPEN/LOAD/SAVE returns. A=0 means "no filename" and is
380
+ legal for LOAD/SAVE on tape (loads the first PRG it finds) and for OPEN
381
+ on the printer or screen. Filenames sent to a disk drive may carry
382
+ embedded commas separating filename / file-type / mode, e.g.
383
+ `"DATA,S,R"` (sequential, read).
384
+
385
+ ### $FFC0 — OPEN — Open a logical file
386
+
387
+ **Input:** SETLFS and SETNAM state must be set first
388
+ **Output:** C=0 on success; C=1 on error, A = error code
389
+ **Affects:** A, X, Y, C
390
+ **Clobbers zero page:** $01, $90-$96, $98-$9C, $9E-$B1, $B4-$B6, $B9, $BD-$C2, $C5-$C7, $C9, $CB, $CD-$FA (may; ROM walk from $FFC0, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
391
+ **Clobbers zero page:** $90, $94-$95, $98, $A3, $A5, $B9 (must; VICE x64sc store trace, OPEN of a PRG file on drive 8 (true drive emulation))
392
+ **Pairs with:** SETLFS, SETNAM, CLOSE, CHKIN, CHKOUT
393
+ **Description:** Opens a logical file using the parameters previously
394
+ stored by SETLFS and SETNAM. For disk devices, sends LISTEN +
395
+ secondary-address (`$F0 | sec`) over the IEC bus, then sends the
396
+ filename bytes one at a time via IECOUT, then UNLISTEN. On success the
397
+ KERNAL records the logical file number in its open-file table
398
+ (`$0259-$0262` for LF, `$0263-$026C` for device, `$026D-$0276` for
399
+ secondary). Errors: 1=too many open files (10 already open), 2=logical
400
+ file number already open, 5=device not present, 6=logical file number 0
401
+ (the only 6 OPEN returns; OPEN never returns 7 — 6 and 7 as direction
402
+ errors belong to CHKIN/CHKOUT), 9=illegal device (tape OPEN with the
403
+ tape-buffer pointer `$B2/$B3` below `$0200`). OPEN's own code-4 exit is
404
+ on the tape branch only and is reached only through the STOP key during
405
+ the header search, and then only when `$93` (the load/verify flag left
406
+ by the last LOAD or VERIFY) is non-zero; with `$93` = 0 the same STOP
407
+ returns C=1, A=0. A name that is not on the tape does not produce 4: the
408
+ search reads on until an end-of-tape marker, which OPEN returns as C=1,
409
+ A=5 (BASIC prints ?DEVICE NOT PRESENT). Disk OPEN never reports a
410
+ missing file — read the error channel. Errors 1, 2 and 6 are detected
411
+ before the table entry is stored; for 4, 5 and 9 the entry has already
412
+ been added (`$98` incremented, LFN/device/secondary stored at
413
+ `$0259/$0263/$026D,X`) and stays there, so a retry with the same logical
414
+ file number returns 2 — CLOSE it (or CLALL) first. (An earlier version
415
+ of this entry listed 6 and 7 as direction errors, called 4 LOAD-only and
416
+ said a failed OPEN left no table entry; all three were checked against
417
+ the KERNAL ROM bytes and in VICE x64sc.) OPEN on tape (device 1) also
418
+ returns C=1 with A=0 if RUN/STOP is pressed while it waits for
419
+ PLAY/RECORD or during the header search or write (`$F399`/`$F3B8` ->
420
+ `$F3D4`); the serial OPEN path has no STOP check (ROM bytes).
421
+
422
+ ### $FFC3 — CLOSE — Close a logical file
423
+
424
+ **Input:** A = logical file number
425
+ **Output:** C=0 on success; C=1 on error, A = error code
426
+ **Affects:** A, X, Y, C
427
+ **Clobbers zero page:** $01, $90-$96, $98-$9C, $9E-$B1, $B4-$B6, $B8-$BA, $BD-$C2, $C5-$C7, $C9, $CB, $CD-$F6, $F8, $FA (may; ROM walk from $FFC3, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
428
+ **Clobbers zero page:** $95, $98, $A5, $B8-$BA (must; VICE x64sc store trace, CLOSE of that file)
429
+ **Pairs with:** OPEN, CLRCHN
430
+ **Description:** Closes the named logical file. For IEC devices, sends
431
+ LISTEN + close-secondary (`$E0 | sec`), then UNLISTEN. The KERNAL
432
+ removes the entry from its open-file table. Calling CLOSE on a
433
+ logical file number that isn't open returns silently (no error). If
434
+ the file is currently the active input or output channel, CLOSE does
435
+ *not* reset the channel — call CLRCHN first.
436
+
437
+ ### $FFC6 — CHKIN — Redirect input to logical file
438
+
439
+ **Input:** X = logical file number (must already be OPEN)
440
+ **Output:** C=0 on success; C=1 on error, A = error code
441
+ **Affects:** A, X, C
442
+ **Clobbers zero page:** $01, $90-$96, $99-$9C, $9E-$B1, $B4-$B6, $B8-$BA, $BD-$C2, $C5-$C7, $C9, $CB, $CD-$F6 (may; ROM walk from $FFC6, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
443
+ **Clobbers zero page:** $90, $95, $99, $A5, $B8-$BA (must; VICE x64sc store trace, CHKIN on that file)
444
+ **Pairs with:** OPEN, CHRIN, GETIN, CLRCHN
445
+ **Description:** Tells the KERNAL that subsequent CHRIN / GETIN calls
446
+ should read from the named logical file rather than the keyboard. For
447
+ IEC devices, sends TALK + secondary (`$60 | sec`). Errors: 3=file not
448
+ open; 6=not input file, raised only for a tape file whose secondary
449
+ address is not 0 (opened for write; `$F22A-$F230` compares the stored SA
450
+ with `$60`). CHKIN does not check direction on a serial device: on a
451
+ disk channel opened `,S,R` it returns C=0 (measured in VICE x64sc).
452
+ There is a device-not-present exit (5) at `$F24D` but it is not
453
+ reachable for a missing serial device: TKSA falls through into the bus
454
+ turnaround at `$EDCC`, whose wait for the talker's clock at `$EDD6` has
455
+ no timeout, so CHKIN on a serial device that does not answer hangs the
456
+ machine rather than returning (measured in VICE x64sc: an
457
+ open-file-table entry for device 9 hung CHKIN both on an empty bus and
458
+ with a 1541 on device 8). An earlier version of this entry said 6 meant
459
+ the device was opened for write; that is the tape rule only.
460
+
461
+ ### $FFC9 — CHKOUT — Redirect output to logical file
462
+
463
+ **Input:** X = logical file number (must already be OPEN)
464
+ **Output:** C=0 on success; C=1 on error, A = error code
465
+ **Affects:** A, X, C
466
+ **Clobbers zero page:** $01, $90-$96, $99-$9C, $9E-$B1, $B4-$B6, $B8-$BA, $BD-$C2, $C5-$C7, $C9, $CB, $CD-$F6 (may; ROM walk from $FFC9, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
467
+ **Pairs with:** OPEN, CHROUT, CLRCHN
468
+ **Description:** Tells the KERNAL that subsequent CHROUT calls should
469
+ write to the named logical file rather than the screen. For IEC
470
+ devices, sends LISTEN + secondary (`$60 | sec`). Errors: 3=file not
471
+ open; 5=device not present (no serial device pulled DATA low in answer
472
+ to ATN during LISTEN/SECOND, `$ED40-$ED47`, ST bit 7; measured C=1, A=5
473
+ in VICE x64sc both on an empty bus and with another drive present);
474
+ 7=not output file, raised for the keyboard (device 0) and for a tape
475
+ file opened with secondary address 0 (read). Disk channels are not
476
+ direction-checked: CHKOUT on a channel opened `,S,R` returns C=0 (A=8),
477
+ the bytes you CHROUT afterwards are accepted, and with the 1541-II DOS
478
+ in VICE the drive's error channel still read 00 afterwards, so nothing
479
+ reports the mistake. An earlier version said 7 meant the device was
480
+ opened for read; that is the tape rule only.
481
+
482
+ ### $FFCC — CLRCHN — Reset default I/O channels
483
+
484
+ **Input:** None
485
+ **Output:** None
486
+ **Affects:** A, X
487
+ **Clobbers zero page:** $90, $94-$95, $99-$9A, $A3, $A5 (may; ROM walk from $FFCC, power-on vectors)
488
+ **Clobbers zero page:** $95, $99-$9A, $A5 (must; VICE x64sc store trace, CLRCHN with that file as input)
489
+ **Pairs with:** CHKIN, CHKOUT, CLOSE
490
+ **Description:** Resets the current input channel to the keyboard
491
+ (device 0) and the current output channel to the screen (device 3).
492
+ If a CHKOUT had redirected output to an IEC device, CLRCHN sends
493
+ UNLISTEN; if a CHKIN had redirected input to an IEC device, CLRCHN
494
+ sends UNTALK. It does *not* close any logical file; the files remain
495
+ open and can be re-selected with another CHKIN/CHKOUT. Always call
496
+ CLRCHN between operations on different files.
497
+
498
+ ### $FFE7 — CLALL — Close all logical files
499
+
500
+ **Input:** None
501
+ **Output:** None
502
+ **Affects:** A, X
503
+ **Clobbers zero page:** $90, $94-$95, $98-$9A, $A3, $A5 (may; ROM walk from $FFE7, power-on vectors)
504
+ **Pairs with:** CLRCHN
505
+ **Description:** Closes all open logical files at once by zeroing the
506
+ KERNAL's open-file count (`$0098`). This does *not* send proper CLOSE
507
+ commands to IEC devices — it just discards the KERNAL's open-file
508
+ table. CLALL also calls CLRCHN, so default channels are reset. Use
509
+ CLALL when you need to reset I/O state from an unknown starting
510
+ point (e.g. in an error handler), and accept that any disk-side state
511
+ (open relative-file channels with dirty buffers) will be left
512
+ dangling until you UNLISTEN. The jump-table entry for CLALL is at
513
+ `$FFE7`, despite its conceptual pair with CLOSE.
514
+
515
+ ### $FFD5 — LOAD — Load or verify a file
516
+
517
+ **Input:** A = 0 to load, 1 to verify; X = low byte of start address (used only if SETLFS Y=0); Y = high byte of start address; SETLFS + SETNAM state required
518
+ **Output:** C=0 on success, X+Y = end-address+1 (one past the last byte loaded); C=1 on error, A = error code
519
+ **Affects:** A, X, Y, C
520
+ **Clobbers zero page:** $01, $90-$96, $99-$9C, $9E-$B1, $B4-$B6, $B9, $BD-$C7, $C9, $CB, $CD-$F6 (may; ROM walk from $FFD5, power-on vectors; also stores through ($AC), ($AE), ($B2), ($D1), ($F3), ($F9))
521
+ **Clobbers zero page:** $90, $93-$95, $A3-$A5, $AE-$AF, $B9, $C3-$C4 (must; VICE x64sc store trace, LOAD of a 3-block PRG from drive 8 to its own address)
522
+ **Pairs with:** SETLFS, SETNAM
523
+ **Description:** Loads a file from the device specified by SETLFS into
524
+ RAM. If the secondary address from SETLFS is 0, the file is loaded at
525
+ the address in X/Y (passed in by the caller). If secondary is 1
526
+ (non-zero), the file's first two bytes are used as the load address
527
+ (this is how `LOAD "FILE",8,1` works in BASIC). On the C64 the load
528
+ goes into RAM even where BASIC or KERNAL ROM is mapped: the serial path
529
+ stores each byte with `STA ($AE),Y` through the zero-page pointer
530
+ `$AE/$AF` (EAL/EAH, the store at `$F51C`; the tape path stores through
531
+ `$AC/$AD` at `$FB41`), and a 6510 write to a ROM-mapped address always
532
+ lands in the RAM beneath. An earlier version of this page said the
533
+ pointer was in page 1 — that is the stack. The rule does *not* extend
534
+ to `$D000-$DFFF`: LOAD never touches `$01`, so it runs with I/O mapped
535
+ in, and a file whose load address falls there is written into the
536
+ VIC/SID/CIA registers or colour RAM while the RAM underneath is left
537
+ untouched (measured in VICE x64sc: a `,8,1` load of two bytes to
538
+ `$D020` set the border and background registers and left the RAM
539
+ beneath at its prefill). You cannot bank I/O out around the call — the
540
+ serial routines drive the IEC bus through CIA2 at `$DD00` and the tape
541
+ routines time pulses through CIA1 — so LOAD such data elsewhere and
542
+ copy it under I/O yourself with interrupts disabled. Loading straight
543
+ into colour RAM at `$D800` is the one case where writing the chips is
544
+ what you want. After a successful load, X/Y hold the address
545
+ immediately *past* the last byte loaded. With A=1, LOAD compares the
546
+ file against memory instead of writing — sets the status byte's "verify
547
+ mismatch" bit on differences. Errors: 4=file not found, 5=device not
548
+ present, 8=missing filename, 9=illegal device. A C=1 return with A=0 is
549
+ none of these: RUN/STOP aborted the transfer. The serial loop calls
550
+ STOP before every byte (`$F4F9`); the tape path polls it while waiting
551
+ for PLAY and throughout the IRQ-driven block transfer, so pressing STOP
552
+ at the PRESS PLAY prompt returns the same way. The KERNAL has already
553
+ closed the serial channel or stopped the tape motor. BASIC reports this
554
+ return as ?BREAK ERROR; Commodore's own KERNAL error table numbers it
555
+ 0, "routine terminated by the STOP key" (Programmer's Reference Guide,
556
+ not verified here). Test A=0 before indexing an error-message table.
557
+ (Measured in VICE x64sc 3.10: LOAD from device 8 with STOP forced true
558
+ on its 10th poll returned A=`$00`, C=1 after nine bytes; ROM bytes
559
+ `$F4F9`/`$F633`.)
560
+
561
+ ### $FFD8 — SAVE — Save memory to file
562
+
563
+ **Input:** A = zero-page pointer to start address (e.g. A=`$2B` means start = ($2B/$2C) ); X = low byte of end-address+1; Y = high byte of end-address+1; SETLFS + SETNAM state required
564
+ **Output:** C=0 on success; C=1 on error, A = error code
565
+ **Affects:** A, X, Y, C
566
+ **Clobbers zero page:** $01, $90-$96, $99-$9C, $9E-$B1, $B4-$B6, $B9, $BD-$C2, $C5-$C7, $C9, $CB, $CD-$F6 (may; ROM walk from $FFD8, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
567
+ **Pairs with:** SETLFS, SETNAM
568
+ **Description:** Saves the memory range [start, end+1) to the device.
569
+ The start address is read indirectly through the zero-page byte
570
+ pointed to by A (the BASIC start-of-program pointer at `$2B/$2C`
571
+ is the conventional value, which is why BASIC `SAVE` saves the
572
+ current program). The end-address+1 is passed directly in X/Y.
573
+ On the serial bus the first two bytes sent are the start address (low,
574
+ high), then the data — the format LOAD expects. On tape the addresses
575
+ are not in the data stream at all: the KERNAL writes a separate
576
+ 192-byte header block first (`$F76A`: type byte — 1 relocatable, 3
577
+ non-relocatable when the secondary address has bit 0 set — then start
578
+ address, end-address+1, and the filename, space-padded), and the data
579
+ block that follows is the raw memory bytes with no address prefix. An
580
+ earlier version of this page said both tape and serial began with the
581
+ load address; only serial does (KERNAL 901227-03, `$F617-$F621` vs
582
+ `$F76A`/`$F867`). Errors: 5=device not present, 8=missing filename,
583
+ 9=illegal device. As with LOAD, C=1 with A=0 means RUN/STOP aborted the
584
+ transfer — polled before every byte on the serial bus (`$F62E`), and
585
+ while waiting for RECORD/PLAY and during the block write on tape — not
586
+ an I/O error; BASIC reports it as ?BREAK ERROR (ROM bytes
587
+ `$F62E`/`$F633`, `$F8D0`).
588
+
589
+ ## Character I/O
590
+
591
+ The C64's character-I/O layer has three entry points: CHROUT for writing,
592
+ CHRIN for blocking reads, and GETIN for non-blocking reads. All three
593
+ indirect through RAM vectors at `$0326` (BSOUT), `$0324` (CHRIN), and
594
+ `$032A` (GETIN) — patching those vectors hooks every character that
595
+ flows through the KERNAL.
596
+
597
+ ### $FFD2 — CHROUT — Output a character
598
+
599
+ **Input:** A = PETSCII byte to print
600
+ **Output:** None
601
+ **Affects:** A (preserved on success), C
602
+ **Clobbers zero page:** $01, $90-$96, $99-$9C, $9E-$B1, $B4-$B6, $BD-$C2, $C5-$C7, $C9, $CB, $CD-$F6 (may; ROM walk from $FFD2, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
603
+ **Clobbers zero page:** $AC-$AF, $C7, $C9, $CD, $D0-$F4 (must; VICE x64sc store trace, CHROUT printing 33 lines, 30 of them 50 characters, so the screen scrolls)
604
+ **Pairs with:** CHKOUT, CLRCHN
605
+ **Description:** Writes one byte to the current output channel (screen
606
+ by default; a logical file if CHKOUT was called). The byte is
607
+ interpreted as PETSCII: printable codes (`$20-$5F`, `$60-$7F`,
608
+ `$A0-$FF`) display the corresponding character; control codes do
609
+ their named action — `$0D` = carriage return, `$11` = cursor down,
610
+ `$13` = home, `$14` = delete, `$93` = clear screen, `$05/$1C/$1E/$1F/$81/$90/$95/$9F` etc.
611
+ = color changes, `$0E` = lower/upper case, `$8E` = upper/graphics
612
+ case. When writing to the screen, CHROUT *does* modify VIC-II state:
613
+ PETSCII `$0E` and `$8E` write to `$D018` to switch the character
614
+ ROM source between charset 1 and charset 2; color changes write to
615
+ the current-color byte at `$0286` (page 2, not zero page — earlier
616
+ text called it zero-page; the KERNAL stores it with an absolute
617
+ `STX $0286` at `$E8D6`, measured in VICE x64sc: CHROUT `$1C` leaves
618
+ `$0286` = 2). To suppress these side
619
+ effects, write directly to screen RAM (`$0400`) and color RAM
620
+ (`$D800`) instead. A and the carry flag are conventionally preserved
621
+ on success.
622
+
623
+ ### $FFCF — CHRIN — Read a character (blocking)
624
+
625
+ **Input:** None (current input channel must be set; default is keyboard)
626
+ **Output:** A = PETSCII byte read; C=0 on success, C=1 on error
627
+ **Affects:** A, X, Y, C
628
+ **Clobbers zero page:** $01, $90-$97, $99-$9C, $9E-$B1, $B4-$B6, $BD-$C2, $C5-$F6 (may; ROM walk from $FFCF, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
629
+ **Clobbers zero page:** $A4-$A5 (must; VICE x64sc store trace, 20 CHRIN calls reading that file)
630
+ **Clobbers zero page:** $C6, $C8-$CA, $CC-$CD, $D0, $D3-$D4, $D7, $F3-$F4 (must; VICE x64sc store trace, CHRIN from the keyboard reading a 2-character line)
631
+ **Pairs with:** CHKIN, CLRCHN, READST
632
+ **Description:** Reads one PETSCII byte from the current input
633
+ channel. With the default keyboard channel, CHRIN runs the screen
634
+ editor: it blinks the cursor, waits for the user to type a line and
635
+ press RETURN, then returns characters one at a time until the
636
+ RETURN (`$0D`) is delivered. This is the BASIC `INPUT` mechanism.
637
+ With an IEC device active (after CHKIN), CHRIN reads one byte from
638
+ the bus via IECIN, sets the status byte on EOI or error, and returns.
639
+ With tape, CHRIN reads one byte from the tape buffer. Use READST
640
+ after each CHRIN to check end-of-file: status byte `$40` indicates
641
+ EOI (last byte of file just delivered).
642
+
643
+ ### $FFE4 — GETIN — Get a character (non-blocking)
644
+
645
+ A line-entry loop on top of GETIN (echo, DEL, RETURN, length cap, blinking
646
+ cursor) is `text_input_line` in `../techniques/text.md`.
647
+
648
+ **Input:** None
649
+ **Output:** A = PETSCII byte (0 if no byte available); C=0 on success
650
+ **Affects:** A, X, Y, C
651
+ **Clobbers zero page:** $01, $90-$97, $99-$9C, $9E-$B1, $B4-$B6, $BD-$C2, $C5-$C9, $CB-$F6 (may; ROM walk from $FFE4, power-on vectors; also stores through ($AC), ($B2), ($D1), ($F3), ($F9))
652
+ **Clobbers zero page:** $C6 (must; VICE x64sc store trace, 5 GETIN calls on a 4-key buffer)
653
+ **Pairs with:** CHKIN, SCNKEY, STOP
654
+ **Description:** Returns immediately. With the default keyboard
655
+ channel, GETIN reads one byte from the keyboard queue (`$0277-$0280`)
656
+ maintained by the IRQ handler; if the queue is empty, A=0. With an
657
+ IEC device active (after CHKIN), GETIN behaves like CHRIN — it
658
+ *does* block on the bus, because the IEC protocol has no peek-ahead.
659
+ The non-blocking property only applies to the keyboard. GETIN is
660
+ the standard primitive for game loops and any code that must remain
661
+ responsive while polling input.
662
+
663
+ ## Screen and cursor
664
+
665
+ ### $FFED — SCREEN — Read screen dimensions
666
+
667
+ **Input:** None
668
+ **Output:** X = number of columns, Y = number of rows
669
+ **Affects:** X, Y
670
+ **Clobbers zero page:** none (may; ROM walk from $FFED, power-on vectors)
671
+ **Pairs with:** PLOT
672
+ **Description:** Returns the physical dimensions of the screen. On a
673
+ stock C64 this is always X=40, Y=25. The routine exists so that
674
+ programs targeting the whole Commodore-8-bit family (which includes
675
+ 80-column machines like the C128) can adapt to the screen they're
676
+ running on without hard-coding 40x25.
677
+
678
+ ### $FFF0 — PLOT — Get or set cursor position
679
+
680
+ **Input:** C=0 to set: X = row (0-24), Y = column (0-39); C=1 to read: input ignored
681
+ **Output:** With C=1 on entry: X = row, Y = column
682
+ **Affects:** A, X, Y
683
+ **Clobbers zero page:** $D1-$D3, $D5-$D6, $F3-$F4 (may; ROM walk from $FFF0, power-on vectors)
684
+ **Clobbers zero page:** $D1-$D3, $D5-$D6, $F3-$F4 (must; VICE x64sc store trace, PLOT set, then read)
685
+ **Pairs with:** CHROUT, SCREEN
686
+ **Description:** Reads or writes the cursor position used by the
687
+ screen editor. With C=1 (read mode), returns the current row in X
688
+ and current column in Y. With C=0 (write mode), moves the cursor
689
+ to the supplied row/column. PLOT does not draw anything; it just
690
+ positions the cursor so the next CHROUT writes there. Note the
691
+ unusual axis order: X holds the *row* (0-24) and Y holds the
692
+ *column* (0-39), which is the opposite of typical (x,y) plotting
693
+ conventions.
694
+
695
+ ### $FF81 — CINT — Initialize screen editor
696
+
697
+ **Input:** None
698
+ **Output:** None
699
+ **Affects:** A, X, Y
700
+ **Clobbers zero page:** $99-$9A, $CC-$CD, $CF, $D1-$D3, $D5-$D6, $D9-$F4 (may; ROM walk from $FF81, power-on vectors; also stores through ($D1), ($F3))
701
+ **Pairs with:** IOINIT, RAMTAS
702
+ **Description:** Performs the screen-editor portion of the cold-start
703
+ sequence: programs the VIC-II registers for 25-row x 40-column text
704
+ mode, sets screen RAM to `$0400-$07E7` and color RAM to `$D800-$DBE7`,
705
+ fills screen with `$20` (space) and color RAM with the current
706
+ foreground color, sets the cursor to row 0 column 0, sets the
707
+ keyboard-decode vector `$028F/$0290` to `$EB48`, the keyboard-buffer
708
+ size `$0289` = 10, the key-repeat delay `$028C` = 10 and speed `$028B`
709
+ = 4, and the default character colour `$0286` = 14 (light blue), and
710
+ initializes the IRQ-driven keyboard queue. (An earlier version of this
711
+ page said CINT also set the keyboard-table pointer `$F5/$F6`; it does
712
+ not — read from the 901227-03 ROM and confirmed in VICE, `$E518-$E598`
713
+ never stores to `$F5/$F6`. That pointer is written by SCNKEY only on a
714
+ scan that finds a key held: first to `$EB81` at `$EA9D/$EAA1`, then
715
+ re-selected by shift state through the `$028F` vector at `$EB48`; an
716
+ idle scan leaves it alone.) CINT is
717
+ called once at power-on after RAMTAS and IOINIT. Applications can
718
+ re-invoke it to recover from screen corruption (e.g. after a wild
719
+ write to `$D000` zeroed half the VIC registers), but doing so
720
+ overwrites screen and color RAM.
721
+
722
+ ## Stop key
723
+
724
+ ### $FFE1 — STOP — Test the RUN/STOP key
725
+
726
+ **Input:** None
727
+ **Output:** Z=1 if `$91` = `$7F` (the last UDTIM sample of the STOP-key column showed RUN/STOP down with no shift key), Z=0 otherwise. A is always overwritten: when STOP is not detected A = `$91`, the raw column-7 row byte (`$FF` with nothing in that column held; the *Programmer's Reference Guide* documents using it to test the other keys in that column); when STOP is detected A = 0, because CLRCHN's final `LDA #0` is what is left in A. An earlier version of this page said A = `$7F` when pressed and unchanged otherwise, and that STOP latched "since the last call" — measured in VICE x64sc against the ROM bytes, none of that holds: `$FFE1` -> `$F6ED` = `LDA $91 / CMP #$7F / BNE / PHP / JSR CLRCHN / STA $C6 / PLP / RTS`, and the only writer of `$91` in the ROMs is UDTIM at `$F6DA`, so nothing is consumed by reading it.
728
+ **Affects:** A, N, Z, C (the flags are those of `CMP #$7F` against `$91`). When STOP is detected it also calls CLRCHN (`$FFCC`): input device `$99` is reset to 0 and output device `$9A` to 3, with UNTALK/UNLISTEN sent first only if the current device number was above 3, and the keyboard queue is emptied (`$C6` = 0).
729
+ **Clobbers zero page:** $90, $94-$95, $99-$9A, $A3, $A5, $C6 (may; ROM walk from $FFE1, power-on vectors)
730
+ **Clobbers zero page:** none (must; VICE x64sc store trace, STOP with no key down)
731
+ **Pairs with:** GETIN, UDTIM
732
+ **Description:** Reads the STOP-key flag (zero page `$91`, set by UDTIM
733
+ (`$FFEA`), not SCNKEY, to `$7F` when STOP is held in its matrix column)
734
+ and returns Z=1 if STOP is currently pressed. The
735
+ canonical interruptible-loop pattern is:
736
+
737
+ ```asm
738
+ loop: jsr $FFE1 ; STOP
739
+ beq abort ; Z=1 means STOP pressed
740
+ ; ... work ...
741
+ jmp loop
742
+ abort: ; restore state, exit
743
+ rts
744
+ ```
745
+
746
+ STOP reads `$91`, not the keyboard matrix directly, so it depends on
747
+ the IRQ handler running. If the user has disabled IRQs (`SEI` without
748
+ re-enabling), STOP will never return Z=1. To make STOP work in an
749
+ IRQ-disabled context, `JSR $FFEA` (UDTIM) inside the loop (this also
750
+ advances the jiffy clock, so call it at most once per frame if `TI$`
751
+ matters). An earlier version of this page said to call SCNKEY (`$FF9F`)
752
+ here; SCNKEY never writes `$91` — the only store to `$91` in the KERNAL
753
+ is UDTIM's at `$F6DA` — so that advice could not have worked. UDTIM
754
+ reads the STOP column through `$DC01` without selecting it, relying on
755
+ `$DC00` still holding `$7F` as SCNKEY and the KERNAL IRQ leave it. STOP
756
+ also serves a secondary purpose in some KERNAL routines: when called
757
+ from inside disk I/O, it aborts the operation, and when called from
758
+ the cassette routines, it aborts the tape transfer.
759
+
760
+ ## Time and jiffy clock
761
+
762
+ The C64 maintains a 24-bit "jiffy clock" — a counter of `1/60`-second
763
+ ticks (1/50 in PAL territory, despite the name) that wraps every
764
+ 24 hours. The counter lives at `$A0/$A1/$A2` (high/mid/low byte) and
765
+ is incremented by UDTIM, which is called from the IRQ handler every
766
+ jiffy. BASIC exposes the counter via the `TI` (numeric) and `TI$`
767
+ (string `HHMMSS`) reserved variables.
768
+
769
+ ### $FFDB — SETTIM — Set the jiffy clock
770
+
771
+ **Input:** A = jiffy-clock high byte (will go to `$A0`), X = mid byte (`$A1`), Y = low byte (`$A2`)
772
+ **Output:** None
773
+ **Affects:** None (writes `$A0`, `$A1`, `$A2`)
774
+ **Clobbers zero page:** $A0-$A2 (may; ROM walk from $FFDB, power-on vectors)
775
+ **Clobbers zero page:** $A0-$A2 (must; VICE x64sc store trace, SETTIM)
776
+ **Pairs with:** RDTIM, UDTIM
777
+ **Description:** Stores the supplied 24-bit value into the jiffy-clock
778
+ counter. Note the high-byte-first ordering, which is opposite the
779
+ 6502's natural little-endian. This is the routine that BASIC's
780
+ `TI$ = "000000"` translates to. SETTIM disables IRQs while writing
781
+ the three bytes, so the IRQ handler can't see a half-updated value.
782
+
783
+ ### $FFDE — RDTIM — Read the jiffy clock
784
+
785
+ **Input:** None
786
+ **Output:** A = high byte, X = mid byte, Y = low byte
787
+ **Affects:** A, X, Y
788
+ **Clobbers zero page:** $A0-$A2 (may; ROM walk from $FFDE, power-on vectors)
789
+ **Clobbers zero page:** $A0-$A2 (must; VICE x64sc store trace, RDTIM)
790
+ **Pairs with:** SETTIM, UDTIM
791
+ **Description:** Reads the three-byte jiffy counter and returns it in
792
+ A/X/Y (high/mid/low). Disables IRQs during the read so the value is
793
+ atomic. For a millisecond-ish elapsed-time stopwatch, call RDTIM
794
+ twice and subtract; one jiffy = 1/60 s NTSC or 1/50 s PAL.
795
+
796
+ ### $FFEA — UDTIM — Increment jiffy clock + check STOP
797
+
798
+ **Input:** None
799
+ **Output:** None
800
+ **Affects:** A, X
801
+ **Clobbers zero page:** $91, $A0-$A2 (may; ROM walk from $FFEA, power-on vectors)
802
+ **Clobbers zero page:** $91, $A2 (must; VICE x64sc store trace, UDTIM once)
803
+ **Pairs with:** RDTIM, SETTIM, STOP
804
+ **Description:** Increments the 24-bit jiffy-clock counter at
805
+ `$A0/$A1/$A2` by one. Wraps to zero after `$4F1A00` (24 hours of
806
+ 1/60 s ticks) or `$4A6800` (24 hours of 1/50 s ticks; KERNAL uses
807
+ the NTSC constant unless explicitly told otherwise, so PAL drifts
808
+ slightly). UDTIM also reads the keyboard-matrix row that contains
809
+ the STOP key (column at port `$DC00`, row at port `$DC01`) and
810
+ sets `$91` to `$7F` if STOP is pressed, which is what makes the
811
+ STOP routine work. UDTIM is called from the IRQ handler at `$EA31`
812
+ every jiffy — if you replace the IRQ vector with your own code,
813
+ you must `JSR $FFEA` somewhere in your handler or the jiffy clock
814
+ and STOP will freeze.
815
+
816
+ ## Memory
817
+
818
+ The two memory routines manipulate the KERNAL's notion of where RAM
819
+ starts and ends. They share a unified read/write convention:
820
+ **carry-flag = direction**. C=0 means "write the supplied value into
821
+ the KERNAL pointer"; C=1 means "read the current value into the
822
+ return registers".
823
+
824
+ ### $FF99 — MEMTOP — Read or set top of RAM
825
+
826
+ **Input:** C=1 to read (input ignored); C=0 to set, X = low byte, Y = high byte of new top
827
+ **Output:** With C=1: X = low byte, Y = high byte of current top
828
+ **Affects:** A, X, Y
829
+ **Clobbers zero page:** none (may; ROM walk from $FF99, power-on vectors)
830
+ **Pairs with:** MEMBOT, RAMTAS
831
+ **Description:** Reads or writes the KERNAL's top-of-memory pointer,
832
+ stored at `$0283-$0284`. On a stock 38911-byte BASIC system, the
833
+ default value is `$A000` (`$00`/`$A0`) — BASIC strings grow downward
834
+ from this address, and BASIC's free-memory message reports
835
+ `top - vartab`. Lowering MEMTOP reserves a block at the top of RAM
836
+ that BASIC will not touch; for example, setting it to `$C000` keeps
837
+ the 4 KiB at `$C000-$CFFF` free for machine-language code that
838
+ coexists with BASIC. Most programs use this protect-from-BASIC
839
+ mechanism by writing MEMTOP early in their startup. Note that
840
+ lowering MEMTOP does *not* shrink memory available to ML programs;
841
+ it only signals BASIC to stay below the new ceiling.
842
+
843
+ ### $FF9C — MEMBOT — Read or set bottom of RAM
844
+
845
+ **Input:** C=1 to read (input ignored); C=0 to set, X = low byte, Y = high byte of new bottom
846
+ **Output:** With C=1: X = low byte, Y = high byte of current bottom
847
+ **Affects:** A, X, Y
848
+ **Clobbers zero page:** none (may; ROM walk from $FF9C, power-on vectors)
849
+ **Pairs with:** MEMTOP, RAMTAS
850
+ **Description:** Reads or writes the KERNAL's bottom-of-memory
851
+ pointer, stored at `$0281-$0282`. The default value is `$0800`,
852
+ placing the bottom of the BASIC text area at `$0801` (the byte at
853
+ `$0800` is a required zero terminator). Raising MEMBOT reserves a
854
+ block at the bottom of RAM for non-BASIC use. Setting MEMBOT does
855
+ *not* relocate the existing BASIC program; if BASIC has already
856
+ loaded a program, you must move it manually. Application programs
857
+ that need a small RAM scratchpad often raise MEMBOT to `$0900` or
858
+ `$0A00`, leaving the cassette buffer at `$033C-$03FB` free if
859
+ they need more space without disturbing BASIC.
860
+
861
+ ## Vectors
862
+
863
+ The KERNAL exposes 16 indirect RAM vectors at `$0314-$0333` that the
864
+ high-level KERNAL routines and the CPU's IRQ/BRK/NMI hardware
865
+ dispatch through. Patching a vector replaces the corresponding
866
+ KERNAL routine.
867
+
868
+ | Addr | Vector | Purpose |
869
+ |---------------|---------|----------------------------------------------------------|
870
+ | `$0314-$0315` | CINV | IRQ handler (CPU vector indirects here from `$FFFE`) |
871
+ | `$0316-$0317` | CBINV | BRK handler |
872
+ | `$0318-$0319` | NMINV | NMI handler (CPU vector indirects here from `$FFFA`) |
873
+ | `$031A-$031B` | IOPEN | OPEN (called by `$FFC0`) |
874
+ | `$031C-$031D` | ICLOSE | CLOSE (called by `$FFC3`) |
875
+ | `$031E-$031F` | ICHKIN | CHKIN (called by `$FFC6`) |
876
+ | `$0320-$0321` | ICKOUT | CHKOUT (called by `$FFC9`) |
877
+ | `$0322-$0323` | ICLRCH | CLRCHN (called by `$FFCC`) |
878
+ | `$0324-$0325` | IBASIN | CHRIN (called by `$FFCF`) |
879
+ | `$0326-$0327` | IBSOUT | CHROUT (called by `$FFD2`) |
880
+ | `$0328-$0329` | ISTOP | STOP (called by `$FFE1`) |
881
+ | `$032A-$032B` | IGETIN | GETIN (called by `$FFE4`) |
882
+ | `$032C-$032D` | ICLALL | CLALL (called by `$FFE7`) |
883
+ | `$032E-$032F` | USRCMD | User function (unused by KERNAL; BASIC `USR` hooks here) |
884
+ | `$0330-$0331` | ILOAD | LOAD (called by `$FFD5`) |
885
+ | `$0332-$0333` | ISAVE | SAVE (called by `$FFD8`) |
886
+
887
+ The four initialization routines below populate or restore these
888
+ vectors and other related state.
889
+
890
+ ### $FF84 — IOINIT — Initialize I/O chips
891
+
892
+ **Input:** None
893
+ **Output:** None
894
+ **Affects:** A, X, Y
895
+ **Clobbers zero page:** $00-$01 (may; ROM walk from $FF84, power-on vectors)
896
+ **Pairs with:** CINT, RAMTAS, RESTOR
897
+ **Description:** Initializes the two CIA chips (sets DDRs, programs
898
+ Timer A on CIA1 for the 60/50 Hz jiffy IRQ), initializes the SID
899
+ (silences all three voices), sets the IEC bus lines to idle, and
900
+ clears the CIA interrupt-control registers. Called once at power-on
901
+ between RAMTAS and CINT. Application code can call IOINIT to recover
902
+ from chip-state corruption, but doing so will silence any in-progress
903
+ sound and reset the keyboard-scan IRQ rate to the KERNAL default.
904
+
905
+ ### $FF87 — RAMTAS — RAM test and clear
906
+
907
+ **Input:** None
908
+ **Output:** None
909
+ **Affects:** A, X, Y
910
+ **Clobbers zero page:** $02-$FF (may; ROM walk from $FF87, power-on vectors; also stores through ($C1))
911
+ **Pairs with:** IOINIT, CINT, MEMTOP, MEMBOT
912
+ **Description:** Performs the RAM-test portion of cold start: walks
913
+ through each page from `$0800` upward writing `$55` then `$AA` then
914
+ reading back, until it finds a page that doesn't echo back the
915
+ written value, which becomes the top-of-RAM. Zeroes `$0002-$00FF` and
916
+ `$0200-$03FF` (zero page below the stack, the BASIC input buffer and
917
+ the screen-editor work area); the stack page is not cleared apart from
918
+ the two bytes `$0100-$0101` that the `STA $0002,Y` loop spills into.
919
+ Earlier versions of this page said "pages 0/1/2/3" in the table and
920
+ "pages 2 and 3" here; the ROM loop at `$FD50` is `STA $0002,Y / STA
921
+ $0200,Y / STA $0300,Y` with Y 0-255, confirmed in VICE x64sc 3.10. It
922
+ sets MEMTOP to the discovered top and MEMBOT to `$0800`,
923
+ clears the cassette buffer at `$033C-$03FB`. RAMTAS is destructive
924
+ and is normally called only at power-on. Calling it from a running
925
+ program will erase the BASIC input buffer and the open-file table.
926
+
927
+ ### $FF8A — RESTOR — Restore default vectors
928
+
929
+ **Input:** None
930
+ **Output:** None
931
+ **Affects:** A, X, Y
932
+ **Clobbers zero page:** $C3-$C4 (may; ROM walk from $FF8A, power-on vectors; also stores through ($C3))
933
+ **Pairs with:** VECTOR, IOINIT
934
+ **Description:** Copies the KERNAL's default vector table (16 vectors
935
+ starting at `$FD30` in ROM) into the RAM vectors at `$0314-$0333`.
936
+ Use RESTOR after any program that has patched the KERNAL vectors
937
+ (e.g. a custom IRQ handler, a CHROUT wedge, a fast-load patch) to
938
+ return to known defaults. RESTOR is equivalent to calling VECTOR
939
+ with C=0 and a pointer to the ROM default table.
940
+
941
+ ### $FF8D — VECTOR — Read or write all RAM vectors
942
+
943
+ **Input:** C=0 to write vectors from supplied table; C=1 to read vectors into supplied buffer. X = low byte of buffer/table, Y = high byte
944
+ **Output:** With C=1: buffer at (X,Y) filled with 32 bytes (16 vectors)
945
+ **Affects:** A, X, Y
946
+ **Clobbers zero page:** $C3-$C4 (may; ROM walk from $FF8D, power-on vectors; also stores through ($C3))
947
+ **Pairs with:** RESTOR
948
+ **Description:** Block-copies the 16 RAM vectors between memory and
949
+ the KERNAL's vector area at `$0314-$0333`. With C=1, copies *from*
950
+ the vector area into the supplied buffer (snapshot the current vector
951
+ state). With C=0, copies *into* the vector area from the supplied
952
+ table (install a complete vector set in one call). Typical use:
953
+ snapshot with C=1, patch one or two entries, install with C=0 — but
954
+ in practice it's cheaper to just write the two bytes of the one
955
+ vector you care about directly to `$0326`/`$0327` etc.
956
+
957
+ ### $FF90 — SETMSG — Set KERNAL message verbosity
958
+
959
+ **Input:** A = message-control bits (bit 7 = print KERNAL error messages, bit 6 = print KERNAL control messages like "SEARCHING", "LOADING", "PRESS PLAY ON TAPE")
960
+ **Output:** None
961
+ **Affects:** None (stores A at `$009D`)
962
+ **Clobbers zero page:** $90, $9D (may; ROM walk from $FF90, power-on vectors)
963
+ **Pairs with:** OPEN, LOAD, SAVE
964
+ **Description:** Controls whether the KERNAL prints status messages
965
+ to the screen during file operations. A=`$80` (bit 7 only) enables
966
+ error messages but suppresses control messages — useful when an
967
+ application wants to handle "press play on tape" prompts itself.
968
+ A=`$C0` enables both (the default for BASIC). A=`$00` suppresses
969
+ everything — useful for headless tools that drive the KERNAL from
970
+ machine code and don't want stray text appearing on screen.
971
+
972
+ ## IEC bus low-level
973
+
974
+ The eight routines in this section drive the C64's serial IEC bus
975
+ directly, byte by byte. Most application code never calls them
976
+ because OPEN/CLOSE/LOAD/SAVE wrap them, but custom bus protocols
977
+ (fast loaders, IEEE-488 adapters, custom drive commands) use them.
978
+
979
+ The IEC protocol is a five-state sequence:
980
+
981
+ 1. **LISTEN/TALK** sends a device-address byte with the ATN line low
982
+ to announce which device is being addressed.
983
+ 2. **SECOND/TKSA** sends a secondary-address byte (still with ATN
984
+ low) — typically a file-channel number on disk drives, or a
985
+ format command.
986
+ 3. **IECOUT/IECIN** transfers data bytes one at a time, with ATN
987
+ high — IECOUT writes (after LISTEN+SECOND), IECIN reads (after
988
+ TALK+TKSA).
989
+ 4. **UNLSN/UNTLK** ends the transfer by releasing the bus.
990
+
991
+ ### $FFB1 — LISTEN — Send LISTEN command
992
+
993
+ **Input:** A = device number (0-31)
994
+ **Output:** None (status byte set on error)
995
+ **Affects:** A
996
+ **Clobbers zero page:** $90, $94-$95, $A3, $A5 (may; ROM walk from $FFB1, power-on vectors)
997
+ **Pairs with:** SECOND, IECOUT, UNLSN
998
+ **Description:** Sends the LISTEN command byte (`$20 | device`) on
999
+ the IEC bus with the ATN line asserted. After LISTEN, all subsequent
1000
+ data sent via IECOUT goes to the addressed device until UNLSN is
1001
+ sent. The status byte (READST) is set to `$80` if the device does
1002
+ not acknowledge — device not present. Internally the routine
1003
+ manipulates the data line (`$DD00` bit 5), clock line (`$DD00`
1004
+ bit 4), and ATN line (`$DD00` bit 3) of CIA2 port A to drive the
1005
+ serial bus signals; on a real C64 the entire byte takes about
1006
+ 1 ms. Multiple LISTEN commands can be sent in sequence to address
1007
+ multiple listeners simultaneously, but only one device can talk
1008
+ at a time. The device argument is just the device number 0-30;
1009
+ the `$20 | device` encoding is done internally.
1010
+
1011
+ ### $FFB4 — TALK — Send TALK command
1012
+
1013
+ **Input:** A = device number (0-31)
1014
+ **Output:** None (status byte set on error)
1015
+ **Affects:** A
1016
+ **Clobbers zero page:** $90, $94-$95, $A3, $A5 (may; ROM walk from $FFB4, power-on vectors)
1017
+ **Pairs with:** TKSA, IECIN, UNTLK
1018
+ **Description:** Sends the TALK command byte (`$40 | device`) on the
1019
+ IEC bus with ATN asserted. After TALK, the addressed device becomes
1020
+ the bus talker, and the C64 will receive its data via IECIN until
1021
+ UNTLK is sent. Status byte set to `$80` on no-acknowledge. Only one
1022
+ device on the bus can be the talker at any time, so a TALK command
1023
+ implicitly silences any previous talker. After the TALK byte goes
1024
+ out, the routine releases ATN and the device begins to drive the
1025
+ data line; the first IECIN call then reads the first byte the
1026
+ device produces. If you want to read from a specific channel of a
1027
+ disk drive (e.g. the error channel at secondary 15), follow TALK
1028
+ with TKSA.
1029
+
1030
+ ### $FF93 — SECOND — Send secondary address after LISTEN
1031
+
1032
+ **Input:** A = secondary-address byte (typically `$60 | channel` for open channel, `$F0 | channel` to open a file, `$E0 | channel` to close)
1033
+ **Output:** None
1034
+ **Affects:** A
1035
+ **Clobbers zero page:** $90, $95, $A5 (may; ROM walk from $FF93, power-on vectors)
1036
+ **Pairs with:** LISTEN, IECOUT
1037
+ **Description:** Sends a secondary-address byte after a LISTEN. The
1038
+ secondary address selects the file channel or command on the
1039
+ addressed device. For disk drives, secondary `$60 | n` opens
1040
+ channel n for data transfer, `$F0 | n` opens a file (followed by
1041
+ filename bytes via IECOUT), and `$E0 | n` closes channel n. SECOND
1042
+ keeps ATN asserted while sending so the device sees the byte as a
1043
+ command, not data.
1044
+
1045
+ ### $FF96 — TKSA — Send secondary address after TALK
1046
+
1047
+ **Input:** A = secondary-address byte
1048
+ **Output:** None
1049
+ **Affects:** A
1050
+ **Clobbers zero page:** $90, $95, $A5 (may; ROM walk from $FF96, power-on vectors)
1051
+ **Pairs with:** TALK, IECIN
1052
+ **Description:** Sends a secondary-address byte after a TALK, same
1053
+ encoding as SECOND. Use TKSA to tell the talking device which
1054
+ channel to read from. Like SECOND, keeps ATN asserted during the
1055
+ byte to mark it as a command.
1056
+
1057
+ ### $FFA5 — IECIN — Receive one byte from serial bus
1058
+
1059
+ **Input:** None (TALK + TKSA must have been called)
1060
+ **Output:** A = byte received; status byte updated on EOI / error
1061
+ **Affects:** A
1062
+ **Clobbers zero page:** $90, $95, $A4-$A5 (may; ROM walk from $FFA5, power-on vectors)
1063
+ **Pairs with:** TALK, TKSA, UNTLK, READST
1064
+ **Description:** Clocks one byte off the IEC bus from the currently
1065
+ talking device. On the last byte of a transfer (EOI), the device
1066
+ holds the data line low for an extended period before the eighth
1067
+ bit; the KERNAL detects this and sets status byte bit 6 (`$40`).
1068
+ Call READST after each IECIN to detect EOI and error conditions
1069
+ (`$01` = timeout writing, `$02` = timeout reading, `$80` = device
1070
+ not present). Historical name: ACPTR.
1071
+
1072
+ ### $FFA8 — IECOUT — Send one byte to serial bus
1073
+
1074
+ **Input:** A = byte to send
1075
+ **Output:** None (status byte updated on error)
1076
+ **Affects:** None (A preserved)
1077
+ **Clobbers zero page:** $90, $94-$95, $A5 (may; ROM walk from $FFA8, power-on vectors)
1078
+ **Pairs with:** LISTEN, SECOND, UNLSN, READST
1079
+ **Description:** Sends one byte to the currently listening device.
1080
+ The byte is clocked out using the C64's bit-banged IEC protocol;
1081
+ total time per byte is about 1 ms on a real drive. Errors
1082
+ (`$01`/`$02`/`$80`) are recorded in the status byte. Historical
1083
+ name: CIOUT.
1084
+
1085
+ ### $FFAB — UNTLK — Send UNTALK
1086
+
1087
+ **Input:** None
1088
+ **Output:** None
1089
+ **Affects:** A
1090
+ **Clobbers zero page:** $90, $94-$95, $A3, $A5 (may; ROM walk from $FFAB, power-on vectors)
1091
+ **Pairs with:** TALK, TKSA, IECIN
1092
+ **Description:** Sends the UNTALK command (`$5F`) with ATN
1093
+ asserted. The current talking device, if any, releases the data
1094
+ line and the bus returns to idle. The command is broadcast, so
1095
+ all listeners and the (single) talker simultaneously hear it; the
1096
+ talker stops talking, the listeners stop listening for that
1097
+ talker. Use UNTLK to end a TALK transaction. Calling UNTLK with
1098
+ no active talker is harmless — the command is sent to all
1099
+ devices but none act on it.
1100
+
1101
+ ### $FFAE — UNLSN — Send UNLISTEN
1102
+
1103
+ **Input:** None
1104
+ **Output:** None
1105
+ **Affects:** A
1106
+ **Clobbers zero page:** $90, $94-$95, $A3, $A5 (may; ROM walk from $FFAE, power-on vectors)
1107
+ **Pairs with:** LISTEN, SECOND, IECOUT
1108
+ **Description:** Sends the UNLISTEN command (`$3F`) with ATN
1109
+ asserted. All bus-listening devices stop receiving data. Use
1110
+ UNLSN to end a LISTEN transaction. On a disk drive, UNLSN with
1111
+ secondary `$F0` (file-open) pending tells the drive to finalize
1112
+ the OPEN — the drive parses the filename it received since
1113
+ LISTEN+SECOND, locates the file, and is ready for subsequent
1114
+ IECIN/IECOUT against the opened channel. Without UNLSN the
1115
+ drive doesn't know the filename is complete and won't open the
1116
+ file. This is why OPEN always ends with UNLSN even though no
1117
+ filename byte follows.
1118
+
1119
+ ### $FFB7 — READST — Read serial bus status
1120
+
1121
+ **Input:** None
1122
+ **Output:** A = status byte (alias for the zero-page `$90` ST byte)
1123
+ **Affects:** A
1124
+ **Clobbers zero page:** $90 (may; ROM walk from $FFB7, power-on vectors)
1125
+ **Clobbers zero page:** $90 (must; VICE x64sc store trace, READST)
1126
+ **Pairs with:** IECIN, IECOUT, CHRIN, CHROUT
1127
+ **Description:** Reads and clears the KERNAL's serial-bus status
1128
+ byte. Bit values:
1129
+
1130
+ | Bit | Hex | Meaning (cassette) | Meaning (serial bus) |
1131
+ |-----|-------|---------------------------------|-----------------------------------|
1132
+ | 0 | `$01` | Unused | Timeout while writing |
1133
+ | 1 | `$02` | Unused | Timeout while reading |
1134
+ | 2 | `$04` | Short block (tape) | Unused |
1135
+ | 3 | `$08` | Long block (tape) | Unused |
1136
+ | 4 | `$10` | Unrecoverable read error (tape) | Verify error |
1137
+ | 5 | `$20` | Checksum error (tape) | Unused |
1138
+ | 6 | `$40` | End of file (tape) | EOI on input (last byte of file) |
1139
+ | 7 | `$80` | End of tape | Device not present |
1140
+
1141
+ A status of `$00` after a CHRIN means "more data available". `$40`
1142
+ after a CHRIN means "the byte just delivered was the last byte of
1143
+ the file" (the file is now at EOF). `$80` indicates a missing
1144
+ device and is set by LISTEN/TALK if no device acknowledges.
1145
+
1146
+ ### $FF9F — SCNKEY — Scan keyboard
1147
+
1148
+ **Input:** None
1149
+ **Output:** None (keyboard queue at `$0277-$0280` may gain new entries)
1150
+ **Affects:** A, X, Y
1151
+ **Clobbers zero page:** $C5-$C6, $CB, $F5-$F6 (may; ROM walk from $FF9F, power-on vectors)
1152
+ **Clobbers zero page:** $C5, $CB (must; VICE x64sc store trace, SCNKEY with no key down)
1153
+ **Pairs with:** GETIN, STOP
1154
+ **Description:** Scans the 8x8 keyboard matrix via CIA1 ports A
1155
+ and B, decodes the pressed key against the current keyboard table
1156
+ (four tables: unshifted, shifted, Commodore-shifted, control), and
1157
+ pushes the resulting PETSCII byte into the keyboard queue. SCNKEY
1158
+ also tracks the shift/Commodore/CTRL state at `$028D`. It does not
1159
+ touch the STOP flag at `$91` (an earlier version of this page said it
1160
+ did; the only store to `$91` in the KERNAL is in UDTIM at `$F6DA`).
1161
+ SCNKEY is called from the IRQ handler at `$EA31`, after UDTIM, and
1162
+ exits with `$DC00` = `$7F`, which is the column drive UDTIM's `$DC01`
1163
+ read relies on. An application that disables IRQs must call SCNKEY
1164
+ itself to keep the keyboard queue filling and UDTIM (`$FFEA`) to keep
1165
+ the STOP flag and jiffy clock updating.
1166
+
1167
+ ### $FFA2 — SETTMO — Set IEEE timeout flag
1168
+
1169
+ **Input:** A = timeout flag (bit 7 = enable timeouts)
1170
+ **Output:** None
1171
+ **Affects:** None
1172
+ **Clobbers zero page:** none (may; ROM walk from $FFA2, power-on vectors)
1173
+ **Pairs with:** READST
1174
+ **Description:** On the C64 this routine is a no-op. It exists for
1175
+ source compatibility with the PET, where it controlled the timeout
1176
+ behavior of the IEEE-488 bus. The C64's IEC serial bus has its own
1177
+ fixed timeout logic that cannot be disabled. Code can call SETTMO
1178
+ without effect; the routine just returns. The C64 ROM does contain
1179
+ a SETTMO entry point for compatibility with code originally written
1180
+ for the VIC-1541 IEEE adapter and PET — on those machines the
1181
+ input A controls whether the bus driver times out after about 64 ms
1182
+ or waits forever. On a stock C64 with only IEC devices, the
1183
+ timeouts are wired in: the KERNAL's IEC driver gives up after
1184
+ about 64 ms of clock-low time and sets the status byte to `$02`
1185
+ (read timeout) or `$01` (write timeout). Reading READST after a
1186
+ suspicious IECIN/IECOUT is the C64 substitute for SETTMO.
1187
+
1188
+ ### $FFF3 — IOBASE — Get I/O block base address
1189
+
1190
+ **Input:** None
1191
+ **Output:** X = low byte, Y = high byte of I/O base (always `$00`/`$DC` on C64)
1192
+ **Affects:** X, Y
1193
+ **Clobbers zero page:** none (may; ROM walk from $FFF3, power-on vectors)
1194
+ **Pairs with:** SCREEN
1195
+ **Description:** Returns the base address of the I/O block, which
1196
+ on the C64 is always `$DC00` (the start of CIA1). Self-relocating
1197
+ code that wants to address CIAs / SID / VIC-II by offset from
1198
+ this base can use IOBASE so that it remains portable to other
1199
+ Commodore machines where the I/O block lives elsewhere. On the
1200
+ C64 the value is fixed in ROM and never changes. The original
1201
+ intent was to let one program binary run on C64, C128, B-series,
1202
+ and Plus/4 by replacing all `LDA $DC00` constants with
1203
+ `LDY ($IOBASE_VEC),Y` indirect-Y addressing through an
1204
+ IOBASE-derived pointer. In practice almost no C64 software
1205
+ took advantage — the I/O addresses are so deeply hard-coded
1206
+ in tutorials and listings that compatibility was lost long
1207
+ before IOBASE was needed.
1208
+
1209
+ ## Pairs and contracts
1210
+
1211
+ KERNAL routines compose into stateful sequences. The graph below
1212
+ captures which calls must precede which.
1213
+
1214
+ ```
1215
+ SETLFS ─┐
1216
+ ├─► OPEN ─► CHKIN ─► CHRIN ─┐
1217
+ SETNAM ─┘ │ CHKOUT ─► CHROUT ─┤
1218
+ │ │
1219
+ ├─► LOAD │
1220
+ ├─► SAVE │
1221
+ └─► CLOSE ◄── CLRCHN ─────┘
1222
+ ```
1223
+
1224
+ ### File-I/O setup chain
1225
+
1226
+ | Order | Routine | Why |
1227
+ |-------|---------|-----------------------------------------------------------|
1228
+ | 1 | SETLFS | Stores logical file / device / secondary in zero page |
1229
+ | 2 | SETNAM | Stores filename pointer + length in zero page |
1230
+ | 3 | OPEN | Reads both, sends LISTEN+SECOND+filename+UNLISTEN |
1231
+ | 4 | CHKIN | (or CHKOUT) — selects input (or output) channel |
1232
+ | 5 | CHRIN | (or CHROUT, GETIN) — transfers data |
1233
+ | 6 | CLRCHN | Releases channel (sends UNTALK or UNLISTEN) |
1234
+ | 7 | CLOSE | Tells device to close its end of the channel |
1235
+
1236
+ Each step is independent — the KERNAL doesn't enforce ordering — but
1237
+ skipping or reordering creates predictable bugs:
1238
+
1239
+ - **Skipping SETLFS before OPEN** uses stale parameters from the
1240
+ previous SETLFS call. Calling OPEN twice in a row will reuse the
1241
+ last set of parameters, which is usually wrong.
1242
+ - **Skipping SETNAM** is legal for some devices (printer, screen,
1243
+ tape with no name) but produces error 8 ("missing filename") for
1244
+ disk OPENs that need a filename.
1245
+ - **CHRIN/CHROUT without CHKIN/CHKOUT** acts on the default channel
1246
+ (keyboard in, screen out). This is sometimes intentional, but it's
1247
+ a frequent bug when programmers forget that CHKIN/CHKOUT are
1248
+ required to redirect.
1249
+ - **CLOSE without CLRCHN** leaves the channel selected as the active
1250
+ input or output. The next CHRIN/CHROUT will operate on a
1251
+ freshly-closed file and fail. Always: CLRCHN, then CLOSE.
1252
+
1253
+ ### IEC raw-bus chain
1254
+
1255
+ The low-level routines parallel the high-level ones:
1256
+
1257
+ | To write to a device | To read from a device |
1258
+ |---------------------------|---------------------------|
1259
+ | 1. LISTEN (with device #) | 1. TALK (with device #) |
1260
+ | 2. SECOND (with secondary)| 2. TKSA (with secondary) |
1261
+ | 3. IECOUT (repeatedly) | 3. IECIN (repeatedly) |
1262
+ | 4. UNLSN | 4. UNTLK |
1263
+
1264
+ LISTEN/TALK can be combined: send LISTEN to one device, then send a
1265
+ sequence of SECOND+IECOUT to write commands to it, then LISTEN to a
1266
+ second device for further writes, then UNLSN at the end to release
1267
+ all listeners. The bus supports up to 31 devices simultaneously
1268
+ (addresses 0-30; address 31 is reserved).
1269
+
1270
+ ### Vector-patching chain
1271
+
1272
+ To install a custom IRQ handler with vector save/restore:
1273
+
1274
+ ```asm
1275
+ sei
1276
+ lda $0314 ; save current IRQ vector
1277
+ sta old_irq_lo
1278
+ lda $0315
1279
+ sta old_irq_hi
1280
+ lda #<my_irq
1281
+ sta $0314
1282
+ lda #>my_irq
1283
+ sta $0315
1284
+ cli
1285
+ ; ... run ...
1286
+ sei
1287
+ lda old_irq_lo
1288
+ sta $0314
1289
+ lda old_irq_hi
1290
+ sta $0315
1291
+ cli
1292
+ ```
1293
+
1294
+ Or, equivalently, before exit:
1295
+
1296
+ ```asm
1297
+ jsr $FF8A ; RESTOR — restores all 16 vectors at once
1298
+ ```
1299
+
1300
+ RESTOR is the brute-force option. If your program patched only the
1301
+ IRQ vector, RESTOR is fine. If something else (e.g. a wedge that
1302
+ patched IBSOUT to filter screen output) was already running, RESTOR
1303
+ will erase its patches too.
1304
+
1305
+ ### Time-clock pairing
1306
+
1307
+ `SETTIM`/`RDTIM` operate on the three-byte counter that `UDTIM`
1308
+ increments. Programs that disable IRQs and then read the jiffy clock
1309
+ will see a frozen value; either re-enable IRQs or call UDTIM manually
1310
+ inside the critical section to keep the counter advancing.
1311
+
1312
+ ### Cold-start / warm-start sequence
1313
+
1314
+ The KERNAL's reset vector (`$FFFC`) points at the cold-start routine
1315
+ that runs this sequence:
1316
+
1317
+ ```
1318
+ RESET → STX $D016 ; harmless write to anchor the stack
1319
+ → JSR $FDA3 (IOINIT)
1320
+ → JSR $FD50 (RAMTAS)
1321
+ → JSR $FD15 (RESTOR)
1322
+ → JSR $FF5B (CINT)
1323
+ → JMP ($A000) ; cold-start BASIC
1324
+ ```
1325
+
1326
+ In jump-table terms: IOINIT → RAMTAS → RESTOR → CINT, then jump to
1327
+ BASIC. An application that wants to restart "from scratch" without
1328
+ a hard reset can call the same four routines (in the same order)
1329
+ followed by `JMP $A000` (or its own entry point).
1330
+
1331
+ ### Status-byte interaction with file I/O
1332
+
1333
+ Every file-I/O routine that touches the IEC or cassette bus updates
1334
+ the status byte at `$90`. Reading the status byte via READST is the
1335
+ *only* reliable way to detect end-of-file and bus errors — the
1336
+ carry flag returned from CHRIN/CHROUT/IECIN/IECOUT signals only
1337
+ "could not complete this operation", not "end of file".
1338
+
1339
+ The canonical end-of-file read loop:
1340
+
1341
+ ```asm
1342
+ read_loop:
1343
+ jsr $FFCF ; CHRIN
1344
+ sta buffer,y
1345
+ iny
1346
+ jsr $FFB7 ; READST
1347
+ beq read_loop ; ST == 0 means more data
1348
+ and #$40 ; EOI bit?
1349
+ bne ok ; ok, file just ended
1350
+ ; non-zero ST without EOI bit = real error
1351
+ jmp error
1352
+ ok: rts
1353
+ ```
1354
+
1355
+ A common mistake is to use `bcc` after CHRIN instead of READST.
1356
+ CHRIN clears carry on a successful read *including the last byte
1357
+ of the file*, then sets EOI in the status byte. The carry doesn't
1358
+ become set until the byte *after* EOI, by which point you've
1359
+ already read past end-of-file. Always test READST, not carry, for
1360
+ end-of-file.
1361
+
1362
+ ### Worked examples
1363
+
1364
+ These are complete, runnable snippets that show the canonical
1365
+ KERNAL call sequences. All examples assume the assembler's
1366
+ default segment starts somewhere safe (e.g. `$0801` with a BASIC
1367
+ SYS stub, or `$C000` for a standalone ML program).
1368
+
1369
+ #### Reading the disk error channel
1370
+
1371
+ As a technique with a measured round-trip: `error_channel_check` in `../techniques/file-io.md` and `../recipes/kickassembler/file-io-roundtrip.md`.
1372
+
1373
+ A common "is my disk command happy?" check — open the command
1374
+ channel (secondary 15), read the response into a buffer until
1375
+ end-of-file or CR, then close.
1376
+
1377
+ ```asm
1378
+ read_error:
1379
+ lda #15 ; logical file
1380
+ ldx #8 ; device
1381
+ ldy #15 ; secondary = command channel
1382
+ jsr $FFBA ; SETLFS
1383
+ lda #0 ; no filename
1384
+ jsr $FFBD ; SETNAM
1385
+ jsr $FFC0 ; OPEN
1386
+ bcs err_open
1387
+
1388
+ ldx #15
1389
+ jsr $FFC6 ; CHKIN
1390
+ bcs err_chkin
1391
+
1392
+ ldy #0
1393
+ @loop: jsr $FFCF ; CHRIN
1394
+ sta buf,y
1395
+ iny
1396
+ jsr $FFB7 ; READST
1397
+ beq @loop
1398
+ ; status non-zero — either EOI or error
1399
+ and #$BF ; mask off EOI bit
1400
+ bne @err
1401
+
1402
+ jsr $FFCC ; CLRCHN
1403
+ lda #15
1404
+ jsr $FFC3 ; CLOSE
1405
+ rts
1406
+
1407
+ @err: jsr $FFCC
1408
+ lda #15
1409
+ jsr $FFC3
1410
+ sec
1411
+ rts
1412
+ ```
1413
+
1414
+ After this call, `buf` contains the drive status line like
1415
+ "00, OK,00,00" or "21,READ ERROR,18,01" (terminated by CR).
1416
+
1417
+ #### Writing a sequential disk file
1418
+
1419
+ As techniques with a measured round-trip: `kernal_file_write_seq` and `kernal_file_read_seq` in `../techniques/file-io.md`.
1420
+
1421
+ Write a small block to a new disk file. Two SETLFS+SETNAM
1422
+ sequences are used: one to scratch any existing file with the
1423
+ same name (via the command channel), then one to create the
1424
+ new file.
1425
+
1426
+ ```asm
1427
+ ; scratch FILE first
1428
+ lda #15
1429
+ ldx #8
1430
+ ldy #15
1431
+ jsr $FFBA ; SETLFS for command channel
1432
+ lda #scr_end-scr
1433
+ ldx #<scr
1434
+ ldy #>scr
1435
+ jsr $FFBD ; SETNAM (command string)
1436
+ jsr $FFC0 ; OPEN (sends the command)
1437
+ bcs err
1438
+
1439
+ lda #15
1440
+ jsr $FFC3 ; CLOSE command channel
1441
+
1442
+ ; now create FILE for write
1443
+ lda #2 ; logical
1444
+ ldx #8
1445
+ ldy #2 ; secondary = open channel
1446
+ jsr $FFBA ; SETLFS
1447
+ lda #fn_end-fn
1448
+ ldx #<fn
1449
+ ldy #>fn
1450
+ jsr $FFBD ; SETNAM
1451
+ jsr $FFC0 ; OPEN
1452
+ bcs err
1453
+
1454
+ ldx #2
1455
+ jsr $FFC9 ; CHKOUT (redirect output)
1456
+ bcs err
1457
+
1458
+ ldy #0
1459
+ @loop: lda data,y
1460
+ jsr $FFD2 ; CHROUT
1461
+ iny
1462
+ cpy #data_end-data
1463
+ bne @loop
1464
+
1465
+ jsr $FFCC ; CLRCHN
1466
+ lda #2
1467
+ jsr $FFC3 ; CLOSE
1468
+ rts
1469
+
1470
+ err: jsr $FFCC
1471
+ lda #2
1472
+ jsr $FFC3
1473
+ sec
1474
+ rts
1475
+
1476
+ scr: .byte "S0:FILE"
1477
+ scr_end:
1478
+ fn: .byte "FILE,S,W"
1479
+ fn_end:
1480
+ data: .byte "HELLO, WORLD", 13
1481
+ data_end:
1482
+ ```
1483
+
1484
+ #### Custom IRQ handler that calls UDTIM
1485
+
1486
+ Replace the system IRQ handler with one that does custom work
1487
+ each jiffy but still calls UDTIM so the jiffy clock and STOP
1488
+ key keep working.
1489
+
1490
+ ```asm
1491
+ install:
1492
+ sei
1493
+ lda $0314
1494
+ sta old_lo
1495
+ lda $0315
1496
+ sta old_hi
1497
+ lda #<my_irq
1498
+ sta $0314
1499
+ lda #>my_irq
1500
+ sta $0315
1501
+ cli
1502
+ rts
1503
+
1504
+ my_irq:
1505
+ ; ... my custom work, fast ...
1506
+ lda $D019 ; ack VIC IRQs
1507
+ sta $D019
1508
+ jsr $FFEA ; UDTIM — keep jiffy clock + STOP working
1509
+ jmp (old_irq_target)
1510
+ old_irq_target = $EA31 ; default KERNAL IRQ entry, or use stashed vector
1511
+ old_lo: .byte 0
1512
+ old_hi: .byte 0
1513
+ ```
1514
+
1515
+ #### Polling input non-blocking in a game loop
1516
+
1517
+ The canonical game-loop input pattern:
1518
+
1519
+ ```asm
1520
+ game_loop:
1521
+ jsr $FFE4 ; GETIN
1522
+ beq @no_key
1523
+ cmp #$20 ; space?
1524
+ beq do_fire
1525
+ cmp #'A'
1526
+ beq turn_left
1527
+ cmp #'D'
1528
+ beq turn_right
1529
+ @no_key:
1530
+ jsr $FFE1 ; STOP
1531
+ beq quit
1532
+ ; ... per-frame logic ...
1533
+ jmp game_loop
1534
+ quit: rts
1535
+ ```
1536
+
1537
+ GETIN doesn't block on the keyboard, so the loop runs every
1538
+ frame regardless of input. STOP is checked at the end of each
1539
+ iteration. This is the same loop structure BASIC programs use
1540
+ when they alternate between `GET A$` and game logic, except in
1541
+ ML it runs hundreds of times faster.
1542
+
1543
+ #### Reading the jiffy clock for timing
1544
+
1545
+ A simple "wait 30 jiffies" delay using RDTIM:
1546
+
1547
+ ```asm
1548
+ jsr $FFDE ; RDTIM — A=high, X=mid, Y=low
1549
+ sty start_lo
1550
+ ; assume the wait is < 256 jiffies so we ignore mid/high
1551
+ @wait: jsr $FFDE
1552
+ sec
1553
+ tya
1554
+ sbc start_lo
1555
+ cmp #30
1556
+ bcc @wait
1557
+ rts
1558
+ start_lo: .byte 0
1559
+ ```
1560
+
1561
+ For longer waits, store all three RDTIM bytes and do 24-bit
1562
+ subtraction. Note that RDTIM reads atomically (with IRQs briefly
1563
+ disabled), so the three bytes are always consistent.
1564
+
1565
+ ### Pair-with notation
1566
+
1567
+ The `**Pairs with:**` lines on each routine identify routines that
1568
+ typically appear together in correct code. The pairing has three
1569
+ flavors:
1570
+
1571
+ - **Setup pairing** — must call routine X before routine Y for Y
1572
+ to have valid input (e.g. SETLFS pairs with OPEN; LISTEN pairs
1573
+ with IECOUT).
1574
+ - **Cleanup pairing** — must call routine X after routine Y to
1575
+ release state (e.g. CLRCHN pairs with CHKIN/CHKOUT; UNLSN
1576
+ pairs with LISTEN+IECOUT).
1577
+ - **Symmetric pairing** — routines that read and write the same
1578
+ state (e.g. MEMTOP and RAMTAS; SETTIM and RDTIM).
1579
+
1580
+ The graph extractor reads these lines and produces `PAIRS_WITH`
1581
+ edges in the knowledge graph, so a developer asking "what do I
1582
+ need to call before OPEN?" can navigate from OPEN to its
1583
+ SETLFS+SETNAM dependencies in one query.
1584
+
1585
+ ## Pitfalls
1586
+
1587
+ - **CHROUT modifies VIC-II state.** Writing PETSCII `$0E` (charset 2
1588
+ / lower case) or `$8E` (charset 1 / upper-graphics) causes CHROUT
1589
+ to write to `$D018`, changing the character ROM source. If your
1590
+ program has set up a custom bitmap or a charset other than the
1591
+ KERNAL defaults, sending a `$0E` or `$8E` byte will revert it.
1592
+ Color-code PETSCII bytes (`$05`, `$1C`-`$1F`, `$81`, `$90`-`$9F`)
1593
+ similarly write to the current-color byte at `$0286` (page 2, not
1594
+ zero page) and change the foreground color of subsequent character
1595
+ writes. To
1596
+ send a literal `$0E` to a file (e.g. when dumping binary to disk),
1597
+ use IECOUT directly rather than CHROUT after CHKOUT.
1598
+
1599
+ - **CHKIN/CHKOUT require a prior successful OPEN.** They return
1600
+ C=1 + error code 3 ("file not open") if the logical file isn't
1601
+ in the open-file table. A common pattern bug is to call OPEN,
1602
+ check carry, jump to error on failure, then unconditionally call
1603
+ CHKIN on the (un-opened) logical file — which then fails with
1604
+ the misleading error 3. Always re-check carry after each
1605
+ KERNAL call.
1606
+
1607
+ - **CHKOUT to a disk file opened for read does NOT fail.** Measured
1608
+ in VICE x64sc: `OPEN 2,8,2,"FILE,S,R"` then CHKOUT 2 returns C=0; the
1609
+ bytes are accepted and, with the 1541-II DOS, the error channel still
1610
+ reads 00. Error 7 comes only from the keyboard (device 0) or a tape
1611
+ file opened for read. Use CHKIN for a read channel; nothing will tell
1612
+ you if you do not. (Earlier text said CHKOUT returned error 7 here;
1613
+ it does not.)
1614
+
1615
+ - **CLOSE without CLRCHN leaves a dangling channel.** Subsequent
1616
+ CHRIN/CHROUT will read/write to a closed file's slot. Symptoms:
1617
+ garbage bytes, frozen reads, status byte not updating. The cure
1618
+ is unconditional: `JSR $FFCC` (CLRCHN) immediately after every
1619
+ CHRIN/CHROUT loop, before any CLOSE.
1620
+
1621
+ - **CLALL doesn't tell IEC devices to close.** Unlike CLOSE, CLALL
1622
+ just zeros the KERNAL's open-file table. Disk-side state (channel
1623
+ buffers, dirty relative-file blocks) is left untouched until the
1624
+ device sees an UNLISTEN. After CLALL, if you re-OPEN a file
1625
+ with the same secondary on the same device, the drive may return
1626
+ stale data from the abandoned channel.
1627
+
1628
+ - **LOAD destination is X/Y on the call, but X/Y on return mean
1629
+ end-address.** A programmer who calls LOAD then expects X/Y to
1630
+ still hold the load address will see the value after the last
1631
+ byte loaded — often 65535-ish for a long load that fills memory
1632
+ to the top. The output-X/Y convention is *end-address + 1*, so
1633
+ for a file loaded to `$1000-$1FFF` the call returns X=`$00`,
1634
+ Y=`$20` (i.e. `$2000`).
1635
+
1636
+ - **SAVE end-address is exclusive.** The end-address+1 passed to
1637
+ SAVE in X/Y is the byte after the last one to save. Saving
1638
+ `$1000-$1FFF` requires X=`$00`, Y=`$20`. A common bug is passing
1639
+ the inclusive end-address `$1FFF`, which produces a file one byte
1640
+ short.
1641
+
1642
+ - **SAVE start-address is indirect.** A holds a *zero-page byte
1643
+ number*, not an address. The KERNAL reads two bytes starting at
1644
+ that zero-page byte to get the actual start address. BASIC uses
1645
+ `$2B` because BASIC's start-of-program pointer lives at
1646
+ `$2B/$2C`. To save a custom range from ML, store the start
1647
+ address into two consecutive zero-page bytes (e.g. `$FB/$FC`)
1648
+ and pass A=`$FB`.
1649
+
1650
+ - **STOP depends on the IRQ handler.** Reading `$91` only returns
1651
+ `$7F` if UDTIM (`$FFEA`) has been sampling the STOP column each
1652
+ jiffy — the IRQ handler at `$EA31` calls it before SCNKEY. In an
1653
+ SEI-protected critical section, STOP will never trigger. To make
1654
+ STOP work inside SEI code, `JSR $FFEA` explicitly inside your loop
1655
+ (at most once per frame if `TI$` matters, since it also advances
1656
+ the jiffy clock). An earlier version of this bullet said to call
1657
+ SCNKEY; SCNKEY does not write `$91` — the only store to it in the
1658
+ KERNAL is UDTIM's at `$F6DA`.
1659
+
1660
+ - **UDTIM is required by STOP.** If you install a custom IRQ
1661
+ handler that doesn't `JSR $FFEA`, the jiffy clock will stop and
1662
+ the STOP-key detection will stop working as a side effect (since
1663
+ STOP reads `$91`, which UDTIM updates via the keyboard-row read
1664
+ buried in its code).
1665
+
1666
+ - **PAL machines drift on TI$.** The KERNAL uses the same constants
1667
+ for the jiffy-clock wrap regardless of region. On PAL machines
1668
+ the clock ticks at 50 Hz but wraps at the NTSC-calibrated 24-hour
1669
+ value, so `TI$` slowly drifts behind wall-clock time. For
1670
+ accurate timing on PAL, use a CIA timer A in 50 Hz mode and
1671
+ ignore TI$.
1672
+
1673
+ - **GETIN blocks on IEC.** Only the keyboard channel makes GETIN
1674
+ non-blocking. After a CHKIN to an IEC device, GETIN waits for a
1675
+ byte from the bus just like CHRIN does. If you need a polling
1676
+ read from a disk file, you must implement timeouts yourself
1677
+ using a CIA timer.
1678
+
1679
+ - **SECOND/TKSA encoding is non-obvious.** The secondary-address
1680
+ byte is not just the secondary number — it's `$60 | sec` for an
1681
+ open channel, `$F0 | sec` for open-file, `$E0 | sec` for close.
1682
+ Passing the raw secondary number to SECOND will address the
1683
+ wrong command bits.
1684
+
1685
+ - **IOBASE on the C64 is always `$DC00`.** Code that uses IOBASE
1686
+ to access VIC-II (`$D000-$D02E`) or SID (`$D400-$D41C`) needs to
1687
+ subtract from `$DC00`, not add. The convention exists for C128
1688
+ / PET compatibility where I/O lives at different addresses, but
1689
+ on the C64 the offset arithmetic is non-trivial.
1690
+
1691
+ - **SETTMO is a no-op.** It accepts a parameter and returns. If
1692
+ you're chasing a real timeout misbehavior on the IEC bus, SETTMO
1693
+ isn't the answer — the C64's bus timeouts are wired in and
1694
+ cannot be changed from software. The right fix is usually
1695
+ retrying the operation after the status byte reports `$01`
1696
+ (write timeout) or `$02` (read timeout).
1697
+
1698
+ - **MEMTOP doesn't protect RAM from ML code.** Lowering MEMTOP
1699
+ only tells BASIC to stay below the new ceiling. Direct
1700
+ pokes from ML, including the KERNAL's own LOAD into RAM
1701
+ beyond MEMTOP, ignore it. To truly protect RAM from
1702
+ KERNAL+BASIC, you must also avoid `LOAD` calls that would
1703
+ span the protected region.
1704
+
1705
+ - **MEMBOT doesn't relocate the BASIC program.** Raising MEMBOT
1706
+ after BASIC has loaded a program leaves the program at the
1707
+ old address; BASIC will then misread its own start pointer.
1708
+ Set MEMBOT before BASIC loads anything (or before any
1709
+ CHRGET-based BASIC operation runs), or accept that you must
1710
+ also move the program manually.
1711
+
1712
+ - **RAMTAS is destructive.** Never call RAMTAS from a running
1713
+ program unless you want to lose the BASIC input buffer,
1714
+ the cassette buffer at `$033C-$03FB`, and the open-file
1715
+ table. The cold-start sequence calls RAMTAS exactly once,
1716
+ before any application state exists.
1717
+
1718
+ - **CINT clears the screen.** Calling CINT from an application
1719
+ will fill screen RAM with spaces and color RAM with the
1720
+ current foreground color. If you need to re-init the VIC-II
1721
+ for text mode without clearing the screen, write to the
1722
+ VIC-II registers directly rather than calling CINT.
1723
+
1724
+ - **VECTOR with C=0 installs all 16 vectors.** Don't use VECTOR
1725
+ to patch one vector — pointing the supplied table at random
1726
+ memory will overwrite the other 15 KERNAL vectors with
1727
+ garbage and crash the machine on the next IRQ. Patch single
1728
+ vectors by writing directly to `$0314`-`$0333`.
1729
+
1730
+ - **SETMSG bit 7 alone suppresses control messages but allows
1731
+ errors.** A `$80` value is the standard "no chatty messages
1732
+ but tell me about real errors" setting for applications.
1733
+ `$C0` is the default BASIC-style verbose setting. `$00` is
1734
+ silent — the KERNAL will not print anything during file
1735
+ operations even if the device is missing, so the
1736
+ application must check carry/status itself.
1737
+
1738
+ - **The fast-load problem.** The KERNAL's IEC bus protocol is
1739
+ notoriously slow — about 400-800 bytes/sec on a 1541. Every
1740
+ successful commercial fast-loader (Action Replay, Final
1741
+ Cartridge, Krakout, JiffyDOS, Epyx Fastload, etc.) replaces
1742
+ the KERNAL's IECIN/IECOUT bit-banging with custom code that
1743
+ uploads a small handler to the drive's 6502 and uses
1744
+ non-standard line timing for 5-15x speedup. Such fast-loaders
1745
+ typically patch IBSOUT, ILOAD, and ISAVE vectors to call into
1746
+ the cartridge code. After running with a fast-loader cart,
1747
+ vector state is non-default and RESTOR is necessary before
1748
+ removing the cartridge or returning to BASIC.
1749
+
1750
+ - **CHKIN on the screen returns success silently.** CHKIN on
1751
+ device 3 (screen) succeeds with C=0 but then CHRIN returns
1752
+ the screen contents at the cursor row, byte by byte, in a
1753
+ PETSCII-encoded form. This is the legacy "read the screen as
1754
+ if it were input" mechanism that early Commodore BASIC used
1755
+ to implement the screen editor. Modern code that accidentally
1756
+ invokes this by re-using a logical file number tied to the
1757
+ screen sees mysterious "input" arriving from nowhere.
1758
+
1759
+ - **A logical-file collision is a silent error in some calls.**
1760
+ OPEN with a logical file number that is already open returns
1761
+ error 2 ("file already open"). But CLOSE on a logical file
1762
+ that is not open returns C=0 (success). This asymmetry can
1763
+ mask state bugs — a CLOSE that should report "wasn't open"
1764
+ silently succeeds. The cure is to track open-file state in
1765
+ the application rather than relying on the KERNAL to detect
1766
+ double-closes.
1767
+
1768
+ - **Zero-page locations the KERNAL routines use.** Many KERNAL
1769
+ routines use specific zero-page bytes as workspace. Programs
1770
+ that themselves use the same zero-page locations and call
1771
+ the KERNAL between writes will see their values clobbered.
1772
+ The most-touched locations:
1773
+
1774
+ | Addr | Used by |
1775
+ |-----------|----------------------------------------------------|
1776
+ | `$90` | Status byte (READST) |
1777
+ | `$91` | STOP-key flag (UDTIM, STOP) |
1778
+ | `$93` | LOAD/VERIFY flag |
1779
+ | `$95` | Buffered character (CHRIN/CHROUT) |
1780
+ | `$98` | Number of open files |
1781
+ | `$99-$9A` | Default input/output devices |
1782
+ | `$9D` | KERNAL message control (SETMSG) |
1783
+ | `$A0-$A2` | Jiffy clock (SETTIM, RDTIM, UDTIM) |
1784
+ | `$AC-$AF` | LOAD/SAVE start and end addresses |
1785
+ | `$B7` | Filename length (SETNAM) |
1786
+ | `$B8` | Logical file number (SETLFS) |
1787
+ | `$B9` | Secondary address (SETLFS) |
1788
+ | `$BA` | Device number (SETLFS) |
1789
+ | `$BB-$BC` | Filename pointer (SETNAM) |
1790
+ | `$C5` | Current key pressed (SCNKEY) |
1791
+ | `$C6` | Number of chars in keyboard queue |
1792
+ | `$D1-$D2` | Pointer to start of current screen line (PLOT) |
1793
+ | `$D3` | Cursor column (PLOT) |
1794
+ | `$D6` | Cursor row (PLOT) |
1795
+
1796
+ An ML program that needs these zero-page locations for its
1797
+ own use must avoid calling any KERNAL routine that touches
1798
+ them, or save and restore the affected bytes around each
1799
+ KERNAL call. The screen-editor zero-page locations
1800
+ (`$D1-$F2`) are particularly aggressive — calling CHROUT
1801
+ modifies a dozen of them.
1802
+
1803
+ - **Banking and KERNAL calls.** KERNAL ROM is mapped in at
1804
+ `$E000-$FFFF` only when `$01` bit 1 (HIRAM) is set. When
1805
+ HIRAM is cleared (e.g. to expose the underlying RAM at
1806
+ `$E000-$FFFF`), KERNAL jump-table calls become "JMP to
1807
+ whatever's in RAM at `$FFD2`" — typically garbage. The
1808
+ conventional pattern is to save `$01`, set HIRAM, call the
1809
+ KERNAL routine, restore `$01`. The CPU's IRQ/NMI vectors
1810
+ also live in the KERNAL bank at `$FFFA-$FFFF`, so disabling
1811
+ HIRAM without first disabling interrupts is a fast crash:
1812
+
1813
+ ```asm
1814
+ pha
1815
+ lda $01
1816
+ pha
1817
+ ora #$02 ; set HIRAM
1818
+ sta $01
1819
+ jsr $FFD2 ; CHROUT
1820
+ pla
1821
+ sta $01
1822
+ pla
1823
+ rts
1824
+ ```
1825
+
1826
+ - **Cross-reference:** See [c64-memory-map.md](c64-memory-map.md)
1827
+ for the meanings of zero-page locations `$90` (status), `$91`
1828
+ (STOP flag), `$A0-$A2` (jiffy clock), `$0314-$0333` (RAM
1829
+ vectors), and `$0259-$0276` (open-file tables). See
1830
+ [cia-reference.md](cia-reference.md) for the CIA1 timer
1831
+ programming that drives the 60/50 Hz IRQ that calls UDTIM.
1832
+ See [vic-ii-reference.md](vic-ii-reference.md) for the
1833
+ `$D018` register that CHROUT writes when processing PETSCII
1834
+ case-toggle bytes (`$0E`, `$8E`).
1835
+
1836
+ <!-- doc-type: hardware-reference -->