simh-testsetgenerator/VAX/vax4xx_rd.c

937 lines
36 KiB
C

/* vax4xx_rd.c: HDC9224 hard disk simulator
Copyright (c) 2019, Matt Burke
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the author.
rd HDC9224 Hard Disk Controller
*/
#include "vax_defs.h"
#include "sim_disk.h"
#if defined(VAX_420)
#include "vax_ka420_rdrz_bin.h"
#else
#define BOOT_CODE_ARRAY NULL
#define BOOT_CODE_SIZE 0
#endif
#define RD_NUMDR 3
#define RD_RMV(u) ((drv_tab[GET_DTYPE (u->flags)].flgs & RDDF_RMV)? \
UF_RMV: 0)
#define RDDF_RMV 01 /* removable */
#define RD_NUMBY 512 /* bytes/sector */
#define RD_MAXFR (1 << 14) /* max transfer */
/* HDC commands */
#define CMD_RESET 1
#define CMD_SETREG 2
#define CMD_DESELECT 3
#define CMD_DRVSEL 4
#define CMD_RESTORE 5
#define CMD_STEP 6
#define CMD_POLL 7
#define CMD_RDID 8
#define CMD_FORMAT 9
#define CMD_RDTRK 10
#define CMD_RDPHY 11
#define CMD_RDLOG 12
#define CMD_WRPHY 13
#define CMD_WRLOG 14
#define CMD_UNKNOWN 15
/* drive status */
#define DST_WRF 0x01 /* write fault */
#define DST_RDY 0x02 /* ready */
#define DST_WPT 0x04 /* write protect */
#define DST_DS3 0x08 /* drive status 3 */
#define DST_TRK0 0x10 /* track 0 */
#define DST_SCOM 0x20 /* seek complete */
#define DST_IDX 0x40 /* index */
#define DST_SELA 0x80 /* sel ack */
/* chip status */
#define CST_SDRV 0x03 /* selected drive */
#define CST_CMPE 0x04 /* compare error */
#define CST_SYNCE 0x08 /* sync error */
#define CST_DELD 0x10 /* deleted data mark */
#define CST_ECCE 0x20 /* ECC error */
#define CST_ECCC 0x40 /* ECC correction attepted */
#define CST_RETR 0x80 /* retry required */
/* interrupt status port */
#define STAT_V_BAD 0 /* bad sect */
#define STAT_V_OVR 1 /* overrun */
#define STAT_V_RDYC 2 /* ready chng */
#define STAT_V_TRMC 3 /* term code */
#define STAT_M_TRMC 0x3
#define STAT_V_DONE 5 /* done */
#define STAT_V_DMARQ 6 /* dmareq */
#define STAT_V_INT 7 /* intpend */
#define STAT_BAD (1u << STAT_V_BAD)
#define STAT_OVR (1u << STAT_V_OVR)
#define STAT_RDYC (1u << STAT_V_RDYC)
#define STAT_TRMC (STAT_M_TRMC << STAT_V_TRMC)
#define STAT_DONE (1u << STAT_V_DONE)
#define STAT_DMARQ (1u << STAT_V_DMARQ)
#define STAT_INT (1u << STAT_V_INT)
/* termination codes */
#define TRM_OK 0 /* Success completion */
#define TRM_ERR_RD 1 /* Error in READ ID sequence */
#define TRM_ERR_VER 2 /* Error in VERIFY sequence */
#define TRM_ERR_TRAN 3 /* Error in DATA TRANSFER sequence */
#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE)
#define DBG_REG 0x0001 /* registers */
#define DBG_CMD 0x0002 /* commands */
#define DBG_RD 0x0004 /* disk reads */
#define DBG_WR 0x0008 /* disk writes */
#define DBG_REQ 0x0010 /* display transfer requests */
#define DBG_DSK 0x0020 /* display sim_disk activities */
#define DBG_DAT 0x0040 /* display transfer data */
#define CYL u3 /* current cylinder */
#define HEAD u4 /* current head */
#define STAT u5 /* drive status */
#define CMD u6 /* current command */
#define CUR_DRV (rd_cstat & CST_SDRV) /* currently selected drive */
#define GET_SPT(u) (drv_tab[GET_DTYPE (u->flags)].sect)
#define GET_SURF(u) (drv_tab[GET_DTYPE (u->flags)].surf)
#define GET_DA(u,c,h,s) ((c * (GET_SPT(u) * GET_SURF(u))) \
+ (h * GET_SPT(u)) + s)
/* The HDC9224 supports multiple disk drive types:
type sec surf cyl tpg gpc RCT LBNs
RX33 15 2 80 2 1 - 2400
RD31 17 4 615 4 1 3*8 41584
RD32 17 6 820 6 1 ? 83236
RD53 17 7 1024 7 1 5*8 138712
RD54 17 15 1225 15 1 7*8 311256
*/
#define RX33_DTYPE 0
#define RX33_SECT 15
#define RX33_SURF 2
#define RX33_CYL 80
#define RX33_TPG 2
#define RX33_XBN 0
#define RX33_DBN 0
#define RX33_LBN 2400
#define RX33_RCTS 0
#define RX33_RCTC 0
#define RX33_RBN 0
#define RX33_CYLP 0
#define RX33_CYLR 0
#define RX33_CCS 0
#define RX33_MED 0x25658021
#define RX33_FLGS RDDF_RMV
#define RD31_DTYPE 1
#define RD31_SECT 17
#define RD31_SURF 4
#define RD31_CYL 616 /* last unused */
#define RD31_TPG RD31_SURF
#define RD31_XBN 54
#define RD31_DBN 14
#define RD31_LBN 41584
#define RD31_RCTS 3
#define RD31_RCTC 8
#define RD31_RBN 100
#define RD31_CYLP 256
#define RD31_CYLR 615
#define RD31_CCS 9
#define RD31_MED 0x2564401F
#define RD31_FLGS 0
#define RD32_DTYPE 2
#define RD32_SECT 17
#define RD32_SURF 6
#define RD32_CYL 821 /* last unused */
#define RD32_TPG RD32_SURF
#define RD32_XBN 54
#define RD32_DBN 48
#define RD32_LBN 83236
#define RD32_RCTS 4
#define RD32_RCTC 8
#define RD32_RBN 200
#define RD32_CYLP 821
#define RD32_CYLR 821
#define RD32_CCS 14
#define RD32_MED 0x25644020
#define RD32_FLGS 0
#define RD53_DTYPE 3
#define RD53_SECT 17
#define RD53_SURF 8
#define RD53_CYL 1024 /* last unused */
#define RD53_TPG RD53_SURF
#define RD53_XBN 54
#define RD53_DBN 82
#define RD53_LBN 138712
#define RD53_RCTS 5
#define RD53_RCTC 8
#define RD53_RBN 280
#define RD53_CYLP 1024
#define RD53_CYLR 1024
#define RD53_CCS 13
#define RD53_MED 0x25644035
#define RD53_FLGS 0
#define RD54_DTYPE 4
#define RD54_SECT 17
#define RD54_SURF 15
#define RD54_CYL 1225 /* last unused */
#define RD54_TPG RD54_SURF
#define RD54_XBN 54
#define RD54_DBN 201
#define RD54_LBN 311256
#define RD54_RCTS 7
#define RD54_RCTC 8
#define RD54_RBN 609
#define RD54_CYLP 1225
#define RD54_CYLR 1225
#define RD54_CCS 14
#define RD54_MED 0x25644036
#define RD54_FLGS 0
#define UNIT_V_WLK (DKUF_V_UF + 0) /* hwre write lock */
#define UNIT_V_DTYPE (DKUF_V_UF + 1) /* drive type */
#define UNIT_M_DTYPE 0xF
#define UNIT_WLK (1u << UNIT_V_WLK)
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protected */
#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE)
struct drvtyp {
int32 sect; /* sectors */
int32 surf; /* surfaces */
int32 cyl; /* cylinders */
int32 tpg; /* trk/grp */
int32 xbn; /* XBN size */
int32 dbn; /* DBN size */
uint32 lbn; /* LBN size */
int32 rcts; /* RCT size */
int32 rctc; /* RCT copies */
int32 rbn; /* RBNs */
int32 cylp; /* first cyl for write precomp */
int32 cylr; /* first cyl for reduced write current */
int32 ccs; /* cyl/cyl skew */
int32 med; /* MSCP media */
int32 flgs; /* flags */
const char *name; /* name */
};
#define RD_DRV(d) \
d##_SECT, d##_SURF, d##_CYL, d##_TPG, \
d##_XBN, d##_DBN, d##_LBN, d##_RCTS, \
d##_RCTC, d##_RBN, d##_CYLP, d##_CYLR, \
d##_CCS, d##_MED, d##_FLGS
#define RD_SIZE(d) (d##_LBN * RD_NUMBY)
static struct drvtyp drv_tab[] = {
{ RD_DRV (RX33), "RX33" },{ RD_DRV (RD31), "RD31" },
{ RD_DRV (RD32), "RD32" },{ RD_DRV (RD53), "RD53" },
{ RD_DRV (RD54), "RD54" },
{ 0 }
};
int32 rd_cwait = 20; /* command wait time */
int32 rd_dwait = 20; /* data trasfer wait time */
int32 rd_rg_p = 0; /* register pointer */
int32 rd_stat = 0; /* interrupt status port */
int32 rd_dma = 0; /* DMA address */
int32 rd_dsect = 0; /* desired sector */
int32 rd_dhead = 0; /* desired head */
int32 rd_dcyl = 0; /* desired cylinder */
int32 rd_scnt = 0; /* sector count */
int32 rd_rtcnt = 0; /* retry count */
int32 rd_mode = 0; /* operating mode */
int32 rd_cstat = 0; /* chip status */
int32 rd_term = 0; /* termination conditions */
int32 rd_data = 0;
uint16 *rd_xb = NULL; /* xfer buffer */
t_stat rd_svc (UNIT *uptr);
t_stat rd_reset (DEVICE *dptr);
void rd_set_dstat (UNIT *uptr);
void rd_done (int32 term_code, t_bool setint);
void rd_cmd (int32 data);
int32 rd_decode_cmd (int32 data);
t_stat rd_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat rd_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat rd_attach (UNIT *uptr, CONST char *cptr);
t_stat rd_detach (UNIT *uptr);
t_stat rd_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
const char *rd_description (DEVICE *dptr);
/* RD data structures
rd_dev RD device descriptor
rd_unit RD unit list
rd_reg RD register list
rd_mod RD modifier list
*/
DIB rd_dib = {
RD_ROM_INDEX, BOOT_CODE_ARRAY, BOOT_CODE_SIZE
};
UNIT rd_unit[] = {
{ UDATA (&rd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
(RD54_DTYPE << UNIT_V_DTYPE), RD_SIZE (RD54)) },
{ UDATA (&rd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
(RD54_DTYPE << UNIT_V_DTYPE), RD_SIZE (RD54)) },
{ UDATA (&rd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+
(RX33_DTYPE << UNIT_V_DTYPE), RD_SIZE (RX33)) }
};
REG rd_reg[] = {
{ DRDATA (RPTR, rd_rg_p, 4), REG_HRO },
{ HRDATAD (STAT, rd_stat, 8, "Interrupt Status") },
{ HRDATAD (DMA, rd_dma, 24, "DMA Address") },
{ DRDATAD (DSECT, rd_dsect, 8, "Desired Sector") },
{ DRDATAD (DHEAD, rd_dhead, 4, "Desired Head") },
{ DRDATAD (DCYL, rd_dcyl, 11, "Desired Cylinder") },
{ URDATAD (CHEAD, rd_unit[0].HEAD, 10, 4, 0, RD_NUMDR, 0, "Current Head") },
{ URDATAD (CCYL, rd_unit[0].CYL, 10, 11, 0, RD_NUMDR, 0, "Current Cylinder") },
{ URDATAD (DSTAT, rd_unit[0].STAT, 16, 8, 0, RD_NUMDR, 0, "Drive Status") },
{ URDATAD (CMD, rd_unit[0].CMD, 10, 8, 0, RD_NUMDR, 0, "Current Command") },
{ DRDATAD (SCNT, rd_scnt, 8, "Sector Count") },
{ DRDATAD (RCNT, rd_rtcnt, 8, "Retry Count") },
{ HRDATAD (MODE, rd_mode, 8, "Operating Mode") },
{ HRDATAD (CSTAT, rd_cstat, 8, "Chip Status") },
{ HRDATAD (TCON, rd_term, 8, "Termination Conditions") },
{ DRDATAD (CWAIT, rd_cwait, 24, "Command wait time"), PV_LEFT + REG_NZ },
{ DRDATAD (DWAIT, rd_dwait, 24, "Data wait time"), PV_LEFT + REG_NZ },
{ URDATA (CAPAC, rd_unit[0].capac, 10, T_ADDR_W, 0, RD_NUMDR, REG_HRO | PV_LEFT) },
{ FLDATAD (INT, int_req[IPL_SCA], INT_V_SCA, "Interrupt pending flag") },
{ NULL }
};
DEBTAB rd_debug[] = {
{ "REG", DBG_REG, "trace read/write registers" },
{ "CMD", DBG_CMD, "display commands" },
{ "RD", DBG_RD, "display disk reads" },
{ "WR", DBG_WR, "display disk writes" },
{ "REQ", DBG_REQ, "display transfer requests" },
{ "DISK", DBG_DSK, "display sim_disk activities" },
{ "DATA", DBG_DAT, "display transfer data" },
{ 0 }
};
MTAB rd_mod[] = {
{ UNIT_WLK, 0, "write enabled", "WRITEENABLED",
NULL, NULL, NULL, "Write enable disk drive" },
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED",
NULL, NULL, NULL, "Write lock disk drive" },
{ MTAB_XTD|MTAB_VUN, RX33_DTYPE, NULL, "RX33",
&rd_set_type, NULL, NULL, "Set RX33 Disk Type" },
{ MTAB_XTD|MTAB_VUN, RD31_DTYPE, NULL, "RD31",
&rd_set_type, NULL, NULL, "Set RD31 Disk Type" },
{ MTAB_XTD|MTAB_VUN, RD32_DTYPE, NULL, "RD32",
&rd_set_type, NULL, NULL, "Set RD32 Disk Type" },
{ MTAB_XTD|MTAB_VUN, RD53_DTYPE, NULL, "RD53",
&rd_set_type, NULL, NULL, "Set RD53 Disk Type" },
{ MTAB_XTD|MTAB_VUN, RD54_DTYPE, NULL, "RD54",
&rd_set_type, NULL, NULL, "Set RD54 Disk Type" },
{ MTAB_XTD|MTAB_VUN, 0, "TYPE", NULL,
NULL, &rd_show_type, NULL, "Display device type" },
{ MTAB_XTD|MTAB_VUN | MTAB_VALR, 0, "FORMAT", "FORMAT={SIMH|VHD|RAW}",
&sim_disk_set_fmt, &sim_disk_show_fmt, NULL, "Display disk format" },
{ 0 }
};
DEVICE rd_dev = {
"RD", rd_unit, rd_reg, rd_mod,
RD_NUMDR, DEV_RDX, 20, 1, DEV_RDX, 8,
NULL, NULL, &rd_reset,
NULL, &rd_attach, &rd_detach,
&rd_dib, DEV_DEBUG | RD_FLAGS, 0,
rd_debug, NULL, NULL, &rd_help, NULL, NULL,
&rd_description
};
/* RD read
200C0000 register data access
200C0004 interrupt status
*/
int32 rd_rd (int32 pa)
{
int32 rg = (pa >> 2) & 3;
int32 data = 0;
UNIT *uptr = &rd_unit[CUR_DRV];
if (rd_dev.flags & DEV_DIS) /* disabled? */
return 0;
switch (rg) {
case 0: /* DKC_REG */
switch (rd_rg_p) {
case 0: /* UDC_DMA7 */
data = rd_dma & BMASK;
break;
case 1: /* UDC_DMA15 */
data = (rd_dma >> 8) & BMASK;
break;
case 2: /* UDC_DMA23 */
data = (rd_dma >> 16) & BMASK;
break;
case 3: /* UDC_DSECT */
data = rd_dsect & BMASK;
break;
case 4: /* UDC_CHEAD */
data = ((uptr->CYL & 0x700) >> 4) | \
(uptr->HEAD & 0xF);
break;
case 5: /* UDC_CCYL */
data = uptr->CYL & BMASK;
break;
case 6: /* temporary store */
data = 0;
break;
case 7: /* temporary store */
data = 0;
break;
case 8: /* UDC_CSTAT */
data = rd_cstat & BMASK;
break;
case 9: /* UDC_DSTAT */
data = uptr->STAT & BMASK;
break;
case 10: /* UDC_DATA */
data = rd_data & BMASK;
break;
}
sim_debug (DBG_REG, &rd_dev, "reg %d read, value = %X\n", rd_rg_p, data);
if (rd_rg_p < 10)
rd_rg_p++; /* Advance to next register */
break;
case 1: /* DKC_STAT */
data = rd_stat & BMASK;
rd_stat = rd_stat & ~(STAT_INT | STAT_RDYC);
sim_debug (DBG_REG, &rd_dev, "int status read, value = %X\n", data);
break;
}
return data;
}
/* RD write
200C0000 register data access
200C0004 controller command
*/
void rd_wr (int32 pa, int32 data, int32 access)
{
int32 rg = (pa >> 2) & 3;
if (rd_dev.flags & DEV_DIS) /* disabled? */
return;
switch (rg) {
case 0: /* DKC_REG */
switch (rd_rg_p) {
case 0: /* UDC_DMA7 */
rd_dma = (rd_dma & ~BMASK) | (data & BMASK);
break;
case 1: /* UDC_DMA15 */
rd_dma = (rd_dma & ~(BMASK << 8)) | ((data & BMASK) << 8);
break;
case 2: /* UDC_DMA23 */
rd_dma = (rd_dma & ~(BMASK << 16)) | ((data & BMASK) << 16);
break;
case 3: /* UDC_DSECT */
rd_dsect = data & BMASK;
break;
case 4: /* UDC_DHEAD */
rd_dhead = data & 0xF; /* head # in data <04:00> */
rd_dcyl = (rd_dcyl & ~(0x700)) | /* cyl <10:08> in data <06:04> */
((data & 0x70) << 4);
break;
case 5: /* UDC_DCYL */
rd_dcyl = (rd_dcyl & ~BMASK) | /* cyl <07:00> */
(data & BMASK);
break;
case 6: /* UDC_SCNT */
rd_scnt = data & BMASK;
break;
case 7: /* UDC_RTCNT */
rd_rtcnt = data & BMASK;
break;
case 8: /* UDC_MODE */
rd_mode = data & BMASK;
break;
case 9: /* UDC_TERM */
rd_term = data & BMASK;
break;
case 10: /* UDC_DATA */
rd_data = data & BMASK;
break;
}
sim_debug (DBG_REG, &rd_dev, "reg %d write, value = %X\n", rd_rg_p, data);
if (rd_rg_p < 10)
rd_rg_p++; /* Advance to next register */
break;
case 1: /* DKC_CMD */
rd_cmd (data);
break;
}
SET_IRQL;
}
void rd_cmd (int32 data)
{
int32 max_cyl;
UNIT *uptr = &rd_unit[CUR_DRV];
/* put command in unit */
uptr->CMD = rd_decode_cmd (data);
switch (uptr->CMD) {
case CMD_RESET:
rd_rg_p = 0;
rd_term = 0;
rd_dsect = 0;
rd_dhead = 0;
rd_dcyl = 0;
uptr->CYL = 0;
sim_debug (DBG_CMD, &rd_dev, "RESET\n");
rd_done (TRM_OK, FALSE);
break;
case CMD_SETREG:
rd_rg_p = data & 0xF;
sim_debug (DBG_CMD, &rd_dev, "SETREG, reg = %d\n", rd_rg_p);
break;
case CMD_DESELECT:
sim_debug (DBG_CMD, &rd_dev, "DESELECT\n");
rd_done (TRM_OK, TRUE);
break;
case CMD_DRVSEL:
rd_cstat = (rd_cstat & ~CST_SDRV) | (data & CST_SDRV);
uptr = &rd_unit[CUR_DRV]; /* get new unit */
if ((uptr->flags & (UNIT_DIS + UNIT_ATT)) == UNIT_ATT) {
rd_done (TRM_OK, TRUE); /* drive installed */
uptr->HEAD = rd_dhead;
uptr->CYL = 0;
rd_set_dstat (uptr);
sim_debug (DBG_CMD, &rd_dev, "DRVSEL, drive = %d\n", CUR_DRV);
}
else {
rd_done (TRM_ERR_TRAN, TRUE); /* drive not installed */
rd_set_dstat (uptr);
sim_debug (DBG_CMD, &rd_dev, "DRVSEL, drive = %d (not present)\n", CUR_DRV);
}
break;
case CMD_STEP:
sim_debug (DBG_CMD, &rd_dev, "STEP\n");
if (data & 0x2) /* direction */
uptr->CYL--; /* in */
else
uptr->CYL++; /* out */
max_cyl = drv_tab[GET_DTYPE (uptr->flags)].cyl;
if (uptr->CYL == max_cyl) /* check for wrap */
uptr->CYL = 0;
if (uptr->CYL == -1)
uptr->CYL = (max_cyl - 1);
rd_set_dstat (uptr);
rd_done (TRM_OK, TRUE);
break;
default:
sim_activate (uptr, rd_cwait);
break;
}
}
int32 rd_decode_cmd (int32 data)
{
if (data == 0) return CMD_RESET; /* 00000000 Reset */
if (data & 0x80) {
if (data & 0x20) return CMD_WRPHY; /* 1x0xxxxx Write Physical */
else return CMD_WRLOG; /* 1x1xxxxx Write Logical */
}
if (data & 0x40) {
if(data & 0x20) return CMD_FORMAT; /* 011xx_x_ Format Track */
else if (!(data & 0x10)) return CMD_SETREG; /* 0100xxxx Set Register pointer */
else if (!(data & 0x8)) return CMD_RDID; /* 01010xxx Seek/Read ID */
else if (data & 0x4) return CMD_RDLOG; /* 010111xx Read Logical */
else if (data & 0x2) return CMD_RDTRK; /* 0101101x Read Track */
else return CMD_RDPHY; /* 0101100x Read Physical */
}
if (data & 0x20) return CMD_DRVSEL; /* 001xxxxx Drive Select */
else if (data & 0x10) return CMD_POLL; /* 0001xxxx Poll Drives */
else if (data & 0x4) return CMD_STEP; /* 000001xx Step */
else if (data & 0x2) return CMD_RESTORE; /* 0000001x Restore Drive */
else if (data & 0x1) return CMD_DESELECT; /* 00000001 Deselect Drive */
return CMD_UNKNOWN;
}
/* read cylinder 0 - simulate special formatting */
t_stat rd_rdcyl0 (int32 hd, int32 dtype)
{
uint32 i;
uint16 c;
if (hd <= 2) {
memset (rd_xb, 0, sizeof(*rd_xb) * 256); /* fill sector buffer with 0's */
/* rd_xb[0]-rd_xb[3] */ /* 8 bytes of zero */
rd_xb[4] = 0x3600;
rd_xb[5] = drv_tab[dtype].xbn & WMASK; /* number of XBNs */
rd_xb[6] = (drv_tab[dtype].xbn >> 16) & WMASK;
rd_xb[7] = drv_tab[dtype].dbn & WMASK; /* number of DBNs */
rd_xb[8] = (drv_tab[dtype].dbn >> 16) & WMASK;
rd_xb[9] = drv_tab[dtype].lbn & WMASK; /* number of LBNs (Logical-Block-Numbers) */
rd_xb[10] = (drv_tab[dtype].lbn >> 16) & WMASK;
rd_xb[11] = drv_tab[dtype].rbn & WMASK; /* number of RBNs (Replacement-Block-Numbers) */
rd_xb[12] = (drv_tab[dtype].rbn >> 16) & WMASK;
rd_xb[13] = drv_tab[dtype].sect; /* number of sectors per track */
rd_xb[14] = drv_tab[dtype].tpg; /* number of tracks */
rd_xb[15] = drv_tab[dtype].cyl; /* number of cylinders */
rd_xb[16] = drv_tab[dtype].cylp; /* first cylinder for write precompensation */
rd_xb[17] = drv_tab[dtype].cylr; /* first cylinder for reduced write current */
rd_xb[18] = 0; /* seek rate or zero for buffered seeks */
rd_xb[19] = 1; /* 0 if CRC, 1 if ECC is being used */
rd_xb[20] = drv_tab[dtype].rcts; /* "replacement control table" (RCT) */
rd_xb[21] = drv_tab[dtype].rctc; /* number of copies of the RCT */
rd_xb[22] = drv_tab[dtype].med & WMASK; /* media identifier */
rd_xb[23] = (drv_tab[dtype].med >> 16) & WMASK;
rd_xb[24] = 1; /* sector-to-sector interleave */
rd_xb[25] = 7; /* head-to-head skew */
rd_xb[26] = drv_tab[dtype].ccs; /* cylinder-to-cylinder skew */
rd_xb[27] = 16; /* size of GAP 0 in the MFM format */
rd_xb[28] = 16; /* size of GAP 1 in the MFM format */
rd_xb[29] = 5; /* size of GAP 2 in the MFM format */
rd_xb[30] = 40; /* size of GAP 3 in the MFM format */
rd_xb[31] = 13; /* sync value used when formatting */
/* rd_xb[32]-rd_xb[47] */ /* 32 bytes of zero - reserved for use by the RQDX formatter */
rd_xb[48] = 0x3039; /* serial number */
/* rd_xb[49]-rd_xb[255] */ /* 414 bytes of zero - Filler bytes to the end of the block */
for (i = c = 0; i < 256; i++)
c = c + rd_xb[i];
rd_xb[255] = c; /* checksum */
}
else
memset (&rd_xb[0], 0, RD_NUMBY);
return SCPE_OK;
}
t_stat rd_rddata (UNIT *uptr, t_lba lba, t_seccnt sects)
{
t_seccnt sectsread;
t_stat r;
r = sim_disk_rdsect (uptr, lba, (uint8 *)rd_xb, &sectsread, sects);
sim_disk_data_trace (uptr, (uint8 *)rd_xb, lba, sectsread*RD_NUMBY, "sim_disk_rdsect", DBG_DAT & rd_dev.dctrl, DBG_REQ);
return r;
}
t_stat rd_wrdata (UNIT *uptr, t_lba lba, t_seccnt sects)
{
t_seccnt sectswritten;
sim_disk_data_trace (uptr, (uint8 *)rd_xb, lba, sects*RD_NUMBY, "sim_disk_wrsect", DBG_DAT & rd_dev.dctrl, DBG_REQ);
return sim_disk_wrsect (uptr, lba, (uint8 *)rd_xb, &sectswritten, sects);
}
/* Unit service */
t_stat rd_svc (UNIT *uptr)
{
t_lba lba;
int32 dtype = GET_DTYPE (uptr->flags);
switch (uptr->CMD) {
case CMD_RDPHY:
case CMD_RDLOG:
uptr->CYL = rd_dcyl;
uptr->HEAD = rd_dhead;
if (dtype >= RD31_DTYPE) {
if (rd_dcyl == 0) {
lba = 0;
sim_debug (DBG_RD, &rd_dev, "cyl=%04d, hd=%d, sect=%02d, lba=%08X\n", rd_dcyl, rd_dhead, rd_dsect, lba);
rd_rdcyl0 (rd_dhead, dtype);
}
else {
lba = GET_DA (uptr, (rd_dcyl - 1), rd_dhead, rd_dsect);
sim_debug (DBG_RD, &rd_dev, "cyl=%04d, hd=%d, sect=%02d, lba=%08X\n", rd_dcyl, rd_dhead, rd_dsect, lba);
rd_rddata (uptr, lba, rd_scnt);
}
}
else {
if (rd_rtcnt & 0x1) {
rd_cstat |= CST_SYNCE;
rd_done (TRM_ERR_RD, TRUE);
return SCPE_OK;
}
lba = GET_DA (uptr, rd_dcyl, rd_dhead, (rd_dsect - 1));
sim_debug (DBG_RD, &rd_dev, "cyl=%04d, hd=%d, sect=%02d, lba=%08X\n", rd_dcyl, rd_dhead, rd_dsect, lba);
rd_rddata (uptr, lba, rd_scnt);
}
ddb_WriteW (rd_dma, (rd_scnt * RD_NUMBY), rd_xb);
rd_dma = (rd_dma + (rd_scnt * RD_NUMBY)) & 0xFFFFFF;
rd_dsect = rd_dsect + rd_scnt - 1;
rd_scnt = 0;
rd_done (TRM_OK, TRUE);
break;
case CMD_WRPHY:
case CMD_WRLOG:
uptr->CYL = rd_dcyl;
uptr->HEAD = rd_dhead;
ddb_ReadW (rd_dma, (rd_scnt * RD_NUMBY), rd_xb);
rd_dma = (rd_dma + (rd_scnt * RD_NUMBY)) & 0xFFFFFF;
if (dtype >= RD31_DTYPE) {
if (rd_dcyl == 0) {
lba = 0;
sim_debug (DBG_WR, &rd_dev, "cyl=%04d, hd=%d, sect=%02d, lba=%08X (ignored)\n", rd_dcyl, rd_dhead, rd_dsect, lba);
}
else {
lba = GET_DA (uptr, (rd_dcyl - 1), rd_dhead, rd_dsect);
sim_debug (DBG_WR, &rd_dev, "cyl=%04d, hd=%d, sect=%02d, lba=%08X\n", rd_dcyl, rd_dhead, rd_dsect, lba);
rd_wrdata (uptr, lba, rd_scnt);
}
}
else {
if (rd_rtcnt & 0x1) {
rd_cstat |= 0x8;
rd_done (TRM_ERR_RD, TRUE);
return SCPE_OK;
}
lba = GET_DA (uptr, rd_dcyl, rd_dhead, (rd_dsect - 1));
sim_debug (DBG_WR, &rd_dev, "cyl=%04d, hd=%d, sect=%02d, lba=%08X\n", rd_dcyl, rd_dhead, rd_dsect, lba);
rd_wrdata (uptr, lba, rd_scnt);
}
rd_dsect = rd_dsect + rd_scnt - 1;
rd_scnt = 0;
rd_done (TRM_OK, TRUE);
break;
case CMD_RESTORE:
sim_debug (DBG_CMD, &rd_dev, "RESTORE\n");
uptr->CYL = 0;
rd_set_dstat (uptr);
rd_done (TRM_OK, TRUE);
break;
case CMD_RDID:
sim_debug (DBG_CMD, &rd_dev, "RD ID\n");
if (uptr->CMD & 0x4) { /* step to new position? */
uptr->CYL = rd_dcyl;
uptr->HEAD = rd_dhead;
}
rd_done (TRM_OK, TRUE);
break;
case CMD_RDTRK: /* not implemented */
sim_debug (DBG_CMD, &rd_dev, "RD TRK\n");
rd_done (TRM_OK, TRUE);
break;
case CMD_POLL: /* not implemented */
sim_debug (DBG_CMD, &rd_dev, "POLL\n");
rd_done (TRM_OK, TRUE);
break;
case CMD_FORMAT: /* not implemented */
sim_debug (DBG_CMD, &rd_dev, "FORMAT\n");
rd_done (TRM_OK, TRUE);
break;
default:
rd_done (TRM_OK, TRUE);
}
return SCPE_OK;
}
/* Update the drive status register to reflect the current unit state */
void rd_set_dstat (UNIT *uptr)
{
if ((uptr->flags & (UNIT_DIS + UNIT_ATT)) == UNIT_ATT) { /* drive present? */
uptr->STAT = (DST_SCOM | DST_RDY);
if (uptr->flags & UNIT_WPRT) /* write protected? */
uptr->STAT |= DST_WPT;
if (CUR_DRV != 2) /* not fdd? */
uptr->STAT |= DST_SELA;
if (uptr->CYL == 0) /* at cyl 0? */
uptr->STAT |= DST_TRK0;
}
else /* drive not present */
uptr->STAT = 0;
}
/* Command complete. Set done and put final value in interface register,
request interrupt if needed, return to IDLE state.
*/
void rd_done (int32 term_code, t_bool setint)
{
rd_stat = ((term_code & STAT_M_TRMC) << STAT_V_TRMC) | STAT_DONE;
if ((rd_term & 0x20) && setint) {
SET_INT (SCA);
rd_stat = rd_stat | STAT_INT;
}
}
/* Device initialization. */
t_stat rd_reset (DEVICE *dptr)
{
rd_rg_p = 0;
CLR_INT (SCA); /* clear int req */
rd_done (TRM_OK, FALSE);
sim_cancel (&rd_unit[0]); /* cancel drive 0 */
sim_cancel (&rd_unit[1]); /* cancel drive 1 */
sim_cancel (&rd_unit[2]); /* cancel drive 2 */
if (rd_xb == NULL)
rd_xb = (uint16 *) calloc (RD_MAXFR, sizeof (uint8));
if (rd_xb == NULL)
return SCPE_MEM;
return SCPE_OK;
}
/* Attach routine */
t_stat rd_attach (UNIT *uptr, CONST char *cptr)
{
return sim_disk_attach (uptr, cptr, RD_NUMBY,
sizeof (uint8), TRUE, DBG_DSK,
drv_tab[GET_DTYPE (uptr->flags)].name, 0, 0);
}
/* Detach routine */
t_stat rd_detach (UNIT *uptr)
{
sim_cancel (uptr);
return sim_disk_detach (uptr);
}
/* Set unit type */
t_stat rd_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
if (uptr->flags & UNIT_ATT)
return SCPE_ALATT;
if ((uptr == &rd_unit[0]) || (uptr == &rd_unit[1])) { /* hard disk only */
if (val < RD31_DTYPE)
return SCPE_ARG;
uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (val << UNIT_V_DTYPE);
}
if (uptr == &rd_unit[2]) { /* floppy disk only */
if (val > RX33_DTYPE)
return SCPE_ARG;
uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (val << UNIT_V_DTYPE);
}
uptr->capac = ((t_addr) drv_tab[val].lbn) * RD_NUMBY;
return SCPE_OK;
}
/* Show unit type */
t_stat rd_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
fprintf (st, "%s", drv_tab[GET_DTYPE (uptr->flags)].name);
return SCPE_OK;
}
t_stat rd_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "HDC9224 Disk Controller (RD)\n\n");
fprintf (st, "The RD controller simulates the HDC9224 Universal Disk Controller\n");
fprintf (st, "chip with up to two hard drives and one floppy drive.\n");
if (dptr->flags & DEV_DISABLE)
fprintf (st, "Initially the RD controller is disabled.\n");
else
fprintf (st, "The RD controller cannot be disabled.\n");
fprintf (st, "Each unit can be set to one of several drive types:\n");
fprint_set_help (st, dptr);
fprintf (st, "\nUnit RD0 and RD1 only support hard disk types (RDxx) and unit RD2\n");
fprintf (st, "only supports a floppy disk type (RX33)\n");
fprintf (st, "Configured options can be displayed with:\n\n");
fprint_show_help (st, dptr);
fprint_reg_help (st, dptr);
fprintf (st, "\nDisk drives on the RD device can be attached to simulated storage in the\n");
fprintf (st, "following ways:\n\n");
sim_disk_attach_help (st, dptr, uptr, flag, cptr);
return SCPE_OK;
}
const char *rd_description (DEVICE *dptr)
{
return "HDC9224 disk controller";
}