2009-07-06 02:10:26 +00:00
|
|
|
// Copyright (C) 2003-2009 Dolphin Project.
|
|
|
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, version 2.0.
|
|
|
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
|
|
|
|
// Official SVN repository and contact information can be found at
|
|
|
|
// http://code.google.com/p/dolphin-emu/
|
|
|
|
|
|
|
|
// Additional copyrights go to Duddie and Tratax (c) 2004
|
|
|
|
|
|
|
|
#include "DSPInterpreter.h"
|
|
|
|
#include "DSPCore.h"
|
|
|
|
#include "DSPMemoryMap.h"
|
|
|
|
#include "DSPStacks.h"
|
|
|
|
|
|
|
|
#include "DSPIntCCUtil.h"
|
|
|
|
#include "DSPIntUtil.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.hex & 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.hex & 0xf))
|
|
|
|
{
|
|
|
|
u8 reg = (opc.hex >> 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.hex & 0xf))
|
|
|
|
{
|
|
|
|
// skip the next opcode - we have to lookup its size.
|
|
|
|
g_dsp.pc += opSize[dsp_peek_code()];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.hex & 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.hex & 0xf))
|
|
|
|
{
|
|
|
|
u8 reg = (opc.hex >> 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.hex & 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[DSP_REG_SR] = dsp_reg_load_stack(DSP_STACK_D);
|
|
|
|
g_dsp.pc = dsp_reg_load_stack(DSP_STACK_C);
|
|
|
|
|
2009-07-10 11:19:47 +00:00
|
|
|
g_dsp.exception_in_progress = -1;
|
2009-07-06 02:10:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2009-07-06 21:27:56 +00:00
|
|
|
// then PC is modified with value from call stack $st0. Otherwise values from
|
|
|
|
// call stack $st0 and both loop stacks $st2 and $st3 are poped and execution
|
2009-07-06 02:10:26 +00:00
|
|
|
// continues at next opcode.
|
2009-07-06 19:19:03 +00:00
|
|
|
void HandleLoop()
|
|
|
|
{
|
|
|
|
// Handle looping hardware.
|
2009-07-06 21:27:56 +00:00
|
|
|
const u16 rCallAddress = g_dsp.r[DSP_REG_ST0];
|
|
|
|
const u16 rLoopAddress = g_dsp.r[DSP_REG_ST2];
|
2009-07-06 19:19:03 +00:00
|
|
|
u16& rLoopCounter = g_dsp.r[DSP_REG_ST3];
|
|
|
|
|
2009-07-06 21:27:56 +00:00
|
|
|
if (rLoopAddress > 0 && rLoopCounter > 0)
|
|
|
|
{
|
2009-07-11 10:18:25 +00:00
|
|
|
// FIXME: why -1? because we just read past it.
|
2009-07-06 21:27:56 +00:00
|
|
|
if (g_dsp.pc - 1 == rLoopAddress)
|
2009-07-06 19:19:03 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-07-06 02:10:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
// 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.hex & 0x1f;
|
|
|
|
u16 cnt = g_dsp.r[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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.hex & 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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 is allowed.
|
|
|
|
void bloop(const UDSPInstruction& opc)
|
|
|
|
{
|
|
|
|
u16 reg = opc.hex & 0x1f;
|
|
|
|
u16 cnt = g_dsp.r[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);
|
|
|
|
}
|
2009-07-08 20:06:15 +00:00
|
|
|
else
|
2009-07-06 02:10:26 +00:00
|
|
|
{
|
|
|
|
g_dsp.pc = loop_pc;
|
|
|
|
g_dsp.pc += opSize[dsp_peek_code()];
|
2009-07-08 20:06:15 +00:00
|
|
|
}
|
2009-07-06 02:10:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 is allowed.
|
|
|
|
void bloopi(const UDSPInstruction& opc)
|
|
|
|
{
|
|
|
|
u16 cnt = opc.hex & 0xff;
|
|
|
|
u16 loop_pc = dsp_fetch_code();
|
|
|
|
|
2009-07-06 21:27:56 +00:00
|
|
|
if (cnt)
|
2009-07-06 02:10:26 +00:00
|
|
|
{
|
|
|
|
dsp_reg_store_stack(0, g_dsp.pc);
|
|
|
|
dsp_reg_store_stack(2, loop_pc);
|
|
|
|
dsp_reg_store_stack(3, cnt);
|
|
|
|
}
|
2009-07-08 20:06:15 +00:00
|
|
|
else
|
2009-07-06 02:10:26 +00:00
|
|
|
{
|
|
|
|
g_dsp.pc = loop_pc;
|
|
|
|
g_dsp.pc += opSize[dsp_peek_code()];
|
2009-07-08 20:06:15 +00:00
|
|
|
}
|
2009-07-06 02:10:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|