Large diffs are not rendered by default.

@@ -11,12 +11,21 @@
#include <memory>
#include <string>

#include "Common/Event.h"
#include "Core/DSP/DSPBreakpoints.h"
#include "Core/DSP/DSPCaptureLogger.h"

class PointerWrap;

namespace DSP
{
class Accelerator;
class DSPCore;

namespace Interpreter
{
class Interpreter;
}

namespace JIT
{
@@ -216,6 +225,12 @@ enum class ExceptionType
ExternalInterrupt = 7 // 0x000e external int (message from CPU)
};

enum Mailbox : int
{
MAILBOX_CPU,
MAILBOX_DSP
};

struct DSP_Regs
{
u16 ar[4];
@@ -259,114 +274,299 @@ struct DSP_Regs
} ac[2];
};

struct DSPInitOptions
{
// DSP IROM blob, which is where the DSP boots from. Embedded into the DSP.
std::array<u16, DSP_IROM_SIZE> irom_contents;

// DSP DROM blob, which contains resampling coefficients.
std::array<u16, DSP_COEF_SIZE> coef_contents;

// Core used to emulate the DSP.
// Default: JIT64.
enum class CoreType
{
Interpreter,
JIT64,
};
CoreType core_type = CoreType::JIT64;

// Optional capture logger used to log internal DSP data transfers.
// Default: dummy implementation, does nothing.
DSPCaptureLogger* capture_logger;

DSPInitOptions() : capture_logger(new DefaultDSPCaptureLogger()) {}
};

// All the state of the DSP should be in this struct. Any DSP state that is not filled on init
// should be moved here.
struct SDSP
{
DSP_Regs r;
u16 pc;
#if PROFILE
u16 err_pc;
#endif
explicit SDSP(DSPCore& core);
~SDSP();

SDSP(const SDSP&) = delete;
SDSP& operator=(const SDSP&) = delete;

SDSP(SDSP&&) = delete;
SDSP& operator=(SDSP&&) = delete;

// Initializes overall state.
bool Initialize(const DSPInitOptions& opts);

// Shuts down any necessary DSP state.
void Shutdown();

// Resets DSP state as if the reset exception vector has been taken.
void Reset();

// Initializes the IFX registers.
void InitializeIFX();

// Writes to IFX registers.
void WriteIFX(u32 address, u16 value);

// Reads from IFX registers.
u16 ReadIFX(u16 address);

// Checks the whole value within a mailbox.
u32 PeekMailbox(Mailbox mailbox) const;

// Reads the low part of the value in the specified mailbox.
u16 ReadMailboxLow(Mailbox mailbox);

// Reads the high part of the value in the specified mailbox.
u16 ReadMailboxHigh(Mailbox mailbox);

// Writes to the low part of the mailbox.
void WriteMailboxLow(Mailbox mailbox, u16 value);

// Writes to the high part of the mailbox.
void WriteMailboxHigh(Mailbox mailbox, u16 value);

// Reads from instruction memory.
u16 ReadIMEM(u16 address) const;

// Reads from data memory.
u16 ReadDMEM(u16 address);

// Write to data memory.
void WriteDMEM(u16 address, u16 value);

// Fetches the next instruction and increments the PC.
u16 FetchInstruction();

// Fetches the instruction at the PC address, but doesn't increment the PC.
u16 PeekInstruction() const;

// Skips over the next instruction in memory.
void SkipInstruction();

// Sets the given flags in the SR register.
void SetSRFlag(u16 flag) { r.sr |= flag; }

// Whether or not the given flag is set in the SR register.
bool IsSRFlagSet(u16 flag) const { return (r.sr & flag) != 0; }

// Indicates that a particular exception has occurred
// and sets a flag in the pending exception register.
void SetException(ExceptionType exception);

// Checks if any exceptions occurred an updates the DSP state as appropriate.
void CheckExceptions();

// Notify that an external interrupt is pending (used by thread mode)
void SetExternalInterrupt(bool val);

// Coming from the CPU
void CheckExternalInterrupt();

// Stores a value into the specified stack
void StoreStack(StackRegister stack_reg, u16 val);

// Pops a value off of the specified stack
u16 PopStack(StackRegister stack_reg);

// Reads the current value from a particular register.
u16 ReadRegister(size_t reg) const;

// Writes a value to a given register.
void WriteRegister(size_t reg, u16 val);

// Saves and loads any necessary state.
void DoState(PointerWrap& p);

DSP_Regs r{};
u16 pc = 0;

// This is NOT the same cr as r.cr.
// This register is shared with the main emulation, see DSP.cpp
// The engine has control over 0x0C07 of this reg.
// Bits are defined in a struct in DSP.cpp.
u16 cr;
u16 cr = 0;

u8 reg_stack_ptrs[4];
u8 exceptions; // pending exceptions
volatile bool external_interrupt_waiting;
bool reset_dspjit_codespace;
u8 reg_stack_ptrs[4]{};
u8 exceptions = 0; // pending exceptions
volatile bool external_interrupt_waiting = false;
bool reset_dspjit_codespace = false;

// DSP hardware stacks. They're mapped to a bunch of registers, such that writes
// to them push and reads pop.
// Let's make stack depth 32 for now, which is way more than what's needed.
// The real DSP has different depths for the different stacks, but it would
// be strange if any ucode relied on stack overflows since on the DSP, when
// the stack overflows, you're screwed.
u16 reg_stacks[4][DSP_STACK_DEPTH];
u16 reg_stacks[4][DSP_STACK_DEPTH]{};

// For debugging.
u32 iram_crc;
u64 step_counter;
u32 iram_crc = 0;
u64 step_counter = 0;

// Mailbox.
std::atomic<u32> mbox[2];

// Accelerator / DMA / other hardware registers. Not GPRs.
std::array<u16, 256> ifx_regs;
std::array<u16, 256> ifx_regs{};

std::unique_ptr<Accelerator> accelerator;

// When state saving, all of the above can just be memcpy'd into the save state.
// The below needs special handling.
u16* iram;
u16* dram;
u16* irom;
u16* coef;
u16* iram = nullptr;
u16* dram = nullptr;
u16* irom = nullptr;
u16* coef = nullptr;

private:
void FreeMemoryPages();

void DoDMA();
const u8* DDMAIn(u16 dsp_addr, u32 addr, u32 size);
const u8* DDMAOut(u16 dsp_addr, u32 addr, u32 size);
const u8* IDMAIn(u16 dsp_addr, u32 addr, u32 size);
const u8* IDMAOut(u16 dsp_addr, u32 addr, u32 size);

u16 ReadIFXImpl(u16 address);

DSPCore& m_dsp_core;
};

extern SDSP g_dsp;
extern DSPBreakpoints g_dsp_breakpoints;
extern bool g_init_hax;
extern std::unique_ptr<JIT::DSPEmitter> g_dsp_jit;
extern std::unique_ptr<DSPCaptureLogger> g_dsp_cap;
enum class State
{
Stopped,
Running,
Stepping,
};

struct DSPInitOptions
class DSPCore
{
// DSP IROM blob, which is where the DSP boots from. Embedded into the DSP.
std::array<u16, DSP_IROM_SIZE> irom_contents;
public:
DSPCore();
~DSPCore();

// DSP DROM blob, which contains resampling coefficients.
std::array<u16, DSP_COEF_SIZE> coef_contents;
DSPCore(const DSPCore&) = delete;
DSPCore& operator=(const DSPCore&) = delete;

// Core used to emulate the DSP.
// Default: JIT64.
enum class CoreType
{
Interpreter,
JIT64,
};
CoreType core_type = CoreType::JIT64;
DSPCore(DSPCore&&) = delete;
DSPCore& operator=(DSPCore&&) = delete;

// Optional capture logger used to log internal DSP data transfers.
// Default: dummy implementation, does nothing.
DSPCaptureLogger* capture_logger;
// Initializes the DSP emulator using the provided options. Takes ownership of
// all the pointers contained in the options structure.
bool Initialize(const DSPInitOptions& opts);

DSPInitOptions() : capture_logger(new DefaultDSPCaptureLogger()) {}
};
// Shuts down the DSP core and cleans up any necessary state.
void Shutdown();

// Initializes the DSP emulator using the provided options. Takes ownership of
// all the pointers contained in the options structure.
bool DSPCore_Init(const DSPInitOptions& opts);
// Delegates to JIT or interpreter as appropriate.
// Handle state changes and stepping.
int RunCycles(int cycles);

void DSPCore_Reset();
void DSPCore_Shutdown(); // Frees all allocated memory.
// Steps the DSP by a single instruction.
void Step();

void DSPCore_CheckExternalInterrupt();
void DSPCore_CheckExceptions();
void DSPCore_SetExternalInterrupt(bool val);
// Resets DSP state as if the reset exception vector has been taken.
void Reset();

// sets a flag in the pending exception register.
void DSPCore_SetException(ExceptionType exception);
// Clears the DSP instruction RAM.
void ClearIRAM();

enum class State
{
Stopped,
Running,
Stepping,
};
// Dictates whether or not the DSP is currently stopped, running or stepping
// through instructions.
void SetState(State new_state);

// Retrieves the current execution state of the DSP.
State GetState() const;

// Indicates that a particular exception has occurred
// and sets a flag in the pending exception register.
void SetException(ExceptionType exception);

// Notify that an external interrupt is pending (used by thread mode)
void SetExternalInterrupt(bool val);

// Coming from the CPU
void CheckExternalInterrupt();

// Checks if any exceptions occurred an updates the DSP state as appropriate.
void CheckExceptions();

int DSPCore_RunCycles(int cycles);
// Reads the current value from a particular register.
u16 ReadRegister(size_t reg) const;

// These are meant to be called from the UI thread.
void DSPCore_SetState(State new_state);
State DSPCore_GetState();
// Writes a value to a given register.
void WriteRegister(size_t reg, u16 val);

void DSPCore_Step();
// Checks the value within a mailbox.
u32 PeekMailbox(Mailbox mailbox) const;

u16 DSPCore_ReadRegister(size_t reg);
void DSPCore_WriteRegister(size_t reg, u16 val);
// Reads the low part of the specified mailbox register.
u16 ReadMailboxLow(Mailbox mailbox);

// Reads the high part of the specified mailbox register.
u16 ReadMailboxHigh(Mailbox mailbox);

// Writes to the low part of the mailbox register.
void WriteMailboxLow(Mailbox mailbox, u16 value);

// Writes to the high part of the mailbox register.
void WriteMailboxHigh(Mailbox mailbox, u16 value);

// Logs an IFX register read.
void LogIFXRead(u16 address, u16 read_value);

// Logs an IFX register write.
void LogIFXWrite(u16 address, u16 written_value);

// Logs a DMA operation
void LogDMA(u16 control, u32 gc_address, u16 dsp_address, u16 length, const u8* data);

// Whether or not the JIT has been created.
bool IsJITCreated() const;

// Writes or loads state for savestates.
void DoState(PointerWrap& p);

// Accessors for the DSP breakpoint facilities.
DSPBreakpoints& BreakPoints() { return m_dsp_breakpoints; }
const DSPBreakpoints& BreakPoints() const { return m_dsp_breakpoints; }

SDSP& DSPState() { return m_dsp; }
const SDSP& DSPState() const { return m_dsp; }

Interpreter::Interpreter& GetInterpreter() { return *m_dsp_interpreter; }
const Interpreter::Interpreter& GetInterpreter() const { return *m_dsp_interpreter; }

bool GetInitHax() const { return m_init_hax; }
void SetInitHax(bool value) { m_init_hax = value; }

private:
SDSP m_dsp;
DSPBreakpoints m_dsp_breakpoints;
State m_core_state = State::Stopped;
bool m_init_hax = false;
std::unique_ptr<Interpreter::Interpreter> m_dsp_interpreter;
std::unique_ptr<JIT::DSPEmitter> m_dsp_jit;
std::unique_ptr<DSPCaptureLogger> m_dsp_cap;
Common::Event m_step_event;
};
} // namespace DSP

Large diffs are not rendered by default.

This file was deleted.

@@ -13,6 +13,11 @@
// core isn't used, for example in an asm/disasm tool, then most of these
// can be stubbed out.

namespace DSP
{
class DSPCore;
}

namespace DSP::Host
{
u8 ReadHostMemory(u32 addr);
@@ -23,7 +28,7 @@ void OSD_AddMessage(std::string str, u32 ms);
bool OnThread();
bool IsWiiHost();
void InterruptRequest();
void CodeLoaded(u32 addr, size_t size);
void CodeLoaded(const u8* ptr, size_t size);
void CodeLoaded(DSPCore& dsp, u32 addr, size_t size);
void CodeLoaded(DSPCore& dsp, const u8* ptr, size_t size);
void UpdateDebugger();
} // namespace DSP::Host
@@ -3,86 +3,81 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "Core/DSP/DSPMemoryMap.h"

#include "Common/Logging/Log.h"

#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPHWInterface.h"
#include "Core/DSP/DSPTables.h"

namespace DSP
{
u16 dsp_imem_read(u16 addr)
u16 SDSP::ReadIMEM(u16 address) const
{
switch (addr >> 12)
switch (address >> 12)
{
case 0: // 0xxx IRAM
return g_dsp.iram[addr & DSP_IRAM_MASK];
return iram[address & DSP_IRAM_MASK];

case 8: // 8xxx IROM - contains code to receive code for IRAM, and a bunch of mixing loops.
return g_dsp.irom[addr & DSP_IROM_MASK];
return irom[address & DSP_IROM_MASK];

default: // Unmapped/non-existing memory
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Executing from invalid ({:04x}) memory", g_dsp.pc,
addr);
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Executing from invalid ({:04x}) memory", pc, address);
return 0;
}
}

u16 dsp_dmem_read(u16 addr)
u16 SDSP::ReadDMEM(u16 address)
{
switch (addr >> 12)
switch (address >> 12)
{
case 0x0: // 0xxx DRAM
return g_dsp.dram[addr & DSP_DRAM_MASK];
return dram[address & DSP_DRAM_MASK];

case 0x1: // 1xxx COEF
DEBUG_LOG_FMT(DSPLLE, "{:04x} : Coefficient Read @ {:04x}", g_dsp.pc, addr);
return g_dsp.coef[addr & DSP_COEF_MASK];
DEBUG_LOG_FMT(DSPLLE, "{:04x} : Coefficient Read @ {:04x}", pc, address);
return coef[address & DSP_COEF_MASK];

case 0xf: // Fxxx HW regs
return gdsp_ifx_read(addr);
return ReadIFX(address);

default: // Unmapped/non-existing memory
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Read from UNKNOWN ({:04x}) memory", g_dsp.pc, addr);
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Read from UNKNOWN ({:04x}) memory", pc, address);
return 0;
}
}

void dsp_dmem_write(u16 addr, u16 val)
void SDSP::WriteDMEM(u16 address, u16 value)
{
switch (addr >> 12)
switch (address >> 12)
{
case 0x0: // 0xxx DRAM
g_dsp.dram[addr & DSP_DRAM_MASK] = val;
dram[address & DSP_DRAM_MASK] = value;
break;

case 0xf: // Fxxx HW regs
gdsp_ifx_write(addr, val);
WriteIFX(address, value);
break;

default: // Unmapped/non-existing memory
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Write to UNKNOWN ({:04x}) memory", g_dsp.pc, addr);
ERROR_LOG_FMT(DSPLLE, "{:04x} DSP ERROR: Write to UNKNOWN ({:04x}) memory", pc, address);
break;
}
}

u16 dsp_fetch_code()
u16 SDSP::FetchInstruction()
{
u16 opc = dsp_imem_read(g_dsp.pc);

g_dsp.pc++;
const u16 opc = PeekInstruction();
pc++;
return opc;
}

u16 dsp_peek_code()
u16 SDSP::PeekInstruction() const
{
return dsp_imem_read(g_dsp.pc);
return ReadIMEM(pc);
}

void dsp_skip_inst()
void SDSP::SkipInstruction()
{
g_dsp.pc += GetOpTemplate(dsp_peek_code())->size;
pc += GetOpTemplate(PeekInstruction())->size;
}
} // namespace DSP

This file was deleted.

@@ -3,8 +3,6 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "Core/DSP/DSPStacks.h"

#include <cstddef>

#include "Common/CommonTypes.h"
@@ -13,34 +11,26 @@
// Stacks. The stacks are outside the DSP RAM, in dedicated hardware.
namespace DSP
{
static void dsp_reg_stack_push(size_t stack_reg)
{
g_dsp.reg_stack_ptrs[stack_reg]++;
g_dsp.reg_stack_ptrs[stack_reg] &= DSP_STACK_MASK;
g_dsp.reg_stacks[stack_reg][g_dsp.reg_stack_ptrs[stack_reg]] = g_dsp.r.st[stack_reg];
}

static void dsp_reg_stack_pop(size_t stack_reg)
{
g_dsp.r.st[stack_reg] = g_dsp.reg_stacks[stack_reg][g_dsp.reg_stack_ptrs[stack_reg]];
g_dsp.reg_stack_ptrs[stack_reg]--;
g_dsp.reg_stack_ptrs[stack_reg] &= DSP_STACK_MASK;
}

void dsp_reg_store_stack(StackRegister stack_reg, u16 val)
void SDSP::StoreStack(StackRegister stack_reg, u16 val)
{
const auto reg_index = static_cast<size_t>(stack_reg);

dsp_reg_stack_push(reg_index);
g_dsp.r.st[reg_index] = val;
reg_stack_ptrs[reg_index]++;
reg_stack_ptrs[reg_index] &= DSP_STACK_MASK;
reg_stacks[reg_index][reg_stack_ptrs[reg_index]] = r.st[reg_index];

r.st[reg_index] = val;
}

u16 dsp_reg_load_stack(StackRegister stack_reg)
u16 SDSP::PopStack(StackRegister stack_reg)
{
const auto reg_index = static_cast<size_t>(stack_reg);
const u16 val = r.st[reg_index];

r.st[reg_index] = reg_stacks[reg_index][reg_stack_ptrs[reg_index]];
reg_stack_ptrs[reg_index]--;
reg_stack_ptrs[reg_index] &= DSP_STACK_MASK;

const u16 val = g_dsp.r.st[reg_index];
dsp_reg_stack_pop(reg_index);
return val;
}
} // namespace DSP

This file was deleted.

@@ -477,9 +477,6 @@ const std::array<pdlabel_t, 36> regnames =
}};
// clang-format on

std::array<u16, WRITEBACK_LOG_SIZE> writeBackLog;
std::array<int, WRITEBACK_LOG_SIZE> writeBackLogIdx;

const char* pdname(u16 val)
{
static char tmpstr[12]; // nasty
@@ -612,10 +609,5 @@ void InitInstructionTable()
else
ERROR_LOG_FMT(DSPLLE, "opcode table place {} already in use for {}", i, iter->name);
}

writeBackLogIdx.fill(-1);

// Ensure the interpreter tables are all set up, as JITs also rely on them.
Interpreter::InitInstructionTables();
}
} // namespace DSP
@@ -85,10 +85,6 @@ struct DSPOPCTemplate
// Opcodes
extern const DSPOPCTemplate cw;

constexpr size_t WRITEBACK_LOG_SIZE = 5;
extern std::array<u16, WRITEBACK_LOG_SIZE> writeBackLog;
extern std::array<int, WRITEBACK_LOG_SIZE> writeBackLogIdx;

// Predefined labels
struct pdlabel_t
{
@@ -105,9 +101,6 @@ const char* pdregname(int val);
const char* pdregnamelong(int val);

void InitInstructionTable();
void ApplyWriteBackLog();
void ZeroWriteBackLog();
void ZeroWriteBackLogPreserveAcc(u8 acc);

// Used by the assembler and disassembler for info retrieval.
const DSPOPCTemplate* FindOpInfoByOpcode(UDSPInstruction opcode);

Large diffs are not rendered by default.

@@ -5,10 +5,6 @@
// Additional copyrights go to Duddie and Tratax (c) 2004

#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/DSPStacks.h"
#include "Core/DSP/Interpreter/DSPIntCCUtil.h"
#include "Core/DSP/Interpreter/DSPIntUtil.h"
#include "Core/DSP/Interpreter/DSPInterpreter.h"

namespace DSP::Interpreter
@@ -20,14 +16,16 @@ namespace DSP::Interpreter
// Call function if condition cc has been met. Push program counter of
// instruction following "call" to $st0. Set program counter to address
// represented by value that follows this "call" instruction.
void call(const UDSPInstruction opc)
void Interpreter::call(const UDSPInstruction opc)
{
auto& state = m_dsp_core.DSPState();

// must be outside the if.
u16 dest = dsp_fetch_code();
const u16 dest = state.FetchInstruction();
if (CheckCondition(opc & 0xf))
{
dsp_reg_store_stack(StackRegister::Call, g_dsp.pc);
g_dsp.pc = dest;
state.StoreStack(StackRegister::Call, state.pc);
state.pc = dest;
}
}

@@ -37,28 +35,29 @@ void call(const UDSPInstruction opc)
// Call function if condition cc has been met. Push program counter of
// instruction following "call" to call stack $st0. Set program counter to
// register $R.
void callr(const UDSPInstruction opc)
void Interpreter::callr(const UDSPInstruction opc)
{
if (CheckCondition(opc & 0xf))
{
u8 reg = (opc >> 5) & 0x7;
u16 addr = dsp_op_read_reg(reg);
dsp_reg_store_stack(StackRegister::Call, g_dsp.pc);
g_dsp.pc = addr;
}
if (!CheckCondition(opc & 0xf))
return;

auto& state = m_dsp_core.DSPState();
const u8 reg = (opc >> 5) & 0x7;
const u16 addr = OpReadRegister(reg);
state.StoreStack(StackRegister::Call, state.pc);
state.pc = addr;
}

// Generic if implementation
// IFcc
// 0000 0010 0111 cccc
// Execute following opcode if the condition has been met.
void ifcc(const UDSPInstruction opc)
void Interpreter::ifcc(const UDSPInstruction opc)
{
if (!CheckCondition(opc & 0xf))
{
// skip the next opcode - we have to lookup its size.
dsp_skip_inst();
}
if (CheckCondition(opc & 0xf))
return;

// skip the next opcode - we have to lookup its size.
m_dsp_core.DSPState().SkipInstruction();
}

// Generic jmp implementation
@@ -67,59 +66,64 @@ void ifcc(const UDSPInstruction opc)
// aaaa aaaa aaaa aaaa
// Jump to addressA if condition cc has been met. Set program counter to
// address represented by value that follows this "jmp" instruction.
void jcc(const UDSPInstruction opc)
void Interpreter::jcc(const UDSPInstruction opc)
{
u16 dest = dsp_fetch_code();
auto& state = m_dsp_core.DSPState();
const u16 dest = state.FetchInstruction();
if (CheckCondition(opc & 0xf))
{
g_dsp.pc = dest;
state.pc = dest;
}
}

// Generic jmpr implementation
// JMPcc $R
// 0001 0111 rrr0 cccc
// Jump to address; set program counter to a value from register $R.
void jmprcc(const UDSPInstruction opc)
void Interpreter::jmprcc(const UDSPInstruction opc)
{
if (CheckCondition(opc & 0xf))
{
u8 reg = (opc >> 5) & 0x7;
g_dsp.pc = dsp_op_read_reg(reg);
}
if (!CheckCondition(opc & 0xf))
return;

auto& state = m_dsp_core.DSPState();
const u8 reg = (opc >> 5) & 0x7;
state.pc = OpReadRegister(reg);
}

// Generic ret implementation
// RETcc
// 0000 0010 1101 cccc
// Return from subroutine if condition cc has been met. Pops stored PC
// from call stack $st0 and sets $pc to this location.
void ret(const UDSPInstruction opc)
void Interpreter::ret(const UDSPInstruction opc)
{
if (CheckCondition(opc & 0xf))
{
g_dsp.pc = dsp_reg_load_stack(StackRegister::Call);
}
if (!CheckCondition(opc & 0xf))
return;

auto& state = m_dsp_core.DSPState();
state.pc = state.PopStack(StackRegister::Call);
}

// RTI
// 0000 0010 1111 1111
// Return from exception. Pops stored status register $sr from data stack
// $st1 and program counter PC from call stack $st0 and sets $pc to this
// location.
void rti(const UDSPInstruction opc)
void Interpreter::rti(const UDSPInstruction)
{
g_dsp.r.sr = dsp_reg_load_stack(StackRegister::Data);
g_dsp.pc = dsp_reg_load_stack(StackRegister::Call);
auto& state = m_dsp_core.DSPState();
state.r.sr = state.PopStack(StackRegister::Data);
state.pc = state.PopStack(StackRegister::Call);
}

// HALT
// 0000 0000 0020 0001
// Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR.
void halt(const UDSPInstruction opc)
void Interpreter::halt(const UDSPInstruction)
{
g_dsp.cr |= 0x4;
g_dsp.pc--;
auto& state = m_dsp_core.DSPState();
state.cr |= 0x4;
state.pc--;
}

// LOOP handling: Loop stack is used to control execution of repeated blocks of
@@ -128,29 +132,31 @@ void halt(const UDSPInstruction opc)
// then PC is modified with value from call stack $st0. Otherwise values from
// call stack $st0 and both loop stacks $st2 and $st3 are popped and execution
// continues at next opcode.
void HandleLoop()
void Interpreter::HandleLoop()
{
auto& state = m_dsp_core.DSPState();

// Handle looping hardware.
const u16 rCallAddress = g_dsp.r.st[0];
const u16 rLoopAddress = g_dsp.r.st[2];
u16& rLoopCounter = g_dsp.r.st[3];
const u16 rCallAddress = state.r.st[0];
const u16 rLoopAddress = state.r.st[2];
u16& rLoopCounter = state.r.st[3];

if (rLoopAddress > 0 && rLoopCounter > 0)
{
// FIXME: why -1? because we just read past it.
if (g_dsp.pc - 1 == rLoopAddress)
if (state.pc - 1 == rLoopAddress)
{
rLoopCounter--;
if (rLoopCounter > 0)
{
g_dsp.pc = rCallAddress;
state.pc = rCallAddress;
}
else
{
// end of loop
dsp_reg_load_stack(StackRegister::Call);
dsp_reg_load_stack(StackRegister::LoopAddress);
dsp_reg_load_stack(StackRegister::LoopCounter);
state.PopStack(StackRegister::Call);
state.PopStack(StackRegister::LoopAddress);
state.PopStack(StackRegister::LoopCounter);
}
}
}
@@ -164,21 +170,22 @@ void HandleLoop()
// then looped instruction will not get executed.
// Actually, this instruction simply prepares the loop stacks for the above.
// The looping hardware takes care of the rest.
void loop(const UDSPInstruction opc)
void Interpreter::loop(const UDSPInstruction opc)
{
u16 reg = opc & 0x1f;
u16 cnt = dsp_op_read_reg(reg);
u16 loop_pc = g_dsp.pc;
auto& state = m_dsp_core.DSPState();
const u16 reg = opc & 0x1f;
const u16 cnt = OpReadRegister(reg);
const u16 loop_pc = state.pc;

if (cnt)
if (cnt != 0)
{
dsp_reg_store_stack(StackRegister::Call, g_dsp.pc);
dsp_reg_store_stack(StackRegister::LoopAddress, loop_pc);
dsp_reg_store_stack(StackRegister::LoopCounter, cnt);
state.StoreStack(StackRegister::Call, state.pc);
state.StoreStack(StackRegister::LoopAddress, loop_pc);
state.StoreStack(StackRegister::LoopCounter, cnt);
}
else
{
dsp_skip_inst();
state.SkipInstruction();
}
}

@@ -190,20 +197,21 @@ void loop(const UDSPInstruction opc)
// instruction will not get executed.
// Actually, this instruction simply prepares the loop stacks for the above.
// The looping hardware takes care of the rest.
void loopi(const UDSPInstruction opc)
void Interpreter::loopi(const UDSPInstruction opc)
{
u16 cnt = opc & 0xff;
u16 loop_pc = g_dsp.pc;
auto& state = m_dsp_core.DSPState();
const u16 cnt = opc & 0xff;
const u16 loop_pc = state.pc;

if (cnt)
if (cnt != 0)
{
dsp_reg_store_stack(StackRegister::Call, g_dsp.pc);
dsp_reg_store_stack(StackRegister::LoopAddress, loop_pc);
dsp_reg_store_stack(StackRegister::LoopCounter, cnt);
state.StoreStack(StackRegister::Call, state.pc);
state.StoreStack(StackRegister::LoopAddress, loop_pc);
state.StoreStack(StackRegister::LoopCounter, cnt);
}
else
{
dsp_skip_inst();
state.SkipInstruction();
}
}

@@ -216,22 +224,23 @@ void loopi(const UDSPInstruction opc)
// included in loop. Counter is pushed on loop stack $st3, end of block address
// is pushed on loop stack $st2 and repeat address is pushed on call stack $st0.
// Up to 4 nested loops are allowed.
void bloop(const UDSPInstruction opc)
void Interpreter::bloop(const UDSPInstruction opc)
{
u16 reg = opc & 0x1f;
u16 cnt = dsp_op_read_reg(reg);
u16 loop_pc = dsp_fetch_code();
auto& state = m_dsp_core.DSPState();
const u16 reg = opc & 0x1f;
const u16 cnt = OpReadRegister(reg);
const u16 loop_pc = state.FetchInstruction();

if (cnt)
if (cnt != 0)
{
dsp_reg_store_stack(StackRegister::Call, g_dsp.pc);
dsp_reg_store_stack(StackRegister::LoopAddress, loop_pc);
dsp_reg_store_stack(StackRegister::LoopCounter, cnt);
state.StoreStack(StackRegister::Call, state.pc);
state.StoreStack(StackRegister::LoopAddress, loop_pc);
state.StoreStack(StackRegister::LoopCounter, cnt);
}
else
{
g_dsp.pc = loop_pc;
dsp_skip_inst();
state.pc = loop_pc;
state.SkipInstruction();
}
}

@@ -244,21 +253,22 @@ void bloop(const UDSPInstruction opc)
// loop. Counter is pushed on loop stack $st3, end of block address is pushed
// on loop stack $st2 and repeat address is pushed on call stack $st0. Up to 4
// nested loops are allowed.
void bloopi(const UDSPInstruction opc)
void Interpreter::bloopi(const UDSPInstruction opc)
{
u16 cnt = opc & 0xff;
u16 loop_pc = dsp_fetch_code();
auto& state = m_dsp_core.DSPState();
const u16 cnt = opc & 0xff;
const u16 loop_pc = state.FetchInstruction();

if (cnt)
if (cnt != 0)
{
dsp_reg_store_stack(StackRegister::Call, g_dsp.pc);
dsp_reg_store_stack(StackRegister::LoopAddress, loop_pc);
dsp_reg_store_stack(StackRegister::LoopCounter, cnt);
state.StoreStack(StackRegister::Call, state.pc);
state.StoreStack(StackRegister::LoopAddress, loop_pc);
state.StoreStack(StackRegister::LoopCounter, cnt);
}
else
{
g_dsp.pc = loop_pc;
dsp_skip_inst();
state.pc = loop_pc;
state.SkipInstruction();
}
}
} // namespace DSP::Interpreter

This file was deleted.

@@ -6,36 +6,29 @@

#pragma once

// Anything to do with SR and conditions goes here.

#include "Common/CommonTypes.h"

// Anything to do with SR and conditions goes here.

namespace DSP::Interpreter
{
bool CheckCondition(u8 _Condition);

void Update_SR_Register16(s16 _Value, bool carry = false, bool overflow = false,
bool overS32 = false);
void Update_SR_Register64(s64 _Value, bool carry = false, bool overflow = false);
void Update_SR_LZ(bool value);

inline bool isCarry(u64 val, u64 result)
constexpr bool isCarry(u64 val, u64 result)
{
return (val > result);
return val > result;
}

inline bool isCarry2(u64 val, u64 result)
constexpr bool isCarry2(u64 val, u64 result)
{
return (val >= result);
return val >= result;
}

inline bool isOverflow(s64 val1, s64 val2, s64 res)
constexpr bool isOverflow(s64 val1, s64 val2, s64 res)
{
return ((val1 ^ res) & (val2 ^ res)) < 0;
}

inline bool isOverS32(s64 acc)
constexpr bool isOverS32(s64 acc)
{
return (acc != (s32)acc) ? true : false;
return acc != static_cast<s32>(acc);
}
} // namespace DSP::Interpreter

Large diffs are not rendered by default.

This file was deleted.

@@ -4,8 +4,7 @@
//
// Additional copyrights go to Duddie and Tratax (c) 2004

#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/Interpreter/DSPIntUtil.h"
#include "Common/CommonTypes.h"
#include "Core/DSP/Interpreter/DSPInterpreter.h"

namespace DSP::Interpreter
@@ -16,247 +15,267 @@ namespace DSP::Interpreter
// CR[0-7] | M. That is, the upper 8 bits of the address are the
// bottom 8 bits from CR, and the lower 8 bits are from the 8-bit immediate.
// Note: pc+=2 in duddie's doc seems wrong
void srs(const UDSPInstruction opc)
void Interpreter::srs(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x7) + 0x18;
u16 addr = (g_dsp.r.cr << 8) | (opc & 0xFF);
auto& state = m_dsp_core.DSPState();
const auto reg = static_cast<u8>(((opc >> 8) & 0x7) + 0x18);
const auto addr = static_cast<u16>((state.r.cr << 8) | (opc & 0xFF));

if (reg >= DSP_REG_ACM0)
dsp_dmem_write(addr, dsp_op_read_reg_and_saturate(reg - DSP_REG_ACM0));
state.WriteDMEM(addr, OpReadRegisterAndSaturate(reg - DSP_REG_ACM0));
else
dsp_dmem_write(addr, dsp_op_read_reg(reg));
state.WriteDMEM(addr, OpReadRegister(reg));
}

// LRS $(0x18+D), @M
// 0010 0ddd mmmm mmmm
// Move value from data memory pointed by address CR[0-7] | M to register
// $(0x18+D). That is, the upper 8 bits of the address are the bottom 8 bits
// from CR, and the lower 8 bits are from the 8-bit immediate.
void lrs(const UDSPInstruction opc)
void Interpreter::lrs(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x7) + 0x18;
u16 addr = (g_dsp.r.cr << 8) | (opc & 0xFF);
dsp_op_write_reg(reg, dsp_dmem_read(addr));
dsp_conditional_extend_accum(reg);
auto& state = m_dsp_core.DSPState();
const auto reg = static_cast<u8>(((opc >> 8) & 0x7) + 0x18);
const auto addr = static_cast<u16>((state.r.cr << 8) | (opc & 0xFF));

OpWriteRegister(reg, state.ReadDMEM(addr));
ConditionalExtendAccum(reg);
}

// LR $D, @M
// 0000 0000 110d dddd
// mmmm mmmm mmmm mmmm
// Move value from data memory pointed by address M to register $D.
void lr(const UDSPInstruction opc)
void Interpreter::lr(const UDSPInstruction opc)
{
u8 reg = opc & 0x1F;
u16 addr = dsp_fetch_code();
u16 val = dsp_dmem_read(addr);
dsp_op_write_reg(reg, val);
dsp_conditional_extend_accum(reg);
auto& state = m_dsp_core.DSPState();
const u8 reg = opc & 0x1F;
const u16 addr = state.FetchInstruction();
const u16 val = state.ReadDMEM(addr);

OpWriteRegister(reg, val);
ConditionalExtendAccum(reg);
}

// SR @M, $S
// 0000 0000 111s ssss
// mmmm mmmm mmmm mmmm
// Store value from register $S to a memory pointed by address M.
void sr(const UDSPInstruction opc)
void Interpreter::sr(const UDSPInstruction opc)
{
u8 reg = opc & 0x1F;
u16 addr = dsp_fetch_code();
auto& state = m_dsp_core.DSPState();
const u8 reg = opc & 0x1F;
const u16 addr = state.FetchInstruction();

if (reg >= DSP_REG_ACM0)
dsp_dmem_write(addr, dsp_op_read_reg_and_saturate(reg - DSP_REG_ACM0));
state.WriteDMEM(addr, OpReadRegisterAndSaturate(reg - DSP_REG_ACM0));
else
dsp_dmem_write(addr, dsp_op_read_reg(reg));
state.WriteDMEM(addr, OpReadRegister(reg));
}

// SI @M, #I
// 0001 0110 mmmm mmmm
// iiii iiii iiii iiii
// Store 16-bit immediate value I to a memory location pointed by address
// M (M is 8-bit value sign extended).
void si(const UDSPInstruction opc)
void Interpreter::si(const UDSPInstruction opc)
{
u16 addr = (s8)opc;
u16 imm = dsp_fetch_code();
dsp_dmem_write(addr, imm);
auto& state = m_dsp_core.DSPState();
const u16 addr = static_cast<u16>(static_cast<s8>(opc));
const u16 imm = state.FetchInstruction();

state.WriteDMEM(addr, imm);
}

// LRR $D, @$S
// 0001 1000 0ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
void lrr(const UDSPInstruction opc)
void Interpreter::lrr(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;
const u8 sreg = (opc >> 5) & 0x3;
const u8 dreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();

u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
dsp_op_write_reg(dreg, val);
dsp_conditional_extend_accum(dreg);
const u16 val = state.ReadDMEM(OpReadRegister(sreg));
OpWriteRegister(dreg, val);
ConditionalExtendAccum(dreg);
}

// LRRD $D, @$S
// 0001 1000 1ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Decrement register $S.
void lrrd(const UDSPInstruction opc)
void Interpreter::lrrd(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;

u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
dsp_op_write_reg(dreg, val);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[sreg] = dsp_decrement_addr_reg(sreg);
const u8 sreg = (opc >> 5) & 0x3;
const u8 dreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();

const u16 val = state.ReadDMEM(OpReadRegister(sreg));
OpWriteRegister(dreg, val);
ConditionalExtendAccum(dreg);
state.r.ar[sreg] = DecrementAddressRegister(sreg);
}

// LRRI $D, @$S
// 0001 1001 0ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Increment register $S.
void lrri(const UDSPInstruction opc)
void Interpreter::lrri(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;

u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
dsp_op_write_reg(dreg, val);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[sreg] = dsp_increment_addr_reg(sreg);
const u8 sreg = (opc >> 5) & 0x3;
const u8 dreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();

const u16 val = state.ReadDMEM(OpReadRegister(sreg));
OpWriteRegister(dreg, val);
ConditionalExtendAccum(dreg);
state.r.ar[sreg] = IncrementAddressRegister(sreg);
}

// LRRN $D, @$S
// 0001 1001 1ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Add indexing register $(0x4+S) to register $S.
void lrrn(const UDSPInstruction opc)
void Interpreter::lrrn(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;

u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
dsp_op_write_reg(dreg, val);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[sreg] = dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]);
const u8 sreg = (opc >> 5) & 0x3;
const u8 dreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();

const u16 val = state.ReadDMEM(OpReadRegister(sreg));
OpWriteRegister(dreg, val);
ConditionalExtendAccum(dreg);
state.r.ar[sreg] = IncreaseAddressRegister(sreg, static_cast<s16>(state.r.ix[sreg]));
}

// SRR @$D, $S
// 0001 1010 0dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D.
void srr(const UDSPInstruction opc)
void Interpreter::srr(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
const u8 dreg = (opc >> 5) & 0x3;
const u8 sreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();

if (sreg >= DSP_REG_ACM0)
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
state.WriteDMEM(state.r.ar[dreg], OpReadRegisterAndSaturate(sreg - DSP_REG_ACM0));
else
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg(sreg));
state.WriteDMEM(state.r.ar[dreg], OpReadRegister(sreg));
}

// SRRD @$D, $S
// 0001 1010 1dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Decrement register $D.
void srrd(const UDSPInstruction opc)
void Interpreter::srrd(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
const u8 dreg = (opc >> 5) & 0x3;
const u8 sreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();

if (sreg >= DSP_REG_ACM0)
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
state.WriteDMEM(state.r.ar[dreg], OpReadRegisterAndSaturate(sreg - DSP_REG_ACM0));
else
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg(sreg));
state.WriteDMEM(state.r.ar[dreg], OpReadRegister(sreg));

g_dsp.r.ar[dreg] = dsp_decrement_addr_reg(dreg);
state.r.ar[dreg] = DecrementAddressRegister(dreg);
}

// SRRI @$D, $S
// 0001 1011 0dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Increment register $D.
void srri(const UDSPInstruction opc)
void Interpreter::srri(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
const u8 dreg = (opc >> 5) & 0x3;
const u8 sreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();

if (sreg >= DSP_REG_ACM0)
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
state.WriteDMEM(state.r.ar[dreg], OpReadRegisterAndSaturate(sreg - DSP_REG_ACM0));
else
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg(sreg));
state.WriteDMEM(state.r.ar[dreg], OpReadRegister(sreg));

g_dsp.r.ar[dreg] = dsp_increment_addr_reg(dreg);
state.r.ar[dreg] = IncrementAddressRegister(dreg);
}

// SRRN @$D, $S
// 0001 1011 1dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Add DSP_REG_IX0 register to register $D.
void srrn(const UDSPInstruction opc)
void Interpreter::srrn(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
const u8 dreg = (opc >> 5) & 0x3;
const u8 sreg = opc & 0x1f;
auto& state = m_dsp_core.DSPState();

if (sreg >= DSP_REG_ACM0)
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
state.WriteDMEM(state.r.ar[dreg], OpReadRegisterAndSaturate(sreg - DSP_REG_ACM0));
else
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg(sreg));
state.WriteDMEM(state.r.ar[dreg], OpReadRegister(sreg));

g_dsp.r.ar[dreg] = dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]);
state.r.ar[dreg] = IncreaseAddressRegister(dreg, static_cast<s16>(state.r.ix[dreg]));
}

// ILRR $acD.m, @$arS
// 0000 001d 0001 00ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m.
void ilrr(const UDSPInstruction opc)
void Interpreter::ilrr(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
const u16 reg = opc & 0x3;
const u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
auto& state = m_dsp_core.DSPState();

g_dsp.r.ac[dreg - DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
dsp_conditional_extend_accum(dreg);
state.r.ac[dreg - DSP_REG_ACM0].m = state.ReadIMEM(state.r.ar[reg]);
ConditionalExtendAccum(dreg);
}

// ILRRD $acD.m, @$arS
// 0000 001d 0001 01ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m. Decrement addressing register $arS.
void ilrrd(const UDSPInstruction opc)
void Interpreter::ilrrd(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
const u16 reg = opc & 0x3;
const u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
auto& state = m_dsp_core.DSPState();

g_dsp.r.ac[dreg - DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[reg] = dsp_decrement_addr_reg(reg);
state.r.ac[dreg - DSP_REG_ACM0].m = state.ReadIMEM(state.r.ar[reg]);
ConditionalExtendAccum(dreg);
state.r.ar[reg] = DecrementAddressRegister(reg);
}

// ILRRI $acD.m, @$S
// 0000 001d 0001 10ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m. Increment addressing register $arS.
void ilrri(const UDSPInstruction opc)
void Interpreter::ilrri(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
const u16 reg = opc & 0x3;
const u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
auto& state = m_dsp_core.DSPState();

g_dsp.r.ac[dreg - DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[reg] = dsp_increment_addr_reg(reg);
state.r.ac[dreg - DSP_REG_ACM0].m = state.ReadIMEM(state.r.ar[reg]);
ConditionalExtendAccum(dreg);
state.r.ar[reg] = IncrementAddressRegister(reg);
}

// ILRRN $acD.m, @$arS
// 0000 001d 0001 11ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m. Add corresponding indexing
// register $ixS to addressing register $arS.
void ilrrn(const UDSPInstruction opc)
void Interpreter::ilrrn(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
const u16 reg = opc & 0x3;
const u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
auto& state = m_dsp_core.DSPState();

g_dsp.r.ac[dreg - DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[reg] = dsp_increase_addr_reg(reg, (s16)g_dsp.r.ix[reg]);
state.r.ac[dreg - DSP_REG_ACM0].m = state.ReadIMEM(state.r.ar[reg]);
ConditionalExtendAccum(dreg);
state.r.ar[reg] = IncreaseAddressRegister(reg, static_cast<s16>(state.r.ix[reg]));
}
} // namespace DSP::Interpreter
@@ -5,7 +5,6 @@
// Additional copyrights go to Duddie and Tratax (c) 2004

#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/DSPTables.h"
#include "Core/DSP/Interpreter/DSPIntUtil.h"
#include "Core/DSP/Interpreter/DSPInterpreter.h"
@@ -15,17 +14,17 @@ namespace DSP::Interpreter
// MRR $D, $S
// 0001 11dd ddds ssss
// Move value from register $S to register $D.
void mrr(const UDSPInstruction opc)
void Interpreter::mrr(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1f;
u8 dreg = (opc >> 5) & 0x1f;
const u8 sreg = opc & 0x1f;
const u8 dreg = (opc >> 5) & 0x1f;

if (sreg >= DSP_REG_ACM0)
dsp_op_write_reg(dreg, dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
OpWriteRegister(dreg, OpReadRegisterAndSaturate(sreg - DSP_REG_ACM0));
else
dsp_op_write_reg(dreg, dsp_op_read_reg(sreg));
OpWriteRegister(dreg, OpReadRegister(sreg));

dsp_conditional_extend_accum(dreg);
ConditionalExtendAccum(dreg);
}

// LRI $D, #I
@@ -37,23 +36,26 @@ void mrr(const UDSPInstruction opc)
// register, has a different behaviour in S40 mode if loaded to AC0.M: The
// value gets sign extended to the whole accumulator! This does not happen in
// S16 mode.
void lri(const UDSPInstruction opc)
void Interpreter::lri(const UDSPInstruction opc)
{
u8 reg = opc & 0x1F;
u16 imm = dsp_fetch_code();
dsp_op_write_reg(reg, imm);
dsp_conditional_extend_accum(reg);
auto& state = m_dsp_core.DSPState();
const u8 reg = opc & 0x1F;
const u16 imm = state.FetchInstruction();

OpWriteRegister(reg, imm);
ConditionalExtendAccum(reg);
}

// LRIS $(0x18+D), #I
// 0000 1ddd iiii iiii
// Load immediate value I (8-bit sign extended) to accumulator register.
void lris(const UDSPInstruction opc)
void Interpreter::lris(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x7) + DSP_REG_AXL0;
u16 imm = (s8)opc;
dsp_op_write_reg(reg, imm);
dsp_conditional_extend_accum(reg);
const u8 reg = ((opc >> 8) & 0x7) + DSP_REG_AXL0;
const u16 imm = static_cast<u16>(static_cast<s8>(opc));

OpWriteRegister(reg, imm);
ConditionalExtendAccum(reg);
}

//----
@@ -63,7 +65,7 @@ void lris(const UDSPInstruction opc)
// No operation, but can be extended with extended opcode.
// This opcode is supposed to do nothing - it's used if you want to use
// an opcode extension but not do anything. At least according to duddie.
void nx(const UDSPInstruction opc)
void Interpreter::nx(const UDSPInstruction)
{
ZeroWriteBackLog();
}
@@ -73,38 +75,48 @@ void nx(const UDSPInstruction opc)
// DAR $arD
// 0000 0000 0000 01dd
// Decrement address register $arD.
void dar(const UDSPInstruction opc)
void Interpreter::dar(const UDSPInstruction opc)
{
g_dsp.r.ar[opc & 0x3] = dsp_decrement_addr_reg(opc & 0x3);
auto& state = m_dsp_core.DSPState();
const u16 index = opc & 3;

state.r.ar[index] = DecrementAddressRegister(index);
}

// IAR $arD
// 0000 0000 0000 10dd
// Increment address register $arD.
void iar(const UDSPInstruction opc)
void Interpreter::iar(const UDSPInstruction opc)
{
g_dsp.r.ar[opc & 0x3] = dsp_increment_addr_reg(opc & 0x3);
auto& state = m_dsp_core.DSPState();
const u16 index = opc & 3;

state.r.ar[index] = IncrementAddressRegister(index);
}

// SUBARN $arD
// 0000 0000 0000 11dd
// Subtract indexing register $ixD from an addressing register $arD.
// used only in IPL-NTSC ucode
void subarn(const UDSPInstruction opc)
void Interpreter::subarn(const UDSPInstruction opc)
{
u8 dreg = opc & 0x3;
g_dsp.r.ar[dreg] = dsp_decrease_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]);
auto& state = m_dsp_core.DSPState();
const u8 dreg = opc & 0x3;

state.r.ar[dreg] = DecreaseAddressRegister(dreg, static_cast<s16>(state.r.ix[dreg]));
}

// ADDARN $arD, $ixS
// 0000 0000 0001 ssdd
// Adds indexing register $ixS to an addressing register $arD.
// It is critical for the Zelda ucode that this one wraps correctly.
void addarn(const UDSPInstruction opc)
void Interpreter::addarn(const UDSPInstruction opc)
{
u8 dreg = opc & 0x3;
u8 sreg = (opc >> 2) & 0x3;
g_dsp.r.ar[dreg] = dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[sreg]);
auto& state = m_dsp_core.DSPState();
const u8 dreg = opc & 0x3;
const u8 sreg = (opc >> 2) & 0x3;

state.r.ar[dreg] = IncreaseAddressRegister(dreg, static_cast<s16>(state.r.ix[sreg]));
}

//----
@@ -113,45 +125,51 @@ void addarn(const UDSPInstruction opc)
// 0001 0010 aaaa aiii
// bit of status register $sr. Bit number is calculated by adding 6 to
// immediate value I.
void sbclr(const UDSPInstruction opc)
void Interpreter::sbclr(const UDSPInstruction opc)
{
u8 bit = (opc & 0x7) + 6;
g_dsp.r.sr &= ~(1 << bit);
auto& state = m_dsp_core.DSPState();
const u8 bit = (opc & 0x7) + 6;

state.r.sr &= ~(1U << bit);
}

// SBSET #I
// 0001 0011 aaaa aiii
// Set bit of status register $sr. Bit number is calculated by adding 6 to
// immediate value I.
void sbset(const UDSPInstruction opc)
void Interpreter::sbset(const UDSPInstruction opc)
{
u8 bit = (opc & 0x7) + 6;
g_dsp.r.sr |= (1 << bit);
auto& state = m_dsp_core.DSPState();
const u8 bit = (opc & 0x7) + 6;

state.r.sr |= (1U << bit);
}

// This is a bunch of flag setters, flipping bits in SR.
void srbith(const UDSPInstruction opc)
void Interpreter::srbith(const UDSPInstruction opc)
{
auto& state = m_dsp_core.DSPState();

ZeroWriteBackLog();
switch ((opc >> 8) & 0x7)
{
case 2: // M2
g_dsp.r.sr &= ~SR_MUL_MODIFY;
state.r.sr &= ~SR_MUL_MODIFY;
break;
case 3: // M0
g_dsp.r.sr |= SR_MUL_MODIFY;
state.r.sr |= SR_MUL_MODIFY;
break;
case 4: // CLR15
g_dsp.r.sr &= ~SR_MUL_UNSIGNED;
state.r.sr &= ~SR_MUL_UNSIGNED;
break;
case 5: // SET15
g_dsp.r.sr |= SR_MUL_UNSIGNED;
state.r.sr |= SR_MUL_UNSIGNED;
break;
case 6: // SET16 (CLR40)
g_dsp.r.sr &= ~SR_40_MODE_BIT;
state.r.sr &= ~SR_40_MODE_BIT;
break;
case 7: // SET40
g_dsp.r.sr |= SR_40_MODE_BIT;
state.r.sr |= SR_40_MODE_BIT;
break;
default:
break;

Large diffs are not rendered by default.

@@ -8,7 +8,6 @@

#include "Common/CommonTypes.h"
#include "Core/DSP/DSPTables.h"
#include "Core/DSP/Interpreter/DSPIntExtOps.h"
#include "Core/DSP/Interpreter/DSPInterpreter.h"

namespace DSP::Interpreter
@@ -23,216 +22,216 @@ struct InterpreterOpInfo
// clang-format off
constexpr std::array<InterpreterOpInfo, 124> s_opcodes
{{
{0x0000, 0xfffc, nop},
{0x0000, 0xfffc, &Interpreter::nop},

{0x0004, 0xfffc, dar},
{0x0008, 0xfffc, iar},
{0x000c, 0xfffc, subarn},
{0x0010, 0xfff0, addarn},
{0x0004, 0xfffc, &Interpreter::dar},
{0x0008, 0xfffc, &Interpreter::iar},
{0x000c, 0xfffc, &Interpreter::subarn},
{0x0010, 0xfff0, &Interpreter::addarn},

{0x0021, 0xffff, halt},
{0x0021, 0xffff, &Interpreter::halt},

{0x02d0, 0xfff0, ret},
{0x02d0, 0xfff0, &Interpreter::ret},

{0x02ff, 0xffff, rti},
{0x02ff, 0xffff, &Interpreter::rti},

{0x02b0, 0xfff0, call},
{0x02b0, 0xfff0, &Interpreter::call},

{0x0270, 0xfff0, ifcc},
{0x0270, 0xfff0, &Interpreter::ifcc},

{0x0290, 0xfff0, jcc},
{0x0290, 0xfff0, &Interpreter::jcc},

{0x1700, 0xff10, jmprcc},
{0x1700, 0xff10, &Interpreter::jmprcc},

{0x1710, 0xff10, callr},
{0x1710, 0xff10, &Interpreter::callr},

{0x1200, 0xff00, sbclr},
{0x1300, 0xff00, sbset},
{0x1200, 0xff00, &Interpreter::sbclr},
{0x1300, 0xff00, &Interpreter::sbset},

{0x1400, 0xfec0, lsl},
{0x1440, 0xfec0, lsr},
{0x1480, 0xfec0, asl},
{0x14c0, 0xfec0, asr},
{0x1400, 0xfec0, &Interpreter::lsl},
{0x1440, 0xfec0, &Interpreter::lsr},
{0x1480, 0xfec0, &Interpreter::asl},
{0x14c0, 0xfec0, &Interpreter::asr},

// these two were discovered by ector
{0x02ca, 0xffff, lsrn},
{0x02cb, 0xffff, asrn},
{0x02ca, 0xffff, &Interpreter::lsrn},
{0x02cb, 0xffff, &Interpreter::asrn},

{0x0080, 0xffe0, lri},
{0x00c0, 0xffe0, lr},
{0x00e0, 0xffe0, sr},
{0x0080, 0xffe0, &Interpreter::lri},
{0x00c0, 0xffe0, &Interpreter::lr},
{0x00e0, 0xffe0, &Interpreter::sr},

{0x1c00, 0xfc00, mrr},
{0x1c00, 0xfc00, &Interpreter::mrr},

{0x1600, 0xff00, si},
{0x1600, 0xff00, &Interpreter::si},

{0x0400, 0xfe00, addis},
{0x0600, 0xfe00, cmpis},
{0x0800, 0xf800, lris},
{0x0400, 0xfe00, &Interpreter::addis},
{0x0600, 0xfe00, &Interpreter::cmpis},
{0x0800, 0xf800, &Interpreter::lris},

{0x0200, 0xfeff, addi},
{0x0220, 0xfeff, xori},
{0x0240, 0xfeff, andi},
{0x0260, 0xfeff, ori},
{0x0280, 0xfeff, cmpi},
{0x0200, 0xfeff, &Interpreter::addi},
{0x0220, 0xfeff, &Interpreter::xori},
{0x0240, 0xfeff, &Interpreter::andi},
{0x0260, 0xfeff, &Interpreter::ori},
{0x0280, 0xfeff, &Interpreter::cmpi},

{0x02a0, 0xfeff, andf},
{0x02c0, 0xfeff, andcf},
{0x02a0, 0xfeff, &Interpreter::andf},
{0x02c0, 0xfeff, &Interpreter::andcf},

{0x0210, 0xfefc, ilrr},
{0x0214, 0xfefc, ilrrd},
{0x0218, 0xfefc, ilrri},
{0x021c, 0xfefc, ilrrn},
{0x0210, 0xfefc, &Interpreter::ilrr},
{0x0214, 0xfefc, &Interpreter::ilrrd},
{0x0218, 0xfefc, &Interpreter::ilrri},
{0x021c, 0xfefc, &Interpreter::ilrrn},

// LOOPS
{0x0040, 0xffe0, loop},
{0x0060, 0xffe0, bloop},
{0x1000, 0xff00, loopi},
{0x1100, 0xff00, bloopi},
{0x0040, 0xffe0, &Interpreter::loop},
{0x0060, 0xffe0, &Interpreter::bloop},
{0x1000, 0xff00, &Interpreter::loopi},
{0x1100, 0xff00, &Interpreter::bloopi},

// load and store value pointed by indexing reg and increment; LRR/SRR variants
{0x1800, 0xff80, lrr},
{0x1880, 0xff80, lrrd},
{0x1900, 0xff80, lrri},
{0x1980, 0xff80, lrrn},
{0x1800, 0xff80, &Interpreter::lrr},
{0x1880, 0xff80, &Interpreter::lrrd},
{0x1900, 0xff80, &Interpreter::lrri},
{0x1980, 0xff80, &Interpreter::lrrn},

{0x1a00, 0xff80, srr},
{0x1a80, 0xff80, srrd},
{0x1b00, 0xff80, srri},
{0x1b80, 0xff80, srrn},
{0x1a00, 0xff80, &Interpreter::srr},
{0x1a80, 0xff80, &Interpreter::srrd},
{0x1b00, 0xff80, &Interpreter::srri},
{0x1b80, 0xff80, &Interpreter::srrn},

// 2
{0x2000, 0xf800, lrs},
{0x2800, 0xf800, srs},
{0x2000, 0xf800, &Interpreter::lrs},
{0x2800, 0xf800, &Interpreter::srs},

// opcodes that can be extended

// 3 - main opcode defined by 9 bits, extension defined by last 7 bits!!
{0x3000, 0xfc80, xorr},
{0x3400, 0xfc80, andr},
{0x3800, 0xfc80, orr},
{0x3c00, 0xfe80, andc},
{0x3e00, 0xfe80, orc},
{0x3080, 0xfe80, xorc},
{0x3280, 0xfe80, notc},
{0x3480, 0xfc80, lsrnrx},
{0x3880, 0xfc80, asrnrx},
{0x3c80, 0xfe80, lsrnr},
{0x3e80, 0xfe80, asrnr},
{0x3000, 0xfc80, &Interpreter::xorr},
{0x3400, 0xfc80, &Interpreter::andr},
{0x3800, 0xfc80, &Interpreter::orr},
{0x3c00, 0xfe80, &Interpreter::andc},
{0x3e00, 0xfe80, &Interpreter::orc},
{0x3080, 0xfe80, &Interpreter::xorc},
{0x3280, 0xfe80, &Interpreter::notc},
{0x3480, 0xfc80, &Interpreter::lsrnrx},
{0x3880, 0xfc80, &Interpreter::asrnrx},
{0x3c80, 0xfe80, &Interpreter::lsrnr},
{0x3e80, 0xfe80, &Interpreter::asrnr},

// 4
{0x4000, 0xf800, addr},
{0x4800, 0xfc00, addax},
{0x4c00, 0xfe00, add},
{0x4e00, 0xfe00, addp},
{0x4000, 0xf800, &Interpreter::addr},
{0x4800, 0xfc00, &Interpreter::addax},
{0x4c00, 0xfe00, &Interpreter::add},
{0x4e00, 0xfe00, &Interpreter::addp},

// 5
{0x5000, 0xf800, subr},
{0x5800, 0xfc00, subax},
{0x5c00, 0xfe00, sub},
{0x5e00, 0xfe00, subp},
{0x5000, 0xf800, &Interpreter::subr},
{0x5800, 0xfc00, &Interpreter::subax},
{0x5c00, 0xfe00, &Interpreter::sub},
{0x5e00, 0xfe00, &Interpreter::subp},

// 6
{0x6000, 0xf800, movr},
{0x6800, 0xfc00, movax},
{0x6c00, 0xfe00, mov},
{0x6e00, 0xfe00, movp},
{0x6000, 0xf800, &Interpreter::movr},
{0x6800, 0xfc00, &Interpreter::movax},
{0x6c00, 0xfe00, &Interpreter::mov},
{0x6e00, 0xfe00, &Interpreter::movp},

// 7
{0x7000, 0xfc00, addaxl},
{0x7400, 0xfe00, incm},
{0x7600, 0xfe00, inc},
{0x7800, 0xfe00, decm},
{0x7a00, 0xfe00, dec},
{0x7c00, 0xfe00, neg},
{0x7e00, 0xfe00, movnp},
{0x7000, 0xfc00, &Interpreter::addaxl},
{0x7400, 0xfe00, &Interpreter::incm},
{0x7600, 0xfe00, &Interpreter::inc},
{0x7800, 0xfe00, &Interpreter::decm},
{0x7a00, 0xfe00, &Interpreter::dec},
{0x7c00, 0xfe00, &Interpreter::neg},
{0x7e00, 0xfe00, &Interpreter::movnp},

// 8
{0x8000, 0xf700, nx},
{0x8100, 0xf700, clr},
{0x8200, 0xff00, cmp},
{0x8300, 0xff00, mulaxh},
{0x8400, 0xff00, clrp},
{0x8500, 0xff00, tstprod},
{0x8600, 0xfe00, tstaxh},
{0x8a00, 0xff00, srbith},
{0x8b00, 0xff00, srbith},
{0x8c00, 0xff00, srbith},
{0x8d00, 0xff00, srbith},
{0x8e00, 0xff00, srbith},
{0x8f00, 0xff00, srbith},
{0x8000, 0xf700, &Interpreter::nx},
{0x8100, 0xf700, &Interpreter::clr},
{0x8200, 0xff00, &Interpreter::cmp},
{0x8300, 0xff00, &Interpreter::mulaxh},
{0x8400, 0xff00, &Interpreter::clrp},
{0x8500, 0xff00, &Interpreter::tstprod},
{0x8600, 0xfe00, &Interpreter::tstaxh},
{0x8a00, 0xff00, &Interpreter::srbith},
{0x8b00, 0xff00, &Interpreter::srbith},
{0x8c00, 0xff00, &Interpreter::srbith},
{0x8d00, 0xff00, &Interpreter::srbith},
{0x8e00, 0xff00, &Interpreter::srbith},
{0x8f00, 0xff00, &Interpreter::srbith},

// 9
{0x9000, 0xf700, mul},
{0x9100, 0xf700, asr16},
{0x9200, 0xf600, mulmvz},
{0x9400, 0xf600, mulac},
{0x9600, 0xf600, mulmv},
{0x9000, 0xf700, &Interpreter::mul},
{0x9100, 0xf700, &Interpreter::asr16},
{0x9200, 0xf600, &Interpreter::mulmvz},
{0x9400, 0xf600, &Interpreter::mulac},
{0x9600, 0xf600, &Interpreter::mulmv},

// A-B
{0xa000, 0xe700, mulx},
{0xa100, 0xf700, abs},
{0xa200, 0xe600, mulxmvz},
{0xa400, 0xe600, mulxac},
{0xa600, 0xe600, mulxmv},
{0xb100, 0xf700, tst},
{0xa000, 0xe700, &Interpreter::mulx},
{0xa100, 0xf700, &Interpreter::abs},
{0xa200, 0xe600, &Interpreter::mulxmvz},
{0xa400, 0xe600, &Interpreter::mulxac},
{0xa600, 0xe600, &Interpreter::mulxmv},
{0xb100, 0xf700, &Interpreter::tst},

// C-D
{0xc000, 0xe700, mulc},
{0xc100, 0xe700, cmpar},
{0xc200, 0xe600, mulcmvz},
{0xc400, 0xe600, mulcac},
{0xc600, 0xe600, mulcmv},
{0xc000, 0xe700, &Interpreter::mulc},
{0xc100, 0xe700, &Interpreter::cmpar},
{0xc200, 0xe600, &Interpreter::mulcmvz},
{0xc400, 0xe600, &Interpreter::mulcac},
{0xc600, 0xe600, &Interpreter::mulcmv},

// E
{0xe000, 0xfc00, maddx},
{0xe400, 0xfc00, msubx},
{0xe800, 0xfc00, maddc},
{0xec00, 0xfc00, msubc},
{0xe000, 0xfc00, &Interpreter::maddx},
{0xe400, 0xfc00, &Interpreter::msubx},
{0xe800, 0xfc00, &Interpreter::maddc},
{0xec00, 0xfc00, &Interpreter::msubc},

// F
{0xf000, 0xfe00, lsl16},
{0xf200, 0xfe00, madd},
{0xf400, 0xfe00, lsr16},
{0xf600, 0xfe00, msub},
{0xf800, 0xfc00, addpaxz},
{0xfc00, 0xfe00, clrl},
{0xfe00, 0xfe00, movpz},
{0xf000, 0xfe00, &Interpreter::lsl16},
{0xf200, 0xfe00, &Interpreter::madd},
{0xf400, 0xfe00, &Interpreter::lsr16},
{0xf600, 0xfe00, &Interpreter::msub},
{0xf800, 0xfc00, &Interpreter::addpaxz},
{0xfc00, 0xfe00, &Interpreter::clrl},
{0xfe00, 0xfe00, &Interpreter::movpz},
}};

constexpr std::array<InterpreterOpInfo, 25> s_opcodes_ext
{{
{0x0000, 0x00fc, Ext::nop},

{0x0004, 0x00fc, Ext::dr},
{0x0008, 0x00fc, Ext::ir},
{0x000c, 0x00fc, Ext::nr},
{0x0010, 0x00f0, Ext::mv},

{0x0020, 0x00e4, Ext::s},
{0x0024, 0x00e4, Ext::sn},

{0x0040, 0x00c4, Ext::l},
{0x0044, 0x00c4, Ext::ln},

{0x0080, 0x00ce, Ext::ls},
{0x0082, 0x00ce, Ext::sl},
{0x0084, 0x00ce, Ext::lsn},
{0x0086, 0x00ce, Ext::sln},
{0x0088, 0x00ce, Ext::lsm},
{0x008a, 0x00ce, Ext::slm},
{0x008c, 0x00ce, Ext::lsnm},
{0x008e, 0x00ce, Ext::slnm},

{0x00c3, 0x00cf, Ext::ldax},
{0x00c7, 0x00cf, Ext::ldaxn},
{0x00cb, 0x00cf, Ext::ldaxm},
{0x00cf, 0x00cf, Ext::ldaxnm},

{0x00c0, 0x00cc, Ext::ld},
{0x00c4, 0x00cc, Ext::ldn},
{0x00c8, 0x00cc, Ext::ldm},
{0x00cc, 0x00cc, Ext::ldnm},
{0x0000, 0x00fc, &Interpreter::nop_ext},

{0x0004, 0x00fc, &Interpreter::dr},
{0x0008, 0x00fc, &Interpreter::ir},
{0x000c, 0x00fc, &Interpreter::nr},
{0x0010, 0x00f0, &Interpreter::mv},

{0x0020, 0x00e4, &Interpreter::s},
{0x0024, 0x00e4, &Interpreter::sn},

{0x0040, 0x00c4, &Interpreter::l},
{0x0044, 0x00c4, &Interpreter::ln},

{0x0080, 0x00ce, &Interpreter::ls},
{0x0082, 0x00ce, &Interpreter::sl},
{0x0084, 0x00ce, &Interpreter::lsn},
{0x0086, 0x00ce, &Interpreter::sln},
{0x0088, 0x00ce, &Interpreter::lsm},
{0x008a, 0x00ce, &Interpreter::slm},
{0x008c, 0x00ce, &Interpreter::lsnm},
{0x008e, 0x00ce, &Interpreter::slnm},

{0x00c3, 0x00cf, &Interpreter::ldax},
{0x00c7, 0x00cf, &Interpreter::ldaxn},
{0x00cb, 0x00cf, &Interpreter::ldaxm},
{0x00cf, 0x00cf, &Interpreter::ldaxnm},

{0x00c0, 0x00cc, &Interpreter::ld},
{0x00c4, 0x00cc, &Interpreter::ldn},
{0x00c8, 0x00cc, &Interpreter::ldm},
{0x00cc, 0x00cc, &Interpreter::ldnm},
}};
// clang-format on

@@ -266,7 +265,7 @@ void InitInstructionTables()
// ext op table
for (size_t i = 0; i < s_ext_op_table.size(); i++)
{
s_ext_op_table[i] = nop;
s_ext_op_table[i] = &Interpreter::nop;

const auto iter = FindByOpcode(static_cast<UDSPInstruction>(i), s_opcodes_ext);
if (iter == s_opcodes_ext.cend())
@@ -278,7 +277,7 @@ void InitInstructionTables()
// op table
for (size_t i = 0; i < s_op_table.size(); i++)
{
s_op_table[i] = nop;
s_op_table[i] = &Interpreter::nop;

const auto iter = FindByOpcode(static_cast<UDSPInstruction>(i), s_opcodes);
if (iter == s_opcodes.cend())
@@ -8,7 +8,9 @@

namespace DSP::Interpreter
{
using InterpreterFunction = void (*)(UDSPInstruction);
class Interpreter;

using InterpreterFunction = void (Interpreter::*)(UDSPInstruction);

InterpreterFunction GetOp(UDSPInstruction inst);
InterpreterFunction GetExtOp(UDSPInstruction inst);
@@ -5,376 +5,27 @@

#pragma once

#include "Common/Assert.h"
#include "Common/CommonTypes.h"

#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPStacks.h"

namespace DSP::Interpreter
{
// ---------------------------------------------------------------------------------------
// --- SR
// ---------------------------------------------------------------------------------------

static inline void dsp_SR_set_flag(int flag)
{
g_dsp.r.sr |= flag;
}

static inline bool dsp_SR_is_flag_set(int flag)
{
return (g_dsp.r.sr & flag) != 0;
}

// ---------------------------------------------------------------------------------------
// --- AR increments, decrements
// ---------------------------------------------------------------------------------------

static inline u16 dsp_increase_addr_reg(u16 reg, s16 _ix)
{
u32 ar = g_dsp.r.ar[reg];
u32 wr = g_dsp.r.wr[reg];
s32 ix = _ix;

u32 mx = (wr | 1) << 1;
u32 nar = ar + ix;
u32 dar = (nar ^ ar ^ ix) & mx;

if (ix >= 0)
{
if (dar > wr) // overflow
nar -= wr + 1;
}
else
{
if ((((nar + wr + 1) ^ nar) & dar) <= wr) // underflow or below min for mask
nar += wr + 1;
}
return nar;
}

static inline u16 dsp_decrease_addr_reg(u16 reg, s16 _ix)
{
u32 ar = g_dsp.r.ar[reg];
u32 wr = g_dsp.r.wr[reg];
s32 ix = _ix;

u32 mx = (wr | 1) << 1;
u32 nar = ar - ix;
u32 dar = (nar ^ ar ^ ~ix) & mx;

if ((u32)ix > 0xFFFF8000) //(ix < 0 && ix != -0x8000)
{
if (dar > wr) // overflow
nar -= wr + 1;
}
else
{
if ((((nar + wr + 1) ^ nar) & dar) <= wr) // underflow or below min for mask
nar += wr + 1;
}
return nar;
}

static inline u16 dsp_increment_addr_reg(u16 reg)
{
u32 ar = g_dsp.r.ar[reg];
u32 wr = g_dsp.r.wr[reg];

u32 nar = ar + 1;

if ((nar ^ ar) > ((wr | 1) << 1))
nar -= wr + 1;
return nar;
}

static inline u16 dsp_decrement_addr_reg(u16 reg)
{
u32 ar = g_dsp.r.ar[reg];
u32 wr = g_dsp.r.wr[reg];

u32 nar = ar + wr;

if (((nar ^ ar) & ((wr | 1) << 1)) > wr)
nar -= wr + 1;
return nar;
}

// ---------------------------------------------------------------------------------------
// --- reg
// ---------------------------------------------------------------------------------------

static inline u16 dsp_op_read_reg(int _reg)
{
int reg = _reg & 0x1f;

switch (reg)
{
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
return dsp_reg_load_stack(static_cast<StackRegister>(reg - DSP_REG_ST0));
case DSP_REG_AR0:
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
return g_dsp.r.ar[reg - DSP_REG_AR0];
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
return g_dsp.r.ix[reg - DSP_REG_IX0];
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
return g_dsp.r.wr[reg - DSP_REG_WR0];
case DSP_REG_ACH0:
case DSP_REG_ACH1:
return g_dsp.r.ac[reg - DSP_REG_ACH0].h;
case DSP_REG_CR:
return g_dsp.r.cr;
case DSP_REG_SR:
return g_dsp.r.sr;
case DSP_REG_PRODL:
return g_dsp.r.prod.l;
case DSP_REG_PRODM:
return g_dsp.r.prod.m;
case DSP_REG_PRODH:
return g_dsp.r.prod.h;
case DSP_REG_PRODM2:
return g_dsp.r.prod.m2;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
return g_dsp.r.ax[reg - DSP_REG_AXL0].l;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
return g_dsp.r.ax[reg - DSP_REG_AXH0].h;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
return g_dsp.r.ac[reg - DSP_REG_ACL0].l;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
return g_dsp.r.ac[reg - DSP_REG_ACM0].m;
default:
ASSERT_MSG(DSP_INT, 0, "cannot happen");
return 0;
}
}

static inline void dsp_op_write_reg(int _reg, u16 val)
{
int reg = _reg & 0x1f;

switch (reg)
{
// 8-bit sign extended registers. Should look at prod.h too...
case DSP_REG_ACH0:
case DSP_REG_ACH1:
// sign extend from the bottom 8 bits.
g_dsp.r.ac[reg - DSP_REG_ACH0].h = (u16)(s16)(s8)(u8)val;
break;

// Stack registers.
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
dsp_reg_store_stack(static_cast<StackRegister>(reg - DSP_REG_ST0), val);
break;
case DSP_REG_AR0:
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
g_dsp.r.ar[reg - DSP_REG_AR0] = val;
break;
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
g_dsp.r.ix[reg - DSP_REG_IX0] = val;
break;
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
g_dsp.r.wr[reg - DSP_REG_WR0] = val;
break;
case DSP_REG_CR:
g_dsp.r.cr = val;
break;
case DSP_REG_SR:
g_dsp.r.sr = val;
break;
case DSP_REG_PRODL:
g_dsp.r.prod.l = val;
break;
case DSP_REG_PRODM:
g_dsp.r.prod.m = val;
break;
case DSP_REG_PRODH:
g_dsp.r.prod.h = val;
break;
case DSP_REG_PRODM2:
g_dsp.r.prod.m2 = val;
break;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
g_dsp.r.ax[reg - DSP_REG_AXL0].l = val;
break;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
g_dsp.r.ax[reg - DSP_REG_AXH0].h = val;
break;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
g_dsp.r.ac[reg - DSP_REG_ACL0].l = val;
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
g_dsp.r.ac[reg - DSP_REG_ACM0].m = val;
break;
}
}

static inline void dsp_conditional_extend_accum(int reg)
{
switch (reg)
{
case DSP_REG_ACM0:
case DSP_REG_ACM1:
if (g_dsp.r.sr & SR_40_MODE_BIT)
{
// Sign extend into whole accum.
u16 val = g_dsp.r.ac[reg - DSP_REG_ACM0].m;
g_dsp.r.ac[reg - DSP_REG_ACM0].h = (val & 0x8000) ? 0xFFFF : 0x0000;
g_dsp.r.ac[reg - DSP_REG_ACM0].l = 0;
}
}
}

// ---------------------------------------------------------------------------------------
// --- prod (40-bit)
// ---------------------------------------------------------------------------------------

static inline s64 dsp_get_long_prod()
{
s64 val = (s8)(u8)g_dsp.r.prod.h;
val <<= 32;
s64 low_prod = g_dsp.r.prod.m;
low_prod += g_dsp.r.prod.m2;
low_prod <<= 16;
low_prod |= g_dsp.r.prod.l;
val += low_prod;
return val;
}

static inline s64 dsp_get_long_prod_round_prodl()
{
s64 prod = dsp_get_long_prod();

if (prod & 0x10000)
prod = (prod + 0x8000) & ~0xffff;
else
prod = (prod + 0x7fff) & ~0xffff;

return prod;
}

// For accurate emulation, this is wrong - but the real prod registers behave
// in completely bizarre ways. Not needed to emulate them correctly for game ucodes.
inline void dsp_set_long_prod(s64 val)
{
g_dsp.r.prod.val = val & 0x000000FFFFFFFFFFULL;
}

// ---------------------------------------------------------------------------------------
// --- ACC - main accumulators (40-bit)
// ---------------------------------------------------------------------------------------

inline s64 dsp_get_long_acc(int reg)
{
return ((s64)(g_dsp.r.ac[reg].val << 24) >> 24);
}

inline void dsp_set_long_acc(int _reg, s64 val)
{
g_dsp.r.ac[_reg].val = (u64)val;
}

inline s64 dsp_convert_long_acc(s64 val) // s64 -> s40
// s64 -> s40
inline s64 dsp_convert_long_acc(s64 val)
{
return ((val << 24) >> 24);
}

inline s64 dsp_round_long_acc(s64 val)
{
if (val & 0x10000)
if ((val & 0x10000) != 0)
val = (val + 0x8000) & ~0xffff;
else
val = (val + 0x7fff) & ~0xffff;

return val;
}

inline s16 dsp_get_acc_l(int _reg)
{
return (s16)g_dsp.r.ac[_reg].l;
}

inline s16 dsp_get_acc_m(int _reg)
{
return (s16)g_dsp.r.ac[_reg].m;
}

inline s16 dsp_get_acc_h(int _reg)
{
return (s16)g_dsp.r.ac[_reg].h;
}

inline u16 dsp_op_read_reg_and_saturate(u8 _reg)
{
if (g_dsp.r.sr & SR_40_MODE_BIT)
{
s64 acc = dsp_get_long_acc(_reg);

if (acc != (s32)acc)
{
if (acc > 0)
return 0x7fff;
else
return 0x8000;
}
else
{
return g_dsp.r.ac[_reg].m;
}
}
else
{
return g_dsp.r.ac[_reg].m;
}
}

// ---------------------------------------------------------------------------------------
// --- AX - extra accumulators (32-bit)
// ---------------------------------------------------------------------------------------

inline s32 dsp_get_long_acx(int _reg)
{
return (s32)(((u32)g_dsp.r.ax[_reg].h << 16) | g_dsp.r.ax[_reg].l);
}

inline s16 dsp_get_ax_l(int _reg)
{
return (s16)g_dsp.r.ax[_reg].l;
}

inline s16 dsp_get_ax_h(int _reg)
{
return (s16)g_dsp.r.ax[_reg].h;
}

} // namespace DSP::Interpreter