- Avoid macro name conflic REG_NONE changed to REG_NOREG - Avoid a t_offset truncation assigning to a uint16 in drm_attach.
967 lines
32 KiB
C
967 lines
32 KiB
C
/*
|
|
|
|
Copyright (c) 2015-2017, John Forecast
|
|
|
|
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
|
|
JOHN FORECAST 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 John Forecast shall not
|
|
be used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from John Forecast.
|
|
|
|
*/
|
|
|
|
/* cdc1700_drm.c: drum memory controller
|
|
* Simh device: drm
|
|
*/
|
|
|
|
/*
|
|
* Notes:
|
|
*
|
|
* 1. The 1752 Drum Memory Subsystem consists of a 3600 RPM drum with 32
|
|
* sectors (96 words each) per track. There can be 64 - 1024 tracks
|
|
* depending on the model ordered.
|
|
*
|
|
* There is 1 readable register which needs to be handled specially - the
|
|
* Sector Address Status. This register consists of 3 fields:
|
|
*
|
|
* - Current sector address as read from the drum (0 - 31)
|
|
* - Current track address from last I/O request (zero on startup)
|
|
* - Core address compare - set if transfer is to last address of buffer
|
|
*
|
|
* The SMM17 diagnostic for the 1752 uses this register to verify that
|
|
* the hardware is operational before allowing the diagnostic to run. The
|
|
* register fields will be implemented as follows:
|
|
*
|
|
* 1. Current sector address
|
|
*
|
|
* Rather than use a repeating service routine, we will timestamp
|
|
* (using the instruction count) when the Sector Address Status register
|
|
* was last referenced or used as part of an I/O operation. When the
|
|
* register is next referenced, we will compute the number of sectors
|
|
* which have passed under the head (521 uSec/sector so 350 instructions
|
|
* assuming 1.5 microsecond/instruction) and update the sector address
|
|
* field appropriately. If an I/O is active, the sector address will
|
|
* reflect that used by the current I/O. This may result in a sudden
|
|
* change in value.
|
|
*
|
|
* 2. Current track address
|
|
*
|
|
* This will be the last track address referenced by an I/O request.
|
|
*
|
|
* 3. Core address compare
|
|
*
|
|
* This bit is set when the current/next DMA request is to the last
|
|
* address of the buffer. Rather than simulate DMA word at a time,
|
|
* we simulate sector transfer at a time. We will set this bit if the
|
|
* last address of the buffer is somewhere within the current sector
|
|
* and also set the Core Address Status to be the last address of
|
|
* the buffer.
|
|
*
|
|
* 2. This is the first, and only, device driver which requires dynamic
|
|
* processing of the Director Status Register. The Sector Compare is only
|
|
* set when the requested sector is under the read head. The I/O framework
|
|
* did not require any changes to allow this to work.
|
|
*/
|
|
|
|
#include "cdc1700_defs.h"
|
|
|
|
#define SASTATUS iod_readR[2]
|
|
#define CASTATUS iod_readR[3]
|
|
#define DATASTATUS iod_readR[4]
|
|
|
|
extern char INTprefix[];
|
|
|
|
extern void RaiseExternalInterrupt(DEVICE *);
|
|
|
|
extern t_bool doDirectorFunc(DEVICE *, t_bool);
|
|
extern t_bool fw_reject(IO_DEVICE *, t_bool, uint8);
|
|
extern void fw_IOunderwayEOP2(IO_DEVICE *, uint16);
|
|
extern void fw_IOcompleteEOP2(t_bool, DEVICE *, IO_DEVICE *, uint16, const char *);
|
|
extern void fw_IOalarm(t_bool, DEVICE *, IO_DEVICE *, const char *);
|
|
extern void fw_IOintr(t_bool, DEVICE *, IO_DEVICE *, uint16, uint16, uint16, const char *);
|
|
|
|
extern void rebuildPending(void);
|
|
|
|
extern t_stat show_addr(FILE *, UNIT *, int32, CONST void *);
|
|
|
|
extern t_stat set_protected(UNIT *, int32, CONST char *, void *);
|
|
extern t_stat clear_protected(UNIT *, int32, CONST char *, void *);
|
|
|
|
extern t_stat set_equipment(UNIT *, int32, CONST char *, void *);
|
|
|
|
extern t_stat set_stoponrej(UNIT *, int32, CONST char *, void *);
|
|
extern t_stat clr_stoponrej(UNIT *, int32, CONST char *, void *);
|
|
|
|
extern uint16 LoadFromMem(uint16);
|
|
extern t_bool IOStoreToMem(uint16, uint16, t_bool);
|
|
|
|
extern uint16 M[], Areg, IOAreg;
|
|
extern t_uint64 Instructions;
|
|
|
|
extern t_bool IOFWinitialized;
|
|
|
|
extern UNIT cpu_unit;
|
|
|
|
t_stat drm_help(FILE *, DEVICE *, UNIT *, int32, const char *);
|
|
|
|
/* Constants */
|
|
|
|
#define DRM_NUMWD (96) /* words/sector */
|
|
#define DRM_NUMBY (DRM_NUMWD * sizeof(uint16))
|
|
#define DRM_NUMSC (32) /* sectors/track */
|
|
#define DRM_SIZE (512 * DRM_NUMSC * DRM_NUMBY)
|
|
#define DRM_MINTRACKS (64) /* Min # of tracks supported */
|
|
#define DRM_MAXTRACKS (1024) /* Max # of tracks supported */
|
|
|
|
#define DRM_AUTOLOAD (16) /* Sectors to autoload */
|
|
|
|
/*
|
|
* Drum address fields
|
|
*/
|
|
#define DRM_TRK_MASK 0x7FE0 /* Track address */
|
|
#define DRM_TRK_SHIFT 5 /* and shift */
|
|
#define DRM_SEC_MASK 0x001F /* Sector mask */
|
|
|
|
#define DRM_COMPARE 0x8000 /* Core address compare */
|
|
|
|
enum drmio_status {
|
|
DRMIO_MORE, /* More I/O pending */
|
|
DRMIO_DONE, /* I/O processing complete */
|
|
DRMIO_PROTECT, /* Protect fault */
|
|
DRMIO_ADDRERR /* Addressing error */
|
|
};
|
|
|
|
t_stat drm_svc(UNIT *);
|
|
t_stat drm_reset(DEVICE *);
|
|
t_stat drm_attach(UNIT *, CONST char *);
|
|
t_stat drm_detach(UNIT *);
|
|
|
|
t_stat drm_set_size(UNIT *, int32, CONST char *, void *);
|
|
|
|
void DRMstate(const char *, DEVICE *, IO_DEVICE *);
|
|
t_bool DRMreject(IO_DEVICE *, t_bool, uint8);
|
|
enum IOstatus DRMin(IO_DEVICE *, uint8);
|
|
enum IOstatus DRMout(IO_DEVICE *, uint8);
|
|
void DRMclear(DEVICE *);
|
|
uint8 DRMdecode(IO_DEVICE *, t_bool, uint8);
|
|
|
|
/*
|
|
1752-A/B/C/D Drum memory controller
|
|
|
|
Addresses
|
|
Computer Instruction
|
|
Q Register Output From A Input to A
|
|
(Bits 03-00)
|
|
|
|
0000 Initiate Write Op Illegal
|
|
0001 Director Function Director Status
|
|
0010 Illegal Sector Address Status
|
|
0011 Director Function Core Address Status
|
|
0100 Initiate Read Op Data Status
|
|
0101 Director Function Illegal
|
|
0110 Illegal Illegal
|
|
0111 Director Function Illegal
|
|
1000 Load ISA Illegal
|
|
1001 Director Function Illegal
|
|
1010 Illegal Illegal
|
|
1011 Director Function Illegal
|
|
1100 Load Initial Addr Illegal
|
|
1101 Director Function Illegal
|
|
1110 Load Final Addr Illegal
|
|
1111 Director Function Illegal
|
|
|
|
Operations:
|
|
|
|
Initiate Drum Write Operation
|
|
|
|
15 14 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Director Function
|
|
|
|
15 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| X | X | X | X | X | X | X | X | X | X | X | | | X | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | |
|
|
| | | Clr Controller
|
|
| | Clr Interrupts
|
|
| EOP Interrupt Req.
|
|
Interrupt on Alarm
|
|
|
|
Initiate Drum Read Operation
|
|
|
|
15 14 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Load Initial Sector Address
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 0 | | | | | | | | | | | | | | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | |
|
|
+-----------------------------------+ +---------------+
|
|
Desired Initial Track Address Desired Initial
|
|
Sector Addr - 1
|
|
|
|
Load Initial Core Address, Load Final Core Address
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | | | | | | | | | | | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| |
|
|
+-----------------------------------------------------------+
|
|
Core Address
|
|
|
|
Status Response:
|
|
|
|
Director Status
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | | | | | | | | | | | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | | | | | | | | | | | |
|
|
| | | | | | | | | | | | | | | Ready
|
|
| | | | | | | | | | | | | | Busy
|
|
| | | | | | | | | | | | | Interrupt
|
|
| | | | | | | | | | | | Data
|
|
| | | | | | | | | | | End of Operation
|
|
| | | | | | | | | | Alarm
|
|
| | | | | | | | | Lost Data
|
|
| | | | | | | | Protected
|
|
| | | | | | | Checkword Error
|
|
| | | | | | Protect Fault
|
|
| | | | | Guarded Address Enabled
|
|
| | | | Timing Track Error
|
|
| | | Power Failure
|
|
| | Sector Compare
|
|
| Guarded Address Error
|
|
Sector Overrange Error
|
|
|
|
Sector Address Status
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | | | | | | | | | | | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | |
|
|
| +-----------------------------------+ +---------------+
|
|
| Track Address Register Contents Sector Address
|
|
| Register Contents
|
|
Core Address Compare
|
|
|
|
Core Address Status
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | | | | | | | | | | | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| |
|
|
+-----------------------------------------------------------+
|
|
Core Address Register Contents
|
|
|
|
Data Status
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | | | | | | | | | | | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| |
|
|
+-----------------------------------------------------------+
|
|
Data Register Zero Contents
|
|
|
|
*/
|
|
|
|
IO_DEVICE DRMdev = IODEV(NULL, "1752", 1752, 2, 0xFF, 0,
|
|
DRMreject, DRMin, DRMout, NULL, NULL,
|
|
DRMstate, NULL, NULL, DRMclear, DRMdecode, NULL,
|
|
0x7F, 16,
|
|
MASK_REGISTER0 | MASK_REGISTER1 | MASK_REGISTER2 | \
|
|
MASK_REGISTER3 | MASK_REGISTER4 | MASK_REGISTER5 | \
|
|
MASK_REGISTER6 | MASK_REGISTER7 | MASK_REGISTER8 | \
|
|
MASK_REGISTER9 | MASK_REGISTER10 | MASK_REGISTER11 | \
|
|
MASK_REGISTER12 | MASK_REGISTER13 | \
|
|
MASK_REGISTER14 | MASK_REGISTER15,
|
|
MASK_REGISTER3 | MASK_REGISTER4,
|
|
MASK_REGISTER0 | MASK_REGISTER5 | MASK_REGISTER6 | \
|
|
MASK_REGISTER7 | MASK_REGISTER8 | MASK_REGISTER9 | \
|
|
MASK_REGISTER10 | MASK_REGISTER11 | \
|
|
MASK_REGISTER12 | MASK_REGISTER13 | \
|
|
MASK_REGISTER14 | MASK_REGISTER15,
|
|
MASK_REGISTER2 | MASK_REGISTER6 | MASK_REGISTER10,
|
|
0, 0, NULL);
|
|
|
|
/*
|
|
* Define usage for "private" IO_DEVICE data areas.
|
|
*/
|
|
#define iod_tracks iod_private /* # of tracks on device */
|
|
#define iod_compare iod_private4 /* TRUE if DMA of last buffer word */
|
|
#define iod_isa iod_private6 /* Initial sector address */
|
|
#define iod_ica iod_private7 /* Initial core address */
|
|
#define iod_fca iod_private8 /* Final core address */
|
|
#define iod_state iod_private9 /* Current controller state */
|
|
#define iod_ca iod_private11 /* Current DMA address */
|
|
#define iod_trk iod_private12 /* Current track # */
|
|
#define iod_sec iod_private13 /* Current sector # */
|
|
|
|
#define DRM_IDLE 0x00 /* Idle */
|
|
#define DRM_WRITE 0x01 /* Write data */
|
|
#define DRM_READ 0x02 /* Read data */
|
|
|
|
/* DRM data structures
|
|
|
|
drm_dev DRM device descriptor
|
|
drm_unit DRM unit descriptor
|
|
drm_reg DRM register list
|
|
drm_mod DRM modifier list
|
|
*/
|
|
|
|
UNIT drm_unit = {
|
|
UDATA(&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DRM_SIZE)
|
|
};
|
|
|
|
REG drm_reg[] = {
|
|
{ HRDATAD(FUNCTION, DRMdev.FUNCTION, 16, "Last director function issued") },
|
|
{ HRDATAD(STATUS, DRMdev.STATUS, 16, "Director status register") },
|
|
{ HRDATAD(IENABLE, DRMdev.IENABLE, 16, "Interrupts enabled") },
|
|
/*** more ***/
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB drm_mod[] = {
|
|
{ MTAB_XTD | MTAB_VDV, 0, "1752 Drum Memory Controller" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "EQUIPMENT", "EQUIPMENT=hexAddress",
|
|
&set_equipment, &show_addr, NULL, "Set/Display equipment address" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "STOPONREJECT",
|
|
&set_stoponrej, NULL, NULL, "Stop simulation if I/O is rejected" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOSTOPONREJECT",
|
|
&clr_stoponrej, NULL, NULL, "Don't stop simulation if I/O is rejected" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "PROTECT",
|
|
&set_protected, NULL, NULL, "Device is protected (unimplemented)" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOPROTECT",
|
|
&clear_protected, NULL, NULL, "Device is unprotected (unimplemented)" },
|
|
{ UNIT_DRMSIZE, 64, NULL, "64",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 64 tracks" },
|
|
{ UNIT_DRMSIZE, 128, NULL, "128",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 128 tracks" },
|
|
{ UNIT_DRMSIZE, 192, NULL, "192",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 192 tracks" },
|
|
{ UNIT_DRMSIZE, 256, NULL, "256",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 256 tracks" },
|
|
{ UNIT_DRMSIZE, 320, NULL, "320",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 320 tracks" },
|
|
{ UNIT_DRMSIZE, 384, NULL, "384",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 384 tracks" },
|
|
{ UNIT_DRMSIZE, 448, NULL, "448",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 448 tracks" },
|
|
{ UNIT_DRMSIZE, 512, NULL, "512",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 512 tracks" },
|
|
{ UNIT_DRMSIZE, 576, NULL, "576",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 576 tracks" },
|
|
{ UNIT_DRMSIZE, 640, NULL, "640",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 640 tracks" },
|
|
{ UNIT_DRMSIZE, 704, NULL, "704",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 704 tracks" },
|
|
{ UNIT_DRMSIZE, 768, NULL, "768",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 768 tracks" },
|
|
{ UNIT_DRMSIZE, 832, NULL, "832",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 832 tracks" },
|
|
{ UNIT_DRMSIZE, 896, NULL, "896",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 896 tracks" },
|
|
{ UNIT_DRMSIZE, 960, NULL, "960",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 960 tracks" },
|
|
{ UNIT_DRMSIZE, 1024, NULL, "1024",
|
|
&drm_set_size, NULL, NULL, "Set drum storage to 1024 tracks" },
|
|
{ 0 }
|
|
};
|
|
|
|
DEBTAB drm_deb[] = {
|
|
{ "TRACE", DBG_DTRACE, "Trace device I/O requests" },
|
|
{ "STATE", DBG_DSTATE, "Display device state changes" },
|
|
{ "INTR", DBG_DINTR, "Display device interrupt requests" },
|
|
{ "ERROR", DBG_DERROR, "Display device errors" },
|
|
{ "LOCATION", DBG_DLOC, "Display address for I/O instructions" },
|
|
{ "FIRSTREJ", DBG_DFIRSTREJ, "Suppress display of 2nd ... I/O rejects" },
|
|
{ "ALL", DBG_DTRACE | DBG_DSTATE | DBG_DINTR | DBG_DERROR | DBG_DLOC },
|
|
{ NULL }
|
|
};
|
|
|
|
DEVICE drm_dev = {
|
|
"DRM", &drm_unit, drm_reg, drm_mod,
|
|
1, 10, 31, 1, 8, 8,
|
|
NULL, NULL, &drm_reset,
|
|
NULL, &drm_attach, &drm_detach,
|
|
&DRMdev,
|
|
DEV_DEBUG | DEV_DISK | DEV_DISABLE | DEV_INDEV | DEV_OUTDEV | DEV_PROTECT,
|
|
0, drm_deb,
|
|
NULL, NULL, &drm_help, NULL, NULL, NULL
|
|
};
|
|
|
|
/*
|
|
* Dump the current internal state of the DRM device.
|
|
*/
|
|
const char *DRMStateStr[] = {
|
|
"Idle", "Write", "Read"
|
|
};
|
|
|
|
void DRMstate(const char *where, DEVICE *dev, IO_DEVICE *iod)
|
|
{
|
|
fprintf(DBGOUT,
|
|
"%s[%s %s: %s, Func: %04X, Sta: %04X, Ena: %04X]\r\n",
|
|
INTprefix, dev->name, where, DRMStateStr[iod->iod_state],
|
|
iod->FUNCTION, iod->STATUS, iod->IENABLE);
|
|
fprintf(DBGOUT,
|
|
"%s[%s: ISA: %04X, ICA: %04X, FCA: %04X, SAS: %04X, CAS: %04X]\r\n",
|
|
INTprefix, dev->name,
|
|
iod->iod_isa, iod->iod_ica, iod->iod_fca,
|
|
iod->SASTATUS, iod->CASTATUS);
|
|
fprintf(DBGOUT,
|
|
"%s[%s: Trk: %03X, Sec: %02X, Cur: %04X, Comp: %c]\r\n",
|
|
INTprefix, dev->name, iod->iod_trk, iod->iod_sec, iod->iod_ca,
|
|
iod->iod_compare ? 'T' : 'F');
|
|
}
|
|
|
|
/*
|
|
* Load and validate drum address in the Initial Sector Address Register
|
|
*/
|
|
static t_bool LoadDrumAddress(void)
|
|
{
|
|
uint16 track = (DRMdev.iod_isa & DRM_TRK_MASK) >> DRM_TRK_SHIFT;
|
|
|
|
if (track >= DRMdev.iod_tracks)
|
|
return FALSE;
|
|
|
|
/*
|
|
* The sector field initially holds (sector # - 1) and needs to be
|
|
* incremented without touching the track #.
|
|
*/
|
|
DRMdev.iod_trk = track;
|
|
DRMdev.iod_sec = (DRMdev.iod_isa + 1) & DRM_SEC_MASK;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Set up a drum I/O operation using the currently set parameters
|
|
*/
|
|
static void StartDrumIO(t_bool wr)
|
|
{
|
|
if (LoadDrumAddress()) {
|
|
DRMdev.iod_compare =(DRMdev.iod_fca >= DRMdev.CASTATUS) &&
|
|
(DRMdev.iod_fca < (DRMdev.CASTATUS + DRM_NUMWD));
|
|
DRMdev.iod_ca = DRMdev.iod_ica;
|
|
DRMdev.CASTATUS = DRMdev.iod_compare ? DRMdev.iod_ica : DRMdev.iod_fca;
|
|
|
|
fw_IOunderwayEOP2(&DRMdev, IO_ST_DATA);
|
|
|
|
if ((drm_dev.dctrl & DBG_DTRACE) != 0) {
|
|
fprintf(DBGOUT,
|
|
"%sDRM - Start %s I/O, Trk: %03X, Sec: %02X, Start: %04X, End: %04X\r\n",
|
|
INTprefix, wr ? "Write" : "Read",
|
|
DRMdev.iod_trk, DRMdev.iod_sec, DRMdev.iod_ica, DRMdev.iod_fca);
|
|
}
|
|
|
|
DRMdev.iod_state = wr ? DRM_WRITE : DRM_READ;
|
|
sim_activate(&drm_unit, DRM_ACCESS_WAIT);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Generate a sector overrange error and possible interrupt.
|
|
*/
|
|
DRMdev.STATUS &= ~IO_ST_DATA;
|
|
DRMdev.STATUS |= IO_1752_OVERR;
|
|
fw_IOalarm(FALSE, &drm_dev, &DRMdev, "Invalid track #");
|
|
}
|
|
|
|
/*
|
|
* Increment the drum sector address.
|
|
*/
|
|
static void DrumIOIncSector(void)
|
|
{
|
|
DRMdev.iod_sec = (DRMdev.iod_sec + 1) & DRM_SEC_MASK;
|
|
if (DRMdev.iod_sec == 0)
|
|
DRMdev.iod_trk++;
|
|
}
|
|
|
|
/*
|
|
* Initiate a read operation on the drum.
|
|
*/
|
|
static enum drmio_status DrumIORead(UNIT *uptr)
|
|
{
|
|
uint16 buf[DRM_NUMWD];
|
|
uint32 lba = (DRMdev.iod_trk << DRM_TRK_SHIFT) | DRMdev.iod_sec;
|
|
int i;
|
|
|
|
if (DRMdev.iod_trk >= DRMdev.iod_tracks)
|
|
return DRMIO_ADDRERR;
|
|
|
|
/*
|
|
* Report any error in the underlying container infrastructure as an
|
|
* address error.
|
|
*/
|
|
if (sim_fseeko(uptr->fileref, lba * DRM_NUMBY, SEEK_SET) ||
|
|
(sim_fread(buf, sizeof(uint16), DRM_NUMWD, uptr->fileref) != DRM_NUMWD))
|
|
return DRMIO_ADDRERR;
|
|
|
|
for (i = 0; i < DRM_NUMWD; i++) {
|
|
/*** TODO: fix protect check ***/
|
|
if (!IOStoreToMem(DRMdev.iod_ca, buf[i], TRUE))
|
|
return DRMIO_PROTECT;
|
|
|
|
DRMdev.DATASTATUS = buf[i];
|
|
|
|
if (DRMdev.iod_ca++ == DRMdev.iod_fca) {
|
|
DRMdev.CASTATUS = DRMdev.iod_ca;
|
|
DrumIOIncSector();
|
|
return DRMIO_DONE;
|
|
}
|
|
}
|
|
DrumIOIncSector();
|
|
if ((drm_dev.dctrl & DBG_DTRACE) != 0)
|
|
fprintf(DBGOUT,
|
|
"%sDRM - Continue Read I/O, Trk: %03X, Sec: %02X, Cur: %04X, End: %04X\r\n",
|
|
INTprefix, DRMdev.iod_trk, DRMdev.iod_sec,
|
|
DRMdev.iod_ca, DRMdev.iod_fca);
|
|
|
|
return DRMIO_MORE;
|
|
}
|
|
|
|
/*
|
|
* Initiate a write operation on the drum.
|
|
*/
|
|
static enum drmio_status DrumIOWrite(UNIT *uptr)
|
|
{
|
|
uint16 buf[DRM_NUMWD];
|
|
uint32 lba = (DRMdev.iod_trk << DRM_TRK_SHIFT) | DRMdev.iod_sec;
|
|
t_bool done = FALSE;
|
|
int i;
|
|
|
|
if (DRMdev.iod_trk >= DRMdev.iod_tracks)
|
|
return DRMIO_ADDRERR;
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
for (i = 0; i < DRM_NUMWD; i++) {
|
|
DRMdev.DATASTATUS = buf[i] = LoadFromMem(DRMdev.iod_ca);
|
|
if (DRMdev.iod_ca++ == DRMdev.iod_fca) {
|
|
DRMdev.CASTATUS = DRMdev.iod_ca;
|
|
done = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Report any error in the underlying container infrastructure as an
|
|
* address error.
|
|
*/
|
|
if (sim_fseeko(uptr->fileref, lba * DRM_NUMBY, SEEK_SET) ||
|
|
(sim_fwrite(buf, sizeof(uint16), DRM_NUMWD, uptr->fileref) != DRM_NUMWD))
|
|
return DRMIO_ADDRERR;
|
|
|
|
DrumIOIncSector();
|
|
if (((drm_dev.dctrl & DBG_DTRACE) != 0) && !done)
|
|
fprintf(DBGOUT,
|
|
"%sDRM - Continue Write I/O, Trk: %03X, Sec: %02X, Cur: %04X, End: %04X\r\n",
|
|
INTprefix, DRMdev.iod_trk, DRMdev.iod_sec,
|
|
DRMdev.iod_ca, DRMdev.iod_fca);
|
|
|
|
return done ? DRMIO_DONE : DRMIO_MORE;
|
|
}
|
|
|
|
/*
|
|
* Perform read/write sector operations from within the unit service routine.
|
|
*/
|
|
void DrumIO(UNIT *uptr, uint8 iotype)
|
|
{
|
|
const char *error = "Unknown";
|
|
enum drmio_status status = DRMIO_ADDRERR;
|
|
|
|
switch (iotype) {
|
|
case DRM_WRITE:
|
|
status = DrumIOWrite(uptr);
|
|
break;
|
|
|
|
case DRM_READ:
|
|
status = DrumIORead(uptr);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Update the sector address status register if the I/O was successful.
|
|
* Note that since we perform sector at a time I/O, we assert the "Core
|
|
* Address Compare" bit for the entire period.
|
|
*/
|
|
if ((status == DRMIO_MORE) || (status == DRMIO_DONE)) {
|
|
DRMdev.iod_compare =(DRMdev.iod_fca >= DRMdev.iod_ca) &&
|
|
(DRMdev.iod_fca < (DRMdev.iod_ca + DRM_NUMWD));
|
|
}
|
|
|
|
switch (status) {
|
|
case DRMIO_MORE:
|
|
sim_activate(uptr, DRM_SECTOR_WAIT);
|
|
break;
|
|
|
|
case DRMIO_PROTECT:
|
|
DRMdev.STATUS |= IO_1752_PROTF;
|
|
error = "Protection Fault";
|
|
goto err;
|
|
|
|
case DRMIO_ADDRERR:
|
|
DRMdev.STATUS |= IO_1752_OVERR;
|
|
error = "Address Error";
|
|
err:
|
|
DRMdev.iod_compare = FALSE;
|
|
DRMdev.iod_state = DRM_IDLE;
|
|
|
|
if ((drm_dev.dctrl & DBG_DERROR) != 0)
|
|
fprintf(DBGOUT,
|
|
"%sDRM - Read/Write failed - %s\r\n",
|
|
INTprefix, error);
|
|
|
|
fw_IOalarm(FALSE, &drm_dev, &DRMdev, "Alarm");
|
|
break;
|
|
|
|
case DRMIO_DONE:
|
|
DRMdev.iod_compare = FALSE;
|
|
DRMdev.iod_event = Instructions;
|
|
DRMdev.iod_state = DRM_IDLE;
|
|
|
|
if ((drm_dev.dctrl & DBG_DTRACE) != 0)
|
|
fprintf(DBGOUT,
|
|
"%sDRM - Read/Write transfer complete\r\n", INTprefix);
|
|
|
|
DRMdev.STATUS |= IO_ST_DATA;
|
|
fw_IOcompleteEOP2(FALSE, &drm_dev, &DRMdev, 0xFFFF, "Transfer complete");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Unit service */
|
|
|
|
t_stat drm_svc(UNIT *uptr)
|
|
{
|
|
if ((drm_dev.dctrl & DBG_DTRACE) != 0) {
|
|
fprintf(DBGOUT, "%s[DRM: drm_svc() entry]\r\n", INTprefix);
|
|
if ((drm_dev.dctrl & DBG_DSTATE) != 0)
|
|
DRMstate("svc_entry", &drm_dev, &DRMdev);
|
|
}
|
|
|
|
switch (DRMdev.iod_state) {
|
|
case DRM_IDLE:
|
|
/*
|
|
* Unit is idle, nothing to do.
|
|
*/
|
|
break;
|
|
|
|
case DRM_WRITE:
|
|
case DRM_READ:
|
|
DrumIO(uptr, DRMdev.iod_state);
|
|
break;
|
|
}
|
|
|
|
if ((drm_dev.dctrl & DBG_DTRACE) != 0) {
|
|
fprintf(DBGOUT, "%s[DRM: drm_svc() exit]\r\n", INTprefix);
|
|
if ((drm_dev.dctrl & DBG_DSTATE) != 0)
|
|
DRMstate("svc_exit", &drm_dev, &DRMdev);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Reset routine */
|
|
|
|
t_stat drm_reset(DEVICE *dptr)
|
|
{
|
|
DRMdev.STATUS = 0;
|
|
if ((drm_unit.flags & UNIT_ATT) != 0)
|
|
DRMdev.STATUS |= IO_ST_READY | IO_ST_DATA;
|
|
|
|
DRMdev.SASTATUS =
|
|
DRMdev.CASTATUS =
|
|
DRMdev.DATASTATUS = 0;
|
|
|
|
DRMdev.iod_trk =
|
|
DRMdev.iod_sec = 0;
|
|
|
|
DRMdev.iod_event = Instructions;
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Attach routine */
|
|
|
|
t_stat drm_attach (UNIT *uptr, CONST char *cptr)
|
|
{
|
|
t_addr capac = uptr->capac;
|
|
t_stat r;
|
|
t_offset tracks;
|
|
|
|
r = attach_unit(uptr, cptr);
|
|
if (r != SCPE_OK)
|
|
return r;
|
|
|
|
/*
|
|
* If this ia a newly created file, set the drum size appropriately.
|
|
*/
|
|
if (sim_fsize_ex(uptr->fileref) == 0)
|
|
sim_set_fsize(uptr->fileref, capac);
|
|
|
|
/*
|
|
* If we are attaching to an existing file, make sure that its size:
|
|
*
|
|
* - is a multiple of 3072 words
|
|
* - is between 64 and 1024 tracks (each of 3072 words)
|
|
* - is a multiple of 64 tracks
|
|
*/
|
|
tracks = sim_fsize_ex(uptr->fileref) / (DRM_NUMSC * DRM_NUMBY);
|
|
if (((sim_fsize_ex(uptr->fileref) % (DRM_NUMSC * DRM_NUMBY)) != 0) ||
|
|
(tracks < DRM_MINTRACKS) ||
|
|
(tracks > DRM_MAXTRACKS) ||
|
|
((tracks & (DRM_MINTRACKS - 1)) != 0)) {
|
|
detach_unit(uptr);
|
|
uptr->capac = capac;
|
|
return sim_messagef(SCPE_OPENERR, "Invalid file size");
|
|
}
|
|
DRMdev.STATUS = IO_ST_READY | IO_ST_DATA;
|
|
DRMdev.iod_tracks = (uint16)tracks;
|
|
DRMdev.iod_event = Instructions;
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Detach routine */
|
|
|
|
t_stat drm_detach(UNIT *uptr)
|
|
{
|
|
sim_cancel(uptr);
|
|
DRMdev.STATUS &= ~(IO_ST_READY | IO_ST_DATA);
|
|
return detach_unit(uptr);
|
|
}
|
|
|
|
/*
|
|
* Change drum capacity
|
|
*/
|
|
t_stat drm_set_size(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
if ((uptr->flags & UNIT_ATT) != 0)
|
|
return SCPE_ALATT;
|
|
|
|
drm_unit.capac = val * DRM_NUMSC * DRM_NUMBY;
|
|
DRMdev.iod_tracks = val;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Check if I/O should be rejected */
|
|
|
|
t_bool DRMreject(IO_DEVICE *iod, t_bool output, uint8 reg)
|
|
{
|
|
if (output) {
|
|
if (reg != 0x1)
|
|
return (DRMdev.STATUS & IO_ST_BUSY) != 0;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Perform I/O */
|
|
|
|
enum IOstatus DRMin(IO_DEVICE *iod, uint8 reg)
|
|
{
|
|
/*
|
|
* The I/O framework passes input requests for the Director Status register
|
|
* and the Sector Address Status register so that we can return values
|
|
* which are dependent on the rotational position of the drum.
|
|
*/
|
|
|
|
/*
|
|
* Update the current sector value.
|
|
*/
|
|
if (DRMdev.iod_state == DRM_IDLE) {
|
|
t_uint64 sectors = (Instructions - DRMdev.iod_event) / DRM_SECTOR_WAIT;
|
|
|
|
if (sectors != 0) {
|
|
DRMdev.iod_sec = (DRMdev.iod_sec + sectors) & DRM_SEC_MASK;
|
|
DRMdev.iod_event += sectors * DRM_SECTOR_WAIT;
|
|
}
|
|
}
|
|
|
|
switch (reg) {
|
|
case 0x01:
|
|
/*
|
|
* Director Status
|
|
*/
|
|
if (DRMdev.iod_sec == (DRMdev.iod_isa & DRM_SEC_MASK))
|
|
DRMdev.STATUS |= IO_1752_SECCMP;
|
|
else DRMdev.STATUS &= ~IO_1752_SECCMP;
|
|
Areg = DRMdev.STATUS;
|
|
return IO_REPLY;
|
|
|
|
case 0x02:
|
|
/*
|
|
* Sector Address Status
|
|
*/
|
|
DRMdev.SASTATUS = (DRMdev.iod_trk << DRM_TRK_SHIFT) | DRMdev.iod_sec;
|
|
if (DRMdev.iod_compare)
|
|
DRMdev.SASTATUS |= DRM_COMPARE;
|
|
Areg = DRMdev.SASTATUS;
|
|
return IO_REPLY;
|
|
}
|
|
return IO_REJECT;
|
|
}
|
|
|
|
enum IOstatus DRMout(IO_DEVICE *iod, uint8 reg)
|
|
{
|
|
switch (reg) {
|
|
case 0x00:
|
|
/*
|
|
* Initiate Drum Write Operation
|
|
*/
|
|
StartDrumIO(TRUE);
|
|
break;
|
|
|
|
case 0x01:
|
|
/*
|
|
* Director function.
|
|
*/
|
|
doDirectorFunc(&drm_dev, FALSE);
|
|
break;
|
|
|
|
case 0x04:
|
|
/*
|
|
* Initiate Drum Read Operation
|
|
*/
|
|
StartDrumIO(FALSE);
|
|
break;
|
|
|
|
case 0x08:
|
|
/*
|
|
* Load Initial Sector Address Register.
|
|
*/
|
|
DRMdev.iod_isa = IOAreg;
|
|
DRMdev.iod_trk = (IOAreg & DRM_TRK_MASK) >> DRM_TRK_SHIFT;
|
|
break;
|
|
|
|
case 0x0C:
|
|
/*
|
|
* Load Initial Core Address Register
|
|
*/
|
|
DRMdev.iod_ica = DRMdev.CASTATUS = IOAreg;
|
|
DRMdev.iod_compare = DRMdev.iod_ica == DRMdev.iod_fca;
|
|
break;
|
|
|
|
case 0x0E:
|
|
/*
|
|
* Load Final Core Address Register
|
|
*/
|
|
DRMdev.iod_fca = IOAreg;
|
|
DRMdev.iod_compare = DRMdev.iod_ica == DRMdev.iod_fca;
|
|
break;
|
|
}
|
|
/*
|
|
* Any non-rejected output clears EOP interrupt.
|
|
*/
|
|
if ((DRMdev.STATUS & IO_ST_EOP) != 0)
|
|
DRMdev.STATUS &= ~(IO_ST_INT | IO_ST_EOP);
|
|
rebuildPending();
|
|
|
|
return IO_REPLY;
|
|
}
|
|
|
|
/*
|
|
* Device clear routine. Clear controller operation from a director
|
|
* function - same as device reset but don't clear CASTATUS and DATASTATUS
|
|
*/
|
|
void DRMclear(DEVICE *dptr)
|
|
{
|
|
DRMdev.STATUS = 0;
|
|
if ((drm_unit.flags & UNIT_ATT) != 0)
|
|
DRMdev.STATUS |= IO_ST_READY | IO_ST_DATA;
|
|
|
|
DRMdev.SASTATUS = 0;
|
|
|
|
DRMdev.iod_trk =
|
|
DRMdev.iod_sec = 0;
|
|
|
|
DRMdev.iod_event = Instructions;
|
|
}
|
|
|
|
/*
|
|
* Address decode routine. If bit 0 of an output register address is set,
|
|
* clear bits 1 - 7 since they are ignored.
|
|
*/
|
|
uint8 DRMdecode(IO_DEVICE *iod, t_bool output, uint8 reg)
|
|
{
|
|
if (output && ((reg & 0x01) != 0))
|
|
reg &= 0x01;
|
|
return reg;
|
|
}
|
|
|
|
/*
|
|
* Autoload support
|
|
*/
|
|
t_stat DRMautoload(void)
|
|
{
|
|
UNIT *uptr = &drm_unit;
|
|
|
|
if ((uptr->flags & UNIT_ATT) != 0) {
|
|
uint32 i;
|
|
|
|
for (i = 0; i < DRM_AUTOLOAD; i++) {
|
|
t_offset offset = i * DRM_NUMBY;
|
|
void *buf = &M[i * DRM_NUMBY];
|
|
|
|
if (sim_fseeko(uptr->fileref, offset, SEEK_SET) ||
|
|
(sim_fread(buf, sizeof(uint16), DRM_NUMWD, uptr->fileref) != DRM_NUMWD))
|
|
return SCPE_IOERR;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
return SCPE_UNATT;
|
|
}
|
|
|
|
t_stat drm_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
|
{
|
|
const char helpString[] =
|
|
/****************************************************************************/
|
|
" The %D device is a 1752 drum memory controller.\n"
|
|
"1 Hardware Description\n"
|
|
" The 1752-1/2/3/4 consists of a controller and field expandable drum\n"
|
|
" storage from 196608 to 1572864 words. A custom order may provide\n"
|
|
" additional storage up to 3145728 words.\n"
|
|
"2 Equipment Address\n"
|
|
" Drum controllers are typically set to equipment address 2. This address\n"
|
|
" may be changed by:\n\n"
|
|
"+sim> SET %D EQUIPMENT=hexValue\n\n"
|
|
"2 $Registers\n"
|
|
"\n"
|
|
" These registers contain the emulated state of the device. These values\n"
|
|
" don't necessarily relate to any detail of the original device being\n"
|
|
" emulated but are merely internal details of the emulation. STATUS always\n"
|
|
" contains the current status of the device as it would be read by an\n"
|
|
" application program.\n"
|
|
"1 Configuration\n"
|
|
" A %D device is configured with various simh SET and ATTACH commands\n"
|
|
"2 $Set commands\n";
|
|
|
|
return scp_help(st, dptr, uptr, flag, helpString, cptr);
|
|
}
|