1356 lines
64 KiB
C
1356 lines
64 KiB
C
/* hp3000_ds.c: HP 3000 30229B Cartridge Disc Interface simulator
|
|
|
|
Copyright (c) 2016-2018, 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.
|
|
|
|
DS HP 30229B Cartridge Disc Interface
|
|
|
|
27-Dec-18 JDB Revised fall through comments to comply with gcc 7
|
|
05-Sep-17 JDB Changed REG_A (permit any symbolic override) to REG_X
|
|
12-Sep-16 JDB Changed DIB register macro usage from SRDATA to DIB_REG
|
|
09-Jun-16 JDB Added casts for ptrdiff_t to int32 values
|
|
08-Jun-16 JDB Corrected %d format to %u for unsigned values
|
|
16-May-16 JDB Fixed interrupt mask setting
|
|
13-May-16 JDB Modified for revised SCP API function parameter types
|
|
24-Mar-16 JDB Changed the buffer element type from uint16 to DL_BUFFER
|
|
21-Mar-16 JDB Changed uint16 types to HP_WORD
|
|
21-Jul-15 JDB First release version
|
|
15-Jun-15 JDB Passes the cartridge disc diagnostic (D419A)
|
|
15-Feb-15 JDB Created
|
|
|
|
References:
|
|
- Model 7905A Cartridge Disc Subsystem Installation and Service Manual
|
|
(30129-90003, May 1976)
|
|
- Stand-Alone HP 30129A (7905A) Disc Cartridge Diagnostic
|
|
(30129-90007, February 1976)
|
|
- HP 3000 Series III Engineering Diagrams Set
|
|
(30000-90141, April 1980)
|
|
- 13037 Disc Controller Technical Information Package
|
|
(13037-90902, August 1980)
|
|
- 7925D Disc Drive Service Manual
|
|
(07925-90913, April 1984)
|
|
|
|
|
|
The HP 30129A Cartridge Disc Subsystem connects the 7905A, 7906A, 7920A, and
|
|
7925A disc drives to the HP 3000. The subsystem consists of a 30229B
|
|
Cartridge Disc Interface, a 13037D Multiple-Access Disc Controller ("MAC"),
|
|
and from one to eight MAC drives. The subsystem uses the Selector Channel to
|
|
achieve a 937.5 KB/second transfer rate to the CPU.
|
|
|
|
The disc controller connects from one to eight HP 7905 (15 MB), 7906 (20 MB),
|
|
7920 (50 MB), or 7925 (120 MB) disc drives to interfaces installed in from
|
|
one to eight CPUs. The drives use a common command set and present data to
|
|
the controller synchronously at a 468.75 kiloword per second (2.133
|
|
microseconds per word) data rate.
|
|
|
|
The disc interface is used to connect the HP 3000 CPU to the 13037's device
|
|
controller. While the controller supports multiple-CPU systems, the HP 3000
|
|
does not use this capability.
|
|
|
|
This module simulates a 30229B interface connected to a 13037D controller;
|
|
the controller simulation is provided by the hp_disclib module. From one to
|
|
eight drives may be connected, and drive types may be freely intermixed. A
|
|
unit that is enabled but not attached appears to be a connected drive that
|
|
does not have a disc pack in place. A unit that is disabled appears to be
|
|
disconnected. An extra unit for the use of the disc controller library is
|
|
also allocated.
|
|
|
|
In hardware, the controller runs continuously in one of three states: in the
|
|
Poll Loop (idle state), in the Command Wait Loop (wait state), or in command
|
|
execution (busy state). In simulation, the controller is run only when a
|
|
command is executing or when a transition into or out of the two loops might
|
|
occur. Internally, the controller handles these transitions:
|
|
|
|
- when a command other than End terminates (busy => wait)
|
|
- when the End command terminates (busy => idle)
|
|
- when a command timeout occurs (wait => idle)
|
|
- when a parameter timeout occurs (busy => idle)
|
|
- when a seek completes (if idle, and interrupts are enabled: idle => wait)
|
|
|
|
The interface must call the controller library to handle these transitions:
|
|
|
|
- when a command is received from the CPU (idle or wait => busy)
|
|
- when interrupts are enabled (if idle and drive Attention, idle => wait)
|
|
|
|
In addition, each transition to the wait state must check for a pending
|
|
command, and each transition to the idle state must check for both a pending
|
|
command and a drive with Attention status asserted.
|
|
|
|
While the controller is in the busy state, command execution is broken up
|
|
into a series of phases. Phase transitions are scheduled on the drive units
|
|
for commands that access the drives and on the controller unit otherwise.
|
|
The interface unit service routine must call the disc controller to inform it
|
|
of these events.
|
|
|
|
|
|
The disc interface 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 | T | - - - - - - - - - - - - - |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Where:
|
|
|
|
M = master reset
|
|
R = reset interrupts
|
|
T = test mode
|
|
|
|
Test mode inhibits the flag bus signals. This allows the diagnostic to
|
|
exercise the interface without causing the disc controller to react.
|
|
|
|
|
|
Control Word Format (SIO Control):
|
|
|
|
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| - - - - - - - - - - - - - - - | W | word 1
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| disc controller command word | word 2
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Where:
|
|
|
|
W = set the Wait flip-flop
|
|
|
|
The command opcode is the disc controller command to execute. If the command
|
|
takes or returns parameters, an SIO Write or Read must follow the Control
|
|
order to supply or receive them.
|
|
|
|
|
|
Status Word Format (TIO and SIO Status):
|
|
|
|
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| S | T | I |termination status | - - - - | unit number |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Where:
|
|
|
|
S = SIO OK
|
|
T = test mode is enabled (also DIO OK)
|
|
I = interrupt request
|
|
|
|
The termination status and unit number report the success or failure of the
|
|
last disc controller command. Also, note that the test mode flip-flop output
|
|
is reported as DIO OK. This means that RIO and WIO are inhibited unless test
|
|
mode is set.
|
|
|
|
|
|
Output Data Word Format (WIO and SIO Write):
|
|
|
|
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| data buffer register value |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
Input Data Word Format (RIO and SIO Read):
|
|
|
|
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| data buffer register value |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
Disc read or write commands may transfer up to 4K words with a single SIO
|
|
Read or Write order. Chained orders are necessary if longer transfers are
|
|
required.
|
|
|
|
The interface allows the channel to retry a failed transfer without CPU
|
|
intervention. The controller asserts the DVEND signal for transfer errors
|
|
that it considers retryable (e.g., a disc read error). A channel program can
|
|
detect this condition via a Conditional Jump order, which will succeed for
|
|
each retryable failure until the retry count expires.
|
|
|
|
Unusually among HP 3000 interfaces, this device reacts to the PFWARN signal.
|
|
A pending power failure will abort the current disc transfer and channel
|
|
program, so that the operating system will know to retry the transfer once
|
|
power has been restored.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. As only a single interface connected to the disc controller is supported,
|
|
the interface select address jumpers are not simulated. Instead, the
|
|
interface behaves as though it is always selected and does not process
|
|
the SELIF and DSCIF functions from the controller.
|
|
|
|
2. In hardware, jumper W1 selects whether the interface should assert the
|
|
CLEAR signal to the disc controller when the interface is preset. This
|
|
jumper is needed in a multiple-interface system so that only one
|
|
interface clears the controller. The simulation does check the state of
|
|
jumper W1, but as only a single interface is supported, the jumper
|
|
position is hard-coded as ENABLED rather than being configurable via the
|
|
user interface.
|
|
|
|
3. Several of the hardware flip-flops that directly drive flag signals to
|
|
the controller are modeled in simulation by setting and clearing the
|
|
corresponding bits in the flags word itself.
|
|
|
|
4. The simulation provides REALTIME and FASTTIME options. FASTTIME settings
|
|
may be altered via the register interface. Performing a power-on reset
|
|
(RESET -P) will restore the original FASTTIME values.
|
|
|
|
5. This simulation provides diagnostic override settings to allow complete
|
|
testing coverage via the offline disc diagnostic. See the comments in
|
|
the disc controller library for details of this capability.
|
|
*/
|
|
|
|
|
|
|
|
#include "hp3000_defs.h"
|
|
#include "hp3000_io.h"
|
|
#include "hp_disclib.h"
|
|
|
|
|
|
|
|
/* Program constants */
|
|
|
|
#define DRIVE_COUNT (DL_MAXDRIVE + 1) /* number of disc drive units */
|
|
#define UNIT_COUNT (DRIVE_COUNT + DL_AUXUNITS) /* total number of units */
|
|
|
|
#define ds_cntlr ds_unit [DL_MAXDRIVE + 1] /* controller unit alias */
|
|
|
|
#define OVERRIDE_COUNT 50 /* count of diagnostic override entries */
|
|
|
|
#define PRESET_ENABLE TRUE /* Preset Jumper (W1) is enabled */
|
|
|
|
#define UNUSED_COMMANDS (BUSY | DSCIF | SELIF | IFPRF | STDFL | FREE) /* unused disc interface commands */
|
|
|
|
|
|
/* Debug flags (interface-specific) */
|
|
|
|
#define DEB_IOB DL_DEB_IOB /* trace I/O bus signals and data words */
|
|
#define DEB_CSRW (1u << DL_DEB_V_UF + 0) /* trace control, status, read, and write commands */
|
|
|
|
|
|
/* Control word.
|
|
|
|
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| M | R | T | - - - - - - - - - - - - - | DIO
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| - - - - - - - - - - - - - - - | W | PIO word 1
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| disc controller command word | PIO word 2
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
*/
|
|
|
|
#define CN_MR 0100000u /* (M) master reset */
|
|
#define CN_RIN 0040000u /* (R) reset interrupt */
|
|
#define CN_TEST 0020000u /* (T) test mode */
|
|
|
|
#define CN_WAIT 0000001u /* (W) wait for data */
|
|
|
|
#define CN_OPCODE_MASK 0017400u /* command word opcode mask */
|
|
|
|
#define CN_OPCODE_SHIFT 8 /* controller opcode alignment shift */
|
|
|
|
#define CN_OPCODE(c) ((CNTLR_OPCODE) (((c) & CN_OPCODE_MASK) >> CN_OPCODE_SHIFT))
|
|
|
|
static const BITSET_NAME control_names [] = { /* Control word names */
|
|
"master reset", /* bit 0 */
|
|
"reset interrupt", /* bit 1 */
|
|
"test mode" /* bit 2 */
|
|
};
|
|
|
|
static const BITSET_FORMAT control_format = /* names, offset, direction, alternates, bar */
|
|
{ FMT_INIT (control_names, 13, msb_first, no_alt, no_bar) };
|
|
|
|
|
|
/* Status word.
|
|
|
|
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| S | T | I |termination status | - - - - | unit number |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
*/
|
|
|
|
#define ST_SIO_OK 0100000u /* (S) SIO OK to use */
|
|
#define ST_TEST 0040000u /* (T) test mode enabled */
|
|
#define ST_INTREQ 0020000u /* (I) interrupt requested */
|
|
#define ST_STATUS_MASK 0017400u /* encoded termination status mask */
|
|
#define ST_UNIT_MASK 0000017u /* unit number mask */
|
|
|
|
#define ST_MASK ~(ST_SIO_OK | ST_TEST | ST_INTREQ)
|
|
|
|
#define ST_STATUS_SHIFT 8 /* termination status alignment shift */
|
|
#define ST_UNIT_SHIFT 0 /* unit number alignment shift */
|
|
|
|
#define ST_STATUS(n) ((n) << ST_STATUS_SHIFT & ST_STATUS_MASK)
|
|
|
|
#define ST_TO_UNIT(s) (((s) & ST_UNIT_MASK) >> ST_UNIT_SHIFT)
|
|
#define ST_TO_STATUS(s) (CNTLR_STATUS) (((s) & ST_STATUS_MASK) >> ST_STATUS_SHIFT)
|
|
|
|
|
|
static const BITSET_NAME status_names [] = { /* Status word names */
|
|
"SIO OK", /* bit 0 */
|
|
"test mode", /* bit 1 */
|
|
"interrupt" /* bit 2 */
|
|
};
|
|
|
|
static const BITSET_FORMAT status_format = /* names, offset, direction, alternates, bar */
|
|
{ FMT_INIT (status_names, 13, msb_first, no_alt, append_bar) };
|
|
|
|
|
|
/* Disc controller library data structures */
|
|
|
|
#define DS_SEEK_ONE uS (25) /* track-to-track seek time */
|
|
#define DS_SEEK_FULL uS (250) /* full-stroke seek time */
|
|
#define DS_SECTOR_FULL uS (50) /* full sector rotation time */
|
|
#define DS_DATA_XFER uS (1) /* data transfer response time */
|
|
#define DS_ISG uS (25) /* intersector gap rotation time */
|
|
#define DS_OVERHEAD uS (25) /* controller execution overhead */
|
|
|
|
static DELAY_PROPS fast_times = /* FASTTIME delays */
|
|
{ DELAY_INIT (DS_SEEK_ONE, DS_SEEK_FULL,
|
|
DS_SECTOR_FULL, DS_DATA_XFER,
|
|
DS_ISG, DS_OVERHEAD) } ;
|
|
|
|
static DIAG_ENTRY overrides [OVERRIDE_COUNT] = { /* diagnostic overrides array */
|
|
{ DL_OVEND }
|
|
};
|
|
|
|
|
|
/* Interface state */
|
|
|
|
static FLIP_FLOP sio_busy = CLEAR; /* SIO busy flip-flop */
|
|
static FLIP_FLOP device_sr = CLEAR; /* device service request flip-flop */
|
|
static FLIP_FLOP input_xfer = CLEAR; /* input transfer flip-flop */
|
|
static FLIP_FLOP output_xfer = CLEAR; /* output transfer flip-flop */
|
|
static FLIP_FLOP interrupt_mask = SET; /* interrupt mask flip-flop */
|
|
static FLIP_FLOP jump_met = CLEAR; /* jump met flip-flop */
|
|
static FLIP_FLOP device_end = CLEAR; /* device end flip-flop */
|
|
static FLIP_FLOP data_overrun = CLEAR; /* data overrun flip-flop */
|
|
static FLIP_FLOP end_of_data = CLEAR; /* end of data flip-flop */
|
|
static FLIP_FLOP test_mode = CLEAR; /* test mode flip-flop */
|
|
static FLIP_FLOP data_wait = CLEAR; /* wait flip-flop */
|
|
|
|
static HP_WORD status_word = 0; /* status register */
|
|
static HP_WORD buffer_word = 0; /* data buffer register */
|
|
static HP_WORD retry_counter = 0; /* retry counter */
|
|
static CNTLR_FLAG_SET flags = NO_FLAGS; /* disc controller interface flag set */
|
|
|
|
static DL_BUFFER buffer [DL_BUFSIZE]; /* command/status/sector buffer */
|
|
|
|
DEVICE ds_dev; /* incomplete device structure */
|
|
|
|
static CNTLR_VARS mac_cntlr = /* MAC controller */
|
|
{ CNTLR_INIT (MAC, ds_dev, buffer, overrides, fast_times) };
|
|
|
|
|
|
/* Interface local SCP support routines */
|
|
|
|
static CNTLR_INTRF ds_interface;
|
|
static t_stat ds_service (UNIT *uptr);
|
|
static t_stat ds_reset (DEVICE *dptr);
|
|
static t_stat ds_boot (int32 unit_number, DEVICE *dptr);
|
|
static t_stat ds_attach (UNIT *uptr, CONST char *cptr);
|
|
static t_stat ds_detach (UNIT *uptr);
|
|
static t_stat ds_load_unload (UNIT *uptr, int32 value, CONST char *cptr, void *desc);
|
|
|
|
|
|
/* Interface local utility routines */
|
|
|
|
static void master_reset (void);
|
|
static void deny_sio_busy (void);
|
|
static void clear_interface_logic (DIB *dibptr);
|
|
static void call_controller (UNIT *uptr);
|
|
|
|
|
|
/* Interface SCP data structures */
|
|
|
|
|
|
/* Device information block */
|
|
|
|
static DIB ds_dib = {
|
|
&ds_interface, /* device interface */
|
|
4, /* device number */
|
|
SRNO_UNUSED, /* service request number */
|
|
4, /* interrupt priority */
|
|
INTMASK_E /* interrupt mask */
|
|
};
|
|
|
|
/* Unit list */
|
|
|
|
#define UNIT_FLAGS (UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD)
|
|
|
|
static UNIT ds_unit [UNIT_COUNT] = {
|
|
{ UDATA (&ds_service, UNIT_FLAGS | UNIT_7905, WORDS_7905) }, /* drive unit 0 */
|
|
{ UDATA (&ds_service, UNIT_FLAGS | UNIT_7905, WORDS_7905) }, /* drive unit 1 */
|
|
{ UDATA (&ds_service, UNIT_FLAGS | UNIT_7905, WORDS_7905) }, /* drive unit 2 */
|
|
{ UDATA (&ds_service, UNIT_FLAGS | UNIT_7905, WORDS_7905) }, /* drive unit 3 */
|
|
{ UDATA (&ds_service, UNIT_FLAGS | UNIT_7905, WORDS_7905) }, /* drive unit 4 */
|
|
{ UDATA (&ds_service, UNIT_FLAGS | UNIT_7905, WORDS_7905) }, /* drive unit 5 */
|
|
{ UDATA (&ds_service, UNIT_FLAGS | UNIT_7905, WORDS_7905) }, /* drive unit 6 */
|
|
{ UDATA (&ds_service, UNIT_FLAGS | UNIT_7905, WORDS_7905) }, /* drive unit 7 */
|
|
{ UDATA (&ds_service, UNIT_DIS, 0) } /* controller unit */
|
|
};
|
|
|
|
/* Register list */
|
|
|
|
static REG ds_reg [] = {
|
|
/* Macro Name Location Width Offset Flags */
|
|
/* ------ ------ --------------- ----- ------ ------------------------- */
|
|
{ FLDATA (SIOBSY, sio_busy, 0) },
|
|
{ FLDATA (DEVSR, device_sr, 0) },
|
|
{ FLDATA (INXFR, input_xfer, 0) },
|
|
{ FLDATA (OUTXFR, output_xfer, 0) },
|
|
{ FLDATA (INTMSK, interrupt_mask, 0) },
|
|
{ FLDATA (JMPMET, jump_met, 0) },
|
|
{ FLDATA (DEVEND, device_end, 0) },
|
|
|
|
{ FLDATA (DATOVR, data_overrun, 0) },
|
|
{ FLDATA (ENDDAT, end_of_data, 0) },
|
|
{ FLDATA (TEST, test_mode, 0) },
|
|
{ FLDATA (WAIT, data_wait, 0) },
|
|
|
|
{ FLDATA (CLEAR, flags, 0) },
|
|
{ FLDATA (CMRDY, flags, 1) },
|
|
{ FLDATA (DTRDY, flags, 2) },
|
|
{ FLDATA (EOD, flags, 3) },
|
|
{ FLDATA (INTOK, flags, 4) },
|
|
{ FLDATA (OVRUN, flags, 5) },
|
|
{ FLDATA (XFRNG, flags, 6) },
|
|
|
|
{ ORDATA (BUFFER, buffer_word, 16), REG_X | REG_FIT | PV_RZRO },
|
|
{ ORDATA (STATUS, status_word, 16), REG_FIT | PV_RZRO },
|
|
{ DRDATA (RETRY, retry_counter, 4), REG_FIT | PV_LEFT },
|
|
|
|
{ SVDATA (DIAG, overrides) },
|
|
|
|
DIB_REGS (ds_dib),
|
|
|
|
DL_REGS (mac_cntlr, ds_unit, UNIT_COUNT, buffer, fast_times),
|
|
|
|
{ NULL }
|
|
};
|
|
|
|
/* Modifier list */
|
|
|
|
static MTAB ds_mod [] = {
|
|
|
|
DL_MODS (mac_cntlr, ds_load_unload, OVERRIDE_COUNT),
|
|
|
|
/* Entry Flags Value Print String Match String Validation Display Descriptor */
|
|
/* ----------- ----------- ------------ ------------ ------------ ------------- ---------------- */
|
|
{ MTAB_XDV, VAL_DEVNO, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &ds_dib },
|
|
{ MTAB_XDV, VAL_INTMASK, "INTMASK", "INTMASK", &hp_set_dib, &hp_show_dib, (void *) &ds_dib },
|
|
{ MTAB_XDV, VAL_INTPRI, "INTPRI", "INTPRI", &hp_set_dib, &hp_show_dib, (void *) &ds_dib },
|
|
{ 0 }
|
|
};
|
|
|
|
/* Debugging trace list */
|
|
|
|
static DEBTAB ds_deb [] = {
|
|
{ "CMD", DL_DEB_CMD }, /* controller commands */
|
|
{ "INCO", DL_DEB_INCO }, /* controller command initiations and completions */
|
|
{ "CSRW", DEB_CSRW }, /* interface control, status, read, and write actions */
|
|
{ "STATE", DL_DEB_STATE }, /* controller execution state changes */
|
|
{ "SERV", DL_DEB_SERV }, /* controller unit service scheduling calls */
|
|
{ "XFER", DL_DEB_XFER }, /* controller data reads and writes */
|
|
{ "IOBUS", DEB_IOB }, /* interface and controller I/O bus signals and data words */
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
/* Device descriptor */
|
|
|
|
DEVICE ds_dev = {
|
|
"DS", /* device name */
|
|
ds_unit, /* unit array */
|
|
ds_reg, /* register array */
|
|
ds_mod, /* modifier array */
|
|
UNIT_COUNT, /* number of units */
|
|
8, /* address radix */
|
|
27, /* address width = 128 MB */
|
|
1, /* address increment */
|
|
8, /* data radix */
|
|
16, /* data width */
|
|
NULL, /* examine routine */
|
|
NULL, /* deposit routine */
|
|
&ds_reset, /* reset routine */
|
|
&ds_boot, /* boot routine */
|
|
&ds_attach, /* attach routine */
|
|
&ds_detach, /* detach routine */
|
|
&ds_dib, /* device information block */
|
|
DEV_DEBUG | DEV_DISABLE, /* device flags */
|
|
0, /* debug control flags */
|
|
ds_deb, /* debug flag name table */
|
|
NULL, /* memory size change routine */
|
|
NULL /* logical device name */
|
|
};
|
|
|
|
|
|
|
|
/* Interface local SCP support routines */
|
|
|
|
|
|
|
|
/* Disc controller interface.
|
|
|
|
The interface is installed on the IOP and Selector Channel buses and receives
|
|
direct and programmed I/O commands from the IOP and Selector 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.
|
|
|
|
MAC disc controller commands take from 0 to 2 parameters and return from 0 to
|
|
7 status words. All communication with the disc controller is via programmed
|
|
I/O. Direct I/O is used only to communicate with the interface.
|
|
|
|
Commands consist of a Control I/O order optionally followed by a one- or
|
|
two-word Write order to supply the parameters. Commands that return status
|
|
consist of a Control order followed by a Read order to send the status.
|
|
Controller command opcodes are carried in the IOAW of a programmed I/O
|
|
Control order. The IOCW is not used, except for bit 15, which is clocked
|
|
into the WAIT flip-flop. This bit must be set for commands that return
|
|
parameters (Request Status, Request Sector Address, Request Syndrome, and
|
|
Request Disc Address) to hold off the controller until the channel has
|
|
executed a Read I/O order. Setting WAIT asserts DTRDY unconditionally; the
|
|
controller will not send a word to the CPU until DTRDY denies, indicating
|
|
that the interface data buffer is empty and ready to receive the word.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. In hardware, the disc controller executes a status command, such as
|
|
Request Status, by first asserting IFGTC to clear the command from the
|
|
interface and then asserting IFIN to tell the interface that the (first)
|
|
status word is ready for pickup. Both IFGTC and IFIN assert CHANSR to
|
|
the channel; the first completes the Control I/O order, and the second
|
|
completes the TOGGLEINXFER phase of the Read I/O order. Simulating this
|
|
sequential assertion requires two calls to the controller. The second
|
|
call is placed the TOGGLEINXFER handler, although in hardware this signal
|
|
has no effect on the controller state.
|
|
|
|
2. In hardware, the PREADSTB and PWRITESTB signals each toggle the Data
|
|
Ready flip-flop, rather than explicitly clearing and setting it,
|
|
respectively. The simulation maintains this action.
|
|
|
|
3. In hardware, three serially connected End of Data flip-flops are
|
|
employed. The first presets on EOT, the second clocks on the leading
|
|
edge of TOGGLEXFER, and the third clocks when the Data Ready flip-flop
|
|
clears. The output of the third drives the EOD line to the disc
|
|
controller. For a read, DTRDY denies when the trailing edge of PREADSTB
|
|
clocks the third flip-flop. For this to work, the implied relationship
|
|
is EOT asserts before the leading edge of TOGGLEINXFER, which asserts
|
|
before the trailing edge of PREADSTB.
|
|
|
|
In simulation, PREADSTB is processed before EOT, which is processed
|
|
before TOGGLEINXFER, which is the order of the leading edges of the
|
|
hardware signals. To simulate clocking the Data Ready flip-flop on the
|
|
trailing edge, the action is performed in the TOGGLEINXFER handler
|
|
instead of the PREADSTB handler.
|
|
|
|
4. In hardware, the Device SR 1 flip-flop is cleared by assertion of the
|
|
PCONTSTB or PWRITESTB signals, and the the Device SR 2 flip-flop is
|
|
cleared by assertion of CHANSO without DEVEND or by the clear output of
|
|
the SIO busy flip-flop. Also, DEVEND forces CHANSR assertion. In
|
|
simulation, a unified device_sr flip-flop is employed that is cleared if
|
|
CHANSO is asserted or SIO Busy is clear.
|
|
|
|
5. When TOGGLESIOOK clears the sio_busy flip-flop, the controller must be
|
|
called to poll the drives for attention. Consider an SIO program that
|
|
does a Seek on drive 0, followed by a Read on drive 1, followed by an
|
|
End. If the seek completes during the read, the Drive Attention
|
|
interrupt won't occur after the End unless the drive is polled from the
|
|
TOGGLESIOOK handler, as INTOK isn't asserted until the channel program
|
|
ends.
|
|
|
|
6. 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.
|
|
*/
|
|
|
|
static SIGNALS_DATA ds_interface (DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
|
|
{
|
|
INBOUND_SIGNAL signal;
|
|
INBOUND_SET working_set = inbound_signals;
|
|
HP_WORD outbound_value = 0;
|
|
OUTBOUND_SET outbound_signals = NO_SIGNALS;
|
|
|
|
dprintf (ds_dev, DEB_IOB, "Received data %06o with signals %s\n",
|
|
inbound_value, fmt_bitset (inbound_signals, inbound_format));
|
|
|
|
if (inbound_signals & CHANSO || sio_busy == CLEAR) /* if a PIO signal is asserted or SIO is inactive */
|
|
device_sr = CLEAR; /* then clear the device SR flip-flop */
|
|
|
|
while (working_set) {
|
|
signal = IONEXTSIG (working_set); /* isolate the next signal */
|
|
|
|
switch (signal) { /* dispatch an I/O signal */
|
|
|
|
case SETINT:
|
|
case DSETINT:
|
|
dibptr->interrupt_request = SET; /* request an interrupt */
|
|
|
|
if (interrupt_mask) /* if the interrupt mask is satisfied */
|
|
outbound_signals |= INTREQ; /* then assert the INTREQ signal */
|
|
break;
|
|
|
|
|
|
case DRESETINT:
|
|
dibptr->interrupt_active = CLEAR; /* reset the interrupt active flip-flop */
|
|
break;
|
|
|
|
|
|
case DSETMASK:
|
|
if (dibptr->interrupt_mask == INTMASK_E) /* if the mask is always enabled */
|
|
interrupt_mask = SET; /* then set the mask flip-flop */
|
|
else /* otherwise */
|
|
interrupt_mask = D_FF (dibptr->interrupt_mask /* set the mask flip-flop if the mask bit */
|
|
& inbound_value); /* is present in the mask value */
|
|
|
|
if (interrupt_mask && dibptr->interrupt_request) /* if the mask is enabled and a request is pending */
|
|
outbound_signals |= INTREQ; /* then assert the INTREQ signal */
|
|
break;
|
|
|
|
|
|
case DCONTSTB:
|
|
dprintf (ds_dev, DEB_CSRW, "Control is %s\n",
|
|
fmt_bitset (inbound_value, control_format));
|
|
|
|
if (inbound_value & CN_MR) /* if the master reset bit is set */
|
|
master_reset (); /* then reset the interface */
|
|
|
|
if (inbound_value & CN_RIN) /* if the reset interrupt bit is set */
|
|
dibptr->interrupt_request = CLEAR; /* then clear the interrupt request */
|
|
|
|
test_mode = D_FF (inbound_value & CN_TEST); /* set the test mode flip-flop from the test bit */
|
|
break;
|
|
|
|
|
|
case PSTATSTB:
|
|
case DSTATSTB:
|
|
outbound_value = status_word; /* get the controller status */
|
|
|
|
if (sio_busy == CLEAR && sel_is_idle) /* if the interface and channel are inactive */
|
|
outbound_value |= ST_SIO_OK; /* then add the SIO OK status bit */
|
|
|
|
if (test_mode == SET) /* if test mode is enabled */
|
|
outbound_value |= ST_TEST; /* then add the DIO OK status bit */
|
|
|
|
if (dibptr->interrupt_request == SET) /* if an interrupt request is pending */
|
|
outbound_value |= ST_INTREQ; /* then add the IRQ status bit */
|
|
|
|
dprintf (ds_dev, DEB_CSRW, "Status is %s%s | unit %u\n",
|
|
fmt_bitset (outbound_value, status_format),
|
|
dl_status_name (ST_TO_STATUS (outbound_value)),
|
|
ST_TO_UNIT (outbound_value));
|
|
break;
|
|
|
|
|
|
case DREADSTB:
|
|
outbound_value = buffer_word; /* return the data buffer register value */
|
|
|
|
dprintf (ds_dev, DEB_CSRW, "Buffer value %06o returned\n",
|
|
outbound_value);
|
|
break;
|
|
|
|
|
|
case DWRITESTB:
|
|
dprintf (ds_dev, DEB_CSRW, "Buffer value %06o set\n",
|
|
inbound_value);
|
|
|
|
buffer_word = inbound_value; /* set the data buffer register value */
|
|
break;
|
|
|
|
|
|
case DSTARTIO:
|
|
dprintf (ds_dev, DEB_CSRW, "Channel program started\n");
|
|
|
|
sio_busy = SET; /* set the SIO busy flip-flop */
|
|
flags &= ~INTOK; /* and clear the interrupt OK flag */
|
|
|
|
sel_assert_REQ (dibptr); /* request the channel */
|
|
break;
|
|
|
|
|
|
case TOGGLESIOOK:
|
|
TOGGLE (sio_busy); /* set or clear the SIO busy flip-flop */
|
|
|
|
if (sio_busy == CLEAR) { /* if the flip-flop was cleared */
|
|
deny_sio_busy (); /* then reset the associated devices */
|
|
|
|
dprintf (ds_dev, DEB_CSRW, "Channel program ended\n");
|
|
|
|
call_controller (NULL); /* check for drive attention held off by INTOK denied */
|
|
}
|
|
break;
|
|
|
|
|
|
case TOGGLEINXFER:
|
|
TOGGLE (input_xfer); /* set or clear the input transfer flip-flop */
|
|
|
|
if (input_xfer == SET) /* if the transfer is starting */
|
|
call_controller (NULL); /* then let the controller know to output the first word */
|
|
|
|
else if (end_of_data == SET) /* otherwise if EOT is asserted */
|
|
flags |= EOD; /* then PREADSTB has cleared DTRDY */
|
|
break;
|
|
|
|
|
|
case TOGGLEOUTXFER:
|
|
TOGGLE (output_xfer); /* set or clear the output transfer flip-flop */
|
|
|
|
if (output_xfer == SET) /* if the transfer is starting */
|
|
device_sr = SET; /* then request the first word from the channel */
|
|
break;
|
|
|
|
|
|
case PCMD1:
|
|
data_wait = D_FF (inbound_value & CN_WAIT); /* set the wait flip-flop from the supplied value */
|
|
|
|
if (data_wait == SET) /* if the wait flip-flip is set */
|
|
flags |= DTRDY; /* then the data ready flag is forced true */
|
|
|
|
device_sr = SET; /* request the second control word */
|
|
|
|
dprintf (ds_dev, DEB_CSRW, "Control is %s wait\n",
|
|
(data_wait == SET ? "set" : "clear"));
|
|
break;
|
|
|
|
|
|
case PCONTSTB:
|
|
dprintf (ds_dev, DEB_CSRW, "Control is %06o (%s)\n",
|
|
inbound_value, dl_opcode_name (MAC, CN_OPCODE (inbound_value)));
|
|
|
|
buffer_word = inbound_value; /* store the command in the data buffer register */
|
|
flags |= CMRDY; /* and set the command ready flag */
|
|
|
|
call_controller (NULL); /* tell the controller to start the command */
|
|
break;
|
|
|
|
|
|
case PREADSTB:
|
|
outbound_value = buffer_word; /* return the data buffer register value */
|
|
flags ^= DTRDY; /* and toggle (clear) the data ready flag */
|
|
|
|
call_controller (NULL); /* tell the controller that the buffer is empty */
|
|
break;
|
|
|
|
|
|
case PWRITESTB:
|
|
buffer_word = inbound_value; /* save the word to write */
|
|
flags ^= DTRDY; /* and toggle (set) the data ready flag */
|
|
|
|
if (inbound_signals & TOGGLEOUTXFER) /* EOT asserted with TOGGLEOUTXFER */
|
|
end_of_data = SET; /* sets the End of Data flip-flop */
|
|
|
|
call_controller (NULL); /* tell the controller that the buffer is full */
|
|
break;
|
|
|
|
|
|
case EOT:
|
|
if (inbound_signals & TOGGLEINXFER) /* EOT asserted with TOGGLEINXFER */
|
|
end_of_data = SET; /* sets the End of Data 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 XFERERROR:
|
|
case PFWARN:
|
|
dprintf (ds_dev, DEB_CSRW, "Channel program aborted\n");
|
|
|
|
flags |= XFRNG; /* set the transfer error flag */
|
|
clear_interface_logic (dibptr); /* and clear the interface to abort the transfer */
|
|
break;
|
|
|
|
|
|
case SETJMP:
|
|
if (jump_met == SET) /* if the jump met flip-flop is set */
|
|
outbound_signals |= JMPMET; /* then assert the JMPMET signal */
|
|
|
|
jump_met = CLEAR; /* reset the flip-flop */
|
|
break;
|
|
|
|
|
|
case CHANSO:
|
|
if (device_end == SET) { /* if the device end flip-flop is set */
|
|
outbound_signals |= DEVEND | CHANSR; /* then assert DEVEND and CHANSR to the channel */
|
|
|
|
device_end = D_FF (input_xfer | output_xfer); /* clear device end if the transfer has stopped */
|
|
}
|
|
|
|
else if (device_sr == SET || test_mode == SET) /* if the interface requests service */
|
|
outbound_signals |= CHANSR; /* then assert CHANSR to the channel */
|
|
|
|
outbound_signals |= CHANACK; /* assert CHANACK to acknowledge the signal */
|
|
break;
|
|
|
|
|
|
case READNEXTWD: /* not used by this interface */
|
|
case ACKSR: /* not used by this interface */
|
|
case DEVNODB: /* not used by this interface */
|
|
case TOGGLESR: /* not used by this interface */
|
|
break;
|
|
}
|
|
|
|
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
|
|
}
|
|
|
|
dprintf (ds_dev, 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 a controller or drive unit.
|
|
|
|
The service routine is called to execute scheduled controller command phases
|
|
for the specified unit. The actions to be taken depend on the current state
|
|
of the controller and the drive unit.
|
|
|
|
This routine is entered for three general reasons:
|
|
|
|
1. A disc unit is ready to execute the next command phase.
|
|
|
|
2. The controller unit is ready to execute the next command phase.
|
|
|
|
3. The controller unit has timed out while waiting for a new command.
|
|
|
|
Generally, the controller library handles all of the disc operations. All
|
|
that is necessary is to notify the controller, which will process the next
|
|
phase of command execution. Because the controller can overlap operations,
|
|
in particular scheduling seeks on several drive units simultaneously, each
|
|
drive unit carries its own current operation code and execution phase. The
|
|
controller uses these to determine what to do next.
|
|
*/
|
|
|
|
static t_stat ds_service (UNIT *uptr)
|
|
{
|
|
dprintf (ds_dev, DL_DEB_SERV, (uptr == &ds_cntlr
|
|
? "Controller unit service entered\n"
|
|
: "Unit %d service entered\n"),
|
|
(int32) (uptr - &ds_unit [0]));
|
|
|
|
call_controller (uptr); /* call the controller */
|
|
|
|
if (device_sr == SET) /* if the interface requests service */
|
|
sel_assert_CHANSR (&ds_dib); /* then assert CHANSR to the channel */
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Device reset routine.
|
|
|
|
This routine is called for a RESET, RESET DS, or BOOT DS 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 the programmed master reset. In
|
|
addition, if a power-on reset (RESET -P) is done, the original FASTTIME
|
|
settings are restored.
|
|
*/
|
|
|
|
static t_stat ds_reset (DEVICE *dptr)
|
|
{
|
|
master_reset (); /* perform a master reset */
|
|
|
|
if (sim_switches & SWMASK ('P')) { /* if this is a power-on reset */
|
|
fast_times.seek_one = DS_SEEK_ONE; /* then reset the track-to-track seek time, */
|
|
fast_times.seek_full = DS_SEEK_FULL; /* the full-stroke seek time, */
|
|
fast_times.sector_full = DS_SECTOR_FULL; /* the full-sector rotation time, */
|
|
fast_times.data_xfer = DS_DATA_XFER; /* the per-word data transfer time, */
|
|
fast_times.intersector_gap = DS_ISG; /* the intersector gap time, */
|
|
fast_times.overhead = DS_OVERHEAD; /* and the controller execution overhead */
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Device boot routine.
|
|
|
|
This routine is called for the BOOT DS command to initiate the system cold
|
|
load procedure for the disc. It is the simulation equivalent to presetting
|
|
the System Switch Register to the appropriate control and device number bytes
|
|
and then pressing the ENABLE+LOAD front panel switches.
|
|
|
|
For this interface, the switch register is set to %0000nn, where "nn"
|
|
is the current disc interface device number, which defaults to 4. The
|
|
control byte is 0 (Cold Load Read).
|
|
|
|
The cold load procedure always uses unit 0.
|
|
*/
|
|
|
|
static t_stat ds_boot (int32 unit_number, DEVICE *dptr)
|
|
{
|
|
if (unit_number != 0) /* if a unit other than 0 is specified */
|
|
return SCPE_ARG; /* then fail with an invalid argument error */
|
|
|
|
else { /* otherwise */
|
|
cpu_front_panel (TO_WORD (Cold_Load_Read, /* set up the cold load */
|
|
ds_dib.device_number), /* from disc unit 0 */
|
|
Cold_Load);
|
|
|
|
return SCPE_OK; /* return to run the bootstrap */
|
|
}
|
|
}
|
|
|
|
|
|
/* Attach a disc image file to a drive unit.
|
|
|
|
The specified file is attached to the indicated drive unit. This is the
|
|
simulation equivalent to inserting a disc pack into the drive and setting
|
|
the RUN/STOP switch to RUN, which will load the heads and set the First
|
|
Status and Attention bits in the drive status.
|
|
|
|
The controller library routine handles command validation and setting the
|
|
appropriate drive unit status. It will return an error code if the command
|
|
fails. Otherwise, it will return SCPE_INCOMP if the command must be
|
|
completed with a controller call or SCPE_OK if the command is complete. If
|
|
the controller is idle, a call will be needed to poll the drives for
|
|
attention; otherwise, the drives will be polled the next time the controller
|
|
becomes idle.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. If we are called during a RESTORE command to reattach a file previously
|
|
attached when the simulation was SAVEd, the unit status will not be
|
|
changed by the controller, so the unit will not request attention.
|
|
*/
|
|
|
|
static t_stat ds_attach (UNIT *uptr, CONST char *cptr)
|
|
{
|
|
t_stat result;
|
|
|
|
result = dl_attach (&mac_cntlr, uptr, cptr); /* attach the drive */
|
|
|
|
if (result == SCPE_INCOMP) { /* if the controller must be called before returning */
|
|
call_controller (NULL); /* then let it know to poll the drives */
|
|
return SCPE_OK; /* before returning with success */
|
|
}
|
|
|
|
else /* otherwise */
|
|
return result; /* return the status of the attach */
|
|
}
|
|
|
|
|
|
/* Detach a disc image file from a drive unit.
|
|
|
|
The specified file is detached from the indicated drive unit. This is the
|
|
simulation equivalent to setting the RUN/STOP switch to STOP and removing the
|
|
disc pack from the drive. Stopping the drive will unload the heads and set
|
|
the Attention bit in the drive status.
|
|
|
|
The controller library routine handles command validation and setting the
|
|
appropriate drive unit status. It will return an error code if the command
|
|
fails. Otherwise, it will return SCPE_INCOMP if the command must be
|
|
completed with a controller call or SCPE_OK if the command is complete. If
|
|
the controller is idle, a call will be needed to poll the drives for
|
|
attention; otherwise, the drives will be polled the next time the controller
|
|
becomes idle.
|
|
*/
|
|
|
|
static t_stat ds_detach (UNIT *uptr)
|
|
{
|
|
t_stat result;
|
|
|
|
result = dl_detach (&mac_cntlr, uptr); /* detach the drive */
|
|
|
|
if (result == SCPE_INCOMP) { /* if the controller must be called before returning */
|
|
call_controller (NULL); /* then let it know to poll the drives */
|
|
return SCPE_OK; /* before returning with success */
|
|
}
|
|
|
|
else /* otherwise */
|
|
return result; /* return the status of the detach */
|
|
}
|
|
|
|
|
|
/* Load or unload the drive heads.
|
|
|
|
The SET DSn UNLOADED command simulates setting the hardware RUN/STOP switch
|
|
to STOP. The heads are unloaded, and the drive is spun down.
|
|
|
|
The SET DSn LOADED command simulates setting the switch to RUN. The drive is
|
|
spun up, and the heads are loaded. Loading fails if there is no pack in the
|
|
drive, i.e., if the unit is not attached to a disc image file.
|
|
|
|
The controller library routine handles command validation and setting the
|
|
appropriate drive unit status. It will return an error code if the command
|
|
fails. Otherwise, it will return SCPE_INCOMP if the command must be
|
|
completed with a controller call or SCPE_OK if the command is complete. If
|
|
the controller is idle, a call will be needed to poll the drives for
|
|
attention; otherwise, the drives will be polled the next time the controller
|
|
becomes idle.
|
|
*/
|
|
|
|
static t_stat ds_load_unload (UNIT *uptr, int32 value, CONST char *cptr, void *desc)
|
|
{
|
|
const t_bool load = (value != UNIT_UNLOAD); /* TRUE if the heads are loading */
|
|
t_stat result;
|
|
|
|
result = dl_load_unload (&mac_cntlr, uptr, load); /* load or unload the heads */
|
|
|
|
if (result == SCPE_INCOMP) { /* if the controller must be called before returning */
|
|
call_controller (NULL); /* then let it know to poll the drives */
|
|
return SCPE_OK; /* before returning with success */
|
|
}
|
|
|
|
else /* otherwise */
|
|
return result; /* return the status of the load or unload */
|
|
}
|
|
|
|
|
|
|
|
/* Interface local utility routines */
|
|
|
|
|
|
|
|
/* Master reset.
|
|
|
|
A master reset is generated either by an IORESET signal or a Programmed
|
|
Master Clear (CIO with bit 0 set). It initializes the interface to its idle
|
|
state. In addition, if jumper W1 (PRESET_ENABLE) is set, it asserts the
|
|
CLEAR flag to the disc controller to perform a hard clear.
|
|
*/
|
|
|
|
static void master_reset (void)
|
|
{
|
|
interrupt_mask = SET; /* set the interrupt mask */
|
|
|
|
ds_dib.interrupt_request = CLEAR; /* clear any current */
|
|
ds_dib.interrupt_active = CLEAR; /* interrupt request */
|
|
|
|
sio_busy = CLEAR; /* clear the SIO busy */
|
|
input_xfer = CLEAR; /* input transfer */
|
|
output_xfer = CLEAR; /* and output transfer flip-flops */
|
|
data_overrun = CLEAR; /* clear the data overrun */
|
|
end_of_data = CLEAR; /* end of data */
|
|
test_mode = CLEAR; /* and test mode flip-flops */
|
|
|
|
deny_sio_busy (); /* clear the logic affected by SIO Busy */
|
|
|
|
flags &= ~XFRNG; /* clear the transfer error flag */
|
|
|
|
status_word = 0; /* clear the status register */
|
|
|
|
if (PRESET_ENABLE) { /* if jumper W1 (preset) is set to "E" */
|
|
flags |= CLEARF; /* then assert CLEAR */
|
|
call_controller (NULL); /* to the controller */
|
|
flags &= ~CLEARF; /* to do a hard clear */
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Deny SIO busy.
|
|
|
|
The internal SIO Busy signal enables a number of logic devices on the
|
|
interface associated with SIO channel transfers. When SIO Busy is denied,
|
|
those devices are set or cleared as appropriate in preparation for the next
|
|
SIO program.
|
|
*/
|
|
|
|
static void deny_sio_busy (void)
|
|
{
|
|
device_sr = CLEAR; /* clear the service request, */
|
|
jump_met = CLEAR; /* jump met, */
|
|
data_wait = CLEAR; /* and wait flip-flops */
|
|
|
|
retry_counter = 0; /* clear the retry counter */
|
|
|
|
flags = flags & ~(CMRDY | DTRDY) | INTOK | EOD; /* clear CMRDY and DTRDY and set INTOK and EOD flags */
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Clear interface logic.
|
|
|
|
The clear interface logic signal is asserted during channel operation either
|
|
when the interface requests an interrupt or the channel indicates a transfer
|
|
failure by asserting XFERERROR. It clears the SIO Busy, Input Transfer, and
|
|
Output Transfer flip-flops, pulses the REQ line to abort the channel program,
|
|
and sends EOD to the disc controller to abort any in-progress data transfer.
|
|
|
|
The signal is inhibited when an SIO program is not active.
|
|
*/
|
|
|
|
static void clear_interface_logic (DIB *dibptr)
|
|
{
|
|
if (sio_busy == SET) { /* if a channel program is in progress */
|
|
sio_busy = CLEAR; /* then clear the SIO busy */
|
|
input_xfer = CLEAR; /* input transfer */
|
|
output_xfer = CLEAR; /* and output transfer flip-flops */
|
|
|
|
end_of_data = SET; /* set the end of data flip-flop */
|
|
|
|
deny_sio_busy (); /* deny the SIO Busy signal */
|
|
|
|
sel_assert_REQ (dibptr); /* abort the channel program */
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Call the disc controller.
|
|
|
|
The 13037 disc controller connects to CPU interfaces via a 16-bit data bus, a
|
|
6-bit flag bus, a 4-bit function bus, and five additional control signals.
|
|
The controller continuously monitors the flag bus and reacts to the interface
|
|
changing the flag states by placing or accepting data on the data bus and
|
|
issuing commands to the interface via the function bus. The controller
|
|
supports up to eight CPU interfaces simultaneously, and provision is made to
|
|
poll each interface in turn via select and disconnect functions. An
|
|
interface only responds if it is currently selected.
|
|
|
|
In simulation, a call to the dl_controller routine informs the controller of
|
|
a (potential) change in flag state. The current set of flags and data bus
|
|
value are supplied, and the controller returns a combined set of functions
|
|
and a data bus value.
|
|
|
|
The controller must be called any time there is a change in the state of the
|
|
interface or the drive units. Generally, the cases that require notification
|
|
are when the interface:
|
|
|
|
- has a new command to execute
|
|
- has a new data word available to send
|
|
- has obtained the last data word received
|
|
- has received a unit service event notification
|
|
- has detected insertion or removal of a disc pack from a drive
|
|
- has detected loading or unloading of a drive's heads
|
|
- wants to hard-clear the controller
|
|
|
|
The set of returned functions is processed sequentially, updating the
|
|
interface state as indicated. Some functions are not used by this interface,
|
|
so they are masked off before processing to improve performance.
|
|
|
|
Disc commands may be "stacked" on the interface by asserting PCONTSTB to
|
|
store the new command into the data buffer register while the controller is
|
|
still busy with the previous command. This will assert CMRDY (if not in test
|
|
mode), but the controller will not react to this signal until it finishes the
|
|
current command. For example, a channel program containing the Wakeup and
|
|
End commands will transmit the End before the Wakeup completes. This occurs
|
|
because Wakeup asserts IFGTC to clear the command (and thereby asserts CHANSR
|
|
to allow the channel to continue) about 18 microseconds before the controller
|
|
completes the command and returns to the wait loop. In simulation, an
|
|
explicit check is made when a command completes. If a stacked command is
|
|
seen, the controller is called again to start it.
|
|
|
|
Because the disc is a synchronous device, overrun or underrun can occur if
|
|
the interface is not ready when the controller must transfer data. There are
|
|
four conditions that lead to an overrun or underrun:
|
|
|
|
1. The controller is ready with a disc read word (IFCLK * IFIN), but the
|
|
interface buffer is full (DTRDY).
|
|
|
|
2. The controller needs a disc write word (IFCLK * IFOUT), but the interface
|
|
buffer is empty (~DTRDY).
|
|
|
|
3. The CPU attempts to read a word, but the interface buffer is empty
|
|
(~DTRDY).
|
|
|
|
4. The CPU attempts to write a word, but the interface buffer is full
|
|
(DTRDY).
|
|
|
|
The hardware design of the interface prevents the last two conditions, as the
|
|
interface will assert CHANSR only when the buffer is full (read) or empty
|
|
(write). The interface does detect the first two conditions and sets the
|
|
data overrun flip-flop if either occurs.
|
|
|
|
Implementation notes:
|
|
|
|
1. In hardware, OVRUN will be asserted when the controller requests write
|
|
data when the buffer is empty. In simulation, OVRUN will not be asserted
|
|
when the controller is called with the empty buffer; instead, it will be
|
|
asserted for the next controller call. Because the controller will be
|
|
called for the intersector phase, and because OVRUN isn't checked until
|
|
that point, this "late" assertion does not affect overrun detection.
|
|
|
|
2. In hardware, the data ready flip-flop is toggled as a result of reading
|
|
or writing a word from or to the controller. We follow that practice
|
|
here, rather than setting or clearing it, which would be more
|
|
appropriate.
|
|
|
|
3. The hardware interface decodes the DSCIF and SELIF functions to allow the
|
|
controller to be shared by two or more CPUs. In simulation, these
|
|
functions are ignored, as the simulator supports only one CPU connected
|
|
to the interface.
|
|
*/
|
|
|
|
static void call_controller (UNIT *uptr)
|
|
{
|
|
CNTLR_IFN_IBUS result;
|
|
CNTLR_IFN_SET command_set;
|
|
CNTLR_IFN command;
|
|
CNTLR_FLAG_SET flag_set;
|
|
|
|
if (data_overrun == SET && (flags & XFRNG) == NO_FLAGS) /* if an overrun occurred without a transfer error */
|
|
flags |= OVRUN; /* then tell the controller */
|
|
|
|
if (test_mode == SET) /* if in test mode */
|
|
flag_set = flags & CLEARF; /* then all flags except CLEAR are inhibited */
|
|
else /* otherwise */
|
|
flag_set = flags; /* present the full set of flags to the controller */
|
|
|
|
|
|
do { /* call the controller potentially more than once */
|
|
result = /* to start or continue a command */
|
|
dl_controller (&mac_cntlr, uptr, flag_set, (CNTLR_IBUS) buffer_word);
|
|
|
|
command_set = DLIFN (result) & ~UNUSED_COMMANDS; /* strip the commands we don't use as an efficiency */
|
|
|
|
while (command_set) { /* process the set of returned interface commands */
|
|
command = DLNEXTIFN (command_set); /* isolate the next command */
|
|
|
|
switch (command) { /* dispatch an interface command */
|
|
|
|
case IFIN: /* Interface In */
|
|
if (flags & DTRDY) /* if the buffer is still full */
|
|
data_overrun = SET; /* then this input overruns it */
|
|
|
|
else { /* otherwise the buffer is empty */
|
|
device_sr = D_FF (! end_of_data); /* so request the next word unless EOT */
|
|
|
|
if ((input_xfer == CLEAR /* if not configured to read */
|
|
|| output_xfer == SET) /* or configured to write */
|
|
&& (flags & EOD) == NO_FLAGS) /* and the transfer is active */
|
|
flags |= XFRNG; /* then set the transfer is no good */
|
|
}
|
|
|
|
buffer_word = DLIBUS (result); /* store the data word in the buffer */
|
|
flags ^= DTRDY; /* and toggle (set) the data ready flag */
|
|
break;
|
|
|
|
|
|
case IFOUT: /* Interface Out */
|
|
if ((flags & DTRDY) == NO_FLAGS) /* if the buffer is empty */
|
|
data_overrun = SET; /* then this output underruns it */
|
|
|
|
if (end_of_data == SET) /* if this is the last transfer */
|
|
flags |= EOD; /* then tell the controller */
|
|
|
|
else { /* otherwise the transfer continues */
|
|
device_sr = SET; /* so request the next word */
|
|
|
|
if ((output_xfer == CLEAR /* if not configured to write */
|
|
|| input_xfer == SET) /* or configured to read */
|
|
&& (flags & EOD) == NO_FLAGS) /* and the transfer is active */
|
|
flags |= XFRNG; /* then set the transfer is no good */
|
|
}
|
|
|
|
flags ^= DTRDY; /* toggle (clear) the data ready flag */
|
|
break;
|
|
|
|
|
|
case IFGTC: /* Interface Get Command */
|
|
flags &= ~(CMRDY | DTRDY | EOD | OVRUN); /* clear the interface transfer flags */
|
|
|
|
end_of_data = CLEAR; /* clear the end-of-data */
|
|
data_overrun = CLEAR; /* and data-overrun flip-flops */
|
|
|
|
device_sr = SET; /* request channel service */
|
|
break;
|
|
|
|
|
|
case RQSRV: /* Request Service */
|
|
flags &= ~(EOD | OVRUN); /* clear the end of data and data overrun flags */
|
|
|
|
end_of_data = CLEAR; /* clear the */
|
|
data_overrun = CLEAR; /* corresponding flip-flops */
|
|
|
|
device_sr = SET; /* request channel service */
|
|
break;
|
|
|
|
|
|
case SRTRY: /* Set Retry */
|
|
retry_counter = DLIBUS (result); /* store the data value into the retry counter */
|
|
break;
|
|
|
|
|
|
case DVEND: /* Device End */
|
|
device_end = SET; /* set the device end */
|
|
jump_met = SET; /* and the "jump met condition" flip-flops */
|
|
|
|
if (retry_counter > 0) { /* if retries remain */
|
|
retry_counter = retry_counter - 1; /* then decrement the retry counter */
|
|
break; /* and try again */
|
|
}
|
|
/* otherwise, request an interrupt */
|
|
/* fall through into the STINT case */
|
|
|
|
case STINT: /* Set Interrupt */
|
|
flags &= ~XFRNG; /* clear the transfer error flag */
|
|
|
|
clear_interface_logic (&ds_dib); /* clear the interface to abort the transfer */
|
|
|
|
ds_dib.interrupt_request = SET; /* set the request flip-flop */
|
|
|
|
if (interrupt_mask) /* if the interrupt mask is satisfied */
|
|
iop_assert_INTREQ (&ds_dib); /* then assert the INTREQ signal */
|
|
break;
|
|
|
|
|
|
case WRTIO: /* Write TIO */
|
|
status_word = DLIBUS (result) & ST_MASK; /* save the value without the SPD bits for TIO */
|
|
break;
|
|
|
|
|
|
case DSCIF: /* not used by this simulation */
|
|
case SELIF: /* not used by this simulation */
|
|
break;
|
|
|
|
case BUSY: /* not decoded by this interface */
|
|
case IFPRF: /* not decoded by this interface */
|
|
case FREE: /* not decoded by this interface */
|
|
case STDFL: /* not decoded by this interface */
|
|
break;
|
|
}
|
|
|
|
command_set &= ~command; /* remove the current command from the set */
|
|
} /* and continue with the remaining commands */
|
|
}
|
|
while (flags & CMRDY /* call the controller again if a command is pending */
|
|
&& result & FREE /* and a prior command just completed */
|
|
&& test_mode == CLEAR); /* and not in test mode, which inhibits CMRDY */
|
|
|
|
return;
|
|
}
|