From d007b2d7807965e6ba1ff81db471af29ef396ff8 Mon Sep 17 00:00:00 2001
From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
Date: Fri, 21 Jul 2023 00:55:08 +0300
Subject: [PATCH 1/2] Emulator cleanup, add AR conditionals

---
 include/action_replay.hpp            |  7 ++-
 include/services/hid.hpp             |  1 +
 include/services/service_manager.hpp | 14 +----
 src/core/action_replay.cpp           | 27 +++++++-
 src/emulator.cpp                     | 92 ++++++++++++++--------------
 5 files changed, 79 insertions(+), 62 deletions(-)

diff --git a/include/action_replay.hpp b/include/action_replay.hpp
index a7cce230..d9499cee 100644
--- a/include/action_replay.hpp
+++ b/include/action_replay.hpp
@@ -1,5 +1,6 @@
 #pragma once
 #include <array>
+#include <bitset>
 #include <vector>
 
 #include "helpers.hpp"
@@ -7,6 +8,7 @@
 
 class ActionReplay {
 	using Cheat = std::vector<u32>;  // A cheat is really just a bunch of 64-bit opcodes neatly encoded into 32-bit chunks
+	static constexpr size_t ifStackSize = 32; // TODO: How big is this, really?
 
 	u32 offset1, offset2;    // Memory offset registers. Non-persistent.
 	u32 data1, data2;        // Data offset registers. Non-persistent.
@@ -15,7 +17,10 @@ class ActionReplay {
 	// When an instruction does not specify which offset or data register to use, we use the "active" one
 	// Which is by default #1 and may be changed by certain AR operations
 	u32 *activeOffset, *activeData, *activeStorage;
-	
+	u32 ifStackIndex;    // Our index in the if stack. Shows how many entries we have at the moment.
+	u32 loopStackIndex;  // Same but for loops
+	std::bitset<32> ifStack;
+
 	// Program counter
 	u32 pc = 0;
 	Memory& mem;
diff --git a/include/services/hid.hpp b/include/services/hid.hpp
index 6a3aab95..23a36ec6 100644
--- a/include/services/hid.hpp
+++ b/include/services/hid.hpp
@@ -91,6 +91,7 @@ class HIDService {
 	void pressKey(u32 mask) { newButtons |= mask; }
 	void releaseKey(u32 mask) { newButtons &= ~mask; }
 
+	u32 getOldButtons() { return oldButtons; }
 	s16 getCirclepadX() { return circlePadX; }
 	s16 getCirclepadY() { return circlePadY; }
 
diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp
index 1d93641c..51d6d554 100644
--- a/include/services/service_manager.hpp
+++ b/include/services/service_manager.hpp
@@ -90,17 +90,5 @@ class ServiceManager {
 	void signalDSPEvents() { dsp.signalEvents(); }
 
 	// Input function wrappers
-	void pressKey(u32 key) { hid.pressKey(key); }
-	void releaseKey(u32 key) { hid.releaseKey(key); }
-	s16 getCirclepadX() { return hid.getCirclepadX(); }
-	s16 getCirclepadY() { return hid.getCirclepadY(); }
-	void setCirclepadX(s16 x) { hid.setCirclepadX(x); }
-	void setCirclepadY(s16 y) { hid.setCirclepadY(y); }
-	void updateInputs(u64 currentTimestamp) { hid.updateInputs(currentTimestamp); }
-	void setTouchScreenPress(u16 x, u16 y) { hid.setTouchScreenPress(x, y); }
-	void releaseTouchScreen() { hid.releaseTouchScreen(); }
-
-	void setRoll(s16 roll) { hid.setRoll(roll); }
-	void setPitch(s16 pitch) { hid.setPitch(pitch); }
-	void setYaw(s16 yaw) { hid.setYaw(yaw); }
+	HIDService& getHID() { return hid; }
 };
diff --git a/src/core/action_replay.cpp b/src/core/action_replay.cpp
index cc3f6d20..2db0ace3 100644
--- a/src/core/action_replay.cpp
+++ b/src/core/action_replay.cpp
@@ -15,6 +15,8 @@ void ActionReplay::runCheat(const Cheat& cheat) {
 	// Set offset and data registers to 0 at the start of a cheat
 	data1 = data2 = offset1 = offset2 = 0;
 	pc = 0;
+	ifStackIndex = 0;
+	loopStackIndex = 0;
 	running = true;
 
 	activeOffset = &offset1;
@@ -25,9 +27,16 @@ void ActionReplay::runCheat(const Cheat& cheat) {
 		if (pc + 1 >= cheat.size()) {
 			return;
 		}
-
 		// Fetch instruction
 		const u32 instruction = cheat[pc++];
+		
+		// Instructions D0000000 00000000 and D2000000 00000000 are unconditional
+		bool isUnconditional = cheat[pc] == 0 && (instruction == 0xD0000000 || instruction == 0xD2000000);
+		if (ifStackIndex > 0 && !isUnconditional && !ifStack[ifStackIndex - 1]) {
+			pc++; // Eat up dummy word
+			continue; // Skip conditional instructions where the condition is false
+		}
+		
 		runInstruction(cheat, instruction);
 	}
 }
@@ -162,13 +171,27 @@ void ActionReplay::executeDType(const Cheat& cheat, u32 instruction) {
 				case 0x00020000: storage1 = data1; break;
 				case 0x00020001: storage2 = data2; break;
 				default:
-					Helpers::warn("Unknown ActionReplay data operation");
+					Helpers::warn("Unknown ActionReplay data operation: %08X", subopcode);
 					running = false;
 					break;
 			}
 			break;
 		}
 
+		// Control flow block operations
+		case 0xD2000000: {
+			const u32 subopcode = cheat[pc++];
+			switch (subopcode) {
+				// Ends all loop/execute blocks	
+				case 0:
+					loopStackIndex = 0;
+					ifStackIndex = 0;
+					break;
+				default: Helpers::panic("Unknown ActionReplay control flow operation: %08X", subopcode); break;
+			}
+			break;
+		}
+
 		default: Helpers::panic("ActionReplay: Unimplemented d-type opcode: %08X", instruction); break;
 	}
 }
\ No newline at end of file
diff --git a/src/emulator.cpp b/src/emulator.cpp
index ff9fc22f..39ab022c 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -105,7 +105,7 @@ void Emulator::run() {
 
 	while (running) {
 		runFrame();
-		ServiceManager& srv = kernel.getServiceManager();
+		HIDService& hid = kernel.getServiceManager().getHID();
 
 		SDL_Event event;
 		while (SDL_PollEvent(&event)) {
@@ -121,41 +121,41 @@ void Emulator::run() {
 					if (romType == ROMType::None) break;
 
 					switch (event.key.keysym.sym) {
-						case SDLK_l: srv.pressKey(Keys::A); break;
-						case SDLK_k: srv.pressKey(Keys::B); break;
-						case SDLK_o: srv.pressKey(Keys::X); break;
-						case SDLK_i: srv.pressKey(Keys::Y); break;
+						case SDLK_l: hid.pressKey(Keys::A); break;
+						case SDLK_k: hid.pressKey(Keys::B); break;
+						case SDLK_o: hid.pressKey(Keys::X); break;
+						case SDLK_i: hid.pressKey(Keys::Y); break;
 
-						case SDLK_q: srv.pressKey(Keys::L); break;
-						case SDLK_p: srv.pressKey(Keys::R); break;
+						case SDLK_q: hid.pressKey(Keys::L); break;
+						case SDLK_p: hid.pressKey(Keys::R); break;
 
-						case SDLK_RIGHT: srv.pressKey(Keys::Right); break;
-						case SDLK_LEFT: srv.pressKey(Keys::Left); break;
-						case SDLK_UP: srv.pressKey(Keys::Up); break;
-						case SDLK_DOWN: srv.pressKey(Keys::Down); break;
+						case SDLK_RIGHT: hid.pressKey(Keys::Right); break;
+						case SDLK_LEFT: hid.pressKey(Keys::Left); break;
+						case SDLK_UP: hid.pressKey(Keys::Up); break;
+						case SDLK_DOWN: hid.pressKey(Keys::Down); break;
 
 						case SDLK_w:
-							srv.setCirclepadY(0x9C);
+							hid.setCirclepadY(0x9C);
 							keyboardAnalogY = true;
 							break;
 
 						case SDLK_a:
-							srv.setCirclepadX(-0x9C);
+							hid.setCirclepadX(-0x9C);
 							keyboardAnalogX = true;
 							break;
 
 						case SDLK_s:
-							srv.setCirclepadY(-0x9C);
+							hid.setCirclepadY(-0x9C);
 							keyboardAnalogY = true;
 							break;
 
 						case SDLK_d:
-							srv.setCirclepadX(0x9C);
+							hid.setCirclepadX(0x9C);
 							keyboardAnalogX = true;
 							break;
 
-						case SDLK_RETURN: srv.pressKey(Keys::Start); break;
-						case SDLK_BACKSPACE: srv.pressKey(Keys::Select); break;
+						case SDLK_RETURN: hid.pressKey(Keys::Start); break;
+						case SDLK_BACKSPACE: hid.pressKey(Keys::Select); break;
 					}
 					break;
 
@@ -163,34 +163,34 @@ void Emulator::run() {
 					if (romType == ROMType::None) break;
 
 					switch (event.key.keysym.sym) {
-						case SDLK_l: srv.releaseKey(Keys::A); break;
-						case SDLK_k: srv.releaseKey(Keys::B); break;
-						case SDLK_o: srv.releaseKey(Keys::X); break;
-						case SDLK_i: srv.releaseKey(Keys::Y); break;
+						case SDLK_l: hid.releaseKey(Keys::A); break;
+						case SDLK_k: hid.releaseKey(Keys::B); break;
+						case SDLK_o: hid.releaseKey(Keys::X); break;
+						case SDLK_i: hid.releaseKey(Keys::Y); break;
 
-						case SDLK_q: srv.releaseKey(Keys::L); break;
-						case SDLK_p: srv.releaseKey(Keys::R); break;
+						case SDLK_q: hid.releaseKey(Keys::L); break;
+						case SDLK_p: hid.releaseKey(Keys::R); break;
 
-						case SDLK_RIGHT: srv.releaseKey(Keys::Right); break;
-						case SDLK_LEFT: srv.releaseKey(Keys::Left); break;
-						case SDLK_UP: srv.releaseKey(Keys::Up); break;
-						case SDLK_DOWN: srv.releaseKey(Keys::Down); break;
+						case SDLK_RIGHT: hid.releaseKey(Keys::Right); break;
+						case SDLK_LEFT: hid.releaseKey(Keys::Left); break;
+						case SDLK_UP: hid.releaseKey(Keys::Up); break;
+						case SDLK_DOWN: hid.releaseKey(Keys::Down); break;
 
 						// Err this is probably not ideal
 						case SDLK_w:
 						case SDLK_s:
-							srv.setCirclepadY(0);
+							hid.setCirclepadY(0);
 							keyboardAnalogY = false;
 							break;
 
 						case SDLK_a:
 						case SDLK_d:
-							srv.setCirclepadX(0);
+							hid.setCirclepadX(0);
 							keyboardAnalogX = false;
 							break;
 
-						case SDLK_RETURN: srv.releaseKey(Keys::Start); break;
-						case SDLK_BACKSPACE: srv.releaseKey(Keys::Select); break;
+						case SDLK_RETURN: hid.releaseKey(Keys::Start); break;
+						case SDLK_BACKSPACE: hid.releaseKey(Keys::Select); break;
 					}
 					break;
 
@@ -207,9 +207,9 @@ void Emulator::run() {
 							u16 x_converted = static_cast<u16>(x) - 40;
 							u16 y_converted = static_cast<u16>(y) - 240;
 
-							srv.setTouchScreenPress(x_converted, y_converted);
+							hid.setTouchScreenPress(x_converted, y_converted);
 						} else {
-							srv.releaseTouchScreen();
+							hid.releaseTouchScreen();
 						}
 					} else if (event.button.button == SDL_BUTTON_RIGHT) {
 						holdingRightClick = true;
@@ -221,7 +221,7 @@ void Emulator::run() {
 					if (romType == ROMType::None) break;
 
 					if (event.button.button == SDL_BUTTON_LEFT) {
-						srv.releaseTouchScreen();
+						hid.releaseTouchScreen();
 					} else if (event.button.button == SDL_BUTTON_RIGHT) {
 						holdingRightClick = false;
 					}
@@ -264,9 +264,9 @@ void Emulator::run() {
 
 					if (key != 0) {
 						if (event.cbutton.state == SDL_PRESSED) {
-							srv.pressKey(key);
+							hid.pressKey(key);
 						} else {
-							srv.releaseKey(key);
+							hid.releaseKey(key);
 						}
 					}
 					break;
@@ -285,8 +285,8 @@ void Emulator::run() {
 					// So up until then, we will set the gyroscope euler angles to fixed values based on the direction of the relative motion
 					const s32 roll = motionX > 0 ? 0x7f : -0x7f;
 					const s32 pitch = motionY > 0 ? 0x7f : -0x7f;
-					srv.setRoll(roll);
-					srv.setPitch(pitch);
+					hid.setRoll(roll);
+					hid.setPitch(pitch);
 					break;
 				}
 
@@ -313,19 +313,19 @@ void Emulator::run() {
 
 				// Avoid overriding the keyboard's circlepad input
 				if (abs(stickX) < deadzone && !keyboardAnalogX) {
-					srv.setCirclepadX(0);
+					hid.setCirclepadX(0);
 				} else {
-					srv.setCirclepadX(stickX / div);
+					hid.setCirclepadX(stickX / div);
 				}
 
 				if (abs(stickY) < deadzone && !keyboardAnalogY) {
-					srv.setCirclepadY(0);
+					hid.setCirclepadY(0);
 				} else {
-					srv.setCirclepadY(-(stickY / div));
+					hid.setCirclepadY(-(stickY / div));
 				}
 			}
 
-			srv.updateInputs(cpu.getTicks());
+			hid.updateInputs(cpu.getTicks());
 		}
 
 		// Update inputs in the HID module
@@ -452,7 +452,7 @@ void Emulator::initGraphicsContext() { gpu.initGraphicsContext(); }
 void Emulator::pollHttpServer() {
 	std::scoped_lock lock(httpServer.actionMutex);
 
-	ServiceManager& srv = kernel.getServiceManager();
+	HIDService& hid = kernel.getServiceManager().getHID();
 
 	if (httpServer.pendingAction) {
 		switch (httpServer.action) {
@@ -460,14 +460,14 @@ void Emulator::pollHttpServer() {
 
 			case HttpAction::PressKey:
 				if (httpServer.pendingKey != 0) {
-					srv.pressKey(httpServer.pendingKey);
+					hid.pressKey(httpServer.pendingKey);
 					httpServer.pendingKey = 0;
 				}
 				break;
 
 			case HttpAction::ReleaseKey:
 				if (httpServer.pendingKey != 0) {
-					srv.releaseKey(httpServer.pendingKey);
+					hid.releaseKey(httpServer.pendingKey);
 					httpServer.pendingKey = 0;
 				}
 				break;

From 4a4559930346df5a8f4a3eaddfb986f614b0929b Mon Sep 17 00:00:00 2001
From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
Date: Fri, 21 Jul 2023 03:34:21 +0300
Subject: [PATCH 2/2] [ActionReplay] Add HID stuff

---
 include/action_replay.hpp  |  6 +++++-
 include/cheats.hpp         |  5 +++--
 src/core/action_replay.cpp | 15 ++++++++++++++-
 src/core/cheats.cpp        |  2 +-
 src/emulator.cpp           |  2 +-
 5 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/include/action_replay.hpp b/include/action_replay.hpp
index d9499cee..a6b97df9 100644
--- a/include/action_replay.hpp
+++ b/include/action_replay.hpp
@@ -5,6 +5,7 @@
 
 #include "helpers.hpp"
 #include "memory.hpp"
+#include "services/hid.hpp"
 
 class ActionReplay {
 	using Cheat = std::vector<u32>;  // A cheat is really just a bunch of 64-bit opcodes neatly encoded into 32-bit chunks
@@ -24,6 +25,7 @@ class ActionReplay {
 	// Program counter
 	u32 pc = 0;
 	Memory& mem;
+	HIDService& hid;
 
 	// Has the cheat ended?
 	bool running = false;
@@ -41,8 +43,10 @@ class ActionReplay {
 	void write16(u32 addr, u16 value);
 	void write32(u32 addr, u32 value);
 
+	void pushConditionBlock(bool condition);
+
   public:
-	ActionReplay(Memory& mem);
+	ActionReplay(Memory& mem, HIDService& hid);
 	void runCheat(const Cheat& cheat);
 	void reset();
 };
\ No newline at end of file
diff --git a/include/cheats.hpp b/include/cheats.hpp
index d2ee8166..6ada7d20 100644
--- a/include/cheats.hpp
+++ b/include/cheats.hpp
@@ -4,6 +4,7 @@
 
 #include "action_replay.hpp"
 #include "helpers.hpp"
+#include "services/hid.hpp"
 
 // Forward-declare this since it's just passed and we don't want to include memory.hpp and increase compile time
 class Memory;
@@ -20,12 +21,12 @@ class Cheats {
 		std::vector<u32> instructions;
 	};
 
-	Cheats(Memory& mem);
+	Cheats(Memory& mem, HIDService& hid);
 	void addCheat(const Cheat& cheat);
 	void reset();
 	void run();
 
   private:
-	ActionReplay ar; // An ActionReplay cheat machine for executing CTRPF codes
+	ActionReplay ar;  // An ActionReplay cheat machine for executing CTRPF codes
 	std::vector<Cheat> cheats;
 };
\ No newline at end of file
diff --git a/src/core/action_replay.cpp b/src/core/action_replay.cpp
index 2db0ace3..ad391b36 100644
--- a/src/core/action_replay.cpp
+++ b/src/core/action_replay.cpp
@@ -1,6 +1,6 @@
 #include "action_replay.hpp"
 
-ActionReplay::ActionReplay(Memory& mem) : mem(mem) { reset(); }
+ActionReplay::ActionReplay(Memory& mem, HIDService& hid) : mem(mem), hid(hid) { reset(); }
 
 void ActionReplay::reset() {
 	// Default value of storage regs is 0
@@ -119,6 +119,9 @@ void ActionReplay::executeDType(const Cheat& cheat, u32 instruction) {
 		// DD000000 XXXXXXXX - if KEYPAD has value XXXXXXXX execute next block
 		case 0xDD000000: {
 			const u32 mask = cheat[pc++];
+			const u32 buttons = hid.getOldButtons();
+
+			pushConditionBlock((buttons & mask) == mask);
 			break;
 		}
 
@@ -194,4 +197,14 @@ void ActionReplay::executeDType(const Cheat& cheat, u32 instruction) {
 
 		default: Helpers::panic("ActionReplay: Unimplemented d-type opcode: %08X", instruction); break;
 	}
+}
+
+void ActionReplay::pushConditionBlock(bool condition) {
+	if (ifStackIndex >= 32) {
+		Helpers::warn("ActionReplay if stack overflowed");
+		running = false;
+		return;
+	}
+
+	ifStack[ifStackIndex++] = condition;
 }
\ No newline at end of file
diff --git a/src/core/cheats.cpp b/src/core/cheats.cpp
index fd772550..4c63652b 100644
--- a/src/core/cheats.cpp
+++ b/src/core/cheats.cpp
@@ -1,6 +1,6 @@
 #include "cheats.hpp"
 
-Cheats::Cheats(Memory& mem) : ar(mem) { reset(); }
+Cheats::Cheats(Memory& mem, HIDService& hid) : ar(mem, hid) { reset(); }
 
 void Cheats::reset() {
 	cheats.clear();  // Unload loaded cheats
diff --git a/src/emulator.cpp b/src/emulator.cpp
index 39ab022c..ce42d273 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -16,7 +16,7 @@ __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1;
 
 Emulator::Emulator()
 	: config(std::filesystem::current_path() / "config.toml"), kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory, config),
-	  memory(cpu.getTicksRef()), cheats(memory) {
+	  memory(cpu.getTicksRef()), cheats(memory, kernel.getServiceManager().getHID()) {
 	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
 		Helpers::panic("Failed to initialize SDL2");
 	}