1684 lines
85 KiB
C
1684 lines
85 KiB
C
/* hp2100_ds.c: HP 13037D/13175D disc controller/interface simulator
|
|
|
|
Copyright (c) 2004-2012, Robert M. Supnik
|
|
Copyright (c) 2012-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
|
|
AUTHORS 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 names of the authors 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 authors.
|
|
|
|
DS 13037D/13175D disc controller/interface
|
|
|
|
13-Jun-18 JDB Revised I/O model
|
|
07-May-18 JDB Removed "dl_clear_controller" status return
|
|
27-Feb-18 JDB Added the BMDL
|
|
21-Feb-18 JDB ATTACH -N now creates a full-size disc image
|
|
11-Jul-17 JDB Renamed "ibl_copy" to "cpu_ibl"
|
|
15-Mar-17 JDB Trace flags are now global
|
|
Changed DEBUG_PRI calls to tprintfs
|
|
10-Mar-17 JDB Added IOBUS to the debug table
|
|
09-Mar-17 JDB Deprecated LOCKED/WRITEENABLED for PROTECT/UNPROTECT
|
|
13-May-16 JDB Modified for revised SCP API function parameter types
|
|
04-Mar-16 JDB Name changed to "hp2100_disclib" until HP 3000 integration
|
|
30-Dec-14 JDB Added S-register parameters to ibl_copy
|
|
24-Dec-14 JDB Use T_ADDR_FMT with t_addr values for 64-bit compatibility
|
|
18-Mar-13 JDB Fixed poll_drives definition to match declaration
|
|
24-Oct-12 JDB Changed CNTLR_OPCODE to title case to avoid name clash
|
|
29-Mar-12 JDB Rewritten to use the MAC/ICD disc controller library
|
|
ioIOO now notifies controller service of parameter output
|
|
14-Feb-12 JDB Corrected SRQ generation and FIFO under/overrun detection
|
|
Corrected Clear command to conform to the hardware
|
|
Fixed Request Status to return Unit Unavailable if illegal
|
|
Seek and Cold Load Read now Seek Check if seek in progress
|
|
Remodeled command wait for seek completion
|
|
10-Feb-12 JDB Deprecated DEVNO in favor of SC
|
|
21-Jun-11 JDB Corrected status returns for disabled drive, auto-seek
|
|
beyond drive limits, Request Sector Address and Wakeup
|
|
with invalid or offline unit
|
|
Address verification reenabled if auto-seek during
|
|
Read Without Verify
|
|
28-Mar-11 JDB Tidied up signal handling
|
|
26-Oct-10 JDB Changed I/O signal handler for revised signal model
|
|
26-Jun-08 JDB Rewrote device I/O to model backplane signals
|
|
31-Dec-07 JDB Corrected and verified ioCRS action
|
|
20-Dec-07 JDB Corrected DPTR register definition from FLDATA to DRDATA
|
|
28-Dec-06 JDB Added ioCRS state to I/O decoders
|
|
03-Aug-06 JDB Fixed REQUEST STATUS command to clear status-1
|
|
Removed redundant attached test in "ds_detach"
|
|
18-Mar-05 RMS Added attached test to detach routine
|
|
01-Mar-05 JDB Added SET UNLOAD/LOAD
|
|
|
|
References:
|
|
- 13037 Disc Controller Technical Information Package
|
|
(13037-90902, August 1980)
|
|
- 7925D Disc Drive Service Manual
|
|
(07925-90913, April 1984)
|
|
- HP 12992 Loader ROMs Installation Manual
|
|
(12992-90001, April 1986)
|
|
- DVR32 RTE Moving Head Driver source
|
|
(92084-18711, Revision 5000)
|
|
|
|
|
|
The 13037D multiple-access (MAC) disc controller supports from one to eight
|
|
HP 7905 (15 MB), 7906 (20MB), 7920 (50 MB), and 7925 (120 MB) disc drives
|
|
accessed by one to eight CPUs. The controller hardware consists of a 16-bit
|
|
microprogrammed processor constructed from 74S181 bit slices operating at 5
|
|
MHz, a device controller providing the interconnections to the drives and CPU
|
|
interfaces, and an error correction controller that enables the correction of
|
|
up to 32-bit error bursts. 1024 words of 24-bit firmware are stored in ROM.
|
|
|
|
The 13175D disc interface is used to connect the HP 1000 CPU to the 13037
|
|
device controller. In a multiple-CPU system, one interface is strapped to
|
|
reset the controller when the CPU's front panel PRESET button is pressed.
|
|
|
|
This module simulates a 13037D connected to a single 13175D interface. 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.
|
|
|
|
This simulator is an adaptation of the code originally written by Bob Supnik.
|
|
The functions of the controller have been separated from the functions of the
|
|
interface, with the former placed into a separate disc controller library.
|
|
This allows the library to support other CPU interfaces, such as the 12821A
|
|
HP-IB disc interface, that use substantially different communication
|
|
protocols. The library functions implement the controller command set for
|
|
the drive units. The interface functions handle the transfer of commands and
|
|
data to and from the CPU.
|
|
|
|
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.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. Although the 13175D has a 16-word FIFO, the "full" level is set at 5
|
|
entries in hardware to avoid a long DCPC preemption time at the start of
|
|
a disc write as the FIFO fills.
|
|
*/
|
|
|
|
|
|
|
|
#include "hp2100_defs.h"
|
|
#include "hp2100_io.h"
|
|
#include "hp2100_disclib.h"
|
|
|
|
|
|
|
|
/* Program constants */
|
|
|
|
#define DS_DRIVES (DL_MAXDRIVE + 1) /* number of disc drive units */
|
|
#define DS_UNITS (DS_DRIVES + DL_AUXUNITS) /* total number of units */
|
|
|
|
#define ds_cntlr ds_unit [DL_MAXDRIVE + 1] /* controller unit alias */
|
|
|
|
#define FIFO_SIZE 16 /* FIFO depth */
|
|
|
|
#define FIFO_EMPTY (ds.fifo_count == 0) /* FIFO empty test */
|
|
#define FIFO_STOP (ds.fifo_count >= 5) /* FIFO stop filling test */
|
|
#define FIFO_FULL (ds.fifo_count == FIFO_SIZE) /* FIFO full test */
|
|
|
|
#define PRESET_ENABLE TRUE /* Preset Jumper (W4) is enabled */
|
|
|
|
|
|
/* Per-card state variables */
|
|
|
|
typedef struct {
|
|
FLIP_FLOP control; /* control flip-flop */
|
|
FLIP_FLOP flag; /* flag flip-flop */
|
|
FLIP_FLOP flag_buffer; /* flag buffer flip-flop */
|
|
FLIP_FLOP srq; /* SRQ flip-flop */
|
|
FLIP_FLOP edt; /* EDT flip-flop */
|
|
FLIP_FLOP cmfol; /* command follows flip-flop */
|
|
FLIP_FLOP cmrdy; /* command ready flip-flop */
|
|
uint16 fifo [FIFO_SIZE]; /* FIFO buffer */
|
|
uint32 fifo_count; /* FIFO occupancy counter */
|
|
REG *fifo_reg; /* FIFO register pointer */
|
|
} CARD_STATE;
|
|
|
|
|
|
/* MAC disc state variables */
|
|
|
|
static UNIT ds_unit [DS_UNITS]; /* unit array */
|
|
|
|
static CARD_STATE ds; /* card state */
|
|
|
|
static uint16 buffer [DL_BUFSIZE]; /* command/status/sector buffer */
|
|
|
|
static CNTLR_VARS mac_cntlr = /* MAC controller */
|
|
{ CNTLR_INIT (MAC, buffer, &ds_cntlr) };
|
|
|
|
|
|
|
|
/* Interface local SCP support routines */
|
|
|
|
static INTERFACE ds_interface;
|
|
|
|
|
|
/* Interface local SCP support routines */
|
|
|
|
static t_stat ds_service_drive (UNIT *uptr);
|
|
static t_stat ds_service_controller (UNIT *uptr);
|
|
static t_stat ds_service_timer (UNIT *uptr);
|
|
static t_stat ds_reset (DEVICE *dptr);
|
|
static t_stat ds_attach (UNIT *uptr, CONST char *cptr);
|
|
static t_stat ds_detach (UNIT *uptr);
|
|
static t_stat ds_boot (int32 unitno, DEVICE *dptr);
|
|
static t_stat ds_load_unload (UNIT *uptr, int32 value, CONST char *cptr, void *desc);
|
|
|
|
|
|
/* MAC disc local utility routines */
|
|
|
|
static void start_command (void);
|
|
static void poll_interface (void);
|
|
static void poll_drives (void);
|
|
static void fifo_load (uint16 data);
|
|
static uint16 fifo_unload (void);
|
|
static void fifo_clear (void);
|
|
static t_stat activate_unit (UNIT *uptr);
|
|
|
|
|
|
/* Interface SCP data structures */
|
|
|
|
|
|
/* Device information block */
|
|
|
|
static DIB ds_dib = {
|
|
&ds_interface, /* the device's I/O interface function pointer */
|
|
DS, /* the device's select code (02-77) */
|
|
0, /* the card index */
|
|
"13175D Disc Controller Interface", /* the card description */
|
|
"12992B 7905/7906/7920/7925 Disc Loader" /* the ROM description */
|
|
};
|
|
|
|
|
|
/* Units list */
|
|
|
|
#define UNIT_FLAGS (UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD)
|
|
|
|
static UNIT ds_unit [] = {
|
|
{ UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* drive unit 0 */
|
|
{ UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* drive unit 1 */
|
|
{ UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* drive unit 2 */
|
|
{ UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* drive unit 3 */
|
|
{ UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* drive unit 4 */
|
|
{ UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* drive unit 5 */
|
|
{ UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* drive unit 6 */
|
|
{ UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* drive unit 7 */
|
|
{ UDATA (&ds_service_controller, UNIT_DIS, 0) }, /* controller unit */
|
|
{ UDATA (&ds_service_timer, UNIT_DIS, 0) } /* timer unit */
|
|
};
|
|
|
|
|
|
/* Register list */
|
|
|
|
static REG ds_reg [] = {
|
|
/* Macro Name Location Radix Width Offset Depth Flags */
|
|
/* ------ ------ ------------------------ ----- ---------- ------ ------------- ----------------- */
|
|
{ FLDATA (CMFOL, ds.cmfol, 0) },
|
|
{ FLDATA (CMRDY, ds.cmrdy, 0) },
|
|
{ DRDATA (FCNT, ds.fifo_count, 5) },
|
|
{ BRDATA (FIFO, ds.fifo, 8, 16, FIFO_SIZE), REG_CIRC },
|
|
{ ORDATA (FREG, ds.fifo_reg, 32), REG_HRO },
|
|
|
|
{ ORDATA (CNTYPE, mac_cntlr.type, 2), REG_HRO },
|
|
{ ORDATA (STATE, mac_cntlr.state, 2) },
|
|
{ ORDATA (OPCODE, mac_cntlr.opcode, 6) },
|
|
{ ORDATA (STATUS, mac_cntlr.status, 6) },
|
|
{ FLDATA (EOC, mac_cntlr.eoc, 0) },
|
|
{ FLDATA (EOD, mac_cntlr.eod, 0) },
|
|
{ ORDATA (SPDU, mac_cntlr.spd_unit, 16) },
|
|
{ ORDATA (FLMASK, mac_cntlr.file_mask, 4) },
|
|
{ ORDATA (RETRY, mac_cntlr.retry, 4), REG_HRO },
|
|
{ ORDATA (CYL, mac_cntlr.cylinder, 16) },
|
|
{ ORDATA (HEAD, mac_cntlr.head, 6) },
|
|
{ ORDATA (SECTOR, mac_cntlr.sector, 8) },
|
|
{ ORDATA (VFYCNT, mac_cntlr.verify_count, 16) },
|
|
{ ORDATA (LASPOL, mac_cntlr.poll_unit, 3) },
|
|
{ HRDATA (BUFPTR, mac_cntlr.buffer, 32), REG_HRO },
|
|
{ BRDATA (BUFFER, buffer, 8, 16, DL_BUFSIZE) },
|
|
{ DRDATA (INDEX, mac_cntlr.index, 8) },
|
|
{ DRDATA (LENGTH, mac_cntlr.length, 8) },
|
|
{ HRDATA (AUXPTR, mac_cntlr.aux, 32), REG_HRO },
|
|
{ DRDATA (STIME, mac_cntlr.seek_time, 24), PV_LEFT | REG_NZ },
|
|
{ DRDATA (ITIME, mac_cntlr.sector_time, 24), PV_LEFT | REG_NZ },
|
|
{ DRDATA (CTIME, mac_cntlr.cmd_time, 24), PV_LEFT | REG_NZ },
|
|
{ DRDATA (DTIME, mac_cntlr.data_time, 24), PV_LEFT | REG_NZ },
|
|
{ DRDATA (WTIME, mac_cntlr.wait_time, 31), PV_LEFT | REG_NZ },
|
|
|
|
{ FLDATA (CTL, ds.control, 0) },
|
|
{ FLDATA (FLG, ds.flag, 0) },
|
|
{ FLDATA (FBF, ds.flag_buffer, 0) },
|
|
{ FLDATA (SRQ, ds.srq, 0) },
|
|
{ FLDATA (EDT, ds.edt, 0) },
|
|
|
|
{ URDATA (UCYL, ds_unit[0].CYL, 10, 10, 0, DS_UNITS, PV_LEFT) },
|
|
{ URDATA (UOP, ds_unit[0].OP, 8, 6, 0, DS_UNITS, PV_RZRO) },
|
|
{ URDATA (USTAT, ds_unit[0].STAT, 2, 8, 0, DS_UNITS, PV_RZRO) },
|
|
{ URDATA (UPHASE, ds_unit[0].PHASE, 8, 3, 0, DS_UNITS, PV_RZRO) },
|
|
{ URDATA (UPOS, ds_unit[0].pos, 8, T_ADDR_W, 0, DS_UNITS, PV_LEFT) },
|
|
{ URDATA (UWAIT, ds_unit[0].wait, 8, 32, 0, DS_UNITS, PV_LEFT) },
|
|
|
|
DIB_REGS (ds_dib),
|
|
|
|
{ NULL }
|
|
};
|
|
|
|
|
|
/* Modifier list.
|
|
|
|
For the drive models, the modifiers provide this SHOW behavior:
|
|
|
|
- when detached and autosized, prints "autosize"
|
|
- when detached and not autosized, prints the model number
|
|
- when attached, prints the model number (regardless of autosizing)
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The validation routine does not allow the model number or autosizing
|
|
option to be changed when the unit is attached. Therefore, specifying
|
|
UNIT_ATT in the mask field has no adverse effect.
|
|
|
|
2. The modifier DEVNO is deprecated in favor of SC but is retained for
|
|
compatibility.
|
|
*/
|
|
|
|
static MTAB ds_mod [] = {
|
|
/* Mask Value Match Value Print String Match String Validation Display Descriptor */
|
|
/* ------------ ------------ ----------------- --------------- ---------------- ------- ---------- */
|
|
{ UNIT_UNLOAD, UNIT_UNLOAD, "heads unloaded", "UNLOADED", &ds_load_unload, NULL, NULL },
|
|
{ UNIT_UNLOAD, 0, "heads loaded", "LOADED", &ds_load_unload, NULL, NULL },
|
|
|
|
{ UNIT_WLK, UNIT_WLK, "protected", "PROTECT", NULL, NULL, NULL },
|
|
{ UNIT_WLK, 0, "unprotected", "UNPROTECT", NULL, NULL, NULL },
|
|
|
|
{ UNIT_WLK, UNIT_WLK, NULL, "LOCKED", NULL, NULL, NULL },
|
|
{ UNIT_WLK, 0, NULL, "WRITEENABLED", NULL, NULL, NULL },
|
|
|
|
{ UNIT_FMT, UNIT_FMT, "format enabled", "FORMAT", NULL, NULL, NULL },
|
|
{ UNIT_FMT, 0, "format disabled", "NOFORMAT", NULL, NULL, NULL },
|
|
|
|
/* Print Match */
|
|
/* Mask Value Match Value String String Validation Disp Desc */
|
|
/* --------------------------------- --------------------- ---------- ---------- ------------- ---- ---- */
|
|
{ UNIT_AUTO | UNIT_ATT, UNIT_AUTO, "autosize", "AUTOSIZE", &dl_set_model, NULL, NULL },
|
|
{ UNIT_AUTO | UNIT_ATT | UNIT_MODEL, MODEL_7905, "7905", "7905", &dl_set_model, NULL, NULL },
|
|
{ UNIT_AUTO | UNIT_ATT | UNIT_MODEL, MODEL_7906, "7906", "7906", &dl_set_model, NULL, NULL },
|
|
{ UNIT_AUTO | UNIT_ATT | UNIT_MODEL, MODEL_7920, "7920", "7920", &dl_set_model, NULL, NULL },
|
|
{ UNIT_AUTO | UNIT_ATT | UNIT_MODEL, MODEL_7925, "7925", "7925", &dl_set_model, NULL, NULL },
|
|
{ UNIT_ATT | UNIT_MODEL, UNIT_ATT | MODEL_7905, "7905", NULL, NULL, NULL, NULL },
|
|
{ UNIT_ATT | UNIT_MODEL, UNIT_ATT | MODEL_7906, "7906", NULL, NULL, NULL, NULL },
|
|
{ UNIT_ATT | UNIT_MODEL, UNIT_ATT | MODEL_7920, "7920", NULL, NULL, NULL, NULL },
|
|
{ UNIT_ATT | UNIT_MODEL, UNIT_ATT | MODEL_7925, "7925", NULL, NULL, NULL, NULL },
|
|
|
|
/* Entry Flags Value Print String Match String Validation Display Descriptor */
|
|
/* ------------------- ----- ------------ ------------ ------------ ------------- ---------------- */
|
|
{ MTAB_XDV, 1u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &ds_dib },
|
|
{ MTAB_XDV | MTAB_NMO, ~1u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &ds_dib },
|
|
{ 0 }
|
|
};
|
|
|
|
|
|
/* Debugging trace list */
|
|
|
|
static DEBTAB ds_deb [] = {
|
|
{ "RWSC", DEB_RWSC },
|
|
{ "CMDS", DEB_CMDS },
|
|
{ "CPU", DEB_CPU },
|
|
{ "BUF", DEB_BUF },
|
|
{ "SERV", DEB_SERV },
|
|
{ "IOBUS", TRACE_IOBUS },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
|
|
/* Device descriptor */
|
|
|
|
DEVICE ds_dev = {
|
|
"DS", /* device name */
|
|
ds_unit, /* unit array */
|
|
ds_reg, /* register array */
|
|
ds_mod, /* modifier array */
|
|
DS_UNITS, /* 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_DISABLE | DEV_DEBUG, /* 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 interface.
|
|
|
|
The 13175D disc interface data path consists of an input multiplexer/latch
|
|
and a 16-word FIFO buffer. The FIFO source may be either the CPU's I/O
|
|
input bus or the controller's interface data bus. The output of the FIFO may
|
|
be enabled either to the CPU's I/O output bus or the interface data bus.
|
|
|
|
The control path consists of the usual control, flag buffer, flag, and SRQ
|
|
flip-flops, although flag and SRQ are decoupled to allow the full DCPC
|
|
transfer rate through the FIFO (driving SRQ from the flag limits transfers to
|
|
every other cycle). SRQ is based on the FIFO level: if data or room in the
|
|
FIFO is available, SRQ is set to initiate a transfer. The flag is only used
|
|
to signal an interrupt at the end of a command.
|
|
|
|
One unusual aspect is that SFC and SFS test different things, rather than
|
|
complementary states of the same thing. SFC tests the controller busy state,
|
|
and SFS tests the flag flip-flop.
|
|
|
|
In addition, the card contains end-of-data-transfer, command-follows, and
|
|
command-ready flip-flops. EDT is set when the DCPC EDT signal is asserted
|
|
and is used in conjunction with the FIFO level to assert the end-of-data
|
|
signal to the controller. The command-follows flip-flop is set by a CLC to
|
|
indicate that the next data word output from the CPU is a disc command. The
|
|
command-ready flip-flop is set when a command is received to schedule an
|
|
interface poll.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. In hardware, SRQ is enabled only when the controller is reading or
|
|
writing the disc (IFIN or IFOUT functions are asserted) and set when the
|
|
FIFO is not empty (read) or not full (write). In simulation, SRQ is set
|
|
by the unit service read/write data phase transfers and cleared in the
|
|
IOI and IOO signal handlers when the FIFO is empty (read) or full
|
|
(write).
|
|
|
|
2. The DCPC EDT signal cannot set the controller's end-of-data flag directly
|
|
because a write EOD must occur only after the FIFO has been drained.
|
|
|
|
3. Polling the interface or drives must be deferred to the end of I/O signal
|
|
handling. If they are performed in the IOO/STC handlers themselves, an
|
|
associated CLF might clear the flag that was set by the poll.
|
|
|
|
4. Executing a CLC sets the controller's end-of-data flag, which will abort
|
|
a read or write data transfer in progress. Parameter transfers are not
|
|
affected. If a command is received when a parameter is expected, the
|
|
word is interpreted as data, even though the command-ready flip-flop is
|
|
set. The controller firmware only checks DTRDY for a parameter transfer,
|
|
and DTRDY is asserted whenever the FIFO is not empty.
|
|
|
|
5. The hardware Interface Function and Flag Buses are not implemented
|
|
explicitly. Instead, interface functions and signals are inferred by the
|
|
interface from the current command operation and phase.
|
|
*/
|
|
|
|
static SIGNALS_VALUE ds_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
|
|
{
|
|
static const char * const output_state [] = { "Data", "Command" };
|
|
const char * const hold_or_clear = (inbound_signals & ioCLF ? ",C" : "");
|
|
INBOUND_SIGNAL signal;
|
|
INBOUND_SET working_set = inbound_signals;
|
|
SIGNALS_VALUE outbound = { ioNONE, 0 };
|
|
t_bool irq_enabled = FALSE;
|
|
t_bool command_issued = FALSE;
|
|
t_bool interrupt_enabled = FALSE;
|
|
|
|
while (working_set) {
|
|
signal = IONEXTSIG (working_set); /* isolate the next signal */
|
|
|
|
switch (signal) { /* dispatch the I/O signal */
|
|
|
|
case ioCLF: /* clear flag flip-flop */
|
|
ds.flag_buffer = CLEAR; /* clear the flag buffer */
|
|
ds.flag = CLEAR; /* and flag */
|
|
|
|
tprintf (ds_dev, DEB_CMDS, "[CLF] Flag cleared\n");
|
|
break;
|
|
|
|
|
|
case ioSTF: /* set flag flip-flop */
|
|
ds.flag_buffer = SET; /* set the flag buffer */
|
|
|
|
tprintf (ds_dev, DEB_CMDS, "[STF] Flag set\n");
|
|
break;
|
|
|
|
|
|
case ioENF: /* enable flag */
|
|
if (ds.flag_buffer == SET)
|
|
ds.flag = SET;
|
|
break;
|
|
|
|
|
|
case ioSFC: /* skip if flag is clear */
|
|
if (mac_cntlr.state != cntlr_busy) /* skip if the controller is not busy */
|
|
outbound.signals |= ioSKF;
|
|
break;
|
|
|
|
|
|
case ioSFS: /* skip if flag is set */
|
|
if (ds.flag == SET) /* assert SKF if the flag is set */
|
|
outbound.signals |= ioSKF;
|
|
break;
|
|
|
|
|
|
case ioIOI: /* I/O data input */
|
|
outbound.value = fifo_unload (); /* unload the next word from the FIFO */
|
|
|
|
tprintf (ds_dev, DEB_CPU, "[LIx%s] Data = %06o\n", hold_or_clear, outbound.value);
|
|
|
|
if (FIFO_EMPTY) { /* is the FIFO now empty? */
|
|
if (ds.srq == SET)
|
|
tprintf (ds_dev, DEB_CMDS, "[LIx%s] SRQ cleared\n", hold_or_clear);
|
|
|
|
ds.srq = CLEAR; /* clear SRQ */
|
|
|
|
if (ds_cntlr.PHASE == data_phase) { /* is this an outbound parameter? */
|
|
ds_cntlr.wait = mac_cntlr.data_time; /* activate the controller */
|
|
activate_unit (&ds_cntlr); /* to acknowledge the data */
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case ioIOO: /* I/O data output */
|
|
tprintf (ds_dev, DEB_CPU, "[OTx%s] %s = %06o\n",
|
|
hold_or_clear, output_state [ds.cmfol], inbound_value);
|
|
|
|
fifo_load ((uint16) inbound_value); /* load the word into the FIFO */
|
|
|
|
if (ds.cmfol == SET) { /* are we expecting a command? */
|
|
ds.cmfol = CLEAR; /* clear the command follows flip-flop */
|
|
ds.cmrdy = SET; /* set the command ready flip-flop */
|
|
command_issued = TRUE; /* and request an interface poll */
|
|
}
|
|
|
|
else { /* not a command */
|
|
if (ds_cntlr.PHASE == data_phase) { /* is this an inbound parameter? */
|
|
ds_cntlr.wait = mac_cntlr.data_time; /* activate the controller */
|
|
activate_unit (&ds_cntlr); /* to receive the data */
|
|
}
|
|
|
|
if (FIFO_STOP) { /* is the FIFO now full enough? */
|
|
if (ds.srq == SET)
|
|
tprintf (ds_dev, DEB_CMDS, "[OTx%s] SRQ cleared\n", hold_or_clear);
|
|
|
|
ds.srq = CLEAR; /* clear SRQ to stop filling */
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case ioPOPIO: /* power-on preset to I/O */
|
|
ds.flag_buffer = SET; /* set flag buffer */
|
|
ds.cmrdy = CLEAR; /* clear the command ready flip-flop */
|
|
|
|
tprintf (ds_dev, DEB_CMDS, "[POPIO] Flag set\n");
|
|
break;
|
|
|
|
|
|
case ioCRS: /* control reset */
|
|
tprintf (ds_dev, DEB_CMDS, "[CRS] Master reset\n");
|
|
|
|
ds.control = CLEAR; /* clear the control */
|
|
ds.cmfol = CLEAR; /* and command follows flip-flops */
|
|
|
|
if (PRESET_ENABLE) { /* is preset enabled for this interface? */
|
|
fifo_clear (); /* clear the FIFO */
|
|
|
|
dl_clear_controller (&mac_cntlr, ds_unit, /* do a hard clear of the controller */
|
|
hard_clear);
|
|
}
|
|
break;
|
|
|
|
|
|
case ioCLC: /* clear control flip-flop */
|
|
tprintf (ds_dev, DEB_CMDS, "[CLC%s] Control cleared\n", hold_or_clear);
|
|
|
|
ds.control = CLEAR; /* clear the control */
|
|
ds.edt = CLEAR; /* and EDT flip-flops */
|
|
ds.cmfol = SET; /* set the command follows flip-flop */
|
|
mac_cntlr.eod = SET; /* set the controller's EOD flag */
|
|
|
|
fifo_clear (); /* clear the FIFO */
|
|
break;
|
|
|
|
|
|
case ioSTC: /* set control flip-flop */
|
|
ds.control = SET; /* set the control flip-flop */
|
|
|
|
interrupt_enabled = TRUE; /* check for drive attention */
|
|
|
|
tprintf (ds_dev, DEB_CMDS, "[STC%s] Control set\n", hold_or_clear);
|
|
break;
|
|
|
|
|
|
case ioEDT: /* end data transfer */
|
|
ds.edt = SET; /* set the EDT flip-flop */
|
|
|
|
tprintf (ds_dev, DEB_CPU, "[EDT] DCPC transfer ended\n");
|
|
break;
|
|
|
|
|
|
case ioSIR: /* set interrupt request */
|
|
if (ds.control & ds.flag) /* if control and flag are set */
|
|
outbound.signals |= cnVALID; /* then deny PRL */
|
|
else /* otherwise */
|
|
outbound.signals |= cnPRL | cnVALID; /* conditionally assert PRL */
|
|
|
|
if (ds.control & ds.flag & ds.flag_buffer) /* if control and flag and flag buffer are set */
|
|
outbound.signals |= cnIRQ | cnVALID; /* then conditionally assert IRQ */
|
|
|
|
if (ds.srq == SET) /* if the SRQ flip-flop is set */
|
|
outbound.signals |= ioSRQ; /* then assert SRQ */
|
|
break;
|
|
|
|
|
|
case ioIAK: /* interrupt acknowledge */
|
|
ds.flag_buffer = CLEAR;
|
|
break;
|
|
|
|
|
|
case ioIEN: /* interrupt enable */
|
|
irq_enabled = TRUE;
|
|
break;
|
|
|
|
|
|
case ioPRH: /* priority high */
|
|
if (irq_enabled && outbound.signals & cnIRQ) /* if IRQ is enabled and conditionally asserted */
|
|
outbound.signals |= ioIRQ | ioFLG; /* then assert IRQ and FLG */
|
|
|
|
if (!irq_enabled || outbound.signals & cnPRL) /* if IRQ is disabled or PRL is conditionally asserted */
|
|
outbound.signals |= ioPRL; /* then assert it unconditionally */
|
|
break;
|
|
|
|
|
|
case ioPON: /* not used by this interface */
|
|
break;
|
|
}
|
|
|
|
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
|
|
}
|
|
|
|
|
|
if (command_issued) /* was a command received? */
|
|
poll_interface (); /* poll the interface for the next command */
|
|
else if (interrupt_enabled) /* were interrupts enabled? */
|
|
poll_drives (); /* poll the drives for Attention */
|
|
|
|
return outbound; /* return the outbound signals and value */
|
|
}
|
|
|
|
|
|
/* Service the disc drive unit.
|
|
|
|
The unit service routine is called to execute scheduled controller commands
|
|
for the specified unit. The actions to be taken depend on the current state
|
|
of the controller and the unit.
|
|
|
|
Generally, the controller library service routine handles all of the disc
|
|
operations except data transfer to and from the interface. Read transfers
|
|
are responsible for loading words from the sector buffer into the FIFO and
|
|
enabling SRQ. If the current sector transfer is complete, either due to EDT
|
|
assertion or buffer exhaustion, the controller is moved to the end phase to
|
|
complete or continue the read with the next sector. In either case, the unit
|
|
is rescheduled. If the FIFO overflows, the read terminates with a data
|
|
overrun error.
|
|
|
|
Write transfers set the initial SRQ to request words from the CPU. As each
|
|
word arrives, it is unloaded from the FIFO into the sector buffer, and SRQ is
|
|
enabled. If the current sector transfer is complete, the controller is moved
|
|
to the end phase. If the FIFO underflows, the write terminates with a data
|
|
overrun error.
|
|
|
|
The synchronous nature of the disc drive requires that data be supplied or
|
|
accepted continuously by the CPU. DCPC generally assures that this occurs,
|
|
and the FIFO allows for some latency before an overrun or underrun occurs.
|
|
|
|
The other operation the interface must handle is seek completion. The
|
|
controller handles seek completion by setting Attention status in the drive's
|
|
status word. The interface is responsible for polling the drives if the
|
|
controller is idle and interrupts are enabled.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. Every command except Seek, Recalibrate, and End sets the flag when the
|
|
command completes. A command completes when the controller is no longer
|
|
busy (it becomes idle for Seek, Recalibrate, and End, or it becomes
|
|
waiting for all others). Seek and Recalibrate may generate errors (e.g.,
|
|
heads unloaded), in which case the flag must be set. But in these cases,
|
|
the controller state is waiting, not idle.
|
|
|
|
However, it is insufficient simply to check that the controller has moved
|
|
to the wait state, because a seek may complete while the controller is
|
|
waiting for the next command. For example, a Seek is started on unit 0,
|
|
and the controller moves to the idle state. But before the seek
|
|
completes, another command is issued that attempts to access unit 1,
|
|
which is not ready. The command fails with a Status-2 error, and the
|
|
controller moves to the wait state. When the seek completes, the
|
|
controller is waiting with error status. We must determine whether the
|
|
seek completed successfully or not, as we must interrupt in the latter
|
|
case.
|
|
|
|
Therefore, we determine seek completion by checking if the Attention
|
|
status was set. Attention sets only if the seek completes successfully.
|
|
|
|
(Actually, Attention sets if a seek check occurs, but in that case, the
|
|
command terminated before the seek ever started. Also, a seek may
|
|
complete while the controller is busy, waiting, or idle.)
|
|
|
|
2. For debug printouts, we want to print the name of the command that has
|
|
completed when the controller returns to the idle or wait state.
|
|
Normally, we would use the controller's "opcode" field to identify the
|
|
command that completed. However, while waiting for Seek or Recalibrate
|
|
completion, "opcode" may be set to another command if that command does
|
|
not access this drive. For example, it might be set to a Read of another
|
|
unit, or a Request Status for this unit. So we can't rely on "opcode" to
|
|
report the correct name of the completed positioning command.
|
|
|
|
However, we cannot rely on "uptr->OP" either, as that can be changed
|
|
during the course of a command. For example, Read Without Verify is
|
|
changed to Read after a track crossing.
|
|
|
|
Instead, we have to determine whether a seek is completing. If it is,
|
|
then we report "uptr->OP"; otherwise, we report "opcode".
|
|
|
|
3. The initial write SRQ must set only at the transition from the start
|
|
phase to the data phase. If a write command begins with an auto-seek,
|
|
the drive service will be entered twice in the start phase (the first
|
|
entry performs the seek, and the second begins the write). In hardware,
|
|
SRQ does not assert until the write begins.
|
|
|
|
4. The DCPC EDT signal cannot set the controller's end-of-data flag
|
|
directly because a write EOD must only occur after the FIFO has been
|
|
drained.
|
|
*/
|
|
|
|
static t_stat ds_service_drive (UNIT *uptr)
|
|
{
|
|
t_stat result;
|
|
t_bool seek_completion;
|
|
FLIP_FLOP entry_srq = ds.srq; /* get the SRQ state on entry */
|
|
CNTLR_PHASE entry_phase = (CNTLR_PHASE) uptr->PHASE; /* get the operation phase on entry */
|
|
uint32 entry_status = uptr->STAT; /* get the drive status on entry */
|
|
|
|
result = dl_service_drive (&mac_cntlr, uptr); /* service the drive */
|
|
|
|
if ((CNTLR_PHASE) uptr->PHASE == data_phase) /* is the drive in the data phase? */
|
|
switch ((CNTLR_OPCODE) uptr->OP) { /* dispatch the current operation */
|
|
|
|
case Read: /* read operations */
|
|
case Read_Full_Sector:
|
|
case Read_With_Offset:
|
|
case Read_Without_Verify:
|
|
if (mac_cntlr.length == 0 || ds.edt == SET) { /* is the data phase complete? */
|
|
mac_cntlr.eod = ds.edt; /* set EOD if DCPC is done */
|
|
uptr->PHASE = end_phase; /* set the end phase */
|
|
uptr->wait = mac_cntlr.cmd_time; /* and schedule the controller */
|
|
}
|
|
|
|
else if (FIFO_FULL) /* is the FIFO already full? */
|
|
dl_end_command (&mac_cntlr, data_overrun); /* terminate the command with an overrun */
|
|
|
|
else {
|
|
fifo_load (buffer [mac_cntlr.index++]); /* load the next word into the FIFO */
|
|
mac_cntlr.length--; /* count it */
|
|
ds.srq = SET; /* ask DCPC to pick it up */
|
|
io_assert (&ds_dev, ioa_SIR); /* and assert SRQ */
|
|
uptr->wait = mac_cntlr.data_time; /* schedule the next data transfer */
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case Write: /* write operations */
|
|
case Write_Full_Sector:
|
|
case Initialize:
|
|
if (entry_phase == start_phase) { /* is this the phase transition? */
|
|
ds.srq = SET; /* start the DCPC transfer */
|
|
io_assert (&ds_dev, ioa_SIR); /* and assert SRQ */
|
|
}
|
|
|
|
else if (FIFO_EMPTY) /* is the FIFO empty? */
|
|
dl_end_command (&mac_cntlr, data_overrun); /* terminate the command with an underrun */
|
|
|
|
else {
|
|
buffer [mac_cntlr.index++] = fifo_unload (); /* unload the next word from the FIFO */
|
|
mac_cntlr.length--; /* count it */
|
|
|
|
if (ds.edt == SET && FIFO_EMPTY) /* if DCPC is complete and the FIFO is empty */
|
|
mac_cntlr.eod = SET; /* then set the end-of-data flag */
|
|
|
|
if (mac_cntlr.length == 0 || mac_cntlr.eod == SET) { /* is the data phase complete? */
|
|
uptr->PHASE = end_phase; /* set the end phase */
|
|
uptr->wait = mac_cntlr.cmd_time; /* and schedule the controller */
|
|
}
|
|
|
|
else {
|
|
if (ds.edt == CLEAR) { /* if DCPC is still transferring */
|
|
ds.srq = SET; /* then request the next word */
|
|
io_assert (&ds_dev, ioa_SIR); /* and assert SRQ */
|
|
}
|
|
|
|
uptr->wait = mac_cntlr.data_time; /* schedule the next data transfer */
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
default: /* we were entered with an invalid state */
|
|
result = SCPE_IERR; /* return an internal (programming) error */
|
|
break;
|
|
} /* end of data phase operation dispatch */
|
|
|
|
|
|
if (entry_srq != ds.srq)
|
|
tprintf (ds_dev, DEB_CMDS, "SRQ %s\n", ds.srq == SET ? "set" : "cleared");
|
|
|
|
if (uptr->wait) /* was service requested? */
|
|
activate_unit (uptr); /* schedule the next event */
|
|
|
|
seek_completion = ~entry_status & uptr->STAT & DL_S2ATN; /* seek is complete when Attention sets */
|
|
|
|
if (mac_cntlr.state != cntlr_busy) { /* is the command complete? */
|
|
if (mac_cntlr.state == cntlr_wait && !seek_completion) { /* is it command but not seek completion? */
|
|
ds.flag_buffer = SET; /* set the data flag */
|
|
io_assert (&ds_dev, ioa_ENF); /* to interrupt the CPU */
|
|
}
|
|
|
|
poll_interface (); /* poll the interface for the next command */
|
|
poll_drives (); /* poll the drives for Attention */
|
|
}
|
|
|
|
|
|
if (result == SCPE_IERR) /* did an internal error occur? */
|
|
tprintf (ds_dev, DEB_RWSC, "Unit %d %s command %s phase service not handled\n",
|
|
uptr - ds_unit, dl_opcode_name (MAC, (CNTLR_OPCODE) uptr->OP),
|
|
dl_phase_name ((CNTLR_PHASE) uptr->PHASE));
|
|
|
|
else if (seek_completion) /* if a seek has completed */
|
|
tprintf (ds_dev, DEB_RWSC, "Unit %d %s command completed\n", /* report the unit command */
|
|
uptr - ds_unit, dl_opcode_name (MAC, (CNTLR_OPCODE) uptr->OP));
|
|
|
|
else if (mac_cntlr.state == cntlr_wait) /* if the controller has stopped */
|
|
tprintf (ds_dev, DEB_RWSC, "Unit %d %s command completed\n", /* report the controller command */
|
|
uptr - ds_unit, dl_opcode_name (MAC, mac_cntlr.opcode));
|
|
|
|
return result; /* return the result of the service */
|
|
}
|
|
|
|
|
|
/* Service the controller unit.
|
|
|
|
The controller service routine is called to execute scheduled controller
|
|
commands that do not access drive units. It is also called to obtain command
|
|
parameters from the interface and to return command result values to the
|
|
interface.
|
|
|
|
Most controller commands are handled completely in the library's service
|
|
routine, so we call that first. Commands that neither accept nor supply
|
|
parameters are complete when the library routine returns, so all we have to
|
|
do is set the interface flag if required.
|
|
|
|
For parameter transfers in the data phase, the interface is responsible for
|
|
moving words between the sector buffer and the FIFO and setting the flag to
|
|
notify the CPU.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. In hardware, the Read With Offset command sets the data flag after the
|
|
offset parameter has been read and the head positioner has been moved by
|
|
the indicated amount. The intent is to delay the DCPC start until the
|
|
drive is ready to supply data from the disc.
|
|
|
|
In simulation, the flag is set as soon as the parameter is received.
|
|
*/
|
|
|
|
static t_stat ds_service_controller (UNIT *uptr)
|
|
{
|
|
t_stat result;
|
|
const CNTLR_OPCODE opcode = (CNTLR_OPCODE) uptr->OP;
|
|
|
|
result = dl_service_controller (&mac_cntlr, uptr); /* service the controller */
|
|
|
|
switch ((CNTLR_PHASE) uptr->PHASE) { /* dispatch the current phase */
|
|
|
|
case start_phase: /* most controller operations */
|
|
case end_phase: /* start and end on the same phase */
|
|
switch (opcode) { /* dispatch the current operation */
|
|
|
|
case Request_Status:
|
|
case Request_Sector_Address:
|
|
case Address_Record:
|
|
case Request_Syndrome:
|
|
case Load_TIO_Register:
|
|
case Request_Disc_Address:
|
|
case End:
|
|
break; /* complete the operation without setting the flag */
|
|
|
|
|
|
case Clear:
|
|
case Set_File_Mask:
|
|
case Wakeup:
|
|
ds.flag_buffer = SET; /* complete the operation */
|
|
io_assert (&ds_dev, ioa_ENF); /* and set the flag */
|
|
break;
|
|
|
|
|
|
default: /* we were entered with an invalid state */
|
|
result = SCPE_IERR; /* return an internal (programming) error */
|
|
break;
|
|
} /* end of operation dispatch */
|
|
break; /* end of start and end phase handlers */
|
|
|
|
|
|
case data_phase:
|
|
switch (opcode) { /* dispatch the current operation */
|
|
|
|
case Seek: /* operations that accept parameters */
|
|
case Verify:
|
|
case Address_Record:
|
|
case Read_With_Offset:
|
|
case Load_TIO_Register:
|
|
buffer [mac_cntlr.index++] = fifo_unload (); /* unload the next word from the FIFO */
|
|
mac_cntlr.length--; /* count it */
|
|
|
|
if (mac_cntlr.length) { /* are there more words to transfer? */
|
|
ds.flag_buffer = SET; /* set the flag */
|
|
io_assert (&ds_dev, ioa_ENF); /* to request the next one */
|
|
}
|
|
|
|
else { /* all parameters have been received */
|
|
uptr->PHASE = end_phase; /* set the end phase */
|
|
|
|
if (opcode == Read_With_Offset) { /* a Read With Offset command sets the flag */
|
|
ds.flag_buffer = SET; /* to indicate that offsetting */
|
|
io_assert (&ds_dev, ioa_ENF); /* is complete */
|
|
}
|
|
|
|
start_command (); /* the command is now ready to execute */
|
|
}
|
|
break;
|
|
|
|
|
|
case Request_Status: /* operations that supply parameters */
|
|
case Request_Sector_Address:
|
|
case Request_Syndrome:
|
|
case Request_Disc_Address:
|
|
if (mac_cntlr.length) { /* are there more words to return? */
|
|
fifo_load (buffer [mac_cntlr.index++]); /* load the next word into the FIFO */
|
|
mac_cntlr.length--; /* count it */
|
|
|
|
ds.flag_buffer = SET; /* set the data flag */
|
|
io_assert (&ds_dev, ioa_ENF); /* to request pickup by the CPU */
|
|
}
|
|
|
|
else { /* all parameters have been sent */
|
|
uptr->PHASE = end_phase; /* set the end phase */
|
|
uptr->wait = mac_cntlr.cmd_time; /* schedule the controller */
|
|
activate_unit (uptr); /* to complete the command */
|
|
}
|
|
break;
|
|
|
|
|
|
default: /* we were entered with an invalid state */
|
|
result = SCPE_IERR; /* return an internal (programming) error */
|
|
break;
|
|
} /* end of operation dispatch */
|
|
break; /* end of data phase handlers */
|
|
} /* end of phase dispatch */
|
|
|
|
|
|
if (result == SCPE_IERR) /* did an internal error occur? */
|
|
tprintf (ds_dev, DEB_RWSC, "Controller %s command %s phase service not handled\n",
|
|
dl_opcode_name (MAC, opcode), dl_phase_name ((CNTLR_PHASE) uptr->PHASE));
|
|
|
|
|
|
if (mac_cntlr.state != cntlr_busy) { /* has the controller stopped? */
|
|
poll_interface (); /* poll the interface for the next command */
|
|
poll_drives (); /* poll the drives for Attention status */
|
|
|
|
tprintf (ds_dev, DEB_RWSC, "Controller %s command completed\n",
|
|
dl_opcode_name (MAC, opcode));
|
|
}
|
|
|
|
return result; /* return the result of the service */
|
|
}
|
|
|
|
|
|
/* Service the command wait timer unit.
|
|
|
|
The command wait timer service routine is called if the command wait timer
|
|
expires. The library is called to reset the file mask and idle the
|
|
controller. Then the interface is polled for a command and the drives are
|
|
polled for Attention status.
|
|
*/
|
|
|
|
static t_stat ds_service_timer (UNIT *uptr)
|
|
{
|
|
t_stat result;
|
|
|
|
result = dl_service_timer (&mac_cntlr, uptr); /* service the timer */
|
|
|
|
poll_interface (); /* poll the interface for the next command */
|
|
poll_drives (); /* poll the drives for Attention status */
|
|
|
|
return result; /* return the result of the service */
|
|
}
|
|
|
|
|
|
/* Reset the simulator.
|
|
|
|
In hardware, the PON signal clears the Interface Selected flip-flop,
|
|
disconnecting the interface from the disc controller. In simulation, the
|
|
interface always remains connected to the controller, so no special action is
|
|
needed.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. During a power-on reset, a pointer to the FIFO simulation register is
|
|
saved to allow access to the "qptr" field during FIFO loading and
|
|
unloading. This enables SCP to view the FIFO as a circular queue, so
|
|
that the bottom word of the FIFO is always displayed as FIFO[0],
|
|
regardless of where it is in the actual FIFO array.
|
|
|
|
2. SRQ is denied because neither IFIN nor IFOUT is asserted when the
|
|
interface is not selected.
|
|
*/
|
|
|
|
static t_stat ds_reset (DEVICE *dptr)
|
|
{
|
|
uint32 unit;
|
|
|
|
if (sim_switches & SWMASK ('P')) { /* is this a power-on reset? */
|
|
ds.fifo_reg = find_reg ("FIFO", NULL, dptr); /* find the FIFO register entry */
|
|
|
|
if (ds.fifo_reg == NULL) /* if it cannot be found, */
|
|
return SCPE_IERR; /* report a programming error */
|
|
|
|
else { /* found it */
|
|
ds.fifo_reg->qptr = 0; /* so reset the FIFO bottom index */
|
|
ds.fifo_count = 0; /* and clear the FIFO */
|
|
}
|
|
|
|
for (unit = 0; unit < dptr->numunits; unit++) { /* loop through all of the units */
|
|
sim_cancel (dptr->units + unit); /* cancel activation */
|
|
dptr->units [unit].CYL = 0; /* reset the head position to cylinder 0 */
|
|
dptr->units [unit].pos = 0; /* (irrelevant for the controller and timer) */
|
|
}
|
|
}
|
|
|
|
io_assert (dptr, ioa_POPIO); /* PRESET the device */
|
|
ds.srq = CLEAR; /* clear SRQ */
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Attach a drive unit.
|
|
|
|
The specified file is attached to the indicated drive unit. The library
|
|
attach routine will load the heads. This will set the First Status and
|
|
Attention bits in the drive status, so we poll the drives to ensure that the
|
|
CPU is notified that the drive is now online.
|
|
|
|
If a new file is specified, the file is initialized to its capacity by
|
|
writing a zero to the last byte in the file.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. If we are called during a RESTORE command, the drive status will not be
|
|
changed, so polling the drives will have no effect.
|
|
|
|
2. The C standard says, "A binary stream need not meaningfully support fseek
|
|
calls with a whence value of SEEK_END," so instead we determine the
|
|
offset from the start of the file to the last byte and seek there.
|
|
*/
|
|
|
|
static t_stat ds_attach (UNIT *uptr, CONST char *cptr)
|
|
{
|
|
t_stat result;
|
|
t_addr offset;
|
|
const uint8 zero = 0;
|
|
|
|
result = dl_attach (&mac_cntlr, uptr, cptr); /* attach the drive */
|
|
|
|
if (result == SCPE_OK) { /* if the attach was successful */
|
|
poll_drives (); /* then poll the drives to notify the CPU */
|
|
|
|
if (sim_switches & SWMASK ('N')) { /* if this is a new disc image */
|
|
offset = (t_addr) /* then determine the offset of */
|
|
(uptr->capac * sizeof (int16) - sizeof zero); /* the last byte in a full-sized file */
|
|
|
|
if (sim_fseek (uptr->fileref, offset, SEEK_SET) != 0 /* seek to the last byte */
|
|
|| fwrite (&zero, sizeof zero, 1, uptr->fileref) == 0 /* and write a zero to fill */
|
|
|| fflush (uptr->fileref) != 0) /* the file to its capacity */
|
|
clearerr (uptr->fileref); /* clear and ignore any errors */
|
|
}
|
|
}
|
|
|
|
return result; /* return the result of the attach */
|
|
}
|
|
|
|
|
|
/* Detach a drive unit.
|
|
|
|
The specified file is detached from the indicated drive unit. The library
|
|
detach routine will unload the heads. This will set the Attention bit in the
|
|
drive status, so we poll the drives to ensure that the CPU is notified that
|
|
the drive is now offline.
|
|
*/
|
|
|
|
static t_stat ds_detach (UNIT *uptr)
|
|
{
|
|
t_stat result;
|
|
|
|
result = dl_detach (&mac_cntlr, uptr); /* detach the drive */
|
|
|
|
if (result == SCPE_OK) /* was the detach successful? */
|
|
poll_drives (); /* poll the drives to notify the CPU */
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* MAC disc bootstrap loaders (BMDL and 12992B).
|
|
|
|
The Basic Moving-Head Disc Loader (BMDL) consists of two programs. The
|
|
program starting at address x7700 loads absolute paper tapes into memory.
|
|
The program starting at address x7750 loads a disc-resident bootstrap from
|
|
the MAC disc drive into memory. The S register specifies the head to use.
|
|
|
|
For a 2100/14/15/16 CPU, entering a LOAD DS or BOOT DS command loads the BMDL
|
|
into memory and executes the disc portion starting at x7750. The bootstrap
|
|
reads 2047 words from cylinder 0 sector 0 of the specified head into memory
|
|
starting at location 2011 octal. Loader execution ends with one of the
|
|
following instructions:
|
|
|
|
* HLT 11B - the disc read failed.
|
|
* JSB 2055,I - the disc read completed.
|
|
|
|
The HP 1000 uses the 12992B boot loader ROM to bootstrap the disc. The head
|
|
number is obtained from bits 2-0 of the existing S-register value when the
|
|
loader is executed. Bits 5-3 of the existing S-register value are also
|
|
retained and are available to the boot extension program. The loader reads
|
|
6144 words from cylinder 0 sector 0 of the specified head into memory
|
|
starting at location 2011 octal. Loader execution ends with one of the
|
|
following instructions:
|
|
|
|
* HLT 30 - the drive is not ready..
|
|
* JSB 2055,I - the disc read succeeded.
|
|
|
|
The loader automatically retries the operations for all disc errors other
|
|
than a drive fault.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. After the BMDL has been loaded into memory, the paper tape portion may be
|
|
executed manually by setting the P register to the starting address
|
|
(x7700).
|
|
|
|
2. For compatibility with the "cpu_copy_loader" routine, the BMDL device I/O
|
|
instructions address select codes 10 and 11.
|
|
*/
|
|
|
|
static const LOADER_ARRAY ds_loaders = {
|
|
{ /* HP 21xx Basic Moving-Head Disc Loader (BMDL-7905) */
|
|
050, /* loader starting index */
|
|
076, /* DMA index */
|
|
034, /* FWA index */
|
|
{ 0002401, /* 77700: PTAPE CLA,RSS Paper Tape start */
|
|
0063722, /* 77701: LDA 77722 */
|
|
0107700, /* 77702: CLC 0,C */
|
|
0002307, /* 77703: CCE,INA,SZA,RSS */
|
|
0102077, /* 77704: HLT 77 */
|
|
0017735, /* 77705: JSB 77735 */
|
|
0007307, /* 77706: CMB,CCE,INB,SZB,RSS */
|
|
0027702, /* 77707: JMP 77702 */
|
|
0077733, /* 77710: STB 77733 */
|
|
0017735, /* 77711: JSB 77735 */
|
|
0017735, /* 77712: JSB 77735 */
|
|
0074000, /* 77713: STB 0 */
|
|
0077747, /* 77714: STB 77747 */
|
|
0047734, /* 77715: ADB 77734 */
|
|
0002140, /* 77716: SEZ,CLE */
|
|
0102055, /* 77717: HLT 55 */
|
|
0017735, /* 77720: JSB 77735 */
|
|
0040001, /* 77721: ADA 1 */
|
|
0177747, /* 77722: STB 77747,I */
|
|
0067747, /* 77723: LDB 77747 */
|
|
0006104, /* 77724: CLE,INB */
|
|
0037733, /* 77725: ISZ 77733 */
|
|
0027714, /* 77726: JMP 77714 */
|
|
0017735, /* 77727: JSB 77735 */
|
|
0054000, /* 77730: CPB 0 */
|
|
0027701, /* 77731: JMP 77701 */
|
|
0102011, /* 77732: HLT 11 */
|
|
0000000, /* 77733: NOP */
|
|
0100100, /* 77734: RRL 16 */
|
|
0000000, /* 77735: NOP */
|
|
0006400, /* 77736: CLB */
|
|
0103710, /* 77737: STC 10,C */
|
|
0102310, /* 77740: SFS 10 */
|
|
0027740, /* 77741: JMP 77740 */
|
|
0106410, /* 77742: MIB 10 */
|
|
0002240, /* 77743: SEZ,CME */
|
|
0127735, /* 77744: JMP 77735,I */
|
|
0005727, /* 77745: BLF,BLF */
|
|
0027737, /* 77746: JMP 77737 */
|
|
0000000, /* 77747: NOP */
|
|
0067777, /* 77750: DISC LDB 77777 */
|
|
0174001, /* 77751: STB 1,I */
|
|
0006004, /* 77752: INB */
|
|
0063732, /* 77753: LDA 77732 */
|
|
0170001, /* 77754: STA 1,I */
|
|
0067776, /* 77755: LDB 77776 */
|
|
0106606, /* 77756: OTB 6 */
|
|
0106702, /* 77757: CLC 2 */
|
|
0102602, /* 77760: OTA 2 */
|
|
0102702, /* 77761: STC 2 */
|
|
0063751, /* 77762: LDA 77751 */
|
|
0102602, /* 77763: OTA 2 */
|
|
0102501, /* 77764: LIA 1 */
|
|
0001027, /* 77765: ALS,ALF */
|
|
0013767, /* 77766: AND 77767 */
|
|
0000160, /* 77767: CLE,ALS */
|
|
0106710, /* 77770: CLC 10 */
|
|
0103610, /* 77771: OTA 10,C */
|
|
0103706, /* 77772: STC 6,C */
|
|
0102310, /* 77773: SFS 10 */
|
|
0027773, /* 77774: JMP 77773 */
|
|
0117717, /* 77775: JSB 77717,I */
|
|
0000010, /* 77776: SLA */
|
|
0002055 } }, /* 77777: SEZ,SLA,INA,RSS */
|
|
|
|
{ /* HP 1000 Loader ROM (12992B) */
|
|
IBL_START, /* loader starting index */
|
|
IBL_DMA, /* DMA index */
|
|
IBL_FWA, /* FWA index */
|
|
{ 0017727, /* 77700: START JSB STAT GET STATUS */
|
|
0002021, /* 77701: SSA,RSS IS DRIVE READY ? */
|
|
0027742, /* 77702: JMP DMA YES, SET UP DMA */
|
|
0013714, /* 77703: AND B20 NO, CHECK STATUS BITS */
|
|
0002002, /* 77704: SZA IS DRIVE FAULTY OR HARD DOWN ? */
|
|
0102030, /* 77705: HLT 30B YES, HALT 30B, "RUN" TO TRY AGAIN */
|
|
0027700, /* 77706: JMP START NO, TRY AGAIN FOR DISC READY */
|
|
0102011, /* 77707: ADDR1 OCT 102011 */
|
|
0102055, /* 77710: ADDR2 OCT 102055 */
|
|
0164000, /* 77711: CNT DEC -6144 */
|
|
0000007, /* 77712: D7 OCT 7 */
|
|
0001400, /* 77713: STCMD OCT 1400 */
|
|
0000020, /* 77714: B20 OCT 20 */
|
|
0017400, /* 77715: STMSK OCT 17400 */
|
|
0000000, /* 77716: NOP */
|
|
0000000, /* 77717: NOP */
|
|
0000000, /* 77720: NOP */
|
|
0000000, /* 77721: NOP */
|
|
0000000, /* 77722: NOP */
|
|
0000000, /* 77723: NOP */
|
|
0000000, /* 77724: NOP */
|
|
0000000, /* 77725: NOP */
|
|
0000000, /* 77726: NOP */
|
|
0000000, /* 77727: STAT NOP STATUS CHECK SUBROUTINE */
|
|
0107710, /* 77730: CLC DC,C SET STATUS COMMAND MODE */
|
|
0063713, /* 77731: LDA STCMD GET STATUS COMMAND */
|
|
0102610, /* 77732: OTA DC OUTPUT STATUS COMMAND */
|
|
0102310, /* 77733: SFS DC WAIT FOR STATUS#1 WORD */
|
|
0027733, /* 77734: JMP *-1 */
|
|
0107510, /* 77735: LIB DC,C B-REG = STATUS#1 WORD */
|
|
0102310, /* 77736: SFS DC WAIT FOR STATUS#2 WORD */
|
|
0027736, /* 77737: JMP *-1 */
|
|
0103510, /* 77740: LIA DC,C A-REG = STATUS#2 WORD */
|
|
0127727, /* 77741: JMP STAT,I RETURN */
|
|
0067776, /* 77742: DMA LDB DMACW GET DMA CONTROL WORD */
|
|
0106606, /* 77743: OTB 6 OUTPUT DMA CONTROL WORD */
|
|
0067707, /* 77744: LDB ADDR1 GET MEMORY ADDRESS */
|
|
0106702, /* 77745: CLC 2 SET MEMORY ADDRESS INPUT MODE */
|
|
0106602, /* 77746: OTB 2 OUTPUT MEMORY ADDRESS TO DMA */
|
|
0102702, /* 77747: STC 2 SET WORD COUNT INPUT MODE */
|
|
0067711, /* 77750: LDB CNT GET WORD COUNT */
|
|
0106602, /* 77751: OTB 2 OUTPUT WORD COUNT TO DMA */
|
|
0106710, /* 77752: CLDLD CLC DC SET COMMAND INPUT MODE */
|
|
0102501, /* 77753: LIA 1 LOAD SWITCH */
|
|
0106501, /* 77754: LIB 1 REGISTER SETTINGS */
|
|
0013712, /* 77755: AND D7 ISOLATE HEAD NUMBER */
|
|
0005750, /* 77756: BLF,CLE,SLB BIT 12=0? */
|
|
0027762, /* 77757: JMP *+3 NO,MANUAL BOOT */
|
|
0002002, /* 77760: SZA YES,RPL BOOT. HEAD#=0? */
|
|
0001000, /* 77761: ALS NO,HEAD#1, MAKE HEAD#=2 */
|
|
0001720, /* 77762: ALF,ALS FORM COLD LOAD */
|
|
0001000, /* 77763: ALS COMMAND WORD */
|
|
0103706, /* 77764: STC 6,C ACTIVATE DMA */
|
|
0103610, /* 77765: OTA DC,C OUTPUT COLD LOAD COMMAND */
|
|
0102310, /* 77766: SFS DC IS COLD LOAD COMPLETED ? */
|
|
0027766, /* 77767: JMP *-1 NO, WAIT */
|
|
0017727, /* 77770: JSB STAT YES, GET STATUS */
|
|
0060001, /* 77771: LDA 1 */
|
|
0013715, /* 77772: AND STMSK A-REG = STATUS BITS OF STATUS#1 WD */
|
|
0002002, /* 77773: SZA IS TRANSFER OK ? */
|
|
0027700, /* 77774: JMP START NO,TRY AGAIN */
|
|
0117710, /* 77775: EXIT JSB ADDR2,I YES, EXEC LOADED PROGRAM @ 2055B */
|
|
0000010, /* 77776: DMACW ABS DC */
|
|
0170100 } } /* 77777: ABS -START */
|
|
};
|
|
|
|
|
|
/* Device boot routine.
|
|
|
|
This routine is called directly by the BOOT DS and LOAD DS commands to copy
|
|
the device bootstrap into the upper 64 words of the logical address space.
|
|
It is also called indirectly by a BOOT CPU or LOAD CPU command when the
|
|
specified HP 1000 loader ROM socket contains a 12992B ROM.
|
|
|
|
When called in response to a BOOT DS or LOAD DS command, the "unitno"
|
|
parameter indicates the unit number specified in the BOOT command or is zero
|
|
for the LOAD command, and "dptr" points at the DS device structure. The
|
|
bootstrap supports loading only from unit 0, and the command will be rejected
|
|
if another unit is specified (e.g., BOOT DS1). Otherwise, depending on the
|
|
current CPU model, the BMDL or 12992B loader ROM will be copied into memory
|
|
and configured for the DS select code. If the CPU is a 1000, the S register
|
|
will be set as it would be by the front-panel microcode.
|
|
|
|
When called for a BOOT/LOAD CPU command, the "unitno" parameter indicates the
|
|
select code to be used for configuration, and "dptr" will be NULL. As above,
|
|
the BMDL or 12992B loader ROM will be copied into memory and configured for
|
|
the specified select code. The S register is assumed to be set correctly on
|
|
entry and is not modified.
|
|
|
|
In either case, if the CPU is a 21xx model, the paper tape portion of the
|
|
BMDL will be automatically configured for the select code of the paper tape
|
|
reader.
|
|
|
|
For the 12992B boot loader ROM for the HP 1000, the S register is set as
|
|
follows:
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| ROM # | 0 1 | select code | reserved | 0 | head |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Bit 12 must be 1 for a manual boot. Bits 5-3 are nominally zero but are
|
|
reserved for the target operating system. For example, RTE uses bit 5 to
|
|
indicate whether a standard (0) or reconfiguration (1) boot is desired.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. In hardware, the BMDL was hand-configured for the disc and paper tape
|
|
reader select codes when it was installed on a given system. Under
|
|
simulation, the LOAD and BOOT commands automatically configure the BMDL
|
|
to the current select codes of the PTR and DS devices.
|
|
|
|
2. The HP 1000 Loader ROMs manual indicates that bits 2-0 select the head to
|
|
use, implying that heads 0-7 are valid. However, Table 5 has entries
|
|
only for heads 0-3, and the boot loader code will malfunction if heads
|
|
4-7 are specified. The code masks the head number to three bits but
|
|
forms the Cold Load Read command by shifting the head number six bits to
|
|
the left. As the head field in the command is only two bits wide,
|
|
specifying heads 4-7 will result in bit 2 being shifted into the opcode
|
|
field, resulting in a Recalibrate command.
|
|
*/
|
|
|
|
static t_stat ds_boot (int32 unitno, DEVICE *dptr)
|
|
{
|
|
static const HP_WORD ds_preserved = 0000073u; /* S-register bits 5-3 and 1-0 are preserved */
|
|
static const HP_WORD ds_manual_boot = 0010000u; /* S-register bit 12 set for a manual boot */
|
|
uint32 start;
|
|
|
|
if (dptr == NULL) /* if we are being called for a BOOT/LOAD CPU */
|
|
start = cpu_copy_loader (ds_loaders, unitno, /* then copy the boot loader to memory */
|
|
IBL_S_NOCLEAR, IBL_S_NOSET); /* but do not alter the S register */
|
|
|
|
else if (unitno != 0) /* otherwise a BOOT DS for a non-zero unit */
|
|
return SCPE_NOFNC; /* is rejected as unsupported */
|
|
|
|
else /* otherwise this is a BOOT/LOAD DS */
|
|
start = cpu_copy_loader (ds_loaders, ds_dib.select_code, /* so copy the boot loader to memory */
|
|
ds_preserved, ds_manual_boot); /* and configure the S register if 1000 CPU */
|
|
|
|
if (start == 0) /* if the copy failed */
|
|
return SCPE_NOFNC; /* then reject the command */
|
|
else /* otherwise */
|
|
return SCPE_OK; /* the boot loader was successfully copied */
|
|
}
|
|
|
|
|
|
|
|
/* MAC disc global SCP routines */
|
|
|
|
|
|
/* Load or unload the drive heads.
|
|
|
|
The SCP command SET DSn UNLOADED 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.
|
|
|
|
The library handles command validation and setting the appropriate drive unit
|
|
status.
|
|
*/
|
|
|
|
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 */
|
|
|
|
return dl_load_unload (&mac_cntlr, uptr, load); /* load or unload the heads */
|
|
}
|
|
|
|
|
|
|
|
/* MAC disc local utility routines */
|
|
|
|
|
|
/* Start a command.
|
|
|
|
The previously prepared command is executed by calling the corresponding
|
|
library routine. On entry, the controller's opcode field contains the
|
|
command to start, and the buffer contains the command word in element 0 and
|
|
the parameters required by the command, if any, beginning in element 1.
|
|
|
|
If the command started, the returned pointer will point at the unit to
|
|
activate (if that unit's "wait" field is non-zero). If the returned pointer
|
|
is NULL, the command failed to start, and the controller status has been set
|
|
to indicate the reason. The interface flag is set to notify the CPU of the
|
|
failure.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. If a command that accesses the drive is attempted on a drive currently
|
|
seeking, the returned pointer will be valid, but the unit's "wait" time
|
|
will be zero. The unit must not be activated (as it already is active).
|
|
When the seek completes, the command will be executed automatically.
|
|
|
|
If a Seek or Cold Load Read command is attempted on a drive currently
|
|
seeking, seek completion will occur normally, but Seek Check status will
|
|
be set.
|
|
|
|
2. For debug printouts, we want to print the name of the command (Seek or
|
|
Recalibrate) in progress when a new command is started. However, when
|
|
the library routine returns, the unit operation and controller opcode
|
|
have been changed to reflect the new command. Therefore, we must record
|
|
the operation in progress before calling the library.
|
|
|
|
The problem is in determining which unit's operation code to record. We
|
|
cannot blindly use the unit field from the new command, as recorded in
|
|
the controller, as preparation has ensured only that the target unit
|
|
number is legal but not necessarily valid. Therefore, we must validate
|
|
the unit number before accessing the unit's operation code.
|
|
|
|
If the unit number is invalid, the command will not start, but the
|
|
compiler does not know this. Therefore, we must ensure that the saved
|
|
operation code is initialized, or a "variable used uninitialized" warning
|
|
will occur.
|
|
*/
|
|
|
|
static void start_command (void)
|
|
{
|
|
int32 unit, time;
|
|
UNIT *uptr;
|
|
CNTLR_OPCODE drive_command;
|
|
|
|
unit = GET_S1UNIT (mac_cntlr.spd_unit); /* get the (prepared) unit from the command */
|
|
|
|
if (unit <= DL_MAXDRIVE) /* is the unit number valid? */
|
|
drive_command = (CNTLR_OPCODE) ds_unit [unit].OP; /* get the opcode from the unit that will be used */
|
|
else /* the unit is invalid, so the command will not start */
|
|
drive_command = End; /* but the compiler doesn't know this! */
|
|
|
|
uptr = dl_start_command (&mac_cntlr, ds_unit, DL_MAXDRIVE); /* ask the controller to start the command */
|
|
|
|
if (uptr) { /* did the command start? */
|
|
time = uptr->wait; /* save the activation time */
|
|
|
|
if (time) /* was the unit scheduled? */
|
|
activate_unit (uptr); /* activate it (and clear the "wait" field) */
|
|
|
|
if (time == 0) /* was the unit busy? */
|
|
tprintf (ds_dev, DEB_RWSC, "Unit %d %s in progress\n",
|
|
uptr - ds_unit, dl_opcode_name (MAC, drive_command));
|
|
|
|
if (uptr - ds_unit > DL_MAXDRIVE)
|
|
tprintf (ds_dev, DEB_RWSC, "Controller %s command initiated\n",
|
|
dl_opcode_name (MAC, mac_cntlr.opcode));
|
|
else
|
|
tprintf (ds_dev, DEB_RWSC, "Unit %d position %" T_ADDR_FMT "d %s command initiated\n",
|
|
uptr - ds_unit, uptr->pos, dl_opcode_name (MAC, mac_cntlr.opcode));
|
|
}
|
|
|
|
else { /* the command failed to start */
|
|
ds.flag_buffer = SET; /* so set the data flag */
|
|
io_assert (&ds_dev, ioa_ENF); /* to notify the CPU */
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Poll the interface for a new command.
|
|
|
|
If a new command is available, and the controller is not busy, prepare the
|
|
command for execution. If preparation succeeded, and the command needs
|
|
parameters before executing, set the flag to request the first one from the
|
|
CPU. If no parameters are needed, the command is ready to execute.
|
|
|
|
If preparation failed, set the flag to notify the CPU. The controller
|
|
status contains the reason for the failure.
|
|
*/
|
|
|
|
static void poll_interface (void)
|
|
{
|
|
if (ds.cmrdy == SET && mac_cntlr.state != cntlr_busy) { /* are the interface and controller ready? */
|
|
buffer [0] = fifo_unload (); /* unload the command into the buffer */
|
|
|
|
if (dl_prepare_command (&mac_cntlr, ds_unit, DL_MAXDRIVE)) { /* prepare the command; did it succeed? */
|
|
if (mac_cntlr.length) { /* does the command require parameters? */
|
|
ds.flag_buffer = SET; /* set the data flag */
|
|
io_assert (&ds_dev, ioa_ENF); /* to request the first one */
|
|
}
|
|
|
|
else /* if not waiting for parameters */
|
|
start_command (); /* start the command */
|
|
}
|
|
|
|
else { /* preparation failed */
|
|
ds.flag_buffer = SET; /* so set the data flag */
|
|
io_assert (&ds_dev, ioa_ENF); /* to notify the CPU */
|
|
}
|
|
|
|
ds.cmrdy = CLEAR; /* flush the command from the interface */
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Poll the drives for attention requests.
|
|
|
|
If the controller is idle and interrupts are allowed, the drives are polled
|
|
to see if any drive is requesting attention. If one is found, the controller
|
|
resets that drive's Attention status, saves the drive's unit number, sets
|
|
Drive Attention status, and waits for a command from the CPU. The interface
|
|
sets the flag to notify the CPU.
|
|
*/
|
|
|
|
static void poll_drives (void)
|
|
{
|
|
if (mac_cntlr.state == cntlr_idle && ds.control == SET) /* is the controller idle and interrupts are allowed? */
|
|
if (dl_poll_drives (&mac_cntlr, ds_unit, DL_MAXDRIVE)) { /* poll the drives; was Attention seen? */
|
|
ds.flag_buffer = SET; /* set the data flag */
|
|
io_assert (&ds_dev, ioa_ENF); /* to request an interrupt */
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
/* Load a word into the FIFO.
|
|
|
|
A word is loaded into the next available location in the FIFO, and the FIFO
|
|
occupancy count is incremented. If the FIFO is full on entry, the load is
|
|
ignored.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The FIFO is implemented as circular queue to take advantage of REG_CIRC
|
|
EXAMINE semantics. REG->qptr is the index of the first word currently in
|
|
the FIFO. By specifying REG_CIRC, examining FIFO[0-n] will always
|
|
display the words in load order, regardless of the actual array index of
|
|
the start of the list. The number of words currently present in the FIFO
|
|
is kept in fifo_count (0 = empty, 1-16 = number of words available).
|
|
|
|
If fifo_count < FIFO_SIZE, (REG->qptr + fifo_count) mod FIFO_SIZE is the
|
|
index of the new word location. Loading stores the word there and then
|
|
increments fifo_count.
|
|
|
|
2. Because the load and unload routines need access to qptr in the REG
|
|
structure for the FIFO array, a pointer to the REG is stored in the
|
|
fifo_reg variable during device reset.
|
|
*/
|
|
|
|
static void fifo_load (uint16 data)
|
|
{
|
|
uint32 index;
|
|
|
|
if (FIFO_FULL) { /* is the FIFO already full? */
|
|
tprintf (ds_dev, DEB_BUF, "Attempted load to full FIFO, data %06o\n", data);
|
|
|
|
return; /* return with the load ignored */
|
|
}
|
|
|
|
index = (ds.fifo_reg->qptr + ds.fifo_count) % FIFO_SIZE; /* calculate the index of the next available location */
|
|
|
|
ds.fifo [index] = data; /* store the word in the FIFO */
|
|
ds.fifo_count = ds.fifo_count + 1; /* increment the count of words stored */
|
|
|
|
tprintf (ds_dev, DEB_BUF, "Data %06o loaded into FIFO (%d)\n",
|
|
data, ds.fifo_count);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Unload a word from the FIFO.
|
|
|
|
A word is unloaded from the first location in the FIFO, and the FIFO
|
|
occupancy count is decremented. If the FIFO is empty on entry, the unload
|
|
returns dummy data.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. If fifo_count > 0, REG->qptr is the index of the word to remove. Removal
|
|
gets the word and then increments qptr (mod FIFO_SIZE) and decrements
|
|
fifo_count.
|
|
*/
|
|
|
|
static uint16 fifo_unload (void)
|
|
{
|
|
uint16 data;
|
|
|
|
if (FIFO_EMPTY) { /* is the FIFO already empty? */
|
|
tprintf (ds_dev, DEB_BUF, "Attempted unload from empty FIFO\n");
|
|
return 0; /* return with no data */
|
|
}
|
|
|
|
data = ds.fifo [ds.fifo_reg->qptr]; /* get the word from the FIFO */
|
|
|
|
ds.fifo_reg->qptr = (ds.fifo_reg->qptr + 1) % FIFO_SIZE; /* update the FIFO queue pointer */
|
|
ds.fifo_count = ds.fifo_count - 1; /* decrement the count of words stored */
|
|
|
|
tprintf (ds_dev, DEB_BUF, "Data %06o unloaded from FIFO (%d)\n",
|
|
data, ds.fifo_count);
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
/* Clear the FIFO.
|
|
|
|
The FIFO is cleared by setting the occupancy counter to zero.
|
|
*/
|
|
|
|
static void fifo_clear (void)
|
|
{
|
|
ds.fifo_count = 0; /* clear the FIFO */
|
|
|
|
tprintf (ds_dev, DEB_BUF, "FIFO cleared\n");
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Activate the unit.
|
|
|
|
The specified unit is activated using the unit's "wait" time. If debugging
|
|
is enabled, the activation is logged to the debug file.
|
|
*/
|
|
|
|
static t_stat activate_unit (UNIT *uptr)
|
|
{
|
|
t_stat result;
|
|
|
|
if (uptr == &ds_cntlr)
|
|
tprintf (ds_dev, DEB_SERV, "Controller delay %d service scheduled\n",
|
|
uptr->wait);
|
|
else
|
|
tprintf (ds_dev, DEB_SERV, "Unit %d delay %d service scheduled\n",
|
|
uptr - ds_unit, uptr->wait);
|
|
|
|
result = sim_activate (uptr, uptr->wait); /* activate the unit */
|
|
uptr->wait = 0; /* reset the activation time */
|
|
|
|
return result; /* return the activation status */
|
|
}
|