diff --git a/src/disk/hdc_esdi_at.c b/src/disk/hdc_esdi_at.c
index e3bc9aa62..6e3946a80 100644
--- a/src/disk/hdc_esdi_at.c
+++ b/src/disk/hdc_esdi_at.c
@@ -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,
* Miran Grca,
* Fred N. van Kempen,
*
- * 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);