BizHawk/libmeteor/source/dma.cpp

318 lines
7.8 KiB
C++

// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program 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.
//
// This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/dma.hpp"
#include "ameteor/io.hpp"
#include "ameteor/memory.hpp"
#include "globals.hpp"
#include "ameteor.hpp"
#include "debug.hpp"
#include <sstream>
namespace AMeteor
{
void Dma::Reset ()
{
for (Channel* chan = m_chans; chan < m_chans+4; ++chan)
{
chan->src = 0;
chan->dest = 0;
chan->count = 0;
chan->control.w = 0;
}
m_graphic = 0;
}
void Dma::UpdateCnt (uint8_t channum)
{
Channel& chan = m_chans[channum];
if (chan.control.w !=
IO.DRead16(Io::DMA0CNT_H + channum * Io::DMA_CHANSIZE))
{
if (!chan.control.b.enable &&
(IO.DRead16(Io::DMA0CNT_H + channum * Io::DMA_CHANSIZE)
& (0x1 << 15)))
// if we changed enable from 0 to 1
{
chan.dest = IO.DRead32(Io::DMA0DAD + channum * Io::DMA_CHANSIZE);
if (channum == 3)
chan.dest &= 0x0FFFFFFF;
else
chan.dest &= 0x07FFFFFF;
chan.src = IO.DRead32(Io::DMA0SAD + channum * Io::DMA_CHANSIZE);
if (channum == 0)
chan.src &= 0x07FFFFFF;
else
chan.src &= 0x0FFFFFFF;
chan.count = chan.reload;
if (channum != 3)
chan.count &= 0x3FFF;
chan.control.w =
IO.DRead16(Io::DMA0CNT_H + channum * Io::DMA_CHANSIZE);
Check(channum, Immediately);
}
else
chan.control.w =
IO.DRead16(Io::DMA0CNT_H + channum * Io::DMA_CHANSIZE);
if (chan.control.b.start == Special)
{
switch (channum)
{
case 0:
met_abort("prohibited !");
break;
case 1:
case 2:
// sound dma
if (chan.dest != 0x040000A0 && chan.dest != 0x040000A4)
met_abort("Special DMA 1 or 2 with unauthorized address : "
<< IOS_ADD << chan.dest);
if (!chan.control.b.repeat)
met_abort("Special DMA 1 or 2 without repeat");
// 4 words of 32 bits
chan.count = 4;
chan.control.b.type = 1;
// no increment
chan.control.b.dest = 2;
break;
case 3:
met_abort("not implemented");
break;
}
}
}
}
void Dma::Check (uint8_t channum, uint8_t reason)
{
register Channel::Control cnt = m_chans[channum].control;
if (cnt.b.enable &&
cnt.b.start == reason)
Process(channum);
}
void Dma::Process (uint8_t channum)
{
Channel& chan = m_chans[channum];
int8_t s_inc, d_inc;
s_inc = d_inc = 2; // increment or increment reload
if (chan.control.b.src == 1) // decrement
s_inc = -2;
else if (chan.control.b.src == 2) // fixed
s_inc = 0;
else if (chan.control.b.src == 3)
met_abort("prohibited");
if (chan.control.b.dest == 1)
d_inc = -2;
else if (chan.control.b.dest == 2)
d_inc = 0;
//else if (chan.control.b.dest == 3)
// same as 0, but we do something at the end
if (chan.control.b.type) // 32 bits transfer
{
s_inc <<= 1;
d_inc <<= 1;
}
if (chan.count == 0)
{
if (channum != 3)
chan.count = 0x4000;
// 0x10000 doesn't fill in 16 bits, we treat this case in the Copy() call
}
//printf("DMA%hhu from %08X to %08X of %hu %s\n", channum, chan.src, chan.dest, chan.count, chan.control.b.type ? "words":"halfwords");
//if (i > debut)
debug ("DMA" << IOS_NOR << (int)channum << ", from " << IOS_ADD << chan.src
<< " to " << IOS_ADD << chan.dest
<< " of " << IOS_NOR << (chan.count ? chan.count : 0x10000)
<< (chan.control.b.type ? " words" : " halfwords"));
if (traceenabled)
{
std::stringstream ss;
ss << "DMA" << IOS_NOR << (int)channum << ", from " << IOS_ADD << chan.src
<< " to " << IOS_ADD << chan.dest
<< " of " << IOS_NOR << (chan.count ? chan.count : 0x10000)
<< (chan.control.b.type ? " words" : " halfwords");
trace_bizhawk(ss.str());
}
#if 0
if (channum == 3 && (chan.dest >> 24) == 0x0D || (chan.src >> 24) == 0x0D)
{
if (chan.control.b.type)
met_abort("Word copy for EEPROM DMA3");
if (d_inc != 2 || s_inc != 2)
met_abort("Source or destination not incremeting in EEPROM DMA3");
if ((chan.dest >> 24) == 0x0D)
MEM.WriteEepromDma(chan.src, chan.count ? chan.count : 0x10000);
else if ((chan.src >> 24) == 0x0D)
MEM.ReadEepromDma(chan.dest, chan.count ? chan.count : 0x10000);
chan.src += chan.count * 2;
chan.dest += chan.count * 2;
}
else
#endif
if (channum == 3 && (chan.dest >> 24) == 0x0D)
{
if (chan.control.b.type)
met_abort("Word copy for EEPROM DMA3");
if (d_inc != 2 || s_inc != 2)
met_abort("Source or destination not incremeting in EEPROM DMA3");
MEM.WriteEepromDma(chan.src, chan.count);
chan.src += chan.count * 2;
chan.dest += chan.count * 2;
}
else
Copy(chan.src, chan.dest, s_inc, d_inc,
chan.count ? chan.count : 0x10000, chan.control.b.type);
if (chan.control.b.type)
{
CYCLES32NSeq(chan.src, chan.count);
CYCLES32NSeq(chan.dest, chan.count);
ICYCLES(2);
}
else
{
CYCLES16NSeq(chan.src, chan.count);
CYCLES16NSeq(chan.dest, chan.count);
ICYCLES(2);
}
uint32_t d = chan.dest >> 24;
uint32_t s = chan.src >> 24;
if (d >= 0x08 && d <= 0x0D && s >= 0x08 && s <= 0x0D)
// if both source and destination are in GamePak
ICYCLES(2);
if (chan.control.b.irq)
CPU.SendInterrupt(0x1 << (8 + channum));
if (chan.control.b.dest == 3)
{
chan.dest = IO.DRead32(Io::DMA0DAD + channum * Io::DMA_CHANSIZE);
if (channum == 3)
chan.dest &= 0x0FFFFFFF;
else
chan.dest &= 0x07FFFFFF;
}
// if repeat but not sound dma
//if (!((channum == 1 || channum == 2) && chan.control.b.start == Special) && chan.control.b.repeat)
if (((channum != 1 && channum != 2) || chan.control.b.start != Special)
&& chan.control.b.repeat)
{
chan.count = chan.reload;
}
if (!chan.control.b.repeat || chan.control.b.start == Immediately)
{
chan.control.b.enable = 0;
IO.GetRef16(Io::DMA0CNT_H + channum * Io::DMA_CHANSIZE) &= 0x7FFF;
}
}
void Dma::Copy (uint32_t& src, uint32_t& dest, int8_t s_inc, int8_t d_inc,
uint32_t count, bool word)
{
uint32_t basedest = dest;
if (word)
{
src &= 0xFFFFFFFC;
dest &= 0xFFFFFFFC;
}
else
{
src &= 0xFFFFFFFE;
dest &= 0xFFFFFFFE;
}
// sound
if (dest == 0x040000A0)
{
SOUND.SendDigitalA((uint8_t*)MEM.GetRealAddress(src));
src += 4*4;
if (d_inc != 0)
met_abort("dinc != 0 on dma sound, should not happen");
return;
}
if (dest == 0x040000A4)
{
SOUND.SendDigitalB((uint8_t*)MEM.GetRealAddress(src));
src += 4*4;
if (d_inc != 0)
met_abort("dinc != 0 on dma sound, should not happen");
return;
}
if ((dest >> 24) >= 0x05 &&
(dest >> 24) <= 0x07)
m_graphic = true;
if (word)
{
for (uint32_t cur = 0; cur < count; ++cur)
{
MEM.Write32(dest, MEM.Read32(src));
src += s_inc;
dest += d_inc;
}
}
else
{
for (uint32_t cur = 0; cur < count; ++cur)
{
MEM.Write16(dest, MEM.Read16(src));
src += s_inc;
dest += d_inc;
}
}
m_graphic = false;
//if ((basedest >> 24) == 0x07)
// LCD.OamWrite(basedest, dest);
}
bool Dma::SaveState (std::ostream& stream)
{
SS_WRITE_ARRAY(m_chans);
// no need to save or load m_graphic since we shouldn't save or load during
// a dma
return true;
}
bool Dma::LoadState (std::istream& stream)
{
SS_READ_ARRAY(m_chans);
return true;
}
}