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