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