summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/cRSID/C64/CPU.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/cRSID/C64/CPU.c')
-rw-r--r--lib/rbcodec/codecs/cRSID/C64/CPU.c423
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
5void 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
10unsigned 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
404static 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}