Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libsnes/bsnes/snes/cpu/dma/dma.cpp
2 views
1
#ifdef CPU_CPP
2
3
void CPU::dma_add_clocks(unsigned clocks) {
4
status.dma_clocks += clocks;
5
add_clocks(clocks);
6
}
7
8
//=============
9
//memory access
10
//=============
11
12
bool CPU::dma_transfer_valid(uint8 bbus, uint32 abus) {
13
//transfers from WRAM to WRAM are invalid; chip only has one address bus
14
if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) return false;
15
return true;
16
}
17
18
bool CPU::dma_addr_valid(uint32 abus) {
19
//A-bus access to B-bus or S-CPU registers are invalid
20
if((abus & 0x40ff00) == 0x2100) return false; //$[00-3f|80-bf]:[2100-21ff]
21
if((abus & 0x40fe00) == 0x4000) return false; //$[00-3f|80-bf]:[4000-41ff]
22
if((abus & 0x40ffe0) == 0x4200) return false; //$[00-3f|80-bf]:[4200-421f]
23
if((abus & 0x40ff80) == 0x4300) return false; //$[00-3f|80-bf]:[4300-437f]
24
return true;
25
}
26
27
uint8 CPU::dma_read(uint32 abus) {
28
if(dma_addr_valid(abus) == false) return 0x00;
29
return bus.read(abus);
30
}
31
32
//simulate two-stage pipeline for DMA transfers; example:
33
//cycle 0: read N+0
34
//cycle 1: write N+0 & read N+1 (parallel; one on A-bus, one on B-bus)
35
//cycle 2: write N+1 & read N+2 (parallel)
36
//cycle 3: write N+2
37
void CPU::dma_write(bool valid, unsigned addr, uint8 data) {
38
if(pipe.valid) bus.write(pipe.addr, pipe.data);
39
pipe.valid = valid;
40
pipe.addr = addr;
41
pipe.data = data;
42
}
43
44
void CPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) {
45
if(direction == 0) {
46
dma_add_clocks(4);
47
regs.mdr = dma_read(abus);
48
dma_add_clocks(4);
49
dma_write(dma_transfer_valid(bbus, abus), 0x2100 | bbus, regs.mdr);
50
} else {
51
dma_add_clocks(4);
52
regs.mdr = dma_transfer_valid(bbus, abus) ? bus.read(0x2100 | bbus) : 0x00;
53
dma_add_clocks(4);
54
dma_write(dma_addr_valid(abus), abus, regs.mdr);
55
}
56
}
57
58
//===================
59
//address calculation
60
//===================
61
62
uint8 CPU::dma_bbus(unsigned i, unsigned index) {
63
switch(channel[i].transfer_mode) { default:
64
case 0: return (channel[i].dest_addr); //0
65
case 1: return (channel[i].dest_addr + (index & 1)); //0,1
66
case 2: return (channel[i].dest_addr); //0,0
67
case 3: return (channel[i].dest_addr + ((index >> 1) & 1)); //0,0,1,1
68
case 4: return (channel[i].dest_addr + (index & 3)); //0,1,2,3
69
case 5: return (channel[i].dest_addr + (index & 1)); //0,1,0,1
70
case 6: return (channel[i].dest_addr); //0,0 [2]
71
case 7: return (channel[i].dest_addr + ((index >> 1) & 1)); //0,0,1,1 [3]
72
}
73
}
74
75
inline uint32 CPU::dma_addr(unsigned i) {
76
uint32 r = (channel[i].source_bank << 16) | (channel[i].source_addr);
77
78
if(channel[i].fixed_transfer == false) {
79
if(channel[i].reverse_transfer == false) {
80
channel[i].source_addr++;
81
} else {
82
channel[i].source_addr--;
83
}
84
}
85
86
return r;
87
}
88
89
inline uint32 CPU::hdma_addr(unsigned i) {
90
return (channel[i].source_bank << 16) | (channel[i].hdma_addr++);
91
}
92
93
inline uint32 CPU::hdma_iaddr(unsigned i) {
94
return (channel[i].indirect_bank << 16) | (channel[i].indirect_addr++);
95
}
96
97
//==============
98
//channel status
99
//==============
100
101
uint8 CPU::dma_enabled_channels() {
102
uint8 r = 0;
103
for(unsigned i = 0; i < 8; i++) {
104
if(channel[i].dma_enabled) r++;
105
}
106
return r;
107
}
108
109
inline bool CPU::hdma_active(unsigned i) {
110
return (channel[i].hdma_enabled && !channel[i].hdma_completed);
111
}
112
113
inline bool CPU::hdma_active_after(unsigned i) {
114
for(unsigned n = i + 1; n < 8; n++) {
115
if(hdma_active(n) == true) return true;
116
}
117
return false;
118
}
119
120
inline uint8 CPU::hdma_enabled_channels() {
121
uint8 r = 0;
122
for(unsigned i = 0; i < 8; i++) {
123
if(channel[i].hdma_enabled) r++;
124
}
125
return r;
126
}
127
128
inline uint8 CPU::hdma_active_channels() {
129
uint8 r = 0;
130
for(unsigned i = 0; i < 8; i++) {
131
if(hdma_active(i) == true) r++;
132
}
133
return r;
134
}
135
136
//==============
137
//core functions
138
//==============
139
140
void CPU::dma_run() {
141
dma_add_clocks(8);
142
dma_write(false);
143
dma_edge();
144
145
for(unsigned i = 0; i < 8; i++) {
146
if(channel[i].dma_enabled == false) continue;
147
148
unsigned index = 0;
149
do {
150
dma_transfer(channel[i].direction, dma_bbus(i, index++), dma_addr(i));
151
dma_edge();
152
} while(channel[i].dma_enabled && --channel[i].transfer_size);
153
154
dma_add_clocks(8);
155
dma_write(false);
156
dma_edge();
157
158
channel[i].dma_enabled = false;
159
}
160
161
status.irq_lock = true;
162
}
163
164
void CPU::hdma_update(unsigned i) {
165
dma_add_clocks(4);
166
regs.mdr = dma_read((channel[i].source_bank << 16) | channel[i].hdma_addr);
167
dma_add_clocks(4);
168
dma_write(false);
169
170
if((channel[i].line_counter & 0x7f) == 0) {
171
channel[i].line_counter = regs.mdr;
172
channel[i].hdma_addr++;
173
174
channel[i].hdma_completed = (channel[i].line_counter == 0);
175
channel[i].hdma_do_transfer = !channel[i].hdma_completed;
176
177
if(channel[i].indirect) {
178
dma_add_clocks(4);
179
regs.mdr = dma_read(hdma_addr(i));
180
channel[i].indirect_addr = regs.mdr << 8;
181
dma_add_clocks(4);
182
dma_write(false);
183
184
if(!channel[i].hdma_completed || hdma_active_after(i)) {
185
dma_add_clocks(4);
186
regs.mdr = dma_read(hdma_addr(i));
187
channel[i].indirect_addr >>= 8;
188
channel[i].indirect_addr |= regs.mdr << 8;
189
dma_add_clocks(4);
190
dma_write(false);
191
}
192
}
193
}
194
}
195
196
void CPU::hdma_run() {
197
dma_add_clocks(8);
198
dma_write(false);
199
200
for(unsigned i = 0; i < 8; i++) {
201
if(hdma_active(i) == false) continue;
202
channel[i].dma_enabled = false; //HDMA run during DMA will stop DMA mid-transfer
203
204
if(channel[i].hdma_do_transfer) {
205
static const unsigned transfer_length[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
206
unsigned length = transfer_length[channel[i].transfer_mode];
207
for(unsigned index = 0; index < length; index++) {
208
unsigned addr = channel[i].indirect == false ? hdma_addr(i) : hdma_iaddr(i);
209
dma_transfer(channel[i].direction, dma_bbus(i, index), addr);
210
}
211
}
212
}
213
214
for(unsigned i = 0; i < 8; i++) {
215
if(hdma_active(i) == false) continue;
216
217
channel[i].line_counter--;
218
channel[i].hdma_do_transfer = channel[i].line_counter & 0x80;
219
hdma_update(i);
220
}
221
222
status.irq_lock = true;
223
}
224
225
void CPU::hdma_init_reset() {
226
for(unsigned i = 0; i < 8; i++) {
227
channel[i].hdma_completed = false;
228
channel[i].hdma_do_transfer = false;
229
}
230
}
231
232
void CPU::hdma_init() {
233
dma_add_clocks(8);
234
dma_write(false);
235
236
for(unsigned i = 0; i < 8; i++) {
237
if(!channel[i].hdma_enabled) continue;
238
channel[i].dma_enabled = false; //HDMA init during DMA will stop DMA mid-transfer
239
240
channel[i].hdma_addr = channel[i].source_addr;
241
channel[i].line_counter = 0;
242
hdma_update(i);
243
}
244
245
status.irq_lock = true;
246
}
247
248
//==============
249
//initialization
250
//==============
251
252
void CPU::dma_power() {
253
for(unsigned i = 0; i < 8; i++) {
254
channel[i].direction = 1;
255
channel[i].indirect = true;
256
channel[i].unused = true;
257
channel[i].reverse_transfer = true;
258
channel[i].fixed_transfer = true;
259
channel[i].transfer_mode = 7;
260
261
channel[i].dest_addr = 0xff;
262
263
channel[i].source_addr = 0xffff;
264
channel[i].source_bank = 0xff;
265
266
channel[i].transfer_size = 0xffff;
267
channel[i].indirect_bank = 0xff;
268
269
channel[i].hdma_addr = 0xffff;
270
channel[i].line_counter = 0xff;
271
channel[i].unknown = 0xff;
272
}
273
}
274
275
void CPU::dma_reset() {
276
for(unsigned i = 0; i < 8; i++) {
277
channel[i].dma_enabled = false;
278
channel[i].hdma_enabled = false;
279
280
channel[i].hdma_completed = false;
281
channel[i].hdma_do_transfer = false;
282
}
283
284
pipe.valid = false;
285
pipe.addr = 0;
286
pipe.data = 0;
287
}
288
289
#endif
290
291