cpu.cpp 80.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/******************************************************************************/
/* Mednafen Sony PS1 Emulation Module                                         */
/******************************************************************************/
/* cpu.cpp:
**  Copyright (C) 2011-2016 Mednafen Team
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation, Inc.,
** 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#pragma GCC optimize ("unroll-loops")
Themaister's avatar
Themaister committed
23
24
25
26

#include "psx.h"
#include "cpu.h"

Libretro-Admin's avatar
Update    
Libretro-Admin committed
27
#include "../state_helpers.h"
Libretro-Admin's avatar
Libretro-Admin committed
28
#include "../math_ops.h"
29
#include "../mednafen.h"
30
#include "../mednafen-endian.h"
Libretro-Admin's avatar
Update    
Libretro-Admin committed
31

iCatButler's avatar
iCatButler committed
32
33
34
35
// iCB: PGXP STUFF
#include "../pgxp/pgxp_cpu.h"
#include "../pgxp/pgxp_gte.h"
#include "../pgxp/pgxp_main.h"
Zachary Cook's avatar
Zachary Cook committed
36
int pgxpMode = PGXP_GetModes();
37

Zachary Cook's avatar
Zachary Cook committed
38
#ifdef HAVE_LIGHTREC
Libretro-Admin's avatar
Libretro-Admin committed
39
40
41
#include <lightrec.h>
#include <unistd.h>
#include <signal.h>
Zachary Cook's avatar
Zachary Cook committed
42
43

enum DYNAREC prev_dynarec;
44
bool prev_invalidate;
Zachary Cook's avatar
Zachary Cook committed
45
extern bool psx_dynarec_invalidate;
Zachary Cook's avatar
Zachary Cook committed
46
extern uint8 psx_mmap;
Zachary Cook's avatar
Zachary Cook committed
47
static struct lightrec_state *lightrec_state;
48
uint8 next_interpreter;
Zachary Cook's avatar
Zachary Cook committed
49
#endif
50

Zachary Cook's avatar
Zachary Cook committed
51
52
53
54
55
56
57
extern bool psx_gte_overclock;
pscpu_timestamp_t PS_CPU::next_event_ts;
uint32 PS_CPU::IPCache;
uint32 PS_CPU::BIU;
bool PS_CPU::Halted;
struct PS_CPU::CP0 PS_CPU::CP0;
char PS_CPU::cache_buf[64 * 1024];
58

Themaister's avatar
Themaister committed
59
#define BIU_ENABLE_ICACHE_S1	0x00000800	// Enable I-cache, set 1
60
61
#define BIU_ICACHE_FSIZE_MASK	0x00000300      // I-cache fill size mask; 0x000 = 2 words, 0x100 = 4 words, 0x200 = 8 words, 0x300 = 16 words
#define BIU_ENABLE_DCACHE	0x00000080	// Enable D-cache
Libretro-Admin's avatar
Libretro-Admin committed
62
#define BIU_DCACHE_SCRATCHPAD	0x00000008	// Enable scratchpad RAM mode of D-cache
63
#define BIU_TAG_TEST_MODE	0x00000004	// Enable TAG test mode(IsC must be set to 1 as well presumably?)
Themaister's avatar
Themaister committed
64
#define BIU_INVALIDATE_MODE	0x00000002	// Enable Invalidate mode(IsC must be set to 1 as well presumably?)
65
66
67
68
69
70
71
72
#define BIU_LOCK_MODE		0x00000001	// Enable Lock mode(IsC must be set to 1 as well presumably?)


#if NOT_LIBRETRO
namespace MDFN_IEN_PSX
{
#endif

Themaister's avatar
Themaister committed
73
74
75

PS_CPU::PS_CPU()
{
76
 //printf("%zu\n", (size_t)((uintptr_t)ICache - (uintptr_t)this));
Themaister's avatar
Themaister committed
77

Lionel Flandrin's avatar
Lionel Flandrin committed
78
79
80
81
82
83
84
85
86
   addr_mask[0] = 0xFFFFFFFF;
   addr_mask[1] = 0xFFFFFFFF;
   addr_mask[2] = 0xFFFFFFFF;
   addr_mask[3] = 0xFFFFFFFF;
   addr_mask[4] = 0x7FFFFFFF;
   addr_mask[5] = 0x1FFFFFFF;
   addr_mask[6] = 0xFFFFFFFF;
   addr_mask[7] = 0xFFFFFFFF;

87
 Halted = false;
Themaister's avatar
Themaister committed
88

89
90
 memset(FastMap, 0, sizeof(FastMap));
 memset(DummyPage, 0xFF, sizeof(DummyPage));	// 0xFF to trigger an illegal instruction exception, so we'll know what's up when debugging.
Themaister's avatar
Themaister committed
91

92
93
 for(uint64 a = 0x00000000; a < (1ULL << 32); a += FAST_MAP_PSIZE)
  SetFastMap(DummyPage, a, FAST_MAP_PSIZE);
94

95
96
 CPUHook = NULL;
 ADDBT = NULL;
97

98
 GTE_Init();
99

100
101
102
 for(unsigned i = 0; i < 24; i++)
 {
  uint8 v = 7;
103

104
105
  if(i < 12)
   v += 4;
106

107
108
109
110
111
  if(i < 21)
   v += 3;

  MULT_Tab24[i] = v;
 }
Themaister's avatar
Themaister committed
112
113
114
115
}

PS_CPU::~PS_CPU()
{
Zachary Cook's avatar
Zachary Cook committed
116
117
118
119
#ifdef HAVE_LIGHTREC
 if (lightrec_state)
  lightrec_plugin_shutdown();
#endif
Themaister's avatar
Themaister committed
120
121
122

}

123
void PS_CPU::SetFastMap(void *region_mem, uint32 region_address, uint32 region_size)
Themaister's avatar
Themaister committed
124
{
125
126
 // FAST_MAP_SHIFT
 // FAST_MAP_PSIZE
Themaister's avatar
Themaister committed
127

128
129
130
131
 for(uint64 A = region_address; A < (uint64)region_address + region_size; A += FAST_MAP_PSIZE)
 {
  FastMap[A >> FAST_MAP_SHIFT] = ((uintptr_t)region_mem - region_address);
 }
Themaister's avatar
Themaister committed
132
133
134
135
}

INLINE void PS_CPU::RecalcIPCache(void)
{
136
 IPCache = 0;
137

138
139
 if((CP0.SR & CP0.CAUSE & 0xFF00) && (CP0.SR & 1))
  IPCache = 0x80;
Themaister's avatar
Themaister committed
140

141
142
 if(Halted)
  IPCache = 0x80;
Themaister's avatar
Themaister committed
143
144
145
146
}

void PS_CPU::SetHalt(bool status)
{
147
148
 Halted = status;
 RecalcIPCache();
Themaister's avatar
Themaister committed
149
150
}

Zachary Cook's avatar
Zachary Cook committed
151
152
const uint8_t *PSX_LoadExpansion1(void);

Themaister's avatar
Themaister committed
153
154
void PS_CPU::Power(void)
{
155
 assert(sizeof(ICache) == sizeof(ICache_Bulk));
Themaister's avatar
Themaister committed
156

157
158
159
160
 memset(GPR, 0, sizeof(GPR));
 memset(&CP0, 0, sizeof(CP0));
 LO = 0;
 HI = 0;
161

162
163
 gte_ts_done = 0;
 muldiv_ts_done = 0;
Themaister's avatar
Themaister committed
164

165
166
167
 BACKED_PC = 0xBFC00000;
 BACKED_new_PC = BACKED_PC + 4;
 BDBT = 0;
Themaister's avatar
Themaister committed
168

169
170
171
172
173
174
 BACKED_LDWhich = 0x20;
 BACKED_LDValue = 0;
 LDAbsorb = 0;
 memset(ReadAbsorb, 0, sizeof(ReadAbsorb));
 ReadAbsorbWhich = 0;
 ReadFudge = 0;
Themaister's avatar
Themaister committed
175

176
177
 CP0.SR |= (1 << 22);	// BEV
 CP0.SR |= (1 << 21);	// TS
Themaister's avatar
Themaister committed
178

179
 CP0.PRID = 0x2;
Themaister's avatar
Themaister committed
180

181
 RecalcIPCache();
Themaister's avatar
Themaister committed
182
183


184
 BIU = 0;
Themaister's avatar
Themaister committed
185

Zachary Cook's avatar
Zachary Cook committed
186
187
 memset(ScratchRAM->data8, 0, 1024);

Zachary Cook's avatar
Zachary Cook committed
188
189
 PGXP_Init();

Zachary Cook's avatar
Zachary Cook committed
190
#ifdef HAVE_LIGHTREC
191
 next_interpreter = 0;
Zachary Cook's avatar
Zachary Cook committed
192
 prev_dynarec = psx_dynarec;
193
 prev_invalidate = psx_dynarec_invalidate;
Zachary Cook's avatar
Zachary Cook committed
194
 pgxpMode = PGXP_GetModes();
Zachary Cook's avatar
Zachary Cook committed
195
196
197
 if(psx_dynarec != DYNAREC_DISABLED)
  lightrec_plugin_init();
#endif
Themaister's avatar
Themaister committed
198

199
200
201
202
203
204
 // Not quite sure about these poweron/reset values:
 for(unsigned i = 0; i < 1024; i++)
 {
  ICache[i].TV = 0x2 | ((BIU & 0x800) ? 0x0 : 0x1);
  ICache[i].Data = 0;
 }
Themaister's avatar
Themaister committed
205

206
 GTE_Power();
Themaister's avatar
Themaister committed
207
208
}

209
int PS_CPU::StateAction(StateMem *sm, const unsigned load, const bool data_only)
Themaister's avatar
Themaister committed
210
{
211
 uint32 OPM = BDBT;
Themaister's avatar
Themaister committed
212

213
214
215
216
217
218
219
220
 SFORMAT StateRegs[] =
 {
  SFARRAY32(GPR, 32),
  SFVAR(LO),
  SFVAR(HI),
  SFVAR(BACKED_PC),
  SFVAR(BACKED_new_PC),
  SFVARN(OPM, "BACKED_new_PC_mask"),
Themaister's avatar
Themaister committed
221

222
223
  SFVAR(IPCache),
  SFVAR(Halted),
Themaister's avatar
Themaister committed
224

225
226
227
  SFVAR(BACKED_LDWhich),
  SFVAR(BACKED_LDValue),
  SFVAR(LDAbsorb),
228

229
230
231
  SFVAR(next_event_ts),
  SFVAR(gte_ts_done),
  SFVAR(muldiv_ts_done),
232

233
234
  SFVAR(BIU),
  SFVAR(ICache_Bulk),
235

236
  SFVAR(CP0.Regs),
Themaister's avatar
Themaister committed
237

238
239
240
241
  SFARRAY(ReadAbsorb, 0x20),
  SFVARN(ReadAbsorb[0x20], "ReadAbsorbDummy"),
  SFVAR(ReadAbsorbWhich),
  SFVAR(ReadFudge),
Themaister's avatar
Themaister committed
242

243
  SFARRAYN(ScratchRAM->data8, 1024, "ScratchRAM.data8"),
Themaister's avatar
Themaister committed
244

245
246
247
  SFEND
 };
 int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "CPU");
Themaister's avatar
Themaister committed
248

249
 ret &= GTE_StateAction(sm, load, data_only);
Themaister's avatar
Themaister committed
250

251
252
 if(load)
 {
Zachary Cook's avatar
Zachary Cook committed
253
254
#ifdef HAVE_LIGHTREC
  if(psx_dynarec != DYNAREC_DISABLED) {
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
   if(lightrec_state) {
    /* Hack to prevent Dynarec + Runahead from causing a crash by
     * switching to lightrec interpreter after load state in bios */
    if(psx_dynarec != DYNAREC_RUN_INTERPRETER &&
       BACKED_PC >= 0xBFC00000 && BACKED_PC <= 0xBFC80000) {
     if(next_interpreter == 0) {
      log_cb(RETRO_LOG_INFO, "PC 0x%08x Dynarec using interpreter for a few "
                             "frames, avoid crash due to Runahead\n",BACKED_PC);
      lightrec_plugin_init();
     }
     /* run lightrec's interpreter for a few frames
      * 76 for NTSC, 93 for PAL seems to prevent crash at max runahead */
     next_interpreter = 93;
    } else
     lightrec_invalidate_all(lightrec_state);
   }else
Zachary Cook's avatar
Zachary Cook committed
271
272
273
    lightrec_plugin_init();
  }
#endif
274
275
276
277
278
279
  if(load < 0x939)
  {
   //
   // For compatibility with pre-0.9.39 save states.
   //
   uint32 NOPM = ~OPM;
Themaister's avatar
Themaister committed
280

281
   //printf("Old: new_PC=0x%08x, new_PC_mask=0x%08x\n", BACKED_new_PC, OPM);
Themaister's avatar
Themaister committed
282

283
   BDBT = ((NOPM << 1) | (NOPM >> 1)) & 0x3;
Themaister's avatar
Themaister committed
284

285
286
287
288
   BACKED_new_PC = (BACKED_PC & OPM) + BACKED_new_PC;
  }
  else
   BDBT = OPM;
Themaister's avatar
Themaister committed
289

290
291
  ReadAbsorbWhich &= 0x1F;
  BACKED_LDWhich %= 0x21;
Themaister's avatar
Themaister committed
292

293
294
295
  //printf("PC=0x%08x, new_PC=0x%08x, BDBT=0x%02x\n", BACKED_PC, BACKED_new_PC, BDBT);
 }
 return ret;
Themaister's avatar
Themaister committed
296
297
}

298
void PS_CPU::AssertIRQ(unsigned which, bool asserted)
Themaister's avatar
Themaister committed
299
{
300
 assert(which <= 5);
Themaister's avatar
Themaister committed
301

302
 CP0.CAUSE &= ~(1 << (10 + which));
Themaister's avatar
Themaister committed
303

304
305
 if(asserted)
  CP0.CAUSE |= 1 << (10 + which);
Themaister's avatar
Themaister committed
306

307
 RecalcIPCache();
Themaister's avatar
Themaister committed
308
309
}

310
void PS_CPU::SetBIU(uint32 val)
Themaister's avatar
Themaister committed
311
{
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
 const uint32 old_BIU = BIU;

 BIU = val & ~(0x440);

 if((BIU ^ old_BIU) & 0x800)
 {
  if(BIU & 0x800)	// ICache enabled
  {
   for(unsigned i = 0; i < 1024; i++)
    ICache[i].TV &= ~0x1;
  }
  else			// ICache disabled
  {
   for(unsigned i = 0; i < 1024; i++)
    ICache[i].TV |= 0x1;
  }
 }
Themaister's avatar
Themaister committed
329
330
}

331
332
333
334
uint32 PS_CPU::GetBIU(void)
{
 return BIU;
}
Themaister's avatar
Themaister committed
335
336

template<typename T>
337
INLINE T PS_CPU::PeekMemory(uint32 address)
Themaister's avatar
Themaister committed
338
{
339
340
 T ret;
 address &= addr_mask[address >> 29];
Themaister's avatar
Themaister committed
341

342
 if(address >= 0x1F800000 && address <= 0x1F8003FF)
Zachary Cook's avatar
Zachary Cook committed
343
  return ScratchRAM->Read<T>(address & 0x3FF);
Themaister's avatar
Themaister committed
344

345
 //assert(!(CP0.SR & 0x10000));
Themaister's avatar
Themaister committed
346

347
348
349
350
351
352
353
354
 if(sizeof(T) == 1)
  ret = PSX_MemPeek8(address);
 else if(sizeof(T) == 2)
  ret = PSX_MemPeek16(address);
 else
  ret = PSX_MemPeek32(address);

 return(ret);
Themaister's avatar
Themaister committed
355
356
}

Libretro-Admin's avatar
Libretro-Admin committed
357
358
359
template<typename T>
void PS_CPU::PokeMemory(uint32 address, T value)
{
360
 address &= addr_mask[address >> 29];
Libretro-Admin's avatar
Libretro-Admin committed
361

362
 if(address >= 0x1F800000 && address <= 0x1F8003FF)
Zachary Cook's avatar
Zachary Cook committed
363
  return ScratchRAM->Write<T>(address & 0x3FF, value);
Libretro-Admin's avatar
Libretro-Admin committed
364

365
366
367
368
369
370
 if(sizeof(T) == 1)
  PSX_MemPoke8(address, value);
 else if(sizeof(T) == 2)
  PSX_MemPoke16(address, value);
 else
  PSX_MemPoke32(address, value);
Libretro-Admin's avatar
Libretro-Admin committed
371
372
}

Themaister's avatar
Themaister committed
373
template<typename T>
374
INLINE T PS_CPU::ReadMemory(pscpu_timestamp_t &timestamp, uint32 address, bool DS24, bool LWC_timing)
Themaister's avatar
Themaister committed
375
{
376
377
378
379
 T ret;

 ReadAbsorb[ReadAbsorbWhich] = 0;
 ReadAbsorbWhich = 0;
Themaister's avatar
Themaister committed
380

381
382
383
384
#if 0
 if(MDFN_UNLIKELY(CP0.SR & 0x10000))
 {
  uint32 tmp = 0;	// TODO(someday): = open bus
Themaister's avatar
Themaister committed
385

386
  LDAbsorb = 0;
Themaister's avatar
Themaister committed
387

388
389
390
  if(BIU & (BIU_TAG_TEST_MODE | BIU_INVALIDATE_MODE | BIU_LOCK_MODE))
  {
   if(BIU & BIU_TAG_TEST_MODE)
Libretro-Admin's avatar
Libretro-Admin committed
391
   {
392
    const __ICache* const ICI = &ICache[((address & 0xFF0) >> 2)];
Themaister's avatar
Themaister committed
393

394
395
396
397
398
399
400
401
402
403
404
405
406
    tmp &= ~0x3F;	// bits 0-3 validity, bit 4 tag compare match, bit 5 forced to 0(lock bit apparently unimplemented in the PS1).

    //
    // Get validity bits.
    //
    for(unsigned i = 0; i < 4; i++)
     tmp |= (!(ICI[i].TV & 0x02)) << i;

    //
    // Tag compare.
    //
    if(!((address ^ ICI[0].TV) & 0xFFFFF000))
     tmp |= 0x10;
Libretro-Admin's avatar
Libretro-Admin committed
407
   }
408
409
410
411
412
413
414
415
416
417
418
419
  }
  else
  {
   tmp = ICache[(address & 0xFFC) >> 2].Data;
  }

  return tmp >> ((address & 0x3) * 8);
 }
#endif
 //
 //
 //
Themaister's avatar
Themaister committed
420

421
 address &= addr_mask[address >> 29];
Themaister's avatar
Themaister committed
422

423
424
425
 if(address >= 0x1F800000 && address <= 0x1F8003FF)
 {
  LDAbsorb = 0;
Themaister's avatar
Themaister committed
426

427
  if(DS24)
Zachary Cook's avatar
Zachary Cook committed
428
   return ScratchRAM->ReadU24(address & 0x3FF);
429
  else
Zachary Cook's avatar
Zachary Cook committed
430
   return ScratchRAM->Read<T>(address & 0x3FF);
431
 }
Themaister's avatar
Themaister committed
432

433
 timestamp += (ReadFudge >> 4) & 2;
Themaister's avatar
Themaister committed
434

435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
 //assert(!(CP0.SR & 0x10000));

 pscpu_timestamp_t lts = timestamp;

 if(sizeof(T) == 1)
  ret = PSX_MemRead8(lts, address);
 else if(sizeof(T) == 2)
  ret = PSX_MemRead16(lts, address);
 else
 {
  if(DS24)
   ret = PSX_MemRead24(lts, address) & 0xFFFFFF;
  else
   ret = PSX_MemRead32(lts, address);
 }

 if(LWC_timing)
  lts += 1;
 else
  lts += 2;
Themaister's avatar
Themaister committed
455

456
457
 LDAbsorb = (lts - timestamp);
 timestamp = lts;
Themaister's avatar
Themaister committed
458

459
 return(ret);
Themaister's avatar
Themaister committed
460
461
462
}

template<typename T>
463
INLINE void PS_CPU::WriteMemory(pscpu_timestamp_t &timestamp, uint32 address, uint32 value, bool DS24)
Themaister's avatar
Themaister committed
464
{
465
466
467
468
469
470
471
 if(MDFN_LIKELY(!(CP0.SR & 0x10000)))
 {
  address &= addr_mask[address >> 29];

  if(address >= 0x1F800000 && address <= 0x1F8003FF)
  {
   if(DS24)
Zachary Cook's avatar
Zachary Cook committed
472
    ScratchRAM->WriteU24(address & 0x3FF, value);
473
   else
Zachary Cook's avatar
Zachary Cook committed
474
    ScratchRAM->Write<T>(address & 0x3FF, value);
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495

   return;
  }

  if(sizeof(T) == 1)
   PSX_MemWrite8(timestamp, address, value);
  else if(sizeof(T) == 2)
   PSX_MemWrite16(timestamp, address, value);
  else
  {
   if(DS24)
    PSX_MemWrite24(timestamp, address, value);
   else
    PSX_MemWrite32(timestamp, address, value);
  }
 }
 else
 {
  if(BIU & BIU_ENABLE_ICACHE_S1)	// Instruction cache is enabled/active
  {
   if(BIU & (BIU_TAG_TEST_MODE | BIU_INVALIDATE_MODE | BIU_LOCK_MODE))
Themaister's avatar
Themaister committed
496
   {
497
498
    const uint8 valid_bits = (BIU & BIU_TAG_TEST_MODE) ? ((value << ((address & 0x3) * 8)) & 0x0F) : 0x00;
    __ICache* const ICI = &ICache[((address & 0xFF0) >> 2)];
Libretro-Admin's avatar
Libretro-Admin committed
499

500
501
502
503
504
    //
    // Set validity bits and tag.
    //
    for(unsigned i = 0; i < 4; i++)
     ICI[i].TV = ((valid_bits & (1U << i)) ? 0x00 : 0x02) | (address & 0xFFFFFFF0) | (i << 2);
Themaister's avatar
Themaister committed
505
   }
Libretro-Admin's avatar
Libretro-Admin committed
506
   else
Themaister's avatar
Themaister committed
507
   {
508
    ICache[(address & 0xFFC) >> 2].Data = value << ((address & 0x3) * 8);
Themaister's avatar
Themaister committed
509
   }
510
511
512
513
514
  }

  if((BIU & 0x081) == 0x080)	// Writes to the scratchpad(TODO test)
  {
   if(DS24)
Zachary Cook's avatar
Zachary Cook committed
515
    ScratchRAM->WriteU24(address & 0x3FF, value);
516
   else
Zachary Cook's avatar
Zachary Cook committed
517
    ScratchRAM->Write<T>(address & 0x3FF, value);
518
519
520
  }
  //printf("IsC WRITE%d 0x%08x 0x%08x -- CP0.SR=0x%08x\n", (int)sizeof(T), address, value, CP0.SR);
 }
Themaister's avatar
Themaister committed
521
522
}

523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
//
// ICache emulation here is not very accurate.  More accurate emulation had about a 6% performance penalty for simple
// code that just looped infinitely, with no tangible known benefit for commercially-released games.
//
// We do emulate the tag test mode functionality in regards to loading custom tag, valid bits, and instruction word data, as it could
// hypothetically be useful for homebrew.  However, due to inaccuracies, it's possible to load a tag for an address in the non-cached
// 0xA0000000-0xBFFFFFFF range, jump to the address, and have it execute out of instruction cache, which is wrong and not possible on a PS1.
//
// The other major inaccuracy here is how the region 0x80000000-0x9FFFFFFF is handled.  On a PS1, when fetching instruction word data
// from this region, the upper bit is forced to 0 before the tag compare(though the tag compare IS a full 20 bit compare),
// and this address with the upper bit set to 0 is also written to the tag on cache miss.  We don't do the address masking here,
// so in addition to the tag test mode implications, a program that jumps from somewhere in 0x00000000-0x1FFFFFFF to the corresponding
// location in 0x80000000-0x9FFFFFFF will always cause a cache miss in Mednafen.
//
// On a PS1, icache miss fill size can be programmed to 2, 4, 8, or 16 words(though 4 words is the nominally-correct setting).  We always emulate the cache
// miss fill size as 4-words.  Fill size of 8-words and 16-words are buggy on a PS1, and will write the wrong instruction data values(or read from the wrong
// addresses?) to cache when a cache miss occurs on an address that isn't aligned to a 4-word boundary.
// Fill size of 2-words seems to work on a PS1, and even behaves as if the line size is 2 words in regards to clearing
// the valid bits(when the tag matches, of course), but is obviously not very efficient unless running code that's just endless branching.
//
Libretro-Admin's avatar
Libretro-Admin committed
543
544
INLINE uint32 PS_CPU::ReadInstruction(pscpu_timestamp_t &timestamp, uint32 address)
{
545
 uint32 instr;
Libretro-Admin's avatar
Libretro-Admin committed
546

547
 instr = ICache[(address & 0xFFC) >> 2].Data;
iCatButler's avatar
iCatButler committed
548

549
550
551
552
 if(ICache[(address & 0xFFC) >> 2].TV != address)
 {
  ReadAbsorb[ReadAbsorbWhich] = 0;
  ReadAbsorbWhich = 0;
iCatButler's avatar
iCatButler committed
553

554
555
556
  if(address >= 0xA0000000 || !(BIU & 0x800))
  {
   instr = MDFN_de32lsb<true>((uint8*)(FastMap[address >> FAST_MAP_SHIFT] + address));
iCatButler's avatar
iCatButler committed
557

558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
   if (!psx_gte_overclock) {
      timestamp += 4;	// Approximate best-case cache-disabled time, per PS1 tests(executing out of 0xA0000000+); it can be 5 in *some* sequences of code(like a lot of sequential "nop"s, probably other simple instructions too).
   }
  }
  else
  {
   __ICache *ICI = &ICache[((address & 0xFF0) >> 2)];
   const uint8 *FMP = (uint8*)(FastMap[(address & 0xFFFFFFF0) >> FAST_MAP_SHIFT] + (address & 0xFFFFFFF0));

   // | 0x2 to simulate (in)validity bits.
   ICI[0x00].TV = (address & 0xFFFFFFF0) | 0x0 | 0x2;
   ICI[0x01].TV = (address & 0xFFFFFFF0) | 0x4 | 0x2;
   ICI[0x02].TV = (address & 0xFFFFFFF0) | 0x8 | 0x2;
   ICI[0x03].TV = (address & 0xFFFFFFF0) | 0xC | 0x2;

   if (!psx_gte_overclock) {
      timestamp += 3;
   }
iCatButler's avatar
iCatButler committed
576

577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
   switch(address & 0xC)
   {
    case 0x0:
        if (!psx_gte_overclock) {
           timestamp++;
        }
        ICI[0x00].TV &= ~0x2;
	ICI[0x00].Data = MDFN_de32lsb<true>(&FMP[0x0]);
    case 0x4:
        if (!psx_gte_overclock) {
           timestamp++;
        }
        ICI[0x01].TV &= ~0x2;
	ICI[0x01].Data = MDFN_de32lsb<true>(&FMP[0x4]);
    case 0x8:
        if (!psx_gte_overclock) {
           timestamp++;
        }
        ICI[0x02].TV &= ~0x2;
	ICI[0x02].Data = MDFN_de32lsb<true>(&FMP[0x8]);
    case 0xC:
        if (!psx_gte_overclock) {
           timestamp++;
        }
        ICI[0x03].TV &= ~0x2;
	ICI[0x03].Data = MDFN_de32lsb<true>(&FMP[0xC]);
	break;
   }
   instr = ICache[(address & 0xFFC) >> 2].Data;
  }
 }
Libretro-Admin's avatar
Libretro-Admin committed
608

609
 return instr;
Libretro-Admin's avatar
Libretro-Admin committed
610
611
}

612
uint32 NO_INLINE PS_CPU::Exception(uint32 code, uint32 PC, const uint32 NP, const uint32 instr)
Themaister's avatar
Themaister committed
613
{
614
 uint32 handler = 0x80000080;
Themaister's avatar
Themaister committed
615

616
 assert(code < 16);
Themaister's avatar
Themaister committed
617

618
619
620
621
622
623
624
625
626
 if(CP0.SR & (1 << 22))	// BEV
  handler = 0xBFC00180;

 CP0.EPC = PC;
 if(BDBT & 2)
 {
  CP0.EPC -= 4;
  CP0.TAR = NP;
 }
Themaister's avatar
Themaister committed
627

628
629
 if(ADDBT)
  ADDBT(PC, handler, true);
Themaister's avatar
Themaister committed
630

631
632
 // "Push" IEc and KUc(so that the new IEc and KUc are 0)
 CP0.SR = (CP0.SR & ~0x3F) | ((CP0.SR << 2) & 0x3F);
Themaister's avatar
Themaister committed
633

634
635
636
 // Setup cause register
 CP0.CAUSE &= 0x0000FF00;
 CP0.CAUSE |= code << 2;
Themaister's avatar
Themaister committed
637

638
639
 CP0.CAUSE |= BDBT << 30;
 CP0.CAUSE |= (instr << 2) & (0x3 << 28);	// CE
Themaister's avatar
Themaister committed
640

641
642
643
644
645
646
647
648
 //
 //
 //
 RecalcIPCache();

 BDBT = 0;

 return(handler);
Themaister's avatar
Themaister committed
649
650
651
652
653
654
655
656
657
658
659
660
661
662
}

#define BACKING_TO_ACTIVE			\
	PC = BACKED_PC;				\
	new_PC = BACKED_new_PC;			\
	LDWhich = BACKED_LDWhich;		\
	LDValue = BACKED_LDValue;

#define ACTIVE_TO_BACKING			\
	BACKED_PC = PC;				\
	BACKED_new_PC = new_PC;			\
	BACKED_LDWhich = LDWhich;		\
	BACKED_LDValue = LDValue;

663
664
665
//
//
#define GPR_DEPRES_BEGIN { uint8 back = ReadAbsorb[0];
666
#define GPR_DEP(n) { unsigned tn = (n); ReadAbsorb[tn] = 0; }
Themaister's avatar
Themaister committed
667
668
669
#define GPR_RES(n) { unsigned tn = (n); ReadAbsorb[tn] = 0; }
#define GPR_DEPRES_END ReadAbsorb[0] = back; }

670
671
template<bool DebugMode, bool BIOSPrintMode, bool ILHMode>
pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in)
Themaister's avatar
Themaister committed
672
{
Libretro-Admin's avatar
Libretro-Admin committed
673
 pscpu_timestamp_t timestamp = timestamp_in;
674

Libretro-Admin's avatar
Libretro-Admin committed
675
676
677
678
 uint32 PC;
 uint32 new_PC;
 uint32 LDWhich;
 uint32 LDValue;
679
680
681
682
683
684
685
686
 
 //printf("%d %d\n", gte_ts_done, muldiv_ts_done);

 gte_ts_done += timestamp;
 muldiv_ts_done += timestamp;

 BACKING_TO_ACTIVE;

Zachary Cook's avatar
Zachary Cook committed
687
688
689
690
#if defined(HAVE_LIGHTREC) && defined(LIGHTREC_DEBUG)
 u32 oldpc = PC;
#endif

691
692
693
694
695
696
697
 do
 {
  //printf("Running: %d %d\n", timestamp, next_event_ts);
  while(MDFN_LIKELY(timestamp < next_event_ts))
  {
   uint32 instr;
   uint32 opf;
698

699
700
   // Zero must be zero...until the Master Plan is enacted.
   GPR[0] = 0;
Themaister's avatar
Themaister committed
701

702
#ifdef DEBUG
703
   if(DebugMode && CPUHook)
Themaister's avatar
Themaister committed
704
   {
705
    ACTIVE_TO_BACKING;
Libretro-Admin's avatar
Libretro-Admin committed
706

707
708
709
    // For save states in step mode.
    gte_ts_done -= timestamp;
    muldiv_ts_done -= timestamp;
Libretro-Admin's avatar
Libretro-Admin committed
710

711
    CPUHook(timestamp, PC);
Libretro-Admin's avatar
Libretro-Admin committed
712

713
714
715
    // For save states in step mode.
    gte_ts_done += timestamp;
    muldiv_ts_done += timestamp;
716

717
718
    BACKING_TO_ACTIVE;
   }
719
#endif
720

721
722
723
724
725
726
727
728
729
730
731
#if NOT_LIBRETRO
   if(BIOSPrintMode)
   {
    if(PC == 0xB0)
    {
     if(MDFN_UNLIKELY(GPR[9] == 0x3D))
     {
      PSX_DBG_BIOS_PUTC(GPR[4]);
     }
    }
   }
Libretro-Admin's avatar
Libretro-Admin committed
732
#endif
Libretro-Admin's avatar
Libretro-Admin committed
733

734
735
736
737
738
739
740
741
742
743
744
   //
   // Instruction fetch
   //
   if(MDFN_UNLIKELY(PC & 0x3))
   {
    // This will block interrupt processing, but since we're going more for keeping broken homebrew/hacks from working
    // than super-duper-accurate pipeline emulation, it shouldn't be a problem.
    CP0.BADA = PC;
    new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, 0);
    goto OpDone;
   }
Libretro-Admin's avatar
Libretro-Admin committed
745

746
   instr = ReadInstruction(timestamp, PC);
Libretro-Admin's avatar
Libretro-Admin committed
747
748


749
750
751
752
   // 
   // Instruction decode
   //
   opf = instr & 0x3F;
Libretro-Admin's avatar
Libretro-Admin committed
753

754
755
   if(instr & (0x3F << 26))
    opf = 0x40 | (instr >> 26);
Themaister's avatar
Themaister committed
756

757
   opf |= IPCache;
iCatButler's avatar
iCatButler committed
758

759
760
761
762
   if(ReadAbsorb[ReadAbsorbWhich])
    ReadAbsorb[ReadAbsorbWhich]--;
   else
    timestamp++;
Themaister's avatar
Themaister committed
763

764
   #define DO_LDS() { GPR[LDWhich] = LDValue; ReadAbsorb[LDWhich] = LDAbsorb; ReadFudge = LDWhich; ReadAbsorbWhich |= LDWhich & 0x1F; LDWhich = 0x20; }
Libretro-Admin's avatar
Libretro-Admin committed
765
   #define BEGIN_OPF(name) { op_##name:
766
767
   #define END_OPF goto OpDone; }

768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
#ifdef DEBUG
#define DEBUG_ADDBT() if(DebugMode && ADDBT) { ADDBT(PC, new_PC, false); }
#define DEBUG_ILH() \
	  if(ILHMode)						\
	  {							\
	   if(old_PC == (((new_PC - 4) & mask) + offset))	\
	   {							\
	    if(MDFN_densb<uint32, true>((uint8*)(FastMap[PC >> FAST_MAP_SHIFT] + PC)) == 0)	\
	    {							\
	     if(next_event_ts > timestamp) /* Necessary since next_event_ts might be set to something like "0" to force a call to the event handler. */		\
	     {							\
	      timestamp = next_event_ts;			\
	     }							\
	    }							\
	   }							\
	  }
#else
#define DEBUG_ADDBT()
#define DEBUG_ILH()
#endif

789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
   #define DO_BRANCH(arg_cond, arg_offset, arg_mask, arg_dolink, arg_linkreg)\
	{							\
	 const bool cond = (arg_cond);				\
	 const uint32 offset = (arg_offset);			\
	 const uint32 mask = (arg_mask);			\
	 const uint32 old_PC = PC;				\
	 PC = new_PC;						\
	 new_PC += 4;						\
	 BDBT = 2;						\
								\
	 if(arg_dolink)						\
	  GPR[(arg_linkreg)] = new_PC;				\
								\
	 if(cond)						\
	 {							\
804
     DEBUG_ILH() \
805
806
	  new_PC = ((new_PC - 4) & mask) + offset;		\
	  BDBT = 3;						\
807
     DEBUG_ADDBT() \
808
809
810
	 }							\
								\
	 goto SkipNPCStuff;					\
811
812
813
814
815
816
	}

   #define ITYPE uint32 rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32 rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32 immediate = (int32)(int16)(instr & 0xFFFF); /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/
   #define ITYPE_ZE uint32 rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32 rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32 immediate = instr & 0xFFFF; /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/
   #define JTYPE uint32 target = instr & ((1 << 26) - 1); /*printf(" target=(%08x) ", target);*/
   #define RTYPE uint32 rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32 rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32 rd MDFN_NOWARN_UNUSED = (instr >> 11) & 0x1F; uint32 shamt MDFN_NOWARN_UNUSED = (instr >> 6) & 0x1F; /*printf(" rs=%02x(%08x), rt=%02x(%08x), rd=%02x(%08x) ", rs, GPR[rs], rt, GPR[rt], rd, GPR[rd]);*/
Libretro-Admin's avatar
Libretro-Admin committed
817

818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
#if HAVE_COMPUTED_GOTO
   #if 0
	//
	// These truncated 32-bit table values apparently can't be calculated at compile/link time by gcc on x86_64, so gcc inserts initialization code, but
	// the compare for the initialization code path is placed sub-optimally(near where the table value is used instead of at the beginning of the function).
	//
	#define CGBEGIN static const uint32 const op_goto_table[256] = {
	#define CGE(l) (uint32)(uintptr_t)&&l,
	#define CGEND }; goto *(void*)(uintptr_t)op_goto_table[opf];
   #else
	#define CGBEGIN static const void *const op_goto_table[256] = {
	#define CGE(l) &&l,
	#define CGEND }; goto *op_goto_table[opf];
   #endif
#else
833
834
835
836
837
838
   /* (uint8) cast for cheaper alternative to generated branch+compare bounds check instructions, but still more
      expensive than computed goto which needs no masking nor bounds checking.
   */
   #define CGBEGIN { enum { CGESB = 1 + __COUNTER__ }; switch((uint8)opf) {
   #define CGE(l) case __COUNTER__ - CGESB: goto l;
   #define CGEND } }
Libretro-Admin's avatar
Libretro-Admin committed
839
#endif
Libretro-Admin's avatar
Libretro-Admin committed
840

841
842
843
844
845
846
847
848
849
850
851
852
   CGBEGIN
    CGE(op_SLL)  CGE(op_ILL)   CGE(op_SRL)  CGE(op_SRA)   CGE(op_SLLV)    CGE(op_ILL)   CGE(op_SRLV) CGE(op_SRAV)
    CGE(op_JR)   CGE(op_JALR)  CGE(op_ILL)  CGE(op_ILL)   CGE(op_SYSCALL) CGE(op_BREAK) CGE(op_ILL)  CGE(op_ILL)
    CGE(op_MFHI) CGE(op_MTHI)  CGE(op_MFLO) CGE(op_MTLO)  CGE(op_ILL)     CGE(op_ILL)   CGE(op_ILL)  CGE(op_ILL)
    CGE(op_MULT) CGE(op_MULTU) CGE(op_DIV)  CGE(op_DIVU)  CGE(op_ILL)     CGE(op_ILL)   CGE(op_ILL)  CGE(op_ILL)
    CGE(op_ADD)  CGE(op_ADDU)  CGE(op_SUB)  CGE(op_SUBU)  CGE(op_AND)     CGE(op_OR)    CGE(op_XOR)  CGE(op_NOR)
    CGE(op_ILL)  CGE(op_ILL)   CGE(op_SLT)  CGE(op_SLTU)  CGE(op_ILL)     CGE(op_ILL)   CGE(op_ILL)  CGE(op_ILL)
    CGE(op_ILL)  CGE(op_ILL)   CGE(op_ILL)  CGE(op_ILL)   CGE(op_ILL)     CGE(op_ILL)   CGE(op_ILL)  CGE(op_ILL)
    CGE(op_ILL)  CGE(op_ILL)   CGE(op_ILL)  CGE(op_ILL)   CGE(op_ILL)     CGE(op_ILL)   CGE(op_ILL)  CGE(op_ILL)

    CGE(op_ILL)  CGE(op_BCOND) CGE(op_J)    CGE(op_JAL)   CGE(op_BEQ)  CGE(op_BNE) CGE(op_BLEZ) CGE(op_BGTZ)
    CGE(op_ADDI) CGE(op_ADDIU) CGE(op_SLTI) CGE(op_SLTIU) CGE(op_ANDI) CGE(op_ORI) CGE(op_XORI) CGE(op_LUI)
853
    CGE(op_COP0) CGE(op_COP13) CGE(op_COP2) CGE(op_COP13) CGE(op_ILL)  CGE(op_ILL) CGE(op_ILL)  CGE(op_ILL)
854
855
856
    CGE(op_ILL)  CGE(op_ILL)   CGE(op_ILL)  CGE(op_ILL)   CGE(op_ILL)  CGE(op_ILL) CGE(op_ILL)  CGE(op_ILL)
    CGE(op_LB)   CGE(op_LH)    CGE(op_LWL)  CGE(op_LW)    CGE(op_LBU)  CGE(op_LHU) CGE(op_LWR)  CGE(op_ILL)
    CGE(op_SB)   CGE(op_SH)    CGE(op_SWL)  CGE(op_SW)    CGE(op_ILL)  CGE(op_ILL) CGE(op_SWR)  CGE(op_ILL)
857
858
    CGE(op_LWC013) CGE(op_LWC013)  CGE(op_LWC2) CGE(op_LWC013)  CGE(op_ILL)  CGE(op_ILL) CGE(op_ILL)  CGE(op_ILL)
    CGE(op_SWC013) CGE(op_SWC013)  CGE(op_SWC2) CGE(op_SWC013)  CGE(op_ILL)  CGE(op_ILL) CGE(op_ILL)  CGE(op_ILL)
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879

    // Interrupt portion of this table is constructed so that an interrupt won't be taken when the PC is pointing to a GTE instruction,
    // to avoid problems caused by pipeline vs coprocessor nuances that aren't emulated.
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)

    CGE(op_ILL)       CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_COP2)      CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
    CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT)
   CGEND
Libretro-Admin's avatar
Libretro-Admin committed
880

881
   {
Libretro-Admin's avatar
Libretro-Admin committed
882
    BEGIN_OPF(ILL);
883
	     DO_LDS();
884
	     new_PC = Exception(EXCEPTION_RI, PC, new_PC, instr);
885
886
887
888
889
    END_OPF;

    //
    // ADD - Add Word
    //
Libretro-Admin's avatar
Libretro-Admin committed
890
    BEGIN_OPF(ADD);
891
892
893
894
895
896
897
898
899
900
901
	RTYPE;

	GPR_DEPRES_BEGIN
	GPR_DEP(rs);
	GPR_DEP(rt);
 	GPR_RES(rd);
	GPR_DEPRES_END

	uint32 result = GPR[rs] + GPR[rt];
	bool ep = ((~(GPR[rs] ^ GPR[rt])) & (GPR[rs] ^ result)) & 0x80000000;

iCatButler's avatar
iCatButler committed
902
903
904
	if (PGXP_GetModes() & PGXP_MODE_CPU)
		PGXP_CPU_ADD(instr, result, GPR[rs], GPR[rt]);

905
906
907
908
	DO_LDS();

	if(MDFN_UNLIKELY(ep))
	{
909
	 new_PC = Exception(EXCEPTION_OV, PC, new_PC, instr);
910
911
912
913
914
915
916
917
918
	}
	else
	 GPR[rd] = result;

    END_OPF;

    //
    // ADDI - Add Immediate Word
    //
Libretro-Admin's avatar
Libretro-Admin committed
919
    BEGIN_OPF(ADDI);
920
921
922
923
924
925
926
927
	ITYPE;

	GPR_DEPRES_BEGIN
	GPR_DEP(rs);
	GPR_RES(rt);
	GPR_DEPRES_END

        uint32 result = GPR[rs] + immediate;
928
	bool ep = ((~(GPR[rs] ^ immediate)) & (GPR[rs] ^ result)) & 0x80000000;
iCatButler's avatar
iCatButler committed
929
930
931
932

	if (PGXP_GetModes() & PGXP_MODE_CPU)
		PGXP_CPU_ADDI(instr, result, GPR[rs]);

933
934
935
936
	DO_LDS();

        if(MDFN_UNLIKELY(ep))
	{
937
	 new_PC = Exception(EXCEPTION_OV, PC, new_PC, instr);
938
939
940
941
942
943
944
945
946
	}
        else
         GPR[rt] = result;

    END_OPF;

    //
    // ADDIU - Add Immediate Unsigned Word
    //
Libretro-Admin's avatar
Libretro-Admin committed
947
    BEGIN_OPF(ADDIU);
948
949
950
951
952
953
954
955
956
	ITYPE;

	GPR_DEPRES_BEGIN
	GPR_DEP(rs);
	GPR_RES(rt);
	GPR_DEPRES_END

	uint32 result = GPR[rs] + immediate;

iCatButler's avatar
iCatButler committed
957
958
959
	if (PGXP_GetModes() & PGXP_MODE_CPU)
		PGXP_CPU_ADDIU(instr, result, GPR[rs]);

960
961
962
963
964
965
966
967
968
	DO_LDS();

	GPR[rt] = result;

    END_OPF;

    //
    // ADDU - Add Unsigned Word
    //
Libretro-Admin's avatar
Libretro-Admin committed
969
    BEGIN_OPF(ADDU);
970
971
972
973
974
975
976
977
978
979
	RTYPE;

	GPR_DEPRES_BEGIN
	GPR_DEP(rs);
	GPR_DEP(rt);
 	GPR_RES(rd);
	GPR_DEPRES_END

	uint32 result = GPR[rs] + GPR[rt];

iCatButler's avatar
iCatButler committed
980
981
982
	if (PGXP_GetModes() & PGXP_MODE_CPU)
		PGXP_CPU_ADDU(instr, result, GPR[rs], GPR[rt]);

983
984
985
986
987
988
989
990
991
	DO_LDS();

	GPR[rd] = result;

    END_OPF;

    //
    // AND - And
    //
Libretro-Admin's avatar
Libretro-Admin committed
992
    BEGIN_OPF(AND);
993
994
995
996
997
998
999
1000
1001
1002
	RTYPE;

	GPR_DEPRES_BEGIN
	GPR_DEP(rs);
	GPR_DEP(rt);
 	GPR_RES(rd);
	GPR_DEPRES_END

	uint32 result = GPR[rs] & GPR[rt];

iCatButler's avatar
iCatButler committed
1003
1004
1005
	if (PGXP_GetModes() & PGXP_MODE_CPU)
		PGXP_CPU_AND(instr, result, GPR[rs], GPR[rt]);

1006
1007
1008
1009
1010
1011
1012
1013
1014
	DO_LDS();

	GPR[rd] = result;

    END_OPF;

    //
    // ANDI - And Immediate
    //
Libretro-Admin's avatar
Libretro-Admin committed
1015
    BEGIN_OPF(ANDI);
1016
1017
1018
1019
1020
1021
1022
1023
1024
	ITYPE_ZE;

	GPR_DEPRES_BEGIN
	GPR_DEP(rs);
	GPR_RES(rt);
	GPR_DEPRES_END

	uint32 result = GPR[rs] & immediate;

iCatButler's avatar
iCatButler committed
1025
1026
1027
	if (PGXP_GetModes() & PGXP_MODE_CPU)
		PGXP_CPU_ANDI(instr, result, GPR[rs]);

1028
1029
1030
1031
1032
1033
1034
1035
1036
	DO_LDS();

	GPR[rt] = result;

    END_OPF;

    //
    // BEQ - Branch on Equal
    //
Libretro-Admin's avatar
Libretro-Admin committed
1037
    BEGIN_OPF(BEQ);
1038
1039
1040
1041
1042
1043
1044
	ITYPE;

	GPR_DEPRES_BEGIN
	GPR_DEP(rs);
	GPR_DEP(rt);
	GPR_DEPRES_END

1045
	const bool result = (GPR[rs] == GPR[rt]);
1046
1047
1048

	DO_LDS();

1049
	DO_BRANCH(result, (immediate << 2), ~0U, false, 0);
1050
1051
1052
1053
    END_OPF;

    // Bah, why does MIPS encoding have to be funky like this. :(
    // Handles BGEZ, BGEZAL, BLTZ, BLTZAL
Libretro-Admin's avatar
Libretro-Admin committed
1054
    BEGIN_OPF(BCOND);
1055
	const uint32 tv = GPR[(instr >> 21) & 0x1F];
1056
1057
1058
	const uint32 riv = (instr >> 16) & 0x1F;
	const uint32 immediate = (int32)(int16)(instr & 0xFFFF);
	const bool result = (int32)(tv ^ (riv << 31)) < 0;
1059
	const uint32 link = ((riv & 0x1E) == 0x10) ? 31 : 0;
1060
1061
1062

	GPR_DEPRES_BEGIN
	GPR_DEP((instr >> 21) & 0x1F);
1063
	GPR_RES(link);
1064
1065
1066
1067
	GPR_DEPRES_END

	DO_LDS();

1068
	DO_BRANCH(result, (immediate << 2), ~0U, true, link);
1069
1070
1071
1072
1073
1074
    END_OPF;


    //
    // BGTZ - Branch on Greater than Zero
    //
Libretro-Admin's avatar
Libretro-Admin committed
1075
    BEGIN_OPF(BGTZ);
1076
1077
1078
1079
1080
1081
	ITYPE;

	GPR_DEPRES_BEGIN
	GPR_DEP(rs);
	GPR_DEPRES_END

1082
	const bool result = (int32)GPR[rs] > 0;
1083
1084
1085

	DO_LDS();

1086
	DO_BRANCH(result, (immediate << 2), ~0U, false, 0);
1087
1088
1089
1090
1091
    END_OPF;

    //
    // BLEZ - Branch on Less Than or Equal to Zero
    //
Libretro-Admin's avatar
Libretro-Admin committed
1092
    BEGIN_OPF(BLEZ);
1093
1094
1095
1096
1097
1098
	ITYPE;

	GPR_DEPRES_BEGIN
	GPR_DEP(rs);
	GPR_DEPRES_END

1099
	const bool result = (int32)GPR[rs] <= 0;
1100
1101
1102

	DO_LDS();

1103
	DO_BRANCH(result, (immediate << 2), ~0U, false, 0);
1104
1105
1106
1107
1108
    END_OPF;

    //
    // BNE - Branch on Not Equal
    //
Libretro-Admin's avatar
Libretro-Admin committed
1109
    BEGIN_OPF(BNE);
1110
1111
1112
1113
1114
1115
1116
	ITYPE;

	GPR_DEPRES_BEGIN
	GPR_DEP(rs);
	GPR_DEP(rt);
	GPR_DEPRES_END

1117
	const bool result = GPR[rs] != GPR[rt];
1118
1119
1120

	DO_LDS();

1121
	DO_BRANCH(result, (immediate << 2), ~0U, false, 0);
1122
1123
1124
1125
1126
    END_OPF;

    //
    // BREAK - Breakpoint
    //
Libretro-Admin's avatar
Libretro-Admin committed
1127
    BEGIN_OPF(BREAK);
1128
	DO_LDS();
1129
	new_PC = Exception(EXCEPTION_BP, PC, new_PC, instr);
1130
1131
1132
1133
1134
    END_OPF;

    // Cop "instructions":	CFCz(no CP0), COPz, CTCz(no CP0), LWCz(no CP0), MFCz, MTCz, SWCz(no CP0)
    //
    // COP0 instructions
1135
    //
Libretro-Admin's avatar
Libretro-Admin committed
1136
    BEGIN_OPF(COP0);
1137
1138
1139
1140
	const uint32 sub_op = (instr >> 21) & 0x1F;
	const uint32 rt = (instr >> 16) & 0x1F;
	const uint32 rd = (instr >> 11) & 0x1F;
	const uint32 val = GPR[rt];
1141
1142

	switch(sub_op)
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165