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
19extern uint8 *HDMAMemPointers[8];
20extern int HDMA_ModeByteCounts[8];
21extern SPC7110 s7emu;
22
23static uint8 sdd1_decode_buffer[0x10000];
24
25static inline bool8 addCyclesInDMA (uint8);
26static inline bool8 HDMAReadLineCount (int);
27
28
29static 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
52bool8 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
1131static 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
1192void 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
1236uint8 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
1614void 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