/*
 * 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.
 *
 *          IBM XGA emulation.
 *
 * Authors: TheCollector1995.
 *
 *          Copyright 2022 TheCollector1995.
 */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
//#include <86box/bswap.h>
#include <86box/86box.h>
#include <86box/io.h>
#include <86box/machine.h>
#include <86box/mem.h>
#include <86box/dma.h>
#include <86box/rom.h>
#include <86box/mca.h>
#include <86box/device.h>
#include <86box/timer.h>
#include <86box/video.h>
#include <86box/vid_xga.h>
#include <86box/vid_svga.h>
#include <86box/vid_svga_render.h>
#include <86box/vid_xga_device.h>
#include "cpu.h"
#include <86box/plat.h>
#include <86box/plat_unused.h>

#define XGA_BIOS_PATH       "roms/video/xga/XGA_37F9576_Ver200.BIN"
#define XGA2_BIOS_PATH      "roms/video/xga/xga2_v300.bin"
#define INMOS_XGA_BIOS_PATH "roms/video/xga/InMOS XGA - Fairchild NM27C256Q-150.BIN"

static video_timings_t timing_xga_isa = { .type = VIDEO_ISA, .write_b = 3, .write_w = 3, .write_l =  6, .read_b = 5, .read_w = 5, .read_l = 10 };
static video_timings_t timing_xga_mca = { .type = VIDEO_MCA, .write_b = 4, .write_w = 5, .write_l = 10, .read_b = 5, .read_w = 5, .read_l = 10 };

static void    xga_ext_outb(uint16_t addr, uint8_t val, void *priv);
static uint8_t xga_ext_inb(uint16_t addr, void *priv);

static void     xga_writew(uint32_t addr, uint16_t val, void *priv);
static uint16_t xga_readw(uint32_t addr, void *priv);

static void xga_render_4bpp(svga_t *svga);
static void xga_render_8bpp(svga_t *svga);
static void xga_render_16bpp(svga_t *svga);

static void xga_write(uint32_t addr, uint8_t val, void *priv);
static void xga_writew(uint32_t addr, uint16_t val, void *priv);
static void xga_writel(uint32_t addr, uint32_t val, void *priv);
static uint8_t xga_read(uint32_t addr, void *priv);
static uint16_t xga_readw(uint32_t addr, void *priv);
static uint32_t xga_readl(uint32_t addr, void *priv);

int xga_active = 0;

#ifdef ENABLE_XGA_LOG
int xga_do_log = ENABLE_XGA_LOG;

static void
xga_log(const char *fmt, ...)
{
    va_list ap;

    if (xga_do_log) {
        va_start(ap, fmt);
        pclog_ex(fmt, ap);
        va_end(ap);
    }
}
#else
#    define xga_log(fmt, ...)
#endif

void
svga_xga_out(uint16_t addr, uint8_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    uint8_t old;

    if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1))
        addr ^= 0x60;

    switch (addr) {
        case 0x3D4:
            svga->crtcreg = val & 0x3f;
            return;
        case 0x3D5:
            if (svga->crtcreg & 0x20)
                return;
            if ((svga->crtcreg < 7) && (svga->crtc[0x11] & 0x80))
                return;
            if ((svga->crtcreg == 7) && (svga->crtc[0x11] & 0x80))
                val = (svga->crtc[7] & ~0x10) | (val & 0x10);
            old                       = svga->crtc[svga->crtcreg];
            svga->crtc[svga->crtcreg] = val;
            if (old != val) {
                if (svga->crtcreg < 0xe || svga->crtcreg > 0x10) {
                    if ((svga->crtcreg == 0xc) || (svga->crtcreg == 0xd)) {
                        svga->fullchange = 3;
                        svga->memaddr_latch   = ((svga->crtc[0xc] << 8) | svga->crtc[0xd]) + ((svga->crtc[8] & 0x60) >> 5);
                    } else {
                        svga->fullchange = changeframecount;
                        svga_recalctimings(svga);
                    }
                }
            }
            break;

        default:
            break;
    }
    svga_out(addr, val, svga);
}

uint8_t
svga_xga_in(uint16_t addr, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    uint8_t temp;

    if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1))
        addr ^= 0x60;

    switch (addr) {
        case 0x3D4:
            temp = svga->crtcreg;
            break;
        case 0x3D5:
            if (svga->crtcreg & 0x20)
                temp = 0xff;
            else
                temp = svga->crtc[svga->crtcreg];
            break;
        default:
            temp = svga_in(addr, svga);
            break;
    }
    return temp;
}

void
xga_updatemapping(svga_t *svga)
{
    xga_t *xga = (xga_t *) svga->xga;

    xga_log("OpMode = %x, linear base = %08x, aperture cntl = %d, access mode = %x, map = %x, "
            "endian reverse = %d, a5test = %d, XGA on = %d.\n", xga->op_mode, xga->linear_base,
            xga->aperture_cntl, xga->access_mode, svga->gdcreg[6] & 0x0c,
            xga->linear_endian_reverse, xga->a5_test, xga->on);

    switch (xga->op_mode & 7) {
        case 0:
            xga_log("XGA: VGA mode address decode disabled.\n");
            mem_mapping_disable(&xga->linear_mapping);
            xga->test_stage = 0;
            xga->mapping_base = svga->mapping.base;
            break;
        case 1:
            xga_log("XGA: VGA mode address decode enabled.\n");
            mem_mapping_disable(&xga->linear_mapping);
            xga->test_stage = 0;
            mem_mapping_set_handler(&svga->mapping, svga->read, svga->readw, svga->readl, svga->write, svga->writew, svga->writel);
            switch (svga->gdcreg[6] & 0xc) {
                case 0x0: /*128k at A0000*/
                    mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x20000);
                    svga->banked_mask = 0xffff;
                    break;
                case 0x4: /*64k at A0000*/
                    mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x10000);
                    svga->banked_mask = 0xffff;
                    break;
                case 0x8: /*32k at B0000*/
                    mem_mapping_set_addr(&svga->mapping, 0xb0000, 0x08000);
                    svga->banked_mask = 0x7fff;
                    break;
                case 0xC: /*32k at B8000*/
                    mem_mapping_set_addr(&svga->mapping, 0xb8000, 0x08000);
                    svga->banked_mask = 0x7fff;
                    break;

                default:
                    break;
            }
            xga->mapping_base = svga->mapping.base;
            break;
        case 2:
            xga_log("XGA: 132-Column mode address decode disabled.\n");
            mem_mapping_disable(&xga->linear_mapping);
            xga->test_stage = 0;
            xga->mapping_base = svga->mapping.base;
            break;
        case 3:
            xga_log("XGA: 132-Column mode address decode enabled.\n");
            mem_mapping_disable(&xga->linear_mapping);
            xga->test_stage = 0;
            mem_mapping_set_handler(&svga->mapping, svga->read, svga->readw, svga->readl, svga->write, svga->writew, svga->writel);
            switch (svga->gdcreg[6] & 0xc) {
                case 0x0: /*128k at A0000*/
                    mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x20000);
                    svga->banked_mask = 0xffff;
                    break;
                case 0x4: /*64k at A0000*/
                    mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x10000);
                    svga->banked_mask = 0xffff;
                    break;
                case 0x8: /*32k at B0000*/
                    mem_mapping_set_addr(&svga->mapping, 0xb0000, 0x08000);
                    svga->banked_mask = 0x7fff;
                    break;
                case 0xC: /*32k at B8000*/
                    mem_mapping_set_addr(&svga->mapping, 0xb8000, 0x08000);
                    svga->banked_mask = 0x7fff;
                    break;

                default:
                    break;
            }
            xga->mapping_base = svga->mapping.base;
            break;
        default:
            xga_log("XGA: Extended Graphics mode, ap=%d, mapbits=%x.\n", xga->aperture_cntl, svga->gdcreg[6] & 0xc);
            switch (xga->aperture_cntl) {
                case 0:
                    xga_log("XGA: No 64KB aperture: 1MB=%x, 4MB=%x, SVGA Mapping Base=%x, mapbits=%x.\n", xga->base_addr_1mb, xga->linear_base, svga->mapping.base, svga->gdcreg[6] & 0xc);
                    if (xga->base_addr_1mb) {
                        mem_mapping_set_addr(&xga->linear_mapping, xga->base_addr_1mb, 0x100000);
                        mem_mapping_enable(&xga->linear_mapping);
                    } else if (xga->linear_base) {
                        mem_mapping_set_addr(&xga->linear_mapping, xga->linear_base, 0x400000);
                        mem_mapping_enable(&xga->linear_mapping);
                    } else
                        mem_mapping_disable(&xga->linear_mapping);

                    xga->test_stage = 0;
                    mem_mapping_set_handler(&svga->mapping, svga->read, svga->readw, svga->readl, svga->write, svga->writew, svga->writel);
                    switch (svga->gdcreg[6] & 0xc) {
                        case 0x0: /*128k at A0000*/
                            mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x20000);
                            svga->banked_mask = 0xffff;
                            break;
                        case 0x4: /*64k at A0000*/
                            mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x10000);
                            svga->banked_mask = 0xffff;
                            break;
                        case 0x8: /*32k at B0000*/
                            mem_mapping_set_addr(&svga->mapping, 0xb0000, 0x08000);
                            svga->banked_mask = 0x7fff;
                            break;
                        case 0xC: /*32k at B8000*/
                            mem_mapping_set_addr(&svga->mapping, 0xb8000, 0x08000);
                            svga->banked_mask = 0x7fff;
                            break;

                        default:
                            break;
                    }
                    xga->mapping_base = svga->mapping.base;
                    break;
                case 1:
                    mem_mapping_disable(&xga->linear_mapping);
                    xga_log("XGA: 64KB aperture at A0000, on=%d.\n", xga->on);
                    xga->test_stage = 0;
                    xga->mapping_base = 0xa0000;
                    mem_mapping_set_handler(&svga->mapping, xga_read, xga_readw, xga_readl, xga_write, xga_writew, xga_writel);
                    mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x10000);
                    xga->banked_mask = 0xffff;
                    break;
                case 2:
                    mem_mapping_disable(&xga->linear_mapping);
                    xga_log("XGA: 64KB aperture at B0000.\n");
                    xga->test_stage = 0;
                    xga->mapping_base = 0xb0000;
                    mem_mapping_set_handler(&svga->mapping, xga_read, xga_readw, xga_readl, xga_write, xga_writew, xga_writel);
                    mem_mapping_set_addr(&svga->mapping, 0xb0000, 0x10000);
                    xga->banked_mask = 0xffff;
                    break;
                default:
                    break;
            }
            break;
    }
}

static void
xga_render_blank(svga_t *svga)
{
    xga_t *xga = (xga_t *) svga->xga;

    if ((xga->displine + svga->y_add) < 0)
        return;

    if (xga->firstline_draw == 2000)
        xga->firstline_draw = xga->displine;

    xga->lastline_draw = xga->displine;

    uint32_t *line_ptr   = &svga->monitor->target_buffer->line[xga->displine + svga->y_add][svga->x_add];
    uint32_t  line_width = (uint32_t)(xga->h_disp) * sizeof(uint32_t);

    if (xga->h_disp > 0)
        memset(line_ptr, 0, line_width);
}

void
xga_recalctimings(svga_t *svga)
{
    xga_t *xga = (xga_t *) svga->xga;

    xga_log("DispCntl2=%d, lowres=%x.\n", xga->disp_cntl_2 & 7, svga->lowres);
    if (xga->on) {
        xga->h_total      = xga->htotal + 1;
        xga->v_total      = xga->vtotal + 1;
        xga->dispend      = xga->vdispend + 1;
        xga->v_syncstart  = xga->vsyncstart + 1;
        xga->split        = xga->linecmp + 1;
        xga->v_blankstart = xga->vblankstart + 1;

        xga->h_disp = (xga->hdisp + 1) << 3;
        xga->h_disp_time = xga->h_disp >> 3;

        xga->rowoffset = xga->pix_map_width;

        xga->interlace = !!(xga->disp_cntl_1 & 0x08);
        xga->rowcount  = (xga->disp_cntl_2 & 0xc0) >> 6;

        if (xga->interlace) {
            xga->v_total >>= 1;
            xga->dispend >>= 1;
            xga->v_syncstart >>= 1;
            xga->split >>= 1;
            xga->v_blankstart >>= 1;
        }

        xga->memaddr_latch = xga->disp_start_addr;

        xga_log("XGA ClkSel1 = %d, ClkSel2 = %02x, dispcntl2=%02x.\n", (xga->clk_sel_1 >> 2) & 3, xga->clk_sel_2 & 0x80, xga->disp_cntl_2 & 0xc0);
        switch ((xga->clk_sel_1 >> 2) & 3) {
            case 0:
                xga_log("HDISP VGA0 = %d, XGA = %d.\n", svga->hdisp, xga->h_disp);
                if (xga->clk_sel_2 & 0x80)
                    svga->clock_xga = (cpuclock * (double) (1ULL << 32)) / 41539000.0;
                else
                    svga->clock_xga = (cpuclock * (double) (1ULL << 32)) / 25175000.0;
                break;
            case 1:
                xga_log("HDISP VGA1 = %d, XGA = %d.\n", svga->hdisp, xga->h_disp);
                svga->clock_xga = (cpuclock * (double) (1ULL << 32)) / 28322000.0;
                break;
            case 3:
                svga->clock_xga = (cpuclock * (double) (1ULL << 32)) / 44900000.0;
                break;

            default:
                break;
        }

        svga->render_xga = xga_render_blank;
        switch (xga->disp_cntl_2 & 7) {
            case 2:
                svga->render_xga = xga_render_4bpp;
                break;
            case 3:
                svga->render_xga = xga_render_8bpp;
                break;
            case 4:
                svga->render_xga = xga_render_16bpp;
                break;

            default:
                break;
        }

        xga_log("XGA: H_TOTAL=%d, rowoffset=%x, rowcount=%x, memaddr_latch=%06x, bpp type=%d.\n", xga->h_total, xga->rowoffset, xga->rowcount, xga->memaddr_latch, xga->disp_cntl_2 & 7);
    }
}

static void
xga_ext_out_reg(xga_t *xga, svga_t *svga, uint8_t idx, uint8_t val)
{
    uint8_t index;

    switch (idx) {
        case 0x10:
            xga->htotal = (xga->htotal & 0xff00) | val;
            break;
        case 0x11:
            xga->htotal = (xga->htotal & 0xff) | (val << 8);
            svga_recalctimings(svga);
            break;

        case 0x12:
            xga->hdisp = (xga->hdisp & 0xff00) | val;
            break;
        case 0x13:
            xga->hdisp = (xga->hdisp & 0xff) | (val << 8);
            svga_recalctimings(svga);
            break;

        case 0x20:
            xga->vtotal = (xga->vtotal & 0xff00) | val;
            break;
        case 0x21:
            xga->vtotal = (xga->vtotal & 0xff) | (val << 8);
            svga_recalctimings(svga);
            break;

        case 0x22:
            xga->vdispend = (xga->vdispend & 0xff00) | val;
            break;
        case 0x23:
            xga->vdispend = (xga->vdispend & 0xff) | (val << 8);
            svga_recalctimings(svga);
            break;

        case 0x24:
            xga->vblankstart = (xga->vblankstart & 0xff00) | val;
            break;
        case 0x25:
            xga->vblankstart = (xga->vblankstart & 0xff) | (val << 8);
            svga_recalctimings(svga);
            break;

        case 0x28:
            xga->vsyncstart = (xga->vsyncstart & 0xff00) | val;
            break;
        case 0x29:
            xga->vsyncstart = (xga->vsyncstart & 0xff) | (val << 8);
            svga_recalctimings(svga);
            break;

        case 0x2c:
            xga->linecmp = (xga->linecmp & 0xff00) | val;
            break;
        case 0x2d:
            xga->linecmp = (xga->linecmp & 0xff) | (val << 8);
            svga_recalctimings(svga);
            break;

        case 0x30:
            xga->hwc_pos_x  = (xga->hwc_pos_x & 0x0700) | val;
            xga->hwcursor.x = xga->hwc_pos_x;
            break;
        case 0x31:
            xga->hwc_pos_x  = (xga->hwc_pos_x & 0xff) | ((val & 0x07) << 8);
            xga->hwcursor.x = xga->hwc_pos_x;
            break;

        case 0x32:
            xga->hwc_hotspot_x = val & 0x3f;
            xga->hwcursor.xoff = val & 0x3f;
            break;

        case 0x33:
            xga->hwc_pos_y  = (xga->hwc_pos_y & 0x0700) | val;
            xga->hwcursor.y = xga->hwc_pos_y;
            break;
        case 0x34:
            xga->hwc_pos_y  = (xga->hwc_pos_y & 0xff) | ((val & 0x07) << 8);
            xga->hwcursor.y = xga->hwc_pos_y;
            break;

        case 0x35:
            xga->hwc_hotspot_y = val & 0x3f;
            xga->hwcursor.yoff = val & 0x3f;
            break;

        case 0x36:
            xga->hwc_control  = val;
            xga->hwcursor.ena = xga->hwc_control & 1;
            break;

        case 0x38:
            xga->hwc_color0 = (xga->hwc_color0 & 0xffff00) | val;
            break;
        case 0x39:
            xga->hwc_color0 = (xga->hwc_color0 & 0xff00ff) | (val << 8);
            break;
        case 0x3a:
            xga->hwc_color0 = (xga->hwc_color0 & 0x00ffff) | (val << 16);
            break;

        case 0x3b:
            xga->hwc_color1 = (xga->hwc_color1 & 0xffff00) | val;
            break;
        case 0x3c:
            xga->hwc_color1 = (xga->hwc_color1 & 0xff00ff) | (val << 8);
            break;
        case 0x3d:
            xga->hwc_color1 = (xga->hwc_color1 & 0x00ffff) | (val << 16);
            break;

        case 0x40:
            xga->disp_start_addr = (xga->disp_start_addr & 0x7ff00) | val;
            xga_log("DISPSTARTADDR0=%x.\n", xga->disp_start_addr);
            break;
        case 0x41:
            xga->disp_start_addr = (xga->disp_start_addr & 0x700ff) | (val << 8);
            xga_log("DISPSTARTADDR8=%x.\n", xga->disp_start_addr);
            break;
        case 0x42:
            xga->disp_start_addr = (xga->disp_start_addr & 0x0ffff) | ((val & 0x07) << 16);
            xga_log("DISPSTARTADDR16=%x.\n", xga->disp_start_addr);
            svga_recalctimings(svga);
            break;

        case 0x43:
            xga->pix_map_width = (xga->pix_map_width & 0x700) | val;
            break;
        case 0x44:
            xga->pix_map_width = (xga->pix_map_width & 0xff) | ((val & 0x07) << 8);
            svga_recalctimings(svga);
            break;

        case 0x50:
            xga_log("Reg50 write=%02x.\n", val);
            xga->disp_cntl_1 = val;
            svga_recalctimings(svga);
            break;

        case 0x51:
            xga_log("Reg51 write=%02x.\n", val & 0x07);
            xga->disp_cntl_2 = val;
            xga->on          = ((val & 0x07) >= 0x02);
            svga_recalctimings(svga);
            break;

        case 0x54:
            xga_log("Reg54 write = %02x.\n", val);
            xga->clk_sel_1 = val;
            svga_recalctimings(svga);
            break;

        case 0x55:
            xga->border_color = val;
            break;

        case 0x59:
            xga->direct_color = val;
            break;

        case 0x60:
            xga->sprite_pal_addr_idx = (xga->sprite_pal_addr_idx & 0x3f00) | val;
            xga->dac_pos             = 0;
            xga->dac_addr            = val & 0xff;
            break;
        case 0x61:
            xga->sprite_pal_addr_idx = (xga->sprite_pal_addr_idx & 0xff) | ((val & 0x3f) << 8);
            xga->sprite_pos          = xga->sprite_pal_addr_idx & 0x1ff;

            xga_log("Sprite POS = %d, data on = %d, idx = %d, apcntl = %d\n", xga->sprite_pos,
                    xga->cursor_data_on, xga->sprite_pal_addr_idx, xga->aperture_cntl);
            break;

        case 0x64:
            xga->dac_mask = val;
            xga_log("DAC mask=%02x.\n", val);
            break;

        case 0x65:
            svga->fullchange = svga->monitor->mon_changeframecount;
            switch (xga->dac_pos) {
                case 0:
                    xga->dac_r = val;
                    xga->dac_pos++;
                    break;
                case 1:
                    if (xga->pal_seq & 0x04)
                        xga->pal_b = val;
                    else
                        xga->dac_g = val;

                    xga->dac_pos++;
                    break;
                case 2:
                    if (xga->pal_seq & 0x04) {
                        xga->dac_g = val;
                        xga->dac_pos++;
                    } else {
                        xga->pal_b            = val;
                        index                 = xga->dac_addr & 0xff;
                        xga->xgapal[index].r = xga->dac_r;
                        xga->xgapal[index].g = xga->dac_g;
                        xga->xgapal[index].b = xga->pal_b;
                        xga->pallook[index]  = makecol32(xga->xgapal[index].r, xga->xgapal[index].g, xga->xgapal[index].b);
                        xga_log("XGA Pallook=%06x, idx=%d.\n", xga->pallook[index], index);
                        xga->dac_pos         = 0;
                        xga->dac_addr        = (xga->dac_addr + 1) & 0xff;
                    }
                    break;
                case 3:
                    if (xga->pal_seq & 0x04) {
                        index                 = xga->dac_addr & 0xff;
                        xga->xgapal[index].r = xga->dac_r;
                        xga->xgapal[index].b = xga->pal_b;
                        xga->xgapal[index].g = xga->dac_g;
                        xga->pallook[index]  = makecol32(xga->xgapal[index].r, xga->xgapal[index].g, xga->xgapal[index].b);
                        xga_log("XGA Pallook=%06x, idx=%d.\n", xga->pallook[index], index);
                        xga->dac_pos         = 0;
                        xga->dac_addr        = (xga->dac_addr + 1) & 0xff;
                    }
                    break;

                default:
                    break;
            }
            break;

        case 0x66:
            xga_log("Palette Sequence=%02x.\n", val);
            xga->pal_seq = val;
            break;

        case 0x6a:
            xga->sprite_data[xga->sprite_pos] = val;
            xga->sprite_pos                   = (xga->sprite_pos + 1) & 0x3ff;
            break;

        case 0x70:
            xga_log("Reg70 write = %02x.\n", val);
            xga->clk_sel_2 = val;
            svga_recalctimings(svga);
            break;

        default:
            break;
    }
}

static void
xga_ext_outb(uint16_t addr, uint8_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    xga_log("[%04X:%08X]: EXT OUTB = %02x, val = %02x\n", CS, cpu_state.pc, addr, val);
    switch (addr & 0x0f) {
        case 0:
            xga_log("[%04X:%08X]: EXT OUTB = %02x, val = %02x\n", CS, cpu_state.pc, addr, val);
            xga->op_mode = val;
            break;
        case 1:
            xga_log("[%04X:%08X]: EXT OUTB = %02x, val = %02x\n", CS, cpu_state.pc, addr, val);
            xga->aperture_cntl = val & 3;
            xga_updatemapping(svga);
            break;
        case 4:
            xga_log("[%04X:%08X]: EXT OUTB = %02x, val = %02x\n", CS, cpu_state.pc, addr, val);
            xga->int_ena = val;
            break;
        case 5:
            xga_log("[%04X:%08X]: EXT OUTB = %02x, val = %02x\n", CS, cpu_state.pc, addr, val);
            xga->int_stat = val;
            break;
        case 6:
            xga_log("[%04X:%08X]: EXT OUTB = %02x, val = %02x\n", CS, cpu_state.pc, addr, val);
            break;
        case 7:
            xga_log("[%04X:%08X]: EXT OUTB = %02x, val = %02x\n", CS, cpu_state.pc, addr, val);
            break;
        case 8:
            xga->ap_idx = val;
            xga_log("Aperture CNTL = %d, val = %02x, up to bit6 = %02x\n", xga->aperture_cntl,
                    val, val & 0x3f);
            if ((xga->op_mode & 7) < 4)
                xga->write_bank = xga->read_bank = 0;
            else {
                if (xga->base_addr_1mb) {
                    if (xga->aperture_cntl) {
                        xga->write_bank = (xga->ap_idx & 0x3f) << 16;
                        xga->read_bank  = xga->write_bank;
                    } else {
                        xga->write_bank = (xga->ap_idx & 0x30) << 16;
                        xga->read_bank  = xga->write_bank;
                    }
                } else {
                    xga->write_bank = (xga->ap_idx & 0x3f) << 16;
                    xga->read_bank  = xga->write_bank;
                }
            }
            break;
        case 9:
            xga->access_mode = val;
            break;
        case 0x0a:
            xga->regs_idx = val;
            break;
        case 0x0b:
        case 0x0c:
        case 0x0d:
        case 0x0e:
        case 0x0f:
            xga->regs[xga->regs_idx] = val;
            xga_log("EXT OUT Reg=%02x, val=%02x.\n", xga->regs_idx, val);
            xga_ext_out_reg(xga, svga, xga->regs_idx, xga->regs[xga->regs_idx]);
            break;

        default:
            xga_log("[%04X:%08X]: EXT OUTB = %02x, val = %02x\n", CS, cpu_state.pc, addr, val);
            break;
    }
}

static uint8_t
xga_ext_inb(uint16_t addr, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;
    uint8_t ret  = 0;
    uint8_t index;

    switch (addr & 0x0f) {
        case 0:
            ret = xga->op_mode;
            break;
        case 1:
            ret = xga->aperture_cntl;
            break;
        case 4:
            ret = xga->int_ena;
            break;
        case 5:
            ret = xga->int_stat;
            break;
        case 8:
            ret = xga->ap_idx;
            break;
        case 9:
            ret = xga->access_mode;
            break;
        case 0x0a:
            ret = xga->regs_idx;
            break;
        case 0x0b:
        case 0x0c:
        case 0x0d:
        case 0x0e:
        case 0x0f:
            switch (xga->regs_idx) {
                case 0:
                    ret = (xga->bus & DEVICE_MCA) ? 0x02 : 0x01;
                    break;
                case 4:
                    if (xga->bus & DEVICE_MCA)
                        ret = 0x01; /*32-bit MCA*/
                    else
                        ret = 0x10; /*16-bit ISA*/
                    break;
                case 0x10:
                    ret = xga->htotal & 0xff;
                    break;
                case 0x11:
                    ret = xga->htotal >> 8;
                    break;
                case 0x12:
                    ret = xga->hdisp & 0xff;
                    break;
                case 0x13:
                    ret = xga->hdisp >> 8;
                    break;
                case 0x20:
                    ret = xga->vtotal & 0xff;
                    break;
                case 0x21:
                    ret = xga->vtotal >> 8;
                    break;
                case 0x22:
                    ret = xga->vdispend & 0xff;
                    break;
                case 0x23:
                    ret = xga->vdispend >> 8;
                    break;
                case 0x24:
                    ret = xga->vblankstart & 0xff;
                    break;
                case 0x25:
                    ret = xga->vblankstart >> 8;
                    break;
                case 0x28:
                    ret = xga->vsyncstart & 0xff;
                    break;
                case 0x29:
                    ret = xga->vsyncstart >> 8;
                    break;
                case 0x2c:
                    ret = xga->linecmp & 0xff;
                    break;
                case 0x2d:
                    ret = xga->linecmp >> 8;
                    break;
                case 0x30:
                    ret = xga->hwc_pos_x & 0xff;
                    break;
                case 0x31:
                    ret = xga->hwc_pos_x >> 8;
                    break;
                case 0x32:
                    ret = xga->hwc_hotspot_x;
                    break;
                case 0x33:
                    ret = xga->hwc_pos_y & 0xff;
                    break;
                case 0x34:
                    ret = xga->hwc_pos_y >> 8;
                    break;
                case 0x35:
                    ret = xga->hwc_hotspot_y;
                    break;
                case 0x36:
                    ret = xga->hwc_control;
                    break;
                case 0x38:
                    ret = xga->hwc_color0 & 0xff;
                    break;
                case 0x39:
                    ret = xga->hwc_color0 >> 8;
                    break;
                case 0x3a:
                    ret = xga->hwc_color0 >> 16;
                    break;
                case 0x3b:
                    ret = xga->hwc_color1 & 0xff;
                    break;
                case 0x3c:
                    ret = xga->hwc_color1 >> 8;
                    break;
                case 0x3d:
                    ret = xga->hwc_color1 >> 16;
                    break;
                case 0x40:
                    ret = xga->disp_start_addr & 0xff;
                    break;
                case 0x41:
                    ret = xga->disp_start_addr >> 8;
                    break;
                case 0x42:
                    ret = xga->disp_start_addr >> 16;
                    break;
                case 0x43:
                    ret = xga->pix_map_width & 0xff;
                    break;
                case 0x44:
                    ret = xga->pix_map_width >> 8;
                    break;
                case 0x50:
                    ret = xga->disp_cntl_1 | 0x20;
                    break;
                case 0x51:
                    ret = xga->disp_cntl_2;
                    break;
                case 0x52:
                    ret = xga->type ? 0xfa : 0xea;
                    break;
                case 0x53:
                    ret = xga->type ? 0x53 : 0x30;
                    break;
                case 0x54:
                    ret = xga->clk_sel_1;
                    break;
                case 0x55:
                    ret = xga->border_color;
                    break;

                case 0x59:
                    ret = xga->direct_color;
                    break;

                case 0x60:
                    ret = xga->sprite_pal_addr_idx & 0xff;
                    break;
                case 0x61:
                    ret = xga->sprite_pal_addr_idx >> 8;
                    break;

                case 0x64:
                    ret = xga->dac_mask;
                    break;

                case 0x65:
                    index = xga->dac_addr & 0xff;
                    switch (xga->dac_pos) {
                        case 0:
                            xga->dac_pos++;
                            ret = xga->xgapal[index].r;
                            break;
                        case 1:
                            xga->dac_pos++;
                            if (xga->pal_seq & 0x04)
                                ret = xga->xgapal[index].b;
                            else
                                ret = xga->xgapal[index].g;
                            break;
                        case 2:
                            if (xga->pal_seq & 0x04) {
                                xga->dac_pos++;
                                ret = xga->xgapal[index].g;
                            } else {
                                xga->dac_pos  = 0;
                                xga->dac_addr = (xga->dac_addr + 1) & 0xff;
                                ret = xga->xgapal[index].b;
                            }
                            break;
                        case 3:
                            if (xga->pal_seq & 0x04) {
                                xga->dac_pos  = 0;
                                xga->dac_addr = (xga->dac_addr + 1) & 0xff;
                            }
                            break;

                        default:
                            break;
                    }
                    break;

                case 0x66:
                    ret = xga->pal_seq;
                    break;

                case 0x6a:
                    xga_log("Sprite POS Read=%d.\n", xga->sprite_pos);
                    ret                      = xga->sprite_data[xga->sprite_pos];
                    xga->sprite_pos = (xga->sprite_pos + 1) & 0x3ff;
                    break;

                case 0x70:
                    ret = xga->clk_sel_2;
                    break;

                case 0x74:
                    if (xga->bus & DEVICE_MCA)
                        ret = xga->regs[xga->regs_idx];
                    else {
                        ret = (xga->dma_channel << 1);
                        if (xga->dma_channel)
                            ret |= 1;
                    }
                    break;

                default:
                    ret = xga->regs[xga->regs_idx];
                    if ((xga->regs_idx == 0x0c) || (xga->regs_idx == 0x0d))
                        xga_log("EXT IN Reg=%02x, val=%02x.\n", xga->regs_idx, ret);
                    break;
            }
            break;

        default:
            xga_log("[%04X:%08X]: EXT INB = %02x, ret = %02x.\n\n", CS, cpu_state.pc, addr, ret);
            break;
    }

    xga_log("[%04X:%08X]: EXT INB = %02x, ret = %02x.\n\n", CS, cpu_state.pc, addr, ret);

    return ret;
}

#define ROP(mix, d, s)                                                                 \
    {                                                                                  \
        switch ((mix) ? (xga->accel.frgd_mix & 0x1f) : (xga->accel.bkgd_mix & 0x1f)) { \
            case 0x00:                                                                 \
                d = 0;                                                                 \
                break;                                                                 \
            case 0x01:                                                                 \
                d = s & d;                                                             \
                break;                                                                 \
            case 0x02:                                                                 \
                d = s & ~d;                                                            \
                break;                                                                 \
            case 0x03:                                                                 \
                d = s;                                                                 \
                break;                                                                 \
            case 0x04:                                                                 \
                d = ~s & d;                                                            \
                break;                                                                 \
            case 0x05:                                                                 \
                d = d;                                                                 \
                break;                                                                 \
            case 0x06:                                                                 \
                d = s ^ d;                                                             \
                break;                                                                 \
            case 0x07:                                                                 \
                d = s | d;                                                             \
                break;                                                                 \
            case 0x08:                                                                 \
                d = ~s & ~d;                                                           \
                break;                                                                 \
            case 0x09:                                                                 \
                d = s ^ ~d;                                                            \
                break;                                                                 \
            case 0x0a:                                                                 \
                d = ~d;                                                                \
                break;                                                                 \
            case 0x0b:                                                                 \
                d = s | ~d;                                                            \
                break;                                                                 \
            case 0x0c:                                                                 \
                d = ~s;                                                                \
                break;                                                                 \
            case 0x0d:                                                                 \
                d = ~s | d;                                                            \
                break;                                                                 \
            case 0x0e:                                                                 \
                d = ~s | ~d;                                                           \
                break;                                                                 \
            case 0x0f:                                                                 \
                d = ~0;                                                                \
                break;                                                                 \
            case 0x10:                                                                 \
                d = MAX(s, d);                                                         \
                break;                                                                 \
            case 0x11:                                                                 \
                d = MIN(s, d);                                                         \
                break;                                                                 \
            case 0x12:                                                                 \
                d = MIN(~0, s + d);                                                    \
                break;                                                                 \
            case 0x13:                                                                 \
                d = MAX(0, d - s);                                                     \
                break;                                                                 \
            case 0x14:                                                                 \
                d = MAX(0, s - d);                                                     \
                break;                                                                 \
            case 0x15:                                                                 \
                d = (s + d) >> 1;                                                      \
                break;                                                                 \
        }                                                                              \
    }

static uint32_t
xga_transform_addr(uint32_t addr, uint8_t mode)
{
    uint32_t ret  = addr;

    if ((mode & 0x0f) == 0x0c)
        ret ^= 1;

    return ret;
}

static uint16_t
xga_transform_val(uint16_t val, uint8_t mode, int bits)
{
    uint16_t ret  = 0x0000;

    switch (mode & 0x0f) {
        default:
            ret = val;
            break;
        case 0x08:    /* 1 bpp */
            for (int i = 0; i < bits; i++)
                ret |= ((val >> i) & 0x01) << (i ^ 7);
            break;
        case 0x09:    /* 2 bpp */
            for (int i = 0; i < bits; i += 2)
                ret |= ((val >> i) & 0x03) << (i ^ 6);
            break;
        case 0x0a:    /* 4 bpp */
            for (int i = 0; i < bits; i += 4)
                ret |= ((val >> i) & 0x0f) << (i ^ 4);
            break;
        case 0x0c:    /* 16 bpp */
            if (bits == 16)  for (int i = 0; i < (bits >> 3); i++)
                ret |= ((val >> (i << 3)) & 0xff) << ((i << 3) ^ 8);
            else
                ret = val;
            break;
    }

    return ret & ((1 << bits) - 1);
}

static uint8_t
xga_map_readb(svga_t *svga, uint32_t addr, int map)
{
    xga_t    *xga  = (xga_t *) svga->xga;
    uint8_t   ret;

    if ((addr >= xga->linear_base) && (addr <= (xga->linear_base + 0xfffff)))
        ret = xga->vram[addr & xga->vram_mask];
    else
        ret = xga_transform_val(mem_readb_phys(xga_transform_addr(addr, xga->accel.px_map_format[map])),
                                xga->accel.px_map_format[map], 8);
    return ret;
}

static uint16_t
xga_map_readw(svga_t *svga, uint32_t addr, int map)
{
    xga_t    *xga  = (xga_t *) svga->xga;
    uint16_t  ret;

    if ((addr >= xga->linear_base) && (addr <= (xga->linear_base + 0xfffff)))
        ret = *(uint16_t *) &xga->vram[addr & xga->vram_mask];
    else
        ret = xga_transform_val(mem_readw_phys(addr), xga->accel.px_map_format[map], 16);

    return ret;
}

static void
xga_map_writeb(svga_t *svga, uint32_t addr, int map, uint8_t val)
{
    xga_t    *xga  = (xga_t *) svga->xga;

    if ((addr >= xga->linear_base) && (addr <= (xga->linear_base + 0xfffff))) {
        xga->vram[addr & xga->vram_mask]                = val;
        xga->changedvram[(addr & xga->vram_mask) >> 12] = svga->monitor->mon_changeframecount;
    } else
        mem_writeb_phys(xga_transform_addr(addr, xga->accel.px_map_format[map]),
                        xga_transform_val(val, xga->accel.px_map_format[map], 8));
}

static void
xga_map_writew(svga_t *svga, uint32_t addr, int map, uint16_t val)
{
    xga_t    *xga  = (xga_t *) svga->xga;

    if ((addr >= xga->linear_base) && (addr <= (xga->linear_base + 0xfffff))) {
       *(uint16_t *) &xga->vram[addr & xga->vram_mask] = val;
        xga->changedvram[(addr & xga->vram_mask) >> 12] = svga->monitor->mon_changeframecount;
    } else
        mem_writew_phys(addr, xga_transform_val(val, xga->accel.px_map_format[map], 16));
}

static int
xga_calc_pos(int x, int y, int width)
{
    int ret = (y * (width + 1)) + (x % (width + 1));

    return ret;
}

static uint32_t
xga_add_to_addr(xga_t *xga, uint32_t addr, int pos, int map)
{
    int      pos_div[8] = { 8, 4, 2, 1, 1, 1, 1, 1 };
    int      pos_mul[8] = { 1, 1, 1, 1, 2, 1, 1, 1 };
    int      div_val    = pos_div[xga->accel.px_map_format[map] & 0x07];
    int      mul_val    = pos_mul[xga->accel.px_map_format[map] & 0x07];
    uint32_t ret        = addr + ((pos / div_val) * mul_val);

    return ret;
}

#define READ_MAP(base, pos, map)           xga_map_readb(svga, xga_add_to_addr(xga, base, pos, map), map)
#define READ_MAPW(base, pos, map)          xga_map_readw(svga, xga_add_to_addr(xga, base, pos, map), map)
#define WRITE_MAP(base, pos, map, val)     xga_map_writeb(svga, xga_add_to_addr(xga, base, pos, map), map, val)
#define WRITE_MAPW(base, pos, map, val)    xga_map_writew(svga, xga_add_to_addr(xga, base, pos, map), map, val)

#define DO_READ_MAP(map)                   READ_MAP(base, pos, map)

#define PIXEL_MASK(bits)                   (0x07 / bits)
#define ALL_SET(bits)                      ((1 << bits) - 1)

#define MASK(map, bits)                    ((pos & PIXEL_MASK(bits)) * bits)
#define MASKED_READ(map, bits)             (DO_READ_MAP(map) >> MASK(map, bits)) & ALL_SET(bits)

static uint32_t
xga_accel_read_pattern_map_pixel(svga_t *svga, int x, int y)
{
    xga_t    *xga  = (xga_t *) svga->xga;
    int       map  = 0;
    uint32_t  ret  = 0x000000ff;

    switch ((xga->accel.command >> 12) & 0x0f) {
        default:
        case 8:
            map = 0;
            break;
        case 1 ... 3:
            map = (xga->accel.command >> 12) & 0x0f;
            break;
        case 9:
            map = (xga->accel.command >> 20) & 0x0f;
            if (map > 0x03)
                map = 0;
            break;
    }

    if (map != 0) {
        int       width = xga->accel.px_map_width[map];
        int       pos   = xga_calc_pos(x, y, width);
        uint32_t  base  = xga->accel.px_map_base[map];

        ret = MASKED_READ(map, 1);
    }

    return ret;
}

static uint32_t
xga_accel_read_map_pixel(svga_t *svga, int x, int y, int map)
{
    xga_t   *xga   = (xga_t *) svga->xga;
    int      width = xga->accel.px_map_width[map];
    int      pos   = xga_calc_pos(x, y, width);
    uint32_t base  = xga->accel.px_map_base[map];
    uint32_t ret   = 0x00000000;

    switch (xga->accel.px_map_format[map] & 0x07) {
        case 0:    /* 1 bpp */
            ret = MASKED_READ(map, 1);
            break;

        case 1:    /* 2 bpp */
            ret = MASKED_READ(map, 2);
            break;

        case 2:    /* 4 bpp */
            ret = MASKED_READ(map, 4);
            break;

        case 3:    /* 8 bpp */
            ret = READ_MAP(base, pos, map);
            break;

        case 4:    /* 16 bpp */
            ret = READ_MAPW(base, pos, map);
            break;

        default:
            break;
    }

    return ret;
}

static void
xga_accel_write_map_pixel(svga_t *svga, int x, int y, int map, uint32_t pixel)
{
    xga_t   *xga  = (xga_t *) svga->xga;
    uint8_t  byte;
    uint8_t  mask;
    int      bit;
    int      width = xga->accel.px_map_width[map];
    int      pos   = xga_calc_pos(x, y, width);
    uint32_t base  = xga->accel.px_map_base[map];

    switch (xga->accel.px_map_format[map] & 0x07) {
        case 0: /* 1 bpp */
            byte = READ_MAP(base, pos, map);
            bit = pos & 7;

            pixel <<= bit;
            mask = 1 << bit;

            byte = (byte & ~mask) | (pixel & mask);
            WRITE_MAP(base, pos, map, byte);
            break;

        case 1: /* 2 bpp */
            byte = READ_MAP(base, pos, map);
            mask = ((pos & 3) << 1);

            pixel <<= mask;
            mask = 0x03 << mask;

            byte = (byte & ~mask) | (pixel & mask);
            WRITE_MAP(base, pos, map, byte);
            break;

        case 2: /* 4 bpp */
            byte = READ_MAP(base, pos, map);
            mask = ((pos & 1) << 2);

            pixel <<= mask;
            mask = 0x0f << mask;

            byte = (byte & ~mask) | (pixel & mask);
            WRITE_MAP(base, pos, map, byte);
            break;

        case 3: /* 8bpp */
            WRITE_MAP(base, pos, map, pixel);
            break;

        case 4: /* 16bpp */
            WRITE_MAPW(base, pos, map, pixel);
            break;

        default:
            break;
    }
}

static void
xga_short_stroke(svga_t *svga, uint8_t ssv)
{
    xga_t   *xga = (xga_t *) svga->xga;
    uint32_t src_dat;
    uint32_t dest_dat;
    uint32_t old_dest_dat;
    uint32_t color_cmp  = xga->accel.color_cmp;
    uint32_t plane_mask = xga->accel.plane_mask;
    int      y          = ssv & 0x0f;
    int      x          = 0;
    int16_t  dx;
    int16_t  dy;
    int      dirx = 0;
    int      diry = 0;

    dx = xga->accel.dst_map_x;
    if (xga->accel.dst_map_x >= 0x1800)
        dx |= ~0x17ff;

    dy = xga->accel.dst_map_y;
    if (xga->accel.dst_map_y >= 0x1800)
        dy |= ~0x17ff;

    switch ((ssv >> 5) & 7) {
        case 0:
            dirx = 1;
            diry = 0;
            break;
        case 1:
            dirx = 1;
            diry = -1;
            break;
        case 2:
            dirx = 0;
            diry = -1;
            break;
        case 3:
            dirx = -1;
            diry = -1;
            break;
        case 4:
            dirx = -1;
            diry = 0;
            break;
        case 5:
            dirx = -1;
            diry = 1;
            break;
        case 6:
            dirx = 0;
            diry = 1;
            break;
        case 7:
            dirx = 1;
            diry = 1;
            break;

        default:
            break;
    }

    if (xga->accel.pat_src == 8) {
        while (y >= 0) {
            if (xga->accel.command & 0xc0) {
                if ((dx >= xga->accel.mask_map_origin_x_off) && (dx <= ((xga->accel.px_map_width[0] & 0xfff) + xga->accel.mask_map_origin_x_off)) && (dy >= xga->accel.mask_map_origin_y_off) && (dy <= ((xga->accel.px_map_height[0] & 0xfff) + xga->accel.mask_map_origin_y_off))) {
                    src_dat  = (((xga->accel.command >> 28) & 3) == 2) ?
                        xga_accel_read_map_pixel(svga, xga->accel.src_map_x & 0xfff,
                                                 xga->accel.src_map_y & 0xfff, xga->accel.src_map) :
                        xga->accel.frgd_color;
                    dest_dat = xga_accel_read_map_pixel(svga, dx, dy, xga->accel.dst_map);

                    if ((xga->accel.cc_cond == 4) || ((xga->accel.cc_cond == 1) && (dest_dat > color_cmp)) || ((xga->accel.cc_cond == 2) && (dest_dat == color_cmp)) || ((xga->accel.cc_cond == 3) && (dest_dat < color_cmp)) || ((xga->accel.cc_cond == 5) && (dest_dat >= color_cmp)) || ((xga->accel.cc_cond == 6) && (dest_dat != color_cmp)) || ((xga->accel.cc_cond == 7) && (dest_dat <= color_cmp))) {
                        old_dest_dat = dest_dat;
                        ROP(1, dest_dat, src_dat);
                        dest_dat = (dest_dat & plane_mask) | (old_dest_dat & ~plane_mask);
                        if ((xga->accel.command & 0x30) == 0) {
                            if (ssv & 0x10)
                                xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                        } else if (((xga->accel.command & 0x30) == 0x10) && x) {
                            if (ssv & 0x10)
                                xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                        } else if (((xga->accel.command & 0x30) == 0x20) && y) {
                            if (ssv & 0x10)
                                xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                        }
                    }
                }
            } else {
                src_dat  = (((xga->accel.command >> 28) & 3) == 2) ?
                    xga_accel_read_map_pixel(svga, xga->accel.src_map_x & 0xfff,
                                             xga->accel.src_map_y & 0xfff, xga->accel.src_map) :
                    xga->accel.frgd_color;
                dest_dat = xga_accel_read_map_pixel(svga, dx, dy, xga->accel.dst_map);

                if ((xga->accel.cc_cond == 4) || ((xga->accel.cc_cond == 1) && (dest_dat > color_cmp)) || ((xga->accel.cc_cond == 2) && (dest_dat == color_cmp)) || ((xga->accel.cc_cond == 3) && (dest_dat < color_cmp)) || ((xga->accel.cc_cond == 5) && (dest_dat >= color_cmp)) || ((xga->accel.cc_cond == 6) && (dest_dat != color_cmp)) || ((xga->accel.cc_cond == 7) && (dest_dat <= color_cmp))) {
                    old_dest_dat = dest_dat;
                    ROP(1, dest_dat, src_dat);
                    dest_dat = (dest_dat & plane_mask) | (old_dest_dat & ~plane_mask);
                    if ((xga->accel.command & 0x30) == 0) {
                        if (ssv & 0x10)
                            xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                    } else if (((xga->accel.command & 0x30) == 0x10) && x) {
                        if (ssv & 0x10)
                            xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                    } else if (((xga->accel.command & 0x30) == 0x20) && y) {
                        if (ssv & 0x10)
                            xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                    }
                }
            }

            if (!y)
                break;

            dx += dirx;
            dy += diry;

            x++;
            y--;
        }
    }

    xga->accel.dst_map_x = dx;
    xga->accel.dst_map_y = dy;
}

static void
xga_line_draw_write(svga_t *svga)
{
    xga_t   *xga = (xga_t *) svga->xga;
    uint32_t src_dat;
    uint32_t dest_dat;
    uint32_t old_dest_dat = 0x00000000;
    uint32_t color_cmp  = xga->accel.color_cmp;
    uint32_t plane_mask = xga->accel.plane_mask;
    int      y = xga->accel.blt_width;
    int      x = 0;
    int      draw_pixel = 0;
    int16_t  dx;
    int16_t  dy;
    int16_t  cx;
    int16_t  cy;

    cx = xga->accel.src_map_x & 0xfff;
    cy = xga->accel.src_map_y & 0xfff;

    dx = xga->accel.dst_map_x;
    if (xga->accel.dst_map_x >= 0x1800)
        dx |= ~0x17ff;

    dy = xga->accel.dst_map_y;
    if (xga->accel.dst_map_y >= 0x1800)
        dy |= ~0x17ff;

    if ((xga->accel.command & 0x30) == 0x30)
        xga_log("Line Draw Write Fill: DX=%d, DY=%d, BLTWIDTH=%d, BLTHEIGHT=%d, FRGDCOLOR=%04x, negative XDIR=%i, negative YDIR=%i, YMAJOR=%d, ERR=%d, BRESK2=%d, BRESK1=%d, mask=%02x, frgdmix=%02x, bkgdmix=%02x.\n", dx, dy, xga->accel.blt_width, xga->accel.blt_height, xga->accel.frgd_color & 0xffff, (xga->accel.octant & 0x04), (xga->accel.octant & 0x02), (xga->accel.octant & 0x01), xga->accel.bres_err_term, xga->accel.bres_k2, xga->accel.bres_k1, xga->accel.command & 0xc0, xga->accel.frgd_mix & 0x1f, xga->accel.bkgd_mix & 0x1f);

    if (xga->accel.pat_src == 8) {
        if ((xga->accel.command & 0x30) == 0x30) {
            while (y >= 0) {
                draw_pixel = 0;

                if (xga->accel.octant & 0x01) { /*Y Major*/
                    if (xga->accel.octant & 0x02) { /*Bottom to Top*/
                        if (x)
                            draw_pixel = 1;
                    } else { /*Top to Bottom*/
                        if (y)
                            draw_pixel = 1;
                    }
                } else { /*X Major*/
                    if (xga->accel.octant & 0x04) { /*Right to Left*/
                         if (xga->accel.bres_err_term >= 0) {
                            if (xga->accel.octant & 0x02) { /*Bottom to Top*/
                                if (x)
                                    draw_pixel = 1;
                            } else { /*Top to Bottom*/
                                if (y)
                                    draw_pixel = 1;
                            }
                        }
                    } else { /*Left to Right*/
                         if (xga->accel.bres_err_term < (xga->accel.bres_k1 + xga->accel.bres_k2)) {
                            if (xga->accel.octant & 0x02) { /*Bottom to Top*/
                                if (x)
                                    draw_pixel = 1;
                            } else { /*Top to Bottom*/
                                if (y)
                                    draw_pixel = 1;
                            }
                        }
                    }
                }

                xga_log("Draw Boundary: DX=%d, DY=%d, wrt_pix=%d, ymajor=%d, bottomtotop=%x, len=%d, err=%d, frgdmix=%02x.\n", dx, dy, draw_pixel, xga->accel.octant & 0x01, xga->accel.octant & 0x02, y, xga->accel.bres_err_term, xga->accel.frgd_mix & 0x1f);
                if (xga->accel.command & 0xc0) {
                    if ((dx >= xga->accel.mask_map_origin_x_off) && (dx <= ((xga->accel.px_map_width[0] & 0xfff) + xga->accel.mask_map_origin_x_off)) && (dy >= xga->accel.mask_map_origin_y_off) && (dy <= ((xga->accel.px_map_height[0] & 0xfff) + xga->accel.mask_map_origin_y_off)) && draw_pixel) {
                        src_dat  = (((xga->accel.command >> 28) & 3) == 2) ?
                            xga_accel_read_map_pixel(svga, cx, cy, xga->accel.src_map) :
                            xga->accel.frgd_color;
                        dest_dat = xga_accel_read_map_pixel(svga, dx, dy, xga->accel.dst_map);

                        if ((xga->accel.cc_cond == 4) || ((xga->accel.cc_cond == 1) && (dest_dat > color_cmp)) || ((xga->accel.cc_cond == 2) && (dest_dat == color_cmp)) || ((xga->accel.cc_cond == 3) && (dest_dat < color_cmp)) || ((xga->accel.cc_cond == 5) && (dest_dat >= color_cmp)) || ((xga->accel.cc_cond == 6) && (dest_dat != color_cmp)) || ((xga->accel.cc_cond == 7) && (dest_dat <= color_cmp))) {
                            old_dest_dat = dest_dat;
                            ROP(1, dest_dat, src_dat);
                            dest_dat = (dest_dat & plane_mask) | (old_dest_dat & ~plane_mask);
                            xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                        }
                    }
                } else {
                    if (draw_pixel) {
                        src_dat  = (((xga->accel.command >> 28) & 3) == 2) ?
                            xga_accel_read_map_pixel(svga, cx, cy, xga->accel.src_map) :
                                xga->accel.frgd_color;
                        dest_dat = xga_accel_read_map_pixel(svga, dx, dy, xga->accel.dst_map);

                        if ((xga->accel.cc_cond == 4) || ((xga->accel.cc_cond == 1) && (dest_dat > color_cmp)) || ((xga->accel.cc_cond == 2) && (dest_dat == color_cmp)) || ((xga->accel.cc_cond == 3) && (dest_dat < color_cmp)) || ((xga->accel.cc_cond == 5) && (dest_dat >= color_cmp)) || ((xga->accel.cc_cond == 6) && (dest_dat != color_cmp)) || ((xga->accel.cc_cond == 7) && (dest_dat <= color_cmp))) {
                            old_dest_dat = dest_dat;
                            ROP(1, dest_dat, src_dat);
                            dest_dat = (dest_dat & plane_mask) | (old_dest_dat & ~plane_mask);
                            xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                        }
                    }
                }

                if (x == xga->accel.blt_width) {
                    xga->accel.dst_map_x = dx;
                    xga->accel.dst_map_y = dy;
                    break;
                }

                if (xga->accel.octant & 0x01) {
                    if (xga->accel.octant & 0x02)
                        dy--;
                    else
                        dy++;

                    if (xga->accel.bres_err_term >= 0) {
                        xga->accel.bres_err_term += xga->accel.bres_k2;
                        if (xga->accel.octant & 0x04)
                            dx--;
                        else
                            dx++;
                    } else
                        xga->accel.bres_err_term += xga->accel.bres_k1;
                } else {
                    if (xga->accel.octant & 0x04)
                        dx--;
                    else
                        dx++;

                    if (xga->accel.bres_err_term >= 0) {
                        xga->accel.bres_err_term += xga->accel.bres_k2;
                        if (xga->accel.octant & 0x02)
                            dy--;
                        else
                            dy++;
                    } else
                        xga->accel.bres_err_term += xga->accel.bres_k1;
                }
                x++;
                y--;
            }
        } else {
            while (y >= 0) {
                if (xga->accel.command & 0xc0) {
                    if ((dx >= xga->accel.mask_map_origin_x_off) && (dx <= ((xga->accel.px_map_width[0] & 0xfff) + xga->accel.mask_map_origin_x_off)) && (dy >= xga->accel.mask_map_origin_y_off) && (dy <= ((xga->accel.px_map_height[0] & 0xfff) + xga->accel.mask_map_origin_y_off))) {
                        src_dat  = (((xga->accel.command >> 28) & 3) == 2) ?
                            xga_accel_read_map_pixel(svga, xga->accel.src_map_x & 0xfff,
                                                     xga->accel.src_map_y & 0xfff, xga->accel.src_map) :
                            xga->accel.frgd_color;
                        dest_dat = xga_accel_read_map_pixel(svga, dx, dy, xga->accel.dst_map);

                        if ((xga->accel.cc_cond == 4) || ((xga->accel.cc_cond == 1) && (dest_dat > color_cmp)) || ((xga->accel.cc_cond == 2) && (dest_dat == color_cmp)) || ((xga->accel.cc_cond == 3) && (dest_dat < color_cmp)) || ((xga->accel.cc_cond == 5) && (dest_dat >= color_cmp)) || ((xga->accel.cc_cond == 6) && (dest_dat != color_cmp)) || ((xga->accel.cc_cond == 7) && (dest_dat <= color_cmp))) {
                            old_dest_dat = dest_dat;
                            ROP(1, dest_dat, src_dat);
                            dest_dat = (dest_dat & plane_mask) | (old_dest_dat & ~plane_mask);
                            if ((xga->accel.command & 0x30) == 0)
                                xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                            else if (((xga->accel.command & 0x30) == 0x10) && x)
                                xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                            else if (((xga->accel.command & 0x30) == 0x20) && y)
                                xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                        }
                    }
                } else {
                    src_dat  = (((xga->accel.command >> 28) & 3) == 2) ?
                        xga_accel_read_map_pixel(svga, xga->accel.src_map_x & 0xfff,
                                                 xga->accel.src_map_y & 0xfff, xga->accel.src_map) :
                        xga->accel.frgd_color;
                    dest_dat = xga_accel_read_map_pixel(svga, dx, dy, xga->accel.dst_map);

                    if ((xga->accel.cc_cond == 4) || ((xga->accel.cc_cond == 1) && (dest_dat > color_cmp)) || ((xga->accel.cc_cond == 2) && (dest_dat == color_cmp)) || ((xga->accel.cc_cond == 3) && (dest_dat < color_cmp)) || ((xga->accel.cc_cond == 5) && (dest_dat >= color_cmp)) || ((xga->accel.cc_cond == 6) && (dest_dat != color_cmp)) || ((xga->accel.cc_cond == 7) && (dest_dat <= color_cmp))) {
                        old_dest_dat = dest_dat;
                        ROP(1, dest_dat, src_dat);
                        dest_dat = (dest_dat & plane_mask) | (old_dest_dat & ~plane_mask);
                        if ((xga->accel.command & 0x30) == 0)
                            xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                        else if (((xga->accel.command & 0x30) == 0x10) && x)
                            xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                        else if (((xga->accel.command & 0x30) == 0x20) && y)
                            xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                    }
                }

                if (!y) {
                    xga->accel.dst_map_x = dx;
                    xga->accel.dst_map_y = dy;
                    break;
                }

                if (xga->accel.octant & 0x01) {
                    if (xga->accel.octant & 0x02)
                        dy--;
                    else
                        dy++;

                    if (xga->accel.bres_err_term >= 0) {
                        xga->accel.bres_err_term += xga->accel.bres_k2;
                        if (xga->accel.octant & 0x04)
                            dx--;
                        else
                            dx++;
                    } else
                        xga->accel.bres_err_term += xga->accel.bres_k1;
                } else {
                    if (xga->accel.octant & 0x04)
                        dx--;
                    else
                        dx++;

                    if (xga->accel.bres_err_term >= 0) {
                        xga->accel.bres_err_term += xga->accel.bres_k2;
                        if (xga->accel.octant & 0x02)
                            dy--;
                        else
                            dy++;
                    } else
                        xga->accel.bres_err_term += xga->accel.bres_k1;
                }
                y--;
                x++;
            }
        }
    }
}

static void
xga_bitblt(svga_t *svga)
{
    xga_t   *xga = (xga_t *) svga->xga;
    uint32_t src_dat;
    uint32_t dest_dat;
    uint32_t old_dest_dat;
    uint32_t color_cmp  = xga->accel.color_cmp;
    uint32_t plane_mask = xga->accel.plane_mask;
    uint32_t patwidth;
    uint32_t dstwidth   = xga->accel.px_map_width[xga->accel.dst_map];
    uint32_t srcwidth   = xga->accel.px_map_width[xga->accel.src_map];
    uint32_t patheight;
    uint32_t srcheight  = xga->accel.px_map_height[xga->accel.src_map];
    uint32_t dstheight  = xga->accel.px_map_height[xga->accel.dst_map];
    uint32_t frgdcol = xga->accel.frgd_color;
    uint32_t bkgdcol = xga->accel.bkgd_color;
    int16_t  dx;
    int16_t  dy;
    int      mix  = 0;
    int      xdir = (xga->accel.octant & 0x04) ? -1 : 1;
    int      ydir = (xga->accel.octant & 0x02) ? -1 : 1;

    xga->accel.x = xga->accel.blt_width & 0xfff;
    xga->accel.y = xga->accel.blt_height & 0xfff;

    xga->accel.sx = xga->accel.src_map_x & 0xfff;
    xga->accel.sy = xga->accel.src_map_y & 0xfff;
    xga->accel.px = xga->accel.pat_map_x & 0xfff;
    xga->accel.py = xga->accel.pat_map_y & 0xfff;
    dx = xga->accel.dst_map_x;
    dy = xga->accel.dst_map_y;
    if (xga->accel.dst_map_x >= 0x1800)
        dx |= ~0x17ff;
    if (xga->accel.dst_map_y >= 0x1800)
        dy |= ~0x17ff;

    xga_log("D(%d,%d), SWH(%d,%d), BLT(%d,%d), dstwidth=%d, frgdcol=%04x, bkgdcol=%04x.\n", dx, dy, xga->accel.x, xga->accel.y, srcwidth, srcheight, dstwidth, frgdcol, bkgdcol);

    xga->accel.pattern = 0;
    xga->accel.filling = 0;
    xga->accel.y_len = 0;

    xga_log("XGA bitblt access_mode=%x, octanty=%d, src command=%08x, "
            "pxsrcmap=%x, pxpatmap=%x, pxdstmap=%x, srcmap=%d, patmap=%d, dstmap=%d, "
            "usesrcvramfr=%d, usevrambk=%d, planemask=%04x, frgdcol=%04x, bkgdcol=%04x, bgmix=%02x, fgmix=%02x.\n",
            xga->access_mode & 0x0f, ydir, xga->accel.command,
            xga->accel.px_map_format[xga->accel.src_map] & 0x0f,
            xga->accel.px_map_format[xga->accel.pat_src & 0x03] & 0x0f,
            xga->accel.px_map_format[xga->accel.dst_map] & 0x0f,
            xga->accel.src_map, xga->accel.pat_src,
            xga->accel.dst_map, ((xga->accel.command >> 28) & 3), ((xga->accel.command >> 30) & 3),
            xga->accel.plane_mask, frgdcol, bkgdcol, xga->accel.bkgd_mix & 0x1f, xga->accel.frgd_mix & 0x1f);

    if (xga->accel.pat_src == 8) {
        if (srcheight == 7)
            xga->accel.pattern = 1;
        else {
            if ((dstwidth == (xga->h_disp - 1)) && (srcwidth == 1)) {
                if ((xga->accel.dst_map == 1) && (xga->accel.src_map == 2)) {
                    if ((xga->accel.px_map_format[xga->accel.dst_map] >= 0x0a) && (xga->accel.px_map_format[xga->accel.src_map] >= 0x0a))
                        xga->accel.pattern = 1;
                    else if (!(xga->accel.px_map_format[xga->accel.dst_map] & 0x08) && !(xga->accel.px_map_format[xga->accel.src_map] & 0x08)) {
                        if ((xga->accel.px_map_format[xga->accel.dst_map] >= 0x02) && (xga->accel.px_map_format[xga->accel.src_map] >= 0x02))
                            xga->accel.pattern = 1;
                    }
                }
            }
        }

        xga_log("CMD=%08x, EnablePat=%d, SRC%d, DST%d: SrcFormat=%x, DstFormat=%x, SrcWidth=%d, SrcHeight=%d, DstWidth=%d, DstHeight=%d, sx=%d, sy=%d.\n", xga->accel.command,
              xga->accel.pattern, xga->accel.src_map, xga->accel.dst_map, (xga->accel.px_map_format[xga->accel.src_map] & 0x0f), (xga->accel.px_map_format[xga->accel.dst_map] & 0x0f),
              srcwidth, srcheight, dstwidth, dstheight, xga->accel.sx, xga->accel.sy);

        while (xga->accel.y >= 0) {
            if (xga->accel.command & 0xc0) {
                if ((dx >= xga->accel.mask_map_origin_x_off) && (dx <= ((xga->accel.px_map_width[0] & 0xfff) + xga->accel.mask_map_origin_x_off)) && (dy >= xga->accel.mask_map_origin_y_off) && (dy <= ((xga->accel.px_map_height[0] & 0xfff) + xga->accel.mask_map_origin_y_off))) {
                    src_dat  = (((xga->accel.command >> 28) & 3) == 2) ?
                        xga_accel_read_map_pixel(svga, xga->accel.sx, xga->accel.sy, xga->accel.src_map) :
                            frgdcol;
                    dest_dat = xga_accel_read_map_pixel(svga, dx, dy, xga->accel.dst_map);
                    if ((xga->accel.cc_cond == 4) || ((xga->accel.cc_cond == 1) && (dest_dat > color_cmp)) || ((xga->accel.cc_cond == 2) && (dest_dat == color_cmp)) || ((xga->accel.cc_cond == 3) && (dest_dat < color_cmp)) || ((xga->accel.cc_cond == 5) && (dest_dat >= color_cmp)) || ((xga->accel.cc_cond == 6) && (dest_dat != color_cmp)) || ((xga->accel.cc_cond == 7) && (dest_dat <= color_cmp))) {
                        old_dest_dat = dest_dat;
                        ROP(1, dest_dat, src_dat);
                        dest_dat = (dest_dat & plane_mask) | (old_dest_dat & ~plane_mask);
                        xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                    }
                }
            } else {
                if ((dx >= 0) && (dx <= dstwidth) && (dy >= 0) && (dy <= dstheight)) {
                    src_dat  = (((xga->accel.command >> 28) & 3) == 2) ?
                        xga_accel_read_map_pixel(svga, xga->accel.sx, xga->accel.sy, xga->accel.src_map) :
                            frgdcol;
                    dest_dat = xga_accel_read_map_pixel(svga, dx, dy, xga->accel.dst_map);
                    if ((xga->accel.cc_cond == 4) || ((xga->accel.cc_cond == 1) && (dest_dat > color_cmp)) || ((xga->accel.cc_cond == 2) && (dest_dat == color_cmp)) || ((xga->accel.cc_cond == 3) && (dest_dat < color_cmp)) || ((xga->accel.cc_cond == 5) && (dest_dat >= color_cmp)) || ((xga->accel.cc_cond == 6) && (dest_dat != color_cmp)) || ((xga->accel.cc_cond == 7) && (dest_dat <= color_cmp))) {
                        old_dest_dat = dest_dat;
                        ROP(1, dest_dat, src_dat);
                        dest_dat = (dest_dat & plane_mask) | (old_dest_dat & ~plane_mask);
                        xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                    }
                }
            }

            if (xga->accel.pattern)
                xga->accel.sx = ((xga->accel.sx + xdir) & srcwidth) | (xga->accel.sx & ~srcwidth);
            else
                xga->accel.sx += xdir;

            dx += xdir;

            xga->accel.x--;
            if (xga->accel.x < 0) {
                xga->accel.x = xga->accel.blt_width & 0xfff;

                dx = xga->accel.dst_map_x;
                if (xga->accel.dst_map_x >= 0x1800)
                    dx |= ~0x17ff;
                xga->accel.sx = xga->accel.src_map_x & 0xfff;

                dy += ydir;
                xga->accel.y_len++;

                if (xga->accel.pattern)
                    xga->accel.sy = ((xga->accel.sy + ydir) & srcheight) | (xga->accel.sy & ~srcheight);
                else
                    xga->accel.sy += ydir;

                xga->accel.y--;

                if (xga->accel.y < 0) {
                    xga->accel.dst_map_x = dx;
                    xga->accel.dst_map_y = dy;
                    return;
                }
            }
        }
    } else if (xga->accel.pat_src >= 1) {
        patwidth = xga->accel.px_map_width[xga->accel.pat_src];
        patheight = xga->accel.px_map_height[xga->accel.pat_src];

        if (patheight == 7) {
            if (xga->accel.src_map != 1)
                xga->accel.pattern = 1;
            else if ((xga->accel.src_map == 1) && (patwidth == 7))
                xga->accel.pattern = 1;
        } else {
            if (dstwidth == (xga->h_disp - 1)) {
                if (srcwidth == (xga->h_disp - 1)) {
                    if ((xga->accel.src_map == 1) && (xga->accel.dst_map == 1) && (xga->accel.pat_src == 2)) {
                        if ((xga->accel.px_map_format[xga->accel.dst_map] >= 0x0a) && (xga->accel.px <= 7) && (xga->accel.py <= 3))
                            xga->accel.pattern = 1;
                    }
                } else {
                    if (!xga->accel.src_map && (xga->accel.dst_map == 1) && (xga->accel.pat_src == 2)) {
                        if ((xga->accel.px_map_format[xga->accel.dst_map] >= 0x0a) && (xga->accel.px <= 7) && (xga->accel.py <= 3)) {
                            if (patheight > 1)
                                xga->accel.pattern = 0;
                            else
                                xga->accel.pattern = 1;
                        } else if (!(xga->accel.px_map_format[xga->accel.dst_map] & 0x08)) {
                            if ((xga->accel.px_map_format[xga->accel.dst_map] >= 0x02) && (xga->accel.px <= 7) && (xga->accel.py <= 3)) {
                                if (patheight > 1)
                                    xga->accel.pattern = 0;
                                else
                                    xga->accel.pattern = 1;
                            }
                        }
                    }
                }
            }
        }

        xga_log("CMD=%08x, EnablePat=%d, PAT%d, SRC%d, DST%d: PatFormat=%x, SrcFormat=%x, DstFormat=%x, PatWidth=%d, PatHeight=%d, SrcWidth=%d, SrcHeight=%d, DstWidth=%d, DstHeight=%d, px=%d, py=%d.\n", xga->accel.command,
              xga->accel.pattern, xga->accel.pat_src, xga->accel.src_map, xga->accel.dst_map, xga->accel.px_map_format[xga->accel.pat_src], (xga->accel.px_map_format[xga->accel.src_map] & 0x0f), (xga->accel.px_map_format[xga->accel.dst_map] & 0x0f),
              patwidth, patheight, srcwidth, srcheight, dstwidth, dstheight, xga->accel.px, xga->accel.py);

        if (((xga->accel.command >> 24) & 0x0f) == 0x0a) {
            if ((xga->accel.bkgd_mix & 0x1f) == 0x05) {
                while (xga->accel.y >= 0) {
                    mix = xga_accel_read_pattern_map_pixel(svga, xga->accel.px, xga->accel.py);
                    if (mix)
                        xga->accel.filling ^= 1;

                    xga_log("Area Fill Command: dx=%d, dy=%d, mix=%x, filling=%x.\n", dx, dy, mix, xga->accel.filling);

                    if (xga->accel.command & 0xc0) {
                        if ((dx >= xga->accel.mask_map_origin_x_off) && (dx <= ((xga->accel.px_map_width[0] & 0xfff) + xga->accel.mask_map_origin_x_off)) && (dy >= xga->accel.mask_map_origin_y_off) && (dy <= ((xga->accel.px_map_height[0] & 0xfff) + xga->accel.mask_map_origin_y_off)) && xga->accel.filling) {
                            src_dat  = (((xga->accel.command >> 28) & 3) == 2) ?
                                xga_accel_read_map_pixel(svga, xga->accel.sx, xga->accel.sy, xga->accel.src_map) :
                                frgdcol;
                            dest_dat = xga_accel_read_map_pixel(svga, dx, dy, xga->accel.dst_map);
                            if ((xga->accel.cc_cond == 4) || ((xga->accel.cc_cond == 1) && (dest_dat > color_cmp)) || ((xga->accel.cc_cond == 2) && (dest_dat == color_cmp)) || ((xga->accel.cc_cond == 3) && (dest_dat < color_cmp)) || ((xga->accel.cc_cond == 5) && (dest_dat >= color_cmp)) || ((xga->accel.cc_cond == 6) && (dest_dat != color_cmp)) || ((xga->accel.cc_cond == 7) && (dest_dat <= color_cmp))) {
                                old_dest_dat = dest_dat;
                                ROP(1, dest_dat, src_dat);
                                dest_dat = (dest_dat & plane_mask) | (old_dest_dat & ~plane_mask);
                                xga_log("XGA Area Fill1: Dest=%02x, Src=%02x, OldD=%02x.\n", dest_dat, src_dat, old_dest_dat);
                                xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                            }
                        }
                    } else {
                        if ((dx >= 0) && (dx <= dstwidth) && (dy >= 0) && (dy <= dstheight) && xga->accel.filling) {
                            src_dat = (((xga->accel.command >> 28) & 3) == 2) ?
                                xga_accel_read_map_pixel(svga, xga->accel.sx, xga->accel.sy, xga->accel.src_map) :
                                frgdcol;
                            dest_dat = xga_accel_read_map_pixel(svga, dx, dy, xga->accel.dst_map);
                            if ((xga->accel.cc_cond == 4) || ((xga->accel.cc_cond == 1) && (dest_dat > color_cmp)) || ((xga->accel.cc_cond == 2) && (dest_dat == color_cmp)) || ((xga->accel.cc_cond == 3) && (dest_dat < color_cmp)) || ((xga->accel.cc_cond == 5) && (dest_dat >= color_cmp)) || ((xga->accel.cc_cond == 6) && (dest_dat != color_cmp)) || ((xga->accel.cc_cond == 7) && (dest_dat <= color_cmp))) {
                                old_dest_dat = dest_dat;
                                ROP(1, dest_dat, src_dat);
                                dest_dat = (dest_dat & plane_mask) | (old_dest_dat & ~plane_mask);
                                xga_log("XGA Area Fill2: Dest=%02x, Src=%02x, OldD=%02x.\n", dest_dat, src_dat, old_dest_dat);
                                xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                            }
                        }
                    }

                    xga->accel.sx = ((xga->accel.sx + xdir) & srcwidth) | (xga->accel.sx & ~srcwidth);
                    xga->accel.px++;

                    dx++;
                    xga->accel.x--;
                    if (xga->accel.x < 0) {
                        xga->accel.y--;
                        xga->accel.x = xga->accel.blt_width & 0xfff;

                        dx = xga->accel.dst_map_x;
                        if (xga->accel.dst_map_x >= 0x1800)
                            dx |= ~0x17ff;

                        xga->accel.sx = xga->accel.src_map_x & 0xfff;
                        xga->accel.px = xga->accel.pat_map_x & 0xfff;

                        xga->accel.sy = ((xga->accel.sy + ydir) & srcheight) | (xga->accel.sy & ~srcheight);
                        xga->accel.py++;

                        dy++;
                        xga->accel.filling = 0;

                        if (xga->accel.y < 0) {
                            xga->accel.dst_map_x = dx;
                            xga->accel.dst_map_y = dy;
                            return;
                        }
                    }
                }
            }
        } else {
            while (xga->accel.y >= 0) {
                mix = xga_accel_read_pattern_map_pixel(svga, xga->accel.px, xga->accel.py);

                if (xga->accel.command & 0xc0) {
                    if ((dx >= xga->accel.mask_map_origin_x_off) && (dx <= ((xga->accel.px_map_width[0] & 0xfff) + xga->accel.mask_map_origin_x_off)) && (dy >= xga->accel.mask_map_origin_y_off) && (dy <= ((xga->accel.px_map_height[0] & 0xfff) + xga->accel.mask_map_origin_y_off))) {
                        if (mix)
                            src_dat = (((xga->accel.command >> 28) & 3) == 2) ?
                                xga_accel_read_map_pixel(svga, xga->accel.sx, xga->accel.sy, xga->accel.src_map) :
                                frgdcol;
                        else
                            src_dat = (((xga->accel.command >> 30) & 3) == 2) ?
                                 xga_accel_read_map_pixel(svga, xga->accel.sx, xga->accel.sy, xga->accel.src_map) :
                                 bkgdcol;

                        dest_dat = xga_accel_read_map_pixel(svga, dx, dy, xga->accel.dst_map);
                        if ((xga->accel.cc_cond == 4) || ((xga->accel.cc_cond == 1) && (dest_dat > color_cmp)) || ((xga->accel.cc_cond == 2) && (dest_dat == color_cmp)) || ((xga->accel.cc_cond == 3) && (dest_dat < color_cmp)) || ((xga->accel.cc_cond == 5) && (dest_dat >= color_cmp)) || ((xga->accel.cc_cond == 6) && (dest_dat != color_cmp)) || ((xga->accel.cc_cond == 7) && (dest_dat <= color_cmp))) {
                            old_dest_dat = dest_dat;
                            ROP(mix, dest_dat, src_dat);
                            dest_dat = (dest_dat & plane_mask) | (old_dest_dat & ~plane_mask);
                            xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                        }
                    }
                } else {
                    if ((dx >= 0) && (dx <= dstwidth) && (dy >= 0) && (dy <= dstheight)) {
                        if (mix)
                            src_dat = (((xga->accel.command >> 28) & 3) == 2) ?
                                xga_accel_read_map_pixel(svga, xga->accel.sx, xga->accel.sy, xga->accel.src_map) :
                                frgdcol;
                        else
                            src_dat = (((xga->accel.command >> 30) & 3) == 2) ?
                                xga_accel_read_map_pixel(svga, xga->accel.sx, xga->accel.sy, xga->accel.src_map) :
                                bkgdcol;

                        dest_dat = xga_accel_read_map_pixel(svga, dx, dy, xga->accel.dst_map);
                        if ((xga->accel.cc_cond == 4) || ((xga->accel.cc_cond == 1) && (dest_dat > color_cmp)) || ((xga->accel.cc_cond == 2) && (dest_dat == color_cmp)) || ((xga->accel.cc_cond == 3) && (dest_dat < color_cmp)) || ((xga->accel.cc_cond == 5) && (dest_dat >= color_cmp)) || ((xga->accel.cc_cond == 6) && (dest_dat != color_cmp)) || ((xga->accel.cc_cond == 7) && (dest_dat <= color_cmp))) {
                            old_dest_dat = dest_dat;
                            ROP(mix, dest_dat, src_dat);
                            dest_dat = (dest_dat & plane_mask) | (old_dest_dat & ~plane_mask);
                            xga_accel_write_map_pixel(svga, dx, dy, xga->accel.dst_map, dest_dat);
                        }
                    }
                }

                xga->accel.sx += xdir;

                if (xga->accel.pattern)
                    xga->accel.px = ((xga->accel.px + xdir) & patwidth) | (xga->accel.px & ~patwidth);
                else
                    xga->accel.px += xdir;

                xga_log("MIX=%d, DX=%d, DY=%d, LX=%d, LY=%d, PX=%d, PY=%d, SX=%d, SY=%d.\n", mix, dx, dy, xga->accel.x, xga->accel.y, xga->accel.px, xga->accel.py, xga->accel.sx, xga->accel.sy);

                dx += xdir;

                xga->accel.x--;
                if (xga->accel.x < 0) {
                    xga->accel.y--;
                    xga->accel.x = xga->accel.blt_width & 0xfff;

                    dx = xga->accel.dst_map_x;
                    if (xga->accel.dst_map_x >= 0x1800)
                        dx |= ~0x17ff;

                    xga->accel.sx = xga->accel.src_map_x & 0xfff;
                    xga->accel.px = xga->accel.pat_map_x & 0xfff;

                    xga->accel.sy += ydir;

                    if (xga->accel.pattern)
                        xga->accel.py = ((xga->accel.py + ydir) & patheight) | (xga->accel.py & ~patheight);
                    else
                        xga->accel.py += ydir;

                    dy += ydir;

                    if (xga->accel.y < 0) {
                        xga->accel.dst_map_x = dx;
                        xga->accel.dst_map_y = dy;
                        return;
                    }
                }
            }
        }
    }
}

static void
xga_mem_write(uint32_t addr, uint32_t val, xga_t *xga, svga_t *svga, int len)
{
    uint32_t min_addr;
    uint32_t max_addr;
    int mmio_addr_enable = 0;

    if (xga_standalone_enabled) {
        addr &= 0x1fff;
        min_addr = (0x1c00 + (xga->instance << 7));
        max_addr = (0x1c00 + (xga->instance << 7)) + 0x7f;
    } else {
        addr &= 0x7fff;
        min_addr = (0x7c00 + (xga->instance << 7));
        max_addr = (0x7c00 + (xga->instance << 7)) + 0x7f;
    }

    if ((addr >= min_addr) && (addr <= max_addr))
        mmio_addr_enable = 1;

    if (mmio_addr_enable) {
        switch (addr & 0x7f) {
            case 0x11:
                xga->accel.control = val;
                xga_log("Control=%02x.\n", val);
                break;

            case 0x12:
                xga->accel.px_map_idx = val & 3;
                break;

            case 0x14:
                xga_log("MMIO14, len=%d, val=%08x, pxmapidx=%d.\n", len, val, xga->accel.px_map_idx);
                if (len == 4)
                    xga->accel.px_map_base[xga->accel.px_map_idx] = val;
                else if (len == 2)
                    xga->accel.px_map_base[xga->accel.px_map_idx] = (xga->accel.px_map_base[xga->accel.px_map_idx] & 0xffff0000) | val;
                else
                    xga->accel.px_map_base[xga->accel.px_map_idx] = (xga->accel.px_map_base[xga->accel.px_map_idx] & 0xffffff00) | val;
                break;
            case 0x15:
                if (len == 1)
                    xga->accel.px_map_base[xga->accel.px_map_idx] = (xga->accel.px_map_base[xga->accel.px_map_idx] & 0xffff00ff) | (val << 8);
                break;
            case 0x16:
                xga_log("MMIO16, len=%d, val=%08x, pxmapidx=%d.\n", len, val, xga->accel.px_map_idx);
                if (len == 2)
                    xga->accel.px_map_base[xga->accel.px_map_idx] = (xga->accel.px_map_base[xga->accel.px_map_idx] & 0x0000ffff) | (val << 16);
                else
                    xga->accel.px_map_base[xga->accel.px_map_idx] = (xga->accel.px_map_base[xga->accel.px_map_idx] & 0xff00ffff) | (val << 16);
                break;
            case 0x17:
                if (len == 1)
                    xga->accel.px_map_base[xga->accel.px_map_idx] = (xga->accel.px_map_base[xga->accel.px_map_idx] & 0x00ffffff) | (val << 24);
                break;

            case 0x18:
                if (len == 4) {
                    xga->accel.px_map_width[xga->accel.px_map_idx]  = val & 0xffff;
                    xga->accel.px_map_height[xga->accel.px_map_idx] = (val >> 16) & 0xffff;
                } else if (len == 2) {
                    xga->accel.px_map_width[xga->accel.px_map_idx] = val & 0xffff;
                    xga_log("MMIO18, len=2, val=%04x, pxmapidx=%d.\n", val & 0xffff, xga->accel.px_map_idx);
                } else
                    xga->accel.px_map_width[xga->accel.px_map_idx] = (xga->accel.px_map_width[xga->accel.px_map_idx] & 0xff00) | val;
                break;
            case 0x19:
                xga_log("MMIO19, len=%d, val=%08x.\n", len, val);
                if (len == 1)
                    xga->accel.px_map_width[xga->accel.px_map_idx] = (xga->accel.px_map_width[xga->accel.px_map_idx] & 0xff) | (val << 8);
                break;

            case 0x1a:
                if (len == 2)
                    xga->accel.px_map_height[xga->accel.px_map_idx] = val & 0xffff;
                else
                    xga->accel.px_map_height[xga->accel.px_map_idx] = (xga->accel.px_map_height[xga->accel.px_map_idx] & 0xff00) | val;
                break;
            case 0x1b:
                if (len == 1)
                    xga->accel.px_map_height[xga->accel.px_map_idx] = (xga->accel.px_map_height[xga->accel.px_map_idx] & 0xff) | (val << 8);
                break;

            case 0x1c:
                xga->accel.px_map_format[xga->accel.px_map_idx] = val;
                if (val & 0x08)
                    xga_log("Big Endian Pixel Format=%d, AccessMode=%x.\n", xga->accel.px_map_idx, xga->access_mode & 0x08);
                else
                    xga_log("Little Endian Pixel Format=%d, AccessMode=%x.\n", xga->accel.px_map_idx, xga->access_mode & 0x08);
                break;

            case 0x20:
                if (len >= 2) {
                    xga->accel.bres_err_term = val & 0x3fff;
                    if (val & 0x2000)
                        xga->accel.bres_err_term |= ~0x1fff;
                } else
                    xga->accel.bres_err_term = (xga->accel.bres_err_term & 0x3f00) | val;
                break;
            case 0x21:
                if (len == 1) {
                    xga->accel.bres_err_term = (xga->accel.bres_err_term & 0xff) | ((val & 0x3f) << 8);
                    if (val & 0x20)
                        xga->accel.bres_err_term |= ~0x1fff;
                }
                break;

            case 0x24:
                if (len >= 2) {
                    xga->accel.bres_k1 = val & 0x3fff;
                    if (val & 0x2000)
                        xga->accel.bres_k1 |= ~0x1fff;
                } else
                    xga->accel.bres_k1 = (xga->accel.bres_k1 & 0x3f00) | val;
                break;
            case 0x25:
                if (len == 1) {
                    xga->accel.bres_k1 = (xga->accel.bres_k1 & 0xff) | ((val & 0x3f) << 8);
                    if (val & 0x20)
                        xga->accel.bres_k1 |= ~0x1fff;
                }
                break;

            case 0x28:
                if (len >= 2) {
                    xga->accel.bres_k2 = val & 0x3fff;
                    if (val & 0x2000)
                        xga->accel.bres_k2 |= ~0x1fff;
                } else
                    xga->accel.bres_k2 = (xga->accel.bres_k2 & 0x3f00) | val;
                break;
            case 0x29:
                if (len == 1) {
                    xga->accel.bres_k2 = (xga->accel.bres_k2 & 0xff) | ((val & 0x3f) << 8);
                    if (val & 0x20)
                        xga->accel.bres_k2 |= ~0x1fff;
                }
                break;

            case 0x2c:
                if (len == 4) {
                    xga->accel.short_stroke         = val;
                    xga->accel.short_stroke_vector1 = xga->accel.short_stroke & 0xff;
                    xga->accel.short_stroke_vector2 = (xga->accel.short_stroke >> 8) & 0xff;
                    xga->accel.short_stroke_vector3 = (xga->accel.short_stroke >> 16) & 0xff;
                    xga->accel.short_stroke_vector4 = (xga->accel.short_stroke >> 24) & 0xff;

                    xga_log("1Vector = %02x, 2Vector = %02x, 3Vector = %02x, 4Vector = %02x\n",
                            xga->accel.short_stroke_vector1, xga->accel.short_stroke_vector2,
                            xga->accel.short_stroke_vector3, xga->accel.short_stroke_vector4);

                    xga_short_stroke(svga, xga->accel.short_stroke_vector1);
                    xga_short_stroke(svga, xga->accel.short_stroke_vector2);
                    xga_short_stroke(svga, xga->accel.short_stroke_vector3);
                    xga_short_stroke(svga, xga->accel.short_stroke_vector4);
                } else if (len == 2)
                    xga->accel.short_stroke = (xga->accel.short_stroke & 0xffff0000) | val;
                else
                    xga->accel.short_stroke = (xga->accel.short_stroke & 0xffffff00) | val;
                break;
            case 0x2d:
                if (len == 1)
                    xga->accel.short_stroke = (xga->accel.short_stroke & 0xffff00ff) | (val << 8);
                break;
            case 0x2e:
                if (len == 2) {
                    xga->accel.short_stroke = (xga->accel.short_stroke & 0x0000ffff) | (val << 16);
                } else
                    xga->accel.short_stroke = (xga->accel.short_stroke & 0xff00ffff) | (val << 16);
                break;
            case 0x2f:
                if (len == 1) {
                    xga->accel.short_stroke = (xga->accel.short_stroke & 0x00ffffff) | (val << 24);
                }
                break;

            case 0x48:
                xga->accel.frgd_mix = val & 0xff;
                if (len == 4) {
                    xga->accel.bkgd_mix = (val >> 8) & 0xff;
                    xga->accel.cc_cond  = (val >> 16) & 0x07;
                } else if (len == 2) {
                    xga->accel.bkgd_mix = (val >> 8) & 0xff;
                }
                break;

            case 0x49:
                xga->accel.bkgd_mix = val & 0xff;
                break;

            case 0x4a:
                xga->accel.cc_cond = val & 0x07;
                break;

            case 0x4c:
                if (len == 4)
                    xga->accel.color_cmp = val;
                else if (len == 2)
                    xga->accel.color_cmp = (xga->accel.color_cmp & 0xffff0000) | val;
                else
                    xga->accel.color_cmp = (xga->accel.color_cmp & 0xffffff00) | val;
                break;
            case 0x4d:
                if (len == 1)
                    xga->accel.color_cmp = (xga->accel.color_cmp & 0xffff00ff) | (val << 8);
                break;
            case 0x4e:
                if (len == 2)
                    xga->accel.color_cmp = (xga->accel.color_cmp & 0x0000ffff) | (val << 16);
                else
                    xga->accel.color_cmp = (xga->accel.color_cmp & 0xff00ffff) | (val << 16);
                break;
            case 0x4f:
                if (len == 1)
                    xga->accel.color_cmp = (xga->accel.color_cmp & 0x00ffffff) | (val << 24);
                break;

            case 0x50:
                if (len == 4)
                    xga->accel.plane_mask = val;
                else if (len == 2)
                    xga->accel.plane_mask = (xga->accel.plane_mask & 0xffff0000) | val;
                else
                    xga->accel.plane_mask = (xga->accel.plane_mask & 0xffffff00) | val;
                break;
            case 0x51:
                if (len == 1)
                    xga->accel.plane_mask = (xga->accel.plane_mask & 0xffff00ff) | (val << 8);
                break;
            case 0x52:
                if (len == 2)
                    xga->accel.plane_mask = (xga->accel.plane_mask & 0x0000ffff) | (val << 16);
                else
                    xga->accel.plane_mask = (xga->accel.plane_mask & 0xff00ffff) | (val << 16);
                break;
            case 0x53:
                if (len == 1)
                    xga->accel.plane_mask = (xga->accel.plane_mask & 0x00ffffff) | (val << 24);
                break;

            case 0x54:
                xga_log("MMIO54, len=%d, val=%08x.\n", len, val);
                if (len == 4)
                    xga->accel.carry_chain = val;
                else if (len == 2)
                    xga->accel.carry_chain = (xga->accel.carry_chain & 0xffff0000) | val;
                else
                    xga->accel.carry_chain = (xga->accel.carry_chain & 0xffffff00) | val;
                break;
            case 0x55:
                if (len == 1)
                    xga->accel.carry_chain = (xga->accel.carry_chain & 0xffff00ff) | (val << 8);
                break;
            case 0x56:
                xga_log("MMIO56, len=%d, val=%04x.\n", len, val);
                if (len == 2)
                    xga->accel.carry_chain = (xga->accel.carry_chain & 0x0000ffff) | (val << 16);
                else
                    xga->accel.carry_chain = (xga->accel.carry_chain & 0xff00ffff) | (val << 16);
                break;
            case 0x57:
                if (len == 1)
                    xga->accel.carry_chain = (xga->accel.carry_chain & 0x00ffffff) | (val << 24);
                break;


            case 0x58:
                if (len == 4)
                    xga->accel.frgd_color = val;
                else if (len == 2)
                    xga->accel.frgd_color = (xga->accel.frgd_color & 0xffff0000) | val;
                else
                    xga->accel.frgd_color = (xga->accel.frgd_color & 0xffffff00) | val;
                break;
            case 0x59:
                if (len == 1)
                    xga->accel.frgd_color = (xga->accel.frgd_color & 0xffff00ff) | (val << 8);
                break;
            case 0x5a:
                if (len == 2)
                    xga->accel.frgd_color = (xga->accel.frgd_color & 0x0000ffff) | (val << 16);
                else
                    xga->accel.frgd_color = (xga->accel.frgd_color & 0xff00ffff) | (val << 16);
                break;
            case 0x5b:
                if (len == 1)
                    xga->accel.frgd_color = (xga->accel.frgd_color & 0x00ffffff) | (val << 24);
                break;

            case 0x5c:
                if (len == 4)
                    xga->accel.bkgd_color = val;
                else if (len == 2)
                    xga->accel.bkgd_color = (xga->accel.bkgd_color & 0xffff0000) | val;
                else
                    xga->accel.bkgd_color = (xga->accel.bkgd_color & 0xffffff00) | val;
                break;
            case 0x5d:
                if (len == 1)
                    xga->accel.bkgd_color = (xga->accel.bkgd_color & 0xffff00ff) | (val << 8);
                break;
            case 0x5e:
                if (len == 2)
                    xga->accel.bkgd_color = (xga->accel.bkgd_color & 0x0000ffff) | (val << 16);
                else
                    xga->accel.bkgd_color = (xga->accel.bkgd_color & 0xff00ffff) | (val << 16);
                break;
            case 0x5f:
                if (len == 1)
                    xga->accel.bkgd_color = (xga->accel.bkgd_color & 0x00ffffff) | (val << 24);
                break;

            case 0x60:
                if (len == 4) {
                    xga->accel.blt_width  = val & 0xffff;
                    xga->accel.blt_height = (val >> 16) & 0xffff;
                } else if (len == 2) {
                    xga->accel.blt_width = val;
                } else
                    xga->accel.blt_width = (xga->accel.blt_width & 0xff00) | val;
                break;
            case 0x61:
                if (len == 1)
                    xga->accel.blt_width = (xga->accel.blt_width & 0xff) | (val << 8);
                break;

            case 0x62:
                if (len == 2)
                    xga->accel.blt_height = val;
                else
                    xga->accel.blt_height = (xga->accel.blt_height & 0xff00) | val;
                break;
            case 0x63:
                if (len == 1)
                    xga->accel.blt_height = (xga->accel.blt_height & 0xff) | (val << 8);
                break;

            case 0x6c:
                if (len == 4) {
                    xga->accel.mask_map_origin_x_off = val & 0xffff;
                    xga->accel.mask_map_origin_y_off = (val >> 16) & 0xffff;
                } else if (len == 2) {
                    xga->accel.mask_map_origin_x_off = val;
                } else
                    xga->accel.mask_map_origin_x_off = (xga->accel.mask_map_origin_x_off & 0xff00) | val;
                break;
            case 0x6d:
                if (len == 1)
                    xga->accel.mask_map_origin_x_off = (xga->accel.mask_map_origin_x_off & 0xff) | (val << 8);
                break;

            case 0x6e:
                if (len == 2)
                    xga->accel.mask_map_origin_y_off = val;
                else
                    xga->accel.mask_map_origin_y_off = (xga->accel.mask_map_origin_y_off & 0xff00) | val;
                break;
            case 0x6f:
                if (len == 1)
                    xga->accel.mask_map_origin_y_off = (xga->accel.mask_map_origin_y_off & 0xff) | (val << 8);
                break;

            case 0x70:
                if (len == 4) {
                    xga->accel.src_map_x = val & 0xffff;
                    xga->accel.src_map_y = (val >> 16) & 0xffff;
                } else if (len == 2)
                    xga->accel.src_map_x = val;
                else
                    xga->accel.src_map_x = (xga->accel.src_map_x & 0xff00) | val;
                break;
            case 0x71:
                if (len == 1)
                    xga->accel.src_map_x = (xga->accel.src_map_x & 0xff) | (val << 8);
                break;

            case 0x72:
                if (len == 2)
                    xga->accel.src_map_y = val;
                else
                    xga->accel.src_map_y = (xga->accel.src_map_y & 0xff00) | val;
                break;
            case 0x73:
                if (len == 1)
                    xga->accel.src_map_y = (xga->accel.src_map_y & 0xff) | (val << 8);
                break;

            case 0x74:
                if (len == 4) {
                    xga->accel.pat_map_x = val & 0xffff;
                    xga->accel.pat_map_y = (val >> 16) & 0xffff;
                } else if (len == 2)
                    xga->accel.pat_map_x = val;
                else
                    xga->accel.pat_map_x = (xga->accel.pat_map_x & 0xff00) | val;
                break;
            case 0x75:
                if (len == 1)
                    xga->accel.pat_map_x = (xga->accel.pat_map_x & 0xff) | (val << 8);
                break;

            case 0x76:
                if (len == 2)
                    xga->accel.pat_map_y = val;
                else
                    xga->accel.pat_map_y = (xga->accel.pat_map_y & 0xff00) | val;
                break;
            case 0x77:
                if (len == 1)
                    xga->accel.pat_map_y = (xga->accel.pat_map_y & 0xff) | (val << 8);
                break;

            case 0x78:
                if (len == 4) {
                    xga->accel.dst_map_x = val & 0xffff;
                    xga->accel.dst_map_y = (val >> 16) & 0xffff;
                } else if (len == 2)
                    xga->accel.dst_map_x = val;
                else
                    xga->accel.dst_map_x = (xga->accel.dst_map_x & 0xff00) | val;
                break;
            case 0x79:
                if (len == 1)
                    xga->accel.dst_map_x = (xga->accel.dst_map_x & 0xff) | (val << 8);
                break;

            case 0x7a:
                if (len == 2)
                    xga->accel.dst_map_y = val;
                else
                    xga->accel.dst_map_y = (xga->accel.dst_map_y & 0xff00) | val;
                break;
            case 0x7b:
                if (len == 1)
                    xga->accel.dst_map_y = (xga->accel.dst_map_y & 0xff) | (val << 8);
                break;

            case 0x7c:
                if (len == 4) {
                    xga->accel.command = val;
exec_command:
                    xga->accel.octant    = xga->accel.command & 0x07;
                    xga->accel.draw_mode = xga->accel.command & 0x30;
                    xga->accel.pat_src   = ((xga->accel.command >> 12) & 0x0f);
                    xga->accel.dst_map   = ((xga->accel.command >> 16) & 0x0f);
                    xga->accel.src_map   = ((xga->accel.command >> 20) & 0x0f);
                    xga_log("PATMAP=%x, DSTMAP=%x, SRCMAP=%x.\n", xga->accel.px_map_format[xga->accel.pat_src], xga->accel.px_map_format[xga->accel.dst_map], xga->accel.px_map_format[xga->accel.src_map]);

                    xga_log("[%04X:%08X]: Accel Command = %02x, full = %08x, patwidth = %d, "
                                "dstwidth = %d, srcwidth = %d, patheight = %d, dstheight = %d, "
                                "srcheight = %d, px = %d, py = %d, dx = %d, dy = %d, sx = %d, "
                                "sy = %d, patsrc = %d, dstmap = %d, srcmap = %d, dstbase = %08x, "
                                "srcbase = %08x, patbase = %08x, dstformat = %x, srcformat = %x, "
                                "planemask = %08x.\n",
                                CS, cpu_state.pc, ((xga->accel.command >> 24) & 0x0f),
                                xga->accel.command, xga->accel.px_map_width[xga->accel.pat_src],
                                xga->accel.px_map_width[xga->accel.dst_map],
                                xga->accel.px_map_width[xga->accel.src_map],
                                xga->accel.px_map_height[xga->accel.pat_src],
                                xga->accel.px_map_height[xga->accel.dst_map],
                                xga->accel.px_map_height[xga->accel.src_map],
                                xga->accel.pat_map_x, xga->accel.pat_map_y,
                                xga->accel.dst_map_x, xga->accel.dst_map_y,
                                xga->accel.src_map_x, xga->accel.src_map_y,
                                xga->accel.pat_src, xga->accel.dst_map,
                                xga->accel.src_map, xga->accel.px_map_base[xga->accel.dst_map],
                                xga->accel.px_map_base[xga->accel.src_map],
                                xga->accel.px_map_base[xga->accel.pat_src],
                                xga->accel.px_map_format[xga->accel.dst_map] & 0x0f,
                                xga->accel.px_map_format[xga->accel.src_map] & 0x0f,
                                xga->accel.plane_mask);

                    switch ((xga->accel.command >> 24) & 0x0f) {
                        case 2: /*Short Stroke Vectors Read */
                            xga_log("Short Stroke Vectors Read.\n");
                            break;
                        case 3: /*Bresenham Line Draw Read*/
                            xga_log("Line Draw Read\n");
                            break;
                        case 4: /*Short Stroke Vectors Write*/
                            xga_log("Short Stroke Vectors Write.\n");
                            break;
                        case 5: /*Bresenham Line Draw Write*/
                            xga_log("Line Draw Write.\n");
                            xga_line_draw_write(svga);
                            break;
                        case 8: /*BitBLT*/
                            xga_log("Normal BitBLT.\n");
                            xga_bitblt(svga);
                            break;
                        case 9: /*Inverting BitBLT*/
                            xga_log("Inverting BitBLT\n");
                            break;
                        case 0x0a: /*Area Fill*/
                            xga_log("Area Fill.\n");
                            xga_bitblt(svga);
                            break;

                        default:
                            break;
                    }
                } else if (len == 2) {
                    xga->accel.command = (xga->accel.command & 0xffff0000) | val;
                } else
                    xga->accel.command = (xga->accel.command & 0xffffff00) | val;
                break;
            case 0x7d:
                if (len == 1)
                    xga->accel.command = (xga->accel.command & 0xffff00ff) | (val << 8);
                break;
            case 0x7e:
                if (len == 2) {
                    xga->accel.command = (xga->accel.command & 0x0000ffff) | (val << 16);
                    goto exec_command;
                } else
                    xga->accel.command = (xga->accel.command & 0xff00ffff) | (val << 16);
                break;
            case 0x7f:
                if (len == 1) {
                    xga->accel.command = (xga->accel.command & 0x00ffffff) | (val << 24);
                    goto exec_command;
                }
                break;

            default:
                break;
        }
    }
}

static void
xga_memio_writeb(uint32_t addr, uint8_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    xga_mem_write(addr, val, xga, svga, 1);

    xga_log("[%04X:%08X]: Write MEMIOB = %04x, val = %02x\n", CS, cpu_state.pc, addr, val);
}

static void
xga_memio_writew(uint32_t addr, uint16_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    xga_mem_write(addr, val, xga, svga, 2);

    xga_log("[%04X:%08X]: Write MEMIOW = %04x, val = %04x\n", CS, cpu_state.pc, addr, val);
}

static void
xga_memio_writel(uint32_t addr, uint32_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    xga_mem_write(addr, val, xga, svga, 4);

    xga_log("[%04X:%08X]: Write MEMIOL = %04x, val = %08x\n", CS, cpu_state.pc, addr, val);
}

static uint8_t
xga_mem_read(uint32_t addr, xga_t *xga, UNUSED(svga_t *svga))
{
    uint32_t min_addr;
    uint32_t max_addr;
    uint8_t temp = 0;
    int mmio_addr_enable = 0;

    if (xga_standalone_enabled) {
        addr &= 0x1fff;
        min_addr = (0x1c00 + (xga->instance << 7));
        max_addr = (0x1c00 + (xga->instance << 7)) + 0x7f;
        if (addr < 0x1c00)
            temp = xga->bios_rom.rom[addr];
        else if ((addr >= 0x1c00) && (addr <= 0x1c7f) && xga->instance)
            temp = 0xff;
        else if ((addr >= min_addr) && (addr <= max_addr))
            mmio_addr_enable = 1;
    } else {
        addr &= 0x7fff;
        min_addr = (0x7c00 + (xga->instance << 7));
        max_addr = (0x7c00 + (xga->instance << 7)) + 0x7f;
        if (addr < 0x7c00)
            temp = xga->bios_rom.rom[addr];
        else if ((addr >= 0x7c00) && (addr <= 0x7c7f) && xga->instance)
            temp = 0xff;
        else if ((addr >= min_addr) && (addr <= max_addr))
            mmio_addr_enable = 1;
    }

    if (mmio_addr_enable) {
        switch (addr & 0x7f) {
            case 0x11:
                temp = xga->accel.control;
                if (xga->accel.control & 0x08)
                    temp |= 0x10;
                else
                    temp &= ~0x10;
                break;

            case 0x20:
                temp = xga->accel.bres_err_term & 0xff;
                break;
            case 0x21:
                temp = xga->accel.bres_err_term >> 8;
                break;
            case 0x22:
                temp = xga->accel.bres_err_term >> 16;
                break;
            case 0x23:
                temp = xga->accel.bres_err_term >> 24;
                break;

            case 0x70:
                temp = xga->accel.src_map_x & 0xff;
                break;
            case 0x71:
                temp = xga->accel.src_map_x >> 8;
                break;

            case 0x72:
                temp = xga->accel.src_map_y & 0xff;
                break;
            case 0x73:
                temp = xga->accel.src_map_y >> 8;
                break;

            case 0x74:
                temp = xga->accel.pat_map_x & 0xff;
                break;
            case 0x75:
                temp = xga->accel.pat_map_x >> 8;
                break;

            case 0x76:
                temp = xga->accel.pat_map_y & 0xff;
                break;
            case 0x77:
                temp = xga->accel.pat_map_y >> 8;
                break;

            case 0x78:
                temp = xga->accel.dst_map_x & 0xff;
                break;
            case 0x79:
                temp = xga->accel.dst_map_x >> 8;
                break;

            case 0x7a:
                temp = xga->accel.dst_map_y & 0xff;
                break;
            case 0x7b:
                temp = xga->accel.dst_map_y >> 8;
                break;

            default:
                break;
        }
        xga_log("MMIO Addr=%02x, ret=%02x.\n", addr & 0x7f, temp);
    }
    return temp;
}

static uint8_t
xga_memio_readb(uint32_t addr, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;
    uint8_t temp;

    temp = xga_mem_read(addr, xga, svga);

    xga_log("[%04X:%08X]: Read MEMIOB = %04x, temp = %02x\n", CS, cpu_state.pc, addr, temp);

    return temp;
}

static uint16_t
xga_memio_readw(uint32_t addr, void *priv)
{
    svga_t  *svga = (svga_t *) priv;
    xga_t   *xga  = (xga_t *) svga->xga;
    uint16_t temp;

    temp = xga_mem_read(addr, xga, svga);
    temp |= (xga_mem_read(addr + 1, xga, svga) << 8);

    xga_log("[%04X:%08X]: Read MEMIOW = %04x, temp = %04x\n", CS, cpu_state.pc, addr, temp);

    return temp;
}

static uint32_t
xga_memio_readl(uint32_t addr, void *priv)
{
    svga_t  *svga = (svga_t *) priv;
    xga_t   *xga  = (xga_t *) svga->xga;
    uint32_t temp;

    temp = xga_mem_read(addr, xga, svga);
    temp |= (xga_mem_read(addr + 1, xga, svga) << 8);
    temp |= (xga_mem_read(addr + 2, xga, svga) << 16);
    temp |= (xga_mem_read(addr + 3, xga, svga) << 24);

    xga_log("[%04X:%08X]: Read MEMIOL = %04x, temp = %08x\n", CS, cpu_state.pc, addr, temp);

    return temp;
}

static void
xga_hwcursor_draw(svga_t *svga, int displine)
{
    xga_t          *xga    = (xga_t *) svga->xga;
    int             comb;
    uint8_t         dat    = 0;
    int             offset = xga->hwcursor_latch.x - xga->hwcursor_latch.xoff;
    int             idx    = 0;
    int             x_pos;
    int             y_pos;
    uint32_t       *p;
    const uint8_t  *cd;

    if (xga->hwcursor_latch.xoff & 0x20)
        idx = 32;

    cd = (uint8_t *) xga->sprite_data;

    if (xga->interlace && xga->hwcursor_oddeven)
        xga->hwcursor_latch.addr += 16;

    for (int x = 0; x < xga->hwcursor_latch.cur_xsize; x++) {
        dat = cd[xga->hwcursor_latch.addr & 0x3ff];

        comb = (dat >> ((x & 0x03) << 1)) & 0x03;

        y_pos = displine;
        x_pos = offset + svga->x_add;
        p     = buffer32->line[y_pos];

        if (x >= idx) {
            switch (comb) {
                case 0x00:
                    /* Cursor Color 1 */
                    p[x_pos] = xga->hwc_color0;
                    break;
                case 0x01:
                    /* Cursor Color 2 */
                    p[x_pos] = xga->hwc_color1;
                    break;
                case 0x03:
                    /* Complement */
                    p[x_pos] ^= 0xffffff;
                    break;

                default:
                    break;
            }
        }
        offset++;
        xga_log("P=%08x, xpos=%d, comb=%x, ypos=%d, offset=%d, latchx=%d, latchxoff=%d.\n", p[x_pos], x_pos, comb, y_pos, offset, xga->hwcursor_latch.x, xga->hwcursor_latch.xoff);

        if ((x & 0x03) == 0x03)
            xga->hwcursor_latch.addr++;
    }

    if (xga->interlace && !xga->hwcursor_oddeven)
        xga->hwcursor_latch.addr += 16;
}

static void
xga_render_overscan_left(xga_t *xga, svga_t *svga)
{
    if ((xga->displine + svga->y_add) < 0)
        return;

    if (svga->scrblank || (xga->h_disp == 0))
        return;

    uint32_t *line_ptr = buffer32->line[xga->displine + svga->y_add];
    for (int i = 0; i < svga->x_add; i++)
        *line_ptr++ = svga->overscan_color;
}

static void
xga_render_overscan_right(xga_t *xga, svga_t *svga)
{
    int right;

    if ((xga->displine + svga->y_add) < 0)
        return;

    if (svga->scrblank || (xga->h_disp == 0))
        return;

    uint32_t *line_ptr = &buffer32->line[xga->displine + svga->y_add][svga->x_add + xga->h_disp];
    right              = (overscan_x >> 1);
    for (int i = 0; i < right; i++)
        *line_ptr++ = svga->overscan_color;
}

static void
xga_render_4bpp(svga_t *svga)
{
    xga_t *xga = (xga_t *) svga->xga;
    uint32_t *p;
    uint32_t  dat;

    if ((xga->displine + svga->y_add) < 0)
        return;

    if (xga->changedvram[xga->memaddr >> 12] || xga->changedvram[(xga->memaddr >> 12) + 1] || svga->fullchange) {
        p = &buffer32->line[xga->displine + svga->y_add][svga->x_add];

        if (xga->firstline_draw == 2000)
            xga->firstline_draw = xga->displine;
        xga->lastline_draw = xga->displine;

        for (int x = 0; x <= xga->h_disp; x += 16) {
            dat  = *(uint32_t *) (&xga->vram[xga->memaddr & xga->vram_mask]);
            p[1] = xga->pallook[dat & 0x0f];
            p[0] = xga->pallook[(dat >> 4) & 0x0f];
            p[3] = xga->pallook[(dat >> 8) & 0x0f];
            p[2] = xga->pallook[(dat >> 12) & 0x0f];
            p[5] = xga->pallook[(dat >> 16) & 0x0f];
            p[4] = xga->pallook[(dat >> 20) & 0x0f];
            p[7] = xga->pallook[(dat >> 24) & 0x0f];
            p[6] = xga->pallook[(dat >> 28) & 0x0f];

            dat  = *(uint32_t *) (&xga->vram[(xga->memaddr + 4) & xga->vram_mask]);
            p[9] = xga->pallook[dat & 0x0f];
            p[8] = xga->pallook[(dat >> 4) & 0x0f];
            p[11] = xga->pallook[(dat >> 8) & 0x0f];
            p[10] = xga->pallook[(dat >> 12) & 0x0f];
            p[13] = xga->pallook[(dat >> 16) & 0x0f];
            p[12] = xga->pallook[(dat >> 20) & 0x0f];
            p[15] = xga->pallook[(dat >> 24) & 0x0f];
            p[14] = xga->pallook[(dat >> 28) & 0x0f];

            xga->memaddr += 8;
            p += 16;
        }
        xga->memaddr &= xga->vram_mask;
    }
}

static void
xga_render_8bpp(svga_t *svga)
{
    xga_t *xga = (xga_t *) svga->xga;
    uint32_t *p;
    uint32_t  dat;

    if ((xga->displine + svga->y_add) < 0)
        return;

    if (xga->changedvram[xga->memaddr >> 12] || xga->changedvram[(xga->memaddr >> 12) + 1] || svga->fullchange) {
        p = &buffer32->line[xga->displine + svga->y_add][svga->x_add];

        if (xga->firstline_draw == 2000)
            xga->firstline_draw = xga->displine;
        xga->lastline_draw = xga->displine;

        for (int x = 0; x <= xga->h_disp; x += 8) {
            dat  = *(uint32_t *) (&xga->vram[xga->memaddr & xga->vram_mask]);
            p[0] = xga->pallook[dat & 0xff];
            p[1] = xga->pallook[(dat >> 8) & 0xff];
            p[2] = xga->pallook[(dat >> 16) & 0xff];
            p[3] = xga->pallook[(dat >> 24) & 0xff];

            dat  = *(uint32_t *) (&xga->vram[(xga->memaddr + 4) & xga->vram_mask]);
            p[4] = xga->pallook[dat & 0xff];
            p[5] = xga->pallook[(dat >> 8) & 0xff];
            p[6] = xga->pallook[(dat >> 16) & 0xff];
            p[7] = xga->pallook[(dat >> 24) & 0xff];

            xga->memaddr += 8;
            p += 8;
        }
        xga->memaddr &= xga->vram_mask;
    }
}

static void
xga_render_16bpp(svga_t *svga)
{
    xga_t *xga = (xga_t *) svga->xga;
    int       x;
    uint32_t *p;
    uint32_t  dat;

    if ((xga->displine + svga->y_add) < 0)
        return;

    if (xga->changedvram[xga->memaddr >> 12] || xga->changedvram[(xga->memaddr >> 12) + 1] || svga->fullchange) {
        p = &buffer32->line[xga->displine + svga->y_add][svga->x_add];

        if (xga->firstline_draw == 2000)
            xga->firstline_draw = xga->displine;
        xga->lastline_draw = xga->displine;

        for (x = 0; x <= xga->h_disp; x += 8) {
            dat      = *(uint32_t *) (&xga->vram[(xga->memaddr + (x << 1)) & xga->vram_mask]);
            p[x]     = video_16to32[dat & 0xffff];
            p[x + 1] = video_16to32[dat >> 16];

            dat      = *(uint32_t *) (&xga->vram[(xga->memaddr + (x << 1) + 4) & xga->vram_mask]);
            p[x + 2] = video_16to32[dat & 0xffff];
            p[x + 3] = video_16to32[dat >> 16];

            dat      = *(uint32_t *) (&xga->vram[(xga->memaddr + (x << 1) + 8) & xga->vram_mask]);
            p[x + 4] = video_16to32[dat & 0xffff];
            p[x + 5] = video_16to32[dat >> 16];

            dat      = *(uint32_t *) (&xga->vram[(xga->memaddr + (x << 1) + 12) & xga->vram_mask]);
            p[x + 6] = video_16to32[dat & 0xffff];
            p[x + 7] = video_16to32[dat >> 16];
        }
        xga->memaddr += x << 1;
        xga->memaddr &= xga->vram_mask;
    }
}

void
xga_write_test(uint32_t addr, uint8_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga = (xga_t *) svga->xga;

    if (xga_active && xga) {
        if ((xga->op_mode & 7) == 4) {
            if (xga->aperture_cntl && !xga->test_stage) {
                if (val == 0xa5) { /*Memory size test of XGA*/
                    xga->test    = val;
                    if (addr == (xga->mapping_base + 0x0001))
                        xga->a5_test = 1;
                    else if (addr == (xga->mapping_base + xga->banked_mask - 0x0001))
                        xga->a5_test = 2;

                    xga->test_stage = 1;
                    xga->on = 0;
                    xga_log("XGA test1 addr=%05x, test=%02x.\n", addr, xga->a5_test);
                } else if (val == 0x5a) {
                    xga->test = val;
                    xga->test_stage = 1;
                    xga->on = 0;
                    xga_log("XGA test2 addr = %05x.\n", addr);
                } else if ((addr == xga->mapping_base) || (addr == (xga->mapping_base + 0x0010))) {
                    addr += xga->write_bank;
                    xga->vram[addr & xga->vram_mask] = val;
                    xga_log("XGA Linear endian reverse write, val = %02x, addr = %05x, banked mask = %04x, a5test=%d.\n", val, addr, svga->banked_mask, xga->a5_test);
                }
            } else {
                xga->test_stage = 0;
                xga->on = 0;
                xga_log("Write: AP=%x, teststage=%x, on=%d.\n", xga->aperture_cntl, xga->test_stage, xga->on);
            }
        } else {
            xga->test_stage = 0;
            xga->on = 0;
            xga_log("OFF XGA write.\n");
        }
    }
}

static void
xga_write_banked(uint32_t addr, uint8_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    xga->changedvram[(xga_transform_addr(addr, xga->access_mode) & xga->vram_mask) >> 12] =
        svga->monitor->mon_changeframecount;
    xga->vram[xga_transform_addr(addr, xga->access_mode) & xga->vram_mask]                =
        xga_transform_val(val, xga->access_mode, 8);
}

static void
xga_writew_banked(uint32_t addr, uint16_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    xga->changedvram[(addr & xga->vram_mask) >> 12]   = svga->monitor->mon_changeframecount;
    *(uint16_t *) &(xga->vram[addr & xga->vram_mask]) = xga_transform_val(val, xga->access_mode, 16);
}

static void
xga_write(uint32_t addr, uint8_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    addr &= xga->banked_mask;
    addr += xga->write_bank;

    xga_log("WriteBankedB addr=%08x, val=%02x, ison=%d, mapbits=%x.\n", addr, val, xga->on, svga->gdcreg[6] & 0x0c);
    if (addr >= xga->vram_size)
        return;

    cycles -= svga->monitor->mon_video_timing_write_b;

    xga_write_banked(addr, val, svga);
}

static void
xga_writew(uint32_t addr, uint16_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    addr &= xga->banked_mask;
    addr += xga->write_bank;

    xga_log("WriteBankedW addr=%08x, val=%04x, ison=%d.\n", addr, val, xga->on);
    if (addr >= xga->vram_size)
        return;

    cycles -= svga->monitor->mon_video_timing_write_w;

    xga_writew_banked(addr, val, svga);
}

static void
xga_writel(uint32_t addr, uint32_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    addr &= xga->banked_mask;
    addr += xga->write_bank;

    xga_log("WriteBankedL addr=%08x, val=%08x.\n", addr, val);
    if (addr >= xga->vram_size)
        return;

    cycles -= svga->monitor->mon_video_timing_write_l;

    xga_writew_banked(addr, val & 0xffff, svga);
    xga_writew_banked(addr + 2, val >> 16, svga);
}

uint8_t
xga_read_test(uint32_t addr, void *priv)
{
    svga_t  *svga = (svga_t *) priv;
    xga_t   *xga = (xga_t *) svga->xga;
    uint8_t ret = 0x00;

    if (xga_active && xga) {
        xga_log("Read: OPMODE=%x, APCNTL=%x, base=%05x, test=%x.\n", xga->op_mode & 7, xga->aperture_cntl, svga->mapping.base, xga->test);
        if ((xga->op_mode & 7) == 4) {
            if (xga->aperture_cntl && (xga->test_stage == 1)) {
                if (xga->test == 0xa5) { /*Memory size test of XGA*/
                    if (addr == (xga->mapping_base + 0x0001)) {
                        xga_log("A5 test bank = %x, svgabase=%05x.\n", addr, svga->mapping.base);
                        ret = xga->test;
                    } else if ((addr == xga->mapping_base) && (xga->a5_test == 1)) { /*This is required by XGAKIT to pass the memory test*/
                        xga_log("A5 test bank = %x.\n", addr);
                        addr += xga->read_bank;
                        ret = xga->vram[addr & xga->vram_mask];
                    } else
                        ret = xga->test;

                    xga->test_stage = 0;
                    xga->on = 1;
                    xga_log("A5 read: XGA ON = %d, addr = %05x, ret = %02x, test1 = %x.\n", xga->on, addr, ret, xga->a5_test);
                    return ret;
                } else if (xga->test == 0x5a) {
                    xga->test_stage = 0;
                    ret = xga->test;
                    xga->on = 1;
                    xga_log("5A read: XGA ON = %d.\n", xga->on);
                    return ret;
                } else if ((addr == xga->mapping_base) || (addr == (xga->mapping_base + 0x0010))) {
                    addr += xga->read_bank;
                    return xga->vram[addr & xga->vram_mask];
                }
            } else {
                xga->test_stage = 0;
                xga->on = 0;
                xga_log("Read: AP=%x, teststage=%x, on=%d.\n", xga->aperture_cntl, xga->test_stage, xga->on);
            }
        } else {
            xga->test_stage = 0;
            xga->on = 0;
            xga_log("OFF XGA read.\n");
        }
    }
    return ret;
}

static uint8_t
xga_read_banked(uint32_t addr, void *priv)
{
    svga_t      *svga = (svga_t *) priv;
    xga_t *xga  = (xga_t *) svga->xga;
    uint8_t      ret  = 0xff;

    ret = xga_transform_val(xga->vram[xga_transform_addr(addr, xga->access_mode) & xga->vram_mask],
                            xga->access_mode, 8);

    return ret;
}

static uint16_t
xga_readw_banked(uint32_t addr, void *priv)
{
    svga_t      *svga = (svga_t *) priv;
    xga_t *xga  = (xga_t *) svga->xga;
    uint16_t      ret  = 0xffff;

    ret = xga_transform_val(*(uint16_t *) &(xga->vram[addr & xga->vram_mask]),
                            xga->access_mode, 16);

    return ret;
}

static uint8_t
xga_read(uint32_t addr, void *priv)
{
    svga_t      *svga = (svga_t *) priv;
    xga_t *xga  = (xga_t *) svga->xga;
    uint8_t     ret  = 0xff;

    addr &= xga->banked_mask;
    addr += xga->read_bank;

    if (addr >= xga->vram_size) {
        xga_log("Over Read ADDR=%x.\n", addr);
        return ret;
    }

    cycles -= svga->monitor->mon_video_timing_read_b;

    ret = xga_read_banked(addr, svga);
    xga_log("ReadBankedB addr=%08x, ret=%02x, ison=%d.\n", addr, ret, xga->on);
    return ret;
}

static uint16_t
xga_readw(uint32_t addr, void *priv)
{
    svga_t      *svga = (svga_t *) priv;
    xga_t *xga  = (xga_t *) svga->xga;
    uint16_t     ret  = 0xffff;

    addr &= xga->banked_mask;
    addr += xga->read_bank;

    if (addr >= xga->vram_size) {
        xga_log("Over Read ADDR=%x.\n", addr);
        return ret;
    }

    cycles -= svga->monitor->mon_video_timing_read_w;

    ret = xga_readw_banked(addr, svga);
    xga_log("ReadBankedW addr=%08x, ret=%04x, ison=%d.\n", addr, ret, xga->on);
    return ret;
}

static uint32_t
xga_readl(uint32_t addr, void *priv)
{
    svga_t      *svga = (svga_t *) priv;
    xga_t *xga  = (xga_t *) svga->xga;
    uint32_t     ret  = 0xffffffff;

    addr &= xga->banked_mask;
    addr += xga->read_bank;

    if (addr >= xga->vram_size) {
        xga_log("Over Read ADDR=%x.\n", addr);
        return ret;
    }

    cycles -= svga->monitor->mon_video_timing_read_l;

    ret = xga_readw_banked(addr, svga);
    ret |= (xga_readw_banked(addr + 2, svga) << 16);
    return ret;
}

static void
xga_write_linear(uint32_t addr, uint8_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    xga_log("WriteLL XGA=%d, addr=%08x, val=%02x.\n", xga->on, addr, val);
    if (!xga->on) {
        svga_write_linear(addr, val, svga);
        return;
    }

    addr &= xga->vram_mask;

    if (addr >= xga->vram_size) {
        xga_log("Write Linear Over!.\n");
        return;
    }

    cycles -= svga->monitor->mon_video_timing_write_b;

    xga->changedvram[(xga_transform_addr(addr, xga->access_mode) & xga->vram_mask) >> 12] =
        svga->monitor->mon_changeframecount;
    xga->vram[xga_transform_addr(addr, xga->access_mode) & xga->vram_mask]                =
        xga_transform_val(val, xga->access_mode, 8);
}

static void
xga_writew_linear(uint32_t addr, uint16_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    xga_log("WriteLL XGA=%d, addr=%08x, val=%02x.\n", xga->on, addr, val);
    if (!xga->on) {
        svga_writew_linear(addr, val, svga);
        return;
    }

    addr &= xga->vram_mask;

    if (addr >= xga->vram_size) {
        xga_log("Write Linear Over!.\n");
        return;
    }

    cycles -= svga->monitor->mon_video_timing_write_b;

    xga->changedvram[(addr & xga->vram_mask) >> 12]   = svga->monitor->mon_changeframecount;
    *(uint16_t *) &(xga->vram[addr & xga->vram_mask]) = xga_transform_val(val, xga->access_mode, 16);
}

static void
xga_writel_linear(uint32_t addr, uint32_t val, void *priv)
{
    svga_t      *svga = (svga_t *) priv;
    const xga_t *xga  = (xga_t *) svga->xga;

    if (!xga->on) {
        svga_writel_linear(addr, val, svga);
        return;
    }

    xga_writew_linear(addr, val, priv);
    xga_writew_linear(addr + 2, val >> 16, priv);
}

static uint8_t
xga_read_linear(uint32_t addr, void *priv)
{
    svga_t      *svga = (svga_t *) priv;
    const xga_t *xga  = (xga_t *) svga->xga;
    uint8_t      ret  = 0xff;

    if (xga->on) {
        addr &= xga->vram_mask;

        if (addr < xga->vram_size) {
            cycles -= svga->monitor->mon_video_timing_read_b;

            ret = xga_transform_val(xga->vram[xga_transform_addr(addr, xga->access_mode) & xga->vram_mask],
                                    xga->access_mode, 8);
        }
    } else
        ret = svga_read_linear(addr, svga);

    return ret;
}

static uint16_t
xga_readw_linear(uint32_t addr, void *priv)
{
    svga_t      *svga = (svga_t *) priv;
    const xga_t *xga  = (xga_t *) svga->xga;
    uint16_t     ret  = 0xffff;

    if (xga->on) {
        addr &= xga->vram_mask;

        if (addr < xga->vram_size) {
            cycles -= svga->monitor->mon_video_timing_read_b;

            ret = xga_transform_val(*(uint16_t *) &(xga->vram[addr & xga->vram_mask]),
                                    xga->access_mode, 16);
        }
    } else
        ret = svga_readw_linear(addr, svga);

    return ret;
}

static uint32_t
xga_readl_linear(uint32_t addr, void *priv)
{
    svga_t      *svga = (svga_t *) priv;
    const xga_t *xga  = (xga_t *) svga->xga;
    uint32_t     ret;

    if (!xga->on)
        return svga_readl_linear(addr, svga);

    ret = xga_readw_linear(addr, svga);
    ret |= (xga_readw_linear(addr + 2, svga) << 16);

    return ret;
}

void
xga_set_poll(svga_t *svga)
{
    timer_set_callback(&svga->timer, xga_poll);
}

void
xga_poll(void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t *xga   = (xga_t *) svga->xga;
    uint32_t x;
    int      wx;
    int      wy;

    xga_log("XGA Poll=%d.\n", xga->on);
    if (xga->on) {
        if (svga->override)
            svga_set_poll(svga);
        else {
            if (!xga->linepos) {
                if (xga->displine == xga->hwcursor_latch.y && xga->hwcursor_latch.ena) {
                    xga->hwcursor_on      = xga->hwcursor_latch.cur_ysize - ((xga->hwcursor_latch.yoff & 0x20) ? 32 : 0);
                    xga->hwcursor_oddeven = 0;
                }

                if (xga->displine == (xga->hwcursor_latch.y + 1) && xga->hwcursor_latch.ena && xga->interlace) {
                    xga->hwcursor_on      = xga->hwcursor_latch.cur_ysize - ((xga->hwcursor_latch.yoff & 0x20) ? 33 : 1);
                    xga->hwcursor_oddeven = 1;
                }

                timer_advance_u64(&svga->timer, xga->dispofftime);
                svga->cgastat |= 1;
                xga->linepos = 1;

                if (xga->dispon) {
                    xga->h_disp_on = 1;

                    xga->memaddr &= xga->vram_mask;

                    if (xga->firstline == 2000) {
                        xga->firstline = xga->displine;
                        video_wait_for_buffer_monitor(svga->monitor_index);
                    }

                    if (xga->hwcursor_on)
                        xga->changedvram[xga->memaddr >> 12] = xga->changedvram[(xga->memaddr >> 12) + 1] = xga->interlace ? 3 : 2;

                    svga->render_xga(svga);

                    svga->x_add = svga->left_overscan;
                    xga_render_overscan_left(xga, svga);
                    xga_render_overscan_right(xga, svga);
                    svga->x_add = svga->left_overscan;

                    if (xga->hwcursor_on) {
                        xga_hwcursor_draw(svga, xga->displine + svga->y_add);
                        xga->hwcursor_on--;
                        if (xga->hwcursor_on && xga->interlace)
                            xga->hwcursor_on--;
                    }

                    if (xga->lastline < xga->displine)
                        xga->lastline = xga->displine;
                }

                xga->displine++;
                if (xga->interlace)
                    xga->displine++;
                if ((svga->cgastat & 8) && ((xga->displine & 0x0f) == (svga->crtc[0x11] & 0x0f)) && svga->vslines)
                    svga->cgastat &= ~8;
                if (xga->displine > 1500)
                    xga->displine = 0;
            } else {
                timer_advance_u64(&svga->timer, xga->dispontime);
                if (xga->dispon)
                    svga->cgastat &= ~1;

                xga->h_disp_on = 0;

                xga->linepos = 0;
                if (xga->dispon) {
                    if (xga->scanline == xga->rowcount) {
                        xga->scanline = 0;

                        xga_log("MA=%08x, MALATCH=%x.\n", xga->memaddr, xga->memaddr_latch);
                        xga->memaddr_backup += (xga->rowoffset << 3);
                        if (xga->interlace)
                            xga->memaddr_backup += (xga->rowoffset << 3);

                        xga->memaddr_backup &= xga->vram_mask;
                        xga->memaddr = xga->memaddr_backup;
                    } else {
                        xga->scanline++;
                        xga->scanline &= 0x1f;
                        xga->memaddr = xga->memaddr_backup;
                    }
                }

                xga->vc++;
                xga->vc &= 0x7ff;

                if (xga->vc == xga->split) {
                    if (xga->interlace && xga->oddeven)
                        xga->memaddr = xga->memaddr_backup = (xga->rowoffset << 1);
                    else
                        xga->memaddr = xga->memaddr_backup = 0;

                    xga->memaddr     = (xga->memaddr << 2);
                    xga->memaddr_backup = (xga->memaddr_backup << 2);

                    xga->scanline = 0;
                }
                if (xga->vc == xga->dispend) {
                    xga->dispon = 0;

                    for (x = 0; x < ((xga->vram_mask + 1) >> 12); x++) {
                        if (xga->changedvram[x])
                            xga->changedvram[x]--;
                    }
                    if (svga->fullchange)
                        svga->fullchange--;
                }
                if (xga->vc == xga->v_syncstart) {
                    xga->dispon = 0;
                    svga->cgastat |= 8;
                    x           = xga->h_disp;

                    if (xga->interlace && !xga->oddeven)
                        xga->lastline++;
                    if (xga->interlace && xga->oddeven)
                        xga->firstline--;

                    wx = x;

                    wy = xga->lastline - xga->firstline;
                    svga_doblit(wx, wy, svga);

                    xga->firstline = 2000;
                    xga->lastline  = 0;

                    xga->firstline_draw = 2000;
                    xga->lastline_draw  = 0;

                    xga->oddeven ^= 1;

                    svga->monitor->mon_changeframecount = xga->interlace ? 3 : 2;

                    if (xga->interlace && xga->oddeven)
                        xga->memaddr = xga->memaddr_backup = xga->memaddr_latch + (xga->rowoffset << 1);
                    else
                        xga->memaddr = xga->memaddr_backup = xga->memaddr_latch;

                    xga->memaddr     = (xga->memaddr << 2);
                    xga->memaddr_backup = (xga->memaddr_backup << 2);
                }
                if (xga->vc == xga->v_total) {
                    xga->vc       = 0;
                    xga->scanline = 0;
                    xga->dispon   = 1;
                    xga->displine = (xga->interlace && xga->oddeven) ? 1 : 0;

                    svga->x_add = svga->left_overscan;

                    xga->hwcursor_on    = 0;
                    xga->hwcursor_latch = xga->hwcursor;
                }
            }
        }
    } else
        svga_recalctimings(svga);
}

static uint8_t
xga_mca_read(int port, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;
    uint8_t       ret  = xga->pos_regs[port & 7];

    xga_log("[%04X:%08X]: POS Read Port = %x, val = %02x\n", CS, cpu_state.pc,
            port & 7, xga->pos_regs[port & 7]);

    return ret;
}

static void
xga_mca_write(int port, uint8_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    /* MCA does not write registers below 0x0100. */
    if (port < 0x0102)
        return;

    io_removehandler(0x2100 + (xga->instance << 4), 0x0010, xga_ext_inb, NULL, NULL, xga_ext_outb, NULL, NULL, svga);
    mem_mapping_disable(&xga->memio_mapping);
    xga->on                    = 0;
    xga->a5_test               = 0;
    xga->test_stage            = 0;

    /* Save the MCA register value. */
    xga->pos_regs[port & 7] = val;

    if (xga->pos_regs[2] & 1) {
        xga->instance      = (xga->pos_regs[2] & 0x0e) >> 1;
        xga->base_addr_1mb = (xga->pos_regs[5] & 0x0f) << 20;
        xga->linear_base   = ((xga->pos_regs[4] & 0xfe) * 0x1000000) + (xga->instance << 22);
        xga->rom_addr      = 0xc0000 + (((xga->pos_regs[2] & 0xf0) >> 4) * 0x2000);
        if (!xga->base_addr_1mb)
            xga->pos_regs[4] |= 1; /*If 1MB VRAM aperture is disabled on MCA, enable the 4MB VRAM aperture instead.*/

        io_sethandler(0x2100 + (xga->instance << 4), 0x0010, xga_ext_inb, NULL, NULL, xga_ext_outb, NULL, NULL, svga);

        if (xga->rom_addr) {
            mem_mapping_set_addr(&xga->memio_mapping, xga->rom_addr, 0x2000);
            xga_log("ROM address=%05x.\n", xga->rom_addr);
        }
    }

    xga_log("[%04X:%08X]: POS Write Port = %x, val = %02x, linear base = %08x, instance = %d, "
            "rom addr = %05x\n", CS, cpu_state.pc, port & 7, val, xga->linear_base,
            xga->instance, xga->rom_addr);
}

static uint8_t
xga_mca_feedb(void *priv)
{
    const svga_t *svga = (svga_t *) priv;
    const xga_t  *xga  = (xga_t *) svga->xga;

    xga_log("FeedB.\n");
    return xga->pos_regs[2] & 1;
}

static void
xga_mca_reset(void *priv)
{
    svga_t *svga = (svga_t *) priv;

    xga_log("MCA Reset.\n");
    mem_mapping_set_handler(&svga->mapping, svga->read, svga->readw, svga->readl, svga->write, svga->writew, svga->writel);
    xga_mca_write(0x102, 0, svga);
}

static void
xga_reset(void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    xga_log("Normal Reset.\n");
    mem_mapping_disable(&xga->memio_mapping);

    xga->on                    = 0;
    xga->a5_test               = 0;
    xga->test_stage            = 0;
    mem_mapping_set_handler(&svga->mapping, svga->read, svga->readw, svga->readl, svga->write, svga->writew, svga->writel);
    svga_set_poll(svga);
}

static uint8_t
xga_pos_in(uint16_t addr, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;
    uint8_t ret  = 0x00;

    if (!xga_standalone_enabled) {
        switch (addr) {
            case 0x0100:
            case 0x0101:
                if (xga->instance == xga->instance_num)
                    ret = xga->pos_regs[addr & 7];
                else
                    ret = 0xff;

                xga_log("%03xRead=%02x.\n", addr, ret);
                break;
            case 0x0102:
                ret = xga->pos_regs[2] | 0x30;
                break;
            case 0x0105:
                ret = xga->pos_regs[5];
                xga_log("POS IDX Read 010%x ret = %02x.\n", addr & 7, ret);
                break;
            case 0x0103:
                if ((xga->pos_idx & 3) == 0) {
                    ret = xga->pos_regs[3];
                    ret |= (xga->dma_channel << 3);
                }
                xga_log("POS IDX for 0103 = %d, ret = %02x.\n", xga->pos_idx & 3, ret);
                break;
            case 0x0104:
                switch (xga->pos_idx & 3) {
                    case 0:
                        ret = xga->pos_regs[4];
                        break;
                    case 1:
                        ret = xga->pos_regs[0];
                        break;
                    case 2:
                        ret = xga->pos_regs[1];
                        break;

                    default:
                        break;
                }
                xga_log("POS IDX for 0104 = %d, ret = %02x.\n", xga->pos_idx & 3, ret);
                break;
            case 0x0106:
                ret = xga->pos_idx >> 8;
                break;
            case 0x0107:
                ret = xga->pos_idx & 0xff;
                break;
            case 0x0108 ... 0x010f:
                xga->instance_num = addr & 0x07;
                if (xga->instance == xga->instance_num)
                    ret = xga->instance;

                ret |= xga->isa_pos_enable;
                xga_log("%03xRead=%02x.\n", addr, ret);
                break;

            default:
                break;
        }
    } else {
        switch (addr) {
            case 0x0096:
                ret = xga->vga_post;
                break;
            case 0x0100:
            case 0x0101:
                ret = xga->pos_regs[addr & 7];
                break;
            case 0x0103:
                ret = xga->pos_regs[3] | 0x06;
                ret |= (xga->dma_channel << 3);
                break;
            case 0x0102:
            case 0x0104:
            case 0x0105:
            case 0x0106:
            case 0x0107:
                ret = (xga_mca_read(addr, svga));
                break;

            default:
                break;
        }
    }
    xga_log("[%04X:%08X]: XGA POS IN addr=%04x, ret=%02x.\n", CS, cpu_state.pc, addr, ret);
    return ret;
}

static void
xga_pos_out(uint16_t addr, uint8_t val, void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    xga_log("[%04X:%08X]: XGA POS OUT addr=%04x, val=%02x.\n", CS, cpu_state.pc, addr, val);
    if (!xga_standalone_enabled) {
        switch (addr) {
            case 0x0096:
                xga->instance_num = val & 0x07;
                xga->isa_pos_enable = val & 0x08;
                xga_log("096Write=%02x.\n", val);
                break;
            case 0x0102:
                xga_log("[%04X:%08X]: 102Write=%02x.\n", CS, cpu_state.pc, val);
                xga->pos_regs[2] = val | 0x02; /*Instance 0 is not recommended on AT bus/ISA bus systems, so force it to use instance 1.*/
                io_removehandler(0x2100 + (xga->instance << 4), 0x0010, xga_ext_inb, NULL, NULL, xga_ext_outb, NULL, NULL, svga);
                mem_mapping_disable(&xga->memio_mapping);
                if (xga->pos_regs[2] & 0x01) {
                    xga->rom_addr    = 0xc0000 + (((xga->pos_regs[2] & 0xc0) >> 6) * 0x8000);
                    xga->instance    = (xga->pos_regs[2] & 0x0e) >> 1;
                    xga->linear_base = ((xga->pos_regs[4] & 0xfe) * 0x1000000) + (xga->instance << 22);
                    xga->base_addr_1mb = (xga->pos_regs[5] & 0x0f) << 20;
                    io_sethandler(0x2100 + (xga->instance << 4), 0x0010, xga_ext_inb, NULL, NULL, xga_ext_outb, NULL, NULL, svga);
                    xga_log("XGA ISA ROM address=%05x, instance=%d.\n", xga->rom_addr, xga->instance);
                    mem_mapping_set_addr(&xga->memio_mapping, xga->rom_addr, 0x8000);
                }
                break;
            case 0x0103:
                if ((xga->pos_idx & 3) == 0)
                    xga->pos_regs[3] = val;

                xga_log("[%04X:%08X]: 103Write=%02x.\n", CS, cpu_state.pc, val);
                break;
            case 0x0104:
                xga_log("104Write=%02x.\n", val);
                if ((xga->pos_idx & 3) == 0)
                    xga->pos_regs[4] = val;
                break;
            case 0x0105:
                xga_log("105Write=%02x.\n", val);
                xga->pos_regs[5] = val;
                break;
            case 0x0106:
                xga->pos_idx = (xga->pos_idx & 0x00ff) | (val << 8);
                break;
            case 0x0107:
                xga->pos_idx = (xga->pos_idx & 0xff00) | val;
                xga_log("POS IDX Write = %04x.\n", xga->pos_idx);
                break;
            case 0x0108 ... 0x010f:
                xga_log("%03xWrite=%02x.\n", addr, val);
                xga->instance_num = addr & 0x07;
                xga->isa_pos_enable = val & 0x08;
                break;

            default:
                break;
        }
    } else {
        xga_log("XGA Standalone ISA Write Port=%04x, Val=%02x.\n", addr, val);
        switch (addr) {
            case 0x0096:
                xga->vga_post = val;
                break;

            default:
                break;
        }
    }
}

static void *
xga_init(const device_t *info)
{
    if (svga_get_pri() == NULL)
        return NULL;

    svga_t  *svga     = svga_get_pri();
    xga_t   *xga      = (xga_t *) calloc(1, sizeof(xga_t));

    svga->xga         = xga;

    xga->type         = device_get_config_int("type");
    xga->dma_channel  = device_get_config_int("dma");
    xga->bus          = info->flags;

    xga->vram_size             = 1024 << 10;
    xga->vram_mask             = xga->vram_size - 1;
    xga->vram                  = calloc(xga->vram_size, 1);
    xga->changedvram           = calloc((xga->vram_size >> 12) + 1, 1);
    xga->on                    = 0;
    xga->hwcursor.cur_xsize    = 64;
    xga->hwcursor.cur_ysize    = 64;
    xga->a5_test               = 0;
    xga->test_stage            = 0;

    if (info->flags & DEVICE_MCA) {
        video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_xga_mca);
        xga->base_addr_1mb = 0;
        xga->linear_base = 0;
        xga->instance    = 0;
        xga->rom_addr    = 0;
        rom_init(&xga->bios_rom, xga->type ? XGA2_BIOS_PATH : XGA_BIOS_PATH, 0xc0000, 0x2000, 0x1fff, 0, MEM_MAPPING_EXTERNAL);
        mem_mapping_disable(&xga->bios_rom.mapping);
        mem_mapping_add(&xga->memio_mapping, 0, 0, xga_memio_readb, xga_memio_readw, xga_memio_readl,
                        xga_memio_writeb, xga_memio_writew, xga_memio_writel,
                        xga->bios_rom.rom, MEM_MAPPING_EXTERNAL, svga);
    } else {
        xga->pos_regs[4] = 0x02;
        if (!xga_standalone_enabled) {
            rom_init(&xga->bios_rom, INMOS_XGA_BIOS_PATH, 0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); /*VGA BIOS only*/
            mem_mapping_add(&xga->memio_mapping, 0, 0, xga_memio_readb, xga_memio_readw, xga_memio_readl,
                            xga_memio_writeb, xga_memio_writew, xga_memio_writel,
                            xga->bios_rom.rom, MEM_MAPPING_EXTERNAL, svga);
        }
    }

    mem_mapping_add(&xga->linear_mapping, 0, 0, xga_read_linear, xga_readw_linear, xga_readl_linear,
                    xga_write_linear, xga_writew_linear, xga_writel_linear,
                    NULL, MEM_MAPPING_EXTERNAL, svga);

    mem_mapping_disable(&xga->linear_mapping);
    mem_mapping_disable(&xga->memio_mapping);

    xga->pos_regs[0] = xga->type ? 0xda : 0xdb;
    xga->pos_regs[1] = 0x8f;

    if (xga->bus & DEVICE_MCA) {
        mca_add(xga_mca_read, xga_mca_write, xga_mca_feedb, xga_mca_reset, svga);
    } else {
        io_sethandler(0x0096, 0x0001, xga_pos_in, NULL, NULL, xga_pos_out, NULL, NULL, svga);
        io_sethandler(0x0100, 0x0010, xga_pos_in, NULL, NULL, xga_pos_out, NULL, NULL, svga);
    }
    return svga;
}

static void *
svga_xga_init(const device_t *info)
{
    svga_t *svga = (svga_t *) calloc(1, sizeof(svga_t));

    video_inform(VIDEO_FLAG_TYPE_XGA, &timing_xga_isa);

    svga_init(info, svga, svga, 1 << 18, /*256kB*/
              NULL,
              svga_xga_in, svga_xga_out,
              NULL,
              NULL);

    io_sethandler(0x03c0, 0x0020, svga_xga_in, NULL, NULL, svga_xga_out, NULL, NULL, svga);

    svga->bpp     = 8;
    svga->miscout = 1;
    xga_active    = 1;

    return xga_init(info);
}

static void
xga_close(void *priv)
{
    svga_t *svga = (svga_t *) priv;
    xga_t  *xga  = (xga_t *) svga->xga;

    if (svga) {
        free(xga->vram);
        free(xga->changedvram);

        free(xga);
    }
}

static int
xga_available(void)
{
    return rom_present(XGA_BIOS_PATH) && rom_present(XGA2_BIOS_PATH);
}

static int
inmos_xga_available(void)
{
    return rom_present(INMOS_XGA_BIOS_PATH);
}

static void
xga_speed_changed(void *priv)
{
    svga_t *svga = (svga_t *) priv;

    svga_recalctimings(svga);
}

static void
xga_force_redraw(void *priv)
{
    svga_t *svga = (svga_t *) priv;

    svga->fullchange = svga->monitor->mon_changeframecount;
}

static const device_config_t xga_mca_configuration[] = {
  // clang-format off
    {
        .name           = "type",
        .description    = "XGA type",
        .type           = CONFIG_SELECTION,
        .default_string = NULL,
        .default_int    = 0,
        .file_filter    = NULL,
        .spinner        = { 0 },
        .selection      = {
            { .description = "XGA-1", .value = 0 },
            { .description = "XGA-2", .value = 1 },
            { .description = ""                  }
        },
        .bios           = { { 0 } }
    },
    { .name = "", .description = "", .type = CONFIG_END }
  // clang-format on
};

static const device_config_t xga_inmos_isa_configuration[] = {
  // clang-format off
    {
        .name           = "type",
        .description    = "XGA type",
        .type           = CONFIG_SELECTION,
        .default_string = NULL,
        .default_int    = 0,
        .file_filter    = NULL,
        .spinner        = { 0 },
        .selection      = {
            { .description = "XGA-1", .value = 0 },
            { .description = "XGA-2", .value = 1 },
            { .description = ""                  }
        },
        .bios           = { { 0 } }
    },
    {
        .name           = "dma",
        .description    = "DMA",
        .type           = CONFIG_SELECTION,
        .default_string = NULL,
        .default_int    = 7,
        .file_filter    = NULL,
        .spinner        = { 0 },
        .selection      = {
            { .description = "Disabled", .value = 0 },
            { .description = "DMA 6",    .value = 6 },
            { .description = "DMA 7",    .value = 7 },
            { .description = ""                     }
        },
        .bios           = { { 0 } }
    },
    { .name = "", .description = "", .type = CONFIG_END }
  // clang-format on
};

const device_t xga_device = {
    .name          = "XGA (MCA)",
    .internal_name = "xga_mca",
    .flags         = DEVICE_MCA,
    .local         = 0,
    .init          = xga_init,
    .close         = xga_close,
    .reset         = xga_reset,
    .available     = xga_available,
    .speed_changed = xga_speed_changed,
    .force_redraw  = xga_force_redraw,
    .config        = xga_mca_configuration
};

const device_t inmos_isa_device = {
    .name          = "INMOS XGA (ISA)",
    .internal_name = "inmos_xga_isa",
    .flags         = DEVICE_ISA16,
    .local         = 0,
    .init          = svga_xga_init,
    .close         = xga_close,
    .reset         = xga_reset,
    .available     = inmos_xga_available,
    .speed_changed = xga_speed_changed,
    .force_redraw  = xga_force_redraw,
    .config        = xga_inmos_isa_configuration
};

void
xga_device_add(void)
{
    if (!xga_standalone_enabled)
        return;

    if (machine_has_bus(machine, MACHINE_BUS_MCA))
        device_add(&xga_device);
}
