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
					
				
					 9 changed files with 46 additions and 11 deletions
				
			
		|  | @ -57,7 +57,7 @@ _##symbol: | |||
| #define REG_Z_FLAG        (21 * 4) | ||||
| #define REG_C_FLAG        (22 * 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 REG_SAVE          (26 * 4) | ||||
| 
 | ||||
|  |  | |||
|  | @ -39,7 +39,7 @@ _##symbol: | |||
| #define REG_Z_FLAG        (21 * 4) | ||||
| #define REG_C_FLAG        (22 * 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 CPU_ALERT_HALT    (1 << 0) | ||||
|  |  | |||
							
								
								
									
										8
									
								
								cpu.c
									
										
									
									
									
								
							
							
						
						
									
										8
									
								
								cpu.c
									
										
									
									
									
								
							|  | @ -1489,7 +1489,12 @@ u32 check_and_raise_interrupts() | |||
|     reg[REG_PC] = 0x00000018; | ||||
| 
 | ||||
|     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 0; | ||||
|  | @ -3690,6 +3695,7 @@ void init_cpu(void) | |||
|     spsr[i] = 0x00000010; | ||||
| 
 | ||||
|   reg[CPU_HALT_STATE] = CPU_ACTIVE; | ||||
|   reg[REG_SLEEP_CYCLES] = 0; | ||||
| 
 | ||||
|   if (selected_boot_mode == boot_game) { | ||||
|     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_HALT            1 | ||||
| #define CPU_STOP            2 | ||||
| #define CPU_DMA             3  /* CPU is idling due to DMA transfer */ | ||||
| 
 | ||||
| typedef u8 cpu_alert_type; | ||||
| 
 | ||||
|  | @ -91,7 +92,7 @@ typedef enum | |||
|   REG_Z_FLAG        = 21, | ||||
|   REG_C_FLAG        = 22, | ||||
|   REG_V_FLAG        = 23, | ||||
|   REG_UNUSED_1      = 24, | ||||
|   REG_SLEEP_CYCLES  = 24, | ||||
|   OAM_UPDATED       = 25, | ||||
|   REG_SAVE          = 26, | ||||
|   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); | ||||
|       if(start_type == DMA_START_IMMEDIATELY) | ||||
|         return dma_transfer(dma_number, NULL); | ||||
|       if(start_type == DMA_START_IMMEDIATELY) { | ||||
|         // 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 | ||||
|  |  | |||
							
								
								
									
										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.
 | ||||
|     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++) | ||||
|     { | ||||
|        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) || | ||||
|       !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; | ||||
| 
 | ||||
|   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) && | ||||
|          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; | ||||
| 
 | ||||
|   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, "exec-cycles", execute_cycles); | ||||
|   bson_write_int32(dst, "video-count", video_count); | ||||
|   bson_write_int32(dst, "sleep-cycles", reg[REG_SLEEP_CYCLES]); | ||||
|   bson_finish_document(dst, wbptr); | ||||
| 
 | ||||
|   bson_start_document(dst, "timers", wbptr); | ||||
|  |  | |||
|  | @ -104,7 +104,7 @@ symbol: | |||
| .equ REG_Z_FLAG,          (21 * 4) | ||||
| .equ REG_C_FLAG,          (22 * 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 REG_SAVE,            (26 * 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 */ | ||||
| #define GBA_STATE_MEM_SIZE                    (416*1024) | ||||
| #define GBA_STATE_MAGIC                       0x06BAC0DE | ||||
| #define GBA_STATE_VERSION                     0x00010001 | ||||
| #define GBA_STATE_VERSION                     0x00010002 | ||||
| 
 | ||||
| bool gba_load_state(const void *src); | ||||
| void gba_save_state(void *dst); | ||||
|  |  | |||
|  | @ -95,7 +95,7 @@ _##symbol: | |||
| .equ REG_Z_FLAG,        (21 * 4) | ||||
| .equ REG_C_FLAG,        (22 * 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 REG_SAVE,          (26 * 4) | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue