| /* nova_qty.c: NOVA multiplexor (QTY/ALM) simulator | |
| Copyright (c) 2000-2008, Robert M. Supnik | |
| Written by Bruce Ray and used with his gracious permission. | |
| Permission is hereby granted, free of charge, to any person obtaining a | |
| copy of this software and associated documentation files (the "Software"), | |
| to deal in the Software without restriction, including without limitation | |
| the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| and/or sell copies of the Software, and to permit persons to whom the | |
| Software is furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in | |
| all copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| Except as contained in this notice, the name of Robert M Supnik shall not be | |
| used in advertising or otherwise to promote the sale, use or other dealings | |
| in this Software without prior written authorization from Robert M Supnik. | |
| qty multiplexor: QTY = 4060, ALM = 42xx | |
| 04-Jul-07 BKR fixed QTY output line number calculation (affected higher line numbers), | |
| 25-Mar-04 RMS Updated for V3.2 | |
| 12-Jan-04 BKR Initial release | |
| includes both original DG "quad" multiplexor (QTY) | |
| and later Asynchronous Line Multiplexor (ALM) support. | |
| */ | |
| /*----------------------------------------------------------------------*/ | |
| /* QTY [4060-compatible] multiplexor */ | |
| /*----------------------------------------------------------------------*/ | |
| /* | |
| * Emulate the DG 4060 "quad" (QTY) serial port multiplexor. DG modem | |
| * control is not supported in this revision due to its obtuse nature | |
| * of using a separate [semi-secret] device MDM which is actually part | |
| * of the DG 4026/4027 multiplexor hardware(!). | |
| * (Full modem support is provided in the ALM driver.) | |
| * | |
| * | |
| * 4060 Hardware | |
| * | |
| * device code: 030 [primary], | |
| * 070 [secondary] | |
| * interrupt mask: B14 [000002] | |
| * ASM mnemonic: QTY | |
| * | |
| * | |
| * 4060 Input/Output Word Format: | |
| * | |
| * _________________________________________________________________ | |
| * | RI| TI| channel | character | | |
| * ----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
| * | |
| * | |
| * RI - receiver interrupt | |
| * TI - transmitter interrupt | |
| * channel - channel number, 0 - 63. | |
| * character- character (valid if receiver interrupt, undefined if transmitter) | |
| * | |
| * Notes: | |
| * | |
| * Maximum 64 lines supported. | |
| * DONE set whenever any received character fully assembled and ready, | |
| * or when any output character transmitted and line is ready | |
| * to accept next output character. | |
| * BUSY set whenever output character is being sent on any line. | |
| * Note that early 4060s did NOT have a busy flag! | |
| * IORST clears device Done, no other user instruction does. | |
| * IORST clears each line's individual R.I. and T.I. | |
| * | |
| * | |
| * Instructions: | |
| * | |
| * DIA get multiplexor status word [format defined above] | |
| * DOA send character to QTY line [format defined above, RI & SI ] | |
| * DIB <ignored> [returns backplane bus noise] | |
| * DOB clear QTY line | |
| * DIC <ignored> [returns backplace bus noise] | |
| * DOC <ignored> | |
| * 'C' clears global done, then checks for RI and TI; | |
| * 'P' <ignored> | |
| * 'S' <ignored> | |
| */ | |
| #include "nova_defs.h" | |
| #include "sim_sock.h" | |
| #include "sim_tmxr.h" | |
| #define UNIT_V_8B (UNIT_V_UF + 0) /* 8b output */ | |
| #define UNIT_8B (1 << UNIT_V_8B) | |
| extern int32 int_req, dev_busy, dev_done, dev_disable ; | |
| extern int32 tmxr_poll ; /* calibrated delay */ | |
| t_stat qty_setnl ( UNIT * uptr, int32 val, char * cptr, void * desc ) ; | |
| t_stat qty_attach ( UNIT * uptr, char * cptr ) ; | |
| t_stat qty_detach ( UNIT * uptr ) ; | |
| t_stat qty_reset ( DEVICE * dptr ) ; | |
| t_stat qty_svc ( UNIT * uptr ) ; | |
| int32 qty ( int32 pulse, int32 code, int32 AC ) ; | |
| t_stat alm_reset ( DEVICE * dptr ) ; | |
| t_stat alm_svc ( UNIT * uptr ) ; | |
| int32 alm ( int32 pulse, int32 code, int32 AC ) ; | |
| DEVICE alm_dev ; | |
| #define QTY_MAX 64 /* max number of QTY lines - hardware */ | |
| int32 qty_brkio = SCPE_OK ; /* default I/O status code */ | |
| int32 qty_max = QTY_MAX ; /* max # QTY lines - user */ | |
| /* controllable */ | |
| int32 qty_mdm = 0 ; /* QTY modem control active? */ | |
| int32 qty_auto = 0 ; /* QTY auto disconnect active? */ | |
| int32 qty_polls = 0 ; /* total 'qty_svc' polls */ | |
| TMLN qty_ldsc[ QTY_MAX ] = { 0 } ; /* QTY line descriptors */ | |
| TMXR qty_desc = { QTY_MAX, 0, 0, qty_ldsc } ; /* mux descriptor */ | |
| int32 qty_status[ QTY_MAX ] = { 0 } ; /* QTY line status */ | |
| /* (must be at least 32 bits) */ | |
| int32 qty_tx_chr[ QTY_MAX ] = { 0 } ; /* QTY line output character */ | |
| /* QTY data structures | |
| qty_dev QTY device descriptor | |
| qty_unit QTY unit descriptor | |
| qty_reg QTY register list | |
| */ | |
| DIB qty_dib = { DEV_QTY, INT_QTY, PI_QTY, &qty } ; | |
| UNIT qty_unit = | |
| { | |
| UDATA (&qty_svc, (UNIT_ATTABLE), 0) | |
| } ; | |
| REG qty_reg[] = /* ('alm_reg' should be similar to this except for device code related items) */ | |
| { | |
| { ORDATA (BUF, qty_unit.buf, 8) }, | |
| { FLDATA (BUSY, dev_busy, INT_V_QTY) }, | |
| { FLDATA (DONE, dev_done, INT_V_QTY) }, | |
| { FLDATA (DISABLE, dev_disable, INT_V_QTY) }, | |
| { FLDATA (INT, int_req, INT_V_QTY) }, | |
| { FLDATA (MDMCTL, qty_mdm, 0) }, | |
| { FLDATA (AUTODS, qty_auto, 0) }, | |
| { DRDATA (POLLS, qty_polls, 32) }, | |
| { NULL } | |
| } ; | |
| MTAB qty_mod[] = | |
| { | |
| { UNIT_8B, 0, "7b", "7B", NULL }, | |
| { UNIT_8B, UNIT_8B, "8b", "8B", NULL }, | |
| { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", | |
| &tmxr_dscln, NULL, (void *)&qty_desc }, | |
| { UNIT_ATT, UNIT_ATT, "connections", NULL, | |
| NULL, &tmxr_show_summ, (void *)&qty_desc }, | |
| { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, | |
| NULL, &tmxr_show_cstat, (void *)&qty_desc }, | |
| { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, | |
| NULL, &tmxr_show_cstat, (void *)&qty_desc }, | |
| { MTAB_XTD | MTAB_VDV, 0, "LINES", "LINES", | |
| &qty_setnl, &tmxr_show_lines, (void *) &qty_desc }, | |
| { 0 } | |
| } ; | |
| DEVICE qty_dev = | |
| { | |
| "QTY", &qty_unit, qty_reg, qty_mod, | |
| 1, 10, 31, 1, 8, 8, | |
| NULL, NULL, &qty_reset, | |
| NULL, &qty_attach, &qty_detach, | |
| &qty_dib, (DEV_DISABLE | DEV_DIS | DEV_MUX) | |
| }; | |
| #define DG_RETURN( status, data ) (int32)(((status) << IOT_V_REASON) | ((data) & 0x0FFFF) ) | |
| /* | |
| * QTY_S_xxx QTY device status reference | |
| * QTY_L_xxx QTY line status word reference (qty_status[]) | |
| */ | |
| /*----------------------------------------------*/ | |
| /* QTY device status */ | |
| /*----------------------------------------------*/ | |
| #define QTY_S_RI 0x8000 /* Receiver Interrupt */ | |
| #define QTY_S_TI 0x4000 /* Transmitter interrupt */ | |
| #define QTY_S_LMASK 0x3F00 /* line mask */ | |
| #define QTY_S_DMASK 0x00FF /* data mask (received char) */ | |
| #define QTY_MASTER_ACTIVE( desc ) ( (desc)->master ) | |
| #define QTY_LINE_EXTRACT( x ) (((x) & QTY_S_LMASK) >> 8) | |
| #define QTY_LINE_TX_CHAR( line ) qty_tx_chr[ ((line) % QTY_MAX) ] | |
| #define QTY_LINE_RX_CHAR( line ) (qty_status[ (line) ] & QTY_S_DMASK) | |
| #define QTY_UNIT_ACTIVE( unitp ) ( (unitp)->conn ) | |
| #define QTY_LINE_BITS( line, bits ) (qty_status[ (line) ] & bits) | |
| #define QTY_LINE_SET_BIT( line, bit ) qty_status[ (line) ] |= (bit) ; | |
| #define QTY_LINE_CLEAR_BIT( line, bit ) qty_status[ (line) ] &= ~(bit) ; | |
| #define QTY_LINE_BIT_SET( line, bit ) (qty_status[ (line) ] & (bit)) | |
| /*----------------------------------------------*/ | |
| /* QTY line status */ | |
| /*----------------------------------------------*/ | |
| #define QTY_L_RXE 0x800000 /* receiver enabled? */ | |
| #define QTY_L_RXBZ 0x400000 /* receiver busy? */ | |
| #define QTY_L_RXDN 0x200000 /* receiver done? */ | |
| #define QTY_L_TXE 0x080000 /* transmitter enabled? */ | |
| #define QTY_L_TXBZ 0x040000 /* transmitter busy? */ | |
| #define QTY_L_TXDN 0x020000 /* transmitter done? */ | |
| #define QTY_L_BREAK 0x008000 /* BREAK character received */ | |
| #define QTY_L_RING 0x004000 /* Ring interrupt */ | |
| #define QTY_L_CD 0x002000 /* Carrier Detect */ | |
| #define QTY_L_DTR 0x001000 /* Data Terminal Ready */ | |
| /* <0x00FF = character> */ | |
| #define QTY_L_LOOPBK 0x00010000 /* loopback mode */ | |
| #define QTY_L_OVRERR 0x00020000 /* overrun error */ | |
| #define QTY_L_FRMERR 0x00040000 /* framing error */ | |
| #define QTY_L_PARERR 0x00080000 /* parity error */ | |
| /* CD, CTS, DSR, RI */ | |
| /* <future> */ | |
| #define QTY_L_MODEM 0x0080 /* <not yet used> */ | |
| #define QTY_L_TELNET 0x0040 /* <not yet used> */ | |
| #define QTY_L_AUTODIS 0x0020 /* <not yet used> */ | |
| #define QTY_L_PARITY | |
| #define QTY_L_7BIT | |
| #define QTY_L_BAUD /* <4 bits> */ | |
| #define QTY_L_DMASK 0x000FF /* data mask (always 8 bits) */ | |
| /* Note: use at least an 'int32' for this guy */ | |
| /*------------------------------*/ | |
| /* qty_tmxr_putc */ | |
| /*------------------------------*/ | |
| int qty_tmxr_putc( int line, TMLN * lp, int kar ) | |
| { | |
| int a ; | |
| /*----------------------------------------------*/ | |
| /* Send character to given QTY/telnet line. */ | |
| /* */ | |
| /* enter: line QTY line # */ | |
| /* lp Telnet unit def ptr */ | |
| /* kar character to send */ | |
| /* */ | |
| /* return: SCPE_OK */ | |
| /* SCPE_STALL */ | |
| /* SCPE_LOST */ | |
| /*----------------------------------------------*/ | |
| a = tmxr_putc_ln( lp, kar ) ; | |
| if ( a == SCPE_OK) | |
| { | |
| QTY_LINE_SET_BIT( line, QTY_L_TXDN ) | |
| QTY_LINE_CLEAR_BIT( line, QTY_L_TXBZ ) | |
| } | |
| else if ( a == SCPE_STALL ) | |
| { | |
| /* | |
| (should we try to output the buffer | |
| and then regroup...?) | |
| */ | |
| QTY_LINE_SET_BIT( line, QTY_L_TXBZ ) | |
| QTY_LINE_CLEAR_BIT( line, QTY_L_TXDN ) | |
| QTY_LINE_TX_CHAR( line ) = kar ; | |
| } | |
| else if ( a == SCPE_LOST ) | |
| { | |
| /* no connection - hangup? */ | |
| QTY_LINE_SET_BIT( line, QTY_L_TXBZ ) | |
| QTY_LINE_CLEAR_BIT( line, QTY_L_TXDN ) | |
| QTY_LINE_TX_CHAR( line ) = kar ; | |
| } | |
| return ( a ) ; | |
| } /* end of 'qty_tmxr_putc' */ | |
| /*----------------------------------------------*/ | |
| /* qty_update_rcvi */ | |
| /*----------------------------------------------*/ | |
| int qty_update_rcvi( TMXR * mp ) | |
| { | |
| int line ; | |
| TMLN * lp ; | |
| int32 datum ; | |
| int changes ; | |
| /*------------------------------------------------------*/ | |
| /* Search through connected telnet lines for any input */ | |
| /* activity. */ | |
| /* */ | |
| /* enter: mp master telnet qty desc ptr */ | |
| /* */ | |
| /* return: int change count (0 = none seen) */ | |
| /*------------------------------------------------------*/ | |
| for ( changes = line = 0; line < mp->lines; ++line ) | |
| if ( (lp=mp->ldsc+line)->conn && lp->rcve ) | |
| if ( (datum=tmxr_getc_ln(lp)) ) | |
| { | |
| if ( datum & SCPE_BREAK ) | |
| { | |
| /* what should we do here - set QTY_L_BREAK? */ | |
| datum = datum & 0x00FF ; | |
| } | |
| else | |
| { | |
| datum = datum & 0x00FF ; | |
| } | |
| /* <check parity, masking, forced parity, CR/LF xlation> */ | |
| QTY_LINE_CLEAR_BIT( line, (QTY_L_RXBZ | QTY_L_DMASK) ) ; | |
| QTY_LINE_SET_BIT( line, (QTY_L_RXDN | datum) ) ; | |
| ++changes ; | |
| } | |
| return ( changes ) ; | |
| } /* end of 'qty_update_rcvi' */ | |
| /*----------------------------------------------*/ | |
| /* qty_update_xmti */ | |
| /*----------------------------------------------*/ | |
| int qty_update_xmti( TMXR * mp ) | |
| { | |
| int line ; | |
| TMLN * lp ; | |
| int changes ; | |
| /*------------------------------------------------------*/ | |
| /* Search through connected telnet lines for any de- */ | |
| /* ferred output activity. */ | |
| /* */ | |
| /* enter: mp master telnet qty desc ptr */ | |
| /* */ | |
| /* return: int change count (0 = none seen) */ | |
| /*------------------------------------------------------*/ | |
| /* any TX DONE flags set | |
| * any TX BUSY flags set | |
| */ | |
| for ( changes = line = 0; line < mp->lines; ++line ) | |
| if ( QTY_LINE_BIT_SET(line,QTY_L_TXBZ) ) | |
| if ( (lp=mp->ldsc+line)->conn && lp->xmte ) | |
| { | |
| /* why are we busy? buffer was full? */ | |
| /* now some space available - try | |
| * to stuff pending character in | |
| * buffer and free up the world | |
| */ | |
| qty_tmxr_putc( line, lp, QTY_LINE_TX_CHAR(line) ) ; | |
| ++changes ; | |
| } | |
| return ( changes ) ; | |
| } /* end of 'qty_update_xmti' */ | |
| /*----------------------------------------------*/ | |
| /* qty_update_status */ | |
| /*----------------------------------------------*/ | |
| int qty_update_status( DIB * dibp, TMXR * tmxr_desc ) | |
| { | |
| int line ; | |
| int status ; | |
| int txbusy ; | |
| /*----------------------------------------------*/ | |
| /* return global device status for current qty */ | |
| /* state. */ | |
| /* */ | |
| /* Receiver interrupts have higher priority */ | |
| /* than transmitter interrupts according to DG */ | |
| /* but this routine could be modified to use */ | |
| /* different priority criteria. */ | |
| /* */ | |
| /* Round-robin polling could also be used in */ | |
| /* some future release rather than starting */ | |
| /* with line 0 each time. */ | |
| /* */ | |
| /* Return <QTY_S_RI + line # + character> of */ | |
| /* first waiting character, else return */ | |
| /* <QTY_S_TI + line #> of first finished line */ | |
| /* output, else return 0. */ | |
| /* */ | |
| /* This routine does -not- clear input line */ | |
| /* BZ/DN flags; caller should do this. */ | |
| /* */ | |
| /* Global device done and busy flags are */ | |
| /* updated. */ | |
| /*----------------------------------------------*/ | |
| for ( txbusy = status = line = 0 ; line < qty_max ; ++line ) | |
| { | |
| txbusy |= (QTY_LINE_BIT_SET(line,QTY_L_TXBZ)) ; | |
| if ( QTY_LINE_BIT_SET(line,QTY_L_RXDN) ) | |
| { | |
| if ( ! status ) | |
| { | |
| status = QTY_LINE_BITS( line, QTY_S_DMASK ) | QTY_S_RI ; | |
| status = status | (line << 8) ; | |
| } | |
| break ; | |
| } | |
| else if ( QTY_LINE_BIT_SET(line,QTY_L_TXDN) ) | |
| { | |
| if ( ! (status & QTY_S_RI) ) | |
| if ( ! (status & QTY_S_RI) ) | |
| { | |
| status = QTY_S_TI ; | |
| status = status | (line << 8) ; | |
| } | |
| } | |
| } | |
| /* <we could check each line for TX busy to set DEV_SET_BUSY)?> */ | |
| DEV_CLR_BUSY( INT_QTY ) ; | |
| DEV_CLR_DONE( INT_QTY ) ; | |
| if ( txbusy ) | |
| { | |
| DEV_SET_BUSY( INT_QTY ) ; | |
| } | |
| if ( status & (QTY_S_RI | QTY_S_TI) ) | |
| { | |
| DEV_SET_DONE( INT_QTY ) ; | |
| } | |
| DEV_UPDATE_INTR ; /* update final intr status */ | |
| return ( status ) ; | |
| } /* end of 'qty_update_status' */ | |
| /*--------------------------------------------------------------*/ | |
| /* qty_attach */ | |
| /*--------------------------------------------------------------*/ | |
| t_stat qty_attach( UNIT * unitp, char * cptr ) | |
| { | |
| t_stat r ; | |
| int a ; | |
| /* switches: A auto-disconnect | |
| * M modem control | |
| */ | |
| qty_mdm = qty_auto = 0; /* modem ctl off */ | |
| r = tmxr_attach( &qty_desc, unitp, cptr ) ; /* attach QTY */ | |
| if ( r != SCPE_OK ) | |
| { | |
| return ( r ) ; /* error! */ | |
| } | |
| if ( sim_switches & SWMASK('M') ) /* modem control? */ | |
| { | |
| qty_mdm = 1; | |
| printf( "Modem control activated\n" ) ; | |
| if ( sim_log ) fprintf( sim_log, "Modem control activated\n" ) ; | |
| if ( sim_switches & SWMASK ('A') ) /* autodisconnect? */ | |
| { | |
| qty_auto = 1 ; | |
| printf( "Auto disconnect activated\n" ) ; | |
| if ( sim_log ) fprintf( sim_log, "Auto disconnect activated\n" ) ; | |
| } | |
| } | |
| qty_polls = 0 ; | |
| for ( a = 0 ; a < QTY_MAX ; ++a ) | |
| { | |
| /* QTY lines are always enabled - force RX and TX to 'enabled' */ | |
| qty_status[ a ] = (QTY_L_RXE | QTY_L_TXE) ; | |
| } | |
| sim_activate( unitp, tmxr_poll ) ; | |
| return ( SCPE_OK ) ; | |
| } /* end of 'qty_attach' */ | |
| /*--------------------------------------------------------------*/ | |
| /* qty_detach */ | |
| /*--------------------------------------------------------------*/ | |
| t_stat qty_detach( UNIT * unitp ) | |
| { | |
| sim_cancel( unitp ) ; | |
| return ( tmxr_detach(&qty_desc,unitp) ) ; | |
| } /* end of 'qty_detach' */ | |
| /*--------------------------------------------------------------*/ | |
| /* qty_clear */ | |
| /*--------------------------------------------------------------*/ | |
| t_stat qty_clear( t_bool flag ) | |
| { | |
| int line ; | |
| for ( line = 0 ; line < qty_max ; ++line ) | |
| { | |
| qty_ldsc[line].xmte = 0 ; | |
| qty_ldsc[line].rcve = 0 ; | |
| if ( ! qty_ldsc[line].conn ) | |
| { | |
| qty_ldsc[line].xmte = 1 ; /* set xmt enb */ | |
| qty_ldsc[line].rcve = 1 ; /* clr rcv enb */ | |
| } | |
| } | |
| return ( SCPE_OK ) ; | |
| } /* end of 'qty_clear' */ | |
| /*----------------------------------------------*/ | |
| /* qty_common_reset */ | |
| /*----------------------------------------------*/ | |
| t_stat qty_common_reset( DIB * dibp, UNIT * unitp, DEVICE * dptr ) | |
| { | |
| if ((dptr->flags & DEV_DIS) == 0) | |
| { | |
| if (dptr == &qty_dev) alm_dev.flags |= DEV_DIS; | |
| else qty_dev.flags |= DEV_DIS; | |
| } | |
| qty_clear( TRUE ) ; | |
| DEV_CLR_BUSY( INT_QTY ) ; /* clear busy */ | |
| DEV_CLR_DONE( INT_QTY ) ; /* clear done, int */ | |
| DEV_UPDATE_INTR ; | |
| if ( QTY_MASTER_ACTIVE(&qty_desc) ) | |
| { | |
| sim_activate( unitp, tmxr_poll ) ; | |
| } | |
| else | |
| { | |
| sim_cancel( unitp ) ; | |
| } | |
| return ( SCPE_OK ) ; | |
| } /* end of 'qty_common_reset' */ | |
| /*--------------------------------------------------------------*/ | |
| /* qty_reset */ | |
| /*--------------------------------------------------------------*/ | |
| t_stat qty_reset( DEVICE * dptr ) | |
| { | |
| return ( qty_common_reset(&qty_dib,&qty_unit,dptr) ) ; | |
| } /* end of 'qty_reset' */ | |
| /* Unit service routine | |
| The QTY/ALM polls to see if asynchronous activity has occurred and now | |
| needs to be processed. The polling interval is controlled by the clock | |
| simulator, so for most environments, it is calibrated to real time. | |
| The simulator assumes that software enables all of the multiplexors, | |
| or none of them. | |
| */ | |
| /*----------------------------------------------*/ | |
| /* qty_common_svc */ | |
| /*----------------------------------------------*/ | |
| t_stat qty_common_svc( DIB * dibp, UNIT * unitp ) | |
| { | |
| int line ; | |
| int newln ; | |
| TMLN * tmlnp ; | |
| ++qty_polls ; /* another time 'round the track */ | |
| newln = tmxr_poll_conn( &qty_desc ) ; /* anybody knocking at the door? */ | |
| if ( (newln >= 0) && qty_mdm ) | |
| { | |
| if ( newln >= qty_max ) | |
| { | |
| return SCPE_IERR; /* WTF - sanity check failed, over? */ | |
| } | |
| else | |
| { | |
| line = newln ; /* handle modem control */ | |
| tmlnp =&qty_ldsc[ line ] ; | |
| tmlnp->rcve = tmlnp->xmte = 1 ; | |
| /* do QTY_LINE_ bit fiddling and state machine | |
| * manipulation with modem control signals | |
| */ | |
| } | |
| } | |
| tmxr_poll_rx( &qty_desc ) ; /* poll input */ | |
| qty_update_rcvi( &qty_desc ) ; /* update receiver interrupt status */ | |
| tmxr_poll_tx( &qty_desc ) ; /* poll output */ | |
| qty_update_xmti( &qty_desc ) ; /* update transmitter interrupt status */ | |
| qty_update_status( dibp, &qty_desc ) ; /* update device status */ | |
| sim_activate( unitp, tmxr_poll ) ; /* restart the bubble machine */ | |
| return ( SCPE_OK ) ; | |
| } /* end of 'qty_common_svc' */ | |
| /*--------------------------------------------------------------*/ | |
| /* qty_svc */ | |
| /*--------------------------------------------------------------*/ | |
| t_stat qty_svc( UNIT * uptr ) | |
| { | |
| return ( qty_common_svc(&qty_dib,uptr) ) ; | |
| } /* end of 'qty_svc' */ | |
| /*--------------------------------------------------------------*/ | |
| /* qty */ | |
| /*--------------------------------------------------------------*/ | |
| int32 qty( int32 pulse, int32 code, int32 AC ) | |
| { | |
| int32 iodata ; | |
| int32 ioresult ; | |
| int line ; | |
| TMLN * tmlnp ; | |
| int a ; | |
| int kar ; | |
| /*--------------------------------------------------------------*/ | |
| /* DG 4060[-compatible] "quad" multiplexor instruction handler */ | |
| /*--------------------------------------------------------------*/ | |
| ioresult= qty_brkio ; /* (assume returning I/O break value */ | |
| iodata = 0 ; /* (assume 16-bit Nova/Eclipse bus) */ | |
| switch ( code ) | |
| { | |
| case ioNIO : /* <no operation> */ | |
| break ; | |
| case ioDIA : /* get current QTY status */ | |
| iodata = qty_update_status( &qty_dib, &qty_desc ) ; | |
| if ( iodata & QTY_S_RI ) | |
| { /* clear line's input buffer */ | |
| QTY_LINE_CLEAR_BIT( (QTY_LINE_EXTRACT(iodata)), (QTY_L_RXBZ | QTY_L_RXDN) ) | |
| /* | |
| character masking ; | |
| parity checking ; | |
| parity generating ; | |
| */ | |
| } | |
| qty_update_status( &qty_dib, &qty_desc ) ; | |
| break ; | |
| case ioDOA : /* send character to QTY */ | |
| line = QTY_LINE_EXTRACT( AC ) ; | |
| if ( line < qty_max ) | |
| if ( QTY_LINE_BIT_SET(line,QTY_L_TXE) ) | |
| { | |
| /* | |
| perform any character translation: | |
| 7 bit/ 8 bit | |
| parity generation | |
| */ | |
| kar = AC & ((qty_unit.flags & UNIT_8B)? 0377: 0177) ; | |
| /* do any parity calculations also */ | |
| tmlnp = &qty_ldsc[ line ] ; | |
| a = qty_tmxr_putc( line, tmlnp, kar ) ; | |
| if ( a != SCPE_OK) | |
| { | |
| /* do anything at this point? */ | |
| } | |
| qty_update_status( &qty_dib, &qty_desc ) ; | |
| } | |
| break ; | |
| case ioDIB : /* no QTY function - return bus noise in AC */ | |
| break ; | |
| case ioDOB : /* clear QTY output channel busy and done flag */ | |
| QTY_LINE_CLEAR_BIT( (QTY_LINE_EXTRACT(AC)), (QTY_L_TXBZ | QTY_L_TXDN) ) | |
| qty_update_status( &qty_dib, &qty_desc ) ; | |
| break ; | |
| case ioDIC : /* no QTY function - return bus noise in AC */ | |
| break ; | |
| case ioDOC : /* no QTY function - ignore */ | |
| break ; | |
| case ioSKP : /* I/O skip test - should never come here */ | |
| break ; | |
| default : | |
| /* <illegal I/O operation value> */ | |
| break ; | |
| } | |
| switch ( pulse ) | |
| { | |
| case iopN : /* <ignored (of course)> */ | |
| break ; | |
| case iopS : /* <ignored> */ | |
| break ; | |
| case iopP : /* <ignored> */ | |
| break ; | |
| case iopC : | |
| qty_update_status( &qty_dib, &qty_desc ) ; | |
| break ; | |
| default : | |
| /* <illegal pulse value> */ | |
| break ; | |
| } | |
| return ( DG_RETURN( ioresult, iodata ) ) ; | |
| } /* end of 'qty' */ | |
| /*--------------------------------------------------------------*/ | |
| /* qty_setnl */ | |
| /*--------------------------------------------------------------*/ | |
| t_stat qty_setnl( UNIT * uptr, int32 val, char * cptr, void * desc ) | |
| { | |
| int32 newln, i, t ; | |
| t_stat r ; | |
| if ( cptr == NULL ) | |
| { | |
| return ( SCPE_ARG ) ; | |
| } | |
| newln = (int32) get_uint( cptr, 10, QTY_MAX, &r ) ; | |
| if ( (r != SCPE_OK) || (newln == qty_desc.lines) ) | |
| { | |
| return ( r ) ; | |
| } | |
| if ( (newln == 0) || (newln > QTY_MAX) ) | |
| { | |
| return ( SCPE_ARG ) ; | |
| } | |
| if ( newln < qty_desc.lines ) | |
| { | |
| for ( i = newln, t = 0 ; i < qty_desc.lines ; ++i ) | |
| { | |
| t = t | qty_ldsc[i].conn ; | |
| } | |
| if ( t && ! get_yn("This will disconnect users; proceed [N]?", FALSE) ) | |
| { | |
| return ( SCPE_OK ) ; | |
| } | |
| for ( i = newln ; i < qty_desc.lines ; ++i ) | |
| { | |
| if ( qty_ldsc[i].conn ) | |
| { /* reset line */ | |
| tmxr_msg( qty_ldsc[i].conn, "\r\nOperator disconnected line\r\n" ) ; | |
| tmxr_reset_ln( &qty_ldsc[i] ) ; | |
| } | |
| qty_clear( TRUE ) ; /* reset mux */ | |
| } | |
| } | |
| qty_max = qty_desc.lines = newln ; | |
| /* Huh, I don't understand this yet... | |
| qty_max = ((qty_dev.flags & DEV_DIS)? 0 : (qty_desc.lines / QTY_MAX)) ; | |
| */ | |
| return ( SCPE_OK ) ; | |
| } /* end of 'qty_setnl' */ | |
| /*----------------------------------------------------------------------*/ | |
| /* ALM [425x-compatible] multiplexor */ | |
| /*----------------------------------------------------------------------*/ | |
| /* | |
| * device code: 034 [primary], | |
| * 074 [secondary] | |
| * interrupt mask: B14 [000002] | |
| * ASM mnemonic: ALM | |
| * | |
| * ALM [4255-4258] I/O instructions | |
| * | |
| * DIA read line and section requesting service | |
| * DOA select line and section (lines 0-255, 8-bits) + rcvr/xmit | |
| * DIB receive data | |
| * DOB 00 transmit data | |
| * 01 transmit BREAK | |
| * 10 set modem control status | |
| * 11 <ignored> | |
| * DIC read receiver or modem status | |
| * DOC 00 control line section and diag mode | |
| * 01 | |
| * 10 specify line characteristics | |
| * 11 | |
| * | |
| * undocumented DG "features": | |
| * | |
| * NIOS sets board offline | |
| * NIOC sets board online | |
| * Modem control signal state change can signal interrupt | |
| * explicit line select with DOA | |
| * implicit line select with DIA | |
| * | |
| * We support 64 lines maximum in this release although some ALM's could | |
| * theoretically support up to 256. | |
| */ | |
| DIB alm_dib = { DEV_ALM, INT_ALM, PI_ALM, &alm } ; | |
| UNIT alm_unit = | |
| { | |
| UDATA (&alm_svc, (UNIT_ATTABLE), 0) | |
| } ; | |
| REG alm_reg[] = /* ('qty_reg' should be similar to this except for device code related items) */ | |
| { | |
| { ORDATA (BUF, alm_unit.buf, 8) }, | |
| { FLDATA (BUSY, dev_busy, INT_V_ALM) }, | |
| { FLDATA (DONE, dev_done, INT_V_ALM) }, | |
| { FLDATA (DISABLE, dev_disable, INT_V_ALM) }, | |
| { FLDATA (INT, int_req, INT_V_ALM) }, | |
| { FLDATA (MDMCTL, qty_mdm, 0) }, | |
| { FLDATA (AUTODS, qty_auto, 0) }, | |
| { DRDATA (POLLS, qty_polls, 32) }, | |
| { NULL } | |
| } ; | |
| DEVICE alm_dev = | |
| { | |
| "ALM", &alm_unit, alm_reg, qty_mod, | |
| 1, 10, 31, 1, 8, 8, | |
| NULL, NULL, &alm_reset, | |
| NULL, &qty_attach, &qty_detach, | |
| &alm_dib, (DEV_DISABLE | DEV_NET) | |
| } ; | |
| int alm_section = -1 ; /* current line "section" (0 = RCV, 1 = XMT) */ | |
| int alm_line = -1 ; /* current line [0-63] */ | |
| int alm_diag_mode = 0 ; /* <not yet supported> */ | |
| int alm_line_mask = 0x003F ; /* maximum of 64 lines in this rev */ | |
| #define ALM_LINE_EXTRACT( x ) (((x) >> 1) & alm_line_mask) | |
| #define ALM_SECT_EXTRACT( x ) ((x) & 0x0001) | |
| /*--------------------------------------------------------------*/ | |
| /* alm_reset */ | |
| /*--------------------------------------------------------------*/ | |
| t_stat alm_reset( DEVICE * dptr ) | |
| { | |
| return ( qty_common_reset(&alm_dib,&alm_unit,dptr) ) ; | |
| } /* end of 'alm_reset' */ | |
| /*--------------------------------------------------------------*/ | |
| /* alm_svc */ | |
| /*--------------------------------------------------------------*/ | |
| t_stat alm_svc( UNIT * uptr ) | |
| { | |
| return ( qty_common_svc(&alm_dib,uptr) ) ; | |
| } /* end of 'alm_svc' */ | |
| /*--------------------------------------------------------------*/ | |
| /* alm */ | |
| /*--------------------------------------------------------------*/ | |
| int32 alm( int32 pulse, int32 code, int32 AC ) | |
| { | |
| int32 iodata ; | |
| int32 ioresult ; | |
| TMLN * tmlnp ; | |
| int a ; | |
| int kar ; | |
| /*--------------------------------------------------------------*/ | |
| /* DG 425x[-compatible] "ALM" multiplexor instruction handler */ | |
| /*--------------------------------------------------------------*/ | |
| ioresult= qty_brkio ; /* (assume returning I/O break value */ | |
| iodata = 0 ; /* (assume 16-bit Nova/Eclipse bus) */ | |
| switch ( code ) | |
| { | |
| case ioNIO : /* <no operation> */ | |
| break ; | |
| case ioDIA : /* read line and section requesting service */ | |
| iodata = qty_update_status( &alm_dib, &qty_desc ) ; | |
| alm_line = (QTY_LINE_EXTRACT(iodata) & alm_line_mask) ; | |
| /* (mask with 'alm_line_mask' in case ALM mask is different than QTY */ | |
| alm_section = 0 ; | |
| if ( ! ( iodata & QTY_S_RI) ) | |
| if ( iodata & QTY_S_TI ) | |
| { | |
| alm_section = 1 ; /* receiver quiet - transmitter done */ | |
| } | |
| iodata = (alm_line << 1) | alm_section ; | |
| break ; | |
| case ioDOA : /* set line and section */ | |
| alm_section = ALM_SECT_EXTRACT( AC ) ; | |
| alm_line = ALM_LINE_EXTRACT( AC ) ; | |
| break ; | |
| case ioDIB : /* no ALM function - return bus noise in AC */ | |
| if ( alm_line < qty_max ) | |
| { | |
| iodata = QTY_LINE_RX_CHAR( alm_line ) ; | |
| } | |
| break ; | |
| case ioDOB : /* output and modem control functions */ | |
| switch ( (AC >> 14) & 03 ) | |
| { | |
| case 00 : /* transmit data */ | |
| if ( alm_line < qty_max ) | |
| if ( QTY_LINE_BIT_SET(alm_line,QTY_L_TXE) ) | |
| { | |
| /* | |
| perform any character translation: | |
| 7 bit/ 8 bit | |
| parity generation | |
| */ | |
| kar = AC & ((alm_unit.flags & UNIT_8B)? 0377: 0177) ; | |
| /* do any parity calculations also */ | |
| tmlnp = &qty_ldsc[ alm_line ] ; | |
| a = qty_tmxr_putc( alm_line, tmlnp, kar ) ; | |
| if ( a != SCPE_OK) | |
| { | |
| /* do anything at this point? */ | |
| } | |
| qty_update_status( &alm_dib, &qty_desc ) ; | |
| } | |
| break ; | |
| case 01 : /* transmit break */ | |
| if ( alm_line < qty_max ) | |
| if ( QTY_LINE_BIT_SET(alm_line,QTY_L_TXE) ) | |
| { | |
| tmlnp = &qty_ldsc[ alm_line ] ; | |
| /* | |
| a = qty_tmxr_putc( alm_line, tmlnp, kar ) ; | |
| if ( a != SCPE_OK) | |
| { | |
| } | |
| */ | |
| qty_update_status( &alm_dib, &qty_desc ) ; | |
| } | |
| break ; | |
| case 02 : /* set modem control status */ | |
| break ; | |
| case 03 : /* unused */ | |
| break ; | |
| } | |
| break ; | |
| case ioDIC : /* get modem or receiver status */ | |
| if ( alm_line < qty_max ) | |
| { | |
| if ( alm_section ) | |
| { | |
| /* get modem section status */ | |
| if ( qty_ldsc[ alm_line ].xmte ) | |
| { | |
| iodata = 0035 ; /* set CD, CTS, DSR, MDM flags */ | |
| } | |
| } | |
| else | |
| { | |
| /* get receiver section status */ | |
| iodata = 0 ; /* receiver error status - no errors by default */ | |
| } | |
| } | |
| break ; | |
| case ioDOC : /* set line attributes */ | |
| switch ( (AC >> 14) & 03 ) | |
| { | |
| case 00 : /* control line section */ | |
| break ; | |
| case 01 : /* unused */ | |
| break ; | |
| case 02 : /* set line characteristics */ | |
| break ; | |
| case 03 : /* unused */ | |
| break ; | |
| } | |
| break ; | |
| case ioSKP : /* I/O skip test - should never come here */ | |
| break ; | |
| default : | |
| /* <illegal I/O operation value> */ | |
| break ; | |
| } | |
| switch ( pulse ) | |
| { | |
| case iopN : /* <ignored (of course)> */ | |
| break ; | |
| case iopS : /* set device busy | |
| * set all lines on board offline | |
| * clear each line's done | |
| * clear internal system | |
| * clear device busy | |
| */ | |
| for ( a = 0 ; a < qty_max ; ++a ) | |
| if ( 1 /* (not yet optimized) */ ) | |
| { | |
| QTY_LINE_CLEAR_BIT( a, (QTY_L_RXBZ | QTY_L_RXDN | QTY_L_TXBZ | QTY_L_TXDN) ) ; | |
| } | |
| qty_update_status( &alm_dib, &qty_desc ) ; | |
| break ; | |
| case iopP : /* stop clock for all boards in off-line mode */ | |
| break ; | |
| case iopC : | |
| for ( a = 0 ; a < qty_max ; ++a ) | |
| if ( 1 /* (not yet optimized) */ ) | |
| { | |
| QTY_LINE_CLEAR_BIT( a, (QTY_L_RXBZ | QTY_L_RXDN | QTY_L_TXBZ | QTY_L_TXDN) ) ; | |
| } | |
| qty_update_status( &alm_dib, &qty_desc ) ; | |
| break ; | |
| default : | |
| /* <illegal pulse value> */ | |
| break ; | |
| } | |
| return ( DG_RETURN( ioresult, iodata ) ) ; | |
| } /* end of 'alm' */ |