implement all types of RTC IRQ

This commit is contained in:
Arisotura 2023-10-29 14:52:23 +01:00
parent 9ccdc23fa0
commit b90576b757
1 changed files with 128 additions and 26 deletions

View File

@ -249,7 +249,7 @@ void SetIRQ(u8 irq)
if ((!(oldstat & 0x30)) && (State.IRQFlag & 0x30)) if ((!(oldstat & 0x30)) && (State.IRQFlag & 0x30))
{ {
if ((NDS::RCnt & 0xC100) == 0x8100) if ((NDS::RCnt & 0xC100) == 0x8100)
{printf("IRQ\n"); {
// CHECKME: is the IRQ status readable in RCNT? // CHECKME: is the IRQ status readable in RCNT?
NDS::SetIRQ(1, NDS::IRQ_RTC); NDS::SetIRQ(1, NDS::IRQ_RTC);
} }
@ -261,6 +261,125 @@ void ClearIRQ(u8 irq)
State.IRQFlag &= ~irq; State.IRQFlag &= ~irq;
} }
void ProcessIRQ(int type) // 0=minute carry 1=periodic 2=status reg write
{
// INT1
switch (State.StatusReg2 & 0x0F)
{
case 0b0000: // none
if (type == 2)
{
ClearIRQ(0x10);
}
break;
case 0b0001:
case 0b0101: // selected frequency steady interrupt
if ((type == 1 && (!(ClockCount & 0x3FF))) || (type == 2))
{
u32 mask = 0;
if (State.Alarm1[2] & (1<<0)) mask |= 0x4000;
if (State.Alarm1[2] & (1<<1)) mask |= 0x2000;
if (State.Alarm1[2] & (1<<2)) mask |= 0x1000;
if (State.Alarm1[2] & (1<<3)) mask |= 0x0800;
if (State.Alarm1[2] & (1<<4)) mask |= 0x0400;
if (mask && ((ClockCount & mask) != mask))
SetIRQ(0x10);
else
ClearIRQ(0x10);
}
break;
case 0b0010:
case 0b0110: // per-minute edge interrupt
if ((type == 0) || (type == 2 && (State.IRQFlag & 0x01)))
{
SetIRQ(0x10);
}
break;
case 0b0011: // per-minute steady interrupt 1 (duty 30s)
if ((type == 0) || (type == 2 && (State.IRQFlag & 0x01)))
{
SetIRQ(0x10);
}
else if ((type == 1) && (State.DateTime[6] == 0x30))
{
ClearIRQ(0x10);
}
break;
case 0b0111: // per-minute steady interrupt 2 (duty 256 cycles)
if ((type == 0) || (type == 2 && (State.IRQFlag & 0x01)))
{
SetIRQ(0x10);
}
else if ((type == 1) && (State.DateTime[6] == 0x00) && ((ClockCount & 0x7FFF) == 256))
{
ClearIRQ(0x10);
}
break;
case 0b0100: // alarm interrupt
if (type == 0)
{
bool cond = true;
if (State.Alarm1[0] & (1<<7))
cond = cond && ((State.Alarm1[0] & 0x07) == State.DateTime[3]);
if (State.Alarm1[1] & (1<<7))
cond = cond && ((State.Alarm1[1] & 0x7F) == State.DateTime[4]);
if (State.Alarm1[2] & (1<<7))
cond = cond && ((State.Alarm1[2] & 0x7F) == State.DateTime[5]);
if (cond)
SetIRQ(0x10);
else
ClearIRQ(0x10);
}
break;
default: // 32KHz output
if (type == 1)
{
SetIRQ(0x10);
ClearIRQ(0x10);
}
break;
}
// INT2
if (State.StatusReg2 & (1<<6))
{
// alarm interrupt
if (type == 0)
{
bool cond = true;
if (State.Alarm2[0] & (1<<7))
cond = cond && ((State.Alarm2[0] & 0x07) == State.DateTime[3]);
if (State.Alarm2[1] & (1<<7))
cond = cond && ((State.Alarm2[1] & 0x7F) == State.DateTime[4]);
if (State.Alarm2[2] & (1<<7))
cond = cond && ((State.Alarm2[2] & 0x7F) == State.DateTime[5]);
if (cond)
SetIRQ(0x20);
else
ClearIRQ(0x20);
}
}
else
{
if (type == 2)
{
ClearIRQ(0x20);
}
}
}
u8 DaysInMonth() u8 DaysInMonth()
{ {
@ -382,6 +501,9 @@ void CountMinute()
State.DateTime[5] = 0; State.DateTime[5] = 0;
CountHour(); CountHour();
} }
State.IRQFlag |= 0x01; // store minute carry flag
ProcessIRQ(0);
} }
void CountSecond() void CountSecond()
@ -417,29 +539,13 @@ void ClockTimer(u32 param)
// count up one second // count up one second
CountSecond(); CountSecond();
} }
else if ((ClockCount & 0x7FFF) == 4)
if (State.StatusReg2 & (1<<3))
{ {
// 32KHz IRQ output // minute-carry flag lasts 4 cycles
SetIRQ(0x10); State.IRQFlag &= ~0x01;
ClearIRQ(0x10);
} }
else if ((State.StatusReg2 & 0x03) == 0x01)
{
// selected frequency steady interrupt
u32 mask = 0; ProcessIRQ(1);
if (State.Alarm1[2] & (1<<0)) mask |= 0x4000;
if (State.Alarm1[2] & (1<<1)) mask |= 0x2000;
if (State.Alarm1[2] & (1<<2)) mask |= 0x1000;
if (State.Alarm1[2] & (1<<3)) mask |= 0x0800;
if (State.Alarm1[2] & (1<<4)) mask |= 0x0400;
if (mask && ((ClockCount & mask) != mask))
SetIRQ(0x10);
else
ClearIRQ(0x10);
}
ScheduleTimer(false); ScheduleTimer(false);
} }
@ -647,11 +753,7 @@ void CmdWrite(u8 val)
if (InputPos == 1) if (InputPos == 1)
{ {
State.StatusReg2 = val; State.StatusReg2 = val;
if (State.StatusReg2 & 0x4F) ProcessIRQ(2);
Log(LogLevel::Debug, "RTC INTERRUPT ON: %02X, %02X %02X %02X, %02X %02X %02X\n",
State.StatusReg2,
State.Alarm1[0], State.Alarm1[1], State.Alarm1[2],
State.Alarm2[0], State.Alarm2[1], State.Alarm2[2]);
} }
break; break;