+/* HLE */
+static void hleDummy() {
+ log_unhandled("hleDummy called @%08x ra=%08x\n", psxRegs.pc - 4, ra);
+ psxRegs.pc = ra;
+ psxRegs.cycle += 1000;
+
+ psxBranchTest();
+}
+
+static void hleA0() {
+ u32 call = t1 & 0xff;
+ u32 entry = loadRam32(A_A0_TABLE + call * 4);
+
+ 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);
+ pc0 = ra;
+ PSXBIOS_LOG(" -> %08x\n", v0);
+ }
+ else if (biosA0[call])
+ biosA0[call]();
+
+ //printf("A(%02x) -> %x\n", call, v0);
+ psxBranchTest();
+}
+
+static void hleB0() {
+ u32 call = t1 & 0xff;
+ 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_TRAP;
+ else
+ 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);
+ softCall(entry);
+ pc0 = ra;
+ PSXBIOS_LOG(" -> %08x\n", v0);
+ }
+ else if (biosB0[call])
+ biosB0[call]();
+
+ //printf("B(%02x) -> %x\n", call, v0);
+ psxBranchTest();
+}
+
+static void hleC0() {
+ u32 call = t1 & 0xff;
+ u32 entry = loadRam32(A_C0_TABLE + call * 4);
+
+ 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);
+ pc0 = ra;
+ PSXBIOS_LOG(" -> %08x\n", v0);
+ }
+ else if (biosC0[call])
+ biosC0[call]();
+
+ //printf("C(%02x) -> %x\n", call, v0);
+ 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();
+ LoadCdrom();
+}
+
+static void hleExecRet() {
+ const EXEC *header = (EXEC *)PSXM(s0);
+
+ PSXBIOS_LOG("ExecRet %x: %x\n", s0, header->ret);
+
+ ra = SWAP32(header->ret);
+ sp = SWAP32(header->_sp);
+ fp = SWAP32(header->_fp);
+ gp = SWAP32(header->_gp);
+ s0 = SWAP32(header->base);
+
+ v0 = 1;
+ psxRegs.pc = ra;
+}
+
+void (* const psxHLEt[hleop_count_])() = {
+ hleDummy, hleA0, hleB0, hleC0,
+ hleBootstrap, hleExecRet, psxBiosException, hleDummy,
+ hleExc0_0_1, hleExc0_0_2,
+ hleExc0_1_1, hleExc0_1_2, hleExc0_2_2_syscall,
+ hleExc1_0_1, hleExc1_0_2,
+ hleExc1_1_1, hleExc1_1_2,
+ hleExc1_2_1, hleExc1_2_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
+}
+