/* * 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. * * Implementation of Ports 61, 62, and 63 used by various * machines. * * * * Authors: Miran Grca, * * Copyright 2021 Miran Grca. */ #include #include #include #include #include #include <86box/86box.h> #include <86box/device.h> #include "cpu.h" #include <86box/timer.h> #include <86box/io.h> #include <86box/keyboard.h> #include <86box/mem.h> #include <86box/m_xt_xi8088.h> #include <86box/fdd.h> #include <86box/fdc.h> #include <86box/sound.h> #include <86box/snd_speaker.h> #include <86box/pit.h> #include <86box/ppi.h> #include <86box/video.h> #include <86box/port_6x.h> #include <86box/plat_unused.h> #include <86box/random.h> #define PS2_REFRESH_TIME (16 * TIMER_USEC) #define PORT_6X_TURBO 1 #define PORT_6X_EXT_REF 2 #define PORT_6X_MIRROR 4 #define PORT_6X_SWA 8 static int cycles_sub = 0; static void port_6x_write(uint16_t port, uint8_t val, void *priv) { const port_6x_t *dev = (port_6x_t *) priv; port &= 3; cycles -= cycles_sub; if ((port == 3) && (dev->flags & PORT_6X_MIRROR)) port = 1; switch (port) { case 1: ppi.pb = (ppi.pb & 0x10) | (val & 0x0f); speaker_update(); speaker_gated = val & 1; speaker_enable = val & 2; if (speaker_enable) was_speaker_enable = 1; pit_devs[0].set_gate(pit_devs[0].data, 2, val & 1); if (dev->flags & PORT_6X_TURBO) xi8088_turbo_set(!!(val & 0x04)); break; default: break; } } static uint8_t port_61_read_simple(UNUSED(uint16_t port), UNUSED(void *priv)) { uint8_t ret = ppi.pb & 0x1f; cycles -= cycles_sub; if (ppispeakon) ret |= 0x20; return ret; } static uint8_t port_61_read(UNUSED(uint16_t port), void *priv) { const port_6x_t *dev = (port_6x_t *) priv; uint8_t ret = 0xff; cycles -= cycles_sub; if (dev->flags & PORT_6X_EXT_REF) { ret = ppi.pb & 0x0f; if (dev->refresh) ret |= 0x10; } else ret = ppi.pb & 0x1f; if (ppispeakon) ret |= 0x20; if (dev->flags & PORT_6X_TURBO) ret = (ret & 0xfb) | (xi8088_turbo_get() ? 0x04 : 0x00); return ret; } static uint8_t port_62_read(UNUSED(uint16_t port), UNUSED(void *priv)) { uint8_t ret = 0xff; /* SWA on Olivetti M240 mainboard (off=1) */ ret = 0x00; if (ppi.pb & 0x8) { /* Switches 4, 5 - floppy drives (number) */ int fdd_count = 0; for (uint8_t i = 0; i < FDD_NUM; i++) { if (fdd_get_flags(i)) fdd_count++; } if (!fdd_count) ret |= 0x00; else ret |= ((fdd_count - 1) << 2); /* Switches 6, 7 - monitor type */ if (video_is_mda()) ret |= 0x3; else if (video_is_cga()) ret |= 0x2; /* 0x10 would be 40x25 */ else ret |= 0x0; } else { /* bit 2 always on */ ret |= 0x4; /* Switch 8 - 8087 FPU. */ if (hasfpu) ret |= 0x02; } return ret; } static void port_6x_refresh(void *priv) { port_6x_t *dev = (port_6x_t *) priv; dev->refresh = !dev->refresh; timer_advance_u64(&dev->refresh_timer, PS2_REFRESH_TIME); } static void port_6x_close(void *priv) { port_6x_t *dev = (port_6x_t *) priv; timer_disable(&dev->refresh_timer); free(dev); } void * port_6x_init(const device_t *info) { port_6x_t *dev = (port_6x_t *) malloc(sizeof(port_6x_t)); memset(dev, 0, sizeof(port_6x_t)); dev->flags = info->local & 0xff; if (dev->flags & (PORT_6X_TURBO | PORT_6X_EXT_REF)) { io_sethandler(0x0061, 1, port_61_read, NULL, NULL, port_6x_write, NULL, NULL, dev); if (dev->flags & PORT_6X_EXT_REF) timer_add(&dev->refresh_timer, port_6x_refresh, dev, 1); if (dev->flags & PORT_6X_MIRROR) io_sethandler(0x0063, 1, port_61_read, NULL, NULL, port_6x_write, NULL, NULL, dev); } else { io_sethandler(0x0061, 1, port_61_read_simple, NULL, NULL, port_6x_write, NULL, NULL, dev); if (dev->flags & PORT_6X_MIRROR) io_sethandler(0x0063, 1, port_61_read_simple, NULL, NULL, port_6x_write, NULL, NULL, dev); } if (dev->flags & PORT_6X_SWA) io_sethandler(0x0062, 1, port_62_read, NULL, NULL, NULL, NULL, NULL, dev); cycles_sub = is486 ? ISA_CYCLES(8) : 0; return dev; } const device_t port_6x_device = { .name = "Port 6x Registers", .internal_name = "port_6x", .flags = 0, .local = 0, .init = port_6x_init, .close = port_6x_close, .reset = NULL, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; const device_t port_6x_xi8088_device = { .name = "Port 6x Registers (Xi8088)", .internal_name = "port_6x_xi8088", .flags = 0, .local = PORT_6X_TURBO | PORT_6X_EXT_REF | PORT_6X_MIRROR, .init = port_6x_init, .close = port_6x_close, .reset = NULL, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; const device_t port_6x_ps2_device = { .name = "Port 6x Registers (IBM PS/2)", .internal_name = "port_6x_ps2", .flags = 0, .local = PORT_6X_EXT_REF, .init = port_6x_init, .close = port_6x_close, .reset = NULL, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; const device_t port_6x_olivetti_device = { .name = "Port 6x Registers (Olivetti)", .internal_name = "port_6x_olivetti", .flags = 0, .local = PORT_6X_SWA, .init = port_6x_init, .close = port_6x_close, .reset = NULL, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL };