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; bool previous_interrupt_line = gb->stat_interrupt_line | gb->oam_interrupt_line;
gb->stat_interrupt_line = gb->oam_interrupt_line; gb->stat_interrupt_line = gb->oam_interrupt_line;
/* Set LY=LYC bit */ /* Set LY=LYC bit */
if (gb->ly_for_comparison == gb->io_registers[GB_IO_LYC]) { if (gb->ly_for_comparison != (uint16_t)-1 || !gb->is_cgb) {
gb->lyc_interrupt_line = true; if (gb->ly_for_comparison == gb->io_registers[GB_IO_LYC]) {
gb->io_registers[GB_IO_STAT] |= 4; gb->lyc_interrupt_line = true;
} gb->io_registers[GB_IO_STAT] |= 4;
else { }
if (gb->ly_for_comparison != (uint16_t)-1) { else {
gb->lyc_interrupt_line = false; 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) { 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, 26);
GB_STATE(gb, display, 27); GB_STATE(gb, display, 27);
GB_STATE(gb, display, 28); GB_STATE(gb, display, 28);
GB_STATE(gb, display, 29);
} }
if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) { 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_SLEEP(gb, display, 6, 3);
gb->io_registers[GB_IO_LY] = gb->current_line; gb->io_registers[GB_IO_LY] = gb->current_line;
gb->oam_read_blocked = true; 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. /* The OAM STAT interrupt occurs 1 T-cycle before STAT actually changes, except on line 0.
PPU glitch? (Todo: and in double speed mode?) */ 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 */ /* Lines 144 - 152 */
for (; gb->current_line < VIRTUAL_LINES - 1; gb->current_line++) { for (; gb->current_line < VIRTUAL_LINES - 1; gb->current_line++) {
gb->io_registers[GB_IO_LY] = 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); GB_SLEEP(gb, display, 26, 2);
if (gb->current_line == LINES) { if (gb->current_line == LINES) {
gb->oam_interrupt_line = gb->io_registers[GB_IO_STAT] & 0x20; 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. */ /* TODO: Verified on SGB2 and CGB-E. Actual interrupt timings not tested. */
/* Lines 153 */ /* Lines 153 */
gb->io_registers[GB_IO_LY] = 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_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->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->ly_for_comparison = gb->is_cgb? 153 : -1;
GB_STAT_update(gb); GB_STAT_update(gb);
GB_SLEEP(gb, display, 16, 4); GB_SLEEP(gb, display, 16, 4);
gb->ly_for_comparison = 0; gb->ly_for_comparison = 0;
GB_STAT_update(gb); 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 */ /* Reset window rendering state */
gb->wy_diff = 0; 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; gb->io_registers[addr & 0xFF] = value;
return; return;
case GB_IO_LYC: 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->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; return;
case GB_IO_TIMA: case GB_IO_TIMA:

View File

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