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)
|
||||
- Debugger: Add breakpoint and watchpoint listing
|
||||
- Qt: Updated Italian translation (by Vecna)
|
||||
- GBA: Improve delayed IRQ timing
|
||||
|
||||
0.7.0: (2019-01-26)
|
||||
Features:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue