diff --git a/gba_memory.c b/gba_memory.c index 2e14ebb..e6e4304 100644 --- a/gba_memory.c +++ b/gba_memory.c @@ -699,7 +699,7 @@ static cpu_alert_type trigger_dma(u32 dma_number, u32 value) write_dmareg(REG_DMA0CNT_H, dma_number, value); if(start_type == DMA_START_IMMEDIATELY) - return dma_transfer(dma_number); + return dma_transfer(dma_number, NULL); } } else @@ -2804,7 +2804,7 @@ static cpu_alert_type dma_transfer_copy( return CPU_ALERT_NONE; } -cpu_alert_type dma_transfer(unsigned dma_chan) +cpu_alert_type dma_transfer(unsigned dma_chan, int *usedcycles) { dma_transfer_type *dmach = &dma[dma_chan]; u32 src_ptr = dmach->source_address & ( @@ -2862,6 +2862,12 @@ cpu_alert_type dma_transfer(unsigned dma_chan) ret = CPU_ALERT_IRQ; } + // This is an approximation for the most common case (no region cross) + if (usedcycles) + *usedcycles += dmach->length * ( + waitstate_cycles_sequential[src_ptr >> 24][tfsizes] + + waitstate_cycles_sequential[dst_ptr >> 24][tfsizes]); + return ret; } diff --git a/gba_memory.h b/gba_memory.h index 6e26260..4564b8b 100644 --- a/gba_memory.h +++ b/gba_memory.h @@ -191,7 +191,7 @@ extern char gamepak_code[5]; extern char gamepak_maker[3]; extern char gamepak_filename[512]; -cpu_alert_type dma_transfer(unsigned dma_chan); +cpu_alert_type dma_transfer(unsigned dma_chan, int *cycles); u8 *memory_region(u32 address, u32 *memory_limit); u32 load_gamepak(const struct retro_game_info* info, const char *name); u32 load_backup(char *name); diff --git a/main.c b/main.c index 16e97d1..35a1531 100644 --- a/main.c +++ b/main.c @@ -39,9 +39,9 @@ char save_path[512]; void trigger_ext_event(void); -static void update_timers(irq_type *irq_raised) +static unsigned update_timers(irq_type *irq_raised) { - unsigned i; + unsigned i, ret = 0; for (i = 0; i < 4; i++) { if(timer[i].status == TIMER_INACTIVE) @@ -70,14 +70,15 @@ static void update_timers(irq_type *irq_raised) if(i < 2) { if(timer[i].direct_sound_channels & 0x01) - sound_timer(timer[i].frequency_step, 0); + ret += sound_timer(timer[i].frequency_step, 0); if(timer[i].direct_sound_channels & 0x02) - sound_timer(timer[i].frequency_step, 1); + ret += sound_timer(timer[i].frequency_step, 1); } timer[i].count += (timer[i].reload << timer[i].prescale); } + return ret; } void init_main(void) @@ -109,6 +110,7 @@ void init_main(void) u32 update_gba(void) { irq_type irq_raised = IRQ_NONE; + int dma_cycles; do { @@ -125,7 +127,8 @@ u32 update_gba(void) gbc_sound_update = 0; } - update_timers(&irq_raised); + // Timers can trigger DMA (usually sound) and consume cycles + dma_cycles = update_timers(&irq_raised); video_count -= execute_cycles; @@ -152,7 +155,7 @@ u32 update_gba(void) for(i = 0; i < 4; i++) { if(dma[i].start_type == DMA_START_HBLANK) - dma_transfer(i); + dma_transfer(i, &dma_cycles); } } @@ -183,7 +186,7 @@ u32 update_gba(void) for(i = 0; i < 4; i++) { if(dma[i].start_type == DMA_START_VBLANK) - dma_transfer(i); + dma_transfer(i, &dma_cycles); } } else @@ -235,7 +238,7 @@ u32 update_gba(void) if(irq_raised) raise_interrupt(irq_raised); - execute_cycles = video_count; + execute_cycles = MAX(0, video_count); for (i = 0; i < 4; i++) { @@ -247,7 +250,11 @@ u32 update_gba(void) } } while(reg[CPU_HALT_STATE] != CPU_ACTIVE && !reg[COMPLETED_FRAME]); - return execute_cycles; + // We voluntarily limit this. It is not accurate but it would be much harder. + dma_cycles = MIN(64, dma_cycles); + dma_cycles = MIN(execute_cycles, dma_cycles); + + return execute_cycles - dma_cycles; } void reset_gba(void) @@ -288,7 +295,7 @@ bool main_read_savestate(const u8 *src) const u8 *p2 = bson_find_key(src, "timers"); if (!p1 || !p2) return false; - execute_cycles = 123; + if (!(bson_read_int32(p1, "cpu-ticks", &cpu_ticks) && bson_read_int32(p1, "exec-cycles", &execute_cycles) && bson_read_int32(p1, "video-count", (u32*)&video_count))) diff --git a/sound.c b/sound.c index a99c9b2..e3ab45d 100644 --- a/sound.c +++ b/sound.c @@ -74,8 +74,9 @@ void sound_timer_queue32(u32 channel, u32 value) } -void sound_timer(fixed8_24 frequency_step, u32 channel) +unsigned sound_timer(fixed8_24 frequency_step, u32 channel) { + int ret = 0; u32 sample_status = DIRECT_SOUND_INACTIVE; direct_sound_struct *ds = &direct_sound_channel[channel]; @@ -160,11 +161,12 @@ void sound_timer(fixed8_24 frequency_step, u32 channel) if(((ds->fifo_top - ds->fifo_base) % 32) <= 16) { if(dma[1].direct_sound_channel == channel) - dma_transfer(1); + dma_transfer(1, &ret); if(dma[2].direct_sound_channel == channel) - dma_transfer(2); + dma_transfer(2, &ret); } + return ret; } void sound_reset_fifo(u32 channel) diff --git a/sound.h b/sound.h index 28076dc..e2c7145 100644 --- a/sound.h +++ b/sound.h @@ -101,7 +101,7 @@ extern u32 sound_on; void sound_timer_queue8(u32 channel, u8 value); void sound_timer_queue16(u32 channel, u16 value); void sound_timer_queue32(u32 channel, u32 value); -void sound_timer(fixed8_24 frequency_step, u32 channel); +unsigned sound_timer(fixed8_24 frequency_step, u32 channel); void sound_reset_fifo(u32 channel); void update_gbc_sound(u32 cpu_ticks); void init_sound(int need_reset);