A bit tacky, but T-cycle accurate emulation of LYC write conflicts on the CGB. Only single speed mode verified. Closes #54

This commit is contained in:
Lior Halphon 2018-05-11 12:38:55 +03:00
parent af3554c1d1
commit 713dc02e46
3 changed files with 55 additions and 20 deletions

View File

@ -246,15 +246,17 @@ void GB_STAT_update(GB_gameboy_t *gb)
bool previous_interrupt_line = gb->stat_interrupt_line | gb->oam_interrupt_line;
gb->stat_interrupt_line = gb->oam_interrupt_line;
/* Set LY=LYC bit */
if (gb->ly_for_comparison == gb->io_registers[GB_IO_LYC]) {
gb->lyc_interrupt_line = true;
gb->io_registers[GB_IO_STAT] |= 4;
}
else {
if (gb->ly_for_comparison != (uint16_t)-1) {
gb->lyc_interrupt_line = false;
if (gb->ly_for_comparison != (uint16_t)-1 || !gb->is_cgb) {
if (gb->ly_for_comparison == gb->io_registers[GB_IO_LYC]) {
gb->lyc_interrupt_line = true;
gb->io_registers[GB_IO_STAT] |= 4;
}
else {
if (gb->ly_for_comparison != (uint16_t)-1) {
gb->lyc_interrupt_line = false;
}
gb->io_registers[GB_IO_STAT] &= ~4;
}
gb->io_registers[GB_IO_STAT] &= ~4;
}
switch (gb->io_registers[GB_IO_STAT] & 3) {
@ -565,6 +567,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
GB_STATE(gb, display, 26);
GB_STATE(gb, display, 27);
GB_STATE(gb, display, 28);
GB_STATE(gb, display, 29);
}
if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) {
@ -638,7 +641,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
GB_SLEEP(gb, display, 6, 3);
gb->io_registers[GB_IO_LY] = gb->current_line;
gb->oam_read_blocked = true;
gb->ly_for_comparison = gb->current_line? (gb->is_cgb? gb->current_line - 1 : -1) : 0;
gb->ly_for_comparison = gb->current_line? -1 : 0;
/* The OAM STAT interrupt occurs 1 T-cycle before STAT actually changes, except on line 0.
PPU glitch? (Todo: and in double speed mode?) */
@ -822,9 +825,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
/* Lines 144 - 152 */
for (; gb->current_line < VIRTUAL_LINES - 1; gb->current_line++) {
gb->io_registers[GB_IO_LY] = gb->current_line;
if (!gb->is_cgb) {
gb->ly_for_comparison = -1;
}
gb->ly_for_comparison = -1;
GB_SLEEP(gb, display, 26, 2);
if (gb->current_line == LINES) {
gb->oam_interrupt_line = gb->io_registers[GB_IO_STAT] & 0x20;
@ -866,22 +867,27 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
/* TODO: Verified on SGB2 and CGB-E. Actual interrupt timings not tested. */
/* Lines 153 */
gb->io_registers[GB_IO_LY] = 153;
gb->ly_for_comparison = gb->is_cgb? 152 : -1;
gb->ly_for_comparison = -1;
GB_STAT_update(gb);
GB_SLEEP(gb, display, 14, 6);
GB_SLEEP(gb, display, 14, gb->is_cgb? 4: 6);
if (!gb->is_cgb) {
gb->io_registers[GB_IO_LY] = 0;
}
gb->ly_for_comparison = 153;
GB_STAT_update(gb);
GB_SLEEP(gb, display, 15, gb->is_cgb? 4: 2);
gb->io_registers[GB_IO_LY] = 0;
gb->ly_for_comparison = gb->is_cgb? 0 : 153;
GB_STAT_update(gb);
GB_SLEEP(gb, display, 15, 2);
gb->ly_for_comparison = gb->is_cgb? 153 : -1;
GB_STAT_update(gb);
GB_SLEEP(gb, display, 16, 4);
gb->ly_for_comparison = 0;
GB_STAT_update(gb);
GB_SLEEP(gb, display, 17, LINE_LENGTH - 12);
GB_SLEEP(gb, display, 29, 12); /* Writing to LYC during this period on a CGB has side effects */
GB_SLEEP(gb, display, 17, LINE_LENGTH - 24);
/* Reset window rendering state */
gb->wy_diff = 0;

View File

@ -595,8 +595,36 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
gb->io_registers[addr & 0xFF] = value;
return;
case GB_IO_LYC:
/* TODO: Probably completely wrong in double speed mode */
/* TODO: This hack is disgusting */
if (gb->display_state == 29 && gb->is_cgb) {
gb->ly_for_comparison = 153;
GB_STAT_update(gb);
gb->ly_for_comparison = 0;
}
gb->io_registers[addr & 0xFF] = value;
GB_STAT_update(gb);
/* These are the states when LY changes, let the display routine call GB_STAT_update for use
so it correctly handles T-cycle accurate LYC writes */
if (!gb->is_cgb || (
gb->display_state != 6 &&
gb->display_state != 26 &&
gb->display_state != 15 &&
gb->display_state != 16)) {
/* More hacks to make LYC write conflicts work */
if (gb->display_state == 14 && gb->is_cgb) {
gb->ly_for_comparison = 153;
GB_STAT_update(gb);
gb->ly_for_comparison = -1;
}
else {
GB_STAT_update(gb);
}
}
return;
case GB_IO_TIMA:

View File

@ -20,6 +20,7 @@ typedef enum {
/* Todo: How does double speed mode affect these? */
static const GB_conflict_t cgb_conflict_map[0x80] = {
[GB_IO_IF] = GB_CONFLICT_WRITE_CPU,
[GB_IO_LYC] = GB_CONFLICT_WRITE_CPU,
/* Todo: most values not verified, and probably differ between revisions */
};