Commit | Line | Data |
---|---|---|
ef79bbde P |
1 | /*************************************************************************** |
2 | * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team * | |
633d365e | 3 | * Copyright (C) 2023 notaz * |
ef79bbde P |
4 | * * |
5 | * This program is free software; you can redistribute it and/or modify * | |
6 | * it under the terms of the GNU General Public License as published by * | |
7 | * the Free Software Foundation; either version 2 of the License, or * | |
8 | * (at your option) any later version. * | |
9 | * * | |
10 | * This program is distributed in the hope that it will be useful, * | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
13 | * GNU General Public License for more details. * | |
14 | * * | |
15 | * You should have received a copy of the GNU General Public License * | |
16 | * along with this program; if not, write to the * | |
17 | * Free Software Foundation, Inc., * | |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA. * | |
19 | ***************************************************************************/ | |
20 | ||
21 | /* | |
22 | * PSX assembly interpreter. | |
23 | */ | |
24 | ||
25 | #include "psxcommon.h" | |
26 | #include "r3000a.h" | |
27 | #include "gte.h" | |
28 | #include "psxhle.h" | |
630b122b | 29 | #include "psxinterpreter.h" |
cab87bf3 | 30 | #include <stddef.h> |
630b122b | 31 | #include <assert.h> |
dec3c1d7 | 32 | #include "../include/compiler_features.h" |
33 | ||
34 | // these may cause issues: because of poor timing we may step | |
35 | // on instructions that real hardware would never reach | |
36 | #define DO_EXCEPTION_RESERVEDI | |
633d365e | 37 | #define DO_EXCEPTION_ALIGNMENT_BRANCH |
38 | //#define DO_EXCEPTION_ALIGNMENT_DATA | |
39 | #define HANDLE_LOAD_DELAY | |
ef79bbde P |
40 | |
41 | static int branch = 0; | |
42 | static int branch2 = 0; | |
ef79bbde | 43 | |
cab87bf3 | 44 | #ifdef __i386__ |
45 | #define INT_ATTR __attribute__((regparm(2))) | |
46 | #else | |
47 | #define INT_ATTR | |
48 | #endif | |
49 | #ifndef INVALID_PTR | |
50 | #define INVALID_PTR NULL | |
51 | #endif | |
52 | ||
ef79bbde | 53 | // Subsets |
cab87bf3 | 54 | static void (INT_ATTR *psxBSC[64])(psxRegisters *regs_, u32 code); |
55 | static void (INT_ATTR *psxSPC[64])(psxRegisters *regs_, u32 code); | |
56 | ||
633d365e | 57 | // load delay |
58 | static void doLoad(psxRegisters *regs, u32 r, u32 val) | |
59 | { | |
60 | #ifdef HANDLE_LOAD_DELAY | |
61 | int sel = regs->dloadSel ^ 1; | |
62 | assert(regs->dloadReg[sel] == 0); | |
63 | regs->dloadReg[sel] = r; | |
64 | regs->dloadVal[sel] = r ? val : 0; | |
65 | if (regs->dloadReg[sel ^ 1] == r) | |
66 | regs->dloadVal[sel ^ 1] = regs->dloadReg[sel ^ 1] = 0; | |
67 | #else | |
68 | regs->GPR.r[r] = r ? val : 0; | |
69 | #endif | |
70 | } | |
71 | ||
72 | static void dloadRt(psxRegisters *regs, u32 r, u32 val) | |
73 | { | |
74 | #ifdef HANDLE_LOAD_DELAY | |
75 | int sel = regs->dloadSel; | |
76 | if (unlikely(regs->dloadReg[sel] == r)) | |
77 | regs->dloadVal[sel] = regs->dloadReg[sel] = 0; | |
78 | #endif | |
79 | regs->GPR.r[r] = r ? val : 0; | |
80 | } | |
81 | ||
82 | static void dloadStep(psxRegisters *regs) | |
83 | { | |
84 | #ifdef HANDLE_LOAD_DELAY | |
85 | int sel = regs->dloadSel; | |
86 | regs->GPR.r[regs->dloadReg[sel]] = regs->dloadVal[sel]; | |
87 | regs->dloadVal[sel] = regs->dloadReg[sel] = 0; | |
88 | regs->dloadSel ^= 1; | |
89 | assert(regs->GPR.r[0] == 0); | |
90 | #endif | |
91 | } | |
92 | ||
93 | static void dloadFlush(psxRegisters *regs) | |
94 | { | |
95 | #ifdef HANDLE_LOAD_DELAY | |
96 | regs->GPR.r[regs->dloadReg[0]] = regs->dloadVal[0]; | |
97 | regs->GPR.r[regs->dloadReg[1]] = regs->dloadVal[1]; | |
98 | regs->dloadVal[0] = regs->dloadVal[1] = 0; | |
99 | regs->dloadReg[0] = regs->dloadReg[1] = 0; | |
100 | assert(regs->GPR.r[0] == 0); | |
101 | #endif | |
102 | } | |
103 | ||
104 | static void dloadClear(psxRegisters *regs) | |
105 | { | |
106 | #ifdef HANDLE_LOAD_DELAY | |
107 | regs->dloadVal[0] = regs->dloadVal[1] = 0; | |
108 | regs->dloadReg[0] = regs->dloadReg[1] = 0; | |
109 | regs->dloadSel = 0; | |
110 | #endif | |
111 | } | |
112 | ||
113 | static void intException(psxRegisters *regs, u32 pc, u32 cause) | |
114 | { | |
115 | dloadFlush(regs); | |
116 | regs->pc = pc; | |
117 | psxException(cause, branch, ®s->CP0); | |
118 | } | |
119 | ||
dec3c1d7 | 120 | // get an opcode without triggering exceptions or affecting cache |
121 | u32 intFakeFetch(u32 pc) | |
122 | { | |
123 | u8 *base = psxMemRLUT[pc >> 16]; | |
124 | u32 *code; | |
125 | if (unlikely(base == INVALID_PTR)) | |
126 | return 0; // nop | |
127 | code = (u32 *)(base + (pc & 0xfffc)); | |
128 | return SWAP32(*code); | |
129 | ||
130 | } | |
131 | ||
132 | static u32 INT_ATTR fetchNoCache(psxRegisters *regs, u8 **memRLUT, u32 pc) | |
630b122b | 133 | { |
cab87bf3 | 134 | u8 *base = memRLUT[pc >> 16]; |
dec3c1d7 | 135 | u32 *code; |
136 | if (unlikely(base == INVALID_PTR)) { | |
137 | SysPrintf("game crash @%08x, ra=%08x\n", pc, regs->GPR.n.ra); | |
633d365e | 138 | intException(regs, pc, R3000E_IBE << 2); |
dec3c1d7 | 139 | return 0; // execute as nop |
140 | } | |
141 | code = (u32 *)(base + (pc & 0xfffc)); | |
cab87bf3 | 142 | return SWAP32(*code); |
630b122b | 143 | } |
144 | ||
7a811716 | 145 | /* |
146 | Formula One 2001 : | |
147 | Use old CPU cache code when the RAM location is updated with new code (affects in-game racing) | |
148 | */ | |
630b122b | 149 | static struct cache_entry { |
150 | u32 tag; | |
151 | u32 data[4]; | |
152 | } ICache[256]; | |
7a811716 | 153 | |
dec3c1d7 | 154 | static u32 INT_ATTR fetchICache(psxRegisters *regs, u8 **memRLUT, u32 pc) |
630b122b | 155 | { |
156 | // cached? | |
157 | if (pc < 0xa0000000) | |
7a811716 | 158 | { |
630b122b | 159 | // this is not how the hardware works but whatever |
160 | struct cache_entry *entry = &ICache[(pc & 0xff0) >> 4]; | |
161 | ||
162 | if (((entry->tag ^ pc) & 0xfffffff0) != 0 || pc < entry->tag) | |
7a811716 | 163 | { |
cab87bf3 | 164 | const u8 *base = memRLUT[pc >> 16]; |
165 | const u32 *code; | |
dec3c1d7 | 166 | if (unlikely(base == INVALID_PTR)) { |
167 | SysPrintf("game crash @%08x, ra=%08x\n", pc, regs->GPR.n.ra); | |
633d365e | 168 | intException(regs, pc, R3000E_IBE << 2); |
dec3c1d7 | 169 | return 0; // execute as nop |
170 | } | |
cab87bf3 | 171 | code = (u32 *)(base + (pc & 0xfff0)); |
630b122b | 172 | |
173 | entry->tag = pc; | |
174 | // treat as 4 words, although other configurations are said to be possible | |
175 | switch (pc & 0x0c) | |
176 | { | |
177 | case 0x00: entry->data[0] = SWAP32(code[0]); | |
178 | case 0x04: entry->data[1] = SWAP32(code[1]); | |
179 | case 0x08: entry->data[2] = SWAP32(code[2]); | |
180 | case 0x0c: entry->data[3] = SWAP32(code[3]); | |
181 | } | |
7a811716 | 182 | } |
630b122b | 183 | return entry->data[(pc & 0x0f) >> 2]; |
7a811716 | 184 | } |
185 | ||
dec3c1d7 | 186 | return fetchNoCache(regs, memRLUT, pc); |
7a811716 | 187 | } |
630b122b | 188 | |
dec3c1d7 | 189 | static u32 (INT_ATTR *fetch)(psxRegisters *regs_, u8 **memRLUT, u32 pc) = fetchNoCache; |
7a811716 | 190 | |
1562ed57 | 191 | // Make the timing events trigger faster as we are currently assuming everything |
192 | // takes one cycle, which is not the case on real hardware. | |
193 | // FIXME: count cache misses, memory latencies, stalls to get rid of this | |
194 | static inline void addCycle(void) | |
195 | { | |
196 | assert(psxRegs.subCycleStep >= 0x10000); | |
197 | psxRegs.subCycle += psxRegs.subCycleStep; | |
198 | psxRegs.cycle += psxRegs.subCycle >> 16; | |
199 | psxRegs.subCycle &= 0xffff; | |
200 | } | |
201 | ||
cab87bf3 | 202 | /**** R3000A Instruction Macros ****/ |
203 | #define _PC_ regs_->pc // The next PC to be executed | |
204 | ||
205 | #define _fOp_(code) ((code >> 26) ) // The opcode part of the instruction register | |
206 | #define _fFunct_(code) ((code ) & 0x3F) // The funct part of the instruction register | |
207 | #define _fRd_(code) ((code >> 11) & 0x1F) // The rd part of the instruction register | |
208 | #define _fRt_(code) ((code >> 16) & 0x1F) // The rt part of the instruction register | |
209 | #define _fRs_(code) ((code >> 21) & 0x1F) // The rs part of the instruction register | |
210 | #define _fSa_(code) ((code >> 6) & 0x1F) // The sa part of the instruction register | |
211 | #define _fIm_(code) ((u16)code) // The immediate part of the instruction register | |
212 | #define _fTarget_(code) (code & 0x03ffffff) // The target part of the instruction register | |
213 | ||
214 | #define _fImm_(code) ((s16)code) // sign-extended immediate | |
215 | #define _fImmU_(code) (code&0xffff) // zero-extended immediate | |
216 | ||
217 | #define _Op_ _fOp_(code) | |
218 | #define _Funct_ _fFunct_(code) | |
219 | #define _Rd_ _fRd_(code) | |
220 | #define _Rt_ _fRt_(code) | |
221 | #define _Rs_ _fRs_(code) | |
222 | #define _Sa_ _fSa_(code) | |
223 | #define _Im_ _fIm_(code) | |
224 | #define _Target_ _fTarget_(code) | |
225 | ||
226 | #define _Imm_ _fImm_(code) | |
227 | #define _ImmU_ _fImmU_(code) | |
228 | ||
229 | #define _rRs_ regs_->GPR.r[_Rs_] // Rs register | |
230 | #define _rRt_ regs_->GPR.r[_Rt_] // Rt register | |
cab87bf3 | 231 | #define _rSa_ regs_->GPR.r[_Sa_] // Sa register |
cab87bf3 | 232 | |
233 | #define _rHi_ regs_->GPR.n.hi // The HI register | |
234 | #define _rLo_ regs_->GPR.n.lo // The LO register | |
235 | ||
236 | #define _JumpTarget_ ((_Target_ * 4) + (_PC_ & 0xf0000000)) // Calculates the target during a jump instruction | |
237 | #define _BranchTarget_ ((s16)_Im_ * 4 + _PC_) // Calculates the target during a branch instruction | |
238 | ||
633d365e | 239 | #define _SetLink(x) dloadRt(regs_, x, _PC_ + 4); // Sets the return address in the link register |
cab87bf3 | 240 | |
241 | #define OP(name) \ | |
242 | static inline INT_ATTR void name(psxRegisters *regs_, u32 code) | |
243 | ||
ef79bbde P |
244 | // this defines shall be used with the tmp |
245 | // of the next func (instead of _Funct_...) | |
246 | #define _tFunct_ ((tmp ) & 0x3F) // The funct part of the instruction register | |
247 | #define _tRd_ ((tmp >> 11) & 0x1F) // The rd part of the instruction register | |
248 | #define _tRt_ ((tmp >> 16) & 0x1F) // The rt part of the instruction register | |
249 | #define _tRs_ ((tmp >> 21) & 0x1F) // The rs part of the instruction register | |
250 | #define _tSa_ ((tmp >> 6) & 0x1F) // The sa part of the instruction register | |
251 | ||
cab87bf3 | 252 | #define _i32(x) (s32)(x) |
253 | #define _u32(x) (u32)(x) | |
254 | ||
0f15c5bc | 255 | #define isBranch(c_) \ |
256 | ((1 <= ((c_) >> 26) && ((c_) >> 26) <= 7) || ((c_) & 0xfc00003e) == 8) | |
257 | #define swap_(a_, b_) { u32 t_ = a_; a_ = b_; b_ = t_; } | |
990cb018 | 258 | |
0f15c5bc | 259 | // tar1 is main branch target, 'code' is opcode in DS |
260 | static u32 psxBranchNoDelay(psxRegisters *regs_, u32 tar1, u32 code, int *taken) { | |
261 | u32 temp, rt; | |
262 | ||
263 | assert(isBranch(code)); | |
264 | *taken = 1; | |
265 | switch (code >> 26) { | |
990cb018 | 266 | case 0x00: // SPECIAL |
267 | switch (_Funct_) { | |
268 | case 0x08: // JR | |
269 | return _u32(_rRs_); | |
270 | case 0x09: // JALR | |
271 | temp = _u32(_rRs_); | |
0f15c5bc | 272 | if (_Rd_) |
273 | regs_->GPR.r[_Rd_] = tar1 + 4; | |
990cb018 | 274 | return temp; |
275 | } | |
276 | break; | |
277 | case 0x01: // REGIMM | |
0f15c5bc | 278 | rt = _Rt_; |
279 | switch (rt) { | |
280 | case 0x10: // BLTZAL | |
281 | regs_->GPR.n.ra = tar1 + 4; | |
990cb018 | 282 | if (_i32(_rRs_) < 0) |
0f15c5bc | 283 | return tar1 + (s16)_Im_ * 4; |
990cb018 | 284 | break; |
0f15c5bc | 285 | case 0x11: // BGEZAL |
286 | regs_->GPR.n.ra = tar1 + 4; | |
990cb018 | 287 | if (_i32(_rRs_) >= 0) |
0f15c5bc | 288 | return tar1 + (s16)_Im_ * 4; |
990cb018 | 289 | break; |
0f15c5bc | 290 | default: |
291 | if (rt & 1) { // BGEZ | |
292 | if (_i32(_rRs_) >= 0) | |
293 | return tar1 + (s16)_Im_ * 4; | |
990cb018 | 294 | } |
0f15c5bc | 295 | else { // BLTZ |
296 | if (_i32(_rRs_) < 0) | |
297 | return tar1 + (s16)_Im_ * 4; | |
990cb018 | 298 | } |
299 | break; | |
300 | } | |
301 | break; | |
302 | case 0x02: // J | |
0f15c5bc | 303 | return (tar1 & 0xf0000000u) + _Target_ * 4; |
990cb018 | 304 | case 0x03: // JAL |
0f15c5bc | 305 | regs_->GPR.n.ra = tar1 + 4; |
306 | return (tar1 & 0xf0000000u) + _Target_ * 4; | |
990cb018 | 307 | case 0x04: // BEQ |
308 | if (_i32(_rRs_) == _i32(_rRt_)) | |
0f15c5bc | 309 | return tar1 + (s16)_Im_ * 4; |
990cb018 | 310 | break; |
311 | case 0x05: // BNE | |
312 | if (_i32(_rRs_) != _i32(_rRt_)) | |
0f15c5bc | 313 | return tar1 + (s16)_Im_ * 4; |
990cb018 | 314 | break; |
315 | case 0x06: // BLEZ | |
316 | if (_i32(_rRs_) <= 0) | |
0f15c5bc | 317 | return tar1 + (s16)_Im_ * 4; |
990cb018 | 318 | break; |
319 | case 0x07: // BGTZ | |
320 | if (_i32(_rRs_) > 0) | |
0f15c5bc | 321 | return tar1 + (s16)_Im_ * 4; |
990cb018 | 322 | break; |
323 | } | |
324 | ||
0f15c5bc | 325 | *taken = 0; |
326 | return tar1; | |
990cb018 | 327 | } |
328 | ||
0f15c5bc | 329 | static void psxDoDelayBranch(psxRegisters *regs, u32 tar1, u32 code1) { |
330 | u32 tar2, code; | |
331 | int taken, lim; | |
990cb018 | 332 | |
0f15c5bc | 333 | tar2 = psxBranchNoDelay(regs, tar1, code1, &taken); |
334 | regs->pc = tar1; | |
335 | if (!taken) | |
336 | return; | |
990cb018 | 337 | |
990cb018 | 338 | /* |
0f15c5bc | 339 | * taken branch in delay slot: |
990cb018 | 340 | * - execute 1 instruction at tar1 |
341 | * - jump to tar2 (target of branch in delay slot; this branch | |
342 | * has no normal delay slot, instruction at tar1 was fetched instead) | |
343 | */ | |
0f15c5bc | 344 | for (lim = 0; lim < 8; lim++) { |
345 | regs->code = code = fetch(regs, psxMemRLUT, tar1); | |
346 | addCycle(); | |
347 | if (likely(!isBranch(code))) { | |
633d365e | 348 | dloadStep(regs); |
0f15c5bc | 349 | psxBSC[code >> 26](regs, code); |
350 | regs->pc = tar2; | |
351 | return; | |
352 | } | |
353 | tar1 = psxBranchNoDelay(regs, tar2, code, &taken); | |
354 | regs->pc = tar2; | |
355 | if (!taken) | |
356 | return; | |
357 | swap_(tar1, tar2); | |
990cb018 | 358 | } |
0f15c5bc | 359 | SysPrintf("Evil chained DS branches @ %08x %08x %08x\n", regs->pc, tar1, tar2); |
990cb018 | 360 | } |
361 | ||
0f15c5bc | 362 | static void doBranch(psxRegisters *regs, u32 tar) { |
633d365e | 363 | u32 code, pc; |
ef79bbde P |
364 | |
365 | branch2 = branch = 1; | |
990cb018 | 366 | |
0f15c5bc | 367 | // fetch the delay slot |
368 | pc = regs->pc; | |
369 | regs->pc = pc + 4; | |
370 | regs->code = code = fetch(regs, psxMemRLUT, pc); | |
dec3c1d7 | 371 | |
1562ed57 | 372 | addCycle(); |
ef79bbde | 373 | |
0f15c5bc | 374 | // check for branch in delay slot |
375 | if (unlikely(isBranch(code))) { | |
376 | psxDoDelayBranch(regs, tar, code); | |
377 | log_unhandled("branch in DS: %08x->%08x\n", pc, regs->pc); | |
378 | branch = 0; | |
379 | psxBranchTest(); | |
380 | return; | |
381 | } | |
382 | ||
633d365e | 383 | dloadStep(regs); |
0f15c5bc | 384 | psxBSC[code >> 26](regs, code); |
ef79bbde P |
385 | |
386 | branch = 0; | |
0f15c5bc | 387 | regs->pc = tar; |
ef79bbde P |
388 | |
389 | psxBranchTest(); | |
390 | } | |
391 | ||
0f15c5bc | 392 | static void doBranchReg(psxRegisters *regs, u32 tar) { |
633d365e | 393 | #ifdef DO_EXCEPTION_ALIGNMENT_BRANCH |
dec3c1d7 | 394 | if (unlikely(tar & 3)) { |
633d365e | 395 | SysPrintf("game crash @%08x, ra=%08x\n", tar, regs->GPR.n.ra); |
396 | psxRegs.CP0.n.BadVAddr = tar; | |
397 | intException(regs, tar, R3000E_AdEL << 2); | |
dec3c1d7 | 398 | return; |
399 | } | |
400 | #else | |
401 | tar &= ~3; | |
402 | #endif | |
0f15c5bc | 403 | doBranch(regs, tar); |
dec3c1d7 | 404 | } |
405 | ||
406 | #if __has_builtin(__builtin_add_overflow) || (defined(__GNUC__) && __GNUC__ >= 5) | |
407 | #define add_overflow(a, b, r) __builtin_add_overflow(a, b, &(r)) | |
408 | #define sub_overflow(a, b, r) __builtin_sub_overflow(a, b, &(r)) | |
409 | #else | |
410 | #define add_overflow(a, b, r) ({r = (u32)a + (u32)b; (a ^ ~b) & (a ^ r) & (1u<<31);}) | |
411 | #define sub_overflow(a, b, r) ({r = (u32)a - (u32)b; (a ^ b) & (a ^ r) & (1u<<31);}) | |
412 | #endif | |
413 | ||
414 | static void addExc(psxRegisters *regs, u32 rt, s32 a1, s32 a2) { | |
633d365e | 415 | s32 val; |
416 | if (add_overflow(a1, a2, val)) { | |
417 | //printf("ov %08x + %08x = %08x\n", a1, a2, val); | |
418 | intException(regs, regs->pc - 4, R3000E_Ov << 2); | |
dec3c1d7 | 419 | return; |
420 | } | |
633d365e | 421 | dloadRt(regs, rt, val); |
dec3c1d7 | 422 | } |
423 | ||
424 | static void subExc(psxRegisters *regs, u32 rt, s32 a1, s32 a2) { | |
633d365e | 425 | s32 val; |
426 | if (sub_overflow(a1, a2, val)) { | |
427 | intException(regs, regs->pc - 4, R3000E_Ov << 2); | |
dec3c1d7 | 428 | return; |
429 | } | |
633d365e | 430 | dloadRt(regs, rt, val); |
dec3c1d7 | 431 | } |
432 | ||
ef79bbde P |
433 | /********************************************************* |
434 | * Arithmetic with immediate operand * | |
435 | * Format: OP rt, rs, immediate * | |
436 | *********************************************************/ | |
633d365e | 437 | OP(psxADDI) { addExc (regs_, _Rt_, _i32(_rRs_), _Imm_); } // Rt = Rs + Im (Exception on Integer Overflow) |
438 | OP(psxADDIU) { dloadRt(regs_, _Rt_, _u32(_rRs_) + _Imm_ ); } // Rt = Rs + Im | |
439 | OP(psxANDI) { dloadRt(regs_, _Rt_, _u32(_rRs_) & _ImmU_); } // Rt = Rs And Im | |
440 | OP(psxORI) { dloadRt(regs_, _Rt_, _u32(_rRs_) | _ImmU_); } // Rt = Rs Or Im | |
441 | OP(psxXORI) { dloadRt(regs_, _Rt_, _u32(_rRs_) ^ _ImmU_); } // Rt = Rs Xor Im | |
442 | OP(psxSLTI) { dloadRt(regs_, _Rt_, _i32(_rRs_) < _Imm_ ); } // Rt = Rs < Im (Signed) | |
443 | OP(psxSLTIU) { dloadRt(regs_, _Rt_, _u32(_rRs_) < ((u32)_Imm_)); } // Rt = Rs < Im (Unsigned) | |
ef79bbde P |
444 | |
445 | /********************************************************* | |
446 | * Register arithmetic * | |
447 | * Format: OP rd, rs, rt * | |
448 | *********************************************************/ | |
633d365e | 449 | OP(psxADD) { addExc (regs_, _Rd_, _i32(_rRs_), _i32(_rRt_)); } // Rd = Rs + Rt (Exception on Integer Overflow) |
450 | OP(psxSUB) { subExc (regs_, _Rd_, _i32(_rRs_), _i32(_rRt_)); } // Rd = Rs - Rt (Exception on Integer Overflow) | |
451 | OP(psxADDU) { dloadRt(regs_, _Rd_, _u32(_rRs_) + _u32(_rRt_)); } // Rd = Rs + Rt | |
452 | OP(psxSUBU) { dloadRt(regs_, _Rd_, _u32(_rRs_) - _u32(_rRt_)); } // Rd = Rs - Rt | |
453 | OP(psxAND) { dloadRt(regs_, _Rd_, _u32(_rRs_) & _u32(_rRt_)); } // Rd = Rs And Rt | |
454 | OP(psxOR) { dloadRt(regs_, _Rd_, _u32(_rRs_) | _u32(_rRt_)); } // Rd = Rs Or Rt | |
455 | OP(psxXOR) { dloadRt(regs_, _Rd_, _u32(_rRs_) ^ _u32(_rRt_)); } // Rd = Rs Xor Rt | |
456 | OP(psxNOR) { dloadRt(regs_, _Rd_, ~_u32(_rRs_ | _u32(_rRt_))); } // Rd = Rs Nor Rt | |
457 | OP(psxSLT) { dloadRt(regs_, _Rd_, _i32(_rRs_) < _i32(_rRt_)); } // Rd = Rs < Rt (Signed) | |
458 | OP(psxSLTU) { dloadRt(regs_, _Rd_, _u32(_rRs_) < _u32(_rRt_)); } // Rd = Rs < Rt (Unsigned) | |
ef79bbde P |
459 | |
460 | /********************************************************* | |
461 | * Register mult/div & Register trap logic * | |
462 | * Format: OP rs, rt * | |
463 | *********************************************************/ | |
cab87bf3 | 464 | OP(psxDIV) { |
465 | if (!_rRt_) { | |
466 | _rHi_ = _rRs_; | |
467 | if (_rRs_ & 0x80000000) { | |
468 | _rLo_ = 1; | |
469 | } else { | |
470 | _rLo_ = 0xFFFFFFFF; | |
471 | } | |
472 | } | |
630b122b | 473 | #if !defined(__arm__) && !defined(__aarch64__) |
cab87bf3 | 474 | else if (_rRs_ == 0x80000000 && _rRt_ == 0xFFFFFFFF) { |
475 | _rLo_ = 0x80000000; | |
476 | _rHi_ = 0; | |
477 | } | |
630b122b | 478 | #endif |
cab87bf3 | 479 | else { |
480 | _rLo_ = _i32(_rRs_) / _i32(_rRt_); | |
481 | _rHi_ = _i32(_rRs_) % _i32(_rRt_); | |
482 | } | |
ef79bbde P |
483 | } |
484 | ||
cab87bf3 | 485 | OP(psxDIV_stall) { |
486 | regs_->muldivBusyCycle = regs_->cycle + 37; | |
487 | psxDIV(regs_, code); | |
630b122b | 488 | } |
489 | ||
cab87bf3 | 490 | OP(psxDIVU) { |
ef79bbde P |
491 | if (_rRt_ != 0) { |
492 | _rLo_ = _rRs_ / _rRt_; | |
493 | _rHi_ = _rRs_ % _rRt_; | |
494 | } | |
c7a56f4f | 495 | else { |
cab87bf3 | 496 | _rLo_ = 0xffffffff; |
497 | _rHi_ = _rRs_; | |
c7a56f4f | 498 | } |
ef79bbde P |
499 | } |
500 | ||
cab87bf3 | 501 | OP(psxDIVU_stall) { |
502 | regs_->muldivBusyCycle = regs_->cycle + 37; | |
503 | psxDIVU(regs_, code); | |
630b122b | 504 | } |
505 | ||
cab87bf3 | 506 | OP(psxMULT) { |
507 | u64 res = (s64)_i32(_rRs_) * _i32(_rRt_); | |
ef79bbde | 508 | |
cab87bf3 | 509 | regs_->GPR.n.lo = (u32)res; |
510 | regs_->GPR.n.hi = (u32)(res >> 32); | |
ef79bbde P |
511 | } |
512 | ||
cab87bf3 | 513 | OP(psxMULT_stall) { |
630b122b | 514 | // approximate, but maybe good enough |
515 | u32 rs = _rRs_; | |
516 | u32 lz = __builtin_clz(((rs ^ ((s32)rs >> 21)) | 1)); | |
517 | u32 c = 7 + (2 - (lz / 11)) * 4; | |
cab87bf3 | 518 | regs_->muldivBusyCycle = regs_->cycle + c; |
519 | psxMULT(regs_, code); | |
630b122b | 520 | } |
521 | ||
cab87bf3 | 522 | OP(psxMULTU) { |
523 | u64 res = (u64)_u32(_rRs_) * _u32(_rRt_); | |
ef79bbde | 524 | |
cab87bf3 | 525 | regs_->GPR.n.lo = (u32)(res & 0xffffffff); |
526 | regs_->GPR.n.hi = (u32)((res >> 32) & 0xffffffff); | |
ef79bbde P |
527 | } |
528 | ||
cab87bf3 | 529 | OP(psxMULTU_stall) { |
630b122b | 530 | // approximate, but maybe good enough |
531 | u32 lz = __builtin_clz(_rRs_ | 1); | |
532 | u32 c = 7 + (2 - (lz / 11)) * 4; | |
cab87bf3 | 533 | regs_->muldivBusyCycle = regs_->cycle + c; |
534 | psxMULTU(regs_, code); | |
630b122b | 535 | } |
536 | ||
ef79bbde P |
537 | /********************************************************* |
538 | * Register branch logic * | |
539 | * Format: OP rs, offset * | |
540 | *********************************************************/ | |
424e6d52 | 541 | #define RepZBranchi32(op) \ |
542 | if(_i32(_rRs_) op 0) \ | |
0f15c5bc | 543 | doBranch(regs_, _BranchTarget_); |
424e6d52 | 544 | #define RepZBranchLinki32(op) { \ |
545 | s32 temp = _i32(_rRs_); \ | |
546 | _SetLink(31); \ | |
547 | if(temp op 0) \ | |
0f15c5bc | 548 | doBranch(regs_, _BranchTarget_); \ |
424e6d52 | 549 | } |
ef79bbde | 550 | |
cab87bf3 | 551 | OP(psxBGEZ) { RepZBranchi32(>=) } // Branch if Rs >= 0 |
552 | OP(psxBGEZAL) { RepZBranchLinki32(>=) } // Branch if Rs >= 0 and link | |
553 | OP(psxBGTZ) { RepZBranchi32(>) } // Branch if Rs > 0 | |
554 | OP(psxBLEZ) { RepZBranchi32(<=) } // Branch if Rs <= 0 | |
555 | OP(psxBLTZ) { RepZBranchi32(<) } // Branch if Rs < 0 | |
556 | OP(psxBLTZAL) { RepZBranchLinki32(<) } // Branch if Rs < 0 and link | |
ef79bbde P |
557 | |
558 | /********************************************************* | |
559 | * Shift arithmetic with constant shift * | |
560 | * Format: OP rd, rt, sa * | |
561 | *********************************************************/ | |
633d365e | 562 | OP(psxSLL) { dloadRt(regs_, _Rd_, _u32(_rRt_) << _Sa_); } // Rd = Rt << sa |
563 | OP(psxSRA) { dloadRt(regs_, _Rd_, _i32(_rRt_) >> _Sa_); } // Rd = Rt >> sa (arithmetic) | |
564 | OP(psxSRL) { dloadRt(regs_, _Rd_, _u32(_rRt_) >> _Sa_); } // Rd = Rt >> sa (logical) | |
ef79bbde P |
565 | |
566 | /********************************************************* | |
567 | * Shift arithmetic with variant register shift * | |
568 | * Format: OP rd, rt, rs * | |
569 | *********************************************************/ | |
633d365e | 570 | OP(psxSLLV) { dloadRt(regs_, _Rd_, _u32(_rRt_) << (_u32(_rRs_) & 0x1F)); } // Rd = Rt << rs |
571 | OP(psxSRAV) { dloadRt(regs_, _Rd_, _i32(_rRt_) >> (_u32(_rRs_) & 0x1F)); } // Rd = Rt >> rs (arithmetic) | |
572 | OP(psxSRLV) { dloadRt(regs_, _Rd_, _u32(_rRt_) >> (_u32(_rRs_) & 0x1F)); } // Rd = Rt >> rs (logical) | |
ef79bbde P |
573 | |
574 | /********************************************************* | |
575 | * Load higher 16 bits of the first word in GPR with imm * | |
576 | * Format: OP rt, immediate * | |
577 | *********************************************************/ | |
633d365e | 578 | OP(psxLUI) { dloadRt(regs_, _Rt_, code << 16); } // Upper halfword of Rt = Im |
ef79bbde P |
579 | |
580 | /********************************************************* | |
581 | * Move from HI/LO to GPR * | |
582 | * Format: OP rd * | |
583 | *********************************************************/ | |
633d365e | 584 | OP(psxMFHI) { dloadRt(regs_, _Rd_, _rHi_); } // Rd = Hi |
585 | OP(psxMFLO) { dloadRt(regs_, _Rd_, _rLo_); } // Rd = Lo | |
ef79bbde | 586 | |
cab87bf3 | 587 | static void mflohiCheckStall(psxRegisters *regs_) |
630b122b | 588 | { |
cab87bf3 | 589 | u32 left = regs_->muldivBusyCycle - regs_->cycle; |
630b122b | 590 | if (left <= 37) { |
591 | //printf("muldiv stall %u\n", left); | |
cab87bf3 | 592 | regs_->cycle = regs_->muldivBusyCycle; |
630b122b | 593 | } |
594 | } | |
595 | ||
cab87bf3 | 596 | OP(psxMFHI_stall) { mflohiCheckStall(regs_); psxMFHI(regs_, code); } |
597 | OP(psxMFLO_stall) { mflohiCheckStall(regs_); psxMFLO(regs_, code); } | |
630b122b | 598 | |
ef79bbde P |
599 | /********************************************************* |
600 | * Move to GPR to HI/LO & Register jump * | |
601 | * Format: OP rs * | |
602 | *********************************************************/ | |
cab87bf3 | 603 | OP(psxMTHI) { _rHi_ = _rRs_; } // Hi = Rs |
604 | OP(psxMTLO) { _rLo_ = _rRs_; } // Lo = Rs | |
ef79bbde P |
605 | |
606 | /********************************************************* | |
607 | * Special purpose instructions * | |
608 | * Format: OP * | |
609 | *********************************************************/ | |
cab87bf3 | 610 | OP(psxBREAK) { |
633d365e | 611 | intException(regs_, regs_->pc - 4, R3000E_Bp << 2); |
ef79bbde P |
612 | } |
613 | ||
cab87bf3 | 614 | OP(psxSYSCALL) { |
633d365e | 615 | intException(regs_, regs_->pc - 4, R3000E_Syscall << 2); |
ef79bbde P |
616 | } |
617 | ||
dec3c1d7 | 618 | static inline void execI_(u8 **memRLUT, psxRegisters *regs_); |
619 | ||
620 | static inline void psxTestSWInts(psxRegisters *regs_, int step) { | |
cab87bf3 | 621 | if (regs_->CP0.n.Cause & regs_->CP0.n.Status & 0x0300 && |
622 | regs_->CP0.n.Status & 0x1) { | |
dec3c1d7 | 623 | if (step) |
624 | execI_(psxMemRLUT, regs_); | |
cab87bf3 | 625 | regs_->CP0.n.Cause &= ~0x7c; |
633d365e | 626 | intException(regs_, regs_->pc, regs_->CP0.n.Cause); |
cab87bf3 | 627 | } |
628 | } | |
629 | ||
630 | OP(psxRFE) { | |
ef79bbde | 631 | // SysPrintf("psxRFE\n"); |
cab87bf3 | 632 | regs_->CP0.n.Status = (regs_->CP0.n.Status & 0xfffffff0) | |
633 | ((regs_->CP0.n.Status & 0x3c) >> 2); | |
dec3c1d7 | 634 | psxTestSWInts(regs_, 0); |
ef79bbde P |
635 | } |
636 | ||
637 | /********************************************************* | |
638 | * Register branch logic * | |
639 | * Format: OP rs, rt, offset * | |
640 | *********************************************************/ | |
0f15c5bc | 641 | #define RepBranchi32(op) { \ |
642 | if (_i32(_rRs_) op _i32(_rRt_)) \ | |
643 | doBranch(regs_, _BranchTarget_); \ | |
644 | } | |
ef79bbde | 645 | |
cab87bf3 | 646 | OP(psxBEQ) { RepBranchi32(==) } // Branch if Rs == Rt |
647 | OP(psxBNE) { RepBranchi32(!=) } // Branch if Rs != Rt | |
ef79bbde P |
648 | |
649 | /********************************************************* | |
650 | * Jump to target * | |
651 | * Format: OP target * | |
652 | *********************************************************/ | |
0f15c5bc | 653 | OP(psxJ) { doBranch(regs_, _JumpTarget_); } |
654 | OP(psxJAL) { _SetLink(31); doBranch(regs_, _JumpTarget_); } | |
ef79bbde P |
655 | |
656 | /********************************************************* | |
657 | * Register jump * | |
658 | * Format: OP rs, rd * | |
659 | *********************************************************/ | |
cab87bf3 | 660 | OP(psxJR) { |
0f15c5bc | 661 | doBranchReg(regs_, _rRs_); |
ef79bbde P |
662 | psxJumpTest(); |
663 | } | |
664 | ||
cab87bf3 | 665 | OP(psxJALR) { |
ef79bbde P |
666 | u32 temp = _u32(_rRs_); |
667 | if (_Rd_) { _SetLink(_Rd_); } | |
0f15c5bc | 668 | doBranchReg(regs_, temp); |
ef79bbde P |
669 | } |
670 | ||
671 | /********************************************************* | |
672 | * Load and store for GPR * | |
673 | * Format: OP rt, offset(base) * | |
674 | *********************************************************/ | |
675 | ||
633d365e | 676 | static int algnChkL(psxRegisters *regs, u32 addr, u32 m) { |
677 | if (unlikely(addr & m)) { | |
678 | log_unhandled("unaligned load %08x @%08x\n", addr, regs->pc - 4); | |
679 | #ifdef DO_EXCEPTION_ALIGNMENT_DATA | |
680 | psxRegs.CP0.n.BadVAddr = addr; | |
681 | intException(regs, regs->pc - 4, R3000E_AdEL << 2); | |
682 | return 0; | |
683 | #endif | |
684 | } | |
685 | return 1; | |
686 | } | |
687 | ||
688 | static int algnChkS(psxRegisters *regs, u32 addr, u32 m) { | |
689 | if (unlikely(addr & m)) { | |
690 | log_unhandled("unaligned store %08x @%08x\n", addr, regs->pc - 4); | |
691 | #ifdef DO_EXCEPTION_ALIGNMENT_DATA | |
692 | psxRegs.CP0.n.BadVAddr = addr; | |
693 | intException(regs, regs->pc - 4, R3000E_AdES << 2); | |
694 | return 0; | |
695 | #endif | |
696 | } | |
697 | return 1; | |
698 | } | |
699 | ||
700 | /********************************************************* | |
701 | * Load and store for GPR * | |
702 | * Format: OP rt, offset(base) * | |
703 | *********************************************************/ | |
704 | ||
cab87bf3 | 705 | #define _oB_ (regs_->GPR.r[_Rs_] + _Imm_) |
ef79bbde | 706 | |
633d365e | 707 | OP(psxLB) { doLoad(regs_, _Rt_, (s8)psxMemRead8(_oB_)); } |
708 | OP(psxLBU) { doLoad(regs_, _Rt_, psxMemRead8(_oB_)); } | |
709 | OP(psxLH) { if (algnChkL(regs_, _oB_, 1)) doLoad(regs_, _Rt_, (s16)psxMemRead16(_oB_)); } | |
710 | OP(psxLHU) { if (algnChkL(regs_, _oB_, 1)) doLoad(regs_, _Rt_, psxMemRead16(_oB_)); } | |
711 | OP(psxLW) { if (algnChkL(regs_, _oB_, 3)) doLoad(regs_, _Rt_, psxMemRead32(_oB_)); } | |
ef79bbde | 712 | |
cab87bf3 | 713 | OP(psxLWL) { |
714 | static const u32 LWL_MASK[4] = { 0xffffff, 0xffff, 0xff, 0 }; | |
715 | static const u32 LWL_SHIFT[4] = { 24, 16, 8, 0 }; | |
633d365e | 716 | u32 addr = _oB_, val; |
ef79bbde P |
717 | u32 shift = addr & 3; |
718 | u32 mem = psxMemRead32(addr & ~3); | |
633d365e | 719 | u32 rt = _Rt_; |
720 | u32 oldval = regs_->GPR.r[rt]; | |
ef79bbde | 721 | |
633d365e | 722 | #ifdef HANDLE_LOAD_DELAY |
723 | int sel = regs_->dloadSel; | |
724 | if (regs_->dloadReg[sel] == rt) | |
725 | oldval = regs_->dloadVal[sel]; | |
726 | #endif | |
727 | val = (oldval & LWL_MASK[shift]) | (mem << LWL_SHIFT[shift]); | |
728 | doLoad(regs_, rt, val); | |
ef79bbde P |
729 | |
730 | /* | |
731 | Mem = 1234. Reg = abcd | |
732 | ||
733 | 0 4bcd (mem << 24) | (reg & 0x00ffffff) | |
734 | 1 34cd (mem << 16) | (reg & 0x0000ffff) | |
735 | 2 234d (mem << 8) | (reg & 0x000000ff) | |
736 | 3 1234 (mem ) | (reg & 0x00000000) | |
737 | */ | |
738 | } | |
739 | ||
cab87bf3 | 740 | OP(psxLWR) { |
741 | static const u32 LWR_MASK[4] = { 0, 0xff000000, 0xffff0000, 0xffffff00 }; | |
742 | static const u32 LWR_SHIFT[4] = { 0, 8, 16, 24 }; | |
633d365e | 743 | u32 addr = _oB_, val; |
ef79bbde P |
744 | u32 shift = addr & 3; |
745 | u32 mem = psxMemRead32(addr & ~3); | |
633d365e | 746 | u32 rt = _Rt_; |
747 | u32 oldval = regs_->GPR.r[rt]; | |
ef79bbde | 748 | |
633d365e | 749 | #ifdef HANDLE_LOAD_DELAY |
750 | int sel = regs_->dloadSel; | |
751 | if (regs_->dloadReg[sel] == rt) | |
752 | oldval = regs_->dloadVal[sel]; | |
753 | #endif | |
754 | val = (oldval & LWR_MASK[shift]) | (mem >> LWR_SHIFT[shift]); | |
755 | doLoad(regs_, rt, val); | |
ef79bbde P |
756 | |
757 | /* | |
758 | Mem = 1234. Reg = abcd | |
759 | ||
760 | 0 1234 (mem ) | (reg & 0x00000000) | |
761 | 1 a123 (mem >> 8) | (reg & 0xff000000) | |
762 | 2 ab12 (mem >> 16) | (reg & 0xffff0000) | |
763 | 3 abc1 (mem >> 24) | (reg & 0xffffff00) | |
764 | */ | |
765 | } | |
766 | ||
633d365e | 767 | OP(psxSB) { psxMemWrite8 (_oB_, _rRt_ & 0xff); } |
768 | OP(psxSH) { if (algnChkS(regs_, _oB_, 1)) psxMemWrite16(_oB_, _rRt_ & 0xffff); } | |
769 | OP(psxSW) { if (algnChkS(regs_, _oB_, 3)) psxMemWrite32(_oB_, _rRt_); } | |
ef79bbde | 770 | |
633d365e | 771 | // FIXME: this rmw implementation is wrong and would break on io like fifos |
cab87bf3 | 772 | OP(psxSWL) { |
773 | static const u32 SWL_MASK[4] = { 0xffffff00, 0xffff0000, 0xff000000, 0 }; | |
774 | static const u32 SWL_SHIFT[4] = { 24, 16, 8, 0 }; | |
ef79bbde P |
775 | u32 addr = _oB_; |
776 | u32 shift = addr & 3; | |
777 | u32 mem = psxMemRead32(addr & ~3); | |
778 | ||
779 | psxMemWrite32(addr & ~3, (_u32(_rRt_) >> SWL_SHIFT[shift]) | | |
780 | ( mem & SWL_MASK[shift]) ); | |
781 | /* | |
782 | Mem = 1234. Reg = abcd | |
783 | ||
784 | 0 123a (reg >> 24) | (mem & 0xffffff00) | |
785 | 1 12ab (reg >> 16) | (mem & 0xffff0000) | |
786 | 2 1abc (reg >> 8) | (mem & 0xff000000) | |
787 | 3 abcd (reg ) | (mem & 0x00000000) | |
788 | */ | |
789 | } | |
790 | ||
cab87bf3 | 791 | OP(psxSWR) { |
792 | static const u32 SWR_MASK[4] = { 0, 0xff, 0xffff, 0xffffff }; | |
793 | static const u32 SWR_SHIFT[4] = { 0, 8, 16, 24 }; | |
ef79bbde P |
794 | u32 addr = _oB_; |
795 | u32 shift = addr & 3; | |
796 | u32 mem = psxMemRead32(addr & ~3); | |
797 | ||
798 | psxMemWrite32(addr & ~3, (_u32(_rRt_) << SWR_SHIFT[shift]) | | |
799 | ( mem & SWR_MASK[shift]) ); | |
800 | ||
801 | /* | |
802 | Mem = 1234. Reg = abcd | |
803 | ||
804 | 0 abcd (reg ) | (mem & 0x00000000) | |
805 | 1 bcd4 (reg << 8) | (mem & 0x000000ff) | |
806 | 2 cd34 (reg << 16) | (mem & 0x0000ffff) | |
807 | 3 d234 (reg << 24) | (mem & 0x00ffffff) | |
808 | */ | |
809 | } | |
810 | ||
811 | /********************************************************* | |
812 | * Moves between GPR and COPx * | |
813 | * Format: OP rt, fs * | |
814 | *********************************************************/ | |
dec3c1d7 | 815 | OP(psxMFC0) { |
816 | u32 r = _Rd_; | |
817 | #ifdef DO_EXCEPTION_RESERVEDI | |
633d365e | 818 | if (unlikely(r == 0)) |
819 | intException(regs_, regs_->pc - 4, R3000E_RI << 2); | |
dec3c1d7 | 820 | #endif |
633d365e | 821 | doLoad(regs_, _Rt_, regs_->CP0.r[r]); |
dec3c1d7 | 822 | } |
823 | ||
633d365e | 824 | OP(psxCFC0) { doLoad(regs_, _Rt_, regs_->CP0.r[_Rd_]); } |
ef79bbde | 825 | |
dec3c1d7 | 826 | static void setupCop(u32 sr); |
827 | ||
cab87bf3 | 828 | void MTC0(psxRegisters *regs_, int reg, u32 val) { |
ef79bbde P |
829 | // SysPrintf("MTC0 %d: %x\n", reg, val); |
830 | switch (reg) { | |
831 | case 12: // Status | |
dec3c1d7 | 832 | if (unlikely((regs_->CP0.n.Status ^ val) & (1 << 16))) |
c6b7420b | 833 | psxMemOnIsolate((val >> 16) & 1); |
dec3c1d7 | 834 | if (unlikely((regs_->CP0.n.Status ^ val) & (7 << 29))) |
835 | setupCop(val); | |
c6b7420b | 836 | regs_->CP0.n.Status = val; |
dec3c1d7 | 837 | psxTestSWInts(regs_, 1); |
ef79bbde P |
838 | break; |
839 | ||
840 | case 13: // Cause | |
cab87bf3 | 841 | regs_->CP0.n.Cause &= ~0x0300; |
842 | regs_->CP0.n.Cause |= val & 0x0300; | |
dec3c1d7 | 843 | psxTestSWInts(regs_, 0); |
ef79bbde P |
844 | break; |
845 | ||
846 | default: | |
cab87bf3 | 847 | regs_->CP0.r[reg] = val; |
ef79bbde P |
848 | break; |
849 | } | |
850 | } | |
851 | ||
cab87bf3 | 852 | OP(psxMTC0) { MTC0(regs_, _Rd_, _u32(_rRt_)); } |
853 | OP(psxCTC0) { MTC0(regs_, _Rd_, _u32(_rRt_)); } | |
ef79bbde P |
854 | |
855 | /********************************************************* | |
dec3c1d7 | 856 | * Unknown instruction (would generate an exception) * |
ef79bbde P |
857 | * Format: ? * |
858 | *********************************************************/ | |
cab87bf3 | 859 | static inline void psxNULL_(void) { |
dec3c1d7 | 860 | //printf("op %08x @%08x\n", psxRegs.code, psxRegs.pc); |
861 | } | |
862 | ||
863 | OP(psxNULL) { | |
864 | psxNULL_(); | |
865 | #ifdef DO_EXCEPTION_RESERVEDI | |
633d365e | 866 | intException(regs_, regs_->pc - 4, R3000E_RI << 2); |
ef79bbde P |
867 | #endif |
868 | } | |
869 | ||
dec3c1d7 | 870 | void gteNULL(struct psxCP2Regs *regs) { |
871 | psxNULL_(); | |
872 | } | |
ef79bbde | 873 | |
cab87bf3 | 874 | OP(psxSPECIAL) { |
875 | psxSPC[_Funct_](regs_, code); | |
ef79bbde P |
876 | } |
877 | ||
cab87bf3 | 878 | OP(psxCOP0) { |
879 | switch (_Rs_) { | |
880 | case 0x00: psxMFC0(regs_, code); break; | |
881 | case 0x02: psxCFC0(regs_, code); break; | |
882 | case 0x04: psxMTC0(regs_, code); break; | |
883 | case 0x06: psxCTC0(regs_, code); break; | |
884 | case 0x10: psxRFE(regs_, code); break; | |
885 | default: psxNULL_(); break; | |
886 | } | |
ef79bbde P |
887 | } |
888 | ||
dec3c1d7 | 889 | OP(psxLWC0) { |
890 | // MTC0(regs_, _Rt_, psxMemRead32(_oB_)); // ? | |
891 | log_unhandled("LWC0 %08x\n", code); | |
892 | } | |
893 | ||
894 | OP(psxCOP1) { | |
895 | // ??? what actually happens here? | |
896 | } | |
897 | ||
898 | OP(psxCOP1d) { | |
899 | #ifdef DO_EXCEPTION_RESERVEDI | |
633d365e | 900 | intException(regs_, regs_->pc - 4, (1<<28) | (R3000E_RI << 2)); |
dec3c1d7 | 901 | #endif |
902 | } | |
903 | ||
cab87bf3 | 904 | OP(psxCOP2) { |
905 | psxCP2[_Funct_](®s_->CP2); | |
ef79bbde P |
906 | } |
907 | ||
cab87bf3 | 908 | OP(psxCOP2_stall) { |
630b122b | 909 | u32 f = _Funct_; |
910 | gteCheckStall(f); | |
cab87bf3 | 911 | psxCP2[f](®s_->CP2); |
912 | } | |
913 | ||
dec3c1d7 | 914 | OP(psxCOP2d) { |
915 | #ifdef DO_EXCEPTION_RESERVEDI | |
633d365e | 916 | intException(regs_, regs_->pc - 4, (2<<28) | (R3000E_RI << 2)); |
dec3c1d7 | 917 | #endif |
918 | } | |
919 | ||
cab87bf3 | 920 | OP(gteMFC2) { |
633d365e | 921 | doLoad(regs_, _Rt_, MFC2(®s_->CP2, _Rd_)); |
cab87bf3 | 922 | } |
923 | ||
924 | OP(gteCFC2) { | |
633d365e | 925 | doLoad(regs_, _Rt_, regs_->CP2C.r[_Rd_]); |
cab87bf3 | 926 | } |
927 | ||
928 | OP(gteMTC2) { | |
929 | MTC2(®s_->CP2, regs_->GPR.r[_Rt_], _Rd_); | |
930 | } | |
931 | ||
932 | OP(gteCTC2) { | |
933 | CTC2(®s_->CP2, regs_->GPR.r[_Rt_], _Rd_); | |
934 | } | |
935 | ||
936 | OP(gteLWC2) { | |
937 | MTC2(®s_->CP2, psxMemRead32(_oB_), _Rt_); | |
938 | } | |
939 | ||
940 | OP(gteSWC2) { | |
941 | psxMemWrite32(_oB_, MFC2(®s_->CP2, _Rt_)); | |
942 | } | |
943 | ||
944 | OP(gteLWC2_stall) { | |
945 | gteCheckStall(0); | |
946 | gteLWC2(regs_, code); | |
db63e60d | 947 | } |
948 | ||
cab87bf3 | 949 | OP(gteSWC2_stall) { |
950 | gteCheckStall(0); | |
951 | gteSWC2(regs_, code); | |
952 | } | |
953 | ||
dec3c1d7 | 954 | OP(psxCOP3) { |
955 | // ??? what actually happens here? | |
956 | } | |
957 | ||
958 | OP(psxCOP3d) { | |
959 | #ifdef DO_EXCEPTION_RESERVEDI | |
633d365e | 960 | intException(regs_, regs_->pc - 4, (3<<28) | (R3000E_RI << 2)); |
dec3c1d7 | 961 | #endif |
962 | } | |
963 | ||
964 | OP(psxLWCx) { | |
965 | // does this read memory? | |
966 | log_unhandled("LWCx %08x\n", code); | |
967 | } | |
968 | ||
969 | OP(psxSWCx) { | |
970 | // does this write something to memory? | |
971 | log_unhandled("SWCx %08x\n", code); | |
972 | } | |
973 | ||
cab87bf3 | 974 | static void psxBASIC(struct psxCP2Regs *cp2regs) { |
975 | psxRegisters *regs_ = (void *)((char *)cp2regs - offsetof(psxRegisters, CP2)); | |
976 | u32 code = regs_->code; | |
977 | assert(regs_ == &psxRegs); | |
978 | switch (_Rs_) { | |
979 | case 0x00: gteMFC2(regs_, code); break; | |
980 | case 0x02: gteCFC2(regs_, code); break; | |
981 | case 0x04: gteMTC2(regs_, code); break; | |
982 | case 0x06: gteCTC2(regs_, code); break; | |
983 | default: psxNULL_(); break; | |
984 | } | |
985 | } | |
986 | ||
987 | OP(psxREGIMM) { | |
424e6d52 | 988 | u32 rt = _Rt_; |
989 | switch (rt) { | |
cab87bf3 | 990 | case 0x10: psxBLTZAL(regs_, code); break; |
991 | case 0x11: psxBGEZAL(regs_, code); break; | |
424e6d52 | 992 | default: |
993 | if (rt & 1) | |
994 | psxBGEZ(regs_, code); | |
995 | else | |
996 | psxBLTZ(regs_, code); | |
cab87bf3 | 997 | } |
ef79bbde P |
998 | } |
999 | ||
cab87bf3 | 1000 | OP(psxHLE) { |
dec3c1d7 | 1001 | u32 hleCode; |
1002 | if (unlikely(!Config.HLE)) { | |
1003 | psxSWCx(regs_, code); | |
1004 | return; | |
1005 | } | |
1006 | hleCode = code & 0x03ffffff; | |
1007 | if (hleCode >= (sizeof(psxHLEt) / sizeof(psxHLEt[0]))) { | |
1008 | psxSWCx(regs_, code); | |
1009 | return; | |
1010 | } | |
1011 | psxHLEt[hleCode](); | |
ef79bbde P |
1012 | } |
1013 | ||
cab87bf3 | 1014 | static void (INT_ATTR *psxBSC[64])(psxRegisters *regs_, u32 code) = { |
ef79bbde P |
1015 | psxSPECIAL, psxREGIMM, psxJ , psxJAL , psxBEQ , psxBNE , psxBLEZ, psxBGTZ, |
1016 | psxADDI , psxADDIU , psxSLTI, psxSLTIU, psxANDI, psxORI , psxXORI, psxLUI , | |
dec3c1d7 | 1017 | psxCOP0 , psxCOP1d , psxCOP2, psxCOP3d, psxNULL, psxCOP1d,psxCOP2d,psxCOP3d, |
1018 | psxNULL , psxCOP1d , psxCOP2d,psxCOP3d, psxNULL, psxCOP1d,psxCOP2d,psxCOP3d, | |
1019 | psxLB , psxLH , psxLWL , psxLW , psxLBU , psxLHU , psxLWR , psxCOP3d, | |
1020 | psxSB , psxSH , psxSWL , psxSW , psxNULL, psxCOP1d,psxSWR , psxCOP3d, | |
1021 | psxLWC0 , psxLWCx , gteLWC2, psxLWCx , psxNULL, psxCOP1d,psxCOP2d,psxCOP3d, | |
1022 | psxSWCx , psxSWCx , gteSWC2, psxHLE , psxNULL, psxCOP1d,psxCOP2d,psxCOP3d, | |
ef79bbde P |
1023 | }; |
1024 | ||
cab87bf3 | 1025 | static void (INT_ATTR *psxSPC[64])(psxRegisters *regs_, u32 code) = { |
ef79bbde P |
1026 | psxSLL , psxNULL , psxSRL , psxSRA , psxSLLV , psxNULL , psxSRLV, psxSRAV, |
1027 | psxJR , psxJALR , psxNULL, psxNULL, psxSYSCALL, psxBREAK, psxNULL, psxNULL, | |
1028 | psxMFHI, psxMTHI , psxMFLO, psxMTLO, psxNULL , psxNULL , psxNULL, psxNULL, | |
1029 | psxMULT, psxMULTU, psxDIV , psxDIVU, psxNULL , psxNULL , psxNULL, psxNULL, | |
1030 | psxADD , psxADDU , psxSUB , psxSUBU, psxAND , psxOR , psxXOR , psxNOR , | |
1031 | psxNULL, psxNULL , psxSLT , psxSLTU, psxNULL , psxNULL , psxNULL, psxNULL, | |
1032 | psxNULL, psxNULL , psxNULL, psxNULL, psxNULL , psxNULL , psxNULL, psxNULL, | |
1033 | psxNULL, psxNULL , psxNULL, psxNULL, psxNULL , psxNULL , psxNULL, psxNULL | |
1034 | }; | |
1035 | ||
6c0eefaf | 1036 | void (*psxCP2[64])(struct psxCP2Regs *regs) = { |
cab87bf3 | 1037 | psxBASIC, gteRTPS , gteNULL , gteNULL, gteNULL, gteNULL , gteNCLIP, gteNULL, // 00 |
1038 | gteNULL , gteNULL , gteNULL , gteNULL, gteOP , gteNULL , gteNULL , gteNULL, // 08 | |
1039 | gteDPCS , gteINTPL, gteMVMVA, gteNCDS, gteCDP , gteNULL , gteNCDT , gteNULL, // 10 | |
1040 | gteNULL , gteNULL , gteNULL , gteNCCS, gteCC , gteNULL , gteNCS , gteNULL, // 18 | |
1041 | gteNCT , gteNULL , gteNULL , gteNULL, gteNULL, gteNULL , gteNULL , gteNULL, // 20 | |
1042 | gteSQR , gteDCPL , gteDPCT , gteNULL, gteNULL, gteAVSZ3, gteAVSZ4, gteNULL, // 28 | |
1043 | gteRTPT , gteNULL , gteNULL , gteNULL, gteNULL, gteNULL , gteNULL , gteNULL, // 30 | |
1044 | gteNULL , gteNULL , gteNULL , gteNULL, gteNULL, gteGPF , gteGPL , gteNCCT // 38 | |
ef79bbde P |
1045 | }; |
1046 | ||
ef79bbde P |
1047 | /////////////////////////////////////////// |
1048 | ||
1049 | static int intInit() { | |
1050 | return 0; | |
1051 | } | |
1052 | ||
1053 | static void intReset() { | |
633d365e | 1054 | dloadClear(&psxRegs); |
ef79bbde P |
1055 | } |
1056 | ||
cab87bf3 | 1057 | static inline void execI_(u8 **memRLUT, psxRegisters *regs_) { |
dec3c1d7 | 1058 | u32 pc = regs_->pc; |
cab87bf3 | 1059 | regs_->pc += 4; |
dec3c1d7 | 1060 | regs_->code = fetch(regs_, memRLUT, pc); |
1061 | ||
1562ed57 | 1062 | addCycle(); |
cab87bf3 | 1063 | |
633d365e | 1064 | dloadStep(regs_); |
cab87bf3 | 1065 | psxBSC[regs_->code >> 26](regs_, regs_->code); |
1066 | } | |
1067 | ||
1068 | static void intExecute() { | |
1069 | psxRegisters *regs_ = &psxRegs; | |
1070 | u8 **memRLUT = psxMemRLUT; | |
796a91ef | 1071 | extern int stop; |
cab87bf3 | 1072 | |
1073 | while (!stop) | |
1074 | execI_(memRLUT, regs_); | |
ef79bbde P |
1075 | } |
1076 | ||
f3bc907d | 1077 | void intExecuteBlock(enum blockExecCaller caller) { |
cab87bf3 | 1078 | psxRegisters *regs_ = &psxRegs; |
1079 | u8 **memRLUT = psxMemRLUT; | |
1080 | ||
ef79bbde | 1081 | branch2 = 0; |
cab87bf3 | 1082 | while (!branch2) |
1083 | execI_(memRLUT, regs_); | |
ef79bbde P |
1084 | } |
1085 | ||
1086 | static void intClear(u32 Addr, u32 Size) { | |
1087 | } | |
1088 | ||
20196899 | 1089 | static void intNotify(enum R3000Anote note, void *data) { |
1090 | switch (note) { | |
633d365e | 1091 | case R3000ACPU_NOTIFY_BEFORE_SAVE: |
1092 | dloadFlush(&psxRegs); | |
1093 | break; | |
20196899 | 1094 | case R3000ACPU_NOTIFY_AFTER_LOAD: |
633d365e | 1095 | dloadClear(&psxRegs); |
dec3c1d7 | 1096 | setupCop(psxRegs.CP0.n.Status); |
1097 | // fallthrough | |
1098 | case R3000ACPU_NOTIFY_CACHE_ISOLATED: // Armored Core? | |
630b122b | 1099 | memset(&ICache, 0xff, sizeof(ICache)); |
20196899 | 1100 | break; |
1101 | case R3000ACPU_NOTIFY_CACHE_UNISOLATED: | |
20196899 | 1102 | break; |
7a811716 | 1103 | } |
7a811716 | 1104 | } |
1105 | ||
dec3c1d7 | 1106 | static void setupCop(u32 sr) |
1107 | { | |
1108 | if (sr & (1u << 29)) | |
1109 | psxBSC[17] = psxCOP1; | |
1110 | else | |
1111 | psxBSC[17] = psxCOP1d; | |
1112 | if (sr & (1u << 30)) | |
1113 | psxBSC[18] = Config.DisableStalls ? psxCOP2 : psxCOP2_stall; | |
1114 | else | |
1115 | psxBSC[18] = psxCOP2d; | |
1116 | if (sr & (1u << 31)) | |
1117 | psxBSC[19] = psxCOP3; | |
1118 | else | |
1119 | psxBSC[19] = psxCOP3d; | |
1120 | } | |
1121 | ||
630b122b | 1122 | void intApplyConfig() { |
1562ed57 | 1123 | int cycle_mult; |
1124 | ||
630b122b | 1125 | assert(psxBSC[50] == gteLWC2 || psxBSC[50] == gteLWC2_stall); |
1126 | assert(psxBSC[58] == gteSWC2 || psxBSC[58] == gteSWC2_stall); | |
1127 | assert(psxSPC[16] == psxMFHI || psxSPC[16] == psxMFHI_stall); | |
1128 | assert(psxSPC[18] == psxMFLO || psxSPC[18] == psxMFLO_stall); | |
1129 | assert(psxSPC[24] == psxMULT || psxSPC[24] == psxMULT_stall); | |
1130 | assert(psxSPC[25] == psxMULTU || psxSPC[25] == psxMULTU_stall); | |
1131 | assert(psxSPC[26] == psxDIV || psxSPC[26] == psxDIV_stall); | |
1132 | assert(psxSPC[27] == psxDIVU || psxSPC[27] == psxDIVU_stall); | |
1133 | ||
1134 | if (Config.DisableStalls) { | |
1135 | psxBSC[18] = psxCOP2; | |
1136 | psxBSC[50] = gteLWC2; | |
1137 | psxBSC[58] = gteSWC2; | |
1138 | psxSPC[16] = psxMFHI; | |
1139 | psxSPC[18] = psxMFLO; | |
1140 | psxSPC[24] = psxMULT; | |
1141 | psxSPC[25] = psxMULTU; | |
1142 | psxSPC[26] = psxDIV; | |
1143 | psxSPC[27] = psxDIVU; | |
1144 | } else { | |
1145 | psxBSC[18] = psxCOP2_stall; | |
1146 | psxBSC[50] = gteLWC2_stall; | |
1147 | psxBSC[58] = gteSWC2_stall; | |
1148 | psxSPC[16] = psxMFHI_stall; | |
1149 | psxSPC[18] = psxMFLO_stall; | |
1150 | psxSPC[24] = psxMULT_stall; | |
1151 | psxSPC[25] = psxMULTU_stall; | |
1152 | psxSPC[26] = psxDIV_stall; | |
1153 | psxSPC[27] = psxDIVU_stall; | |
7a811716 | 1154 | } |
dec3c1d7 | 1155 | setupCop(psxRegs.CP0.n.Status); |
7a811716 | 1156 | |
630b122b | 1157 | // dynarec may occasionally call the interpreter, in such a case the |
1158 | // cache won't work (cache only works right if all fetches go through it) | |
1159 | if (!Config.icache_emulation || psxCpu != &psxInt) | |
1160 | fetch = fetchNoCache; | |
1161 | else | |
1162 | fetch = fetchICache; | |
1562ed57 | 1163 | |
1164 | cycle_mult = Config.cycle_multiplier_override && Config.cycle_multiplier == CYCLE_MULT_DEFAULT | |
1165 | ? Config.cycle_multiplier_override : Config.cycle_multiplier; | |
1166 | psxRegs.subCycleStep = 0x10000 * cycle_mult / 100; | |
630b122b | 1167 | } |
1168 | ||
1169 | static void intShutdown() { | |
ef79bbde P |
1170 | } |
1171 | ||
cab87bf3 | 1172 | // single step (may do several ops in case of a branch) |
4600ba03 | 1173 | void execI() { |
cab87bf3 | 1174 | execI_(psxMemRLUT, &psxRegs); |
ef79bbde P |
1175 | } |
1176 | ||
1177 | R3000Acpu psxInt = { | |
1178 | intInit, | |
1179 | intReset, | |
1180 | intExecute, | |
1181 | intExecuteBlock, | |
1182 | intClear, | |
7a811716 | 1183 | intNotify, |
630b122b | 1184 | intApplyConfig, |
ef79bbde P |
1185 | intShutdown |
1186 | }; |