Video.cpp (30892B)
1/* 2 * Gearboy - Nintendo Game Boy Emulator 3 * Copyright (C) 2012 Ignacio Sanchez 4 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * any later version. 9 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see http://www.gnu.org/licenses/ 17 * 18 */ 19 20#include "Video.h" 21#include "Memory.h" 22#include "Processor.h" 23 24Video::Video(Memory* pMemory, Processor* pProcessor) 25{ 26 m_pMemory = pMemory; 27 m_pMemory->SetVideo(this); 28 m_pProcessor = pProcessor; 29 InitPointer(m_pFrameBuffer); 30 InitPointer(m_pColorFrameBuffer); 31 InitPointer(m_pSpriteXCacheBuffer); 32 InitPointer(m_pColorCacheBuffer); 33 m_iStatusMode = 0; 34 m_iStatusModeCounter = 0; 35 m_iStatusModeCounterAux = 0; 36 m_iStatusModeLYCounter = 0; 37 m_iScreenEnableDelayCycles = 0; 38 m_iStatusVBlankLine = 0; 39 m_iWindowLine = 0; 40 m_iPixelCounter = 0; 41 m_iTileCycleCounter = 0; 42 m_bScreenEnabled = true; 43 m_bCGB = false; 44 m_bScanLineTransfered = false; 45 m_iHideFrames = 0; 46 m_IRQ48Signal = 0; 47 m_pixelFormat = GB_PIXEL_RGB565; 48} 49 50Video::~Video() 51{ 52 SafeDeleteArray(m_pSpriteXCacheBuffer); 53 SafeDeleteArray(m_pColorCacheBuffer); 54 SafeDeleteArray(m_pFrameBuffer); 55} 56 57void Video::Init() 58{ 59 m_pFrameBuffer = new u8[GAMEBOY_WIDTH * GAMEBOY_HEIGHT]; 60 m_pSpriteXCacheBuffer = new int[GAMEBOY_WIDTH * GAMEBOY_HEIGHT]; 61 m_pColorCacheBuffer = new u8[GAMEBOY_WIDTH * GAMEBOY_HEIGHT]; 62 Reset(false); 63} 64 65void Video::Reset(bool bCGB) 66{ 67 for (int i = 0; i < (GAMEBOY_WIDTH * GAMEBOY_HEIGHT); i++) 68 m_pSpriteXCacheBuffer[i] = m_pFrameBuffer[i] = m_pColorCacheBuffer[i] = 0; 69 70 for (int p = 0; p < 8; p++) 71 for (int c = 0; c < 4; c++) 72 { 73 m_CGBBackgroundPalettes[p][c][0] = m_CGBSpritePalettes[p][c][0] = 0x0000; 74 m_CGBBackgroundPalettes[p][c][1] = m_CGBSpritePalettes[p][c][1] = 0x0000; 75 } 76 77 m_iStatusMode = 1; 78 m_iStatusModeCounter = 0; 79 m_iStatusModeCounterAux = 0; 80 m_iStatusModeLYCounter = 144; 81 m_iScreenEnableDelayCycles = 0; 82 m_iStatusVBlankLine = 0; 83 m_iWindowLine = 0; 84 m_iPixelCounter = 0; 85 m_iTileCycleCounter = 0; 86 m_bScreenEnabled = true; 87 m_bScanLineTransfered = false; 88 m_bCGB = bCGB; 89 m_iHideFrames = 0; 90 m_IRQ48Signal = 0; 91} 92 93bool Video::Tick(unsigned int &clockCycles, u16* pColorFrameBuffer, GB_Color_Format pixelFormat) 94{ 95 m_pColorFrameBuffer = pColorFrameBuffer; 96 m_pixelFormat = pixelFormat; 97 98 bool vblank = false; 99 m_iStatusModeCounter += clockCycles; 100 101 if (m_bScreenEnabled) 102 { 103 switch (m_iStatusMode) 104 { 105 // During H-BLANK 106 case 0: 107 { 108 if (m_iStatusModeCounter >= 204) 109 { 110 m_iStatusModeCounter -= 204; 111 m_iStatusMode = 2; 112 113 m_iStatusModeLYCounter++; 114 m_pMemory->Load(0xFF44, m_iStatusModeLYCounter); 115 CompareLYToLYC(); 116 117 if (m_bCGB && m_pMemory->IsHDMAEnabled() && (!m_pProcessor->Halted() || m_pProcessor->InterruptIsAboutToRaise())) 118 { 119 unsigned int cycles = m_pMemory->PerformHDMA(); 120 m_iStatusModeCounter += cycles; 121 clockCycles += cycles; 122 } 123 124 if (m_iStatusModeLYCounter == 144) 125 { 126 m_iStatusMode = 1; 127 m_iStatusVBlankLine = 0; 128 m_iStatusModeCounterAux = m_iStatusModeCounter; 129 130 m_pProcessor->RequestInterrupt(Processor::VBlank_Interrupt); 131 132 m_IRQ48Signal &= 0x09; 133 u8 stat = m_pMemory->Retrieve(0xFF41); 134 if (IsSetBit(stat, 4)) 135 { 136 if (!IsSetBit(m_IRQ48Signal, 0) && !IsSetBit(m_IRQ48Signal, 3)) 137 { 138 m_pProcessor->RequestInterrupt(Processor::LCDSTAT_Interrupt); 139 } 140 m_IRQ48Signal = SetBit(m_IRQ48Signal, 1); 141 } 142 m_IRQ48Signal &= 0x0E; 143 144 if (m_iHideFrames > 0) 145 m_iHideFrames--; 146 else 147 vblank = true; 148 149 m_iWindowLine = 0; 150 } 151 else 152 { 153 m_IRQ48Signal &= 0x09; 154 u8 stat = m_pMemory->Retrieve(0xFF41); 155 if (IsSetBit(stat, 5)) 156 { 157 if (m_IRQ48Signal == 0) 158 { 159 m_pProcessor->RequestInterrupt(Processor::LCDSTAT_Interrupt); 160 } 161 m_IRQ48Signal = SetBit(m_IRQ48Signal, 2); 162 } 163 m_IRQ48Signal &= 0x0E; 164 } 165 166 UpdateStatRegister(); 167 } 168 break; 169 } 170 // During V-BLANK 171 case 1: 172 { 173 m_iStatusModeCounterAux += clockCycles; 174 175 if (m_iStatusModeCounterAux >= 456) 176 { 177 m_iStatusModeCounterAux -= 456; 178 m_iStatusVBlankLine++; 179 180 if (m_iStatusVBlankLine <= 9) 181 { 182 m_iStatusModeLYCounter++; 183 m_pMemory->Load(0xFF44, m_iStatusModeLYCounter); 184 CompareLYToLYC(); 185 } 186 } 187 188 if ((m_iStatusModeCounter >= 4104) && (m_iStatusModeCounterAux >= 4) && (m_iStatusModeLYCounter == 153)) 189 { 190 m_iStatusModeLYCounter = 0; 191 m_pMemory->Load(0xFF44, m_iStatusModeLYCounter); 192 CompareLYToLYC(); 193 } 194 195 if (m_iStatusModeCounter >= 4560) 196 { 197 m_iStatusModeCounter -= 4560; 198 m_iStatusMode = 2; 199 UpdateStatRegister(); 200 m_IRQ48Signal &= 0x07; 201 202 203 m_IRQ48Signal &= 0x0A; 204 u8 stat = m_pMemory->Retrieve(0xFF41); 205 if (IsSetBit(stat, 5)) 206 { 207 if (m_IRQ48Signal == 0) 208 { 209 m_pProcessor->RequestInterrupt(Processor::LCDSTAT_Interrupt); 210 } 211 m_IRQ48Signal = SetBit(m_IRQ48Signal, 2); 212 } 213 m_IRQ48Signal &= 0x0D; 214 } 215 break; 216 } 217 // During searching OAM RAM 218 case 2: 219 { 220 if (m_iStatusModeCounter >= 80) 221 { 222 m_iStatusModeCounter -= 80; 223 m_iStatusMode = 3; 224 m_bScanLineTransfered = false; 225 m_IRQ48Signal &= 0x08; 226 UpdateStatRegister(); 227 } 228 break; 229 } 230 // During transfering data to LCD driver 231 case 3: 232 { 233#ifndef PERFORMANCE 234 if (m_iPixelCounter < 160) 235 { 236 m_iTileCycleCounter += clockCycles; 237 u8 lcdc = m_pMemory->Retrieve(0xFF40); 238 239 if (m_bScreenEnabled && IsSetBit(lcdc, 7)) 240 { 241 while (m_iTileCycleCounter >= 3) 242 { 243 if (IsValidPointer(m_pColorFrameBuffer)) 244 { 245 RenderBG(m_iStatusModeLYCounter, m_iPixelCounter); 246 } 247 m_iPixelCounter += 4; 248 m_iTileCycleCounter -= 3; 249 250 if (m_iPixelCounter >= 160) 251 { 252 break; 253 } 254 } 255 } 256 } 257#endif 258 259 if (m_iStatusModeCounter >= 160 && !m_bScanLineTransfered) 260 { 261 ScanLine(m_iStatusModeLYCounter); 262 m_bScanLineTransfered = true; 263 } 264 265 if (m_iStatusModeCounter >= 172) 266 { 267 m_iPixelCounter = 0; 268 m_iStatusModeCounter -= 172; 269 m_iStatusMode = 0; 270 m_iTileCycleCounter = 0; 271 UpdateStatRegister(); 272 273 m_IRQ48Signal &= 0x08; 274 u8 stat = m_pMemory->Retrieve(0xFF41); 275 if (IsSetBit(stat, 3)) 276 { 277 if (!IsSetBit(m_IRQ48Signal, 3)) 278 { 279 m_pProcessor->RequestInterrupt(Processor::LCDSTAT_Interrupt); 280 } 281 m_IRQ48Signal = SetBit(m_IRQ48Signal, 0); 282 } 283 } 284 break; 285 } 286 } 287 } 288 // Screen disabled 289 else 290 { 291 if (m_iScreenEnableDelayCycles > 0) 292 { 293 m_iScreenEnableDelayCycles -= clockCycles; 294 295 if (m_iScreenEnableDelayCycles <= 0) 296 { 297 m_iScreenEnableDelayCycles = 0; 298 m_bScreenEnabled = true; 299 m_iHideFrames = 3; 300 m_iStatusMode = 0; 301 m_iStatusModeCounter = 0; 302 m_iStatusModeCounterAux = 0; 303 m_iStatusModeLYCounter = 0; 304 m_iWindowLine = 0; 305 m_iStatusVBlankLine = 0; 306 m_iPixelCounter = 0; 307 m_iTileCycleCounter = 0; 308 m_pMemory->Load(0xFF44, m_iStatusModeLYCounter); 309 m_IRQ48Signal = 0; 310 311 u8 stat = m_pMemory->Retrieve(0xFF41); 312 if (IsSetBit(stat, 5)) 313 { 314 m_pProcessor->RequestInterrupt(Processor::LCDSTAT_Interrupt); 315 m_IRQ48Signal = SetBit(m_IRQ48Signal, 2); 316 } 317 318 CompareLYToLYC(); 319 } 320 } 321 else if (m_iStatusModeCounter >= 70224) 322 { 323 m_iStatusModeCounter -= 70224; 324 vblank = true; 325 } 326 } 327 return vblank; 328} 329 330void Video::EnableScreen() 331{ 332 if (!m_bScreenEnabled) 333 { 334 m_iScreenEnableDelayCycles = 244; 335 } 336} 337 338void Video::DisableScreen() 339{ 340 m_bScreenEnabled = false; 341 m_pMemory->Load(0xFF44, 0x00); 342 u8 stat = m_pMemory->Retrieve(0xFF41); 343 stat &= 0x7C; 344 m_pMemory->Load(0xFF41, stat); 345 m_iStatusMode = 0; 346 m_iStatusModeCounter = 0; 347 m_iStatusModeCounterAux = 0; 348 m_iStatusModeLYCounter = 0; 349 m_IRQ48Signal = 0; 350} 351 352bool Video::IsScreenEnabled() const 353{ 354 return m_bScreenEnabled; 355} 356 357const u8* Video::GetFrameBuffer() const 358{ 359 return m_pFrameBuffer; 360} 361 362void Video::UpdatePaletteToSpecification(bool background, u8 value) 363{ 364 bool hl = IsSetBit(value, 0); 365 int index = (value >> 1) & 0x03; 366 int pal = (value >> 3) & 0x07; 367 368 u16 color = (background ? m_CGBBackgroundPalettes[pal][index][0] : m_CGBSpritePalettes[pal][index][0]); 369 370 m_pMemory->Load(background ? 0xFF69 : 0xFF6B, hl ? (color >> 8) & 0xFF : color & 0xFF); 371} 372 373void Video::SetColorPalette(bool background, u8 value) 374{ 375 u8 ps = background ? m_pMemory->Retrieve(0xFF68) : m_pMemory->Retrieve(0xFF6A); 376 bool hl = IsSetBit(ps, 0); 377 int index = (ps >> 1) & 0x03; 378 int pal = (ps >> 3) & 0x07; 379 bool increment = IsSetBit(ps, 7); 380 381 if (increment) 382 { 383 u8 address = ps & 0x3F; 384 address++; 385 address &= 0x3F; 386 ps = (ps & 0x80) | address; 387 m_pMemory->Load(background ? 0xFF68 : 0xFF6A, ps); 388 UpdatePaletteToSpecification(background, ps); 389 } 390 391 u16* palette_color_gbc = background ? &m_CGBBackgroundPalettes[pal][index][0] : &m_CGBSpritePalettes[pal][index][0]; 392 u16* palette_color_final = background ? &m_CGBBackgroundPalettes[pal][index][1] : &m_CGBSpritePalettes[pal][index][1]; 393 394 *palette_color_gbc = hl ? (*palette_color_gbc & 0x00FF) | (value << 8) : (*palette_color_gbc & 0xFF00) | value; 395 396 u8 red_5bit = *palette_color_gbc & 0x1F; 397 u8 blue_5bit = (*palette_color_gbc >> 10) & 0x1F; 398 399 switch (m_pixelFormat) 400 { 401 case GB_PIXEL_RGB565: 402 { 403 u8 green_6bit = (*palette_color_gbc >> 4) & 0x3E; 404 *palette_color_final = (red_5bit << 11) | (green_6bit << 5) | blue_5bit; 405 break; 406 } 407 case GB_PIXEL_BGR565: 408 { 409 u8 green_6bit = (*palette_color_gbc >> 4) & 0x3E; 410 *palette_color_final = (blue_5bit << 11) | (green_6bit << 5) | red_5bit; 411 break; 412 } 413 case GB_PIXEL_RGB555: 414 { 415 u8 green_5bit = (*palette_color_gbc >> 5) & 0x1F; 416 *palette_color_final = 0x8000 | (red_5bit << 10) | (green_5bit << 5) | blue_5bit; 417 break; 418 } 419 case GB_PIXEL_BGR555: 420 { 421 u8 green_5bit = (*palette_color_gbc >> 5) & 0x1F; 422 *palette_color_final = 0x8000 | (blue_5bit << 10) | (green_5bit << 5) | red_5bit; 423 break; 424 } 425 } 426 427} 428 429int Video::GetCurrentStatusMode() const 430{ 431 return m_iStatusMode; 432} 433 434void Video::ResetWindowLine() 435{ 436 u8 wy = m_pMemory->Retrieve(0xFF4A); 437 438 if ((m_iWindowLine == 0) && (m_iStatusModeLYCounter < 144) && (m_iStatusModeLYCounter > wy)) 439 m_iWindowLine = 144; 440} 441 442void Video::ScanLine(int line) 443{ 444 if (IsValidPointer(m_pColorFrameBuffer)) 445 { 446 u8 lcdc = m_pMemory->Retrieve(0xFF40); 447 448 if (m_bScreenEnabled && IsSetBit(lcdc, 7)) 449 { 450#ifdef PERFORMANCE 451 RenderBG(line, 0); 452#endif 453 RenderWindow(line); 454 RenderSprites(line); 455 } 456 else 457 { 458 int line_width = (line * GAMEBOY_WIDTH); 459 if (m_bCGB) 460 { 461 for (int x = 0; x < GAMEBOY_WIDTH; x++) 462 m_pColorFrameBuffer[line_width + x] = 0x8000; 463 } 464 else 465 { 466 for (int x = 0; x < GAMEBOY_WIDTH; x++) 467 m_pFrameBuffer[line_width + x] = 0; 468 } 469 } 470 } 471} 472 473void Video::RenderBG(int line, int pixel) 474{ 475 u8 lcdc = m_pMemory->Retrieve(0xFF40); 476 int line_width = (line * GAMEBOY_WIDTH); 477 478 if (m_bCGB || IsSetBit(lcdc, 0)) 479 { 480#ifdef PERFORMANCE 481 int pixels_to_render = 160; 482#else 483 int pixels_to_render = 4; 484#endif 485 int offset_x_init = pixel & 0x7; 486 int offset_x_end = offset_x_init + pixels_to_render; 487 int screen_tile = pixel >> 3; 488 int tile_start_addr = IsSetBit(lcdc, 4) ? 0x8000 : 0x8800; 489 int map_start_addr = IsSetBit(lcdc, 3) ? 0x9C00 : 0x9800; 490 u8 scroll_x = m_pMemory->Retrieve(0xFF43); 491 u8 scroll_y = m_pMemory->Retrieve(0xFF42); 492 u8 line_scrolled = line + scroll_y; 493 int line_scrolled_32 = (line_scrolled >> 3) << 5; 494 int tile_pixel_y = line_scrolled & 0x7; 495 int tile_pixel_y_2 = tile_pixel_y << 1; 496 int tile_pixel_y_flip_2 = (7 - tile_pixel_y) << 1; 497 u8 palette = m_pMemory->Retrieve(0xFF47); 498 499 for (int offset_x = offset_x_init; offset_x < offset_x_end; offset_x++) 500 { 501 int screen_pixel_x = (screen_tile << 3) + offset_x; 502 u8 map_pixel_x = screen_pixel_x + scroll_x; 503 int map_tile_x = map_pixel_x >> 3; 504 int map_tile_offset_x = map_pixel_x & 0x7; 505 u16 map_tile_addr = map_start_addr + line_scrolled_32 + map_tile_x; 506 int map_tile = 0; 507 508 if (tile_start_addr == 0x8800) 509 { 510 map_tile = static_cast<s8> (m_pMemory->Retrieve(map_tile_addr)); 511 map_tile += 128; 512 } 513 else 514 { 515 map_tile = m_pMemory->Retrieve(map_tile_addr); 516 } 517 518 u8 cgb_tile_attr = m_bCGB ? m_pMemory->ReadCGBLCDRAM(map_tile_addr, true) : 0; 519 u8 cgb_tile_pal = m_bCGB ? (cgb_tile_attr & 0x07) : 0; 520 bool cgb_tile_bank = m_bCGB ? IsSetBit(cgb_tile_attr, 3) : false; 521 bool cgb_tile_xflip = m_bCGB ? IsSetBit(cgb_tile_attr, 5) : false; 522 bool cgb_tile_yflip = m_bCGB ? IsSetBit(cgb_tile_attr, 6) : false; 523 int map_tile_16 = map_tile << 4; 524 u8 byte1 = 0; 525 u8 byte2 = 0; 526 int final_pixely_2 = cgb_tile_yflip ? tile_pixel_y_flip_2 : tile_pixel_y_2; 527 int tile_address = tile_start_addr + map_tile_16 + final_pixely_2; 528 529 if (cgb_tile_bank) 530 { 531 byte1 = m_pMemory->ReadCGBLCDRAM(tile_address, true); 532 byte2 = m_pMemory->ReadCGBLCDRAM(tile_address + 1, true); 533 } 534 else 535 { 536 byte1 = m_pMemory->Retrieve(tile_address); 537 byte2 = m_pMemory->Retrieve(tile_address + 1); 538 } 539 540 int pixel_x_in_tile = map_tile_offset_x; 541 542 if (cgb_tile_xflip) 543 { 544 pixel_x_in_tile = 7 - pixel_x_in_tile; 545 } 546 int pixel_x_in_tile_bit = 0x1 << (7 - pixel_x_in_tile); 547 int pixel_data = (byte1 & pixel_x_in_tile_bit) ? 1 : 0; 548 pixel_data |= (byte2 & pixel_x_in_tile_bit) ? 2 : 0; 549 550 int index = line_width + screen_pixel_x; 551 m_pColorCacheBuffer[index] = pixel_data & 0x03; 552 553 if (m_bCGB) 554 { 555 bool cgb_tile_priority = IsSetBit(cgb_tile_attr, 7) && IsSetBit(lcdc, 0); 556 if (cgb_tile_priority && (pixel_data != 0)) 557 m_pColorCacheBuffer[index] = SetBit(m_pColorCacheBuffer[index], 2); 558 m_pColorFrameBuffer[index] = m_CGBBackgroundPalettes[cgb_tile_pal][pixel_data][1]; 559 } 560 else 561 { 562 u8 color = (palette >> (pixel_data << 1)) & 0x03; 563 m_pColorFrameBuffer[index] = m_pFrameBuffer[index] = color; 564 } 565 } 566 } 567 else 568 { 569 for (int x = 0; x < 4; x++) 570 { 571 int position = line_width + pixel + x; 572 m_pFrameBuffer[position] = 0; 573 m_pColorCacheBuffer[position] = 0; 574 } 575 } 576} 577 578void Video::RenderWindow(int line) 579{ 580 if (m_iWindowLine > 143) 581 return; 582 583 u8 lcdc = m_pMemory->Retrieve(0xFF40); 584 if (!IsSetBit(lcdc, 5)) 585 return; 586 587 int wx = m_pMemory->Retrieve(0xFF4B) - 7; 588 if (wx > 159) 589 return; 590 591 u8 wy = m_pMemory->Retrieve(0xFF4A); 592 if ((wy > 143) || (wy > line)) 593 return; 594 595 int tiles = IsSetBit(lcdc, 4) ? 0x8000 : 0x8800; 596 int map = IsSetBit(lcdc, 6) ? 0x9C00 : 0x9800; 597 int lineAdjusted = m_iWindowLine; 598 int y_32 = (lineAdjusted >> 3) << 5; 599 int pixely = lineAdjusted & 0x7; 600 int pixely_2 = pixely << 1; 601 int pixely_2_flip = (7 - pixely) << 1; 602 int line_width = (line * GAMEBOY_WIDTH); 603 u8 palette = m_pMemory->Retrieve(0xFF47); 604 605 for (int x = 0; x < 32; x++) 606 { 607 int tile = 0; 608 609 if (tiles == 0x8800) 610 { 611 tile = static_cast<s8> (m_pMemory->Retrieve(map + y_32 + x)); 612 tile += 128; 613 } 614 else 615 { 616 tile = m_pMemory->Retrieve(map + y_32 + x); 617 } 618 619 u8 cgb_tile_attr = m_bCGB ? m_pMemory->ReadCGBLCDRAM(map + y_32 + x, true) : 0; 620 u8 cgb_tile_pal = m_bCGB ? (cgb_tile_attr & 0x07) : 0; 621 bool cgb_tile_bank = m_bCGB ? IsSetBit(cgb_tile_attr, 3) : false; 622 bool cgb_tile_xflip = m_bCGB ? IsSetBit(cgb_tile_attr, 5) : false; 623 bool cgb_tile_yflip = m_bCGB ? IsSetBit(cgb_tile_attr, 6) : false; 624 int mapOffsetX = x << 3; 625 int tile_16 = tile << 4; 626 u8 byte1 = 0; 627 u8 byte2 = 0; 628 int final_pixely_2 = (m_bCGB && cgb_tile_yflip) ? pixely_2_flip : pixely_2; 629 int tile_address = tiles + tile_16 + final_pixely_2; 630 631 if (m_bCGB && cgb_tile_bank) 632 { 633 byte1 = m_pMemory->ReadCGBLCDRAM(tile_address, true); 634 byte2 = m_pMemory->ReadCGBLCDRAM(tile_address + 1, true); 635 } 636 else 637 { 638 byte1 = m_pMemory->Retrieve(tile_address); 639 byte2 = m_pMemory->Retrieve(tile_address + 1); 640 } 641 642 for (int pixelx = 0; pixelx < 8; pixelx++) 643 { 644 int bufferX = (mapOffsetX + pixelx + wx); 645 646 if (bufferX < 0 || bufferX >= GAMEBOY_WIDTH) 647 continue; 648 649 int pixelx_pos = pixelx; 650 651 if (m_bCGB && cgb_tile_xflip) 652 { 653 pixelx_pos = 7 - pixelx_pos; 654 } 655 656 int pixel = (byte1 & (0x1 << (7 - pixelx_pos))) ? 1 : 0; 657 pixel |= (byte2 & (0x1 << (7 - pixelx_pos))) ? 2 : 0; 658 659 int position = line_width + bufferX; 660 m_pColorCacheBuffer[position] = pixel & 0x03; 661 662 if (m_bCGB) 663 { 664 bool cgb_tile_priority = IsSetBit(cgb_tile_attr, 7) && IsSetBit(lcdc, 0); 665 if (cgb_tile_priority && (pixel != 0)) 666 m_pColorCacheBuffer[position] = SetBit(m_pColorCacheBuffer[position], 2); 667 m_pColorFrameBuffer[position] = m_CGBBackgroundPalettes[cgb_tile_pal][pixel][1]; 668 } 669 else 670 { 671 u8 color = (palette >> (pixel << 1)) & 0x03; 672 m_pColorFrameBuffer[position] = m_pFrameBuffer[position] = color; 673 } 674 } 675 } 676 m_iWindowLine++; 677} 678 679void Video::RenderSprites(int line) 680{ 681 u8 lcdc = m_pMemory->Retrieve(0xFF40); 682 683 if (!IsSetBit(lcdc, 1)) 684 return; 685 686 int sprite_height = IsSetBit(lcdc, 2) ? 16 : 8; 687 int line_width = (line * GAMEBOY_WIDTH); 688 689 bool visible_sprites[40]; 690 int sprite_limit = 0; 691 692 for (int sprite = 0; sprite < 40; sprite++) 693 { 694 int sprite_4 = sprite << 2; 695 int sprite_y = m_pMemory->Retrieve(0xFE00 + sprite_4) - 16; 696 697 if ((sprite_y > line) || ((sprite_y + sprite_height) <= line)) 698 { 699 visible_sprites[sprite] = false; 700 continue; 701 } 702 703 sprite_limit++; 704 705 visible_sprites[sprite] = sprite_limit <= 10; 706 } 707 708 for (int sprite = 39; sprite >= 0; sprite--) 709 { 710 if (!visible_sprites[sprite]) 711 continue; 712 713 int sprite_4 = sprite << 2; 714 int sprite_x = m_pMemory->Retrieve(0xFE00 + sprite_4 + 1) - 8; 715 716 if ((sprite_x < -7) || (sprite_x >= GAMEBOY_WIDTH)) 717 continue; 718 719 int sprite_y = m_pMemory->Retrieve(0xFE00 + sprite_4) - 16; 720 int sprite_tile_16 = (m_pMemory->Retrieve(0xFE00 + sprite_4 + 2) 721 & ((sprite_height == 16) ? 0xFE : 0xFF)) << 4; 722 u8 sprite_flags = m_pMemory->Retrieve(0xFE00 + sprite_4 + 3); 723 int sprite_pallette = IsSetBit(sprite_flags, 4) ? 1 : 0; 724 u8 palette = m_pMemory->Retrieve(sprite_pallette ? 0xFF49 : 0xFF48); 725 bool xflip = IsSetBit(sprite_flags, 5); 726 bool yflip = IsSetBit(sprite_flags, 6); 727 bool aboveBG = (!IsSetBit(sprite_flags, 7)); 728 bool cgb_tile_bank = IsSetBit(sprite_flags, 3); 729 int cgb_tile_pal = sprite_flags & 0x07; 730 int tiles = 0x8000; 731 int pixel_y = yflip ? ((sprite_height == 16) ? 15 : 7) - (line - sprite_y) : line - sprite_y; 732 u8 byte1 = 0; 733 u8 byte2 = 0; 734 int pixel_y_2 = 0; 735 int offset = 0; 736 737 if (sprite_height == 16 && (pixel_y >= 8)) 738 { 739 pixel_y_2 = (pixel_y - 8) << 1; 740 offset = 16; 741 } 742 else 743 pixel_y_2 = pixel_y << 1; 744 745 int tile_address = tiles + sprite_tile_16 + pixel_y_2 + offset; 746 747 if (m_bCGB && cgb_tile_bank) 748 { 749 byte1 = m_pMemory->ReadCGBLCDRAM(tile_address, true); 750 byte2 = m_pMemory->ReadCGBLCDRAM(tile_address + 1, true); 751 } 752 else 753 { 754 byte1 = m_pMemory->Retrieve(tile_address); 755 byte2 = m_pMemory->Retrieve(tile_address + 1); 756 } 757 758 for (int pixelx = 0; pixelx < 8; pixelx++) 759 { 760 int pixel = (byte1 & (0x01 << (xflip ? pixelx : 7 - pixelx))) ? 1 : 0; 761 pixel |= (byte2 & (0x01 << (xflip ? pixelx : 7 - pixelx))) ? 2 : 0; 762 763 if (pixel == 0) 764 continue; 765 766 int bufferX = (sprite_x + pixelx); 767 768 if (bufferX < 0 || bufferX >= GAMEBOY_WIDTH) 769 continue; 770 771 int position = line_width + bufferX; 772 u8 color_cache = m_pColorCacheBuffer[position]; 773 774 if (m_bCGB) 775 { 776 if (IsSetBit(color_cache, 2)) 777 continue; 778 } 779 else 780 { 781 int sprite_x_cache = m_pSpriteXCacheBuffer[position]; 782 if (IsSetBit(color_cache, 3) && (sprite_x_cache < sprite_x)) 783 continue; 784 } 785 786 if (!aboveBG && (color_cache & 0x03)) 787 continue; 788 789 m_pColorCacheBuffer[position] = SetBit(color_cache, 3); 790 m_pSpriteXCacheBuffer[position] = sprite_x; 791 if (m_bCGB) 792 { 793 m_pColorFrameBuffer[position] = m_CGBSpritePalettes[cgb_tile_pal][pixel][1]; 794 } 795 else 796 { 797 u8 color = (palette >> (pixel << 1)) & 0x03; 798 m_pColorFrameBuffer[position] = m_pFrameBuffer[position] = color; 799 } 800 } 801 } 802} 803 804void Video::UpdateStatRegister() 805{ 806 // Updates the STAT register with current mode 807 u8 stat = m_pMemory->Retrieve(0xFF41); 808 m_pMemory->Load(0xFF41, (stat & 0xFC) | (m_iStatusMode & 0x3)); 809} 810 811void Video::CompareLYToLYC() 812{ 813 if (m_bScreenEnabled) 814 { 815 u8 lyc = m_pMemory->Retrieve(0xFF45); 816 u8 stat = m_pMemory->Retrieve(0xFF41); 817 818 if (lyc == m_iStatusModeLYCounter) 819 { 820 stat = SetBit(stat, 2); 821 if (IsSetBit(stat, 6)) 822 { 823 if (m_IRQ48Signal == 0) 824 { 825 m_pProcessor->RequestInterrupt(Processor::LCDSTAT_Interrupt); 826 } 827 m_IRQ48Signal = SetBit(m_IRQ48Signal, 3); 828 } 829 } 830 else 831 { 832 stat = UnsetBit(stat, 2); 833 m_IRQ48Signal = UnsetBit(m_IRQ48Signal, 3); 834 } 835 836 m_pMemory->Load(0xFF41, stat); 837 } 838} 839 840u8 Video::GetIRQ48Signal() const 841{ 842 return m_IRQ48Signal; 843} 844 845void Video::SetIRQ48Signal(u8 signal) 846{ 847 m_IRQ48Signal = signal; 848} 849 850void Video::SaveState(std::ostream& stream) 851{ 852 using namespace std; 853 854 stream.write(reinterpret_cast<const char*> (m_pFrameBuffer), GAMEBOY_WIDTH * GAMEBOY_HEIGHT); 855 stream.write(reinterpret_cast<const char*> (m_pSpriteXCacheBuffer), sizeof(int) * GAMEBOY_WIDTH * GAMEBOY_HEIGHT); 856 stream.write(reinterpret_cast<const char*> (m_pColorCacheBuffer), GAMEBOY_WIDTH * GAMEBOY_HEIGHT); 857 stream.write(reinterpret_cast<const char*> (&m_iStatusMode), sizeof(m_iStatusMode)); 858 stream.write(reinterpret_cast<const char*> (&m_iStatusModeCounter), sizeof(m_iStatusModeCounter)); 859 stream.write(reinterpret_cast<const char*> (&m_iStatusModeCounterAux), sizeof(m_iStatusModeCounterAux)); 860 stream.write(reinterpret_cast<const char*> (&m_iStatusModeLYCounter), sizeof(m_iStatusModeLYCounter)); 861 stream.write(reinterpret_cast<const char*> (&m_iScreenEnableDelayCycles), sizeof(m_iScreenEnableDelayCycles)); 862 stream.write(reinterpret_cast<const char*> (&m_iStatusVBlankLine), sizeof(m_iStatusVBlankLine)); 863 stream.write(reinterpret_cast<const char*> (&m_iPixelCounter), sizeof(m_iPixelCounter)); 864 stream.write(reinterpret_cast<const char*> (&m_iTileCycleCounter), sizeof(m_iTileCycleCounter)); 865 stream.write(reinterpret_cast<const char*> (&m_bScreenEnabled), sizeof(m_bScreenEnabled)); 866 stream.write(reinterpret_cast<const char*> (m_CGBSpritePalettes), sizeof(m_CGBSpritePalettes)); 867 stream.write(reinterpret_cast<const char*> (m_CGBBackgroundPalettes), sizeof(m_CGBBackgroundPalettes)); 868 stream.write(reinterpret_cast<const char*> (&m_bScanLineTransfered), sizeof(m_bScanLineTransfered)); 869 stream.write(reinterpret_cast<const char*> (&m_iWindowLine), sizeof(m_iWindowLine)); 870 stream.write(reinterpret_cast<const char*> (&m_iHideFrames), sizeof(m_iHideFrames)); 871 stream.write(reinterpret_cast<const char*> (&m_IRQ48Signal), sizeof(m_IRQ48Signal)); 872} 873 874void Video::LoadState(std::istream& stream) 875{ 876 using namespace std; 877 878 stream.read(reinterpret_cast<char*> (m_pFrameBuffer), GAMEBOY_WIDTH * GAMEBOY_HEIGHT); 879 stream.read(reinterpret_cast<char*> (m_pSpriteXCacheBuffer), sizeof(int) * GAMEBOY_WIDTH * GAMEBOY_HEIGHT); 880 stream.read(reinterpret_cast<char*> (m_pColorCacheBuffer), GAMEBOY_WIDTH * GAMEBOY_HEIGHT); 881 stream.read(reinterpret_cast<char*> (&m_iStatusMode), sizeof(m_iStatusMode)); 882 stream.read(reinterpret_cast<char*> (&m_iStatusModeCounter), sizeof(m_iStatusModeCounter)); 883 stream.read(reinterpret_cast<char*> (&m_iStatusModeCounterAux), sizeof(m_iStatusModeCounterAux)); 884 stream.read(reinterpret_cast<char*> (&m_iStatusModeLYCounter), sizeof(m_iStatusModeLYCounter)); 885 stream.read(reinterpret_cast<char*> (&m_iScreenEnableDelayCycles), sizeof(m_iScreenEnableDelayCycles)); 886 stream.read(reinterpret_cast<char*> (&m_iStatusVBlankLine), sizeof(m_iStatusVBlankLine)); 887 stream.read(reinterpret_cast<char*> (&m_iPixelCounter), sizeof(m_iPixelCounter)); 888 stream.read(reinterpret_cast<char*> (&m_iTileCycleCounter), sizeof(m_iTileCycleCounter)); 889 stream.read(reinterpret_cast<char*> (&m_bScreenEnabled), sizeof(m_bScreenEnabled)); 890 stream.read(reinterpret_cast<char*> (m_CGBSpritePalettes), sizeof(m_CGBSpritePalettes)); 891 stream.read(reinterpret_cast<char*> (m_CGBBackgroundPalettes), sizeof(m_CGBBackgroundPalettes)); 892 stream.read(reinterpret_cast<char*> (&m_bScanLineTransfered), sizeof(m_bScanLineTransfered)); 893 stream.read(reinterpret_cast<char*> (&m_iWindowLine), sizeof(m_iWindowLine)); 894 stream.read(reinterpret_cast<char*> (&m_iHideFrames), sizeof(m_iHideFrames)); 895 stream.read(reinterpret_cast<char*> (&m_IRQ48Signal), sizeof(m_IRQ48Signal)); 896} 897 898PaletteMatrix Video::GetCGBBackgroundPalettes() 899{ 900 return &m_CGBBackgroundPalettes; 901} 902 903PaletteMatrix Video::GetCGBSpritePalettes() 904{ 905 return &m_CGBSpritePalettes; 906}