mirror of https://github.com/mgba-emu/mgba.git
GBA: Improve delayed IRQ timing
This commit is contained in:
parent
cf08815347
commit
f9f105a852
1
CHANGES
1
CHANGES
|
@ -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:
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue