#include <stdlib.h>
#include "ibm.h"
#include "device.h"
#include "io.h"
#include "mem.h"
#include "rom.h"
#include "video.h"
#include "vid_ht216.h"
#include "vid_svga.h"
#include "vid_svga_render.h"
#include "vid_unk_ramdac.h"

typedef struct ht216_t {
        svga_t svga;

        mem_mapping_t linear_mapping;

        rom_t bios_rom;

        uint32_t vram_mask;

        int ext_reg_enable;
        int clk_sel;

        uint8_t read_bank_reg[2], write_bank_reg[2];
        uint32_t read_bank[2], write_bank[2];
        uint8_t misc;

        uint8_t bg_latch[8];

        uint8_t ht_regs[256];
} ht216_t;

#define HT_MISC_PAGE_SEL (1 << 5)

/*Shifts CPU VRAM read address by 3 bits, for use with fat pixel colour expansion*/
#define HT_REG_C8_MOVSB (1 << 0)
#define HT_REG_C8_E256 (1 << 4)
#define HT_REG_C8_XLAM (1 << 6)

#define HT_REG_CD_FP8PCEXP (1 << 1)
#define HT_REG_CD_BMSKSL (3 << 2)
#define HT_REG_CD_RMWMDE (1 << 5)
/*Use GDC data rotate as offset when reading VRAM data into latches*/
#define HT_REG_CD_ASTODE (1 << 6)
#define HT_REG_CD_EXALU (1 << 7)

#define HT_REG_E0_SBAE (1 << 7)

#define HT_REG_F9_XPSEL (1 << 0)

/*Enables A[14:15] of VRAM address in chain-4 modes*/
#define HT_REG_FC_ECOLRE (1 << 2)

#define HT_REG_FE_FBRC (1 << 1)
#define HT_REG_FE_FBMC (3 << 2)
#define HT_REG_FE_FBRSL (3 << 4)

void ht216_remap(ht216_t *ht216);

void ht216_out(uint16_t addr, uint8_t val, void *p);
uint8_t ht216_in(uint16_t addr, void *p);

void ht216_out(uint16_t addr, uint8_t val, void *p) {
        ht216_t *ht216 = (ht216_t *)p;
        svga_t *svga = &ht216->svga;
        uint8_t old;

        //        pclog("ht216 %i out %04X %02X %04X:%04X\n", svga->miscout & 1, addr, val, CS, cpu_state.pc);

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

        switch (addr) {
        case 0x3c2:
                ht216->clk_sel = (ht216->clk_sel & ~3) | ((val & 0x0c) >> 2);
                ht216->misc = val;
                ht216->read_bank_reg[0] = (ht216->read_bank_reg[0] & ~0x20) | ((val & HT_MISC_PAGE_SEL) ? 0x20 : 0);
                ht216->write_bank_reg[0] = (ht216->write_bank_reg[0] & ~0x20) | ((val & HT_MISC_PAGE_SEL) ? 0x20 : 0);
                ht216_remap(ht216);
                svga_recalctimings(&ht216->svga);
                break;

        case 0x3c5:
                if (svga->seqaddr == 4) {
                        svga->chain4 = val & 8;
                        ht216_remap(ht216);
                } else if (svga->seqaddr == 6) {
                        if (val == 0xea)
                                ht216->ext_reg_enable = 1;
                        else if (val == 0xae)
                                ht216->ext_reg_enable = 0;
                } else if (svga->seqaddr >= 0x80 && ht216->ext_reg_enable) {
                        ht216->ht_regs[svga->seqaddr & 0xff] = val;
                        //                        pclog("HT[%02x]=%02x %04x(%08x):%08x\n", svga->seqaddr, val,
                        //                        CS,cs,cpu_state.pc);
                        switch (svga->seqaddr & 0xff) {
                        case 0x83:
                                svga->attraddr = val & 0x1f;
                                svga->attrff = (val & 0x80) ? 1 : 0;
                                break;

                        case 0x94:
                                svga->hwcursor.addr = ((val << 6) | (3 << 14) | ((ht216->ht_regs[0xff] & 0x60) << 11)) << 2;
                                // pclog("cursor_addr = %05x\n", svga->hwcursor.addr);
                                break;
                        case 0x9c:
                        case 0x9d:
                                svga->hwcursor.x = ht216->ht_regs[0x9d] | ((ht216->ht_regs[0x9c] & 7) << 8);
                                break;
                        case 0x9e:
                        case 0x9f:
                                svga->hwcursor.y = ht216->ht_regs[0x9f] | ((ht216->ht_regs[0x9e] & 3) << 8);
                                break;
                        case 0xa0:
                                svga->la = val;
                                break;
                        case 0xa1:
                                svga->lb = val;
                                break;
                        case 0xa2:
                                svga->lc = val;
                                break;
                        case 0xa3:
                                svga->ld = val;
                                break;
                        case 0xa4:
                                ht216->clk_sel = (val >> 2) & 0xf;
                                svga->miscout = (svga->miscout & ~0xc) | ((ht216->clk_sel & 3) << 2);
                                break;
                        case 0xa5:
                                svga->hwcursor.ena = val & 0x80;
                                break;
                        case 0xe8:
                                ht216->read_bank_reg[0] = val;
                                ht216->write_bank_reg[0] = val;
                                break;
                        case 0xe9:
                                ht216->read_bank_reg[1] = val;
                                ht216->write_bank_reg[1] = val;
                                break;
                        case 0xf6:
                                svga->vram_display_mask = (val & 0x40) ? ht216->vram_mask : 0x3ffff;
                                ht216->read_bank_reg[0] = (ht216->read_bank_reg[0] & ~0xc0) | ((val & 0xc) << 4);
                                ht216->write_bank_reg[0] = (ht216->write_bank_reg[0] & ~0xc0) | ((val & 0x3) << 6);
                                break;
                        case 0xf9:
                                ht216->read_bank_reg[0] = (ht216->read_bank_reg[0] & ~0x10) | ((val & 1) ? 0x10 : 0);
                                ht216->write_bank_reg[0] = (ht216->write_bank_reg[0] & ~0x10) | ((val & 1) ? 0x10 : 0);
                                break;
                        case 0xfc:
                                svga->packed_chain4 = val & 0x20;
                                break;
                        case 0xff:
                                svga->hwcursor.addr = ((ht216->ht_regs[0x94] << 6) | (3 << 14) | ((val & 0x60) << 11)) << 2;
                                // pclog("cursor_addr = %05x\n", svga->hwcursor.addr);
                                break;
                        }
                        switch (svga->seqaddr & 0xff) {
                        case 0xa4:
                        case 0xf6:
                        case 0xfc:
                                svga->fullchange = changeframecount;
                                svga_recalctimings(&ht216->svga);
                                break;
                        }
                        switch (svga->seqaddr & 0xff) {
                        case 0xc8:
                        case 0xc9:
                        case 0xcf:
                        case 0xe0:
                        case 0xe8:
                        case 0xe9:
                        case 0xf6:
                        case 0xf9:
                                ht216_remap(ht216);
                                break;
                        }
                        return;
                }
                break;

        case 0x3cf:
                if (svga->gdcaddr == 6) {
                        if (val & 8)
                                svga->banked_mask = 0x7fff;
                        else
                                svga->banked_mask = 0xffff;
                }
                break;

        case 0x3D4:
                svga->crtcreg = val & 0x3f;
                return;
        case 0x3D5:
                if ((svga->crtcreg < 7) && (svga->crtc[0x11] & 0x80))
                        return;
                if ((svga->crtcreg == 7) && (svga->crtc[0x11] & 0x80))
                        val = (svga->crtc[7] & ~0x10) | (val & 0x10);

                //                if (svga->crtcreg == 0xc)
                //                        pclog("CRTC[C]=%02x     %08x\n", val, svga->vram_display_mask);
                old = svga->crtc[svga->crtcreg];
                svga->crtc[svga->crtcreg] = val;

                if (old != val) {
                        if (svga->crtcreg < 0xe || svga->crtcreg > 0x10) {
                                svga->fullchange = changeframecount;
                                svga_recalctimings(&ht216->svga);
                        }
                }
                break;

        case 0x46e8:
                io_removehandler(0x03c0, 0x0020, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216);
                mem_mapping_disable(&ht216->svga.mapping);
                mem_mapping_disable(&ht216->linear_mapping);
                if (val & 8) {
                        io_sethandler(0x03c0, 0x0020, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216);
                        mem_mapping_enable(&ht216->svga.mapping);
                        ht216_remap(ht216);
                }
                break;
        }
        svga_out(addr, val, svga);
}

uint8_t ht216_in(uint16_t addr, void *p) {
        ht216_t *ht216 = (ht216_t *)p;
        svga_t *svga = &ht216->svga;

        //        if (addr != 0x3ba && addr != 0x3da) pclog("ht216 %i in %04X\n", svga->miscout & 1, addr);

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

        switch (addr) {
        case 0x3c2:
                //                pclog("3C2 read\n");
                break;
                //                return 0x10;

        case 0x3c5:
                if (svga->seqaddr == 6)
                        return ht216->ext_reg_enable;
                if (svga->seqaddr >= 0x80) {
                        if (ht216->ext_reg_enable) {
                                //                                pclog("Read HT[%02x]=%02x\n", svga->seqaddr & 0xff,
                                //                                ht216->ht_regs[svga->seqaddr & 0xff]);
                                switch (svga->seqaddr & 0xff) {
                                case 0x83:
                                        if (svga->attrff)
                                                return svga->attraddr | 0x80;
                                        return svga->attraddr;

                                case 0x8e:
                                        return 0x61; /*HT216-32*/
                                case 0x8f:
                                        return 0x78;

                                case 0xa0:
                                        return svga->la;
                                case 0xa1:
                                        return svga->lb;
                                case 0xa2:
                                        return svga->lc;
                                case 0xa3:
                                        return svga->ld;
                                }
                                return ht216->ht_regs[svga->seqaddr & 0xff];
                        } else
                                return 0xff;
                }
                break;

        case 0x3D4:
                return svga->crtcreg;
        case 0x3D5:
                if (svga->crtcreg == 0x1f)
                        return svga->crtc[0xc] ^ 0xea;
                return svga->crtc[svga->crtcreg];
        }
        return svga_in(addr, svga);
}

void ht216_remap(ht216_t *ht216) {
        svga_t *svga = &ht216->svga;

        mem_mapping_disable(&ht216->linear_mapping);
        if (ht216->ht_regs[0xc8] & HT_REG_C8_XLAM) {
                uint32_t linear_base = ((ht216->ht_regs[0xc9] & 0xf) << 20) | (ht216->ht_regs[0xcf] << 24);

                pclog("Linear base %08x\n", linear_base);
                mem_mapping_set_addr(&ht216->linear_mapping, linear_base, 0x100000);

                /*Linear mapping enabled*/
        } else {
                uint8_t read_bank_reg[2] = {ht216->read_bank_reg[0], ht216->read_bank_reg[1]};
                uint8_t write_bank_reg[2] = {ht216->write_bank_reg[0], ht216->write_bank_reg[1]};

                if (!svga->chain4 || !(ht216->ht_regs[0xfc] & HT_REG_FC_ECOLRE)) {
                        read_bank_reg[0] &= ~0x30;
                        read_bank_reg[1] &= ~0x30;
                        write_bank_reg[0] &= ~0x30;
                        write_bank_reg[1] &= ~0x30;
                }

                ht216->read_bank[0] = read_bank_reg[0] << 12;
                ht216->write_bank[0] = write_bank_reg[0] << 12;
                if (ht216->ht_regs[0xe0] & HT_REG_E0_SBAE) {
                        /*Split bank*/
                        ht216->read_bank[1] = read_bank_reg[1] << 12;
                        ht216->write_bank[1] = write_bank_reg[1] << 12;
                } else {
                        ht216->read_bank[1] = ht216->read_bank[0] + (svga->chain4 ? 0x8000 : 0x20000);
                        ht216->write_bank[1] = ht216->write_bank[0] + (svga->chain4 ? 0x8000 : 0x20000);
                }

                if (!svga->chain4) {
                        ht216->read_bank[0] >>= 2;
                        ht216->read_bank[1] >>= 2;
                        ht216->write_bank[0] >>= 2;
                        ht216->write_bank[1] >>= 2;
                }
        }
        //        pclog("Remap - chain4=%i [e0]=%02x [e8]=%02x [e9]=%02x [f6]=%02x [fc]=%02x [f9]=%02x misc=%02x gdc[6]=%02x
        //        read[0]=%05X write[0]=%05X read[1]=%05X write[1]=%05X\n", svga->chain4, ht216->ht_regs[0xe0],
        //        ht216->ht_regs[0xe8], ht216->ht_regs[0xe9], ht216->ht_regs[0xf6], ht216->ht_regs[0xfc], ht216->ht_regs[0xf9],
        //        ht216->misc, svga->gdcreg[6], ht216->read_bank[0], ht216->write_bank[0], ht216->read_bank[1],
        //        ht216->write_bank[1]);
}

void ht216_recalctimings(svga_t *svga) {
        ht216_t *ht216 = (ht216_t *)svga->p;

        //        pclog("clk_sel = %i\n", ht216->clk_sel);
        switch (ht216->clk_sel) {
        case 5:
                svga->clock = (cpuclock * (double)(1ull << 32)) / 65000000.0;
                break;
        case 6:
                svga->clock = (cpuclock * (double)(1ull << 32)) / 40000000.0;
                break;
        case 10:
                svga->clock = (cpuclock * (double)(1ull << 32)) / 80000000.0;
                break;
        }
        svga->lowres = !(ht216->ht_regs[0xc8] & HT_REG_C8_E256);
        svga->ma_latch |= ((ht216->ht_regs[0xf6] & 0x30) << 12);
        svga->interlace = ht216->ht_regs[0xe0] & 1;

        if (svga->bpp == 8 && !svga->lowres)
                svga->render = svga_render_8bpp_highres;
}

static void ht216_hwcursor_draw(svga_t *svga, int displine) {
        //        ht216_t *ht216 = (ht216_t *)svga->p;
        int x;
        uint32_t dat[2];
        int offset = svga->hwcursor_latch.x + 32;

        if (svga->interlace && svga->hwcursor_oddeven)
                svga->hwcursor_latch.addr += 4;

        dat[0] = (svga->vram[svga->hwcursor_latch.addr] << 24) | (svga->vram[svga->hwcursor_latch.addr + 1] << 16) |
                 (svga->vram[svga->hwcursor_latch.addr + 2] << 8) | svga->vram[svga->hwcursor_latch.addr + 3];
        dat[1] = (svga->vram[svga->hwcursor_latch.addr + 128] << 24) | (svga->vram[svga->hwcursor_latch.addr + 128 + 1] << 16) |
                 (svga->vram[svga->hwcursor_latch.addr + 128 + 2] << 8) | svga->vram[svga->hwcursor_latch.addr + 128 + 3];

        for (x = 0; x < 32; x++) {
                if (!(dat[0] & 0x80000000))
                        ((uint32_t *)buffer32->line[displine])[offset + x] = 0;
                if (dat[1] & 0x80000000)
                        ((uint32_t *)buffer32->line[displine])[offset + x] ^= 0xffffff;

                dat[0] <<= 1;
                dat[1] <<= 1;
        }

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

static inline uint8_t extalu(int op, uint8_t input_a, uint8_t input_b) {
        uint8_t val;

        switch (op) {
        case 0x0:
                val = 0;
                break;
        case 0x1:
                val = ~(input_a | input_b);
                break;
        case 0x2:
                val = input_a & ~input_b;
                break;
        case 0x3:
                val = ~input_b;
                break;
        case 0x4:
                val = ~input_a & input_b;
                break;
        case 0x5:
                val = ~input_a;
                break;
        case 0x6:
                val = input_a ^ input_b;
                break;
        case 0x7:
                val = ~(input_a & input_b);
                break;
        case 0x8:
                val = input_a & input_b;
                break;
        case 0x9:
                val = ~(input_a ^ input_b);
                break;
        case 0xa:
                val = input_a;
                break;
        case 0xb:
                val = input_a | ~input_b;
                break;
        case 0xc:
                val = input_b;
                break;
        case 0xd:
                val = ~input_a | input_b;
                break;
        case 0xe:
                val = input_a | input_b;
                break;
        case 0xf:
        default:
                val = 0xff;
                break;
        }

        return val;
}

/*Remap address for chain-4/doubleword style layout*/
static inline uint32_t dword_remap(svga_t *svga, uint32_t in_addr) {
        if (svga->packed_chain4)
                return in_addr;
        return ((in_addr & 0xfffc) << 2) | ((in_addr & 0x30000) >> 14) | (in_addr & ~0x3ffff);
}

static void ht216_dm_write(ht216_t *ht216, uint32_t addr, uint8_t cpu_dat, uint8_t cpu_dat_unexpanded) {
        svga_t *svga = &ht216->svga;
        uint8_t vala, valb, valc, vald, wm = svga->writemask;
        int writemask2 = svga->writemask;
        uint8_t fg_data[4] = {0, 0, 0, 0};

        if (!(svga->gdcreg[6] & 1))
                svga->fullchange = 2;
        if (svga->chain4) {
                writemask2 = 1 << (addr & 3);
                addr = dword_remap(svga, addr) & ~3;
        } else if (svga->chain2_write) {
                writemask2 &= ~0xa;
                if (addr & 1)
                        writemask2 <<= 1;
                addr &= ~1;
                addr <<= 2;
        } else {
                addr <<= 2;
        }
        if (addr >= svga->vram_max)
                return;

        svga->changedvram[addr >> 12] = changeframecount;

        switch (ht216->ht_regs[0xfe] & HT_REG_FE_FBMC) {
        case 0x00:
                fg_data[0] = fg_data[1] = fg_data[2] = fg_data[3] = cpu_dat;
                break;
        case 0x04:
                if (ht216->ht_regs[0xfe] & HT_REG_FE_FBRC) {
                        if (addr & 4) {
                                fg_data[0] = (cpu_dat_unexpanded & (1 << (((addr + 4) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                  : ht216->ht_regs[0xfb];
                                fg_data[1] = (cpu_dat_unexpanded & (1 << (((addr + 5) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                  : ht216->ht_regs[0xfb];
                                fg_data[2] = (cpu_dat_unexpanded & (1 << (((addr + 6) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                  : ht216->ht_regs[0xfb];
                                fg_data[3] = (cpu_dat_unexpanded & (1 << (((addr + 7) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                  : ht216->ht_regs[0xfb];
                        } else {
                                fg_data[0] = (cpu_dat_unexpanded & (1 << (((addr + 0) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                  : ht216->ht_regs[0xfb];
                                fg_data[1] = (cpu_dat_unexpanded & (1 << (((addr + 1) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                  : ht216->ht_regs[0xfb];
                                fg_data[2] = (cpu_dat_unexpanded & (1 << (((addr + 2) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                  : ht216->ht_regs[0xfb];
                                fg_data[3] = (cpu_dat_unexpanded & (1 << (((addr + 3) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                  : ht216->ht_regs[0xfb];
                        }
                } else {
                        if (addr & 4) {
                                fg_data[0] = (ht216->ht_regs[0xf5] & (1 << (((addr + 4) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                    : ht216->ht_regs[0xfb];
                                fg_data[1] = (ht216->ht_regs[0xf5] & (1 << (((addr + 5) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                    : ht216->ht_regs[0xfb];
                                fg_data[2] = (ht216->ht_regs[0xf5] & (1 << (((addr + 6) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                    : ht216->ht_regs[0xfb];
                                fg_data[3] = (ht216->ht_regs[0xf5] & (1 << (((addr + 7) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                    : ht216->ht_regs[0xfb];
                        } else {
                                fg_data[0] = (ht216->ht_regs[0xf5] & (1 << (((addr + 0) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                    : ht216->ht_regs[0xfb];
                                fg_data[1] = (ht216->ht_regs[0xf5] & (1 << (((addr + 1) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                    : ht216->ht_regs[0xfb];
                                fg_data[2] = (ht216->ht_regs[0xf5] & (1 << (((addr + 2) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                    : ht216->ht_regs[0xfb];
                                fg_data[3] = (ht216->ht_regs[0xf5] & (1 << (((addr + 3) & 7) ^ 7))) ? ht216->ht_regs[0xfa]
                                                                                                    : ht216->ht_regs[0xfb];
                        }
                }
                break;
        case 0x08:
                fg_data[0] = ht216->ht_regs[0xec];
                fg_data[1] = ht216->ht_regs[0xed];
                fg_data[2] = ht216->ht_regs[0xee];
                fg_data[3] = ht216->ht_regs[0xef];
                break;
        case 0x0c:
                fg_data[0] = ht216->ht_regs[0xec];
                fg_data[1] = ht216->ht_regs[0xed];
                fg_data[2] = ht216->ht_regs[0xee];
                fg_data[3] = ht216->ht_regs[0xef];
                break;
        }

        switch (svga->writemode) {
        case 1:
                if (writemask2 & 1)
                        svga->vram[addr] = svga->la;
                if (writemask2 & 2)
                        svga->vram[addr | 0x1] = svga->lb;
                if (writemask2 & 4)
                        svga->vram[addr | 0x2] = svga->lc;
                if (writemask2 & 8)
                        svga->vram[addr | 0x3] = svga->ld;
                break;
        case 0:
                if (svga->gdcreg[8] == 0xff && !(svga->gdcreg[3] & 0x18) && (!svga->gdcreg[1] || svga->set_reset_disabled)) {
                        if (writemask2 & 1)
                                svga->vram[addr] = fg_data[0];
                        if (writemask2 & 2)
                                svga->vram[addr | 0x1] = fg_data[1];
                        if (writemask2 & 4)
                                svga->vram[addr | 0x2] = fg_data[2];
                        if (writemask2 & 8)
                                svga->vram[addr | 0x3] = fg_data[3];
                } else {
                        if (svga->gdcreg[1] & 1)
                                vala = (svga->gdcreg[0] & 1) ? 0xff : 0;
                        else
                                vala = fg_data[0];
                        if (svga->gdcreg[1] & 2)
                                valb = (svga->gdcreg[0] & 2) ? 0xff : 0;
                        else
                                valb = fg_data[1];
                        if (svga->gdcreg[1] & 4)
                                valc = (svga->gdcreg[0] & 4) ? 0xff : 0;
                        else
                                valc = fg_data[2];
                        if (svga->gdcreg[1] & 8)
                                vald = (svga->gdcreg[0] & 8) ? 0xff : 0;
                        else
                                vald = fg_data[3];

                        switch (svga->gdcreg[3] & 0x18) {
                        case 0: /*Set*/
                                if (writemask2 & 1)
                                        svga->vram[addr] = (vala & svga->gdcreg[8]) | (svga->la & ~svga->gdcreg[8]);
                                if (writemask2 & 2)
                                        svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) | (svga->lb & ~svga->gdcreg[8]);
                                if (writemask2 & 4)
                                        svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) | (svga->lc & ~svga->gdcreg[8]);
                                if (writemask2 & 8)
                                        svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) | (svga->ld & ~svga->gdcreg[8]);
                                break;
                        case 8: /*AND*/
                                if (writemask2 & 1)
                                        svga->vram[addr] = (vala | ~svga->gdcreg[8]) & svga->la;
                                if (writemask2 & 2)
                                        svga->vram[addr | 0x1] = (valb | ~svga->gdcreg[8]) & svga->lb;
                                if (writemask2 & 4)
                                        svga->vram[addr | 0x2] = (valc | ~svga->gdcreg[8]) & svga->lc;
                                if (writemask2 & 8)
                                        svga->vram[addr | 0x3] = (vald | ~svga->gdcreg[8]) & svga->ld;
                                break;
                        case 0x10: /*OR*/
                                if (writemask2 & 1)
                                        svga->vram[addr] = (vala & svga->gdcreg[8]) | svga->la;
                                if (writemask2 & 2)
                                        svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) | svga->lb;
                                if (writemask2 & 4)
                                        svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) | svga->lc;
                                if (writemask2 & 8)
                                        svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) | svga->ld;
                                break;
                        case 0x18: /*XOR*/
                                if (writemask2 & 1)
                                        svga->vram[addr] = (vala & svga->gdcreg[8]) ^ svga->la;
                                if (writemask2 & 2)
                                        svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) ^ svga->lb;
                                if (writemask2 & 4)
                                        svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) ^ svga->lc;
                                if (writemask2 & 8)
                                        svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) ^ svga->ld;
                                break;
                        }
                        //                        pclog("- %02X %02X %02X %02X
                        //                        %08X\n",vram[addr],vram[addr|0x1],vram[addr|0x2],vram[addr|0x3],addr);
                }
                break;
        case 2:
                if (!(svga->gdcreg[3] & 0x18) && (!svga->gdcreg[1] || svga->set_reset_disabled)) {
                        if (writemask2 & 1)
                                svga->vram[addr] = (((cpu_dat & 1) ? 0xff : 0) & svga->gdcreg[8]) | (svga->la & ~svga->gdcreg[8]);
                        if (writemask2 & 2)
                                svga->vram[addr | 0x1] =
                                        (((cpu_dat & 2) ? 0xff : 0) & svga->gdcreg[8]) | (svga->lb & ~svga->gdcreg[8]);
                        if (writemask2 & 4)
                                svga->vram[addr | 0x2] =
                                        (((cpu_dat & 4) ? 0xff : 0) & svga->gdcreg[8]) | (svga->lc & ~svga->gdcreg[8]);
                        if (writemask2 & 8)
                                svga->vram[addr | 0x3] =
                                        (((cpu_dat & 8) ? 0xff : 0) & svga->gdcreg[8]) | (svga->ld & ~svga->gdcreg[8]);
                } else {
                        vala = ((cpu_dat & 1) ? 0xff : 0);
                        valb = ((cpu_dat & 2) ? 0xff : 0);
                        valc = ((cpu_dat & 4) ? 0xff : 0);
                        vald = ((cpu_dat & 8) ? 0xff : 0);
                        switch (svga->gdcreg[3] & 0x18) {
                        case 0: /*Set*/
                                if (writemask2 & 1)
                                        svga->vram[addr] = (vala & svga->gdcreg[8]) | (svga->la & ~svga->gdcreg[8]);
                                if (writemask2 & 2)
                                        svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) | (svga->lb & ~svga->gdcreg[8]);
                                if (writemask2 & 4)
                                        svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) | (svga->lc & ~svga->gdcreg[8]);
                                if (writemask2 & 8)
                                        svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) | (svga->ld & ~svga->gdcreg[8]);
                                break;
                        case 8: /*AND*/
                                if (writemask2 & 1)
                                        svga->vram[addr] = (vala | ~svga->gdcreg[8]) & svga->la;
                                if (writemask2 & 2)
                                        svga->vram[addr | 0x1] = (valb | ~svga->gdcreg[8]) & svga->lb;
                                if (writemask2 & 4)
                                        svga->vram[addr | 0x2] = (valc | ~svga->gdcreg[8]) & svga->lc;
                                if (writemask2 & 8)
                                        svga->vram[addr | 0x3] = (vald | ~svga->gdcreg[8]) & svga->ld;
                                break;
                        case 0x10: /*OR*/
                                if (writemask2 & 1)
                                        svga->vram[addr] = (vala & svga->gdcreg[8]) | svga->la;
                                if (writemask2 & 2)
                                        svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) | svga->lb;
                                if (writemask2 & 4)
                                        svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) | svga->lc;
                                if (writemask2 & 8)
                                        svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) | svga->ld;
                                break;
                        case 0x18: /*XOR*/
                                if (writemask2 & 1)
                                        svga->vram[addr] = (vala & svga->gdcreg[8]) ^ svga->la;
                                if (writemask2 & 2)
                                        svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) ^ svga->lb;
                                if (writemask2 & 4)
                                        svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) ^ svga->lc;
                                if (writemask2 & 8)
                                        svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) ^ svga->ld;
                                break;
                        }
                }
                break;
        case 3:
                wm = svga->gdcreg[8];
                svga->gdcreg[8] &= cpu_dat;

                vala = (svga->gdcreg[0] & 1) ? 0xff : 0;
                valb = (svga->gdcreg[0] & 2) ? 0xff : 0;
                valc = (svga->gdcreg[0] & 4) ? 0xff : 0;
                vald = (svga->gdcreg[0] & 8) ? 0xff : 0;
                switch (svga->gdcreg[3] & 0x18) {
                case 0: /*Set*/
                        if (writemask2 & 1)
                                svga->vram[addr] = (vala & svga->gdcreg[8]) | (svga->la & ~svga->gdcreg[8]);
                        if (writemask2 & 2)
                                svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) | (svga->lb & ~svga->gdcreg[8]);
                        if (writemask2 & 4)
                                svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) | (svga->lc & ~svga->gdcreg[8]);
                        if (writemask2 & 8)
                                svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) | (svga->ld & ~svga->gdcreg[8]);
                        break;
                case 8: /*AND*/
                        if (writemask2 & 1)
                                svga->vram[addr] = (vala | ~svga->gdcreg[8]) & svga->la;
                        if (writemask2 & 2)
                                svga->vram[addr | 0x1] = (valb | ~svga->gdcreg[8]) & svga->lb;
                        if (writemask2 & 4)
                                svga->vram[addr | 0x2] = (valc | ~svga->gdcreg[8]) & svga->lc;
                        if (writemask2 & 8)
                                svga->vram[addr | 0x3] = (vald | ~svga->gdcreg[8]) & svga->ld;
                        break;
                case 0x10: /*OR*/
                        if (writemask2 & 1)
                                svga->vram[addr] = (vala & svga->gdcreg[8]) | svga->la;
                        if (writemask2 & 2)
                                svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) | svga->lb;
                        if (writemask2 & 4)
                                svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) | svga->lc;
                        if (writemask2 & 8)
                                svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) | svga->ld;
                        break;
                case 0x18: /*XOR*/
                        if (writemask2 & 1)
                                svga->vram[addr] = (vala & svga->gdcreg[8]) ^ svga->la;
                        if (writemask2 & 2)
                                svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) ^ svga->lb;
                        if (writemask2 & 4)
                                svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) ^ svga->lc;
                        if (writemask2 & 8)
                                svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) ^ svga->ld;
                        break;
                }
                svga->gdcreg[8] = wm;
                break;
        }
}

static void ht216_dm_extalu_write(ht216_t *ht216, uint32_t addr, uint8_t cpu_dat, uint8_t bit_mask, uint8_t cpu_dat_unexpanded,
                                  uint8_t rop_select) {
        /*Input B = CD.5
          Input A = FE[3:2]
                00 = Set/Reset output mode
                        output = CPU-side ALU input
                01 = Solid fg/bg mode (3C4:FA/FB)
                        Bit mask = 3CF.F5 or CPU byte
                10 = Dithered fg  (3CF:EC-EF)
                11 = RMW (dest data) (set if CD.5 = 1)
          F/B ROP select = FE[5:4]
                00 = CPU byte
                01 = Bit mask (3CF:8)
                1x = (3C4:F5)*/
        svga_t *svga = &ht216->svga;
        uint8_t input_a = 0, input_b = 0;
        uint8_t fg, bg;
        uint8_t output;
        uint32_t remapped_addr = dword_remap(svga, addr);

        if (ht216->ht_regs[0xcd] & HT_REG_CD_RMWMDE) /*RMW*/
                input_b = svga->vram[remapped_addr];
        else
                input_b = ht216->bg_latch[addr & 7];

        switch (ht216->ht_regs[0xfe] & HT_REG_FE_FBMC) {
        case 0x00:
                input_a = cpu_dat;
                break;
        case 0x04:
                if (ht216->ht_regs[0xfe] & HT_REG_FE_FBRC)
                        input_a = (cpu_dat_unexpanded & (1 << ((addr & 7) ^ 7))) ? ht216->ht_regs[0xfa] : ht216->ht_regs[0xfb];
                else
                        input_a = (ht216->ht_regs[0xf5] & (1 << ((addr & 7) ^ 7))) ? ht216->ht_regs[0xfa] : ht216->ht_regs[0xfb];
                break;
        case 0x08:
                input_a = ht216->ht_regs[0xec + (addr & 3)];
                break;
        case 0x0c:
                input_a = ht216->bg_latch[addr & 7];
                break;
        }

        fg = extalu(ht216->ht_regs[0xce] >> 4, input_a, input_b);
        bg = extalu(ht216->ht_regs[0xce] & 0xf, input_a, input_b);
        //        svga->vram[addr]
        output = (fg & rop_select) | (bg & ~rop_select);
        svga->vram[addr] = (svga->vram[remapped_addr] & ~bit_mask) | (output & bit_mask);
        //        pclog("  A=%02x B=%02x R=%02x FG=%02x BG=%02x ops=%02x val=%02x addr=%05x\n", input_a, input_b, rop_select, fg,
        //        bg, ht216->ht_regs[0xce], svga->vram[addr], addr);
        svga->changedvram[remapped_addr >> 12] = changeframecount;
}

static void ht216_write_common(ht216_t *ht216, uint32_t addr, uint8_t val) {
        /*Input B = CD.5
          Input A = FE[3:2]
                00 = Set/Reset output mode
                        output = CPU-side ALU input
                01 = Solid fg/bg mode (3C4:FA/FB)
                        Bit mask = 3CF.F5 or CPU byte
                10 = Dithered fg  (3CF:EC-EF)
                11 = RMW (dest data) (set if CD.5 = 1)
          F/B ROP select = FE[5:4]
                00 = CPU byte
                01 = Bit mask (3CF:8)
                1x = (3C4:F5)
        */
        svga_t *svga = &ht216->svga;

        cycles -= video_timing_write_b;
        cycles_lost += video_timing_write_b;

        egawrites++;

        addr &= 0xfffff;

        //        pclog("%08X   %02x %08x\n", addr,  svga->gdcreg[6], svga->banked_mask);

        val = svga_rotate[svga->gdcreg[3] & 7][val];

        if (ht216->ht_regs[0xcd] & HT_REG_CD_EXALU) /*Extended ALU*/
        {
                uint8_t bit_mask = 0, rop_select = 0;

                switch (ht216->ht_regs[0xfe] & HT_REG_FE_FBRSL) {
                case 0x00:
                        rop_select = val;
                        break;
                case 0x10:
                        rop_select = svga->gdcreg[8];
                        break;
                case 0x20:
                case 0x30:
                        rop_select = ht216->ht_regs[0xf5];
                        break;
                }

                switch (ht216->ht_regs[0xcd] & HT_REG_CD_BMSKSL) {
                case 0x00:
                        bit_mask = svga->gdcreg[8];
                        break;
                case 0x04:
                        bit_mask = val;
                        break;
                case 0x08:
                case 0x0c:
                        bit_mask = ht216->ht_regs[0xf5];
                        break;
                }

                if (ht216->ht_regs[0xcd] & HT_REG_CD_FP8PCEXP) /*1->8 bit expansion*/
                {
                        addr = (addr << 3) & 0xfffff;
                        ht216_dm_extalu_write(ht216, addr, (val & 0x80) ? 0xff : 0, (bit_mask & 0x80) ? 0xff : 0, val,
                                              (rop_select & 0x80) ? 0xff : 0);
                        ht216_dm_extalu_write(ht216, addr + 1, (val & 0x40) ? 0xff : 0, (bit_mask & 0x40) ? 0xff : 0, val,
                                              (rop_select & 0x40) ? 0xff : 0);
                        ht216_dm_extalu_write(ht216, addr + 2, (val & 0x20) ? 0xff : 0, (bit_mask & 0x20) ? 0xff : 0, val,
                                              (rop_select & 0x20) ? 0xff : 0);
                        ht216_dm_extalu_write(ht216, addr + 3, (val & 0x10) ? 0xff : 0, (bit_mask & 0x10) ? 0xff : 0, val,
                                              (rop_select & 0x10) ? 0xff : 0);
                        ht216_dm_extalu_write(ht216, addr + 4, (val & 0x08) ? 0xff : 0, (bit_mask & 0x08) ? 0xff : 0, val,
                                              (rop_select & 0x08) ? 0xff : 0);
                        ht216_dm_extalu_write(ht216, addr + 5, (val & 0x04) ? 0xff : 0, (bit_mask & 0x04) ? 0xff : 0, val,
                                              (rop_select & 0x04) ? 0xff : 0);
                        ht216_dm_extalu_write(ht216, addr + 6, (val & 0x02) ? 0xff : 0, (bit_mask & 0x02) ? 0xff : 0, val,
                                              (rop_select & 0x02) ? 0xff : 0);
                        ht216_dm_extalu_write(ht216, addr + 7, (val & 0x01) ? 0xff : 0, (bit_mask & 0x01) ? 0xff : 0, val,
                                              (rop_select & 0x01) ? 0xff : 0);
                } else
                        ht216_dm_extalu_write(ht216, addr, val, bit_mask, val, rop_select);
        } else {
                if (ht216->ht_regs[0xcd] & HT_REG_CD_FP8PCEXP) /*1->8 bit expansion*/
                {
                        addr = (addr << 3) & 0xfffff;
                        ht216_dm_write(ht216, addr, (val & 0x80) ? 0xff : 0, val);
                        ht216_dm_write(ht216, addr + 1, (val & 0x40) ? 0xff : 0, val);
                        ht216_dm_write(ht216, addr + 2, (val & 0x20) ? 0xff : 0, val);
                        ht216_dm_write(ht216, addr + 3, (val & 0x10) ? 0xff : 0, val);
                        ht216_dm_write(ht216, addr + 4, (val & 0x08) ? 0xff : 0, val);
                        ht216_dm_write(ht216, addr + 5, (val & 0x04) ? 0xff : 0, val);
                        ht216_dm_write(ht216, addr + 6, (val & 0x02) ? 0xff : 0, val);
                        ht216_dm_write(ht216, addr + 7, (val & 0x01) ? 0xff : 0, val);
                } else
                        ht216_dm_write(ht216, addr, val, val);
        }
}

static void ht216_write(uint32_t addr, uint8_t val, void *p) {
        ht216_t *ht216 = (ht216_t *)p;
        svga_t *svga = &ht216->svga;

        /*        if (ht216->ht_regs[0xcd])
                        pclog("ht216_write: [CD]=%02x [FE]=%02x %08x %02x  ", ht216->ht_regs[0xcd], ht216->ht_regs[0xfe], addr,
           val); pclog("ht216_write : %05X %02X  ", addr, val);*/
        addr &= svga->banked_mask;
        addr = (addr & 0x7fff) + ht216->write_bank[(addr >> 15) & 1];
        //        pclog("phys_addr=%05x\n", addr);
        if (!ht216->ht_regs[0xcd] && !ht216->ht_regs[0xfe])
                svga_write_linear(addr, val, &ht216->svga);
        else {
                ht216_write_common(ht216, addr, val);
        }
}
static void ht216_writew(uint32_t addr, uint16_t val, void *p) {
        ht216_t *ht216 = (ht216_t *)p;
        svga_t *svga = &ht216->svga;

        addr &= svga->banked_mask;
        addr = (addr & 0x7fff) + ht216->write_bank[(addr >> 15) & 1];
        if (!ht216->ht_regs[0xcd] && !ht216->ht_regs[0xfe])
                svga_writew_linear(addr, val, &ht216->svga);
        else {
                ht216_write_common(ht216, addr, val);
                ht216_write_common(ht216, addr + 1, val >> 8);
        }
}
static void ht216_writel(uint32_t addr, uint32_t val, void *p) {
        ht216_t *ht216 = (ht216_t *)p;
        svga_t *svga = &ht216->svga;

        addr &= svga->banked_mask;
        addr = (addr & 0x7fff) + ht216->write_bank[(addr >> 15) & 1];
        if (!ht216->ht_regs[0xcd] && !ht216->ht_regs[0xfe])
                svga_writel_linear(addr, val, &ht216->svga);
        else {
                ht216_write_common(ht216, addr, val);
                ht216_write_common(ht216, addr + 1, val >> 8);
                ht216_write_common(ht216, addr + 2, val >> 16);
                ht216_write_common(ht216, addr + 3, val >> 24);
        }
}

static void ht216_write_linear(uint32_t addr, uint8_t val, void *p) {
        ht216_t *ht216 = (ht216_t *)p;
        svga_t *svga = &ht216->svga;

        if (!svga->chain4) /*Bits 16 and 17 of linear address seem to be unused in planar modes*/
                addr = (addr & 0xffff) | ((addr & 0xc0000) >> 2);

        if (!ht216->ht_regs[0xcd] && !ht216->ht_regs[0xfe])
                svga_write_linear(addr, val, &ht216->svga);
        else
                ht216_write_common(ht216, addr, val);
}
static void ht216_writew_linear(uint32_t addr, uint16_t val, void *p) {
        ht216_t *ht216 = (ht216_t *)p;
        svga_t *svga = &ht216->svga;

        if (!svga->chain4)
                addr = (addr & 0xffff) | ((addr & 0xc0000) >> 2);

        if (!ht216->ht_regs[0xcd] && !ht216->ht_regs[0xfe])
                svga_writew_linear(addr, val, &ht216->svga);
        else {
                ht216_write_common(ht216, addr, val);
                ht216_write_common(ht216, addr + 1, val >> 8);
        }
}
static void ht216_writel_linear(uint32_t addr, uint32_t val, void *p) {
        ht216_t *ht216 = (ht216_t *)p;
        svga_t *svga = &ht216->svga;

        if (!svga->chain4)
                addr = (addr & 0xffff) | ((addr & 0xc0000) >> 2);

        if (!ht216->ht_regs[0xcd] && !ht216->ht_regs[0xfe])
                svga_writel_linear(addr, val, &ht216->svga);
        else {
                ht216_write_common(ht216, addr, val);
                ht216_write_common(ht216, addr + 1, val >> 8);
                ht216_write_common(ht216, addr + 2, val >> 16);
                ht216_write_common(ht216, addr + 3, val >> 24);
        }
}

static uint8_t ht216_read_common(ht216_t *ht216, uint32_t addr) {
        svga_t *svga = &ht216->svga;
        uint8_t temp, temp2, temp3, temp4;
        int readplane = svga->readplane;
        uint32_t latch_addr;

        if (ht216->ht_regs[0xc8] & HT_REG_C8_MOVSB)
                addr <<= 3;
        //        pclog("%08X\n", addr);

        addr &= 0xfffff;

        cycles -= video_timing_read_b;
        cycles_lost += video_timing_read_b;

        egareads++;

        if (svga->chain4) {
                //                if (ht216->ht_regs[0xcd])
                //                        pclog(" addr=%08x decode_mask=%08x vram_max=%08x\n", addr, svga->decode_mask,
                //                        svga->vram_max);
                addr &= svga->decode_mask;
                if (addr >= svga->vram_max)
                        return 0xff;

                latch_addr = (addr & svga->vram_mask) & ~7;
                if (ht216->ht_regs[0xcd] & HT_REG_CD_ASTODE)
                        latch_addr += (svga->gdcreg[3] & 7);
                ht216->bg_latch[0] = svga->vram[dword_remap(svga, latch_addr)];
                ht216->bg_latch[1] = svga->vram[dword_remap(svga, latch_addr + 1)];
                ht216->bg_latch[2] = svga->vram[dword_remap(svga, latch_addr + 2)];
                ht216->bg_latch[3] = svga->vram[dword_remap(svga, latch_addr + 3)];
                ht216->bg_latch[4] = svga->vram[dword_remap(svga, latch_addr + 4)];
                ht216->bg_latch[5] = svga->vram[dword_remap(svga, latch_addr + 5)];
                ht216->bg_latch[6] = svga->vram[dword_remap(svga, latch_addr + 6)];
                ht216->bg_latch[7] = svga->vram[dword_remap(svga, latch_addr + 7)];

                /*pclog("  Read %08x  %02x %02x %02x %02x %02x %02x %02x %02x\n", addr,
                        ht216->bg_latch[0],ht216->bg_latch[1],ht216->bg_latch[2],ht216->bg_latch[3],
                        ht216->bg_latch[4],ht216->bg_latch[5],ht216->bg_latch[6],ht216->bg_latch[7]);*/

                return svga->vram[dword_remap(svga, addr) & svga->vram_mask];
        } else if (svga->chain2_read) {
                readplane = (readplane & 2) | (addr & 1);
                addr &= ~1;
                addr <<= 2;
        } else
                addr <<= 2;

        addr &= svga->decode_mask;

        if (addr >= svga->vram_max)
                return 0xff;

        addr &= svga->vram_mask;

        latch_addr = addr & ~7;
        if (ht216->ht_regs[0xcd] & HT_REG_CD_ASTODE) {
                int offset = addr & 7;

                ht216->bg_latch[0] = svga->vram[latch_addr | offset];
                ht216->bg_latch[1] = svga->vram[latch_addr | ((offset + 1) & 7)];
                ht216->bg_latch[2] = svga->vram[latch_addr | ((offset + 2) & 7)];
                ht216->bg_latch[3] = svga->vram[latch_addr | ((offset + 3) & 7)];
                ht216->bg_latch[4] = svga->vram[latch_addr | ((offset + 4) & 7)];
                ht216->bg_latch[5] = svga->vram[latch_addr | ((offset + 5) & 7)];
                ht216->bg_latch[6] = svga->vram[latch_addr | ((offset + 6) & 7)];
                ht216->bg_latch[7] = svga->vram[latch_addr | ((offset + 7) & 7)];
        } else {
                ht216->bg_latch[0] = svga->vram[latch_addr];
                ht216->bg_latch[1] = svga->vram[latch_addr | 1];
                ht216->bg_latch[2] = svga->vram[latch_addr | 2];
                ht216->bg_latch[3] = svga->vram[latch_addr | 3];
                ht216->bg_latch[4] = svga->vram[latch_addr | 4];
                ht216->bg_latch[5] = svga->vram[latch_addr | 5];
                ht216->bg_latch[6] = svga->vram[latch_addr | 6];
                ht216->bg_latch[7] = svga->vram[latch_addr | 7];
        }
        /*pclog("  Read %02x %02x %02x %02x %02x %02x %02x %02x\n",
                ht216->bg_latch[0],ht216->bg_latch[1],ht216->bg_latch[2],ht216->bg_latch[3],
                ht216->bg_latch[4],ht216->bg_latch[5],ht216->bg_latch[6],ht216->bg_latch[7]);*/
        if (addr & 4) {
                svga->la = ht216->bg_latch[4];
                svga->lb = ht216->bg_latch[5];
                svga->lc = ht216->bg_latch[6];
                svga->ld = ht216->bg_latch[7];
        } else {
                svga->la = ht216->bg_latch[0];
                svga->lb = ht216->bg_latch[1];
                svga->lc = ht216->bg_latch[2];
                svga->ld = ht216->bg_latch[3];
        }
        if (svga->readmode) {
                temp = svga->la;
                temp ^= (svga->colourcompare & 1) ? 0xff : 0;
                temp &= (svga->colournocare & 1) ? 0xff : 0;
                temp2 = svga->lb;
                temp2 ^= (svga->colourcompare & 2) ? 0xff : 0;
                temp2 &= (svga->colournocare & 2) ? 0xff : 0;
                temp3 = svga->lc;
                temp3 ^= (svga->colourcompare & 4) ? 0xff : 0;
                temp3 &= (svga->colournocare & 4) ? 0xff : 0;
                temp4 = svga->ld;
                temp4 ^= (svga->colourcompare & 8) ? 0xff : 0;
                temp4 &= (svga->colournocare & 8) ? 0xff : 0;
                return ~(temp | temp2 | temp3 | temp4);
        }
        // printf("Read %02X %04X %04X\n",vram[addr|svga->readplane],addr,svga->readplane);
        return svga->vram[addr | readplane];

        //        return svga_read_linear(addr, &ht216->svga);
}
static uint8_t ht216_read(uint32_t addr, void *p) {
        ht216_t *ht216 = (ht216_t *)p;
        svga_t *svga = &ht216->svga;

        //        if (ht216->ht_regs[0xcd])
        //                pclog("ht216_read: [CD]=%02x [FC]=%02x  %08x\n", ht216->ht_regs[0xcd], ht216->ht_regs[0xfc], addr);
        //        pclog("ht216_read : %05X ", addr);
        addr &= svga->banked_mask;
        addr = (addr & 0x7fff) + ht216->read_bank[(addr >> 15) & 1];

        return ht216_read_common(ht216, addr);
}

/*static uint16_t ht216_readw(uint32_t addr, void *p)
{
        ht216_t *ht216 = (ht216_t *)p;
        svga_t *svga = &ht216->svga;

        addr &= svga->banked_mask;
        addr = (addr & 0x7fff) + ht216->write_bank[(addr >> 15) & 1];
        return svga_readw_linear(addr, &ht216->svga);
}*/

static uint8_t ht216_read_linear(uint32_t addr, void *p) {
        ht216_t *ht216 = (ht216_t *)p;
        svga_t *svga = &ht216->svga;

        if (svga->chain4)
                return ht216_read_common(ht216, addr);
        else
                return ht216_read_common(ht216, (addr & 0xffff) | ((addr & 0xc0000) >> 2));
}

void *ht216_init() {
        ht216_t *ht216 = malloc(sizeof(ht216_t));
        svga_t *svga = &ht216->svga;
        memset(ht216, 0, sizeof(ht216_t));

        io_sethandler(0x03c0, 0x0020, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216);
        io_sethandler(0x46e8, 0x0001, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216);

        svga_init(&ht216->svga, ht216, 1 << 20, /*1MB*/
                  ht216_recalctimings, ht216_in, ht216_out, ht216_hwcursor_draw, NULL);
        svga->hwcursor.ysize = 32;
        ht216->vram_mask = 0xfffff;
        svga->decode_mask = 0xfffff;

        mem_mapping_set_handler(&ht216->svga.mapping, ht216_read, NULL /*ht216_readw*/, NULL, ht216_write, ht216_writew,
                                ht216_writel);
        mem_mapping_set_p(&ht216->svga.mapping, ht216);
        mem_mapping_add(&ht216->linear_mapping, 0, 0, ht216_read_linear, NULL, NULL, ht216_write_linear, ht216_writew_linear,
                        ht216_writel_linear, NULL, 0, &ht216->svga);

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

        ht216->ht_regs[0xb4] = 0x08; /*32-bit DRAM bus*/

        return ht216;
}

static void *ht216_pb410a_init() {
        ht216_t *ht216 = ht216_init();

        return ht216;
}

void ht216_close(void *p) {
        ht216_t *ht216 = (ht216_t *)p;

        svga_close(&ht216->svga);

        free(ht216);
}

void ht216_speed_changed(void *p) {
        ht216_t *ht216 = (ht216_t *)p;

        svga_recalctimings(&ht216->svga);
}

void ht216_force_redraw(void *p) {
        ht216_t *ht216 = (ht216_t *)p;

        ht216->svga.fullchange = changeframecount;
}

void ht216_add_status_info(char *s, int max_len, void *p) {
        ht216_t *ht216 = (ht216_t *)p;

        svga_add_status_info(s, max_len, &ht216->svga);
}

device_t ht216_32_pb410a_device = {"Headland HT216-32 (Packard Bell PB410A)",
                                   0,
                                   ht216_pb410a_init,
                                   ht216_close,
                                   NULL,
                                   ht216_speed_changed,
                                   ht216_force_redraw,
                                   ht216_add_status_info};
