aboutsummaryrefslogtreecommitdiff
path: root/libpcsxcore
diff options
context:
space:
mode:
authorPaul Cercueil2020-01-07 14:26:14 -0300
committerPaul Cercueil2020-02-08 11:44:53 -0300
commit6f1edc3c7fd1f7f58155107d0c99d0ac7d22443b (patch)
treef1a447b8d2de843485cfef1cb197f6f86021da04 /libpcsxcore
parent38c19eff92f3440c9a50f336b2adc9b297925f0c (diff)
downloadpcsx_rearmed-6f1edc3c7fd1f7f58155107d0c99d0ac7d22443b.tar.gz
pcsx_rearmed-6f1edc3c7fd1f7f58155107d0c99d0ac7d22443b.tar.bz2
pcsx_rearmed-6f1edc3c7fd1f7f58155107d0c99d0ac7d22443b.zip
Add support for the Lightrec dynarec
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Diffstat (limited to 'libpcsxcore')
-rw-r--r--libpcsxcore/gte.c6
-rw-r--r--libpcsxcore/gte.h4
-rw-r--r--libpcsxcore/lightrec/plugin.c579
3 files changed, 586 insertions, 3 deletions
diff --git a/libpcsxcore/gte.c b/libpcsxcore/gte.c
index 77dff1b..83bb4b6 100644
--- a/libpcsxcore/gte.c
+++ b/libpcsxcore/gte.c
@@ -258,7 +258,7 @@ static inline u32 limE_(psxCP2Regs *regs, u32 result) {
#ifndef FLAGLESS
-static inline u32 MFC2(int reg) {
+u32 MFC2(int reg) {
psxCP2Regs *regs = &psxRegs.CP2;
switch (reg) {
case 1:
@@ -293,7 +293,7 @@ static inline u32 MFC2(int reg) {
return psxRegs.CP2D.r[reg];
}
-static inline void MTC2(u32 value, int reg) {
+void MTC2(u32 value, int reg) {
psxCP2Regs *regs = &psxRegs.CP2;
switch (reg) {
case 15:
@@ -339,7 +339,7 @@ static inline void MTC2(u32 value, int reg) {
}
}
-static inline void CTC2(u32 value, int reg) {
+void CTC2(u32 value, int reg) {
switch (reg) {
case 4:
case 12:
diff --git a/libpcsxcore/gte.h b/libpcsxcore/gte.h
index 7646226..8bc6988 100644
--- a/libpcsxcore/gte.h
+++ b/libpcsxcore/gte.h
@@ -67,6 +67,10 @@ extern "C" {
struct psxCP2Regs;
+u32 MFC2(int reg);
+void MTC2(u32 value, int reg);
+void CTC2(u32 value, int reg);
+
void gteMFC2();
void gteCFC2();
void gteMTC2();
diff --git a/libpcsxcore/lightrec/plugin.c b/libpcsxcore/lightrec/plugin.c
new file mode 100644
index 0000000..64587be
--- /dev/null
+++ b/libpcsxcore/lightrec/plugin.c
@@ -0,0 +1,579 @@
+#include <lightrec.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include "../cdrom.h"
+#include "../gpu.h"
+#include "../gte.h"
+#include "../mdec.h"
+#include "../psxdma.h"
+#include "../psxhw.h"
+#include "../psxmem.h"
+#include "../r3000a.h"
+
+#include "../frontend/main.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) ? sizeof(x) / sizeof((x)[0]) : 0)
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define LE32TOH(x) __builtin_bswap32(x)
+# define HTOLE32(x) __builtin_bswap32(x)
+# define LE16TOH(x) __builtin_bswap16(x)
+# define HTOLE16(x) __builtin_bswap16(x)
+#else
+# define LE32TOH(x) (x)
+# define HTOLE32(x) (x)
+# define LE16TOH(x) (x)
+# define HTOLE16(x) (x)
+#endif
+
+#ifdef __GNUC__
+# define likely(x) __builtin_expect(!!(x),1)
+# define unlikely(x) __builtin_expect(!!(x),0)
+#else
+# define likely(x) (x)
+# define unlikely(x) (x)
+#endif
+
+static struct lightrec_state *lightrec_state;
+
+static char *name = "retroarch.exe";
+
+static bool use_lightrec_interpreter;
+static bool lightrec_debug;
+static bool lightrec_very_debug;
+static u32 lightrec_begin_cycles;
+
+int stop;
+u32 cycle_multiplier;
+int new_dynarec_hacks;
+
+/* Unused for now */
+u32 event_cycles[PSXINT_COUNT];
+u32 next_interupt;
+
+void new_dyna_before_save() {}
+void new_dyna_after_save() {}
+void new_dyna_freeze(void *f, int i) {}
+
+enum my_cp2_opcodes {
+ OP_CP2_RTPS = 0x01,
+ OP_CP2_NCLIP = 0x06,
+ OP_CP2_OP = 0x0c,
+ OP_CP2_DPCS = 0x10,
+ OP_CP2_INTPL = 0x11,
+ OP_CP2_MVMVA = 0x12,
+ OP_CP2_NCDS = 0x13,
+ OP_CP2_CDP = 0x14,
+ OP_CP2_NCDT = 0x16,
+ OP_CP2_NCCS = 0x1b,
+ OP_CP2_CC = 0x1c,
+ OP_CP2_NCS = 0x1e,
+ OP_CP2_NCT = 0x20,
+ OP_CP2_SQR = 0x28,
+ OP_CP2_DCPL = 0x29,
+ OP_CP2_DPCT = 0x2a,
+ OP_CP2_AVSZ3 = 0x2d,
+ OP_CP2_AVSZ4 = 0x2e,
+ OP_CP2_RTPT = 0x30,
+ OP_CP2_GPF = 0x3d,
+ OP_CP2_GPL = 0x3e,
+ OP_CP2_NCCT = 0x3f,
+};
+
+static void (*cp2_ops[])(struct psxCP2Regs *) = {
+ [OP_CP2_RTPS] = gteRTPS,
+ [OP_CP2_RTPS] = gteRTPS,
+ [OP_CP2_NCLIP] = gteNCLIP,
+ [OP_CP2_OP] = gteOP,
+ [OP_CP2_DPCS] = gteDPCS,
+ [OP_CP2_INTPL] = gteINTPL,
+ [OP_CP2_MVMVA] = gteMVMVA,
+ [OP_CP2_NCDS] = gteNCDS,
+ [OP_CP2_CDP] = gteCDP,
+ [OP_CP2_NCDT] = gteNCDT,
+ [OP_CP2_NCCS] = gteNCCS,
+ [OP_CP2_CC] = gteCC,
+ [OP_CP2_NCS] = gteNCS,
+ [OP_CP2_NCT] = gteNCT,
+ [OP_CP2_SQR] = gteSQR,
+ [OP_CP2_DCPL] = gteDCPL,
+ [OP_CP2_DPCT] = gteDPCT,
+ [OP_CP2_AVSZ3] = gteAVSZ3,
+ [OP_CP2_AVSZ4] = gteAVSZ4,
+ [OP_CP2_RTPT] = gteRTPT,
+ [OP_CP2_GPF] = gteGPF,
+ [OP_CP2_GPL] = gteGPL,
+ [OP_CP2_NCCT] = gteNCCT,
+};
+
+static char cache_buf[64 * 1024];
+
+static u32 cop0_mfc(struct lightrec_state *state, u8 reg)
+{
+ return psxRegs.CP0.r[reg];
+}
+
+static u32 cop2_mfc_cfc(struct lightrec_state *state, u8 reg, bool cfc)
+{
+ if (cfc)
+ return psxRegs.CP2C.r[reg];
+ else
+ return MFC2(reg);
+}
+
+static u32 cop2_mfc(struct lightrec_state *state, u8 reg)
+{
+ return cop2_mfc_cfc(state, reg, false);
+}
+
+static u32 cop2_cfc(struct lightrec_state *state, u8 reg)
+{
+ return cop2_mfc_cfc(state, reg, true);
+}
+
+static void cop0_mtc_ctc(struct lightrec_state *state,
+ u8 reg, u32 value, bool ctc)
+{
+ switch (reg) {
+ case 1:
+ case 4:
+ case 8:
+ case 14:
+ case 15:
+ /* Those registers are read-only */
+ break;
+ case 12: /* Status */
+ if ((psxRegs.CP0.n.Status & ~value) & (1 << 16)) {
+ memcpy(psxM, cache_buf, sizeof(cache_buf));
+ lightrec_invalidate_all(state);
+ } else if ((~psxRegs.CP0.n.Status & value) & (1 << 16)) {
+ memcpy(cache_buf, psxM, sizeof(cache_buf));
+ }
+
+ psxRegs.CP0.n.Status = value;
+ lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
+ break;
+ case 13: /* Cause */
+ psxRegs.CP0.n.Cause &= ~0x0300;
+ psxRegs.CP0.n.Cause |= value & 0x0300;
+ lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
+ break;
+ default:
+ psxRegs.CP0.r[reg] = value;
+ break;
+ }
+}
+
+static void cop2_mtc_ctc(struct lightrec_state *state,
+ u8 reg, u32 value, bool ctc)
+{
+ if (ctc)
+ CTC2(value, reg);
+ else
+ MTC2(value, reg);
+}
+
+static void cop0_mtc(struct lightrec_state *state, u8 reg, u32 value)
+{
+ cop0_mtc_ctc(state, reg, value, false);
+}
+
+static void cop0_ctc(struct lightrec_state *state, u8 reg, u32 value)
+{
+ cop0_mtc_ctc(state, reg, value, true);
+}
+
+static void cop2_mtc(struct lightrec_state *state, u8 reg, u32 value)
+{
+ cop2_mtc_ctc(state, reg, value, false);
+}
+
+static void cop2_ctc(struct lightrec_state *state, u8 reg, u32 value)
+{
+ cop2_mtc_ctc(state, reg, value, true);
+}
+
+static void cop0_op(struct lightrec_state *state, u32 func)
+{
+ fprintf(stderr, "Invalid access to COP0\n");
+}
+
+static void cop2_op(struct lightrec_state *state, u32 func)
+{
+ psxRegs.code = func;
+
+ if (unlikely(!cp2_ops[func & 0x3f]))
+ fprintf(stderr, "Invalid CP2 function %u\n", func);
+ else
+ cp2_ops[func & 0x3f](&psxRegs.CP2);
+}
+
+static void hw_write_byte(struct lightrec_state *state, u32 mem, u8 val)
+{
+ psxRegs.cycle = lightrec_current_cycle_count(state);
+
+ psxHwWrite8(mem, val);
+ lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
+
+ lightrec_reset_cycle_count(state, psxRegs.cycle);
+}
+
+static void hw_write_half(struct lightrec_state *state, u32 mem, u16 val)
+{
+ psxRegs.cycle = lightrec_current_cycle_count(state);
+
+ psxHwWrite16(mem, val);
+ lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
+
+ lightrec_reset_cycle_count(state, psxRegs.cycle);
+}
+
+static void hw_write_word(struct lightrec_state *state, u32 mem, u32 val)
+{
+ psxRegs.cycle = lightrec_current_cycle_count(state);
+
+ psxHwWrite32(mem, val);
+ lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
+
+ lightrec_reset_cycle_count(state, psxRegs.cycle);
+}
+
+static u8 hw_read_byte(struct lightrec_state *state, u32 mem)
+{
+ u8 val;
+
+ psxRegs.cycle = lightrec_current_cycle_count(state);
+
+ lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
+ val = psxHwRead8(mem);
+ lightrec_reset_cycle_count(state, psxRegs.cycle);
+
+ return val;
+}
+
+static u16 hw_read_half(struct lightrec_state *state, u32 mem)
+{
+ u16 val;
+
+ psxRegs.cycle = lightrec_current_cycle_count(state);
+
+ lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
+ val = psxHwRead16(mem);
+ lightrec_reset_cycle_count(state, psxRegs.cycle);
+
+ return val;
+}
+
+static u32 hw_read_word(struct lightrec_state *state, u32 mem)
+{
+ u32 val;
+
+ psxRegs.cycle = lightrec_current_cycle_count(state);
+
+ lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
+ val = psxHwRead32(mem);
+ lightrec_reset_cycle_count(state, psxRegs.cycle);
+
+ return val;
+}
+
+static struct lightrec_mem_map_ops hw_regs_ops = {
+ .sb = hw_write_byte,
+ .sh = hw_write_half,
+ .sw = hw_write_word,
+ .lb = hw_read_byte,
+ .lh = hw_read_half,
+ .lw = hw_read_word,
+};
+
+static u32 cache_ctrl;
+
+static void cache_ctrl_write_word(struct lightrec_state *state, u32 mem, u32 val)
+{
+ cache_ctrl = val;
+}
+
+static u32 cache_ctrl_read_word(struct lightrec_state *state, u32 mem)
+{
+ return cache_ctrl;
+}
+
+static struct lightrec_mem_map_ops cache_ctrl_ops = {
+ .sw = cache_ctrl_write_word,
+ .lw = cache_ctrl_read_word,
+};
+
+static struct lightrec_mem_map lightrec_map[] = {
+ [PSX_MAP_KERNEL_USER_RAM] = {
+ /* Kernel and user memory */
+ .pc = 0x00000000,
+ .length = 0x200000,
+ },
+ [PSX_MAP_BIOS] = {
+ /* BIOS */
+ .pc = 0x1fc00000,
+ .length = 0x80000,
+ },
+ [PSX_MAP_SCRATCH_PAD] = {
+ /* Scratch pad */
+ .pc = 0x1f800000,
+ .length = 0x400,
+ },
+ [PSX_MAP_PARALLEL_PORT] = {
+ /* Parallel port */
+ .pc = 0x1f000000,
+ .length = 0x10000,
+ },
+ [PSX_MAP_HW_REGISTERS] = {
+ /* Hardware registers */
+ .pc = 0x1f801000,
+ .length = 0x2000,
+ .ops = &hw_regs_ops,
+ },
+ [PSX_MAP_CACHE_CONTROL] = {
+ /* Cache control */
+ .pc = 0x5ffe0130,
+ .length = 4,
+ .ops = &cache_ctrl_ops,
+ },
+
+ /* Mirrors of the kernel/user memory */
+ [PSX_MAP_MIRROR1] = {
+ .pc = 0x00200000,
+ .length = 0x200000,
+ .mirror_of = &lightrec_map[PSX_MAP_KERNEL_USER_RAM],
+ },
+ [PSX_MAP_MIRROR2] = {
+ .pc = 0x00400000,
+ .length = 0x200000,
+ .mirror_of = &lightrec_map[PSX_MAP_KERNEL_USER_RAM],
+ },
+ [PSX_MAP_MIRROR3] = {
+ .pc = 0x00600000,
+ .length = 0x200000,
+ .mirror_of = &lightrec_map[PSX_MAP_KERNEL_USER_RAM],
+ },
+};
+
+static const struct lightrec_ops lightrec_ops = {
+ .cop0_ops = {
+ .mfc = cop0_mfc,
+ .cfc = cop0_mfc,
+ .mtc = cop0_mtc,
+ .ctc = cop0_ctc,
+ .op = cop0_op,
+ },
+ .cop2_ops = {
+ .mfc = cop2_mfc,
+ .cfc = cop2_cfc,
+ .mtc = cop2_mtc,
+ .ctc = cop2_ctc,
+ .op = cop2_op,
+ },
+};
+
+static int lightrec_plugin_init(void)
+{
+ lightrec_map[PSX_MAP_KERNEL_USER_RAM].address = psxM;
+ lightrec_map[PSX_MAP_BIOS].address = psxR;
+ lightrec_map[PSX_MAP_SCRATCH_PAD].address = psxH;
+ lightrec_map[PSX_MAP_PARALLEL_PORT].address = psxP;
+
+ lightrec_debug = !!getenv("LIGHTREC_DEBUG");
+ lightrec_very_debug = !!getenv("LIGHTREC_VERY_DEBUG");
+ use_lightrec_interpreter = !!getenv("LIGHTREC_INTERPRETER");
+ if (getenv("LIGHTREC_BEGIN_CYCLES"))
+ lightrec_begin_cycles = (unsigned int) strtol(
+ getenv("LIGHTREC_BEGIN_CYCLES"), NULL, 0);
+
+ lightrec_state = lightrec_init(name,
+ lightrec_map, ARRAY_SIZE(lightrec_map),
+ &lightrec_ops);
+
+ fprintf(stderr, "M=0x%lx, P=0x%lx, R=0x%lx, H=0x%lx\n",
+ (uintptr_t) psxM,
+ (uintptr_t) psxP,
+ (uintptr_t) psxR,
+ (uintptr_t) psxH);
+
+#ifndef _WIN32
+ signal(SIGPIPE, exit);
+#endif
+ return 0;
+}
+
+static u32 hash_calculate_le(const void *buffer, u32 count)
+{
+ unsigned int i;
+ u32 *data = (u32 *) buffer;
+ u32 hash = 0xffffffff;
+
+ count /= 4;
+ for(i = 0; i < count; ++i) {
+ hash += LE32TOH(data[i]);
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
+}
+
+static u32 hash_calculate(const void *buffer, u32 count)
+{
+ unsigned int i;
+ u32 *data = (u32 *) buffer;
+ u32 hash = 0xffffffff;
+
+ count /= 4;
+ for(i = 0; i < count; ++i) {
+ hash += data[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
+}
+
+static const char * const mips_regs[] = {
+ "zero",
+ "at",
+ "v0", "v1",
+ "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9",
+ "k0", "k1",
+ "gp", "sp", "fp", "ra",
+ "lo", "hi",
+};
+
+static void print_for_big_ass_debugger(void)
+{
+ unsigned int i;
+
+ printf("CYCLE 0x%08x PC 0x%08x", psxRegs.cycle, psxRegs.pc);
+
+ if (lightrec_very_debug)
+ printf(" RAM 0x%08x SCRATCH 0x%08x HW 0x%08x",
+ hash_calculate_le(psxM, 0x200000),
+ hash_calculate_le(psxH, 0x400),
+ hash_calculate_le(psxH + 0x1000, 0x2000));
+
+ printf(" CP0 0x%08x CP2D 0x%08x CP2C 0x%08x INT 0x%04x INTCYCLE 0x%08x GPU 0x%08x",
+ hash_calculate(&psxRegs.CP0.r,
+ sizeof(psxRegs.CP0.r)),
+ hash_calculate(&psxRegs.CP2D.r,
+ sizeof(psxRegs.CP2D.r)),
+ hash_calculate(&psxRegs.CP2C.r,
+ sizeof(psxRegs.CP2C.r)),
+ psxRegs.interrupt,
+ hash_calculate(psxRegs.intCycle,
+ sizeof(psxRegs.intCycle)),
+ LE32TOH(HW_GPU_STATUS));
+
+ if (lightrec_very_debug)
+ for (i = 0; i < 34; i++)
+ printf(" %s 0x%08x", mips_regs[i], psxRegs.GPR.r[i]);
+ else
+ printf(" GPR 0x%08x", hash_calculate(&psxRegs.GPR.r,
+ sizeof(psxRegs.GPR.r)));
+ printf("\n");
+}
+
+static u32 old_cycle_counter;
+
+static void lightrec_plugin_execute_block(void)
+{
+ u32 old_pc = psxRegs.pc;
+ u32 flags;
+
+ lightrec_reset_cycle_count(lightrec_state, psxRegs.cycle);
+ lightrec_restore_registers(lightrec_state, psxRegs.GPR.r);
+
+ if (use_lightrec_interpreter)
+ psxRegs.pc = lightrec_run_interpreter(lightrec_state, psxRegs.pc);
+ else
+ psxRegs.pc = lightrec_execute_one(lightrec_state, psxRegs.pc);
+
+ psxRegs.cycle = lightrec_current_cycle_count(lightrec_state);
+
+ lightrec_dump_registers(lightrec_state, psxRegs.GPR.r);
+ flags = lightrec_exit_flags(lightrec_state);
+
+ if (flags & LIGHTREC_EXIT_SEGFAULT) {
+ fprintf(stderr, "Exiting at cycle 0x%08x\n",
+ psxRegs.cycle);
+ exit(1);
+ }
+
+ if (flags & LIGHTREC_EXIT_SYSCALL)
+ psxException(0x20, 0);
+
+ psxBranchTest();
+
+ if (lightrec_debug && psxRegs.cycle >= lightrec_begin_cycles
+ && psxRegs.pc != old_pc)
+ print_for_big_ass_debugger();
+
+ if ((psxRegs.CP0.n.Cause & psxRegs.CP0.n.Status & 0x300) &&
+ (psxRegs.CP0.n.Status & 0x1)) {
+ /* Handle software interrupts */
+ psxRegs.CP0.n.Cause &= ~0x7c;
+ psxException(psxRegs.CP0.n.Cause, 0);
+ }
+
+ if ((psxRegs.cycle & ~0xfffffff) != old_cycle_counter) {
+ printf("RAM usage: IR %u KiB, CODE %u KiB, "
+ "MIPS %u KiB, TOTAL %u KiB, avg. IPI %f\n",
+ lightrec_get_mem_usage(MEM_FOR_IR) / 1024,
+ lightrec_get_mem_usage(MEM_FOR_CODE) / 1024,
+ lightrec_get_mem_usage(MEM_FOR_MIPS_CODE) / 1024,
+ lightrec_get_total_mem_usage() / 1024,
+ lightrec_get_average_ipi());
+ old_cycle_counter = psxRegs.cycle & ~0xfffffff;
+ }
+}
+
+static void lightrec_plugin_execute(void)
+{
+ extern int stop;
+
+ while (!stop)
+ lightrec_plugin_execute_block();
+}
+
+static void lightrec_plugin_clear(u32 addr, u32 size)
+{
+ /* size * 4: PCSX uses DMA units */
+ lightrec_invalidate(lightrec_state, addr, size * 4);
+}
+
+static void lightrec_plugin_shutdown(void)
+{
+ lightrec_destroy(lightrec_state);
+}
+
+static void lightrec_plugin_reset(void)
+{
+ lightrec_plugin_shutdown();
+ lightrec_plugin_init();
+}
+
+R3000Acpu psxRec =
+{
+ lightrec_plugin_init,
+ lightrec_plugin_reset,
+ lightrec_plugin_execute,
+ lightrec_plugin_execute_block,
+ lightrec_plugin_clear,
+ lightrec_plugin_shutdown,
+};