dma: execute sync transfer in blocks, allow cpu execution

This commit is contained in:
Jakub Czekański 2020-12-23 04:00:02 +01:00
parent 3dede45f93
commit 1dc4c8edba
9 changed files with 138 additions and 94 deletions

View file

@ -28,6 +28,17 @@ void DMA::reset() {
}
void DMA::step() {
for (int channel = 0; channel < 7; channel++) {
dma[channel]->step();
if (dma[channel]->irqFlag) {
dma[channel]->irqFlag = false;
if (status.getEnableDma(channel)) {
status.setFlagDma(channel, 1);
pendingInterrupt = status.calcMasterFlag();
}
}
}
if (pendingInterrupt) {
pendingInterrupt = false;
sys->interrupt->trigger(interrupt::DMA);
@ -67,13 +78,6 @@ void DMA::write(uint32_t address, uint8_t data) {
}
dma[channel]->write(address % 0x10, data);
if (dma[channel]->irqFlag) {
dma[channel]->irqFlag = false;
if (status.getEnableDma(channel)) {
status.setFlagDma(channel, 1);
pendingInterrupt = status.calcMasterFlag();
}
}
}
bool DMA::isChannelEnabled(Channel ch) {

View file

@ -5,4 +5,6 @@ namespace device::dma {
DMA0Channel::DMA0Channel(Channel channel, System* sys, mdec::MDEC* mdec) : DMAChannel(channel, sys), mdec(mdec) {}
void DMA0Channel::writeDevice(uint32_t data) { mdec->write(0, data); }
bool DMA0Channel::dataRequest() { return mdec->dataInRequest(); }
} // namespace device::dma

View file

@ -10,6 +10,7 @@ class DMA0Channel : public DMAChannel {
mdec::MDEC* mdec;
void writeDevice(uint32_t data) override;
bool dataRequest() override;
public:
DMA0Channel(Channel channel, System* sys, mdec::MDEC* mdec);

View file

@ -5,4 +5,6 @@ namespace device::dma {
DMA1Channel::DMA1Channel(Channel channel, System* sys, mdec::MDEC* mdec) : DMAChannel(channel, sys), mdec(mdec) {}
uint32_t DMA1Channel::readDevice() { return mdec->read(0); }
bool DMA1Channel::dataRequest() { return mdec->dataOutRequest(); }
} // namespace device::dma

View file

@ -11,6 +11,9 @@ class DMA1Channel : public DMAChannel {
uint32_t readDevice() override;
protected:
bool dataRequest() override;
public:
DMA1Channel(Channel channel, System* sys, mdec::MDEC* mdec);
};

View file

@ -17,8 +17,7 @@ void DMA6Channel::maskControl() {
control.unknown1 = false;
}
void DMA6Channel::startTransfer() {
control.startTrigger = CHCR::StartTrigger::clear;
void DMA6Channel::burstTransfer() {
uint32_t addr = baseAddress.address;
uint32_t wordCount = count.syncMode0.wordCount;
if (wordCount == 0) {

View file

@ -4,7 +4,7 @@
namespace device::dma {
class DMA6Channel : public DMAChannel {
void maskControl() override;
void startTransfer() override;
void burstTransfer() override;
public:
DMA6Channel(Channel channel, System* sys);

View file

@ -20,7 +20,7 @@ uint8_t DMAChannel::read(uint32_t address) {
if (address >= 0x8 && address < 0xc) return control._byte[address - 8];
return 0;
}
static bool canLog = true;
void DMAChannel::write(uint32_t address, uint8_t data) {
if (address < 0x4) {
baseAddress._byte[address] = data;
@ -32,102 +32,126 @@ void DMAChannel::write(uint32_t address, uint8_t data) {
maskControl();
if (address == 0xb) {
if (!sys->dma->isChannelEnabled(channel)) return;
if (control.syncMode == CHCR::SyncMode::block && control.startTrigger != CHCR::StartTrigger::manual) return;
if (control.enabled != CHCR::Enabled::start) return;
startTransfer();
canLog = true;
step();
}
}
}
void DMAChannel::step() {
if (!sys->dma->isChannelEnabled(channel)) return;
if (control.enabled != CHCR::Enabled::start) return;
if (control.syncMode == CHCR::SyncMode::block && control.startTrigger != CHCR::StartTrigger::manual) return;
control.startTrigger = CHCR::StartTrigger::clear;
if (control.syncMode == CHCR::SyncMode::block) {
burstTransfer();
} else if (control.syncMode == CHCR::SyncMode::sync) {
syncBlockTransfer();
} else if (control.syncMode == CHCR::SyncMode::linkedList) {
linkedListTransfer();
}
}
void DMAChannel::maskControl() { control._reg &= ~CHCR::MASK; }
void DMAChannel::startTransfer() {
using namespace magic_enum;
int step = control.memoryAddressStep == CHCR::MemoryAddressStep::forward ? 4 : -4;
// Sync 0 - no cpu execution in between
void DMAChannel::burstTransfer() {
uint32_t addr = baseAddress.address;
control.startTrigger = CHCR::StartTrigger::clear;
uint32_t wordCount = count.syncMode0.wordCount;
if (wordCount == 0) wordCount = 0x10000;
if (control.syncMode == CHCR::SyncMode::block) {
if (verbose) {
fmt::print("[DMA{}] {:<8} {} RAM @ 0x{:08x}, {}, count: 0x{:04x}\n", (int)channel, enum_name(channel), control.dir(), addr,
enum_name(control.syncMode), (int)count.syncMode0.wordCount);
if (verbose && canLog) {
fmt::print("[DMA{}] {:<8} {} RAM @ 0x{:08x}, {}, count: 0x{:04x}\n", (int)channel, magic_enum::enum_name(channel), control.dir(),
addr, magic_enum::enum_name(control.syncMode), wordCount);
canLog = false;
}
if (control.direction == CHCR::Direction::toRam) {
for (size_t i = 0; i < wordCount; i++, addr += control.step()) {
sys->writeMemory32(addr, readDevice());
}
if (control.direction == CHCR::Direction::fromRam) {
for (size_t i = 0; i < count.syncMode0.wordCount; i++, addr += step) {
writeDevice(sys->readMemory32(addr));
}
} else if (control.direction == CHCR::Direction::toRam) {
for (size_t i = 0; i < count.syncMode0.wordCount; i++, addr += step) {
sys->writeMemory32(addr, readDevice());
}
} else if (control.direction == CHCR::Direction::fromRam) {
for (size_t i = 0; i < wordCount; i++, addr += control.step()) {
writeDevice(sys->readMemory32(addr));
}
control.enabled = CHCR::Enabled::stop;
} else if (control.syncMode == CHCR::SyncMode::sync) {
int blockCount = count.syncMode1.blockCount;
int blockSize = count.syncMode1.blockSize;
if (blockCount == 0) blockCount = 0x10000;
if (verbose) {
fmt::print("[DMA{}] {:<8} {} RAM @ 0x{:08x}, {}, BS: 0x{:04x}, BC: 0x{:04x}\n", (int)channel, enum_name(channel), control.dir(),
addr, enum_name(control.syncMode), blockSize, blockCount);
}
// TODO: Execute sync with chopping
if (control.direction == CHCR::Direction::toRam) {
for (int block = 0; block < blockCount; block++) {
for (int i = 0; i < blockSize; i++, addr += step) {
sys->writeMemory32(addr, readDevice());
}
}
} else if (control.direction == CHCR::Direction::fromRam) {
for (int block = 0; block < blockCount; block++) {
for (int i = 0; i < blockSize; i++, addr += step) {
writeDevice(sys->readMemory32(addr));
}
}
}
// TODO: Need proper Chopping implementation for SPU READ to work
baseAddress.address = addr;
count.syncMode1.blockCount = 0;
} else if (control.syncMode == CHCR::SyncMode::linkedList) {
if (verbose) {
fmt::print("[DMA{}] {:<8} {} RAM @ 0x{:08x}, {}\n", (int)channel, enum_name(channel), control.dir(), addr,
enum_name(control.syncMode));
}
std::unordered_set<uint32_t> visited;
for (;;) {
uint32_t blockInfo = sys->readMemory32(addr);
int commandCount = blockInfo >> 24;
int nextAddr = blockInfo & 0xffffff;
if (verbose >= 2) {
fmt::print("[DMA{}] {:<8} {} RAM @ 0x{:08x}, {}, count: {}, nextAddr: 0x{:08x}\n", (int)channel, enum_name(channel),
control.dir(), addr, enum_name(control.syncMode), commandCount, nextAddr);
}
addr += step;
for (int i = 0; i < commandCount; i++, addr += step) {
writeDevice(sys->readMemory32(addr));
}
addr = nextAddr;
if (addr == 0xffffff || addr == 0) break;
if (visited.find(addr) != visited.end()) {
fmt::print("[DMA{}] GPU DMA transfer loop detected, breaking.\n", (int)channel);
break;
}
visited.insert(addr);
}
baseAddress.address = addr;
}
irqFlag = true;
control.enabled = CHCR::Enabled::completed;
}
void DMAChannel::syncBlockTransfer() {
if (!dataRequest()) return;
uint32_t addr = baseAddress.address;
if (verbose && canLog) {
fmt::print("[DMA{}] {:<8} {} RAM @ 0x{:08x}, {}, BS: 0x{:04x}, BC: 0x{:04x}\n", (int)channel, magic_enum::enum_name(channel),
control.dir(), addr, magic_enum::enum_name(control.syncMode), (int)count.syncMode1.blockSize,
(int)count.syncMode1.blockCount);
canLog = false;
}
// TODO: DREQ DACK for MDEC
// TODO: Execute sync with chopping
if (control.direction == CHCR::Direction::toRam) {
for (int i = 0; i < count.syncMode1.blockSize; i++, addr += control.step()) {
sys->writeMemory32(addr, readDevice());
}
} else if (control.direction == CHCR::Direction::fromRam) {
for (int i = 0; i < count.syncMode1.blockSize; i++, addr += control.step()) {
writeDevice(sys->readMemory32(addr));
}
}
// TODO: Need proper Chopping implementation for SPU READ to work
baseAddress.address = addr;
if (--count.syncMode1.blockCount == 0) {
irqFlag = true;
control.enabled = CHCR::Enabled::completed;
}
}
void DMAChannel::linkedListTransfer() {
uint32_t addr = baseAddress.address;
if (verbose && canLog) {
fmt::print("[DMA{}] {:<8} {} RAM @ 0x{:08x}, {}\n", (int)channel, magic_enum::enum_name(channel), control.dir(), addr,
magic_enum::enum_name(control.syncMode));
canLog = false;
}
// TODO: Break execution in between
std::unordered_set<uint32_t> visited;
for (;;) {
uint32_t blockInfo = sys->readMemory32(addr);
int commandCount = blockInfo >> 24;
int nextAddr = blockInfo & 0xffffff;
if (verbose >= 2) {
fmt::print("[DMA{}] {:<8} {} RAM @ 0x{:08x}, {}, count: {}, nextAddr: 0x{:08x}\n", (int)channel, magic_enum::enum_name(channel),
control.dir(), addr, magic_enum::enum_name(control.syncMode), commandCount, nextAddr);
}
addr += control.step();
for (int i = 0; i < commandCount; i++, addr += control.step()) {
writeDevice(sys->readMemory32(addr));
}
addr = nextAddr;
if (addr == 0xffffff || addr == 0) break;
if (visited.find(addr) != visited.end()) {
fmt::print("[DMA{}] GPU DMA transfer loop detected, breaking.\n", (int)channel);
break;
}
visited.insert(addr);
}
baseAddress.address = addr;
irqFlag = true;
control.enabled = CHCR::Enabled::completed;
}
} // namespace device::dma

View file

@ -74,6 +74,8 @@ union CHCR {
else
return "->";
}
int step() const { return memoryAddressStep == MemoryAddressStep::forward ? 4 : -4; }
};
class DMAChannel {
@ -90,7 +92,13 @@ class DMAChannel {
virtual uint32_t readDevice();
virtual void writeDevice(uint32_t data);
virtual void maskControl();
virtual void startTransfer();
virtual void burstTransfer();
void syncBlockTransfer();
void linkedListTransfer();
// DACK/DREQ
virtual bool dataRequest() { return true; }
public:
bool irqFlag = false;
@ -99,6 +107,7 @@ class DMAChannel {
uint8_t read(uint32_t address);
void write(uint32_t address, uint8_t data);
void step();
template <class Archive>
void serialize(Archive& ar) {