mirror of
https://github.com/JaCzekanski/Avocado.git
synced 2024-05-20 12:57:38 -04:00
dma: execute sync transfer in blocks, allow cpu execution
This commit is contained in:
parent
3dede45f93
commit
1dc4c8edba
|
@ -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) {
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue