mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-25 07:10:00 -06:00
lay base for DSP core interface (so it can switch between LLE and HLE)
This commit is contained in:
@ -58,7 +58,8 @@ add_library(core STATIC
|
||||
Wifi.cpp
|
||||
WifiAP.cpp
|
||||
|
||||
DSP_HLE/Ucode_Base.cpp
|
||||
DSP_HLE/UcodeBase.cpp
|
||||
DSP_HLE/GraphicsUcode.cpp
|
||||
|
||||
fatfs/ff.c
|
||||
fatfs/ffsystem.c
|
||||
|
@ -59,8 +59,17 @@ u32 CRC32(const u8 *data, int len, u32 start)
|
||||
|
||||
u32 crc = start ^ 0xFFFFFFFF;
|
||||
|
||||
while (len--)
|
||||
crc = (crc >> 8) ^ Crc32Table[(crc & 0xFF) ^ *data++];
|
||||
if (data)
|
||||
{
|
||||
while (len--)
|
||||
crc = (crc >> 8) ^ Crc32Table[(crc & 0xFF) ^ *data++];
|
||||
}
|
||||
else
|
||||
{
|
||||
// null data acts like checksumming a block of zeros
|
||||
while (len--)
|
||||
crc = (crc >> 8) ^ Crc32Table[(crc & 0xFF)];
|
||||
}
|
||||
|
||||
return (crc ^ 0xFFFFFFFF);
|
||||
}
|
||||
|
235
src/DSP_HLE/GraphicsUcode.cpp
Normal file
235
src/DSP_HLE/GraphicsUcode.cpp
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
melonDS is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#include "../DSi.h"
|
||||
#include "GraphicsUcode.h"
|
||||
#include "../Platform.h"
|
||||
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
namespace DSP_HLE
|
||||
{
|
||||
|
||||
|
||||
GraphicsUcode::GraphicsUcode(melonDS::DSi& dsi, int version) : UcodeBase(dsi)
|
||||
{
|
||||
Log(LogLevel::Info, "DSP_HLE: initializing Graphics SDK ucode version %d\n", version);
|
||||
}
|
||||
|
||||
GraphicsUcode::~GraphicsUcode()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
void GraphicsUcode::Reset()
|
||||
{
|
||||
UcodeBase::Reset();
|
||||
}
|
||||
|
||||
void GraphicsUcode::DoSavestate(Savestate *file)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
void GraphicsUcode::SendData(u8 index, u16 val)
|
||||
{
|
||||
UcodeBase::SendData(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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GraphicsUcode::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 GraphicsUcode::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 GraphicsUcode::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);
|
||||
|
||||
if (filter == 2)
|
||||
{
|
||||
// bilinear
|
||||
|
||||
for (u32 y = 0; y < dst_height; y++)
|
||||
{
|
||||
u32 sy = (y * y_factor) + 0x200;// + 0x3FF;
|
||||
u32 syf = sy & 0x3FF;
|
||||
u32 src_line1 = src_addr + (((sy >> 10) * src_width) << 1);
|
||||
u32 src_line2 = src_line1 + (src_width << 1);
|
||||
|
||||
for (u32 x = 0; x < dst_width; x++)
|
||||
{
|
||||
u32 sx = (x * x_factor) + 0x200;// + 0x3FF;
|
||||
u32 sxf = sx & 0x3FF;
|
||||
|
||||
// TODO caching? see what the ucode does
|
||||
// ucode loads enough lines to fill 32K buffer (16K dsp words)
|
||||
// keeps last scanline from previous buffer
|
||||
// uses 32bit DMA
|
||||
// also starting pos is 0x200 (0.5), 0x600 for bicubic
|
||||
u16 v[4];
|
||||
v[0] = DSi.ARM9Read16(src_line1 + ((sx >> 10) << 1));
|
||||
v[1] = DSi.ARM9Read16(src_line1 + ((sx >> 10) << 1) + 2);
|
||||
v[2] = DSi.ARM9Read16(src_line2 + ((sx >> 10) << 1));
|
||||
v[3] = DSi.ARM9Read16(src_line2 + ((sx >> 10) << 1) + 2);
|
||||
|
||||
u16 r[4], g[4], b[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
r[i] = v[i] & 0x1F;
|
||||
g[i] = (v[i] >> 5) & 0x1F;
|
||||
b[i] = (v[i] >> 10) & 0x1F;
|
||||
}
|
||||
|
||||
u32 f_r, f_g, f_b;
|
||||
u32 t1, t2;
|
||||
|
||||
t1 = (r[0] * (0x400-sxf)) + (r[1] * sxf);
|
||||
t2 = (r[2] * (0x400-sxf)) + (r[3] * sxf);
|
||||
f_r = (t1 * (0x400-syf)) + (t2 * syf);
|
||||
f_r = (f_r >> 20) & 0x1F;
|
||||
|
||||
t1 = (g[0] * (0x400-sxf)) + (g[1] * sxf);
|
||||
t2 = (g[2] * (0x400-sxf)) + (g[3] * sxf);
|
||||
f_g = (t1 * (0x400-syf)) + (t2 * syf);
|
||||
f_g = (f_g >> 15) & 0x3E0;
|
||||
|
||||
t1 = (b[0] * (0x400-sxf)) + (b[1] * sxf);
|
||||
t2 = (b[2] * (0x400-sxf)) + (b[3] * sxf);
|
||||
f_b = (t1 * (0x400-syf)) + (t2 * syf);
|
||||
f_b = (f_b >> 10) & 0x7C00;
|
||||
|
||||
DSi.ARM9Write16(dst_addr, f_r | f_g | f_b | 0x8000);
|
||||
|
||||
dst_addr += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (filter == 3)
|
||||
{
|
||||
// bicubic
|
||||
}
|
||||
else
|
||||
{
|
||||
// nearest neighbor
|
||||
|
||||
for (u32 y = 0; y < dst_height; y++)
|
||||
{
|
||||
u32 sy = ((y * y_factor) + 0x3FF) >> 10;
|
||||
u32 src_line = src_addr + ((sy * src_width) << 1);
|
||||
|
||||
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);
|
||||
|
||||
dst_addr += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
56
src/DSP_HLE/GraphicsUcode.h
Normal file
56
src/DSP_HLE/GraphicsUcode.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
melonDS is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#ifndef GRAPHICSUCODE_H
|
||||
#define GRAPHICSUCODE_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "UcodeBase.h"
|
||||
#include "../Savestate.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
namespace DSP_HLE
|
||||
{
|
||||
|
||||
class GraphicsUcode : public UcodeBase
|
||||
{
|
||||
public:
|
||||
GraphicsUcode(melonDS::DSi& dsi, int version);
|
||||
~GraphicsUcode();
|
||||
void Reset() override;
|
||||
void DoSavestate(Savestate* file) override;
|
||||
|
||||
typedef std::function<void()> fnReplyReadCb;
|
||||
|
||||
//void SetRecvDataHandler(u8 index, std::function<void()> func);
|
||||
//void SetSemaphoreHandler(std::function<void()> func);
|
||||
|
||||
void SendData(u8 index, u16 val) override;
|
||||
|
||||
protected:
|
||||
void RunUcodeCmd();
|
||||
void OnUcodeCmdFinish(u32 param);
|
||||
void UcodeCmd_Scaling(u16* pipe);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GRAPHICSUCODE_H
|
@ -17,8 +17,7 @@
|
||||
*/
|
||||
|
||||
#include "../DSi.h"
|
||||
#include "../DSi_DSP.h"
|
||||
#include "Ucode_Base.h"
|
||||
#include "UcodeBase.h"
|
||||
#include "../Platform.h"
|
||||
|
||||
|
||||
@ -27,18 +26,21 @@ namespace melonDS
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
|
||||
DSPHLE_UcodeBase::DSPHLE_UcodeBase(melonDS::DSi& dsi) : DSi(dsi)
|
||||
namespace DSP_HLE
|
||||
{
|
||||
DSi.RegisterEventFuncs(Event_DSi_DSPHLE, this, {MakeEventThunk(DSPHLE_UcodeBase, OnUcodeCmdFinish)});
|
||||
|
||||
|
||||
UcodeBase::UcodeBase(melonDS::DSi& dsi) : DSi(dsi)
|
||||
{
|
||||
DSi.RegisterEventFuncs(Event_DSi_DSPHLE, this, {MakeEventThunk(UcodeBase, OnUcodeCmdFinish)});
|
||||
}
|
||||
|
||||
DSPHLE_UcodeBase::~DSPHLE_UcodeBase()
|
||||
UcodeBase::~UcodeBase()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
void DSPHLE_UcodeBase::Reset()
|
||||
void UcodeBase::Reset()
|
||||
{
|
||||
DataMemory = nullptr;
|
||||
|
||||
@ -46,7 +48,6 @@ void DSPHLE_UcodeBase::Reset()
|
||||
memset(CmdWritten, 0, sizeof(CmdWritten));
|
||||
memset(ReplyReg, 0, sizeof(ReplyReg));
|
||||
memset(ReplyWritten, 0, sizeof(ReplyWritten));
|
||||
//memset(ReplyReadCb, 0, sizeof(ReplyReadCb));
|
||||
ReplyReadCb[0] = nullptr;
|
||||
ReplyReadCb[1] = nullptr;
|
||||
ReplyReadCb[2] = nullptr;
|
||||
@ -58,23 +59,23 @@ void DSPHLE_UcodeBase::Reset()
|
||||
UcodeCmd = 0;
|
||||
}
|
||||
|
||||
void DSPHLE_UcodeBase::DoSavestate(Savestate *file)
|
||||
void UcodeBase::DoSavestate(Savestate *file)
|
||||
{
|
||||
//
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
||||
bool DSPHLE_UcodeBase::RecvDataIsReady(u8 index)
|
||||
bool UcodeBase::RecvDataIsReady(u8 index) const
|
||||
{
|
||||
return ReplyWritten[index];
|
||||
}
|
||||
|
||||
bool DSPHLE_UcodeBase::SendDataIsEmpty(u8 index)
|
||||
bool UcodeBase::SendDataIsEmpty(u8 index) const
|
||||
{
|
||||
return !CmdWritten[index];
|
||||
}
|
||||
|
||||
u16 DSPHLE_UcodeBase::RecvData(u8 index)
|
||||
u16 UcodeBase::RecvData(u8 index)
|
||||
{
|
||||
if (!ReplyWritten[index]) printf("DSP: receive reply%d but empty\n", index);
|
||||
if (!ReplyWritten[index]) return 0; // CHECKME
|
||||
@ -91,7 +92,7 @@ printf("DSP: receive reply%d %04X\n", index, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DSPHLE_UcodeBase::SendData(u8 index, u16 val)
|
||||
void UcodeBase::SendData(u8 index, u16 val)
|
||||
{
|
||||
// TODO less ambiguous naming for those functions
|
||||
if (CmdWritten[index])
|
||||
@ -104,35 +105,11 @@ void DSPHLE_UcodeBase::SendData(u8 index, u16 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();
|
||||
}
|
||||
// extra shit shall be implemented in subclasses
|
||||
}
|
||||
|
||||
|
||||
void DSPHLE_UcodeBase::SendReply(u8 index, u16 val)
|
||||
void UcodeBase::SendReply(u8 index, u16 val)
|
||||
{
|
||||
if (ReplyWritten[index])
|
||||
{
|
||||
@ -151,37 +128,43 @@ void DSPHLE_UcodeBase::SendReply(u8 index, u16 val)
|
||||
}
|
||||
}
|
||||
|
||||
void DSPHLE_UcodeBase::SetReplyReadCallback(u8 index, fnReplyReadCb callback)
|
||||
void UcodeBase::SetReplyReadCallback(u8 index, fnReplyReadCb callback)
|
||||
{
|
||||
ReplyReadCb[index] = callback;
|
||||
}
|
||||
|
||||
|
||||
u16 DSPHLE_UcodeBase::DMAChan0GetDstHigh()
|
||||
u16 UcodeBase::DMAChan0GetSrcHigh()
|
||||
{
|
||||
// TODO?
|
||||
return 0;
|
||||
}
|
||||
|
||||
u16 DSPHLE_UcodeBase::AHBMGetDmaChannel(u16 index)
|
||||
u16 UcodeBase::DMAChan0GetDstHigh()
|
||||
{
|
||||
// TODO?
|
||||
return 0;
|
||||
}
|
||||
|
||||
u16 UcodeBase::AHBMGetDmaChannel(u16 index) const
|
||||
{
|
||||
//
|
||||
return 0;
|
||||
}
|
||||
|
||||
u16 DSPHLE_UcodeBase::AHBMGetDirection(u16 index)
|
||||
u16 UcodeBase::AHBMGetDirection(u16 index) const
|
||||
{
|
||||
//
|
||||
return 0;
|
||||
}
|
||||
|
||||
u16 DSPHLE_UcodeBase::AHBMGetUnitSize(u16 index)
|
||||
u16 UcodeBase::AHBMGetUnitSize(u16 index) const
|
||||
{
|
||||
//
|
||||
return 0;
|
||||
}
|
||||
|
||||
u16 DSPHLE_UcodeBase::DataReadA32(u32 addr)
|
||||
u16 UcodeBase::DataReadA32(u32 addr) const
|
||||
{
|
||||
printf("ucode: DataReadA32 %08X\n", addr);
|
||||
|
||||
@ -198,7 +181,7 @@ u16 DSPHLE_UcodeBase::DataReadA32(u32 addr)
|
||||
}
|
||||
}
|
||||
|
||||
void DSPHLE_UcodeBase::DataWriteA32(u32 addr, u16 val)
|
||||
void UcodeBase::DataWriteA32(u32 addr, u16 val)
|
||||
{
|
||||
printf("ucode: DataWriteA32 %08X %04X\n", addr, val);
|
||||
|
||||
@ -215,71 +198,71 @@ void DSPHLE_UcodeBase::DataWriteA32(u32 addr, u16 val)
|
||||
}
|
||||
}
|
||||
|
||||
u16 DSPHLE_UcodeBase::MMIORead(u16 addr)
|
||||
u16 UcodeBase::MMIORead(u16 addr)
|
||||
{
|
||||
//
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DSPHLE_UcodeBase::MMIOWrite(u16 addr, u16 val)
|
||||
void UcodeBase::MMIOWrite(u16 addr, u16 val)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
u16 DSPHLE_UcodeBase::ProgramRead(u32 addr)
|
||||
u16 UcodeBase::ProgramRead(u32 addr) const
|
||||
{
|
||||
//
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DSPHLE_UcodeBase::ProgramWrite(u32 addr, u16 val)
|
||||
void UcodeBase::ProgramWrite(u32 addr, u16 val)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
u16 DSPHLE_UcodeBase::AHBMRead16(u32 addr)
|
||||
u16 UcodeBase::AHBMRead16(u32 addr)
|
||||
{
|
||||
//
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 DSPHLE_UcodeBase::AHBMRead32(u32 addr)
|
||||
u16 UcodeBase::AHBMRead32(u32 addr)
|
||||
{
|
||||
//
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DSPHLE_UcodeBase::AHBMWrite16(u32 addr, u16 val)
|
||||
void UcodeBase::AHBMWrite16(u32 addr, u16 val)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
void DSPHLE_UcodeBase::AHBMWrite32(u32 addr, u32 val)
|
||||
void UcodeBase::AHBMWrite32(u32 addr, u32 val)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
u16 DSPHLE_UcodeBase::GetSemaphore()
|
||||
u16 UcodeBase::GetSemaphore() const
|
||||
{
|
||||
return SemaphoreOut;
|
||||
}
|
||||
|
||||
void DSPHLE_UcodeBase::SetSemaphore(u16 val)
|
||||
void UcodeBase::SetSemaphore(u16 val)
|
||||
{
|
||||
SemaphoreIn |= val;
|
||||
}
|
||||
|
||||
void DSPHLE_UcodeBase::ClearSemaphore(u16 val)
|
||||
void UcodeBase::ClearSemaphore(u16 val)
|
||||
{
|
||||
SemaphoreOut &= ~val;
|
||||
}
|
||||
|
||||
void DSPHLE_UcodeBase::MaskSemaphore(u16 val)
|
||||
void UcodeBase::MaskSemaphore(u16 val)
|
||||
{
|
||||
SemaphoreMask = val;
|
||||
}
|
||||
|
||||
void DSPHLE_UcodeBase::SetSemaphoreOut(u16 val)
|
||||
void UcodeBase::SetSemaphoreOut(u16 val)
|
||||
{
|
||||
SemaphoreOut |= val;
|
||||
if (SemaphoreOut & (~SemaphoreMask))
|
||||
@ -287,7 +270,7 @@ void DSPHLE_UcodeBase::SetSemaphoreOut(u16 val)
|
||||
}
|
||||
|
||||
|
||||
void DSPHLE_UcodeBase::Start()
|
||||
void UcodeBase::Start()
|
||||
{
|
||||
printf("DSP HLE: start\n");
|
||||
// TODO later: detect which ucode it is and create the right class!
|
||||
@ -322,13 +305,7 @@ void DSPHLE_UcodeBase::Start()
|
||||
}
|
||||
|
||||
|
||||
void DSPHLE_UcodeBase::Run(u32 cycles)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
u16* DSPHLE_UcodeBase::LoadPipe(u8 index)
|
||||
u16* UcodeBase::LoadPipe(u8 index)
|
||||
{
|
||||
const u16 pipeaddr = 0x0800;
|
||||
u16* mem = (u16*)DSi.NWRAMMap_C[2][0];
|
||||
@ -337,7 +314,7 @@ u16* DSPHLE_UcodeBase::LoadPipe(u8 index)
|
||||
return pipe;
|
||||
}
|
||||
|
||||
u32 DSPHLE_UcodeBase::GetPipeLength(u16* pipe)
|
||||
u32 UcodeBase::GetPipeLength(u16* pipe)
|
||||
{
|
||||
u32 ret;
|
||||
u16 rdptr = pipe[2];
|
||||
@ -356,7 +333,7 @@ u32 DSPHLE_UcodeBase::GetPipeLength(u16* pipe)
|
||||
return ret >> 1;
|
||||
}
|
||||
|
||||
u32 DSPHLE_UcodeBase::ReadPipe(u16* pipe, u16* data, u32 len)
|
||||
u32 UcodeBase::ReadPipe(u16* pipe, u16* data, u32 len)
|
||||
{
|
||||
u16* mem = (u16*)DSi.NWRAMMap_C[2][0];
|
||||
u16* pipebuf = &mem[pipe[0]];
|
||||
@ -380,86 +357,23 @@ printf("-> rd=%d\n", rdptr);
|
||||
return rdlen;
|
||||
}
|
||||
|
||||
void DSPHLE_UcodeBase::RunUcodeCmd()
|
||||
void 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)
|
||||
void 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -16,68 +16,58 @@
|
||||
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#ifndef UCODE_BASE_H
|
||||
#define UCODE_BASE_H
|
||||
#ifndef UCODEBASE_H
|
||||
#define UCODEBASE_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "../types.h"
|
||||
#include "../DSi_DSP.h"
|
||||
#include "../Savestate.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
namespace DSP_HLE
|
||||
{
|
||||
|
||||
class DSi;
|
||||
|
||||
class DSPHLE_UcodeBase
|
||||
class UcodeBase: public DSPInterface
|
||||
{
|
||||
public:
|
||||
DSPHLE_UcodeBase(melonDS::DSi& dsi);
|
||||
~DSPHLE_UcodeBase();
|
||||
void Reset();
|
||||
void DoSavestate(Savestate* file);
|
||||
UcodeBase(melonDS::DSi& dsi);
|
||||
virtual ~UcodeBase();
|
||||
virtual void Reset();
|
||||
virtual void DoSavestate(Savestate* file);
|
||||
|
||||
typedef std::function<void()> fnReplyReadCb;
|
||||
|
||||
//void SetRecvDataHandler(u8 index, std::function<void()> func);
|
||||
//void SetSemaphoreHandler(std::function<void()> func);
|
||||
|
||||
bool RecvDataIsReady(u8 index);
|
||||
bool SendDataIsEmpty(u8 index);
|
||||
bool RecvDataIsReady(u8 index) const;
|
||||
bool SendDataIsEmpty(u8 index) const;
|
||||
u16 RecvData(u8 index);
|
||||
void SendData(u8 index, u16 val);
|
||||
|
||||
// TODO receive cmd
|
||||
void SendReply(u8 index, u16 val);
|
||||
void SetReplyReadCallback(u8 index, fnReplyReadCb callback);
|
||||
virtual void SendData(u8 index, u16 val);
|
||||
|
||||
u16 DMAChan0GetSrcHigh();
|
||||
u16 DMAChan0GetDstHigh();
|
||||
u16 AHBMGetDmaChannel(u16 index);
|
||||
u16 AHBMGetDirection(u16 index);
|
||||
u16 AHBMGetUnitSize(u16 index);
|
||||
u16 AHBMGetDmaChannel(u16 index) const;
|
||||
u16 AHBMGetDirection(u16 index) const;
|
||||
u16 AHBMGetUnitSize(u16 index) const;
|
||||
|
||||
u16 DataReadA32(u32 addr);
|
||||
u16 DataReadA32(u32 addr) const;
|
||||
void DataWriteA32(u32 addr, u16 val);
|
||||
u16 MMIORead(u16 addr);
|
||||
void MMIOWrite(u16 addr, u16 val);
|
||||
u16 ProgramRead(u32 addr);
|
||||
u16 ProgramRead(u32 addr) const;
|
||||
void ProgramWrite(u32 addr, u16 val);
|
||||
u16 AHBMRead16(u32 addr);
|
||||
u32 AHBMRead32(u32 addr);
|
||||
u16 AHBMRead32(u32 addr);
|
||||
void AHBMWrite16(u32 addr, u16 val);
|
||||
void AHBMWrite32(u32 addr, u32 val);
|
||||
|
||||
u16 GetSemaphore();
|
||||
u16 GetSemaphore() const;
|
||||
void SetSemaphore(u16 val);
|
||||
void ClearSemaphore(u16 val);
|
||||
void MaskSemaphore(u16 val);
|
||||
|
||||
void SetSemaphoreOut(u16 val);
|
||||
|
||||
void Start();
|
||||
|
||||
void Run(u32 cycles);
|
||||
|
||||
protected:
|
||||
melonDS::DSi& DSi;
|
||||
u16* DataMemory;
|
||||
@ -94,15 +84,20 @@ protected:
|
||||
|
||||
u16 UcodeCmd;
|
||||
|
||||
void SendReply(u8 index, u16 val);
|
||||
void SetReplyReadCallback(u8 index, fnReplyReadCb callback);
|
||||
|
||||
void SetSemaphoreOut(u16 val);
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UCODE_BASE_H
|
||||
#endif // UCODEBASE_H
|
347
src/DSi_DSP.cpp
347
src/DSi_DSP.cpp
@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
#include "teakra/include/teakra/teakra.h"
|
||||
#include "DSP_HLE/Ucode_Base.h"
|
||||
#include "DSP_HLE/GraphicsUcode.h"
|
||||
|
||||
#include "DSi.h"
|
||||
#include "DSi_DSP.h"
|
||||
@ -34,6 +34,9 @@ using Platform::LogLevel;
|
||||
const u32 DSi_DSP::DataMemoryOffset = 0x20000; // from Teakra memory_interface.h
|
||||
// NOTE: ^ IS IN DSP WORDS, NOT IN BYTES!
|
||||
|
||||
// TODO add proper setting for this!
|
||||
bool __temp_dsphle = true;
|
||||
|
||||
|
||||
u16 DSi_DSP::GetPSTS() const
|
||||
{
|
||||
@ -44,18 +47,15 @@ u16 DSi_DSP::GetPSTS() const
|
||||
if ( PDATAReadFifo.IsFull ()) r |= 1<<5;
|
||||
if (!PDATAReadFifo.IsEmpty()) r |=(1<<6)|(1<<0);
|
||||
|
||||
/*if (!TeakraCore->SendDataIsEmpty(0)) r |= 1<<13;
|
||||
if (!TeakraCore->SendDataIsEmpty(1)) r |= 1<<14;
|
||||
if (!TeakraCore->SendDataIsEmpty(2)) r |= 1<<15;
|
||||
if ( TeakraCore->RecvDataIsReady(0)) r |= 1<<10;
|
||||
if ( TeakraCore->RecvDataIsReady(1)) r |= 1<<11;
|
||||
if ( TeakraCore->RecvDataIsReady(2)) r |= 1<<12;*/
|
||||
if (!HleCore->SendDataIsEmpty(0)) r |= 1<<13;
|
||||
if (!HleCore->SendDataIsEmpty(1)) r |= 1<<14;
|
||||
if (!HleCore->SendDataIsEmpty(2)) r |= 1<<15;
|
||||
if ( HleCore->RecvDataIsReady(0)) r |= 1<<10;
|
||||
if ( HleCore->RecvDataIsReady(1)) r |= 1<<11;
|
||||
if ( HleCore->RecvDataIsReady(2)) r |= 1<<12;
|
||||
if (DSPCore)
|
||||
{
|
||||
if (!DSPCore->SendDataIsEmpty(0)) r |= 1 << 13;
|
||||
if (!DSPCore->SendDataIsEmpty(1)) r |= 1 << 14;
|
||||
if (!DSPCore->SendDataIsEmpty(2)) r |= 1 << 15;
|
||||
if (DSPCore->RecvDataIsReady(0)) r |= 1 << 10;
|
||||
if (DSPCore->RecvDataIsReady(1)) r |= 1 << 11;
|
||||
if (DSPCore->RecvDataIsReady(2)) r |= 1 << 12;
|
||||
}
|
||||
//printf("GetPSTS: %04X\n", r); 8100
|
||||
return r;
|
||||
}
|
||||
@ -114,29 +114,64 @@ void DSi_DSP::AudioCb(std::array<s16, 2> frame)
|
||||
// TODO
|
||||
}
|
||||
|
||||
DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi)
|
||||
|
||||
void DSi_DSP::StartDSPHLE()
|
||||
{
|
||||
DSi.RegisterEventFuncs(Event_DSi_DSP, this, {MakeEventThunk(DSi_DSP, DSPCatchUpU32)});
|
||||
// TODO
|
||||
// check CRC32 of code
|
||||
// fallback to Teakra if not found
|
||||
printf("create HLE core\n");
|
||||
|
||||
//TeakraCore = new Teakra::Teakra();
|
||||
HleCore = new DSPHLE_UcodeBase(DSi);
|
||||
SCFG_RST = false;
|
||||
u32 crc = 0;
|
||||
|
||||
// ????
|
||||
//if (!TeakraCore) return false;
|
||||
// Hash NWRAM B, which contains the DSP program
|
||||
// The hash should be in the DSP's memory view
|
||||
for (u32 addr = 0; addr < 0x40000; addr += 0x8000)
|
||||
{
|
||||
const u8* ptr = DSi.NWRAMMap_B[2][(addr >> 15) & 0x7];
|
||||
crc = CRC32(ptr, 0x8000, crc);
|
||||
}
|
||||
|
||||
printf("CRC = %08X\n", crc);
|
||||
|
||||
switch (crc)
|
||||
{
|
||||
case 0x63CAEC33: // Graphics SDK ucode v3
|
||||
DSPCore = new DSP_HLE::GraphicsUcode(DSi, 3);
|
||||
break;
|
||||
|
||||
default:
|
||||
StartDSPLLE();
|
||||
break;
|
||||
}
|
||||
|
||||
if (DSPCore)
|
||||
DSPCore->Reset();
|
||||
}
|
||||
|
||||
void DSi_DSP::StopDSP()
|
||||
{
|
||||
if (DSPCore) delete DSPCore;
|
||||
DSPCore = nullptr;
|
||||
}
|
||||
|
||||
void DSi_DSP::StartDSPLLE()
|
||||
{
|
||||
auto teakra = new Teakra::Teakra();
|
||||
DSPCore = teakra;
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
/*TeakraCore->SetRecvDataHandler(0, std::bind(&DSi_DSP::IrqRep0, this));
|
||||
TeakraCore->SetRecvDataHandler(1, std::bind(&DSi_DSP::IrqRep1, this));
|
||||
TeakraCore->SetRecvDataHandler(2, std::bind(&DSi_DSP::IrqRep2, this));
|
||||
teakra->SetRecvDataHandler(0, std::bind(&DSi_DSP::IrqRep0, this));
|
||||
teakra->SetRecvDataHandler(1, std::bind(&DSi_DSP::IrqRep1, this));
|
||||
teakra->SetRecvDataHandler(2, std::bind(&DSi_DSP::IrqRep2, this));
|
||||
|
||||
TeakraCore->SetSemaphoreHandler(std::bind(&DSi_DSP::IrqSem, this));
|
||||
teakra->SetSemaphoreHandler(std::bind(&DSi_DSP::IrqSem, this));
|
||||
|
||||
Teakra::SharedMemoryCallback smcb;
|
||||
smcb.read16 = std::bind(&DSi_DSP::DSPRead16, this, _1);
|
||||
smcb.write16 = std::bind(&DSi_DSP::DSPWrite16, this, _1, _2);
|
||||
TeakraCore->SetSharedMemoryCallback(smcb);
|
||||
teakra->SetSharedMemoryCallback(smcb);
|
||||
|
||||
// these happen instantaneously and without too much regard for bus aribtration
|
||||
// rules, so, this might have to be changed later on
|
||||
@ -147,9 +182,23 @@ DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi)
|
||||
cb.write16 = [this](auto addr, auto val) { DSi.ARM9Write16(addr, val); };
|
||||
cb.read32 = [this](auto addr) { return DSi.ARM9Read32(addr); };
|
||||
cb.write32 = [this](auto addr, auto val) { DSi.ARM9Write32(addr, val); };
|
||||
TeakraCore->SetAHBMCallback(cb);
|
||||
teakra->SetAHBMCallback(cb);
|
||||
|
||||
TeakraCore->SetAudioCallback(std::bind(&DSi_DSP::AudioCb, this, _1));*/
|
||||
teakra->SetAudioCallback(std::bind(&DSi_DSP::AudioCb, this, _1));
|
||||
}
|
||||
|
||||
|
||||
DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi)
|
||||
{
|
||||
DSi.RegisterEventFuncs(Event_DSi_DSP, this, {MakeEventThunk(DSi_DSP, DSPCatchUpU32)});
|
||||
|
||||
DSPCore = nullptr;
|
||||
SCFG_RST = false;
|
||||
|
||||
if (!__temp_dsphle)
|
||||
{
|
||||
StartDSPLLE();
|
||||
}
|
||||
|
||||
//PDATAReadFifo = new FIFO<u16>(16);
|
||||
//PDATAWriteFifo = new FIFO<u16>(16);
|
||||
@ -158,13 +207,10 @@ DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi)
|
||||
DSi_DSP::~DSi_DSP()
|
||||
{
|
||||
//if (PDATAWriteFifo) delete PDATAWriteFifo;
|
||||
//if (TeakraCore) delete TeakraCore;
|
||||
if (HleCore) delete HleCore;
|
||||
StopDSP();
|
||||
|
||||
//PDATAReadFifo = NULL;
|
||||
//PDATAWriteFifo = NULL;
|
||||
//TeakraCore = NULL;
|
||||
HleCore = nullptr;
|
||||
|
||||
DSi.UnregisterEventFuncs(Event_DSi_DSP);
|
||||
}
|
||||
@ -186,7 +232,7 @@ void DSi_DSP::Reset()
|
||||
PDATAReadFifo.Clear();
|
||||
//PDATAWriteFifo->Clear();
|
||||
//TeakraCore->Reset();
|
||||
HleCore->Reset();
|
||||
if (DSPCore) DSPCore->Reset();
|
||||
|
||||
DSi.CancelEvent(Event_DSi_DSP);
|
||||
|
||||
@ -248,58 +294,46 @@ void DSi_DSP::PDataDMAWrite(u16 wrval)
|
||||
{
|
||||
u32 addr = DSP_PADR;
|
||||
|
||||
switch (DSP_PCFG & (7<<12)) // memory region select
|
||||
if (DSPCore)
|
||||
{
|
||||
case 0<<12: // data
|
||||
//addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
|
||||
//TeakraCore->DataWriteA32(addr, wrval);
|
||||
addr |= (u32)HleCore->DMAChan0GetDstHigh() << 16;
|
||||
HleCore->DataWriteA32(addr, wrval);
|
||||
break;
|
||||
case 1<<12: // mmio
|
||||
//TeakraCore->MMIOWrite(addr & 0x7FF, wrval);
|
||||
HleCore->MMIOWrite(addr & 0x7FF, wrval);
|
||||
break;
|
||||
case 5<<12: // program
|
||||
//addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
|
||||
//TeakraCore->ProgramWrite(addr, wrval);
|
||||
addr |= (u32)HleCore->DMAChan0GetDstHigh() << 16;
|
||||
HleCore->ProgramWrite(addr, wrval);
|
||||
break;
|
||||
case 7<<12:
|
||||
#if 0
|
||||
addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
|
||||
// only do stuff when AHBM is configured correctly
|
||||
if (TeakraCore->AHBMGetDmaChannel(0) == 0 && TeakraCore->AHBMGetDirection(0) == 1/*W*/)
|
||||
switch (DSP_PCFG & (7<<12)) // memory region select
|
||||
{
|
||||
switch (TeakraCore->AHBMGetUnitSize(0))
|
||||
case 0<<12: // data
|
||||
//addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
|
||||
//TeakraCore->DataWriteA32(addr, wrval);
|
||||
addr |= (u32)DSPCore->DMAChan0GetDstHigh() << 16;
|
||||
DSPCore->DataWriteA32(addr, wrval);
|
||||
break;
|
||||
case 1<<12: // mmio
|
||||
//TeakraCore->MMIOWrite(addr & 0x7FF, wrval);
|
||||
DSPCore->MMIOWrite(addr & 0x7FF, wrval);
|
||||
break;
|
||||
case 5<<12: // program
|
||||
//addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
|
||||
//TeakraCore->ProgramWrite(addr, wrval);
|
||||
addr |= (u32)DSPCore->DMAChan0GetDstHigh() << 16;
|
||||
DSPCore->ProgramWrite(addr, wrval);
|
||||
break;
|
||||
case 7<<12:
|
||||
addr |= (u32)DSPCore->DMAChan0GetDstHigh() << 16;
|
||||
// only do stuff when AHBM is configured correctly
|
||||
if (DSPCore->AHBMGetDmaChannel(0) == 0 && DSPCore->AHBMGetDirection(0) == 1/*W*/)
|
||||
{
|
||||
case 0: /* 8bit */ DSi.ARM9Write8 (addr, (u8)wrval); break;
|
||||
case 1: /* 16 b */ TeakraCore->AHBMWrite16(addr, wrval); break;
|
||||
// does it work like this, or should it first buffer two u16's
|
||||
// until it has enough data to write to the actual destination?
|
||||
// -> this seems to be correct behavior!
|
||||
case 2: /* 32 b */ TeakraCore->AHBMWrite32(addr, wrval); break;
|
||||
switch (DSPCore->AHBMGetUnitSize(0))
|
||||
{
|
||||
case 0: /* 8bit */ DSi.ARM9Write8 (addr, (u8)wrval); break;
|
||||
case 1: /* 16 b */ DSPCore->AHBMWrite16(addr, wrval); break;
|
||||
// does it work like this, or should it first buffer two u16's
|
||||
// until it has enough data to write to the actual destination?
|
||||
// -> this seems to be correct behavior!
|
||||
case 2: /* 32 b */ DSPCore->AHBMWrite32(addr, wrval); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
#endif
|
||||
addr |= (u32)HleCore->DMAChan0GetDstHigh() << 16;
|
||||
// only do stuff when AHBM is configured correctly
|
||||
if (HleCore->AHBMGetDmaChannel(0) == 0 && HleCore->AHBMGetDirection(0) == 1/*W*/)
|
||||
{
|
||||
switch (HleCore->AHBMGetUnitSize(0))
|
||||
{
|
||||
case 0: /* 8bit */ DSi.ARM9Write8 (addr, (u8)wrval); break;
|
||||
case 1: /* 16 b */ HleCore->AHBMWrite16(addr, wrval); break;
|
||||
// does it work like this, or should it first buffer two u16's
|
||||
// until it has enough data to write to the actual destination?
|
||||
// -> this seems to be correct behavior!
|
||||
case 2: /* 32 b */ HleCore->AHBMWrite32(addr, wrval); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: return;
|
||||
}printf("DSP: PDATA write %08X -> %04X\n", addr, wrval);
|
||||
}
|
||||
printf("DSP: PDATA write %08X -> %04X\n", addr, wrval);
|
||||
|
||||
if (DSP_PCFG & (1<<1)) // auto-increment
|
||||
++DSP_PADR; // overflows and stays within a 64k 'page' // TODO: is this +1 or +2?
|
||||
@ -311,52 +345,44 @@ u16 DSi_DSP::PDataDMARead()
|
||||
{
|
||||
u16 r = 0;
|
||||
u32 addr = DSP_PADR;
|
||||
switch (DSP_PCFG & (7<<12)) // memory region select
|
||||
|
||||
if (DSPCore)
|
||||
{
|
||||
case 0<<12: // data
|
||||
//addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
|
||||
//r = TeakraCore->DataReadA32(addr);
|
||||
addr |= (u32)HleCore->DMAChan0GetDstHigh() << 16;
|
||||
r = HleCore->DataReadA32(addr);
|
||||
break;
|
||||
case 1<<12: // mmio
|
||||
//r = TeakraCore->MMIORead(addr & 0x7FF);
|
||||
r = HleCore->MMIORead(addr & 0x7FF);
|
||||
break;
|
||||
case 5<<12: // program
|
||||
//addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
|
||||
//r = TeakraCore->ProgramRead(addr);
|
||||
addr |= (u32)HleCore->DMAChan0GetDstHigh() << 16;
|
||||
r = HleCore->ProgramRead(addr);
|
||||
break;
|
||||
case 7<<12:
|
||||
#if 0
|
||||
addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
|
||||
// only do stuff when AHBM is configured correctly
|
||||
if (TeakraCore->AHBMGetDmaChannel(0) == 0 && TeakraCore->AHBMGetDirection(0) == 0/*R*/)
|
||||
switch (DSP_PCFG & (7<<12)) // memory region select
|
||||
{
|
||||
switch (TeakraCore->AHBMGetUnitSize(0))
|
||||
case 0<<12: // data
|
||||
//addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
|
||||
//r = TeakraCore->DataReadA32(addr);
|
||||
addr |= (u32)DSPCore->DMAChan0GetDstHigh() << 16;
|
||||
r = DSPCore->DataReadA32(addr);
|
||||
break;
|
||||
case 1<<12: // mmio
|
||||
//r = TeakraCore->MMIORead(addr & 0x7FF);
|
||||
r = DSPCore->MMIORead(addr & 0x7FF);
|
||||
break;
|
||||
case 5<<12: // program
|
||||
//addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
|
||||
//r = TeakraCore->ProgramRead(addr);
|
||||
addr |= (u32)DSPCore->DMAChan0GetDstHigh() << 16;
|
||||
r = DSPCore->ProgramRead(addr);
|
||||
break;
|
||||
case 7<<12:
|
||||
addr |= (u32)DSPCore->DMAChan0GetDstHigh() << 16;
|
||||
// only do stuff when AHBM is configured correctly
|
||||
if (DSPCore->AHBMGetDmaChannel(0) == 0 && DSPCore->AHBMGetDirection(0) == 0/*R*/)
|
||||
{
|
||||
case 0: /* 8bit */ r = DSi.ARM9Read8 (addr); break;
|
||||
case 1: /* 16 b */ r = TeakraCore->AHBMRead16(addr); break;
|
||||
case 2: /* 32 b */ r = (u16)TeakraCore->AHBMRead32(addr); break;
|
||||
switch (DSPCore->AHBMGetUnitSize(0))
|
||||
{
|
||||
case 0: /* 8bit */ r = DSi.ARM9Read8 (addr); break;
|
||||
case 1: /* 16 b */ r = DSPCore->AHBMRead16(addr); break;
|
||||
case 2: /* 32 b */ r = (u16)DSPCore->AHBMRead32(addr); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: return r;
|
||||
}
|
||||
#endif
|
||||
addr |= (u32)HleCore->DMAChan0GetDstHigh() << 16;
|
||||
// only do stuff when AHBM is configured correctly
|
||||
if (HleCore->AHBMGetDmaChannel(0) == 0 && HleCore->AHBMGetDirection(0) == 0/*R*/)
|
||||
{
|
||||
switch (HleCore->AHBMGetUnitSize(0))
|
||||
{
|
||||
case 0: /* 8bit */ r = DSi.ARM9Read8 (addr); break;
|
||||
case 1: /* 16 b */ r = HleCore->AHBMRead16(addr); break;
|
||||
case 2: /* 32 b */ r = (u16)HleCore->AHBMRead32(addr); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: return r;
|
||||
}printf("DSP: PDATA read %08X -> %04X (%04X)\n", addr, r, DSP_PCFG);
|
||||
}
|
||||
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?
|
||||
@ -444,8 +470,12 @@ u8 DSi_DSP::Read8(u32 addr)
|
||||
// no DSP_PCLEAR read
|
||||
//case 0x1C: return TeakraCore->GetSemaphore() & 0xFF; // SEM
|
||||
//case 0x1D: return TeakraCore->GetSemaphore() >> 8;
|
||||
case 0x1C: return HleCore->GetSemaphore() & 0xFF; // SEM
|
||||
case 0x1D: return HleCore->GetSemaphore() >> 8;
|
||||
case 0x1C:
|
||||
if (!DSPCore) return 0;
|
||||
return DSPCore->GetSemaphore() & 0xFF; // SEM
|
||||
case 0x1D:
|
||||
if (!DSPCore) return 0;
|
||||
return DSPCore->GetSemaphore() >> 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -468,8 +498,9 @@ u16 DSi_DSP::Read16(u32 addr)
|
||||
case 0x10: return DSP_PSEM;
|
||||
case 0x14: return DSP_PMASK;
|
||||
// no DSP_PCLEAR read
|
||||
//case 0x1C: return TeakraCore->GetSemaphore(); // SEM
|
||||
case 0x1C: return HleCore->GetSemaphore(); // SEM
|
||||
case 0x1C:
|
||||
if (!DSPCore) return 0;
|
||||
return DSPCore->GetSemaphore(); // SEM
|
||||
|
||||
case 0x20: return DSP_CMD[0];
|
||||
case 0x28: return DSP_CMD[1];
|
||||
@ -477,20 +508,20 @@ u16 DSi_DSP::Read16(u32 addr)
|
||||
|
||||
case 0x24:
|
||||
{
|
||||
//u16 r = TeakraCore->RecvData(0);printf("DSP: read CMD0, %04X\n", r);
|
||||
u16 r = HleCore->RecvData(0);printf("DSP: read CMD0, %04X\n", r);
|
||||
if (!DSPCore) return 0;
|
||||
u16 r = DSPCore->RecvData(0);printf("DSP: read CMD0, %04X\n", r);
|
||||
return r;
|
||||
}
|
||||
case 0x2C:
|
||||
{
|
||||
//u16 r = TeakraCore->RecvData(1);printf("DSP: read CMD1, %04X\n", r);
|
||||
u16 r = HleCore->RecvData(1);printf("DSP: read CMD1, %04X\n", r);
|
||||
if (!DSPCore) return 0;
|
||||
u16 r = DSPCore->RecvData(1);printf("DSP: read CMD1, %04X\n", r);
|
||||
return r;
|
||||
}
|
||||
case 0x34:
|
||||
{
|
||||
//u16 r = TeakraCore->RecvData(2);printf("DSP: read CMD2, %04X\n", r);
|
||||
u16 r = HleCore->RecvData(2);printf("DSP: read CMD2, %04X\n", r);
|
||||
if (!DSPCore) return 0;
|
||||
u16 r = DSPCore->RecvData(2);printf("DSP: read CMD2, %04X\n", r);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
@ -540,16 +571,18 @@ void DSi_DSP::Write16(u32 addr, u16 val)
|
||||
|
||||
case 0x08:
|
||||
if ((DSP_PCFG & (1<<0)) && (!(val & (1<<0))))
|
||||
HleCore->Start();
|
||||
DSP_PCFG = val;
|
||||
if (DSP_PCFG & (1<<0))
|
||||
//TeakraCore->Reset();
|
||||
HleCore->Reset();
|
||||
/*else if (!fazil)
|
||||
{
|
||||
fazil = true;
|
||||
DSi.debug(0);
|
||||
}*/
|
||||
if (__temp_dsphle)
|
||||
StartDSPHLE();
|
||||
}
|
||||
else if ((!(DSP_PCFG & (1<<0))) && (val & (1<<0)))
|
||||
{
|
||||
if (__temp_dsphle)
|
||||
StopDSP();
|
||||
else if (DSPCore)
|
||||
DSPCore->Reset();
|
||||
}
|
||||
DSP_PCFG = val;
|
||||
if (DSP_PCFG & (1<<4))
|
||||
PDataDMAStart();
|
||||
else
|
||||
@ -558,38 +591,40 @@ void DSi_DSP::Write16(u32 addr, u16 val)
|
||||
// no PSTS writes
|
||||
case 0x10:
|
||||
DSP_PSEM = val;
|
||||
//TeakraCore->SetSemaphore(val);
|
||||
HleCore->SetSemaphore(val);
|
||||
if (DSPCore)
|
||||
DSPCore->SetSemaphore(val);
|
||||
break;
|
||||
case 0x14:
|
||||
DSP_PMASK = val;
|
||||
//TeakraCore->MaskSemaphore(val);
|
||||
HleCore->MaskSemaphore(val);
|
||||
if (DSPCore)
|
||||
DSPCore->MaskSemaphore(val);
|
||||
break;
|
||||
case 0x18: // PCLEAR
|
||||
//TeakraCore->ClearSemaphore(val);
|
||||
HleCore->ClearSemaphore(val);
|
||||
//if (TeakraCore->GetSemaphore() == 0)
|
||||
if (HleCore->GetSemaphore() == 0)
|
||||
if (DSPCore)
|
||||
{
|
||||
DSPCore->ClearSemaphore(val);
|
||||
if (DSPCore->GetSemaphore() == 0)
|
||||
DSP_PSTS &= ~(1<<9);
|
||||
}
|
||||
else
|
||||
DSP_PSTS &= ~(1<<9);
|
||||
|
||||
break;
|
||||
// SEM not writable
|
||||
|
||||
case 0x20: // CMD0
|
||||
DSP_CMD[0] = val;printf("DSP: CMD0 = %04X\n", val);
|
||||
//TeakraCore->SendData(0, val);
|
||||
HleCore->SendData(0, val);
|
||||
if (DSPCore)
|
||||
DSPCore->SendData(0, val);
|
||||
break;
|
||||
case 0x28: // CMD1
|
||||
DSP_CMD[1] = val;printf("DSP: CMD1 = %04X\n", val);
|
||||
//TeakraCore->SendData(1, val);
|
||||
HleCore->SendData(1, val);
|
||||
if (DSPCore)
|
||||
DSPCore->SendData(1, val);
|
||||
break;
|
||||
case 0x30: // CMD2
|
||||
DSP_CMD[2] = val;printf("DSP: CMD2 = %04X\n", val);
|
||||
//TeakraCore->SendData(2, val);
|
||||
HleCore->SendData(2, val);
|
||||
if (DSPCore)
|
||||
DSPCore->SendData(2, val);
|
||||
break;
|
||||
|
||||
// no REPx writes
|
||||
@ -627,8 +662,8 @@ void DSi_DSP::Run(u32 cycles)
|
||||
return;
|
||||
}
|
||||
|
||||
//TeakraCore->Run(cycles);
|
||||
HleCore->Run(cycles);
|
||||
if (DSPCore)
|
||||
DSPCore->Run(cycles);
|
||||
|
||||
DSPTimestamp += cycles;
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "types.h"
|
||||
#include "Savestate.h"
|
||||
#include "FIFO.h"
|
||||
|
||||
// TODO: for actual sound output
|
||||
// * audio callbacks
|
||||
@ -29,8 +30,60 @@ namespace Teakra { class Teakra; }
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
class DSi;
|
||||
class DSPHLE_UcodeBase;
|
||||
|
||||
class DSPInterface
|
||||
{
|
||||
public:
|
||||
virtual ~DSPInterface() {};
|
||||
|
||||
virtual void Reset() = 0;
|
||||
virtual void DoSavestate(Savestate* file) = 0;
|
||||
|
||||
// APBP Data
|
||||
virtual bool SendDataIsEmpty(std::uint8_t index) const = 0;
|
||||
virtual void SendData(std::uint8_t index, std::uint16_t value) = 0;
|
||||
virtual bool RecvDataIsReady(std::uint8_t index) const = 0;
|
||||
virtual std::uint16_t RecvData(std::uint8_t index) = 0;
|
||||
//virtual std::uint16_t PeekRecvData(std::uint8_t index) = 0;
|
||||
|
||||
// APBP Semaphore
|
||||
virtual void SetSemaphore(std::uint16_t value) = 0;
|
||||
virtual void ClearSemaphore(std::uint16_t value) = 0;
|
||||
virtual void MaskSemaphore(std::uint16_t value) = 0;
|
||||
virtual std::uint16_t GetSemaphore() const = 0;
|
||||
|
||||
// for implementing DSP_PDATA/PADR DMA transfers
|
||||
virtual std::uint16_t ProgramRead(std::uint32_t address) const = 0;
|
||||
virtual void ProgramWrite(std::uint32_t address, std::uint16_t value) = 0;
|
||||
//virtual std::uint16_t DataRead(std::uint16_t address, bool bypass_mmio = false) = 0;
|
||||
//virtual void DataWrite(std::uint16_t address, std::uint16_t value, bool bypass_mmio = false) = 0;
|
||||
virtual std::uint16_t DataReadA32(std::uint32_t address) const = 0;
|
||||
virtual void DataWriteA32(std::uint32_t address, std::uint16_t value) = 0;
|
||||
virtual std::uint16_t MMIORead(std::uint16_t address) = 0;
|
||||
virtual void MMIOWrite(std::uint16_t address, std::uint16_t value) = 0;
|
||||
|
||||
// DSP_PADR is only 16-bit, so this is where the DMA interface gets the
|
||||
// upper 16-bits from
|
||||
virtual std::uint16_t DMAChan0GetSrcHigh() = 0;
|
||||
virtual std::uint16_t DMAChan0GetDstHigh() = 0;
|
||||
|
||||
virtual std::uint16_t AHBMGetUnitSize(std::uint16_t i) const = 0;
|
||||
virtual std::uint16_t AHBMGetDirection(std::uint16_t i) const = 0;
|
||||
virtual std::uint16_t AHBMGetDmaChannel(std::uint16_t i) const = 0;
|
||||
// we need these as AHBM does some weird stuff on unaligned accesses internally
|
||||
virtual std::uint16_t AHBMRead16(std::uint32_t addr) = 0;
|
||||
virtual void AHBMWrite16(std::uint32_t addr, std::uint16_t value) = 0;
|
||||
virtual std::uint16_t AHBMRead32(std::uint32_t addr) = 0;
|
||||
virtual void AHBMWrite32(std::uint32_t addr, std::uint32_t value) = 0;
|
||||
|
||||
// core
|
||||
virtual void Start() {};
|
||||
virtual void Run(unsigned cycle) {};
|
||||
};
|
||||
|
||||
class DSi_DSP
|
||||
{
|
||||
public:
|
||||
@ -39,6 +92,10 @@ public:
|
||||
void Reset();
|
||||
void DoSavestate(Savestate* file);
|
||||
|
||||
void StartDSPLLE();
|
||||
void StartDSPHLE();
|
||||
void StopDSP();
|
||||
|
||||
void DSPCatchUpU32(u32 _);
|
||||
|
||||
// SCFG_RST bit0
|
||||
@ -74,8 +131,7 @@ private:
|
||||
// not sure whether to not rather put it somewhere else
|
||||
u16 SNDExCnt;
|
||||
|
||||
Teakra::Teakra* TeakraCore;
|
||||
DSPHLE_UcodeBase* HleCore;
|
||||
DSPInterface* DSPCore;
|
||||
|
||||
bool SCFG_RST;
|
||||
|
||||
|
5
src/teakra/include/teakra/teakra.h
vendored
5
src/teakra/include/teakra/teakra.h
vendored
@ -5,6 +5,8 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "../../../DSi_DSP.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
struct SharedMemoryCallback {
|
||||
@ -23,12 +25,13 @@ struct AHBMCallback {
|
||||
std::function<void(std::uint32_t address, std::uint32_t value)> write32;
|
||||
};
|
||||
|
||||
class Teakra {
|
||||
class Teakra : public melonDS::DSPInterface {
|
||||
public:
|
||||
Teakra();
|
||||
~Teakra();
|
||||
|
||||
void Reset();
|
||||
void DoSavestate(melonDS::Savestate* file);
|
||||
|
||||
// APBP Data
|
||||
bool SendDataIsEmpty(std::uint8_t index) const;
|
||||
|
4
src/teakra/src/teakra.cpp
vendored
4
src/teakra/src/teakra.cpp
vendored
@ -70,6 +70,10 @@ void Teakra::Reset() {
|
||||
impl->Reset();
|
||||
}
|
||||
|
||||
void Teakra::DoSavestate(melonDS::Savestate* file) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void Teakra::Run(unsigned cycle) {
|
||||
impl->processor.Run(cycle);
|
||||
}
|
||||
|
Reference in New Issue
Block a user