Commit | Line | Data |
---|---|---|
ef79bbde P |
1 | /*************************************************************************** |
2 | * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team * | |
f9ae4f29 | 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" | |
3968e69e | 29 | #include "psxinterpreter.h" |
4cc373dd | 30 | #include <stddef.h> |
32631e6a | 31 | #include <assert.h> |
905b7c25 | 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 | |
f9ae4f29 | 37 | #define HANDLE_LOAD_DELAY |
ef79bbde | 38 | |
4cc373dd | 39 | #ifdef __i386__ |
40 | #define INT_ATTR __attribute__((regparm(2))) | |
41 | #else | |
42 | #define INT_ATTR | |
43 | #endif | |
44 | #ifndef INVALID_PTR | |
45 | #define INVALID_PTR NULL | |
46 | #endif | |
47 | ||
ef79bbde | 48 | // Subsets |
4cc373dd | 49 | static void (INT_ATTR *psxBSC[64])(psxRegisters *regs_, u32 code); |
50 | static void (INT_ATTR *psxSPC[64])(psxRegisters *regs_, u32 code); | |
51 | ||
f9ae4f29 | 52 | // load delay |
53 | static void doLoad(psxRegisters *regs, u32 r, u32 val) | |
54 | { | |
55 | #ifdef HANDLE_LOAD_DELAY | |
56 | int sel = regs->dloadSel ^ 1; | |
57 | assert(regs->dloadReg[sel] == 0); | |
58 | regs->dloadReg[sel] = r; | |
59 | regs->dloadVal[sel] = r ? val : 0; | |
60 | if (regs->dloadReg[sel ^ 1] == r) | |
61 | regs->dloadVal[sel ^ 1] = regs->dloadReg[sel ^ 1] = 0; | |
62 | #else | |
63 | regs->GPR.r[r] = r ? val : 0; | |
64 | #endif | |
65 | } | |
66 | ||
67 | static void dloadRt(psxRegisters *regs, u32 r, u32 val) | |
68 | { | |
69 | #ifdef HANDLE_LOAD_DELAY | |
70 | int sel = regs->dloadSel; | |
71 | if (unlikely(regs->dloadReg[sel] == r)) | |
72 | regs->dloadVal[sel] = regs->dloadReg[sel] = 0; | |
73 | #endif | |
74 | regs->GPR.r[r] = r ? val : 0; | |
75 | } | |
76 | ||
77 | static void dloadStep(psxRegisters *regs) | |
78 | { | |
79 | #ifdef HANDLE_LOAD_DELAY | |
80 | int sel = regs->dloadSel; | |
81 | regs->GPR.r[regs->dloadReg[sel]] = regs->dloadVal[sel]; | |
82 | regs->dloadVal[sel] = regs->dloadReg[sel] = 0; | |
83 | regs->dloadSel ^= 1; | |
84 | assert(regs->GPR.r[0] == 0); | |
85 | #endif | |
86 | } | |
87 | ||
88 | static void dloadFlush(psxRegisters *regs) | |
89 | { | |
90 | #ifdef HANDLE_LOAD_DELAY | |
91 | regs->GPR.r[regs->dloadReg[0]] = regs->dloadVal[0]; | |
92 | regs->GPR.r[regs->dloadReg[1]] = regs->dloadVal[1]; | |
93 | regs->dloadVal[0] = regs->dloadVal[1] = 0; | |
94 | regs->dloadReg[0] = regs->dloadReg[1] = 0; | |
95 | assert(regs->GPR.r[0] == 0); | |
96 | #endif | |
97 | } | |
98 | ||
99 | static void dloadClear(psxRegisters *regs) | |
100 | { | |
101 | #ifdef HANDLE_LOAD_DELAY | |
102 | regs->dloadVal[0] = regs->dloadVal[1] = 0; | |
103 | regs->dloadReg[0] = regs->dloadReg[1] = 0; | |
104 | regs->dloadSel = 0; | |
105 | #endif | |
106 | } | |
107 | ||
108 | static void intException(psxRegisters *regs, u32 pc, u32 cause) | |
109 | { | |
bc7c5acb | 110 | if (cause != 0x20) { |
111 | //FILE *f = fopen("/tmp/psx_ram.bin", "wb"); | |
112 | //fwrite(psxM, 1, 0x200000, f); fclose(f); | |
dc4fa8bc | 113 | log_unhandled("exception %08x @%08x ra=%08x\n", |
114 | cause, pc, regs->GPR.n.ra); | |
bc7c5acb | 115 | } |
f9ae4f29 | 116 | dloadFlush(regs); |
117 | regs->pc = pc; | |
bc7c5acb | 118 | psxException(cause, regs->branching, ®s->CP0); |
119 | regs->branching = R3000A_BRANCH_NONE_OR_EXCEPTION; | |
120 | } | |
121 | ||
122 | // exception caused by current instruction (excluding unkasking) | |
123 | static void intExceptionInsn(psxRegisters *regs, u32 cause) | |
124 | { | |
125 | cause |= (regs->code & 0x0c000000) << 2; | |
126 | intException(regs, regs->pc - 4, cause); | |
127 | } | |
128 | ||
e9183d95 | 129 | static noinline void intExceptionReservedInsn(psxRegisters *regs) |
130 | { | |
131 | #ifdef DO_EXCEPTION_RESERVEDI | |
132 | static u32 ppc_ = ~0u; | |
133 | if (regs->pc != ppc_) { | |
134 | SysPrintf("reserved instruction %08x @%08x ra=%08x\n", | |
135 | regs->code, regs->pc - 4, regs->GPR.n.ra); | |
136 | ppc_ = regs->pc; | |
137 | } | |
138 | intExceptionInsn(regs, R3000E_RI << 2); | |
139 | #endif | |
140 | } | |
141 | ||
bc7c5acb | 142 | // 29 Enable for 80000000-ffffffff |
143 | // 30 Enable for 00000000-7fffffff | |
144 | // 31 Enable exception | |
145 | #define DBR_ABIT(dc, a) ((dc) & (1u << (29+(((a)>>31)^1)))) | |
146 | #define DBR_EN_EXEC(dc, a) (((dc) & 0x01800000) == 0x01800000 && DBR_ABIT(dc, a)) | |
147 | #define DBR_EN_LD(dc, a) (((dc) & 0x06800000) == 0x06800000 && DBR_ABIT(dc, a)) | |
148 | #define DBR_EN_ST(dc, a) (((dc) & 0x0a800000) == 0x0a800000 && DBR_ABIT(dc, a)) | |
149 | static void intExceptionDebugBp(psxRegisters *regs, u32 pc) | |
150 | { | |
151 | psxCP0Regs *cp0 = ®s->CP0; | |
152 | dloadFlush(regs); | |
153 | cp0->n.Cause &= 0x300; | |
154 | cp0->n.Cause |= (regs->branching << 30) | (R3000E_Bp << 2); | |
155 | cp0->n.SR = (cp0->n.SR & ~0x3f) | ((cp0->n.SR & 0x0f) << 2); | |
156 | cp0->n.EPC = regs->branching ? pc - 4 : pc; | |
c87406ff | 157 | regs->pc = 0x80000040; |
bc7c5acb | 158 | } |
159 | ||
160 | static int execBreakCheck(psxRegisters *regs, u32 pc) | |
161 | { | |
162 | if (unlikely(DBR_EN_EXEC(regs->CP0.n.DCIC, pc) && | |
163 | ((pc ^ regs->CP0.n.BPC) & regs->CP0.n.BPCM) == 0)) | |
164 | { | |
165 | regs->CP0.n.DCIC |= 0x03; | |
166 | if (regs->CP0.n.DCIC & (1u << 31)) { | |
167 | intExceptionDebugBp(regs, pc); | |
168 | return 1; | |
169 | } | |
170 | } | |
171 | return 0; | |
f9ae4f29 | 172 | } |
173 | ||
905b7c25 | 174 | // get an opcode without triggering exceptions or affecting cache |
175 | u32 intFakeFetch(u32 pc) | |
176 | { | |
4bb8d7e1 PC |
177 | u32 *code = (u32 *)psxm(pc & ~0x3, 0); |
178 | if (unlikely(code == INVALID_PTR)) | |
905b7c25 | 179 | return 0; // nop |
905b7c25 | 180 | return SWAP32(*code); |
181 | ||
182 | } | |
183 | ||
184 | static u32 INT_ATTR fetchNoCache(psxRegisters *regs, u8 **memRLUT, u32 pc) | |
61ad2a61 | 185 | { |
4bb8d7e1 PC |
186 | u32 *code = (u32 *)psxm_lut(pc & ~0x3, 0, memRLUT); |
187 | if (unlikely(code == INVALID_PTR)) { | |
905b7c25 | 188 | SysPrintf("game crash @%08x, ra=%08x\n", pc, regs->GPR.n.ra); |
f9ae4f29 | 189 | intException(regs, pc, R3000E_IBE << 2); |
905b7c25 | 190 | return 0; // execute as nop |
191 | } | |
4cc373dd | 192 | return SWAP32(*code); |
61ad2a61 | 193 | } |
194 | ||
943a507a | 195 | /* |
196 | Formula One 2001 : | |
197 | Use old CPU cache code when the RAM location is updated with new code (affects in-game racing) | |
198 | */ | |
9f84fc93 | 199 | static struct cache_entry { |
200 | u32 tag; | |
201 | u32 data[4]; | |
202 | } ICache[256]; | |
203 | ||
905b7c25 | 204 | static u32 INT_ATTR fetchICache(psxRegisters *regs, u8 **memRLUT, u32 pc) |
943a507a | 205 | { |
9f84fc93 | 206 | // cached? |
207 | if (pc < 0xa0000000) | |
943a507a | 208 | { |
9f84fc93 | 209 | // this is not how the hardware works but whatever |
210 | struct cache_entry *entry = &ICache[(pc & 0xff0) >> 4]; | |
211 | ||
212 | if (((entry->tag ^ pc) & 0xfffffff0) != 0 || pc < entry->tag) | |
943a507a | 213 | { |
4bb8d7e1 PC |
214 | const u32 *code = (u32 *)psxm_lut(pc & ~0xf, 0, memRLUT); |
215 | if (unlikely(code == INVALID_PTR)) { | |
905b7c25 | 216 | SysPrintf("game crash @%08x, ra=%08x\n", pc, regs->GPR.n.ra); |
f9ae4f29 | 217 | intException(regs, pc, R3000E_IBE << 2); |
905b7c25 | 218 | return 0; // execute as nop |
219 | } | |
9f84fc93 | 220 | |
221 | entry->tag = pc; | |
222 | // treat as 4 words, although other configurations are said to be possible | |
223 | switch (pc & 0x0c) | |
224 | { | |
225 | case 0x00: entry->data[0] = SWAP32(code[0]); | |
226 | case 0x04: entry->data[1] = SWAP32(code[1]); | |
227 | case 0x08: entry->data[2] = SWAP32(code[2]); | |
228 | case 0x0c: entry->data[3] = SWAP32(code[3]); | |
229 | } | |
943a507a | 230 | } |
9f84fc93 | 231 | return entry->data[(pc & 0x0f) >> 2]; |
943a507a | 232 | } |
233 | ||
905b7c25 | 234 | return fetchNoCache(regs, memRLUT, pc); |
943a507a | 235 | } |
61ad2a61 | 236 | |
905b7c25 | 237 | static u32 (INT_ATTR *fetch)(psxRegisters *regs_, u8 **memRLUT, u32 pc) = fetchNoCache; |
943a507a | 238 | |
d5aeda23 | 239 | // Make the timing events trigger faster as we are currently assuming everything |
240 | // takes one cycle, which is not the case on real hardware. | |
241 | // FIXME: count cache misses, memory latencies, stalls to get rid of this | |
bc7c5acb | 242 | static inline void addCycle(psxRegisters *regs) |
d5aeda23 | 243 | { |
bc7c5acb | 244 | assert(regs->subCycleStep >= 0x10000); |
245 | regs->subCycle += regs->subCycleStep; | |
246 | regs->cycle += regs->subCycle >> 16; | |
247 | regs->subCycle &= 0xffff; | |
d5aeda23 | 248 | } |
249 | ||
4cc373dd | 250 | /**** R3000A Instruction Macros ****/ |
251 | #define _PC_ regs_->pc // The next PC to be executed | |
252 | ||
253 | #define _fOp_(code) ((code >> 26) ) // The opcode part of the instruction register | |
254 | #define _fFunct_(code) ((code ) & 0x3F) // The funct part of the instruction register | |
255 | #define _fRd_(code) ((code >> 11) & 0x1F) // The rd part of the instruction register | |
256 | #define _fRt_(code) ((code >> 16) & 0x1F) // The rt part of the instruction register | |
257 | #define _fRs_(code) ((code >> 21) & 0x1F) // The rs part of the instruction register | |
258 | #define _fSa_(code) ((code >> 6) & 0x1F) // The sa part of the instruction register | |
259 | #define _fIm_(code) ((u16)code) // The immediate part of the instruction register | |
260 | #define _fTarget_(code) (code & 0x03ffffff) // The target part of the instruction register | |
261 | ||
262 | #define _fImm_(code) ((s16)code) // sign-extended immediate | |
263 | #define _fImmU_(code) (code&0xffff) // zero-extended immediate | |
264 | ||
265 | #define _Op_ _fOp_(code) | |
266 | #define _Funct_ _fFunct_(code) | |
267 | #define _Rd_ _fRd_(code) | |
268 | #define _Rt_ _fRt_(code) | |
269 | #define _Rs_ _fRs_(code) | |
270 | #define _Sa_ _fSa_(code) | |
271 | #define _Im_ _fIm_(code) | |
272 | #define _Target_ _fTarget_(code) | |
273 | ||
274 | #define _Imm_ _fImm_(code) | |
275 | #define _ImmU_ _fImmU_(code) | |
276 | ||
277 | #define _rRs_ regs_->GPR.r[_Rs_] // Rs register | |
278 | #define _rRt_ regs_->GPR.r[_Rt_] // Rt register | |
4cc373dd | 279 | #define _rSa_ regs_->GPR.r[_Sa_] // Sa register |
4cc373dd | 280 | |
281 | #define _rHi_ regs_->GPR.n.hi // The HI register | |
282 | #define _rLo_ regs_->GPR.n.lo // The LO register | |
283 | ||
284 | #define _JumpTarget_ ((_Target_ * 4) + (_PC_ & 0xf0000000)) // Calculates the target during a jump instruction | |
285 | #define _BranchTarget_ ((s16)_Im_ * 4 + _PC_) // Calculates the target during a branch instruction | |
286 | ||
f9ae4f29 | 287 | #define _SetLink(x) dloadRt(regs_, x, _PC_ + 4); // Sets the return address in the link register |
4cc373dd | 288 | |
289 | #define OP(name) \ | |
290 | static inline INT_ATTR void name(psxRegisters *regs_, u32 code) | |
291 | ||
ef79bbde P |
292 | // this defines shall be used with the tmp |
293 | // of the next func (instead of _Funct_...) | |
294 | #define _tFunct_ ((tmp ) & 0x3F) // The funct part of the instruction register | |
295 | #define _tRd_ ((tmp >> 11) & 0x1F) // The rd part of the instruction register | |
296 | #define _tRt_ ((tmp >> 16) & 0x1F) // The rt part of the instruction register | |
297 | #define _tRs_ ((tmp >> 21) & 0x1F) // The rs part of the instruction register | |
298 | #define _tSa_ ((tmp >> 6) & 0x1F) // The sa part of the instruction register | |
299 | ||
4cc373dd | 300 | #define _i32(x) (s32)(x) |
301 | #define _u32(x) (u32)(x) | |
302 | ||
b9698f9d | 303 | #define isBranch(c_) \ |
304 | ((1 <= ((c_) >> 26) && ((c_) >> 26) <= 7) || ((c_) & 0xfc00003e) == 8) | |
305 | #define swap_(a_, b_) { u32 t_ = a_; a_ = b_; b_ = t_; } | |
990cb018 | 306 | |
b9698f9d | 307 | // tar1 is main branch target, 'code' is opcode in DS |
308 | static u32 psxBranchNoDelay(psxRegisters *regs_, u32 tar1, u32 code, int *taken) { | |
309 | u32 temp, rt; | |
310 | ||
311 | assert(isBranch(code)); | |
312 | *taken = 1; | |
313 | switch (code >> 26) { | |
990cb018 | 314 | case 0x00: // SPECIAL |
315 | switch (_Funct_) { | |
316 | case 0x08: // JR | |
317 | return _u32(_rRs_); | |
318 | case 0x09: // JALR | |
319 | temp = _u32(_rRs_); | |
b9698f9d | 320 | if (_Rd_) |
321 | regs_->GPR.r[_Rd_] = tar1 + 4; | |
990cb018 | 322 | return temp; |
323 | } | |
324 | break; | |
325 | case 0x01: // REGIMM | |
b9698f9d | 326 | rt = _Rt_; |
327 | switch (rt) { | |
328 | case 0x10: // BLTZAL | |
329 | regs_->GPR.n.ra = tar1 + 4; | |
990cb018 | 330 | if (_i32(_rRs_) < 0) |
b9698f9d | 331 | return tar1 + (s16)_Im_ * 4; |
990cb018 | 332 | break; |
b9698f9d | 333 | case 0x11: // BGEZAL |
334 | regs_->GPR.n.ra = tar1 + 4; | |
990cb018 | 335 | if (_i32(_rRs_) >= 0) |
b9698f9d | 336 | return tar1 + (s16)_Im_ * 4; |
990cb018 | 337 | break; |
b9698f9d | 338 | default: |
339 | if (rt & 1) { // BGEZ | |
340 | if (_i32(_rRs_) >= 0) | |
341 | return tar1 + (s16)_Im_ * 4; | |
990cb018 | 342 | } |
b9698f9d | 343 | else { // BLTZ |
344 | if (_i32(_rRs_) < 0) | |
345 | return tar1 + (s16)_Im_ * 4; | |
990cb018 | 346 | } |
347 | break; | |
348 | } | |
349 | break; | |
350 | case 0x02: // J | |
b9698f9d | 351 | return (tar1 & 0xf0000000u) + _Target_ * 4; |
990cb018 | 352 | case 0x03: // JAL |
b9698f9d | 353 | regs_->GPR.n.ra = tar1 + 4; |
354 | return (tar1 & 0xf0000000u) + _Target_ * 4; | |
990cb018 | 355 | case 0x04: // BEQ |
356 | if (_i32(_rRs_) == _i32(_rRt_)) | |
b9698f9d | 357 | return tar1 + (s16)_Im_ * 4; |
990cb018 | 358 | break; |
359 | case 0x05: // BNE | |
360 | if (_i32(_rRs_) != _i32(_rRt_)) | |
b9698f9d | 361 | return tar1 + (s16)_Im_ * 4; |
990cb018 | 362 | break; |
363 | case 0x06: // BLEZ | |
364 | if (_i32(_rRs_) <= 0) | |
b9698f9d | 365 | return tar1 + (s16)_Im_ * 4; |
990cb018 | 366 | break; |
367 | case 0x07: // BGTZ | |
368 | if (_i32(_rRs_) > 0) | |
b9698f9d | 369 | return tar1 + (s16)_Im_ * 4; |
990cb018 | 370 | break; |
371 | } | |
372 | ||
b9698f9d | 373 | *taken = 0; |
374 | return tar1; | |
990cb018 | 375 | } |
376 | ||
b9698f9d | 377 | static void psxDoDelayBranch(psxRegisters *regs, u32 tar1, u32 code1) { |
378 | u32 tar2, code; | |
379 | int taken, lim; | |
990cb018 | 380 | |
b9698f9d | 381 | tar2 = psxBranchNoDelay(regs, tar1, code1, &taken); |
382 | regs->pc = tar1; | |
383 | if (!taken) | |
384 | return; | |
990cb018 | 385 | |
990cb018 | 386 | /* |
b9698f9d | 387 | * taken branch in delay slot: |
990cb018 | 388 | * - execute 1 instruction at tar1 |
389 | * - jump to tar2 (target of branch in delay slot; this branch | |
390 | * has no normal delay slot, instruction at tar1 was fetched instead) | |
391 | */ | |
b9698f9d | 392 | for (lim = 0; lim < 8; lim++) { |
393 | regs->code = code = fetch(regs, psxMemRLUT, tar1); | |
bc7c5acb | 394 | addCycle(regs); |
b9698f9d | 395 | if (likely(!isBranch(code))) { |
f9ae4f29 | 396 | dloadStep(regs); |
b9698f9d | 397 | psxBSC[code >> 26](regs, code); |
398 | regs->pc = tar2; | |
399 | return; | |
400 | } | |
401 | tar1 = psxBranchNoDelay(regs, tar2, code, &taken); | |
402 | regs->pc = tar2; | |
403 | if (!taken) | |
404 | return; | |
405 | swap_(tar1, tar2); | |
990cb018 | 406 | } |
b9698f9d | 407 | SysPrintf("Evil chained DS branches @ %08x %08x %08x\n", regs->pc, tar1, tar2); |
990cb018 | 408 | } |
409 | ||
bc7c5acb | 410 | static void doBranch(psxRegisters *regs, u32 tar, enum R3000Abdt taken) { |
411 | u32 code, pc, pc_final; | |
ef79bbde | 412 | |
c87406ff | 413 | regs->branchSeen = regs->branching = taken; |
bc7c5acb | 414 | pc_final = taken == R3000A_BRANCH_TAKEN ? tar : regs->pc + 4; |
990cb018 | 415 | |
b9698f9d | 416 | // fetch the delay slot |
417 | pc = regs->pc; | |
418 | regs->pc = pc + 4; | |
419 | regs->code = code = fetch(regs, psxMemRLUT, pc); | |
905b7c25 | 420 | |
bc7c5acb | 421 | addCycle(regs); |
ef79bbde | 422 | |
b9698f9d | 423 | // check for branch in delay slot |
424 | if (unlikely(isBranch(code))) { | |
bc7c5acb | 425 | regs->pc = pc; |
426 | if (taken == R3000A_BRANCH_TAKEN) | |
427 | psxDoDelayBranch(regs, tar, code); | |
b9698f9d | 428 | log_unhandled("branch in DS: %08x->%08x\n", pc, regs->pc); |
bc7c5acb | 429 | regs->branching = 0; |
b9698f9d | 430 | psxBranchTest(); |
431 | return; | |
432 | } | |
433 | ||
f9ae4f29 | 434 | dloadStep(regs); |
b9698f9d | 435 | psxBSC[code >> 26](regs, code); |
ef79bbde | 436 | |
bc7c5acb | 437 | if (likely(regs->branching != R3000A_BRANCH_NONE_OR_EXCEPTION)) |
438 | regs->pc = pc_final; | |
439 | else | |
440 | regs->CP0.n.Target = pc_final; | |
441 | regs->branching = 0; | |
ef79bbde P |
442 | |
443 | psxBranchTest(); | |
444 | } | |
445 | ||
b9698f9d | 446 | static void doBranchReg(psxRegisters *regs, u32 tar) { |
bc7c5acb | 447 | doBranch(regs, tar & ~3, R3000A_BRANCH_TAKEN); |
448 | } | |
449 | ||
450 | static void doBranchRegE(psxRegisters *regs, u32 tar) { | |
451 | if (unlikely(DBR_EN_EXEC(regs->CP0.n.DCIC, tar) && | |
452 | ((tar ^ regs->CP0.n.BPC) & regs->CP0.n.BPCM) == 0)) | |
453 | regs->CP0.n.DCIC |= 0x03; | |
905b7c25 | 454 | if (unlikely(tar & 3)) { |
f9ae4f29 | 455 | SysPrintf("game crash @%08x, ra=%08x\n", tar, regs->GPR.n.ra); |
bc7c5acb | 456 | regs->CP0.n.BadVAddr = tar; |
f9ae4f29 | 457 | intException(regs, tar, R3000E_AdEL << 2); |
905b7c25 | 458 | return; |
459 | } | |
bc7c5acb | 460 | doBranch(regs, tar, R3000A_BRANCH_TAKEN); |
905b7c25 | 461 | } |
462 | ||
905b7c25 | 463 | static void addExc(psxRegisters *regs, u32 rt, s32 a1, s32 a2) { |
f9ae4f29 | 464 | s32 val; |
465 | if (add_overflow(a1, a2, val)) { | |
466 | //printf("ov %08x + %08x = %08x\n", a1, a2, val); | |
bc7c5acb | 467 | intExceptionInsn(regs, R3000E_Ov << 2); |
905b7c25 | 468 | return; |
469 | } | |
f9ae4f29 | 470 | dloadRt(regs, rt, val); |
905b7c25 | 471 | } |
472 | ||
473 | static void subExc(psxRegisters *regs, u32 rt, s32 a1, s32 a2) { | |
f9ae4f29 | 474 | s32 val; |
475 | if (sub_overflow(a1, a2, val)) { | |
bc7c5acb | 476 | intExceptionInsn(regs, R3000E_Ov << 2); |
905b7c25 | 477 | return; |
478 | } | |
f9ae4f29 | 479 | dloadRt(regs, rt, val); |
905b7c25 | 480 | } |
481 | ||
ef79bbde P |
482 | /********************************************************* |
483 | * Arithmetic with immediate operand * | |
484 | * Format: OP rt, rs, immediate * | |
485 | *********************************************************/ | |
f9ae4f29 | 486 | OP(psxADDI) { addExc (regs_, _Rt_, _i32(_rRs_), _Imm_); } // Rt = Rs + Im (Exception on Integer Overflow) |
487 | OP(psxADDIU) { dloadRt(regs_, _Rt_, _u32(_rRs_) + _Imm_ ); } // Rt = Rs + Im | |
488 | OP(psxANDI) { dloadRt(regs_, _Rt_, _u32(_rRs_) & _ImmU_); } // Rt = Rs And Im | |
489 | OP(psxORI) { dloadRt(regs_, _Rt_, _u32(_rRs_) | _ImmU_); } // Rt = Rs Or Im | |
490 | OP(psxXORI) { dloadRt(regs_, _Rt_, _u32(_rRs_) ^ _ImmU_); } // Rt = Rs Xor Im | |
491 | OP(psxSLTI) { dloadRt(regs_, _Rt_, _i32(_rRs_) < _Imm_ ); } // Rt = Rs < Im (Signed) | |
492 | OP(psxSLTIU) { dloadRt(regs_, _Rt_, _u32(_rRs_) < ((u32)_Imm_)); } // Rt = Rs < Im (Unsigned) | |
ef79bbde P |
493 | |
494 | /********************************************************* | |
495 | * Register arithmetic * | |
496 | * Format: OP rd, rs, rt * | |
497 | *********************************************************/ | |
f9ae4f29 | 498 | OP(psxADD) { addExc (regs_, _Rd_, _i32(_rRs_), _i32(_rRt_)); } // Rd = Rs + Rt (Exception on Integer Overflow) |
499 | OP(psxSUB) { subExc (regs_, _Rd_, _i32(_rRs_), _i32(_rRt_)); } // Rd = Rs - Rt (Exception on Integer Overflow) | |
500 | OP(psxADDU) { dloadRt(regs_, _Rd_, _u32(_rRs_) + _u32(_rRt_)); } // Rd = Rs + Rt | |
501 | OP(psxSUBU) { dloadRt(regs_, _Rd_, _u32(_rRs_) - _u32(_rRt_)); } // Rd = Rs - Rt | |
502 | OP(psxAND) { dloadRt(regs_, _Rd_, _u32(_rRs_) & _u32(_rRt_)); } // Rd = Rs And Rt | |
503 | OP(psxOR) { dloadRt(regs_, _Rd_, _u32(_rRs_) | _u32(_rRt_)); } // Rd = Rs Or Rt | |
504 | OP(psxXOR) { dloadRt(regs_, _Rd_, _u32(_rRs_) ^ _u32(_rRt_)); } // Rd = Rs Xor Rt | |
505 | OP(psxNOR) { dloadRt(regs_, _Rd_, ~_u32(_rRs_ | _u32(_rRt_))); } // Rd = Rs Nor Rt | |
506 | OP(psxSLT) { dloadRt(regs_, _Rd_, _i32(_rRs_) < _i32(_rRt_)); } // Rd = Rs < Rt (Signed) | |
507 | OP(psxSLTU) { dloadRt(regs_, _Rd_, _u32(_rRs_) < _u32(_rRt_)); } // Rd = Rs < Rt (Unsigned) | |
ef79bbde P |
508 | |
509 | /********************************************************* | |
510 | * Register mult/div & Register trap logic * | |
511 | * Format: OP rs, rt * | |
512 | *********************************************************/ | |
4cc373dd | 513 | OP(psxDIV) { |
514 | if (!_rRt_) { | |
515 | _rHi_ = _rRs_; | |
516 | if (_rRs_ & 0x80000000) { | |
517 | _rLo_ = 1; | |
518 | } else { | |
519 | _rLo_ = 0xFFFFFFFF; | |
520 | } | |
521 | } | |
a4ae3997 | 522 | #if !defined(__arm__) && !defined(__aarch64__) |
4cc373dd | 523 | else if (_rRs_ == 0x80000000 && _rRt_ == 0xFFFFFFFF) { |
524 | _rLo_ = 0x80000000; | |
525 | _rHi_ = 0; | |
526 | } | |
a4ae3997 | 527 | #endif |
4cc373dd | 528 | else { |
529 | _rLo_ = _i32(_rRs_) / _i32(_rRt_); | |
530 | _rHi_ = _i32(_rRs_) % _i32(_rRt_); | |
531 | } | |
ef79bbde P |
532 | } |
533 | ||
4cc373dd | 534 | OP(psxDIV_stall) { |
535 | regs_->muldivBusyCycle = regs_->cycle + 37; | |
536 | psxDIV(regs_, code); | |
32631e6a | 537 | } |
538 | ||
4cc373dd | 539 | OP(psxDIVU) { |
ef79bbde P |
540 | if (_rRt_ != 0) { |
541 | _rLo_ = _rRs_ / _rRt_; | |
542 | _rHi_ = _rRs_ % _rRt_; | |
543 | } | |
c7a56f4f | 544 | else { |
4cc373dd | 545 | _rLo_ = 0xffffffff; |
546 | _rHi_ = _rRs_; | |
c7a56f4f | 547 | } |
ef79bbde P |
548 | } |
549 | ||
4cc373dd | 550 | OP(psxDIVU_stall) { |
551 | regs_->muldivBusyCycle = regs_->cycle + 37; | |
552 | psxDIVU(regs_, code); | |
32631e6a | 553 | } |
554 | ||
4cc373dd | 555 | OP(psxMULT) { |
556 | u64 res = (s64)_i32(_rRs_) * _i32(_rRt_); | |
ef79bbde | 557 | |
4cc373dd | 558 | regs_->GPR.n.lo = (u32)res; |
559 | regs_->GPR.n.hi = (u32)(res >> 32); | |
ef79bbde P |
560 | } |
561 | ||
4cc373dd | 562 | OP(psxMULT_stall) { |
32631e6a | 563 | // approximate, but maybe good enough |
564 | u32 rs = _rRs_; | |
565 | u32 lz = __builtin_clz(((rs ^ ((s32)rs >> 21)) | 1)); | |
566 | u32 c = 7 + (2 - (lz / 11)) * 4; | |
4cc373dd | 567 | regs_->muldivBusyCycle = regs_->cycle + c; |
568 | psxMULT(regs_, code); | |
32631e6a | 569 | } |
570 | ||
4cc373dd | 571 | OP(psxMULTU) { |
572 | u64 res = (u64)_u32(_rRs_) * _u32(_rRt_); | |
ef79bbde | 573 | |
4cc373dd | 574 | regs_->GPR.n.lo = (u32)(res & 0xffffffff); |
575 | regs_->GPR.n.hi = (u32)((res >> 32) & 0xffffffff); | |
ef79bbde P |
576 | } |
577 | ||
4cc373dd | 578 | OP(psxMULTU_stall) { |
32631e6a | 579 | // approximate, but maybe good enough |
580 | u32 lz = __builtin_clz(_rRs_ | 1); | |
581 | u32 c = 7 + (2 - (lz / 11)) * 4; | |
4cc373dd | 582 | regs_->muldivBusyCycle = regs_->cycle + c; |
583 | psxMULTU(regs_, code); | |
32631e6a | 584 | } |
585 | ||
ef79bbde P |
586 | /********************************************************* |
587 | * Register branch logic * | |
588 | * Format: OP rs, offset * | |
589 | *********************************************************/ | |
bc7c5acb | 590 | #define BrCond(c) (c) ? R3000A_BRANCH_TAKEN : R3000A_BRANCH_NOT_TAKEN |
19fae7fc | 591 | #define RepZBranchi32(op) \ |
bc7c5acb | 592 | doBranch(regs_, _BranchTarget_, BrCond(_i32(_rRs_) op 0)); |
19fae7fc | 593 | #define RepZBranchLinki32(op) { \ |
594 | s32 temp = _i32(_rRs_); \ | |
bc7c5acb | 595 | dloadFlush(regs_); \ |
19fae7fc | 596 | _SetLink(31); \ |
bc7c5acb | 597 | doBranch(regs_, _BranchTarget_, BrCond(temp op 0)); \ |
19fae7fc | 598 | } |
ef79bbde | 599 | |
4cc373dd | 600 | OP(psxBGEZ) { RepZBranchi32(>=) } // Branch if Rs >= 0 |
601 | OP(psxBGEZAL) { RepZBranchLinki32(>=) } // Branch if Rs >= 0 and link | |
602 | OP(psxBGTZ) { RepZBranchi32(>) } // Branch if Rs > 0 | |
603 | OP(psxBLEZ) { RepZBranchi32(<=) } // Branch if Rs <= 0 | |
604 | OP(psxBLTZ) { RepZBranchi32(<) } // Branch if Rs < 0 | |
605 | OP(psxBLTZAL) { RepZBranchLinki32(<) } // Branch if Rs < 0 and link | |
ef79bbde P |
606 | |
607 | /********************************************************* | |
608 | * Shift arithmetic with constant shift * | |
609 | * Format: OP rd, rt, sa * | |
610 | *********************************************************/ | |
f9ae4f29 | 611 | OP(psxSLL) { dloadRt(regs_, _Rd_, _u32(_rRt_) << _Sa_); } // Rd = Rt << sa |
612 | OP(psxSRA) { dloadRt(regs_, _Rd_, _i32(_rRt_) >> _Sa_); } // Rd = Rt >> sa (arithmetic) | |
613 | OP(psxSRL) { dloadRt(regs_, _Rd_, _u32(_rRt_) >> _Sa_); } // Rd = Rt >> sa (logical) | |
ef79bbde P |
614 | |
615 | /********************************************************* | |
616 | * Shift arithmetic with variant register shift * | |
617 | * Format: OP rd, rt, rs * | |
618 | *********************************************************/ | |
f9ae4f29 | 619 | OP(psxSLLV) { dloadRt(regs_, _Rd_, _u32(_rRt_) << (_u32(_rRs_) & 0x1F)); } // Rd = Rt << rs |
620 | OP(psxSRAV) { dloadRt(regs_, _Rd_, _i32(_rRt_) >> (_u32(_rRs_) & 0x1F)); } // Rd = Rt >> rs (arithmetic) | |
621 | OP(psxSRLV) { dloadRt(regs_, _Rd_, _u32(_rRt_) >> (_u32(_rRs_) & 0x1F)); } // Rd = Rt >> rs (logical) | |
ef79bbde P |
622 | |
623 | /********************************************************* | |
624 | * Load higher 16 bits of the first word in GPR with imm * | |
625 | * Format: OP rt, immediate * | |
626 | *********************************************************/ | |
f9ae4f29 | 627 | OP(psxLUI) { dloadRt(regs_, _Rt_, code << 16); } // Upper halfword of Rt = Im |
ef79bbde P |
628 | |
629 | /********************************************************* | |
630 | * Move from HI/LO to GPR * | |
631 | * Format: OP rd * | |
632 | *********************************************************/ | |
f9ae4f29 | 633 | OP(psxMFHI) { dloadRt(regs_, _Rd_, _rHi_); } // Rd = Hi |
634 | OP(psxMFLO) { dloadRt(regs_, _Rd_, _rLo_); } // Rd = Lo | |
ef79bbde | 635 | |
4cc373dd | 636 | static void mflohiCheckStall(psxRegisters *regs_) |
32631e6a | 637 | { |
4cc373dd | 638 | u32 left = regs_->muldivBusyCycle - regs_->cycle; |
32631e6a | 639 | if (left <= 37) { |
640 | //printf("muldiv stall %u\n", left); | |
4cc373dd | 641 | regs_->cycle = regs_->muldivBusyCycle; |
32631e6a | 642 | } |
643 | } | |
644 | ||
4cc373dd | 645 | OP(psxMFHI_stall) { mflohiCheckStall(regs_); psxMFHI(regs_, code); } |
646 | OP(psxMFLO_stall) { mflohiCheckStall(regs_); psxMFLO(regs_, code); } | |
32631e6a | 647 | |
ef79bbde P |
648 | /********************************************************* |
649 | * Move to GPR to HI/LO & Register jump * | |
650 | * Format: OP rs * | |
651 | *********************************************************/ | |
4cc373dd | 652 | OP(psxMTHI) { _rHi_ = _rRs_; } // Hi = Rs |
653 | OP(psxMTLO) { _rLo_ = _rRs_; } // Lo = Rs | |
ef79bbde P |
654 | |
655 | /********************************************************* | |
656 | * Special purpose instructions * | |
657 | * Format: OP * | |
658 | *********************************************************/ | |
4cc373dd | 659 | OP(psxBREAK) { |
bc7c5acb | 660 | intExceptionInsn(regs_, R3000E_Bp << 2); |
ef79bbde P |
661 | } |
662 | ||
4cc373dd | 663 | OP(psxSYSCALL) { |
bc7c5acb | 664 | intExceptionInsn(regs_, R3000E_Syscall << 2); |
ef79bbde P |
665 | } |
666 | ||
905b7c25 | 667 | static inline void execI_(u8 **memRLUT, psxRegisters *regs_); |
668 | ||
669 | static inline void psxTestSWInts(psxRegisters *regs_, int step) { | |
bc7c5acb | 670 | if ((regs_->CP0.n.Cause & regs_->CP0.n.SR & 0x0300) && |
671 | (regs_->CP0.n.SR & 0x1)) { | |
905b7c25 | 672 | if (step) |
673 | execI_(psxMemRLUT, regs_); | |
4cc373dd | 674 | regs_->CP0.n.Cause &= ~0x7c; |
f9ae4f29 | 675 | intException(regs_, regs_->pc, regs_->CP0.n.Cause); |
4cc373dd | 676 | } |
677 | } | |
678 | ||
679 | OP(psxRFE) { | |
bc7c5acb | 680 | regs_->CP0.n.SR = (regs_->CP0.n.SR & ~0x0f) | ((regs_->CP0.n.SR & 0x3c) >> 2); |
905b7c25 | 681 | psxTestSWInts(regs_, 0); |
ef79bbde P |
682 | } |
683 | ||
684 | /********************************************************* | |
685 | * Register branch logic * | |
686 | * Format: OP rs, rt, offset * | |
687 | *********************************************************/ | |
bc7c5acb | 688 | #define RepBranchi32(op) \ |
689 | doBranch(regs_, _BranchTarget_, BrCond(_i32(_rRs_) op _i32(_rRt_))); | |
ef79bbde | 690 | |
4cc373dd | 691 | OP(psxBEQ) { RepBranchi32(==) } // Branch if Rs == Rt |
692 | OP(psxBNE) { RepBranchi32(!=) } // Branch if Rs != Rt | |
ef79bbde P |
693 | |
694 | /********************************************************* | |
695 | * Jump to target * | |
696 | * Format: OP target * | |
697 | *********************************************************/ | |
bc7c5acb | 698 | OP(psxJ) { doBranch(regs_, _JumpTarget_, R3000A_BRANCH_TAKEN); } |
699 | OP(psxJAL) { | |
700 | dloadFlush(regs_); | |
701 | _SetLink(31); | |
702 | doBranch(regs_, _JumpTarget_, R3000A_BRANCH_TAKEN); | |
703 | } | |
ef79bbde P |
704 | |
705 | /********************************************************* | |
706 | * Register jump * | |
707 | * Format: OP rs, rd * | |
708 | *********************************************************/ | |
4cc373dd | 709 | OP(psxJR) { |
b9698f9d | 710 | doBranchReg(regs_, _rRs_); |
ef79bbde P |
711 | psxJumpTest(); |
712 | } | |
713 | ||
bc7c5acb | 714 | OP(psxJRe) { |
715 | doBranchRegE(regs_, _rRs_); | |
716 | psxJumpTest(); | |
717 | } | |
718 | ||
4cc373dd | 719 | OP(psxJALR) { |
ef79bbde | 720 | u32 temp = _u32(_rRs_); |
bc7c5acb | 721 | dloadFlush(regs_); |
ef79bbde | 722 | if (_Rd_) { _SetLink(_Rd_); } |
b9698f9d | 723 | doBranchReg(regs_, temp); |
ef79bbde P |
724 | } |
725 | ||
bc7c5acb | 726 | OP(psxJALRe) { |
727 | u32 temp = _u32(_rRs_); | |
728 | dloadFlush(regs_); | |
729 | if (_Rd_) { _SetLink(_Rd_); } | |
730 | doBranchRegE(regs_, temp); | |
731 | } | |
732 | ||
ef79bbde | 733 | /********************************************************* |
ef79bbde P |
734 | *********************************************************/ |
735 | ||
bc7c5acb | 736 | // revisit: incomplete |
737 | #define BUS_LOCKED_ADDR(a) \ | |
738 | ((0x1fc80000u <= (a) && (a) < 0x80000000u) || \ | |
739 | (0xc0000000u <= (a) && (a) < 0xfffe0000u)) | |
740 | ||
741 | // exception checking order is important | |
742 | static inline int checkLD(psxRegisters *regs, u32 addr, u32 m) { | |
743 | int bpException = 0; | |
744 | if (unlikely(DBR_EN_LD(regs->CP0.n.DCIC, addr) && | |
745 | ((addr ^ regs->CP0.n.BDA) & regs->CP0.n.BDAM) == 0)) { | |
746 | regs->CP0.n.DCIC |= 0x0d; | |
747 | bpException = regs->CP0.n.DCIC >> 31; | |
748 | } | |
f9ae4f29 | 749 | if (unlikely(addr & m)) { |
bc7c5acb | 750 | regs->CP0.n.BadVAddr = addr; |
751 | intExceptionInsn(regs, R3000E_AdEL << 2); | |
752 | return 0; | |
753 | } | |
754 | if (unlikely(bpException)) { | |
755 | intExceptionDebugBp(regs, regs->pc - 4); | |
756 | return 0; | |
757 | } | |
758 | if (unlikely(BUS_LOCKED_ADDR(addr))) { | |
26f32f12 | 759 | log_unhandled("bus error read addr=%08x @%08x ra=%08x\n", |
760 | addr, regs->pc - 4, regs->GPR.n.ra); | |
bc7c5acb | 761 | intException(regs, regs->pc - 4, R3000E_DBE << 2); |
f9ae4f29 | 762 | return 0; |
f9ae4f29 | 763 | } |
764 | return 1; | |
765 | } | |
766 | ||
bc7c5acb | 767 | static inline int checkST(psxRegisters *regs, u32 addr, u32 m) { |
768 | int bpException = 0; | |
769 | if (unlikely(DBR_EN_ST(regs->CP0.n.DCIC, addr) && | |
770 | ((addr ^ regs->CP0.n.BDA) & regs->CP0.n.BDAM) == 0)) { | |
771 | regs->CP0.n.DCIC |= 0x15; | |
772 | bpException = regs->CP0.n.DCIC >> 31; | |
773 | } | |
f9ae4f29 | 774 | if (unlikely(addr & m)) { |
bc7c5acb | 775 | regs->CP0.n.BadVAddr = addr; |
776 | intExceptionInsn(regs, R3000E_AdES << 2); | |
777 | return 0; | |
778 | } | |
779 | if (unlikely(bpException)) { | |
780 | intExceptionDebugBp(regs, regs->pc - 4); | |
781 | return 0; | |
782 | } | |
783 | if (unlikely(BUS_LOCKED_ADDR(addr))) { | |
26f32f12 | 784 | log_unhandled("bus error write addr=%08x @%08x ra=%08x\n", |
785 | addr, regs->pc - 4, regs->GPR.n.ra); | |
bc7c5acb | 786 | intException(regs, regs->pc - 4, R3000E_DBE << 2); |
f9ae4f29 | 787 | return 0; |
f9ae4f29 | 788 | } |
789 | return 1; | |
790 | } | |
791 | ||
792 | /********************************************************* | |
793 | * Load and store for GPR * | |
794 | * Format: OP rt, offset(base) * | |
795 | *********************************************************/ | |
796 | ||
bc7c5acb | 797 | /********************************************************* |
798 | * Load and store for GPR * | |
799 | * Format: OP rt, offset(base) * | |
800 | *********************************************************/ | |
801 | ||
4cc373dd | 802 | #define _oB_ (regs_->GPR.r[_Rs_] + _Imm_) |
ef79bbde | 803 | |
bc7c5acb | 804 | OP(psxLB) { doLoad(regs_, _Rt_, (s8)psxMemRead8(_oB_)); } |
805 | OP(psxLBU) { doLoad(regs_, _Rt_, psxMemRead8(_oB_)); } | |
806 | OP(psxLH) { doLoad(regs_, _Rt_, (s16)psxMemRead16(_oB_ & ~1)); } | |
807 | OP(psxLHU) { doLoad(regs_, _Rt_, psxMemRead16(_oB_ & ~1)); } | |
808 | OP(psxLW) { doLoad(regs_, _Rt_, psxMemRead32(_oB_ & ~3)); } | |
ef79bbde | 809 | |
bc7c5acb | 810 | OP(psxLBe) { if (checkLD(regs_, _oB_, 0)) doLoad(regs_, _Rt_, (s8)psxMemRead8(_oB_)); } |
811 | OP(psxLBUe) { if (checkLD(regs_, _oB_, 0)) doLoad(regs_, _Rt_, psxMemRead8(_oB_)); } | |
812 | OP(psxLHe) { if (checkLD(regs_, _oB_, 1)) doLoad(regs_, _Rt_, (s16)psxMemRead16(_oB_)); } | |
813 | OP(psxLHUe) { if (checkLD(regs_, _oB_, 1)) doLoad(regs_, _Rt_, psxMemRead16(_oB_)); } | |
814 | OP(psxLWe) { if (checkLD(regs_, _oB_, 3)) doLoad(regs_, _Rt_, psxMemRead32(_oB_)); } | |
815 | ||
816 | static void doLWL(psxRegisters *regs, u32 rt, u32 addr) { | |
4cc373dd | 817 | static const u32 LWL_MASK[4] = { 0xffffff, 0xffff, 0xff, 0 }; |
818 | static const u32 LWL_SHIFT[4] = { 24, 16, 8, 0 }; | |
ef79bbde | 819 | u32 shift = addr & 3; |
bc7c5acb | 820 | u32 val, mem; |
821 | u32 oldval = regs->GPR.r[rt]; | |
ef79bbde | 822 | |
f9ae4f29 | 823 | #ifdef HANDLE_LOAD_DELAY |
bc7c5acb | 824 | int sel = regs->dloadSel; |
825 | if (regs->dloadReg[sel] == rt) | |
826 | oldval = regs->dloadVal[sel]; | |
f9ae4f29 | 827 | #endif |
bc7c5acb | 828 | mem = psxMemRead32(addr & ~3); |
f9ae4f29 | 829 | val = (oldval & LWL_MASK[shift]) | (mem << LWL_SHIFT[shift]); |
bc7c5acb | 830 | doLoad(regs, rt, val); |
ef79bbde P |
831 | |
832 | /* | |
833 | Mem = 1234. Reg = abcd | |
834 | ||
835 | 0 4bcd (mem << 24) | (reg & 0x00ffffff) | |
836 | 1 34cd (mem << 16) | (reg & 0x0000ffff) | |
837 | 2 234d (mem << 8) | (reg & 0x000000ff) | |
838 | 3 1234 (mem ) | (reg & 0x00000000) | |
839 | */ | |
840 | } | |
841 | ||
bc7c5acb | 842 | static void doLWR(psxRegisters *regs, u32 rt, u32 addr) { |
4cc373dd | 843 | static const u32 LWR_MASK[4] = { 0, 0xff000000, 0xffff0000, 0xffffff00 }; |
844 | static const u32 LWR_SHIFT[4] = { 0, 8, 16, 24 }; | |
ef79bbde | 845 | u32 shift = addr & 3; |
bc7c5acb | 846 | u32 val, mem; |
847 | u32 oldval = regs->GPR.r[rt]; | |
ef79bbde | 848 | |
f9ae4f29 | 849 | #ifdef HANDLE_LOAD_DELAY |
bc7c5acb | 850 | int sel = regs->dloadSel; |
851 | if (regs->dloadReg[sel] == rt) | |
852 | oldval = regs->dloadVal[sel]; | |
f9ae4f29 | 853 | #endif |
bc7c5acb | 854 | mem = psxMemRead32(addr & ~3); |
f9ae4f29 | 855 | val = (oldval & LWR_MASK[shift]) | (mem >> LWR_SHIFT[shift]); |
bc7c5acb | 856 | doLoad(regs, rt, val); |
ef79bbde P |
857 | |
858 | /* | |
859 | Mem = 1234. Reg = abcd | |
860 | ||
861 | 0 1234 (mem ) | (reg & 0x00000000) | |
862 | 1 a123 (mem >> 8) | (reg & 0xff000000) | |
863 | 2 ab12 (mem >> 16) | (reg & 0xffff0000) | |
864 | 3 abc1 (mem >> 24) | (reg & 0xffffff00) | |
865 | */ | |
866 | } | |
867 | ||
bc7c5acb | 868 | OP(psxLWL) { doLWL(regs_, _Rt_, _oB_); } |
869 | OP(psxLWR) { doLWR(regs_, _Rt_, _oB_); } | |
ef79bbde | 870 | |
bc7c5acb | 871 | OP(psxLWLe) { if (checkLD(regs_, _oB_ & ~3, 0)) doLWL(regs_, _Rt_, _oB_); } |
872 | OP(psxLWRe) { if (checkLD(regs_, _oB_ , 0)) doLWR(regs_, _Rt_, _oB_); } | |
ef79bbde | 873 | |
20bfbac0 | 874 | OP(psxSB) { psxMemWrite8 (_oB_, _rRt_); } |
875 | OP(psxSH) { psxMemWrite16(_oB_, _rRt_); } | |
bc7c5acb | 876 | OP(psxSW) { psxMemWrite32(_oB_, _rRt_); } |
877 | ||
20bfbac0 | 878 | OP(psxSBe) { if (checkST(regs_, _oB_, 0)) psxMemWrite8 (_oB_, _rRt_); } |
879 | OP(psxSHe) { if (checkST(regs_, _oB_, 1)) psxMemWrite16(_oB_, _rRt_); } | |
bc7c5acb | 880 | OP(psxSWe) { if (checkST(regs_, _oB_, 3)) psxMemWrite32(_oB_, _rRt_); } |
881 | ||
882 | static void doSWL(psxRegisters *regs, u32 rt, u32 addr) { | |
883 | u32 val = regs->GPR.r[rt]; | |
884 | switch (addr & 3) { | |
885 | case 0: psxMemWrite8( addr , val >> 24); break; | |
886 | case 1: psxMemWrite16(addr & ~3, val >> 16); break; | |
887 | case 2: // revisit: should be a single 24bit write | |
888 | psxMemWrite16(addr & ~3, (val >> 8) & 0xffff); | |
889 | psxMemWrite8( addr , val >> 24); break; | |
890 | case 3: psxMemWrite32(addr & ~3, val); break; | |
891 | } | |
ef79bbde P |
892 | /* |
893 | Mem = 1234. Reg = abcd | |
894 | ||
895 | 0 123a (reg >> 24) | (mem & 0xffffff00) | |
896 | 1 12ab (reg >> 16) | (mem & 0xffff0000) | |
897 | 2 1abc (reg >> 8) | (mem & 0xff000000) | |
898 | 3 abcd (reg ) | (mem & 0x00000000) | |
899 | */ | |
900 | } | |
901 | ||
bc7c5acb | 902 | static void doSWR(psxRegisters *regs, u32 rt, u32 addr) { |
903 | u32 val = regs->GPR.r[rt]; | |
904 | switch (addr & 3) { | |
905 | case 0: psxMemWrite32(addr , val); break; | |
906 | case 1: // revisit: should be a single 24bit write | |
907 | psxMemWrite8 (addr , val & 0xff); | |
908 | psxMemWrite16(addr + 1, (val >> 8) & 0xffff); break; | |
909 | case 2: psxMemWrite16(addr , val & 0xffff); break; | |
910 | case 3: psxMemWrite8 (addr , val & 0xff); break; | |
911 | } | |
ef79bbde P |
912 | |
913 | /* | |
914 | Mem = 1234. Reg = abcd | |
915 | ||
916 | 0 abcd (reg ) | (mem & 0x00000000) | |
917 | 1 bcd4 (reg << 8) | (mem & 0x000000ff) | |
918 | 2 cd34 (reg << 16) | (mem & 0x0000ffff) | |
919 | 3 d234 (reg << 24) | (mem & 0x00ffffff) | |
920 | */ | |
921 | } | |
922 | ||
bc7c5acb | 923 | OP(psxSWL) { doSWL(regs_, _Rt_, _oB_); } |
924 | OP(psxSWR) { doSWR(regs_, _Rt_, _oB_); } | |
925 | ||
926 | OP(psxSWLe) { if (checkST(regs_, _oB_ & ~3, 0)) doSWL(regs_, _Rt_, _oB_); } | |
927 | OP(psxSWRe) { if (checkST(regs_, _oB_ , 0)) doSWR(regs_, _Rt_, _oB_); } | |
928 | ||
ef79bbde P |
929 | /********************************************************* |
930 | * Moves between GPR and COPx * | |
931 | * Format: OP rt, fs * | |
932 | *********************************************************/ | |
905b7c25 | 933 | OP(psxMFC0) { |
934 | u32 r = _Rd_; | |
bc7c5acb | 935 | if (unlikely(0x00000417u & (1u << r))) |
e9183d95 | 936 | intExceptionReservedInsn(regs_); |
f9ae4f29 | 937 | doLoad(regs_, _Rt_, regs_->CP0.r[r]); |
905b7c25 | 938 | } |
939 | ||
905b7c25 | 940 | static void setupCop(u32 sr); |
941 | ||
4cc373dd | 942 | void MTC0(psxRegisters *regs_, int reg, u32 val) { |
ef79bbde P |
943 | // SysPrintf("MTC0 %d: %x\n", reg, val); |
944 | switch (reg) { | |
bc7c5acb | 945 | case 12: // SR |
f707f14b | 946 | if (unlikely((regs_->CP0.n.SR ^ val) & (1u << 16))) |
679d5ee3 | 947 | psxMemOnIsolate((val >> 16) & 1); |
f707f14b | 948 | if (unlikely((regs_->CP0.n.SR ^ val) & (7u << 29))) |
905b7c25 | 949 | setupCop(val); |
bc7c5acb | 950 | regs_->CP0.n.SR = val; |
905b7c25 | 951 | psxTestSWInts(regs_, 1); |
ef79bbde P |
952 | break; |
953 | ||
954 | case 13: // Cause | |
4cc373dd | 955 | regs_->CP0.n.Cause &= ~0x0300; |
956 | regs_->CP0.n.Cause |= val & 0x0300; | |
905b7c25 | 957 | psxTestSWInts(regs_, 0); |
ef79bbde P |
958 | break; |
959 | ||
bc7c5acb | 960 | case 7: |
961 | if ((regs_->CP0.n.DCIC ^ val) & 0xff800000) | |
962 | log_unhandled("DCIC: %08x->%08x\n", regs_->CP0.n.DCIC, val); | |
f3746eea | 963 | goto default_; |
964 | case 3: | |
965 | if (regs_->CP0.n.BPC != val) | |
966 | log_unhandled("BPC: %08x->%08x\n", regs_->CP0.n.BPC, val); | |
967 | goto default_; | |
968 | ||
ef79bbde | 969 | default: |
f3746eea | 970 | default_: |
4cc373dd | 971 | regs_->CP0.r[reg] = val; |
ef79bbde P |
972 | break; |
973 | } | |
974 | } | |
975 | ||
4cc373dd | 976 | OP(psxMTC0) { MTC0(regs_, _Rd_, _u32(_rRt_)); } |
ef79bbde | 977 | |
bc7c5acb | 978 | // no exception |
979 | static inline void psxNULLne(psxRegisters *regs) { | |
980 | log_unhandled("unhandled op %08x @%08x\n", regs->code, regs->pc - 4); | |
981 | } | |
982 | ||
ef79bbde | 983 | /********************************************************* |
905b7c25 | 984 | * Unknown instruction (would generate an exception) * |
ef79bbde P |
985 | * Format: ? * |
986 | *********************************************************/ | |
905b7c25 | 987 | |
988 | OP(psxNULL) { | |
bc7c5acb | 989 | psxNULLne(regs_); |
e9183d95 | 990 | intExceptionReservedInsn(regs_); |
ef79bbde P |
991 | } |
992 | ||
905b7c25 | 993 | void gteNULL(struct psxCP2Regs *regs) { |
bc7c5acb | 994 | psxRegisters *regs_ = (psxRegisters *)((u8 *)regs - offsetof(psxRegisters, CP2)); |
995 | psxNULLne(regs_); | |
905b7c25 | 996 | } |
ef79bbde | 997 | |
4cc373dd | 998 | OP(psxSPECIAL) { |
999 | psxSPC[_Funct_](regs_, code); | |
ef79bbde P |
1000 | } |
1001 | ||
4cc373dd | 1002 | OP(psxCOP0) { |
3d1c03e7 | 1003 | u32 rs = _Rs_; |
1004 | if (rs & 0x10) { | |
1005 | u32 op2 = code & 0x1f; | |
1006 | switch (op2) { | |
1007 | case 0x01: | |
1008 | case 0x02: | |
1009 | case 0x06: | |
1010 | case 0x08: psxNULL(regs_, code); break; | |
1011 | case 0x10: psxRFE(regs_, code); break; | |
1012 | default: psxNULLne(regs_); break; | |
1013 | } | |
1014 | return; | |
1015 | } | |
1016 | switch (rs) { | |
4cc373dd | 1017 | case 0x00: psxMFC0(regs_, code); break; |
4cc373dd | 1018 | case 0x04: psxMTC0(regs_, code); break; |
3d1c03e7 | 1019 | case 0x02: // CFC |
1020 | case 0x06: psxNULL(regs_, code); break; // CTC -> exception | |
1021 | case 0x08: | |
1022 | case 0x0c: log_unhandled("BC0 %08x @%08x\n", code, regs_->pc - 4); | |
bc7c5acb | 1023 | default: psxNULLne(regs_); break; |
4cc373dd | 1024 | } |
ef79bbde P |
1025 | } |
1026 | ||
905b7c25 | 1027 | OP(psxCOP1) { |
1028 | // ??? what actually happens here? | |
bc7c5acb | 1029 | log_unhandled("COP1 %08x @%08x\n", code, regs_->pc - 4); |
905b7c25 | 1030 | } |
1031 | ||
4cc373dd | 1032 | OP(psxCOP2) { |
3d1c03e7 | 1033 | u32 rt = _Rt_, rd = _Rd_, rs = _Rs_; |
1034 | if (rs & 0x10) { | |
1035 | psxCP2[_Funct_](®s_->CP2); | |
1036 | return; | |
1037 | } | |
1038 | switch (rs) { | |
1039 | case 0x00: doLoad(regs_, rt, MFC2(®s_->CP2, rd)); break; // MFC2 | |
1040 | case 0x02: doLoad(regs_, rt, regs_->CP2C.r[rd]); break; // CFC2 | |
1041 | case 0x04: MTC2(®s_->CP2, regs_->GPR.r[rt], rd); break; // MTC2 | |
1042 | case 0x06: CTC2(®s_->CP2, regs_->GPR.r[rt], rd); break; // CTC2 | |
1043 | case 0x08: | |
1044 | case 0x0c: log_unhandled("BC2 %08x @%08x\n", code, regs_->pc - 4); | |
1045 | default: psxNULLne(regs_); break; | |
1046 | } | |
32631e6a | 1047 | } |
1048 | ||
4cc373dd | 1049 | OP(psxCOP2_stall) { |
81dbbf4c | 1050 | u32 f = _Funct_; |
32631e6a | 1051 | gteCheckStall(f); |
3d1c03e7 | 1052 | psxCOP2(regs_, code); |
4cc373dd | 1053 | } |
1054 | ||
1055 | OP(gteLWC2) { | |
1056 | MTC2(®s_->CP2, psxMemRead32(_oB_), _Rt_); | |
1057 | } | |
1058 | ||
4cc373dd | 1059 | OP(gteLWC2_stall) { |
1060 | gteCheckStall(0); | |
1061 | gteLWC2(regs_, code); | |
ef79bbde P |
1062 | } |
1063 | ||
bc7c5acb | 1064 | OP(gteLWC2e_stall) { |
1065 | gteCheckStall(0); | |
1066 | if (checkLD(regs_, _oB_, 3)) | |
1067 | MTC2(®s_->CP2, psxMemRead32(_oB_), _Rt_); | |
1068 | } | |
1069 | ||
1070 | OP(gteSWC2) { | |
1071 | psxMemWrite32(_oB_, MFC2(®s_->CP2, _Rt_)); | |
1072 | } | |
1073 | ||
4cc373dd | 1074 | OP(gteSWC2_stall) { |
1075 | gteCheckStall(0); | |
1076 | gteSWC2(regs_, code); | |
1077 | } | |
1078 | ||
bc7c5acb | 1079 | OP(gteSWC2e_stall) { |
1080 | gteCheckStall(0); | |
1081 | if (checkST(regs_, _oB_, 3)) | |
1082 | gteSWC2(regs_, code); | |
1083 | } | |
1084 | ||
905b7c25 | 1085 | OP(psxCOP3) { |
1086 | // ??? what actually happens here? | |
bc7c5acb | 1087 | log_unhandled("COP3 %08x @%08x\n", code, regs_->pc - 4); |
905b7c25 | 1088 | } |
1089 | ||
bc7c5acb | 1090 | OP(psxCOPd) { |
1091 | log_unhandled("disabled cop%d @%08x\n", (code >> 26) & 3, regs_->pc - 4); | |
905b7c25 | 1092 | #ifdef DO_EXCEPTION_RESERVEDI |
bc7c5acb | 1093 | intExceptionInsn(regs_, R3000E_CpU << 2); |
905b7c25 | 1094 | #endif |
1095 | } | |
1096 | ||
1097 | OP(psxLWCx) { | |
bc7c5acb | 1098 | log_unhandled("LWCx %08x @%08x\n", code, regs_->pc - 4); |
1099 | checkLD(regs_, _oB_, 3); | |
905b7c25 | 1100 | } |
1101 | ||
1102 | OP(psxSWCx) { | |
1103 | // does this write something to memory? | |
bc7c5acb | 1104 | log_unhandled("SWCx %08x @%08x\n", code, regs_->pc - 4); |
1105 | checkST(regs_, _oB_, 3); | |
905b7c25 | 1106 | } |
1107 | ||
4cc373dd | 1108 | OP(psxREGIMM) { |
19fae7fc | 1109 | u32 rt = _Rt_; |
1110 | switch (rt) { | |
4cc373dd | 1111 | case 0x10: psxBLTZAL(regs_, code); break; |
1112 | case 0x11: psxBGEZAL(regs_, code); break; | |
19fae7fc | 1113 | default: |
1114 | if (rt & 1) | |
1115 | psxBGEZ(regs_, code); | |
1116 | else | |
1117 | psxBLTZ(regs_, code); | |
4cc373dd | 1118 | } |
ef79bbde P |
1119 | } |
1120 | ||
4cc373dd | 1121 | OP(psxHLE) { |
905b7c25 | 1122 | u32 hleCode; |
1123 | if (unlikely(!Config.HLE)) { | |
1124 | psxSWCx(regs_, code); | |
1125 | return; | |
1126 | } | |
1127 | hleCode = code & 0x03ffffff; | |
1128 | if (hleCode >= (sizeof(psxHLEt) / sizeof(psxHLEt[0]))) { | |
1129 | psxSWCx(regs_, code); | |
1130 | return; | |
1131 | } | |
7c3332fb | 1132 | dloadFlush(regs_); |
905b7c25 | 1133 | psxHLEt[hleCode](); |
c87406ff | 1134 | regs_->branchSeen = 1; |
ef79bbde P |
1135 | } |
1136 | ||
4cc373dd | 1137 | static void (INT_ATTR *psxBSC[64])(psxRegisters *regs_, u32 code) = { |
ef79bbde P |
1138 | psxSPECIAL, psxREGIMM, psxJ , psxJAL , psxBEQ , psxBNE , psxBLEZ, psxBGTZ, |
1139 | psxADDI , psxADDIU , psxSLTI, psxSLTIU, psxANDI, psxORI , psxXORI, psxLUI , | |
bc7c5acb | 1140 | psxCOP0 , psxCOPd , psxCOP2, psxCOPd, psxNULL, psxNULL, psxNULL, psxNULL, |
1141 | psxNULL , psxNULL , psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, | |
1142 | psxLB , psxLH , psxLWL , psxLW , psxLBU , psxLHU , psxLWR , psxNULL, | |
1143 | psxSB , psxSH , psxSWL , psxSW , psxNULL, psxNULL, psxSWR , psxNULL, | |
1144 | psxLWCx , psxLWCx , gteLWC2, psxLWCx , psxNULL, psxNULL, psxNULL, psxNULL, | |
1145 | psxSWCx , psxSWCx , gteSWC2, psxHLE , psxNULL, psxNULL, psxNULL, psxNULL, | |
ef79bbde P |
1146 | }; |
1147 | ||
4cc373dd | 1148 | static void (INT_ATTR *psxSPC[64])(psxRegisters *regs_, u32 code) = { |
ef79bbde P |
1149 | psxSLL , psxNULL , psxSRL , psxSRA , psxSLLV , psxNULL , psxSRLV, psxSRAV, |
1150 | psxJR , psxJALR , psxNULL, psxNULL, psxSYSCALL, psxBREAK, psxNULL, psxNULL, | |
1151 | psxMFHI, psxMTHI , psxMFLO, psxMTLO, psxNULL , psxNULL , psxNULL, psxNULL, | |
1152 | psxMULT, psxMULTU, psxDIV , psxDIVU, psxNULL , psxNULL , psxNULL, psxNULL, | |
1153 | psxADD , psxADDU , psxSUB , psxSUBU, psxAND , psxOR , psxXOR , psxNOR , | |
1154 | psxNULL, psxNULL , psxSLT , psxSLTU, psxNULL , psxNULL , psxNULL, psxNULL, | |
1155 | psxNULL, psxNULL , psxNULL, psxNULL, psxNULL , psxNULL , psxNULL, psxNULL, | |
1156 | psxNULL, psxNULL , psxNULL, psxNULL, psxNULL , psxNULL , psxNULL, psxNULL | |
1157 | }; | |
1158 | ||
6c0eefaf | 1159 | void (*psxCP2[64])(struct psxCP2Regs *regs) = { |
3d1c03e7 | 1160 | gteNULL , gteRTPS , gteNULL , gteNULL, gteNULL, gteNULL , gteNCLIP, gteNULL, // 00 |
4cc373dd | 1161 | gteNULL , gteNULL , gteNULL , gteNULL, gteOP , gteNULL , gteNULL , gteNULL, // 08 |
1162 | gteDPCS , gteINTPL, gteMVMVA, gteNCDS, gteCDP , gteNULL , gteNCDT , gteNULL, // 10 | |
1163 | gteNULL , gteNULL , gteNULL , gteNCCS, gteCC , gteNULL , gteNCS , gteNULL, // 18 | |
1164 | gteNCT , gteNULL , gteNULL , gteNULL, gteNULL, gteNULL , gteNULL , gteNULL, // 20 | |
1165 | gteSQR , gteDCPL , gteDPCT , gteNULL, gteNULL, gteAVSZ3, gteAVSZ4, gteNULL, // 28 | |
1166 | gteRTPT , gteNULL , gteNULL , gteNULL, gteNULL, gteNULL , gteNULL , gteNULL, // 30 | |
1167 | gteNULL , gteNULL , gteNULL , gteNULL, gteNULL, gteGPF , gteGPL , gteNCCT // 38 | |
ef79bbde P |
1168 | }; |
1169 | ||
ef79bbde P |
1170 | /////////////////////////////////////////// |
1171 | ||
1172 | static int intInit() { | |
c6809aec | 1173 | intApplyConfig(); |
ef79bbde P |
1174 | return 0; |
1175 | } | |
1176 | ||
1177 | static void intReset() { | |
f9ae4f29 | 1178 | dloadClear(&psxRegs); |
3d1c03e7 | 1179 | psxRegs.subCycle = 0; |
ef79bbde P |
1180 | } |
1181 | ||
bc7c5acb | 1182 | static inline void execI_(u8 **memRLUT, psxRegisters *regs) { |
1183 | u32 pc = regs->pc; | |
905b7c25 | 1184 | |
bc7c5acb | 1185 | addCycle(regs); |
1186 | dloadStep(regs); | |
4cc373dd | 1187 | |
bc7c5acb | 1188 | regs->pc += 4; |
1189 | regs->code = fetch(regs, memRLUT, pc); | |
1190 | psxBSC[regs->code >> 26](regs, regs->code); | |
1191 | } | |
1192 | ||
1193 | static inline void execIbp(u8 **memRLUT, psxRegisters *regs) { | |
1194 | u32 pc = regs->pc; | |
1195 | ||
1196 | addCycle(regs); | |
1197 | dloadStep(regs); | |
1198 | ||
1199 | if (execBreakCheck(regs, pc)) | |
1200 | return; | |
1201 | ||
1202 | regs->pc += 4; | |
1203 | regs->code = fetch(regs, memRLUT, pc); | |
1204 | psxBSC[regs->code >> 26](regs, regs->code); | |
4cc373dd | 1205 | } |
1206 | ||
c87406ff | 1207 | static void intExecute(psxRegisters *regs) { |
4cc373dd | 1208 | u8 **memRLUT = psxMemRLUT; |
4cc373dd | 1209 | |
c87406ff | 1210 | while (!regs->stop) |
1211 | execI_(memRLUT, regs); | |
ef79bbde P |
1212 | } |
1213 | ||
c87406ff | 1214 | static void intExecuteBp(psxRegisters *regs) { |
bc7c5acb | 1215 | u8 **memRLUT = psxMemRLUT; |
bc7c5acb | 1216 | |
c87406ff | 1217 | while (!regs->stop) |
1218 | execIbp(memRLUT, regs); | |
bc7c5acb | 1219 | } |
1220 | ||
c87406ff | 1221 | static void intExecuteBlock(psxRegisters *regs, enum blockExecCaller caller) { |
4cc373dd | 1222 | u8 **memRLUT = psxMemRLUT; |
1223 | ||
c87406ff | 1224 | regs->branchSeen = 0; |
1225 | while (!regs->branchSeen) | |
1226 | execI_(memRLUT, regs); | |
ef79bbde P |
1227 | } |
1228 | ||
c87406ff | 1229 | static void intExecuteBlockBp(psxRegisters *regs, enum blockExecCaller caller) { |
0b1633d7 | 1230 | u8 **memRLUT = psxMemRLUT; |
1231 | ||
c87406ff | 1232 | regs->branchSeen = 0; |
1233 | while (!regs->branchSeen) | |
1234 | execIbp(memRLUT, regs); | |
0b1633d7 | 1235 | } |
1236 | ||
ef79bbde P |
1237 | static void intClear(u32 Addr, u32 Size) { |
1238 | } | |
1239 | ||
980f7a58 | 1240 | static void intNotify(enum R3000Anote note, void *data) { |
1241 | switch (note) { | |
f9ae4f29 | 1242 | case R3000ACPU_NOTIFY_BEFORE_SAVE: |
1243 | dloadFlush(&psxRegs); | |
1244 | break; | |
980f7a58 | 1245 | case R3000ACPU_NOTIFY_AFTER_LOAD: |
f9ae4f29 | 1246 | dloadClear(&psxRegs); |
3d1c03e7 | 1247 | psxRegs.subCycle = 0; |
bc7c5acb | 1248 | setupCop(psxRegs.CP0.n.SR); |
905b7c25 | 1249 | // fallthrough |
1250 | case R3000ACPU_NOTIFY_CACHE_ISOLATED: // Armored Core? | |
802ff0a4 | 1251 | if (fetch == fetchICache) |
1252 | memset(&ICache, 0xff, sizeof(ICache)); | |
980f7a58 | 1253 | break; |
1254 | case R3000ACPU_NOTIFY_CACHE_UNISOLATED: | |
980f7a58 | 1255 | break; |
943a507a | 1256 | } |
943a507a | 1257 | } |
1258 | ||
905b7c25 | 1259 | static void setupCop(u32 sr) |
1260 | { | |
1261 | if (sr & (1u << 29)) | |
1262 | psxBSC[17] = psxCOP1; | |
1263 | else | |
bc7c5acb | 1264 | psxBSC[17] = psxCOPd; |
905b7c25 | 1265 | if (sr & (1u << 30)) |
1266 | psxBSC[18] = Config.DisableStalls ? psxCOP2 : psxCOP2_stall; | |
1267 | else | |
bc7c5acb | 1268 | psxBSC[18] = psxCOPd; |
905b7c25 | 1269 | if (sr & (1u << 31)) |
1270 | psxBSC[19] = psxCOP3; | |
1271 | else | |
bc7c5acb | 1272 | psxBSC[19] = psxCOPd; |
905b7c25 | 1273 | } |
1274 | ||
61ad2a61 | 1275 | void intApplyConfig() { |
d5aeda23 | 1276 | int cycle_mult; |
1277 | ||
32631e6a | 1278 | assert(psxSPC[16] == psxMFHI || psxSPC[16] == psxMFHI_stall); |
1279 | assert(psxSPC[18] == psxMFLO || psxSPC[18] == psxMFLO_stall); | |
1280 | assert(psxSPC[24] == psxMULT || psxSPC[24] == psxMULT_stall); | |
1281 | assert(psxSPC[25] == psxMULTU || psxSPC[25] == psxMULTU_stall); | |
1282 | assert(psxSPC[26] == psxDIV || psxSPC[26] == psxDIV_stall); | |
1283 | assert(psxSPC[27] == psxDIVU || psxSPC[27] == psxDIVU_stall); | |
1284 | ||
1285 | if (Config.DisableStalls) { | |
1286 | psxBSC[18] = psxCOP2; | |
1287 | psxBSC[50] = gteLWC2; | |
1288 | psxBSC[58] = gteSWC2; | |
1289 | psxSPC[16] = psxMFHI; | |
1290 | psxSPC[18] = psxMFLO; | |
1291 | psxSPC[24] = psxMULT; | |
1292 | psxSPC[25] = psxMULTU; | |
1293 | psxSPC[26] = psxDIV; | |
1294 | psxSPC[27] = psxDIVU; | |
1295 | } else { | |
1296 | psxBSC[18] = psxCOP2_stall; | |
1297 | psxBSC[50] = gteLWC2_stall; | |
1298 | psxBSC[58] = gteSWC2_stall; | |
1299 | psxSPC[16] = psxMFHI_stall; | |
1300 | psxSPC[18] = psxMFLO_stall; | |
1301 | psxSPC[24] = psxMULT_stall; | |
1302 | psxSPC[25] = psxMULTU_stall; | |
1303 | psxSPC[26] = psxDIV_stall; | |
1304 | psxSPC[27] = psxDIVU_stall; | |
1305 | } | |
bc7c5acb | 1306 | setupCop(psxRegs.CP0.n.SR); |
1307 | ||
1308 | if (Config.PreciseExceptions) { | |
1309 | psxBSC[0x20] = psxLBe; | |
1310 | psxBSC[0x21] = psxLHe; | |
1311 | psxBSC[0x22] = psxLWLe; | |
1312 | psxBSC[0x23] = psxLWe; | |
1313 | psxBSC[0x24] = psxLBUe; | |
1314 | psxBSC[0x25] = psxLHUe; | |
1315 | psxBSC[0x26] = psxLWRe; | |
1316 | psxBSC[0x28] = psxSBe; | |
1317 | psxBSC[0x29] = psxSHe; | |
1318 | psxBSC[0x2a] = psxSWLe; | |
1319 | psxBSC[0x2b] = psxSWe; | |
1320 | psxBSC[0x2e] = psxSWRe; | |
1321 | psxBSC[0x32] = gteLWC2e_stall; | |
1322 | psxBSC[0x3a] = gteSWC2e_stall; | |
1323 | psxSPC[0x08] = psxJRe; | |
1324 | psxSPC[0x09] = psxJALRe; | |
1325 | psxInt.Execute = intExecuteBp; | |
0b1633d7 | 1326 | psxInt.ExecuteBlock = intExecuteBlockBp; |
bc7c5acb | 1327 | } else { |
1328 | psxBSC[0x20] = psxLB; | |
1329 | psxBSC[0x21] = psxLH; | |
1330 | psxBSC[0x22] = psxLWL; | |
1331 | psxBSC[0x23] = psxLW; | |
1332 | psxBSC[0x24] = psxLBU; | |
1333 | psxBSC[0x25] = psxLHU; | |
1334 | psxBSC[0x26] = psxLWR; | |
1335 | psxBSC[0x28] = psxSB; | |
1336 | psxBSC[0x29] = psxSH; | |
1337 | psxBSC[0x2a] = psxSWL; | |
1338 | psxBSC[0x2b] = psxSW; | |
1339 | psxBSC[0x2e] = psxSWR; | |
1340 | // LWC2, SWC2 handled by Config.DisableStalls | |
1341 | psxSPC[0x08] = psxJR; | |
1342 | psxSPC[0x09] = psxJALR; | |
1343 | psxInt.Execute = intExecute; | |
0b1633d7 | 1344 | psxInt.ExecuteBlock = intExecuteBlock; |
bc7c5acb | 1345 | } |
61ad2a61 | 1346 | |
bc7c5acb | 1347 | // the dynarec may occasionally call the interpreter, in such a case the |
61ad2a61 | 1348 | // cache won't work (cache only works right if all fetches go through it) |
802ff0a4 | 1349 | if (!Config.icache_emulation || psxCpu != &psxInt) { |
61ad2a61 | 1350 | fetch = fetchNoCache; |
802ff0a4 | 1351 | memset(&ICache, 0xff, sizeof(ICache)); |
1352 | } | |
61ad2a61 | 1353 | else |
1354 | fetch = fetchICache; | |
d5aeda23 | 1355 | |
1356 | cycle_mult = Config.cycle_multiplier_override && Config.cycle_multiplier == CYCLE_MULT_DEFAULT | |
1357 | ? Config.cycle_multiplier_override : Config.cycle_multiplier; | |
1358 | psxRegs.subCycleStep = 0x10000 * cycle_mult / 100; | |
32631e6a | 1359 | } |
1360 | ||
ef79bbde | 1361 | static void intShutdown() { |
a5cd72d0 | 1362 | dloadClear(&psxRegs); |
ef79bbde P |
1363 | } |
1364 | ||
a5cd72d0 | 1365 | // single step (may do several ops in case of a branch or load delay) |
de6dbc52 | 1366 | // called by asm/dynarec |
bc7c5acb | 1367 | void execI(psxRegisters *regs) { |
a5cd72d0 | 1368 | do { |
1369 | execIbp(psxMemRLUT, regs); | |
1370 | } while (regs->dloadReg[0] || regs->dloadReg[1]); | |
ef79bbde P |
1371 | } |
1372 | ||
1373 | R3000Acpu psxInt = { | |
1374 | intInit, | |
1375 | intReset, | |
1376 | intExecute, | |
1377 | intExecuteBlock, | |
1378 | intClear, | |
943a507a | 1379 | intNotify, |
61ad2a61 | 1380 | intApplyConfig, |
ef79bbde P |
1381 | intShutdown |
1382 | }; |