RESTRICTION: The PDP-15 FPP is only partially debugged. Do NOT enable this feature for normal operations. WARNING: The core simulator files (scp.c, sim_*.c) have been reorganized. Unzip V3.2-0 to an empty directory before attempting to compile the source. IMPORTANT: If you are compiling for UNIX, please read the notes for Ethernet very carefully. You may need to download a new version of the pcap library, or make changes to the makefile, to get Ethernet support to work. 1. New Features in 3.2-0 1.1 SCP and libraries - Added SHOW <device> RADIX command. - Added SHOW <device> MODIFIERS command. - Added SHOW <device> NAMES command. - Added SET/SHOW <device> DEBUG command. - Added sim_vm_parse_addr and sim_vm_fprint_addr optional interfaces. - Added REG_VMAD flag. - Split SCP into separate libraries for easier modification. - Added more room to the device and unit flag fields. - Changed terminal multiplexor library to support unlimited. number of async lines. 1.2 All DECtapes - Added STOP_EOR flag to enable end-of-reel error stop - Added device debug support. 1.3 Nova and Eclipse - Added QTY and ALM multiplexors (Bruce Ray). 1.4 LGP-30 - Added LGP-30/LGP-21 simulator. 1.5 PDP-11 - Added format, address increment inhibit, transfer overrun detection to RK. - Added device debug support to HK, RP, TM, TQ, TS. - Added DEUNA/DELUA (XU) support (Dave Hittner). - Add DZ per-line logging. 1.6 18b PDP's - Added support for 1-4 (PDP-9)/1-16 (PDP-15) additional terminals. 1.7 PDP-10 - Added DEUNA/DELUA (XU) support (Dave Hittner). 1.8 VAX - Added extended memory to 512MB (Mark Pizzolato). - Added RXV21 support. 2. Bugs Fixed in 3.2-0 2.1 SCP - Fixed double logging of SHOW BREAK (found by Mark Pizzolato). - Fixed implementation of REG_VMIO. 2.2 Nova and Eclipse - Fixed device enable/disable support (found by Bruce Ray). 2.3 PDP-1 - Fixed bug in LOAD (found by Mark Crispin). 2.4 PDP-10 - Fixed bug in floating point unpack. - Fixed bug in FIXR (found by Phil Stone, fixed by Chris Smith). 2.6 PDP-11 - Fixed bug in RQ interrupt control (found by Tom Evans). 2.6 PDP-18B - Fixed bug in PDP-15 XVM g_mode implementation. - Fixed bug in PDP-15 indexed address calculation. - Fixed bug in PDP-15 autoindexed address calculation. - Fixed bugs in FPP-15 instruction decode. - Fixed clock response to CAF. - Fixed bug in hardware read-in mode bootstrap. - Fixed PDP-15 XVM instruction decoding errors. 2.7 VAX - Fixed PC read fault in EXTxV. - Fixed PC write fault in INSV.
1130 lines
32 KiB
C
1130 lines
32 KiB
C
/* 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' */
|