melonDS/DMA.cpp
2017-01-18 01:33:06 +01:00

145 lines
3.5 KiB
C++

/*
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 <stdio.h>
#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);
}