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