@henols/c64-re-tools 1.1.0 → 2.0.0-rc.2

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/LICENSE +21 -0
  2. package/README.md +190 -37
  3. package/distribution/package.ts +36 -0
  4. package/distribution/plugin.ts +120 -0
  5. package/package.json +34 -40
  6. package/skills/c64-assembler/SKILL.md +73 -0
  7. package/skills/c64-assembler/scripts/assemble.ts +103 -0
  8. package/skills/c64-basic/SKILL.md +67 -0
  9. package/skills/c64-basic/scripts/basic.ts +46 -0
  10. package/skills/c64-disk/SKILL.md +79 -0
  11. package/skills/c64-disk/scripts/disk.ts +65 -0
  12. package/skills/c64-emulator/SKILL.md +124 -0
  13. package/skills/c64-knowledge/SKILL.md +102 -0
  14. package/skills/c64-knowledge/scripts/knowledge.ts +257 -0
  15. package/skills/c64-memory-map/SKILL.md +76 -0
  16. package/skills/c64-memory-map/scripts/memmap.ts +81 -0
  17. package/skills/c64-memory-map/scripts/platform.ts +424 -0
  18. package/skills/c64-provenance/SKILL.md +76 -0
  19. package/skills/c64-provenance/scripts/compare.ts +92 -0
  20. package/skills/c64-provenance/scripts/provenance.ts +83 -0
  21. package/skills/c64-reverse-engineering/SKILL.md +90 -0
  22. package/skills/c64-static-analysis/SKILL.md +96 -0
  23. package/skills/c64-static-analysis/scripts/analyze.ts +175 -0
  24. package/skills/c64-static-analysis/scripts/findings.ts +73 -0
  25. package/skills/c64-testing/SKILL.md +79 -0
  26. package/skills/c64-testing/references/scenario-format.md +76 -0
  27. package/skills/c64-testing/scripts/checklist.ts +51 -0
  28. package/skills/c64-testing/scripts/scenario.ts +473 -0
  29. package/skills/c64-testing/scripts/test.ts +55 -0
  30. package/skills/c64-unpacker/SKILL.md +82 -0
  31. package/skills/c64-unpacker/scripts/evidence.ts +135 -0
  32. package/skills/c64-unpacker/scripts/unpack.ts +216 -0
  33. package/src/c64.ts +281 -0
  34. package/src/cli/ap-sdk.ts +44 -0
  35. package/src/cli/cleanup.ts +194 -0
  36. package/src/cli/main.ts +122 -0
  37. package/src/cli/status.ts +34 -0
  38. package/src/host/main.ts +121 -0
  39. package/src/host/server.ts +409 -0
  40. package/src/host/tools/c1541.ts +255 -0
  41. package/src/host/tools/index.ts +31 -0
  42. package/src/host/tools/petcat.ts +70 -0
  43. package/src/host/tools/status.ts +30 -0
  44. package/src/host/tools/version.ts +41 -0
  45. package/src/host/vice/adapter.ts +761 -0
  46. package/src/host/vice/binary-monitor.ts +389 -0
  47. package/src/host/vice/chips.ts +108 -0
  48. package/src/host/vice/connect.ts +34 -0
  49. package/src/host/vice/process.ts +402 -0
  50. package/src/host/vice/screen.ts +183 -0
  51. package/src/host/vice/session.ts +1316 -0
  52. package/src/host/vice/text-monitor.ts +223 -0
  53. package/src/host-client/connect.ts +241 -0
  54. package/src/host-client/tools.ts +89 -0
  55. package/src/host-client/transfer.ts +100 -0
  56. package/src/host-client/vice-session.ts +254 -0
  57. package/src/knowledge/database.ts +132 -0
  58. package/src/knowledge/history.ts +128 -0
  59. package/src/knowledge/import.ts +344 -0
  60. package/src/knowledge/read.ts +191 -0
  61. package/src/knowledge/schema.ts +140 -0
  62. package/src/knowledge/write.ts +348 -0
  63. package/src/mcp/main.ts +109 -0
  64. package/src/mcp/server.ts +307 -0
  65. package/src/mcp/tools/debug.ts +237 -0
  66. package/src/mcp/tools/execution.ts +116 -0
  67. package/src/mcp/tools/input.ts +69 -0
  68. package/src/mcp/tools/machine.ts +97 -0
  69. package/src/mcp/tools/media.ts +106 -0
  70. package/src/mcp/tools/memory.ts +203 -0
  71. package/src/mcp/tools/video.ts +260 -0
  72. package/src/native/acme.ts +116 -0
  73. package/src/native/discover.ts +141 -0
  74. package/src/native/dxa.ts +166 -0
  75. package/src/native/ghidra/analyze.ts +86 -0
  76. package/src/native/ghidra/index.ts +157 -0
  77. package/src/native/ghidra/language/c64rt_6510.cspec +34 -0
  78. package/src/native/ghidra/language/c64rt_6510.ldefs +15 -0
  79. package/src/native/ghidra/language/c64rt_6510.pspec +15 -0
  80. package/src/native/ghidra/language/c64rt_6510.slaspec +596 -0
  81. package/src/native/ghidra/scripts/C64Export.java +242 -0
  82. package/src/native/ghidra/scripts/C64Prepare.java +60 -0
  83. package/src/native/local.ts +34 -0
  84. package/src/native/processes.ts +350 -0
  85. package/src/native/run.ts +172 -0
  86. package/src/native/staging.ts +83 -0
  87. package/src/native/status.ts +101 -0
  88. package/src/native/types.ts +228 -0
  89. package/src/native/watchdog.ts +78 -0
  90. package/src/project.ts +39 -0
  91. package/src/protocol/framing.ts +132 -0
  92. package/src/protocol/messages.ts +252 -0
  93. package/src/protocol/tools.ts +247 -0
  94. package/src/protocol/vice.ts +1235 -0
  95. package/src/script.ts +59 -0
  96. package/src/time.ts +15 -0
  97. package/src/trace.ts +162 -0
  98. package/THIRD-PARTY-NOTICES.md +0 -26
  99. package/bin/cli.mjs +0 -283
  100. package/skills/acme-build/SKILL.md +0 -260
  101. package/skills/acme-build/scripts/acme.mjs +0 -341
  102. package/skills/acme-build/template.a +0 -53
  103. package/skills/c64-disk-access/SKILL.md +0 -159
  104. package/skills/c64-disk-access/scripts/c1541.mjs +0 -569
  105. package/skills/c64-memory-mapping/SKILL.md +0 -631
  106. package/skills/c64-memory-mapping/memmap.json +0 -8800
  107. package/skills/c64-memory-mapping/scripts/driver.mjs +0 -553
  108. package/skills/c64-petcat/SKILL.md +0 -87
  109. package/skills/c64-petcat/scripts/petcat.mjs +0 -221
  110. package/skills/c64-program-recon/SKILL.md +0 -706
  111. package/skills/c64-program-recon/references/control-flow.md +0 -180
  112. package/skills/c64-program-recon/references/graphics.md +0 -79
  113. package/skills/c64-program-recon/references/observation-hazards.md +0 -143
  114. package/skills/c64-program-recon/references/reconstruction.md +0 -133
  115. package/skills/c64-program-recon/references/sound-and-input.md +0 -74
  116. package/skills/c64-program-recon/references/tool-selection.md +0 -78
  117. package/skills/c64-program-recon/scripts/derive.mjs +0 -364
  118. package/skills/c64-program-recon/scripts/packer-finding.mjs +0 -709
  119. package/skills/c64-program-recon/templates/memory-map.template.md +0 -101
  120. package/skills/c64-provenance-diff/SKILL.md +0 -292
  121. package/skills/c64-provenance-diff/scripts/diff-images.mjs +0 -984
  122. package/skills/c64-provenance-diff/scripts/recovery-schema.mjs +0 -395
  123. package/skills/c64-ram-capture/RELEASES.json.example +0 -17
  124. package/skills/c64-ram-capture/SKILL.md +0 -474
  125. package/skills/c64-ram-capture/scripts/compare-cross-binary.mjs +0 -525
  126. package/skills/c64-ram-capture/scripts/compare.mjs +0 -258
  127. package/skills/c64-ram-capture/scripts/derive-transients.mjs +0 -575
  128. package/skills/c64-ram-capture/scripts/dump-artifacts.mjs +0 -317
  129. package/skills/c64-ram-capture/scripts/mcp-module.mjs +0 -174
  130. package/skills/c64-ram-capture/scripts/project-paths.mjs +0 -81
  131. package/skills/c64-ram-capture/scripts/releases.mjs +0 -109
  132. package/skills/c64-ram-capture/scripts/vsf-slice.mjs +0 -147
  133. package/skills/c64-ram-capture/scripts/watch-loads.mjs +0 -581
  134. package/skills/c64-ram-capture/templates/capture-record.template.md +0 -99
  135. package/skills/c64-ram-capture/transients/README.md +0 -137
  136. package/skills/routine-queue-walker/SKILL.md +0 -394
  137. package/skills/routine-queue-walker/scripts/completeness-report.mjs +0 -465
  138. package/skills/vice-wedge-triage/SKILL.md +0 -269
@@ -0,0 +1,596 @@
1
+ # NMOS 6502/6510 for the C64: all 256 opcodes.
2
+ #
3
+ # The 151 documented opcodes, plus the 105 undocumented ones:
4
+ # - deterministic ones (SLO RLA SRE RRA SAX LAX DCP ISC ANC ALR ARR SBX USBC
5
+ # LAS and the NOPs with operands) have full p-code;
6
+ # - unstable ones (ANE LXA SHA SHX SHY TAS) keep their inputs and addresses
7
+ # but compute their value with an opaque pcodeop;
8
+ # - JAM halts the CPU: it loops on itself, so flow never falls through.
9
+ # ADC, SBC and the undocumented instructions that use them (RRA ISC USBC ARR)
10
+ # follow NMOS decimal mode when D is set. The processor spec tracks D=0 at
11
+ # function entry, so code that never sets D decompiles without the decimal path.
12
+ #
13
+ # Registers and stack follow Ghidra's 6502 language. JSR pushes the return
14
+ # address minus one and RTS adds one, as the CPU does, so return-address tricks
15
+ # resolve correctly.
16
+
17
+ define endian=little;
18
+ define alignment=1;
19
+
20
+ define space RAM type=ram_space size=2 default;
21
+ define space register type=register_space size=1;
22
+
23
+ define register offset=0x00 size=1 [ A X Y P ];
24
+ define register offset=0x20 size=2 [ PC SP ];
25
+ define register offset=0x20 size=1 [ PCL PCH S SH ];
26
+ define register offset=0x30 size=1 [ N V B D I Z C ];
27
+
28
+ define token opbyte (8)
29
+ op = (0,7)
30
+ aaa = (5,7)
31
+ bbb = (2,4)
32
+ cc = (0,1)
33
+ ;
34
+
35
+ define token data8 (8)
36
+ imm8 = (0,7)
37
+ rel = (0,7) signed
38
+ zpff = (0,7)
39
+ ;
40
+
41
+ define token data16 (16)
42
+ imm16 = (0,15)
43
+ abslo = (0,7)
44
+ abshi = (8,15)
45
+ ;
46
+
47
+ # Unstable undocumented instructions: the result depends on the chip and the bus.
48
+ define pcodeop ane;
49
+ define pcodeop lxa;
50
+ define pcodeop sha;
51
+ define pcodeop shx;
52
+ define pcodeop shy;
53
+ define pcodeop tas;
54
+ # The CPU stops until a reset.
55
+ define pcodeop jam;
56
+
57
+ macro resultFlags(value) {
58
+ Z = (value == 0);
59
+ N = (value s< 0);
60
+ }
61
+
62
+ macro pushP(brk) {
63
+ local p:1 = (N << 7) | (V << 6) | 0x20 | (brk << 4) | (D << 3) | (I << 2) | (Z << 1) | C;
64
+ *:1 SP = p;
65
+ SP = SP - 1;
66
+ }
67
+
68
+ macro pullP() {
69
+ SP = SP + 1;
70
+ local p:1 = *:1 SP;
71
+ N = p[7,1];
72
+ V = p[6,1];
73
+ B = p[4,1];
74
+ D = p[3,1];
75
+ I = p[2,1];
76
+ Z = p[1,1];
77
+ C = p[0,1];
78
+ }
79
+
80
+ macro adcBinary(value) {
81
+ local sum:2 = zext(A) + zext(value) + zext(C);
82
+ local r:1 = sum:1;
83
+ V = ((A ^ r) & (value ^ r) & 0x80) != 0;
84
+ C = sum > 0xff;
85
+ A = r;
86
+ resultFlags(A);
87
+ }
88
+
89
+ # NMOS decimal ADC: N and V come from the intermediate result, Z from the binary sum.
90
+ macro adcDecimal(value) {
91
+ local a2:2 = zext(A);
92
+ local b2:2 = zext(value);
93
+ local c2:2 = zext(C);
94
+ Z = ((a2 + b2 + c2) & 0xff) == 0;
95
+ local al:2 = (a2 & 0x0f) + (b2 & 0x0f) + c2;
96
+ local low:2 = zext(al >= 0x0a);
97
+ al = ((((al + 0x06) & 0x0f) + 0x10) * low) + (al * (1 - low));
98
+ local sum:2 = (a2 & 0xf0) + (b2 & 0xf0) + al;
99
+ N = (sum & 0x80) != 0;
100
+ local signed:2 = sext(A & 0xf0) + sext(value & 0xf0) + al;
101
+ V = (signed s< -128) || (signed s> 127);
102
+ local high:2 = zext(sum >= 0xa0);
103
+ sum = sum + (0x60 * high);
104
+ C = sum > 0xff;
105
+ A = sum:1;
106
+ }
107
+
108
+ macro sbcBinary(value) {
109
+ adcBinary(~value);
110
+ }
111
+
112
+ # NMOS decimal SBC: the flags are the binary ones.
113
+ macro sbcDecimal(value) {
114
+ local a2:2 = zext(A);
115
+ local b2:2 = zext(value);
116
+ local c2:2 = zext(C);
117
+ local binary:2 = a2 + zext(~value) + c2;
118
+ local r:1 = binary:1;
119
+ V = ((A ^ r) & ((~value) ^ r) & 0x80) != 0;
120
+ resultFlags(r);
121
+ local al:2 = (a2 & 0x0f) - (b2 & 0x0f) + c2 - 1;
122
+ local low:2 = zext(al s< 0);
123
+ al = (((((al - 0x06) & 0x0f) - 0x10)) * low) + (al * (1 - low));
124
+ local diff:2 = (a2 & 0xf0) - (b2 & 0xf0) + al;
125
+ local high:2 = zext(diff s< 0);
126
+ diff = diff - (0x60 * high);
127
+ C = binary > 0xff;
128
+ A = diff:1;
129
+ }
130
+
131
+ macro compare(register, value) {
132
+ local r:1 = register - value;
133
+ resultFlags(r);
134
+ C = register >= value;
135
+ }
136
+
137
+ ################################################################
138
+ # Operand tables selected by the opcode's bbb bits (the rest of the opcode
139
+ # selects the instruction). A table has no row for a bbb that the
140
+ # instruction does not have, so that opcode decodes as another instruction.
141
+
142
+ # ORA AND EOR ADC LDA CMP SBC
143
+ OP1: "#"imm8 is bbb=2; imm8 { local v:1 = imm8; export v; }
144
+ OP1: imm8 is bbb=1; imm8 { export *:1 imm8; }
145
+ OP1: imm8,X is bbb=5 & X; imm8 { local a:2 = zext(imm8 + X); export *:1 a; }
146
+ OP1: imm16 is bbb=3; imm16 { export *:1 imm16; }
147
+ OP1: imm16,X is bbb=7 & X; imm16 { local a:2 = imm16 + zext(X); export *:1 a; }
148
+ OP1: imm16,Y is bbb=6 & Y; imm16 { local a:2 = imm16 + zext(Y); export *:1 a; }
149
+ # (zp,X): the pointer wraps in zero page, so at $ff its high byte comes from $00.
150
+ OP1: (imm8,X) is bbb=0 & X; imm8 { local z:1 = imm8 + X; local lo:2 = zext(z); local hi:2 = zext(z + 1); local a:2 = zext(*:1 hi) << 8 | zext(*:1 lo); export *:1 a; }
151
+ OP1: (imm8),Y is bbb=4 & Y; imm8 { local z:2 = imm8; local a:2 = (*:2 z) + zext(Y); export *:1 a; }
152
+ # (zp),Y with zp $ff: the pointer's high byte comes from $00.
153
+ OP1: (imm8),Y is bbb=4 & Y; imm8 & zpff=0xff { local a:2 = (zext(*:1 0x00:2) << 8 | zext(*:1 0xff:2)) + zext(Y); export *:1 a; }
154
+
155
+ # STA and the undocumented SLO RLA SRE RRA DCP ISC: as OP1 without immediate.
156
+ MEM: imm8 is bbb=1; imm8 { export *:1 imm8; }
157
+ MEM: imm8,X is bbb=5 & X; imm8 { local a:2 = zext(imm8 + X); export *:1 a; }
158
+ MEM: imm16 is bbb=3; imm16 { export *:1 imm16; }
159
+ MEM: imm16,X is bbb=7 & X; imm16 { local a:2 = imm16 + zext(X); export *:1 a; }
160
+ MEM: imm16,Y is bbb=6 & Y; imm16 { local a:2 = imm16 + zext(Y); export *:1 a; }
161
+ MEM: (imm8,X) is bbb=0 & X; imm8 { local z:1 = imm8 + X; local lo:2 = zext(z); local hi:2 = zext(z + 1); local a:2 = zext(*:1 hi) << 8 | zext(*:1 lo); export *:1 a; }
162
+ MEM: (imm8),Y is bbb=4 & Y; imm8 { local z:2 = imm8; local a:2 = (*:2 z) + zext(Y); export *:1 a; }
163
+ MEM: (imm8),Y is bbb=4 & Y; imm8 & zpff=0xff { local a:2 = (zext(*:1 0x00:2) << 8 | zext(*:1 0xff:2)) + zext(Y); export *:1 a; }
164
+
165
+ # ASL ROL LSR ROR
166
+ SHIFT: A is bbb=2 & A { export A; }
167
+ SHIFT: imm8 is bbb=1; imm8 { export *:1 imm8; }
168
+ SHIFT: imm8,X is bbb=5 & X; imm8 { local a:2 = zext(imm8 + X); export *:1 a; }
169
+ SHIFT: imm16 is bbb=3; imm16 { export *:1 imm16; }
170
+ SHIFT: imm16,X is bbb=7 & X; imm16 { local a:2 = imm16 + zext(X); export *:1 a; }
171
+
172
+ # INC DEC
173
+ INCDEC: imm8 is bbb=1; imm8 { export *:1 imm8; }
174
+ INCDEC: imm8,X is bbb=5 & X; imm8 { local a:2 = zext(imm8 + X); export *:1 a; }
175
+ INCDEC: imm16 is bbb=3; imm16 { export *:1 imm16; }
176
+ INCDEC: imm16,X is bbb=7 & X; imm16 { local a:2 = imm16 + zext(X); export *:1 a; }
177
+
178
+ # LDX
179
+ LDXM: "#"imm8 is bbb=0; imm8 { local v:1 = imm8; export v; }
180
+ LDXM: imm8 is bbb=1; imm8 { export *:1 imm8; }
181
+ LDXM: imm8,Y is bbb=5 & Y; imm8 { local a:2 = zext(imm8 + Y); export *:1 a; }
182
+ LDXM: imm16 is bbb=3; imm16 { export *:1 imm16; }
183
+ LDXM: imm16,Y is bbb=7 & Y; imm16 { local a:2 = imm16 + zext(Y); export *:1 a; }
184
+
185
+ # STX
186
+ STXM: imm8 is bbb=1; imm8 { export *:1 imm8; }
187
+ STXM: imm8,Y is bbb=5 & Y; imm8 { local a:2 = zext(imm8 + Y); export *:1 a; }
188
+ STXM: imm16 is bbb=3; imm16 { export *:1 imm16; }
189
+
190
+ # LDY
191
+ LDYM: "#"imm8 is bbb=0; imm8 { local v:1 = imm8; export v; }
192
+ LDYM: imm8 is bbb=1; imm8 { export *:1 imm8; }
193
+ LDYM: imm8,X is bbb=5 & X; imm8 { local a:2 = zext(imm8 + X); export *:1 a; }
194
+ LDYM: imm16 is bbb=3; imm16 { export *:1 imm16; }
195
+ LDYM: imm16,X is bbb=7 & X; imm16 { local a:2 = imm16 + zext(X); export *:1 a; }
196
+
197
+ # STY
198
+ STYM: imm8 is bbb=1; imm8 { export *:1 imm8; }
199
+ STYM: imm8,X is bbb=5 & X; imm8 { local a:2 = zext(imm8 + X); export *:1 a; }
200
+ STYM: imm16 is bbb=3; imm16 { export *:1 imm16; }
201
+
202
+ # CPX CPY
203
+ CPM: "#"imm8 is bbb=0; imm8 { local v:1 = imm8; export v; }
204
+ CPM: imm8 is bbb=1; imm8 { export *:1 imm8; }
205
+ CPM: imm16 is bbb=3; imm16 { export *:1 imm16; }
206
+
207
+ # Operand tables for instructions decoded by their full opcode.
208
+ IMMB: "#"imm8 is imm8 { local v:1 = imm8; export v; }
209
+ ZPB: imm8 is imm8 { export *:1 imm8; }
210
+ ZPXB: imm8,X is X & imm8 { local a:2 = zext(imm8 + X); export *:1 a; }
211
+ ZPYB: imm8,Y is Y & imm8 { local a:2 = zext(imm8 + Y); export *:1 a; }
212
+ ABSB: imm16 is imm16 { export *:1 imm16; }
213
+ ABSXB: imm16,X is X & imm16 { local a:2 = imm16 + zext(X); export *:1 a; }
214
+ ABSYB: imm16,Y is Y & imm16 { local a:2 = imm16 + zext(Y); export *:1 a; }
215
+ INDXB: (imm8,X) is X & imm8 { local z:1 = imm8 + X; local lo:2 = zext(z); local hi:2 = zext(z + 1); local a:2 = zext(*:1 hi) << 8 | zext(*:1 lo); export *:1 a; }
216
+ INDYB: (imm8),Y is Y & imm8 { local z:2 = imm8; local a:2 = (*:2 z) + zext(Y); export *:1 a; }
217
+
218
+ REL: target is rel [ target = inst_next + rel; ] { export *:2 target; }
219
+
220
+ ################################################################
221
+ # Documented instructions
222
+
223
+ :ADC OP1 is (cc=1 & aaa=3) ... & OP1 {
224
+ local value:1 = OP1;
225
+ if (D) goto <decimal>;
226
+ adcBinary(value);
227
+ goto <done>;
228
+ <decimal>
229
+ adcDecimal(value);
230
+ <done>
231
+ }
232
+
233
+ :AND OP1 is (cc=1 & aaa=1) ... & OP1 { A = A & OP1; resultFlags(A); }
234
+
235
+ :ASL SHIFT is (cc=2 & aaa=0) ... & SHIFT {
236
+ local value:1 = SHIFT;
237
+ C = value >> 7;
238
+ value = value << 1;
239
+ SHIFT = value;
240
+ resultFlags(value);
241
+ }
242
+
243
+ :BCC REL is op=0x90; REL { if (C == 0) goto REL; }
244
+ :BCS REL is op=0xB0; REL { if (C) goto REL; }
245
+ :BEQ REL is op=0xF0; REL { if (Z) goto REL; }
246
+ :BMI REL is op=0x30; REL { if (N) goto REL; }
247
+ :BNE REL is op=0xD0; REL { if (Z == 0) goto REL; }
248
+ :BPL REL is op=0x10; REL { if (N == 0) goto REL; }
249
+ :BVC REL is op=0x50; REL { if (V == 0) goto REL; }
250
+ :BVS REL is op=0x70; REL { if (V) goto REL; }
251
+
252
+ :BIT ZPB is op=0x24; ZPB {
253
+ local value:1 = ZPB;
254
+ N = value[7,1];
255
+ V = value[6,1];
256
+ Z = (A & value) == 0;
257
+ }
258
+ :BIT ABSB is op=0x2C; ABSB {
259
+ local value:1 = ABSB;
260
+ N = value[7,1];
261
+ V = value[6,1];
262
+ Z = (A & value) == 0;
263
+ }
264
+
265
+ # BRK skips its padding byte: the return address is two bytes on.
266
+ :BRK is op=0x00 {
267
+ *:2 (SP - 1) = inst_next + 1;
268
+ SP = SP - 2;
269
+ pushP(1);
270
+ I = 1;
271
+ local vector:2 = 0xfffe;
272
+ goto [*:2 vector];
273
+ }
274
+
275
+ :CLC is op=0x18 { C = 0; }
276
+ :CLD is op=0xD8 { D = 0; }
277
+ :CLI is op=0x58 { I = 0; }
278
+ :CLV is op=0xB8 { V = 0; }
279
+
280
+ :CMP OP1 is (cc=1 & aaa=6) ... & OP1 { local value:1 = OP1; compare(A, value); }
281
+ :CPX CPM is (cc=0 & aaa=7) ... & CPM { local value:1 = CPM; compare(X, value); }
282
+ :CPY CPM is (cc=0 & aaa=6) ... & CPM { local value:1 = CPM; compare(Y, value); }
283
+
284
+ :DEC INCDEC is (cc=2 & aaa=6) ... & INCDEC { local value:1 = INCDEC - 1; INCDEC = value; resultFlags(value); }
285
+ :DEX is op=0xCA { X = X - 1; resultFlags(X); }
286
+ :DEY is op=0x88 { Y = Y - 1; resultFlags(Y); }
287
+
288
+ :EOR OP1 is (cc=1 & aaa=2) ... & OP1 { A = A ^ OP1; resultFlags(A); }
289
+
290
+ :INC INCDEC is (cc=2 & aaa=7) ... & INCDEC { local value:1 = INCDEC + 1; INCDEC = value; resultFlags(value); }
291
+ :INX is op=0xE8 { X = X + 1; resultFlags(X); }
292
+ :INY is op=0xC8 { Y = Y + 1; resultFlags(Y); }
293
+
294
+ :JMP ABSB is op=0x4C; ABSB { goto ABSB; }
295
+ # The NMOS indirect JMP does not carry into the high byte of the pointer address.
296
+ :JMP (imm16) is op=0x6C; imm16 { local pointer:2 = imm16; goto [*:2 pointer]; }
297
+ :JMP (imm16) is op=0x6C; imm16 & abslo=0xff {
298
+ local low:2 = imm16;
299
+ local high:2 = imm16 & 0xff00;
300
+ local target:2 = zext(*:1 low) | (zext(*:1 high) << 8);
301
+ goto [target];
302
+ }
303
+
304
+ :JSR ABSB is op=0x20; ABSB {
305
+ *:2 (SP - 1) = inst_next - 1;
306
+ SP = SP - 2;
307
+ call ABSB;
308
+ }
309
+
310
+ :LDA OP1 is (cc=1 & aaa=5) ... & OP1 { A = OP1; resultFlags(A); }
311
+ :LDX LDXM is (cc=2 & aaa=5) ... & LDXM { X = LDXM; resultFlags(X); }
312
+ :LDY LDYM is (cc=0 & aaa=5) ... & LDYM { Y = LDYM; resultFlags(Y); }
313
+
314
+ :LSR SHIFT is (cc=2 & aaa=2) ... & SHIFT {
315
+ local value:1 = SHIFT;
316
+ C = value & 1;
317
+ value = value >> 1;
318
+ SHIFT = value;
319
+ resultFlags(value);
320
+ }
321
+
322
+ :NOP is op=0xEA { }
323
+
324
+ :ORA OP1 is (cc=1 & aaa=0) ... & OP1 { A = A | OP1; resultFlags(A); }
325
+
326
+ :PHA is op=0x48 { *:1 SP = A; SP = SP - 1; }
327
+ :PHP is op=0x08 { pushP(1); }
328
+ :PLA is op=0x68 { SP = SP + 1; A = *:1 SP; resultFlags(A); }
329
+ :PLP is op=0x28 { pullP(); }
330
+
331
+ :ROL SHIFT is (cc=2 & aaa=1) ... & SHIFT {
332
+ local value:1 = SHIFT;
333
+ local carry:1 = C;
334
+ C = value >> 7;
335
+ value = (value << 1) | carry;
336
+ SHIFT = value;
337
+ resultFlags(value);
338
+ }
339
+
340
+ :ROR SHIFT is (cc=2 & aaa=3) ... & SHIFT {
341
+ local value:1 = SHIFT;
342
+ local carry:1 = C << 7;
343
+ C = value & 1;
344
+ value = (value >> 1) | carry;
345
+ SHIFT = value;
346
+ resultFlags(value);
347
+ }
348
+
349
+ :RTI is op=0x40 {
350
+ pullP();
351
+ SP = SP + 1;
352
+ local target:2 = *:2 SP;
353
+ SP = SP + 1;
354
+ return [target];
355
+ }
356
+
357
+ :RTS is op=0x60 {
358
+ SP = SP + 1;
359
+ local target:2 = *:2 SP;
360
+ SP = SP + 1;
361
+ target = target + 1;
362
+ return [target];
363
+ }
364
+
365
+ :SBC OP1 is (cc=1 & aaa=7) ... & OP1 {
366
+ local value:1 = OP1;
367
+ if (D) goto <decimal>;
368
+ sbcBinary(value);
369
+ goto <done>;
370
+ <decimal>
371
+ sbcDecimal(value);
372
+ <done>
373
+ }
374
+
375
+ :SEC is op=0x38 { C = 1; }
376
+ :SED is op=0xF8 { D = 1; }
377
+ :SEI is op=0x78 { I = 1; }
378
+
379
+ :STA MEM is (cc=1 & aaa=4) ... & MEM { MEM = A; }
380
+ :STX STXM is (cc=2 & aaa=4) ... & STXM { STXM = X; }
381
+ :STY STYM is (cc=0 & aaa=4) ... & STYM { STYM = Y; }
382
+
383
+ :TAX is op=0xAA { X = A; resultFlags(X); }
384
+ :TAY is op=0xA8 { Y = A; resultFlags(Y); }
385
+ :TSX is op=0xBA { X = S; resultFlags(X); }
386
+ :TXA is op=0x8A { A = X; resultFlags(A); }
387
+ :TXS is op=0x9A { S = X; }
388
+ :TYA is op=0x98 { A = Y; resultFlags(A); }
389
+
390
+ ################################################################
391
+ # Undocumented instructions: deterministic
392
+
393
+ # SLO: ASL memory, then ORA.
394
+ :SLO MEM is (cc=3 & aaa=0) ... & MEM {
395
+ local value:1 = MEM;
396
+ C = value >> 7;
397
+ value = value << 1;
398
+ MEM = value;
399
+ A = A | value;
400
+ resultFlags(A);
401
+ }
402
+
403
+ # RLA: ROL memory, then AND.
404
+ :RLA MEM is (cc=3 & aaa=1) ... & MEM {
405
+ local value:1 = MEM;
406
+ local carry:1 = C;
407
+ C = value >> 7;
408
+ value = (value << 1) | carry;
409
+ MEM = value;
410
+ A = A & value;
411
+ resultFlags(A);
412
+ }
413
+
414
+ # SRE: LSR memory, then EOR.
415
+ :SRE MEM is (cc=3 & aaa=2) ... & MEM {
416
+ local value:1 = MEM;
417
+ C = value & 1;
418
+ value = value >> 1;
419
+ MEM = value;
420
+ A = A ^ value;
421
+ resultFlags(A);
422
+ }
423
+
424
+ # RRA: ROR memory, then ADC (decimal mode applies).
425
+ :RRA MEM is (cc=3 & aaa=3) ... & MEM {
426
+ local value:1 = MEM;
427
+ local carry:1 = C << 7;
428
+ C = value & 1;
429
+ value = (value >> 1) | carry;
430
+ MEM = value;
431
+ if (D) goto <decimal>;
432
+ adcBinary(value);
433
+ goto <done>;
434
+ <decimal>
435
+ adcDecimal(value);
436
+ <done>
437
+ }
438
+
439
+ # SAX: store A AND X; no flags.
440
+ :SAX INDXB is op=0x83; INDXB { INDXB = A & X; }
441
+ :SAX ZPB is op=0x87; ZPB { ZPB = A & X; }
442
+ :SAX ABSB is op=0x8F; ABSB { ABSB = A & X; }
443
+ :SAX ZPYB is op=0x97; ZPYB { ZPYB = A & X; }
444
+
445
+ # LAX: load A and X.
446
+ :LAX INDXB is op=0xA3; INDXB { A = INDXB; X = A; resultFlags(A); }
447
+ :LAX ZPB is op=0xA7; ZPB { A = ZPB; X = A; resultFlags(A); }
448
+ :LAX ABSB is op=0xAF; ABSB { A = ABSB; X = A; resultFlags(A); }
449
+ :LAX INDYB is op=0xB3; INDYB { A = INDYB; X = A; resultFlags(A); }
450
+ :LAX ZPYB is op=0xB7; ZPYB { A = ZPYB; X = A; resultFlags(A); }
451
+ :LAX ABSYB is op=0xBF; ABSYB { A = ABSYB; X = A; resultFlags(A); }
452
+
453
+ # DCP: DEC memory, then CMP.
454
+ :DCP MEM is (cc=3 & aaa=6) ... & MEM {
455
+ local value:1 = MEM - 1;
456
+ MEM = value;
457
+ compare(A, value);
458
+ }
459
+
460
+ # ISC: INC memory, then SBC (decimal mode applies).
461
+ :ISC MEM is (cc=3 & aaa=7) ... & MEM {
462
+ local value:1 = MEM + 1;
463
+ MEM = value;
464
+ if (D) goto <decimal>;
465
+ sbcBinary(value);
466
+ goto <done>;
467
+ <decimal>
468
+ sbcDecimal(value);
469
+ <done>
470
+ }
471
+
472
+ # ANC: AND, then C = N.
473
+ :ANC IMMB is (op=0x0B | op=0x2B); IMMB { A = A & IMMB; resultFlags(A); C = N; }
474
+
475
+ # ALR: AND, then LSR A.
476
+ :ALR IMMB is op=0x4B; IMMB {
477
+ local value:1 = A & IMMB;
478
+ C = value & 1;
479
+ A = value >> 1;
480
+ resultFlags(A);
481
+ }
482
+
483
+ # ARR: AND, then ROR A, with its own flags; NMOS decimal mode adjusts the result.
484
+ :ARR IMMB is op=0x6B; IMMB {
485
+ local t:1 = A & IMMB;
486
+ local r:1 = (t >> 1) | (C << 7);
487
+ if (D) goto <decimal>;
488
+ A = r;
489
+ resultFlags(A);
490
+ C = r[6,1];
491
+ V = r[6,1] ^ r[5,1];
492
+ goto <done>;
493
+ <decimal>
494
+ N = C;
495
+ Z = (r == 0);
496
+ V = ((t ^ r) & 0x40) != 0;
497
+ local t2:2 = zext(t);
498
+ local low:1 = ((t2 & 0x0f) + (t2 & 0x01)) > 5;
499
+ r = (((r & 0xf0) | ((r + 6) & 0x0f)) * low) + (r * (1 - low));
500
+ local high:1 = ((t2 & 0xf0) + (t2 & 0x10)) > 0x50;
501
+ r = (((r & 0x0f) | ((r + 0x60) & 0xf0)) * high) + (r * (1 - high));
502
+ C = high;
503
+ A = r;
504
+ <done>
505
+ }
506
+
507
+ # SBX: X = (A AND X) - operand, compare-style carry; no decimal mode.
508
+ :SBX IMMB is op=0xCB; IMMB {
509
+ local value:1 = IMMB;
510
+ local t:1 = A & X;
511
+ C = t >= value;
512
+ X = t - value;
513
+ resultFlags(X);
514
+ }
515
+
516
+ # USBC: the same as SBC #imm.
517
+ :USBC IMMB is op=0xEB; IMMB {
518
+ local value:1 = IMMB;
519
+ if (D) goto <decimal>;
520
+ sbcBinary(value);
521
+ goto <done>;
522
+ <decimal>
523
+ sbcDecimal(value);
524
+ <done>
525
+ }
526
+
527
+ # LAS: memory AND S into A, X and S.
528
+ :LAS ABSYB is op=0xBB; ABSYB {
529
+ local value:1 = ABSYB & S;
530
+ A = value;
531
+ X = value;
532
+ S = value;
533
+ resultFlags(value);
534
+ }
535
+
536
+ # NOPs with operands read their operand and change nothing.
537
+ :NOP IMMB is (op=0x80 | op=0x82 | op=0x89 | op=0xC2 | op=0xE2); IMMB { }
538
+ :NOP ZPB is (op=0x04 | op=0x44 | op=0x64); ZPB { }
539
+ :NOP ZPXB is (op=0x14 | op=0x34 | op=0x54 | op=0x74 | op=0xD4 | op=0xF4); ZPXB { }
540
+ :NOP ABSB is op=0x0C; ABSB { }
541
+ :NOP ABSXB is (op=0x1C | op=0x3C | op=0x5C | op=0x7C | op=0xDC | op=0xFC); ABSXB { }
542
+ :NOP is (op=0x1A | op=0x3A | op=0x5A | op=0x7A | op=0xDA | op=0xFA) { }
543
+
544
+ # JAM: the CPU stops. It is modelled as a loop on itself, so no flow falls through.
545
+ :JAM is (op=0x02 | op=0x12 | op=0x22 | op=0x32 | op=0x42 | op=0x52 | op=0x62 | op=0x72 | op=0x92 | op=0xB2 | op=0xD2 | op=0xF2) {
546
+ jam();
547
+ goto inst_start;
548
+ }
549
+
550
+ ################################################################
551
+ # Undocumented instructions: unstable
552
+ #
553
+ # The data-flow inputs and the effective address stay visible; the value is
554
+ # opaque. H+1 is the high byte of the base address plus one.
555
+
556
+ # ANE (XAA): A = (A OR magic) AND X AND operand.
557
+ :ANE IMMB is op=0x8B; IMMB { A = ane(A, X, IMMB); resultFlags(A); }
558
+
559
+ # LXA: A = X = (A OR magic) AND operand.
560
+ :LXA IMMB is op=0xAB; IMMB { local value:1 = lxa(A, IMMB); A = value; X = value; resultFlags(value); }
561
+
562
+ # SHA (AHX): store A AND X AND H+1.
563
+ :SHA (imm8),Y is op=0x93 & Y; imm8 {
564
+ local z:2 = imm8;
565
+ local base:2 = *:2 z;
566
+ local address:2 = base + zext(Y);
567
+ local high:1 = base(1) + 1;
568
+ *:1 address = sha(A, X, high);
569
+ }
570
+ :SHA imm16,Y is op=0x9F & Y; imm16 & abshi {
571
+ local address:2 = imm16 + zext(Y);
572
+ local high:1 = abshi + 1;
573
+ *:1 address = sha(A, X, high);
574
+ }
575
+
576
+ # SHX: store X AND H+1.
577
+ :SHX imm16,Y is op=0x9E & Y; imm16 & abshi {
578
+ local address:2 = imm16 + zext(Y);
579
+ local high:1 = abshi + 1;
580
+ *:1 address = shx(X, high);
581
+ }
582
+
583
+ # SHY: store Y AND H+1.
584
+ :SHY imm16,X is op=0x9C & X; imm16 & abshi {
585
+ local address:2 = imm16 + zext(X);
586
+ local high:1 = abshi + 1;
587
+ *:1 address = shy(Y, high);
588
+ }
589
+
590
+ # TAS: S = A AND X, then store S AND H+1.
591
+ :TAS imm16,Y is op=0x9B & Y; imm16 & abshi {
592
+ S = A & X;
593
+ local address:2 = imm16 + zext(Y);
594
+ local high:1 = abshi + 1;
595
+ *:1 address = tas(S, high);
596
+ }