Adding DMA transfer "Sleep" mode
This accounts for DMA stealing cycles from the CPU whenever the CPU triggers a DMA (does not affect H/V blank or sound DMAs). Works by moving the CPU to a PAUSED state where the cycles are accounted for, reusing a similar mechanism for HALT/STOP. Fixes a couple of games, notably GTA that has a DMA/IRQ race condition (likely a bug really) if cycles are grossly miscalculated.
This commit is contained in:
parent
2352adcc50
commit
c0d8ffaa38
|
@ -57,7 +57,7 @@ _##symbol:
|
||||||
#define REG_Z_FLAG (21 * 4)
|
#define REG_Z_FLAG (21 * 4)
|
||||||
#define REG_C_FLAG (22 * 4)
|
#define REG_C_FLAG (22 * 4)
|
||||||
#define REG_V_FLAG (23 * 4)
|
#define REG_V_FLAG (23 * 4)
|
||||||
#define REG_UNUSED_1 (24 * 4)
|
#define REG_SLEEP_CYCLES (24 * 4)
|
||||||
#define OAM_UPDATED (25 * 4)
|
#define OAM_UPDATED (25 * 4)
|
||||||
#define REG_SAVE (26 * 4)
|
#define REG_SAVE (26 * 4)
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ _##symbol:
|
||||||
#define REG_Z_FLAG (21 * 4)
|
#define REG_Z_FLAG (21 * 4)
|
||||||
#define REG_C_FLAG (22 * 4)
|
#define REG_C_FLAG (22 * 4)
|
||||||
#define REG_V_FLAG (23 * 4)
|
#define REG_V_FLAG (23 * 4)
|
||||||
#define REG_UNUSED_1 (24 * 4)
|
#define REG_SLEEP_CYCLES (24 * 4)
|
||||||
#define OAM_UPDATED (25 * 4)
|
#define OAM_UPDATED (25 * 4)
|
||||||
|
|
||||||
#define CPU_ALERT_HALT (1 << 0)
|
#define CPU_ALERT_HALT (1 << 0)
|
||||||
|
|
8
cpu.c
8
cpu.c
|
@ -1489,7 +1489,12 @@ u32 check_and_raise_interrupts()
|
||||||
reg[REG_PC] = 0x00000018;
|
reg[REG_PC] = 0x00000018;
|
||||||
|
|
||||||
set_cpu_mode(MODE_IRQ);
|
set_cpu_mode(MODE_IRQ);
|
||||||
reg[CPU_HALT_STATE] = CPU_ACTIVE;
|
|
||||||
|
// Wake up CPU if it is stopped/sleeping.
|
||||||
|
if (reg[CPU_HALT_STATE] == CPU_STOP ||
|
||||||
|
reg[CPU_HALT_STATE] == CPU_HALT)
|
||||||
|
reg[CPU_HALT_STATE] = CPU_ACTIVE;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -3690,6 +3695,7 @@ void init_cpu(void)
|
||||||
spsr[i] = 0x00000010;
|
spsr[i] = 0x00000010;
|
||||||
|
|
||||||
reg[CPU_HALT_STATE] = CPU_ACTIVE;
|
reg[CPU_HALT_STATE] = CPU_ACTIVE;
|
||||||
|
reg[REG_SLEEP_CYCLES] = 0;
|
||||||
|
|
||||||
if (selected_boot_mode == boot_game) {
|
if (selected_boot_mode == boot_game) {
|
||||||
reg[REG_SP] = 0x03007F00;
|
reg[REG_SP] = 0x03007F00;
|
||||||
|
|
3
cpu.h
3
cpu.h
|
@ -45,6 +45,7 @@ typedef u32 cpu_mode_type;
|
||||||
#define CPU_ACTIVE 0
|
#define CPU_ACTIVE 0
|
||||||
#define CPU_HALT 1
|
#define CPU_HALT 1
|
||||||
#define CPU_STOP 2
|
#define CPU_STOP 2
|
||||||
|
#define CPU_DMA 3 /* CPU is idling due to DMA transfer */
|
||||||
|
|
||||||
typedef u8 cpu_alert_type;
|
typedef u8 cpu_alert_type;
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ typedef enum
|
||||||
REG_Z_FLAG = 21,
|
REG_Z_FLAG = 21,
|
||||||
REG_C_FLAG = 22,
|
REG_C_FLAG = 22,
|
||||||
REG_V_FLAG = 23,
|
REG_V_FLAG = 23,
|
||||||
REG_UNUSED_1 = 24,
|
REG_SLEEP_CYCLES = 24,
|
||||||
OAM_UPDATED = 25,
|
OAM_UPDATED = 25,
|
||||||
REG_SAVE = 26,
|
REG_SAVE = 26,
|
||||||
REG_SAVE2 = 27,
|
REG_SAVE2 = 27,
|
||||||
|
|
13
gba_memory.c
13
gba_memory.c
|
@ -761,8 +761,17 @@ static cpu_alert_type trigger_dma(u32 dma_number, u32 value)
|
||||||
}
|
}
|
||||||
|
|
||||||
write_dmareg(REG_DMA0CNT_H, dma_number, value);
|
write_dmareg(REG_DMA0CNT_H, dma_number, value);
|
||||||
if(start_type == DMA_START_IMMEDIATELY)
|
if(start_type == DMA_START_IMMEDIATELY) {
|
||||||
return dma_transfer(dma_number, NULL);
|
// Excutes the DMA now! Copies the data and returns side effects.
|
||||||
|
int dma_cycles = 0;
|
||||||
|
cpu_alert_type ret = dma_transfer(dma_number, &dma_cycles);
|
||||||
|
if (!dma_cycles)
|
||||||
|
return ret;
|
||||||
|
// Sleep CPU for N cycles and return HALT as side effect (so it does).
|
||||||
|
reg[CPU_HALT_STATE] = CPU_DMA;
|
||||||
|
reg[REG_SLEEP_CYCLES] = 0x80000000 | (u32)dma_cycles;
|
||||||
|
return CPU_ALERT_HALT | ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
23
main.c
23
main.c
|
@ -247,6 +247,22 @@ u32 function_cc update_gba(int remaining_cycles)
|
||||||
// a video event or a timer event, whatever happens first.
|
// a video event or a timer event, whatever happens first.
|
||||||
execute_cycles = MAX(video_count, 0);
|
execute_cycles = MAX(video_count, 0);
|
||||||
|
|
||||||
|
// If we are paused due to a DMA, cap the number of cyles to that amount.
|
||||||
|
if (reg[CPU_HALT_STATE] == CPU_DMA) {
|
||||||
|
u32 dma_cyc = reg[REG_SLEEP_CYCLES];
|
||||||
|
// The first iteration is marked by bit 31 set, just do nothing now.
|
||||||
|
if (dma_cyc & 0x80000000)
|
||||||
|
dma_cyc &= 0x7FFFFFFF; // Start counting DMA cycles from now on.
|
||||||
|
else
|
||||||
|
dma_cyc -= MIN(dma_cyc, completed_cycles); // Account DMA cycles.
|
||||||
|
|
||||||
|
reg[REG_SLEEP_CYCLES] = dma_cyc;
|
||||||
|
if (!dma_cyc)
|
||||||
|
reg[CPU_HALT_STATE] = CPU_ACTIVE; // DMA finished, resume execution.
|
||||||
|
else
|
||||||
|
execute_cycles = MIN(execute_cycles, dma_cyc); // Continue sleeping.
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
if (timer[i].status == TIMER_PRESCALE &&
|
if (timer[i].status == TIMER_PRESCALE &&
|
||||||
|
@ -294,7 +310,8 @@ bool main_check_savestate(const u8 *src)
|
||||||
|
|
||||||
if (!bson_contains_key(p1, "cpu-ticks", BSON_TYPE_INT32) ||
|
if (!bson_contains_key(p1, "cpu-ticks", BSON_TYPE_INT32) ||
|
||||||
!bson_contains_key(p1, "exec-cycles", BSON_TYPE_INT32) ||
|
!bson_contains_key(p1, "exec-cycles", BSON_TYPE_INT32) ||
|
||||||
!bson_contains_key(p1, "video-count", BSON_TYPE_INT32))
|
!bson_contains_key(p1, "video-count", BSON_TYPE_INT32) ||
|
||||||
|
!bson_contains_key(p1, "sleep-cycles", BSON_TYPE_INT32))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
|
@ -327,7 +344,8 @@ bool main_read_savestate(const u8 *src)
|
||||||
|
|
||||||
if (!(bson_read_int32(p1, "cpu-ticks", &cpu_ticks) &&
|
if (!(bson_read_int32(p1, "cpu-ticks", &cpu_ticks) &&
|
||||||
bson_read_int32(p1, "exec-cycles", &execute_cycles) &&
|
bson_read_int32(p1, "exec-cycles", &execute_cycles) &&
|
||||||
bson_read_int32(p1, "video-count", (u32*)&video_count)))
|
bson_read_int32(p1, "video-count", (u32*)&video_count) &&
|
||||||
|
bson_read_int32(p1, "sleep-cycles", ®[REG_SLEEP_CYCLES])))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
|
@ -357,6 +375,7 @@ unsigned main_write_savestate(u8* dst)
|
||||||
bson_write_int32(dst, "cpu-ticks", cpu_ticks);
|
bson_write_int32(dst, "cpu-ticks", cpu_ticks);
|
||||||
bson_write_int32(dst, "exec-cycles", execute_cycles);
|
bson_write_int32(dst, "exec-cycles", execute_cycles);
|
||||||
bson_write_int32(dst, "video-count", video_count);
|
bson_write_int32(dst, "video-count", video_count);
|
||||||
|
bson_write_int32(dst, "sleep-cycles", reg[REG_SLEEP_CYCLES]);
|
||||||
bson_finish_document(dst, wbptr);
|
bson_finish_document(dst, wbptr);
|
||||||
|
|
||||||
bson_start_document(dst, "timers", wbptr);
|
bson_start_document(dst, "timers", wbptr);
|
||||||
|
|
|
@ -104,7 +104,7 @@ symbol:
|
||||||
.equ REG_Z_FLAG, (21 * 4)
|
.equ REG_Z_FLAG, (21 * 4)
|
||||||
.equ REG_C_FLAG, (22 * 4)
|
.equ REG_C_FLAG, (22 * 4)
|
||||||
.equ REG_V_FLAG, (23 * 4)
|
.equ REG_V_FLAG, (23 * 4)
|
||||||
.equ REG_UNUSED_1, (24 * 4)
|
.equ REG_SLEEP_CYCLES, (24 * 4)
|
||||||
.equ OAM_UPDATED, (25 * 4)
|
.equ OAM_UPDATED, (25 * 4)
|
||||||
.equ REG_SAVE, (26 * 4)
|
.equ REG_SAVE, (26 * 4)
|
||||||
.equ REG_SAVE2, (27 * 4)
|
.equ REG_SAVE2, (27 * 4)
|
||||||
|
|
|
@ -92,7 +92,7 @@ bool bson_read_bytes(const u8 *srcp, const char *key, void* buffer, unsigned cnt
|
||||||
/* this is an upper limit, leave room for future (?) stuff */
|
/* this is an upper limit, leave room for future (?) stuff */
|
||||||
#define GBA_STATE_MEM_SIZE (416*1024)
|
#define GBA_STATE_MEM_SIZE (416*1024)
|
||||||
#define GBA_STATE_MAGIC 0x06BAC0DE
|
#define GBA_STATE_MAGIC 0x06BAC0DE
|
||||||
#define GBA_STATE_VERSION 0x00010001
|
#define GBA_STATE_VERSION 0x00010002
|
||||||
|
|
||||||
bool gba_load_state(const void *src);
|
bool gba_load_state(const void *src);
|
||||||
void gba_save_state(void *dst);
|
void gba_save_state(void *dst);
|
||||||
|
|
|
@ -95,7 +95,7 @@ _##symbol:
|
||||||
.equ REG_Z_FLAG, (21 * 4)
|
.equ REG_Z_FLAG, (21 * 4)
|
||||||
.equ REG_C_FLAG, (22 * 4)
|
.equ REG_C_FLAG, (22 * 4)
|
||||||
.equ REG_V_FLAG, (23 * 4)
|
.equ REG_V_FLAG, (23 * 4)
|
||||||
.equ REG_UNUSED_1, (24 * 4)
|
.equ REG_SLEEP_CYCLES, (24 * 4)
|
||||||
.equ OAM_UPDATED, (25 * 4)
|
.equ OAM_UPDATED, (25 * 4)
|
||||||
.equ REG_SAVE, (26 * 4)
|
.equ REG_SAVE, (26 * 4)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue