1 |
/* |
2 |
* SAM.h - Simple Assembler and Monitor With Integrated System Explorer |
3 |
* |
4 |
* Frodo (C) 1994-1997,2002-2009 Christian Bauer |
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 "sysdeps.h" |
22 |
|
23 |
#include "SAM.h" |
24 |
#include "C64.h" |
25 |
#include "CPUC64.h" |
26 |
#include "CPU1541.h" |
27 |
#include "VIC.h" |
28 |
#include "SID.h" |
29 |
#include "CIA.h" |
30 |
|
31 |
|
32 |
// Pointers to chips |
33 |
static MOS6510 *TheCPU; |
34 |
static MOS6502_1541 *TheCPU1541; |
35 |
static MOS6569 *TheVIC; |
36 |
static MOS6581 *TheSID; |
37 |
static MOS6526_1 *TheCIA1; |
38 |
static MOS6526_2 *TheCIA2; |
39 |
|
40 |
// 6510/6502 registers |
41 |
static MOS6510State R64; |
42 |
static MOS6502State R1541; |
43 |
|
44 |
static bool access_1541; // false: accessing C64, true: accessing 1541 |
45 |
|
46 |
// Access to 6510/6502 address space |
47 |
static inline uint8 SAMReadByte(uint16 adr) |
48 |
{ |
49 |
if (access_1541) |
50 |
return TheCPU1541->ExtReadByte(adr); |
51 |
else |
52 |
return TheCPU->ExtReadByte(adr); |
53 |
} |
54 |
|
55 |
static inline void SAMWriteByte(uint16 adr, uint8 byte) |
56 |
{ |
57 |
if (access_1541) |
58 |
TheCPU1541->ExtWriteByte(adr, byte); |
59 |
else |
60 |
TheCPU->ExtWriteByte(adr, byte); |
61 |
} |
62 |
|
63 |
|
64 |
// Streams for input, output and error messages |
65 |
static FILE *fin, *fout, *ferr; |
66 |
|
67 |
// Input line |
68 |
#define INPUT_LENGTH 80 |
69 |
static char input[INPUT_LENGTH]; |
70 |
static char *in_ptr; |
71 |
|
72 |
static uint16 address, end_address; |
73 |
|
74 |
|
75 |
// Input tokens |
76 |
enum Token { |
77 |
T_NULL, // Invalid token |
78 |
T_END, // End of line |
79 |
T_NUMBER, // Hexadecimal number |
80 |
T_STRING, // String enclosed in "" |
81 |
T_LPAREN, // '(' |
82 |
T_RPAREN, // ')' |
83 |
T_ADD, // '+' |
84 |
T_SUB, // '-' |
85 |
T_MUL, // '*' |
86 |
T_DIV, // '/' |
87 |
T_COMMA, // ',' |
88 |
T_IMMED, // '#' |
89 |
T_X, // 'x' |
90 |
T_Y, // 'y' |
91 |
T_PC, // 'pc' |
92 |
T_SP, // 'sp' |
93 |
|
94 |
T_A, // 'a' (get_reg_token() only) |
95 |
T_DR, // 'dr' (get_reg_token() only) |
96 |
T_PR // 'pr' (get_reg_token() only) |
97 |
}; |
98 |
|
99 |
static enum Token the_token; // Last token read |
100 |
static uint16 the_number; // Contains the number if the_token==T_NUMBER |
101 |
static char the_string[INPUT_LENGTH]; // Contains the string if the_token==T_STRING |
102 |
|
103 |
|
104 |
// Addressing modes |
105 |
enum { |
106 |
A_IMPL, |
107 |
A_ACCU, // A |
108 |
A_IMM, // #zz |
109 |
A_REL, // Branches |
110 |
A_ZERO, // zz |
111 |
A_ZEROX, // zz,x |
112 |
A_ZEROY, // zz,y |
113 |
A_ABS, // zzzz |
114 |
A_ABSX, // zzzz,x |
115 |
A_ABSY, // zzzz,y |
116 |
A_IND, // (zzzz) |
117 |
A_INDX, // (zz,x) |
118 |
A_INDY // (zz),y |
119 |
}; |
120 |
|
121 |
// Mnemonics |
122 |
enum { |
123 |
M_ADC, M_AND, M_ASL, M_BCC, M_BCS, M_BEQ, M_BIT, M_BMI, M_BNE, M_BPL, |
124 |
M_BRK, M_BVC, M_BVS, M_CLC, M_CLD, M_CLI, M_CLV, M_CMP, M_CPX, M_CPY, |
125 |
M_DEC, M_DEX, M_DEY, M_EOR, M_INC, M_INX, M_INY, M_JMP, M_JSR, M_LDA, |
126 |
M_LDX, M_LDY, M_LSR, M_NOP, M_ORA, M_PHA, M_PHP, M_PLA, M_PLP, M_ROL, |
127 |
M_ROR, M_RTI, M_RTS, M_SBC, M_SEC, M_SED, M_SEI, M_STA, M_STX, M_STY, |
128 |
M_TAX, M_TAY, M_TSX, M_TXA, M_TXS, M_TYA, |
129 |
|
130 |
M_ILLEGAL, // Undocumented opcodes start here |
131 |
|
132 |
M_IANC, M_IANE, M_IARR, M_IASR, M_IDCP, M_IISB, M_IJAM, M_INOP, M_ILAS, |
133 |
M_ILAX, M_ILXA, M_IRLA, M_IRRA, M_ISAX, M_ISBC, M_ISBX, M_ISHA, M_ISHS, |
134 |
M_ISHX, M_ISHY, M_ISLO, M_ISRE, |
135 |
|
136 |
M_MAXIMUM // Highest element |
137 |
}; |
138 |
|
139 |
// Mnemonic for each opcode |
140 |
static const char mnemonic[256] = { |
141 |
M_BRK , M_ORA , M_IJAM, M_ISLO, M_INOP, M_ORA, M_ASL , M_ISLO, // 00 |
142 |
M_PHP , M_ORA , M_ASL , M_IANC, M_INOP, M_ORA, M_ASL , M_ISLO, |
143 |
M_BPL , M_ORA , M_IJAM, M_ISLO, M_INOP, M_ORA, M_ASL , M_ISLO, // 10 |
144 |
M_CLC , M_ORA , M_INOP, M_ISLO, M_INOP, M_ORA, M_ASL , M_ISLO, |
145 |
M_JSR , M_AND , M_IJAM, M_IRLA, M_BIT , M_AND, M_ROL , M_IRLA, // 20 |
146 |
M_PLP , M_AND , M_ROL , M_IANC, M_BIT , M_AND, M_ROL , M_IRLA, |
147 |
M_BMI , M_AND , M_IJAM, M_IRLA, M_INOP, M_AND, M_ROL , M_IRLA, // 30 |
148 |
M_SEC , M_AND , M_INOP, M_IRLA, M_INOP, M_AND, M_ROL , M_IRLA, |
149 |
M_RTI , M_EOR , M_IJAM, M_ISRE, M_INOP, M_EOR, M_LSR , M_ISRE, // 40 |
150 |
M_PHA , M_EOR , M_LSR , M_IASR, M_JMP , M_EOR, M_LSR , M_ISRE, |
151 |
M_BVC , M_EOR , M_IJAM, M_ISRE, M_INOP, M_EOR, M_LSR , M_ISRE, // 50 |
152 |
M_CLI , M_EOR , M_INOP, M_ISRE, M_INOP, M_EOR, M_LSR , M_ISRE, |
153 |
M_RTS , M_ADC , M_IJAM, M_IRRA, M_INOP, M_ADC, M_ROR , M_IRRA, // 60 |
154 |
M_PLA , M_ADC , M_ROR , M_IARR, M_JMP , M_ADC, M_ROR , M_IRRA, |
155 |
M_BVS , M_ADC , M_IJAM, M_IRRA, M_INOP, M_ADC, M_ROR , M_IRRA, // 70 |
156 |
M_SEI , M_ADC , M_INOP, M_IRRA, M_INOP, M_ADC, M_ROR , M_IRRA, |
157 |
M_INOP, M_STA , M_INOP, M_ISAX, M_STY , M_STA, M_STX , M_ISAX, // 80 |
158 |
M_DEY , M_INOP, M_TXA , M_IANE, M_STY , M_STA, M_STX , M_ISAX, |
159 |
M_BCC , M_STA , M_IJAM, M_ISHA, M_STY , M_STA, M_STX , M_ISAX, // 90 |
160 |
M_TYA , M_STA , M_TXS , M_ISHS, M_ISHY, M_STA, M_ISHX, M_ISHA, |
161 |
M_LDY , M_LDA , M_LDX , M_ILAX, M_LDY , M_LDA, M_LDX , M_ILAX, // a0 |
162 |
M_TAY , M_LDA , M_TAX , M_ILXA, M_LDY , M_LDA, M_LDX , M_ILAX, |
163 |
M_BCS , M_LDA , M_IJAM, M_ILAX, M_LDY , M_LDA, M_LDX , M_ILAX, // b0 |
164 |
M_CLV , M_LDA , M_TSX , M_ILAS, M_LDY , M_LDA, M_LDX , M_ILAX, |
165 |
M_CPY , M_CMP , M_INOP, M_IDCP, M_CPY , M_CMP, M_DEC , M_IDCP, // c0 |
166 |
M_INY , M_CMP , M_DEX , M_ISBX, M_CPY , M_CMP, M_DEC , M_IDCP, |
167 |
M_BNE , M_CMP , M_IJAM, M_IDCP, M_INOP, M_CMP, M_DEC , M_IDCP, // d0 |
168 |
M_CLD , M_CMP , M_INOP, M_IDCP, M_INOP, M_CMP, M_DEC , M_IDCP, |
169 |
M_CPX , M_SBC , M_INOP, M_IISB, M_CPX , M_SBC, M_INC , M_IISB, // e0 |
170 |
M_INX , M_SBC , M_NOP , M_ISBC, M_CPX , M_SBC, M_INC , M_IISB, |
171 |
M_BEQ , M_SBC , M_IJAM, M_IISB, M_INOP, M_SBC, M_INC , M_IISB, // f0 |
172 |
M_SED , M_SBC , M_INOP, M_IISB, M_INOP, M_SBC, M_INC , M_IISB |
173 |
}; |
174 |
|
175 |
// Addressing mode for each opcode |
176 |
static const char adr_mode[256] = { |
177 |
A_IMPL, A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 00 |
178 |
A_IMPL, A_IMM , A_ACCU, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, |
179 |
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 10 |
180 |
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, |
181 |
A_ABS , A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 20 |
182 |
A_IMPL, A_IMM , A_ACCU, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, |
183 |
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 30 |
184 |
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, |
185 |
A_IMPL, A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 40 |
186 |
A_IMPL, A_IMM , A_ACCU, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, |
187 |
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 50 |
188 |
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, |
189 |
A_IMPL, A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 60 |
190 |
A_IMPL, A_IMM , A_ACCU, A_IMM , A_IND , A_ABS , A_ABS , A_ABS, |
191 |
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 70 |
192 |
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, |
193 |
A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 80 |
194 |
A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, |
195 |
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROY, A_ZEROY, // 90 |
196 |
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSY , A_ABSY, |
197 |
A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // a0 |
198 |
A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, |
199 |
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROY, A_ZEROY, // b0 |
200 |
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSY , A_ABSY, |
201 |
A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // c0 |
202 |
A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, |
203 |
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // d0 |
204 |
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, |
205 |
A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // e0 |
206 |
A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, |
207 |
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // f0 |
208 |
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX |
209 |
}; |
210 |
|
211 |
// Chars for each mnemonic |
212 |
static const char mnem_1[] = "aaabbbbbbbbbbcccccccdddeiiijjllllnopppprrrrssssssstttttt?aaaadijnlllrrsssssssss"; |
213 |
static const char mnem_2[] = "dnscceimnprvvllllmppeeeonnnmsdddsorhhlloottbeeetttaasxxy?nnrscsaoaaxlrabbhhhhlr"; |
214 |
static const char mnem_3[] = "cdlcsqtielkcscdivpxycxyrcxypraxyrpaapaplrisccdiaxyxyxasa?cerrpbmpsxaaaxcxasxyoe"; |
215 |
|
216 |
// Instruction length for each addressing mode |
217 |
static const char adr_length[] = {1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2}; |
218 |
|
219 |
|
220 |
// Prototypes |
221 |
static void error(const char *s); |
222 |
static void handle_abort(...); |
223 |
static void init_abort(void); |
224 |
static void exit_abort(void); |
225 |
static bool aborted(void); |
226 |
|
227 |
static void read_line(void); // Scanner |
228 |
static char get_char(void); |
229 |
static void put_back(char c); |
230 |
static enum Token get_token(void); |
231 |
static enum Token get_reg_token(void); |
232 |
static uint16 get_number(void); |
233 |
static enum Token get_string(char *str); |
234 |
|
235 |
static bool expression(uint16 *number); // Parser |
236 |
static bool term(uint16 *number); |
237 |
static bool factor(uint16 *number); |
238 |
static bool address_args(void); |
239 |
static bool range_args(int def_range); |
240 |
static bool instr_args(uint16 *number, char *mode); |
241 |
|
242 |
static void help(void); // Routines for commands |
243 |
static void registers(void); |
244 |
static void display_registers(void); |
245 |
static void memory_dump(void); |
246 |
static void ascii_dump(void); |
247 |
static char conv_from_64(char c); |
248 |
static void screen_dump(void); |
249 |
static char conv_from_scode(char c); |
250 |
static void binary_dump(void); |
251 |
static void sprite_dump(void); |
252 |
static void byte_to_bin(uint8 byte, char *str); |
253 |
static void disassemble(void); |
254 |
static int disass_line(uint16 adr, uint8 op, uint8 lo, uint8 hi); |
255 |
static void assemble(void); |
256 |
static char find_mnemonic(char op1, char op2, char op3); |
257 |
static bool find_opcode(char mnem, char mode, uint8 *opcode); |
258 |
static void mem_config(void); |
259 |
static void fill(void); |
260 |
static void compare(void); |
261 |
static void transfer(void); |
262 |
static void modify(void); |
263 |
static void print_expr(void); |
264 |
static void redir_output(void); |
265 |
static void int_vectors(void); |
266 |
static void view_state(void); |
267 |
static void view_cia_state(void); |
268 |
static void dump_cia_ints(uint8 i); |
269 |
static void view_sid_state(void); |
270 |
static void dump_sid_waveform(uint8 wave); |
271 |
static void view_vic_state(void); |
272 |
static void dump_spr_flags(uint8 f); |
273 |
static void dump_vic_ints(uint8 i); |
274 |
static void view_1541_state(void); |
275 |
static void dump_via_ints(uint8 i); |
276 |
static void load_data(void); |
277 |
static void save_data(void); |
278 |
|
279 |
|
280 |
/* |
281 |
* Open and handle SAM |
282 |
*/ |
283 |
|
284 |
void SAM(C64 *the_c64) |
285 |
{ |
286 |
bool done = false; |
287 |
char c; |
288 |
|
289 |
TheCPU = the_c64->TheCPU; |
290 |
TheCPU1541 = the_c64->TheCPU1541; |
291 |
TheVIC = the_c64->TheVIC; |
292 |
TheSID = the_c64->TheSID; |
293 |
TheCIA1 = the_c64->TheCIA1; |
294 |
TheCIA2 = the_c64->TheCIA2; |
295 |
|
296 |
// Get CPU registers and current memory configuration |
297 |
TheCPU->GetState(&R64); |
298 |
TheCPU->ExtConfig = (~R64.ddr | R64.pr) & 7; |
299 |
TheCPU1541->GetState(&R1541); |
300 |
|
301 |
#ifdef __riscos__ |
302 |
Wimp_CommandWindow((int)"SAM"); |
303 |
#endif |
304 |
|
305 |
#ifdef AMIGA |
306 |
if (!(fin = fout = ferr = fopen("CON:0/0/640/480/SAM", "w+"))) |
307 |
return; |
308 |
#else |
309 |
fin = stdin; |
310 |
fout = stdout; |
311 |
ferr = stdout; |
312 |
#endif |
313 |
|
314 |
access_1541 = false; |
315 |
address = R64.pc; |
316 |
|
317 |
fprintf(ferr, "\n *** SAM - Simple Assembler and Monitor ***\n *** Press 'h' for help ***\n\n"); |
318 |
init_abort(); |
319 |
display_registers(); |
320 |
|
321 |
while (!done) { |
322 |
if (access_1541) |
323 |
fprintf(ferr, "1541> "); |
324 |
else |
325 |
fprintf(ferr, "C64> "); |
326 |
fflush(ferr); |
327 |
read_line(); |
328 |
while ((c = get_char()) == ' ') ; |
329 |
|
330 |
switch (c) { |
331 |
case 'a': // Assemble |
332 |
get_token(); |
333 |
assemble(); |
334 |
break; |
335 |
|
336 |
case 'b': // Binary dump |
337 |
get_token(); |
338 |
binary_dump(); |
339 |
break; |
340 |
|
341 |
case 'c': // Compare |
342 |
get_token(); |
343 |
compare(); |
344 |
break; |
345 |
|
346 |
case 'd': // Disassemble |
347 |
get_token(); |
348 |
disassemble(); |
349 |
break; |
350 |
|
351 |
case 'e': // Interrupt vectors |
352 |
int_vectors(); |
353 |
break; |
354 |
|
355 |
case 'f': // Fill |
356 |
get_token(); |
357 |
fill(); |
358 |
break; |
359 |
|
360 |
case 'h': // Help |
361 |
help(); |
362 |
break; |
363 |
|
364 |
case 'i': // ASCII dump |
365 |
get_token(); |
366 |
ascii_dump(); |
367 |
break; |
368 |
|
369 |
case 'k': // Memory configuration |
370 |
get_token(); |
371 |
mem_config(); |
372 |
break; |
373 |
|
374 |
case 'l': // Load data |
375 |
get_token(); |
376 |
load_data(); |
377 |
break; |
378 |
|
379 |
case 'm': // Memory dump |
380 |
get_token(); |
381 |
memory_dump(); |
382 |
break; |
383 |
|
384 |
case 'n': // Screen code dump |
385 |
get_token(); |
386 |
screen_dump(); |
387 |
break; |
388 |
|
389 |
case 'o': // Redirect output |
390 |
get_token(); |
391 |
redir_output(); |
392 |
break; |
393 |
|
394 |
case 'p': // Sprite dump |
395 |
get_token(); |
396 |
sprite_dump(); |
397 |
break; |
398 |
|
399 |
case 'r': // Registers |
400 |
get_reg_token(); |
401 |
registers(); |
402 |
break; |
403 |
|
404 |
case 's': // Save data |
405 |
get_token(); |
406 |
save_data(); |
407 |
break; |
408 |
|
409 |
case 't': // Transfer |
410 |
get_token(); |
411 |
transfer(); |
412 |
break; |
413 |
|
414 |
case 'v': // View machine state |
415 |
view_state(); |
416 |
break; |
417 |
|
418 |
case 'x': // Exit |
419 |
done = true; |
420 |
break; |
421 |
|
422 |
case ':': // Change memory |
423 |
get_token(); |
424 |
modify(); |
425 |
break; |
426 |
|
427 |
case '1': // Switch to 1541 mode |
428 |
access_1541 = true; |
429 |
break; |
430 |
|
431 |
case '6': // Switch to C64 mode |
432 |
access_1541 = false; |
433 |
break; |
434 |
|
435 |
case '?': // Compute expression |
436 |
get_token(); |
437 |
print_expr(); |
438 |
break; |
439 |
|
440 |
case '\n': // Blank line |
441 |
break; |
442 |
|
443 |
default: // Unknown command |
444 |
error("Unknown command"); |
445 |
break; |
446 |
} |
447 |
} |
448 |
|
449 |
exit_abort(); |
450 |
|
451 |
#ifdef AMIGA |
452 |
fclose(fin); |
453 |
#endif |
454 |
if (fout != ferr) |
455 |
fclose(fout); |
456 |
|
457 |
#ifdef __riscos__ |
458 |
Wimp_CommandWindow(-1); |
459 |
#endif |
460 |
|
461 |
// Set CPU registers |
462 |
TheCPU->SetState(&R64); |
463 |
TheCPU1541->SetState(&R1541); |
464 |
} |
465 |
|
466 |
|
467 |
/* |
468 |
* Print error message |
469 |
*/ |
470 |
|
471 |
static void error(const char *s) |
472 |
{ |
473 |
fprintf(ferr, "*** %s\n", s); |
474 |
} |
475 |
|
476 |
|
477 |
/* |
478 |
* CTRL-C pressed? |
479 |
*/ |
480 |
|
481 |
static bool WasAborted; |
482 |
|
483 |
#ifdef HAVE_SIGACTION |
484 |
struct sigaction my_sa; |
485 |
#endif |
486 |
|
487 |
static void handle_abort(...) |
488 |
{ |
489 |
WasAborted = true; |
490 |
#if !defined(HAVE_SIGACTION) && defined(HAVE_SIGNAL) |
491 |
#ifdef __riscos__ |
492 |
signal(SIGINT, (Handler*) handle_abort); |
493 |
#else |
494 |
signal(SIGINT, (sighandler_t) handle_abort); |
495 |
#endif |
496 |
#endif |
497 |
} |
498 |
|
499 |
static void init_abort(void) |
500 |
{ |
501 |
WasAborted = false; |
502 |
#if defined(HAVE_SIGACTION) |
503 |
my_sa.sa_handler = (void (*)(int))handle_abort; |
504 |
my_sa.sa_flags = 0; |
505 |
sigemptyset(&my_sa.sa_mask); |
506 |
sigaction(SIGINT, &my_sa, NULL); |
507 |
#elif defined(HAVE_SIGNAL) |
508 |
#ifdef __riscos__ |
509 |
signal(SIGINT, (Handler*) handle_abort); |
510 |
#else |
511 |
signal(SIGINT, (sighandler_t) handle_abort); |
512 |
#endif |
513 |
#endif |
514 |
} |
515 |
|
516 |
static void exit_abort(void) |
517 |
{ |
518 |
#if defined(HAVE_SIGACTION) |
519 |
my_sa.sa_handler = SIG_DFL; |
520 |
sigaction(SIGINT, &my_sa, NULL); |
521 |
#elif defined(HAVE_SIGNAL) |
522 |
signal(SIGINT, SIG_DFL); |
523 |
#endif |
524 |
} |
525 |
|
526 |
static bool aborted(void) |
527 |
{ |
528 |
bool ret = WasAborted; |
529 |
|
530 |
WasAborted = false; |
531 |
return ret; |
532 |
} |
533 |
|
534 |
|
535 |
/* |
536 |
* Read a line from the keyboard |
537 |
*/ |
538 |
|
539 |
static void read_line(void) |
540 |
{ |
541 |
#ifdef __riscos__ |
542 |
OS_ReadLine(in_ptr = input, INPUT_LENGTH, 0, 255, 0); |
543 |
#else |
544 |
fgets(in_ptr = input, INPUT_LENGTH, fin); |
545 |
#endif |
546 |
} |
547 |
|
548 |
|
549 |
/* |
550 |
* Read a character from the input line |
551 |
*/ |
552 |
|
553 |
static char get_char(void) |
554 |
{ |
555 |
return *in_ptr++; |
556 |
} |
557 |
|
558 |
|
559 |
/* |
560 |
* Stuff back a character into the input line |
561 |
*/ |
562 |
|
563 |
static void put_back(char c) |
564 |
{ |
565 |
*(--in_ptr) = c; |
566 |
} |
567 |
|
568 |
|
569 |
/* |
570 |
* Scanner: Get a token from the input line |
571 |
*/ |
572 |
|
573 |
static enum Token get_token(void) |
574 |
{ |
575 |
char c; |
576 |
|
577 |
// Skip spaces |
578 |
while ((c = get_char()) == ' ') ; |
579 |
|
580 |
switch (c) { |
581 |
case '\n': |
582 |
return the_token = T_END; |
583 |
case '(': |
584 |
return the_token = T_LPAREN; |
585 |
case ')': |
586 |
return the_token = T_RPAREN; |
587 |
case '+': |
588 |
return the_token = T_ADD; |
589 |
case '-': |
590 |
return the_token = T_SUB; |
591 |
case '*': |
592 |
return the_token = T_MUL; |
593 |
case '/': |
594 |
return the_token = T_DIV; |
595 |
case ',': |
596 |
return the_token = T_COMMA; |
597 |
case '#': |
598 |
return the_token = T_IMMED; |
599 |
case 'x': |
600 |
return the_token = T_X; |
601 |
case 'y': |
602 |
return the_token = T_Y; |
603 |
case 'p': |
604 |
if (get_char() == 'c') |
605 |
return the_token = T_PC; |
606 |
else { |
607 |
error("Unrecognized token"); |
608 |
return the_token = T_NULL; |
609 |
} |
610 |
case 's': |
611 |
if (get_char() == 'p') |
612 |
return the_token = T_SP; |
613 |
else { |
614 |
error("Unrecognized token"); |
615 |
return the_token = T_NULL; |
616 |
} |
617 |
case '0': case '1': case '2': case '3': case '4': |
618 |
case '5': case '6': case '7': case '8': case '9': |
619 |
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': |
620 |
put_back(c); |
621 |
the_number = get_number(); |
622 |
return the_token = T_NUMBER; |
623 |
case '"': |
624 |
return the_token = get_string(the_string); |
625 |
default: |
626 |
error("Unrecognized token"); |
627 |
return the_token = T_NULL; |
628 |
} |
629 |
} |
630 |
|
631 |
static enum Token get_reg_token(void) |
632 |
{ |
633 |
char c; |
634 |
|
635 |
// Skip spaces |
636 |
while ((c = get_char()) == ' ') ; |
637 |
|
638 |
switch (c) { |
639 |
case '\n': |
640 |
return the_token = T_END; |
641 |
case 'a': |
642 |
return the_token = T_A; |
643 |
case 'd': |
644 |
if (get_char() == 'r') |
645 |
return the_token = T_DR; |
646 |
else { |
647 |
error("Unrecognized token"); |
648 |
return the_token = T_NULL; |
649 |
} |
650 |
case 'p': |
651 |
if ((c = get_char()) == 'c') |
652 |
return the_token = T_PC; |
653 |
else if (c == 'r') |
654 |
return the_token = T_PR; |
655 |
else { |
656 |
error("Unrecognized token"); |
657 |
return the_token = T_NULL; |
658 |
} |
659 |
case 's': |
660 |
if (get_char() == 'p') |
661 |
return the_token = T_SP; |
662 |
else { |
663 |
error("Unrecognized token"); |
664 |
return the_token = T_NULL; |
665 |
} |
666 |
case 'x': |
667 |
return the_token = T_X; |
668 |
case 'y': |
669 |
return the_token = T_Y; |
670 |
default: |
671 |
error("Unrecognized token"); |
672 |
return the_token = T_NULL; |
673 |
} |
674 |
} |
675 |
|
676 |
static uint16 get_number(void) |
677 |
{ |
678 |
char c; |
679 |
uint16 i = 0; |
680 |
|
681 |
while (((c = get_char()) >= '0') && (c <= '9') || (c >= 'a') && (c <= 'f')) |
682 |
if (c < 'a') |
683 |
i = (i << 4) + (c - '0'); |
684 |
else |
685 |
i = (i << 4) + (c - 'a' + 10); |
686 |
|
687 |
put_back(c); |
688 |
return i; |
689 |
} |
690 |
|
691 |
static enum Token get_string(char *str) |
692 |
{ |
693 |
char c; |
694 |
|
695 |
while ((c = get_char()) != '\n') { |
696 |
if (c == '"') { |
697 |
*str = 0; |
698 |
return T_STRING; |
699 |
} |
700 |
*str++ = c; |
701 |
} |
702 |
|
703 |
error("Unterminated string"); |
704 |
return T_NULL; |
705 |
} |
706 |
|
707 |
|
708 |
/* |
709 |
* expression = term {(ADD | SUB) term} |
710 |
* true: OK, false: Error |
711 |
*/ |
712 |
|
713 |
static bool expression(uint16 *number) |
714 |
{ |
715 |
uint16 accu, trm; |
716 |
|
717 |
if (!term(&accu)) |
718 |
return false; |
719 |
|
720 |
for (;;) |
721 |
switch (the_token) { |
722 |
case T_ADD: |
723 |
get_token(); |
724 |
if (!term(&trm)) |
725 |
return false; |
726 |
accu += trm; |
727 |
break; |
728 |
|
729 |
case T_SUB: |
730 |
get_token(); |
731 |
if (!term(&trm)) |
732 |
return false; |
733 |
accu -= trm; |
734 |
break; |
735 |
|
736 |
default: |
737 |
*number = accu; |
738 |
return true; |
739 |
} |
740 |
} |
741 |
|
742 |
|
743 |
/* |
744 |
* term = factor {(MUL | DIV) factor} |
745 |
* true: OK, false: Error |
746 |
*/ |
747 |
|
748 |
static bool term(uint16 *number) |
749 |
{ |
750 |
uint16 accu, fact; |
751 |
|
752 |
if (!factor(&accu)) |
753 |
return false; |
754 |
|
755 |
for (;;) |
756 |
switch (the_token) { |
757 |
case T_MUL: |
758 |
get_token(); |
759 |
if (!factor(&fact)) |
760 |
return false; |
761 |
accu *= fact; |
762 |
break; |
763 |
|
764 |
case T_DIV: |
765 |
get_token(); |
766 |
if (!factor(&fact)) |
767 |
return false; |
768 |
if (fact == 0) { |
769 |
error("Division by 0"); |
770 |
return false; |
771 |
} |
772 |
accu /= fact; |
773 |
break; |
774 |
|
775 |
default: |
776 |
*number = accu; |
777 |
return true; |
778 |
} |
779 |
} |
780 |
|
781 |
|
782 |
/* |
783 |
* factor = NUMBER | PC | SP | LPAREN expression RPAREN |
784 |
* true: OK, false: Error |
785 |
*/ |
786 |
|
787 |
static bool factor(uint16 *number) |
788 |
{ |
789 |
switch (the_token) { |
790 |
case T_NUMBER: |
791 |
*number = the_number; |
792 |
get_token(); |
793 |
return true; |
794 |
|
795 |
case T_PC: |
796 |
get_token(); |
797 |
*number = access_1541 ? R1541.pc : R64.pc; |
798 |
return true; |
799 |
|
800 |
case T_SP: |
801 |
get_token(); |
802 |
*number = access_1541 ? R1541.sp : R64.sp; |
803 |
return true; |
804 |
|
805 |
case T_LPAREN: |
806 |
get_token(); |
807 |
if (expression(number)) |
808 |
if (the_token == T_RPAREN) { |
809 |
get_token(); |
810 |
return true; |
811 |
} else { |
812 |
error("Missing ')'"); |
813 |
return false; |
814 |
} |
815 |
else { |
816 |
error("Error in expression"); |
817 |
return false; |
818 |
} |
819 |
|
820 |
case T_END: |
821 |
error("Required argument missing"); |
822 |
return false; |
823 |
|
824 |
default: |
825 |
error("'pc', 'sp', '(' or number expected"); |
826 |
return false; |
827 |
} |
828 |
} |
829 |
|
830 |
|
831 |
/* |
832 |
* address_args = [expression] END |
833 |
* |
834 |
* Read start to "address" |
835 |
* |
836 |
* true: OK, false: Error |
837 |
*/ |
838 |
|
839 |
static bool address_args(void) |
840 |
{ |
841 |
if (the_token == T_END) |
842 |
return true; |
843 |
else { |
844 |
if (!expression(&address)) |
845 |
return false; |
846 |
return the_token == T_END; |
847 |
} |
848 |
} |
849 |
|
850 |
|
851 |
/* |
852 |
* range_args = [expression] [[COMMA] expression] END |
853 |
* |
854 |
* Read start address to "address", end address to "end_address" |
855 |
* |
856 |
* true: OK, false: Error |
857 |
*/ |
858 |
|
859 |
static bool range_args(int def_range) |
860 |
{ |
861 |
end_address = address + def_range; |
862 |
|
863 |
if (the_token == T_END) |
864 |
return true; |
865 |
else { |
866 |
if (!expression(&address)) |
867 |
return false; |
868 |
end_address = address + def_range; |
869 |
if (the_token == T_END) |
870 |
return true; |
871 |
else { |
872 |
if (the_token == T_COMMA) get_token(); |
873 |
if (!expression(&end_address)) |
874 |
return false; |
875 |
return the_token == T_END; |
876 |
} |
877 |
} |
878 |
} |
879 |
|
880 |
|
881 |
/* |
882 |
* instr_args = END |
883 |
* | IMMED NUMBER END |
884 |
* | NUMBER [COMMA (X | Y)] END |
885 |
* | LPAREN NUMBER (RPAREN [COMMA Y] | COMMA X RPAREN) END |
886 |
* |
887 |
* Read arguments of a 6510 instruction, determine address and addressing mode |
888 |
* |
889 |
* true: OK, false: Error |
890 |
*/ |
891 |
|
892 |
static bool instr_args(uint16 *number, char *mode) |
893 |
{ |
894 |
switch (the_token) { |
895 |
|
896 |
case T_END: |
897 |
*mode = A_IMPL; |
898 |
return true; |
899 |
|
900 |
case T_IMMED: |
901 |
get_token(); |
902 |
if (the_token == T_NUMBER) { |
903 |
*number = the_number; |
904 |
*mode = A_IMM; |
905 |
get_token(); |
906 |
return the_token == T_END; |
907 |
} else { |
908 |
error("Number expected"); |
909 |
return false; |
910 |
} |
911 |
|
912 |
case T_NUMBER: |
913 |
*number = the_number; |
914 |
get_token(); |
915 |
switch (the_token) { |
916 |
|
917 |
case T_END: |
918 |
if (*number < 0x100) |
919 |
*mode = A_ZERO; |
920 |
else |
921 |
*mode = A_ABS; |
922 |
return true; |
923 |
|
924 |
case T_COMMA: |
925 |
get_token(); |
926 |
switch (the_token) { |
927 |
|
928 |
case T_X: |
929 |
get_token(); |
930 |
if (*number < 0x100) |
931 |
*mode = A_ZEROX; |
932 |
else |
933 |
*mode = A_ABSX; |
934 |
return the_token == T_END; |
935 |
|
936 |
case T_Y: |
937 |
get_token(); |
938 |
if (*number < 0x100) |
939 |
*mode = A_ZEROY; |
940 |
else |
941 |
*mode = A_ABSY; |
942 |
return the_token == T_END; |
943 |
|
944 |
default: |
945 |
error("Illegal index register"); |
946 |
return false; |
947 |
} |
948 |
|
949 |
default: |
950 |
return false; |
951 |
} |
952 |
|
953 |
case T_LPAREN: |
954 |
get_token(); |
955 |
if (the_token == T_NUMBER) { |
956 |
*number = the_number; |
957 |
get_token(); |
958 |
switch (the_token) { |
959 |
|
960 |
case T_RPAREN: |
961 |
get_token(); |
962 |
switch (the_token) { |
963 |
|
964 |
case T_END: |
965 |
*mode = A_IND; |
966 |
return true; |
967 |
|
968 |
case T_COMMA: |
969 |
get_token(); |
970 |
if (the_token == T_Y) { |
971 |
*mode = A_INDY; |
972 |
get_token(); |
973 |
return the_token == T_END; |
974 |
} else { |
975 |
error("Only 'y' index register allowed"); |
976 |
return false; |
977 |
} |
978 |
|
979 |
default: |
980 |
error("Illegal characters after ')'"); |
981 |
return false; |
982 |
} |
983 |
|
984 |
case T_COMMA: |
985 |
get_token(); |
986 |
if (the_token == T_X) { |
987 |
get_token(); |
988 |
if (the_token == T_RPAREN) { |
989 |
*mode = A_INDX; |
990 |
get_token(); |
991 |
return the_token == T_END; |
992 |
} else { |
993 |
error("')' expected"); |
994 |
return false; |
995 |
} |
996 |
} else { |
997 |
error("Only 'x' index register allowed"); |
998 |
return false; |
999 |
} |
1000 |
|
1001 |
default: |
1002 |
error("')' or ',' expected"); |
1003 |
return false; |
1004 |
} |
1005 |
} else { |
1006 |
error("Number expected"); |
1007 |
return false; |
1008 |
} |
1009 |
|
1010 |
default: |
1011 |
error("'(', '#' or number expected"); |
1012 |
return false; |
1013 |
} |
1014 |
} |
1015 |
|
1016 |
|
1017 |
/* |
1018 |
* Display help |
1019 |
* h |
1020 |
*/ |
1021 |
|
1022 |
static void help(void) |
1023 |
{ |
1024 |
fprintf(fout, "a [start] Assemble\n" |
1025 |
"b [start] [end] Binary dump\n" |
1026 |
"c start end dest Compare memory\n" |
1027 |
"d [start] [end] Disassemble\n" |
1028 |
"e Show interrupt vectors\n" |
1029 |
"f start end byte Fill memory\n" |
1030 |
"i [start] [end] ASCII/PETSCII dump\n" |
1031 |
"k [config] Show/set C64 memory configuration\n" |
1032 |
"l start \"file\" Load data\n" |
1033 |
"m [start] [end] Memory dump\n" |
1034 |
"n [start] [end] Screen code dump\n" |
1035 |
"o [\"file\"] Redirect output\n" |
1036 |
"p [start] [end] Sprite dump\n" |
1037 |
"r [reg value] Show/set CPU registers\n" |
1038 |
"s start end \"file\" Save data\n" |
1039 |
"t start end dest Transfer memory\n" |
1040 |
"vc1 View CIA 1 state\n" |
1041 |
"vc2 View CIA 2 state\n" |
1042 |
"vf View 1541 state\n" |
1043 |
"vs View SID state\n" |
1044 |
"vv View VIC state\n" |
1045 |
"x Return to Frodo\n" |
1046 |
": addr {byte} Modify memory\n" |
1047 |
"1541 Switch to 1541\n" |
1048 |
"64 Switch to C64\n" |
1049 |
"? expression Calculate expression\n"); |
1050 |
} |
1051 |
|
1052 |
|
1053 |
/* |
1054 |
* Display/change 6510 registers |
1055 |
* r [reg value] |
1056 |
*/ |
1057 |
|
1058 |
static void registers(void) |
1059 |
{ |
1060 |
enum Token the_reg; |
1061 |
uint16 value; |
1062 |
|
1063 |
if (the_token != T_END) |
1064 |
switch (the_reg = the_token) { |
1065 |
case T_A: |
1066 |
case T_X: |
1067 |
case T_Y: |
1068 |
case T_PC: |
1069 |
case T_SP: |
1070 |
case T_DR: |
1071 |
case T_PR: |
1072 |
get_token(); |
1073 |
if (!expression(&value)) |
1074 |
return; |
1075 |
|
1076 |
switch (the_reg) { |
1077 |
case T_A: |
1078 |
if (access_1541) |
1079 |
R1541.a = value; |
1080 |
else |
1081 |
R64.a = value; |
1082 |
break; |
1083 |
case T_X: |
1084 |
if (access_1541) |
1085 |
R1541.x = value; |
1086 |
else |
1087 |
R64.x = value; |
1088 |
break; |
1089 |
case T_Y: |
1090 |
if (access_1541) |
1091 |
R1541.y = value; |
1092 |
else |
1093 |
R64.y = value; |
1094 |
break; |
1095 |
case T_PC: |
1096 |
if (access_1541) |
1097 |
R1541.pc = value; |
1098 |
else |
1099 |
R64.pc = value; |
1100 |
break; |
1101 |
case T_SP: |
1102 |
if (access_1541) |
1103 |
R1541.sp = (value & 0xff) | 0x0100; |
1104 |
else |
1105 |
R64.sp = (value & 0xff) | 0x0100; |
1106 |
break; |
1107 |
case T_DR: |
1108 |
if (!access_1541) |
1109 |
R64.ddr = value; |
1110 |
break; |
1111 |
case T_PR: |
1112 |
if (!access_1541) |
1113 |
R64.pr = value; |
1114 |
break; |
1115 |
default: |
1116 |
break; |
1117 |
} |
1118 |
break; |
1119 |
|
1120 |
default: |
1121 |
return; |
1122 |
} |
1123 |
|
1124 |
display_registers(); |
1125 |
} |
1126 |
|
1127 |
static void display_registers(void) |
1128 |
{ |
1129 |
if (access_1541) { |
1130 |
fprintf(fout, " PC A X Y SP NVDIZC Instruction\n"); |
1131 |
fprintf(fout, "%04lx %02lx %02lx %02lx %04lx %c%c%c%c%c%c ", |
1132 |
R1541.pc, R1541.a, R1541.x, R1541.y, R1541.sp, |
1133 |
R1541.p & 0x80 ? '1' : '0', R1541.p & 0x40 ? '1' : '0', R1541.p & 0x08 ? '1' : '0', |
1134 |
R1541.p & 0x04 ? '1' : '0', R1541.p & 0x02 ? '1' : '0', R1541.p & 0x01 ? '1' : '0'); |
1135 |
disass_line(R1541.pc, SAMReadByte(R1541.pc), SAMReadByte(R1541.pc+1), SAMReadByte(R1541.pc+2)); |
1136 |
} else { |
1137 |
fprintf(fout, " PC A X Y SP DR PR NVDIZC Instruction\n"); |
1138 |
fprintf(fout, "%04lx %02lx %02lx %02lx %04lx %02lx %02lx %c%c%c%c%c%c ", |
1139 |
R64.pc, R64.a, R64.x, R64.y, R64.sp, R64.ddr, R64.pr, |
1140 |
R64.p & 0x80 ? '1' : '0', R64.p & 0x40 ? '1' : '0', R64.p & 0x08 ? '1' : '0', |
1141 |
R64.p & 0x04 ? '1' : '0', R64.p & 0x02 ? '1' : '0', R64.p & 0x01 ? '1' : '0'); |
1142 |
disass_line(R64.pc, SAMReadByte(R64.pc), SAMReadByte(R64.pc+1), SAMReadByte(R64.pc+2)); |
1143 |
} |
1144 |
} |
1145 |
|
1146 |
|
1147 |
/* |
1148 |
* Memory dump |
1149 |
* m [start] [end] |
1150 |
*/ |
1151 |
|
1152 |
#define MEMDUMP_BPL 16 // Bytes per line |
1153 |
|
1154 |
static void memory_dump(void) |
1155 |
{ |
1156 |
bool done = false; |
1157 |
short i; |
1158 |
uint8 mem[MEMDUMP_BPL + 2]; |
1159 |
uint8 byte; |
1160 |
|
1161 |
mem[MEMDUMP_BPL] = 0; |
1162 |
|
1163 |
if (!range_args(16 * MEMDUMP_BPL - 1)) // 16 lines unless end address specified |
1164 |
return; |
1165 |
|
1166 |
do { |
1167 |
fprintf(fout, "%04lx:", address); |
1168 |
for (i=0; i<MEMDUMP_BPL; i++, address++) { |
1169 |
if (address == end_address) done = true; |
1170 |
|
1171 |
fprintf(fout, " %02lx", byte = SAMReadByte(address)); |
1172 |
if ((byte >= ' ') && (byte <= '~')) |
1173 |
mem[i] = conv_from_64(byte); |
1174 |
else |
1175 |
mem[i] = '.'; |
1176 |
} |
1177 |
fprintf(fout, " '%s'\n", mem); |
1178 |
} while (!done && !aborted()); |
1179 |
} |
1180 |
|
1181 |
|
1182 |
/* |
1183 |
* ASCII dump |
1184 |
* i [start] [end] |
1185 |
*/ |
1186 |
|
1187 |
#define ASCIIDUMP_BPL 64 // Bytes per line |
1188 |
|
1189 |
static void ascii_dump(void) |
1190 |
{ |
1191 |
bool done = false; |
1192 |
short i; |
1193 |
uint8 mem[ASCIIDUMP_BPL + 2]; |
1194 |
uint8 byte; |
1195 |
|
1196 |
mem[ASCIIDUMP_BPL] = 0; |
1197 |
|
1198 |
if (!range_args(16 * ASCIIDUMP_BPL - 1)) // 16 lines unless end address specified |
1199 |
return; |
1200 |
|
1201 |
do { |
1202 |
fprintf(fout, "%04lx:", address); |
1203 |
for (i=0; i<ASCIIDUMP_BPL; i++, address++) { |
1204 |
if (address == end_address) done = true; |
1205 |
|
1206 |
byte = SAMReadByte(address); |
1207 |
if ((byte >= ' ') && (byte <= '~')) |
1208 |
mem[i] = conv_from_64(byte); |
1209 |
else |
1210 |
mem[i] = '.'; |
1211 |
} |
1212 |
fprintf(fout, " '%s'\n", mem); |
1213 |
} while (!done && !aborted()); |
1214 |
} |
1215 |
|
1216 |
|
1217 |
/* |
1218 |
* Convert PETSCII->ASCII |
1219 |
*/ |
1220 |
|
1221 |
static char conv_from_64(char c) |
1222 |
{ |
1223 |
if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) |
1224 |
return c ^ 0x20; |
1225 |
else |
1226 |
return c; |
1227 |
} |
1228 |
|
1229 |
|
1230 |
/* |
1231 |
* Screen code dump |
1232 |
* n [start] [end] |
1233 |
*/ |
1234 |
|
1235 |
#define SCRDUMP_BPL 64 // Bytes per line |
1236 |
|
1237 |
static void screen_dump(void) |
1238 |
{ |
1239 |
bool done = false; |
1240 |
short i; |
1241 |
uint8 mem[SCRDUMP_BPL + 2]; |
1242 |
uint8 byte; |
1243 |
|
1244 |
mem[SCRDUMP_BPL] = 0; |
1245 |
|
1246 |
if (!range_args(16 * SCRDUMP_BPL - 1)) // 16 Zeilen unless end address specified |
1247 |
return; |
1248 |
|
1249 |
do { |
1250 |
fprintf(fout, "%04lx:", address); |
1251 |
for (i=0; i<SCRDUMP_BPL; i++, address++) { |
1252 |
if (address == end_address) done = true; |
1253 |
|
1254 |
byte = SAMReadByte(address); |
1255 |
if (byte < 90) |
1256 |
mem[i] = conv_from_scode(byte); |
1257 |
else |
1258 |
mem[i] = '.'; |
1259 |
} |
1260 |
fprintf(fout, " '%s'\n", mem); |
1261 |
} while (!done && !aborted()); |
1262 |
} |
1263 |
|
1264 |
|
1265 |
/* |
1266 |
* Convert screen code->ASCII |
1267 |
*/ |
1268 |
|
1269 |
static char conv_from_scode(char c) |
1270 |
{ |
1271 |
c &= 0x7f; |
1272 |
|
1273 |
if (c <= 31) |
1274 |
return c + 64; |
1275 |
else |
1276 |
if (c >= 64) |
1277 |
return c + 32; |
1278 |
else |
1279 |
return c; |
1280 |
} |
1281 |
|
1282 |
|
1283 |
/* |
1284 |
* Binary dump |
1285 |
* b [start] [end] |
1286 |
*/ |
1287 |
|
1288 |
static void binary_dump(void) |
1289 |
{ |
1290 |
bool done = false; |
1291 |
char bin[10]; |
1292 |
|
1293 |
bin[8] = 0; |
1294 |
|
1295 |
if (!range_args(7)) // 8 lines unless end address specified |
1296 |
return; |
1297 |
|
1298 |
do { |
1299 |
if (address == end_address) done = true; |
1300 |
|
1301 |
byte_to_bin(SAMReadByte(address), bin); |
1302 |
fprintf(fout, "%04lx: %s\n", address++, bin); |
1303 |
} while (!done && !aborted()); |
1304 |
} |
1305 |
|
1306 |
|
1307 |
/* |
1308 |
* Sprite data dump |
1309 |
* p [start] [end] |
1310 |
*/ |
1311 |
|
1312 |
static void sprite_dump(void) |
1313 |
{ |
1314 |
bool done = false; |
1315 |
short i; |
1316 |
char bin[10]; |
1317 |
|
1318 |
bin[8] = 0; |
1319 |
|
1320 |
if (!range_args(21 * 3 - 1)) // 21 lines unless end address specified |
1321 |
return; |
1322 |
|
1323 |
do { |
1324 |
fprintf(fout, "%04lx: ", address); |
1325 |
for (i=0; i<3; i++, address++) { |
1326 |
if (address == end_address) done = true; |
1327 |
|
1328 |
byte_to_bin(SAMReadByte(address), bin); |
1329 |
fprintf(fout, "%s", bin); |
1330 |
} |
1331 |
fputc('\n', fout); |
1332 |
} while (!done && !aborted()); |
1333 |
} |
1334 |
|
1335 |
|
1336 |
/* |
1337 |
* Convert byte to binary representation |
1338 |
*/ |
1339 |
|
1340 |
static void byte_to_bin(uint8 byte, char *str) |
1341 |
{ |
1342 |
short i; |
1343 |
|
1344 |
for (i=0; i<8; i++, byte<<=1) |
1345 |
if (byte & 0x80) |
1346 |
str[i] = '#'; |
1347 |
else |
1348 |
str[i] = '.'; |
1349 |
} |
1350 |
|
1351 |
|
1352 |
/* |
1353 |
* Disassemble |
1354 |
* d [start] [end] |
1355 |
*/ |
1356 |
|
1357 |
static void disassemble(void) |
1358 |
{ |
1359 |
bool done = false; |
1360 |
short i; |
1361 |
uint8 op[3]; |
1362 |
uint16 adr; |
1363 |
|
1364 |
if (!range_args(31)) // 32 bytes unless end address specified |
1365 |
return; |
1366 |
|
1367 |
do { |
1368 |
fprintf(fout, "%04lx:", adr = address); |
1369 |
for (i=0; i<3; i++, adr++) { |
1370 |
if (adr == end_address) done = true; |
1371 |
op[i] = SAMReadByte(adr); |
1372 |
} |
1373 |
address += disass_line(address, op[0], op[1], op[2]); |
1374 |
} while (!done && !aborted()); |
1375 |
} |
1376 |
|
1377 |
|
1378 |
/* |
1379 |
* Disassemble one instruction, return length |
1380 |
*/ |
1381 |
|
1382 |
static int disass_line(uint16 adr, uint8 op, uint8 lo, uint8 hi) |
1383 |
{ |
1384 |
char mode = adr_mode[op], mnem = mnemonic[op]; |
1385 |
|
1386 |
// Display instruction bytes in hex |
1387 |
switch (adr_length[mode]) { |
1388 |
case 1: |
1389 |
fprintf(fout, " %02lx ", op); |
1390 |
break; |
1391 |
|
1392 |
case 2: |
1393 |
fprintf(fout, " %02lx %02lx ", op, lo); |
1394 |
break; |
1395 |
|
1396 |
case 3: |
1397 |
fprintf(fout, " %02lx %02lx %02lx ", op, lo, hi); |
1398 |
break; |
1399 |
} |
1400 |
|
1401 |
// Tag undocumented opcodes with an asterisk |
1402 |
if (mnem > M_ILLEGAL) |
1403 |
fputc('*', fout); |
1404 |
else |
1405 |
fputc(' ', fout); |
1406 |
|
1407 |
// Print mnemonic |
1408 |
fprintf(fout, "%c%c%c ", mnem_1[mnem], mnem_2[mnem], mnem_3[mnem]); |
1409 |
|
1410 |
// Pring argument |
1411 |
switch (mode) { |
1412 |
case A_IMPL: |
1413 |
break; |
1414 |
|
1415 |
case A_ACCU: |
1416 |
fprintf(fout, "a"); |
1417 |
break; |
1418 |
|
1419 |
case A_IMM: |
1420 |
fprintf(fout, "#%02lx", lo); |
1421 |
break; |
1422 |
|
1423 |
case A_REL: |
1424 |
fprintf(fout, "%04lx", ((adr + 2) + (int8)lo) & 0xffff); |
1425 |
break; |
1426 |
|
1427 |
case A_ZERO: |
1428 |
fprintf(fout, "%02lx", lo); |
1429 |
break; |
1430 |
|
1431 |
case A_ZEROX: |
1432 |
fprintf(fout, "%02lx,x", lo); |
1433 |
break; |
1434 |
|
1435 |
case A_ZEROY: |
1436 |
fprintf(fout, "%02lx,y", lo); |
1437 |
break; |
1438 |
|
1439 |
case A_ABS: |
1440 |
fprintf(fout, "%04lx", (hi << 8) | lo); |
1441 |
break; |
1442 |
|
1443 |
case A_ABSX: |
1444 |
fprintf(fout, "%04lx,x", (hi << 8) | lo); |
1445 |
break; |
1446 |
|
1447 |
case A_ABSY: |
1448 |
fprintf(fout, "%04lx,y", (hi << 8) | lo); |
1449 |
break; |
1450 |
|
1451 |
case A_IND: |
1452 |
fprintf(fout, "(%04lx)", (hi << 8) | lo); |
1453 |
break; |
1454 |
|
1455 |
case A_INDX: |
1456 |
fprintf(fout, "(%02lx,x)", lo); |
1457 |
break; |
1458 |
|
1459 |
case A_INDY: |
1460 |
fprintf(fout, "(%02lx),y", lo); |
1461 |
break; |
1462 |
} |
1463 |
|
1464 |
fputc('\n', fout); |
1465 |
return adr_length[mode]; |
1466 |
} |
1467 |
|
1468 |
|
1469 |
/* |
1470 |
* Assemble |
1471 |
* a [start] |
1472 |
*/ |
1473 |
|
1474 |
static void assemble(void) |
1475 |
{ |
1476 |
bool done = false; |
1477 |
char c1, c2, c3; |
1478 |
char mnem, mode; |
1479 |
uint8 opcode; |
1480 |
uint16 arg; |
1481 |
int16 rel; |
1482 |
|
1483 |
// Read parameters |
1484 |
if (!address_args()) |
1485 |
return; |
1486 |
|
1487 |
do { |
1488 |
fprintf(fout, "%04lx> ", address); |
1489 |
fflush(ferr); |
1490 |
read_line(); |
1491 |
|
1492 |
c1 = get_char(); |
1493 |
c2 = get_char(); |
1494 |
c3 = get_char(); |
1495 |
|
1496 |
if (c1 != '\n') { |
1497 |
|
1498 |
if ((mnem = find_mnemonic(c1, c2, c3)) != M_ILLEGAL) { |
1499 |
|
1500 |
get_token(); |
1501 |
if (instr_args(&arg, &mode)) { |
1502 |
|
1503 |
// Convert A_IMPL -> A_ACCU if necessary |
1504 |
if ((mode == A_IMPL) && find_opcode(mnem, A_ACCU, &opcode)) |
1505 |
mode = A_ACCU; |
1506 |
|
1507 |
// Handle relative addressing seperately |
1508 |
if (((mode == A_ABS) || (mode == A_ZERO)) && find_opcode(mnem, A_REL, &opcode)) { |
1509 |
mode = A_REL; |
1510 |
rel = arg - (address + 2) & 0xffff; |
1511 |
if ((rel < -128) || (rel > 127)) { |
1512 |
error("Branch too long"); |
1513 |
continue; |
1514 |
} else |
1515 |
arg = rel & 0xff; |
1516 |
} |
1517 |
|
1518 |
if (find_opcode(mnem, mode, &opcode)) { |
1519 |
|
1520 |
// Print disassembled line |
1521 |
fprintf(fout, "\v%04lx:", address); |
1522 |
disass_line(address, opcode, arg & 0xff, arg >> 8); |
1523 |
|
1524 |
switch (adr_length[mode]) { |
1525 |
case 1: |
1526 |
SAMWriteByte(address++, opcode); |
1527 |
break; |
1528 |
|
1529 |
case 2: |
1530 |
SAMWriteByte(address++, opcode); |
1531 |
SAMWriteByte(address++, arg); |
1532 |
break; |
1533 |
|
1534 |
case 3: |
1535 |
SAMWriteByte(address++, opcode); |
1536 |
SAMWriteByte(address++, arg & 0xff); |
1537 |
SAMWriteByte(address++, arg >> 8); |
1538 |
break; |
1539 |
|
1540 |
default: |
1541 |
error("Internal error"); |
1542 |
break; |
1543 |
} |
1544 |
|
1545 |
} else |
1546 |
error("Addressing mode not supported by instruction"); |
1547 |
|
1548 |
} else |
1549 |
error("Unrecognized addressing mode"); |
1550 |
|
1551 |
} else |
1552 |
error("Unknown instruction"); |
1553 |
|
1554 |
} else // Input is terminated with a blank line |
1555 |
done = true; |
1556 |
} while (!done); |
1557 |
} |
1558 |
|
1559 |
|
1560 |
/* |
1561 |
* Find mnemonic code to three letters |
1562 |
* M_ILLEGAL: No matching mnemonic found |
1563 |
*/ |
1564 |
|
1565 |
static char find_mnemonic(char op1, char op2, char op3) |
1566 |
{ |
1567 |
int i; |
1568 |
|
1569 |
for (i=0; i<M_MAXIMUM; i++) |
1570 |
if ((mnem_1[i] == op1) && (mnem_2[i] == op2) && (mnem_3[i] == op3)) |
1571 |
return i; |
1572 |
|
1573 |
return M_ILLEGAL; |
1574 |
} |
1575 |
|
1576 |
|
1577 |
/* |
1578 |
* Determine opcode of an instruction given mnemonic and addressing mode |
1579 |
* true: OK, false: Mnemonic can't have specified addressing mode |
1580 |
*/ |
1581 |
|
1582 |
static bool find_opcode(char mnem, char mode, uint8 *opcode) |
1583 |
{ |
1584 |
int i; |
1585 |
|
1586 |
for (i=0; i<256; i++) |
1587 |
if ((mnemonic[i] == mnem) && (adr_mode[i] == mode)) { |
1588 |
*opcode = i; |
1589 |
return true; |
1590 |
} |
1591 |
|
1592 |
return false; |
1593 |
} |
1594 |
|
1595 |
|
1596 |
/* |
1597 |
* Show/set memory configuration |
1598 |
* k [config] |
1599 |
*/ |
1600 |
|
1601 |
static void mem_config(void) |
1602 |
{ |
1603 |
uint16 con; |
1604 |
|
1605 |
if (the_token != T_END) |
1606 |
if (!expression(&con)) |
1607 |
return; |
1608 |
else |
1609 |
TheCPU->ExtConfig = con; |
1610 |
else |
1611 |
con = TheCPU->ExtConfig; |
1612 |
|
1613 |
fprintf(fout, "Configuration: %ld\n", con & 7); |
1614 |
fprintf(fout, "A000-BFFF: %s\n", (con & 3) == 3 ? "Basic" : "RAM"); |
1615 |
fprintf(fout, "D000-DFFF: %s\n", (con & 3) ? ((con & 4) ? "I/O" : "Char") : "RAM"); |
1616 |
fprintf(fout, "E000-FFFF: %s\n", (con & 2) ? "Kernal" : "RAM"); |
1617 |
} |
1618 |
|
1619 |
|
1620 |
/* |
1621 |
* Fill |
1622 |
* f start end byte |
1623 |
*/ |
1624 |
|
1625 |
static void fill(void) |
1626 |
{ |
1627 |
bool done = false; |
1628 |
uint16 adr, end_adr, value; |
1629 |
|
1630 |
if (!expression(&adr)) |
1631 |
return; |
1632 |
if (!expression(&end_adr)) |
1633 |
return; |
1634 |
if (!expression(&value)) |
1635 |
return; |
1636 |
|
1637 |
do { |
1638 |
if (adr == end_adr) done = true; |
1639 |
|
1640 |
SAMWriteByte(adr++, value); |
1641 |
} while (!done); |
1642 |
} |
1643 |
|
1644 |
|
1645 |
/* |
1646 |
* Compare |
1647 |
* c start end dest |
1648 |
*/ |
1649 |
|
1650 |
static void compare(void) |
1651 |
{ |
1652 |
bool done = false; |
1653 |
uint16 adr, end_adr, dest; |
1654 |
int num = 0; |
1655 |
|
1656 |
if (!expression(&adr)) |
1657 |
return; |
1658 |
if (!expression(&end_adr)) |
1659 |
return; |
1660 |
if (!expression(&dest)) |
1661 |
return; |
1662 |
|
1663 |
do { |
1664 |
if (adr == end_adr) done = true; |
1665 |
|
1666 |
if (SAMReadByte(adr) != SAMReadByte(dest)) { |
1667 |
fprintf(fout, "%04lx ", adr); |
1668 |
num++; |
1669 |
if (!(num & 7)) |
1670 |
fputc('\n', fout); |
1671 |
} |
1672 |
adr++; dest++; |
1673 |
} while (!done && !aborted()); |
1674 |
|
1675 |
if (num & 7) |
1676 |
fputc('\n', fout); |
1677 |
fprintf(fout, "%ld byte(s) different\n", num); |
1678 |
} |
1679 |
|
1680 |
|
1681 |
/* |
1682 |
* Transfer memory |
1683 |
* t start end dest |
1684 |
*/ |
1685 |
|
1686 |
static void transfer(void) |
1687 |
{ |
1688 |
bool done = false; |
1689 |
uint16 adr, end_adr, dest; |
1690 |
|
1691 |
if (!expression(&adr)) |
1692 |
return; |
1693 |
if (!expression(&end_adr)) |
1694 |
return; |
1695 |
if (!expression(&dest)) |
1696 |
return; |
1697 |
|
1698 |
if (dest < adr) |
1699 |
do { |
1700 |
if (adr == end_adr) done = true; |
1701 |
SAMWriteByte(dest++, SAMReadByte(adr++)); |
1702 |
} while (!done); |
1703 |
else { |
1704 |
dest += end_adr - adr; |
1705 |
do { |
1706 |
if (adr == end_adr) done = true; |
1707 |
SAMWriteByte(dest--, SAMReadByte(end_adr--)); |
1708 |
} while (!done); |
1709 |
} |
1710 |
} |
1711 |
|
1712 |
|
1713 |
/* |
1714 |
* Change memory |
1715 |
* : addr {byte} |
1716 |
*/ |
1717 |
|
1718 |
static void modify(void) |
1719 |
{ |
1720 |
uint16 adr, val; |
1721 |
|
1722 |
if (!expression(&adr)) |
1723 |
return; |
1724 |
|
1725 |
while (the_token != T_END) |
1726 |
if (expression(&val)) |
1727 |
SAMWriteByte(adr++, val); |
1728 |
else |
1729 |
return; |
1730 |
} |
1731 |
|
1732 |
|
1733 |
/* |
1734 |
* Compute and display expression |
1735 |
* ? expression |
1736 |
*/ |
1737 |
|
1738 |
static void print_expr(void) |
1739 |
{ |
1740 |
uint16 val; |
1741 |
|
1742 |
if (!expression(&val)) |
1743 |
return; |
1744 |
|
1745 |
fprintf(fout, "Hex: %04lx\nDec: %lu\n", val, val); |
1746 |
} |
1747 |
|
1748 |
|
1749 |
/* |
1750 |
* Redirect output |
1751 |
* o [file] |
1752 |
*/ |
1753 |
|
1754 |
static void redir_output(void) |
1755 |
{ |
1756 |
// Close old file |
1757 |
if (fout != ferr) { |
1758 |
fclose(fout); |
1759 |
fout = ferr; |
1760 |
return; |
1761 |
} |
1762 |
|
1763 |
// No argument given? |
1764 |
if (the_token == T_END) |
1765 |
return; |
1766 |
|
1767 |
// Otherwise open file |
1768 |
if (the_token == T_STRING) { |
1769 |
if (!(fout = fopen(the_string, "w"))) |
1770 |
error("Unable to open file"); |
1771 |
} else |
1772 |
error("'\"' around file name expected"); |
1773 |
} |
1774 |
|
1775 |
|
1776 |
/* |
1777 |
* Display interrupt vectors |
1778 |
*/ |
1779 |
|
1780 |
static void int_vectors(void) |
1781 |
{ |
1782 |
fprintf(fout, " IRQ BRK NMI\n"); |
1783 |
fprintf(fout, "%d : %04lx %04lx %04lx\n", |
1784 |
access_1541 ? 6502 : 6510, |
1785 |
SAMReadByte(0xffff) << 8 | SAMReadByte(0xfffe), |
1786 |
SAMReadByte(0xffff) << 8 | SAMReadByte(0xfffe), |
1787 |
SAMReadByte(0xfffb) << 8 | SAMReadByte(0xfffa)); |
1788 |
|
1789 |
if (!access_1541 && TheCPU->ExtConfig & 2) |
1790 |
fprintf(fout, "Kernal: %04lx %04lx %04lx\n", |
1791 |
SAMReadByte(0x0315) << 8 | SAMReadByte(0x0314), |
1792 |
SAMReadByte(0x0317) << 8 | SAMReadByte(0x0316), |
1793 |
SAMReadByte(0x0319) << 8 | SAMReadByte(0x0318)); |
1794 |
} |
1795 |
|
1796 |
|
1797 |
/* |
1798 |
* Display state of custom chips |
1799 |
*/ |
1800 |
|
1801 |
static void view_state(void) |
1802 |
{ |
1803 |
switch (get_char()) { |
1804 |
case 'c': // CIA |
1805 |
view_cia_state(); |
1806 |
break; |
1807 |
|
1808 |
case 's': // SID |
1809 |
view_sid_state(); |
1810 |
break; |
1811 |
|
1812 |
case 'v': // VIC |
1813 |
view_vic_state(); |
1814 |
break; |
1815 |
|
1816 |
case 'f': // Floppy |
1817 |
view_1541_state(); |
1818 |
break; |
1819 |
|
1820 |
default: |
1821 |
error("Unknown command"); |
1822 |
break; |
1823 |
} |
1824 |
} |
1825 |
|
1826 |
static void view_cia_state(void) |
1827 |
{ |
1828 |
MOS6526State cs; |
1829 |
|
1830 |
switch (get_char()) { |
1831 |
case '1': |
1832 |
TheCIA1->GetState(&cs); |
1833 |
break; |
1834 |
case '2': |
1835 |
TheCIA2->GetState(&cs); |
1836 |
break; |
1837 |
default: |
1838 |
error("Unknown command"); |
1839 |
return; |
1840 |
} |
1841 |
|
1842 |
fprintf(fout, "Timer A : %s\n", cs.cra & 1 ? "On" : "Off"); |
1843 |
fprintf(fout, " Counter : %04lx Latch: %04lx\n", (cs.ta_hi << 8) | cs.ta_lo, cs.latcha); |
1844 |
fprintf(fout, " Run mode: %s\n", cs.cra & 8 ? "One-shot" : "Continuous"); |
1845 |
fprintf(fout, " Input : %s\n", cs.cra & 0x20 ? "CNT" : "Phi2"); |
1846 |
fprintf(fout, " Output : "); |
1847 |
if (cs.cra & 2) |
1848 |
if (cs.cra & 4) |
1849 |
fprintf(fout, "PB6 Toggle\n\n"); |
1850 |
else |
1851 |
fprintf(fout, "PB6 Pulse\n\n"); |
1852 |
else |
1853 |
fprintf(fout, "None\n\n"); |
1854 |
|
1855 |
fprintf(fout, "Timer B : %s\n", cs.crb & 1 ? "On" : "Off"); |
1856 |
fprintf(fout, " Counter : %04lx Latch: %04lx\n", (cs.tb_hi << 8) | cs.tb_lo, cs.latchb); |
1857 |
fprintf(fout, " Run mode: %s\n", cs.crb & 8 ? "One-shot" : "Continuous"); |
1858 |
fprintf(fout, " Input : "); |
1859 |
if (cs.crb & 0x40) |
1860 |
if (cs.crb & 0x20) |
1861 |
fprintf(fout, "Timer A underflow (CNT high)\n"); |
1862 |
else |
1863 |
fprintf(fout, "Timer A underflow\n"); |
1864 |
else |
1865 |
if (cs.crb & 0x20) |
1866 |
fprintf(fout, "CNT\n"); |
1867 |
else |
1868 |
fprintf(fout, "Phi2\n"); |
1869 |
fprintf(fout, " Output : "); |
1870 |
if (cs.crb & 2) |
1871 |
if (cs.crb & 4) |
1872 |
fprintf(fout, "PB7 Toggle\n\n"); |
1873 |
else |
1874 |
fprintf(fout, "PB7 Pulse\n\n"); |
1875 |
else |
1876 |
fprintf(fout, "None\n\n"); |
1877 |
|
1878 |
fprintf(fout, "TOD : %lx%lx:%lx%lx:%lx%lx.%lx %s\n", |
1879 |
(cs.tod_hr >> 4) & 1, cs.tod_hr & 0x0f, |
1880 |
(cs.tod_min >> 4) & 7, cs.tod_min & 0x0f, |
1881 |
(cs.tod_sec >> 4) & 7, cs.tod_sec & 0x0f, |
1882 |
cs.tod_10ths & 0x0f, cs.tod_hr & 0x80 ? "PM" : "AM"); |
1883 |
fprintf(fout, "Alarm : %lx%lx:%lx%lx:%lx%lx.%lx %s\n", |
1884 |
(cs.alm_hr >> 4) & 1, cs.alm_hr & 0x0f, |
1885 |
(cs.alm_min >> 4) & 7, cs.alm_min & 0x0f, |
1886 |
(cs.alm_sec >> 4) & 7, cs.alm_sec & 0x0f, |
1887 |
cs.alm_10ths & 0x0f, cs.alm_hr & 0x80 ? "PM" : "AM"); |
1888 |
fprintf(fout, "TOD input : %s\n", cs.cra & 0x80 ? "50Hz" : "60Hz"); |
1889 |
fprintf(fout, "Write to : %s registers\n\n", cs.crb & 0x80 ? "Alarm" : "TOD"); |
1890 |
|
1891 |
fprintf(fout, "Serial data : %02lx\n", cs.sdr); |
1892 |
fprintf(fout, "Serial mode : %s\n\n", cs.cra & 0x40 ? "Output" : "Input"); |
1893 |
|
1894 |
fprintf(fout, "Pending int.: "); |
1895 |
dump_cia_ints(cs.int_data); |
1896 |
fprintf(fout, "Enabled int.: "); |
1897 |
dump_cia_ints(cs.int_mask); |
1898 |
} |
1899 |
|
1900 |
static void dump_cia_ints(uint8 i) |
1901 |
{ |
1902 |
if (i & 0x1f) { |
1903 |
if (i & 1) fprintf(fout, "TA "); |
1904 |
if (i & 2) fprintf(fout, "TB "); |
1905 |
if (i & 4) fprintf(fout, "Alarm "); |
1906 |
if (i & 8) fprintf(fout, "Serial "); |
1907 |
if (i & 0x10) fprintf(fout, "Flag"); |
1908 |
} else |
1909 |
fprintf(fout, "None"); |
1910 |
fputc('\n', fout); |
1911 |
} |
1912 |
|
1913 |
static void view_sid_state(void) |
1914 |
{ |
1915 |
MOS6581State ss; |
1916 |
|
1917 |
TheSID->GetState(&ss); |
1918 |
|
1919 |
fprintf(fout, "Voice 1\n"); |
1920 |
fprintf(fout, " Frequency : %04lx\n", (ss.freq_hi_1 << 8) | ss.freq_lo_1); |
1921 |
fprintf(fout, " Pulse Width: %04lx\n", ((ss.pw_hi_1 & 0x0f) << 8) | ss.pw_lo_1); |
1922 |
fprintf(fout, " Env. (ADSR): %lx %lx %lx %lx\n", ss.AD_1 >> 4, ss.AD_1 & 0x0f, ss.SR_1 >> 4, ss.SR_1 & 0x0f); |
1923 |
fprintf(fout, " Waveform : "); |
1924 |
dump_sid_waveform(ss.ctrl_1); |
1925 |
fprintf(fout, " Gate : %s Ring mod.: %s\n", ss.ctrl_1 & 0x01 ? "On " : "Off", ss.ctrl_1 & 0x04 ? "On" : "Off"); |
1926 |
fprintf(fout, " Test bit : %s Synchron.: %s\n", ss.ctrl_1 & 0x08 ? "On " : "Off", ss.ctrl_1 & 0x02 ? "On" : "Off"); |
1927 |
fprintf(fout, " Filter : %s\n", ss.res_filt & 0x01 ? "On" : "Off"); |
1928 |
|
1929 |
fprintf(fout, "\nVoice 2\n"); |
1930 |
fprintf(fout, " Frequency : %04lx\n", (ss.freq_hi_2 << 8) | ss.freq_lo_2); |
1931 |
fprintf(fout, " Pulse Width: %04lx\n", ((ss.pw_hi_2 & 0x0f) << 8) | ss.pw_lo_2); |
1932 |
fprintf(fout, " Env. (ADSR): %lx %lx %lx %lx\n", ss.AD_2 >> 4, ss.AD_2 & 0x0f, ss.SR_2 >> 4, ss.SR_2 & 0x0f); |
1933 |
fprintf(fout, " Waveform : "); |
1934 |
dump_sid_waveform(ss.ctrl_2); |
1935 |
fprintf(fout, " Gate : %s Ring mod.: %s\n", ss.ctrl_2 & 0x01 ? "On " : "Off", ss.ctrl_2 & 0x04 ? "On" : "Off"); |
1936 |
fprintf(fout, " Test bit : %s Synchron.: %s\n", ss.ctrl_2 & 0x08 ? "On " : "Off", ss.ctrl_2 & 0x02 ? "On" : "Off"); |
1937 |
fprintf(fout, " Filter : %s\n", ss.res_filt & 0x02 ? "On" : "Off"); |
1938 |
|
1939 |
fprintf(fout, "\nVoice 3\n"); |
1940 |
fprintf(fout, " Frequency : %04lx\n", (ss.freq_hi_3 << 8) | ss.freq_lo_3); |
1941 |
fprintf(fout, " Pulse Width: %04lx\n", ((ss.pw_hi_3 & 0x0f) << 8) | ss.pw_lo_3); |
1942 |
fprintf(fout, " Env. (ADSR): %lx %lx %lx %lx\n", ss.AD_3 >> 4, ss.AD_3 & 0x0f, ss.SR_3 >> 4, ss.SR_3 & 0x0f); |
1943 |
fprintf(fout, " Waveform : "); |
1944 |
dump_sid_waveform(ss.ctrl_3); |
1945 |
fprintf(fout, " Gate : %s Ring mod.: %s\n", ss.ctrl_3 & 0x01 ? "On " : "Off", ss.ctrl_3 & 0x04 ? "On" : "Off"); |
1946 |
fprintf(fout, " Test bit : %s Synchron.: %s\n", ss.ctrl_3 & 0x08 ? "On " : "Off", ss.ctrl_3 & 0x02 ? "On" : "Off"); |
1947 |
fprintf(fout, " Filter : %s Mute : %s\n", ss.res_filt & 0x04 ? "On" : "Off", ss.mode_vol & 0x80 ? "Yes" : "No"); |
1948 |
|
1949 |
fprintf(fout, "\nFilters/Volume\n"); |
1950 |
fprintf(fout, " Frequency: %04lx\n", (ss.fc_hi << 3) | (ss.fc_lo & 0x07)); |
1951 |
fprintf(fout, " Resonance: %lx\n", ss.res_filt >> 4); |
1952 |
fprintf(fout, " Mode : "); |
1953 |
if (ss.mode_vol & 0x70) { |
1954 |
if (ss.mode_vol & 0x10) fprintf(fout, "Low-pass "); |
1955 |
if (ss.mode_vol & 0x20) fprintf(fout, "Band-pass "); |
1956 |
if (ss.mode_vol & 0x40) fprintf(fout, "High-pass"); |
1957 |
} else |
1958 |
fprintf(fout, "None"); |
1959 |
fprintf(fout, "\n Volume : %lx\n", ss.mode_vol & 0x0f); |
1960 |
} |
1961 |
|
1962 |
static void dump_sid_waveform(uint8 wave) |
1963 |
{ |
1964 |
if (wave & 0xf0) { |
1965 |
if (wave & 0x10) fprintf(fout, "Triangle "); |
1966 |
if (wave & 0x20) fprintf(fout, "Sawtooth "); |
1967 |
if (wave & 0x40) fprintf(fout, "Rectangle "); |
1968 |
if (wave & 0x80) fprintf(fout, "Noise"); |
1969 |
} else |
1970 |
fprintf(fout, "None"); |
1971 |
fputc('\n', fout); |
1972 |
} |
1973 |
|
1974 |
static void view_vic_state(void) |
1975 |
{ |
1976 |
MOS6569State vs; |
1977 |
short i; |
1978 |
|
1979 |
TheVIC->GetState(&vs); |
1980 |
|
1981 |
fprintf(fout, "Raster line : %04lx\n", vs.raster | ((vs.ctrl1 & 0x80) << 1)); |
1982 |
fprintf(fout, "IRQ raster line : %04lx\n\n", vs.irq_raster); |
1983 |
|
1984 |
fprintf(fout, "X scroll : %ld\n", vs.ctrl2 & 7); |
1985 |
fprintf(fout, "Y scroll : %ld\n", vs.ctrl1 & 7); |
1986 |
fprintf(fout, "Horizontal border : %ld columns\n", vs.ctrl2 & 8 ? 40 : 38); |
1987 |
fprintf(fout, "Vertical border : %ld rows\n\n", vs.ctrl1 & 8 ? 25 : 24); |
1988 |
|
1989 |
fprintf(fout, "Display mode : "); |
1990 |
switch (((vs.ctrl1 >> 4) & 6) | ((vs.ctrl2 >> 4) & 1)) { |
1991 |
case 0: |
1992 |
fprintf(fout, "Standard text\n"); |
1993 |
break; |
1994 |
case 1: |
1995 |
fprintf(fout, "Multicolor text\n"); |
1996 |
break; |
1997 |
case 2: |
1998 |
fprintf(fout, "Standard bitmap\n"); |
1999 |
break; |
2000 |
case 3: |
2001 |
fprintf(fout, "Multicolor bitmap\n"); |
2002 |
break; |
2003 |
case 4: |
2004 |
fprintf(fout, "ECM text\n"); |
2005 |
break; |
2006 |
case 5: |
2007 |
fprintf(fout, "Invalid text (ECM+MCM)\n"); |
2008 |
break; |
2009 |
case 6: |
2010 |
fprintf(fout, "Invalid bitmap (ECM+BMM)\n"); |
2011 |
break; |
2012 |
case 7: |
2013 |
fprintf(fout, "Invalid bitmap (ECM+BMM+ECM)\n"); |
2014 |
break; |
2015 |
} |
2016 |
fprintf(fout, "Sequencer state : %s\n", vs.display_state ? "Display" : "Idle"); |
2017 |
fprintf(fout, "Bad line state : %s\n", vs.bad_line ? "Yes" : "No"); |
2018 |
fprintf(fout, "Bad lines enabled : %s\n", vs.bad_line_enable ? "Yes" : "No"); |
2019 |
fprintf(fout, "Video counter : %04lx\n", vs.vc); |
2020 |
fprintf(fout, "Video counter base: %04lx\n", vs.vc_base); |
2021 |
fprintf(fout, "Row counter : %ld\n\n", vs.rc); |
2022 |
|
2023 |
fprintf(fout, "VIC bank : %04lx-%04lx\n", vs.bank_base, vs.bank_base + 0x3fff); |
2024 |
fprintf(fout, "Video matrix base : %04lx\n", vs.matrix_base); |
2025 |
fprintf(fout, "Character base : %04lx\n", vs.char_base); |
2026 |
fprintf(fout, "Bitmap base : %04lx\n\n", vs.bitmap_base); |
2027 |
|
2028 |
fprintf(fout, " Spr.0 Spr.1 Spr.2 Spr.3 Spr.4 Spr.5 Spr.6 Spr.7\n"); |
2029 |
fprintf(fout, "Enabled: "); dump_spr_flags(vs.me); |
2030 |
fprintf(fout, "Data : %04lx %04lx %04lx %04lx %04lx %04lx %04lx %04lx\n", |
2031 |
vs.sprite_base[0], vs.sprite_base[1], vs.sprite_base[2], vs.sprite_base[3], |
2032 |
vs.sprite_base[4], vs.sprite_base[5], vs.sprite_base[6], vs.sprite_base[7]); |
2033 |
fprintf(fout, "MC : %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx\n", |
2034 |
vs.mc[0], vs.mc[1], vs.mc[2], vs.mc[3], vs.mc[4], vs.mc[5], vs.mc[6], vs.mc[7]); |
2035 |
|
2036 |
fprintf(fout, "Mode : "); |
2037 |
for (i=0; i<8; i++) |
2038 |
if (vs.mmc & (1<<i)) |
2039 |
fprintf(fout, "Multi "); |
2040 |
else |
2041 |
fprintf(fout, "Std. "); |
2042 |
|
2043 |
fprintf(fout, "\nX-Exp. : "); dump_spr_flags(vs.mxe); |
2044 |
fprintf(fout, "Y-Exp. : "); dump_spr_flags(vs.mye); |
2045 |
|
2046 |
fprintf(fout, "Prio. : "); |
2047 |
for (i=0; i<8; i++) |
2048 |
if (vs.mdp & (1<<i)) |
2049 |
fprintf(fout, "Back "); |
2050 |
else |
2051 |
fprintf(fout, "Fore "); |
2052 |
|
2053 |
fprintf(fout, "\nSS Coll: "); dump_spr_flags(vs.mm); |
2054 |
fprintf(fout, "SD Coll: "); dump_spr_flags(vs.md); |
2055 |
|
2056 |
fprintf(fout, "\nPending interrupts: "); |
2057 |
dump_vic_ints(vs.irq_flag); |
2058 |
fprintf(fout, "Enabled interrupts: "); |
2059 |
dump_vic_ints(vs.irq_mask); |
2060 |
} |
2061 |
|
2062 |
static void dump_spr_flags(uint8 f) |
2063 |
{ |
2064 |
short i; |
2065 |
|
2066 |
for (i=0; i<8; i++) |
2067 |
if (f & (1<<i)) |
2068 |
fprintf(fout, "Yes "); |
2069 |
else |
2070 |
fprintf(fout, "No "); |
2071 |
|
2072 |
fputc('\n', fout); |
2073 |
} |
2074 |
|
2075 |
static void dump_vic_ints(uint8 i) |
2076 |
{ |
2077 |
if (i & 0x1f) { |
2078 |
if (i & 1) fprintf(fout, "Raster "); |
2079 |
if (i & 2) fprintf(fout, "Spr-Data "); |
2080 |
if (i & 4) fprintf(fout, "Spr-Spr "); |
2081 |
if (i & 8) fprintf(fout, "Lightpen"); |
2082 |
} else |
2083 |
fprintf(fout, "None"); |
2084 |
fputc('\n', fout); |
2085 |
} |
2086 |
|
2087 |
static void view_1541_state(void) |
2088 |
{ |
2089 |
fprintf(fout, "VIA 1:\n"); |
2090 |
fprintf(fout, " Timer 1 Counter: %04x Latch: %04x\n", R1541.via1_t1c, R1541.via1_t1l); |
2091 |
fprintf(fout, " Timer 2 Counter: %04x Latch: %04x\n", R1541.via1_t2c, R1541.via1_t2l); |
2092 |
fprintf(fout, " ACR: %02x\n", R1541.via1_acr); |
2093 |
fprintf(fout, " PCR: %02x\n", R1541.via1_pcr); |
2094 |
fprintf(fout, " Pending interrupts: "); |
2095 |
dump_via_ints(R1541.via1_ifr); |
2096 |
fprintf(fout, " Enabled interrupts: "); |
2097 |
dump_via_ints(R1541.via1_ier); |
2098 |
|
2099 |
fprintf(fout, "\nVIA 2:\n"); |
2100 |
fprintf(fout, " Timer 1 Counter: %04x Latch: %04x\n", R1541.via2_t1c, R1541.via2_t1l); |
2101 |
fprintf(fout, " Timer 2 Counter: %04x Latch: %04x\n", R1541.via2_t2c, R1541.via2_t2l); |
2102 |
fprintf(fout, " ACR: %02x\n", R1541.via2_acr); |
2103 |
fprintf(fout, " PCR: %02x\n", R1541.via2_pcr); |
2104 |
fprintf(fout, " Pending interrupts: "); |
2105 |
dump_via_ints(R1541.via2_ifr); |
2106 |
fprintf(fout, " Enabled interrupts: "); |
2107 |
dump_via_ints(R1541.via2_ier); |
2108 |
} |
2109 |
|
2110 |
static void dump_via_ints(uint8 i) |
2111 |
{ |
2112 |
if (i & 0x7f) { |
2113 |
if (i & 0x40) fprintf(fout, "T1 "); |
2114 |
if (i & 0x20) fprintf(fout, "T2 "); |
2115 |
if (i & 2) fprintf(fout, "CA1 "); |
2116 |
if (i & 1) fprintf(fout, "CA2 "); |
2117 |
if (i & 0x10) fprintf(fout, "CB1 "); |
2118 |
if (i & 8) fprintf(fout, "CB2 "); |
2119 |
if (i & 4) fprintf(fout, "Serial "); |
2120 |
} else |
2121 |
fprintf(fout, "None"); |
2122 |
fputc('\n', fout); |
2123 |
} |
2124 |
|
2125 |
|
2126 |
/* |
2127 |
* Load data |
2128 |
* l start "file" |
2129 |
*/ |
2130 |
|
2131 |
static void load_data(void) |
2132 |
{ |
2133 |
uint16 adr; |
2134 |
FILE *file; |
2135 |
int fc; |
2136 |
|
2137 |
if (!expression(&adr)) |
2138 |
return; |
2139 |
if (the_token == T_END) { |
2140 |
error("Missing file name"); |
2141 |
return; |
2142 |
} |
2143 |
if (the_token != T_STRING) { |
2144 |
error("'\"' around file name expected"); |
2145 |
return; |
2146 |
} |
2147 |
|
2148 |
if (!(file = fopen(the_string, "rb"))) |
2149 |
error("Unable to open file"); |
2150 |
else { |
2151 |
while ((fc = fgetc(file)) != EOF) |
2152 |
SAMWriteByte(adr++, fc); |
2153 |
fclose(file); |
2154 |
} |
2155 |
} |
2156 |
|
2157 |
|
2158 |
/* |
2159 |
* Save data |
2160 |
* s start end "file" |
2161 |
*/ |
2162 |
|
2163 |
static void save_data(void) |
2164 |
{ |
2165 |
bool done = false; |
2166 |
uint16 adr, end_adr; |
2167 |
FILE *file; |
2168 |
|
2169 |
if (!expression(&adr)) |
2170 |
return; |
2171 |
if (!expression(&end_adr)) |
2172 |
return; |
2173 |
if (the_token == T_END) { |
2174 |
error("Missing file name"); |
2175 |
return; |
2176 |
} |
2177 |
if (the_token != T_STRING) { |
2178 |
error("'\"' around file name expected"); |
2179 |
return; |
2180 |
} |
2181 |
|
2182 |
if (!(file = fopen(the_string, "wb"))) |
2183 |
error("Unable to create file"); |
2184 |
else { |
2185 |
do { |
2186 |
if (adr == end_adr) done = true; |
2187 |
|
2188 |
fputc(SAMReadByte(adr++), file); |
2189 |
} while (!done); |
2190 |
fclose(file); |
2191 |
} |
2192 |
} |