yaze 0.2.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) continue;
167 // do channel i
168 WaitCycle(snes, memory); // overhead per channel
169 int offIndex = 0;
170 while (channel[i].dma_active) {
171 WaitCycle(snes, memory);
172 TransferByte(snes, memory, channel[i].a_addr, channel[i].a_bank,
173 channel[i].b_addr + bAdrOffsets[channel[i].mode][offIndex++],
174 channel[i].from_b);
175 offIndex &= 3;
176 if (!channel[i].fixed) {
177 channel[i].a_addr += channel[i].decrement ? -1 : 1;
178 }
179 channel[i].size--;
180 if (channel[i].size == 0) {
181 channel[i].dma_active = false;
182 }
183 }
184 }
185
186 // re-align to cpu cycles
187 snes->SyncCycles(false, cpuCycles);
188}
189
190void HandleDma(Snes* snes, MemoryImpl* memory, int cpu_cycles) {
191 // if hdma triggered, do it, except if dmastate indicates dma will be done now
192 // (it will be done as part of the dma in that case)
193 if (memory->hdma_init_requested() && memory->dma_state() != 2)
194 InitHdma(snes, memory, true, cpu_cycles);
195 if (memory->hdma_run_requested() && memory->dma_state() != 2)
196 DoHdma(snes, memory, true, cpu_cycles);
197 if (memory->dma_state() == 1) {
198 memory->set_dma_state(2);
199 return;
200 }
201 if (memory->dma_state() == 2) {
202 // do dma
203 DoDma(snes, memory, cpu_cycles);
204 memory->set_dma_state(0);
205 }
206}
207
208void WaitCycle(Snes* snes, MemoryImpl* memory) {
209 // run hdma if requested, no sync (already sycned due to dma)
210 if (memory->hdma_init_requested()) InitHdma(snes, memory, false, 0);
211 if (memory->hdma_run_requested()) DoHdma(snes, memory, false, 0);
212
213 snes->RunCycles(8);
214}
215
216void InitHdma(Snes* snes, MemoryImpl* memory, bool do_sync, int cpu_cycles) {
217 auto channel = memory->dma_channels();
218 memory->set_hdma_init_requested(false);
219 bool hdmaEnabled = false;
220 // check if a channel is enabled, and do reset
221 for (int i = 0; i < 8; i++) {
222 if (channel[i].hdma_active) hdmaEnabled = true;
223 channel[i].do_transfer = false;
224 channel[i].terminated = false;
225 }
226 if (!hdmaEnabled) return;
227 snes->cpu().set_int_delay(true);
228 if (do_sync) snes->SyncCycles(true, 8);
229
230 // full transfer overhead
231 snes->RunCycles(8);
232 for (int i = 0; i < 8; i++) {
233 if (channel[i].hdma_active) {
234 // terminate any dma
235 channel[i].dma_active = false;
236 // load address, repCount, and indirect address if needed
237 snes->RunCycles(8);
238 channel[i].table_addr = channel[i].a_addr;
239 channel[i].rep_count =
240 snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++);
241 if (channel[i].rep_count == 0) channel[i].terminated = true;
242 if (channel[i].indirect) {
243 snes->RunCycles(8);
244 channel[i].size =
245 snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++);
246 snes->RunCycles(8);
247 channel[i].size |=
248 snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++) << 8;
249 }
250 channel[i].do_transfer = true;
251 }
252 }
253 if (do_sync) snes->SyncCycles(false, cpu_cycles);
254}
255
256void DoHdma(Snes* snes, MemoryImpl* memory, bool do_sync, int cycles) {
257 auto channel = memory->dma_channels();
258 memory->set_hdma_run_requested(false);
259 bool hdmaActive = false;
260 int lastActive = 0;
261 for (int i = 0; i < 8; i++) {
262 if (channel[i].hdma_active) {
263 hdmaActive = true;
264 if (!channel[i].terminated) lastActive = i;
265 }
266 }
267
268 if (!hdmaActive) return;
269 snes->cpu().set_int_delay(true);
270
271 if (do_sync) snes->SyncCycles(true, 8);
272
273 // full transfer overhead
274 snes->RunCycles(8);
275 // do all copies
276 for (int i = 0; i < 8; i++) {
277 // terminate any dma
278 if (channel[i].hdma_active) channel[i].dma_active = false;
279 if (channel[i].hdma_active && !channel[i].terminated) {
280 // do the hdma
281 if (channel[i].do_transfer) {
282 for (int j = 0; j < transferLength[channel[i].mode]; j++) {
283 snes->RunCycles(8);
284 if (channel[i].indirect) {
285 TransferByte(snes, memory, channel[i].size++, channel[i].ind_bank,
286 channel[i].b_addr + bAdrOffsets[channel[i].mode][j],
287 channel[i].from_b);
288 } else {
289 TransferByte(snes, memory, channel[i].table_addr++, channel[i].a_bank,
290 channel[i].b_addr + bAdrOffsets[channel[i].mode][j],
291 channel[i].from_b);
292 }
293 }
294 }
295 }
296 }
297 // do all updates
298 for (int i = 0; i < 8; i++) {
299 if (channel[i].hdma_active && !channel[i].terminated) {
300 channel[i].rep_count--;
301 channel[i].do_transfer = channel[i].rep_count & 0x80;
302 snes->RunCycles(8);
303 uint8_t newRepCount =
304 snes->Read((channel[i].a_bank << 16) | channel[i].table_addr);
305 if ((channel[i].rep_count & 0x7f) == 0) {
306 channel[i].rep_count = newRepCount;
307 channel[i].table_addr++;
308 if (channel[i].indirect) {
309 if (channel[i].rep_count == 0 && i == lastActive) {
310 // if this is the last active channel, only fetch high, and use 0
311 // for low
312 channel[i].size = 0;
313 } else {
314 snes->RunCycles(8);
315 channel[i].size =
316 snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++);
317 }
318 snes->RunCycles(8);
319 channel[i].size |=
320 snes->Read((channel[i].a_bank << 16) | channel[i].table_addr++) << 8;
321 }
322 if (channel[i].rep_count == 0) channel[i].terminated = true;
323 channel[i].do_transfer = true;
324 }
325 }
326 }
327
328 if (do_sync) snes->SyncCycles(false, cycles);
329}
330
331void TransferByte(Snes* snes, MemoryImpl* memory, uint16_t aAdr, uint8_t aBank,
332 uint8_t bAdr, bool fromB) {
333 // accessing 0x2180 via b-bus while a-bus accesses ram gives open bus
334 bool validB =
335 !(bAdr == 0x80 &&
336 (aBank == 0x7e || aBank == 0x7f ||
337 ((aBank < 0x40 || (aBank >= 0x80 && aBank < 0xc0)) && aAdr < 0x2000)));
338 // accesing b-bus, or dma regs via a-bus gives open bus
339 bool validA = !((aBank < 0x40 || (aBank >= 0x80 && aBank < 0xc0)) &&
340 (aAdr == 0x420b || aAdr == 0x420c ||
341 (aAdr >= 0x4300 && aAdr < 0x4380) ||
342 (aAdr >= 0x2100 && aAdr < 0x2200)));
343 if (fromB) {
344 uint8_t val = validB ? snes->ReadBBus(bAdr) : memory->open_bus();
345 if (validA) snes->Write((aBank << 16) | aAdr, val);
346 } else {
347 uint8_t val =
348 validA ? snes->Read((aBank << 16) | aAdr) : memory->open_bus();
349 if (validB) snes->WriteBBus(bAdr, val);
350 }
351}
352
353void StartDma(MemoryImpl* memory, uint8_t val, bool hdma) {
354 auto channel = memory->dma_channels();
355 for (int i = 0; i < 8; i++) {
356 if (hdma) {
357 channel[i].hdma_active = val & (1 << i);
358 } else {
359 channel[i].dma_active = val & (1 << i);
360 }
361 }
362 if (!hdma) {
363 memory->set_dma_state(val != 0 ? 1 : 0);
364 }
365}
366
367} // namespace emu
368} // 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:224
void WriteBBus(uint8_t adr, uint8_t val)
Definition snes.cc:350
uint8_t Read(uint32_t adr)
Definition snes.cc:344
uint8_t ReadBBus(uint8_t adr)
Definition snes.cc:245
void Write(uint32_t adr, uint8_t val)
Definition snes.cc:468
auto cpu() -> Cpu &
Definition snes.h:57
void SyncCycles(bool start, int sync_cycles)
Definition snes.cc:234
SNES Emulation and debugging tools.
Definition apu.cc:13
void TransferByte(Snes *snes, MemoryImpl *memory, uint16_t aAdr, uint8_t aBank, uint8_t bAdr, bool fromB)
Definition dma.cc:331
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:190
void WaitCycle(Snes *snes, MemoryImpl *memory)
Definition dma.cc:208
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:353
void InitHdma(Snes *snes, MemoryImpl *memory, bool do_sync, int cpu_cycles)
Definition dma.cc:216
void DoHdma(Snes *snes, MemoryImpl *memory, bool do_sync, int cycles)
Definition dma.cc:256
Main namespace for the application.
Definition controller.cc:18