diff --git a/Core/Console.cpp b/Core/Console.cpp index 8f2999a..075c9ae 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -137,7 +137,7 @@ void Console::LoadRom(VirtualFile romFile, VirtualFile patchFile) _memoryManager->Initialize(shared_from_this()); _cpu.reset(new Cpu(_memoryManager.get())); - _memoryManager->IncrementMasterClockValue<160>(); + _memoryManager->IncrementMasterClockValue<162>(); //if(_debugger) { //Reset debugger if it was running before diff --git a/Core/Cpu.Instructions.cpp b/Core/Cpu.Instructions.cpp index a14f1c9..ab840f7 100644 --- a/Core/Cpu.Instructions.cpp +++ b/Core/Cpu.Instructions.cpp @@ -268,11 +268,13 @@ void Cpu::SEI() void Cpu::REP() { + Idle(); ClearFlags((uint8_t)_operand); } void Cpu::SEP() { + Idle(); SetFlags((uint8_t)_operand); if(CheckFlag(ProcFlags::IndexMode8)) { //Truncate X/Y when 8-bit indexes are enabled diff --git a/Core/Cpu.cpp b/Core/Cpu.cpp index 2a1cc4d..789a418 100644 --- a/Core/Cpu.cpp +++ b/Core/Cpu.cpp @@ -11,7 +11,9 @@ Cpu::Cpu(MemoryManager* memoryManager) _state.SP = 0x1FF; _state.EmulationMode = true; _nmiFlag = false; + _prevNmiFlag = false; _irqSource = (uint8_t)IrqSource::None; + _prevIrqSource = (uint8_t)IrqSource::None; SetFlags(ProcFlags::MemoryMode8); SetFlags(ProcFlags::IndexMode8); } @@ -286,11 +288,12 @@ void Cpu::Exec() case 0xFE: AddrMode_AbsIdxX(); INC(); break; case 0xFF: AddrMode_AbsLngIdxX(); SBC(); break; } - - if(_nmiFlag) { + + //Use the state of the IRQ/NMI flags on the previous cycle to determine if an IRQ is processed or not + if(_prevNmiFlag) { ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyNmiVector : Cpu::NmiVector); _nmiFlag = false; - } else if(_irqSource && !CheckFlag(ProcFlags::IrqDisable)) { + } else if(_prevIrqSource && !CheckFlag(ProcFlags::IrqDisable)) { ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyIrqVector : Cpu::IrqVector); } } @@ -339,6 +342,8 @@ uint8_t Cpu::GetOpCode() void Cpu::Idle() { #ifndef DUMMYCPU + _prevNmiFlag = _nmiFlag; + _prevIrqSource = _irqSource; _memoryManager->IncrementMasterClockValue<6>(); #endif } @@ -372,6 +377,8 @@ uint8_t Cpu::Read(uint32_t addr, MemoryOperationType type) LogRead(addr, value); return value; #else + _prevNmiFlag = _nmiFlag; + _prevIrqSource = _irqSource; return _memoryManager->Read(addr, type); #endif } @@ -415,6 +422,8 @@ void Cpu::Write(uint32_t addr, uint8_t value, MemoryOperationType type) #ifdef DUMMYCPU LogWrite(addr, value); #else + _prevNmiFlag = _nmiFlag; + _prevIrqSource = _irqSource; _memoryManager->Write(addr, value, type); #endif } diff --git a/Core/Cpu.h b/Core/Cpu.h index 6f334d4..4a8e48d 100644 --- a/Core/Cpu.h +++ b/Core/Cpu.h @@ -38,7 +38,9 @@ private: CpuState _state; uint32_t _operand; bool _nmiFlag; + bool _prevNmiFlag; uint8_t _irqSource; + uint8_t _prevIrqSource; uint32_t GetProgramAddress(uint16_t addr); uint32_t GetDataAddress(uint16_t addr); diff --git a/Core/MemoryManager.cpp b/Core/MemoryManager.cpp index 4b6423c..e4e379b 100644 --- a/Core/MemoryManager.cpp +++ b/Core/MemoryManager.cpp @@ -14,7 +14,7 @@ void MemoryManager::Initialize(shared_ptr console) { - _lastMasterClock = 0; + _cyclesToRun = 0; _masterClock = 0; _console = console; _regs = console->GetInternalRegisters().get(); @@ -102,7 +102,7 @@ void MemoryManager::GenerateMasterClockTable() uint8_t page = (i & 0xFF); if(page <= 0x1F) { //Slow - _masterClockTable[j][i] = 6; + _masterClockTable[j][i] = 8; } else if(page >= 0x20 && page <= 0x3F) { //Fast _masterClockTable[j][i] = 6; @@ -127,20 +127,26 @@ void MemoryManager::GenerateMasterClockTable() void MemoryManager::IncrementMasterClock(uint32_t addr) { - _previousSpeed = _masterClockTable[(uint8_t)_regs->IsFastRomEnabled()][addr >> 8]; - _masterClock += _previousSpeed; - while(_lastMasterClock < _masterClock - 3) { - _ppu->Exec(); - _lastMasterClock += 4; - } + IncrementMasterClockValue(_masterClockTable[(uint8_t)_regs->IsFastRomEnabled()][addr >> 8]); } void MemoryManager::IncrementMasterClockValue(uint16_t value) { _masterClock += value; - while(_lastMasterClock < _masterClock - 3) { + _cyclesToRun += value; + + if(_cyclesToRun >= 12) { + _cyclesToRun -= 12; + _ppu->Exec(); + _ppu->Exec(); + _ppu->Exec(); + } else if(_cyclesToRun >= 8) { + _cyclesToRun -= 8; + _ppu->Exec(); + _ppu->Exec(); + } else if(_cyclesToRun >= 4) { + _cyclesToRun -= 4; _ppu->Exec(); - _lastMasterClock += 4; } } diff --git a/Core/MemoryManager.h b/Core/MemoryManager.h index 07fab1d..c53720e 100644 --- a/Core/MemoryManager.h +++ b/Core/MemoryManager.h @@ -23,17 +23,16 @@ private: shared_ptr _registerHandlerA; shared_ptr _registerHandlerB; - InternalRegisters* _regs; + InternalRegisters *_regs; shared_ptr _ppu; IMemoryHandler* _handlers[0x100 * 0x10]; vector> _workRamHandlers; - uint8_t * _workRam; + uint8_t *_workRam; uint64_t _masterClock; - uint8_t _previousSpeed; - uint64_t _lastMasterClock; + uint64_t _cyclesToRun; uint8_t _masterClockTable[2][0x10000]; public: @@ -67,8 +66,9 @@ template void MemoryManager::IncrementMasterClockValue() { _masterClock += value; - while(_lastMasterClock < _masterClock - 3) { + _cyclesToRun += value; + while(_cyclesToRun >= 4) { + _cyclesToRun -= 4; _ppu->Exec(); - _lastMasterClock += 4; } } \ No newline at end of file diff --git a/Core/Ppu.cpp b/Core/Ppu.cpp index 034a94f..41cb12f 100644 --- a/Core/Ppu.cpp +++ b/Core/Ppu.cpp @@ -71,9 +71,19 @@ PpuState Ppu::GetState() void Ppu::Exec() { + //"normally dots 323 and 327 are 6 master cycles instead of 4." + //Add 1 extra dot to compensate (0-340 instead of 0-339) + //TODO fix this properly if(_cycle == 340) { _cycle = -1; _scanline++; + + _drawStartX = 0; + _drawEndX = 0; + _pixelsDrawn = 0; + _subPixelsDrawn = 0; + memset(_rowPixelFlags, 0, sizeof(_rowPixelFlags)); + memset(_subScreenFilled, 0, sizeof(_subScreenFilled)); if(_scanline == (_overscanMode ? 240 : 225)) { //Reset OAM address at the start of vblank? @@ -93,8 +103,9 @@ void Ppu::Exec() } } else if(_scanline == 240 && _frameCount & 0x01) { //Skip 1 tick every other frame + //TODO : some modes don't skip this? _cycle++; - } else if(_scanline == 261) { + } else if(_scanline == 262) { _regs->SetNmiFlag(false); _scanline = 0; _rangeOver = false; @@ -109,6 +120,7 @@ void Ppu::Exec() if(_regs->IsVerticalIrqEnabled() && !_regs->IsHorizontalIrqEnabled() && _scanline == _regs->GetVerticalTimer()) { //An IRQ will occur sometime just after the V Counter reaches the value set in $4209/$420A. _console->GetCpu()->SetIrqSource(IrqSource::Ppu); + _irqDelay = 4; } } @@ -117,18 +129,24 @@ void Ppu::Exec() if(_regs->IsHorizontalIrqEnabled() && _cycle == _regs->GetHorizontalTimer() && (!_regs->IsVerticalIrqEnabled() || _scanline == _regs->GetVerticalTimer())) { //An IRQ will occur sometime just after the H Counter reaches the value set in $4207/$4208. - _console->GetCpu()->SetIrqSource(IrqSource::Ppu); + _irqDelay = 4; + } + + if(_irqDelay > 0) { + _irqDelay--; + if(_irqDelay == 0) { + _console->GetCpu()->SetIrqSource(IrqSource::Ppu); + } } if(_cycle == 278 && _scanline <= (_overscanMode ? 239 : 224)) { if(_scanline != 0) { RenderScanline(); - } else { - EvaluateNextLineSprites(); } + EvaluateNextLineSprites(); _console->GetDmaController()->ProcessHdmaChannels(); } else if(_cycle == 134) { - //TODO Approximation + //TODO Approximation (DRAM refresh timing is not exact) _console->GetMemoryManager()->IncrementMasterClockValue<40>(); } } @@ -380,10 +398,7 @@ void Ppu::RenderMode7() void Ppu::RenderScanline() { - _pixelsDrawn = 0; - _subPixelsDrawn = 0; - memset(_rowPixelFlags, 0, sizeof(_rowPixelFlags)); - memset(_subScreenFilled, 0, sizeof(_subScreenFilled)); + _drawEndX = std::min(_cycle - 22, 255); if(_forcedVblank) { RenderBgColor(); @@ -437,9 +452,8 @@ void Ppu::RenderScanline() ApplyColorMath(); ApplyBrightness(); ApplyHiResMode(); - - //Process sprites for next scanline - EvaluateNextLineSprites(); + + _drawStartX = _drawEndX + 1; } template @@ -450,7 +464,7 @@ void Ppu::RenderBgColor() } uint16_t bgColor = _cgram[0] | (_cgram[1] << 8); - for(int x = 0; x < 256; x++) { + for(int x = _drawStartX; x <= _drawEndX; x++) { if(forMainScreen) { if(!_rowPixelFlags[x]) { uint8_t pixelFlags = PixelFlags::Filled | ((_colorMathEnabled & 0x20) ? PixelFlags::AllowColorMath : 0); @@ -484,7 +498,7 @@ void Ppu::RenderSprites() } if(forMainScreen) { - for(int x = 0; x < 256; x++) { + for(int x = _drawStartX; x <= _drawEndX; x++) { if(!_rowPixelFlags[x] && _spritePriority[x] == priority) { if(activeWindowCount && ProcessMaskWindow(activeWindowCount, x)) { //This pixel was masked @@ -496,7 +510,7 @@ void Ppu::RenderSprites() } } } else { - for(int x = 0; x < 256; x++) { + for(int x = _drawStartX; x < _drawEndX; x++) { if(!_subScreenFilled[x] && _spritePriority[x] == priority) { if(activeWindowCount && ProcessMaskWindow(activeWindowCount, x)) { //This pixel was masked @@ -562,7 +576,7 @@ void Ppu::RenderTilemap() /* The tilemap address to read the tile data from */ uint16_t addr; - for(int x = 0; x < 256; x++) { + for(int x = _drawStartX; x <= _drawEndX; x++) { if(hiResMode) { realX = (x << 1) + (forMainScreen ? 1 : 0); } else { @@ -791,7 +805,7 @@ void Ppu::RenderTilemapMode7() uint8_t pixelFlags = PixelFlags::Filled | (((_colorMathEnabled >> layerIndex) & 0x01) ? PixelFlags::AllowColorMath : 0); - for(int x = 0; x < 256; x++) { + for(int x = _drawStartX; x <= _drawEndX; x++) { uint16_t realX = _mode7.HorizontalMirroring ? (255 - x) : x; if(forMainScreen) { @@ -884,7 +898,7 @@ void Ppu::ApplyColorMath() uint8_t activeWindowCount = (uint8_t)_window[0].ActiveLayers[Ppu::ColorWindowIndex] + (uint8_t)_window[1].ActiveLayers[Ppu::ColorWindowIndex]; - for(int x = 0; x < 256; x++) { + for(int x = _drawStartX; x < _drawEndX; x++) { if(_rowPixelFlags[x] & PixelFlags::AllowColorMath) { uint8_t halfShift = _colorMathHalveResult ? 1 : 0; uint16_t &mainPixel = _mainScreenBuffer[x]; @@ -966,7 +980,7 @@ template void Ppu::ApplyBrightness() { if(_screenBrightness != 15) { - for(int x = 0; x < 256; x++) { + for(int x = _drawStartX; x < _drawEndX; x++) { uint16_t &pixel = (forMainScreen ? _mainScreenBuffer : _subScreenBuffer)[x]; uint16_t r = (pixel & 0x1F) * _screenBrightness / 15; uint16_t g = ((pixel >> 5) & 0x1F) * _screenBrightness / 15; @@ -1222,6 +1236,10 @@ uint8_t Ppu::Read(uint16_t addr) void Ppu::Write(uint32_t addr, uint8_t value) { + if(_scanline < (_overscanMode ? 239 : 224) && _scanline > 0 && _cycle >= 22 && _cycle <= 277) { + RenderScanline(); + } + switch(addr) { case 0x2100: _forcedVblank = (value & 0x80) != 0; diff --git a/Core/Ppu.h b/Core/Ppu.h index 8353b75..32b97a4 100644 --- a/Core/Ppu.h +++ b/Core/Ppu.h @@ -51,6 +51,10 @@ private: uint16_t _cycle = 0; uint16_t _scanline = 0; uint32_t _frameCount = 0; + + uint8_t _drawStartX = 0; + uint8_t _drawEndX = 0; + uint8_t _irqDelay = 0; uint8_t _bgMode = 0; bool _mode1Bg3Priority = false;