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