release r2, update credits
[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 ADDCYC(x) \
48{ \
49 int __x=x; \
50 _tcount+=__x; \
51 _count-=__x*48; \
52 timestamp+=__x; \
53}
54
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
62//#define _PZ X.PZ // unused?
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{
72 int _DB1=ARead[A](A);
73#ifdef DEBUG_ASM_6502
74 //_DB=_DB1;
75 //printf("a == %x, pc == %x\n", A, _PC);
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 }
82 if (A >= 0x2000 && A != _PC - 1) {
83 dreads[dread_count_c++] = _DB1;
84 if (dread_count_c > 4) { printf("dread_count out of range\n"); exit(1); }
85 }
86#else
87 _DB=_DB1;
88#endif
89 return _DB1;
90}
91
92static INLINE void WrMem(unsigned int A, uint8 V)
93{
94 //printf("w [%04x] %02x\n", A, V);
95 if ((A&0xe000) == 0) { // RAM area (always 0-0x1fff)
96 RAM[A&0x7FF] = V;
97 return;
98 }
99 BWrite[A](A,V);
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
104}
105
106static INLINE uint8 RdRAM(unsigned int A)
107{
108 //return((_DB=RAM[A]));
109 int _DB1=RAM[A];
110#ifndef DEBUG_ASM_6502
111 _DB=_DB1;
112#endif
113 return _DB1;
114}
115
116static INLINE void WrRAM(unsigned int A, uint8 V)
117{
118 RAM[A]=V;
119}
120
121uint8 X6502_DMR(uint32 A)
122{
123 ADDCYC(1);
124 return(X.DB=ARead[A](A));
125}
126
127void X6502_DMW(uint32 A, uint8 V)
128{
129 ADDCYC(1);
130 BWrite[A](A,V);
131 #ifdef _S9XLUA_H
132 CallRegisteredLuaMemHook(A, 1, V, LUAMEMHOOK_WRITE);
133 #endif
134}
135
136void FASTAPASS(1) X6502_AddCycles_c(int x)
137{
138 ADDCYC(x);
139}
140
141#define PUSH(V) \
142{ \
143 uint8 VTMP=V; \
144 WrRAM(0x100+_S,VTMP); \
145 _S--; \
146}
147
148#define POP() RdRAM(0x100+(++_S))
149
150static uint8 ZNTable[256];
151/* Some of these operations will only make sense if you know what the flag
152 constants are. */
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++; \
172}
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 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; \
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 _PC++; \
259 target|=RdMem(_PC)<<8; \
260 _PC++; \
261}
262
263/* Absolute Indexed(for reads) */
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 } \
276}
277
278/* Absolute Indexed(for writes and rmws) */
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)); \
287}
288
289/* Zero Page */
290#define GetZP(target) \
291{ \
292 target=RdMem(_PC); \
293 _PC++; \
294}
295
296/* Zero Page Indexed */
297#define GetZPI(target,i) \
298{ \
299 target=i+RdMem(_PC); \
300 _PC++; \
301}
302
303/* Indexed Indirect */
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; \
313}
314
315/* Indirect Indexed(for reads) */
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 } \
333}
334
335/* Indirect Indexed(for writes and rmws) */
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)); \
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; }
359#define RMW_ABX(op) RMW_ABI(_X,op)
360#define RMW_ABY(op) RMW_ABI(_Y,op)
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
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;}
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;}
370#define LD_AB(op) {unsigned int A; uint8 x; GetAB(A); x=RdMem(A); op; break; }
371#define LD_ABI(reg,op) {unsigned int A; uint8 x; GetABIRD(A,reg); x=RdMem(A); op; break;}
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; }
386
387static uint8 CycTable[256] =
388{
389/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
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
408void FASTAPASS(1) X6502_IRQBegin_c(int w)
409{
410 _IRQlow|=w;
411}
412
413void FASTAPASS(1) X6502_IRQEnd_c(int w)
414{
415 _IRQlow&=~w;
416}
417
418void TriggerIRQ_c(void) /* This function should probably be phased out. */
419{
420 _IRQlow|=FCEU_IQTEMP;
421}
422
423void TriggerNMI_c(void)
424{
425 _IRQlow|=FCEU_IQNMI;
426}
427
428void TriggerNMI2(void)
429{
430 //_IRQlow|=FCEU_IQNMI2;
431 _IRQlow|=FCEU_IQNMI;
432}
433
434void X6502_Reset_c(void)
435{
436 //_IRQlow=FCEU_IQRESET;
437 _PC=RdMem(0xFFFC);
438 _PC|=RdMem(0xFFFD)<<8;
439 _jammed=0;
440 _PI=_P=I_FLAG;
441}
442
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
470void X6502_Power_c(void)
471{
472 memset((void *)&X,0,sizeof(X));
473 timestamp=0;
474 X6502_Reset_c();
475}
476
477void X6502_Run_c(void/*int32 cycles*/)
478{
479#if 0
480 if(PAL)
481 cycles*=15; // 15*4=60
482 else
483 cycles*=16; // 16*4=64
484
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;
506#ifdef DEBUG_ASM_6502
507 PC_prev = _PC;
508 OP_prev = 0x100;
509#endif
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;
523#ifdef DEBUG_ASM_6502
524 PC_prev = _PC;
525 OP_prev = 0x101;
526#endif
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 }
541
542 _PI=_P;
543#ifdef DEBUG_ASM_6502
544 b1=RdMem(_PC++); _PC--;
545#else
546 b1=RdMem(_PC);
547#endif
548
549 ADDCYC(CycTable[b1]);
550
551 temp=_tcount; /* Gradius II (J) glitches if _tcount is not used */
552 _tcount=0;
553 if(MapIRQHook) {
554#ifdef DEBUG_ASM_6502
555 mapirq_cyc_c = temp;
556#endif
557 MapIRQHook(temp);
558 }
559 FCEU_SoundCPUHook(temp*48);
560
561#ifdef DEBUG_ASM_6502
562 PC_prev = _PC;
563 OP_prev = b1;
564#endif
565 _PC++;
566 switch(b1)
567 {
568 #include "ops.h"
569 }
570#ifdef DEBUG_ASM_6502
571 _PI=_P;
572#endif
573 }
574}
575