DSP: Move interpreter source files to their own directory
Keeps code organized similar to the DSP JIT.
This commit is contained in:
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
|
||||
Reference in new issue
Block a user