#include "sio.h"
#include "psxhle.h"
#include "psxinterpreter.h"
+#include "psxevents.h"
#include <zlib.h>
#ifndef PSXBIOS_LOG
#define A_CD_EVENTS 0xb9b8
#define A_EXC_GP 0xf450
-#define A_A0_DUMMY 0x1010
-#define A_B0_DUMMY 0x2010
-#define A_C0_DUMMY 0x3010
-#define A_B0_5B_DUMMY 0x43d0
+#define A_A0_TRAPS 0x1010
+#define A_B0_TRAPS 0x2010
+#define A_C0_TRAPS 0x3010
+#define A_B0_5B_TRAP 0x43d0
#define HLEOP(n) SWAPu32((0x3b << 26) | (n));
ra = 0x80001000;
psxRegs.CP0.n.SR &= ~0x404; // disable interrupts
+ assert(psxRegs.cpuInRecursion <= 1);
+ psxRegs.cpuInRecursion++;
psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, PTR_1);
- while (pc0 != 0x80001000 && ++lim < 1000000)
+ while (pc0 != 0x80001000 && ++lim < 0x100000)
psxCpu->ExecuteBlock(EXEC_CALLER_HLE);
psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, PTR_1);
+ psxRegs.cpuInRecursion--;
- if (lim == 1000000)
+ if (lim == 0x100000)
PSXBIOS_LOG("softCall @%x hit lim\n", pc);
ra = sra;
psxRegs.CP0.n.SR |= ssr & 0x404;
return;
ra = 0x80001000;
+ psxRegs.cpuInRecursion++;
psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, PTR_1);
- while (!returned_from_exception() && pc0 != 0x80001000 && ++lim < 1000000)
+ while (!returned_from_exception() && pc0 != 0x80001000 && ++lim < 0x100000)
psxCpu->ExecuteBlock(EXEC_CALLER_HLE);
psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, PTR_1);
+ psxRegs.cpuInRecursion--;
- if (lim == 1000000)
+ if (lim == 0x100000)
PSXBIOS_LOG("softCallInException @%x hit lim\n", pc);
if (pc0 == 0x80001000)
ra = sra;
pc0 = ra;
}
-void psxBios_todigit(void) // 0x0a
+static u32 do_todigit(u32 c)
{
- int c = a0;
-#ifdef PSXBIOS_LOG
- PSXBIOS_LOG("psxBios_%s\n", biosA0n[0x0a]);
-#endif
c &= 0xFF;
if (c >= 0x30 && c < 0x3A) {
c -= 0x30;
c = c - 0x41 + 10;
}
else if (c >= 0x80) {
+ log_unhandled("todigit %02x\n", c);
c = -1;
}
else
{
c = 0x0098967F;
}
- v0 = c;
- pc0 = ra;
+ use_cycles(40);
+ return c;
+}
+
+static void psxBios_todigit(void) // 0x0a
+{
+ mips_return(do_todigit(a0));
+ PSXBIOS_LOG("psxBios_%s '%c' -> %u\n", biosA0n[0x0a], a0, v0);
+}
+
+static void do_strtol(char *p, void *end_, u32 base, int can_neg) {
+ s32 n = 0, f = 0, t;
+ u32 *end = end_;
+
+ use_cycles(12);
+ if (p == INVALID_PTR) {
+ mips_return(0);
+ return;
+ }
+
+ for (; (0x09 <= *p && *p <= '\r') || *p == ' '; p++)
+ use_cycles(9);
+ if (can_neg) {
+ for (; *p == '-'; f = 1, p++)
+ use_cycles(4);
+ }
+ if (base == 0 || base > 36)
+ base = 10;
+ if (*p == '0') {
+ switch (*p++) {
+ case 'b': case 'B': base = 2; break;
+ case 'x': case 'X': base = 16; break;
+ }
+ }
+ else if (*p == 'o' || *p == 'O') {
+ base = 8;
+ p++;
+ }
+
+ for (; (t = do_todigit(*p)) < base; p++) {
+ n = n * base + t;
+ use_cycles(12);
+ }
+
+ n = (f ? -n : n);
+ if (end != INVALID_PTR)
+ *end = SWAP32(a0 + (p - Ra0));
+ mips_return_c(n, 100);
+}
+
+static void psxBios_strtoul() { // 0x0c
+ do_strtol(a0 ? Ra0 : INVALID_PTR, a1 ? Ra1 : INVALID_PTR, a2, 0);
+ PSXBIOS_LOG("psxBios_%s %s (%x), %x, %x -> 0x%x\n",
+ biosA0n[0x0c], a0 ? Ra0 : NULL, a0, a1, a2, v0);
+}
+
+static void psxBios_strtol() { // 0x0d
+ do_strtol(a0 ? Ra0 : INVALID_PTR, a1 ? Ra1 : INVALID_PTR, a2, 1);
+ PSXBIOS_LOG("psxBios_%s %s (%x), %x, %x -> 0x%x\n",
+ biosA0n[0x0d], a0 ? Ra0 : NULL, a0, a1, a2, v0);
}
void psxBios_abs() { // 0x0e
}
static void psxBios_cd() { // 0x40
- const char *p, *dir = castRam8ptr(a0);
+ const char *p, *dir = Ra0;
PSXBIOS_LOG("psxBios_%s %x(%s)\n", biosB0n[0x40], a0, dir);
- if ((p = strchr(dir, ':')))
- dir = ++p;
- if (*dir == '\\')
- dir++;
- snprintf(cdir, sizeof(cdir), "%s", dir);
+ if (dir != INVALID_PTR) {
+ if ((p = strchr(dir, ':')))
+ dir = ++p;
+ if (*dir == '\\')
+ dir++;
+ snprintf(cdir, sizeof(cdir), "%s", dir);
+ }
mips_return_c(1, 100);
}
}
static void bufile(const u8 *mcd_data, u32 dir_) {
- struct DIRENTRY *dir = (struct DIRENTRY *)castRam8ptr(dir_);
+ struct DIRENTRY *dir = (struct DIRENTRY *)PSXM(dir_);
const char *pfile = ffile + 5;
const u8 *data = mcd_data;
int i = 0, match = 0;
u32 head = 0;
v0 = 0;
+ if (dir == INVALID_PTR)
+ return;
+
for (; nfile <= 15 && !match; nfile++) {
const char *name;
*/
static void psxBios_firstfile() { // 42
- char *pa0 = castRam8ptr(a0);
+ char *pa0 = Ra0;
PSXBIOS_LOG("psxBios_%s %s %x\n", biosB0n[0x42], pa0, a1);
v0 = 0;
+ if (pa0 != INVALID_PTR)
{
snprintf(ffile, sizeof(ffile), "%s", pa0);
if (ffile[5] == 0)
GPU_writeStatus(gpu_ctl_def[i]);
for (i = 0; i < sizeof(gpu_data_def) / sizeof(gpu_data_def[0]); i++)
GPU_writeData(gpu_data_def[i]);
- HW_GPU_STATUS |= SWAP32(PSXGPU_nBUSY);
// spu
for (i = 0x1f801d80; i < sizeof(spu_config) / sizeof(spu_config[0]); i++)
int i;
uLongf len;
+ psxRegs.biosBranchCheck = ~0;
+
memset(psxM, 0, 0x10000);
for(i = 0; i < 256; i++) {
biosA0[i] = NULL;
biosA0[0x09] = psxBios_putc;
biosA0[0x0a] = psxBios_todigit;
//biosA0[0x0b] = psxBios_atof;
- //biosA0[0x0c] = psxBios_strtoul;
- //biosA0[0x0d] = psxBios_strtol;
+ biosA0[0x0c] = psxBios_strtoul;
+ biosA0[0x0d] = psxBios_strtol;
biosA0[0x0e] = psxBios_abs;
biosA0[0x0f] = psxBios_labs;
biosA0[0x10] = psxBios_atoi;
// fill the api jumptables with fake entries as some games patch them
// (or rather the funcs listed there)
+ // also trap the destination as some "Cheats Edition" thing overrides the
+ // dispatcher with a wrapper and then jumps to the table entries directly
ptr = (u32 *)&psxM[A_A0_TABLE];
- for (i = 0; i < 256; i++)
- ptr[i] = SWAP32(A_A0_DUMMY);
-
+ for (i = 0; i < 256; i++) {
+ ptr[i] = SWAP32(A_A0_TRAPS + i*4);
+ ram32[A_A0_TRAPS/4 + i] = HLEOP(hleop_a0t);
+ }
ptr = (u32 *)&psxM[A_B0_TABLE];
- for (i = 0; i < 256; i++)
- ptr[i] = SWAP32(A_B0_DUMMY);
+ for (i = 0; i < 256; i++) {
+ ptr[i] = SWAP32(A_B0_TRAPS + i*4);
+ ram32[A_B0_TRAPS/4 + i] = HLEOP(hleop_b0t);
+ }
// B(5b) is special because games patch (sometimes even jump to)
// code at fixed offsets from it, nocash lists offsets:
// patch: +3d8, +4dc, +594, +62c, +9c8, +1988
// call: +7a0=4b70, +884=4c54, +894=4c64
- ptr[0x5b] = SWAP32(A_B0_5B_DUMMY); // 0x43d0
+ ptr[0x5b] = SWAP32(A_B0_5B_TRAP); // 0x43d0
+ ram32[A_B0_5B_TRAP/4] = HLEOP(hleop_b0t);
+
ram32[0x4b70/4] = SWAP32(0x03e00008); // jr $ra // setPadOutputBuf
ram32[0x4c54/4] = SWAP32(0x240e0001); // mov $t6, 1
ram32[0x4c68/4] = SWAP32(0xac000000 + A_PAD_IRQR_ENA); // sw $0, ...
ptr = (u32 *)&psxM[A_C0_TABLE];
- for (i = 0; i < 256/2; i++)
- ptr[i] = SWAP32(A_C0_DUMMY);
+ for (i = 0; i < 256/2; i++) {
+ ptr[i] = SWAP32(A_C0_TRAPS + i*4);
+ ram32[A_C0_TRAPS/4 + i] = HLEOP(hleop_c0t);
+ }
ptr[6] = SWAP32(A_EXCEPTION);
// more HLE traps
- ram32[A_A0_DUMMY/4] = HLEOP(hleop_dummy);
- ram32[A_B0_DUMMY/4] = HLEOP(hleop_dummy);
- ram32[A_C0_DUMMY/4] = HLEOP(hleop_dummy);
- ram32[A_B0_5B_DUMMY/4] = HLEOP(hleop_dummy);
+ ram32[A_A0_TRAPS/4 - 1] = HLEOP(hleop_dummy);
+ ram32[A_B0_TRAPS/4 - 1] = HLEOP(hleop_dummy);
+ ram32[A_C0_TRAPS/4 - 1] = HLEOP(hleop_dummy);
+ ram32[0x7ffc/4] = HLEOP(hleop_dummy);
ram32[0x8000/4] = HLEOP(hleop_execret);
ram32[A_EEXIT_PTR/4] = SWAP32(A_EEXIT_DEF);
sp = fp = loadRam32(A_EXC_SP);
gp = A_EXC_GP;
use_cycles(46);
+ assert(!psxRegs.cpuInRecursion);
// do the chains (always 4)
for (c = lim = 0; c < 4; c++) {
u32 call = t1 & 0xff;
u32 entry = loadRam32(A_A0_TABLE + call * 4);
- if (call < 192 && entry != A_A0_DUMMY) {
+ use_cycles(4+7);
+ if (call < 192 && entry != A_A0_TRAPS + call * 4) {
PSXBIOS_LOG("custom A%02x %s(0x%x, ) addr=%08x ra=%08x\n",
call, biosA0n[call], a0, entry, ra);
softCall(entry);
u32 entry = loadRam32(A_B0_TABLE + call * 4);
int is_custom = 0;
+ use_cycles(4+7);
if (call == 0x5b)
- is_custom = entry != A_B0_5B_DUMMY;
+ is_custom = entry != A_B0_5B_TRAP;
else
- is_custom = entry != A_B0_DUMMY;
+ is_custom = entry != A_B0_TRAPS + call * 4;
if (is_custom) {
PSXBIOS_LOG("custom B%02x %s(0x%x, ) addr=%08x ra=%08x\n",
call, biosB0n[call], a0, entry, ra);
u32 call = t1 & 0xff;
u32 entry = loadRam32(A_C0_TABLE + call * 4);
- if (call < 128 && entry != A_C0_DUMMY) {
+ use_cycles(4+7);
+ if (call < 128 && entry != A_C0_TRAPS + call * 4) {
PSXBIOS_LOG("custom C%02x %s(0x%x, ) addr=%08x ra=%08x\n",
call, biosC0n[call], a0, entry, ra);
softCall(entry);
psxBranchTest();
}
+static void hleA0t() {
+ u32 call = (pc0 - A_A0_TRAPS) / 4 - 1;
+ if (call >= 256u || !biosA0[call]) {
+ log_unhandled("unexpected A trap @%08x ra=%08x\n", pc0 - 4, ra);
+ mips_return_void_c(1000);
+ }
+ else
+ biosA0[call]();
+
+ //printf("A(%02x) -> %x\n", call, v0);
+ psxBranchTest();
+}
+
+static void hleB0t() {
+ u32 call = (pc0 - A_B0_TRAPS) / 4 - 1;
+ if (pc0 - 4 == A_B0_5B_TRAP)
+ call = 0x5b;
+ if (call >= 256u || !biosB0[call]) {
+ log_unhandled("unexpected B trap @%08x ra=%08x\n", pc0 - 4, ra);
+ mips_return_void_c(1000);
+ }
+ else
+ biosB0[call]();
+
+ //printf("B(%02x) -> %x\n", call, v0);
+ psxBranchTest();
+}
+
+static void hleC0t() {
+ u32 call = (pc0 - A_C0_TRAPS) / 4 - 1;
+ if (call >= 128u || !biosC0[call]) {
+ log_unhandled("unexpected C trap @%08x ra=%08x\n", pc0 - 4, ra);
+ mips_return_void_c(1000);
+ }
+ else
+ biosC0[call]();
+
+ //printf("C(%02x) -> %x\n", call, v0);
+ psxBranchTest();
+}
+
// currently not used
static void hleBootstrap() {
CheckCdrom();
psxRegs.pc = ra;
}
-void (* const psxHLEt[24])() = {
+void (* const psxHLEt[hleop_count_])() = {
hleDummy, hleA0, hleB0, hleC0,
hleBootstrap, hleExecRet, psxBiosException, hleDummy,
hleExc0_0_1, hleExc0_0_2,
hleExc1_3_1, hleExc1_3_2,
hleExc3_0_2_defint,
hleExcPadCard1, hleExcPadCard2,
+ hleA0t, hleB0t, hleC0t,
};
+void psxBiosCheckExe(u32 t_addr, u32 t_size, int loading_state)
+{
+ // lw $v0, 0x10($sp)
+ // nop
+ // addiu $v0, -1
+ // sw $v0, 0x10($sp)
+ // lw $v0, 0x10($sp)
+ // nop
+ // bne $v0, $v1, not_timeout
+ // nop
+ // lui $a0, ...
+ static const u8 pattern[] = {
+ 0x10, 0x00, 0xA2, 0x8F, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0x42, 0x24, 0x10, 0x00, 0xA2, 0xAF,
+ 0x10, 0x00, 0xA2, 0x8F, 0x00, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x43, 0x14, 0x00, 0x00, 0x00, 0x00,
+ };
+ u32 start = t_addr & 0x1ffffc;
+ u32 end = (start + t_size) & 0x1ffffc;
+ u32 buf[sizeof(pattern) / sizeof(u32)];
+ const u32 *r32 = (u32 *)(psxM + start);
+ u32 i, j;
+
+ if (end <= start)
+ return;
+ if (!Config.HLE)
+ return;
+
+ memcpy(buf, pattern, sizeof(buf));
+ for (i = 0; i < t_size / 4; i += j + 1) {
+ for (j = 0; j < sizeof(buf) / sizeof(buf[0]); j++)
+ if (r32[i + j] != buf[j])
+ break;
+ if (j != sizeof(buf) / sizeof(buf[0]))
+ continue;
+
+ if ((SWAP32(r32[i + j]) >> 16) != 0x3c04) // lui
+ continue;
+ if (!loading_state)
+ SysPrintf("HLE vsync @%08x\n", start + i * 4);
+ psxRegs.biosBranchCheck = (t_addr & 0xa01ffffc) + i * 4;
+ }
+}
+
+void psxBiosCheckBranch(void)
+{
+#if 1
+ // vsync HLE hack
+ static u32 cycles_prev, v0_prev;
+ u32 cycles_passed, waste_cycles;
+ u32 loops, v0_expect = v0_prev - 1;
+ if (v0 != 1)
+ return;
+ execI(&psxRegs);
+ cycles_passed = psxRegs.cycle - cycles_prev;
+ cycles_prev = psxRegs.cycle;
+ v0_prev = v0;
+ if (cycles_passed < 10 || cycles_passed > 50 || v0 != v0_expect)
+ return;
+
+ waste_cycles = schedule_timeslice() - psxRegs.cycle;
+ loops = waste_cycles / cycles_passed;
+ if (loops > v0)
+ loops = v0;
+ v0 -= loops;
+ psxRegs.cycle += loops * cycles_passed;
+ //printf("c %4u %d\n", loops, cycles_passed);
+#endif
+}
#define bfreeze(ptr, size) { \
if (Mode == 1) memcpy(&psxR[base], ptr, size); \