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