81eb56ec17263e45f64edfc8ee0d27cd259cf899
[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 #ifdef DEBUG_ASM_6502
30 extern uint32 PC_prev, OP_prev;
31 extern int cpu_lastval;
32 #endif
33
34 X6502 X;
35 uint32 timestamp;
36 void 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
53 static 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
79 static 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
88 static INLINE uint8 RdRAM(unsigned int A)
89 {
90  return((_DB=RAM[A]));
91 }
92
93 static INLINE void WrRAM(unsigned int A, uint8 V)
94 {
95  RAM[A]=V;
96 }
97
98 static INLINE void ADDCYC(int x)
99 {
100  //_tcount+=x;
101  _count-=x*48;
102  timestamp+=x;
103 }
104
105 void FASTAPASS(1) X6502_AddCycles_c(int x)
106 {
107  ADDCYC(x);
108 }
109
110 static INLINE void PUSH(uint8 V)
111 {
112  WrRAM(0x100+_S,V);
113  _S--;
114 }
115
116 static INLINE uint8 POP(void)
117 {
118  _S++;
119  return(RdRAM(0x100+_S));
120 }
121
122 #if 0
123 static 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
363 static 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
383 void FASTAPASS(1) X6502_IRQBegin_c(int w)
384 {
385  _IRQlow|=w;
386 }
387
388 void FASTAPASS(1) X6502_IRQEnd_c(int w)
389 {
390  _IRQlow&=~w;
391 }
392
393 void TriggerIRQ_c(void) /* This function should probably be phased out. */
394 {
395  _IRQlow|=FCEU_IQTEMP;
396 }
397
398 void 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
407 void TriggerNMI_c(void)
408 {
409  _IRQlow|=FCEU_IQNMI;
410 }
411
412 static 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
430 void 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
449 void 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
458 void 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;
467 void 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