1 /*
2 * tape.c - Tape unit emulation.
3 *
4 * Written by
5 * Ettore Perazzoli <ettore@comm2000.it>
6 * Andreas Boose <viceteam@t-online.de>
7 *
8 * Based on older code by
9 * Jouko Valta <jopi@stekt.oulu.fi>
10 *
11 * This file is part of VICE, the Versatile Commodore Emulator.
12 * See README for copyright notice.
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
27 * 02111-1307 USA.
28 *
29 */
30
31 #include "vice.h"
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "datasette.h"
38 #include "lib.h"
39 #include "log.h"
40 #include "machine.h"
41 #include "maincpu.h"
42 #include "mem.h"
43 #include "network.h"
44 #include "t64.h"
45 #include "tap.h"
46 #include "tape-internal.h"
47 #include "tape.h"
48 #include "tapeimage.h"
49 #include "traps.h"
50 #include "types.h"
51 #include "uiapi.h"
52 #include "vice-event.h"
53
54 /* #define DEBUG_TAPE */
55
56 /* Cassette Format Constants */
57 #define CAS_TYPE_OFFSET 0
58 #define CAS_STAD_OFFSET 1 /* start address */
59 #define CAS_ENAD_OFFSET 3 /* end address */
60 #define CAS_NAME_OFFSET 5 /* filename */
61
62 /* CPU addresses for tape routine variables. */
63 static uint16_t buffer_pointer_addr;
64 static uint16_t st_addr;
65 static uint16_t verify_flag_addr;
66 static uint16_t stal_addr;
67 static uint16_t eal_addr;
68 static uint16_t kbd_buf_addr;
69 static uint16_t kbd_buf_pending_addr;
70 static int irqval;
71 static uint16_t irqtmp;
72
73 /* Flag: has tape been initialized? */
74 static int tape_is_initialized = 0;
75
76 /* Tape traps to be installed. */
77 static const trap_t *tape_traps;
78
79 /* Logging goes here. */
80 static log_t tape_log = LOG_ERR;
81
82 /* The tape image for device 1. */
83 tape_image_t *tape_image_dev1 = NULL;
84
85 /* ------------------------------------------------------------------------- */
86
set_st(uint8_t b)87 static inline void set_st(uint8_t b)
88 {
89 mem_store(st_addr, (uint8_t)(mem_read(st_addr) | b));
90 }
91
92 /* ------------------------------------------------------------------------- */
93
tape_traps_install(void)94 void tape_traps_install(void)
95 {
96 const trap_t *p;
97
98 if (tape_traps != NULL) {
99 for (p = tape_traps; p->func != NULL; p++) {
100 traps_add(p);
101 }
102 }
103 }
104
tape_traps_deinstall(void)105 void tape_traps_deinstall(void)
106 {
107 const trap_t *p;
108
109 if (tape_traps != NULL) {
110 for (p = tape_traps; p->func != NULL; p++) {
111 traps_remove(p);
112 }
113 }
114 }
115
tape_init_vars(const tape_init_t * init)116 static void tape_init_vars(const tape_init_t *init)
117 {
118 /* Set addresses of tape routine variables. */
119 st_addr = init->st_addr;
120 buffer_pointer_addr = init->buffer_pointer_addr;
121 verify_flag_addr = init->verify_flag_addr;
122 irqtmp = init->irqtmp;
123 irqval = init->irqval;
124 stal_addr = init->stal_addr;
125 eal_addr = init->eal_addr;
126
127 kbd_buf_addr = init->kbd_buf_addr;
128 kbd_buf_pending_addr = init->kbd_buf_pending_addr;
129
130 tape_traps = init->trap_list;
131 }
132
133 /* Initialize the tape emulation, using the traps in `trap_list'. */
134 /* FIXME: This should be passed through a struct. */
tape_init(const tape_init_t * init)135 int tape_init(const tape_init_t *init)
136 {
137 if (tape_log == LOG_ERR) {
138 tape_log = log_open("Tape");
139 }
140
141 tape_internal_init();
142 tape_image_init();
143
144 lib_free(tape_image_dev1);
145 tape_image_dev1 = lib_calloc(1, sizeof(tape_image_t));
146
147 tap_init(init);
148
149 tape_init_vars(init);
150 tape_traps_install();
151
152 tape_is_initialized = 1;
153 return 0;
154 }
155
156 /* re-init the traps, needed to switch between different configurations in x128 */
tape_reinit(const tape_init_t * init)157 int tape_reinit(const tape_init_t *init)
158 {
159 if (tape_is_initialized == 0) {
160 return -1;
161 }
162
163 tape_traps_deinstall();
164 tape_traps = NULL;
165
166 tape_init_vars(init);
167 tape_traps_install();
168
169 return 0;
170 }
171
tape_shutdown(void)172 void tape_shutdown(void)
173 {
174 lib_free(tape_image_dev1);
175 }
176
tape_deinstall(void)177 int tape_deinstall(void)
178 {
179 if (!tape_is_initialized) {
180 return -1;
181 }
182
183 if (tape_image_dev1->name != NULL &&
184 tape_image_dev1->type == TAPE_TYPE_T64) {
185 tape_image_detach_internal(1);
186 }
187
188 tape_traps_deinstall();
189
190 tape_traps = NULL;
191
192 tape_is_initialized = 0;
193
194 return 0;
195 }
196
197 /* ------------------------------------------------------------------------- */
198
199 /* Tape traps. These functions implement the standard kernal replacements
200 for the tape functions. Every emulator can either use these traps, or
201 install its own ones, by passing an appropriate `trap_list' to
202 `tape_init()'. */
203
204 /* Find the next Tape Header and load it onto the Tape Buffer. */
tape_find_header_trap(void)205 int tape_find_header_trap(void)
206 {
207 int err;
208 uint8_t *cassette_buffer;
209
210 cassette_buffer = mem_ram + (mem_read(buffer_pointer_addr) | (mem_read((uint16_t)(buffer_pointer_addr + 1)) << 8));
211
212 if (tape_image_dev1->name == NULL
213 || tape_image_dev1->type != TAPE_TYPE_T64) {
214 err = 1;
215 } else {
216 t64_t *t64;
217 t64_file_record_t *rec;
218
219 t64 = (t64_t *)tape_image_dev1->data;
220 rec = NULL;
221
222 err = 0;
223 do {
224 if (t64_seek_to_next_file(t64, 1) < 0) {
225 err = 1;
226 break;
227 }
228
229 rec = t64_get_current_file_record(t64);
230 } while (rec->entry_type != T64_FILE_RECORD_NORMAL);
231
232 if (!err) {
233 cassette_buffer[CAS_TYPE_OFFSET] = machine_tape_type_default();
234 cassette_buffer[CAS_STAD_OFFSET] = rec->start_addr & 0xff;
235 cassette_buffer[CAS_STAD_OFFSET + 1] = rec->start_addr >> 8;
236 cassette_buffer[CAS_ENAD_OFFSET] = rec->end_addr & 0xff;
237 cassette_buffer[CAS_ENAD_OFFSET + 1] = rec->end_addr >> 8;
238 memcpy(cassette_buffer + CAS_NAME_OFFSET,
239 rec->cbm_name, T64_REC_CBMNAME_LEN);
240 }
241 }
242
243 if (err) {
244 cassette_buffer[CAS_TYPE_OFFSET] = TAPE_CAS_TYPE_EOF;
245 }
246
247 mem_store(st_addr, 0); /* Clear the STATUS word. */
248 mem_store(verify_flag_addr, 0);
249
250 if (irqtmp) {
251 mem_store(irqtmp, (uint8_t)(irqval & 0xff));
252 mem_store((uint16_t)(irqtmp + 1), (uint8_t)((irqval >> 8) & 0xff));
253 }
254
255 /* Check if STOP has been pressed. */
256 {
257 int i, n = mem_read(kbd_buf_pending_addr);
258
259 maincpu_set_carry(0);
260 for (i = 0; i < n; i++) {
261 if (mem_read((uint16_t)(kbd_buf_addr + i)) == 0x3) {
262 maincpu_set_carry(1);
263 break;
264 }
265 }
266 }
267
268 maincpu_set_zero(1);
269 return 1;
270 }
271
tape_find_header_trap_plus4(void)272 int tape_find_header_trap_plus4(void)
273 {
274 int err;
275 uint8_t *cassette_buffer;
276
277 cassette_buffer = mem_ram + buffer_pointer_addr;
278
279 if (tape_image_dev1->name == NULL
280 || tape_image_dev1->type != TAPE_TYPE_T64) {
281 err = 1;
282 } else {
283 t64_t *t64;
284 t64_file_record_t *rec;
285
286 t64 = (t64_t *)tape_image_dev1->data;
287 rec = NULL;
288
289 err = 0;
290 do {
291 if (t64_seek_to_next_file(t64, 1) < 0) {
292 err = 1;
293 break;
294 }
295
296 rec = t64_get_current_file_record(t64);
297 } while (rec->entry_type != T64_FILE_RECORD_NORMAL);
298
299 if (!err) {
300 mem_store(0xF8, TAPE_CAS_TYPE_BAS);
301 cassette_buffer[CAS_STAD_OFFSET - 1] = rec->start_addr & 0xff;
302 cassette_buffer[CAS_STAD_OFFSET] = rec->start_addr >> 8;
303 cassette_buffer[CAS_ENAD_OFFSET - 1] = rec->end_addr & 0xff;
304 cassette_buffer[CAS_ENAD_OFFSET] = rec->end_addr >> 8;
305 memcpy(cassette_buffer + CAS_NAME_OFFSET - 1,
306 rec->cbm_name, T64_REC_CBMNAME_LEN);
307 }
308 }
309
310 if (err) {
311 mem_store(0xF8, TAPE_CAS_TYPE_EOF);
312 }
313
314 mem_store(0xb6, 0x33);
315 mem_store(0xb7, 0x03);
316
317 mem_store(st_addr, 0); /* Clear the STATUS word. */
318 mem_store(verify_flag_addr, 0);
319
320 /* Check if STOP has been pressed. */
321 {
322 int i, n = mem_read(kbd_buf_pending_addr);
323
324 maincpu_set_carry(0);
325 for (i = 0; i < n; i++) {
326 if (mem_read((uint16_t)(kbd_buf_addr + i)) == 0x3) {
327 maincpu_set_carry(1);
328 break;
329 }
330 }
331 }
332
333 maincpu_set_zero(1);
334 return 1;
335 }
336
337 /* Cassette Data transfer trap.
338
339 XR flags the function to be performed on IRQ:
340
341 08 Write tape
342 0a Write tape leader
343 0c Normal keyscan
344 0e Read tape
345
346 Luckily enough, these values are valid for all the machines. */
tape_receive_trap(void)347 int tape_receive_trap(void)
348 {
349 int len;
350 uint16_t start, end;
351 uint8_t st;
352
353 start = (mem_read(stal_addr) | (mem_read((uint16_t)(stal_addr + 1)) << 8));
354 end = (mem_read(eal_addr) | (mem_read((uint16_t)(eal_addr + 1)) << 8));
355
356 switch (maincpu_get_x()) {
357 case 0x0e:
358 {
359 int amount;
360
361 len = (int)(end - start);
362 amount = t64_read((t64_t *)tape_image_dev1->data, mem_ram + (int)start, len);
363 if (amount == len) {
364 st = 0x40; /* EOF */
365 } else {
366 st = 0x10;
367
368 log_warning(tape_log,
369 "Unexpected end of tape: file may be truncated.");
370 }
371 }
372 break;
373 default:
374 log_error(tape_log, "Kernal command %x not supported.",
375 maincpu_get_x());
376 st = 0x40;
377 break;
378 }
379
380 /* Set registers and flags like the Kernal routine does. */
381
382 if (irqtmp) {
383 mem_store(irqtmp, (uint8_t)(irqval & 0xff));
384 mem_store((uint16_t)(irqtmp + 1), (uint8_t)((irqval >> 8) & 0xff));
385 }
386
387 set_st(st); /* EOF and possible errors */
388
389 maincpu_set_carry(0);
390 maincpu_set_interrupt(0);
391 return 1;
392 }
393
tape_receive_trap_plus4(void)394 int tape_receive_trap_plus4(void)
395 {
396 uint16_t start, end, len;
397 uint8_t st;
398
399 start = (mem_read(stal_addr) | (mem_read((uint16_t)(stal_addr + 1)) << 8));
400 end = (mem_read(eal_addr) | (mem_read((uint16_t)(eal_addr + 1)) << 8));
401
402 /* Read block. */
403 len = end - start;
404
405 if (t64_read((t64_t *)tape_image_dev1->data,
406 mem_ram + (int) start, (int)len) == (int) len) {
407 st = 0x40; /* EOF */
408 } else {
409 st = 0x10;
410
411 log_warning(tape_log,
412 "Unexpected end of tape: file may be truncated.");
413 }
414
415 /* Set registers and flags like the Kernal routine does. */
416
417
418 set_st(st); /* EOF and possible errors */
419 return 1;
420 }
421
tape_get_file_name(void)422 const char *tape_get_file_name(void)
423 {
424 if (tape_image_dev1 == NULL) {
425 return "";
426 }
427
428 return tape_image_dev1->name;
429 }
430
tape_tap_attached(void)431 int tape_tap_attached(void)
432 {
433 if (tape_image_dev1->name != NULL
434 && tape_image_dev1->type == TAPE_TYPE_TAP) {
435 return 1;
436 }
437
438 return 0;
439 }
440
441 /* ------------------------------------------------------------------------- */
442
443 /* Detach. */
tape_image_detach_internal(unsigned int unit)444 int tape_image_detach_internal(unsigned int unit)
445 {
446 int retval = 0;
447 char event_data[2];
448
449 if (unit != 1) {
450 return -1;
451 }
452
453 if (tape_image_dev1 == NULL || tape_image_dev1->name == NULL) {
454 return 0;
455 }
456
457 switch (tape_image_dev1->type) {
458 case TAPE_TYPE_T64:
459 log_message(tape_log,
460 "Detaching T64 image `%s'.", tape_image_dev1->name);
461 /* Tape detached: release play button. */
462 datasette_set_tape_sense(0);
463 break;
464 case TAPE_TYPE_TAP:
465 log_message(tape_log,
466 "Detaching TAP image `%s'.", tape_image_dev1->name);
467 datasette_set_tape_image(NULL);
468
469 tape_traps_install();
470 break;
471 default:
472 log_error(tape_log, "Unknown tape type %u.",
473 tape_image_dev1->type);
474 }
475
476 retval = tape_image_close(tape_image_dev1);
477
478 ui_display_tape_current_image("");
479
480 event_data[0] = (char)unit;
481 event_data[1] = 0;
482
483 event_record(EVENT_ATTACHTAPE, (void *)event_data, 2);
484
485 return retval;
486 }
487
tape_image_detach(unsigned int unit)488 int tape_image_detach(unsigned int unit)
489 {
490 char event_data[2];
491
492 if (unit != 1) {
493 return -1;
494 }
495
496 event_data[0] = (char)unit;
497 event_data[1] = 0;
498
499 if (event_playback_active()) {
500 return -1;
501 }
502
503 if (network_connected()) {
504 network_event_record(EVENT_ATTACHTAPE, (void *)event_data, 2);
505 return 0;
506 }
507
508 return tape_image_detach_internal(unit);
509 }
510
511 /* Attach. */
tape_image_attach_internal(unsigned int unit,const char * name)512 static int tape_image_attach_internal(unsigned int unit, const char *name)
513 {
514 tape_image_t tape_image;
515
516 if (unit != 1) {
517 return -1;
518 }
519
520 if (!name || !*name) {
521 return -1;
522 }
523
524 tape_image.name = lib_strdup(name);
525 tape_image.read_only = 0;
526
527 if (tape_image_open(&tape_image) < 0) {
528 lib_free(tape_image.name);
529 log_error(tape_log, "Cannot open file `%s'", name);
530 return -1;
531 }
532
533 tape_image_detach_internal(unit);
534
535 memcpy(tape_image_dev1, &tape_image, sizeof(tape_image_t));
536
537 ui_display_tape_current_image(tape_image_dev1->name);
538
539 switch (tape_image_dev1->type) {
540 case TAPE_TYPE_T64:
541 log_message(tape_log, "T64 image '%s' attached.", name);
542 /* Tape attached: press play button. */
543 datasette_set_tape_sense(1);
544 break;
545 case TAPE_TYPE_TAP:
546 datasette_set_tape_image((tap_t *)tape_image_dev1->data);
547 log_message(tape_log, "TAP image '%s' attached.", name);
548 log_message(tape_log, "TAP image version: %i, system: %i.",
549 ((tap_t *)tape_image_dev1->data)->version,
550 ((tap_t *)tape_image_dev1->data)->system);
551 tape_traps_deinstall();
552 break;
553 default:
554 log_error(tape_log, "Unknown tape type %u.",
555 tape_image_dev1->type);
556 return -1;
557 }
558
559 event_record_attach_image(unit, 0, name, tape_image.read_only);
560
561 return 0;
562 }
563
tape_image_attach(unsigned int unit,const char * name)564 int tape_image_attach(unsigned int unit, const char *name)
565 {
566 if (event_playback_active()) {
567 return -1;
568 }
569
570 if (network_connected()) {
571 network_attach_image(unit, name);
572 return 0;
573 }
574
575 return tape_image_attach_internal(unit, name);
576 }
577
tape_image_event_playback(unsigned int unit,const char * filename)578 void tape_image_event_playback(unsigned int unit, const char *filename)
579 {
580 if (filename == NULL || filename[0] == 0) {
581 tape_image_detach_internal(unit);
582 } else {
583 tape_image_attach_internal(unit, filename);
584 }
585 }
586