883 lines
30 KiB
C#
883 lines
30 KiB
C#
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Microsoft Research Singularity
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
// File: Vga.cs
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
// Based on code originally written by Erick Smith (ErickS)
|
||
|
//
|
||
|
// Useful reference URLs:
|
||
|
// http://www.xs4all.nl/~ganswijk/chipdir/reg/vga.txt "The VGA Registers"
|
||
|
// http://osdev.neopages.net/FreeVGA/vga/graphreg.htm
|
||
|
//
|
||
|
|
||
|
using Microsoft.Singularity.Channels;
|
||
|
using Microsoft.Singularity.Hal;
|
||
|
using Microsoft.Singularity.Io;
|
||
|
using Microsoft.Singularity.Configuration;
|
||
|
|
||
|
using System;
|
||
|
|
||
|
namespace Microsoft.Singularity.Drivers
|
||
|
{
|
||
|
// create the resource object for CTR to fill in
|
||
|
[DriverCategory]
|
||
|
[Signature("pci/cc_0300")]
|
||
|
internal class VgaResources : DriverCategoryDeclaration
|
||
|
{
|
||
|
[IoFixedPortRange(Base = 0x3c0, Length = 0x20, Shared = true)]
|
||
|
IoPortRange dev;
|
||
|
|
||
|
[IoFixedMemoryRange(Base = 0xa0000, Length = 0x20000, Shared = true)]
|
||
|
public IoMemoryRange screenMemory;
|
||
|
|
||
|
[IoFixedMemoryRange(Base = 0xb8000, Length = 0x8000, Shared = true)]
|
||
|
public IoMemoryRange textBuffer;
|
||
|
|
||
|
// Provide to unify creation.
|
||
|
public static IDevice! DeviceCreate(IoConfig! configobj, String! instanceName)
|
||
|
{
|
||
|
DebugStub.Print("Creating VGA Display\n");
|
||
|
return new Vga();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class Vga : ISvgaDevice
|
||
|
{
|
||
|
public const ushort ATC_ADDR_PORT = 0x00; // 3c0: Attribute Controller Address and
|
||
|
public const ushort MISC_OUTPUT_REG_WRITE_PORT = 0x02; // 3c2: Miscellaneous Output reg write port
|
||
|
public const ushort SEQ_ADDR_PORT = 0x04; // 3c4: Sequence Controller Address and
|
||
|
public const ushort SEQ_DATA_PORT = 0x05; // 3c5: Data registers
|
||
|
public const ushort DAC_PIXEL_MASK_PORT = 0x06; // 3c6: DAC pixel mask reg
|
||
|
public const ushort DAC_ADDR_WRITE_PORT = 0x08; // 3c8: DAC register write index reg
|
||
|
public const ushort DAC_DATA_REG_PORT = 0x09; // 3c9: DAC data transfer reg
|
||
|
public const ushort GRAPH_ADDR_PORT = 0x0e; // 3ce: Graphics Controller Address
|
||
|
public const ushort GRAPH_DATA_PORT = 0x0f; // 3cf: Graphics Controller Data
|
||
|
public const ushort CRTC_ADDR_COLOR_PORT = 0x14; // 3d4: CRT Controller Address and
|
||
|
public const ushort INPUT_STATUS_COLOR_PORT = 0x1a; // 3da: Input Status 1 register read port in color mode
|
||
|
|
||
|
public const int SCREEN_HEIGHT = 480;
|
||
|
public const int SCREEN_WIDTH = 640;
|
||
|
|
||
|
public void BitBltPng(int x, int y, byte[] buffer)
|
||
|
{ /// dummy def
|
||
|
}
|
||
|
|
||
|
public Vga()
|
||
|
{
|
||
|
IoPortRange dev = new IoPortRange(0x3c0, 0x20, Access.ReadWrite);
|
||
|
|
||
|
atcAddrPort = dev.PortAtOffset(ATC_ADDR_PORT, 2, Access.ReadWrite);
|
||
|
miscOutputRegWritePort = dev.PortAtOffset(MISC_OUTPUT_REG_WRITE_PORT, 2, Access.ReadWrite);
|
||
|
seqAddrPort = dev.PortAtOffset(SEQ_ADDR_PORT, 2, Access.Write);
|
||
|
seqDataPort = dev.PortAtOffset(SEQ_DATA_PORT, 2, Access.ReadWrite);
|
||
|
dacPixelMaskPort = dev.PortAtOffset(DAC_PIXEL_MASK_PORT, 2, Access.Write);
|
||
|
dacAddressWritePort = dev.PortAtOffset(DAC_ADDR_WRITE_PORT, 2, Access.Write);
|
||
|
dacDataRegPort = dev.PortAtOffset(DAC_DATA_REG_PORT, 2, Access.Write);
|
||
|
graphAddrPort = dev.PortAtOffset(GRAPH_ADDR_PORT, 2, Access.Write);
|
||
|
graphDataPort = dev.PortAtOffset(GRAPH_DATA_PORT, 2, Access.ReadWrite);
|
||
|
crtcAddressColorPort = dev.PortAtOffset(CRTC_ADDR_COLOR_PORT, 2, Access.ReadWrite);
|
||
|
inputStatusColorPort = dev.PortAtOffset(INPUT_STATUS_COLOR_PORT, 2, Access.ReadWrite);
|
||
|
|
||
|
screenBuffer = IoMemory.AllocateIo(0xa0000, 0x20000, true, true);
|
||
|
textBuffer = IoMemory.AllocateIo(0xb8000, 0x8000, true, true);
|
||
|
}
|
||
|
|
||
|
private IHalConsole lastScreen;
|
||
|
|
||
|
//
|
||
|
// VGA register definitions
|
||
|
//
|
||
|
private IoPort atcAddrPort; // Attribute Controller Address and
|
||
|
private IoPort miscOutputRegWritePort; // Miscellaneous Output reg write port
|
||
|
private IoPort seqAddrPort; // Sequence Controller Address and
|
||
|
private IoPort seqDataPort; // Data registers
|
||
|
private IoPort dacPixelMaskPort; // DAC pixel mask reg
|
||
|
private IoPort dacAddressWritePort; // DAC register write index reg
|
||
|
private IoPort dacDataRegPort; // DAC data transfer reg
|
||
|
private IoPort graphAddrPort; // Graphics Controller Address
|
||
|
private IoPort graphDataPort; // Graphics Controller Data
|
||
|
private IoPort crtcAddressColorPort; // CRT Controller Address and
|
||
|
private IoPort inputStatusColorPort; // Input Status 1 register read port in color mode
|
||
|
|
||
|
private IoMemory screenBuffer;
|
||
|
private IoMemory textBuffer;
|
||
|
|
||
|
//
|
||
|
// VGA indexed register indexes.
|
||
|
//
|
||
|
private const byte SEQ_RESET_REG = 0;
|
||
|
private const byte SEQ_CLOCK_REG = 1;
|
||
|
private const byte SEQ_PLANEMASK_REG = 2;
|
||
|
private const byte SEQ_CHARSET_REG = 3;
|
||
|
private const byte SEQ_MEMORY_REG = 4;
|
||
|
|
||
|
private const byte GRAPH_SET_REG = 0;
|
||
|
private const byte GRAPH_ENABLE_REG = 1;
|
||
|
private const byte GRAPH_COMPARE_REG = 2;
|
||
|
private const byte GRAPH_OPERATION_REG = 3;
|
||
|
private const byte GRAPH_READSEL_REG = 4;
|
||
|
private const byte GRAPH_MODE_REG = 5;
|
||
|
private const byte GRAPH_MISC_REG = 6;
|
||
|
private const byte GRAPH_DONTCARE_REG = 7;
|
||
|
private const byte GRAPH_BITMASK_REG = 8;
|
||
|
|
||
|
private const byte WRITE_MODE_0 = 0;
|
||
|
private const byte WRITE_MODE_1 = 1;
|
||
|
private const byte WRITE_MODE_2 = 2;
|
||
|
private const byte WRITE_MODE_3 = 3;
|
||
|
private const byte READ_MODE_0 = 0;
|
||
|
private const byte READ_MODE_1 = 8;
|
||
|
|
||
|
//
|
||
|
// Values for Attribute Controller Index register to turn video off
|
||
|
// and on, by setting bit 5 to 0 (off) or 1 (on).
|
||
|
//
|
||
|
private const byte VIDEO_ENABLE = 0x20;
|
||
|
|
||
|
private const uint BI_RLE4 = 2;
|
||
|
|
||
|
//
|
||
|
// globals to track screen position
|
||
|
//
|
||
|
|
||
|
private const int DELTA = 80;
|
||
|
|
||
|
static private byte[] lMaskTable = new byte[8] {0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
|
||
|
static private byte[] rMaskTable = new byte[8] {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
|
||
|
static private byte[] PixelMask = new byte[8] {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
|
||
|
|
||
|
private void ReadWriteMode(byte mode)
|
||
|
{
|
||
|
graphAddrPort.Write8(GRAPH_MODE_REG);
|
||
|
graphDataPort.Write8(mode);
|
||
|
}
|
||
|
|
||
|
private void Plot(int x, int y, byte color)
|
||
|
{
|
||
|
int offset = y * DELTA + (x / 8);
|
||
|
|
||
|
ReadWriteMode(READ_MODE_1 | WRITE_MODE_2);
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG);
|
||
|
graphDataPort.Write8(PixelMask[x % 8]);
|
||
|
|
||
|
// The read triggers the latch register.
|
||
|
screenBuffer.Read8(offset);
|
||
|
screenBuffer.Write8(offset, color);
|
||
|
}
|
||
|
|
||
|
private void Fill(int x1, int y1, int x2, int y2, byte color)
|
||
|
{
|
||
|
if (x1 < 0 || x1 >= SCREEN_WIDTH ||
|
||
|
x2 < 0 || x2 >= SCREEN_WIDTH ||
|
||
|
y1 < 0 || y1 >= SCREEN_HEIGHT ||
|
||
|
y2 < 0 || y2 >= SCREEN_HEIGHT) {
|
||
|
|
||
|
throw new OverflowException("Draw bounds invalid.");
|
||
|
}
|
||
|
|
||
|
byte lMask = lMaskTable[x1 % 8];
|
||
|
byte rMask = rMaskTable[x2 % 8];
|
||
|
|
||
|
int bank1 = x1 / 8;
|
||
|
int bank2 = x2 / 8;
|
||
|
int count = bank2 - bank1;
|
||
|
|
||
|
if (count == 0) {
|
||
|
lMask &= rMask;
|
||
|
}
|
||
|
|
||
|
ReadWriteMode(READ_MODE_1 | WRITE_MODE_2);
|
||
|
|
||
|
//
|
||
|
// Do the left edge
|
||
|
//
|
||
|
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG);
|
||
|
graphDataPort.Write8(lMask);
|
||
|
|
||
|
int pDst = y1 * DELTA + bank1;
|
||
|
for (int y = y1; y <= y2; y++) {
|
||
|
|
||
|
// The read triggers the latch register.
|
||
|
screenBuffer.Read8(pDst);
|
||
|
screenBuffer.Write8(pDst, color);
|
||
|
pDst += DELTA;
|
||
|
}
|
||
|
|
||
|
if (count != 0) {
|
||
|
|
||
|
//
|
||
|
// Do the right edge
|
||
|
//
|
||
|
|
||
|
pDst = y1 * DELTA + bank2;
|
||
|
count--;
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG);
|
||
|
graphDataPort.Write8(rMask);
|
||
|
|
||
|
for (int y = y1; y <= y2; y++) {
|
||
|
// The read triggers the latch register.
|
||
|
screenBuffer.Read8(pDst);
|
||
|
screenBuffer.Write8(pDst, color);
|
||
|
pDst += DELTA;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Do the center section
|
||
|
//
|
||
|
|
||
|
if (count != 0) {
|
||
|
|
||
|
pDst = y1 * DELTA + bank1 + 1;
|
||
|
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG);
|
||
|
graphDataPort.Write8(0xff);
|
||
|
|
||
|
for (int y = y1; y <= y2; y++) {
|
||
|
screenBuffer.Write8(pDst, (byte)color, count);
|
||
|
pDst += DELTA;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void BitBlt4(int x, int y, int width, int height,
|
||
|
byte[] buffer, int offset, int ScanWidth)
|
||
|
{
|
||
|
byte[] Plane = new byte [81];
|
||
|
bool bRightEdge = false;
|
||
|
bool bCenterSection = false;
|
||
|
|
||
|
byte lMask = lMaskTable[x % 8];
|
||
|
byte rMask = rMaskTable[(x + width - 1) % 8];
|
||
|
|
||
|
int bank1 = x / 8;
|
||
|
int bank2 = (x + width - 1) / 8;
|
||
|
|
||
|
int count = bank2 - bank1;
|
||
|
|
||
|
if (bank1 == bank2) {
|
||
|
lMask &= rMask;
|
||
|
}
|
||
|
|
||
|
if (count != 0) {
|
||
|
|
||
|
bRightEdge = true;
|
||
|
|
||
|
count--;
|
||
|
|
||
|
if (count != 0) {
|
||
|
|
||
|
bCenterSection = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int pDst = (y * DELTA) + (x / 8);
|
||
|
int pSrc = offset;
|
||
|
|
||
|
ReadWriteMode(READ_MODE_0 | WRITE_MODE_0);
|
||
|
|
||
|
for (uint j = 0; j < height; j++) {
|
||
|
|
||
|
for (byte plane = 1; plane < 16; plane <<= 1) {
|
||
|
|
||
|
int pSrcTemp = pSrc;
|
||
|
int pDstTemp = pDst;
|
||
|
|
||
|
//
|
||
|
// Convert the packed bitmap data into planar data
|
||
|
// for this plane.
|
||
|
//
|
||
|
|
||
|
int bank = bank1;
|
||
|
Plane[bank] = 0;
|
||
|
byte Mask = PixelMask[x % 8];
|
||
|
uint toggle = 0;
|
||
|
|
||
|
for (uint i = 0; i < width; i++) {
|
||
|
|
||
|
if ((toggle++ & 0x1) != 0) {
|
||
|
|
||
|
if ((buffer[pSrcTemp] & plane) != 0) {
|
||
|
Plane[bank] |= Mask;
|
||
|
}
|
||
|
|
||
|
pSrcTemp++;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if (((buffer[pSrcTemp] >> 4) & plane) != 0) {
|
||
|
Plane[bank] |= Mask;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Mask >>= 1;
|
||
|
|
||
|
if (Mask == 0) {
|
||
|
bank++;
|
||
|
Plane[bank] = 0;
|
||
|
Mask = 0x80;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set up the vga so that we see the correct bit plane.
|
||
|
//
|
||
|
|
||
|
seqAddrPort.Write8(SEQ_PLANEMASK_REG);
|
||
|
seqDataPort.Write8(plane);
|
||
|
|
||
|
//
|
||
|
// bank will go from bank1 to bank2
|
||
|
//
|
||
|
|
||
|
bank = bank1;
|
||
|
pDstTemp = pDst;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Set Bitmask for left edge.
|
||
|
//
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG);
|
||
|
graphDataPort.Write8(lMask);
|
||
|
|
||
|
// The read triggers the latch register.
|
||
|
screenBuffer.Read8(pDstTemp);
|
||
|
screenBuffer.Write8(pDstTemp++, Plane[bank++]);
|
||
|
|
||
|
if (bCenterSection) {
|
||
|
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG);
|
||
|
graphDataPort.Write8(0xff);
|
||
|
|
||
|
screenBuffer.Write8(pDstTemp, Plane, bank, count);
|
||
|
bank += count;
|
||
|
pDstTemp += count;
|
||
|
}
|
||
|
|
||
|
if (bRightEdge) {
|
||
|
|
||
|
//
|
||
|
// Set bitmask for right edge.
|
||
|
//
|
||
|
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG);
|
||
|
graphDataPort.Write8(rMask);
|
||
|
|
||
|
// The read triggers the latch register.
|
||
|
screenBuffer.Read8(pDstTemp);
|
||
|
screenBuffer.Write8(pDstTemp, Plane[bank]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pDst += DELTA;
|
||
|
pSrc += ScanWidth;
|
||
|
}
|
||
|
|
||
|
// Restore the settings.
|
||
|
seqAddrPort.Write8(SEQ_PLANEMASK_REG);
|
||
|
seqDataPort.Write8(0x0f);
|
||
|
}
|
||
|
|
||
|
private void BitBlt1(int x, int y, int width, int height,
|
||
|
byte[] buffer, int offset, int ScanWidth, byte color)
|
||
|
{
|
||
|
int bank1 = x / 8;
|
||
|
int bank2 = (x + width - 1) / 8;
|
||
|
|
||
|
byte lMask = lMaskTable[x % 8];
|
||
|
byte rMask = rMaskTable[(x + width - 1) % 8];
|
||
|
|
||
|
if (bank1 == bank2) {
|
||
|
lMask &= rMask;
|
||
|
}
|
||
|
|
||
|
int pSrc = offset;
|
||
|
int pDst = (y * DELTA) + (x / 8);
|
||
|
|
||
|
int shift = x % 8;
|
||
|
|
||
|
ReadWriteMode(READ_MODE_0 | WRITE_MODE_0);
|
||
|
|
||
|
graphAddrPort.Write8(GRAPH_ENABLE_REG);
|
||
|
graphDataPort.Write8((byte)(color ^ 0x0f));
|
||
|
|
||
|
for (uint j = 0; j < height; j++) {
|
||
|
|
||
|
int pDstTemp = pDst;
|
||
|
int pSrcTemp = pSrc;
|
||
|
int count = width;
|
||
|
|
||
|
//
|
||
|
// non aligned case
|
||
|
//
|
||
|
|
||
|
if (shift != 0) {
|
||
|
|
||
|
//
|
||
|
// Left Edge.
|
||
|
//
|
||
|
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG);
|
||
|
graphDataPort.Write8(lMask);
|
||
|
|
||
|
// The read triggers the latch register.
|
||
|
screenBuffer.Read8(pDstTemp);
|
||
|
screenBuffer.Write8(pDstTemp++, (byte)(buffer[pSrcTemp] >> shift));
|
||
|
|
||
|
count -= (8 - shift);
|
||
|
|
||
|
//
|
||
|
// Now do center section
|
||
|
//
|
||
|
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG);
|
||
|
graphDataPort.Write8(0xff);
|
||
|
|
||
|
while (count > 7) {
|
||
|
screenBuffer.Write8(pDstTemp++,
|
||
|
(byte)((buffer[pSrcTemp] << (8 - shift)) |
|
||
|
(buffer[pSrcTemp+1] >> shift)));
|
||
|
|
||
|
pSrcTemp++;
|
||
|
count -= 8;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now do the right edge.
|
||
|
//
|
||
|
|
||
|
if (count != 0) {
|
||
|
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG);
|
||
|
graphDataPort.Write8(rMask);
|
||
|
|
||
|
// The read triggers the latch register.
|
||
|
screenBuffer.Read8(pDstTemp);
|
||
|
screenBuffer.Write8(pDstTemp++,
|
||
|
(byte)(buffer[pSrcTemp] << (8 - shift)));
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Aligned case.
|
||
|
//
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG);
|
||
|
graphDataPort.Write8(0xff);
|
||
|
|
||
|
screenBuffer.Write8(pDstTemp, buffer, pSrcTemp, count / 8);
|
||
|
pSrcTemp += count / 8;
|
||
|
pDstTemp += count / 8;
|
||
|
count %= 8;
|
||
|
|
||
|
//
|
||
|
// Now do any remaining bits.
|
||
|
//
|
||
|
|
||
|
if (count != 0) {
|
||
|
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG);
|
||
|
graphDataPort.Write8(rMask);
|
||
|
|
||
|
// The read triggers the latch register.
|
||
|
screenBuffer.Read8(pDstTemp);
|
||
|
screenBuffer.Write8(pDstTemp++, buffer[pSrcTemp]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pSrc += ScanWidth;
|
||
|
pDst += DELTA;
|
||
|
}
|
||
|
|
||
|
graphAddrPort.Write8(GRAPH_ENABLE_REG);
|
||
|
graphDataPort.Write8(0x00);
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG);
|
||
|
graphDataPort.Write8(0xff);
|
||
|
}
|
||
|
|
||
|
// Display a bitmap at a given location.
|
||
|
public void BitBltBmp(int x, int y, byte[] buffer)
|
||
|
{
|
||
|
BITMAPFILEHEADER bfh;
|
||
|
BITMAPINFOHEADER bih;
|
||
|
int lDelta;
|
||
|
int cbScanLine;
|
||
|
int used;
|
||
|
int offset = 0;
|
||
|
|
||
|
bfh = BITMAPFILEHEADER.Read(buffer, offset, out used);
|
||
|
bih = BITMAPINFOHEADER.Read(buffer, used, out used);
|
||
|
|
||
|
if (bih.biWidth == 0 || bih.biHeight == 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (x < 0) {
|
||
|
x = SCREEN_WIDTH + x - bih.biWidth;
|
||
|
}
|
||
|
if (y < 0) {
|
||
|
y = SCREEN_HEIGHT + y - bih.biHeight;
|
||
|
}
|
||
|
|
||
|
if (x < 0 || x + bih.biWidth > SCREEN_WIDTH ||
|
||
|
y < 0 || y + bih.biHeight > SCREEN_HEIGHT) {
|
||
|
|
||
|
throw new OverflowException("Draw bounds invalid.");
|
||
|
}
|
||
|
|
||
|
offset = offset + bfh.bfOffBits;
|
||
|
used = offset;
|
||
|
|
||
|
//
|
||
|
// Make sure this is a 1bpp or 4bpp bitmap.
|
||
|
//
|
||
|
|
||
|
if ((bih.biBitCount * bih.biPlanes) <= 4 || bih.biCompression != 0) {
|
||
|
|
||
|
cbScanLine = (((bih.biWidth * bih.biBitCount) + 31) & ~31) / 8;
|
||
|
|
||
|
if (bih.biHeight < 0) {
|
||
|
|
||
|
// top down bitmap
|
||
|
lDelta = cbScanLine;
|
||
|
bih.biHeight = -bih.biHeight;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
// bottom up bitmap
|
||
|
offset += cbScanLine * (bih.biHeight - 1);
|
||
|
lDelta = -cbScanLine;
|
||
|
}
|
||
|
|
||
|
if (used + cbScanLine * bih.biHeight > buffer.Length) {
|
||
|
DebugStub.Print("{0} + {1} * {2} = {3} > {4}\n",
|
||
|
__arglist(
|
||
|
used,
|
||
|
cbScanLine,
|
||
|
bih.biHeight,
|
||
|
used + cbScanLine * bih.biHeight,
|
||
|
buffer.Length);
|
||
|
throw new OverflowException("Bitmap invalid.");
|
||
|
}
|
||
|
|
||
|
if (bih.biBitCount == 1) {
|
||
|
BitBlt1(x, y, bih.biWidth, bih.biHeight, buffer, offset, lDelta, 15);
|
||
|
}
|
||
|
else if (bih.biBitCount == 4) {
|
||
|
BitBlt4(x, y, bih.biWidth, bih.biHeight, buffer, offset, lDelta);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// We don't support this type of bitmap.
|
||
|
//
|
||
|
DebugStub.Print("((bih.biBitCount * bih.biPlanes) not supported");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Scroll(int x1, int y1, int x2, int y2, int CharHeight)
|
||
|
{
|
||
|
if (x1 < 0 || x1 >= SCREEN_WIDTH ||
|
||
|
x2 < 0 || x2 >= SCREEN_WIDTH ||
|
||
|
y1 < 0 || y1 >= SCREEN_HEIGHT ||
|
||
|
y2 < 0 || y2 >= SCREEN_HEIGHT ||
|
||
|
y2 - y1 < CharHeight) {
|
||
|
|
||
|
throw new OverflowException("Draw bounds invalid.");
|
||
|
}
|
||
|
|
||
|
int width = (x2 / 8) - (x1 / 8) + 1;
|
||
|
|
||
|
ReadWriteMode(READ_MODE_0 | WRITE_MODE_1);
|
||
|
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG); // enable write to all bits in plane
|
||
|
graphDataPort.Write8(0xff);
|
||
|
|
||
|
int pDst = y1 * DELTA + (x1 / 8);
|
||
|
int pSrc = pDst + DELTA * CharHeight;
|
||
|
|
||
|
for (int i = y1; i <= y2 - CharHeight; i++) {
|
||
|
|
||
|
int pDstTemp = pDst;
|
||
|
int pSrcTemp = pSrc;
|
||
|
|
||
|
for (int j = 0; j < width; j++) {
|
||
|
// The read triggers the latch register.
|
||
|
screenBuffer.Read8(pSrcTemp++);
|
||
|
// The write triggers a write from the latch register, value ignored.
|
||
|
screenBuffer.Write8(pDstTemp++, 0);
|
||
|
}
|
||
|
|
||
|
pDst += DELTA;
|
||
|
pSrc += DELTA;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Plot(int x, int y, RGB color)
|
||
|
{
|
||
|
Plot(x, y, (byte)color);
|
||
|
}
|
||
|
|
||
|
public void Fill(int x1, int y1, int x2, int y2, RGB color)
|
||
|
{
|
||
|
Fill(x1, y1, x2, y2, (byte)color);
|
||
|
}
|
||
|
|
||
|
public void BitBltChr(int x, int y, int width, int height,
|
||
|
byte[] buffer, int offset, int ScanWidth,
|
||
|
RGB color, RGB background)
|
||
|
{
|
||
|
BitBlt1(x, y, width, height, buffer, offset + height - ScanWidth, -ScanWidth, (byte)color);
|
||
|
}
|
||
|
|
||
|
private void SetPaletteEntry(byte index, uint RGB)
|
||
|
{
|
||
|
dacAddressWritePort.Write8(index);
|
||
|
dacDataRegPort.Write8((byte)(RGB & 0xff));
|
||
|
dacDataRegPort.Write8((byte)((RGB >> 8) & 0xff));
|
||
|
dacDataRegPort.Write8((byte)((RGB >> 16) & 0xff));
|
||
|
}
|
||
|
|
||
|
private void InitializePalette()
|
||
|
{
|
||
|
uint[] Palette = new uint[]
|
||
|
{
|
||
|
0x00000000,
|
||
|
0x00000020,
|
||
|
0x00002000,
|
||
|
0x00002020,
|
||
|
0x00200000,
|
||
|
0x00200020,
|
||
|
0x00202000,
|
||
|
0x00202020,
|
||
|
0x00303030,
|
||
|
0x0000003f,
|
||
|
0x00003f00,
|
||
|
0x00003f3f,
|
||
|
0x003f0000,
|
||
|
0x003f003f,
|
||
|
0x003f3f00,
|
||
|
0x003f3f3f,
|
||
|
};
|
||
|
|
||
|
for (byte i = 0; i < Palette.Length; i++) {
|
||
|
SetPaletteEntry(i, Palette[i]);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// This routine initializes the VGA.
|
||
|
public void Initialize()
|
||
|
{
|
||
|
DebugStub.Print("Initializing VGA Driver\n");
|
||
|
|
||
|
Vga640x480();
|
||
|
DebugStub.Print(" VGA.AtInitialization\n");
|
||
|
AtInitialization();
|
||
|
DebugStub.Print(" VGA.InitializePalette\n");
|
||
|
InitializePalette();
|
||
|
|
||
|
DebugStub.Print(" VGA.Fill\n");
|
||
|
Fill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, 11);
|
||
|
DebugStub.Print(" VGA.new SVGA\n");
|
||
|
SvgaWindow console = new SvgaWindow(this, 0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1);
|
||
|
|
||
|
lastScreen = Console.Screen;
|
||
|
Console.Screen = console;
|
||
|
|
||
|
Console.WriteLine("Singularity VGA Driver");
|
||
|
Console.WriteLine();
|
||
|
Console.WriteLine();
|
||
|
Console.WriteLine();
|
||
|
}
|
||
|
|
||
|
public void Finalize()
|
||
|
{
|
||
|
if (lastScreen != null) {
|
||
|
Console.Screen = lastScreen;
|
||
|
}
|
||
|
|
||
|
AtInitialization();
|
||
|
InitializePalette();
|
||
|
|
||
|
VgaTextMode();
|
||
|
|
||
|
for (int i = 0; i < 50 * 80 * 2; i++) {
|
||
|
textBuffer.Write16(i, 0x1f41);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void IndxOut(IoPort port, byte[] values)
|
||
|
{
|
||
|
for (ushort i = 0; i < values.Length; i++) {
|
||
|
port.Write16((ushort)(i + ((ushort)values[i] << 8)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void AtcOut(IoPort port, byte[] values)
|
||
|
{
|
||
|
for (byte i = 0; i < values.Length; i++) {
|
||
|
port.Write8(i);
|
||
|
port.Write8(values[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
private void VgaTextMode()
|
||
|
{
|
||
|
// start sync reset program up sequencer
|
||
|
IndxOut(seqAddrPort, new byte[] { 0x01,0x00,0x03,0x00,0x02 } );
|
||
|
|
||
|
miscOutputRegWritePort.Write8(0x67);
|
||
|
graphAddrPort.Write16(0x0e06);
|
||
|
|
||
|
// EndSyncResetCmd
|
||
|
seqAddrPort.Write16(0x0300);
|
||
|
|
||
|
// Unlock the CTC registers.
|
||
|
crtcAddressColorPort.Write16(0x0E11);
|
||
|
|
||
|
// program crtc registers
|
||
|
IndxOut(crtcAddressColorPort, new byte[] { 0x5F,0x4f,0x50,0x82,0x55,0x81,
|
||
|
0xbf,0x1f,0x00,0x4f,0x0d,0x0e,
|
||
|
0x00,0x00,0x00,0x00,0x9c,0x8e,
|
||
|
0x8f,0x28,0x1f,0x96,0xb9,0xa3,
|
||
|
0xFF } );
|
||
|
|
||
|
// prepare atc for writing
|
||
|
inputStatusColorPort.Read8();
|
||
|
AtcOut(atcAddrPort, new byte[] { 0x00,0x01,0x02,0x03,0x04,0x05,0x14,0x07,
|
||
|
0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
|
||
|
0x04,0x00,0x0F,0x08,0x00 } );
|
||
|
|
||
|
// program graphics controller registers
|
||
|
IndxOut(graphAddrPort, new byte[] { 0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00,0xff});
|
||
|
|
||
|
// DAC mask registers
|
||
|
dacPixelMaskPort.Write8(0xFF);
|
||
|
|
||
|
// prepare atc for writing
|
||
|
inputStatusColorPort.Read8();
|
||
|
|
||
|
// turn video on.
|
||
|
atcAddrPort.Write8(VIDEO_ENABLE);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Code to put vga into mode 12.
|
||
|
//
|
||
|
private void Vga640x480()
|
||
|
{
|
||
|
DebugStub.Print(" Vga640x480:1\n");
|
||
|
// start sync reset program up sequencer
|
||
|
seqAddrPort.Write8(SEQ_RESET_REG);
|
||
|
seqDataPort.Write8(0);
|
||
|
seqAddrPort.Write8(SEQ_CLOCK_REG);
|
||
|
seqDataPort.Write8(0x01);
|
||
|
seqAddrPort.Write8(SEQ_PLANEMASK_REG);
|
||
|
seqDataPort.Write8(0x0f);
|
||
|
seqAddrPort.Write8(SEQ_CHARSET_REG);
|
||
|
seqDataPort.Write8(0x00);
|
||
|
seqAddrPort.Write8(SEQ_MEMORY_REG);
|
||
|
seqDataPort.Write8(0x06);
|
||
|
|
||
|
DebugStub.Print(" Vga640x480:2\n");
|
||
|
miscOutputRegWritePort.Write8(0xe3);
|
||
|
|
||
|
DebugStub.Print(" Vga640x480:3\n");
|
||
|
// Set chain mode in sync reset
|
||
|
graphAddrPort.Write8(GRAPH_MISC_REG);
|
||
|
graphDataPort.Write8(0x05); // graphics and 64KB buffer
|
||
|
|
||
|
DebugStub.Print(" Vga640x480:4\n");
|
||
|
// EndSyncResetCmd
|
||
|
seqAddrPort.Write8(SEQ_RESET_REG);
|
||
|
seqDataPort.Write8(0x03);
|
||
|
|
||
|
DebugStub.Print(" Vga640x480:5\n");
|
||
|
// Unlock CRTC registers 0-7
|
||
|
crtcAddressColorPort.Write16(0x0511);
|
||
|
|
||
|
DebugStub.Print(" Vga640x480:6\n");
|
||
|
// program crtc registers
|
||
|
IndxOut(crtcAddressColorPort, new byte[] { 0x5F,0x4F,0x50,0x82,0x54,0x80,
|
||
|
0x0B,0x3E,0x00,0x40,0x00,0x00,
|
||
|
0x00,0x00,0x00,0x00,0xEA,0x8C,
|
||
|
0xDF,0x28,0x00,0xE7,0x04,0xE3,
|
||
|
0xFF } );
|
||
|
|
||
|
// prepare atc for writing
|
||
|
inputStatusColorPort.Read8();
|
||
|
|
||
|
DebugStub.Print(" Vga640x480:7\n");
|
||
|
// program attribute controller registers
|
||
|
AtcOut(atcAddrPort, new byte[] { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
|
||
|
0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
|
||
|
0x01,0x00,0x0F,0x00,0x00 } );
|
||
|
|
||
|
DebugStub.Print(" Vga640x480:8\n");
|
||
|
// program graphics controller registers
|
||
|
graphAddrPort.Write8(GRAPH_SET_REG);
|
||
|
graphDataPort.Write8(0x00);
|
||
|
graphAddrPort.Write8(GRAPH_ENABLE_REG);
|
||
|
graphDataPort.Write8(0x00);
|
||
|
graphAddrPort.Write8(GRAPH_COMPARE_REG);
|
||
|
graphDataPort.Write8(0x00);
|
||
|
graphAddrPort.Write8(GRAPH_OPERATION_REG);
|
||
|
graphDataPort.Write8(0x00);
|
||
|
graphAddrPort.Write8(GRAPH_READSEL_REG);
|
||
|
graphDataPort.Write8(0x00);
|
||
|
graphAddrPort.Write8(GRAPH_MODE_REG);
|
||
|
graphDataPort.Write8(0x00);
|
||
|
graphAddrPort.Write8(GRAPH_MISC_REG);
|
||
|
graphDataPort.Write8(0x05);
|
||
|
graphAddrPort.Write8(GRAPH_DONTCARE_REG);
|
||
|
graphDataPort.Write8(0x00); // was 0x0f
|
||
|
graphAddrPort.Write8(GRAPH_BITMASK_REG);
|
||
|
graphDataPort.Write8(0xff);
|
||
|
|
||
|
DebugStub.Print(" Vga640x480:9\n");
|
||
|
// DAC mask registers
|
||
|
dacPixelMaskPort.Write8(0xFF);
|
||
|
|
||
|
DebugStub.Print(" Vga640x480:10\n");
|
||
|
// prepare atc for writing
|
||
|
inputStatusColorPort.Read8();
|
||
|
|
||
|
DebugStub.Print(" Vga640x480:11\n");
|
||
|
// turn video on.
|
||
|
atcAddrPort.Write8(VIDEO_ENABLE);
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Initialize AT registers
|
||
|
//
|
||
|
private void AtInitialization()
|
||
|
{
|
||
|
// prepare atc for writing
|
||
|
inputStatusColorPort.Read8();
|
||
|
|
||
|
// program attribute controller registers
|
||
|
AtcOut(atcAddrPort, new byte[] { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
|
||
|
0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f
|
||
|
});
|
||
|
|
||
|
// prepare atc for writing
|
||
|
inputStatusColorPort.Read8();
|
||
|
|
||
|
// turn video on.
|
||
|
atcAddrPort.Write8(VIDEO_ENABLE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|