SM(S)C FDC37C669 Super I/O chip rewrite and proper FDC power down behavior, fixes floppy drive errors on the new Daeweoo machine.

This commit is contained in:
OBattler
2024-01-07 01:42:34 +01:00
parent 2fb04b63ec
commit 4f392ca8e3
5 changed files with 397 additions and 340 deletions

View File

@@ -12,13 +12,15 @@
*
* Authors: Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2016-2018 Miran Grca.
* Copyright 2016-2024 Miran Grca.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/io.h>
#include <86box/timer.h>
@@ -45,35 +47,67 @@ typedef struct fdc37c669_t {
static int next_id = 0;
static uint16_t
make_port(fdc37c669_t *dev, uint8_t reg)
#ifdef ENABLE_FDC37C669_LOG
int fdc37c669_do_log = ENABLE_FDC37C669_LOG;
static void
fdc37c669_log(const char *fmt, ...)
{
uint16_t p = 0;
uint16_t mask = 0;
va_list ap;
switch (reg) {
case 0x20:
case 0x21:
case 0x22:
mask = 0xfc;
break;
case 0x23:
mask = 0xff;
break;
case 0x24:
case 0x25:
mask = 0xfe;
break;
default:
break;
if (fdc37c669_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define fdc37c669_log(fmt, ...)
#endif
p = ((uint16_t) (dev->regs[reg] & mask)) << 2;
if (reg == 0x22)
p |= 6;
static void
fdc37c669_fdc_handler(fdc37c669_t *dev)
{
fdc_remove(dev->fdc);
if (dev->regs[0x20] & 0xc0)
fdc_set_base(dev->fdc, ((uint16_t) dev->regs[0x20]) << 2);
}
return p;
static void
fdc37c669_uart_handler(fdc37c669_t *dev, uint8_t uart)
{
uint8_t uart_reg = 0x24 + uart;
uint8_t pwrdn_mask = 0x08 << (uart << 2);
uint8_t uart_shift = ((uart ^ 1) << 2);
serial_remove(dev->uart[uart]);
if ((dev->regs[0x02] & pwrdn_mask) && (dev->regs[uart_reg] & 0xc0))
serial_setup(dev->uart[0], ((uint16_t) dev->regs[0x24]) << 2,
(dev->regs[0x28] >> uart_shift) & 0x0f);
}
static double
fdc37c669_uart_get_clock_src(fdc37c669_t *dev, uint8_t uart)
{
double clock_srcs[4] = { 24000000.0 / 13.0, 24000000.0 / 12.0, 24000000.0 / 3.0, 24000000.0 / 3.0 };
double ret;
uint8_t clock_src_0 = !!(dev->regs[0x04] & (0x10 << uart));
uint8_t clock_src_1 = !!(dev->regs[0x0c] & (0x40 << uart));
uint8_t clock_src = clock_src_0 | (clock_src_1 << 1);
ret = clock_srcs[clock_src];
return ret;
}
static void
fdc37c669_lpt_handler(fdc37c669_t *dev)
{
uint8_t mask = ~(dev->regs[0x04] & 0x01);
lpt_port_remove(dev->id);
if ((dev->regs[0x01] & 0x04) && (dev->regs[0x23] >= 0x40))
lpt_port_init(dev->id, ((uint16_t) (dev->regs[0x23] & mask)) << 2);
}
static void
@@ -81,149 +115,153 @@ fdc37c669_write(uint16_t port, uint8_t val, void *priv)
{
fdc37c669_t *dev = (fdc37c669_t *) priv;
uint8_t index = (port & 1) ? 0 : 1;
uint8_t valxor = 0;
uint8_t max = 42;
uint8_t valxor = val ^ dev->regs[dev->cur_reg];
fdc37c669_log("[%04X:%08X] [W] %04X = %02X (%i, %i)\n", CS, cpu_state.pc, port, val,
dev->tries, dev->locked);
if (index) {
if ((val == 0x55) && !dev->locked) {
if (dev->tries) {
dev->tries = (dev->tries + 1) & 1;
if (!dev->tries)
dev->locked = 1;
dev->tries = 0;
} else
dev->tries++;
} else {
if (dev->locked) {
if (val < max)
dev->cur_reg = val;
if (val == 0xaa)
dev->locked = 0;
} else {
if (dev->tries)
dev->tries = 0;
}
else
dev->cur_reg = val;
} else
dev->tries = 0;
}
return;
} else {
if (dev->locked) {
if ((dev->cur_reg < 0x18) && (dev->rw_locked))
return;
if ((dev->cur_reg >= 0x26) && (dev->cur_reg <= 0x27))
return;
if (dev->cur_reg == 0x29)
return;
valxor = val ^ dev->regs[dev->cur_reg];
} else if (!dev->rw_locked || (dev->cur_reg > 0x0f)) switch (dev->cur_reg) {
case 0x00:
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0x74) | (val & 0x8b);
if (!dev->id && (valxor & 8))
fdc_set_power_down(dev->fdc, !(val & 0x08));
break;
case 0x01:
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0x73) | (val & 0x8c);
if (valxor & 0x04)
fdc37c669_lpt_handler(dev);
if (valxor & 0x80)
dev->rw_locked = !(val & 0x80);
break;
case 0x02:
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0x77) | (val & 0x88);
if (valxor & 0x08)
fdc37c669_uart_handler(dev, 0);
if (valxor & 0x80)
fdc37c669_uart_handler(dev, 1);
break;
case 0x03:
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0x08) | (val & 0xf7);
if (!dev->id && (valxor & 0x02))
fdc_update_enh_mode(dev->fdc, !!(val & 0x02));
break;
case 0x04:
dev->regs[dev->cur_reg] = val;
} else
return;
}
switch (dev->cur_reg) {
case 0:
if (!dev->id && (valxor & 8)) {
fdc_remove(dev->fdc);
if ((dev->regs[0] & 8) && (dev->regs[0x20] & 0xc0))
fdc_set_base(dev->fdc, make_port(dev, 0x20));
}
if (valxor & 0x03)
fdc37c669_lpt_handler(dev);
if (valxor & 0x10)
serial_set_clock_src(dev->uart[0], fdc37c669_uart_get_clock_src(dev, 0));
if (valxor & 0x20)
serial_set_clock_src(dev->uart[1], fdc37c669_uart_get_clock_src(dev, 1));
break;
case 1:
if (valxor & 4) {
if (dev->id) {
lpt2_remove();
if ((dev->regs[1] & 4) && (dev->regs[0x23] >= 0x40))
lpt2_init(make_port(dev, 0x23));
} else {
lpt1_remove();
if ((dev->regs[1] & 4) && (dev->regs[0x23] >= 0x40))
lpt1_init(make_port(dev, 0x23));
}
}
if (valxor & 7)
dev->rw_locked = (val & 8) ? 0 : 1;
break;
case 2:
if (valxor & 8) {
serial_remove(dev->uart[0]);
if ((dev->regs[2] & 8) && (dev->regs[0x24] >= 0x40))
serial_setup(dev->uart[0], make_port(dev, 0x24), (dev->regs[0x28] & 0xf0) >> 4);
}
if (valxor & 0x80) {
serial_remove(dev->uart[1]);
if ((dev->regs[2] & 0x80) && (dev->regs[0x25] >= 0x40))
serial_setup(dev->uart[1], make_port(dev, 0x25), dev->regs[0x28] & 0x0f);
}
break;
case 3:
if (!dev->id && (valxor & 2))
fdc_update_enh_mode(dev->fdc, (val & 2) ? 1 : 0);
break;
case 5:
case 0x05:
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0x83) | (val & 0x7c);
if (!dev->id && (valxor & 0x18))
fdc_update_densel_force(dev->fdc, (val & 0x18) >> 3);
if (!dev->id && (valxor & 0x20))
fdc_set_swap(dev->fdc, (val & 0x20) >> 5);
break;
case 0xB:
if (!dev->id && (valxor & 3))
fdc_update_rwc(dev->fdc, 0, val & 3);
if (!dev->id && (valxor & 0xC))
fdc_update_rwc(dev->fdc, 1, (val & 0xC) >> 2);
case 0x06:
dev->regs[dev->cur_reg] = val;
break;
case 0x07:
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0x06) | (val & 0xf9);
break;
case 0x08:
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0x0f) | (val & 0xf0);
break;
case 0x09:
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0x38) | (val & 0xc7);
break;
case 0x0a:
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0xf0) | (val & 0x0f);
break;
case 0x0b:
dev->regs[dev->cur_reg] = val;
if (!dev->id && (valxor & 0x03))
fdc_update_rwc(dev->fdc, 0, val & 0x03);
if (!dev->id && (valxor & 0x0c))
fdc_update_rwc(dev->fdc, 1, (val & 0x0c) >> 2);
break;
case 0x0c:
dev->regs[dev->cur_reg] = val;
if (valxor & 0x40)
serial_set_clock_src(dev->uart[0], fdc37c669_uart_get_clock_src(dev, 0));
if (valxor & 0x80)
serial_set_clock_src(dev->uart[1], fdc37c669_uart_get_clock_src(dev, 1));
break;
case 0x0f:
case 0x12 ... 0x1f:
dev->regs[dev->cur_reg] = val;
break;
case 0x10:
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0x07) | (val & 0xf8);
break;
case 0x11:
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0xfc) | (val & 0x03);
break;
case 0x20:
if (!dev->id && (valxor & 0xfc)) {
fdc_remove(dev->fdc);
if ((dev->regs[0] & 8) && (dev->regs[0x20] & 0xc0))
fdc_set_base(dev->fdc, make_port(dev, 0x20));
}
dev->regs[dev->cur_reg] = val & 0xfc;
if (!dev->id && (valxor & 0xfc))
fdc37c669_fdc_handler(dev);
break;
case 0x21:
dev->regs[dev->cur_reg] = val & 0xfc;
break;
case 0x22:
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0x03) | (val & 0xfc);
break;
case 0x23:
if (valxor) {
if (dev->id) {
lpt2_remove();
if ((dev->regs[1] & 4) && (dev->regs[0x23] >= 0x40))
lpt2_init(make_port(dev, 0x23));
} else {
lpt1_remove();
if ((dev->regs[1] & 4) && (dev->regs[0x23] >= 0x40))
lpt1_init(make_port(dev, 0x23));
}
}
dev->regs[dev->cur_reg] = val;
if (valxor)
fdc37c669_lpt_handler(dev);
break;
case 0x24:
if (valxor & 0xfe) {
serial_remove(dev->uart[0]);
if ((dev->regs[2] & 8) && (dev->regs[0x24] >= 0x40))
serial_setup(dev->uart[0], make_port(dev, 0x24), (dev->regs[0x28] & 0xf0) >> 4);
}
dev->regs[dev->cur_reg] = val & 0xfe;
if (valxor & 0xfe)
fdc37c669_uart_handler(dev, 0);
break;
case 0x25:
if (valxor & 0xfe) {
serial_remove(dev->uart[1]);
if ((dev->regs[2] & 0x80) && (dev->regs[0x25] >= 0x40))
serial_setup(dev->uart[1], make_port(dev, 0x25), dev->regs[0x28] & 0x0f);
}
dev->regs[dev->cur_reg] = val & 0xfe;
if (valxor & 0xfe)
fdc37c669_uart_handler(dev, 1);
break;
case 0x26:
dev->regs[dev->cur_reg] = val;
if (valxor & 0xf0)
fdc_set_dma_ch(dev->fdc, val >> 4);
break;
case 0x27:
if (valxor & 0xf) {
if (dev->id)
lpt2_irq(val & 0xf);
else
lpt1_irq(val & 0xf);
}
dev->regs[dev->cur_reg] = val;
if (valxor & 0xf0)
fdc_set_irq(dev->fdc, val >> 4);
if (valxor & 0x0f)
lpt_port_irq(dev->id, val & 0x0f);
break;
case 0x28:
if (valxor & 0xf) {
serial_remove(dev->uart[1]);
if ((dev->regs[2] & 0x80) && (dev->regs[0x25] >= 0x40))
serial_setup(dev->uart[1], make_port(dev, 0x25), dev->regs[0x28] & 0x0f);
}
if (valxor & 0xf0) {
serial_remove(dev->uart[0]);
if ((dev->regs[2] & 8) && (dev->regs[0x24] >= 0x40))
serial_setup(dev->uart[0], make_port(dev, 0x24), (dev->regs[0x28] & 0xf0) >> 4);
}
dev->regs[dev->cur_reg] = val;
if (valxor & 0xf0)
fdc37c669_uart_handler(dev, 0);
if (valxor & 0x0f)
fdc37c669_uart_handler(dev, 1);
break;
default:
case 0x29:
dev->regs[dev->cur_reg] = val & 0x0f;
break;
}
}
@@ -238,23 +276,23 @@ fdc37c669_read(uint16_t port, void *priv)
if (dev->locked) {
if (index)
ret = dev->cur_reg;
else if ((dev->cur_reg >= 0x18) || !dev->rw_locked)
else if (!dev->rw_locked || (dev->cur_reg > 0x0f))
ret = dev->regs[dev->cur_reg];
}
fdc37c669_log("[%04X:%08X] [R] %04X = %02X (%i, %i)\n", CS, cpu_state.pc, port, ret,
dev->tries, dev->locked);
return ret;
}
static void
fdc37c669_reset(fdc37c669_t *dev)
fdc37c669_reset(void *priv)
{
serial_remove(dev->uart[0]);
serial_setup(dev->uart[0], COM1_ADDR, COM1_IRQ);
fdc37c669_t *dev = (fdc37c669_t *) priv;
serial_remove(dev->uart[1]);
serial_setup(dev->uart[1], COM2_ADDR, COM2_IRQ);
memset(dev->regs, 0x00, 42);
memset(dev->regs, 0, 42);
dev->regs[0x00] = 0x28;
dev->regs[0x01] = 0x9c;
dev->regs[0x02] = 0x88;
@@ -262,32 +300,23 @@ fdc37c669_reset(fdc37c669_t *dev)
dev->regs[0x06] = 0xff;
dev->regs[0x0d] = 0x03;
dev->regs[0x0e] = 0x02;
dev->regs[0x1e] = 0x80; /* Gameport controller. */
dev->regs[0x20] = (FDC_PRIMARY_ADDR >> 2) & 0xfc;
dev->regs[0x21] = (0x1f0 >> 2) & 0xfc;
dev->regs[0x22] = ((0x3f6 >> 2) & 0xfc) | 1;
dev->regs[0x1e] = 0x3c; /* Gameport controller. */
dev->regs[0x20] = 0x3c;
dev->regs[0x21] = 0x3c;
dev->regs[0x22] = 0x3d;
if (dev->id == 1) {
dev->regs[0x23] = (LPT2_ADDR >> 2);
lpt2_remove();
lpt2_init(LPT2_ADDR);
dev->regs[0x24] = (COM3_ADDR >> 2) & 0xfe;
dev->regs[0x25] = (COM4_ADDR >> 2) & 0xfe;
} else {
fdc_reset(dev->fdc);
lpt1_remove();
lpt1_init(LPT1_ADDR);
dev->regs[0x23] = (LPT1_ADDR >> 2);
dev->regs[0x24] = (COM1_ADDR >> 2) & 0xfe;
dev->regs[0x25] = (COM2_ADDR >> 2) & 0xfe;
fdc37c669_fdc_handler(dev);
}
dev->regs[0x26] = (2 << 4) | 3;
dev->regs[0x27] = (6 << 4) | (dev->id ? 5 : 7);
dev->regs[0x28] = (4 << 4) | 3;
fdc37c669_uart_handler(dev, 0);
serial_set_clock_src(dev->uart[0], fdc37c669_uart_get_clock_src(dev, 0));
fdc37c669_uart_handler(dev, 1);
serial_set_clock_src(dev->uart[1], fdc37c669_uart_get_clock_src(dev, 1));
fdc37c669_lpt_handler(dev);
dev->locked = 0;
dev->rw_locked = 0;
@@ -317,8 +346,8 @@ fdc37c669_init(const device_t *info)
dev->uart[0] = device_add_inst(&ns16550_device, (next_id << 1) + 1);
dev->uart[1] = device_add_inst(&ns16550_device, (next_id << 1) + 2);
io_sethandler(info->local ? FDC_SECONDARY_ADDR : (next_id ? FDC_SECONDARY_ADDR : FDC_PRIMARY_ADDR), 0x0002,
fdc37c669_read, NULL, NULL, fdc37c669_write, NULL, NULL, dev);
io_sethandler(info->local ? FDC_SECONDARY_ADDR : (next_id ? FDC_SECONDARY_ADDR : FDC_PRIMARY_ADDR),
0x0002, fdc37c669_read, NULL, NULL, fdc37c669_write, NULL, NULL, dev);
fdc37c669_reset(dev);
@@ -334,7 +363,7 @@ const device_t fdc37c669_device = {
.local = 0,
.init = fdc37c669_init,
.close = fdc37c669_close,
.reset = NULL,
.reset = fdc37c669_reset,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
@@ -348,7 +377,7 @@ const device_t fdc37c669_370_device = {
.local = 1,
.init = fdc37c669_init,
.close = fdc37c669_close,
.reset = NULL,
.reset = fdc37c669_reset,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,