Show IOSPACE doesn't always get the number of devices right due to device creativity. o The distinction between UNIT and DEVICE has blurred o MUX devices merge several physical devices into one device/unit o Dynamic device sizing has made things more volatile. This edit solves the problem for SHOW IOSPACE by adding an (optional) word to the DIBs. The word contains the amount of IO space consumed by each instance of the physical device that's being emulated. E.G., if it's a DZ11, the device is the DZ11 module, or 8 lines, even though the MUX device may support 32. This enables SHOW IOSPACE to determine the number of physical devices being emulated, which is what folks need when configuring software. The word may have other uses - in a generic dynamic device sizing routine - which is why the amount of IOSPACE per device was chosen rather than the 'number of physical devices.' The edit should not make any existing device regress. If the new word (ulnt) is zero (not initialized), SHOW IOSPACE will default to the number of units in the device, or if there's no device (CPUs), 1 as before. If it is present, the number of devices is the calculated as total allocation/allocation-per-device. The edit updates all the devices that seem to require this treatment, and all the processors that define the UNIBUS/QBUS DIBs.
479 lines
18 KiB
C
479 lines
18 KiB
C
/* pdp11_kg.c - Communications Arithmetic Option KG11-A
|
||
|
||
Copyright (c) 2007-2010, John A. Dundas III
|
||
|
||
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 the author shall not be
|
||
used in advertising or otherwise to promote the sale, use or other dealings
|
||
in this Software without prior written authorization from the author.
|
||
|
||
kg KG11-A Communications Arithmetic Option (M7251)
|
||
|
||
03-Jan-10 JAD Eliminate gcc warnings
|
||
08-Jan-08 JAD First public release integrated with SIMH V3.7-3.
|
||
09-Dec-07 JAD SIMH-style debugging.
|
||
Finished validating against real hardware.
|
||
Support for up to 8 units, the maximum.
|
||
Keep all module data in the UNIT structure.
|
||
Clean up bit and mask definitions.
|
||
01-Dec-07 JAD Now work on the corner cases that the
|
||
diagnostic does not check.
|
||
CLR does not clear the QUO bit.
|
||
Correct SR write logic.
|
||
29-Nov-07 JAD Original implementation and testing based on
|
||
an idea from 07-Jul-03. Passes the ZKGAB0
|
||
diagnostic.
|
||
|
||
Information necessary to create this simulation was gathered from
|
||
a number of sources including:
|
||
|
||
KG11-A Exclusive-OR and CRC block check manual, DEC-11-HKGAA-B-D
|
||
<http://www.computer.museum.uq.edu.au/pdf/DEC-11-HKGAA-B-D%20KG11-A%20Exclusive-OR%20and%20CRC%20Block%20Check%20Manual.pdf>
|
||
Maintenance print set
|
||
<http://bitsavers.org/pdf/dec/unibus/KG11A_EngrDrws.pdf>
|
||
A Painless Guide to CRC Error Detection Algorithms, Ross N. Williams
|
||
<http://www.ross.net/crc/download/crc_v3.txt">
|
||
|
||
The original PDP-11 instruction set, as implemented in the /20,
|
||
/15, /10, and /5, did not include XOR. [One of the differences
|
||
tables incorrectly indicates the /04 does not implement this
|
||
instruction.] This device implements XOR, XORB, and a variety of
|
||
CRCs.
|
||
|
||
The maintenance prints indicate that the device was probably available
|
||
starting in late 1971. May need to check further. The first edition
|
||
of the manual was May 1972.
|
||
|
||
The device was still sold at least as late as mid-1982 according
|
||
to the PDP-11 Systems and Options Summary. RSTS/E included support
|
||
for up to 8 units in support of the 2780 emulation or for use with
|
||
DP11, DU11, or DUP11. The device appears to have been retired by
|
||
1983-03, and possibly earlier.
|
||
|
||
I/O Page Registers
|
||
|
||
SR 7707x0 (read-write) status
|
||
BCC 7707x2 (read-only) BCC (block check character)
|
||
DR 7707x4 (write-only) data
|
||
|
||
Vector: none
|
||
|
||
Priority: none
|
||
|
||
The KG11-A is a programmed-I/O, non-interrupting device. Therefore
|
||
no vector or bus request level are necessary. It is a Unibus device
|
||
but since it does not address memory itself (it only presents
|
||
registers in the I/O page) it is compatible with extended Unibus
|
||
machines (22-bit) as well as traditional Unibus.
|
||
|
||
Implements 5 error detection codes:
|
||
LRC-8
|
||
LRC-16
|
||
CRC-12
|
||
CRC-16
|
||
CRC-CCITT
|
||
*/
|
||
|
||
#if !defined (VM_PDP11)
|
||
#error "KG11 is not supported!"
|
||
#endif
|
||
#include "pdp11_defs.h"
|
||
|
||
extern REG cpu_reg[];
|
||
extern int32 R[];
|
||
|
||
#ifndef KG_UNITS
|
||
#define KG_UNITS (8)
|
||
#endif
|
||
|
||
/* Control and Status Register */
|
||
|
||
#define KGSR_V_QUO (8) /* RO */
|
||
#define KGSR_V_DONE (7) /* RO */
|
||
#define KGSR_V_SEN (6) /* R/W shift enable */
|
||
#define KGSR_V_STEP (5) /* W */
|
||
#define KGSR_V_CLR (4) /* W */
|
||
#define KGSR_V_DDB (3) /* R/W double data byte */
|
||
#define KGSR_V_CRCIC (2) /* R/W */
|
||
#define KGSR_V_LRC (1) /* R/W */
|
||
#define KGSR_V_16 (0) /* R/W */
|
||
|
||
#define KGSR_M_QUO (1u << KGSR_V_QUO)
|
||
#define KGSR_M_DONE (1u << KGSR_V_DONE)
|
||
#define KGSR_M_SEN (1u << KGSR_V_SEN)
|
||
#define KGSR_M_STEP (1u << KGSR_V_STEP)
|
||
#define KGSR_M_CLR (1u << KGSR_V_CLR)
|
||
#define KGSR_M_DDB (1u << KGSR_V_DDB)
|
||
#define KGSR_M_CRCIC (1u << KGSR_V_CRCIC)
|
||
#define KGSR_M_LRC (1u << KGSR_V_LRC)
|
||
#define KGSR_M_16 (1u << KGSR_V_16)
|
||
|
||
#define KG_SR_RDMASK (KGSR_M_QUO | KGSR_M_DONE | KGSR_M_SEN | KGSR_M_DDB | \
|
||
KGSR_M_CRCIC | KGSR_M_LRC | KGSR_M_16)
|
||
#define KG_SR_WRMASK (KGSR_M_SEN | KGSR_M_DDB | KGSR_M_CRCIC | \
|
||
KGSR_M_LRC | KGSR_M_16)
|
||
|
||
#define KG_SR_POLYMASK (KGSR_M_CRCIC|KGSR_M_LRC|KGSR_M_16)
|
||
|
||
/* Unit structure redefinitions */
|
||
#define SR u3
|
||
#define BCC u4
|
||
#define DR u5
|
||
#define PULSCNT u6
|
||
|
||
#define POLY_LRC8 (0x0008)
|
||
#define POLY_LRC16 (0x0080)
|
||
#define POLY_CRC12 (0x0f01)
|
||
#define POLY_CRC16 (0xa001)
|
||
#define POLY_CCITT (0x8408)
|
||
|
||
static const struct {
|
||
uint16 poly;
|
||
uint16 pulses;
|
||
const char * const name;
|
||
} config[] = {
|
||
/* DDB=0 */
|
||
{ POLY_CRC12, 6, "CRC-12" },
|
||
{ POLY_CRC16, 8, "CRC-16" },
|
||
{ POLY_LRC8, 8, "LRC-8" },
|
||
{ POLY_LRC16, 8, "LRC-16" },
|
||
{ 0, 0, "undefined" },
|
||
{ POLY_CCITT, 8, "CRC-CCITT" },
|
||
{ 0, 0, "undefined" },
|
||
{ 0, 0, "undefined" },
|
||
/* DDB=1 */
|
||
{ POLY_CRC12, 12, "CRC-12" },
|
||
{ POLY_CRC16, 16, "CRC-16" },
|
||
{ POLY_LRC8, 16, "LRC-8" },
|
||
{ POLY_LRC16, 16, "LRC-16" },
|
||
{ 0, 0, "undefined" },
|
||
{ POLY_CCITT, 16, "CRC-CCITT" },
|
||
{ 0, 0, "undefined" },
|
||
{ 0, 0, "undefined" }
|
||
};
|
||
|
||
/* Forward declarations */
|
||
|
||
static t_stat kg_rd (int32 *, int32, int32);
|
||
static t_stat kg_wr (int32, int32, int32);
|
||
static t_stat kg_reset (DEVICE *);
|
||
static void do_poly (int, t_bool);
|
||
static t_stat set_units (UNIT *, int32, char *, void *);
|
||
|
||
/* 16-bit rotate right */
|
||
|
||
#define ROR(n,v) (((v >> n) & DMASK) | ((v << (16 - n)) & DMASK))
|
||
|
||
/* 8-bit rotate right */
|
||
|
||
#define RORB(n,v) (((v & 0377) >> n) | ((v << (8 - n)) & 0377))
|
||
|
||
/* KG data structures
|
||
|
||
kg_dib KG PDP-11 device information block
|
||
kg_unit KG unit descriptor
|
||
kg_reg KG register list
|
||
kg_mod KG modifiers table
|
||
kg_debug KG debug names table
|
||
kg_dev KG device descriptor
|
||
*/
|
||
|
||
#define IOLN_KG 006
|
||
|
||
static DIB kg_dib = {
|
||
IOBA_AUTO,
|
||
(IOLN_KG + 2) * KG_UNITS,
|
||
&kg_rd,
|
||
&kg_wr,
|
||
0, 0, 0, { NULL }, IOLN_KG+2
|
||
};
|
||
|
||
static UNIT kg_unit[] = {
|
||
{ UDATA (NULL, 0, 0) },
|
||
{ UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) },
|
||
{ UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) },
|
||
{ UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) },
|
||
{ UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) },
|
||
{ UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) },
|
||
{ UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) },
|
||
{ UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) },
|
||
};
|
||
|
||
static const REG kg_reg[] = {
|
||
{ ORDATA (SR0, kg_unit[0].SR, 16) },
|
||
{ ORDATA (SR1, kg_unit[1].SR, 16) },
|
||
{ ORDATA (SR2, kg_unit[2].SR, 16) },
|
||
{ ORDATA (SR3, kg_unit[3].SR, 16) },
|
||
{ ORDATA (SR4, kg_unit[4].SR, 16) },
|
||
{ ORDATA (SR5, kg_unit[5].SR, 16) },
|
||
{ ORDATA (SR6, kg_unit[6].SR, 16) },
|
||
{ ORDATA (SR7, kg_unit[7].SR, 16) },
|
||
{ ORDATA (BCC0, kg_unit[0].BCC, 16) },
|
||
{ ORDATA (BCC1, kg_unit[1].BCC, 16) },
|
||
{ ORDATA (BCC2, kg_unit[2].BCC, 16) },
|
||
{ ORDATA (BCC3, kg_unit[3].BCC, 16) },
|
||
{ ORDATA (BCC4, kg_unit[4].BCC, 16) },
|
||
{ ORDATA (BCC5, kg_unit[5].BCC, 16) },
|
||
{ ORDATA (BCC6, kg_unit[6].BCC, 16) },
|
||
{ ORDATA (BCC7, kg_unit[7].BCC, 16) },
|
||
{ ORDATA (DR0, kg_unit[0].DR, 16) },
|
||
{ ORDATA (DR1, kg_unit[1].DR, 16) },
|
||
{ ORDATA (DR2, kg_unit[2].DR, 16) },
|
||
{ ORDATA (DR3, kg_unit[3].DR, 16) },
|
||
{ ORDATA (DR4, kg_unit[4].DR, 16) },
|
||
{ ORDATA (DR5, kg_unit[5].DR, 16) },
|
||
{ ORDATA (DR6, kg_unit[6].DR, 16) },
|
||
{ ORDATA (DR7, kg_unit[7].DR, 16) },
|
||
{ ORDATA (PULSCNT0, kg_unit[0].PULSCNT, 16) },
|
||
{ ORDATA (PULSCNT1, kg_unit[1].PULSCNT, 16) },
|
||
{ ORDATA (PULSCNT2, kg_unit[2].PULSCNT, 16) },
|
||
{ ORDATA (PULSCNT3, kg_unit[3].PULSCNT, 16) },
|
||
{ ORDATA (PULSCNT4, kg_unit[4].PULSCNT, 16) },
|
||
{ ORDATA (PULSCNT5, kg_unit[5].PULSCNT, 16) },
|
||
{ ORDATA (PULSCNT6, kg_unit[6].PULSCNT, 16) },
|
||
{ ORDATA (PULSCNT7, kg_unit[7].PULSCNT, 16) },
|
||
{ ORDATA (DEVADDR, kg_dib.ba, 32), REG_HRO },
|
||
{ NULL }
|
||
};
|
||
|
||
static const MTAB kg_mod[] = {
|
||
{ MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_addr, NULL },
|
||
{ MTAB_XTD|MTAB_VDV, 0, NULL, "UNITS=0..8", &set_units, NULL, NULL },
|
||
{ 0 }
|
||
};
|
||
|
||
#define DBG_REG (01)
|
||
#define DBG_POLY (02)
|
||
#define DBG_CYCLE (04)
|
||
|
||
static const DEBTAB kg_debug[] = {
|
||
{"REG", DBG_REG},
|
||
{"POLY", DBG_POLY},
|
||
{"CYCLE", DBG_CYCLE},
|
||
{0},
|
||
};
|
||
|
||
DEVICE kg_dev = {
|
||
"KG", (UNIT *) &kg_unit, (REG *) kg_reg, (MTAB *) kg_mod,
|
||
KG_UNITS, 8, 16, 2, 8, 16,
|
||
NULL, /* examine */
|
||
NULL, /* deposit */
|
||
&kg_reset, /* reset */
|
||
NULL, /* boot */
|
||
NULL, /* attach */
|
||
NULL, /* detach */
|
||
&kg_dib,
|
||
DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG,
|
||
0, /* debug control */
|
||
(DEBTAB *) &kg_debug, /* debug flags */
|
||
NULL, /* memory size chage */
|
||
NULL /* logical name */
|
||
};
|
||
/* KG I/O address routines */
|
||
|
||
static t_stat kg_rd (int32 *data, int32 PA, int32 access)
|
||
{
|
||
int unit = (PA >> 3) & 07;
|
||
|
||
if ((unit >= KG_UNITS) || (kg_unit[unit].flags & UNIT_DIS))
|
||
return (SCPE_NXM);
|
||
switch ((PA >> 1) & 03) {
|
||
|
||
case 00: /* SR */
|
||
if (DEBUG_PRI(kg_dev, DBG_REG))
|
||
fprintf (sim_deb, ">>KG%d: rd SR %06o, PC %06o\n",
|
||
unit, kg_unit[unit].SR, PC);
|
||
*data = kg_unit[unit].SR & KG_SR_RDMASK;
|
||
break;
|
||
|
||
case 01: /* BCC */
|
||
if (DEBUG_PRI(kg_dev, DBG_REG))
|
||
fprintf (sim_deb, ">>KG%d rd BCC %06o, PC %06o\n",
|
||
unit, kg_unit[unit].BCC, PC);
|
||
*data = kg_unit[unit].BCC & DMASK;
|
||
break;
|
||
|
||
case 02: /* DR */
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
return (SCPE_OK);
|
||
}
|
||
|
||
static t_stat kg_wr (int32 data, int32 PA, int32 access)
|
||
{
|
||
int setup;
|
||
int unit = (PA >> 3) & 07;
|
||
|
||
if ((unit >= KG_UNITS) || (kg_unit[unit].flags & UNIT_DIS))
|
||
return (SCPE_NXM);
|
||
switch ((PA >> 1) & 03) {
|
||
|
||
case 00: /* SR */
|
||
if (access == WRITEB)
|
||
data = (PA & 1) ?
|
||
(kg_unit[unit].SR & 0377) | (data << 8) :
|
||
(kg_unit[unit].SR & ~0377) | data;
|
||
if (DEBUG_PRI(kg_dev, DBG_REG))
|
||
fprintf (sim_deb, ">>KG%d: wr SR %06o, PC %06o\n",
|
||
unit, data, PC);
|
||
if (data & KGSR_M_CLR) {
|
||
kg_unit[unit].PULSCNT = 0; /* not sure about this */
|
||
kg_unit[unit].BCC = 0;
|
||
kg_unit[unit].SR |= KGSR_M_DONE;
|
||
}
|
||
setup = (kg_unit[unit].SR & 017) ^ (data & 017);
|
||
kg_unit[unit].SR = (kg_unit[unit].SR & ~KG_SR_WRMASK) |
|
||
(data & KG_SR_WRMASK);
|
||
/* if low 4b changed, reset C1 & C2 */
|
||
if (setup) {
|
||
kg_unit[unit].PULSCNT = 0;
|
||
if (DEBUG_PRI(kg_dev, DBG_POLY))
|
||
fprintf (sim_deb, ">>KG%d poly %s %d\n",
|
||
unit, config[data & 017].name, config[data & 017].pulses);
|
||
}
|
||
if (data & KGSR_M_SEN)
|
||
break;
|
||
if (data & KGSR_M_STEP) {
|
||
do_poly (unit, TRUE);
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case 01: /* BCC */
|
||
break; /* ignored */
|
||
|
||
case 02: /* DR */
|
||
if (access == WRITEB)
|
||
data = (PA & 1) ?
|
||
(kg_unit[unit].DR & 0377) | (data << 8) :
|
||
(kg_unit[unit].DR & ~0377) | data;
|
||
kg_unit[unit].DR = data & DMASK;
|
||
if (DEBUG_PRI(kg_dev, DBG_REG))
|
||
fprintf (sim_deb, ">>KG%d: wr DR %06o, data %06o, PC %06o\n",
|
||
unit, kg_unit[unit].DR, data, PC);
|
||
kg_unit[unit].SR &= ~KGSR_M_DONE;
|
||
|
||
/* In a typical device, this is normally where we would use sim_activate()
|
||
to initiate an I/O to be completed later. The KG is a little
|
||
different. When it was first introduced, it's computation operation
|
||
completed before another instruction could execute (on early PDP-11s),
|
||
and software often took "advantage" of this fact by not bothering
|
||
to check the status of the DONE bit. In reality, the execution
|
||
time of the polynomial is dependent upon the width of the poly; if
|
||
8 bits 1us, if 16 bits, 2us. Since known existing software will
|
||
break if we actually defer the computation, it is performed immediately
|
||
instead. However this could easily be made into a run-time option,
|
||
if there were software to validate correct operation. */
|
||
|
||
if (kg_unit[unit].SR & KGSR_M_SEN)
|
||
do_poly (unit, FALSE);
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
return (SCPE_OK);
|
||
}
|
||
|
||
/* KG reset */
|
||
|
||
static t_stat kg_reset (DEVICE *dptr)
|
||
{
|
||
int i;
|
||
|
||
if (DEBUG_PRI(kg_dev, DBG_REG))
|
||
fprintf (sim_deb, ">>KG: reset PC %06o\n", PC);
|
||
for (i = 0; i < KG_UNITS; i++) {
|
||
kg_unit[i].SR = KGSR_M_DONE;
|
||
kg_unit[i].BCC = 0;
|
||
kg_unit[i].PULSCNT = 0;
|
||
}
|
||
return auto_config(0, 0);
|
||
}
|
||
|
||
static void cycleOneBit (int unit)
|
||
{
|
||
int quo;
|
||
|
||
if (DEBUG_PRI(kg_dev, DBG_CYCLE))
|
||
fprintf (sim_deb, ">>KG%d: cycle s BCC %06o DR %06o\n",
|
||
unit, kg_unit[unit].BCC, kg_unit[unit].DR);
|
||
if (kg_unit[unit].SR & KGSR_M_DONE)
|
||
return;
|
||
if ((kg_unit[unit].SR & KG_SR_POLYMASK) == 0)
|
||
kg_unit[unit].BCC = (kg_unit[unit].BCC & 077) |
|
||
((kg_unit[unit].BCC >> 2) & 07700);
|
||
kg_unit[unit].SR &= ~KGSR_M_QUO;
|
||
quo = (kg_unit[unit].BCC & 01) ^ (kg_unit[unit].DR & 01);
|
||
kg_unit[unit].BCC = (kg_unit[unit].BCC & ~01) | quo;
|
||
if (kg_unit[unit].SR & KGSR_M_LRC)
|
||
kg_unit[unit].BCC = (kg_unit[unit].SR & KGSR_M_16) ?
|
||
ROR(1, kg_unit[unit].BCC) :
|
||
RORB(1, kg_unit[unit].BCC);
|
||
else
|
||
kg_unit[unit].BCC = (kg_unit[unit].BCC & 01) ?
|
||
(kg_unit[unit].BCC >> 1) ^ config[kg_unit[unit].SR & 07].poly :
|
||
kg_unit[unit].BCC >> 1;
|
||
kg_unit[unit].DR >>= 1;
|
||
kg_unit[unit].SR |= quo << KGSR_V_QUO;
|
||
if ((kg_unit[unit].SR & KG_SR_POLYMASK) == 0)
|
||
kg_unit[unit].BCC = (kg_unit[unit].BCC & 077) |
|
||
((kg_unit[unit].BCC & 07700) << 2);
|
||
kg_unit[unit].PULSCNT++;
|
||
if (kg_unit[unit].PULSCNT >= config[kg_unit[unit].SR & 017].pulses)
|
||
kg_unit[unit].SR |= KGSR_M_DONE;
|
||
if (DEBUG_PRI(kg_dev, DBG_CYCLE))
|
||
fprintf (sim_deb, ">>KG%d: cycle e BCC %06o DR %06o\n",
|
||
unit, kg_unit[unit].BCC, kg_unit[unit].DR);
|
||
}
|
||
|
||
static void do_poly (int unit, t_bool step)
|
||
{
|
||
if (kg_unit[unit].SR & KGSR_M_DONE)
|
||
return;
|
||
if (step)
|
||
cycleOneBit (unit);
|
||
else {
|
||
while (!(kg_unit[unit].SR & KGSR_M_DONE))
|
||
cycleOneBit (unit);
|
||
}
|
||
}
|
||
|
||
static t_stat set_units (UNIT *u, int32 val, char *s, void *desc)
|
||
{
|
||
int32 i, units;
|
||
t_stat stat;
|
||
|
||
if (s == NULL)
|
||
return (SCPE_ARG);
|
||
units = get_uint (s, 10, KG_UNITS, &stat);
|
||
if (stat != SCPE_OK)
|
||
return (stat);
|
||
for (i = 0; i < KG_UNITS; i++) {
|
||
if (i < units)
|
||
kg_unit[i].flags &= ~UNIT_DIS;
|
||
else
|
||
kg_unit[i].flags |= UNIT_DIS;
|
||
}
|
||
kg_dev.numunits = units;
|
||
return (SCPE_OK);
|
||
}
|