Mesen-S/Core/InternalRegisters.h
Sour d32f512b7b NMI: Set CPU's nmi flag during irq/nmi flag update instead of right away
Fixes a freeze at power on in Chou Aniki.
The game writes to 4200+4201 (16-bit write) to enable NMIs in the middle of vblank and expects the instruction after the write to run BEFORE the nmi handler is called (not doing so causes the game to freeze)
2020-03-01 11:25:40 -05:00

81 lines
2 KiB
C++

#pragma once
#include "stdafx.h"
#include "AluMulDiv.h"
#include "Console.h"
#include "Cpu.h"
#include "Ppu.h"
#include "InternalRegisterTypes.h"
#include "../Utilities/ISerializable.h"
class MemoryManager;
class InternalRegisters final : public ISerializable
{
private:
Console* _console;
Cpu* _cpu;
Ppu* _ppu;
MemoryManager* _memoryManager;
AluMulDiv _aluMulDiv;
InternalRegisterState _state;
bool _nmiFlag = false;
bool _irqLevel = false;
uint8_t _needIrq = 0;
bool _irqFlag = false;
void SetIrqFlag(bool irqFlag);
public:
InternalRegisters();
void Initialize(Console* console);
void Reset();
void ProcessAutoJoypadRead();
__forceinline void ProcessIrqCounters();
uint8_t GetIoPortOutput();
void SetNmiFlag(bool nmiFlag);
bool IsVerticalIrqEnabled() { return _state.EnableVerticalIrq; }
bool IsHorizontalIrqEnabled() { return _state.EnableHorizontalIrq; }
bool IsNmiEnabled() { return _state.EnableNmi; }
bool IsFastRomEnabled() { return _state.EnableFastRom; }
uint16_t GetHorizontalTimer() { return _state.HorizontalTimer; }
uint16_t GetVerticalTimer() { return _state.VerticalTimer; }
uint8_t Peek(uint16_t addr);
uint8_t Read(uint16_t addr);
void Write(uint16_t addr, uint8_t value);
InternalRegisterState GetState();
AluState GetAluState();
void Serialize(Serializer &s) override;
};
void InternalRegisters::ProcessIrqCounters()
{
if(_needIrq > 0) {
_needIrq--;
if(_needIrq == 0) {
SetIrqFlag(true);
}
}
bool irqLevel = (
(_state.EnableHorizontalIrq || _state.EnableVerticalIrq) &&
(!_state.EnableHorizontalIrq || (_state.HorizontalTimer <= 339 && (_ppu->GetCycle() == _state.HorizontalTimer) && (_ppu->GetLastScanline() != _ppu->GetRealScanline() || _state.HorizontalTimer < 339))) &&
(!_state.EnableVerticalIrq || _ppu->GetRealScanline() == _state.VerticalTimer)
);
if(!_irqLevel && irqLevel) {
//Trigger IRQ signal 16 master clocks later
_needIrq = 4;
}
_irqLevel = irqLevel;
_cpu->SetNmiFlag(_state.EnableNmi & _nmiFlag);
}