1 |
dpavlin |
23 |
/** M6502: portable 6502 emulator ****************************/ |
2 |
|
|
/** **/ |
3 |
|
|
/** M6502.c **/ |
4 |
|
|
/** **/ |
5 |
|
|
/** This file contains implementation for 6502 CPU. Don't **/ |
6 |
|
|
/** forget to provide Rd6502(), Wr6502(), Loop6502(), and **/ |
7 |
|
|
/** possibly Op6502() functions to accomodate the emulated **/ |
8 |
|
|
/** machine's architecture. **/ |
9 |
|
|
/** **/ |
10 |
|
|
/** Copyright (C) Marat Fayzullin 1996-2007 **/ |
11 |
|
|
/** Alex Krasivsky 1996 **/ |
12 |
|
|
/** You are not allowed to distribute this software **/ |
13 |
|
|
/** commercially. Please, notify me, if you make any **/ |
14 |
|
|
/** changes to this file. **/ |
15 |
|
|
/*************************************************************/ |
16 |
|
|
|
17 |
|
|
#include "M6502.h" |
18 |
|
|
#include "Tables.h" |
19 |
|
|
#include <stdio.h> |
20 |
|
|
|
21 |
|
|
/** INLINE ***************************************************/ |
22 |
|
|
/** C99 standard has "inline", but older compilers used **/ |
23 |
|
|
/** __inline for the same purpose. **/ |
24 |
|
|
/*************************************************************/ |
25 |
|
|
#ifdef __C99__ |
26 |
|
|
#define INLINE static inline |
27 |
|
|
#else |
28 |
|
|
#define INLINE static __inline |
29 |
|
|
#endif |
30 |
|
|
|
31 |
|
|
/** System-Dependent Stuff ***********************************/ |
32 |
|
|
/** This is system-dependent code put here to speed things **/ |
33 |
|
|
/** up. It has to stay inlined to be fast. **/ |
34 |
|
|
/*************************************************************/ |
35 |
|
|
#ifdef INES |
36 |
|
|
#define FAST_RDOP |
37 |
|
|
extern byte *Page[]; |
38 |
|
|
INLINE byte Op6502(register word A) |
39 |
|
|
{ |
40 |
|
|
#ifndef S60 |
41 |
|
|
return(Page[A>>13][A&0x1FFF]); |
42 |
|
|
#else |
43 |
|
|
asm |
44 |
|
|
( |
45 |
|
|
"and r2,%1,#0xE000\t\n" |
46 |
|
|
"ldr r2,[%2,r2,lsr #11]\t\n" |
47 |
|
|
"mov %0,%1,lsl #19\t\n" |
48 |
|
|
"ldrb %0,[r2,%0,lsr #19]\t\n" |
49 |
|
|
: "=r"(A) |
50 |
|
|
: "0"(A),"r"(Page) |
51 |
|
|
: "r2" |
52 |
|
|
); |
53 |
|
|
return(A); |
54 |
|
|
#endif /* S60 */ |
55 |
|
|
} |
56 |
|
|
#endif /* INES */ |
57 |
|
|
|
58 |
|
|
/** FAST_RDOP ************************************************/ |
59 |
|
|
/** With this #define not present, Rd6502() should perform **/ |
60 |
|
|
/** the functions of Rd6502(). **/ |
61 |
|
|
/*************************************************************/ |
62 |
|
|
#ifndef FAST_RDOP |
63 |
|
|
#define Op6502(A) Rd6502(A) |
64 |
|
|
#endif |
65 |
|
|
|
66 |
|
|
/** Addressing Methods ***************************************/ |
67 |
|
|
/** These macros calculate and return effective addresses. **/ |
68 |
|
|
/*************************************************************/ |
69 |
|
|
#define MC_Ab(Rg) M_LDWORD(Rg) |
70 |
|
|
#define MC_Zp(Rg) Rg.W=Op6502(R->PC.W++) |
71 |
|
|
#define MC_Zx(Rg) Rg.W=(byte)(Op6502(R->PC.W++)+R->X) |
72 |
|
|
#define MC_Zy(Rg) Rg.W=(byte)(Op6502(R->PC.W++)+R->Y) |
73 |
|
|
#define MC_Ax(Rg) M_LDWORD(Rg);Rg.W+=R->X |
74 |
|
|
#define MC_Ay(Rg) M_LDWORD(Rg);Rg.W+=R->Y |
75 |
|
|
#define MC_Ix(Rg) K.W=(byte)(Op6502(R->PC.W++)+R->X); \ |
76 |
|
|
Rg.B.l=Op6502(K.W++);Rg.B.h=Op6502(K.W) |
77 |
|
|
#define MC_Iy(Rg) K.W=Op6502(R->PC.W++); \ |
78 |
|
|
Rg.B.l=Op6502(K.W++);Rg.B.h=Op6502(K.W); \ |
79 |
|
|
Rg.W+=R->Y |
80 |
|
|
|
81 |
|
|
/** Reading From Memory **************************************/ |
82 |
|
|
/** These macros calculate address and read from it. **/ |
83 |
|
|
/*************************************************************/ |
84 |
|
|
#define MR_Ab(Rg) MC_Ab(J);Rg=Rd6502(J.W) |
85 |
|
|
#define MR_Im(Rg) Rg=Op6502(R->PC.W++) |
86 |
|
|
#define MR_Zp(Rg) MC_Zp(J);Rg=Rd6502(J.W) |
87 |
|
|
#define MR_Zx(Rg) MC_Zx(J);Rg=Rd6502(J.W) |
88 |
|
|
#define MR_Zy(Rg) MC_Zy(J);Rg=Rd6502(J.W) |
89 |
|
|
#define MR_Ax(Rg) MC_Ax(J);Rg=Rd6502(J.W) |
90 |
|
|
#define MR_Ay(Rg) MC_Ay(J);Rg=Rd6502(J.W) |
91 |
|
|
#define MR_Ix(Rg) MC_Ix(J);Rg=Rd6502(J.W) |
92 |
|
|
#define MR_Iy(Rg) MC_Iy(J);Rg=Rd6502(J.W) |
93 |
|
|
|
94 |
|
|
/** Writing To Memory ****************************************/ |
95 |
|
|
/** These macros calculate address and write to it. **/ |
96 |
|
|
/*************************************************************/ |
97 |
|
|
#define MW_Ab(Rg) MC_Ab(J);Wr6502(J.W,Rg) |
98 |
|
|
#define MW_Zp(Rg) MC_Zp(J);Wr6502(J.W,Rg) |
99 |
|
|
#define MW_Zx(Rg) MC_Zx(J);Wr6502(J.W,Rg) |
100 |
|
|
#define MW_Zy(Rg) MC_Zy(J);Wr6502(J.W,Rg) |
101 |
|
|
#define MW_Ax(Rg) MC_Ax(J);Wr6502(J.W,Rg) |
102 |
|
|
#define MW_Ay(Rg) MC_Ay(J);Wr6502(J.W,Rg) |
103 |
|
|
#define MW_Ix(Rg) MC_Ix(J);Wr6502(J.W,Rg) |
104 |
|
|
#define MW_Iy(Rg) MC_Iy(J);Wr6502(J.W,Rg) |
105 |
|
|
|
106 |
|
|
/** Modifying Memory *****************************************/ |
107 |
|
|
/** These macros calculate address and modify it. **/ |
108 |
|
|
/*************************************************************/ |
109 |
|
|
#define MM_Ab(Cmd) MC_Ab(J);I=Rd6502(J.W);Cmd(I);Wr6502(J.W,I) |
110 |
|
|
#define MM_Zp(Cmd) MC_Zp(J);I=Rd6502(J.W);Cmd(I);Wr6502(J.W,I) |
111 |
|
|
#define MM_Zx(Cmd) MC_Zx(J);I=Rd6502(J.W);Cmd(I);Wr6502(J.W,I) |
112 |
|
|
#define MM_Ax(Cmd) MC_Ax(J);I=Rd6502(J.W);Cmd(I);Wr6502(J.W,I) |
113 |
|
|
|
114 |
|
|
/** Other Macros *********************************************/ |
115 |
|
|
/** Calculating flags, stack, jumps, arithmetics, etc. **/ |
116 |
|
|
/*************************************************************/ |
117 |
|
|
#define M_FL(Rg) R->P=(R->P&~(Z_FLAG|N_FLAG))|ZNTable[Rg] |
118 |
|
|
#define M_LDWORD(Rg) Rg.B.l=Op6502(R->PC.W++);Rg.B.h=Op6502(R->PC.W++) |
119 |
|
|
|
120 |
|
|
#define M_PUSH(Rg) Wr6502(0x0100|R->S,Rg);R->S-- |
121 |
|
|
#define M_POP(Rg) R->S++;Rg=Op6502(0x0100|R->S) |
122 |
|
|
#define M_JR R->PC.W+=(offset)Op6502(R->PC.W)+1;R->ICount-- |
123 |
|
|
|
124 |
|
|
#ifdef NO_DECIMAL |
125 |
|
|
|
126 |
|
|
#define M_ADC(Rg) \ |
127 |
|
|
K.W=R->A+Rg+(R->P&C_FLAG); \ |
128 |
|
|
R->P&=~(N_FLAG|V_FLAG|Z_FLAG|C_FLAG); \ |
129 |
|
|
R->P|=(~(R->A^Rg)&(R->A^K.B.l)&0x80? V_FLAG:0)| \ |
130 |
|
|
(K.B.h? C_FLAG:0)|ZNTable[K.B.l]; \ |
131 |
|
|
R->A=K.B.l |
132 |
|
|
|
133 |
|
|
/* Warning! C_FLAG is inverted before SBC and after it */ |
134 |
|
|
#define M_SBC(Rg) \ |
135 |
|
|
K.W=R->A-Rg-(~R->P&C_FLAG); \ |
136 |
|
|
R->P&=~(N_FLAG|V_FLAG|Z_FLAG|C_FLAG); \ |
137 |
|
|
R->P|=((R->A^Rg)&(R->A^K.B.l)&0x80? V_FLAG:0)| \ |
138 |
|
|
(K.B.h? 0:C_FLAG)|ZNTable[K.B.l]; \ |
139 |
|
|
R->A=K.B.l |
140 |
|
|
|
141 |
|
|
#else /* NO_DECIMAL */ |
142 |
|
|
|
143 |
|
|
#define M_ADC(Rg) \ |
144 |
|
|
if(R->P&D_FLAG) \ |
145 |
|
|
{ \ |
146 |
|
|
K.B.l=(R->A&0x0F)+(Rg&0x0F)+(R->P&C_FLAG); \ |
147 |
|
|
if(K.B.l>9) K.B.l+=6; \ |
148 |
|
|
K.B.h=(R->A>>4)+(Rg>>4)+(K.B.l>15? 1:0); \ |
149 |
|
|
R->A=(K.B.l&0x0F)|(K.B.h<<4); \ |
150 |
|
|
R->P=(R->P&~C_FLAG)|(K.B.h>15? C_FLAG:0); \ |
151 |
|
|
} \ |
152 |
|
|
else \ |
153 |
|
|
{ \ |
154 |
|
|
K.W=R->A+Rg+(R->P&C_FLAG); \ |
155 |
|
|
R->P&=~(N_FLAG|V_FLAG|Z_FLAG|C_FLAG); \ |
156 |
|
|
R->P|=(~(R->A^Rg)&(R->A^K.B.l)&0x80? V_FLAG:0)| \ |
157 |
|
|
(K.B.h? C_FLAG:0)|ZNTable[K.B.l]; \ |
158 |
|
|
R->A=K.B.l; \ |
159 |
|
|
} |
160 |
|
|
|
161 |
|
|
/* Warning! C_FLAG is inverted before SBC and after it */ |
162 |
|
|
#define M_SBC(Rg) \ |
163 |
|
|
if(R->P&D_FLAG) \ |
164 |
|
|
{ \ |
165 |
|
|
K.B.l=(R->A&0x0F)-(Rg&0x0F)-(~R->P&C_FLAG); \ |
166 |
|
|
if(K.B.l&0x10) K.B.l-=6; \ |
167 |
|
|
K.B.h=(R->A>>4)-(Rg>>4)-((K.B.l&0x10)>>4); \ |
168 |
|
|
if(K.B.h&0x10) K.B.h-=6; \ |
169 |
|
|
R->A=(K.B.l&0x0F)|(K.B.h<<4); \ |
170 |
|
|
R->P=(R->P&~C_FLAG)|(K.B.h>15? 0:C_FLAG); \ |
171 |
|
|
} \ |
172 |
|
|
else \ |
173 |
|
|
{ \ |
174 |
|
|
K.W=R->A-Rg-(~R->P&C_FLAG); \ |
175 |
|
|
R->P&=~(N_FLAG|V_FLAG|Z_FLAG|C_FLAG); \ |
176 |
|
|
R->P|=((R->A^Rg)&(R->A^K.B.l)&0x80? V_FLAG:0)| \ |
177 |
|
|
(K.B.h? 0:C_FLAG)|ZNTable[K.B.l]; \ |
178 |
|
|
R->A=K.B.l; \ |
179 |
|
|
} |
180 |
|
|
|
181 |
|
|
#endif /* NO_DECIMAL */ |
182 |
|
|
|
183 |
|
|
#define M_CMP(Rg1,Rg2) \ |
184 |
|
|
K.W=Rg1-Rg2; \ |
185 |
|
|
R->P&=~(N_FLAG|Z_FLAG|C_FLAG); \ |
186 |
|
|
R->P|=ZNTable[K.B.l]|(K.B.h? 0:C_FLAG) |
187 |
|
|
#define M_BIT(Rg) \ |
188 |
|
|
R->P&=~(N_FLAG|V_FLAG|Z_FLAG); \ |
189 |
|
|
R->P|=(Rg&(N_FLAG|V_FLAG))|(Rg&R->A? 0:Z_FLAG) |
190 |
|
|
|
191 |
|
|
#define M_AND(Rg) R->A&=Rg;M_FL(R->A) |
192 |
|
|
#define M_ORA(Rg) R->A|=Rg;M_FL(R->A) |
193 |
|
|
#define M_EOR(Rg) R->A^=Rg;M_FL(R->A) |
194 |
|
|
#define M_INC(Rg) Rg++;M_FL(Rg) |
195 |
|
|
#define M_DEC(Rg) Rg--;M_FL(Rg) |
196 |
|
|
|
197 |
|
|
#define M_ASL(Rg) R->P&=~C_FLAG;R->P|=Rg>>7;Rg<<=1;M_FL(Rg) |
198 |
|
|
#define M_LSR(Rg) R->P&=~C_FLAG;R->P|=Rg&C_FLAG;Rg>>=1;M_FL(Rg) |
199 |
|
|
#define M_ROL(Rg) K.B.l=(Rg<<1)|(R->P&C_FLAG); \ |
200 |
|
|
R->P&=~C_FLAG;R->P|=Rg>>7;Rg=K.B.l; \ |
201 |
|
|
M_FL(Rg) |
202 |
|
|
#define M_ROR(Rg) K.B.l=(Rg>>1)|(R->P<<7); \ |
203 |
|
|
R->P&=~C_FLAG;R->P|=Rg&C_FLAG;Rg=K.B.l; \ |
204 |
|
|
M_FL(Rg) |
205 |
|
|
|
206 |
|
|
/** Reset6502() **********************************************/ |
207 |
|
|
/** This function can be used to reset the registers before **/ |
208 |
|
|
/** starting execution with Run6502(). It sets registers to **/ |
209 |
|
|
/** their initial values. **/ |
210 |
|
|
/*************************************************************/ |
211 |
|
|
void Reset6502(M6502 *R) |
212 |
|
|
{ |
213 |
|
|
R->A=R->X=R->Y=0x00; |
214 |
|
|
R->P=Z_FLAG|R_FLAG; |
215 |
|
|
R->S=0xFF; |
216 |
|
|
R->PC.B.l=Rd6502(0xFFFC); |
217 |
|
|
R->PC.B.h=Rd6502(0xFFFD); |
218 |
|
|
R->ICount=R->IPeriod; |
219 |
|
|
R->IRequest=INT_NONE; |
220 |
|
|
R->AfterCLI=0; |
221 |
|
|
} |
222 |
|
|
|
223 |
|
|
/** Exec6502() ***********************************************/ |
224 |
|
|
/** This function will execute a single 6502 opcode. It **/ |
225 |
|
|
/** will then return next PC, and current register values **/ |
226 |
|
|
/** in R. **/ |
227 |
|
|
/*************************************************************/ |
228 |
|
|
#ifdef EXEC6502 |
229 |
|
|
int Exec6502(M6502 *R,int RunCycles) |
230 |
|
|
{ |
231 |
|
|
register pair J,K; |
232 |
|
|
register byte I; |
233 |
|
|
|
234 |
|
|
/* Execute requested number of cycles */ |
235 |
|
|
while(RunCycles>0) |
236 |
|
|
{ |
237 |
|
|
#ifdef DEBUG |
238 |
|
|
/* Turn tracing on when reached trap address */ |
239 |
|
|
if(R->PC.W==R->Trap) R->Trace=1; |
240 |
|
|
/* Call single-step debugger, exit if requested */ |
241 |
|
|
if(R->Trace) |
242 |
|
|
if(!Debug6502(R)) return(RunCycles); |
243 |
|
|
#endif |
244 |
|
|
|
245 |
|
|
I=Op6502(R->PC.W++); |
246 |
|
|
RunCycles-=Cycles[I]; |
247 |
|
|
switch(I) |
248 |
|
|
{ |
249 |
|
|
#include "Codes.h" |
250 |
|
|
} |
251 |
|
|
} |
252 |
|
|
|
253 |
|
|
/* Return number of cycles left (<=0) */ |
254 |
|
|
return(RunCycles); |
255 |
|
|
} |
256 |
|
|
#endif /* EXEC6502 */ |
257 |
|
|
|
258 |
|
|
/** Int6502() ************************************************/ |
259 |
|
|
/** This function will generate interrupt of a given type. **/ |
260 |
|
|
/** INT_NMI will cause a non-maskable interrupt. INT_IRQ **/ |
261 |
|
|
/** will cause a normal interrupt, unless I_FLAG set in R. **/ |
262 |
|
|
/*************************************************************/ |
263 |
|
|
void Int6502(M6502 *R,byte Type) |
264 |
|
|
{ |
265 |
|
|
register pair J; |
266 |
|
|
|
267 |
|
|
if((Type==INT_NMI)||((Type==INT_IRQ)&&!(R->P&I_FLAG))) |
268 |
|
|
{ |
269 |
|
|
R->ICount-=7; |
270 |
|
|
M_PUSH(R->PC.B.h); |
271 |
|
|
M_PUSH(R->PC.B.l); |
272 |
|
|
M_PUSH(R->P&~B_FLAG); |
273 |
|
|
R->P&=~D_FLAG; |
274 |
|
|
if(R->IAutoReset&&(Type==R->IRequest)) R->IRequest=INT_NONE; |
275 |
|
|
if(Type==INT_NMI) J.W=0xFFFA; else { R->P|=I_FLAG;J.W=0xFFFE; } |
276 |
|
|
R->PC.B.l=Rd6502(J.W++); |
277 |
|
|
R->PC.B.h=Rd6502(J.W); |
278 |
|
|
} |
279 |
|
|
} |
280 |
|
|
|
281 |
|
|
/** Run6502() ************************************************/ |
282 |
|
|
/** This function will run 6502 code until Loop6502() call **/ |
283 |
|
|
/** returns INT_QUIT. It will return the PC at which **/ |
284 |
|
|
/** emulation stopped, and current register values in R. **/ |
285 |
|
|
/*************************************************************/ |
286 |
|
|
#ifndef EXEC6502 |
287 |
|
|
word Run6502(M6502 *R) |
288 |
|
|
{ |
289 |
|
|
register pair J,K; |
290 |
|
|
register byte I; |
291 |
|
|
|
292 |
|
|
for(;;) |
293 |
|
|
{ |
294 |
|
|
#ifdef DEBUG |
295 |
|
|
/* Turn tracing on when reached trap address */ |
296 |
|
|
if(R->PC.W==R->Trap) R->Trace=1; |
297 |
|
|
/* Call single-step debugger, exit if requested */ |
298 |
|
|
if(R->Trace) |
299 |
|
|
if(!Debug6502(R)) return(R->PC.W); |
300 |
|
|
#endif |
301 |
|
|
|
302 |
|
|
I=Op6502(R->PC.W++); |
303 |
|
|
R->ICount-=Cycles[I]; |
304 |
|
|
switch(I) |
305 |
|
|
{ |
306 |
|
|
#include "Codes.h" |
307 |
|
|
} |
308 |
|
|
|
309 |
|
|
/* If cycle counter expired... */ |
310 |
|
|
if(R->ICount<=0) |
311 |
|
|
{ |
312 |
|
|
/* If we have come after CLI, get INT_? from IRequest */ |
313 |
|
|
/* Otherwise, get it from the loop handler */ |
314 |
|
|
if(R->AfterCLI) |
315 |
|
|
{ |
316 |
|
|
I=R->IRequest; /* Get pending interrupt */ |
317 |
|
|
R->ICount+=R->IBackup-1; /* Restore the ICount */ |
318 |
|
|
R->AfterCLI=0; /* Done with AfterCLI state */ |
319 |
|
|
} |
320 |
|
|
else |
321 |
|
|
{ |
322 |
|
|
I=Loop6502(R); /* Call the periodic handler */ |
323 |
|
|
R->ICount+=R->IPeriod; /* Reset the cycle counter */ |
324 |
|
|
if(!I) I=R->IRequest; /* Realize pending interrupt */ |
325 |
|
|
} |
326 |
|
|
|
327 |
|
|
if(I==INT_QUIT) return(R->PC.W); /* Exit if INT_QUIT */ |
328 |
|
|
if(I) Int6502(R,I); /* Interrupt if needed */ |
329 |
|
|
} |
330 |
|
|
} |
331 |
|
|
|
332 |
|
|
/* Execution stopped */ |
333 |
|
|
return(R->PC.W); |
334 |
|
|
} |
335 |
|
|
#endif /* !EXEC6502 */ |