@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.
- package/README.md +111 -2
- package/dist/c64.d.ts +8 -0
- package/dist/c64.d.ts.map +1 -0
- package/dist/c64.js +325 -0
- package/dist/c64.js.map +1 -0
- package/dist/c64types.d.ts +209 -0
- package/dist/c64types.d.ts.map +1 -0
- package/dist/c64types.js +181 -0
- package/dist/c64types.js.map +1 -0
- package/dist/cia.d.ts +19 -0
- package/dist/cia.d.ts.map +1 -0
- package/dist/cia.js +160 -0
- package/dist/cia.js.map +1 -0
- package/dist/combiwaves.d.ts +9 -0
- package/dist/combiwaves.d.ts.map +1 -0
- package/dist/combiwaves.js +533 -0
- package/dist/combiwaves.js.map +1 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +23 -0
- package/dist/config.js.map +1 -0
- package/dist/cpu.d.ts +12 -0
- package/dist/cpu.d.ts.map +1 -0
- package/dist/cpu.js +848 -0
- package/dist/cpu.js.map +1 -0
- package/dist/filtercurves.d.ts +13 -0
- package/dist/filtercurves.d.ts.map +1 -0
- package/dist/filtercurves.js +546 -0
- package/dist/filtercurves.js.map +1 -0
- package/dist/host.d.ts +4 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +14 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/instance.d.ts +119 -0
- package/dist/instance.d.ts.map +1 -0
- package/dist/instance.js +51 -0
- package/dist/instance.js.map +1 -0
- package/dist/loader.d.ts +26 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +383 -0
- package/dist/loader.js.map +1 -0
- package/dist/mem.d.ts +30 -0
- package/dist/mem.d.ts.map +1 -0
- package/dist/mem.js +187 -0
- package/dist/mem.js.map +1 -0
- package/dist/psiddigi.d.ts +3 -0
- package/dist/psiddigi.d.ts.map +1 -0
- package/dist/psiddigi.js +73 -0
- package/dist/psiddigi.js.map +1 -0
- package/dist/settings.d.ts +41 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +64 -0
- package/dist/settings.js.map +1 -0
- package/dist/sid.d.ts +21 -0
- package/dist/sid.d.ts.map +1 -0
- package/dist/sid.js +78 -0
- package/dist/sid.js.map +1 -0
- package/dist/sidadsr.d.ts +9 -0
- package/dist/sidadsr.d.ts.map +1 -0
- package/dist/sidadsr.js +105 -0
- package/dist/sidadsr.js.map +1 -0
- package/dist/sidoscwaves.d.ts +15 -0
- package/dist/sidoscwaves.d.ts.map +1 -0
- package/dist/sidoscwaves.js +475 -0
- package/dist/sidoscwaves.js.map +1 -0
- package/dist/sidoutputs.d.ts +17 -0
- package/dist/sidoutputs.d.ts.map +1 -0
- package/dist/sidoutputs.js +185 -0
- package/dist/sidoutputs.js.map +1 -0
- package/dist/sidplugin.d.ts +18 -0
- package/dist/sidplugin.d.ts.map +1 -0
- package/dist/sidplugin.js +174 -0
- package/dist/sidplugin.js.map +1 -0
- package/dist/sidrouting.d.ts +8 -0
- package/dist/sidrouting.d.ts.map +1 -0
- package/dist/sidrouting.js +560 -0
- package/dist/sidrouting.js.map +1 -0
- package/dist/sidseek.d.ts +8 -0
- package/dist/sidseek.d.ts.map +1 -0
- package/dist/sidseek.js +60 -0
- package/dist/sidseek.js.map +1 -0
- package/dist/sincwindow.d.ts +3 -0
- package/dist/sincwindow.d.ts.map +1 -0
- package/dist/sincwindow.js +78 -0
- package/dist/sincwindow.js.map +1 -0
- package/dist/songlengths.d.ts +20 -0
- package/dist/songlengths.d.ts.map +1 -0
- package/dist/songlengths.js +81 -0
- package/dist/songlengths.js.map +1 -0
- package/dist/vic.d.ts +14 -0
- package/dist/vic.d.ts.map +1 -0
- package/dist/vic.js +63 -0
- package/dist/vic.js.map +1 -0
- 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
|