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