/* Fake86: A portable, open-source 8086 PC emulator. Copyright (C)2010-2013 Mike Chambers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* video.c: many various functions to emulate bits of the video controller. a lot of this code is inefficient, and just plain ugly. i plan to rework large sections of it soon. */ #include #include #include #include "mutex.h" #include "cpu.h" extern void set_port_write_redirector (uint16_t startport, uint16_t endport, void *callback); extern void set_port_read_redirector (uint16_t startport, uint16_t endport, void *callback); extern SDL_Surface *screen; extern uint8_t verbose; extern union _bytewordregs_ regs; extern uint8_t RAM[0x100000], readonly[0x100000]; extern uint8_t portram[0x10000]; extern uint16_t segregs[4]; extern uint8_t read86 (uint32_t addr32); extern uint8_t write86 (uint32_t addr32, uint8_t value); extern uint8_t scrmodechange; uint8_t VRAM[262144], vidmode, cgabg, blankattr, vidgfxmode, vidcolor; uint16_t cursx, cursy, cols = 80, rows = 25, vgapage, cursorposition, cursorvisible; extern uint8_t portout16; uint8_t updatedscreen, clocksafe, port3da, port6; uint16_t VGA_SC[0x100], VGA_CRTC[0x100], VGA_ATTR[0x100], VGA_GC[0x100]; uint32_t videobase= 0xB8000, textbase = 0xB8000, x, y; uint8_t fontcga[32768]; uint32_t palettecga[16], palettevga[256]; uint32_t usefullscreen = 0, usegrabmode = SDL_GRAB_OFF; uint8_t latchRGB = 0, latchPal = 0, VGA_latch[4], stateDAC = 0; uint8_t latchReadRGB = 0, latchReadPal = 0; uint32_t tempRGB; uint16_t oldw, oldh; //used when restoring screen mode uint32_t rgb(uint32_t r, uint32_t g, uint32_t b) { #ifdef __BIG_ENDIAN__ return ( (r<<24) | (g<<16) | (b<<8) ); #else return (r | (g<<8) | (b<<16) ); #endif } extern uint32_t nw, nh; void vidinterrupt() { uint32_t tempcalc, memloc, n; updatedscreen = 1; switch (regs.byteregs[regah]) { //what video interrupt function? case 0: //set video mode if (verbose) { printf ("Set video mode %02Xh\n", regs.byteregs[regal]); } VGA_SC[0x4] = 0; //VGA modes are in chained mode by default after a mode switch //regs.byteregs[regal] = 3; switch (regs.byteregs[regal] & 0x7F) { case 0: //40x25 mono text videobase = textbase; cols = 40; rows = 25; vidcolor = 0; vidgfxmode = 0; blankattr = 7; for (tempcalc = videobase; tempcalc> 8; break; case 0x3C5: //sequence controller data VGA_SC[portram[0x3C4]] = value & 255; /*if (portram[0x3C4] == 2) { printf("VGA_SC[2] = %02X\n", value); }*/ break; case 0x3D4: //CRT controller index portram[0x3D4] = value & 255; //if (portout16) VGA_CRTC[value & 255] = value >> 8; break; case 0x3C7: //color index register (read operations) latchReadPal = value & 255; latchReadRGB = 0; stateDAC = 0; break; case 0x3C8: //color index register (write operations) latchPal = value & 255; latchRGB = 0; tempRGB = 0; stateDAC = 3; break; case 0x3C9: //RGB data register value = value & 63; switch (latchRGB) { #ifdef __BIG_ENDIAN__ case 0: //red tempRGB = value << 26; break; case 1: //green tempRGB |= value << 18; break; case 2: //blue tempRGB |= value << 10; palettevga[latchPal] = tempRGB; latchPal = latchPal + 1; break; #else case 0: //red tempRGB = value << 2; break; case 1: //green tempRGB |= value << 10; break; case 2: //blue tempRGB |= value << 18; palettevga[latchPal] = tempRGB; latchPal = latchPal + 1; break; #endif } latchRGB = (latchRGB + 1) % 3; break; case 0x3D5: //cursor position latch VGA_CRTC[portram[0x3D4]] = value & 255; if (portram[0x3D4]==0xE) cursorposition = (cursorposition&0xFF) | (value<<8); else if (portram[0x3D4]==0xF) cursorposition = (cursorposition&0xFF00) |value; cursy = cursorposition/cols; cursx = cursorposition%cols; if (portram[0x3D4] == 6) { vtotal = value | ( ( (uint16_t) VGA_GC[7] & 1) << 8) | ( ( (VGA_GC[7] & 32) ? 1 : 0) << 9); //printf("Vertical total: %u\n", vtotal); } break; case 0x3CF: VGA_GC[portram[0x3CE]] = value; break; default: portram[portnum] = value; } } uint8_t inVGA (uint16_t portnum) { switch (portnum) { case 0x3C1: return ( (uint8_t) VGA_ATTR[portram[0x3C0]]); case 0x3C5: return ( (uint8_t) VGA_SC[portram[0x3C4]]); case 0x3D5: return ( (uint8_t) VGA_CRTC[portram[0x3D4]]); case 0x3C7: //DAC state return (stateDAC); case 0x3C8: //palette index return (latchReadPal); case 0x3C9: //RGB data register switch (latchReadRGB++) { #ifdef __BIG_ENDIAN__ case 0: //blue return ( (palettevga[latchReadPal] >> 26) & 63); case 1: //green return ( (palettevga[latchReadPal] >> 18) & 63); case 2: //red latchReadRGB = 0; return ( (palettevga[latchReadPal++] >> 10) & 63); #else case 0: //blue return ( (palettevga[latchReadPal] >> 2) & 63); case 1: //green return ( (palettevga[latchReadPal] >> 10) & 63); case 2: //red latchReadRGB = 0; return ( (palettevga[latchReadPal++] >> 18) & 63); #endif } case 0x3DA: return (port3da); } return (portram[portnum]); //this won't be reached, but without it the compiler gives a warning } #define shiftVGA(value) {\ for (cnt=0; cnt<(VGA_GC[3] & 7); cnt++) {\ value = (value >> 1) | ((value & 1) << 7);\ }\ } #define logicVGA(curval, latchval) {\ switch ((VGA_GC[3]>>3) & 3) {\ case 1: curval &= latchval; break;\ case 2: curval |= latchval; break;\ case 3: curval ^= latchval; break;\ }\ } uint8_t lastmode = 0, tempvalue; void writeVGA (uint32_t addr32, uint8_t value) { uint32_t planesize; uint8_t curval, tempand, cnt; updatedscreen = 1; planesize = 0x10000; //if (lastmode != VGA_GC[5] & 3) printf("write mode %u\n", VGA_GC[5] & 3); //lastmode = VGA_GC[5] & 3; switch (VGA_GC[5] & 3) { //get write mode case 0: shiftVGA (value); if (VGA_SC[2] & 1) { if (VGA_GC[1] & 1) if (VGA_GC[0] & 1) curval = 255; else curval = 0; else curval = value; logicVGA (curval, VGA_latch[0]); curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[0]); VRAM[addr32] = curval; } if (VGA_SC[2] & 2) { if (VGA_GC[1] & 2) if (VGA_GC[0] & 2) curval = 255; else curval = 0; else curval = value; logicVGA (curval, VGA_latch[1]); curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[1]); VRAM[addr32+planesize] = curval; } if (VGA_SC[2] & 4) { if (VGA_GC[1] & 4) if (VGA_GC[0] & 4) curval = 255; else curval = 0; else curval = value; logicVGA (curval, VGA_latch[2]); curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[2]); VRAM[addr32+planesize*2] = curval; } if (VGA_SC[2] & 8) { if (VGA_GC[1] & 8) if (VGA_GC[0] & 8) curval = 255; else curval = 0; else curval = value; logicVGA (curval, VGA_latch[3]); curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[3]); VRAM[addr32+planesize*3] = curval; } break; case 1: if (VGA_SC[2] & 1) VRAM[addr32] = VGA_latch[0]; if (VGA_SC[2] & 2) VRAM[addr32+planesize] = VGA_latch[1]; if (VGA_SC[2] & 4) VRAM[addr32+planesize*2] = VGA_latch[2]; if (VGA_SC[2] & 8) VRAM[addr32+planesize*3] = VGA_latch[3]; break; case 2: if (VGA_SC[2] & 1) { if (VGA_GC[1] & 1) if (value & 1) curval = 255; else curval = 0; else curval = value; logicVGA (curval, VGA_latch[0]); curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[0]); VRAM[addr32] = curval; } if (VGA_SC[2] & 2) { if (VGA_GC[1] & 2) if (value & 2) curval = 255; else curval = 0; else curval = value; logicVGA (curval, VGA_latch[1]); curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[1]); VRAM[addr32+planesize] = curval; } if (VGA_SC[2] & 4) { if (VGA_GC[1] & 4) if (value & 4) curval = 255; else curval = 0; else curval = value; logicVGA (curval, VGA_latch[2]); curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[2]); VRAM[addr32+planesize*2] = curval; } if (VGA_SC[2] & 8) { if (VGA_GC[1] & 8) if (value & 8) curval = 255; else curval = 0; else curval = value; logicVGA (curval, VGA_latch[3]); curval = (~VGA_GC[8] & curval) | (VGA_GC[8] & VGA_latch[3]); VRAM[addr32+planesize*3] = curval; } break; case 3: tempand = value & VGA_GC[8]; shiftVGA (value); if (VGA_SC[2] & 1) { if (VGA_GC[0] & 1) curval = 255; else curval = 0; //logicVGA (curval, VGA_latch[0]); curval = (~tempand & curval) | (tempand & VGA_latch[0]); VRAM[addr32] = curval; } if (VGA_SC[2] & 2) { if (VGA_GC[0] & 2) curval = 255; else curval = 0; //logicVGA (curval, VGA_latch[1]); curval = (~tempand & curval) | (tempand & VGA_latch[1]); VRAM[addr32+planesize] = curval; } if (VGA_SC[2] & 4) { if (VGA_GC[0] & 4) curval = 255; else curval = 0; //logicVGA (curval, VGA_latch[2]); curval = (~tempand & curval) | (tempand & VGA_latch[2]); VRAM[addr32+planesize*2] = curval; } if (VGA_SC[2] & 8) { if (VGA_GC[0] & 8) curval = 255; else curval = 0; //logicVGA (curval, VGA_latch[3]); curval = (~tempand & curval) | (tempand & VGA_latch[3]); VRAM[addr32+planesize*3] = curval; } break; } } uint8_t readmode; uint32_t readmap; uint8_t readVGA (uint32_t addr32) { uint32_t planesize; planesize = 0x10000; VGA_latch[0] = VRAM[addr32]; VGA_latch[1] = VRAM[addr32+planesize]; VGA_latch[2] = VRAM[addr32+planesize*2]; VGA_latch[3] = VRAM[addr32+planesize*3]; if (VGA_SC[2] & 1) return (VRAM[addr32]); if (VGA_SC[2] & 2) return (VRAM[addr32+planesize]); if (VGA_SC[2] & 4) return (VRAM[addr32+planesize*2]); if (VGA_SC[2] & 8) return (VRAM[addr32+planesize*3]); return (0); //this won't be reached, but without it some compilers give a warning } void initVideoPorts() { set_port_write_redirector (0x3B0, 0x3DA, &outVGA); set_port_read_redirector (0x3B0, 0x3DA, &inVGA); }