diff --git a/Core/display.c b/Core/display.c index c09764a3..99b8185d 100644 --- a/Core/display.c +++ b/Core/display.c @@ -912,6 +912,9 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) advance_fetcher_state_machine(gb); gb->cycles_for_line++; GB_SLEEP(gb, display, 27, 1); + if (!(gb->io_registers[GB_IO_LCDC] & 2) && !GB_is_cgb(gb)) { + goto abort_fetching_object; + } } /* Todo: Measure if penalty occurs before or after waiting for the fetcher. */ @@ -920,11 +923,19 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) gb->cycles_for_line += gb->extra_penalty_for_sprite_at_0; GB_SLEEP(gb, display, 28, gb->extra_penalty_for_sprite_at_0); gb->extra_penalty_for_sprite_at_0 = 0; + if (gb->object_fetch_aborted) { + gb->object_fetch_aborted = false; + goto abort_fetching_object; + } } } gb->cycles_for_line += 6; GB_SLEEP(gb, display, 20, 6); + if (gb->object_fetch_aborted) { + gb->object_fetch_aborted = false; + goto abort_fetching_object; + } /* TODO: what does the PPU read if DMA is active? */ const GB_object_t *object = &objects[gb->visible_objs[gb->n_visible_objs - 1]]; if (gb->oam_ppu_blocked) { @@ -961,6 +972,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) gb->n_visible_objs--; } +abort_fetching_object: /* Handle window */ /* Todo: Timing (Including penalty and access timings) not verified by test ROM */ if (!gb->in_window && window_enabled(gb) && diff --git a/Core/gb.h b/Core/gb.h index 56b0a9af..4e31e00d 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -518,6 +518,7 @@ struct GB_gameboy_internal_s { bool oam_ppu_blocked; bool vram_ppu_blocked; bool cgb_palettes_ppu_blocked; + bool object_fetch_aborted; ); /* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */ diff --git a/Core/sm83_cpu.c b/Core/sm83_cpu.c index 53bca480..1dbfc5ef 100644 --- a/Core/sm83_cpu.c +++ b/Core/sm83_cpu.c @@ -195,14 +195,51 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value) } case GB_CONFLICT_DMG_LCDC: { - /* Seems to be affected by screen? Both my DMG (B, blob) and Game Boy Light behave this way though. */ + /* Similar to the palette registers, these interact directly with the LCD, so they appear to be affected by it. Both my DMG (B, blob) and Game Boy Light behave this way though. + + Additionally, LCDC.1 is very nasty because on the it is read both by the FIFO when popping pixels, + and the sprite-fetching state machine, and both behave differently when it comes to access conflicts. + Hacks ahead. + */ + + + uint8_t old_value = GB_read_memory(gb, addr); GB_advance_cycles(gb, gb->pending_cycles - 2); + + if (gb->current_lcd_line == 108) { + + } + + /* Handle disabling objects while already fetching an object */ + if ((old_value & 2) && !(value & 2)) { + if (gb->display_state == 27) { + old_value &= ~2; + } + else if (gb->display_state == 20 || gb->display_state == 28) { + gb->cycles_for_line -= gb->display_cycles; + gb->display_cycles = 0; + gb->object_fetch_aborted = true; + } + } + if (/* gb->model != GB_MODEL_MGB && */ gb->position_in_line == 0 && (old_value & 2) && !(value & 2)) { old_value &= ~2; } + GB_write_memory(gb, addr, old_value | (value & 1)); GB_advance_cycles(gb, 1); + /* Handle disabling objects while already fetching an object */ + if ((old_value & 2) && !(value & 2)) { + if (gb->display_state == 27) { + old_value &= ~2; + } + else if (gb->display_state == 20 || gb->display_state == 28) { + gb->cycles_for_line -= gb->display_cycles; + gb->display_cycles = 0; + gb->object_fetch_aborted = true; + } + } GB_write_memory(gb, addr, value); gb->pending_cycles = 5; return;