// Debug only public void RenderFullBgToImage(Bitmap bmp, bool renderViewPortBox, int bgSelect) { if (bgSelect == -1) { bgSelect = MemoryRegisters.LCDC.BgTileMapSelect; } TileMap tileMap = TileMaps[bgSelect]; for (int y = 0; y < 256; y++) { for (int x = 0; x < 256; x++) { Tile tile = tileMap.TileFromXY((byte)(x), (byte)(y)); bmp.SetPixel(x, y, Palettes.BackgroundPalette[tile.renderTile[x % 8, y % 8]]); } } if (renderViewPortBox) { byte viewPortX; byte viewPortY; // Where are we viewing the logical 256x256 tile map? viewPortX = MemoryRegisters.BgScrollX; viewPortY = MemoryRegisters.BgScrollY; Pen pen = new Pen(Color.RoyalBlue, 1.0f); using (var graphics = Graphics.FromImage(bmp)) { int x1 = viewPortX; int x2 = viewPortX + Screen_X_Resolution; int y1 = viewPortY; int y2 = viewPortY + Screen_Y_Resolution; // Each side can take 2 lines to draw if it wraps int adjustX2 = x2; if (x2 >= BG_Width_Pixels) { adjustX2 = x2 - BG_Width_Pixels; } int adjustY2 = y2; if (y2 >= BG_Height_Pixels) { adjustY2 = y2 - BG_Height_Pixels; } // Top of rect (can go off end of image) graphics.DrawLine(pen, x1, y1, x2, y1); if (x2 != adjustX2) { graphics.DrawLine(pen, 0, y1, adjustX2, y1); } // Bottom of rect graphics.DrawLine(pen, x1, adjustY2, x2, adjustY2); if (x2 != adjustX2) { graphics.DrawLine(pen, 0, adjustY2, adjustX2, adjustY2); } // Left of rect (can go off end of image) graphics.DrawLine(pen, x1, y1, x1, y2); if (y2 != adjustY2) { graphics.DrawLine(pen, x1, 0, x1, adjustY2); } // Right graphics.DrawLine(pen, adjustX2, y1, adjustX2, y2); if (y2 != adjustY2) { graphics.DrawLine(pen, adjustX2, 0, adjustX2, adjustY2); } } } }
// Gamebopy screen resolution = 160x144 void RenderScanline() { /* * // Very temporary * int offset = 0; * foreach (Tile t in Tiles) * { * t.Parse(Memory.VRam, offset); * offset += 16; * } */ PpuAccessingVram = true; // Render the BG // Total BG size in VRam is 32x32 tiles // Viewport is 20x18 tiles if (MemoryRegisters.LCDC.BgWinDisplay == 1) { TileMap tileMap = TileMaps[MemoryRegisters.LCDC.BgTileMapSelect]; byte screenY = CurrentScanline; // Where are we viewing the logical 256x256 tile map? byte viewPortX = MemoryRegisters.BgScrollX; byte viewPortY = MemoryRegisters.BgScrollY; int bgY = viewPortY + screenY; if (bgY >= 256) { bgY -= 256; } // What row are we rendering within a tile? byte tilePixelY = (byte)(bgY % 8); for (byte screenX = 0; screenX < Screen_X_Resolution; screenX++) { int bgX = viewPortX + screenX; if (bgX >= 256) { bgX -= 256; } // What column are we rendering within a tile? byte tilePixelX = (byte)(bgX % 8); Tile tile = tileMap.TileFromXY((byte)(bgX), (byte)(bgY)); drawBuffer.SetPixel(screenX, screenY, Palettes.BackgroundPalette[tile.renderTile[tilePixelX, tilePixelY]]); } } // Render Window if (MemoryRegisters.LCDC.WindowDisplay == 1 && MemoryRegisters.WindowX < Screen_X_Resolution && MemoryRegisters.WindowY < Screen_Y_Resolution) { if (CurrentScanline >= MemoryRegisters.WindowY) { TileMap tileMap = TileMaps[MemoryRegisters.LCDC.WindowTileMapSelect]; // Window X, Y tell you where on screen to start drawing the tiles found at 0,0 in the tilemap. // The Window DOES NOT WRAP // WindowX draws -7 pixels from its actual value byte windowScreenSpaceX = MemoryRegisters.WindowX; byte windowScreenSpaceY = MemoryRegisters.WindowY; int windowScreenSpaceXAdjusted = windowScreenSpaceX - 7; // These track the X&Y in the tile map; byte windowDataX = 0; byte windowDataY = (byte)(CurrentScanline - windowScreenSpaceY); int tilePixelY = (windowDataY % 8); for (int x = windowScreenSpaceXAdjusted; x < Screen_X_Resolution; x++) { // Remember, this is window X adjusted by 7, so this is not wrapping if (windowScreenSpaceXAdjusted < 0) { // Because of -7, the window can be offscreen for a few pixels windowScreenSpaceXAdjusted++; continue; } // What column are we rendering within a tile? byte tilePixelX = (byte)(windowDataX % 8); Tile tile = tileMap.TileFromXY(windowDataX, windowDataY); drawBuffer.SetPixel(x, CurrentScanline, Palettes.BackgroundPalette[tile.renderTile[tilePixelX, tilePixelY]]); windowScreenSpaceXAdjusted++; windowDataX++; } } } // Skip sprite rendering this line if a dma transfer has occured midframe and stomped all over the OAM entries. // OAM entries will become 'clean' after next OAM search (next line). if (OamDirty == false) { // Render Sprites, we already know that they all are visible on this scanline and they are already ordered so that the right most is first foreach (var sprite in oamSearchResults) { byte sx = sprite.X; byte sy = sprite.Y; // can be negative int spriteXScreenSpace = sx - 8; int spriteYScreenSpace = sy - 16; // Which row of the sprite is being rendered on this line? int spriteYLine = CurrentScanline - spriteYScreenSpace; Color[] palette = Palettes.ObjPalette0; if (sprite.PaletteNumber == 1) { palette = Palettes.ObjPalette1; } Tile tile = null; if (MemoryRegisters.LCDC.SpriteHeight == 0) { tile = GetSpriteTileByIndex(sprite.TileIndex); } // if using 16 pixel high sprites (2 tiles) then potentially adjust to the next tile and fix up the line if we are drawing the sewcond tile // The tiles themselves also index opposite when Y flipped else { if (spriteYLine >= 8) { if (sprite.YFlip == false) { tile = GetSpriteTileByIndex((byte)(sprite.TileIndex + 1)); } else { tile = GetSpriteTileByIndex((byte)(sprite.TileIndex)); } spriteYLine -= 8; } else { if (sprite.YFlip == false) { tile = GetSpriteTileByIndex((byte)(sprite.TileIndex)); } else { tile = GetSpriteTileByIndex((byte)(sprite.TileIndex + 1)); } } } for (int i = 0; i < 8; i++) { // Offscreen if (spriteXScreenSpace + i >= Screen_X_Resolution) { break; } if (spriteXScreenSpace + i < 0) { continue; } int sprPixelX = i; int sprPixelY = spriteYLine; if (sprite.XFlip) { sprPixelX = 7 - sprPixelX; } if (sprite.YFlip) { sprPixelY = 7 - sprPixelY; } byte paletteIndex = tile.renderTile[sprPixelX, sprPixelY]; // If the priority is 0, sprites redner on top. If it is 1 then sprite pixels only render on top of 'white' otherwise they are obscured if (sprite.ObjPriority == 1) { Color pixel = drawBuffer.GetPixel(spriteXScreenSpace + i, CurrentScanline); if (pixel != Palettes.BackgroundPalette[0]) { continue; } } // Palette entry 0 == translucent for sprites if (paletteIndex != 0) { drawBuffer.SetPixel(spriteXScreenSpace + i, CurrentScanline, palette[paletteIndex]); } } } } oamSearchResults.Clear(); PpuAccessingVram = false; }