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