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) - GBA Video: Improve sprite cycle counting (fixes mgba.io/i/1274)
- Debugger: Add breakpoint and watchpoint listing - Debugger: Add breakpoint and watchpoint listing
- Qt: Updated Italian translation (by Vecna) - Qt: Updated Italian translation (by Vecna)
- GBA: Improve delayed IRQ timing
0.7.0: (2019-01-26) 0.7.0: (2019-01-26)
Features: Features:

View File

@ -143,8 +143,8 @@ void GBADestroy(struct GBA* gba);
void GBAReset(struct ARMCore* cpu); void GBAReset(struct ARMCore* cpu);
void GBASkipBIOS(struct GBA* gba); void GBASkipBIOS(struct GBA* gba);
void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq); void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq, uint32_t cyclesLate);
void GBATestIRQ(struct ARMCore* cpu); void GBATestIRQ(struct GBA* gba, uint32_t cyclesLate);
void GBAHalt(struct GBA* gba); void GBAHalt(struct GBA* gba);
void GBAStop(struct GBA* gba); void GBAStop(struct GBA* gba);
void GBADebug(struct GBA* gba, uint16_t value); 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; dma->nextDest = dma->dest;
} }
if (GBADMARegisterIsDoIRQ(dma->reg)) { if (GBADMARegisterIsDoIRQ(dma->reg)) {
GBARaiseIRQ(gba, IRQ_DMA0 + memory->activeDMA); GBARaiseIRQ(gba, IRQ_DMA0 + memory->activeDMA, cyclesLate);
} }
GBADMAUpdate(gba); 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->p->memory.io[REG_SIODATA32_HI >> 1] = 0;
gate->d.p->normalControl.start = 0; gate->d.p->normalControl.start = 0;
if (gate->d.p->normalControl.irq) { if (gate->d.p->normalControl.irq) {
GBARaiseIRQ(gate->d.p->p, IRQ_SIO); GBARaiseIRQ(gate->d.p->p, IRQ_SIO, cyclesLate);
} }
return; 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; gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply;
if (gate->d.p->multiplayerControl.irq) { 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 GBAHitStub(struct ARMCore* cpu, uint32_t opcode);
static void GBAIllegal(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 GBABreakpoint(struct ARMCore* cpu, int immediate);
static void GBATestIRQNoDelay(struct ARMCore* cpu);
static void _triggerIRQ(struct mTiming*, void* user, uint32_t cyclesLate); static void _triggerIRQ(struct mTiming*, void* user, uint32_t cyclesLate);
@ -180,7 +181,7 @@ void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh) {
irqh->swi16 = GBASwi16; irqh->swi16 = GBASwi16;
irqh->swi32 = GBASwi32; irqh->swi32 = GBASwi32;
irqh->hitIllegal = GBAIllegal; irqh->hitIllegal = GBAIllegal;
irqh->readCPSR = GBATestIRQ; irqh->readCPSR = GBATestIRQNoDelay;
irqh->hitStub = GBAHitStub; irqh->hitStub = GBAHitStub;
irqh->bkpt16 = GBABreakpoint; irqh->bkpt16 = GBABreakpoint;
irqh->bkpt32 = GBABreakpoint; irqh->bkpt32 = GBABreakpoint;
@ -433,7 +434,7 @@ void GBAYankROM(struct GBA* gba) {
gba->yankedRomSize = gba->memory.romSize; gba->yankedRomSize = gba->memory.romSize;
gba->memory.romSize = 0; gba->memory.romSize = 0;
gba->memory.romMask = 0; gba->memory.romMask = 0;
GBARaiseIRQ(gba, IRQ_GAMEPAK); GBARaiseIRQ(gba, IRQ_GAMEPAK, 0);
} }
void GBALoadBIOS(struct GBA* gba, struct VFile* vf) { 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); 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; 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; 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 (gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) {
if (!mTimingIsScheduled(&gba->timing, &gba->irqEvent)) { 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; uint16_t keyInput = *gba->keySource & keycnt;
if (isAnd && keycnt == keyInput) { if (isAnd && keycnt == keyInput) {
GBARaiseIRQ(gba, IRQ_KEYPAD); GBARaiseIRQ(gba, IRQ_KEYPAD, 0);
} else if (!isAnd && keyInput) { } 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_LO >> 1] = tx;
gbp->p->p->memory.io[REG_SIODATA32_HI >> 1] = tx >> 16; gbp->p->p->memory.io[REG_SIODATA32_HI >> 1] = tx >> 16;
if (gbp->d.p->normalControl.irq) { 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->d.p->normalControl.start = 0;
gbp->p->p->memory.io[REG_SIOCNT >> 1] = gbp->d.p->siocnt & ~0x0080; 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; break;
case REG_IE: case REG_IE:
gba->memory.io[REG_IE >> 1] = value; gba->memory.io[REG_IE >> 1] = value;
GBATestIRQ(gba->cpu); GBATestIRQ(gba->cpu, 1);
return; return;
case REG_IF: case REG_IF:
value = gba->memory.io[REG_IF >> 1] & ~value; value = gba->memory.io[REG_IF >> 1] & ~value;
gba->memory.io[REG_IF >> 1] = value; gba->memory.io[REG_IF >> 1] = value;
GBATestIRQ(gba->cpu); GBATestIRQ(gba->cpu, 1);
return; return;
case REG_IME: case REG_IME:
gba->memory.io[REG_IME >> 1] = value; gba->memory.io[REG_IME >> 1] = value;
GBATestIRQ(gba->cpu); GBATestIRQ(gba->cpu, 1);
return; return;
case REG_MAX: case REG_MAX:
// Some bad interrupt libraries will write to this // 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 & 0x0081) == 0x0081) {
if (value & 0x4000) { if (value & 0x4000) {
// TODO: Test this on hardware to see if this is correct // TODO: Test this on hardware to see if this is correct
GBARaiseIRQ(sio->p, IRQ_SIO); GBARaiseIRQ(sio->p, IRQ_SIO, 0);
} }
value &= ~0x0080; value &= ~0x0080;
} }

View File

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

View File

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

View File

@ -22,7 +22,7 @@ static void GBATimerUpdate(struct GBA* gba, int timerId, uint32_t cyclesLate) {
} }
if (GBATimerFlagsIsDoIrq(timer->flags)) { if (GBATimerFlagsIsDoIrq(timer->flags)) {
GBARaiseIRQ(gba, IRQ_TIMER0 + timerId); GBARaiseIRQ(gba, IRQ_TIMER0 + timerId, cyclesLate);
} }
if (gba->audio.enable && timerId < 2) { 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)) { if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
dispstat = GBARegisterDISPSTATFillVcounter(dispstat); dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) { if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
GBARaiseIRQ(video->p, IRQ_VCOUNTER); GBARaiseIRQ(video->p, IRQ_VCOUNTER, cyclesLate);
} }
} else { } else {
dispstat = GBARegisterDISPSTATClearVcounter(dispstat); dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
@ -152,7 +152,7 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
} }
GBADMARunVblank(video->p, -cyclesLate); GBADMARunVblank(video->p, -cyclesLate);
if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) { if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
GBARaiseIRQ(video->p, IRQ_VBLANK); GBARaiseIRQ(video->p, IRQ_VBLANK, cyclesLate);
} }
GBAFrameEnded(video->p); GBAFrameEnded(video->p);
mCoreSyncPostFrame(video->p->sync); mCoreSyncPostFrame(video->p->sync);
@ -188,7 +188,7 @@ void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
GBADMARunDisplayStart(video->p, -cyclesLate); GBADMARunDisplayStart(video->p, -cyclesLate);
} }
if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) { if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
GBARaiseIRQ(video->p, IRQ_HBLANK); GBARaiseIRQ(video->p, IRQ_HBLANK, cyclesLate);
} }
video->p->memory.io[REG_DISPSTAT >> 1] = dispstat; video->p->memory.io[REG_DISPSTAT >> 1] = dispstat;
} }