Skip to content

Commit

Permalink
PCE: Fixed regression that caused incorrect audio in Order of the Gri…
Browse files Browse the repository at this point in the history
…ffon

Timer IRQ appears to behave slightly differently from VDC IRQs, the previous fix for Jackie Chan+Final Soldier broke Order of the Griffon. This fix allows all 3 games to work properly.
  • Loading branch information
SourMesen committed Aug 7, 2023
1 parent 8e79c08 commit 094d433
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 13 deletions.
44 changes: 31 additions & 13 deletions Core/PCE/PceCpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ void PceCpu::Exec()
FetchOperand();
(this->*_opTable[opCode])();

if(_pendingIrqs) {
if(_pendingIrqs || _memoryManager->HasIrqSource(PceIrqSource::TimerIrq)) {
ProcessIrq(false);
}
}
Expand Down Expand Up @@ -274,6 +274,7 @@ void PceCpu::ProcessCpuCycle()
_memoryManager->Exec();

_pendingIrqs = CheckFlag(PceCpuFlags::Interrupt) ? 0 : _memoryManager->GetPendingIrqs();
_prevInterruptFlag = CheckFlag(PceCpuFlags::Interrupt);
}

#ifndef DUMMYCPU
Expand Down Expand Up @@ -383,9 +384,34 @@ uint16_t PceCpu::GetIndYAddr()

void PceCpu::ProcessIrq(bool forBrk)
{
//Keep a copy of _pendingIrqs here, because its value can change
//by the cycles the CPU runs before the value is used at the end
uint8_t pendingIrqs = _pendingIrqs;
//Check target vector before dummy reads, pushing PC/flags, etc.
uint16_t vector;
if(forBrk) {
vector = PceCpu::Irq2Vector;
} else if(!_prevInterruptFlag && _memoryManager->HasIrqSource(PceIrqSource::TimerIrq)) {
//Timer IRQ appears to behave differently from the VDC IRQ2
//When a timer IRQ is pending and the following sequence runs:
// CLI ;enable interrupts
// STA $1403 ;acknowledge timer IRQ
//"D&D: Order of the Griffon" expects the timer IRQ to NOT occur after the STA
//despite the IRQ being active until the last cycle of the STA instruction
vector = PceCpu::TimerIrqVector;
} else if(_pendingIrqs & (uint8_t)PceIrqSource::Irq1) {
vector = PceCpu::Irq1Vector;
} else if(_pendingIrqs & (uint8_t)PceIrqSource::Irq2) {
//In constrast to the timer IRQ above, when a VDC IRQ is pending and the following sequence runs:
// CLI ;enable interrupts
// LDA $0000 ;acknowledge VDC IRQ
//Games expect the VDC IRQ to occur after the LDA because the IRQ
//was active on the before-last cycle of the LDA instruction
//"Jackie Chan" and "Final Soldier" both shake if the IRQ does not occur in this scenario.
//Unsure why both IRQs appear to behave differently (or if there is another explanation for this)
//This has not been tested on hardware.
vector = PceCpu::Irq2Vector;
} else {
//No IRQ actually needs to be processed
return;
}

#ifndef DUMMYCPU
uint16_t originalPc = PC();
Expand Down Expand Up @@ -414,15 +440,7 @@ void PceCpu::ProcessIrq(bool forBrk)
ClearFlags(PceCpuFlags::Decimal | PceCpuFlags::Memory);
SetFlags(PceCpuFlags::Interrupt);

if(forBrk) {
SetPC(MemoryReadWord(PceCpu::Irq2Vector));
} else if(pendingIrqs & (uint8_t)PceIrqSource::TimerIrq) {
SetPC(MemoryReadWord(PceCpu::TimerIrqVector));
} else if(pendingIrqs & (uint8_t)PceIrqSource::Irq1) {
SetPC(MemoryReadWord(PceCpu::Irq1Vector));
} else if(pendingIrqs & (uint8_t)PceIrqSource::Irq2) {
SetPC(MemoryReadWord(PceCpu::Irq2Vector));
}
SetPC(MemoryReadWord(vector));

if(!forBrk) {
#ifndef DUMMYCPU
Expand Down
1 change: 1 addition & 0 deletions Core/PCE/PceCpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class PceCpu final : public ISerializable
bool _memoryFlag = false;

uint8_t _pendingIrqs = 0;
bool _prevInterruptFlag = false;
PceAddrMode _instAddrMode;

private:
Expand Down
1 change: 1 addition & 0 deletions Core/PCE/PceMemoryManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class PceMemoryManager final : public ISerializable

void SetIrqSource(PceIrqSource source) { _state.ActiveIrqs |= (int)source; }
__forceinline uint8_t GetPendingIrqs() { return (_state.ActiveIrqs & ~_state.DisabledIrqs); }
__forceinline bool HasIrqSource(PceIrqSource source) { return (_state.ActiveIrqs & ~_state.DisabledIrqs & (int)source) != 0; }
void ClearIrqSource(PceIrqSource source) { _state.ActiveIrqs &= ~(int)source; }

void Serialize(Serializer& s);
Expand Down

0 comments on commit 094d433

Please sign in to comment.