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,3184 @@
1
+ # C64 Memory Map
2
+
3
+ ## Overview
4
+
5
+ The Commodore 64 has a flat 16-bit address space — exactly 65,536 bytes from
6
+ $0000 to $FFFF — but it ships with far more than 64 KB of physical storage.
7
+ Inside the machine there are 64 KB of dynamic RAM, 8 KB of BASIC ROM, 8 KB
8
+ of KERNAL ROM, 4 KB of character generator ROM, the 47-register VIC-II,
9
+ the 29-register SID, two CIA chips, and 1 KB of nibble-wide Color RAM. All
10
+ of those pieces have to share the single address bus the 6510 sees. They do
11
+ not coexist in parallel — they are *banked* in and out of view by a tiny
12
+ configuration latch built into the CPU itself.
13
+
14
+ The 6510 has six on-chip I/O pins that exit the chip alongside the address
15
+ and data buses. The lowest three of those pins (P0, P1, P2) drive the
16
+ PLA chip's bank-select inputs. Software controls them by writing the
17
+ data-direction register at $0000 (DDR) and the data register at $0001.
18
+ With three bits there are eight possible patterns; the PLA collapses them
19
+ into seven distinct memory layouts that swap chunks of RAM, ROM, and I/O
20
+ into the address space. Every C64 program — every game, every demo, every
21
+ copy-protection scheme — ultimately reduces to choosing one of those
22
+ seven configurations and writing to whatever happens to be visible.
23
+
24
+ This document maps the entire $0000-$FFFF address space, explains how
25
+ banking works, lists every fixed-purpose region of zero page, the stack
26
+ page, KERNAL workspace, ROM ranges, and the I/O area. It is the primary
27
+ reference for any code that needs to know "is this address RAM or ROM
28
+ right now?" or "what does the KERNAL clobber if I let it run?"
29
+
30
+ Memory addresses in this doc use the standard Commodore convention:
31
+ hexadecimal with a `$` prefix, four digits, uppercase. The dollar sign is
32
+ syntactically meaningful in 6502 assemblers and is the recognized
33
+ canonical form across the entire C64 documentation corpus. Decimal
34
+ equivalents are given where useful.
35
+
36
+ ### The 64KB address space at a glance
37
+
38
+ | Range | Contents (default after reset) |
39
+ |---------------|----------------------------------------------|
40
+ | $0000-$0001 | 6510 processor port (DDR + data) |
41
+ | $0002-$00FF | Zero page (BASIC + KERNAL workspace + free) |
42
+ | $0100-$01FF | CPU stack |
43
+ | $0200-$02FF | KERNAL input buffer + workspace |
44
+ | $0300-$03FF | KERNAL vectors + workspace |
45
+ | $0400-$07E7 | Default screen RAM (40 × 25 = 1000 bytes) |
46
+ | $07E8-$07F7 | Unused (tail of the screen page) |
47
+ | $07F8-$07FF | Default sprite pointers (8 bytes) |
48
+ | $0800-$9FFF | BASIC program + variables / general user RAM |
49
+ | $A000-$BFFF | BASIC ROM (or RAM under it) |
50
+ | $C000-$CFFF | Free RAM (never banked, always present) |
51
+ | $D000-$D3FF | VIC-II registers (or character ROM, or RAM) |
52
+ | $D400-$D7FF | SID registers (or character ROM, or RAM) |
53
+ | $D800-$DBFF | Color RAM (4 bits wide; or character ROM) |
54
+ | $DC00-$DCFF | CIA #1 (or character ROM, or RAM) |
55
+ | $DD00-$DDFF | CIA #2 (or character ROM, or RAM) |
56
+ | $DE00-$DEFF | I/O expansion area 1 (cartridge) |
57
+ | $DF00-$DFFF | I/O expansion area 2 (cartridge) |
58
+ | $E000-$FFFF | KERNAL ROM (or RAM under it) |
59
+
60
+ ### Hardware backing for each region
61
+
62
+ Address space is the CPU's view; *physical storage* is what the bytes
63
+ actually live in. The C64 has the following physical sources of bytes
64
+ visible to the CPU:
65
+
66
+ - 64 KB DRAM (eight 4164 chips), addressing the full $0000-$FFFF
67
+ - 8 KB KERNAL ROM, mapped at $E000-$FFFF when banked in
68
+ - 8 KB BASIC ROM, mapped at $A000-$BFFF when banked in
69
+ - 4 KB character ROM, mapped at $D000-$DFFF when banked in (charrom mode)
70
+ - 1 KB Color RAM (a separate 4-bit static RAM at $D800-$DBFF)
71
+ - VIC-II registers, mapped at $D000-$D02F (64-byte window, shadow-repeats
72
+ through $D03F and the whole way to $D3FF as 64-byte mirrors)
73
+ - SID registers, mapped at $D400-$D41C (29 bytes; shadow-repeats every
74
+ 32 bytes through $D7FF)
75
+ - CIA #1 at $DC00-$DC0F (16 bytes; shadow-repeats every 16 bytes through $DCFF)
76
+ - CIA #2 at $DD00-$DD0F (16 bytes; shadow-repeats every 16 bytes through $DDFF)
77
+ - Cartridge ROMs and expansion-port I/O at $DE00-$DFFF and $8000-$9FFF
78
+ and $A000-$BFFF (depending on cartridge type — GAME and EXROM lines)
79
+
80
+ The 64 KB of DRAM is always physically present at every address. When a
81
+ ROM is "banked in," the PLA disables the RAM's CAS line for reads only in
82
+ that range — writes still go to RAM. This is why you can poke a value into
83
+ $E000 even though KERNAL ROM is mapped there: the byte lands in the RAM
84
+ underneath and reveals itself if you bank the ROM out. This dual-write
85
+ behavior is critical for relocating screens, swapping in custom fonts,
86
+ and using $E000-$FFFF as 8 KB of additional RAM by banking KERNAL out.
87
+
88
+ ### Reset state
89
+
90
+ When the CPU comes out of reset, the KERNAL's IOINIT ($FDA3; called from
91
+ RESET at $FCF2, and reachable through the jump table at $FF84) puts the
92
+ 6510 processor port into a known state: it writes $E7 to the data
93
+ register at $0001 first, then $2F (00101111, bits 0-3 + 5 outputs; bits 4
94
+ and 6 inputs; bit 7 unused on a stock C64) to the DDR at $0000
95
+ (`LDA #$E7 / STA $01 / LDA #$2F / STA $00` at $FDD5). With that DDR and no
96
+ tape button pressed, $01 reads back as $37 (00110111) once the
97
+ unconnected bits 6/7 have decayed to 0, and the KERNAL's default IRQ
98
+ service then writes the read-back value ORed with $20 (motor off) back
99
+ into $01 on every tick ($EA6B-$EA79, the datasette motor interlock), so a
100
+ running C64 holds $37 in the latch as well. An earlier version of this
101
+ section said the data register was "set to $37"; that is the settled
102
+ read-back value, not what IOINIT writes (measured in VICE x64sc: latch
103
+ $E7 immediately after `JSR $FF84`, $F7 a few frames after IRQs start, $37
104
+ by the time BASIC is running). Bits 2-0 = 111 mean: BASIC
105
+ ROM in, KERNAL ROM in, I/O in (the default user layout).
106
+
107
+ The KERNAL cold start ($FCE2, via the $FFFC vector) runs IOINIT, RAMTAS,
108
+ RESTOR and CINT in that order. RAMTAS ($FF87 → $FD50) clears $0002-$0101
109
+ and $0200-$03FF — the rest of the stack page, $0102-$01FF, is not touched
110
+ — sets the tape-buffer pointer $B2/$B3 to $033C, finds the top of memory
111
+ by walking pages from $0400 (MEMTOP = $A000 with BASIC ROM in), and sets
112
+ MEMBOT to $0800 ($0282) and the screen page $0288 to $04. It does not
113
+ clear the screen and it does not install any vector: its page-3 clear
114
+ leaves $0314/$0315 at $0000. RESTOR ($FF8A → $FD15) then copies the
115
+ 16-vector table at $FD30 into $0314-$0333, which is what puts $EA31 in
116
+ $0314/$0315, and CINT ($FF81 → $FF5B → $E518) clears the screen at
117
+ $0400-$07E7 and initialises the VIC-II. An earlier version of this page
118
+ credited all of this to RAMTAS; the split is from the ROM bytes and a
119
+ VICE x64sc run calling $FF87 alone. By the time BASIC's
120
+ "READY." prompt prints, the system is in mode 31 (the default seven-bank
121
+ configuration) with all ROMs visible.
122
+
123
+ ### Banks vs banking — terminology
124
+
125
+ "Bank" is overloaded in C64 literature. Three distinct uses:
126
+
127
+ - **CPU bank** / **memory mode** / **mode N** (0-31): one of the 32
128
+ configurations controlled by $01 bits 0-2 plus the GAME and EXROM lines
129
+ from the cartridge port. Only 7 of the 32 are functionally distinct in
130
+ the absence of a cartridge.
131
+ - **VIC-II bank** (0-3): a 16 KB window the VIC-II chip uses to fetch
132
+ screen, character, sprite, and bitmap data. Selected by CIA #2 port A
133
+ bits 0-1 (inverted). Bank 0 = $0000-$3FFF; bank 1 = $4000-$7FFF;
134
+ bank 2 = $8000-$BFFF; bank 3 = $C000-$FFFF. See
135
+ [vic-ii-reference.md](vic-ii-reference.md) for VIC-II bank details.
136
+ - **Bank** in the C128/REU sense: 64 KB chunks. Not applicable to a
137
+ stock C64.
138
+
139
+ This doc covers CPU banks. VIC-II banking is independent and orthogonal —
140
+ the VIC sees its own memory map and never sees ROMs (with one wrinkle:
141
+ the character ROM is shadowed into VIC banks 0 and 2 at $1000-$1FFF and
142
+ $9000-$9FFF respectively).
143
+
144
+ ### Sources
145
+
146
+ - *Mapping the Commodore 64* (Sheldon Leemon, Compute! Publications 1984;
147
+ Project 64 transcription at zimmers.net) — the canonical zeropage and
148
+ KERNAL workspace map.
149
+ - C64-Wiki Memory Map article — modern collation.
150
+ - sta.c64.org cbm64mem.html (Joe Forster/STA's "Commodore 64 Memory Map")
151
+ — exhaustive per-byte zero-page reference.
152
+ - *Commodore 64 Programmer's Reference Guide* (Commodore, 1982) chapters 2 + 7.
153
+ - *The C64 PLA Dissected* (Thomas Giesel, 2010) for bank-select truth table.
154
+
155
+ ## Quick reference
156
+
157
+ The address space resolves at each fetch through three layers:
158
+
159
+ 1. **CPU pins** A0-A15 emit the address.
160
+ 2. **PLA (906114)** sees A12-A15 (the high nibble, which picks the 4 KB
161
+ chunk — the same inputs listed under [Bank switching](#bank-switching)),
162
+ the GAME/EXROM lines from the cartridge, and the LORAM/HIRAM/CHAREN
163
+ lines from the 6510's processor port. It produces enable signals:
164
+ CASRAM (RAM read enable), ROML/ROMH (cartridge ROM enables), KERNAL,
165
+ BASIC, CHAROM, and I/O. Splitting the I/O block below 4 KB into VIC,
166
+ SID, colour RAM, CIA1, CIA2, IO1 and IO2 is done from A8-A11 by a
167
+ separate decoder downstream of the PLA's I/O enable (a 74LS139 on the
168
+ boards that carry the 906114; from the schematics, not measured here).
169
+ An earlier revision of this line said A8-A15; the 906114 has no
170
+ A8-A11 inputs.
171
+ 3. **Selected device** drives the data bus.
172
+
173
+ For most software the only thing that matters is the value at $01. Bits
174
+ 2-0 of $01 control:
175
+
176
+ | $01 bit | Name | 0 means | 1 means |
177
+ |---------|---------|-----------------------------------------|-------------------------------|
178
+ | 0 | LORAM | $A000-$BFFF is RAM | $A000-$BFFF can be BASIC ROM |
179
+ | 1 | HIRAM | $E000-$FFFF is RAM | $E000-$FFFF can be KERNAL ROM |
180
+ | 2 | CHAREN | $D000-$DFFF is character ROM | $D000-$DFFF is I/O |
181
+
182
+ Higher bits of $01 are used for cassette control (bits 3-5) and the cassette
183
+ sense line (bit 4 input).
184
+
185
+ The seven distinct CPU bank configurations (with no cartridge attached —
186
+ GAME=1, EXROM=1) are summarized in the [Bank switching](#bank-switching)
187
+ section.
188
+
189
+ ### Memory ranges that are always RAM
190
+
191
+ These ranges are always RAM regardless of $01 setting, on a stock C64 with
192
+ no cartridge:
193
+
194
+ - $0000-$0001 — processor port (special — see below)
195
+ - $0002-$9FFF — 40 KB of contiguous RAM
196
+ - $C000-$CFFF — 4 KB of free RAM
197
+
198
+ The remaining 20 KB ($A000-$BFFF, $D000-$DFFF, $E000-$FFFF) is the
199
+ multiplexed region.
200
+
201
+ ### Memory ranges that change meaning
202
+
203
+ These ranges depend on $01 and cartridge lines:
204
+
205
+ - $A000-$BFFF — 8 KB: BASIC ROM, RAM, or cartridge ROM (ROMH)
206
+ - $D000-$DFFF — 4 KB: I/O, character ROM, or RAM
207
+ - $E000-$FFFF — 8 KB: KERNAL ROM, RAM, or cartridge ROM (ROMH in Ultimax)
208
+ - $8000-$9FFF — 8 KB: RAM, or cartridge ROM (ROML in 8K/16K/Ultimax)
209
+
210
+ ## Memory regions
211
+
212
+ The following H3 entries cover the entire $0000-$FFFF address space. Each
213
+ region includes its default function on a freshly-booted C64, whether the
214
+ region is bank-switchable, and key sub-addresses or registers within it.
215
+
216
+ ### $0000-$0001 — Processor I/O port
217
+
218
+ **Default use:** 6510 on-chip I/O port; controls memory banking and cassette
219
+ **Bank-switchable:** No (the port itself is on-die in the CPU and is always reachable)
220
+
221
+ The 6510 CPU has six general-purpose I/O pins that exit the chip on the
222
+ package pins normally unused by a stock 6502. These pins are made
223
+ accessible to software as memory locations $0000 (the data direction
224
+ register) and $0001 (the data register). The PLA reads three of those
225
+ pins to decide which ROMs are banked into the address space.
226
+
227
+ $0000 = DDR. Each bit decides whether the corresponding pin in $0001 is
228
+ an output (bit = 1) or an input (bit = 0).
229
+
230
+ $0001 = data. For output pins, the written value drives the pin. For
231
+ input pins, the value read reflects the pin's external state.
232
+
233
+ **Reset value:** DDR = $2F; data written by IOINIT = $E7, reads back as $37 (see Reset state above).
234
+
235
+ After reset, bits 0-2 of $01 are outputs driving LORAM, HIRAM, CHAREN
236
+ high — selecting BASIC ROM + KERNAL ROM + I/O visible.
237
+
238
+ | Bit | Name | I/O | Function |
239
+ |-----|-----------|-------|-----------------------------------------------------------------------|
240
+ | 0 | LORAM | Out | 0 = $A000-$BFFF is RAM; 1 = BASIC ROM enabled (subject to HIRAM) |
241
+ | 1 | HIRAM | Out | 0 = $E000-$FFFF is RAM; 1 = KERNAL ROM enabled |
242
+ | 2 | CHAREN | Out | 0 = $D000-$DFFF is char ROM (if HIRAM or LORAM); 1 = $D000-$DFFF I/O |
243
+ | 3 | CASSDOUT | Out | Datasette write line |
244
+ | 4 | CASSSENS | In | Datasette sense (0 = button pressed) |
245
+ | 5 | CASSMOT | Out | Datasette motor control (0 = motor on) |
246
+ | 6 | - | - | No pin on the 6510. As an input it reads the last level driven when it was an output, decaying to 0 after a few hundred ms (VICE models 350,000-420,000 cycles, ~0.35-0.43 s; measured 360k-417k in x64sc 3.10). Reads 0 after boot ($01 = $37). Do not rely on it. |
247
+ | 7 | - | - | No pin on the 6510. As an input it reads the last level driven when it was an output, decaying to 0 after a few hundred ms (VICE models 350,000-420,000 cycles, ~0.35-0.43 s; measured 360k-417k in x64sc 3.10). Reads 0 after boot ($01 = $37). Do not rely on it. |
248
+
249
+ **Notes:**
250
+ - **DDR must be set first.** When you write a new value to $01, the
251
+ underlying CPU latches the value through the DDR mask. Bits configured
252
+ as input ignore writes. If you intend to drive a pin, set the DDR
253
+ bit to 1 first, then write the data register.
254
+ - **Reading $01 can be glitchy.** When CASSMOT is driving low (motor on)
255
+ and you read $01, the input bit CASSSENS may read back as the most
256
+ recent value the line was driven to during the cassette sense window.
257
+ This is reported in *Mapping the Commodore 64* (p. 32) and is not
258
+ checked here — no tape device is attached in the headless runs.
259
+ - **Bit 6/7 unconnected.** On a stock C64 these pins are not bonded out
260
+ and read as their previous output state with very slow capacitive
261
+ decay. *Do not rely on bit 6/7 values for anything.* An earlier version
262
+ of the table said these bits were "pulled high"; the reset state
263
+ DDR=$2F / $01=$37 contradicts that — they are inputs reading 0, and a
264
+ driven 0 switched to input stays 0. See
265
+ [6510-cpu-reference.md](6510-cpu-reference.md#reading-the-port) for the
266
+ decay figure.
267
+
268
+ ### $0002-$0002 — Unused (free zero-page byte)
269
+
270
+ **Default use:** None — not referenced by BASIC or the KERNAL; free for user code alongside $FB-$FE
271
+ **Bank-switchable:** No (always RAM)
272
+
273
+ Cleared to $00 by RAMTAS (`STA $0002,Y` at $FD53) and never touched
274
+ again: no instruction in the BASIC (901226-01) or KERNAL (901227-03) ROM
275
+ names $02 apart from that clearing loop — the only `$02` operands are
276
+ `$02,X` offsets into FAC1/FAC2 in the math package, with X = $61 or $69
277
+ (the sites at $B4DF and $B98D-$B9B0 run with X ≥ $19) — and a sentinel
278
+ POKEd into $02 survives a forced garbage collection (FRE(0)) and the
279
+ BASIC I/O error path (verified against the ROM images and in VICE x64sc
280
+ 3.9). An earlier version of this entry said BASIC used $02 as a working
281
+ register during garbage collection and I/O error handling; neither
282
+ routine ($B526-$B605, $E0F9-$E109) references it. Programs that call ROM
283
+ routines can use it too; the one caveat is KERNAL SAVE, which reads its
284
+ start address through whichever zero-page pointer the caller names in A.
285
+
286
+ ### $0003-$0004 — Float-to-fix vector
287
+
288
+ **Default use:** Vector to BASIC's float-to-fix conversion ($B1AA)
289
+ **Bank-switchable:** No (always RAM)
290
+
291
+ Patchable: if you want to override how BASIC converts floats to integers,
292
+ point this here. Installed by BASIC's cold-start initialisation at $E3BF
293
+ (the KERNAL reset reaches it only via JMP ($A000) -> $E394 -> JSR $E3BF),
294
+ not by the KERNAL. RESTOR ($FF8A -> $FD15) rewrites only $0314-$0333 from
295
+ the table at $FD30 and leaves $03-$06 alone; an earlier version of this
296
+ page said RESTOR pre-loads them (ROM bytes; confirmed in VICE x64sc 3.10).
297
+
298
+ ### $0005-$0006 — Fix-to-float vector
299
+
300
+ **Default use:** Vector to BASIC's fix-to-float conversion ($B391)
301
+ **Bank-switchable:** No (always RAM)
302
+
303
+ Installed by the same BASIC init at $E3BF, default $B391; see $0003-$0004.
304
+
305
+ ### $0007-$0008 — Search character / quote flag
306
+
307
+ **Default use:** BASIC tokenization workspace (current search char + quote-state flag)
308
+ **Bank-switchable:** No
309
+
310
+ ### $0009-$0009 — TRMPOS (TAB/SPC cursor column)
311
+
312
+ **Default use:** Cursor column (read via PLOT $FFF0) at the moment PRINT evaluates a TAB( or SPC( — the column before the move. Earlier text called this an input-buffer pointer; the only ROM references are $AAFD/$AB0A in PRINT's TAB/SPC code (measured in VICE x64sc: 3 after PRINT "ABC";TAB(20)).
313
+ **Bank-switchable:** No
314
+
315
+ ### $000A-$000A — VERCK (LOAD/VERIFY flag)
316
+
317
+ **Default use:** BASIC LOAD = $00, VERIFY = $01; passed in A to KERNAL LOAD ($FFD5). Earlier text called this an array-dimension flag; the only references are $E16A/$E16F/$E17A in the BASIC LOAD/VERIFY entry.
318
+ **Bank-switchable:** No
319
+
320
+ ### $000B-$000B — COUNT (input-buffer index / subscript count)
321
+
322
+ **Default use:** Tokeniser keyword counter and crunched-line length; number of array subscripts while an array reference is processed (compared with the array header at $B256, counted down to 0 by the dimension loops). Earlier text called this a type flag; the numeric/string flag is VALTYP at $000D.
323
+ **Bank-switchable:** No
324
+
325
+ ### $000C-$000C — DIMFLG (DIM in progress)
326
+
327
+ **Default use:** Non-zero while DIM is creating an array (DIM enters the variable-lookup routine with this set; it holds the first character of the array name), $00 for an ordinary variable reference. Earlier text called this a DEF FN flag; the FN flag is SUBFLG at $0010.
328
+ **Bank-switchable:** No
329
+
330
+ ### $000D-$000D — Data type flag
331
+
332
+ **Default use:** $00 = numeric expression result, $FF = string
333
+ **Bank-switchable:** No
334
+
335
+ ### $000E-$000E — Integer/float flag
336
+
337
+ **Default use:** $80 = integer, $00 = floating-point result
338
+ **Bank-switchable:** No
339
+
340
+ ### $000F-$000F — GARBFL (DATA-scan / LIST-quote / garbage-collection flag)
341
+
342
+ **Default use:** Tokeniser: set when a DATA statement is found so the rest of the line is not tokenised, cleared at ':'; LIST: quote-mode toggle; string allocation: bit 7 set once garbage collection has run so it is not run twice. A flag, not workspace, and not a DATA pointer — DATPTR is $0041-$0042 below.
343
+ **Bank-switchable:** No
344
+
345
+ ### $0010-$0010 — SUBFLG (subscript / FN reference allowed)
346
+
347
+ **Default use:** $00 = subscripts and integer variables allowed; $80 = set by FOR and DEF FN/FN so the variable-lookup routine refuses an integer name or a subscript (?SYNTAX ERROR). Cleared by CLR and after each plain variable lookup.
348
+ **Bank-switchable:** No
349
+
350
+ ### $0011-$0013 — Input source + LIST mode + scan offset
351
+
352
+ **Default use:** Subscript pointer and IO type code; LIST current line pointer
353
+ **Bank-switchable:** No
354
+
355
+ ### $0014-$0015 — Line number temp
356
+
357
+ **Default use:** Temporary line number from BASIC text — used by GOTO, GOSUB, RUN, LIST
358
+ **Bank-switchable:** No
359
+
360
+ ### $0016-$001A — String descriptor stack
361
+
362
+ **Default use:** Top of BASIC string descriptor stack (LASTPT, TEMPPT)
363
+ **Bank-switchable:** No
364
+
365
+ ### $001B-$0021 — Misc BASIC workspace
366
+
367
+ **Default use:** String descriptor temp + index/temp pointers used by BASIC
368
+ **Bank-switchable:** No
369
+
370
+ ### $0022-$0025 — INDEX1, INDEX2
371
+
372
+ **Default use:** General-purpose 16-bit pointers used by BASIC functions
373
+ **Bank-switchable:** No
374
+
375
+ Usable by assembly when BASIC is dormant.
376
+
377
+ ### $0026-$002A — Floating-point accumulator C
378
+
379
+ **Default use:** Working register for BASIC's floating-point routines
380
+ **Bank-switchable:** No
381
+
382
+ ### $002B-$002C — TXTTAB (start of BASIC program)
383
+
384
+ **Default use:** Pointer to first byte of stored BASIC program (default $0801)
385
+ **Bank-switchable:** No
386
+
387
+ Reset value: $0801. Set lower to relocate the BASIC program below the
388
+ screen; set higher to reserve memory below the program. After modifying
389
+ TXTTAB, POKE the byte at TXTTAB-1 with $00 (e.g. POKE 16384,0 for TXTTAB
390
+ = $4001) and run NEW so BASIC initializes the link bytes. RUN and NEW
391
+ both set TXTPTR to TXTTAB-1 ($A68E) and the statement loop ($A7C0) reads
392
+ that byte expecting an end-of-line zero, so anything else there gives
393
+ ?SYNTAX ERROR before the first line runs; the cold start zeroes $0800
394
+ ($E419) only because the default TXTTAB is $0801. An earlier version of
395
+ this page said to zero $0800, which is right only for the default.
396
+ (Measured in VICE x64sc 3.10: POKE44,64:POKE2048,0:NEW gave ?SYNTAX ERROR,
397
+ POKE44,64:POKE16384,0:NEW ran.)
398
+
399
+ ### $002D-$002E — VARTAB (start of BASIC variables)
400
+
401
+ **Default use:** Pointer to first byte past the tokenized BASIC program; start of simple variables
402
+ **Bank-switchable:** No
403
+
404
+ Set by BASIC at the end of every program edit. Reading VARTAB gives the
405
+ true end of the BASIC program in memory.
406
+
407
+ ### $002F-$0030 — ARYTAB (start of arrays)
408
+
409
+ **Default use:** Pointer to first byte past simple variables; start of array storage
410
+ **Bank-switchable:** No
411
+
412
+ ### $0031-$0032 — STREND (end of arrays)
413
+
414
+ **Default use:** Pointer to first byte past arrays; bottom of free memory
415
+ **Bank-switchable:** No
416
+
417
+ The 256-byte BASIC variable namespace lives between $002B and $0033 as a
418
+ chain of four pointers. The order is invariant: TXTTAB < VARTAB < ARYTAB
419
+ < STREND. STREND <= MEMSIZ (top-of-BASIC) always.
420
+
421
+ ### $0033-$0034 — STRBOT (bottom of string descriptor heap)
422
+
423
+ **Default use:** Pointer to bottom of string heap (grows down from MEMSIZ)
424
+ **Bank-switchable:** No
425
+
426
+ Strings live at the top of BASIC's workspace and grow downward. The
427
+ distance between STREND and STRBOT is the available free memory before
428
+ garbage collection runs.
429
+
430
+ ### $0035-$0036 — Unused / FRESPC
431
+
432
+ **Default use:** Pointer used during string concatenation; safe to clobber
433
+ **Bank-switchable:** No
434
+
435
+ ### $0037-$0038 — MEMSIZ (top of BASIC)
436
+
437
+ **Default use:** Highest address BASIC can use (default $A000 on cold boot)
438
+ **Bank-switchable:** No
439
+
440
+ Lower MEMSIZ to reserve a chunk of high RAM for assembly code, custom
441
+ char sets, or sprite data. MEMSIZ is BASIC's hard cap — string heap
442
+ grows down from MEMSIZ.
443
+
444
+ ### $0039-$003A — CURLIN (current BASIC line number)
445
+
446
+ **Default use:** Line number of the currently-executing BASIC statement; in direct mode only the high byte is set ($3A = $FF) — the low byte keeps whatever line ran last
447
+ **Bank-switchable:** No
448
+
449
+ A useful test: PEEK(58) = 255 means direct mode. Do not compare the
450
+ 16-bit value with 65535 — the main loop ($A490: LDX #$FF / STX $3A) sets
451
+ only the high byte, so after a program has run the pair reads $FFnn with
452
+ nn the low byte of the last executed line, and even on a fresh boot it
453
+ reads 65280 ($39 is 0 from the KERNAL's RAM clear; nothing in either ROM
454
+ ever writes $FF to $39). BASIC's own direct-mode checks ($A46C, $A838,
455
+ $B3A6) test only $3A. An earlier version of this page said 65535;
456
+ measured in VICE x64sc 3.10: 65280 at boot, 65300 after a program ending
457
+ at line 20.
458
+
459
+ ### $003B-$003C — OLDLIN (previous BASIC line)
460
+
461
+ **Default use:** Line of last STOP, END, or CONT-able statement
462
+ **Bank-switchable:** No
463
+
464
+ ### $003D-$003E — OLDTXT (previous BASIC text pointer)
465
+
466
+ **Default use:** Text pointer for CONT (continue) command
467
+ **Bank-switchable:** No
468
+
469
+ ### $003F-$0040 — DATLIN (last DATA line number)
470
+
471
+ **Default use:** Line number containing the next DATA item for READ
472
+ **Bank-switchable:** No
473
+
474
+ ### $0041-$0042 — DATPTR (next DATA pointer)
475
+
476
+ **Default use:** Pointer to next DATA byte to be read by READ
477
+ **Bank-switchable:** No
478
+
479
+ RESTORE sets DATPTR back to start of program.
480
+
481
+ ### $0043-$0044 — INPTR (input vector)
482
+
483
+ **Default use:** Pointer to current INPUT character source
484
+ **Bank-switchable:** No
485
+
486
+ ### $0045-$0046 — VARNAM (current variable name)
487
+
488
+ **Default use:** Two-character variable name being looked up
489
+ **Bank-switchable:** No
490
+
491
+ ### $0047-$0048 — VARPNT (current variable pointer)
492
+
493
+ **Default use:** Pointer to variable value in BASIC variable space
494
+ **Bank-switchable:** No
495
+
496
+ ### $0049-$004A — FORPNT (FOR loop pointer)
497
+
498
+ **Default use:** Pointer to FOR-loop control variable
499
+ **Bank-switchable:** No
500
+
501
+ ### $004B-$0052 — Misc BASIC arithmetic / expression evaluation
502
+
503
+ **Default use:** Op stack temps for BASIC's expression evaluator
504
+ **Bank-switchable:** No
505
+
506
+ ### $0053-$0053 — Garbage-collection step size
507
+
508
+ **Default use:** Step the BASIC garbage collector adds to its walk pointer ($22-$23): 3 while scanning the temporary string-descriptor stack and array elements, 7 while scanning simple variables (set at $B54D and $B56A, applied at $B5F6); BASIC cold start initialises it to 3 ($E3EA). Not a string length, and not VALTYP — the numeric/string type flag is $000D.
509
+ **Bank-switchable:** No
510
+
511
+ Conventionally labelled FOUR6 (the Microsoft/Commodore source name — not
512
+ verified from an instrument on this machine). An earlier version of this
513
+ entry was headed VALTYP; that name belongs to $000D, and the ROM shows
514
+ $53 is the collector's step size (3 or 7), not a length.
515
+
516
+ ### $0054-$0056 — Jump-to-function temp
517
+
518
+ **Default use:** JMP instruction template used by BASIC to call function code
519
+ **Bank-switchable:** No
520
+
521
+ ### $0057-$0060 — Misc numeric work area (two 5-byte FP temporaries)
522
+
523
+ **Default use:** BASIC math temporaries at $57-$5B and $5C-$60; MOV2F stores FAC to either ($BBCA -> $57, $BBC7 -> $5C), the series evaluator POLY1 ($E043) multiplies by them, and FIN clears $5D-$67 at entry and uses $5D-$60 as its digit/exponent counters
524
+ **Bank-switchable:** No
525
+
526
+ Earlier revisions called this ARG; ARG is at $69-$6E (see below), as the
527
+ BASIC ROM's CONUPK $BA8C shows. The conventional labels TEMPF1/TEMPF2
528
+ are names from the published maps, not read from an instrument.
529
+
530
+ ### $0061-$0066 — Floating-point accumulator #1 (FAC)
531
+
532
+ **Default use:** Primary floating-point register; result of every BASIC math op
533
+ **Bank-switchable:** No
534
+
535
+ | Address | Meaning |
536
+ |---------|----------------------------------------|
537
+ | $0061 | Exponent |
538
+ | $0062-$0065 | Mantissa (4 bytes, MSB first) |
539
+ | $0066 | Sign |
540
+
541
+ ### $0067-$0067 — SGNFLG
542
+
543
+ **Default use:** Sign flag during number input (FIN, $BD02); term counter during series evaluation, loaded from the coefficient table's first byte at $E062 and decremented at $E088 — it is a counter, not a pointer
544
+ **Bank-switchable:** No
545
+
546
+ ### $0068-$0068 — BITS
547
+
548
+ **Default use:** FAC overflow byte, set by INT ($BCA9) and normalisation, cleared by BASIC init ($E3F0)
549
+ **Bank-switchable:** No
550
+
551
+ ### $0069-$006E — Floating-point accumulator #2 (ARG)
552
+
553
+ **Default use:** Second floating-point register used by BASIC math; CONUPK ($BA8C) loads it from memory
554
+ **Bank-switchable:** No
555
+
556
+ | Address | Meaning |
557
+ |---------|------------------------------------------------------------|
558
+ | $0069 | Exponent |
559
+ | $006A-$006D | Mantissa (4 bytes, MSB first, bit 7 of $6A forced set) |
560
+ | $006E | Sign |
561
+
562
+ ### $006F-$006F — ARISGN
563
+
564
+ **Default use:** Sign-comparison byte = ARG sign EOR FAC sign, written by CONUPK ($BAA5-$BAA7) and tested by FADD ($B8A3)
565
+ **Bank-switchable:** No
566
+
567
+ Earlier revisions labelled $67-$6F polynomial workspace; the series
568
+ evaluator's temporaries are actually $57-$60, $67 and $71-$72.
569
+
570
+ ### $0070-$0072 — FACOV + FBUFPT (floating-point scratch)
571
+
572
+ **Default use:** $70 is the FAC rounding/overflow byte (FADD reads it at $B86F, ROUND shifts it at $BC1B, MOVAF zeroes it, FMULT uses it as the first multiplier byte); $71-$72 is the pointer the series/polynomial evaluator POLY1 stores at $E043 for the coefficient table (also borrowed by the tokenizer and string routines as scratch)
573
+ **Bank-switchable:** No
574
+
575
+ Not part of CHRGET: the reset-time copy loop (LDX #$1C / LDA $E3A2,X /
576
+ STA $73,X) starts at $73, so an earlier version of this page that titled
577
+ $70-$72 "CHRGET / CHRGOT" was wrong. Label names FACOV/FBUFPT follow the
578
+ conventional Commodore source labels; the usages above are read from the
579
+ ROM images.
580
+
581
+ ### $0073-$008A — CHRGET routine (executable code)
582
+
583
+ **Default use:** Inline machine code for BASIC's next-token reader
584
+ **Bank-switchable:** No
585
+
586
+ Machine code copied from the ROM image at $E3A2 by BASIC's cold-start
587
+ init ($E3BF, in the KERNAL ROM image) at reset — the CHRGET/CHRGOT
588
+ subroutine BASIC uses to read its own tokenized program. CHRGET is $0073
589
+ (advance TXTPTR then fetch); CHRGOT is $0079 (re-fetch the current byte
590
+ without advancing); the LDA operand at $007A-$007B is TXTPTR. The bytes
591
+ at $0073-$008F are a 29-byte image copied down from $E3A2 (LDX #$1C /
592
+ LDA $E3A2,X / STA $73,X): the CHRGET/CHRGOT subroutine at $73-$8A plus
593
+ the initial RND seed at $8B-$8F ($80 $4F $C7 $52 $58). $E3A2 is BASIC
594
+ interpreter code that physically lives in the KERNAL ROM chip's
595
+ $E000-$E4D2 span (kernal-901227-03.bin offset $03A2), not in
596
+ basic-901226-01.bin. Nothing else reinstalls it — not the KERNAL's
597
+ RESTOR/CINT/IOINIT and not the RUN/STOP-RESTORE warm start at $E37B — so
598
+ once overwritten only a cold start ($E394, SYS 58260) or a reset brings
599
+ it back. An earlier version of this page said the KERNAL copies it in at
600
+ reset.
601
+
602
+ **Notes:**
603
+ - This block is *executable code in zeropage*. Overwriting it crashes BASIC.
604
+ - If you don't use BASIC (i.e. you're a pure assembly program that
605
+ doesn't return to the READY prompt), this 24-byte range is free.
606
+
607
+ ### $007A-$007B — TXTPTR (BASIC program pointer)
608
+
609
+ **Default use:** Pointer to next byte the BASIC interpreter will read
610
+ **Bank-switchable:** No
611
+
612
+ Embedded inside the CHRGET routine. Modifying TXTPTR changes where
613
+ BASIC reads its next statement — used by RUN, GOTO, GOSUB.
614
+
615
+ ### $008B-$008F — RNDX (random number seed)
616
+
617
+ **Default use:** 5-byte floating-point seed for BASIC's RND function
618
+ **Bank-switchable:** No
619
+
620
+ Seeded to $80 $4F $C7 $52 $58 by the same 29-byte copy from $E3A2 that
621
+ installs CHRGET (BASIC cold-start init at $E3BF, not the KERNAL); nothing
622
+ else rewrites it except RND itself.
623
+
624
+ ### $0090-$0090 — STATUS (KERNAL I/O status)
625
+
626
+ **Default use:** I/O operation status flags; read by READST/CALLST
627
+ **Bank-switchable:** No
628
+
629
+ | Bit | Meaning (after IEC/cassette op) |
630
+ |-----|--------------------------------------------------|
631
+ | 0 | Time out write (IEC) |
632
+ | 1 | Time out read (IEC) |
633
+ | 2 | Short block (cassette) |
634
+ | 3 | Long block (cassette) |
635
+ | 4 | Unrecoverable read error (cassette) |
636
+ | 5 | Checksum error (cassette) |
637
+ | 6 | EOI (end-of-info) line set on IEC |
638
+ | 7 | Device not present (IEC) |
639
+
640
+ ST in BASIC reads this byte.
641
+
642
+ ### $0091-$0091 — Stop key flag
643
+
644
+ **Default use:** Set to $7F if RUN/STOP key was depressed at last keyboard scan
645
+ **Bank-switchable:** No
646
+
647
+ Polled by the KERNAL STOP routine ($FFE1). $7F means STOP pressed
648
+ *alone*. Any other value means STOP not pressed.
649
+
650
+ ### $0092-$0092 — Cassette timing constant
651
+
652
+ **Default use:** Timing adjustment for cassette read; KERNAL internal
653
+ **Bank-switchable:** No
654
+
655
+ ### $0093-$0093 — LOAD/VERIFY flag
656
+
657
+ **Default use:** $00 = LOAD, $01 = VERIFY for the next ROM LOAD call
658
+ **Bank-switchable:** No
659
+
660
+ ### $0094-$0094 — Serial output deferred char
661
+
662
+ **Default use:** Holds the next IEC byte to send (output buffer flag)
663
+ **Bank-switchable:** No
664
+
665
+ ### $0095-$0095 — Serial deferred char
666
+
667
+ **Default use:** Buffered byte for IEC output
668
+ **Bank-switchable:** No
669
+
670
+ ### $0096-$0096 — Cassette short-count
671
+
672
+ **Default use:** Cassette read short-count tracker
673
+ **Bank-switchable:** No
674
+
675
+ ### $0097-$0097 — Temp for register save during IRQ
676
+
677
+ **Default use:** Y-register temp during IRQ entry to GETIN
678
+ **Bank-switchable:** No
679
+
680
+ ### $0098-$0098 — Logical files open (LDTND)
681
+
682
+ **Default use:** Number of currently-open logical files; max 10
683
+ **Bank-switchable:** No
684
+
685
+ ### $0099-$0099 — Default input device (DFLTN)
686
+
687
+ **Default use:** Current input device (0 = keyboard)
688
+ **Bank-switchable:** No
689
+
690
+ Used by CHKIN/CHRIN. Changed by CHKIN to redirect input from a file.
691
+
692
+ ### $009A-$009A — Default output device (DFLTO)
693
+
694
+ **Default use:** Current output device (3 = screen)
695
+ **Bank-switchable:** No
696
+
697
+ Used by CHKOUT/CHROUT. Changed by CHKOUT to redirect output to a file.
698
+
699
+ ### $009B-$009B — Parity byte (cassette / IEC)
700
+
701
+ **Default use:** Running parity during cassette/IEC byte transfer
702
+ **Bank-switchable:** No
703
+
704
+ ### $009C-$009C — Byte-received flag
705
+
706
+ **Default use:** Flag for byte ready (IEC RS-232-ish protocols)
707
+ **Bank-switchable:** No
708
+
709
+ ### $009D-$009D — Direct/program mode message flag
710
+
711
+ **Default use:** $80 = direct mode (control messages on, error text off), $00 = program mode (both off)
712
+ **Bank-switchable:** No
713
+
714
+ Bit 7 = print KERNAL control messages (SEARCHING FOR, LOADING, VERIFYING,
715
+ SAVING, FOUND); bit 6 = print KERNAL error messages ('I/O ERROR #n'). An
716
+ earlier version of this page had the two bits swapped. BASIC writes $80
717
+ here in direct mode (READY, $A47B) and $00 when a program is RUN ($A872),
718
+ so a running program prints neither unless it calls SETMSG ($FF90, which
719
+ just stores A at $9D). $C0 prints both; $00 is silent. Two things this
720
+ flag does NOT control: the tape prompts 'PRESS PLAY ON TAPE' / 'PRESS
721
+ RECORD & PLAY ON TAPE' and the 'OK' after them are printed
722
+ unconditionally ($F81E/$F82B enter the printer at $F12F, past the $9D
723
+ test), and 'FILE NOT FOUND' is BASIC's message from its own error table,
724
+ not the KERNAL's — the KERNAL prints 'I/O ERROR #4' for that case.
725
+ Verified against the ROM bytes and in VICE x64sc.
726
+
727
+ ### $009E-$009F — Cassette block-read pointer + byte temp
728
+
729
+ **Default use:** KERNAL cassette internal
730
+ **Bank-switchable:** No
731
+
732
+ ### $00A0-$00A2 — Jiffy clock (TI variable)
733
+
734
+ **Default use:** 24-bit jiffy counter incremented by KERNAL IRQ; TI in BASIC
735
+ **Bank-switchable:** No
736
+
737
+ | Address | Meaning |
738
+ |---------|--------------------------|
739
+ | $00A0 | Jiffy MSB |
740
+ | $00A1 | Jiffy middle byte |
741
+ | $00A2 | Jiffy LSB |
742
+
743
+ Incremented once per KERNAL IRQ by UDTIM ($FFEA → $F69B). That IRQ is
744
+ CIA #1 Timer A, which the KERNAL sets at $FDDD from the $02A6 region flag
745
+ to ≈60 Hz on BOTH regions — latch $4025 on PAL (16,422 cycles, 59.996 Hz)
746
+ and $4295 on NTSC (17,046 cycles, 59.998 Hz), read from the 901227-03
747
+ ROM and confirmed in VICE x64sc (359 jiffies in 300 PAL frames, 301 in
748
+ 300 NTSC frames; a second run counted 298-299 ticks in 250 PAL frames).
749
+ So TI counts ~60ths of a second everywhere and runs very slightly *slow*
750
+ on both (0.007 % on PAL, 0.003 % on NTSC); UDTIM's 24-hour rollover at
751
+ $4F1A01 = 86,400 × 60 assumes the same 60/s. An earlier version of this
752
+ page said the PAL IRQ was 50 Hz and TI ran fast; neither was true. See
753
+ pal-ntsc-reference.md.
754
+
755
+ TI$ in BASIC formats the jiffy clock as "HHMMSS".
756
+
757
+ ### $00A3-$00A4 — Serial bit count + cassette bit timer
758
+
759
+ **Default use:** Workspace for serial/cassette bit-banging
760
+ **Bank-switchable:** No
761
+
762
+ ### $00A5-$00A6 — Cassette synchronization
763
+
764
+ **Default use:** Cassette sync mark counter
765
+ **Bank-switchable:** No
766
+
767
+ ### $00A7-$00A8 — Serial-byte buffer + cycle counter
768
+
769
+ **Default use:** Cassette and serial-bus workspace
770
+ **Bank-switchable:** No
771
+
772
+ ### $00A9-$00AD — Tape header / file workspace
773
+
774
+ **Default use:** Workspace for OPEN, LOAD, SAVE tape protocols
775
+ **Bank-switchable:** No
776
+
777
+ ### $00AE-$00AF — EAL: end address of the last LOAD/SAVE
778
+
779
+ **Default use:** One past the last byte loaded or saved
780
+ **Bank-switchable:** No
781
+
782
+ SAVE ($FFD8) takes the end address in X/Y and stores it here on entry
783
+ ($F5DD). LOAD uses it as the running store pointer (STA ($AE),Y / INC at
784
+ $F51C-$F522) and returns it in X/Y at exit ($F5AA-$F5AE), so after a
785
+ successful LOAD, $AE/$AF (or the returned X/Y) is the end of the program.
786
+ Also used as a temporary pointer by the screen-editor scroll
787
+ ($E8F0-$E9ED). Verified against kernal-901227-03 and a SAVE/LOAD round
788
+ trip in VICE x64sc.
789
+
790
+ ### $00B0-$00B1 — Tape header / file workspace
791
+
792
+ **Default use:** Workspace for OPEN, LOAD, SAVE tape protocols
793
+ **Bank-switchable:** No
794
+
795
+ ### $00B2-$00B3 — Tape buffer pointer
796
+
797
+ **Default use:** Pointer to next byte in tape I/O buffer at $033C
798
+ **Bank-switchable:** No
799
+
800
+ ### $00B4-$00BD — Cassette read/write internal state
801
+
802
+ **Default use:** Bit/byte/dword counters for tape protocol
803
+ **Bank-switchable:** No
804
+
805
+ ### $00BE-$00BE — Tape block count
806
+
807
+ **Default use:** Number of tape blocks remaining
808
+ **Bank-switchable:** No
809
+
810
+ ### $00BF-$00C0 — Serial output workspace
811
+
812
+ **Default use:** IEC clock-low timing constants
813
+ **Bank-switchable:** No
814
+
815
+ ### $00C1-$00C2 — Memory pointer #1 / Filename source
816
+
817
+ **Default use:** Start-address pointer for SAVE and for tape LOAD
818
+ **Bank-switchable:** No
819
+
820
+ Start address for SAVE (copied from the zero-page pointer named in A,
821
+ $F5E4/$F5E8) and for tape LOAD ($F59C/$F5A0); a serial LOAD does not use
822
+ it — it stores straight through $AE/$AF.
823
+
824
+ ### $00C3-$00C4 — MEMUSS: load address passed to LOAD
825
+
826
+ **Default use:** The X/Y the caller hands to LOAD ($FFD5); honoured only when the secondary address is 0 (relocating load)
827
+ **Bank-switchable:** No
828
+
829
+ LOAD's entry ($F49E) stores X/Y here before anything else. With SA=0 the
830
+ KERNAL copies it into $AE/$AF and loads there; with SA≠0 it is ignored
831
+ and the file's own address is used. A tape LOAD overwrites it with the
832
+ header's start address for non-relocatable (type 3) files or when SA≠0
833
+ ($F56C-$F575). The VECTOR routine ($FD1A) borrows it as the pointer to
834
+ the caller's vector table. SAVE never touches it. Earlier revisions of
835
+ this page called it the end-address pointer; that is $AE/$AF — verified
836
+ against the ROM bytes and a SAVE/LOAD round trip in VICE x64sc.
837
+
838
+ ### $00C5-$00C5 — Last key pressed (matrix code)
839
+
840
+ **Default use:** Raw matrix index of last key scanned by IRQ; $40 = no key
841
+ **Bank-switchable:** No
842
+
843
+ Updated 60 times per second by the KERNAL IRQ. *Not* the PETSCII code —
844
+ this is the row/column matrix index 0-63. The actual key code goes
845
+ through KEYTAB ($EB81-$ECB9) to produce a PETSCII byte that gets
846
+ written to the keyboard buffer at $0277.
847
+
848
+ ### $00C6-$00C6 — Number of chars in keyboard buffer (NDX)
849
+
850
+ **Default use:** Count of characters waiting in keyboard buffer; 0-10
851
+ **Bank-switchable:** No
852
+
853
+ Polled by GETIN. Stuff $C6 to a count and write characters to $0277-$0280
854
+ to simulate keyboard input.
855
+
856
+ ### $00C7-$00C7 — Reverse-video flag
857
+
858
+ **Default use:** $00 = normal, $12 = reverse video; controls upcoming PRINT chars
859
+ **Bank-switchable:** No
860
+
861
+ ### $00C8-$00C8 — End-of-line pointer
862
+
863
+ **Default use:** PRINT routine end-of-line column
864
+ **Bank-switchable:** No
865
+
866
+ ### $00C9-$00CA — Cursor X/Y on input
867
+
868
+ **Default use:** Cursor position saved at INPUT command entry
869
+ **Bank-switchable:** No
870
+
871
+ ### $00CB-$00CB — Key being processed
872
+
873
+ **Default use:** Current key matrix code being processed by IRQ
874
+ **Bank-switchable:** No
875
+
876
+ ### $00CC-$00CC — Cursor enable flag
877
+
878
+ **Default use:** $00 = blink enabled, $01 = disabled (set to $01 to suppress cursor)
879
+ **Bank-switchable:** No
880
+
881
+ ### $00CD-$00CD — Blink timer
882
+
883
+ **Default use:** Frames until next cursor blink
884
+ **Bank-switchable:** No
885
+
886
+ ### $00CE-$00CE — Character under cursor
887
+
888
+ **Default use:** Screen code of the character the cursor is sitting on
889
+ **Bank-switchable:** No
890
+
891
+ ### $00CF-$00CF — Cursor blink phase
892
+
893
+ **Default use:** $00 or $01 — which half of the blink cycle
894
+ **Bank-switchable:** No
895
+
896
+ ### $00D0-$00D0 — Input from screen flag
897
+
898
+ **Default use:** $00 = keyboard input, $03 = screen scroll buffer input
899
+ **Bank-switchable:** No
900
+
901
+ ### $00D1-$00D2 — Pointer to current screen line (PNT)
902
+
903
+ **Default use:** Address of first character of the line containing the cursor
904
+ **Bank-switchable:** No
905
+
906
+ The cursor line pointer. PNT + cursor X = the screen RAM byte for the
907
+ character about to be printed.
908
+
909
+ ### $00D3-$00D3 — Cursor column (X)
910
+
911
+ **Default use:** Cursor column 0-39
912
+ **Bank-switchable:** No
913
+
914
+ ### $00D4-$00D4 — Quote-mode flag
915
+
916
+ **Default use:** $01 = inside a quoted string in PRINT; controls control-char printing
917
+ **Bank-switchable:** No
918
+
919
+ ### $00D5-$00D5 — Screen line length
920
+
921
+ **Default use:** 39 for unwrapped lines, 79 for logical wrapped lines
922
+ **Bank-switchable:** No
923
+
924
+ ### $00D6-$00D6 — Cursor row (Y)
925
+
926
+ **Default use:** Cursor row 0-24
927
+ **Bank-switchable:** No
928
+
929
+ ### $00D7-$00D7 — Last key shift state / temp char
930
+
931
+ **Default use:** Last PETSCII char output; shift state during keyscan
932
+ **Bank-switchable:** No
933
+
934
+ ### $00D8-$00D8 — Insert count
935
+
936
+ **Default use:** Number of pending insert operations
937
+ **Bank-switchable:** No
938
+
939
+ ### $00D9-$00F2 — Screen line link table
940
+
941
+ **Default use:** 26-byte table — one entry per row plus a sentinel at $F2; bits 0-1 give the row's address high-byte offset, bit 7 set marks the start of a logical line
942
+ **Bank-switchable:** No
943
+
944
+ Bit 7 set means the row starts a logical line; clear means it is the
945
+ continuation of the row above (a fresh screen has bit 7 set on every
946
+ entry). The KERNAL walks this table to find a logical line's start and
947
+ length. (This page previously ended the table at $F1 and had a separate
948
+ "$00F2 — Insert mode count" entry; the KERNAL's fill loop at $E544
949
+ writes 26 entries and never addresses $F2 on its own — there is one
950
+ insert counter and it is $D8.)
951
+
952
+ ### $00F3-$00F4 — Color RAM pointer
953
+
954
+ **Default use:** Pointer to current line's color RAM (PNT + $D800)
955
+ **Bank-switchable:** No
956
+
957
+ ### $00F5-$00F6 — Keytable pointer
958
+
959
+ **Default use:** Pointer to one of four keyboard decode tables (selected by shift state)
960
+ **Bank-switchable:** No
961
+
962
+ ### $00F7-$00F8 — RS-232 input buffer pointer
963
+
964
+ **Default use:** Pointer to user-allocated RS-232 input ring buffer
965
+ **Bank-switchable:** No
966
+
967
+ ### $00F9-$00FA — RS-232 output buffer pointer
968
+
969
+ **Default use:** Pointer to user-allocated RS-232 output ring buffer
970
+ **Bank-switchable:** No
971
+
972
+ ### $00FB-$00FE — Free zero-page
973
+
974
+ **Default use:** Unused by KERNAL or BASIC; 4 bytes of free zero-page for user code
975
+ **Bank-switchable:** No
976
+
977
+ **Notes:** Together with $02 these are the five zero-page bytes neither
978
+ ROM touches after reset (this page used to say $FB-$FE were the only
979
+ four). Assembly that coexists with BASIC should use $FB-$FE and $02 for
980
+ its hottest pointers.
981
+
982
+ ### $00FF-$00FF — BASIC float-to-ASCII workspace
983
+
984
+ **Default use:** Temp byte used by BASIC's FOUT (float-to-string) routine
985
+ **Bank-switchable:** No
986
+
987
+ If BASIC's PRINT is not in use, $FF is free.
988
+
989
+ ### $0100-$01FF — Stack page
990
+
991
+ **Default use:** 6510 hardware stack (S pointer descends from $01FF)
992
+ **Bank-switchable:** No
993
+
994
+ The 6510 has a single 8-bit stack pointer S that fixes the stack on
995
+ memory page 1 ($0100-$01FF). Pushes write to $0100+S then decrement S;
996
+ pulls increment S then read $0100+S. The stack grows downward from
997
+ $01FF toward $0100.
998
+
999
+ The KERNAL reset ($FCE2) sets S = $FF and hands over with JMP ($A000),
1000
+ leaving nothing of its own on the stack. It is BASIC that lowers it: the
1001
+ cold start sets S = $FB ($E39D), and CLR -- run by NEW, CLR, RUN and the
1002
+ RUN/STOP-RESTORE warm start via $A67A -- sets S = $FA ($A681). A routine
1003
+ entered by SYS sees four bytes less again (NEWSTT's return address and
1004
+ SYS's own pushed $E146): S = $F7 from immediate mode straight after a
1005
+ cold start, S = $F6 from a RUN BASIC stub. Measured in VICE x64sc 3.10
1006
+ against the ROM images; an earlier revision attributed the $FB to the
1007
+ KERNAL.
1008
+
1009
+ **Subdivisions:**
1010
+
1011
+ - $0100-$010F shared with tape buffer index in some workflows
1012
+ - $01FA-$01FF reserved for KERNAL's deepest pushes during reset
1013
+ - Floating-point ASCII conversion (BASIC's FOUT, $BDDD) uses $00FF-$010F
1014
+ as a temporary buffer — $0100-$010F from PRINT, $00FF-$010E from STR$
1015
+ (measured in VICE; *Mapping the C64* p. 39 gives $0100-$010A, which is
1016
+ five bytes short)
1017
+
1018
+ **Notes:**
1019
+ - **Stack overflow wraps.** If S decrements past $00, it wraps to $FF
1020
+ and overwrites the top of the stack page. There is no overflow trap.
1021
+ - **JSR pushes return-address-minus-one.** RTS pulls then increments.
1022
+ This affects manual stack manipulation.
1023
+ - BASIC uses *much* more of the stack than a typical assembly program,
1024
+ especially with deep GOSUBs and nested FOR loops. Each GOSUB pushes
1025
+ 5 bytes; each FOR pushes 18 bytes.
1026
+
1027
+ ### $0200-$0258 — BASIC input buffer (BUF)
1028
+
1029
+ **Default use:** Direct-mode keyboard input buffer; lines from screen end up here
1030
+ **Bank-switchable:** No
1031
+
1032
+ 89-byte buffer. When you hit RETURN at the BASIC prompt, the screen line
1033
+ under the cursor is copied to $0200-$0258 (or until 89 chars or RETURN
1034
+ hits) and then tokenized.
1035
+
1036
+ ### $0259-$0262 — Logical file table (LAT)
1037
+
1038
+ **Default use:** 10-entry table of logical file numbers passed to OPEN
1039
+ **Bank-switchable:** No
1040
+
1041
+ ### $0263-$026C — Device number table (FAT)
1042
+
1043
+ **Default use:** Device numbers for each open logical file
1044
+ **Bank-switchable:** No
1045
+
1046
+ ### $026D-$0276 — Secondary address table (SAT)
1047
+
1048
+ **Default use:** Secondary addresses for each open logical file
1049
+ **Bank-switchable:** No
1050
+
1051
+ ### $0277-$0280 — Keyboard buffer (KEYD)
1052
+
1053
+ **Default use:** PETSCII keystrokes waiting to be consumed by GETIN; max 10 chars
1054
+ **Bank-switchable:** No
1055
+
1056
+ Stuff characters here and update NDX ($C6) to inject keystrokes — this
1057
+ is how autostart utilities feed BASIC a "LOAD..." command after reset.
1058
+
1059
+ ### $0281-$0282 — MEMSTR (start of basic memory)
1060
+
1061
+ **Default use:** Lowest address BASIC can use (default $0800)
1062
+ **Bank-switchable:** No
1063
+
1064
+ ### $0283-$0284 — MEMSIZ (top of memory)
1065
+
1066
+ **Default use:** Copy of top-of-RAM pointer set by MEMTOP at boot ($A000)
1067
+ **Bank-switchable:** No
1068
+
1069
+ ### $0285-$0285 — IEC timeout
1070
+
1071
+ **Default use:** Serial bus timeout flag
1072
+ **Bank-switchable:** No
1073
+
1074
+ ### $0286-$0286 — Current screen color (COLOR)
1075
+
1076
+ **Default use:** Current PRINT color — value 0-15 (next char printed uses this)
1077
+ **Bank-switchable:** No
1078
+
1079
+ Loaded by KERNAL CHROUT with the upper-nibble color of any color control
1080
+ char processed. Read by all subsequent PRINTs.
1081
+
1082
+ ### $0287-$0287 — Color under cursor
1083
+
1084
+ **Default use:** Color RAM value the cursor is currently sitting over
1085
+ **Bank-switchable:** No
1086
+
1087
+ ### $0288-$0288 — Screen memory page
1088
+
1089
+ **Default use:** High byte of current screen RAM start ($04 = $0400)
1090
+ **Bank-switchable:** No
1091
+
1092
+ Used by PLOT and CHROUT to compute screen addresses. Change this byte to
1093
+ move the screen to a different page; also update VIC-II $D018 and the
1094
+ line-link table to keep things consistent.
1095
+
1096
+ ### $0289-$0289 — Keyboard buffer max size
1097
+
1098
+ **Default use:** Maximum chars allowed in keyboard buffer (default 10)
1099
+ **Bank-switchable:** No
1100
+
1101
+ Lower this to zero to disable keyboard input; raise it to extend buffer
1102
+ (buffer area is only 10 bytes; raising doesn't extend storage).
1103
+
1104
+ ### $028A-$028A — Key repeat enable
1105
+
1106
+ **Default use:** Key-repeat mode (RPTFLG). Bit 7 set ($80) = all keys repeat; bit 6 set with bit 7 clear ($40) = no key repeats; $00 (the power-up value — RAMTAS clears it and nothing in the ROMs ever writes it) = only INST/DEL, SPACE and the four cursor keys repeat. Bit 7 wins when both are set (KERNAL $EAF2: BIT $028A / BMI / BVS, then CMP #$14/#$20/#$1D/#$11 — read from the 901227-03 ROM image; boot value measured in VICE x64sc). An earlier revision of this line had bit 6 and $00 the wrong way round.
1107
+ **Bank-switchable:** No
1108
+
1109
+ ### $028B-$028C — Repeat speed counter / countdown
1110
+
1111
+ **Default use:** Frames between key repeats
1112
+ **Bank-switchable:** No
1113
+
1114
+ ### $028D-$028D — Shift / Ctrl / C= state
1115
+
1116
+ **Default use:** Bitmask of shift modifier keys held down this frame
1117
+ **Bank-switchable:** No
1118
+
1119
+ | Value | Bit | Meaning |
1120
+ |-------|-----|------------------------------------------------------------------------------------------------------|
1121
+ | 1 | 0 | SHIFT — left shift, right shift or shift-lock, which the KERNAL does not distinguish (both matrix positions carry the value 1 in the $EB81 table) |
1122
+ | 2 | 1 | Commodore key |
1123
+ | 4 | 2 | CTRL |
1124
+
1125
+ There is no bit 3. The scanner ($EAB7) ORs the raw table values 1, 2 and
1126
+ 4 into $028D and never stores those keys as the keypress; the value 3 at
1127
+ index 63 is RUN/STOP and is treated as an ordinary key. Table selection
1128
+ ($EB48-$EB76): 0 -> unshifted $EB81, 1 -> shifted $EBC2, 2 -> C= $EC03,
1129
+ any value with CTRL set (4-7) -> CTRL $EC78. Value 3 (SHIFT+C=) does NOT
1130
+ go through that selector: on the frame it first appears, and unless
1131
+ $0291 bit 7 is set, the KERNAL flips $D018 bit 1 (upper/lower case) and
1132
+ decodes the key through the unshifted table, which $EA9B resets to $EB81
1133
+ at the start of every scan; while SHIFT+C= is held steady ($028D ==
1134
+ $028E) the scan exits at $EB42 without decoding any key. An earlier
1135
+ revision listed four bits (left shift, right shift, C=, CTRL as bits
1136
+ 0-3); the values were read from kernal-901227-03.bin.
1137
+
1138
+ ### $028E-$028E — Last shift state
1139
+
1140
+ **Default use:** Previous frame's $028D value (debouncing)
1141
+ **Bank-switchable:** No
1142
+
1143
+ ### $028F-$0290 — Keyboard table-setup vector (KEYLOG)
1144
+
1145
+ **Default use:** Vector to the routine that selects the keyboard decode table from the shift state; default $EB48 (set by CINT at $E522)
1146
+ **Bank-switchable:** No
1147
+
1148
+ The keyscan calls it with JMP ($028F) at $EADD; the routine reads the
1149
+ shift/Ctrl/C= flags in $028D, picks one of the four tables listed at
1150
+ $EB79 ($EB81 unshifted, $EBC2 shifted, $EC03 C=, $EC78 Ctrl) and stores
1151
+ that address in $F5/$F6, then exits with JMP $EAE0, where the keyscan
1152
+ fetches the PETSCII code with LDY $CB / LDA ($F5),Y. To install custom
1153
+ key tables, point $028F/$0290 at a routine that does the same — leave
1154
+ the table address in $F5/$F6 and exit via JMP $EAE0 — rather than at a
1155
+ table: this vector holds a code address, not a table address. (An
1156
+ earlier version of this entry called it a pointer to the decode table
1157
+ itself; it never was — the table pointer is $F5/$F6, see $00F5-$00F6.
1158
+ Read from the 901227-03 ROM and confirmed in VICE: $028F/$0290 = $48 $EB
1159
+ at boot.)
1160
+
1161
+ ### $0291-$0291 — Shift-lock / Commodore-shift switch
1162
+
1163
+ **Default use:** $00 = SHIFT+C= switches between upper/lower; $80 = locked
1164
+ **Bank-switchable:** No
1165
+
1166
+ ### $0292-$0292 — Scroll-down flag
1167
+
1168
+ **Default use:** $00 enables auto-scroll on cursor down past line 24
1169
+ **Bank-switchable:** No
1170
+
1171
+ ### $0293-$0297 — RS-232 workspace
1172
+
1173
+ **Default use:** Baud rate, parity, etc., for RS-232 implementation
1174
+ **Bank-switchable:** No
1175
+
1176
+ ### $0298-$029B — RS-232 status + buffer pointers
1177
+
1178
+ **Default use:** Software UART internal state
1179
+ **Bank-switchable:** No
1180
+
1181
+ ### $029C-$029F — RS-232 bit counters
1182
+
1183
+ **Default use:** Sample timing for software UART
1184
+ **Bank-switchable:** No
1185
+
1186
+ ### $02A0-$02A1 — RS-232 timer constants
1187
+
1188
+ **Default use:** CIA-derived bit timing constants for RS-232
1189
+ **Bank-switchable:** No
1190
+
1191
+ ### $02A2-$02A4 — Tape I/O temporaries
1192
+
1193
+ **Default use:** CIA #1 control/ICR images saved by the cassette code ($02A2 written at $F887 in the tape setup and read at $F911; $02A3 at $F94B/$F9B0 and $02A4 at $F917/$F9C0 in the tape read IRQ). Not RS-232: free only if you never touch tape.
1194
+ **Bank-switchable:** No
1195
+
1196
+ ### $02A5-$02A5 — Screen scroll temporary
1197
+
1198
+ **Default use:** Line index used by the screen editor's scroll and insert-line code ($E8EA-$E9C2: $E8FC, $E935, $E96C, $E978, $E993, $E9AB, $E9BF). Clobbered by any PRINT that scrolls the screen, whatever RS-232 is doing.
1199
+ **Bank-switchable:** No
1200
+
1201
+ ### $02A6-$02A6 — PAL/NTSC flag
1202
+
1203
+ **Default use:** 1 = PAL, 0 = NTSC. Written once by CINT ($FF5B: waits for $D012 = 0 after the VIC table set raster compare 311, then stores $D019 bit 0), read by IOINIT at $FDDD to pick the CIA #1 Timer A jiffy latch ($4025 PAL / $4295 NTSC) and by RS-232 OPEN at $F42C to pick the baud table. Leave it alone: RUN/STOP-RESTORE ($FE66) re-runs IOINIT but not CINT, so an overwritten flag is not re-derived and the jiffy clock and RS-232 timing switch to the other region (measured in VICE x64sc: storing 0 here and calling $FF84 on a PAL machine moves the Timer A latch from $4025 to $4295). Bit-exact in 901227-02 and -03; -01 has no detector and leaves this byte alone.
1204
+ **Bank-switchable:** No
1205
+
1206
+ ### $02A7-$02FF — Unused
1207
+
1208
+ **Default use:** Free for user code. No KERNAL or BASIC ROM write touches this range (census of absolute operands in 901227-03 / 901226-01); the only reads are two `BIT $02A9` skip idioms at $EFCF and $F6FD that discard the value. RAMTAS clears it at reset. An earlier version of this page lumped $02A2-$02A6 into this range as "extra RS-232 workspace, safe if RS-232 is not in use"; five of those bytes belong to the tape driver, the screen editor and the PAL/NTSC flag.
1209
+ **Bank-switchable:** No
1210
+
1211
+ ### $0300-$0301 — IERROR (BASIC error vector)
1212
+
1213
+ **Default use:** Pointer to BASIC's error-message routine; default $E38B
1214
+ **Bank-switchable:** No
1215
+
1216
+ Patch this to intercept all BASIC errors. The KERNAL hand-off into
1217
+ "ERROR" passes the error number in X.
1218
+
1219
+ ### $0302-$0303 — IMAIN (BASIC warm-start vector)
1220
+
1221
+ **Default use:** Pointer to BASIC's input-loop routine; default $A483
1222
+ **Bank-switchable:** No
1223
+
1224
+ Default is the BASIC interpreter ready-prompt loop. Patch this to take
1225
+ control after every command.
1226
+
1227
+ ### $0304-$0305 — ICRNCH (BASIC crunch / tokenize vector)
1228
+
1229
+ **Default use:** Pointer to BASIC's tokenize routine; default $A57C
1230
+ **Bank-switchable:** No
1231
+
1232
+ ### $0306-$0307 — IQPLOP (BASIC LIST vector)
1233
+
1234
+ **Default use:** Pointer to BASIC's de-tokenize routine; default $A71A
1235
+ **Bank-switchable:** No
1236
+
1237
+ ### $0308-$0309 — IGONE (BASIC execute-statement vector)
1238
+
1239
+ **Default use:** Pointer to BASIC's statement-dispatch routine; default $A7E4
1240
+ **Bank-switchable:** No
1241
+
1242
+ ### $030A-$030B — IEVAL (BASIC expression evaluator)
1243
+
1244
+ **Default use:** Pointer to BASIC's expression evaluator; default $AE86
1245
+ **Bank-switchable:** No
1246
+
1247
+ ### $030C-$030C — Saved A
1248
+
1249
+ **Default use:** Saved A register for SYS call argument-pass
1250
+ **Bank-switchable:** No
1251
+
1252
+ ### $030D-$030D — Saved X
1253
+
1254
+ **Default use:** Saved X for SYS
1255
+ **Bank-switchable:** No
1256
+
1257
+ ### $030E-$030E — Saved Y
1258
+
1259
+ **Default use:** Saved Y for SYS
1260
+ **Bank-switchable:** No
1261
+
1262
+ ### $030F-$030F — Saved P (flags)
1263
+
1264
+ **Default use:** Saved status flags for SYS
1265
+ **Bank-switchable:** No
1266
+
1267
+ ### $0310-$0312 — USR vector
1268
+
1269
+ **Default use:** USR()'s 3-byte jump instruction (JMP to user routine)
1270
+ **Bank-switchable:** No
1271
+
1272
+ The $4C opcode and the default target are installed by BASIC init
1273
+ ($E3BF), not by USR(): USR() jumps *through* $0310 (its entry in the
1274
+ function-dispatch table at $A058 is $0310 itself, dispatched via the JMP
1275
+ that the same init writes at $54). The default target is $B248 = LDX
1276
+ #$0E / JMP $A437, i.e. ?ILLEGAL QUANTITY ERROR, so POKE 785/786 (lo/hi)
1277
+ with the routine address before calling USR(). An earlier version said
1278
+ USR() sets this vector.
1279
+
1280
+ ### $0313-$0313 — Unused
1281
+
1282
+ **Default use:** Unused
1283
+ **Bank-switchable:** No
1284
+
1285
+ ### $0314-$0315 — CINV (IRQ vector)
1286
+
1287
+ **Default use:** Pointer to IRQ handler; default $EA31
1288
+ **Bank-switchable:** No
1289
+
1290
+ **This is THE vector** for replacing the IRQ. Every C64 demo that does
1291
+ raster effects writes to $0314/$0315 to point at its own raster handler.
1292
+
1293
+ ### $0316-$0317 — CBINV (BRK vector)
1294
+
1295
+ **Default use:** Pointer to BRK handler; default $FE66
1296
+ **Bank-switchable:** No
1297
+
1298
+ BRK instructions vector through here (which on a 6510 is the same line
1299
+ as IRQ; the handler reads the B flag to distinguish).
1300
+
1301
+ ### $0318-$0319 — NMINV (NMI vector)
1302
+
1303
+ **Default use:** Pointer to NMI handler; default $FE47
1304
+ **Bank-switchable:** No
1305
+
1306
+ NMI fires on RESTORE key and on CIA #2 events. The default NMI handler
1307
+ checks for RUN/STOP+RESTORE (warm boot) and returns.
1308
+
1309
+ ### $031A-$031B — IOPEN
1310
+
1311
+ **Default use:** Pointer to OPEN logic; default $F34A
1312
+ **Bank-switchable:** No
1313
+
1314
+ ### $031C-$031D — ICLOSE
1315
+
1316
+ **Default use:** Pointer to CLOSE logic; default $F291
1317
+ **Bank-switchable:** No
1318
+
1319
+ ### $031E-$031F — ICHKIN
1320
+
1321
+ **Default use:** Pointer to CHKIN logic; default $F20E
1322
+ **Bank-switchable:** No
1323
+
1324
+ ### $0320-$0321 — ICKOUT
1325
+
1326
+ **Default use:** Pointer to CHKOUT logic; default $F250
1327
+ **Bank-switchable:** No
1328
+
1329
+ ### $0322-$0323 — ICLRCH
1330
+
1331
+ **Default use:** Pointer to CLRCHN logic; default $F333
1332
+ **Bank-switchable:** No
1333
+
1334
+ ### $0324-$0325 — IBASIN
1335
+
1336
+ **Default use:** Pointer to CHRIN; default $F157
1337
+ **Bank-switchable:** No
1338
+
1339
+ Patching IBASIN intercepts all input from any device.
1340
+
1341
+ ### $0326-$0327 — IBSOUT
1342
+
1343
+ **Default use:** Pointer to CHROUT; default $F1CA
1344
+ **Bank-switchable:** No
1345
+
1346
+ Patching IBSOUT intercepts all output; the default lets KERNAL handle
1347
+ character output, but games and demos hook here to add character filters,
1348
+ implement custom screen managers, or capture printf-style debug streams.
1349
+
1350
+ ### $0328-$0329 — ISTOP
1351
+
1352
+ **Default use:** Pointer to STOP-key check; default $F6ED
1353
+ **Bank-switchable:** No
1354
+
1355
+ ### $032A-$032B — IGETIN
1356
+
1357
+ **Default use:** Pointer to GETIN; default $F13E
1358
+ **Bank-switchable:** No
1359
+
1360
+ ### $032C-$032D — ICLALL
1361
+
1362
+ **Default use:** Pointer to CLALL; default $F32F
1363
+ **Bank-switchable:** No
1364
+
1365
+ ### $032E-$032F — User vector (USRCMD)
1366
+
1367
+ **Default use:** Generic user-command vector; default $FE66
1368
+ **Bank-switchable:** No
1369
+
1370
+ ### $0330-$0331 — ILOAD
1371
+
1372
+ **Default use:** Pointer to LOAD; default $F4A5
1373
+ **Bank-switchable:** No
1374
+
1375
+ Patching ILOAD is the classic way to install a fastloader: when BASIC's
1376
+ LOAD command runs, control goes through this vector instead of the
1377
+ ROM's IEC-bus byte-banging routine.
1378
+
1379
+ ### $0332-$0333 — ISAVE
1380
+
1381
+ **Default use:** Pointer to SAVE; default $F5ED
1382
+ **Bank-switchable:** No
1383
+
1384
+ ### $0334-$033B — Unused KERNAL workspace
1385
+
1386
+ **Default use:** Reserved; usually safe for user code
1387
+ **Bank-switchable:** No
1388
+
1389
+ ### $033C-$03FB — Cassette tape buffer
1390
+
1391
+ **Default use:** 192-byte tape I/O buffer
1392
+ **Bank-switchable:** No
1393
+
1394
+ When the cassette is not in use, this 192-byte buffer is free RAM. It
1395
+ is a popular place to hide small machine-language routines (raster
1396
+ interrupt handlers, sprite editors). Programs that load themselves into
1397
+ this buffer and then exit can survive a tape LOAD.
1398
+
1399
+ ### $03FC-$03FF — Unused / sound workspace
1400
+
1401
+ **Default use:** Free RAM in default configuration
1402
+ **Bank-switchable:** No
1403
+
1404
+ ### $0400-$07E7 — Default screen RAM
1405
+
1406
+ **Default use:** 40 × 25 = 1000 screen-code character bytes
1407
+ **Bank-switchable:** No (always RAM; the *VIC* sees this address through its VIC bank)
1408
+
1409
+ The screen at $0400 is the default location after a cold boot. The
1410
+ VIC-II reads screen codes from here when fetching characters for the
1411
+ text display. Writing a character code (0-255) to $0400+row*40+col
1412
+ places that character on screen.
1413
+
1414
+ The screen code is *not* PETSCII — it is an 8-bit index into the
1415
+ character generator. In the default uppercase/graphics set, codes 0–31
1416
+ are @ A–Z [ £ ] ↑ ←, 32–63 are space, punctuation and the digits (48–57
1417
+ = 0–9), 64–127 are the graphics glyphs, and 128–255 are the same 128
1418
+ glyphs in reverse video. The lowercase set puts a–z at 1–26 and A–Z at
1419
+ 65–90. See [Character ROM layout](#character-rom-layout) below (ranges
1420
+ read from chargen-901225-01.bin; an earlier revision linked a
1421
+ vic-ii-reference section that does not exist).
1422
+
1423
+ To move the screen: set the upper nibble of $D018 to the 1 KB slot
1424
+ inside the current VIC bank (upper nibble × 1 KB = screen offset from
1425
+ the base of the VIC bank; e.g. $D018 = $3x puts the screen at bank+$0C00,
1426
+ and the default $14 gives $0400). An earlier revision said 4 × upper
1427
+ nibble, which would have put the default screen at $1000; measured in
1428
+ VICE x64sc: $D018 = $34 displays $0C00 and $C4 displays $3000. Then
1429
+ update $0288 (the KERNAL's idea of screen page) and the line-link table
1430
+ at $D9-$F2.
1431
+
1432
+ ### $07E8-$07F7 — Unused tail of the screen page
1433
+
1434
+ **Default use:** 16 bytes between the 1000-byte screen and the sprite pointers; the KERNAL neither clears nor uses them (measured in VICE x64sc: they still hold the RAM-init pattern after boot)
1435
+ **Bank-switchable:** No
1436
+
1437
+ ### $07F8-$07FF — Default sprite pointers
1438
+
1439
+ **Default use:** 8 bytes — one sprite pointer per hardware sprite (0-7)
1440
+ **Bank-switchable:** No (RAM region; visible to VIC inside VIC bank)
1441
+
1442
+ The eight bytes at the *very end* of screen memory hold the sprite
1443
+ pointers. Each byte's value × 64 is the address (within the current
1444
+ VIC bank) of that sprite's 63-byte data block.
1445
+
1446
+ By default sprite 0's data pointer is at $07F8, sprite 1's at $07F9, etc.
1447
+ If you relocate the screen, the sprite pointers move too (always 1016
1448
+ bytes past the start of screen RAM). (An earlier version of this page
1449
+ headed this entry $07E8-$07FF, a 24-byte range; the pointers are the
1450
+ last 8 bytes of the 1 KB screen page, screen base + $3F8.)
1451
+
1452
+ ### $0800-$9FFF — BASIC program area / general user RAM
1453
+
1454
+ **Default use:** Tokenized BASIC program + variables + free user RAM (38 KB = 38,912 bytes; 38,911 from $0801, the figure the boot screen prints — an earlier revision said 38.4 KB, which matches no byte count)
1455
+ **Bank-switchable:** No (always RAM; only $A000-$BFFF can swap to ROM)
1456
+
1457
+ **Subdivisions on cold boot:**
1458
+
1459
+ - $0800 = zero byte (BASIC program-start sentinel; mandatory)
1460
+ - $0801-VARTAB = tokenized BASIC program
1461
+ - VARTAB-ARYTAB = simple variables
1462
+ - ARYTAB-STREND = arrays
1463
+ - STREND-STRBOT = free RAM
1464
+ - STRBOT-MEMSIZ = string descriptor heap (grows down)
1465
+
1466
+ **Notes:**
1467
+ - A pure-assembly program with no BASIC stub can use $0801-$9FFF as 38 KB
1468
+ of contiguous free RAM.
1469
+ - Loading at $0801 with a BASIC stub like `1 SYS 2061` is the canonical
1470
+ way to autostart a machine-language program (2061 = $080D, the address
1471
+ just past the stub).
1472
+ - The screen at $0400-$07FF is a separate region — *not* part of the BASIC
1473
+ area despite being lower in memory.
1474
+
1475
+ ### $0800-$0800 — BASIC start sentinel
1476
+
1477
+ **Default use:** Always $00; BASIC program-start marker
1478
+ **Bank-switchable:** No
1479
+
1480
+ ### $0801-$9EFF — BASIC program + variables + free RAM
1481
+
1482
+ **Default use:** Tokenized BASIC + variables + heap
1483
+ **Bank-switchable:** No
1484
+
1485
+ ### $9F00-$9FFF — Top of BASIC RAM (just below ROM boundary)
1486
+
1487
+ **Default use:** Last 256 bytes of contiguous free RAM before $A000 ROM/RAM switch
1488
+ **Bank-switchable:** No
1489
+
1490
+ ### $A000-$BFFF — BASIC ROM region
1491
+
1492
+ **Default use:** 8 KB of BASIC interpreter ROM (when LORAM = 1 and HIRAM = 1)
1493
+ **Bank-switchable:** Yes — see [Bank switching](#bank-switching)
1494
+
1495
+ The BASIC interpreter lives here on a fresh boot. Bank out by clearing
1496
+ LORAM ($01 bit 0) to reveal the 8 KB of RAM underneath. The RAM is
1497
+ *always there* — writes to $A000-$BFFF always go to RAM. Reads return
1498
+ ROM bytes only when the BASIC ROM is banked in.
1499
+
1500
+ **Notes:**
1501
+ - This is the standard place to put a 256-cell character set (only need
1502
+ 2 KB at $1000 or $3000 inside the VIC bank — but the underlying RAM
1503
+ here is sometimes used for sprite data or extra screen pages).
1504
+ - A 16 KB cartridge (EXROM = 0, GAME = 0) maps its ROMH here whenever
1505
+ HIRAM = 1, whatever LORAM is (modes 2, 3, 6 and 7); with HIRAM = 0 the
1506
+ range is RAM even with the cart present. See
1507
+ [Cartridge modes](#cartridge-modes-exrom--game). (This note used to
1508
+ point at 'modes 8 and 16': mode 8 is an 8 KB cart with all three port
1509
+ bits low and is all RAM, and mode 16 is Ultimax with ROMH at $E000 -
1510
+ measured in VICE x64sc, default C64C model, not on a bench.)
1511
+
1512
+ ### $A000-$A00B — BASIC cold-start vectors
1513
+
1514
+ **Default use:** Cold-start jump table — first 12 bytes of BASIC ROM
1515
+ **Bank-switchable:** Yes
1516
+
1517
+ | Address | Content | Meaning |
1518
+ |----------|--------------|---------------------------------|
1519
+ | $A000 | $94 $E3 | Cold-start vector (jumps $E394) |
1520
+ | $A002 | $7B $E3 | Warm-start vector ($E37B) |
1521
+ | $A004 | $43 $42 $4D… | "CBMBASIC" ID string |
1522
+
1523
+ The cold-start vector at $A000-$A001 is where the KERNAL's reset routine
1524
+ ends up (`JMP ($A000)` at $FCFE, after IOINIT, RAMTAS, RESTOR and CINT).
1525
+ Cartridge auto-start does not involve $A000 at all. The reset routine
1526
+ ($FCE2) first calls the signature check at $FD02, which compares
1527
+ $8004-$8008 with `$C3 $C2 $CD $38 $30` ("CBM80", held at $FD10) and, on
1528
+ a match, does `JMP ($8000)` — the cartridge *cold*-start vector — before
1529
+ any RAM or I/O initialisation. A cart ROM's first nine bytes are
1530
+ therefore: cold vector (2), warm vector (2), `CBM80` (5), at
1531
+ $8000-$8008. The warm vector at $8002 is taken by the RESTORE/NMI
1532
+ handler ($FE56: `JSR $FD02; BNE; JMP ($8002)`), and only when CIA #2 did
1533
+ not raise the NMI. See the $8000 pitfall below. (An earlier version of
1534
+ this page put the signature at $A000 and had reset jump through the warm
1535
+ vector; both were wrong — at reset $A000 is BASIC ROM, whose first bytes
1536
+ are the `$94 $E3 $7B $E3 'CBMBASIC'` shown above. Verified against
1537
+ kernal-901227-03 bytes and in VICE x64sc 3.10.)
1538
+
1539
+ ### $A00C-$A389 — BASIC dispatch tables, keywords, messages
1540
+
1541
+ **Default use:** Statement dispatch ($A00C, entry-1), function dispatch ($A052), operator table, keyword text ($A09E), error messages, "OK"/"ERROR"/"IN"/"READY."/"BREAK" strings ($A364-$A389)
1542
+ **Bank-switchable:** Yes
1543
+
1544
+ ### $A38A-$A47F — Stack search, memory checks, ERROR, READY
1545
+
1546
+ **Default use:** FOR-stack search ($A38A), stack/memory checks ($A3FB, $A408), ERROR ($A437 = JMP ($0300)), READY ($A474)
1547
+ **Bank-switchable:** Yes
1548
+
1549
+ ### $A480-$A641 — BASIC main loop, line entry, CRUNCH
1550
+
1551
+ **Default use:** Main loop ($A480 = JMP ($0302), $A483), insert line, INLIN ($A560), CRUNCH ($A57C), FNDLIN ($A613)
1552
+ **Bank-switchable:** Yes
1553
+
1554
+ ### $A642-$A741 — NEW, CLR, LIST
1555
+
1556
+ **Default use:** NEW ($A642), CLR ($A65E), LIST ($A69C, detokenize $A71A)
1557
+ **Bank-switchable:** Yes
1558
+
1559
+ ### $A742-$A9A4 — FOR, statement loop, control-flow statements
1560
+
1561
+ **Default use:** FOR ($A742), NEWSTT/GONE ($A7AE/$A7E4), RESTORE ($A81D), STOP/END ($A82F/$A831), CONT ($A857), RUN ($A871), GOSUB ($A883), GOTO ($A8A0), RETURN ($A8D2), DATA ($A8F8), IF ($A928), REM ($A93B), ON ($A94B), LINGET
1562
+ **Bank-switchable:** Yes
1563
+
1564
+ ### $A9A5-$AC05 — LET, PRINT, GET, INPUT
1565
+
1566
+ **Default use:** LET ($A9A5), PRINT# ($AA80), CMD ($AA86), PRINT ($AAA0), STROUT ($AB1E), GET ($AB7B), INPUT# ($ABA5), INPUT ($ABBF)
1567
+ **Bank-switchable:** Yes
1568
+
1569
+ ### $AC06-$AD1D — READ
1570
+
1571
+ **Default use:** READ ($AC06) and its "?EXTRA IGNORED"/"?REDO FROM START" messages
1572
+ **Bank-switchable:** Yes
1573
+
1574
+ ### $AD1E-$AD89 — NEXT
1575
+
1576
+ **Default use:** NEXT ($AD1E)
1577
+ **Bank-switchable:** Yes
1578
+
1579
+ ### $AD8A-$B07D — Expression evaluator
1580
+
1581
+ **Default use:** FRMNUM ($AD8A), CHKNUM ($AD8D), FRMEVL ($AD9E), EVAL ($AE86), string concatenation entry via $ADE5
1582
+ **Bank-switchable:** Yes
1583
+
1584
+ ### $B07E-$B08A — DIM
1585
+
1586
+ **Default use:** DIM (dispatch entry $B081)
1587
+ **Bank-switchable:** Yes
1588
+
1589
+ ### $B08B-$B3B2 — Variable lookup, FRE, POS
1590
+
1591
+ **Default use:** PTRGET ($B08B) variable/array lookup, FRE ($B37D), POS ($B39E)
1592
+ **Bank-switchable:** Yes
1593
+
1594
+ ### $B3B3-$B6DA — DEF/FN, string allocation, garbage collection
1595
+
1596
+ **Default use:** DEF/FN ($B3B3), STR$ ($B465), string allocation ($B475), garbage collection ($B526), concatenation ($B63D), string move ($B67A)
1597
+ **Bank-switchable:** Yes
1598
+
1599
+ ### $B6DB-$B80C — String functions
1600
+
1601
+ **Default use:** CHR$ ($B6EC), LEFT$ ($B700), RIGHT$ ($B72C), MID$ ($B737), LEN ($B77C), ASC ($B78B), VAL ($B7AD), GETNUM/GETADR ($B7EB/$B7F7); the block opens with FRETMS ($B6DB), the string-descriptor-stack pop
1602
+ **Bank-switchable:** Yes
1603
+
1604
+ ### $B80D-$BAE1 — PEEK/POKE/WAIT and the floating-point add/multiply package
1605
+
1606
+ **Default use:** PEEK ($B80D), POKE ($B824), WAIT ($B82D), FADDH/FSUB/FADD ($B849/$B850/$B867), LOG ($B9EA), FMULT ($BA28), CONUPK ($BA8C)
1607
+ **Bank-switchable:** Yes
1608
+
1609
+ This is the famous "Microsoft BASIC math". FADD is at $B867 (FADDT
1610
+ $B86A; FSUB $B850, FMULT $BA28, FDIV $BB0F, FIN $BCF3, FOUT $BDDD, SQR
1611
+ $BF71) — an earlier revision said $B6DB, which is FRETMS, the
1612
+ string-descriptor-stack pop. The package runs on past $BAE1 to EXP at
1613
+ $BFED, which ends with JMP $E000: SIN, COS, TAN, ATN and RND live in the
1614
+ KERNAL ROM ($E26B, $E264, $E2B4, $E30E, $E097). Demo coders sometimes
1615
+ bank in BASIC just to call these.
1616
+
1617
+ ### $BAE2-$BFFF — Rest of the floating-point package (FIN, FOUT, SQR, EXP)
1618
+
1619
+ **Default use:** MUL10 ($BAE2), DIV10 ($BAFE), FDIV ($BB0F), MOVFM ($BBA2), MOVMF ($BBD4), MOVFA ($BBFC), MOVAF ($BC0C), SGN ($BC39), ABS ($BC58), FCOMP ($BC5B), INT ($BCCC), FIN ($BCF3), FOUT ($BDDD), SQR ($BF71), power, EXP ($BFED; ends with JMP $E000 and continues in the KERNAL ROM)
1620
+ **Bank-switchable:** Yes
1621
+
1622
+ An earlier version of this sub-map placed the string functions at $A9A5
1623
+ (that is LET), FIN/FOUT in $AF7E-$B6DA (they are at $BCF3/$BDDD) and
1624
+ "initialization, sign-on message" in $BAE2-$BFFF; BASIC's cold start
1625
+ ($E394), warm start ($E37B), init ($E3BF), default vector table ($E447)
1626
+ and sign-on text ($E460ff), plus the LOAD/SAVE/VERIFY/SYS/OPEN/CLOSE
1627
+ statement handlers ($E12A-$E1C7) and RND/COS/SIN/TAN/ATN, live in the
1628
+ KERNAL ROM, where EXP also finishes. Ranges read from basic-901226-01.bin
1629
+ and kernal-901227-03.bin.
1630
+
1631
+ ### $C000-$CFFF — Free RAM (never banked)
1632
+
1633
+ **Default use:** 4 KB of always-RAM, never overlaid by any ROM or I/O
1634
+ **Bank-switchable:** No
1635
+
1636
+ This 4 KB region is special: it is the **largest contiguous chunk of RAM
1637
+ guaranteed to be RAM in every bank configuration**. Cartridges can't map
1638
+ here; KERNAL doesn't touch it; BASIC doesn't use it.
1639
+
1640
+ Everyone uses $C000 for machine-language routines that need to coexist
1641
+ with BASIC. The canonical pattern is to write a BASIC loader that POKEs
1642
+ a small ML routine into $C000 and then SYS 49152 to call it.
1643
+
1644
+ **Notes:**
1645
+ - Some software uses $C800-$CFFF as a 2 KB custom character set since
1646
+ it falls within VIC bank 3 at $C000-$FFFF — but only after relocating
1647
+ the VIC bank. By default the VIC sees bank 0 ($0000-$3FFF) and would
1648
+ not fetch from $C000.
1649
+ - Cassette I/O does NOT use $C000-$CFFF (it uses $033C-$03FB only).
1650
+
1651
+ ### $D000-$D02E — VIC-II registers
1652
+
1653
+ **Default use:** 47 VIC-II registers (when CHAREN = 1 and I/O is banked in)
1654
+ **Bank-switchable:** Yes — character ROM or RAM replaces this when CHAREN = 0
1655
+
1656
+ Full per-register details are in [vic-ii-reference.md](vic-ii-reference.md).
1657
+
1658
+ | Range | Function |
1659
+ |-------------|------------------------------------------------|
1660
+ | $D000-$D00F | Sprite X/Y positions (8 sprites × 2 bytes) |
1661
+ | $D010 | Sprite X MSBs |
1662
+ | $D011 | Screen control 1 / Y-scroll / display enable |
1663
+ | $D012 | Raster compare / current raster line |
1664
+ | $D013-$D014 | Light pen X, Y (latched) |
1665
+ | $D015 | Sprite enable |
1666
+ | $D016 | Screen control 2 / X-scroll / 38-col |
1667
+ | $D017 | Sprite Y expand |
1668
+ | $D018 | Memory pointers (video matrix / charset) |
1669
+ | $D019 | IRQ status (write 1 to clear) |
1670
+ | $D01A | IRQ mask |
1671
+ | $D01B | Sprite-to-background priority |
1672
+ | $D01C | Sprite multicolor enable |
1673
+ | $D01D | Sprite X expand |
1674
+ | $D01E-$D01F | Collision registers (sprite-sprite / sprite-BG)|
1675
+ | $D020-$D024 | Border + background colors |
1676
+ | $D025-$D026 | Sprite multicolor (shared) |
1677
+ | $D027-$D02E | Sprite 0-7 individual colors |
1678
+
1679
+ ### $D02F-$D03F — VIC-II unused / shadow padding
1680
+
1681
+ **Default use:** Unused; reads as $FF
1682
+ **Bank-switchable:** Yes
1683
+
1684
+ ### $D040-$D3FF — VIC-II shadow mirrors
1685
+
1686
+ **Default use:** $D000-$D03F repeats every 64 bytes through $D3FF
1687
+ **Bank-switchable:** Yes
1688
+
1689
+ Reading $D040 returns the same value as $D000, and so on. Avoid relying
1690
+ on this — direct-address all VIC writes in the $D000-$D03F window.
1691
+
1692
+ ### $D400-$D418 — SID registers
1693
+
1694
+ **Default use:** 25 SID write registers (voice control, filter, master volume)
1695
+ **Bank-switchable:** Yes — character ROM or RAM replaces this when CHAREN = 0
1696
+
1697
+ Full per-register details are in [sid-reference.md](sid-reference.md).
1698
+
1699
+ | Range | Function |
1700
+ |-------------|------------------------------------------------|
1701
+ | $D400-$D406 | Voice 1 (freq lo/hi, pulse lo/hi, ctrl, AD, SR)|
1702
+ | $D407-$D40D | Voice 2 (same layout) |
1703
+ | $D40E-$D414 | Voice 3 (same layout) |
1704
+ | $D415-$D416 | Filter cutoff lo/hi |
1705
+ | $D417 | Filter resonance / voice routing |
1706
+ | $D418 | Master volume + filter type select |
1707
+
1708
+ ### $D419-$D41C — SID read registers
1709
+
1710
+ **Default use:** Paddle X/Y, oscillator 3 / envelope 3 readback
1711
+ **Bank-switchable:** Yes
1712
+
1713
+ | Addr | Function |
1714
+ |--------|-------------------------------------------|
1715
+ | $D419 | Paddle X (POTX) |
1716
+ | $D41A | Paddle Y (POTY) |
1717
+ | $D41B | Voice 3 oscillator readback (OSC3) |
1718
+ | $D41C | Voice 3 envelope readback (ENV3) |
1719
+
1720
+ ### $D41D-$D41F — SID unused
1721
+
1722
+ **Default use:** Reads return last data-bus value (open bus)
1723
+ **Bank-switchable:** Yes
1724
+
1725
+ ### $D420-$D7FF — SID shadow mirrors
1726
+
1727
+ **Default use:** $D400-$D41F repeats every 32 bytes through $D7FF
1728
+ **Bank-switchable:** Yes
1729
+
1730
+ Like the VIC mirrors, every 32-byte block from $D420 onward is an alias
1731
+ of $D400-$D41F. The SID's last register $D41C cuts off at 29; writes to
1732
+ $D41D-$D41F have no effect.
1733
+
1734
+ ### $D800-$DBE7 — Color RAM (visible 1000 bytes)
1735
+
1736
+ **Default use:** 4-bit color attribute per character cell on screen
1737
+ **Bank-switchable:** Yes — character ROM replaces this when CHAREN = 0
1738
+
1739
+ The Color RAM is a separate 1024 × 4-bit static RAM chip. Each byte
1740
+ corresponds positionally with screen RAM at $0400-$07E7: writing $1
1741
+ to $D800 makes the character at top-left ($0400) display as color 1
1742
+ (white).
1743
+
1744
+ Only the lower 4 bits matter; the upper 4 bits are open bus and read
1745
+ random values. *Always mask with #$0F when reading Color RAM*.
1746
+
1747
+ ### $DBE8-$DBFF — Color RAM (unused 24 bytes)
1748
+
1749
+ **Default use:** 24 bytes of Color RAM not aligned with any visible cell
1750
+ **Bank-switchable:** Yes
1751
+
1752
+ The Color RAM is 1024 bytes but only 1000 cells are on screen. The
1753
+ trailing 24 bytes ($DBE8-$DBFF, immediately past the last screen cell at
1754
+ $DBE7) are still RAM and can hold 24 nibbles of program state. An
1755
+ earlier revision of this page headed this block $D8E8-$DBFF, which is a
1756
+ 792-byte range starting at row 5, column 32 of the visible screen;
1757
+ $D800 + 1000 = $DBE8 is the first unused byte (measured in VICE x64sc: a
1758
+ write to $DBE7 colours cell 24/39, a write to $DBE8 colours nothing).
1759
+
1760
+ ### $DC00-$DC0F — CIA #1 registers
1761
+
1762
+ **Default use:** Keyboard scan, joystick port 2, IRQ timer
1763
+ **Bank-switchable:** Yes — character ROM or RAM replaces this when CHAREN = 0
1764
+
1765
+ Full details in [cia-reference.md](cia-reference.md).
1766
+
1767
+ | Addr | Function |
1768
+ |--------|-------------------------------------------------|
1769
+ | $DC00 | Data port A (keyboard col output, joy 2) |
1770
+ | $DC01 | Data port B (keyboard row input, joy 1) |
1771
+ | $DC02 | Data direction A |
1772
+ | $DC03 | Data direction B |
1773
+ | $DC04 | Timer A low |
1774
+ | $DC05 | Timer A high |
1775
+ | $DC06 | Timer B low |
1776
+ | $DC07 | Timer B high |
1777
+ | $DC08 | TOD tenths |
1778
+ | $DC09 | TOD seconds |
1779
+ | $DC0A | TOD minutes |
1780
+ | $DC0B | TOD hours |
1781
+ | $DC0C | Serial shift register |
1782
+ | $DC0D | Interrupt control |
1783
+ | $DC0E | Control A |
1784
+ | $DC0F | Control B |
1785
+
1786
+ CIA #1's IRQ line drives the 6510's IRQ pin. The KERNAL's 60 Hz IRQ
1787
+ ticks via CIA #1 Timer A.
1788
+
1789
+ ### $DC10-$DCFF — CIA #1 shadow mirrors
1790
+
1791
+ **Default use:** $DC00-$DC0F repeats every 16 bytes through $DCFF
1792
+ **Bank-switchable:** Yes
1793
+
1794
+ ### $DD00-$DD0F — CIA #2 registers
1795
+
1796
+ **Default use:** Serial IEC bus, VIC-II bank select, RS-232 port, user port
1797
+ **Bank-switchable:** Yes
1798
+
1799
+ | Addr | Function |
1800
+ |--------|-------------------------------------------------|
1801
+ | $DD00 | Data port A (IEC bus + VIC-II bank select bits) |
1802
+ | $DD01 | Data port B (user port / RS-232) |
1803
+ | $DD02 | Data direction A |
1804
+ | $DD03 | Data direction B |
1805
+ | $DD04 | Timer A low |
1806
+ | $DD05 | Timer A high |
1807
+ | $DD06 | Timer B low |
1808
+ | $DD07 | Timer B high |
1809
+ | $DD08 | TOD tenths |
1810
+ | $DD09 | TOD seconds |
1811
+ | $DD0A | TOD minutes |
1812
+ | $DD0B | TOD hours |
1813
+ | $DD0C | Serial shift register |
1814
+ | $DD0D | Interrupt control |
1815
+ | $DD0E | Control A |
1816
+ | $DD0F | Control B |
1817
+
1818
+ CIA #2's IRQ line drives the 6510's NMI pin (not IRQ). The RESTORE key
1819
+ also pulls /NMI low — in parallel with CIA2's /IRQ, not through the CIA,
1820
+ so no $DD0D write touches it (this line used to say "through CIA #2";
1821
+ see `pitfalls/kernal-and-io.md` → `restore_nmi_not_maskable`).
1822
+
1823
+ **$DD00 bits 0-1** select the VIC-II bank, *inverted*:
1824
+
1825
+ | Bits | VIC bank | Address range |
1826
+ |------|----------|----------------|
1827
+ | 11 | 0 | $0000-$3FFF |
1828
+ | 10 | 1 | $4000-$7FFF |
1829
+ | 01 | 2 | $8000-$BFFF |
1830
+ | 00 | 3 | $C000-$FFFF |
1831
+
1832
+ ### $DD10-$DDFF — CIA #2 shadow mirrors
1833
+
1834
+ **Default use:** $DD00-$DD0F repeats every 16 bytes through $DDFF
1835
+ **Bank-switchable:** Yes
1836
+
1837
+ ### $DE00-$DEFF — I/O expansion area 1
1838
+
1839
+ **Default use:** Cartridge I/O area 1 (mapped only when a cartridge presents I/O)
1840
+ **Bank-switchable:** Yes — open bus when no cartridge present
1841
+
1842
+ On a bare machine, reads from $DE00-$DEFF return open-bus values
1843
+ (usually echoes of the most recent VIC fetch). Cartridges with an
1844
+ I/O space (REU, geoRAM, IDE64, etc.) decode their registers here.
1845
+
1846
+ ### $DF00-$DFFF — I/O expansion area 2
1847
+
1848
+ **Default use:** Cartridge I/O area 2 (mapped only when a cartridge presents I/O)
1849
+ **Bank-switchable:** Yes — open bus when no cartridge present
1850
+
1851
+ Same logic as area 1. The REU at $DF00-$DF0A is the most prominent user.
1852
+
1853
+ ### $E000-$FFFF — KERNAL ROM region
1854
+
1855
+ **Default use:** 8 KB of KERNAL ROM (when HIRAM = 1)
1856
+ **Bank-switchable:** Yes — bank out by clearing HIRAM ($01 bit 1)
1857
+
1858
+ Holds the I/O routines, IRQ handler, BRK handler, NMI handler,
1859
+ character output, screen editor, IEC bus driver, cassette driver, RS-232
1860
+ driver, and the public jump table at $FF81-$FFF5.
1861
+
1862
+ Writes to $E000-$FFFF go to RAM regardless of HIRAM. To use this 8 KB
1863
+ as RAM, bank out KERNAL and read it back: `LDA #$35 / STA $01` puts
1864
+ KERNAL out and I/O still in.
1865
+
1866
+ The sub-map below was rebuilt from the 901227-03 jump table and the
1867
+ $FD30 vector table because ten of its earlier headings named routines
1868
+ that live elsewhere — RESET at $FCE2 not $FCFC, RAMTAS at $FD50 not
1869
+ $FD90+, SCNKEY at $EA87 not $E4AC, the keyboard decode tables at
1870
+ $EB81-$ECB8 not $ECE7, SETMSG/MEMTOP/MEMBOT at $FE18/$FE25/$FE34 not
1871
+ $F130, the IRQ handler at $EA31-$EA86 not $E97D, and the cassette read
1872
+ IRQ at $F92C where "Disk LOAD/SAVE" stood. Every address below was read
1873
+ from the ROM image; the routine names are the conventional Commodore
1874
+ labels.
1875
+
1876
+ ### $E000-$E393 — BASIC ROM continuation (EXP tail, RND, trig, BASIC I/O statements, warm start)
1877
+
1878
+ **Default use:** Not KERNAL code: EXP finishes here ($E000, entered by JMP from $BFED), the polynomial evaluator ($E043/$E059), RND ($E097), the SYS/SAVE/VERIFY/LOAD/OPEN/CLOSE statement handlers ($E12A-$E1C7), COS/SIN/TAN/ATN ($E264/$E26B/$E2B4/$E30E) and the RUN/STOP-RESTORE warm start ($E37B, reached through the $A002 vector)
1879
+ **Bank-switchable:** Yes
1880
+
1881
+ ### $E394-$E4AB — BASIC cold start, init, default vectors, sign-on text
1882
+
1883
+ **Default use:** Cold start $E394 (the $A000 vector), CHRGET/RND-seed image $E3A2, init $E3BF (copies CHRGET to $73, installs $03-$06, $54 and $0310, snapshots MEMTOP), default BASIC vector table $E447 (copied to $0300-$030B), sign-on text $E460-$E4AB printed from $E473 (" BASIC BYTES FREE" precedes "**** COMMODORE 64 BASIC V2 ****", which starts at $E47E)
1884
+ **Bank-switchable:** Yes
1885
+
1886
+ ### $E4AC-$E4FF — Revision patch area
1887
+
1888
+ **Default use:** Code added in 901227-02/-03: CHKOUT wrapper patch $E4AD (called from BASIC's $E118), RS-232 receive patch $E4D3, screen-clear colour $E4DA, FOUND-message pause $E4E0, PAL RS-232 baud table $E4EC; $E4B7-$E4D2 are unused ($AA fill)
1889
+ **Bank-switchable:** Yes
1890
+
1891
+ ### $E500-$E5C9 — IOBASE, SCREEN, PLOT, CINT body, screen clear, VIC init
1892
+
1893
+ **Default use:** IOBASE $E500 (returns $DC00), SCREEN $E505 (40 × 25), PLOT $E50A, CINT body $E518 (sets $028F/$0290 = $EB48 and the editor defaults, then VIC init $E5A0 from the $ECB9 table), clear screen $E544 (also fills the $D9-$F2 link table), home $E566, cursor set-up $E56C, keyboard-buffer fetch $E5B4
1894
+ **Bank-switchable:** Yes
1895
+
1896
+ ### $E5CA-$EA30 — Screen editor (keyboard/screen input, CHROUT screen body, scroll)
1897
+
1898
+ **Default use:** Keyboard input $E5CA, input from screen $E632, CHROUT's screen-device body $E716 (control codes, colour codes, cursor movement), scroll $E8EA, insert line $E965, screen-line set-up $E9F0, print-to-screen $EA13 and colour-pointer sync $EA24
1899
+ **Bank-switchable:** Yes
1900
+
1901
+ ### $EA31-$EA86 — Default IRQ handler
1902
+
1903
+ **Default use:** The service routine $0314/$0315 points to: jiffy clock, cursor blink, tape-motor sense, keyboard scan, CIA #1 acknowledge, register restore and RTI
1904
+ **Bank-switchable:** Yes
1905
+
1906
+ The default IRQ handler at $EA31 is what $0314/$0315 points to. Read
1907
+ from the kernal-901227-03 ROM bytes, it:
1908
+
1909
+ 1. Calls UDTIM ($FFEA -> $F69B) to bump the jiffy clock and sample the
1910
+ STOP key.
1911
+ 2. Runs the cursor blink ($EA34-$EA60), skipped when $CC is non-zero.
1912
+ 3. Handles the cassette-switch / tape-motor sense via $01 ($EA61-$EA79).
1913
+ 4. Calls SCNKEY ($EA7B: JSR $EA87).
1914
+ 5. Reads $DC0D to acknowledge CIA #1 — last, at $EA7E.
1915
+ 6. Restores Y, X and A and RTIs ($EA81-$EA86).
1916
+
1917
+ Replacing $0314/$0315 with your own handler lets you intercept this
1918
+ sequence — typically you JMP $EA31 to run the whole service after your
1919
+ raster routine completes, or JMP $EA81 for the bare register-restore
1920
+ exit. $EA7E is not a cursor-skipping shortcut: it is the
1921
+ acknowledge-and-exit tail (LDA $DC0D, then $EA81's PLA/TAY/PLA/TAX/PLA/
1922
+ RTI), so entering there skips the jiffy clock, the STOP-key sense and
1923
+ the keyboard scan as well as the cursor. Measured in VICE x64sc: with
1924
+ $0314 pointed at $EA7E, TI did not advance in one second (0 jiffies,
1925
+ against 64 through $EA31). An earlier version of this page listed the
1926
+ $DC0D read first and described $EA7E as a shorter path that skipped only
1927
+ the cursor; the ROM bytes say otherwise.
1928
+
1929
+ ### $EA87-$ECE6 — SCNKEY keyboard scanner, key decode and key tables
1930
+
1931
+ **Default use:** SCNKEY body $EA87 (jump table $FF9F; $EA9B resets the table pointer to $EB81), KEYLOG table-select routine $EB48 (reached via $028F/$0290; toggles $D018 bit 1 on SHIFT+C= at $EB5E), table pointers $EB79, decode tables $EB81 (unshifted), $EBC2 (shifted), $EC03 (C=), the CHR$(14)/CHR$(142) case switch $EC44-$EC5B, CTRL table $EC78-$ECB8, 47-byte VIC-II power-on register table $ECB9-$ECE6
1932
+ **Bank-switchable:** Yes
1933
+
1934
+ ### $ECE7-$ED08 — "LOAD/RUN" string and screen-line low-byte table
1935
+
1936
+ **Default use:** $ECE7-$ECEF is the PETSCII string `LOAD<CR>RUN<CR>` that SHIFT+RUN/STOP stuffs into the keyboard buffer; $ECF0-$ED08 is the 25-entry table of screen-line start low bytes ($00 $28 $50 …). Not the keyboard decode tables, which are at $EB81-$ECB8 — an earlier version of this page labelled this range KEYTAB
1937
+ **Bank-switchable:** Yes
1938
+
1939
+ ### $ED09-$EEBA — IEC bus driver
1940
+
1941
+ **Default use:** TALK $ED09, LISTEN $ED0C, SECOND $EDB9, TKSA $EDC7, IECOUT $EDDD, UNTLK $EDEF, UNLSN $EDFE, IECIN $EE13, clock/data line helpers to $EEB2, 1 ms delay $EEB3 — the only span of the ROM that touches $DD00
1942
+ **Bank-switchable:** Yes
1943
+
1944
+ ### $EEBB-$F0BC — RS-232 driver
1945
+
1946
+ **Default use:** Software UART: send $EEBB, receive, buffer handling; driven from CIA #2 timers through the NMI and touching only $DD01/$DD0D. (An earlier version of this page began the RS-232 driver at $EF94 and ran the IEC driver to $EF93; the IEC code ends at $EEBA.)
1947
+ **Bank-switchable:** Yes
1948
+
1949
+ ### $F0BD-$F12A — KERNAL message text
1950
+
1951
+ **Default use:** The control/error message table: I/O ERROR #, SEARCHING FOR, PRESS PLAY ON TAPE, PRESS RECORD & PLAY ON TAPE, LOADING, SAVING, VERIFYING, FOUND, OK (bit 7 set on the last character of each)
1952
+ **Bank-switchable:** Yes
1953
+
1954
+ ### $F12B-$F349 — Message printer, GETIN, CHRIN, CHROUT, CHKIN, CHKOUT, CLOSE, CLALL, CLRCHN
1955
+
1956
+ **Default use:** Message printer $F12B (tests $9D bit 7; $F12F prints unconditionally), GETIN $F13E, CHRIN $F157, CHROUT $F1CA, CHKIN $F20E, CHKOUT $F250, CLOSE $F291, CLALL $F32F, CLRCHN $F333 (RTS at $F349). STATUS/SETMSG/MEMTOP/MEMBOT are not here — READST is $FE07, SETMSG $FE18, MEMTOP $FE25, MEMBOT $FE34; an earlier version of this page headed $F130-$F156 with those names
1957
+ **Bank-switchable:** Yes
1958
+
1959
+ ### $F34A-$F49D — OPEN
1960
+
1961
+ **Default use:** OPEN body $F34A (the $031A default), including the RS-232 OPEN path $F409
1962
+ **Bank-switchable:** Yes
1963
+
1964
+ ### $F49E-$F5DC — LOAD
1965
+
1966
+ **Default use:** LOAD entry $F49E (stores X/Y to $C3/$C4, then JMP ($0330) → body $F4A5, the $0330 default); serial LOAD falls through at $F4B8, tape LOAD branches at $F4B6 to $F533. VERIFY is LOAD with A = 1
1967
+ **Bank-switchable:** Yes
1968
+
1969
+ ### $F5DD-$F69A — SAVE
1970
+
1971
+ **Default use:** SAVE entry $F5DD (stores the end address to $AE/$AF, then JMP ($0332) → body $F5ED, the $0332 default)
1972
+ **Bank-switchable:** Yes
1973
+
1974
+ ### $F69B-$F6FA — UDTIM, RDTIM, SETTIM, STOP
1975
+
1976
+ **Default use:** UDTIM $F69B (jiffy increment, then the STOP-key sample at $F6BC that debounces $DC01 row 7 into $91), RDTIM $F6DD, SETTIM $F6E4, STOP $F6ED (the $0328 default). An earlier version headed $F69C-$F7AF "VERIFY, MEMTOP/MEMBOT defaults"; VERIFY is a LOAD mode and MEMTOP/MEMBOT are at $FE25/$FE34
1977
+ **Bank-switchable:** Yes
1978
+
1979
+ ### $F6FB-$F92B — I/O error entries, tape header handling, tape prompts
1980
+
1981
+ **Default use:** I/O error entry points $F6FB-$F713 (errors 1-9 → "I/O ERROR #n" through the $9D bit-6 test), tape header find $F72C and write $F76A, buffer address helpers $F7D0, PRESS PLAY $F817 / PRESS RECORD $F838 prompts (printed unconditionally through $F12F), tape read/write set-up $F841/$F864
1982
+ **Bank-switchable:** Yes
1983
+
1984
+ ### $F92C-$FBCC — Cassette read IRQ
1985
+
1986
+ **Default use:** The tape READ interrupt service $F92C (installed from the $FD9B vector table; measures pulse widths on CIA #1 Timer B) and the byte-store routine $FA60. Not disk I/O: this span references CIA #1 sixteen times and CIA #2 never; an earlier version of this page headed $F901-$FB54 "Disk LOAD/SAVE (IEC-bus protocol)" — the serial path is inside LOAD/SAVE above
1987
+ **Bank-switchable:** Yes
1988
+
1989
+ ### $FBCD-$FCE1 — Cassette write IRQs and tape housekeeping
1990
+
1991
+ **Default use:** Tape WRITE interrupt services ($FBCD data, $FC6A leader), leader/bit timing, restore normal IRQ and screen $FC93, tape motor off $FCCA, read/write pointer compare and bump $FCD1/$FCDB
1992
+ **Bank-switchable:** Yes
1993
+
1994
+ ### $FCE2-$FD4F — RESET, cartridge check, RESTOR, VECTOR, default vector table
1995
+
1996
+ **Default use:** RESET $FCE2 (the $FFFC vector: LDX #$FF / SEI / TXS / CLD, cartridge check, then IOINIT, RAMTAS, RESTOR, CINT, CLI, JMP ($A000)), cartridge signature check $FD02 comparing $8004-$8008 with "CBM80" at $FD10, RESTOR $FD15, VECTOR $FD1A, 16-word default vector table $FD30-$FD4F copied to $0314-$0333. An earlier version headed $FCFC-$FD2F "RESET handler" and $FD30-$FD8F "vectors initialization"; RESET is $FCE2 and $FD30 is the table, not code
1997
+ **Bank-switchable:** Yes
1998
+
1999
+ The first code that runs after the CPU comes out of reset is $FCE2,
2000
+ entered via the reset vector at $FFFC.
2001
+
2002
+ ### $FD50-$FDF8 — RAMTAS, tape IRQ vector table, IOINIT
2003
+
2004
+ **Default use:** RAMTAS $FD50 (clears $0002-$0101 and $0200-$03FF, finds top of memory, sets $B2/$B3, $0282, $0288), tape-IRQ vector table $FD9B ($FC6A, $FBCD, $EA31, $F92C), IOINIT $FDA3 (CIA set-up, $01/$00 = $E7/$2F at $FDD5, jiffy timer latch from $02A6 at $FDDD, continued at $FF6E)
2005
+ **Bank-switchable:** Yes
2006
+
2007
+ ### $FDF9-$FE42 — SETNAM, SETLFS, READST, SETMSG, SETTMO, MEMTOP, MEMBOT
2008
+
2009
+ **Default use:** SETNAM $FDF9, SETLFS $FE00, READST $FE07 (RS-232 status from $0297 when the device is 2), SETMSG $FE18 (STA $9D), SETTMO $FE21, MEMTOP $FE25, MEMBOT $FE34
2010
+ **Bank-switchable:** Yes
2011
+
2012
+ ### $FE43-$FF42 — NMI handler, RUN/STOP-RESTORE warm start, RS-232 NMI paths
2013
+
2014
+ **Default use:** NMI entry $FE43 (SEI, JMP ($0318)); default handler $FE47 (acknowledges CIA #2, cartridge check, STOP-key test); warm start $FE66 (RESTOR, IOINIT, CINT, JMP ($A002)); NMI exit $FEBC; RS-232 baud-rate table $FEC2; RS-232 NMI receive $FED6 and send $FF07
2015
+ **Bank-switchable:** Yes
2016
+
2017
+ ### $FF43-$FF7F — IRQ/BRK dispatcher, CINT wrapper, IOINIT tail
2018
+
2019
+ **Default use:** Fake-IRQ entry $FF43 (PHP / PLA / AND #$EF / PHA, falling into) the dispatcher $FF48 (the $FFFE vector: saves A, X, Y, tests the pushed B flag, JMP ($0316) or JMP ($0314)); CINT wrapper $FF5B (the $FF81 target: JSR $E518, PAL/NTSC detect into $02A6, JMP $FDDD); IOINIT timer-start tail $FF6E (reached by JMP from $FDF6)
2020
+ **Bank-switchable:** Yes
2021
+
2022
+ ### $FF80-$FF80 — KERNAL revision byte
2023
+
2024
+ **Default use:** $AA in 901227-01, $00 in -02, $03 in -03 (PEEK(65408))
2025
+ **Bank-switchable:** Yes
2026
+
2027
+ ### $FF81-$FFA5 — KERNAL routine jump table (Editor + new)
2028
+
2029
+ **Default use:** Public jump table entries for CINT, IOINIT, RAMTAS, RESTOR, VECTOR
2030
+ **Bank-switchable:** Yes
2031
+
2032
+ | $FF81 | CINT | Initialize screen editor |
2033
+ | $FF84 | IOINIT | Initialize I/O |
2034
+ | $FF87 | RAMTAS | Initialize RAM, allocate workspace |
2035
+ | $FF8A | RESTOR | Restore default I/O vectors |
2036
+ | $FF8D | VECTOR | Read/set indirect vectors |
2037
+ | $FF90 | SETMSG | Control KERNAL messages |
2038
+ | $FF93 | SECOND | Send secondary address after LISTEN |
2039
+ | $FF96 | TKSA | Send secondary address after TALK |
2040
+ | $FF99 | MEMTOP | Set/read top of memory |
2041
+ | $FF9C | MEMBOT | Set/read bottom of memory |
2042
+ | $FF9F | SCNKEY | Scan keyboard |
2043
+ | $FFA2 | SETTMO | Set IEC bus timeout |
2044
+
2045
+ ### $FFA5-$FFC0 — KERNAL jump table (IEC bus)
2046
+
2047
+ **Default use:** IEC bus jump entries
2048
+ **Bank-switchable:** Yes
2049
+
2050
+ | $FFA5 | IECIN | Read byte from IEC bus |
2051
+ | $FFA8 | IECOUT | Write byte to IEC bus |
2052
+ | $FFAB | UNTLK | Send UNTALK on IEC bus |
2053
+ | $FFAE | UNLSN | Send UNLISTEN on IEC bus |
2054
+ | $FFB1 | LISTEN | Command device on IEC to LISTEN |
2055
+ | $FFB4 | TALK | Command device on IEC to TALK |
2056
+ | $FFB7 | READST | Read I/O STATUS |
2057
+ | $FFBA | SETLFS | Set logical file params |
2058
+ | $FFBD | SETNAM | Set filename |
2059
+
2060
+ ### $FFC0-$FFE5 — KERNAL jump table (File I/O + Char I/O)
2061
+
2062
+ **Default use:** Public entry points for KERNAL routines
2063
+ **Bank-switchable:** Yes
2064
+
2065
+ | $FFC0 | OPEN | Open a logical file |
2066
+ | $FFC3 | CLOSE | Close a logical file |
2067
+ | $FFC6 | CHKIN | Set input channel |
2068
+ | $FFC9 | CHKOUT | Set output channel |
2069
+ | $FFCC | CLRCHN | Restore default I/O |
2070
+ | $FFCF | CHRIN | Get char from current input |
2071
+ | $FFD2 | CHROUT | Output char to current output |
2072
+ | $FFD5 | LOAD | Load file from device |
2073
+ | $FFD8 | SAVE | Save file to device |
2074
+ | $FFDB | SETTIM | Set jiffy clock |
2075
+ | $FFDE | RDTIM | Read jiffy clock |
2076
+ | $FFE1 | STOP | Check if STOP key pressed |
2077
+ | $FFE4 | GETIN | Get char from queue (non-blocking) |
2078
+
2079
+ ### $FFE6-$FFF5 — KERNAL jump table (close-all, etc.)
2080
+
2081
+ **Default use:** Final entries of the public jump table
2082
+ **Bank-switchable:** Yes
2083
+
2084
+ | $FFE7 | CLALL | Close all files |
2085
+ | $FFEA | UDTIM | Update jiffy clock + STOP flag |
2086
+ | $FFED | SCREEN | Return screen dimensions |
2087
+ | $FFF0 | PLOT | Read/set cursor row/col |
2088
+ | $FFF3 | IOBASE | Return base address of CIA #1 |
2089
+
2090
+ ### $FFF6-$FFF9 — Unused
2091
+
2092
+ **Default use:** Four bytes not reached by any code: $52 $52 $42 $59 in 901227-03
2093
+ **Bank-switchable:** Yes
2094
+
2095
+ ### $FFFA-$FFFB — NMI vector
2096
+
2097
+ **Default use:** Pointer to NMI handler ($FE43 in KERNAL ROM)
2098
+ **Bank-switchable:** Yes — if KERNAL banked out, this is RAM and you must write your own vector
2099
+
2100
+ The CPU reads $FFFA/$FFFB on every NMI to find the handler address. In
2101
+ the default config this is in KERNAL ROM and points to the chained NMI
2102
+ handler that dispatches through $0318.
2103
+
2104
+ ### $FFFC-$FFFD — Reset vector
2105
+
2106
+ **Default use:** Pointer to RESET handler ($FCE2 in KERNAL ROM)
2107
+ **Bank-switchable:** Yes
2108
+
2109
+ Read by the CPU on power-on and on the hardware RESET line.
2110
+
2111
+ ### $FFFE-$FFFF — IRQ / BRK vector
2112
+
2113
+ **Default use:** Pointer to IRQ/BRK handler ($FF48 in KERNAL ROM)
2114
+ **Bank-switchable:** Yes — if KERNAL banked out, must point at user-supplied handler
2115
+
2116
+ The CPU reads $FFFE/$FFFF on every IRQ and BRK. With KERNAL banked in,
2117
+ this points at $FF48 which saves registers and then JMPs through
2118
+ $0314 (IRQ) or $0316 (BRK).
2119
+
2120
+ **Crucial:** If you bank out KERNAL ROM ($01 = $35 or lower) and IRQ
2121
+ fires, the CPU reads $FFFE/$FFFF *from RAM*. You must have pre-loaded
2122
+ RAM bytes at $FFFE/$FFFF with the address of your own IRQ handler before
2123
+ banking out, or the machine will crash. Same for NMI ($FFFA/$FFFB) and
2124
+ RESET ($FFFC/$FFFD).
2125
+
2126
+ ## Bank switching
2127
+
2128
+ The 6510 sees a 64 KB address space but the C64 holds more memory than
2129
+ that. The PLA (programmable logic array, MOS 906114) decides, at every
2130
+ memory access, which physical resource the CPU is talking to. Its
2131
+ inputs are:
2132
+
2133
+ - **A15-A12** — the high nibble of the address bus, which selects the
2134
+ 4 KB chunk.
2135
+ - **R/W** — distinguishes reads from writes.
2136
+ - **LORAM, HIRAM, CHAREN** — the three configurable processor-port pins
2137
+ from $01 bits 0, 1, 2.
2138
+ - **GAME, EXROM** — the two cartridge-port lines, pulled high (1) when
2139
+ no cart is present.
2140
+
2141
+ The PLA hardwires 32 possible input combinations into seven distinct
2142
+ output configurations (plus a few cartridge-only modes). These are the
2143
+ **memory modes** — sometimes called **banks** in PLA literature.
2144
+
2145
+ Software running on a bare C64 (no cart) sees five practically-useful
2146
+ configurations and two rarely-used edge cases.
2147
+
2148
+ ### Mode 31 — default (BASIC + KERNAL + I/O)
2149
+
2150
+ **$01 value:** $37 (or any value with bits 0-2 = 111)
2151
+ **LORAM:** 1, **HIRAM:** 1, **CHAREN:** 1
2152
+
2153
+ | Range | Contents |
2154
+ |--------------|--------------|
2155
+ | $0000-$0FFF | RAM |
2156
+ | $1000-$7FFF | RAM |
2157
+ | $8000-$9FFF | RAM |
2158
+ | $A000-$BFFF | BASIC ROM |
2159
+ | $C000-$CFFF | RAM |
2160
+ | $D000-$DFFF | I/O |
2161
+ | $E000-$FFFF | KERNAL ROM |
2162
+
2163
+ The mode the machine boots into. BASIC visible, KERNAL visible, I/O
2164
+ visible at $D000-$DFFF.
2165
+
2166
+ ### Mode 30 — KERNAL + I/O (BASIC banked out)
2167
+
2168
+ **$01 value:** $36
2169
+ **LORAM:** 0, **HIRAM:** 1, **CHAREN:** 1
2170
+
2171
+ | Range | Contents |
2172
+ |--------------|--------------|
2173
+ | $A000-$BFFF | RAM (BASIC banked out) |
2174
+ | $D000-$DFFF | I/O |
2175
+ | $E000-$FFFF | KERNAL ROM |
2176
+
2177
+ Used by machine-language programs that want the 8 KB at $A000-$BFFF as
2178
+ RAM but still want KERNAL routines accessible.
2179
+
2180
+ ### Mode 27 — character ROM + KERNAL + BASIC (no I/O)
2181
+
2182
+ **$01 value:** $33
2183
+ **LORAM:** 1, **HIRAM:** 1, **CHAREN:** 0
2184
+
2185
+ | Range | Contents |
2186
+ |--------------|--------------|
2187
+ | $A000-$BFFF | BASIC ROM |
2188
+ | $D000-$DFFF | Character ROM|
2189
+ | $E000-$FFFF | KERNAL ROM |
2190
+
2191
+ The I/O chips at $D000-$DFFF are replaced by the character generator
2192
+ ROM. Useful for copying the character set into RAM (`LDA $D000,X / STA
2193
+ target,X`) — you read the font directly, then bank I/O back in.
2194
+
2195
+ **Caution:** With I/O banked out, you cannot read CIA, VIC, or SID
2196
+ registers. *And* the IRQ vector goes through $FFFE/$FFFF which still
2197
+ points at the KERNAL ROM, so the KERNAL IRQ handler will run — and the
2198
+ KERNAL handler tries to LDA $DC0D *which now reads the character ROM*.
2199
+ It does not work; an earlier version of this page said it generally did.
2200
+ The handler does discard the value, but the read itself is the
2201
+ acknowledge: `LDA $DC0D` at $EA7E is the only place the service routine
2202
+ touches the CIA's interrupt register, and the ICR is cleared only when
2203
+ the CIA sees the read. With CHAREN = 0 the PLA selects the character ROM
2204
+ instead of the CIA, so the flag stays set, /IRQ stays low, and because
2205
+ RTI has no interrupt-delay the CPU re-enters the handler immediately
2206
+ after every RTI. The main program never executes another instruction —
2207
+ measured in VICE x64sc: with a `$0314` handler counting passes, a
2208
+ main-program instruction counter stayed at 0 across 4,909 handler passes
2209
+ in 20M cycles and 7,822 in 30M, while the identical code with $01 = $37
2210
+ finished inside one jiffy. (The handler also reads $DC01 as char-ROM
2211
+ byte $99, so SCNKEY runs a full phantom matrix scan on every pass.)
2212
+ *Always SEI before banking the character ROM in, and restore $01 before
2213
+ CLI — not only "if timing matters".*
2214
+
2215
+ ### Mode 26 — KERNAL + character ROM (no BASIC, no I/O)
2216
+
2217
+ **$01 value:** $32
2218
+ **LORAM:** 0, **HIRAM:** 1, **CHAREN:** 0
2219
+
2220
+ | Range | Contents |
2221
+ |--------------|--------------|
2222
+ | $A000-$BFFF | RAM |
2223
+ | $D000-$DFFF | Character ROM|
2224
+ | $E000-$FFFF | KERNAL ROM |
2225
+
2226
+ ### Mode 25 — Character ROM only (no BASIC, no KERNAL, no I/O)
2227
+
2228
+ **$01 value:** $31
2229
+ **LORAM:** 1, **HIRAM:** 0, **CHAREN:** 0
2230
+
2231
+ | Range | Contents |
2232
+ |--------------|--------------|
2233
+ | $A000-$BFFF | RAM |
2234
+ | $D000-$DFFF | Character ROM|
2235
+ | $E000-$FFFF | RAM |
2236
+
2237
+ Almost never used — see Mode 24 instead.
2238
+
2239
+ ### Mode 29 — I/O only (no BASIC, no KERNAL)
2240
+
2241
+ **$01 value:** $35
2242
+ **LORAM:** 1, **HIRAM:** 0, **CHAREN:** 1
2243
+
2244
+ | Range | Contents |
2245
+ |--------------|--------------|
2246
+ | $A000-$BFFF | RAM |
2247
+ | $D000-$DFFF | I/O |
2248
+ | $E000-$FFFF | RAM |
2249
+
2250
+ The most common alternative to mode 31. Demos and games use this when
2251
+ they want all RAM but still need to talk to VIC/SID/CIA. The big catch:
2252
+ the IRQ vectors at $FFFE/$FFFF are now in RAM. You must:
2253
+
2254
+ 1. Disable IRQs with SEI.
2255
+ 2. Write your IRQ handler's address into $FFFE/$FFFF (and NMI into
2256
+ $FFFA/$FFFB).
2257
+ 3. Bank out KERNAL with `LDA #$35 / STA $01`.
2258
+ 4. Re-enable IRQs with CLI when ready.
2259
+
2260
+ (An earlier version of this page gave LORAM = 0 for modes 29 and 25 and
2261
+ CHAREN = 1 for mode 24; the $01 values and the truth table below were
2262
+ right, the bit prose was not — measured in VICE x64sc: $35 maps
2263
+ RAM/I-O/RAM at $A000/$D000/$E000, $31 maps RAM/char ROM/RAM, and $30
2264
+ and $34 are both all RAM.)
2265
+
2266
+ ### Mode 24 — all RAM (no ROM, no I/O)
2267
+
2268
+ **$01 value:** $30
2269
+ **LORAM:** 0, **HIRAM:** 0, **CHAREN:** 0 ($34, CHAREN = 1, is mode 28 and maps identically — with LORAM and HIRAM both 0 the PLA ignores CHAREN)
2270
+
2271
+ | Range | Contents |
2272
+ |--------------|--------------|
2273
+ | $A000-$BFFF | RAM |
2274
+ | $D000-$DFFF | RAM |
2275
+ | $E000-$FFFF | RAM |
2276
+
2277
+ A pure 64 KB of RAM. Used for software that brings its own everything
2278
+ — typically the cracked-game intro / demoscene "filler" code that
2279
+ needs maximum RAM for music + graphics + code.
2280
+
2281
+ **Same IRQ catch as mode 29.** Plus you lose all I/O — your code must
2282
+ either disable interrupts and never need to talk to VIC/SID/CIA, or
2283
+ temporarily bank I/O back in (`LDA #$35 / STA $01`) when needed.
2284
+
2285
+ ### The truth table - no-cartridge rows
2286
+
2287
+ Below are the eight no-cartridge rows (GAME = EXROM = 1, modes 24-31) of
2288
+ the 32-row PLA table. Modes are numbered 0-31 as LORAM + HIRAM x 2 +
2289
+ CHAREN x 4 + GAME x 8 + EXROM x 16, so the eight rows here are $01 bits
2290
+ 0-2 plus 24. The remaining 24 rows are the cartridge configurations
2291
+ (16 KB cart: modes 0-7; 8 KB cart: 8-15; Ultimax: 16-23) - see
2292
+ [Cartridge modes](#cartridge-modes-exrom--game). An earlier version of
2293
+ this page called this table the full 32-mode table; it never was.
2294
+
2295
+ Cart-active modes (EXROM = 0 or GAME = 0) include "Ultimax" cartridges
2296
+ where ROMH+ROML+CHAREN+I/O selectively replace huge swaths of the map.
2297
+
2298
+ | Mode | $01 | LORAM | HIRAM | CHAREN | $8000-$9FFF | $A000-$BFFF | $D000-$DFFF | $E000-$FFFF |
2299
+ |------|-----|-------|-------|--------|-------------|-------------|-------------|-------------|
2300
+ | 31 | 7 | 1 | 1 | 1 | RAM | BASIC ROM | I/O | KERNAL ROM |
2301
+ | 30 | 6 | 0 | 1 | 1 | RAM | RAM | I/O | KERNAL ROM |
2302
+ | 29 | 5 | 1 | 0 | 1 | RAM | RAM | I/O | RAM |
2303
+ | 28 | 4 | 0 | 0 | 1 | RAM | RAM | RAM | RAM |
2304
+ | 27 | 3 | 1 | 1 | 0 | RAM | BASIC ROM | CHARROM | KERNAL ROM |
2305
+ | 26 | 2 | 0 | 1 | 0 | RAM | RAM | CHARROM | KERNAL ROM |
2306
+ | 25 | 1 | 1 | 0 | 0 | RAM | RAM | CHARROM | RAM |
2307
+ | 24 | 0 | 0 | 0 | 0 | RAM | RAM | RAM | RAM |
2308
+
2309
+ The seven distinct configurations boil down to **which of {RAM, BASIC,
2310
+ CHARROM} sits at $A000-$BFFF**, **which of {RAM, KERNAL} sits at
2311
+ $E000-$FFFF**, and **which of {RAM, CHARROM, I/O} sits at $D000-$DFFF**.
2312
+
2313
+ ### Writes always go to RAM
2314
+
2315
+ This is the most important invariant in C64 banking: **writes to
2316
+ $A000-$BFFF, $D000-$DFFF (when I/O isn't there), and $E000-$FFFF always
2317
+ hit RAM**. Reads return ROM bytes when ROM is banked in; writes never
2318
+ do — they always update the underlying DRAM cell.
2319
+
2320
+ Exceptions to this rule:
2321
+ - $D000-$DFFF when I/O is banked in: writes go to the chip (VIC, SID,
2322
+ CIA, Color RAM, etc.) — not to RAM.
2323
+ - $D000-$DFFF when CHARROM is banked in: writes go to RAM (character ROM
2324
+ is read-only). This is unintuitive — you can write to $D000 while
2325
+ CHARROM is selected and the byte will be silently buried in RAM, only
2326
+ visible if you then bank I/O *out* and CHARROM out and read again.
2327
+
2328
+ ### DDR must be set before $01 writes
2329
+
2330
+ The DDR at $0000 controls which bits of $01 are outputs. Bits configured
2331
+ as inputs ignore writes to $01.
2332
+
2333
+ The KERNAL sets DDR to $2F at reset (bits 0-3 and 5 are outputs; bits 4
2334
+ and 6 are inputs). Bits 0-2 are *always* outputs in the default config,
2335
+ so writing $35, $36, $37 to $01 takes effect.
2336
+
2337
+ **The pitfall:** code that does `LDA #$30 / STA $01` to bank to mode 24
2338
+ needs to *first* write $2F to $00 — otherwise some prior program may
2339
+ have left bit 7 of DDR as an input, in which case the high bit of $01
2340
+ won't change. In practice this is rarely a problem on a reset-fresh C64
2341
+ but is a known reliability issue when chaining loader stages.
2342
+
2343
+ The safest banking sequence:
2344
+
2345
+ ```
2346
+ SEI ; disable IRQs
2347
+ LDA #$2F ; DDR mask: bits 0-3,5 output
2348
+ STA $00
2349
+ LDA #$35 ; new bank value
2350
+ STA $01
2351
+ ```
2352
+
2353
+ ### Cartridge modes (EXROM / GAME)
2354
+
2355
+ When a cartridge is inserted, EXROM and/or GAME pull low and the PLA
2356
+ maps cartridge ROM into the address space. The two relevant cart
2357
+ configurations are:
2358
+
2359
+ - **8 KB cart** (EXROM = 0, GAME = 1): ROML at $8000-$9FFF.
2360
+ - **16 KB cart** (EXROM = 0, GAME = 0): ROML at $8000-$9FFF + ROMH at
2361
+ $A000-$BFFF, with BASIC ROM disabled. ROMH at $A000 requires HIRAM = 1;
2362
+ LORAM only gates ROML (measured in VICE x64sc).
2363
+ - **Ultimax cart** (EXROM = 1, GAME = 0): a weird hybrid where ROML is
2364
+ at $8000-$9FFF + ROMH is at $E000-$FFFF replacing KERNAL, *and* most
2365
+ of the RAM is disabled. Used by the Commodore Ultimax — the only place
2366
+ in the C64 ecosystem this matters.
2367
+
2368
+ The PLA truth table for cart modes is documented in *The C64 PLA
2369
+ Dissected*. Most software developers don't need to think about cart
2370
+ modes unless they're writing cartridges or copy-protection.
2371
+
2372
+ ## Zero page details
2373
+
2374
+ The 6510's zero-page addressing modes — LDA $XX, LDA $XX,X, LDA
2375
+ ($XX,X), LDA ($XX),Y — are one or two cycles faster than absolute
2376
+ modes and use one fewer byte. Zero page (the first 256 bytes of RAM,
2377
+ $0000-$00FF) is therefore precious real estate. The KERNAL and BASIC
2378
+ have already claimed most of it.
2379
+
2380
+ ### What KERNAL uses
2381
+
2382
+ The KERNAL workspace in zero page is roughly $90-$FE. Subsections:
2383
+
2384
+ - **$90-$93** — I/O state: STATUS, STOP-key flag, cassette flag, etc.
2385
+ - **$94-$9F** — IEC and cassette bit-level workspace.
2386
+ - **$A0-$A2** — Jiffy clock (TI).
2387
+ - **$A3-$BD** — More cassette + IEC workspace.
2388
+ - **$BE-$BF** — Tape block + serial workspace.
2389
+ - **$C1-$C4** — Memory pointer pairs (LOAD/SAVE/VERIFY).
2390
+ - **$C5-$D7** — Screen editor: last key, keyboard buffer count, cursor
2391
+ X/Y, cursor blink state, line pointer.
2392
+ - **$D8** — Insert count (INSRT): number of pending INST presses; CHROUT
2393
+ decrements it as characters are typed.
2394
+ - **$D9-$F2** — Screen line link table (LDTB1), 26 entries: one per
2395
+ screen row plus a sentinel entry at $F2 so row 24 is never read as
2396
+ continued. Bits 0-1 of an entry are the row's screen-address high-byte
2397
+ offset (OR'd with $0288 when used); bit 7 set means the row starts a
2398
+ logical line, clear means it continues the row above. (This page
2399
+ previously ended the table at $F1 and put the insert count at $F2; the
2400
+ KERNAL's fill loop at $E544 writes 26 entries and never addresses $F2
2401
+ on its own — measured from the ROM and in VICE x64sc.)
2402
+ - **$F3-$F4** — Colour RAM pointer for the current line (low byte = $D1;
2403
+ high byte = ($D2 & 3) | $D8).
2404
+ - **$F5-$F6** — Key decode table pointer.
2405
+ - **$F7-$FA** — RS-232 buffer pointers.
2406
+
2407
+ If your program calls *any* KERNAL routine — even CHROUT — the KERNAL
2408
+ will read and write zero page. The full list of zeropage bytes the
2409
+ KERNAL uses is in *Mapping the Commodore 64* chapter 1.
2410
+
2411
+ Five zero-page bytes are never touched by either ROM after RAMTAS's
2412
+ reset clear: $02 and $FB-$FE (this page used to say $FB-$FE were the
2413
+ only four).
2414
+
2415
+ ### What BASIC uses
2416
+
2417
+ BASIC's workspace is $03-$8F, with the executable CHRGET routine at
2418
+ $73-$8A ($02 is not BASIC's — see $0002 above).
2419
+
2420
+ - **$03-$8A** — Working registers and pointers for parsing, tokenization,
2421
+ expression evaluation, floating-point math.
2422
+ - **$8B-$8F** — RND seed.
2423
+
2424
+ If your program doesn't return to the BASIC READY prompt and doesn't
2425
+ call BASIC ROM routines, all of $03-$8F is free except where it overlaps
2426
+ the CHRGET code at $73-$8A. To free up $73-$8A, you'd need to make sure
2427
+ no future BASIC interpretation happens.
2428
+
2429
+ ### Best practices for zero page
2430
+
2431
+ When writing assembly that coexists with KERNAL:
2432
+
2433
+ 1. **Use $FB-$FE and $02 first.** These five bytes are safe.
2434
+ 2. **Disable IRQs (SEI) before using any other zeropage location.** That
2435
+ prevents the KERNAL IRQ (jiffy clock + keyscan) from clobbering your
2436
+ bytes mid-operation.
2437
+ 3. **Read $0314-$0333 to find what hooks you've installed.** A
2438
+ non-default vector indicates someone else has installed a handler
2439
+ and may use additional zero page.
2440
+
2441
+ When writing assembly that takes over the machine completely (bank
2442
+ KERNAL out + own IRQ handler):
2443
+
2444
+ 1. The entire zero page $00-$FF is yours, except $00 and $01 which still
2445
+ talk to the 6510 processor port.
2446
+
2447
+ ## Stack page
2448
+
2449
+ $0100-$01FF is the 6502/6510 hardware stack. The stack pointer S
2450
+ descends from $01FF and the page address is hard-coded — the stack
2451
+ *always* lives on page 1.
2452
+
2453
+ ### Stack operations
2454
+
2455
+ - **PHA / PHP** — push 1 byte, decrement S.
2456
+ - **PLA / PLP** — increment S, pull 1 byte.
2457
+ - **JSR addr** — push the address of the JSR's last byte (the return
2458
+ address minus one), high byte first then low byte, so inside the
2459
+ callee the low byte is at $0101+S and the high byte at $0102+S
2460
+ (measured in VICE x64sc; an earlier version of this bullet said the
2461
+ low byte sat at the higher address, which is backwards). Then
2462
+ PC := addr.
2463
+ - **RTS** — pull return-address-minus-one + 1.
2464
+ - **BRK / IRQ / NMI** — push PCH, PCL, status; PC := vector.
2465
+
2466
+ ### BASIC use of the stack
2467
+
2468
+ BASIC pushes substantial state on the stack for GOSUB and FOR-NEXT:
2469
+
2470
+ - **GOSUB** pushes 5 bytes: `$8D` (GOSUB token), current line number
2471
+ (2 bytes), text pointer (2 bytes).
2472
+ - **FOR** pushes 18 bytes (push order; read back last-pushed first):
2473
+ text pointer (2), line number (2), TO limit (5 bytes FP, sign folded
2474
+ into the first mantissa byte), STEP sign (1), STEP value (5 bytes FP),
2475
+ pointer to the index variable's descriptor (2), `$81` (FOR token). The
2476
+ stack search at $A38A walks frames in steps of 18 (`ADC #$12`). An
2477
+ earlier revision of this page also listed an "index variable name (2)"
2478
+ entry and put STEP before the limit; the frame holds a pointer, not
2479
+ the name, and that entry made the itemization sum to 20 (verified
2480
+ against the ROM at $A75D-$A7AD and a stack dump in VICE x64sc).
2481
+
2482
+ So a deeply-nested BASIC program can exhaust the stack faster than you'd
2483
+ expect — 8 nested FORs = 144 bytes, more than half the stack.
2484
+
2485
+ ### Assembly hygiene
2486
+
2487
+ The KERNAL reset ($FCE2) sets S to $FF (LDX #$FF / SEI / TXS) and hands
2488
+ over to BASIC with S still $FF; it is BASIC's cold start that then does
2489
+ LDX #$FB / TXS ($E39D), and CLR/RUN lower the base to $FA ($A681).
2490
+ Earlier text credited the $FB to the KERNAL. By the time a SYS reaches
2491
+ your code S is lower again: $F7 for a SYS typed at the READY prompt on a
2492
+ freshly booted machine, $F6 for a SYS inside a RUN program or typed
2493
+ after one (RUN's CLR resets the base to $FA, then the statement
2494
+ dispatcher's JSR and SYS's pushed return address each take two bytes) —
2495
+ both measured in VICE x64sc 3.10. Pure-assembly programs that take over
2496
+ should do `LDX #$FF / TXS` to reset the stack pointer to the top.
2497
+
2498
+ When you push values for "local variables" via PHA, pull them back in
2499
+ *reverse order*. The 6502 has no offset-based stack-relative addressing
2500
+ (unlike the 65C816), so manually accessing pushed values from inside a
2501
+ routine requires `TSX` to load S into X and then absolute-indexed reads
2502
+ from $0100,X.
2503
+
2504
+ ## KERNAL ROM map
2505
+
2506
+ The KERNAL ROM at $E000-$FFFF (8 KB) contains the firmware that handles
2507
+ all I/O, character output, screen scrolling, cursor management,
2508
+ keyboard scanning, the IRQ/NMI handlers, the BRK handler, the IEC bus
2509
+ driver, the RS-232 driver, and the cassette driver. It also contains
2510
+ the standard reset routine.
2511
+
2512
+ The public interface is the jump table at $FF81-$FFF5 — 39 entries of 3
2513
+ bytes each. Twenty-nine are `JMP addr`; the ten I/O entries (OPEN,
2514
+ CLOSE, CHKIN, CHKOUT, CLRCHN, CHRIN, CHROUT, STOP, GETIN, CLALL) are
2515
+ `JMP (vector)` through the RAM vectors at $031A-$032D, which is why
2516
+ patching $0326/$0327 redirects `JSR $FFD2` while a direct `JSR $F1CA`
2517
+ bypasses the hook. (An earlier version said every entry was a direct
2518
+ `JMP addr`; the ROM bytes show opcode $6C at ten of them.) Calling
2519
+ KERNAL routines through the jump table is *strongly* preferred over
2520
+ calling them directly: the jump table is stable across KERNAL ROM
2521
+ revisions; the implementation addresses are not.
2522
+
2523
+ ### KERNAL revisions
2524
+
2525
+ Three production KERNAL ROMs shipped: 901227-01, -02 and -03 (the C64C's
2526
+ combined 16 KB BASIC+KERNAL mask, usually given as 251913-01, is
2527
+ documented as carrying the -03 KERNAL — no image on this machine, not
2528
+ measured here). `PEEK(65408)` ($FF80) returns 170, 0 or 3. The jump
2529
+ table $FF81-$FFF5 is at the same addresses in all three and 38 of 39
2530
+ targets are unchanged; the one that moved is CINT ($FF81 = `JMP $E518`
2531
+ in -01, `JMP $FF5B` in -02/-03). The differences are 277 bytes of
2532
+ in-place patches (-01/-02 227, -02/-03 57; `cmp` of the VICE 3.9
2533
+ images):
2534
+
2535
+ - **-02 added PAL/NTSC detection.** A CINT wrapper at $FF5B runs $E518,
2536
+ waits for $D012 to wrap, stores $D019 bit 0 (the raster compare for
2537
+ line 311 set by the new VIC init table $D011/$D012 = $9B/$37) in
2538
+ $02A6, and IOINIT ($FDDD) then loads the jiffy timer with $4025 (PAL)
2539
+ or $4295 (NTSC) where -01 hard-coded $411B. RS-232 OPEN ($F428) and
2540
+ the NMI baud code ($FF08-$FF42) pick a PAL baud table at $E4EC by
2541
+ $02A6; BASIC's CHKOUT wrapper ($E118) calls a patch at $E4AD that
2542
+ preserves A.
2543
+ - **-02 and -03 each changed the screen-clear colour** ($EA0B → $E4DA:
2544
+ -01 white `LDA #$01`, -02 background `LDA $D021`, -03 current text
2545
+ colour `LDA $0286`). -03 also patched the screen editor's cursor-line
2546
+ set-up ($E57C-$E599, $E621: the colour pointer $F3/$F4 is resynced via
2547
+ $EA24, and the line-start search is skipped when the cursor row
2548
+ already equals the input row $C9) and one RS-232 receive path ($EF94
2549
+ → $E4D3).
2550
+ - **The only cassette change is the pause after the FOUND message**
2551
+ ($F761): -01 waits for a key to register in $91; -02/-03 wait until
2552
+ the jiffy clock's middle byte $A1 has advanced by three (`ADC #$02`
2553
+ with the carry left set by the preceding `CPY #$15`, roughly 9-13 s)
2554
+ or a key registers in $91 — routine $E4E0. The tape read/write code
2555
+ $F767-$FCFB, every pulse-timing constant included, is byte-identical
2556
+ in all three, and so are the keyboard scan, decode and key tables
2557
+ ($EA13-$ECC9). An earlier version of this section said the revisions
2558
+ differed in "cassette timing constants and one keyboard-handling fix";
2559
+ neither is in the ROM bytes.
2560
+
2561
+ *Always JSR through the jump table.* See kernal-routines-reference.md
2562
+ § KERNAL ROM revisions, which gives the same account.
2563
+
2564
+ ### KERNAL layout summary
2565
+
2566
+ | Range | Function |
2567
+ |---------------|----------------------------------------------------|
2568
+ | $E000-$E393 | BASIC ROM overflow (EXP/RND/trig, BASIC I/O command handlers, warm start $E37B) |
2569
+ | $E394-$E4AB | BASIC cold start $E394, init $E3BF, default vector table $E447, sign-on text ($E460-$E4AB, printed from $E473) |
2570
+ | $E4AC-$E4FF | Revision patches (CHKOUT wrapper $E4AD, RS-232 $E4D3, screen-clear colour $E4DA, FOUND pause $E4E0, PAL baud table $E4EC); $E4B7-$E4D2 unused |
2571
+ | $E500-$E5C9 | IOBASE $E500, SCREEN $E505, PLOT $E50A, CINT body $E518, clear screen $E544, VIC init $E5A0 |
2572
+ | $E5CA-$EA30 | Screen editor: keyboard/screen input, CHROUT screen body $E716, scroll $E8EA |
2573
+ | $EA31-$EA86 | Default IRQ handler (CIA acknowledge $EA7E, register restore/RTI $EA81) |
2574
+ | $EA87-$ECE6 | SCNKEY $EA87, KEYLOG $EB48, key decode tables $EB81-$ECB8, VIC power-on table $ECB9 |
2575
+ | $ECE7-$ED08 | "LOAD/RUN" string, screen-line low-byte table |
2576
+ | $ED09-$EEBA | IEC bus (TALK $ED09, LISTEN $ED0C, SECOND $EDB9, TKSA $EDC7, IECOUT $EDDD, UNTLK $EDEF, UNLSN $EDFE, IECIN $EE13, 1 ms delay $EEB3) |
2577
+ | $EEBB-$F0BC | RS-232 driver |
2578
+ | $F0BD-$F12A | KERNAL message text |
2579
+ | $F12B-$F349 | Message printer $F12B, GETIN $F13E, CHRIN $F157, CHROUT $F1CA, CHKIN $F20E, CHKOUT $F250, CLOSE $F291, CLALL $F32F, CLRCHN $F333 |
2580
+ | $F34A-$F49D | OPEN |
2581
+ | $F49E-$F5DC | LOAD (entry $F49E, body $F4A5; serial $F4B8, tape $F533) |
2582
+ | $F5DD-$F69A | SAVE (entry $F5DD, body $F5ED) |
2583
+ | $F69B-$F6FA | UDTIM $F69B (STOP-key sample $F6BC), RDTIM $F6DD, SETTIM $F6E4, STOP $F6ED |
2584
+ | $F6FB-$F92B | I/O error entries, tape header find/write, PRESS PLAY/RECORD prompts |
2585
+ | $F92C-$FBCC | Cassette READ IRQ ($F92C), tape byte store $FA60 |
2586
+ | $FBCD-$FCE1 | Cassette WRITE IRQs ($FBCD, $FC6A), restore IRQ $FC93, motor off $FCCA |
2587
+ | $FCE2-$FD4F | RESET $FCE2, cartridge check $FD02, RESTOR $FD15, VECTOR $FD1A, vector table $FD30 |
2588
+ | $FD50-$FDF8 | RAMTAS $FD50, tape IRQ vectors $FD9B, IOINIT $FDA3 (timer latch $FDDD) |
2589
+ | $FDF9-$FE42 | SETNAM $FDF9, SETLFS $FE00, READST $FE07, SETMSG $FE18, SETTMO $FE21, MEMTOP $FE25, MEMBOT $FE34 |
2590
+ | $FE43-$FF42 | NMI $FE43 → $FE47, RUN/STOP-RESTORE warm start $FE66, RS-232 NMI paths $FED6/$FF07 |
2591
+ | $FF43-$FF7F | IRQ/BRK dispatcher $FF48 (fake-IRQ entry $FF43), CINT wrapper $FF5B, IOINIT timer tail $FF6E |
2592
+ | $FF80-$FF80 | Revision byte ($03) |
2593
+ | $FF81-$FFF5 | Public jump table (39 entries; 10 indirect via $031A-$032D) |
2594
+ | $FFF6-$FFF9 | Unused |
2595
+ | $FFFA-$FFFF | CPU vectors (NMI $FE43, RESET $FCE2, IRQ/BRK $FF48) |
2596
+
2597
+ The previous version of this table misattributed several ranges — the
2598
+ keyboard scan sat inside a row labelled "IEC bus driver", the cassette
2599
+ read IRQ was labelled "Disk LOAD/SAVE via IEC", RAMTAS was placed in the
2600
+ BASIC-entry row, "$FF48-$FF80" was called IRQ/BRK preamble although
2601
+ $FF5B-$FF7F is CINT's PAL/NTSC wrapper and IOINIT's timer tail, and the
2602
+ first row overlapped the second by one byte. The boundaries above were
2603
+ read from the 901227-03 jump table, the $FD30 vector table and the
2604
+ $FD9B tape-IRQ vector table.
2605
+
2606
+ ### Public jump table at $FF81-$FFF5
2607
+
2608
+ The full jump table is documented in
2609
+ [kernal-routines-reference.md](kernal-routines-reference.md). Summary:
2610
+
2611
+ | Addr | Routine | Function |
2612
+ |-------|---------|---------------------------------------|
2613
+ | $FF81 | CINT | Initialize screen editor |
2614
+ | $FF84 | IOINIT | Initialize I/O chips |
2615
+ | $FF87 | RAMTAS | RAM test, allocate workspace |
2616
+ | $FF8A | RESTOR | Restore default vectors |
2617
+ | $FF8D | VECTOR | Read or set indirect vectors |
2618
+ | $FF90 | SETMSG | Control KERNAL messages |
2619
+ | $FF93 | SECOND | Send secondary address (after LISTEN) |
2620
+ | $FF96 | TKSA | Send secondary address (after TALK) |
2621
+ | $FF99 | MEMTOP | Set / read top-of-memory |
2622
+ | $FF9C | MEMBOT | Set / read bottom-of-memory |
2623
+ | $FF9F | SCNKEY | Scan the keyboard |
2624
+ | $FFA2 | SETTMO | Set IEC timeout |
2625
+ | $FFA5 | IECIN | Receive byte from IEC bus |
2626
+ | $FFA8 | IECOUT | Send byte to IEC bus |
2627
+ | $FFAB | UNTLK | Send UNTALK on IEC bus |
2628
+ | $FFAE | UNLSN | Send UNLISTEN on IEC bus |
2629
+ | $FFB1 | LISTEN | Command device on IEC to LISTEN |
2630
+ | $FFB4 | TALK | Command device on IEC to TALK |
2631
+ | $FFB7 | READST | Read I/O STATUS |
2632
+ | $FFBA | SETLFS | Set logical file parameters |
2633
+ | $FFBD | SETNAM | Set filename |
2634
+ | $FFC0 | OPEN | Open a file |
2635
+ | $FFC3 | CLOSE | Close a file |
2636
+ | $FFC6 | CHKIN | Set input channel |
2637
+ | $FFC9 | CHKOUT | Set output channel |
2638
+ | $FFCC | CLRCHN | Restore default I/O |
2639
+ | $FFCF | CHRIN | Get char from input |
2640
+ | $FFD2 | CHROUT | Output char |
2641
+ | $FFD5 | LOAD | Load file |
2642
+ | $FFD8 | SAVE | Save file |
2643
+ | $FFDB | SETTIM | Set jiffy clock |
2644
+ | $FFDE | RDTIM | Read jiffy clock |
2645
+ | $FFE1 | STOP | Test STOP key |
2646
+ | $FFE4 | GETIN | Get char from queue |
2647
+ | $FFE7 | CLALL | Close all files |
2648
+ | $FFEA | UDTIM | Update jiffy clock |
2649
+ | $FFED | SCREEN | Return screen dimensions |
2650
+ | $FFF0 | PLOT | Read / set cursor |
2651
+ | $FFF3 | IOBASE | Return base of CIA #1 |
2652
+
2653
+ ### Critical KERNAL internal addresses
2654
+
2655
+ A few internal KERNAL addresses are worth knowing because they're
2656
+ referenced by replacement IRQ handlers:
2657
+
2658
+ - **$EA31** — default IRQ handler entry (the one $0314 points to).
2659
+ After your custom raster IRQ, you typically `JMP $EA31` to chain
2660
+ back to the default handler.
2661
+ - **$EA7E** — acknowledge-and-exit tail of the default handler:
2662
+ `LDA $DC0D`, then the `$EA81` register restore and RTI. It does not
2663
+ run UDTIM or SCNKEY; chain here only when your own handler has done,
2664
+ or does not need, the jiffy clock, the STOP key and the keyboard.
2665
+ (This bullet used to say $EA7E kept UDTIM and the keyscan and skipped
2666
+ only the cursor blink; it skips all three — an agent following the
2667
+ old text ships a dead keyboard, a frozen TI$ and a dead RUN/STOP.)
2668
+ - **$EA81** — bare exit: PLA/TAY/PLA/TAX/PLA/RTI. Chain here from a
2669
+ handler that has acknowledged its own interrupt source.
2670
+ - **$FF48** — IRQ preamble (saves A, X, Y to stack). The default IRQ
2671
+ vector at $FFFE points here.
2672
+ - **$F6BC** — the STOP-key sample at the tail of UDTIM ($F69B falls
2673
+ into it): reads keyboard row 7 from $DC01 (SCNKEY leaves $DC00 = $7F,
2674
+ the row that holds RUN/STOP), debounces it and stores it in $91 for
2675
+ STOP ($FFE1 → $F6ED) to test, then RTS. Calling it yourself only
2676
+ refreshes $91. An earlier revision called this the "RUN/STOP loop
2677
+ entry" that RUN/STOP+RESTORE bounces to; it is not. RUN/STOP+RESTORE
2678
+ is the NMI handler ($FFFA → $FE43 → ($0318) = **$FE47**): it
2679
+ acknowledges CIA #2 ($DD0D — a CIA #2 NMI takes the RS-232 path
2680
+ instead), checks for a CBM80 cartridge ($FD02; a cartridge's $8002
2681
+ vector wins), then `JSR $F6BC`, `JSR $FFE1`, and if STOP is down falls
2682
+ into **$FE66**: RESTOR ($FD15), IOINIT ($FDA3), CINT ($E518),
2683
+ `JMP ($A002)` → $E37B warm start. RESTORE alone (STOP up) returns
2684
+ through $FEBC and does nothing. To do what RUN/STOP+RESTORE does from
2685
+ software use `SEI : JMP $FE66` (SYS 65126 from BASIC): the real path
2686
+ arrives with I set, RESTOR rewrites $0314–$0333 with no SEI of its
2687
+ own, and $E37B ends in CLI. Measured in VICE x64sc 3.10 against the
2688
+ 901227-03 ROM: `JSR $F6BC` returns with $91 = $FF; `SEI : JMP $FE66`
2689
+ clears the screen, restores $D020/$D021 to $0E/$06 and prints READY.
2690
+ with no banner; `JMP $FCE2` (SYS 64738, cold reset) prints the banner.
2691
+
2692
+ ## BASIC ROM map
2693
+
2694
+ The BASIC ROM at $A000-$BFFF (8 KB) contains Microsoft BASIC v2.0,
2695
+ licensed from Microsoft in 1977. The implementation is virtually
2696
+ identical to PET BASIC 4.0 minus the disk commands, plus a few C64
2697
+ quirks.
2698
+
2699
+ ### BASIC ROM layout
2700
+
2701
+ | Range | Function |
2702
+ |---|---|
2703
+ | $A000-$A00B | Cold/warm-start vectors ($E394/$E37B), "CBMBASIC" |
2704
+ | $A00C-$A389 | Statement dispatch ($A00C, entry-1), function dispatch ($A052), operator table, keyword text ($A09E), error messages, "OK"/"ERROR"/"IN"/"READY."/"BREAK" strings ($A364-$A389) |
2705
+ | $A38A-$A47F | FOR-stack search ($A38A), stack/memory checks ($A3FB, $A408), ERROR ($A437 = JMP ($0300)), READY ($A474) |
2706
+ | $A480-$A641 | Main loop ($A480 = JMP ($0302), $A483), insert line, INLIN ($A560), CRUNCH ($A57C), FNDLIN ($A613) |
2707
+ | $A642-$A741 | NEW ($A642), CLR ($A65E), LIST ($A69C, detokenize $A71A) |
2708
+ | $A742-$A9A4 | FOR ($A742), NEWSTT/GONE ($A7AE/$A7E4), RESTORE ($A81D), STOP/END ($A82F/$A831), CONT ($A857), RUN ($A871), GOSUB ($A883), GOTO ($A8A0), RETURN ($A8D2), DATA ($A8F8), IF ($A928), REM ($A93B), ON ($A94B), LINGET |
2709
+ | $A9A5-$AC05 | LET ($A9A5), PRINT# ($AA80), CMD ($AA86), PRINT ($AAA0), STROUT ($AB1E), GET ($AB7B), INPUT# ($ABA5), INPUT ($ABBF) |
2710
+ | $AC06-$AD1D | READ ($AC06) and its "?EXTRA IGNORED"/"?REDO FROM START" messages |
2711
+ | $AD1E-$AD89 | NEXT ($AD1E) |
2712
+ | $AD8A-$B07D | Expression evaluator: FRMNUM ($AD8A), CHKNUM ($AD8D), FRMEVL ($AD9E), EVAL ($AE86), string concatenation entry via $ADE5 |
2713
+ | $B07E-$B08A | DIM (dispatch entry $B081) |
2714
+ | $B08B-$B3B2 | PTRGET ($B08B) variable/array lookup, FRE ($B37D), POS ($B39E) |
2715
+ | $B3B3-$B6DA | DEF/FN ($B3B3), STR$ ($B465), string allocation ($B475), garbage collection ($B526), concatenation ($B63D), string move ($B67A) |
2716
+ | $B6DB-$B80C | String functions: CHR$ ($B6EC), LEFT$ ($B700), RIGHT$ ($B72C), MID$ ($B737), LEN ($B77C), ASC ($B78B), VAL ($B7AD), GETNUM/GETADR ($B7EB/$B7F7) |
2717
+ | $B80D-$BAE1 | PEEK ($B80D), POKE ($B824), WAIT ($B82D), FADDH/FSUB/FADD ($B849/$B850/$B867), LOG ($B9EA), FMULT ($BA28), CONUPK ($BA8C) |
2718
+ | $BAE2-$BFFF | MUL10 ($BAE2), DIV10 ($BAFE), FDIV ($BB0F), MOVFM ($BBA2), MOVMF ($BBD4), MOVFA ($BBFC), MOVAF ($BC0C), SGN ($BC39), ABS ($BC58), FCOMP ($BC5B), INT ($BCCC), FIN ($BCF3), FOUT ($BDDD), SQR ($BF71), power, EXP ($BFED; ends with JMP $E000 and continues in the KERNAL ROM) |
2719
+
2720
+ An earlier version of this table placed the string functions at $A9A5
2721
+ (that is LET), FIN/FOUT at $AF7E-$B6DA (they are at $BCF3/$BDDD) and
2722
+ the cold start in $BAE2-$BFFF; BASIC's cold start ($E394), warm start
2723
+ ($E37B), init ($E3BF), default vector table ($E447) and sign-on text
2724
+ ($E460ff), plus the LOAD/SAVE/VERIFY/SYS/OPEN/CLOSE statement handlers
2725
+ ($E12A-$E1C7) and RND/COS/SIN/TAN/ATN, live in the KERNAL ROM, where EXP
2726
+ also finishes. All addresses read from basic-901226-01.bin /
2727
+ kernal-901227-03.bin.
2728
+
2729
+ ### Useful BASIC entry points
2730
+
2731
+ Even if you're writing pure assembly, a few BASIC ROM routines are
2732
+ worth calling. The addresses and behaviour below are read from the
2733
+ basic-901226-01 ROM image; the names are the conventional Microsoft
2734
+ BASIC labels, corroborated by the call sites (PRINT's number path is
2735
+ FOUT's only caller, FPWRT's tail is EXP's only caller, FOR loads its
2736
+ default STEP through $BBA2). An earlier version of this list had five of
2737
+ the six entries mislabelled; each line says what it used to claim.
2738
+
2739
+ - **$A560** — INLIN (read a line of input into the buffer at $0200);
2740
+ an earlier version listed it as CRUNCH.
2741
+ - **$A57C** — CRUNCH body (tokenize the line at $0200); $A579 is the
2742
+ `JMP ($0304)` that reaches it.
2743
+ - **$AB1E** — STROUT (print a string terminated by $00 or a quote,
2744
+ pointer in A=lo, **Y=hi**; A, X and Y are all clobbered). An earlier
2745
+ version of this line said X=hi; STRLIT at $B487 stores `STA $6F /
2746
+ STY $70` and overwrites X with #$22 before ever reading it, so a call
2747
+ with the high byte in X prints from a garbage pointer.
2748
+ - **$BA8C** — CONUPK (unpack the 5-byte float at (A/Y) into ARG,
2749
+ $69-$6E); an earlier version called this MOVFA. MOVFA (ARG → FAC) is
2750
+ $BBFC.
2751
+ - **$BBA2** — MOVFM (load FAC from the 5-byte float at (A/Y)); an
2752
+ earlier version called this FIN.
2753
+ - **$BC0C** — MOVAF (copy FAC to ARG); an earlier version called this
2754
+ FOUT.
2755
+ - **$BCF3** — FIN (ASCII at TXTPTR to FAC). Call it the way the ROM
2756
+ does: JSR $0079 (CHRGOT) or $0073 (CHRGET) first, so A holds the first
2757
+ character and the carry is clear for a digit; both ROM callers ($AE8A,
2758
+ $B7D7) do exactly that.
2759
+ - **$BDDD** — FOUT (FAC to a $00-terminated ASCII string starting at
2760
+ $0100; A/Y return the pointer).
2761
+ - **$BFED** — EXP (e^FAC; continues at $E000). Earlier revisions called
2762
+ this MULT2.
2763
+
2764
+ ### BASIC vectors (RAM)
2765
+
2766
+ BASIC's behavior is partly steered by 6 vector pairs at $0300-$030B (an
2767
+ earlier revision of this page said 7; the range is 12 bytes, and the
2768
+ defaults are the six words the BASIC cold start copies from the ROM
2769
+ table at $E447 via the loop at $E453). $030C-$030F hold the SYS register
2770
+ images and $0310-$0312 the USR JMP, not further vectors.
2771
+
2772
+ | Vector | Default | Function |
2773
+ |---------|---------|------------------------------------|
2774
+ | IERROR | $E38B | Print BASIC error message |
2775
+ | IMAIN | $A483 | Main BASIC interpreter loop |
2776
+ | ICRNCH | $A57C | Tokenize a line |
2777
+ | IQPLOP | $A71A | De-tokenize for LIST |
2778
+ | IGONE | $A7E4 | Execute a tokenized statement |
2779
+ | IEVAL | $AE86 | Evaluate an expression |
2780
+
2781
+ These get re-initialized to the defaults above on every reset. Custom
2782
+ BASIC extensions (Simon's BASIC, Power BASIC, etc.) wedge in by patching
2783
+ these vectors.
2784
+
2785
+ ## Character ROM map
2786
+
2787
+ The character generator ROM is a 4 KB ROM that the VIC-II reads to
2788
+ fetch the pixel patterns for text characters. The CPU can also read
2789
+ it via $D000-$DFFF, but only when CHAREN is 0 (and either LORAM or
2790
+ HIRAM is 1 so the I/O area isn't kept visible).
2791
+
2792
+ ### Two character sets
2793
+
2794
+ The character ROM holds two complete 256-character sets:
2795
+
2796
+ - **Set 1 (uppercase + graphics):** $D000-$D7FF (2 KB) — the default
2797
+ after reset.
2798
+ - **Set 2 (uppercase + lowercase):** $D800-$DFFF (2 KB) — switched in
2799
+ by pressing SHIFT+C= or by VIC's character pointer.
2800
+
2801
+ Each character is 8 bytes (8 × 8 = 64 pixels). 256 chars × 8 bytes =
2802
+ 2 KB per set.
2803
+
2804
+ ### Character ROM layout
2805
+
2806
+ | Range | Set | Codes | Contents |
2807
+ |--------------|-----|---------|-----------------------------------------------------------------|
2808
+ | $D000-$D0FF | 1 | 0-31 | @, A-Z, [ £ ] ↑ ← |
2809
+ | $D100-$D1FF | 1 | 32-63 | space ! " # $ % & ' ( ) * + , - . / 0-9 : ; < = > ? |
2810
+ | $D200-$D3FF | 1 | 64-127 | graphics: horizontal bar (64), card suits, box-drawing, blocks |
2811
+ | $D400-$D7FF | 1 | 128-255 | reverse video of 0-127 |
2812
+ | $D800-$D8FF | 2 | 0-31 | @, a-z, [ £ ] ↑ ← |
2813
+ | $D900-$D9FF | 2 | 32-63 | space, punctuation, 0-9 (byte-identical to set 1) |
2814
+ | $DA00-$DBFF | 2 | 64-127 | horizontal bar (64), A-Z (65-90), remaining graphics |
2815
+ | $DC00-$DFFF | 2 | 128-255 | reverse video of set 2's 0-127 |
2816
+
2817
+ An earlier version of this table put space, punctuation and the digits
2818
+ at codes 64-127 and listed a backslash at code 28; measured against
2819
+ `chargen-901225-01.bin`, the digits and punctuation are codes 32-63
2820
+ (`0` is code 48 at $D180), codes 64-127 are the graphics set (code 64 at
2821
+ $D200 is the horizontal bar), and code 28 is £ — the C64 character ROM
2822
+ has no backslash. The reverse-video halves are the bitwise inverse of
2823
+ codes 0-127 for every glyph except `@`: reversed `@` (code 128, $D400
2824
+ and $DC00) differs from the true inverse by one pixel in row 6 ($99
2825
+ where NOT $62 = $9D).
2826
+
2827
+ ### How VIC-II finds the character set
2828
+
2829
+ The VIC-II's $D018 register controls character pointer + screen pointer
2830
+ within the current 16 KB VIC bank. The default value $14 means:
2831
+
2832
+ - Screen RAM at offset $400 inside VIC bank 0 → $0400-$07E7.
2833
+ - Character ROM at offset $1000 inside VIC bank 0 → $1000-$1FFF (a
2834
+ shadow of $D000-$D7FF inside the VIC bank).
2835
+
2836
+ The character ROM is *shadowed* into VIC banks 0 and 2:
2837
+ - Bank 0 ($0000-$3FFF): char ROM at $1000-$1FFF.
2838
+ - Bank 2 ($8000-$BFFF): char ROM at $9000-$9FFF.
2839
+ - Banks 1 and 3 see RAM there.
2840
+
2841
+ This is why custom character sets typically live in VIC bank 1 or 3:
2842
+ those banks don't have the character ROM shadowed, so the VIC sees
2843
+ whatever RAM is there.
2844
+
2845
+ ### Copying the char ROM to RAM
2846
+
2847
+ The canonical pattern for a custom font is to copy the char ROM out
2848
+ of $D000-$D7FF into RAM somewhere (often $3000-$37FF), then update
2849
+ $D018 to point at the RAM copy. The copy code:
2850
+
2851
+ ```
2852
+ SEI ; disable IRQs (KERNAL will choke)
2853
+ LDA $01
2854
+ PHA
2855
+ AND #$FB ; clear CHAREN bit (CHAREN = 0 → char ROM visible)
2856
+ STA $01
2857
+
2858
+ LDX #$00
2859
+ loop:
2860
+ LDA $D000,X
2861
+ STA $3000,X
2862
+ LDA $D100,X
2863
+ STA $3100,X
2864
+ ... ; repeat for $D200-$D7FF
2865
+ INX
2866
+ BNE loop
2867
+
2868
+ PLA
2869
+ STA $01 ; restore $01 (CHAREN back to default)
2870
+ CLI
2871
+ ```
2872
+
2873
+ After copying, set $D018 bits 1-3 to point at $3000 (= $0C):
2874
+ `LDA #$1C / STA $D018`.
2875
+
2876
+ ## I/O area
2877
+
2878
+ The I/O area at $D000-$DFFF is only present when CHAREN = 1 *and*
2879
+ LORAM = 1 *or* HIRAM = 1. In the default config it's a 4 KB window
2880
+ containing four chips' worth of registers (VIC-II, SID, CIA #1, CIA #2)
2881
+ plus 1 KB of Color RAM plus 2 × 256 bytes of cartridge I/O. (An earlier
2882
+ version of this sentence said five; the Color RAM is a nibble-wide
2883
+ static RAM with no registers and is already counted separately here.)
2884
+
2885
+ ### Sub-regions
2886
+
2887
+ | Range | Contents |
2888
+ |--------------|----------------------------------------------|
2889
+ | $D000-$D3FF | VIC-II registers + 16-fold shadow |
2890
+ | $D400-$D7FF | SID registers + 32-fold shadow |
2891
+ | $D800-$DBFF | Color RAM (1024 × 4 bits) |
2892
+ | $DC00-$DCFF | CIA #1 + 16-fold shadow |
2893
+ | $DD00-$DDFF | CIA #2 + 16-fold shadow |
2894
+ | $DE00-$DEFF | I/O expansion area 1 (cartridge) |
2895
+ | $DF00-$DFFF | I/O expansion area 2 (cartridge) |
2896
+
2897
+ ### Shadow / mirror behavior
2898
+
2899
+ Each chip occupies a small number of register bytes but is decoded
2900
+ across a much larger address window:
2901
+
2902
+ - **VIC-II**: 47 registers ($D000-$D02E) + 1 unused. The chip decodes
2903
+ on the lower 6 bits of the address, so the next 64-byte block
2904
+ ($D040-$D07F) is a mirror, and the pattern repeats every 64 bytes
2905
+ through $D3FF.
2906
+ - **SID**: 29 registers ($D400-$D41C). Decoded on the lower 5 bits, so
2907
+ $D420-$D43F mirrors $D400-$D41F, repeating every 32 bytes through
2908
+ $D7FF.
2909
+ - **Color RAM**: 1024 bytes; no shadow within $D800-$DBFF.
2910
+ - **CIA #1**: 16 registers ($DC00-$DC0F). Mirrors every 16 bytes through
2911
+ $DCFF.
2912
+ - **CIA #2**: 16 registers ($DD00-$DD0F). Mirrors every 16 bytes through
2913
+ $DDFF.
2914
+
2915
+ Software should always address the canonical low addresses ($D000,
2916
+ $D400, $DC00, $DD00) — the shadows are an artifact of partial address
2917
+ decoding, not a feature, and some clones don't replicate them.
2918
+
2919
+ ### When you read from a non-existent I/O location
2920
+
2921
+ If you read from an address in the I/O block that no chip drives, you
2922
+ get an "open bus" read: whatever the data bus last carried, which on a
2923
+ C64 is usually the byte the VIC-II fetched in the preceding half-cycle.
2924
+ On a stock machine that means $DE00-$DFFF with no cartridge fitted (and
2925
+ the upper nibble of Color RAM, see $D800 above). The VIC-II's unused
2926
+ addresses $D02F-$D03F are *not* open bus: the chip answers $FF and
2927
+ ignores writes (measured in VICE x64sc; see
2928
+ [vic-ii-reference.md](vic-ii-reference.md)). An earlier version of this
2929
+ section used $D02F as the open-bus example, which it is not. Open-bus
2930
+ values are unpredictable; don't rely on them.
2931
+
2932
+ ### I/O timing
2933
+
2934
+ Reading or writing any I/O register takes the normal 4 CPU cycles for
2935
+ the access. The CIAs and SID have no wait states. The VIC-II can,
2936
+ however, *steal* the bus from the CPU on badlines and during sprite
2937
+ DMA, which appears to the CPU as the instruction simply taking longer
2938
+ to execute. See [vic-ii-reference.md](vic-ii-reference.md#raster-system).
2939
+
2940
+ ## Pitfalls
2941
+
2942
+ - **$0001 — clobbering KERNAL zeropage.** Writing to addresses in
2943
+ $90-$FE while interrupts are enabled risks the KERNAL IRQ handler
2944
+ reading or writing the same byte. The IRQ ticks about 60 times a
2945
+ second on both PAL and NTSC (CIA #1 Timer A, latch $4025 on PAL and
2946
+ $4295 on NTSC — the period is latch+1 cycles; see $00A0-$00A2; an
2947
+ earlier version said "or 50 Hz on PAL") and runs UDTIM ($FFEA) + the
2948
+ keyscan, both of which touch zeropage. Disable IRQs (SEI) before
2949
+ manipulating KERNAL-owned zero-page bytes.
2950
+
2951
+ - **$0000 / $0001 — DDR must be set first.** Writing to the
2952
+ processor-port data register $01 only affects pins configured as
2953
+ outputs by the DDR at $00. If a prior program (or a bug) has left
2954
+ bits of $00 as inputs, your bank-switch write won't take. Always
2955
+ write the expected DDR ($2F for a stock C64) before writing $01:
2956
+ `LDA #$2F / STA $00 / LDA #$35 / STA $01`.
2957
+
2958
+ - **$01 bit 6/7 — unconnected pins on stock C64.** Bits 6 and 7 of $01
2959
+ are not bonded out on a stock C64 (the C128's 8502 gives bit 6 to the
2960
+ CAPS LOCK key; whether SX-64 boards differ is sometimes claimed and
2961
+ not verified here).
2962
+ Reading them yields the last-driven value with very slow capacitive
2963
+ decay. Treat as undefined and mask off when reading $01 for the
2964
+ bank-config bits.
2965
+
2966
+ - **$E000-$FFFF — IRQ vectors live in RAM when KERNAL banked out.**
2967
+ When you bank KERNAL ROM out (any mode with HIRAM = 0), $FFFA/$FFFB
2968
+ (NMI), $FFFC/$FFFD (RESET), and $FFFE/$FFFF (IRQ/BRK) are *RAM*. The
2969
+ CPU still reads them on interrupts. You *must* populate those RAM
2970
+ bytes with valid handler addresses before banking out. Forgetting
2971
+ causes the machine to jump to wherever-uninitialized-RAM-happens-to-
2972
+ point and crash. The fix: SEI; write your handler addresses into
2973
+ $FFFA-$FFFF; then write $01.
2974
+
2975
+ - **$D018 — CHROUT changes it for exactly two codes, and the keyboard
2976
+ can too.** CHROUT does not touch $D018 on ordinary output (an earlier
2977
+ version of this page said it wrote $D018 on every call "in screen
2978
+ mode 1"; the KERNAL has no such path — the 901227-03 ROM holds only
2979
+ the two conditional stores named here). PETSCII $0E / CHR$(14) *sets*
2980
+ bit 1 (charset 2, lower case) and $8E / CHR$(142) *clears* it
2981
+ ($EC44-$EC5B); the IRQ keyscan *toggles* bit 1 with EOR when SHIFT+C=
2982
+ is pressed ($EB48-$EB61), once per press and only while $0291 bit 7
2983
+ is clear. Measured in VICE x64sc: with $D018 preset to $1C, 60
2984
+ CHROUTs of "A" and 30 scrolling CRs left it unchanged, and a sweep of
2985
+ all 256 codes changed it exactly twice, at $0E and $8E. A custom font
2986
+ is the usual victim, because bit 1 selects the odd 2 KB half: a font
2987
+ at $2000 ($D018 = $18) becomes $2800 ($1A) on SHIFT+C=. Remedies: do
2988
+ not send $0E/$8E; lock the keyboard path with CHR$(8) (or POKE $0291
2989
+ = $80) — which does *not* suppress the CHROUT codes (measured: after
2990
+ CHR$(8), CHR$(14) still set bit 1); or re-write $D018 in your own
2991
+ IRQ. Wrapping every CHROUT in a save/restore of $D018 is unnecessary.
2992
+
2993
+ - **$DD00 — VIC-II bank changes via CIA #2 are inverted.** Setting
2994
+ $DD00 bits 0-1 to 11 (binary) selects VIC bank 0, not 3. The bits
2995
+ are inverted because the lines drive the VIC's address pins active-
2996
+ low. Off-by-one bank selection is a classic crash mode. (An earlier
2997
+ version of this entry was headed $DC00-$DCFF; that range is CIA #1,
2998
+ the keyboard/joystick chip, and writing bank bits there leaves the
2999
+ VIC where it was — measured in VICE x64sc.)
3000
+
3001
+ - **$D7FF — SID write side-effects via shadow.** A write to $D7FF lands
3002
+ in SID register $1F (which is undefined). Some emulators handle this
3003
+ differently from real hardware. Avoid writing to SID shadow addresses;
3004
+ always use the canonical $D400-$D41C window.
3005
+
3006
+ - **$02, $00FB-$00FE — five bytes of safe zeropage.** When coexisting
3007
+ with BASIC and KERNAL, $02 and $FB-$FE are the only zero-page bytes
3008
+ neither ROM touches after reset. Using $03-$8F clobbers BASIC; using
3009
+ $90-$FA clobbers KERNAL; $FF is BASIC's FOUT workspace. (Earlier text
3010
+ counted four bytes and put $02 in BASIC's range; the ROM bytes show no
3011
+ reference to $02 beyond RAMTAS's reset clear.) The fix: take over the
3012
+ machine, or stay inside those five.
3013
+
3014
+ - **$0100-$01FF — stack page wraparound.** The stack pointer S is 8
3015
+ bits and wraps from $00 to $FF without warning — but only your own
3016
+ machine code can get there. BASIC guards its stack: every GOSUB, FOR
3017
+ and expression-nesting level first calls the check at $A3FB, which
3018
+ raises ?OUT OF MEMORY (error 16) unless the stack pointer seen inside
3019
+ that routine is at least 2×n+$3E ($44 for GOSUB, n=3; $50 for FOR,
3020
+ n=9; $40 per expression level, n=1). So `10 GOSUB 10` stops with
3021
+ "?OUT OF MEMORY ERROR IN 10" and S never falls below about $40 —
3022
+ measured in VICE x64sc: the GOSUB loop trips with S=$40, 64 bytes of
3023
+ the page still unused. An earlier version of this page said a deep
3024
+ BASIC nest wraps S past zero with no trap; it does not. PHA/JSR chains
3025
+ in your own code under an existing stack have no such guard: that is
3026
+ where the wrap bites.
3027
+
3028
+ - **$0314-$0333 — vector resetting on KERNAL RESTOR.** The KERNAL's
3029
+ RESTOR routine ($FF8A, body at $FD15) copies the ROM default table at
3030
+ $FD30-$FD4F over $0314-$0333. Calling RESTOR, VECTOR ($FF8D) with
3031
+ C=0, or anything that runs the reset ($FCE2) or RUN/STOP+RESTORE
3032
+ ($FE47) sequence — both of which call RESTOR themselves — wipes your
3033
+ custom IRQ handler. IOINIT ($FF84) and CINT ($FF81) do *not*: the only
3034
+ callers of $FD15 in the ROM are $FCF8 and $FE66, and in VICE x64sc a
3035
+ custom $0314/$0315 survives both IOINIT and CINT and is replaced by
3036
+ RESTOR (an earlier version of this page said IOINIT calls RESTOR
3037
+ internally; it does not). Hook by writing $0314/$0315 *after* the
3038
+ last such call.
3039
+
3040
+ - **$0800 — BASIC program-start sentinel.** BASIC requires the byte
3041
+ just below TXTTAB — $0800, not $0801 as an earlier revision of this
3042
+ entry said — to be $00 (see $0800 above). It stands in for the
3043
+ end-of-line marker of the line before the first one: RUN and NEW both
3044
+ reset TXTPTR to TXTTAB−1 (RUNC, $A68E) and the next-statement fetch
3045
+ ($A7BE) reads that byte, so anything but $00 there answers
3046
+ `?SYNTAX ERROR` (measured in VICE x64sc: `POKE 2048,1` then RUN →
3047
+ `?SYNTAX ERROR`; NEW → `?SYNTAX ERROR` too, although the program is
3048
+ still erased). LIST is unaffected — it walks the link chain from
3049
+ TXTTAB. $0801–$0802 are the first line's link pointer and are
3050
+ non-zero in any program with a line (`0B 08` for the stub below). A
3051
+ machine-language file with load address $0801 cannot disturb the
3052
+ sentinel; what breaks it is a file that loads at $0800 with a non-zero
3053
+ first byte, or code that uses $0800 as scratch. Use a BASIC SYS stub
3054
+ at $0801; a raw load at $0801 without one is started with `SYS 2049`,
3055
+ not RUN.
3056
+
3057
+ - **$8000 — "CBM80" cart auto-start magic.** At reset (and on NMI) the
3058
+ KERNAL routine at $FD02 (called from $FCE2) compares $8004-$8008 with
3059
+ `C3 C2 CD 38 30` ("CBM80" in PETSCII, the letters shifted); on a
3060
+ match reset does `JMP ($8000)` before any initialisation, and RESTORE
3061
+ jumps through $8002/$8003. A cartridge at $8000-$9FFF (or a 16 KB one
3062
+ at $8000-$BFFF) autostarts this way; ROM at $A000-$BFFF alone cannot,
3063
+ because the check runs while BASIC ROM is still mapped there.
3064
+ $8000-$9FFF is ordinary RAM when no cartridge is inserted, so if a
3065
+ program leaves those nine bytes looking like a cartridge header, the
3066
+ next reset jumps through whatever is at $8000 instead of booting
3067
+ BASIC — a trick reset-protected software has used deliberately, and a
3068
+ crash mode if it is left there by accident (which programs do it is
3069
+ not verified here). (An earlier version placed the check at
3070
+ $A000-$A008; $A000 is BASIC ROM at reset and is never checked — the
3071
+ ROM compares $8003,X, verified against kernal-901227-03 and in VICE.)
3072
+
3073
+ - **$D018 — pointing at non-existent character data.** The character-set
3074
+ bits in $D018 (bits 1-3) point to 2 KB chunks inside the current VIC
3075
+ bank. If you select a chunk that has no font data — say a screen
3076
+ RAM range — the display will show garbage. Always set $D018 to a
3077
+ known-good font pointer before enabling display.
3078
+
3079
+ - **$DD00 — touching CIA #2 disables RS-232.** Writes to $DD00 control
3080
+ both VIC-II bank selection *and* the RS-232 transmit line. If you're
3081
+ using RS-232 + want to change VIC bank, you must mask carefully:
3082
+ preserve bits 2-7 of $DD00 while updating bits 0-1.
3083
+
3084
+ - **$FFFE-$FFFF — IRQ fired during bank switch.** If an IRQ fires between
3085
+ your write to $01 (banking KERNAL out) and your install of the new
3086
+ IRQ handler, the CPU will read $FFFE/$FFFF from RAM and crash. *Always*
3087
+ SEI before banking; install $FFFA-$FFFF; only then CLI.
3088
+
3089
+ - **$D012 raster wrap vs $D011 bit 7.** $D012 reports the low 8 bits
3090
+ of the current raster line, but PAL has 312 raster lines (NTSC has
3091
+ 263). Lines ≥ 256 read $D012 with bit 8 in $D011. Comparing $D012
3092
+ to 256+ requires combining $D011 bit 7 — see
3093
+ [vic-ii-reference.md](vic-ii-reference.md#raster-system).
3094
+
3095
+ - **Mixing CHAREN + raster IRQs.** If you bank in the character ROM
3096
+ ($01 with CHAREN = 0) and a raster IRQ fires, the IRQ handler will
3097
+ read $DC0D as part of acknowledging — but $DC0D is now reading
3098
+ character-ROM data, not the CIA. The KERNAL's IRQ acknowledge will
3099
+ not clear the CIA, and the IRQ will fire again immediately, locking
3100
+ the machine. Always bank I/O in before allowing IRQs.
3101
+
3102
+ - **Color RAM upper nibble is random.** Reads from $D800-$DBFF return
3103
+ 4 bits of color in the lower nibble and garbage in the upper. Always
3104
+ AND with #$0F when reading Color RAM.
3105
+
3106
+ - **Stack page double-duty.** BASIC's float-to-ASCII routine FOUT
3107
+ ($BDDD, in the BASIC ROM; reached by PRINT and STR$, and by the
3108
+ line-number print that LIST and the `IN` message use) builds its
3109
+ string at the bottom of the stack page. The PRINT entry writes
3110
+ $0100-$010F in the worst case (`-1.23456789E+28` is 15 characters
3111
+ plus the terminator; the routine caps output at nine digits); the
3112
+ STR$ and LIST entries start one byte lower, at $00FF. Measured in
3113
+ VICE x64sc by seeding $00F8-$011F and calling the routine. An earlier
3114
+ version of this note called the routine the KERNAL's, said it was
3115
+ reached through CHROUT, and stopped the buffer at $010A — CHROUT
3116
+ prints one character and never converts a number. Anything parked at
3117
+ $00FF-$010F (a depacker, a saved register block, the tape error log)
3118
+ is overwritten the next time BASIC prints a number. Pushed data is
3119
+ only reached once S has descended below $10 — about 240 bytes deep —
3120
+ so "PHA'd 11 bytes" was never the hazard.
3121
+
3122
+ - **$DE00/$DF00 open bus.** With no cartridge, reads from $DE00-$DFFF
3123
+ return open-bus garbage (typically the last VIC fetch). Don't probe
3124
+ for cartridge presence by reading these — write a known pattern
3125
+ and read it back, or use a dedicated detection sequence.
3126
+
3127
+ - **$0000/$0001 cassette-motor write side-effect.** Writing to $01
3128
+ with bit 5 cleared turns on the cassette motor. If your bank-switch
3129
+ code uses `LDA #$05 / STA $01`, you're also turning the cassette
3130
+ motor *on*. Mask carefully: typical bank values keep bit 5 = 1.
3131
+
3132
+ - **MEMSIZ is snapshotted by BASIC.** $0283/$0284 is the KERNAL's
3133
+ top-of-memory, set by RAMTAS (via $FE2D) at reset. BASIC copies it
3134
+ into its own MEMSIZ ($37/$38) and STRBOT/FRETOP ($33/$34) once, at
3135
+ cold start ($E40A-$E414: SEC / JSR $FF99 / STX $37 / STY $38 /
3136
+ STX $33 / STY $34), and never reads $0283/$0284 again — the BASIC ROM
3137
+ contains no reference to it. Poking $0283/$0284, or calling MEMTOP
3138
+ ($FF99) with C=0 — which does nothing but STX $0283 / STY $0284 —
3139
+ therefore has no effect on BASIC's string space (an earlier revision
3140
+ of this page said to call MEMTOP instead of poking; measured in VICE,
3141
+ both leave BASIC's top at $A000 and the next string is placed just
3142
+ under it). To reserve memory above BASIC, lower $37/$38 and then CLR,
3143
+ which recopies MEMSIZ into FRETOP: `POKE 55,lo: POKE 56,hi: CLR`
3144
+ (e.g. `POKE 56,128: CLR` for $8000). The one runtime path that does
3145
+ resync BASIC from the KERNAL's top is RS-232: OPEN/CLOSE of device 2
3146
+ returns pseudo-error $F0 with the new top in X/Y ($F47D), and BASIC's
3147
+ error handler ($E0F9) stores it to $37/$38 and jumps into CLR at
3148
+ $A663 — which is why OPEN 2,2 performs an automatic CLR and lowers
3149
+ BASIC's top by 512 bytes.
3150
+
3151
+ - **TXTPTR mid-statement.** Modifying $7A/$7B (TXTPTR) while a BASIC
3152
+ statement is executing makes BASIC read its next token from your
3153
+ chosen address. Useful for "GOSUB to dynamic line number" but easy
3154
+ to crash if you point at non-BASIC data.
3155
+
3156
+ - **$0801 BASIC stub for ML programs.** The canonical ML autostart
3157
+ uses a BASIC stub like `10 SYS 2064` at $0801: `0B 08` (link to the
3158
+ next line at $080B) `0A 00` (line 10) `9E` (SYS token) `32 30 36 34`
3159
+ ("2064" as PETSCII digits) `00` (end of line) `00 00` (end of
3160
+ program) — 12 bytes, $0801-$080C — with the ML at $0810 (= 2064).
3161
+ This is byte-for-byte what KickAssembler 5.25's `BasicUpstart2` emits
3162
+ for code at `*=$0810`; the assembler zero-fills $080D-$080F. If you
3163
+ type the line at the keyboard instead, the tokenizer keeps the space
3164
+ after SYS: `0C 08 0A 00 9E 20 32 30 36 34 00 00 00`, 13 bytes to
3165
+ $080D with the link at $080C (measured in VICE x64sc 3.10). An earlier
3166
+ version of this bullet mixed the two forms — the $20 space with the
3167
+ 12-byte link $080B — and put the ML at $080D, which SYS 2064 does not
3168
+ reach ($080D is 2061). Skipping the stub and loading raw code at
3169
+ $0801 means the user must type `SYS 2049` (2049 = $0801).
3170
+
3171
+ - **CIA timers count φ2 cycles.** In the default input mode (CRA bit 5
3172
+ = 0, CRB bits 5-6 = 00) a timer *decrements* once per CPU cycle —
3173
+ 985,248/s PAL, 1,022,727/s NTSC — and underflows every latch+1 cycles
3174
+ (measured in VICE x64sc: latch 9 gives 400 underflows in 4006
3175
+ cycles). There is no prescaler; the rate is set by the latch, and
3176
+ Timer B can instead be clocked by Timer A underflows (CRB INMODE 10,
3177
+ also measured), or either timer by CNT-pin edges (datasheet; not
3178
+ measurable in VICE). Code copied from a platform whose timers tick at
3179
+ a divided clock will time wrong. (An earlier version of this bullet
3180
+ said the timer "underflows once per CPU cycle" under a "DC clock" and
3181
+ that the divisor was fixed; it decrements once per cycle, and Timer
3182
+ B's input is selectable.)
3183
+
3184
+ <!-- doc-type: hardware-reference -->