Merge pull request #10038 from Pokechu22/dsp-rti-srs

DSPLLE: Split SRS into SRS and SRSH and implement conditional variants of RTI
This commit is contained in:
Tilka 2021-08-23 01:02:45 +01:00 committed by GitHub
commit 67dce9f35d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 726 additions and 418 deletions

Binary file not shown.

View File

@ -33,25 +33,28 @@ static bool VerifyRoms(const SDSP& dsp)
u32 hash_drom; // dsp_coef.bin
};
static const std::array<DspRomHashes, 6> known_roms = {{
static const std::array<DspRomHashes, 7> known_roms = {{
// Official Nintendo ROM
{0x66f334fe, 0xf3b93527},
// LM1234 replacement ROM (Zelda UCode only)
// v0.1: LM1234 replacement ROM (Zelda UCode only)
{0x9c8f593c, 0x10000001},
// delroth's improvement on LM1234 replacement ROM (Zelda and AX only,
// v0.2: delroth's improvement on LM1234 replacement ROM (Zelda and AX only,
// IPL/Card/GBA still broken)
{0xd9907f71, 0xb019c2fb},
// above with improved resampling coefficients
// v0.2.1: above with improved resampling coefficients
{0xd9907f71, 0xdb6880c1},
// above with support for GBA ucode
// v0.3: above with support for GBA ucode
{0x3aa4a793, 0xa4a575f5},
// above with fix to skip bootucode_ax when running from ROM entrypoint
// v0.3.1: above with fix to skip bootucode_ax when running from ROM entrypoint
{0x128ea7a2, 0xa4a575f5},
// v0.4: above with fixes for invalid use of SRS instruction
{0xe789b5a5, 0xa4a575f5},
}};
const u32 hash_irom =
@ -69,28 +72,30 @@ static bool VerifyRoms(const SDSP& dsp)
if (rom_idx < 0)
{
if (AskYesNoFmtT("Your DSP ROMs have incorrect hashes.\n"
if (AskYesNoFmtT("Your DSP ROMs have incorrect hashes.\n\n"
"Delete the dsp_rom.bin and dsp_coef.bin files in the GC folder in the Global "
"User Directory to use the free DSP ROM, or replace them with good dumps from "
"a real GameCube/Wii.\n\n"
"Would you like to stop now to fix the problem?\n"
"If you select \"No\", audio might be garbled."))
{
return false;
}
}
if (rom_idx == 1)
if (rom_idx >= 1 && rom_idx <= 5)
{
Host::OSD_AddMessage("You are using an old free DSP ROM made by the Dolphin Team.", 6000);
Host::OSD_AddMessage("Only games using the Zelda UCode will work correctly.", 6000);
}
else if (rom_idx == 2 || rom_idx == 3)
{
Host::OSD_AddMessage("You are using a free DSP ROM made by the Dolphin Team.", 8000);
Host::OSD_AddMessage("All Wii games will work correctly, and most GameCube games", 8000);
Host::OSD_AddMessage("should also work fine, but the GBA/CARD UCodes will not work.", 8000);
}
else if (rom_idx == 4)
{
Host::OSD_AddMessage("You are using a free DSP ROM made by the Dolphin Team.", 8000);
Host::OSD_AddMessage("All Wii games will work correctly, and most GameCube games", 8000);
Host::OSD_AddMessage("should also work fine, but the CARD UCode will not work.", 8000);
if (AskYesNoFmtT(
"You are using an old free DSP ROM made by the Dolphin Team.\n"
"Due to emulation accuracy improvements, this ROM no longer works correctly.\n\n"
"Delete the dsp_rom.bin and dsp_coef.bin files in the GC folder in the Global "
"User Directory to use the most recent free DSP ROM, or replace them with "
"good dumps from a real GameCube/Wii.\n\n"
"Would you like to stop now to fix the problem?\n"
"If you select \"No\", audio might be garbled."))
{
return false;
}
}
return true;

View File

@ -18,7 +18,7 @@
namespace DSP
{
// clang-format off
const std::array<DSPOPCTemplate, 214> s_opcodes =
const std::array<DSPOPCTemplate, 230> s_opcodes =
{{
// # of parameters----+ {type, size, loc, lshift, mask} branch reads PC // instruction approximation
// name opcode mask size-V V param 1 param 2 param 3 extendable uncond. updates SR
@ -48,7 +48,22 @@ const std::array<DSPOPCTemplate, 214> s_opcodes =
{"RETO", 0x02de, 0xffff, 1, 0, {}, false, true, false, true, false}, // return if overflow
{"RET", 0x02df, 0xffff, 1, 0, {}, false, true, true, false, false}, // unconditional return
{"RTI", 0x02ff, 0xffff, 1, 0, {}, false, true, true, false, false}, // return from interrupt
{"RTIGE", 0x02f0, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if greater or equal
{"RTIL", 0x02f1, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if less
{"RTIG", 0x02f2, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if greater
{"RTILE", 0x02f3, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if less or equal
{"RTINZ", 0x02f4, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if not zero
{"RTIZ", 0x02f5, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if zero
{"RTINC", 0x02f6, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if not carry
{"RTIC", 0x02f7, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if carry
{"RTIx8", 0x02f8, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if TODO
{"RTIx9", 0x02f9, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if TODO
{"RTIxA", 0x02fa, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if TODO
{"RTIxB", 0x02fb, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if TODO
{"RTILNZ", 0x02fc, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if logic not zero
{"RTILZ", 0x02fd, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if logic zero
{"RTIO", 0x02fe, 0xffff, 1, 0, {}, false, true, false, true, false}, // return from interrupt if overflow
{"RTI", 0x02ff, 0xffff, 1, 0, {}, false, true, true, false, false}, // return from interrupt unconditionally
{"CALLGE", 0x02b0, 0xffff, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false}, // call if greater or equal
{"CALLL", 0x02b1, 0xffff, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false}, // call if less
@ -192,7 +207,8 @@ const std::array<DSPOPCTemplate, 214> s_opcodes =
//2
{"LRS", 0x2000, 0xf800, 1, 2, {{P_REG18, 1, 0, 8, 0x0700}, {P_MEM, 1, 0, 0, 0x00ff}}, false, false, false, false, false}, // $(D+24) = MEM[($cr[0-7] << 8) | I]
{"SRS", 0x2800, 0xf800, 1, 2, {{P_MEM, 1, 0, 0, 0x00ff}, {P_REG18, 1, 0, 8, 0x0700}}, false, false, false, false, false}, // MEM[($cr[0-7] << 8) | I] = $(S+24)
{"SRSH", 0x2800, 0xfe00, 1, 2, {{P_MEM, 1, 0, 0, 0x00ff}, {P_ACCH, 1, 0, 8, 0x0100}}, false, false, false, false, false}, // MEM[($cr[0-7] << 8) | I] = $acS.h
{"SRS", 0x2c00, 0xfc00, 1, 2, {{P_MEM, 1, 0, 0, 0x00ff}, {P_REG1C, 1, 0, 8, 0x0300}}, false, false, false, false, false}, // MEM[($cr[0-7] << 8) | I] = $(S+24)
// opcodes that can be extended

View File

@ -44,16 +44,13 @@ enum partype_t
P_ACCM = P_REG | 0x1e00, // used for mid part of accum
// The following are not in gcdsptool
P_ACCM_D = P_REG | 0x1e80,
P_ACC = P_REG | 0x2000, // used for full accum.
P_ACC = P_REG | 0x2000, // used for full accum.
P_ACCH = P_REG | 0x1000, // used for high part of accum
P_ACC_D = P_REG | 0x2080,
P_AX = P_REG | 0x2200,
P_REGS_MASK = 0x03f80, // gcdsptool's value = 0x01f80
P_REF = P_REG | 0x4000,
P_PRG = P_REF | P_REG,
// The following seem like junk:
// P_REG10 = P_REG | 0x1000,
// P_AX_D = P_REG | 0x2280,
};
struct param2_t

View File

@ -104,13 +104,17 @@ void Interpreter::ret(const UDSPInstruction opc)
state.pc = state.PopStack(StackRegister::Call);
}
// RTI
// RTIcc
// 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 Interpreter::rti(const UDSPInstruction)
// This instruction has a conditional form, but it is not used by any official ucode.
void Interpreter::rti(const UDSPInstruction opc)
{
if (!CheckCondition(opc & 0xf))
return;
auto& state = m_dsp_core.DSPState();
state.r.sr = state.PopStack(StackRegister::Data);
state.pc = state.PopStack(StackRegister::Call);

View File

@ -8,15 +8,29 @@
namespace DSP::Interpreter
{
// SRS @M, $(0x18+S)
// 0010 1sss mmmm mmmm
// Move value from register $(0x18+S) to data memory pointed by address
// SRSH @M, $acS.h
// 0010 10ss mmmm mmmm
// Move value from register $acS.h 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.
void Interpreter::srsh(const UDSPInstruction opc)
{
auto& state = m_dsp_core.DSPState();
const auto reg = static_cast<u8>(((opc >> 8) & 0x1) + DSP_REG_ACH0);
const auto addr = static_cast<u16>((state.r.cr << 8) | (opc & 0xFF));
state.WriteDMEM(addr, OpReadRegister(reg));
}
// SRS @M, $(0x1C+S)
// 0010 11ss mmmm mmmm
// Move value from register $(0x1C+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.
void Interpreter::srs(const UDSPInstruction opc)
{
auto& state = m_dsp_core.DSPState();
const auto reg = static_cast<u8>(((opc >> 8) & 0x7) + 0x18);
const auto reg = static_cast<u8>(((opc >> 8) & 0x3) + DSP_REG_ACL0);
const auto addr = static_cast<u16>((state.r.cr << 8) | (opc & 0xFF));
if (reg >= DSP_REG_ACM0)

View File

@ -19,7 +19,7 @@ struct InterpreterOpInfo
};
// clang-format off
constexpr std::array<InterpreterOpInfo, 124> s_opcodes
constexpr std::array<InterpreterOpInfo, 125> s_opcodes
{{
{0x0000, 0xfffc, &Interpreter::nop},
@ -32,7 +32,7 @@ constexpr std::array<InterpreterOpInfo, 124> s_opcodes
{0x02d0, 0xfff0, &Interpreter::ret},
{0x02ff, 0xffff, &Interpreter::rti},
{0x02f0, 0xfff0, &Interpreter::rti},
{0x02b0, 0xfff0, &Interpreter::call},
@ -101,7 +101,8 @@ constexpr std::array<InterpreterOpInfo, 124> s_opcodes
// 2
{0x2000, 0xf800, &Interpreter::lrs},
{0x2800, 0xf800, &Interpreter::srs},
{0x2800, 0xfe00, &Interpreter::srsh},
{0x2c00, 0xfc00, &Interpreter::srs},
// opcodes that can be extended

View File

@ -149,6 +149,7 @@ public:
void srri(UDSPInstruction opc);
void srrn(UDSPInstruction opc);
void srs(UDSPInstruction opc);
void srsh(UDSPInstruction opc);
void sub(UDSPInstruction opc);
void subarn(UDSPInstruction opc);
void subax(UDSPInstruction opc);

View File

@ -88,6 +88,7 @@ public:
void bloopi(UDSPInstruction opc);
// Load/Store
void srsh(UDSPInstruction opc);
void srs(UDSPInstruction opc);
void lrs(UDSPInstruction opc);
void lr(UDSPInstruction opc);
@ -220,6 +221,7 @@ private:
void r_callr(UDSPInstruction opc);
void r_ifcc(UDSPInstruction opc);
void r_ret(UDSPInstruction opc);
void r_rti(UDSPInstruction opc);
void Update_SR_Register(Gen::X64Reg val = Gen::EAX, Gen::X64Reg scratch = Gen::EDX);

View File

@ -255,12 +255,7 @@ void DSPEmitter::ret(const UDSPInstruction opc)
ReJitConditional(opc, &DSPEmitter::r_ret);
}
// 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 DSPEmitter::rti(const UDSPInstruction opc)
void DSPEmitter::r_rti(const UDSPInstruction opc)
{
// g_dsp.r[DSP_REG_SR] = dsp_reg_load_stack(StackRegister::Data);
dsp_reg_load_stack(StackRegister::Data);
@ -268,6 +263,20 @@ void DSPEmitter::rti(const UDSPInstruction opc)
// g_dsp.pc = dsp_reg_load_stack(StackRegister::Call);
dsp_reg_load_stack(StackRegister::Call);
MOV(16, M_SDSP_pc(), R(DX));
WriteBranchExit();
}
// RTIcc
// 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.
// This instruction has a conditional form, but it is not used by any official ucode.
// NOTE: Cannot use FallBackToInterpreter(opc) here because of the need to write branch exit
void DSPEmitter::rti(const UDSPInstruction opc)
{
MOV(16, M_SDSP_pc(), Imm16(m_compile_pc + 1));
ReJitConditional(opc, &DSPEmitter::r_rti);
}
// HALT

View File

@ -12,14 +12,35 @@ using namespace Gen;
namespace DSP::JIT::x64
{
// SRS @M, $(0x18+S)
// SRSH @M, $acS.h
// 0010 1sss mmmm mmmm
// Move value from register $(0x18+S) to data memory pointed by address
// Move value from register $acS.h 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.
void DSPEmitter::srsh(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x1) + DSP_REG_ACH0;
// u16 addr = (g_dsp.r.cr << 8) | (opc & 0xFF);
X64Reg tmp1 = m_gpr.GetFreeXReg();
dsp_op_read_reg(reg, tmp1, RegisterExtension::Zero);
dsp_op_read_reg(DSP_REG_CR, RAX, RegisterExtension::Zero);
SHL(16, R(EAX), Imm8(8));
OR(16, R(EAX), Imm16(opc & 0xFF));
dmem_write(tmp1);
m_gpr.PutXReg(tmp1);
}
// SRS @M, $(0x1C+S)
// 0010 1sss mmmm mmmm
// Move value from register $(0x1C+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.
void DSPEmitter::srs(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x7) + 0x18;
u8 reg = ((opc >> 8) & 0x3) + DSP_REG_ACL0;
// u16 addr = (g_dsp.r.cr << 8) | (opc & 0xFF);
X64Reg tmp1 = m_gpr.GetFreeXReg();

View File

@ -19,7 +19,7 @@ struct JITOpInfo
};
// clang-format off
const std::array<JITOpInfo, 124> s_opcodes =
const std::array<JITOpInfo, 125> s_opcodes =
{{
{0x0000, 0xfffc, &DSPEmitter::nop},
@ -32,7 +32,7 @@ const std::array<JITOpInfo, 124> s_opcodes =
{0x02d0, 0xfff0, &DSPEmitter::ret},
{0x02ff, 0xffff, &DSPEmitter::rti},
{0x02f0, 0xfff0, &DSPEmitter::rti},
{0x02b0, 0xfff0, &DSPEmitter::call},
@ -101,7 +101,8 @@ const std::array<JITOpInfo, 124> s_opcodes =
// 2
{0x2000, 0xf800, &DSPEmitter::lrs},
{0x2800, 0xf800, &DSPEmitter::srs},
{0x2800, 0xfe00, &DSPEmitter::srsh},
{0x2c00, 0xfc00, &DSPEmitter::srs},
// opcodes that can be extended

View File

@ -1,6 +1,8 @@
incdir "tests"
include "dsp_base.inc"
test_main:
; Test parameters
lri $AC0.M, #0x0000 ; start
lri $AC0.L, #0x0000 ; start

View File

@ -2,7 +2,6 @@
; See https://github.com/dolphin-emu/dolphin/pull/5997
incdir "tests"
include "dsp_base.inc"
jmp test_main
; Writes the passed format, start and end addresses to the accelerator registers,
; then reads them back to registers.

View File

@ -5,7 +5,7 @@ include "dsp_base.inc"
;
; 3d80 andc'ls $AC1.M : $AX0.L, $AC0.M
test_main:
clr $ACC0
clr $ACC1
lri $AX0.L, #0x0000
@ -14,7 +14,7 @@ include "dsp_base.inc"
lri $AC0.M, #0x0004
lri $AC1.M, #0x1234
andc'ls $AC1.M : $AX0.L, $AC0.M
cw 0x3d80 ; andc'ls $AC1.M : $AX0.L, $AC0.M
call send_back

View File

@ -2,6 +2,8 @@
incdir "tests"
include "dsp_base.inc"
test_main:
clr $acc0
tst $acc0

View File

@ -12,6 +12,7 @@ TEST_ADDR: equ 0xFFA0 ; 0x0000
TEST_MEM: equ 0x00A0 ; 0x0000
TEST_MEM_2: equ 0x01A0 ; 0x0100
test_main:
LRI $AC0.L, #0xf00f
SR @TEST_REG, $AC0.L
SR @TEST_MEM, $AC0.L

View File

@ -27,6 +27,7 @@ include "dsp_base.inc"
; And thus it's tricky to implement in software too :p
; test using indexing register 1 - 0 is used in send_back
test_main:
lri $AR1, #16
lri $IX1, #32
lri $WR1, #0

View File

@ -1,9 +1,3 @@
; This is the trojan program we send to the DSP from DSPSpy to figure it out.
REGS_BASE: equ 0x0f80
MEM_HI: equ 0x0f7E
MEM_LO: equ 0x0f7F
; Interrupt vectors 8 vectors, 2 opcodes each
jmp irq0
jmp irq1
@ -14,276 +8,5 @@ MEM_LO: equ 0x0f7F
jmp irq6
jmp irq7
; Main code (and normal entrypoint) at 0x10
sbset #0x02
sbset #0x03
sbclr #0x04
sbset #0x05
sbset #0x06
s16
lri $CR, #0x00ff
clr $acc1
clr $acc0
; get address of memory dump and copy it to DRAM
call 0x807e
si @DMBH, #0x8888
si @DMBL, #0xdead
si @DIRQ, #0x0001
call 0x8078
andi $ac0.m, #0x7fff
lrs $ac1.m, @CMBL
sr @MEM_HI, $ac0.m
sr @MEM_LO, $ac1.m
lri $ax0.l, #0
lri $ax1.l, #0 ;(DSP_CR_IMEM | DSP_CR_TO_CPU)
lri $ax0.h, #0x2000
lr $ac0.l, @MEM_HI
lr $ac0.m, @MEM_LO
call do_dma
; get address of registers and DMA them to ram
call 0x807e
si @DMBH, #0x8888
si @DMBL, #0xbeef
si @DIRQ, #0x0001
call 0x8078
andi $ac0.m, #0x7fff
lrs $ac1.m, @CMBL
sr @MEM_HI, $ac0.m
sr @MEM_LO, $ac1.m
lri $ax0.l, #REGS_BASE
lri $ax1.l, #0 ;(DSP_CR_IMEM | DSP_CR_TO_CPU)
lri $ax0.h, #0x80
lr $ac0.l, @MEM_HI
lr $ac0.m, @MEM_LO
call do_dma
; Read in all the registers from RAM
lri $ar0, #REGS_BASE+1
lrri $ar1, @$ar0
lrri $ar2, @$ar0
lrri $ar3, @$ar0
lrri $ix0, @$ar0
lrri $ix1, @$ar0
lrri $ix2, @$ar0
lrri $ix3, @$ar0
lrri $wr0, @$ar0
lrri $wr1, @$ar0
lrri $wr2, @$ar0
lrri $wr3, @$ar0
lrri $st0, @$ar0
lrri $st1, @$ar0
lrri $st2, @$ar0
lrri $st3, @$ar0
lrri $ac0.h, @$ar0
lrri $ac1.h, @$ar0
lrri $cr, @$ar0
lrri $sr, @$ar0
lrri $prod.l, @$ar0
lrri $prod.m1, @$ar0
lrri $prod.h, @$ar0
lrri $prod.m2, @$ar0
lrri $ax0.l, @$ar0
lrri $ax1.l, @$ar0
lrri $ax0.h, @$ar0
lrri $ax1.h, @$ar0
lrri $ac0.l, @$ar0
lrri $ac1.l, @$ar0
lrri $ac0.m, @$ar0
lrri $ac1.m, @$ar0
lr $ar0, @REGS_BASE
jmp start_of_test
; This is where we jump when we're done testing, see above.
; We just fall into a loop, playing dead until someone resets the DSP.
end_of_test:
nop
jmp end_of_test
; Utility function to do DMA.
do_dma:
sr @DSMAH, $ac0.l
sr @DSMAL, $ac0.m
sr @DSPA, $ax0.l
sr @DSCR, $ax1.l
sr @DSBL, $ax0.h ; This kicks off the DMA.
call 0x863d ; Wait for DMA to complete by watching a bit in DSCR.
ret
; IRQ handlers. Just send back exception# and die
irq0:
lri $ac0.m, #0x0000
jmp irq
irq1:
lri $ac0.m, #0x0001
jmp irq
irq2:
lri $ac0.m, #0x0002
jmp irq
irq3:
lri $ac0.m, #0x0003
jmp irq
irq4:
lri $ac0.m, #0x0004
jmp irq
irq5:
lrs $ac0.m, @DMBH
andcf $ac0.m, #0x8000
jlz irq5
si @DMBH, #0x8005
si @DMBL, #0x0000
si @DIRQ, #0x0001
lri $ac0.m, #0xbbbb
sr @0xffda, $ac0.m ; pred scale
sr @0xffdb, $ac0.m ; yn1
lr $ix2, @ARAM
sr @0xffdc, $ac0.m ; yn2
rti
irq6:
lri $ac0.m, #0x0006
jmp irq
irq7:
lri $ac0.m, #0x0007
irq:
lrs $ac1.m, @DMBH
andcf $ac1.m, #0x8000
jlz irq
si @DMBH, #0x8bad
;sr @DMBL, $wr3 ; ???
sr @DMBL, $ac0.m ; Exception number
si @DIRQ, #0x0001
halt ; Through some magic this allows us to properly ack the exception in dspspy
;rti ; allow dumping of ucodes which cause exceptions...probably not safe at all
; DMA:s the current state of the registers back to the PowerPC. To do this,
; it must write the contents of all regs to DRAM.
send_back:
; first, store $sr so we can modify it
sr @(REGS_BASE + 19), $sr
set16
; Now store $wr0, as it must be 0xffff for srri to work as we expect
sr @(REGS_BASE + 8), $wr0
lri $wr0, #0xffff
; store registers to reg table
sr @REGS_BASE, $ar0
lri $ar0, #(REGS_BASE + 1)
srri @$ar0, $ar1
srri @$ar0, $ar2
srri @$ar0, $ar3
srri @$ar0, $ix0
srri @$ar0, $ix1
srri @$ar0, $ix2
srri @$ar0, $ix3
; skip $wr0 since we already stored and modified it
iar $ar0
srri @$ar0, $wr1
srri @$ar0, $wr2
srri @$ar0, $wr3
srri @$ar0, $st0
srri @$ar0, $st1
srri @$ar0, $st2
srri @$ar0, $st3
srri @$ar0, $ac0.h
srri @$ar0, $ac1.h
srri @$ar0, $cr
; skip $sr since we already stored and modified it
iar $ar0
srri @$ar0, $prod.l
srri @$ar0, $prod.m1
srri @$ar0, $prod.h
srri @$ar0, $prod.m2
srri @$ar0, $ax0.l
srri @$ar0, $ax1.l
srri @$ar0, $ax0.h
srri @$ar0, $ax1.h
srri @$ar0, $ac0.l
srri @$ar0, $ac1.l
srri @$ar0, $ac0.m
srri @$ar0, $ac1.m
; Regs are stored. Prepare DMA.
; $cr must be 0x00ff because the ROM uses lrs and srs with the assumption that
; they will modify hardware registers.
lri $cr, #0x00ff
lri $ax0.l, #0x0000
lri $ax1.l, #1 ;(DSP_CR_IMEM | DSP_CR_TO_CPU)
lri $ax0.h, #0x200
lr $ac0.l, @MEM_HI
lr $ac0.m, @MEM_LO
; Now, why are we looping here?
lri $ar1, #8+8
bloop $ar1, dma_copy
call do_dma
addi $ac0.m, #0x200
mrr $ac1.m, $ax0.l
addi $ac1.m, #0x100
dma_copy:
mrr $ax0.l, $ac1.m
; Wait for the CPU to send us a mail.
call 0x807e
si @DMBH, #0x8888
si @DMBL, #0xfeeb
si @DIRQ, #0x0001
; wait for the CPU to recieve our response before we execute the next op
call 0x8078
andi $ac0.m, #0x7fff
lrs $ac1.m, @CMBL
; Restore all regs again so we're ready to execute another op.
lri $ar0, #REGS_BASE+1
lrri $ar1, @$ar0
lrri $ar2, @$ar0
lrri $ar3, @$ar0
lrri $ix0, @$ar0
lrri $ix1, @$ar0
lrri $ix2, @$ar0
lrri $ix3, @$ar0
; leave $wr for later
iar $ar0
lrri $wr1, @$ar0
lrri $wr2, @$ar0
lrri $wr3, @$ar0
lrri $st0, @$ar0
lrri $st1, @$ar0
lrri $st2, @$ar0
lrri $st3, @$ar0
lrri $ac0.h, @$ar0
lrri $ac1.h, @$ar0
lrri $cr, @$ar0
; leave $sr for later
iar $ar0
lrri $prod.l, @$ar0
lrri $prod.m1, @$ar0
lrri $prod.h, @$ar0
lrri $prod.m2, @$ar0
lrri $ax0.l, @$ar0
lrri $ax1.l, @$ar0
lrri $ax0.h, @$ar0
lrri $ax1.h, @$ar0
lrri $ac0.l, @$ar0
lrri $ac1.l, @$ar0
lrri $ac0.m, @$ar0
lrri $ac1.m, @$ar0
lr $ar0, @REGS_BASE
lr $wr0, @(REGS_BASE+8)
lr $sr, @(REGS_BASE+19)
ret ; from send_back
; Obviously this must be included directly before your test code
start_of_test:
; The rest is in dsp_base_noirq.inc
include "dsp_base_noirq.inc"

View File

@ -0,0 +1,283 @@
; This is the trojan program we send to the DSP from DSPSpy to figure it out.
REGS_BASE: equ 0x0f80
MEM_HI: equ 0x0f7E
MEM_LO: equ 0x0f7F
WARNPC 0x10
ORG 0x10
; Main code (and normal entrypoint) at 0x10
; It is expected that IRQs were listed beforehand
; (e.g. by including dsp_base.inc instead of dsp_base_noirq.inc)
sbset #0x02
sbset #0x03
sbclr #0x04
sbset #0x05
sbset #0x06
s16
lri $CR, #0x00ff
clr $acc1
clr $acc0
; get address of memory dump and copy it to DRAM
call 0x807e
si @DMBH, #0x8888
si @DMBL, #0xdead
si @DIRQ, #0x0001
call 0x8078
andi $ac0.m, #0x7fff
lrs $ac1.m, @CMBL
sr @MEM_HI, $ac0.m
sr @MEM_LO, $ac1.m
lri $ax0.l, #0
lri $ax1.l, #0 ;(DSP_CR_IMEM | DSP_CR_TO_CPU)
lri $ax0.h, #0x2000
lr $ac0.l, @MEM_HI
lr $ac0.m, @MEM_LO
call do_dma
; get address of registers and DMA them to ram
call 0x807e
si @DMBH, #0x8888
si @DMBL, #0xbeef
si @DIRQ, #0x0001
call 0x8078
andi $ac0.m, #0x7fff
lrs $ac1.m, @CMBL
sr @MEM_HI, $ac0.m
sr @MEM_LO, $ac1.m
lri $ax0.l, #REGS_BASE
lri $ax1.l, #0 ;(DSP_CR_IMEM | DSP_CR_TO_CPU)
lri $ax0.h, #0x80
lr $ac0.l, @MEM_HI
lr $ac0.m, @MEM_LO
call do_dma
; Read in all the registers from RAM
lri $ar0, #REGS_BASE+1
lrri $ar1, @$ar0
lrri $ar2, @$ar0
lrri $ar3, @$ar0
lrri $ix0, @$ar0
lrri $ix1, @$ar0
lrri $ix2, @$ar0
lrri $ix3, @$ar0
lrri $wr0, @$ar0
lrri $wr1, @$ar0
lrri $wr2, @$ar0
lrri $wr3, @$ar0
lrri $st0, @$ar0
lrri $st1, @$ar0
lrri $st2, @$ar0
lrri $st3, @$ar0
lrri $ac0.h, @$ar0
lrri $ac1.h, @$ar0
lrri $cr, @$ar0
lrri $sr, @$ar0
lrri $prod.l, @$ar0
lrri $prod.m1, @$ar0
lrri $prod.h, @$ar0
lrri $prod.m2, @$ar0
lrri $ax0.l, @$ar0
lrri $ax1.l, @$ar0
lrri $ax0.h, @$ar0
lrri $ax1.h, @$ar0
lrri $ac0.l, @$ar0
lrri $ac1.l, @$ar0
lrri $ac0.m, @$ar0
lrri $ac1.m, @$ar0
lr $ar0, @REGS_BASE
jmp test_main
; This is where we jump when we're done testing, see above.
; We just fall into a loop, playing dead until someone resets the DSP.
end_of_test:
nop
jmp end_of_test
; Utility function to do DMA.
do_dma:
sr @DSMAH, $ac0.l
sr @DSMAL, $ac0.m
sr @DSPA, $ax0.l
sr @DSCR, $ax1.l
sr @DSBL, $ax0.h ; This kicks off the DMA.
wait_dma_finish:
lr $ac1.m, @DSCR
andcf $ac1.m, #0x4
jlz wait_dma_finish
ret
; IRQ handlers. Just send back exception# and die
irq0:
lri $ac0.m, #0x0000
jmp irq
irq1:
lri $ac0.m, #0x0001
jmp irq
irq2:
lri $ac0.m, #0x0002
jmp irq
irq3:
lri $ac0.m, #0x0003
jmp irq
irq4:
lri $ac0.m, #0x0004
jmp irq
irq5:
lrs $ac0.m, @DMBH
andcf $ac0.m, #0x8000
jlz irq5
si @DMBH, #0x8005
si @DMBL, #0x0000
si @DIRQ, #0x0001
lri $ac0.m, #0xbbbb
sr @0xffda, $ac0.m ; pred scale
sr @0xffdb, $ac0.m ; yn1
lr $ix2, @ARAM
sr @0xffdc, $ac0.m ; yn2
rti
irq6:
lri $ac0.m, #0x0006
jmp irq
irq7:
lri $ac0.m, #0x0007
irq:
lrs $ac1.m, @DMBH
andcf $ac1.m, #0x8000
jlz irq
si @DMBH, #0x8bad
;sr @DMBL, $wr3 ; ???
sr @DMBL, $ac0.m ; Exception number
si @DIRQ, #0x0001
halt ; Through some magic this allows us to properly ack the exception in dspspy
;rti ; allow dumping of ucodes which cause exceptions...probably not safe at all
; DMA:s the current state of the registers back to the PowerPC. To do this,
; it must write the contents of all regs to DRAM.
send_back:
; first, store $sr so we can modify it
sr @(REGS_BASE + 19), $sr
set16
; Now store $wr0, as it must be 0xffff for srri to work as we expect
sr @(REGS_BASE + 8), $wr0
lri $wr0, #0xffff
; store registers to reg table
sr @REGS_BASE, $ar0
lri $ar0, #(REGS_BASE + 1)
srri @$ar0, $ar1
srri @$ar0, $ar2
srri @$ar0, $ar3
srri @$ar0, $ix0
srri @$ar0, $ix1
srri @$ar0, $ix2
srri @$ar0, $ix3
; skip $wr0 since we already stored and modified it
iar $ar0
srri @$ar0, $wr1
srri @$ar0, $wr2
srri @$ar0, $wr3
srri @$ar0, $st0
srri @$ar0, $st1
srri @$ar0, $st2
srri @$ar0, $st3
srri @$ar0, $ac0.h
srri @$ar0, $ac1.h
srri @$ar0, $cr
; skip $sr since we already stored and modified it
iar $ar0
srri @$ar0, $prod.l
srri @$ar0, $prod.m1
srri @$ar0, $prod.h
srri @$ar0, $prod.m2
srri @$ar0, $ax0.l
srri @$ar0, $ax1.l
srri @$ar0, $ax0.h
srri @$ar0, $ax1.h
srri @$ar0, $ac0.l
srri @$ar0, $ac1.l
srri @$ar0, $ac0.m
srri @$ar0, $ac1.m
; Regs are stored. Prepare DMA.
; $cr must be 0x00ff because the ROM uses lrs and srs with the assumption that
; they will modify hardware registers.
lri $cr, #0x00ff
lri $ax0.l, #0x0000
lri $ax1.l, #1 ;(DSP_CR_IMEM | DSP_CR_TO_CPU)
lri $ax0.h, #0x200
lr $ac0.l, @MEM_HI
lr $ac0.m, @MEM_LO
; Now, why are we looping here?
lri $ar1, #8+8
bloop $ar1, dma_copy
call do_dma
addi $ac0.m, #0x200
mrr $ac1.m, $ax0.l
addi $ac1.m, #0x100
dma_copy:
mrr $ax0.l, $ac1.m
; Wait for the CPU to send us a mail.
call 0x807e
si @DMBH, #0x8888
si @DMBL, #0xfeeb
si @DIRQ, #0x0001
; wait for the CPU to recieve our response before we execute the next op
call 0x8078
andi $ac0.m, #0x7fff
lrs $ac1.m, @CMBL
; Restore all regs again so we're ready to execute another op.
lri $ar0, #REGS_BASE+1
lrri $ar1, @$ar0
lrri $ar2, @$ar0
lrri $ar3, @$ar0
lrri $ix0, @$ar0
lrri $ix1, @$ar0
lrri $ix2, @$ar0
lrri $ix3, @$ar0
; leave $wr for later
iar $ar0
lrri $wr1, @$ar0
lrri $wr2, @$ar0
lrri $wr3, @$ar0
lrri $st0, @$ar0
lrri $st1, @$ar0
lrri $st2, @$ar0
lrri $st3, @$ar0
lrri $ac0.h, @$ar0
lrri $ac1.h, @$ar0
lrri $cr, @$ar0
; leave $sr for later
iar $ar0
lrri $prod.l, @$ar0
lrri $prod.m1, @$ar0
lrri $prod.h, @$ar0
lrri $prod.m2, @$ar0
lrri $ax0.l, @$ar0
lrri $ax1.l, @$ar0
lrri $ax0.h, @$ar0
lrri $ax1.h, @$ar0
lrri $ac0.l, @$ar0
lrri $ac1.l, @$ar0
lrri $ac0.m, @$ar0
lrri $ac1.m, @$ar0
lr $ar0, @REGS_BASE
lr $wr0, @(REGS_BASE+8)
lr $sr, @(REGS_BASE+19)
ret ; from send_back

View File

@ -6,13 +6,14 @@ include "dsp_base.inc"
; We can call send_back at any time to send data back to the PowerPC.
lri $AC0.M, #0x1000
call send_back
test_main:
lri $AC0.M, #0x1000
call send_back
set40
lri $AC0.M, #0x1000
set16
call send_back
set40
lri $AC0.M, #0x1000
set16
call send_back
; We're done, DO NOT DELETE THIS LINE
jmp end_of_test
jmp end_of_test

View File

@ -30,7 +30,7 @@ include "dsp_base.inc"
; I really don't know how the above could possibly be efficiently implemented in hardware.
; And thus it's tricky to implement in software too :p
test_main:
; test using indexing register 1 - 0 is used in send_back
lri $AR1, #16
lri $IX1, #32

View File

@ -2,6 +2,7 @@
incdir "tests"
include "dsp_base.inc"
test_main:
lri $AR0, #0x001c
lri $AR1, #0x001d
lri $AR2, #0x001e

View File

@ -1,6 +1,7 @@
incdir "tests"
include "dsp_base.inc"
test_main:
CLR $acc0
CLR $acc1
LRI $ac0.h, #0x0050

View File

@ -2,8 +2,9 @@
incdir "tests"
include "dsp_base.inc"
; Results is in capitails like this: UNSIGNED
; Results are in capitals like this: UNSIGNED
test_main:
CLR15
; Test MULXMVZ - SET15

View File

@ -2,6 +2,7 @@
incdir "tests"
include "dsp_base.inc"
test_main:
clr $ACC0
neg $ACC0

View File

@ -10,6 +10,7 @@ include "dsp_base.inc"
// 0x02cb is the same opcode, but arithmetic.
// We'll call it asrn, no arguments.
test_main:
clr $ACC0
clr $ACC1
lri $AC0.H, #0

View File

@ -1,6 +1,7 @@
incdir "tests"
include "dsp_base.inc"
test_main:
; Test what happens various values are written to every register
LRI $ar0, #0xffff
CALL set_all_regs

View File

@ -0,0 +1,55 @@
; This test needs to manually specify IRQs
jmp irq0
jmp irq1
jmp irq2
jmp irq3
jmp irq4
jmp accov_irq
jmp irq6
jmp irq7
incdir "tests"
include "dsp_base_noirq.inc"
test_main:
; Use the accelerator to generate an IRQ by setting the start and end address to 0
; This will result in an interrupt on every read
SI @0xffda, #0 ; pred_scale
SI @0xffdb, #0 ; yn1
SI @0xffdc, #0 ; yn2
SI @0xffd1, #0 ; SampleFormat
SI @ACSAH, #0
SI @ACCAH, #0
SI @ACSAL, #0
SI @ACCAL, #0
SI @ACEAH, #0
SI @ACEAL, #0
LRI $AX1.H, #0x0000
LRS $AX0.L, @ARAM ; Trigger interrupt
CALL send_back
LRI $AX1.H, #0x0001
LRS $AX0.L, @ARAM ; Trigger interrupt
CALL send_back
LRI $AX1.H, #0x0000
LRS $AX0.L, @ARAM ; Trigger interrupt
CALL send_back
jmp end_of_test
accov_irq:
; Restore registers, otherwise no new interrupt will be generated
SI @0xffda, #0 ; pred_scale
SI @0xffdb, #0 ; yn1
SI @0xffdc, #0 ; yn2
TSTAXH $AX1.H
LRI $AX1.L, #0x1111
cw 0x02f4 ; RTINZ if it exists
LRI $AX1.L, #0x2222
cw 0x02f5 ; RTIZ if it exists
LRI $AX1.L, #0x3333
RTI

View File

@ -0,0 +1,131 @@
incdir "tests"
include "dsp_base.inc"
test_main:
; Test registers used by LRS and SRS
LRI $CR, #0x0000
CALL clear_regs
CALL store_mem_sr
; Write with SR, read with LR
LRI $AR0, #0xA00A
CALL create_pattern
CALL store_mem_sr
CALL send_back
CALL clear_regs
CALL read_mem_lr
CALL send_back
; Write with SR, read with LRS
LRI $AR0, #0xB00B
CALL create_pattern
CALL store_mem_sr
CALL send_back
CALL clear_regs
CALL read_mem_lrs
CALL send_back
; Write with SRS, read with LR
LRI $AR0, #0xC00C
CALL create_pattern
CALL store_mem_srs
CALL send_back
CALL clear_regs
CALL read_mem_lr
CALL send_back
; Write with SR, read with LRS
LRI $AR0, #0xD00D
CALL create_pattern
CALL store_mem_srs
CALL send_back
CALL clear_regs
CALL read_mem_lrs
CALL send_back
; We're done, DO NOT DELETE THIS LINE
JMP end_of_test
create_pattern:
LRI $IX0, #0x0110
MRR $AX0.L, $AR0
ADDARN $AR0, $IX0
MRR $AX1.L, $AR0
ADDARN $AR0, $IX0
MRR $AX0.H, $AR0
ADDARN $AR0, $IX0
MRR $AX1.H, $AR0
ADDARN $AR0, $IX0
MRR $AC0.L, $AR0
ADDARN $AR0, $IX0
MRR $AC1.L, $AR0
ADDARN $AR0, $IX0
MRR $AC0.M, $AR0
ADDARN $AR0, $IX0
MRR $AC1.M, $AR0
ADDARN $AR0, $IX0
; AC0.H and AC1.H have odd results since they're 8-bit sign-extended, but that's fine.
MRR $AC0.H, $AR0
ADDARN $AR0, $IX0
MRR $AC1.H, $AR0
RET
clear_regs:
LRI $AX0.L, #0x0000
LRI $AX1.L, #0x0000
LRI $AX0.H, #0x0000
LRI $AX1.H, #0x0000
LRI $AC0.L, #0x0000
LRI $AC1.L, #0x0000
LRI $AC0.M, #0x0000
LRI $AC1.M, #0x0000
LRI $AC0.H, #0x0000
LRI $AC1.H, #0x0000
RET
read_mem_lr:
LR $AX0.L, @0x0000
LR $AX1.L, @0x0001
LR $AX0.H, @0x0002
LR $AX1.H, @0x0003
LR $AC0.L, @0x0004
LR $AC1.L, @0x0005
LR $AC0.M, @0x0006
LR $AC1.M, @0x0007
RET
read_mem_lrs:
LRS $AX0.L, @0x00
LRS $AX1.L, @0x01
LRS $AX0.H, @0x02
LRS $AX1.H, @0x03
LRS $AC0.L, @0x04
LRS $AC1.L, @0x05
LRS $AC0.M, @0x06
LRS $AC1.M, @0x07
RET
store_mem_sr:
SR @0x0000, $AX0.L
SR @0x0001, $AX1.L
SR @0x0002, $AX0.H
SR @0x0003, $AX1.H
SR @0x0004, $AC0.L
SR @0x0005, $AC1.L
SR @0x0006, $AC0.M
SR @0x0007, $AC1.M
RET
store_mem_srs:
; For future compatibility these have been changed to cw.
; The way the instructions were originally encoded is commented,
; but this does not match their behavior.
cw 0x2800 ; SRS @0x00, $AX0.L - actually SRSH @0x00, $AC0.H
cw 0x2901 ; SRS @0x01, $AX1.L - actually SRSH @0x01, $AC1.H
cw 0x2A02 ; SRS @0x02, $AX0.H - actually unknown, no store performed
cw 0x2B03 ; SRS @0x03, $AX1.H - actually unknown, no store performed
cw 0x2C04 ; SRS @0x04, $AC0.L
cw 0x2D05 ; SRS @0x05, $AC1.L
cw 0x2E06 ; SRS @0x06, $AC0.M
cw 0x2F07 ; SRS @0x07, $AC1.M
RET

View File

@ -2,7 +2,7 @@ incdir "tests"
include "dsp_base.inc"
; Reads regs from 0xFF80 to 0xFF8D and sends them back
test_main:
lr $AC0.M, @0xff80
call send_back

View File

@ -86,11 +86,11 @@ WARNPC 0x8b
ORG 0x8b
; called by GBA ucode
dram_to_cpu:
srs @DSMAH, $AX0.H
srs @DSMAL, $AX0.L
sr @DSMAH, $AX0.H
sr @DSMAL, $AX0.L
si @DSCR, #0x1
srs @DSPA, $AX1.H
srs @DSBL, $AX1.L
sr @DSPA, $AX1.H
sr @DSBL, $AX1.L
call wait_dma+#IROM_BASE
ret
@ -108,11 +108,11 @@ ORG 0xbc
; called by GBA ucode
bootucode_ax:
lris $AC0.M, #0
srs @DSCR, $AC0.M
srs @DSMAH, $AX0.H
srs @DSMAL, $AX0.L
srs @DSPA, $AX1.H
srs @DSBL, $AX1.L
srs @DSCR, $AC0.M
sr @DSMAH, $AX0.H
sr @DSMAL, $AX0.L
sr @DSPA, $AX1.H
sr @DSBL, $AX1.L
call wait_dma+#IROM_BASE
bootucode_ix:
@ -145,9 +145,15 @@ ORG 0xe7
; Returns:
; AX0.L is the value of the last sample from input 1
; AX1.H is the value of the last sample from input 2
;
; for i = 0..31:
; ar3[i] = ((ar2[i] << 16) + ar0[i] * ar1[0]) >> 16
; for i = 0..31:
; ix1[i] = ((ix1[i] << 16) + ix0[i] * ar1[2]) >> 16
; ax0.l = ar0[31] * ar1[0]
; ax1.h = ix0[31] * ar1[2]
mix_two_add:
call mix_add+#IROM_BASE
iar $AR1
mrr $AR0, $IX0
mrr $AR2, $IX1
mrr $AR3, $IX1
@ -183,8 +189,14 @@ ORG 0x1f9
; Returns:
; AX0.L is the value of the last sample
; AX1.H is the first address after the output
;
; for i = 0..31:
; ar3[i] = ((ar2[i] << 16) + ar0[i] * ar1[0]) >> 16
; ax0.l = ar0[31] * ar1[0]
; ax1.h = ar3 + 32 // assuming ar3 is a s32 pointer
mix_add:
lrri $AX1.L, @$AR1
iar $AR1
bloopi #32, ____mix_add_end_loop+#IROM_BASE
lrri $AC0.M, @$AR2
lrri $AC0.L, @$AR2
@ -203,6 +215,13 @@ ____mix_add_end_loop:
WARNPC 0x282
ORG 0x282
; for i = 0..31:
; ar3[i] = ar1[0] + i * ar1[1]
; ar2[i] = ((ar2[i] << 16) + ar0[i] * ar3[i]) >> 16
; ar3[i+32] = ar1[2] + i * ar1[3]
; ix1[i] = ((ix1[i] << 16) + ix0[i] * ar3[i+32]) >> 16
; ax0.l = ar0[31] * ar3[31]
; ax1.h = ix0[31] * ar3[63]
mix_two_add_ramp:
call mix_add_ramp+#IROM_BASE
mrr $AR0, $IX0
@ -227,6 +246,10 @@ sub_8458:
WARNPC 0x45d
ORG 0x45d
; for i = 0..31:
; ar3[i] = ar1[0] + i * ar1[1]
; ar2[i] = ((ar2[i] << 16) + ar0[i] * ar3[i]) >> 16
; ax0.l = ar0[31] * ar3[31]
mix_add_ramp:
clr $ACC0
clr $ACC1
@ -259,7 +282,6 @@ ____mix_add_ramp_end_loop:
srri @$AR3, $AC0.L
movp $ACC0
mrr $AX0.L, $AC0.M
mrr $AX1.H, $AR3
mrr $AR1, $IX3
mrr $AR3, $IX2
ret

View File

@ -1,64 +1,75 @@
Legal GC/WII DSP IROM replacement (v0.3.1)
-------------------------------------------------------
- irom: When running from the ROM entrypoint, skip the bootucode_ax branch
of the bootucode procedure. Since the ROM doesn't set any of the AX
registers, it could cause bad DMA transfers and crashes.
ligfx
10/aug/2017
Legal GC/WII DSP IROM replacement (v0.3)
-------------------------------------------------------
- coef: Explicitly set 23 different values that are used by GBA UCode, and
tweaked overall parameters to more closely match those 23 values.
- irom: Moved a few functions to their proper places, updated BootUCode to
configure DMA transfers using AX registers as well as IX registers (the GBA
UCode uses this to do two sequential transfers in one call), and added
partial functions used by GBA UCode.
ligfx
2/june/2017
Legal GC/WII DSP IROM replacement (v0.2.1)
-------------------------------------------------------
- coef: 4-tap polyphase FIR filters
- irom: unchanged
Coefficients are roughly equivalent to those in the official DROM.
Improves resampling quality greatly over linear interpolation.
See generate_coefs.py for details.
stgn
29/june/2015
Legal GC/WII DSP IROM replacement (v0.2)
-------------------------------------------------------
- coef: crafted to use a linear interpolation when resampling (instead of
having a real 4 TAP FIR filter)
- irom: added all the mixing functions, some functions not used by AX/Zelda are
still missing
Should work with all AX, AXWii and Zelda UCode games. Card/IPL/GBA are most
likely still broken with it and require a real DSP ROM.
delroth
16/march/2013
Legal GC/WII DSP IROM replacement (v0.1)
-------------------------------------------------------
- coef: fake (zeroes)
- irom: reversed and rewrote ucode loading/reset part, everything else is missing
Good enough for Zelda ucode games (and maybe some AX too):
- WII: SMG 1/2, Pikmin 1/2 WII, Zelda TP WII, Donkey Kong Jungle Beat (WII), ...
- GC: Mario Kart Double Dash, Luigi Mansion, Super Mario Sunshine, Pikmin 1/2, Zelda WW, Zelda TP, ...
Basically... If game is not using coef and irom mixing functions it will work ok.
Dolphin emulator will report wrong CRCs, but it will work ok with mentioned games.
LM
31/july/2011
Legal GC/WII DSP IROM replacement (v0.4) (0xe789b5a5, 0xa4a575f5)
-------------------------------------------------------
- irom: Minor accuracy and documentation improvements
- irom: Remove use of SRS instruction with AX registers, as those instructions
do not actually exist
Tilka, Pokechu22
17/aug/2021
Legal GC/WII DSP IROM replacement (v0.3.1) (0x128ea7a2, 0xa4a575f5)
-------------------------------------------------------
- irom: When running from the ROM entrypoint, skip the bootucode_ax branch
of the bootucode procedure. Since the ROM doesn't set any of the AX
registers, it could cause bad DMA transfers and crashes.
ligfx
10/aug/2017
Legal GC/WII DSP IROM replacement (v0.3) (0x3aa4a793, 0xa4a575f5)
-------------------------------------------------------
- coef: Explicitly set 23 different values that are used by GBA UCode, and
tweaked overall parameters to more closely match those 23 values.
- irom: Moved a few functions to their proper places, updated BootUCode to
configure DMA transfers using AX registers as well as IX registers (the GBA
UCode uses this to do two sequential transfers in one call), and added
partial functions used by GBA UCode.
ligfx
2/june/2017
Legal GC/WII DSP IROM replacement (v0.2.1) (0xd9907f71, 0xdb6880c1)
-------------------------------------------------------
- coef: 4-tap polyphase FIR filters
- irom: unchanged
Coefficients are roughly equivalent to those in the official DROM.
Improves resampling quality greatly over linear interpolation.
See generate_coefs.py for details.
stgn
29/june/2015
Legal GC/WII DSP IROM replacement (v0.2) (0xd9907f71, 0xb019c2fb)
-------------------------------------------------------
- coef: crafted to use a linear interpolation when resampling (instead of
having a real 4 TAP FIR filter)
- irom: added all the mixing functions, some functions not used by AX/Zelda are
still missing
Should work with all AX, AXWii and Zelda UCode games. Card/IPL/GBA are most
likely still broken with it and require a real DSP ROM.
delroth
16/march/2013
Legal GC/WII DSP IROM replacement (v0.1) (0x9c8f593c, 0x10000001)
-------------------------------------------------------
- coef: fake (zeroes)
- irom: reversed and rewrote ucode loading/reset part, everything else is missing
Good enough for Zelda ucode games (and maybe some AX too):
- WII: SMG 1/2, Pikmin 1/2 WII, Zelda TP WII, Donkey Kong Jungle Beat (WII), ...
- GC: Mario Kart Double Dash, Luigi Mansion, Super Mario Sunshine, Pikmin 1/2, Zelda WW, Zelda TP, ...
Basically... If game is not using coef and irom mixing functions it will work ok.
Dolphin emulator will report wrong CRCs, but it will work ok with mentioned games.
LM
31/july/2011