From 7ed28f32df09faac4d5f09bb785271b6e3211f9a Mon Sep 17 00:00:00 2001 From: OBattler Date: Sat, 16 Aug 2025 02:45:05 +0200 Subject: [PATCH] Ported the Yamaha V6355 from PCem. --- src/include/86box/vid_v6355.h | 72 ++ src/include/86box/video.h | 8 +- src/video/CMakeLists.txt | 1 + src/video/vid_cga_v6355.c | 1177 +++++++++++++++++++++++++++++++++ src/video/vid_table.c | 1 + 5 files changed, 1256 insertions(+), 3 deletions(-) create mode 100644 src/include/86box/vid_v6355.h create mode 100644 src/video/vid_cga_v6355.c diff --git a/src/include/86box/vid_v6355.h b/src/include/86box/vid_v6355.h new file mode 100644 index 000000000..f8fd11519 --- /dev/null +++ b/src/include/86box/vid_v6355.h @@ -0,0 +1,72 @@ +/* + * 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 old and new IBM CGA graphics cards. + * + * Authors: Sarah Walker, + * Miran Grca, + * Connor Hyde / starfrost, + * + * Copyright 2008-2018 Sarah Walker. + * Copyright 2016-2018 Miran Grca. + * Copyright 2025 starfrost (refactoring). + */ + +#ifndef VIDEO_V6355_H +#define VIDEO_V6355_H + +typedef struct v6355_t { + mem_mapping_t mapping; + + uint8_t cgastat; + uint8_t cgamode; + uint8_t cgacol; + + uint8_t pad[3]; + uint8_t crtc[32]; + uint8_t v6355data[106]; + uint8_t charbuffer[256]; + + uint16_t ma; + uint16_t maback; + + /* The V6355 has its own set of registers, as well as the emulated MC6845 */ + int v6355reg; + int crtcreg; + int fontbase; + int linepos; + int displine; + int sc; + int vc; + int cgadispon; + int con; + int coff; + int cursoron; + int cgablink; + int vsynctime; + int vadj; + int oddeven; + int display_type; + int firstline; + int lastline; + int drawcursor; + int revision; + int rgb_type; + int double_type; + + uint32_t v6355pal[16]; + + uint64_t dispontime; + uint64_t dispofftime; + + pc_timer_t timer; + + uint8_t * vram; +} v6355_t; + +#endif /*VIDEO_V6355_H*/ diff --git a/src/include/86box/video.h b/src/include/86box/video.h index b991fa767..a9e21acc8 100644 --- a/src/include/86box/video.h +++ b/src/include/86box/video.h @@ -401,11 +401,10 @@ extern const device_t gd5446_pci_device; extern const device_t gd5446_stb_pci_device; extern const device_t gd5480_pci_device; - -/* IBM CGA*/ +/* IBM CGA */ extern const device_t cga_device; -/* pravetz CGA */ +/* Pravetz CGA */ extern const device_t cga_pravetz_device; /* Compaq CGA */ @@ -630,6 +629,9 @@ extern const device_t velocity_200_agp_device; /* Wyse 700 */ extern const device_t wy700_device; +/* Yamaha V6355 */ +extern const device_t v6355d_device; + /* Tandy */ extern const device_t tandy_1000_video_device; extern const device_t tandy_1000hx_video_device; diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index 1159c93e2..f34455136 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -58,6 +58,7 @@ add_library(vid OBJECT vid_cga_quadcolor.c vid_cga_toshiba_t1000.c vid_cga_toshiba_t3100e.c + vid_cga_v6355.c # PCJr/Tandy vid_pcjr.c diff --git a/src/video/vid_cga_v6355.c b/src/video/vid_cga_v6355.c new file mode 100644 index 000000000..c95a96739 --- /dev/null +++ b/src/video/vid_cga_v6355.c @@ -0,0 +1,1177 @@ +/* + * 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 Yamaha V6355 graphics card. + * + * Authors: Sarah Walker, + * John Elliott, + * Miran Grca, + * W. M. Martinez, + * + * Copyright 2008-2025 Sarah Walker. + * Copyright 2025 John Elliott. + * Copyright 2016-2025 Miran Grca. + * Copyright 2023-2025 W. M. Martinez + */ +#include +#include +#include +#include +#include +#include +#include <86box/86box.h> +#include "cpu.h" +#include <86box/io.h> +#include <86box/timer.h> +#include <86box/pit.h> +#include <86box/mem.h> +#include <86box/rom.h> +#include <86box/device.h> +#include <86box/video.h> +#include <86box/vid_v6355.h> +#include <86box/vid_cga_comp.h> +#include <86box/plat_unused.h> + +/* Emulation of the Yamaha V6355 chipset. This is a CGA clone that was + * probably designed primarily for laptops, where the primary display was + * a fixed-resolution LCD panel and there was the option of connecting to + * an external CGA monitor or PAL/SECAM television. + * + * Consequently, unlike a real CGA, it doesn't implement the first ten 6845 + * registers; instead, a small number of fixed resolutions can be selected + * using the V6355's own registers at 0x3DD / 0x3DF. Width is either 512 or 640 + * pixels; height is 64, 192, 200 or 204 pixels. + * + * Other features include: + * - MDA attribute support + * - Palette support - mapping from RGBI colour to 9-bit rrrgggbbb colours + * (when output is to composite) + * - Hardware mouse pointer support + * + * Outline of the V6355's extra registers, accessed through ports 0x3DD (index) + * and 0x3DE (data): + * + * 0x00-0x1F: Mouse pointer AND mask + * 0x20-0x3F: Mouse pointer XOR mask + * 0x40-0x5F: Palette for composite output. 0r,gb,0r,gb,0r,gb etc. + * 0x60-0x61: Mouse pointer X (big-endian, in 320x200 coordinates) + * 0x62: Not used (would be high byte of mouse pointer Y) + * 0x63: Mouse pointer Y + * 0x64: Mouse pointer visibility & vertical adjustment + * 0x65: Screen height & width, display type, RAM type + * 0x66: LCD adjust, MDA attribute emulation + * 0x67: Horizontal adjustment, other configuration + * 0x68: Mouse pointer colour + * 0x69: Control data register (not well documented) + * + * Currently unimplemented: + * > Display type (PAL/SECAM @50Hz vs NTSC @60Hz) + * > MDA monitor support + * > LCD panel support + * > Horizontal / vertical position adjustments + * > 160x200x16 and 640x200x16 video modes. Documentation suggests that these + * should be selected by setting bit 6 of the CGA control register, but + * that doesn't work on my real hardware, so I can't test and therefore + * can't replicate + * > Palette support on composite output. Composite_Process() does not + * appear to have any support for an arbitrary palette. + */ + +#define V6355_RGB 0 +#define V6355_COMPOSITE 1 +#define V6355_TRUECOLOR 2 + +#define COMPOSITE_OLD 0 +#define COMPOSITE_NEW 1 + +#define DOUBLE_NONE 0 +#define DOUBLE_SIMPLE 1 +#define DOUBLE_INTERPOLATE_SRGB 2 +#define DOUBLE_INTERPOLATE_LINEAR 3 + +#define DEVICE_VRAM 0x4000 +#define DEVICE_VRAM_MASK 0x3fff + +typedef union { + uint32_t color; + struct { + uint8_t b; + uint8_t g; + uint8_t r; + uint8_t a; + }; +} color_t; + +static uint8_t crtcmask[32] = { + 0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x7f, 0x7f, 0xf3, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static uint8_t interp_lut[2][256][256]; + +static video_timings_t timing_v6355 = { .type = VIDEO_ISA, .write_b = 8, .write_w = 16, .write_l = 32, .read_b = 8, .read_w = 16, .read_l = 32 }; + +static uint8_t mdamap[256][2][2]; + +/* Default values for palette registers */ +static uint8_t defpalette[32] = { + 0x00, 0x00, /* Black */ + 0x00, 0x04, /* Blue */ + 0x00, 0x40, /* Green */ + 0x00, 0x44, /* Cyan */ + 0x04, 0x00, /* Red */ + 0x04, 0x04, /* Magenta */ + 0x04, 0x40, /* Yellow */ + 0x04, 0x44, /* Light grey */ + 0x01, 0x11, /* Dark grey */ + 0x00, 0x06, /* Bright blue */ + 0x00, 0x60, /* Bright green */ + 0x00, 0x66, /* Bright cyan */ + 0x06, 0x00, /* Bright red */ + 0x06, 0x06, /* Bright magenta */ + 0x06, 0x60, /* Bright yellow */ + 0x07, 0x77, /* Bright white */ +}; + +static void v6355_recalctimings(v6355_t *v6355); + +static void +v6355_out(uint16_t addr, uint8_t val, void *priv) +{ + v6355_t *v6355 = (v6355_t *) priv; + uint8_t old; + + switch (addr) { + case 0x3d0: + case 0x3d2: + case 0x3d4: + case 0x3d6: + v6355->crtcreg = val & 31; + break; + case 0x3d1: + case 0x3d3: + case 0x3d5: + case 0x3d7: + old = v6355->crtc[v6355->crtcreg]; + v6355->crtc[v6355->crtcreg] = val & crtcmask[v6355->crtcreg]; + if (old != val) { + if (v6355->crtcreg < 0xe || v6355->crtcreg > 0x10) + v6355_recalctimings(v6355); + } + break; + case 0x3d8: + if (((v6355->cgamode ^ val) & 5) != 0) { + v6355->cgamode = val; + update_cga16_color(v6355->cgamode); + } + v6355->cgamode = val; + break; + case 0x3d9: + v6355->cgacol = val; + break; + case 0x3dd: + v6355->v6355reg = val; + break; + case 0x3de: + v6355->v6355data[v6355->v6355reg] = val; + + /* Writes in the 0x40-0x5F range update the palette */ + if (v6355->v6355reg >= 0x40 && v6355->v6355reg < 0x60) { + int r = (v6355->v6355data[v6355->v6355reg & 0xFE]) & 7; + int g = (v6355->v6355data[v6355->v6355reg | 0x01] >> 4) & 7; + int b = (v6355->v6355data[v6355->v6355reg | 0x01]) & 7; + v6355->v6355pal[(v6355->v6355reg - 0x40) / 2] = + makecol(r * 0xFF / 7, g * 0xFF / 7, b * 0xFF / 7); + } + + /* Register autoincrements after a write. */ + v6355->v6355reg = (v6355->v6355reg + 1) % sizeof(v6355->v6355data); + break; + case 0x3df: + /* Supposedly used for memory paging in 16-colour mode, but + * I've found no documentation to explain how */ + break; + } +} + +static uint8_t +v6355_in(uint16_t addr, void *priv) +{ + v6355_t *v6355 = (v6355_t *) priv; + uint8_t ret = 0xff; + + switch (addr) { + case 0x3d4: + ret = v6355->crtcreg; + break; + case 0x3d5: + ret = v6355->crtc[v6355->crtcreg]; + break; + case 0x3da: + ret = v6355->cgastat; + break; + } + + return ret; +} + +static void +v6355_write(uint32_t addr, uint8_t val, void *priv) +{ + v6355_t *v6355 = (v6355_t *) priv; + + v6355->vram[addr & 0x3fff] = val; + + cycles -= 4; +} + +static uint8_t +v6355_read(uint32_t addr, void *priv) +{ + v6355_t *v6355 = (v6355_t *) priv; + + cycles -= 4; + + return v6355->vram[addr & 0x3fff]; +} + +/* Get width of display area (always 512px or 640px) */ +static uint32_t +v6355_width(v6355_t *v6355) +{ + return (v6355->v6355data[0x65] & 4) ? 512 : 640; +} + +/* Get height of display area (192px, 200px, 204px or 64px) */ +static uint32_t +v6355_height(v6355_t *v6355) +{ + static const unsigned heights[4] = { 192, 200, 204, 64 }; + + return heights[v6355->v6355data[0x65] & 3]; +} + +/* Timings on a V6355 are largely fixed */ +static void +v6355_recalctimings(v6355_t *v6355) +{ + double disptime; + double _dispontime, _dispofftime; +#ifndef USE_CGA_TIMINGS + double crtcconst = (cpuclock / 21477270.0 * (double) (1ULL << 32)) * 8.0; +#endif + + uint32_t w = v6355_width(v6355); + + disptime = w + 33; + _dispontime = w; + _dispofftime = disptime - _dispontime; +#ifdef USE_CGA_TIMINGS + _dispontime *= CGACONST; + _dispofftime *= CGACONST; +#else + _dispontime *= crtcconst; + _dispofftime *= crtcconst; +#endif + v6355->dispontime = (uint64_t)_dispontime; + v6355->dispofftime = (uint64_t)_dispofftime; +} + +/* Overlay the pointer on a line of the display. pixel[] is an array of 640 + * IBGR values containing the pixels to draw onto */ +static void +v6355_pointer(v6355_t *v6355, uint8_t *pixel) +{ + int c, pxc; + int y = v6355->displine - v6355->firstline; + + /* The pointer coordinates are on a 336x216 grid, with (16,16) being + * the top left-hand corner of the visible area */ + int pointer_x = (v6355->v6355data[0x60] << 8) | (v6355->v6355data[0x61]); + int pointer_y = v6355->v6355data[0x63]; + uint8_t mc; + uint8_t mand; + uint8_t mxor; + uint8_t mflags; + + /* Mouse drawing options */ + mflags = v6355->v6355data[0x64]; + + /* If the pointer is blinking and not currently shown, don't draw it */ + if ((v6355->cgablink & 8) && (mflags & 1)) + return; + + /* If this line doesn't intersect the pointer, nothing to do */ + if ((y < (pointer_y - 16)) || (y >= pointer_y)) + return; + + y -= (pointer_y - 16); + + /* Get mouse AND and XOR masks */ + mand = v6355->v6355data[0x68] & 0x0F; + mxor = (v6355->v6355data[0x68] >> 4) & 0x0F; + + /* Draw up to 16 double-width pixels */ + for (c = 0; c < 32; c++) { + mc = 0x80 >> ((c & 0x0E) >> 1); + pxc = c + 2 * (pointer_x - 16); + + if (pxc < 0 || pxc >= 640) + /* X clipping */ + continue; + + if (mflags & 2) { + /* Apply AND mask? */ + if (v6355->v6355data[y * 2 + c / 16] & mc) + pixel[pxc] &= mand; + } + + if (mflags & 4) { + /* Apply XOR mask? */ + if (v6355->v6355data[y * 2 + c / 16 + 32] & mc) + pixel[pxc] ^= mxor; + } + } +} + +/* Convert attribute byte to CGA colours */ +static void +v6355_map_attrs(v6355_t *v6355, uint8_t chr, uint8_t attr, uint8_t *cols) +{ + if (v6355->v6355data[0x66] & 0x40) { + /* MDA-style attributes */ + int blink = (v6355->cgamode & 0x20) && (v6355->cgablink & 8) && (attr & 0x80); + + cols[1] = mdamap[attr][blink][1]; + cols[0] = mdamap[attr][blink][0]; + } else { + /* CGA attributes (blinking enabled) */ + if (v6355->cgamode & 0x20) { + cols[1] = attr & 15; + cols[0] = (attr >> 4) & 7; + if ((v6355->cgablink & 8) && (attr & 0x80) && !v6355->drawcursor) + cols[1] = cols[0]; + } else { + /* CGA attributes (blinking disabled) */ + cols[1] = attr & 15; + cols[0] = attr >> 4; + } + } +} + +/* Render a line as 640 pixels in 80-column text mode */ +static void +v6355_line_text80(v6355_t *v6355, uint8_t *pixel, uint16_t ca) +{ + int32_t x, c; + uint8_t chr, attr; + uint32_t w = v6355_width(v6355) / 8; + uint8_t cols[2]; + + for (x = 0; x < w; x++) { + if (v6355->cgamode & 8) { + chr = v6355->charbuffer[x << 1]; + attr = v6355->charbuffer[(x << 1) + 1]; + } else + chr = attr = 0; + + v6355_map_attrs(v6355, chr, attr, cols); + + for (c = 0; c < 8; c++) { + uint8_t data = fontdat[chr + v6355->fontbase][v6355->sc & 7]; + + /* Underline attribute if enabled */ + if ((v6355->v6355data[0x66] & 0x80) && ((attr & 7) == 1) && ((v6355->sc & 7) == 7)) + data = 0xff; + + pixel[(x << 3) + c] = cols[(data & (1 << (c ^ 7))) ? 1 : 0]; + } + } +} + +/* Render a line as 640 pixels in 40-column text mode */ +static void +v6355_line_text40(v6355_t *v6355, uint8_t *pixel, uint16_t ca) +{ + int32_t x, c; + uint8_t chr, attr; + uint32_t w = v6355_width(v6355) / 16; + uint8_t cols[2]; + + for (x = 0; x < w; x++) { + if (v6355->cgamode & 8) { + chr = v6355->vram[((v6355->ma + x) << 1) & 0x3fff]; + attr = v6355->vram[(((v6355->ma + x) << 1) + 1) & 0x3fff]; + } else + chr = attr = 0; + + v6355_map_attrs(v6355, chr, attr, cols); + + for (c = 0; c < 8; c++) { + uint8_t data = fontdat[chr + v6355->fontbase][v6355->sc & 7]; + + /* Underline attribute if enabled */ + if ((v6355->v6355data[0x66] & 0x80) && ((attr & 7) == 1) && ((v6355->sc & 7) == 7)) + data = 0xff; + + pixel[(x << 4) + (c << 1)] = pixel[(x << 4) + (c << 1) + 1] = + cols[(data & (1 << (c ^ 7))) ? 1 : 0]; + } + } +} + +/* Render a line as 640 pixels in 320-pixel graphics mode */ +static void +v6355_line_graphics320(v6355_t *v6355, uint8_t *pixel) +{ + int32_t x; + int32_t c; + uint8_t cols[4]; + uint8_t intensity; + uint16_t dat; + uint32_t width = v6355_width(v6355) / 16; + + cols[0] = v6355->cgacol & 15; + + intensity = (v6355->cgacol & 16) ? 8 : 0; + + if (v6355->cgamode & 4) { + cols[1] = intensity | 3; + cols[2] = intensity | 4; + cols[3] = intensity | 7; + } else if (v6355->cgacol & 32) { + cols[1] = intensity | 3; + cols[2] = intensity | 5; + cols[3] = intensity | 7; + } else { + cols[1] = intensity | 2; + cols[2] = intensity | 4; + cols[3] = intensity | 6; + } + + for (x = 0; x < width; x++) { + if (v6355->cgamode & 8) + dat = (v6355->vram[((v6355->ma << 1) & 0x1fff) + ((v6355->sc & 1) * 0x2000)] << 8) | + v6355->vram[((v6355->ma << 1) & 0x1fff) + ((v6355->sc & 1) * 0x2000) + 1]; + else + dat = 0; + + v6355->ma++; + + for (c = 0; c < 8; c++) { + pixel[(x << 4) + (c << 1)] = pixel[(x << 4) + (c << 1) + 1] = cols[dat >> 14]; + dat <<= 2; + } + } +} + +/* Render a line as 640 pixels in 640-pixel graphics mode */ +static void +v6355_line_graphics640(v6355_t *v6355, uint8_t *pixel) +{ + int32_t x; + int32_t c; + uint8_t cols[2]; + uint16_t dat; + uint32_t width = v6355_width(v6355) / 16; + + cols[0] = 0; + cols[1] = v6355->cgacol & 15; + + for (x = 0; x < width; x++) { + if (v6355->cgamode & 8) + dat = (v6355->vram[((v6355->ma << 1) & 0x1fff) + ((v6355->sc & 1) * 0x2000)] << 8) | + v6355->vram[((v6355->ma << 1) & 0x1fff) + ((v6355->sc & 1) * 0x2000) + 1]; + else + dat = 0; + + v6355->ma++; + + for (c = 0; c < 16; c++) { + pixel[(x << 4) + c] = cols[dat >> 15]; + dat <<= 1; + } + } +} + +static void +v6355_render(v6355_t *v6355, int line) +{ + uint16_t ca = (v6355->crtc[15] | (v6355->crtc[14] << 8)) & 0x3fff; + int width = v6355_width(v6355); + int c; + int x; + int drawcursor; + uint32_t cols[4]; + uint8_t pixel[640]; + + /* Draw border */ + cols[0] = ((v6355->cgamode & 0x12) == 0x12) ? 0 : (v6355->cgacol & 15); + + for (c = 0; c < 8; c++) { + ((uint32_t *) buffer32->line[line])[c] = cols[0]; + ((uint32_t *) buffer32->line[line])[c + width + 8] = cols[0]; + } + + /* Render screen data. */ + if (v6355->cgamode & 1) { + /* High-res text */ + v6355_line_text80(v6355, pixel, ca); + v6355_pointer(v6355, pixel); + + for (x = 0; x < (width / 8); x++) { + drawcursor = ((v6355->ma == ca) && v6355->con && v6355->cursoron); + if (drawcursor) { + for (c = 0; c < 8; c++) + ((uint32_t *) buffer32->line[line])[(x << 3) + c + 8] = pixel[(x << 3) + c] ^ 0xffffff; + } else { + for (c = 0; c < 8; c++) + ((uint32_t *)buffer32->line[line])[(x << 3) + c + 8] = pixel[(x << 3) + c]; + } + + v6355->ma++; + } + } else if (!(v6355->cgamode & 2)) { + /* Low-res text */ + v6355_line_text40(v6355, pixel, ca); + v6355_pointer(v6355, pixel); + + for (x = 0; x < (width / 16); x++) { + drawcursor = ((v6355->ma == ca) && v6355->con && v6355->cursoron); + if (drawcursor) { + for (c = 0; c < 16; c++) + ((uint32_t *) buffer32->line[line])[(x << 4) + c + 8] = pixel[(x << 4) + c] ^ 0xffffff; + } else { + for (c = 0; c < 16; c++) + ((uint32_t *)buffer32->line[line])[(x << 4) + c + 8] = pixel[(x << 4) + c]; + } + + v6355->ma++; + } + } else if (!(v6355->cgamode & 16)) { + /* Low-res graphics + * XXX There should be a branch for 160x200x16 graphics somewhere around here */ + v6355_line_graphics320(v6355, pixel); + v6355_pointer(v6355, pixel); + + for (x = 0; x < (width / 16); x++) { + for (c = 0; c < 16; c++) + ((uint32_t *) buffer32->line[line])[(x << 4) + c + 8] = pixel[(x << 4) + c]; + } + } else { + /* High-res graphics + * XXX There should be a branch for 640x200x16 graphics somewhere around here */ + v6355_line_graphics640(v6355, pixel); + v6355_pointer(v6355, pixel); + + for (x = 0; x < (width / 16); x++) { + for (c = 0; c < 16; c++) + ((uint32_t *) buffer32->line[line])[(x << 4) + c + 8] = pixel[(x << 4) + c]; + } + } +} + +static void +v6355_render_blank(v6355_t *v6355, int line) +{ + int width = v6355_width(v6355); + uint32_t cols[4]; + + cols[0] = ((v6355->cgamode & 0x12) == 0x12) ? 0 : (v6355->cgacol & 15); + hline(buffer32, 0, line, width + 16, cols[0]); +} + +static void +v6355_render_process(v6355_t *v6355, int line) +{ + int c; + uint8_t border; + int width = v6355_width(v6355); + int x = width + 16; + + /* Now render the 640 pixels to the display buffer */ + switch (v6355->display_type) { + /* XXX V6355_COMPOSITE can't use the V6355's palette registers */ + case V6355_COMPOSITE: + for (c = 0; c < x; c++) + buffer32->line[line][c] = ((uint32_t *) buffer32->line[line])[c] & 0xf; + + border = ((v6355->cgamode & 0x12) == 0x12) ? 0 : (v6355->cgacol & 15); + + Composite_Process(v6355->cgamode, border, (width + 16) >> 2, buffer32->line[line]); + break; + case V6355_TRUECOLOR: + /* V6355_TRUECOLOR is a fictitious display that behaves like RGB except it + * takes account of the V6355's palette registers */ + for (c = 0; c < x; c++) + ((uint32_t *) buffer32->line[line])[c] = v6355->v6355pal[((uint32_t *) buffer32->line[line])[c] & 0xf]; + break; + default: + video_process_8(width + 16, line); + break; + } +} + +static uint8_t +v6355_interpolate_srgb(uint8_t co1, uint8_t co2, double fraction) +{ + uint8_t ret = ((co2 - co1) * fraction + co1); + + return ret; +} + +static uint8_t +v6355_interpolate_linear(uint8_t co1, uint8_t co2, double fraction) +{ + double c1, c2; + double r1, r2; + uint8_t ret; + + c1 = ((double) co1) / 255.0; + c1 = pow((co1 >= 0) ? c1 : -c1, 2.19921875); + if (co1 <= 0) + c1 = -c1; + c2 = ((double) co2) / 255.0; + c2 = pow((co2 >= 0) ? c2 : -c2, 2.19921875); + if (co2 <= 0) + c2 = -c2; + r1 = ((c2 - c1) * fraction + c1); + r2 = pow((r1 >= 0.0) ? r1 : -r1, 1.0 / 2.19921875); + if (r1 <= 0.0) + r2 = -r2; + ret = (uint8_t) round(r2 * 255.0); + + return ret; +} + +static color_t +v6355_interpolate_lookup(v6355_t *v6355, color_t color1, color_t color2, UNUSED(double fraction)) +{ + color_t ret; + uint8_t dt = v6355->double_type - DOUBLE_INTERPOLATE_SRGB; + + ret.a = 0x00; + ret.r = interp_lut[dt][color1.r][color2.r]; + ret.g = interp_lut[dt][color1.g][color2.g]; + ret.b = interp_lut[dt][color1.b][color2.b]; + + return ret; +} + +static void +v6355_interpolate(v6355_t *v6355, int x, int y, int w, int h) +{ + double quotient = 0.5; + + for (int i = y; i < (y + h); i++) { + if (i & 1) for (int j = x; j < (x + w); j++) { + int prev = i - 1; + int next = i + 1; + color_t prev_color, next_color; + color_t black; + color_t interim_1, interim_2; + color_t final; + + if (i < 0) + continue; + + black.color = 0x00000000; + + if ((prev >= 0) && (prev < (y + h))) + prev_color.color = buffer32->line[prev][j]; + else + prev_color.color = 0x00000000; + + if ((next >= 0) && (next < (y + h))) + next_color.color = buffer32->line[next][j]; + else + next_color.color = 0x00000000; + + interim_1 = v6355_interpolate_lookup(v6355, prev_color, black, quotient); + interim_2 = v6355_interpolate_lookup(v6355, black, next_color, quotient); + final = v6355_interpolate_lookup(v6355, interim_1, interim_2, quotient); + + buffer32->line[i][j] = final.color; + } + } +} + +static void +v6355_blit_memtoscreen(v6355_t *v6355, int x, int y, int w, int h) +{ + if (v6355->double_type > DOUBLE_SIMPLE) + v6355_interpolate(v6355, x, y, w, h); + + video_blit_memtoscreen(x, y, w, h); +} + +static void +v6355_poll(void *priv) +{ + v6355_t *v6355 = (v6355_t *) priv; + int x; + int oldvc; + int oldsc; + int xs_temp; + int ys_temp; + int old_ma; + + int width = v6355_width(v6355); + int height = v6355_height(v6355); + + /* Simulated CRTC height registers */ + int crtc4, crtc5, crtc6, crtc7, crtc8, crtc9; + + if (!(v6355->cgamode & 2)) { + /* Text mode values */ + crtc4 = (height + 54) / 8; + crtc5 = 6; + crtc6 = height / 8; + crtc7 = (height + 24) / 8; + crtc8 = 2; + crtc9 = 7; + } else { + /* Graphics mode values */ + crtc4 = (height + 54) / 2; + crtc5 = 6; + crtc6 = height / 2; + crtc7 = (height + 24) / 2; + crtc8 = 2; + crtc9 = 1; + } + + if (!v6355->linepos) { + timer_advance_u64(&v6355->timer, v6355->dispofftime); + + v6355->cgastat |= 1; + v6355->linepos = 1; + + oldsc = v6355->sc; + + if ((crtc8 & 3) == 3) + v6355->sc = ((v6355->sc << 1) + v6355->oddeven) & 7; + + if (v6355->cgadispon) { + if (v6355->displine < v6355->firstline) { + v6355->firstline = v6355->displine; + video_wait_for_buffer(); + } + + v6355->lastline = v6355->displine; + + switch (v6355->double_type) { + default: + v6355_render(v6355, v6355->displine << 1); + v6355_render_blank(v6355, (v6355->displine << 1) + 1); + break; + case DOUBLE_NONE: + v6355_render(v6355, v6355->displine); + break; + case DOUBLE_SIMPLE: + old_ma = v6355->ma; + v6355_render(v6355, v6355->displine << 1); + v6355->ma = old_ma; + v6355_render(v6355, (v6355->displine << 1) + 1); + break; + } + } else { + switch (v6355->double_type) { + default: + v6355_render_blank(v6355, v6355->displine << 1); + break; + case DOUBLE_NONE: + v6355_render_blank(v6355, v6355->displine); + break; + case DOUBLE_SIMPLE: + v6355_render_blank(v6355, v6355->displine << 1); + v6355_render_blank(v6355, (v6355->displine << 1) + 1); + break; + } + } + + switch (v6355->double_type) { + default: + v6355_render_process(v6355, v6355->displine << 1); + v6355_render_process(v6355, (v6355->displine << 1) + 1); + break; + case DOUBLE_NONE: + v6355_render_process(v6355, v6355->displine); + break; + } + + v6355->sc = oldsc; + + if (v6355->vc == crtc7 && !v6355->sc) + v6355->cgastat |= 8; + + v6355->displine++; + + if (v6355->displine >= 360) + v6355->displine = 0; + } else { + timer_advance_u64(&v6355->timer, v6355->dispontime); + + v6355->linepos = 0; + + if (v6355->vsynctime) { + v6355->vsynctime--; + + if (!v6355->vsynctime) + v6355->cgastat &= ~8; + } + + if (v6355->sc == (v6355->crtc[11] & 31) || ((crtc8 & 3) == 3 && v6355->sc == ((v6355->crtc[11] & 31) >> 1))) { + v6355->con = 0; + v6355->coff = 1; + } + + if ((crtc8 & 3) == 3 && v6355->sc == (crtc9 >> 1)) + v6355->maback = v6355->ma; + + if (v6355->vadj) { + v6355->sc++; + v6355->sc &= 31; + v6355->ma = v6355->maback; + v6355->vadj--; + + if (!v6355->vadj) { + v6355->cgadispon = 1; + v6355->ma = v6355->maback = (v6355->crtc[13] | (v6355->crtc[12] << 8)) & 0x3fff; + v6355->sc = 0; + } + } else if (v6355->sc == crtc9) { + v6355->maback = v6355->ma; + v6355->sc = 0; + oldvc = v6355->vc; + v6355->vc++; + v6355->vc &= 127; + + if (v6355->vc == crtc6) + v6355->cgadispon = 0; + + if (oldvc == crtc4) { + v6355->vc = 0; + v6355->vadj = crtc5; + + if (!v6355->vadj) + v6355->cgadispon = 1; + + if (!v6355->vadj) + v6355->ma = v6355->maback = (v6355->crtc[13] | (v6355->crtc[12] << 8)) & 0x3fff; + + if ((v6355->crtc[10] & 0x60) == 0x20) + v6355->cursoron = 0; + else + v6355->cursoron = v6355->cgablink & 8; + } + + if (v6355->vc == crtc7) { + v6355->cgadispon = 0; + v6355->displine = 0; + v6355->vsynctime = 16; + if (crtc7) { + x = width + 16; + v6355->lastline++; + + xs_temp = x; + ys_temp = v6355->lastline - v6355->firstline; + if (v6355->double_type > DOUBLE_NONE) + ys_temp <<= 1; + + if ((xs_temp > 0) && (ys_temp > 0)) { + if (xs_temp < 64) + xs_temp = 656; + if (ys_temp < 32) + ys_temp = 200; + if (!enable_overscan) + xs_temp -= 16; + + if (((xs_temp != xsize) || (ys_temp != ysize) || video_force_resize_get())) { + xsize = xs_temp; + ysize = ys_temp; + if (v6355->double_type > DOUBLE_NONE) + set_screen_size(xsize, ysize + (enable_overscan ? 16 : 0)); + else + set_screen_size(xsize, ysize + (enable_overscan ? 8 : 0)); + + if (video_force_resize_get()) + video_force_resize_set(0); + } + + if (v6355->double_type > DOUBLE_NONE) { + if (enable_overscan) + v6355_blit_memtoscreen(v6355, 0, (v6355->firstline - 4) << 1, + xsize, ((v6355->lastline - v6355->firstline) << 1) + 16); + else + v6355_blit_memtoscreen(v6355, 8, v6355->firstline << 1, + xsize, (v6355->lastline - v6355->firstline) << 1); + } else { + if (enable_overscan) + video_blit_memtoscreen(0, v6355->firstline - 4, + xsize, (v6355->lastline - v6355->firstline) + 8); + else + video_blit_memtoscreen(8, v6355->firstline, + xsize, v6355->lastline - v6355->firstline); + } + } + + frames++; + + video_res_x = xsize - 16; + video_res_y = ysize; + if (v6355->cgamode & 1) { + video_res_x /= 8; + video_res_y /= crtc9 + 1; + video_bpp = 0; + } else if (!(v6355->cgamode & 2)) { + video_res_x /= 16; + video_res_y /= crtc9 + 1; + video_bpp = 0; + } else if (!(v6355->cgamode & 16)) { + video_res_x /= 2; + video_bpp = 2; + } else + video_bpp = 1; + } + + v6355->firstline = 1000; + v6355->lastline = 0; + v6355->cgablink++; + v6355->oddeven ^= 1; + } + } else { + v6355->sc++; + v6355->sc &= 31; + v6355->ma = v6355->maback; + } + + if (v6355->cgadispon) + v6355->cgastat &= ~1; + + if ((v6355->sc == (v6355->crtc[10] & 31) || ((crtc8 & 3) == 3 && v6355->sc == ((v6355->crtc[10] & 31) >> 1)))) + v6355->con = 1; + + if (v6355->cgadispon && (v6355->cgamode & 1)) { + for (x = 0; x < ((width / 8) * 2); x++) + v6355->charbuffer[x] = v6355->vram[(((v6355->ma << 1) + x) & 0x3fff)]; + } + } +} + +static void * +v6355_standalone_init(const device_t *info) { + int n; + int c; + v6355_t *v6355 = calloc(1, sizeof(v6355_t)); + + video_inform(VIDEO_FLAG_TYPE_CGA, &timing_v6355); + + v6355->display_type = device_get_config_int("display_type"); + + overscan_x = overscan_y = 16; + + /* Initialise the palette registers to default values */ + memcpy(v6355->v6355data + 0x40, defpalette, 0x20); + + for (n = 0; n < 16; n++) { + int r = (v6355->v6355data[0x40 + 2 * n]) & 7; + int g = (v6355->v6355data[0x41 + 2 * n] >> 4) & 7; + int b = (v6355->v6355data[0x41 + 2 * n]) & 7; + + v6355->v6355pal[n] = makecol((r * 0xff) / 7, (g * 0xff) / 7, (b * 0xff) / 7); + } + + /* Default to 200 lines */ + v6355->v6355data[0x65] = 0x01; + + /* Set up CGA -> MDA attribute mapping */ + for (c = 0; c < 256; c++) { + mdamap[c][0][0] = mdamap[c][1][0] = mdamap[c][1][1] = 0; + + if (c & 8) + mdamap[c][0][1] = 0xf; + else + mdamap[c][0][1] = 0x7; + } + + mdamap[0x70][0][1] = 0; + mdamap[0x70][0][0] = mdamap[0x70][1][0] = mdamap[0x70][1][1] = 0xf; + mdamap[0xF0][0][1] = 0; + mdamap[0xF0][0][0] = mdamap[0xF0][1][0] = mdamap[0xF0][1][1] = 0xf; + mdamap[0x78][0][1] = 7; + mdamap[0x78][0][0] = mdamap[0x78][1][0] = mdamap[0x78][1][1] = 0xf; + mdamap[0xF8][0][1] = 7; + mdamap[0xF8][0][0] = mdamap[0xF8][1][0] = mdamap[0xF8][1][1] = 0xf; + mdamap[0x00][0][1] = mdamap[0x00][1][1] = 0; + mdamap[0x08][0][1] = mdamap[0x08][1][1] = 0; + mdamap[0x80][0][1] = mdamap[0x80][1][1] = 0; + mdamap[0x88][0][1] = mdamap[0x88][1][1] = 0; + + v6355->display_type = device_get_config_int("display_type"); + v6355->revision = device_get_config_int("composite_type"); + + v6355->vram = malloc(0x4000); + + cga_comp_init(v6355->revision); + + timer_add(&v6355->timer, v6355_poll, v6355, 1); + + mem_mapping_add(&v6355->mapping, 0xb8000, 0x08000, + v6355_read, NULL, NULL, v6355_write, NULL, NULL, NULL, + MEM_MAPPING_EXTERNAL, v6355); + + io_sethandler(0x03d0, 0x0010, + v6355_in, NULL, NULL, v6355_out, NULL, NULL, + v6355); + + v6355->rgb_type = device_get_config_int("rgb_type"); + cga_palette = (v6355->rgb_type << 1); + cgapal_rebuild(); + update_cga16_color(v6355->cgamode); + + v6355->double_type = device_get_config_int("double_type"); + + for (uint16_t i = 0; i < 256; i++) { + for (uint16_t j = 0; j < 256; j++) { + interp_lut[0][i][j] = v6355_interpolate_srgb(i, j, 0.5); + interp_lut[1][i][j] = v6355_interpolate_linear(i, j, 0.5); + } + } + + switch(device_get_config_int("font")) { + case 0: + loadfont(FONT_IBM_MDA_437_PATH, 0); + break; + case 1: + loadfont(FONT_IBM_MDA_437_NORDIC_PATH, 0); + break; + case 4: + loadfont(FONT_TULIP_DGA_PATH, 0); + break; + } + + return v6355; +} + +static void +v6355_close(void *priv) { + v6355_t *v6355 = (v6355_t *) priv; + + free(v6355->vram); + free(v6355); +} + +static void +v6355_speed_changed(void *priv) { + v6355_t *v6355 = (v6355_t *) priv; + + v6355_recalctimings(v6355); +} + +// clang-format off +const device_config_t v6355_config[] = { + { + .name = "display_type", + .description = "Display type", + .type = CONFIG_SELECTION, + .default_string = NULL, + .default_int = V6355_RGB, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { + { .description = "RGB", .value = V6355_RGB }, + { .description = "Composite", .value = V6355_COMPOSITE }, + { .description = "True color", .value = V6355_TRUECOLOR }, + { .description = "" } + }, + .bios = { { 0 } } + }, + { + .name = "composite_type", + .description = "Composite type", + .type = CONFIG_SELECTION, + .default_string = NULL, + .default_int = COMPOSITE_OLD, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { + { .description = "Old", .value = COMPOSITE_OLD }, + { .description = "New", .value = COMPOSITE_NEW }, + { .description = "" } + }, + .bios = { { 0 } } + }, + { + .name = "rgb_type", + .description = "RGB type", + .type = CONFIG_SELECTION, + .default_string = NULL, + .default_int = 5, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { + { .description = "Color (generic)", .value = 0 }, + { .description = "Green Monochrome", .value = 1 }, + { .description = "Amber Monochrome", .value = 2 }, + { .description = "Gray Monochrome", .value = 3 }, + { .description = "Color (no brown)", .value = 4 }, + { .description = "Color (IBM 5153)", .value = 5 }, + { .description = "" } + }, + .bios = { { 0 } } + }, + { + .name = "double_type", + .description = "Line doubling type", + .type = CONFIG_SELECTION, + .default_string = NULL, + .default_int = DOUBLE_NONE, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { + { .description = "None", .value = DOUBLE_NONE }, + { .description = "Simple doubling", .value = DOUBLE_SIMPLE }, + { .description = "sRGB interpolation", .value = DOUBLE_INTERPOLATE_SRGB }, + { .description = "Linear interpolation", .value = DOUBLE_INTERPOLATE_LINEAR }, + { .description = "" } + }, + .bios = { { 0 } } + }, + { + .name = "font", + .description = "Font", + .type = CONFIG_SELECTION, + .default_string = NULL, + .default_int = 0, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { + { .description = "US (CP 437)", .value = 0 }, + { .description = "IBM Nordic (CP 437-Nordic)", .value = 1 }, + { .description = "Tulip DGA", .value = 4 }, + { .description = "" } + }, + .bios = { { 0 } } + }, + { .name = "", .description = "", .type = CONFIG_END } +}; +// clang-format on + +const device_t v6355d_device = { + .name = "Yamaha V6355D", + .internal_name = "v6355d", + .flags = DEVICE_ISA, + .local = 0, + .init = v6355_standalone_init, + .close = v6355_close, + .reset = NULL, + .available = NULL, + .speed_changed = v6355_speed_changed, + .force_redraw = NULL, + .config = v6355_config +}; diff --git a/src/video/vid_table.c b/src/video/vid_table.c index 17ed64bdd..037f2a16c 100644 --- a/src/video/vid_table.c +++ b/src/video/vid_table.c @@ -114,6 +114,7 @@ video_cards[] = { { .device = &et4000_isa_device, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &v7_vga_1024i_device, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &wy700_device, .flags = VIDEO_FLAG_TYPE_NONE }, + { .device = &v6355d_device, .flags = VIDEO_FLAG_TYPE_NONE }, /* ISA16 */ { .device = &mach64gx_isa_device, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &gd5420_isa_device, .flags = VIDEO_FLAG_TYPE_NONE },