Merge pull request #10048 from Pokechu22/dsp-manual
Improvements to DSP manual
This commit is contained in:
commit
e7a30dd468
|
@ -47,7 +47,7 @@ void Interpreter::clrl(const UDSPInstruction opc)
|
||||||
// 0000 001r 1100 0000
|
// 0000 001r 1100 0000
|
||||||
// iiii iiii iiii iiii
|
// iiii iiii iiii iiii
|
||||||
// Set logic zero (LZ) flag in status register $sr if result of logic AND of
|
// Set logic zero (LZ) flag in status register $sr if result of logic AND of
|
||||||
// accumulator mid part $acD.m with immediate value I is equal I.
|
// accumulator mid part $acD.m with immediate value I is equal to I.
|
||||||
//
|
//
|
||||||
// flags out: -x-- ----
|
// flags out: -x-- ----
|
||||||
void Interpreter::andcf(const UDSPInstruction opc)
|
void Interpreter::andcf(const UDSPInstruction opc)
|
||||||
|
@ -64,7 +64,7 @@ void Interpreter::andcf(const UDSPInstruction opc)
|
||||||
// iiii iiii iiii iiii
|
// iiii iiii iiii iiii
|
||||||
// Set logic zero (LZ) flag in status register $sr if result of logical AND
|
// Set logic zero (LZ) flag in status register $sr if result of logical AND
|
||||||
// operation of accumulator mid part $acD.m with immediate value I is equal
|
// operation of accumulator mid part $acD.m with immediate value I is equal
|
||||||
// immediate value 0.
|
// to immediate value 0.
|
||||||
//
|
//
|
||||||
// flags out: -x-- ----
|
// flags out: -x-- ----
|
||||||
void Interpreter::andf(const UDSPInstruction opc)
|
void Interpreter::andf(const UDSPInstruction opc)
|
||||||
|
@ -126,8 +126,7 @@ void Interpreter::cmp(const UDSPInstruction)
|
||||||
|
|
||||||
// CMPAR $acS axR.h
|
// CMPAR $acS axR.h
|
||||||
// 110r s001 xxxx xxxx
|
// 110r s001 xxxx xxxx
|
||||||
// Compares accumulator $acS with accumulator axR.h.
|
// Compares accumulator $acS with accumulator $axR.h.
|
||||||
// Not described by Duddie's doc - at least not as a separate instruction.
|
|
||||||
//
|
//
|
||||||
// flags out: x-xx xxxx
|
// flags out: x-xx xxxx
|
||||||
void Interpreter::cmpar(const UDSPInstruction opc)
|
void Interpreter::cmpar(const UDSPInstruction opc)
|
||||||
|
@ -794,7 +793,7 @@ void Interpreter::movr(const UDSPInstruction opc)
|
||||||
|
|
||||||
// MOVAX $acD, $axS
|
// MOVAX $acD, $axS
|
||||||
// 0110 10sd xxxx xxxx
|
// 0110 10sd xxxx xxxx
|
||||||
// Moves secondary accumulator $axS to accumulator $axD.
|
// Moves secondary accumulator $axS to accumulator $acD.
|
||||||
//
|
//
|
||||||
// flags out: --xx xx00
|
// flags out: --xx xx00
|
||||||
void Interpreter::movax(const UDSPInstruction opc)
|
void Interpreter::movax(const UDSPInstruction opc)
|
||||||
|
@ -812,7 +811,7 @@ void Interpreter::movax(const UDSPInstruction opc)
|
||||||
|
|
||||||
// MOV $acD, $ac(1-D)
|
// MOV $acD, $ac(1-D)
|
||||||
// 0110 110d xxxx xxxx
|
// 0110 110d xxxx xxxx
|
||||||
// Moves accumulator $ax(1-D) to accumulator $axD.
|
// Moves accumulator $ac(1-D) to accumulator $acD.
|
||||||
//
|
//
|
||||||
// flags out: --x0 xx00
|
// flags out: --x0 xx00
|
||||||
void Interpreter::mov(const UDSPInstruction opc)
|
void Interpreter::mov(const UDSPInstruction opc)
|
||||||
|
|
|
@ -76,9 +76,10 @@ void Interpreter::jcc(const UDSPInstruction opc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic jmpr implementation
|
// Generic jmpr implementation
|
||||||
// JMPcc $R
|
// JRcc $R
|
||||||
// 0001 0111 rrr0 cccc
|
// 0001 0111 rrr0 cccc
|
||||||
// Jump to address; set program counter to a value from register $R.
|
// Jump to address if condition cc has been met. Set program counter to
|
||||||
|
// a value from register $R.
|
||||||
void Interpreter::jmprcc(const UDSPInstruction opc)
|
void Interpreter::jmprcc(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
if (!CheckCondition(opc & 0xf))
|
if (!CheckCondition(opc & 0xf))
|
||||||
|
@ -116,7 +117,7 @@ void Interpreter::rti(const UDSPInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HALT
|
// HALT
|
||||||
// 0000 0000 0020 0001
|
// 0000 0000 0010 0001
|
||||||
// Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR.
|
// Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR.
|
||||||
void Interpreter::halt(const UDSPInstruction)
|
void Interpreter::halt(const UDSPInstruction)
|
||||||
{
|
{
|
||||||
|
|
|
@ -144,7 +144,7 @@ void Interpreter::l(const UDSPInstruction opc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LN $axD.D, @$arS
|
// LN $axD.D, @$arS
|
||||||
// xxxx xxxx 01dd d0ss
|
// xxxx xxxx 01dd d1ss
|
||||||
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
|
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
|
||||||
// Add indexing register $ixS to register $arS.
|
// Add indexing register $ixS to register $arS.
|
||||||
void Interpreter::ln(const UDSPInstruction opc)
|
void Interpreter::ln(const UDSPInstruction opc)
|
||||||
|
@ -327,17 +327,17 @@ void Interpreter::slnm(const UDSPInstruction opc)
|
||||||
IncreaseAddressRegister(DSP_REG_AR0, static_cast<s16>(state.r.ix[0])));
|
IncreaseAddressRegister(DSP_REG_AR0, static_cast<s16>(state.r.ix[0])));
|
||||||
}
|
}
|
||||||
|
|
||||||
// LD $ax0.d, $ax1.r, @$arS
|
// LD $ax0.D, $ax1.R, @$arS
|
||||||
// xxxx xxxx 11dr 00ss
|
// xxxx xxxx 11dr 00ss
|
||||||
// example for "nx'ld $AX0.L, $AX1.L, @$AR3"
|
// Load register $ax0.D (either $ax0.l or $ax0.h) with value from memory pointed by register $arS.
|
||||||
// Loads the word pointed by AR0 to AX0.H, then loads the word pointed by AR3 to AX0.L.
|
// Load register $ax1.R (either $ax1.l or $ax1.h) with value from memory pointed by register $ar3.
|
||||||
// Increments AR0 and AR3.
|
// Increment both $arS and $ar3.
|
||||||
// If AR0 and AR3 point into the same memory page (upper 6 bits of addr are the same -> games are
|
// S cannot be 3, as that encodes LDAX. Thus $arS and $ar3 are known to be distinct.
|
||||||
// not doing that!)
|
// If $ar0 and $ar3 point into the same memory page (upper 6 bits of addr are the same -> games are
|
||||||
// then the value pointed by AR0 is loaded to BOTH AX0.H and AX0.L.
|
// not doing that!) then the value pointed by $ar0 is loaded to BOTH $ax0.D and $ax1.R.
|
||||||
// If AR0 points into an invalid memory page (ie 0x2000), then AX0.H keeps its old value. (not
|
// If $ar0 points into an invalid memory page (ie 0x2000), then $ax0.D keeps its old value. (not
|
||||||
// implemented yet)
|
// implemented yet)
|
||||||
// If AR3 points into an invalid memory page, then AX0.L gets the same value as AX0.H. (not
|
// If $ar3 points into an invalid memory page, then $ax1.R gets the same value as $ax0.D. (not
|
||||||
// implemented yet)
|
// implemented yet)
|
||||||
void Interpreter::ld(const UDSPInstruction opc)
|
void Interpreter::ld(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
|
@ -360,6 +360,9 @@ void Interpreter::ld(const UDSPInstruction opc)
|
||||||
|
|
||||||
// LDAX $axR, @$arS
|
// LDAX $axR, @$arS
|
||||||
// xxxx xxxx 11sr 0011
|
// xxxx xxxx 11sr 0011
|
||||||
|
// Load register $axR.h with value from memory pointed by register $arS.
|
||||||
|
// Load register $axR.l with value from memory pointed by register $ar3.
|
||||||
|
// Increment both $arS and $ar3.
|
||||||
void Interpreter::ldax(const UDSPInstruction opc)
|
void Interpreter::ldax(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
const u8 sreg = (opc >> 5) & 0x1;
|
const u8 sreg = (opc >> 5) & 0x1;
|
||||||
|
@ -378,7 +381,7 @@ void Interpreter::ldax(const UDSPInstruction opc)
|
||||||
WriteToBackLog(3, DSP_REG_AR3, IncrementAddressRegister(DSP_REG_AR3));
|
WriteToBackLog(3, DSP_REG_AR3, IncrementAddressRegister(DSP_REG_AR3));
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDN $ax0.d, $ax1.r, @$arS
|
// LDN $ax0.D, $ax1.R, @$arS
|
||||||
// xxxx xxxx 11dr 01ss
|
// xxxx xxxx 11dr 01ss
|
||||||
void Interpreter::ldn(const UDSPInstruction opc)
|
void Interpreter::ldn(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
|
@ -419,7 +422,7 @@ void Interpreter::ldaxn(const UDSPInstruction opc)
|
||||||
WriteToBackLog(3, DSP_REG_AR3, IncrementAddressRegister(DSP_REG_AR3));
|
WriteToBackLog(3, DSP_REG_AR3, IncrementAddressRegister(DSP_REG_AR3));
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDM $ax0.d, $ax1.r, @$arS
|
// LDM $ax0.D, $ax1.R, @$arS
|
||||||
// xxxx xxxx 11dr 10ss
|
// xxxx xxxx 11dr 10ss
|
||||||
void Interpreter::ldm(const UDSPInstruction opc)
|
void Interpreter::ldm(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
|
@ -462,7 +465,7 @@ void Interpreter::ldaxm(const UDSPInstruction opc)
|
||||||
IncreaseAddressRegister(DSP_REG_AR3, static_cast<s16>(state.r.ix[3])));
|
IncreaseAddressRegister(DSP_REG_AR3, static_cast<s16>(state.r.ix[3])));
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDNM $ax0.d, $ax1.r, @$arS
|
// LDNM $ax0.D, $ax1.R, @$arS
|
||||||
// xxxx xxxx 11dr 11ss
|
// xxxx xxxx 11dr 11ss
|
||||||
void Interpreter::ldnm(const UDSPInstruction opc)
|
void Interpreter::ldnm(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,7 +13,6 @@ namespace DSP::Interpreter
|
||||||
// Move value from register $(0x18+S) to data memory pointed by address
|
// Move value from register $(0x18+S) to data memory pointed by address
|
||||||
// CR[0-7] | M. That is, the upper 8 bits of the address are the
|
// 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.
|
// 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 Interpreter::srs(const UDSPInstruction opc)
|
void Interpreter::srs(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
auto& state = m_dsp_core.DSPState();
|
auto& state = m_dsp_core.DSPState();
|
||||||
|
@ -86,9 +85,9 @@ void Interpreter::si(const UDSPInstruction opc)
|
||||||
state.WriteDMEM(addr, imm);
|
state.WriteDMEM(addr, imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LRR $D, @$S
|
// LRR $D, @$arS
|
||||||
// 0001 1000 0ssd dddd
|
// 0001 1000 0ssd dddd
|
||||||
// Move value from data memory pointed by addressing register $S to register $D.
|
// Move value from data memory pointed by addressing register $arS to register $D.
|
||||||
void Interpreter::lrr(const UDSPInstruction opc)
|
void Interpreter::lrr(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
const u8 sreg = (opc >> 5) & 0x3;
|
const u8 sreg = (opc >> 5) & 0x3;
|
||||||
|
@ -100,10 +99,10 @@ void Interpreter::lrr(const UDSPInstruction opc)
|
||||||
ConditionalExtendAccum(dreg);
|
ConditionalExtendAccum(dreg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LRRD $D, @$S
|
// LRRD $D, @$arS
|
||||||
// 0001 1000 1ssd dddd
|
// 0001 1000 1ssd dddd
|
||||||
// Move value from data memory pointed by addressing register $S to register $D.
|
// Move value from data memory pointed by addressing register $arS to register $D.
|
||||||
// Decrement register $S.
|
// Decrement register $arS.
|
||||||
void Interpreter::lrrd(const UDSPInstruction opc)
|
void Interpreter::lrrd(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
const u8 sreg = (opc >> 5) & 0x3;
|
const u8 sreg = (opc >> 5) & 0x3;
|
||||||
|
@ -116,10 +115,10 @@ void Interpreter::lrrd(const UDSPInstruction opc)
|
||||||
state.r.ar[sreg] = DecrementAddressRegister(sreg);
|
state.r.ar[sreg] = DecrementAddressRegister(sreg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LRRI $D, @$S
|
// LRRI $D, @$arS
|
||||||
// 0001 1001 0ssd dddd
|
// 0001 1001 0ssd dddd
|
||||||
// Move value from data memory pointed by addressing register $S to register $D.
|
// Move value from data memory pointed by addressing register $arS to register $D.
|
||||||
// Increment register $S.
|
// Increment register $arS.
|
||||||
void Interpreter::lrri(const UDSPInstruction opc)
|
void Interpreter::lrri(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
const u8 sreg = (opc >> 5) & 0x3;
|
const u8 sreg = (opc >> 5) & 0x3;
|
||||||
|
@ -132,10 +131,10 @@ void Interpreter::lrri(const UDSPInstruction opc)
|
||||||
state.r.ar[sreg] = IncrementAddressRegister(sreg);
|
state.r.ar[sreg] = IncrementAddressRegister(sreg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LRRN $D, @$S
|
// LRRN $D, @$arS
|
||||||
// 0001 1001 1ssd dddd
|
// 0001 1001 1ssd dddd
|
||||||
// Move value from data memory pointed by addressing register $S to register $D.
|
// Move value from data memory pointed by addressing register $arS to register $D.
|
||||||
// Add indexing register $(0x4+S) to register $S.
|
// Add corresponding indexing register $ixS to register $arS.
|
||||||
void Interpreter::lrrn(const UDSPInstruction opc)
|
void Interpreter::lrrn(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
const u8 sreg = (opc >> 5) & 0x3;
|
const u8 sreg = (opc >> 5) & 0x3;
|
||||||
|
@ -148,10 +147,10 @@ void Interpreter::lrrn(const UDSPInstruction opc)
|
||||||
state.r.ar[sreg] = IncreaseAddressRegister(sreg, static_cast<s16>(state.r.ix[sreg]));
|
state.r.ar[sreg] = IncreaseAddressRegister(sreg, static_cast<s16>(state.r.ix[sreg]));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SRR @$D, $S
|
// SRR @$arD, $S
|
||||||
// 0001 1010 0dds ssss
|
// 0001 1010 0dds ssss
|
||||||
// Store value from source register $S to a memory location pointed by
|
// Store value from source register $S to a memory location pointed by
|
||||||
// addressing register $D.
|
// addressing register $arD.
|
||||||
void Interpreter::srr(const UDSPInstruction opc)
|
void Interpreter::srr(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
const u8 dreg = (opc >> 5) & 0x3;
|
const u8 dreg = (opc >> 5) & 0x3;
|
||||||
|
@ -164,10 +163,10 @@ void Interpreter::srr(const UDSPInstruction opc)
|
||||||
state.WriteDMEM(state.r.ar[dreg], OpReadRegister(sreg));
|
state.WriteDMEM(state.r.ar[dreg], OpReadRegister(sreg));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SRRD @$D, $S
|
// SRRD @$arD, $S
|
||||||
// 0001 1010 1dds ssss
|
// 0001 1010 1dds ssss
|
||||||
// Store value from source register $S to a memory location pointed by
|
// Store value from source register $S to a memory location pointed by
|
||||||
// addressing register $D. Decrement register $D.
|
// addressing register $arD. Decrement register $arD.
|
||||||
void Interpreter::srrd(const UDSPInstruction opc)
|
void Interpreter::srrd(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
const u8 dreg = (opc >> 5) & 0x3;
|
const u8 dreg = (opc >> 5) & 0x3;
|
||||||
|
@ -182,10 +181,10 @@ void Interpreter::srrd(const UDSPInstruction opc)
|
||||||
state.r.ar[dreg] = DecrementAddressRegister(dreg);
|
state.r.ar[dreg] = DecrementAddressRegister(dreg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SRRI @$D, $S
|
// SRRI @$arD, $S
|
||||||
// 0001 1011 0dds ssss
|
// 0001 1011 0dds ssss
|
||||||
// Store value from source register $S to a memory location pointed by
|
// Store value from source register $S to a memory location pointed by
|
||||||
// addressing register $D. Increment register $D.
|
// addressing register $arD. Increment register $arD.
|
||||||
void Interpreter::srri(const UDSPInstruction opc)
|
void Interpreter::srri(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
const u8 dreg = (opc >> 5) & 0x3;
|
const u8 dreg = (opc >> 5) & 0x3;
|
||||||
|
@ -200,10 +199,10 @@ void Interpreter::srri(const UDSPInstruction opc)
|
||||||
state.r.ar[dreg] = IncrementAddressRegister(dreg);
|
state.r.ar[dreg] = IncrementAddressRegister(dreg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SRRN @$D, $S
|
// SRRN @$arD, $S
|
||||||
// 0001 1011 1dds ssss
|
// 0001 1011 1dds ssss
|
||||||
// Store value from source register $S to a memory location pointed by
|
// Store value from source register $S to a memory location pointed by
|
||||||
// addressing register $D. Add DSP_REG_IX0 register to register $D.
|
// addressing register $arD. Add corresponding indexing register $ixD to register $arD.
|
||||||
void Interpreter::srrn(const UDSPInstruction opc)
|
void Interpreter::srrn(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
const u8 dreg = (opc >> 5) & 0x3;
|
const u8 dreg = (opc >> 5) & 0x3;
|
||||||
|
|
|
@ -122,8 +122,8 @@ void Interpreter::addarn(const UDSPInstruction opc)
|
||||||
|
|
||||||
// SBCLR #I
|
// SBCLR #I
|
||||||
// 0001 0010 aaaa aiii
|
// 0001 0010 aaaa aiii
|
||||||
// bit of status register $sr. Bit number is calculated by adding 6 to
|
// Clear bit of status register $sr. Bit number is calculated by adding 6 to immediate value I;
|
||||||
// immediate value I.
|
// thus, bits 6 through 13 (LZ through AM) can be cleared with this instruction.
|
||||||
void Interpreter::sbclr(const UDSPInstruction opc)
|
void Interpreter::sbclr(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
auto& state = m_dsp_core.DSPState();
|
auto& state = m_dsp_core.DSPState();
|
||||||
|
@ -134,8 +134,8 @@ void Interpreter::sbclr(const UDSPInstruction opc)
|
||||||
|
|
||||||
// SBSET #I
|
// SBSET #I
|
||||||
// 0001 0011 aaaa aiii
|
// 0001 0011 aaaa aiii
|
||||||
// Set bit of status register $sr. Bit number is calculated by adding 6 to
|
// Set bit of status register $sr. Bit number is calculated by adding 6 to immediate value I;
|
||||||
// immediate value I.
|
// thus, bits 6 through 13 (LZ through AM) can be set with this instruction.
|
||||||
void Interpreter::sbset(const UDSPInstruction opc)
|
void Interpreter::sbset(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
auto& state = m_dsp_core.DSPState();
|
auto& state = m_dsp_core.DSPState();
|
||||||
|
|
|
@ -247,7 +247,7 @@ void Interpreter::mulx(const UDSPInstruction opc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MULXAC $ax0.S, $ax1.T, $acR
|
// MULXAC $ax0.S, $ax1.T, $acR
|
||||||
// 101s t01r xxxx xxxx
|
// 101s t10r xxxx xxxx
|
||||||
// Add product register to accumulator register $acR. Multiply one part
|
// Add product register to accumulator register $acR. Multiply one part
|
||||||
// $ax0 by one part $ax1. Part is selected by S and
|
// $ax0 by one part $ax1. Part is selected by S and
|
||||||
// T bits. Zero selects low part, one selects high part.
|
// T bits. Zero selects low part, one selects high part.
|
||||||
|
@ -372,7 +372,6 @@ void Interpreter::mulcac(const UDSPInstruction opc)
|
||||||
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
|
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
|
||||||
// secondary accumulator $axT (treat them both as signed). Move product
|
// secondary accumulator $axT (treat them both as signed). Move product
|
||||||
// register before multiplication to accumulator $acR.
|
// register before multiplication to accumulator $acR.
|
||||||
// possible mistake in duddie's doc axT.h rather than axS.h
|
|
||||||
//
|
//
|
||||||
// flags out: --xx xx0x
|
// flags out: --xx xx0x
|
||||||
void Interpreter::mulcmv(const UDSPInstruction opc)
|
void Interpreter::mulcmv(const UDSPInstruction opc)
|
||||||
|
@ -395,7 +394,6 @@ void Interpreter::mulcmv(const UDSPInstruction opc)
|
||||||
|
|
||||||
// MULCMVZ $acS.m, $axT.h, $acR
|
// MULCMVZ $acS.m, $axT.h, $acR
|
||||||
// 110s t01r xxxx xxxx
|
// 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
|
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
|
||||||
// secondary accumulator $axT (treat them both as signed). Move product
|
// secondary accumulator $axT (treat them both as signed). Move product
|
||||||
// register before multiplication to accumulator $acR, set (round) low part of
|
// register before multiplication to accumulator $acR, set (round) low part of
|
||||||
|
|
|
@ -56,7 +56,7 @@ void DSPEmitter::clrl(const UDSPInstruction opc)
|
||||||
// 0000 001r 1100 0000
|
// 0000 001r 1100 0000
|
||||||
// iiii iiii iiii iiii
|
// iiii iiii iiii iiii
|
||||||
// Set logic zero (LZ) flag in status register $sr if result of logic AND of
|
// Set logic zero (LZ) flag in status register $sr if result of logic AND of
|
||||||
// accumulator mid part $acD.m with immediate value I is equal I.
|
// accumulator mid part $acD.m with immediate value I is equal to I.
|
||||||
//
|
//
|
||||||
// flags out: -x-- ----
|
// flags out: -x-- ----
|
||||||
void DSPEmitter::andcf(const UDSPInstruction opc)
|
void DSPEmitter::andcf(const UDSPInstruction opc)
|
||||||
|
@ -91,7 +91,7 @@ void DSPEmitter::andcf(const UDSPInstruction opc)
|
||||||
// iiii iiii iiii iiii
|
// iiii iiii iiii iiii
|
||||||
// Set logic zero (LZ) flag in status register $sr if result of logical AND
|
// Set logic zero (LZ) flag in status register $sr if result of logical AND
|
||||||
// operation of accumulator mid part $acD.m with immediate value I is equal
|
// operation of accumulator mid part $acD.m with immediate value I is equal
|
||||||
// immediate value 0.
|
// to immediate value 0.
|
||||||
//
|
//
|
||||||
// flags out: -x-- ----
|
// flags out: -x-- ----
|
||||||
void DSPEmitter::andf(const UDSPInstruction opc)
|
void DSPEmitter::andf(const UDSPInstruction opc)
|
||||||
|
@ -185,8 +185,7 @@ void DSPEmitter::cmp(const UDSPInstruction opc)
|
||||||
|
|
||||||
// CMPAR $acS axR.h
|
// CMPAR $acS axR.h
|
||||||
// 110r s001 xxxx xxxx
|
// 110r s001 xxxx xxxx
|
||||||
// Compares accumulator $acS with accumulator axR.h.
|
// Compares accumulator $acS with accumulator $axR.h.
|
||||||
// Not described by Duddie's doc - at least not as a separate instruction.
|
|
||||||
//
|
//
|
||||||
// flags out: x-xx xxxx
|
// flags out: x-xx xxxx
|
||||||
void DSPEmitter::cmpar(const UDSPInstruction opc)
|
void DSPEmitter::cmpar(const UDSPInstruction opc)
|
||||||
|
@ -1090,7 +1089,7 @@ void DSPEmitter::movr(const UDSPInstruction opc)
|
||||||
|
|
||||||
// MOVAX $acD, $axS
|
// MOVAX $acD, $axS
|
||||||
// 0110 10sd xxxx xxxx
|
// 0110 10sd xxxx xxxx
|
||||||
// Moves secondary accumulator $axS to accumulator $axD.
|
// Moves secondary accumulator $axS to accumulator $acD.
|
||||||
//
|
//
|
||||||
// flags out: --xx xx00
|
// flags out: --xx xx00
|
||||||
void DSPEmitter::movax(const UDSPInstruction opc)
|
void DSPEmitter::movax(const UDSPInstruction opc)
|
||||||
|
@ -1111,7 +1110,7 @@ void DSPEmitter::movax(const UDSPInstruction opc)
|
||||||
|
|
||||||
// MOV $acD, $ac(1-D)
|
// MOV $acD, $ac(1-D)
|
||||||
// 0110 110d xxxx xxxx
|
// 0110 110d xxxx xxxx
|
||||||
// Moves accumulator $ax(1-D) to accumulator $axD.
|
// Moves accumulator $ac(1-D) to accumulator $acD.
|
||||||
//
|
//
|
||||||
// flags out: --x0 xx00
|
// flags out: --x0 xx00
|
||||||
void DSPEmitter::mov(const UDSPInstruction opc)
|
void DSPEmitter::mov(const UDSPInstruction opc)
|
||||||
|
|
|
@ -156,9 +156,10 @@ void DSPEmitter::r_jmprcc(const UDSPInstruction opc)
|
||||||
WriteBranchExit();
|
WriteBranchExit();
|
||||||
}
|
}
|
||||||
// Generic jmpr implementation
|
// Generic jmpr implementation
|
||||||
// JMPcc $R
|
// JRcc $R
|
||||||
// 0001 0111 rrr0 cccc
|
// 0001 0111 rrr0 cccc
|
||||||
// Jump to address; set program counter to a value from register $R.
|
// Jump to address if condition cc has been met. Set program counter to
|
||||||
|
// a value from register $R.
|
||||||
// NOTE: Cannot use FallBackToInterpreter(opc) here because of the need to write branch exit
|
// NOTE: Cannot use FallBackToInterpreter(opc) here because of the need to write branch exit
|
||||||
void DSPEmitter::jmprcc(const UDSPInstruction opc)
|
void DSPEmitter::jmprcc(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
|
@ -270,7 +271,7 @@ void DSPEmitter::rti(const UDSPInstruction opc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HALT
|
// HALT
|
||||||
// 0000 0000 0020 0001
|
// 0000 0000 0010 0001
|
||||||
// Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR.
|
// Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR.
|
||||||
void DSPEmitter::halt(const UDSPInstruction)
|
void DSPEmitter::halt(const UDSPInstruction)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
using namespace Gen;
|
using namespace Gen;
|
||||||
|
|
||||||
/* It is safe to directly write to the address registers as they are
|
/* It is safe to directly write to the address registers as they are
|
||||||
neither read not written by any extendable opcode. The same is true
|
neither read nor written by any extendable opcode. The same is true
|
||||||
for memory accesses.
|
for memory accesses.
|
||||||
It probably even is safe to write to all registers except for
|
It probably even is safe to write to all registers except for
|
||||||
SR, ACx.x, AXx.x and PROD, which may be modified by the main op.
|
SR, ACx.x, AXx.x and PROD, which may be modified by the main op.
|
||||||
|
@ -131,7 +131,7 @@ void DSPEmitter::l(const UDSPInstruction opc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LN $axD.D, @$arS
|
// LN $axD.D, @$arS
|
||||||
// xxxx xxxx 01dd d0ss
|
// xxxx xxxx 01dd d1ss
|
||||||
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
|
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
|
||||||
// Add indexing register $ixS to register $arS.
|
// Add indexing register $ixS to register $arS.
|
||||||
void DSPEmitter::ln(const UDSPInstruction opc)
|
void DSPEmitter::ln(const UDSPInstruction opc)
|
||||||
|
@ -354,16 +354,18 @@ void DSPEmitter::slnm(const UDSPInstruction opc)
|
||||||
increase_addr_reg(DSP_REG_AR0, DSP_REG_AR0);
|
increase_addr_reg(DSP_REG_AR0, DSP_REG_AR0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LD $ax0.d, $ax1.r, @$arS
|
// LD $ax0.D, $ax1.R, @$arS
|
||||||
// xxxx xxxx 11dr 00ss
|
// xxxx xxxx 11dr 00ss
|
||||||
// example for "nx'ld $AX0.L, $AX1.L, @$AR3"
|
// Load register $ax0.D (either $ax0.l or $ax0.h) with value from memory pointed by register $arS.
|
||||||
// Loads the word pointed by AR0 to AX0.H, then loads the word pointed by AR3
|
// Load register $ax1.R (either $ax1.l or $ax1.h) with value from memory pointed by register $ar3.
|
||||||
// to AX0.L. Increments AR0 and AR3. If AR0 and AR3 point into the same
|
// Increment both $arS and $ar3.
|
||||||
// memory page (upper 6 bits of addr are the same -> games are not doing that!)
|
// S cannot be 3, as that encodes LDAX. Thus $arS and $ar3 are known to be distinct.
|
||||||
// then the value pointed by AR0 is loaded to BOTH AX0.H and AX0.L. If AR0
|
// If $ar0 and $ar3 point into the same memory page (upper 6 bits of addr are the same -> games are
|
||||||
// points into an invalid memory page (ie 0x2000), then AX0.H keeps its old
|
// not doing that!) then the value pointed by $ar0 is loaded to BOTH $ax0.D and $ax1.R.
|
||||||
// value. (not implemented yet) If AR3 points into an invalid memory page, then
|
// If $ar0 points into an invalid memory page (ie 0x2000), then $ax0.D keeps its old value. (not
|
||||||
// AX0.L gets the same value as AX0.H. (not implemented yet)
|
// implemented yet)
|
||||||
|
// If $ar3 points into an invalid memory page, then $ax1.R gets the same value as $ax0.D. (not
|
||||||
|
// implemented yet)
|
||||||
void DSPEmitter::ld(const UDSPInstruction opc)
|
void DSPEmitter::ld(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
u8 dreg = (opc >> 5) & 0x1;
|
u8 dreg = (opc >> 5) & 0x1;
|
||||||
|
@ -396,6 +398,9 @@ void DSPEmitter::ld(const UDSPInstruction opc)
|
||||||
|
|
||||||
// LDAX $axR, @$arS
|
// LDAX $axR, @$arS
|
||||||
// xxxx xxxx 11sr 0011
|
// xxxx xxxx 11sr 0011
|
||||||
|
// Load register $axR.h with value from memory pointed by register $arS.
|
||||||
|
// Load register $axR.l with value from memory pointed by register $ar3.
|
||||||
|
// Increment both $arS and $ar3.
|
||||||
void DSPEmitter::ldax(const UDSPInstruction opc)
|
void DSPEmitter::ldax(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
u8 sreg = (opc >> 5) & 0x1;
|
u8 sreg = (opc >> 5) & 0x1;
|
||||||
|
@ -425,7 +430,7 @@ void DSPEmitter::ldax(const UDSPInstruction opc)
|
||||||
increment_addr_reg(DSP_REG_AR3);
|
increment_addr_reg(DSP_REG_AR3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDN $ax0.d, $ax1.r, @$arS
|
// LDN $ax0.D, $ax1.R, @$arS
|
||||||
// xxxx xxxx 11dr 01ss
|
// xxxx xxxx 11dr 01ss
|
||||||
void DSPEmitter::ldn(const UDSPInstruction opc)
|
void DSPEmitter::ldn(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
|
@ -488,7 +493,7 @@ void DSPEmitter::ldaxn(const UDSPInstruction opc)
|
||||||
increment_addr_reg(DSP_REG_AR3);
|
increment_addr_reg(DSP_REG_AR3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDM $ax0.d, $ax1.r, @$arS
|
// LDM $ax0.D, $ax1.R, @$arS
|
||||||
// xxxx xxxx 11dr 10ss
|
// xxxx xxxx 11dr 10ss
|
||||||
void DSPEmitter::ldm(const UDSPInstruction opc)
|
void DSPEmitter::ldm(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
|
@ -551,7 +556,7 @@ void DSPEmitter::ldaxm(const UDSPInstruction opc)
|
||||||
increase_addr_reg(DSP_REG_AR3, DSP_REG_AR3);
|
increase_addr_reg(DSP_REG_AR3, DSP_REG_AR3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDNM $ax0.d, $ax1.r, @$arS
|
// LDNM $ax0.D, $ax1.R, @$arS
|
||||||
// xxxx xxxx 11dr 11ss
|
// xxxx xxxx 11dr 11ss
|
||||||
void DSPEmitter::ldnm(const UDSPInstruction opc)
|
void DSPEmitter::ldnm(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,7 +17,6 @@ namespace DSP::JIT::x64
|
||||||
// Move value from register $(0x18+S) to data memory pointed by address
|
// Move value from register $(0x18+S) to data memory pointed by address
|
||||||
// CR[0-7] | M. That is, the upper 8 bits of the address are the
|
// 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.
|
// 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 DSPEmitter::srs(const UDSPInstruction opc)
|
void DSPEmitter::srs(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
u8 reg = ((opc >> 8) & 0x7) + 0x18;
|
u8 reg = ((opc >> 8) & 0x7) + 0x18;
|
||||||
|
@ -105,9 +104,9 @@ void DSPEmitter::si(const UDSPInstruction opc)
|
||||||
m_gpr.PutXReg(tmp1);
|
m_gpr.PutXReg(tmp1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LRR $D, @$S
|
// LRR $D, @$arS
|
||||||
// 0001 1000 0ssd dddd
|
// 0001 1000 0ssd dddd
|
||||||
// Move value from data memory pointed by addressing register $S to register $D.
|
// Move value from data memory pointed by addressing register $arS to register $D.
|
||||||
void DSPEmitter::lrr(const UDSPInstruction opc)
|
void DSPEmitter::lrr(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
u8 sreg = (opc >> 5) & 0x3;
|
u8 sreg = (opc >> 5) & 0x3;
|
||||||
|
@ -124,10 +123,10 @@ void DSPEmitter::lrr(const UDSPInstruction opc)
|
||||||
dsp_conditional_extend_accum(dreg);
|
dsp_conditional_extend_accum(dreg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LRRD $D, @$S
|
// LRRD $D, @$arS
|
||||||
// 0001 1000 1ssd dddd
|
// 0001 1000 1ssd dddd
|
||||||
// Move value from data memory pointed by addressing register $S to register $D.
|
// Move value from data memory pointed by addressing register $arS to register $D.
|
||||||
// Decrement register $S.
|
// Decrement register $arS.
|
||||||
void DSPEmitter::lrrd(const UDSPInstruction opc)
|
void DSPEmitter::lrrd(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
u8 sreg = (opc >> 5) & 0x3;
|
u8 sreg = (opc >> 5) & 0x3;
|
||||||
|
@ -145,10 +144,10 @@ void DSPEmitter::lrrd(const UDSPInstruction opc)
|
||||||
decrement_addr_reg(sreg);
|
decrement_addr_reg(sreg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LRRI $D, @$S
|
// LRRI $D, @$arS
|
||||||
// 0001 1001 0ssd dddd
|
// 0001 1001 0ssd dddd
|
||||||
// Move value from data memory pointed by addressing register $S to register $D.
|
// Move value from data memory pointed by addressing register $arS to register $D.
|
||||||
// Increment register $S.
|
// Increment register $arS.
|
||||||
void DSPEmitter::lrri(const UDSPInstruction opc)
|
void DSPEmitter::lrri(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
u8 sreg = (opc >> 5) & 0x3;
|
u8 sreg = (opc >> 5) & 0x3;
|
||||||
|
@ -166,10 +165,10 @@ void DSPEmitter::lrri(const UDSPInstruction opc)
|
||||||
increment_addr_reg(sreg);
|
increment_addr_reg(sreg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LRRN $D, @$S
|
// LRRN $D, @$arS
|
||||||
// 0001 1001 1ssd dddd
|
// 0001 1001 1ssd dddd
|
||||||
// Move value from data memory pointed by addressing register $S to register $D.
|
// Move value from data memory pointed by addressing register $arS to register $D.
|
||||||
// Add indexing register $(0x4+S) to register $S.
|
// Add indexing register $ixS to register $arS.
|
||||||
void DSPEmitter::lrrn(const UDSPInstruction opc)
|
void DSPEmitter::lrrn(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
u8 sreg = (opc >> 5) & 0x3;
|
u8 sreg = (opc >> 5) & 0x3;
|
||||||
|
@ -187,10 +186,10 @@ void DSPEmitter::lrrn(const UDSPInstruction opc)
|
||||||
increase_addr_reg(sreg, sreg);
|
increase_addr_reg(sreg, sreg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SRR @$D, $S
|
// SRR @$arD, $S
|
||||||
// 0001 1010 0dds ssss
|
// 0001 1010 0dds ssss
|
||||||
// Store value from source register $S to a memory location pointed by
|
// Store value from source register $S to a memory location pointed by
|
||||||
// addressing register $D.
|
// addressing register $arD.
|
||||||
void DSPEmitter::srr(const UDSPInstruction opc)
|
void DSPEmitter::srr(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
u8 dreg = (opc >> 5) & 0x3;
|
u8 dreg = (opc >> 5) & 0x3;
|
||||||
|
@ -205,10 +204,10 @@ void DSPEmitter::srr(const UDSPInstruction opc)
|
||||||
m_gpr.PutXReg(tmp1);
|
m_gpr.PutXReg(tmp1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SRRD @$D, $S
|
// SRRD @$arD, $S
|
||||||
// 0001 1010 1dds ssss
|
// 0001 1010 1dds ssss
|
||||||
// Store value from source register $S to a memory location pointed by
|
// Store value from source register $S to a memory location pointed by
|
||||||
// addressing register $D. Decrement register $D.
|
// addressing register $arD. Decrement register $arD.
|
||||||
void DSPEmitter::srrd(const UDSPInstruction opc)
|
void DSPEmitter::srrd(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
u8 dreg = (opc >> 5) & 0x3;
|
u8 dreg = (opc >> 5) & 0x3;
|
||||||
|
@ -225,10 +224,10 @@ void DSPEmitter::srrd(const UDSPInstruction opc)
|
||||||
decrement_addr_reg(dreg);
|
decrement_addr_reg(dreg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SRRI @$D, $S
|
// SRRI @$arD, $S
|
||||||
// 0001 1011 0dds ssss
|
// 0001 1011 0dds ssss
|
||||||
// Store value from source register $S to a memory location pointed by
|
// Store value from source register $S to a memory location pointed by
|
||||||
// addressing register $D. Increment register $D.
|
// addressing register $arD. Increment register $arD.
|
||||||
void DSPEmitter::srri(const UDSPInstruction opc)
|
void DSPEmitter::srri(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
u8 dreg = (opc >> 5) & 0x3;
|
u8 dreg = (opc >> 5) & 0x3;
|
||||||
|
@ -245,10 +244,10 @@ void DSPEmitter::srri(const UDSPInstruction opc)
|
||||||
increment_addr_reg(dreg);
|
increment_addr_reg(dreg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SRRN @$D, $S
|
// SRRN @$arD, $S
|
||||||
// 0001 1011 1dds ssss
|
// 0001 1011 1dds ssss
|
||||||
// Store value from source register $S to a memory location pointed by
|
// Store value from source register $S to a memory location pointed by
|
||||||
// addressing register $D. Add DSP_REG_IX0 register to register $D.
|
// addressing register $arD. Add corresponding indexing register $ixD to register $arD.
|
||||||
void DSPEmitter::srrn(const UDSPInstruction opc)
|
void DSPEmitter::srrn(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
u8 dreg = (opc >> 5) & 0x3;
|
u8 dreg = (opc >> 5) & 0x3;
|
||||||
|
|
|
@ -130,8 +130,8 @@ void DSPEmitter::clrCompileSR(u16 bit)
|
||||||
}
|
}
|
||||||
// SBCLR #I
|
// SBCLR #I
|
||||||
// 0001 0011 aaaa aiii
|
// 0001 0011 aaaa aiii
|
||||||
// bit of status register $sr. Bit number is calculated by adding 6 to
|
// Clear bit of status register $sr. Bit number is calculated by adding 6 to immediate value I;
|
||||||
// immediate value I.
|
// thus, bits 6 through 13 (LZ through AM) can be cleared with this instruction.
|
||||||
void DSPEmitter::sbclr(const UDSPInstruction opc)
|
void DSPEmitter::sbclr(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
u8 bit = (opc & 0x7) + 6;
|
u8 bit = (opc & 0x7) + 6;
|
||||||
|
@ -141,8 +141,8 @@ void DSPEmitter::sbclr(const UDSPInstruction opc)
|
||||||
|
|
||||||
// SBSET #I
|
// SBSET #I
|
||||||
// 0001 0010 aaaa aiii
|
// 0001 0010 aaaa aiii
|
||||||
// Set bit of status register $sr. Bit number is calculated by adding 6 to
|
// Set bit of status register $sr. Bit number is calculated by adding 6 to immediate value I;
|
||||||
// immediate value I.
|
// thus, bits 6 through 13 (LZ through AM) can be set with this instruction.
|
||||||
void DSPEmitter::sbset(const UDSPInstruction opc)
|
void DSPEmitter::sbset(const UDSPInstruction opc)
|
||||||
{
|
{
|
||||||
u8 bit = (opc & 0x7) + 6;
|
u8 bit = (opc & 0x7) + 6;
|
||||||
|
|
|
@ -416,7 +416,7 @@ void DSPEmitter::mulx(const UDSPInstruction opc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MULXAC $ax0.S, $ax1.T, $acR
|
// MULXAC $ax0.S, $ax1.T, $acR
|
||||||
// 101s t01r xxxx xxxx
|
// 101s t10r xxxx xxxx
|
||||||
// Add product register to accumulator register $acR. Multiply one part
|
// Add product register to accumulator register $acR. Multiply one part
|
||||||
// $ax0 by one part $ax1. Part is selected by S and
|
// $ax0 by one part $ax1. Part is selected by S and
|
||||||
// T bits. Zero selects low part, one selects high part.
|
// T bits. Zero selects low part, one selects high part.
|
||||||
|
@ -487,7 +487,7 @@ void DSPEmitter::mulxmv(const UDSPInstruction opc)
|
||||||
m_gpr.PutXReg(tmp1);
|
m_gpr.PutXReg(tmp1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MULXMV $ax0.S, $ax1.T, $acR
|
// MULXMVZ $ax0.S, $ax1.T, $acR
|
||||||
// 101s t01r xxxx xxxx
|
// 101s t01r xxxx xxxx
|
||||||
// Move product register to accumulator register $acR and clear (round) low part
|
// 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
|
// of accumulator register $acR.l. Multiply one part $ax0 by one part $ax1
|
||||||
|
@ -586,7 +586,6 @@ void DSPEmitter::mulcac(const UDSPInstruction opc)
|
||||||
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
|
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
|
||||||
// secondary accumulator $axT (treat them both as signed). Move product
|
// secondary accumulator $axT (treat them both as signed). Move product
|
||||||
// register before multiplication to accumulator $acR.
|
// register before multiplication to accumulator $acR.
|
||||||
// possible mistake in duddie's doc axT.h rather than axS.h
|
|
||||||
|
|
||||||
// flags out: --xx xx0x
|
// flags out: --xx xx0x
|
||||||
void DSPEmitter::mulcmv(const UDSPInstruction opc)
|
void DSPEmitter::mulcmv(const UDSPInstruction opc)
|
||||||
|
@ -618,7 +617,6 @@ void DSPEmitter::mulcmv(const UDSPInstruction opc)
|
||||||
|
|
||||||
// MULCMVZ $acS.m, $axT.h, $acR
|
// MULCMVZ $acS.m, $axT.h, $acR
|
||||||
// 110s t01r xxxx xxxx
|
// 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
|
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
|
||||||
// secondary accumulator $axT (treat them both as signed). Move product
|
// secondary accumulator $axT (treat them both as signed). Move product
|
||||||
// register before multiplication to accumulator $acR, set (round) low part of
|
// register before multiplication to accumulator $acR, set (round) low part of
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
dsp_code.h
|
||||||
|
build/
|
||||||
|
*.dol
|
||||||
|
*.elf
|
|
@ -1,4 +0,0 @@
|
||||||
// Copyright 2003 Dolphin Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
// Dummy file for common to compile
|
|
|
@ -23,7 +23,7 @@ include $(DEVKITPPC)/$(HW_TYPE)_rules
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
TARGET := $(notdir $(CURDIR))_$(HW_TYPE)
|
TARGET := $(notdir $(CURDIR))_$(HW_TYPE)
|
||||||
BUILD := build
|
BUILD := build
|
||||||
SOURCES := . emu
|
SOURCES := .
|
||||||
DATA := data
|
DATA := data
|
||||||
INCLUDES := include ../Core/Common .
|
INCLUDES := include ../Core/Common .
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
# DSPSpy
|
||||||
|
|
||||||
|
DSPSpy is a homebrew tool for experimenting with the GameCube/Wii DSP. It can also be used to dump the DSP ROMs.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
DSPSpy is built using [devkitPPC](https://wiibrew.org/wiki/DevkitPPC); see the [devkitPro getting started page](https://devkitpro.org/wiki/Getting_Started) for more information. DSPSpy also requires DSPTool to be built.
|
||||||
|
|
||||||
|
First, run DSPTool to generate `dsp_code.h`, for instance from `tests/dsp_test.ds`. The following commands assume an x64 Windows setup running in the DSPSpy directory:
|
||||||
|
|
||||||
|
```
|
||||||
|
../../Binary/x64/DSPTool.exe -h dsp_code tests/dsp_test.ds
|
||||||
|
```
|
||||||
|
|
||||||
|
To use the ROM-dumping code, run this:
|
||||||
|
|
||||||
|
```
|
||||||
|
../../Binary/x64/DSPTool.exe -h dsp_code util/dump_roms.ds
|
||||||
|
```
|
||||||
|
|
||||||
|
DSPTool can also generate a header for multiple DSP programs at the same time. First, create a file (in this example, it was named `file_list.txt`) with the following contents:
|
||||||
|
|
||||||
|
```
|
||||||
|
tests/dsp_test.ds
|
||||||
|
tests/mul_test.ds
|
||||||
|
tests/neg_test.ds
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run:
|
||||||
|
|
||||||
|
```
|
||||||
|
../../Binary/x64/DSPTool.exe -h dsp_code -m file_list.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
After `dsp_code.h` has been generated, simply run `make` to generate `dspspy_wii.dol`, which can be loaded by normal means.
|
||||||
|
|
||||||
|
## Dumping DSP ROMs
|
||||||
|
|
||||||
|
Build DSPSpy with `util/dump_roms.ds`. When launched, DSPSpy will automatically create files `dsp_rom.bin` and `dsp_coef.bin` on the SD card (only SD cards are supported); DSPSpy can be exited immediately afterwards.
|
|
@ -1,6 +0,0 @@
|
||||||
../../Binary/x64/DSPTool.exe -h dsp_code tests/mul_test.ds
|
|
||||||
mkdir emu
|
|
||||||
cp ../Core/Core/DSP/*.cpp emu
|
|
||||||
cp ../Core/Core/DSP/*.h emu
|
|
||||||
make
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
../../Binary/x64/DSPTool.exe -h dsp_code tests/mul_test.ds
|
|
||||||
rm -rf emu
|
|
||||||
make
|
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
incdir "tests"
|
||||||
|
include "dsp_base.inc"
|
||||||
|
|
||||||
|
; Tests the behavior of SI, SRS, and LRS when CR is changed
|
||||||
|
|
||||||
|
; Register that is writable but with contents that doesn't matter (COEF_A1_0)
|
||||||
|
TEST_REG: equ 0xFFA0 ; 0xFF00 (not writable)
|
||||||
|
; This is separate because SRS and SI currently require value 0..7f or ff80..ffff,
|
||||||
|
; though the actual behavior doesn't match that
|
||||||
|
TEST_ADDR: equ 0xFFA0 ; 0x0000
|
||||||
|
; Memory addresses
|
||||||
|
TEST_MEM: equ 0x00A0 ; 0x0000
|
||||||
|
TEST_MEM_2: equ 0x01A0 ; 0x0100
|
||||||
|
|
||||||
|
LRI $AC0.L, #0xf00f
|
||||||
|
SR @TEST_REG, $AC0.L
|
||||||
|
SR @TEST_MEM, $AC0.L
|
||||||
|
SR @TEST_MEM_2, $AC0.L
|
||||||
|
CALL send_regs
|
||||||
|
|
||||||
|
; Observed: writes to TEST_REG
|
||||||
|
SI @TEST_ADDR, #0xf11f
|
||||||
|
CALL send_regs
|
||||||
|
|
||||||
|
LRI $AC0.L, #0xf22f
|
||||||
|
; Observed: writes to TEST_REG
|
||||||
|
SRS @TEST_ADDR, $AC0.L
|
||||||
|
CALL send_regs
|
||||||
|
|
||||||
|
LRI $CR, #0x0000
|
||||||
|
; Observed: still writes to TEST_REG
|
||||||
|
SI @TEST_ADDR, #0xf33f
|
||||||
|
CALL send_regs
|
||||||
|
|
||||||
|
LRI $AC0.L, #0xf44f
|
||||||
|
; Observed: writes to TEST_MEM
|
||||||
|
SRS @TEST_ADDR, $AC0.L
|
||||||
|
CALL send_regs
|
||||||
|
|
||||||
|
LRI $CR, #0x0001
|
||||||
|
; Observed: still writes to TEST_REG
|
||||||
|
SI @TEST_ADDR, #0xf55f
|
||||||
|
CALL send_regs
|
||||||
|
|
||||||
|
LRI $AC0.L, #0xf66f
|
||||||
|
; Observed: writes to TEST_MEM_2
|
||||||
|
SRS @TEST_ADDR, $AC0.L
|
||||||
|
CALL send_regs
|
||||||
|
|
||||||
|
; At this point, TEST_REG should be f55f, TEST_MEM should be f44f,
|
||||||
|
; and TEST_MEM_2 should be f66f. Test the behavior of LRS.
|
||||||
|
; Changes to prod.l are for display only.
|
||||||
|
LRI $CR, #0x00ff
|
||||||
|
LRI $prod.l, #0xf55f
|
||||||
|
LRS $AC0.L, @TEST_ADDR
|
||||||
|
CALL send_regs
|
||||||
|
|
||||||
|
LRI $CR, #0x0000
|
||||||
|
LRI $prod.l, #0xf44f
|
||||||
|
LRS $AC0.L, @TEST_ADDR
|
||||||
|
CALL send_regs
|
||||||
|
|
||||||
|
LRI $CR, #0x0001
|
||||||
|
LRI $prod.l, #0xf66f
|
||||||
|
LRS $AC0.L, @TEST_ADDR
|
||||||
|
CALL send_regs
|
||||||
|
|
||||||
|
; We're done, DO NOT DELETE THIS LINE
|
||||||
|
JMP end_of_test
|
||||||
|
|
||||||
|
send_regs:
|
||||||
|
; For display purposes only
|
||||||
|
LRI $prod.m1, #TEST_REG
|
||||||
|
LRI $prod.h, #TEST_MEM
|
||||||
|
LRI $prod.m2, #TEST_MEM_2
|
||||||
|
; Actual registers
|
||||||
|
LR $AC1.L, @TEST_REG
|
||||||
|
LR $AC0.M, @TEST_MEM
|
||||||
|
LR $AC1.M, @TEST_MEM_2
|
||||||
|
CALL send_back
|
||||||
|
RET
|
|
@ -0,0 +1,290 @@
|
||||||
|
GameCube_DSP_Users_Manual.pdf
|
||||||
|
|
||||||
|
## Core latex/pdflatex auxiliary files:
|
||||||
|
*.aux
|
||||||
|
*.lof
|
||||||
|
*.log
|
||||||
|
*.lot
|
||||||
|
*.fls
|
||||||
|
*.out
|
||||||
|
*.toc
|
||||||
|
*.fmt
|
||||||
|
*.fot
|
||||||
|
*.cb
|
||||||
|
*.cb2
|
||||||
|
.*.lb
|
||||||
|
|
||||||
|
## Intermediate documents:
|
||||||
|
*.dvi
|
||||||
|
*.xdv
|
||||||
|
*-converted-to.*
|
||||||
|
# these rules might exclude image files for figures etc.
|
||||||
|
# *.ps
|
||||||
|
# *.eps
|
||||||
|
# *.pdf
|
||||||
|
|
||||||
|
## Generated if empty string is given at "Please type another file name for output:"
|
||||||
|
.pdf
|
||||||
|
|
||||||
|
## Bibliography auxiliary files (bibtex/biblatex/biber):
|
||||||
|
*.bbl
|
||||||
|
*.bcf
|
||||||
|
*.blg
|
||||||
|
*-blx.aux
|
||||||
|
*-blx.bib
|
||||||
|
*.run.xml
|
||||||
|
|
||||||
|
## Build tool auxiliary files:
|
||||||
|
*.fdb_latexmk
|
||||||
|
*.synctex
|
||||||
|
*.synctex(busy)
|
||||||
|
*.synctex.gz
|
||||||
|
*.synctex.gz(busy)
|
||||||
|
*.pdfsync
|
||||||
|
|
||||||
|
## Build tool directories for auxiliary files
|
||||||
|
# latexrun
|
||||||
|
latex.out/
|
||||||
|
|
||||||
|
## Auxiliary and intermediate files from other packages:
|
||||||
|
# algorithms
|
||||||
|
*.alg
|
||||||
|
*.loa
|
||||||
|
|
||||||
|
# achemso
|
||||||
|
acs-*.bib
|
||||||
|
|
||||||
|
# amsthm
|
||||||
|
*.thm
|
||||||
|
|
||||||
|
# beamer
|
||||||
|
*.nav
|
||||||
|
*.pre
|
||||||
|
*.snm
|
||||||
|
*.vrb
|
||||||
|
|
||||||
|
# changes
|
||||||
|
*.soc
|
||||||
|
|
||||||
|
# comment
|
||||||
|
*.cut
|
||||||
|
|
||||||
|
# cprotect
|
||||||
|
*.cpt
|
||||||
|
|
||||||
|
# elsarticle (documentclass of Elsevier journals)
|
||||||
|
*.spl
|
||||||
|
|
||||||
|
# endnotes
|
||||||
|
*.ent
|
||||||
|
|
||||||
|
# fixme
|
||||||
|
*.lox
|
||||||
|
|
||||||
|
# feynmf/feynmp
|
||||||
|
*.mf
|
||||||
|
*.mp
|
||||||
|
*.t[1-9]
|
||||||
|
*.t[1-9][0-9]
|
||||||
|
*.tfm
|
||||||
|
|
||||||
|
#(r)(e)ledmac/(r)(e)ledpar
|
||||||
|
*.end
|
||||||
|
*.?end
|
||||||
|
*.[1-9]
|
||||||
|
*.[1-9][0-9]
|
||||||
|
*.[1-9][0-9][0-9]
|
||||||
|
*.[1-9]R
|
||||||
|
*.[1-9][0-9]R
|
||||||
|
*.[1-9][0-9][0-9]R
|
||||||
|
*.eledsec[1-9]
|
||||||
|
*.eledsec[1-9]R
|
||||||
|
*.eledsec[1-9][0-9]
|
||||||
|
*.eledsec[1-9][0-9]R
|
||||||
|
*.eledsec[1-9][0-9][0-9]
|
||||||
|
*.eledsec[1-9][0-9][0-9]R
|
||||||
|
|
||||||
|
# glossaries
|
||||||
|
*.acn
|
||||||
|
*.acr
|
||||||
|
*.glg
|
||||||
|
*.glo
|
||||||
|
*.gls
|
||||||
|
*.glsdefs
|
||||||
|
*.lzo
|
||||||
|
*.lzs
|
||||||
|
|
||||||
|
# uncomment this for glossaries-extra (will ignore makeindex's style files!)
|
||||||
|
# *.ist
|
||||||
|
|
||||||
|
# gnuplottex
|
||||||
|
*-gnuplottex-*
|
||||||
|
|
||||||
|
# gregoriotex
|
||||||
|
*.gaux
|
||||||
|
*.glog
|
||||||
|
*.gtex
|
||||||
|
|
||||||
|
# htlatex
|
||||||
|
*.4ct
|
||||||
|
*.4tc
|
||||||
|
*.idv
|
||||||
|
*.lg
|
||||||
|
*.trc
|
||||||
|
*.xref
|
||||||
|
|
||||||
|
# hyperref
|
||||||
|
*.brf
|
||||||
|
|
||||||
|
# knitr
|
||||||
|
*-concordance.tex
|
||||||
|
# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files
|
||||||
|
# *.tikz
|
||||||
|
*-tikzDictionary
|
||||||
|
|
||||||
|
# listings
|
||||||
|
*.lol
|
||||||
|
|
||||||
|
# luatexja-ruby
|
||||||
|
*.ltjruby
|
||||||
|
|
||||||
|
# makeidx
|
||||||
|
*.idx
|
||||||
|
*.ilg
|
||||||
|
*.ind
|
||||||
|
|
||||||
|
# minitoc
|
||||||
|
*.maf
|
||||||
|
*.mlf
|
||||||
|
*.mlt
|
||||||
|
*.mtc[0-9]*
|
||||||
|
*.slf[0-9]*
|
||||||
|
*.slt[0-9]*
|
||||||
|
*.stc[0-9]*
|
||||||
|
|
||||||
|
# minted
|
||||||
|
_minted*
|
||||||
|
*.pyg
|
||||||
|
|
||||||
|
# morewrites
|
||||||
|
*.mw
|
||||||
|
|
||||||
|
# newpax
|
||||||
|
*.newpax
|
||||||
|
|
||||||
|
# nomencl
|
||||||
|
*.nlg
|
||||||
|
*.nlo
|
||||||
|
*.nls
|
||||||
|
|
||||||
|
# pax
|
||||||
|
*.pax
|
||||||
|
|
||||||
|
# pdfpcnotes
|
||||||
|
*.pdfpc
|
||||||
|
|
||||||
|
# sagetex
|
||||||
|
*.sagetex.sage
|
||||||
|
*.sagetex.py
|
||||||
|
*.sagetex.scmd
|
||||||
|
|
||||||
|
# scrwfile
|
||||||
|
*.wrt
|
||||||
|
|
||||||
|
# sympy
|
||||||
|
*.sout
|
||||||
|
*.sympy
|
||||||
|
sympy-plots-for-*.tex/
|
||||||
|
|
||||||
|
# pdfcomment
|
||||||
|
*.upa
|
||||||
|
*.upb
|
||||||
|
|
||||||
|
# pythontex
|
||||||
|
*.pytxcode
|
||||||
|
pythontex-files-*/
|
||||||
|
|
||||||
|
# tcolorbox
|
||||||
|
*.listing
|
||||||
|
|
||||||
|
# thmtools
|
||||||
|
*.loe
|
||||||
|
|
||||||
|
# TikZ & PGF
|
||||||
|
*.dpth
|
||||||
|
*.md5
|
||||||
|
*.auxlock
|
||||||
|
|
||||||
|
# todonotes
|
||||||
|
*.tdo
|
||||||
|
|
||||||
|
# vhistory
|
||||||
|
*.hst
|
||||||
|
*.ver
|
||||||
|
|
||||||
|
# easy-todo
|
||||||
|
*.lod
|
||||||
|
|
||||||
|
# xcolor
|
||||||
|
*.xcp
|
||||||
|
|
||||||
|
# xmpincl
|
||||||
|
*.xmpi
|
||||||
|
|
||||||
|
# xindy
|
||||||
|
*.xdy
|
||||||
|
|
||||||
|
# xypic precompiled matrices and outlines
|
||||||
|
*.xyc
|
||||||
|
*.xyd
|
||||||
|
|
||||||
|
# endfloat
|
||||||
|
*.ttt
|
||||||
|
*.fff
|
||||||
|
|
||||||
|
# Latexian
|
||||||
|
TSWLatexianTemp*
|
||||||
|
|
||||||
|
## Editors:
|
||||||
|
# WinEdt
|
||||||
|
*.bak
|
||||||
|
*.sav
|
||||||
|
|
||||||
|
# Texpad
|
||||||
|
.texpadtmp
|
||||||
|
|
||||||
|
# LyX
|
||||||
|
*.lyx~
|
||||||
|
|
||||||
|
# Kile
|
||||||
|
*.backup
|
||||||
|
|
||||||
|
# gummi
|
||||||
|
.*.swp
|
||||||
|
|
||||||
|
# KBibTeX
|
||||||
|
*~[0-9]*
|
||||||
|
|
||||||
|
# TeXnicCenter
|
||||||
|
*.tps
|
||||||
|
|
||||||
|
# auto folder when using emacs and auctex
|
||||||
|
./auto/*
|
||||||
|
*.el
|
||||||
|
|
||||||
|
# expex forward references with \gathertags
|
||||||
|
*-tags.tex
|
||||||
|
|
||||||
|
# standalone packages
|
||||||
|
*.sta
|
||||||
|
|
||||||
|
# Makeindex log files
|
||||||
|
*.lpz
|
||||||
|
|
||||||
|
# xwatermark package
|
||||||
|
*.xwm
|
||||||
|
|
||||||
|
# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib
|
||||||
|
# option is specified. Footnotes are the stored in a file with suffix Notes.bib.
|
||||||
|
# Uncomment the next line to have this generated file ignored.
|
||||||
|
#*Notes.bib
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue