/* 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 | |
13-May-16 JDB Modified for revised SCP API function parameter types | |
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 clear interface 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 (1 << 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 (1 << 0) /* trace commands received and status returned */ | |
#define DEB_XFER (1 << 1) /* trace channel data reads and writes */ | |
#define DEB_SERV (1 << 2) /* trace unit service scheduling calls */ | |
#define DEB_IOB (1 << 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 0100000 /* M = master reset */ | |
#define CN_IRQ_RESET 0040000 /* R = interrupt reset */ | |
#define CN_JMPMET 0020000 /* J = set jump met */ | |
#define CN_DEVEND 0010000 /* V = device end */ | |
#define CN_NOACK 0004000 /* A = inhibit channel acknowledge */ | |
#define CN_NOSR 0002000 /* S = inhibit service request */ | |
#define CN_LOAD_MASK 0001400 /* load operation mask */ | |
#define CN_HSREQ 0000200 /* H = high speed service request */ | |
#define CN_DEVNO 0000100 /* N = special device number */ | |
#define CN_TERM_COUNT 0000040 /* T = terminate on count */ | |
#define CN_TERM_COMP 0000020 /* C = terminate on miscompare */ | |
#define CN_CLEAR_IF 0000010 /* L = clear interface */ | |
#define CN_CNTR_MASK 0000007 /* 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 */ | |
"\1clear interface\0device 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 0100000 /* S = SIO OK to use */ | |
#define ST_DIO_OK 0040000 /* D = direct I/O OK to use (always 1) */ | |
#define ST_INTREQ 0020000 /* R = interrupt requested */ | |
#define ST_INTACT 0010000 /* A = interrupt active */ | |
#define ST_XFERERR 0004000 /* X = transfer error is asserted */ | |
#define ST_SIOENABLED 0002000 /* N = SIO enabled is asserted */ | |
#define ST_DEVEND 0001000 /* V = device end is asserted */ | |
#define ST_EOT 0000400 /* E = end of transfer is asserted */ | |
#define ST_END_MISCMP 0000200 /* C = end on miscompare occurred */ | |
#define ST_END_COUNT 0000100 /* T = end on terminal count occurred */ | |
#define ST_INXFER 0000040 /* I = input transfer is asserted */ | |
#define ST_OUTXFER 0000020 /* O = output transfer is asserted */ | |
#define ST_CLEAR_IF 0000010 /* 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 { | |
uint16 control_word; /* control word register */ | |
uint16 status_word; /* status word register */ | |
uint16 counter; /* counter/buffer register */ | |
uint16 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) }, | |
{ SRDATA (DIB, scmb_dib [card1]), REG_HRO }, | |
{ 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) }, | |
{ SRDATA (DIB, scmb_dib [card1]), REG_HRO }, | |
{ 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, uint16 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; | |
uint16 saved_devno; | |
INBOUND_SIGNAL signal; | |
INBOUND_SET working_set = inbound_signals; | |
uint16 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 */ | |
(scmb [card].control_word & CN_DEVEND) != 0; /* 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 */ | |
(scmb [card].control_word & CN_DEVEND) != 0; /* 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 = scmb [card].channel_sr | scmb [card].device_sr; /* then assert SR immediately if indicated */ | |
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 %d 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; | |
} |