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