CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/MIPS/MIPSAnalyst.cpp
Views: 1401
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include "ppsspp_config.h"
19
#include <algorithm>
20
#include <map>
21
#include <set>
22
#include <unordered_map>
23
#include <unordered_set>
24
#include <mutex>
25
26
#include "ext/cityhash/city.h"
27
#include "ext/xxhash.h"
28
29
#include "Common/File/FileUtil.h"
30
#include "Common/Log.h"
31
#include "Common/StringUtils.h"
32
#include "Common/TimeUtil.h"
33
#include "Core/Config.h"
34
#include "Core/MemMap.h"
35
#include "Core/System.h"
36
#include "Core/MIPS/MIPS.h"
37
#include "Core/MIPS/MIPSVFPUUtils.h"
38
#include "Core/MIPS/MIPSTables.h"
39
#include "Core/MIPS/MIPSAnalyst.h"
40
#include "Core/MIPS/MIPSCodeUtils.h"
41
#include "Core/Debugger/SymbolMap.h"
42
#include "Core/Debugger/DebugInterface.h"
43
#include "Core/HLE/ReplaceTables.h"
44
45
using namespace MIPSCodeUtils;
46
47
// Not in a namespace because MSVC's debugger doesn't like it
48
typedef std::vector<MIPSAnalyst::AnalyzedFunction> FunctionsVector;
49
static FunctionsVector functions;
50
std::recursive_mutex functions_lock;
51
52
// One function can appear in multiple copies in memory, and they will all have
53
// the same hash and should all be replaced if possible.
54
static std::unordered_multimap<u64, MIPSAnalyst::AnalyzedFunction *> hashToFunction;
55
56
struct HashMapFunc {
57
char name[64];
58
u64 hash;
59
u32 size; //number of bytes
60
bool hardcoded; // should not be saved
61
62
bool operator < (const HashMapFunc &other) const {
63
return hash < other.hash || (hash == other.hash && size < other.size);
64
}
65
66
bool operator == (const HashMapFunc &other) const {
67
return hash == other.hash && size == other.size;
68
}
69
};
70
71
namespace std {
72
template <>
73
struct hash<HashMapFunc> {
74
size_t operator()(const HashMapFunc &f) const {
75
return std::hash<u64>()(f.hash) ^ f.size;
76
}
77
};
78
}
79
80
static std::unordered_set<HashMapFunc> hashMap;
81
82
static Path hashmapFileName;
83
84
#define MIPSTABLE_IMM_MASK 0xFC000000
85
86
// Similar to HashMapFunc but has a char pointer for the name for efficiency.
87
struct HardHashTableEntry {
88
uint64_t hash;
89
int funcSize;
90
const char *funcName;
91
92
bool operator <(const HardHashTableEntry &e) const {
93
if (hash < e.hash) return true;
94
if (hash > e.hash) return false;
95
return funcSize < e.funcSize;
96
}
97
};
98
99
// Some hardcoded hashes. Some have a comment specifying at least one game they are found in.
100
static const HardHashTableEntry hardcodedHashes[] = {
101
{ 0x006b570008068310, 184, "strtok_r", },
102
{ 0x019ba2099fb88f3c, 48, "vector_normalize_t", },
103
{ 0x0266f96d740c7e03, 912, "memcpy", }, // Final Fantasy 4 (US)
104
{ 0x02bd2859045d2383, 240, "bcmp", },
105
{ 0x030507c9a1f0fc85, 92, "sceVfpuMatrix4RotX", },
106
{ 0x0483fceefa4557ff, 1360, "__udivdi3", },
107
{ 0x0558ad5c5be00ca1, 76, "vtfm_t", },
108
{ 0x05aceb23092fd6a1, 36, "zettai_hero_update_minimap_tex", }, // Zettai Hero Project (US)
109
{ 0x05aedd0c04b451a1, 356, "sqrt", },
110
{ 0x0654fc8adbe16ef7, 28, "vmul_q", },
111
{ 0x06628f6052cda3c1, 1776, "toheart2_download_frame", }, // To Heart 2 Portable
112
{ 0x06b243c926fa6ab5, 24, "vf2in_q", },
113
{ 0x06e2826e02056114, 56, "wcslen", },
114
{ 0x073cf0b61d3b875a, 416, "hexyzforce_monoclome_thread", }, // Hexyz Force (US)
115
{ 0x075fa9b234b41e9b, 32, "fmodf", },
116
{ 0x0a051019bdd786c3, 184, "strcasecmp", },
117
{ 0x0a1bed70958935d2, 644, "youkosohitsujimura_download_frame", }, // Youkoso Hitsuji-Mura Portable
118
{ 0x0a46dc426054bb9d, 24, "vector_add_t", },
119
{ 0x0c0173ed70f84f66, 48, "vnormalize_t", },
120
{ 0x0c65188f5bfb3915, 24, "vsgn_q", },
121
{ 0x0d898513a722ea3c, 40, "copysignf", },
122
{ 0x0e99b037b852c8ea, 68, "isnan", },
123
// Unsafe due to immediates.
124
//{ 0x0eb5f2e95f59276a, 40, "dl_write_lightmode", },
125
{ 0x0f1e7533a546f6a1, 228, "dl_write_bone_matrix_4", },
126
{ 0x0f2a1106ad84fb74, 52, "strcmp", },
127
{ 0x0ffa5db8396d4274, 64, "memcpy_jak", }, // CRUSH
128
{ 0x1252e902d0b49bfb, 44, "vector_sub_q_2", },
129
{ 0x12df3d33a58d0298, 52, "sceVfpuMatrix3Unit", },
130
{ 0x12feef7b017d3431, 700, "memmove", },
131
{ 0x1322c7e3fe6dff4d, 784, "_free_r", },
132
{ 0x1376c115d5f1d90c, 36, "strlen", },
133
{ 0x1448134dd3acd1f9, 240, "memchr", },
134
{ 0x14800e59c04968d7, 100, "wcsstr", },
135
{ 0x14b56e858a27a8a4, 24, "vi2f_q", },
136
{ 0x15c4662d5d3c728e, 308, "acosf", },
137
{ 0x1616ee7052542059, 48, "vtfm_t", },
138
{ 0x16965ca11a4e7dac, 104, "vmmul_q_transp", },
139
{ 0x16afe830a5dd2de2, 40, "vdiv_q", },
140
{ 0x184e834a63a79016, 32, "isnanf", },
141
{ 0x1874ee898c7b9f16, 512, "kokoroconnect_download_frame", }, // Kokoro Connect Yochi Random
142
{ 0x189212bda9c94df1, 736, "atanf", },
143
{ 0x199821ce500ef9d2, 24, "vocp_t", },
144
{ 0x1a3c8e9d637ed421, 104, "__adddf3", },
145
{ 0x1a7564fa3e25c992, 844, "memcpy", }, // Valkyria Chronicles 3
146
{ 0x1aad94c0723edfc0, 124, "sceVfpuMatrix3Mul", },
147
{ 0x1ab33b12b3cb8cb0, 28, "vqmul_q", },
148
{ 0x1ac05627df1f87f4, 112, "memcpy16", }, // Valkyria Chronicles 3
149
{ 0x1bdf3600844373fd, 112, "strstr", },
150
{ 0x1c967be07917ddc9, 92, "strcat", },
151
{ 0x1d03fa48334ca966, 556, "_strtol_r", },
152
{ 0x1d1311966d2243e9, 428, "suikoden1_and_2_download_frame_1", }, // Gensou Suikoden 1&2
153
{ 0x1d7de04b4e87d00b, 680, "kankabanchoutbr_download_frame", }, // Kenka Banchou Bros: Tokyo Battle Royale
154
{ 0x1daf6eaf0442391d, 1024, "utawarerumono_download_frame", }, // Utawarerumono portable
155
{ 0x1e1525e3bc2f6703, 676, "rint", },
156
{ 0x1ec055f28bb9f4d1, 88, "_sceGuUpdateStallAddr", },
157
{ 0x1ef9cfe6afd3c035, 180, "memset", }, // Kingdom Hearts (US)
158
{ 0x1f53eac122f96b37, 224, "cosf", },
159
{ 0x2097a8b75c8fe651, 436, "atan2", },
160
{ 0x21411b3c860822c0, 36, "matrix_scale_q_t", },
161
{ 0x2161ea81a06bcacd, 1032, "soltrigger_render_ucschar", }, // Sol Trigger
162
{ 0x24d82a8675800808, 220, "ceilf", },
163
{ 0x26cc90cb25af9d27, 476, "log10", },
164
{ 0x275c79791a2bab83, 116, "rezel_cross_download_frame", }, // Rezel Cross
165
{ 0x2774614d57d4baa2, 28, "vsub_q", },
166
{ 0x279c6bf9cf99cc85, 436, "strncpy", },
167
{ 0x2876ed93c5fd1211, 328, "dl_write_matrix_4", },
168
{ 0x2965b1ad3ca15cc1, 44, "vtfm_t", },
169
{ 0x299a370587df078f, 116, "strange_copy_routine", },
170
{ 0x2aa9634a9951c7df, 212, "sdgundamggenerationportable_download_frame", }, // SD Gundam G Generation Portable
171
{ 0x2abca53599f09ea7, 608, "dl_write_matrix_3", },
172
{ 0x2adb92e8855c454e, 48, "vtfm_q", },
173
{ 0x2adc229bef7bbc75, 40, "isnan", },
174
{ 0x2bcf5268dd26345a, 340, "acos", },
175
{ 0x2c4cb2028a1735bf, 600, "floor", },
176
{ 0x2c61a9a06a345b43, 1084, "otomenoheihou_download_frame", }, // Sangoku Koi Senki Otome no Heihou
177
{ 0x2ca5958bb816c72e, 44, "sceVfpuVector3FromIVector", },
178
{ 0x2e7022d9767c9018, 2100, "atan", },
179
{ 0x2f10d3faec84b5bb, 276, "sinf", },
180
{ 0x2f639673670caa0e, 772, "sceGupSetMatrix", },
181
{ 0x2f718936b371fc44, 40, "sceVfpuScalarCos", },
182
{ 0x3024e961d1811dea, 396, "fmod", },
183
{ 0x3050bfd0e729dfbf, 220, "atvoffroadfuryblazintrails_download_frame", }, // ATV Offroad Fury Blazin' Trails (US)
184
{ 0x30c9c4f420573eb6, 540, "expf", },
185
{ 0x311779b4db21dbf3, 124, "motorstorm_pixel_read" }, // Motorstorm Arctic Edge (US)
186
{ 0x317afeb882ff324a, 212, "memcpy", }, // Mimana (US)
187
{ 0x31ea2e192f5095a1, 52, "vector_add_t", },
188
{ 0x31f523ef18898e0e, 420, "logf", },
189
{ 0x32215b1d2196377f, 844, "godseaterburst_blit_texture", }, // Gods Eater Burst (US)
190
{ 0x32806967fe81568b, 40, "vector_sub_t_2", },
191
{ 0x32ceb9a7f72b9385, 440, "_strtoul_r", },
192
{ 0x32e6bc7c151491ed, 68, "memchr", },
193
{ 0x335df69db1073a8d, 96, "wcscpy", },
194
{ 0x33dc6b144cb302c1, 304, "memmove", }, // Kingdom Hearts (US)
195
{ 0x35d3527ff8c22ff2, 56, "matrix_scale_q", },
196
{ 0x368f6cf979709a31, 744, "memmove", }, // Jui Dr. Touma Jotarou
197
{ 0x373ce518eee5a2d2, 20, "matrix300_store_q", },
198
{ 0x3840f5766fada4b1, 592, "dissidia_recordframe_avi", }, // Dissidia (US), Dissidia 012 (US)
199
{ 0x388043e96b0e11fd, 144, "sceGupMaterial", },
200
{ 0x38f19bc3be215acc, 388, "log10f", },
201
{ 0x3913b81ddcbe1efe, 880, "katamari_render_check", }, // Me and My Katamari (US)
202
{ 0x393047f06eceaba1, 96, "strcspn", },
203
{ 0x39a651942a0b3861, 204, "tan", },
204
{ 0x3a3bc2b20a55bf02, 68, "memchr", },
205
{ 0x3ab08b5659de1746, 40, "sceVfpuScalarSin", },
206
{ 0x3c421a9265f37ebc, 700, "memmove", }, // Final Fantasy 4 (US)
207
{ 0x3cbc2d50a3db59e9, 100, "strncmp", },
208
{ 0x3ce1806699a91d9d, 148, "sceGupLight", },
209
{ 0x3d5e914011c181d4, 444, "scalbnf", },
210
{ 0x3ea41eafb53fc99a, 388, "logf", },
211
{ 0x3fe38bff09ac3da0, 436, "_strtoul_r", },
212
{ 0x40a25c7e1fd44fe2, 24, "fabsf", },
213
// Unsafe due to immediates.
214
//{ 0x410d48d9b6580b4a, 36, "dl_write_ztest", },
215
{ 0x42dc17c8018f30f2, 44, "sceVfpuScalarTan", },
216
{ 0x436b07caa2aab931, 352, "acos", },
217
{ 0x444472537eedf966, 32, "sceVfpuMatrix4Zero", },
218
{ 0x449ff96982626338, 28, "vmidt_q", },
219
{ 0x44f65b1a72c45703, 36, "strlen", },
220
{ 0x45528de3948615dc, 64, "memcpy", },
221
{ 0x456a0d78ac318d15, 164, "gta_dl_write_matrix", },
222
{ 0x497248c9d12f44fd, 68, "strcpy", },
223
{ 0x4a70207212a4c497, 24, "strlen", },
224
{ 0x4b16a5c602c74c6c, 24, "vsub_t", },
225
{ 0x4bb677dace6ca526, 184, "memset", }, // Final FantasyTactics (JPN)
226
{ 0x4c4bdedcc13ac77c, 624, "dl_write_matrix_5", },
227
{ 0x4c91c556d1aa896b, 104, "dl_write_material_3", },
228
{ 0x4cf38c368078181e, 616, "dl_write_matrix", },
229
{ 0x4d3e7085e01d30e4, 324, "memcpy", }, // PoPoLoCrois (JPN)
230
{ 0x4d72b294501cddfb, 80, "copysign", },
231
{ 0x4ddd83b7f4ed8d4e, 844, "memcpy", },
232
{ 0x4e266783291b0220, 28, "vsub_t", },
233
{ 0x4e5950928c0bb082, 44, "vmmul_q_transp4", },
234
{ 0x4f34fc596ecf5b25, 40, "vdiv_t", },
235
{ 0x500a949afb39133f, 24, "vf2iu_q", },
236
{ 0x50d8f01ea8fa713d, 48, "send_commandi", },
237
{ 0x50fa6db2fb14814a, 544, "rint", },
238
{ 0x513ce13cd7ce97ea, 332, "scalbnf", },
239
{ 0x514161da54d37416, 1416, "__umoddi3", },
240
{ 0x51c52d7dd4d2191c, 360, "cos", },
241
{ 0x5287d4b8abd5806b, 768, "_strtoll_r", },
242
{ 0x52d5141545a75eda, 60, "sceGuClutMode", },
243
{ 0x530cbe1ce9b45d58, 108, "sceGuLightAtt", },
244
{ 0x53c9aa23504a630f, 96, "vmmul_q_5", },
245
{ 0x54015ccbcbc75374, 24, "strlen", }, // Metal Gear Solid: Peace Walker demo
246
{ 0x5550d87a851c218c, 168, "dl_write_viewport", },
247
{ 0x55c1294280bfade0, 88, "sceGuBlendFunc", },
248
{ 0x5642a63f3802a792, 456, "orenoimouto_download_frame", }, // Ore no Imouto ga Konnani Kawaii Wake ga Nai
249
{ 0x56c9929e8c8c5768, 24, "fabsf", },
250
{ 0x572b2d9e57e6e363, 788, "memcpy_thingy", },
251
{ 0x580200b840b47c58, 1856, "_realloc_r", },
252
{ 0x5961f681bbd69035, 28, "vfad_q", },
253
{ 0x598b91c64cf7e036, 2388, "qsort", },
254
{ 0x59a0cb08f5ecf8b6, 28, "copysignf", },
255
{ 0x5ae4ec2a5e133de3, 28, "vector_cross_t", },
256
{ 0x5b005f8375d7c364, 236, "floorf", },
257
{ 0x5b103d973fd1dd94, 92, "sceVfpuMatrix4RotY", },
258
{ 0x5b9d7e9d4c905694, 196, "_calloc_r", },
259
{ 0x5bf7a77b028e9f66, 324, "sqrtf", },
260
{ 0x5c0b3edc0e48852c, 148, "memmove", }, // Dissidia 1 (US)
261
{ 0x5e898df42c4af6b8, 76, "wcsncmp", },
262
{ 0x5f473780835e3458, 52, "vclamp_q", },
263
{ 0x5fc58ed2c4d48b79, 40, "sceVfpuMatrix4Transform", },
264
{ 0x6145029ef86f0365, 76, "__extendsfdf2", },
265
{ 0x62815f41fa86a131, 656, "scalbn", },
266
{ 0x6301fa5149bd973a, 120, "wcscat", },
267
{ 0x658b07240a690dbd, 36, "strlen", },
268
{ 0x66122f0ab50b2ef9, 296, "dl_write_dither_matrix_5", },
269
{ 0x66f7f1beccbc104a, 256, "memcpy_swizzled", }, // God Eater 2
270
{ 0x679e647e34ecf7f1, 132, "roundf", },
271
{ 0x67afe74d9ec72f52, 4380, "_strtod_r", },
272
{ 0x68b22c2aa4b8b915, 400, "sqrt", },
273
{ 0x6962da85a6dad937, 60, "strrchr", },
274
{ 0x69a3c4f774859404, 64, "vmmul_q_transp2", },
275
{ 0x6ab54910104ef000, 628, "sd_gundam_g_generation_download_frame", }, // SD Gundam G Generation World
276
{ 0x6ac2cd44e042592b, 252, "atvoffroadfurypro_download_frame", }, // ATV Offroad Fury Pro (US)
277
{ 0x6b022e20ee3fa733, 68, "__negdf2", },
278
{ 0x6b2a6347c0dfcb57, 152, "strcpy", },
279
{ 0x6b4148322c569cb3, 240, "wmemchr", },
280
{ 0x6c4cb6d25851553a, 44, "vtfm_t", },
281
{ 0x6c7b2462b9ec7bc7, 56, "vmmul_q", },
282
{ 0x6ca9cc8fa485d096, 304, "__ieee754_sqrtf", },
283
{ 0x6ccffc753d2c148e, 96, "strlwr", },
284
{ 0x6e40ec681fb5c571, 40, "matrix_copy_q", },
285
{ 0x6e9884c842a51142, 236, "strncasecmp", },
286
{ 0x6f101c5c4311c144, 276, "floorf", },
287
{ 0x6f1731f84bbf76c3, 116, "strcmp", },
288
{ 0x6f4e1a1a84df1da0, 68, "sceGupTexMode", },
289
{ 0x6f7c9109b5b8fa47, 688, "danganronpa1_2_download_frame", }, // Danganronpa 1
290
{ 0x70649c7211f6a8da, 16, "fabsf", },
291
{ 0x70a6152b265228e8, 296, "unendingbloodycall_download_frame", }, // unENDing Bloody Call
292
{ 0x7245b74db370ae72, 64, "sceVfpuMatrix4Mul", },
293
{ 0x7259d52b21814a5a, 40, "sceVfpuMatrix4TransformXYZ", },
294
{ 0x730f59cc6c0f5732, 452, "godseaterburst_depthmask_5551", }, // Gods Eater Burst (US)
295
{ 0x7354fd206796d817, 864, "flowers_download_frame", }, // Flowers
296
{ 0x736b34ebc702d873, 104, "vmmul_q_transp", },
297
{ 0x73a614c08f777d52, 792, "danganronpa2_2_download_frame", }, // Danganronpa 2
298
{ 0x7499a2ce8b60d801, 12, "abs", },
299
{ 0x74c77fb521740cd2, 284, "toheart2_download_frame_2", }, // To Heart 2 Portable
300
{ 0x74ebbe7d341463f3, 72, "sceGuColorFunc", },
301
{ 0x755a41f9183bb89a, 60, "vmmul_q", },
302
{ 0x757d7ab0afbc03f5, 948, "kirameki_school_life_download_frame", }, // Toradora! Portable
303
{ 0x759834c69bb12c12, 68, "strcpy", },
304
{ 0x75c5a88d62c9c99f, 276, "sinf", },
305
{ 0x76c661fecbb39990, 364, "sin", },
306
{ 0x770c9c07bf58fd14, 16, "fabsf", },
307
{ 0x774e479eb9634525, 464, "_strtol_r", },
308
{ 0x77aeb1c23f9aa2ad, 56, "strchr", },
309
{ 0x78e8c65b5a458f33, 148, "memcmp", },
310
{ 0x794d1b073c183c77, 24, "fabsf", },
311
{ 0x7978a886cf70b1c9, 56, "wcschr", },
312
{ 0x79faa339fff5a80c, 28, "finitef", },
313
{ 0x7c50728008c288e3, 36, "vector_transform_q_4x4", },
314
{ 0x7e33d4eaf573f937, 208, "memset", }, // Toukiden (JPN)
315
{ 0x7f1fc0dce6be120a, 404, "fmod", },
316
{ 0x8126a59ffa504614, 540, "brandish_download_frame", }, // Brandish, Zero no Kiseki, and Ao no Kiseki
317
{ 0x828b98925af9ff8f, 40, "vector_distance_t", },
318
{ 0x83ac39971df4b966, 336, "sqrtf", },
319
{ 0x84c6cd47834f4c79, 1284, "powf", },
320
{ 0x8734dc1d155ea493, 24, "vf2iz_q", },
321
{ 0x87fe3f7e621ddebb, 212, "memcpy", },
322
{ 0x891ca854e1c664e9, 2392, "qsort", },
323
{ 0x8965d4b004adad28, 420, "log10f", },
324
{ 0x89e1858ba11b84e4, 52, "memset", },
325
{ 0x8a00e7207e7dbc81, 232, "_exit", },
326
{ 0x8a1f9daadecbaf7f, 104, "vmmul_q_transp", },
327
{ 0x8a610f34078ce360, 32, "vector_copy_q_t", },
328
{ 0x8c3fd997a544d0b1, 268, "memcpy", }, // Valkyrie Profile (US)
329
{ 0x8da0164e69e9b531, 1040, "grisaianokajitsu_download_frame", }, // Grisaia no Kajitsu La Fruit de la Grisaia
330
{ 0x8dd0546db930ef25, 992, "memmove", }, // PoPoLoCrois (JPN)
331
{ 0x8df2928848857e97, 164, "strcat", },
332
{ 0x8e48cabd529ca6b5, 52, "sceVfpuVector3Mul", },
333
{ 0x8e97dcb03fbaba5c, 104, "vmmul_q_transp", },
334
{ 0x8ecf804bbe7922e5, 572, "worms_copy_normalize_alpha" }, // Worms Battle Islands (US)
335
{ 0x8ee81b03d2eef1e7, 28, "vmul_t", },
336
{ 0x8f09fb8693c3c49d, 992, "kirameki_school_life_download_frame", }, // Hentai Ouji To Warawanai Neko
337
{ 0x8f19c41e8b987e18, 100, "matrix_mogrify", },
338
{ 0x8ff11e9bed387401, 700, "memmove", }, // God Eater 2
339
{ 0x910140c1a07aa59e, 256, "sceVfpuMatrix4Rot", },
340
{ 0x91606bd72ae90481, 44, "wmemcpy", },
341
{ 0x92c7d2de74068c9c, 32, "vcross_t", },
342
{ 0x93d8a275ba288b26, 32, "vdot_t", },
343
{ 0x94c7083b64a946b4, 2028, "powf", },
344
{ 0x94eb1e7dccca76a4, 680, "shinigamitoshoujo_download_frame", }, // Shinigami to Shoujo (JP)
345
{ 0x95a52ce1bc460108, 2036, "_malloc_r", },
346
{ 0x95bd33ac373c019a, 24, "fabsf", },
347
{ 0x9705934b0950d68d, 280, "dl_write_framebuffer_ptr", },
348
{ 0x9734cf721bc0f3a1, 732, "atanf", },
349
{ 0x99b85c5fce389911, 408, "mytranwars_upload_frame", }, // Mytran Wars
350
{ 0x99c9288185c352ea, 592, "orenoimouto_download_frame_2", }, // Ore no Imouto ga Konnani Kawaii Wake ga Nai
351
{ 0x9a06b9d5c16c4c20, 76, "dl_write_clut_ptrload", },
352
{ 0x9b88b739267d189e, 88, "strrchr", },
353
{ 0x9ce53975bb88c0e7, 96, "strncpy", },
354
{ 0x9d4f5f56b52f07f2, 808, "memmove", }, // Jeanne d'Arc (US)
355
{ 0x9e2941c4a5c5e847, 792, "memcpy", }, // LittleBigPlanet (US)
356
{ 0x9e6ce11f9d49f954, 292, "memcpy", }, // Jeanne d'Arc (US)
357
{ 0x9f269daa6f0da803, 128, "sceGupScissor", },
358
{ 0x9f7919eeb43982b0, 208, "__fixdfsi", },
359
{ 0xa1c9b0a2c71235bf, 1752, "marvelalliance1_copy" }, // Marvel Ultimate Alliance 1 (EU)
360
{ 0x9b76c7f2a41aa805, 1752, "marvelalliance1_copy" }, // Marvel Ultimate alliance 1 (US)
361
{ 0xa1ca0640f11182e7, 72, "strcspn", },
362
{ 0xa243486be51ce224, 272, "cosf", },
363
{ 0xa2bcef60a550a3ef, 92, "sceVfpuMatrix4RotZ", },
364
{ 0xa373f55c65cd757a, 312, "memcpy_swizzled" }, // God Eater Burst Demo
365
{ 0xa41989db0f9bf97e, 1304, "pow", },
366
{ 0xa44f6227fdbc12b1, 132, "memcmp", }, // Popolocrois (US)
367
{ 0xa46cc6ea720d5775, 44, "sceGupFrontFace", },
368
{ 0xa54967288afe8f26, 600, "ceil", },
369
{ 0xa5ddbbc688e89a4d, 56, "isinf", },
370
{ 0xa615f6bd33195dae, 220, "atvoffroadfuryprodemo_download_frame", }, // ATV Offroad Fury Pro (US) demo
371
{ 0xa662359e30b829e4, 148, "memcmp", },
372
{ 0xa6a03f0487a911b0, 392, "danganronpa1_1_download_frame", }, // Danganronpa 1
373
{ 0xa8390e65fa087c62, 140, "vtfm_t_q", },
374
{ 0xa85e48ee10b2dc50, 432, "omertachinmokunookitethelegacy_download_frame", }, // Omerta Chinmoku No Okite The Legacy
375
{ 0xa85fe8abb88b1c6f, 52, "vector_sub_t", },
376
{ 0xa9194e55cc586557, 268, "memcpy", },
377
{ 0xa91b3d60bd75105b, 28, "vadd_t", },
378
{ 0xab97ec58c58a7c75, 52, "sceVfpuVector3Div", },
379
{ 0xac84fa7571895c9a, 68, "memcpy", }, // Marvel Ultimate Alliance 2
380
{ 0xacc2c11c3ea28320, 268, "ceilf", },
381
{ 0xad67add5122b8c64, 52, "sceVfpuMatrix4Transfer", },
382
{ 0xada952a1adcea4f5, 60, "vmmul_q_transp5", },
383
{ 0xadfbf8fb8c933193, 56, "fabs", },
384
{ 0xae39bac51fd6e76b, 628, "gakuenheaven_download_frame", }, // Gakuen Heaven: Boy's Love Scramble!
385
{ 0xae50226363135bdd, 24, "vector_sub_t", },
386
{ 0xae6cd7dfac82c244, 48, "sceVfpuScalarPow", },
387
{ 0xaf85d47f95ad2921, 1936, "pow", },
388
{ 0xafb2c7e56c04c8e9, 48, "vtfm_q", },
389
{ 0xafc9968e7d246a5e, 1588, "atan", },
390
{ 0xafcb7dfbc4d72588, 44, "vector_transform_3x4", },
391
{ 0xb07f9d82d79deea9, 536, "brandish_download_frame", }, // Brandish, and Sora no kiseki 3rd
392
{ 0xb09c9bc1343a774c, 456, "danganronpa2_1_download_frame", }, // Danganronpa 2
393
{ 0xb0db731f27d3aa1b, 40, "sceVfpuScalarMax", },
394
{ 0xb0ef265e87899f0a, 32, "vector_divide_t_s", },
395
{ 0xb183a37baa12607b, 32, "vscl_t", },
396
{ 0xb1a3e60a89af9857, 20, "fabs", },
397
{ 0xb25670ff47b4843d, 232, "starocean_clear_framebuf" },
398
{ 0xb3fef47fb27d57c9, 44, "vector_scale_t", },
399
{ 0xb43fd5078ae78029, 84, "send_commandi_stall", },
400
{ 0xb43ffbd4dc446dd2, 324, "atan2f", },
401
{ 0xb5fdb3083e6f4b3f, 36, "vhtfm_t", },
402
{ 0xb6a04277fb1e1a1a, 104, "vmmul_q_transp", },
403
{ 0xb726917d688ac95b, 268, "kagaku_no_ensemble_download_frame", }, // Toaru Majutsu to Kagaku no Ensemble
404
{ 0xb7448c5ffdd3b0fc, 356, "atan2f", },
405
{ 0xb7d88567dc22aab1, 820, "memcpy", }, // Trails in the Sky (US)
406
{ 0xb877d3c37a7aaa5d, 60, "vmmul_q_2", },
407
{ 0xb89aa73b6f94ba95, 52, "vclamp_t", },
408
{ 0xb8bd1f0e02e9ad87, 156, "sceGuLightSpot", },
409
{ 0xb8cfaeebfeb2de20, 7548, "_vfprintf_r", },
410
{ 0xb97f352e85661af6, 32, "finitef", },
411
{ 0xba76a8e853426baa, 544, "soranokiseki_fc_download_frame", }, // Sora no kiseki FC
412
{ 0xbb3c6592ed319ba4, 132, "sceGuFog", },
413
{ 0xbb7d7c93e4c08577, 124, "__truncdfsf2", },
414
{ 0xbdf54d66079afb96, 200, "sceGuBoneMatrix", },
415
{ 0xbe773f78afd1a70f, 128, "rand", },
416
{ 0xbf5d02ccb8514881, 108, "strcmp", },
417
{ 0xbf791954ebef4afb, 396, "expf", },
418
{ 0xbfa8c16038b7753d, 868, "sakurasou_download_frame", }, // Sakurasou No Pet Na Kanojo
419
{ 0xbfe07e305abc4cd1, 808, "memmove" }, // Final Fantasy Tactics (US)
420
{ 0xc062f2545ef5dc39, 1076, "kirameki_school_life_download_frame", },// Kirameki School Life SP,and Boku wa Tomodati ga Sukunai
421
{ 0xc0feb88cc04a1dc7, 48, "sceVfpuVector3Neg", },
422
{ 0xc1220040b0599a75, 472, "soranokiseki_sc_download_frame", }, // Sora no kiseki SC
423
{ 0xc1f34599d0b9146b, 116, "__subdf3", },
424
{ 0xc3089f66ee6f0a24, 464, "growlanser_create_saveicon", }, // Growlanswer IV
425
{ 0xc319f0d107dd2f45, 888, "__muldf3", },
426
{ 0xc35c10300b6b6091, 620, "floor", },
427
{ 0xc3dbf3e6c80a0a51, 164, "dl_write_bone_matrix", },
428
{ 0xc51519f5dab342d4, 224, "cosf", },
429
{ 0xc52c14b9af8c3008, 76, "memcmp", },
430
{ 0xc54eae62622f1e11, 164, "dl_write_bone_matrix_2", },
431
{ 0xc6b29de7d3245198, 656, "starocean_write_stencil" }, // Star Ocean 1 (US)
432
{ 0xc7b1113cfdfedab6, 104, "tonyhawkp8_upload_tutorial_frame", }, // Tony Hawk's Project 8 (US)
433
{ 0xc96e3a087ebf49a9, 100, "dl_write_light_color", },
434
{ 0xca7cb2c0b9410618, 680, "kudwafter_download_frame", }, // Kud Wafter
435
{ 0xcb22120018386319, 692, "photokano_download_frame", }, // Photo Kano
436
{ 0xcb7a2edd603ecfef, 48, "vtfm_p", },
437
{ 0xcdf64d21418b2667, 24, "vzero_q", },
438
{ 0xce1c95ee25b8e2ea, 448, "fmod", },
439
{ 0xce4d18a75b98859f, 40, "vector_add_t_2", },
440
// Unsafe due to immediates.
441
//{ 0xceb5372d0003d951, 52, "dl_write_stenciltest", },
442
{ 0xcee11483b550ce8f, 24, "vocp_q", },
443
{ 0xcfecf208769ed5fd, 272, "cosf", },
444
{ 0xd019b067b58cf6c3, 700, "memmove", }, // Star Ocean 1 (US)
445
{ 0xd12a3a91e0040229, 524, "sceGupSetStatus", },
446
{ 0xd141d1efbfe13ca3, 968, "kirameki_school_life_download_frame", }, // Kirameki School Life SP,and Boku wa Tomodati ga Sukunai
447
{ 0xd1db467a23ebe00d, 724, "rewrite_download_frame", }, // Rewrite Portable
448
{ 0xd1faacfc711d61e8, 68, "__negdf2", },
449
{ 0xd207b0650a41dd9c, 28, "vmin_q", },
450
{ 0xd6d6e0bb21654778, 24, "vneg_t", },
451
{ 0xd7229fee680e7851, 40, "sceVfpuScalarMin", },
452
{ 0xd75670860a7f4b05, 144, "wcsncpy", },
453
{ 0xd76d1a8804c7ec2c, 100, "sceGupModelColor", },
454
{ 0xd7d350c0b33a4662, 28, "vadd_q", },
455
{ 0xd80051931427dca0, 116, "__subdf3", },
456
{ 0xd96ba6e4ff86f1bf, 276, "katamari_screenshot_to_565", }, // Me and My Katamari (US)
457
{ 0xda51dab503b06979, 32, "vmidt_q", },
458
{ 0xdc0cc8b400ecfbf2, 36, "strcmp", },
459
{ 0xdcab869acf2bacab, 292, "strncasecmp", },
460
{ 0xdcdf7e1c1a3dc260, 372, "strncmp", },
461
{ 0xdcfc28e624a81bf1, 5476, "_dtoa_r", },
462
{ 0xddfa5a85937aa581, 32, "vdot_q", },
463
{ 0xdeb6a583659e3948, 1080, "littlebustersce_download_frame", }, // Little Busters! Converted Edition (JP)
464
{ 0xe0214719d8a0aa4e, 104, "strstr", },
465
{ 0xe029f0699ca3a886, 76, "matrix300_transform_by", },
466
{ 0xe086d5c9ce89148f, 212, "bokunonatsuyasumi4_download_frame", }, // Boku no Natsuyasumi 2 and 4,
467
{ 0xe093c2b0194d52b3, 820, "ff1_battle_effect", }, // Final Fantasy 1 (US)
468
{ 0xe1107cf3892724a0, 460, "_memalign_r", },
469
{ 0xe1724e6e29209d97, 24, "vector_length_t_2", },
470
{ 0xe1a5d939cc308195, 68, "wcscmp", },
471
{ 0xe2d9106e5b9e39e6, 80, "strnlen", },
472
{ 0xe3154c81a76515fa, 208, "narisokonai_download_frame", }, // Narisokonai Eiyuutan
473
{ 0xe32cb5c062d1a1c4, 700, "_strtoull_r", },
474
{ 0xe3835fb2c9c04e59, 44, "vmmul_q", },
475
{ 0xe527c62d8613f297, 136, "strcpy", },
476
{ 0xe6002fc9affd678e, 480, "topx_create_saveicon", }, // Tales of Phantasia X
477
{ 0xe7b36c2c1348551d, 148, "tan", },
478
{ 0xe83a7a9d80a21c11, 4448, "_strtod_r", },
479
{ 0xe894bda909a8a8f9, 1064, "expensive_wipeout_pulse", },
480
{ 0xe8ad7719be44e7c8, 276, "strchr", },
481
{ 0xeabb9c1b4f83d2b4, 52, "memset_jak", }, // Crisis Core (US), Jak and Daxter (this is a slow memset and needs to have slow timing)
482
{ 0xeb0f7bf63d52ece9, 88, "strncat", },
483
{ 0xeb8c0834d8bbc28c, 416, "fmodf", },
484
{ 0xed8918f378e9a563, 628, "sd_gundam_g_generation_download_frame", }, // SD Gundam G Generation Overworld
485
{ 0xedbbe9bf9fbceca8, 172, "dl_write_viewport2", },
486
{ 0xedc3f476221f96e6, 148, "tanf", },
487
{ 0xf1f660fdf349eac2, 1588, "_malloc_r", },
488
{ 0xf38a356a359dbe06, 28, "vmax_q", },
489
{ 0xf3fc2220ed0f2703, 32, "send_commandf", },
490
{ 0xf4d797cef4ac88cd, 684, "_free_r", },
491
{ 0xf4ea7d2ec943fa02, 224, "sinf", },
492
{ 0xf4f8cdf479dfc4a4, 224, "sinf", },
493
{ 0xf527d906d69005a0, 848, "photokano_download_frame_2", }, // Photo Kano
494
{ 0xf52f993e444b6c52, 44, "sceGupShadeModel", },
495
{ 0xf56641884b36c638, 468, "scalbn", },
496
{ 0xf5e91870b5b76ddc, 288, "motorstorm_download_frame", }, // MotorStorm: Arctic Edge
497
{ 0xf5f7826b4a61767c, 40, "matrix_copy_q", },
498
{ 0xf73c094e492bc163, 396, "hypot", },
499
{ 0xf773297d89ff7a63, 532, "kumonohatateni_download_frame", }, // Amatsumi Sora ni Kumo no Hatate ni, and Hanakisou
500
{ 0xf7fc691db0147e25, 96, "strspn", },
501
{ 0xf842aea3baa61f29, 32, "vector_length_t", },
502
{ 0xf8e0902f4099a9d6, 2260, "qsort", },
503
{ 0xf972543ab7df071a, 32, "sceVfpuScalarSqrt", },
504
{ 0xf9b00ef163e8b9d4, 32, "vscl_q", },
505
{ 0xf9ea1bf2a897ef24, 588, "ceil", },
506
{ 0xfa156c48461eeeb9, 24, "vf2id_q", },
507
{ 0xfb4253a1d9d9df9f, 20, "isnanf", },
508
{ 0xfd34a9ad94fa6241, 76, "__extendsfdf2", },
509
{ 0xfe2566ad957054b7, 232, "suikoden1_and_2_download_frame_2", }, // Gensou Suikoden 1&2
510
{ 0xfe4f0280240008e9, 28, "vavg_q", },
511
{ 0xfe5dd338ab862291, 216, "memset", }, // Metal Gear Solid: Peace Walker demo
512
{ 0xffc8f5f8f946152c, 192, "dl_write_light_color", },
513
{ 0x249a3c5981c73480, 1472, "openseason_data_decode", }, // Open Season
514
{ 0x795d940ad0a605f8, 40, "gow_fps_hack", }, // God of War (all)
515
{ 0x4c75043b7b0c643b, 512, "gow_vortex_hack", }, // God of War: Ghost of Sparta vortex timer hack, avoids softlock #8299
516
{ 0x7624dde603717640, 288, "ZZT3_select_hack", }, // Zettai Zetsumei Toshi 3 - bypasses softlock on character select screen #4901
517
{ 0x0dc5ca84f707863c, 452, "blitz_fps_hack", }, // Blitz: Overtime
518
{ 0xf93d3cd093595a6c, 856, "brian_lara_fps_hack", }, // Brian Lara 2007: Pressure Play
519
};
520
521
namespace MIPSAnalyst {
522
// Only can ever output a single reg.
523
MIPSGPReg GetOutGPReg(MIPSOpcode op) {
524
MIPSInfo opinfo = MIPSGetInfo(op);
525
if (opinfo & OUT_RT) {
526
return MIPS_GET_RT(op);
527
}
528
if (opinfo & OUT_RD) {
529
return MIPS_GET_RD(op);
530
}
531
if (opinfo & OUT_RA) {
532
return MIPS_REG_RA;
533
}
534
return MIPS_REG_INVALID;
535
}
536
537
bool ReadsFromGPReg(MIPSOpcode op, MIPSGPReg reg) {
538
MIPSInfo info = MIPSGetInfo(op);
539
if ((info & IN_RS) != 0 && MIPS_GET_RS(op) == reg) {
540
return true;
541
}
542
if ((info & IN_RT) != 0 && MIPS_GET_RT(op) == reg) {
543
return true;
544
}
545
return false;
546
}
547
548
bool IsDelaySlotNiceReg(MIPSOpcode branchOp, MIPSOpcode op, MIPSGPReg reg1, MIPSGPReg reg2) {
549
MIPSInfo branchInfo = MIPSGetInfo(branchOp);
550
MIPSInfo info = MIPSGetInfo(op);
551
if (info & IS_CONDBRANCH) {
552
return false;
553
}
554
// $0 is never an out reg, it's always 0.
555
if (reg1 != MIPS_REG_ZERO && GetOutGPReg(op) == reg1) {
556
return false;
557
}
558
if (reg2 != MIPS_REG_ZERO && GetOutGPReg(op) == reg2) {
559
return false;
560
}
561
// If the branch is an "and link" branch, check the delay slot for RA.
562
if ((branchInfo & OUT_RA) != 0) {
563
return GetOutGPReg(op) != MIPS_REG_RA && !ReadsFromGPReg(op, MIPS_REG_RA);
564
}
565
return true;
566
}
567
568
bool IsDelaySlotNiceVFPU(MIPSOpcode branchOp, MIPSOpcode op) {
569
MIPSInfo info = MIPSGetInfo(op);
570
if (info & IS_CONDBRANCH) {
571
return false;
572
}
573
return (info & OUT_VFPU_CC) == 0;
574
}
575
576
bool IsDelaySlotNiceFPU(MIPSOpcode branchOp, MIPSOpcode op) {
577
MIPSInfo info = MIPSGetInfo(op);
578
if (info & IS_CONDBRANCH) {
579
return false;
580
}
581
return (info & OUT_FPUFLAG) == 0;
582
}
583
584
bool IsSyscall(MIPSOpcode op) {
585
// Syscalls look like this: 0000 00-- ---- ---- ---- --00 1100
586
return (op >> 26) == 0 && (op & 0x3f) == 12;
587
}
588
589
static bool IsSWInstr(MIPSOpcode op) {
590
return (op & MIPSTABLE_IMM_MASK) == 0xAC000000;
591
}
592
static bool IsSBInstr(MIPSOpcode op) {
593
return (op & MIPSTABLE_IMM_MASK) == 0xA0000000;
594
}
595
static bool IsSHInstr(MIPSOpcode op) {
596
return (op & MIPSTABLE_IMM_MASK) == 0xA4000000;
597
}
598
599
static bool IsSWLInstr(MIPSOpcode op) {
600
return (op & MIPSTABLE_IMM_MASK) == 0xA8000000;
601
}
602
static bool IsSWRInstr(MIPSOpcode op) {
603
return (op & MIPSTABLE_IMM_MASK) == 0xB8000000;
604
}
605
606
static bool IsSWC1Instr(MIPSOpcode op) {
607
return (op & MIPSTABLE_IMM_MASK) == 0xE4000000;
608
}
609
static bool IsSVSInstr(MIPSOpcode op) {
610
return (op & MIPSTABLE_IMM_MASK) == 0xE8000000;
611
}
612
static bool IsSVQInstr(MIPSOpcode op) {
613
return (op & MIPSTABLE_IMM_MASK) == 0xF8000000;
614
}
615
616
int OpMemoryAccessSize(u32 pc) {
617
const auto op = Memory::Read_Instruction(pc, true);
618
return MIPSGetMemoryAccessSize(op);
619
}
620
621
bool IsOpMemoryWrite(u32 pc) {
622
const auto op = Memory::Read_Instruction(pc, true);
623
MIPSInfo info = MIPSGetInfo(op);
624
return (info & OUT_MEM) != 0;
625
}
626
627
bool OpHasDelaySlot(u32 pc) {
628
const auto op = Memory::Read_Instruction(pc, true);
629
MIPSInfo info = MIPSGetInfo(op);
630
return (info & DELAYSLOT) != 0;
631
}
632
633
bool OpWouldChangeMemory(u32 pc, u32 addr, u32 size) {
634
const auto op = Memory::Read_Instruction(pc, true);
635
636
// TODO: Trap sc/ll, svl.q, svr.q?
637
638
int gprMask = 0;
639
if (IsSWInstr(op))
640
gprMask = 0xFFFFFFFF;
641
if (IsSHInstr(op))
642
gprMask = 0x0000FFFF;
643
if (IsSBInstr(op))
644
gprMask = 0x000000FF;
645
if (IsSWLInstr(op)) {
646
const u32 shift = (addr & 3) * 8;
647
gprMask = 0xFFFFFFFF >> (24 - shift);
648
}
649
if (IsSWRInstr(op)) {
650
const u32 shift = (addr & 3) * 8;
651
gprMask = 0xFFFFFFFF << shift;
652
}
653
654
u32 writeVal = 0xFFFFFFFF;
655
u32 prevVal = 0x00000000;
656
657
if (gprMask != 0)
658
{
659
MIPSGPReg rt = MIPS_GET_RT(op);
660
writeVal = currentMIPS->r[rt] & gprMask;
661
prevVal = Memory::Read_U32(addr) & gprMask;
662
}
663
664
if (IsSWC1Instr(op)) {
665
int ft = MIPS_GET_FT(op);
666
writeVal = currentMIPS->fi[ft];
667
prevVal = Memory::Read_U32(addr);
668
}
669
670
if (IsSVSInstr(op)) {
671
int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5);
672
writeVal = currentMIPS->vi[voffset[vt]];
673
prevVal = Memory::Read_U32(addr);
674
}
675
676
if (IsSVQInstr(op)) {
677
int vt = (((op >> 16) & 0x1f)) | ((op & 1) << 5);
678
float rd[4];
679
ReadVector(rd, V_Quad, vt);
680
return memcmp(rd, Memory::GetPointerRange(addr, 16), sizeof(float) * 4) != 0;
681
}
682
683
return writeVal != prevVal;
684
}
685
686
AnalysisResults Analyze(u32 address) {
687
const int MAX_ANALYZE = 10000;
688
689
AnalysisResults results;
690
691
//set everything to -1 (FF)
692
memset(&results, 255, sizeof(AnalysisResults));
693
for (int i = 0; i < MIPS_NUM_GPRS; i++) {
694
results.r[i].used = false;
695
results.r[i].readCount = 0;
696
results.r[i].writeCount = 0;
697
results.r[i].readAsAddrCount = 0;
698
}
699
700
for (u32 addr = address, endAddr = address + MAX_ANALYZE; addr <= endAddr; addr += 4) {
701
MIPSOpcode op = Memory::Read_Instruction(addr, true);
702
MIPSInfo info = MIPSGetInfo(op);
703
704
MIPSGPReg rs = MIPS_GET_RS(op);
705
MIPSGPReg rt = MIPS_GET_RT(op);
706
707
if (info & IN_RS) {
708
if ((info & IN_RS_ADDR) == IN_RS_ADDR) {
709
results.r[rs].MarkReadAsAddr(addr);
710
} else {
711
results.r[rs].MarkRead(addr);
712
}
713
}
714
715
if (info & IN_RT) {
716
results.r[rt].MarkRead(addr);
717
}
718
719
MIPSGPReg outReg = GetOutGPReg(op);
720
if (outReg != MIPS_REG_INVALID) {
721
results.r[outReg].MarkWrite(addr);
722
}
723
724
if (info & DELAYSLOT) {
725
// Let's just finish the delay slot before bailing.
726
endAddr = addr + 4;
727
}
728
}
729
730
int numUsedRegs = 0;
731
static int totalUsedRegs = 0;
732
static int numAnalyzings = 0;
733
for (int i = 0; i < MIPS_NUM_GPRS; i++) {
734
if (results.r[i].used) {
735
numUsedRegs++;
736
}
737
}
738
totalUsedRegs += numUsedRegs;
739
numAnalyzings++;
740
VERBOSE_LOG(Log::CPU, "[ %08x ] Used regs: %i Average: %f", address, numUsedRegs, (float)totalUsedRegs / (float)numAnalyzings);
741
742
return results;
743
}
744
745
void Reset() {
746
std::lock_guard<std::recursive_mutex> guard(functions_lock);
747
functions.clear();
748
hashToFunction.clear();
749
}
750
751
void UpdateHashToFunctionMap() {
752
std::lock_guard<std::recursive_mutex> guard(functions_lock);
753
hashToFunction.clear();
754
// Really need to detect C++11 features with better defines.
755
#if !PPSSPP_PLATFORM(IOS)
756
hashToFunction.reserve(functions.size());
757
#endif
758
for (auto iter = functions.begin(); iter != functions.end(); iter++) {
759
AnalyzedFunction &f = *iter;
760
if (f.hasHash && f.size > 16) {
761
hashToFunction.emplace(f.hash, &f);
762
}
763
}
764
}
765
766
enum RegisterUsage {
767
USAGE_CLOBBERED,
768
USAGE_INPUT,
769
USAGE_UNKNOWN,
770
};
771
772
static RegisterUsage DetermineInOutUsage(u64 inFlag, u64 outFlag, u32 addr, int instrs) {
773
const u32 start = addr;
774
u32 end = addr + instrs * sizeof(u32);
775
bool canClobber = true;
776
while (addr < end) {
777
const MIPSOpcode op = Memory::Read_Instruction(addr, true);
778
const MIPSInfo info = MIPSGetInfo(op);
779
780
// Yes, used.
781
if (info & inFlag)
782
return USAGE_INPUT;
783
784
// Clobbered, so not used.
785
if (info & outFlag)
786
return canClobber ? USAGE_CLOBBERED : USAGE_UNKNOWN;
787
788
// Bail early if we hit a branch (could follow each path for continuing?)
789
if ((info & IS_CONDBRANCH) || (info & IS_JUMP)) {
790
// Still need to check the delay slot (so end after it.)
791
// We'll assume likely are taken.
792
end = addr + 8;
793
// The reason for the start != addr check is that we compile delay slots before branches.
794
// That means if we're starting at the branch, it's not safe to allow the delay slot
795
// to clobber, since it might have already been compiled.
796
// As for LIKELY, we don't know if it'll run the branch or not.
797
canClobber = (info & LIKELY) == 0 && start != addr;
798
}
799
addr += 4;
800
}
801
return USAGE_UNKNOWN;
802
}
803
804
static RegisterUsage DetermineRegisterUsage(MIPSGPReg reg, u32 addr, int instrs) {
805
switch (reg) {
806
case MIPS_REG_HI:
807
return DetermineInOutUsage(IN_HI, OUT_HI, addr, instrs);
808
case MIPS_REG_LO:
809
return DetermineInOutUsage(IN_LO, OUT_LO, addr, instrs);
810
case MIPS_REG_FPCOND:
811
return DetermineInOutUsage(IN_FPUFLAG, OUT_FPUFLAG, addr, instrs);
812
case MIPS_REG_VFPUCC:
813
return DetermineInOutUsage(IN_VFPU_CC, OUT_VFPU_CC, addr, instrs);
814
default:
815
break;
816
}
817
818
if (reg >= 32) {
819
return USAGE_UNKNOWN;
820
}
821
822
const u32 start = addr;
823
u32 end = addr + instrs * sizeof(u32);
824
bool canClobber = true;
825
while (addr < end) {
826
const MIPSOpcode op = Memory::Read_Instruction(addr, true);
827
const MIPSInfo info = MIPSGetInfo(op);
828
829
// Yes, used.
830
if ((info & IN_RS) && (MIPS_GET_RS(op) == reg))
831
return USAGE_INPUT;
832
if ((info & IN_RT) && (MIPS_GET_RT(op) == reg))
833
return USAGE_INPUT;
834
835
// Clobbered, so not used.
836
bool clobbered = false;
837
if ((info & OUT_RT) && (MIPS_GET_RT(op) == reg))
838
clobbered = true;
839
if ((info & OUT_RD) && (MIPS_GET_RD(op) == reg))
840
clobbered = true;
841
if ((info & OUT_RA) && (reg == MIPS_REG_RA))
842
clobbered = true;
843
if (clobbered) {
844
if (!canClobber || (info & IS_CONDMOVE))
845
return USAGE_UNKNOWN;
846
return USAGE_CLOBBERED;
847
}
848
849
// Bail early if we hit a branch (could follow each path for continuing?)
850
if ((info & IS_CONDBRANCH) || (info & IS_JUMP)) {
851
// Still need to check the delay slot (so end after it.)
852
// We'll assume likely are taken.
853
end = addr + 8;
854
// The reason for the start != addr check is that we compile delay slots before branches.
855
// That means if we're starting at the branch, it's not safe to allow the delay slot
856
// to clobber, since it might have already been compiled.
857
// As for LIKELY, we don't know if it'll run the branch or not.
858
canClobber = (info & LIKELY) == 0 && start != addr;
859
}
860
addr += 4;
861
}
862
return USAGE_UNKNOWN;
863
}
864
865
bool IsRegisterUsed(MIPSGPReg reg, u32 addr, int instrs) {
866
return DetermineRegisterUsage(reg, addr, instrs) == USAGE_INPUT;
867
}
868
869
bool IsRegisterClobbered(MIPSGPReg reg, u32 addr, int instrs) {
870
return DetermineRegisterUsage(reg, addr, instrs) == USAGE_CLOBBERED;
871
}
872
873
void HashFunctions() {
874
std::lock_guard<std::recursive_mutex> guard(functions_lock);
875
std::vector<u32> buffer;
876
877
for (auto iter = functions.begin(), end = functions.end(); iter != end; iter++) {
878
AnalyzedFunction &f = *iter;
879
if (!Memory::IsValidRange(f.start, f.end - f.start + 4)) {
880
continue;
881
}
882
883
// This is unfortunate. In case of emuhacks or relocs, we have to make a copy.
884
buffer.resize((f.end - f.start + 4) / 4);
885
size_t pos = 0;
886
for (u32 addr = f.start; addr <= f.end; addr += 4) {
887
u32 validbits = 0xFFFFFFFF;
888
MIPSOpcode instr = Memory::ReadUnchecked_Instruction(addr, true);
889
if (MIPS_IS_EMUHACK(instr)) {
890
f.hasHash = false;
891
goto skip;
892
}
893
894
MIPSInfo flags = MIPSGetInfo(instr);
895
if (flags & IN_IMM16)
896
validbits &= ~0xFFFF;
897
if (flags & IN_IMM26)
898
validbits &= ~0x03FFFFFF;
899
buffer[pos++] = instr & validbits;
900
}
901
902
f.hash = CityHash64((const char *) &buffer[0], buffer.size() * sizeof(u32));
903
f.hasHash = true;
904
skip:
905
;
906
}
907
}
908
909
void PrecompileFunction(u32 startAddr, u32 length) {
910
// Direct calls to this ignore the bPreloadFunctions flag, since it's just for stubs.
911
std::lock_guard<std::recursive_mutex> guard(MIPSComp::jitLock);
912
if (MIPSComp::jit) {
913
MIPSComp::jit->CompileFunction(startAddr, length);
914
}
915
}
916
917
void PrecompileFunctions() {
918
if (!g_Config.bPreloadFunctions) {
919
return;
920
}
921
std::lock_guard<std::recursive_mutex> guard(functions_lock);
922
923
// TODO: Load from cache file if available instead.
924
925
double st = time_now_d();
926
for (auto iter = functions.begin(), end = functions.end(); iter != end; iter++) {
927
const AnalyzedFunction &f = *iter;
928
929
PrecompileFunction(f.start, f.end - f.start + 4);
930
}
931
double et = time_now_d();
932
933
NOTICE_LOG(Log::JIT, "Precompiled %d MIPS functions in %0.2f milliseconds", (int)functions.size(), (et - st) * 1000.0);
934
}
935
936
static const char *DefaultFunctionName(char buffer[256], u32 startAddr) {
937
snprintf(buffer, 256, "z_un_%08x", startAddr);
938
return buffer;
939
}
940
941
static bool IsDefaultFunction(const char *name) {
942
if (name == NULL) {
943
// Must be I guess?
944
return true;
945
}
946
947
// Un named stubs, just in case.
948
if (!strncmp(name, "[UNK:", strlen("[UNK:")))
949
return true;
950
951
// Assume any z_un, not just the address, is a default func.
952
return !strncmp(name, "z_un_", strlen("z_un_")) || !strncmp(name, "u_un_", strlen("u_un_"));
953
}
954
955
static bool IsDefaultFunction(const std::string &name) {
956
if (name.empty()) {
957
// Must be I guess?
958
return true;
959
}
960
961
return IsDefaultFunction(name.c_str());
962
}
963
964
static u32 ScanAheadForJumpback(u32 fromAddr, u32 knownStart, u32 knownEnd) {
965
static const u32 MAX_AHEAD_SCAN = 0x1000;
966
// Maybe a bit high... just to make sure we don't get confused by recursive tail recursion.
967
static const u32 MAX_FUNC_SIZE = 0x20000;
968
969
if (fromAddr > knownEnd + MAX_FUNC_SIZE) {
970
return INVALIDTARGET;
971
}
972
973
// Code might jump halfway up to before fromAddr, but after knownEnd.
974
// In that area, there could be another jump up to the valid range.
975
// So we track that for a second scan.
976
u32 closestJumpbackAddr = INVALIDTARGET;
977
u32 closestJumpbackTarget = fromAddr;
978
979
// We assume the furthest jumpback is within the func.
980
u32 furthestJumpbackAddr = INVALIDTARGET;
981
982
const u32 scanEnd = fromAddr + Memory::ValidSize(fromAddr, MAX_AHEAD_SCAN);
983
for (u32 ahead = fromAddr; ahead < scanEnd; ahead += 4) {
984
MIPSOpcode aheadOp = Memory::Read_Instruction(ahead, true);
985
u32 target = GetBranchTargetNoRA(ahead, aheadOp);
986
if (target == INVALIDTARGET && ((aheadOp & 0xFC000000) == 0x08000000)) {
987
target = GetJumpTarget(ahead);
988
}
989
990
if (target != INVALIDTARGET) {
991
// Only if it comes back up to known code within this func.
992
if (target >= knownStart && target <= knownEnd) {
993
furthestJumpbackAddr = ahead;
994
}
995
// But if it jumps above fromAddr, we should scan that area too...
996
if (target < closestJumpbackTarget && target < fromAddr && target > knownEnd) {
997
closestJumpbackAddr = ahead;
998
closestJumpbackTarget = target;
999
}
1000
}
1001
if (aheadOp == MIPS_MAKE_JR_RA()) {
1002
break;
1003
}
1004
}
1005
1006
if (closestJumpbackAddr != INVALIDTARGET && furthestJumpbackAddr == INVALIDTARGET) {
1007
for (u32 behind = closestJumpbackTarget; behind < fromAddr; behind += 4) {
1008
MIPSOpcode behindOp = Memory::Read_Instruction(behind, true);
1009
u32 target = GetBranchTargetNoRA(behind, behindOp);
1010
if (target == INVALIDTARGET && ((behindOp & 0xFC000000) == 0x08000000)) {
1011
target = GetJumpTarget(behind);
1012
}
1013
1014
if (target != INVALIDTARGET) {
1015
if (target >= knownStart && target <= knownEnd) {
1016
furthestJumpbackAddr = closestJumpbackAddr;
1017
}
1018
}
1019
}
1020
}
1021
1022
return furthestJumpbackAddr;
1023
}
1024
1025
bool ScanForFunctions(u32 startAddr, u32 endAddr, bool insertSymbols) {
1026
std::lock_guard<std::recursive_mutex> guard(functions_lock);
1027
1028
FunctionsVector new_functions;
1029
1030
AnalyzedFunction currentFunction = {startAddr};
1031
1032
u32 furthestBranch = 0;
1033
bool looking = false;
1034
bool end = false;
1035
bool isStraightLeaf = true;
1036
bool decreasedSp = false;
1037
1038
u32 addr;
1039
for (addr = startAddr; addr <= endAddr; addr += 4) {
1040
MIPSOpcode op = Memory::Read_Instruction(addr, true);
1041
u32 target = GetBranchTargetNoRA(addr, op);
1042
if (target != INVALIDTARGET) {
1043
isStraightLeaf = false;
1044
if (target > furthestBranch) {
1045
furthestBranch = target;
1046
}
1047
// j X
1048
} else if ((op & 0xFC000000) == 0x08000000) {
1049
u32 sureTarget = GetJumpTarget(addr);
1050
// Check for a tail call. Might not even have a jr ra.
1051
if (sureTarget != INVALIDTARGET && sureTarget < currentFunction.start) {
1052
if (furthestBranch > addr) {
1053
looking = true;
1054
addr += 4;
1055
} else {
1056
end = true;
1057
}
1058
} else if (sureTarget != INVALIDTARGET && sureTarget > addr && sureTarget > furthestBranch) {
1059
static const u32 MAX_JUMP_FORWARD = 128;
1060
// If it's a nearby forward jump, and not a stackless leaf, assume not a tail call.
1061
if (sureTarget <= addr + MAX_JUMP_FORWARD && decreasedSp) {
1062
// But let's check the delay slot.
1063
MIPSOpcode op = Memory::Read_Instruction(addr + 4, true);
1064
// addiu sp, sp, +X
1065
if ((op & 0xFFFF8000) != 0x27BD0000) {
1066
furthestBranch = sureTarget;
1067
continue;
1068
}
1069
}
1070
1071
// A jump later. Probably tail, but let's check if it jumps back.
1072
// We use + 8 here in case it jumps right back to the delay slot. We'll consider that inside the func.
1073
u32 knownEnd = furthestBranch == 0 ? addr + 8 : furthestBranch;
1074
u32 jumpback = ScanAheadForJumpback(sureTarget, currentFunction.start, knownEnd);
1075
if (jumpback != INVALIDTARGET && jumpback > addr && jumpback > knownEnd) {
1076
furthestBranch = jumpback;
1077
} else {
1078
if (furthestBranch > addr) {
1079
looking = true;
1080
addr += 4;
1081
} else {
1082
end = true;
1083
}
1084
}
1085
}
1086
}
1087
if (op == MIPS_MAKE_JR_RA()) {
1088
// If a branch goes to the jr ra, it's still ending here.
1089
if (furthestBranch > addr) {
1090
looking = true;
1091
addr += 4;
1092
} else {
1093
end = true;
1094
}
1095
}
1096
// addiu sp, sp, -X
1097
if ((op & 0xFFFF8000) == 0x27BD8000) {
1098
decreasedSp = true;
1099
}
1100
// addiu sp, sp, +X
1101
if ((op & 0xFFFF8000) == 0x27BD0000) {
1102
decreasedSp = false;
1103
}
1104
if (op == MIPS_MAKE_NOP() && currentFunction.start == addr) {
1105
// Skip nop padding at the beginning of functions (alignment?)
1106
currentFunction.start += 4;
1107
}
1108
1109
if (looking) {
1110
if (addr >= furthestBranch) {
1111
u32 sureTarget = GetSureBranchTarget(addr);
1112
// Regular j only, jals are to new funcs.
1113
if (sureTarget == INVALIDTARGET && ((op & 0xFC000000) == 0x08000000)) {
1114
sureTarget = GetJumpTarget(addr);
1115
}
1116
1117
if (sureTarget != INVALIDTARGET && sureTarget < addr) {
1118
end = true;
1119
} else if (sureTarget != INVALIDTARGET) {
1120
// Okay, we have a downward jump. Might be an else or a tail call...
1121
// If there's a jump back upward in spitting distance of it, it's an else.
1122
u32 knownEnd = furthestBranch == 0 ? addr : furthestBranch;
1123
u32 jumpback = ScanAheadForJumpback(sureTarget, currentFunction.start, knownEnd);
1124
if (jumpback != INVALIDTARGET && jumpback > addr && jumpback > knownEnd) {
1125
furthestBranch = jumpback;
1126
}
1127
}
1128
}
1129
}
1130
1131
if (end) {
1132
currentFunction.end = addr + 4;
1133
currentFunction.isStraightLeaf = isStraightLeaf;
1134
1135
// Check if we already have symbol info starting here. If so, skip insertion.
1136
// We used to use the symbols to find the functions, but sometimes we'd find
1137
// wrong ones due to two modules with the same name.
1138
u32 existingSize = g_symbolMap->GetFunctionSize(currentFunction.start);
1139
if (existingSize != SymbolMap::INVALID_ADDRESS) {
1140
currentFunction.foundInSymbolMap = true;
1141
1142
// If we run into a func with a different size, skip updating the hash map.
1143
// This will prevent us saving incorrectly named funcs with wrong hashes.
1144
u32 detectedSize = currentFunction.end - currentFunction.start + 4;
1145
if (existingSize != detectedSize) {
1146
insertSymbols = false;
1147
}
1148
}
1149
1150
new_functions.push_back(currentFunction);
1151
1152
furthestBranch = 0;
1153
addr += 4;
1154
looking = false;
1155
end = false;
1156
isStraightLeaf = true;
1157
decreasedSp = false;
1158
currentFunction.start = addr + 4;
1159
currentFunction.foundInSymbolMap = false;
1160
}
1161
}
1162
1163
if (addr <= endAddr) {
1164
currentFunction.end = addr + 4;
1165
new_functions.push_back(currentFunction);
1166
}
1167
1168
for (auto iter = new_functions.begin(); iter != new_functions.end(); iter++) {
1169
iter->size = iter->end - iter->start + 4;
1170
if (insertSymbols && !iter->foundInSymbolMap) {
1171
char temp[256];
1172
g_symbolMap->AddFunction(DefaultFunctionName(temp, iter->start), iter->start, iter->end - iter->start + 4);
1173
}
1174
}
1175
1176
// Concatenate the new functions to the end of the old ones.
1177
functions.insert(functions.end(), new_functions.begin(), new_functions.end());
1178
return insertSymbols;
1179
}
1180
1181
void FinalizeScan(bool insertSymbols) {
1182
HashFunctions();
1183
1184
if (g_Config.bFuncHashMap || g_Config.bFuncReplacements) {
1185
LoadBuiltinHashMap();
1186
if (g_Config.bFuncHashMap) {
1187
Path hashMapFilename = GetSysDirectory(DIRECTORY_SYSTEM) / "knownfuncs.ini";
1188
LoadHashMap(hashMapFilename);
1189
StoreHashMap(hashMapFilename);
1190
}
1191
if (insertSymbols) {
1192
ApplyHashMap();
1193
}
1194
if (g_Config.bFuncReplacements) {
1195
ReplaceFunctions();
1196
}
1197
}
1198
}
1199
1200
bool SkipFuncHash(const std::string &name) {
1201
std::vector<std::string> funcs;
1202
SplitString(g_Config.sSkipFuncHashMap, ',', funcs);
1203
return std::find(funcs.begin(), funcs.end(), name) != funcs.end();
1204
}
1205
1206
void RegisterFunction(u32 startAddr, u32 size, const char *name) {
1207
std::lock_guard<std::recursive_mutex> guard(functions_lock);
1208
1209
// Check if we have this already
1210
for (auto iter = functions.begin(); iter != functions.end(); iter++) {
1211
if (iter->start == startAddr) {
1212
// Let's just add it to the hashmap.
1213
if (iter->hasHash && size > 16 && SkipFuncHash(name)) {
1214
HashMapFunc hfun;
1215
hfun.hash = iter->hash;
1216
strncpy(hfun.name, name, 64);
1217
hfun.name[63] = 0;
1218
hfun.size = size;
1219
hashMap.insert(hfun);
1220
return;
1221
} else if (!iter->hasHash || size == 0) {
1222
ERROR_LOG(Log::HLE, "%s: %08x %08x : match but no hash (%i) or no size", name, startAddr, size, iter->hasHash);
1223
}
1224
}
1225
}
1226
1227
// Cheats a little.
1228
AnalyzedFunction fun;
1229
fun.start = startAddr;
1230
fun.end = startAddr + size - 4;
1231
fun.isStraightLeaf = false; // dunno really
1232
strncpy(fun.name, name, 64);
1233
fun.name[63] = 0;
1234
functions.push_back(fun);
1235
1236
HashFunctions();
1237
}
1238
1239
void ForgetFunctions(u32 startAddr, u32 endAddr) {
1240
std::lock_guard<std::recursive_mutex> guard(functions_lock);
1241
1242
// It makes sense to forget functions as modules are unloaded but it breaks
1243
// the easy way of saving a hashmap by unloading and loading a game. I added
1244
// an alternative way.
1245
1246
// Most of the time, functions from the same module will be contiguous in functions.
1247
FunctionsVector::iterator prevMatch = functions.end();
1248
size_t originalSize = functions.size();
1249
for (auto iter = functions.begin(); iter != functions.end(); ++iter) {
1250
const bool hadPrevMatch = prevMatch != functions.end();
1251
const bool match = iter->start >= startAddr && iter->start <= endAddr;
1252
1253
if (!hadPrevMatch && match) {
1254
// Entering a range.
1255
prevMatch = iter;
1256
} else if (hadPrevMatch && !match) {
1257
// Left a range.
1258
iter = functions.erase(prevMatch, iter);
1259
prevMatch = functions.end();
1260
}
1261
}
1262
if (prevMatch != functions.end()) {
1263
// Cool, this is the fastest way.
1264
functions.erase(prevMatch, functions.end());
1265
}
1266
1267
RestoreReplacedInstructions(startAddr, endAddr);
1268
1269
if (functions.empty()) {
1270
hashToFunction.clear();
1271
} else if (originalSize != functions.size()) {
1272
UpdateHashToFunctionMap();
1273
}
1274
}
1275
1276
void ReplaceFunctions() {
1277
std::lock_guard<std::recursive_mutex> guard(functions_lock);
1278
1279
for (size_t i = 0; i < functions.size(); i++) {
1280
WriteReplaceInstructions(functions[i].start, functions[i].hash, functions[i].size);
1281
}
1282
}
1283
1284
void UpdateHashMap() {
1285
std::lock_guard<std::recursive_mutex> guard(functions_lock);
1286
1287
for (auto it = functions.begin(), end = functions.end(); it != end; ++it) {
1288
const AnalyzedFunction &f = *it;
1289
// Small functions aren't very interesting.
1290
if (!f.hasHash || f.size <= 16) {
1291
continue;
1292
}
1293
// Functions with default names aren't very interesting either.
1294
const std::string name = g_symbolMap->GetLabelString(f.start);
1295
if (IsDefaultFunction(name) || SkipFuncHash(name)) {
1296
continue;
1297
}
1298
1299
HashMapFunc mf = { "", f.hash, f.size };
1300
strncpy(mf.name, name.c_str(), sizeof(mf.name) - 1);
1301
hashMap.insert(mf);
1302
}
1303
}
1304
1305
const char *LookupHash(u64 hash, u32 funcsize) {
1306
const HashMapFunc f = { "", hash, funcsize };
1307
auto it = hashMap.find(f);
1308
if (it != hashMap.end()) {
1309
return it->name;
1310
}
1311
return 0;
1312
}
1313
1314
void SetHashMapFilename(const std::string& filename) {
1315
if (filename.empty())
1316
hashmapFileName = GetSysDirectory(DIRECTORY_SYSTEM) / "knownfuncs.ini";
1317
else
1318
hashmapFileName = Path(filename);
1319
}
1320
1321
void StoreHashMap(Path filename) {
1322
if (filename.empty())
1323
filename = hashmapFileName;
1324
1325
UpdateHashMap();
1326
if (hashMap.empty()) {
1327
return;
1328
}
1329
1330
FILE *file = File::OpenCFile(filename, "wt");
1331
if (!file) {
1332
WARN_LOG(Log::Loader, "Could not store hash map: %s", filename.c_str());
1333
return;
1334
}
1335
1336
for (auto it = hashMap.begin(), end = hashMap.end(); it != end; ++it) {
1337
const HashMapFunc &mf = *it;
1338
if (!mf.hardcoded) {
1339
if (fprintf(file, "%016llx:%d = %s\n", mf.hash, mf.size, mf.name) <= 0) {
1340
WARN_LOG(Log::Loader, "Could not store hash map: %s", filename.c_str());
1341
break;
1342
}
1343
}
1344
}
1345
fclose(file);
1346
}
1347
1348
void ApplyHashMap() {
1349
UpdateHashToFunctionMap();
1350
1351
for (auto mf = hashMap.begin(), end = hashMap.end(); mf != end; ++mf) {
1352
auto range = hashToFunction.equal_range(mf->hash);
1353
if (range.first == range.second) {
1354
continue;
1355
}
1356
1357
// Yay, found a function.
1358
for (auto iter = range.first; iter != range.second; ++iter) {
1359
AnalyzedFunction &f = *iter->second;
1360
if (f.hash == mf->hash && f.size == mf->size) {
1361
strncpy(f.name, mf->name, sizeof(mf->name) - 1);
1362
1363
std::string existingLabel = g_symbolMap->GetLabelString(f.start);
1364
char defaultLabel[256];
1365
// If it was renamed, keep it. Only change the name if it's still the default.
1366
if (existingLabel.empty() || existingLabel == DefaultFunctionName(defaultLabel, f.start)) {
1367
g_symbolMap->SetLabelName(mf->name, f.start);
1368
}
1369
}
1370
}
1371
}
1372
}
1373
1374
void LoadBuiltinHashMap() {
1375
HashMapFunc mf;
1376
for (size_t i = 0; i < ARRAY_SIZE(hardcodedHashes); i++) {
1377
mf.hash = hardcodedHashes[i].hash;
1378
mf.size = hardcodedHashes[i].funcSize;
1379
strncpy(mf.name, hardcodedHashes[i].funcName, sizeof(mf.name));
1380
mf.name[sizeof(mf.name) - 1] = 0;
1381
mf.hardcoded = true;
1382
hashMap.insert(mf);
1383
}
1384
}
1385
1386
void LoadHashMap(const Path &filename) {
1387
FILE *file = File::OpenCFile(filename, "rt");
1388
if (!file) {
1389
WARN_LOG(Log::Loader, "Could not load hash map: %s", filename.c_str());
1390
return;
1391
}
1392
hashmapFileName = filename;
1393
1394
while (!feof(file)) {
1395
HashMapFunc mf = { "" };
1396
mf.hardcoded = false;
1397
if (fscanf(file, "%llx:%d = %63s\n", &mf.hash, &mf.size, mf.name) < 3) {
1398
char temp[1024];
1399
if (!fgets(temp, 1024, file)) {
1400
WARN_LOG(Log::Loader, "Could not read from hash map: %s", filename.c_str());
1401
}
1402
continue;
1403
}
1404
1405
hashMap.insert(mf);
1406
}
1407
fclose(file);
1408
}
1409
1410
std::vector<MIPSGPReg> GetInputRegs(MIPSOpcode op) {
1411
std::vector<MIPSGPReg> vec;
1412
MIPSInfo info = MIPSGetInfo(op);
1413
if (info & IN_RS) vec.push_back(MIPS_GET_RS(op));
1414
if (info & IN_RT) vec.push_back(MIPS_GET_RT(op));
1415
return vec;
1416
}
1417
1418
std::vector<MIPSGPReg> GetOutputRegs(MIPSOpcode op) {
1419
std::vector<MIPSGPReg> vec;
1420
MIPSInfo info = MIPSGetInfo(op);
1421
if (info & OUT_RD) vec.push_back(MIPS_GET_RD(op));
1422
if (info & OUT_RT) vec.push_back(MIPS_GET_RT(op));
1423
if (info & OUT_RA) vec.push_back(MIPS_REG_RA);
1424
return vec;
1425
}
1426
1427
MipsOpcodeInfo GetOpcodeInfo(DebugInterface* cpu, u32 address) {
1428
MipsOpcodeInfo info;
1429
memset(&info, 0, sizeof(info));
1430
1431
if (!Memory::IsValidAddress(address)) {
1432
info.opcodeAddress = address;
1433
return info;
1434
}
1435
1436
info.cpu = cpu;
1437
info.opcodeAddress = address;
1438
info.encodedOpcode = Memory::Read_Instruction(address);
1439
1440
MIPSOpcode op = info.encodedOpcode;
1441
MIPSInfo opInfo = MIPSGetInfo(op);
1442
info.isLikelyBranch = (opInfo & LIKELY) != 0;
1443
1444
// gather relevant address for alu operations
1445
// that's usually the value of the dest register
1446
switch (MIPS_GET_OP(op)) {
1447
case 0: // special
1448
switch (MIPS_GET_FUNC(op)) {
1449
case 0x20: // add
1450
case 0x21: // addu
1451
info.hasRelevantAddress = true;
1452
info.relevantAddress = cpu->GetRegValue(0,MIPS_GET_RS(op))+cpu->GetRegValue(0,MIPS_GET_RT(op));
1453
break;
1454
case 0x22: // sub
1455
case 0x23: // subu
1456
info.hasRelevantAddress = true;
1457
info.relevantAddress = cpu->GetRegValue(0,MIPS_GET_RS(op))-cpu->GetRegValue(0,MIPS_GET_RT(op));
1458
break;
1459
}
1460
break;
1461
case 0x08: // addi
1462
case 0x09: // addiu
1463
info.hasRelevantAddress = true;
1464
info.relevantAddress = cpu->GetRegValue(0, MIPS_GET_RS(op)) + SignExtend16ToS32(op & 0xFFFF);
1465
break;
1466
}
1467
1468
//j , jal, ...
1469
if (opInfo & IS_JUMP) {
1470
info.isBranch = true;
1471
if ((opInfo & OUT_RA) || (opInfo & OUT_RD)) { // link
1472
info.isLinkedBranch = true;
1473
}
1474
1475
if (opInfo & IN_RS) { // to register
1476
info.isBranchToRegister = true;
1477
info.branchRegisterNum = (int)MIPS_GET_RS(op);
1478
info.branchTarget = cpu->GetRegValue(0,info.branchRegisterNum);
1479
} else { // to immediate
1480
info.branchTarget = GetJumpTarget(address);
1481
}
1482
}
1483
1484
// movn, movz
1485
if (opInfo & IS_CONDMOVE) {
1486
info.isConditional = true;
1487
1488
u32 rt = cpu->GetRegValue(0, (int)MIPS_GET_RT(op));
1489
switch (opInfo & CONDTYPE_MASK) {
1490
case CONDTYPE_EQ:
1491
info.conditionMet = (rt == 0);
1492
break;
1493
case CONDTYPE_NE:
1494
info.conditionMet = (rt != 0);
1495
break;
1496
}
1497
}
1498
1499
// beq, bgtz, ...
1500
if (opInfo & IS_CONDBRANCH) {
1501
info.isBranch = true;
1502
info.isConditional = true;
1503
info.branchTarget = GetBranchTarget(address);
1504
1505
if (opInfo & OUT_RA) { // link
1506
info.isLinkedBranch = true;
1507
}
1508
1509
u32 rt = cpu->GetRegValue(0, (int)MIPS_GET_RT(op));
1510
u32 rs = cpu->GetRegValue(0, (int)MIPS_GET_RS(op));
1511
switch (opInfo & CONDTYPE_MASK) {
1512
case CONDTYPE_EQ:
1513
if (opInfo & IN_FPUFLAG) { // fpu branch
1514
info.conditionMet = currentMIPS->fpcond == 0;
1515
} else {
1516
info.conditionMet = (rt == rs);
1517
if (MIPS_GET_RT(op) == MIPS_GET_RS(op)) { // always true
1518
info.isConditional = false;
1519
}
1520
}
1521
break;
1522
case CONDTYPE_NE:
1523
if (opInfo & IN_FPUFLAG) { // fpu branch
1524
info.conditionMet = currentMIPS->fpcond != 0;
1525
} else {
1526
info.conditionMet = (rt != rs);
1527
if (MIPS_GET_RT(op) == MIPS_GET_RS(op)) { // always true
1528
info.isConditional = false;
1529
}
1530
}
1531
break;
1532
case CONDTYPE_LEZ:
1533
info.conditionMet = (((s32)rs) <= 0);
1534
break;
1535
case CONDTYPE_GTZ:
1536
info.conditionMet = (((s32)rs) > 0);
1537
break;
1538
case CONDTYPE_LTZ:
1539
info.conditionMet = (((s32)rs) < 0);
1540
break;
1541
case CONDTYPE_GEZ:
1542
info.conditionMet = (((s32)rs) >= 0);
1543
break;
1544
}
1545
}
1546
1547
// lw, sh, ...
1548
if (!IsSyscall(op) && (opInfo & (IN_MEM | OUT_MEM)) != 0) {
1549
info.isDataAccess = true;
1550
info.dataSize = MIPSGetMemoryAccessSize(op);
1551
1552
u32 rs = cpu->GetRegValue(0, (int)MIPS_GET_RS(op));
1553
s16 imm16 = op & 0xFFFF;
1554
info.dataAddress = rs + imm16;
1555
1556
info.hasRelevantAddress = true;
1557
info.relevantAddress = info.dataAddress;
1558
}
1559
1560
return info;
1561
}
1562
}
1563
1564