Fix pending interrupts not raised on IME/IE writes
Whenever an interrupt is pending and interrupts are disabled (via IME/IE), an IE/IME write that re-enables IRQs will fail to raise an IRQ. This makes some games hang. Most games seem to use CPSR.IRQ to enable and disable interruts, so they are not affected. However some others use IME/IE (or all of them), causing these deadlocks and some race conditions. This fixes a bunch of games that did not crash but would "hang" in some interesting ways.
This commit is contained in:
parent
f9d7f51ef1
commit
2bbd77054e
22
cpu.c
22
cpu.c
|
@ -1573,14 +1573,11 @@ void set_cpu_mode(cpu_mode_type new_mode)
|
||||||
reg[CPU_MODE] = new_mode;
|
reg[CPU_MODE] = new_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void raise_interrupt(irq_type irq_raised)
|
cpu_alert_type check_interrupts()
|
||||||
{
|
{
|
||||||
// The specific IRQ must be enabled in IE, master IRQ enable must be on,
|
// Check any IRQ flag pending, IME and CPSR-IRQ enabled
|
||||||
// and it must be on in the flags.
|
u16 umirq = read_ioreg(REG_IE) & io_registers[REG_IF];
|
||||||
write_ioreg(REG_IF, read_ioreg(REG_IF) | irq_raised);
|
if(!(reg[REG_CPSR] & 0x80) && read_ioreg(REG_IME) && umirq)
|
||||||
|
|
||||||
if((read_ioreg(REG_IE) & irq_raised) && read_ioreg(REG_IME) &&
|
|
||||||
((reg[REG_CPSR] & 0x80) == 0))
|
|
||||||
{
|
{
|
||||||
// Value after the FIQ returns, should be improved
|
// Value after the FIQ returns, should be improved
|
||||||
reg[REG_BUS_VALUE] = 0xe55ec002;
|
reg[REG_BUS_VALUE] = 0xe55ec002;
|
||||||
|
@ -1594,7 +1591,18 @@ void raise_interrupt(irq_type irq_raised)
|
||||||
set_cpu_mode(MODE_IRQ);
|
set_cpu_mode(MODE_IRQ);
|
||||||
reg[CPU_HALT_STATE] = CPU_ACTIVE;
|
reg[CPU_HALT_STATE] = CPU_ACTIVE;
|
||||||
reg[CHANGED_PC_STATUS] = 1;
|
reg[CHANGED_PC_STATUS] = 1;
|
||||||
|
return CPU_ALERT_IRQ;
|
||||||
}
|
}
|
||||||
|
return CPU_ALERT_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void raise_interrupt(irq_type irq_raised)
|
||||||
|
{
|
||||||
|
// The specific IRQ must be enabled in IE, master IRQ enable must be on,
|
||||||
|
// and it must be on in the flags.
|
||||||
|
write_ioreg(REG_IF, read_ioreg(REG_IF) | irq_raised);
|
||||||
|
|
||||||
|
check_interrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef HAVE_DYNAREC
|
#ifndef HAVE_DYNAREC
|
||||||
|
|
1
cpu.h
1
cpu.h
|
@ -110,6 +110,7 @@ typedef enum
|
||||||
extern u32 instruction_count;
|
extern u32 instruction_count;
|
||||||
|
|
||||||
void execute_arm(u32 cycles);
|
void execute_arm(u32 cycles);
|
||||||
|
cpu_alert_type check_interrupts(void);
|
||||||
void raise_interrupt(irq_type irq_raised);
|
void raise_interrupt(irq_type irq_raised);
|
||||||
void set_cpu_mode(cpu_mode_type new_mode);
|
void set_cpu_mode(cpu_mode_type new_mode);
|
||||||
|
|
||||||
|
|
10
gba_memory.c
10
gba_memory.c
|
@ -1339,6 +1339,11 @@ cpu_alert_type function_cc write_io_register16(u32 address, u32 value)
|
||||||
case 0x130:
|
case 0x130:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// REG_IE
|
||||||
|
case 0x200:
|
||||||
|
write_ioreg(REG_IE, value);
|
||||||
|
return check_interrupts();
|
||||||
|
|
||||||
// Interrupt flag
|
// Interrupt flag
|
||||||
case 0x202:
|
case 0x202:
|
||||||
write_ioreg(REG_IF, read_ioreg(REG_IF) & (~value));
|
write_ioreg(REG_IF, read_ioreg(REG_IF) & (~value));
|
||||||
|
@ -1349,6 +1354,11 @@ cpu_alert_type function_cc write_io_register16(u32 address, u32 value)
|
||||||
write_ioreg(REG_WAITCNT, value);
|
write_ioreg(REG_WAITCNT, value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// REG_IME
|
||||||
|
case 0x208:
|
||||||
|
write_ioreg(REG_IME, value);
|
||||||
|
return check_interrupts();
|
||||||
|
|
||||||
// Halt
|
// Halt
|
||||||
case 0x300:
|
case 0x300:
|
||||||
if(((value >> 8) & 0x01) == 0)
|
if(((value >> 8) & 0x01) == 0)
|
||||||
|
|
Loading…
Reference in New Issue