Skip to content

Commit f32d32d

Browse files
committed
dma: refactoring, fixes for Channel 6/OTC
Tested against dma/otc-test
1 parent f857966 commit f32d32d

File tree

6 files changed

+152
-95
lines changed

6 files changed

+152
-95
lines changed

src/device/dma/dma.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,9 @@ void DMA::write(uint32_t address, uint8_t data) {
6868
}
6969
}
7070
}
71+
72+
bool DMA::isChannelEnabled(Channel ch) {
73+
uint32_t mask = 0b1000 << ((int)ch * 4);
74+
return (control._reg & mask) != 0;
75+
}
7176
} // namespace device::dma

src/device/dma/dma.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ union DPCR {
3434
uint32_t _reg;
3535
uint8_t _byte[4];
3636

37-
DPCR() : _reg(0) {}
37+
DPCR() : _reg(0x07654321) {}
3838
};
3939

4040
// 0x1f8010f4 - DICR, DMA Interrupt Register
@@ -108,6 +108,8 @@ class DMA {
108108
uint8_t read(uint32_t address);
109109
void write(uint32_t address, uint8_t data);
110110

111+
bool isChannelEnabled(Channel ch);
112+
111113
template <class Archive>
112114
void serialize(Archive& ar) {
113115
ar(control._reg, status._reg, pendingInterrupt);

src/device/dma/dma6_channel.cpp

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,40 @@
11
#include "dma6_channel.h"
2+
#include <fmt/core.h>
3+
#include <magic_enum.hpp>
4+
#include "system.h"
25

36
namespace device::dma {
4-
uint32_t DMA6Channel::readDevice() { return 0xffffffff; }
5-
67
DMA6Channel::DMA6Channel(Channel channel, System* sys) : DMAChannel(channel, sys) {}
8+
9+
void DMA6Channel::maskControl() {
10+
control._reg &= ~CHCR::MASK;
11+
control.direction = CHCR::Direction::toRam;
12+
control.syncMode = CHCR::SyncMode::block;
13+
control.memoryAddressStep = CHCR::MemoryAddressStep::backward;
14+
control.choppingEnable = false;
15+
control.choppingDmaWindowSize = 0;
16+
control.choppingCpuWindowSize = 0;
17+
control.unknown1 = false;
18+
}
19+
20+
void DMA6Channel::startTransfer() {
21+
control.startTrigger = CHCR::StartTrigger::clear;
22+
uint32_t addr = baseAddress.address;
23+
uint32_t wordCount = count.syncMode0.wordCount;
24+
if (wordCount == 0) {
25+
wordCount = 0x10000;
26+
}
27+
28+
if (verbose) {
29+
fmt::print("[DMA{}] {:<8} -> RAM @ 0x{:08x}, block, count: 0x{:04x}\n", (int)channel, magic_enum::enum_name(channel), addr,
30+
wordCount);
31+
}
32+
33+
for (int i = wordCount - 1; i >= 0; i--, addr -= 4) {
34+
sys->writeMemory32(addr, (i == 0) ? 0xffffff : (addr - 4) & 0xffffff);
35+
}
36+
37+
irqFlag = true;
38+
control.enabled = CHCR::Enabled::completed;
39+
}
740
} // namespace device::dma

src/device/dma/dma6_channel.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
namespace device::dma {
55
class DMA6Channel : public DMAChannel {
6-
uint32_t readDevice() override;
6+
void maskControl() override;
7+
void startTransfer() override;
78

89
public:
910
DMA6Channel(Channel channel, System* sys);

src/device/dma/dma_channel.cpp

Lines changed: 91 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <magic_enum.hpp>
44
#include "config.h"
55
#include "system.h"
6+
#include <unordered_set>
67

78
namespace device::dma {
89
DMAChannel::DMAChannel(Channel channel, System* sys) : channel(channel), sys(sys) { verbose = config["debug"]["log"]["dma"]; }
@@ -17,112 +18,115 @@ uint8_t DMAChannel::read(uint32_t address) {
1718
if (address < 0x4) return baseAddress._byte[address];
1819
if (address >= 0x4 && address < 0x8) return count._byte[address - 4];
1920
if (address >= 0x8 && address < 0xc) return control._byte[address - 8];
20-
21-
fmt::print("R Unimplemented DMA%d address 0x%08x\n", (int)channel, address);
2221
return 0;
2322
}
2423

2524
void DMAChannel::write(uint32_t address, uint8_t data) {
26-
if (address < 0x4)
25+
if (address < 0x4) {
2726
baseAddress._byte[address] = data;
28-
else if (address >= 0x4 && address < 0x8)
27+
baseAddress._reg &= 0xffffff;
28+
} else if (address >= 0x4 && address < 0x8) {
2929
count._byte[address - 4] = data;
30-
else if (address >= 0x8 && address < 0xc) {
31-
control._byte[address - 0x8] = data;
32-
33-
if (address != 0xb) return;
34-
35-
if (control.enabled != CHCR::Enabled::start) return;
36-
control.startTrigger = CHCR::StartTrigger::clear;
37-
38-
if (control.syncMode == CHCR::SyncMode::startImmediately) {
39-
int addr = baseAddress.address;
40-
// TODO: Check Transfer Direction
41-
// TODO: Check Memory Address Step
42-
43-
if (verbose) {
44-
if (control.direction == CHCR::Direction::toRam) {
45-
fmt::print("[DMA{}] {:<8} -> RAM @ 0x{:08x}, block, count: 0x{:04x}\n", (int)channel, magic_enum::enum_name(channel),
46-
addr, static_cast<int>(count.syncMode0.wordCount));
47-
} else if (control.direction == CHCR::Direction::fromRam) {
48-
fmt::print("[DMA{}] {:<8} <- RAM @ 0x{:08x}, block, count: 0x{:04x}\n", (int)channel, magic_enum::enum_name(channel),
49-
addr, static_cast<int>(count.syncMode0.wordCount));
50-
}
30+
} else if (address >= 0x8 && address < 0xc) {
31+
control._byte[address - 8] = data;
32+
maskControl();
33+
34+
if (address == 0xb) {
35+
if (!sys->dma->isChannelEnabled(channel)) return;
36+
if (control.syncMode == CHCR::SyncMode::block && control.startTrigger != CHCR::StartTrigger::manual) return;
37+
if (control.enabled != CHCR::Enabled::start) return;
38+
39+
startTransfer();
40+
}
41+
}
42+
}
43+
44+
void DMAChannel::maskControl() { control._reg &= ~CHCR::MASK; }
45+
46+
void DMAChannel::startTransfer() {
47+
using namespace magic_enum;
48+
49+
int step = control.memoryAddressStep == CHCR::MemoryAddressStep::forward ? 4 : -4;
50+
uint32_t addr = baseAddress.address;
51+
52+
control.startTrigger = CHCR::StartTrigger::clear;
53+
54+
if (control.syncMode == CHCR::SyncMode::block) {
55+
if (verbose) {
56+
fmt::print("[DMA{}] {:<8} {} RAM @ 0x{:08x}, {}, count: 0x{:04x}\n", (int)channel, enum_name(channel), control.dir(), addr,
57+
enum_name(control.syncMode), (int)count.syncMode0.wordCount);
58+
}
59+
if (control.direction == CHCR::Direction::fromRam) {
60+
for (size_t i = 0; i < count.syncMode0.wordCount; i++, addr += step) {
61+
writeDevice(sys->readMemory32(addr));
62+
}
63+
} else if (control.direction == CHCR::Direction::toRam) {
64+
for (size_t i = 0; i < count.syncMode0.wordCount; i++, addr += step) {
65+
sys->writeMemory32(addr, readDevice());
5166
}
52-
if (channel == Channel::CDROM) {
53-
for (size_t i = 0; i < count.syncMode0.wordCount; i++) {
67+
}
68+
control.enabled = CHCR::Enabled::stop;
69+
} else if (control.syncMode == CHCR::SyncMode::sync) {
70+
int blockCount = count.syncMode1.blockCount;
71+
int blockSize = count.syncMode1.blockSize;
72+
if (blockCount == 0) blockCount = 0x10000;
73+
74+
if (verbose) {
75+
fmt::print("[DMA{}] {:<8} {} RAM @ 0x{:08x}, {}, BS: 0x{:04x}, BC: 0x{:04x}\n", (int)channel, enum_name(channel), control.dir(),
76+
addr, enum_name(control.syncMode), blockSize, blockCount);
77+
}
78+
79+
// TODO: Execute sync with chopping
80+
if (control.direction == CHCR::Direction::toRam) {
81+
for (int block = 0; block < blockCount; block++) {
82+
for (int i = 0; i < blockSize; i++, addr += step) {
5483
sys->writeMemory32(addr, readDevice());
55-
addr += 4;
56-
}
57-
} else {
58-
for (size_t i = 0; i < count.syncMode0.wordCount; i++) {
59-
if (i == count.syncMode0.wordCount - 1) {
60-
sys->writeMemory32(addr, 0xffffff);
61-
} else {
62-
sys->writeMemory32(addr, (addr - 4) & 0xffffff);
63-
}
64-
addr -= 4;
6584
}
6685
}
67-
control.enabled = CHCR::Enabled::stop;
68-
} else if (control.syncMode == CHCR::SyncMode::syncBlockToDmaRequests) {
69-
uint32_t addr = baseAddress.address;
70-
int blockCount = count.syncMode1.blockCount;
71-
int blockSize = count.syncMode1.blockSize;
72-
if (blockCount == 0) blockCount = 0x10000;
73-
74-
if (control.direction == CHCR::Direction::toRam) {
75-
if (verbose) {
76-
fmt::print("[DMA{}] {:<8} -> RAM @ 0x{:08x}, sync, BS: 0x{:04x}, BC: 0x{:04x}\n", (int)channel,
77-
magic_enum::enum_name(channel), addr, blockSize, blockCount);
78-
}
79-
for (int block = 0; block < blockCount; block++) {
80-
for (int i = 0; i < blockSize; i++, addr += 4) {
81-
sys->writeMemory32(addr, readDevice());
82-
}
83-
}
84-
} else if (control.direction == CHCR::Direction::fromRam) {
85-
if (verbose) {
86-
fmt::print("[DMA{}] {:<8} <- RAM @ 0x{:08x}, sync, BS: 0x{:04x}, BC: 0x{:04x}\n", (int)channel,
87-
magic_enum::enum_name(channel), addr, blockSize, blockCount);
88-
}
89-
90-
for (int block = 0; block < blockCount; block++) {
91-
for (int i = 0; i < blockSize; i++, addr += 4) {
92-
writeDevice(sys->readMemory32(addr));
93-
}
86+
} else if (control.direction == CHCR::Direction::fromRam) {
87+
for (int block = 0; block < blockCount; block++) {
88+
for (int i = 0; i < blockSize; i++, addr += step) {
89+
writeDevice(sys->readMemory32(addr));
9490
}
9591
}
96-
baseAddress.address = addr;
97-
} else if (control.syncMode == CHCR::SyncMode::linkedListMode) {
98-
int addr = baseAddress.address;
92+
}
93+
baseAddress.address = addr;
94+
count.syncMode1.blockCount = 0;
95+
} else if (control.syncMode == CHCR::SyncMode::linkedList) {
96+
if (verbose) {
97+
fmt::print("[DMA{}] {:<8} {} RAM @ 0x{:08x}, {}\n", (int)channel, enum_name(channel), control.dir(), addr,
98+
enum_name(control.syncMode));
99+
}
100+
101+
std::unordered_set<uint32_t> visited;
102+
for (;;) {
103+
uint32_t blockInfo = sys->readMemory32(addr);
104+
int commandCount = blockInfo >> 24;
105+
int nextAddr = blockInfo & 0xffffff;
99106

100-
if (verbose) {
101-
fmt::print("[DMA{}] {:<8} <- RAM @ 0x{:08x}, linked list\n", (int)channel, magic_enum::enum_name(channel), addr);
107+
if (verbose >= 2) {
108+
fmt::print("[DMA{}] {:<8} {} RAM @ 0x{:08x}, {}, count: {}, nextAddr: 0x{:08x}\n", (int)channel, enum_name(channel),
109+
control.dir(), addr, enum_name(control.syncMode), commandCount, nextAddr);
102110
}
103111

104-
int breaker = 0;
105-
for (;;) {
106-
uint32_t blockInfo = sys->readMemory32(addr);
107-
int commandCount = blockInfo >> 24;
112+
addr += step;
113+
for (int i = 0; i < commandCount; i++, addr += step) {
114+
writeDevice(sys->readMemory32(addr));
115+
}
108116

109-
addr += 4;
110-
for (int i = 0; i < commandCount; i++, addr += 4) {
111-
writeDevice(sys->readMemory32(addr));
112-
}
113-
addr = blockInfo & 0xffffff;
114-
if (addr == 0xffffff || addr == 0) break;
117+
addr = nextAddr;
118+
if (addr == 0xffffff || addr == 0) break;
115119

116-
if (++breaker > 0x4000) {
117-
fmt::print("[DMA{}] GPU DMA transfer too long, breaking.\n", (int)channel);
118-
break;
119-
}
120+
if (visited.find(addr) != visited.end()) {
121+
fmt::print("[DMA{}] GPU DMA transfer loop detected, breaking.\n", (int)channel);
122+
break;
120123
}
121-
baseAddress.address = addr;
124+
visited.insert(addr);
122125
}
123-
124-
irqFlag = true;
125-
control.enabled = CHCR::Enabled::completed;
126+
baseAddress.address = addr;
126127
}
128+
129+
irqFlag = true;
130+
control.enabled = CHCR::Enabled::completed;
127131
}
128132
} // namespace device::dma

src/device/dma/dma_channel.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,18 @@ union BCR {
3838

3939
// DMA Channel Control
4040
union CHCR {
41+
static inline const uint32_t MASK = 0b10001110'10001000'11111000'11111100;
4142
enum class Direction : uint32_t { toRam = 0, fromRam = 1 };
4243
enum class MemoryAddressStep : uint32_t { forward = 0, backward = 1 };
43-
enum class SyncMode : uint32_t { startImmediately = 0, syncBlockToDmaRequests = 1, linkedListMode = 2 };
44-
enum class ChoppingEnable : uint32_t { normal = 0, chopping = 1 };
44+
enum class SyncMode : uint32_t { block = 0, sync = 1, linkedList = 2 };
4545
enum class Enabled : uint32_t { completed = 0, stop = 0, start = 1 };
4646
enum class StartTrigger : uint32_t { clear = 0, automatic = 0, manual = 1 };
4747

4848
struct {
4949
Direction direction : 1;
5050
MemoryAddressStep memoryAddressStep : 1;
5151
uint32_t : 6;
52-
ChoppingEnable choppingEnable : 1;
52+
uint32_t choppingEnable : 1;
5353
SyncMode syncMode : 2;
5454
uint32_t : 5;
5555
uint32_t choppingDmaWindowSize : 3; // Chopping DMA Window Size (1 SHL N words)
@@ -59,15 +59,25 @@ union CHCR {
5959
Enabled enabled : 1; // stopped/completed, start/enable/busy
6060
uint32_t : 3;
6161
StartTrigger startTrigger : 1;
62-
uint32_t : 3;
62+
uint32_t unknown1 : 1;
63+
uint32_t unknown2 : 1;
64+
uint32_t : 1;
6365
};
6466
uint32_t _reg;
6567
uint8_t _byte[4];
6668

6769
CHCR() : _reg(0) {}
70+
71+
const char* dir() const {
72+
if (direction == Direction::fromRam)
73+
return "<-";
74+
else
75+
return "->";
76+
}
6877
};
6978

7079
class DMAChannel {
80+
protected:
7181
int verbose;
7282
Channel channel;
7383

@@ -79,6 +89,8 @@ class DMAChannel {
7989

8090
virtual uint32_t readDevice();
8191
virtual void writeDevice(uint32_t data);
92+
virtual void maskControl();
93+
virtual void startTransfer();
8294

8395
public:
8496
bool irqFlag = false;

0 commit comments

Comments
 (0)