DSP: Move interpreter source files to their own directory

Keeps code organized similar to the DSP JIT.
This commit is contained in:
Lioncash committed 2016-12-22 18:18:48 -05:00
1 parent 1068d24c09
commit a4cd11ec87
35 files changed
+114 -90

No files matched your search

File diff suppressed because it is too large. Load diff
@@ -0,0 +1,265 @@
// Copyright 2009 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
//
// 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 DSPInterpreter
{
// Generic call implementation
// CALLcc addressA
// 0000 0010 1011 cccc
// aaaa aaaa aaaa aaaa
// 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)
{
// must be outside the if.
u16 dest = dsp_fetch_code();
if (CheckCondition(opc & 0xf))
{
dsp_reg_store_stack(DSP_STACK_C, g_dsp.pc);
g_dsp.pc = dest;
}
}
// Generic callr implementation
// CALLRcc $R
// 0001 0111 rrr1 cccc
// 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)
{
if (CheckCondition(opc & 0xf))
{
u8 reg = (opc >> 5) & 0x7;
u16 addr = dsp_op_read_reg(reg);
dsp_reg_store_stack(DSP_STACK_C, g_dsp.pc);
g_dsp.pc = addr;
}
}
// Generic if implementation
// IFcc
// 0000 0010 0111 cccc
// Execute following opcode if the condition has been met.
void ifcc(const UDSPInstruction opc)
{
if (!CheckCondition(opc & 0xf))
{
// skip the next opcode - we have to lookup its size.
dsp_skip_inst();
}
}
// Generic jmp implementation
// Jcc addressA
// 0000 0010 1001 cccc
// 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)
{
u16 dest = dsp_fetch_code();
if (CheckCondition(opc & 0xf))
{
g_dsp.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)
{
if (CheckCondition(opc & 0xf))
{
u8 reg = (opc >> 5) & 0x7;
g_dsp.pc = dsp_op_read_reg(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)
{
if (CheckCondition(opc & 0xf))
{
g_dsp.pc = dsp_reg_load_stack(DSP_STACK_C);
}
}
// 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)
{
g_dsp.r.sr = dsp_reg_load_stack(DSP_STACK_D);
g_dsp.pc = dsp_reg_load_stack(DSP_STACK_C);
}
// HALT
// 0000 0000 0020 0001
// Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR.
void halt(const UDSPInstruction opc)
{
g_dsp.cr |= 0x4;
g_dsp.pc--;
}
// LOOP handling: Loop stack is used to control execution of repeated blocks of
// instructions. Whenever there is value on stack $st2 and current PC is equal
// value at $st2, then value at stack $st3 is decremented. If value is not zero
// 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()
{
// 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];
if (rLoopAddress > 0 && rLoopCounter > 0)
{
// FIXME: why -1? because we just read past it.
if (g_dsp.pc - 1 == rLoopAddress)
{
rLoopCounter--;
if (rLoopCounter > 0)
{
g_dsp.pc = rCallAddress;
}
else
{
// end of loop
dsp_reg_load_stack(0);
dsp_reg_load_stack(2);
dsp_reg_load_stack(3);
}
}
}
}
// LOOP $R
// 0000 0000 010r rrrr
// Repeatedly execute following opcode until counter specified by value
// from register $R reaches zero. Each execution decrement counter. Register
// $R remains unchanged. If register $R is set to zero at the beginning of loop
// 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)
{
u16 reg = opc & 0x1f;
u16 cnt = dsp_op_read_reg(reg);
u16 loop_pc = g_dsp.pc;
if (cnt)
{
dsp_reg_store_stack(0, g_dsp.pc);
dsp_reg_store_stack(2, loop_pc);
dsp_reg_store_stack(3, cnt);
}
else
{
dsp_skip_inst();
}
}
// LOOPI #I
// 0001 0000 iiii iiii
// Repeatedly execute following opcode until counter specified by
// immediate value I reaches zero. Each execution decrement counter. If
// immediate value I is set to zero at the beginning of loop 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 loopi(const UDSPInstruction opc)
{
u16 cnt = opc & 0xff;
u16 loop_pc = g_dsp.pc;
if (cnt)
{
dsp_reg_store_stack(0, g_dsp.pc);
dsp_reg_store_stack(2, loop_pc);
dsp_reg_store_stack(3, cnt);
}
else
{
dsp_skip_inst();
}
}
// BLOOP $R, addrA
// 0000 0000 011r rrrr
// aaaa aaaa aaaa aaaa
// Repeatedly execute block of code starting at following opcode until
// counter specified by value from register $R reaches zero. Block ends at
// specified address addrA inclusive, ie. opcode at addrA is the last opcode
// 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)
{
u16 reg = opc & 0x1f;
u16 cnt = dsp_op_read_reg(reg);
u16 loop_pc = dsp_fetch_code();
if (cnt)
{
dsp_reg_store_stack(0, g_dsp.pc);
dsp_reg_store_stack(2, loop_pc);
dsp_reg_store_stack(3, cnt);
}
else
{
g_dsp.pc = loop_pc;
dsp_skip_inst();
}
}
// BLOOPI #I, addrA
// 0001 0001 iiii iiii
// aaaa aaaa aaaa aaaa
// Repeatedly execute block of code starting at following opcode until
// counter specified by immediate value I reaches zero. Block ends at specified
// address addrA inclusive, ie. opcode at addrA is the last opcode 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 bloopi(const UDSPInstruction opc)
{
u16 cnt = opc & 0xff;
u16 loop_pc = dsp_fetch_code();
if (cnt)
{
dsp_reg_store_stack(0, g_dsp.pc);
dsp_reg_store_stack(2, loop_pc);
dsp_reg_store_stack(3, cnt);
}
else
{
g_dsp.pc = loop_pc;
dsp_skip_inst();
}
}
} // namespace
@@ -0,0 +1,184 @@
// Copyright 2009 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
//
// Additional copyrights go to Duddie and Tratax (c) 2004
// HELPER FUNCTIONS
#include "Core/DSP/Interpreter/DSPIntCCUtil.h"
#include "Core/DSP/DSPCore.h"
namespace DSPInterpreter
{
void Update_SR_Register64(s64 _Value, bool carry, bool overflow)
{
g_dsp.r.sr &= ~SR_CMP_MASK;
// 0x01
if (carry)
{
g_dsp.r.sr |= SR_CARRY;
}
// 0x02 and 0x80
if (overflow)
{
g_dsp.r.sr |= SR_OVERFLOW;
g_dsp.r.sr |= SR_OVERFLOW_STICKY;
}
// 0x04
if (_Value == 0)
{
g_dsp.r.sr |= SR_ARITH_ZERO;
}
// 0x08
if (_Value < 0)
{
g_dsp.r.sr |= SR_SIGN;
}
// 0x10
if (_Value != (s32)_Value)
{
g_dsp.r.sr |= SR_OVER_S32;
}
// 0x20 - Checks if top bits of m are equal
if (((_Value & 0xc0000000) == 0) || ((_Value & 0xc0000000) == 0xc0000000))
{
g_dsp.r.sr |= SR_TOP2BITS;
}
}
void Update_SR_Register16(s16 _Value, bool carry, bool overflow, bool overS32)
{
g_dsp.r.sr &= ~SR_CMP_MASK;
// 0x01
if (carry)
{
g_dsp.r.sr |= SR_CARRY;
}
// 0x02 and 0x80
if (overflow)
{
g_dsp.r.sr |= SR_OVERFLOW;
g_dsp.r.sr |= SR_OVERFLOW_STICKY;
}
// 0x04
if (_Value == 0)
{
g_dsp.r.sr |= SR_ARITH_ZERO;
}
// 0x08
if (_Value < 0)
{
g_dsp.r.sr |= SR_SIGN;
}
// 0x10
if (overS32)
{
g_dsp.r.sr |= SR_OVER_S32;
}
// 0x20 - Checks if top bits of m are equal
if ((((u16)_Value >> 14) == 0) || (((u16)_Value >> 14) == 3))
{
g_dsp.r.sr |= SR_TOP2BITS;
}
}
void Update_SR_LZ(bool value)
{
if (value == true)
g_dsp.r.sr |= SR_LOGIC_ZERO;
else
g_dsp.r.sr &= ~SR_LOGIC_ZERO;
}
static bool IsCarry()
{
return (g_dsp.r.sr & SR_CARRY) != 0;
}
static bool IsOverflow()
{
return (g_dsp.r.sr & SR_OVERFLOW) != 0;
}
static bool IsOverS32()
{
return (g_dsp.r.sr & SR_OVER_S32) != 0;
}
static bool IsLess()
{
return (!(g_dsp.r.sr & SR_OVERFLOW) != !(g_dsp.r.sr & SR_SIGN));
}
static bool IsZero()
{
return (g_dsp.r.sr & SR_ARITH_ZERO) != 0;
}
static bool IsLogicZero()
{
return (g_dsp.r.sr & SR_LOGIC_ZERO) != 0;
}
static bool IsConditionA()
{
return (((g_dsp.r.sr & SR_OVER_S32) || (g_dsp.r.sr & SR_TOP2BITS)) &&
!(g_dsp.r.sr & SR_ARITH_ZERO)) != 0;
}
// see DSPCore.h for flags
bool CheckCondition(u8 _Condition)
{
switch (_Condition & 0xf)
{
case 0xf: // Always true.
return true;
case 0x0: // GE - Greater Equal
return !IsLess();
case 0x1: // L - Less
return IsLess();
case 0x2: // G - Greater
return !IsLess() && !IsZero();
case 0x3: // LE - Less Equal
return IsLess() || IsZero();
case 0x4: // NZ - Not Zero
return !IsZero();
case 0x5: // Z - Zero
return IsZero();
case 0x6: // NC - Not carry
return !IsCarry();
case 0x7: // C - Carry
return IsCarry();
case 0x8: // ? - Not over s32
return !IsOverS32();
case 0x9: // ? - Over s32
return IsOverS32();
case 0xa: // ?
return IsConditionA();
case 0xb: // ?
return !IsConditionA();
case 0xc: // LNZ - Logic Not Zero
return !IsLogicZero();
case 0xd: // LZ - Logic Zero
return IsLogicZero();
case 0xe: // 0 - Overflow
return IsOverflow();
default:
return true;
}
}
} // namespace
@@ -0,0 +1,42 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
//
// Additional copyrights go to Duddie and Tratax (c) 2004
#pragma once
// Anything to do with SR and conditions goes here.
#include "Common/CommonTypes.h"
namespace DSPInterpreter
{
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)
{
return (val > result);
}
inline bool isCarry2(u64 val, u64 result)
{
return (val >= result);
}
inline bool isOverflow(s64 val1, s64 val2, s64 res)
{
return ((val1 ^ res) & (val2 ^ res)) < 0;
}
inline bool isOverS32(s64 acc)
{
return (acc != (s32)acc) ? true : false;
}
} // namespace
@@ -0,0 +1,564 @@
// Copyright 2009 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/DSP/Interpreter/DSPIntExtOps.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/Interpreter/DSPIntUtil.h"
// not needed for game ucodes (it slows down interpreter/dspjit32 + easier to compare int VS
// dspjit64 without it)
//#define PRECISE_BACKLOG
// Extended opcodes do not exist on their own. These opcodes can only be
// attached to opcodes that allow extending (8 (or 7) lower bits of opcode not used by
// opcode). Extended opcodes do not modify program counter $pc register.
// Most of the suffixes increment or decrement one or more addressing registers
// (the first four, ARx). The increment/decrement is either 1, or the
// corresponding "index" register (the second four, IXx). The addressing
// registers will wrap in odd ways, dictated by the corresponding wrapping
// register, WR0-3.
// Needs comments.
inline static void writeToBackLog(int i, int idx, u16 value)
{
writeBackLog[i] = value;
writeBackLogIdx[i] = idx;
}
namespace DSPInterpreter
{
namespace Ext
{
inline bool IsSameMemArea(u16 a, u16 b)
{
// LM: tested on Wii
if ((a >> 10) == (b >> 10))
return true;
else
return false;
}
// DR $arR
// xxxx xxxx 0000 01rr
// Decrement addressing register $arR.
void dr(const UDSPInstruction opc)
{
writeToBackLog(0, opc & 0x3, dsp_decrement_addr_reg(opc & 0x3));
}
// IR $arR
// xxxx xxxx 0000 10rr
// Increment addressing register $arR.
void ir(const UDSPInstruction opc)
{
writeToBackLog(0, opc & 0x3, dsp_increment_addr_reg(opc & 0x3));
}
// NR $arR
// xxxx xxxx 0000 11rr
// Add corresponding indexing register $ixR to addressing register $arR.
void nr(const UDSPInstruction opc)
{
u8 reg = opc & 0x3;
writeToBackLog(0, reg, dsp_increase_addr_reg(reg, (s16)g_dsp.r.ix[reg]));
}
// MV $axD.D, $acS.S
// xxxx xxxx 0001 ddss
// Move value of $acS.S to the $axD.D.
void mv(const UDSPInstruction opc)
{
u8 sreg = (opc & 0x3) + DSP_REG_ACL0;
u8 dreg = ((opc >> 2) & 0x3);
switch (sreg)
{
case DSP_REG_ACL0:
case DSP_REG_ACL1:
writeToBackLog(0, dreg + DSP_REG_AXL0, g_dsp.r.ac[sreg - DSP_REG_ACL0].l);
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
writeToBackLog(0, dreg + DSP_REG_AXL0, dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
break;
}
}
// S @$arD, $acS.S
// xxxx xxxx 001s s0dd
// Store value of $acS.S in the memory pointed by register $arD.
// Post increment register $arD.
void s(const UDSPInstruction opc)
{
u8 dreg = opc & 0x3;
u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
switch (sreg)
{
case DSP_REG_ACL0:
case DSP_REG_ACL1:
dsp_dmem_write(g_dsp.r.ar[dreg], g_dsp.r.ac[sreg - DSP_REG_ACL0].l);
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
break;
}
writeToBackLog(0, dreg, dsp_increment_addr_reg(dreg));
}
// SN @$arD, $acS.S
// xxxx xxxx 001s s1dd
// Store value of register $acS.S in the memory pointed by register $arD.
// Add indexing register $ixD to register $arD.
void sn(const UDSPInstruction opc)
{
u8 dreg = opc & 0x3;
u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
switch (sreg)
{
case DSP_REG_ACL0:
case DSP_REG_ACL1:
dsp_dmem_write(g_dsp.r.ar[dreg], g_dsp.r.ac[sreg - DSP_REG_ACL0].l);
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
break;
}
writeToBackLog(0, dreg, dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]));
}
// L $axD.D, @$arS
// xxxx xxxx 01dd d0ss
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
// Post increment register $arS.
void l(const UDSPInstruction opc)
{
u8 sreg = opc & 0x3;
u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0;
if ((dreg >= DSP_REG_ACM0) && (g_dsp.r.sr & SR_40_MODE_BIT))
{
u16 val = dsp_dmem_read(g_dsp.r.ar[sreg]);
writeToBackLog(0, dreg - DSP_REG_ACM0 + DSP_REG_ACH0, (val & 0x8000) ? 0xFFFF : 0x0000);
writeToBackLog(1, dreg, val);
writeToBackLog(2, dreg - DSP_REG_ACM0 + DSP_REG_ACL0, 0);
writeToBackLog(3, sreg, dsp_increment_addr_reg(sreg));
}
else
{
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[sreg]));
writeToBackLog(1, sreg, dsp_increment_addr_reg(sreg));
}
}
// LN $axD.D, @$arS
// xxxx xxxx 01dd d0ss
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
// Add indexing register $ixS to register $arS.
void ln(const UDSPInstruction opc)
{
u8 sreg = opc & 0x3;
u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0;
if ((dreg >= DSP_REG_ACM0) && (g_dsp.r.sr & SR_40_MODE_BIT))
{
u16 val = dsp_dmem_read(g_dsp.r.ar[sreg]);
writeToBackLog(0, dreg - DSP_REG_ACM0 + DSP_REG_ACH0, (val & 0x8000) ? 0xFFFF : 0x0000);
writeToBackLog(1, dreg, val);
writeToBackLog(2, dreg - DSP_REG_ACM0 + DSP_REG_ACL0, 0);
writeToBackLog(3, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
}
else
{
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[sreg]));
writeToBackLog(1, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
}
}
// LS $axD.D, $acS.m108
// xxxx xxxx 10dd 000s
// Load register $axD.D with value from memory pointed by register
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Increment both $ar0 and $ar3.
void ls(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[3], dsp_op_read_reg_and_saturate(sreg));
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
writeToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
writeToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
}
// LSN $axD.D, $acS.m
// xxxx xxxx 10dd 010s
// Load register $axD.D with value from memory pointed by register
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Add corresponding indexing register $ix0 to addressing
// register $ar0 and increment $ar3.
void lsn(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[3], dsp_op_read_reg_and_saturate(sreg));
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
writeToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
writeToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
}
// LSM $axD.D, $acS.m
// xxxx xxxx 10dd 100s
// Load register $axD.D with value from memory pointed by register
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Add corresponding indexing register $ix3 to addressing
// register $ar3 and increment $ar0.
void lsm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[3], dsp_op_read_reg_and_saturate(sreg));
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
writeToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
writeToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
}
// LSMN $axD.D, $acS.m
// xxxx xxxx 10dd 110s
// Load register $axD.D with value from memory pointed by register
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Add corresponding indexing register $ix0 to addressing
// register $ar0 and add corresponding indexing register $ix3 to addressing
// register $ar3.
void lsnm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[3], dsp_op_read_reg_and_saturate(sreg));
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
writeToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
writeToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
}
// SL $acS.m, $axD.D
// xxxx xxxx 10dd 001s
// Store value from register $acS.m to memory location pointed by register
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Increment both $ar0 and $ar3.
void sl(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[0], dsp_op_read_reg_and_saturate(sreg));
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
writeToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
}
// SLN $acS.m, $axD.D
// xxxx xxxx 10dd 011s
// Store value from register $acS.m to memory location pointed by register
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Add corresponding indexing register $ix0 to addressing register $ar0
// and increment $ar3.
void sln(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[0], dsp_op_read_reg_and_saturate(sreg));
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
writeToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
}
// SLM $acS.m, $axD.D
// xxxx xxxx 10dd 101s
// Store value from register $acS.m to memory location pointed by register
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Add corresponding indexing register $ix3 to addressing register $ar3
// and increment $ar0.
void slm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[0], dsp_op_read_reg_and_saturate(sreg));
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
writeToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
}
// SLMN $acS.m, $axD.D
// xxxx xxxx 10dd 111s
// Store value from register $acS.m to memory location pointed by register
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Add corresponding indexing register $ix0 to addressing register $ar0
// and add corresponding indexing register $ix3 to addressing register $ar3.
void slnm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[0], dsp_op_read_reg_and_saturate(sreg));
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
writeToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
}
// LD $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 00ss
// example for "nx'ld $AX0.L, $AX1.L, @$AR3"
// Loads the word pointed by AR0 to AX0.H, then loads the word pointed by AR3 to AX0.L.
// Increments AR0 and AR3.
// If AR0 and AR3 point into the same memory page (upper 6 bits of addr are the same -> games are
// not doing that!)
// then the value pointed by AR0 is loaded to BOTH AX0.H and AX0.L.
// If AR0 points into an invalid memory page (ie 0x2000), then AX0.H keeps its old value. (not
// implemented yet)
// If AR3 points into an invalid memory page, then AX0.L gets the same value as AX0.H. (not
// implemented yet)
void ld(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
writeToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
else
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
writeToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
}
// LDAX $axR, @$arS
// xxxx xxxx 11sr 0011
void ldax(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
writeToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
else
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
writeToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
}
// LDN $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 01ss
void ldn(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
writeToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
else
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
writeToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
}
// LDAXN $axR, @$arS
// xxxx xxxx 11sr 0111
void ldaxn(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
writeToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
else
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
writeToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
}
// LDM $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 10ss
void ldm(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
writeToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
else
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
writeToBackLog(3, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
}
// LDAXM $axR, @$arS
// xxxx xxxx 11sr 1011
void ldaxm(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
writeToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
else
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
writeToBackLog(3, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
}
// LDNM $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 11ss
void ldnm(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
writeToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
else
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
writeToBackLog(3, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
}
// LDAXNM $axR, @$arS
// xxxx xxxx 11dr 1111
void ldaxnm(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
writeToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
else
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
writeToBackLog(3, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
}
void nop(const UDSPInstruction opc)
{
}
} // end namespace ext
} // end namespace DSPInterpeter
// The ext ops are calculated in parallel with the actual op. That means that
// both the main op and the ext op see the same register state as input. The
// output is simple as long as the main and ext ops don't change the same
// register. If they do the output is the bitwise or of the result of both the
// main and ext ops.
// The ext op are writing their output into the backlog which is
// being applied to the real registers after the main op was executed
void applyWriteBackLog()
{
// always make sure to have an extra entry at the end w/ -1 to avoid
// infinitive loops
for (int i = 0; writeBackLogIdx[i] != -1; i++)
{
#ifdef PRECISE_BACKLOG
dsp_op_write_reg(writeBackLogIdx[i], dsp_op_read_reg(writeBackLogIdx[i]) | writeBackLog[i]);
#else
dsp_op_write_reg(writeBackLogIdx[i], writeBackLog[i]);
#endif
// Clear back log
writeBackLogIdx[i] = -1;
}
}
// This function is being called in the main op after all input regs were read
// and before it writes into any regs. This way we can always use bitwise or to
// apply the ext command output, because if the main op didn't change the value
// then 0 | ext output = ext output and if it did then bitwise or is still the
// right thing to do
// Only needed for cases when mainop and extended are modifying the same ACC
// Games are not doing that + in motorola (similar DSP) dox this is forbidden to do.
void zeroWriteBackLog()
{
#ifdef PRECISE_BACKLOG
// always make sure to have an extra entry at the end w/ -1 to avoid
// infinitive loops
for (int i = 0; writeBackLogIdx[i] != -1; i++)
{
dsp_op_write_reg(writeBackLogIdx[i], 0);
}
#endif
}
void zeroWriteBackLogPreserveAcc(u8 acc)
{
#ifdef PRECISE_BACKLOG
for (int i = 0; writeBackLogIdx[i] != -1; i++)
{
// acc0
if ((acc == 0) &&
((writeBackLogIdx[i] == DSP_REG_ACL0) || (writeBackLogIdx[i] == DSP_REG_ACM0) ||
(writeBackLogIdx[i] == DSP_REG_ACH0)))
continue;
// acc1
if ((acc == 1) &&
((writeBackLogIdx[i] == DSP_REG_ACL1) || (writeBackLogIdx[i] == DSP_REG_ACM1) ||
(writeBackLogIdx[i] == DSP_REG_ACH1)))
continue;
dsp_op_write_reg(writeBackLogIdx[i], 0);
}
#endif
}
@@ -0,0 +1,45 @@
// Copyright 2008 Dolphin Emulator Project
// Copyright 2005 Duddie
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Core/DSP/DSPCommon.h"
// Extended opcode support.
// Many opcode have the lower 0xFF (some only 0x7f) free - there, an opcode extension
// can be stored.
namespace DSPInterpreter
{
namespace Ext
{
void l(const UDSPInstruction opc);
void ln(const UDSPInstruction opc);
void ls(const UDSPInstruction opc);
void lsn(const UDSPInstruction opc);
void lsm(const UDSPInstruction opc);
void lsnm(const UDSPInstruction opc);
void sl(const UDSPInstruction opc);
void sln(const UDSPInstruction opc);
void slm(const UDSPInstruction opc);
void slnm(const UDSPInstruction opc);
void s(const UDSPInstruction opc);
void sn(const UDSPInstruction opc);
void ld(const UDSPInstruction opc);
void ldax(const UDSPInstruction opc);
void ldn(const UDSPInstruction opc);
void ldaxn(const UDSPInstruction opc);
void ldm(const UDSPInstruction opc);
void ldaxm(const UDSPInstruction opc);
void ldnm(const UDSPInstruction opc);
void ldaxnm(const UDSPInstruction opc);
void mv(const UDSPInstruction opc);
void dr(const UDSPInstruction opc);
void ir(const UDSPInstruction opc);
void nr(const UDSPInstruction opc);
void nop(const UDSPInstruction opc);
} // end namespace Ext
} // end namespace DSPinterpeter
@@ -0,0 +1,263 @@
// Copyright 2009 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
//
// Additional copyrights go to Duddie and Tratax (c) 2004
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/Interpreter/DSPIntUtil.h"
#include "Core/DSP/Interpreter/DSPInterpreter.h"
namespace DSPInterpreter
{
// SRS @M, $(0x18+S)
// 0010 1sss mmmm mmmm
// Move value from register $(0x18+D) to data memory pointed by address
// 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)
{
u8 reg = ((opc >> 8) & 0x7) + 0x18;
u16 addr = (g_dsp.r.cr << 8) | (opc & 0xFF);
if (reg >= DSP_REG_ACM0)
dsp_dmem_write(addr, dsp_op_read_reg_and_saturate(reg - DSP_REG_ACM0));
else
dsp_dmem_write(addr, dsp_op_read_reg(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)
{
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);
}
// 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)
{
u8 reg = opc & DSP_REG_MASK;
u16 addr = dsp_fetch_code();
u16 val = dsp_dmem_read(addr);
dsp_op_write_reg(reg, val);
dsp_conditional_extend_accum(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)
{
u8 reg = opc & DSP_REG_MASK;
u16 addr = dsp_fetch_code();
if (reg >= DSP_REG_ACM0)
dsp_dmem_write(addr, dsp_op_read_reg_and_saturate(reg - DSP_REG_ACM0));
else
dsp_dmem_write(addr, dsp_op_read_reg(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)
{
u16 addr = (s8)opc;
u16 imm = dsp_fetch_code();
dsp_dmem_write(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)
{
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);
}
// LRRD $D, @$S
// 0001 1000 1ssd dddd
// Move value from data memory pointed by addressing register $S toregister $D.
// Decrement register $S.
void 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);
}
// 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)
{
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);
}
// 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)
{
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]);
}
// 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)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
if (sreg >= DSP_REG_ACM0)
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
else
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg(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)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
if (sreg >= DSP_REG_ACM0)
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
else
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg(sreg));
g_dsp.r.ar[dreg] = dsp_decrement_addr_reg(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)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
if (sreg >= DSP_REG_ACM0)
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
else
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg(sreg));
g_dsp.r.ar[dreg] = dsp_increment_addr_reg(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)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
if (sreg >= DSP_REG_ACM0)
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
else
dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg(sreg));
g_dsp.r.ar[dreg] = dsp_increase_addr_reg(dreg, (s16)g_dsp.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)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
g_dsp.r.ac[dreg - DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
dsp_conditional_extend_accum(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)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
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);
}
// 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)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
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);
}
// 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)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
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]);
}
} // namespace
@@ -0,0 +1,161 @@
// Copyright 2009 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
//
// 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"
namespace DSPInterpreter
{
// MRR $D, $S
// 0001 11dd ddds ssss
// Move value from register $S to register $D.
void mrr(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1f;
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));
else
dsp_op_write_reg(dreg, dsp_op_read_reg(sreg));
dsp_conditional_extend_accum(dreg);
}
// LRI $D, #I
// 0000 0000 100d dddd
// iiii iiii iiii iiii
// Load immediate value I to register $D.
//
// DSPSpy discovery: This, and possibly other instructions that load a
// 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)
{
u8 reg = opc & DSP_REG_MASK;
u16 imm = dsp_fetch_code();
dsp_op_write_reg(reg, imm);
dsp_conditional_extend_accum(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)
{
u8 reg = ((opc >> 8) & 0x7) + DSP_REG_AXL0;
u16 imm = (s8)opc;
dsp_op_write_reg(reg, imm);
dsp_conditional_extend_accum(reg);
}
//----
// NX
// 1000 -000 xxxx xxxx
// 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)
{
zeroWriteBackLog();
}
//----
// DAR $arD
// 0000 0000 0000 01dd
// Decrement address register $arD.
void dar(const UDSPInstruction opc)
{
g_dsp.r.ar[opc & 0x3] = dsp_decrement_addr_reg(opc & 0x3);
}
// IAR $arD
// 0000 0000 0000 10dd
// Increment address register $arD.
void iar(const UDSPInstruction opc)
{
g_dsp.r.ar[opc & 0x3] = dsp_increment_addr_reg(opc & 0x3);
}
// 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)
{
u8 dreg = opc & 0x3;
g_dsp.r.ar[dreg] = dsp_decrease_addr_reg(dreg, (s16)g_dsp.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)
{
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]);
}
//----
// SBCLR #I
// 0001 0011 aaaa aiii
// bit of status register $sr. Bit number is calculated by adding 6 to
// immediate value I.
void sbclr(const UDSPInstruction opc)
{
u8 bit = (opc & 0x7) + 6;
g_dsp.r.sr &= ~(1 << bit);
}
// SBSET #I
// 0001 0010 aaaa aiii
// Set bit of status register $sr. Bit number is calculated by adding 6 to
// immediate value I.
void sbset(const UDSPInstruction opc)
{
u8 bit = (opc & 0x7) + 6;
g_dsp.r.sr |= (1 << bit);
}
// This is a bunch of flag setters, flipping bits in SR.
void srbith(const UDSPInstruction opc)
{
zeroWriteBackLog();
switch ((opc >> 8) & 0xf)
{
case 0xa: // M2
g_dsp.r.sr &= ~SR_MUL_MODIFY;
break;
case 0xb: // M0
g_dsp.r.sr |= SR_MUL_MODIFY;
break;
case 0xc: // CLR15
g_dsp.r.sr &= ~SR_MUL_UNSIGNED;
break;
case 0xd: // SET15
g_dsp.r.sr |= SR_MUL_UNSIGNED;
break;
case 0xe: // SET16 (CLR40)
g_dsp.r.sr &= ~SR_40_MODE_BIT;
break;
case 0xf: // SET40
g_dsp.r.sr |= SR_40_MODE_BIT;
break;
default:
break;
}
}
} // namespace
@@ -0,0 +1,595 @@
// Copyright 2009 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
//
// Additional copyrights go to Duddie and Tratax (c) 2004
// Multiplier and product register control
#include "Core/DSP/DSPTables.h"
#include "Core/DSP/Interpreter/DSPIntCCUtil.h"
#include "Core/DSP/Interpreter/DSPIntUtil.h"
#include "Core/DSP/Interpreter/DSPInterpreter.h"
namespace DSPInterpreter
{
// Only MULX family instructions have unsigned/mixed support.
inline s64 dsp_get_multiply_prod(u16 a, u16 b, u8 sign)
{
s64 prod;
if ((sign == 1) && (g_dsp.r.sr & SR_MUL_UNSIGNED)) // unsigned
prod = (u32)(a * b);
else if ((sign == 2) && (g_dsp.r.sr & SR_MUL_UNSIGNED)) // mixed
prod = a * (s16)b;
else
prod = (s16)a * (s16)b; // signed
// Conditionally multiply by 2.
if ((g_dsp.r.sr & SR_MUL_MODIFY) == 0)
prod <<= 1;
return prod;
}
inline s64 dsp_multiply(u16 a, u16 b, u8 sign = 0)
{
s64 prod = dsp_get_multiply_prod(a, b, sign);
return prod;
}
inline s64 dsp_multiply_add(u16 a, u16 b, u8 sign = 0)
{
s64 prod = dsp_get_long_prod() + dsp_get_multiply_prod(a, b, sign);
return prod;
}
inline s64 dsp_multiply_sub(u16 a, u16 b, u8 sign = 0)
{
s64 prod = dsp_get_long_prod() - dsp_get_multiply_prod(a, b, sign);
return prod;
}
inline s64 dsp_multiply_mulx(u8 axh0, u8 axh1, u16 val1, u16 val2)
{
s64 result;
if ((axh0 == 0) && (axh1 == 0))
result = dsp_multiply(val1, val2, 1); // unsigned support ON if both ax?.l regs are used
else if ((axh0 == 0) && (axh1 == 1))
result = dsp_multiply(val1, val2, 2); // mixed support ON (u16)axl.0 * (s16)axh.1
else if ((axh0 == 1) && (axh1 == 0))
result = dsp_multiply(val2, val1, 2); // mixed support ON (u16)axl.1 * (s16)axh.0
else
result = dsp_multiply(val1, val2, 0); // unsigned support OFF if both ax?.h regs are used
return result;
}
//----
// CLRP
// 1000 0100 xxxx xxxx
// Clears product register $prod.
// Magic numbers taken from duddie's doc
//
// 00ff_(fff0 + 0010)_0000 = 0100_0000_0000, conveniently, lower 40bits = 0
//
// It's not ok, to just zero all of them, correct values should be set because of
// direct use of prod regs by AX/AXWII (look @that part of ucode).
void clrp(const UDSPInstruction opc)
{
zeroWriteBackLog();
g_dsp.r.prod.l = 0x0000;
g_dsp.r.prod.m = 0xfff0;
g_dsp.r.prod.h = 0x00ff;
g_dsp.r.prod.m2 = 0x0010;
}
// TSTPROD
// 1000 0101 xxxx xxxx
// Test prod regs value.
//
// flags out: --xx xx0x
void tstprod(const UDSPInstruction opc)
{
s64 prod = dsp_get_long_prod();
Update_SR_Register64(prod);
zeroWriteBackLog();
}
//----
// MOVP $acD
// 0110 111d xxxx xxxx
// Moves multiply product from $prod register to accumulator $acD register.
//
// flags out: --xx xx0x
void movp(const UDSPInstruction opc)
{
u8 dreg = (opc >> 8) & 0x1;
s64 acc = dsp_get_long_prod();
zeroWriteBackLog();
dsp_set_long_acc(dreg, acc);
Update_SR_Register64(acc);
}
// MOVNP $acD
// 0111 111d xxxx xxxx
// Moves negative of multiply product from $prod register to accumulator
// $acD register.
//
// flags out: --xx xx0x
void movnp(const UDSPInstruction opc)
{
u8 dreg = (opc >> 8) & 0x1;
s64 acc = -dsp_get_long_prod();
zeroWriteBackLog();
dsp_set_long_acc(dreg, acc);
Update_SR_Register64(acc);
}
// MOVPZ $acD
// 1111 111d xxxx xxxx
// Moves multiply product from $prod register to accumulator $acD
// register and sets (rounds) $acD.l to 0
//
// flags out: --xx xx0x
void movpz(const UDSPInstruction opc)
{
u8 dreg = (opc >> 8) & 0x01;
s64 acc = dsp_get_long_prod_round_prodl();
zeroWriteBackLog();
dsp_set_long_acc(dreg, acc);
Update_SR_Register64(acc);
}
// ADDPAXZ $acD, $axS
// 1111 10sd xxxx xxxx
// Adds secondary accumulator $axS to product register and stores result
// in accumulator register. Low 16-bits of $acD ($acD.l) are set (round) to 0.
//
// TODO: ugly code and still small error here (+/- 1 in .m - randomly)
// flags out: --xx xx0x
void addpaxz(const UDSPInstruction opc)
{
u8 dreg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
s64 oldprod = dsp_get_long_prod();
s64 prod = dsp_get_long_prod_round_prodl();
s64 ax = dsp_get_long_acx(sreg);
s64 res = prod + (ax & ~0xffff);
zeroWriteBackLog();
dsp_set_long_acc(dreg, res);
res = dsp_get_long_acc(dreg);
Update_SR_Register64(res, isCarry(oldprod, res), false);
}
//----
// MULAXH
// 1000 0011 xxxx xxxx
// Multiply $ax0.h by $ax0.h
void mulaxh(const UDSPInstruction opc)
{
s64 prod = dsp_multiply(dsp_get_ax_h(0), dsp_get_ax_h(0));
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
//----
// MUL $axS.l, $axS.h
// 1001 s000 xxxx xxxx
// Multiply low part $axS.l of secondary accumulator $axS by high part
// $axS.h of secondary accumulator $axS (treat them both as signed).
void mul(const UDSPInstruction opc)
{
u8 sreg = (opc >> 11) & 0x1;
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply(axh, axl);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MULAC $axS.l, $axS.h, $acR
// 1001 s10r xxxx xxxx
// Add product register to accumulator register $acR. Multiply low part
// $axS.l of secondary accumulator $axS by high part $axS.h of secondary
// accumulator $axS (treat them both as signed).
//
// flags out: --xx xx0x
void mulac(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 11) & 0x1;
s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply(axl, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
// MULMV $axS.l, $axS.h, $acR
// 1001 s11r xxxx xxxx
// Move product register to accumulator register $acR. Multiply low part
// $axS.l of secondary accumulator $axS by high part $axS.h of secondary
// accumulator $axS (treat them both as signed).
//
// flags out: --xx xx0x
void mulmv(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 sreg = ((opc >> 11) & 0x1);
s64 acc = dsp_get_long_prod();
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply(axl, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
// MULMVZ $axS.l, $axS.h, $acR
// 1001 s01r xxxx xxxx
// Move product register to accumulator register $acR and clear (round) low part
// of accumulator register $acR.l. Multiply low part $axS.l of secondary
// accumulator $axS by high part $axS.h of secondary accumulator $axS (treat
// them both as signed).
//
// flags out: --xx xx0x
void mulmvz(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 11) & 0x1;
s64 acc = dsp_get_long_prod_round_prodl();
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply(axl, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
//----
// MULX $ax0.S, $ax1.T
// 101s t000 xxxx xxxx
// Multiply one part $ax0 by one part $ax1.
// Part is selected by S and T bits. Zero selects low part, one selects high part.
void mulx(const UDSPInstruction opc)
{
u8 treg = ((opc >> 11) & 0x1);
u8 sreg = ((opc >> 12) & 0x1);
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MULXAC $ax0.S, $ax1.T, $acR
// 101s t01r xxxx xxxx
// Add product register to accumulator register $acR. Multiply one part
// $ax0 by one part $ax1. Part is selected by S and
// T bits. Zero selects low part, one selects high part.
//
// flags out: --xx xx0x
void mulxac(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
// MULXMV $ax0.S, $ax1.T, $acR
// 101s t11r xxxx xxxx
// Move product register to accumulator register $acR. Multiply one part
// $ax0 by one part $ax1. Part is selected by S and
// T bits. Zero selects low part, one selects high part.
//
// flags out: --xx xx0x
void mulxmv(const UDSPInstruction opc)
{
u8 rreg = ((opc >> 8) & 0x1);
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_prod();
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
// MULXMVZ $ax0.S, $ax1.T, $acR
// 101s t01r xxxx xxxx
// Move product register to accumulator register $acR and clear (round) low part
// of accumulator register $acR.l. Multiply one part $ax0 by one part $ax1
// Part is selected by S and T bits. Zero selects low part,
// one selects high part.
//
// flags out: --xx xx0x
void mulxmvz(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_prod_round_prodl();
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
//----
// MULC $acS.m, $axT.h
// 110s t000 xxxx xxxx
// Multiply mid part of accumulator register $acS.m by high part $axS.h of
// secondary accumulator $axS (treat them both as signed).
void mulc(const UDSPInstruction opc)
{
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply(accm, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MULCAC $acS.m, $axT.h, $acR
// 110s t10r xxxx xxxx
// Multiply mid part of accumulator register $acS.m by high part $axS.h of
// secondary accumulator $axS (treat them both as signed). Add product
// register before multiplication to accumulator $acR.
//
// flags out: --xx xx0x
void mulcac(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply(accm, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
// MULCMV $acS.m, $axT.h, $acR
// 110s t11r xxxx xxxx
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
// secondary accumulator $axT (treat them both as signed). Move product
// register before multiplication to accumulator $acR.
// possible mistake in duddie's doc axT.h rather than axS.h
//
// flags out: --xx xx0x
void mulcmv(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_prod();
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply(accm, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
// MULCMVZ $acS.m, $axT.h, $acR
// 110s t01r xxxx xxxx
// (fixed possible bug in duddie's description, s->t)
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
// secondary accumulator $axT (treat them both as signed). Move product
// register before multiplication to accumulator $acR, set (round) low part of
// accumulator $acR.l to zero.
//
// flags out: --xx xx0x
void mulcmvz(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_prod_round_prodl();
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply(accm, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
//----
// MADDX ax0.S ax1.T
// 1110 00st xxxx xxxx
// Multiply one part of secondary accumulator $ax0 (selected by S) by
// one part of secondary accumulator $ax1 (selected by T) (treat them both as
// signed) and add result to product register.
void maddx(const UDSPInstruction opc)
{
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_add(val1, val2);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MSUBX $(0x18+S*2), $(0x19+T*2)
// 1110 01st xxxx xxxx
// Multiply one part of secondary accumulator $ax0 (selected by S) by
// one part of secondary accumulator $ax1 (selected by T) (treat them both as
// signed) and subtract result from product register.
void msubx(const UDSPInstruction opc)
{
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_sub(val1, val2);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MADDC $acS.m, $axT.h
// 1110 10st xxxx xxxx
// Multiply middle part of accumulator $acS.m by high part of secondary
// accumulator $axT.h (treat them both as signed) and add result to product
// register.
void maddc(const UDSPInstruction opc)
{
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply_add(accm, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MSUBC $acS.m, $axT.h
// 1110 11st xxxx xxxx
// Multiply middle part of accumulator $acS.m by high part of secondary
// accumulator $axT.h (treat them both as signed) and subtract result from
// product register.
void msubc(const UDSPInstruction opc)
{
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply_sub(accm, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MADD $axS.l, $axS.h
// 1111 001s xxxx xxxx
// Multiply low part $axS.l of secondary accumulator $axS by high part
// $axS.h of secondary accumulator $axS (treat them both as signed) and add
// result to product register.
void madd(const UDSPInstruction opc)
{
u8 sreg = (opc >> 8) & 0x1;
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply_add(axl, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MSUB $axS.l, $axS.h
// 1111 011s xxxx xxxx
// Multiply low part $axS.l of secondary accumulator $axS by high part
// $axS.h of secondary accumulator $axS (treat them both as signed) and
// subtract result from product register.
void msub(const UDSPInstruction opc)
{
u8 sreg = (opc >> 8) & 0x1;
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply_sub(axl, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
} // namespace
@@ -0,0 +1,376 @@
// Copyright 2008 Dolphin Emulator Project
// Copyright 2005 Duddie
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Common/CommonTypes.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPStacks.h"
// ---------------------------------------------------------------------------------------
// --- 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(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(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
{
return ((val << 24) >> 24);
}
inline s64 dsp_round_long_acc(s64 val)
{
if (val & 0x10000)
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;
}
@@ -0,0 +1,241 @@
// Copyright 2008 Dolphin Emulator Project
// Copyright 2004 Duddie & Tratax
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/DSP/Interpreter/DSPInterpreter.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Core/DSP/DSPAnalyzer.h"
#include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPMemoryMap.h"
#include "Core/DSP/DSPTables.h"
namespace DSPInterpreter
{
namespace
{
void ExecuteInstruction(const UDSPInstruction inst)
{
const DSPOPCTemplate* opcode_template = GetOpTemplate(inst);
if (opcode_template->extended)
{
if ((inst >> 12) == 0x3)
extOpTable[inst & 0x7F]->intFunc(inst);
else
extOpTable[inst & 0xFF]->intFunc(inst);
}
opcode_template->intFunc(inst);
if (opcode_template->extended)
{
applyWriteBackLog();
}
}
} // Anonymous namespace
// NOTE: These have nothing to do with g_dsp.r.cr !
void WriteCR(u16 val)
{
// reset
if (val & 1)
{
INFO_LOG(DSPLLE, "DSP_CONTROL RESET");
DSPCore_Reset();
val &= ~1;
}
// init
else if (val == 4)
{
// HAX!
// OSInitAudioSystem ucode should send this mail - not DSP core itself
INFO_LOG(DSPLLE, "DSP_CONTROL INIT");
g_init_hax = true;
val |= 0x800;
}
// update cr
g_dsp.cr = val;
}
u16 ReadCR()
{
if (g_dsp.pc & 0x8000)
{
g_dsp.cr |= 0x800;
}
else
{
g_dsp.cr &= ~0x800;
}
return g_dsp.cr;
}
void Step()
{
DSPCore_CheckExceptions();
g_dsp.step_counter++;
#if PROFILE
g_dsp.err_pc = g_dsp.pc;
ProfilerAddDelta(g_dsp.err_pc, 1);
if (g_dsp.step_counter == 1)
{
ProfilerInit();
}
if ((g_dsp.step_counter & 0xFFFFF) == 0)
{
ProfilerDump(g_dsp.step_counter);
}
#endif
u16 opc = dsp_fetch_code();
ExecuteInstruction(UDSPInstruction(opc));
if (DSPAnalyzer::code_flags[static_cast<u16>(g_dsp.pc - 1u)] & DSPAnalyzer::CODE_LOOP_END)
HandleLoop();
}
// Used by thread mode.
int RunCyclesThread(int cycles)
{
while (true)
{
if (g_dsp.cr & CR_HALT)
return 0;
if (g_dsp.external_interrupt_waiting)
{
DSPCore_CheckExternalInterrupt();
DSPCore_SetExternalInterrupt(false);
}
Step();
cycles--;
if (cycles < 0)
return 0;
}
}
// This one has basic idle skipping, and checks breakpoints.
int RunCyclesDebug(int cycles)
{
// First, let's run a few cycles with no idle skipping so that things can progress a bit.
for (int i = 0; i < 8; i++)
{
if (g_dsp.cr & CR_HALT)
return 0;
if (g_dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
{
DSPCore_SetState(DSPCORE_STEPPING);
return cycles;
}
Step();
cycles--;
if (cycles < 0)
return 0;
}
while (true)
{
// Next, let's run a few cycles with idle skipping, so that we can skip
// idle loops.
for (int i = 0; i < 8; i++)
{
if (g_dsp.cr & CR_HALT)
return 0;
if (g_dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
{
DSPCore_SetState(DSPCORE_STEPPING);
return cycles;
}
// Idle skipping.
if (DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP)
return 0;
Step();
cycles--;
if (cycles < 0)
return 0;
}
// Now, lets run some more without idle skipping.
for (int i = 0; i < 200; i++)
{
if (g_dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
{
DSPCore_SetState(DSPCORE_STEPPING);
return cycles;
}
Step();
cycles--;
if (cycles < 0)
return 0;
// We don't bother directly supporting pause - if the main emu pauses,
// it just won't call this function anymore.
}
}
}
// Used by non-thread mode. Meant to be efficient.
int RunCycles(int cycles)
{
// First, let's run a few cycles with no idle skipping so that things can
// progress a bit.
for (int i = 0; i < 8; i++)
{
if (g_dsp.cr & CR_HALT)
return 0;
Step();
cycles--;
if (cycles < 0)
return 0;
}
while (true)
{
// Next, let's run a few cycles with idle skipping, so that we can skip
// idle loops.
for (int i = 0; i < 8; i++)
{
if (g_dsp.cr & CR_HALT)
return 0;
// Idle skipping.
if (DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP)
return 0;
Step();
cycles--;
if (cycles < 0)
return 0;
}
// Now, lets run some more without idle skipping.
for (int i = 0; i < 200; i++)
{
Step();
cycles--;
if (cycles < 0)
return 0;
// We don't bother directly supporting pause - if the main emu pauses,
// it just won't call this function anymore.
}
}
}
void nop(const UDSPInstruction opc)
{
// The real nop is 0. Anything else is bad.
if (opc == 0)
return;
ERROR_LOG(DSPLLE, "LLE: Unrecognized opcode 0x%04x", opc);
}
} // namespace
@@ -0,0 +1,149 @@
// Copyright 2009 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Core/DSP/DSPCommon.h"
#define DSP_REG_MASK 0x1f
namespace DSPInterpreter
{
void Step();
// See: DspIntBranch.cpp
void HandleLoop();
// If these simply return the same number of cycles as was passed into them,
// chances are that the DSP is halted.
// The difference between them is that the debug one obeys breakpoints.
int RunCyclesThread(int cycles);
int RunCycles(int cycles);
int RunCyclesDebug(int cycles);
void WriteCR(u16 val);
u16 ReadCR();
// All the opcode functions.
void abs(const UDSPInstruction opc);
void add(const UDSPInstruction opc);
void addarn(const UDSPInstruction opc);
void addax(const UDSPInstruction opc);
void addaxl(const UDSPInstruction opc);
void addi(const UDSPInstruction opc);
void addis(const UDSPInstruction opc);
void addp(const UDSPInstruction opc);
void addpaxz(const UDSPInstruction opc);
void addr(const UDSPInstruction opc);
void andc(const UDSPInstruction opc);
void andcf(const UDSPInstruction opc);
void andf(const UDSPInstruction opc);
void andi(const UDSPInstruction opc);
void andr(const UDSPInstruction opc);
void asl(const UDSPInstruction opc);
void asr(const UDSPInstruction opc);
void asr16(const UDSPInstruction opc);
void asrn(const UDSPInstruction opc);
void asrnr(const UDSPInstruction opc);
void asrnrx(const UDSPInstruction opc);
void bloop(const UDSPInstruction opc);
void bloopi(const UDSPInstruction opc);
void call(const UDSPInstruction opc);
void callr(const UDSPInstruction opc);
void clr(const UDSPInstruction opc);
void clrl(const UDSPInstruction opc);
void clrp(const UDSPInstruction opc);
void cmp(const UDSPInstruction opc);
void cmpar(const UDSPInstruction opc);
void cmpi(const UDSPInstruction opc);
void cmpis(const UDSPInstruction opc);
void dar(const UDSPInstruction opc);
void dec(const UDSPInstruction opc);
void decm(const UDSPInstruction opc);
void halt(const UDSPInstruction opc);
void iar(const UDSPInstruction opc);
void ifcc(const UDSPInstruction opc);
void ilrr(const UDSPInstruction opc);
void ilrrd(const UDSPInstruction opc);
void ilrri(const UDSPInstruction opc);
void ilrrn(const UDSPInstruction opc);
void inc(const UDSPInstruction opc);
void incm(const UDSPInstruction opc);
void jcc(const UDSPInstruction opc);
void jmprcc(const UDSPInstruction opc);
void loop(const UDSPInstruction opc);
void loopi(const UDSPInstruction opc);
void lr(const UDSPInstruction opc);
void lri(const UDSPInstruction opc);
void lris(const UDSPInstruction opc);
void lrr(const UDSPInstruction opc);
void lrrd(const UDSPInstruction opc);
void lrri(const UDSPInstruction opc);
void lrrn(const UDSPInstruction opc);
void lrs(const UDSPInstruction opc);
void lsl(const UDSPInstruction opc);
void lsl16(const UDSPInstruction opc);
void lsr(const UDSPInstruction opc);
void lsr16(const UDSPInstruction opc);
void lsrn(const UDSPInstruction opc);
void lsrnr(const UDSPInstruction opc);
void lsrnrx(const UDSPInstruction opc);
void madd(const UDSPInstruction opc);
void maddc(const UDSPInstruction opc);
void maddx(const UDSPInstruction opc);
void mov(const UDSPInstruction opc);
void movax(const UDSPInstruction opc);
void movnp(const UDSPInstruction opc);
void movp(const UDSPInstruction opc);
void movpz(const UDSPInstruction opc);
void movr(const UDSPInstruction opc);
void mrr(const UDSPInstruction opc);
void msub(const UDSPInstruction opc);
void msubc(const UDSPInstruction opc);
void msubx(const UDSPInstruction opc);
void mul(const UDSPInstruction opc);
void mulac(const UDSPInstruction opc);
void mulaxh(const UDSPInstruction opc);
void mulc(const UDSPInstruction opc);
void mulcac(const UDSPInstruction opc);
void mulcmv(const UDSPInstruction opc);
void mulcmvz(const UDSPInstruction opc);
void mulmv(const UDSPInstruction opc);
void mulmvz(const UDSPInstruction opc);
void mulx(const UDSPInstruction opc);
void mulxac(const UDSPInstruction opc);
void mulxmv(const UDSPInstruction opc);
void mulxmvz(const UDSPInstruction opc);
void neg(const UDSPInstruction opc);
void nop(const UDSPInstruction opc);
void notc(const UDSPInstruction opc);
void nx(const UDSPInstruction opc);
void orc(const UDSPInstruction opc);
void ori(const UDSPInstruction opc);
void orr(const UDSPInstruction opc);
void ret(const UDSPInstruction opc);
void rti(const UDSPInstruction opc);
void sbclr(const UDSPInstruction opc);
void sbset(const UDSPInstruction opc);
void si(const UDSPInstruction opc);
void sr(const UDSPInstruction opc);
void srbith(const UDSPInstruction opc);
void srr(const UDSPInstruction opc);
void srrd(const UDSPInstruction opc);
void srri(const UDSPInstruction opc);
void srrn(const UDSPInstruction opc);
void srs(const UDSPInstruction opc);
void sub(const UDSPInstruction opc);
void subarn(const UDSPInstruction opc);
void subax(const UDSPInstruction opc);
void subp(const UDSPInstruction opc);
void subr(const UDSPInstruction opc);
void tst(const UDSPInstruction opc);
void tstaxh(const UDSPInstruction opc);
void tstprod(const UDSPInstruction opc);
void xorc(const UDSPInstruction opc);
void xori(const UDSPInstruction opc);
void xorr(const UDSPInstruction opc);
} // namespace