/// <summary> /// Draw the frame buffer to the screen. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> unsafe void Gpu_Paint(object sender, PaintEventArgs e) { paintCycle++; if (DesignMode) { e.Graphics.DrawString("Design Mode", this.Font, TextBrush, 0, 0); return; } // Read the Master Control Register byte MCRegister = VICKY.ReadByte(0); // Reading address $AF:0000 byte MCRHigh = (byte)(VICKY.ReadByte(1) & 3); // Reading address $AF:0001 int resX = 640; int resY = 480; bool isPixelDoubled = false; switch (MCRHigh) { case 1: resX = 800; resY = 600; break; case 2: resX = 320; resY = 240; isPixelDoubled = true; break; case 3: resX = 400; resY = 300; isPixelDoubled = true; break; } pixVals = new byte[resX]; int top = 0; // top gets modified if error messages are displayed //Graphics g = Graphics.FromImage(frameBuffer); Graphics g = e.Graphics; byte ColumnsVisible = (byte)(resX / CHAR_WIDTH); //byte ColumnsVisible = RAM.ReadByte(MemoryMap.COLS_VISIBLE); byte LinesVisible = (byte)(resY / CHAR_HEIGHT); //byte LinesVisible = RAM.ReadByte(MemoryMap.LINES_VISIBLE); if (MCRegister == 0 || (MCRegister & 0x80) == 0x80) { g.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 System.Console.WriteLine("Skipped Frame"); return; } drawing = true; // Check if SOF is enabled if (MCRegister != 0 && MCRegister != 0x80) { StartOfFrame?.Invoke(); } g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed; // Bilinear interpolation has effect very similar to real HW g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; //e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed; // Determine if we display a border byte border_register = VICKY.ReadByte(MemoryMap.BORDER_CTRL_REG - MemoryMap.VICKY_BASE_ADDR); bool displayBorder = (border_register & 1) != 0; int borderXSize = displayBorder ? VICKY.ReadByte(MemoryMap.BORDER_X_SIZE - MemoryMap.VICKY_BASE_ADDR) : 0; int borderYSize = displayBorder ? VICKY.ReadByte(MemoryMap.BORDER_Y_SIZE - MemoryMap.VICKY_BASE_ADDR) : 0; //this may get corrected in Vicky in the near future. // if (isPixelDoubled) //{ // borderXSize >>= 1; // divide by 2 // borderYSize >>= 1; // divide by 2 //} //Rectangle rect = new Rectangle(0, 0, resX, resY); Rectangle rect = new Rectangle(0, 0, resX - 1, resY - 1); BitmapData bitmapData = frameBuffer.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); int * bitmapPointer = (int *)bitmapData.Scan0.ToPointer(); // Load the SOL register - a lines int SOLRegAddr = MemoryMap.VKY_LINE_IRQ_CTRL_REG - MemoryMap.VICKY_BASE_ADDR; int SOLLine0Addr = MemoryMap.VKY_LINE0_CMP_VALUE_LO - MemoryMap.VICKY_BASE_ADDR; int SOLLine1Addr = MemoryMap.VKY_LINE1_CMP_VALUE_LO - MemoryMap.VICKY_BASE_ADDR; // Reset LUT Cache lutCache = new int[256 * 8]; // 8 LUTs for (int line = 0; line < resY; line++) { // Handle SOL interrupts byte SOLRegister = VICKY.ReadByte(SOLRegAddr); if ((SOLRegister & 1) != 0) { int SOLLine0 = VICKY.ReadWord(SOLLine0Addr); if (line == SOLLine0) { StartOfLine?.Invoke(); } } if ((SOLRegister & 2) != 0) { int SOLLine1 = VICKY.ReadWord(SOLLine1Addr); if (line == SOLLine1) { StartOfLine?.Invoke(); } } bool gammaCorrection = (MCRegister & 0x40) == 0x40; // 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) { borderRed = VICKY.ReadByte(MemoryMap.GAMMA_BASE_ADDR - MemoryMap.VICKY_BASE_ADDR + 0x200 + borderRed); //gammaCorrection[0x200 + borderGreen]; borderGreen = VICKY.ReadByte(MemoryMap.GAMMA_BASE_ADDR - MemoryMap.VICKY_BASE_ADDR + 0x100 + borderGreen); //gammaCorrection[0x100 + borderGreen]; borderBlue = VICKY.ReadByte(MemoryMap.GAMMA_BASE_ADDR - MemoryMap.VICKY_BASE_ADDR + borderBlue); // gammaCorrection[borderBlue]; } int borderColor = (int)(0xFF000000 + (borderBlue << 16) + (borderGreen << 8) + borderRed); if (tileEditorMode) { borderColor = Color.LightGray.ToArgb(); } //int offset = line * 640; int *ptr = bitmapPointer + line * STRIDE; if (line < borderYSize || line >= resY - borderYSize) { for (int x = 0; x < resX; x++) { //System.Runtime.InteropServices.Marshal.WriteInt32(bitmapPointer, (offset + x) * 4, borderColor); ptr[x] = borderColor; } } else { // Graphics Mode int backgroundColor = unchecked ((int)0xFF000000); if ((MCRegister & 0x4) == 0x4) { byte backRed = VICKY.ReadByte(MemoryMap.BACKGROUND_COLOR_B - MemoryMap.VICKY_BASE_ADDR); byte backGreen = VICKY.ReadByte(MemoryMap.BACKGROUND_COLOR_G - MemoryMap.VICKY_BASE_ADDR); byte backBlue = VICKY.ReadByte(MemoryMap.BACKGROUND_COLOR_R - MemoryMap.VICKY_BASE_ADDR); if (gammaCorrection) { backRed = VICKY.ReadByte(MemoryMap.GAMMA_BASE_ADDR - MemoryMap.VICKY_BASE_ADDR + 0x200 + backRed); // gammaCorrection[0x200 + backRed]; backGreen = VICKY.ReadByte(MemoryMap.GAMMA_BASE_ADDR - MemoryMap.VICKY_BASE_ADDR + 0x100 + backGreen); //gammaCorrection[0x100 + backGreen]; backBlue = VICKY.ReadByte(MemoryMap.GAMMA_BASE_ADDR - MemoryMap.VICKY_BASE_ADDR + backBlue); //gammaCorrection[backBlue]; } backgroundColor = (int)(0xFF000000 + (backBlue << 16) + (backGreen << 8) + backRed); } for (int x = 0; x < resX; x++) { int resetValue = x < borderXSize || x >= resX - borderXSize ? borderColor : backgroundColor; ptr[x] = resetValue; } // Bitmap Mode - draw the layers in revers order from back to front if ((MCRegister & 0x4) == 0x4 || tileEditorMode) { // Layer 12 - sprite layer 6 if ((MCRegister & 0x20) != 0) { DrawSprites(bitmapPointer, gammaCorrection, 6, displayBorder, borderXSize, borderYSize, line, resX, resY); } // Layer 11 - bitmap 1 if ((MCRegister & 0x8) == 0x8) { DrawBitmap(bitmapPointer, gammaCorrection, 1, displayBorder, backgroundColor, borderXSize, borderYSize, line, resX, resY); } // Layer 10 - sprite layer 5 if ((MCRegister & 0x20) != 0) { DrawSprites(bitmapPointer, gammaCorrection, 5, displayBorder, borderXSize, borderYSize, line, resX, resY); } // Layer 9 - tilemap layer 3 if ((MCRegister & 0x10) == 0x10) { DrawTiles(bitmapPointer, gammaCorrection, ColumnsVisible, 3, displayBorder, borderXSize, line, resX); } // Layer 8 - sprite layer 4 if ((MCRegister & 0x20) != 0) { DrawSprites(bitmapPointer, gammaCorrection, 4, displayBorder, borderXSize, borderYSize, line, resX, resY); } // Layer 7 - tilemap layer 2 if ((MCRegister & 0x10) == 0x10) { DrawTiles(bitmapPointer, gammaCorrection, ColumnsVisible, 2, displayBorder, borderXSize, line, resX); } // Layer 6 - sprite layer 3 if ((MCRegister & 0x20) != 0) { DrawSprites(bitmapPointer, gammaCorrection, 3, displayBorder, borderXSize, borderYSize, line, resX, resY); } // Layer 5 - tilemap layer 1 if ((MCRegister & 0x10) == 0x10) { DrawTiles(bitmapPointer, gammaCorrection, ColumnsVisible, 1, displayBorder, borderXSize, line, resX); } // Layer 4 - sprite layer 2 if ((MCRegister & 0x20) != 0) { DrawSprites(bitmapPointer, gammaCorrection, 2, displayBorder, borderXSize, borderYSize, line, resX, resY); } // Layer 3 - tilemap layer 0 if ((MCRegister & 0x10) == 0x10) { DrawTiles(bitmapPointer, gammaCorrection, ColumnsVisible, 0, displayBorder, borderXSize, line, resX); } // Layer 2 - sprite layer 1 if ((MCRegister & 0x20) != 0) { DrawSprites(bitmapPointer, gammaCorrection, 1, displayBorder, borderXSize, borderYSize, line, resX, resY); } // Layer 1 - bitmap layer 0 if ((MCRegister & 0x8) == 0x8) { DrawBitmap(bitmapPointer, gammaCorrection, 0, displayBorder, backgroundColor, borderXSize, borderYSize, line, resX, resY); } // Layer 0 - sprite layer 0 if ((MCRegister & 0x20) != 0) { DrawSprites(bitmapPointer, gammaCorrection, 0, displayBorder, borderXSize, borderYSize, line, resX, resY); } } // Draw the text if ((MCRegister & 7) == 0x1 || (MCRegister & 7) == 3 || (MCRegister & 7) == 7) { if (top == 0) { DrawBitmapText(bitmapPointer, MCRegister, gammaCorrection, ColumnsVisible, LinesVisible, borderXSize, borderYSize, line, resX, resY); } } } if (!TileEditorMode) { DrawMouse(bitmapPointer, gammaCorrection, line, resX, resY); } } frameBuffer.UnlockBits(bitmapData); g.DrawImage(frameBuffer, ClientRectangle, rect, GraphicsUnit.Pixel); //e.Graphics.DrawImageUnscaled(frameBuffer, rect); // Use this to debug drawing = false; }
private void DrawTiles(ref BitmapData bd, int layer, int bitmapWidth, bool bkgrnd) { // There are four possible tilesets to choose from int addrTileset = MemoryMap.TILE_CONTROL_REGISTER_ADDR + layer * 8; int reg = VICKY.ReadByte(addrTileset - MemoryMap.VICKY_BASE_ADDR); // if the set is not enabled, we're done. if ((reg & 0x01) == 00) { return; } // This is hard coded for now int lines = 52; int lutIndex = (reg & 14) >> 1; // 8 possible LUTs bool striding = (reg & 0x80) == 0x80; int tilesetAddress = VICKY.ReadLong(addrTileset + 1 - MemoryMap.VICKY_BASE_ADDR); int strideX = striding ? 256 : VICKY.ReadWord(addrTileset + 4 - MemoryMap.VICKY_BASE_ADDR); int strideY = VICKY.ReadWord(addrTileset + 6 - MemoryMap.VICKY_BASE_ADDR); // Now read the tilemap int tilemapAddress = 0xAF5000 + 0x800 * layer; IntPtr p = bd.Scan0; int colOffset = bkgrnd ? (80 - ColumnsVisible) / 2 * charWidth / tileSize: 0; int lineOffset = bkgrnd ? (60 - lines) / 2 * charHeight / tileSize : 0; int borderXSize = VICKY.ReadByte(0xAF_0008 - MemoryMap.VICKY_BASE_ADDR); int borderYSize = VICKY.ReadByte(0xAF_0009 - MemoryMap.VICKY_BASE_ADDR); for (int tileRow = lineOffset; tileRow < (30 - lineOffset); tileRow++) { if (tileRow * 16 < borderYSize || tileRow * 16 > (480 - borderYSize)) { continue; } for (int tileCol = colOffset; tileCol < (40 - colOffset); tileCol++) { if (tileCol * 16 < borderXSize || (tileCol + 1) * 16 > (640 - borderXSize)) { continue; } int tile = VICKY.ReadByte(tilemapAddress + tileCol + tileRow * 64 - MemoryMap.VICKY_BASE_ADDR); int pixelIndex = 0; int value = 0; // Tiles are 16 x 16 for (int line = 0; line < 16; line++) { int offset = tilesetAddress + ((tile / 16) * 256 * 16 + (tile % 16) * 16) + line * strideX; for (int col = 0; col < 16; col++) { // Lookup the pixel in the tileset pixelIndex = VRAM.ReadByte(offset + col); if (pixelIndex != 0) { value = (int)graphicsLUT[lutIndex * 256 + pixelIndex]; if (gammaCorrection != null) { //value = (int)((blue << 16) + (green << 8) + red + 0xFF000000); 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 * bitmapWidth + col + tileCol * 16 + tileRow * 16 * 640) * 4, value); } } } } } }