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 (19 years, 6 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

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