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