| /* 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 | |
| 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, 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, 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; | |
| } |