More accurate emulation of STOP mode
This commit is contained in:
parent
08eb2f3d98
commit
bec09a012c
@ -136,23 +136,18 @@ static void display_vblank(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!gb->disable_rendering || gb->sgb) && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || gb->stopped) || gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON)) {
|
bool is_ppu_stopped = !GB_is_cgb(gb) && gb->stopped && gb->io_registers[GB_IO_LCDC] & 0x80;
|
||||||
|
|
||||||
|
if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || is_ppu_stopped) || gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON)) {
|
||||||
/* LCD is off, set screen to white or black (if LCD is on in stop mode) */
|
/* LCD is off, set screen to white or black (if LCD is on in stop mode) */
|
||||||
if (gb->sgb) {
|
if (!GB_is_sgb(gb)) {
|
||||||
for (unsigned i = 0; i < WIDTH * LINES; i++) {
|
|
||||||
gb->sgb->screen_buffer[i] = 0x0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
uint32_t color = 0;
|
uint32_t color = 0;
|
||||||
if (GB_is_cgb(gb)) {
|
if (GB_is_cgb(gb)) {
|
||||||
color = (gb->io_registers[GB_IO_LCDC] & 0x80) && gb->stopped ?
|
color = gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
|
||||||
gb->rgb_encode_callback(gb, 0, 0, 0) :
|
|
||||||
gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
color = (gb->io_registers[GB_IO_LCDC] & 0x80) && gb->stopped ?
|
color = is_ppu_stopped ?
|
||||||
gb->background_palettes_rgb[3] :
|
gb->background_palettes_rgb[0] :
|
||||||
gb->background_palettes_rgb[4];
|
gb->background_palettes_rgb[4];
|
||||||
}
|
}
|
||||||
if (gb->border_mode == GB_BORDER_ALWAYS) {
|
if (gb->border_mode == GB_BORDER_ALWAYS) {
|
||||||
@ -413,6 +408,10 @@ static void add_object_from_index(GB_gameboy_t *gb, unsigned index)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gb->oam_ppu_blocked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* 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;
|
||||||
@ -499,6 +498,9 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
|||||||
icd_pixel = pixel;
|
icd_pixel = pixel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (gb->cgb_palettes_ppu_blocked) {
|
||||||
|
*dest = gb->rgb_encode_callback(gb, 0, 0, 0);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
*dest = gb->background_palettes_rgb[fifo_item->palette * 4 + pixel];
|
*dest = gb->background_palettes_rgb[fifo_item->palette * 4 + pixel];
|
||||||
}
|
}
|
||||||
@ -521,6 +523,9 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
|||||||
//gb->icd_pixel_callback(gb, pixel);
|
//gb->icd_pixel_callback(gb, pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (gb->cgb_palettes_ppu_blocked) {
|
||||||
|
*dest = gb->rgb_encode_callback(gb, 0, 0, 0);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
*dest = gb->sprite_palettes_rgb[oam_fifo_item->palette * 4 + pixel];
|
*dest = gb->sprite_palettes_rgb[oam_fifo_item->palette * 4 + pixel];
|
||||||
}
|
}
|
||||||
@ -585,10 +590,16 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
|||||||
gb->fetcher_y = y;
|
gb->fetcher_y = y;
|
||||||
}
|
}
|
||||||
gb->current_tile = gb->vram[map + gb->fetcher_x + y / 8 * 32];
|
gb->current_tile = gb->vram[map + gb->fetcher_x + y / 8 * 32];
|
||||||
|
if (gb->vram_ppu_blocked) {
|
||||||
|
gb->current_tile = 0xFF;
|
||||||
|
}
|
||||||
if (GB_is_cgb(gb)) {
|
if (GB_is_cgb(gb)) {
|
||||||
/* The CGB actually accesses both the tile index AND the attributes in the same T-cycle.
|
/* 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. */
|
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->current_tile_attributes = gb->vram[map + gb->fetcher_x + y / 8 * 32 + 0x2000];
|
||||||
|
if (gb->vram_ppu_blocked) {
|
||||||
|
gb->current_tile_attributes = 0xFF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
gb->fetcher_x++;
|
gb->fetcher_x++;
|
||||||
gb->fetcher_x &= 0x1f;
|
gb->fetcher_x &= 0x1f;
|
||||||
@ -616,6 +627,9 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
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) {
|
||||||
|
gb->current_tile_data[0] = 0xFF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
gb->fetcher_state++;
|
gb->fetcher_state++;
|
||||||
break;
|
break;
|
||||||
@ -643,6 +657,9 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
gb->current_tile_data[1] =
|
gb->current_tile_data[1] =
|
||||||
gb->vram[tile_address + ((y & 7) ^ y_flip) * 2 + 1];
|
gb->vram[tile_address + ((y & 7) ^ y_flip) * 2 + 1];
|
||||||
|
if (gb->vram_ppu_blocked) {
|
||||||
|
gb->current_tile_data[1] = 0xFF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
gb->fetcher_state++;
|
gb->fetcher_state++;
|
||||||
break;
|
break;
|
||||||
@ -913,7 +930,11 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
gb->cycles_for_line += 6;
|
gb->cycles_for_line += 6;
|
||||||
GB_SLEEP(gb, display, 20, 6);
|
GB_SLEEP(gb, display, 20, 6);
|
||||||
/* TODO: what does the PPU read if DMA is active? */
|
/* TODO: what does the PPU read if DMA is active? */
|
||||||
GB_object_t *object = &objects[gb->visible_objs[gb->n_visible_objs - 1]];
|
const GB_object_t *object = &objects[gb->visible_objs[gb->n_visible_objs - 1]];
|
||||||
|
if (gb->oam_ppu_blocked) {
|
||||||
|
static const GB_object_t blocked = {0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
object = &blocked;
|
||||||
|
}
|
||||||
|
|
||||||
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0; /* Todo: Which T-cycle actually reads this? */
|
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0; /* Todo: Which T-cycle actually reads this? */
|
||||||
uint8_t tile_y = (gb->current_line - object->y) & (height_16? 0xF : 7);
|
uint8_t tile_y = (gb->current_line - object->y) & (height_16? 0xF : 7);
|
||||||
@ -933,10 +954,9 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
if (gb->cgb_mode) {
|
if (gb->cgb_mode) {
|
||||||
palette = object->flags & 0x7;
|
palette = object->flags & 0x7;
|
||||||
}
|
}
|
||||||
|
|
||||||
fifo_overlay_object_row(&gb->oam_fifo,
|
fifo_overlay_object_row(&gb->oam_fifo,
|
||||||
gb->vram[line_address],
|
gb->vram_ppu_blocked? 0xFF : gb->vram[line_address],
|
||||||
gb->vram[line_address + 1],
|
gb->vram_ppu_blocked? 0xFF : gb->vram[line_address + 1],
|
||||||
palette,
|
palette,
|
||||||
object->flags & 0x80,
|
object->flags & 0x80,
|
||||||
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,
|
||||||
|
@ -515,6 +515,9 @@ struct GB_gameboy_internal_s {
|
|||||||
uint8_t current_lcd_line; // The LCD can go out of sync since the vsync signal is skipped in some cases.
|
uint8_t current_lcd_line; // The LCD can go out of sync since the vsync signal is skipped in some cases.
|
||||||
uint32_t cycles_in_stop_mode;
|
uint32_t cycles_in_stop_mode;
|
||||||
uint8_t object_priority;
|
uint8_t object_priority;
|
||||||
|
bool oam_ppu_blocked;
|
||||||
|
bool vram_ppu_blocked;
|
||||||
|
bool cgb_palettes_ppu_blocked;
|
||||||
);
|
);
|
||||||
|
|
||||||
/* 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 */
|
||||||
|
@ -236,6 +236,26 @@ static void nop(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void enter_stop_mode(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
gb->stopped = true;
|
||||||
|
gb->oam_ppu_blocked = !gb->oam_read_blocked;
|
||||||
|
gb->vram_ppu_blocked = !gb->vram_read_blocked;
|
||||||
|
gb->cgb_palettes_ppu_blocked = !gb->cgb_palettes_blocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void leave_stop_mode(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
/* The CPU takes more time to wake up then the other components */
|
||||||
|
for (unsigned i = 0x200; i--;) {
|
||||||
|
GB_advance_cycles(gb, 0x10);
|
||||||
|
}
|
||||||
|
gb->stopped = false;
|
||||||
|
gb->oam_ppu_blocked = false;
|
||||||
|
gb->vram_ppu_blocked = false;
|
||||||
|
gb->cgb_palettes_ppu_blocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
static void stop(GB_gameboy_t *gb, uint8_t opcode)
|
static void stop(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
if (gb->io_registers[GB_IO_KEY1] & 0x1) {
|
if (gb->io_registers[GB_IO_KEY1] & 0x1) {
|
||||||
@ -252,9 +272,8 @@ static void stop(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
gb->cgb_double_speed ^= true;
|
gb->cgb_double_speed ^= true;
|
||||||
gb->io_registers[GB_IO_KEY1] = 0;
|
gb->io_registers[GB_IO_KEY1] = 0;
|
||||||
|
|
||||||
for (unsigned i = 0x800; i--;) {
|
enter_stop_mode(gb);
|
||||||
GB_advance_cycles(gb, 0x40);
|
leave_stop_mode(gb);
|
||||||
}
|
|
||||||
|
|
||||||
if (!needs_alignment) {
|
if (!needs_alignment) {
|
||||||
GB_advance_cycles(gb, 0x4);
|
GB_advance_cycles(gb, 0x4);
|
||||||
@ -270,7 +289,7 @@ static void stop(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
gb->halted = true;
|
gb->halted = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gb->stopped = true;
|
enter_stop_mode(gb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1429,11 +1448,7 @@ void GB_cpu_run(GB_gameboy_t *gb)
|
|||||||
GB_timing_sync(gb);
|
GB_timing_sync(gb);
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
if ((gb->io_registers[GB_IO_JOYP] & 0xF) != 0xF) {
|
if ((gb->io_registers[GB_IO_JOYP] & 0xF) != 0xF) {
|
||||||
gb->stopped = false;
|
leave_stop_mode(gb);
|
||||||
/* The CPU takes more time to wake up then the other components */
|
|
||||||
for (unsigned i = 0x800; i--;) {
|
|
||||||
GB_advance_cycles(gb, 0x40);
|
|
||||||
}
|
|
||||||
GB_advance_cycles(gb, 8);
|
GB_advance_cycles(gb, 8);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -139,6 +139,11 @@ static void GB_set_internal_div_counter(GB_gameboy_t *gb, uint32_t value)
|
|||||||
|
|
||||||
static void GB_timers_run(GB_gameboy_t *gb, uint8_t cycles)
|
static void GB_timers_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||||
{
|
{
|
||||||
|
if (gb->stopped) {
|
||||||
|
gb->apu.apu_cycles += 4 << !gb->cgb_double_speed;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
GB_STATE_MACHINE(gb, div, cycles, 1) {
|
GB_STATE_MACHINE(gb, div, cycles, 1) {
|
||||||
GB_STATE(gb, div, 1);
|
GB_STATE(gb, div, 1);
|
||||||
GB_STATE(gb, div, 2);
|
GB_STATE(gb, div, 2);
|
||||||
@ -213,8 +218,8 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
// Affected by speed boost
|
// Affected by speed boost
|
||||||
gb->dma_cycles += cycles;
|
gb->dma_cycles += cycles;
|
||||||
|
|
||||||
if (!gb->stopped) {
|
|
||||||
GB_timers_run(gb, cycles);
|
GB_timers_run(gb, cycles);
|
||||||
|
if (!gb->stopped) {
|
||||||
advance_serial(gb, cycles); // TODO: Verify what happens in STOP mode
|
advance_serial(gb, cycles); // TODO: Verify what happens in STOP mode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user