c64bridge 0.5.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 (138) hide show
  1. package/.github/prompts/README.md +6 -0
  2. package/.github/prompts/assembly-routine-session.prompt.md +13 -0
  3. package/.github/prompts/basic-program-session.prompt.md +13 -0
  4. package/.github/prompts/drive-management-session.prompt.md +13 -0
  5. package/.github/prompts/petscii-art-session.prompt.md +13 -0
  6. package/.github/prompts/play-classic-game.prompt.md +13 -0
  7. package/.github/prompts/sid-music-session.prompt.md +13 -0
  8. package/AGENTS.md +173 -0
  9. package/LICENSE +339 -0
  10. package/README.md +582 -0
  11. package/data/README.md +16 -0
  12. package/data/api/basic-api-spec.md +154 -0
  13. package/data/api/kernal-api-spec.md +68 -0
  14. package/data/assembly/assembly-spec.md +196 -0
  15. package/data/assembly/examples/effects/sinewave.asm +5 -0
  16. package/data/assembly/examples/graphics/sprite/mover.asm +15 -0
  17. package/data/assembly/examples/sid-file-structure.md +8 -0
  18. package/data/assembly/examples/sid-overview.md +11 -0
  19. package/data/audio/examples/Biosphere.sid +0 -0
  20. package/data/audio/sid-file-structure.md +91 -0
  21. package/data/audio/sid-programming-best-practices.md +198 -0
  22. package/data/audio/sid-spec.md +166 -0
  23. package/data/audio/sidwave.md +131 -0
  24. package/data/audio/sidwave.schema.json +64 -0
  25. package/data/basic/basic-spec.md +230 -0
  26. package/data/basic/examples/audio/alle-meine-entchen-expressive.bas +41 -0
  27. package/data/basic/examples/audio/c-major-scale-pleasant.bas +29 -0
  28. package/data/basic/examples/io/joystick.bas +5 -0
  29. package/data/basic/examples/video/bounce.bas +8 -0
  30. package/data/basic/examples/video/entchen-petscii.bas +16 -0
  31. package/data/basic/examples/video/hello-world.bas +4 -0
  32. package/data/basic/examples/video/wave.bas +7 -0
  33. package/data/context/bootstrap.md +148 -0
  34. package/data/context/chat.md +36 -0
  35. package/data/drive/drive-spec.md +215 -0
  36. package/data/embeddings_asm.json +1 -0
  37. package/data/embeddings_basic.json +1 -0
  38. package/data/embeddings_hardware.json +1 -0
  39. package/data/embeddings_mixed.json +1 -0
  40. package/data/embeddings_other.json +1 -0
  41. package/data/io/cia-spec.md +115 -0
  42. package/data/io/io-spec.md +124 -0
  43. package/data/memory/kernal-memory-map.md +227 -0
  44. package/data/memory/low-memory-map.md +133 -0
  45. package/data/memory/memory-map.md +186 -0
  46. package/data/printer/printer-commodore-bitmap.md +31 -0
  47. package/data/printer/printer-commodore.md +28 -0
  48. package/data/printer/printer-epson-bitmap.md +29 -0
  49. package/data/printer/printer-epson.md +30 -0
  50. package/data/printer/printer-prompts.md +29 -0
  51. package/data/printer/printer-spec.md +180 -0
  52. package/data/video/character-set.csv +257 -0
  53. package/data/video/vic-spec.md +276 -0
  54. package/dist/assemblyConverter.js +1299 -0
  55. package/dist/audio/record_and_analyze_audio.js +292 -0
  56. package/dist/basicConverter.js +193 -0
  57. package/dist/bootstrap/stdio-logger.js +32 -0
  58. package/dist/c64Client.js +1052 -0
  59. package/dist/chargen.js +197 -0
  60. package/dist/config.js +142 -0
  61. package/dist/context.js +157 -0
  62. package/dist/device.js +413 -0
  63. package/dist/index.js +7 -0
  64. package/dist/knowledge.js +454 -0
  65. package/dist/logger.js +182 -0
  66. package/dist/loggingHttpClient.js +88 -0
  67. package/dist/mcp-server.js +426 -0
  68. package/dist/petscii.js +135 -0
  69. package/dist/petsciiArt.js +356 -0
  70. package/dist/platform.js +82 -0
  71. package/dist/prompts/registry.js +560 -0
  72. package/dist/rag/discover.config.json +41 -0
  73. package/dist/rag/discover.js +401 -0
  74. package/dist/rag/embeddings.js +60 -0
  75. package/dist/rag/externalFetcher.js +934 -0
  76. package/dist/rag/indexer.js +770 -0
  77. package/dist/rag/init.js +139 -0
  78. package/dist/rag/knowledgeIndex.js +361 -0
  79. package/dist/rag/loggingRetriever.js +46 -0
  80. package/dist/rag/rateLimiter.js +127 -0
  81. package/dist/rag/retriever.js +74 -0
  82. package/dist/rag/sources.csv +31 -0
  83. package/dist/rag/types.js +5 -0
  84. package/dist/rag/urlUtils.js +38 -0
  85. package/dist/sidplayRunner.js +240 -0
  86. package/dist/sidwave.js +101 -0
  87. package/dist/sidwaveCompiler.js +329 -0
  88. package/dist/tools/audio.js +944 -0
  89. package/dist/tools/developer.js +515 -0
  90. package/dist/tools/errors.js +59 -0
  91. package/dist/tools/graphics.js +469 -0
  92. package/dist/tools/machineControl.js +267 -0
  93. package/dist/tools/memory.js +224 -0
  94. package/dist/tools/printer.js +482 -0
  95. package/dist/tools/programRunners.js +315 -0
  96. package/dist/tools/rag.js +153 -0
  97. package/dist/tools/registry.js +58 -0
  98. package/dist/tools/responses.js +36 -0
  99. package/dist/tools/schema.js +304 -0
  100. package/dist/tools/storage.js +740 -0
  101. package/dist/tools/streaming.js +132 -0
  102. package/dist/tools/types.js +99 -0
  103. package/dist/viceRunner.js +239 -0
  104. package/doc/MCP_SETUP.md +153 -0
  105. package/doc/README.md +7 -0
  106. package/doc/developer.md +312 -0
  107. package/doc/img/logo.png +0 -0
  108. package/doc/img/prompts/duck_petscii.png +0 -0
  109. package/doc/img/prompts/duck_song.png +0 -0
  110. package/doc/img/vscode/vscode-copilot-c64-chat-mode.png +0 -0
  111. package/doc/img/vscode/vscode-copilot-hello-world.png +0 -0
  112. package/doc/prompts/README.md +96 -0
  113. package/doc/prompts/user-prompt-ideas.md +147 -0
  114. package/doc/rest/c64-openapi.yaml +979 -0
  115. package/doc/rest/c64-rest-api.md +106 -0
  116. package/doc/troubleshooting-mcp.md +177 -0
  117. package/generated/c64/index.js +857 -0
  118. package/mcp.json +106 -0
  119. package/package.json +79 -0
  120. package/scripts/c64-cli.mjs +142 -0
  121. package/scripts/c64-tool.sh +83 -0
  122. package/scripts/check-package.mjs +89 -0
  123. package/scripts/cli.js +61 -0
  124. package/scripts/fetch-external-sources.mjs +67 -0
  125. package/scripts/generate-changelog.mjs +131 -0
  126. package/scripts/postbuild.mjs +101 -0
  127. package/scripts/prepare-release.mjs +58 -0
  128. package/scripts/rag-rebuild.mjs +53 -0
  129. package/scripts/register-ts-node.mjs +6 -0
  130. package/scripts/run-mcp-check.sh +104 -0
  131. package/scripts/run-tests.mjs +195 -0
  132. package/scripts/sid2wav.sh +94 -0
  133. package/scripts/smoke-run.sh +65 -0
  134. package/scripts/start-mock-c64.mjs +51 -0
  135. package/scripts/start-mock.mjs +31 -0
  136. package/scripts/start.mjs +72 -0
  137. package/scripts/update-readme.ts +164 -0
  138. package/scripts/verify-package.mjs +147 -0
@@ -0,0 +1,154 @@
1
+ # C64 BASIC API — Callable Routines & Token Dispatch (ROM $A000–$BFFF)
2
+
3
+ > Purpose: Minimal, deduplicated interface spec for **callable** BASIC API entry points and token dispatch.
4
+ > Format: Markdown table (one row per routine). Addresses shown in **hex** and **decimal**. **Bold** Name = callable/public API.
5
+ > Conventions: A=Accumulator, X,Y=Index registers, C=Carry; Token shown where a BASIC token invokes the routine; “—” if not applicable.
6
+
7
+ ## Use of Low Memory ($0000–$03FF)
8
+
9
+ BASIC uses the [low memory area](./memory/low-memory-map.md) for program management, variable tracking, and floating-point math, with temporary overlap of certain KERNAL variables. It shares this space with the Kernal.
10
+
11
+ | Range | Purpose | Key Variables / Pointers |
12
+ |:------|:---------|:-------------------------|
13
+ | `$002B–$0030` | Core pointers defining BASIC memory layout | TXTTAB, VARTAB, ARYTAB |
14
+ | `$0031–$0038` | Free-space and string pool boundaries | STREND, FREETOP, MEMSIZ |
15
+ | `$0039–$0042` | Current/previous line context and DATA pointer | CURLIN, OLDLIN, DATPTR |
16
+ | `$0061–$006E` | Floating point accumulators (FAC1/FAC2) | FP math workspace |
17
+ | `$0073–$007A` | Inline copy of CHRGET/CHRGOT tokenizer entry | BASIC text reader |
18
+ | `$0099–$009A` | Default I/O device numbers | DFLTN (input), DFLTO (output) |
19
+ | `$0200–$0258` | Input buffer for keyboard and direct statements | INBUF |
20
+ | `$0281–$0284` | BASIC start and end addresses after memory test | BASBOT, BASTOP |
21
+ | `$0300–$030B` | **BASIC RAM vectors** (redirectable entry points) | e.g. LIST, NEW, RUN |
22
+ | `$033C–$03FB` | Cassette buffer reused by BASIC LOAD/SAVE | TBUFFER |
23
+
24
+ BASIC and KERNAL share portions of this map; modifying these variables directly
25
+ allows the relocation of program text, redefining top-of-memory, or injecting custom LOAD/SAVE behavior.
26
+
27
+ ## Callable Routines
28
+
29
+ | Address | Decimal | Token | Name | Function | Args | Input | Output | Notes |
30
+ |:--------|:--------|:------|:-----|:---------|:-----|:------|:-------|:------|
31
+ | `$A000` | 40960 | — | **COLDV** | Cold start vector target (BASIC init) | — | Entry via OS after power-on | — | Prints banner/bytes free; see `$E394` |
32
+ | `$A002` | 40962 | — | **WARMV** | Warm start vector target (STOP/RESTORE) | — | Entry via BRK/STOP | — | Preserves program; see `$E37B` |
33
+ | `$A00C` | 40972 | — | **STMDSP** | Statement dispatch table (addr-1 entries) | — | NEWSTT preloads return to CHRGET | — | Maps tokens 128–162 to handlers |
34
+ | `$A052` | 41042 | — | **FUNDSP** | Function dispatch table | — | FRMEVAL evals arg; jumps by token | — | Maps tokens 180–202 to handlers |
35
+ | `$A080` | 41088 | — | **OPTAB** | Operator dispatch table (addr-1 + precedence) | — | Expression parsing | — | Maps tokens 170–179 |
36
+ | `$A19E` | 41374 | — | **ERRTAB** | BASIC error message texts | — | — | — | Indexed via vector table `$A328` |
37
+ | `$A328` | 41768 | — | **ERRVEC** | Error message vector table | — | X=err# (in ERROR) | — | 30 entries; BREAK msg in misc |
38
+ | `$A365` | 41829 | — | **MSGTBL** | Misc messages (OK, ERROR, READY., BREAK) | — | — | — | Zero-terminated strings |
39
+ | `$A38A` | 41866 | — | **FNDFOR** | Find FOR block on stack | — | Stack contains FOR frames | — | Used by NEXT/LOOP control |
40
+ | `$A3B8` | 41912 | — | **BLTU** | Open space for new line/variable | — | Requested insert size | — | Moves text/vars; checks space |
41
+ | `$A3FB` | 41979 | — | **GETSTK** | Check stack space | — | Needed bytes in A? | — | Errors on overflow |
42
+ | `$A408` | 41992 | — | **REASON** | Check free memory; GC if needed | — | Requested bytes | — | Calls GARBAG `$B526` then error |
43
+ | `$A435` | 42037 | — | **OMERR** | OUT OF MEMORY handler | X=err# | — | — | Falls through to ERROR |
44
+ | `$A437` | 42039 | — | **ERROR** | General error handler | X=err# | — | — | Vectored via RAM $0300 for trapping |
45
+ | `$A474` | 42089 | — | **READY** | Print READY and enter main loop | — | — | — | Sets KERNAL message flag |
46
+ | `$A480` | 42112 | — | **MAIN** | Main loop: read line, exec/store | — | Via RAM vector $0302 | — | Keyboard via CHRIN `$F157` |
47
+ | `$A49C` | 42140 | — | **MAIN1** | Add/replace program line | — | Line in input buffer | — | Tokenizes; updates links; CLR |
48
+ | `$A533` | 42291 | — | **LINKPRG** | Relink tokenized program lines | — | Program text | — | Rewrites next-line pointers |
49
+ | `$A560` | 42336 | — | **INLIN** | Read input line to buffer ($0200) | — | From current input (keyboard) | — | Max 80 chars; error if >80 |
50
+ | `$A579` | 42361 | — | **CRUNCH** | Tokenize input buffer line | — | Buffer @ $0200 | — | Vectored via RAM $0304 |
51
+ | `$A613` | 42515 | — | **FINDLN** | Find line number (target in $14/$15) | — | Program text | C=1 if found; $5F/$60=ptr | C=0 if not found |
52
+ | `$A642` | 42562 | 162 ($A2) | **NEW** | NEW: clear program to zero link | — | — | — | Falls through CLR |
53
+ | `$A65E` | 42590 | 156 ($9C) | **CLR** | CLR: close files; reset var areas | — | — | — | Calls KERNAL CLALL `$F32F` |
54
+ | `$A68E` | 42638 | — | **RUNC** | Reset text pointer to start | — | — | — | Sets TXTPTR $7A/$7B |
55
+ | `$A69C` | 42652 | 155 ($9B) | **LIST** | LIST: print lines in range | — | Start/end in $5F/$60 and $14/$15 | — | Uses QPLOP for tokens |
56
+ | `$A717` | 42775 | — | **QPLOP** | Convert tokens → ASCII for LIST | — | Vectored via RAM $0306 | — | Supports custom keywords |
57
+ | `$A742` | 42818 | 129 ($81) | **FOR** | Save FOR frame on stack | — | TO expr eval once; STEP default 1 | — | Non-array FP loop var |
58
+ | `$A7AE` | 42926 | — | **NEWSTT** | Prepare next statement execution | — | — | — | Tests STOP; advances pointers |
59
+ | `$A7E4` | 42980 | — | **GONE** | Fetch next token and execute | — | Vectored via RAM $0308 | — | PUSH (addr-1) from STMDSP; CHRGET |
60
+ | `$A81D` | 43037 | 140 ($8C) | **RESTORE** | RESET DATA pointer | — | — | — | DATA ptr $41/$42 ← start $2B/$2C |
61
+ | `$A82C` | 43052 | — | **STOPTST** | Test STOP key and branch | — | Calls KERNAL STOP `$F6ED` | — | |
62
+ | `$A82F` | 43055 | 144 ($90) | **STOP** | STOP: break program | — | STOP pressed | — | Prints BREAK then READY |
63
+ | `$A831` | 43057 | 128 ($80) | **END** | END: terminate program | — | — | — | Preserves CONT pointers |
64
+ | `$A857` | 43095 | 154 ($9A) | **CONT** | CONT: resume after STOP/END | — | Saved CONT pointers | — | Errors if missing |
65
+ | `$A871` | 43121 | 138 ($8A) | **RUN** | RUN: SETMSG, CLR, optional GOTO | — | Optional line# | — | SETMSG `$FE18` |
66
+ | `$A883` | 43139 | 141 ($8D) | **GOSUB** | Push return context; then GOTO | — | — | — | Marks stack frame (0x8D) |
67
+ | `$A8A0` | 43168 | 137 ($89) | **GOTO** | Branch to target line | — | Scans program for line | — | Adjusts pointers to target |
68
+ | `$A8D2` | 43218 | 142 ($8E) | **RETURN** | Pop GOSUB frame; resume | — | Stack contains frame | — | Restores line/char pointers |
69
+ | `$A8F8` | 43256 | 131 ($83) | **DATA** | Skip DATA text | — | — | — | Skips to next stmt (like REM) |
70
+ | `$A906` | 43270 | — | **DATAN** | Find end of current statement | — | — | — | Stops at 0 or colon (outside quotes) |
71
+ | `$A928` | 43304 | 139 ($8B) | **IF** | Eval expr; if nonzero do THEN-part | — | Uses FRMEVAL `$AD9E` | — | Else falls through to REM |
72
+ | `$A93B` | 43323 | 143 ($8F) | **REM** | Skip to next statement | — | — | — | Shares code path with IF |
73
+ | `$A94B` | 43339 | 145 ($91) | **ON** | ON GOTO/GOSUB list dispatch | — | Arg → integer; select target | — | No-op if out-of-range |
74
+ | `$A96B` | 43371 | — | **LINGET** | Parse decimal → 2-byte line# | — | Range 0–63999 | $14/$15=line# | |
75
+ | `$A9A5` | 43429 | 136 ($88) | **LET** | Evaluate/assign var (all types) | — | Var ref and expr | — | Type-checked assign/create |
76
+ | `$AA80` | 43648 | 152 ($98) | **PRINT#** | PRINT# (to device) | — | Calls CMD then CLRCHN `$F333` | — | |
77
+ | `$AA86` | 43654 | 157 ($9D) | **CMD** | Route output to device; keep open | — | CHKOUT `$F250`; PRINT text | — | Leaves channel open |
78
+ | `$AAA0` | 43680 | 153 ($99) | **PRINT** | PRINT with TAB/SPC/;/, variables | — | Converts to strings; CHROUT | — | Handles TI, TI$, ST |
79
+ | `$AB1E` | 43806 | — | **STROUT** | Print zero-terminated string at (A,Y) | A=lo,Y=hi | Points to string | — | |
80
+ | `$AB4D` | 43853 | — | **DOAGIN** | Input error formatting (GET/INPUT/READ) | — | — | — | |
81
+ | `$AB7B` | 43899 | 161 ($A1) | **GET** | GET/GET#: get single char | — | Optional CHKIN/CLRCHN | A=char | Uses READ common I/O |
82
+ | `$ABA5` | 43941 | 132 ($84) | **INPUT#** | INPUT# from device | — | CHKIN; then INPUT; CHKOUT | — | Discards excess silently |
83
+ | `$ABBF` | 43967 | 133 ($85) | **INPUT** | Prompt; read; assign to vars | — | Not allowed in direct mode | — | Uses READ common code |
84
+ | `$AC06` | 44038 | 135 ($87) | **READ** | Read from DATA; assign | — | DATA pointer | — | Shared I/O for GET/INPUT |
85
+ | `$ACFC` | 44284 | — | **EXIGNT** | Input error message texts | — | — | — | “?EXTRA IGNORED”, “?REDO FROM START” |
86
+ | `$AD1E` | 44318 | 130 ($82) | **NEXT** | Increment FOR var; loop test | — | FOR frame on stack | — | Removes frame when complete |
87
+ | `$AD8A` | 44426 | — | **FRMNUM** | Type check (string vs numeric) | — | Desired type | — | TYPE MISMATCH on mismatch |
88
+ | `$AD9E` | 44446 | — | **FRMEVAL** | Evaluate expression | — | Text pointer at term start | Result→FAC1/desc | Sets flags at $0D/$0E |
89
+ | `$AE83` | 44675 | — | **EVAL** | Parse single numeric term → FAC1 | — | Constant/variable/PI | FAC1 | Vectored via RAM $030A |
90
+ | `$AEF1` | 44785 | — | **PARCHK** | Evaluate expression in parentheses | — | — | — | Calls FRMEVAL |
91
+ | `$AEFF` | 44799 | — | **SYNCHR** | Syntax helper: expect & skip char | A=expected | — | — | For comma/parens checks |
92
+ | `$AF08` | 44808 | — | **SNERR** | Print SYNTAX ERROR | — | — | — | |
93
+ | `$AF2B` | 44843 | — | **ISVAR** | Fetch variable value | — | — | Result in FAC1/desc | |
94
+ | `$AFA7` | 44967 | — | **ISFUN** | Dispatch and evaluate function | — | Token in current text | — | Uses FUNDSP |
95
+ | `$AFE6` | 45030 | 176 ($B0) | **OR** | Logical OR | — | Operands coerced to 16-bit ints | FAC1=0/−1 | Shares code with AND |
96
+ | `$AFE9` | 45033 | 175 ($AF) | **AND** | Logical AND | — | Operands coerced to 16-bit ints | FAC1=0/−1 | — |
97
+ | `$B016` | 45078 | 177 ($B1) | **REL** | Comparisons (>,<,=) | — | Strings or floats | FAC1=0/−1 | Used by tokens 177/178/179 |
98
+ | `$B081` | 45185 | 134 ($86) | **DIM** | Create array(s) | — | Variables list | — | Default size 11 elements if missing |
99
+ | `$B08B` | 45195 | — | **PTRGET** | Find/create variable descriptor | — | Name in text | Ptr→$47/$48 | |
100
+ | `$B113` | 45331 | — | **ISALPHA** | Test A for alphabetic | A=char | — | Z/N set | Part of var-name check |
101
+ | `$B11D` | 45341 | — | **NOTFNS** | Create new variable descriptor | — | — | — | Moves storage up 7 bytes |
102
+ | `$B185` | 45445 | — | **FINPTR** | Return address of variable | — | — | Ptr→$47/$48 | |
103
+ | `$B194` | 45460 | — | **ARYGET** | Allocate array descriptor | — | Dims parsed | — | 5 + 2*ndim bytes |
104
+ | `$B1AA` | 45482 | — | **FTOI_AY** | Float→signed int in A(high)/Y(low) | — | FAC1 | A,Y=int | Also writes $64/$65 |
105
+ | `$B1B2` | 45490 | — | **INTIDX** | Subscript to positive integer | — | FAC1 | — | Range check |
106
+ | `$B1BF` | 45503 | — | **AYINT** | Float→signed int @ $64/$65 | — | FAC1 in range | — | Errors if out of range |
107
+ | `$B245` | 45637 | — | **BSERR** | BAD SUBSCRIPT error | — | — | — | |
108
+ | `$B248` | 45640 | — | **FCERR** | ILLEGAL QUANTITY error | — | — | — | |
109
+ | `$B34C` | 45900 | — | **UMULT** | Size of multidimensional array | — | Dims | — | Multiply dims |
110
+ | `$B37D` | 45949 | 184 ($B8) | **FRE** | Return free memory (fix sign) | — | Calls GARBAG `$B526` | FAC1 | Use `FRE(0)-65536*(FRE(0)<0)` |
111
+ | `$B391` | 45969 | — | **GIVAYF** | 16-bit signed int (A,Y) → float | A,Y=int | — | FAC1 | RAM vector at 5–6 |
112
+ | `$B39E` | 45982 | 185 ($B9) | **POS** | Cursor position (logical) | — | Calls PLOT `$E50A` | FAC1 | Equivalent to PEEK(211) |
113
+ | `$B3B3` | 46003 | 150 ($96) | **DEF** | Define FN | — | Syntax checks; push frame | — | Nested functions allowed |
114
+ | `$B3E1` | 46049 | — | **GETFNM** | Check DEF/FN syntax | — | — | — | Finds/creates dep. var |
115
+ | `$B3F4` | 46068 | — | **FNDOER** | Evaluate FN(...) | — | — | FAC1 | Uses definition text |
116
+ | `$B465` | 46181 | 196 ($C4) | **STR$** | Number → string | — | Number required | String desc | |
117
+ | `$B487` | 46215 | — | **STRLIT** | Scan string literal; set pointers | — | — | — | Allocates/copies as needed |
118
+ | `$B4F4` | 46324 | — | **GETSPA** | Allocate string space (may GC) | — | Length in A? | — | Calls GARBAG on shortage |
119
+ | `$B526` | 46374 | — | **GARBAG** | String garbage collection | — | — | — | Compacts live strings; moves bottom ptr |
120
+ | `$B63D` | 46653 | — | **CAT** | Concatenate strings | — | A$+B$ | New string desc | Allocates & builds |
121
+ | `$B67A` | 46714 | — | **MOVINS** | Move string text | — | — | — | Utility |
122
+ | `$B6A3` | 46755 | — | **FRESTR** | Discard temporary string | — | — | — | Updates bottom if topmost |
123
+ | `$B6DB` | 46811 | — | **FRETMS** | Remove entry from temp string stack | — | — | — | |
124
+ | `$B6EC` | 46828 | 199 ($C7) | **CHR$** | Create one-byte string | — | Byte value | String desc | |
125
+ | `$B700` | 46848 | 200 ($C8) | **LEFT$** | Left substring | — | String, count | String desc | |
126
+ | `$B72C` | 46892 | 201 ($C9) | **RIGHT$** | Right substring | — | String, count | String desc | |
127
+ | `$B737` | 46903 | 202 ($CA) | **MID$** | Mid substring | — | String, pos[,len] | String desc | |
128
+ | `$B761` | 46945 | — | **PREAM** | Pull params for LEFT$/RIGHT$/MID$ | — | — | — | Helper |
129
+ | `$B77C` | 46972 | 195 ($C3) | **LEN** | String length → number | — | String desc | FAC1 | |
130
+ | `$B78B` | 46987 | 198 ($C6) | **ASC** | First char code → number | — | String | FAC1 | |
131
+ | `$B79B` | 47003 | — | **GETBYTC** | Read byte parameter (0–255) | — | — | X=byte | For USR/new commands |
132
+ | `$B7AD` | 47021 | 197 ($C5) | **VAL** | Parse number in string | — | String | FAC1 | |
133
+ | `$B7EB` | 47083 | — | **GETNUM** | Get 16-bit address and 8-bit byte | — | Parses next numeric; range-checks | $14/$15=addr, X=byte | For POKE/WAIT |
134
+ | `$B7F7` | 47095 | — | **GETADR** | Float→unsigned 16-bit address | — | FAC1 in range | $14/$15 | For PEEK |
135
+ | `$B80D` | 47117 | 194 ($C2) | **PEEK** | Read byte at address | — | Addr via GETADR | FAC1 | Reads via Y then float |
136
+ | `$B824` | 47140 | 151 ($97) | **POKE** | Write byte to address | — | GETNUM (addr,byte) | — | |
137
+ | `$B82D` | 47149 | 146 ($92) | **WAIT** | Wait on (addr XOR pat) AND mask | — | Addr, mask[,pattern] | — | Poll loop until nonzero |
138
+ | `$BC39` | 48185 | 180 ($B4) | **SGN** | Sign of number | — | FAC1 | FAC1 | |
139
+ | `$BC58` | 48216 | 182 ($B6) | **ABS** | Absolute value | — | FAC1 | FAC1 | |
140
+ | `$BCCC` | 48332 | 181 ($B5) | **INT** | Truncate to integer | — | FAC1 | FAC1 | |
141
+ | `$BDDD` | 48605 | — | **FOUT** | Float → ASCII string | — | FAC1 | A,Y→string | Used by PRINT/STR$ |
142
+ | `$BF71` | 49009 | 186 ($BA) | **SQR** | Square root | — | FAC1 | FAC1 | |
143
+ | `$BF7B` | 49019 | 174 ($AE) | **^** | Exponentiation | — | FAC2^FAC1 | FAC1 | |
144
+ | `$BFB4` | 49076 | 168 ($A8) | **NOT / >** | Logical NOT (also helper for >) | — | FAC1 | FAC1 | NOT X = -(X+1) |
145
+ | `$BFED` | 49133 | 189 ($BD) | **EXP** | e^X | — | FAC1 | FAC1 | Jumps into KERNAL continuation |
146
+ | `$B86A` | 47210 | 170 ($AA) | **+** | Addition | — | FAC1,FAC2 | FAC1 | |
147
+ | `$B853` | 47187 | 171 ($AB) | **-** | Subtraction | — | FAC1,FAC2 | FAC1 | |
148
+ | `$BA2B` | 47659 | 172 ($AC) | **\*** | Multiplication | — | FAC1,FAC2 | FAC1 | |
149
+ | `$BB12` | 47890 | 173 ($AD) | **/** | Division | — | FAC2/FAC1 | FAC1 | |
150
+ | `$AFE6` | 45030 | 176 ($B0) | **AND** | Logical AND | — | ints | FAC1 | duplicate entry for clarity |
151
+ | `$AFE9` | 45033 | 175 ($AF) | **OR** | Logical OR | — | ints | FAC1 | duplicate entry for clarity |
152
+ | `$AED4` | 44756 | 178 ($B2) | **=** | Equal comparison | — | — | FAC1=0/−1 | Uses REL |
153
+ | `$BFB4` | 49076 | 177 ($B1) | **>** | Greater than comparison | — | — | FAC1=0/−1 | Uses REL/NEGOP |
154
+ | `$B016` | 45078 | 179 ($B3) | **<** | Less than comparison | — | — | FAC1=0/−1 | Uses REL |
@@ -0,0 +1,68 @@
1
+ # C64 KERNAL API — Callable Routines (ROM $E000–$FFFF)
2
+
3
+ > Purpose: Minimal, deduplicated interface spec for **callable** KERNAL API routines.
4
+ > Format: Markdown table (one row per routine). Addresses shown in **hex** and **decimal** for BASIC friendliness.
5
+ > Conventions: A=Accumulator, X,Y=Index registers, C=Carry; “—” means not applicable. **Bold** Name = callable/public API.
6
+
7
+ ## Use of Low Memory ($0000–$03FF)
8
+
9
+ The KERNAL relies on the [low memory region](./memory/low-memory-map.md) for all I/O and interrupt control. It shares this space with BASIC.
10
+
11
+ | Range | Purpose | Key Variables / Vectors |
12
+ |:------|:---------|:------------------------|
13
+ | `$0000–$0001` | 6510 I/O registers controlling memory banking and cassette motor | D6510, R6510 |
14
+ | `$0090` | I/O **status byte** updated by KERNAL routines | STATUS |
15
+ | `$009B–$00A2` | System clock and **TIME** variables for RDTIM/SETTIM | TI, TI$ |
16
+ | `$00B8–$00BC` | File/device association (LFN, SA, FA) | LA, SA, FA, FNADR |
17
+ | `$00C6` | Keyboard buffer count | NDX |
18
+ | `$00F3–$00F6` | Vectors to color RAM and keyboard decode tables | USER, KEYTAB |
19
+ | `$0314–$0333` | **Interrupt and I/O vectors** (modifiable by user) | CINV, CBINV, IRQ, BRK, NMI, etc. |
20
+ | `$033C–$03FB` | **Cassette buffer** (192 bytes, reusable for ML code if no tape is used) | TBUFFER |
21
+ | `$03FC–$03FF` | Unused bytes (safe for temporary data) | — |
22
+
23
+ The KERNAL modifies `$0001` to switch ROM/RAM banks, writes device status to `$0090`,
24
+ and maintains IRQ/NMI linkage through `$0314–$0318`. Avoid overwriting these unless intercepting system vectors.
25
+
26
+ ## Callable Routines
27
+
28
+ | Address | Decimal | Name | Function | Args | Input | Output | Notes |
29
+ |:--------|:--------|:-----|:---------|:-----|:------|:-------|:------|
30
+ | `$FF81` | 65409 | **CINT** | Init screen editor; clear/home; cursor state | — | Call after IOINIT/RAMTAS/RESTOR | — | Sets screen/keyboard defaults |
31
+ | `$FF84` | 65412 | **IOINIT** | Init CIAs; SID volume off; set IRQ @ 60 Hz | — | Power‑up or RESTORE flow | — | Sets CIA1 Timer A, bus lines |
32
+ | `$FF87` | 65415 | **RAMTAS** | RAM test; set MEMBOT/MEMTOP; zp/pages clear | — | Power‑up | MEMBOT=`$0281/2`, MEMTOP=`$0283/4` | Sets screen base via `$0288` |
33
+ | `$FF8A` | 65418 | **RESTOR** | Restore default RAM vectors | — | — | Vectors at `$0314–$0333` reset | Uses ROM table at `$FD30` |
34
+ | `$FF8D` | 65421 | **VECTOR** | Read/Write RAM vector table | C=1 read; C=0 write; X/Y=table addr | SEI recommended | Vectors copied to/from (X,Y) | Affects IRQ/NMI; use SEI/CLI |
35
+ | `$FF90` | 65424 | **SETMSG** | Enable/disable control & error messages | A: bit6=control, bit7=errors | — | — | Does not suppress cassette prompts |
36
+ | `$FF93` | 65427 | **SECOND** | Send secondary addr after LISTEN (serial) | A=sec addr | Device is LISTENing | — | For serial LISTEN path |
37
+ | `$FF96` | 65430 | **TKSA** | Send secondary addr after TALK (serial) | A=sec addr | Device is TALKing | — | For serial TALK path |
38
+ | `$FF99` | 65433 | **MEMTOP** | Get/Set top of BASIC RAM pointer | C=1 get; C=0 set; (get→X/Y; set: X/Y=in) | — | On get: X=lo,Y=hi | Pointer at `$0283/4` |
39
+ | `$FF9C` | 65436 | **MEMBOT** | Get/Set bottom of BASIC RAM pointer | C=1 get; C=0 set; (get→X/Y; set: X/Y=in) | — | On get: X=lo,Y=hi | Pointer at `$0281/2` |
40
+ | `$FF9F` | 65439 | **SCNKEY** | Scan keyboard; buffer PETSCII | — | IRQ normally handles; call if IRQ off | — | Keycode→`$CB`; buffer at `$0277` |
41
+ | `$FFA2` | 65442 | **SETTMO** | Set IEEE/serial timeout flag | A bit7=0 enable; bit7=1 disable | — | — | Rarely used |
42
+ | `$FFA5` | 65445 | **ACPTR** | Read byte from current TALKer (serial) | — | Device TALK/TKSA set | A=byte | Use READST for status |
43
+ | `$FFA8` | 65448 | **CIOUT** | Send byte to current LISTENer (serial) | A=byte | Device LISTEN/SECOND set | — | Buffers until next/UNLSN |
44
+ | `$FFAB` | 65451 | **UNTLK** | Send UNTALK on serial bus | — | — | — | Ends TALK state |
45
+ | `$FFAE` | 65454 | **UNLSN** | Send UNLISTEN on serial bus | — | — | — | Ends LISTEN state |
46
+ | `$FFB1` | 65457 | **LISTEN** | Send LISTEN+dev | A=device# | — | — | — |
47
+ | `$FFB4` | 65460 | **TALK** | Send TALK+dev | A=device# | — | — | — |
48
+ | `$FFB7` | 65463 | **READST** | Read & clear I/O status | — | — | A=status | RS‑232 clears its own |
49
+ | `$FFBA` | 65466 | **SETLFS** | Set logical file#, device#, secondary | A=file#, X=device#, Y=secondary or `$FF` | — | — | Required before OPEN/LOAD/SAVE |
50
+ | `$FFBD` | 65469 | **SETNAM** | Set filename pointer & length | A=len, X=lo, Y=hi | — | — | Points to PETSCII name |
51
+ | `$FFC0` | 65472 | **OPEN** | Open channel to device | — | After SETLFS/SETNAM | C=1 on error | Entry uses vector `$031A` |
52
+ | `$FFC3` | 65475 | **CLOSE** | Close logical file | A=file# | File was OPENed | — | Frees resources; sends UNLSN |
53
+ | `$FFC6` | 65478 | **CHKIN** | Select input channel (logical file) | X=file# | After OPEN | — | For CHRIN/GETIN |
54
+ | `$FFC9` | 65481 | **CHKOUT** | Select output channel (logical file) | X=file# | After OPEN | — | For CHROUT |
55
+ | `$FFCC` | 65484 | **CLRCHN** | Restore default I/O devices | — | — | — | Keyboard/screen; sends UNTALK/UNLSN if needed |
56
+ | `$FFCF` | 65487 | **CHRIN** | Read byte from current input device | — | After CHKIN (or default keyboard) | A=byte | Keyboard path echoes & line-buffers |
57
+ | `$FFD2` | 65490 | **CHROUT** | Write byte to current output device | A=byte | After CHKOUT (or default screen) | — | Screen path handles control codes |
58
+ | `$FFD5` | 65493 | **LOAD** | Load/Verify to RAM | A=0 load, A=1 verify; X/Y=start | SETLFS/SETNAM done | X/Y=end addr loaded | SA=1 uses header address |
59
+ | `$FFD8` | 65496 | **SAVE** | Save RAM to device | A=zp ptr offset; X/Y=end addr | SETLFS/SETNAM done; ZP ptr→start | — | Cassette buffers; disk writes file |
60
+ | `$FFDB` | 65499 | **SETTIM** | Set software clock | A=lo, X=mid, Y=hi | — | Clock `$00A0–$00A2` set | Disables IRQ during set |
61
+ | `$FFDE` | 65502 | **RDTIM** | Read software clock | — | — | A=lo, X=mid, Y=hi | From `$00A0–$00A2` |
62
+ | `$FFE1` | 65505 | **STOP** | Test STOP key | — | UDTIM updates key state | Z=1 if STOP pressed | Also clears I/O channels on stop |
63
+ | `$FFE4` | 65508 | **GETIN** | Get next char (unbuffered if possible) | — | Device selected; keyboard uses buffer | A=byte | Via vector `$032A` |
64
+ | `$FFE7` | 65511 | **CLALL** | Close all files | — | — | — | Resets open-file index; restores I/O |
65
+ | `$FFEA` | 65514 | **UDTIM** | Jiffy clock tick; STOP key scan | — | IRQ calls every 1/60 s | `$00A0–$00A2`++ | Part of standard IRQ |
66
+ | `$FFED` | 65517 | **SCREEN** | Return screen size | — | — | X=cols(40), Y=rows(25) | For cross‑platform compatibility |
67
+ | `$FFF0` | 65520 | **PLOT** | Read/Set cursor | C=1 read; C=0 set; Y=row, X=col | — | On read: X=col, Y=row | Uses PNTR `$00D3`/TBLX `$00D6` |
68
+ | `$FFF3` | 65523 | **IOBASE** | Return I/O base address | — | — | X=lo, Y=hi | Present value `$DC00` (CIA1) |
@@ -0,0 +1,196 @@
1
+ # Language
2
+
3
+ ## Overview
4
+
5
+ MOS 6510 (C64 CPU) is a NMOS 6502 core with an 8‑bit data path and a 16‑bit address space (64 KiB). Registers: `A`, `X`, `Y` (8‑bit), `PC` (16‑bit), `SP` (8‑bit, stack at `$0100..$01FF`), `P` status `[N V - B D I Z C]`. The stack grows downward; pushes write to `$0100+SP` then decrement `SP`.
6
+
7
+ ## Lexical Conventions
8
+
9
+ - **Numbers:** decimal `42`, hexadecimal `$2A`, binary `%00101010`. Immediate prefix `#`.
10
+ - **Identifiers:** `[A–Z_][A–Z0–9_]*` (case‑insensitive).
11
+ - **Labels:** an identifier followed by `:` defines the current address. Reference with the bare identifier (optionally signed offsets in expressions).
12
+ - **Comments:** `;` to end of line. Whitespace is insignificant except as separator.
13
+ - **Expressions:** integers with `+ - * / & | ^ ~ << >>` and parentheses (assembler‑dependent), may refer to labels and the current location counter `*`.
14
+
15
+ ## Addressing Modes (names and syntax)
16
+
17
+ | Abbrev | Name | Operand Syntax | Example |
18
+ |---|---|---|---|
19
+ | `A` | Accumulator | `A` or omitted | `ASL A` / `ASL` |
20
+ | `#` | Immediate | `#byte` | `LDA #$0F` |
21
+ | `ZP` | Zeropage | `$LL` | `LDA $00` |
22
+ | `ZPX` | Zeropage,X | `$LL,X` | `LDA $10,X` |
23
+ | `ZPY` | Zeropage,Y | `$LL,Y` | `LDX $10,Y` |
24
+ | `ABS` | Absolute | `$HHLL` | `LDA $C000` |
25
+ | `ABSX` | Absolute,X | `$HHLL,X` | `LDA $D000,X` |
26
+ | `ABSY` | Absolute,Y | `$HHLL,Y` | `LDA $D000,Y` |
27
+ | `IND` | Indirect | `($HHLL)` | `JMP ($FFFC)` |
28
+ | `IZX` | (ZP,X) | `($LL,X)` | `LDA ($40,X)` |
29
+ | `IZY` | (ZP),Y | `($LL),Y` | `LDA ($40),Y` |
30
+ | `REL` | Relative | label | `BEQ done` |
31
+ | `IMP` | Implied | — | `CLC` |
32
+
33
+ *6510 `JMP (addr)` has the classic page‑wrap bug at `$xxFF → $xx00`.*
34
+
35
+ ## Labels and Symbol Rules
36
+
37
+ - **Definition:** `label:` binds `label` to the current address (`*`). Multiple labels may precede an instruction.
38
+ - **Use:** Any place an absolute/relative/address expression is expected (`LDA label`, `BEQ loop`).
39
+ - **Forward refs:** allowed; assembler resolves in a second pass.
40
+ - **Constants:** `name = expr` or `.equ name, expr` (assembler‑specific). Location counter may be set with `.org expr`. Data directives: `.byte`, `.word`, `.fill`, `.align` (names may vary).
41
+
42
+ ## Instruction Semantics (brief)
43
+
44
+ - **Load/Store:** `LDA/LDX/LDY` load; `STA/STX/STY` store. Affect `N,Z` (stores don’t).
45
+ - **ALU:** `ADC/SBC` (use `CLC`/`SEC` before add/sub), `AND/ORA/EOR`, `INC/DEC` (memory), `INX/INY/DEX/DEY` (registers). Flags as per 6502.
46
+ - **Shifts/Rotates:** `ASL/LSR/ROL/ROR` on `A` or memory. Shifted‑out bit enters `C`.
47
+ - **Compare:** `CMP/CPX/CPY` set `C` for ≥, `Z` for =, `N` for sign of result (no writeback).
48
+ - **Branch:** `BPL/BMI/BVC/BVS/BCC/BCS/BNE/BEQ` use signed 8‑bit `REL` offset (−128..+127 from `PC+2`). Taken branch +1 cycle; +1 more if page crosses.
49
+ - **Jumps/Calls:** `JMP` `ABS|IND`, `JSR ABS`, `RTS`, `BRK`/`RTI`.
50
+ - **Stack/Flags/Xfer:** `PHA/PLA`, `PHP/PLP`, `TAX/TAY/TXA/TYA`, `TSX/TXS`, `CLC/SEC/CLI/SEI/CLD/SED/CLV`, `NOP`.
51
+ - **Decimal mode:** C64 typically keeps `D=0` (`CLD`). Use BCD only if intentional.
52
+
53
+ ## EBNF (assembly source)
54
+
55
+ ```
56
+ program = { line } ;
57
+ line = [ label ] , [ instruction | directive ] , [ comment ] , EOL ;
58
+ label = identifier , ":" ;
59
+ instruction = mnemonic , [ operand ] ;
60
+ directive = "." , ident , [ operandlist ] ;
61
+
62
+ operand = expr
63
+ | "#" , byte
64
+ | "(" , expr , ")" (* IND for JMP *)
65
+ | "(" , zp , "," , "X" , ")" (* IZX *)
66
+ | "(" , zp , ")" , "," , "Y" (* IZY *)
67
+ | expr , "," , "X" (* ABSX or ZPX *)
68
+ | expr , "," , "Y" (* ABSY or ZPY *)
69
+ ;
70
+ operandlist = expr , { "," , expr } ;
71
+
72
+ expr = term , { ("+"|"-"|"|"|"^") , term } ;
73
+ term = factor , { ("*"|"/"|"&") , factor } ;
74
+ factor = ["+"|"-"|"~"] , primary ;
75
+ primary = number | identifier | "(" , expr , ")" | "*" ;
76
+
77
+ mnemonic = "ADC"|"AND"|"ASL"|"BCC"|"BCS"|"BEQ"|"BIT"|"BMI"|"BNE"|"BPL"|
78
+ "BRK"|"BVC"|"BVS"|"CLC"|"CLD"|"CLI"|"CLV"|"CMP"|"CPX"|"CPY"|
79
+ "DEC"|"DEX"|"DEY"|"EOR"|"INC"|"INX"|"INY"|"JMP"|"JSR"|"LDA"|
80
+ "LDX"|"LDY"|"LSR"|"NOP"|"ORA"|"PHA"|"PHP"|"PLA"|"PLP"|"ROL"|
81
+ "ROR"|"RTI"|"RTS"|"SBC"|"SEC"|"SED"|"SEI"|"STA"|"STX"|"STY"|
82
+ "TAX"|"TAY"|"TSX"|"TXA"|"TXS"|"TYA" ;
83
+
84
+ identifier = ( "A".."Z" | "_" ) , { "A".."Z" | "0".."9" | "_" } ;
85
+ number = dec | "$" hex | "%" bin ;
86
+ dec = "0".."9" , { "0".."9" } ;
87
+ hex = hexDigit , { hexDigit } ;
88
+ bin = "0"|"1" , { "0"|"1" } ;
89
+ hexDigit = "0".."9" | "A".."F" ;
90
+ byte = number ; (* assembler validates range *)
91
+ zp = number ; (* assembler may restrict to 0..255 for ZP *)
92
+ comment = ";" , { any-not-EOL } ;
93
+ EOL = "\n" ;
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Tokenization
99
+
100
+ ### Addressing‑Mode Profiles (bytes/cycles)
101
+
102
+ Unless noted, page cross on `ABSX/ABSY/IZY` adds **+1** cycle.
103
+
104
+ | Group | Mode → bytes/cycles | Notes |
105
+ |---|---|---|
106
+ | **ALU (ADC,AND,CMP,EOR,LDA,ORA,SBC)** | `#` 2/2 · `ZP` 2/3 · `ZPX` 2/4 · `ABS` 3/4 · `ABSX` 3/4+ · `ABSY` 3/4+ · `IZX` 2/6 · `IZY` 2/5+ | CMP shares counts; LDA as listed. |
107
+ | **STA** | `ZP` 2/3 · `ZPX` 2/4 · `ABS` 3/4 · `ABSX` 3/5 · `ABSY` 3/5 · `IZX` 2/6 · `IZY` 2/6 | Store does **not** set `N,Z`. |
108
+ | **STX** | `ZP` 2/3 · `ZPY` 2/4 · `ABS` 3/4 | |
109
+ | **STY** | `ZP` 2/3 · `ZPX` 2/4 · `ABS` 3/4 | |
110
+ | **LDX** | `#` 2/2 · `ZP` 2/3 · `ZPY` 2/4 · `ABS` 3/4 · `ABSY` 3/4+ | |
111
+ | **LDY** | `#` 2/2 · `ZP` 2/3 · `ZPX` 2/4 · `ABS` 3/4 · `ABSX` 3/4+ | |
112
+ | **INC/DEC (mem)** | `ZP` 2/5 · `ZPX` 2/6 · `ABS` 3/6 · `ABSX` 3/7 | |
113
+ | **INX/INY/DEX/DEY** | `IMP` 1/2 | |
114
+ | **ASL/LSR/ROL/ROR** | `A` 1/2 · `ZP` 2/5 · `ZPX` 2/6 · `ABS` 3/6 · `ABSX` 3/7 | |
115
+ | **BIT** | `ZP` 2/3 · `ABS` 3/4 | On 6510 only ZP/ABS. |
116
+ | **JMP** | `ABS` 3/3 · `IND` 3/5 | Buggy wrap on `IND`. |
117
+ | **JSR/RTS/RTI/BRK** | `JSR ABS` 3/6 · `RTS` 1/6 · `RTI` 1/6 · `BRK` 1/7 | |
118
+ | **Branches (Bcc)** | `REL` 2/2(+1 taken, +1 xpage) | |
119
+ | **Flag ops** | `IMP` 1/2 | `CLC,CLD,CLI,CLV,SEC,SED,SEI` |
120
+ | **Stack ops** | `PHA,PHP` 1/3 · `PLA,PLP` 1/4 | |
121
+ | **Transfers** | `TAX,TAY,TXA,TYA,TSX,TXS` 1/2 | |
122
+ | **NOP** | `IMP` 1/2 | Documented form only. |
123
+
124
+ ### Opcode × Addressing‑Mode Matrix (✓ = supported on 6510)
125
+
126
+ Modes (columns): `A # ZP ZPX ZPY ABS ABSX ABSY IND IZX IZY REL IMP`
127
+
128
+ | Mnemonic |A|#|ZP|ZPX|ZPY|ABS|ABSX|ABSY|IND|IZX|IZY|REL|IMP|
129
+ |---|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
130
+ | ADC | |✓|✓|✓| |✓|✓|✓| |✓|✓| | |
131
+ | AND | |✓|✓|✓| |✓|✓|✓| |✓|✓| | |
132
+ | ASL |✓| |✓|✓| |✓|✓| | | | | | |
133
+ | BCC | | | | | | | | | | | |✓| |
134
+ | BCS | | | | | | | | | | | |✓| |
135
+ | BEQ | | | | | | | | | | | |✓| |
136
+ | BIT | | |✓| | |✓| | | | | | | |
137
+ | BMI | | | | | | | | | | | |✓| |
138
+ | BNE | | | | | | | | | | | |✓| |
139
+ | BPL | | | | | | | | | | | |✓| |
140
+ | BRK | | | | | | | | | | | | |✓|
141
+ | BVC | | | | | | | | | | | |✓| |
142
+ | BVS | | | | | | | | | | | |✓| |
143
+ | CLC | | | | | | | | | | | | |✓|
144
+ | CLD | | | | | | | | | | | | |✓|
145
+ | CLI | | | | | | | | | | | | |✓|
146
+ | CLV | | | | | | | | | | | | |✓|
147
+ | CMP | |✓|✓|✓| |✓|✓|✓| |✓|✓| | |
148
+ | CPX | |✓|✓| | |✓| | | | | | | |
149
+ | CPY | |✓|✓| | |✓| | | | | | | |
150
+ | DEC | | |✓|✓| |✓|✓| | | | | | |
151
+ | DEX | | | | | | | | | | | | |✓|
152
+ | DEY | | | | | | | | | | | | |✓|
153
+ | EOR | |✓|✓|✓| |✓|✓|✓| |✓|✓| | |
154
+ | INC | | |✓|✓| |✓|✓| | | | | | |
155
+ | INX | | | | | | | | | | | | |✓|
156
+ | INY | | | | | | | | | | | | |✓|
157
+ | JMP | | | | | |✓| |✓|✓| | | | |
158
+ | JSR | | | | | |✓| | | | | | | |
159
+ | LDA | |✓|✓|✓| |✓|✓|✓| |✓|✓| | |
160
+ | LDX | |✓|✓| |✓|✓| |✓| | | | | |
161
+ | LDY | |✓|✓|✓| |✓|✓| | | | | | |
162
+ | LSR |✓| |✓|✓| |✓|✓| | | | | | |
163
+ | NOP | | | | | | | | | | | | |✓|
164
+ | ORA | |✓|✓|✓| |✓|✓|✓| |✓|✓| | |
165
+ | PHA | | | | | | | | | | | | |✓|
166
+ | PHP | | | | | | | | | | | | |✓|
167
+ | PLA | | | | | | | | | | | | |✓|
168
+ | PLP | | | | | | | | | | | | |✓|
169
+ | ROL |✓| |✓|✓| |✓|✓| | | | | | |
170
+ | ROR |✓| |✓|✓| |✓|✓| | | | | | |
171
+ | RTI | | | | | | | | | | | | |✓|
172
+ | RTS | | | | | | | | | | | | |✓|
173
+ | SBC | |✓|✓|✓| |✓|✓|✓| |✓|✓| | |
174
+ | SEC | | | | | | | | | | | | |✓|
175
+ | SED | | | | | | | | | | | | |✓|
176
+ | SEI | | | | | | | | | | | | |✓|
177
+ | STA | | |✓|✓| |✓|✓|✓| |✓|✓| | |
178
+ | STX | | |✓| |✓|✓| | | | | | | |
179
+ | STY | | |✓|✓| |✓| | | | | | | |
180
+ | TAX | | | | | | | | | | | | |✓|
181
+ | TAY | | | | | | | | | | | | |✓|
182
+ | TSX | | | | | | | | | | | | |✓|
183
+ | TXA | | | | | | | | | | | | |✓|
184
+ | TXS | | | | | | | | | | | | |✓|
185
+ | TYA | | | | | | | | | | | | |✓|
186
+
187
+ ### Vector Entry Points (C64)
188
+
189
+ - NMI `$FFFA/B`, RESET `$FFFC/D`, IRQ/BRK `$FFFE/F`. After RESET, execution starts at vector `$FFFC` (KERNAL init).
190
+
191
+ ### Minimal Directives (common)
192
+
193
+ - Location: `.org addr` / `* = addr`
194
+ - Data: `.byte expr[,…]`, `.word expr[,…]`
195
+ - Symbols: `name = expr` / `.equ name, expr`
196
+ - Alignment/fill (assembler‑specific): `.align n`, `.fill count, value`
@@ -0,0 +1,5 @@
1
+ *= $0801
2
+ LDA #$00
3
+ LOOP STA $D020
4
+ ADC #$01
5
+ JMP LOOP
@@ -0,0 +1,15 @@
1
+ *= $0801
2
+ SEI
3
+ LDA #$01
4
+ STA $D015 ; enable sprite 0
5
+ LDA #$00
6
+ STA $D001 ; sprite 0 Y
7
+ LDA #$18
8
+ STA $D000 ; sprite 0 X
9
+ LOOP INC $D000 ; move right
10
+ LDA $D000
11
+ CMP #$F0
12
+ BNE LOOP
13
+ LDA #$18
14
+ STA $D000
15
+ JMP LOOP
@@ -0,0 +1,8 @@
1
+ # SID File Structure Reference (for RAG)
2
+
3
+ See `data/audio/sid-file-structure.md` for the complete explanation of PSID/RSID headers, fields, and playback behavior. This stub ensures the RAG index includes this topic and can surface it when generating or playing songs.
4
+
5
+ - Magic: `PSID` or `RSID`
6
+ - Key fields: version, dataOffset, loadAddress, initAddress, playAddress, songs, startSong, speed
7
+ - v2–v4 extras: flags (PAL/NTSC, 6581/8580), startPage/pageLength, second/third SID base
8
+ - Song length lookup: HVSC `Songlengths.md5` by MD5 of full file
@@ -0,0 +1,11 @@
1
+ # SID Quick Reference (for RAG)
2
+
3
+ See `data/audio/sid-spec.md` for the full guide. This stub ensures the RAG index includes SID guidance.
4
+
5
+ - Base address: $D400
6
+ - Voice control: $D404/$D40B/$D412 (bits: NOISE PULSE SAW TRI TEST RING SYNC GATE)
7
+ - Play note: set FREQ LO/HI, PW LO/HI, AD, SR; set CTRL with waveform|GATE
8
+ - Stop note: clear GATE bit in CTRL
9
+ - Volume/filter: $D418 (bits 0..3 volume, 4..6 LP/BP/HP)
10
+ - Filter cutoff: $D415/$D416 (11 bits)
11
+ - Useful effects: arpeggio (rewrite FREQ), vibrato (modulate FREQ), PWM (modulate PW), filter sweeps ($D415/$D416)
@@ -0,0 +1,91 @@
1
+ # SID File Structure (PSID/RSID) — Concise Overview
2
+
3
+ This guide summarizes the High Voltage SID Collection (HVSC) "PSID file format" header used by classic SID tunes. A SID file consists of:
4
+
5
+ - A header containing metadata and playback parameters
6
+ - The actual 6502 machine code and data that the player runs on the C64
7
+
8
+ You can play a SID file on a connected device using the REST API via the MCP tool `sidplay_file`.
9
+
10
+ ## Quick start: play a SID via REST/MCP
11
+
12
+ - Endpoint: `POST /tools/sidplay_file`
13
+ - Body: `{ "path": "/flash/music/Example.sid", "songnr": 1 }` (optional `songnr`)
14
+
15
+ Refer to `doc/rest/c64-rest-api.md` for the underlying device endpoint and options.
16
+
17
+ ## Header layout (common fields)
18
+
19
+ Offsets are from the start of the file. All multi-byte fields are big-endian.
20
+
21
+ - +00 — magic (4 bytes): ASCII `PSID` or `RSID`
22
+ - `PSID`: C64-compatible files (player-friendly subset); `RSID`: more strict C64 requirements (no BASIC SYS stubs, tighter hardware expectations)
23
+ - +04 — version (uint16): 1..4
24
+ - Higher versions extend the header with more fields (see below)
25
+ - +06 — dataOffset (uint16): byte offset where the code/data block begins
26
+ - Typically static per version; points to the start of the machine code region
27
+ - +08 — loadAddress (uint16): target RAM address for the data block
28
+ - If 0, the first two bytes of the data block contain the load address
29
+ - +0A — initAddress (uint16): address to call once at start
30
+ - Accumulator selects the sub-song number when calling init
31
+ - If 0, use `loadAddress` as the init routine address
32
+ - +0C — playAddress (uint16): address to call repeatedly to advance playback
33
+ - Usually invoked by the IRQ handler at 50/60 Hz
34
+ - If 0, the code is expected to install its own ISR during init
35
+ - +0E — songs (uint16): number of available sub-songs (>= 1)
36
+ - +10 — startSong (uint16): default sub-song to start with (1-based)
37
+ - +12 — speed (uint32): per-song timing bits (bit i = song i)
38
+ - Bit = 0: use vertical blank (VIC raster; PAL/NTSC rate)
39
+ - Bit = 1: use CIA1 timer-driven playback (~60 Hz)
40
+
41
+ For version 1, the header ends here and the data begins at `+76` (decimal) unless `dataOffset` states otherwise.
42
+
43
+ ## Extended header (versions 2–4)
44
+
45
+ These versions append more detail for environment and chip configuration.
46
+
47
+ - +76 — flags (uint32): bitfield
48
+ - b0: 0 = built-in player, 1 = Compute! player
49
+ - b1: 0 = C64-compatible PSID, 1 = PlaySID-specific/C64 BASIC
50
+ - b2–b3 (video): `01`=PAL, `10`=NTSC, `11`=Both, `00`=Unknown
51
+ - b4–b5 (primary SID model): `00`=unknown, `01`=6581, `10`=8580, `11`=both
52
+ - b6–b7 (second SID model): same encoding (v3+)
53
+ - b8–b9 (third SID model): same encoding (v4)
54
+ - Remaining bits reserved (set to 0)
55
+ - +78 — startPage (uint8): high byte of a free memory page for a music driver; `0` means free outside data block, `0xFF` means none
56
+ - +79 — pageLength (uint8): number of contiguous pages available starting at `startPage`
57
+ - +7A — secondSIDAddress (uint8): `0x2x` → second SID at `$D2x0`, etc. (v3+)
58
+ - +7B — thirdSIDAddress (uint8): third SID address like above (v4)
59
+ - +7C — data start (beginning of machine code and data)
60
+
61
+ Notes:
62
+
63
+ - Version number loosely correlates with multi-SID usage: v2 single-SID, v3 dual-SID, v4 triple-SID.
64
+ - PAL vs NTSC differences and SID model (6581/8580) materially affect sound; consult `flags`.
65
+
66
+ ## Timing and interrupts
67
+
68
+ - `speed` determines whether playback uses VBlank (VIC raster IRQ) or CIA1 timer.
69
+ - In RSID, VIC raster is generally not to be assumed; rely on the player-installed timing per header hints.
70
+
71
+ ## Song length database (Songlengths.md5)
72
+
73
+ SID files do not embed duration. HVSC ships a `Songlengths.md5` database mapping each file’s MD5 to a track length:
74
+
75
+ - Format: INI-like: a header line `[database]`, then repeated groups of a commented filename line (starts with `;`) and a line with `MD5 = mm:ss[.ms]`.
76
+ - To obtain a length: compute MD5 of the full SID file (header + data), look up the matching entry, parse minutes/seconds (and optional milliseconds).
77
+
78
+ ## Practical playback flow
79
+
80
+ 1) Read header (verify `PSID`/`RSID`).
81
+ 2) Resolve `dataOffset` and `loadAddress`; if `loadAddress=0`, fetch it from first two data bytes.
82
+ 3) Load data to RAM and call `initAddress` (or `loadAddress` if `initAddress=0`), passing the desired sub-song in `A`.
83
+ 4) Repeatedly call `playAddress` (or let the player’s ISR drive it) at the cadence implied by `speed`.
84
+ 5) Select sub-songs via re-invoking `init` with accumulator set to song number.
85
+
86
+ ## Related API and tools
87
+
88
+ - Play from device filesystem: `sidplay_file` (see generated `mcp-manifest.json` and `src/index.ts`)
89
+ - Low-level SID register programming: see `data/audio/sid-spec.md`
90
+
91
+ References for further reading: HVSC PSID v2NG file format (spec), community resources, and project notes from implementations like SIDman.