Redesigned vblank callback scheduling scheme, should be more regular and less prune to various sorts of frontend DOS
This commit is contained in:
parent
4b3c77bfa5
commit
25e3414974
@ -109,9 +109,11 @@ typedef struct __attribute__((packed)) {
|
|||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
} object_t;
|
} object_t;
|
||||||
|
|
||||||
static void display_vblank(GB_gameboy_t *gb)
|
void GB_display_vblank(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
gb->vblank_just_occured = true;
|
gb->vblank_just_occured = true;
|
||||||
|
gb->cycles_since_vblank_callback = 0;
|
||||||
|
gb->lcd_disabled_outside_of_vblank = false;
|
||||||
|
|
||||||
/* TODO: Slow in turbo mode! */
|
/* TODO: Slow in turbo mode! */
|
||||||
if (GB_is_hle_sgb(gb)) {
|
if (GB_is_hle_sgb(gb)) {
|
||||||
@ -126,7 +128,7 @@ static void display_vblank(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
bool is_ppu_stopped = !GB_is_cgb(gb) && gb->stopped && gb->io_registers[GB_IO_LCDC] & 0x80;
|
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->cgb_repeated_a_frame)) {
|
if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || is_ppu_stopped) || gb->cgb_repeated_a_frame || 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_is_sgb(gb)) {
|
if (!GB_is_sgb(gb)) {
|
||||||
uint32_t color = 0;
|
uint32_t color = 0;
|
||||||
@ -855,12 +857,12 @@ static uint16_t get_object_line_address(GB_gameboy_t *gb, const object_t *object
|
|||||||
*/
|
*/
|
||||||
void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||||
{
|
{
|
||||||
|
gb->cycles_since_vblank_callback += cycles / 2;
|
||||||
|
|
||||||
/* The PPU does not advance while in STOP mode on the DMG */
|
/* The PPU does not advance while in STOP mode on the DMG */
|
||||||
if (gb->stopped && !GB_is_cgb(gb)) {
|
if (gb->stopped && !GB_is_cgb(gb)) {
|
||||||
gb->cycles_in_stop_mode += cycles;
|
if (gb->cycles_since_vblank_callback >= LCDC_PERIOD) {
|
||||||
if (gb->cycles_in_stop_mode >= LCDC_PERIOD) {
|
GB_display_vblank(gb);
|
||||||
gb->cycles_in_stop_mode -= LCDC_PERIOD;
|
|
||||||
display_vblank(gb);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -912,8 +914,10 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
|
|
||||||
if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) {
|
if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) {
|
||||||
while (true) {
|
while (true) {
|
||||||
GB_SLEEP(gb, display, 1, LCDC_PERIOD);
|
if (gb->cycles_since_vblank_callback < LCDC_PERIOD) {
|
||||||
display_vblank(gb);
|
GB_SLEEP(gb, display, 1, LCDC_PERIOD - gb->cycles_since_vblank_callback);
|
||||||
|
}
|
||||||
|
GB_display_vblank(gb);
|
||||||
gb->cgb_repeated_a_frame = true;
|
gb->cgb_repeated_a_frame = true;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -1327,7 +1331,7 @@ abort_fetching_object:
|
|||||||
// Todo: unverified timing
|
// Todo: unverified timing
|
||||||
gb->current_lcd_line++;
|
gb->current_lcd_line++;
|
||||||
if (gb->current_lcd_line == LINES && GB_is_sgb(gb)) {
|
if (gb->current_lcd_line == LINES && GB_is_sgb(gb)) {
|
||||||
display_vblank(gb);
|
GB_display_vblank(gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->icd_hreset_callback) {
|
if (gb->icd_hreset_callback) {
|
||||||
@ -1361,13 +1365,13 @@ abort_fetching_object:
|
|||||||
|
|
||||||
if (gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON) {
|
if (gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON) {
|
||||||
if (GB_is_cgb(gb)) {
|
if (GB_is_cgb(gb)) {
|
||||||
GB_timing_sync(gb);
|
GB_display_vblank(gb);
|
||||||
gb->frame_skip_state = GB_FRAMESKIP_FIRST_FRAME_SKIPPED;
|
gb->frame_skip_state = GB_FRAMESKIP_FIRST_FRAME_SKIPPED;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) {
|
if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) {
|
||||||
gb->is_odd_frame ^= true;
|
gb->is_odd_frame ^= true;
|
||||||
display_vblank(gb);
|
GB_display_vblank(gb);
|
||||||
}
|
}
|
||||||
gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED;
|
gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED;
|
||||||
}
|
}
|
||||||
@ -1375,7 +1379,7 @@ abort_fetching_object:
|
|||||||
else {
|
else {
|
||||||
if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) {
|
if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) {
|
||||||
gb->is_odd_frame ^= true;
|
gb->is_odd_frame ^= true;
|
||||||
display_vblank(gb);
|
GB_display_vblank(gb);
|
||||||
}
|
}
|
||||||
if (gb->frame_skip_state == GB_FRAMESKIP_FIRST_FRAME_SKIPPED) {
|
if (gb->frame_skip_state == GB_FRAMESKIP_FIRST_FRAME_SKIPPED) {
|
||||||
gb->cgb_repeated_a_frame = true;
|
gb->cgb_repeated_a_frame = true;
|
||||||
|
@ -10,6 +10,7 @@ internal void GB_display_run(GB_gameboy_t *gb, uint8_t cycles);
|
|||||||
internal void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index);
|
internal void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index);
|
||||||
internal void GB_STAT_update(GB_gameboy_t *gb);
|
internal void GB_STAT_update(GB_gameboy_t *gb);
|
||||||
internal void GB_lcd_off(GB_gameboy_t *gb);
|
internal void GB_lcd_off(GB_gameboy_t *gb);
|
||||||
|
internal void GB_display_vblank(GB_gameboy_t *gb);
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
GB_OBJECT_PRIORITY_X,
|
GB_OBJECT_PRIORITY_X,
|
||||||
|
@ -514,6 +514,9 @@ struct GB_gameboy_internal_s {
|
|||||||
int32_t speed_switch_halt_countdown;
|
int32_t speed_switch_halt_countdown;
|
||||||
uint8_t speed_switch_countdown; // To compensate for the lack of pipeline emulation
|
uint8_t speed_switch_countdown; // To compensate for the lack of pipeline emulation
|
||||||
uint8_t speed_switch_freeze; // Solely for realigning the PPU, should be removed when the odd modes are implemented
|
uint8_t speed_switch_freeze; // Solely for realigning the PPU, should be removed when the odd modes are implemented
|
||||||
|
/* For timing of the vblank callback */
|
||||||
|
uint32_t cycles_since_vblank_callback;
|
||||||
|
bool lcd_disabled_outside_of_vblank;
|
||||||
);
|
);
|
||||||
|
|
||||||
/* APU */
|
/* APU */
|
||||||
@ -579,7 +582,6 @@ struct GB_gameboy_internal_s {
|
|||||||
bool lyc_interrupt_line;
|
bool lyc_interrupt_line;
|
||||||
bool cgb_palettes_blocked;
|
bool cgb_palettes_blocked;
|
||||||
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;
|
|
||||||
uint8_t object_priority;
|
uint8_t object_priority;
|
||||||
bool oam_ppu_blocked;
|
bool oam_ppu_blocked;
|
||||||
bool vram_ppu_blocked;
|
bool vram_ppu_blocked;
|
||||||
|
@ -1311,6 +1311,21 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
|
|
||||||
case GB_IO_LCDC:
|
case GB_IO_LCDC:
|
||||||
if ((value & 0x80) && !(gb->io_registers[GB_IO_LCDC] & 0x80)) {
|
if ((value & 0x80) && !(gb->io_registers[GB_IO_LCDC] & 0x80)) {
|
||||||
|
if (value & 0x80) {
|
||||||
|
// LCD turned on
|
||||||
|
if (!gb->lcd_disabled_outside_of_vblank &&
|
||||||
|
(gb->cycles_since_vblank_callback > 10 * 456 || GB_is_sgb(gb))) {
|
||||||
|
// Trigger a vblank here so we don't exceed LCDC_PERIOD
|
||||||
|
GB_display_vblank(gb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// LCD turned off
|
||||||
|
if (gb->current_line < 144) {
|
||||||
|
// ROM might be repeatedly disabling LCDC outside of vblank, avoid callback spam
|
||||||
|
gb->lcd_disabled_outside_of_vblank = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
gb->display_cycles = 0;
|
gb->display_cycles = 0;
|
||||||
gb->display_state = 0;
|
gb->display_state = 0;
|
||||||
gb->double_speed_alignment = 0;
|
gb->double_speed_alignment = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user