blob: a8b7feaae895df5dd1abf0a2caf18e2871c10d9f [file] [log] [blame] [raw]
/* hp3000_scmb.c: HP 3000 30033A Selector Channel Maintenance Board simulator
Copyright (c) 2016, J. David Bryan
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 THE
AUTHOR 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.
SCMB1,SCMB2 HP 30033A Selector Channel Maintenance Board
12-Sep-16 JDB Changed DIB register macro usage from SRDATA to DIB_REG
11-Jun-16 JDB Bit mask constants are now unsigned
13-May-16 JDB Modified for revised SCP API function parameter types
21-Mar-16 JDB Changed uint16 types to HP_WORD
21-Sep-15 JDB First release version
27-Jan-15 JDB Passes the selector channel diagnostic (D429A)
12-Jan-15 JDB Passes the SCMB diagnostic (D429A)
07-Jan-15 JDB Created
References:
- HP 3000 Series II/III System Reference Manual
(30000-90020, July 1978)
- HP 3000 Series II Computer System System Service Manual
(30000-90018, March 1977)
- Stand-Alone HP 30030B/C Selector Channel Diagnostic
(30030-90011, July 1978)
- HP 3000 Series III Engineering Diagrams Set
(30000-90141, April 1980)
The HP 30033A Selector Channel Maintenance Board provides the circuitry
necessary to test the I/O bus signals driven and received by the selector and
multiplexer channels. Used with the Stand-Alone Selector Channel and
Multiplexer Channel diagnostics, the SCMB is used to verify that the correct
bus signals are driven in response to each of the programmed I/O orders, and
that the channel responds correctly to the signals returned to it. The SCMB
functions as a programmable interface that can log incoming signals and drive
the outgoing signals, as well as simulate a number of interface hardware
faults. Two SCMBs are required to test the multiplexer channel fully, so two
SCMBs are provided; they are named "SCMB" (or "SCMB1") and "SCMB2".
In hardware, the SCMB is connected either to the selector channel or
multiplexer channel buses, and jumper W1 must be set to the SC or MX
position, depending on the desired diagnostic test. The device number and
the service request number jumpers may be configured to use any unassigned
numbers.
In simulation, a SET SCMB SC configures the interface for the selector
channel diagnostic, and a SET SCMB MX configures the interface for the
multiplexer diagnostic. If the selector channel diagnostic is run with SET
SCMB MX, the SCMB itself is tested. The multiplexer diagnostic requires two
SCMB cards, so SET SCMB1 MX and SET SCMB2 MX are required.
The SCMB responds to direct and programmed I/O instructions, as follows:
Control Word Format (CIO):
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| M | R | J | V | A | S | load | H | N | T | C | L | counter |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
M = master reset
R = reset interrupt
J = set jump met condition
V = set device end condition
A = inhibit channel acknowledge
S = inhibit service request
H = enable high speed service request
N = enable special device number
T = terminate on terminal count
C = terminate on compare failure
L = enable device end/clear interface (0/1) on terminate
Load:
00 = load the IOAW into the control word
01 = load the IOCW into the buffer
10 = load the IOAW into the buffer
11 = load the IOCW and then the IOAW into the buffer
Counter:
000 = counter is disabled
001 = count READNEXTWD signals
010 = count PREADSTB signals
011 = count TOGGLEINXFER signals
100 = count PWRITESTB signals
101 = count TOGGLEOUTXFER signals
110 = count EOT signals
111 = count CHANSO signals
The Load field defines how programmed I/O orders will affect the control word
and buffer. The Counter field defines which signal occurrences, if any, are
counted. If value 000 is selected, the counter does not operate, and the
buffer value does not change.
Control Word Format (SIO Control):
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| - | 1 0 0 | buffer value | word 1
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| M | R | J | V | A | S | load | H | N | T | C | L | counter | word 2
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
If the current control word specifies a Load field value of 01, word 1 is
loaded into the counter/buffer register. Otherwise, word 2 is loaded into
the control word register or counter/buffer register, depending on the Load
field value.
If the A bit (inhibit channel acknowledge) is set, CHANACK will be issued for
this Control order, but all future orders will not be acknowledged.
Similarly, if the S bit (inhibit service request) is set, a CHANSR will be
issued for this order but not for future orders.
Status Word Format (TIO and SIO Status):
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| S | D | R | A | X | N | V | E | C | T | I | O | L | 0 0 0 |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
S = SIO OK
D = direct I/O OK (always 1)
R = interrupt requested
A = interrupt active
X = transfer error was asserted
N = SIO enabled is asserted
V = device end was asserted
E = end of transfer was asserted
C = an end-on-miscompare occurred
T = an end-on-terminal-count occurred
I = an input transfer is in progress
O = an output transfer is in progress
L = a clear interface has asserted to abort the I/O program
Note that the Series II Service Manual and the Series III CE Handbook list
the wrong assignments for status bits 8-11. The Selector Channel Diagnostic
manual has the correct assignments.
Output Data Word Format (WIO and SIO Write):
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| new counter/buffer register value |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
If the control word C bit (terminate on compare failure) is set, the current
counter/buffer value is compared to the new value. If they are not equal,
the C bit (an end-on-miscompare occurred) is set in the status word.
The new value is stored in the counter/buffer register only if the control
word Counter field value is less than 100 (i.e., it is not set to count
writes). Otherwise, the value is ignored, but the write is counted.
If DEVEND is asserted for a selector channel SIO Write, the write is ignored,
and the PWRITESTB signal is not counted.
Input Data Word Format (RIO and SIO Read):
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| current counter/buffer register value |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
If DEVEND is asserted for a selector channel SIO Read, the read is ignored,
and the PREADSTB signal is not counted.
*/
#include "hp3000_defs.h"
#include "hp3000_io.h"
/* Program constants */
#define SERVICE_DELAY uS (5) /* 5 microsecond delay for non-high-speed service request */
/* Unit flags */
#define UNIT_W1_SHIFT (UNIT_V_UF + 0) /* jumper W1 */
#define UNIT_W1_SEL (1u << UNIT_W1_SHIFT)
#define MPX_BUS(card) ((scmb_unit [card].flags & UNIT_W1_SEL) == 0)
#define SEL_BUS(card) ((scmb_unit [card].flags & UNIT_W1_SEL) != 0)
/* Debug flags */
#define DEB_CSRW (1u << 0) /* trace commands received and status returned */
#define DEB_XFER (1u << 1) /* trace channel data reads and writes */
#define DEB_SERV (1u << 2) /* trace unit service scheduling calls */
#define DEB_IOB (1u << 3) /* trace I/O bus signals and data words */
/* Control word.
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| M | R | J | V | A | S | load | H | N | T | C | L | counter |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/
#define CN_MR 0100000u /* M = master reset */
#define CN_IRQ_RESET 0040000u /* R = interrupt reset */
#define CN_JMPMET 0020000u /* J = set jump met */
#define CN_DEVEND 0010000u /* V = set device end */
#define CN_NOACK 0004000u /* A = inhibit channel acknowledge */
#define CN_NOSR 0002000u /* S = inhibit service request */
#define CN_LOAD_MASK 0001400u /* load operation mask */
#define CN_HSREQ 0000200u /* H = high speed service request */
#define CN_DEVNO 0000100u /* N = special device number */
#define CN_TERM_COUNT 0000040u /* T = terminate on count */
#define CN_TERM_COMP 0000020u /* C = terminate on miscompare */
#define CN_CLEAR_IF 0000010u /* L = clear interface */
#define CN_CNTR_MASK 0000007u /* counter operation mask */
#define CN_LOAD_SHIFT 8 /* load operation alignment shift */
#define CN_CNTR_SHIFT 0 /* counter operation alignment shift */
#define CN_LOAD(c) ((LOAD_OP) (((c) & CN_LOAD_MASK) >> CN_LOAD_SHIFT))
#define CN_CNTR(c) ((CNTR_OP) (((c) & CN_CNTR_MASK) >> CN_CNTR_SHIFT))
typedef enum { /* load operations */
load_cntl_IOAW = 0,
load_bufr_IOCW = 1,
load_bufr_IOAW = 2,
load_bufr_both = 3
} LOAD_OP;
static const char *const load_names [4] = { /* indexed by LOAD_OP */
"load control IOAW", /* 00 = load IOAW into control word */
"load buffer IOCW", /* 01 = load IOCW into buffer */
"load buffer IOAW", /* 10 = load IOAW into buffer */
"load buffer IOCW/AW" /* 11 = load IOCW and IOAW into buffer */
};
typedef enum { /* counter operations */
count_nothing = 0,
count_READNEXTWD = 1,
count_PREADSTB = 2,
count_TOGGLEINXFER = 3,
count_PWRITESTB = 4,
count_TOGGLEOUTXFER = 5,
count_EOT = 6,
count_CHANSO = 7
} CNTR_OP;
static const char *const count_names [8] = { /* indexed by CNTR_OP */
"count nothing", /* 000 = counter is disabled */
"count READNEXTWD", /* 001 = count READNEXTWD */
"count PREADSTB", /* 010 = count PREADSTB */
"count TOGGLEINXFER", /* 011 = count TOGGLEINXFER */
"count PWRITESTB", /* 100 = count PWRITESTB */
"count TOGGLEOUTXFER", /* 101 = count TOGGLEOUTXFER */
"count EOT", /* 110 = count EOT */
"count CHANSO" /* 111 = count CHANSO */
};
static const BITSET_NAME control_names [] = { /* Control word names */
"master reset", /* bit 0 */
"reset interrupt", /* bit 1 */
"set JMPMET", /* bit 2 */
"set DEVEND", /* bit 3 */
"inhibit CHANACK", /* bit 4 */
"inhibit SR", /* bit 5 */
NULL, /* bit 6 */
NULL, /* bit 7 */
"high speed", /* bit 8 */
"send DEVNO", /* bit 9 */
"end on count", /* bit 10 */
"end on miscompare", /* bit 11 */
"\1end with clear interface\0end with device end" /* bit 12 */
};
static const BITSET_FORMAT control_format = /* names, offset, direction, alternates, bar */
{ FMT_INIT (control_names, 3, msb_first, has_alt, append_bar) };
/* Status word.
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| S | D | R | A | X | N | V | E | C | T | I | O | L | 0 0 0 |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
NOTE: The Series II Service Manual and the Series III CE Handbook list the
wrong assignments for status bits 8-11. The Selector Channel Diagnostic
manual has the correct assignments.
*/
#define ST_SIO_OK 0100000u /* S = SIO OK to use */
#define ST_DIO_OK 0040000u /* D = direct I/O OK to use (always 1) */
#define ST_INTREQ 0020000u /* R = interrupt requested */
#define ST_INTACT 0010000u /* A = interrupt active */
#define ST_XFERERR 0004000u /* X = transfer error is asserted */
#define ST_SIOENABLED 0002000u /* N = SIO enabled is asserted */
#define ST_DEVEND 0001000u /* V = device end is asserted */
#define ST_EOT 0000400u /* E = end of transfer is asserted */
#define ST_END_MISCMP 0000200u /* C = end on miscompare occurred */
#define ST_END_COUNT 0000100u /* T = end on terminal count occurred */
#define ST_INXFER 0000040u /* I = input transfer is asserted */
#define ST_OUTXFER 0000020u /* O = output transfer is asserted */
#define ST_CLEAR_IF 0000010u /* L = clear interface is asserted */
#define END_CONDITION (ST_END_MISCMP | ST_END_COUNT)
static const BITSET_NAME status_names [] = { /* Status word names */
"SIO OK", /* bit 0 */
"DIO OK", /* bit 1 */
"int request", /* bit 2 */
"int active", /* bit 3 */
"transfer error", /* bit 4 */
"SIO enabled", /* bit 5 */
"device end", /* bit 6 */
"end of transfer", /* bit 7 */
"miscompare", /* bit 8 */
"terminal count", /* bit 9 */
"input transfer", /* bit 10 */
"output transfer", /* bit 11 */
"clear interface" /* bit 12 */
};
static const BITSET_FORMAT status_format = /* names, offset, direction, alternates, bar */
{ FMT_INIT (status_names, 3, msb_first, no_alt, no_bar) };
/* SCMB state */
typedef enum {
card1, /* first card ID */
card2 /* second card ID */
} CARD_ID;
typedef struct {
HP_WORD control_word; /* control word register */
HP_WORD status_word; /* status word register */
HP_WORD counter; /* counter/buffer register */
HP_WORD flags; /* status flags */
uint32 saved_srn; /* saved SR number */
FLIP_FLOP sio_busy; /* SIO busy flip-flop */
FLIP_FLOP channel_sr; /* channel service request flip-flop */
FLIP_FLOP device_sr; /* device service request flip-flop */
FLIP_FLOP input_xfer; /* input transfer flip-flop */
FLIP_FLOP output_xfer; /* output transfer flip-flop */
FLIP_FLOP jump_met; /* jump met flip-flop */
FLIP_FLOP device_end; /* device end flip-flop */
FLIP_FLOP stop_transfer; /* stop transfer flip-flop */
} SCMB_STATE;
static SCMB_STATE scmb [2]; /* per-card state variables */
/* SCMB local SCP support routines */
static CNTLR_INTRF scmb_interface;
static t_stat scmb_service (UNIT *uptr);
static t_stat scmb_reset (DEVICE *dptr);
static t_stat scmb_set_bus (UNIT *uptr, int32 value, CONST char *cptr, void *desc);
/* SCMB local utility routines */
static void sio_reset (CARD_ID card);
static void clear_logic (CARD_ID card);
static void increment_counter (CARD_ID card);
/* SCMB SCP interface data structures.
Implementation notes:
1. The DIB, UNIT, and DEVICE structures for the two cards must be arrayed so
that access via card number is possible.
2. The SCMB interfaces are disabled by default, as they are only used during
diagnostic testing.
*/
/* Device information blocks */
static DIB scmb_dib [] = {
{ &scmb_interface, /* device interface */
65, /* device number */
0, /* service request number */
10, /* interrupt priority */
INTMASK_UNUSED, /* interrupt mask */
card1 /* card index for card 1 */
},
{ &scmb_interface, /* device interface */
66, /* device number */
1, /* service request number */
11, /* interrupt priority */
INTMASK_UNUSED, /* interrupt mask */
card2 /* card index for card 2 */
}
};
/* Unit list */
static UNIT scmb_unit [] = {
{ UDATA (&scmb_service, 0, 0), SERVICE_DELAY }, /* unit for card 1 */
{ UDATA (&scmb_service, 0, 0), SERVICE_DELAY } /* unit for card 2 */
};
/* Register lists */
static REG scmb1_reg [] = {
/* Macro Name Location Width Offset Flags */
/* ------ ------ -------------------------- ----- ------ ----------------- */
{ ORDATA (CNTL, scmb [card1].control_word, 16), REG_FIT },
{ ORDATA (STAT, scmb [card1].status_word, 16), REG_FIT },
{ ORDATA (CNTR, scmb [card1].counter, 16), REG_FIT },
{ ORDATA (SRSAVE, scmb [card1].saved_srn, 8), REG_HRO },
{ FLDATA (SIOBSY, scmb [card1].sio_busy, 0) },
{ FLDATA (CHANSR, scmb [card1].channel_sr, 0) },
{ FLDATA (DEVSR, scmb [card1].device_sr, 0) },
{ FLDATA (INXFR, scmb [card1].input_xfer, 0) },
{ FLDATA (OUTXFR, scmb [card1].output_xfer, 0) },
{ FLDATA (JMPMET, scmb [card1].jump_met, 0) },
{ FLDATA (XFRERR, scmb [card1].flags, 11) },
{ FLDATA (EOT, scmb [card1].flags, 8) },
{ FLDATA (TRMCNT, scmb [card1].flags, 6) },
{ FLDATA (MISCMP, scmb [card1].flags, 7) },
{ FLDATA (DEVEND, scmb [card1].device_end, 0) },
{ FLDATA (STOP, scmb [card1].stop_transfer, 0) },
DIB_REGS (scmb_dib [card1]),
{ NULL }
};
static REG scmb2_reg [] = {
/* Macro Name Location Width Offset Flags */
/* ------ ------ -------------------------- ----- ------ ----------------- */
{ ORDATA (CNTL, scmb [card2].control_word, 16), REG_FIT },
{ ORDATA (STAT, scmb [card2].status_word, 16), REG_FIT },
{ ORDATA (CNTR, scmb [card2].counter, 16), REG_FIT },
{ ORDATA (SRSAVE, scmb [card2].saved_srn, 8), REG_HRO },
{ FLDATA (SIOBSY, scmb [card2].sio_busy, 0) },
{ FLDATA (CHANSR, scmb [card2].channel_sr, 0) },
{ FLDATA (DEVSR, scmb [card2].device_sr, 0) },
{ FLDATA (INXFR, scmb [card2].input_xfer, 0) },
{ FLDATA (OUTXFR, scmb [card2].output_xfer, 0) },
{ FLDATA (JMPMET, scmb [card2].jump_met, 0) },
{ FLDATA (XFRERR, scmb [card2].flags, 11) },
{ FLDATA (EOT, scmb [card2].flags, 8) },
{ FLDATA (TRMCNT, scmb [card2].flags, 6) },
{ FLDATA (MISCMP, scmb [card2].flags, 7) },
{ FLDATA (DEVEND, scmb [card2].device_end, 0) },
{ FLDATA (STOP, scmb [card2].stop_transfer, 0) },
DIB_REGS (scmb_dib [card2]),
{ NULL }
};
/* Modifier lists */
static MTAB scmb1_mod [] = {
/* Mask Value Match Value Print String Match String Validation Display Descriptor */
/* ----------- ----------- ------------ ------------ ------------- ------- ---------- */
{ UNIT_W1_SEL, UNIT_W1_SEL, "W1=SC", "SC", &scmb_set_bus, NULL, NULL },
{ UNIT_W1_SEL, 0, "W1=MX", "MX", &scmb_set_bus, NULL, NULL },
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* ----------- ---------- ------------ ------------ ----------- ------------ ------------------------- */
{ MTAB_XDV, VAL_DEVNO, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &scmb_dib [card1] },
{ MTAB_XDV, VAL_INTPRI, "INTPRI", "INTPRI", &hp_set_dib, &hp_show_dib, (void *) &scmb_dib [card1] },
{ MTAB_XDV, VAL_SRNO, "SRNO", "SRNO", &hp_set_dib, &hp_show_dib, (void *) &scmb_dib [card1] },
{ 0 }
};
static MTAB scmb2_mod [] = {
/* Mask Value Match Value Print String Match String Validation Display Descriptor */
/* ----------- ----------- ------------ ------------ ------------- ------- ---------- */
{ UNIT_W1_SEL, UNIT_W1_SEL, "W1=SC", "SC", &scmb_set_bus, NULL, NULL },
{ UNIT_W1_SEL, 0, "W1=MX", "MX", &scmb_set_bus, NULL, NULL },
/* Entry Flags Value Print String Match String Validation Display Descriptor */
/* ----------- ---------- ------------ ------------ ----------- ------------ ------------------------- */
{ MTAB_XDV, VAL_DEVNO, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &scmb_dib [card2] },
{ MTAB_XDV, VAL_INTPRI, "INTPRI", "INTPRI", &hp_set_dib, &hp_show_dib, (void *) &scmb_dib [card2] },
{ MTAB_XDV, VAL_SRNO, "SRNO", "SRNO", &hp_set_dib, &hp_show_dib, (void *) &scmb_dib [card2] },
{ 0 }
};
/* Debugging trace list */
static DEBTAB scmb_deb [] = {
{ "CSRW", DEB_CSRW }, /* Interface control, status, read, and write actions */
{ "XFER", DEB_XFER }, /* Channel data reads and writes */
{ "SERV", DEB_SERV }, /* Unit service scheduling calls */
{ "IOBUS", DEB_IOB }, /* Interface I/O bus signals and data words */
{ NULL, 0 }
};
/* Device descriptors */
DEVICE scmb_dev [] = {
{ "SCMB", /* device name */
&scmb_unit [card1], /* unit array */
scmb1_reg, /* register array */
scmb1_mod, /* modifier array */
1, /* number of units */
8, /* address radix */
PA_WIDTH, /* address width */
1, /* address increment */
8, /* data radix */
DV_WIDTH, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
&scmb_reset, /* reset routine */
NULL, /* boot routine */
NULL, /* attach routine */
NULL, /* detach routine */
&scmb_dib [card1], /* device information block pointer */
DEV_DEBUG | DEV_DISABLE | DEV_DIS, /* device flags */
0, /* debug control flags */
scmb_deb, /* debug flag name array */
NULL, /* memory size change routine */
NULL /* logical device name */
},
{ "SCMB2", /* device name */
&scmb_unit [card2], /* unit array */
scmb2_reg, /* register array */
scmb2_mod, /* modifier array */
1, /* number of units */
8, /* address radix */
PA_WIDTH, /* address width */
1, /* address increment */
8, /* data radix */
DV_WIDTH, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
&scmb_reset, /* reset routine */
NULL, /* boot routine */
NULL, /* attach routine */
NULL, /* detach routine */
&scmb_dib [card2], /* device information block pointer */
DEV_DEBUG | DEV_DISABLE | DEV_DIS, /* device flags */
0, /* debug control flags */
scmb_deb, /* debug flag name array */
NULL, /* memory size change routine */
NULL /* logical device name */
}
};
/* SCMB local SCP support routines */
/* Selector Channel Maintenance Board interface.
The interface is installed on the IOP bus and either the Multiplexer or
Selector Channel bus and receives direct and programmed I/O commands from the
IOP and channel, respectively. In simulation, the asserted signals on the
buses are represented as bits in the inbound_signals set. Each signal is
processed sequentially in numerical order, and a set of similar
outbound_signals is assembled and returned to the caller, simulating
assertion of the corresponding backplane signals.
Jumper W1 on the interface PCA must be set to match the bus (multiplexer or
selector) to which the SCMB is connected. The multiplexer and selector
channels have slightly different signal requirements, and this jumper
configures the logic to account for the difference.
The diagnostics use direct I/O to configure the SCMB and then use programmed
I/O to test the channel's interaction with the interface.
Implementation notes:
1. In hardware, asserting DSTARTIO sets the Channel SR flip-flop, but the
output is masked off unless the SCMB is connected to the multiplexer
channel (the selector channel does not use the Channel SR flip-flop).
In simulation, setting the flip-flop is inhibited.
2. In hardware, asserting DEVEND to the selector channel inhibits
generation of the PREADSTB and PWRITESTB signals. In simulation, DEVEND
is returned in response to a PREADSTB or PWRITESTB if the Device End
flip-flop is set. As the strobes may cause the counter to increment,
counting is inhibited in simulation if the Device End flip-flop is set
and the SCMB is on the selector channel bus.
3. In hardware, the SCMB does not use ACKSR to reset the Device SR
flip-flop. Instead, the flip-flop is preset by PCMD1 or PCONTSTB or if
the Input Transfer or Output Transfer flip-flop is set; it is clocked to
zero by the leading edge of CHANSO. In simulation, the Device SR
flip-flop is cleared on entry by CHANSO if both the Input and Output
Transfer flip-flops are clear. This provides the same action as the
asynchronous set overriding the synchronous clear in hardware.
4. If channel acknowledgement is inhibited, the CHANACK signal is not
returned to the selector channel. This causes a CHANSO timeout in the
channel. Similarly, if channel service requests are inhibited, CHANSR
will not be returned to the selector channel, which will cause a timeout
and channel abort.
5. In hardware, clearing the "enable high speed service request" bit in the
control word delays SR assertion for five microseconds after the device
SR flip-flop sets. In software, the SCMB unit service routine is
scheduled and the request signal is not returned from the interface; when
the delay expires, the service routine calls either "mpx_assert_SRn" or
"sel_assert_CHANSR" to request channel service.
However, if the "inhibit channel acknowledge" or "inhibit service
request" bit in the control word is also set, then scheduling of the
service routine is inhibited. Otherwise, SR would be asserted after the
channel had been aborted by the timeout.
6. In hardware, setting the "enable special device number" bit in the
control word causes the SCMB to gate bits 8-15 of the counter/buffer
register onto SR6-13 for the DSTARTIO signal only. This supplies the
selector channel with a configurable device number instead of that of the
SCMB. For all other operations, e.g., interrupts, the regular SCMB
device number is used.
In simulation, the device number is obtained from the DIB passed to the
"sel_assert_REQ" routine. If the special bit is set, the device number
is changed temporarily before calling "sel_assert_REQ" and then restored
afterward. This ensures that interrupts in particular are handled
correctly. (An alternate method of passing a secondary DIB containing
the special device number won't work, as the selector channel will use
the secondary DIB to request an interrupt, but the IOP will use the
standard DIB to respond to the interrupt.)
7. Receipt of a DRESETINT signal clears the interrupt request and active
flip-flops but does not cancel a request pending but not yet serviced by
the IOP. However, when the IOP does service the request by asserting
INTPOLLIN, the interface routine returns INTPOLLOUT, which will cancel
the request.
8. Although support for the "count_CHANSO" option is provided, none of the
diagnostics (SCMB, MPX, and SEL) test this option.
9. In simulation, we allow the device number to be changed during a
simulation stop. However, the SCMB may be spoofing the device number,
and it is this spoofed number that must be restored during the channel
initialization that follows resumption. This presents no problem to the
multiplexer channel, which asserts DEVNODB to the interface as part of
each I/O order execution. However, the selector channel requests the
device number once during the REQ assertion that starts the I/O program
and saves it internally for later use.
To accommodate changing device numbers while spoofing is enabled, the
selector channel simulator asserts DEVNODB to the interface during
initialization. The SCMB responds to the DEVNODB signal, as it supports
connection to the multiplexer channel. Devices that connect only to the
selector channel will not respond to DEVNODB, causing the initializer to
use the DIB field to obtain the device number.
*/
static SIGNALS_DATA scmb_interface (DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
{
const CARD_ID card = (CARD_ID) (dibptr->card_index); /* the ID number of the card */
LOAD_OP load_operation;
FLIP_FLOP assert_sr;
HP_WORD saved_devno;
INBOUND_SIGNAL signal;
INBOUND_SET working_set = inbound_signals;
HP_WORD outbound_value = 0;
OUTBOUND_SET outbound_signals = NO_SIGNALS;
dprintf (scmb_dev [card], DEB_IOB, "Received data %06o with signals %s\n",
inbound_value, fmt_bitset (inbound_signals, inbound_format));
if (inbound_signals & CHANSO /* the leading edge of CHANSO */
&& scmb [card].input_xfer == CLEAR /* clears the Device SR flip-flop */
&& scmb [card].output_xfer == CLEAR) /* if not overridden by the Q outputs */
scmb [card].device_sr = CLEAR; /* of the Input and Output Transfer flip-flops */
while (working_set) { /* while there are signals to process */
signal = IONEXTSIG (working_set); /* isolate the next signal */
switch (signal) { /* dispatch an I/O signal */
case DWRITESTB:
dprintf (scmb_dev [card], DEB_CSRW, "Counter/buffer value %06o set\n",
inbound_value);
scmb [card].counter = inbound_value; /* set the counter/buffer */
break;
case DREADSTB:
outbound_value = scmb [card].counter; /* return the counter/buffer value */
dprintf (scmb_dev [card], DEB_CSRW, "Counter/buffer value %06o returned\n",
outbound_value);
break;
case DCONTSTB:
dprintf (scmb_dev [card], DEB_CSRW, "Control is %s%s | %s\n",
fmt_bitset (inbound_value, control_format),
load_names [CN_LOAD (inbound_value)],
count_names [CN_CNTR (inbound_value)]);
scmb [card].control_word = inbound_value; /* save the new control word value */
if (scmb [card].control_word & CN_MR) /* if master reset is requested */
scmb_reset (&scmb_dev [card]); /* then perform an I/O reset */
if (scmb [card].control_word & CN_IRQ_RESET) /* if reset interrupt is requested */
dibptr->interrupt_request = CLEAR; /* then clear the interrupt request */
scmb [card].device_end = CLEAR; /* clear DEVEND and EOT status */
scmb [card].flags &= ~ST_EOT; /* in preparation for a new transfer */
break;
case DSTATSTB:
case PSTATSTB:
scmb [card].status_word = ST_DIO_OK | scmb [card].flags; /* copy the flags to the status word */
if (MPX_BUS (card) || sel_is_idle) { /* if we're on the MPX bus or the SEL is not busy */
scmb [card].status_word |= ST_SIOENABLED; /* then SIO is enabled */
if (scmb [card].sio_busy == CLEAR) /* if we're not running an SIO program */
scmb [card].status_word |= ST_SIO_OK; /* then report that the SCMB is available */
}
if (dibptr->interrupt_request == SET) /* reflect the interrupt request state */
scmb [card].status_word |= ST_INTREQ; /* in the status word */
if (dibptr->interrupt_active == SET) /* reflect the interrupt active state */
scmb [card].status_word |= ST_INTACT; /* in the status word */
if (scmb [card].device_end == SET) /* reflect the device end flip-flop state */
scmb [card].status_word |= ST_DEVEND; /* in the status word */
if (scmb [card].input_xfer == SET) /* reflect the input transfer flip-flop state */
scmb [card].status_word |= ST_INXFER; /* in the status word */
if (scmb [card].output_xfer == SET) /* reflect the output transfer flip-flop state */
scmb [card].status_word |= ST_OUTXFER; /* in the status word */
outbound_value = scmb [card].status_word; /* return the status word */
dprintf (scmb_dev [card], DEB_CSRW, "Status is %s\n",
fmt_bitset (outbound_value, status_format));
break;
case DSETINT:
case SETINT:
dibptr->interrupt_request = SET; /* set the interrupt request flip-flop */
outbound_signals |= INTREQ; /* and request the interrupt */
break;
case DRESETINT:
dibptr->interrupt_active = CLEAR; /* reset the interrupt active flip-flop */
break;
case INTPOLLIN:
if (dibptr->interrupt_request) { /* if a request is pending */
dibptr->interrupt_request = CLEAR; /* then clear it */
dibptr->interrupt_active = SET; /* and mark it now active */
outbound_signals = INTACK; /* acknowledge the interrupt */
outbound_value = dibptr->device_number; /* and return our device number */
}
else /* otherwise the request has been reset */
outbound_signals = INTPOLLOUT; /* so let the IOP know to cancel it */
break;
case DSTARTIO:
dprintf (scmb_dev [card], DEB_CSRW, "Channel program started\n");
scmb [card].sio_busy = SET; /* set the SIO busy flip-flop */
scmb [card].stop_transfer = CLEAR; /* and clear the stop transfer flip-flop */
sio_reset (card); /* clear in preparation for the new program */
if (MPX_BUS (card)) { /* if the card is configured for the multiplexer channel */
scmb [card].channel_sr = SET; /* then set the channel service request flip-flop */
mpx_assert_REQ (dibptr); /* and request the channel */
outbound_signals |= SRn;
}
else /* otherwise request the selector channel */
if (scmb [card].control_word & CN_DEVNO) { /* if the special device number flag is set */
saved_devno = dibptr->device_number; /* then save the real device number */
dibptr->device_number = /* use the counter as the device number */
LOWER_BYTE (scmb [card].counter);
sel_assert_REQ (dibptr); /* request the channel */
dibptr->device_number = saved_devno; /* restore the real device number */
}
else /* otherwise request the channel */
sel_assert_REQ (dibptr); /* with the standard device number */
break;
case CHANSO:
if (CN_CNTR (scmb [card].control_word) == count_CHANSO) /* if counting is enabled for this signal */
increment_counter (card); /* then increment the counter */
if ((scmb [card].control_word & CN_NOACK) == 0) /* if CHANACK is not inhibited */
outbound_signals |= CHANACK; /* then acknowledge the CHANSO signal */
break;
case TOGGLESR:
TOGGLE (scmb [card].channel_sr); /* set or clear the service request flip-flop */
break;
case TOGGLESIOOK:
TOGGLE (scmb [card].sio_busy); /* set or clear the SIO busy flip-flop */
if (scmb [card].sio_busy == CLEAR) /* if the channel is now idle */
dprintf (scmb_dev [card], DEB_CSRW, "Channel program ended\n");
break;
case TOGGLEINXFER:
if (CN_CNTR (scmb [card].control_word) == count_TOGGLEINXFER) /* if counting is enabled for this signal */
increment_counter (card); /* then increment the counter */
TOGGLE (scmb [card].input_xfer); /* set or clear the input transfer flip-flop */
if (scmb [card].input_xfer == SET) { /* if we're starting a new transfer */
scmb [card].flags &= ~ST_EOT; /* then clear the EOT flag */
scmb [card].device_end = /* set or clear device end status depending on */
D_FF (scmb [card].control_word & CN_DEVEND); /* whether an immediate device end is enabled */
}
scmb [card].device_sr = SET; /* preset the device SR flip-flop */
break;
case TOGGLEOUTXFER:
if (CN_CNTR (scmb [card].control_word) == count_TOGGLEOUTXFER) /* if counting is enabled for this signal */
increment_counter (card); /* then increment the counter */
TOGGLE (scmb [card].output_xfer); /* set or clear the output transfer flip-flop */
if (scmb [card].output_xfer == SET) { /* if we're starting a new transfer */
scmb [card].flags &= ~ST_EOT; /* then clear the EOT flag */
scmb [card].device_end = /* set or clear device end status depending on */
D_FF (scmb [card].control_word & CN_DEVEND); /* whether an immediate device end is enabled */
}
scmb [card].device_sr = SET; /* preset the device SR flip-flop */
break;
case DEVNODB:
if (scmb [card].control_word & CN_DEVNO) /* if the special device number flag is set */
outbound_value = LOWER_BYTE (scmb [card].counter) * 4; /* then use the counter as the device number */
else /* otherwise */
outbound_value = dibptr->device_number * 4; /* use the preset device number */
outbound_signals = NO_SIGNALS; /* clear CHANACK in case SEL issued the signal */
break;
case PCMD1:
if (CN_LOAD (scmb [card].control_word) == load_bufr_IOCW) /* if buffer load is enabled */
working_set |= DWRITESTB; /* then set the counter to the inbound value */
scmb [card].device_sr = SET; /* request channel service */
break;
case PCONTSTB:
load_operation = CN_LOAD (scmb [card].control_word); /* isolate the load operation from the control word */
if (load_operation == load_cntl_IOAW) { /* if loading IOAW into the control word is requested */
working_set |= DCONTSTB; /* then set the control word to the inbound value */
if (inbound_value & CN_NOACK) /* if the CHANACK timeout will be enabled */
outbound_signals |= CHANACK; /* then acknowledge the CHANSO signal this time only */
}
else if (load_operation != load_bufr_IOCW) /* otherwise if loading the IOAW into the buffer if enabled */
working_set |= DWRITESTB; /* then set the buffer to the inbound value */
scmb [card].device_sr = SET; /* request channel service */
break;
case READNEXTWD:
if (CN_CNTR (scmb [card].control_word) == count_READNEXTWD) /* if counting is enabled for this signal */
increment_counter (card); /* then increment the counter */
break;
case PREADSTB:
if (scmb [card].device_end == CLEAR || MPX_BUS (card)) { /* if device end is clear or we're on the MPX bus */
outbound_value = scmb [card].counter; /* then read the counter/buffer value */
if (CN_CNTR (scmb [card].control_word) == count_PREADSTB) /* if counting is enabled for this signal */
increment_counter (card); /* then increment the counter */
dprintf (scmb_dev [card], DEB_XFER, "Counter/buffer value %06o read\n",
outbound_value);
}
break;
case PWRITESTB:
if (scmb [card].device_end == CLEAR || MPX_BUS (card)) { /* if device end is clear or we're on the MPX bus */
if (scmb [card].control_word & CN_TERM_COMP) { /* then if we're doing a comparison */
if (scmb [card].counter != inbound_value) /* and the inbound value doesn't match */
scmb [card].flags |= ST_END_MISCMP; /* then set the miscompare flag */
dprintf (scmb_dev [card], DEB_XFER, "Inbound value %06o compared to counter/buffer value %06o\n",
inbound_value, scmb [card].counter);
}
else if (CN_CNTR (scmb [card].control_word) < count_PWRITESTB) { /* otherwise if we're not counting writes */
scmb [card].counter = inbound_value; /* then set the counter/buffer */
dprintf (scmb_dev [card], DEB_XFER, "Counter/buffer value %06o written\n",
inbound_value);
}
if (CN_CNTR (scmb [card].control_word) == count_PWRITESTB) /* if counting is enabled for this signal */
increment_counter (card); /* then increment the counter */
}
break;
case SETJMP:
if (scmb [card].control_word & CN_JMPMET) /* if conditional jumps are configured to succeed */
scmb [card].jump_met = SET; /* then set JMPMET status */
break;
case EOT:
if (CN_CNTR (scmb [card].control_word) == count_EOT) /* if counting is enabled for this signal */
increment_counter (card); /* then increment the counter */
scmb [card].flags |= ST_EOT; /* set the end of transfer status */
break;
case XFERERROR:
if (scmb [card].stop_transfer == CLEAR) { /* if we haven't stopped yet */
clear_logic (card); /* then clear the interface and abort the transfer */
scmb [card].stop_transfer = SET; /* inhibit another interface clear */
scmb [card].flags |= ST_XFERERR | ST_CLEAR_IF; /* set the transfer error and clear interface status */
sim_cancel (&scmb_unit [card]); /* cancel any pending delayed SR assertion */
dibptr->interrupt_request = SET; /* set the interrupt request flip-flop */
outbound_signals |= INTREQ; /* and request the interrupt */
}
break;
case DSETMASK: /* not used by this interface */
case ACKSR: /* not used by this interface */
case PFWARN: /* not used by this interface */
break;
}
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
}
if (scmb [card].flags & END_CONDITION) /* if a termination condition is present */
if ((scmb [card].control_word & CN_CLEAR_IF) == 0) /* then if we want a device end */
scmb [card].device_end = SET; /* then indicate a device end abort */
else if (scmb [card].stop_transfer == CLEAR) { /* otherwise if we haven't stopped yet */
clear_logic (card); /* then clear the interface and abort the transfer */
scmb [card].stop_transfer = SET; /* inhibit another interface clear */
scmb [card].flags |= ST_CLEAR_IF; /* and set the clear interface status */
dibptr->interrupt_request = SET; /* set the request flip-flop */
outbound_signals |= INTREQ; /* and request the interrupt */
}
if (scmb [card].control_word & CN_HSREQ) /* if high-speed requests are enabled */
assert_sr = D_FF (scmb [card].channel_sr /* then assert SR immediately if indicated */
| scmb [card].device_sr);
else { /* otherwise assert SR immediately */
assert_sr = scmb [card].channel_sr; /* only if the channel is requesting service */
if ((! assert_sr & scmb [card].device_sr) /* if a delayed device SR assertion is requested */
&& (MPX_BUS (card) || outbound_signals & CHANACK) /* and we're on the MPX bus or CHANACK is not inhibited */
&& (scmb [card].control_word & CN_NOSR) == 0) { /* and channel service is not inhibited */
sim_activate (&scmb_unit [card], /* then schedule SR assertion in 5 microseconds */
scmb_unit [card].wait);
dprintf (scmb_dev [card], DEB_SERV, "Delay %u SR service scheduled\n",
scmb_unit [card].wait);
}
}
if (assert_sr) /* if a service request is indicated */
if (MPX_BUS (card)) /* then if we're on the multiplexer bus */
outbound_signals |= SRn; /* then assert the SRn signal */
else if ((scmb [card].control_word & CN_NOSR) == 0) /* otherwise if channel service is not inhibited */
outbound_signals |= CHANSR; /* then assert the CHANSR signal */
if (scmb [card].jump_met == SET) /* if the jump met flip-flop is set */
outbound_signals |= JMPMET; /* then assert the JMPMET signal */
if (scmb [card].device_end == SET && SEL_BUS (card)) /* if device end is set and we're on the SEL bus */
outbound_signals |= DEVEND; /* then assert the DEVEND signal */
dprintf (scmb_dev [card], DEB_IOB, "Returned data %06o with signals %s\n",
outbound_value, fmt_bitset (outbound_signals, outbound_format));
return IORETURN (outbound_signals, outbound_value); /* return the outbound signals and value */
}
/* Service the SCMB.
The service routine delays assertion of channel service request if the SCMB
is not in high-speed mode. The delay corresponds to five microseconds.
It is important that scheduling not be performed if the channel is given an
abort condition. Otherwise, SR would be asserted while the channel is idle
or servicing another device.
*/
static t_stat scmb_service (UNIT *uptr)
{
const CARD_ID card = (CARD_ID) (uptr == &scmb_unit [card2]); /* the ID number of the card */
dprintf (scmb_dev [card], DEB_SERV, "SR service entered\n");
if (MPX_BUS (card)) /* if we're connected to the multiplexer channel */
mpx_assert_SRn (&scmb_dib [card]); /* then assert the SRn signal */
else /* otherwise we're connected to the selector channel */
sel_assert_CHANSR (&scmb_dib [card]); /* so assert the CHANSR signal */
return SCPE_OK;
}
/* Reset the SCMB.
This routine is called for a RESET or RESET SCMB command. It is the
simulation equivalent of the IORESET signal, which is asserted by the front
panel LOAD and DUMP switches.
For this interface, IORESET is identical to a Programmed Master Reset, which
corresponds to the internal RST1 signal.
For a power-on reset, the logical name "SCMB1" is assigned to the first SCMB
card, so that it may referenced either as that name or as "SCMB" for use
where only one SCMB is needed.
*/
static t_stat scmb_reset (DEVICE *dptr)
{
const DIB *const dibptr = (DIB *) (dptr->ctxt); /* the DIB pointer */
const CARD_ID card = (CARD_ID) (dibptr->card_index); /* the ID number of the card */
if ((sim_switches & SWMASK ('P')) /* if this is a power-on reset */
&& card == card1 /* and we're being called for SCMB1 */
&& scmb_dev [card1].lname == NULL) /* and the logical name has not been set yet */
scmb_dev [card1].lname = strdup ("SCMB1"); /* then allocate and initialize the name */
scmb [card].counter = 0; /* clear the counter/buffer register */
scmb [card].control_word = 0; /* and the control word register */
sio_reset (card); /* reset the remainder */
clear_logic (card); /* of the card logic */
sim_cancel (dptr->units); /* cancel any pending delayed SR assertion */
return SCPE_OK;
}
/* Set the bus connection.
The SCMB may be connected either to the multiplexer or the selector channel
bus. If the interface is being moved from the multiplexer to the selector,
save the SCMB's current service request number and set it to "unused" so that
multiplexer initialization won't pick it up by mistake.
*/
static t_stat scmb_set_bus (UNIT *uptr, int32 value, CONST char *cptr, void *desc)
{
const CARD_ID card = (CARD_ID) (uptr == &scmb_unit [card2]);
if (value == UNIT_W1_SEL && MPX_BUS (card)) { /* if we're moving from MPX to SEL */
scmb [card].saved_srn = scmb_dib [card].service_request_number; /* then save the current SR number */
scmb_dib [card].service_request_number = SRNO_UNUSED; /* for later restoration */
}
else if (value == 0 && SEL_BUS (card)) /* otherwise if moving from SEL to MPX */
scmb_dib [card].service_request_number = scmb [card].saved_srn; /* then restore the previous SR number */
return SCPE_OK;
}
/* SCMB local utility routines */
/* Reset for a new program.
This routine is called for an IORESET signal, a Programmed Master Reset, or
in response to an SIO instruction. It corresponds in hardware to the
internal RST2 signal, which is generated to clear the SCMB logic in
preparation for a new I/O program.
*/
static void sio_reset (CARD_ID card)
{
scmb [card].jump_met = CLEAR; /* clear the JMPMET */
scmb [card].device_end = CLEAR; /* and DEVEND flip-flops */
scmb [card].flags = 0; /* clear the flags */
scmb_dib [card].interrupt_request = CLEAR; /* clear the interrupt request flip-flop */
return;
}
/* Reset the interface logic.
This routine is called for an IORESET signal, a Programmed Master Reset, or
an internal CLRIL signal, which, if enabled, is generated for a condition
that terminates an I/O program. It corresponds in hardware to the internal
RST3 signal.
*/
static void clear_logic (CARD_ID card)
{
scmb [card].sio_busy = CLEAR; /* clear the SIO Busy flip-flop */
scmb [card].channel_sr = CLEAR; /* clear the channel */
scmb [card].device_sr = CLEAR; /* and device service request flip-flops */
scmb [card].input_xfer = CLEAR; /* clear the input */
scmb [card].output_xfer = CLEAR; /* and output transfer flip-flops */
scmb_dib [card].interrupt_active = CLEAR; /* clear the interrupt active flip-flop */
if (SEL_BUS (card) && ! sel_is_idle) /* if we're connected to the selector channel and it's busy */
sel_assert_REQ (&scmb_dib [card]); /* then abort the transfer */
return;
}
/* Increment the counter.
Increment the counter/buffer register in response to an enabled count
condition. If the count rolls over, and the "terminate on terminal count"
condition is enabled, then set the end-on-terminal-count status.
*/
static void increment_counter (CARD_ID card)
{
scmb [card].counter = scmb [card].counter + 1 & R_MASK; /* increment the counter with rollover */
if (scmb [card].counter == 0 /* if the counter rolled over */
&& scmb [card].control_word & CN_TERM_COUNT) /* and termination is enabled */
scmb [card].flags |= ST_END_COUNT; /* then set the terminal count flag */
return;
}