Add DMA cycle accounting for H/V blank and sound DMA

This is just responsible for a few cycles every frame (could be around 1
and 2% depending on the game usage) but makes emulation a bit more
accurate and potentially faster.
This commit is contained in:
David Guillen Fandos 2021-12-16 20:10:00 +01:00
parent 7b181cb6ff
commit bc8c07272b
5 changed files with 32 additions and 17 deletions

View file

@ -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;
}

View file

@ -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);

27
main.c
View file

@ -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)))

View file

@ -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)

View file

@ -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);