3
3
#include < magic_enum.hpp>
4
4
#include " config.h"
5
5
#include " system.h"
6
+ #include < unordered_set>
6
7
7
8
namespace device ::dma {
8
9
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) {
17
18
if (address < 0x4 ) return baseAddress._byte [address];
18
19
if (address >= 0x4 && address < 0x8 ) return count._byte [address - 4 ];
19
20
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);
22
21
return 0 ;
23
22
}
24
23
25
24
void DMAChannel::write (uint32_t address, uint8_t data) {
26
- if (address < 0x4 )
25
+ if (address < 0x4 ) {
27
26
baseAddress._byte [address] = data;
28
- else if (address >= 0x4 && address < 0x8 )
27
+ baseAddress._reg &= 0xffffff ;
28
+ } else if (address >= 0x4 && address < 0x8 ) {
29
29
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 ());
51
66
}
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) {
54
83
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 ;
65
84
}
66
85
}
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));
94
90
}
95
91
}
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 ;
99
106
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);
102
110
}
103
111
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
+ }
108
116
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 ;
115
119
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 ;
120
123
}
121
- baseAddress. address = addr;
124
+ visited. insert ( addr) ;
122
125
}
123
-
124
- irqFlag = true ;
125
- control.enabled = CHCR::Enabled::completed;
126
+ baseAddress.address = addr;
126
127
}
128
+
129
+ irqFlag = true ;
130
+ control.enabled = CHCR::Enabled::completed;
127
131
}
128
132
} // namespace device::dma
0 commit comments