@modplayjs/fmt-sid 0.0.0-stage → 0.2.1

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 (98) hide show
  1. package/README.md +111 -2
  2. package/dist/c64.d.ts +8 -0
  3. package/dist/c64.d.ts.map +1 -0
  4. package/dist/c64.js +325 -0
  5. package/dist/c64.js.map +1 -0
  6. package/dist/c64types.d.ts +209 -0
  7. package/dist/c64types.d.ts.map +1 -0
  8. package/dist/c64types.js +181 -0
  9. package/dist/c64types.js.map +1 -0
  10. package/dist/cia.d.ts +19 -0
  11. package/dist/cia.d.ts.map +1 -0
  12. package/dist/cia.js +160 -0
  13. package/dist/cia.js.map +1 -0
  14. package/dist/combiwaves.d.ts +9 -0
  15. package/dist/combiwaves.d.ts.map +1 -0
  16. package/dist/combiwaves.js +533 -0
  17. package/dist/combiwaves.js.map +1 -0
  18. package/dist/config.d.ts +15 -0
  19. package/dist/config.d.ts.map +1 -0
  20. package/dist/config.js +23 -0
  21. package/dist/config.js.map +1 -0
  22. package/dist/cpu.d.ts +12 -0
  23. package/dist/cpu.d.ts.map +1 -0
  24. package/dist/cpu.js +848 -0
  25. package/dist/cpu.js.map +1 -0
  26. package/dist/filtercurves.d.ts +13 -0
  27. package/dist/filtercurves.d.ts.map +1 -0
  28. package/dist/filtercurves.js +546 -0
  29. package/dist/filtercurves.js.map +1 -0
  30. package/dist/host.d.ts +4 -0
  31. package/dist/host.d.ts.map +1 -0
  32. package/dist/host.js +14 -0
  33. package/dist/host.js.map +1 -0
  34. package/dist/index.d.ts +7 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +36 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/instance.d.ts +119 -0
  39. package/dist/instance.d.ts.map +1 -0
  40. package/dist/instance.js +51 -0
  41. package/dist/instance.js.map +1 -0
  42. package/dist/loader.d.ts +26 -0
  43. package/dist/loader.d.ts.map +1 -0
  44. package/dist/loader.js +383 -0
  45. package/dist/loader.js.map +1 -0
  46. package/dist/mem.d.ts +30 -0
  47. package/dist/mem.d.ts.map +1 -0
  48. package/dist/mem.js +187 -0
  49. package/dist/mem.js.map +1 -0
  50. package/dist/psiddigi.d.ts +3 -0
  51. package/dist/psiddigi.d.ts.map +1 -0
  52. package/dist/psiddigi.js +73 -0
  53. package/dist/psiddigi.js.map +1 -0
  54. package/dist/settings.d.ts +41 -0
  55. package/dist/settings.d.ts.map +1 -0
  56. package/dist/settings.js +64 -0
  57. package/dist/settings.js.map +1 -0
  58. package/dist/sid.d.ts +21 -0
  59. package/dist/sid.d.ts.map +1 -0
  60. package/dist/sid.js +78 -0
  61. package/dist/sid.js.map +1 -0
  62. package/dist/sidadsr.d.ts +9 -0
  63. package/dist/sidadsr.d.ts.map +1 -0
  64. package/dist/sidadsr.js +105 -0
  65. package/dist/sidadsr.js.map +1 -0
  66. package/dist/sidoscwaves.d.ts +15 -0
  67. package/dist/sidoscwaves.d.ts.map +1 -0
  68. package/dist/sidoscwaves.js +475 -0
  69. package/dist/sidoscwaves.js.map +1 -0
  70. package/dist/sidoutputs.d.ts +17 -0
  71. package/dist/sidoutputs.d.ts.map +1 -0
  72. package/dist/sidoutputs.js +185 -0
  73. package/dist/sidoutputs.js.map +1 -0
  74. package/dist/sidplugin.d.ts +18 -0
  75. package/dist/sidplugin.d.ts.map +1 -0
  76. package/dist/sidplugin.js +174 -0
  77. package/dist/sidplugin.js.map +1 -0
  78. package/dist/sidrouting.d.ts +8 -0
  79. package/dist/sidrouting.d.ts.map +1 -0
  80. package/dist/sidrouting.js +560 -0
  81. package/dist/sidrouting.js.map +1 -0
  82. package/dist/sidseek.d.ts +8 -0
  83. package/dist/sidseek.d.ts.map +1 -0
  84. package/dist/sidseek.js +60 -0
  85. package/dist/sidseek.js.map +1 -0
  86. package/dist/sincwindow.d.ts +3 -0
  87. package/dist/sincwindow.d.ts.map +1 -0
  88. package/dist/sincwindow.js +78 -0
  89. package/dist/sincwindow.js.map +1 -0
  90. package/dist/songlengths.d.ts +20 -0
  91. package/dist/songlengths.d.ts.map +1 -0
  92. package/dist/songlengths.js +81 -0
  93. package/dist/songlengths.js.map +1 -0
  94. package/dist/vic.d.ts +14 -0
  95. package/dist/vic.d.ts.map +1 -0
  96. package/dist/vic.js +63 -0
  97. package/dist/vic.js.map +1 -0
  98. package/package.json +48 -4
package/dist/cpu.js ADDED
@@ -0,0 +1,848 @@
1
+ // SPDX-License-Identifier: BSD-3-Clause
2
+ // Copyright (c) 2026 Bitti09 — modplayjs contributors
3
+ // Ported from: cRSID 1.58 libcRSID/C64/CPU.c — 6510 CPU emulation for
4
+ // SID/PRG playback, including the illegal opcodes used by scene tunes.
5
+ // (c) Hermit (Mihaly Horvath), license: WTF ("do what the frick you want").
6
+ //
7
+ // C's rd()/wr()/wr2() wrap getMemReadPtr/getMemWritePtr and side-effect
8
+ // IRQ-acknowledge handling; those map to mem.ts read/write + cia/vic hooks.
9
+ import { cRSID } from './instance.js';
10
+ import { cRSID_readMemPtr, cRSID_writeMemPtr } from './mem.js';
11
+ import { cRSID_acknowledgeCIAIRQ, cRSID_writeCIAIRQmask, cRSID_writeCIAlatchAhi, cRSID_writeCIAlatchBhi } from './cia.js';
12
+ import { cRSID_acknowledgeVICrasterIRQ } from './vic.js';
13
+ // enum cRSID_StatusFlagBitValues (CPU.c:4)
14
+ const N = 0x80, V = 0x40, B = 0x10, D = 0x08, I = 0x04, Z = 0x02, C = 0x01;
15
+ /** cRSID_initCPU (CPU.c:16-19). */
16
+ export function cRSID_initCPU(C64, mempos) {
17
+ C64.CPU.PC = mempos;
18
+ C64.CPU.A = 0;
19
+ C64.CPU.X = 0;
20
+ C64.CPU.Y = 0;
21
+ C64.CPU.ST = 0x04;
22
+ C64.CPU.SP = 0xff;
23
+ C64.CPU.PrevNMI = 0;
24
+ }
25
+ /** rd (CPU.c:30-40). */
26
+ function rd(C64, address) {
27
+ const value = cRSID_readMemPtr(C64, address);
28
+ if (C64.RealSIDmode) {
29
+ if (C64.RAMbank[1] & 3) {
30
+ if (address === 0xdc0d)
31
+ cRSID_acknowledgeCIAIRQ(C64, C64.CIA[1]);
32
+ else if (address === 0xdd0d)
33
+ cRSID_acknowledgeCIAIRQ(C64, C64.CIA[2]);
34
+ }
35
+ }
36
+ return value;
37
+ }
38
+ /** wr (CPU.c:42-52). */
39
+ function wr(C64, address, data) {
40
+ cRSID_writeMemPtr(C64, address, data);
41
+ if (C64.RealSIDmode && C64.RAMbank[1] & 3) {
42
+ // if(data&1) { //only writing 1 to $d019 bit0 would acknowledge, not any value (but RMW instructions write $d019 back before mod.)
43
+ if (address === 0xd019)
44
+ cRSID_acknowledgeVICrasterIRQ(C64);
45
+ // }
46
+ }
47
+ }
48
+ /** wr2 (CPU.c:54-115) — PSID-hack specific memory-write. */
49
+ function wr2(C64, address, data) {
50
+ cRSID_writeMemPtr(C64, address, data);
51
+ if (C64.RAMbank[1] & 3) {
52
+ if (C64.RealSIDmode) {
53
+ if ((address & 0xfe00) === 0xdc00) {
54
+ switch (address) {
55
+ case 0xdc0d:
56
+ cRSID_writeCIAIRQmask(C64, C64.CIA[1], data);
57
+ break;
58
+ case 0xdd0d:
59
+ cRSID_writeCIAIRQmask(C64, C64.CIA[2], data);
60
+ break;
61
+ case 0xdc0c:
62
+ C64.IObankRD[address] = data;
63
+ break; // mirror WR to RD (e.g. if byte at DC0C is used as RTI)
64
+ case 0xdd0c:
65
+ C64.IObankRD[address] = data;
66
+ break; // mirror WR to RD (e.g. Wonderland_XIII_tune_1.sid)
67
+ case 0xdc05:
68
+ cRSID_writeCIAlatchAhi(C64, C64.CIA[1], data);
69
+ break;
70
+ case 0xdc07:
71
+ cRSID_writeCIAlatchBhi(C64, C64.CIA[1], data);
72
+ break;
73
+ case 0xdd05:
74
+ cRSID_writeCIAlatchAhi(C64, C64.CIA[2], data);
75
+ break;
76
+ case 0xdd07:
77
+ cRSID_writeCIAlatchBhi(C64, C64.CIA[2], data);
78
+ break;
79
+ }
80
+ }
81
+ else if (address === 0xd019 && data & 1) {
82
+ // only writing 1 to $d019 bit0 would acknowledge
83
+ cRSID_acknowledgeVICrasterIRQ(C64);
84
+ }
85
+ }
86
+ else {
87
+ // PSID-mode
88
+ switch (address) {
89
+ case 0xdc05:
90
+ case 0xdc04:
91
+ if (cRSID.TimerSource) {
92
+ // dynamic CIA-setting (Galway/Rubicon workaround)
93
+ cRSID.FrameCycles = C64.IObankWR[0xdc04] + (C64.IObankWR[0xdc05] << 8);
94
+ }
95
+ break;
96
+ case 0xdc08:
97
+ C64.IObankRD[0xdc08] = data;
98
+ break; // refresh TOD-clock
99
+ case 0xdc09:
100
+ C64.IObankRD[0xdc09] = data;
101
+ break; // refresh TOD-clock
102
+ case 0xd012:
103
+ // dynamic VIC IRQ-rasterline setting (Microprose Soccer V1 workaround)
104
+ if (C64.PrevRasterLine >= 0) {
105
+ // was $d012 set before? (or set only once?)
106
+ if (C64.IObankWR[0xd012] !== C64.PrevRasterLine) {
107
+ let Tmp = C64.IObankWR[0xd012] - C64.PrevRasterLine;
108
+ if (Tmp < 0)
109
+ Tmp += C64.VIC.RasterLines;
110
+ C64.FrameCycleCnt = cRSID.FrameCycles - Tmp * C64.VIC.RasterRowCycles;
111
+ }
112
+ }
113
+ C64.PrevRasterLine = C64.IObankWR[0xd012];
114
+ break;
115
+ }
116
+ }
117
+ }
118
+ }
119
+ // ---------------------------------------------------------------------------
120
+ // Addressing modes (CPU.c:117-166)
121
+ // ---------------------------------------------------------------------------
122
+ function addrModeImmediate(C64) {
123
+ ++C64.CPU.PC;
124
+ C64.CPU.Addr = C64.CPU.PC;
125
+ C64.CPU.Cycles = 2;
126
+ } // imm.
127
+ function addrModeZeropage(C64) {
128
+ ++C64.CPU.PC;
129
+ C64.CPU.Addr = rd(C64, C64.CPU.PC);
130
+ C64.CPU.Cycles = 3;
131
+ } // zp
132
+ function addrModeAbsolute(C64) {
133
+ ++C64.CPU.PC;
134
+ C64.CPU.Addr = rd(C64, C64.CPU.PC);
135
+ ++C64.CPU.PC;
136
+ C64.CPU.Addr += rd(C64, C64.CPU.PC) << 8;
137
+ C64.CPU.Cycles = 4;
138
+ } // abs
139
+ function addrModeZeropageXindexed(C64) {
140
+ ++C64.CPU.PC;
141
+ C64.CPU.Addr = (rd(C64, C64.CPU.PC) + C64.CPU.X) & 0xff;
142
+ C64.CPU.Cycles = 4;
143
+ } // zp,x (with zeropage-wraparound of 6502)
144
+ function addrModeZeropageYindexed(C64) {
145
+ ++C64.CPU.PC;
146
+ C64.CPU.Addr = (rd(C64, C64.CPU.PC) + C64.CPU.Y) & 0xff;
147
+ C64.CPU.Cycles = 4;
148
+ } // zp,y (with zeropage-wraparound of 6502)
149
+ function addrModeXindexed(C64) {
150
+ // abs,x (only STA is 5 cycles, others are 4 if page not crossed, RMW:7)
151
+ ++C64.CPU.PC;
152
+ C64.CPU.Addr = rd(C64, C64.CPU.PC) + C64.CPU.X;
153
+ ++C64.CPU.PC;
154
+ C64.CPU.SamePage = C64.CPU.Addr <= 0xff ? 1 : 0;
155
+ C64.CPU.Addr += rd(C64, C64.CPU.PC) << 8;
156
+ C64.CPU.Cycles = 5;
157
+ }
158
+ function addrModeYindexed(C64) {
159
+ // abs,y (only STA is 5 cycles, others are 4 if page not crossed, RMW:7)
160
+ ++C64.CPU.PC;
161
+ C64.CPU.Addr = rd(C64, C64.CPU.PC) + C64.CPU.Y;
162
+ ++C64.CPU.PC;
163
+ C64.CPU.SamePage = C64.CPU.Addr <= 0xff ? 1 : 0;
164
+ C64.CPU.Addr += rd(C64, C64.CPU.PC) << 8;
165
+ C64.CPU.Cycles = 5;
166
+ }
167
+ function addrModeIndirectYindexed(C64) {
168
+ // (zp),y (only STA is 6 cycles, others are 5 if page not crossed, RMW:8)
169
+ ++C64.CPU.PC;
170
+ C64.CPU.Addr = rd(C64, rd(C64, C64.CPU.PC)) + C64.CPU.Y;
171
+ C64.CPU.SamePage = C64.CPU.Addr <= 0xff ? 1 : 0;
172
+ C64.CPU.Addr += rd(C64, (rd(C64, C64.CPU.PC) + 1) & 0xff) << 8;
173
+ C64.CPU.Cycles = 6;
174
+ }
175
+ function addrModeXindexedIndirect(C64) {
176
+ // (zp,x)
177
+ ++C64.CPU.PC;
178
+ C64.CPU.Addr =
179
+ (rd(C64, rd(C64, C64.CPU.PC) + C64.CPU.X) & 0xff) + ((rd(C64, rd(C64, C64.CPU.PC) + C64.CPU.X + 1) & 0xff) << 8);
180
+ C64.CPU.Cycles = 6;
181
+ }
182
+ // ---------------------------------------------------------------------------
183
+ // Flag helpers (CPU.c:168-219)
184
+ // ---------------------------------------------------------------------------
185
+ function clrC(C64) {
186
+ C64.CPU.ST &= ~C;
187
+ } // clear Carry-flag
188
+ function setC(C64, expr) {
189
+ C64.CPU.ST &= ~C;
190
+ C64.CPU.ST |= expr !== 0 ? 1 : 0;
191
+ } // set Carry-flag if expression is not zero, else clear it
192
+ function clrNZC(C64) {
193
+ C64.CPU.ST &= ~(N | Z | C);
194
+ } // clear flags
195
+ export function clrNVZC(C64) {
196
+ C64.CPU.ST &= ~(N | V | Z | C);
197
+ } // clear flags
198
+ function setNZbyA(C64) {
199
+ C64.CPU.ST &= ~(N | Z);
200
+ C64.CPU.ST |= ((C64.CPU.A ? 0 : 1) << 1) | (C64.CPU.A & N);
201
+ } // set Negative-flag and Zero-flag based on result in Accumulator
202
+ function setNZbyT(C64, t) {
203
+ t &= 0xff;
204
+ C64.CPU.ST &= ~(N | Z);
205
+ C64.CPU.ST |= ((t ? 0 : 1) << 1) | (t & N);
206
+ }
207
+ function setNZbyX(C64) {
208
+ C64.CPU.ST &= ~(N | Z);
209
+ C64.CPU.ST |= ((C64.CPU.X ? 0 : 1) << 1) | (C64.CPU.X & N);
210
+ } // set Negative-flag and Zero-flag based on result in X-register
211
+ function setNZbyY(C64) {
212
+ C64.CPU.ST &= ~(N | Z);
213
+ C64.CPU.ST |= ((C64.CPU.Y ? 0 : 1) << 1) | (C64.CPU.Y & N);
214
+ } // set Negative-flag and Zero-flag based on result in Y-register
215
+ function setNZbyM(C64) {
216
+ const m = rd(C64, C64.CPU.Addr);
217
+ C64.CPU.ST &= ~(N | Z);
218
+ C64.CPU.ST |= ((m ? 0 : 1) << 1) | (m & N);
219
+ } // set Negative-flag and Zero-flag based on result at Memory-Address
220
+ function setNZCbyAdd(C64) {
221
+ C64.CPU.ST &= ~(N | Z | C);
222
+ C64.CPU.ST |= (C64.CPU.A & N) | (C64.CPU.A > 255 ? 1 : 0);
223
+ C64.CPU.A &= 0xff;
224
+ C64.CPU.ST |= (C64.CPU.A ? 0 : 1) << 1;
225
+ } // after increase/addition
226
+ function setVbyAdd(C64, m, t) {
227
+ C64.CPU.ST &= ~V;
228
+ C64.CPU.ST |= ((~(t ^ m)) & (t ^ C64.CPU.A) & N) >>> 1 & V;
229
+ } // calculate V-flag from A and T (previous A) and input2 (Memory)
230
+ function setNZCbySub(C64, t) {
231
+ C64.CPU.ST &= ~(N | Z | C);
232
+ C64.CPU.ST |= (t & N) | (t >= 0 ? 1 : 0);
233
+ C64.CPU.ST |= ((t & 0xff) === 0 ? 1 : 0) << 1;
234
+ }
235
+ function push(C64, value) {
236
+ C64.RAMbank[0x100 + C64.CPU.SP] = value;
237
+ --C64.CPU.SP;
238
+ C64.CPU.SP &= 0xff;
239
+ } // push a value to stack
240
+ function pop(C64) {
241
+ ++C64.CPU.SP;
242
+ C64.CPU.SP &= 0xff;
243
+ return C64.RAMbank[0x100 + C64.CPU.SP];
244
+ } // pop a value from stack
245
+ // ---------------------------------------------------------------------------
246
+ // cRSID_emulateCPU (CPU.c:223-459)
247
+ // ---------------------------------------------------------------------------
248
+ /** FlagSwitches / BranchFlags (CPU.c:228-229). */
249
+ const FlagSwitches = new Uint8Array([0x01, 0x21, 0x04, 0x24, 0x00, 0x40, 0x08, 0x28]);
250
+ const BranchFlags = new Uint8Array([0x80, 0x40, 0x01, 0x02]);
251
+ /** cRSID_emulateCPU (CPU.c:223-459) — the CPU emulation for SID/PRG playback
252
+ * (C ToDo comment: CIA/VIC-IRQ/NMI/RESET vectors, BCD-mode). Returns the
253
+ * instruction's cycle count, or 0xFF (init returns / idle) / 0xFE
254
+ * (RTI with empty stack / KERNAL IRQ-return substitute in PSID). */
255
+ export function cRSID_emulateCPU(C64) {
256
+ const PrevPC = C64.CPU.PC;
257
+ const IR = rd(C64, C64.CPU.PC);
258
+ C64.CPU.Cycles = 2;
259
+ C64.CPU.SamePage = 0; // 'Cycles': ensure smallest 6510 instruction runtime (for implied/register addressing-modes)
260
+ let T;
261
+ if (IR & 1) {
262
+ // nybble2: 1/5/9/D:accu.instructions, 3/7/B/F:illegal opcodes
263
+ switch ((IR & 0x1f) >> 1) {
264
+ // value-forming to cause jump-table //PC wraparound not handled inside to save codespace
265
+ case 0:
266
+ case 1:
267
+ addrModeXindexedIndirect(C64);
268
+ break; // (zp,x)
269
+ case 2:
270
+ case 3:
271
+ addrModeZeropage(C64);
272
+ break;
273
+ case 4:
274
+ case 5:
275
+ addrModeImmediate(C64);
276
+ break;
277
+ case 6:
278
+ case 7:
279
+ addrModeAbsolute(C64);
280
+ break;
281
+ case 8:
282
+ case 9:
283
+ addrModeIndirectYindexed(C64);
284
+ break; // (zp),y (5..6 cycles, 8 for R-M-W)
285
+ case 0xa:
286
+ addrModeZeropageXindexed(C64);
287
+ break; // zp,x
288
+ case 0xb:
289
+ if ((IR & 0xc0) !== 0x80)
290
+ addrModeZeropageXindexed(C64); // zp,x for illegal opcodes
291
+ else
292
+ addrModeZeropageYindexed(C64); // zp,y for LAX/SAX illegal opcodes
293
+ break;
294
+ case 0xc:
295
+ case 0xd:
296
+ addrModeYindexed(C64);
297
+ break;
298
+ case 0xe:
299
+ addrModeXindexed(C64);
300
+ break;
301
+ case 0xf:
302
+ if ((IR & 0xc0) !== 0x80)
303
+ addrModeXindexed(C64); // abs,x for illegal opcodes
304
+ else
305
+ addrModeYindexed(C64); // abs,y for LAX/SAX illegal opcodes
306
+ break;
307
+ }
308
+ C64.CPU.Addr &= 0xffff;
309
+ switch ((IR & 0xe0) >> 5) {
310
+ // value-forming to cause gapless case-values and faster jump-table creation from switch-case
311
+ case 0:
312
+ if ((IR & 0x1f) !== 0xb) {
313
+ // ORA / SLO(ASO)=ASL+ORA
314
+ if ((IR & 3) === 3) {
315
+ clrNZC(C64);
316
+ setC(C64, rd(C64, C64.CPU.Addr) >= N ? 1 : 0);
317
+ wr(C64, C64.CPU.Addr, rd(C64, C64.CPU.Addr) << 1);
318
+ C64.CPU.Cycles += 2;
319
+ } // for SLO
320
+ else
321
+ C64.CPU.Cycles -= C64.CPU.SamePage;
322
+ C64.CPU.A |= rd(C64, C64.CPU.Addr);
323
+ setNZbyA(C64); // ORA
324
+ }
325
+ else {
326
+ C64.CPU.A &= rd(C64, C64.CPU.Addr);
327
+ setNZbyA(C64);
328
+ setC(C64, C64.CPU.A >= N ? 1 : 0);
329
+ } // ANC (AND+Carry=bit7)
330
+ break;
331
+ case 1:
332
+ if ((IR & 0x1f) !== 0xb) {
333
+ // AND / RLA (ROL+AND)
334
+ if ((IR & 3) === 3) {
335
+ // for RLA
336
+ T = (rd(C64, C64.CPU.Addr) << 1) + (C64.CPU.ST & C);
337
+ clrNZC(C64);
338
+ setC(C64, T > 255 ? 1 : 0);
339
+ T &= 0xff;
340
+ wr(C64, C64.CPU.Addr, T);
341
+ C64.CPU.Cycles += 2;
342
+ }
343
+ else
344
+ C64.CPU.Cycles -= C64.CPU.SamePage;
345
+ C64.CPU.A &= rd(C64, C64.CPU.Addr);
346
+ setNZbyA(C64); // AND
347
+ }
348
+ else {
349
+ C64.CPU.A &= rd(C64, C64.CPU.Addr);
350
+ setNZbyA(C64);
351
+ setC(C64, C64.CPU.A >= N ? 1 : 0);
352
+ } // ANC (AND+Carry=bit7)
353
+ break;
354
+ case 2:
355
+ if ((IR & 0x1f) !== 0xb) {
356
+ // EOR / SRE(LSE)=LSR+EOR
357
+ if ((IR & 3) === 3) {
358
+ clrNZC(C64);
359
+ setC(C64, rd(C64, C64.CPU.Addr) & 1);
360
+ wr(C64, C64.CPU.Addr, rd(C64, C64.CPU.Addr) >> 1);
361
+ C64.CPU.Cycles += 2;
362
+ } // for SRE
363
+ else
364
+ C64.CPU.Cycles -= C64.CPU.SamePage;
365
+ C64.CPU.A ^= rd(C64, C64.CPU.Addr);
366
+ setNZbyA(C64); // EOR
367
+ }
368
+ else {
369
+ C64.CPU.A &= rd(C64, C64.CPU.Addr);
370
+ setC(C64, C64.CPU.A & 1);
371
+ C64.CPU.A >>= 1;
372
+ C64.CPU.A &= 0xff;
373
+ setNZbyA(C64);
374
+ } // ALR(ASR)=(AND+LSR)
375
+ break;
376
+ case 3:
377
+ if ((IR & 0x1f) !== 0xb) {
378
+ // RRA (ROR+ADC) / ADC
379
+ if ((IR & 3) === 3) {
380
+ // for RRA
381
+ T = (rd(C64, C64.CPU.Addr) >>> 1) + ((C64.CPU.ST & C) << 7);
382
+ clrNZC(C64);
383
+ setC(C64, T & 1);
384
+ wr(C64, C64.CPU.Addr, T);
385
+ C64.CPU.Cycles += 2;
386
+ }
387
+ else
388
+ C64.CPU.Cycles -= C64.CPU.SamePage;
389
+ T = C64.CPU.A;
390
+ C64.CPU.A += rd(C64, C64.CPU.Addr) + (C64.CPU.ST & C);
391
+ if ((C64.CPU.ST & D) !== 0 && (C64.CPU.A & 0xf) > 9) {
392
+ C64.CPU.A += 0x10;
393
+ C64.CPU.A &= 0xf0;
394
+ } // BCD?
395
+ setNZCbyAdd(C64);
396
+ setVbyAdd(C64, rd(C64, C64.CPU.Addr), T); // ADC
397
+ }
398
+ else {
399
+ // ARR (AND+ROR, bit0 not going to C, but C and bit7 get exchanged.)
400
+ C64.CPU.A &= rd(C64, C64.CPU.Addr);
401
+ T = C64.CPU.A;
402
+ C64.CPU.A = (C64.CPU.A >> 1) + ((C64.CPU.ST & C) << 7);
403
+ setC(C64, T >= N ? 1 : 0);
404
+ setNZbyA(C64);
405
+ C64.CPU.ST &= ~V;
406
+ C64.CPU.ST |= ((T & V) ^ (C64.CPU.A & V)) & V; // corrected: V is set accoring to whether rotate changes Accu bit 6: Tbit6^Abit6)
407
+ }
408
+ break;
409
+ case 4:
410
+ if ((IR & 0x1f) === 0xb) {
411
+ C64.CPU.A = (C64.CPU.X & rd(C64, C64.CPU.Addr)) & 0xff;
412
+ setNZbyA(C64); // XAA (TXA+AND), highly unstable on real 6502!
413
+ }
414
+ else if ((IR & 0x1f) === 0x1b) {
415
+ C64.CPU.SP = C64.CPU.A & C64.CPU.X;
416
+ wr(C64, C64.CPU.Addr, C64.CPU.SP & ((C64.CPU.Addr >> 8) + 1));
417
+ } // TAS(SHS) (SP=A&X, mem=S&H} - unstable on real 6502
418
+ else {
419
+ wr2(C64, C64.CPU.Addr, C64.CPU.A & ((IR & 3) === 3 ? C64.CPU.X : 0xff));
420
+ } // STA / SAX (at times same as AHX/SHX/SHY) (illegal)
421
+ break;
422
+ case 5:
423
+ if ((IR & 0x1f) !== 0x1b) {
424
+ C64.CPU.A = rd(C64, C64.CPU.Addr);
425
+ if ((IR & 3) === 3)
426
+ C64.CPU.X = C64.CPU.A & 0xff;
427
+ } // LDA / LAX (illegal, used by my 1 rasterline player) (LAX #imm is unstable on C64)
428
+ else {
429
+ C64.CPU.A = C64.CPU.X = C64.CPU.SP = rd(C64, C64.CPU.Addr) & C64.CPU.SP;
430
+ C64.CPU.X &= 0xff;
431
+ } // LAS(LAR)
432
+ setNZbyA(C64);
433
+ C64.CPU.Cycles -= C64.CPU.SamePage;
434
+ break;
435
+ case 6:
436
+ if ((IR & 0x1f) !== 0xb) {
437
+ // CMP / DCP(DEC+CMP)
438
+ if ((IR & 3) === 3) {
439
+ wr(C64, C64.CPU.Addr, rd(C64, C64.CPU.Addr) - 1);
440
+ C64.CPU.Cycles += 2;
441
+ } // DCP
442
+ else
443
+ C64.CPU.Cycles -= C64.CPU.SamePage;
444
+ T = C64.CPU.A - rd(C64, C64.CPU.Addr);
445
+ }
446
+ else {
447
+ C64.CPU.X = T = (C64.CPU.A & C64.CPU.X) - rd(C64, C64.CPU.Addr);
448
+ C64.CPU.X &= 0xff;
449
+ } // SBX(AXS) (CMP+DEX at the same time)
450
+ setNZCbySub(C64, T);
451
+ break;
452
+ case 7:
453
+ if ((IR & 3) === 3 && (IR & 0x1f) !== 0xb) {
454
+ wr(C64, C64.CPU.Addr, rd(C64, C64.CPU.Addr) + 1);
455
+ C64.CPU.Cycles += 2;
456
+ } // ISC(ISB)=INC+SBC / SBC
457
+ else
458
+ C64.CPU.Cycles -= C64.CPU.SamePage;
459
+ T = C64.CPU.A;
460
+ C64.CPU.A -= rd(C64, C64.CPU.Addr) + ((C64.CPU.ST & C) === 0 ? 1 : 0);
461
+ setNZCbySub(C64, C64.CPU.A);
462
+ C64.CPU.A &= 0xff;
463
+ setVbyAdd(C64, ~rd(C64, C64.CPU.Addr) & 0xff, T);
464
+ break;
465
+ }
466
+ }
467
+ else if (IR & 2) {
468
+ // nybble2: 2:illegal/LDX, 6:A/X/INC/DEC, A:Accu-shift/reg.transfer/NOP, E:shift/X/INC/DEC
469
+ switch (IR & 0x1f) {
470
+ // Addressing modes
471
+ case 2:
472
+ addrModeImmediate(C64);
473
+ break;
474
+ case 6:
475
+ addrModeZeropage(C64);
476
+ break;
477
+ case 0xe:
478
+ addrModeAbsolute(C64);
479
+ break;
480
+ case 0x16:
481
+ if ((IR & 0xc0) !== 0x80)
482
+ addrModeZeropageXindexed(C64); // zp,x
483
+ else
484
+ addrModeZeropageYindexed(C64); // zp,y
485
+ break;
486
+ case 0x1e:
487
+ if ((IR & 0xc0) !== 0x80)
488
+ addrModeXindexed(C64); // abs,x
489
+ else
490
+ addrModeYindexed(C64); // abs,y
491
+ break;
492
+ }
493
+ C64.CPU.Addr &= 0xffff;
494
+ switch ((IR & 0xe0) >> 5) {
495
+ case 0: {
496
+ clrC(C64); // clear C for ASL //the rest of case 0 and 1 are identical but newer GCC gave notifications about 'fallthrough', so duplicated it
497
+ if ((IR & 0xf) === 0xa) {
498
+ C64.CPU.A = (C64.CPU.A << 1) + (C64.CPU.ST & C);
499
+ setNZCbyAdd(C64);
500
+ } // ASL/ROL (Accu)
501
+ else {
502
+ T = (rd(C64, C64.CPU.Addr) << 1) + (C64.CPU.ST & C);
503
+ setC(C64, T > 255 ? 1 : 0);
504
+ setNZbyT(C64, T);
505
+ wr(C64, C64.CPU.Addr, T);
506
+ C64.CPU.Cycles += 2;
507
+ } // RMW (Read-Write-Modify)
508
+ break;
509
+ }
510
+ case 1:
511
+ if ((IR & 0xf) === 0xa) {
512
+ C64.CPU.A = (C64.CPU.A << 1) + (C64.CPU.ST & C);
513
+ setNZCbyAdd(C64);
514
+ } // ASL/ROL (Accu)
515
+ else {
516
+ T = (rd(C64, C64.CPU.Addr) << 1) + (C64.CPU.ST & C);
517
+ setC(C64, T > 255 ? 1 : 0);
518
+ setNZbyT(C64, T);
519
+ wr(C64, C64.CPU.Addr, T);
520
+ C64.CPU.Cycles += 2;
521
+ } // RMW (Read-Write-Modify)
522
+ break;
523
+ case 2: {
524
+ clrC(C64); // clear C for LSR //the rest of case 2 and 3 are identical but newer GCC gave notifications about 'fallthrough', so duplicated it
525
+ if ((IR & 0xf) === 0xa) {
526
+ T = C64.CPU.A;
527
+ C64.CPU.A = (C64.CPU.A >> 1) + ((C64.CPU.ST & C) << 7);
528
+ setC(C64, T & 1);
529
+ C64.CPU.A &= 0xff;
530
+ setNZbyA(C64);
531
+ } // LSR/ROR (Accu)
532
+ else {
533
+ T = (rd(C64, C64.CPU.Addr) >>> 1) + ((C64.CPU.ST & C) << 7);
534
+ setC(C64, rd(C64, C64.CPU.Addr) & 1);
535
+ setNZbyT(C64, T);
536
+ wr(C64, C64.CPU.Addr, T);
537
+ C64.CPU.Cycles += 2;
538
+ } // memory (RMW)
539
+ break;
540
+ }
541
+ case 3:
542
+ if ((IR & 0xf) === 0xa) {
543
+ T = C64.CPU.A;
544
+ C64.CPU.A = (C64.CPU.A >> 1) + ((C64.CPU.ST & C) << 7);
545
+ setC(C64, T & 1);
546
+ C64.CPU.A &= 0xff;
547
+ setNZbyA(C64);
548
+ } // LSR/ROR (Accu)
549
+ else {
550
+ T = (rd(C64, C64.CPU.Addr) >>> 1) + ((C64.CPU.ST & C) << 7);
551
+ setC(C64, rd(C64, C64.CPU.Addr) & 1);
552
+ setNZbyT(C64, T);
553
+ wr(C64, C64.CPU.Addr, T);
554
+ C64.CPU.Cycles += 2;
555
+ } // memory (RMW)
556
+ break;
557
+ case 4:
558
+ if (IR & 4) {
559
+ wr2(C64, C64.CPU.Addr, C64.CPU.X);
560
+ } // STX
561
+ else if (IR & 0x10)
562
+ C64.CPU.SP = C64.CPU.X; // TXS
563
+ else {
564
+ C64.CPU.A = C64.CPU.X;
565
+ setNZbyA(C64);
566
+ } // TXA
567
+ break;
568
+ case 5:
569
+ if ((IR & 0xf) !== 0xa) {
570
+ C64.CPU.X = rd(C64, C64.CPU.Addr) & 0xff;
571
+ C64.CPU.Cycles -= C64.CPU.SamePage;
572
+ } // LDX
573
+ else if (IR & 0x10)
574
+ C64.CPU.X = C64.CPU.SP; // TSX
575
+ else
576
+ C64.CPU.X = C64.CPU.A; // TAX
577
+ setNZbyX(C64);
578
+ break;
579
+ case 6:
580
+ if (IR & 4) {
581
+ wr(C64, C64.CPU.Addr, rd(C64, C64.CPU.Addr) - 1);
582
+ setNZbyM(C64);
583
+ C64.CPU.Cycles += 2;
584
+ } // DEC
585
+ else {
586
+ C64.CPU.X = (C64.CPU.X - 1) & 0xff;
587
+ setNZbyX(C64);
588
+ } // DEX
589
+ break;
590
+ case 7:
591
+ if (IR & 4) {
592
+ wr(C64, C64.CPU.Addr, rd(C64, C64.CPU.Addr) + 1);
593
+ setNZbyM(C64);
594
+ C64.CPU.Cycles += 2;
595
+ } // INC/NOP
596
+ break;
597
+ }
598
+ }
599
+ else if ((IR & 0xc) === 8) {
600
+ // nybble2: 8:register/statusflag
601
+ if (IR & 0x10) {
602
+ if (IR === 0x98) {
603
+ C64.CPU.A = C64.CPU.Y;
604
+ setNZbyA(C64);
605
+ } // TYA
606
+ else {
607
+ // CLC/SEC/CLI/SEI/CLV/CLD/SED
608
+ if (FlagSwitches[IR >> 5] & 0x20)
609
+ C64.CPU.ST |= FlagSwitches[IR >> 5] & 0xdf;
610
+ else
611
+ C64.CPU.ST &= ~(FlagSwitches[IR >> 5] & 0xdf);
612
+ }
613
+ }
614
+ else {
615
+ switch ((IR & 0xf0) >> 5) {
616
+ case 0:
617
+ push(C64, C64.CPU.ST);
618
+ C64.CPU.Cycles = 3;
619
+ break; // PHP
620
+ case 1:
621
+ C64.CPU.ST = pop(C64);
622
+ C64.CPU.Cycles = 4;
623
+ break; // PLP
624
+ case 2:
625
+ push(C64, C64.CPU.A);
626
+ C64.CPU.Cycles = 3;
627
+ break; // PHA
628
+ case 3:
629
+ C64.CPU.A = pop(C64);
630
+ setNZbyA(C64);
631
+ C64.CPU.Cycles = 4;
632
+ break; // PLA
633
+ case 4:
634
+ C64.CPU.Y = (C64.CPU.Y - 1) & 0xff;
635
+ setNZbyY(C64);
636
+ break; // DEY
637
+ case 5:
638
+ C64.CPU.Y = C64.CPU.A;
639
+ setNZbyY(C64);
640
+ break; // TAY
641
+ case 6:
642
+ C64.CPU.Y = (C64.CPU.Y + 1) & 0xff;
643
+ setNZbyY(C64);
644
+ break; // INY
645
+ case 7:
646
+ C64.CPU.X = (C64.CPU.X + 1) & 0xff;
647
+ setNZbyX(C64);
648
+ break; // INX
649
+ }
650
+ }
651
+ }
652
+ else {
653
+ // nybble2: 0: control/branch/Y/compare 4: Y/compare C:Y/compare/JMP
654
+ if ((IR & 0x1f) === 0x10) {
655
+ // BPL/BMI/BVC/BVS/BCC/BCS/BNE/BEQ relative branch
656
+ ++C64.CPU.PC;
657
+ T = rd(C64, C64.CPU.PC);
658
+ if (T & 0x80)
659
+ T -= 0x100;
660
+ if (IR & 0x20) {
661
+ if (C64.CPU.ST & BranchFlags[IR >> 6]) {
662
+ C64.CPU.PC += T;
663
+ C64.CPU.Cycles = 3;
664
+ }
665
+ }
666
+ else {
667
+ if (!(C64.CPU.ST & BranchFlags[IR >> 6])) {
668
+ C64.CPU.PC += T;
669
+ C64.CPU.Cycles = 3;
670
+ } // plus 1 cycle if page is crossed?
671
+ }
672
+ }
673
+ else {
674
+ // nybble2: 0:Y/control/Y/compare 4:Y/compare C:Y/compare/JMP
675
+ switch (IR & 0x1f) {
676
+ // Addressing modes
677
+ case 0:
678
+ addrModeImmediate(C64);
679
+ break; // imm. (or abs.low for JSR/BRK)
680
+ case 4:
681
+ addrModeZeropage(C64);
682
+ break;
683
+ case 0xc:
684
+ addrModeAbsolute(C64);
685
+ break;
686
+ case 0x14:
687
+ addrModeZeropageXindexed(C64);
688
+ break; // zp,x
689
+ case 0x1c:
690
+ addrModeXindexed(C64);
691
+ break; // abs,x
692
+ }
693
+ C64.CPU.Addr &= 0xffff;
694
+ switch ((IR & 0xe0) >> 5) {
695
+ case 0:
696
+ if (!(IR & 4)) {
697
+ // BRK / NOP-absolute/abs,x/zp/zp,x
698
+ push(C64, (C64.CPU.PC + 2 - 1) >> 8);
699
+ push(C64, (C64.CPU.PC + 2 - 1) & 0xff);
700
+ push(C64, C64.CPU.ST | B);
701
+ C64.CPU.ST |= I; // BRK
702
+ C64.CPU.PC = rd(C64, 0xfffe) + (rd(C64, 0xffff) << 8) - 1;
703
+ C64.CPU.Cycles = 7;
704
+ }
705
+ else if (IR === 0x1c)
706
+ C64.CPU.Cycles -= C64.CPU.SamePage; // NOP abs,x
707
+ break;
708
+ case 1:
709
+ if (IR & 0xf) {
710
+ // BIT / NOP-abs,x/zp,x
711
+ if (!(IR & 0x10)) {
712
+ C64.CPU.ST &= 0x3d;
713
+ C64.CPU.ST |= (rd(C64, C64.CPU.Addr) & 0xc0) | ((C64.CPU.A & rd(C64, C64.CPU.Addr) ? 0 : 1) << 1);
714
+ } // BIT
715
+ else if (IR === 0x3c)
716
+ C64.CPU.Cycles -= C64.CPU.SamePage; // NOP abs,x
717
+ }
718
+ else {
719
+ // JSR
720
+ push(C64, (C64.CPU.PC + 2 - 1) >> 8);
721
+ push(C64, (C64.CPU.PC + 2 - 1) & 0xff);
722
+ C64.CPU.PC = rd(C64, C64.CPU.Addr) + rd(C64, C64.CPU.Addr + 1) * 256 - 1;
723
+ C64.CPU.Cycles = 6;
724
+ }
725
+ break;
726
+ case 2:
727
+ if (IR & 0xf) {
728
+ // JMP / NOP-abs,x/zp/zp,x
729
+ if (IR === 0x4c) {
730
+ // JMP
731
+ C64.CPU.PC =
732
+ C64.RealSIDmode && C64.NMI && (PrevPC === 0xdc02 || PrevPC === 0xdc04)
733
+ ? (0x800 + (C64.CPU.Addr & 0xff)) - 1 // Workaround: Hi_Fi_Sky.sid/WonderLand-XII/Hunters_Moon/File_Deleted/Storebror.sid and the like needs cycle/subcycle-exact emulation to work well. This value is OK (though jittery) for most tunes (as WebSID proved).
734
+ : C64.CPU.Addr - 1;
735
+ C64.CPU.Cycles = 3;
736
+ rd(C64, C64.CPU.Addr + 1); // a read from jump-address highbyte to acknowledge CIA-IRQ is used in some tunes with 'jmp DC0C' or 'jmp DD0C' (e.g. Wonderland_XIII_tune_1.sid or Hi_Fi_Sky.sid)
737
+ // if (C64.CPU.Addr==PrevPC) {storeReg(); C64.Returned=1; return 0xFF;} //turn self-jump mainloop (after init) into idle time
738
+ }
739
+ else if (IR === 0x5c)
740
+ C64.CPU.Cycles -= C64.CPU.SamePage; // NOP abs,x
741
+ }
742
+ else {
743
+ // RTI
744
+ C64.CPU.ST = pop(C64);
745
+ T = pop(C64);
746
+ C64.CPU.PC = ((pop(C64) << 8) + T - 1) >>> 0;
747
+ C64.CPU.Cycles = 6;
748
+ if (C64.Returned && C64.CPU.SP >= 0xff) {
749
+ ++C64.CPU.PC;
750
+ return 0xfe;
751
+ }
752
+ }
753
+ break;
754
+ case 3:
755
+ if (IR & 0xf) {
756
+ // JMP() (indirect) / NOP-abs,x/zp/zp,x
757
+ if (IR === 0x6c) {
758
+ // JMP() (indirect)
759
+ C64.CPU.PC = rd(C64, (C64.CPU.Addr & 0xff00) + ((C64.CPU.Addr + 1) & 0xff)); // (with highbyte-wraparound bug)
760
+ C64.CPU.PC = ((C64.CPU.PC << 8) + rd(C64, C64.CPU.Addr) - 1) >>> 0;
761
+ C64.CPU.Cycles = 5;
762
+ }
763
+ else if (IR === 0x7c)
764
+ C64.CPU.Cycles -= C64.CPU.SamePage; // NOP abs,x
765
+ }
766
+ else {
767
+ // RTS
768
+ if (C64.CPU.SP >= 0xff) {
769
+ /*storeReg();*/
770
+ C64.Returned = 1;
771
+ return 0xff; // Init returns, provide idle-time between IRQs
772
+ }
773
+ T = pop(C64);
774
+ C64.CPU.PC = (pop(C64) << 8) + T;
775
+ C64.CPU.Cycles = 6;
776
+ }
777
+ break;
778
+ case 4:
779
+ if (IR & 4) {
780
+ wr2(C64, C64.CPU.Addr, C64.CPU.Y);
781
+ } // STY / NOP #imm
782
+ break;
783
+ case 5:
784
+ C64.CPU.Y = rd(C64, C64.CPU.Addr) & 0xff;
785
+ setNZbyY(C64);
786
+ C64.CPU.Cycles -= C64.CPU.SamePage; // LDY
787
+ break;
788
+ case 6:
789
+ if (!(IR & 0x10)) {
790
+ // CPY / NOP abs,x/zp,x
791
+ T = C64.CPU.Y - rd(C64, C64.CPU.Addr);
792
+ setNZCbySub(C64, T); // CPY
793
+ }
794
+ else if (IR === 0xdc)
795
+ C64.CPU.Cycles -= C64.CPU.SamePage; // NOP abs,x
796
+ break;
797
+ case 7:
798
+ if (!(IR & 0x10)) {
799
+ // CPX / NOP abs,x/zp,x
800
+ T = C64.CPU.X - rd(C64, C64.CPU.Addr);
801
+ setNZCbySub(C64, T); // CPX
802
+ }
803
+ else if (IR === 0xfc)
804
+ C64.CPU.Cycles -= C64.CPU.SamePage; // NOP abs,x
805
+ break;
806
+ }
807
+ }
808
+ }
809
+ ++C64.CPU.PC; // PC&=0xFFFF;
810
+ // storeReg();
811
+ if (!C64.RealSIDmode) {
812
+ // substitute KERNAL IRQ-return in PSID (e.g. Microprose Soccer)
813
+ if ((C64.RAMbank[1] & 3) > 1 && PrevPC < 0xe000 && (C64.CPU.PC === 0xea31 || C64.CPU.PC === 0xea81 || C64.CPU.PC === 0xea7e)) {
814
+ return 0xfe;
815
+ }
816
+ }
817
+ return C64.CPU.Cycles;
818
+ }
819
+ // ---------------------------------------------------------------------------
820
+ // cRSID_handleCPUinterrupts (CPU.c:466-495)
821
+ // ---------------------------------------------------------------------------
822
+ /** handle entering into IRQ and NMI interrupt (CPU.c:466-495). */
823
+ export function cRSID_handleCPUinterrupts(C64) {
824
+ if (C64.NMI > C64.CPU.PrevNMI) {
825
+ // if IRQ and NMI at the same time, NMI is serviced first (or is it?!)
826
+ // C64.CPU.ST &= ~B;
827
+ push(C64, C64.CPU.PC >> 8);
828
+ push(C64, C64.CPU.PC & 0xff);
829
+ push(C64, C64.CPU.ST);
830
+ C64.CPU.ST |= I;
831
+ C64.CPU.PC = cRSID_readMemPtr(C64, 0xfffa) + (cRSID_readMemPtr(C64, 0xfffb) << 8); // NMI-vector
832
+ C64.CPU.PrevNMI = C64.NMI;
833
+ return 1;
834
+ }
835
+ else if (C64.IRQ && !(C64.CPU.ST & I)) {
836
+ // C64.CPU.ST &= ~B;
837
+ push(C64, C64.CPU.PC >> 8);
838
+ push(C64, C64.CPU.PC & 0xff);
839
+ push(C64, C64.CPU.ST);
840
+ C64.CPU.ST |= I;
841
+ C64.CPU.PC = cRSID_readMemPtr(C64, 0xfffe) + (cRSID_readMemPtr(C64, 0xffff) << 8); // maskable IRQ-vector
842
+ C64.CPU.PrevNMI = C64.NMI;
843
+ return 1;
844
+ }
845
+ C64.CPU.PrevNMI = C64.NMI; // prepare for NMI edge-detection
846
+ return 0;
847
+ }
848
+ //# sourceMappingURL=cpu.js.map