diff --git a/src/device/serial.c b/src/device/serial.c index 25c9c0a49..3de0c6826 100644 --- a/src/device/serial.c +++ b/src/device/serial.c @@ -128,7 +128,7 @@ serial_update_ints(serial_t *dev) dev->iir = 0; } - if (stat && ((dev->mctrl & 8) || (dev->type == SERIAL_8250_PCJR))) { + if (stat && (dev->irq != 0xff) && ((dev->mctrl & 8) || (dev->type == SERIAL_8250_PCJR))) { if (dev->type >= SERIAL_NS16450) picintlevel(1 << dev->irq); else @@ -615,7 +615,7 @@ serial_remove(serial_t *dev) void -serial_setup(serial_t *dev, uint16_t addr, int irq) +serial_setup(serial_t *dev, uint16_t addr, uint8_t irq) { serial_log("Adding serial port %i at %04X...\n", dev->inst, addr); diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index fd01e37eb..7250cc839 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -622,6 +622,9 @@ extern int machine_tandy_init(const machine_t *); extern int machine_tandy1000hx_init(const machine_t *); extern int machine_tandy1000sl2_init(const machine_t *); +/* m_v86p.c */ +extern int machine_v86p_init(const machine_t *); + #ifdef EMU_DEVICE_H extern const device_t *tandy1k_get_device(void); extern const device_t *tandy1k_hx_get_device(void); diff --git a/src/include/86box/nvr.h b/src/include/86box/nvr.h index ceb1bc459..f1776bf79 100644 --- a/src/include/86box/nvr.h +++ b/src/include/86box/nvr.h @@ -121,6 +121,7 @@ extern void nvr_wp_set(int set, int h, nvr_t *nvr); extern void nvr_via_wp_set(int set, int reg, nvr_t *nvr); extern void nvr_bank_set(int base, uint8_t bank, nvr_t *nvr); extern void nvr_lock_set(int base, int size, int lock, nvr_t *nvr); +extern void nvr_irq_set(int irq, nvr_t *nvr); #endif /*EMU_NVR_H*/ diff --git a/src/include/86box/serial.h b/src/include/86box/serial.h index 59e179903..0b6b99aa5 100644 --- a/src/include/86box/serial.h +++ b/src/include/86box/serial.h @@ -77,7 +77,7 @@ extern serial_t * serial_attach(int port, void *priv); extern void serial_remove(serial_t *dev); extern void serial_set_type(serial_t *dev, int type); -extern void serial_setup(serial_t *dev, uint16_t addr, int irq); +extern void serial_setup(serial_t *dev, uint16_t addr, uint8_t irq); extern void serial_clear_fifo(serial_t *dev); extern void serial_write_fifo(serial_t *dev, uint8_t dat); extern void serial_set_next_inst(int ni); diff --git a/src/include/86box/sio.h b/src/include/86box/sio.h index bf9f5526c..f409f4d48 100644 --- a/src/include/86box/sio.h +++ b/src/include/86box/sio.h @@ -20,6 +20,7 @@ extern void vt82c686_sio_write(uint8_t addr, uint8_t val, void *priv); extern const device_t acc3221_device; extern const device_t f82c710_device; +extern const device_t f82c606_device; extern const device_t fdc37c651_device; extern const device_t fdc37c661_device; extern const device_t fdc37c663_device; diff --git a/src/include/86box/video.h b/src/include/86box/video.h index 59fead3b9..2232667ef 100644 --- a/src/include/86box/video.h +++ b/src/include/86box/video.h @@ -249,6 +249,9 @@ extern const device_t compaq_cga_2_device; extern const device_t ogc_device; extern const device_t ogc_m24_device; +/* Chips & Technologies 82C425 */ +extern const device_t f82c425_video_device; + /* NCR NGA */ extern const device_t nga_device; diff --git a/src/machine/CMakeLists.txt b/src/machine/CMakeLists.txt index 9b16413f6..ac62fbe16 100644 --- a/src/machine/CMakeLists.txt +++ b/src/machine/CMakeLists.txt @@ -16,7 +16,8 @@ add_library(mch OBJECT machine.c machine_table.c m_xt.c m_xt_compaq.c m_xt_philips.c m_xt_t1000.c m_xt_t1000_vid.c m_xt_xi8088.c m_xt_zenith.c m_pcjr.c - m_amstrad.c m_europc.c m_xt_olivetti.c m_tandy.c m_at.c m_at_commodore.c + m_amstrad.c m_europc.c m_xt_olivetti.c m_tandy.c m_v86p.c + m_at.c m_at_commodore.c m_at_t3100e.c m_at_t3100e_vid.c m_ps1.c m_ps1_hdc.c m_ps2_isa.c m_ps2_mca.c m_at_compaq.c m_at_286_386sx.c m_at_386dx_486.c m_at_socket4_5.c m_at_socket7.c m_at_sockets7.c m_at_socket8.c diff --git a/src/machine/m_v86p.c b/src/machine/m_v86p.c new file mode 100644 index 000000000..6aa30985a --- /dev/null +++ b/src/machine/m_v86p.c @@ -0,0 +1,83 @@ +/* + * 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. + * + * Victor V86P portable computer emulation. + * + * Author: Lubomir Rintel, + * + * Copyright 2021 Lubomir Rintel. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307 + * USA. + */ +#include +#include +#include <86box/86box.h> +#include "cpu.h" +#include <86box/mem.h> +#include <86box/timer.h> +#include <86box/rom.h> +#include <86box/machine.h> +#include <86box/device.h> +#include <86box/fdd.h> +#include <86box/fdc.h> +#include <86box/fdc_ext.h> +#include <86box/keyboard.h> +#include <86box/sio.h> +#include <86box/video.h> + +int +machine_v86p_init(const machine_t *model) +{ + int ret; + + ret = bios_load_interleavedr("roms/machines/v86p/INTEL8086AWD_BIOS_S3.1_V86P_122089_Even.rom", + "roms/machines/v86p/INTEL8086AWD_BIOS_S3.1_V86P_122089_Odd.rom", + 0x000f8000, 65536, 0); + + if (!ret) { + /* Try an older version of the BIOS. */ + ret = bios_load_interleavedr("roms/machines/v86p/INTEL8086AWD_BIOS_S3.1_V86P_090489_Even.rom", + "roms/machines/v86p/INTEL8086AWD_BIOS_S3.1_V86P_090489_Odd.rom", + 0x000f8000, 65536, 0); + } + + if (bios_only || !ret) + return ret; + + loadfont("roms/machines/v86p/v86pfont.rom", 8); + + machine_common_init(model); + + device_add(&f82c606_device); + + device_add(&keyboard_xt_device); + + if (fdc_type == FDC_INTERNAL) + device_add(&fdc_xt_device); + + if (gfxcard == VID_INTERNAL) + device_add(&f82c425_video_device); + + return ret; +} diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index f8a4f3bb7..71e6c58f3 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -105,6 +105,7 @@ const machine_t machines[] = { { "[8086] Olivetti M240", "m240", MACHINE_TYPE_8086, CPU_PKG_8086, 0, 0, 0, 0, 0, 0, 0, MACHINE_PC, 128, 640, 128, 0, machine_xt_m240_init, NULL }, { "[8086] Schetmash Iskra-3104", "iskra3104", MACHINE_TYPE_8086, CPU_PKG_8086, 0, 0, 0, 0, 0, 0, 0, MACHINE_PC, 128, 640, 128, 0, machine_xt_iskra3104_init, NULL }, { "[8086] Tandy 1000 SL/2", "tandy1000sl2", MACHINE_TYPE_8086, CPU_PKG_8086, 0, 0, 0, 0, 0, 0, 0, MACHINE_PC | MACHINE_VIDEO_FIXED, 512, 768, 128, 0, machine_tandy1000sl2_init, tandy1k_sl_get_device }, + { "[8086] Victor V86P", "v86p", MACHINE_TYPE_8086, CPU_PKG_8086, 0, 0, 0, 0, 0, 0, 0, MACHINE_PC | MACHINE_VIDEO, 512, 512, 128, 127, machine_v86p_init, NULL }, { "[8086] Toshiba T1200", "t1200", MACHINE_TYPE_8086, CPU_PKG_8086, 0, 0, 0, 0, 0, 0, 0, MACHINE_PC | MACHINE_VIDEO, 1024, 2048,1024, 63, machine_xt_t1200_init, t1200_get_device }, #if defined(DEV_BRANCH) && defined(USE_LASERXT) diff --git a/src/nvr_at.c b/src/nvr_at.c index ff1c6fcb0..171a131cc 100644 --- a/src/nvr_at.c +++ b/src/nvr_at.c @@ -922,6 +922,13 @@ nvr_lock_set(int base, int size, int lock, nvr_t *nvr) } +void +nvr_irq_set(int irq, nvr_t *nvr) +{ + nvr->irq = irq; +} + + static void nvr_at_reset(void *priv) { diff --git a/src/sio/sio_f82c710.c b/src/sio/sio_f82c710.c index 16b1fb4b2..9d52e399b 100644 --- a/src/sio/sio_f82c710.c +++ b/src/sio/sio_f82c710.c @@ -6,13 +6,23 @@ * * This file is part of the 86Box distribution. * - * Implementation of the Chips & Technologies F82C710 Universal Peripheral Controller (UPC). + * Implementation of the Chips & Technologies F82C710 Universal Peripheral + * Controller (UPC) and 82C606 CHIPSpak Multifunction Controller. + * + * Relevant literature: + * + * [1] Chips and Technologies, Inc., + * 82C605/82C606 CHIPSpak/CHIPSport MULTIFUNCTION CONTROLLERS, + * PRELIMINARY Data Sheet, Revision 1, May 1987. + * * * Authors: Sarah Walker, * Eluan Costa Miranda + * Lubomir Rintel * * Copyright 2020 Sarah Walker. * Copyright 2020 Eluan Costa Miranda. + * Copyright 2021 Lubomir Rintel. */ #include #include @@ -25,14 +35,17 @@ #include <86box/device.h> #include <86box/lpt.h> #include <86box/serial.h> +#include <86box/gameport.h> #include <86box/hdc.h> #include <86box/hdc_ide.h> #include <86box/fdd.h> #include <86box/fdc.h> +#include <86box/nvr.h> #include <86box/sio.h> typedef struct upc_t { + uint32_t local; int configuration_state; /* state of algorithm to enter configuration mode */ int configuration_mode; uint16_t cri_addr; /* cri = configuration index register, addr is even */ @@ -42,6 +55,8 @@ typedef struct upc_t /* these regs are not affected by reset */ uint8_t regs[15]; /* there are 16 indexes, but there is no need to store the last one which is: R = cri_addr / 4, W = exit config mode */ fdc_t *fdc; + nvr_t *nvr; + void *gameport; serial_t *uart[2]; } upc_t; @@ -87,6 +102,66 @@ f82c710_update_ports(upc_t *upc) } } +static void +f82c606_update_ports(upc_t *upc) +{ + uint8_t uart1_int = 0xff; + uint8_t uart2_int = 0xff; + uint8_t lpt1_int = 0xff; + int nvr_int = -1; + + serial_remove(upc->uart[0]); + serial_remove(upc->uart[1]); + lpt1_remove(); + lpt2_remove(); + + nvr_at_handler(0, upc->regs[3] * 4, upc->nvr); + nvr_at_handler(0, 0x70, upc->nvr); + + switch (upc->regs[8] & 0xc0) { + case 0x40: nvr_int = 4; break; + case 0x80: uart1_int = 4; break; + case 0xc0: uart2_int = 4; break; + } + + switch (upc->regs[8] & 0x30) { + case 0x10: nvr_int = 3; break; + case 0x20: uart1_int = 3; break; + case 0x30: uart2_int = 3; break; + } + + switch (upc->regs[8] & 0x0c) { + case 0x04: nvr_int = 5; break; + case 0x08: uart1_int = 5; break; + case 0x0c: lpt1_int = 5; break; + } + + switch (upc->regs[8] & 0x03) { + case 0x01: nvr_int = 7; break; + case 0x02: uart2_int = 7; break; + case 0x03: lpt1_int = 7; break; + } + + if (upc->regs[0] & 1) + gameport_remap(upc->gameport, upc->regs[7] * 4); + else + gameport_remap(upc->gameport, 0); + + if (upc->regs[0] & 2) + serial_setup(upc->uart[0], upc->regs[4] * 4, uart1_int); + + if (upc->regs[0] & 4) + serial_setup(upc->uart[1], upc->regs[5] * 4, uart2_int); + + if (upc->regs[0] & 8) { + lpt1_init(upc->regs[6] * 4); + lpt1_irq(lpt1_int); + } + + nvr_at_handler(1, upc->regs[3] * 4, upc->nvr); + nvr_irq_set(nvr_int, upc->nvr); +} + static uint8_t f82c710_config_read(uint16_t port, void *priv) { @@ -151,7 +226,11 @@ f82c710_config_write(uint16_t port, uint8_t val, void *priv) if (upc->cri == 0xf) { upc->configuration_mode = 0; io_removehandler(upc->cri_addr, 0x0002, f82c710_config_read, NULL, NULL, f82c710_config_write, NULL, NULL, upc); - f82c710_update_ports(upc); /* TODO: any benefit in updating at each register write instead of when exiting config mode? */ + /* TODO: any benefit in updating at each register write instead of when exiting config mode? */ + if (upc->local == 710) + f82c710_update_ports(upc); + if (upc->local == 606) + f82c606_update_ports(upc); } else { upc->regs[upc->cri] = val; } @@ -179,7 +258,8 @@ f82c710_reset(upc_t *upc) lpt1_init(0x378); lpt1_irq(7); - fdc_reset(upc->fdc); + if (upc->local == 710) + fdc_reset(upc->fdc); } static void * @@ -187,8 +267,43 @@ f82c710_init(const device_t *info) { upc_t *upc = (upc_t *) malloc(sizeof(upc_t)); memset(upc, 0, sizeof(upc_t)); + upc->local = info->local; - upc->fdc = device_add(&fdc_at_device); + if (upc->local == 710) { + upc->regs[0] = 0x0c; + upc->regs[1] = 0x00; + upc->regs[2] = 0x00; + upc->regs[3] = 0x00; + upc->regs[4] = 0xfe; + upc->regs[5] = 0x00; + upc->regs[6] = 0x9e; + upc->regs[7] = 0x00; + upc->regs[8] = 0x00; + upc->regs[9] = 0xb0; + upc->regs[10] = 0x00; + upc->regs[11] = 0x00; + upc->regs[12] = 0xa0; + upc->regs[13] = 0x00; + upc->regs[14] = 0x00; + + upc->fdc = device_add(&fdc_at_device); + } + + if (upc->local == 606) { + /* Set power-on defaults. */ + upc->regs[0] = 0x00; /* Enable */ + upc->regs[1] = 0x00; /* Configuration Register */ + upc->regs[2] = 0x00; /* Ext Baud Rate Select */ + upc->regs[3] = 0xb0; /* RTC Base */ + upc->regs[4] = 0xfe; /* UART1 Base */ + upc->regs[5] = 0xbe; /* UART2 Base */ + upc->regs[6] = 0x9e; /* Parallel Base */ + upc->regs[7] = 0x80; /* Game Base */ + upc->regs[8] = 0xec; /* Interrupt Select */ + + upc->nvr = device_add(&at_nvr_old_device); + upc->gameport = gameport_add(&gameport_sio_device); + } upc->uart[0] = device_add_inst(&ns16450_device, 1); upc->uart[1] = device_add_inst(&ns16450_device, 2); @@ -196,25 +311,12 @@ f82c710_init(const device_t *info) io_sethandler(0x02fa, 0x0001, NULL, NULL, NULL, f82c710_config_write, NULL, NULL, upc); io_sethandler(0x03fa, 0x0001, NULL, NULL, NULL, f82c710_config_write, NULL, NULL, upc); - upc->regs[0] = 0x0c; - upc->regs[1] = 0x00; - upc->regs[2] = 0x00; - upc->regs[3] = 0x00; - upc->regs[4] = 0xfe; - upc->regs[5] = 0x00; - upc->regs[6] = 0x9e; - upc->regs[7] = 0x00; - upc->regs[8] = 0x00; - upc->regs[9] = 0xb0; - upc->regs[10] = 0x00; - upc->regs[11] = 0x00; - upc->regs[12] = 0xa0; - upc->regs[13] = 0x00; - upc->regs[14] = 0x00; - f82c710_reset(upc); - f82c710_update_ports(upc); + if (upc->local == 710) + f82c710_update_ports(upc); + if (upc->local == 606) + f82c606_update_ports(upc); return upc; } @@ -227,10 +329,19 @@ f82c710_close(void *priv) free(upc); } -const device_t f82c710_device = { - "F82C710 UPC Super I/O", - 0, +const device_t f82c606_device = { + "82C606 CHIPSpak Multifunction Controller", 0, + 606, + f82c710_init, f82c710_close, NULL, + { NULL }, NULL, NULL, + NULL +}; + +const device_t f82c710_device = { + "F82C710 UPC Super I/O", + 0, + 710, f82c710_init, f82c710_close, NULL, { NULL }, NULL, NULL, NULL diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index 4cbc18535..50572dbd8 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -22,7 +22,7 @@ add_library(vid OBJECT video.c vid_table.c vid_cga.c vid_cga_comp.c vid_av9194.c vid_icd2061.c vid_ics2494.c vid_ics2595.c vid_cl54xx.c vid_et4000.c vid_sc1148x_ramdac.c vid_sc1502x_ramdac.c vid_et4000w32.c vid_stg_ramdac.c vid_ht216.c vid_oak_oti.c vid_paradise.c vid_rtg310x.c - vid_ti_cf62011.c vid_tvga.c vid_tgui9440.c vid_tkd8001_ramdac.c + vid_f82c425.c vid_ti_cf62011.c vid_tvga.c vid_tgui9440.c vid_tkd8001_ramdac.c vid_att20c49x_ramdac.c vid_s3.c vid_s3_virge.c vid_ibm_rgb528_ramdac.c vid_sdac_ramdac.c vid_ogc.c vid_nga.c vid_tvp3026_ramdac.c) diff --git a/src/video/vid_f82c425.c b/src/video/vid_f82c425.c new file mode 100644 index 000000000..243727c31 --- /dev/null +++ b/src/video/vid_f82c425.c @@ -0,0 +1,666 @@ +/* + * 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. + * + * Chips & Technologies 82C425 display controller emulation, + * with support for 640x200 LCD and SMARTMAP text contrast + * enhancement. + * + * Relevant literature: + * + * [1] Chips and Technologies, Inc., 82C425 CGA LCD/CRT Controller, + * Data Sheet, Revision No. 2.2, September 1991. + * + * + * [2] Pleva et al., COLOR TO MONOCHROME CONVERSION, + * U.S. Patent 4,977,398, Dec. 11, 1990. + * + * + * Based on Toshiba T1000 plasma display emulation code. + * + * Authors: Fred N. van Kempen, + * Miran Grca, + * Sarah Walker, + * Lubomir Rintel, + * + * Copyright 2018,2019 Fred N. van Kempen. + * Copyright 2018,2019 Miran Grca. + * Copyright 2018,2019 Sarah Walker. + * Copyright 2021 Lubomir Rintel. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307 + * USA. + */ +#include +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/io.h> +#include <86box/mem.h> +#include <86box/timer.h> +#include "cpu.h" +#include <86box/video.h> +#include <86box/vid_cga.h> + +#define F82C425_XSIZE 640 +#define F82C425_YSIZE 200 + +/* Mapping of attributes to colours */ +static uint32_t smartmap[256][2]; +static uint32_t colormap[4]; + +static video_timings_t timing_f82c425 = {VIDEO_ISA, 8,16,32, 8,16,32}; + +static uint8_t st_video_options; +static uint8_t st_enabled = 1; +static int8_t st_display_internal = -1; + +void f82c425_video_options_set(uint8_t options) +{ + st_video_options = options; +} + +void f82c425_video_enable(uint8_t enabled) +{ + st_enabled = enabled; +} + +void f82c425_display_set(uint8_t internal) +{ + st_display_internal = (int8_t)internal; +} + +uint8_t f82c425_display_get() +{ + return (uint8_t)st_display_internal; +} + + +typedef struct f82c425_t +{ + mem_mapping_t mapping; + cga_t cga; + uint8_t crtcreg; + + uint64_t dispontime, dispofftime; + + int linepos, displine; + int dispon; + uint8_t video_options; + + uint8_t *vram; + + /* Registers specific to 82C425. */ + uint8_t ac_limit; + uint8_t threshold; + uint8_t shift; + uint8_t hsync; + uint8_t vsync_blink; + uint8_t timing; + uint8_t function; +} f82c425_t; + + +/* Convert IRGB representation to RGBI, + * useful in SMARTMAP calculations. */ +static inline uint8_t f82c425_rgbi(uint8_t irgb) +{ + return ((irgb & 0x7) << 1) | (irgb >> 3); +} + +/* Convert IRGB SMARTMAP output to a RGB representation of one of 4/8 grey + * shades we'd see on an actual V86P display: with some bias toward lighter + * shades and a backlight with yellow/green-ish tint. */ +static inline uint32_t f82c425_makecol(uint8_t rgbi, int gs4, int inv) +{ + uint8_t c; + + gs4 = 1 + !!gs4; + if (!inv) + { + rgbi = 15 - rgbi; + } + c = 0x10 * gs4 * ((rgbi >> gs4) + 2); + + return makecol(c, c + 0x08, c - 0x20); +} + +/* Saturating/non-saturating addition for SMARTMAP(see below). */ +static inline int f82c425_smartmap_add(int a, int b, int sat) +{ + int c = a + b; + + /* (SATURATING OR NON SATURATING) */ + if (sat) + { + if (c < 0) + c = 0; + else if (c > 15) + c = 15; + } + + return c & 0xf; +} + +/* Calculate and cache mapping of CGA text color attribute to a + * shade of gray enhanced via the SMARTMAP algorithm. + * + * This is a straightforward implementation of the algorithm as described + * in U.S. Patent 4,977,398 [2]. The comments in capitals refer to portions + * of a figure on page 4. */ +static void f82c425_smartmap(f82c425_t *f82c425) +{ + int i; + + for (i = 0; i < 256; i++) { + uint8_t bg = f82c425_rgbi(i >> 4); + uint8_t fg = f82c425_rgbi(i & 0xf); + + /* FIG._4. */ + if (abs(bg - fg) <= (f82c425->threshold & 0x0f)) + { + /* FOREGROUND=BACKGROUND */ + if (bg == fg) + { + /* SPECIAL CASE */ + if (f82c425->shift == 0xff) + { + /* CHECK MOST SIGNIFICANT BIT */ + if (fg & 0x8) + { + /* FULL WHITE */ + fg = bg = 15; + } + else + { + /* FULL BLACK */ + fg = bg = 0; + } + } + } + else + { + uint8_t sat = f82c425->threshold & 0x10; + + /* DETERMINE WHICH IS LIGHT */ + if (fg > bg) + { + fg = f82c425_smartmap_add(fg, f82c425->shift & 0x0f, sat); + bg = f82c425_smartmap_add(bg, -(f82c425->shift >> 4), sat); + } + else + { + fg = f82c425_smartmap_add(fg, -(f82c425->shift & 0x0f), sat); + bg = f82c425_smartmap_add(bg, f82c425->shift >> 4, sat); + } + } + } + + smartmap[i][0] = f82c425_makecol(bg, f82c425->threshold & 0x20, f82c425->function & 0x80); + smartmap[i][1] = f82c425_makecol(fg, f82c425->threshold & 0x20, f82c425->function & 0x80); + } +} + +/* Calculate mapping of 320x200 graphical mode colors. */ +static void f82c425_colormap(f82c425_t *f82c425) +{ + int i; + + for (i = 0; i < 4; i++) + colormap[i] = f82c425_makecol(5 * i, 0, f82c425->function & 0x80); +} + +static void f82c425_out(uint16_t addr, uint8_t val, void *p) +{ + f82c425_t *f82c425 = (f82c425_t *)p; + + if (addr == 0x3d4) + f82c425->crtcreg = val; + + if (f82c425->function & 0x01 == 0 && (f82c425->crtcreg != 0xdf || addr != 0x3d5)) + return; + + if (addr != 0x3d5 || f82c425->crtcreg <= 31) + { + cga_out(addr, val, &f82c425->cga); + return; + } + + switch (f82c425->crtcreg) + { + case 0xd9: + f82c425->ac_limit = val; + break; + case 0xda: + f82c425->threshold = val; + f82c425_smartmap(f82c425); + break; + case 0xdb: + f82c425->shift = val; + f82c425_smartmap(f82c425); + break; + case 0xdc: + f82c425->hsync = val; + break; + case 0xdd: + f82c425->vsync_blink = val; + break; + case 0xde: + f82c425->timing = val; + break; + case 0xdf: + f82c425->function = val; + f82c425_smartmap(f82c425); + f82c425_colormap(f82c425); + break; + } +} + +static uint8_t f82c425_in(uint16_t addr, void *p) +{ + f82c425_t *f82c425 = (f82c425_t *)p; + + if (f82c425->function & 0x01 == 0) + return 0xff; + + if (addr == 0x3d4) + return f82c425->crtcreg; + + if (addr != 0x3d5 || f82c425->crtcreg <= 31) + return cga_in(addr, &f82c425->cga); + + switch (f82c425->crtcreg) + { + case 0xd9: + return f82c425->ac_limit; + case 0xda: + return f82c425->threshold; + case 0xdb: + return f82c425->shift; + case 0xdc: + return f82c425->hsync; + case 0xdd: + return f82c425->vsync_blink; + case 0xde: + return f82c425->timing; + case 0xdf: + return f82c425->function; + } +} + +static void f82c425_write(uint32_t addr, uint8_t val, void *p) +{ + f82c425_t *f82c425 = (f82c425_t *)p; + + f82c425->vram[addr & 0x3fff] = val; + cycles -= 4; +} + +static uint8_t f82c425_read(uint32_t addr, void *p) +{ + f82c425_t *f82c425 = (f82c425_t *)p; + cycles -= 4; + + return f82c425->vram[addr & 0x3fff]; +} + +static void f82c425_recalctimings(f82c425_t *f82c425) +{ + double disptime; + double _dispontime, _dispofftime; + + if (f82c425->function & 0x08) + { + cga_recalctimings(&f82c425->cga); + return; + } + + disptime = 651; + _dispontime = 640; + _dispofftime = disptime - _dispontime; + f82c425->dispontime = (uint64_t)(_dispontime * xt_cpu_multi); + f82c425->dispofftime = (uint64_t)(_dispofftime * xt_cpu_multi); +} + +/* Draw a row of text. */ +static void f82c425_text_row(f82c425_t *f82c425) +{ + uint32_t colors[2]; + int x, c; + uint8_t chr, attr; + int drawcursor; + int cursorline; + int blink; + uint16_t addr; + uint8_t sc; + uint16_t ma = (f82c425->cga.crtc[0x0d] | (f82c425->cga.crtc[0x0c] << 8)) & 0x3fff; + uint16_t ca = (f82c425->cga.crtc[0x0f] | (f82c425->cga.crtc[0x0e] << 8)) & 0x3fff; + uint8_t sl = f82c425->cga.crtc[9] + 1; + int columns = f82c425->cga.crtc[1]; + + sc = (f82c425->displine) & 7; + addr = ((ma & ~1) + (f82c425->displine >> 3) * columns) * 2; + ma += (f82c425->displine >> 3) * columns; + + if ((f82c425->cga.crtc[0x0a] & 0x60) == 0x20) + { + cursorline = 0; + } + else + { + cursorline = ((f82c425->cga.crtc[0x0a] & 0x0F) <= sc) && + ((f82c425->cga.crtc[0x0b] & 0x0F) >= sc); + } + + for (x = 0; x < columns; x++) + { + chr = f82c425->vram[(addr + 2 * x) & 0x3FFF]; + attr = f82c425->vram[(addr + 2 * x + 1) & 0x3FFF]; + drawcursor = ((ma == ca) && cursorline && + (f82c425->cga.cgamode & 0x8) && (f82c425->cga.cgablink & 0x10)); + + blink = ((f82c425->cga.cgablink & 0x10) && (f82c425->cga.cgamode & 0x20) && + (attr & 0x80) && !drawcursor); + + if (drawcursor) + { + colors[0] = smartmap[~attr & 0xff][0]; + colors[1] = smartmap[~attr & 0xff][1]; + } + else + { + colors[0] = smartmap[attr][0]; + colors[1] = smartmap[attr][1]; + } + + if (blink) + colors[1] = colors[0]; + + if (f82c425->cga.cgamode & 0x01) + { + /* High resolution (80 cols) */ + for (c = 0; c < sl; c++) + { + ((uint32_t *)buffer32->line[f82c425->displine])[(x << 3) + c] = + colors[(fontdat[chr][sc] & (1 <<(c ^ 7))) ? 1 : 0]; + } + } + else + { + /* Low resolution (40 columns, stretch pixels horizontally) */ + for (c = 0; c < sl; c++) + { + ((uint32_t *)buffer32->line[f82c425->displine])[(x << 4) + c*2] = + ((uint32_t *)buffer32->line[f82c425->displine])[(x << 4) + c*2+1] = + colors[(fontdat[chr][sc] & (1 <<(c ^ 7))) ? 1 : 0]; + } + } + + ++ma; + } +} + +/* Draw a line in CGA 640x200 mode */ +static void f82c425_cgaline6(f82c425_t *f82c425) +{ + int x, c; + uint8_t dat; + uint16_t addr; + + uint16_t ma = (f82c425->cga.crtc[0x0d] | (f82c425->cga.crtc[0x0c] << 8)) & 0x3fff; + + addr = ((f82c425->displine) & 1) * 0x2000 + + (f82c425->displine >> 1) * 80 + + ((ma & ~1) << 1); + + for (x = 0; x < 80; x++) + { + dat = f82c425->vram[addr & 0x3FFF]; + addr++; + + for (c = 0; c < 8; c++) + { + ((uint32_t *)buffer32->line[f82c425->displine])[x*8+c] = + colormap[dat & 0x80 ? 3 : 0]; + + dat = dat << 1; + } + } +} + +/* Draw a line in CGA 320x200 mode. */ +static void f82c425_cgaline4(f82c425_t *f82c425) +{ + int x, c; + uint8_t dat, pattern; + uint16_t addr; + + uint16_t ma = (f82c425->cga.crtc[0x0d] | (f82c425->cga.crtc[0x0c] << 8)) & 0x3fff; + addr = ((f82c425->displine) & 1) * 0x2000 + + (f82c425->displine >> 1) * 80 + + ((ma & ~1) << 1); + + for (x = 0; x < 80; x++) + { + dat = f82c425->vram[addr & 0x3FFF]; + addr++; + + for (c = 0; c < 4; c++) + { + pattern = (dat & 0xC0) >> 6; + if (!(f82c425->cga.cgamode & 0x08)) pattern = 0; + + ((uint32_t *)buffer32->line[f82c425->displine])[x*8+2*c] = + ((uint32_t *)buffer32->line[f82c425->displine])[x*8+2*c+1] = + colormap[pattern & 3]; + + dat = dat << 2; + } + } +} + +static void f82c425_poll(void *p) +{ + f82c425_t *f82c425 = (f82c425_t *)p; + + if (f82c425->video_options != st_video_options || + !!(f82c425->function & 1) != st_enabled) + { + f82c425->video_options = st_video_options; + f82c425->function &= ~1; + f82c425->function |= st_enabled ? 1 : 0; + + if (f82c425->function & 0x01) + mem_mapping_enable(&f82c425->mapping); + else + mem_mapping_disable(&f82c425->mapping); + } + /* Switch between internal LCD and external CRT display. */ + if (st_display_internal != -1 && st_display_internal != !!(f82c425->function & 0x08)) + { + if (st_display_internal) + { + f82c425->function &= ~0x08; + f82c425->timing &= ~0x20; + } + else + { + f82c425->function |= 0x08; + f82c425->timing |= 0x20; + } + f82c425_recalctimings(f82c425); + } + + if (f82c425->function & 0x08) + { + cga_poll(&f82c425->cga); + return; + } + + if (!f82c425->linepos) + { + timer_advance_u64(&f82c425->cga.timer, f82c425->dispofftime); + f82c425->cga.cgastat |= 1; + f82c425->linepos = 1; + if (f82c425->dispon) + { + if (f82c425->displine == 0) + { + video_wait_for_buffer(); + } + + switch (f82c425->cga.cgamode & 0x13) + { + case 0x12: + f82c425_cgaline6(f82c425); + break; + case 0x02: + f82c425_cgaline4(f82c425); + break; + case 0x00: + case 0x01: + f82c425_text_row(f82c425); + break; + } + } + f82c425->displine++; + + /* Hardcode a fixed refresh rate and VSYNC timing */ + if (f82c425->displine >= 216) + { + /* End of VSYNC */ + f82c425->displine = 0; + f82c425->cga.cgastat &= ~8; + f82c425->dispon = 1; + } + else + if (f82c425->displine == (f82c425->cga.crtc[9] + 1) * f82c425->cga.crtc[6]) + { + /* Start of VSYNC */ + f82c425->cga.cgastat |= 8; + f82c425->dispon = 0; + } + } + else + { + if (f82c425->dispon) + f82c425->cga.cgastat &= ~1; + timer_advance_u64(&f82c425->cga.timer, f82c425->dispontime); + f82c425->linepos = 0; + + if (f82c425->displine == 200) + { + /* Hardcode 640x200 window size */ + if ((F82C425_XSIZE != xsize) || (F82C425_YSIZE != ysize) || video_force_resize_get()) + { + xsize = F82C425_XSIZE; + ysize = F82C425_YSIZE; + set_screen_size(xsize, ysize); + + if (video_force_resize_get()) + video_force_resize_set(0); + } + video_blit_memtoscreen(0, 0, 0, ysize, xsize, ysize); + frames++; + + /* Fixed 640x200 resolution */ + video_res_x = F82C425_XSIZE; + video_res_y = F82C425_YSIZE; + + switch (f82c425->cga.cgamode & 0x12) + { + case 0x12: + video_bpp = 1; + break; + case 0x02: + video_bpp = 2; + break; + default: + video_bpp = 0; + } + + f82c425->cga.cgablink++; + } + } +} + +static void *f82c425_init(const device_t *info) +{ + f82c425_t *f82c425 = malloc(sizeof(f82c425_t)); + memset(f82c425, 0, sizeof(f82c425_t)); + cga_init(&f82c425->cga); + video_inform(VIDEO_FLAG_TYPE_CGA, &timing_f82c425); + + /* Initialize registers that don't default to zero. */ + f82c425->hsync = 0x40; + f82c425->vsync_blink = 0x72; + + /* 16k video RAM */ + f82c425->vram = malloc(0x4000); + + timer_set_callback(&f82c425->cga.timer, f82c425_poll); + timer_set_p(&f82c425->cga.timer, f82c425); + + /* Occupy memory between 0xB8000 and 0xBFFFF */ + mem_mapping_add(&f82c425->mapping, 0xb8000, 0x8000, f82c425_read, NULL, NULL, f82c425_write, NULL, NULL, NULL, 0, f82c425); + /* Respond to CGA I/O ports */ + io_sethandler(0x03d0, 0x000c, f82c425_in, NULL, NULL, f82c425_out, NULL, NULL, f82c425); + + /* Initialize color maps for text & graphic modes */ + f82c425_smartmap(f82c425); + f82c425_colormap(f82c425); + + /* Start off in 80x25 text mode */ + f82c425->cga.cgastat = 0xF4; + f82c425->cga.vram = f82c425->vram; + f82c425->video_options = 0x01; + + return f82c425; +} + +static void f82c425_close(void *p) +{ + f82c425_t *f82c425 = (f82c425_t *)p; + + free(f82c425->vram); + free(f82c425); +} + +static void f82c425_speed_changed(void *p) +{ + f82c425_t *f82c425 = (f82c425_t *)p; + + f82c425_recalctimings(f82c425); +} + +const device_t f82c425_video_device = { + "82C425 CGA LCD/CRT Controller", + 0, 0, + f82c425_init, f82c425_close, NULL, + { NULL }, + f82c425_speed_changed, + NULL, + NULL +}; diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index e050dab7c..d51c3aa30 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -635,7 +635,7 @@ MCHOBJ := machine.o machine_table.o \ m_xt_xi8088.o m_xt_zenith.o \ m_pcjr.o \ m_amstrad.o m_europc.o \ - m_xt_olivetti.o m_tandy.o \ + m_xt_olivetti.o m_tandy.o m_v86p.o \ m_at.o m_at_commodore.o \ m_at_t3100e.o m_at_t3100e_vid.o \ m_ps1.o m_ps1_hdc.o \ @@ -773,6 +773,7 @@ VIDOBJ := video.o \ vid_paradise.o \ vid_rtg310x.o \ vid_ti_cf62011.o \ + vid_f82c425.o \ vid_tvga.o \ vid_tgui9440.o vid_tkd8001_ramdac.o \ vid_att20c49x_ramdac.o \