mirror of https://github.com/mgba-emu/mgba.git
GB: Fix STAT blocking
This commit is contained in:
parent
5031ef7c39
commit
53bcea6373
1
CHANGES
1
CHANGES
|
@ -57,6 +57,7 @@ Bugfixes:
|
||||||
- Test: Fix crash when fuzzing fails to load a file
|
- Test: Fix crash when fuzzing fails to load a file
|
||||||
- Qt: Disable "New multiplayer window" when MAX_GBAS is reached (fixes mgba.io/i/107)
|
- Qt: Disable "New multiplayer window" when MAX_GBAS is reached (fixes mgba.io/i/107)
|
||||||
- LR35902: Fix decoding LD r, $imm and 0-valued immediates (fixes mgba.io/i/735)
|
- LR35902: Fix decoding LD r, $imm and 0-valued immediates (fixes mgba.io/i/735)
|
||||||
|
- GB: Fix STAT blocking
|
||||||
Misc:
|
Misc:
|
||||||
- SDL: Remove scancode key input
|
- SDL: Remove scancode key input
|
||||||
- GBA Video: Clean up unused timers
|
- GBA Video: Clean up unused timers
|
||||||
|
|
|
@ -106,6 +106,33 @@ void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* ren
|
||||||
video->renderer->init(video->renderer, video->p->model);
|
video->renderer->init(video->renderer, video->p->model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool _statIRQAsserted(struct GBVideo* video, GBRegisterSTAT stat) {
|
||||||
|
// TODO: variable for the IRQ line value?
|
||||||
|
if (GBRegisterSTATIsLYCIRQ(stat) && GBRegisterSTATIsLYC(stat)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
switch (GBRegisterSTATGetMode(stat)) {
|
||||||
|
case 0:
|
||||||
|
if (GBRegisterSTATIsHblankIRQ(stat)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (GBRegisterSTATIsVblankIRQ(stat)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (GBRegisterSTATIsOAMIRQ(stat)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
struct GBVideo* video = context;
|
struct GBVideo* video = context;
|
||||||
if (video->frameskipCounter <= 0) {
|
if (video->frameskipCounter <= 0) {
|
||||||
|
@ -115,15 +142,13 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
int32_t next;
|
int32_t next;
|
||||||
++video->ly;
|
++video->ly;
|
||||||
video->p->memory.io[REG_LY] = video->ly;
|
video->p->memory.io[REG_LY] = video->ly;
|
||||||
|
GBRegisterSTAT oldStat = video->stat;
|
||||||
video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->ly);
|
video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->ly);
|
||||||
if (video->ly < GB_VIDEO_VERTICAL_PIXELS) {
|
if (video->ly < GB_VIDEO_VERTICAL_PIXELS) {
|
||||||
// TODO: Cache SCX & 7 in case it changes during mode 2
|
// TODO: Cache SCX & 7 in case it changes during mode 2
|
||||||
next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
|
next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
|
||||||
video->mode = 2;
|
video->mode = 2;
|
||||||
video->modeEvent.callback = _endMode2;
|
video->modeEvent.callback = _endMode2;
|
||||||
if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsOAMIRQ(video->stat)) {
|
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
next = GB_VIDEO_HORIZONTAL_LENGTH;
|
next = GB_VIDEO_HORIZONTAL_LENGTH;
|
||||||
video->mode = 1;
|
video->mode = 1;
|
||||||
|
@ -131,16 +156,16 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
|
|
||||||
mTimingSchedule(&video->p->timing, &video->frameEvent, -cyclesLate);
|
mTimingSchedule(&video->p->timing, &video->frameEvent, -cyclesLate);
|
||||||
|
|
||||||
if (GBRegisterSTATIsVblankIRQ(video->stat) || GBRegisterSTATIsOAMIRQ(video->stat)) {
|
if (!_statIRQAsserted(video, oldStat) && GBRegisterSTATIsOAMIRQ(video->stat)) {
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
}
|
}
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK);
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK);
|
||||||
}
|
}
|
||||||
if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) {
|
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
||||||
|
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
}
|
}
|
||||||
GBUpdateIRQs(video->p);
|
GBUpdateIRQs(video->p);
|
||||||
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
|
||||||
video->p->memory.io[REG_STAT] = video->stat;
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
|
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
|
||||||
}
|
}
|
||||||
|
@ -160,10 +185,6 @@ void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
|
next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
|
||||||
video->mode = 2;
|
video->mode = 2;
|
||||||
video->modeEvent.callback = _endMode2;
|
video->modeEvent.callback = _endMode2;
|
||||||
if (GBRegisterSTATIsOAMIRQ(video->stat)) {
|
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
|
||||||
GBUpdateIRQs(video->p);
|
|
||||||
}
|
|
||||||
if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) {
|
if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) {
|
||||||
video->p->memory.rotation->sample(video->p->memory.rotation);
|
video->p->memory.rotation->sample(video->p->memory.rotation);
|
||||||
}
|
}
|
||||||
|
@ -178,9 +199,10 @@ void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
next = GB_VIDEO_HORIZONTAL_LENGTH;
|
next = GB_VIDEO_HORIZONTAL_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GBRegisterSTAT oldStat = video->stat;
|
||||||
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
||||||
video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->p->memory.io[REG_LY]);
|
video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->p->memory.io[REG_LY]);
|
||||||
if (video->ly && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->p->memory.io[REG_LY]) {
|
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
GBUpdateIRQs(video->p);
|
GBUpdateIRQs(video->p);
|
||||||
}
|
}
|
||||||
|
@ -196,7 +218,12 @@ void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
int32_t next = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 11 - (video->p->memory.io[REG_SCX] & 7);
|
int32_t next = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 11 - (video->p->memory.io[REG_SCX] & 7);
|
||||||
video->mode = 3;
|
video->mode = 3;
|
||||||
video->modeEvent.callback = _endMode3;
|
video->modeEvent.callback = _endMode3;
|
||||||
|
GBRegisterSTAT oldStat = video->stat;
|
||||||
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
||||||
|
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
||||||
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
|
GBUpdateIRQs(video->p);
|
||||||
|
}
|
||||||
video->p->memory.io[REG_STAT] = video->stat;
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
|
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
|
||||||
}
|
}
|
||||||
|
@ -204,10 +231,6 @@ void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
struct GBVideo* video = context;
|
struct GBVideo* video = context;
|
||||||
GBVideoProcessDots(video);
|
GBVideoProcessDots(video);
|
||||||
if (GBRegisterSTATIsHblankIRQ(video->stat)) {
|
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
|
||||||
GBUpdateIRQs(video->p);
|
|
||||||
}
|
|
||||||
if (video->ly < GB_VIDEO_VERTICAL_PIXELS && video->p->memory.isHdma && video->p->memory.io[REG_HDMA5] != 0xFF) {
|
if (video->ly < GB_VIDEO_VERTICAL_PIXELS && video->p->memory.isHdma && video->p->memory.io[REG_HDMA5] != 0xFF) {
|
||||||
video->p->memory.hdmaRemaining = 0x10;
|
video->p->memory.hdmaRemaining = 0x10;
|
||||||
mTimingDeschedule(timing, &video->p->memory.hdmaEvent);
|
mTimingDeschedule(timing, &video->p->memory.hdmaEvent);
|
||||||
|
@ -215,7 +238,12 @@ void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
}
|
}
|
||||||
video->mode = 0;
|
video->mode = 0;
|
||||||
video->modeEvent.callback = _endMode0;
|
video->modeEvent.callback = _endMode0;
|
||||||
|
GBRegisterSTAT oldStat = video->stat;
|
||||||
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
||||||
|
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
||||||
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
|
GBUpdateIRQs(video->p);
|
||||||
|
}
|
||||||
video->p->memory.io[REG_STAT] = video->stat;
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
int32_t next = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 11;
|
int32_t next = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 11;
|
||||||
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
|
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
|
||||||
|
@ -318,9 +346,10 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
||||||
video->ly = 0;
|
video->ly = 0;
|
||||||
video->p->memory.io[REG_LY] = 0;
|
video->p->memory.io[REG_LY] = 0;
|
||||||
// TODO: Does this read as 0 for 4 T-cycles?
|
// TODO: Does this read as 0 for 4 T-cycles?
|
||||||
|
GBRegisterSTAT oldStat = video->stat;
|
||||||
video->stat = GBRegisterSTATSetMode(video->stat, 2);
|
video->stat = GBRegisterSTATSetMode(video->stat, 2);
|
||||||
video->stat = GBRegisterSTATSetLYC(video->stat, video->ly == video->p->memory.io[REG_LYC]);
|
video->stat = GBRegisterSTATSetLYC(video->stat, video->ly == video->p->memory.io[REG_LYC]);
|
||||||
if (GBRegisterSTATIsLYCIRQ(video->stat) && video->ly == video->p->memory.io[REG_LYC]) {
|
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
GBUpdateIRQs(video->p);
|
GBUpdateIRQs(video->p);
|
||||||
}
|
}
|
||||||
|
@ -344,20 +373,21 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) {
|
void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) {
|
||||||
|
GBRegisterSTAT oldStat = video->stat;
|
||||||
video->stat = (video->stat & 0x7) | (value & 0x78);
|
video->stat = (video->stat & 0x7) | (value & 0x78);
|
||||||
if (video->p->model == GB_MODEL_DMG && video->mode == 1) {
|
if (video->p->model == GB_MODEL_DMG && !_statIRQAsserted(video, oldStat) && video->mode < 3) {
|
||||||
|
// TODO: variable for the IRQ line value?
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
GBUpdateIRQs(video->p);
|
GBUpdateIRQs(video->p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBVideoWriteLYC(struct GBVideo* video, uint8_t value) {
|
void GBVideoWriteLYC(struct GBVideo* video, uint8_t value) {
|
||||||
if (video->mode == 2) {
|
GBRegisterSTAT oldStat = video->stat;
|
||||||
video->stat = GBRegisterSTATSetLYC(video->stat, value == video->ly);
|
video->stat = GBRegisterSTATSetLYC(video->stat, value == video->ly);
|
||||||
if (GBRegisterSTATIsLYCIRQ(video->stat) && value == video->ly) {
|
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
GBUpdateIRQs(video->p);
|
GBUpdateIRQs(video->p);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue