/*
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 + "]";
}
}