yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dma.cc
Go to the documentation of this file.
2
3namespace yaze {
4namespace emu {
5
6static const int bAdrOffsets[8][4] = {{0, 0, 0, 0}, {0, 1, 0, 1}, {0, 0, 0, 0},
7 {0, 0, 1, 1}, {0, 1, 2, 3}, {0, 1, 0, 1},
8 {0, 0, 0, 0}, {0, 0, 1, 1}};
9
10static const int transferLength[8] = {1, 2, 2, 4, 4, 4, 2, 4};
11
12void ResetDma(MemoryImpl* memory) {
13 auto channel = memory->dma_channels();
14 for (int i = 0; i < 8; i++) {
15 channel[i].b_addr = 0xff;
16 channel[i].a_addr = 0xffff;
17 channel[i].a_bank = 0xff;
18 channel[i].size = 0xffff;
19 channel[i].ind_bank = 0xff;
20 channel[i].table_addr = 0xffff;
21 channel[i].rep_count = 0xff;
22 channel[i].unusedByte = 0xff;
23 channel[i].dma_active = false;
24 channel[i].hdma_active = false;
25 channel[i].mode = 7;
26 channel[i].fixed = true;
27 channel[i].decrement = true;
28 channel[i].indirect = true;
29 channel[i].from_b = true;
30 channel[i].unusedBit = true;
31 channel[i].do_transfer = false;
32 channel[i].terminated = false;
33 }
34 memory->set_dma_state(0);
35 memory->set_hdma_init_requested(false);
36 memory->set_hdma_run_requested(false);
37}
38
39uint8_t ReadDma(MemoryImpl* memory, uint16_t adr) {
40 auto channel = memory->dma_channels();
41 uint8_t c = (adr & 0x70) >> 4;
42 switch (adr & 0xf) {
43 case 0x0: {
44 uint8_t val = channel[c].mode;
45 val |= channel[c].fixed << 3;
46 val |= channel[c].decrement << 4;
47 val |= channel[c].unusedBit << 5;
48 val |= channel[c].indirect << 6;
49 val |= channel[c].from_b << 7;
50 return val;
51 }
52 case 0x1: {
53 return channel[c].b_addr;
54 }
55 case 0x2: {
56 return channel[c].a_addr & 0xff;
57 }
58 case 0x3: {
59 return channel[c].a_addr >> 8;
60 }
61 case 0x4: {
62 return channel[c].a_bank;
63 }
64 case 0x5: {
65 return channel[c].size & 0xff;
66 }
67 case 0x6: {
68 return channel[c].size >> 8;
69 }
70 case 0x7: {
71 return channel[c].ind_bank;
72 }
73 case 0x8: {
74 return channel[c].table_addr & 0xff;
75 }
76 case 0x9: {
77 return channel[c].table_addr >> 8;
78 }
79 case 0xa: {
80 return channel[c].rep_count;
81 }
82 case 0xb:
83 case 0xf: {
84 return channel[c].unusedByte;
85 }
86 default: {
87 return memory->open_bus();
88 }
89 }
90}
91
92void WriteDma(MemoryImpl* memory, uint16_t adr, uint8_t val) {
93 auto channel = memory->dma_channels();
94 uint8_t c = (adr & 0x70) >> 4;
95 switch (adr & 0xf) {
96 case 0x0: {
97 channel[c].mode = val & 0x7;
98 channel[c].fixed = val & 0x8;
99 channel[c].decrement = val & 0x10;
100 channel[c].unusedBit = val & 0x20;
101 channel[c].indirect = val & 0x40;
102 channel[c].from_b = val & 0x80;
103 break;
104 }
105 case 0x1: {
106 channel[c].b_addr = val;
107 break;
108 }
109 case 0x2: {
110 channel[c].a_addr = (channel[c].a_addr & 0xff00) | val;
111 break;
112 }
113 case 0x3: {
114 channel[c].a_addr = (channel[c].a_addr & 0xff) | (val << 8);
115 break;
116 }
117 case 0x4: {
118 channel[c].a_bank = val;
119 break;
120 }
121 case 0x5: {
122 channel[c].size = (channel[c].size & 0xff00) | val;
123 break;
124 }
125 case 0x6: {
126 channel[c].size = (channel[c].size & 0xff) | (val << 8);
127 break;
128 }
129 case 0x7: {
130 channel[c].ind_bank = val;
131 break;
132 }
133 case 0x8: {
134 channel[c].table_addr = (channel[c].table_addr & 0xff00) | val;
135 break;
136 }
137 case 0x9: {
138 channel[c].table_addr = (channel[c].table_addr & 0xff) | (val << 8);
139 break;
140 }
141 case 0xa: {
142 channel[c].rep_count = val;
143 break;
144 }
145 case 0xb:
146 case 0xf: {
147 channel[c].unusedByte = val;
148 break;
149 }
150 default: {
151 break;
152 }
153 }
154}
155
156void DoDma(Snes* snes, MemoryImpl* memory, int cpuCycles) {
157 auto channel = memory->dma_channels();
158 snes->cpu().set_int_delay(true);
159
160 // align to multiple of 8
161 snes->SyncCycles(true, 8);
162
163 // full transfer overhead
164 WaitCycle(snes, memory);
165 for (int i = 0; i < 8; i++) {
166 if (!channel[i].dma_active)
167 continue;
168
169 // do channel i
170 WaitCycle(snes, memory); // overhead per channel
171 int offIndex = 0;
172 while (channel[i].dma_active) {
173 WaitCycle(snes, memory);
174 TransferByte(snes, memory, channel[i].a_addr, channel[i].a_bank,
175 channel[i].b_addr + bAdrOffsets[channel[i].mode][offIndex++],
176 channel[i].from_b);
177 offIndex &= 3;
178 if (!channel[i].fixed) {
179 channel[i].a_addr += channel[i].decrement ? -1 : 1;
180 }
181 channel[i].size--;
182 if (channel[i].size == 0) {
183 channel[i].dma_active = false;
184 }
185 }
186 }
187
188 // re-align to cpu cycles
189 snes->SyncCycles(false, cpuCycles);
190}
191
192void HandleDma(Snes* snes, MemoryImpl* memory, int cpu_cycles) {
193 // if hdma triggered, do it, except if dmastate indicates dma will be done now
194 // (it will be done as part of the dma in that case)
195 if (memory->hdma_init_requested() && memory->dma_state() != 2)
196 InitHdma(snes, memory, true, cpu_cycles);
197 if (memory->hdma_run_requested() && memory->dma_state() != 2)
198 DoHdma(snes, memory, true, cpu_cycles);
199 if (memory->dma_state() == 1) {
200 memory->set_dma_state(2);
201 return;
202 }
203 if (memory->dma_state() == 2) {
204 // do dma
205 DoDma(snes, memory, cpu_cycles);
206 memory->set_dma_state(0);
207 }
208}
209
210void WaitCycle(Snes* snes, MemoryImpl* memory) {
211 // run hdma if requested, no sync (already sycned due to dma)
212 if (memory->hdma_init_requested())
213 InitHdma(snes, memory, false, 0);
214 if (memory->hdma_run_requested())
215 DoHdma(snes, memory, false, 0);
216
217 snes->RunCycles(8);
218}
219
220void InitHdma(Snes* snes, MemoryImpl* memory, bool do_sync, int cpu_cycles) {
221 auto channel = memory->dma_channels();
222 memory->set_hdma_init_requested(false);
223 bool hdmaEnabled = false;
224 // check if a channel is enabled, and do reset
225 for (int i = 0; i < 8; i++) {
226 if (channel[i].hdma_active)
227 hdmaEnabled = true;
228 channel[i].do_transfer = false;
229 channel[i].terminated = false;
230 }
231 if (!hdmaEnabled)
232 return;
233 snes->cpu().set_int_delay(true);
234 if (do_sync)
235 snes->SyncCycles(true, 8);
236
237 // full transfer overhead
238 snes->RunCycles(8);
239 for (int i = 0; i < 8; i++) {
240 if (channel[i].hdma_active) {
241 // terminate any dma
242 channel[i].dma_active = false;
243 // load address, repCount, and indirect address if needed
244 snes->RunCycles(8);
245 channel[i].table_addr = channel[i].a_addr;
246 channel[i].rep_count =
247 snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++);
248 if (channel[i].rep_count == 0)
249 channel[i].terminated = true;
250 if (channel[i].indirect) {
251 snes->RunCycles(8);
252 channel[i].size =
253 snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++);
254 snes->RunCycles(8);
255 channel[i].size |=
256 snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++)
257 << 8;
258 }
259 channel[i].do_transfer = true;
260 }
261 }
262 if (do_sync)
263 snes->SyncCycles(false, cpu_cycles);
264}
265
266void DoHdma(Snes* snes, MemoryImpl* memory, bool do_sync, int cycles) {
267 auto channel = memory->dma_channels();
268 memory->set_hdma_run_requested(false);
269 bool hdmaActive = false;
270 int lastActive = 0;
271 for (int i = 0; i < 8; i++) {
272 if (channel[i].hdma_active) {
273 hdmaActive = true;
274 if (!channel[i].terminated)
275 lastActive = i;
276 }
277 }
278
279 if (!hdmaActive)
280 return;
281 snes->cpu().set_int_delay(true);
282
283 if (do_sync)
284 snes->SyncCycles(true, 8);
285
286 // full transfer overhead
287 snes->RunCycles(8);
288 // do all copies
289 for (int i = 0; i < 8; i++) {
290 // terminate any dma
291 if (channel[i].hdma_active)
292 channel[i].dma_active = false;
293 if (channel[i].hdma_active && !channel[i].terminated) {
294 // do the hdma
295 if (channel[i].do_transfer) {
296 for (int j = 0; j < transferLength[channel[i].mode]; j++) {
297 snes->RunCycles(8);
298 if (channel[i].indirect) {
299 TransferByte(snes, memory, channel[i].size++, channel[i].ind_bank,
300 channel[i].b_addr + bAdrOffsets[channel[i].mode][j],
301 channel[i].from_b);
302 } else {
303 TransferByte(snes, memory, channel[i].table_addr++,
304 channel[i].a_bank,
305 channel[i].b_addr + bAdrOffsets[channel[i].mode][j],
306 channel[i].from_b);
307 }
308 }
309 }
310 }
311 }
312 // do all updates
313 for (int i = 0; i < 8; i++) {
314 if (channel[i].hdma_active && !channel[i].terminated) {
315 channel[i].rep_count--;
316 channel[i].do_transfer = channel[i].rep_count & 0x80;
317 snes->RunCycles(8);
318 uint8_t newRepCount =
319 snes->Read((channel[i].a_bank << 16) | channel[i].table_addr);
320 if ((channel[i].rep_count & 0x7f) == 0) {
321 channel[i].rep_count = newRepCount;
322 channel[i].table_addr++;
323 if (channel[i].indirect) {
324 if (channel[i].rep_count == 0 && i == lastActive) {
325 // if this is the last active channel, only fetch high, and use 0
326 // for low
327 channel[i].size = 0;
328 } else {
329 snes->RunCycles(8);
330 channel[i].size =
331 snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++);
332 }
333 snes->RunCycles(8);
334 channel[i].size |=
335 snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++)
336 << 8;
337 }
338 if (channel[i].rep_count == 0)
339 channel[i].terminated = true;
340 channel[i].do_transfer = true;
341 }
342 }
343 }
344
345 if (do_sync)
346 snes->SyncCycles(false, cycles);
347}
348
349void TransferByte(Snes* snes, MemoryImpl* memory, uint16_t aAdr, uint8_t aBank,
350 uint8_t bAdr, bool fromB) {
351 // accessing 0x2180 via b-bus while a-bus accesses ram gives open bus
352 bool validB =
353 !(bAdr == 0x80 &&
354 (aBank == 0x7e || aBank == 0x7f ||
355 ((aBank < 0x40 || (aBank >= 0x80 && aBank < 0xc0)) && aAdr < 0x2000)));
356 // accesing b-bus, or dma regs via a-bus gives open bus
357 bool validA = !((aBank < 0x40 || (aBank >= 0x80 && aBank < 0xc0)) &&
358 (aAdr == 0x420b || aAdr == 0x420c ||
359 (aAdr >= 0x4300 && aAdr < 0x4380) ||
360 (aAdr >= 0x2100 && aAdr < 0x2200)));
361 auto record_vram = [&](uint8_t b_addr) {
362 switch (b_addr) {
363 case 0x18: // VRAM data write low
364 case 0x19: // VRAM data write high
365 case 0x04: // OAM data write
366 case 0x22: // CGRAM data write
367 snes->AccumulateVramBytes(1);
368 break;
369 default:
370 break;
371 }
372 };
373 if (fromB) {
374 uint8_t val = validB ? snes->ReadBBus(bAdr) : memory->open_bus();
375 if (validA) {
376 snes->AccumulateDmaBytes(1);
377 record_vram(bAdr);
378 snes->Write((aBank << 16) | aAdr, val);
379 }
380 } else {
381 uint8_t val =
382 validA ? snes->Read((aBank << 16) | aAdr) : memory->open_bus();
383 if (validB) {
384 snes->AccumulateDmaBytes(1);
385 record_vram(bAdr);
386 snes->WriteBBus(bAdr, val);
387 }
388 }
389}
390
391void StartDma(MemoryImpl* memory, uint8_t val, bool hdma) {
392 auto channel = memory->dma_channels();
393 for (int i = 0; i < 8; i++) {
394 if (hdma) {
395 channel[i].hdma_active = val & (1 << i);
396 } else {
397 channel[i].dma_active = val & (1 << i);
398 }
399 }
400 if (!hdma) {
401 memory->set_dma_state(val != 0 ? 1 : 0);
402 }
403}
404
405} // namespace emu
406} // namespace yaze
Implementation of the Memory interface for emulating memory in a SNES system.
Definition memory.h:118
auto open_bus() const -> uint8_t override
Definition memory.h:258
void set_dma_state(uint8_t value)
Definition memory.h:281
auto dma_state() -> uint8_t &
Definition memory.h:280
auto dma_channels() -> DmaChannel *
Definition memory.h:282
void set_hdma_run_requested(bool value) override
Definition memory.h:267
auto hdma_init_requested() const -> bool override
Definition memory.h:259
void set_hdma_init_requested(bool value) override
Definition memory.h:270
auto hdma_run_requested() const -> bool override
Definition memory.h:262
void RunCycles(int cycles)
Definition snes.cc:473
void WriteBBus(uint8_t adr, uint8_t val)
Definition snes.cc:626
uint8_t Read(uint32_t adr)
Definition snes.cc:620
uint8_t ReadBBus(uint8_t adr)
Definition snes.cc:494
void Write(uint32_t adr, uint8_t val)
Definition snes.cc:770
auto cpu() -> Cpu &
Definition snes.h:84
void SyncCycles(bool start, int sync_cycles)
Definition snes.cc:483
void AccumulateDmaBytes(uint32_t bytes)
Definition snes.h:105
void AccumulateVramBytes(uint32_t bytes)
Definition snes.h:106
void TransferByte(Snes *snes, MemoryImpl *memory, uint16_t aAdr, uint8_t aBank, uint8_t bAdr, bool fromB)
Definition dma.cc:349
void ResetDma(MemoryImpl *memory)
Definition dma.cc:12
void WriteDma(MemoryImpl *memory, uint16_t adr, uint8_t val)
Definition dma.cc:92
void HandleDma(Snes *snes, MemoryImpl *memory, int cpu_cycles)
Definition dma.cc:192
void WaitCycle(Snes *snes, MemoryImpl *memory)
Definition dma.cc:210
uint8_t ReadDma(MemoryImpl *memory, uint16_t adr)
Definition dma.cc:39
void DoDma(Snes *snes, MemoryImpl *memory, int cpuCycles)
Definition dma.cc:156
void StartDma(MemoryImpl *memory, uint8_t val, bool hdma)
Definition dma.cc:391
void InitHdma(Snes *snes, MemoryImpl *memory, bool do_sync, int cpu_cycles)
Definition dma.cc:220
void DoHdma(Snes *snes, MemoryImpl *memory, bool do_sync, int cycles)
Definition dma.cc:266