summaryrefslogtreecommitdiff
path: root/Source/Core/DSPCore/Src/DSPAnalyzer.cpp
blob: c182fa33c8e6ffcd3d5bc9690d9429f56053557c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Copyright (C) 2003-2009 Dolphin Project.

// 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 "gdsp_memory.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.
#define NUM_IDLE_SIGS 4
#define MAX_IDLE_SIG_SIZE 6

// 0xFFFF means ignore.
const u16 idle_skip_sigs[NUM_IDLE_SIGS][MAX_IDLE_SIG_SIZE + 1] =
{
	{ 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
};

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.
	int addr = start_addr;
	while (addr < end_addr)
	{
		UDSPInstruction inst = dsp_imem_read(addr);
		const DSPOPCTemplate *opcode = GetOpTemplate(inst);
		if (!opcode)
		{
			addr++;
			continue;
		}
		code_flags[addr] |= CODE_START_OF_INST;
		addr += opcode->size;

		// Look for loops.
		if ((inst.hex & 0xffe0) == 0x0060 || (inst.hex & 0xff00) == 0x1100) {
			// BLOOP, BLOOPI
			u16 loop_end = dsp_imem_read(addr + 1);
			code_flags[loop_end] |= CODE_LOOP_END;
		} else if ((inst.hex & 0xffe0) == 0x0040 || (inst.hex & 0xff00) == 0x1000) {
			// LOOP, LOOPI
			code_flags[addr + 1] |= CODE_LOOP_END;
		}
	}

	// 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)
			{
				NOTICE_LOG(DSPLLE, "Idle skip location found at %02x", addr);
				code_flags[addr] |= CODE_IDLE_SKIP;
				// TODO: actually use this flag somewhere.
			}
		}
	}
	NOTICE_LOG(DSPLLE, "Finished analysis.");
}

void Analyze()
{
	Reset();
	AnalyzeRange(0x0000, 0x1000);  // IRAM
	AnalyzeRange(0x8000, 0x9000);  // IROM
}

}  // namespace