1 | /*****************************************************************************\ |
2 | Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. |
3 | This file is licensed under the Snes9x License. |
4 | For further information, consult the LICENSE file in the root directory. |
5 | \*****************************************************************************/ |
6 | |
7 | #include "snes9x.h" |
8 | #include "memmap.h" |
9 | #include "dma.h" |
10 | #include "apu/apu.h" |
11 | #include "sdd1emu.h" |
12 | #include "spc7110emu.h" |
13 | #ifdef DEBUGGER |
14 | #include "missing.h" |
15 | #endif |
16 | |
17 | #define ADD_CYCLES(n) { CPU.Cycles += (n); } |
18 | |
19 | extern uint8 *HDMAMemPointers[8]; |
20 | extern int HDMA_ModeByteCounts[8]; |
21 | extern SPC7110 s7emu; |
22 | |
23 | static uint8 sdd1_decode_buffer[0x10000]; |
24 | |
25 | static inline bool8 addCyclesInDMA (uint8); |
26 | static inline bool8 HDMAReadLineCount (int); |
27 | |
28 | |
29 | static inline bool8 addCyclesInDMA (uint8 dma_channel) |
30 | { |
31 | // Add 8 cycles per byte, sync APU, and do HC related events. |
32 | // If HDMA was done in S9xDoHEventProcessing(), check if it used the same channel as DMA. |
33 | ADD_CYCLES(SLOW_ONE_CYCLE); |
34 | while (CPU.Cycles >= CPU.NextEvent) |
35 | S9xDoHEventProcessing(); |
36 | |
37 | if (CPU.HDMARanInDMA & (1 << dma_channel)) |
38 | { |
39 | CPU.HDMARanInDMA = 0; |
40 | #ifdef DEBUGGER |
41 | printf("HDMA and DMA use the same channel %d!\n" , dma_channel); |
42 | #endif |
43 | // If HDMA triggers in the middle of DMA transfer and it uses the same channel, |
44 | // it kills the DMA transfer immediately. $43x2 and $43x5 stop updating. |
45 | return (FALSE); |
46 | } |
47 | |
48 | CPU.HDMARanInDMA = 0; |
49 | return (TRUE); |
50 | } |
51 | |
52 | bool8 S9xDoDMA (uint8 Channel) |
53 | { |
54 | CPU.InDMA = TRUE; |
55 | CPU.InDMAorHDMA = TRUE; |
56 | CPU.CurrentDMAorHDMAChannel = Channel; |
57 | |
58 | SDMA *d = &DMA[Channel]; |
59 | |
60 | // Check invalid DMA first |
61 | if ((d->ABank == 0x7E || d->ABank == 0x7F) && d->BAddress == 0x80 && !d->ReverseTransfer) |
62 | { |
63 | // Attempting a DMA from WRAM to $2180 will not work, WRAM will not be written. |
64 | // Attempting a DMA from $2180 to WRAM will similarly not work, |
65 | // the value written is (initially) the OpenBus value. |
66 | // In either case, the address in $2181-3 is not incremented. |
67 | |
68 | // Does an invalid DMA actually take time? |
69 | // I'd say yes, since 'invalid' is probably just the WRAM chip |
70 | // not being able to read and write itself at the same time |
71 | // And no, PPU.WRAM should not be updated. |
72 | |
73 | int32 c = d->TransferBytes; |
74 | // Writing $0000 to $43x5 actually results in a transfer of $10000 bytes, not 0. |
75 | if (c == 0) |
76 | c = 0x10000; |
77 | |
78 | // 8 cycles per channel |
79 | ADD_CYCLES(SLOW_ONE_CYCLE); |
80 | // 8 cycles per byte |
81 | while (c) |
82 | { |
83 | d->TransferBytes--; |
84 | d->AAddress++; |
85 | c--; |
86 | if (!addCyclesInDMA(Channel)) |
87 | { |
88 | CPU.InDMA = FALSE; |
89 | CPU.InDMAorHDMA = FALSE; |
90 | CPU.CurrentDMAorHDMAChannel = -1; |
91 | return (FALSE); |
92 | } |
93 | } |
94 | |
95 | #ifdef DEBUGGER |
96 | if (Settings.TraceDMA) |
97 | { |
98 | sprintf(String, "DMA[%d]: WRAM Bank:%02X->$2180" , Channel, d->ABank); |
99 | S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String); |
100 | } |
101 | #endif |
102 | |
103 | CPU.InDMA = FALSE; |
104 | CPU.InDMAorHDMA = FALSE; |
105 | CPU.CurrentDMAorHDMAChannel = -1; |
106 | return (TRUE); |
107 | } |
108 | |
109 | // Prepare for accessing $2118-2119 |
110 | switch (d->BAddress) |
111 | { |
112 | case 0x18: |
113 | case 0x19: |
114 | if (IPPU.RenderThisFrame) |
115 | FLUSH_REDRAW(); |
116 | break; |
117 | } |
118 | |
119 | int32 inc = d->AAddressFixed ? 0 : (!d->AAddressDecrement ? 1 : -1); |
120 | int32 count = d->TransferBytes; |
121 | // Writing $0000 to $43x5 actually results in a transfer of $10000 bytes, not 0. |
122 | if (count == 0) |
123 | count = 0x10000; |
124 | |
125 | // Prepare for custom chip DMA |
126 | |
127 | // S-DD1 |
128 | |
129 | uint8 *in_sdd1_dma = NULL; |
130 | |
131 | if (Settings.SDD1) |
132 | { |
133 | if (d->AAddressFixed && Memory.FillRAM[0x4801] > 0) |
134 | { |
135 | // XXX: Should probably verify that we're DMAing from ROM? |
136 | // And somewhere we should make sure we're not running across a mapping boundary too. |
137 | // Hacky support for pre-decompressed S-DD1 data |
138 | inc = !d->AAddressDecrement ? 1 : -1; |
139 | |
140 | uint8 *in_ptr = S9xGetBasePointer(((d->ABank << 16) | d->AAddress)); |
141 | if (in_ptr) |
142 | { |
143 | in_ptr += d->AAddress; |
144 | SDD1_decompress(sdd1_decode_buffer, in_ptr, d->TransferBytes); |
145 | } |
146 | #ifdef DEBUGGER |
147 | else |
148 | { |
149 | sprintf(String, "S-DD1: DMA from non-block address $%02X:%04X" , d->ABank, d->AAddress); |
150 | S9xMessage(S9X_WARNING, S9X_DMA_TRACE, String); |
151 | } |
152 | #endif |
153 | |
154 | in_sdd1_dma = sdd1_decode_buffer; |
155 | } |
156 | |
157 | Memory.FillRAM[0x4801] = 0; |
158 | } |
159 | |
160 | // SPC7110 |
161 | |
162 | uint8 *spc7110_dma = NULL; |
163 | |
164 | if (Settings.SPC7110) |
165 | { |
166 | if (d->AAddress == 0x4800 || d->ABank == 0x50) |
167 | { |
168 | spc7110_dma = new uint8[d->TransferBytes]; |
169 | for (int i = 0; i < d->TransferBytes; i++) |
170 | spc7110_dma[i] = s7emu.decomp.read(); |
171 | |
172 | int32 icount = s7emu.r4809 | (s7emu.r480a << 8); |
173 | icount -= d->TransferBytes; |
174 | s7emu.r4809 = icount & 0x00ff; |
175 | s7emu.r480a = (icount & 0xff00) >> 8; |
176 | |
177 | inc = 1; |
178 | d->AAddress -= count; |
179 | } |
180 | } |
181 | |
182 | // SA-1 |
183 | |
184 | bool8 in_sa1_dma = FALSE; |
185 | |
186 | if (Settings.SA1) |
187 | { |
188 | if (SA1.in_char_dma && d->BAddress == 0x18 && (d->ABank & 0xf0) == 0x40) |
189 | { |
190 | // Perform packed bitmap to PPU character format conversion on the data |
191 | // before transmitting it to V-RAM via-DMA. |
192 | int32 num_chars = 1 << ((Memory.FillRAM[0x2231] >> 2) & 7); |
193 | int32 depth = (Memory.FillRAM[0x2231] & 3) == 0 ? 8 : (Memory.FillRAM[0x2231] & 3) == 1 ? 4 : 2; |
194 | int32 bytes_per_char = 8 * depth; |
195 | int32 bytes_per_line = depth * num_chars; |
196 | int32 char_line_bytes = bytes_per_char * num_chars; |
197 | uint32 addr = (d->AAddress / char_line_bytes) * char_line_bytes; |
198 | |
199 | uint8 *base = S9xGetBasePointer((d->ABank << 16) + addr); |
200 | if (!base) |
201 | { |
202 | sprintf(String, "SA-1: DMA from non-block address $%02X:%04X" , d->ABank, addr); |
203 | S9xMessage(S9X_WARNING, S9X_DMA_TRACE, String); |
204 | base = Memory.ROM; |
205 | } |
206 | |
207 | base += addr; |
208 | |
209 | uint8 *buffer = &Memory.ROM[CMemory::MAX_ROM_SIZE - 0x10000]; |
210 | uint8 *p = buffer; |
211 | uint32 inc_sa1 = char_line_bytes - (d->AAddress % char_line_bytes); |
212 | uint32 char_count = inc_sa1 / bytes_per_char; |
213 | |
214 | in_sa1_dma = TRUE; |
215 | |
216 | #if 0 |
217 | printf("SA-1 DMA: %08x," , base); |
218 | printf("depth = %d, count = %d, bytes_per_char = %d, bytes_per_line = %d, num_chars = %d, char_line_bytes = %d\n" , |
219 | depth, count, bytes_per_char, bytes_per_line, num_chars, char_line_bytes); |
220 | #endif |
221 | |
222 | switch (depth) |
223 | { |
224 | case 2: |
225 | for (int32 i = 0; i < count; i += inc_sa1, base += char_line_bytes, inc_sa1 = char_line_bytes, char_count = num_chars) |
226 | { |
227 | uint8 *line = base + (num_chars - char_count) * 2; |
228 | for (uint32 j = 0; j < char_count && p - buffer < count; j++, line += 2) |
229 | { |
230 | uint8 *q = line; |
231 | for (int32 l = 0; l < 8; l++, q += bytes_per_line) |
232 | { |
233 | for (int32 b = 0; b < 2; b++) |
234 | { |
235 | uint8 r = *(q + b); |
236 | *(p + 0) = (*(p + 0) << 1) | ((r >> 0) & 1); |
237 | *(p + 1) = (*(p + 1) << 1) | ((r >> 1) & 1); |
238 | *(p + 0) = (*(p + 0) << 1) | ((r >> 2) & 1); |
239 | *(p + 1) = (*(p + 1) << 1) | ((r >> 3) & 1); |
240 | *(p + 0) = (*(p + 0) << 1) | ((r >> 4) & 1); |
241 | *(p + 1) = (*(p + 1) << 1) | ((r >> 5) & 1); |
242 | *(p + 0) = (*(p + 0) << 1) | ((r >> 6) & 1); |
243 | *(p + 1) = (*(p + 1) << 1) | ((r >> 7) & 1); |
244 | } |
245 | |
246 | p += 2; |
247 | } |
248 | } |
249 | } |
250 | |
251 | break; |
252 | |
253 | case 4: |
254 | for (int32 i = 0; i < count; i += inc_sa1, base += char_line_bytes, inc_sa1 = char_line_bytes, char_count = num_chars) |
255 | { |
256 | uint8 *line = base + (num_chars - char_count) * 4; |
257 | for (uint32 j = 0; j < char_count && p - buffer < count; j++, line += 4) |
258 | { |
259 | uint8 *q = line; |
260 | for (int32 l = 0; l < 8; l++, q += bytes_per_line) |
261 | { |
262 | for (int32 b = 0; b < 4; b++) |
263 | { |
264 | uint8 r = *(q + b); |
265 | *(p + 0) = (*(p + 0) << 1) | ((r >> 0) & 1); |
266 | *(p + 1) = (*(p + 1) << 1) | ((r >> 1) & 1); |
267 | *(p + 16) = (*(p + 16) << 1) | ((r >> 2) & 1); |
268 | *(p + 17) = (*(p + 17) << 1) | ((r >> 3) & 1); |
269 | *(p + 0) = (*(p + 0) << 1) | ((r >> 4) & 1); |
270 | *(p + 1) = (*(p + 1) << 1) | ((r >> 5) & 1); |
271 | *(p + 16) = (*(p + 16) << 1) | ((r >> 6) & 1); |
272 | *(p + 17) = (*(p + 17) << 1) | ((r >> 7) & 1); |
273 | } |
274 | |
275 | p += 2; |
276 | } |
277 | |
278 | p += 32 - 16; |
279 | } |
280 | } |
281 | |
282 | break; |
283 | |
284 | case 8: |
285 | for (int32 i = 0; i < count; i += inc_sa1, base += char_line_bytes, inc_sa1 = char_line_bytes, char_count = num_chars) |
286 | { |
287 | uint8 *line = base + (num_chars - char_count) * 8; |
288 | for (uint32 j = 0; j < char_count && p - buffer < count; j++, line += 8) |
289 | { |
290 | uint8 *q = line; |
291 | for (int32 l = 0; l < 8; l++, q += bytes_per_line) |
292 | { |
293 | for (int32 b = 0; b < 8; b++) |
294 | { |
295 | uint8 r = *(q + b); |
296 | *(p + 0) = (*(p + 0) << 1) | ((r >> 0) & 1); |
297 | *(p + 1) = (*(p + 1) << 1) | ((r >> 1) & 1); |
298 | *(p + 16) = (*(p + 16) << 1) | ((r >> 2) & 1); |
299 | *(p + 17) = (*(p + 17) << 1) | ((r >> 3) & 1); |
300 | *(p + 32) = (*(p + 32) << 1) | ((r >> 4) & 1); |
301 | *(p + 33) = (*(p + 33) << 1) | ((r >> 5) & 1); |
302 | *(p + 48) = (*(p + 48) << 1) | ((r >> 6) & 1); |
303 | *(p + 49) = (*(p + 49) << 1) | ((r >> 7) & 1); |
304 | } |
305 | |
306 | p += 2; |
307 | } |
308 | |
309 | p += 64 - 16; |
310 | } |
311 | } |
312 | |
313 | break; |
314 | } |
315 | } |
316 | } |
317 | |
318 | #ifdef DEBUGGER |
319 | if (Settings.TraceDMA) |
320 | { |
321 | sprintf(String, "DMA[%d]: %s Mode:%d 0x%02X%04X->0x21%02X Bytes:%d (%s) V:%03d" , |
322 | Channel, d->ReverseTransfer ? "PPU->CPU" : "CPU->PPU" , d->TransferMode, d->ABank, d->AAddress, d->BAddress, |
323 | d->TransferBytes, d->AAddressFixed ? "fixed" : (d->AAddressDecrement ? "dec" : "inc" ), CPU.V_Counter); |
324 | |
325 | if (d->BAddress == 0x18 || d->BAddress == 0x19 || d->BAddress == 0x39 || d->BAddress == 0x3a) |
326 | sprintf(String, "%s VRAM: %04X (%d,%d) %s" , String, |
327 | PPU.VMA.Address, PPU.VMA.Increment, PPU.VMA.FullGraphicCount, PPU.VMA.High ? "word" : "byte" ); |
328 | else |
329 | if (d->BAddress == 0x22 || d->BAddress == 0x3b) |
330 | sprintf(String, "%s CGRAM: %02X (%x)" , String, PPU.CGADD, PPU.CGFLIP); |
331 | else |
332 | if (d->BAddress == 0x04 || d->BAddress == 0x38) |
333 | sprintf(String, "%s OBJADDR: %04X" , String, PPU.OAMAddr); |
334 | |
335 | S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String); |
336 | } |
337 | #endif |
338 | |
339 | // Do Transfer |
340 | |
341 | uint8 Work; |
342 | |
343 | // 8 cycles per channel |
344 | ADD_CYCLES(SLOW_ONE_CYCLE); |
345 | |
346 | if (!d->ReverseTransfer) |
347 | { |
348 | // CPU -> PPU |
349 | int32 b = 0; |
350 | uint16 p = d->AAddress; |
351 | uint8 *base = S9xGetBasePointer((d->ABank << 16) + d->AAddress); |
352 | bool8 inWRAM_DMA; |
353 | |
354 | int32 rem = count; |
355 | // Transfer per block if d->AAdressFixed is FALSE |
356 | count = d->AAddressFixed ? rem : (d->AAddressDecrement ? ((p & MEMMAP_MASK) + 1) : (MEMMAP_BLOCK_SIZE - (p & MEMMAP_MASK))); |
357 | |
358 | // Settings for custom chip DMA |
359 | if (in_sa1_dma) |
360 | { |
361 | base = &Memory.ROM[CMemory::MAX_ROM_SIZE - 0x10000]; |
362 | p = 0; |
363 | count = rem; |
364 | } |
365 | else |
366 | if (in_sdd1_dma) |
367 | { |
368 | base = in_sdd1_dma; |
369 | p = 0; |
370 | count = rem; |
371 | } |
372 | else |
373 | if (spc7110_dma) |
374 | { |
375 | base = spc7110_dma; |
376 | p = 0; |
377 | count = rem; |
378 | } |
379 | |
380 | inWRAM_DMA = ((!in_sa1_dma && !in_sdd1_dma && !spc7110_dma) && |
381 | (d->ABank == 0x7e || d->ABank == 0x7f || (!(d->ABank & 0x40) && d->AAddress < 0x2000))); |
382 | |
383 | // 8 cycles per byte |
384 | #define UPDATE_COUNTERS \ |
385 | d->TransferBytes--; \ |
386 | d->AAddress += inc; \ |
387 | p += inc; \ |
388 | if (!addCyclesInDMA(Channel)) \ |
389 | { \ |
390 | CPU.InDMA = FALSE; \ |
391 | CPU.InDMAorHDMA = FALSE; \ |
392 | CPU.InWRAMDMAorHDMA = FALSE; \ |
393 | CPU.CurrentDMAorHDMAChannel = -1; \ |
394 | return (FALSE); \ |
395 | } |
396 | |
397 | while (1) |
398 | { |
399 | if (count > rem) |
400 | count = rem; |
401 | rem -= count; |
402 | |
403 | CPU.InWRAMDMAorHDMA = inWRAM_DMA; |
404 | |
405 | if (!base) |
406 | { |
407 | // DMA SLOW PATH |
408 | if (d->TransferMode == 0 || d->TransferMode == 2 || d->TransferMode == 6) |
409 | { |
410 | do |
411 | { |
412 | Work = S9xGetByte((d->ABank << 16) + p); |
413 | S9xSetPPU(Work, 0x2100 + d->BAddress); |
414 | UPDATE_COUNTERS; |
415 | } while (--count > 0); |
416 | } |
417 | else |
418 | if (d->TransferMode == 1 || d->TransferMode == 5) |
419 | { |
420 | // This is a variation on Duff's Device. It is legal C/C++. |
421 | switch (b) |
422 | { |
423 | default: |
424 | while (count > 1) |
425 | { |
426 | Work = S9xGetByte((d->ABank << 16) + p); |
427 | S9xSetPPU(Work, 0x2100 + d->BAddress); |
428 | UPDATE_COUNTERS; |
429 | count--; |
430 | // Fall through |
431 | case 1: |
432 | Work = S9xGetByte((d->ABank << 16) + p); |
433 | S9xSetPPU(Work, 0x2101 + d->BAddress); |
434 | UPDATE_COUNTERS; |
435 | count--; |
436 | } |
437 | } |
438 | |
439 | if (count == 1) |
440 | { |
441 | Work = S9xGetByte((d->ABank << 16) + p); |
442 | S9xSetPPU(Work, 0x2100 + d->BAddress); |
443 | UPDATE_COUNTERS; |
444 | b = 1; |
445 | } |
446 | else |
447 | b = 0; |
448 | } |
449 | else |
450 | if (d->TransferMode == 3 || d->TransferMode == 7) |
451 | { |
452 | switch (b) |
453 | { |
454 | default: |
455 | do |
456 | { |
457 | Work = S9xGetByte((d->ABank << 16) + p); |
458 | S9xSetPPU(Work, 0x2100 + d->BAddress); |
459 | UPDATE_COUNTERS; |
460 | if (--count <= 0) |
461 | { |
462 | b = 1; |
463 | break; |
464 | } |
465 | // Fall through |
466 | case 1: |
467 | Work = S9xGetByte((d->ABank << 16) + p); |
468 | S9xSetPPU(Work, 0x2100 + d->BAddress); |
469 | UPDATE_COUNTERS; |
470 | if (--count <= 0) |
471 | { |
472 | b = 2; |
473 | break; |
474 | } |
475 | // Fall through |
476 | case 2: |
477 | Work = S9xGetByte((d->ABank << 16) + p); |
478 | S9xSetPPU(Work, 0x2101 + d->BAddress); |
479 | UPDATE_COUNTERS; |
480 | if (--count <= 0) |
481 | { |
482 | b = 3; |
483 | break; |
484 | } |
485 | // Fall through |
486 | case 3: |
487 | Work = S9xGetByte((d->ABank << 16) + p); |
488 | S9xSetPPU(Work, 0x2101 + d->BAddress); |
489 | UPDATE_COUNTERS; |
490 | if (--count <= 0) |
491 | { |
492 | b = 0; |
493 | break; |
494 | } |
495 | } while (1); |
496 | } |
497 | } |
498 | else |
499 | if (d->TransferMode == 4) |
500 | { |
501 | switch (b) |
502 | { |
503 | default: |
504 | do |
505 | { |
506 | Work = S9xGetByte((d->ABank << 16) + p); |
507 | S9xSetPPU(Work, 0x2100 + d->BAddress); |
508 | UPDATE_COUNTERS; |
509 | if (--count <= 0) |
510 | { |
511 | b = 1; |
512 | break; |
513 | } |
514 | // Fall through |
515 | case 1: |
516 | Work = S9xGetByte((d->ABank << 16) + p); |
517 | S9xSetPPU(Work, 0x2101 + d->BAddress); |
518 | UPDATE_COUNTERS; |
519 | if (--count <= 0) |
520 | { |
521 | b = 2; |
522 | break; |
523 | } |
524 | // Fall through |
525 | case 2: |
526 | Work = S9xGetByte((d->ABank << 16) + p); |
527 | S9xSetPPU(Work, 0x2102 + d->BAddress); |
528 | UPDATE_COUNTERS; |
529 | if (--count <= 0) |
530 | { |
531 | b = 3; |
532 | break; |
533 | } |
534 | // Fall through |
535 | case 3: |
536 | Work = S9xGetByte((d->ABank << 16) + p); |
537 | S9xSetPPU(Work, 0x2103 + d->BAddress); |
538 | UPDATE_COUNTERS; |
539 | if (--count <= 0) |
540 | { |
541 | b = 0; |
542 | break; |
543 | } |
544 | } while (1); |
545 | } |
546 | } |
547 | #ifdef DEBUGGER |
548 | else |
549 | { |
550 | sprintf(String, "Unknown DMA transfer mode: %d on channel %d\n" , d->TransferMode, Channel); |
551 | S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String); |
552 | } |
553 | #endif |
554 | } |
555 | else |
556 | { |
557 | // DMA FAST PATH |
558 | if (d->TransferMode == 0 || d->TransferMode == 2 || d->TransferMode == 6) |
559 | { |
560 | switch (d->BAddress) |
561 | { |
562 | case 0x04: // OAMDATA |
563 | do |
564 | { |
565 | Work = *(base + p); |
566 | REGISTER_2104(Work); |
567 | UPDATE_COUNTERS; |
568 | } while (--count > 0); |
569 | |
570 | break; |
571 | |
572 | case 0x18: // VMDATAL |
573 | if (!PPU.VMA.FullGraphicCount) |
574 | { |
575 | do |
576 | { |
577 | Work = *(base + p); |
578 | REGISTER_2118_linear(Work); |
579 | UPDATE_COUNTERS; |
580 | } while (--count > 0); |
581 | } |
582 | else |
583 | { |
584 | do |
585 | { |
586 | Work = *(base + p); |
587 | REGISTER_2118_tile(Work); |
588 | UPDATE_COUNTERS; |
589 | } while (--count > 0); |
590 | } |
591 | |
592 | break; |
593 | |
594 | case 0x19: // VMDATAH |
595 | if (!PPU.VMA.FullGraphicCount) |
596 | { |
597 | do |
598 | { |
599 | Work = *(base + p); |
600 | REGISTER_2119_linear(Work); |
601 | UPDATE_COUNTERS; |
602 | } while (--count > 0); |
603 | } |
604 | else |
605 | { |
606 | do |
607 | { |
608 | Work = *(base + p); |
609 | REGISTER_2119_tile(Work); |
610 | UPDATE_COUNTERS; |
611 | } while (--count > 0); |
612 | } |
613 | |
614 | break; |
615 | |
616 | case 0x22: // CGDATA |
617 | do |
618 | { |
619 | Work = *(base + p); |
620 | REGISTER_2122(Work); |
621 | UPDATE_COUNTERS; |
622 | } while (--count > 0); |
623 | |
624 | break; |
625 | |
626 | case 0x80: // WMDATA |
627 | if (!CPU.InWRAMDMAorHDMA) |
628 | { |
629 | do |
630 | { |
631 | Work = *(base + p); |
632 | REGISTER_2180(Work); |
633 | UPDATE_COUNTERS; |
634 | } while (--count > 0); |
635 | } |
636 | else |
637 | { |
638 | do |
639 | { |
640 | UPDATE_COUNTERS; |
641 | } while (--count > 0); |
642 | } |
643 | |
644 | break; |
645 | |
646 | default: |
647 | do |
648 | { |
649 | Work = *(base + p); |
650 | S9xSetPPU(Work, 0x2100 + d->BAddress); |
651 | UPDATE_COUNTERS; |
652 | } while (--count > 0); |
653 | |
654 | break; |
655 | } |
656 | } |
657 | else |
658 | if (d->TransferMode == 1 || d->TransferMode == 5) |
659 | { |
660 | if (d->BAddress == 0x18) |
661 | { |
662 | // VMDATAL |
663 | if (!PPU.VMA.FullGraphicCount) |
664 | { |
665 | switch (b) |
666 | { |
667 | default: |
668 | while (count > 1) |
669 | { |
670 | Work = *(base + p); |
671 | REGISTER_2118_linear(Work); |
672 | UPDATE_COUNTERS; |
673 | count--; |
674 | // Fall through |
675 | case 1: |
676 | OpenBus = *(base + p); |
677 | REGISTER_2119_linear(OpenBus); |
678 | UPDATE_COUNTERS; |
679 | count--; |
680 | } |
681 | } |
682 | |
683 | if (count == 1) |
684 | { |
685 | Work = *(base + p); |
686 | REGISTER_2118_linear(Work); |
687 | UPDATE_COUNTERS; |
688 | b = 1; |
689 | } |
690 | else |
691 | b = 0; |
692 | } |
693 | else |
694 | { |
695 | switch (b) |
696 | { |
697 | default: |
698 | while (count > 1) |
699 | { |
700 | Work = *(base + p); |
701 | REGISTER_2118_tile(Work); |
702 | UPDATE_COUNTERS; |
703 | count--; |
704 | // Fall through |
705 | case 1: |
706 | Work = *(base + p); |
707 | REGISTER_2119_tile(Work); |
708 | UPDATE_COUNTERS; |
709 | count--; |
710 | } |
711 | } |
712 | |
713 | if (count == 1) |
714 | { |
715 | Work = *(base + p); |
716 | REGISTER_2118_tile(Work); |
717 | UPDATE_COUNTERS; |
718 | b = 1; |
719 | } |
720 | else |
721 | b = 0; |
722 | } |
723 | } |
724 | else |
725 | { |
726 | // DMA mode 1 general case |
727 | switch (b) |
728 | { |
729 | default: |
730 | while (count > 1) |
731 | { |
732 | Work = *(base + p); |
733 | S9xSetPPU(Work, 0x2100 + d->BAddress); |
734 | UPDATE_COUNTERS; |
735 | count--; |
736 | // Fall through |
737 | case 1: |
738 | Work = *(base + p); |
739 | S9xSetPPU(Work, 0x2101 + d->BAddress); |
740 | UPDATE_COUNTERS; |
741 | count--; |
742 | } |
743 | } |
744 | |
745 | if (count == 1) |
746 | { |
747 | Work = *(base + p); |
748 | S9xSetPPU(Work, 0x2100 + d->BAddress); |
749 | UPDATE_COUNTERS; |
750 | b = 1; |
751 | } |
752 | else |
753 | b = 0; |
754 | } |
755 | } |
756 | else |
757 | if (d->TransferMode == 3 || d->TransferMode == 7) |
758 | { |
759 | switch (b) |
760 | { |
761 | default: |
762 | do |
763 | { |
764 | Work = *(base + p); |
765 | S9xSetPPU(Work, 0x2100 + d->BAddress); |
766 | UPDATE_COUNTERS; |
767 | if (--count <= 0) |
768 | { |
769 | b = 1; |
770 | break; |
771 | } |
772 | // Fall through |
773 | case 1: |
774 | Work = *(base + p); |
775 | S9xSetPPU(Work, 0x2100 + d->BAddress); |
776 | UPDATE_COUNTERS; |
777 | if (--count <= 0) |
778 | { |
779 | b = 2; |
780 | break; |
781 | } |
782 | // Fall through |
783 | case 2: |
784 | Work = *(base + p); |
785 | S9xSetPPU(Work, 0x2101 + d->BAddress); |
786 | UPDATE_COUNTERS; |
787 | if (--count <= 0) |
788 | { |
789 | b = 3; |
790 | break; |
791 | } |
792 | // Fall through |
793 | case 3: |
794 | Work = *(base + p); |
795 | S9xSetPPU(Work, 0x2101 + d->BAddress); |
796 | UPDATE_COUNTERS; |
797 | if (--count <= 0) |
798 | { |
799 | b = 0; |
800 | break; |
801 | } |
802 | } while (1); |
803 | } |
804 | } |
805 | else |
806 | if (d->TransferMode == 4) |
807 | { |
808 | switch (b) |
809 | { |
810 | default: |
811 | do |
812 | { |
813 | Work = *(base + p); |
814 | S9xSetPPU(Work, 0x2100 + d->BAddress); |
815 | UPDATE_COUNTERS; |
816 | if (--count <= 0) |
817 | { |
818 | b = 1; |
819 | break; |
820 | } |
821 | // Fall through |
822 | case 1: |
823 | Work = *(base + p); |
824 | S9xSetPPU(Work, 0x2101 + d->BAddress); |
825 | UPDATE_COUNTERS; |
826 | if (--count <= 0) |
827 | { |
828 | b = 2; |
829 | break; |
830 | } |
831 | // Fall through |
832 | case 2: |
833 | Work = *(base + p); |
834 | S9xSetPPU(Work, 0x2102 + d->BAddress); |
835 | UPDATE_COUNTERS; |
836 | if (--count <= 0) |
837 | { |
838 | b = 3; |
839 | break; |
840 | } |
841 | // Fall through |
842 | case 3: |
843 | Work = *(base + p); |
844 | S9xSetPPU(Work, 0x2103 + d->BAddress); |
845 | UPDATE_COUNTERS; |
846 | if (--count <= 0) |
847 | { |
848 | b = 0; |
849 | break; |
850 | } |
851 | } while (1); |
852 | } |
853 | } |
854 | #ifdef DEBUGGER |
855 | else |
856 | { |
857 | sprintf(String, "Unknown DMA transfer mode: %d on channel %d\n" , d->TransferMode, Channel); |
858 | S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String); |
859 | } |
860 | #endif |
861 | } |
862 | |
863 | if (rem <= 0) |
864 | break; |
865 | |
866 | base = S9xGetBasePointer((d->ABank << 16) + d->AAddress); |
867 | count = MEMMAP_BLOCK_SIZE; |
868 | inWRAM_DMA = ((!in_sa1_dma && !in_sdd1_dma && !spc7110_dma) && |
869 | (d->ABank == 0x7e || d->ABank == 0x7f || (!(d->ABank & 0x40) && d->AAddress < 0x2000))); |
870 | } |
871 | |
872 | #undef UPDATE_COUNTERS |
873 | } |
874 | else |
875 | { |
876 | // PPU -> CPU |
877 | |
878 | // 8 cycles per byte |
879 | #define UPDATE_COUNTERS \ |
880 | d->TransferBytes--; \ |
881 | d->AAddress += inc; \ |
882 | if (!addCyclesInDMA(Channel)) \ |
883 | { \ |
884 | CPU.InDMA = FALSE; \ |
885 | CPU.InDMAorHDMA = FALSE; \ |
886 | CPU.InWRAMDMAorHDMA = FALSE; \ |
887 | CPU.CurrentDMAorHDMAChannel = -1; \ |
888 | return (FALSE); \ |
889 | } |
890 | |
891 | if (d->BAddress > 0x80 - 4 && d->BAddress <= 0x83 && !(d->ABank & 0x40)) |
892 | { |
893 | // REVERSE-DMA REALLY-SLOW PATH |
894 | do |
895 | { |
896 | switch (d->TransferMode) |
897 | { |
898 | case 0: |
899 | case 2: |
900 | case 6: |
901 | CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000); |
902 | Work = S9xGetPPU(0x2100 + d->BAddress); |
903 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
904 | UPDATE_COUNTERS; |
905 | count--; |
906 | |
907 | break; |
908 | |
909 | case 1: |
910 | case 5: |
911 | CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000); |
912 | Work = S9xGetPPU(0x2100 + d->BAddress); |
913 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
914 | UPDATE_COUNTERS; |
915 | if (!--count) |
916 | break; |
917 | |
918 | CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000); |
919 | Work = S9xGetPPU(0x2101 + d->BAddress); |
920 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
921 | UPDATE_COUNTERS; |
922 | count--; |
923 | |
924 | break; |
925 | |
926 | case 3: |
927 | case 7: |
928 | CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000); |
929 | Work = S9xGetPPU(0x2100 + d->BAddress); |
930 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
931 | UPDATE_COUNTERS; |
932 | if (!--count) |
933 | break; |
934 | |
935 | CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000); |
936 | Work = S9xGetPPU(0x2100 + d->BAddress); |
937 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
938 | UPDATE_COUNTERS; |
939 | if (!--count) |
940 | break; |
941 | |
942 | CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000); |
943 | Work = S9xGetPPU(0x2101 + d->BAddress); |
944 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
945 | UPDATE_COUNTERS; |
946 | if (!--count) |
947 | break; |
948 | |
949 | CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000); |
950 | Work = S9xGetPPU(0x2101 + d->BAddress); |
951 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
952 | UPDATE_COUNTERS; |
953 | count--; |
954 | |
955 | break; |
956 | |
957 | case 4: |
958 | CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000); |
959 | Work = S9xGetPPU(0x2100 + d->BAddress); |
960 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
961 | UPDATE_COUNTERS; |
962 | if (!--count) |
963 | break; |
964 | |
965 | CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000); |
966 | Work = S9xGetPPU(0x2101 + d->BAddress); |
967 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
968 | UPDATE_COUNTERS; |
969 | if (!--count) |
970 | break; |
971 | |
972 | CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000); |
973 | Work = S9xGetPPU(0x2102 + d->BAddress); |
974 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
975 | UPDATE_COUNTERS; |
976 | if (!--count) |
977 | break; |
978 | |
979 | CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000); |
980 | Work = S9xGetPPU(0x2103 + d->BAddress); |
981 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
982 | UPDATE_COUNTERS; |
983 | count--; |
984 | |
985 | break; |
986 | |
987 | default: |
988 | #ifdef DEBUGGER |
989 | sprintf(String, "Unknown DMA transfer mode: %d on channel %d\n" , d->TransferMode, Channel); |
990 | S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String); |
991 | #endif |
992 | while (count) |
993 | { |
994 | UPDATE_COUNTERS; |
995 | count--; |
996 | } |
997 | |
998 | break; |
999 | } |
1000 | } while (count); |
1001 | } |
1002 | else |
1003 | { |
1004 | // REVERSE-DMA FASTER PATH |
1005 | CPU.InWRAMDMAorHDMA = (d->ABank == 0x7e || d->ABank == 0x7f); |
1006 | do |
1007 | { |
1008 | switch (d->TransferMode) |
1009 | { |
1010 | case 0: |
1011 | case 2: |
1012 | case 6: |
1013 | Work = S9xGetPPU(0x2100 + d->BAddress); |
1014 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
1015 | UPDATE_COUNTERS; |
1016 | count--; |
1017 | |
1018 | break; |
1019 | |
1020 | case 1: |
1021 | case 5: |
1022 | Work = S9xGetPPU(0x2100 + d->BAddress); |
1023 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
1024 | UPDATE_COUNTERS; |
1025 | if (!--count) |
1026 | break; |
1027 | |
1028 | Work = S9xGetPPU(0x2101 + d->BAddress); |
1029 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
1030 | UPDATE_COUNTERS; |
1031 | count--; |
1032 | |
1033 | break; |
1034 | |
1035 | case 3: |
1036 | case 7: |
1037 | Work = S9xGetPPU(0x2100 + d->BAddress); |
1038 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
1039 | UPDATE_COUNTERS; |
1040 | if (!--count) |
1041 | break; |
1042 | |
1043 | Work = S9xGetPPU(0x2100 + d->BAddress); |
1044 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
1045 | UPDATE_COUNTERS; |
1046 | if (!--count) |
1047 | break; |
1048 | |
1049 | Work = S9xGetPPU(0x2101 + d->BAddress); |
1050 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
1051 | UPDATE_COUNTERS; |
1052 | if (!--count) |
1053 | break; |
1054 | |
1055 | Work = S9xGetPPU(0x2101 + d->BAddress); |
1056 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
1057 | UPDATE_COUNTERS; |
1058 | count--; |
1059 | |
1060 | break; |
1061 | |
1062 | case 4: |
1063 | Work = S9xGetPPU(0x2100 + d->BAddress); |
1064 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
1065 | UPDATE_COUNTERS; |
1066 | if (!--count) |
1067 | break; |
1068 | |
1069 | Work = S9xGetPPU(0x2101 + d->BAddress); |
1070 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
1071 | UPDATE_COUNTERS; |
1072 | if (!--count) |
1073 | break; |
1074 | |
1075 | Work = S9xGetPPU(0x2102 + d->BAddress); |
1076 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
1077 | UPDATE_COUNTERS; |
1078 | if (!--count) |
1079 | break; |
1080 | |
1081 | Work = S9xGetPPU(0x2103 + d->BAddress); |
1082 | S9xSetByte(Work, (d->ABank << 16) + d->AAddress); |
1083 | UPDATE_COUNTERS; |
1084 | count--; |
1085 | |
1086 | break; |
1087 | |
1088 | default: |
1089 | #ifdef DEBUGGER |
1090 | sprintf(String, "Unknown DMA transfer mode: %d on channel %d\n" , d->TransferMode, Channel); |
1091 | S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String); |
1092 | #endif |
1093 | while (count) |
1094 | { |
1095 | UPDATE_COUNTERS; |
1096 | count--; |
1097 | } |
1098 | |
1099 | break; |
1100 | } |
1101 | } while (count); |
1102 | } |
1103 | } |
1104 | |
1105 | if (CPU.NMIPending && (Timings.NMITriggerPos != 0xffff)) |
1106 | { |
1107 | Timings.NMITriggerPos = CPU.Cycles + Timings.NMIDMADelay; |
1108 | } |
1109 | |
1110 | // Release the memory used in SPC7110 DMA |
1111 | if (Settings.SPC7110) |
1112 | { |
1113 | if (spc7110_dma) |
1114 | delete [] spc7110_dma; |
1115 | } |
1116 | |
1117 | #if 0 |
1118 | // sanity check |
1119 | if (d->TransferBytes != 0) |
1120 | fprintf(stderr,"DMA[%d] TransferBytes not 0! $21%02x Reverse:%d %04x\n" , Channel, d->BAddress, d->ReverseTransfer, d->TransferBytes); |
1121 | #endif |
1122 | |
1123 | CPU.InDMA = FALSE; |
1124 | CPU.InDMAorHDMA = FALSE; |
1125 | CPU.InWRAMDMAorHDMA = FALSE; |
1126 | CPU.CurrentDMAorHDMAChannel = -1; |
1127 | |
1128 | return (TRUE); |
1129 | } |
1130 | |
1131 | static inline bool8 HDMAReadLineCount (int d) |
1132 | { |
1133 | // CPU.InDMA is set, so S9xGetXXX() / S9xSetXXX() incur no charges. |
1134 | |
1135 | uint8 line; |
1136 | |
1137 | line = S9xGetByte((DMA[d].ABank << 16) + DMA[d].Address); |
1138 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1139 | |
1140 | if (!line) |
1141 | { |
1142 | DMA[d].Repeat = FALSE; |
1143 | DMA[d].LineCount = 128; |
1144 | |
1145 | if (DMA[d].HDMAIndirectAddressing) |
1146 | { |
1147 | if (PPU.HDMA & (0xfe << d)) |
1148 | { |
1149 | DMA[d].Address++; |
1150 | ADD_CYCLES(SLOW_ONE_CYCLE << 1); |
1151 | } |
1152 | else |
1153 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1154 | |
1155 | DMA[d].IndirectAddress = S9xGetWord((DMA[d].ABank << 16) + DMA[d].Address); |
1156 | DMA[d].Address++; |
1157 | } |
1158 | |
1159 | DMA[d].Address++; |
1160 | HDMAMemPointers[d] = NULL; |
1161 | |
1162 | return (FALSE); |
1163 | } |
1164 | else |
1165 | if (line == 0x80) |
1166 | { |
1167 | DMA[d].Repeat = TRUE; |
1168 | DMA[d].LineCount = 128; |
1169 | } |
1170 | else |
1171 | { |
1172 | DMA[d].Repeat = !(line & 0x80); |
1173 | DMA[d].LineCount = line & 0x7f; |
1174 | } |
1175 | |
1176 | DMA[d].Address++; |
1177 | DMA[d].DoTransfer = TRUE; |
1178 | |
1179 | if (DMA[d].HDMAIndirectAddressing) |
1180 | { |
1181 | ADD_CYCLES(SLOW_ONE_CYCLE << 1); |
1182 | DMA[d].IndirectAddress = S9xGetWord((DMA[d].ABank << 16) + DMA[d].Address); |
1183 | DMA[d].Address += 2; |
1184 | HDMAMemPointers[d] = S9xGetMemPointer((DMA[d].IndirectBank << 16) + DMA[d].IndirectAddress); |
1185 | } |
1186 | else |
1187 | HDMAMemPointers[d] = S9xGetMemPointer((DMA[d].ABank << 16) + DMA[d].Address); |
1188 | |
1189 | return (TRUE); |
1190 | } |
1191 | |
1192 | void S9xStartHDMA (void) |
1193 | { |
1194 | PPU.HDMA = Memory.FillRAM[0x420c]; |
1195 | |
1196 | #ifdef DEBUGGER |
1197 | missing.hdma_this_frame = PPU.HDMA; |
1198 | #endif |
1199 | |
1200 | PPU.HDMAEnded = 0; |
1201 | |
1202 | int32 tmpch; |
1203 | |
1204 | CPU.InHDMA = TRUE; |
1205 | CPU.InDMAorHDMA = TRUE; |
1206 | tmpch = CPU.CurrentDMAorHDMAChannel; |
1207 | |
1208 | // XXX: Not quite right... |
1209 | if (PPU.HDMA != 0) |
1210 | ADD_CYCLES(Timings.DMACPUSync); |
1211 | |
1212 | for (uint8 i = 0; i < 8; i++) |
1213 | { |
1214 | if (PPU.HDMA & (1 << i)) |
1215 | { |
1216 | CPU.CurrentDMAorHDMAChannel = i; |
1217 | |
1218 | DMA[i].Address = DMA[i].AAddress; |
1219 | |
1220 | if (!HDMAReadLineCount(i)) |
1221 | { |
1222 | PPU.HDMA &= ~(1 << i); |
1223 | PPU.HDMAEnded |= (1 << i); |
1224 | } |
1225 | } |
1226 | else |
1227 | DMA[i].DoTransfer = FALSE; |
1228 | } |
1229 | |
1230 | CPU.InHDMA = FALSE; |
1231 | CPU.InDMAorHDMA = CPU.InDMA; |
1232 | CPU.HDMARanInDMA = CPU.InDMA ? PPU.HDMA : 0; |
1233 | CPU.CurrentDMAorHDMAChannel = tmpch; |
1234 | } |
1235 | |
1236 | uint8 S9xDoHDMA (uint8 byte) |
1237 | { |
1238 | struct SDMA *p; |
1239 | |
1240 | uint32 ShiftedIBank; |
1241 | uint16 IAddr; |
1242 | bool8 temp; |
1243 | int32 tmpch; |
1244 | int d; |
1245 | uint8 mask; |
1246 | |
1247 | CPU.InHDMA = TRUE; |
1248 | CPU.InDMAorHDMA = TRUE; |
1249 | CPU.HDMARanInDMA = CPU.InDMA ? byte : 0; |
1250 | temp = CPU.InWRAMDMAorHDMA; |
1251 | tmpch = CPU.CurrentDMAorHDMAChannel; |
1252 | |
1253 | // XXX: Not quite right... |
1254 | ADD_CYCLES(Timings.DMACPUSync); |
1255 | |
1256 | for (mask = 1, p = &DMA[0], d = 0; mask; mask <<= 1, p++, d++) |
1257 | { |
1258 | if (byte & mask) |
1259 | { |
1260 | CPU.InWRAMDMAorHDMA = FALSE; |
1261 | CPU.CurrentDMAorHDMAChannel = d; |
1262 | |
1263 | if (p->HDMAIndirectAddressing) |
1264 | { |
1265 | ShiftedIBank = (p->IndirectBank << 16); |
1266 | IAddr = p->IndirectAddress; |
1267 | } |
1268 | else |
1269 | { |
1270 | ShiftedIBank = (p->ABank << 16); |
1271 | IAddr = p->Address; |
1272 | } |
1273 | |
1274 | if (!HDMAMemPointers[d]) |
1275 | HDMAMemPointers[d] = S9xGetMemPointer(ShiftedIBank + IAddr); |
1276 | |
1277 | if (p->DoTransfer) |
1278 | { |
1279 | // XXX: Hack for Uniracers, because we don't understand |
1280 | // OAM Address Invalidation |
1281 | if (p->BAddress == 0x04) |
1282 | { |
1283 | if (SNESGameFixes.Uniracers) |
1284 | { |
1285 | PPU.OAMAddr = 0x10c; |
1286 | PPU.OAMFlip = 0; |
1287 | } |
1288 | } |
1289 | |
1290 | #ifdef DEBUGGER |
1291 | if (Settings.TraceHDMA && p->DoTransfer) |
1292 | { |
1293 | sprintf(String, "H-DMA[%d] %s (%d) 0x%06X->0x21%02X %s, Count: %3d, Rep: %s, V-LINE: %3ld %02X%04X" , |
1294 | p-DMA, p->ReverseTransfer? "read" : "write" , |
1295 | p->TransferMode, ShiftedIBank+IAddr, p->BAddress, |
1296 | p->HDMAIndirectAddressing ? "ind" : "abs" , |
1297 | p->LineCount, |
1298 | p->Repeat ? "yes" : "no " , (long) CPU.V_Counter, |
1299 | p->ABank, p->Address); |
1300 | S9xMessage(S9X_TRACE, S9X_HDMA_TRACE, String); |
1301 | } |
1302 | #endif |
1303 | |
1304 | if (!p->ReverseTransfer) |
1305 | { |
1306 | if ((IAddr & MEMMAP_MASK) + HDMA_ModeByteCounts[p->TransferMode] >= MEMMAP_BLOCK_SIZE) |
1307 | { |
1308 | // HDMA REALLY-SLOW PATH |
1309 | HDMAMemPointers[d] = NULL; |
1310 | |
1311 | #define DOBYTE(Addr, RegOff) \ |
1312 | CPU.InWRAMDMAorHDMA = (ShiftedIBank == 0x7e0000 || ShiftedIBank == 0x7f0000 || \ |
1313 | (!(ShiftedIBank & 0x400000) && ((uint16) (Addr)) < 0x2000)); \ |
1314 | S9xSetPPU(S9xGetByte(ShiftedIBank + ((uint16) (Addr))), 0x2100 + p->BAddress + (RegOff)); |
1315 | |
1316 | switch (p->TransferMode) |
1317 | { |
1318 | case 0: |
1319 | DOBYTE(IAddr, 0); |
1320 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1321 | break; |
1322 | |
1323 | case 5: |
1324 | DOBYTE(IAddr + 0, 0); |
1325 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1326 | DOBYTE(IAddr + 1, 1); |
1327 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1328 | DOBYTE(IAddr + 2, 0); |
1329 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1330 | DOBYTE(IAddr + 3, 1); |
1331 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1332 | break; |
1333 | |
1334 | case 1: |
1335 | DOBYTE(IAddr + 0, 0); |
1336 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1337 | DOBYTE(IAddr + 1, 1); |
1338 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1339 | break; |
1340 | |
1341 | case 2: |
1342 | case 6: |
1343 | DOBYTE(IAddr + 0, 0); |
1344 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1345 | DOBYTE(IAddr + 1, 0); |
1346 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1347 | break; |
1348 | |
1349 | case 3: |
1350 | case 7: |
1351 | DOBYTE(IAddr + 0, 0); |
1352 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1353 | DOBYTE(IAddr + 1, 0); |
1354 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1355 | DOBYTE(IAddr + 2, 1); |
1356 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1357 | DOBYTE(IAddr + 3, 1); |
1358 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1359 | break; |
1360 | |
1361 | case 4: |
1362 | DOBYTE(IAddr + 0, 0); |
1363 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1364 | DOBYTE(IAddr + 1, 1); |
1365 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1366 | DOBYTE(IAddr + 2, 2); |
1367 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1368 | DOBYTE(IAddr + 3, 3); |
1369 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1370 | break; |
1371 | } |
1372 | |
1373 | #undef DOBYTE |
1374 | } |
1375 | else |
1376 | { |
1377 | CPU.InWRAMDMAorHDMA = (ShiftedIBank == 0x7e0000 || ShiftedIBank == 0x7f0000 || |
1378 | (!(ShiftedIBank & 0x400000) && IAddr < 0x2000)); |
1379 | |
1380 | if (!HDMAMemPointers[d]) |
1381 | { |
1382 | // HDMA SLOW PATH |
1383 | uint32 Addr = ShiftedIBank + IAddr; |
1384 | |
1385 | switch (p->TransferMode) |
1386 | { |
1387 | case 0: |
1388 | S9xSetPPU(S9xGetByte(Addr), 0x2100 + p->BAddress); |
1389 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1390 | break; |
1391 | |
1392 | case 5: |
1393 | S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress); |
1394 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1395 | S9xSetPPU(S9xGetByte(Addr + 1), 0x2101 + p->BAddress); |
1396 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1397 | Addr += 2; |
1398 | /* fall through */ |
1399 | case 1: |
1400 | S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress); |
1401 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1402 | S9xSetPPU(S9xGetByte(Addr + 1), 0x2101 + p->BAddress); |
1403 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1404 | break; |
1405 | |
1406 | case 2: |
1407 | case 6: |
1408 | S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress); |
1409 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1410 | S9xSetPPU(S9xGetByte(Addr + 1), 0x2100 + p->BAddress); |
1411 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1412 | break; |
1413 | |
1414 | case 3: |
1415 | case 7: |
1416 | S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress); |
1417 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1418 | S9xSetPPU(S9xGetByte(Addr + 1), 0x2100 + p->BAddress); |
1419 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1420 | S9xSetPPU(S9xGetByte(Addr + 2), 0x2101 + p->BAddress); |
1421 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1422 | S9xSetPPU(S9xGetByte(Addr + 3), 0x2101 + p->BAddress); |
1423 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1424 | break; |
1425 | |
1426 | case 4: |
1427 | S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress); |
1428 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1429 | S9xSetPPU(S9xGetByte(Addr + 1), 0x2101 + p->BAddress); |
1430 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1431 | S9xSetPPU(S9xGetByte(Addr + 2), 0x2102 + p->BAddress); |
1432 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1433 | S9xSetPPU(S9xGetByte(Addr + 3), 0x2103 + p->BAddress); |
1434 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1435 | break; |
1436 | } |
1437 | } |
1438 | else |
1439 | { |
1440 | // HDMA FAST PATH |
1441 | switch (p->TransferMode) |
1442 | { |
1443 | case 0: |
1444 | S9xSetPPU(*HDMAMemPointers[d]++, 0x2100 + p->BAddress); |
1445 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1446 | break; |
1447 | |
1448 | case 5: |
1449 | S9xSetPPU(*(HDMAMemPointers[d] + 0), 0x2100 + p->BAddress); |
1450 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1451 | S9xSetPPU(*(HDMAMemPointers[d] + 1), 0x2101 + p->BAddress); |
1452 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1453 | HDMAMemPointers[d] += 2; |
1454 | /* fall through */ |
1455 | case 1: |
1456 | S9xSetPPU(*(HDMAMemPointers[d] + 0), 0x2100 + p->BAddress); |
1457 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1458 | // XXX: All HDMA should read to MDR first. This one just |
1459 | // happens to fix Speedy Gonzales. |
1460 | OpenBus = *(HDMAMemPointers[d] + 1); |
1461 | S9xSetPPU(OpenBus, 0x2101 + p->BAddress); |
1462 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1463 | HDMAMemPointers[d] += 2; |
1464 | break; |
1465 | |
1466 | case 2: |
1467 | case 6: |
1468 | S9xSetPPU(*(HDMAMemPointers[d] + 0), 0x2100 + p->BAddress); |
1469 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1470 | S9xSetPPU(*(HDMAMemPointers[d] + 1), 0x2100 + p->BAddress); |
1471 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1472 | HDMAMemPointers[d] += 2; |
1473 | break; |
1474 | |
1475 | case 3: |
1476 | case 7: |
1477 | S9xSetPPU(*(HDMAMemPointers[d] + 0), 0x2100 + p->BAddress); |
1478 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1479 | S9xSetPPU(*(HDMAMemPointers[d] + 1), 0x2100 + p->BAddress); |
1480 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1481 | S9xSetPPU(*(HDMAMemPointers[d] + 2), 0x2101 + p->BAddress); |
1482 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1483 | S9xSetPPU(*(HDMAMemPointers[d] + 3), 0x2101 + p->BAddress); |
1484 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1485 | HDMAMemPointers[d] += 4; |
1486 | break; |
1487 | |
1488 | case 4: |
1489 | S9xSetPPU(*(HDMAMemPointers[d] + 0), 0x2100 + p->BAddress); |
1490 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1491 | S9xSetPPU(*(HDMAMemPointers[d] + 1), 0x2101 + p->BAddress); |
1492 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1493 | S9xSetPPU(*(HDMAMemPointers[d] + 2), 0x2102 + p->BAddress); |
1494 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1495 | S9xSetPPU(*(HDMAMemPointers[d] + 3), 0x2103 + p->BAddress); |
1496 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1497 | HDMAMemPointers[d] += 4; |
1498 | break; |
1499 | } |
1500 | } |
1501 | } |
1502 | } |
1503 | else |
1504 | { |
1505 | // REVERSE HDMA REALLY-SLOW PATH |
1506 | // anomie says: Since this is apparently never used |
1507 | // (otherwise we would have noticed before now), let's not bother with faster paths. |
1508 | HDMAMemPointers[d] = NULL; |
1509 | |
1510 | #define DOBYTE(Addr, RegOff) \ |
1511 | CPU.InWRAMDMAorHDMA = (ShiftedIBank == 0x7e0000 || ShiftedIBank == 0x7f0000 || \ |
1512 | (!(ShiftedIBank & 0x400000) && ((uint16) (Addr)) < 0x2000)); \ |
1513 | S9xSetByte(S9xGetPPU(0x2100 + p->BAddress + (RegOff)), ShiftedIBank + ((uint16) (Addr))); |
1514 | |
1515 | switch (p->TransferMode) |
1516 | { |
1517 | case 0: |
1518 | DOBYTE(IAddr, 0); |
1519 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1520 | break; |
1521 | |
1522 | case 5: |
1523 | DOBYTE(IAddr + 0, 0); |
1524 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1525 | DOBYTE(IAddr + 1, 1); |
1526 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1527 | DOBYTE(IAddr + 2, 0); |
1528 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1529 | DOBYTE(IAddr + 3, 1); |
1530 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1531 | break; |
1532 | |
1533 | case 1: |
1534 | DOBYTE(IAddr + 0, 0); |
1535 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1536 | DOBYTE(IAddr + 1, 1); |
1537 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1538 | break; |
1539 | |
1540 | case 2: |
1541 | case 6: |
1542 | DOBYTE(IAddr + 0, 0); |
1543 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1544 | DOBYTE(IAddr + 1, 0); |
1545 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1546 | break; |
1547 | |
1548 | case 3: |
1549 | case 7: |
1550 | DOBYTE(IAddr + 0, 0); |
1551 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1552 | DOBYTE(IAddr + 1, 0); |
1553 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1554 | DOBYTE(IAddr + 2, 1); |
1555 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1556 | DOBYTE(IAddr + 3, 1); |
1557 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1558 | break; |
1559 | |
1560 | case 4: |
1561 | DOBYTE(IAddr + 0, 0); |
1562 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1563 | DOBYTE(IAddr + 1, 1); |
1564 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1565 | DOBYTE(IAddr + 2, 2); |
1566 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1567 | DOBYTE(IAddr + 3, 3); |
1568 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1569 | break; |
1570 | } |
1571 | |
1572 | #undef DOBYTE |
1573 | } |
1574 | } |
1575 | } |
1576 | } |
1577 | |
1578 | for (mask = 1, p = &DMA[0], d = 0; mask; mask <<= 1, p++, d++) |
1579 | { |
1580 | if (byte & mask) |
1581 | { |
1582 | if (p->DoTransfer) |
1583 | { |
1584 | if (p->HDMAIndirectAddressing) |
1585 | p->IndirectAddress += HDMA_ModeByteCounts[p->TransferMode]; |
1586 | else |
1587 | p->Address += HDMA_ModeByteCounts[p->TransferMode]; |
1588 | } |
1589 | |
1590 | p->DoTransfer = !p->Repeat; |
1591 | |
1592 | if (!--p->LineCount) |
1593 | { |
1594 | if (!HDMAReadLineCount(d)) |
1595 | { |
1596 | byte &= ~mask; |
1597 | PPU.HDMAEnded |= mask; |
1598 | p->DoTransfer = FALSE; |
1599 | } |
1600 | } |
1601 | else |
1602 | ADD_CYCLES(SLOW_ONE_CYCLE); |
1603 | } |
1604 | } |
1605 | |
1606 | CPU.InHDMA = FALSE; |
1607 | CPU.InDMAorHDMA = CPU.InDMA; |
1608 | CPU.InWRAMDMAorHDMA = temp; |
1609 | CPU.CurrentDMAorHDMAChannel = tmpch; |
1610 | |
1611 | return (byte); |
1612 | } |
1613 | |
1614 | void S9xResetDMA (void) |
1615 | { |
1616 | for (int d = 0; d < 8; d++) |
1617 | { |
1618 | DMA[d].ReverseTransfer = TRUE; |
1619 | DMA[d].HDMAIndirectAddressing = TRUE; |
1620 | DMA[d].AAddressFixed = TRUE; |
1621 | DMA[d].AAddressDecrement = TRUE; |
1622 | DMA[d].TransferMode = 7; |
1623 | DMA[d].BAddress = 0xff; |
1624 | DMA[d].AAddress = 0xffff; |
1625 | DMA[d].ABank = 0xff; |
1626 | DMA[d].DMACount_Or_HDMAIndirectAddress = 0xffff; |
1627 | DMA[d].IndirectBank = 0xff; |
1628 | DMA[d].Address = 0xffff; |
1629 | DMA[d].Repeat = FALSE; |
1630 | DMA[d].LineCount = 0x7f; |
1631 | DMA[d].UnknownByte = 0xff; |
1632 | DMA[d].DoTransfer = FALSE; |
1633 | DMA[d].UnusedBit43x0 = 1; |
1634 | } |
1635 | } |
1636 | |