mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-08 23:25:40 +12:00
Get our first AR code running
This commit is contained in:
parent
ae69c8f8c4
commit
97f8ea6cfd
6 changed files with 110 additions and 23 deletions
|
@ -3,9 +3,10 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
#include "memory.hpp"
|
||||||
|
|
||||||
class ActionReplay {
|
class ActionReplay {
|
||||||
using Cheat = std::vector<u32>; // A cheat is really just a bunch of u32 opcodes
|
using Cheat = std::vector<u32>; // A cheat is really just a bunch of 64-bit opcodes neatly encoded into 32-bit chunks
|
||||||
|
|
||||||
u32 offset1, offset2; // Memory offset registers. Non-persistent.
|
u32 offset1, offset2; // Memory offset registers. Non-persistent.
|
||||||
u32 data1, data2; // Data offset registers. Non-persistent.
|
u32 data1, data2; // Data offset registers. Non-persistent.
|
||||||
|
@ -14,9 +15,29 @@ class ActionReplay {
|
||||||
// When an instruction does not specify which offset or data register to use, we use the "active" one
|
// 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
|
// Which is by default #1 and may be changed by certain AR operations
|
||||||
u32 *activeOffset, *activeData;
|
u32 *activeOffset, *activeData;
|
||||||
|
|
||||||
|
// Program counter
|
||||||
|
u32 pc = 0;
|
||||||
|
Memory& mem;
|
||||||
|
|
||||||
|
// Has the cheat ended?
|
||||||
|
bool running = false;
|
||||||
|
// Run 1 AR instruction
|
||||||
|
void runInstruction(const Cheat& cheat, u32 instruction);
|
||||||
|
|
||||||
|
// Action Replay has a billion D-type opcodes so this handles all of them
|
||||||
|
void executeDType(const Cheat& cheat, u32 instruction);
|
||||||
|
|
||||||
|
u8 read8(u32 addr);
|
||||||
|
u16 read16(u32 addr);
|
||||||
|
u32 read32(u32 addr);
|
||||||
|
|
||||||
|
void write8(u32 addr, u8 value);
|
||||||
|
void write16(u32 addr, u16 value);
|
||||||
|
void write32(u32 addr, u32 value);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ActionReplay();
|
ActionReplay(Memory& mem);
|
||||||
void runCheat(const Cheat& cheat);
|
void runCheat(const Cheat& cheat);
|
||||||
void reset();
|
void reset();
|
||||||
};
|
};
|
|
@ -5,6 +5,9 @@
|
||||||
#include "action_replay.hpp"
|
#include "action_replay.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
|
||||||
|
// Forward-declare this since it's just passed and we don't want to include memory.hpp and increase compile time
|
||||||
|
class Memory;
|
||||||
|
|
||||||
class Cheats {
|
class Cheats {
|
||||||
public:
|
public:
|
||||||
enum class CheatType {
|
enum class CheatType {
|
||||||
|
@ -17,10 +20,10 @@ class Cheats {
|
||||||
std::vector<u32> instructions;
|
std::vector<u32> instructions;
|
||||||
};
|
};
|
||||||
|
|
||||||
Cheats();
|
Cheats(Memory& mem);
|
||||||
void addCheat(const Cheat& cheat);
|
void addCheat(const Cheat& cheat);
|
||||||
void runCheats();
|
|
||||||
void reset();
|
void reset();
|
||||||
|
void run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ActionReplay ar; // An ActionReplay cheat machine for executing CTRPF codes
|
ActionReplay ar; // An ActionReplay cheat machine for executing CTRPF codes
|
||||||
|
|
|
@ -32,7 +32,7 @@ class Emulator {
|
||||||
Memory memory;
|
Memory memory;
|
||||||
Kernel kernel;
|
Kernel kernel;
|
||||||
Crypto::AESEngine aesEngine;
|
Crypto::AESEngine aesEngine;
|
||||||
ActionReplay cheats;
|
Cheats cheats;
|
||||||
|
|
||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "action_replay.hpp"
|
#include "action_replay.hpp"
|
||||||
|
|
||||||
ActionReplay::ActionReplay() { reset(); }
|
ActionReplay::ActionReplay(Memory& mem) : mem(mem) { reset(); }
|
||||||
|
|
||||||
void ActionReplay::reset() {
|
void ActionReplay::reset() {
|
||||||
// Default value of storage regs is 0
|
// Default value of storage regs is 0
|
||||||
|
@ -11,4 +11,59 @@ void ActionReplay::reset() {
|
||||||
void ActionReplay::runCheat(const Cheat& cheat) {
|
void ActionReplay::runCheat(const Cheat& cheat) {
|
||||||
// Set offset and data registers to 0 at the start of a cheat
|
// Set offset and data registers to 0 at the start of a cheat
|
||||||
data1 = data2 = offset1 = offset2 = 0;
|
data1 = data2 = offset1 = offset2 = 0;
|
||||||
|
pc = 0;
|
||||||
|
running = true;
|
||||||
|
|
||||||
|
activeOffset = &offset1;
|
||||||
|
activeData = &data1;
|
||||||
|
|
||||||
|
while (running) {
|
||||||
|
// See if we can fetch 1 64-bit opcode, otherwise we're out of bounds. Cheats seem to end when going out of bounds?
|
||||||
|
if (pc + 1 >= cheat.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch instruction
|
||||||
|
const u32 instruction = cheat[pc++];
|
||||||
|
runInstruction(cheat, instruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 ActionReplay::read8(u32 addr) { return mem.read8(addr); }
|
||||||
|
u16 ActionReplay::read16(u32 addr) { return mem.read16(addr); }
|
||||||
|
u32 ActionReplay::read32(u32 addr) { return mem.read32(addr); }
|
||||||
|
|
||||||
|
void ActionReplay::write8(u32 addr, u8 value) { mem.write8(addr, value); }
|
||||||
|
void ActionReplay::write16(u32 addr, u16 value) { mem.write16(addr, value); }
|
||||||
|
void ActionReplay::write32(u32 addr, u32 value) { mem.write32(addr, value); }
|
||||||
|
|
||||||
|
void ActionReplay::runInstruction(const Cheat& cheat, u32 instruction) {
|
||||||
|
// Top nibble determines the instruction type
|
||||||
|
const u32 type = instruction >> 28;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
// 8 bit write to [XXXXXXX + offset]
|
||||||
|
case 0x2: {
|
||||||
|
const u32 baseAddr = Helpers::getBits<0, 28>(instruction);
|
||||||
|
const u8 value = u8(cheat[pc++]);
|
||||||
|
write8(baseAddr + *activeOffset, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less Than (YYYYYYYY < [XXXXXXX + offset])
|
||||||
|
case 0x4: {
|
||||||
|
const u32 baseAddr = Helpers::getBits<0, 28>(instruction);
|
||||||
|
const u32 imm = cheat[pc++];
|
||||||
|
const u32 value = read32(baseAddr + *activeOffset);
|
||||||
|
Helpers::panic("TODO: How do ActionReplay conditional blocks work?");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0xD: executeDType(cheat, instruction); break;
|
||||||
|
default: Helpers::panic("Unimplemented ActionReplay instruction type %X", type); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionReplay::executeDType(const Cheat& cheat, u32 instruction) {
|
||||||
|
Helpers::panic("ActionReplay: Unimplemented d-type opcode: %08X", instruction);
|
||||||
}
|
}
|
|
@ -1,13 +1,15 @@
|
||||||
#include "cheats.hpp"
|
#include "cheats.hpp"
|
||||||
|
|
||||||
Cheats::Cheats() { reset(); }
|
Cheats::Cheats(Memory& mem) : ar(mem) { reset(); }
|
||||||
|
|
||||||
void Cheats::reset() {
|
void Cheats::reset() {
|
||||||
cheats.clear(); // Unload loaded cheats
|
cheats.clear(); // Unload loaded cheats
|
||||||
ar.reset(); // Reset AR boi
|
ar.reset(); // Reset ActionReplay
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cheats::runCheats() {
|
void Cheats::addCheat(const Cheat& cheat) { cheats.push_back(cheat); }
|
||||||
|
|
||||||
|
void Cheats::run() {
|
||||||
for (const Cheat& cheat : cheats) {
|
for (const Cheat& cheat : cheats) {
|
||||||
switch (cheat.type) {
|
switch (cheat.type) {
|
||||||
case CheatType::ActionReplay: {
|
case CheatType::ActionReplay: {
|
||||||
|
|
|
@ -16,7 +16,7 @@ __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1;
|
||||||
|
|
||||||
Emulator::Emulator()
|
Emulator::Emulator()
|
||||||
: config(std::filesystem::current_path() / "config.toml"), kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory, config),
|
: config(std::filesystem::current_path() / "config.toml"), kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory, config),
|
||||||
memory(cpu.getTicksRef()) {
|
memory(cpu.getTicksRef()), cheats(memory) {
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
|
||||||
Helpers::panic("Failed to initialize SDL2");
|
Helpers::panic("Failed to initialize SDL2");
|
||||||
}
|
}
|
||||||
|
@ -104,20 +104,9 @@ void Emulator::run() {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
|
runFrame();
|
||||||
ServiceManager& srv = kernel.getServiceManager();
|
ServiceManager& srv = kernel.getServiceManager();
|
||||||
|
|
||||||
if (romType != ROMType::None) {
|
|
||||||
#ifdef PANDA3DS_ENABLE_HTTP_SERVER
|
|
||||||
pollHttpServer();
|
|
||||||
#endif
|
|
||||||
runFrame(); // Run 1 frame of instructions
|
|
||||||
gpu.display(); // Display graphics
|
|
||||||
|
|
||||||
// Send VBlank interrupts
|
|
||||||
srv.sendGPUInterrupt(GPUInterrupt::VBlank0);
|
|
||||||
srv.sendGPUInterrupt(GPUInterrupt::VBlank1);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
namespace Keys = HID::Keys;
|
namespace Keys = HID::Keys;
|
||||||
|
@ -344,7 +333,24 @@ void Emulator::run() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::runFrame() { cpu.runFrame(); }
|
void Emulator::runFrame() {
|
||||||
|
if (romType != ROMType::None) {
|
||||||
|
#ifdef PANDA3DS_ENABLE_HTTP_SERVER
|
||||||
|
pollHttpServer();
|
||||||
|
#endif
|
||||||
|
cpu.runFrame(); // Run 1 frame of instructions
|
||||||
|
gpu.display(); // Display graphics
|
||||||
|
|
||||||
|
// Send VBlank interrupts
|
||||||
|
ServiceManager& srv = kernel.getServiceManager();
|
||||||
|
srv.sendGPUInterrupt(GPUInterrupt::VBlank0);
|
||||||
|
srv.sendGPUInterrupt(GPUInterrupt::VBlank1);
|
||||||
|
|
||||||
|
if (haveCheats) [[unlikely]] {
|
||||||
|
cheats.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Emulator::loadROM(const std::filesystem::path& path) {
|
bool Emulator::loadROM(const std::filesystem::path& path) {
|
||||||
// Reset the emulator if we've already loaded a ROM
|
// Reset the emulator if we've already loaded a ROM
|
||||||
|
|
Loading…
Add table
Reference in a new issue