diff --git a/src/DSP_HLE/Ucode_Base.cpp b/src/DSP_HLE/Ucode_Base.cpp index 954180bf..402d8c46 100644 --- a/src/DSP_HLE/Ucode_Base.cpp +++ b/src/DSP_HLE/Ucode_Base.cpp @@ -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); +} + + } \ No newline at end of file diff --git a/src/DSP_HLE/Ucode_Base.h b/src/DSP_HLE/Ucode_Base.h index acf5c180..8c400ab3 100644 --- a/src/DSP_HLE/Ucode_Base.h +++ b/src/DSP_HLE/Ucode_Base.h @@ -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); }; } diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp index 733c8687..bfb50fab 100644 --- a/src/DSi_DSP.cpp +++ b/src/DSi_DSP.cpp @@ -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? diff --git a/src/NDS.h b/src/NDS.h index e7340c76..ded1d119 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -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 };