From 97fa754ea10367f512b08c11c6f661d0beb1934f Mon Sep 17 00:00:00 2001
From: Paul Cercueil <paul@crapouillou.net>
Date: Fri, 17 Jan 2025 01:25:17 +0100
Subject: [PATCH] lightrec: Add LIGHTREC_DEBUG functionality

When set, Lightrec will be built with the debug log level, the built-in
disassembler for PSX code and JIT code (requires binutils), and support
outputing data to the big-ass debugger (if the proper environment
variables are set).

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
---
 Makefile                           |  11 ++-
 frontend/main.c                    |   2 +
 include/lightrec/lightrec-config.h |   2 +-
 jni/Android.mk                     |   2 +
 libpcsxcore/lightrec/plugin.c      | 122 ++++++++++++++++++++++++++++-
 5 files changed, 136 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile
index 7b9ac5ff..ec07225e 100644
--- a/Makefile
+++ b/Makefile
@@ -118,13 +118,22 @@ endif
 ifeq "$(DYNAREC)" "lightrec"
 CFLAGS += -Ideps/lightning/include -Ideps/lightrec -Iinclude/lightning -Iinclude/lightrec \
 		  -DLIGHTREC -DLIGHTREC_STATIC
+ifeq ($(LIGHTREC_DEBUG),1)
+deps/lightrec/%.o: CFLAGS += -DLOG_LEVEL=DEBUG_L
+libpcsxcore/lightrec/plugin.o: CFLAGS += -DLIGHTREC_DEBUG=1
+frontend/main.o: CFLAGS += -DLIGHTREC_DEBUG=1
+deps/lightning/%.o: CFLAGS += -DDISASSEMBLER=1 -DBINUTILS_2_38=1 -DBINUTILS_2_29=1 \
+	-DHAVE_DISASSEMBLE_INIT_FOR_TARGET=1 -DPACKAGE_VERSION=1
+LDFLAGS += -lopcodes -lbfd
+endif
 LIGHTREC_CUSTOM_MAP ?= 0
 LIGHTREC_CUSTOM_MAP_OBJ ?= libpcsxcore/lightrec/mem.o
 LIGHTREC_THREADED_COMPILER ?= 0
 LIGHTREC_CODE_INV ?= 0
 CFLAGS += -DLIGHTREC_CUSTOM_MAP=$(LIGHTREC_CUSTOM_MAP) \
 	  -DLIGHTREC_CODE_INV=$(LIGHTREC_CODE_INV) \
-	  -DLIGHTREC_ENABLE_THREADED_COMPILER=$(LIGHTREC_THREADED_COMPILER)
+	  -DLIGHTREC_ENABLE_THREADED_COMPILER=$(LIGHTREC_THREADED_COMPILER) \
+	  -DLIGHTREC_ENABLE_DISASSEMBLER=$(or $(LIGHTREC_DEBUG),0)
 ifeq ($(LIGHTREC_CUSTOM_MAP),1)
 LDLIBS += -lrt
 OBJS += $(LIGHTREC_CUSTOM_MAP_OBJ)
diff --git a/frontend/main.c b/frontend/main.c
index 2dd5ca4c..b2a4fea4 100644
--- a/frontend/main.c
+++ b/frontend/main.c
@@ -737,7 +737,9 @@ int main(int argc, char *argv[])
 	else
 		menu_loop();
 
+#ifndef LIGHTREC_DEBUG
 	pl_start_watchdog();
+#endif
 
 	while (!g_emu_want_quit)
 	{
diff --git a/include/lightrec/lightrec-config.h b/include/lightrec/lightrec-config.h
index 3d4b81e6..8453fe46 100644
--- a/include/lightrec/lightrec-config.h
+++ b/include/lightrec/lightrec-config.h
@@ -8,7 +8,7 @@
 
 #define ENABLE_THREADED_COMPILER LIGHTREC_ENABLE_THREADED_COMPILER
 #define ENABLE_FIRST_PASS 1
-#define ENABLE_DISASSEMBLER 0
+#define ENABLE_DISASSEMBLER LIGHTREC_ENABLE_DISASSEMBLER
 #define ENABLE_CODE_BUFFER 1
 
 #define HAS_DEFAULT_ELM 1
diff --git a/jni/Android.mk b/jni/Android.mk
index 8439f3d7..0fe5cb95 100644
--- a/jni/Android.mk
+++ b/jni/Android.mk
@@ -134,6 +134,7 @@ HAVE_ARI64=0
 HAVE_LIGHTREC=0
 LIGHTREC_CUSTOM_MAP=0
 LIGHTREC_THREADED_COMPILER=0
+LIGHTREC_DEBUG=0
 HAVE_GPU_NEON=0
 ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
   HAVE_ARI64=1
@@ -154,6 +155,7 @@ else
 endif
   COREFLAGS   += -DLIGHTREC_CUSTOM_MAP=$(LIGHTREC_CUSTOM_MAP)
   COREFLAGS   += -DLIGHTREC_ENABLE_THREADED_COMPILER=$(LIGHTREC_THREADED_COMPILER)
+  COREFLAGS   += -DLIGHTREC_ENABLE_DISASSEMBLER=$(or $(LIGHTREC_DEBUG),0)
 
 ifeq ($(HAVE_ARI64),1)
   SOURCES_C   += $(DYNAREC_DIR)/new_dynarec.c \
diff --git a/libpcsxcore/lightrec/plugin.c b/libpcsxcore/lightrec/plugin.c
index d62f35bd..0ca44e44 100644
--- a/libpcsxcore/lightrec/plugin.c
+++ b/libpcsxcore/lightrec/plugin.c
@@ -72,6 +72,9 @@ static bool use_lightrec_interpreter;
 static bool block_stepping;
 //static bool use_pcsx_interpreter;
 #define use_pcsx_interpreter 0
+static bool ram_disabled;
+static bool lightrec_debug, lightrec_very_debug;
+static u32 lightrec_begin_cycles;
 
 extern u32 lightrec_hacks;
 
@@ -340,6 +343,8 @@ static void lightrec_enable_ram(struct lightrec_state *state, bool enable)
 		memcpy(psxM, cache_buf, sizeof(cache_buf));
 	else
 		memcpy(cache_buf, psxM, sizeof(cache_buf));
+
+	ram_disabled = !enable;
 }
 
 static bool lightrec_can_hw_direct(u32 kaddr, bool is_write, u8 size)
@@ -465,6 +470,16 @@ static int lightrec_plugin_init(void)
 
 	use_lightrec_interpreter = !!getenv("LIGHTREC_INTERPRETER");
 
+#ifdef LIGHTREC_DEBUG
+	char *cycles = getenv("LIGHTREC_BEGIN_CYCLES");
+
+	lightrec_very_debug = !!getenv("LIGHTREC_VERY_DEBUG");
+	lightrec_debug = lightrec_very_debug || !!getenv("LIGHTREC_DEBUG");
+
+	if (cycles)
+		lightrec_begin_cycles = (unsigned int) strtol(cycles, NULL, 0);
+#endif
+
 	lightrec_state = lightrec_init(LIGHTREC_PROG_NAME,
 			lightrec_map, ARRAY_SIZE(lightrec_map),
 			&lightrec_ops);
@@ -481,6 +496,104 @@ static int lightrec_plugin_init(void)
 	return 0;
 }
 
+static u32 do_calculate_hash(const void *buffer, u32 count, u32 needle, bool le)
+{
+	unsigned int i;
+	const u32 *data = (const u32 *) buffer;
+	u32 hash = needle;
+
+	count /= 4;
+	for(i = 0; i < count; ++i) {
+		hash += le ? LE32TOH(data[i]) : data[i];
+		hash += (hash << 10);
+		hash ^= (hash >> 6);
+	}
+
+	hash += (hash << 3);
+	hash ^= (hash >> 11);
+	hash += (hash << 15);
+
+	return hash;
+}
+
+static u32 hash_calculate_le(const void *buffer, u32 count)
+{
+	return do_calculate_hash(buffer, count, 0xffffffff, true);
+}
+
+u32 hash_calculate(const void *buffer, u32 count)
+{
+	return do_calculate_hash(buffer, count, 0xffffffff, false);
+}
+
+static u32 hash_calculate_ram(const void *buffer, u32 ram_size)
+{
+	u32 hash;
+
+	if (ram_disabled)
+		hash = hash_calculate_le(cache_buf, sizeof(cache_buf));
+	else
+		hash = hash_calculate_le(buffer, sizeof(cache_buf));
+
+	return do_calculate_hash(buffer + sizeof(cache_buf),
+				 ram_size - sizeof(cache_buf),
+				 hash, true);
+}
+
+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)
+{
+	struct lightrec_registers *regs;
+	unsigned int i;
+
+	regs = lightrec_get_registers(lightrec_state);
+
+	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_ram(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(regs->cp0, sizeof(regs->cp0)),
+			hash_calculate(regs->cp2d, sizeof(regs->cp2d)),
+			hash_calculate(regs->cp2c, sizeof(regs->cp2c)),
+			psxRegs.interrupt,
+			hash_calculate(psxRegs.intCycle, sizeof(psxRegs.intCycle)),
+			LE32TOH(HW_GPU_STATUS));
+
+	if (lightrec_very_debug) {
+		for (i = 0; i < 32; i++)
+			printf(" CP2D%u 0x%08x", i, regs->cp2d[i]);
+		for (i = 0; i < 32; i++)
+			printf(" CP2C%u 0x%08x", i, regs->cp2c[i]);
+	}
+
+	if (lightrec_very_debug)
+		for (i = 0; i < 34; i++)
+			printf(" %s 0x%08x", mips_regs[i], regs->gpr[i]);
+	else
+		printf(" GPR 0x%08x",
+		       hash_calculate(regs->gpr, sizeof(regs->gpr)));
+	printf("\n");
+
+	fflush(stdout);
+}
+
 static void lightrec_plugin_sync_regs_to_pcsx(bool need_cp2);
 static void lightrec_plugin_sync_regs_from_pcsx(bool need_cp2);
 
@@ -488,6 +601,7 @@ static void lightrec_plugin_execute_internal(bool block_only)
 {
 	struct lightrec_registers *regs;
 	u32 flags, cycles_pcsx;
+	u32 old_pc = psxRegs.pc;
 
 	regs = lightrec_get_registers(lightrec_state);
 	gen_interupt((psxCP0Regs *)regs->cp0);
@@ -522,6 +636,8 @@ static void lightrec_plugin_execute_internal(bool block_only)
 		if (flags & LIGHTREC_EXIT_SEGFAULT) {
 			fprintf(stderr, "Exiting at cycle 0x%08x\n",
 				psxRegs.cycle);
+			if (lightrec_debug)
+				print_for_big_ass_debugger();
 			exit(1);
 		}
 
@@ -542,6 +658,10 @@ static void lightrec_plugin_execute_internal(bool block_only)
 		}
 	}
 
+	if (lightrec_debug && psxRegs.cycle >= lightrec_begin_cycles && psxRegs.pc != old_pc) {
+		print_for_big_ass_debugger();
+	}
+
 	if ((regs->cp0[13] & regs->cp0[12] & 0x300) && (regs->cp0[12] & 0x1)) {
 		/* Handle software interrupts */
 		regs->cp0[13] &= ~0x7c;
@@ -552,7 +672,7 @@ static void lightrec_plugin_execute_internal(bool block_only)
 static void lightrec_plugin_execute(psxRegisters *regs)
 {
 	while (!regs->stop)
-		lightrec_plugin_execute_internal(false);
+		lightrec_plugin_execute_internal(lightrec_very_debug);
 }
 
 static void lightrec_plugin_execute_block(psxRegisters *regs,
-- 
2.39.5