mirror of https://github.com/bsnes-emu/bsnes.git
Implement CGB-mode TILE_SEL mixing, fixes cgb-acid-hell and m3_lcdc_tile_sel_change2, closes #308
This commit is contained in:
parent
c36bdc22f6
commit
7fdc58a07e
|
@ -430,6 +430,23 @@ static void add_object_from_index(GB_gameboy_t *gb, unsigned index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t data_for_tile_sel_glitch(GB_gameboy_t *gb, bool *should_use)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Based on Matt Currie's research here:
|
||||||
|
https://github.com/mattcurrie/mealybug-tearoom-tests/blob/master/the-comprehensive-game-boy-ppu-documentation.md#tile_sel-bit-4
|
||||||
|
*/
|
||||||
|
|
||||||
|
*should_use = true;
|
||||||
|
if (gb->io_registers[GB_IO_LCDC] & 0x10) {
|
||||||
|
*should_use = !(gb->current_tile & 0x80);
|
||||||
|
/* if (gb->model != GB_MODEL_CGB_D) */ return gb->current_tile;
|
||||||
|
// TODO: CGB D behaves differently
|
||||||
|
}
|
||||||
|
return gb->data_for_sel_glitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void render_pixel_if_possible(GB_gameboy_t *gb)
|
static void render_pixel_if_possible(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
GB_fifo_item_t *fifo_item = NULL;
|
GB_fifo_item_t *fifo_item = NULL;
|
||||||
|
@ -621,6 +638,10 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GB_FETCHER_GET_TILE_DATA_LOWER: {
|
case GB_FETCHER_GET_TILE_DATA_LOWER: {
|
||||||
|
bool use_glitched = false;
|
||||||
|
if (gb->tile_sel_glitch) {
|
||||||
|
gb->current_tile_data[0] = data_for_tile_sel_glitch(gb, &use_glitched);
|
||||||
|
}
|
||||||
uint8_t y_flip = 0;
|
uint8_t y_flip = 0;
|
||||||
uint16_t tile_address = 0;
|
uint16_t tile_address = 0;
|
||||||
uint8_t y = gb->model > GB_MODEL_CGB_C ? gb->fetcher_y : fetcher_y(gb);
|
uint8_t y = gb->model > GB_MODEL_CGB_C ? gb->fetcher_y : fetcher_y(gb);
|
||||||
|
@ -638,20 +659,32 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
||||||
if (gb->current_tile_attributes & 0x40) {
|
if (gb->current_tile_attributes & 0x40) {
|
||||||
y_flip = 0x7;
|
y_flip = 0x7;
|
||||||
}
|
}
|
||||||
|
if (!use_glitched) {
|
||||||
gb->current_tile_data[0] =
|
gb->current_tile_data[0] =
|
||||||
gb->vram[tile_address + ((y & 7) ^ y_flip) * 2];
|
gb->vram[tile_address + ((y & 7) ^ y_flip) * 2];
|
||||||
if (gb->vram_ppu_blocked) {
|
if (gb->vram_ppu_blocked) {
|
||||||
gb->current_tile_data[0] = 0xFF;
|
gb->current_tile_data[0] = 0xFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
gb->data_for_sel_glitch =
|
||||||
|
gb->vram[tile_address + ((y & 7) ^ y_flip) * 2];
|
||||||
|
if (gb->vram_ppu_blocked) {
|
||||||
|
gb->data_for_sel_glitch = 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
gb->fetcher_state++;
|
gb->fetcher_state++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GB_FETCHER_GET_TILE_DATA_HIGH: {
|
case GB_FETCHER_GET_TILE_DATA_HIGH: {
|
||||||
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong.
|
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
|
||||||
Additionally, on CGB-D and newer mixing two tiles by changing the tileset
|
|
||||||
bit mid-fetching causes a glitched mixing of the two, in comparison to the
|
bool use_glitched = false;
|
||||||
more logical DMG version. */
|
if (gb->tile_sel_glitch) {
|
||||||
|
gb->current_tile_data[1] = data_for_tile_sel_glitch(gb, &use_glitched);
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t tile_address = 0;
|
uint16_t tile_address = 0;
|
||||||
uint8_t y = gb->model > GB_MODEL_CGB_C ? gb->fetcher_y : fetcher_y(gb);
|
uint8_t y = gb->model > GB_MODEL_CGB_C ? gb->fetcher_y : fetcher_y(gb);
|
||||||
|
|
||||||
|
@ -669,12 +702,22 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
||||||
y_flip = 0x7;
|
y_flip = 0x7;
|
||||||
}
|
}
|
||||||
gb->last_tile_data_address = tile_address + ((y & 7) ^ y_flip) * 2 + 1;
|
gb->last_tile_data_address = tile_address + ((y & 7) ^ y_flip) * 2 + 1;
|
||||||
|
if (!use_glitched) {
|
||||||
gb->current_tile_data[1] =
|
gb->current_tile_data[1] =
|
||||||
gb->vram[gb->last_tile_data_address];
|
gb->vram[gb->last_tile_data_address];
|
||||||
if (gb->vram_ppu_blocked) {
|
if (gb->vram_ppu_blocked) {
|
||||||
gb->current_tile_data[1] = 0xFF;
|
gb->current_tile_data[1] = 0xFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
if ((gb->io_registers[GB_IO_LCDC] & 0x10) && gb->tile_sel_glitch) {
|
||||||
|
gb->data_for_sel_glitch = gb->vram[gb->last_tile_data_address];
|
||||||
|
if (gb->vram_ppu_blocked) {
|
||||||
|
gb->data_for_sel_glitch = 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (gb->wx_triggered) {
|
if (gb->wx_triggered) {
|
||||||
gb->window_tile_x++;
|
gb->window_tile_x++;
|
||||||
gb->window_tile_x &= 0x1f;
|
gb->window_tile_x &= 0x1f;
|
||||||
|
@ -1113,6 +1156,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||||
gb->object_priority == GB_OBJECT_PRIORITY_INDEX? gb->visible_objs[gb->n_visible_objs - 1] : 0,
|
gb->object_priority == GB_OBJECT_PRIORITY_INDEX? gb->visible_objs[gb->n_visible_objs - 1] : 0,
|
||||||
object->flags & 0x20);
|
object->flags & 0x20);
|
||||||
|
|
||||||
|
gb->data_for_sel_glitch = gb->vram_ppu_blocked? 0xFF : gb->vram[line_address + 1];
|
||||||
gb->n_visible_objs--;
|
gb->n_visible_objs--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1128,6 +1172,14 @@ abort_fetching_object:
|
||||||
GB_SLEEP(gb, display, 21, 1);
|
GB_SLEEP(gb, display, 21, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: Verify */
|
||||||
|
if (gb->fetcher_state == 4 || gb->fetcher_state == 5) {
|
||||||
|
gb->data_for_sel_glitch = gb->current_tile_data[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->data_for_sel_glitch = gb->current_tile_data[1];
|
||||||
|
}
|
||||||
|
|
||||||
while (gb->lcd_x != 160 && !gb->disable_rendering && gb->screen && !gb->sgb) {
|
while (gb->lcd_x != 160 && !gb->disable_rendering && gb->screen && !gb->sgb) {
|
||||||
/* Oh no! The PPU and LCD desynced! Fill the rest of the line whith white. */
|
/* Oh no! The PPU and LCD desynced! Fill the rest of the line whith white. */
|
||||||
uint32_t *dest = NULL;
|
uint32_t *dest = NULL;
|
||||||
|
|
|
@ -548,6 +548,7 @@ struct GB_gameboy_internal_s {
|
||||||
uint16_t last_tile_data_address;
|
uint16_t last_tile_data_address;
|
||||||
uint16_t last_tile_index_address;
|
uint16_t last_tile_index_address;
|
||||||
bool cgb_repeated_a_frame;
|
bool cgb_repeated_a_frame;
|
||||||
|
uint8_t data_for_sel_glitch;
|
||||||
);
|
);
|
||||||
|
|
||||||
/* 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 */
|
||||||
|
@ -692,6 +693,7 @@ struct GB_gameboy_internal_s {
|
||||||
|
|
||||||
/* Temporary state */
|
/* Temporary state */
|
||||||
bool wx_just_changed;
|
bool wx_just_changed;
|
||||||
|
bool tile_sel_glitch;
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,12 @@ typedef enum {
|
||||||
GB_CONFLICT_DMG_LCDC,
|
GB_CONFLICT_DMG_LCDC,
|
||||||
GB_CONFLICT_SGB_LCDC,
|
GB_CONFLICT_SGB_LCDC,
|
||||||
GB_CONFLICT_WX,
|
GB_CONFLICT_WX,
|
||||||
|
GB_CONFLICT_CGB_LCDC,
|
||||||
} GB_conflict_t;
|
} GB_conflict_t;
|
||||||
|
|
||||||
/* 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_LCDC] = GB_CONFLICT_CGB_LCDC,
|
||||||
[GB_IO_IF] = GB_CONFLICT_WRITE_CPU,
|
[GB_IO_IF] = GB_CONFLICT_WRITE_CPU,
|
||||||
[GB_IO_LYC] = GB_CONFLICT_WRITE_CPU,
|
[GB_IO_LYC] = GB_CONFLICT_WRITE_CPU,
|
||||||
[GB_IO_STAT] = GB_CONFLICT_STAT_CGB,
|
[GB_IO_STAT] = GB_CONFLICT_STAT_CGB,
|
||||||
|
@ -241,6 +243,34 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||||
gb->wx_just_changed = false;
|
gb->wx_just_changed = false;
|
||||||
gb->pending_cycles = 3;
|
gb->pending_cycles = 3;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case GB_CONFLICT_CGB_LCDC:
|
||||||
|
if ((value ^ gb->io_registers[GB_IO_LCDC]) & 0x10) {
|
||||||
|
// Todo: This is difference is because my timing is off in one of the models
|
||||||
|
if (gb->model > GB_MODEL_CGB_C) {
|
||||||
|
GB_advance_cycles(gb, gb->pending_cycles);
|
||||||
|
gb->tile_sel_glitch = true;
|
||||||
|
GB_advance_cycles(gb, 1);
|
||||||
|
gb->tile_sel_glitch = false;
|
||||||
|
GB_write_memory(gb, addr, value);
|
||||||
|
gb->pending_cycles = 3;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_advance_cycles(gb, gb->pending_cycles - 1);
|
||||||
|
gb->tile_sel_glitch = true;
|
||||||
|
GB_advance_cycles(gb, 1);
|
||||||
|
gb->tile_sel_glitch = false;
|
||||||
|
GB_write_memory(gb, addr, value);
|
||||||
|
gb->pending_cycles = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_advance_cycles(gb, gb->pending_cycles);
|
||||||
|
GB_write_memory(gb, addr, value);
|
||||||
|
gb->pending_cycles = 4;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue