Added support for DTC 5150X HDD controller.

Fixed some bugs in the 'Xebec' MFM controller in the process.
This commit is contained in:
SarahW 2017-03-19 21:50:51 +00:00
commit ed7021a2e1
3 changed files with 324 additions and 86 deletions

View file

@ -20,11 +20,12 @@ static struct
int is_mfm;
} hdd_controllers[] =
{
{"None", "none", &null_hdd_device, 0},
{"AT Fixed Disk Adapter", "mfm_at", &mfm_at_device, 1},
{"Fixed Disk Adapter (Xebec)", "mfm_xebec", &mfm_xebec_device, 1},
{"XTIDE", "xtide", &xtide_device, 0},
{"XTIDE (AT)", "xtide_at", &xtide_at_device, 0},
{"None", "none", &null_hdd_device, 0},
{"AT Fixed Disk Adapter", "mfm_at", &mfm_at_device, 1},
{"DTC 5150X", "dtc5150x", &dtc_5150x_device, 1},
{"Fixed Disk Adapter (Xebec)", "mfm_xebec", &mfm_xebec_device, 1},
{"XTIDE", "xtide", &xtide_device, 0},
{"XTIDE (AT)", "xtide_at", &xtide_at_device, 0},
{"", "", NULL, 0}
};

View file

@ -70,12 +70,8 @@ typedef struct xebec_t
uint8_t completion_byte;
uint8_t error;
// int cylinder[2];
int drive_sel;
// int max_cylinders[2], max_heads[2];
mfm_drive_t drives[2];
int sector, head, cylinder;
@ -98,14 +94,22 @@ typedef struct xebec_t
#define CMD_RECALIBRATE 0x01
#define CMD_READ_STATUS 0x03
#define CMD_VERIFY_SECTORS 0x05
#define CMD_FORMAT_TRACK 0x06
#define CMD_READ_SECTORS 0x08
#define CMD_WRITE_SECTORS 0x0a
#define CMD_SEEK 0x0b
#define CMD_INIT_DRIVE_PARAMS 0x0c
#define CMD_WRITE_SECTOR_BUFFER 0x0f
#define CMD_BUFFER_DIAGNOSTIC 0xe0
#define CMD_CONTROLLER_DIAGNOSTIC 0xe4
#define CMD_DTC_GET_DRIVE_PARAMS 0xfb
#define CMD_DTC_SET_STEP_RATE 0xfc
#define CMD_DTC_SET_GEOMETRY 0xfe
#define CMD_DTC_GET_GEOMETRY 0xff
#define ERR_NOT_READY 0x04
#define ERR_NOT_READY 0x04
#define ERR_SEEK_ERROR 0x15
#define ERR_ILLEGAL_SECTOR_ADDRESS 0x21
static uint8_t xebec_read(uint16_t port, void *p)
{
@ -257,21 +261,25 @@ static int xebec_get_sector(xebec_t *xebec, off64_t *addr)
if (drive->current_cylinder != xebec->cylinder)
{
pclog("mfm_get_sector: wrong cylinder\n");
xebec->error = ERR_ILLEGAL_SECTOR_ADDRESS;
return 1;
}
if (xebec->head > heads)
{
pclog("mfm_get_sector: past end of configured heads\n");
xebec->error = ERR_ILLEGAL_SECTOR_ADDRESS;
return 1;
}
if (xebec->head > drive->hpc)
{
pclog("mfm_get_sector: past end of heads\n");
xebec->error = ERR_ILLEGAL_SECTOR_ADDRESS;
return 1;
}
if (xebec->sector >= 17)
{
pclog("mfm_get_sector: past end of sectors\n");
xebec->error = ERR_ILLEGAL_SECTOR_ADDRESS;
return 1;
}
@ -368,7 +376,12 @@ static void xebec_callback(void *p)
// pclog("xebec Verify %i,%i,%i %i\n", xebec->cylinder, xebec->head, xebec->sector, xebec->sector_count);
if (xebec_get_sector(xebec, &addr))
fatal("xebec_get_sector failed\n");
{
pclog("xebec_get_sector failed\n");
xebec_error(xebec, xebec->error);
xebec_complete(xebec);
return;
}
xebec_next_sector(xebec);
@ -385,11 +398,37 @@ static void xebec_callback(void *p)
}
break;
case CMD_FORMAT_TRACK:
{
off64_t addr;
int c;
xebec->cylinder = xebec->command[3] | ((xebec->command[2] & 0xc0) << 2);
drive->current_cylinder = (xebec->cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : xebec->cylinder;
xebec->head = xebec->command[1] & 0x1f;
// pclog("Format track %i,%i\n", xebec->cylinder, xebec->head);
if (xebec_get_sector(xebec, &addr))
{
pclog("xebec_get_sector failed\n");
xebec_error(xebec, xebec->error);
xebec_complete(xebec);
return;
}
fseeko64(drive->hdfile, addr * 512, SEEK_SET);
for (c = 0; c < 17; c++)
fwrite(xebec->sector_buf, 512, 1, drive->hdfile);
xebec_complete(xebec);
}
break;
case CMD_READ_SECTORS:
switch (xebec->state)
{
case STATE_START_COMMAND:
xebec->cylinder = xebec->command[3] | ((xebec->command[2] & 0xe0) << 3);
xebec->cylinder = xebec->command[3] | ((xebec->command[2] & 0xc0) << 2);
drive->current_cylinder = (xebec->cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : xebec->cylinder;
xebec->head = xebec->command[1] & 0x1f;
xebec->sector = xebec->command[2] & 0x1f;
@ -397,54 +436,91 @@ static void xebec_callback(void *p)
xebec->state = STATE_SEND_DATA;
xebec->data_pos = 0;
xebec->data_len = 512;
{
off64_t addr;
if (xebec_get_sector(xebec, &addr))
{
xebec_error(xebec, xebec->error);
xebec_complete(xebec);
return;
}
// pclog("xebec Read %i,%i,%i\n", xebec->cylinder, xebec->head, xebec->sector);
fseeko64(drive->hdfile, addr * 512, SEEK_SET);
fread(xebec->sector_buf, 512, 1, drive->hdfile);
readflash = 1;
}
if (xebec->irq_dma_mask & DMA_ENA)
xebec->callback = XEBEC_TIME;
else
fatal("READ_SECTORS no DMA\n");
{
xebec->status = STAT_BSY | STAT_IO | STAT_REQ;
memcpy(xebec->data, xebec->sector_buf, 512);
}
break;
case STATE_SEND_DATA:
xebec->status = STAT_BSY;
if (xebec->irq_dma_mask & DMA_ENA)
{
do
for (; xebec->data_pos < 512; xebec->data_pos++)
{
if (!xebec->data_pos)
int val = dma_channel_write(3, xebec->sector_buf[xebec->data_pos]);
if (val == DMA_NODATA)
{
off64_t addr;
if (xebec_get_sector(xebec, &addr))
fatal("xebec_get_sector failed\n");
pclog("xebec Read %i,%i,%i %i %08llx %i\n", xebec->cylinder, xebec->head, xebec->sector, xebec->sector_count, addr, xebec->drive_sel);
fseeko64(drive->hdfile, addr * 512, SEEK_SET);
fread(xebec->sector_buf, 512, 1, drive->hdfile);
pclog("CMD_READ_SECTORS out of data!\n");
xebec->status = STAT_BSY | STAT_CD | STAT_IO | STAT_REQ;
xebec->callback = XEBEC_TIME;
return;
}
for (; xebec->data_pos < 512; xebec->data_pos++)
{
int val = dma_channel_write(3, xebec->sector_buf[xebec->data_pos]);
if (val == DMA_NODATA)
{
pclog("CMD_READ_SECTORS out of data!\n");
xebec->callback = XEBEC_TIME;
return;
}
}
xebec_next_sector(xebec);
xebec->data_pos = 0;
xebec->sector_count = (xebec->sector_count-1) & 0xff;
readflash = 1;
} while (xebec->sector_count);
xebec_complete(xebec);
}
xebec->state = STATE_SENT_DATA;
xebec->callback = XEBEC_TIME;
}
else
fatal("Read sectors no DMA!\n");
fatal("Read sectors no DMA! - shouldn't get here\n");
break;
case STATE_SENT_DATA:
xebec_next_sector(xebec);
xebec->data_pos = 0;
xebec->sector_count = (xebec->sector_count-1) & 0xff;
if (xebec->sector_count)
{
off64_t addr;
if (xebec_get_sector(xebec, &addr))
{
xebec_error(xebec, xebec->error);
xebec_complete(xebec);
return;
}
// pclog("xebec Read %i,%i,%i\n", xebec->cylinder, xebec->head, xebec->sector);
fseeko64(drive->hdfile, addr * 512, SEEK_SET);
fread(xebec->sector_buf, 512, 1, drive->hdfile);
readflash = 1;
xebec->state = STATE_SEND_DATA;
if (xebec->irq_dma_mask & DMA_ENA)
xebec->callback = XEBEC_TIME;
else
{
xebec->status = STAT_BSY | STAT_IO | STAT_REQ;
memcpy(xebec->data, xebec->sector_buf, 512);
}
}
else
xebec_complete(xebec);
break;
default:
fatal("CMD_READ_SECTORS: bad state %i\n", xebec->state);
@ -455,7 +531,7 @@ static void xebec_callback(void *p)
switch (xebec->state)
{
case STATE_START_COMMAND:
xebec->cylinder = xebec->command[3] | ((xebec->command[2] & 0xe0) << 3);
xebec->cylinder = xebec->command[3] | ((xebec->command[2] & 0xc0) << 2);
drive->current_cylinder = (xebec->cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : xebec->cylinder;
xebec->head = xebec->command[1] & 0x1f;
xebec->sector = xebec->command[2] & 0x1f;
@ -466,61 +542,93 @@ static void xebec_callback(void *p)
if (xebec->irq_dma_mask & DMA_ENA)
xebec->callback = XEBEC_TIME;
else
fatal("READ_SECTORS no DMA\n");
xebec->status = STAT_BSY | STAT_REQ;
break;
case STATE_RECEIVE_DATA:
xebec->status = STAT_BSY;
if (xebec->irq_dma_mask & DMA_ENA)
{
do
{
if (!xebec->data_pos)
for (; xebec->data_pos < 512; xebec->data_pos++)
{
int val = dma_channel_read(3);
if (val == DMA_NODATA)
{
off64_t addr;
// pclog("xebec Write %i,%i,%i %i\n", xebec->cylinder, xebec->head, xebec->sector, xebec->sector_count);
if (xebec_get_sector(xebec, &addr))
fatal("xebec_get_sector failed\n");
fseeko64(drive->hdfile, addr * 512, SEEK_SET);
pclog("CMD_WRITE_SECTORS out of data!\n");
xebec->status = STAT_BSY | STAT_CD | STAT_IO | STAT_REQ;
xebec->callback = XEBEC_TIME;
return;
}
for (; xebec->data_pos < 512; xebec->data_pos++)
{
int val = dma_channel_read(3);
if (val == DMA_NODATA)
{
pclog("CMD_WRITE_SECTORS out of data!\n");
xebec->callback = XEBEC_TIME;
return;
}
xebec->sector_buf[xebec->data_pos] = val & 0xff;
}
fwrite(xebec->sector_buf, 512, 1, drive->hdfile);
xebec_next_sector(xebec);
xebec->sector_buf[xebec->data_pos] = val & 0xff;
}
xebec->data_pos = 0;
xebec->sector_count = (xebec->sector_count-1) & 0xff;
readflash = 1;
} while (xebec->sector_count);
xebec_complete(xebec);
xebec->state = STATE_RECEIVED_DATA;
xebec->callback = XEBEC_TIME;
}
else
fatal("Write sectors no DMA!\n");
fatal("Write sectors no DMA! - should never get here\n");
break;
case STATE_RECEIVED_DATA:
if (!(xebec->irq_dma_mask & DMA_ENA))
memcpy(xebec->sector_buf, xebec->data, 512);
{
off64_t addr;
if (xebec_get_sector(xebec, &addr))
{
xebec_error(xebec, xebec->error);
xebec_complete(xebec);
return;
}
// pclog("xebec Write %i,%i,%i %i\n", xebec->cylinder, xebec->head, xebec->sector, xebec->drive_sel);
fseeko64(drive->hdfile, addr * 512, SEEK_SET);
fwrite(xebec->sector_buf, 512, 1, drive->hdfile);
}
readflash = 1;
xebec_next_sector(xebec);
xebec->data_pos = 0;
xebec->sector_count = (xebec->sector_count-1) & 0xff;
if (xebec->sector_count)
{
xebec->state = STATE_RECEIVE_DATA;
if (xebec->irq_dma_mask & DMA_ENA)
xebec->callback = XEBEC_TIME;
else
xebec->status = STAT_BSY | STAT_REQ;
}
else
xebec_complete(xebec);
break;
default:
fatal("CMD_WRITE_SECTORS: bad state %i\n", xebec->state);
}
break;
case CMD_SEEK:
if (!drive->hdfile)
xebec_error(xebec, ERR_NOT_READY);
else
{
int cylinder = xebec->command[3] | ((xebec->command[2] & 0xc0) << 2);
drive->current_cylinder = (cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : cylinder;
if (cylinder != drive->current_cylinder)
xebec_error(xebec, ERR_SEEK_ERROR);
}
xebec_complete(xebec);
break;
case CMD_INIT_DRIVE_PARAMS:
switch (xebec->state)
{
@ -532,7 +640,6 @@ static void xebec_callback(void *p)
break;
case STATE_RECEIVED_DATA:
// pclog("State STATE_RECEIVED_DATA\n");
drive->cfg_cyl = xebec->data[1] | (xebec->data[0] << 8);
drive->cfg_hpc = xebec->data[2];
pclog("Drive %i: cylinders=%i, heads=%i\n", xebec->drive_sel, drive->cfg_cyl, drive->cfg_hpc);
@ -548,24 +655,44 @@ static void xebec_callback(void *p)
switch (xebec->state)
{
case STATE_START_COMMAND:
xebec->state = STATE_RECEIVE_DATA;
xebec->data_pos = 0;
xebec->data_len = 512;
if (xebec->irq_dma_mask & DMA_ENA)
xebec->callback = XEBEC_TIME;
else
xebec->status = STAT_BSY | STAT_REQ;
break;
case STATE_RECEIVE_DATA:
if (xebec->irq_dma_mask & DMA_ENA)
{
int c;
for (c = 0; c < 512; c++)
xebec->status = STAT_BSY;
for (; xebec->data_pos < 512; xebec->data_pos++)
{
int val = dma_channel_read(3);
if (val == DMA_NODATA)
fatal("CMD_WRITE_SECTOR_DATA out of data!\n");
{
pclog("CMD_WRITE_SECTOR_BUFFER out of data!\n");
xebec->status = STAT_BSY | STAT_CD | STAT_IO | STAT_REQ;
xebec->callback = XEBEC_TIME;
return;
}
xebec->sector_buf[c] = val & 0xff;
xebec->data[xebec->data_pos] = val & 0xff;
}
xebec_complete(xebec);
xebec->state = STATE_RECEIVED_DATA;
xebec->callback = XEBEC_TIME;
}
else
fatal("CMD_WRITE_SECTOR_BUFFER - no DMA\n");
fatal("CMD_WRITE_SECTOR_BUFFER - should never get here!\n");
break;
case STATE_RECEIVED_DATA:
memcpy(xebec->sector_buf, xebec->data, 512);
xebec_complete(xebec);
break;
default:
@ -578,6 +705,75 @@ static void xebec_callback(void *p)
xebec_complete(xebec);
break;
case 0xfa:
xebec_complete(xebec);
break;
case CMD_DTC_SET_STEP_RATE:
xebec_complete(xebec);
break;
case CMD_DTC_GET_DRIVE_PARAMS:
switch (xebec->state)
{
case STATE_START_COMMAND:
xebec->state = STATE_SEND_DATA;
xebec->data_pos = 0;
xebec->data_len = 4;
xebec->status = STAT_BSY | STAT_IO | STAT_REQ;
memset(xebec->data, 0, 4);
xebec->data[0] = drive->tracks & 0xff;
xebec->data[1] = 17 | ((drive->tracks >> 2) & 0xc0);
xebec->data[2] = drive->hpc-1;
pclog("Get drive params %02x %02x %02x %i\n", xebec->data[0], xebec->data[1], xebec->data[2], drive->tracks);
break;
case STATE_SENT_DATA:
xebec_complete(xebec);
break;
default:
fatal("CMD_INIT_DRIVE_PARAMS bad state %i\n", xebec->state);
}
break;
case CMD_DTC_GET_GEOMETRY:
switch (xebec->state)
{
case STATE_START_COMMAND:
xebec->state = STATE_SEND_DATA;
xebec->data_pos = 0;
xebec->data_len = 16;
xebec->status = STAT_BSY | STAT_IO | STAT_REQ;
memset(xebec->data, 0, 16);
xebec->data[0x4] = drive->tracks & 0xff;
xebec->data[0x5] = (drive->tracks >> 8) & 0xff;
xebec->data[0xa] = drive->hpc;
break;
case STATE_SENT_DATA:
xebec_complete(xebec);
break;
}
break;
case CMD_DTC_SET_GEOMETRY:
switch (xebec->state)
{
case STATE_START_COMMAND:
xebec->state = STATE_RECEIVE_DATA;
xebec->data_pos = 0;
xebec->data_len = 16;
xebec->status = STAT_BSY | STAT_REQ;
break;
case STATE_RECEIVED_DATA:
/*Bit of a cheat here - we always report the actual geometry of the drive in use*/
xebec_complete(xebec);
break;
}
break;
default:
fatal("Unknown Xebec command - %02x %02x %02x %02x %02x %02x\n",
xebec->command[0], xebec->command[1],
@ -715,3 +911,43 @@ device_t mfm_xebec_device =
NULL,
NULL
};
static void *dtc_5150x_init()
{
xebec_t *xebec = malloc(sizeof(xebec_t));
memset(xebec, 0, sizeof(xebec_t));
loadhd(xebec, 0, ide_fn[0]);
loadhd(xebec, 1, ide_fn[1]);
xebec->switches = 0xff;
xebec->drives[0].cfg_cyl = xebec->drives[0].tracks;
xebec->drives[0].cfg_hpc = xebec->drives[0].hpc;
xebec->drives[1].cfg_cyl = xebec->drives[1].tracks;
xebec->drives[1].cfg_hpc = xebec->drives[1].hpc;
rom_init(&xebec->bios_rom, "roms/dtc_cxd21a.bin", 0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL);
io_sethandler(0x0320, 0x0004, xebec_read, NULL, NULL, xebec_write, NULL, NULL, xebec);
timer_add(xebec_callback, &xebec->callback, &xebec->callback, xebec);
return xebec;
}
static int dtc_5150x_available()
{
return rom_present("roms/dtc_cxd21a.bin");
}
device_t dtc_5150x_device =
{
"DTC 5150X",
0,
dtc_5150x_init,
xebec_close,
dtc_5150x_available,
NULL,
NULL,
NULL,
NULL
};

View file

@ -1 +1,2 @@
extern device_t mfm_xebec_device;
extern device_t dtc_5150x_device;