diff --git a/Core/WS/WsCpu.cpp b/Core/WS/WsCpu.cpp index 3d66aaca..0d22f41d 100644 --- a/Core/WS/WsCpu.cpp +++ b/Core/WS/WsCpu.cpp @@ -49,7 +49,7 @@ void WsCpu::Exec() if(irqPending) { _state.Halted = false; - if(_state.Flags.Irq) { + if(_state.Flags.Irq && _suppressIrqClock != _state.CycleCount) { Interrupt(_memoryManager->GetIrqVector(), true); } } @@ -232,7 +232,12 @@ void WsCpu::PopFlags() Idle<2>(); uint16_t flags; Pop(flags); + bool irq = _state.Flags.Irq; _state.Flags.Set(flags); + if(!irq && _state.Flags.Irq) { + //Suppress IRQs for the next instruction when irq flag gets set + SuppressIrq(false); + } } void WsCpu::PopMemory() @@ -475,7 +480,12 @@ void WsCpu::RetInterrupt() Pop(_state.CS); uint16_t flags; Pop(flags); + bool irq = _state.Flags.Irq; _state.Flags.Set(flags); + if(!irq && _state.Flags.Irq) { + //Suppress IRQs for the next instruction when irq flag gets set + SuppressIrq(false); + } ClearPrefetch(); } @@ -809,12 +819,33 @@ void WsCpu::Halt() _state.Halted = true; } +void WsCpu::SuppressIrq(bool suppressTrap) +{ + //When the IRQ flag gets set, or when the SS segment is modified, IRQs + //are suppressed after the current instruction and will only trigger + //on the instruction after that. + _suppressIrqClock = _state.CycleCount; + if(suppressTrap) { + _suppressTrapClock = _state.CycleCount; + } +} + void WsCpu::SetFlagValue(bool& flag, bool value) { Idle<4>(); flag = value; } +void WsCpu::SetIrqFlag() +{ + Idle<4>(); + if(!_state.Flags.Irq) { + _state.Flags.Irq = true; + //Suppress IRQs for the next instruction when irq flag gets set + SuppressIrq(false); + } +} + void WsCpu::ClearPrefetch() { #ifndef DUMMYCPU @@ -1070,6 +1101,10 @@ void WsCpu::MoveSegment() if constexpr(direction) { SetModSegRegister(_modRm.Register, param); + if(_modRm.Register == 2) { + //SS was updated, suppress IRQs after this instruction + SuppressIrq(true); + } } else { SetModRm(param); } @@ -1827,7 +1862,7 @@ start: case 0x14: ProcessAluImm(); break; case 0x15: ProcessAluImm(); break; case 0x16: PushSegment(_state.SS); break; - case 0x17: PopSegment(_state.SS); break; + case 0x17: PopSegment(_state.SS); SuppressIrq(true); break; case 0x18: ProcessAluModRm(); break; case 0x19: ProcessAluModRm(); break; case 0x1A: ProcessAluModRm(); break; @@ -2069,7 +2104,7 @@ start: case 0xF8: SetFlagValue(_state.Flags.Carry, false); break; //CLC case 0xF9: SetFlagValue(_state.Flags.Carry, true); break; //STC case 0xFA: SetFlagValue(_state.Flags.Irq, false); break; //CLI - case 0xFB: SetFlagValue(_state.Flags.Irq, true); break; //STI + case 0xFB: SetIrqFlag(); break; //STI case 0xFC: SetFlagValue(_state.Flags.Direction, false); break; //CLD case 0xFD: SetFlagValue(_state.Flags.Direction, true); break; //STD case 0xFE: Grp45ModRm(); break; @@ -2080,7 +2115,7 @@ start: _prefix = {}; } - if(_state.Flags.Trap) { + if(_state.Flags.Trap && _suppressTrapClock != _state.CycleCount) { Interrupt(1); } } @@ -2129,4 +2164,7 @@ void WsCpu::Serialize(Serializer& s) SV(_modRm.Mode); SV(_modRm.Register); SV(_modRm.Rm); + + SV(_suppressIrqClock); + SV(_suppressTrapClock); } diff --git a/Core/WS/WsCpu.h b/Core/WS/WsCpu.h index 0fdce51d..a6f5b7b3 100644 --- a/Core/WS/WsCpu.h +++ b/Core/WS/WsCpu.h @@ -70,6 +70,9 @@ private: ModRmState _modRm = {}; PrefixState _prefix = {}; + uint64_t _suppressIrqClock = 0; + uint64_t _suppressTrapClock = 0; + #ifndef DUMMYCPU WsCpuPrefetch _prefetch; #endif @@ -253,7 +256,10 @@ private: void Wait(); void Halt(); + void SuppressIrq(bool suppressTrap); + void SetFlagValue(bool& flag, bool value); + void SetIrqFlag(); public: WsCpu(Emulator* emu, WsMemoryManager* memoryManager); diff --git a/Core/WS/WsPpu.cpp b/Core/WS/WsPpu.cpp index 50d09a58..a0d700ff 100644 --- a/Core/WS/WsPpu.cpp +++ b/Core/WS/WsPpu.cpp @@ -59,10 +59,10 @@ void WsPpu::ProcessHblank() _timer->TickHorizontalTimer(); if(_state.Scanline < WsConstants::ScreenHeight) { switch(_state.Mode) { - case WsVideoMode::Monochrome: DrawScanline(); break; - case WsVideoMode::Color2bpp: DrawScanline(); break; - case WsVideoMode::Color4bpp: DrawScanline(); break; - case WsVideoMode::Color4bppPacked: DrawScanline(); break; + case WsVideoMode::Monochrome: DrawScanline(); break; + case WsVideoMode::Color2bpp: DrawScanline(); break; + case WsVideoMode::Color4bpp: DrawScanline(); break; + case WsVideoMode::Color4bppPacked: DrawScanline(); break; } } } @@ -157,11 +157,9 @@ void WsPpu::DrawSprites() if(x >= 224) { continue; - } - else if(_rowData[rowIndex][x].Priority > 0) { + } else if(_rowData[rowIndex][x].Priority > 0) { continue; - } - else if(_state.SpriteWindow.EnabledLatch && showOutsideWindow == _state.SpriteWindow.IsInsideWindow(x, scanline)) { + } else if(_state.SpriteWindow.EnabledLatch && showOutsideWindow == _state.SpriteWindow.IsInsideWindow(x, scanline)) { //Don't draw this pixel, it's outside/inside the window and should only be drawn on the other side continue; } @@ -257,40 +255,39 @@ template uint16_t WsPpu::GetPixelColor(uint16_t tileAddr, uint8_t column) { switch(mode) { - case WsVideoMode::Monochrome: { - uint8_t tileData = _vram[tileAddr]; - uint8_t tileData2 = _vram[tileAddr + 1]; - return ( - ((tileData << column) & 0x80) >> 7 | - ((tileData2 << column) & 0x80) >> 6 + case WsVideoMode::Monochrome: { + uint8_t tileData = _vram[tileAddr]; + uint8_t tileData2 = _vram[tileAddr + 1]; + return ( + ((tileData << column) & 0x80) >> 7 | + ((tileData2 << column) & 0x80) >> 6 ); - } + } - case WsVideoMode::Color2bpp: { - uint8_t tileData = _vram[tileAddr]; - uint8_t tileData2 = _vram[tileAddr + 1]; - return ( - ((tileData << column) & 0x80) >> 7 | - ((tileData2 << column) & 0x80) >> 6 + case WsVideoMode::Color2bpp: { + uint8_t tileData = _vram[tileAddr]; + uint8_t tileData2 = _vram[tileAddr + 1]; + return ( + ((tileData << column) & 0x80) >> 7 | + ((tileData2 << column) & 0x80) >> 6 ); - } + } - case WsVideoMode::Color4bpp: { - uint8_t tileData = _vram[tileAddr]; - uint8_t tileData2 = _vram[tileAddr + 1]; - uint8_t tileData3 = _vram[tileAddr + 2]; - uint8_t tileData4 = _vram[tileAddr + 3]; - return ( - ((tileData << column) & 0x80) >> 7 | - ((tileData2 << column) & 0x80) >> 6 | - ((tileData3 << column) & 0x80) >> 5 | - ((tileData4 << column) & 0x80) >> 4 + case WsVideoMode::Color4bpp: { + uint8_t tileData = _vram[tileAddr]; + uint8_t tileData2 = _vram[tileAddr + 1]; + uint8_t tileData3 = _vram[tileAddr + 2]; + uint8_t tileData4 = _vram[tileAddr + 3]; + return ( + ((tileData << column) & 0x80) >> 7 | + ((tileData2 << column) & 0x80) >> 6 | + ((tileData3 << column) & 0x80) >> 5 | + ((tileData4 << column) & 0x80) >> 4 ); - } + } - case WsVideoMode::Color4bppPacked: { - return (_vram[tileAddr + column / 2] >> (column & 0x01 ? 0 : 4)) & 0x0F; - } + case WsVideoMode::Color4bppPacked: + return (_vram[tileAddr + column / 2] >> (column & 0x01 ? 0 : 4)) & 0x0F; } return 0; @@ -535,7 +532,7 @@ uint8_t WsPpu::ReadPort(uint16_t port) return ( (_state.LcdEnabled ? 0x01 : 0) | (_state.HighContrast ? 0x02 : 0) - ); + ); case 0x15: return _state.Icons.Value; case 0x16: return _state.LastScanline; @@ -549,14 +546,14 @@ uint8_t WsPpu::ReadPort(uint16_t port) return ( _state.BwShades[(port - 0x1C) * 2] | (_state.BwShades[(port - 0x1C) * 2 + 1] << 4) - ); + ); default: if(port >= 0x20 && port <= 0x3F) { return ( _state.BwPalettes[(port - 0x20) * 2] | (_state.BwPalettes[(port - 0x20) * 2 + 1] << 4) - ); + ); } else { LogDebug("[Debug] PPU Read - missing handler: $" + HexUtilities::ToHex(port)); @@ -647,8 +644,7 @@ void WsPpu::WritePort(uint16_t port, uint8_t value) if(port >= 0x20 && port <= 0x3F) { _state.BwPalettes[(port - 0x20) * 2] = value & 0x07; _state.BwPalettes[(port - 0x20) * 2 + 1] = (value >> 4) & 0x07; - } - else { + } else { LogDebug("[Debug] PPU Write - missing handler: $" + HexUtilities::ToHex(port) + " = " + HexUtilities::ToHex(value)); } break;