Fixed the AT ESDI hard disk controller.

This commit is contained in:
OBattler
2019-11-01 01:31:18 +01:00
parent 5f5da6fe11
commit db88c9cf1d

View File

@@ -8,15 +8,15 @@
*
* Driver for the ESDI controller (WD1007-vse1) for PC/AT.
*
* Version: @(#)hdc_esdi_at.c 1.0.14 2018/10/17
* Version: @(#)hdc_esdi_at.c 1.0.15 2018/10/31
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2008-2018 Sarah Walker.
* Copyright 2016-2018 Miran Grca.
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2008-2019 Sarah Walker.
* Copyright 2016-2019 Miran Grca.
* Copyright 2017-2019 Fred N. van Kempen.
*/
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
@@ -98,8 +98,7 @@ typedef struct {
uint16_t buffer[256];
int irqstat;
uint64_t callback;
pc_timer_t timer;
pc_timer_t callback_timer;
drive_t drives[2];
@@ -127,7 +126,7 @@ esdi_at_log(const char *fmt, ...)
#endif
static inline void
static __inline void
irq_raise(esdi_t *esdi)
{
if (!(esdi->fdisk & 2))
@@ -137,15 +136,18 @@ irq_raise(esdi_t *esdi)
}
static inline void
static __inline void
irq_lower(esdi_t *esdi)
{
if (esdi->irqstat) {
if (!(esdi->fdisk & 2))
picintc(1 << 14);
picintc(1 << 14);
}
esdi->irqstat = 0;
}
static __inline void
irq_update(esdi_t *esdi)
{
if (esdi->irqstat && !((pic2.pend | pic2.ins) & 0x40) && !(esdi->fdisk & 2))
picint(1 << 14);
}
@@ -209,21 +211,6 @@ next_sector(esdi_t *esdi)
}
}
static void
esdi_set_callback(esdi_t *esdi, uint64_t callback)
{
if (!esdi) {
return;
}
if (callback) {
esdi->callback = callback;
timer_set_delay_u64(&esdi->timer, esdi->callback);
} else {
esdi->callback = 0;
timer_disable(&esdi->timer);
}
}
static void
esdi_writew(uint16_t port, uint16_t val, void *priv)
@@ -237,7 +224,7 @@ esdi_writew(uint16_t port, uint16_t val, void *priv)
esdi->pos = 0;
esdi->status = STAT_BUSY;
/* 390.625 us per sector at 10 Mbit/s = 1280 kB/s. */
esdi_set_callback(esdi, (3125 * TIMER_USEC) / 8);
timer_set_delay_u64(&esdi->callback_timer, (3125 * TIMER_USEC) / 8);
}
}
@@ -277,11 +264,10 @@ esdi_write(uint16_t port, uint8_t val, void *priv)
case 0x1f6: /* drive/Head */
esdi->head = val & 0xF;
esdi->drive_sel = (val & 0x10) ? 1 : 0;
if (esdi->drives[esdi->drive_sel].present) {
esdi->status = STAT_READY|STAT_DSC;
} else {
if (esdi->drives[esdi->drive_sel].present)
esdi->status = STAT_READY | STAT_DSC;
else
esdi->status = 0;
}
return;
case 0x1f7: /* command register */
@@ -295,20 +281,20 @@ esdi_write(uint16_t port, uint8_t val, void *priv)
case CMD_RESTORE:
esdi->command &= ~0x0f; /*mask off step rate*/
esdi->status = STAT_BUSY;
esdi_set_callback(esdi, 200 * HDC_TIME);
timer_set_delay_u64(&esdi->callback_timer, 200 * HDC_TIME);
break;
case CMD_SEEK:
esdi->command &= ~0x0f; /*mask off step rate*/
esdi->status = STAT_BUSY;
esdi_set_callback(esdi, 200 * HDC_TIME);
timer_set_delay_u64(&esdi->callback_timer, 200 * HDC_TIME);
break;
default:
switch (val) {
case CMD_NOP:
esdi->status = STAT_BUSY;
esdi_set_callback(esdi, 200 * HDC_TIME);
timer_set_delay_u64(&esdi->callback_timer, 200 * HDC_TIME);
break;
case CMD_READ:
@@ -321,7 +307,7 @@ esdi_write(uint16_t port, uint8_t val, void *priv)
case 0xa0:
esdi->status = STAT_BUSY;
esdi_set_callback(esdi, 200 * HDC_TIME);
timer_set_delay_u64(&esdi->callback_timer, 200 * HDC_TIME);
break;
case CMD_WRITE:
@@ -339,7 +325,7 @@ esdi_write(uint16_t port, uint8_t val, void *priv)
case CMD_VERIFY+1:
esdi->command &= ~0x01;
esdi->status = STAT_BUSY;
esdi_set_callback(esdi, 200 * HDC_TIME);
timer_set_delay_u64(&esdi->callback_timer, 200 * HDC_TIME);
break;
case CMD_FORMAT:
@@ -349,25 +335,25 @@ esdi_write(uint16_t port, uint8_t val, void *priv)
case CMD_SET_PARAMETERS: /* Initialize Drive Parameters */
esdi->status = STAT_BUSY;
esdi_set_callback(esdi, 30 * HDC_TIME);
timer_set_delay_u64(&esdi->callback_timer, 30 * HDC_TIME);
break;
case CMD_DIAGNOSE: /* Execute Drive Diagnostics */
esdi->status = STAT_BUSY;
esdi_set_callback(esdi, 200 * HDC_TIME);
timer_set_delay_u64(&esdi->callback_timer, 200 * HDC_TIME);
break;
case 0xe0: /*???*/
case CMD_READ_PARAMETERS:
esdi->status = STAT_BUSY;
esdi_set_callback(esdi, 200 * HDC_TIME);
timer_set_delay_u64(&esdi->callback_timer, 200 * HDC_TIME);
break;
default:
esdi_at_log("WD1007: bad command %02X\n", val);
case 0xe8: /*???*/
esdi->status = STAT_BUSY;
esdi_set_callback(esdi, 200 * HDC_TIME);
timer_set_delay_u64(&esdi->callback_timer, 200 * HDC_TIME);
break;
}
}
@@ -375,20 +361,18 @@ esdi_write(uint16_t port, uint8_t val, void *priv)
case 0x3f6: /* Device control */
if ((esdi->fdisk & 0x04) && !(val & 0x04)) {
esdi_set_callback(esdi, 500 * HDC_TIME);
timer_set_delay_u64(&esdi->callback_timer, 500 * HDC_TIME);
esdi->reset = 1;
esdi->status = STAT_BUSY;
}
if (val & 0x04) {
/*Drive held in reset*/
esdi_set_callback(esdi, 0);
/* Drive held in reset. */
timer_disable(&esdi->callback_timer);
esdi->status = STAT_BUSY;
}
esdi->fdisk = val;
/* Lower IRQ on IRQ disable. */
if ((val & 2) && !(esdi->fdisk & 0x02))
picintc(1 << 14);
irq_update(esdi);
break;
}
}
@@ -412,7 +396,7 @@ esdi_readw(uint16_t port, void *priv)
next_sector(esdi);
esdi->status = STAT_BUSY;
/* 390.625 us per sector at 10 Mbit/s = 1280 kB/s. */
esdi_set_callback(esdi, (3125 * TIMER_USEC) / 8);
timer_set_delay_u64(&esdi->callback_timer, (3125 * TIMER_USEC) / 8);
} else {
ui_sb_update_icon(SB_HDD|HDD_BUS_ESDI, 0);
}
@@ -447,15 +431,15 @@ esdi_read(uint16_t port, void *priv)
break;
case 0x1f4: /* cylinder low */
temp = (uint8_t)(esdi->cylinder&0xff);
temp = (uint8_t) (esdi->cylinder&0xff);
break;
case 0x1f5: /* cylinder high */
temp = (uint8_t)(esdi->cylinder>>8);
temp = (uint8_t) (esdi->cylinder>>8);
break;
case 0x1f6: /* drive/Head */
temp = (uint8_t)(0xa0|esdi->head|(esdi->drive_sel?0x10:0));
temp = (uint8_t) (esdi->head | (esdi->drive_sel ? 0x10 : 0) | 0xa0);
break;
case 0x1f7: /* status */
@@ -477,8 +461,6 @@ esdi_callback(void *priv)
drive_t *drive = &esdi->drives[esdi->drive_sel];
off64_t addr;
esdi_set_callback(esdi, 0);
if (esdi->reset) {
esdi->status = STAT_READY|STAT_DSC;
esdi->error = 1;
@@ -489,11 +471,10 @@ esdi_callback(void *priv)
esdi->reset = 0;
ui_sb_update_icon(SB_HDD|HDD_BUS_ESDI, 0);
return;
}
esdi_at_log("WD1007: command %02x\n", esdi->command);
esdi_at_log("WD1007: command %02x on drive %i\n", esdi->command, esdi->drive_sel);
switch (esdi->command) {
case CMD_RESTORE:
@@ -511,9 +492,8 @@ esdi_callback(void *priv)
if (! drive->present) {
esdi->status = STAT_READY|STAT_ERR|STAT_DSC;
esdi->error = ERR_ABRT;
} else {
} else
esdi->status = STAT_READY|STAT_DSC;
}
irq_raise(esdi);
break;
@@ -522,23 +502,26 @@ esdi_callback(void *priv)
esdi->status = STAT_READY|STAT_ERR|STAT_DSC;
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
}
} else {
if (get_sector(esdi, &addr)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY|STAT_DSC|STAT_ERR;
irq_raise(esdi);
break;
}
if (get_sector(esdi, &addr)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY|STAT_DSC|STAT_ERR;
if (hdd_image_read_ex(drive->hdd_num, addr, 1, (uint8_t *)esdi->buffer)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
irq_raise(esdi);
break;
}
esdi->pos = 0;
esdi->status = STAT_DRQ|STAT_READY|STAT_DSC;
irq_raise(esdi);
break;
ui_sb_update_icon(SB_HDD|HDD_BUS_ESDI, 1);
}
hdd_image_read(drive->hdd_num, addr, 1,
(uint8_t *)esdi->buffer);
esdi->pos = 0;
esdi->status = STAT_DRQ|STAT_READY|STAT_DSC;
irq_raise(esdi);
ui_sb_update_icon(SB_HDD|HDD_BUS_ESDI, 1);
break;
case CMD_WRITE:
@@ -547,28 +530,31 @@ esdi_callback(void *priv)
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
}
if (get_sector(esdi, &addr)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY|STAT_DSC|STAT_ERR;
irq_raise(esdi);
break;
}
hdd_image_write(drive->hdd_num, addr, 1,
(uint8_t *)esdi->buffer);
irq_raise(esdi);
esdi->secount = (esdi->secount - 1) & 0xff;
if (esdi->secount) {
esdi->status = STAT_DRQ|STAT_READY|STAT_DSC;
esdi->pos = 0;
next_sector(esdi);
} else {
esdi->status = STAT_READY|STAT_DSC;
if (get_sector(esdi, &addr)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY|STAT_DSC|STAT_ERR;
irq_raise(esdi);
break;
}
if (hdd_image_write_ex(drive->hdd_num, addr, 1, (uint8_t *)esdi->buffer)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
irq_raise(esdi);
break;
}
irq_raise(esdi);
esdi->secount = (esdi->secount - 1) & 0xff;
if (esdi->secount) {
esdi->status = STAT_DRQ|STAT_READY|STAT_DSC;
esdi->pos = 0;
next_sector(esdi);
} else
esdi->status = STAT_READY|STAT_DSC;
ui_sb_update_icon(SB_HDD|HDD_BUS_ESDI, 1);
}
ui_sb_update_icon(SB_HDD|HDD_BUS_ESDI, 1);
break;
case CMD_VERIFY:
@@ -577,27 +563,31 @@ esdi_callback(void *priv)
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
}
} else {
if (get_sector(esdi, &addr)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY|STAT_DSC|STAT_ERR;
irq_raise(esdi);
break;
}
if (get_sector(esdi, &addr)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY|STAT_DSC|STAT_ERR;
irq_raise(esdi);
break;
}
if (hdd_image_read_ex(drive->hdd_num, addr, 1, (uint8_t *)esdi->buffer)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
irq_raise(esdi);
break;
}
hdd_image_read(drive->hdd_num, addr, 1,
(uint8_t *)esdi->buffer);
ui_sb_update_icon(SB_HDD|HDD_BUS_ESDI, 1);
next_sector(esdi);
esdi->secount = (esdi->secount - 1) & 0xff;
if (esdi->secount)
esdi_set_callback(esdi, 6 * HDC_TIME);
else {
esdi->pos = 0;
esdi->status = STAT_READY|STAT_DSC;
irq_raise(esdi);
ui_sb_update_icon(SB_HDD|HDD_BUS_ESDI, 1);
next_sector(esdi);
esdi->secount = (esdi->secount - 1) & 0xff;
if (esdi->secount)
timer_set_delay_u64(&esdi->callback_timer, 6 * HDC_TIME);
else {
esdi->pos = 0;
esdi->status = STAT_READY|STAT_DSC;
irq_raise(esdi);
}
}
break;
@@ -607,45 +597,54 @@ esdi_callback(void *priv)
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
}
} else {
if (get_sector(esdi, &addr)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY|STAT_DSC|STAT_ERR;
irq_raise(esdi);
break;
}
if (get_sector(esdi, &addr)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY|STAT_DSC|STAT_ERR;
if (hdd_image_zero_ex(drive->hdd_num, addr, esdi->secount)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
irq_raise(esdi);
break;
}
esdi->status = STAT_READY|STAT_DSC;
irq_raise(esdi);
break;
ui_sb_update_icon(SB_HDD|HDD_BUS_ESDI, 1);
}
hdd_image_zero(drive->hdd_num, addr, esdi->secount);
esdi->status = STAT_READY|STAT_DSC;
irq_raise(esdi);
ui_sb_update_icon(SB_HDD|HDD_BUS_ESDI, 1);
break;
case CMD_DIAGNOSE:
/* This is basically controller diagnostics - it resets drive select to 0,
and resets error and status to ready, DSC, and no error detected. */
esdi->drive_sel = 0;
drive = &esdi->drives[esdi->drive_sel];
esdi->error = 1; /*no error detected*/
esdi->status = STAT_READY|STAT_DSC;
irq_raise(esdi);
break;
case CMD_SET_PARAMETERS: /* Initialize Drive Parameters */
if (drive->present == 0) {
if (! drive->present) {
esdi->status = STAT_READY|STAT_ERR|STAT_DSC;
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
} else {
drive->cfg_spt = esdi->secount;
drive->cfg_hpc = esdi->head+1;
esdi_at_log("WD1007: parameters: spt=%i hpc=%i\n", drive->cfg_spt,drive->cfg_hpc);
if (! esdi->secount)
fatal("WD1007: secount=0\n");
esdi->status = STAT_READY|STAT_DSC;
irq_raise(esdi);
}
drive->cfg_spt = esdi->secount;
drive->cfg_hpc = esdi->head+1;
esdi_at_log("WD1007: parameters: spt=%i hpc=%i\n", drive->cfg_spt,drive->cfg_hpc);
if (! esdi->secount)
fatal("WD1007: secount=0\n");
esdi->status = STAT_READY|STAT_DSC;
irq_raise(esdi);
break;
case CMD_NOP:
@@ -660,31 +659,30 @@ esdi_callback(void *priv)
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
} else {
switch (esdi->cylinder >> 8) {
case 0x31:
esdi->cylinder = drive->real_tracks;
break;
case 0x33:
esdi->cylinder = drive->real_hpc;
break;
case 0x35:
esdi->cylinder = 0x200;
break;
case 0x36:
esdi->cylinder = drive->real_spt;
break;
default:
esdi_at_log("WD1007: bad read config %02x\n", esdi->cylinder >> 8);
}
esdi->status = STAT_READY|STAT_DSC;
irq_raise(esdi);
}
switch (esdi->cylinder >> 8) {
case 0x31:
esdi->cylinder = drive->real_tracks;
break;
case 0x33:
esdi->cylinder = drive->real_hpc;
break;
case 0x35:
esdi->cylinder = 0x200;
break;
case 0x36:
esdi->cylinder = drive->real_spt;
break;
default:
esdi_at_log("WD1007: bad read config %02x\n",
esdi->cylinder >> 8);
}
esdi->status = STAT_READY|STAT_DSC;
irq_raise(esdi);
break;
case 0xa0:
@@ -705,35 +703,34 @@ esdi_callback(void *priv)
esdi->status = STAT_READY|STAT_ERR|STAT_DSC;
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
} else {
memset(esdi->buffer, 0x00, 512);
esdi->buffer[0] = 0x44; /* general configuration */
esdi->buffer[1] = drive->real_tracks; /* number of non-removable cylinders */
esdi->buffer[2] = 0; /* number of removable cylinders */
esdi->buffer[3] = drive->real_hpc; /* number of heads */
esdi->buffer[4] = 600; /* number of unformatted bytes/sector */
esdi->buffer[5] = esdi->buffer[4] * drive->real_spt; /* number of unformatted bytes/track */
esdi->buffer[6] = drive->real_spt; /* number of sectors */
esdi->buffer[7] = 0; /*minimum bytes in inter-sector gap*/
esdi->buffer[8] = 0; /* minimum bytes in postamble */
esdi->buffer[9] = 0; /* number of words of vendor status */
/* controller info */
esdi->buffer[20] = 2; /* controller type */
esdi->buffer[21] = 1; /* sector buffer size, in sectors */
esdi->buffer[22] = 0; /* ecc bytes appended */
esdi->buffer[27] = 'W' | ('D' << 8);
esdi->buffer[28] = '1' | ('0' << 8);
esdi->buffer[29] = '0' | ('7' << 8);
esdi->buffer[30] = 'V' | ('-' << 8);
esdi->buffer[31] = 'S' | ('E' << 8);
esdi->buffer[32] = '1';
esdi->buffer[47] = 0; /* sectors per interrupt */
esdi->buffer[48] = 0; /* can use double word read/write? */
esdi->pos = 0;
esdi->status = STAT_DRQ|STAT_READY|STAT_DSC;
irq_raise(esdi);
}
memset(esdi->buffer, 0x00, 512);
esdi->buffer[0] = 0x44; /* general configuration */
esdi->buffer[1] = drive->real_tracks; /* number of non-removable cylinders */
esdi->buffer[2] = 0; /* number of removable cylinders */
esdi->buffer[3] = drive->real_hpc; /* number of heads */
esdi->buffer[4] = 600; /* number of unformatted bytes/track */
esdi->buffer[5] = esdi->buffer[4] * drive->real_spt; /* number of unformatted bytes/sector */
esdi->buffer[6] = drive->real_spt; /* number of sectors */
esdi->buffer[7] = 0; /*minimum bytes in inter-sector gap*/
esdi->buffer[8] = 0; /* minimum bytes in postamble */
esdi->buffer[9] = 0; /* number of words of vendor status */
/* controller info */
esdi->buffer[20] = 2; /* controller type */
esdi->buffer[21] = 1; /* sector buffer size, in sectors */
esdi->buffer[22] = 0; /* ecc bytes appended */
esdi->buffer[27] = 'W' | ('D' << 8);
esdi->buffer[28] = '1' | ('0' << 8);
esdi->buffer[29] = '0' | ('7' << 8);
esdi->buffer[30] = 'V' | ('-' << 8);
esdi->buffer[31] = 'S' | ('E' << 8);
esdi->buffer[32] = '1';
esdi->buffer[47] = 0; /* sectors per interrupt */
esdi->buffer[48] = 0; /* can use double word read/write? */
esdi->pos = 0;
esdi->status = STAT_DRQ|STAT_READY|STAT_DSC;
irq_raise(esdi);
break;
default:
@@ -770,6 +767,18 @@ loadhd(esdi_t *esdi, int hdd_num, int d, const wchar_t *fn)
}
static void
esdi_rom_write(uint32_t addr, uint8_t val, void *p)
{
rom_t *rom = (rom_t *)p;
addr &= rom->mask;
if (addr >= 0x1f00 && addr < 0x2000)
rom->rom[addr] = val;
}
static void *
wd1007vse1_init(const device_t *info)
{
@@ -793,6 +802,10 @@ wd1007vse1_init(const device_t *info)
rom_init(&esdi->bios_rom,
BIOS_FILE, 0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL);
mem_mapping_set_handler(&esdi->bios_rom.mapping,
rom_read, rom_readw, rom_readl,
esdi_rom_write, NULL, NULL);
io_sethandler(0x01f0, 1,
esdi_read, esdi_readw, NULL,
esdi_write, esdi_writew, NULL, esdi);
@@ -802,7 +815,7 @@ wd1007vse1_init(const device_t *info)
io_sethandler(0x03f6, 1, NULL, NULL, NULL,
esdi_write, NULL, NULL, esdi);
timer_add(&esdi->timer, esdi_callback, esdi, 0);
timer_add(&esdi->callback_timer, esdi_callback, esdi, 0);
ui_sb_update_icon(SB_HDD | HDD_BUS_ESDI, 0);