/// <summary> /// Returns a single row (byte) in the character data. /// </summary> /// <param name="CharacterCode">Character code</param> /// <param name="Row">Row in glpyh</param> /// <returns></returns> public byte Read(int CharacterCode, int Row) { if (CharacterData == null) { return(0); } int addr = StartAddress + CharacterCode * charHeight + Row; return(CharacterData.ReadByte(addr)); }
public static int[] LoadLUT(MemoryRAM VKY) { // Read the color lookup tables int lutAddress = MemoryMap.GRP_LUT_BASE_ADDR - MemoryMap.VICKY_BASE_ADDR; int lookupTables = 4; int[] result = new int[lookupTables * 256]; for (int c = 0; c < lookupTables * 256; c++) { byte blue = VKY.ReadByte(lutAddress++); byte green = VKY.ReadByte(lutAddress++); byte red = VKY.ReadByte(lutAddress++); lutAddress++; // skip the alpha channel result[c] = (0xFF << 24) + (red << 16) + (green << 8) + blue; } return(result); }
private void DrawBitmap(ref BitmapData bd, bool bkgrnd) { // Bitmap Controller is located at $AF:0140 int reg = VICKY.ReadByte(MemoryMap.BITMAP_CONTROL_REGISTER_ADDR - MemoryMap.VICKY_BASE_ADDR); if ((reg & 0x01) == 00) { return; } int lutIndex = (reg & 14) >> 1; // 8 possible LUTs int bitmapAddress = VICKY.ReadLong(0xAF_0141 - MemoryMap.VICKY_BASE_ADDR); int width = VICKY.ReadWord(0xAF_0144 - MemoryMap.VICKY_BASE_ADDR); int height = VICKY.ReadWord(0xAF_0146 - MemoryMap.VICKY_BASE_ADDR); int borderXSize = VICKY.ReadByte(0xAF_0008 - MemoryMap.VICKY_BASE_ADDR); int borderYSize = VICKY.ReadByte(0xAF_0009 - MemoryMap.VICKY_BASE_ADDR); //BitmapData bitmapData = frameBuffer.LockBits(new Rectangle(0,0,640, 480), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); IntPtr p = bd.Scan0; int value = 0; for (int line = borderYSize; line < (height - borderYSize); line++) { for (int col = borderXSize; col < (width - borderXSize); col++) { value = graphicsLUT[lutIndex * 256 + VRAM.ReadByte(bitmapAddress + col + line * 640)]; if (gammaCorrection != null) { value = (int)((gammaCorrection[(value & 0x00FF0000) >> 0x10] << 0x10) + (gammaCorrection[0x100 + ((value & 0x0000FF00) >> 0x08)] << 0x08) + (gammaCorrection[0x200 + (value & 0x000000FF)]) + 0xFF000000); } System.Runtime.InteropServices.Marshal.WriteInt32(p, (line * width + col) * 4, value); } } }
private unsafe void DrawTiles(int *p, bool gammaCorrection, byte TextColumns, int layer, bool bkgrnd, int borderXSize, int line, int width) { // There are four possible tilemaps to choose from int addrTileCtrlReg = MemoryMap.TILE_CONTROL_REGISTER_ADDR + layer * 12 - MemoryMap.VICKY_BASE_ADDR; int reg = VICKY.ReadByte(addrTileCtrlReg); // if the set is not enabled, we're done. if ((reg & 0x01) == 00) { return; } int tilemapWidth = VICKY.ReadWord(addrTileCtrlReg + 4) & 0x3FF; // 10 bits int tilemapHeight = VICKY.ReadWord(addrTileCtrlReg + 6) & 0x3FF; // 10 bits int tilemapAddress = VICKY.ReadLong(addrTileCtrlReg + 1); int tilemapWindowX = VICKY.ReadWord(addrTileCtrlReg + 8); bool dirUp = (tilemapWindowX & 0x4000) != 0; byte scrollX = (byte)(tilemapWindowX & 0x3C00 >> 10); tilemapWindowX &= 0x3FF; byte tileXOffset = (byte)(tilemapWindowX % TILE_SIZE); int tilemapWindowY = VICKY.ReadWord(addrTileCtrlReg + 10); bool dirRight = (tilemapWindowY & 0x4000) != 0; byte scrollY = (byte)(tilemapWindowY & 0x3C00 >> 10); tilemapWindowY &= 0x3FF; int tileRow = (line + tilemapWindowY) / TILE_SIZE; int tileYOffset = (line + tilemapWindowY) % TILE_SIZE; // we always read tiles 0 to width/TILE_SIZE + 1 - this is to ensure we can display partial tiles, with X,Y offsets int tilemapItemCount = width / TILE_SIZE + 1; byte[] tiles = new byte[tilemapItemCount * 2]; int[] tilesetOffsets = new int[tilemapItemCount]; VRAM.CopyIntoBuffer(tilemapAddress + (1 + tilemapWindowX / TILE_SIZE) * 2 + (tileRow + 0) * tilemapWidth * 2, tiles, 0, tilemapItemCount * 2); // cache of tilesetPointers int[] tilesetPointers = new int[8]; int[] strides = new int[8]; for (int i = 0; i < 8; i++) { tilesetPointers[i] = VICKY.ReadLong(MemoryMap.TILESET_BASE_ADDR - MemoryMap.VICKY_BASE_ADDR + i * 4); byte tilesetConfig = VICKY.ReadByte(MemoryMap.TILESET_BASE_ADDR - MemoryMap.VICKY_BASE_ADDR + i * 4 + 3); strides[i] = (tilesetConfig & 8) != 0 ? 256 : 1; } for (int i = 0; i < tilemapItemCount; i++) { byte tile = tiles[i * 2]; byte tilesetReg = tiles[i * 2 + 1]; byte tileset = (byte)(tilesetReg & 7); //byte tileLUT = (byte)((tilesetReg & 0x38) >> 3); // tileset int tilesetPointer = tilesetPointers[tileset]; int strideX = strides[tileset]; tilesetOffsets[i] = tilesetPointer + ((tile / TILE_SIZE) * strideX * TILE_SIZE + (tile % TILE_SIZE) * TILE_SIZE) + tileYOffset * strideX; } int *ptr = p + line * STRIDE; // Ensure that only one line gets drawn, this avoid incorrect wrapping for (int x = borderXSize; x < width - borderXSize; x++) { int tileIndex = (x + tileXOffset) / TILE_SIZE; //byte tile = tiles[tileIndex]; byte tilesetReg = tiles[tileIndex * 2 + 1]; //byte tileset = (byte)(tilesetReg & 7); byte tileLUT = (byte)((tilesetReg & 0x38) >> 3); // tileset //int tilesetPointer = tilesetPointers[tileset]; //int strideX = strides[tileset]; int tilesetOffsetAddress = tilesetOffsets[tileIndex] + (x + tilemapWindowX) % TILE_SIZE; //((tile / TILE_SIZE) * strideX * TILE_SIZE + (tile % TILE_SIZE) * TILE_SIZE) + tileYOffset * strideX + (x + tilemapWindowX) % TILE_SIZE; //byte pixelIndex = VRAM.ReadByte(tilesetPointer + tilesetOffsetAddress ); byte pixelIndex = VRAM.ReadByte(tilesetOffsetAddress); if (pixelIndex > 0) { int value = GetLUTValue(tileLUT, pixelIndex, gammaCorrection); ptr[x] = value; } } }
private unsafe void DrawBitmapText(int *p, int MCR, bool gammaCorrection, byte TextColumns, byte TextRows, int colOffset, int rowOffset, int line, int width, int height) { bool overlayBitSet = (MCR & 0x02) == 0x02; int lineStartAddress = MemoryLocations.MemoryMap.SCREEN_PAGE0 - VICKY.StartAddress; int colorStartAddress = MemoryLocations.MemoryMap.SCREEN_PAGE1 - VICKY.StartAddress; int fontBaseAddress = MemoryLocations.MemoryMap.FONT0_MEMORY_BANK_START - VICKY.StartAddress; // Find which line of characters to display int txtline = (line - rowOffset) / CHAR_HEIGHT; //if (txtline + 1 > RAM.ReadByte(MemoryMap.LINES_MAX)) return; //byte COLS_PER_LINE = RAM.ReadByte(MemoryMap.COLS_PER_LINE); // Initialize the LUTs FGTextLUT = new int[16]; BGTextLUT = new int[16]; // Cursor Values byte CursorY = RAM.ReadByte(MemoryMap.CURSORY); byte CursorX = RAM.ReadByte(MemoryMap.CURSORX); bool CursorEnabled = (VICKY.ReadByte(MemoryMap.VKY_TXT_CURSOR_CTRL_REG - MemoryMap.VICKY_START) & 1) != 0; // Each character is defined by 8 bytes int fontLine = (line - rowOffset) % CHAR_HEIGHT; int *ptr = p + line * STRIDE + colOffset; for (int col = 0; col < width / CHAR_WIDTH; col++) { int x = col * CHAR_WIDTH; if (x + colOffset > width - 1 - colOffset) { continue; } int offset = 0; if (col < TextColumns) { offset = TextColumns * txtline + col; } int textAddr = lineStartAddress + offset; int colorAddr = colorStartAddress + offset; // Each character will have foreground and background colors byte character = VICKY.ReadByte(textAddr); byte color = VICKY.ReadByte(colorAddr); // Display the cursor if (CursorX == col && CursorY == txtline && CursorState && CursorEnabled) { character = VICKY.ReadByte(MemoryLocations.MemoryMap.VKY_TXT_CURSOR_CHAR_REG - VICKY.StartAddress); } byte fgColor = (byte)((color & 0xF0) >> 4); byte bgColor = (byte)(color & 0x0F); int[] textColors = GetTextLUT(fgColor, bgColor, gammaCorrection); byte value = VICKY.ReadByte(fontBaseAddress + character * 8 + fontLine); //int offset = (x + line * 640) * 4; // For each bit in the font, set the foreground color - if the bit is 0 and overlay is set, skip it (keep the background) for (int b = 0x80; b > 0; b = b >> 1) { if ((value & b) != 0) { //System.Runtime.InteropServices.Marshal.WriteInt32(p, offset, fgValue); ptr[0] = textColors[0]; } else if (!overlayBitSet) { //System.Runtime.InteropServices.Marshal.WriteInt32(p, offset, bgValue); ptr[0] = textColors[1]; } ptr++; //offset += 4; } } }
public Point GetScreenSize() { byte MCRHigh = (byte)(VICKY.ReadByte(1) & 3); // Reading address $AF:0001 Point p = new Point(640, 480); switch (MCRHigh) { case 1: p.X = 800; p.Y = 600; break; case 2: p.X = 320; p.Y = 240; break; case 3: p.X = 400; p.Y = 300; break; } return(p); }
void Gpu_Paint(object sender, PaintEventArgs e) { paintCycle++; if (DesignMode) { e.Graphics.DrawString("Design Mode", this.Font, TextBrush, 0, 0); return; } if (VICKY == null) { e.Graphics.DrawString("IO Memory Not Initialized", this.Font, TextBrush, 0, 0); return; } if (VRAM == null) { e.Graphics.DrawString("VRAM Not Initialized", this.Font, TextBrush, 0, 0); return; } if (RAM == null) { e.Graphics.DrawString("RAM Not Initialized", this.Font, TextBrush, 0, 0); return; } // Read the Master Control Register byte MCRegister = VICKY.ReadByte(0); // Reading address $AF:0000 int top = 0; // top gets modified if error messages are displayed Graphics g = Graphics.FromImage(frameBuffer); if (MCRegister == 0 || (MCRegister & 0x80) == 0x80) { e.Graphics.DrawString("Graphics Mode disabled", this.Font, TextBrush, 0, 0); return; } else if ((MCRegister & 0x1) == 0x1) { if (ColumnsVisible < 1 || ColumnsVisible > MAX_TEXT_COLS) { DrawTextWithBackground("ColumnsVisible invalid:" + ColumnsVisible.ToString(), g, Color.Black, 0, top); top += 12; } if (LinesVisible < 1 || LinesVisible > MAX_TEXT_LINES) { DrawTextWithBackground("LinesVisible invalid:" + LinesVisible.ToString(), g, Color.Black, 0, top); top += 12; } } if (drawing) { // drop the frame return; } drawing = true; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); e.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed; e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed; // Determine if we display a border int border_register = VICKY.ReadByte(4); bool displayBorder = (border_register & 1) == 1; int colOffset = VICKY.ReadByte(8); int rowOffset = VICKY.ReadByte(9); // Load Graphical LUTs graphicsLUT = LoadLUT(VICKY); // Apply gamma correct if ((MCRegister & 0x40) == 0x40) { gammaCorrection = LoadGammaCorrection(VICKY); } else { gammaCorrection = null; } // Default background color to border color // In Text mode, the border color is stored at $AF:0005. byte borderRed = VICKY.ReadByte(5); byte borderGreen = VICKY.ReadByte(6); byte borderBlue = VICKY.ReadByte(7); if (gammaCorrection != null) { borderRed = gammaCorrection[0x200 + borderRed]; borderGreen = gammaCorrection[0x100 + borderGreen]; borderBlue = gammaCorrection[borderBlue]; } int borderColor = (int)(0xFF000000 + (borderBlue << 16) + (borderGreen << 8) + borderRed); if (tileEditorMode) { g.Clear(Color.LightGray); DrawTextWithBackground("Tile Editing Mode", g, Color.Black, 240, 10); DrawTextWithBackground("Tile Editing Mode", g, Color.Black, 240, 455); } else { g.Clear(Color.FromArgb(borderColor)); } // Graphics Mode if ((MCRegister & 0x4) == 0x4) { byte backRed = VICKY.ReadByte(0xD); byte backGreen = VICKY.ReadByte(0XE); byte backBlue = VICKY.ReadByte(0xF); if (gammaCorrection != null) { backRed = gammaCorrection[0x200 + backRed]; backGreen = gammaCorrection[0x100 + backGreen]; backBlue = gammaCorrection[backBlue]; } int backgroundColor = (int)(0xFF000000 + (backBlue << 16) + (backGreen << 8) + backRed); Brush graphBackgroundBrush = new SolidBrush(Color.FromArgb(backgroundColor)); g.FillRectangle(graphBackgroundBrush, colOffset, rowOffset, 640 - 2 * colOffset, 480 - 2 * rowOffset); } Rectangle rect = new Rectangle(0, 0, 640, 480); BitmapData bitmapData = frameBuffer.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); // Bitmap Mode if ((MCRegister & 0x4) == 0x4 || tileEditorMode) { if ((MCRegister & 0x8) == 0x8) { DrawBitmap(ref bitmapData, displayBorder); } for (int layer = 4; layer > 0; --layer) { if ((MCRegister & 0x10) == 0x10) { DrawTiles(ref bitmapData, layer - 1, 640, displayBorder); } if ((MCRegister & 0x20) == 0x20) { DrawSprites(ref bitmapData, layer - 1, 640, displayBorder); } } } if ((MCRegister & 7) == 0x1 || (MCRegister & 7) == 3 || (MCRegister & 7) == 7) { if (top == 0) { DrawBitmapText(ref bitmapData, colOffset, rowOffset); } } byte mouseReg = VICKY.ReadByte(0x700); MousePointerMode = (mouseReg & 1) == 1; if (MousePointerMode && !TileEditorMode) { DrawMouse(ref bitmapData, 640); } frameBuffer.UnlockBits(bitmapData); e.Graphics.DrawImage(frameBuffer, ClientRectangle); drawing = false; stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; if (ts.Milliseconds > 10) { Console.WriteLine("Drawing Time: " + ts.Milliseconds + "ms"); } // This seems to make the rendering much better. Do we care if this is 1 frame off? if (MCRegister != 0 && MCRegister != 0x80) { StartOfFrame?.Invoke(); } }
private void DrawVectorText(Graphics g) { float x; float y; if (TextFont == null) { TextFont = GetBestFont(); } SizeF charSize = MeasureFont(TextFont, g); float charWidth = charSize.Width / MEASURE_STRING.Length; float charHeight = charSize.Height; float RightCol = charWidth * ColumnsVisible; float BottomRow = charWidth * LinesVisible; float ScaleX = this.ClientRectangle.Width / RightCol; float ScaleY = this.ClientRectangle.Height / BottomRow; g.ResetTransform(); g.ScaleTransform(ScaleX, ScaleY); if (VRAM == null) { return; } int col = 0, row = 0; for (int i = 0; i < BufferSize; i++) { if (col < ColumnsVisible) { x = col * charWidth; y = row * charHeight; char c = (char)VRAM.ReadByte(i); g.DrawString(c.ToString(), this.Font, TextBrush, x, y, StringFormat.GenericTypographic); } col++; if (col >= COLS_PER_LINE) { col = 0; row++; } } //if (CursorState && CursorEnabled) //{ // x = X * charWidth; // y = Y * charHeight; // g.FillRectangle(CursorBrush, x, y, charWidth, charHeight); // g.DrawString(CharacterData[GetCharPos(Y, X)].ToString(), // TextFont, // InvertedBrush, // x, y, // StringFormat.GenericTypographic); //} }