Moved the fetcher state machine to another function
This commit is contained in:
parent
0461fb5b2a
commit
0751eae90b
235
Core/display.c
235
Core/display.c
@ -399,6 +399,130 @@ static inline uint8_t fetcher_y(GB_gameboy_t *gb)
|
|||||||
return gb->current_line + (gb->in_window? - gb->io_registers[GB_IO_WY] - gb->wy_diff : gb->io_registers[GB_IO_SCY]);
|
return gb->current_line + (gb->in_window? - gb->io_registers[GB_IO_WY] - gb->wy_diff : gb->io_registers[GB_IO_SCY]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t advance_fetcher_state_machine(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
typedef enum {
|
||||||
|
GB_FETCHER_GET_TILE,
|
||||||
|
GB_FETCHER_GET_TILE_DATA_LOWER,
|
||||||
|
GB_FETCHER_GET_TILE_DATA_HIGH,
|
||||||
|
GB_FETCHER_PUSH,
|
||||||
|
GB_FETCHER_SLEEP,
|
||||||
|
} fetcher_step_t;
|
||||||
|
|
||||||
|
fetcher_step_t fetcher_state_machine [8] = {
|
||||||
|
GB_FETCHER_SLEEP,
|
||||||
|
GB_FETCHER_GET_TILE,
|
||||||
|
GB_FETCHER_SLEEP,
|
||||||
|
GB_FETCHER_GET_TILE_DATA_LOWER,
|
||||||
|
GB_FETCHER_SLEEP,
|
||||||
|
GB_FETCHER_GET_TILE_DATA_HIGH,
|
||||||
|
GB_FETCHER_SLEEP,
|
||||||
|
GB_FETCHER_PUSH,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t delay = 0;
|
||||||
|
|
||||||
|
switch (fetcher_state_machine[gb->fetcher_state]) {
|
||||||
|
case GB_FETCHER_GET_TILE: {
|
||||||
|
uint16_t map = 0x1800;
|
||||||
|
|
||||||
|
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
|
||||||
|
if (gb->io_registers[GB_IO_LCDC] & 0x08 && !gb->in_window) {
|
||||||
|
map = 0x1C00;
|
||||||
|
}
|
||||||
|
else if (gb->io_registers[GB_IO_LCDC] & 0x40 && gb->in_window) {
|
||||||
|
map = 0x1C00;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
|
||||||
|
uint8_t y = fetcher_y(gb);
|
||||||
|
if (gb->is_cgb) {
|
||||||
|
/* This value is cached on the CGB, so it cannot be used to mix tiles together */
|
||||||
|
/* Todo: This is NOT true on CGB-B! This is likely the case for all CGBs prior to D.
|
||||||
|
Currently, SameBoy is emulating CGB-E, but if other revisions are added in the future
|
||||||
|
this should be taken care of*/
|
||||||
|
gb->fetcher_y = y;
|
||||||
|
}
|
||||||
|
gb->current_tile = gb->vram[map + gb->fetcher_x + y / 8 * 32];
|
||||||
|
if (gb->is_cgb) {
|
||||||
|
/* The CGB actually accesses both the tile index AND the attributes in the same T-cycle.
|
||||||
|
This probably means the CGB has a 16-bit data bus for the VRAM. */
|
||||||
|
gb->current_tile_attributes = gb->vram[map + gb->fetcher_x + y / 8 * 32 + 0x2000];
|
||||||
|
}
|
||||||
|
gb->fetcher_x++;
|
||||||
|
gb->fetcher_x &= 0x1f;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GB_FETCHER_GET_TILE_DATA_LOWER: {
|
||||||
|
uint8_t y_flip = 0;
|
||||||
|
uint16_t tile_address = 0;
|
||||||
|
uint8_t y = gb->is_cgb? gb->fetcher_y : fetcher_y(gb);
|
||||||
|
|
||||||
|
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
|
||||||
|
if (gb->io_registers[GB_IO_LCDC] & 0x10) {
|
||||||
|
tile_address = gb->current_tile * 0x10;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tile_address = (int8_t)gb->current_tile * 0x10 + 0x1000;
|
||||||
|
}
|
||||||
|
if (gb->current_tile_attributes & 8) {
|
||||||
|
tile_address += 0x2000;
|
||||||
|
}
|
||||||
|
if (gb->current_tile_attributes & 0x40) {
|
||||||
|
y_flip = 0x7;
|
||||||
|
}
|
||||||
|
gb->current_tile_data[0] =
|
||||||
|
gb->vram[tile_address + ((y & 7) ^ y_flip) * 2];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GB_FETCHER_GET_TILE_DATA_HIGH: {
|
||||||
|
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong.
|
||||||
|
Additionally, on the CGB mixing two tiles by changing the tileset bit
|
||||||
|
mid-fetching causes a glitched mixing of the two, in comparison to the
|
||||||
|
more logical DMG version. */
|
||||||
|
uint16_t tile_address = 0;
|
||||||
|
uint8_t y = gb->is_cgb? gb->fetcher_y : fetcher_y(gb);
|
||||||
|
|
||||||
|
if (gb->io_registers[GB_IO_LCDC] & 0x10) {
|
||||||
|
tile_address = gb->current_tile * 0x10;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tile_address = (int8_t)gb->current_tile * 0x10 + 0x1000;
|
||||||
|
}
|
||||||
|
if (gb->current_tile_attributes & 8) {
|
||||||
|
tile_address += 0x2000;
|
||||||
|
}
|
||||||
|
uint8_t y_flip = 0;
|
||||||
|
if (gb->current_tile_attributes & 0x40) {
|
||||||
|
y_flip = 0x7;
|
||||||
|
}
|
||||||
|
gb->current_tile_data[1] =
|
||||||
|
gb->vram[tile_address + ((y & 7) ^ y_flip) * 2 + 1];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GB_FETCHER_PUSH: {
|
||||||
|
|
||||||
|
fifo_push_bg_row(&gb->bg_fifo, gb->current_tile_data[0], gb->current_tile_data[1],
|
||||||
|
gb->current_tile_attributes & 7, gb->current_tile_attributes & 0x80, gb->current_tile_attributes & 0x20);
|
||||||
|
gb->bg_fifo_paused = false;
|
||||||
|
gb->oam_fifo_paused = false;
|
||||||
|
delay = gb->fetcher_stop_penalty;
|
||||||
|
gb->fetcher_stop_penalty = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GB_FETCHER_SLEEP:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb->fetcher_state++;
|
||||||
|
gb->fetcher_state &= 7;
|
||||||
|
return delay;
|
||||||
|
}
|
||||||
|
|
||||||
void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||||
{
|
{
|
||||||
GB_object_t *objects = (GB_object_t *) &gb->oam;
|
GB_object_t *objects = (GB_object_t *) &gb->oam;
|
||||||
@ -567,8 +691,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
GB_SLEEP(gb, display, 10, 5);
|
GB_SLEEP(gb, display, 10, 5);
|
||||||
|
|
||||||
/* The actual rendering cycle */
|
/* The actual rendering cycle */
|
||||||
gb->fetcher_divisor = false;
|
gb->fetcher_state = 0;
|
||||||
gb->fetcher_state = GB_FETCHER_GET_TILE;
|
|
||||||
gb->bg_fifo_paused = false;
|
gb->bg_fifo_paused = false;
|
||||||
gb->oam_fifo_paused = false;
|
gb->oam_fifo_paused = false;
|
||||||
gb->in_window = false;
|
gb->in_window = false;
|
||||||
@ -589,7 +712,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
if (gb->fetcher_stop_penalty == 0) {
|
if (gb->fetcher_stop_penalty == 0) {
|
||||||
/* TODO: figure out why the penalty works this way and actual access timings */
|
/* TODO: figure out why the penalty works this way and actual access timings */
|
||||||
/* Penalty for interrupting the fetcher */
|
/* Penalty for interrupting the fetcher */
|
||||||
gb->fetcher_stop_penalty = (uint8_t[]){5, 4, 3, 2, 1, 0, 0, 0}[gb->fetcher_state * 2 + gb->fetcher_divisor];
|
gb->fetcher_stop_penalty = (uint8_t[]){5, 4, 3, 2, 1, 0, 0, 0}[gb->fetcher_state];
|
||||||
if (gb->obj_comperators[gb->n_visible_objs - 1] == 0) {
|
if (gb->obj_comperators[gb->n_visible_objs - 1] == 0) {
|
||||||
gb->fetcher_stop_penalty += gb->extra_penalty_for_sprite_at_0;
|
gb->fetcher_stop_penalty += gb->extra_penalty_for_sprite_at_0;
|
||||||
}
|
}
|
||||||
@ -636,108 +759,14 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
gb->bg_fifo_paused = true;
|
gb->bg_fifo_paused = true;
|
||||||
gb->oam_fifo_paused = true;
|
gb->oam_fifo_paused = true;
|
||||||
gb->fetcher_x = 0;
|
gb->fetcher_x = 0;
|
||||||
gb->fetcher_state = GB_FETCHER_GET_TILE;
|
gb->fetcher_state = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->fetcher_divisor) {
|
{
|
||||||
|
uint8_t fetcher_delay = advance_fetcher_state_machine(gb);
|
||||||
switch (gb->fetcher_state) {
|
gb->cycles_for_line += fetcher_delay;
|
||||||
case GB_FETCHER_GET_TILE: {
|
GB_SLEEP(gb, display, 19, fetcher_delay);
|
||||||
uint16_t map = 0x1800;
|
|
||||||
|
|
||||||
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
|
|
||||||
if (gb->io_registers[GB_IO_LCDC] & 0x08 && !gb->in_window) {
|
|
||||||
map = 0x1C00;
|
|
||||||
}
|
|
||||||
else if (gb->io_registers[GB_IO_LCDC] & 0x40 && gb->in_window) {
|
|
||||||
map = 0x1C00;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
|
|
||||||
uint8_t y = fetcher_y(gb);
|
|
||||||
if (gb->is_cgb) {
|
|
||||||
/* This value is cached on the CGB, so it cannot be used to mix tiles together */
|
|
||||||
/* Todo: This is NOT true on CGB-B! This is likely the case for all CGBs prior to D.
|
|
||||||
Currently, SameBoy is emulating CGB-E, but if other revisions are added in the future
|
|
||||||
this should be taken care of*/
|
|
||||||
gb->fetcher_y = y;
|
|
||||||
}
|
|
||||||
gb->current_tile = gb->vram[map + gb->fetcher_x + y / 8 * 32];
|
|
||||||
if (gb->is_cgb) {
|
|
||||||
/* The CGB actually accesses both the tile index AND the attributes in the same T-cycle.
|
|
||||||
This probably means the CGB has a 16-bit data bus for the VRAM. */
|
|
||||||
gb->current_tile_attributes = gb->vram[map + gb->fetcher_x + y / 8 * 32 + 0x2000];
|
|
||||||
}
|
|
||||||
gb->fetcher_x++;
|
|
||||||
gb->fetcher_x &= 0x1f;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GB_FETCHER_GET_TILE_DATA_LOWER: {
|
|
||||||
uint8_t y_flip = 0;
|
|
||||||
uint16_t tile_address = 0;
|
|
||||||
uint8_t y = gb->is_cgb? gb->fetcher_y : fetcher_y(gb);
|
|
||||||
|
|
||||||
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
|
|
||||||
if (gb->io_registers[GB_IO_LCDC] & 0x10) {
|
|
||||||
tile_address = gb->current_tile * 0x10;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
tile_address = (int8_t)gb->current_tile * 0x10 + 0x1000;
|
|
||||||
}
|
|
||||||
if (gb->current_tile_attributes & 8) {
|
|
||||||
tile_address += 0x2000;
|
|
||||||
}
|
|
||||||
if (gb->current_tile_attributes & 0x40) {
|
|
||||||
y_flip = 0x7;
|
|
||||||
}
|
|
||||||
gb->current_tile_data[0] =
|
|
||||||
gb->vram[tile_address + ((y & 7) ^ y_flip) * 2];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GB_FETCHER_GET_TILE_DATA_HIGH: {
|
|
||||||
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong.
|
|
||||||
Additionally, on the CGB mixing two tiles by changing the tileset bit
|
|
||||||
mid-fetching causes a glitched mixing of the two, in comparison to the
|
|
||||||
more logical DMG version. */
|
|
||||||
uint16_t tile_address = 0;
|
|
||||||
uint8_t y = gb->is_cgb? gb->fetcher_y : fetcher_y(gb);
|
|
||||||
|
|
||||||
if (gb->io_registers[GB_IO_LCDC] & 0x10) {
|
|
||||||
tile_address = gb->current_tile * 0x10;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
tile_address = (int8_t)gb->current_tile * 0x10 + 0x1000;
|
|
||||||
}
|
|
||||||
if (gb->current_tile_attributes & 8) {
|
|
||||||
tile_address += 0x2000;
|
|
||||||
}
|
|
||||||
uint8_t y_flip = 0;
|
|
||||||
if (gb->current_tile_attributes & 0x40) {
|
|
||||||
y_flip = 0x7;
|
|
||||||
}
|
|
||||||
gb->current_tile_data[1] =
|
|
||||||
gb->vram[tile_address + ((y & 7) ^ y_flip) * 2 + 1];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GB_FETCHER_SLEEP: {
|
|
||||||
gb->cycles_for_line += gb->fetcher_stop_penalty;
|
|
||||||
GB_SLEEP(gb, display, 19, gb->fetcher_stop_penalty);
|
|
||||||
gb->fetcher_stop_penalty = 0;
|
|
||||||
|
|
||||||
fifo_push_bg_row(&gb->bg_fifo, gb->current_tile_data[0], gb->current_tile_data[1],
|
|
||||||
gb->current_tile_attributes & 7, gb->current_tile_attributes & 0x80, gb->current_tile_attributes & 0x20);
|
|
||||||
gb->bg_fifo_paused = false;
|
|
||||||
gb->oam_fifo_paused = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
gb->fetcher_state++;
|
|
||||||
gb->fetcher_state &= 3;
|
|
||||||
}
|
}
|
||||||
gb->fetcher_divisor ^= true;
|
|
||||||
|
|
||||||
render_pixel_if_possible(gb);
|
render_pixel_if_possible(gb);
|
||||||
if (gb->position_in_line == 160) break;
|
if (gb->position_in_line == 160) break;
|
||||||
|
@ -411,14 +411,7 @@ struct GB_gameboy_internal_s {
|
|||||||
uint8_t current_tile;
|
uint8_t current_tile;
|
||||||
uint8_t current_tile_attributes;
|
uint8_t current_tile_attributes;
|
||||||
uint8_t current_tile_data[2];
|
uint8_t current_tile_data[2];
|
||||||
enum {
|
uint8_t fetcher_state;
|
||||||
GB_FETCHER_GET_TILE,
|
|
||||||
GB_FETCHER_GET_TILE_DATA_LOWER,
|
|
||||||
GB_FETCHER_GET_TILE_DATA_HIGH,
|
|
||||||
GB_FETCHER_SLEEP,
|
|
||||||
GB_FETCHER_MAX = GB_FETCHER_SLEEP,
|
|
||||||
} fetcher_state:8;
|
|
||||||
bool fetcher_divisor; // The fetcher runs at 2MHz
|
|
||||||
bool bg_fifo_paused;
|
bool bg_fifo_paused;
|
||||||
bool oam_fifo_paused;
|
bool oam_fifo_paused;
|
||||||
bool in_window;
|
bool in_window;
|
||||||
|
Loading…
Reference in New Issue
Block a user