Cycle accurate OAM search mode

This commit is contained in:
Lior Halphon 2018-03-23 19:07:14 +03:00
parent c11af7ea26
commit 04bfc89816
2 changed files with 23 additions and 33 deletions

View File

@ -289,37 +289,22 @@ void GB_lcd_off(GB_gameboy_t *gb)
gb->ly_for_comparison = 0; gb->ly_for_comparison = 0;
} }
static void search_oam(GB_gameboy_t *gb) static void add_object_from_index(GB_gameboy_t *gb, unsigned index)
{ {
/* if (gb->n_visible_objs == 10) return;
We have very little means to test how the OAM search timing actually
works so we currently implement it atomically.
This is enough for most cases except (All are TODOs):
- The OAM bug on the DMG (Not emulated at all)
- Changing object height via LCDC during mode 2
- What about changing during mode 3?
- Enabling and disabling sprites during mode 2
- Does this flag actually do anything in mode 2? Or only during mode 3?
*/
/* This reverse sorts the visible objects by location and priority */ /* This reverse sorts the visible objects by location and priority */
GB_object_t *objects = (GB_object_t *) &gb->oam; GB_object_t *objects = (GB_object_t *) &gb->oam;
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0; bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0;
gb->n_visible_objs = 0; signed y = objects[index].y - 16;
for (uint8_t i = 0; i < 40; i++) { if (y <= gb->current_line && y + (height_16? 16 : 8) > gb->current_line) {
signed y = objects[i].y - 16; unsigned j = 0;
if (y <= gb->current_line && y + (height_16? 16 : 8) > gb->current_line) { for (; j < gb->n_visible_objs; j++) {
unsigned j = 0; if (objects[gb->visible_objs[j]].x <= objects[index].x) break;
for (; j < gb->n_visible_objs; j++) {
if (objects[gb->visible_objs[j]].x <= objects[i].x) break;
}
memmove(gb->visible_objs + j + 1, gb->visible_objs + j, gb->n_visible_objs - j);
gb->visible_objs[j] = i;
gb->n_visible_objs++;
if (gb->n_visible_objs == 10) return;
} }
memmove(gb->visible_objs + j + 1, gb->visible_objs + j, gb->n_visible_objs - j);
gb->visible_objs[j] = index;
gb->n_visible_objs++;
} }
} }
@ -407,7 +392,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
GB_STATE(gb, display, 6); GB_STATE(gb, display, 6);
GB_STATE(gb, display, 7); GB_STATE(gb, display, 7);
GB_STATE(gb, display, 8); GB_STATE(gb, display, 8);
GB_STATE(gb, display, 9); // GB_STATE(gb, display, 9);
GB_STATE(gb, display, 10); GB_STATE(gb, display, 10);
GB_STATE(gb, display, 11); GB_STATE(gb, display, 11);
GB_STATE(gb, display, 12); GB_STATE(gb, display, 12);
@ -514,14 +499,18 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
gb->oam_write_blocked = true; gb->oam_write_blocked = true;
gb->ly_for_comparison = gb->current_line; gb->ly_for_comparison = gb->current_line;
GB_STAT_update(gb); GB_STAT_update(gb);
GB_SLEEP(gb, display, 8, MODE2_LENGTH - 4); gb->n_visible_objs = 0;
search_oam(gb); for (gb->oam_search_index = 0; gb->oam_search_index < 40; gb->oam_search_index++) {
gb->vram_read_blocked = !gb->is_cgb; add_object_from_index(gb, gb->oam_search_index);
gb->vram_write_blocked = false; GB_SLEEP(gb, display, 8, 2);
gb->oam_write_blocked = gb->is_cgb; if (gb->oam_search_index == 37) {
GB_STAT_update(gb); gb->vram_read_blocked = !gb->is_cgb;
GB_SLEEP(gb, display, 9, 4); gb->vram_write_blocked = false;
gb->oam_write_blocked = gb->is_cgb;
GB_STAT_update(gb);
}
}
gb->io_registers[GB_IO_STAT] &= ~3; gb->io_registers[GB_IO_STAT] &= ~3;
gb->io_registers[GB_IO_STAT] |= 3; gb->io_registers[GB_IO_STAT] |= 3;

View File

@ -426,6 +426,7 @@ struct GB_gameboy_internal_s {
uint8_t visible_objs[10]; uint8_t visible_objs[10];
uint8_t n_visible_objs; uint8_t n_visible_objs;
bool fetching_objects; bool fetching_objects;
uint8_t oam_search_index;
); );
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */ /* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */