1 /* nova_qty.c: NOVA multiplexor (QTY/ALM) simulator
2
3 Copyright (c) 2000-2008, Robert M. Supnik
4 Written by Bruce Ray and used with his gracious permission.
5
6 Permission is hereby granted, free of charge, to any person obtaining a
7 copy of this software and associated documentation files (the "Software"),
8 to deal in the Software without restriction, including without limitation
9 the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 and/or sell copies of the Software, and to permit persons to whom the
11 Software is furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23 Except as contained in this notice, the name of Robert M Supnik shall not be
24 used in advertising or otherwise to promote the sale, use or other dealings
25 in this Software without prior written authorization from Robert M Supnik.
26
27 qty multiplexor: QTY = 4060, ALM = 42xx
28
29 04-Jul-07 BKR fixed QTY output line number calculation (affected higher line numbers),
30 25-Mar-04 RMS Updated for V3.2
31 12-Jan-04 BKR Initial release
32 includes both original DG "quad" multiplexor (QTY)
33 and later Asynchronous Line Multiplexor (ALM) support.
34 */
35
36
37 /*----------------------------------------------------------------------*/
38 /* QTY [4060-compatible] multiplexor */
39 /*----------------------------------------------------------------------*/
40
41 /*
42 * Emulate the DG 4060 "quad" (QTY) serial port multiplexor. DG modem
43 * control is not supported in this revision due to its obtuse nature
44 * of using a separate [semi-secret] device MDM which is actually part
45 * of the DG 4026/4027 multiplexor hardware(!).
46 * (Full modem support is provided in the ALM driver.)
47 *
48 *
49 * 4060 Hardware
50 *
51 * device code: 030 [primary],
52 * 070 [secondary]
53 * interrupt mask: B14 [000002]
54 * ASM mnemonic: QTY
55 *
56 *
57 * 4060 Input/Output Word Format:
58 *
59 * _________________________________________________________________
60 * | RI| TI| channel | character |
61 * ----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
62 * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
63 *
64 *
65 * RI - receiver interrupt
66 * TI - transmitter interrupt
67 * channel - channel number, 0 - 63.
68 * character- character (valid if receiver interrupt, undefined if transmitter)
69 *
70 * Notes:
71 *
72 * Maximum 64 lines supported.
73 * DONE set whenever any received character fully assembled and ready,
74 * or when any output character transmitted and line is ready
75 * to accept next output character.
76 * BUSY set whenever output character is being sent on any line.
77 * Note that early 4060s did NOT have a busy flag!
78 * IORST clears device Done, no other user instruction does.
79 * IORST clears each line's individual R.I. and T.I.
80 *
81 *
82 * Instructions:
83 *
84 * DIA get multiplexor status word [format defined above]
85 * DOA send character to QTY line [format defined above, RI & SI ]
86 * DIB <ignored> [returns backplane bus noise]
87 * DOB clear QTY line
88 * DIC <ignored> [returns backplace bus noise]
89 * DOC <ignored>
90 * 'C' clears global done, then checks for RI and TI;
91 * 'P' <ignored>
92 * 'S' <ignored>
93 */
94
95
96 #include "nova_defs.h"
97
98 #include "sim_sock.h"
99 #include "sim_tmxr.h"
100
101
102 #define UNIT_V_8B (UNIT_V_UF + 0) /* 8b output */
103 #define UNIT_8B (1 << UNIT_V_8B)
104
105
106
107 extern int32 int_req, dev_busy, dev_done, dev_disable ;
108 extern int32 sim_switches ;
109 extern FILE * sim_log ;
110 extern int32 tmxr_poll ; /* calibrated delay */
111
112 t_stat qty_setnl ( UNIT * uptr, int32 val, char * cptr, void * desc ) ;
113
114 t_stat qty_attach ( UNIT * uptr, char * cptr ) ;
115 t_stat qty_detach ( UNIT * uptr ) ;
116 t_stat qty_reset ( DEVICE * dptr ) ;
117 t_stat qty_svc ( UNIT * uptr ) ;
118 int32 qty ( int32 pulse, int32 code, int32 AC ) ;
119
120 t_stat alm_reset ( DEVICE * dptr ) ;
121 t_stat alm_svc ( UNIT * uptr ) ;
122 int32 alm ( int32 pulse, int32 code, int32 AC ) ;
123
124 DEVICE alm_dev ;
125
126
127 #define QTY_MAX 64 /* max number of QTY lines - hardware */
128
129
130 int32 qty_brkio = SCPE_OK ; /* default I/O status code */
131 int32 qty_max = QTY_MAX ; /* max # QTY lines - user */
132 /* controllable */
133 int32 qty_mdm = 0 ; /* QTY modem control active? */
134 int32 qty_auto = 0 ; /* QTY auto disconnect active? */
135 int32 qty_polls = 0 ; /* total 'qty_svc' polls */
136
137
138 TMLN qty_ldsc[ QTY_MAX ] = { 0 } ; /* QTY line descriptors */
139 TMXR qty_desc = { QTY_MAX, 0, 0, qty_ldsc } ; /* mux descriptor */
140 int32 qty_status[ QTY_MAX ] = { 0 } ; /* QTY line status */
141 /* (must be at least 32 bits) */
142 int32 qty_tx_chr[ QTY_MAX ] = { 0 } ; /* QTY line output character */
143
144
145 /* QTY data structures
146
147 qty_dev QTY device descriptor
148 qty_unit QTY unit descriptor
149 qty_reg QTY register list
150 */
151
152 DIB qty_dib = { DEV_QTY, INT_QTY, PI_QTY, &qty } ;
153
154 UNIT qty_unit =
155 {
156 UDATA (&qty_svc, (UNIT_ATTABLE), 0)
157 } ;
158
159 REG qty_reg[] = /* ('alm_reg' should be similar to this except for device code related items) */
160 {
161 { ORDATA (BUF, qty_unit.buf, 8) },
162 { FLDATA (BUSY, dev_busy, INT_V_QTY) },
163 { FLDATA (DONE, dev_done, INT_V_QTY) },
164 { FLDATA (DISABLE, dev_disable, INT_V_QTY) },
165 { FLDATA (INT, int_req, INT_V_QTY) },
166
167 { FLDATA (MDMCTL, qty_mdm, 0) },
168 { FLDATA (AUTODS, qty_auto, 0) },
169 { DRDATA (POLLS, qty_polls, 32) },
170 { NULL }
171 } ;
172
173 MTAB qty_mod[] =
174 {
175 { UNIT_8B, 0, "7b", "7B", NULL },
176 { UNIT_8B, UNIT_8B, "8b", "8B", NULL },
177 { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT",
178 &tmxr_dscln, NULL, (void *)&qty_desc },
179 { UNIT_ATT, UNIT_ATT, "connections", NULL,
180 NULL, &tmxr_show_summ, (void *)&qty_desc },
181 { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL,
182 NULL, &tmxr_show_cstat, (void *)&qty_desc },
183 { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL,
184 NULL, &tmxr_show_cstat, (void *)&qty_desc },
185 { MTAB_XTD | MTAB_VDV, 0, "LINES", "LINES",
186 &qty_setnl, &tmxr_show_lines, (void *) &qty_desc },
187 { 0 }
188 } ;
189
190 DEVICE qty_dev =
191 {
192 "QTY", &qty_unit, qty_reg, qty_mod,
193 1, 10, 31, 1, 8, 8,
194 NULL, NULL, &qty_reset,
195 NULL, &qty_attach, &qty_detach,
196 &qty_dib, (DEV_DISABLE | DEV_DIS | DEV_NET)
197 };
198
199 #define DG_RETURN( status, data ) (int32)(((status) << IOT_V_REASON) | ((data) & 0x0FFFF) )
200
201 /*
202 * QTY_S_xxx QTY device status reference
203 * QTY_L_xxx QTY line status word reference (qty_status[])
204 */
205
206 /*----------------------------------------------*/
207 /* QTY device status */
208 /*----------------------------------------------*/
209
210 #define QTY_S_RI 0x8000 /* Receiver Interrupt */
211 #define QTY_S_TI 0x4000 /* Transmitter interrupt */
212 #define QTY_S_LMASK 0x3F00 /* line mask */
213 #define QTY_S_DMASK 0x00FF /* data mask (received char) */
214
215
216
217 #define QTY_MASTER_ACTIVE( desc ) ( (desc)->master )
218
219 #define QTY_LINE_EXTRACT( x ) (((x) & QTY_S_LMASK) >> 8)
220
221 #define QTY_LINE_TX_CHAR( line ) qty_tx_chr[ ((line) % QTY_MAX) ]
222 #define QTY_LINE_RX_CHAR( line ) (qty_status[ (line) ] & QTY_S_DMASK)
223 #define QTY_UNIT_ACTIVE( unitp ) ( (unitp)->conn )
224
225 #define QTY_LINE_BITS( line, bits ) qty_status[ (line) ] & bits
226
227 #define QTY_LINE_SET_BIT( line, bit ) qty_status[ (line) ] |= (bit) ;
228 #define QTY_LINE_CLEAR_BIT( line, bit ) qty_status[ (line) ] &= ~(bit) ;
229 #define QTY_LINE_BIT_SET( line, bit ) (qty_status[ (line) ] & (bit))
230
231
232 /*----------------------------------------------*/
233 /* QTY line status */
234 /*----------------------------------------------*/
235
236 #define QTY_L_RXE 0x800000 /* receiver enabled? */
237 #define QTY_L_RXBZ 0x400000 /* receiver busy? */
238 #define QTY_L_RXDN 0x200000 /* receiver done? */
239 #define QTY_L_TXE 0x080000 /* transmitter enabled? */
240 #define QTY_L_TXBZ 0x040000 /* transmitter busy? */
241 #define QTY_L_TXDN 0x020000 /* transmitter done? */
242
243 #define QTY_L_BREAK 0x008000 /* BREAK character received */
244 #define QTY_L_RING 0x004000 /* Ring interrupt */
245 #define QTY_L_CD 0x002000 /* Carrier Detect */
246 #define QTY_L_DTR 0x001000 /* Data Terminal Ready */
247 /* <0x00FF = character> */
248
249 #define QTY_L_LOOPBK 0x00010000 /* loopback mode */
250 #define QTY_L_OVRERR 0x00020000 /* overrun error */
251 #define QTY_L_FRMERR 0x00040000 /* framing error */
252 #define QTY_L_PARERR 0x00080000 /* parity error */
253
254
255 /* CD, CTS, DSR, RI */
256 /* <future> */
257
258 #define QTY_L_MODEM 0x0080 /* <not yet used> */
259 #define QTY_L_TELNET 0x0040 /* <not yet used> */
260 #define QTY_L_AUTODIS 0x0020 /* <not yet used> */
261 #define QTY_L_PARITY
262 #define QTY_L_7BIT
263 #define QTY_L_BAUD /* <4 bits> */
264
265
266 #define QTY_L_DMASK 0x000FF /* data mask (always 8 bits) */
267
268 /* Note: use at least an 'int32' for this guy */
269
270 /*------------------------------*/
271 /* qty_tmxr_putc */
272 /*------------------------------*/
273
qty_tmxr_putc(int line,TMLN * lp,int kar)274 int qty_tmxr_putc( int line, TMLN * lp, int kar )
275 {
276 int a ;
277
278 /*----------------------------------------------*/
279 /* Send character to given QTY/telnet line. */
280 /* */
281 /* enter: line QTY line # */
282 /* lp Telnet unit def ptr */
283 /* kar character to send */
284 /* */
285 /* return: SCPE_OK */
286 /* SCPE_STALL */
287 /* SCPE_LOST */
288 /*----------------------------------------------*/
289
290 a = tmxr_putc_ln( lp, kar ) ;
291 if ( a == SCPE_OK)
292 {
293 QTY_LINE_SET_BIT( line, QTY_L_TXDN )
294 QTY_LINE_CLEAR_BIT( line, QTY_L_TXBZ )
295 }
296 else if ( a == SCPE_STALL )
297 {
298 /*
299 (should we try to output the buffer
300 and then regroup...?)
301 */
302 QTY_LINE_SET_BIT( line, QTY_L_TXBZ )
303 QTY_LINE_CLEAR_BIT( line, QTY_L_TXDN )
304 QTY_LINE_TX_CHAR( line ) = kar ;
305 }
306 else if ( a == SCPE_LOST )
307 {
308 /* no connection - hangup? */
309 QTY_LINE_SET_BIT( line, QTY_L_TXBZ )
310 QTY_LINE_CLEAR_BIT( line, QTY_L_TXDN )
311 QTY_LINE_TX_CHAR( line ) = kar ;
312 }
313 return ( a ) ;
314 } /* end of 'qty_tmxr_putc' */
315
316
317 /*----------------------------------------------*/
318 /* qty_update_rcvi */
319 /*----------------------------------------------*/
320
qty_update_rcvi(TMXR * mp)321 int qty_update_rcvi( TMXR * mp )
322 {
323 int line ;
324 TMLN * lp ;
325 int32 datum ;
326 int changes ;
327
328 /*------------------------------------------------------*/
329 /* Search through connected telnet lines for any input */
330 /* activity. */
331 /* */
332 /* enter: mp master telnet qty desc ptr */
333 /* */
334 /* return: int change count (0 = none seen) */
335 /*------------------------------------------------------*/
336
337 for ( changes = line = 0; line < mp->lines; ++line )
338 if ( (lp=mp->ldsc+line)->conn && lp->rcve )
339 if ( (datum=tmxr_getc_ln(lp)) )
340 {
341 if ( datum & SCPE_BREAK )
342 {
343 /* what should we do here - set QTY_L_BREAK? */
344 datum = datum & 0x00FF ;
345 }
346 else
347 {
348 datum = datum & 0x00FF ;
349 }
350 /* <check parity, masking, forced parity, CR/LF xlation> */
351
352 QTY_LINE_CLEAR_BIT( line, (QTY_L_RXBZ | QTY_L_DMASK) ) ;
353 QTY_LINE_SET_BIT( line, (QTY_L_RXDN | datum) ) ;
354 ++changes ;
355 }
356 return ( changes ) ;
357 } /* end of 'qty_update_rcvi' */
358
359
360 /*----------------------------------------------*/
361 /* qty_update_xmti */
362 /*----------------------------------------------*/
363
qty_update_xmti(TMXR * mp)364 int qty_update_xmti( TMXR * mp )
365 {
366 int line ;
367 TMLN * lp ;
368 int changes ;
369
370 /*------------------------------------------------------*/
371 /* Search through connected telnet lines for any de- */
372 /* ferred output activity. */
373 /* */
374 /* enter: mp master telnet qty desc ptr */
375 /* */
376 /* return: int change count (0 = none seen) */
377 /*------------------------------------------------------*/
378
379 /* any TX DONE flags set
380 * any TX BUSY flags set
381 */
382
383 for ( changes = line = 0; line < mp->lines; ++line )
384 if ( QTY_LINE_BIT_SET(line,QTY_L_TXBZ) )
385 if ( (lp=mp->ldsc+line)->conn && lp->xmte )
386 {
387 /* why are we busy? buffer was full? */
388 /* now some space available - try
389 * to stuff pending character in
390 * buffer and free up the world
391 */
392 qty_tmxr_putc( line, lp, QTY_LINE_TX_CHAR(line) ) ;
393 ++changes ;
394 }
395 return ( changes ) ;
396 } /* end of 'qty_update_xmti' */
397
398
399 /*----------------------------------------------*/
400 /* qty_update_status */
401 /*----------------------------------------------*/
402
qty_update_status(DIB * dibp,TMXR * tmxr_desc)403 int qty_update_status( DIB * dibp, TMXR * tmxr_desc )
404 {
405 int line ;
406 int status ;
407 int txbusy ;
408
409 /*----------------------------------------------*/
410 /* return global device status for current qty */
411 /* state. */
412 /* */
413 /* Receiver interrupts have higher priority */
414 /* than transmitter interrupts according to DG */
415 /* but this routine could be modified to use */
416 /* different priority criteria. */
417 /* */
418 /* Round-robin polling could also be used in */
419 /* some future release rather than starting */
420 /* with line 0 each time. */
421 /* */
422 /* Return <QTY_S_RI + line # + character> of */
423 /* first waiting character, else return */
424 /* <QTY_S_TI + line #> of first finished line */
425 /* output, else return 0. */
426 /* */
427 /* This routine does -not- clear input line */
428 /* BZ/DN flags; caller should do this. */
429 /* */
430 /* Global device done and busy flags are */
431 /* updated. */
432 /*----------------------------------------------*/
433
434 for ( txbusy = status = line = 0 ; line < qty_max ; ++line )
435 {
436 txbusy |= (QTY_LINE_BIT_SET(line,QTY_L_TXBZ)) ;
437 if ( QTY_LINE_BIT_SET(line,QTY_L_RXDN) )
438 {
439 if ( ! status )
440 {
441 status = QTY_LINE_BITS( line, QTY_S_DMASK ) | QTY_S_RI ;
442 status = status | (line << 8) ;
443 }
444 break ;
445 }
446 else if ( QTY_LINE_BIT_SET(line,QTY_L_TXDN) )
447 {
448 if ( ! (status & QTY_S_RI) )
449 if ( ! (status & QTY_S_RI) )
450 {
451 status = QTY_S_TI ;
452 status = status | (line << 8) ;
453 }
454 }
455 }
456 /* <we could check each line for TX busy to set DEV_SET_BUSY)?> */
457 DEV_CLR_BUSY( INT_QTY ) ;
458 DEV_CLR_DONE( INT_QTY ) ;
459 if ( txbusy )
460 {
461 DEV_SET_BUSY( INT_QTY ) ;
462 }
463 if ( status & (QTY_S_RI | QTY_S_TI) )
464 {
465 DEV_SET_DONE( INT_QTY ) ;
466 }
467 DEV_UPDATE_INTR ; /* update final intr status */
468 return ( status ) ;
469 } /* end of 'qty_update_status' */
470
471
472 /*--------------------------------------------------------------*/
473 /* qty_attach */
474 /*--------------------------------------------------------------*/
475
qty_attach(UNIT * unitp,char * cptr)476 t_stat qty_attach( UNIT * unitp, char * cptr )
477 {
478 t_stat r ;
479 int a ;
480
481 /* switches: A auto-disconnect
482 * M modem control
483 */
484
485 qty_mdm = qty_auto = 0; /* modem ctl off */
486 r = tmxr_attach( &qty_desc, unitp, cptr ) ; /* attach QTY */
487 if ( r != SCPE_OK )
488 {
489 return ( r ) ; /* error! */
490 }
491 if ( sim_switches & SWMASK('M') ) /* modem control? */
492 {
493 qty_mdm = 1;
494 printf( "Modem control activated\n" ) ;
495 if ( sim_log ) fprintf( sim_log, "Modem control activated\n" ) ;
496 if ( sim_switches & SWMASK ('A') ) /* autodisconnect? */
497 {
498 qty_auto = 1 ;
499 printf( "Auto disconnect activated\n" ) ;
500 if ( sim_log ) fprintf( sim_log, "Auto disconnect activated\n" ) ;
501 }
502 }
503 qty_polls = 0 ;
504 for ( a = 0 ; a < QTY_MAX ; ++a )
505 {
506 /* QTY lines are always enabled - force RX and TX to 'enabled' */
507 qty_status[ a ] = (QTY_L_RXE | QTY_L_TXE) ;
508 }
509 sim_activate( unitp, tmxr_poll ) ;
510 return ( SCPE_OK ) ;
511 } /* end of 'qty_attach' */
512
513
514 /*--------------------------------------------------------------*/
515 /* qty_detach */
516 /*--------------------------------------------------------------*/
517
qty_detach(UNIT * unitp)518 t_stat qty_detach( UNIT * unitp )
519 {
520 sim_cancel( unitp ) ;
521 return ( tmxr_detach(&qty_desc,unitp) ) ;
522 } /* end of 'qty_detach' */
523
524
525 /*--------------------------------------------------------------*/
526 /* qty_clear */
527 /*--------------------------------------------------------------*/
528
qty_clear(t_bool flag)529 t_stat qty_clear( t_bool flag )
530 {
531 int line ;
532
533 for ( line = 0 ; line < qty_max ; ++line )
534 {
535 qty_ldsc[line].xmte = 0 ;
536 qty_ldsc[line].rcve = 0 ;
537 if ( ! qty_ldsc[line].conn )
538 {
539 qty_ldsc[line].xmte = 1 ; /* set xmt enb */
540 qty_ldsc[line].rcve = 1 ; /* clr rcv enb */
541 }
542 }
543 return ( SCPE_OK ) ;
544 } /* end of 'qty_clear' */
545
546
547 /*----------------------------------------------*/
548 /* qty_common_reset */
549 /*----------------------------------------------*/
550
qty_common_reset(DIB * dibp,UNIT * unitp,DEVICE * dptr)551 t_stat qty_common_reset( DIB * dibp, UNIT * unitp, DEVICE * dptr )
552 {
553 if ((dptr->flags & DEV_DIS) == 0)
554 {
555 if (dptr == &qty_dev) alm_dev.flags |= DEV_DIS;
556 else qty_dev.flags |= DEV_DIS;
557 }
558 qty_clear( TRUE ) ;
559 DEV_CLR_BUSY( INT_QTY ) ; /* clear busy */
560 DEV_CLR_DONE( INT_QTY ) ; /* clear done, int */
561 DEV_UPDATE_INTR ;
562 if ( QTY_MASTER_ACTIVE(&qty_desc) )
563 {
564 sim_activate( unitp, tmxr_poll ) ;
565 }
566 else
567 {
568 sim_cancel( unitp ) ;
569 }
570 return ( SCPE_OK ) ;
571 } /* end of 'qty_common_reset' */
572
573
574 /*--------------------------------------------------------------*/
575 /* qty_reset */
576 /*--------------------------------------------------------------*/
577
qty_reset(DEVICE * dptr)578 t_stat qty_reset( DEVICE * dptr )
579 {
580 return ( qty_common_reset(&qty_dib,&qty_unit,dptr) ) ;
581 } /* end of 'qty_reset' */
582
583
584 /* Unit service routine
585
586 The QTY/ALM polls to see if asynchronous activity has occurred and now
587 needs to be processed. The polling interval is controlled by the clock
588 simulator, so for most environments, it is calibrated to real time.
589
590 The simulator assumes that software enables all of the multiplexors,
591 or none of them.
592 */
593
594 /*----------------------------------------------*/
595 /* qty_common_svc */
596 /*----------------------------------------------*/
597
qty_common_svc(DIB * dibp,UNIT * unitp)598 t_stat qty_common_svc( DIB * dibp, UNIT * unitp )
599 {
600 int line ;
601 int newln ;
602 TMLN * tmlnp ;
603
604 ++qty_polls ; /* another time 'round the track */
605 newln = tmxr_poll_conn( &qty_desc ) ; /* anybody knocking at the door? */
606 if ( (newln >= 0) && qty_mdm ) {
607 if ( newln >= qty_max )
608 {
609 return SCPE_IERR; /* WTF - sanity check failed, over? */
610 }
611 }
612 else
613 {
614 line = newln ; /* handle modem control */
615 tmlnp =&qty_ldsc[ line ] ;
616 tmlnp->rcve = tmlnp->xmte = 1 ;
617 /* do QTY_LINE_ bit fiddling and state machine
618 * manipulation with modem control signals
619 */
620 }
621
622 tmxr_poll_rx( &qty_desc ) ; /* poll input */
623 qty_update_rcvi( &qty_desc ) ; /* update receiver interrupt status */
624
625 tmxr_poll_tx( &qty_desc ) ; /* poll output */
626 qty_update_xmti( &qty_desc ) ; /* update transmitter interrupt status */
627
628 qty_update_status( dibp, &qty_desc ) ; /* update device status */
629
630 sim_activate( unitp, tmxr_poll ) ; /* restart the bubble machine */
631 return ( SCPE_OK ) ;
632 } /* end of 'qty_common_svc' */
633
634
635 /*--------------------------------------------------------------*/
636 /* qty_svc */
637 /*--------------------------------------------------------------*/
638
qty_svc(UNIT * uptr)639 t_stat qty_svc( UNIT * uptr )
640 {
641 return ( qty_common_svc(&qty_dib,uptr) ) ;
642 } /* end of 'qty_svc' */
643
644
645 /*--------------------------------------------------------------*/
646 /* qty */
647 /*--------------------------------------------------------------*/
648
qty(int32 pulse,int32 code,int32 AC)649 int32 qty( int32 pulse, int32 code, int32 AC )
650 {
651 int32 iodata ;
652 int32 ioresult ;
653 int line ;
654 TMLN * tmlnp ;
655 int a ;
656 int kar ;
657
658 /*--------------------------------------------------------------*/
659 /* DG 4060[-compatible] "quad" multiplexor instruction handler */
660 /*--------------------------------------------------------------*/
661
662 ioresult= qty_brkio ; /* (assume returning I/O break value */
663 iodata = 0 ; /* (assume 16-bit Nova/Eclipse bus) */
664 switch ( code )
665 {
666 case ioNIO : /* <no operation> */
667 break ;
668
669 case ioDIA : /* get current QTY status */
670 iodata = qty_update_status( &qty_dib, &qty_desc ) ;
671 if ( iodata & QTY_S_RI )
672 { /* clear line's input buffer */
673 QTY_LINE_CLEAR_BIT( (QTY_LINE_EXTRACT(iodata)), (QTY_L_RXBZ | QTY_L_RXDN) )
674 /*
675 character masking ;
676 parity checking ;
677 parity generating ;
678 */
679 }
680 qty_update_status( &qty_dib, &qty_desc ) ;
681 break ;
682
683 case ioDOA : /* send character to QTY */
684 line = QTY_LINE_EXTRACT( AC ) ;
685 if ( line < qty_max )
686 if ( QTY_LINE_BIT_SET(line,QTY_L_TXE) )
687 {
688 /*
689 perform any character translation:
690 7 bit/ 8 bit
691 parity generation
692 */
693 kar = AC & ((qty_unit.flags & UNIT_8B)? 0377: 0177) ;
694 /* do any parity calculations also */
695
696 tmlnp = &qty_ldsc[ line ] ;
697 a = qty_tmxr_putc( line, tmlnp, kar ) ;
698 if ( a != SCPE_OK)
699 {
700 /* do anything at this point? */
701 }
702 qty_update_status( &qty_dib, &qty_desc ) ;
703 }
704 break ;
705
706 case ioDIB : /* no QTY function - return bus noise in AC */
707 break ;
708
709 case ioDOB : /* clear QTY output channel busy and done flag */
710 QTY_LINE_CLEAR_BIT( (QTY_LINE_EXTRACT(AC)), (QTY_L_TXBZ | QTY_L_TXDN) )
711 qty_update_status( &qty_dib, &qty_desc ) ;
712 break ;
713
714 case ioDIC : /* no QTY function - return bus noise in AC */
715 break ;
716
717 case ioDOC : /* no QTY function - ignore */
718 break ;
719
720 case ioSKP : /* I/O skip test - should never come here */
721 break ;
722
723 default :
724 /* <illegal I/O operation value> */
725 break ;
726 }
727
728 switch ( pulse )
729 {
730 case iopN : /* <ignored (of course)> */
731 break ;
732
733 case iopS : /* <ignored> */
734 break ;
735
736 case iopP : /* <ignored> */
737 break ;
738
739 case iopC :
740 qty_update_status( &qty_dib, &qty_desc ) ;
741 break ;
742
743 default :
744 /* <illegal pulse value> */
745 break ;
746 }
747
748 return ( DG_RETURN( ioresult, iodata ) ) ;
749 } /* end of 'qty' */
750
751 /*--------------------------------------------------------------*/
752 /* qty_setnl */
753 /*--------------------------------------------------------------*/
754
qty_setnl(UNIT * uptr,int32 val,char * cptr,void * desc)755 t_stat qty_setnl( UNIT * uptr, int32 val, char * cptr, void * desc )
756 {
757 int32 newln, i, t ;
758
759 t_stat r ;
760 if ( cptr == NULL )
761 {
762 return ( SCPE_ARG ) ;
763 }
764 newln = (int32) get_uint( cptr, 10, QTY_MAX, &r ) ;
765 if ( (r != SCPE_OK) || (newln == qty_desc.lines) )
766 {
767 return ( r ) ;
768 }
769 if ( (newln == 0) || (newln > QTY_MAX) )
770 {
771 return ( SCPE_ARG ) ;
772 }
773 if ( newln < qty_desc.lines )
774 {
775 for ( i = newln, t = 0 ; i < qty_desc.lines ; ++i )
776 {
777 t = t | qty_ldsc[i].conn ;
778 }
779 if ( t && ! get_yn("This will disconnect users; proceed [N]?", FALSE) )
780 {
781 return ( SCPE_OK ) ;
782 }
783 for ( i = newln ; i < qty_desc.lines ; ++i )
784 {
785 if ( qty_ldsc[i].conn )
786 { /* reset line */
787 tmxr_msg( qty_ldsc[i].conn, "\r\nOperator disconnected line\r\n" ) ;
788 tmxr_reset_ln( &qty_ldsc[i] ) ;
789 }
790 qty_clear( TRUE ) ; /* reset mux */
791 }
792 }
793 qty_max = qty_desc.lines = newln ;
794 /* Huh, I don't understand this yet...
795 qty_max = ((qty_dev.flags & DEV_DIS)? 0 : (qty_desc.lines / QTY_MAX)) ;
796 */
797 return ( SCPE_OK ) ;
798 } /* end of 'qty_setnl' */
799
800
801 /*----------------------------------------------------------------------*/
802 /* ALM [425x-compatible] multiplexor */
803 /*----------------------------------------------------------------------*/
804
805 /*
806 * device code: 034 [primary],
807 * 074 [secondary]
808 * interrupt mask: B14 [000002]
809 * ASM mnemonic: ALM
810 *
811 * ALM [4255-4258] I/O instructions
812 *
813 * DIA read line and section requesting service
814 * DOA select line and section (lines 0-255, 8-bits) + rcvr/xmit
815 * DIB receive data
816 * DOB 00 transmit data
817 * 01 transmit BREAK
818 * 10 set modem control status
819 * 11 <ignored>
820 * DIC read receiver or modem status
821 * DOC 00 control line section and diag mode
822 * 01
823 * 10 specify line characteristics
824 * 11
825 *
826 * undocumented DG "features":
827 *
828 * NIOS sets board offline
829 * NIOC sets board online
830 * Modem control signal state change can signal interrupt
831 * explicit line select with DOA
832 * implicit line select with DIA
833 *
834 * We support 64 lines maximum in this release although some ALM's could
835 * theoretically support up to 256.
836 */
837
838
839 DIB alm_dib = { DEV_ALM, INT_ALM, PI_ALM, &alm } ;
840 UNIT alm_unit =
841 {
842 UDATA (&alm_svc, (UNIT_ATTABLE), 0)
843 } ;
844
845 REG alm_reg[] = /* ('qty_reg' should be similar to this except for device code related items) */
846 {
847 { ORDATA (BUF, alm_unit.buf, 8) },
848 { FLDATA (BUSY, dev_busy, INT_V_ALM) },
849 { FLDATA (DONE, dev_done, INT_V_ALM) },
850 { FLDATA (DISABLE, dev_disable, INT_V_ALM) },
851 { FLDATA (INT, int_req, INT_V_ALM) },
852
853 { FLDATA (MDMCTL, qty_mdm, 0) },
854 { FLDATA (AUTODS, qty_auto, 0) },
855 { DRDATA (POLLS, qty_polls, 32) },
856 { NULL }
857 } ;
858
859 DEVICE alm_dev =
860 {
861 "ALM", &alm_unit, alm_reg, qty_mod,
862 1, 10, 31, 1, 8, 8,
863 NULL, NULL, &alm_reset,
864 NULL, &qty_attach, &qty_detach,
865 &alm_dib, (DEV_DISABLE | DEV_NET)
866 } ;
867
868 int alm_section = -1 ; /* current line "section" (0 = RCV, 1 = XMT) */
869 int alm_line = -1 ; /* current line [0-63] */
870 int alm_diag_mode = 0 ; /* <not yet supported> */
871 int alm_line_mask = 0x003F ; /* maximum of 64 lines in this rev */
872
873
874 #define ALM_LINE_EXTRACT( x ) (((x) >> 1) & alm_line_mask)
875 #define ALM_SECT_EXTRACT( x ) ((x) & 0x0001)
876
877
878 /*--------------------------------------------------------------*/
879 /* alm_reset */
880 /*--------------------------------------------------------------*/
881
alm_reset(DEVICE * dptr)882 t_stat alm_reset( DEVICE * dptr )
883 {
884 return ( qty_common_reset(&alm_dib,&alm_unit,dptr) ) ;
885 } /* end of 'alm_reset' */
886
887
888 /*--------------------------------------------------------------*/
889 /* alm_svc */
890 /*--------------------------------------------------------------*/
891
alm_svc(UNIT * uptr)892 t_stat alm_svc( UNIT * uptr )
893 {
894 return ( qty_common_svc(&alm_dib,uptr) ) ;
895 } /* end of 'alm_svc' */
896
897
898 /*--------------------------------------------------------------*/
899 /* alm */
900 /*--------------------------------------------------------------*/
901
alm(int32 pulse,int32 code,int32 AC)902 int32 alm( int32 pulse, int32 code, int32 AC )
903 {
904 int32 iodata ;
905 int32 ioresult ;
906 TMLN * tmlnp ;
907 int a ;
908 int kar ;
909
910 /*--------------------------------------------------------------*/
911 /* DG 425x[-compatible] "ALM" multiplexor instruction handler */
912 /*--------------------------------------------------------------*/
913
914 ioresult= qty_brkio ; /* (assume returning I/O break value */
915 iodata = 0 ; /* (assume 16-bit Nova/Eclipse bus) */
916 switch ( code )
917 {
918 case ioNIO : /* <no operation> */
919 break ;
920
921 case ioDIA : /* read line and section requesting service */
922 iodata = qty_update_status( &alm_dib, &qty_desc ) ;
923 alm_line = (QTY_LINE_EXTRACT(iodata) & alm_line_mask) ;
924 /* (mask with 'alm_line_mask' in case ALM mask is different than QTY */
925 alm_section = 0 ;
926 if ( ! ( iodata & QTY_S_RI) )
927 if ( iodata & QTY_S_TI )
928 {
929 alm_section = 1 ; /* receiver quiet - transmitter done */
930 }
931 iodata = (alm_line << 1) | alm_section ;
932 break ;
933
934 case ioDOA : /* set line and section */
935 alm_section = ALM_SECT_EXTRACT( AC ) ;
936 alm_line = ALM_LINE_EXTRACT( AC ) ;
937 break ;
938
939 case ioDIB : /* no ALM function - return bus noise in AC */
940 if ( alm_line < qty_max )
941 {
942 iodata = QTY_LINE_RX_CHAR( alm_line ) ;
943 }
944 break ;
945
946 case ioDOB : /* output and modem control functions */
947 switch ( (AC >> 14) & 03 )
948 {
949 case 00 : /* transmit data */
950 if ( alm_line < qty_max )
951 if ( QTY_LINE_BIT_SET(alm_line,QTY_L_TXE) )
952 {
953 /*
954 perform any character translation:
955 7 bit/ 8 bit
956 parity generation
957 */
958 kar = AC & ((alm_unit.flags & UNIT_8B)? 0377: 0177) ;
959 /* do any parity calculations also */
960
961 tmlnp = &qty_ldsc[ alm_line ] ;
962 a = qty_tmxr_putc( alm_line, tmlnp, kar ) ;
963 if ( a != SCPE_OK)
964 {
965 /* do anything at this point? */
966 }
967 qty_update_status( &alm_dib, &qty_desc ) ;
968 }
969 break ;
970
971 case 01 : /* transmit break */
972 if ( alm_line < qty_max )
973 if ( QTY_LINE_BIT_SET(alm_line,QTY_L_TXE) )
974 {
975 tmlnp = &qty_ldsc[ alm_line ] ;
976 /*
977 a = qty_tmxr_putc( alm_line, tmlnp, kar ) ;
978 if ( a != SCPE_OK)
979 {
980 }
981 */
982 qty_update_status( &alm_dib, &qty_desc ) ;
983 }
984 break ;
985
986 case 02 : /* set modem control status */
987 break ;
988
989 case 03 : /* unused */
990 break ;
991 }
992 break ;
993
994 case ioDIC : /* get modem or receiver status */
995 if ( alm_line < qty_max ) {
996 if ( alm_section )
997 {
998 /* get modem section status */
999 if ( qty_ldsc[ alm_line ].xmte )
1000 {
1001 iodata = 0035 ; /* set CD, CTS, DSR, MDM flags */
1002 }
1003 }
1004 }
1005 else
1006 {
1007 /* get receiver section status */
1008 iodata = 0 ; /* receiver error status - no errors by default */
1009 }
1010 break ;
1011
1012 case ioDOC : /* set line attributes */
1013 switch ( (AC >> 14) & 03 )
1014 {
1015 case 00 : /* control line section */
1016 break ;
1017
1018 case 01 : /* unused */
1019 break ;
1020
1021 case 02 : /* set line characteristics */
1022 break ;
1023
1024 case 03 : /* unused */
1025 break ;
1026 }
1027 break ;
1028
1029 case ioSKP : /* I/O skip test - should never come here */
1030 break ;
1031
1032 default :
1033 /* <illegal I/O operation value> */
1034 break ;
1035 }
1036
1037 switch ( pulse )
1038 {
1039 case iopN : /* <ignored (of course)> */
1040 break ;
1041
1042 case iopS : /* set device busy
1043 * set all lines on board offline
1044 * clear each line's done
1045 * clear internal system
1046 * clear device busy
1047 */
1048 for ( a = 0 ; a < qty_max ; ++a )
1049 if ( 1 /* (not yet optimized) */ )
1050 {
1051 QTY_LINE_CLEAR_BIT( a, (QTY_L_RXBZ | QTY_L_RXDN | QTY_L_TXBZ | QTY_L_TXDN) ) ;
1052 }
1053 qty_update_status( &alm_dib, &qty_desc ) ;
1054 break ;
1055
1056 case iopP : /* stop clock for all boards in off-line mode */
1057 break ;
1058
1059 case iopC :
1060 for ( a = 0 ; a < qty_max ; ++a )
1061 if ( 1 /* (not yet optimized) */ )
1062 {
1063 QTY_LINE_CLEAR_BIT( a, (QTY_L_RXBZ | QTY_L_RXDN | QTY_L_TXBZ | QTY_L_TXDN) ) ;
1064 }
1065 qty_update_status( &alm_dib, &qty_desc ) ;
1066 break ;
1067
1068 default :
1069 /* <illegal pulse value> */
1070 break ;
1071 }
1072
1073 return ( DG_RETURN( ioresult, iodata ) ) ;
1074 } /* end of 'alm' */
1075