basic attempt at implementing the scaling command

This commit is contained in:
Arisotura 2025-07-16 14:41:45 +02:00
parent af768fe729
commit e46a54ef8f
4 changed files with 264 additions and 20 deletions

View File

@ -28,9 +28,9 @@ using Platform::Log;
using Platform::LogLevel;
DSPHLE_UcodeBase::DSPHLE_UcodeBase()
DSPHLE_UcodeBase::DSPHLE_UcodeBase(melonDS::DSi& dsi) : DSi(dsi)
{
//
DSi.RegisterEventFuncs(Event_DSi_DSPHLE, this, {MakeEventThunk(DSPHLE_UcodeBase, OnUcodeCmdFinish)});
}
DSPHLE_UcodeBase::~DSPHLE_UcodeBase()
@ -40,11 +40,22 @@ DSPHLE_UcodeBase::~DSPHLE_UcodeBase()
void DSPHLE_UcodeBase::Reset()
{
DataMemory = nullptr;
memset(CmdReg, 0, sizeof(CmdReg));
memset(CmdWritten, 0, sizeof(CmdWritten));
memset(ReplyReg, 0, sizeof(ReplyReg));
memset(ReplyWritten, 0, sizeof(ReplyWritten));
memset(ReplyReadCb, 0, sizeof(ReplyReadCb));
//memset(ReplyReadCb, 0, sizeof(ReplyReadCb));
ReplyReadCb[0] = nullptr;
ReplyReadCb[1] = nullptr;
ReplyReadCb[2] = nullptr;
SemaphoreIn = 0;
SemaphoreOut = 0;
SemaphoreMask = 0;
UcodeCmd = 0;
}
void DSPHLE_UcodeBase::DoSavestate(Savestate *file)
@ -65,11 +76,12 @@ bool DSPHLE_UcodeBase::SendDataIsEmpty(u8 index)
u16 DSPHLE_UcodeBase::RecvData(u8 index)
{
if (!ReplyWritten[index]) printf("DSP: receive reply%d but empty\n", index);
if (!ReplyWritten[index]) return 0; // CHECKME
u16 ret = ReplyReg[index];
ReplyWritten[index] = false;
printf("DSP: receive reply%d %04X\n", index, ret);
if (ReplyReadCb[index])
{
ReplyReadCb[index]();
@ -81,6 +93,7 @@ u16 DSPHLE_UcodeBase::RecvData(u8 index)
void DSPHLE_UcodeBase::SendData(u8 index, u16 val)
{
// TODO less ambiguous naming for those functions
if (CmdWritten[index])
{
printf("??? trying to write cmd but there's already one\n");
@ -90,6 +103,32 @@ void DSPHLE_UcodeBase::SendData(u8 index, u16 val)
CmdReg[index] = val;
CmdWritten[index] = true;
printf("DSP: send cmd%d %04X\n", index, val);
if (index == 0)
{
if (UcodeCmd)
{
printf("???? there is already a command pending\n");
return;
}
// writing to CMD0 initiates a ucode-specific command
// parameters are then written to pipe 7
UcodeCmd = val;
CmdWritten[index] = false;
RunUcodeCmd();
}
else if (index == 2)
{
// CMD2 serves to notify that a pipe was written to
// value is the pipe index
CmdWritten[index] = false;
if (UcodeCmd)
RunUcodeCmd();
}
}
@ -104,7 +143,12 @@ void DSPHLE_UcodeBase::SendReply(u8 index, u16 val)
ReplyReg[index] = val;
ReplyWritten[index] = true;
// TODO add callback for when it is successfully written
switch (index)
{
case 0: DSi.DSP.IrqRep0(); break;
case 1: DSi.DSP.IrqRep1(); break;
case 2: DSi.DSP.IrqRep2(); break;
}
}
void DSPHLE_UcodeBase::SetReplyReadCallback(u8 index, fnReplyReadCb callback)
@ -115,7 +159,7 @@ void DSPHLE_UcodeBase::SetReplyReadCallback(u8 index, fnReplyReadCb callback)
u16 DSPHLE_UcodeBase::DMAChan0GetDstHigh()
{
//
// TODO?
return 0;
}
@ -139,13 +183,36 @@ u16 DSPHLE_UcodeBase::AHBMGetUnitSize(u16 index)
u16 DSPHLE_UcodeBase::DataReadA32(u32 addr)
{
//
return 0;
printf("ucode: DataReadA32 %08X\n", addr);
addr <<= 1;
/*if (!(addr & 0x40000))
{
u8* ptr = DSi.NWRAMMap_B[2][(addr >> 15) & 0x7];
return ptr ? *(u16*)&ptr[addr & 0x7FFF] : 0;
}
else*/
{
u8* ptr = DSi.NWRAMMap_C[2][(addr >> 15) & 0x7];
return ptr ? *(u16*)&ptr[addr & 0x7FFF] : 0;
}
}
void DSPHLE_UcodeBase::DataWriteA32(u32 addr, u16 val)
{
//
printf("ucode: DataWriteA32 %08X %04X\n", addr, val);
addr <<= 1;
/*if (!(addr & 0x40000))
{
u8* ptr = DSi.NWRAMMap_B[2][(addr >> 15) & 0x7];
if (ptr) *(u16*)&ptr[addr & 0x7FFF] = val;
}
else*/
{
u8* ptr = DSi.NWRAMMap_C[2][(addr >> 15) & 0x7];
if (ptr) *(u16*)&ptr[addr & 0x7FFF] = val;
}
}
u16 DSPHLE_UcodeBase::MMIORead(u16 addr)
@ -194,23 +261,29 @@ void DSPHLE_UcodeBase::AHBMWrite32(u32 addr, u32 val)
u16 DSPHLE_UcodeBase::GetSemaphore()
{
//
return 0;
return SemaphoreOut;
}
void DSPHLE_UcodeBase::SetSemaphore(u16 val)
{
//
SemaphoreIn |= val;
}
void DSPHLE_UcodeBase::ClearSemaphore(u16 val)
{
//
SemaphoreOut &= ~val;
}
void DSPHLE_UcodeBase::MaskSemaphore(u16 val)
{
//
SemaphoreMask = val;
}
void DSPHLE_UcodeBase::SetSemaphoreOut(u16 val)
{
SemaphoreOut |= val;
if (SemaphoreOut & (~SemaphoreMask))
DSi.DSP.IrqSem();
}
@ -220,6 +293,20 @@ void DSPHLE_UcodeBase::Start()
// TODO later: detect which ucode it is and create the right class!
// (and fall back to Teakra if not a known ucode)
const u16 pipeaddr = 0x0800;
u16* mem = (u16*)DSi.NWRAMMap_C[2][0];
// initialize pipe structure
u16* pipe = &mem[pipeaddr];
for (int i = 0; i < 16; i++)
{
*pipe++ = 0x1000 + (0x100 * i); // buffer address
*pipe++ = 0x200; // length in bytes
*pipe++ = 0; // read pointer
*pipe++ = 0; // write pointer
*pipe++ = i; // pipe index
}
SendReply(0, 1);
SendReply(1, 1);
SendReply(2, 1);
@ -227,7 +314,8 @@ void DSPHLE_UcodeBase::Start()
{
printf("reply 2 was read\n");
SendReply(2, 0x0800);
SendReply(2, pipeaddr);
SetSemaphoreOut(0x8000);
});
// TODO more shit
@ -240,4 +328,138 @@ void DSPHLE_UcodeBase::Run(u32 cycles)
}
u16* DSPHLE_UcodeBase::LoadPipe(u8 index)
{
const u16 pipeaddr = 0x0800;
u16* mem = (u16*)DSi.NWRAMMap_C[2][0];
u16* pipe = &mem[pipeaddr + (index * 5)];
return pipe;
}
u32 DSPHLE_UcodeBase::GetPipeLength(u16* pipe)
{
u32 ret;
u16 rdptr = pipe[2];
u16 wrptr = pipe[3];
if (rdptr > wrptr)
{
u16 len = pipe[1];
ret = wrptr + len - rdptr;
}
else
{
ret = wrptr - rdptr;
}
if (ret & 1) printf("HUH? pipe length is odd? (%d)\n", ret);
return ret >> 1;
}
u32 DSPHLE_UcodeBase::ReadPipe(u16* pipe, u16* data, u32 len)
{
u16* mem = (u16*)DSi.NWRAMMap_C[2][0];
u16* pipebuf = &mem[pipe[0]];
u16 pipelen = pipe[1] >> 1;
u16 rdptr = pipe[2] >> 1;
u16 wrptr = pipe[3] >> 1;
printf("readpipe(%d): len=%d rd=%d wr=%d\n", len, pipelen, rdptr, wrptr);
u32 rdlen = 0;
for (int i = 0; i < len; i++)
{
data[i] = pipebuf[rdptr++];
rdlen++;
if (rdptr >= pipelen) rdptr = 0;
if (rdptr == wrptr) break;
}
printf("-> rd=%d\n", rdptr);
pipe[2] = rdptr << 1;
SendReply(2, pipe[4]);
SetSemaphoreOut(0x8000);
return rdlen;
}
void DSPHLE_UcodeBase::RunUcodeCmd()
{
u16* pipe = LoadPipe(7);
u32 len = GetPipeLength(pipe);
printf("try to run ucode cmd: cmd=%d, len=%d\n", UcodeCmd, len);
switch (UcodeCmd)
{
case 1: // scaling
if (len < 14) return;
UcodeCmd_Scaling(pipe);
break;
}
//UcodeCmd = 0;
}
void DSPHLE_UcodeBase::OnUcodeCmdFinish(u32 param)
{
printf("finish cmd %d, param=%d, %d/%d\n", UcodeCmd, param, CmdWritten[2], ReplyWritten[2]);
UcodeCmd = 0;
SendReply(1, (u16)param);
}
void DSPHLE_UcodeBase::UcodeCmd_Scaling(u16* pipe)
{
u16 params[14];
ReadPipe(pipe, params, 14);
u32 src_addr = (params[1] << 16) | params[0];
u32 dst_addr = (params[3] << 16) | params[2];
u16 filter = params[4];
u16 src_width = params[5];
u16 src_height = params[6];
u16 width_scale = params[7];
u16 height_scale = params[8];
u16 rect_xoffset = params[9];
u16 rect_yoffset = params[10];
u16 rect_width = params[11];
u16 rect_height = params[12];
u32 dst_width = (src_width * width_scale) / 1000;
u32 dst_height = (src_height * height_scale) / 1000;
// TODO those are slightly different for bicubic
u32 x_factor = ((rect_width - 2) << 10) / (dst_width - 1);
u32 y_factor = ((rect_height - 2) << 10) / (dst_height - 1);
// bound check
// CHECKME
//if (dst_width > rect_width) dst_width = rect_width;
//if (dst_height > rect_height) dst_height = rect_height;
// at 1700 it starts going out of bounds
src_addr += (((rect_yoffset * src_width) + rect_xoffset) << 1);
//printf("scale %08X -> %08X, %dx%d %dx%d %dx%d\n", src_addr, dst_addr, src_width, src_height, rect_width, rect_height, dst_width, dst_height);
for (u32 y = 0; y < dst_height; y++)
{
u32 sy = ((y * y_factor) + 0x3FF) >> 10;
u32 src_line = src_addr + ((sy * src_width) << 1);
//printf("line %d->%d %d %08X\n", y, sy, src_width, src_line);
for (u32 x = 0; x < dst_width; x++)
{
u32 sx = ((x * x_factor) + 0x3FF) >> 10;
u16 v = DSi.ARM9Read16(src_line + (sx << 1));
DSi.ARM9Write16(dst_addr, v);
//printf("%d,%d %08X -> %08X\n", y, x, src_line+(sx<<1),dst_addr);
dst_addr += 2;
}
//src_addr += (src_width << 1);
}
// TODO the rest of the shit!!
// TODO add a delay to this
// TODO make the delay realistic
//SendReply(1, 1);
DSi.ScheduleEvent(Event_DSi_DSPHLE, false, 600000, 0, 1);
}
}

View File

@ -27,10 +27,12 @@
namespace melonDS
{
class DSi;
class DSPHLE_UcodeBase
{
public:
DSPHLE_UcodeBase();
DSPHLE_UcodeBase(melonDS::DSi& dsi);
~DSPHLE_UcodeBase();
void Reset();
void DoSavestate(Savestate* file);
@ -70,16 +72,35 @@ public:
void ClearSemaphore(u16 val);
void MaskSemaphore(u16 val);
void SetSemaphoreOut(u16 val);
void Start();
void Run(u32 cycles);
protected:
melonDS::DSi& DSi;
u16* DataMemory;
u16 CmdReg[3];
bool CmdWritten[3];
u16 ReplyReg[3];
bool ReplyWritten[3];
fnReplyReadCb ReplyReadCb[3];
u16 SemaphoreIn; // ARM9 -> DSP
u16 SemaphoreOut; // DSP -> ARM9
u16 SemaphoreMask; // DSP -> ARM9
u16 UcodeCmd;
u16* LoadPipe(u8 index);
u32 GetPipeLength(u16* pipe);
u32 ReadPipe(u16* pipe, u16* data, u32 len);
void RunUcodeCmd();
void OnUcodeCmdFinish(u32 param);
void UcodeCmd_Scaling(u16* pipe);
};
}

View File

@ -56,7 +56,7 @@ u16 DSi_DSP::GetPSTS() const
if ( HleCore->RecvDataIsReady(0)) r |= 1<<10;
if ( HleCore->RecvDataIsReady(1)) r |= 1<<11;
if ( HleCore->RecvDataIsReady(2)) r |= 1<<12;
//printf("GetPSTS: %04X\n", r); 8100
return r;
}
@ -119,7 +119,7 @@ DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi)
DSi.RegisterEventFuncs(Event_DSi_DSP, this, {MakeEventThunk(DSi_DSP, DSPCatchUpU32)});
//TeakraCore = new Teakra::Teakra();
HleCore = new DSPHLE_UcodeBase();
HleCore = new DSPHLE_UcodeBase(DSi);
SCFG_RST = false;
// ????
@ -254,7 +254,7 @@ void DSi_DSP::PDataDMAWrite(u16 wrval)
//addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
//TeakraCore->DataWriteA32(addr, wrval);
addr |= (u32)HleCore->DMAChan0GetDstHigh() << 16;
HleCore->DataWriteA32(addr, wrval);
HleCore->DataWriteA32(addr, wrval);
break;
case 1<<12: // mmio
//TeakraCore->MMIOWrite(addr & 0x7FF, wrval);
@ -356,7 +356,7 @@ u16 DSi_DSP::PDataDMARead()
}
break;
default: return r;
}printf("DSP: PDATA read %08X -> %04X\n", addr, r);
}printf("DSP: PDATA read %08X -> %04X (%04X)\n", addr, r, DSP_PCFG);
if (DSP_PCFG & (1<<1)) // auto-increment
++DSP_PADR; // overflows and stays within a 64k 'page' // TODO: is this +1 or +2?

View File

@ -72,6 +72,7 @@ enum
Event_DSi_CamIRQ,
Event_DSi_CamTransfer,
Event_DSi_DSP,
Event_DSi_DSPHLE, // TODO use same event for both flavors of DSP?
Event_MAX
};