////////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   Vga.cs
//
//  Notes:
//
//  Useful reference URLs:
//      http://www.xs4all.nl/~ganswijk/chipdir/reg/vga.txt "The VGA Registers"
//      http://osdev.neopages.net/FreeVGA/vga/graphreg.htm
//

using System;

using Microsoft.Singularity.Channels;
using Microsoft.Singularity.Hal;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.Configuration;

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 HalConsole 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);
        }
    }
}

