/* 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); }