/* JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine Release Version 2.4 A project from the Physics Dept, The University of Oxford Copyright (C) 2007-2010 The University of Oxford This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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. Details (including contact information) can be found at: jpc.sourceforge.net or the developer website sourceforge.net/projects/jpc/ Conceived and Developed by: Rhys Newman, Ian Preston, Chris Dennis End of licence header */ package org.jpc.emulator.pci.peripheral; import org.jpc.emulator.execution.codeblock.SpanningCodeBlock; import org.jpc.emulator.pci.*; import org.jpc.emulator.motherboard.*; import org.jpc.emulator.memory.*; import org.jpc.emulator.processor.Processor; import org.jpc.emulator.HardwareComponent; import java.awt.*; import java.io.*; import java.util.logging.*; import org.jpc.j2se.PCMonitor; /** * * @author Chris Dennis * @author Rhys Newman * @author Ian Preston */ public abstract class VGACard extends AbstractPCIDevice implements IODevice { private static final Logger LOGGING = Logger.getLogger(VGACard.class.getName()); //VGA_RAM_SIZE must be a power of two private static final int VGA_RAM_SIZE = 16 * 1024 * 1024; private static final int INIT_VGA_RAM_SIZE = 64 * 1024; private static final int PAGE_SHIFT = 12; private static final int[] expand4 = new int[256]; static { for (int i = 0; i < expand4.length; i++) { int v = 0; for (int j = 0; j < 8; j++) v |= ((i >>> j) & 1) << (j * 4); expand4[i] = v; } } private static final int[] expand2 = new int[256]; static { for (int i = 0; i < expand2.length; i++) { int v = 0; for (int j = 0; j < 4; j++) v |= ((i >>> (2 * j)) & 3) << (j * 4); expand2[i] = v; } } private static final int[] expand4to8 = new int[16]; static { for (int i = 0; i < expand4to8.length; i++) { int v = 0; for (int j = 0; j < 4; j++) { int b = ((i >>> j) & 1); v |= b << (2 * j); v |= b << (2 * j + 1); } expand4to8[i] = v; } } private static final int MOR_COLOR_EMULATION = 0x01; private static final int ST01_V_RETRACE = 0x08; private static final int ST01_DISP_ENABLE = 0x01; private static final int VBE_DISPI_MAX_XRES = 1600; private static final int VBE_DISPI_MAX_YRES = 1200; private static final int VBE_DISPI_INDEX_ID = 0x0; private static final int VBE_DISPI_INDEX_XRES = 0x1; private static final int VBE_DISPI_INDEX_YRES = 0x2; private static final int VBE_DISPI_INDEX_BPP = 0x3; private static final int VBE_DISPI_INDEX_ENABLE = 0x4; private static final int VBE_DISPI_INDEX_BANK = 0x5; private static final int VBE_DISPI_INDEX_VIRT_WIDTH = 0x6; private static final int VBE_DISPI_INDEX_VIRT_HEIGHT = 0x7; private static final int VBE_DISPI_INDEX_X_OFFSET = 0x8; private static final int VBE_DISPI_INDEX_Y_OFFSET = 0x9; private static final int VBE_DISPI_INDEX_NB = 0xa; private static final int VBE_DISPI_ID0 = 0xB0C0; private static final int VBE_DISPI_ID1 = 0xB0C1; private static final int VBE_DISPI_ID2 = 0xB0C2; private static final int VBE_DISPI_DISABLED = 0x00; private static final int VBE_DISPI_ENABLED = 0x01; private static final int VBE_DISPI_LFB_ENABLED = 0x40; private static final int VBE_DISPI_NOCLEARMEM = 0x80; private static final int VBE_DISPI_LFB_PHYSICAL_ADDRESS = 0xE0000000; private static final int GMODE_TEXT = 0; private static final int GMODE_GRAPH = 1; private static final int GMODE_BLANK = 2; private static final int CH_ATTR_SIZE = (160 * 100); private static final int VGA_MAX_HEIGHT = 1024; private static final int GR_INDEX_SETRESET = 0x00; private static final int GR_INDEX_ENABLE_SETRESET = 0x01; private static final int GR_INDEX_COLOR_COMPARE = 0x02; private static final int GR_INDEX_DATA_ROTATE = 0x03; private static final int GR_INDEX_READ_MAP_SELECT = 0x04; private static final int GR_INDEX_GRAPHICS_MODE = 0x05; private static final int GR_INDEX_MISC = 0x06; private static final int GR_INDEX_COLOR_DONT_CARE = 0x07; private static final int GR_INDEX_BITMASK = 0x08; private static final int SR_INDEX_RESET = 0x00; private static final int SR_INDEX_CLOCKING_MODE = 0x01; private static final int SR_INDEX_MAP_MASK = 0x02; private static final int SR_INDEX_CHAR_MAP_SELECT = 0x03; private static final int SR_INDEX_SEQ_MEMORY_MODE = 0x04; private static final int AR_INDEX_PALLETE_MIN = 0x00; private static final int AR_INDEX_PALLETE_MAX = 0x0F; private static final int AR_INDEX_ATTR_MODE_CONTROL = 0x10; private static final int AR_INDEX_OVERSCAN_COLOR = 0x11; private static final int AR_INDEX_COLOR_PLANE_ENABLE = 0x12; private static final int AR_INDEX_HORIZ_PIXEL_PANNING = 0x13; private static final int AR_INDEX_COLOR_SELECT = 0x14; private static final int CR_INDEX_HORZ_DISPLAY_END = 0x01; private static final int CR_INDEX_VERT_TOTAL = 0x06; private static final int CR_INDEX_OVERFLOW = 0x07; private static final int CR_INDEX_MAX_SCANLINE = 0x09; private static final int CR_INDEX_CURSOR_START = 0x0a; private static final int CR_INDEX_CURSOR_END = 0x0b; private static final int CR_INDEX_START_ADDR_HIGH = 0x0c; private static final int CR_INDEX_START_ADDR_LOW = 0x0d; private static final int CR_INDEX_CURSOR_LOC_HIGH = 0x0e; private static final int CR_INDEX_CURSOR_LOC_LOW = 0x0f; private static final int CR_INDEX_VERT_RETRACE_END = 0x11; private static final int CR_INDEX_VERT_DISPLAY_END = 0x12; private static final int CR_INDEX_OFFSET = 0x13; private static final int CR_INDEX_CRTC_MODE_CONTROL = 0x17; private static final int CR_INDEX_LINE_COMPARE = 0x18; private static final int[] sequencerRegisterMask = new int[]{ 0x03, //~0xfc, 0x3d, //~0xc2, 0x0f, //~0xf0, 0x3f, //~0xc0, 0x0e, //~0xf1, 0x00, //~0xff, 0x00, //~0xff, 0xff //~0x00 }; private static final int[] graphicsRegisterMask = new int[]{ 0x0f, //~0xf0 0x0f, //~0xf0 0x0f, //~0xf0 0x1f, //~0xe0 0x03, //~0xfc 0x7b, //~0x84 0x0f, //~0xf0 0x0f, //~0xf0 0xff, //~0x00 0x00, //~0xff 0x00, //~0xff 0x00, //~0xff 0x00, //~0xff 0x00, //~0xff 0x00, //~0xff 0x00 //~0xff }; private static final int[] mask16 = new int[]{ 0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff, 0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff, 0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff, 0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff }; private static final int[] dmask16 = new int[]{ 0x00000000, 0xff000000, 0x00ff0000, 0xffff0000, 0x0000ff00, 0xff00ff00, 0x00ffff00, 0xffffff00, 0x000000ff, 0xff0000ff, 0x00ff00ff, 0xffff00ff, 0x0000ffff, 0xff00ffff, 0x00ffffff, 0xffffffff }; private static final int[] dmask4 = new int[]{ 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff }; private static final int[] cursorGlyph = new int[]{ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}; private final GraphicsUpdater VGA_DRAW_LINE2; private final GraphicsUpdater VGA_DRAW_LINE2D2; private final GraphicsUpdater VGA_DRAW_LINE4; private final GraphicsUpdater VGA_DRAW_LINE4D2; private final GraphicsUpdater VGA_DRAW_LINE8D2; private final GraphicsUpdater VGA_DRAW_LINE8; private final GraphicsUpdater VGA_DRAW_LINE15; private final GraphicsUpdater VGA_DRAW_LINE16; private final GraphicsUpdater VGA_DRAW_LINE24; private final GraphicsUpdater VGA_DRAW_LINE32; private int latch; private int sequencerRegisterIndex, graphicsRegisterIndex, attributeRegisterIndex, crtRegisterIndex; private int[] sequencerRegister, graphicsRegister, attributeRegister, crtRegister; //private int[] invalidatedYTable; private boolean attributeRegisterFlipFlop; private int miscellaneousOutputRegister; private int featureControlRegister; private int st00, st01; // status 0 and 1 private int dacReadIndex, dacWriteIndex, dacSubIndex, dacState; private int shiftControl, doubleScan; private int[] dacCache; private int[] palette; private int bankOffset; private int vbeIndex; private int[] vbeRegs; private int vbeStartAddress; private int vbeLineOffset; private int vbeBankMask; private int[] fontOffset; private int graphicMode; private int lineOffset; private int lineCompare; private int startAddress; private int planeUpdated; private int lastCW, lastCH; private int lastWidth, lastHeight; private int lastScreenWidth, lastScreenHeight; private int cursorStart, cursorEnd; private int cursorOffset; private final int[] lastPalette; private int[] lastChar; private boolean ioportRegistered; private boolean pciRegistered; private boolean memoryRegistered; private boolean updatingScreen; private VGARAMIORegion ioRegion; private VGALowMemoryRegion lowIORegion; public VGACard() { ioportRegistered = false; memoryRegistered = false; pciRegistered = false; VGA_DRAW_LINE2 = new DrawLine2(); VGA_DRAW_LINE2D2 = new DrawLine2d2(); VGA_DRAW_LINE4 = new DrawLine4(); VGA_DRAW_LINE4D2 = new DrawLine4d2(); VGA_DRAW_LINE8D2 = new DrawLine8d2(); VGA_DRAW_LINE8 = new DrawLine8(); VGA_DRAW_LINE15 = new DrawLine15(); VGA_DRAW_LINE16 = new DrawLine16(); VGA_DRAW_LINE24 = new DrawLine24(); VGA_DRAW_LINE32 = new DrawLine32(); assignDeviceFunctionNumber(-1); setIRQIndex(16); putConfigWord(PCI_CONFIG_VENDOR_ID, (short)0x1234); // Dummy putConfigWord(PCI_CONFIG_DEVICE_ID, (short)0x1111); putConfigWord(PCI_CONFIG_CLASS_DEVICE, (short)0x0300); // VGA Controller putConfigByte(PCI_CONFIG_HEADER, (byte)0x00); // header_type ioRegion = new VGARAMIORegion(); lowIORegion = new VGALowMemoryRegion(); lastPalette = new int[256]; this.internalReset(); bankOffset = 0; vbeRegs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0; vbeBankMask = ((VGA_RAM_SIZE >>> 16) - 1); vbeRegs[VBE_DISPI_INDEX_XRES] = 1600; vbeRegs[VBE_DISPI_INDEX_YRES] = 1200; vbeRegs[VBE_DISPI_INDEX_BPP] = 32; } public String getText() { StringBuilder b = new StringBuilder(); for (int i=0; i < lastChar.length/80; i++) { StringBuilder row = new StringBuilder(); for (int j=0; j < 80; j++) row.append((char)lastChar[j+80*i]); b.append(row.toString().trim()+"\n"); } return b.toString(); } public void saveState(DataOutput output) throws IOException { super.saveState(output); output.writeInt(expand4to8.length); for (int i = 0; i< expand4to8.length; i++) output.writeInt(expand4to8[i]); output.writeInt(latch); output.writeInt(sequencerRegisterIndex); output.writeInt(graphicsRegisterIndex); output.writeInt(attributeRegisterIndex); output.writeInt(crtRegisterIndex); output.writeInt(sequencerRegister.length); for (int i = 0; i< sequencerRegister.length; i++) output.writeInt(sequencerRegister[i]); output.writeInt(graphicsRegister.length); for (int i = 0; i< graphicsRegister.length; i++) output.writeInt(graphicsRegister[i]); output.writeInt(attributeRegister.length); for (int i = 0; i< attributeRegister.length; i++) output.writeInt(attributeRegister[i]); output.writeInt(crtRegister.length); for (int i = 0; i< crtRegister.length; i++) output.writeInt(crtRegister[i]); output.writeBoolean(attributeRegisterFlipFlop); output.writeInt(miscellaneousOutputRegister); output.writeInt(featureControlRegister); output.writeInt(st00); output.writeInt(st01); output.writeInt(dacReadIndex); output.writeInt(dacWriteIndex); output.writeInt(dacSubIndex); output.writeInt(dacState); output.writeInt(shiftControl); output.writeInt(doubleScan); output.writeInt(dacCache.length); for (int i = 0; i< dacCache.length; i++) output.writeInt(dacCache[i]); output.writeInt(palette.length); for (int i = 0; i< palette.length; i++) output.writeInt(palette[i]); output.writeInt(bankOffset); output.writeInt(vbeIndex); output.writeInt(vbeRegs.length); for (int i = 0; i< vbeRegs.length; i++) output.writeInt(vbeRegs[i]); output.writeInt(vbeStartAddress); output.writeInt(vbeLineOffset); output.writeInt(vbeBankMask); output.writeInt(fontOffset.length); for (int i = 0; i< fontOffset.length; i++) output.writeInt(fontOffset[i]); output.writeInt(graphicMode); output.writeInt(lineOffset); output.writeInt(lineCompare); output.writeInt(startAddress); output.writeInt(planeUpdated); output.writeInt(lastCW); output.writeInt(lastCH); output.writeInt(lastWidth); output.writeInt(lastHeight); output.writeInt(lastScreenWidth); output.writeInt(lastScreenHeight); output.writeInt(cursorStart); output.writeInt(cursorEnd); output.writeInt(cursorOffset); output.writeInt(lastPalette.length); for (int i = 0; i< lastPalette.length; i++) output.writeInt(lastPalette[i]); output.writeInt(lastChar.length); for (int i = 0; i< lastChar.length; i++) output.writeInt(lastChar[i]); output.writeBoolean(updatingScreen); //dump ioregion ioRegion.dumpState(output); } public void loadState(DataInput input) throws IOException { super.loadState(input); ioportRegistered =false; pciRegistered = false; memoryRegistered = false; int len = input.readInt(); for (int i = 0; i< expand4to8.length; i++) expand4to8[i] = input.readInt(); latch = input.readInt(); sequencerRegisterIndex = input.readInt(); graphicsRegisterIndex = input.readInt(); attributeRegisterIndex = input.readInt(); crtRegisterIndex = input.readInt(); len = input.readInt(); sequencerRegister = new int[len]; for (int i = 0; i< sequencerRegister.length; i++) sequencerRegister[i] = input.readInt(); len = input.readInt(); graphicsRegister = new int[len]; for (int i = 0; i< graphicsRegister.length; i++) graphicsRegister[i] = input.readInt(); len = input.readInt(); attributeRegister = new int[len]; for (int i = 0; i< attributeRegister.length; i++) attributeRegister[i] = input.readInt(); len = input.readInt(); crtRegister = new int[len]; for (int i = 0; i< crtRegister.length; i++) crtRegister[i] = input.readInt(); attributeRegisterFlipFlop = input.readBoolean(); miscellaneousOutputRegister = input.readInt(); featureControlRegister = input.readInt(); st00 = input.readInt(); st01 = input.readInt(); dacReadIndex = input.readInt(); dacWriteIndex = input.readInt(); dacSubIndex = input.readInt(); dacState = input.readInt(); shiftControl = input.readInt(); doubleScan = input.readInt(); len = input.readInt(); dacCache = new int[len]; for (int i = 0; i< dacCache.length; i++) dacCache[i] = input.readInt(); len = input.readInt(); palette = new int[len]; for (int i = 0; i< palette.length; i++) palette[i] = input.readInt(); bankOffset = input.readInt(); vbeIndex = input.readInt(); len = input.readInt(); vbeRegs = new int[len]; for (int i = 0; i< vbeRegs.length; i++) vbeRegs[i] = input.readInt(); vbeStartAddress = input.readInt(); vbeLineOffset = input.readInt(); vbeBankMask = input.readInt(); len = input.readInt(); fontOffset = new int[len]; for (int i = 0; i< fontOffset.length; i++) fontOffset[i] = input.readInt(); graphicMode = input.readInt(); lineOffset = input.readInt(); lineCompare = input.readInt(); startAddress = input.readInt(); planeUpdated = input.readInt(); lastCW = input.readInt(); lastCH = input.readInt(); lastWidth = input.readInt(); lastHeight = input.readInt(); lastScreenWidth = input.readInt(); lastScreenHeight = input.readInt(); cursorStart = input.readInt(); cursorEnd = input.readInt(); cursorOffset = input.readInt(); len = input.readInt(); for (int i = 0; i< lastPalette.length; i++) lastPalette[i] = input.readInt(); len = input.readInt(); lastChar = new int[len]; for (int i = 0; i< lastChar.length; i++) lastChar[i] = input.readInt(); updatingScreen = input.readBoolean(); //load ioregion ioRegion.loadState(input); // lowIORegion = new VGALowMemoryRegion(); } public abstract void saveScreenshot(); /** * Convert the given RGB values into this objects packed pixel format. * @param r red value (0..255) * @param g green value (0..255) * @param b blue value (0..255) * @return packed pixel value */ protected abstract int rgbToPixel(int r, int g, int b); /** * Returns the buffer to which raster information should be copied * @return target for monitor information */ protected abstract int[] getDisplayBuffer(); /** * Marks the given monitor raster region as dirtied. * @param x region left edge * @param y region top edge * @param w region width * @param h region height */ protected abstract void dirtyDisplayRegion(int x, int y, int w, int h); /** * Called to indicate the monitor raster has changed size. * @param w new raster width * @param h new raster height */ public abstract void resizeDisplay(int w, int h); /** * Returns the preferred size of the display */ public abstract Dimension getDisplaySize(); public void dirtyScreen() { Dimension size = getDisplaySize(); dirtyDisplayRegion(0, 0, size.width, size.height); } public void setOriginalDisplaySize() { resizeDisplay(lastScreenWidth, lastScreenHeight); } //PCIDevice Methods public IORegion[] getIORegions() { return new IORegion[]{ioRegion}; } public IORegion getIORegion(int index) { if(index == 0) return ioRegion; else return null; } //IODevice Methods public void ioPortWrite8(int address, int data) { //all byte accesses are vgaIOPort ones vgaIOPortWriteByte(address, data); } public void ioPortWrite16(int address, int data) { switch(address) { case 0x1ce: case 0xff80: vbeIOPortWriteIndex(data); break; case 0x1cf: case 0xff81: vbeIOPortWriteData(data); break; default: ioPortWrite8(address, 0xFF & data); ioPortWrite8(address + 1, 0xFF & (data >>> 8)); break; } } public void ioPortWrite32(int address, int data) { ioPortWrite16(address, 0xFFFF & data); ioPortWrite16(address + 2, data >>> 16); } public int ioPortRead8(int address) { //all byte accesses are vgaIOPort ones return vgaIOPortReadByte(address); } public int ioPortRead16(int address) { switch(address) { case 0x1ce: case 0xff80: return vbeIOPortReadIndex(); case 0x1cf: case 0xff81: return vbeIOPortReadData(); default: int b0 = 0xFF & ioPortRead8(address); int b1 = 0xFF & ioPortRead8(address + 1); return b0 | (b1 << 8); } } public int ioPortRead32(int address) { int b0 = 0xFFFF & ioPortRead16(address); int b1 = 0xFFFF & ioPortRead16(address + 2); return b0 | (b1 << 16); } public int[] ioPortsRequested() { return new int[]{0x3b4, 0x3b5, 0x3ba, 0x3d4, 0x3d5, 0x3da, 0x3c0, 0x3c1, 0x3c2, 0x3c3, 0x3c4, 0x3c5, 0x3c6, 0x3c7, 0x3c8, 0x3c9, 0x3ca, 0x3cb, 0x3cc, 0x3cd, 0x3ce, 0x3cf, 0x1ce, 0x1cf, 0xff80, 0xff81 }; } private final void vgaIOPortWriteByte(int address, int data) { if ((address >= 0x3b0 && address <= 0x3bf && ((miscellaneousOutputRegister & MOR_COLOR_EMULATION) != 0)) || (address >= 0x3d0 && address <= 0x3df && ((miscellaneousOutputRegister & MOR_COLOR_EMULATION) == 0))) return; if ((data & ~0xff) != 0) LOGGING.log(Level.WARNING, "possible int/byte register problem"); switch(address) { case 0x3b4: case 0x3d4: crtRegisterIndex = data; break; case 0x3b5: case 0x3d5: if (crtRegisterIndex <= 7 && (crtRegister[CR_INDEX_VERT_RETRACE_END] & 0x80) != 0) { /* can always write bit 4 of CR_INDEX_OVERFLOW */ if (crtRegisterIndex == CR_INDEX_OVERFLOW) crtRegister[CR_INDEX_OVERFLOW] = (crtRegister[CR_INDEX_OVERFLOW] & ~0x10) | (data & 0x10); return; } crtRegister[crtRegisterIndex] = data; break; case 0x3ba: case 0x3da: featureControlRegister = data & 0x10; break; case 0x3c0: if (!attributeRegisterFlipFlop) { data &= 0x3f; attributeRegisterIndex = data; } else { int index = attributeRegisterIndex & 0x1f; switch(index) { case AR_INDEX_PALLETE_MIN: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case AR_INDEX_PALLETE_MAX: attributeRegister[index] = data & 0x3f; break; case AR_INDEX_ATTR_MODE_CONTROL: attributeRegister[AR_INDEX_ATTR_MODE_CONTROL] = data & ~0x10; break; case AR_INDEX_OVERSCAN_COLOR: attributeRegister[AR_INDEX_OVERSCAN_COLOR] = data; break; case AR_INDEX_COLOR_PLANE_ENABLE: attributeRegister[AR_INDEX_COLOR_PLANE_ENABLE] = data & ~0xc0; break; case AR_INDEX_HORIZ_PIXEL_PANNING: attributeRegister[AR_INDEX_HORIZ_PIXEL_PANNING] = data & ~0xf0; break; case AR_INDEX_COLOR_SELECT: attributeRegister[AR_INDEX_COLOR_SELECT] = data & ~0xf0; break; default: break; } } attributeRegisterFlipFlop = !attributeRegisterFlipFlop; break; case 0x3c2: miscellaneousOutputRegister = data & ~0x10; break; case 0x3c4: sequencerRegisterIndex = data & 0x7; break; case 0x3c5: sequencerRegister[sequencerRegisterIndex] = data & sequencerRegisterMask[sequencerRegisterIndex]; break; case 0x3c7: dacReadIndex = data; dacSubIndex = 0; dacState = 3; break; case 0x3c8: dacWriteIndex = data; dacSubIndex = 0; dacState = 0; break; case 0x3c9: dacCache[dacSubIndex] = data; if (++dacSubIndex == 3) { for (int i = 0; i < 3; i++) palette[((0xff & dacWriteIndex) * 3) + i] = dacCache[i]; dacSubIndex = 0; dacWriteIndex++; } break; case 0x3ce: graphicsRegisterIndex = data & 0x0f; break; case 0x3cf: graphicsRegister[graphicsRegisterIndex] = data & graphicsRegisterMask[graphicsRegisterIndex]; break; } } private final int vgaIOPortReadByte(int address) { if ((address >= 0x3b0 && address <= 0x3bf && ((miscellaneousOutputRegister & MOR_COLOR_EMULATION) != 0)) || (address >= 0x3d0 && address <= 0x3df && ((miscellaneousOutputRegister & MOR_COLOR_EMULATION) == 0))) return 0xff; switch (address) { case 0x3c0: if (!attributeRegisterFlipFlop) { return attributeRegisterIndex; } else { return 0; } case 0x3c1: int index = attributeRegisterIndex & 0x1f; if (index < 21) { return attributeRegister[index]; } else { return 0; } case 0x3c2: return st00; case 0x3c4: return sequencerRegisterIndex; case 0x3c5: return sequencerRegister[sequencerRegisterIndex]; case 0x3c7: return dacState; case 0x3c8: return dacWriteIndex; case 0x3c9: int val = palette[dacReadIndex * 3 + dacSubIndex]; if (++dacSubIndex == 3) { dacSubIndex = 0; dacReadIndex++; } return val; case 0x3ca: return featureControlRegister; case 0x3cc: return miscellaneousOutputRegister; case 0x3ce: return graphicsRegisterIndex; case 0x3cf: return graphicsRegister[graphicsRegisterIndex]; case 0x3b4: case 0x3d4: return crtRegisterIndex; case 0x3b5: case 0x3d5: return crtRegister[crtRegisterIndex]; case 0x3ba: case 0x3da: attributeRegisterFlipFlop = false; if (updatingScreen) { st01 &= ~ST01_V_RETRACE; //claim we are not in vertical retrace (in the process of screen refresh) st01 &= ~ST01_DISP_ENABLE; //is set when in h/v retrace (i.e. if e-beam is off, but we claim always on) } else { st01 ^= (ST01_V_RETRACE | ST01_DISP_ENABLE); //if not updating toggle to fool polling in some vga code } return st01; default: return 0x00; } } private final void vbeIOPortWriteIndex(int data) { vbeIndex = data; } private final void vbeIOPortWriteData(int data) { if (vbeIndex <= VBE_DISPI_INDEX_NB) { switch(vbeIndex) { case VBE_DISPI_INDEX_ID: if (data == VBE_DISPI_ID0 || data == VBE_DISPI_ID1 || data == VBE_DISPI_ID2) vbeRegs[vbeIndex] = data; break; case VBE_DISPI_INDEX_XRES: if ((data <= VBE_DISPI_MAX_XRES) && ((data & 7) == 0)) vbeRegs[vbeIndex] = data; break; case VBE_DISPI_INDEX_YRES: if (data <= VBE_DISPI_MAX_YRES) vbeRegs[vbeIndex] = data; break; case VBE_DISPI_INDEX_BPP: if (data == 0) data = 8; if (data == 4 || data == 8 || data == 15 || data == 16 || data == 24 || data == 32) { vbeRegs[vbeIndex] = data; } break; case VBE_DISPI_INDEX_BANK: data &= vbeBankMask; vbeRegs[vbeIndex] = data; bankOffset = data << 16; break; case VBE_DISPI_INDEX_ENABLE: if ((data & VBE_DISPI_ENABLED) != 0) { vbeRegs[VBE_DISPI_INDEX_VIRT_WIDTH] = vbeRegs[VBE_DISPI_INDEX_XRES]; vbeRegs[VBE_DISPI_INDEX_VIRT_HEIGHT] = vbeRegs[VBE_DISPI_INDEX_YRES]; vbeRegs[VBE_DISPI_INDEX_X_OFFSET] = 0; vbeRegs[VBE_DISPI_INDEX_Y_OFFSET] = 0; if (vbeRegs[VBE_DISPI_INDEX_BPP] == 4) vbeLineOffset = vbeRegs[VBE_DISPI_INDEX_XRES] >>> 1; else vbeLineOffset = vbeRegs[VBE_DISPI_INDEX_XRES] * ((vbeRegs[VBE_DISPI_INDEX_BPP] + 7) >>> 3); vbeStartAddress = 0; /* clear the screen (should be done in BIOS) */ if ((data & VBE_DISPI_NOCLEARMEM) == 0) { int limit = vbeRegs[VBE_DISPI_INDEX_YRES] * vbeLineOffset; for (int i=0; i<limit; i++) ioRegion.setByte(i, (byte) 0); } /* we initialise the VGA graphic mode */ /* (should be done in BIOS) */ /* graphic mode + memory map 1 */ graphicsRegister[GR_INDEX_MISC] = (graphicsRegister[GR_INDEX_MISC] & ~0x0c) | 0x05; crtRegister[CR_INDEX_CRTC_MODE_CONTROL] |= 0x3; /* no CGA modes */ crtRegister[CR_INDEX_OFFSET] = (vbeLineOffset >>> 3); /* width */ crtRegister[CR_INDEX_HORZ_DISPLAY_END] = (vbeRegs[VBE_DISPI_INDEX_XRES] >>> 3) - 1; /* height */ int h = vbeRegs[VBE_DISPI_INDEX_YRES] - 1; crtRegister[CR_INDEX_VERT_DISPLAY_END] = h; crtRegister[CR_INDEX_OVERFLOW] = (crtRegister[CR_INDEX_OVERFLOW] & ~0x42) | ((h >>> 7) & 0x02) | ((h >>> 3) & 0x40); /* line compare to 1023 */ crtRegister[CR_INDEX_LINE_COMPARE] = 0xff; crtRegister[CR_INDEX_OVERFLOW] |= 0x10; crtRegister[CR_INDEX_MAX_SCANLINE] |= 0x40; int shiftControl; if (vbeRegs[VBE_DISPI_INDEX_BPP] == 4) { shiftControl = 0; sequencerRegister[SR_INDEX_CLOCKING_MODE] &= ~0x8; /* no double line */ } else { shiftControl = 2; sequencerRegister[SR_INDEX_SEQ_MEMORY_MODE] |= 0x08; /* set chain 4 mode */ sequencerRegister[SR_INDEX_MAP_MASK] |= 0x0f; /* activate all planes */ } graphicsRegister[GR_INDEX_GRAPHICS_MODE] = (graphicsRegister[GR_INDEX_GRAPHICS_MODE] & ~0x60) | (shiftControl << 5); crtRegister[CR_INDEX_MAX_SCANLINE] &= ~0x9f; /* no double scan */ } else { /* XXX: the bios should do that */ bankOffset = 0; } vbeRegs[vbeIndex] = data; break; case VBE_DISPI_INDEX_VIRT_WIDTH: { if (data < vbeRegs[VBE_DISPI_INDEX_XRES]) return; int w = data; int lineOffset; if (vbeRegs[VBE_DISPI_INDEX_BPP] == 4) { lineOffset = data >>> 1; } else { lineOffset = data * ((vbeRegs[VBE_DISPI_INDEX_BPP] + 7) >>> 3); } int h = VGA_RAM_SIZE / lineOffset; /* XXX: support wierd bochs semantics ? */ if (h < vbeRegs[VBE_DISPI_INDEX_YRES]) return; vbeRegs[VBE_DISPI_INDEX_VIRT_WIDTH] = w; vbeRegs[VBE_DISPI_INDEX_VIRT_HEIGHT] = h; vbeLineOffset = lineOffset; } break; case VBE_DISPI_INDEX_X_OFFSET: case VBE_DISPI_INDEX_Y_OFFSET: { vbeRegs[vbeIndex] = data; vbeStartAddress = vbeLineOffset * vbeRegs[VBE_DISPI_INDEX_Y_OFFSET]; int x = vbeRegs[VBE_DISPI_INDEX_X_OFFSET]; if (vbeRegs[VBE_DISPI_INDEX_BPP] == 4) { vbeStartAddress += x >>> 1; } else { vbeStartAddress += x * ((vbeRegs[VBE_DISPI_INDEX_BPP] + 7) >>> 3); } vbeStartAddress >>>= 2; } break; default: System.out.println("Invalid VBE write mode: vbeIndex=" + vbeIndex); break; } } } private final int vbeIOPortReadIndex() { return vbeIndex; } private final int vbeIOPortReadData() { if (vbeIndex <= VBE_DISPI_INDEX_NB) { return vbeRegs[vbeIndex]; } else { return 0; } } private final void internalReset() { latch = 0; sequencerRegisterIndex = graphicsRegisterIndex = attributeRegisterIndex = crtRegisterIndex = 0; attributeRegisterFlipFlop = false; miscellaneousOutputRegister = 0; featureControlRegister = 0; st00 = st01 = 0; // status 0 and 1 dacState = dacSubIndex = dacReadIndex = dacWriteIndex = 0; shiftControl = doubleScan = 0; bankOffset = 0; vbeIndex = 0; vbeStartAddress = 0; vbeLineOffset = 0; vbeBankMask = 0; graphicMode = 0; lineOffset = 0; lineCompare = 0; startAddress = 0; planeUpdated = 0; lastCW = lastCH = 0; lastWidth = lastHeight = 0; lastScreenWidth = lastScreenHeight = 0; cursorStart = cursorEnd = 0; cursorOffset = 0; // for (int i=0; i<lastPalette.length; i++) // lastPalette[i] = new int[256]; //invalidatedYTable = new int[VGA_MAX_HEIGHT / 32]; lastChar = new int[CH_ATTR_SIZE]; fontOffset = new int[2]; vbeRegs = new int[VBE_DISPI_INDEX_NB+1]; dacCache = new int[3]; palette = new int[768]; sequencerRegister = new int[256]; graphicsRegister = new int[256]; attributeRegister = new int[256]; crtRegister = new int[256]; graphicMode = -1; } public class VGALowMemoryRegion implements Memory { public void lock(int addr) {} public void unlock(int addr) {} public void addSpanningBlock(SpanningCodeBlock span, int lengthRemaining) { throw new IllegalStateException("add spanning block: Invalid Operation for VGA Card"); } public void copyContentsIntoArray(int address, byte[] buffer, int off, int len) { throw new IllegalStateException("copyContentsInto: Invalid Operation for VGA Card"); } public void copyArrayIntoContents(int address, byte[] buffer, int off, int len) { throw new IllegalStateException("copyContentsFrom: Invalid Operation for VGA Card"); } public long getSize() { return 0x20000; } public boolean isAllocated() { return false; } public byte getByte(int offset) { /* convert to VGA memory offset */ int memoryMapMode = (graphicsRegister[GR_INDEX_MISC] >>> 2) & 3; offset &= 0x1ffff; switch (memoryMapMode) { case 0: break; case 1: if (offset >= 0x10000) return (byte) 0xff; offset += bankOffset; break; case 2: offset -= 0x10000; if ((offset >= 0x8000) || (offset < 0)) return (byte) 0xff; break; default: case 3: offset -= 0x18000; if (offset < 0) return (byte) 0xff; break; } if ((sequencerRegister[SR_INDEX_SEQ_MEMORY_MODE] & 0x08) != 0) { /* chain 4 mode : simplest access */ //return vramPtr[address]; return ioRegion.getByte(offset); } else if ((graphicsRegister[GR_INDEX_GRAPHICS_MODE] & 0x10) != 0) { /* odd/even mode (aka text mode mapping) */ int plane = (graphicsRegister[GR_INDEX_READ_MAP_SELECT] & 2) | (offset & 1); return ioRegion.getByte(((offset & ~1) << 1) | plane); } else { /* standard VGA latched access */ latch = ioRegion.getDoubleWord(4 * offset); if ((graphicsRegister[GR_INDEX_GRAPHICS_MODE] & 0x08) == 0) { /* read mode 0 */ return (byte)(latch >>> (graphicsRegister[GR_INDEX_READ_MAP_SELECT] * 8)); } else { /* read mode 1 */ int ret = (latch ^ mask16[graphicsRegister[GR_INDEX_COLOR_COMPARE]]) & mask16[graphicsRegister[GR_INDEX_COLOR_DONT_CARE]]; ret |= ret >>> 16; ret |= ret >>> 8; return (byte)(~ret); } } } public short getWord(int offset) { int v = 0xFF & getByte(offset); v |= getByte(offset + 1) << 8; return (short) v; } public int getDoubleWord(int offset) { int v = 0xFF & getByte(offset); v |= (0xFF & getByte(offset + 1)) << 8; v |= (0xFF & getByte(offset + 2)) << 16; v |= (0xFF & getByte(offset + 3)) << 24; return v; } public long getQuadWord(int offset) { long v = 0xFFl & getByte(offset); v |= (0xFFl & getByte(offset + 1)) << 8; v |= (0xFFl & getByte(offset + 2)) << 16; v |= (0xFFl & getByte(offset + 3)) << 24; v |= (0xFFl & getByte(offset + 4)) << 32; v |= (0xFFl & getByte(offset + 5)) << 40; v |= (0xFFl & getByte(offset + 6)) << 48; v |= (0xFFl & getByte(offset + 7)) << 56; return v; } public long getLowerDoubleQuadWord(int offset) { return getQuadWord(offset); } public long getUpperDoubleQuadWord(int offset) { return getQuadWord(offset+8); } public void setByte(int offset, byte data) { /* convert to VGA memory offset */ int memoryMapMode = (graphicsRegister[GR_INDEX_MISC] >>> 2) & 3; offset &= 0x1ffff; switch (memoryMapMode) { case 0: break; case 1: if (offset >= 0x10000) return; offset += bankOffset; break; case 2: offset -= 0x10000; if ((offset >= 0x8000) || (offset < 0)) return; break; default: case 3: offset -= 0x18000; //should be (unsigned) if (offset >= 0x8000) but anding above "offset &= 0x1ffff;" means <=> the below if (offset < 0) return; break; } if ((sequencerRegister[SR_INDEX_SEQ_MEMORY_MODE] & 0x08) != 0) { /* chain 4 mode : simplest access */ int plane = offset & 3; int mask = 1 << plane; if ((sequencerRegister[SR_INDEX_MAP_MASK] & mask) != 0) { ioRegion.setByte(offset, data); planeUpdated |= mask; // only used to detect font change //cpu_physical_memory_set_dirty } } else if ((graphicsRegister[GR_INDEX_GRAPHICS_MODE] & 0x10) != 0) { /* odd/even mode (aka text mode mapping) */ int plane = (graphicsRegister[GR_INDEX_READ_MAP_SELECT] & 2) | (offset & 1); int mask = 1 << plane; if ((sequencerRegister[SR_INDEX_MAP_MASK] & mask) != 0) { ioRegion.setByte(((offset & ~1) << 1) | plane, data); planeUpdated |= mask; // only used to detect font change //cpu_physical_memory_set_dirty } } else { /* standard VGA latched access */ int bitMask = 0; int writeMode = graphicsRegister[GR_INDEX_GRAPHICS_MODE] & 3; int intData = 0xff & data; switch (writeMode) { default: case 0: /* rotate */ int b = graphicsRegister[GR_INDEX_DATA_ROTATE] & 7; intData |= intData << 8; intData |= intData << 16; intData = (intData >>> b) | (intData << -b); //Integer.rotateRight(intData, b); /* apply set/reset mask */ int setMask = mask16[graphicsRegister[GR_INDEX_ENABLE_SETRESET]]; intData = (intData & ~setMask) | (mask16[graphicsRegister[GR_INDEX_SETRESET]] & setMask); bitMask = graphicsRegister[GR_INDEX_BITMASK]; break; case 1: intData = latch; int mask = sequencerRegister[SR_INDEX_MAP_MASK]; planeUpdated |= mask; // only used to detect font change int writeMask = mask16[mask]; //check address being used here; offset <<= 2; ioRegion.setDoubleWord(offset, (ioRegion.getDoubleWord(offset) & ~writeMask) | (intData & writeMask)); return; case 2: intData = mask16[intData & 0x0f]; bitMask = graphicsRegister[GR_INDEX_BITMASK]; break; case 3: /* rotate */ b = graphicsRegister[GR_INDEX_DATA_ROTATE] & 7; intData = ((intData >>> b) | (intData << (8-b))); bitMask = graphicsRegister[GR_INDEX_BITMASK] & intData; intData = mask16[graphicsRegister[GR_INDEX_SETRESET]]; break; } /* apply logical operation */ int funcSelect = graphicsRegister[GR_INDEX_DATA_ROTATE] >>> 3; switch (funcSelect) { default: case 0: /* nothing to do */ break; case 1: /* and */ intData &= latch; break; case 2: /* or */ intData |= latch; break; case 3: /* xor */ intData ^= latch; break; } /* apply bit mask */ bitMask |= bitMask << 8; bitMask |= bitMask << 16; intData = (intData & bitMask) | (latch & ~bitMask); /* mask data according to sequencerRegister[SR_INDEX_MAP_MASK] */ int mask = sequencerRegister[SR_INDEX_MAP_MASK]; planeUpdated |= mask; // only used to detect font change int writeMask = mask16[mask]; offset <<= 2; //check address being used here; ioRegion.setDoubleWord(offset, (ioRegion.getDoubleWord(offset) & ~writeMask) | (intData & writeMask)); } } public void setWord(int offset, short data) { setByte(offset++, (byte)data); data >>>= 8; setByte(offset, (byte)data); } public void setDoubleWord(int offset, int data) { setByte(offset++, (byte)data); data >>>= 8; setByte(offset++, (byte)data); data >>>= 8; setByte(offset++, (byte)data); data >>>= 8; setByte(offset, (byte)data); } public void setQuadWord(int offset, long data) { setDoubleWord(offset, (int) data); setDoubleWord(offset+4, (int) (data >> 32)); } public void setLowerDoubleQuadWord(int offset, long data) { setDoubleWord(offset, (int) data); setDoubleWord(offset+4, (int) (data >> 32)); } public void setUpperDoubleQuadWord(int offset, long data) { offset += 8; setDoubleWord(offset, (int) data); setDoubleWord(offset+4, (int) (data >> 32)); } public void clear() { internalReset(); } public void clear(int start, int length) { clear(); } public int executeReal(Processor cpu, int offset) { throw new IllegalStateException("Invalid Operation"); } public int executeProtected(Processor cpu, int offset) { throw new IllegalStateException("Invalid Operation"); } public int executeVirtual8086(Processor cpu, int offset) { throw new IllegalStateException("Invalid Operation"); } public void loadInitialContents(int address, byte[] buf, int off, int len) { throw new UnsupportedOperationException("Not supported yet."); } } public static class VGARAMIORegion extends MemoryMappedIORegion { private byte[] buffer; private int startAddress; private boolean[] dirtyPages; public VGARAMIORegion() { // buffer = new byte[VGA_RAM_SIZE]; buffer = new byte[INIT_VGA_RAM_SIZE]; dirtyPages = new boolean[(VGA_RAM_SIZE >>> PAGE_SHIFT) + 1]; for (int i = 0; i < dirtyPages.length; i++) dirtyPages[i] = false; startAddress = -1; } public void dumpState(DataOutput output) throws IOException { output.writeInt(startAddress); output.writeInt(buffer.length); output.write(buffer); output.writeInt(dirtyPages.length); for (int i=0; i< dirtyPages.length; i++) output.writeBoolean(dirtyPages[i]); } public void loadState(DataInput input) throws IOException { startAddress = input.readInt(); int len = input.readInt(); buffer = new byte[len]; input.readFully(buffer,0,len); len = input.readInt(); dirtyPages = new boolean[len]; for (int i = 0; i < len; i++) dirtyPages[i] = input.readBoolean(); } private void increaseVGARAMSize(int offset) { if ((offset < 0) || (offset >= VGA_RAM_SIZE)) throw new ArrayIndexOutOfBoundsException("tried to access outside of memory bounds"); int newSize = buffer.length; while (newSize <= offset) newSize = newSize << 1; if (newSize > VGA_RAM_SIZE) newSize = VGA_RAM_SIZE; byte[] newBuf = new byte[newSize]; System.arraycopy(buffer, 0, newBuf, 0, buffer.length); buffer = newBuf; } public void copyContentsIntoArray(int address, byte[] buf, int off, int len) { System.arraycopy(buffer, address, buf, off, len); } public void copyArrayIntoContents(int address, byte[] buf, int off, int len) { System.arraycopy(buf, off, buffer, address, len); } public void clear() { for (int i = 0; i < buffer.length; i++) buffer[i] = 0; for (int i = 0; i < dirtyPages.length; i++) dirtyPages[i] = false; } public void clear(int start, int length) { int limit = start + length; if (limit > getSize()) throw new ArrayIndexOutOfBoundsException("Attempt to clear outside of memory bounds"); try { for (int i = start; i < limit; i++) buffer[i] = 0; } catch (ArrayIndexOutOfBoundsException e) {} int pageStart = start >>> PAGE_SHIFT; int pageLimit = (limit - 1) >>> PAGE_SHIFT; for (int i = pageStart; i <= pageLimit; i++) dirtyPages[i] = true; } public boolean pageIsDirty(int i) { return dirtyPages[i]; } public void cleanPage(int i) { dirtyPages[i] = false; } //IORegion Methods public int getAddress() { return startAddress; } public long getSize() { return VGA_RAM_SIZE; } public int getType() { return PCI_ADDRESS_SPACE_MEM_PREFETCH; } public int getRegionNumber() { return 0; } public void setAddress(int address) { this.startAddress = address; } public void setByte(int offset, byte data) { try { dirtyPages[offset >>> PAGE_SHIFT] = true; buffer[offset] = data; } catch (ArrayIndexOutOfBoundsException e) { increaseVGARAMSize(offset); setByte(offset, data); } } public byte getByte(int offset) { try { return buffer[offset]; } catch (ArrayIndexOutOfBoundsException e) { increaseVGARAMSize(offset); return getByte(offset); } } public void setWord(int offset, short data) { try { buffer[offset] = (byte) data; dirtyPages[offset >>> PAGE_SHIFT] = true; offset++; buffer[offset] = (byte) (data >> 8); dirtyPages[offset >>> PAGE_SHIFT] = true; } catch (ArrayIndexOutOfBoundsException e) { increaseVGARAMSize(offset); setWord(offset, data); } } public short getWord(int offset) { try { int result = 0xFF & buffer[offset]; offset++; result |= buffer[offset] << 8; return (short) result; } catch (ArrayIndexOutOfBoundsException e) { increaseVGARAMSize(offset); return getWord(offset); } } public void setDoubleWord(int offset, int data) { try { dirtyPages[offset >>> PAGE_SHIFT] = true; buffer[offset] = (byte) data; offset++; data >>= 8; buffer[offset] = (byte) (data); offset++; data >>= 8; buffer[offset] = (byte) (data); offset++; data >>= 8; buffer[offset] = (byte) (data); dirtyPages[offset >>> PAGE_SHIFT] = true; } catch (ArrayIndexOutOfBoundsException e) { increaseVGARAMSize(offset); setDoubleWord(offset, data); } } public int getDoubleWord(int offset) { try { int result= 0xFF & buffer[offset]; offset++; result |= (0xFF & buffer[offset]) << 8; offset++; result |= (0xFF & buffer[offset]) << 16; offset++; result |= (buffer[offset]) << 24; return result; } catch (ArrayIndexOutOfBoundsException e) { increaseVGARAMSize(offset); return getDoubleWord(offset); } } public String toString() { return "VGA RAM ByteArray["+getSize()+"]"; } public int executeReal(Processor cpu, int offset) { throw new IllegalStateException("Invalid Operation"); } public int executeProtected(Processor cpu, int offset) { throw new IllegalStateException("Invalid Operation"); } public int executeVirtual8086(Processor cpu, int offset) { throw new IllegalStateException("Invalid Operation"); } public boolean isAllocated() { return true; } public void loadInitialContents(int address, byte[] buf, int off, int len) { throw new UnsupportedOperationException("Not supported yet."); } } //Public Methods Used By Output Device public final void updateDisplay() { // note this causes NON-deterministic execution because of thread scheduling updatingScreen = true; boolean fullUpdate = false; int detGraphicMode; if ((attributeRegisterIndex & 0x20) == 0) detGraphicMode = GMODE_BLANK; else detGraphicMode = graphicsRegister[GR_INDEX_MISC] & 1; if (detGraphicMode != this.graphicMode) { this.graphicMode = detGraphicMode; fullUpdate = true; } switch(graphicMode) { case GMODE_TEXT: drawText(fullUpdate); break; case GMODE_GRAPH: drawGraphic(fullUpdate); break; case GMODE_BLANK: default: drawBlank(fullUpdate); break; } updatingScreen = false; } private final void drawText(boolean fullUpdate) { boolean temp = updatePalette16(); fullUpdate |= temp; int[] palette = lastPalette; /* compute font data address (in plane 2) */ int v = this.sequencerRegister[SR_INDEX_CHAR_MAP_SELECT]; int offset = (((v >>> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2; if (offset != this.fontOffset[0]) { this.fontOffset[0] = offset; fullUpdate = true; } offset = (((v >>> 5) & 1) | ((v >>> 1) & 6)) * 8192 * 4 + 2; if (offset != this.fontOffset[1]) { this.fontOffset[1] = offset; fullUpdate = true; } if ((this.planeUpdated & (1 << 2)) != 0) { /* if the plane 2 was modified since the last display, it indicates the font may have been modified */ this.planeUpdated = 0; fullUpdate = true; } temp = updateBasicParameters(); fullUpdate |= temp; int srcIndex = this.startAddress * 4; /* total width and height */ int charHeight = (crtRegister[CR_INDEX_MAX_SCANLINE] & 0x1f) + 1; int charWidth = 8; if ((sequencerRegister[SR_INDEX_CLOCKING_MODE] & 0x01) == 0) charWidth = 9; if ((sequencerRegister[SR_INDEX_CLOCKING_MODE] & 0x08) != 0) charWidth = 16; /* NOTE: no 18 pixel wide */ int width = crtRegister[CR_INDEX_HORZ_DISPLAY_END] + 1; int height; if (crtRegister[CR_INDEX_VERT_TOTAL] == 100) { /* ugly hack for CGA 160x100x16 */ height = 100; } else { height = crtRegister[CR_INDEX_VERT_DISPLAY_END] | ((crtRegister[CR_INDEX_OVERFLOW] & 0x02) << 7) | ((crtRegister[CR_INDEX_OVERFLOW] & 0x40) << 3); height = (height + 1) / charHeight; } if ((height * width) > CH_ATTR_SIZE) { /* better than nothing: exit if transient size is too big */ return; } if ((width != this.lastWidth) || (height != this.lastHeight) || (charWidth != this.lastCW) || (charHeight != this.lastCH)) { this.lastScreenWidth = width * charWidth; this.lastScreenHeight = height * charHeight; resizeDisplay(this.lastScreenWidth, this.lastScreenHeight); this.lastWidth = width; this.lastHeight = height; this.lastCH = charHeight; this.lastCW = charWidth; fullUpdate = true; } int curCursorOffset = ((crtRegister[CR_INDEX_CURSOR_LOC_HIGH] << 8) | crtRegister[CR_INDEX_CURSOR_LOC_LOW]) - this.startAddress; if ((curCursorOffset != this.cursorOffset) || (crtRegister[CR_INDEX_CURSOR_START] != this.cursorStart) || (crtRegister[CR_INDEX_CURSOR_END] != this.cursorEnd)) { /* if the cursor position changed, we updated the old and new chars */ if ((this.cursorOffset < CH_ATTR_SIZE) && (this.cursorOffset >= 0)) this.lastChar[this.cursorOffset] = -1; if ((curCursorOffset < CH_ATTR_SIZE) && (curCursorOffset >= 0)) this.lastChar[curCursorOffset] = -1; this.cursorOffset = curCursorOffset; this.cursorStart = crtRegister[CR_INDEX_CURSOR_START]; this.cursorEnd = crtRegister[CR_INDEX_CURSOR_END]; } int cursorIndex = (this.startAddress + this.cursorOffset) * 4; int lastCharOffset = 0; switch (charWidth) { case 8: for (int charY = 0; charY < height; charY++) { int srcOffset = srcIndex; for (int charX = 0; charX < width; charX++) { int charShort = 0xffff & ioRegion.getWord(srcOffset); if (fullUpdate || (charShort != this.lastChar[lastCharOffset])) { this.lastChar[lastCharOffset] = charShort; int character = 0xff & charShort; int characterAttribute = charShort >>> 8; int glyphOffset = fontOffset[(characterAttribute >>> 3) & 1] + 32 * 4 * character; int backgroundColor = palette[characterAttribute >>> 4]; int foregroundColor = palette[characterAttribute & 0xf]; drawGlyph8(getDisplayBuffer(), charY * charHeight * lastScreenWidth + charX * 8, lastScreenWidth, glyphOffset, charHeight, foregroundColor, backgroundColor); dirtyDisplayRegion(charX * 8, charY * charHeight, 8, charHeight); if ((srcOffset == cursorIndex) && ((crtRegister[CR_INDEX_CURSOR_START] & 0x20) == 0)) { int lineStart = crtRegister[CR_INDEX_CURSOR_START] & 0x1f; int lineLast = crtRegister[CR_INDEX_CURSOR_END] & 0x1f; /* XXX: check that */ if (lineLast > charHeight - 1) lineLast = charHeight - 1; if ((lineLast >= lineStart) && (lineStart < charHeight)) { int tempHeight = lineLast - lineStart + 1; drawCursorGlyph8(getDisplayBuffer(), (charY * charHeight + lineStart) * lastScreenWidth + charX * 8, lastScreenWidth, tempHeight, foregroundColor, backgroundColor); dirtyDisplayRegion(charX * 8, charY * charHeight + lineStart, 8, tempHeight); } } } srcOffset += 4; lastCharOffset++; } srcIndex += lineOffset; } return; case 9: for (int charY = 0; charY < height; charY++) { int srcOffset = srcIndex; for (int charX = 0; charX < width; charX++) { int charShort = 0xffff & ioRegion.getWord(srcOffset); if (fullUpdate || (charShort != this.lastChar[lastCharOffset])) { this.lastChar[lastCharOffset] = charShort; int character = 0xff & charShort; int characterAttribute = charShort >>> 8; int glyphOffset = fontOffset[(characterAttribute >>> 3) & 1] + 32 * 4 * character; int backgroundColor = palette[characterAttribute >>> 4]; int foregroundColor = palette[characterAttribute & 0xf]; boolean dup9 = ((character >= 0xb0) && (character <= 0xdf) && ((attributeRegister[AR_INDEX_ATTR_MODE_CONTROL] & 0x04) != 0)); drawGlyph9(getDisplayBuffer(), charY * charHeight * lastScreenWidth + charX * 9, lastScreenWidth, glyphOffset, charHeight, foregroundColor, backgroundColor, dup9); dirtyDisplayRegion(charX * 9, charY * charHeight, 9, charHeight); if ((srcOffset == cursorIndex) &&((crtRegister[CR_INDEX_CURSOR_START] & 0x20) == 0)) { int lineStart = crtRegister[CR_INDEX_CURSOR_START] & 0x1f; int lineLast = crtRegister[CR_INDEX_CURSOR_END] & 0x1f; /* XXX: check that */ if (lineLast > charHeight - 1) lineLast = charHeight - 1; if ((lineLast >= lineStart) && (lineStart < charHeight)) { int tempHeight = lineLast - lineStart + 1; drawCursorGlyph9(getDisplayBuffer(), (charY * charHeight + lineStart) * lastScreenWidth + charX * 9, lastScreenWidth, tempHeight, foregroundColor, backgroundColor); dirtyDisplayRegion(charX * 9, charY * charHeight + lineStart, 9, tempHeight); } } } srcOffset += 4; lastCharOffset++; } srcIndex += lineOffset; } return; case 16: for (int charY = 0; charY < height; charY++) { int srcOffset = srcIndex; for (int charX = 0; charX < width; charX++) { int charShort = 0xffff & ioRegion.getWord(srcOffset); if (fullUpdate || (charShort != this.lastChar[lastCharOffset])) { this.lastChar[lastCharOffset] = charShort; int character = 0xff & charShort; int characterAttribute = charShort >>> 8; int glyphOffset = fontOffset[(characterAttribute >>> 3) & 1] + 32 * 4 * character; int backgroundColor = palette[characterAttribute >>> 4]; int foregroundColor = palette[characterAttribute & 0xf]; drawGlyph16(getDisplayBuffer(), charY * charHeight * lastScreenWidth + charX * 16, lastScreenWidth, glyphOffset, charHeight, foregroundColor, backgroundColor); dirtyDisplayRegion(charX * 16, charY * charHeight, 16, charHeight); if ((srcOffset == cursorIndex) &&((crtRegister[CR_INDEX_CURSOR_START] & 0x20) == 0)) { int lineStart = crtRegister[CR_INDEX_CURSOR_START] & 0x1f; int lineLast = crtRegister[CR_INDEX_CURSOR_END] & 0x1f; /* XXX: check that */ if (lineLast > charHeight - 1) lineLast = charHeight - 1; if ((lineLast >= lineStart) && (lineStart < charHeight)) { int tempHeight = lineLast - lineStart + 1; drawCursorGlyph16(getDisplayBuffer(), (charY * charHeight + lineStart) * lastScreenWidth + charX * 16, lastScreenWidth, tempHeight, foregroundColor, backgroundColor); dirtyDisplayRegion(charX * 16, charY * charHeight + lineStart, 16, tempHeight); } } } srcOffset += 4; lastCharOffset++; } srcIndex += lineOffset; } return; default: LOGGING.log(Level.WARNING, "Unknown character width {0}", Integer.valueOf(charWidth)); return; } } abstract class GraphicsUpdater { abstract int byteWidth(int width); abstract void drawLine(int offset, int width, int y, int dispWidth); void updateDisplay(int width, int height, int dispWidth, boolean fullUpdate, int multiScan) { int multiRun = multiScan; int addr1 = 4 * startAddress; //int lineSize = width; // get the line size from the display device?? // if the "cursor_invalidate" function pointer is not null, then call it here. //if (s->cursor_invalidate) //s->cursor_invalidate(s); int y1 = 0; boolean addrMunge1 = (crtRegister[CR_INDEX_CRTC_MODE_CONTROL] & 1) == 0; boolean addrMunge2 = (crtRegister[CR_INDEX_CRTC_MODE_CONTROL] & 2) == 0; boolean addrMunge = addrMunge1 || addrMunge2; int mask = (crtRegister[CR_INDEX_CRTC_MODE_CONTROL] & 3) ^ 3; int pageMin = Integer.MAX_VALUE; int pageMax = Integer.MIN_VALUE; for(int y = 0; y < height; y++) { int addr = addr1; if (addrMunge) { if (addrMunge1) { /* CGA compatibility handling */ int shift = 14 + ((crtRegister[CR_INDEX_CRTC_MODE_CONTROL] >>> 6) & 1); addr = (addr & ~(1 << shift)) | ((y1 & 1) << shift); } if (addrMunge2) addr = (addr & ~0x8000) | ((y1 & 2) << 14); } int pageStart = addr >>> PAGE_SHIFT; int pageEnd = (addr + byteWidth(width) - 1) >>> PAGE_SHIFT; for (int i = pageStart; i <= pageEnd; i++) { if (fullUpdate || ioRegion.pageIsDirty(i)) { pageMin = Math.min(pageMin, pageStart); pageMax = Math.max(pageMax, pageEnd); drawLine(addr, width, y, dispWidth); // if the "cursor_draw_line" function pointer is not null, then call it here. //if (s->cursor_draw_line) // s->cursor_draw_line(s, d, y); break; } } if (multiRun == 0) { if ((y1 & mask) == mask) addr1 += lineOffset; y1++; multiRun = multiScan; } else multiRun--; /* line compare acts on the displayed lines */ if (y == lineCompare) addr1 = 0; } for (int i = pageMin; i <= pageMax; i++) ioRegion.cleanPage(i); } } class DrawLine2 extends GraphicsUpdater { int byteWidth(int width) { return (width / 2); } void drawLine(int offset, int width, int y, int dispWidth) { int[] dest = getDisplayBuffer(); int index = y * dispWidth; int[] palette = lastPalette; int planeMask = mask16[attributeRegister[AR_INDEX_COLOR_PLANE_ENABLE] & 0xf]; width >>>= 3; do { int data = ioRegion.getDoubleWord(offset); data &= planeMask; int v = expand2[data & 0xff]; v |= expand2[(data >>> 16) & 0xff] << 2; dest[index++] = palette[v >>> 12]; dest[index++] = palette[(v >>> 8) & 0xf]; dest[index++] = palette[(v >>> 4) & 0xf]; dest[index++] = palette[(v >>> 0) & 0xf]; v = expand2[(data >>> 8) & 0xff]; v |= expand2[(data >>> 24) & 0xff] << 2; dest[index++] = palette[v >>> 12]; dest[index++] = palette[(v >>> 8) & 0xf]; dest[index++] = palette[(v >>> 4) & 0xf]; dest[index++] = palette[(v >>> 0) & 0xf]; offset += 4; } while (--width != 0); dirtyDisplayRegion(0, y, dispWidth, 1); } } class DrawLine2d2 extends GraphicsUpdater { int byteWidth(int width) { return (width/2); } void drawLine(int offset, int width, int y, int dispWidth) { int[] dest = getDisplayBuffer(); int index = y * dispWidth; int[] palette = lastPalette; int planeMask = mask16[attributeRegister[AR_INDEX_COLOR_PLANE_ENABLE] & 0xf]; width >>>= 3; do { int data = ioRegion.getDoubleWord(offset); data &= planeMask; int v = expand2[data & 0xff]; v |= expand2[(data >>> 16) & 0xff] << 2; dest[index++] = dest[index++] = palette[v >>> 12]; dest[index++] = dest[index++] = palette[(v >>> 8) & 0xf]; dest[index++] = dest[index++] = palette[(v >>> 4) & 0xf]; dest[index++] = dest[index++] = palette[(v >>> 0) & 0xf]; v = expand2[(data >>> 8) & 0xff]; v |= expand2[(data >>> 24) & 0xff] << 2; dest[index++] = dest[index++] = palette[v >>> 12]; dest[index++] = dest[index++] = palette[(v >>> 8) & 0xf]; dest[index++] = dest[index++] = palette[(v >>> 4) & 0xf]; dest[index++] = dest[index++] = palette[(v >>> 0) & 0xf]; offset += 4; } while (--width != 0); dirtyDisplayRegion(0, y, dispWidth, 1); } } class DrawLine4 extends GraphicsUpdater { int byteWidth(int width) { return (width/2); } void drawLine(int offset, int width, int y, int dispWidth) { int[] dest = getDisplayBuffer(); int index = y * dispWidth; int[] palette = lastPalette; int planeMask = mask16[attributeRegister[AR_INDEX_COLOR_PLANE_ENABLE] & 0xf]; width >>>= 3; do { int data = ioRegion.getDoubleWord(offset) & planeMask; int v = expand4[data & 0xff]; data >>>= 8; v |= expand4[data & 0xff] << 1; data >>>= 8; v |= expand4[data & 0xff] << 2; data >>>= 8; v |= expand4[data & 0xff] << 3; dest[index++] = palette[v >>> 28]; dest[index++] = palette[(v >>> 24) & 0xF]; dest[index++] = palette[(v >>> 20) & 0xF]; dest[index++] = palette[(v >>> 16) & 0xF]; dest[index++] = palette[(v >>> 12) & 0xF]; dest[index++] = palette[(v >>> 8) & 0xF]; dest[index++] = palette[(v >>> 4) & 0xF]; dest[index++] = palette[(v >>> 0) & 0xF]; offset += 4; } while (--width != 0); dirtyDisplayRegion(0, y, dispWidth , 1); } } class DrawLine4d2 extends GraphicsUpdater { int byteWidth(int width) { return (width/2); } void drawLine(int offset, int width, int y, int dispWidth) { int[] dest = getDisplayBuffer(); int index = y * dispWidth; int[] palette = lastPalette; int planeMask = mask16[attributeRegister[AR_INDEX_COLOR_PLANE_ENABLE] & 0xf]; width >>>= 3; do { int data = ioRegion.getDoubleWord(offset); data &= planeMask; int v = expand4[data & 0xff]; v |= expand4[(data >>> 8) & 0xff] << 1; v |= expand4[(data >>> 16) & 0xff] << 2; v |= expand4[(data >>> 24) & 0xff] << 3; dest[index++] = dest[index++] = palette[v >>> 28]; dest[index++] = dest[index++] = palette[(v >>> 24) & 0xF]; dest[index++] = dest[index++] = palette[(v >>> 20) & 0xF]; dest[index++] = dest[index++] = palette[(v >>> 16) & 0xF]; dest[index++] = dest[index++] = palette[(v >>> 12) & 0xF]; dest[index++] = dest[index++] = palette[(v >>> 8) & 0xF]; dest[index++] = dest[index++] = palette[(v >>> 4) & 0xF]; dest[index++] = dest[index++] = palette[(v >>> 0) & 0xF]; offset += 4; } while (--width != 0); dirtyDisplayRegion(0, y, dispWidth, 1); } } class DrawLine8d2 extends GraphicsUpdater { int byteWidth(int width) { return (width/2); } void drawLine(int offset, int width, int y, int dispWidth) { int[] dest = getDisplayBuffer(); int index = y * dispWidth; int[] palette = lastPalette; width >>>= 1; do { int val = palette[0xFF & ioRegion.getByte(offset++)]; dest[index++] = val; dest[index++] = val; width--; } while (width != 0); dirtyDisplayRegion(0, y, dispWidth, 1); } } class DrawLine8 extends GraphicsUpdater { int byteWidth(int width) { return width; } void drawLine(int offset, int width, int y, int dispWidth) { int[] dest = getDisplayBuffer(); int index = y * dispWidth; int[] palette = lastPalette; do { dest[index] = palette[0xFF & ioRegion.getByte(offset++)]; index++; width--; } while (width != 0); dirtyDisplayRegion(0, y, dispWidth, 1); } } class DrawLine15 extends GraphicsUpdater { int byteWidth(int width) { return width * 2; } void drawLine(int offset, int width, int y, int dispWidth) { int[] dest = getDisplayBuffer(); int i = y * dispWidth; do { int v = 0xffff & ioRegion.getWord(offset); int r = (v >>> 7) & 0xf8; int g = (v >>> 2) & 0xf8; int b = (v << 3) & 0xf8; dest[i] = rgbToPixel(r, g, b); offset += 2; i++; } while (--width != 0); dirtyDisplayRegion(0, y, dispWidth, 1); } } class DrawLine16 extends GraphicsUpdater { int byteWidth(int width) { return width * 2; } void drawLine(int offset, int width, int y, int dispWidth) { int[] dest = getDisplayBuffer(); int i = y * dispWidth; do { int v = 0xffff & ioRegion.getWord(offset); int r = (v >>> 8) & 0xf8; int g = (v >>> 3) & 0xfc; int b = (v << 3) & 0xf8; dest[i] = rgbToPixel(r, g, b); offset += 2; i++; } while (--width != 0); dirtyDisplayRegion(0, y, dispWidth, 1); } } class DrawLine24 extends GraphicsUpdater { int byteWidth(int width) { return width * 3; } void drawLine(int offset, int width, int y, int dispWidth) { int[] dest = getDisplayBuffer(); int i = y * dispWidth; do { int b = 0xFF & ioRegion.getByte(offset++); int g = 0xFF & ioRegion.getByte(offset++); int r = 0xFF & ioRegion.getByte(offset++); dest[i++] = rgbToPixel(r, g, b); } while (--width != 0); dirtyDisplayRegion(0, y, dispWidth, 1); } } class DrawLine32 extends GraphicsUpdater { int byteWidth(int width) { return width * 4; } void drawLine(int offset, int width, int y, int dispWidth) { int[] dest = getDisplayBuffer(); int i = y * dispWidth; do { int b = 0xff & ioRegion.getByte(offset++); int g = 0xff & ioRegion.getByte(offset++); int r = 0xff & ioRegion.getByte(offset++); offset++; dest[i++] = rgbToPixel(r, g, b); } while (--width != 0); dirtyDisplayRegion(0, y, dispWidth, 1); } } private final void drawGraphic(boolean fullUpdate) { boolean temp = updateBasicParameters(); fullUpdate |= temp; int width = (crtRegister[CR_INDEX_HORZ_DISPLAY_END] + 1) * 8; int height = (crtRegister[CR_INDEX_VERT_DISPLAY_END] | ((crtRegister[CR_INDEX_OVERFLOW] & 0x02) << 7) | ((crtRegister[CR_INDEX_OVERFLOW] & 0x40) << 3)) + 1; int dispWidth = width; int shiftControlBuffer = (graphicsRegister[GR_INDEX_GRAPHICS_MODE] >>> 5) & 3; int doubleScanBuffer = crtRegister[CR_INDEX_MAX_SCANLINE] >>> 7; int multiScan; if (shiftControlBuffer != 1) multiScan = (((crtRegister[CR_INDEX_MAX_SCANLINE] & 0x1f) + 1) << doubleScanBuffer) - 1; else { /* in CGA modes, multi_scan is ignored */ /* XXX: is it correct ? */ multiScan = doubleScanBuffer; } if (shiftControlBuffer != shiftControl || doubleScanBuffer != doubleScan) { fullUpdate = true; this.shiftControl = shiftControlBuffer; this.doubleScan = doubleScanBuffer; } GraphicsUpdater graphicUpdater = null; if (shiftControl == 0) { temp = updatePalette16(); fullUpdate |= temp; if ((sequencerRegister[SR_INDEX_CLOCKING_MODE] & 8) != 0 ) { graphicUpdater = VGA_DRAW_LINE4D2; dispWidth <<= 1; } else graphicUpdater = VGA_DRAW_LINE4; } else if (shiftControl == 1) { temp = updatePalette16(); fullUpdate |= temp; if ((sequencerRegister[SR_INDEX_CLOCKING_MODE] & 8) != 0) { graphicUpdater = VGA_DRAW_LINE2D2; dispWidth <<= 1; } else graphicUpdater = VGA_DRAW_LINE2; } else { int bpp = 0; if ((vbeRegs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) != 0) bpp = vbeRegs[VBE_DISPI_INDEX_BPP]; switch(bpp) { default: case 0: temp = updatePalette256(); fullUpdate |= temp; graphicUpdater = VGA_DRAW_LINE8D2; break; case 8: temp = updatePalette256(); fullUpdate |= temp; graphicUpdater = VGA_DRAW_LINE8; break; case 15: graphicUpdater = VGA_DRAW_LINE15; break; case 16: graphicUpdater = VGA_DRAW_LINE16; break; case 24: graphicUpdater = VGA_DRAW_LINE24; break; case 32: graphicUpdater = VGA_DRAW_LINE32; break; } } if ((dispWidth != lastWidth) || (height != lastHeight)) { fullUpdate = true; lastScreenWidth = lastWidth = dispWidth; lastScreenHeight = lastHeight = height; resizeDisplay(lastScreenWidth, lastScreenHeight); } graphicUpdater.updateDisplay(width, height, dispWidth, fullUpdate, multiScan); } private final void drawBlank(boolean fullUpdate) { if (!fullUpdate) return; if ((lastScreenWidth <= 0) || (lastScreenHeight <= 0)) return; int[] rawBytes = getDisplayBuffer(); int black = rgbToPixel(0, 0, 0); for (int i=rawBytes.length-1; i>=0; i--) rawBytes[i] = black; dirtyDisplayRegion(0, 0, lastScreenWidth, lastScreenHeight); } private final boolean updatePalette16() { boolean fullUpdate = false; int[] palette = lastPalette; for (int colorIndex = AR_INDEX_PALLETE_MIN; colorIndex <= AR_INDEX_PALLETE_MAX; colorIndex++) { int v = attributeRegister[colorIndex]; if ((attributeRegister[AR_INDEX_ATTR_MODE_CONTROL] & 0x80) != 0) v = ((attributeRegister[AR_INDEX_COLOR_SELECT] & 0xf) << 4) | (v & 0xf); else v = ((attributeRegister[AR_INDEX_COLOR_SELECT] & 0xc) << 4) | (v & 0x3f); v *= 3; int col = rgbToPixel(c6to8(this.palette[v]), c6to8(this.palette[v+1]), c6to8(this.palette[v+2])); if (col != palette[colorIndex]) { fullUpdate = true; palette[colorIndex] = col; } } return fullUpdate; } private final boolean updatePalette256() { boolean fullUpdate = false; int[] palette = lastPalette; for (int i = 0, v = 0; i < 256; i++, v+=3) { int col = rgbToPixel(c6to8(this.palette[v]), c6to8(this.palette[v+1]), c6to8(this.palette[v+2])); if (col != palette[i]) { fullUpdate = true; palette[i] = col; } } return fullUpdate; } private final boolean updateBasicParameters() { int curStartAddress, curLineOffset; if ((vbeRegs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) != 0) { curLineOffset = this.vbeLineOffset; curStartAddress = this.vbeStartAddress; } else { /* compute curLineOffset in bytes */ curLineOffset = crtRegister[CR_INDEX_OFFSET]; curLineOffset <<= 3; /* starting address */ curStartAddress = crtRegister[CR_INDEX_START_ADDR_LOW] | (crtRegister[CR_INDEX_START_ADDR_HIGH] << 8); } /* line compare */ int curLineCompare = crtRegister[CR_INDEX_LINE_COMPARE] | ((crtRegister[CR_INDEX_OVERFLOW] & 0x10) << 4) | ((crtRegister[CR_INDEX_MAX_SCANLINE] & 0x40) << 3); if ((curLineOffset != this.lineOffset) || (curStartAddress != this.startAddress) || (curLineCompare != this.lineCompare)) { this.lineOffset = curLineOffset; this.startAddress = curStartAddress; this.lineCompare = curLineCompare; return true; } return false; } private static final int c6to8(int v) { v &= 0x3f; int b = v & 1; return (v << 2) | (b << 1) | b; } private final void drawGlyph8(int[] buffer, int startOffset, int scanSize, int glyphOffset, int charHeight, int foregroundColor, int backgroundColor) { int xorColor = backgroundColor ^ foregroundColor; scanSize -= 8; do { int fontData = ioRegion.getByte(glyphOffset); for (int i = 7; i >= 0; i--) { int pixel = ((-((fontData >>> i) & 1)) & xorColor) ^ backgroundColor; buffer[startOffset++] = pixel; } glyphOffset += 4; startOffset += scanSize; } while (--charHeight != 0); } private final void drawGlyph16(int[] buffer, int startOffset, int scanSize, int glyphOffset, int charHeight, int foregroundColor, int backgroundColor) { int xorColor = backgroundColor ^ foregroundColor; scanSize -= 16; do { int rawData = ioRegion.getByte(glyphOffset); int fontData = expand4to8[(rawData >>> 4) & 0x0f]; for (int i = 7; i >= 0; i--) { int pixel = ((-((fontData >>> i) & 1)) & xorColor) ^ backgroundColor; buffer[startOffset++] = pixel; } fontData = expand4to8[rawData & 0x0f]; for (int i = 7; i >= 0; i--) { int pixel = ((-((fontData >>> i) & 1)) & xorColor) ^ backgroundColor; buffer[startOffset++] = pixel; } glyphOffset += 4; startOffset += scanSize; } while (--charHeight != 0); } private final void drawGlyph9(int[] buffer, int startOffset, int scanSize, int glyphOffset, int charHeight, int foregroundColor, int backgroundColor, boolean dup9) { int xorColor = backgroundColor ^ foregroundColor; scanSize -= 9; if (dup9) { do { int fontData = ioRegion.getByte(glyphOffset); for (int i=7; i>=0; i--) { int pixel = ((-((fontData >>> i) & 1)) & xorColor) ^ backgroundColor; buffer[startOffset++] = pixel; } buffer[startOffset++] = buffer[startOffset-2]; glyphOffset += 4; startOffset += scanSize; } while (--charHeight != 0); } else { do { int fontData = ioRegion.getByte(glyphOffset); for (int i=7; i>=0; i--) { int pixel = ((-((fontData >>> i) & 1)) & xorColor) ^ backgroundColor; buffer[startOffset++] = pixel; } buffer[startOffset++] = backgroundColor; glyphOffset += 4; startOffset += scanSize; } while (--charHeight != 0); } } private final void drawCursorGlyph8(int[] buffer, int startOffset, int scanSize, int charHeight, int foregroundColor, int backgroundColor) { int xorColor = backgroundColor ^ foregroundColor; int glyphOffset = 0; scanSize -= 8; do { int fontData = cursorGlyph[glyphOffset]; for (int i = 7; i >= 0; i--) { int pixel = ((-((fontData >>> i) & 1)) & xorColor) ^ backgroundColor; buffer[startOffset++] = pixel; } glyphOffset += 4; startOffset += scanSize; } while (--charHeight != 0); } private final void drawCursorGlyph16(int[] buffer, int startOffset, int scanSize, int charHeight, int foregroundColor, int backgroundColor) { int glyphOffset = 0; int xorColor = backgroundColor ^ foregroundColor; scanSize -= 16; do { int rawData = cursorGlyph[glyphOffset]; int fontData = expand4to8[(rawData >>> 4) & 0x0f]; for (int i = 7; i >= 0; i--) { int pixel = ((-((fontData >>> i) & 1)) & xorColor) ^ backgroundColor; buffer[startOffset++] = pixel; } fontData = expand4to8[rawData & 0x0f]; for (int i = 7; i >= 0; i--) { int pixel = ((-((fontData >>> i) & 1)) & xorColor) ^ backgroundColor; buffer[startOffset++] = pixel; } glyphOffset += 4; startOffset += scanSize; } while (--charHeight != 0); } private final void drawCursorGlyph9(int[] buffer, int startOffset, int scanSize, int charHeight, int foregroundColor, int backgroundColor) { int glyphOffset = 0; int xorColor = backgroundColor ^ foregroundColor; scanSize -= 9; do { int fontData = cursorGlyph[glyphOffset]; for (int i=7; i>=0; i--) { int pixel = ((-((fontData >>> i) & 1)) & xorColor) ^ backgroundColor; buffer[startOffset++] = pixel; } buffer[startOffset++] = buffer[startOffset-2]; glyphOffset++; startOffset += scanSize; } while (--charHeight != 0); } public boolean initialised() { return ioportRegistered && pciRegistered && memoryRegistered; } public void reset() { ioportRegistered = false; memoryRegistered = false; pciRegistered = false; assignDeviceFunctionNumber(-1); setIRQIndex(16); putConfigWord(PCI_CONFIG_VENDOR_ID, (short)0x1234); // Dummy putConfigWord(PCI_CONFIG_DEVICE_ID, (short)0x1111); putConfigWord(PCI_CONFIG_CLASS_DEVICE, (short)0x0300); // VGA Controller putConfigByte(PCI_CONFIG_HEADER, (byte)0x00); // header_type sequencerRegister = new int[256]; graphicsRegister = new int[256]; attributeRegister = new int[256]; crtRegister = new int[256]; //invalidatedYTable = new int[VGA_MAX_HEIGHT / 32]; dacCache = new int[3]; palette = new int[768]; ioRegion = new VGARAMIORegion(); vbeRegs = new int[VBE_DISPI_INDEX_NB+1]; fontOffset = new int[2]; lastChar = new int[CH_ATTR_SIZE]; this.internalReset(); bankOffset = 0; vbeRegs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0; vbeBankMask = ((VGA_RAM_SIZE >>> 16) - 1); super.reset(); } public boolean updated() { return ioportRegistered && pciRegistered && memoryRegistered; } public void updateComponent(HardwareComponent component) { if ((component instanceof PCIBus)) { ((PCIBus)component).registerDevice(this); pciRegistered = true; } if ((component instanceof IOPortHandler)) { ((IOPortHandler)component).registerIOPortCapable(this); ioportRegistered = true; } if ((component instanceof PhysicalAddressSpace) && component.updated()) { ((PhysicalAddressSpace)component).mapMemoryRegion(lowIORegion, 0xa0000, 0x20000); memoryRegistered = true; } } public void acceptComponent(HardwareComponent component) { if ((component instanceof PCIBus) && component.initialised()) { ((PCIBus)component).registerDevice(this); pciRegistered = true; } if ((component instanceof IOPortHandler) && component.initialised()) { ((IOPortHandler)component).registerIOPortCapable(this); ioportRegistered = true; } if ((component instanceof PhysicalAddressSpace) && component.initialised()) { ((PhysicalAddressSpace)component).mapMemoryRegion(lowIORegion, 0xa0000, 0x20000); memoryRegistered = true; } } public abstract void setMonitor(PCMonitor mon); public String toString() { return "VGA Card [Mode: " + lastScreenWidth + " x " + lastScreenHeight + "]"; } }