#include "stdafx.h" #include "HdPpu.h" #include "CPU.h" #include "Console.h" #include "HdNesPack.h" #include "VideoDecoder.h" #include "RewindManager.h" #include "HdPackConditions.h" #include "NotificationManager.h" #include "BaseMapper.h" #include "MemoryManager.h" void HdPpu::DrawPixel() { uint16_t bufferOffset = (_scanline << 8) + _cycle - 1; uint16_t &pixel = _currentOutputBuffer[bufferOffset]; _lastSprite = nullptr; //init sprite frame ranges at screen start if (bufferOffset == 0) { uint8_t spriteCnt = (_flags.LargeSprites ? 128 : 64); for (int i = 0; i < spriteCnt; i++) { _spriteFrameRanges[i].lastUpdated = _spriteFrameRanges[i].updated; if (_spriteFrameRanges[i].updated) { //copy last frame sprite data memcpy(&(_spriteFrameRanges[i].last), &(_spriteFrameRanges[i].current), sizeof(_spriteFrameRanges[i].current)); _spriteFrameRanges[i].updated = false; _spriteFrameRanges[i].lastStartFrameNumber = _spriteFrameRanges[i].startFrameNumber; } } } if(IsRenderingEnabled() || ((_state.VideoRamAddr & 0x3F00) != 0x3F00)) { bool isChrRam = !_console->GetMapper()->HasChrRom(); BaseMapper *mapper = _console->GetMapper(); uint32_t color = GetPixelColor(); pixel = (_paletteRAM[color & 0x03 ? color : 0] & _paletteRamMask) | _intensifyColorBits; TileInfo* lastTile = &((_state.XScroll + ((_cycle - 1) & 0x07) < 8) ? _previousTile : _currentTile); uint32_t backgroundColor = 0; if(_flags.BackgroundEnabled && _cycle > _minimumDrawBgCycle) { backgroundColor = (((_state.LowBitShift << _state.XScroll) & 0x8000) >> 15) | (((_state.HighBitShift << _state.XScroll) & 0x8000) >> 14); } HdPpuPixelInfo &tileInfo = _info->ScreenTiles[bufferOffset]; tileInfo.Grayscale = _paletteRamMask == 0x30; tileInfo.EmphasisBits = _intensifyColorBits >> 6; tileInfo.Tile.PpuBackgroundColor = ReadPaletteRAM(0); tileInfo.Tile.BgColorIndex = backgroundColor; if(backgroundColor == 0) { tileInfo.Tile.BgColor = tileInfo.Tile.PpuBackgroundColor; } else { tileInfo.Tile.BgColor = ReadPaletteRAM(lastTile->PaletteOffset + backgroundColor); } tileInfo.XScroll = _state.XScroll; tileInfo.TmpVideoRamAddr = _state.TmpVideoRamAddr; if(_lastSprite && _flags.SpritesEnabled) { int j = 0; for(uint8_t i = 0; i < _spriteCount; i++) { int32_t shift = (int32_t)_cycle - _spriteTiles[i].SpriteX - 1; SpriteInfo& sprite = _spriteTiles[i]; if(shift >= 0 && shift < 8) { tileInfo.Sprite[j].TileIndex = sprite.AbsoluteTileAddr / 16; if(isChrRam) { mapper->CopyChrTile(sprite.AbsoluteTileAddr & 0xFFFFFFF0, tileInfo.Sprite[j].TileData); } if(_version >= 100) { tileInfo.Sprite[j].PaletteColors = 0xFF000000 | _paletteRAM[sprite.PaletteOffset + 3] | (_paletteRAM[sprite.PaletteOffset + 2] << 8) | (_paletteRAM[sprite.PaletteOffset + 1] << 16); } else { tileInfo.Sprite[j].PaletteColors = _paletteRAM[sprite.PaletteOffset + 3] | (_paletteRAM[sprite.PaletteOffset + 2] << 8) | (_paletteRAM[sprite.PaletteOffset + 1] << 16); } if(sprite.OffsetY >= 8) { tileInfo.Sprite[j].OffsetY = sprite.OffsetY - 8; } else { tileInfo.Sprite[j].OffsetY = sprite.OffsetY; } tileInfo.Sprite[j].OffsetX = shift; tileInfo.Sprite[j].HorizontalMirroring = sprite.HorizontalMirror; tileInfo.Sprite[j].VerticalMirroring = sprite.VerticalMirror; tileInfo.Sprite[j].BackgroundPriority = sprite.BackgroundPriority; int32_t shift = (int32_t)_cycle - sprite.SpriteX - 1; if(sprite.HorizontalMirror) { tileInfo.Sprite[j].SpriteColorIndex = ((sprite.LowByte >> shift) & 0x01) | ((sprite.HighByte >> shift) & 0x01) << 1; } else { tileInfo.Sprite[j].SpriteColorIndex = ((sprite.LowByte << shift) & 0x80) >> 7 | ((sprite.HighByte << shift) & 0x80) >> 6; } if(tileInfo.Sprite[j].SpriteColorIndex == 0) { tileInfo.Sprite[j].SpriteColor = ReadPaletteRAM(0); } else { tileInfo.Sprite[j].SpriteColor = ReadPaletteRAM(sprite.PaletteOffset + tileInfo.Sprite[j].SpriteColorIndex); } tileInfo.Sprite[j].PaletteOffset = sprite.PaletteOffset; tileInfo.Sprite[j].PpuBackgroundColor = tileInfo.Tile.PpuBackgroundColor; tileInfo.Sprite[j].BgColorIndex = tileInfo.Tile.BgColorIndex; tileInfo.Sprite[j].OAMIndex = (sprite.OffsetY >= 8 ? sprite.OAMIndex + 64 : sprite.OAMIndex); if (!_spriteFrameRanges[tileInfo.Sprite[j].OAMIndex].updated) { //fill the current frame sprite if (isChrRam) { memcpy(_spriteFrameRanges[tileInfo.Sprite[j].OAMIndex].current.TileData, tileInfo.Sprite[j].TileData, 16); } else { _spriteFrameRanges[tileInfo.Sprite[j].OAMIndex].current.TileIndex = tileInfo.Sprite[j].TileIndex; } _spriteFrameRanges[tileInfo.Sprite[j].OAMIndex].current.PaletteColors = tileInfo.Sprite[j].PaletteColors; _spriteFrameRanges[tileInfo.Sprite[j].OAMIndex].current.HorizontalMirroring = tileInfo.Sprite[j].HorizontalMirroring; _spriteFrameRanges[tileInfo.Sprite[j].OAMIndex].current.VerticalMirroring = tileInfo.Sprite[j].VerticalMirroring; _spriteFrameRanges[tileInfo.Sprite[j].OAMIndex].current.BackgroundPriority = tileInfo.Sprite[j].BackgroundPriority; _spriteFrameRanges[tileInfo.Sprite[j].OAMIndex].current.ScreenX = sprite.SpriteX; _spriteFrameRanges[tileInfo.Sprite[j].OAMIndex].current.ScreenY = _scanline - tileInfo.Sprite[j].OffsetY; _spriteFrameRanges[tileInfo.Sprite[j].OAMIndex].updated = true; } j++; if(j >= 4) { break; } } } tileInfo.SpriteCount = j; } else { tileInfo.SpriteCount = 0; } if(_flags.BackgroundEnabled && _cycle > _minimumDrawBgCycle) { tileInfo.Tile.TileIndex = lastTile->AbsoluteTileAddr / 16; if(isChrRam) { mapper->CopyChrTile(lastTile->AbsoluteTileAddr & 0xFFFFFFF0, tileInfo.Tile.TileData); } if(_version >= 100) { tileInfo.Tile.PaletteColors = _paletteRAM[lastTile->PaletteOffset + 3] | (_paletteRAM[lastTile->PaletteOffset + 2] << 8) | (_paletteRAM[lastTile->PaletteOffset + 1] << 16) | (_paletteRAM[0] << 24); } else { tileInfo.Tile.PaletteColors = _paletteRAM[lastTile->PaletteOffset + 3] | (_paletteRAM[lastTile->PaletteOffset + 2] << 8) | (_paletteRAM[lastTile->PaletteOffset + 1] << 16); } tileInfo.Tile.OffsetY = lastTile->OffsetY; tileInfo.Tile.OffsetX = (_state.XScroll + ((_cycle - 1) & 0x07)) & 0x07; } else { tileInfo.Tile.TileIndex = HdPpuTileInfo::NoTile; } //match sprite frame range at screen end if (bufferOffset == PixelCount - 1) { uint16_t distance; uint16_t newDistance; uint16_t newDistanceX; uint16_t newDistanceY; uint8_t spriteCnt = (_flags.LargeSprites ? 128 : 64); for (int i = 0; i < spriteCnt; i++) { if (_spriteFrameRanges[i].updated) { distance = 13; _spriteFrameRanges[i].startFrameNumber = _frameCount; for (int j = 0; j < spriteCnt; j++) { if (_spriteFrameRanges[j].lastUpdated) { newDistanceX = abs(_spriteFrameRanges[i].current.ScreenX - _spriteFrameRanges[j].last.ScreenX); newDistanceY = abs(_spriteFrameRanges[i].current.ScreenY - _spriteFrameRanges[j].last.ScreenY); newDistance = newDistanceX + newDistanceY; if (newDistance < distance && newDistanceX <= 6 && newDistanceY <= 6) { //check for matches bool compareResult = false; if (_spriteFrameRanges[i].current.BackgroundPriority == _spriteFrameRanges[j].last.BackgroundPriority && _spriteFrameRanges[i].current.HorizontalMirroring == _spriteFrameRanges[j].last.HorizontalMirroring && _spriteFrameRanges[i].current.VerticalMirroring == _spriteFrameRanges[j].last.VerticalMirroring && _spriteFrameRanges[i].current.PaletteColors == _spriteFrameRanges[j].last.PaletteColors ) { if (isChrRam) { compareResult = (memcmp(_spriteFrameRanges[i].current.TileData, _spriteFrameRanges[j].last.TileData, 16) == 0); } else { compareResult = (_spriteFrameRanges[i].current.TileIndex == _spriteFrameRanges[j].last.TileIndex); } } if (compareResult) { _spriteFrameRanges[i].startFrameNumber = _spriteFrameRanges[j].lastStartFrameNumber; } distance = newDistance; } } } } } } } else { //"If the current VRAM address points in the range $3F00-$3FFF during forced blanking, the color indicated by this palette location will be shown on screen instead of the backdrop color." pixel = ReadPaletteRAM(_state.VideoRamAddr) | _intensifyColorBits; _info->ScreenTiles[bufferOffset].Tile.TileIndex = HdPpuTileInfo::NoTile; _info->ScreenTiles[bufferOffset].SpriteCount = 0; } } HdPpu::HdPpu(shared_ptr console, HdPackData * hdData) : PPU(console) { _hdData = hdData; if(_hdData) { _version = _hdData->Version; bool isChrRamGame = !console->GetMapper()->HasChrRom(); _screenInfo[0] = new HdScreenInfo(isChrRamGame); _screenInfo[1] = new HdScreenInfo(isChrRamGame); _info = _screenInfo[0]; } } HdPpu::~HdPpu() { if(_hdData) { delete _screenInfo[0]; delete _screenInfo[1]; } } void HdPpu::SendFrame() { _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone, _currentOutputBuffer); _info->FrameNumber = _frameCount; _info->spriteFrameRanges = _spriteFrameRanges; _info->WatchedAddressValues.clear(); for(uint32_t address : _hdData->WatchedMemoryAddresses) { if(address & HdPackBaseMemoryCondition::PpuMemoryMarker) { if((address & 0x3FFF) >= 0x3F00) { _info->WatchedAddressValues[address] = ReadPaletteRAM(address); } else { _info->WatchedAddressValues[address] = _console->GetMapper()->DebugReadVRAM(address & 0x3FFF, true); } } else { _info->WatchedAddressValues[address] = _console->GetMemoryManager()->DebugRead(address); } } #ifdef LIBRETRO _console->GetVideoDecoder()->UpdateFrameSync(_currentOutputBuffer, _info); #else if(_console->GetRewindManager()->IsRewinding()) { _console->GetVideoDecoder()->UpdateFrameSync(_currentOutputBuffer, _info); } else { _console->GetVideoDecoder()->UpdateFrame(_currentOutputBuffer, _info); } _currentOutputBuffer = (_currentOutputBuffer == _outputBuffers[0]) ? _outputBuffers[1] : _outputBuffers[0]; _info = (_info == _screenInfo[0]) ? _screenInfo[1] : _screenInfo[0]; #endif }