Merge pull request #10048 from Pokechu22/dsp-manual

Improvements to DSP manual
This commit is contained in:
Tilka 2021-08-22 04:06:49 +01:00 committed by GitHub
commit e7a30dd468
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 2222 additions and 445 deletions

View File

@ -47,7 +47,7 @@ void Interpreter::clrl(const UDSPInstruction opc)
// 0000 001r 1100 0000
// iiii iiii iiii iiii
// 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-- ----
void Interpreter::andcf(const UDSPInstruction opc)
@ -64,7 +64,7 @@ void Interpreter::andcf(const UDSPInstruction opc)
// iiii iiii iiii iiii
// 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
// immediate value 0.
// to immediate value 0.
//
// flags out: -x-- ----
void Interpreter::andf(const UDSPInstruction opc)
@ -126,8 +126,7 @@ void Interpreter::cmp(const UDSPInstruction)
// CMPAR $acS axR.h
// 110r s001 xxxx xxxx
// Compares accumulator $acS with accumulator axR.h.
// Not described by Duddie's doc - at least not as a separate instruction.
// Compares accumulator $acS with accumulator $axR.h.
//
// flags out: x-xx xxxx
void Interpreter::cmpar(const UDSPInstruction opc)
@ -794,7 +793,7 @@ void Interpreter::movr(const UDSPInstruction opc)
// MOVAX $acD, $axS
// 0110 10sd xxxx xxxx
// Moves secondary accumulator $axS to accumulator $axD.
// Moves secondary accumulator $axS to accumulator $acD.
//
// flags out: --xx xx00
void Interpreter::movax(const UDSPInstruction opc)
@ -812,7 +811,7 @@ void Interpreter::movax(const UDSPInstruction opc)
// MOV $acD, $ac(1-D)
// 0110 110d xxxx xxxx
// Moves accumulator $ax(1-D) to accumulator $axD.
// Moves accumulator $ac(1-D) to accumulator $acD.
//
// flags out: --x0 xx00
void Interpreter::mov(const UDSPInstruction opc)

View File

@ -76,9 +76,10 @@ void Interpreter::jcc(const UDSPInstruction opc)
}
// Generic jmpr implementation
// JMPcc $R
// JRcc $R
// 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)
{
if (!CheckCondition(opc & 0xf))
@ -116,7 +117,7 @@ void Interpreter::rti(const UDSPInstruction)
}
// HALT
// 0000 0000 0020 0001
// 0000 0000 0010 0001
// Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR.
void Interpreter::halt(const UDSPInstruction)
{

View File

@ -144,7 +144,7 @@ void Interpreter::l(const UDSPInstruction opc)
}
// 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.
// Add indexing register $ixS to register $arS.
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])));
}
// LD $ax0.d, $ax1.r, @$arS
// 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
// Load register $ax0.D (either $ax0.l or $ax0.h) with value from memory pointed by register $arS.
// Load register $ax1.R (either $ax1.l or $ax1.h) with value from memory pointed by register $ar3.
// Increment both $arS and $ar3.
// S cannot be 3, as that encodes LDAX. Thus $arS and $ar3 are known to be distinct.
// 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.D and $ax1.R.
// If $ar0 points into an invalid memory page (ie 0x2000), then $ax0.D 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
// If $ar3 points into an invalid memory page, then $ax1.R gets the same value as $ax0.D. (not
// implemented yet)
void Interpreter::ld(const UDSPInstruction opc)
{
@ -360,6 +360,9 @@ void Interpreter::ld(const UDSPInstruction opc)
// LDAX $axR, @$arS
// 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)
{
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));
}
// LDN $ax0.d, $ax1.r, @$arS
// LDN $ax0.D, $ax1.R, @$arS
// xxxx xxxx 11dr 01ss
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));
}
// LDM $ax0.d, $ax1.r, @$arS
// LDM $ax0.D, $ax1.R, @$arS
// xxxx xxxx 11dr 10ss
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])));
}
// LDNM $ax0.d, $ax1.r, @$arS
// LDNM $ax0.D, $ax1.R, @$arS
// xxxx xxxx 11dr 11ss
void Interpreter::ldnm(const UDSPInstruction opc)
{

View File

@ -13,7 +13,6 @@ namespace DSP::Interpreter
// 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
// 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)
{
auto& state = m_dsp_core.DSPState();
@ -86,9 +85,9 @@ void Interpreter::si(const UDSPInstruction opc)
state.WriteDMEM(addr, imm);
}
// LRR $D, @$S
// LRR $D, @$arS
// 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)
{
const u8 sreg = (opc >> 5) & 0x3;
@ -100,10 +99,10 @@ void Interpreter::lrr(const UDSPInstruction opc)
ConditionalExtendAccum(dreg);
}
// LRRD $D, @$S
// LRRD $D, @$arS
// 0001 1000 1ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Decrement register $S.
// Move value from data memory pointed by addressing register $arS to register $D.
// Decrement register $arS.
void Interpreter::lrrd(const UDSPInstruction opc)
{
const u8 sreg = (opc >> 5) & 0x3;
@ -116,10 +115,10 @@ void Interpreter::lrrd(const UDSPInstruction opc)
state.r.ar[sreg] = DecrementAddressRegister(sreg);
}
// LRRI $D, @$S
// LRRI $D, @$arS
// 0001 1001 0ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Increment register $S.
// Move value from data memory pointed by addressing register $arS to register $D.
// Increment register $arS.
void Interpreter::lrri(const UDSPInstruction opc)
{
const u8 sreg = (opc >> 5) & 0x3;
@ -132,10 +131,10 @@ void Interpreter::lrri(const UDSPInstruction opc)
state.r.ar[sreg] = IncrementAddressRegister(sreg);
}
// LRRN $D, @$S
// LRRN $D, @$arS
// 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.
// Move value from data memory pointed by addressing register $arS to register $D.
// Add corresponding indexing register $ixS to register $arS.
void Interpreter::lrrn(const UDSPInstruction opc)
{
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]));
}
// SRR @$D, $S
// SRR @$arD, $S
// 0001 1010 0dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D.
// addressing register $arD.
void Interpreter::srr(const UDSPInstruction opc)
{
const u8 dreg = (opc >> 5) & 0x3;
@ -164,10 +163,10 @@ void Interpreter::srr(const UDSPInstruction opc)
state.WriteDMEM(state.r.ar[dreg], OpReadRegister(sreg));
}
// SRRD @$D, $S
// SRRD @$arD, $S
// 0001 1010 1dds ssss
// 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)
{
const u8 dreg = (opc >> 5) & 0x3;
@ -182,10 +181,10 @@ void Interpreter::srrd(const UDSPInstruction opc)
state.r.ar[dreg] = DecrementAddressRegister(dreg);
}
// SRRI @$D, $S
// SRRI @$arD, $S
// 0001 1011 0dds ssss
// 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)
{
const u8 dreg = (opc >> 5) & 0x3;
@ -200,10 +199,10 @@ void Interpreter::srri(const UDSPInstruction opc)
state.r.ar[dreg] = IncrementAddressRegister(dreg);
}
// SRRN @$D, $S
// SRRN @$arD, $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.
// addressing register $arD. Add corresponding indexing register $ixD to register $arD.
void Interpreter::srrn(const UDSPInstruction opc)
{
const u8 dreg = (opc >> 5) & 0x3;

View File

@ -122,8 +122,8 @@ void Interpreter::addarn(const UDSPInstruction opc)
// SBCLR #I
// 0001 0010 aaaa aiii
// bit of status register $sr. Bit number is calculated by adding 6 to
// immediate value I.
// Clear bit of status register $sr. Bit number is calculated by adding 6 to immediate value I;
// thus, bits 6 through 13 (LZ through AM) can be cleared with this instruction.
void Interpreter::sbclr(const UDSPInstruction opc)
{
auto& state = m_dsp_core.DSPState();
@ -134,8 +134,8 @@ void Interpreter::sbclr(const UDSPInstruction opc)
// SBSET #I
// 0001 0011 aaaa aiii
// Set bit of status register $sr. Bit number is calculated by adding 6 to
// immediate value I.
// Set bit of status register $sr. Bit number is calculated by adding 6 to immediate value I;
// thus, bits 6 through 13 (LZ through AM) can be set with this instruction.
void Interpreter::sbset(const UDSPInstruction opc)
{
auto& state = m_dsp_core.DSPState();

View File

@ -247,7 +247,7 @@ void Interpreter::mulx(const UDSPInstruction opc)
}
// MULXAC $ax0.S, $ax1.T, $acR
// 101s t01r xxxx xxxx
// 101s t10r 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.
@ -343,7 +343,7 @@ void Interpreter::mulc(const UDSPInstruction opc)
}
// MULCAC $acS.m, $axT.h, $acR
// 110s t10r xxxx xxxx
// 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.
@ -372,7 +372,6 @@ void Interpreter::mulcac(const UDSPInstruction opc)
// 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 Interpreter::mulcmv(const UDSPInstruction opc)
@ -394,8 +393,7 @@ void Interpreter::mulcmv(const UDSPInstruction opc)
}
// MULCMVZ $acS.m, $axT.h, $acR
// 110s t01r xxxx xxxx
// (fixed possible bug in duddie's description, s->t)
// 110s t01r 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, set (round) low part of

View File

@ -56,7 +56,7 @@ void DSPEmitter::clrl(const UDSPInstruction opc)
// 0000 001r 1100 0000
// iiii iiii iiii iiii
// 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-- ----
void DSPEmitter::andcf(const UDSPInstruction opc)
@ -91,7 +91,7 @@ void DSPEmitter::andcf(const UDSPInstruction opc)
// iiii iiii iiii iiii
// 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
// immediate value 0.
// to immediate value 0.
//
// flags out: -x-- ----
void DSPEmitter::andf(const UDSPInstruction opc)
@ -185,8 +185,7 @@ void DSPEmitter::cmp(const UDSPInstruction opc)
// CMPAR $acS axR.h
// 110r s001 xxxx xxxx
// Compares accumulator $acS with accumulator axR.h.
// Not described by Duddie's doc - at least not as a separate instruction.
// Compares accumulator $acS with accumulator $axR.h.
//
// flags out: x-xx xxxx
void DSPEmitter::cmpar(const UDSPInstruction opc)
@ -1090,7 +1089,7 @@ void DSPEmitter::movr(const UDSPInstruction opc)
// MOVAX $acD, $axS
// 0110 10sd xxxx xxxx
// Moves secondary accumulator $axS to accumulator $axD.
// Moves secondary accumulator $axS to accumulator $acD.
//
// flags out: --xx xx00
void DSPEmitter::movax(const UDSPInstruction opc)
@ -1111,7 +1110,7 @@ void DSPEmitter::movax(const UDSPInstruction opc)
// MOV $acD, $ac(1-D)
// 0110 110d xxxx xxxx
// Moves accumulator $ax(1-D) to accumulator $axD.
// Moves accumulator $ac(1-D) to accumulator $acD.
//
// flags out: --x0 xx00
void DSPEmitter::mov(const UDSPInstruction opc)

View File

@ -156,9 +156,10 @@ void DSPEmitter::r_jmprcc(const UDSPInstruction opc)
WriteBranchExit();
}
// Generic jmpr implementation
// JMPcc $R
// JRcc $R
// 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
void DSPEmitter::jmprcc(const UDSPInstruction opc)
{
@ -270,7 +271,7 @@ void DSPEmitter::rti(const UDSPInstruction opc)
}
// HALT
// 0000 0000 0020 0001
// 0000 0000 0010 0001
// Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR.
void DSPEmitter::halt(const UDSPInstruction)
{

View File

@ -9,7 +9,7 @@
using namespace Gen;
/* 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.
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.
@ -131,7 +131,7 @@ void DSPEmitter::l(const UDSPInstruction opc)
}
// 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.
// Add indexing register $ixS to register $arS.
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);
}
// LD $ax0.d, $ax1.r, @$arS
// 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)
// Load register $ax0.D (either $ax0.l or $ax0.h) with value from memory pointed by register $arS.
// Load register $ax1.R (either $ax1.l or $ax1.h) with value from memory pointed by register $ar3.
// Increment both $arS and $ar3.
// S cannot be 3, as that encodes LDAX. Thus $arS and $ar3 are known to be distinct.
// 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.D and $ax1.R.
// If $ar0 points into an invalid memory page (ie 0x2000), then $ax0.D keeps its old value. (not
// 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)
{
u8 dreg = (opc >> 5) & 0x1;
@ -396,6 +398,9 @@ void DSPEmitter::ld(const UDSPInstruction opc)
// LDAX $axR, @$arS
// 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)
{
u8 sreg = (opc >> 5) & 0x1;
@ -425,7 +430,7 @@ void DSPEmitter::ldax(const UDSPInstruction opc)
increment_addr_reg(DSP_REG_AR3);
}
// LDN $ax0.d, $ax1.r, @$arS
// LDN $ax0.D, $ax1.R, @$arS
// xxxx xxxx 11dr 01ss
void DSPEmitter::ldn(const UDSPInstruction opc)
{
@ -488,7 +493,7 @@ void DSPEmitter::ldaxn(const UDSPInstruction opc)
increment_addr_reg(DSP_REG_AR3);
}
// LDM $ax0.d, $ax1.r, @$arS
// LDM $ax0.D, $ax1.R, @$arS
// xxxx xxxx 11dr 10ss
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);
}
// LDNM $ax0.d, $ax1.r, @$arS
// LDNM $ax0.D, $ax1.R, @$arS
// xxxx xxxx 11dr 11ss
void DSPEmitter::ldnm(const UDSPInstruction opc)
{

View File

@ -17,7 +17,6 @@ namespace DSP::JIT::x64
// 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
// 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)
{
u8 reg = ((opc >> 8) & 0x7) + 0x18;
@ -105,9 +104,9 @@ void DSPEmitter::si(const UDSPInstruction opc)
m_gpr.PutXReg(tmp1);
}
// LRR $D, @$S
// LRR $D, @$arS
// 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)
{
u8 sreg = (opc >> 5) & 0x3;
@ -124,10 +123,10 @@ void DSPEmitter::lrr(const UDSPInstruction opc)
dsp_conditional_extend_accum(dreg);
}
// LRRD $D, @$S
// LRRD $D, @$arS
// 0001 1000 1ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Decrement register $S.
// Move value from data memory pointed by addressing register $arS to register $D.
// Decrement register $arS.
void DSPEmitter::lrrd(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
@ -145,10 +144,10 @@ void DSPEmitter::lrrd(const UDSPInstruction opc)
decrement_addr_reg(sreg);
}
// LRRI $D, @$S
// LRRI $D, @$arS
// 0001 1001 0ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Increment register $S.
// Move value from data memory pointed by addressing register $arS to register $D.
// Increment register $arS.
void DSPEmitter::lrri(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
@ -166,10 +165,10 @@ void DSPEmitter::lrri(const UDSPInstruction opc)
increment_addr_reg(sreg);
}
// LRRN $D, @$S
// LRRN $D, @$arS
// 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.
// Move value from data memory pointed by addressing register $arS to register $D.
// Add indexing register $ixS to register $arS.
void DSPEmitter::lrrn(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
@ -187,10 +186,10 @@ void DSPEmitter::lrrn(const UDSPInstruction opc)
increase_addr_reg(sreg, sreg);
}
// SRR @$D, $S
// SRR @$arD, $S
// 0001 1010 0dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D.
// addressing register $arD.
void DSPEmitter::srr(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
@ -205,10 +204,10 @@ void DSPEmitter::srr(const UDSPInstruction opc)
m_gpr.PutXReg(tmp1);
}
// SRRD @$D, $S
// SRRD @$arD, $S
// 0001 1010 1dds ssss
// 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)
{
u8 dreg = (opc >> 5) & 0x3;
@ -225,10 +224,10 @@ void DSPEmitter::srrd(const UDSPInstruction opc)
decrement_addr_reg(dreg);
}
// SRRI @$D, $S
// SRRI @$arD, $S
// 0001 1011 0dds ssss
// 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)
{
u8 dreg = (opc >> 5) & 0x3;
@ -245,10 +244,10 @@ void DSPEmitter::srri(const UDSPInstruction opc)
increment_addr_reg(dreg);
}
// SRRN @$D, $S
// SRRN @$arD, $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.
// addressing register $arD. Add corresponding indexing register $ixD to register $arD.
void DSPEmitter::srrn(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;

View File

@ -130,8 +130,8 @@ void DSPEmitter::clrCompileSR(u16 bit)
}
// SBCLR #I
// 0001 0011 aaaa aiii
// bit of status register $sr. Bit number is calculated by adding 6 to
// immediate value I.
// Clear bit of status register $sr. Bit number is calculated by adding 6 to immediate value I;
// thus, bits 6 through 13 (LZ through AM) can be cleared with this instruction.
void DSPEmitter::sbclr(const UDSPInstruction opc)
{
u8 bit = (opc & 0x7) + 6;
@ -141,8 +141,8 @@ void DSPEmitter::sbclr(const UDSPInstruction opc)
// SBSET #I
// 0001 0010 aaaa aiii
// Set bit of status register $sr. Bit number is calculated by adding 6 to
// immediate value I.
// Set bit of status register $sr. Bit number is calculated by adding 6 to immediate value I;
// thus, bits 6 through 13 (LZ through AM) can be set with this instruction.
void DSPEmitter::sbset(const UDSPInstruction opc)
{
u8 bit = (opc & 0x7) + 6;

View File

@ -416,7 +416,7 @@ void DSPEmitter::mulx(const UDSPInstruction opc)
}
// MULXAC $ax0.S, $ax1.T, $acR
// 101s t01r xxxx xxxx
// 101s t10r 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.
@ -487,7 +487,7 @@ void DSPEmitter::mulxmv(const UDSPInstruction opc)
m_gpr.PutXReg(tmp1);
}
// MULXMV $ax0.S, $ax1.T, $acR
// 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
@ -545,7 +545,7 @@ void DSPEmitter::mulc(const UDSPInstruction opc)
}
// MULCAC $acS.m, $axT.h, $acR
// 110s t10r xxxx xxxx
// 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.
@ -586,7 +586,6 @@ void DSPEmitter::mulcac(const UDSPInstruction opc)
// 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 DSPEmitter::mulcmv(const UDSPInstruction opc)
@ -617,8 +616,7 @@ void DSPEmitter::mulcmv(const UDSPInstruction opc)
}
// MULCMVZ $acS.m, $axT.h, $acR
// 110s t01r xxxx xxxx
// (fixed possible bug in duddie's description, s->t)
// 110s t01r 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, set (round) low part of

4
Source/DSPSpy/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
dsp_code.h
build/
*.dol
*.elf

View File

@ -1,4 +0,0 @@
// Copyright 2003 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Dummy file for common to compile

View File

@ -23,7 +23,7 @@ include $(DEVKITPPC)/$(HW_TYPE)_rules
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))_$(HW_TYPE)
BUILD := build
SOURCES := . emu
SOURCES := .
DATA := data
INCLUDES := include ../Core/Common .

39
Source/DSPSpy/README.md Normal file
View File

@ -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.

View File

@ -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

View File

@ -1,4 +0,0 @@
../../Binary/x64/DSPTool.exe -h dsp_code tests/mul_test.ds
rm -rf emu
make

View File

@ -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

View File

@ -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