2017-05-05 01:49:42 +02:00
|
|
|
/*
|
2023-01-06 15:36:05 -05:00
|
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
|
|
|
* running old operating systems and software designed for IBM
|
|
|
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
|
|
|
* system designs based on the PCI bus.
|
2017-05-30 03:38:38 +02:00
|
|
|
*
|
2023-01-06 15:36:05 -05:00
|
|
|
* This file is part of the 86Box distribution.
|
2017-05-30 03:38:38 +02:00
|
|
|
*
|
2023-01-06 15:36:05 -05:00
|
|
|
* Implementation of the SMC FDC37C669 Super I/O Chip.
|
2017-05-30 03:38:38 +02:00
|
|
|
*
|
2020-03-25 00:46:02 +02:00
|
|
|
*
|
2017-05-30 03:38:38 +02:00
|
|
|
*
|
2023-01-06 15:36:05 -05:00
|
|
|
* Authors: Miran Grca, <mgrca8@gmail.com>
|
|
|
|
|
*
|
2024-01-07 01:42:34 +01:00
|
|
|
* Copyright 2016-2024 Miran Grca.
|
2017-05-30 03:38:38 +02:00
|
|
|
*/
|
2024-01-07 01:42:34 +01:00
|
|
|
#include <stdarg.h>
|
2017-09-25 04:31:20 -04:00
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdint.h>
|
2018-11-08 19:21:55 +01:00
|
|
|
#include <stdlib.h>
|
2017-09-25 04:31:20 -04:00
|
|
|
#include <string.h>
|
|
|
|
|
#include <wchar.h>
|
2024-01-07 01:42:34 +01:00
|
|
|
#define HAVE_STDARG_H
|
2020-03-29 14:24:42 +02:00
|
|
|
#include <86box/86box.h>
|
|
|
|
|
#include <86box/io.h>
|
|
|
|
|
#include <86box/timer.h>
|
|
|
|
|
#include <86box/device.h>
|
|
|
|
|
#include <86box/pci.h>
|
|
|
|
|
#include <86box/lpt.h>
|
|
|
|
|
#include <86box/serial.h>
|
|
|
|
|
#include <86box/hdc.h>
|
|
|
|
|
#include <86box/hdc_ide.h>
|
|
|
|
|
#include <86box/fdd.h>
|
|
|
|
|
#include <86box/fdc.h>
|
|
|
|
|
#include <86box/sio.h>
|
2017-09-04 01:52:29 -04:00
|
|
|
|
2023-06-28 13:46:28 -04:00
|
|
|
typedef struct fdc37c669_t {
|
|
|
|
|
uint8_t id;
|
|
|
|
|
uint8_t tries;
|
2025-08-11 16:36:30 +02:00
|
|
|
uint8_t has_ide;
|
|
|
|
|
uint8_t dma_map[4];
|
2025-08-12 01:09:24 +06:00
|
|
|
uint8_t irq_map[16];
|
2025-08-11 16:36:30 +02:00
|
|
|
uint8_t regs[256];
|
2023-06-28 13:46:28 -04:00
|
|
|
int locked;
|
|
|
|
|
int rw_locked;
|
|
|
|
|
int cur_reg;
|
2022-09-18 17:17:00 -04:00
|
|
|
fdc_t *fdc;
|
2025-08-02 14:51:28 +02:00
|
|
|
lpt_t *lpt;
|
2018-11-08 19:21:55 +01:00
|
|
|
serial_t *uart[2];
|
|
|
|
|
} fdc37c669_t;
|
2017-05-05 01:49:42 +02:00
|
|
|
|
2022-09-18 17:17:00 -04:00
|
|
|
static int next_id = 0;
|
2021-08-20 17:50:42 +02:00
|
|
|
|
2024-01-07 01:42:34 +01:00
|
|
|
#ifdef ENABLE_FDC37C669_LOG
|
|
|
|
|
int fdc37c669_do_log = ENABLE_FDC37C669_LOG;
|
2017-05-05 01:49:42 +02:00
|
|
|
|
2024-01-07 01:42:34 +01:00
|
|
|
static void
|
|
|
|
|
fdc37c669_log(const char *fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
va_list ap;
|
2023-06-28 13:46:28 -04:00
|
|
|
|
2024-01-07 01:42:34 +01:00
|
|
|
if (fdc37c669_do_log) {
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
pclog_ex(fmt, ap);
|
|
|
|
|
va_end(ap);
|
2018-11-08 19:21:55 +01:00
|
|
|
}
|
2024-01-07 01:42:34 +01:00
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
# define fdc37c669_log(fmt, ...)
|
|
|
|
|
#endif
|
2017-09-07 01:52:36 +02:00
|
|
|
|
2024-01-07 01:42:34 +01:00
|
|
|
static void
|
|
|
|
|
fdc37c669_fdc_handler(fdc37c669_t *dev)
|
|
|
|
|
{
|
|
|
|
|
fdc_remove(dev->fdc);
|
2025-08-11 16:36:30 +02:00
|
|
|
|
2024-01-07 01:42:34 +01:00
|
|
|
if (dev->regs[0x20] & 0xc0)
|
|
|
|
|
fdc_set_base(dev->fdc, ((uint16_t) dev->regs[0x20]) << 2);
|
|
|
|
|
}
|
2018-11-08 19:21:55 +01:00
|
|
|
|
2024-01-07 01:42:34 +01:00
|
|
|
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]);
|
2025-08-11 16:36:30 +02:00
|
|
|
|
2024-01-07 01:42:34 +01:00
|
|
|
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);
|
|
|
|
|
|
2025-08-02 14:51:28 +02:00
|
|
|
lpt_port_remove(dev->lpt);
|
2025-08-11 16:36:30 +02:00
|
|
|
|
|
|
|
|
if (dev->regs[0x01] & 0x08) {
|
|
|
|
|
lpt_set_ext(dev->lpt, 0);
|
|
|
|
|
|
|
|
|
|
lpt_set_epp(dev->lpt, 0);
|
|
|
|
|
lpt_set_ecp(dev->lpt, 0);
|
|
|
|
|
} else {
|
|
|
|
|
lpt_set_ext(dev->lpt, 1);
|
|
|
|
|
|
|
|
|
|
lpt_set_epp(dev->lpt, dev->regs[0x04] & 0x01);
|
|
|
|
|
lpt_set_ecp(dev->lpt, dev->regs[0x04] & 0x02);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lpt_set_fifo_threshold(dev->lpt, dev->regs[0x0a] & 0x0f);
|
|
|
|
|
|
2024-01-07 01:42:34 +01:00
|
|
|
if ((dev->regs[0x01] & 0x04) && (dev->regs[0x23] >= 0x40))
|
2025-08-02 14:51:28 +02:00
|
|
|
lpt_port_setup(dev->lpt, ((uint16_t) (dev->regs[0x23] & mask)) << 2);
|
2017-05-05 01:49:42 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-11 16:36:30 +02:00
|
|
|
static void
|
|
|
|
|
ide_handler(fdc37c669_t *dev)
|
|
|
|
|
{
|
|
|
|
|
if (dev->has_ide > 0) {
|
|
|
|
|
int ide_id = dev->has_ide - 1;
|
|
|
|
|
|
|
|
|
|
ide_handlers(ide_id, 0);
|
|
|
|
|
|
|
|
|
|
ide_set_base_addr(ide_id, 0, ((uint16_t) (dev->regs[0x21] & 0xfc)) << 2);
|
|
|
|
|
ide_set_base_addr(ide_id, 1, (((uint16_t) (dev->regs[0x22] & 0xfc)) << 2) | 0x0006);
|
|
|
|
|
|
|
|
|
|
if ((dev->regs[0x00] & 0x03) == 0x02)
|
|
|
|
|
ide_handlers(ide_id, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-08 19:21:55 +01:00
|
|
|
static void
|
|
|
|
|
fdc37c669_write(uint16_t port, uint8_t val, void *priv)
|
2017-05-05 01:49:42 +02:00
|
|
|
{
|
2022-09-18 17:17:00 -04:00
|
|
|
fdc37c669_t *dev = (fdc37c669_t *) priv;
|
|
|
|
|
uint8_t index = (port & 1) ? 0 : 1;
|
2024-01-07 01:42:34 +01:00
|
|
|
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);
|
2018-11-08 19:21:55 +01:00
|
|
|
|
|
|
|
|
if (index) {
|
2022-09-18 17:17:00 -04:00
|
|
|
if ((val == 0x55) && !dev->locked) {
|
2024-01-07 01:42:34 +01:00
|
|
|
dev->tries = (dev->tries + 1) & 1;
|
|
|
|
|
|
|
|
|
|
if (!dev->tries)
|
2022-09-18 17:17:00 -04:00
|
|
|
dev->locked = 1;
|
|
|
|
|
} else {
|
|
|
|
|
if (dev->locked) {
|
|
|
|
|
if (val == 0xaa)
|
|
|
|
|
dev->locked = 0;
|
2024-01-07 01:42:34 +01:00
|
|
|
else
|
|
|
|
|
dev->cur_reg = val;
|
|
|
|
|
} else
|
|
|
|
|
dev->tries = 0;
|
2022-09-18 17:17:00 -04:00
|
|
|
}
|
2024-01-07 01:42:34 +01:00
|
|
|
} 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);
|
2025-08-11 16:36:30 +02:00
|
|
|
if (!dev->id && (valxor & 0x08))
|
2024-01-07 01:42:34 +01:00
|
|
|
fdc_set_power_down(dev->fdc, !(val & 0x08));
|
2025-08-11 16:36:30 +02:00
|
|
|
if (!dev->id && (valxor & 0x03))
|
|
|
|
|
ide_handler(dev);
|
2022-09-18 17:17:00 -04:00
|
|
|
break;
|
2024-01-07 01:42:34 +01:00
|
|
|
case 0x01:
|
|
|
|
|
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0x73) | (val & 0x8c);
|
2025-08-11 16:36:30 +02:00
|
|
|
if (valxor & 0x0c)
|
2024-01-07 01:42:34 +01:00
|
|
|
fdc37c669_lpt_handler(dev);
|
|
|
|
|
if (valxor & 0x80)
|
|
|
|
|
dev->rw_locked = !(val & 0x80);
|
2022-09-18 17:17:00 -04:00
|
|
|
break;
|
2024-01-07 01:42:34 +01:00
|
|
|
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);
|
2022-09-18 17:17:00 -04:00
|
|
|
break;
|
2024-01-07 01:42:34 +01:00
|
|
|
case 0x03:
|
|
|
|
|
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0x08) | (val & 0xf7);
|
2025-08-11 16:36:30 +02:00
|
|
|
if ((valxor & 0x60) && (dev->fdc != NULL)) {
|
|
|
|
|
fdc_clear_flags(dev->fdc, FDC_FLAG_PS2 | FDC_FLAG_PS2_MCA);
|
|
|
|
|
switch (val & 0x0c) {
|
|
|
|
|
case 0x00:
|
|
|
|
|
fdc_set_flags(dev->fdc, FDC_FLAG_PS2);
|
|
|
|
|
break;
|
|
|
|
|
case 0x20:
|
|
|
|
|
fdc_set_flags(dev->fdc, FDC_FLAG_PS2_MCA);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-07 01:42:34 +01:00
|
|
|
if (!dev->id && (valxor & 0x02))
|
|
|
|
|
fdc_update_enh_mode(dev->fdc, !!(val & 0x02));
|
|
|
|
|
break;
|
|
|
|
|
case 0x04:
|
|
|
|
|
dev->regs[dev->cur_reg] = val;
|
|
|
|
|
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));
|
2022-09-18 17:17:00 -04:00
|
|
|
break;
|
2024-01-07 01:42:34 +01:00
|
|
|
case 0x05:
|
|
|
|
|
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0x83) | (val & 0x7c);
|
2022-09-18 17:17:00 -04:00
|
|
|
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;
|
2024-01-07 01:42:34 +01:00
|
|
|
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);
|
2025-08-11 16:36:30 +02:00
|
|
|
if (valxor & 0x0f)
|
|
|
|
|
fdc37c669_lpt_handler(dev);
|
2024-01-07 01:42:34 +01:00
|
|
|
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);
|
2022-09-18 17:17:00 -04:00
|
|
|
break;
|
|
|
|
|
case 0x20:
|
2024-01-07 01:42:34 +01:00
|
|
|
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;
|
2025-08-11 16:36:30 +02:00
|
|
|
if (!dev->id && (valxor & 0xfc))
|
|
|
|
|
ide_handler(dev);
|
2024-01-07 01:42:34 +01:00
|
|
|
break;
|
|
|
|
|
case 0x22:
|
|
|
|
|
dev->regs[dev->cur_reg] = (dev->regs[dev->cur_reg] & 0x03) | (val & 0xfc);
|
2025-08-11 16:36:30 +02:00
|
|
|
if (!dev->id && (valxor & 0xfc))
|
|
|
|
|
ide_handler(dev);
|
2022-09-18 17:17:00 -04:00
|
|
|
break;
|
|
|
|
|
case 0x23:
|
2024-01-07 01:42:34 +01:00
|
|
|
dev->regs[dev->cur_reg] = val;
|
|
|
|
|
if (valxor)
|
|
|
|
|
fdc37c669_lpt_handler(dev);
|
2022-09-18 17:17:00 -04:00
|
|
|
break;
|
|
|
|
|
case 0x24:
|
2024-01-07 01:42:34 +01:00
|
|
|
dev->regs[dev->cur_reg] = val & 0xfe;
|
|
|
|
|
if (valxor & 0xfe)
|
|
|
|
|
fdc37c669_uart_handler(dev, 0);
|
2022-09-18 17:17:00 -04:00
|
|
|
break;
|
|
|
|
|
case 0x25:
|
2024-01-07 01:42:34 +01:00
|
|
|
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);
|
2025-08-11 16:36:30 +02:00
|
|
|
if (valxor & 0x0f)
|
|
|
|
|
lpt_port_dma(dev->lpt, val & 0x0f);
|
2022-09-18 17:17:00 -04:00
|
|
|
break;
|
|
|
|
|
case 0x27:
|
2024-01-07 01:42:34 +01:00
|
|
|
dev->regs[dev->cur_reg] = val;
|
|
|
|
|
if (valxor & 0xf0)
|
|
|
|
|
fdc_set_irq(dev->fdc, val >> 4);
|
|
|
|
|
if (valxor & 0x0f)
|
2025-08-02 14:51:28 +02:00
|
|
|
lpt_port_irq(dev->lpt, val & 0x0f);
|
2022-09-18 17:17:00 -04:00
|
|
|
break;
|
|
|
|
|
case 0x28:
|
2024-01-07 01:42:34 +01:00
|
|
|
dev->regs[dev->cur_reg] = val;
|
|
|
|
|
if (valxor & 0xf0)
|
|
|
|
|
fdc37c669_uart_handler(dev, 0);
|
|
|
|
|
if (valxor & 0x0f)
|
|
|
|
|
fdc37c669_uart_handler(dev, 1);
|
2022-09-18 17:17:00 -04:00
|
|
|
break;
|
2024-01-07 01:42:34 +01:00
|
|
|
case 0x29:
|
|
|
|
|
dev->regs[dev->cur_reg] = val & 0x0f;
|
2023-06-28 13:46:28 -04:00
|
|
|
break;
|
2018-11-08 19:21:55 +01:00
|
|
|
}
|
2017-05-05 01:49:42 +02:00
|
|
|
}
|
|
|
|
|
|
2018-11-08 19:21:55 +01:00
|
|
|
static uint8_t
|
|
|
|
|
fdc37c669_read(uint16_t port, void *priv)
|
2017-05-05 01:49:42 +02:00
|
|
|
{
|
2023-08-21 20:24:33 -04:00
|
|
|
const fdc37c669_t *dev = (fdc37c669_t *) priv;
|
|
|
|
|
uint8_t index = (port & 1) ? 0 : 1;
|
|
|
|
|
uint8_t ret = 0xff;
|
2017-05-05 01:49:42 +02:00
|
|
|
|
2018-11-08 19:21:55 +01:00
|
|
|
if (dev->locked) {
|
2022-09-18 17:17:00 -04:00
|
|
|
if (index)
|
|
|
|
|
ret = dev->cur_reg;
|
2024-01-07 01:42:34 +01:00
|
|
|
else if (!dev->rw_locked || (dev->cur_reg > 0x0f))
|
2022-09-18 17:17:00 -04:00
|
|
|
ret = dev->regs[dev->cur_reg];
|
2018-11-08 19:21:55 +01:00
|
|
|
}
|
2017-05-05 01:49:42 +02:00
|
|
|
|
2024-01-07 01:42:34 +01:00
|
|
|
fdc37c669_log("[%04X:%08X] [R] %04X = %02X (%i, %i)\n", CS, cpu_state.pc, port, ret,
|
|
|
|
|
dev->tries, dev->locked);
|
|
|
|
|
|
2018-11-08 19:21:55 +01:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2024-01-07 01:42:34 +01:00
|
|
|
fdc37c669_reset(void *priv)
|
2018-11-08 19:21:55 +01:00
|
|
|
{
|
2024-01-07 01:42:34 +01:00
|
|
|
fdc37c669_t *dev = (fdc37c669_t *) priv;
|
2018-11-08 19:21:55 +01:00
|
|
|
|
2024-01-07 01:42:34 +01:00
|
|
|
memset(dev->regs, 0x00, 42);
|
2018-11-08 19:21:55 +01:00
|
|
|
|
|
|
|
|
dev->regs[0x00] = 0x28;
|
|
|
|
|
dev->regs[0x01] = 0x9c;
|
|
|
|
|
dev->regs[0x02] = 0x88;
|
|
|
|
|
dev->regs[0x03] = 0x78;
|
|
|
|
|
dev->regs[0x06] = 0xff;
|
|
|
|
|
dev->regs[0x0d] = 0x03;
|
|
|
|
|
dev->regs[0x0e] = 0x02;
|
2024-01-07 01:42:34 +01:00
|
|
|
dev->regs[0x1e] = 0x3c; /* Gameport controller. */
|
|
|
|
|
dev->regs[0x20] = 0x3c;
|
|
|
|
|
dev->regs[0x21] = 0x3c;
|
|
|
|
|
dev->regs[0x22] = 0x3d;
|
2021-08-20 17:50:42 +02:00
|
|
|
|
2025-08-11 16:36:30 +02:00
|
|
|
if (!dev->id) {
|
2022-09-18 17:17:00 -04:00
|
|
|
fdc_reset(dev->fdc);
|
2025-08-11 16:36:30 +02:00
|
|
|
|
2024-01-07 01:42:34 +01:00
|
|
|
fdc37c669_fdc_handler(dev);
|
2025-08-11 16:36:30 +02:00
|
|
|
fdc_clear_flags(dev->fdc, FDC_FLAG_PS2 | FDC_FLAG_PS2_MCA);
|
|
|
|
|
|
|
|
|
|
ide_handler(dev);
|
2024-01-07 01:42:34 +01:00
|
|
|
}
|
2021-08-20 17:50:42 +02:00
|
|
|
|
2024-01-07 01:42:34 +01:00
|
|
|
fdc37c669_uart_handler(dev, 0);
|
|
|
|
|
serial_set_clock_src(dev->uart[0], fdc37c669_uart_get_clock_src(dev, 0));
|
2021-08-20 17:50:42 +02:00
|
|
|
|
2024-01-07 01:42:34 +01:00
|
|
|
fdc37c669_uart_handler(dev, 1);
|
|
|
|
|
serial_set_clock_src(dev->uart[1], fdc37c669_uart_get_clock_src(dev, 1));
|
2021-08-20 17:50:42 +02:00
|
|
|
|
2024-01-07 01:42:34 +01:00
|
|
|
fdc37c669_lpt_handler(dev);
|
2018-11-08 19:21:55 +01:00
|
|
|
|
2022-09-18 17:17:00 -04:00
|
|
|
dev->locked = 0;
|
2018-11-08 19:21:55 +01:00
|
|
|
dev->rw_locked = 0;
|
2017-05-05 01:49:42 +02:00
|
|
|
}
|
|
|
|
|
|
2018-11-08 19:21:55 +01:00
|
|
|
static void
|
|
|
|
|
fdc37c669_close(void *priv)
|
2017-05-05 01:49:42 +02:00
|
|
|
{
|
2018-11-08 19:21:55 +01:00
|
|
|
fdc37c669_t *dev = (fdc37c669_t *) priv;
|
|
|
|
|
|
2021-08-20 17:50:42 +02:00
|
|
|
next_id = 0;
|
|
|
|
|
|
2018-11-08 19:21:55 +01:00
|
|
|
free(dev);
|
2017-05-05 01:49:42 +02:00
|
|
|
}
|
|
|
|
|
|
2018-11-08 19:21:55 +01:00
|
|
|
static void *
|
|
|
|
|
fdc37c669_init(const device_t *info)
|
2017-05-05 01:49:42 +02:00
|
|
|
{
|
2025-01-07 00:42:06 -05:00
|
|
|
fdc37c669_t *dev = (fdc37c669_t *) calloc(1, sizeof(fdc37c669_t));
|
2018-01-17 18:43:36 +01:00
|
|
|
|
2021-08-20 17:50:42 +02:00
|
|
|
dev->id = next_id;
|
2017-05-05 01:49:42 +02:00
|
|
|
|
2025-08-11 16:36:30 +02:00
|
|
|
if (next_id != 1) {
|
|
|
|
|
dev->fdc = device_add(&fdc_at_smc_device);
|
|
|
|
|
dev->has_ide = (info->local >> 8) & 0xff;
|
|
|
|
|
}
|
2018-11-08 19:21:55 +01:00
|
|
|
|
2021-08-20 17:50:42 +02:00
|
|
|
dev->uart[0] = device_add_inst(&ns16550_device, (next_id << 1) + 1);
|
|
|
|
|
dev->uart[1] = device_add_inst(&ns16550_device, (next_id << 1) + 2);
|
|
|
|
|
|
2025-08-02 14:51:28 +02:00
|
|
|
dev->lpt = device_add_inst(&lpt_port_device, next_id + 1);
|
|
|
|
|
|
2025-08-11 16:36:30 +02:00
|
|
|
io_sethandler((info->local & FDC37C6XX_370) ? FDC_SECONDARY_ADDR : (next_id ? FDC_SECONDARY_ADDR : FDC_PRIMARY_ADDR),
|
2024-01-07 01:42:34 +01:00
|
|
|
0x0002, fdc37c669_read, NULL, NULL, fdc37c669_write, NULL, NULL, dev);
|
2018-11-08 19:21:55 +01:00
|
|
|
|
2025-08-11 16:36:30 +02:00
|
|
|
dev->dma_map[0] = 4;
|
|
|
|
|
for (int i = 1; i < 4; i++)
|
|
|
|
|
dev->dma_map[i] = i;
|
|
|
|
|
|
|
|
|
|
memset(dev->irq_map, 0xff, 16);
|
|
|
|
|
dev->irq_map[0] = 0xff;
|
|
|
|
|
for (int i = 1; i < 7; i++)
|
|
|
|
|
dev->irq_map[i] = i;
|
|
|
|
|
dev->irq_map[1] = 5;
|
|
|
|
|
dev->irq_map[5] = 7;
|
|
|
|
|
dev->irq_map[7] = 0xff; /* Reserved. */
|
|
|
|
|
dev->irq_map[8] = 10;
|
|
|
|
|
dev->irq_map[9] = 9; /* This is used by the Acrosser PJ-A511M for IRQ 9. */
|
|
|
|
|
dev->irq_map[11] = 11; /* This is used by the Acrosser PJ-A511M for IRQ 11. */
|
|
|
|
|
|
2018-11-08 19:21:55 +01:00
|
|
|
fdc37c669_reset(dev);
|
|
|
|
|
|
2021-08-20 17:50:42 +02:00
|
|
|
next_id++;
|
|
|
|
|
|
2018-11-08 19:21:55 +01:00
|
|
|
return dev;
|
2017-05-05 01:49:42 +02:00
|
|
|
}
|
2018-11-08 19:21:55 +01:00
|
|
|
|
|
|
|
|
const device_t fdc37c669_device = {
|
2022-09-18 17:17:00 -04:00
|
|
|
.name = "SMC FDC37C669 Super I/O",
|
2022-03-13 09:57:57 -04:00
|
|
|
.internal_name = "fdc37c669",
|
2022-09-18 17:17:00 -04:00
|
|
|
.flags = 0,
|
|
|
|
|
.local = 0,
|
|
|
|
|
.init = fdc37c669_init,
|
|
|
|
|
.close = fdc37c669_close,
|
2024-01-07 01:42:34 +01:00
|
|
|
.reset = fdc37c669_reset,
|
2025-01-07 01:12:42 -05:00
|
|
|
.available = NULL,
|
2022-03-13 09:57:57 -04:00
|
|
|
.speed_changed = NULL,
|
2022-09-18 17:17:00 -04:00
|
|
|
.force_redraw = NULL,
|
|
|
|
|
.config = NULL
|
2018-11-08 19:21:55 +01:00
|
|
|
};
|