/* * 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. * * Video 7 VGA 1024i emulation. * * Version: @(#)vid_ht216.c 1.0.3 2019/12/04 * * Authors: Sarah Walker, * Miran Grca, * * Copyright 2019 Sarah Walker. * Copyright 2019 Miran Grca. */ #include #include #include #include #include #include "../86box.h" #include "../cpu/cpu.h" #include "../io.h" #include "../mem.h" #include "../timer.h" #include "../pic.h" #include "../rom.h" #include "../device.h" #include "video.h" #include "vid_svga.h" #include "vid_svga_render.h" #include "vid_ht216.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, pad; uint16_t id; 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); #define BIOS_G2_GC205_PATH L"roms/video/video7/BIOS.BIN" #define BIOS_VIDEO7_VGA_1024I_PATH L"roms/video/video7/Video Seven VGA 1024i - BIOS - v2.19 - 435-0062-05 - U17 - 27C256.BIN" static video_timings_t timing_v7vga = {VIDEO_ISA, 5, 5, 9, 20, 20, 30}; #ifdef ENABLE_HT216_LOG int ht216_do_log = ENABLE_HT216_LOG; static void ht216_log(const char *fmt, ...) { va_list ap; if (ht216_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else #define ht216_log(fmt, ...) #endif 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; ht216_log("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) { old = ht216->ht_regs[svga->seqaddr & 0xff]; ht216->ht_regs[svga->seqaddr & 0xff] = val; 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; 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->latch.b[0] = val; break; case 0xa1: svga->latch.b[1] = val; break; case 0xa2: svga->latch.b[2] = val; break; case 0xa3: svga->latch.b[3] = 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 0xc8: if ((old ^ val) & 0x10) { svga->fullchange = changeframecount; svga_recalctimings(svga); } 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 0xff: svga->hwcursor.addr = ((ht216->ht_regs[0x94] << 6) | (3 << 14) | ((val & 0x60) << 11)) << 2; 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); 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 & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1)) addr ^= 0x60; switch (addr) { case 0x3c2: break; case 0x3c5: if (svga->seqaddr == 6) return ht216->ext_reg_enable; if (svga->seqaddr >= 0x80) { if (ht216->ext_reg_enable) { switch (svga->seqaddr & 0xff) { case 0x83: if (svga->attrff) return svga->attraddr | 0x80; return svga->attraddr; case 0x8e: return ht216->id & 0xff; case 0x8f: return (ht216->id >> 8) & 0xff; case 0xa0: return svga->latch.b[0]; case 0xa1: return svga->latch.b[1]; case 0xa2: return svga->latch.b[2]; case 0xa3: return svga->latch.b[3]; } 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); 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; } } } void ht216_recalctimings(svga_t *svga) { ht216_t *ht216 = (ht216_t *)svga->p; 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) { int x; uint32_t dat[2]; int offset = svga->hwcursor_latch.x + svga->x_add; 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; } 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 || svga->fb_only) { writemask2=1<<(addr&3); 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->latch.b[0]; if (writemask2 & 2) svga->vram[addr | 0x1] = svga->latch.b[1]; if (writemask2 & 4) svga->vram[addr | 0x2] = svga->latch.b[2]; if (writemask2 & 8) svga->vram[addr | 0x3] = svga->latch.b[3]; 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->latch.b[0] & ~svga->gdcreg[8]); if (writemask2 & 2) svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) | (svga->latch.b[1] & ~svga->gdcreg[8]); if (writemask2 & 4) svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) | (svga->latch.b[2] & ~svga->gdcreg[8]); if (writemask2 & 8) svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) | (svga->latch.b[3] & ~svga->gdcreg[8]); break; case 8: /*AND*/ if (writemask2 & 1) svga->vram[addr] = (vala | ~svga->gdcreg[8]) & svga->latch.b[0]; if (writemask2 & 2) svga->vram[addr | 0x1] = (valb | ~svga->gdcreg[8]) & svga->latch.b[1]; if (writemask2 & 4) svga->vram[addr | 0x2] = (valc | ~svga->gdcreg[8]) & svga->latch.b[2]; if (writemask2 & 8) svga->vram[addr | 0x3] = (vald | ~svga->gdcreg[8]) & svga->latch.b[3]; break; case 0x10: /*OR*/ if (writemask2 & 1) svga->vram[addr] = (vala & svga->gdcreg[8]) | svga->latch.b[0]; if (writemask2 & 2) svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) | svga->latch.b[1]; if (writemask2 & 4) svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) | svga->latch.b[2]; if (writemask2 & 8) svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) | svga->latch.b[3]; break; case 0x18: /*XOR*/ if (writemask2 & 1) svga->vram[addr] = (vala & svga->gdcreg[8]) ^ svga->latch.b[0]; if (writemask2 & 2) svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) ^ svga->latch.b[1]; if (writemask2 & 4) svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) ^ svga->latch.b[2]; if (writemask2 & 8) svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) ^ svga->latch.b[3]; break; } } 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->latch.b[0] & ~svga->gdcreg[8]); if (writemask2 & 2) svga->vram[addr | 0x1] = (((cpu_dat & 2) ? 0xff : 0) & svga->gdcreg[8]) | (svga->latch.b[1] & ~svga->gdcreg[8]); if (writemask2 & 4) svga->vram[addr | 0x2] = (((cpu_dat & 4) ? 0xff : 0) & svga->gdcreg[8]) | (svga->latch.b[2] & ~svga->gdcreg[8]); if (writemask2 & 8) svga->vram[addr | 0x3] = (((cpu_dat & 8) ? 0xff : 0) & svga->gdcreg[8]) | (svga->latch.b[3] & ~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->latch.b[0] & ~svga->gdcreg[8]); if (writemask2 & 2) svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) | (svga->latch.b[1] & ~svga->gdcreg[8]); if (writemask2 & 4) svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) | (svga->latch.b[2] & ~svga->gdcreg[8]); if (writemask2 & 8) svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) | (svga->latch.b[3] & ~svga->gdcreg[8]); break; case 8: /*AND*/ if (writemask2 & 1) svga->vram[addr] = (vala | ~svga->gdcreg[8]) & svga->latch.b[0]; if (writemask2 & 2) svga->vram[addr | 0x1] = (valb | ~svga->gdcreg[8]) & svga->latch.b[1]; if (writemask2 & 4) svga->vram[addr | 0x2] = (valc | ~svga->gdcreg[8]) & svga->latch.b[2]; if (writemask2 & 8) svga->vram[addr | 0x3] = (vald | ~svga->gdcreg[8]) & svga->latch.b[3]; break; case 0x10: /*OR*/ if (writemask2 & 1) svga->vram[addr] = (vala & svga->gdcreg[8]) | svga->latch.b[0]; if (writemask2 & 2) svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) | svga->latch.b[1]; if (writemask2 & 4) svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) | svga->latch.b[2]; if (writemask2 & 8) svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) | svga->latch.b[3]; break; case 0x18: /*XOR*/ if (writemask2 & 1) svga->vram[addr] = (vala & svga->gdcreg[8]) ^ svga->latch.b[0]; if (writemask2 & 2) svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) ^ svga->latch.b[1]; if (writemask2 & 4) svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) ^ svga->latch.b[2]; if (writemask2 & 8) svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) ^ svga->latch.b[3]; 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->latch.b[0] & ~svga->gdcreg[8]); if (writemask2 & 2) svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) | (svga->latch.b[1] & ~svga->gdcreg[8]); if (writemask2 & 4) svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) | (svga->latch.b[2] & ~svga->gdcreg[8]); if (writemask2 & 8) svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) | (svga->latch.b[3] & ~svga->gdcreg[8]); break; case 8: /*AND*/ if (writemask2 & 1) svga->vram[addr] = (vala | ~svga->gdcreg[8]) & svga->latch.b[0]; if (writemask2 & 2) svga->vram[addr | 0x1] = (valb | ~svga->gdcreg[8]) & svga->latch.b[1]; if (writemask2 & 4) svga->vram[addr | 0x2] = (valc | ~svga->gdcreg[8]) & svga->latch.b[2]; if (writemask2 & 8) svga->vram[addr | 0x3] = (vald | ~svga->gdcreg[8]) & svga->latch.b[3]; break; case 0x10: /*OR*/ if (writemask2 & 1) svga->vram[addr] = (vala & svga->gdcreg[8]) | svga->latch.b[0]; if (writemask2 & 2) svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) | svga->latch.b[1]; if (writemask2 & 4) svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) | svga->latch.b[2]; if (writemask2 & 8) svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) | svga->latch.b[3]; break; case 0x18: /*XOR*/ if (writemask2 & 1) svga->vram[addr] = (vala & svga->gdcreg[8]) ^ svga->latch.b[0]; if (writemask2 & 2) svga->vram[addr | 0x1] = (valb & svga->gdcreg[8]) ^ svga->latch.b[1]; if (writemask2 & 4) svga->vram[addr | 0x2] = (valc & svga->gdcreg[8]) ^ svga->latch.b[2]; if (writemask2 & 8) svga->vram[addr | 0x3] = (vald & svga->gdcreg[8]) ^ svga->latch.b[3]; 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; if (ht216->ht_regs[0xcd] & HT_REG_CD_RMWMDE) /*RMW*/ input_b = svga->vram[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); output = (fg & rop_select) | (bg & ~rop_select); svga->vram[addr] = (svga->vram[addr] & ~bit_mask) | (output & bit_mask); svga->changedvram[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; uint8_t bit_mask = 0, rop_select = 0; sub_cycles(video_timing_write_b); egawrites++; addr &= 0xfffff; val = svga_rotate[svga->gdcreg[3] & 7][val]; if (ht216->ht_regs[0xcd] & HT_REG_CD_EXALU) { /*Extended ALU*/ 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; addr &= svga->banked_mask; addr = (addr & 0x7fff) + ht216->write_bank[(addr >> 15) & 1]; 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, or; int readplane = svga->readplane; int offset; uint32_t latch_addr; if (ht216->ht_regs[0xc8] & HT_REG_C8_MOVSB) addr <<= 3; addr &= 0xfffff; sub_cycles(video_timing_read_b); egareads++; if (svga->chain4 || svga->fb_only) { 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[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]; return svga->vram[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) { 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]; } or = addr & 4; svga->latch.d[0] = ht216->bg_latch[0 | or] | (ht216->bg_latch[1 | or] << 8) | (ht216->bg_latch[2 | or] << 16) | (ht216->bg_latch[3 | or] << 24); if (svga->readmode) { temp = svga->latch.b[0]; temp ^= (svga->colourcompare & 1) ? 0xff : 0; temp &= (svga->colournocare & 1) ? 0xff : 0; temp2 = svga->latch.b[1]; temp2 ^= (svga->colourcompare & 2) ? 0xff : 0; temp2 &= (svga->colournocare & 2) ? 0xff : 0; temp3 = svga->latch.b[2]; temp3 ^= (svga->colourcompare & 4) ? 0xff : 0; temp3 &= (svga->colournocare & 4) ? 0xff : 0; temp4 = svga->latch.b[3]; temp4 ^= (svga->colourcompare & 8) ? 0xff : 0; temp4 &= (svga->colournocare & 8) ? 0xff : 0; return ~(temp | temp2 | temp3 | temp4); } return svga->vram[addr | readplane]; } static uint8_t ht216_read(uint32_t addr, void *p) { ht216_t *ht216 = (ht216_t *)p; svga_t *svga = &ht216->svga; addr &= svga->banked_mask; addr = (addr & 0x7fff) + ht216->read_bank[(addr >> 15) & 1]; return ht216_read_common(ht216, addr); } 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(const device_t *info, uint32_t mem_size, int has_rom) { ht216_t *ht216 = malloc(sizeof(ht216_t)); svga_t *svga; memset(ht216, 0, sizeof(ht216_t)); svga = &ht216->svga; 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); if (has_rom == 1) rom_init(&ht216->bios_rom, BIOS_VIDEO7_VGA_1024I_PATH, 0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); else if (has_rom == 2) rom_init(&ht216->bios_rom, BIOS_G2_GC205_PATH, 0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_v7vga); svga_init(&ht216->svga, ht216, mem_size, ht216_recalctimings, ht216_in, ht216_out, ht216_hwcursor_draw, NULL); svga->hwcursor.ysize = 32; ht216->vram_mask = mem_size - 1; svga->decode_mask = mem_size - 1; mem_mapping_set_handler(&ht216->svga.mapping, ht216_read, NULL, 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, MEM_MAPPING_EXTERNAL, &ht216->svga); svga->bpp = 8; svga->miscout = 1; ht216->ht_regs[0xb4] = 0x08; /*32-bit DRAM bus*/ ht216->id = info->local; return ht216; } static void * g2_gc205_init(const device_t *info) { ht216_t *ht216 = ht216_init(info, 1 << 19, 2); return ht216; } static void * v7_vga_1024i_init(const device_t *info) { ht216_t *ht216 = ht216_init(info, device_get_config_int("memory") << 10, 1); return ht216; } static void * ht216_pb410a_init(const device_t *info) { ht216_t *ht216 = ht216_init(info, 1 << 20, 0); return ht216; } static int g2_gc205_available(void) { return rom_present(BIOS_G2_GC205_PATH); } static int v7_vga_1024i_available(void) { return rom_present(BIOS_VIDEO7_VGA_1024I_PATH); } 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; } static const device_config_t v7_vga_1024i_config[] = { { "memory", "Memory size", CONFIG_SELECTION, "", 512, { { "256 kB", 256 }, { "512 kB", 512 }, { "" } } }, { "", "", -1 } }; const device_t g2_gc205_device = { "G2 GC205", DEVICE_ISA, 0x7070, g2_gc205_init, ht216_close, NULL, g2_gc205_available, ht216_speed_changed, ht216_force_redraw }; const device_t v7_vga_1024i_device = { "Video 7 VGA 1024i", DEVICE_ISA, 0x7140, v7_vga_1024i_init, ht216_close, NULL, v7_vga_1024i_available, ht216_speed_changed, ht216_force_redraw, v7_vga_1024i_config }; const device_t ht216_32_pb410a_device = { "Headland HT216-32 (Packard Bell PB410A)", DEVICE_ISA, 0x7861, /*HT216-32*/ ht216_pb410a_init, ht216_close, NULL, NULL, ht216_speed_changed, ht216_force_redraw };