/* nova_qty.c: NOVA multiplexor (QTY/ALM) simulator | |
Copyright (c) 2000-2003, 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 | |
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 sim_switches ; | |
extern FILE * sim_log ; | |
extern int32 tmxr_poll ; /* calibrated delay */ | |
t_stat qty_summary ( FILE * st, UNIT * uptr, int32 val, void * desc ) ; | |
t_stat qty_show ( FILE * st, UNIT * uptr, int32 val, void * desc ) ; | |
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_nlreg = { DRDATA (NLINES, qty_desc.lines, 7), PV_LEFT }; | |
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, &qty_desc }, | |
{ UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &qty_summary }, | |
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, | |
NULL, &qty_show, NULL }, | |
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, | |
NULL, &qty_show, NULL }, | |
{ MTAB_XTD | MTAB_VDV | MTAB_VAL, 0, "lines", "LINES", | |
&qty_setnl, NULL, &qty_nlreg }, | |
{ 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_NET) | |
}; | |
/* <define these in 'nova_defs.h' someday...> */ | |
/* (need to mask off non-interrupt bit devices) */ | |
#define DEV_SET_BUSY( dibp ) dev_busy = dev_busy | ( (dibp)->mask ) ; | |
#define DEV_CLEAR_BUSY( dibp ) dev_busy = dev_busy & ~( (dibp)->mask ) ; | |
#define DEV_SET_DONE( dibp ) dev_done = dev_done | ( (dibp)->mask ) ; int_req = int_req & ~( (dibp)->mask ) ; | |
#define DEV_CLEAR_DONE( dibp ) dev_done = dev_done & ~( (dibp)->mask ) ; int_req = int_req & ~( (dibp)->mask ) ; | |
#define DEV_UPDATE_INTR int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable) ; | |
#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_CLEAR_BUSY( dibp ) | |
DEV_CLEAR_DONE( dibp ) | |
if ( txbusy ) | |
{ | |
DEV_SET_BUSY( dibp ) ; | |
} | |
if ( status & (QTY_S_RI | QTY_S_TI) ) | |
{ | |
DEV_SET_DONE( dibp ) | |
} | |
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_CLEAR_BUSY( dibp ) /* clear busy */ | |
DEV_CLEAR_DONE( dibp ) /* 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( iodata ) ; | |
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_summary */ | |
/*--------------------------------------------------------------*/ | |
t_stat qty_summary( FILE * st, UNIT * uptr, int32 val, void * desc ) | |
{ | |
int32 i, t ; | |
for (i = t = 0 ; i < qty_desc.lines ; ++i ) | |
if ( qty_ldsc[i].conn ) | |
{ | |
++t ; | |
} | |
fprintf( st, "%d connection%s", t, ((t)? "s" : "") ) ; | |
return ( SCPE_OK ) ; | |
} /* end of 'qty_summ' */ | |
/*--------------------------------------------------------------*/ | |
/* qty_show */ | |
/*--------------------------------------------------------------*/ | |
t_stat qty_show( FILE * st, UNIT * uptr, int32 val, void * desc ) | |
{ | |
int32 i, t ; | |
for (i = t = 0 ; i < qty_desc.lines ; ++i ) | |
if ( qty_ldsc[i].conn ) | |
{ | |
t = 1; | |
if ( val ) | |
{ | |
tmxr_fconns( st, &qty_ldsc[i], i ) ; | |
} | |
else | |
{ | |
tmxr_fstats( st, &qty_ldsc[i], i ) ; | |
} | |
} | |
if ( t == 0 ) fprintf( st, "none connected\n" ) ; | |
return ( SCPE_OK ) ; | |
} /* end of 'qty_show' */ | |
/*--------------------------------------------------------------*/ | |
/* 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 ) | |
{ | |
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 ) | |
{ | |
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' */ |