blob: 9f43e3b9d30ab4319a74516d124998563d87a69a [file] [log] [blame] [raw]
/* h316_dp.c: Honeywell 4623, 4651, 4720 disk simulator
Copyright (c) 2003-2015, Robert M. Supnik
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
ROBERT M SUPNIK 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 Robert M Supnik shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
dp 4623 disk subsystem
4651 disk subsystem
4720 disk subsystem
03-Jul-13 RLA compatibility changes for extended interrupts
19-Mar-12 RMS Fixed declaration of chan_req (Mark Pizzolato)
04-Sep-05 RMS Fixed missing return (Peter Schorn)
15-Jul-05 RMS Fixed bug in attach routine
01-Dec-04 RMS Fixed bug in skip on !seeking
The Honeywell disks have the unique characteristic of supporting variable
formatting, on a per track basis. To accomodate this, each track is
simulated as 2048 words, divided into records. (2048 words accomodates
the largest record of 1891 + 8 overhead words.) A record is structured
as follows:
word 0 record length n (0 = end of track)
word 1 record address (16b, uninterpreted by the simulator)
word 2 record extension (0 to 4 words of permitted 'overwrite')
word 3 first data word
:
word 3+n-1 last data word
word 3+n checksum word
word 4+n first extension word
:
word 7+n fourth extension word
word 8+n start of next record
Formatting is done in two ways. The SET DPn FORMAT=k command formats
unit n with k records per track, each record having the maximum allowable
record size and a standard record address; or with k words per record.
Alternately, the simulator allows programmatic formating. When a track
is formated, the program supplies record parameters as follows:
word 0 record address
words 1-n data words
word n+1 gap size in bits
To make this work, the simulator tracks the consumption of bits in the
track, against the track capacity in bits. Bit consumption is:
16.5 * 16 for overhead (including address and checksum)
n * 16 for data
'gap' for gap, which must be at least 5% of the record length
*/
#include "h316_defs.h"
#include <math.h>
#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_WLK (1 << UNIT_V_WLK)
#define FNC u3 /* saved function */
#define CYL u4 /* actual cylinder */
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */
#define DP_TRKLEN 2048 /* track length, words */
#define DP_NUMDRV 8 /* max # drives */
#define DP_NUMTYP 3 /* # controller types */
/* Record format */
#define REC_LNT 0 /* length (unextended) */
#define REC_ADDR 1 /* address */
#define REC_EXT 2 /* extension (0-4) */
#define REC_DATA 3 /* start of data */
#define REC_OVHD 8 /* overhead words */
#define REC_MAXEXT 4 /* maximum extension */
#define REC_OVHD_WRDS 16.5 /* 16.5 words */
#define REC_OVHD_BITS ((16 * 16) + 8)
/* Status word, ^ = dynamic */
#define STA_BUSY 0100000 /* busy */
#define STA_RDY 0040000 /* ready */
#define STA_ADRER 0020000 /* address error */
#define STA_FMTER 0010000 /* format error */
#define STA_HNLER 0004000 /* heads not loaded (NI) */
#define STA_OFLER 0002000 /* offline */
#define STA_SEKER 0001000 /* seek error */
#define STA_MBZ 0000700
#define STA_WPRER 0000040 /* write prot error */
#define STA_UNSER 0000020 /* unsafe */
#define STA_CSMER 0000010 /* checksum error */
#define STA_DTRER 0000004 /* transfer rate error */
#define STA_ANYER 0000002 /* any error^ */
#define STA_EOR 0000001 /* end of record */
#define STA_ALLERR (STA_ADRER|STA_FMTER|STA_HNLER|STA_OFLER|STA_SEKER|\
STA_WPRER|STA_UNSER|STA_DTRER)
/* Functions */
#define FNC_SK0 0000 /* recalibrate */
#define FNC_SEEK 0001 /* seek */
#define FNC_RCA 0002 /* read current */
#define FNC_UNL 0004 /* unload */
#define FNC_FMT 0005 /* format */
#define FNC_RW 0006 /* read/write */
#define FNC_STOP 0010 /* stop format */
#define FNC_RDS 0011 /* read status */
#define FNC_DMA 0013 /* DMA/DMC */
#define FNC_AKI 0014 /* acknowledge intr */
#define FNC_IOBUS 0017 /* IO bus */
#define FNC_2ND 0020 /* second state */
#define FNC_3RD 0040 /* third state */
#define FNC_4TH 0060 /* fourth state */
#define FNC_5TH 0100 /* fifth state */
/* Command word 1 */
#define CW1_RW 0100000 /* read/write */
#define CW1_DIR 0000400 /* seek direction */
#define CW1_V_UNIT 11 /* unit select */
#define CW1_V_HEAD 6 /* head select */
#define CW1_V_OFFS 0 /* seek offset */
#define CW1_GETUNIT(x) (((x) >> CW1_V_UNIT) & dp_tab[dp_ctype].umsk)
#define CW1_GETHEAD(x) (((x) >> CW1_V_HEAD) & dp_tab[dp_ctype].hmsk)
#define CW1_GETOFFS(x) (((x) >> CW1_V_OFFS) & dp_tab[dp_ctype].cmsk)
/* OTA states */
#define OTA_NOP 0 /* normal */
#define OTA_CW1 1 /* expecting CW1 */
#define OTA_CW2 2 /* expecting CW2 */
/* Transfer state */
#define XIP_UMSK 007 /* unit mask */
#define XIP_SCHED 010 /* scheduled */
#define XIP_WRT 020 /* write */
#define XIP_FMT 040 /* format */
/* The H316/516 disk emulator supports three disk controllers:
controller units cylinders surfaces data words per track
4651 4 203 2 1908.25
4623 8 203 10 1816.5
4720 8 203 20 1908.25
Disk types may not be intermixed on the same controller.
*/
#define TYPE_4651 0
#define UNIT_4651 4
#define CYL_4651 203
#define SURF_4651 2
#define WRDS_4651 1908.25
#define UMSK_4651 0003
#define HMSK_4651 0001
#define CMSK_4651 0377
#define CAP_4651 (CYL_4651*SURF_4651*DP_TRKLEN)
#define TYPE_4623 1
#define UNIT_4623 8
#define CYL_4623 203
#define SURF_4623 10
#define WRDS_4623 1816.5
#define UMSK_4623 0007
#define HMSK_4623 0017
#define CMSK_4623 0377
#define CAP_4623 (CYL_4623*SURF_4623*DP_TRKLEN)
#define TYPE_4720 2
#define UNIT_4720 8
#define CYL_4720 203
#define SURF_4720 20
#define WRDS_4720 1908.25
#define UMSK_4720 0007
#define HMSK_4720 0037
#define CMSK_4720 0377
#define CAP_4720 (CYL_4720*SURF_4720*DP_TRKLEN)
struct drvtyp {
char *name;
uint32 numu;
uint32 cyl;
uint32 surf;
uint32 cap;
uint32 umsk;
uint32 hmsk;
uint32 cmsk;
float wrds;
};
#define DP_DRV(d) \
#d, \
UNIT_##d, CYL_##d, SURF_##d, CAP_##d, \
UMSK_##d, HMSK_##d, CMSK_##d, WRDS_##d
static struct drvtyp dp_tab[] = {
{ DP_DRV (4651) },
{ DP_DRV (4623) },
{ DP_DRV (4720) }
};
extern int32 dev_int, dev_enb;
extern uint32 chan_req;
extern int32 stop_inst;
extern uint32 dma_ad[DMA_MAX];
uint32 dp_cw1 = 0; /* cmd word 1 */
uint32 dp_cw2 = 0; /* cmd word 2 */
uint32 dp_fnc = 0; /* saved function */
uint32 dp_buf = 0; /* buffer */
uint32 dp_otas = 0; /* state */
uint32 dp_sta = 0; /* status */
uint32 dp_defint = 0; /* deferred seek int */
uint32 dp_ctype = TYPE_4651; /* controller type */
uint32 dp_dma = 0; /* DMA/DMC */
uint32 dp_eor = 0; /* end of range */
uint32 dp_xip = 0; /* transfer in prog */
uint32 dp_csum = 0; /* parity checksum */
uint32 dp_rptr = 0; /* start of record */
uint32 dp_wptr = 0; /* word ptr in record */
uint32 dp_bctr = 0; /* format bit cntr */
uint32 dp_gap = 0; /* format gap size */
uint32 dp_stopioe = 1; /* stop on error */
int32 dp_stime = 1000; /* seek per cylinder */
int32 dp_xtime = 10; /* xfer per word */
int32 dp_btime = 30; /* busy time */
uint16 dpxb[DP_TRKLEN]; /* track buffer */
int32 dpio (int32 inst, int32 fnc, int32 dat, int32 dev);
t_stat dp_svc (UNIT *uptr);
t_stat dp_reset (DEVICE *dptr);
t_stat dp_attach (UNIT *uptr, char *cptr);
t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat dp_go (uint32 dma);
t_stat dp_go1 (uint32 dat);
t_stat dp_go2 (uint32 dat);
t_stat dp_rdtrk (UNIT *uptr, uint16 *buf, uint32 cyl, uint32 hd);
t_stat dp_wrtrk (UNIT *uptr, uint16 *buf, uint32 cyl, uint32 hd);
t_bool dp_findrec (uint32 addr);
t_stat dp_wrwd (UNIT *uptr, uint32 dat);
t_stat dp_wrdone (UNIT *uptr, uint32 flg);
t_stat dp_done (uint32 req, uint32 f);
t_stat dp_setformat (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat dp_showformat (FILE *st, UNIT *uptr, int32 val, void *desc);
/* DP data structures
dp_dev DP device descriptor
dp_unit DP unit list
dp_reg DP register list
dp_mod DP modifier list
*/
DIB dp_dib = { DP, 1, DMC1, IOBUS, INT_V_DP, INT_V_NONE, &dpio, 0 };
UNIT dp_unit[] = {
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, CAP_4651) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, CAP_4651) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, CAP_4651) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, CAP_4651) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, CAP_4651) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, CAP_4651) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, CAP_4651) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, CAP_4651) }
};
REG dp_reg[] = {
{ ORDATA (STA, dp_sta, 16) },
{ ORDATA (BUF, dp_buf, 16) },
{ ORDATA (FNC, dp_fnc, 4) },
{ ORDATA (CW1, dp_cw1, 16) },
{ ORDATA (CW2, dp_cw2, 16) },
{ ORDATA (CSUM, dp_csum, 16) },
{ FLDATA (BUSY, dp_sta, 15) },
{ FLDATA (RDY, dp_sta, 14) },
{ FLDATA (EOR, dp_eor, 0) },
{ FLDATA (DEFINT, dp_defint, 0) },
{ FLDATA (INTREQ, dev_int, INT_V_DP) },
{ FLDATA (ENABLE, dev_enb, INT_V_DP) },
{ BRDATA (TBUF, dpxb, 8, 16, DP_TRKLEN) },
{ ORDATA (RPTR, dp_rptr, 11), REG_RO },
{ ORDATA (WPTR, dp_wptr, 11), REG_RO },
{ ORDATA (BCTR, dp_bctr, 15), REG_RO },
{ ORDATA (GAP, dp_gap, 16), REG_RO },
{ DRDATA (STIME, dp_stime, 24), REG_NZ + PV_LEFT },
{ DRDATA (XTIME, dp_xtime, 24), REG_NZ + PV_LEFT },
{ DRDATA (BTIME, dp_btime, 24), REG_NZ + PV_LEFT },
{ FLDATA (CTYPE, dp_ctype, 0), REG_HRO },
{ URDATA (UCYL, dp_unit[0].CYL, 10, 8, 0,
DP_NUMDRV, PV_LEFT | REG_HRO) },
{ URDATA (UFNC, dp_unit[0].FNC, 8, 7, 0,
DP_NUMDRV, REG_HRO) },
{ URDATA (CAPAC, dp_unit[0].capac, 10, T_ADDR_W, 0,
DP_NUMDRV, PV_LEFT | REG_HRO) },
{ ORDATA (OTAS, dp_otas, 2), REG_HRO },
{ ORDATA (XIP, dp_xip, 6), REG_HRO },
{ ORDATA (CHAN, dp_dib.chan, 5), REG_HRO },
{ FLDATA (STOP_IOE, dp_stopioe, 0) },
{ NULL }
};
MTAB dp_mod[] = {
{ UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
{ MTAB_XTD | MTAB_VDV, TYPE_4623, NULL, "4623",
&dp_settype, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, TYPE_4651, NULL, "4651",
&dp_settype, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, TYPE_4720, NULL, "4720",
&dp_settype, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL,
NULL, &dp_showtype, NULL },
{ MTAB_XTD|MTAB_VDV, 0, NULL, "DMC",
&io_set_dmc, NULL, NULL },
{ MTAB_XTD|MTAB_VDV, 0, NULL, "DMA",
&io_set_dma, NULL, NULL },
{ MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL,
NULL, &io_show_chan, NULL },
{ MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "FORMAT", "FORMAT",
&dp_setformat, &dp_showformat, NULL },
{ 0 }
};
DEVICE dp_dev = {
"DP", dp_unit, dp_reg, dp_mod,
DP_NUMDRV, 8, 24, 1, 8, 16,
NULL, NULL, &dp_reset,
NULL, &dp_attach, NULL,
&dp_dib, DEV_DISABLE
};
/* IOT routines */
int32 dpio (int32 inst, int32 fnc, int32 dat, int32 dev)
{
int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */
int32 u;
UNIT *uptr;
switch (inst) { /* case on opcode */
case ioOCP: /* OCP */
switch (fnc) { /* case on function */
case FNC_SK0: case FNC_SEEK: case FNC_RCA: /* data transfer */
case FNC_UNL: case FNC_FMT: case FNC_RW:
dp_go (fnc); /* if !busy, start */
break;
case FNC_STOP: /* stop transfer */
if (dp_xip) { /* transfer in prog? */
uptr = dp_dev.units + (dp_xip & XIP_UMSK); /* get unit */
sim_cancel (uptr); /* stop operation */
if (dp_xip & (XIP_WRT|XIP_FMT)) /* write or format? */
dp_wrdone (uptr, /* write track */
((dp_xip & XIP_FMT) && /* check fmt state */
(uptr->FNC != (FNC_FMT|FNC_2ND)))?
STA_DTRER: 0);
else dp_done (1, dp_csum? STA_CSMER: 0);/* no, just clr busy */
dp_xip = 0; /* clear flag */
}
dp_otas = OTA_NOP; /* clear state */
dp_sta = dp_sta & ~STA_BUSY; /* clear busy */
break;
case FNC_RDS: /* read status */
if (dp_sta & STA_BUSY) /* ignore if busy */
return dat;
dp_sta = (dp_sta | STA_RDY) & ~(STA_MBZ | STA_ANYER);
if (dp_sta & STA_ALLERR) dp_sta = dp_sta | STA_ANYER;
dp_buf = dp_sta;
if (dp_dma && Q_DMA (ch)) /* DMA? set chan req */
SET_CH_REQ (ch);
break;
case FNC_DMA: /* set DMA/DMC */
dp_dma = 1;
break;
case FNC_IOBUS: /* set IO bus */
dp_dma = 0;
break;
case FNC_AKI: /* ack intr */
CLR_INT (INT_DP);
break;
default: /* undefined */
return IOBADFNC (dat);
}
break;
case ioINA: /* INA */
if (fnc) /* fnc 0 only */
return IOBADFNC (dat);
if (dp_sta & STA_RDY) { /* ready? */
dp_sta = dp_sta & ~STA_RDY; /* clear ready */
return IOSKIP (dat | dp_buf); /* ret buf, skip */
}
break;
case ioOTA: /* OTA */
if (fnc) /* fnc 0 only */
return IOBADFNC (dat);
if (dp_sta & STA_RDY) { /* ready? */
dp_sta = dp_sta & ~STA_RDY; /* clear ready */
dp_buf = dat; /* store buf */
if (dp_otas == OTA_CW1) /* expecting CW1? */
dp_go1 (dat);
else if (dp_otas == OTA_CW2) /* expecting CW2? */
dp_go2 (dat);
return IOSKIP (dat);
}
break;
case ioSKS: /* SKS */
u = 7; /* assume unit 7 */
switch (fnc) {
case 000: /* ready */
if (dp_sta & STA_RDY)
return IOSKIP (dat);
break;
case 001: /* !interrupting */
if (!TST_INTREQ (INT_DP))
return IOSKIP (dat);
break;
case 002: /* operational */
if (!(dp_sta & (STA_BUSY | STA_ALLERR)))
return IOSKIP (dat);
break;
case 003: /* !error */
if (!(dp_sta & STA_ALLERR))
return IOSKIP (dat);
break;
case 004: /* !busy */
if (!(dp_sta & STA_BUSY))
return IOSKIP (dat);
break;
case 011: case 012: case 013: /* !not seeking 0-6 */
case 014: case 015: case 016: case 017:
u = fnc - 011;
case 007: /* !not seeking 7 */
if (!sim_is_active (&dp_unit[u]) || /* quiescent? */
(dp_unit[u].FNC != (FNC_SEEK | FNC_2ND)))
return IOSKIP (dat); /* seeking sets late */
break;
}
break;
case ioEND: /* end of range */
dp_eor = 1; /* transfer done */
break;
}
return dat;
}
/* Start new operation - recal, seek, read address, format, read/write */
t_stat dp_go (uint32 fnc)
{
int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */
if (dp_sta & STA_BUSY) /* ignore if busy */
return SCPE_OK;
dp_fnc = fnc; /* save function */
dp_xip = 0; /* transfer not started */
dp_eor = 0; /* not end of range */
dp_csum = 0; /* init checksum */
dp_otas = OTA_CW1; /* expect CW1 */
dp_sta = (dp_sta | STA_BUSY | STA_RDY) & ~(STA_ALLERR | STA_EOR);
if (dp_dma && Q_DMA (ch)) { /* DMA and DMA channel? */
SET_CH_REQ (ch); /* set channel request */
dma_ad[ch] = dma_ad[ch] & ~DMA_IN; /* force output */
}
return SCPE_OK;
}
/* Process command word 1 - recal, seek, read address, format, read/write */
t_stat dp_go1 (uint32 dat)
{
int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */
uint32 u = CW1_GETUNIT (dat);
UNIT *uptr = dp_dev.units + u;
dp_cw1 = dat; /* store CW1 */
dp_otas = OTA_NOP; /* assume no CW2 */
uptr->FNC = dp_fnc;
if (sim_is_active (uptr)) /* still seeking? */
return dp_done (1, STA_UNSER); /* unsafe */
if (!(uptr->flags & UNIT_ATT)) /* not attached? */
return dp_done (1, STA_OFLER); /* offline */
switch (dp_fnc) { /* case on function */
case FNC_SEEK: /* seek */
case FNC_SK0: /* recalibrate */
case FNC_UNL: /* unload */
sim_activate (uptr, dp_btime); /* quick timeout */
break;
case FNC_FMT: /* format */
if (uptr->flags & UNIT_WPRT) /* write protect? */
return dp_done (1, STA_WPRER); /* stop now */
case FNC_RCA: /* read current addr */
dp_xip = u | XIP_SCHED; /* operation started */
sim_activate (uptr, dp_xtime * 10); /* rotation timeout */
break;
case FNC_RW: /* read/write */
dp_otas = OTA_CW2; /* expect CW2 */
dp_sta = dp_sta | STA_RDY; /* set ready */
if (dp_dma && Q_DMA (ch)) /* DMA? set chan request */
SET_CH_REQ (ch);
break;
}
return SCPE_OK;
}
/* Process command word 2 - read/write only */
t_stat dp_go2 (uint32 dat)
{
uint32 u = CW1_GETUNIT (dp_cw1);
UNIT *uptr = dp_dev.units + u;
dp_cw2 = dat; /* store CW2 */
dp_otas = OTA_NOP; /* normal state */
sim_activate (uptr, dp_xtime * 10); /* rotation timeout */
dp_xip = u | XIP_SCHED; /* operation started */
return SCPE_OK;
}
/* Unit service */
t_stat dp_svc (UNIT *uptr)
{
int32 dcyl = 0; /* assume recalibrate */
int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */
uint32 h = CW1_GETHEAD (dp_cw1); /* head */
int32 st;
uint32 i, offs, lnt, ming, tpos;
t_stat r;
if (!(uptr->flags & UNIT_ATT)) { /* not attached? */
dp_done (1, STA_OFLER); /* offline */
return IORETURN (dp_stopioe, SCPE_UNATT);
}
switch (uptr->FNC) { /* case on function */
case FNC_SEEK: /* seek, need cyl */
offs = CW1_GETOFFS (dp_cw1); /* get offset */
if (dp_cw1 & CW1_DIR) /* get desired cyl */
dcyl = uptr->CYL - offs;
else dcyl = uptr->CYL + offs;
if ((offs == 0) ||
(dcyl < 0) ||
(dcyl >= (int32) dp_tab[dp_ctype].cyl))
return dp_done (1, STA_SEKER); /* bad seek? */
case FNC_SK0: /* recalibrate */
dp_sta = dp_sta & ~STA_BUSY; /* clear busy */
uptr->FNC = FNC_SEEK | FNC_2ND; /* next state */
st = (abs (dcyl - uptr->CYL)) * dp_stime; /* schedule seek */
if (st == 0)
st = dp_stime;
uptr->CYL = dcyl; /* put on cylinder */
sim_activate (uptr, st);
return SCPE_OK;
case FNC_SEEK | FNC_2ND: /* seek, 2nd state */
if (dp_sta & STA_BUSY) /* busy? queue intr */
dp_defint = 1;
else SET_INT (INT_DP); /* no, req intr */
return SCPE_OK;
case FNC_UNL: /* unload */
detach_unit (uptr); /* detach unit */
return dp_done (0, 0); /* clear busy, no intr */
case FNC_RCA: /* read current addr */
if (h >= dp_tab[dp_ctype].surf) /* invalid head? */
return dp_done (1, STA_ADRER); /* error */
if ((r = dp_rdtrk (uptr, dpxb, uptr->CYL, h))) /* get track; error? */
return r;
dp_rptr = 0; /* init rec ptr */
if (dpxb[dp_rptr + REC_LNT] == 0) /* unformated? */
return dp_done (1, STA_ADRER); /* error */
tpos = (uint32) (fmod (sim_gtime () / (double) dp_xtime, DP_TRKLEN));
do { /* scan down track */
dp_buf = dpxb[dp_rptr + REC_ADDR]; /* get rec addr */
dp_rptr = dp_rptr + dpxb[dp_rptr + REC_LNT] + REC_OVHD;
} while ((dp_rptr < tpos) && (dpxb[dp_rptr + REC_LNT] != 0));
if (dp_dma) { /* DMA/DMC? */
if (Q_DMA (ch)) /* DMA? */
dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */
SET_CH_REQ (ch); /* request chan */
}
return dp_done (1, STA_RDY); /* clr busy, set rdy */
/* Formating takes place in five states:
init - clear track buffer, start at first record
address - store address word
data - store data word(s) until end of range
pause - wait for gap word or stop command
gap - validate gap word, advance to next record
Note that formating is stopped externally by an OCP command; the
track buffer is flushed at that point. If the stop does not occur
in the proper state (gap word received), a format error occurs.
*/
case FNC_FMT: /* format */
for (i = 0; i < DP_TRKLEN; i++) /* clear track */
dpxb[i] = 0;
dp_xip = dp_xip | XIP_FMT; /* format in progress */
dp_rptr = 0; /* init record ptr */
dp_gap = 0; /* no gap before first */
dp_bctr = (uint32) (16.0 * dp_tab[dp_ctype].wrds); /* init bit cntr */
uptr->FNC = uptr->FNC | FNC_2ND; /* address state */
break; /* set up next word */
case FNC_FMT | FNC_2ND: /* format, address word */
dp_wptr = 0; /* clear word ptr */
if (dp_bctr < (dp_gap + REC_OVHD_BITS + 16)) /* room for gap, record? */
return dp_wrdone (uptr, STA_FMTER); /* no, format error */
dp_bctr = dp_bctr - dp_gap - REC_OVHD_BITS; /* charge for gap, ovhd */
dpxb[dp_rptr + REC_ADDR] = dp_buf; /* store address */
uptr->FNC = FNC_FMT | FNC_3RD; /* data state */
if (dp_eor) { /* record done? */
dp_eor = 0; /* clear for restart */
if (dp_dma) /* DMA/DMC? intr */
SET_INT (INT_DP);
}
break; /* set up next word */
case FNC_FMT | FNC_3RD: /* format, data word */
if (dp_sta & STA_RDY) /* timing failure? */
return dp_wrdone (uptr, STA_DTRER); /* write trk, err */
else { /* no, have word */
if (dp_bctr < 16) /* room for it? */
return dp_wrdone (uptr, STA_FMTER); /* no, error */
dp_bctr = dp_bctr - 16; /* charge for word */
dp_csum = dp_csum ^ dp_buf; /* update checksum */
dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_buf;/* store word */
dpxb[dp_rptr + REC_LNT]++; /* incr rec lnt */
dp_wptr++; /* incr word ptr */
}
if (dp_eor) { /* record done? */
dp_eor = 0; /* clear for restart */
if (dp_dma) /* DMA/DMC? intr */
SET_INT (INT_DP);
dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; /* store checksum */
uptr->FNC = uptr->FNC | FNC_4TH; /* pause state */
sim_activate (uptr, 5 * dp_xtime); /* schedule pause */
return SCPE_OK; /* don't request word */
}
break; /* set up next word */
case FNC_FMT | FNC_4TH: /* format, pause */
uptr->FNC = FNC_FMT | FNC_5TH; /* gap state */
break; /* request word */
case FNC_FMT | FNC_5TH: /* format, gap word */
ming = ((16 * dp_wptr) + REC_OVHD_BITS) / 20; /* min 5% gap */
if (dp_buf < ming) /* too small? */
return dp_wrdone (uptr, STA_FMTER); /* yes, format error */
dp_rptr = dp_rptr + dp_wptr + REC_OVHD; /* next record */
uptr->FNC = FNC_FMT | FNC_2ND; /* address state */
if (dp_eor) { /* record done? */
dp_eor = 0; /* clear for restart */
if (dp_dma) SET_INT (INT_DP); /* DMA/DMC? intr */
}
dp_gap = dp_buf; /* save gap */
dp_csum = 0; /* clear checksum */
break; /* set up next word */
/* Read and write take place in two states:
init - read track into buffer, find record, validate parameters
data - (read) fetch data from buffer, stop on end of range
- (write) write data into buffer, flush on end of range
*/
case FNC_RW: /* read/write */
if (h >= dp_tab[dp_ctype].surf) /* invalid head? */
return dp_done (1, STA_ADRER); /* error */
if ((r = dp_rdtrk (uptr, dpxb, uptr->CYL, h))) /* get track; error? */
return r;
if (!dp_findrec (dp_cw2)) /* find rec; error? */
return dp_done (1, STA_ADRER); /* address error */
if ((dpxb[dp_rptr + REC_LNT] >= (DP_TRKLEN - dp_rptr - REC_OVHD)) ||
(dpxb[dp_rptr + REC_EXT] >= REC_MAXEXT)) { /* bad lnt or ext? */
dp_done (1, STA_UNSER); /* stop simulation */
return STOP_DPFMT; /* bad format */
}
uptr->FNC = uptr->FNC | FNC_2ND; /* next state */
if (dp_cw1 & CW1_RW) { /* write? */
if (uptr->flags & UNIT_WPRT) /* write protect? */
return dp_done (1, STA_WPRER); /* error */
dp_xip = dp_xip | XIP_WRT; /* write in progress */
dp_sta = dp_sta | STA_RDY; /* set ready */
if (dp_dma) /* if DMA/DMC, req chan */
SET_CH_REQ (ch);
}
else if (Q_DMA (ch)) /* read; DMA? */
dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */
sim_activate (uptr, dp_xtime); /* schedule word */
dp_wptr = 0; /* init word pointer */
return SCPE_OK;
case FNC_RW | FNC_2ND: /* read/write, word */
if (dp_cw1 & CW1_RW) { /* write? */
if (dp_sta & STA_RDY) /* timing failure? */
return dp_wrdone (uptr, STA_DTRER); /* yes, error */
if ((r = dp_wrwd (uptr, dp_buf))) /* wr word, error? */
return r;
if (dp_eor) { /* transfer done? */
dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum;
return dp_wrdone (uptr, 0); /* clear busy, intr req */
}
}
else { /* read? */
lnt = dpxb[dp_rptr + REC_LNT] + dpxb[dp_rptr + REC_EXT];
dp_buf = dpxb[dp_rptr + REC_DATA + dp_wptr];/* current word */
dp_csum = dp_csum ^ dp_buf; /* xor to csum */
if ((dp_wptr > lnt) || dp_eor) /* transfer done? */
return dp_done (1,
(dp_csum? STA_CSMER: 0) | ((dp_wptr >= lnt)? STA_EOR: 0));
if (dp_sta & STA_RDY) /* data buf full? */
return dp_done (1, STA_DTRER); /* no, underrun */
dp_wptr++; /* next word */
}
break;
default:
return SCPE_IERR;
} /* end case */
dp_sta = dp_sta | STA_RDY; /* set ready */
if (dp_dma) /* if DMA/DMC, req chan */
SET_CH_REQ (ch);
sim_activate (uptr, dp_xtime); /* schedule word */
return SCPE_OK;
}
/* Read track */
t_stat dp_rdtrk (UNIT *uptr, uint16 *buf, uint32 c, uint32 h)
{
uint32 da = ((c * dp_tab[dp_ctype].surf) + h) * DP_TRKLEN;
int32 l;
fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET);
l = fxread (buf, sizeof (uint16), DP_TRKLEN, uptr->fileref);
for ( ; l < DP_TRKLEN; l++)
buf[l] = 0;
if (ferror (uptr->fileref)) {
sim_perror ("DP I/O error");
clearerr (uptr->fileref);
dp_done (1, STA_UNSER);
return SCPE_IOERR;
}
return SCPE_OK;
}
/* Write track */
t_stat dp_wrtrk (UNIT *uptr, uint16 *buf, uint32 c, uint32 h)
{
uint32 da = ((c * dp_tab[dp_ctype].surf) + h) * DP_TRKLEN;
fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET);
fxwrite (buf, sizeof (uint16), DP_TRKLEN, uptr->fileref);
if (ferror (uptr->fileref)) {
sim_perror ("DP I/O error");
clearerr (uptr->fileref);
dp_done (1, STA_UNSER);
return SCPE_IOERR;
}
return SCPE_OK;
}
/* Find record; true if found, false if not found */
t_bool dp_findrec (uint32 addr)
{
dp_rptr = 0;
do {
if (dpxb[dp_rptr + REC_LNT] == 0)
return FALSE;
if (dpxb[dp_rptr + REC_LNT] >= DP_TRKLEN)
return TRUE;
if (dpxb[dp_rptr + REC_ADDR] == addr)
return TRUE;
dp_rptr = dp_rptr + dpxb[dp_rptr + REC_LNT] + REC_OVHD;
} while (dp_rptr < DP_TRKLEN);
return FALSE;
}
/* Write next word to track buffer; return TRUE if ok, FALSE if next record trashed */
t_stat dp_wrwd (UNIT *uptr, uint32 dat)
{
uint32 lnt = dpxb[dp_rptr + REC_LNT];
t_stat r;
dp_csum = dp_csum ^ dat;
if (dp_wptr < lnt) {
dpxb[dp_rptr + REC_DATA + dp_wptr++] = dat;
return SCPE_OK;
}
if (dp_wptr < (lnt + REC_MAXEXT)) {
dpxb[dp_rptr + REC_EXT]++;
dpxb[dp_rptr + REC_DATA + dp_wptr++] = dat;
return SCPE_OK;
}
dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; /* write csum */
dpxb[dp_rptr + lnt + REC_OVHD] = 0; /* zap rest of track */
if ((r = dp_wrdone (uptr, STA_UNSER))) /* dump track */
return r;
return STOP_DPOVR;
}
/* Write done, dump track, clear busy */
t_stat dp_wrdone (UNIT *uptr, uint32 flg)
{
dp_done (1, flg);
return dp_wrtrk (uptr, dpxb, uptr->CYL, CW1_GETHEAD (dp_cw1));
}
/* Clear busy, set errors, request interrupt if required */
t_stat dp_done (uint32 req, uint32 flg)
{
dp_xip = 0; /* clear xfr in prog */
dp_sta = (dp_sta | flg) & ~(STA_BUSY | STA_MBZ); /* clear busy */
if (req || dp_defint) /* if req, set intr */
SET_INT (INT_DP);
dp_defint = 0; /* clr def intr */
return SCPE_OK;
}
/* Reset routine */
t_stat dp_reset (DEVICE *dptr)
{
int32 i;
dp_fnc = 0;
dp_cw1 = 0;
dp_cw2 = 0;
dp_sta = 0;
dp_buf = 0;
dp_xip = 0;
dp_eor = 0;
dp_dma = 0;
dp_csum = 0;
dp_rptr = 0;
dp_wptr = 0;
dp_bctr = 0;
dp_gap = 0;
dp_defint = 0;
for (i = 0; i < DP_NUMDRV; i++) { /* loop thru drives */
sim_cancel (&dp_unit[i]); /* cancel activity */
dp_unit[i].FNC = 0; /* clear function */
dp_unit[i].CYL = 0;
}
CLR_INT (INT_DP); /* clear int, enb */
CLR_ENB (INT_DP);
return SCPE_OK;
}
/* Attach routine, test formating */
t_stat dp_attach (UNIT *uptr, char *cptr)
{
t_stat r;
r = attach_unit (uptr, cptr);
if (r != SCPE_OK)
return r;
return dp_showformat (stdout, uptr, 0, NULL);
}
/* Set controller type */
t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc)
{
int32 i;
if ((val < 0) || (val >= DP_NUMTYP) || (cptr != NULL))
return SCPE_ARG;
for (i = 0; i < DP_NUMDRV; i++) {
if (dp_unit[i].flags & UNIT_ATT) return SCPE_ALATT;
}
for (i = 0; i < DP_NUMDRV; i++)
dp_unit[i].capac = dp_tab[val].cap;
dp_ctype = val;
return SCPE_OK;
}
/* Show controller type */
t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc)
{
if (dp_ctype >= DP_NUMTYP)
return SCPE_IERR;
fprintf (st, "%s", dp_tab[dp_ctype].name);
return SCPE_OK;
}
/* Set drive format
There is no standard format for record addresses. This routine
provides two schemes:
-S sequential addressing (starting from 0)
default geometric addressing (8b: cylinder, 5b: head, 3b: sector)
This routine also supports formatting by record count or word count:
-R argument is records per track
default argument is words per record
The relationship between words per record (W), bits per track (B),
and records per track (R), is as follows:
W = (B / (R + ((R - 1) / 20))) - 16.5
where (R - 1) / 20 is the "5% gap" and 16.5 is the overhead, in words,
per record.
*/
t_stat dp_setformat (UNIT *uptr, int32 val, char *cptr, void *desc)
{
uint32 h, c, cntr, rptr;
int32 i, nr, nw, inp;
uint16 tbuf[DP_TRKLEN];
float finp;
t_stat r;
if (uptr == NULL)
return SCPE_IERR;
if (cptr == NULL)
return SCPE_ARG;
if (!(uptr->flags & UNIT_ATT))
return SCPE_UNATT;
inp = (int32) get_uint (cptr, 10, 2048, &r);
if (r != SCPE_OK)
return r;
if (inp == 0)
return SCPE_ARG;
finp = (float) inp;
if (sim_switches & SWMASK ('R')) { /* format records? */
nr = inp;
nw = (int32) ((dp_tab[dp_ctype].wrds / (finp + ((finp - 1.0) / 20.0))) - REC_OVHD_WRDS);
if (nw <= 0)
return SCPE_ARG;
}
else {
nw = inp; /* format words */
nr = (int32) ((((20.0 * dp_tab[dp_ctype].wrds) / (finp + REC_OVHD_WRDS)) + 1.0) / 21.0);
if (nr <= 0)
return SCPE_ARG;
}
sim_printf ("Proposed format: records/track = %d, record size = %d\n", nr, nw);
if (!get_yn ("Formatting will destroy all data on this disk; proceed? [N]", FALSE))
return SCPE_OK;
for (c = cntr = 0; c < dp_tab[dp_ctype].cyl; c++) {
for (h = 0; h < dp_tab[dp_ctype].surf; h++) {
for (i = 0; i < DP_TRKLEN; i++)
tbuf[i] = 0;
rptr = 0;
for (i = 0; i < nr; i++) {
tbuf[rptr + REC_LNT] = nw & DMASK;
if (sim_switches & SWMASK ('S'))
tbuf[rptr + REC_ADDR] = cntr++;
else tbuf[rptr + REC_ADDR] = (c << 8) + (h << 3) + i;
rptr = rptr + nw + REC_OVHD;
}
if ((r = dp_wrtrk (uptr, tbuf, c, h)))
return r;
}
}
sim_printf ("Formatting complete\n");
return SCPE_OK;
}
/* Show format */
t_stat dp_showformat (FILE *st, UNIT *uptr, int32 val, void *desc)
{
uint32 c, h, rptr, rlnt, sec;
uint32 minrec = DP_TRKLEN;
uint32 maxrec = 0;
uint32 minsec = DP_TRKLEN;
uint32 maxsec = 0;
uint16 tbuf[DP_TRKLEN];
t_stat r;
if (uptr == NULL)
return SCPE_IERR;
if ((uptr->flags & UNIT_ATT) == 0)
return SCPE_UNATT;
for (c = 0; c < dp_tab[dp_ctype].cyl; c++) {
for (h = 0; h < dp_tab[dp_ctype].surf; h++) {
if ((r = dp_rdtrk (uptr, tbuf, c, h)))
return r;
rptr = 0;
rlnt = tbuf[rptr + REC_LNT];
if (rlnt == 0) {
if (c || h)
fprintf (st, "Unformatted track, cyl = %d, head = %d\n", c, h);
else fprintf (st, "Disk is unformatted\n");
return SCPE_OK;
}
for (sec = 0; rlnt != 0; sec++) {
if ((rptr + rlnt + REC_OVHD) >= DP_TRKLEN) {
fprintf (st, "Invalid record length %d, cyl = %d, head = %d, sect = %d\n",
rlnt, c, h, sec);
return SCPE_OK;
}
if (tbuf[rptr + REC_EXT] >= REC_MAXEXT) {
fprintf (st, "Invalid record extension %d, cyl = %d, head = %d, sect = %d\n",
tbuf[rptr + REC_EXT], c, h, sec);
return SCPE_OK;
}
if (rlnt > maxrec)
maxrec = rlnt;
if (rlnt < minrec)
minrec = rlnt;
rptr = rptr + rlnt + REC_OVHD;
rlnt = tbuf[rptr + REC_LNT];
}
if (sec > maxsec)
maxsec = sec;
if (sec < minsec)
minsec = sec;
}
}
if ((minrec == maxrec) && (minsec == maxsec))
fprintf (st, "Valid fixed format, records/track = %d, record size = %d\n",
minsec, minrec);
else if (minrec == maxrec)
fprintf (st, "Valid variable format, records/track = %d-%d, record size = %d\n",
minsec, maxsec, minrec);
else if (minsec == maxsec)
fprintf (st, "Valid variable format, records/track = %d, record sizes = %d-%d\n",
minsec, minrec, maxrec);
else fprintf (st, "Valid variable format, records/track = %d-%d, record sizes = %d-%d\n",
minsec, maxsec, minrec, maxrec);
return SCPE_OK;
}