Merge branch 'master' into new_apu
This commit is contained in:
commit
b9bdd6c49c
@ -250,9 +250,6 @@ void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index
|
|||||||
|
|
||||||
static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
|
static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
|
||||||
{
|
{
|
||||||
uint8_t previous_stat_interrupt_line = gb->stat_interrupt_line;
|
|
||||||
gb->stat_interrupt_line = false;
|
|
||||||
|
|
||||||
if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) {
|
if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) {
|
||||||
/* LCD is disabled, state is constant */
|
/* LCD is disabled, state is constant */
|
||||||
|
|
||||||
@ -261,6 +258,7 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
gb->io_registers[GB_IO_LY] = 0;
|
gb->io_registers[GB_IO_LY] = 0;
|
||||||
gb->io_registers[GB_IO_STAT] &= ~3;
|
gb->io_registers[GB_IO_STAT] &= ~3;
|
||||||
gb->io_registers[GB_IO_STAT] |= 4;
|
gb->io_registers[GB_IO_STAT] |= 4;
|
||||||
|
gb->effective_scx = gb->io_registers[GB_IO_SCX];
|
||||||
if (gb->hdma_on_hblank) {
|
if (gb->hdma_on_hblank) {
|
||||||
gb->hdma_on_hblank = false;
|
gb->hdma_on_hblank = false;
|
||||||
gb->hdma_on = false;
|
gb->hdma_on = false;
|
||||||
@ -293,23 +291,37 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
these findings.
|
these findings.
|
||||||
Todo: Investigate what causes the difference between our findings */
|
Todo: Investigate what causes the difference between our findings */
|
||||||
uint8_t stat_delay = gb->cgb_double_speed? 2 : 4; // (gb->cgb_mode? 0 : 4);
|
uint8_t stat_delay = gb->cgb_double_speed? 2 : 4; // (gb->cgb_mode? 0 : 4);
|
||||||
/* Todo: This is correct for DMG. Is it correct for the 3 CGB modes (DMG/single/double)?*/
|
/* Todo: Is this correct for DMG mode CGB? */
|
||||||
uint8_t scx_delay = ((gb->effective_scx & 7) + atomic_increase - 1) & ~(atomic_increase - 1);
|
uint8_t scx_delay = gb->effective_scx;
|
||||||
|
if (gb->cgb_double_speed) {
|
||||||
|
scx_delay = (scx_delay + 1) & ~1 & 7;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scx_delay = (scx_delay + (gb->first_scanline ? 2 : 0)) & ~3 & 7;
|
||||||
|
}
|
||||||
|
|
||||||
/* Todo: These are correct for DMG, DMG-mode CGB, and single speed CGB. Is is correct for double speed CGB? */
|
/* Todo: These are correct for DMG, DMG-mode CGB, and single speed CGB. Is is correct for double speed CGB? */
|
||||||
uint8_t oam_blocking_rush = gb->cgb_double_speed? 2 : 4;
|
uint8_t oam_blocking_rush = gb->cgb_double_speed? 2 : 4;
|
||||||
uint8_t vram_blocking_rush = gb->is_cgb? 0 : 4;
|
uint8_t vram_blocking_rush = gb->is_cgb? 0 : 4;
|
||||||
|
|
||||||
for (; cycles; cycles -= atomic_increase) {
|
for (; cycles; cycles -= atomic_increase) {
|
||||||
gb->delayed_interrupts &= ~3;
|
bool dmg_future_stat = false;
|
||||||
|
gb->io_registers[GB_IO_IF] |= gb->future_interrupts & 3;
|
||||||
|
gb->future_interrupts &= ~3;
|
||||||
|
|
||||||
|
bool previous_stat_interrupt_line = gb->stat_interrupt_line;
|
||||||
|
gb->stat_interrupt_line = false;
|
||||||
|
|
||||||
gb->display_cycles += atomic_increase;
|
gb->display_cycles += atomic_increase;
|
||||||
/* The very first line is 4 clocks shorter when the LCD turns on. Verified on SGB2, CGB in CGB mode and
|
/* The very first line is 4 clocks shorter when the LCD turns on. Verified on SGB2, CGB in CGB mode and
|
||||||
CGB in double speed mode. */
|
CGB in double speed mode. */
|
||||||
if (gb->first_scanline && gb->display_cycles >= LINE_LENGTH - 4) {
|
if (gb->first_scanline && gb->display_cycles >= LINE_LENGTH - 8) {
|
||||||
gb->first_scanline = false;
|
gb->first_scanline = false;
|
||||||
gb->display_cycles += 4;
|
gb->display_cycles += 4;
|
||||||
}
|
}
|
||||||
bool should_compare_ly = true;
|
bool should_compare_ly = true;
|
||||||
uint8_t ly_for_comparison = gb->io_registers[GB_IO_LY] = gb->display_cycles / LINE_LENGTH;
|
uint8_t ly_for_comparison = gb->io_registers[GB_IO_LY] = gb->display_cycles / LINE_LENGTH;
|
||||||
|
bool just_entered_hblank = false;
|
||||||
|
|
||||||
|
|
||||||
/* Handle cycle completion. STAT's initial value depends on model and mode */
|
/* Handle cycle completion. STAT's initial value depends on model and mode */
|
||||||
@ -343,10 +355,11 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
else if (gb->display_cycles == LINES * LINE_LENGTH + stat_delay) {
|
else if (gb->display_cycles == LINES * LINE_LENGTH + stat_delay) {
|
||||||
gb->io_registers[GB_IO_STAT] &= ~3;
|
gb->io_registers[GB_IO_STAT] &= ~3;
|
||||||
gb->io_registers[GB_IO_STAT] |= 1;
|
gb->io_registers[GB_IO_STAT] |= 1;
|
||||||
gb->io_registers[GB_IO_IF] |= 1;
|
|
||||||
if (gb->is_cgb) {
|
if (gb->is_cgb) {
|
||||||
/* See comment on STAT interrupt at the end of the loop */
|
gb->future_interrupts |= 1;
|
||||||
gb->delayed_interrupts |= 1;
|
}
|
||||||
|
else {
|
||||||
|
gb->io_registers[GB_IO_IF] |= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Entering VBlank state triggers the OAM interrupt. In CGB, it happens 4 cycles earlier */
|
/* Entering VBlank state triggers the OAM interrupt. In CGB, it happens 4 cycles earlier */
|
||||||
@ -382,17 +395,19 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
else if (gb->display_cycles == MODE2_LENGTH) {
|
else if (gb->display_cycles == MODE2_LENGTH) {
|
||||||
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;
|
||||||
|
gb->effective_scx = gb->io_registers[GB_IO_SCX];
|
||||||
gb->oam_read_blocked = true;
|
gb->oam_read_blocked = true;
|
||||||
gb->vram_read_blocked = true;
|
gb->vram_read_blocked = true;
|
||||||
gb->oam_write_blocked = true;
|
gb->oam_write_blocked = true;
|
||||||
gb->vram_write_blocked = true;
|
gb->vram_write_blocked = true;
|
||||||
}
|
}
|
||||||
else if (gb->display_cycles == MODE2_LENGTH + MODE3_LENGTH) {
|
else if (gb->display_cycles == MODE2_LENGTH + MODE3_LENGTH + scx_delay) {
|
||||||
gb->io_registers[GB_IO_STAT] &= ~3;
|
gb->io_registers[GB_IO_STAT] &= ~3;
|
||||||
gb->oam_read_blocked = false;
|
gb->oam_read_blocked = false;
|
||||||
gb->vram_read_blocked = false;
|
gb->vram_read_blocked = false;
|
||||||
gb->oam_write_blocked = false;
|
gb->oam_write_blocked = false;
|
||||||
gb->vram_write_blocked = false;
|
gb->vram_write_blocked = false;
|
||||||
|
just_entered_hblank = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,6 +455,7 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
gb->previous_lcdc_x = - (gb->effective_scx & 0x7);
|
gb->previous_lcdc_x = - (gb->effective_scx & 0x7);
|
||||||
}
|
}
|
||||||
else if (position_in_line == MODE2_LENGTH + MODE3_LENGTH + stat_delay + scx_delay) {
|
else if (position_in_line == MODE2_LENGTH + MODE3_LENGTH + stat_delay + scx_delay) {
|
||||||
|
just_entered_hblank = true;
|
||||||
gb->io_registers[GB_IO_STAT] &= ~3;
|
gb->io_registers[GB_IO_STAT] &= ~3;
|
||||||
gb->oam_read_blocked = false;
|
gb->oam_read_blocked = false;
|
||||||
gb->vram_read_blocked = false;
|
gb->vram_read_blocked = false;
|
||||||
@ -541,15 +557,23 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
|
|
||||||
if (!gb->stat_interrupt_line) {
|
if (!gb->stat_interrupt_line) {
|
||||||
switch (gb->io_registers[GB_IO_STAT] & 3) {
|
switch (gb->io_registers[GB_IO_STAT] & 3) {
|
||||||
case 0: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 8; break;
|
case 0:
|
||||||
|
gb->stat_interrupt_line = (gb->io_registers[GB_IO_STAT] & 8);
|
||||||
|
if (!gb->cgb_double_speed && just_entered_hblank && ((gb->effective_scx + (gb->first_scanline ? 2 : 0)) & 3) == 3) {
|
||||||
|
gb->stat_interrupt_line = false;
|
||||||
|
}
|
||||||
|
else if (just_entered_hblank && ((gb->effective_scx + (gb->first_scanline ? 2 : 0)) & 3) != 0) {
|
||||||
|
dmg_future_stat = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 1: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 0x10; break;
|
case 1: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 0x10; break;
|
||||||
case 2: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 0x20; break;
|
case 2: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 0x20; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Use requested a LY=LYC interrupt and the LY=LYC bit is on */
|
/* User requested a LY=LYC interrupt and the LY=LYC bit is on */
|
||||||
if ((gb->io_registers[GB_IO_STAT] & 0x44) == 0x44) {
|
if ((gb->io_registers[GB_IO_STAT] & 0x44) == 0x44) {
|
||||||
gb->stat_interrupt_line = true;
|
gb->stat_interrupt_line = true;
|
||||||
}
|
dmg_future_stat = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -562,12 +586,12 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gb->stat_interrupt_line && !previous_stat_interrupt_line) {
|
if (gb->stat_interrupt_line && !previous_stat_interrupt_line) {
|
||||||
|
if (gb->is_cgb || dmg_future_stat) {
|
||||||
|
gb->future_interrupts |= 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
gb->io_registers[GB_IO_IF] |= 2;
|
gb->io_registers[GB_IO_IF] |= 2;
|
||||||
if (gb->is_cgb) {
|
}
|
||||||
/* On CGB, the STAT interrupt is not aligned to a T-Cycle, therefore it is only effective the next T-Cycle
|
|
||||||
Todo: verify on DMG mode CGB. This was only tested on LYC STAT interrupts, should be tested on others
|
|
||||||
as well. */
|
|
||||||
gb->delayed_interrupts |= 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,10 +331,8 @@ struct GB_gameboy_internal_s {
|
|||||||
GB_PADDING(uint16_t, serial_cycles);
|
GB_PADDING(uint16_t, serial_cycles);
|
||||||
uint16_t serial_cycles; /* This field changed its meaning in v0.10 */
|
uint16_t serial_cycles; /* This field changed its meaning in v0.10 */
|
||||||
uint16_t serial_length;
|
uint16_t serial_length;
|
||||||
uint8_t delayed_interrupts; /* When an interrupt occurs while not aligned to a T-cycle, it must be "delayed" */
|
uint8_t future_interrupts; /* Interrupts can occur in any T-cycle. Some timings result in different interrupt
|
||||||
bool dont_delay_timer_interrupt; /* If the timer glitch causes a TIMA overflow, it causes the timer to overflow
|
timing when the CPU is in halt mode, and might also affect the DI instruction. */
|
||||||
with different timing, so the triggered interrupt is not delayed.
|
|
||||||
Todo: needs test ROM. */
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/* APU */
|
/* APU */
|
||||||
|
@ -136,7 +136,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
|||||||
if (addr < 0xFF80) {
|
if (addr < 0xFF80) {
|
||||||
switch (addr & 0xFF) {
|
switch (addr & 0xFF) {
|
||||||
case GB_IO_IF:
|
case GB_IO_IF:
|
||||||
return gb->io_registers[GB_IO_IF] | 0xE0;
|
return gb->io_registers[GB_IO_IF] | 0xE0 | gb->future_interrupts;
|
||||||
case GB_IO_TAC:
|
case GB_IO_TAC:
|
||||||
return gb->io_registers[GB_IO_TAC] | 0xF8;
|
return gb->io_registers[GB_IO_TAC] | 0xF8;
|
||||||
case GB_IO_STAT:
|
case GB_IO_STAT:
|
||||||
@ -414,8 +414,9 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
case GB_IO_WX:
|
case GB_IO_WX:
|
||||||
GB_window_related_write(gb, addr & 0xFF, value);
|
GB_window_related_write(gb, addr & 0xFF, value);
|
||||||
break;
|
break;
|
||||||
case GB_IO_SCX:
|
|
||||||
case GB_IO_IF:
|
case GB_IO_IF:
|
||||||
|
gb->future_interrupts = 0;
|
||||||
|
case GB_IO_SCX:
|
||||||
case GB_IO_SCY:
|
case GB_IO_SCY:
|
||||||
case GB_IO_LYC:
|
case GB_IO_LYC:
|
||||||
case GB_IO_BGP:
|
case GB_IO_BGP:
|
||||||
|
@ -82,15 +82,13 @@ static void GB_ir_run(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
static void advance_tima_state_machine(GB_gameboy_t *gb)
|
static void advance_tima_state_machine(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
gb->delayed_interrupts &= ~4;
|
gb->io_registers[GB_IO_IF] |= gb->future_interrupts & 4;
|
||||||
|
gb->future_interrupts &= ~4;
|
||||||
if (gb->tima_reload_state == GB_TIMA_RELOADED) {
|
if (gb->tima_reload_state == GB_TIMA_RELOADED) {
|
||||||
gb->tima_reload_state = GB_TIMA_RUNNING;
|
gb->tima_reload_state = GB_TIMA_RUNNING;
|
||||||
}
|
}
|
||||||
else if (gb->tima_reload_state == GB_TIMA_RELOADING) {
|
else if (gb->tima_reload_state == GB_TIMA_RELOADING) {
|
||||||
gb->io_registers[GB_IO_IF] |= 4;
|
gb->future_interrupts |= 4;
|
||||||
if (!gb->dont_delay_timer_interrupt) {
|
|
||||||
gb->delayed_interrupts |= 4; // Timer interrupt is not aligned to a T-cycle and therefore is effective only the next one.
|
|
||||||
}
|
|
||||||
gb->tima_reload_state = GB_TIMA_RELOADED;
|
gb->tima_reload_state = GB_TIMA_RELOADED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,7 +155,6 @@ static void increase_tima(GB_gameboy_t *gb)
|
|||||||
{
|
{
|
||||||
gb->io_registers[GB_IO_TIMA]++;
|
gb->io_registers[GB_IO_TIMA]++;
|
||||||
if (gb->io_registers[GB_IO_TIMA] == 0) {
|
if (gb->io_registers[GB_IO_TIMA] == 0) {
|
||||||
gb->dont_delay_timer_interrupt = false;
|
|
||||||
gb->io_registers[GB_IO_TIMA] = gb->io_registers[GB_IO_TMA];
|
gb->io_registers[GB_IO_TIMA] = gb->io_registers[GB_IO_TMA];
|
||||||
gb->tima_reload_state = GB_TIMA_RELOADING;
|
gb->tima_reload_state = GB_TIMA_RELOADING;
|
||||||
}
|
}
|
||||||
@ -201,7 +198,6 @@ void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac)
|
|||||||
/* And now either the timer must be disabled, or the new bit used for overflow testing be 0. */
|
/* And now either the timer must be disabled, or the new bit used for overflow testing be 0. */
|
||||||
if (!(new_tac & 4) || gb->div_cycles & (new_clocks >> 1)) {
|
if (!(new_tac & 4) || gb->div_cycles & (new_clocks >> 1)) {
|
||||||
increase_tima(gb);
|
increase_tima(gb);
|
||||||
gb->dont_delay_timer_interrupt = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1337,7 +1337,13 @@ static GB_opcode_t *opcodes[256] = {
|
|||||||
void GB_cpu_run(GB_gameboy_t *gb)
|
void GB_cpu_run(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
gb->vblank_just_occured = false;
|
gb->vblank_just_occured = false;
|
||||||
uint8_t interrupt_queue = gb->interrupt_enable & gb->io_registers[GB_IO_IF] & 0x1F & ~gb->delayed_interrupts;
|
uint8_t interrupt_queue = gb->interrupt_enable & gb->io_registers[GB_IO_IF] & 0x1F;
|
||||||
|
if (!gb->halted) {
|
||||||
|
interrupt_queue |= gb->future_interrupts & gb->interrupt_enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb->io_registers[GB_IO_IF] |= gb->future_interrupts;
|
||||||
|
gb->future_interrupts = 0;
|
||||||
|
|
||||||
if (interrupt_queue) {
|
if (interrupt_queue) {
|
||||||
gb->halted = false;
|
gb->halted = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user