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