/* * Gearboy - Nintendo Game Boy Emulator * Copyright (C) 2012 Ignacio Sanchez * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/ * */ #include "Video.h" #include "Memory.h" #include "Processor.h" Video::Video(Memory* pMemory, Processor* pProcessor) { m_pMemory = pMemory; m_pMemory->SetVideo(this); m_pProcessor = pProcessor; InitPointer(m_pFrameBuffer); InitPointer(m_pColorFrameBuffer); InitPointer(m_pSpriteXCacheBuffer); InitPointer(m_pColorCacheBuffer); m_iStatusMode = 0; m_iStatusModeCounter = 0; m_iStatusModeCounterAux = 0; m_iStatusModeLYCounter = 0; m_iScreenEnableDelayCycles = 0; m_iStatusVBlankLine = 0; m_iWindowLine = 0; m_iPixelCounter = 0; m_iTileCycleCounter = 0; m_bScreenEnabled = true; m_bCGB = false; m_bScanLineTransfered = false; m_iHideFrames = 0; m_IRQ48Signal = 0; m_pixelFormat = GB_PIXEL_RGB565; } Video::~Video() { SafeDeleteArray(m_pSpriteXCacheBuffer); SafeDeleteArray(m_pColorCacheBuffer); SafeDeleteArray(m_pFrameBuffer); } void Video::Init() { m_pFrameBuffer = new u8[GAMEBOY_WIDTH * GAMEBOY_HEIGHT]; m_pSpriteXCacheBuffer = new int[GAMEBOY_WIDTH * GAMEBOY_HEIGHT]; m_pColorCacheBuffer = new u8[GAMEBOY_WIDTH * GAMEBOY_HEIGHT]; Reset(false); } void Video::Reset(bool bCGB) { for (int i = 0; i < (GAMEBOY_WIDTH * GAMEBOY_HEIGHT); i++) m_pSpriteXCacheBuffer[i] = m_pFrameBuffer[i] = m_pColorCacheBuffer[i] = 0; for (int p = 0; p < 8; p++) for (int c = 0; c < 4; c++) { m_CGBBackgroundPalettes[p][c][0] = m_CGBSpritePalettes[p][c][0] = 0x0000; m_CGBBackgroundPalettes[p][c][1] = m_CGBSpritePalettes[p][c][1] = 0x0000; } m_iStatusMode = 1; m_iStatusModeCounter = 0; m_iStatusModeCounterAux = 0; m_iStatusModeLYCounter = 144; m_iScreenEnableDelayCycles = 0; m_iStatusVBlankLine = 0; m_iWindowLine = 0; m_iPixelCounter = 0; m_iTileCycleCounter = 0; m_bScreenEnabled = true; m_bScanLineTransfered = false; m_bCGB = bCGB; m_iHideFrames = 0; m_IRQ48Signal = 0; } bool Video::Tick(unsigned int &clockCycles, u16* pColorFrameBuffer, GB_Color_Format pixelFormat) { m_pColorFrameBuffer = pColorFrameBuffer; m_pixelFormat = pixelFormat; bool vblank = false; m_iStatusModeCounter += clockCycles; if (m_bScreenEnabled) { switch (m_iStatusMode) { // During H-BLANK case 0: { if (m_iStatusModeCounter >= 204) { m_iStatusModeCounter -= 204; m_iStatusMode = 2; m_iStatusModeLYCounter++; m_pMemory->Load(0xFF44, m_iStatusModeLYCounter); CompareLYToLYC(); if (m_bCGB && m_pMemory->IsHDMAEnabled() && (!m_pProcessor->Halted() || m_pProcessor->InterruptIsAboutToRaise())) { unsigned int cycles = m_pMemory->PerformHDMA(); m_iStatusModeCounter += cycles; clockCycles += cycles; } if (m_iStatusModeLYCounter == 144) { m_iStatusMode = 1; m_iStatusVBlankLine = 0; m_iStatusModeCounterAux = m_iStatusModeCounter; m_pProcessor->RequestInterrupt(Processor::VBlank_Interrupt); m_IRQ48Signal &= 0x09; u8 stat = m_pMemory->Retrieve(0xFF41); if (IsSetBit(stat, 4)) { if (!IsSetBit(m_IRQ48Signal, 0) && !IsSetBit(m_IRQ48Signal, 3)) { m_pProcessor->RequestInterrupt(Processor::LCDSTAT_Interrupt); } m_IRQ48Signal = SetBit(m_IRQ48Signal, 1); } m_IRQ48Signal &= 0x0E; if (m_iHideFrames > 0) m_iHideFrames--; else vblank = true; m_iWindowLine = 0; } else { m_IRQ48Signal &= 0x09; u8 stat = m_pMemory->Retrieve(0xFF41); if (IsSetBit(stat, 5)) { if (m_IRQ48Signal == 0) { m_pProcessor->RequestInterrupt(Processor::LCDSTAT_Interrupt); } m_IRQ48Signal = SetBit(m_IRQ48Signal, 2); } m_IRQ48Signal &= 0x0E; } UpdateStatRegister(); } break; } // During V-BLANK case 1: { m_iStatusModeCounterAux += clockCycles; if (m_iStatusModeCounterAux >= 456) { m_iStatusModeCounterAux -= 456; m_iStatusVBlankLine++; if (m_iStatusVBlankLine <= 9) { m_iStatusModeLYCounter++; m_pMemory->Load(0xFF44, m_iStatusModeLYCounter); CompareLYToLYC(); } } if ((m_iStatusModeCounter >= 4104) && (m_iStatusModeCounterAux >= 4) && (m_iStatusModeLYCounter == 153)) { m_iStatusModeLYCounter = 0; m_pMemory->Load(0xFF44, m_iStatusModeLYCounter); CompareLYToLYC(); } if (m_iStatusModeCounter >= 4560) { m_iStatusModeCounter -= 4560; m_iStatusMode = 2; UpdateStatRegister(); m_IRQ48Signal &= 0x07; m_IRQ48Signal &= 0x0A; u8 stat = m_pMemory->Retrieve(0xFF41); if (IsSetBit(stat, 5)) { if (m_IRQ48Signal == 0) { m_pProcessor->RequestInterrupt(Processor::LCDSTAT_Interrupt); } m_IRQ48Signal = SetBit(m_IRQ48Signal, 2); } m_IRQ48Signal &= 0x0D; } break; } // During searching OAM RAM case 2: { if (m_iStatusModeCounter >= 80) { m_iStatusModeCounter -= 80; m_iStatusMode = 3; m_bScanLineTransfered = false; m_IRQ48Signal &= 0x08; UpdateStatRegister(); } break; } // During transfering data to LCD driver case 3: { #ifndef PERFORMANCE if (m_iPixelCounter < 160) { m_iTileCycleCounter += clockCycles; u8 lcdc = m_pMemory->Retrieve(0xFF40); if (m_bScreenEnabled && IsSetBit(lcdc, 7)) { while (m_iTileCycleCounter >= 3) { if (IsValidPointer(m_pColorFrameBuffer)) { RenderBG(m_iStatusModeLYCounter, m_iPixelCounter); } m_iPixelCounter += 4; m_iTileCycleCounter -= 3; if (m_iPixelCounter >= 160) { break; } } } } #endif if (m_iStatusModeCounter >= 160 && !m_bScanLineTransfered) { ScanLine(m_iStatusModeLYCounter); m_bScanLineTransfered = true; } if (m_iStatusModeCounter >= 172) { m_iPixelCounter = 0; m_iStatusModeCounter -= 172; m_iStatusMode = 0; m_iTileCycleCounter = 0; UpdateStatRegister(); m_IRQ48Signal &= 0x08; u8 stat = m_pMemory->Retrieve(0xFF41); if (IsSetBit(stat, 3)) { if (!IsSetBit(m_IRQ48Signal, 3)) { m_pProcessor->RequestInterrupt(Processor::LCDSTAT_Interrupt); } m_IRQ48Signal = SetBit(m_IRQ48Signal, 0); } } break; } } } // Screen disabled else { if (m_iScreenEnableDelayCycles > 0) { m_iScreenEnableDelayCycles -= clockCycles; if (m_iScreenEnableDelayCycles <= 0) { m_iScreenEnableDelayCycles = 0; m_bScreenEnabled = true; m_iHideFrames = 3; m_iStatusMode = 0; m_iStatusModeCounter = 0; m_iStatusModeCounterAux = 0; m_iStatusModeLYCounter = 0; m_iWindowLine = 0; m_iStatusVBlankLine = 0; m_iPixelCounter = 0; m_iTileCycleCounter = 0; m_pMemory->Load(0xFF44, m_iStatusModeLYCounter); m_IRQ48Signal = 0; u8 stat = m_pMemory->Retrieve(0xFF41); if (IsSetBit(stat, 5)) { m_pProcessor->RequestInterrupt(Processor::LCDSTAT_Interrupt); m_IRQ48Signal = SetBit(m_IRQ48Signal, 2); } CompareLYToLYC(); } } else if (m_iStatusModeCounter >= 70224) { m_iStatusModeCounter -= 70224; vblank = true; } } return vblank; } void Video::EnableScreen() { if (!m_bScreenEnabled) { m_iScreenEnableDelayCycles = 244; } } void Video::DisableScreen() { m_bScreenEnabled = false; m_pMemory->Load(0xFF44, 0x00); u8 stat = m_pMemory->Retrieve(0xFF41); stat &= 0x7C; m_pMemory->Load(0xFF41, stat); m_iStatusMode = 0; m_iStatusModeCounter = 0; m_iStatusModeCounterAux = 0; m_iStatusModeLYCounter = 0; m_IRQ48Signal = 0; } bool Video::IsScreenEnabled() const { return m_bScreenEnabled; } const u8* Video::GetFrameBuffer() const { return m_pFrameBuffer; } void Video::UpdatePaletteToSpecification(bool background, u8 value) { bool hl = IsSetBit(value, 0); int index = (value >> 1) & 0x03; int pal = (value >> 3) & 0x07; u16 color = (background ? m_CGBBackgroundPalettes[pal][index][0] : m_CGBSpritePalettes[pal][index][0]); m_pMemory->Load(background ? 0xFF69 : 0xFF6B, hl ? (color >> 8) & 0xFF : color & 0xFF); } void Video::SetColorPalette(bool background, u8 value) { u8 ps = background ? m_pMemory->Retrieve(0xFF68) : m_pMemory->Retrieve(0xFF6A); bool hl = IsSetBit(ps, 0); int index = (ps >> 1) & 0x03; int pal = (ps >> 3) & 0x07; bool increment = IsSetBit(ps, 7); if (increment) { u8 address = ps & 0x3F; address++; address &= 0x3F; ps = (ps & 0x80) | address; m_pMemory->Load(background ? 0xFF68 : 0xFF6A, ps); UpdatePaletteToSpecification(background, ps); } u16* palette_color_gbc = background ? &m_CGBBackgroundPalettes[pal][index][0] : &m_CGBSpritePalettes[pal][index][0]; u16* palette_color_final = background ? &m_CGBBackgroundPalettes[pal][index][1] : &m_CGBSpritePalettes[pal][index][1]; *palette_color_gbc = hl ? (*palette_color_gbc & 0x00FF) | (value << 8) : (*palette_color_gbc & 0xFF00) | value; u8 red_5bit = *palette_color_gbc & 0x1F; u8 blue_5bit = (*palette_color_gbc >> 10) & 0x1F; switch (m_pixelFormat) { case GB_PIXEL_RGB565: { u8 green_6bit = (*palette_color_gbc >> 4) & 0x3E; *palette_color_final = (red_5bit << 11) | (green_6bit << 5) | blue_5bit; break; } case GB_PIXEL_BGR565: { u8 green_6bit = (*palette_color_gbc >> 4) & 0x3E; *palette_color_final = (blue_5bit << 11) | (green_6bit << 5) | red_5bit; break; } case GB_PIXEL_RGB555: { u8 green_5bit = (*palette_color_gbc >> 5) & 0x1F; *palette_color_final = 0x8000 | (red_5bit << 10) | (green_5bit << 5) | blue_5bit; break; } case GB_PIXEL_BGR555: { u8 green_5bit = (*palette_color_gbc >> 5) & 0x1F; *palette_color_final = 0x8000 | (blue_5bit << 10) | (green_5bit << 5) | red_5bit; break; } } } int Video::GetCurrentStatusMode() const { return m_iStatusMode; } void Video::ResetWindowLine() { u8 wy = m_pMemory->Retrieve(0xFF4A); if ((m_iWindowLine == 0) && (m_iStatusModeLYCounter < 144) && (m_iStatusModeLYCounter > wy)) m_iWindowLine = 144; } void Video::ScanLine(int line) { if (IsValidPointer(m_pColorFrameBuffer)) { u8 lcdc = m_pMemory->Retrieve(0xFF40); if (m_bScreenEnabled && IsSetBit(lcdc, 7)) { #ifdef PERFORMANCE RenderBG(line, 0); #endif RenderWindow(line); RenderSprites(line); } else { int line_width = (line * GAMEBOY_WIDTH); if (m_bCGB) { for (int x = 0; x < GAMEBOY_WIDTH; x++) m_pColorFrameBuffer[line_width + x] = 0x8000; } else { for (int x = 0; x < GAMEBOY_WIDTH; x++) m_pFrameBuffer[line_width + x] = 0; } } } } void Video::RenderBG(int line, int pixel) { u8 lcdc = m_pMemory->Retrieve(0xFF40); int line_width = (line * GAMEBOY_WIDTH); if (m_bCGB || IsSetBit(lcdc, 0)) { #ifdef PERFORMANCE int pixels_to_render = 160; #else int pixels_to_render = 4; #endif int offset_x_init = pixel & 0x7; int offset_x_end = offset_x_init + pixels_to_render; int screen_tile = pixel >> 3; int tile_start_addr = IsSetBit(lcdc, 4) ? 0x8000 : 0x8800; int map_start_addr = IsSetBit(lcdc, 3) ? 0x9C00 : 0x9800; u8 scroll_x = m_pMemory->Retrieve(0xFF43); u8 scroll_y = m_pMemory->Retrieve(0xFF42); u8 line_scrolled = line + scroll_y; int line_scrolled_32 = (line_scrolled >> 3) << 5; int tile_pixel_y = line_scrolled & 0x7; int tile_pixel_y_2 = tile_pixel_y << 1; int tile_pixel_y_flip_2 = (7 - tile_pixel_y) << 1; u8 palette = m_pMemory->Retrieve(0xFF47); for (int offset_x = offset_x_init; offset_x < offset_x_end; offset_x++) { int screen_pixel_x = (screen_tile << 3) + offset_x; u8 map_pixel_x = screen_pixel_x + scroll_x; int map_tile_x = map_pixel_x >> 3; int map_tile_offset_x = map_pixel_x & 0x7; u16 map_tile_addr = map_start_addr + line_scrolled_32 + map_tile_x; int map_tile = 0; if (tile_start_addr == 0x8800) { map_tile = static_cast (m_pMemory->Retrieve(map_tile_addr)); map_tile += 128; } else { map_tile = m_pMemory->Retrieve(map_tile_addr); } u8 cgb_tile_attr = m_bCGB ? m_pMemory->ReadCGBLCDRAM(map_tile_addr, true) : 0; u8 cgb_tile_pal = m_bCGB ? (cgb_tile_attr & 0x07) : 0; bool cgb_tile_bank = m_bCGB ? IsSetBit(cgb_tile_attr, 3) : false; bool cgb_tile_xflip = m_bCGB ? IsSetBit(cgb_tile_attr, 5) : false; bool cgb_tile_yflip = m_bCGB ? IsSetBit(cgb_tile_attr, 6) : false; int map_tile_16 = map_tile << 4; u8 byte1 = 0; u8 byte2 = 0; int final_pixely_2 = cgb_tile_yflip ? tile_pixel_y_flip_2 : tile_pixel_y_2; int tile_address = tile_start_addr + map_tile_16 + final_pixely_2; if (cgb_tile_bank) { byte1 = m_pMemory->ReadCGBLCDRAM(tile_address, true); byte2 = m_pMemory->ReadCGBLCDRAM(tile_address + 1, true); } else { byte1 = m_pMemory->Retrieve(tile_address); byte2 = m_pMemory->Retrieve(tile_address + 1); } int pixel_x_in_tile = map_tile_offset_x; if (cgb_tile_xflip) { pixel_x_in_tile = 7 - pixel_x_in_tile; } int pixel_x_in_tile_bit = 0x1 << (7 - pixel_x_in_tile); int pixel_data = (byte1 & pixel_x_in_tile_bit) ? 1 : 0; pixel_data |= (byte2 & pixel_x_in_tile_bit) ? 2 : 0; int index = line_width + screen_pixel_x; m_pColorCacheBuffer[index] = pixel_data & 0x03; if (m_bCGB) { bool cgb_tile_priority = IsSetBit(cgb_tile_attr, 7) && IsSetBit(lcdc, 0); if (cgb_tile_priority && (pixel_data != 0)) m_pColorCacheBuffer[index] = SetBit(m_pColorCacheBuffer[index], 2); m_pColorFrameBuffer[index] = m_CGBBackgroundPalettes[cgb_tile_pal][pixel_data][1]; } else { u8 color = (palette >> (pixel_data << 1)) & 0x03; m_pColorFrameBuffer[index] = m_pFrameBuffer[index] = color; } } } else { for (int x = 0; x < 4; x++) { int position = line_width + pixel + x; m_pFrameBuffer[position] = 0; m_pColorCacheBuffer[position] = 0; } } } void Video::RenderWindow(int line) { if (m_iWindowLine > 143) return; u8 lcdc = m_pMemory->Retrieve(0xFF40); if (!IsSetBit(lcdc, 5)) return; int wx = m_pMemory->Retrieve(0xFF4B) - 7; if (wx > 159) return; u8 wy = m_pMemory->Retrieve(0xFF4A); if ((wy > 143) || (wy > line)) return; int tiles = IsSetBit(lcdc, 4) ? 0x8000 : 0x8800; int map = IsSetBit(lcdc, 6) ? 0x9C00 : 0x9800; int lineAdjusted = m_iWindowLine; int y_32 = (lineAdjusted >> 3) << 5; int pixely = lineAdjusted & 0x7; int pixely_2 = pixely << 1; int pixely_2_flip = (7 - pixely) << 1; int line_width = (line * GAMEBOY_WIDTH); u8 palette = m_pMemory->Retrieve(0xFF47); for (int x = 0; x < 32; x++) { int tile = 0; if (tiles == 0x8800) { tile = static_cast (m_pMemory->Retrieve(map + y_32 + x)); tile += 128; } else { tile = m_pMemory->Retrieve(map + y_32 + x); } u8 cgb_tile_attr = m_bCGB ? m_pMemory->ReadCGBLCDRAM(map + y_32 + x, true) : 0; u8 cgb_tile_pal = m_bCGB ? (cgb_tile_attr & 0x07) : 0; bool cgb_tile_bank = m_bCGB ? IsSetBit(cgb_tile_attr, 3) : false; bool cgb_tile_xflip = m_bCGB ? IsSetBit(cgb_tile_attr, 5) : false; bool cgb_tile_yflip = m_bCGB ? IsSetBit(cgb_tile_attr, 6) : false; int mapOffsetX = x << 3; int tile_16 = tile << 4; u8 byte1 = 0; u8 byte2 = 0; int final_pixely_2 = (m_bCGB && cgb_tile_yflip) ? pixely_2_flip : pixely_2; int tile_address = tiles + tile_16 + final_pixely_2; if (m_bCGB && cgb_tile_bank) { byte1 = m_pMemory->ReadCGBLCDRAM(tile_address, true); byte2 = m_pMemory->ReadCGBLCDRAM(tile_address + 1, true); } else { byte1 = m_pMemory->Retrieve(tile_address); byte2 = m_pMemory->Retrieve(tile_address + 1); } for (int pixelx = 0; pixelx < 8; pixelx++) { int bufferX = (mapOffsetX + pixelx + wx); if (bufferX < 0 || bufferX >= GAMEBOY_WIDTH) continue; int pixelx_pos = pixelx; if (m_bCGB && cgb_tile_xflip) { pixelx_pos = 7 - pixelx_pos; } int pixel = (byte1 & (0x1 << (7 - pixelx_pos))) ? 1 : 0; pixel |= (byte2 & (0x1 << (7 - pixelx_pos))) ? 2 : 0; int position = line_width + bufferX; m_pColorCacheBuffer[position] = pixel & 0x03; if (m_bCGB) { bool cgb_tile_priority = IsSetBit(cgb_tile_attr, 7) && IsSetBit(lcdc, 0); if (cgb_tile_priority && (pixel != 0)) m_pColorCacheBuffer[position] = SetBit(m_pColorCacheBuffer[position], 2); m_pColorFrameBuffer[position] = m_CGBBackgroundPalettes[cgb_tile_pal][pixel][1]; } else { u8 color = (palette >> (pixel << 1)) & 0x03; m_pColorFrameBuffer[position] = m_pFrameBuffer[position] = color; } } } m_iWindowLine++; } void Video::RenderSprites(int line) { u8 lcdc = m_pMemory->Retrieve(0xFF40); if (!IsSetBit(lcdc, 1)) return; int sprite_height = IsSetBit(lcdc, 2) ? 16 : 8; int line_width = (line * GAMEBOY_WIDTH); bool visible_sprites[40]; int sprite_limit = 0; for (int sprite = 0; sprite < 40; sprite++) { int sprite_4 = sprite << 2; int sprite_y = m_pMemory->Retrieve(0xFE00 + sprite_4) - 16; if ((sprite_y > line) || ((sprite_y + sprite_height) <= line)) { visible_sprites[sprite] = false; continue; } sprite_limit++; visible_sprites[sprite] = sprite_limit <= 10; } for (int sprite = 39; sprite >= 0; sprite--) { if (!visible_sprites[sprite]) continue; int sprite_4 = sprite << 2; int sprite_x = m_pMemory->Retrieve(0xFE00 + sprite_4 + 1) - 8; if ((sprite_x < -7) || (sprite_x >= GAMEBOY_WIDTH)) continue; int sprite_y = m_pMemory->Retrieve(0xFE00 + sprite_4) - 16; int sprite_tile_16 = (m_pMemory->Retrieve(0xFE00 + sprite_4 + 2) & ((sprite_height == 16) ? 0xFE : 0xFF)) << 4; u8 sprite_flags = m_pMemory->Retrieve(0xFE00 + sprite_4 + 3); int sprite_pallette = IsSetBit(sprite_flags, 4) ? 1 : 0; u8 palette = m_pMemory->Retrieve(sprite_pallette ? 0xFF49 : 0xFF48); bool xflip = IsSetBit(sprite_flags, 5); bool yflip = IsSetBit(sprite_flags, 6); bool aboveBG = (!IsSetBit(sprite_flags, 7)); bool cgb_tile_bank = IsSetBit(sprite_flags, 3); int cgb_tile_pal = sprite_flags & 0x07; int tiles = 0x8000; int pixel_y = yflip ? ((sprite_height == 16) ? 15 : 7) - (line - sprite_y) : line - sprite_y; u8 byte1 = 0; u8 byte2 = 0; int pixel_y_2 = 0; int offset = 0; if (sprite_height == 16 && (pixel_y >= 8)) { pixel_y_2 = (pixel_y - 8) << 1; offset = 16; } else pixel_y_2 = pixel_y << 1; int tile_address = tiles + sprite_tile_16 + pixel_y_2 + offset; if (m_bCGB && cgb_tile_bank) { byte1 = m_pMemory->ReadCGBLCDRAM(tile_address, true); byte2 = m_pMemory->ReadCGBLCDRAM(tile_address + 1, true); } else { byte1 = m_pMemory->Retrieve(tile_address); byte2 = m_pMemory->Retrieve(tile_address + 1); } for (int pixelx = 0; pixelx < 8; pixelx++) { int pixel = (byte1 & (0x01 << (xflip ? pixelx : 7 - pixelx))) ? 1 : 0; pixel |= (byte2 & (0x01 << (xflip ? pixelx : 7 - pixelx))) ? 2 : 0; if (pixel == 0) continue; int bufferX = (sprite_x + pixelx); if (bufferX < 0 || bufferX >= GAMEBOY_WIDTH) continue; int position = line_width + bufferX; u8 color_cache = m_pColorCacheBuffer[position]; if (m_bCGB) { if (IsSetBit(color_cache, 2)) continue; } else { int sprite_x_cache = m_pSpriteXCacheBuffer[position]; if (IsSetBit(color_cache, 3) && (sprite_x_cache < sprite_x)) continue; } if (!aboveBG && (color_cache & 0x03)) continue; m_pColorCacheBuffer[position] = SetBit(color_cache, 3); m_pSpriteXCacheBuffer[position] = sprite_x; if (m_bCGB) { m_pColorFrameBuffer[position] = m_CGBSpritePalettes[cgb_tile_pal][pixel][1]; } else { u8 color = (palette >> (pixel << 1)) & 0x03; m_pColorFrameBuffer[position] = m_pFrameBuffer[position] = color; } } } } void Video::UpdateStatRegister() { // Updates the STAT register with current mode u8 stat = m_pMemory->Retrieve(0xFF41); m_pMemory->Load(0xFF41, (stat & 0xFC) | (m_iStatusMode & 0x3)); } void Video::CompareLYToLYC() { if (m_bScreenEnabled) { u8 lyc = m_pMemory->Retrieve(0xFF45); u8 stat = m_pMemory->Retrieve(0xFF41); if (lyc == m_iStatusModeLYCounter) { stat = SetBit(stat, 2); if (IsSetBit(stat, 6)) { if (m_IRQ48Signal == 0) { m_pProcessor->RequestInterrupt(Processor::LCDSTAT_Interrupt); } m_IRQ48Signal = SetBit(m_IRQ48Signal, 3); } } else { stat = UnsetBit(stat, 2); m_IRQ48Signal = UnsetBit(m_IRQ48Signal, 3); } m_pMemory->Load(0xFF41, stat); } } u8 Video::GetIRQ48Signal() const { return m_IRQ48Signal; } void Video::SetIRQ48Signal(u8 signal) { m_IRQ48Signal = signal; } void Video::SaveState(std::ostream& stream) { using namespace std; stream.write(reinterpret_cast (m_pFrameBuffer), GAMEBOY_WIDTH * GAMEBOY_HEIGHT); stream.write(reinterpret_cast (m_pSpriteXCacheBuffer), sizeof(int) * GAMEBOY_WIDTH * GAMEBOY_HEIGHT); stream.write(reinterpret_cast (m_pColorCacheBuffer), GAMEBOY_WIDTH * GAMEBOY_HEIGHT); stream.write(reinterpret_cast (&m_iStatusMode), sizeof(m_iStatusMode)); stream.write(reinterpret_cast (&m_iStatusModeCounter), sizeof(m_iStatusModeCounter)); stream.write(reinterpret_cast (&m_iStatusModeCounterAux), sizeof(m_iStatusModeCounterAux)); stream.write(reinterpret_cast (&m_iStatusModeLYCounter), sizeof(m_iStatusModeLYCounter)); stream.write(reinterpret_cast (&m_iScreenEnableDelayCycles), sizeof(m_iScreenEnableDelayCycles)); stream.write(reinterpret_cast (&m_iStatusVBlankLine), sizeof(m_iStatusVBlankLine)); stream.write(reinterpret_cast (&m_iPixelCounter), sizeof(m_iPixelCounter)); stream.write(reinterpret_cast (&m_iTileCycleCounter), sizeof(m_iTileCycleCounter)); stream.write(reinterpret_cast (&m_bScreenEnabled), sizeof(m_bScreenEnabled)); stream.write(reinterpret_cast (m_CGBSpritePalettes), sizeof(m_CGBSpritePalettes)); stream.write(reinterpret_cast (m_CGBBackgroundPalettes), sizeof(m_CGBBackgroundPalettes)); stream.write(reinterpret_cast (&m_bScanLineTransfered), sizeof(m_bScanLineTransfered)); stream.write(reinterpret_cast (&m_iWindowLine), sizeof(m_iWindowLine)); stream.write(reinterpret_cast (&m_iHideFrames), sizeof(m_iHideFrames)); stream.write(reinterpret_cast (&m_IRQ48Signal), sizeof(m_IRQ48Signal)); } void Video::LoadState(std::istream& stream) { using namespace std; stream.read(reinterpret_cast (m_pFrameBuffer), GAMEBOY_WIDTH * GAMEBOY_HEIGHT); stream.read(reinterpret_cast (m_pSpriteXCacheBuffer), sizeof(int) * GAMEBOY_WIDTH * GAMEBOY_HEIGHT); stream.read(reinterpret_cast (m_pColorCacheBuffer), GAMEBOY_WIDTH * GAMEBOY_HEIGHT); stream.read(reinterpret_cast (&m_iStatusMode), sizeof(m_iStatusMode)); stream.read(reinterpret_cast (&m_iStatusModeCounter), sizeof(m_iStatusModeCounter)); stream.read(reinterpret_cast (&m_iStatusModeCounterAux), sizeof(m_iStatusModeCounterAux)); stream.read(reinterpret_cast (&m_iStatusModeLYCounter), sizeof(m_iStatusModeLYCounter)); stream.read(reinterpret_cast (&m_iScreenEnableDelayCycles), sizeof(m_iScreenEnableDelayCycles)); stream.read(reinterpret_cast (&m_iStatusVBlankLine), sizeof(m_iStatusVBlankLine)); stream.read(reinterpret_cast (&m_iPixelCounter), sizeof(m_iPixelCounter)); stream.read(reinterpret_cast (&m_iTileCycleCounter), sizeof(m_iTileCycleCounter)); stream.read(reinterpret_cast (&m_bScreenEnabled), sizeof(m_bScreenEnabled)); stream.read(reinterpret_cast (m_CGBSpritePalettes), sizeof(m_CGBSpritePalettes)); stream.read(reinterpret_cast (m_CGBBackgroundPalettes), sizeof(m_CGBBackgroundPalettes)); stream.read(reinterpret_cast (&m_bScanLineTransfered), sizeof(m_bScanLineTransfered)); stream.read(reinterpret_cast (&m_iWindowLine), sizeof(m_iWindowLine)); stream.read(reinterpret_cast (&m_iHideFrames), sizeof(m_iHideFrames)); stream.read(reinterpret_cast (&m_IRQ48Signal), sizeof(m_IRQ48Signal)); } PaletteMatrix Video::GetCGBBackgroundPalettes() { return &m_CGBBackgroundPalettes; } PaletteMatrix Video::GetCGBSpritePalettes() { return &m_CGBSpritePalettes; }