ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/SAM.cpp
Revision: 1.3
Committed: 2003-07-09T13:54:22Z (20 years, 8 months ago) by cebix
Branch: MAIN
Changes since 1.2: +4 -1 lines
Log Message:
applied misc fixes that have accumulated over the time

File Contents

# Content
1 /*
2 * SAM.h - Simple Assembler and Monitor With Integrated System Explorer
3 *
4 * Frodo (C) 1994-1997,2002-2003 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(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(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 }