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