using System; using System.Collections.Generic; using System.Text; using Cosmos.Hardware; using Cosmos.Kernel; namespace Cosmos.Playground.SSchocke { public class VMWareSVGA { private enum SVGA_REG { ID = 0, ENABLE = 1, WIDTH = 2, HEIGHT = 3, MAX_WIDTH = 4, MAX_HEIGHT = 5, DEPTH = 6, BITS_PER_PIXEL = 7, /* Current bpp in the guest */ PSEUDOCOLOR = 8, RED_MASK = 9, GREEN_MASK = 10, BLUE_MASK = 11, BYTES_PER_LINE = 12, FB_START = 13, /* (Deprecated) */ FB_OFFSET = 14, VRAM_SIZE = 15, FB_SIZE = 16, /* ID 0 implementation only had the above registers, then the palette */ CAPABILITIES = 17, MEM_START = 18, /* (Deprecated) */ MEM_SIZE = 19, CONFIG_DONE = 20, /* Set when memory area configured */ SYNC = 21, /* See "FIFO Synchronization Registers" */ BUSY = 22, /* See "FIFO Synchronization Registers" */ GUEST_ID = 23, /* Set guest OS identifier */ CURSOR_ID = 24, /* (Deprecated) */ CURSOR_X = 25, /* (Deprecated) */ CURSOR_Y = 26, /* (Deprecated) */ CURSOR_ON = 27, /* (Deprecated) */ HOST_BITS_PER_PIXEL = 28, /* (Deprecated) */ SCRATCH_SIZE = 29, /* Number of scratch registers */ MEM_REGS = 30, /* Number of FIFO registers */ NUM_DISPLAYS = 31, /* (Deprecated) */ PITCHLOCK = 32, /* Fixed pitch for all modes */ IRQMASK = 33, /* Interrupt mask */ /* Legacy multi-monitor support */ NUM_GUEST_DISPLAYS = 34,/* Number of guest displays in X/Y direction */ DISPLAY_ID = 35, /* Display ID for the following display attributes */ DISPLAY_IS_PRIMARY = 36,/* Whether this is a primary display */ DISPLAY_POSITION_X = 37,/* The display position x */ DISPLAY_POSITION_Y = 38,/* The display position y */ DISPLAY_WIDTH = 39, /* The display's width */ DISPLAY_HEIGHT = 40, /* The display's height */ /* See "Guest memory regions" below. */ GMR_ID = 41, GMR_DESCRIPTOR = 42, GMR_MAX_IDS = 43, GMR_MAX_DESCRIPTOR_LENGTH = 44, TRACES = 45, /* Enable trace-based updates even when FIFO is on */ TOP = 46, /* Must be 1 more than the last register */ SVGA_PALETTE_BASE = 1024, /* Base of SVGA color map */ /* Next 768 (== 256*3) registers exist for colormap */ SVGA_SCRATCH_BASE = SVGA_PALETTE_BASE + 768 /* Base of scratch registers */ /* Next reg[SCRATCH_SIZE] registers exist for scratch usage: First 4 are reserved for VESA BIOS Extension; any remaining are for the use of the current SVGA driver. */ }; private enum SVGA_CAP { NONE = 0x00000000, RECT_COPY = 0x00000002, CURSOR = 0x00000020, CURSOR_BYPASS = 0x00000040, // Legacy (Use Cursor Bypass 3 instead) CURSOR_BYPASS_2 = 0x00000080, // Legacy (Use Cursor Bypass 3 instead) _8BIT_EMULATION = 0x00000100, ALPHA_CURSOR = 0x00000200, _3D = 0x00004000, EXTENDED_FIFO = 0x00008000, MULTIMON = 0x00010000, // Legacy multi-monitor support PITCHLOCK = 0x00020000, IRQMASK = 0x00040000, DISPLAY_TOPOLOGY = 0x00080000, // Legacy multi-monitor support GMR = 0x00100000, TRACES = 0x00200000 }; private enum SVGA_FIFO_CAP { NONE = 0, FENCE = 0x01, ACCELFRONT = 0x02, PITCHLOCK = 0x04, VIDEO = 0x08, CURSOR_BYPASS_3 = 0x10, ESCAPE = 0x20, RESERVE = 0x40, }; private enum SVGA_CMD { INVALID_CMD = 0, UPDATE = 1, RECT_COPY = 3, DEFINE_CURSOR = 19, DEFINE_ALPHA_CURSOR = 22, UPDATE_VERBOSE = 25, FRONT_ROP_FILL = 29, FENCE = 30, ESCAPE = 33, MAX }; private class FifoStruct { /*struct { uint32 reservedSize; Bool usingBounceBuffer; uint8 bounceBuffer[1024 * 1024]; uint32 nextFence; } fifo;*/ internal UInt32 reservedSize; internal bool usingBounceEffect; internal ManagedMemorySpace bounceBuffer; internal UInt32 nextFence; public FifoStruct() { this.bounceBuffer = new ManagedMemorySpace(1024 * 1024, 4); } } public class Rect { public int x, y, w, h; public Rect(int xPos, int yPos, int width, int height) { this.x = xPos; this.y = yPos; this.w = width; this.h = height; } } public class BackBuffer { public MemoryAddressSpace buffer; internal List dirtyRecs; public BackBuffer() { dirtyRecs = new List(); } public void MarkDirty(Rect r) { dirtyRecs.Add(new Rect(r.x, r.y, r.w, r.h)); } public Rect[] DirtyRecs { get { return this.dirtyRecs.ToArray(); } } internal unsafe void DrawRect(Rect r, uint color) { uint bytesPerPixel = 4; uint bytesPerLine = 1024 * bytesPerPixel; for (int y = 0; y < r.h; y++) { uint offset = (uint)(((r.y + y) * bytesPerLine) + (r.x * bytesPerPixel)); for (uint x = 0; x < (r.w * bytesPerPixel); x += 4) { (*(uint*)(buffer.Offset + offset + x)) = color; } } } }; public unsafe class VideoMemoryAddressSpace : MemoryAddressSpace { public VideoMemoryAddressSpace(UInt32 offset, UInt32 size) : base(offset, size) { } } private PCIDevice pciDev; private IOAddressSpace ioBase; private MemoryAddressSpace fifoMem; public MemoryAddressSpace fbMem; private UInt32 fifoSize; private UInt32 fbSize; private UInt32 deviceVersionID; private UInt32 capabilities; private UInt32 width; private UInt32 height; private UInt32 bpp; private UInt32 pitch; private FifoStruct fifo; private const UInt32 SVGA_MAGIC = 0x900000; private const UInt32 SVGA_ID_2 = (SVGA_MAGIC << 8) | 2; private const UInt32 SVGA_ID_1 = (SVGA_MAGIC << 8) | 1; private const UInt32 SVGA_ID_0 = (SVGA_MAGIC << 8) | 0; private const UInt32 SVGA_INDEX_PORT = 0x0; private const UInt32 SVGA_VALUE_PORT = 0x1; private const UInt32 SVGA_IRQSTATUS_PORT = 0x8; private const UInt32 SVGA3D_HWVERSION_CURRENT = 2 << 16; public static void Test() { VMWareSVGA vmsvga = null; Console.WriteLine("Scanning for VMWare SVGA cards..."); foreach (PCIDevice device in Cosmos.Hardware.PCIBus.Devices) { if ((device.VendorID == 0x15AD) && (device.DeviceID == 0x0405) && (device.Claimed == false)) { Console.WriteLine("Found VMWare SVGA on PCI " + device.Bus + ":" + device.Slot + ":" + device.Function); Console.WriteLine("VGA IRQ: " + device.InterruptLine); vmsvga = new VMWareSVGA(device); } } if (vmsvga == null) { Console.WriteLine("No VMWare SVGA Adapter found!!"); return; } vmsvga.SetMode(1024, 768, 32); BackBuffer back = vmsvga.CreateBackBuffer(); Rect screen = new Rect(0, 0, 1024, 768); Rect block = new Rect(100, 100, 100, 100); DebugUtil.SendMessage("VMWare", "Screen Rect: r.x=" + screen.x + ", r.y=" + screen.y + ", r.w=" + screen.w + ", r.h=" + screen.h); back.buffer.SetMem(0); back.MarkDirty(screen); vmsvga.SyncBuffers(back); uint color = 0xFF333333; int ydir = 2; int xdir = 2; while (true) { //DebugUtil.SendMessage("VMWare", "BackBuffer: offset=" + back.buffer.Offset + ", size=" + back.buffer.Size); //DebugUtil.SendMessage("VMWare", "FrameBuffer: offset=" + vmsvga.fbMem.Offset + ", size=" + vmsvga.fbMem.Size); back.DrawRect(block, color); back.MarkDirty(block); vmsvga.SyncBuffers(back); back.DrawRect(block, 0xFF000000); back.MarkDirty(block); block.x += xdir; block.y += ydir; if (block.x + block.w >= vmsvga.width) { xdir *= -1; color += 16; } if (block.y + block.h >= vmsvga.height) { ydir *= -1; color += 16; } if (block.x <= 0) { xdir *= -1; color += 16; } if (block.y <= 0) { ydir *= -1; color += 16; } } } public VMWareSVGA(PCIDevice dev) { this.pciDev = dev; this.fifo = new FifoStruct(); ioBase = pciDev.GetAddressSpace(0) as Kernel.IOAddressSpace; fbMem = pciDev.GetAddressSpace(1) as MemoryAddressSpace; fifoMem = pciDev.GetAddressSpace(2) as Kernel.MemoryAddressSpace; // Determine device revision ID this.deviceVersionID = SVGA_ID_2; do { writeSVGARegister(SVGA_REG.ID, this.deviceVersionID); if (readSVGARegister(SVGA_REG.ID) == this.deviceVersionID) { break; } else { this.deviceVersionID--; } } while (this.deviceVersionID >= SVGA_ID_0); if (this.deviceVersionID < SVGA_ID_0) { Console.WriteLine("Unsupported Revision of VMWare SVGA adapter!!"); return; } this.fbSize = readSVGARegister(SVGA_REG.FB_SIZE); this.fifoSize = readSVGARegister(SVGA_REG.MEM_SIZE); if (this.fbSize < 0x100000) { Console.WriteLine("Framebuffer Size too small... something is wrong!!"); return; } if (this.fifoSize < 0x20000) { Console.WriteLine("FIFO Size too small... something is wrong!!"); return; } this.pciDev.Claimed = true; if (deviceVersionID >= SVGA_ID_1) { this.capabilities = readSVGARegister(SVGA_REG.CAPABILITIES); } if ((this.capabilities & (uint)SVGA_CAP.IRQMASK) != 0) { writeSVGARegister(SVGA_REG.IRQMASK, 0); ioBase.Write32(SVGA_IRQSTATUS_PORT, 0xFF); Console.WriteLine("Adapter has Interrupt support, but not supported at present by us!!"); } uint fifo_caps = this.fifoMem.Read32(16); Console.WriteLine("Adapter Properties: versionID=" + (deviceVersionID & 0xFF) + ", fbSize=" + fbSize + ", fifoSize=" + fifoSize + ", capabilities=" + capabilities + "fifo_cap=" + fifo_caps); DebugUtil.SendMessage("VMWare", "Adapter Properties: versionID=" + (deviceVersionID & 0xFF) + ", fbSize=" + fbSize + ", fifoSize=" + fifoSize + ", capabilities=" + capabilities + "fifo_cap=" + fifo_caps); } public void SetMode(uint width, uint height, uint bpp) { this.width = width; this.height = height; this.bpp = bpp; writeSVGARegister(SVGA_REG.WIDTH, width); writeSVGARegister(SVGA_REG.HEIGHT, height); writeSVGARegister(SVGA_REG.BITS_PER_PIXEL, bpp); writeSVGARegister(SVGA_REG.ENABLE, 1); this.pitch = readSVGARegister(SVGA_REG.BYTES_PER_LINE); // Initialize the command FIFO this.fifoMem.Write32(0, 1164); // MIN = NUM_REGS * 4 this.fifoMem.Write32(4, this.fifoSize); // MAX = end of FIFO mem this.fifoMem.Write32(8, 1164); // NEXT_CMD = MIN this.fifoMem.Write32(12, 1164); // STOP = MIN if ((hasFIFOCap(SVGA_CAP.EXTENDED_FIFO) == true) && (isFIFORegValid(1152) == true)) // FIFO_GUEST_3D_HW_VERSION { fifoMem.Write32(1152, SVGA3D_HWVERSION_CURRENT);// FIFO_GUEST_3D_HW_VERSION } // Enable the FIFO writeSVGARegister(SVGA_REG.CONFIG_DONE, 1); this.fbSize = readSVGARegister(SVGA_REG.FB_SIZE); this.fifoSize = readSVGARegister(SVGA_REG.MEM_SIZE); uint fbOffset = readSVGARegister(SVGA_REG.FB_OFFSET); DebugUtil.SendMessage("VMWare", "Adapter Properties: versionID=" + (deviceVersionID & 0xFF) + ", fbSize=" + fbSize + ", fifoSize=" + fifoSize + ", capabilities=" + capabilities + ", fbOffset=" + fbOffset); } public BackBuffer CreateBackBuffer() { BackBuffer tmp = new BackBuffer(); //tmp.buffer = new MemoryAddressSpace(this.fbMem.Offset + (this.width * this.height * (this.bpp / 8)), (this.width * this.height * (this.bpp / 8))); tmp.buffer = new ManagedMemorySpace(this.width * this.height * (this.bpp / 8)); return tmp; } public void SyncBuffers(BackBuffer back) { //DebugUtil.SendMessage("VMWare", "Buffer Sync: NumRects=" + back.dirtyRecs.Count); for (int rectNum = 0; rectNum < back.dirtyRecs.Count; rectNum++) { Rect r = back.dirtyRecs[rectNum]; /*for (int y = 0; y < r.h; y++) { uint offset = (uint)(((r.y + y) * this.width) + r.x); for (uint x = 0; x < r.w; x++) { fbMem[offset + x] = back.buffer[offset + x]; } }*/ CopyRect(r, back.buffer); //fbMem.CopyFrom(back.buffer); update(r); } back.dirtyRecs.Clear(); SyncToFence(InsertFence()); } internal unsafe void CopyRect(Rect r, MemoryAddressSpace src) { uint bytesPerPixel = this.bpp / 8; uint bytesPerLine = this.width * bytesPerPixel; for (int y = 0; y < r.h; y++) { uint offset = (uint)(((r.y + y) * bytesPerLine) + (r.x * bytesPerPixel)); for (uint x = 0; x < (r.w * bytesPerPixel); x++) { (*(byte*)(fbMem.Offset + offset + x)) = *(byte*)(src.Offset + offset + x); //fbMem[offset + x] = back.buffer[offset + x]; } } } private void SyncToFence(uint fence) { if (fence == 0) { DebugUtil.SendError("VMWare", "SyncToFence fence = 0!!"); return; } if (!hasFIFOCap(SVGA_FIFO_CAP.FENCE)) { /* * Fall back on the legacy sync if the host does not support * fences. This is the old sync mechanism that has been * supported in the SVGA device pretty much since the dawn of * time: write to the SYNC register, then read from BUSY until * it's nonzero. This will drain the entire FIFO. * * The parameter we write to SVGA_REG_SYNC is an arbitrary * nonzero value which can be used for debugging, but which is * ignored by release builds of VMware products. */ //DebugUtil.SendMessage("VMWare", "SyncToFence old style sync!!"); writeSVGARegister(SVGA_REG.SYNC, 1); while (readSVGARegister(SVGA_REG.BUSY) != 0) { } return; } if (hasFencePassed(fence)) { return; } if (isFIFORegValid(1156) && ((this.capabilities & (uint)SVGA_CAP.IRQMASK) != 0)) { // Interrupt based handling of fences... Not supported yet } //else { bool busy = true; writeSVGARegister(SVGA_REG.SYNC, 1); while (hasFencePassed(fence) && busy) { busy = (readSVGARegister(SVGA_REG.BUSY) != 0); } } if (!hasFencePassed(fence)) { /* * This shouldn't happen. If it does, there might be a bug in * the SVGA device. */ DebugUtil.SendError("VMWare", "SyncToFence failed!!"); } } private bool hasFencePassed(uint fence) { if (fence == 0) { DebugUtil.SendError("VMWare", "hasFencePassed fence = 0!!"); return true; } if (!hasFIFOCap(SVGA_FIFO_CAP.FENCE)) { DebugUtil.SendError("VMWare", "hasFencePassed no FENCE capability!!"); return false; } //DebugUtil.SendMessage("VMWare", "hasFencePassed FIFO_FENCE=" + fifoMem.Read32(24)); return (((Int32)(fifoMem.Read32(24) - fence)) >= 0); } private uint InsertFence() { if (!hasFIFOCap(SVGA_FIFO_CAP.FENCE)) { return 1; } if (fifo.nextFence == 0) { fifo.nextFence = 1; } uint fence = fifo.nextFence++; MemoryAddressSpace cmd = FIFOReserve(8); cmd.Write32(0, (uint)SVGA_CMD.FENCE); cmd.Write32(4, fence); /*byte[] temp = new byte[8]; for (uint b = 0; b < temp.Length; b++) { temp[b] = cmd[b]; }*/ //DebugUtil.WriteBinary("VMware", "Fence CMD contents", temp); //DebugUtil.SendMessage("VMWare", "SVGA InsertFence: cmd.Offset=" + cmd.Offset + ", cmd.size=" + cmd.Size); //DebugUtil.SendMessage("VMWare", "InsertFence fence=" + fence); FIFOCommitAll(); return fence; } private void update(Rect r) { MemoryAddressSpace cmd = FIFOReserveCmd(SVGA_CMD.UPDATE, 16); cmd.Write32(4, (uint)r.x); cmd.Write32(8, (uint)r.y); cmd.Write32(12, (uint)r.w); cmd.Write32(16, (uint)r.h); /*byte[] temp = new byte[20]; for (uint b = 0; b < temp.Length; b++) { temp[b] = cmd[b]; }*/ //DebugUtil.WriteBinary("VMware", "Update CMD contents", temp); //DebugUtil.SendMessage("VMWare", "SVGA Update: cmd.Offset=" + cmd.Offset + ", cmd.size=" + cmd.Size); FIFOCommitAll(); } private void FIFOCommitAll() { FIFOCommit(fifo.reservedSize); } private void FIFOCommit(uint bytes) { uint min = fifoMem.Read32(0); uint max = fifoMem.Read32(4); uint nextCmd = fifoMem.Read32(8); bool reserveable = hasFIFOCap(SVGA_FIFO_CAP.RESERVE); /*DebugUtil.SendMessage("VMWare", "FIFOCommit bytes=" + bytes + ", min=" + min + ", max=" + max + ", nextCmd=" + nextCmd + ", reserveable=" + reserveable + ", usingBounceEffect=" + fifo.usingBounceEffect);*/ if (fifo.reservedSize == 0) { DebugUtil.SendError("VMWare","FIFOCommit before FIFOReserve!!"); return; } fifo.reservedSize = 0; if (fifo.usingBounceEffect) { /* * Slow paths: copy out of a bounce buffer. */ MemoryAddressSpace buffer = fifo.bounceBuffer; if (reserveable) { /* * Slow path: bulk copy out of a bounce buffer in two chunks. * * Note that the second chunk may be zero-length if the reserved * size was large enough to wrap around but the commit size was * small enough that everything fit contiguously into the FIFO. * * Note also that we didn't need to tell the FIFO about the * reservation in the bounce buffer, but we do need to tell it * about the data we're bouncing from there into the FIFO. */ uint chunkSize = Math.Min(bytes, max - nextCmd); fifoMem.Write32(54, bytes); // FIFO_RESERVED fifoMem.CopyFrom(buffer, 0, nextCmd, chunkSize); fifoMem.CopyFrom(buffer, chunkSize, min, bytes - chunkSize); } else { /* * Slowest path: copy one dword at a time, updating NEXT_CMD as * we go, so that we bound how much data the guest has written * and the host doesn't know to checkpoint. */ uint index = 0; while (bytes > 0) { fifoMem.Write32(nextCmd, buffer.Read32(index)); nextCmd += 4; index += 4; if (nextCmd == max) { nextCmd = min; } fifoMem.Write32(8, nextCmd); // NEXT_CMD bytes -= 4; } } } // if usingBounceEffect // Atomically update NEXT_CMD, if we didn't already if ((!fifo.usingBounceEffect) || (reserveable)) { nextCmd += bytes; if (nextCmd >= max) { nextCmd -= (max - min); } fifoMem.Write32(8, nextCmd); // NEXT_CMD } // Clear reservation in the FIFO if (reserveable) { fifoMem.Write32(54, 0);// FIFO_RESERVED } } private MemoryAddressSpace FIFOReserveCmd(SVGA_CMD type, uint bytes) { MemoryAddressSpace cmd = FIFOReserve(bytes + 4); cmd.Write32(0, (uint)type); return cmd; } private MemoryAddressSpace FIFOReserve(uint bytes) { uint min = fifoMem.Read32(0); uint max = fifoMem.Read32(4); uint nextCmd = fifoMem.Read32(8); bool reserveable = hasFIFOCap(SVGA_FIFO_CAP.RESERVE); /*DebugUtil.SendMessage("VMWare", "FIFOReserve bytes=" + bytes + ", min=" + min + ", max=" + max + ", nextCmd=" + nextCmd + ", reserveable=" + reserveable);*/ if ((bytes > fifo.bounceBuffer.Size) || (bytes > (max - min))) { DebugUtil.SendError("VMWare","FIFO command too large"); } if ((bytes % 4) != 0) { DebugUtil.SendError("VMWare","FIFO command length not 32-bit aligned"); } if (fifo.reservedSize != 0) { DebugUtil.SendError("VMWare","FIFOReserve before FIFOCommit"); } fifo.reservedSize = bytes; while (true) { uint stop = fifoMem.Read32(12); bool reserveInPlace = false; bool needBounce = false; #region Pick strategy for dealing with data if (nextCmd >= stop) { // No valid FIFO data between nextCmd and max if ((nextCmd + bytes < max) || ((nextCmd + bytes == max) && (stop > min))) { /* * Fastest path 1: There is already enough contiguous space * between nextCmd and max (the end of the buffer). * * Note the edge case: If the "<" path succeeds, we can * quickly return without performing any other tests. If * we end up on the "==" path, we're writing exactly up to * the top of the FIFO and we still need to make sure that * there is at least one unused DWORD at the bottom, in * order to be sure we don't fill the FIFO entirely. * * If the "==" test succeeds, but stop <= min (the FIFO * would be completely full if we were to reserve this * much space) we'll end up hitting the FIFOFull path below. */ reserveInPlace = true; //DebugUtil.SendMessage("VMWare", "FIFOReserve Path 1"); } else if ((max - nextCmd) + (stop - min) <= bytes) { /* * We have to split the FIFO command into two pieces, * but there still isn't enough total free space in * the FIFO to store it. * * Note the "<=". We need to keep at least one DWORD * of the FIFO free at all times, or we won't be able * to tell the difference between full and empty. */ //DebugUtil.SendMessage("VMWare", "FIFOReserve Path 2"); FIFOFull(); } else { //DebugUtil.SendMessage("VMWare", "FIFOReserve Path 3"); needBounce = true; } } else { // There is FIFO Data between nextCmd and max if (nextCmd + bytes < stop) { /* * Fastest path 2: There is already enough contiguous space * between nextCmd and stop. */ reserveInPlace = true; //DebugUtil.SendMessage("VMWare", "FIFOReserve Path 4"); } else { /* * There isn't enough room between nextCmd and stop. * The FIFO is too full to accept this command. */ FIFOFull(); //DebugUtil.SendMessage("VMWare", "FIFOReserve Path 5"); } } #endregion // If we decide we can write directly to the FIFO, ensure VM can support this if (reserveInPlace) { if ((reserveable) || (bytes <= 4)) { fifo.usingBounceEffect = false; if (reserveable) { fifoMem.Write32(54, bytes); // FIFO_RESERVED //DebugUtil.SendMessage("VMWare", "FIFOReserve in cmd"); } return new MemoryAddressSpace(fifoMem.Offset + nextCmd, bytes); } else { needBounce = true; } } if (needBounce) { fifo.usingBounceEffect = true; //DebugUtil.SendMessage("VMWare", "FIFOReserve in bounce buffer"); return fifo.bounceBuffer; } } // while } private void FIFOFull() { // No support for interrupts yet, so lets just handle it the old way for now writeSVGARegister(SVGA_REG.SYNC, 1); readSVGARegister(SVGA_REG.BUSY); } private void writeSVGARegister(SVGA_REG register, uint value) { ioBase.Write32(SVGA_INDEX_PORT, (uint)register); ioBase.Write32(SVGA_VALUE_PORT, value); } private uint readSVGARegister(SVGA_REG register) { ioBase.Write32(SVGA_INDEX_PORT, (uint)register); return ioBase.Read32(SVGA_VALUE_PORT); } private bool hasFIFOCap(SVGA_CAP cap) { return ((this.fifoMem.Read32(16) & (uint)cap) != 0); // Read FIFO_CAPABILITIES to check } private bool hasFIFOCap(SVGA_FIFO_CAP fifo_cap) { return ((this.fifoMem.Read32(16) & (uint)fifo_cap) != 0); // Read FIFO_CAPABILITIES to check } private bool isFIFORegValid(uint reg) { return (fifoMem.Read32(0) > reg); // Read FIFO_MIN to check } } }