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