The makefile now works for Linux and most Unix's. However, for Solaris and MacOS, you must first export the OSTYPE environment variable: > export OSTYPE > make Otherwise, you will get build errors. 1. New Features 1.1 3.8-0 1.1.1 SCP and Libraries - BREAK, NOBREAK, and SHOW BREAK with no argument will set, clear, and show (respectively) a breakpoint at the current PC. 1.1.2 GRI - Added support for the GRI-99 processor. 1.1.3 HP2100 - Added support for the BACI terminal interface. - Added support for RTE OS/VMA/EMA, SIGNAL, VIS firmware extensions. 1.1.4 Nova - Added support for 64KW memory (implemented in third-party CPU's). 1.1.5 PDP-11 - Added support for DC11, RC11, KE11A, KG11A. - Added modem control support for DL11. - Added ASCII character support for all 8b devices. 1.2 3.8-1 1.2.1 SCP and libraries - Added capability to set line connection order for terminal multiplexers. 1.2.2 HP2100 - Added support for 12620A/12936A privileged interrupt fence. - Added support for 12792C eight-channel asynchronous multiplexer. 1.3 3.8-2 1.3.1 1401 - Added "no rewind" option to magtape boot. 1.3.2 PDP-11 - Added RD32 support to RQ - Added debug support to RL 1.3.3 PDP-8 - Added FPP support (many thanks to Rick Murphy for debugging the code) 2. Bugs Fixed Please see the revision history on http://simh.trailing-edge.com or in the source module sim_rev.h.
478 lines
18 KiB
C
478 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 FILE *sim_deb;
|
||
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
|
||
*/
|
||
|
||
static DIB kg_dib = {
|
||
IOBA_KG,
|
||
(IOLN_KG + 2) * KG_UNITS,
|
||
&kg_rd,
|
||
&kg_wr,
|
||
0, 0, 0, { NULL }
|
||
};
|
||
|
||
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 (SCPE_OK);
|
||
}
|
||
|
||
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);
|
||
}
|