From 317df427a394fb5a10a446cdbd7a8e91a1ec0255 Mon Sep 17 00:00:00 2001 From: TC1995 Date: Tue, 5 Dec 2023 21:52:23 +0100 Subject: [PATCH] Initial commit of the PC-98x1 branch, currently display only and not hooked up yet. --- src/include/86box/vid_upd7220.h | 119 +++ src/video/vid_pc98x1_disp.c | 464 +++++++++++ src/video/vid_pc98x1_egc.c | 1298 +++++++++++++++++++++++++++++++ src/video/vid_upd7220.c | 1045 +++++++++++++++++++++++++ 4 files changed, 2926 insertions(+) create mode 100644 src/include/86box/vid_upd7220.h create mode 100644 src/video/vid_pc98x1_disp.c create mode 100644 src/video/vid_pc98x1_egc.c create mode 100644 src/video/vid_upd7220.c diff --git a/src/include/86box/vid_upd7220.h b/src/include/86box/vid_upd7220.h new file mode 100644 index 000000000..16b73f599 --- /dev/null +++ b/src/include/86box/vid_upd7220.h @@ -0,0 +1,119 @@ +/* + * 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 NEC uPD7220 graphic display controller. + * + * + * + * Authors: TAKEDA toshiya, + * yui/Neko Project II + * + * Copyright 2009-2023 TAKEDA, toshiya. + * Copyright 2008-2023 yui/Neko Project II. + */ +#ifndef VIDEO_UPD7220_H +# define VIDEO_UPD7220_H + +#define TVRAM_SIZE 0x4000 +#define VRAM16_SIZE 0x40000 +#define VRAM256_SIZE 0x80000 +#define EMS_SIZE 0x10000 + +#define GDC_BUFFERS 1024 +#define GDC_TABLEMAX 0x1000 + +#define GDC_VTICKS 18 +#define GDC_VSTICKS 2 + +typedef struct upd7220_t { + void *priv; + + /* vram access */ + void (*vram_write)(uint32_t addr, uint8_t val, void *priv); + uint8_t (*vram_read)(uint32_t addr, void *priv); + + /* address */ + uint32_t address[480][80]; + + /* registers */ + int cmdreg; + uint8_t statreg; + + /* params */ + uint8_t sync[16]; + uint8_t zoom, zr, zw; + uint8_t ra[16]; + uint8_t cs[3]; + uint8_t pitch; + uint32_t lad; + uint8_t vect[11]; + uint32_t ead, dad; + uint8_t maskl, maskh; + uint8_t mod; + uint8_t start; + uint8_t dirty; + + /* fifo buffers */ + uint8_t params[16]; + int params_count; + uint8_t data[GDC_BUFFERS]; + int data_count, data_read, data_write; + + /* draw */ + int rt[GDC_TABLEMAX + 1]; + int dx, dy; + int dir, diff, sl, dc, d, d2, d1, dm; + uint16_t pattern; +} upd7220_t; + +extern void upd7220_init(upd7220_t *dev, void *priv, + uint8_t (*vram_read)(uint32_t addr, void *priv), + void (*vram_write)(uint32_t addr, uint8_t val, void *priv)); + +void upd7220_param_write(uint16_t addr, uint8_t value, void *priv); +uint8_t upd7220_statreg_read(uint16_t addr, void *priv); +void upd7220_cmdreg_write(uint16_t addr, uint8_t value, void *priv); +uint8_t upd7220_data_read(uint16_t addr, void *priv); +void upd7220_reset(upd7220_t *dev); + +# ifdef EMU_DEVICE_H +extern const device_t ati68860_ramdac_device; +extern const device_t ati68875_ramdac_device; +extern const device_t att490_ramdac_device; +extern const device_t att491_ramdac_device; +extern const device_t att492_ramdac_device; +extern const device_t att498_ramdac_device; +extern const device_t av9194_device; +extern const device_t bt484_ramdac_device; +extern const device_t att20c504_ramdac_device; +extern const device_t bt485_ramdac_device; +extern const device_t att20c505_ramdac_device; +extern const device_t bt485a_ramdac_device; +extern const device_t gendac_ramdac_device; +extern const device_t ibm_rgb528_ramdac_device; +extern const device_t ics2494an_305_device; +extern const device_t ati18810_device; +extern const device_t ati18811_0_device; +extern const device_t ati18811_1_device; +extern const device_t ics2595_device; +extern const device_t icd2061_device; +extern const device_t ics9161_device; +extern const device_t sc11483_ramdac_device; +extern const device_t sc11487_ramdac_device; +extern const device_t sc11486_ramdac_device; +extern const device_t sc11484_nors2_ramdac_device; +extern const device_t sc1502x_ramdac_device; +extern const device_t sdac_ramdac_device; +extern const device_t stg_ramdac_device; +extern const device_t tkd8001_ramdac_device; +extern const device_t tseng_ics5301_ramdac_device; +extern const device_t tseng_ics5341_ramdac_device; +extern const device_t tvp3026_ramdac_device; +# endif + +#endif /*VIDEO_UPD7220_H*/ diff --git a/src/video/vid_pc98x1_disp.c b/src/video/vid_pc98x1_disp.c new file mode 100644 index 000000000..763fc143d --- /dev/null +++ b/src/video/vid_pc98x1_disp.c @@ -0,0 +1,464 @@ +/* + * 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 EGC graphics processor used by + * the NEC PC-98x1 series of computers. + * + * + * + * Authors: TAKEDA toshiya, + * yui/Neko Project II + * + * Copyright 2009-2023 TAKEDA, toshiya. + * Copyright 2008-2023 yui/Neko Project II. + */ +#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/device.h> +#include <86box/video.h> +#include <86box/vid_pc98x1_disp.h> +#include <86box/plat_unused.h> + +/***********************************************************/ +/* NEC PC-9821 VGA */ + +enum { + PALETTE_G = 0, + PALETTE_R = 1, + PALETTE_B = 2, +}; + +enum { + DIRTY_TVRAM = 0x01, + DIRTY_VRAM0 = 0x02, + DIRTY_VRAM1 = 0x04, + DIRTY_PALETTE = 0x10, + DIRTY_DISPLAY = 0x80, +}; + +enum { + ATTR_ST = 0x01, + ATTR_BL = 0x02, + ATTR_RV = 0x04, + ATTR_UL = 0x08, + ATTR_VL = 0x10, + ATTR_COL = 0xe0, +}; + +static void +pc98x1_update_palette(pc98x1_vid_t *dev) +{ + int i; + uint8_t r, g, b; + + for (i = 0; i < 8; i++) { + r = (i & 2) ? 0xff : 0; + g = (i & 4) ? 0xff : 0; + b = (i & 1) ? 0xff : 0; + dev->palette_chr[i] = makecol(r, g, b); + } + if (dev->mode2[MODE2_256COLOR]) { + for (i = 0; i < 256; i++) { + r = dev->anapal[PALETTE_R][i]; + g = dev->anapal[PALETTE_G][i]; + b = dev->anapal[PALETTE_B][i]; + dev->palette_gfx[i] = makecol(r, g, b); + } + } else if (s->mode2[MODE2_16COLOR]) { + for (i = 0; i < 16; i++) { + r = dev->anapal[PALETTE_R][i] << 4; + g = dev->anapal[PALETTE_G][i] << 4; + b = dev->anapal[PALETTE_B][i] << 4; + dev->palette_gfx[i] = makecol(r, g, b); + } + } else { + for (i = 0; i < 4; i++) { + static int lo[4] = {7, 5, 6, 4}; + static int hi[4] = {3, 1, 2, 0}; + r = (dev->digipal[i] & 0x02) ? 0xff : 0; + g = (dev->digipal[i] & 0x04) ? 0xff : 0; + b = (dev->digipal[i] & 0x01) ? 0xff : 0; + dev->palette_gfx[lo[i]] = makecol(r, g, b); + r = (dev->digipal[i] & 0x20) ? 0xff : 0; + g = (dev->digipal[i] & 0x40) ? 0xff : 0; + b = (dev->digipal[i] & 0x10) ? 0xff : 0; + dev->palette_gfx[hi[i]] = makecol(r, g, b); + } + } +} + +static void +pc98x1_render_chr_screen(pc98x1_vid_t *dev) +{ + int pl, bl, cl; + int sur, sdr; + uint32_t *addr, *addr2; + uint32_t cursor_addr; + int cursor_top, cursor_bottom; + int ytop, ysur, ysdr; + int l, x, y; + int xofs, addrofs; + + pl = dev->pl & 31; + if (pl) + pl = 32 - pl; + + bl = dev->bl + pl + 1; + cl = dev->cl; + sur = dev->sur & 31; + if (sur) + sur = 32 - sur; + + sdr = dev->sdr + 1; + + addr = pc98x1_gdc_get_address(&dev->gdc_chr, 2, 0x1fff); + addr2 = addr + 160 * (sur + sdr); + pc98x1_gdc_get_cursor_address(&dev->gdc_chr, 0x1fff, + &cursor_addr, &cursor_top, &cursor_bottom); + ytop = 0; + ysur = bl * sur; + ysdr = bl * (sur + sdr); + + if (s->mode1[MODE1_COLUMN]) { + xofs = 16; + addrofs = 2; + } else { + xofs = 8; + addrofs = 1; + } + memset(dev->tvram_buffer, 0, 640 * 480); + + for (y = 0; y < 400; y += bl) { + uint32_t gaiji1st = 0, last = 0, offset; + int kanji2nd = 0; + if (y == ysur) { + ytop = y; + y -= dev->ssl; + ysur = 400; + } + if (y >= ysdr) { + y = ytop = ysdr; + addr = addr2; + ysdr = 400; + } + for (x = 0; x < 640; x += xofs) { + uint16_t code = *(uint16_t *)(dev->tvram + *addr); + uint8_t attr = dev->tvram[*addr | 0x2000]; + uint8_t color = (attr & ATTR_COL) ? (attr >> 5) : 8; + uint8_t cursor = (*addr == cursor_addr); + addr += addrofs; + if (kanji2nd) { + kanji2nd = 0; + offset = last + 0x800; + } else if (code & 0xff00) { + uint16_t lo = code & 0x7f; + uint16_t hi = (code >> 8) & 0x7f; + offset = (lo << 4) | (hi << 12); + if (lo == 0x56 || lo == 0x57) { + offset += gaiji1st; + gaiji1st ^= 0x800; + } else { + uint16_t lo = code & 0xff; + if (lo < 0x09 || lo >= 0x0c) + kanji2nd = 1; + + gaiji1st = 0; + } + } else { + uint16_t lo = code & 0xff; + if (dev->mode1[MODE1_FONTSEL]) + offset = 0x80000 | (lo << 4); + else + offset = 0x82000 | (lo << 4); + + gaiji1st = 0; + } + last = offset; + for (l = 0; l < cl && l < 16; l++) { + int yy = y + l + pl; + if (yy >= ytop && yy < 480) { + uint8_t *dest = dev->tvram_buffer + yy * 640 + x; + uint8_t pattern = dev->font[offset + l]; + if (!(attr & ATTR_ST)) + pattern = 0; + else if (((attr & ATTR_BL) && (s->blink & 0x20)) || + (attr & ATTR_RV)) + pattern = ~pattern; + + if ((attr & ATTR_UL) && l == 15) + pattern = 0xff; + + if (attr & ATTR_VL) + pattern |= 0x08; + + if (cursor && l >= cursor_top && l < cursor_bottom) + pattern = ~pattern; + + if (dev->mode1[MODE1_COLUMN]) { + if (pattern & 0x80) dest[ 0] = dest[ 1] = color; + if (pattern & 0x40) dest[ 2] = dest[ 3] = color; + if (pattern & 0x20) dest[ 4] = dest[ 5] = color; + if (pattern & 0x10) dest[ 6] = dest[ 7] = color; + if (pattern & 0x08) dest[ 8] = dest[ 9] = color; + if (pattern & 0x04) dest[10] = dest[11] = color; + if (pattern & 0x02) dest[12] = dest[13] = color; + if (pattern & 0x01) dest[14] = dest[15] = color; + } else { + if (pattern & 0x80) dest[0] = color; + if (pattern & 0x40) dest[1] = color; + if (pattern & 0x20) dest[2] = color; + if (pattern & 0x10) dest[3] = color; + if (pattern & 0x08) dest[4] = color; + if (pattern & 0x04) dest[5] = color; + if (pattern & 0x02) dest[6] = color; + if (pattern & 0x01) dest[7] = color; + } + } + } + } + } +} + +static void +pc98x1_render_gfx_screen(pc98x1_t *dev) +{ + uint8_t *dest; + int x, y; + uint8_t b, r, g, e = 0; + + static int prev_mode = -1; + int mode; + + if (dev->mode2[MODE2_256COLOR]) { + int addr = 0; + if (dev->mode2[MODE2_480LINE]) { + dest = dev->vram0_buffer; + for (y = 0; y < 480; y++) { + for (x = 0; x < 640; x++) + *dest++ = dev->vram256[addr++]; + + addr += 128 * 3; + } + mode = 2; + } else { + if (dev->bank_disp == DIRTY_VRAM0) + dest = dev->vram0_buffer; + else + dest = dev->vram1_buffer; + + for (y = 0; y < 400; y++) { + for (x = 0; x < 640; x++) { + *dest++ = dev->vram256_disp[addr++]; + } + } + mode = 1; + } + } else { + uint32_t *addr = pc98x1_gdc_get_address(&dev->gdc_gfx, 1, 0x7fff); + if (dev->bank_disp == DIRTY_VRAM0) + dest = s->vram0_buffer; + else + dest = s->vram1_buffer; + + for (y = 0; y < 400; y++) { + for (x = 0; x < 640; x += 8) { + b = dev->vram16_draw_b[*addr]; + r = dev->vram16_draw_r[*addr]; + g = dev->vram16_draw_g[*addr]; + if (dev->mode2[MODE2_16COLOR]) + e = dev->vram16_draw_e[*addr]; + + addr++; + *dest++ = ((b & 0x80) >> 7) | ((r & 0x80) >> 6) | ((g & 0x80) >> 5) | ((e & 0x80) >> 4); + *dest++ = ((b & 0x40) >> 6) | ((r & 0x40) >> 5) | ((g & 0x40) >> 4) | ((e & 0x40) >> 3); + *dest++ = ((b & 0x20) >> 5) | ((r & 0x20) >> 4) | ((g & 0x20) >> 3) | ((e & 0x20) >> 2); + *dest++ = ((b & 0x10) >> 4) | ((r & 0x10) >> 3) | ((g & 0x10) >> 2) | ((e & 0x10) >> 1); + *dest++ = ((b & 0x08) >> 3) | ((r & 0x08) >> 2) | ((g & 0x08) >> 1) | ((e & 0x08) >> 0); + *dest++ = ((b & 0x04) >> 2) | ((r & 0x04) >> 1) | ((g & 0x04) >> 0) | ((e & 0x04) << 1); + *dest++ = ((b & 0x02) >> 1) | ((r & 0x02) >> 0) | ((g & 0x02) << 1) | ((e & 0x02) << 2); + *dest++ = ((b & 0x01) >> 0) | ((r & 0x01) << 1) | ((g & 0x01) << 2) | ((e & 0x01) << 3); + } + if (dev->mode1[MODE1_200LINE]) { + memset(dest, 0, 640); + dest += 640; + y++; + } + } + mode = 0; + } + if (prev_mode != mode) { + switch (mode) { + case 0: + pclog("pc98vga: 640x400, 4bpp\n"); + break; + case 1: + pclog("pc98vga: 640x400, 8bpp\n"); + break; + case 2: + pclog("pc98vga: 640x480, 8bpp\n"); + break; + } + prev_mode = mode; + } +} + +static void +pc98x1_vid_timer(void *priv) +{ + pc98x1_vid_t *dev = (pc98x1_vid_t *)priv; + uint8_t prev_blink = dev->blink; + uint8_t dirty; + uint8_t *src_chr; + uint8_t *src_gfx; + + if (dev->mode2[MODE2_256COLOR] && dev->mode2[MODE2_480LINE]) + dev->height = 480; + else + dev->height = 400; + + if (!dev->linepos) { + timer_advance_u64(&dev->timer, dev->dispofftime); + dev->linepos = 1; + if (dev->dispon) { + if (dev->displine == 0) + video_wait_for_buffer(); + + if (dev->mode1[MODE1_DISP]) { + if (dev->dirty & DIRTY_PALETTE) { + /* update palette */ + pc98x1_update_palette(dev); + dev->dirty &= ~DIRTY_PALETTE; + dev->dirty |= DIRTY_DISPLAY; + } + if (dev->gdc_chr.dirty & GDC_DIRTY_START) { + dev->gdc_chr.dirty &= ~GDC_DIRTY_START; + dev->dirty |= DIRTY_DISPLAY; + } + if (dev->gdc_chr.start) { + if ((dev->gdc_chr.dirty & GDC_DIRTY_CHR) || (dev->dirty & DIRTY_TVRAM)) { + /* update text screen */ + pc98x1_render_chr_screen(dev); + dev->gdc_chr.dirty &= ~GDC_DIRTY_CHR; + dev->dirty &= ~DIRTY_TVRAM; + dev->dirty |= DIRTY_DISPLAY; + } + } + if (dev->gdc_gfx.dirty & GDC_DIRTY_START) { + dev->gdc_gfx.dirty &= ~GDC_DIRTY_START; + dev->dirty |= DIRTY_DISPLAY; + } + if (dev->gdc_gfx.start) { + dirty = dev->bank_disp; + if (dev->mode2[MODE2_256COLOR]) { + if (dev->mode2[MODE2_480LINE]) + dirty = DIRTY_VRAM0 | DIRTY_VRAM1; + } + if ((dev->gdc_gfx.dirty & GDC_DIRTY_GFX) || (dev->dirty & dirty)) { + /* update cg screen */ + pc98x1_render_gfx_screen(dev); + dev->gdc_gfx.dirty &= ~GDC_DIRTY_GFX; + dev->dirty &= ~dirty; + dev->dirty |= DIRTY_DISPLAY; + } + } + } + + /* update screen */ + if (dev->dirty & DIRTY_DISPLAY) { + if (dev->mode1[MODE1_DISP]) { + /* output screen */ + if (!dev->gdc_chr.start || dev->mode2[MODE2_256COLOR]) + src_chr = dev->null_buffer; + else + src_chr = dev->tvram_buffer; + + if (!dev->gdc_gfx.start) + src_gfx = dev->null_buffer; + else if (dev->mode2[MODE2_256COLOR] && dev->mode2[MODE2_480LINE]) + src_gfx = dev->vram0_buffer; + else if (dev->bank_disp == DIRTY_VRAM0) + src_gfx = dev->vram0_buffer; + else + src_gfx = dev->vram1_buffer; + + for (int y = 0; y < dev->height; y++) { + for (int x = 0; x < dev->width; x++) { + if (*src_chr) + buffer32->line[dev->displine][x] = dev->palette_chr[*src_chr & 0x07]; + else + buffer32->line[dev->displine][x] = dev->palette_gfx[*src_gfx]; + + src_chr++; + src_gfx++; + } + } + } else { + for (int y = 0; y < dev->height; y++) { + for (int x = 0; x < dev->width; x++) { + buffer32->line[dev->displine][x] = 0; + } + } + } + dev->dirty &= ~DIRTY_DISPLAY; + } + } + + if (++dev->displine == dev->height) { + dev->vsync |= GDC_STAT_VSYNC; + dev->dispon = 0; + if (dev->crtv) { + picint(1 << 2); + dev->crtv = 0; + } + } + + if (dev->displine == dev->height + 32) { + dev->vsync &= ~GDC_STAT_VSYNC; + dev->dispon = 1; + dev->displine = 0; + } + } else { + timer_advance_u64(&dev->timer, dev->dispontime); + dev->linepos = 0; + + if (dev->displine == dev->height) { + /* resize screen */ + if (dev->width != xsize || dev->height != ysize) { + xsize = dev->width; + ysize = dev->height; + set_screen_size(xsize, ysize); + + if (video_force_resize_get()) + video_force_resize_set(0); + + dev->dirty |= DIRTY_DISPLAY; + } + video_blit_memtoscreen(0, 0, xsize, ysize); + frames++; + + video_res_x = dev->width; + video_res_y = dev->height; + video_bpp = 8; + /* blink */ + dev->blink++; + if ((prev_blink & 0x20) != (dev->blink & 0x20)) { + dev->dirty |= DIRTY_TVRAM; + } + } + } +} diff --git a/src/video/vid_pc98x1_egc.c b/src/video/vid_pc98x1_egc.c new file mode 100644 index 000000000..63e78bdc4 --- /dev/null +++ b/src/video/vid_pc98x1_egc.c @@ -0,0 +1,1298 @@ +/* + * 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 EGC graphics processor used by + * the NEC PC-98x1 series of computers. + * + * + * + * Authors: TAKEDA toshiya, + * yui/Neko Project II + * + * Copyright 2009-2023 TAKEDA, toshiya. + * Copyright 2008-2023 yui/Neko Project II. + */ +#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/device.h> +#include <86box/video.h> +#include <86box/vid_pc98x1_egc.h> +#include <86box/plat_unused.h> + +/***********************************************************/ +/* EGC (based on Neko Project 2) */ + +/* shift sub */ + +static const uint8_t egc_bytemask_u0[64] = { + 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, + 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x01, + 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x07, 0x03, 0x01, + 0xf0, 0x78, 0x3c, 0x1e, 0x0f, 0x07, 0x03, 0x01, + 0xf8, 0x7c, 0x3e, 0x1f, 0x0f, 0x07, 0x03, 0x01, + 0xfc, 0x7e, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, + 0xfe, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, + 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01 +}; +static const uint8_t egc_bytemask_u1[8] = { + 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff +}; +static const uint8_t egc_bytemask_d0[64] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, + 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, + 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xc0, 0x80, + 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0xe0, 0xc0, 0x80, + 0x1f, 0x3e, 0x7c, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, + 0x3f, 0x7e, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, + 0x7f, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, + 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 +}; +static const uint8_t egc_bytemask_d1[8] = { + 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff +}; + +/* draw command */ + +static const int gdc_vectdir[16][4] = { + { 0, 1, 1, 0}, { 1, 1, 1,-1}, { 1, 0, 0,-1}, { 1,-1,-1,-1}, + { 0,-1,-1, 0}, {-1,-1,-1, 1}, {-1, 0, 0, 1}, {-1, 1, 1, 1}, + { 0, 1, 1, 1}, { 1, 1, 1, 0}, { 1, 0, 1,-1}, { 1,-1, 0,-1}, + { 0,-1,-1,-1}, {-1,-1,-1, 0}, {-1, 0,-1, 1}, {-1, 1, 0, 1} +}; + +static void +egc_shift(egc_t *dev) +{ + uint8_t src8, dst8; + + dev->remain = (dev->leng & 0xfff) + 1; + dev->func = (dev->sft >> 12) & 1; + if (!dev->func) { + dev->inptr = dev->buf; + dev->outptr = dev->buf; + } else { + dev->inptr = dev->buf + (4096 / 8) + 3; + dev->outptr = dev->buf + (4096 / 8) + 3; + } + dev->srcbit = dev->sft & 0x0f; + dev->dstbit = (dev->sft >> 4) & 0x0f; + + src8 = dev->srcbit & 0x07; + dst8 = dev->dstbit & 0x07; + if (src8 < dst8) { + dev->func += 2; + dev->sft8bitr = dst8 - src8; + dev->sft8bitl = 8 - dev->sft8bitr; + } else if (src8 > dst8) { + dev->func += 4; + dev->sft8bitl = src8 - dst8; + dev->sft8bitr = 8 - dev->sft8bitl; + } + dev->stack = 0; +} + +static void +egc_sftb_upn_sub(egc_t *dev, uint32_t ext) +{ + if (dev->dstbit >= 8) { + dev->dstbit -= 8; + dev->srcmask.b[ext] = 0; + return; + } + if (dev->dstbit) { + if ((dev->dstbit + dev->remain) >= 8) { + dev->srcmask.b[ext] = egc_bytemask_u0[dev->dstbit + (7 * 8)]; + dev->remain -= (8 - dev->dstbit); + dev->dstbit = 0; + } else { + dev->srcmask.b[ext] = egc_bytemask_u0[dev->dstbit + (dev->remain - 1) * 8]; + dev->remain = 0; + dev->dstbit = 0; + } + } else { + if (dev->remain >= 8) + dev->remain -= 8; + else { + dev->srcmask.b[ext] = egc_bytemask_u1[dev->remain - 1]; + dev->remain = 0; + } + } + dev->vram_src.b[0][ext] = dev->outptr[0]; + dev->vram_src.b[1][ext] = dev->outptr[4]; + dev->vram_src.b[2][ext] = dev->outptr[8]; + dev->vram_src.b[3][ext] = dev->outptr[12]; + dev->outptr++; +} + +static void +egc_sftb_dnn_sub(egc_t *dev, uint32_t ext) +{ + if (dev->dstbit >= 8) { + dev->dstbit -= 8; + dev->srcmask.b[ext] = 0; + return; + } + if (dev->dstbit) { + if ((dev->dstbit + dev->remain) >= 8) { + dev->srcmask.b[ext] = egc_bytemask_d0[dev->dstbit + (7 * 8)]; + dev->remain -= (8 - dev->dstbit); + dev->dstbit = 0; + } else { + dev->srcmask.b[ext] = egc_bytemask_d0[dev->dstbit + (dev->remain - 1) * 8]; + dev->remain = 0; + dev->dstbit = 0; + } + } else { + if (dev->remain >= 8) + dev->remain -= 8; + else { + dev->srcmask.b[ext] = egc_bytemask_d1[dev->remain - 1]; + dev->remain = 0; + } + } + dev->vram_src.b[0][ext] = dev->outptr[0]; + dev->vram_src.b[1][ext] = dev->outptr[4]; + dev->vram_src.b[2][ext] = dev->outptr[8]; + dev->vram_src.b[3][ext] = dev->outptr[12]; + dev->outptr--; +} + +static void +egc_sftb_upr_sub(egc_t *dev, uint32_t ext) +{ + if (dev->dstbit >= 8) { + dev->dstbit -= 8; + dev->srcmask.b[ext] = 0; + return; + } + if (dev->dstbit) { + if ((dev->dstbit + dev->remain) >= 8) { + dev->srcmask.b[ext] = egc_bytemask_u0[dev->dstbit + (7 * 8)]; + dev->remain -= (8 - dev->dstbit); + } else { + dev->srcmask.b[ext] = egc_bytemask_u0[dev->dstbit + (dev->remain - 1) * 8]; + dev->remain = 0; + } + dev->dstbit = 0; + dev->vram_src.b[0][ext] = (dev->outptr[0] >> dev->sft8bitr); + dev->vram_src.b[1][ext] = (dev->outptr[4] >> dev->sft8bitr); + dev->vram_src.b[2][ext] = (dev->outptr[8] >> dev->sft8bitr); + dev->vram_src.b[3][ext] = (dev->outptr[12] >> dev->sft8bitr); + } else { + if (dev->remain >= 8) + dev->remain -= 8; + else { + dev->srcmask.b[ext] = egc_bytemask_u1[dev->remain - 1]; + dev->remain = 0; + } + dev->vram_src.b[0][ext] = (dev->outptr[0] << dev->sft8bitl) | (dev->outptr[1] >> dev->sft8bitr); + dev->vram_src.b[1][ext] = (dev->outptr[4] << dev->sft8bitl) | (dev->outptr[5] >> dev->sft8bitr); + dev->vram_src.b[2][ext] = (dev->outptr[8] << dev->sft8bitl) | (dev->outptr[9] >> dev->sft8bitr); + dev->vram_src.b[3][ext] = (dev->outptr[12] << dev->sft8bitl) | (dev->outptr[13] >> dev->sft8bitr); + dev->outptr++; + } +} + +static void +egc_sftb_dnr_sub(egc_t *dev, uint32_t ext) +{ + if (dev->dstbit >= 8) { + dev->dstbit -= 8; + dev->srcmask.b[ext] = 0; + return; + } + if (dev->dstbit) { + if ((dev->dstbit + dev->remain) >= 8) { + dev->srcmask.b[ext] = egc_bytemask_d0[dev->dstbit + (7 * 8)]; + dev->remain -= (8 - dev->dstbit); + } else { + dev->srcmask.b[ext] = egc_bytemask_d0[dev->dstbit + (dev->remain - 1) * 8]; + dev->remain = 0; + } + dev->dstbit = 0; + dev->vram_src.b[0][ext] = (dev->outptr[0] << dev->sft8bitr); + dev->vram_src.b[1][ext] = (dev->outptr[4] << dev->sft8bitr); + dev->vram_src.b[2][ext] = (dev->outptr[8] << dev->sft8bitr); + dev->vram_src.b[3][ext] = (dev->outptr[12] << dev->sft8bitr); + } else { + if (dev->remain >= 8) + dev->remain -= 8; + else { + dev->srcmask.b[ext] = egc_bytemask_d1[dev->remain - 1]; + dev->remain = 0; + } + dev->outptr--; + dev->vram_src.b[0][ext] = (dev->outptr[1] >> dev->sft8bitl) | (dev->outptr[0] << dev->sft8bitr); + dev->vram_src.b[1][ext] = (dev->outptr[5] >> dev->sft8bitl) | (dev->outptr[4] << dev->sft8bitr); + dev->vram_src.b[2][ext] = (dev->outptr[9] >> dev->sft8bitl) | (dev->outptr[8] << dev->sft8bitr); + dev->vram_src.b[3][ext] = (dev->outptr[13] >> dev->sft8bitl) | (dev->outptr[12] << dev->sft8bitr); + } +} + +static void +egc_sftb_upl_sub(egc_t *dev, uint32_t ext) +{ + if (dev->dstbit >= 8) { + dev->dstbit -= 8; + dev->srcmask.b[ext] = 0; + return; + } + if (dev->dstbit) { + if ((dev->dstbit + dev->remain) >= 8) { + dev->srcmask.b[ext] = egc_bytemask_u0[dev->dstbit + (7 * 8)]; + dev->remain -= (8 - dev->dstbit); + dev->dstbit = 0; + } else { + dev->srcmask.b[ext] = egc_bytemask_u0[dev->dstbit + (dev->remain - 1) * 8]; + dev->remain = 0; + dev->dstbit = 0; + } + } else { + if (dev->remain >= 8) + dev->remain -= 8; + else { + dev->srcmask.b[ext] = egc_bytemask_u1[dev->remain - 1]; + dev->remain = 0; + } + } + dev->vram_src.b[0][ext] = (dev->outptr[0] << dev->sft8bitl) | (dev->outptr[1] >> dev->sft8bitr); + dev->vram_src.b[1][ext] = (dev->outptr[4] << dev->sft8bitl) | (dev->outptr[5] >> dev->sft8bitr); + dev->vram_src.b[2][ext] = (dev->outptr[8] << dev->sft8bitl) | (dev->outptr[9] >> dev->sft8bitr); + dev->vram_src.b[3][ext] = (dev->outptr[12] << dev->sft8bitl) | (dev->outptr[13] >> dev->sft8bitr); + dev->outptr++; +} + +static void +egc_sftb_dnl_sub(egc_t *dev, uint32_t ext) +{ + if (dev->dstbit >= 8) { + dev->dstbit -= 8; + dev->srcmask.b[ext] = 0; + return; + } + if (dev->dstbit) { + if ((dev->dstbit + dev->remain) >= 8) { + dev->srcmask.b[ext] = egc_bytemask_d0[dev->dstbit + (7 * 8)]; + dev->remain -= (8 - dev->dstbit); + dev->dstbit = 0; + } else { + dev->srcmask.b[ext] = egc_bytemask_d0[dev->dstbit + (dev->remain - 1) * 8]; + dev->remain = 0; + dev->dstbit = 0; + } + } else { + if (dev->remain >= 8) + dev->remain -= 8; + else { + dev->srcmask.b[ext] = egc_bytemask_d1[dev->remain - 1]; + dev->remain = 0; + } + } + dev->outptr--; + dev->vram_src.b[0][ext] = (dev->outptr[1] >> dev->sft8bitl) | (dev->outptr[0] << dev->sft8bitr); + dev->vram_src.b[1][ext] = (dev->outptr[5] >> dev->sft8bitl) | (dev->outptr[4] << dev->sft8bitr); + dev->vram_src.b[2][ext] = (dev->outptr[9] >> dev->sft8bitl) | (dev->outptr[8] << dev->sft8bitr); + dev->vram_src.b[3][ext] = (dev->outptr[13] >> dev->sft8bitl) | (dev->outptr[12] << dev->sft8bitr); +} + +static void +egc_sftb_upn0(egc_t *dev, uint32_t ext) +{ + if (dev->stack < ((uint32_t)(8 - dev->dstbit))) { + dev->srcmask.b[ext] = 0; + return; + } + dev->stack -= (8 - dev->dstbit); + egc_sftb_upn_sub(dev, ext); + if (!dev->remain) + egc_shift(dev); +} + +static void +egc_sftw_upn0(egc_t *dev) +{ + if (dev->stack < ((uint32_t)(16 - dev->dstbit))) { + dev->srcmask.w = 0; + return; + } + dev->stack -= (16 - dev->dstbit); + egc_sftb_upn_sub(dev, 0); + if (dev->remain) { + egc_sftb_upn_sub(dev, 1); + if (dev->remain) + return; + } else + dev->srcmask.b[1] = 0; + + egc_shift(dev); +} + +static void +egc_sftb_dnn0(egc_t *dev, uint32_t ext) +{ + if (dev->stack < ((uint32_t)(8 - dev->dstbit))) { + dev->srcmask.b[ext] = 0; + return; + } + dev->stack -= (8 - dev->dstbit); + egc_sftb_dnn_sub(dev, ext); + if (!dev->remain) + egc_shift(dev); +} + +static void +egc_sftw_dnn0(egc_t *dev) +{ + if (dev->stack < ((uint32_t)(16 - dev->dstbit))) { + dev->srcmask.w = 0; + return; + } + dev->stack -= (16 - dev->dstbit); + egc_sftb_dnn_sub(dev, 1); + if (dev->remain) { + egc_sftb_dnn_sub(dev, 0); + if (dev->remain) + return; + } else + dev->srcmask.b[0] = 0; + + egc_shift(dev); +} + +static void +egc_sftb_upr0(egc_t *dev, uint32_t ext) +{ + if (dev->stack < ((uint32_t)(8 - dev->dstbit))) { + dev->srcmask.b[ext] = 0; + return; + } + dev->stack -= (8 - dev->dstbit); + egc_sftb_upr_sub(dev, ext); + if (!dev->remain) + egc_shift(dev); +} + +static void +egc_sftw_upr0(egc_t *dev) +{ + if (dev->stack < ((uint32_t)(16 - dev->dstbit))) { + dev->srcmask.w = 0; + return; + } + dev->stack -= (16 - dev->dstbit); + egc_sftb_upr_sub(dev, 0); + if (dev->remain) { + egc_sftb_upr_sub(dev, 1); + if (dev->remain) + return; + } else + dev->srcmask.b[1] = 0; + + egc_shift(dev); +} + +static void +egc_sftb_dnr0(egc_t *dev, uint32_t ext) +{ + if (dev->stack < ((uint32_t)(8 - dev->dstbit))) { + dev->srcmask.b[ext] = 0; + return; + } + dev->stack -= (8 - dev->dstbit); + egc_sftb_dnr_sub(dev, ext); + if (!dev->remain) + egc_shift(dev); +} + +static void +egc_sftw_dnr0(egc_t *dev) +{ + if (dev->stack < ((uint32_t)(16 - dev->dstbit))) { + dev->srcmask.w = 0; + return; + } + dev->stack -= (16 - dev->dstbit); + egc_sftb_dnr_sub(dev, 1); + if (dev->remain) { + egc_sftb_dnr_sub(dev, 0); + if (dev->remain) + return; + } else { + dev->srcmask.b[0] = 0; + } + egc_shift(dev); +} + +static void +egc_sftb_upl0(egc_t *dev, uint32_t ext) +{ + if (dev->stack < ((uint32_t)(8 - dev->dstbit))) { + dev->srcmask.b[ext] = 0; + return; + } + dev->stack -= (8 - dev->dstbit); + egc_sftb_upl_sub(dev, ext); + if (!dev->remain) + egc_shift(dev); +} + +static void +egc_sftw_upl0(egc_t *dev) +{ + if (dev->stack < ((uint32_t)(16 - dev->dstbit))) { + dev->srcmask.w = 0; + return; + } + dev->stack -= (16 - dev->dstbit); + egc_sftb_upl_sub(dev, 0); + if (dev->remain) { + egc_sftb_upl_sub(dev, 1); + if (dev->remain) + return; + } else + dev->srcmask.b[1] = 0; + + egc_shift(dev); +} + +static void +egc_sftb_dnl0(egc_t *dev, uint32_t ext) +{ + if (dev->stack < ((uint32_t)(8 - dev->dstbit))) { + dev->srcmask.b[ext] = 0; + return; + } + dev->stack -= (8 - dev->dstbit); + egc_sftb_dnl_sub(dev, ext); + if (!dev->remain) + egc_shift(dev); +} + +static void +egc_sftw_dnl0(egc_t *dev) +{ + if (dev->stack < ((uint32_t)(16 - dev->dstbit))) { + dev->srcmask.w = 0; + return; + } + dev->stack -= (16 - dev->dstbit); + egc_sftb_dnl_sub(dev, 1); + if (dev->remain) { + egc_sftb_dnl_sub(dev, 0); + if (dev->remain) + return; + } else + dev->srcmask.b[0] = 0; + + egc_shift(dev); +} + +/* shift */ + +typedef void (*PC98EGCSFTB)(egc_t *dev, uint32_t ext); +typedef void (*PC98EGCSFTW)(egc_t *dev); + +static const PC98EGCSFTB egc_sftb[6] = { + egc_sftb_upn0, egc_sftb_dnn0, + egc_sftb_upr0, egc_sftb_dnr0, + egc_sftb_upl0, egc_sftb_dnl0 +}; +static const PC98EGCSFTW egc_sftw[6] = { + egc_sftw_upn0, egc_sftw_dnn0, + egc_sftw_upr0, egc_sftw_dnr0, + egc_sftw_upl0, egc_sftw_dnl0 +}; + +static void +egc_shiftinput_byte(egc_t *dev, uint32_t ext) +{ + if (dev->stack <= 16) { + if (dev->srcbit >= 8) + dev->srcbit -= 8; + else { + dev->stack += (8 - dev->srcbit); + dev->srcbit = 0; + } + if (!(dev->sft & 0x1000)) + dev->inptr++; + else + dev->inptr--; + } + dev->srcmask.b[ext] = 0xff; + (*egc_sftb[dev->func])(dev, ext); +} + +static void +egc_shiftinput_incw(egc_t *dev) +{ + if (dev->stack <= 16) { + dev->inptr += 2; + if (dev->srcbit >= 8) + dev->outptr++; + + dev->stack += (16 - dev->srcbit); + dev->srcbit = 0; + } + dev->srcmask.w = 0xffff; + (*egc_sftw[dev->func])(dev); +} + +static void +egc_shiftinput_decw(egc_t *dev) +{ + if (dev->stack <= 16) { + dev->inptr -= 2; + if (dev->srcbit >= 8) + dev->outptr--; + + dev->stack += (16 - dev->srcbit); + dev->srcbit = 0; + } + dev->srcmask.w = 0xffff; + (*egc_sftw[dev->func])(dev); +} + +/* operation */ + +#define PC98EGC_OPE_SHIFTB \ + do { \ + if (dev->ope & 0x400) { \ + dev->inptr[ 0] = (uint8_t)value; \ + dev->inptr[ 4] = (uint8_t)value; \ + dev->inptr[ 8] = (uint8_t)value; \ + dev->inptr[12] = (uint8_t)value; \ + egc_shiftinput_byte(dev, addr & 1); \ + } \ + } while (0) + +#define PC98EGC_OPE_SHIFTW \ + do { \ + if (dev->ope & 0x400) { \ + if (!(dev->sft & 0x1000)) { \ + dev->inptr[ 0] = (uint8_t)value; \ + dev->inptr[ 1] = (uint8_t)(value >> 8); \ + dev->inptr[ 4] = (uint8_t)value; \ + dev->inptr[ 5] = (uint8_t)(value >> 8); \ + dev->inptr[ 8] = (uint8_t)value; \ + dev->inptr[ 9] = (uint8_t)(value >> 8); \ + dev->inptr[12] = (uint8_t)value; \ + dev->inptr[13] = (uint8_t)(value >> 8); \ + egc_shiftinput_incw(dev); \ + } else { \ + dev->inptr[-1] = (uint8_t)value; \ + dev->inptr[ 0] = (uint8_t)(value >> 8); \ + dev->inptr[ 3] = (uint8_t)value; \ + dev->inptr[ 4] = (uint8_t)(value >> 8); \ + dev->inptr[ 7] = (uint8_t)value; \ + dev->inptr[ 8] = (uint8_t)(value >> 8); \ + dev->inptr[11] = (uint8_t)value; \ + dev->inptr[12] = (uint8_t)(value >> 8); \ + egc_shiftinput_decw(dev); \ + } \ + } \ + } while (0) + +static uint64_t +egc_ope_00(egc_t *dev, uint8_t ope, uint32_t addr) +{ + return 0; +} + +static uint64_t +egc_ope_0f(egc_t *dev, uint8_t ope, uint32_t addr) +{ + dev->vram_data.d[0] = ~dev->vram_src.d[0]; + dev->vram_data.d[1] = ~dev->vram_src.d[1]; + return dev->vram_data.q; +} + +static uint64_t +egc_ope_c0(egc_t *dev, uint8_t ope, uint32_t addr) +{ + egcquad_t dst; + + dst.w[0] = *(uint16_t *)(&dev->vram_b[addr]); + dst.w[1] = *(uint16_t *)(&dev->vram_r[addr]); + dst.w[2] = *(uint16_t *)(&dev->vram_g[addr]); + dst.w[3] = *(uint16_t *)(&dev->vram_e[addr]); + dev->vram_data.d[0] = (dev->vram_src.d[0] & dst.d[0]); + dev->vram_data.d[1] = (dev->vram_src.d[1] & dst.d[1]); + return dev->vram_data.q; +} + +static uint64_t +egc_ope_f0(egc_t *dev, uint8_t ope, uint32_t addr) +{ + return dev->vram_src.q; +} + +static uint64_t +egc_ope_fc(egc_t *dev, uint8_t ope, uint32_t addr) +{ + egcquad_t dst; + + dst.w[0] = *(uint16_t *)(&dev->vram_b[addr]); + dst.w[1] = *(uint16_t *)(&dev->vram_r[addr]); + dst.w[2] = *(uint16_t *)(&dev->vram_g[addr]); + dst.w[3] = *(uint16_t *)(&dev->vram_e[addr]); + dev->vram_data.d[0] = dev->vram_src.d[0]; + dev->vram_data.d[0] |= ((~dev->vram_src.d[0]) & dst.d[0]); + dev->vram_data.d[1] = dev->vram_src.d[1]; + dev->vram_data.d[1] |= ((~dev->vram_src.d[1]) & dst.d[1]); + return dev->vram_data.q; +} + +static uint64_t +egc_ope_ff(egc_t *dev, uint8_t ope, uint32_t addr) +{ + return ~0; +} + +static uint64_t +egc_ope_nd(egc_t *dev, uint8_t ope, uint32_t addr) +{ + egcquad_t pat; + + switch(dev->fgbg & 0x6000) { + case 0x2000: + pat.d[0] = dev->bgc.d[0]; + pat.d[1] = dev->bgc.d[1]; + break; + case 0x4000: + pat.d[0] = dev->fgc.d[0]; + pat.d[1] = dev->fgc.d[1]; + break; + default: + if ((dev->ope & 0x0300) == 0x0100) { + pat.d[0] = dev->vram_src.d[0]; + pat.d[1] = dev->vram_src.d[1]; + } else { + pat.d[0] = dev->patreg.d[0]; + pat.d[1] = dev->patreg.d[1]; + } + break; + } + dev->vram_data.d[0] = 0; + dev->vram_data.d[1] = 0; + if (ope & 0x80) { + dev->vram_data.d[0] |= (pat.d[0] & dev->vram_src.d[0]); + dev->vram_data.d[1] |= (pat.d[1] & dev->vram_src.d[1]); + } + if (ope & 0x40) { + dev->vram_data.d[0] |= ((~pat.d[0]) & dev->vram_src.d[0]); + dev->vram_data.d[1] |= ((~pat.d[1]) & dev->vram_src.d[1]); + } + if (ope & 0x08) { + dev->vram_data.d[0] |= (pat.d[0] & (~dev->vram_src.d[0])); + dev->vram_data.d[1] |= (pat.d[1] & (~dev->vram_src.d[1])); + } + if (ope & 0x04) { + dev->vram_data.d[0] |= ((~pat.d[0]) & (~dev->vram_src.d[0])); + dev->vram_data.d[1] |= ((~pat.d[1]) & (~dev->vram_src.d[1])); + } + return dev->vram_data.q; +} + +static uint64_t +egc_ope_np(egc_t *dev, uint8_t ope, uint32_t addr) +{ + egcquad_t dst; + + dst.w[0] = *(uint16_t *)(&dev->vram_b[addr]); + dst.w[1] = *(uint16_t *)(&dev->vram_r[addr]); + dst.w[2] = *(uint16_t *)(&dev->vram_g[addr]); + dst.w[3] = *(uint16_t *)(&dev->vram_e[addr]); + + dev->vram_data.d[0] = 0; + dev->vram_data.d[1] = 0; + if (ope & 0x80) { + dev->vram_data.d[0] |= (dev->vram_src.d[0] & dst.d[0]); + dev->vram_data.d[1] |= (dev->vram_src.d[1] & dst.d[1]); + } + if (ope & 0x20) { + dev->vram_data.d[0] |= (dev->vram_src.d[0] & (~dst.d[0])); + dev->vram_data.d[1] |= (dev->vram_src.d[1] & (~dst.d[1])); + } + if (ope & 0x08) { + dev->vram_data.d[0] |= ((~dev->vram_src.d[0]) & dst.d[0]); + dev->vram_data.d[1] |= ((~dev->vram_src.d[1]) & dst.d[1]); + } + if (ope & 0x02) { + dev->vram_data.d[0] |= ((~dev->vram_src.d[0]) & (~dst.d[0])); + dev->vram_data.d[1] |= ((~dev->vram_src.d[1]) & (~dst.d[1])); + } + return dev->vram_data.q; +} + +static uint64_t +egc_ope_xx(egc_t *dev, uint8_t ope, uint32_t addr) +{ + egcquad_t pat; + egcquad_t dst; + + switch(dev->fgbg & 0x6000) { + case 0x2000: + pat.d[0] = dev->bgc.d[0]; + pat.d[1] = dev->bgc.d[1]; + break; + case 0x4000: + pat.d[0] = dev->fgc.d[0]; + pat.d[1] = dev->fgc.d[1]; + break; + default: + if ((dev->ope & 0x0300) == 0x0100) { + pat.d[0] = dev->vram_src.d[0]; + pat.d[1] = dev->vram_src.d[1]; + } else { + pat.d[0] = dev->patreg.d[0]; + pat.d[1] = dev->patreg.d[1]; + } + break; + } + dst.w[0] = *(uint16_t *)(&dev->vram_b[addr]); + dst.w[1] = *(uint16_t *)(&dev->vram_r[addr]); + dst.w[2] = *(uint16_t *)(&dev->vram_g[addr]); + dst.w[3] = *(uint16_t *)(&dev->vram_e[addr]); + + dev->vram_data.d[0] = 0; + dev->vram_data.d[1] = 0; + if (ope & 0x80) { + dev->vram_data.d[0] |= (pat.d[0] & dev->vram_src.d[0] & dst.d[0]); + dev->vram_data.d[1] |= (pat.d[1] & dev->vram_src.d[1] & dst.d[1]); + } + if (ope & 0x40) { + dev->vram_data.d[0] |= ((~pat.d[0]) & dev->vram_src.d[0] & dst.d[0]); + dev->vram_data.d[1] |= ((~pat.d[1]) & dev->vram_src.d[1] & dst.d[1]); + } + if (ope & 0x20) { + dev->vram_data.d[0] |= (pat.d[0] & dev->vram_src.d[0] & (~dst.d[0])); + dev->vram_data.d[1] |= (pat.d[1] & dev->vram_src.d[1] & (~dst.d[1])); + } + if (ope & 0x10) { + dev->vram_data.d[0] |= ((~pat.d[0]) & dev->vram_src.d[0] & (~dst.d[0])); + dev->vram_data.d[1] |= ((~pat.d[1]) & dev->vram_src.d[1] & (~dst.d[1])); + } + if (ope & 0x08) { + dev->vram_data.d[0] |= (pat.d[0] & (~dev->vram_src.d[0]) & dst.d[0]); + dev->vram_data.d[1] |= (pat.d[1] & (~dev->vram_src.d[1]) & dst.d[1]); + } + if (ope & 0x04) { + dev->vram_data.d[0] |= ((~pat.d[0]) & (~dev->vram_src.d[0]) & dst.d[0]); + dev->vram_data.d[1] |= ((~pat.d[1]) & (~dev->vram_src.d[1]) & dst.d[1]); + } + if (ope & 0x02) { + dev->vram_data.d[0] |= (pat.d[0] & (~dev->vram_src.d[0]) & (~dst.d[0])); + dev->vram_data.d[1] |= (pat.d[1] & (~dev->vram_src.d[1]) & (~dst.d[1])); + } + if (ope & 0x01) { + dev->vram_data.d[0] |= ((~pat.d[0]) & (~dev->vram_src.d[0]) & (~dst.d[0])); + dev->vram_data.d[1] |= ((~pat.d[1]) & (~dev->vram_src.d[1]) & (~dst.d[1])); + } + return dev->vram_data.q; +} + +typedef uint64_t (*PC98EGCOPEFN)(egc_t *dev, uint8_t ope, uint32_t addr); + +static const PC98EGCOPEFN egc_opefn[256] = { + egc_ope_00, egc_ope_xx, egc_ope_xx, egc_ope_np, + egc_ope_xx, egc_ope_nd, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_nd, egc_ope_xx, + egc_ope_np, egc_ope_xx, egc_ope_xx, egc_ope_0f, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_np, egc_ope_xx, egc_ope_xx, egc_ope_np, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_np, egc_ope_xx, egc_ope_xx, egc_ope_np, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_nd, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_nd, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_nd, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_nd, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_nd, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_nd, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_nd, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_nd, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_c0, egc_ope_xx, egc_ope_xx, egc_ope_np, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_np, egc_ope_xx, egc_ope_xx, egc_ope_np, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_xx, egc_ope_xx, + egc_ope_f0, egc_ope_xx, egc_ope_xx, egc_ope_np, + egc_ope_xx, egc_ope_nd, egc_ope_xx, egc_ope_xx, + egc_ope_xx, egc_ope_xx, egc_ope_nd, egc_ope_xx, + egc_ope_fc, egc_ope_xx, egc_ope_xx, egc_ope_ff +}; + +static uint64_t +egc_opeb(egc_t *dev, uint32_t addr, uint8_t value) +{ + uint32_t tmp; + + dev->mask2.w = dev->mask.w; + switch(dev->ope & 0x1800) { + case 0x0800: + PC98EGC_OPE_SHIFTB; + dev->mask2.w &= dev->srcmask.w; + tmp = dev->ope & 0xff; + return (*egc_opefn[tmp])(dev, (uint8_t)tmp, addr & (~1)); + case 0x1000: + switch(dev->fgbg & 0x6000) { + case 0x2000: + return dev->bgc.q; + case 0x4000: + return dev->fgc.q; + default: + PC98EGC_OPE_SHIFTB; + dev->mask2.w &= dev->srcmask.w; + return dev->vram_src.q; + } + break; + default: + tmp = value & 0xff; + tmp = tmp | (tmp << 8); + dev->vram_data.w[0] = (uint16_t)tmp; + dev->vram_data.w[1] = (uint16_t)tmp; + dev->vram_data.w[2] = (uint16_t)tmp; + dev->vram_data.w[3] = (uint16_t)tmp; + return dev->vram_data.q; + } +} + +static uint64_t +egc_opew(egc_t *dev, uint32_t addr, uint16_t value) +{ + uint32_t tmp; + + dev->mask2.w = dev->mask.w; + switch(dev->ope & 0x1800) { + case 0x0800: + PC98EGC_OPE_SHIFTW; + dev->mask2.w &= dev->srcmask.w; + tmp = dev->ope & 0xff; + return (*egc_opefn[tmp])(dev, (uint8_t)tmp, addr); + case 0x1000: + switch(dev->fgbg & 0x6000) { + case 0x2000: + return dev->bgc.q; + case 0x4000: + return dev->fgc.q; + default: + PC98EGC_OPE_SHIFTW; + dev->mask2.w &= dev->srcmask.w; + return dev->vram_src.q; + } + break; + default: + dev->vram_data.w[0] = (uint16_t)value; + dev->vram_data.w[1] = (uint16_t)value; + dev->vram_data.w[2] = (uint16_t)value; + dev->vram_data.w[3] = (uint16_t)value; + return dev->vram_data.q; + } +} + +/* memory */ + +static const uint16_t egc_maskword[16][4] = { + {0x0000, 0x0000, 0x0000, 0x0000}, {0xffff, 0x0000, 0x0000, 0x0000}, + {0x0000, 0xffff, 0x0000, 0x0000}, {0xffff, 0xffff, 0x0000, 0x0000}, + {0x0000, 0x0000, 0xffff, 0x0000}, {0xffff, 0x0000, 0xffff, 0x0000}, + {0x0000, 0xffff, 0xffff, 0x0000}, {0xffff, 0xffff, 0xffff, 0x0000}, + {0x0000, 0x0000, 0x0000, 0xffff}, {0xffff, 0x0000, 0x0000, 0xffff}, + {0x0000, 0xffff, 0x0000, 0xffff}, {0xffff, 0xffff, 0x0000, 0xffff}, + {0x0000, 0x0000, 0xffff, 0xffff}, {0xffff, 0x0000, 0xffff, 0xffff}, + {0x0000, 0xffff, 0xffff, 0xffff}, {0xffff, 0xffff, 0xffff, 0xffff} +}; + +uint8_t +egc_mem_readb(egc_t *dev, uint32_t addr1) +{ + uint32_t addr = addr1 & 0x7fff; + uint32_t ext = addr1 & 1; + + dev->lastvram.b[0][ext] = dev->vram_b[addr]; + dev->lastvram.b[1][ext] = dev->vram_r[addr]; + dev->lastvram.b[2][ext] = dev->vram_g[addr]; + dev->lastvram.b[3][ext] = dev->vram_e[addr]; + + if (!(dev->ope & 0x400)) { + dev->inptr[0] = dev->lastvram.b[0][ext]; + dev->inptr[4] = dev->lastvram.b[1][ext]; + dev->inptr[8] = dev->lastvram.b[2][ext]; + dev->inptr[12] = dev->lastvram.b[3][ext]; + egc_shiftinput_byte(dev, ext); + } + if ((dev->ope & 0x0300) == 0x0100) { + dev->patreg.b[0][ext] = dev->vram_b[addr]; + dev->patreg.b[1][ext] = dev->vram_r[addr]; + dev->patreg.b[2][ext] = dev->vram_g[addr]; + dev->patreg.b[3][ext] = dev->vram_e[addr]; + } + if (!(dev->ope & 0x2000)) { + int pl = (dev->fgbg >> 8) & 3; + if (!(dev->ope & 0x400)) + return dev->vram_src.b[pl][ext]; + else + return dev->vram_ptr[addr | (0x8000 * pl)]; + } + return dev->vram_ptr[addr1]; +} + +uint16_t +egc_mem_readw(egc_t *dev, uint32_t addr1) +{ + uint32_t addr = addr1 & 0x7fff; + uint16_t value; + + if (!(addr & 1)) { + dev->lastvram.w[0] = *(uint16_t *)(&dev->vram_b[addr]); + dev->lastvram.w[1] = *(uint16_t *)(&dev->vram_r[addr]); + dev->lastvram.w[2] = *(uint16_t *)(&dev->vram_g[addr]); + dev->lastvram.w[3] = *(uint16_t *)(&dev->vram_e[addr]); + + if (!(dev->ope & 0x400)) { + if (!(dev->sft & 0x1000)) { + dev->inptr[ 0] = dev->lastvram.b[0][0]; + dev->inptr[ 1] = dev->lastvram.b[0][1]; + dev->inptr[ 4] = dev->lastvram.b[1][0]; + dev->inptr[ 5] = dev->lastvram.b[1][1]; + dev->inptr[ 8] = dev->lastvram.b[2][0]; + dev->inptr[ 9] = dev->lastvram.b[2][1]; + dev->inptr[12] = dev->lastvram.b[3][0]; + dev->inptr[13] = dev->lastvram.b[3][1]; + egc_shiftinput_incw(dev); + } else { + dev->inptr[-1] = dev->lastvram.b[0][0]; + dev->inptr[ 0] = dev->lastvram.b[0][1]; + dev->inptr[ 3] = dev->lastvram.b[1][0]; + dev->inptr[ 4] = dev->lastvram.b[1][1]; + dev->inptr[ 7] = dev->lastvram.b[2][0]; + dev->inptr[ 8] = dev->lastvram.b[2][1]; + dev->inptr[11] = dev->lastvram.b[3][0]; + dev->inptr[12] = dev->lastvram.b[3][1]; + egc_shiftinput_decw(dev); + } + } + if ((dev->ope & 0x0300) == 0x0100) { + dev->patreg.d[0] = dev->lastvram.d[0]; + dev->patreg.d[1] = dev->lastvram.d[1]; + } + if (!(dev->ope & 0x2000)) { + int pl = (dev->fgbg >> 8) & 3; + if (!(dev->ope & 0x400)) + return dev->vram_src.w[pl]; + else + return *(uint16_t *)(&dev->vram_ptr[addr | (0x8000 * pl)]); + } + return *(uint16_t *)(&dev->vram_ptr[addr1]); + } else if (!(dev->sft & 0x1000)) { + value = egc_mem_readb(dev, addr1); + value |= (egc_mem_readb(dev, addr1 + 1) << 8); + return value; + } else { + value = (egc_mem_readb(dev, addr1) << 8); + value |= egc_mem_readb(dev, addr1 + 1); + return value; + } +} + +void +egc_mem_writeb(egc_t *dev, uint32_t addr1, uint8_t value) +{ + uint32_t addr = addr1 & 0x7fff; + uint32_t ext = addr1 & 1; + egcquad_t data; + + if ((dev->ope & 0x0300) == 0x0200) { + dev->patreg.b[0][ext] = dev->vram_b[addr]; + dev->patreg.b[1][ext] = dev->vram_r[addr]; + dev->patreg.b[2][ext] = dev->vram_g[addr]; + dev->patreg.b[3][ext] = dev->vram_e[addr]; + } + data.q = egc_opeb(dev, addr, value); + if (dev->mask2.b[ext]) { + if (!(dev->access & 1)) { + dev->vram_b[addr] &= ~dev->mask2.b[ext]; + dev->vram_b[addr] |= data.b[0][ext] & dev->mask2.b[ext]; + } + if (!(dev->access & 2)) { + dev->vram_r[addr] &= ~dev->mask2.b[ext]; + dev->vram_r[addr] |= data.b[1][ext] & dev->mask2.b[ext]; + } + if (!(dev->access & 4)) { + dev->vram_g[addr] &= ~dev->mask2.b[ext]; + dev->vram_g[addr] |= data.b[2][ext] & dev->mask2.b[ext]; + } + if (!(dev->access & 8)) { + dev->vram_e[addr] &= ~dev->mask2.b[ext]; + dev->vram_e[addr] |= data.b[3][ext] & dev->mask2.b[ext]; + } + } +} + +void +egc_mem_writew(egc_t *dev, uint32_t addr1, uint16_t value) +{ + uint32_t addr = addr1 & 0x7fff; + egcquad_t data; + + if (!(addr & 1)) { + if ((dev->ope & 0x0300) == 0x0200) { + dev->patreg.w[0] = *(uint16_t *)(&dev->vram_b[addr]); + dev->patreg.w[1] = *(uint16_t *)(&dev->vram_r[addr]); + dev->patreg.w[2] = *(uint16_t *)(&dev->vram_g[addr]); + dev->patreg.w[3] = *(uint16_t *)(&dev->vram_e[addr]); + } + data.q = egc_opew(dev, addr, value); + if (dev->mask2.w) { + if (!(dev->access & 1)) { + *(uint16_t *)(&dev->vram_b[addr]) &= ~dev->mask2.w; + *(uint16_t *)(&dev->vram_b[addr]) |= data.w[0] & dev->mask2.w; + } + if (!(dev->access & 2)) { + *(uint16_t *)(&dev->vram_r[addr]) &= ~dev->mask2.w; + *(uint16_t *)(&dev->vram_r[addr]) |= data.w[1] & dev->mask2.w; + } + if (!(dev->access & 4)) { + *(uint16_t *)(&dev->vram_g[addr]) &= ~dev->mask2.w; + *(uint16_t *)(&dev->vram_g[addr]) |= data.w[2] & dev->mask2.w; + } + if (!(dev->access & 8)) { + *(uint16_t *)(&dev->vram_e[addr]) &= ~dev->mask2.w; + *(uint16_t *)(&dev->vram_e[addr]) |= data.w[3] & dev->mask2.w; + } + } + } else if (!(dev->sft & 0x1000)) { + egc_mem_writeb(s, addr1, value & 0xff); + egc_mem_writeb(s, addr1 + 1, (value >> 8) & 0xff); + } else { + egc_mem_writeb(s, addr1, (value >> 8) & 0xff); + egc_mem_writeb(s, addr1 + 1, value & 0xff); + } +} + +/* i/o */ + +void +egc_ioport_writeb(uint16_t addr, uint8_t value, void *priv) +{ + /* ioport 0x4a0 - 0x4af */ + egc_t *dev = (egc_t *)priv; + pc98x1_vid_t *vid = (pc98x1_vid_t *)dev->vid; + + if (!((vid->grcg_mode & GRCG_CG_MODE) && vid->mode2[MODE2_EGC])) + return; + + switch (addr) { + case 0x4a0: + dev->access &= 0xff00; + dev->access |= value; + break; + case 0x4a1: + dev->access &= 0x00ff; + dev->access |= value << 8; + break; + case 0x4a2: + dev->fgbg &= 0xff00; + dev->fgbg |= value; + break; + case 0x4a3: + dev->fgbg &= 0x00ff; + dev->fgbg |= value << 8; + break; + case 0x4a4: + dev->ope &= 0xff00; + dev->ope |= value; + break; + case 0x4a5: + dev->ope &= 0x00ff; + dev->ope |= value << 8; + break; + case 0x4a6: + dev->fg &= 0xff00; + dev->fg |= value; + dev->fgc.d[0] = *(uint32_t *)(egc_maskword[value & 0x0f] + 0); + dev->fgc.d[1] = *(uint32_t *)(egc_maskword[value & 0x0f] + 2); + break; + case 0x4a7: + dev->fg &= 0x00ff; + dev->fg |= value << 8; + break; + case 0x4a8: + if (!(dev->fgbg & 0x6000)) + dev->mask.b[0] = value; + break; + case 0x4a9: + if (!(dev->fgbg & 0x6000)) + dev->mask.b[1] = value; + break; + case 0x4aa: + dev->bg &= 0xff00; + dev->bg |= value; + dev->bgc.d[0] = *(uint32_t *)(egc_maskword[value & 0x0f] + 0); + dev->bgc.d[1] = *(uint32_t *)(egc_maskword[value & 0x0f] + 2); + break; + case 0x4ab: + dev->bg &= 0x00ff; + dev->bg |= value << 8; + break; + case 0x4ac: + dev->sft &= 0xff00; + dev->sft |= value; + egc_shift(dev); + dev->srcmask.w = 0xffff; + break; + case 0x4ad: + dev->sft &= 0x00ff; + dev->sft |= value << 8; + egc_shift(dev); + dev->srcmask.w = 0xffff; + break; + case 0x4ae: + dev->leng &= 0xff00; + dev->leng |= value; + egc_shift(dev); + dev->srcmask.w = 0xffff; + break; + case 0x4af: + dev->leng &= 0x00ff; + dev->leng |= value << 8; + egc_shift(dev); + dev->srcmask.w = 0xffff; + break; + } +} + +void +egc_ioport_writew(uint16_t addr, uint16_t value, void *priv) +{ + /* ioport 0x4a0 - 0x4af */ + egc_t *dev = (egc_t *)priv; + pc98x1_vid_t *vid = (pc98x1_vid_t *)dev->vid; + + if (!((vid->grcg_mode & GRCG_CG_MODE) && vid->mode2[MODE2_EGC])) + return; + + switch(addr) { + case 0x4a0: + dev->access = value; + break; + case 0x4a2: + dev->fgbg = value; + break; + case 0x4a4: + dev->ope = value; + break; + case 0x4a6: + dev->fg = value; + dev->fgc.d[0] = *(uint32_t *)(egc_maskword[value & 0x0f] + 0); + dev->fgc.d[1] = *(uint32_t *)(egc_maskword[value & 0x0f] + 2); + break; + case 0x4a8: + if (!(dev->fgbg & 0x6000)) + dev->mask.w = value; + break; + case 0x4aa: + dev->bg = value; + dev->bgc.d[0] = *(uint32_t *)(egc_maskword[value & 0x0f] + 0); + dev->bgc.d[1] = *(uint32_t *)(egc_maskword[value & 0x0f] + 2); + break; + case 0x4ac: + dev->sft = value; + egc_shift(dev); + dev->srcmask.w = 0xffff; + break; + case 0x4ae: + dev->leng = value; + egc_shift(dev); + dev->srcmask.w = 0xffff; + break; + } +} + +/* interface */ + +void +egc_set_vram(egc_t *dev, uint8_t *vram_ptr) +{ + dev->vram_ptr = vram_ptr; + dev->vram_b = vram_ptr + 0x00000; + dev->vram_r = vram_ptr + 0x08000; + dev->vram_g = vram_ptr + 0x10000; + dev->vram_e = vram_ptr + 0x18000; +} + +void +egc_reset(egc_t *dev) +{ + pc98x1_vid_t *vid = (pc98x1_vid_t *)dev->vid; + memset(dev, 0, sizeof(egc_t)); + + dev->priv = vid; + dev->access = 0xfff0; + dev->fgbg = 0x00ff; + dev->mask.w = 0xffff; + dev->leng = 0x000f; + egc_shift(dev); + dev->srcmask.w = 0xffff; +} + +void +egc_init(egc_t *dev, void *priv) +{ + dev->priv = priv; + dev->inptr = dev->buf; + dev->outptr = dev->buf; +} diff --git a/src/video/vid_upd7220.c b/src/video/vid_upd7220.c new file mode 100644 index 000000000..989663239 --- /dev/null +++ b/src/video/vid_upd7220.c @@ -0,0 +1,1045 @@ +/* + * 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 NEC uPD7220 graphic display controller. + * + * + * + * Authors: TAKEDA toshiya, + * yui/Neko Project II + * + * Copyright 2009-2023 TAKEDA, toshiya. + * Copyright 2008-2023 yui/Neko Project II. + */ +#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/device.h> +#include <86box/video.h> +#include <86box/vid_upd7220.h> +#include <86box/plat_unused.h> + +static video_timings_t timing_upd7220 = { .type = 0, .write_b = 8, .write_w = 16, .write_l = 32, .read_b = 8, .read_w = 16, .read_l = 32 }; + +/***********************************************************/ +/* NEC uPD7220 GDC (based on Neko Project 2) */ + +/* + * PC-9821 CRT sync timing (24.83KHz/400L monitor) + * + * VCLOCK = 56.43; + * VLINES = 440; +*/ + +/* draw command */ + +static const int gdc_vectdir[16][4] = { + { 0, 1, 1, 0}, { 1, 1, 1,-1}, { 1, 0, 0,-1}, { 1,-1,-1,-1}, + { 0,-1,-1, 0}, {-1,-1,-1, 1}, {-1, 0, 0, 1}, {-1, 1, 1, 1}, + { 0, 1, 1, 1}, { 1, 1, 1, 0}, { 1, 0, 1,-1}, { 1,-1, 0,-1}, + { 0,-1,-1,-1}, {-1,-1,-1, 0}, {-1, 0,-1, 1}, {-1, 1, 0, 1} +}; + +static void +upd7220_draw_pset(upd7220_t *dev, int x, int y) +{ + uint16_t dot = dev->pattern & 1; + uint32_t addr = y * 80 + (x >> 3) + 0x8000; + uint8_t bit = 0x80 >> (x & 7); + uint8_t cur = dev->vram_read(dev->priv, addr); + + dev->pattern = (dev->pattern >> 1) | (dot << 15); + + switch (dev->mod) { + case 0: /* replace */ + dev->vram_write(dev->priv, addr, (cur & ~bit) | (dot ? bit : 0)); + break; + case 1: /* complement */ + dev->vram_write(dev->priv, addr, (cur & ~bit) | ((cur ^ (dot ? 0xff : 0)) & bit)); + break; + case 2: /* reset */ + dev->vram_write(dev->priv, addr, cur & (dot ? ~bit : 0xff)); + break; + case 3: /* set */ + dev->vram_write(dev->priv, addr, cur | (dot ? bit : 0)); + break; + } + dev->dirty |= GDC_DIRTY_VRAM; +} + +static void +upd7220_draw_vectl(upd7220_t *dev) +{ + int x, i; + int step; + + dev->pattern = dev->ra[8] | (dev->ra[9] << 8); + if (dev->dc) { + x = dev->dx; + y = dev->dy; + + switch (dev->dir) { + case 0: + for (i = 0; i <= dev->dc; i++) { + step = (int)((((dev->d1 * i) / dev->dc) + 1) >> 1); + upd7220_draw_pset(dev, x + step, y++); + } + break; + case 1: + for (i = 0; i <= dev->dc; i++) { + step = (int)((((dev->d1 * i) / dev->dc) + 1) >> 1); + upd7220_draw_pset(dev, x++, y + step); + } + break; + case 2: + for (i = 0; i <= dev->dc; i++) { + step = (int)((((dev->d1 * i) / dev->dc) + 1) >> 1); + upd7220_draw_pset(dev, x++, y - step); + } + break; + case 3: + for (i = 0; i <= dev->dc; i++) { + step = (int)((((dev->d1 * i) / dev->dc) + 1) >> 1); + upd7220_draw_pset(dev, x + step, y--); + } + break; + case 4: + for (i = 0; i <= dev->dc; i++) { + step = (int)((((dev->d1 * i) / dev->dc) + 1) >> 1); + upd7220_draw_pset(dev, x - step, y--); + } + break; + case 5: + for (i = 0; i <= dev->dc; i++) { + step = (int)((((dev->d1 * i) / dev->dc) + 1) >> 1); + upd7220_draw_pset(dev, x--, y - step); + } + break; + case 6: + for (i = 0; i <= dev->dc; i++) { + step = (int)((((dev->d1 * i) / dev->dc) + 1) >> 1); + upd7220_draw_pset(dev, x--, y + step); + } + break; + case 7: + for (i = 0; i <= dev->dc; i++) { + step = (int)((((dev->d1 * i) / dev->dc) + 1) >> 1); + upd7220_draw_pset(dev, x - step, y++); + } + break; + } + } else + upd7220_draw_pset(dev, dev->dx, dev->dy); +} + +static void +upd7220_draw_vectt(upd7220_t *dev) +{ + int vx1 = gdc_vectdir[dev->dir][0]; + int vy1 = gdc_vectdir[dev->dir][1]; + int vx2 = gdc_vectdir[dev->dir][2]; + int vy2 = gdc_vectdir[dev->dir][3]; + int muly = dev->zw + 1; + uint16_t draw = dev->ra[8] | (dev->ra[9] << 8); + int cx, cy, xrem, mulx; + + if (dev->sl) { + draw = (draw & 0x0001 ? 0x8000 : 0) | (draw & 0x0002 ? 0x4000 : 0) | + (draw & 0x0004 ? 0x2000 : 0) | (draw & 0x0008 ? 0x1000 : 0) | + (draw & 0x0010 ? 0x0800 : 0) | (draw & 0x0020 ? 0x0400 : 0) | + (draw & 0x0040 ? 0x0200 : 0) | (draw & 0x0080 ? 0x0100 : 0) | + (draw & 0x0100 ? 0x0080 : 0) | (draw & 0x0200 ? 0x0040 : 0) | + (draw & 0x0400 ? 0x0020 : 0) | (draw & 0x0800 ? 0x0010 : 0) | + (draw & 0x1000 ? 0x0008 : 0) | (draw & 0x2000 ? 0x0004 : 0) | + (draw & 0x8000 ? 0x0002 : 0) | (draw & 0x8000 ? 0x0001 : 0); + } + dev->pattern = 0xffff; + while (muly--) { + cx = dev->dx; + cy = dev->dy; + xrem = dev->d; + while (xrem--) { + mulx = dev->zw + 1; + if (draw & 1) { + draw >>= 1; + draw |= 0x8000; + while (mulx--) { + upd7220_draw_pset(dev, cx, cy); + cx += vx1; + cy += vy1; + } + } else { + draw >>= 1; + while (mulx--) { + cx += vx1; + cy += vy1; + } + } + } + dev->dx += vx2; + dev->dy += vy2; + } + dev->ead = ((dev->dx >> 4) + dev->dy) * dev->pitch; + dev->dad = dev->dx & 0x0f; +} + +static void +upd7220_draw_vectc(upd7220_t *dev) +{ + int m = (dev->d * 10000 + 14141) / 14142; + int t = (dev->dc > m) ? m : dev->dc; + int i, c; + + dev->pattern = dev->ra[8] | (dev->ra[9] << 8); + if (m) { + switch (dev->dir) { + case 0: + for (i = dev->dm; i <= t; i++) { + c = (dev->rt[(i << GDC_TABLEBIT) / m] * dev->d); + c = (c + (1 << (GDC_MULBIT - 1))) >> GDC_MULBIT; + upd7220_draw_pset(dev, (dev->dx + c), (dev->dy + i)); + } + break; + case 1: + for (i = dev->dm; i <= t; i++) { + c = (dev->rt[(i << GDC_TABLEBIT) / m] * dev->d); + c = (c + (1 << (GDC_MULBIT - 1))) >> GDC_MULBIT; + upd7220_draw_pset(dev, (dev->dx + i), (dev->dy + c)); + } + break; + case 2: + for (i = dev->dm; i <= t; i++) { + c = (dev->rt[(i << GDC_TABLEBIT) / m] * dev->d); + c = (c + (1 << (GDC_MULBIT - 1))) >> GDC_MULBIT; + upd7220_draw_pset(dev, (dev->dx + i), (dev->dy - c)); + } + break; + case 3: + for (i = dev->dm; i <= t; i++) { + c = (dev->rt[(i << GDC_TABLEBIT) / m] * dev->d); + c = (c + (1 << (GDC_MULBIT - 1))) >> GDC_MULBIT; + upd7220_draw_pset(dev, (dev->dx + c), (dev->dy - i)); + } + break; + case 4: + for (i = dev->dm; i <= t; i++) { + c = (dev->rt[(i << GDC_TABLEBIT) / m] * dev->d); + c = (c + (1 << (GDC_MULBIT - 1))) >> GDC_MULBIT; + upd7220_draw_pset(dev, (dev->dx - c), (dev->dy - i)); + } + break; + case 5: + for (i = dev->dm; i <= t; i++) { + c = (dev->rt[(i << GDC_TABLEBIT) / m] * dev->d); + c = (c + (1 << (GDC_MULBIT - 1))) >> GDC_MULBIT; + upd7220_draw_pset(dev, (dev->dx - i), (dev->dy - c)); + } + break; + case 6: + for (i = dev->dm; i <= t; i++) { + c = (dev->rt[(i << GDC_TABLEBIT) / m] * dev->d); + c = (c + (1 << (GDC_MULBIT - 1))) >> GDC_MULBIT; + upd7220_draw_pset(dev, (dev->dx - i), (dev->dy + c)); + } + break; + case 7: + for (i = dev->dm; i <= t; i++) { + c = (dev->rt[(i << GDC_TABLEBIT) / m] * dev->d); + c = (c + (1 << (GDC_MULBIT - 1))) >> GDC_MULBIT; + upd7220_draw_pset(dev, (dev->dx - c), (dev->dy + i)); + } + break; + } + } else + upd7220_draw_pset(dev, dev->dx, dev->dy); +} + +static void +upd7220_draw_vectr(upd7220_t *dev) +{ + int vx1 = gdc_vectdir[dev->dir][0]; + int vy1 = gdc_vectdir[dev->dir][1]; + int vx2 = gdc_vectdir[dev->dir][2]; + int vy2 = gdc_vectdir[dev->dir][3]; + int i; + + dev->pattern = dev->ra[8] | (dev->ra[9] << 8); + for (i = 0; i < dev->d; i++) { + upd7220_draw_pset(dev, dev->dx, dev->dy); + dev->dx += vx1; + dev->dy += vy1; + } + for (i = 0; i < dev->d2; i++) { + upd7220_draw_pset(dev, dev->dx, dev->dy); + dev->dx += vx2; + dev->dy += vy2; + } + for (i = 0; i < dev->d; i++) { + upd7220_draw_pset(dev, dev->dx, dev->dy); + dev->dx -= vx1; + dev->dy -= vy1; + } + for (i = 0; i < dev->d2; i++) { + upd7220_draw_pset(dev, dev->dx, dev->dy); + dev->dx -= vx2; + dev->dy -= vy2; + } + dev->ead = (dev->dx >> 4) + dev->dy * dev->pitch; + dev->dad = dev->dx & 0x0f; +} + +static void +upd7220_draw_text(upd7220_t *dev) +{ + int dir = dev->dir + (dev->sl ? 8 : 0); + int vx1 = gdc_vectdir[dir][0]; + int vy1 = gdc_vectdir[dir][1]; + int vx2 = gdc_vectdir[dir][2]; + int vy2 = gdc_vectdir[dir][3]; + int sx = dev->d; + int sy = dev->dc + 1; + int index = 15; + int mulx, muly, cx, cy; + int xrem; + uint8_t bit; + + while (sy--) { + muly = dev->zw + 1; + while (muly--) { + cx = dev->dx; + cy = dev->dy; + bit = dev->ra[index]; + xrem = sx; + while (xrem--) { + dev->pattern = (bit & 1) ? 0xffff : 0; + bit = (bit >> 1) | ((bit & 1) ? 0x80 : 0); + mulx = dev->zw + 1; + while (mulx--) { + upd7220_draw_pset(dev, cx, cy); + cx += vx1; + cy += vy1; + } + } + dev->dx += vx2; + dev->dy += vy2; + } + index = ((index - 1) & 7) | 8; + } + dev->ead = (dev->dx >> 4) + dev->dy * dev->pitch; + dev->dad = dev->dx & 0x0f; +} + +/* command sub */ + +static void +upd7220_update_vect(upd7220_t *dev) +{ + dev->dir = dev->vect[0] & 7; + dev->diff = gdc_vectdir[dev->dir][0] + gdc_vectdir[dev->dir][1] * dev->pitch; + dev->sl = dev->vect[0] & 0x80; + dev->dc = (dev->vect[1] | (dev->vect[ 2] << 8)) & 0x3fff; + dev->d = (dev->vect[3] | (dev->vect[ 4] << 8)) & 0x3fff; + dev->d2 = (dev->vect[5] | (dev->vect[ 6] << 8)) & 0x3fff; + dev->d1 = (dev->vect[7] | (dev->vect[ 8] << 8)) & 0x3fff; + dev->dm = (dev->vect[9] | (dev->vect[10] << 8)) & 0x3fff; +} + +static void +upd7220_reset_vect(upd7220_t *dev) +{ + dev->vect[ 1] = 0; + dev->vect[ 2] = 0; + dev->vect[ 3] = 8; + dev->vect[ 4] = 0; + dev->vect[ 5] = 8; + dev->vect[ 6] = 0; + dev->vect[ 7] = 0; + dev->vect[ 8] = 0; + dev->vect[ 9] = 0; + dev->vect[10] = 0; + upd7220_update_vect(dev); +} + +static void +upd7220_write_sub(upd7220_t *dev, uint32_t addr, uint8_t value) +{ + switch (dev->mod) { + case 0: /* replace */ + dev->vram_write(dev->priv, addr, value); + break; + case 1: /* complement */ + dev->vram_write(dev->priv, addr, dev->vram_read(dev->priv, addr) ^ value); + break; + case 2: /* reset */ + dev->vram_write(dev->priv, addr, dev->vram_read(dev->priv, addr) & ~value); + break; + case 3: /* set */ + dev->vram_write(dev->priv, addr, dev->vram_read(dev->priv addr) | value); + break; + } + dev->dirty |= GDC_DIRTY_VRAM; +} + +static uint8_t +upd7220_read_sub(upd7220_t *dev, uint32_t addr) +{ + return dev->vram_read(dev->priv, addr); +} + +static void +upd7220_fifo_write(upd7220_t *dev, uint8_t value) +{ + if (dev->data_count < GDC_BUFFERS) { + dev->data[(dev->data_write++) & (GDC_BUFFERS - 1)] = value; + dev->data_count++; + } +} + +static uint8_t +upd7220_fifo_read(upd7220_t *dev) +{ + uint8_t value; + + if (dev->data_count > 0) { + value = dev->data[(dev->data_read++) & (GDC_BUFFERS - 1)]; + dev->data_count--; + return value; + } + return 0; +} + +/* command */ + +static void +upd7220_cmd_reset(upd7220_t *dev) +{ + dev->sync[6] = 0x90; + dev->sync[7] = 0x01; + dev->zoom = dev->zr = dev->zw = 0; + dev->ra[0] = dev->ra[1] = dev->ra[2] = 0; + dev->ra[3] = 0x1e; /*0x19;*/ + dev->cs[0] = dev->cs[1] = dev->cs[2] = 0; + dev->ead = dev->dad = 0; + dev->maskl = dev->maskh = 0xff; + dev->mod = 0; + dev->start = 0; + + dev->params_count = 0; + dev->data_count = dev->data_read = dev->data_write = 0; + + dev->statreg = 0; + dev->cmdreg = -1; + dev->dirty = 0xff; +} + +static void +upd7220_cmd_sync(upd7220_t *dev) +{ + int i; + + for (i = 0; (i < 8) && (i < dev->params_count); i++) + dev->sync[i] = dev->params[i]; + + dev->cmdreg = -1; +} + +static void +upd7220_cmd_master(upd7220_t *dev) +{ + dev->cmdreg = -1; +} + +static void +upd7220_cmd_slave(upd7220_t *dev) +{ + dev->cmdreg = -1; +} + +static void +upd7220_cmd_start(upd7220_t *dev) +{ + if (!dev->start) { + dev->start = 1; + dev->dirty |= GDC_DIRTY_START; + } + dev->cmdreg = -1; +} + +static void +upd7220_cmd_stop(upd7220_t *dev) +{ + if (dev->start) { + dev->start = 0; + dev->dirty |= GDC_DIRTY_START; + } + dev->cmdreg = -1; +} + +static void +upd7220_cmd_zoom(upd7220_t *dev) +{ + uint8_t tmp; + + if (dev->params_count > 0) { + tmp = dev->params[0]; + dev->zr = tmp >> 4; + dev->zw = tmp & 0x0f; + dev->cmdreg = -1; + } +} + +static void +upd7220_cmd_scroll(upd7220_t *dev) +{ + if (dev->params_count > 0) { + if (dev->ra[dev->cmdreg & 0x0f] != dev->params[0]) { + dev->ra[dev->cmdreg & 0x0f] = dev->params[0]; + dev->dirty |= GDC_DIRTY_SCROLL; + } + if (dev->cmdreg < 0x7f) { + dev->cmdreg++; + dev->params_count = 0; + } else { + dev->cmdreg = -1; + } + } +} + +static void +upd7220_cmd_csrform(upd7220_t *dev) +{ + int i; + + for (i = 0; i < dev->params_count; i++) { + if (dev->cs[i] != dev->params[i]) { + dev->cs[i] = dev->params[i]; + dev->dirty |= GDC_DIRTY_CURSOR; + } + } + if (dev->params_count > 2) + dev->cmdreg = -1; +} + +static void +upd7220_cmd_pitch(upd7220_t *dev) +{ + if (dev->params_count > 0) { + dev->pitch = dev->params[0]; + dev->cmdreg = -1; + } +} + +static void +upd7220_cmd_lpen(upd7220_t *dev) +{ + upd7220_fifo_write(dev, dev->lad & 0xff); + upd7220_fifo_write(dev, (dev->lad >> 8) & 0xff); + upd7220_fifo_write(dev, (dev->lad >> 16) & 0xff); + dev->cmdreg = -1; +} + +static void +upd7220_cmd_vectw(upd7220_t *dev) +{ + int i; + + for (i = 0; (i < 11) && (i < dev->params_count); i++) + dev->vect[i] = dev->params[i]; + + upd7220_update_vect(dev); + dev->cmdreg = -1; +} + +static void +upd7220_cmd_vecte(upd7220_t *dev) +{ + dev->dx = ((dev->ead % dev->pitch) << 4) | (dev->dad & 0x0f); + dev->dy = dev->ead / dev->pitch; + if (!(dev->vect[0] & 0x78)) { + dev->pattern = dev->ra[8] | (dev->ra[9] << 8); + upd7220_draw_pset(dev, dev->dx, dev->dy); + } + if (dev->vect[0] & 0x08) + upd7220_draw_vectl(dev); + + if (dev->vect[0] & 0x10) + upd7220_draw_vectt(dev); + + if (dev->vect[0] & 0x20) + upd7220_draw_vectc(dev); + + if (dev->vect[0] & 0x40) + upd7220_draw_vectr(dev); + + upd7220_reset_vect(dev); + dev->statreg |= GDC_STAT_DRAW; + dev->cmdreg = -1; +} + +static void +upd7220_cmd_texte(upd7220_t *dev) +{ + dev->dx = ((dev->ead % dev->pitch) << 4) | (dev->dad & 0x0f); + dev->dy = dev->ead / dev->pitch; + if (!(dev->vect[0] & 0x78)) { + dev->pattern = dev->ra[8] | (dev->ra[9] << 8); + upd7220_draw_pset(dev, dev->dx, dev->dy); + } + if (dev->vect[0] & 0x08) + upd7220_draw_vectl(dev); + + if (dev->vect[0] & 0x10) + upd7220_draw_text(dev); + + if (dev->vect[0] & 0x20) + upd7220_draw_vectc(dev); + + if (dev->vect[0] & 0x40) + upd7220_draw_vectr(dev); + + upd7220_reset_vect(dev); + dev->statreg |= GDC_STAT_DRAW; + dev->cmdreg = -1; +} + +static void +upd7220_cmd_csrw(upd7220_t *dev) +{ + if (dev->params_count > 0) { + dev->ead = dev->params[0]; + if (dev->params_count > 1) { + dev->ead |= dev->params[1] << 8; + if (dev->params_count > 2) { + dev->ead |= dev->params[2] << 16; + dev->cmdreg = -1; + } + } + dev->dad = (dev->ead >> 20) & 0x0f; + dev->ead &= 0x3ffff; + dev->dirty |= GDC_DIRTY_CURSOR; + } +} + +static void +upd7220_cmd_csrr(upd7220_t *dev) +{ + upd7220_fifo_write(dev, dev->ead & 0xff); + upd7220_fifo_write(dev, (dev->ead >> 8) & 0xff); + upd7220_fifo_write(dev, (dev->ead >> 16) & 0x03); + upd7220_fifo_write(dev, dev->dad & 0xff); + upd7220_fifo_write(dev, (dev->dad >> 8) & 0xff); + dev->cmdreg = -1; +} + +static void +upd7220_cmd_mask(upd7220_t *dev) +{ + if (dev->params_count > 1) { + dev->maskl = dev->params[0]; + dev->maskh = dev->params[1]; + dev->cmdreg = -1; + } +} + +static void +upd7220_cmd_write(upd7220_t *dev) +{ + uint8_t l, h; + int i; + + dev->mod = dev->cmdreg & 3; + switch (dev->cmdreg & 0x18) { + case 0x00: /* low and high */ + if (dev->params_count > 1) { + l = dev->params[0] & dev->maskl; + h = dev->params[1] & dev->maskh; + for (i = 0; i < (dev->dc + 1); i++) { + upd7220_write_sub(dev, dev->ead * 2, l); + upd7220_write_sub(dev, (dev->ead * 2) + 1, h); + dev->ead += dev->diff; + } + upd7220_reset_vect(dev); + dev->cmdreg = -1; + } + break; + case 0x10: /* low byte */ + if (dev->params_count > 0) { + l = dev->params[0] & dev->maskl; + for (i = 0; i < (dev->dc + 1); i++) { + upd7220_write_sub(dev, dev->ead * 2, l); + dev->ead += dev->diff; + } + upd7220_reset_vect(dev); + dev->cmdreg = -1; + } + break; + case 0x18: /* high byte */ + if (dev->params_count > 0) { + h = dev->params[0] & dev->maskh; + for (i = 0; i < (dev->dc + 1); i++) { + upd7220_write_sub(dev, (dev->ead * 2) + 1, h); + dev->ead += dev->diff; + } + upd7220_reset_vect(dev); + dev->cmdreg = -1; + } + break; + default: /* invalid */ + dev->cmdreg = -1; + break; + } +} + +static void +upd7220_cmd_read(upd7220_t *dev) +{ + int i; + + dev->mod = dev->cmdreg & 3; + switch (dev->cmdreg & 0x18) { + case 0x00: /* low and high */ + for (i = 0; i < dev->dc; i++) { + upd7220_fifo_write(dev, upd7220_read_sub(dev, dev->ead * 2)); + upd7220_fifo_write(dev, upd7220_read_sub(dev, (dev->ead * 2) + 1)); + dev->ead += dev->diff; + } + break; + case 0x10: /* low byte */ + for (i = 0; i < dev->dc; i++) { + upd7220_fifo_write(dev, upd7220_read_sub(dev, dev->ead * 2)); + dev->ead += dev->diff; + } + break; + case 0x18: /* high byte */ + for (i = 0; i < dev->dc; i++) { + upd7220_fifo_write(dev, upd7220_read_sub(dev, dev->ead * 2 + 1)); + dev->ead += dev->diff; + } + break; + default: /* invalid */ + break; + } + upd7220_reset_vect(dev); + dev->cmdreg = -1; +} + +static void +upd7220_cmd_dmaw(upd7220_t *dev) +{ + dev->mod = dev->cmdreg & 3; + upd7220_reset_vect(upd7220); +#if 0 + dev->statreg |= GDC_STAT_DMA; +#endif + dev->cmdreg = -1; +} + +static void +upd7220_cmd_dmar(upd7220_t *dev) +{ + dev->mod = dev->cmdreg & 3; + upd7220_reset_vect(upd7220); +#if 0 + dev->statreg |= GDC_STAT_DMA; +#endif + dev->cmdreg = -1; +} + +static void +upd7220_cmd_unk_5a(upd7220_t *dev) +{ + if (dev->params_count > 2) + dev->cmdreg = -1; +} + +static void +upd7220_check_cmd(upd7220_t *dev) +{ + switch (dev->cmdreg) { + case GDC_CMD_RESET: + upd7220_cmd_reset(dev); + break; + case GDC_CMD_SYNC + 0: + case GDC_CMD_SYNC + 1: + if (dev->params_count > 7) + upd7220_cmd_sync(dev); + break; + case GDC_CMD_MASTER: + upd7220_cmd_master(dev); + break; + case GDC_CMD_SLAVE: + upd7220_cmd_slave(dev); + break; + case GDC_CMD_START: + upd7220_cmd_start(dev); + break; + case GDC_CMD_BCTRL + 0: + upd7220_cmd_stop(dev); + break; + case GDC_CMD_BCTRL + 1: + upd7220_cmd_start(dev); + break; + case GDC_CMD_ZOOM: + upd7220_cmd_zoom(dev); + break; + case GDC_CMD_SCROLL + 0: + case GDC_CMD_SCROLL + 1: + case GDC_CMD_SCROLL + 2: + case GDC_CMD_SCROLL + 3: + case GDC_CMD_SCROLL + 4: + case GDC_CMD_SCROLL + 5: + case GDC_CMD_SCROLL + 6: + case GDC_CMD_SCROLL + 7: + case GDC_CMD_TEXTW + 0: + case GDC_CMD_TEXTW + 1: + case GDC_CMD_TEXTW + 2: + case GDC_CMD_TEXTW + 3: + case GDC_CMD_TEXTW + 4: + case GDC_CMD_TEXTW + 5: + case GDC_CMD_TEXTW + 6: + case GDC_CMD_TEXTW + 7: + upd7220_cmd_scroll(dev); + break; + case GDC_CMD_CSRFORM: + upd7220_cmd_csrform(dev); + break; + case GDC_CMD_PITCH: + upd7220_cmd_pitch(dev); + break; + case GDC_CMD_LPEN: + upd7220_cmd_lpen(dev); + break; + case GDC_CMD_VECTW: + if (dev->params_count > 10) + upd7220_cmd_vectw(dev); + break; + case GDC_CMD_VECTE: + upd7220_cmd_vecte(dev); + break; + case GDC_CMD_TEXTE: + upd7220_cmd_texte(dev); + break; + case GDC_CMD_CSRW: + upd7220_cmd_csrw(dev); + break; + case GDC_CMD_CSRR: + upd7220_cmd_csrr(dev); + break; + case GDC_CMD_MASK: + upd7220_cmd_mask(dev); + break; + case GDC_CMD_WRITE + 0x00: + case GDC_CMD_WRITE + 0x01: + case GDC_CMD_WRITE + 0x02: + case GDC_CMD_WRITE + 0x03: + case GDC_CMD_WRITE + 0x08: + case GDC_CMD_WRITE + 0x09: + case GDC_CMD_WRITE + 0x0a: + case GDC_CMD_WRITE + 0x0b: + case GDC_CMD_WRITE + 0x10: + case GDC_CMD_WRITE + 0x11: + case GDC_CMD_WRITE + 0x12: + case GDC_CMD_WRITE + 0x13: + case GDC_CMD_WRITE + 0x18: + case GDC_CMD_WRITE + 0x19: + case GDC_CMD_WRITE + 0x1a: + case GDC_CMD_WRITE + 0x1b: + upd7220_cmd_write(dev); + break; + case GDC_CMD_READ + 0x00: + case GDC_CMD_READ + 0x01: + case GDC_CMD_READ + 0x02: + case GDC_CMD_READ + 0x03: + case GDC_CMD_READ + 0x08: + case GDC_CMD_READ + 0x09: + case GDC_CMD_READ + 0x0a: + case GDC_CMD_READ + 0x0b: + case GDC_CMD_READ + 0x10: + case GDC_CMD_READ + 0x11: + case GDC_CMD_READ + 0x12: + case GDC_CMD_READ + 0x13: + case GDC_CMD_READ + 0x18: + case GDC_CMD_READ + 0x19: + case GDC_CMD_READ + 0x1a: + case GDC_CMD_READ + 0x1b: + upd7220_cmd_read(dev); + break; + case GDC_CMD_DMAW + 0x00: + case GDC_CMD_DMAW + 0x01: + case GDC_CMD_DMAW + 0x02: + case GDC_CMD_DMAW + 0x03: + case GDC_CMD_DMAW + 0x08: + case GDC_CMD_DMAW + 0x09: + case GDC_CMD_DMAW + 0x0a: + case GDC_CMD_DMAW + 0x0b: + case GDC_CMD_DMAW + 0x10: + case GDC_CMD_DMAW + 0x11: + case GDC_CMD_DMAW + 0x12: + case GDC_CMD_DMAW + 0x13: + case GDC_CMD_DMAW + 0x18: + case GDC_CMD_DMAW + 0x19: + case GDC_CMD_DMAW + 0x1a: + case GDC_CMD_DMAW + 0x1b: + upd7220_cmd_dmaw(dev); + break; + case GDC_CMD_DMAR + 0x00: + case GDC_CMD_DMAR + 0x01: + case GDC_CMD_DMAR + 0x02: + case GDC_CMD_DMAR + 0x03: + case GDC_CMD_DMAR + 0x08: + case GDC_CMD_DMAR + 0x09: + case GDC_CMD_DMAR + 0x0a: + case GDC_CMD_DMAR + 0x0b: + case GDC_CMD_DMAR + 0x10: + case GDC_CMD_DMAR + 0x11: + case GDC_CMD_DMAR + 0x12: + case GDC_CMD_DMAR + 0x13: + case GDC_CMD_DMAR + 0x18: + case GDC_CMD_DMAR + 0x19: + case GDC_CMD_DMAR + 0x1a: + case GDC_CMD_DMAR + 0x1b: + upd7220_cmd_dmar(dev); + break; + case GDC_CMD_UNK_5A: + upd7220_cmd_unk_5a(dev); + break; + } +} + +static void +upd7220_process_cmd(upd7220_t *dev) +{ + switch (dev->cmdreg) { + case GDC_CMD_RESET: + upd7220_cmd_reset(dev); + break; + case GDC_CMD_SYNC + 0: + case GDC_CMD_SYNC + 1: + upd7220_cmd_sync(dev); + break; + case GDC_CMD_SCROLL + 0: + case GDC_CMD_SCROLL + 1: + case GDC_CMD_SCROLL + 2: + case GDC_CMD_SCROLL + 3: + case GDC_CMD_SCROLL + 4: + case GDC_CMD_SCROLL + 5: + case GDC_CMD_SCROLL + 6: + case GDC_CMD_SCROLL + 7: + case GDC_CMD_TEXTW + 0: + case GDC_CMD_TEXTW + 1: + case GDC_CMD_TEXTW + 2: + case GDC_CMD_TEXTW + 3: + case GDC_CMD_TEXTW + 4: + case GDC_CMD_TEXTW + 5: + case GDC_CMD_TEXTW + 6: + case GDC_CMD_TEXTW + 7: + upd7220_cmd_scroll(dev); + break; + case GDC_CMD_VECTW: + upd7220_cmd_vectw(dev); + break; + case GDC_CMD_CSRW: + upd7220_cmd_csrw(dev); + break; + } +} + +/* i/o */ + +void +upd7220_param_write(uint16_t addr, uint8_t value, void *priv) +{ + /* ioport 0x60(chr), 0xa0(gfx) */ + upd7220_t *dev = (upd7220_t *) priv; + + if (dev->cmdreg != -1) { + if (dev->params_count < 16) + dev->params[dev->params_count++] = value; + + upd7220_check_cmd(dev); + if (dev->cmdreg == -1) + dev->params_count = 0; + } +} + +uint8_t +upd7220_statreg_read(uint16_t addr, void *priv) +{ + /* ioport 0x60(chr), 0xa0(gfx) */ + upd7220_t *dev = (upd7220_t *) priv; + pc98x1_vid_t *vid = (pc98x1_vid_t *)dev->vid; + uint8_t value = dev->statreg | vid->vsync; + +#if 0 + if (dev->params_count == 0) +#endif + value |= GDC_STAT_EMPTY; + + if (dev->params_count == 16) + value |= GDC_STAT_FULL; + + if (dev->data_count > 0) + value |= GDC_STAT_DRDY; + + dev->statreg &= ~(GDC_STAT_DMA | GDC_STAT_DRAW); + /* toggle hblank bit */ + dev->statreg ^= GDC_STAT_HBLANK; + return value; +} + +void +upd7220_cmdreg_write(uint16_t addr, uint8_t value, void *priv) +{ + /* ioport 0x62(chr), 0xa2(gfx) */ + upd7220_t *dev = (upd7220_t *) priv; + + if (dev->cmdreg != -1) + upd7220_process_cmd(dev); + + dev->cmdreg = value; + dev->params_count = 0; + upd7220_check_cmd(dev); +} + +uint8_t +upd7220_data_read(uint16_t addr, void *priv) +{ + /* ioport 0x62(chr), 0xa2(gfx) */ + upd7220_t *dev = (upd7220_t *) priv; + + return upd7220_fifo_read(dev); +} + +void +upd7220_reset(upd7220_t *dev) +{ + upd7220_cmd_reset(dev); +} + +void upd7220_init(upd7220_t *dev, void *priv, + uint8_t (*vram_read)(uint32_t addr, void *priv), + void (*vram_write)(uint32_t addr, uint8_t val, void *priv)) +{ + int i; + + for (i = 0; i <= GDC_TABLEMAX; i++) + dev->rt[i] = (int)((double)(1 << GDC_MULBIT) * (1 - sqrt(1 - pow((0.70710678118654 * i) / GDC_TABLEMAX, 2)))); + + dev->priv = priv; + dev->vram_read = vram_read; + dev->vram_write = vram_write; +}