1 /*
2  * mon_file.c - The VICE built-in monitor file functions.
3  *
4  * Written by
5  *  Andreas Boose <viceteam@t-online.de>
6  *  Daniel Sladic <sladic@eecg.toronto.edu>
7  *  Ettore Perazzoli <ettore@comm2000.it>
8  *
9  * This file is part of VICE, the Versatile Commodore Emulator.
10  * See README for copyright notice.
11  *
12  *  This program is free software; you can redistribute it and/or modify
13  *  it under the terms of the GNU General Public License as published by
14  *  the Free Software Foundation; either version 2 of the License, or
15  *  (at your option) any later version.
16  *
17  *  This program is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *  GNU General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public License
23  *  along with this program; if not, write to the Free Software
24  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25  *  02111-1307  USA.
26  *
27  */
28 
29 #include "vice.h"
30 
31 #include <stdio.h>
32 #include <string.h>
33 
34 #include "archdep.h"
35 #include "attach.h"
36 #include "autostart.h"
37 #include "cartridge.h"
38 #include "charset.h"
39 #include "lib.h"
40 #include "machine.h"
41 #include "mem.h"
42 #include "montypes.h"
43 #include "mon_drive.h"
44 #include "mon_file.h"
45 #include "mon_util.h"
46 #include "tape.h"
47 #include "uimon.h"
48 #include "vdrive-iec.h"
49 #include "vdrive.h"
50 
51 
52 #define ADDR_LIMIT(x) ((uint16_t)(addr_mask(x)))
53 
54 #define curbank (mon_interfaces[mem]->current_bank)
55 
56 static FILE *fp;
57 static vdrive_t *vdrive;
58 /* we require an EOF buffer to support CBM EOFs. */
59 static int mon_file_read_eof[4][16];
60 
mon_file_open(const char * filename,unsigned int secondary,int device)61 static int mon_file_open(const char *filename,
62                          unsigned int secondary, /* 0: load, 1: save */
63                          int device)
64 {
65     char pname[17];
66     const char *s;
67     int i;
68 
69     /* TODO: drive 1 */
70     unsigned int drive = 0;
71     const char *fspath = NULL;
72     char *fullpath;
73 
74     fp = NULL;
75 
76     switch (device) {
77         case 0:
78             fp = fopen(filename, (secondary == 0) ? MODE_READ : MODE_WRITE);
79             if (fp == NULL) {
80                 return -1;
81             }
82             break;
83         case 8:
84         case 9:
85         case 10:
86         case 11:
87             vdrive = file_system_get_vdrive((unsigned int)device, drive);
88             if (vdrive == NULL || vdrive->image == NULL) {
89                 /* if vdrive did not succeed, try fsdevice */
90                 if ((fspath = mon_drive_get_fsdevice_path(device))) {
91                     fullpath = archdep_join_paths(fspath, filename, NULL);
92                     fp = fopen(fullpath, (secondary == 0) ? MODE_READ : MODE_WRITE);
93                     lib_free(fullpath);
94                     if (fp != NULL) {
95                         return 0;
96                     }
97                 }
98                 return -1;
99             }
100 
101             /* convert filename to petscii */
102             s = filename;
103             for (i = 0; (i < 16) && (*s); ++i) {
104                 pname[i] = charset_p_topetcii(*s);
105                 ++s;
106             }
107             pname[i] = 0;
108 
109             if (vdrive_iec_open(vdrive, (const uint8_t *)pname,
110                                 (int)strlen(pname), secondary, NULL) != SERIAL_OK) {
111                 return -1;
112             }
113 
114             /* initialize EOF buffer. */
115             mon_file_read_eof[device - 8][secondary] = 0;
116             break;
117         default:
118             return -1;
119     }
120     return 0;
121 }
122 
mon_file_read(uint8_t * data,unsigned int secondary,int device)123 static int mon_file_read(uint8_t *data, unsigned int secondary, int device)
124 {
125     if (fp) {
126         if (fread((char *)data, 1, 1, fp) < 1) {
127             return -1;
128         }
129     } else if (device >= 8) {
130         /* Return EOF if we hit a CBM EOF on the last read. */
131         if (mon_file_read_eof[device - 8][secondary]) {
132             *data = 0xc7;
133             return -1;
134         }
135         /* Set next EOF based on CBM EOF. */
136         mon_file_read_eof[device - 8][secondary] =
137             vdrive_iec_read(vdrive, data, secondary);
138     }
139     return 0;
140 }
141 
mon_file_write(uint8_t data,unsigned int secondary,int device)142 static int mon_file_write(uint8_t data, unsigned int secondary, int device)
143 {
144     if (fp) {
145         if (fwrite((char *)&data, 1, 1, fp) < 1) {
146             return -1;
147         }
148     } else if (device >= 8) {
149         if (vdrive_iec_write(vdrive, data, secondary) != SERIAL_OK) {
150             return -1;
151         }
152     }
153     return 0;
154 }
155 
mon_file_close(unsigned int secondary,int device)156 static int mon_file_close(unsigned int secondary, int device)
157 {
158     if (fp) {
159         if (fclose(fp) != 0) {
160             return -1;
161         }
162     } else if (device >= 8) {
163         if (vdrive_iec_close(vdrive, secondary) != SERIAL_OK) {
164             return -1;
165         }
166     }
167     return 0;
168 }
169 
170 
mon_file_load(const char * filename,int device,MON_ADDR start_addr,bool is_bload)171 void mon_file_load(const char *filename, int device, MON_ADDR start_addr,
172                    bool is_bload)
173 {
174     uint16_t adr, load_addr = 0, basic_addr;
175     uint8_t b1 = 0, b2 = 0;
176     int ch = 0;
177     MEMSPACE mem;
178     int origbank = 0;
179 
180     if (mon_file_open(filename, 0, device) < 0) {
181         mon_out("Cannot open %s.\n", filename);
182         return;
183     }
184 
185     /* if loading a .prg file, read/skip the start address */
186     if (is_bload == FALSE) {
187         mon_file_read(&b1, 0, device);
188         mon_file_read(&b2, 0, device);
189         load_addr = (uint8_t)b1 | ((uint8_t)b2 << 8);
190     }
191 
192     mem_get_basic_text(&basic_addr, NULL); /* get BASIC start */
193     mon_evaluate_default_addr(&start_addr); /* get target addr given in monitor */
194 
195     if (!mon_is_valid_addr(start_addr)) {   /* No Load address given */
196         if (is_bload == TRUE) {
197             /* when loading plain binary, load addr is required */
198             mon_out("Invalid LOAD address given.\n");
199             mon_file_close(0, device);
200             return;
201         }
202 
203         if (load_addr == basic_addr) {   /* Load to BASIC start */
204             adr = basic_addr;
205             mem = e_comp_space;
206         } else {
207             start_addr = new_addr(e_default_space, load_addr);
208             mon_evaluate_default_addr(&start_addr);
209             adr = addr_location(start_addr);
210             mem = addr_memspace(start_addr);
211         }
212     } else {
213         adr = addr_location(start_addr);
214         mem = addr_memspace(start_addr);
215     }
216 
217     mon_out("Loading %s from %04X ", filename, adr);
218 
219     if (machine_class == VICE_MACHINE_C64DTV) {
220         origbank = curbank;
221     }
222 
223     do {
224         uint8_t load_byte;
225 
226         if (mon_file_read(&load_byte, 0, device) < 0) {
227             break;
228         }
229         mon_set_mem_val(mem, ADDR_LIMIT(adr + ch), load_byte);
230 
231         /* Hack to be able to read large .prgs for x64dtv */
232         if ((machine_class == VICE_MACHINE_C64DTV) &&
233             (ADDR_LIMIT(adr + ch) == 0xffff) &&
234             ((curbank >= mem_bank_from_name("ram00")) && (curbank <= mem_bank_from_name("ram1f")))) {
235             curbank++;
236             if (curbank > mem_bank_from_name("ram1f")) {
237                 curbank = mem_bank_from_name("ram00");
238             }
239             mon_out("Crossing 64KiB boundary.\n");
240         }
241         ch++;
242     } while (1);
243 
244     if (machine_class == VICE_MACHINE_C64DTV) {
245         curbank = origbank;
246     }
247 
248     mon_out("to %04X (%04X bytes)\n", ADDR_LIMIT((adr + ch) - 1), (unsigned int)ch);
249 
250     /* set end of load addresses like kernal load if
251      * 1. loading .prg file
252      * 2. loading to BASIC start
253      * 3. loading to computer bank/memory
254      */
255     if ((is_bload == FALSE) && (load_addr == basic_addr) && (mem == e_comp_space)) {
256         mem_set_basic_text(adr, (uint16_t)(adr + ch));
257     }
258 
259     mon_file_close(0, device);
260 }
261 
mon_file_save(const char * filename,int device,MON_ADDR start_addr,MON_ADDR end_addr,bool is_bsave)262 void mon_file_save(const char *filename, int device, MON_ADDR start_addr,
263                    MON_ADDR end_addr, bool is_bsave)
264 {
265     uint16_t adr, end;
266     long len;
267     int ch = 0;
268     MEMSPACE mem;
269 
270     len = mon_evaluate_address_range(&start_addr, &end_addr, TRUE, -1);
271 
272     if (len < 0) {
273         mon_out("Invalid range.\n");
274         return;
275     }
276 
277     mem = addr_memspace(start_addr);
278     adr = addr_location(start_addr);
279     end = addr_location(end_addr);
280 
281     if (end < adr) {
282         mon_out("Start address must be below end address.\n");
283         return;
284     }
285 
286     if (mon_file_open(filename, 1, device) < 0) {
287         mon_out("Cannot open %s.\n", filename);
288         return;
289     }
290 
291     printf("Saving file `%s'...\n", filename);
292 
293     if (is_bsave == FALSE) {
294         if (mon_file_write((uint8_t)(adr & 0xff), 1, device) < 0
295             || mon_file_write((uint8_t)((adr >> 8) & 0xff), 1, device) < 0) {
296             mon_out("Saving for `%s' failed.\n", filename);
297             mon_file_close(1, device);
298             return;
299         }
300     }
301 
302     do {
303         unsigned char save_byte;
304 
305         save_byte = mon_get_mem_val(mem, (uint16_t)(adr + ch));
306         if (mon_file_write(save_byte, 1, device) < 0) {
307             mon_out("Saving for `%s' failed.\n", filename);
308             break;
309         }
310         ch++;
311     } while ((adr + ch) <= end);
312 
313     mon_file_close(1, device);
314 }
315 
mon_file_verify(const char * filename,int device,MON_ADDR start_addr,bool is_bverify)316 void mon_file_verify(const char *filename, int device, MON_ADDR start_addr, bool is_bverify)
317 {
318     uint16_t adr, load_addr = 0;
319     uint8_t b1 = 0, b2 = 0;
320     int ch = 0;
321     MEMSPACE mem;
322     int origbank = 0;
323     int diffcount = 0;
324 
325     if (mon_file_open(filename, 0, device) < 0) {
326         mon_out("Cannot open %s.\n", filename);
327         return;
328     }
329 
330     /* if loading a .prg file, read/skip the start address */
331     if (is_bverify == FALSE) {
332         mon_file_read(&b1, 0, device);
333         mon_file_read(&b2, 0, device);
334         load_addr = (uint8_t)b1 | ((uint8_t)b2 << 8);
335     }
336 
337     mon_evaluate_default_addr(&start_addr); /* get target addr given in monitor */
338 
339     if (!mon_is_valid_addr(start_addr)) {   /* No Load address given */
340         if (is_bverify == TRUE) {
341             /* when loading plain binary, load addr is required */
342             mon_out("Invalid VERIFY address given.\n");
343             mon_file_close(0, device);
344             return;
345         }
346 
347         start_addr = new_addr(e_default_space, load_addr);
348         mon_evaluate_default_addr(&start_addr);
349     }
350     adr = addr_location(start_addr);
351     mem = addr_memspace(start_addr);
352 
353     mon_out("Verifying %s from %04X ", filename, adr);
354 
355     if (machine_class == VICE_MACHINE_C64DTV) {
356         origbank = curbank;
357     }
358 
359     do {
360         uint8_t load_byte, mem_byte;
361 
362         if (mon_file_read(&load_byte, 0, device) < 0) {
363             break;
364         }
365         mem_byte = mon_get_mem_val(mem, ADDR_LIMIT(adr + ch));
366         if (load_byte != mem_byte) {
367             if (diffcount == 0) {
368                 mon_out("\naddr:mem file\n");
369             }
370             mon_out("%04x: ", ADDR_LIMIT(adr + ch));
371             mon_out("%02x %02x", mem_byte, load_byte);
372             mon_out("\n");
373             diffcount++;
374         }
375 
376         /* Hack to be able to read large .prgs for x64dtv */
377         if ((machine_class == VICE_MACHINE_C64DTV) &&
378             (ADDR_LIMIT(adr + ch) == 0xffff) &&
379             ((curbank >= mem_bank_from_name("ram00")) && (curbank <= mem_bank_from_name("ram1f")))) {
380             curbank++;
381             if (curbank > mem_bank_from_name("ram1f")) {
382                 curbank = mem_bank_from_name("ram00");
383             }
384             mon_out("Crossing 64KiB boundary.\n");
385         }
386         ch++;
387     } while (1);
388 
389     if (machine_class == VICE_MACHINE_C64DTV) {
390         curbank = origbank;
391     }
392 
393     mon_out("to %04X (%04X bytes)\n", ADDR_LIMIT((adr + ch) - 1), (unsigned int)ch);
394     if (diffcount > 0) {
395         mon_out("%d byte(s) different\n", diffcount);
396     }
397 
398     mon_file_close(0, device);
399 }
400 
401 
mon_attach(const char * filename,int device)402 void mon_attach(const char *filename, int device)
403 {
404     switch (device) {
405         case 1:
406             if (machine_class == VICE_MACHINE_C64DTV) {
407                 mon_out("Unimplemented.\n");
408             } else if (tape_image_attach(device, filename)) {
409                 mon_out("Failed.\n");
410             }
411             break;
412         case 8:
413         case 9:
414         case 10:
415         case 11:
416             /* TODO: drive 1? */
417             if (file_system_attach_disk(device, 0, filename)) {
418                 mon_out("Failed.\n");
419             }
420             break;
421         case 32:
422             if (mon_cart_cmd.cartridge_attach_image != NULL) {
423                 if ((mon_cart_cmd.cartridge_attach_image)(CARTRIDGE_CRT, filename)) {
424                     mon_out("Failed.\n");
425                 }
426             } else {
427                 mon_out("Unsupported.\n");
428             }
429             break;
430         default:
431             mon_out("Unknown device %i.\n", device);
432             break;
433     }
434 }
435 
mon_detach(int device)436 void mon_detach(int device)
437 {
438     switch (device) {
439         case 1:
440             if (machine_class == VICE_MACHINE_C64DTV) {
441                 mon_out("Unimplemented.\n");
442             } else {
443                 tape_image_detach(device);
444             }
445             break;
446         case 8:
447         case 9:
448         case 10:
449         case 11:
450             /* TODO: drive 1? */
451             file_system_detach_disk(device, 0);
452             break;
453         case 32:
454             if (mon_cart_cmd.cartridge_detach_image != NULL) {
455                 (mon_cart_cmd.cartridge_detach_image)(-1); /* FIXME: param should be cart id, -1 detaches all */
456             } else {
457                 mon_out("Unsupported.\n");
458             }
459             break;
460         default:
461             mon_out("Unknown device %i.\n", device);
462             break;
463     }
464 }
465 
mon_autostart(const char * image_name,int file_index,int run)466 int mon_autostart(const char *image_name,
467                    int file_index,
468                    int run)
469 {
470     int result = 0;
471 
472     mon_out("auto%s %s #%d\n", run ? "starting" : "loading",
473             image_name, file_index);
474     result = autostart_autodetect_opt_prgname(image_name, file_index,
475                                      run ? AUTOSTART_MODE_RUN : AUTOSTART_MODE_LOAD);
476 
477     /* leave monitor but return after autostart */
478     autostart_trigger_monitor(1);
479     exit_mon = 1;
480 
481     return result;
482 }
483