GBA: Improve delayed IRQ timing

This commit is contained in:
Vicki Pfau 2019-02-24 00:28:49 -08:00
parent cf08815347
commit f9f105a852
12 changed files with 34 additions and 28 deletions

View File

@ -35,6 +35,7 @@ Misc:
- GBA Video: Improve sprite cycle counting (fixes mgba.io/i/1274)
- Debugger: Add breakpoint and watchpoint listing
- Qt: Updated Italian translation (by Vecna)
- GBA: Improve delayed IRQ timing
0.7.0: (2019-01-26)
Features:

View File

@ -143,8 +143,8 @@ void GBADestroy(struct GBA* gba);
void GBAReset(struct ARMCore* cpu);
void GBASkipBIOS(struct GBA* gba);
void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq);
void GBATestIRQ(struct ARMCore* cpu);
void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq, uint32_t cyclesLate);
void GBATestIRQ(struct GBA* gba, uint32_t cyclesLate);
void GBAHalt(struct GBA* gba);
void GBAStop(struct GBA* gba);
void GBADebug(struct GBA* gba, uint16_t value);

View File

@ -193,7 +193,7 @@ void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
dma->nextDest = dma->dest;
}
if (GBADMARegisterIsDoIRQ(dma->reg)) {
GBARaiseIRQ(gba, IRQ_DMA0 + memory->activeDMA);
GBARaiseIRQ(gba, IRQ_DMA0 + memory->activeDMA, cyclesLate);
}
GBADMAUpdate(gba);
}

View File

@ -101,7 +101,7 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle
gate->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0;
gate->d.p->normalControl.start = 0;
if (gate->d.p->normalControl.irq) {
GBARaiseIRQ(gate->d.p->p, IRQ_SIO);
GBARaiseIRQ(gate->d.p->p, IRQ_SIO, cyclesLate);
}
return;
}
@ -193,6 +193,6 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle
gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply;
if (gate->d.p->multiplayerControl.irq) {
GBARaiseIRQ(gate->d.p->p, IRQ_SIO);
GBARaiseIRQ(gate->d.p->p, IRQ_SIO, cyclesLate);
}
}

View File

@ -46,6 +46,7 @@ static void GBAProcessEvents(struct ARMCore* cpu);
static void GBAHitStub(struct ARMCore* cpu, uint32_t opcode);
static void GBAIllegal(struct ARMCore* cpu, uint32_t opcode);
static void GBABreakpoint(struct ARMCore* cpu, int immediate);
static void GBATestIRQNoDelay(struct ARMCore* cpu);
static void _triggerIRQ(struct mTiming*, void* user, uint32_t cyclesLate);
@ -180,7 +181,7 @@ void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh) {
irqh->swi16 = GBASwi16;
irqh->swi32 = GBASwi32;
irqh->hitIllegal = GBAIllegal;
irqh->readCPSR = GBATestIRQ;
irqh->readCPSR = GBATestIRQNoDelay;
irqh->hitStub = GBAHitStub;
irqh->bkpt16 = GBABreakpoint;
irqh->bkpt32 = GBABreakpoint;
@ -433,7 +434,7 @@ void GBAYankROM(struct GBA* gba) {
gba->yankedRomSize = gba->memory.romSize;
gba->memory.romSize = 0;
gba->memory.romMask = 0;
GBARaiseIRQ(gba, IRQ_GAMEPAK);
GBARaiseIRQ(gba, IRQ_GAMEPAK, 0);
}
void GBALoadBIOS(struct GBA* gba, struct VFile* vf) {
@ -486,16 +487,20 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize);
}
void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq) {
void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq, uint32_t cyclesLate) {
gba->memory.io[REG_IF >> 1] |= 1 << irq;
GBATestIRQ(gba->cpu);
GBATestIRQ(gba, cyclesLate);
}
void GBATestIRQ(struct ARMCore* cpu) {
void GBATestIRQNoDelay(struct ARMCore* cpu) {
struct GBA* gba = (struct GBA*) cpu->master;
GBATestIRQ(gba, 0);
}
void GBATestIRQ(struct GBA* gba, uint32_t cyclesLate) {
if (gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) {
if (!mTimingIsScheduled(&gba->timing, &gba->irqEvent)) {
mTimingSchedule(&gba->timing, &gba->irqEvent, GBA_IRQ_DELAY);
mTimingSchedule(&gba->timing, &gba->irqEvent, GBA_IRQ_DELAY - cyclesLate);
}
}
}
@ -846,9 +851,9 @@ void GBATestKeypadIRQ(struct GBA* gba) {
uint16_t keyInput = *gba->keySource & keycnt;
if (isAnd && keycnt == keyInput) {
GBARaiseIRQ(gba, IRQ_KEYPAD);
GBARaiseIRQ(gba, IRQ_KEYPAD, 0);
} else if (!isAnd && keyInput) {
GBARaiseIRQ(gba, IRQ_KEYPAD);
GBARaiseIRQ(gba, IRQ_KEYPAD, 0);
}
}

View File

@ -585,7 +585,7 @@ void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLat
gbp->p->p->memory.io[REG_SIODATA32_LO >> 1] = tx;
gbp->p->p->memory.io[REG_SIODATA32_HI >> 1] = tx >> 16;
if (gbp->d.p->normalControl.irq) {
GBARaiseIRQ(gbp->p->p, IRQ_SIO);
GBARaiseIRQ(gbp->p->p, IRQ_SIO, cyclesLate);
}
gbp->d.p->normalControl.start = 0;
gbp->p->p->memory.io[REG_SIOCNT >> 1] = gbp->d.p->siocnt & ~0x0080;

View File

@ -548,16 +548,16 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
break;
case REG_IE:
gba->memory.io[REG_IE >> 1] = value;
GBATestIRQ(gba->cpu);
GBATestIRQ(gba->cpu, 1);
return;
case REG_IF:
value = gba->memory.io[REG_IF >> 1] & ~value;
gba->memory.io[REG_IF >> 1] = value;
GBATestIRQ(gba->cpu);
GBATestIRQ(gba->cpu, 1);
return;
case REG_IME:
gba->memory.io[REG_IME >> 1] = value;
GBATestIRQ(gba->cpu);
GBATestIRQ(gba->cpu, 1);
return;
case REG_MAX:
// Some bad interrupt libraries will write to this

View File

@ -158,7 +158,7 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
if ((value & 0x0081) == 0x0081) {
if (value & 0x4000) {
// TODO: Test this on hardware to see if this is correct
GBARaiseIRQ(sio->p, IRQ_SIO);
GBARaiseIRQ(sio->p, IRQ_SIO, 0);
}
value &= ~0x0080;
}

View File

@ -37,7 +37,7 @@ int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command
case JOY_RESET:
sio->p->p->memory.io[REG_JOYCNT >> 1] |= 1;
if (sio->p->p->memory.io[REG_JOYCNT >> 1] & 0x40) {
GBARaiseIRQ(sio->p->p, IRQ_SIO);
GBARaiseIRQ(sio->p->p, IRQ_SIO, 0);
}
// Fall through
case JOY_POLL:
@ -55,7 +55,7 @@ int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command
data[0] = sio->p->p->memory.io[REG_JOYSTAT >> 1];
if (sio->p->p->memory.io[REG_JOYCNT >> 1] & 0x40) {
GBARaiseIRQ(sio->p->p, IRQ_SIO);
GBARaiseIRQ(sio->p->p, IRQ_SIO, 0);
}
return 1;
case JOY_TRANS:
@ -68,7 +68,7 @@ int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command
data[4] = sio->p->p->memory.io[REG_JOYSTAT >> 1];
if (sio->p->p->memory.io[REG_JOYCNT >> 1] & 0x40) {
GBARaiseIRQ(sio->p->p, IRQ_SIO);
GBARaiseIRQ(sio->p->p, IRQ_SIO, 0);
}
return 5;
}

View File

@ -166,7 +166,7 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
sio->multiplayerControl.busy = 0;
sio->multiplayerControl.id = node->id;
if (sio->multiplayerControl.irq) {
GBARaiseIRQ(sio->p, IRQ_SIO);
GBARaiseIRQ(sio->p, IRQ_SIO, 0);
}
break;
case SIO_NORMAL_8:
@ -179,7 +179,7 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
node->d.p->p->memory.io[REG_SIODATA8 >> 1] = 0xFFFF;
}
if (sio->multiplayerControl.irq) {
GBARaiseIRQ(sio->p, IRQ_SIO);
GBARaiseIRQ(sio->p, IRQ_SIO, 0);
}
break;
case SIO_NORMAL_32:
@ -194,7 +194,7 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0xFFFF;
}
if (sio->multiplayerControl.irq) {
GBARaiseIRQ(sio->p, IRQ_SIO);
GBARaiseIRQ(sio->p, IRQ_SIO, 0);
}
break;
default:

View File

@ -22,7 +22,7 @@ static void GBATimerUpdate(struct GBA* gba, int timerId, uint32_t cyclesLate) {
}
if (GBATimerFlagsIsDoIrq(timer->flags)) {
GBARaiseIRQ(gba, IRQ_TIMER0 + timerId);
GBARaiseIRQ(gba, IRQ_TIMER0 + timerId, cyclesLate);
}
if (gba->audio.enable && timerId < 2) {

View File

@ -133,7 +133,7 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
GBARaiseIRQ(video->p, IRQ_VCOUNTER);
GBARaiseIRQ(video->p, IRQ_VCOUNTER, cyclesLate);
}
} else {
dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
@ -152,7 +152,7 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
}
GBADMARunVblank(video->p, -cyclesLate);
if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
GBARaiseIRQ(video->p, IRQ_VBLANK);
GBARaiseIRQ(video->p, IRQ_VBLANK, cyclesLate);
}
GBAFrameEnded(video->p);
mCoreSyncPostFrame(video->p->sync);
@ -188,7 +188,7 @@ void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
GBADMARunDisplayStart(video->p, -cyclesLate);
}
if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
GBARaiseIRQ(video->p, IRQ_HBLANK);
GBARaiseIRQ(video->p, IRQ_HBLANK, cyclesLate);
}
video->p->memory.io[REG_DISPSTAT >> 1] = dispstat;
}