mirror of
https://github.com/86Box/86Box.git
synced 2026-02-26 05:53:15 -07:00
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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user