diff options
| author | Nadia Holmquist Pedersen <nadia@nhp.sh> | 2020-06-03 13:54:28 +0200 | 
|---|---|---|
| committer | Nadia Holmquist Pedersen <nadia@nhp.sh> | 2020-06-03 13:54:28 +0200 | 
| commit | f9f366e296a68be0aa2eb7bfd0552da31896a18c (patch) | |
| tree | f52760d5505b84074a9c1b50b4fe16e40035ba06 /src/DSi_NDMA.cpp | |
| parent | b746c0b7279d1f5b4bcfd02c115b09d583e9df4f (diff) | |
| parent | d6332f96f162849ad0dde2738cacd3fae6e76e5d (diff) | |
Merge remote-tracking branch 'remotes/upstream/master' into feature/qt-platform
# Conflicts:
#	src/frontend/qt_sdl/CMakeLists.txt
#	src/frontend/qt_sdl/Platform.cpp
#	src/frontend/qt_sdl/main.cpp
Diffstat (limited to 'src/DSi_NDMA.cpp')
| -rw-r--r-- | src/DSi_NDMA.cpp | 341 | 
1 files changed, 341 insertions, 0 deletions
diff --git a/src/DSi_NDMA.cpp b/src/DSi_NDMA.cpp new file mode 100644 index 0000000..19c72b6 --- /dev/null +++ b/src/DSi_NDMA.cpp @@ -0,0 +1,341 @@ +/* +    Copyright 2016-2019 Arisotura + +    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 "DSi.h" +#include "DSi_NDMA.h" +#include "GPU.h" +#include "DSi_AES.h" + + + +DSi_NDMA::DSi_NDMA(u32 cpu, u32 num) +{ +    CPU = cpu; +    Num = num; + +    Reset(); +} + +DSi_NDMA::~DSi_NDMA() +{ +} + +void DSi_NDMA::Reset() +{ +    SrcAddr = 0; +    DstAddr = 0; +    TotalLength = 0; +    BlockLength = 0; +    SubblockTimer = 0; +    FillData = 0; +    Cnt = 0; + +    StartMode = 0; +    CurSrcAddr = 0; +    CurDstAddr = 0; +    SubblockLength = 0; +    RemCount = 0; +    IterCount = 0; +    TotalRemCount = 0; +    SrcAddrInc = 0; +    DstAddrInc = 0; + +    Running = false; +    InProgress = false; +} + +void DSi_NDMA::DoSavestate(Savestate* file) +{ +    // TODO! +} + +void DSi_NDMA::WriteCnt(u32 val) +{ +    u32 oldcnt = Cnt; +    Cnt = val; + +    if ((!(oldcnt & 0x80000000)) && (val & 0x80000000)) // checkme +    { +        CurSrcAddr = SrcAddr; +        CurDstAddr = DstAddr; +        TotalRemCount = TotalLength; + +        switch ((Cnt >> 10) & 0x3) +        { +        case 0: DstAddrInc = 1; break; +        case 1: DstAddrInc = -1; break; +        case 2: DstAddrInc = 0; break; +        case 3: DstAddrInc = 1; printf("BAD NDMA DST INC MODE 3\n"); break; +        } + +        switch ((Cnt >> 13) & 0x3) +        { +        case 0: SrcAddrInc = 1; break; +        case 1: SrcAddrInc = -1; break; +        case 2: SrcAddrInc = 0; break; +        case 3: SrcAddrInc = 0; break; // fill mode +        } + +        StartMode = (Cnt >> 24) & 0x1F; +        if (StartMode > 0x10) StartMode = 0x10; +        if (CPU == 1) StartMode |= 0x20; + +        if ((StartMode & 0x1F) == 0x10) +            Start(); + +        if (StartMode != 0x10 && StartMode != 0x30 && +            StartMode != 0x04 && StartMode != 0x06 && StartMode != 0x07 && StartMode != 0x08 && StartMode != 0x09 && +            StartMode != 0x24 && StartMode != 0x26 && StartMode != 0x28 && StartMode != 0x29 && StartMode != 0x2A && StartMode != 0x2B) +            printf("UNIMPLEMENTED ARM%d NDMA%d START MODE %02X, %08X->%08X LEN=%d BLK=%d CNT=%08X\n", +                   CPU?7:9, Num, StartMode, SrcAddr, DstAddr, TotalLength, BlockLength, Cnt); +    } +} + +void DSi_NDMA::Start() +{ +    if (Running) return; + +    if (!InProgress) +    { +        RemCount = BlockLength; +        if (!RemCount) +            RemCount = 0x1000000; +    } + +    // TODO: how does GXFIFO DMA work with all the block shito? +    IterCount = RemCount; + +    if (((StartMode & 0x1F) != 0x10) && !(Cnt & (1<<29))) +    { +        if (IterCount > TotalRemCount) +        { +            IterCount = TotalRemCount; +            RemCount = IterCount; +        } +    } + +    if (Cnt & (1<<12)) CurDstAddr = DstAddr; +    if (Cnt & (1<<15)) CurSrcAddr = SrcAddr; + +    //printf("ARM%d NDMA%d %08X %02X %08X->%08X %d bytes, total=%d\n", CPU?7:9, Num, Cnt, StartMode, CurSrcAddr, CurDstAddr, RemCount*4, TotalRemCount*4); + +    //IsGXFIFODMA = (CPU == 0 && (CurSrcAddr>>24) == 0x02 && CurDstAddr == 0x04000400 && DstAddrInc == 0); + +    // TODO eventually: not stop if we're running code in ITCM + +    //if (SubblockTimer & 0xFFFF) +    //    printf("TODO! NDMA SUBBLOCK TIMER: %08X\n", SubblockTimer); + +    if (NDS::DMAsRunning(CPU)) +        Running = 1; +    else +        Running = 2; + +    InProgress = true; +    NDS::StopCPU(CPU, 1<<(Num+4)); +} + +void DSi_NDMA::Run() +{ +    if (!Running) return; +    if (CPU == 0) return Run9(); +    else          return Run7(); +} + +void DSi_NDMA::Run9() +{ +    if (NDS::ARM9Timestamp >= NDS::ARM9Target) return; + +    Executing = true; + +    // add NS penalty for first accesses in burst +    bool burststart = (Running == 2); +    Running = 1; + +    s32 unitcycles; +    //s32 lastcycles = cycles; + +    bool dofill = ((Cnt >> 13) & 0x3) == 3; + +    if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02) +    { +        unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][2] + NDS::ARM9MemTimings[CurDstAddr >> 14][2]; +    } +    else +    { +        unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][3] + NDS::ARM9MemTimings[CurDstAddr >> 14][3]; +        if ((CurSrcAddr >> 24) == (CurDstAddr >> 24)) +            unitcycles++; +        else if ((CurSrcAddr >> 24) == 0x02) +            unitcycles--; + +        /*if (burststart) +        { +            cycles -= 2; +            cycles -= (NDS::ARM9MemTimings[CurSrcAddr >> 14][2] + NDS::ARM9MemTimings[CurDstAddr >> 14][2]); +            cycles += unitcycles; +        }*/ +    } + +    while (IterCount > 0 && !Stall) +    { +        NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift); + +        if (dofill) +            DSi::ARM9Write32(CurDstAddr, FillData); +        else +            DSi::ARM9Write32(CurDstAddr, DSi::ARM9Read32(CurSrcAddr)); + +        CurSrcAddr += SrcAddrInc<<2; +        CurDstAddr += DstAddrInc<<2; +        IterCount--; +        RemCount--; +        TotalRemCount--; + +        if (NDS::ARM9Timestamp >= NDS::ARM9Target) break; +    } + +    Executing = false; +    Stall = false; + +    if (RemCount) +    { +        if (IterCount == 0) +        { +            Running = 0; +            NDS::ResumeCPU(0, 1<<(Num+4)); + +            //if (StartMode == 0x07) +            //    GPU3D::CheckFIFODMA(); +        } + +        return; +    } + +    if ((StartMode & 0x1F) == 0x10) // CHECKME +    { +        Cnt &= ~(1<<31); +        if (Cnt & (1<<30)) NDS::SetIRQ(0, NDS::IRQ_DSi_NDMA0 + Num); +    } +    else if (!(Cnt & (1<<29))) +    { +        if (TotalRemCount == 0) +        { +            Cnt &= ~(1<<31); +            if (Cnt & (1<<30)) NDS::SetIRQ(0, NDS::IRQ_DSi_NDMA0 + Num); +        } +    } + +    Running = 0; +    InProgress = false; +    NDS::ResumeCPU(0, 1<<(Num+4)); +} + +void DSi_NDMA::Run7() +{ +    if (NDS::ARM7Timestamp >= NDS::ARM7Target) return; + +    Executing = true; + +    // add NS penalty for first accesses in burst +    bool burststart = (Running == 2); +    Running = 1; + +    s32 unitcycles; +    //s32 lastcycles = cycles; + +    bool dofill = ((Cnt >> 13) & 0x3) == 3; + +    if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02) +    { +        unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][2] + NDS::ARM7MemTimings[CurDstAddr >> 15][2]; +    } +    else +    { +        unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][3] + NDS::ARM7MemTimings[CurDstAddr >> 15][3]; +        if ((CurSrcAddr >> 23) == (CurDstAddr >> 23)) +            unitcycles++; +        else if ((CurSrcAddr >> 24) == 0x02) +            unitcycles--; + +        /*if (burststart) +        { +            cycles -= 2; +            cycles -= (NDS::ARM7MemTimings[CurSrcAddr >> 15][2] + NDS::ARM7MemTimings[CurDstAddr >> 15][2]); +            cycles += unitcycles; +        }*/ +    } + +    while (IterCount > 0 && !Stall) +    { +        NDS::ARM7Timestamp += unitcycles; + +        if (dofill) +            DSi::ARM7Write32(CurDstAddr, FillData); +        else +            DSi::ARM7Write32(CurDstAddr, DSi::ARM7Read32(CurSrcAddr)); + +        CurSrcAddr += SrcAddrInc<<2; +        CurDstAddr += DstAddrInc<<2; +        IterCount--; +        RemCount--; +        TotalRemCount--; + +        if (NDS::ARM7Timestamp >= NDS::ARM7Target) break; +    } + +    Executing = false; +    Stall = false; + +    if (RemCount) +    { +        if (IterCount == 0) +        { +            Running = 0; +            NDS::ResumeCPU(1, 1<<(Num+4)); + +            DSi_AES::CheckInputDMA(); +            DSi_AES::CheckOutputDMA(); +        } + +        return; +    } + +    if ((StartMode & 0x1F) == 0x10) // CHECKME +    { +        Cnt &= ~(1<<31); +        if (Cnt & (1<<30)) NDS::SetIRQ(1, NDS::IRQ_DSi_NDMA0 + Num); +    } +    else if (!(Cnt & (1<<29))) +    { +        if (TotalRemCount == 0) +        { +            Cnt &= ~(1<<31); +            if (Cnt & (1<<30)) NDS::SetIRQ(1, NDS::IRQ_DSi_NDMA0 + Num); +        } +    } + +    Running = 0; +    InProgress = false; +    NDS::ResumeCPU(1, 1<<(Num+4)); + +    DSi_AES::CheckInputDMA(); +    DSi_AES::CheckOutputDMA(); +}  |