diff --git a/src/sio/sio_fdc37m60x.c b/src/sio/sio_fdc37m60x.c new file mode 100644 index 000000000..ca0111405 --- /dev/null +++ b/src/sio/sio_fdc37m60x.c @@ -0,0 +1,321 @@ +/* + * 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. + * + * This file is part of the 86Box distribution. + * + * Emulation of the SMSC FDC37M60x Super I/O + * + * Authors: Tiseno100 + * Copyright 2020 Tiseno100 + */ +#include +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/io.h> +#include <86box/timer.h> +#include <86box/device.h> +#include <86box/keyboard.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> + +#define SIO_INDEX_PORT dev->sio_index_port +#define INDEX dev->index + +/* Current Logical Device Number */ +#define CURRENT_LOGICAL_DEVICE dev->regs[0x07] + +/* Global Device Configuration */ +#define ENABLED dev->device_regs[CURRENT_LOGICAL_DEVICE][0x30] +#define BASE_ADDRESS ((dev->device_regs[CURRENT_LOGICAL_DEVICE][0x60] << 8) | (dev->device_regs[CURRENT_LOGICAL_DEVICE][0x61])) +#define IRQ dev->device_regs[CURRENT_LOGICAL_DEVICE][0x70] +#define DMA dev->device_regs[CURRENT_LOGICAL_DEVICE][0x74] + +/* Miscellaneous Chip Functionality */ +#define SOFT_RESET (val & 0x01) +#define POWER_CONTROL dev->regs[0x22] + +#ifdef ENABLE_FDC37M60X_LOG +int fdc37m60x_do_log = ENABLE_FDC37M60X_LOG; +static void +fdc37m60x_log(const char *fmt, ...) +{ + va_list ap; + + if (fdc37m60x_do_log) + { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +#define fdc37m60x_log(fmt, ...) +#endif + +typedef struct +{ + uint8_t index, regs[256], device_regs[10][256], cfg_lock, ide_function; + uint16_t sio_index_port; + + fdc_t *fdc_controller; + serial_t *uart[2]; + +} fdc37m60x_t; + +void fdc37m60x_fdc_handler(fdc37m60x_t *dev); +void fdc37m60x_uart_handler(uint8_t num, fdc37m60x_t *dev); +void fdc37m60x_lpt_handler(fdc37m60x_t *dev); +void fdc37m60x_logical_device_handler(fdc37m60x_t *dev); +static void fdc37m60x_reset(void *priv); + +static void +fdc37m60x_write(uint16_t addr, uint8_t val, void *priv) +{ + fdc37m60x_t *dev = (fdc37m60x_t *)priv; + + switch (addr) + { + case 0x3f0: + case 0x370: + INDEX = val; + + /* Enter/Escape Configuration Mode */ + if (val == 0x55) + dev->cfg_lock = 0; + else if (val == 0xaa) + dev->cfg_lock = 1; + break; + + case 0x3f1: + case 0x371: + if (!dev->cfg_lock) + { + switch (INDEX) + { + /* Global Configuration */ + case 0x02: + dev->regs[INDEX] = val; + if (SOFT_RESET) + fdc37m60x_reset(dev); + break; + + case 0x07: + CURRENT_LOGICAL_DEVICE = (val & 0x0f); + break; + + case 0x22: + POWER_CONTROL = val & 0x3f; + break; + + case 0x23: + dev->regs[INDEX] = val & 0x3f; + break; + + case 0x24: + dev->regs[INDEX] = val & 0xce; + break; + + /* Device Configuration */ + case 0x30: + case 0x60: + case 0x61: + case 0x70: + case 0x74: + if(CURRENT_LOGICAL_DEVICE <= 0x81) /* Avoid Overflow */ + dev->device_regs[CURRENT_LOGICAL_DEVICE][INDEX] = (INDEX == 0x30) ? (val & 1) : val; + fdc37m60x_logical_device_handler(dev); + break; + } + } + break; + } +} + +static uint8_t +fdc37m60x_read(uint16_t addr, void *priv) +{ + fdc37m60x_t *dev = (fdc37m60x_t *)priv; + + return (INDEX >= 0x30) ? dev->device_regs[CURRENT_LOGICAL_DEVICE][INDEX] : dev->regs[INDEX]; +} + +void fdc37m60x_fdc_handler(fdc37m60x_t *dev) +{ + fdc_remove(dev->fdc_controller); + if(ENABLED || (POWER_CONTROL & 0x01)) + { + fdc_set_base(dev->fdc_controller, BASE_ADDRESS); + fdc_set_irq(dev->fdc_controller, IRQ & 0xf); + fdc_set_dma_ch(dev->fdc_controller, DMA & 0x07); + fdc37m60x_log("SMC60x-FDC: BASE %04x IRQ %d DMA %d\n", BASE_ADDRESS, IRQ & 0xf, DMA & 0x07); + } +} + +void fdc37m60x_uart_handler(uint8_t num, fdc37m60x_t *dev) +{ + serial_remove(dev->uart[num & 1]); + if(!(num & 1) ? (ENABLED || (POWER_CONTROL & 0x10)) : (ENABLED || (POWER_CONTROL & 0x20))) + { + serial_setup(dev->uart[num & 1], BASE_ADDRESS, IRQ & 0xf); + fdc37m60x_log("SMC60x-UART%d: BASE %04x IRQ %d\n", num & 1, BASE_ADDRESS, IRQ & 0xf); + } +} + +void fdc37m60x_lpt_handler(fdc37m60x_t *dev) +{ + lpt1_remove(); + if(ENABLED || (POWER_CONTROL & 0x80)) + { + lpt1_init(BASE_ADDRESS); + lpt1_irq(IRQ & 0xf); + fdc37m60x_log("SMC60x-LPT: BASE %04x IRQ %d\n", BASE_ADDRESS, IRQ & 0xf); + } +} + +void fdc37m60x_logical_device_handler(fdc37m60x_t *dev) +{ +/* +Register 07h: +Device 0: FDC +Device 3: LPT +Device 4: UART1 +Device 5: UART2 +*/ + switch (CURRENT_LOGICAL_DEVICE) + { + case 0x00: + fdc37m60x_fdc_handler(dev); + break; + + case 0x03: + fdc37m60x_lpt_handler(dev); + break; + + case 0x04: + fdc37m60x_uart_handler(0, dev); + break; + + case 0x05: + fdc37m60x_uart_handler(1, dev); + break; + } +} + +static void +fdc37m60x_reset(void *priv) +{ + fdc37m60x_t *dev = (fdc37m60x_t *)priv; + + CURRENT_LOGICAL_DEVICE = 0x00; + dev->regs[0x22] = 0x00; + dev->regs[0x26] = SIO_INDEX_PORT & 0xf; + dev->regs[0x27] = (SIO_INDEX_PORT >> 4) & 0xf; + + /* FDC Registers */ + dev->device_regs[0][0x30] = 0x00; + dev->device_regs[0][0x60] = 0x03; /* Base Address */ + dev->device_regs[0][0x61] = 0xf0; + + dev->device_regs[0][0x70] = 0x06; + dev->device_regs[0][0x74] = 0x02; + + /* LPT Port */ + dev->device_regs[3][0x30] = 0x00; + dev->device_regs[3][0x60] = 0x00; /* Base Address */ + dev->device_regs[3][0x61] = 0x00; + + dev->device_regs[3][0x64] = 0x04; + + /* UART1 */ + dev->device_regs[4][0x30] = 0x00; + dev->device_regs[4][0x60] = 0x00; /* Base Address */ + dev->device_regs[4][0x61] = 0x00; + + dev->device_regs[4][0x70] = 0x00; + + /* UART2 */ + dev->device_regs[5][0x30] = 0x00; + dev->device_regs[5][0x60] = 0x00; /* Base Address */ + dev->device_regs[5][0x61] = 0x00; + + dev->device_regs[5][0x70] = 0x00; + + /* AUX */ + dev->device_regs[8][0x30] = 0x00; + + fdc37m60x_fdc_handler(dev); + fdc37m60x_uart_handler(0, dev); + fdc37m60x_uart_handler(1, dev); + fdc37m60x_lpt_handler(dev); +} + +static void +fdc37m60x_close(void *priv) +{ + fdc37m60x_t *dev = (fdc37m60x_t *)priv; + + free(dev); +} + +static void * +fdc37m60x_init(const device_t *info) +{ + fdc37m60x_t *dev = (fdc37m60x_t *)malloc(sizeof(fdc37m60x_t)); + memset(dev, 0, sizeof(fdc37m60x_t)); + SIO_INDEX_PORT = info->local; + + dev->regs[0x20] = 0x47; + dev->regs[0x24] = 0x04; + dev->device_regs[0][0xf0] = 0x0e; + dev->device_regs[0][0xf2] = 0xff; + dev->device_regs[3][0xf0] = 0x3c; + dev->device_regs[4][0xf1] = 0x02; + dev->device_regs[4][0xf2] = 0x03; + dev->device_regs[8][0xc0] = 0x06; + dev->device_regs[8][0xc1] = 0x03; + + dev->fdc_controller = device_add(&fdc_at_smc_device); + dev->uart[0] = device_add_inst(&ns16550_device, 1); + dev->uart[1] = device_add_inst(&ns16550_device, 2); + + io_sethandler(SIO_INDEX_PORT, 0x0002, fdc37m60x_read, NULL, NULL, fdc37m60x_write, NULL, NULL, dev); + + return dev; +} + +const device_t fdc37m60x_device = { + "SMSC FDC37M60X", + 0, + 0x03f0, + fdc37m60x_init, + fdc37m60x_close, + NULL, + {NULL}, + NULL, + NULL, + NULL}; + +const device_t fdc37m60x_370_device = { + "SMSC FDC37M60X with 10K Pull Up Resistor", + 0, + 0x0370, + fdc37m60x_init, + fdc37m60x_close, + NULL, + {NULL}, + NULL, + NULL, + NULL};