1 /*
2 * parallel.c - IEEE488 emulation.
3 *
4 * Written by
5 * Andre Fachat <a.fachat@physik.tu-chemnitz.de>
6 * Andreas Boose <viceteam@t-online.de>
7 *
8 * This file is part of VICE, the Versatile Commodore Emulator.
9 * See README for copyright notice.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24 * 02111-1307 USA.
25 *
26 */
27
28 /* This file contains the ieee488 emulator.
29 * The ieee488 emulator calls (modifed) routines from serial.c
30 * to use the standard floppy interface.
31 * The current state of the bus and methods to set output lines
32 * are exported.
33 * This hardware emulation is necessary, as different PET kernels would
34 * need different traps. But it's also much faster than the (hardware
35 * simulated) serial bus, as it's parallel. So we don't need traps.
36 */
37
38 /* FIXME: This should have its own log instead of using `LOG_DEFAULT'. */
39
40 #include "vice.h"
41
42 #include <stdio.h>
43
44 #include "archdep.h"
45 #include "cmdline.h"
46 #include "drive.h"
47 #include "drivetypes.h"
48 #include "ieee.h"
49 #include "log.h"
50 #include "maincpu.h"
51 #include "parallel-trap.h"
52 #include "parallel.h"
53 #include "resources.h"
54 #include "types.h"
55
56
57 #define PARALLEL_DEBUG_VERBOSE
58 static int parallel_emu = 1;
59
parallel_bus_enable(int enable)60 void parallel_bus_enable(int enable)
61 {
62 parallel_emu = enable;
63 }
64
65 /***************************************************************************
66 * IEEE488 bus lines
67 */
68
69 /* state of the bus lines -> "if (parallel_eoi) { eoi is active }" */
70 uint8_t parallel_eoi = 0;
71 uint8_t parallel_ndac = 0;
72 uint8_t parallel_nrfd = 0;
73 uint8_t parallel_dav = 0;
74 uint8_t parallel_atn = 0;
75
76 uint8_t parallel_bus = 0xff; /* data lines */
77
78 static int par_status = 0; /* lower 8 bits = PET par_status, upper bits own */
79
80
81 /***************************************************************************
82 * State engine for the parallel bus
83 *
84 * Names here are as seen from a device, not from the PET
85 *
86 * Possible States
87 * WaitATN Wait for ATN, ignore everything else
88 *
89 * In1 Wait for DAV low when reading a byte
90 * In2 Wait for DAV high when reading a byte
91 *
92 * OldPet The PET 3032 doesn't set NRFD and NDAC low
93 * before releasing ATN after a TALK command,
94 * wait for a NDAC low first!
95 *
96 * Out1 Wait for NRFD high when sending a byte
97 * Out1a Wait for NRFD low when sending a byte
98 * Trying to save this didn't work
99 * Out2 Wait for NDAC high when sending a byte
100 *
101 *
102 * Each state reacts on the different line transitions.
103 *
104 * ATN_true, ATN_false, NDAC_true, NDAC_false,
105 * NRFD_true, NRFD_false, DAV_true, DAV_false
106 *
107 *
108 * Some common functions are:
109 *
110 * ResetBus Set all lines high
111 * ignore ignore any transition
112 * unexpected this transition is unexpected
113 *
114 * Go change the state
115 *
116 * Globals:
117 *
118 * Trans[] name of the transitions
119 * State[] jump table
120 * state actual state
121 */
122
123 #define NTRANS 8 /* number of possible transitions */
124 #define NSTATE 7
125
126 /* States */
127
128 #define WaitATN 0
129 #define In1 1
130 #define In2 2
131 #define OldPet 3
132 #define Out1 4
133 #define Out1a 5
134 #define Out2 6
135
136 /* Transitions */
137
138 #define ATN_true 0 /* active low: 0 on the physical bus */
139 #define ATN_false 1
140 #define DAV_true 2 /* active low: 0 on the physical bus */
141 #define DAV_false 3
142 #define NDAC_true 4 /* active low: 0 on the physical bus */
143 #define NDAC_false 5
144 #define NRFD_true 6 /* active low: 0 on the physical bus */
145 #define NRFD_false 7
146
147 typedef struct State_t {
148 const char *name;
149 void (*m[NTRANS])(int);
150 } State_t;
151
152 #ifdef DEBUG
153 static const char *Trans[NTRANS] = {
154 "ATN true", "ATN false", "DAV true", "DAV false",
155 "NDAC true", "NDAC false", "NRFD true", "NRFD false"
156 };
157 #endif
158
159 static State_t State[NSTATE];
160
161 static int state = WaitATN;
162
163 #define Go(a) state = (a); return
164 #define isListening() ((par_status & 0xf000) == 0x2000)
165 #define isTalking() ((par_status & 0xf000) == 0x4000)
166
167 #if defined(DEBUG) && defined(PARALLEL_DEBUG_VERBOSE)
DoTrans(int tr)168 static void DoTrans(int tr)
169 {
170 if (debug.ieee) {
171 log_debug("DoTrans(%s).%s", State[state].name, Trans[tr]);
172 }
173 State[state].m[tr](tr);
174 if (debug.ieee) {
175 log_debug(" -> %s", State[state].name);
176 }
177 }
178 #else
179 #define DoTrans(a) State[state].m[(a)]((a))
180 #endif
181
ResetBus(void)182 static void ResetBus(void)
183 {
184 parallel_emu_set_dav(0);
185 parallel_emu_set_eoi(0);
186 parallel_emu_set_nrfd(0);
187 parallel_emu_set_ndac(0);
188 parallel_emu_set_bus(0xff);
189 par_status = 0;
190 }
191
192 /**************************************************************************
193 * transition functions for the state engine
194 */
195
196 /* ignoreown should ignore the transition only when it is initiated
197 * by the virtual IEEE488 device itself. This is not yet implemented,
198 * so we just ignore all of em */
199
200 #define ignoreown ignore
201
ignore(int i)202 static void ignore(int i)
203 {
204 }
205
unexpected(int trans)206 static void unexpected(int trans)
207 {
208 #ifdef DEBUG
209 if (debug.ieee) {
210 log_debug("IEEE488: unexpected line transition in state %s: %s.",
211 State[state].name, Trans[trans]);
212 }
213 #endif
214 }
215
WATN_ATN_true(int tr)216 static void WATN_ATN_true(int tr)
217 {
218 parallel_emu_set_ndac(1);
219 parallel_emu_set_dav(0);
220 parallel_emu_set_eoi(0);
221 parallel_emu_set_bus(0xff);
222 parallel_emu_set_nrfd(0);
223 Go(In1);
224 }
225
226 #define In1_ATN_true WATN_ATN_true
227
In1_ATN_false(int tr)228 static void In1_ATN_false(int tr)
229 {
230 if (par_status & 0xff) {
231 ResetBus();
232 Go(WaitATN);
233 } else {
234 if (isListening()) {
235 Go(In1);
236 } else {
237 if (isTalking()) {
238 ResetBus();
239 if (!parallel_ndac) { /* old pet... */
240 Go(OldPet);
241 } else {
242 State[OldPet].m[NDAC_true](tr);
243 return;
244 }
245 } else {
246 #ifdef DEBUG
247 if (debug.ieee) {
248 log_debug("IEEE488: Ouch, something weird happened: %s got %s",
249 State[In1].name, Trans[tr]);
250 }
251 #endif
252 ResetBus();
253 Go(WaitATN);
254 }
255 }
256 }
257 }
258
In1_DAV_true(int tr)259 static void In1_DAV_true(int tr)
260 {
261 static uint8_t b;
262
263 parallel_emu_set_nrfd(1);
264 b = parallel_bus;
265 parallel_emu_set_ndac(0);
266
267 if (parallel_atn) {
268 par_status = parallel_trap_attention(b ^ 0xff);
269 } else {
270 par_status = parallel_trap_sendbyte((uint8_t)(b ^ 0xff));
271 }
272 #ifdef DEBUG
273 if (debug.ieee) {
274 log_debug("IEEE488: sendbyte returns %04x",
275 (unsigned int)par_status);
276 }
277 #endif
278
279 Go(In2);
280 }
281
In1_NDAC_true(int tr)282 static void In1_NDAC_true(int tr)
283 {
284 if (!parallel_atn) {
285 unexpected(tr);
286 }
287 }
288
In1_NRFD_true(int tr)289 static void In1_NRFD_true(int tr)
290 {
291 if (!parallel_atn) {
292 ignoreown(tr);
293 }
294 }
295
296
In1_NRFD_false(int tr)297 static void In1_NRFD_false(int tr)
298 {
299 if (!parallel_atn) {
300 unexpected(tr);
301 }
302 }
303
304 #define In2_ATN_true WATN_ATN_true
305
In2_ATN_false(int a)306 static void In2_ATN_false(int a)
307 { /* atn data transfer interrupted */
308 ResetBus();
309 Go(WaitATN); /* ??? */
310 }
311
In2_DAV_false(int tr)312 static void In2_DAV_false(int tr)
313 {
314 parallel_emu_set_ndac(1);
315 parallel_emu_set_nrfd(0);
316
317 Go(In1);
318 }
319
In2_NDAC_false(int tr)320 static void In2_NDAC_false(int tr)
321 {
322 if (!parallel_atn) {
323 unexpected(tr);
324 }
325 }
326
327 /* OldPET fixed PET2*** and PET3*** IEEE, as well as CBM610 */
328
329 #define OPet_ATN_true WATN_ATN_true
330
OPet_NDAC_true(int tr)331 static void OPet_NDAC_true(int tr)
332 {
333 if (!parallel_nrfd) {
334 State[Out1].m[NRFD_false](tr);
335 return;
336 } else {
337 Go(Out1);
338 }
339 }
340
341 /* this is for CBM 610 only */
342
OPet_NRFD_true(int tr)343 static void OPet_NRFD_true(int tr)
344 {
345 #ifdef DEBUG
346 if (debug.ieee) {
347 log_debug("OPet_NRFD_true()");
348 }
349 #endif
350 State[Out1].m[NRFD_false](tr);
351 }
352
353 #define Out1_ATN_true WATN_ATN_true
354
Out1_NRFD_false(int tr)355 static void Out1_NRFD_false(int tr)
356 {
357 static uint8_t b;
358
359 par_status = parallel_trap_receivebyte(&b, 1);
360 #ifdef DEBUG
361 if (par_status & PAR_STATUS_DEVICE_NOT_PRESENT) {
362 /* If we get to this function, this status should never be possible */
363 log_error(LOG_DEFAULT, "Some device is talker but not present as virtual device");
364 }
365 #endif
366 parallel_emu_set_bus((uint8_t)(b ^ 0xff));
367
368 if (par_status & PAR_STATUS_EOI) {
369 parallel_emu_set_eoi(1);
370 } else {
371 parallel_emu_set_eoi(0);
372 }
373
374 parallel_emu_set_dav(1);
375
376 Go(Out1a);
377 }
378
379 #define Out1a_ATN_true WATN_ATN_true
380
Out1a_NRFD_true(int tr)381 static void Out1a_NRFD_true(int tr)
382 {
383 Go(Out2);
384 }
385
Out1a_NDAC_false(int tr)386 static void Out1a_NDAC_false(int tr)
387 {
388 ResetBus();
389 Go(WaitATN);
390 }
391
392 #define Out2_ATN_true WATN_ATN_true
393
Out2_NDAC_false(int tr)394 static void Out2_NDAC_false(int tr)
395 {
396 static uint8_t b;
397
398 parallel_emu_set_dav(0);
399 parallel_emu_set_eoi(0);
400 parallel_emu_set_bus(0xff);
401
402 par_status = parallel_trap_receivebyte(&b, 0);
403
404 if (par_status & 0xff) {
405 ResetBus();
406 Go(WaitATN);
407 } else {
408 Go(Out1);
409 }
410 }
411
412 /**************************************************************************
413 * State table
414 *
415 */
416
417 static State_t State[NSTATE] = {
418 { "WaitATN", { WATN_ATN_true, ignore, ignore, ignore,
419 ignore, ignore, ignore, ignore } },
420 { "In1", { In1_ATN_true, In1_ATN_false, In1_DAV_true, unexpected,
421 In1_NDAC_true, ignoreown, In1_NRFD_true, In1_NRFD_false } },
422 { "In2", { In2_ATN_true, In2_ATN_false, unexpected, In2_DAV_false,
423 ignoreown, In2_NDAC_false, unexpected, ignoreown } },
424 { "OldPet", { OPet_ATN_true, unexpected, unexpected, unexpected,
425 OPet_NDAC_true, unexpected, OPet_NRFD_true, unexpected } },
426 { "Out1", { Out1_ATN_true, unexpected, ignoreown, unexpected,
427 ignore, unexpected, unexpected, Out1_NRFD_false } },
428 { "Out1a", { Out1a_ATN_true, unexpected, unexpected, unexpected,
429 unexpected, Out1a_NDAC_false, Out1a_NRFD_true, unexpected } },
430 { "Out2", { Out2_ATN_true, unexpected, unexpected, ignoreown,
431 unexpected, Out2_NDAC_false, unexpected, unexpected } }
432 };
433
434 /**************************************************************************
435 * methods to set handshake lines for the devices
436 *
437 */
438
439 #ifdef DEBUG
440 #define PARALLEL_LINE_DEBUG_CLR(line, linecap) \
441 if (debug.ieee) { \
442 if (old && !parallel_ ## line) { \
443 log_debug("clr_" # line "(%02x) -> " # linecap "_false", \
444 ~mask & 0xffU); } \
445 else \
446 if (old & ~mask) { \
447 log_debug("clr_" # line "(%02x) -> %02x", \
448 ~mask & 0xffU, parallel_ ## line); } \
449 }
450
451 #define PARALLEL_LINE_DEBUG_SET(line, linecap) \
452 if (debug.ieee) { \
453 if (!old) { \
454 log_debug("set_" # line "(%02x) -> " # linecap "_true", mask); } \
455 else \
456 if (!(old & mask)) { \
457 log_debug("set_" # line "(%02x) -> %02x", \
458 mask, parallel_ ## line); } \
459 }
460 #else
461 #define PARALLEL_LINE_DEBUG_CLR(line, linecap)
462 #define PARALLEL_LINE_DEBUG_SET(line, linecap)
463 #endif
464
parallel_set_eoi(uint8_t mask)465 void parallel_set_eoi(uint8_t mask)
466 {
467 #ifdef DEBUG
468 uint8_t old = parallel_eoi;
469 #endif
470 parallel_eoi |= mask;
471
472 PARALLEL_LINE_DEBUG_SET(eoi, EOI)
473 }
474
parallel_clr_eoi(uint8_t mask)475 void parallel_clr_eoi(uint8_t mask)
476 {
477 #ifdef DEBUG
478 uint8_t old = parallel_eoi;
479 #endif
480 parallel_eoi &= mask;
481
482 PARALLEL_LINE_DEBUG_CLR(eoi, EOI)
483 }
484
parallel_atn_signal(int st)485 static void parallel_atn_signal(int st)
486 {
487 unsigned int dnr;
488
489 for (dnr = 0; dnr < NUM_DISK_UNITS; dnr++) {
490 if (diskunit_context[dnr]->enable) {
491 ieee_drive_parallel_set_atn(st, diskunit_context[dnr]);
492 }
493 }
494 }
495
parallel_set_atn(uint8_t mask)496 void parallel_set_atn(uint8_t mask)
497 {
498 uint8_t old = parallel_atn;
499 parallel_atn |= mask;
500
501 PARALLEL_LINE_DEBUG_SET(atn, ATN)
502
503 /* if ATN went active, signal to attached devices */
504 if (!old) {
505 if (parallel_emu) {
506 DoTrans(ATN_true);
507 }
508 parallel_atn_signal(1);
509 }
510 }
511
parallel_clr_atn(uint8_t mask)512 void parallel_clr_atn(uint8_t mask)
513 {
514 uint8_t old = parallel_atn;
515 parallel_atn &= mask;
516
517 PARALLEL_LINE_DEBUG_CLR(atn, ATN)
518
519 /* if ATN went inactive, signal to attached devices */
520 if (old && !parallel_atn) {
521 if (parallel_emu) {
522 DoTrans(ATN_false);
523 }
524 parallel_atn_signal(0);
525 }
526 }
527
parallel_restore_set_atn(uint8_t mask)528 void parallel_restore_set_atn(uint8_t mask)
529 {
530 #ifdef DEBUG
531 uint8_t old = parallel_atn;
532 #endif
533 parallel_atn |= mask;
534
535 #ifdef DEBUG
536 if (debug.ieee && !old) {
537 log_debug("set_atn(%02x) -> ATN_true", mask);
538 }
539 #endif
540
541 /* we do not send IRQ signals to chips on restore */
542 }
543
parallel_restore_clr_atn(uint8_t mask)544 void parallel_restore_clr_atn(uint8_t mask)
545 {
546 #ifdef DEBUG
547 uint8_t old = parallel_atn;
548 #endif
549 parallel_atn &= mask;
550
551 #ifdef DEBUG
552 if (debug.ieee && old && !parallel_atn) {
553 log_debug("clr_atn(%02x) -> ATN_false",
554 (unsigned int)(~mask & 0xff));
555 }
556 #endif
557
558 /* we do not send IRQ signals to chips on restore */
559 }
560
parallel_set_dav(uint8_t mask)561 void parallel_set_dav(uint8_t mask)
562 {
563 uint8_t old = parallel_dav;
564 parallel_dav |= mask;
565
566 PARALLEL_LINE_DEBUG_SET(dav, DAV)
567
568 if (parallel_emu && !old) {
569 DoTrans(DAV_true);
570 }
571 }
572
parallel_clr_dav(uint8_t mask)573 void parallel_clr_dav(uint8_t mask)
574 {
575 uint8_t old = parallel_dav;
576 parallel_dav &= mask;
577
578 PARALLEL_LINE_DEBUG_CLR(dav, DAV)
579
580 if (parallel_emu && old && !parallel_dav) {
581 DoTrans(DAV_false);
582 }
583 }
584
parallel_set_nrfd(uint8_t mask)585 void parallel_set_nrfd(uint8_t mask)
586 {
587 uint8_t old = parallel_nrfd;
588 parallel_nrfd |= mask;
589
590 PARALLEL_LINE_DEBUG_SET(nrfd, NRFD)
591
592 if (parallel_emu && !old) {
593 DoTrans(NRFD_true);
594 }
595 }
596
parallel_clr_nrfd(uint8_t mask)597 void parallel_clr_nrfd(uint8_t mask)
598 {
599 uint8_t old = parallel_nrfd;
600 parallel_nrfd &= mask;
601
602 PARALLEL_LINE_DEBUG_CLR(nrfd, NRFD)
603
604 if (parallel_emu && old && !parallel_nrfd) {
605 DoTrans(NRFD_false);
606 }
607 }
608
parallel_set_ndac(uint8_t mask)609 void parallel_set_ndac(uint8_t mask)
610 {
611 uint8_t old = parallel_ndac;
612 parallel_ndac |= mask;
613
614 PARALLEL_LINE_DEBUG_SET(ndac, NDAC)
615
616 if (parallel_emu && !old) {
617 DoTrans(NDAC_true);
618 }
619 }
620
parallel_clr_ndac(uint8_t mask)621 void parallel_clr_ndac(uint8_t mask)
622 {
623 uint8_t old = parallel_ndac;
624 parallel_ndac &= mask;
625
626 PARALLEL_LINE_DEBUG_CLR(ndac, NDAC)
627
628 if (parallel_emu && old && !parallel_ndac) {
629 DoTrans(NDAC_false);
630 }
631 }
632
633 /**************************************************************************
634 * methods to set data lines
635 */
636
637 #ifdef DEBUG
638 #define PARALLEL_DEBUG_SET_BUS(type) \
639 if (debug.ieee) { \
640 log_debug(# type "_set_bus(%02x) -> %02x (%02x)", \
641 (unsigned int)b, parallel_bus, ~parallel_bus & 0xffu); \
642 }
643 #else
644 #define PARALLEL_DEBUG_SET_BUS(type)
645 #endif
646
647 static uint8_t par_emu_bus = 0xff;
648 static uint8_t par_cpu_bus = 0xff;
649 static uint8_t par_drv_bus[NUM_DISK_UNITS] = { 0xff, 0xff, 0xff, 0xff };
650
parallel_emu_set_bus(uint8_t b)651 void parallel_emu_set_bus(uint8_t b)
652 {
653 par_emu_bus = b;
654 parallel_bus = par_emu_bus & par_cpu_bus &
655 par_drv_bus[0] & par_drv_bus[1] &
656 par_drv_bus[2] & par_drv_bus[3];
657
658 PARALLEL_DEBUG_SET_BUS(emu)
659 }
660
parallel_cpu_set_bus(uint8_t b)661 void parallel_cpu_set_bus(uint8_t b)
662 {
663 par_cpu_bus = b;
664 parallel_bus = par_emu_bus & par_cpu_bus &
665 par_drv_bus[0] & par_drv_bus[1] &
666 par_drv_bus[2] & par_drv_bus[3];
667
668 PARALLEL_DEBUG_SET_BUS(cpu)
669 }
670
parallel_drv0_set_bus(uint8_t b)671 void parallel_drv0_set_bus(uint8_t b)
672 {
673 par_drv_bus[0] = b;
674 parallel_bus = par_emu_bus & par_cpu_bus &
675 par_drv_bus[0] & par_drv_bus[1] &
676 par_drv_bus[2] & par_drv_bus[3];
677
678 PARALLEL_DEBUG_SET_BUS(drv0)
679 }
680
parallel_drv1_set_bus(uint8_t b)681 void parallel_drv1_set_bus(uint8_t b)
682 {
683 par_drv_bus[1] = b;
684 parallel_bus = par_emu_bus & par_cpu_bus &
685 par_drv_bus[0] & par_drv_bus[1] &
686 par_drv_bus[2] & par_drv_bus[3];
687
688 PARALLEL_DEBUG_SET_BUS(drv1)
689 }
690
parallel_drv2_set_bus(uint8_t b)691 void parallel_drv2_set_bus(uint8_t b)
692 {
693 par_drv_bus[2] = b;
694 parallel_bus = par_emu_bus & par_cpu_bus &
695 par_drv_bus[0] & par_drv_bus[1] &
696 par_drv_bus[2] & par_drv_bus[3];
697
698 PARALLEL_DEBUG_SET_BUS(drv2)
699 }
700
parallel_drv3_set_bus(uint8_t b)701 void parallel_drv3_set_bus(uint8_t b)
702 {
703 par_drv_bus[3] = b;
704 parallel_bus = par_emu_bus & par_cpu_bus &
705 par_drv_bus[0] & par_drv_bus[1] &
706 par_drv_bus[2] & par_drv_bus[3];
707
708 PARALLEL_DEBUG_SET_BUS(drv3)
709 }
710
711 drivefunc_context_t drive_funcs[NUM_DISK_UNITS] = {
712 { parallel_drv0_set_bus,
713 parallel_drv0_set_eoi,
714 parallel_drv0_set_dav,
715 parallel_drv0_set_ndac,
716 parallel_drv0_set_nrfd, },
717 { parallel_drv1_set_bus,
718 parallel_drv1_set_eoi,
719 parallel_drv1_set_dav,
720 parallel_drv1_set_ndac,
721 parallel_drv1_set_nrfd, },
722 { parallel_drv2_set_bus,
723 parallel_drv2_set_eoi,
724 parallel_drv2_set_dav,
725 parallel_drv2_set_ndac,
726 parallel_drv2_set_nrfd, },
727 { parallel_drv3_set_bus,
728 parallel_drv3_set_eoi,
729 parallel_drv3_set_dav,
730 parallel_drv3_set_ndac,
731 parallel_drv3_set_nrfd, },
732 };
733
734 /**************************************************************************
735 *
736 */
737
738 #define PARALLEL_CPU_SET_LINE(line, dev, mask) \
739 void parallel_##dev##_set_##line(char val) \
740 { \
741 drive_cpu_execute_all(maincpu_clk); \
742 if (val) { \
743 parallel_set_##line(PARALLEL_##mask); \
744 } else { \
745 parallel_clr_##line(~PARALLEL_##mask); \
746 } \
747 }
748
749 PARALLEL_CPU_SET_LINE(atn, cpu, CPU)
750