simh-testsetgenerator/PDP10/kx10_rc.c
Richard Cornwell 25f053e45d KA10: Added switch for DF10 vs DF10C.
Fix bug in interrupt handling in KI when EPT not at 0.
      Fix bug with handling of CCW_COMP flag on DF10 devices.
2022-06-21 09:43:17 -04:00

573 lines
20 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* kx10_rc.c: RC10 Disk Controller.
Copyright (c) 2013-2020, Richard Cornwell
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
RICHARD CORNWELL 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.
*/
#include "kx10_defs.h"
#ifndef NUM_DEVS_RC
#define NUM_DEVS_RC 0
#endif
#if (NUM_DEVS_RC > 0)
#define RC_DEVNUM 0170 /* 0174 */
#define NUM_UNITS_RC 4
/* Flags in the unit flags word */
#define UNIT_V_DTYPE (UNIT_V_UF + 0) /* disk type */
#define UNIT_M_DTYPE 1
#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE)
#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE)
/* Parameters in the unit descriptor */
#define CUR_CYL u3 /* current cylinder */
#define DATAPTR u4 /* data pointer */
#define UFLAGS u5 /* Function */
#define DISK_SEL 0600000000000LL
#define TRACK 0177600000000LL
#define SEGMENT 0000177000000LL
#define INIT_PAR 0000000770000LL /* Read */
#define DPE_STOP 0000000004000LL
#define CPE_STOP 0000000002000LL
#define WRITE 0000000001000LL
#define EPAR 0000000000001LL
#define SEC_SEL 0000000001400LL /* Read */
#define SECT_CNT 0000000000377LL /* Read */
#define PI 0000007
#define WCW 0000040
#define SEC_SCTR 0600000
#define RST_MSK 0000000177710LL /* CONO reset bits */
#define B22_FLAG 0040000000000LL /* 22 bit controller. */
#define MAINT_SEG 0010000000000LL
#define PRTLT 0004000000000LL /* Protected area less then bounds */
#define STS 0003777000000LL
#define SCRCHCMP 0000000400000LL /* Tranfer in progress. */
#define S_ERROR 0000000200000LL /* Segment not found */
#define DSK_DES_E 0000000100000LL /* Duplicate disk */
#define TRK_SEL_E 0000000040000LL /* Track not BCD number */
#define NOT_RDY 0000000020000LL /* Drive not ready */
#define PSW_FAIL 0000000010000LL /* Power supply fail */
#define DSK_PAR_E 0000000004000LL /* Disk Parity Error */
#define CH_PAR_D 0000000002000LL /* Channel Data Parity Error */
#define CH_PAR_C 0000000001000LL /* Channel Control Parity Error */
#define NXM_ERR 0000000000400LL /* Non existant memory */
#define ILL_WR 0000000000200LL /* Write to protected area */
#define OVRRUN 0000000000100LL /* Over run */
#define CCW_COMP 0000000000040LL /* Control word written */
#define RD10_DTYPE 0
#define RD10_WDS 32
#define RD10_SEGS 80
#define RD10_CYL 200
#define RD10_SIZE (RD10_SEGS * RD10_CYL * RD10_WDS)
#define RM10_DTYPE 1
#define RM10_WDS 64
#define RM10_SEGS 60
#define RM10_CYL 90
#define RM10_SIZE (RM10_SEGS * RM10_CYL * RM10_WDS)
struct drvtyp {
int32 wd_seg; /* Number of words per segment */
int32 seg; /* segments */
int32 cyl; /* cylinders */
int32 size; /* #blocks */
int32 devtype; /* device type */
};
struct drvtyp rc_drv_tab[] = {
{ RD10_WDS, RD10_SEGS, RD10_CYL, RD10_SIZE, RD10_DTYPE},
{ RM10_WDS, RM10_SEGS, RM10_CYL, RM10_SIZE, RM10_DTYPE},
{ 0 }
};
struct df10 rc_df10[NUM_DEVS_RC];
uint64 rc_buf[NUM_DEVS_RC][RM10_WDS];
uint32 rc_ipr[NUM_DEVS_RC];
t_stat rc_devio(uint32 dev, uint64 *data);
t_stat rc_svc(UNIT *);
t_stat rc_boot(int32, DEVICE *);
void rc_ini(UNIT *, t_bool);
t_stat rc_reset(DEVICE *);
t_stat rc_attach(UNIT *, CONST char *);
t_stat rc_detach(UNIT *);
t_stat rc_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat rc_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *rc_description (DEVICE *dptr);
UNIT rc_unit[] = {
/* Controller 1 */
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
#if (NUM_DEVS_RC > 1)
/* Controller 2 */
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
#endif
};
DIB rc_dib[] = {
{RC_DEVNUM+000, 1, &rc_devio, NULL},
{RC_DEVNUM+004, 1, &rc_devio, NULL}
};
MTAB rc_mod[] = {
{ MTAB_XTD|MTAB_VUN, 0, "write enabled", "WRITEENABLED",
&set_writelock, &show_writelock, NULL, "Write enable drive" },
{ MTAB_XTD|MTAB_VUN, 1, NULL, "LOCKED",
&set_writelock, NULL, NULL, "Write lock drive" },
{UNIT_DTYPE, (RD10_DTYPE << UNIT_V_DTYPE), "RD10", "RD10", &rc_set_type },
{UNIT_DTYPE, (RM10_DTYPE << UNIT_V_DTYPE), "RM10", "RM10", &rc_set_type },
{0}
};
REG rca_reg[] = {
{BRDATA(BUFF, rc_buf[0], 16, 64, RM10_WDS), REG_HRO},
{ORDATA(IPR, rc_ipr[0], 2), REG_HRO},
{ORDATA(STATUS, rc_df10[0].status, 18), REG_RO},
{ORDATA(CIA, rc_df10[0].cia, 18)},
{ORDATA(CCW, rc_df10[0].ccw, 18)},
{ORDATA(WCR, rc_df10[0].wcr, 18)},
{ORDATA(CDA, rc_df10[0].cda, 18)},
{ORDATA(DEVNUM, rc_df10[0].devnum, 9), REG_HRO},
{ORDATA(BUF, rc_df10[0].buf, 36), REG_HRO},
{ORDATA(NXM, rc_df10[0].nxmerr, 8), REG_HRO},
{0}
};
DEVICE rca_dev = {
"FHA", rc_unit, rca_reg, rc_mod,
NUM_UNITS_RC, 8, 18, 1, 8, 36,
NULL, NULL, &rc_reset, &rc_boot, &rc_attach, &rc_detach,
&rc_dib[0], DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &rc_help, NULL, NULL, &rc_description
};
#if (NUM_DEVS_RC > 1)
REG rcb_reg[] = {
{BRDATA(BUFF, &rc_buf[1][0], 16, 64, RM10_WDS), REG_HRO},
{ORDATA(IPR, rc_ipr[1], 2), REG_HRO},
{ORDATA(STATUS, rc_df10[1].status, 18), REG_RO},
{ORDATA(CIA, rc_df10[1].cia, 18)},
{ORDATA(CCW, rc_df10[1].ccw, 18)},
{ORDATA(WCR, rc_df10[1].wcr, 18)},
{ORDATA(CDA, rc_df10[1].cda, 18)},
{ORDATA(DEVNUM, rc_df10[1].devnum, 9), REG_HRO},
{ORDATA(BUF, rc_df10[1].buf, 36), REG_HRO},
{ORDATA(NXM, rc_df10[1].nxmerr, 8), REG_HRO},
{0}
};
DEVICE rcb_dev = {
"FHB", &rc_unit[010], rcb_reg, rc_mod,
NUM_UNITS_RC, 8, 18, 1, 8, 36,
NULL, NULL, &rc_reset, &rc_boot, &rc_attach, &rc_detach,
&rc_dib[1], DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &rc_help, NULL, NULL, &rc_description
};
#endif
DEVICE *rc_devs[] = {
&rca_dev,
#if (NUM_DEVS_RC > 1)
&rcb_dev,
#endif
};
t_stat rc_devio(uint32 dev, uint64 *data) {
int ctlr = (dev - RC_DEVNUM) >> 2;
struct df10 *df10;
UNIT *uptr;
DEVICE *dptr;
int unit;
int tmp;
int drv;
int cyl;
int dtype;
if (ctlr < 0 || ctlr >= NUM_DEVS_RC)
return SCPE_OK;
df10 = &rc_df10[ctlr];
dptr = rc_devs[ctlr];
switch(dev & 3) {
case CONI:
*data = df10->status;
#if KI_22BIT
if (cpu_unit[0].flags & UNIT_DF10C) {
*data |= B22_FLAG;
}
#endif
*data |= PRTLT;
sim_debug(DEBUG_CONI, dptr, "HK %03o CONI %06o PC=%o\n", dev,
(uint32)*data, PC);
break;
case CONO:
if (*data & PI_ENABLE)
df10->status &= ~(PI_ENABLE);
clr_interrupt(dev);
df10->status &= ~07;
df10->status |= *data & 07;
df10->status &= ~(RST_MSK & *data);
if ((*data & BUSY) != 0) {
unit = rc_ipr[ctlr] & 3;
drv = unit + (ctlr * NUM_UNITS_RC);
uptr = &rc_unit[drv];
if ((df10->status & BUSY) != 0) {
sim_cancel(uptr);
df10_finish_op(df10, 0);
} else {
df10->status &= ~BUSY;
df10_setirq(df10);
}
}
rc_ipr[ctlr] &= ~SEC_SCTR;
rc_ipr[ctlr] |= *data & SEC_SCTR;
if ((df10->status & BUSY) != 0 && (*data & CCW_COMP) != 0) {
df10_writecw(df10);
df10->status |= CCW_COMP;
} else
df10->status &= ~CCW_COMP;
sim_debug(DEBUG_CONO, dptr, "HK %03o CONO %06o PC=%o %06o\n", dev,
(uint32)*data, PC, df10->status);
break;
case DATAI:
*data = rc_ipr[ctlr];
unit = (rc_ipr[ctlr] & SEC_SCTR) >> 16;
uptr = &rc_unit[(ctlr * NUM_UNITS_RC) + unit];
*data |= (uptr->UFLAGS >> 3) & 0177;
sim_debug(DEBUG_DATAIO, dptr, "HK %03o DATI %012llo PC=%o F=%o\n",
dev, *data, PC, uptr->UFLAGS);
break;
case DATAO:
sim_debug(DEBUG_DATAIO, dptr, "HK %03o DATO %012llo, PC=%o\n",
dev, *data, PC);
if (df10->status & BUSY) {
return SCPE_OK;
}
df10->status &= ~(PI_ENABLE|S_ERROR);
clr_interrupt(RC_DEVNUM + (ctlr * 4));
rc_ipr[ctlr] &= ~(INIT_PAR|3);
rc_ipr[ctlr] |= *data & INIT_PAR;
unit = (*data >> 34) & 03;
rc_ipr[ctlr] |= unit;
drv = unit + (ctlr * NUM_UNITS_RC);
uptr = &rc_unit[drv];
if ((uptr->flags & UNIT_ATT) == 0) {
df10->status &= ~BUSY;
df10->status |= NOT_RDY;
df10_setirq(df10);
return SCPE_OK;
}
if ((uptr->flags & UNIT_WPRT) && *data & WRITE) {
df10->status &= ~BUSY;
df10->status |= ILL_WR;
df10_setirq(df10);
return SCPE_OK;
}
df10_setup(df10, (uint32)*data);
df10->status &= ~CCW_COMP;
tmp = (uint32)(*data >> 15) & ~07;
cyl = (tmp >> 10) & 0777;
if (((cyl & 017) > 9) || (((cyl >> 4) & 017) > 9)) {
sim_debug(DEBUG_DETAIL, dptr, "HK %d non-bcd cyl %02x\n",
ctlr, cyl);
df10_finish_op(df10, TRK_SEL_E);
return SCPE_OK;
}
cyl = (((cyl >> 4) & 017) * 10) + (cyl & 017) +
((cyl & 0x100) ? 100 : 0);
dtype = GET_DTYPE(uptr->flags);
if (cyl >= rc_drv_tab[dtype].cyl) {
sim_debug(DEBUG_DETAIL, dptr, "HK %d invalid cyl %d %d\n",
ctlr, cyl, rc_drv_tab[dtype].cyl);
df10_finish_op(df10, TRK_SEL_E);
return SCPE_OK;
}
cyl = (tmp >> 3) & 0177;
if ((cyl & 017) > 9) {
sim_debug(DEBUG_DETAIL, dptr, "HK %d non-bcd seg %02x\n",
ctlr, cyl);
df10_finish_op(df10, TRK_SEL_E);
return SCPE_OK;
}
uptr->UFLAGS = tmp | ((*data & WRITE) != 0) | (ctlr << 1);
uptr->DATAPTR = -1; /* Set no data */
if ((*data & WRITE) != 0)
(void)df10_read(df10);
sim_debug(DEBUG_DETAIL, dptr, "HK %d cyl %o\n", ctlr, uptr->UFLAGS);
sim_activate(uptr, 100);
break;
}
return SCPE_OK;
}
t_stat rc_svc (UNIT *uptr)
{
int dtype = GET_DTYPE(uptr->flags);
int ctlr = (uptr->UFLAGS >> 1) & 03;
int seg = (uptr->UFLAGS >> 3) & 0177;
int cyl = (uptr->UFLAGS >> 10) & 0777;
int wr = (uptr->UFLAGS & 1);
int seg_size = rc_drv_tab[dtype].wd_seg;
struct df10 *df10 = &rc_df10[ctlr];
int tmp, wc;
DEVICE *dptr;
t_stat err, r;
dptr = rc_devs[ctlr];
/* Check if we need to seek */
if (uptr->DATAPTR == -1) {
cyl = (((cyl >> 4) & 017) * 10) + (cyl & 017) +
((cyl & 0x100) ? 100 : 0);
if (cyl >= rc_drv_tab[dtype].cyl) {
sim_debug(DEBUG_DETAIL, dptr, "HK %d invalid cyl %d %d %o\n",
ctlr, cyl, rc_drv_tab[dtype].cyl, uptr->UFLAGS);
df10_finish_op(df10, TRK_SEL_E);
return SCPE_OK;
}
/* Convert segment from BCD to binary */
if ((seg & 017) > 10) {
sim_debug(DEBUG_DETAIL, dptr, "HK %d non-bcd seg %02x %d %o\n",
ctlr, seg, rc_drv_tab[dtype].seg, uptr->UFLAGS);
df10_finish_op(df10, S_ERROR);
return SCPE_OK;
}
seg = (((seg >> 4) & 07) * 10) + (seg & 017);
if (seg >= rc_drv_tab[dtype].seg) {
sim_debug(DEBUG_DETAIL, dptr, "HK %d invalid sec %d %d %o\n",
ctlr, seg, rc_drv_tab[dtype].seg, uptr->UFLAGS);
df10_finish_op(df10, S_ERROR);
return SCPE_OK;
}
/* Check if reading */
if (!wr) {
/* Read the block */
int da;
da = ((cyl * rc_drv_tab[dtype].seg) + seg) * seg_size;
err = sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET);
wc = sim_fread (&rc_buf[ctlr][0], sizeof(uint64),
seg_size, uptr->fileref);
sim_debug(DEBUG_DETAIL, dptr, "HK %d Read %d %d %d %x\n",
ctlr, da, cyl, seg, uptr->UFLAGS << 1 );
for (; wc < seg_size; wc++)
rc_buf[ctlr][wc] = 0;
}
uptr->DATAPTR = 0;
df10->status |= SCRCHCMP;
}
if (wr) {
rc_buf[ctlr][uptr->DATAPTR] = df10->buf;
r = df10_read(df10);
} else {
df10->buf = rc_buf[ctlr][uptr->DATAPTR];
r = df10_write(df10);
}
sim_debug(DEBUG_DATA, dptr, "Xfer %d %012llo %06o %06o\n", uptr->DATAPTR, df10->buf,
df10->wcr, df10->cda);
uptr->DATAPTR++;
if (uptr->DATAPTR >= seg_size || r == 0 ) {
/* Check if writing */
df10->status &= ~SCRCHCMP;
seg = (((seg >> 4) & 017) * 10) + (seg & 017);
cyl = (((cyl >> 4) & 017) * 10) + (cyl & 017) +
((cyl & 0x100) ? 100 : 0);
if (wr) {
int da;
while(uptr->DATAPTR < seg_size) {
rc_buf[ctlr][uptr->DATAPTR] = 0;
uptr->DATAPTR++;
}
da = ((cyl * rc_drv_tab[dtype].seg) + seg) * seg_size;
sim_debug(DEBUG_DETAIL, dptr, "HK %d Write %d %d %d %x %d\n",
ctlr, da, cyl, seg, uptr->UFLAGS << 1, uptr->DATAPTR );
err = sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET);
wc = sim_fwrite(&rc_buf[ctlr][0],sizeof(uint64),
seg_size, uptr->fileref);
}
uptr->DATAPTR = -1;
seg++;
if (seg >= rc_drv_tab[dtype].seg) {
seg = 0;
cyl++;
if (cyl >= rc_drv_tab[dtype].cyl)
cyl = 0;
}
/* Convert seg back to bcd */
tmp = seg % 10;
seg /= 10;
seg <<= 4;
seg += tmp;
wr = 0;
if (cyl >= 100) {
wr = 0x100;
cyl -= 100;
}
tmp = (cyl % 10);
cyl /= 10;
cyl <<= 4;
cyl += wr + tmp;
uptr->UFLAGS = (uptr->UFLAGS & 7) + (seg << 3) + (cyl << 10);
}
if ((df10->status & PI_ENABLE) == 0) {
sim_activate(uptr, 20);
}
return SCPE_OK;
}
t_stat
rc_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int i;
if (uptr == NULL) return SCPE_IERR;
for (i = 0; rc_drv_tab[i].wd_seg != 0; i++) {
if ((val >> UNIT_V_DTYPE) == rc_drv_tab[i].devtype) {
uptr->capac = rc_drv_tab[i].size;
return SCPE_OK;
}
}
return SCPE_IERR;
}
t_stat
rc_reset(DEVICE * dptr)
{
int unit;
int ctlr;
UNIT *uptr = dptr->units;
for(unit = 0; unit < NUM_UNITS_RC; unit++) {
uptr->UFLAGS = 0;
uptr->CUR_CYL = 0;
uptr++;
}
for (ctlr = 0; ctlr < NUM_DEVS_RC; ctlr++) {
rc_ipr[ctlr] = 0;
df10_init(&rc_df10[ctlr], rc_dib[ctlr].dev_num, 8);
}
return SCPE_OK;
}
/* Boot from given device */
t_stat
rc_boot(int32 unit_num, DEVICE * dptr)
{
UNIT *uptr = &dptr->units[unit_num];
int dtype = GET_DTYPE(uptr->flags);
uint32 addr;
int wc;
int wps;
int seg;
int sect;
uint32 ptr;
addr = (MEMSIZE - 512) & RMASK;
wps = rc_drv_tab[dtype].wd_seg;
for (sect = 4; sect <= 7; sect++) {
seg = (sect * 128) / wps;
(void)sim_fseek(uptr->fileref, (seg * wps) * sizeof(uint64), SEEK_SET);
(void)sim_fread (&rc_buf[0][0], sizeof(uint64), wps, uptr->fileref);
ptr = 0;
for(wc = wps; wc > 0; wc--) {
M[addr++] = rc_buf[0][ptr++];
}
}
PC = (MEMSIZE - 512) & RMASK;
return SCPE_OK;
}
/* Device attach */
t_stat rc_attach (UNIT *uptr, CONST char *cptr)
{
t_stat r;
uptr->capac = rc_drv_tab[GET_DTYPE (uptr->flags)].size;
r = attach_unit (uptr, cptr);
if (r != SCPE_OK || (sim_switches & SIM_SW_REST) != 0)
return r;
uptr->CUR_CYL = 0;
uptr->UFLAGS = 0;
return SCPE_OK;
}
/* Device detach */
t_stat rc_detach (UNIT *uptr)
{
if (!(uptr->flags & UNIT_ATT)) /* attached? */
return SCPE_OK;
if (sim_is_active (uptr)) /* unit active? */
sim_cancel (uptr); /* cancel operation */
return detach_unit (uptr);
}
t_stat rc_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "RD10/RM10 Disk Pack Drives (RC)\n\n");
fprintf (st, "The RC controller implements the RC-10 disk controller that talked\n");
fprintf (st, "to either RD10 mountable pack or RM10 drum drives.\n");
fprintf (st, "Options include the ability to set units write enabled or write locked, to\n");
fprintf (st, "set the drive type to one of two disk types\n\n");
fprint_set_help (st, dptr);
fprint_show_help (st, dptr);
fprintf (st, "\nThe type options can be used only when a unit is not attached to a file.\n");
fprintf (st, "The RC device supports the BOOT command.\n");
fprint_reg_help (st, dptr);
return SCPE_OK;
}
const char *rc_description (DEVICE *dptr)
{
return "RD10/RM10 disk controller";
}
#endif