mapper fixes for ncpu, debug is broken atm
[fceu.git] / x6502.c
... / ...
CommitLineData
1/* FCE Ultra - NES/Famicom Emulator
2 *
3 * Copyright notice for this file:
4 * Copyright (C) 2002 Ben Parnell
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include <string.h>
22
23#include "types.h"
24#include "x6502.h"
25#include "fce.h"
26#include "sound.h"
27#include "cart.h"
28
29#ifdef DEBUG_ASM_6502
30extern uint32 PC_prev, OP_prev;
31extern int cpu_lastval;
32#endif
33
34X6502 X;
35uint32 timestamp;
36void FP_FASTAPASS(1) (*MapIRQHook)(int a);
37
38#define _PC X.PC
39#define _A X.A
40#define _X X.X
41#define _Y X.Y
42#define _S X.S
43#define _P X.P
44#define _PI X.mooPI
45//#define _PZ X.PZ // unused?
46#define _DB X.DB
47#define _count X.count
48#define _tcount X.tcount
49#define _IRQlow X.IRQlow
50#define _jammed X.jammed
51
52
53static INLINE uint8 RdMem(unsigned int A)
54{
55 // notaz: try to avoid lookup of every address at least for ROM and RAM areas
56 // I've verified that if ARead[0xfff0] points to CartBR, it is always normal ROM read.
57#if 0
58 if ((A&0x8000)/* && ARead[0xfff0] == CartBR*/) {
59 return (_DB=Page[A>>11][A]);
60 }
61#endif
62#if 0 // enabling this causes 4fps slowdown. Why?
63 if ((A&0xe000) == 0) { // RAM area (always 0-0x1fff)
64 return (_DB=RAM[A&0x7FF]);
65 }
66#endif
67 _DB=ARead[A](A);
68#ifdef DEBUG_ASM_6502
69 // TODO: read counter, not 0x10000..
70 if (cpu_lastval)
71 cpu_lastval|=_DB<<8;
72 else
73 cpu_lastval=_DB|0x10000;
74 printf("read [%04x] %02x, cpu_lastval=%02x\n", A, _DB, cpu_lastval);
75#endif
76 return _DB;
77}
78
79static INLINE void WrMem(unsigned int A, uint8 V)
80{
81 if ((A&0xe000) == 0) { // RAM area (always 0-0x1fff)
82 RAM[A&0x7FF] = V;
83 return;
84 }
85 BWrite[A](A,V);
86}
87
88static INLINE uint8 RdRAM(unsigned int A)
89{
90 return((_DB=RAM[A]));
91}
92
93static INLINE void WrRAM(unsigned int A, uint8 V)
94{
95 RAM[A]=V;
96}
97
98static INLINE void ADDCYC(int x)
99{
100 //_tcount+=x;
101 _count-=x*48;
102 timestamp+=x;
103}
104
105void FASTAPASS(1) X6502_AddCycles_c(int x)
106{
107 ADDCYC(x);
108}
109
110static INLINE void PUSH(uint8 V)
111{
112 WrRAM(0x100+_S,V);
113 _S--;
114}
115
116static INLINE uint8 POP(void)
117{
118 _S++;
119 return(RdRAM(0x100+_S));
120}
121
122#if 0
123static uint8 ZNTable[256] = {
124 Z_FLAG,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
125 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
126 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
127 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
128 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
129 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
130 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
131 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
132 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
133 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
134 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
135 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
136 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
137 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
138 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
139 N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG
140};
141#endif
142/* Some of these operations will only make sense if you know what the flag
143 constants are. */
144//#define X_ZN(zort) _P&=~(Z_FLAG|N_FLAG);_P|=ZNTable[zort]
145//#define X_ZNT(zort) _P|=ZNTable[zort]
146#define X_ZN(zort) _P&=~(Z_FLAG|N_FLAG);if(!zort) _P|=Z_FLAG;else _P|=zort&N_FLAG
147#define X_ZNT(zort) if(!zort) _P|=Z_FLAG;else _P|=(zort&N_FLAG)
148
149/* Care must be taken if you want to turn this into a macro. Use { and }. */
150#define JR(); \
151{ \
152 uint32 tmp; \
153 int8 disp; \
154 disp=RdMem(_PC++); \
155 ADDCYC(1); \
156 tmp=_PC; \
157 _PC+=disp; \
158 if((tmp^_PC)&0x100) \
159 ADDCYC(1); \
160}
161
162#define LDA _A=x;X_ZN(_A)
163#define LDX _X=x;X_ZN(_X)
164#define LDY _Y=x;X_ZN(_Y)
165
166/* All of the freaky arithmetic operations. */
167#define AND _A&=x;X_ZN(_A)
168//#define BIT _P&=~(Z_FLAG|V_FLAG|N_FLAG);_P|=ZNTable[x&_A]&Z_FLAG;_P|=x&(V_FLAG|N_FLAG)
169#define BIT _P&=~(Z_FLAG|V_FLAG|N_FLAG);if(!(x&_A)) _P|=Z_FLAG;_P|=x&(V_FLAG|N_FLAG)
170#define EOR _A^=x;X_ZN(_A)
171#define ORA _A|=x;X_ZN(_A)
172
173#define ADC { \
174 uint32 l=_A+x+(_P&1); \
175 _P&=~(Z_FLAG|C_FLAG|N_FLAG|V_FLAG); \
176 _P|=((((_A^x)&0x80)^0x80) & ((_A^l)&0x80))>>1; \
177 _P|=(l>>8)&C_FLAG; \
178 _A=l; \
179 X_ZNT(_A); \
180 }
181#define SBC { \
182 uint32 l=_A-x-((_P&1)^1); \
183 _P&=~(Z_FLAG|C_FLAG|N_FLAG|V_FLAG); \
184 _P|=((_A^l)&(_A^x)&0x80)>>1; \
185 _P|=((l>>8)&C_FLAG)^C_FLAG; \
186 _A=l; \
187 X_ZNT(_A); \
188 }
189
190#define CMPL(a1,a2) { \
191 uint32 t=a1-a2; \
192 X_ZN(t&0xFF); \
193 _P&=~C_FLAG; \
194 _P|=((t>>8)&C_FLAG)^C_FLAG; \
195 }
196
197/* Special undocumented operation. Very similar to CMP. */
198#define AXS { \
199 uint32 t=(_A&_X)-x; \
200 X_ZN(t&0xFF); \
201 _P&=~C_FLAG; \
202 _P|=((t>>8)&C_FLAG)^C_FLAG; \
203 _X=t; \
204 }
205
206#define CMP CMPL(_A,x)
207#define CPX CMPL(_X,x)
208#define CPY CMPL(_Y,x)
209
210/* The following operations modify the byte being worked on. */
211#define DEC x--;X_ZN(x)
212#define INC x++;X_ZN(x)
213
214#define ASL _P&=~C_FLAG;_P|=x>>7;x<<=1;X_ZN(x)
215#define LSR _P&=~(C_FLAG|N_FLAG|Z_FLAG);_P|=x&1;x>>=1;X_ZNT(x)
216
217/* For undocumented instructions, maybe for other things later... */
218#define LSRA _P&=~(C_FLAG|N_FLAG|Z_FLAG);_P|=_A&1;_A>>=1;X_ZNT(_A)
219
220#define ROL { \
221 uint8 l=x>>7; \
222 x<<=1; \
223 x|=_P&C_FLAG; \
224 _P&=~(Z_FLAG|N_FLAG|C_FLAG); \
225 _P|=l; \
226 X_ZNT(x); \
227 }
228#define ROR { \
229 uint8 l=x&1; \
230 x>>=1; \
231 x|=(_P&C_FLAG)<<7; \
232 _P&=~(Z_FLAG|N_FLAG|C_FLAG); \
233 _P|=l; \
234 X_ZNT(x); \
235 }
236
237/* Icky icky thing for some undocumented instructions. Can easily be
238 broken if names of local variables are changed.
239*/
240
241/* Absolute */
242#define GetAB(target) \
243{ \
244 target=RdMem(_PC++); \
245 target|=RdMem(_PC++)<<8; \
246}
247
248/* Absolute Indexed(for reads) */
249#define GetABIRD(target, i) \
250{ \
251 unsigned int tmp; \
252 GetAB(tmp); \
253 target=tmp; \
254 target+=i; \
255 if((target^tmp)&0x100) \
256 { \
257 target&=0xFFFF; \
258 RdMem(target^0x100); \
259 ADDCYC(1); \
260 } \
261}
262
263/* Absolute Indexed(for writes and rmws) */
264#define GetABIWR(target, i) \
265{ \
266 unsigned int rt; \
267 GetAB(rt); \
268 target=rt; \
269 target+=i; \
270 target&=0xFFFF; \
271 RdMem((target&0x00FF)|(rt&0xFF00)); \
272}
273
274/* Zero Page */
275#define GetZP(target) \
276{ \
277 target=RdMem(_PC++); \
278}
279
280/* Zero Page Indexed */
281#define GetZPI(target,i) \
282{ \
283 target=i+RdMem(_PC++); \
284}
285
286/* Indexed Indirect */
287#define GetIX(target) \
288{ \
289 uint8 tmp; \
290 tmp=RdMem(_PC++); \
291 tmp+=_X; \
292 target=RdRAM(tmp++); \
293 target|=RdRAM(tmp)<<8; \
294}
295
296/* Indirect Indexed(for reads) */
297#define GetIYRD(target) \
298{ \
299 unsigned int rt; \
300 uint8 tmp; \
301 tmp=RdMem(_PC++); \
302 rt=RdRAM(tmp++); \
303 rt|=RdRAM(tmp)<<8; \
304 target=rt; \
305 target+=_Y; \
306 if((target^rt)&0x100) \
307 { \
308 target&=0xFFFF; \
309 RdMem(target^0x100); \
310 ADDCYC(1); \
311 } \
312}
313
314/* Indirect Indexed(for writes and rmws) */
315#define GetIYWR(target) \
316{ \
317 unsigned int rt; \
318 uint8 tmp; \
319 tmp=RdMem(_PC++); \
320 rt=RdRAM(tmp++); \
321 rt|=RdRAM(tmp)<<8; \
322 target=rt; \
323 target+=_Y; \
324 RdMem((target&0x00FF)|(rt&0xFF00)); \
325}
326
327/* Now come the macros to wrap up all of the above stuff addressing mode functions
328 and operation macros. Note that operation macros will always operate(redundant
329 redundant) on the variable "x".
330*/
331
332#define RMW_A(op) {uint8 x=_A; op; _A=x; break; } /* Meh... */
333#define RMW_AB(op) {unsigned int A; uint8 x; GetAB(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; }
334#define RMW_ABI(reg,op) {unsigned int A; uint8 x; GetABIWR(A,reg); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; }
335#define RMW_ABX(op) RMW_ABI(_X,op)
336#define RMW_ABY(op) RMW_ABI(_Y,op)
337#define RMW_IX(op) {unsigned int A; uint8 x; GetIX(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; }
338#define RMW_IY(op) {unsigned int A; uint8 x; GetIYWR(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; }
339#define RMW_ZP(op) {uint8 A; uint8 x; GetZP(A); x=RdRAM(A); op; WrRAM(A,x); break; }
340#define RMW_ZPX(op) {uint8 A; uint8 x; GetZPI(A,_X); x=RdRAM(A); op; WrRAM(A,x); break;}
341
342#define LD_IM(op) {uint8 x; x=RdMem(_PC++); op; break;}
343#define LD_ZP(op) {uint8 A; uint8 x; GetZP(A); x=RdRAM(A); op; break;}
344#define LD_ZPX(op) {uint8 A; uint8 x; GetZPI(A,_X); x=RdRAM(A); op; break;}
345#define LD_ZPY(op) {uint8 A; uint8 x; GetZPI(A,_Y); x=RdRAM(A); op; break;}
346#define LD_AB(op) {unsigned int A; uint8 x; GetAB(A); x=RdMem(A); op; break; }
347#define LD_ABI(reg,op) {unsigned int A; uint8 x; GetABIRD(A,reg); x=RdMem(A); op; break;}
348#define LD_ABX(op) LD_ABI(_X,op)
349#define LD_ABY(op) LD_ABI(_Y,op)
350#define LD_IX(op) {unsigned int A; uint8 x; GetIX(A); x=RdMem(A); op; break;}
351#define LD_IY(op) {unsigned int A; uint8 x; GetIYRD(A); x=RdMem(A); op; break;}
352
353#define ST_ZP(r) {uint8 A; GetZP(A); WrRAM(A,r); break;}
354#define ST_ZPX(r) {uint8 A; GetZPI(A,_X); WrRAM(A,r); break;}
355#define ST_ZPY(r) {uint8 A; GetZPI(A,_Y); WrRAM(A,r); break;}
356#define ST_AB(r) {unsigned int A; GetAB(A); WrMem(A,r); break;}
357#define ST_ABI(reg,r) {unsigned int A; GetABIWR(A,reg); WrMem(A,r); break; }
358#define ST_ABX(r) ST_ABI(_X,r)
359#define ST_ABY(r) ST_ABI(_Y,r)
360#define ST_IX(r) {unsigned int A; GetIX(A); WrMem(A,r); break; }
361#define ST_IY(r) {unsigned int A; GetIYWR(A); WrMem(A,r); break; }
362
363static uint8 CycTable[256] =
364{
365/*0x00*/ 7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,
366/*0x10*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
367/*0x20*/ 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,
368/*0x30*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
369/*0x40*/ 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,
370/*0x50*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
371/*0x60*/ 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,
372/*0x70*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
373/*0x80*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
374/*0x90*/ 2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,
375/*0xA0*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
376/*0xB0*/ 2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,
377/*0xC0*/ 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,
378/*0xD0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
379/*0xE0*/ 2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6,
380/*0xF0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
381};
382
383void FASTAPASS(1) X6502_IRQBegin_c(int w)
384{
385 _IRQlow|=w;
386}
387
388void FASTAPASS(1) X6502_IRQEnd_c(int w)
389{
390 _IRQlow&=~w;
391}
392
393void TriggerIRQ_c(void) /* This function should probably be phased out. */
394{
395 _IRQlow|=FCEU_IQTEMP;
396}
397
398void TriggerNMINSF_c(void)
399{
400 ADDCYC(7);
401 PUSH(_PC>>8);
402 PUSH(_PC);
403 PUSH((_P&~B_FLAG)|(U_FLAG));
404 _PC=0x3800;
405}
406
407void TriggerNMI_c(void)
408{
409 _IRQlow|=FCEU_IQNMI;
410}
411
412static void TriggerNMIReal(void)
413{
414 if(!_jammed)
415 {
416 ADDCYC(7);
417 PUSH(_PC>>8);
418 PUSH(_PC);
419 _P&=~B_FLAG;
420 PUSH(_P|U_FLAG);
421 _PC=RdMem(0xFFFA);
422 _PC|=RdMem(0xFFFB)<<8;
423#ifdef DEBUG_ASM_6502
424 PC_prev = _PC;
425 OP_prev = 0x100;
426#endif
427 }
428}
429
430void TriggerIRQReal(void)
431{
432 if(!(_PI&I_FLAG) && !_jammed)
433 {
434 ADDCYC(7);
435 PUSH(_PC>>8);
436 PUSH(_PC);
437 _P&=~B_FLAG;
438 PUSH(_P|U_FLAG);
439 _P|=I_FLAG;
440 _PC=RdMem(0xFFFE);
441 _PC|=RdMem(0xFFFF)<<8;
442#ifdef DEBUG_ASM_6502
443 PC_prev = _PC;
444 OP_prev = 0x101;
445#endif
446 }
447}
448
449void X6502_Reset_c(void)
450{
451 _PC=RdMem(0xFFFC);
452 _PC|=RdMem(0xFFFD)<<8;
453 if(FCEUGameInfo.type==GIT_NSF) _PC=0x3830;
454 _jammed=0;
455 _PI=_P=I_FLAG;
456}
457
458void X6502_Power_c(void)
459{
460 memset((void *)&X,0,sizeof(X));
461 timestamp=0;
462 X6502_Reset_c();
463}
464
465
466//int asdc = 0;
467void X6502_Run_c(void/*int32 cycles*/)
468{
469/*
470 if(PAL)
471 cycles*=15; // 15*4=60
472 else
473 cycles*=16; // 16*4=64
474
475 _count+=cycles;
476*/
477// if (_count <= 0) asdc++;
478
479 while(_count>0)
480 {
481 int32 temp;
482 uint8 b1;
483
484 if(_IRQlow)
485 {
486 if(_IRQlow&FCEU_IQNMI)
487 TriggerNMIReal();
488 else
489 TriggerIRQReal();
490
491 _IRQlow&=~(FCEU_IQTEMP|FCEU_IQNMI);
492 if(_count<=0) {_PI=_P;return;} /* Should increase accuracy without a */
493 /* major speed hit. */
494 }
495 _PI=_P;
496 b1=RdMem(_PC);
497 temp=CycTable[b1];
498 ADDCYC(temp);
499 //temp=_tcount;
500 //_tcount=0;
501
502 if(MapIRQHook) MapIRQHook(temp);
503
504 temp*=48;
505
506 fhcnt-=temp;
507 if(fhcnt<=0)
508 {
509 FrameSoundUpdate();
510 fhcnt+=fhinc;
511 }
512
513 if(PCMIRQCount>0)
514 {
515 PCMIRQCount-=temp;
516 if(PCMIRQCount<=0)
517 {
518 vdis=1;
519 if((PSG[0x10]&0x80) && !(PSG[0x10]&0x40))
520 {
521 extern uint8 SIRQStat;
522 SIRQStat|=0x80;
523 X6502_IRQBegin_c(FCEU_IQDPCM);
524 }
525 }
526 }
527
528#ifdef DEBUG_ASM_6502
529 PC_prev = _PC;
530 OP_prev = b1;
531 cpu_lastval = 0;
532#endif
533 //printf("$%04x:$%02x\n",_PC,b1);
534 //_PC++;
535 //printf("$%02x\n",b1);
536 _PC++;
537 switch(b1)
538 {
539 #include "ops.h"
540 }
541
542#ifdef DEBUG_ASM_6502
543 _PI=_P;
544#endif
545 }
546}
547
548