1 |
dpavlin |
23 |
/** M6502: portable 6502 emulator ****************************/ |
2 |
|
|
/** **/ |
3 |
|
|
/** Debug.c **/ |
4 |
|
|
/** **/ |
5 |
|
|
/** This file contains the built-in debugging routine for **/ |
6 |
|
|
/** the 6502 emulator which is called on each 6502 step **/ |
7 |
|
|
/** when Trap!=0. Some NES-dependent features are also **/ |
8 |
|
|
/** included under #ifdef INES. **/ |
9 |
|
|
/** **/ |
10 |
|
|
/** Copyright (C) Marat Fayzullin 1996-2007 **/ |
11 |
|
|
/** You are not allowed to distribute this software **/ |
12 |
|
|
/** commercially. Please, notify me, if you make any **/ |
13 |
|
|
/** changes to this file. **/ |
14 |
|
|
/*************************************************************/ |
15 |
|
|
#ifdef DEBUG |
16 |
|
|
|
17 |
|
|
#include <stdio.h> |
18 |
|
|
#include <string.h> |
19 |
|
|
#include <ctype.h> |
20 |
|
|
|
21 |
|
|
#ifdef INES |
22 |
|
|
#include "NES.h" |
23 |
|
|
extern word VAddr; |
24 |
|
|
extern byte CURLINE; |
25 |
|
|
#endif |
26 |
|
|
|
27 |
|
|
#define RDWORD(A) (Rd6502(A+1)*256+Rd6502(A)) |
28 |
|
|
|
29 |
|
|
enum AddressingModes { Ac=0,Il,Im,Ab,Zp,Zx,Zy,Ax,Ay,Rl,Ix,Iy,In,No }; |
30 |
|
|
|
31 |
|
|
static const byte *Ops[]= |
32 |
|
|
{ |
33 |
|
|
"adc","and","asl","bcc","bcs","beq","bit","bmi", |
34 |
|
|
"bne","bpl","brk","bvc","bvs","clc","cld","cli", |
35 |
|
|
"clv","cmp","cpx","cpy","dec","dex","dey","inx", |
36 |
|
|
"iny","eor","inc","jmp","jsr","lda","nop","ldx", |
37 |
|
|
"ldy","lsr","ora","pha","php","pla","plp","rol", |
38 |
|
|
"ror","rti","rts","sbc","sta","stx","sty","sec", |
39 |
|
|
"sed","sei","tax","tay","txa","tya","tsx","txs" |
40 |
|
|
}; |
41 |
|
|
|
42 |
|
|
static const byte Ads[512]= |
43 |
|
|
{ |
44 |
|
|
10,Il, 34,Ix, No,No, No,No, No,No, 34,Zp, 2,Zp, No,No, |
45 |
|
|
36,Il, 34,Im, 2,Ac, No,No, No,No, 34,Ab, 2,Ab, No,No, |
46 |
|
|
9,Rl, 34,Iy, No,No, No,No, No,No, 34,Zx, 2,Zx, No,No, |
47 |
|
|
13,Il, 34,Ay, No,No, No,No, No,No, 34,Ax, 2,Ax, No,No, |
48 |
|
|
28,Ab, 1,Ix, No,No, No,No, 6,Zp, 1,Zp, 39,Zp, No,No, |
49 |
|
|
38,Il, 1,Im, 39,Ac, No,No, 6,Ab, 1,Ab, 39,Ab, No,No, |
50 |
|
|
7,Rl, 1,Iy, No,No, No,No, No,No, 1,Zx, 39,Zx, No,No, |
51 |
|
|
47,Il, 1,Ay, No,No, No,No, No,No, 1,Ax, 39,Ax, No,No, |
52 |
|
|
41,Il, 25,Ix, No,No, No,No, No,No, 25,Zp, 33,Zp, No,No, |
53 |
|
|
35,Il, 25,Im, 33,Ac, No,No, 27,Ab, 25,Ab, 33,Ab, No,No, |
54 |
|
|
11,Rl, 25,Iy, No,No, No,No, No,No, 25,Zx, 33,Zx, No,No, |
55 |
|
|
15,Il, 25,Ay, No,No, No,No, No,No, 25,Ax, 33,Ax, No,No, |
56 |
|
|
42,Il, 0,Ix, No,No, No,No, No,No, 0,Zp, 40,Zp, No,No, |
57 |
|
|
37,Il, 0,Im, 40,Ac, No,No, 27,In, 0,Ab, 40,Ab, No,No, |
58 |
|
|
12,Rl, 0,Iy, No,No, No,No, No,No, 0,Zx, 40,Zx, No,No, |
59 |
|
|
49,Il, 0,Ay, No,No, No,No, No,No, 0,Ax, 40,Ax, No,No, |
60 |
|
|
No,No, 44,Ix, No,No, No,No, 46,Zp, 44,Zp, 45,Zp, No,No, |
61 |
|
|
22,Il, No,No, 52,Il, No,No, 46,Ab, 44,Ab, 45,Ab, No,No, |
62 |
|
|
3,Rl, 44,Iy, No,No, No,No, 46,Zx, 44,Zx, 45,Zy, No,No, |
63 |
|
|
53,Il, 44,Ay, 55,Il, No,No, No,No, 44,Ax, No,No, No,No, |
64 |
|
|
32,Im, 29,Ix, 31,Im, No,No, 32,Zp, 29,Zp, 31,Zp, No,No, |
65 |
|
|
51,Il, 29,Im, 50,Il, No,No, 32,Ab, 29,Ab, 31,Ab, No,No, |
66 |
|
|
4,Rl, 29,Iy, No,No, No,No, 32,Zx, 29,Zx, 31,Zy, No,No, |
67 |
|
|
16,Il, 29,Ay, 54,Il, No,No, 32,Ax, 29,Ax, 31,Ay, No,No, |
68 |
|
|
19,Im, 17,Ix, No,No, No,No, 19,Zp, 17,Zp, 20,Zp, No,No, |
69 |
|
|
24,Il, 17,Im, 21,Il, No,No, 19,Ab, 17,Ab, 20,Ab, No,No, |
70 |
|
|
8,Rl, 17,Iy, No,No, No,No, No,No, 17,Zx, 20,Zx, No,No, |
71 |
|
|
14,Il, 17,Ay, No,No, No,No, No,No, 17,Ax, 20,Ax, No,No, |
72 |
|
|
18,Im, 43,Ix, No,No, No,No, 18,Zp, 43,Zp, 26,Zp, No,No, |
73 |
|
|
23,Il, 43,Im, 30,Il, No,No, 18,Ab, 43,Ab, 26,Ab, No,No, |
74 |
|
|
5,Rl, 43,Iy, No,No, No,No, No,No, 43,Zx, 26,Zx, No,No, |
75 |
|
|
48,Il, 43,Ay, No,No, No,No, No,No, 43,Ax, 26,Ax, No,No |
76 |
|
|
}; |
77 |
|
|
|
78 |
|
|
/** DAsm() ****************************************************/ |
79 |
|
|
/** This function will disassemble a single command and **/ |
80 |
|
|
/** return the number of bytes disassembled. **/ |
81 |
|
|
/**************************************************************/ |
82 |
|
|
static int DAsm(char *S,word A) |
83 |
|
|
{ |
84 |
|
|
byte J; |
85 |
|
|
word B,OP,TO; |
86 |
|
|
|
87 |
|
|
B=A;OP=Rd6502(B++)*2; |
88 |
|
|
|
89 |
|
|
switch(Ads[OP+1]) |
90 |
|
|
{ |
91 |
|
|
case Ac: sprintf(S,"%s a",Ops[Ads[OP]]);break; |
92 |
|
|
case Il: sprintf(S,"%s",Ops[Ads[OP]]);break; |
93 |
|
|
|
94 |
|
|
case Rl: J=Rd6502(B++);TO=A+2+((J<0x80)? J:(J-256)); |
95 |
|
|
sprintf(S,"%s $%04X",Ops[Ads[OP]],TO);break; |
96 |
|
|
|
97 |
|
|
case Im: sprintf(S,"%s #$%02X",Ops[Ads[OP]],Rd6502(B++));break; |
98 |
|
|
case Zp: sprintf(S,"%s $%02X",Ops[Ads[OP]],Rd6502(B++));break; |
99 |
|
|
case Zx: sprintf(S,"%s $%02X,x",Ops[Ads[OP]],Rd6502(B++));break; |
100 |
|
|
case Zy: sprintf(S,"%s $%02X,y",Ops[Ads[OP]],Rd6502(B++));break; |
101 |
|
|
case Ix: sprintf(S,"%s ($%02X,x)",Ops[Ads[OP]],Rd6502(B++));break; |
102 |
|
|
case Iy: sprintf(S,"%s ($%02X),y",Ops[Ads[OP]],Rd6502(B++));break; |
103 |
|
|
|
104 |
|
|
case Ab: sprintf(S,"%s $%04X",Ops[Ads[OP]],RDWORD(B));B+=2;break; |
105 |
|
|
case Ax: sprintf(S,"%s $%04X,x",Ops[Ads[OP]],RDWORD(B));B+=2;break; |
106 |
|
|
case Ay: sprintf(S,"%s $%04X,y",Ops[Ads[OP]],RDWORD(B));B+=2;break; |
107 |
|
|
case In: sprintf(S,"%s ($%04X)",Ops[Ads[OP]],RDWORD(B));B+=2;break; |
108 |
|
|
|
109 |
|
|
default: sprintf(S,".db $%02X",OP/2); |
110 |
|
|
} |
111 |
|
|
return(B-A); |
112 |
|
|
} |
113 |
|
|
|
114 |
|
|
/** Debug6502() **********************************************/ |
115 |
|
|
/** This function should exist if DEBUG is #defined. When **/ |
116 |
|
|
/** Trace!=0, it is called after each command executed by **/ |
117 |
|
|
/** the CPU, and given the 6502 registers. Emulation exits **/ |
118 |
|
|
/** if Debug6502() returns 0. **/ |
119 |
|
|
/*************************************************************/ |
120 |
|
|
byte Debug6502(M6502 *R) |
121 |
|
|
{ |
122 |
|
|
static char FA[9]="NVRBDIZC"; |
123 |
|
|
char S[128]; |
124 |
|
|
byte *P,F; |
125 |
|
|
int J,I,K; |
126 |
|
|
|
127 |
|
|
DAsm(S,R->PC.W); |
128 |
|
|
|
129 |
|
|
printf |
130 |
|
|
( |
131 |
|
|
"A:%02X P:%02X X:%02X Y:%02X S:%04X PC:%04X Flags:[", |
132 |
|
|
R->A,R->P,R->X,R->Y,R->S+0x0100,R->PC.W |
133 |
|
|
); |
134 |
|
|
|
135 |
|
|
for(J=0,F=R->P;J<8;J++,F<<=1) |
136 |
|
|
printf("%c",F&0x80? FA[J]:'.'); |
137 |
|
|
printf("]\n"); |
138 |
|
|
|
139 |
|
|
printf |
140 |
|
|
( |
141 |
|
|
"AT PC: [%02X - %s] AT SP: [%02X %02X %02X]\n", |
142 |
|
|
Rd6502(R->PC.W),S, |
143 |
|
|
Rd6502(0x0100+(byte)(R->S+1)), |
144 |
|
|
Rd6502(0x0100+(byte)(R->S+2)), |
145 |
|
|
Rd6502(0x0100+(byte)(R->S+3)) |
146 |
|
|
); |
147 |
|
|
|
148 |
|
|
while(1) |
149 |
|
|
{ |
150 |
|
|
printf("\n[Command,'?']-> "); |
151 |
|
|
fflush(stdout);fflush(stdin); |
152 |
|
|
|
153 |
|
|
fgets(S,50,stdin); |
154 |
|
|
for(J=0;S[J]>=' ';J++) |
155 |
|
|
S[J]=toupper(S[J]); |
156 |
|
|
S[J]='\0'; |
157 |
|
|
|
158 |
|
|
switch(S[0]) |
159 |
|
|
{ |
160 |
|
|
case 'H': |
161 |
|
|
case '?': |
162 |
|
|
printf("\n***** Built-in 6502 Debugger Commands *****\n"); |
163 |
|
|
printf("<CR> : Break at the next instruction\n"); |
164 |
|
|
printf("= <addr> : Break at addr\n"); |
165 |
|
|
printf("+ <offset> : Break at PC + offset\n"); |
166 |
|
|
printf("c : Continue without break\n"); |
167 |
|
|
printf("j <addr> : Continue from addr\n"); |
168 |
|
|
printf("m <addr> : Memory dump at addr\n"); |
169 |
|
|
printf("d <addr> : Disassembly at addr\n"); |
170 |
|
|
printf("v : Show interrupt vectors\n"); |
171 |
|
|
#ifdef INES |
172 |
|
|
printf("p : Show NES palette\n"); |
173 |
|
|
printf("r : Show NES hardware registers\n"); |
174 |
|
|
printf("s : Show sprite attributes\n"); |
175 |
|
|
printf("s <number> : Show sprite pattern\n"); |
176 |
|
|
#endif /* INES */ |
177 |
|
|
printf("?,h : Show this help text\n"); |
178 |
|
|
printf("q : Exit emulation\n"); |
179 |
|
|
break; |
180 |
|
|
|
181 |
|
|
case '\0': return(1); |
182 |
|
|
case '=': if(strlen(S)>=2) |
183 |
|
|
{ sscanf(S+1,"%hX",&(R->Trap));R->Trace=0;return(1); } |
184 |
|
|
break; |
185 |
|
|
case '+': if(strlen(S)>=2) |
186 |
|
|
{ |
187 |
|
|
sscanf(S+1,"%hX",&(R->Trap)); |
188 |
|
|
R->Trap+=R->PC.W;R->Trace=0; |
189 |
|
|
return(1); |
190 |
|
|
} |
191 |
|
|
break; |
192 |
|
|
case 'J': if(strlen(S)>=2) |
193 |
|
|
{ sscanf(S+1,"%hX",&(R->PC.W));R->Trace=0;return(1); } |
194 |
|
|
break; |
195 |
|
|
case 'C': R->Trap=0xFFFF;R->Trace=0;return(1); |
196 |
|
|
case 'Q': return(0); |
197 |
|
|
|
198 |
|
|
case 'V': |
199 |
|
|
printf("\n6502 Interrupt Vectors:\n"); |
200 |
|
|
printf("[$FFFC] INIT: $%04X\n",Rd6502(0xFFFC)+256*Rd6502(0xFFFD)); |
201 |
|
|
printf("[$FFFE] IRQ: $%04X\n",Rd6502(0xFFFE)+256*Rd6502(0xFFFF)); |
202 |
|
|
printf("[$FFFA] NMI: $%04X\n",Rd6502(0xFFFA)+256*Rd6502(0xFFFB)); |
203 |
|
|
break; |
204 |
|
|
|
205 |
|
|
case 'M': |
206 |
|
|
{ |
207 |
|
|
word Addr; |
208 |
|
|
|
209 |
|
|
if(sscanf(S+1,"%hX",&Addr)!=1) Addr=R->PC.W; |
210 |
|
|
printf("\n"); |
211 |
|
|
for(J=0;J<16;J++) |
212 |
|
|
{ |
213 |
|
|
printf("%04X: ",Addr); |
214 |
|
|
for(I=0;I<16;I++,Addr++) |
215 |
|
|
printf("%02X ",Rd6502(Addr)); |
216 |
|
|
printf(" | ");Addr-=16; |
217 |
|
|
for(I=0;I<16;I++,Addr++) |
218 |
|
|
printf("%c",isprint(Rd6502(Addr))? Rd6502(Addr):'.'); |
219 |
|
|
printf("\n"); |
220 |
|
|
} |
221 |
|
|
} |
222 |
|
|
break; |
223 |
|
|
|
224 |
|
|
case 'D': |
225 |
|
|
{ |
226 |
|
|
word Addr; |
227 |
|
|
|
228 |
|
|
if(sscanf(S+1,"%hX",&Addr)!=1) Addr=R->PC.W; |
229 |
|
|
printf("\n"); |
230 |
|
|
for(J=0;J<16;J++) |
231 |
|
|
{ |
232 |
|
|
printf("%04X: ",Addr); |
233 |
|
|
Addr+=DAsm(S,Addr); |
234 |
|
|
printf("%s\n",S); |
235 |
|
|
} |
236 |
|
|
} |
237 |
|
|
break; |
238 |
|
|
|
239 |
|
|
#ifdef INES |
240 |
|
|
case 'P': |
241 |
|
|
printf("\nNo | Picture | Sprites\n"); |
242 |
|
|
for(J=0;J<16;J++) |
243 |
|
|
{ |
244 |
|
|
byte B1,B2; |
245 |
|
|
B1=*(VRAM+0x3F00+J);B2=*(VRAM+0x3F10+J); |
246 |
|
|
printf |
247 |
|
|
( |
248 |
|
|
"%2d | %02X (%02X,%02X,%02X) | %02X (%02X,%02X,%02X)\n",J, |
249 |
|
|
B1,Palette[B1][0],Palette[B1][1],Palette[B1][2], |
250 |
|
|
B2,Palette[B2][0],Palette[B2][1],Palette[B2][2] |
251 |
|
|
); |
252 |
|
|
} |
253 |
|
|
break; |
254 |
|
|
|
255 |
|
|
case 'R': |
256 |
|
|
printf |
257 |
|
|
( |
258 |
|
|
"\n[$2000] PPUCONT1 = $%02X\n[$2001] PPUCONT2 = $%02X\n[$2002] PPUSTAT = $%02X\n", |
259 |
|
|
PPU[0],PPU[1],PPU[2] |
260 |
|
|
); |
261 |
|
|
printf |
262 |
|
|
( |
263 |
|
|
"POSITION = %d,%d\nVADDR = $%04X\nSCANLINE = %d\n", |
264 |
|
|
SCROLLX,SCROLLY,VAddr,CURLINE |
265 |
|
|
); |
266 |
|
|
break; |
267 |
|
|
|
268 |
|
|
case 'S': |
269 |
|
|
if(sscanf(S+1,"%d",&J)==1) |
270 |
|
|
{ |
271 |
|
|
K=SRAM[J*4+1]; |
272 |
|
|
P=SPRITES16? |
273 |
|
|
VPage[(K&0x01? 4:0)+(K>>6)]+((K&0x3E)<<4) |
274 |
|
|
:SprGen[K>>6]+((K&0x3F)<<4); |
275 |
|
|
printf("Sprite #%d (pattern $%02X)\n--------\n",J,K); |
276 |
|
|
for(I=SPRITES16? 16:8;I>0;I--,P++) |
277 |
|
|
{ |
278 |
|
|
for(K=0x80;K;K>>=1) |
279 |
|
|
printf("%c",P[0]&K? (P[8]&K? '#':'o'):(P[8]&K? 'O':'.')); |
280 |
|
|
printf("\n"); |
281 |
|
|
if(I==9) P+=8; |
282 |
|
|
} |
283 |
|
|
printf("--------\n"); |
284 |
|
|
} |
285 |
|
|
else |
286 |
|
|
{ |
287 |
|
|
printf("\nNo| Position | Pat | Att |No| Position | Pat | Att |No| Position | Pat | Att\n"); |
288 |
|
|
for(J=I=0;J<64*4;J+=4) |
289 |
|
|
if(!J||(SRAM[J]&&SRAM[J+3]&&(SRAM[J]<240))) |
290 |
|
|
{ |
291 |
|
|
printf |
292 |
|
|
( |
293 |
|
|
"%2d| %3d,%3d | $%02X | $%02X %s", |
294 |
|
|
J/4,SRAM[J+3],SRAM[J],SRAM[J+1],SRAM[J+2], |
295 |
|
|
I%3==2? "\n":"|" |
296 |
|
|
); |
297 |
|
|
I++; |
298 |
|
|
} |
299 |
|
|
} |
300 |
|
|
break; |
301 |
|
|
#endif /* INES */ |
302 |
|
|
} |
303 |
|
|
} |
304 |
|
|
|
305 |
|
|
/* Continue with emulation */ |
306 |
|
|
return(1); |
307 |
|
|
} |
308 |
|
|
|
309 |
|
|
#endif /* DEBUG */ |