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