diff options
Diffstat (limited to 'lib/rbcodec/codecs/cRSID/C64/CPU.c')
-rw-r--r-- | lib/rbcodec/codecs/cRSID/C64/CPU.c | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/cRSID/C64/CPU.c b/lib/rbcodec/codecs/cRSID/C64/CPU.c new file mode 100644 index 0000000000..692e666f8a --- /dev/null +++ b/lib/rbcodec/codecs/cRSID/C64/CPU.c | |||
@@ -0,0 +1,423 @@ | |||
1 | |||
2 | //cRSID CPU-emulation | ||
3 | |||
4 | |||
5 | void cRSID_initCPU (cRSID_CPUinstance* CPU, unsigned short mempos) { | ||
6 | CPU->PC = mempos; CPU->A = 0; CPU->X = 0; CPU->Y = 0; CPU->ST = 0x04; CPU->SP = 0xFF; CPU->PrevNMI = 0; | ||
7 | } | ||
8 | |||
9 | |||
10 | unsigned char cRSID_emulateCPU (void) { //the CPU emulation for SID/PRG playback (ToDo: CIA/VIC-IRQ/NMI/RESET vectors, BCD-mode) | ||
11 | |||
12 | enum StatusFlagBitValues { N=0x80, V=0x40, B=0x10, D=0x08, I=0x04, Z=0x02, C=0x01 }; | ||
13 | |||
14 | static const unsigned char FlagSwitches[]={0x01,0x21,0x04,0x24,0x00,0x40,0x08,0x28}, BranchFlags[]={0x80,0x40,0x01,0x02}; | ||
15 | |||
16 | |||
17 | static cRSID_C64instance* const C64 = &cRSID_C64; //could be a parameter but function-call is faster this way if only 1 main CPU exists | ||
18 | static cRSID_CPUinstance* const CPU = &C64->CPU; | ||
19 | |||
20 | static char Cycles, SamePage; | ||
21 | static unsigned char IR, ST, X, Y; | ||
22 | static short int A, SP, T; | ||
23 | static unsigned int PC, Addr, PrevPC; | ||
24 | |||
25 | |||
26 | inline void loadReg (void) { PC = CPU->PC; SP = CPU->SP; ST = CPU->ST; A = CPU->A; X = CPU->X; Y = CPU->Y; } | ||
27 | inline void storeReg (void) { CPU->PC = PC; CPU->SP = SP; CPU->ST = ST; CPU->A = A; CPU->X = X; CPU->Y = Y; } | ||
28 | |||
29 | inline unsigned char rd (register unsigned short address) { | ||
30 | static unsigned char value; | ||
31 | value = *cRSID_getMemReadPtr(address); | ||
32 | if (C64->RealSIDmode) { | ||
33 | if ( C64->RAMbank[1] & 3 ) { | ||
34 | if (address==0xDC0D) { cRSID_acknowledgeCIAIRQ( &C64->CIA[1] ); } | ||
35 | else if (address==0xDD0D) { cRSID_acknowledgeCIAIRQ( &C64->CIA[2] ); } | ||
36 | } | ||
37 | } | ||
38 | return value; | ||
39 | } | ||
40 | |||
41 | inline void wr (register unsigned short address, register unsigned char data) { | ||
42 | *cRSID_getMemWritePtr(address)=data; | ||
43 | if ( C64->RealSIDmode && (C64->RAMbank[1] & 3) ) { | ||
44 | //if(data&1) { //only writing 1 to $d019 bit0 would acknowledge, not any value (but RMW instructions write $d019 back before mod.) | ||
45 | if (address==0xD019) { cRSID_acknowledgeVICrasterIRQ( &C64->VIC ); } | ||
46 | //} | ||
47 | } | ||
48 | } | ||
49 | |||
50 | inline void wr2 (register unsigned short address, register unsigned char data) { //PSID-hack specific memory-write | ||
51 | static int Tmp; | ||
52 | *cRSID_getMemWritePtr(address)=data; | ||
53 | if ( C64->RAMbank[1] & 3 ) { | ||
54 | if (C64->RealSIDmode) { | ||
55 | if (address==0xDC0D) cRSID_writeCIAIRQmask( &C64->CIA[1], data ); | ||
56 | else if (address==0xDD0D) cRSID_writeCIAIRQmask( &C64->CIA[2], data ); | ||
57 | else if (address==0xDD0C) C64->IObankRD[address]=data; //mirror WR to RD (e.g. Wonderland_XIII_tune_1.sid) | ||
58 | //#ifdef CRSID_PLATFORM_PC //just for info displayer | ||
59 | // else if (address==0xDC05 || address==0xDC04) C64->FrameCycles = ( (C64->IObankWR[0xDC04] + (C64->IObankWR[0xDC05]<<8)) ); | ||
60 | //#endif | ||
61 | else if(address==0xD019 && data&1) { //only writing 1 to $d019 bit0 would acknowledge | ||
62 | cRSID_acknowledgeVICrasterIRQ( &C64->VIC ); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | else { | ||
67 | switch (address) { | ||
68 | case 0xDC05: case 0xDC04: | ||
69 | if (C64->TimerSource ) { //dynamic CIA-setting (Galway/Rubicon workaround) | ||
70 | C64->FrameCycles = ( (C64->IObankWR[0xDC04] + (C64->IObankWR[0xDC05]<<8)) ); //<< 4) / C64->SampleClockRatio; | ||
71 | } | ||
72 | break; | ||
73 | case 0xDC08: C64->IObankRD[0xDC08] = data; break; //refresh TOD-clock | ||
74 | case 0xDC09: C64->IObankRD[0xDC09] = data; break; //refresh TOD-clock | ||
75 | case 0xD012: //dynamic VIC IRQ-rasterline setting (Microprose Soccer V1 workaround) | ||
76 | if (C64->PrevRasterLine>=0) { //was $d012 set before? (or set only once?) | ||
77 | if (C64->IObankWR[0xD012] != C64->PrevRasterLine) { | ||
78 | Tmp = C64->IObankWR[0xD012] - C64->PrevRasterLine; | ||
79 | if (Tmp<0) Tmp += C64->VIC.RasterLines; | ||
80 | C64->FrameCycleCnt = C64->FrameCycles - Tmp * C64->VIC.RasterRowCycles; | ||
81 | } | ||
82 | } | ||
83 | C64->PrevRasterLine = C64->IObankWR[0xD012]; | ||
84 | break; | ||
85 | } | ||
86 | } | ||
87 | |||
88 | } | ||
89 | } | ||
90 | |||
91 | |||
92 | inline void addrModeImmediate (void) { ++PC; Addr=PC; Cycles=2; } //imm. | ||
93 | inline void addrModeZeropage (void) { ++PC; Addr=rd(PC); Cycles=3; } //zp | ||
94 | inline void addrModeAbsolute (void) { ++PC; Addr = rd(PC); ++PC; Addr += rd(PC)<<8; Cycles=4; } //abs | ||
95 | inline void addrModeZeropageXindexed (void) { ++PC; Addr = (rd(PC) + X) & 0xFF; Cycles=4; } //zp,x (with zeropage-wraparound of 6502) | ||
96 | inline void addrModeZeropageYindexed (void) { ++PC; Addr = (rd(PC) + Y) & 0xFF; Cycles=4; } //zp,y (with zeropage-wraparound of 6502) | ||
97 | |||
98 | inline void addrModeXindexed (void) { // abs,x (only STA is 5 cycles, others are 4 if page not crossed, RMW:7) | ||
99 | ++PC; Addr = rd(PC) + X; ++PC; SamePage = (Addr <= 0xFF); Addr += rd(PC)<<8; Cycles=5; | ||
100 | } | ||
101 | |||
102 | inline void addrModeYindexed (void) { // abs,y (only STA is 5 cycles, others are 4 if page not crossed, RMW:7) | ||
103 | ++PC; Addr = rd(PC) + Y; ++PC; SamePage = (Addr <= 0xFF); Addr += rd(PC)<<8; Cycles=5; | ||
104 | } | ||
105 | |||
106 | inline void addrModeIndirectYindexed (void) { // (zp),y (only STA is 6 cycles, others are 5 if page not crossed, RMW:8) | ||
107 | ++PC; Addr = rd(rd(PC)) + Y; SamePage = (Addr <= 0xFF); Addr += rd( (rd(PC)+1)&0xFF ) << 8; Cycles=6; | ||
108 | } | ||
109 | |||
110 | inline void addrModeXindexedIndirect (void) { // (zp,x) | ||
111 | ++PC; Addr = ( rd(rd(PC)+X)&0xFF ) + ( ( rd(rd(PC)+X+1)&0xFF ) << 8 ); Cycles=6; | ||
112 | } | ||
113 | |||
114 | |||
115 | inline void clrC (void) { ST &= ~C; } //clear Carry-flag | ||
116 | inline void setC (unsigned char expr) { ST &= ~C; ST |= (expr!=0); } //set Carry-flag if expression is not zero | ||
117 | inline void clrNZC (void) { ST &= ~(N|Z|C); } //clear flags | ||
118 | inline void clrNVZC (void) { ST &= ~(N|V|Z|C); } //clear flags | ||
119 | inline void setNZbyA (void) { ST &= ~(N|Z); ST |= ((!A)<<1) | (A&N); } //set Negative-flag and Zero-flag based on result in Accumulator | ||
120 | inline void setNZbyT (void) { T&=0xFF; ST &= ~(N|Z); ST |= ((!T)<<1) | (T&N); } | ||
121 | inline void setNZbyX (void) { ST &= ~(N|Z); ST |= ((!X)<<1) | (X&N); } //set Negative-flag and Zero-flag based on result in X-register | ||
122 | inline void setNZbyY (void) { ST &= ~(N|Z); ST |= ((!Y)<<1) | (Y&N); } //set Negative-flag and Zero-flag based on result in Y-register | ||
123 | inline void setNZbyM (void) { ST &= ~(N|Z); ST |= ((!rd(Addr))<<1) | (rd(Addr)&N); } //set Negative-flag and Zero-flag based on result at Memory-Address | ||
124 | inline void setNZCbyAdd (void) { ST &= ~(N|Z|C); ST |= (A&N)|(A>255); A&=0xFF; ST|=(!A)<<1; } //after increase/addition | ||
125 | inline void setVbyAdd (unsigned char M) { ST &= ~V; ST |= ( (~(T^M)) & (T^A) & N ) >> 1; } //calculate V-flag from A and T (previous A) and input2 (Memory) | ||
126 | inline void setNZCbySub (signed short* obj) { ST &= ~(N|Z|C); ST |= (*obj&N) | (*obj>=0); *obj&=0xFF; ST |= ((!(*obj))<<1); } | ||
127 | |||
128 | inline void push (unsigned char value) { C64->RAMbank[0x100+SP] = value; --SP; SP&=0xFF; } //push a value to stack | ||
129 | inline unsigned char pop (void) { ++SP; SP&=0xFF; return C64->RAMbank[0x100+SP]; } //pop a value from stack | ||
130 | |||
131 | |||
132 | loadReg(); PrevPC=PC; | ||
133 | IR = rd(PC); Cycles=2; SamePage=0; //'Cycles': ensure smallest 6510 runtime (for implied/register instructions) | ||
134 | |||
135 | |||
136 | if(IR&1) { //nybble2: 1/5/9/D:accu.instructions, 3/7/B/F:illegal opcodes | ||
137 | |||
138 | switch ( (IR & 0x1F) >> 1 ) { //value-forming to cause jump-table //PC wraparound not handled inside to save codespace | ||
139 | case 0: case 1: addrModeXindexedIndirect(); break; //(zp,x) | ||
140 | case 2: case 3: addrModeZeropage(); break; | ||
141 | case 4: case 5: addrModeImmediate(); break; | ||
142 | case 6: case 7: addrModeAbsolute(); break; | ||
143 | case 8: case 9: addrModeIndirectYindexed(); break; //(zp),y (5..6 cycles, 8 for R-M-W) | ||
144 | case 0xA: addrModeZeropageXindexed(); break; //zp,x | ||
145 | case 0xB: if ((IR&0xC0)!=0x80) addrModeZeropageXindexed(); //zp,x for illegal opcodes | ||
146 | else addrModeZeropageYindexed(); //zp,y for LAX/SAX illegal opcodes | ||
147 | break; | ||
148 | case 0xC: case 0xD: addrModeYindexed(); break; | ||
149 | case 0xE: addrModeXindexed(); break; | ||
150 | case 0xF: if ((IR&0xC0)!=0x80) addrModeXindexed(); //abs,x for illegal opcodes | ||
151 | else addrModeYindexed(); //abs,y for LAX/SAX illegal opcodes | ||
152 | break; | ||
153 | } | ||
154 | Addr&=0xFFFF; | ||
155 | |||
156 | switch ( (IR & 0xE0) >> 5 ) { //value-forming to cause gapless case-values and faster jump-table creation from switch-case | ||
157 | |||
158 | case 0: if ( (IR&0x1F) != 0xB ) { //ORA / SLO(ASO)=ASL+ORA | ||
159 | if ( (IR&3) == 3 ) { clrNZC(); setC(rd(Addr)>=N); wr( Addr, rd(Addr)<<1 ); Cycles+=2; } //for SLO | ||
160 | else Cycles -= SamePage; | ||
161 | A |= rd(Addr); setNZbyA(); //ORA | ||
162 | } | ||
163 | else { A&=rd(Addr); setNZbyA(); setC(A>=N); } //ANC (AND+Carry=bit7) | ||
164 | break; | ||
165 | |||
166 | case 1: if ( (IR&0x1F) != 0xB ) { //AND / RLA (ROL+AND) | ||
167 | if ( (IR&3) == 3 ) { //for RLA | ||
168 | T = (rd(Addr)<<1) + (ST&C); clrNZC(); setC(T>255); T&=0xFF; wr(Addr,T); Cycles+=2; | ||
169 | } | ||
170 | else Cycles -= SamePage; | ||
171 | A &= rd(Addr); setNZbyA(); //AND | ||
172 | } | ||
173 | else { A&=rd(Addr); setNZbyA(); setC(A>=N); } //ANC (AND+Carry=bit7) | ||
174 | break; | ||
175 | |||
176 | case 2: if ( (IR&0x1F) != 0xB ) { //EOR / SRE(LSE)=LSR+EOR | ||
177 | if ( (IR&3) == 3 ) { clrNZC(); setC(rd(Addr)&1); wr(Addr,rd(Addr)>>1); Cycles+=2; } //for SRE | ||
178 | else Cycles -= SamePage; | ||
179 | A ^= rd(Addr); setNZbyA(); //EOR | ||
180 | } | ||
181 | else { A&=rd(Addr); setC(A&1); A>>=1; A&=0xFF; setNZbyA(); } //ALR(ASR)=(AND+LSR) | ||
182 | break; | ||
183 | |||
184 | case 3: if ( (IR&0x1F) != 0xB ) { //RRA (ROR+ADC) / ADC | ||
185 | if( (IR&3) == 3 ) { //for RRA | ||
186 | T = (rd(Addr)>>1) + ((ST&C)<<7); clrNZC(); setC(T&1); wr(Addr,T); Cycles+=2; | ||
187 | } | ||
188 | else Cycles -= SamePage; | ||
189 | T=A; A += rd(Addr)+(ST&C); setNZCbyAdd(); setVbyAdd(rd(Addr)); //ADC | ||
190 | } | ||
191 | else { // ARR (AND+ROR, bit0 not going to C, but C and bit7 get exchanged.) | ||
192 | A &= rd(Addr); T += rd(Addr) + (ST&C); | ||
193 | clrNVZC(); setC(T>255); setVbyAdd(rd(Addr)); //V-flag set by intermediate ADC mechanism: (A&mem)+mem | ||
194 | T=A; A = (A>>1) + ((ST&C)<<7); setC(T>=N); setNZbyA(); | ||
195 | } | ||
196 | break; | ||
197 | |||
198 | case 4: if ( (IR&0x1F) == 0xB ) { A = X & rd(Addr); setNZbyA(); } //XAA (TXA+AND), highly unstable on real 6502! | ||
199 | else if ( (IR&0x1F) == 0x1B ) { SP = A&X; wr( Addr, SP&((Addr>>8)+1) ); } //TAS(SHS) (SP=A&X, mem=S&H} - unstable on real 6502 | ||
200 | else { wr2( Addr, A & (((IR&3)==3)? X:0xFF) ); } //STA / SAX (at times same as AHX/SHX/SHY) (illegal) | ||
201 | break; | ||
202 | |||
203 | case 5: if ( (IR&0x1F) != 0x1B ) { A=rd(Addr); if((IR&3)==3) X=A; } //LDA / LAX (illegal, used by my 1 rasterline player) (LAX #imm is unstable on C64) | ||
204 | else { A=X=SP = rd(Addr) & SP; } //LAS(LAR) | ||
205 | setNZbyA(); Cycles -= SamePage; | ||
206 | break; | ||
207 | |||
208 | case 6: if( (IR&0x1F) != 0xB ) { // CMP / DCP(DEC+CMP) | ||
209 | if ( (IR&3) == 3 ) { wr(Addr,rd(Addr)-1); Cycles+=2;} //DCP | ||
210 | else Cycles -= SamePage; | ||
211 | T = A-rd(Addr); | ||
212 | } | ||
213 | else { X = T = (A&X) - rd(Addr); } //SBX(AXS) //SBX (AXS) (CMP+DEX at the same time) | ||
214 | setNZCbySub(&T); | ||
215 | break; | ||
216 | |||
217 | case 7: if( (IR&3)==3 && (IR&0x1F)!=0xB ) { wr( Addr, rd(Addr)+1 ); Cycles+=2; } //ISC(ISB)=INC+SBC / SBC | ||
218 | else Cycles -= SamePage; | ||
219 | T=A; A -= rd(Addr) + !(ST&C); | ||
220 | setNZCbySub(&A); setVbyAdd(~rd(Addr)); | ||
221 | break; | ||
222 | } | ||
223 | } | ||
224 | |||
225 | |||
226 | else if (IR&2) { //nybble2: 2:illegal/LDX, 6:A/X/INC/DEC, A:Accu-shift/reg.transfer/NOP, E:shift/X/INC/DEC | ||
227 | |||
228 | switch (IR & 0x1F) { //Addressing modes | ||
229 | case 2: addrModeImmediate(); break; | ||
230 | case 6: addrModeZeropage(); break; | ||
231 | case 0xE: addrModeAbsolute(); break; | ||
232 | case 0x16: if ( (IR&0xC0) != 0x80 ) addrModeZeropageXindexed(); //zp,x | ||
233 | else addrModeZeropageYindexed(); //zp,y | ||
234 | break; | ||
235 | case 0x1E: if ( (IR&0xC0) != 0x80 ) addrModeXindexed(); //abs,x | ||
236 | else addrModeYindexed(); //abs,y | ||
237 | break; | ||
238 | } | ||
239 | Addr&=0xFFFF; | ||
240 | |||
241 | switch ( (IR & 0xE0) >> 5 ) { | ||
242 | |||
243 | case 0: clrC(); case 1: | ||
244 | if((IR&0xF)==0xA) { A = (A<<1) + (ST&C); setNZCbyAdd(); } //ASL/ROL (Accu) | ||
245 | else { T = (rd(Addr)<<1) + (ST&C); setC(T>255); setNZbyT(); wr(Addr,T); Cycles+=2; } //RMW (Read-Write-Modify) | ||
246 | break; | ||
247 | |||
248 | case 2: clrC(); case 3: | ||
249 | if((IR&0xF)==0xA) { T=A; A=(A>>1)+((ST&C)<<7); setC(T&1); A&=0xFF; setNZbyA(); } //LSR/ROR (Accu) | ||
250 | else { T = (rd(Addr)>>1) + ((ST&C)<<7); setC(rd(Addr)&1); setNZbyT(); wr(Addr,T); Cycles+=2; } //memory (RMW) | ||
251 | break; | ||
252 | |||
253 | case 4: if (IR&4) {wr2(Addr,X);} //STX | ||
254 | else if(IR&0x10) SP=X; //TXS | ||
255 | else { A=X; setNZbyA(); } //TXA | ||
256 | break; | ||
257 | |||
258 | case 5: if ( (IR&0xF) != 0xA ) { X=rd(Addr); Cycles -= SamePage; } //LDX | ||
259 | else if (IR & 0x10) X=SP; //TSX | ||
260 | else X=A; //TAX | ||
261 | setNZbyX(); | ||
262 | break; | ||
263 | |||
264 | case 6: if (IR&4) { wr(Addr,rd(Addr)-1); setNZbyM(); Cycles+=2; } //DEC | ||
265 | else { --X; setNZbyX(); } //DEX | ||
266 | break; | ||
267 | |||
268 | case 7: if (IR&4) { wr(Addr,rd(Addr)+1); setNZbyM(); Cycles+=2; } //INC/NOP | ||
269 | break; | ||
270 | } | ||
271 | } | ||
272 | |||
273 | |||
274 | else if ( (IR & 0xC) == 8 ) { //nybble2: 8:register/statusflag | ||
275 | if ( IR&0x10 ) { | ||
276 | if ( IR == 0x98 ) { A=Y; setNZbyA(); } //TYA | ||
277 | else { //CLC/SEC/CLI/SEI/CLV/CLD/SED | ||
278 | if (FlagSwitches[IR>>5] & 0x20) ST |= (FlagSwitches[IR>>5] & 0xDF); | ||
279 | else ST &= ~( FlagSwitches[IR>>5] & 0xDF ); | ||
280 | } | ||
281 | } | ||
282 | else { | ||
283 | switch ( (IR & 0xF0) >> 5 ) { | ||
284 | case 0: push(ST); Cycles=3; break; //PHP | ||
285 | case 1: ST=pop(); Cycles=4; break; //PLP | ||
286 | case 2: push(A); Cycles=3; break; //PHA | ||
287 | case 3: A=pop(); setNZbyA(); Cycles=4; break; //PLA | ||
288 | case 4: --Y; setNZbyY(); break; //DEY | ||
289 | case 5: Y=A; setNZbyY(); break; //TAY | ||
290 | case 6: ++Y; setNZbyY(); break; //INY | ||
291 | case 7: ++X; setNZbyX(); break; //INX | ||
292 | } | ||
293 | } | ||
294 | } | ||
295 | |||
296 | |||
297 | else { //nybble2: 0: control/branch/Y/compare 4: Y/compare C:Y/compare/JMP | ||
298 | |||
299 | if ( (IR&0x1F) == 0x10 ) { //BPL/BMI/BVC/BVS/BCC/BCS/BNE/BEQ relative branch | ||
300 | ++PC; | ||
301 | T=rd(PC); if (T & 0x80) T-=0x100; | ||
302 | if (IR & 0x20) { | ||
303 | if (ST & BranchFlags[IR>>6]) { PC+=T; Cycles=3; } | ||
304 | } | ||
305 | else { | ||
306 | if ( !(ST & BranchFlags[IR>>6]) ) { PC+=T; Cycles=3; } //plus 1 cycle if page is crossed? | ||
307 | } | ||
308 | } | ||
309 | |||
310 | else { //nybble2: 0:Y/control/Y/compare 4:Y/compare C:Y/compare/JMP | ||
311 | switch (IR&0x1F) { //Addressing modes | ||
312 | case 0: addrModeImmediate(); break; //imm. (or abs.low for JSR/BRK) | ||
313 | case 4: addrModeZeropage(); break; | ||
314 | case 0xC: addrModeAbsolute(); break; | ||
315 | case 0x14: addrModeZeropageXindexed(); break; //zp,x | ||
316 | case 0x1C: addrModeXindexed(); break; //abs,x | ||
317 | } | ||
318 | Addr&=0xFFFF; | ||
319 | |||
320 | switch ( (IR & 0xE0) >> 5 ) { | ||
321 | |||
322 | case 0: if( !(IR&4) ) { //BRK / NOP-absolute/abs,x/zp/zp,x | ||
323 | push((PC+2-1)>>8); push((PC+2-1)&0xFF); push(ST|B); ST |= I; //BRK | ||
324 | PC = rd(0xFFFE) + (rd(0xFFFF)<<8) - 1; Cycles=7; | ||
325 | } | ||
326 | else if (IR == 0x1C) Cycles -= SamePage; //NOP abs,x | ||
327 | break; | ||
328 | |||
329 | case 1: if (IR & 0xF) { //BIT / NOP-abs,x/zp,x | ||
330 | if ( !(IR&0x10) ) { ST &= 0x3D; ST |= (rd(Addr)&0xC0) | ( (!(A&rd(Addr))) << 1 ); } //BIT | ||
331 | else if (IR == 0x3C) Cycles -= SamePage; //NOP abs,x | ||
332 | } | ||
333 | else { //JSR | ||
334 | push((PC+2-1)>>8); push((PC+2-1)&0xFF); | ||
335 | PC=rd(Addr)+rd(Addr+1)*256-1; Cycles=6; | ||
336 | } | ||
337 | break; | ||
338 | |||
339 | case 2: if (IR & 0xF) { //JMP / NOP-abs,x/zp/zp,x | ||
340 | if (IR == 0x4C) { //JMP | ||
341 | PC = Addr-1; Cycles=3; | ||
342 | rd(Addr+1); //a read from jump-address highbyte is used in some tunes with jmp DD0C (e.g. Wonderland_XIII_tune_1.sid) | ||
343 | //if (Addr==PrevPC) {storeReg(); C64->Returned=1; return 0xFF;} //turn self-jump mainloop (after init) into idle time | ||
344 | } | ||
345 | else if (IR==0x5C) Cycles -= SamePage; //NOP abs,x | ||
346 | } | ||
347 | else { //RTI | ||
348 | ST=pop(); T=pop(); PC = (pop()<<8) + T - 1; Cycles=6; | ||
349 | if (C64->Returned && SP>=0xFF) { ++PC; storeReg(); return 0xFE; } | ||
350 | } | ||
351 | break; | ||
352 | |||
353 | case 3: if (IR & 0xF) { //JMP() (indirect) / NOP-abs,x/zp/zp,x | ||
354 | if (IR == 0x6C) { //JMP() (indirect) | ||
355 | PC = rd( (Addr&0xFF00) + ((Addr+1)&0xFF) ); //(with highbyte-wraparound bug) | ||
356 | PC = (PC<<8) + rd(Addr) - 1; Cycles=5; | ||
357 | } | ||
358 | else if (IR == 0x7C) Cycles -= SamePage; //NOP abs,x | ||
359 | } | ||
360 | else { //RTS | ||
361 | if (SP>=0xFF) {storeReg(); C64->Returned=1; return 0xFF;} //Init returns, provide idle-time between IRQs | ||
362 | T=pop(); PC = (pop()<<8) + T; Cycles=6; | ||
363 | } | ||
364 | break; | ||
365 | |||
366 | case 4: if (IR & 4) { wr2( Addr, Y ); } //STY / NOP #imm | ||
367 | break; | ||
368 | |||
369 | case 5: Y=rd(Addr); setNZbyY(); Cycles -= SamePage; //LDY | ||
370 | break; | ||
371 | |||
372 | case 6: if ( !(IR&0x10) ) { //CPY / NOP abs,x/zp,x | ||
373 | T=Y-rd(Addr); setNZCbySub(&T); //CPY | ||
374 | } | ||
375 | else if (IR==0xDC) Cycles -= SamePage; //NOP abs,x | ||
376 | break; | ||
377 | |||
378 | case 7: if ( !(IR&0x10) ) { //CPX / NOP abs,x/zp,x | ||
379 | T=X-rd(Addr); setNZCbySub(&T); //CPX | ||
380 | } | ||
381 | else if (IR==0xFC) Cycles -= SamePage; //NOP abs,x | ||
382 | break; | ||
383 | } | ||
384 | } | ||
385 | } | ||
386 | |||
387 | |||
388 | ++PC; //PC&=0xFFFF; | ||
389 | |||
390 | storeReg(); | ||
391 | |||
392 | |||
393 | if (!C64->RealSIDmode) { //substitute KERNAL IRQ-return in PSID (e.g. Microprose Soccer) | ||
394 | if ( (C64->RAMbank[1]&3)>1 && PrevPC<0xE000 && (PC==0xEA31 || PC==0xEA81 || PC==0xEA7E) ) return 0xFE; | ||
395 | } | ||
396 | |||
397 | |||
398 | return Cycles; | ||
399 | } | ||
400 | |||
401 | |||
402 | |||
403 | //handle entering into IRQ and NMI interrupt | ||
404 | static inline char cRSID_handleCPUinterrupts (cRSID_CPUinstance* CPU) { | ||
405 | enum StatusFlagBitValues { B=0x10, I=0x04 }; | ||
406 | inline void push (unsigned char value) { CPU->C64->RAMbank[0x100+CPU->SP] = value; --CPU->SP; CPU->SP&=0xFF; } //push a value to stack | ||
407 | |||
408 | if (CPU->C64->NMI > CPU->PrevNMI) { //if IRQ and NMI at the same time, NMI is serviced first | ||
409 | push(CPU->PC>>8); push(CPU->PC&0xFF); push(CPU->ST); CPU->ST |= I; | ||
410 | CPU->PC = *cRSID_getMemReadPtr(0xFFFA) + (*cRSID_getMemReadPtr(0xFFFB)<<8); //NMI-vector | ||
411 | CPU->PrevNMI = CPU->C64->NMI; | ||
412 | return 1; | ||
413 | } | ||
414 | else if ( CPU->C64->IRQ && !(CPU->ST&I) ) { | ||
415 | push(CPU->PC>>8); push(CPU->PC&0xFF); push(CPU->ST); CPU->ST |= I; | ||
416 | CPU->PC = *cRSID_getMemReadPtr(0xFFFE) + (*cRSID_getMemReadPtr(0xFFFF)<<8); //maskable IRQ-vector | ||
417 | CPU->PrevNMI = CPU->C64->NMI; | ||
418 | return 1; | ||
419 | } | ||
420 | CPU->PrevNMI = CPU->C64->NMI; //prepare for NMI edge-detection | ||
421 | |||
422 | return 0; | ||
423 | } | ||