Unverified Commit e3b4350f authored by purringChaos's avatar purringChaos Committed by GitHub
Browse files

Add PoroCYon's DSP code. (#1123)

* Add PoroCYon's DSP code.

* Remove some teakra iles that we dont need.

* make some requested changes.

* move DataMemoryOffset into namespace.

* use deault param.

* ad the switch change

* <Generic> forget about the default parameter
parent 2494058a
......@@ -18,6 +18,7 @@ add_library(core STATIC
DSi.cpp
DSi_AES.cpp
DSi_Camera.cpp
DSi_DSP.cpp
DSi_I2C.cpp
DSi_NDMA.cpp
DSi_NWifi.cpp
......@@ -100,6 +101,10 @@ if (ENABLE_JIT)
endif()
endif()
add_subdirectory(teakra EXCLUDE_FROM_ALL)
target_link_libraries(core teakra)
if (ENABLE_OGLRENDERER)
find_package(PkgConfig REQUIRED)
pkg_check_modules(EPOXY REQUIRED epoxy)
......
......@@ -36,6 +36,7 @@
#include "DSi_I2C.h"
#include "DSi_SD.h"
#include "DSi_AES.h"
#include "DSi_DSP.h"
#include "DSi_Camera.h"
#include "tiny-AES-c/aes.hpp"
......@@ -51,6 +52,7 @@ u16 SCFG_Clock9;
u16 SCFG_Clock7;
u32 SCFG_EXT[2];
u32 SCFG_MC;
u16 SCFG_RST;
u8 ARM9iBIOS[0x10000];
u8 ARM7iBIOS[0x10000];
......@@ -95,6 +97,7 @@ bool Init()
if (!DSi_I2C::Init()) return false;
if (!DSi_AES::Init()) return false;
if (!DSi_DSP::Init()) return false;
NDMAs[0] = new DSi_NDMA(0, 0);
NDMAs[1] = new DSi_NDMA(0, 1);
......@@ -121,6 +124,7 @@ void DeInit()
DSi_I2C::DeInit();
DSi_AES::DeInit();
DSi_DSP::DeInit();
for (int i = 0; i < 8; i++) delete NDMAs[i];
......@@ -146,6 +150,7 @@ void Reset()
DSi_I2C::Reset();
DSi_AES::Reset();
DSi_DSP::Reset();
SDMMC->Reset();
SDIO->Reset();
......@@ -156,6 +161,8 @@ void Reset()
SCFG_EXT[0] = 0x8307F100;
SCFG_EXT[1] = 0x93FFFB06;
SCFG_MC = 0x0010;//0x0011;
SCFG_RST = 0;
DSi_DSP::SetRstLine(false);
// LCD init flag
GPU::DispStat[0] |= (1<<6);
......@@ -202,6 +209,13 @@ void SoftReset()
DSi_AES::Reset();
DSi_AES::Reset();
// TODO: does the DSP get reset? NWRAM doesn't, so I'm assuming no
// *HOWEVER*, the bootrom (which does get rerun) does remap NWRAM, and thus
// the DSP most likely gets reset
DSi_DSP::Reset();
LoadNAND();
SDMMC->Reset();
......@@ -216,6 +230,10 @@ void SoftReset()
SCFG_EXT[0] = 0x8307F100;
SCFG_EXT[1] = 0x93FFFB06;
SCFG_MC = 0x0010;//0x0011;
// TODO: is this actually reset?
SCFG_RST = 0;
DSi_DSP::SetRstLine(false);
// LCD init flag
GPU::DispStat[0] |= (1<<6);
......@@ -602,6 +620,8 @@ void MapNWRAM_B(u32 num, u8 val)
u8* ptr = &NWRAM_B[num << 15];
DSi_DSP::OnMBKCfg('B', num, oldval, val, ptr);
if (oldval & 0x80)
{
if (oldval & 0x02) oldval &= 0xFE;
......@@ -641,6 +661,8 @@ void MapNWRAM_C(u32 num, u8 val)
u8* ptr = &NWRAM_C[num << 15];
DSi_DSP::OnMBKCfg('C', num, oldval, val, ptr);
if (oldval & 0x80)
{
if (oldval & 0x02) oldval &= 0xFE;
......@@ -1482,6 +1504,7 @@ u8 ARM9IORead8(u32 addr)
switch (addr)
{
case 0x04004000: return SCFG_BIOS & 0xFF;
case 0x04004006: return SCFG_RST & 0xFF;
CASE_READ8_32BIT(0x04004040, MBK[0][0])
CASE_READ8_32BIT(0x04004044, MBK[0][1])
......@@ -1500,6 +1523,9 @@ u8 ARM9IORead8(u32 addr)
return DSi_Camera::Read8(addr);
}
if (addr >= 0x04004300 && addr <= 0x04004400)
return DSi_DSP::Read16(addr);
return NDS::ARM9IORead8(addr);
}
......@@ -1509,6 +1535,7 @@ u16 ARM9IORead16(u32 addr)
{
case 0x04004000: return SCFG_BIOS & 0xFF;
case 0x04004004: return SCFG_Clock9;
case 0x04004006: return SCFG_RST;
case 0x04004010: return SCFG_MC & 0xFFFF;
CASE_READ16_32BIT(0x04004040, MBK[0][0])
......@@ -1528,6 +1555,9 @@ u16 ARM9IORead16(u32 addr)
return DSi_Camera::Read16(addr);
}
if (addr >= 0x04004300 && addr <= 0x04004400)
return DSi_DSP::Read32(addr);
return NDS::ARM9IORead16(addr);
}
......@@ -1536,6 +1566,7 @@ u32 ARM9IORead32(u32 addr)
switch (addr)
{
case 0x04004000: return SCFG_BIOS & 0xFF;
case 0x04004004: return SCFG_Clock9 | ((u32)SCFG_RST << 16);
case 0x04004008: return SCFG_EXT[0];
case 0x04004010: return SCFG_MC & 0xFFFF;
......@@ -1603,6 +1634,11 @@ void ARM9IOWrite8(u32 addr, u8 val)
//if (val == 0x80 && NDS::ARM9->R[15] == 0xFFFF0268) NDS::ARM9->Halt(1);
return;
case 0x04004006:
SCFG_RST = (SCFG_RST & 0xFF00) | val;
DSi_DSP::SetRstLine(val & 1);
return;
case 0x04004040: MapNWRAM_A(0, val); return;
case 0x04004041: MapNWRAM_A(1, val); return;
case 0x04004042: MapNWRAM_A(2, val); return;
......@@ -1631,6 +1667,12 @@ void ARM9IOWrite8(u32 addr, u8 val)
return DSi_Camera::Write8(addr, val);
}
if (addr >= 0x04004300 && addr <= 0x04004400)
{
DSi_DSP::Write8(addr, val);
return;
}
return NDS::ARM9IOWrite8(addr, val);
}
......@@ -1642,6 +1684,11 @@ void ARM9IOWrite16(u32 addr, u16 val)
Set_SCFG_Clock9(val);
return;
case 0x04004006:
SCFG_RST = val;
DSi_DSP::SetRstLine(val & 1);
return;
case 0x04004040:
MapNWRAM_A(0, val & 0xFF);
MapNWRAM_A(1, val >> 8);
......@@ -1690,6 +1737,12 @@ void ARM9IOWrite16(u32 addr, u16 val)
return DSi_Camera::Write16(addr, val);
}
if (addr >= 0x04004300 && addr <= 0x04004400)
{
DSi_DSP::Write16(addr, val);
return;
}
return NDS::ARM9IOWrite16(addr, val);
}
......@@ -1697,6 +1750,12 @@ void ARM9IOWrite32(u32 addr, u32 val)
{
switch (addr)
{
case 0x04004004:
Set_SCFG_Clock9(val & 0xFFFF);
SCFG_RST = val >> 16;
DSi_DSP::SetRstLine((val >> 16) & 1);
break;
case 0x04004008:
{
u32 oldram = (SCFG_EXT[0] >> 14) & 0x3;
......@@ -2106,6 +2165,13 @@ void ARM7IOWrite32(u32 addr, u32 val)
return;
}
if (addr >= 0x04004300 && addr <= 0x04004400)
{
DSi_DSP::Write32(addr, val);
return;
}
return NDS::ARM7IOWrite32(addr, val);
}
......
......@@ -26,6 +26,9 @@ namespace DSi
{
extern u16 SCFG_BIOS;
extern u16 SCFG_Clock9;
extern u32 SCFG_EXT[2];
extern u8 ARM9iBIOS[0x10000];
extern u8 ARM7iBIOS[0x10000];
......
/*
Copyright 2020 PoroCYon
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 "teakra/include/teakra/teakra.h"
#include "DSi.h"
#include "DSi_DSP.h"
#include "FIFO.h"
#include "NDS.h"
namespace DSi_DSP
{
Teakra::Teakra* TeakraCore;
bool SCFG_RST;
u16 DSP_PADR;
u16 DSP_PCFG;
u16 DSP_PSTS;
u16 DSP_PSEM;
u16 DSP_PMASK;
u16 DSP_PCLEAR;
u16 DSP_CMD[3];
u16 DSP_REP[3];
u64 DSPTimestamp;
FIFO<u16, 16> PDATAReadFifo/*, *PDATAWriteFifo*/;
int PDataDMALen = 0;
constexpr u32 DataMemoryOffset = 0x20000; // from Teakra memory_interface.h
// NOTE: ^ IS IN DSP WORDS, NOT IN BYTES!
u16 GetPSTS()
{
u16 r = DSP_PSTS & (1<<9); // this is the only sticky bit
//r &= ~((1<<2)|(1<<7)); // we support instant resets and wrfifo xfers
r |= (1<<8); // write fifo is always empty (inf. speed)
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;
return r;
}
void IrqRep0()
{
if (DSP_PCFG & (1<< 9)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
}
void IrqRep1()
{
if (DSP_PCFG & (1<<10)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
}
void IrqRep2()
{
if (DSP_PCFG & (1<<11)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
}
void IrqSem()
{
DSP_PSTS |= 1<<9;
// apparently these are always fired?
NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
}
void AudioCb(std::array<s16, 2> frame)
{
// TODO
}
bool Init()
{
TeakraCore = new Teakra::Teakra();
SCFG_RST = false;
if (!TeakraCore) return false;
TeakraCore->SetRecvDataHandler(0, IrqRep0);
TeakraCore->SetRecvDataHandler(1, IrqRep1);
TeakraCore->SetRecvDataHandler(2, IrqRep2);
TeakraCore->SetSemaphoreHandler(IrqSem);
// these happen instantaneously and without too much regard for bus aribtration
// rules, so, this might have to be changed later on
Teakra::AHBMCallback cb;
cb.read8 = DSi::ARM9Read8;
cb.write8 = DSi::ARM9Write8;
cb.read16 = DSi::ARM9Read16;
cb.write16 = DSi::ARM9Write16;
cb.read32 = DSi::ARM9Read32;
cb.write32 = DSi::ARM9Write32;
TeakraCore->SetAHBMCallback(cb);
TeakraCore->SetAudioCallback(AudioCb);
//PDATAReadFifo = new FIFO<u16>(16);
//PDATAWriteFifo = new FIFO<u16>(16);
return true;
}
void DeInit()
{
//if (PDATAWriteFifo) delete PDATAWriteFifo;
if (TeakraCore) delete TeakraCore;
//PDATAReadFifo = NULL;
//PDATAWriteFifo = NULL;
TeakraCore = NULL;
}
void Reset()
{
DSPTimestamp = 0;
DSP_PADR = 0;
DSP_PCFG = 0;
DSP_PSTS = 0;
DSP_PSEM = 0;
DSP_PMASK = 0xff;
DSP_PCLEAR = 0;
DSP_CMD[2] = DSP_CMD[1] = DSP_CMD[0] = 0;
DSP_REP[2] = DSP_REP[1] = DSP_REP[0] = 0;
PDataDMALen = 0;
PDATAReadFifo.Clear();
//PDATAWriteFifo->Clear();
TeakraCore->Reset();
NDS::CancelEvent(NDS::Event_DSi_DSP);
}
bool IsRstReleased()
{
return SCFG_RST;
}
void SetRstLine(bool release)
{
SCFG_RST = release;
Reset();
DSPTimestamp = NDS::ARM9Timestamp; // only start now!
}
void OnMBKCfg(char bank, u32 slot, u8 oldcfg, u8 newcfg, u8* nwrambacking)
{
if (bank != 'B' && bank != 'C')
{
printf("WTF?? (DSP MBK recfg, nonsense bank '%c')\n", bank);
return;
}
bool olddsp = (oldcfg & 3) >= 2, // was mapped to the DSP
newdsp = (newcfg & 3) >= 2; // will be mapped to the DSP
// nothing changes
if (olddsp == newdsp)
return;
const u8* src;
u8* dst;
if (newdsp)
{
// need to map stuff to DSP memory (== Teakra-owned memory) from NWRAM
src = nwrambacking;
dst = &TeakraCore->GetDspMemory()[((newcfg >> 2) & 7) << 15];
if (bank == 'C') // C: DSP data (B: DSP code)
dst += DataMemoryOffset*2;
}
else //if (olddsp)
{
// it was mapped to the DSP, but now we have to copy it out, back to NWRAM
src = &TeakraCore->GetDspMemory()[((oldcfg >> 2) & 7) << 15];
dst = nwrambacking;
if (bank == 'C') // C: DSP data (B: DSP code)
src += DataMemoryOffset*2;
}
memcpy(dst, src, 1<<15); // 1 full slot
}
inline bool IsDSPCoreEnabled()
{
return (DSi::SCFG_Clock9 & (1<<1)) && SCFG_RST && (DSP_PCFG & (1<<0));
}
bool DSPCatchUp()
{
//asm volatile("int3");
if (!IsDSPCoreEnabled())
{
// nothing to do, but advance the current time so that we don't do an
// unreasonable amount of cycles when rst is released
if (DSPTimestamp < NDS::ARM9Timestamp)
DSPTimestamp = NDS::ARM9Timestamp;
return false;
}
u64 curtime = NDS::ARM9Timestamp;
if (DSPTimestamp >= curtime) return true; // ummmm?!
u64 backlog = curtime - DSPTimestamp;
while (backlog & (1uLL<<32)) // god I hope this never happens
{
Run((u32)(backlog & ((1uLL<<32)-1)));
backlog = curtime - DSPTimestamp;
}
Run((u32)backlog);
return true;
}
void DSPCatchUpU32(u32 _) { DSPCatchUp(); }
void PDataDMAWrite(u16 wrval)
{
u32 addr = DSP_PADR;
switch (DSP_PCFG & (7<<12)) // memory region select
{
case 0<<12: // data
addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
TeakraCore->DataWriteA32(addr, wrval);
break;
case 1<<12: // mmio
TeakraCore->MMIOWrite(addr & 0x7FF, wrval);
break;
case 5<<12: // program
addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
TeakraCore->ProgramWrite(addr, wrval);
break;
case 7<<12:
addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
// only do stuff when AHBM is configured correctly
if (TeakraCore->AHBMGetDmaChannel(0) == 0 && TeakraCore->AHBMGetDirection(0) == 1/*W*/)
{
switch (TeakraCore->AHBMGetUnitSize(0))
{
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;
}
}
break;
default: return;
}
if (DSP_PCFG & (1<<1)) // auto-increment
++DSP_PADR; // overflows and stays within a 64k 'page' // TODO: is this +1 or +2?
NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); // wrfifo empty
}
// TODO: FIFO interrupts! (rd full, nonempty)
u16 PDataDMARead()
{
u16 r = 0;
u32 addr = DSP_PADR;
switch (DSP_PCFG & (7<<12)) // memory region select
{
case 0<<12: // data
addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
r = TeakraCore->DataReadA32(addr);
break;
case 1<<12: // mmio
r = TeakraCore->MMIORead(addr & 0x7FF);
break;
case 5<<12: // program
addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
r = TeakraCore->ProgramRead(addr);
break;
case 7<<12:
addr |= (u32)TeakraCore->DMAChan0GetDstHigh() << 16;
// only do stuff when AHBM is configured correctly
if (TeakraCore->AHBMGetDmaChannel(0) == 0 && TeakraCore->AHBMGetDirection(0) == 0/*R*/)
{
switch (TeakraCore->AHBMGetUnitSize(0))
{
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;
}
}
break;
default: return r;
}
if (DSP_PCFG & (1<<1)) // auto-increment
++DSP_PADR; // overflows and stays within a 64k 'page' // TODO: is this +1 or +2?
return r;
}
void PDataDMAFetch()
{
if (!PDataDMALen) return;
PDATAReadFifo.Write(PDataDMARead());
if (PDataDMALen > 0) --PDataDMALen;
}
void PDataDMAStart()
{
switch ((DSP_PSTS & (3<<2)) >> 2)
{
case 0: PDataDMALen = 1; break;
case 1: PDataDMALen = 8; break;
case 2: PDataDMALen =16; break;
case 3: PDataDMALen =-1; break;
}
// fill a single fifo
int amt = PDataDMALen;
if (amt < 0) amt = 16;
for (int i = 0; i < amt; ++i)
PDataDMAFetch();
NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
}
void PDataDMACancel()
{
PDataDMALen = 0;
PDATAReadFifo.Clear();
}
u16 PDataDMAReadMMIO()
{
u16 ret;
if (!PDATAReadFifo.IsEmpty())
ret = PDATAReadFifo.Read();
// aha, there's more to come
if (PDataDMALen != 0)
{
int left = 16 - PDATAReadFifo.Level();
if (PDataDMALen > 0 && PDataDMALen < left)
left = PDataDMALen;
for (int i = 0; i < left; ++i)
PDataDMAFetch();
ret = PDATAReadFifo.Read();
}
else
{
// ah, crap
ret = 0; // TODO: is this actually 0, or just open bus?
}
if (!PDATAReadFifo.IsEmpty() || PDATAReadFifo.IsFull())
NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
return ret;
}
u8 Read8(u32 addr)
{
if (!(DSi::SCFG_EXT[0] & (1<<18)))
return 0;
if (!DSPCatchUp()) return 0;
addr &= 0x3F; // mirroring wheee
// ports are a bit weird, 16-bit regs in 32-bit spaces
switch (addr)
{
// no 8-bit PDATA read
// no DSP_PADR read
case 0x08: return DSP_PCFG & 0xFF;
case 0x09: return DSP_PCFG >> 8;