some refactoring, no change in performance seen
[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#ifdef DEBUG_ASM_6502
30#include <stdio.h>
31#include <stdlib.h>
32extern uint32 PC_prev, OP_prev;
33extern uint8 dreads[4];
34extern uint32 dwrites_c[2];
35extern int dread_count_c, dwrite_count_c;
36extern int mapirq_cyc_c;
37extern void (*MapIRQHook)(int a);
38#define DummyRdMem(...)
39#else
40#define DummyRdMem RdMem
41void FP_FASTAPASS(1) (*MapIRQHook)(int a);
42#endif
43
44X6502 X;
45uint32 timestamp;
46
47#define _PC X.PC
48#define _A X.A
49#define _X X.X
50#define _Y X.Y
51#define _S X.S
52#define _P X.P
53#define _PI X.mooPI
54//#define _PZ X.PZ // unused?
55#define _DB X.DB
56#define _count X.count
57#define _tcount X.tcount
58#define _IRQlow X.IRQlow
59#define _jammed X.jammed
60
61
62static INLINE uint8 RdMem(unsigned int A)
63{
64 // notaz: try to avoid lookup of every address at least for ROM and RAM areas
65 // I've verified that if ARead[0xfff0] points to CartBR, it is always normal ROM read.
66#if 0
67 if ((A&0x8000)/* && ARead[0xfff0] == CartBR*/) {
68 return (_DB=Page[A>>11][A]);
69 }
70#endif
71#if 0 // enabling this causes 4fps slowdown. Why?
72 if ((A&0xe000) == 0) { // RAM area (always 0-0x1fff)
73 return (_DB=RAM[A&0x7FF]);
74 }
75#endif
76 _DB=ARead[A](A);
77#ifdef DEBUG_ASM_6502
78 //printf("a == %x, pc == %x\n", A, _PC);
79 if (A >= 0x2000 && A != _PC && A != _PC - 1 && A != _PC + 1) {
80 dreads[dread_count_c++] = _DB;
81 if (dread_count_c > 4) { printf("dread_count out of range\n"); exit(1); }
82 }
83#endif
84 return _DB;
85}
86
87static INLINE void WrMem(unsigned int A, uint8 V)
88{
89 //printf("w [%04x] %02x\n", A, V);
90 if ((A&0xe000) == 0) { // RAM area (always 0-0x1fff)
91 RAM[A&0x7FF] = V;
92 return;
93 }
94 BWrite[A](A,V);
95#ifdef DEBUG_ASM_6502
96 dwrites_c[dwrite_count_c++] = (A<<8)|V;
97 if (dwrite_count_c > 2) { printf("dwrite_count_c out of range\n"); exit(1); }
98#endif
99}
100
101static INLINE uint8 RdRAM(unsigned int A)
102{
103 return((_DB=RAM[A]));
104}
105
106static INLINE void WrRAM(unsigned int A, uint8 V)
107{
108 RAM[A]=V;
109}
110
111static INLINE void ADDCYC(int x)
112{
113 _tcount+=x;
114 _count-=x*48;
115 timestamp+=x;
116}
117
118void FASTAPASS(1) X6502_AddCycles_c(int x)
119{
120 ADDCYC(x);
121}
122
123static INLINE void PUSH(uint8 V)
124{
125 WrRAM(0x100+_S,V);
126 _S--;
127}
128
129static INLINE uint8 POP(void)
130{
131 _S++;
132 return(RdRAM(0x100+_S));
133}
134
135#if 0
136static uint8 ZNTable[256] = {
137 Z_FLAG,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
138 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
139 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
140 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
141 0,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 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,
146 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,
147 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,
148 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,
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};
154#endif
155/* Some of these operations will only make sense if you know what the flag
156 constants are. */
157//#define X_ZN(zort) _P&=~(Z_FLAG|N_FLAG);_P|=ZNTable[zort]
158//#define X_ZNT(zort) _P|=ZNTable[zort]
159#define X_ZN(zort) _P&=~(Z_FLAG|N_FLAG);if(!zort) _P|=Z_FLAG;else _P|=zort&N_FLAG
160#define X_ZNT(zort) if(!zort) _P|=Z_FLAG;else _P|=(zort&N_FLAG)
161
162/* Care must be taken if you want to turn this into a macro. Use { and }. */
163#define JR(); \
164{ \
165 uint32 tmp; \
166 int8 disp; \
167 disp=RdMem(_PC++); \
168 ADDCYC(1); \
169 tmp=_PC; \
170 _PC+=disp; \
171 if((tmp^_PC)&0x100) \
172 ADDCYC(1); \
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 BIT _P&=~(Z_FLAG|V_FLAG|N_FLAG);if(!(x&_A)) _P|=Z_FLAG;_P|=x&(V_FLAG|N_FLAG)
183#define EOR _A^=x;X_ZN(_A)
184#define ORA _A|=x;X_ZN(_A)
185
186#define ADC { \
187 uint32 l=_A+x+(_P&1); \
188 _P&=~(Z_FLAG|C_FLAG|N_FLAG|V_FLAG); \
189 _P|=((((_A^x)&0x80)^0x80) & ((_A^l)&0x80))>>1; \
190 _P|=(l>>8)&C_FLAG; \
191 _A=l; \
192 X_ZNT(_A); \
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 target|=RdMem(_PC++)<<8; \
259}
260
261/* Absolute Indexed(for reads) */
262#define GetABIRD(target, i) \
263{ \
264 unsigned int tmp; \
265 GetAB(tmp); \
266 target=tmp; \
267 target+=i; \
268 if((target^tmp)&0x100) \
269 { \
270 target&=0xFFFF; \
271 DummyRdMem(target^0x100); \
272 ADDCYC(1); \
273 } \
274}
275
276/* Absolute Indexed(for writes and rmws) */
277#define GetABIWR(target, i) \
278{ \
279 unsigned int rt; \
280 GetAB(rt); \
281 target=rt; \
282 target+=i; \
283 target&=0xFFFF; \
284 DummyRdMem((target&0x00FF)|(rt&0xFF00)); \
285}
286
287/* Zero Page */
288#define GetZP(target) \
289{ \
290 target=RdMem(_PC++); \
291}
292
293/* Zero Page Indexed */
294#define GetZPI(target,i) \
295{ \
296 target=i+RdMem(_PC++); \
297}
298
299/* Indexed Indirect */
300#define GetIX(target) \
301{ \
302 uint8 tmp; \
303 tmp=RdMem(_PC++); \
304 tmp+=_X; \
305 target=RdRAM(tmp++); \
306 target|=RdRAM(tmp)<<8; \
307}
308
309/* Indirect Indexed(for reads) */
310#define GetIYRD(target) \
311{ \
312 unsigned int rt; \
313 uint8 tmp; \
314 tmp=RdMem(_PC++); \
315 rt=RdRAM(tmp++); \
316 rt|=RdRAM(tmp)<<8; \
317 target=rt; \
318 target+=_Y; \
319 if((target^rt)&0x100) \
320 { \
321 target&=0xFFFF; \
322 DummyRdMem(target^0x100); \
323 ADDCYC(1); \
324 } \
325}
326
327/* Indirect Indexed(for writes and rmws) */
328#define GetIYWR(target) \
329{ \
330 unsigned int rt; \
331 uint8 tmp; \
332 tmp=RdMem(_PC++); \
333 rt=RdRAM(tmp++); \
334 rt|=RdRAM(tmp)<<8; \
335 target=rt; \
336 target+=_Y; \
337 DummyRdMem((target&0x00FF)|(rt&0xFF00)); \
338}
339
340/* Now come the macros to wrap up all of the above stuff addressing mode functions
341 and operation macros. Note that operation macros will always operate(redundant
342 redundant) on the variable "x".
343*/
344
345#define RMW_A(op) {uint8 x=_A; op; _A=x; break; } /* Meh... */
346#define RMW_AB(op) {unsigned int A; uint8 x; GetAB(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; }
347#define RMW_ABI(reg,op) {unsigned int A; uint8 x; GetABIWR(A,reg); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; }
348#define RMW_ABX(op) RMW_ABI(_X,op)
349#define RMW_ABY(op) RMW_ABI(_Y,op)
350#define RMW_IX(op) {unsigned int A; uint8 x; GetIX(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; }
351#define RMW_IY(op) {unsigned int A; uint8 x; GetIYWR(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; }
352#define RMW_ZP(op) {uint8 A; uint8 x; GetZP(A); x=RdRAM(A); op; WrRAM(A,x); break; }
353#define RMW_ZPX(op) {uint8 A; uint8 x; GetZPI(A,_X); x=RdRAM(A); op; WrRAM(A,x); break;}
354
355#define LD_IM(op) {uint8 x; x=RdMem(_PC++); op; break;}
356#define LD_ZP(op) {uint8 A; uint8 x; GetZP(A); x=RdRAM(A); op; break;}
357#define LD_ZPX(op) {uint8 A; uint8 x; GetZPI(A,_X); x=RdRAM(A); op; break;}
358#define LD_ZPY(op) {uint8 A; uint8 x; GetZPI(A,_Y); x=RdRAM(A); op; break;}
359#define LD_AB(op) {unsigned int A; uint8 x; GetAB(A); x=RdMem(A); op; break; }
360#define LD_ABI(reg,op) {unsigned int A; uint8 x; GetABIRD(A,reg); x=RdMem(A); op; break;}
361#define LD_ABX(op) LD_ABI(_X,op)
362#define LD_ABY(op) LD_ABI(_Y,op)
363#define LD_IX(op) {unsigned int A; uint8 x; GetIX(A); x=RdMem(A); op; break;}
364#define LD_IY(op) {unsigned int A; uint8 x; GetIYRD(A); x=RdMem(A); op; break;}
365
366#define ST_ZP(r) {uint8 A; GetZP(A); WrRAM(A,r); break;}
367#define ST_ZPX(r) {uint8 A; GetZPI(A,_X); WrRAM(A,r); break;}
368#define ST_ZPY(r) {uint8 A; GetZPI(A,_Y); WrRAM(A,r); break;}
369#define ST_AB(r) {unsigned int A; GetAB(A); WrMem(A,r); break;}
370#define ST_ABI(reg,r) {unsigned int A; GetABIWR(A,reg); WrMem(A,r); break; }
371#define ST_ABX(r) ST_ABI(_X,r)
372#define ST_ABY(r) ST_ABI(_Y,r)
373#define ST_IX(r) {unsigned int A; GetIX(A); WrMem(A,r); break; }
374#define ST_IY(r) {unsigned int A; GetIYWR(A); WrMem(A,r); break; }
375
376static uint8 CycTable[256] =
377{
378/*0x00*/ 7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,
379/*0x10*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
380/*0x20*/ 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,
381/*0x30*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
382/*0x40*/ 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,
383/*0x50*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
384/*0x60*/ 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,
385/*0x70*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
386/*0x80*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
387/*0x90*/ 2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,
388/*0xA0*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
389/*0xB0*/ 2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,
390/*0xC0*/ 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,
391/*0xD0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
392/*0xE0*/ 2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6,
393/*0xF0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
394};
395
396void FASTAPASS(1) X6502_IRQBegin_c(int w)
397{
398 _IRQlow|=w;
399}
400
401void FASTAPASS(1) X6502_IRQEnd_c(int w)
402{
403 _IRQlow&=~w;
404}
405
406void TriggerIRQ_c(void) /* This function should probably be phased out. */
407{
408 _IRQlow|=FCEU_IQTEMP;
409}
410
411void TriggerNMINSF_c(void)
412{
413 ADDCYC(7);
414 PUSH(_PC>>8);
415 PUSH(_PC);
416 PUSH((_P&~B_FLAG)|(U_FLAG));
417 _PC=0x3800;
418}
419
420void TriggerNMI_c(void)
421{
422 _IRQlow|=FCEU_IQNMI;
423}
424
425static void TriggerNMIReal(void)
426{
427 if(!_jammed)
428 {
429 ADDCYC(7);
430 PUSH(_PC>>8);
431 PUSH(_PC);
432 _P&=~B_FLAG;
433 PUSH(_P|U_FLAG);
434 _PC=RdMem(0xFFFA);
435 _PC|=RdMem(0xFFFB)<<8;
436#ifdef DEBUG_ASM_6502
437 PC_prev = _PC;
438 OP_prev = 0x100;
439#endif
440 }
441}
442
443void TriggerIRQReal(void)
444{
445 if(!(_PI&I_FLAG) && !_jammed)
446 {
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 if(FCEUGameInfo.type==GIT_NSF) _PC=0x3830;
467 _jammed=0;
468 _PI=_P=I_FLAG;
469}
470
471void X6502_Power_c(void)
472{
473 memset((void *)&X,0,sizeof(X));
474 timestamp=0;
475 X6502_Reset_c();
476}
477
478
479//int asdc = 0;
480void X6502_Run_c(void/*int32 cycles*/)
481{
482/*
483 if(PAL)
484 cycles*=15; // 15*4=60
485 else
486 cycles*=16; // 16*4=64
487
488 _count+=cycles;
489*/
490// if (_count <= 0) asdc++;
491
492 while(_count>0)
493 {
494 int32 temp;
495 uint8 b1;
496
497 if(_IRQlow)
498 {
499 if(_IRQlow&FCEU_IQNMI)
500 TriggerNMIReal();
501 else
502 TriggerIRQReal();
503
504 _IRQlow&=~(FCEU_IQTEMP|FCEU_IQNMI);
505 if(_count<=0)
506 {
507#ifdef DEBUG_ASM_6502
508 if(MapIRQHook) mapirq_cyc_c = _tcount;
509 _tcount=0;
510#endif
511 _PI=_P;
512 return; /* Should increase accuracy without a major speed hit. */
513 }
514 }
515 _PI=_P;
516 b1=RdMem(_PC);
517 ADDCYC(CycTable[b1]);
518 temp=_tcount;
519
520 temp*=48;
521
522 fhcnt-=temp;
523 if(fhcnt<=0)
524 {
525 FrameSoundUpdate();
526 fhcnt+=fhinc;
527 }
528
529 if(PCMIRQCount>0)
530 {
531 PCMIRQCount-=temp;
532 if(PCMIRQCount<=0)
533 {
534 vdis=1;
535 if((PSG[0x10]&0x80) && !(PSG[0x10]&0x40))
536 {
537 extern uint8 SIRQStat;
538 SIRQStat|=0x80;
539 X6502_IRQBegin_c(FCEU_IQDPCM);
540 }
541 }
542 }
543
544#ifdef DEBUG_ASM_6502
545 PC_prev = _PC;
546 OP_prev = b1;
547#endif
548 //printf("$%04x:$%02x\n",_PC,b1);
549 //_PC++;
550 //printf("$%02x\n",b1);
551 _PC++;
552 switch(b1)
553 {
554 #include "ops.h"
555 }
556
557 temp=_tcount; /* Gradius II (J) glitches if _tcount is not used */
558 _tcount=0;
559
560 if(MapIRQHook) {
561#ifdef DEBUG_ASM_6502
562 mapirq_cyc_c = temp;
563#endif
564 MapIRQHook(temp);
565 }
566
567#ifdef DEBUG_ASM_6502
568 _PI=_P;
569#endif
570 }
571}
572
573