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