| 
									
										
										
										
											2009-07-28 21:32:10 +00:00
										 |  |  | // Copyright (C) 2003 Dolphin Project.
 | 
					
						
							| 
									
										
										
										
											2009-07-06 02:10:26 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, version 2.0.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful,
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU General Public License 2.0 for more details.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // A copy of the GPL 2.0 should have been included with the program.
 | 
					
						
							|  |  |  | // If not, see http://www.gnu.org/licenses/
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Official SVN repository and contact information can be found at
 | 
					
						
							|  |  |  | // http://code.google.com/p/dolphin-emu/
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "DSPAnalyzer.h"
 | 
					
						
							|  |  |  | #include "DSPInterpreter.h"
 | 
					
						
							|  |  |  | #include "DSPTables.h"
 | 
					
						
							|  |  |  | #include "DSPMemoryMap.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace DSPAnalyzer { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Holds data about all instructions in RAM.
 | 
					
						
							|  |  |  | u8 code_flags[ISPACE]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Good candidates for idle skipping is mail wait loops. If we're time slicing
 | 
					
						
							|  |  |  | // between the main CPU and the DSP, if the DSP runs into one of these, it might
 | 
					
						
							|  |  |  | // as well give up its time slice immediately, after executing once.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Max signature length is 6. A 0 in a signature is ignored.
 | 
					
						
							| 
									
										
										
										
											2010-03-28 17:10:36 +00:00
										 |  |  | #define NUM_IDLE_SIGS 8
 | 
					
						
							| 
									
										
										
										
											2009-07-06 02:10:26 +00:00
										 |  |  | #define MAX_IDLE_SIG_SIZE 6
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 0xFFFF means ignore.
 | 
					
						
							|  |  |  | const u16 idle_skip_sigs[NUM_IDLE_SIGS][MAX_IDLE_SIG_SIZE + 1] = | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	// From AX:
 | 
					
						
							|  |  |  | 	{ 0x26fc,          // LRS $30, @DMBH
 | 
					
						
							|  |  |  | 	  0x02c0, 0x8000,  // ANDCF $30, #0x8000
 | 
					
						
							|  |  |  | 	  0x029d, 0xFFFF,  // JLZ 0x027a
 | 
					
						
							|  |  |  | 	  0, 0 },     // RET
 | 
					
						
							|  |  |  | 	{ 0x27fc,          // LRS $31, @DMBH
 | 
					
						
							|  |  |  | 	  0x03c0, 0x8000,  // ANDCF $31, #0x8000
 | 
					
						
							|  |  |  | 	  0x029d, 0xFFFF,  // JLZ 0x027a
 | 
					
						
							|  |  |  | 	  0, 0 },     // RET
 | 
					
						
							|  |  |  | 	{ 0x26fe,          // LRS $30, @CMBH
 | 
					
						
							|  |  |  | 	  0x02c0, 0x8000,  // ANDCF $30, #0x8000
 | 
					
						
							|  |  |  | 	  0x029c, 0xFFFF,  // JLNZ 0x0280
 | 
					
						
							|  |  |  | 	  0, 0 },     // RET
 | 
					
						
							|  |  |  | 	{ 0x27fe,          // LRS $31, @CMBH
 | 
					
						
							|  |  |  | 	  0x03c0, 0x8000,  // ANDCF $31, #0x8000
 | 
					
						
							|  |  |  | 	  0x029c, 0xFFFF,  // JLNZ 0x0280
 | 
					
						
							|  |  |  | 	  0, 0 },     // RET
 | 
					
						
							| 
									
										
										
										
											2010-03-22 13:46:00 +00:00
										 |  |  | 	{ 0x26fc,			// lrs         $AC0.M, @DMBH
 | 
					
						
							|  |  |  | 	  0x02a0, 0x8000,	// andf        $AC0.M, #0x8000
 | 
					
						
							|  |  |  | 	  0x029c, 0xFFFF,	// jlnz        0x????
 | 
					
						
							|  |  |  | 	  0, 0 },  | 
					
						
							|  |  |  | 	{ 0x27fc,			// lrs         $AC1.M, @DMBH
 | 
					
						
							|  |  |  | 	  0x03a0, 0x8000,	// andf        $AC1.M, #0x8000
 | 
					
						
							|  |  |  | 	  0x029c, 0xFFFF,	// jlnz        0x????
 | 
					
						
							|  |  |  | 	  0, 0 },  | 
					
						
							| 
									
										
										
										
											2009-07-06 02:10:26 +00:00
										 |  |  | 	// From Zelda:
 | 
					
						
							|  |  |  | 	{ 0x00de, 0xFFFE,  // LR $AC0.M, @CMBH
 | 
					
						
							|  |  |  | 	  0x02c0, 0x8000,  // ANDCF $AC0.M, #0x8000 
 | 
					
						
							|  |  |  | 	  0x029c, 0xFFFF,  // JLNZ 0x05cf
 | 
					
						
							| 
									
										
										
										
											2010-03-28 17:10:36 +00:00
										 |  |  | 	  0 }, | 
					
						
							|  |  |  | 	// From Zelda - experimental
 | 
					
						
							|  |  |  | 	{ 0x00da, 0x0352, // lr          $AX0.H, @0x0352
 | 
					
						
							|  |  |  | 	  0x8600,         // tstaxh      $AX0.H
 | 
					
						
							|  |  |  | 	  0x0295, 0xFFFF, // jz          0x???? 
 | 
					
						
							|  |  |  | 	  0, 0 } | 
					
						
							| 
									
										
										
										
											2009-07-06 02:10:26 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Reset() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	memset(code_flags, 0, sizeof(code_flags)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AnalyzeRange(int start_addr, int end_addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	// First we run an extremely simplified version of a disassembler to find
 | 
					
						
							|  |  |  | 	// where all instructions start.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// This may not be 100% accurate in case of jump tables!
 | 
					
						
							|  |  |  | 	// It could get desynced, which would be bad. We'll see if that's an issue.
 | 
					
						
							| 
									
										
										
										
											2010-12-21 14:48:05 +00:00
										 |  |  | 	u16 last_arithmetic = 0; | 
					
						
							| 
									
										
										
										
											2009-09-08 21:16:05 +00:00
										 |  |  | 	for (int addr = start_addr; addr < end_addr;) | 
					
						
							| 
									
										
										
										
											2009-07-06 02:10:26 +00:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		UDSPInstruction inst = dsp_imem_read(addr); | 
					
						
							|  |  |  | 		const DSPOPCTemplate *opcode = GetOpTemplate(inst); | 
					
						
							|  |  |  | 		if (!opcode) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			addr++; | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		code_flags[addr] |= CODE_START_OF_INST; | 
					
						
							| 
									
										
										
										
											2010-05-29 18:22:50 +00:00
										 |  |  | 		// Look for loops.
 | 
					
						
							| 
									
										
										
										
											2010-12-21 14:48:05 +00:00
										 |  |  | 		if ((inst & 0xffe0) == 0x0060 || (inst & 0xff00) == 0x1100) | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2009-07-06 02:10:26 +00:00
										 |  |  | 			// BLOOP, BLOOPI
 | 
					
						
							|  |  |  | 			u16 loop_end = dsp_imem_read(addr + 1); | 
					
						
							| 
									
										
										
										
											2010-03-22 16:32:48 +00:00
										 |  |  | 			code_flags[addr] |= CODE_LOOP_START; | 
					
						
							| 
									
										
										
										
											2009-07-06 02:10:26 +00:00
										 |  |  | 			code_flags[loop_end] |= CODE_LOOP_END; | 
					
						
							| 
									
										
										
										
											2010-12-21 14:48:05 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		else if ((inst & 0xffe0) == 0x0040 || (inst & 0xff00) == 0x1000) | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2009-07-06 02:10:26 +00:00
										 |  |  | 			// LOOP, LOOPI
 | 
					
						
							| 
									
										
										
										
											2010-03-22 16:32:48 +00:00
										 |  |  | 			code_flags[addr] |= CODE_LOOP_START; | 
					
						
							| 
									
										
										
										
											2009-07-06 02:10:26 +00:00
										 |  |  | 			code_flags[addr + 1] |= CODE_LOOP_END; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2010-12-21 14:48:05 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Mark the last arithmetic/multiplier instruction before a branch.
 | 
					
						
							|  |  |  | 		// We must update the SR reg at these instructions
 | 
					
						
							|  |  |  | 		if (opcode->updates_sr) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			last_arithmetic = addr; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (opcode->branch && !opcode->uncond_branch) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			code_flags[last_arithmetic] |= CODE_UPDATE_SR; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-22 16:32:48 +00:00
										 |  |  | 		addr += opcode->size; | 
					
						
							| 
									
										
										
										
											2009-07-06 02:10:26 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Next, we'll scan for potential idle skips.
 | 
					
						
							|  |  |  | 	for (int s = 0; s < NUM_IDLE_SIGS; s++) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		for (int addr = start_addr; addr < end_addr; addr++) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			bool found = false; | 
					
						
							|  |  |  | 			for (int i = 0; i < MAX_IDLE_SIG_SIZE + 1; i++) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				if (idle_skip_sigs[s][i] == 0) | 
					
						
							|  |  |  | 					found = true; | 
					
						
							|  |  |  | 				if (idle_skip_sigs[s][i] == 0xFFFF) | 
					
						
							|  |  |  | 					continue; | 
					
						
							|  |  |  | 				if (idle_skip_sigs[s][i] != dsp_imem_read(addr + i)) | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (found) | 
					
						
							|  |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2010-12-21 14:48:05 +00:00
										 |  |  | 				INFO_LOG(DSPLLE, "Idle skip location found at %02x (sigNum:%d)", addr, s+1); | 
					
						
							| 
									
										
										
										
											2009-07-06 02:10:26 +00:00
										 |  |  | 				code_flags[addr] |= CODE_IDLE_SKIP; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-12-21 14:48:05 +00:00
										 |  |  | 	INFO_LOG(DSPLLE, "Finished analysis."); | 
					
						
							| 
									
										
										
										
											2009-07-06 02:10:26 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Analyze() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	Reset(); | 
					
						
							|  |  |  | 	AnalyzeRange(0x0000, 0x1000);  // IRAM
 | 
					
						
							|  |  |  | 	AnalyzeRange(0x8000, 0x9000);  // IROM
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace
 |