release r2, update credits
[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
c0bf6f9f 29#ifdef DEBUG_ASM_6502
81bd66a1 30#include <stdio.h>
31#include <stdlib.h>
c0bf6f9f 32extern uint32 PC_prev, OP_prev;
81bd66a1 33extern uint8 dreads[4];
34extern uint32 dwrites_c[2];
35extern int dread_count_c, dwrite_count_c;
370cff9a 36extern int mapirq_cyc_c;
37extern void (*MapIRQHook)(int a);
81bd66a1 38#define DummyRdMem(...)
39#else
40#define DummyRdMem RdMem
370cff9a 41void FP_FASTAPASS(1) (*MapIRQHook)(int a);
c0bf6f9f 42#endif
43
c62d2810 44X6502 X;
45uint32 timestamp;
c62d2810 46
3ac1cc0b 47#define ADDCYC(x) \
48{ \
49 int __x=x; \
50 _tcount+=__x; \
51 _count-=__x*48; \
52 timestamp+=__x; \
53}
54
c62d2810 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
92e249b1 62//#define _PZ X.PZ // unused?
c62d2810 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
70static INLINE uint8 RdMem(unsigned int A)
71{
0b65fdb3 72 int _DB1=ARead[A](A);
c0bf6f9f 73#ifdef DEBUG_ASM_6502
e7f52878 74 //_DB=_DB1;
81bd66a1 75 //printf("a == %x, pc == %x\n", A, _PC);
e7f52878 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 }
e5f8a1a9 82 if (A >= 0x2000 && A != _PC - 1) {
0b65fdb3 83 dreads[dread_count_c++] = _DB1;
81bd66a1 84 if (dread_count_c > 4) { printf("dread_count out of range\n"); exit(1); }
85 }
e7f52878 86#else
87 _DB=_DB1;
c0bf6f9f 88#endif
0b65fdb3 89 return _DB1;
c62d2810 90}
91
92static INLINE void WrMem(unsigned int A, uint8 V)
93{
13a7da55 94 //printf("w [%04x] %02x\n", A, V);
937bf65b 95 if ((A&0xe000) == 0) { // RAM area (always 0-0x1fff)
96 RAM[A&0x7FF] = V;
97 return;
98 }
c62d2810 99 BWrite[A](A,V);
81bd66a1 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
c62d2810 104}
105
106static INLINE uint8 RdRAM(unsigned int A)
107{
0b65fdb3 108 //return((_DB=RAM[A]));
e7f52878 109 int _DB1=RAM[A];
110#ifndef DEBUG_ASM_6502
111 _DB=_DB1;
112#endif
113 return _DB1;
c62d2810 114}
115
116static INLINE void WrRAM(unsigned int A, uint8 V)
117{
118 RAM[A]=V;
119}
120
3ac1cc0b 121uint8 X6502_DMR(uint32 A)
c62d2810 122{
3ac1cc0b 123 ADDCYC(1);
124 return(X.DB=ARead[A](A));
c62d2810 125}
126
3ac1cc0b 127void X6502_DMW(uint32 A, uint8 V)
c62d2810 128{
3ac1cc0b 129 ADDCYC(1);
130 BWrite[A](A,V);
131 #ifdef _S9XLUA_H
132 CallRegisteredLuaMemHook(A, 1, V, LUAMEMHOOK_WRITE);
133 #endif
c62d2810 134}
135
3ac1cc0b 136void FASTAPASS(1) X6502_AddCycles_c(int x)
c62d2810 137{
3ac1cc0b 138 ADDCYC(x);
c62d2810 139}
140
3ac1cc0b 141#define PUSH(V) \
142{ \
143 uint8 VTMP=V; \
144 WrRAM(0x100+_S,VTMP); \
145 _S--; \
146}
c62d2810 147
3ac1cc0b 148#define POP() RdRAM(0x100+(++_S))
149
150static uint8 ZNTable[256];
c62d2810 151/* Some of these operations will only make sense if you know what the flag
152 constants are. */
3ac1cc0b 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++; \
c62d2810 172}
173
3ac1cc0b 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)
c62d2810 178
179/* All of the freaky arithmetic operations. */
3ac1cc0b 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; \
c62d2810 208 }
209
210/* Special undocumented operation. Very similar to CMP. */
3ac1cc0b 211#define AXS { \
c62d2810 212 uint32 t=(_A&_X)-x; \
213 X_ZN(t&0xFF); \
214 _P&=~C_FLAG; \
3ac1cc0b 215 _P|=((t>>8)&C_FLAG)^C_FLAG; \
216 _X=t; \
217 }
c62d2810 218
3ac1cc0b 219#define CMP CMPL(_A,x)
220#define CPX CMPL(_X,x)
221#define CPY CMPL(_Y,x)
c62d2810 222
223/* The following operations modify the byte being worked on. */
3ac1cc0b 224#define DEC x--;X_ZN(x)
225#define INC x++;X_ZN(x)
c62d2810 226
3ac1cc0b 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)
c62d2810 229
230/* For undocumented instructions, maybe for other things later... */
3ac1cc0b 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); \
c62d2810 248 }
3ac1cc0b 249
c62d2810 250/* Icky icky thing for some undocumented instructions. Can easily be
251 broken if names of local variables are changed.
252*/
253
254/* Absolute */
3ac1cc0b 255#define GetAB(target) \
256{ \
257 target=RdMem(_PC); \
258 _PC++; \
259 target|=RdMem(_PC)<<8; \
260 _PC++; \
c62d2810 261}
262
263/* Absolute Indexed(for reads) */
3ac1cc0b 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 } \
c62d2810 276}
277
278/* Absolute Indexed(for writes and rmws) */
3ac1cc0b 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)); \
c62d2810 287}
288
289/* Zero Page */
3ac1cc0b 290#define GetZP(target) \
291{ \
292 target=RdMem(_PC); \
293 _PC++; \
c62d2810 294}
295
296/* Zero Page Indexed */
3ac1cc0b 297#define GetZPI(target,i) \
298{ \
299 target=i+RdMem(_PC); \
300 _PC++; \
c62d2810 301}
302
303/* Indexed Indirect */
3ac1cc0b 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; \
c62d2810 313}
314
315/* Indirect Indexed(for reads) */
3ac1cc0b 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 } \
c62d2810 333}
334
335/* Indirect Indexed(for writes and rmws) */
3ac1cc0b 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)); \
c62d2810 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; }
3ac1cc0b 359#define RMW_ABX(op) RMW_ABI(_X,op)
360#define RMW_ABY(op) RMW_ABI(_Y,op)
c62d2810 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
3ac1cc0b 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;}
c62d2810 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;}
3ac1cc0b 370#define LD_AB(op) {unsigned int A; uint8 x; GetAB(A); x=RdMem(A); op; break; }
c62d2810 371#define LD_ABI(reg,op) {unsigned int A; uint8 x; GetABIRD(A,reg); x=RdMem(A); op; break;}
3ac1cc0b 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; }
c62d2810 386
387static uint8 CycTable[256] =
937bf65b 388{
e5f8a1a9 389/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
c62d2810 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
af32b6c2 408void FASTAPASS(1) X6502_IRQBegin_c(int w)
c62d2810 409{
410 _IRQlow|=w;
411}
412
af32b6c2 413void FASTAPASS(1) X6502_IRQEnd_c(int w)
c62d2810 414{
415 _IRQlow&=~w;
416}
417
af32b6c2 418void TriggerIRQ_c(void) /* This function should probably be phased out. */
c62d2810 419{
420 _IRQlow|=FCEU_IQTEMP;
421}
422
af32b6c2 423void TriggerNMI_c(void)
c62d2810 424{
425 _IRQlow|=FCEU_IQNMI;
426}
427
3ac1cc0b 428void TriggerNMI2(void)
429{
430 //_IRQlow|=FCEU_IQNMI2;
431 _IRQlow|=FCEU_IQNMI;
c62d2810 432}
433
af32b6c2 434void X6502_Reset_c(void)
c62d2810 435{
3ac1cc0b 436 //_IRQlow=FCEU_IQRESET;
c62d2810 437 _PC=RdMem(0xFFFC);
438 _PC|=RdMem(0xFFFD)<<8;
c62d2810 439 _jammed=0;
440 _PI=_P=I_FLAG;
441}
442
3ac1cc0b 443/**
444* Initializes the 6502 CPU
445**/
446void 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
af32b6c2 470void X6502_Power_c(void)
c62d2810 471{
937bf65b 472 memset((void *)&X,0,sizeof(X));
c62d2810 473 timestamp=0;
af32b6c2 474 X6502_Reset_c();
c62d2810 475}
476
af32b6c2 477void X6502_Run_c(void/*int32 cycles*/)
c62d2810 478{
3ac1cc0b 479#if 0
480 if(PAL)
481 cycles*=15; // 15*4=60
482 else
483 cycles*=16; // 16*4=64
c62d2810 484
3ac1cc0b 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;
370cff9a 506#ifdef DEBUG_ASM_6502
3ac1cc0b 507 PC_prev = _PC;
508 OP_prev = 0x100;
370cff9a 509#endif
3ac1cc0b 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;
e5f8a1a9 523#ifdef DEBUG_ASM_6502
3ac1cc0b 524 PC_prev = _PC;
525 OP_prev = 0x101;
e5f8a1a9 526#endif
3ac1cc0b 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 }
92e249b1 541
3ac1cc0b 542 _PI=_P;
c0bf6f9f 543#ifdef DEBUG_ASM_6502
3ac1cc0b 544 b1=RdMem(_PC++); _PC--;
545#else
546 b1=RdMem(_PC);
c0bf6f9f 547#endif
3ac1cc0b 548
549 ADDCYC(CycTable[b1]);
550
551 temp=_tcount; /* Gradius II (J) glitches if _tcount is not used */
552 _tcount=0;
553 if(MapIRQHook) {
370cff9a 554#ifdef DEBUG_ASM_6502
3ac1cc0b 555 mapirq_cyc_c = temp;
370cff9a 556#endif
3ac1cc0b 557 MapIRQHook(temp);
558 }
559 FCEU_SoundCPUHook(temp*48);
370cff9a 560
af32b6c2 561#ifdef DEBUG_ASM_6502
3ac1cc0b 562 PC_prev = _PC;
563 OP_prev = b1;
af32b6c2 564#endif
3ac1cc0b 565 _PC++;
566 switch(b1)
567 {
568 #include "ops.h"
569 }
570#ifdef DEBUG_ASM_6502
571 _PI=_P;
572#endif
573 }
c62d2810 574}
937bf65b 575