From 9808b73c6f64ea2506a76e719cd1c2eefda43870 Mon Sep 17 00:00:00 2001 From: StapleButter Date: Wed, 18 Jan 2017 01:33:06 +0100 Subject: [PATCH] DMA support! --- ARM.cpp | 34 ++---------- DMA.cpp | 144 +++++++++++++++++++++++++++++++++++++++++++++++++ DMA.h | 56 +++++++++++++++++++ NDS.cpp | 90 ++++++++++++++++++++++++++++++- main.cpp | 18 +++++++ melonDS.cbp | 2 + melonDS.depend | 15 ++++-- 7 files changed, 324 insertions(+), 35 deletions(-) create mode 100644 DMA.cpp create mode 100644 DMA.h diff --git a/ARM.cpp b/ARM.cpp index 4b63c049..865be59a 100644 --- a/ARM.cpp +++ b/ARM.cpp @@ -291,43 +291,15 @@ s32 ARM::Execute(s32 cycles) } } - // zorp + // TODO optimize this shit!!! if (NDS::HaltInterrupted(Num)) { if (NDS::IME[Num]&1) TriggerIRQ(); } - //if (R[15]==0x2DD0) - // printf("-> %08X %08X\n", R[13], Read32(0x0380FF7C)); - - //if (R[15]==0x37FEC7A) - // printf("!!!!!!!! %08X %08X\n", R[14], CPSR); - - //if (R[15] == 0x037FF102+4 && CPSR&0x20) printf("!!!!! %08X -> %08X, %08X -> %08X, %08X, %08X/%08X\n", - // addr, R[15], cpsr, CPSR, CurInstr, R_SVC[2], R_IRQ[2]); - - if (addr >= 0x37FAD68 && addr < 0x37FAE40) - { - if (addr==0x037FAE2A) debug=2; - //printf("!!! @ %08X %08X / %08X %08X/%08X\n", addr, R[15], Read32(0x03FFFFFC), Read32(0x04000210), Read32(0x04000214)); - } - else if (debug==2)// && (CPSR&0x1F)==0x12) - { - //printf("[%08X|%08X] IRQ RET VAL = %08X | %d\n", R[15], CPSR, Read32(0x0380FF7C), cyclesrun); - } - - /*if (R[15] == 0x03802D54+2) - printf("%08X -> %08X\n", addr, R[15]); - - // 37FE284(ARM) -> 37FE256 -> 37FECA0 !!! - if (R[15] == 0x037FE256+4) - printf("ROYAL %08X %s -> %08X %s | %08X\n", - addr, (cpsr&0x20)?"THUMB":"ARM", R[15], (CPSR&0x20)?"THUMB":"ARM", - R[0]); - if (R[15] == 0x37FE27C) - printf("NOTROYAL %08X %08X %08X/%08X\n", R[0], Read32(0x0380593C + 0x3C), R_SVC[2], Read32(0x0380593C-4));*/ - // R0 = 0380593C + //if (R[15] >= 0x3800170+4 && R[15]<=0x0380017A+4) + // printf("!! %08X: %08X | %08X %08X\n", R[15]-4, R[13], R[0], R[1]); addr = R[15] - (CPSR&0x20 ? 4:8); cpsr = CPSR; diff --git a/DMA.cpp b/DMA.cpp new file mode 100644 index 00000000..1b04c3a1 --- /dev/null +++ b/DMA.cpp @@ -0,0 +1,144 @@ +/* + Copyright 2016-2017 StapleButter + + 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 +#include "NDS.h" +#include "DMA.h" + + +// NOTES ON DMA SHIT +// +// * could use optimized code paths for common types of DMA transfers. for example, VRAM +// * needs to eventually be made more accurate anyway. DMA isn't instant. + + +DMA::DMA(u32 cpu, u32 num) +{ + CPU = cpu; + Num = num; + + Reset(); +} + +DMA::~DMA() +{ +} + +void DMA::Reset() +{ + SrcAddr = 0; + DstAddr = 0; + Cnt = 0; + + StartMode = 0; + CurSrcAddr = 0; + CurDstAddr = 0; + RemCount = 0; + SrcAddrInc = 0; + DstAddrInc = 0; +} + +void DMA::WriteCnt(u32 val) +{ + u32 oldcnt = Cnt; + Cnt = val; + + if ((!(oldcnt & 0x80000000)) && (val & 0x80000000)) + { + CurSrcAddr = SrcAddr; + CurDstAddr = DstAddr; + + switch (Cnt & 0x00060000) + { + case 0x00000000: DstAddrInc = 1; break; + case 0x00020000: DstAddrInc = -1; break; + case 0x00040000: DstAddrInc = 0; break; + case 0x00060000: DstAddrInc = 1; break; + } + + switch (Cnt & 0x00180000) + { + case 0x00000000: SrcAddrInc = 1; break; + case 0x00080000: SrcAddrInc = -1; break; + case 0x00100000: SrcAddrInc = 0; break; + case 0x00180000: SrcAddrInc = 1; printf("BAD DMA SRC INC MODE 3\n"); break; + } + + if (CPU == 0) + StartMode = (Cnt >> 27) & 0x7; + else + StartMode = ((Cnt >> 28) & 0x3) | 0x8; + + if ((StartMode & 0x7) == 0) + Start(); + } +} + +void DMA::Start() +{ + u32 countmask; + if (CPU == 0) + countmask = 0x001FFFFF; + else + countmask = (Num==3 ? 0x0000FFFF : 0x00003FFF); + + RemCount = Cnt & countmask; + if (!RemCount) + RemCount = countmask+1; + + if ((Cnt & 0x00060000) == 0x00060000) + CurDstAddr = DstAddr; + + printf("ARM%d DMA%d %08X->%08X %d units %dbit\n", CPU?7:9, Num, CurSrcAddr, CurDstAddr, RemCount, (Cnt & 0x04000000)?16:32); + + // TODO: NOT MAKE THE DMA INSTANT!! + if (Cnt & 0x04000000) + { + u16 (*readfn)(u32) = CPU ? NDS::ARM7Read16 : NDS::ARM9Read16; + void (*writefn)(u32,u16) = CPU ? NDS::ARM7Write16 : NDS::ARM9Write16; + + while (RemCount > 0) + { + writefn(CurDstAddr, readfn(CurSrcAddr)); + + CurSrcAddr += SrcAddrInc<<1; + CurDstAddr += DstAddrInc<<1; + RemCount--; + } + } + else + { + u32 (*readfn)(u32) = CPU ? NDS::ARM7Read32 : NDS::ARM9Read32; + void (*writefn)(u32,u32) = CPU ? NDS::ARM7Write32 : NDS::ARM9Write32; + + while (RemCount > 0) + { + writefn(CurDstAddr, readfn(CurSrcAddr)); + + CurSrcAddr += SrcAddrInc<<2; + CurDstAddr += DstAddrInc<<2; + RemCount--; + } + } + + if (!(Cnt & 0x02000000)) + Cnt &= ~0x80000000; + + if (Cnt & 0x40000000) + NDS::TriggerIRQ(CPU, NDS::IRQ_DMA0 + Num); +} diff --git a/DMA.h b/DMA.h new file mode 100644 index 00000000..619b1639 --- /dev/null +++ b/DMA.h @@ -0,0 +1,56 @@ +/* + Copyright 2016-2017 StapleButter + + 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 DMA_H +#define DMA_H + +#include "types.h" + +class DMA +{ +public: + DMA(u32 cpu, u32 num); + ~DMA(); + + void Reset(); + + void WriteCnt(u32 val); + void Start(); + + void StartIfNeeded(u32 mode) + { + if ((mode == StartMode) && (Cnt & 0x80000000)) + Start(); + } + + u32 SrcAddr; + u32 DstAddr; + u32 Cnt; + +private: + u32 CPU, Num; + + u32 StartMode; + u32 CurSrcAddr; + u32 CurDstAddr; + u32 RemCount; + u32 SrcAddrInc; + u32 DstAddrInc; +}; + +#endif diff --git a/NDS.cpp b/NDS.cpp index ba45c78e..522c51e1 100644 --- a/NDS.cpp +++ b/NDS.cpp @@ -21,6 +21,7 @@ #include "NDS.h" #include "ARM.h" #include "CP15.h" +#include "DMA.h" #include "FIFO.h" #include "GPU2D.h" #include "SPI.h" @@ -79,6 +80,9 @@ u16 PowerControl7; Timer Timers[8]; +DMA* DMAs[8]; +u32 DMA9Fill[4]; + u16 IPCSync9, IPCSync7; u16 IPCFIFOCnt9, IPCFIFOCnt7; FIFO* IPCFIFO9; // FIFO in which the ARM9 writes @@ -102,6 +106,15 @@ void Init() ARM9 = new ARM(0); ARM7 = new ARM(1); + DMAs[0] = new DMA(0, 0); + DMAs[1] = new DMA(0, 1); + DMAs[2] = new DMA(0, 2); + DMAs[3] = new DMA(0, 3); + DMAs[4] = new DMA(1, 0); + DMAs[5] = new DMA(1, 1); + DMAs[6] = new DMA(1, 2); + DMAs[7] = new DMA(1, 3); + IPCFIFO9 = new FIFO(16); IPCFIFO7 = new FIFO(16); @@ -156,6 +169,7 @@ void LoadROM() void Reset() { FILE* f; + u32 i; f = fopen("bios9.bin", "rb"); if (!f) @@ -216,6 +230,9 @@ void Reset() memset(Timers, 0, 8*sizeof(Timer)); + for (i = 0; i < 8; i++) DMAs[i]->Reset(); + memset(DMA9Fill, 0, 4*4); + GPU2D::Reset(); SPI::Reset(); Wifi::Reset(); @@ -991,7 +1008,7 @@ u8 ARM7Read8(u32 addr) return 0; } - printf("unknown arm7 read8 %08X\n", addr); + printf("unknown arm7 read8 %08X %08X %08X/%08X\n", addr, ARM7->R[15], ARM7->R[0], ARM7->R[1]); return 0; } @@ -1229,6 +1246,15 @@ u16 ARM9IORead16(u32 addr) case 0x04000004: return GPU2D::DispStat[0]; case 0x04000006: return GPU2D::VCount; + case 0x040000E0: return ((u16*)DMA9Fill)[0]; + case 0x040000E2: return ((u16*)DMA9Fill)[1]; + case 0x040000E4: return ((u16*)DMA9Fill)[2]; + case 0x040000E6: return ((u16*)DMA9Fill)[3]; + case 0x040000E8: return ((u16*)DMA9Fill)[4]; + case 0x040000EA: return ((u16*)DMA9Fill)[5]; + case 0x040000EC: return ((u16*)DMA9Fill)[6]; + case 0x040000EE: return ((u16*)DMA9Fill)[7]; + case 0x04000100: return Timers[0].Counter; case 0x04000102: return Timers[0].Control; case 0x04000104: return Timers[1].Counter; @@ -1268,6 +1294,24 @@ u32 ARM9IORead32(u32 addr) { case 0x04000004: return GPU2D::DispStat[0] | (GPU2D::VCount << 16); + case 0x040000B0: return DMAs[0]->SrcAddr; + case 0x040000B4: return DMAs[0]->DstAddr; + case 0x040000B8: return DMAs[0]->Cnt; + case 0x040000BC: return DMAs[1]->SrcAddr; + case 0x040000C0: return DMAs[1]->DstAddr; + case 0x040000C4: return DMAs[1]->Cnt; + case 0x040000C8: return DMAs[2]->SrcAddr; + case 0x040000CC: return DMAs[2]->DstAddr; + case 0x040000D0: return DMAs[2]->Cnt; + case 0x040000D4: return DMAs[3]->SrcAddr; + case 0x040000D8: return DMAs[3]->DstAddr; + case 0x040000DC: return DMAs[3]->Cnt; + + case 0x040000E0: return DMA9Fill[0]; + case 0x040000E4: return DMA9Fill[1]; + case 0x040000E8: return DMA9Fill[2]; + case 0x040000EC: return DMA9Fill[3]; + case 0x04000100: return Timers[0].Counter | (Timers[0].Control << 16); case 0x04000104: return Timers[1].Counter | (Timers[1].Control << 16); case 0x04000108: return Timers[2].Counter | (Timers[2].Control << 16); @@ -1409,6 +1453,24 @@ void ARM9IOWrite32(u32 addr, u32 val) { switch (addr) { + case 0x040000B0: DMAs[0]->SrcAddr = val; return; + case 0x040000B4: DMAs[0]->DstAddr = val; return; + case 0x040000B8: DMAs[0]->WriteCnt(val); return; + case 0x040000BC: DMAs[1]->SrcAddr = val; return; + case 0x040000C0: DMAs[1]->DstAddr = val; return; + case 0x040000C4: DMAs[1]->WriteCnt(val); return; + case 0x040000C8: DMAs[2]->SrcAddr = val; return; + case 0x040000CC: DMAs[2]->DstAddr = val; return; + case 0x040000D0: DMAs[2]->WriteCnt(val); return; + case 0x040000D4: DMAs[3]->SrcAddr = val; return; + case 0x040000D8: DMAs[3]->DstAddr = val; return; + case 0x040000DC: DMAs[3]->WriteCnt(val); return; + + case 0x040000E0: DMA9Fill[0] = val; return; + case 0x040000E4: DMA9Fill[1] = val; return; + case 0x040000E8: DMA9Fill[2] = val; return; + case 0x040000EC: DMA9Fill[3] = val; return; + case 0x04000100: Timers[0].Reload = val & 0xFFFF; TimerStart(0, val>>16); @@ -1561,6 +1623,19 @@ u32 ARM7IORead32(u32 addr) { case 0x04000004: return GPU2D::DispStat[1] | (GPU2D::VCount << 16); + case 0x040000B0: return DMAs[4]->SrcAddr; + case 0x040000B4: return DMAs[4]->DstAddr; + case 0x040000B8: return DMAs[4]->Cnt; + case 0x040000BC: return DMAs[5]->SrcAddr; + case 0x040000C0: return DMAs[5]->DstAddr; + case 0x040000C4: return DMAs[5]->Cnt; + case 0x040000C8: return DMAs[6]->SrcAddr; + case 0x040000CC: return DMAs[6]->DstAddr; + case 0x040000D0: return DMAs[6]->Cnt; + case 0x040000D4: return DMAs[7]->SrcAddr; + case 0x040000D8: return DMAs[7]->DstAddr; + case 0x040000DC: return DMAs[7]->Cnt; + case 0x04000100: return Timers[4].Counter | (Timers[4].Control << 16); case 0x04000104: return Timers[5].Counter | (Timers[5].Control << 16); case 0x04000108: return Timers[6].Counter | (Timers[6].Control << 16); @@ -1713,6 +1788,19 @@ void ARM7IOWrite32(u32 addr, u32 val) { switch (addr) { + case 0x040000B0: DMAs[4]->SrcAddr = val; return; + case 0x040000B4: DMAs[4]->DstAddr = val; return; + case 0x040000B8: DMAs[4]->WriteCnt(val); return; + case 0x040000BC: DMAs[5]->SrcAddr = val; return; + case 0x040000C0: DMAs[5]->DstAddr = val; return; + case 0x040000C4: DMAs[5]->WriteCnt(val); return; + case 0x040000C8: DMAs[6]->SrcAddr = val; return; + case 0x040000CC: DMAs[6]->DstAddr = val; return; + case 0x040000D0: DMAs[6]->WriteCnt(val); return; + case 0x040000D4: DMAs[7]->SrcAddr = val; return; + case 0x040000D8: DMAs[7]->DstAddr = val; return; + case 0x040000DC: DMAs[7]->WriteCnt(val); return; + case 0x04000100: Timers[4].Reload = val & 0xFFFF; TimerStart(4, val>>16); diff --git a/main.cpp b/main.cpp index 5ccf6c13..5b0edd7d 100644 --- a/main.cpp +++ b/main.cpp @@ -128,6 +128,9 @@ int main() NDS::Init(); + u32 nframes = 0; + u32 lasttick = GetTickCount(); + for (;;) { MSG msg; @@ -149,6 +152,21 @@ int main() HDC dc = GetDC(melon); SetDIBitsToDevice(dc, 0, 0, 256, 384, 0, 0, 0, 384, GPU2D::Framebuffer, (BITMAPINFO*)&bmp, DIB_RGB_COLORS); UpdateWindow(melon); + + nframes++; + if (nframes >= 30) + { + u32 tick = GetTickCount(); + u32 diff = tick - lasttick; + lasttick = tick; + + u32 fps = (nframes * 1000) / diff; + nframes = 0; + + char melontitle[100]; + sprintf(melontitle, "melonDS | %d FPS", fps); + SetWindowText(melon, melontitle); + } } return 0; diff --git a/melonDS.cbp b/melonDS.cbp index 744fbeb1..107ed3db 100644 --- a/melonDS.cbp +++ b/melonDS.cbp @@ -48,6 +48,8 @@ + + diff --git a/melonDS.depend b/melonDS.depend index 32c870b8..7b48b42c 100644 --- a/melonDS.depend +++ b/melonDS.depend @@ -1,5 +1,5 @@ # depslib dependency file v1.0 -1481167563 source:c:\documents\sources\melonds\main.cpp +1484695094 source:c:\documents\sources\melonds\main.cpp "NDS.h" @@ -10,18 +10,19 @@ 1481161027 c:\documents\sources\melonds\types.h -1484616465 source:c:\documents\sources\melonds\nds.cpp +1484699425 source:c:\documents\sources\melonds\nds.cpp "NDS.h" "ARM.h" "CP15.h" + "DMA.h" "FIFO.h" "GPU2D.h" "SPI.h" "Wifi.h" -1484538131 source:c:\documents\sources\melonds\arm.cpp +1484693558 source:c:\documents\sources\melonds\arm.cpp "NDS.h" "ARM.h" @@ -102,3 +103,11 @@ 1484612398 c:\documents\sources\melonds\fifo.h "types.h" +1484699433 source:c:\documents\sources\melonds\dma.cpp + + "NDS.h" + "DMA.h" + +1484698068 c:\documents\sources\melonds\dma.h + "types.h" +