gfx.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 "ppu.h" 9 #include "tile.h" 10 #include "controls.h" 11 //#include "font.h" 12 13 #define TILE_PLUS(t, x) (((t) & 0xfc00) | ((t + x) & 0x3ff)) 14 15 static struct SLineData LineData[240]; 16 17 struct SGFX GFX; 18 struct SBG BG; 19 20 21 static void DrawBackground (int bg, uint8 Zh, uint8 Zl) 22 { 23 BG.TileAddress = PPU.BG[bg].NameBase << 1; 24 25 uint32 Tile; 26 uint16 *SC0, *SC1, *SC2, *SC3; 27 28 SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1]; 29 SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0; 30 if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000)) 31 SC1 -= 0x8000; 32 SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0; 33 if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000)) 34 SC2 -= 0x8000; 35 SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2; 36 if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000)) 37 SC3 -= 0x8000; 38 39 uint32 Lines; 40 int OffsetMask = (BG.TileSizeH == 16) ? 0x3ff : 0x1ff; 41 int OffsetShift = (BG.TileSizeV == 16) ? 4 : 3; 42 43 void (*DrawTile) (uint32, uint32, uint32, uint32); 44 void (*DrawClippedTile) (uint32, uint32, uint32, uint32, uint32, uint32); 45 46 for (int clip = 0; clip < GFX.Clip[bg].Count; clip++) 47 { 48 GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1); 49 50 if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2)) 51 { 52 DrawTile = GFX.DrawTileMath; 53 DrawClippedTile = GFX.DrawClippedTileMath; 54 } 55 else 56 { 57 DrawTile = GFX.DrawTileNomath; 58 DrawClippedTile = GFX.DrawClippedTileNomath; 59 } 60 61 for (uint32 Y = GFX.StartY; Y <= GFX.EndY; Y += Lines) 62 { 63 uint32 VOffset = LineData[Y].BG[bg].VOffset; 64 uint32 HOffset = LineData[Y].BG[bg].HOffset; 65 int VirtAlign = ((Y + VOffset) & 7); 66 67 for (Lines = 1; Lines < 8 - VirtAlign; Lines++) 68 { 69 if ((VOffset != LineData[Y + Lines].BG[bg].VOffset) || (HOffset != LineData[Y + Lines].BG[bg].HOffset)) 70 break; 71 } 72 73 if (Y + Lines > GFX.EndY) 74 Lines = GFX.EndY - Y + 1; 75 76 VirtAlign <<= 3; 77 78 uint32 t1, t2; 79 uint32 TilemapRow = (VOffset + Y) >> OffsetShift; 80 81 if ((VOffset + Y) & 8) 82 { 83 t1 = 16; 84 t2 = 0; 85 } 86 else 87 { 88 t1 = 0; 89 t2 = 16; 90 } 91 92 uint16 *b1, *b2; 93 94 if (TilemapRow & 0x20) 95 { 96 b1 = SC2; 97 b2 = SC3; 98 } 99 else 100 { 101 b1 = SC0; 102 b2 = SC1; 103 } 104 105 b1 += (TilemapRow & 0x1f) << 5; 106 b2 += (TilemapRow & 0x1f) << 5; 107 108 uint32 Left = GFX.Clip[bg].Left[clip]; 109 uint32 Right = GFX.Clip[bg].Right[clip]; 110 uint32 Offset = Left + Y * GFX.PPL; 111 uint32 HPos = (HOffset + Left) & OffsetMask; 112 uint32 HTile = HPos >> 3; 113 uint16 *t; 114 115 if (BG.TileSizeH == 8) 116 { 117 if (HTile > 31) 118 t = b2 + (HTile & 0x1f); 119 else 120 t = b1 + HTile; 121 } 122 else 123 { 124 if (HTile > 63) 125 t = b2 + ((HTile >> 1) & 0x1f); 126 else 127 t = b1 + (HTile >> 1); 128 } 129 130 uint32 Width = Right - Left; 131 132 if (HPos & 7) 133 { 134 uint32 l = HPos & 7; 135 uint32 w = 8 - l; 136 if (w > Width) 137 w = Width; 138 139 Offset -= l; 140 Tile = READ_WORD(t); 141 GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl; 142 143 if (BG.TileSizeV == 16) 144 Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1)); 145 146 if (BG.TileSizeH == 8) 147 { 148 DrawClippedTile(Tile, Offset, l, w, VirtAlign, Lines); 149 t++; 150 if (HTile == 31) 151 t = b2; 152 else 153 if (HTile == 63) 154 t = b1; 155 } 156 else 157 { 158 if (!(Tile & H_FLIP)) 159 DrawClippedTile(TILE_PLUS(Tile, (HTile & 1)), Offset, l, w, VirtAlign, Lines); 160 else 161 DrawClippedTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, l, w, VirtAlign, Lines); 162 t += HTile & 1; 163 if (HTile == 63) 164 t = b2; 165 else 166 if (HTile == 127) 167 t = b1; 168 } 169 170 HTile++; 171 Offset += 8; 172 Width -= w; 173 } 174 175 while (Width >= 8) 176 { 177 Tile = READ_WORD(t); 178 GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl; 179 180 if (BG.TileSizeV == 16) 181 Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1)); 182 183 if (BG.TileSizeH == 8) 184 { 185 DrawTile(Tile, Offset, VirtAlign, Lines); 186 t++; 187 if (HTile == 31) 188 t = b2; 189 else 190 if (HTile == 63) 191 t = b1; 192 } 193 else 194 { 195 if (!(Tile & H_FLIP)) 196 DrawTile(TILE_PLUS(Tile, (HTile & 1)), Offset, VirtAlign, Lines); 197 else 198 DrawTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, VirtAlign, Lines); 199 t += HTile & 1; 200 if (HTile == 63) 201 t = b2; 202 else 203 if (HTile == 127) 204 t = b1; 205 } 206 207 HTile++; 208 Offset += 8; 209 Width -= 8; 210 } 211 212 if (Width) 213 { 214 Tile = READ_WORD(t); 215 GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl; 216 217 if (BG.TileSizeV == 16) 218 Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1)); 219 220 if (BG.TileSizeH == 8) 221 DrawClippedTile(Tile, Offset, 0, Width, VirtAlign, Lines); 222 else 223 { 224 if (!(Tile & H_FLIP)) 225 DrawClippedTile(TILE_PLUS(Tile, (HTile & 1)), Offset, 0, Width, VirtAlign, Lines); 226 else 227 DrawClippedTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, 0, Width, VirtAlign, Lines); 228 } 229 } 230 } 231 } 232 } 233 234 static void DrawBackgroundMosaic (int bg, uint8 Zh, uint8 Zl) 235 { 236 BG.TileAddress = PPU.BG[bg].NameBase << 1; 237 238 uint32 Tile; 239 uint16 *SC0, *SC1, *SC2, *SC3; 240 241 SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1]; 242 SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0; 243 if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000)) 244 SC1 -= 0x8000; 245 SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0; 246 if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000)) 247 SC2 -= 0x8000; 248 SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2; 249 if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000)) 250 SC3 -= 0x8000; 251 252 int Lines; 253 int OffsetMask = (BG.TileSizeH == 16) ? 0x3ff : 0x1ff; 254 int OffsetShift = (BG.TileSizeV == 16) ? 4 : 3; 255 256 void (*DrawPix) (uint32, uint32, uint32, uint32, uint32, uint32); 257 258 int MosaicStart = ((uint32) GFX.StartY - PPU.MosaicStart) % PPU.Mosaic; 259 260 for (int clip = 0; clip < GFX.Clip[bg].Count; clip++) 261 { 262 GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1); 263 264 if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2)) 265 DrawPix = GFX.DrawMosaicPixelMath; 266 else 267 DrawPix = GFX.DrawMosaicPixelNomath; 268 269 for (uint32 Y = GFX.StartY - MosaicStart; Y <= GFX.EndY; Y += PPU.Mosaic) 270 { 271 uint32 VOffset = LineData[Y + MosaicStart].BG[bg].VOffset + (0); 272 uint32 HOffset = LineData[Y + MosaicStart].BG[bg].HOffset; 273 274 Lines = PPU.Mosaic - MosaicStart; 275 if (Y + MosaicStart + Lines > GFX.EndY) 276 Lines = GFX.EndY - Y - MosaicStart + 1; 277 278 int VirtAlign = ((Y + VOffset) & 7) << 3; 279 280 uint32 t1, t2; 281 uint32 TilemapRow = (VOffset + Y) >> OffsetShift; 282 283 if ((VOffset + Y) & 8) 284 { 285 t1 = 16; 286 t2 = 0; 287 } 288 else 289 { 290 t1 = 0; 291 t2 = 16; 292 } 293 294 uint16 *b1, *b2; 295 296 if (TilemapRow & 0x20) 297 { 298 b1 = SC2; 299 b2 = SC3; 300 } 301 else 302 { 303 b1 = SC0; 304 b2 = SC1; 305 } 306 307 b1 += (TilemapRow & 0x1f) << 5; 308 b2 += (TilemapRow & 0x1f) << 5; 309 310 uint32 Left = GFX.Clip[bg].Left[clip]; 311 uint32 Right = GFX.Clip[bg].Right[clip]; 312 uint32 Offset = Left + (Y + MosaicStart) * GFX.PPL; 313 uint32 HPos = (HOffset + Left - (Left % PPU.Mosaic)) & OffsetMask; 314 uint32 HTile = HPos >> 3; 315 uint16 *t; 316 317 if (BG.TileSizeH == 8) 318 { 319 if (HTile > 31) 320 t = b2 + (HTile & 0x1f); 321 else 322 t = b1 + HTile; 323 } 324 else 325 { 326 if (HTile > 63) 327 t = b2 + ((HTile >> 1) & 0x1f); 328 else 329 t = b1 + (HTile >> 1); 330 } 331 332 uint32 Width = Right - Left; 333 334 HPos &= 7; 335 336 while (Left < Right) 337 { 338 uint32 w = PPU.Mosaic - (Left % PPU.Mosaic); 339 if (w > Width) 340 w = Width; 341 342 Tile = READ_WORD(t); 343 GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl; 344 345 if (BG.TileSizeV == 16) 346 Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1)); 347 348 if (BG.TileSizeH == 8) 349 DrawPix(Tile, Offset, VirtAlign, HPos & 7, w, Lines); 350 else 351 { 352 if (!(Tile & H_FLIP)) 353 DrawPix(TILE_PLUS(Tile, (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines); 354 else 355 DrawPix(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines); 356 } 357 358 HPos += PPU.Mosaic; 359 360 while (HPos >= 8) 361 { 362 HPos -= 8; 363 364 if (BG.TileSizeH == 8) 365 { 366 t++; 367 if (HTile == 31) 368 t = b2; 369 else 370 if (HTile == 63) 371 t = b1; 372 } 373 else 374 { 375 t += HTile & 1; 376 if (HTile == 63) 377 t = b2; 378 else 379 if (HTile == 127) 380 t = b1; 381 } 382 383 HTile++; 384 } 385 386 Offset += w; 387 Width -= w; 388 Left += w; 389 } 390 391 MosaicStart = 0; 392 } 393 } 394 } 395 396 static void DrawBackgroundOffset (int bg, uint8 Zh, uint8 Zl, int VOffOff) 397 { 398 BG.TileAddress = PPU.BG[bg].NameBase << 1; 399 400 uint32 Tile; 401 uint16 *SC0, *SC1, *SC2, *SC3; 402 uint16 *BPS0, *BPS1, *BPS2, *BPS3; 403 404 BPS0 = (uint16 *) &Memory.VRAM[PPU.BG[2].SCBase << 1]; 405 BPS1 = (PPU.BG[2].SCSize & 1) ? BPS0 + 1024 : BPS0; 406 if (BPS1 >= (uint16 *) (Memory.VRAM + 0x10000)) 407 BPS1 -= 0x8000; 408 BPS2 = (PPU.BG[2].SCSize & 2) ? BPS1 + 1024 : BPS0; 409 if (BPS2 >= (uint16 *) (Memory.VRAM + 0x10000)) 410 BPS2 -= 0x8000; 411 BPS3 = (PPU.BG[2].SCSize & 1) ? BPS2 + 1024 : BPS2; 412 if (BPS3 >= (uint16 *) (Memory.VRAM + 0x10000)) 413 BPS3 -= 0x8000; 414 415 SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1]; 416 SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0; 417 if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000)) 418 SC1 -= 0x8000; 419 SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0; 420 if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000)) 421 SC2 -= 0x8000; 422 SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2; 423 if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000)) 424 SC3 -= 0x8000; 425 426 int OffsetMask = (BG.TileSizeH == 16) ? 0x3ff : 0x1ff; 427 int OffsetShift = (BG.TileSizeV == 16) ? 4 : 3; 428 int Offset2Mask = (BG.OffsetSizeH == 16) ? 0x3ff : 0x1ff; 429 int Offset2Shift = (BG.OffsetSizeV == 16) ? 4 : 3; 430 int OffsetEnableMask = 0x2000 << bg; 431 432 void (*DrawClippedTile) (uint32, uint32, uint32, uint32, uint32, uint32); 433 434 for (int clip = 0; clip < GFX.Clip[bg].Count; clip++) 435 { 436 GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1); 437 438 if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2)) 439 { 440 DrawClippedTile = GFX.DrawClippedTileMath; 441 } 442 else 443 { 444 DrawClippedTile = GFX.DrawClippedTileNomath; 445 } 446 447 for (uint32 Y = GFX.StartY; Y <= GFX.EndY; Y++) 448 { 449 uint32 VOff = LineData[Y].BG[2].VOffset - 1; 450 uint32 HOff = LineData[Y].BG[2].HOffset; 451 uint32 HOffsetRow = VOff >> Offset2Shift; 452 uint32 VOffsetRow = (VOff + VOffOff) >> Offset2Shift; 453 uint16 *s, *s1, *s2; 454 455 if (HOffsetRow & 0x20) 456 { 457 s1 = BPS2; 458 s2 = BPS3; 459 } 460 else 461 { 462 s1 = BPS0; 463 s2 = BPS1; 464 } 465 466 s1 += (HOffsetRow & 0x1f) << 5; 467 s2 += (HOffsetRow & 0x1f) << 5; 468 s = ((VOffsetRow & 0x20) ? BPS2 : BPS0) + ((VOffsetRow & 0x1f) << 5); 469 int32 VOffsetOffset = s - s1; 470 471 uint32 Left = GFX.Clip[bg].Left[clip]; 472 uint32 Right = GFX.Clip[bg].Right[clip]; 473 uint32 Offset = Left + Y * GFX.PPL; 474 uint32 HScroll = LineData[Y].BG[bg].HOffset; 475 uint32 left_edge = (Left < (8 - (HScroll & 7))); 476 uint32 Width = Right - Left; 477 478 while (Left < Right) 479 { 480 uint32 VOffset, HOffset; 481 482 if (left_edge) 483 { 484 // SNES cannot do OPT for leftmost tile column 485 VOffset = LineData[Y].BG[bg].VOffset; 486 HOffset = HScroll; 487 left_edge = FALSE; 488 } 489 else 490 { 491 int HOffTile = ((HOff + Left - 1) & Offset2Mask) >> 3; 492 493 if (BG.OffsetSizeH == 8) 494 { 495 if (HOffTile > 31) 496 s = s2 + (HOffTile & 0x1f); 497 else 498 s = s1 + HOffTile; 499 } 500 else 501 { 502 if (HOffTile > 63) 503 s = s2 + ((HOffTile >> 1) & 0x1f); 504 else 505 s = s1 + (HOffTile >> 1); 506 } 507 508 uint16 HCellOffset = READ_WORD(s); 509 uint16 VCellOffset; 510 511 if (VOffOff) 512 VCellOffset = READ_WORD(s + VOffsetOffset); 513 else 514 { 515 if (HCellOffset & 0x8000) 516 { 517 VCellOffset = HCellOffset; 518 HCellOffset = 0; 519 } 520 else 521 VCellOffset = 0; 522 } 523 524 if (VCellOffset & OffsetEnableMask) 525 VOffset = VCellOffset + 1; 526 else 527 VOffset = LineData[Y].BG[bg].VOffset; 528 529 if (HCellOffset & OffsetEnableMask) 530 HOffset = (HCellOffset & ~7) | (HScroll & 7); 531 else 532 HOffset = HScroll; 533 } 534 535 uint32 t1, t2; 536 int VirtAlign = ((Y + VOffset) & 7) << 3; 537 int TilemapRow = (VOffset + Y) >> OffsetShift; 538 539 if ((VOffset + Y) & 8) 540 { 541 t1 = 16; 542 t2 = 0; 543 } 544 else 545 { 546 t1 = 0; 547 t2 = 16; 548 } 549 550 uint16 *b1, *b2; 551 552 if (TilemapRow & 0x20) 553 { 554 b1 = SC2; 555 b2 = SC3; 556 } 557 else 558 { 559 b1 = SC0; 560 b2 = SC1; 561 } 562 563 b1 += (TilemapRow & 0x1f) << 5; 564 b2 += (TilemapRow & 0x1f) << 5; 565 566 uint32 HPos = (HOffset + Left) & OffsetMask; 567 uint32 HTile = HPos >> 3; 568 uint16 *t; 569 570 if (BG.TileSizeH == 8) 571 { 572 if (HTile > 31) 573 t = b2 + (HTile & 0x1f); 574 else 575 t = b1 + HTile; 576 } 577 else 578 { 579 if (HTile > 63) 580 t = b2 + ((HTile >> 1) & 0x1f); 581 else 582 t = b1 + (HTile >> 1); 583 } 584 585 uint32 l = HPos & 7; 586 uint32 w = 8 - l; 587 if (w > Width) 588 w = Width; 589 590 Offset -= l; 591 Tile = READ_WORD(t); 592 GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl; 593 594 if (BG.TileSizeV == 16) 595 Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1)); 596 597 if (BG.TileSizeH == 8) 598 { 599 DrawClippedTile(Tile, Offset, l, w, VirtAlign, 1); 600 } 601 else 602 { 603 if (!(Tile & H_FLIP)) 604 DrawClippedTile(TILE_PLUS(Tile, (HTile & 1)), Offset, l, w, VirtAlign, 1); 605 else 606 DrawClippedTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, l, w, VirtAlign, 1); 607 } 608 609 Left += w; 610 Offset += 8; 611 Width -= w; 612 } 613 } 614 } 615 } 616 617 static void DrawBackgroundOffsetMosaic (int bg, uint8 Zh, uint8 Zl, int VOffOff) 618 { 619 BG.TileAddress = PPU.BG[bg].NameBase << 1; 620 621 uint32 Tile; 622 uint16 *SC0, *SC1, *SC2, *SC3; 623 uint16 *BPS0, *BPS1, *BPS2, *BPS3; 624 625 BPS0 = (uint16 *) &Memory.VRAM[PPU.BG[2].SCBase << 1]; 626 BPS1 = (PPU.BG[2].SCSize & 1) ? BPS0 + 1024 : BPS0; 627 if (BPS1 >= (uint16 *) (Memory.VRAM + 0x10000)) 628 BPS1 -= 0x8000; 629 BPS2 = (PPU.BG[2].SCSize & 2) ? BPS1 + 1024 : BPS0; 630 if (BPS2 >= (uint16 *) (Memory.VRAM + 0x10000)) 631 BPS2 -= 0x8000; 632 BPS3 = (PPU.BG[2].SCSize & 1) ? BPS2 + 1024 : BPS2; 633 if (BPS3 >= (uint16 *) (Memory.VRAM + 0x10000)) 634 BPS3 -= 0x8000; 635 636 SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1]; 637 SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0; 638 if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000)) 639 SC1 -= 0x8000; 640 SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0; 641 if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000)) 642 SC2 -= 0x8000; 643 SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2; 644 if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000)) 645 SC3 -= 0x8000; 646 647 int Lines; 648 int OffsetMask = (BG.TileSizeH == 16) ? 0x3ff : 0x1ff; 649 int OffsetShift = (BG.TileSizeV == 16) ? 4 : 3; 650 int Offset2Shift = (BG.OffsetSizeV == 16) ? 4 : 3; 651 int OffsetEnableMask = 0x2000 << bg; 652 653 void (*DrawPix) (uint32, uint32, uint32, uint32, uint32, uint32); 654 655 int MosaicStart = ((uint32) GFX.StartY - PPU.MosaicStart) % PPU.Mosaic; 656 657 for (int clip = 0; clip < GFX.Clip[bg].Count; clip++) 658 { 659 GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1); 660 661 if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2)) 662 DrawPix = GFX.DrawMosaicPixelMath; 663 else 664 DrawPix = GFX.DrawMosaicPixelNomath; 665 666 for (uint32 Y = GFX.StartY - MosaicStart; Y <= GFX.EndY; Y += PPU.Mosaic) 667 { 668 uint32 VOff = LineData[Y + MosaicStart].BG[2].VOffset - 1; 669 uint32 HOff = LineData[Y + MosaicStart].BG[2].HOffset; 670 671 Lines = PPU.Mosaic - MosaicStart; 672 if (Y + MosaicStart + Lines > GFX.EndY) 673 Lines = GFX.EndY - Y - MosaicStart + 1; 674 675 uint32 HOffsetRow = VOff >> Offset2Shift; 676 uint32 VOffsetRow = (VOff + VOffOff) >> Offset2Shift; 677 uint16 *s, *s1, *s2; 678 679 if (HOffsetRow & 0x20) 680 { 681 s1 = BPS2; 682 s2 = BPS3; 683 } 684 else 685 { 686 s1 = BPS0; 687 s2 = BPS1; 688 } 689 690 s1 += (HOffsetRow & 0x1f) << 5; 691 s2 += (HOffsetRow & 0x1f) << 5; 692 s = ((VOffsetRow & 0x20) ? BPS2 : BPS0) + ((VOffsetRow & 0x1f) << 5); 693 int32 VOffsetOffset = s - s1; 694 695 uint32 Left = GFX.Clip[bg].Left[clip]; 696 uint32 Right = GFX.Clip[bg].Right[clip]; 697 uint32 Offset = Left + (Y + MosaicStart) * GFX.PPL; 698 uint32 HScroll = LineData[Y + MosaicStart].BG[bg].HOffset; 699 uint32 Width = Right - Left; 700 701 while (Left < Right) 702 { 703 uint32 VOffset, HOffset; 704 705 if (Left < (8 - (HScroll & 7))) 706 { 707 // SNES cannot do OPT for leftmost tile column 708 VOffset = LineData[Y + MosaicStart].BG[bg].VOffset; 709 HOffset = HScroll; 710 } 711 else 712 { 713 int HOffTile = (((Left + (HScroll & 7)) - 8) + (HOff & ~7)) >> 3; 714 715 if (BG.OffsetSizeH == 8) 716 { 717 if (HOffTile > 31) 718 s = s2 + (HOffTile & 0x1f); 719 else 720 s = s1 + HOffTile; 721 } 722 else 723 { 724 if (HOffTile > 63) 725 s = s2 + ((HOffTile >> 1) & 0x1f); 726 else 727 s = s1 + (HOffTile >> 1); 728 } 729 730 uint16 HCellOffset = READ_WORD(s); 731 uint16 VCellOffset; 732 733 if (VOffOff) 734 VCellOffset = READ_WORD(s + VOffsetOffset); 735 else 736 { 737 if (HCellOffset & 0x8000) 738 { 739 VCellOffset = HCellOffset; 740 HCellOffset = 0; 741 } 742 else 743 VCellOffset = 0; 744 } 745 746 if (VCellOffset & OffsetEnableMask) 747 VOffset = VCellOffset + 1; 748 else 749 VOffset = LineData[Y + MosaicStart].BG[bg].VOffset; 750 751 if (HCellOffset & OffsetEnableMask) 752 HOffset = (HCellOffset & ~7) | (HScroll & 7); 753 else 754 HOffset = HScroll; 755 } 756 757 uint32 t1, t2; 758 int VirtAlign = (((Y + VOffset) & 7) >> (0)) << 3; 759 int TilemapRow = (VOffset + Y) >> OffsetShift; 760 761 if ((VOffset + Y) & 8) 762 { 763 t1 = 16; 764 t2 = 0; 765 } 766 else 767 { 768 t1 = 0; 769 t2 = 16; 770 } 771 772 uint16 *b1, *b2; 773 774 if (TilemapRow & 0x20) 775 { 776 b1 = SC2; 777 b2 = SC3; 778 } 779 else 780 { 781 b1 = SC0; 782 b2 = SC1; 783 } 784 785 b1 += (TilemapRow & 0x1f) << 5; 786 b2 += (TilemapRow & 0x1f) << 5; 787 788 uint32 HPos = (HOffset + Left - (Left % PPU.Mosaic)) & OffsetMask; 789 uint32 HTile = HPos >> 3; 790 uint16 *t; 791 792 if (BG.TileSizeH == 8) 793 { 794 if (HTile > 31) 795 t = b2 + (HTile & 0x1f); 796 else 797 t = b1 + HTile; 798 } 799 else 800 { 801 if (HTile > 63) 802 t = b2 + ((HTile >> 1) & 0x1f); 803 else 804 t = b1 + (HTile >> 1); 805 } 806 807 uint32 w = PPU.Mosaic - (Left % PPU.Mosaic); 808 if (w > Width) 809 w = Width; 810 811 Tile = READ_WORD(t); 812 GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl; 813 814 if (BG.TileSizeV == 16) 815 Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1)); 816 817 if (BG.TileSizeH == 8) 818 DrawPix(Tile, Offset, VirtAlign, HPos & 7, w, Lines); 819 else 820 { 821 if (!(Tile & H_FLIP)) 822 DrawPix(TILE_PLUS(Tile, (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines); 823 else 824 if (!(Tile & V_FLIP)) 825 DrawPix(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines); 826 } 827 828 Left += w; 829 Offset += w; 830 Width -= w; 831 } 832 833 MosaicStart = 0; 834 } 835 } 836 } 837 838 static inline void DrawBackgroundMode7 (int bg, void (*DrawMath) (uint32, uint32, int), void (*DrawNomath) (uint32, uint32, int), int D) 839 { 840 for (int clip = 0; clip < GFX.Clip[bg].Count; clip++) 841 { 842 GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1); 843 844 if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2)) 845 DrawMath(GFX.Clip[bg].Left[clip], GFX.Clip[bg].Right[clip], D); 846 else 847 DrawNomath(GFX.Clip[bg].Left[clip], GFX.Clip[bg].Right[clip], D); 848 } 849 } 850 851 static inline void DrawBackdrop (void) 852 { 853 uint32 Offset = GFX.StartY * GFX.PPL; 854 855 for (int clip = 0; clip < GFX.Clip[5].Count; clip++) 856 { 857 GFX.ClipColors = !(GFX.Clip[5].DrawMode[clip] & 1); 858 859 if (BG.EnableMath && (GFX.Clip[5].DrawMode[clip] & 2)) 860 GFX.DrawBackdropMath(Offset, GFX.Clip[5].Left[clip], GFX.Clip[5].Right[clip]); 861 else 862 GFX.DrawBackdropNomath(Offset, GFX.Clip[5].Left[clip], GFX.Clip[5].Right[clip]); 863 } 864 } 865 866 static void SetupOBJ (void) 867 { 868 int SmallWidth, SmallHeight, LargeWidth, LargeHeight; 869 870 switch (PPU.OBJSizeSelect) 871 { 872 case 0: 873 SmallWidth = SmallHeight = 8; 874 LargeWidth = LargeHeight = 16; 875 break; 876 877 case 1: 878 SmallWidth = SmallHeight = 8; 879 LargeWidth = LargeHeight = 32; 880 break; 881 882 case 2: 883 SmallWidth = SmallHeight = 8; 884 LargeWidth = LargeHeight = 64; 885 break; 886 887 case 3: 888 SmallWidth = SmallHeight = 16; 889 LargeWidth = LargeHeight = 32; 890 break; 891 892 case 4: 893 SmallWidth = SmallHeight = 16; 894 LargeWidth = LargeHeight = 64; 895 break; 896 897 case 5: 898 default: 899 SmallWidth = SmallHeight = 32; 900 LargeWidth = LargeHeight = 64; 901 break; 902 903 case 6: 904 SmallWidth = 16; SmallHeight = 32; 905 LargeWidth = 32; LargeHeight = 64; 906 break; 907 908 case 7: 909 SmallWidth = 16; SmallHeight = 32; 910 LargeWidth = LargeHeight = 32; 911 break; 912 } 913 914 // OK, we have three cases here. Either there's no priority, priority is 915 // normal FirstSprite, or priority is FirstSprite+Y. The first two are 916 // easy, the last is somewhat more ... interesting. So we split them up. 917 918 uint8 LineOBJ[SNES_HEIGHT_EXTENDED]; 919 memset(LineOBJ, 0, sizeof(LineOBJ)); 920 921 int Height; 922 923 if (!PPU.OAMPriorityRotation || !(PPU.OAMFlip & PPU.OAMAddr & 1)) // normal case 924 { 925 for (int i = 0; i < SNES_HEIGHT_EXTENDED; i++) 926 { 927 GFX.OBJLines[i].RTOFlags = 0; 928 GFX.OBJLines[i].Tiles = SNES_SPRITE_TILE_PER_LINE; 929 for (int j = 0; j < 32; j++) 930 GFX.OBJLines[i].OBJ[j].Sprite = -1; 931 } 932 933 int FirstSprite = PPU.FirstSprite; 934 int S = FirstSprite; 935 936 do 937 { 938 if (PPU.OBJ[S].Size) 939 { 940 GFX.OBJWidths[S] = LargeWidth; 941 Height = LargeHeight; 942 } 943 else 944 { 945 GFX.OBJWidths[S] = SmallWidth; 946 Height = SmallHeight; 947 } 948 949 int HPos = PPU.OBJ[S].HPos; 950 if (HPos == -256) 951 HPos = 0; 952 953 if (HPos > -GFX.OBJWidths[S] && HPos <= 256) 954 { 955 if (HPos < 0) 956 GFX.OBJVisibleTiles[S] = (GFX.OBJWidths[S] + HPos + 7) >> 3; 957 else 958 if (HPos + GFX.OBJWidths[S] > 255) 959 GFX.OBJVisibleTiles[S] = (256 - HPos + 7) >> 3; 960 else 961 GFX.OBJVisibleTiles[S] = GFX.OBJWidths[S] >> 3; 962 963 for (int line = 0, Y = (uint8) (PPU.OBJ[S].VPos & 0xff); line < Height; Y++, line++) 964 { 965 if (Y >= SNES_HEIGHT_EXTENDED) 966 continue; 967 968 if (LineOBJ[Y] >= 32) 969 { 970 GFX.OBJLines[Y].RTOFlags |= 0x40; 971 continue; 972 } 973 974 GFX.OBJLines[Y].Tiles -= GFX.OBJVisibleTiles[S]; 975 if (GFX.OBJLines[Y].Tiles < 0) 976 GFX.OBJLines[Y].RTOFlags |= 0x80; 977 978 GFX.OBJLines[Y].OBJ[LineOBJ[Y]].Sprite = S; 979 if (PPU.OBJ[S].VFlip) 980 // Yes, Width not Height. It so happens that the 981 // sprites with H=2*W flip as two WxW sprites. 982 GFX.OBJLines[Y].OBJ[LineOBJ[Y]].Line = line ^ (GFX.OBJWidths[S] - 1); 983 else 984 GFX.OBJLines[Y].OBJ[LineOBJ[Y]].Line = line; 985 986 LineOBJ[Y]++; 987 } 988 } 989 990 S = (S + 1) & 0x7f; 991 } while (S != FirstSprite); 992 993 for (int Y = 1; Y < SNES_HEIGHT_EXTENDED; Y++) 994 GFX.OBJLines[Y].RTOFlags |= GFX.OBJLines[Y - 1].RTOFlags; 995 } 996 else // evil FirstSprite+Y case 997 { 998 // printf("evil FirstSprite+Y case\n"); 999 1000 // We can't afford 30K of ram, we abuse the tile cache instead 1001 uint8 *OBJOnLine = (uint8 *)IPPU.TileCacheData; 1002 memset(IPPU.TileCache, 0, (SNES_HEIGHT_EXTENDED * 128) / 64); 1003 1004 for (int S = 0; S < 128; S++) 1005 { 1006 if (PPU.OBJ[S].Size) 1007 { 1008 GFX.OBJWidths[S] = LargeWidth; 1009 Height = LargeHeight; 1010 } 1011 else 1012 { 1013 GFX.OBJWidths[S] = SmallWidth; 1014 Height = SmallHeight; 1015 } 1016 1017 int HPos = PPU.OBJ[S].HPos; 1018 if (HPos == -256) 1019 HPos = 256; 1020 1021 if (HPos > -GFX.OBJWidths[S] && HPos <= 256) 1022 { 1023 if (HPos < 0) 1024 GFX.OBJVisibleTiles[S] = (GFX.OBJWidths[S] + HPos + 7) >> 3; 1025 else 1026 if (HPos + GFX.OBJWidths[S] >= 257) 1027 GFX.OBJVisibleTiles[S] = (257 - HPos + 7) >> 3; 1028 else 1029 GFX.OBJVisibleTiles[S] = GFX.OBJWidths[S] >> 3; 1030 1031 for (int line = 0, Y = (uint8) (PPU.OBJ[S].VPos & 0xff); line < Height; Y++, line++) 1032 { 1033 if (Y >= SNES_HEIGHT_EXTENDED) 1034 continue; 1035 1036 if (!LineOBJ[Y]) { 1037 memset(OBJOnLine + (Y * 128), 0, 128); 1038 LineOBJ[Y] = TRUE; 1039 } 1040 1041 if (PPU.OBJ[S].VFlip) 1042 // Yes, Width not Height. It so happens that the 1043 // sprites with H=2*W flip as two WxW sprites. 1044 OBJOnLine[(Y * 128) + S] = (line ^ (GFX.OBJWidths[S] - 1)) | 0x80; 1045 else 1046 OBJOnLine[(Y * 128) + S] = line | 0x80; 1047 } 1048 } 1049 } 1050 1051 // Now go through and pull out those OBJ that are actually visible. 1052 int j; 1053 for (int Y = 0; Y < SNES_HEIGHT_EXTENDED; Y++) 1054 { 1055 GFX.OBJLines[Y].RTOFlags = Y ? GFX.OBJLines[Y - 1].RTOFlags : 0; 1056 GFX.OBJLines[Y].Tiles = SNES_SPRITE_TILE_PER_LINE; 1057 1058 int FirstSprite = (PPU.FirstSprite + Y) & 0x7f; 1059 int S = FirstSprite; 1060 j = 0; 1061 1062 if (LineOBJ[Y]) 1063 { 1064 do 1065 { 1066 if (OBJOnLine[(Y * 128) + S]) 1067 { 1068 if (j >= 32) 1069 { 1070 GFX.OBJLines[Y].RTOFlags |= 0x40; 1071 break; 1072 } 1073 1074 GFX.OBJLines[Y].Tiles -= GFX.OBJVisibleTiles[S]; 1075 if (GFX.OBJLines[Y].Tiles < 0) 1076 GFX.OBJLines[Y].RTOFlags |= 0x80; 1077 GFX.OBJLines[Y].OBJ[j].Sprite = S; 1078 GFX.OBJLines[Y].OBJ[j++].Line = OBJOnLine[(Y * 128) + S] & ~0x80; 1079 } 1080 1081 S = (S + 1) & 0x7f; 1082 } while (S != FirstSprite); 1083 } 1084 1085 if (j < 32) 1086 GFX.OBJLines[Y].OBJ[j].Sprite = -1; 1087 } 1088 } 1089 1090 IPPU.OBJChanged = FALSE; 1091 } 1092 1093 #if defined(__GNUC__) && !defined(__clang__) 1094 #pragma GCC push_options 1095 #pragma GCC optimize ("no-tree-vrp") 1096 #endif 1097 static void DrawOBJS (int D) 1098 { 1099 void (*DrawTile) (uint32, uint32, uint32, uint32) = NULL; 1100 void (*DrawClippedTile) (uint32, uint32, uint32, uint32, uint32, uint32) = NULL; 1101 1102 GFX.Z1 = 2; 1103 1104 for (uint32 Y = GFX.StartY, Offset = Y * GFX.PPL; Y <= GFX.EndY; Y++, Offset += GFX.PPL) 1105 { 1106 int I = 0; 1107 int tiles = GFX.OBJLines[Y].Tiles; 1108 1109 for (int S = GFX.OBJLines[Y].OBJ[I].Sprite; S >= 0 && I < 32; S = GFX.OBJLines[Y].OBJ[++I].Sprite) 1110 { 1111 tiles += GFX.OBJVisibleTiles[S]; 1112 if (tiles <= 0) 1113 continue; 1114 1115 int BaseTile = (((GFX.OBJLines[Y].OBJ[I].Line << 1) + (PPU.OBJ[S].Name & 0xf0)) & 0xf0) | (PPU.OBJ[S].Name & 0x100) | (PPU.OBJ[S].Palette << 10); 1116 int TileX = PPU.OBJ[S].Name & 0x0f; 1117 int TileLine = (GFX.OBJLines[Y].OBJ[I].Line & 7) * 8; 1118 int TileInc = 1; 1119 1120 if (PPU.OBJ[S].HFlip) 1121 { 1122 TileX = (TileX + (GFX.OBJWidths[S] >> 3) - 1) & 0x0f; 1123 BaseTile |= H_FLIP; 1124 TileInc = -1; 1125 } 1126 1127 GFX.Z2 = D + PPU.OBJ[S].Priority * 4; 1128 1129 int DrawMode = 3; 1130 int clip = 0, next_clip = -1000; 1131 int X = PPU.OBJ[S].HPos; 1132 if (X == -256) 1133 X = 256; 1134 1135 for (int t = tiles, O = Offset + X; X <= 256 && X < PPU.OBJ[S].HPos + GFX.OBJWidths[S]; TileX = (TileX + TileInc) & 0x0f, X += 8, O += 8) 1136 { 1137 if (X < -7 || --t < 0 || X == 256) 1138 continue; 1139 1140 for (int x = X; x < X + 8;) 1141 { 1142 if (x >= next_clip) 1143 { 1144 for (; clip < GFX.Clip[4].Count && GFX.Clip[4].Left[clip] <= x; clip++) ; 1145 if (clip == 0 || x >= GFX.Clip[4].Right[clip - 1]) 1146 { 1147 DrawMode = 0; 1148 next_clip = ((clip < GFX.Clip[4].Count) ? GFX.Clip[4].Left[clip] : 1000); 1149 } 1150 else 1151 { 1152 DrawMode = GFX.Clip[4].DrawMode[clip - 1]; 1153 next_clip = GFX.Clip[4].Right[clip - 1]; 1154 GFX.ClipColors = !(DrawMode & 1); 1155 1156 if (BG.EnableMath && (PPU.OBJ[S].Palette & 4) && (DrawMode & 2)) 1157 { 1158 DrawTile = GFX.DrawTileMath; 1159 DrawClippedTile = GFX.DrawClippedTileMath; 1160 } 1161 else 1162 { 1163 DrawTile = GFX.DrawTileNomath; 1164 DrawClippedTile = GFX.DrawClippedTileNomath; 1165 } 1166 } 1167 } 1168 1169 if (x == X && x + 8 < next_clip) 1170 { 1171 if (DrawMode) 1172 DrawTile(BaseTile | TileX, O, TileLine, 1); 1173 x += 8; 1174 } 1175 else 1176 { 1177 int w = (next_clip <= X + 8) ? next_clip - x : X + 8 - x; 1178 if (DrawMode) 1179 DrawClippedTile(BaseTile | TileX, O, x - X, w, TileLine, 1); 1180 x += w; 1181 } 1182 } 1183 } 1184 } 1185 } 1186 } 1187 #if defined(__GNUC__) && !defined(__clang__) 1188 #pragma GCC pop_options 1189 #endif 1190 1191 static inline void RenderScreen (bool8 sub) 1192 { 1193 int BGActive, D; 1194 1195 if (!sub) 1196 { 1197 GFX.S = GFX.Screen; 1198 GFX.DB = GFX.ZBuffer; 1199 GFX.Clip = IPPU.Clip[0]; 1200 BGActive = Memory.PPU_IO[0x12c] & ~Settings.BG_Forced; 1201 D = 32; 1202 } 1203 else 1204 { 1205 GFX.S = GFX.SubScreen; 1206 GFX.DB = GFX.SubZBuffer; 1207 GFX.Clip = IPPU.Clip[1]; 1208 BGActive = Memory.PPU_IO[0x12d] & ~Settings.BG_Forced; 1209 D = (Memory.PPU_IO[0x130] & 2) << 4; // 'do math' depth flag 1210 } 1211 1212 if (BGActive & 0x10) 1213 { 1214 BG.TileAddress = PPU.OBJNameBase; 1215 BG.NameSelect = PPU.OBJNameSelect; 1216 BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & 0x10); 1217 BG.StartPalette = 128; 1218 S9xSelectTileConverter(4, FALSE, sub, FALSE); 1219 S9xSelectTileRenderers(PPU.BGMode, sub, TRUE); 1220 DrawOBJS(D + 4); 1221 } 1222 1223 BG.NameSelect = 0; 1224 S9xSelectTileRenderers(PPU.BGMode, sub, FALSE); 1225 1226 #define DO_BG(n, pal, depth, hires, offset, Zh, Zl, voffoff) \ 1227 if (BGActive & (1 << n)) \ 1228 { \ 1229 BG.StartPalette = pal; \ 1230 BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & (1 << n)); \ 1231 BG.TileSizeH = (!hires && PPU.BG[n].BGSize) ? 16 : 8; \ 1232 BG.TileSizeV = (PPU.BG[n].BGSize) ? 16 : 8; \ 1233 S9xSelectTileConverter(depth, hires, sub, PPU.BGMosaic[n]); \ 1234 \ 1235 if (offset) \ 1236 { \ 1237 BG.OffsetSizeH = (!hires && PPU.BG[2].BGSize) ? 16 : 8; \ 1238 BG.OffsetSizeV = (PPU.BG[2].BGSize) ? 16 : 8; \ 1239 \ 1240 if (PPU.BGMosaic[n] && (hires || PPU.Mosaic > 1)) \ 1241 DrawBackgroundOffsetMosaic(n, D + Zh, D + Zl, voffoff); \ 1242 else \ 1243 DrawBackgroundOffset(n, D + Zh, D + Zl, voffoff); \ 1244 } \ 1245 else \ 1246 { \ 1247 if (PPU.BGMosaic[n] && (hires || PPU.Mosaic > 1)) \ 1248 DrawBackgroundMosaic(n, D + Zh, D + Zl); \ 1249 else \ 1250 DrawBackground(n, D + Zh, D + Zl); \ 1251 } \ 1252 } 1253 1254 switch (PPU.BGMode) 1255 { 1256 case 0: 1257 DO_BG(0, 0, 2, FALSE, FALSE, 15, 11, 0); 1258 DO_BG(1, 32, 2, FALSE, FALSE, 14, 10, 0); 1259 DO_BG(2, 64, 2, FALSE, FALSE, 7, 3, 0); 1260 DO_BG(3, 96, 2, FALSE, FALSE, 6, 2, 0); 1261 break; 1262 1263 case 1: 1264 DO_BG(0, 0, 4, FALSE, FALSE, 15, 11, 0); 1265 DO_BG(1, 0, 4, FALSE, FALSE, 14, 10, 0); 1266 DO_BG(2, 0, 2, FALSE, FALSE, (PPU.BG3Priority ? 17 : 7), 3, 0); 1267 break; 1268 1269 case 2: 1270 DO_BG(0, 0, 4, FALSE, TRUE, 15, 7, 8); 1271 DO_BG(1, 0, 4, FALSE, TRUE, 11, 3, 8); 1272 break; 1273 1274 case 3: 1275 DO_BG(0, 0, 8, FALSE, FALSE, 15, 7, 0); 1276 DO_BG(1, 0, 4, FALSE, FALSE, 11, 3, 0); 1277 break; 1278 1279 case 4: 1280 DO_BG(0, 0, 8, FALSE, TRUE, 15, 7, 0); 1281 DO_BG(1, 0, 2, FALSE, TRUE, 11, 3, 0); 1282 break; 1283 1284 case 5: 1285 DO_BG(0, 0, 4, TRUE, FALSE, 15, 7, 0); 1286 DO_BG(1, 0, 2, TRUE, FALSE, 11, 3, 0); 1287 break; 1288 1289 case 6: 1290 DO_BG(0, 0, 4, TRUE, TRUE, 15, 7, 8); 1291 break; 1292 1293 case 7: 1294 if (BGActive & 0x01) 1295 { 1296 BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & 1); 1297 DrawBackgroundMode7(0, GFX.DrawMode7BG1Math, GFX.DrawMode7BG1Nomath, D); 1298 } 1299 1300 if ((Memory.PPU_IO[0x133] & 0x40) && (BGActive & 0x02)) 1301 { 1302 BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & 2); 1303 DrawBackgroundMode7(1, GFX.DrawMode7BG2Math, GFX.DrawMode7BG2Nomath, D); 1304 } 1305 break; 1306 } 1307 1308 #undef DO_BG 1309 1310 BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & 0x20); 1311 1312 DrawBackdrop(); 1313 } 1314 1315 static inline uint8 CalcWindowMask (int i, uint8 W1, uint8 W2) 1316 { 1317 if (!PPU.ClipWindow1Enable[i]) 1318 { 1319 if (!PPU.ClipWindow2Enable[i]) 1320 return (0); 1321 if (!PPU.ClipWindow2Inside[i]) 1322 return (~W2); 1323 return (W2); 1324 } 1325 else 1326 { 1327 if (!PPU.ClipWindow2Enable[i]) 1328 { 1329 if (!PPU.ClipWindow1Inside[i]) 1330 return (~W1); 1331 return (W1); 1332 } 1333 else 1334 { 1335 if (!PPU.ClipWindow1Inside[i]) 1336 W1 = ~W1; 1337 if (!PPU.ClipWindow2Inside[i]) 1338 W2 = ~W2; 1339 1340 switch (PPU.ClipWindowOverlapLogic[i]) 1341 { 1342 case 0: // OR 1343 return (W1 | W2); 1344 1345 case 1: // AND 1346 return (W1 & W2); 1347 1348 case 2: // XOR 1349 return (W1 ^ W2); 1350 1351 case 3: // XNOR 1352 return (~(W1 ^ W2)); 1353 } 1354 } 1355 } 1356 1357 // Never get here 1358 return (0); 1359 } 1360 1361 static inline void StoreWindowRegions (uint8 Mask, struct ClipData *Clip, int n_regions, int16 *windows, uint8 *drawing_modes, bool8 sub, bool8 StoreMode0) 1362 { 1363 int ct = 0; 1364 1365 for (int j = 0; j < n_regions; j++) 1366 { 1367 int DrawMode = drawing_modes[j]; 1368 if (sub) 1369 DrawMode |= 1; 1370 if (Mask & (1 << j)) 1371 DrawMode = 0; 1372 1373 if (!StoreMode0 && !DrawMode) 1374 continue; 1375 1376 if (ct > 0 && Clip->Right[ct - 1] == windows[j] && Clip->DrawMode[ct - 1] == DrawMode) 1377 Clip->Right[ct - 1] = windows[j + 1]; // This region borders with and has the same drawing mode as the previous region: merge them. 1378 else 1379 { 1380 // Add a new region to the BG 1381 Clip->Left[ct] = windows[j]; 1382 Clip->Right[ct] = windows[j + 1]; 1383 Clip->DrawMode[ct] = DrawMode; 1384 ct++; 1385 } 1386 } 1387 1388 Clip->Count = ct; 1389 } 1390 1391 static inline void ComputeClipWindows (void) 1392 { 1393 int16 windows[6] = { 0, 256, 256, 256, 256, 256 }; 1394 uint8 drawing_modes[5] = { 0, 0, 0, 0, 0 }; 1395 int n_regions = 1; 1396 int i, j; 1397 1398 // Calculate window regions. We have at most 5 regions, because we have 6 control points 1399 // (screen edges, window 1 left & right, and window 2 left & right). 1400 1401 if (PPU.Window1Left <= PPU.Window1Right) 1402 { 1403 if (PPU.Window1Left > 0) 1404 { 1405 windows[2] = 256; 1406 windows[1] = PPU.Window1Left; 1407 n_regions = 2; 1408 } 1409 1410 if (PPU.Window1Right < 255) 1411 { 1412 windows[n_regions + 1] = 256; 1413 windows[n_regions] = PPU.Window1Right + 1; 1414 n_regions++; 1415 } 1416 } 1417 1418 if (PPU.Window2Left <= PPU.Window2Right) 1419 { 1420 for (i = 0; i <= n_regions; i++) 1421 { 1422 if (PPU.Window2Left == windows[i]) 1423 break; 1424 1425 if (PPU.Window2Left < windows[i]) 1426 { 1427 for (j = n_regions; j >= i; j--) 1428 windows[j + 1] = windows[j]; 1429 1430 windows[i] = PPU.Window2Left; 1431 n_regions++; 1432 break; 1433 } 1434 } 1435 1436 for (; i <= n_regions; i++) 1437 { 1438 if (PPU.Window2Right + 1 == windows[i]) 1439 break; 1440 1441 if (PPU.Window2Right + 1 < windows[i]) 1442 { 1443 for (j = n_regions; j >= i; j--) 1444 windows[j + 1] = windows[j]; 1445 1446 windows[i] = PPU.Window2Right + 1; 1447 n_regions++; 1448 break; 1449 } 1450 } 1451 } 1452 1453 // Get a bitmap of which regions correspond to each window. 1454 1455 const uint8 region_map[6][6] = 1456 { 1457 { 0, 0x01, 0x03, 0x07, 0x0f, 0x1f }, 1458 { 0, 0, 0x02, 0x06, 0x0e, 0x1e }, 1459 { 0, 0, 0, 0x04, 0x0c, 0x1c }, 1460 { 0, 0, 0, 0, 0x08, 0x18 }, 1461 { 0, 0, 0, 0, 0, 0x10 } 1462 }; 1463 1464 uint8 W1 = 0; 1465 uint8 W2 = 0; 1466 1467 if (PPU.Window1Left <= PPU.Window1Right) 1468 { 1469 for (i = 0; windows[i] != PPU.Window1Left; i++) ; 1470 for (j = i; windows[j] != PPU.Window1Right + 1; j++) ; 1471 W1 = region_map[i][j]; 1472 } 1473 1474 if (PPU.Window2Left <= PPU.Window2Right) 1475 { 1476 for (i = 0; windows[i] != PPU.Window2Left; i++) ; 1477 for (j = i; windows[j] != PPU.Window2Right + 1; j++) ; 1478 W2 = region_map[i][j]; 1479 } 1480 1481 // Color Window affects the drawing mode for each region. 1482 // Modes are: 3=Draw as normal, 2=clip color (math only), 1=no math (draw only), 0=nothing. 1483 1484 uint8 CW_color = 0, CW_math = 0; 1485 uint8 CW = CalcWindowMask(5, W1, W2); 1486 1487 switch (Memory.PPU_IO[0x130] & 0xc0) 1488 { 1489 case 0x00: CW_color = 0; break; 1490 case 0x40: CW_color = ~CW; break; 1491 case 0x80: CW_color = CW; break; 1492 case 0xc0: CW_color = 0xff; break; 1493 } 1494 1495 switch (Memory.PPU_IO[0x130] & 0x30) 1496 { 1497 case 0x00: CW_math = 0; break; 1498 case 0x10: CW_math = ~CW; break; 1499 case 0x20: CW_math = CW; break; 1500 case 0x30: CW_math = 0xff; break; 1501 } 1502 1503 for (int i = 0; i < n_regions; i++) 1504 { 1505 if (!(CW_color & (1 << i))) 1506 drawing_modes[i] |= 1; 1507 if (!(CW_math & (1 << i))) 1508 drawing_modes[i] |= 2; 1509 } 1510 1511 // Store backdrop clip window (draw everywhere color window allows) 1512 1513 StoreWindowRegions(0, &IPPU.Clip[0][5], n_regions, windows, drawing_modes, FALSE, TRUE); 1514 StoreWindowRegions(0, &IPPU.Clip[1][5], n_regions, windows, drawing_modes, TRUE, TRUE); 1515 1516 // Store per-BG and OBJ clip windows 1517 1518 for (int j = 0; j < 5; j++) 1519 { 1520 if (Memory.PPU_IO[0x12e] & (1 << j)) 1521 StoreWindowRegions(CalcWindowMask(j, W1, W2), &IPPU.Clip[0][j], n_regions, windows, drawing_modes, 0, FALSE); 1522 else 1523 StoreWindowRegions(0, &IPPU.Clip[0][j], n_regions, windows, drawing_modes, 0, FALSE); 1524 1525 if (Memory.PPU_IO[0x12f] & (1 << j)) 1526 StoreWindowRegions(CalcWindowMask(j, W1, W2), &IPPU.Clip[1][j], n_regions, windows, drawing_modes, 1, FALSE); 1527 else 1528 StoreWindowRegions(0, &IPPU.Clip[1][j], n_regions, windows, drawing_modes, 1, FALSE); 1529 } 1530 } 1531 1532 bool8 S9xGraphicsInit (void) 1533 { 1534 GFX.PPL = SNES_WIDTH; 1535 GFX.ScreenSize = GFX.PPL * SNES_HEIGHT_EXTENDED; 1536 IPPU.OBJChanged = TRUE; 1537 Settings.BG_Forced = 0; 1538 1539 S9xInitTileRenderer(); 1540 1541 GFX.Screen = (SNESPixel *) emu_Malloc(GFX.ScreenSize * sizeof(SNESPixel)); 1542 GFX.SubScreen = (SNESPixel *) emu_SMalloc(GFX.ScreenSize * sizeof(SNESPixel)); 1543 memset((unsigned char*)GFX.Screen, 0, GFX.ScreenSize * sizeof(SNESPixel)); 1544 memset((unsigned char*)GFX.SubScreen, 0, GFX.ScreenSize * sizeof(SNESPixel)); 1545 GFX.ZBuffer = (uint8 *) emu_Malloc(GFX.ScreenSize); 1546 GFX.SubZBuffer = (uint8 *) emu_Malloc(GFX.ScreenSize); 1547 //GFX.ZERO = (SNESPixel *) GFX.SubScreen; // This will cause garbage but for now it's okay 1548 IPPU.TileCacheData = (uint8 *) emu_SMalloc(4096 * 64); 1549 1550 if (!GFX.SubScreen || !GFX.ZBuffer || !GFX.SubZBuffer || !IPPU.TileCacheData) 1551 { 1552 S9xGraphicsDeinit(); 1553 return (FALSE); 1554 } 1555 1556 #if 0 // TO DO: pre-compute GFX.ZERO 1557 1558 // Lookup table for 1/2 color subtraction 1559 //memset(GFX.ZERO, 0, 0x10000 * sizeof(uint16)); 1560 for (uint8 r = 0; r <= MAX_RED; r++) 1561 { 1562 uint8 r2 = (r & 0x10) ? (r & ~0x10) : (0); 1563 1564 for (uint8 g = 0; g <= MAX_GREEN; g++) 1565 { 1566 uint8 g2 = (g & GREEN_HI_BIT) ? (g & ~GREEN_HI_BIT) : (0); 1567 1568 for (uint8 b = 0; b <= MAX_BLUE; b++) 1569 { 1570 uint8 b2 = (b & 0x10) ? (b & ~0x10) : (0); 1571 1572 GFX.ZERO[BUILD_PIXEL2(r, g, b)] = BUILD_PIXEL2(r2, g2, b2); 1573 GFX.ZERO[BUILD_PIXEL2(r, g, b) & ~ALPHA_BITS_MASK] = BUILD_PIXEL2(r2, g2, b2); 1574 } 1575 } 1576 } 1577 #endif 1578 1579 return (TRUE); 1580 } 1581 1582 void S9xGraphicsDeinit (void) 1583 { 1584 if (GFX.SubScreen) { emu_SFree(GFX.SubScreen); GFX.SubScreen = NULL; } 1585 if (GFX.ZBuffer) { emu_Free(GFX.ZBuffer); GFX.ZBuffer = NULL; } 1586 if (GFX.SubZBuffer) { emu_SFree(GFX.SubZBuffer); GFX.SubZBuffer = NULL; } 1587 if (IPPU.TileCacheData) { emu_SFree(IPPU.TileCacheData); IPPU.TileCacheData = NULL; } 1588 } 1589 1590 void S9xGraphicsScreenResize (void) 1591 { 1592 IPPU.RenderedScreenHeight = PPU.ScreenHeight; 1593 IPPU.RenderedScreenWidth = SNES_WIDTH; 1594 IPPU.MaxBrightness = PPU.Brightness; 1595 1596 IPPU.Interlace = Memory.PPU_IO[0x133] & 1; 1597 IPPU.InterlaceOBJ = Memory.PPU_IO[0x133] & 2; 1598 IPPU.PseudoHires = Memory.PPU_IO[0x133] & 8; 1599 1600 static uint8 prev2133 = 0; 1601 if (prev2133 != Memory.PPU_IO[0x133]) 1602 { 1603 sprintf(String, "Int=%d IntOBJ=%d Hires=%d\n", IPPU.Interlace, IPPU.InterlaceOBJ, IPPU.PseudoHires); 1604 S9xMessage(0, 0, String); 1605 prev2133 = Memory.PPU_IO[0x133]; 1606 } 1607 } 1608 1609 void S9xStartScreenRefresh (void) 1610 { 1611 if (IPPU.RenderThisFrame) 1612 { 1613 S9xGraphicsScreenResize(); 1614 1615 IPPU.RenderedFramesCount++; 1616 1617 PPU.MosaicStart = 0; 1618 PPU.RecomputeClipWindows = TRUE; 1619 IPPU.PreviousLine = IPPU.CurrentLine = 0; 1620 1621 memset(GFX.ZBuffer, 0, GFX.ScreenSize); 1622 memset(GFX.SubZBuffer, 0, GFX.ScreenSize); 1623 } 1624 1625 if (++IPPU.FrameCount % Settings.FrameRate == 0) 1626 { 1627 IPPU.DisplayedRenderedFrameCount = IPPU.RenderedFramesCount; 1628 IPPU.RenderedFramesCount = 0; 1629 IPPU.FrameCount = 0; 1630 } 1631 1632 if (GFX.InfoStringTimeout > 0 && --GFX.InfoStringTimeout == 0) 1633 GFX.InfoString = NULL; 1634 1635 IPPU.TotalEmulatedFrames++; 1636 } 1637 1638 void S9xEndScreenRefresh (void) 1639 { 1640 if (IPPU.RenderThisFrame) 1641 { 1642 FLUSH_REDRAW(); 1643 //S9xDisplayMessages(GFX.Screen, GFX.PPL, IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight, 1); 1644 S9xBlitUpdate(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight); 1645 } 1646 1647 #ifdef DEBUGGER 1648 if (CPU.Flags & FRAME_ADVANCE_FLAG) 1649 { 1650 if (ICPU.FrameAdvanceCount) 1651 { 1652 ICPU.FrameAdvanceCount--; 1653 IPPU.RenderThisFrame = TRUE; 1654 IPPU.FrameSkip = 0; 1655 } 1656 else 1657 { 1658 CPU.Flags &= ~FRAME_ADVANCE_FLAG; 1659 CPU.Flags |= DEBUG_MODE_FLAG; 1660 } 1661 } 1662 #endif 1663 } 1664 1665 void S9xRenderLine (int C) 1666 { 1667 if (IPPU.RenderThisFrame) 1668 { 1669 LineData[C].BG[0].VOffset = PPU.BG[0].VOffset + 1; 1670 LineData[C].BG[0].HOffset = PPU.BG[0].HOffset; 1671 LineData[C].BG[1].VOffset = PPU.BG[1].VOffset + 1; 1672 LineData[C].BG[1].HOffset = PPU.BG[1].HOffset; 1673 1674 if (PPU.BGMode == 7) 1675 { 1676 S9UpdateLineMatrix(C); 1677 } 1678 else 1679 { 1680 LineData[C].BG[2].VOffset = PPU.BG[2].VOffset + 1; 1681 LineData[C].BG[2].HOffset = PPU.BG[2].HOffset; 1682 LineData[C].BG[3].VOffset = PPU.BG[3].VOffset + 1; 1683 LineData[C].BG[3].HOffset = PPU.BG[3].HOffset; 1684 } 1685 1686 IPPU.CurrentLine = C + 1; 1687 } 1688 else 1689 { 1690 // if we're not rendering this frame, we still need to update this 1691 // XXX: Check ForceBlank? Or anything else? 1692 if (IPPU.OBJChanged) 1693 SetupOBJ(); 1694 PPU.RangeTimeOver |= GFX.OBJLines[C].RTOFlags; 1695 } 1696 } 1697 1698 1699 void S9xUpdateScreen (void) 1700 { 1701 if (IPPU.OBJChanged) 1702 SetupOBJ(); 1703 1704 // XXX: Check ForceBlank? Or anything else? 1705 PPU.RangeTimeOver |= GFX.OBJLines[GFX.EndY].RTOFlags; 1706 1707 GFX.StartY = IPPU.PreviousLine; 1708 if ((GFX.EndY = IPPU.CurrentLine - 1) >= PPU.ScreenHeight) 1709 GFX.EndY = PPU.ScreenHeight - 1; 1710 1711 if (!PPU.ForcedBlanking) 1712 { 1713 // If force blank, may as well completely skip all this. We only did 1714 // the OBJ because (AFAWK) the RTO flags are updated even during force-blank. 1715 1716 if (PPU.RecomputeClipWindows) 1717 { 1718 ComputeClipWindows(); 1719 PPU.RecomputeClipWindows = FALSE; 1720 } 1721 1722 if ((Memory.PPU_IO[0x130] & 0x30) != 0x30 && (Memory.PPU_IO[0x131] & 0x3f)) 1723 GFX.FixedColour = BUILD_PIXEL(IPPU.XB[PPU.FixedColourRed], IPPU.XB[PPU.FixedColourGreen], IPPU.XB[PPU.FixedColourBlue]); 1724 1725 if (PPU.BGMode == 5 || PPU.BGMode == 6 || IPPU.PseudoHires || 1726 ((Memory.PPU_IO[0x130] & 0x30) != 0x30 && (Memory.PPU_IO[0x130] & 2) && (Memory.PPU_IO[0x131] & 0x3f) && (Memory.PPU_IO[0x12d] & 0x1f))) 1727 // If hires (Mode 5/6 or pseudo-hires) or math is to be done 1728 // involving the subscreen, then we need to render the subscreen... 1729 RenderScreen(TRUE); 1730 1731 RenderScreen(FALSE); 1732 } 1733 // else 1734 // { 1735 // GFX.S = (SNESPixel*)GFX.Screen + GFX.StartY * GFX.PPL; 1736 // // Set to black 1737 // for (int l = GFX.StartY; l <= GFX.EndY; l++, GFX.S += GFX.PPL) { 1738 // memset(GFX.S, 0xff, IPPU.RenderedScreenWidth * sizeof(SNESPixel)); 1739 // } 1740 // } 1741 1742 IPPU.PreviousLine = IPPU.CurrentLine; 1743 } 1744 1745 void S9xSetInfoString (const char *string) 1746 { 1747 if (Settings.InitialInfoStringTimeout > 0) 1748 { 1749 GFX.InfoString = string; 1750 GFX.InfoStringTimeout = Settings.InitialInfoStringTimeout; 1751 if (Settings.Paused) 1752 S9xBlitUpdate(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight); 1753 } 1754 } 1755 1756 /* 1757 static inline void DisplayChar (uint16 *s, uint8 c) 1758 { 1759 const uint16 black = BUILD_PIXEL(0, 0, 0); 1760 1761 int line = ((c - 32) >> 4) * FONT_HEIGHT; 1762 int offset = ((c - 32) & 15) * FONT_WIDTH; 1763 1764 for (int h = 0; h < FONT_HEIGHT; h++, line++, s += GFX.PPL - FONT_WIDTH) 1765 { 1766 for (int w = 0; w < FONT_WIDTH; w++, s++) 1767 { 1768 char p = font[line][offset + w]; 1769 1770 if (p == '#') 1771 *s = Settings.DisplayColor; 1772 else 1773 if (p == '.') 1774 *s = black; 1775 } 1776 } 1777 } 1778 1779 static inline void DisplayStringFromBottom (const char *string, int linesFromBottom, int pixelsFromLeft, bool allowWrap) 1780 { 1781 if (linesFromBottom <= 0) 1782 linesFromBottom = 1; 1783 1784 uint16 *dst = GFX.Screen + (IPPU.RenderedScreenHeight - FONT_HEIGHT * linesFromBottom) * GFX.PPL + pixelsFromLeft; 1785 1786 int len = strlen(string); 1787 int max_chars = IPPU.RenderedScreenWidth / (FONT_WIDTH - 1); 1788 int char_count = 0; 1789 1790 for (int i = 0 ; i < len ; i++, char_count++) 1791 { 1792 if (char_count >= max_chars || (uint8) string[i] < 32) 1793 { 1794 if (!allowWrap) 1795 break; 1796 1797 dst += FONT_HEIGHT * GFX.PPL - (FONT_WIDTH - 1) * max_chars; 1798 if (dst >= GFX.Screen + IPPU.RenderedScreenHeight * GFX.PPL) 1799 break; 1800 1801 char_count -= max_chars; 1802 } 1803 1804 if ((uint8) string[i] < 32) 1805 continue; 1806 1807 DisplayChar(dst, string[i]); 1808 dst += FONT_WIDTH - 1; 1809 } 1810 } 1811 */ 1812 1813 void S9xDisplayMessages (SNESPixel *screen, int ppl, int width, int height, int scale) 1814 { 1815 // if (GFX.InfoString && *GFX.InfoString) 1816 // DisplayStringFromBottom(GFX.InfoString, 5, 1, true); 1817 }