Memory.cpp (18034B)
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 <iostream> 21#include <fstream> 22#include "Memory.h" 23#include "Processor.h" 24#include "Video.h" 25 26Memory::Memory() 27{ 28 InitPointer(m_pProcessor); 29 InitPointer(m_pVideo); 30 InitPointer(m_pMap); 31 InitPointer(m_pDisassembledMap); 32 InitPointer(m_pDisassembledROMMap); 33 InitPointer(m_pWRAMBanks); 34 InitPointer(m_pLCDRAMBank1); 35 InitPointer(m_pCommonMemoryRule); 36 InitPointer(m_pIORegistersMemoryRule); 37 InitPointer(m_pCurrentMemoryRule); 38 InitPointer(m_pBootromDMG); 39 InitPointer(m_pBootromGBC); 40 m_bCGB = false; 41 m_iCurrentWRAMBank = 1; 42 m_iCurrentLCDRAMBank = 0; 43 m_bHDMAEnabled = false; 44 m_iHDMABytes = 0; 45 for (int i = 0; i < 5; i++) 46 m_HDMA[i] = 0; 47 m_HDMASource = 0; 48 m_HDMADestination = 0; 49 m_bBootromDMGEnabled = false; 50 m_bBootromGBCEnabled = false; 51 m_bBootromRegistryDisabled = false; 52 m_bBootromDMGLoaded = false; 53 m_bBootromGBCLoaded = false; 54} 55 56Memory::~Memory() 57{ 58 InitPointer(m_pProcessor); 59 InitPointer(m_pVideo); 60 SafeDeleteArray(m_pMap); 61 SafeDeleteArray(m_pWRAMBanks); 62 SafeDeleteArray(m_pLCDRAMBank1); 63 InitPointer(m_pCommonMemoryRule); 64 InitPointer(m_pIORegistersMemoryRule); 65 InitPointer(m_pCurrentMemoryRule); 66 SafeDeleteArray(m_pBootromDMG); 67 SafeDeleteArray(m_pBootromGBC); 68 69 if (IsValidPointer(m_pDisassembledROMMap)) 70 { 71 for (int i = 0; i < MAX_ROM_SIZE; i++) 72 { 73 SafeDelete(m_pDisassembledROMMap[i]); 74 } 75 SafeDeleteArray(m_pDisassembledROMMap); 76 } 77 78 if (IsValidPointer(m_pDisassembledMap)) 79 { 80 for (int i = 0; i < 65536; i++) 81 { 82 SafeDelete(m_pDisassembledMap[i]); 83 } 84 SafeDeleteArray(m_pDisassembledMap); 85 } 86} 87 88void Memory::SetProcessor(Processor* pProcessor) 89{ 90 m_pProcessor = pProcessor; 91} 92 93void Memory::SetVideo(Video* pVideo) 94{ 95 m_pVideo = pVideo; 96} 97 98void Memory::Init() 99{ 100 m_pMap = new u8[65536]; 101 m_pWRAMBanks = new u8[0x8000]; 102 m_pLCDRAMBank1 = new u8[0x2000]; 103 m_pBootromDMG = new u8[0x100]; 104 m_pBootromGBC = new u8[0x900]; 105#ifndef GEARBOY_DISABLE_DISASSEMBLER 106 m_pDisassembledMap = new stDisassembleRecord*[65536]; 107 for (int i = 0; i < 65536; i++) 108 { 109 InitPointer(m_pDisassembledMap[i]); 110 } 111 112 m_pDisassembledROMMap = new stDisassembleRecord*[MAX_ROM_SIZE]; 113 for (int i = 0; i < MAX_ROM_SIZE; i++) 114 { 115 InitPointer(m_pDisassembledROMMap[i]); 116 } 117#endif 118 m_BreakpointsCPU.clear(); 119 m_BreakpointsMem.clear(); 120 InitPointer(m_pRunToBreakpoint); 121 Reset(false); 122} 123 124void Memory::Reset(bool bCGB) 125{ 126 m_bCGB = bCGB; 127 InitPointer(m_pCommonMemoryRule); 128 InitPointer(m_pIORegistersMemoryRule); 129 InitPointer(m_pCurrentMemoryRule); 130 m_iCurrentWRAMBank = 1; 131 m_iCurrentLCDRAMBank = 0; 132 m_bHDMAEnabled = false; 133 m_iHDMABytes = 0; 134 m_bBootromRegistryDisabled = false; 135 136 if (IsBootromEnabled()) 137 ResetBootromDisassembledMemory(); 138 139 for (int i = 0; i < 65536; i++) 140 { 141 m_pMap[i] = 0x00; 142 143 if ((i >= 0x8000) && (i < 0xA000)) 144 { 145 m_pMap[i] = 0x00; 146 m_pLCDRAMBank1[i - 0x8000] = 0x00; 147 } 148 else if ((i >= 0xC000) && (i < 0xE000)) 149 { 150 if ((i & 0x8) ^((i & 0x800) >> 8)) 151 { 152 if (m_bCGB) 153 { 154 m_pMap[i] = 0x00; 155 if (i >= 0xD000) 156 { 157 for (int a = 0; a < 8; a++) 158 { 159 if (a != 2) 160 m_pWRAMBanks[(i - 0xD000) + (0x1000 * a)] = m_pMap[i - 0x1000]; 161 else 162 m_pWRAMBanks[(i - 0xD000) + (0x1000 * a)] = 0x00; 163 } 164 } 165 } 166 else 167 m_pMap[i] = 0x0f; 168 } 169 else 170 { 171 m_pMap[i] = 0xff; 172 if (i >= 0xD000) 173 { 174 for (int a = 0; a < 8; a++) 175 { 176 if (a != 2) 177 m_pWRAMBanks[(i - 0xD000) + (0x1000 * a)] = m_pMap[i - 0x1000]; 178 else 179 m_pWRAMBanks[(i - 0xD000) + (0x1000 * a)] = 0x00; 180 } 181 } 182 } 183 } 184 else if (i >= 0xFF00) 185 { 186 if (m_bCGB) 187 m_pMap[i] = kInitialValuesForColorFFXX[i - 0xFF00]; 188 else 189 m_pMap[i] = kInitialValuesForFFXX[i - 0xFF00]; 190 } 191 else 192 { 193 m_pMap[i] = 0xFF; 194 } 195 } 196 197 if (m_bCGB) 198 { 199 for (int i = 0; i < 5; i++) 200 { 201 m_HDMA[i] = m_pMap[0xFF51 + i]; 202 } 203 204 u8 hdma1 = m_HDMA[0]; 205 u8 hdma2 = m_HDMA[1]; 206 u8 hdma3 = m_HDMA[2]; 207 u8 hdma4 = m_HDMA[3]; 208 209 if (hdma1 > 0x7f && hdma1 < 0xa0) 210 hdma1 = 0; 211 212 m_HDMASource = (hdma1 << 8) | (hdma2 & 0xF0); 213 m_HDMADestination = ((hdma3 & 0x1F) << 8) | (hdma4 & 0xF0); 214 m_HDMADestination |= 0x8000; 215 } 216} 217 218void Memory::SetCurrentRule(MemoryRule* pRule) 219{ 220 m_pCurrentMemoryRule = pRule; 221} 222 223void Memory::SetCommonRule(CommonMemoryRule* pRule) 224{ 225 m_pCommonMemoryRule = pRule; 226} 227 228void Memory::SetIORule(IORegistersMemoryRule* pRule) 229{ 230 m_pIORegistersMemoryRule = pRule; 231} 232 233MemoryRule* Memory::GetCurrentRule() 234{ 235 return m_pCurrentMemoryRule; 236} 237 238u8* Memory::GetMemoryMap() 239{ 240 return m_pMap; 241} 242 243void Memory::LoadBank0and1FromROM(u8* pTheROM) 244{ 245 // loads the first 32KB only (bank 0 and 1) 246 for (int i = 0; i < 0x8000; i++) 247 { 248 m_pMap[i] = pTheROM[i]; 249 } 250} 251 252void Memory::MemoryDump(const char* szFilePath) 253{ 254 if (!IsValidPointer(m_pDisassembledMap)) 255 return; 256 257 using namespace std; 258 259 ofstream myfile(szFilePath, ios::out | ios::trunc); 260 261 if (myfile.is_open()) 262 { 263 for (int i = 0; i < 65536; i++) 264 { 265 if (IsValidPointer(m_pDisassembledMap[i]) && (m_pDisassembledMap[i]->name[0] != 0)) 266 { 267 myfile << "0x" << hex << i << "\t " << m_pDisassembledMap[i]->name << "\n"; 268 i += (m_pDisassembledMap[i]->size - 1); 269 } 270 else 271 { 272 myfile << "0x" << hex << i << "\t [0x" << hex << (int) m_pMap[i] << "]\n"; 273 } 274 } 275 276 myfile.close(); 277 } 278} 279 280void Memory::PerformDMA(u8 value) 281{ 282 if (m_bCGB) 283 { 284 u16 address = value << 8; 285 if (address < 0xE000) 286 { 287 if (address >= 0x8000 && address < 0xA000) 288 { 289 for (int i = 0; i < 0xA0; i++) 290 Load(0xFE00 + i, ReadCGBLCDRAM(address + i, false)); 291 } 292 else if (address >= 0xD000 && address < 0xE000) 293 { 294 for (int i = 0; i < 0xA0; i++) 295 Load(0xFE00 + i, ReadCGBWRAM(address + i)); 296 } 297 else 298 { 299 for (int i = 0; i < 0xA0; i++) 300 Load(0xFE00 + i, Read(address + i)); 301 } 302 } 303 } 304 else 305 { 306 u16 address = value << 8; 307 if (address >= 0x8000 && address < 0xE000) 308 { 309 for (int i = 0; i < 0xA0; i++) 310 Load(0xFE00 + i, Read(address + i)); 311 } 312 } 313} 314 315void Memory::SwitchCGBDMA(u8 value) 316{ 317 m_iHDMABytes = 16 + ((value & 0x7f) * 16); 318 319 if (m_bHDMAEnabled) 320 { 321 if (IsSetBit(value, 7)) 322 { 323 m_HDMA[4] = value & 0x7F; 324 } 325 else 326 { 327 m_HDMA[4] = 0xFF; 328 m_bHDMAEnabled = false; 329 } 330 } 331 else 332 { 333 if (IsSetBit(value, 7)) 334 { 335 m_bHDMAEnabled = true; 336 m_HDMA[4] = value & 0x7F; 337 if (m_pVideo->GetCurrentStatusMode() == 0) 338 { 339 m_pProcessor->AddCycles(PerformHDMA()); 340 } 341 } 342 else 343 { 344 PerformGDMA(value); 345 } 346 } 347} 348 349unsigned int Memory::PerformHDMA() 350{ 351 u16 source = m_HDMASource & 0xFFF0; 352 u16 destination = (m_HDMADestination & 0x1FF0) | 0x8000; 353 354 if (source >= 0xD000 && source < 0xE000) 355 { 356 for (int i = 0; i < 0x10; i++) 357 WriteCGBLCDRAM(destination + i, ReadCGBWRAM(source + i)); 358 } 359 else 360 { 361 for (int i = 0; i < 0x10; i++) 362 WriteCGBLCDRAM(destination + i, Read(source + i)); 363 } 364 365 m_HDMADestination += 0x10; 366 if (m_HDMADestination == 0xA000) 367 m_HDMADestination = 0x8000; 368 369 m_HDMASource += 0x10; 370 if (m_HDMASource == 0x8000) 371 m_HDMASource = 0xA000; 372 373 m_HDMA[1] = m_HDMASource & 0xFF; 374 m_HDMA[0] = m_HDMASource >> 8; 375 376 m_HDMA[3] = m_HDMADestination & 0xFF; 377 m_HDMA[2] = m_HDMADestination >> 8; 378 379 m_iHDMABytes -= 0x10; 380 m_HDMA[4]--; 381 382 if (m_HDMA[4] == 0xFF) 383 m_bHDMAEnabled = false; 384 385 // return clock cycles used 386 return (m_pProcessor->CGBSpeed() ? 17 : 9) * 4; 387} 388 389void Memory::PerformGDMA(u8 value) 390{ 391 u16 source = m_HDMASource & 0xFFF0; 392 u16 destination = (m_HDMADestination & 0x1FF0) | 0x8000; 393 394 if (source >= 0xD000 && source < 0xE000) 395 { 396 for (int i = 0; i < m_iHDMABytes; i++) 397 WriteCGBLCDRAM(destination + i, ReadCGBWRAM(source + i)); 398 } 399 else 400 { 401 for (int i = 0; i < m_iHDMABytes; i++) 402 WriteCGBLCDRAM(destination + i, Read(source + i)); 403 } 404 405 m_HDMADestination += m_iHDMABytes; 406 m_HDMASource += m_iHDMABytes; 407 408 for (int i = 0; i < 5; i++) 409 m_HDMA[i] = 0xFF; 410 411 int clock_cycles = 0; 412 413 if (m_pProcessor->CGBSpeed()) 414 clock_cycles = 2 + 16 * ((value & 0x7f) + 1); 415 else 416 clock_cycles = 1 + 8 * ((value & 0x7f) + 1); 417 418 m_pProcessor->AddCycles(clock_cycles * 4); 419} 420 421bool Memory::IsHDMAEnabled() const 422{ 423 return m_bHDMAEnabled; 424} 425 426void Memory::SetHDMARegister(int reg, u8 value) 427{ 428 switch (reg) 429 { 430 case 1: 431 { 432 // HDMA1 433 if (value > 0x7f && value < 0xa0) 434 value = 0; 435 m_HDMASource = (value << 8) | (m_HDMASource & 0xF0); 436 break; 437 } 438 case 2: 439 { 440 // HDMA2 441 value &= 0xF0; 442 m_HDMASource = (m_HDMASource & 0xFF00) | value; 443 break; 444 } 445 case 3: 446 { 447 // HDMA3 448 value &= 0x1F; 449 m_HDMADestination = (value << 8) | (m_HDMADestination & 0xF0); 450 m_HDMADestination |= 0x8000; 451 break; 452 } 453 case 4: 454 { 455 // HDMA4 456 value &= 0xF0; 457 m_HDMADestination = (m_HDMADestination & 0x1F00) | value; 458 m_HDMADestination |= 0x8000; 459 break; 460 } 461 } 462 463 m_HDMA[reg - 1] = value; 464} 465 466u8 Memory::GetHDMARegister(int reg) const 467{ 468 return m_HDMA[reg - 1]; 469} 470 471u8* Memory::GetCGBRAM() 472{ 473 return m_pWRAMBanks; 474} 475 476int Memory::GetCurrentCGBRAMBank() 477{ 478 return m_iCurrentWRAMBank; 479} 480 481int Memory::GetCurrentLCDRAMBank() 482{ 483 return m_iCurrentLCDRAMBank; 484} 485 486void Memory::SaveState(std::ostream& stream) 487{ 488 using namespace std; 489 490 stream.write(reinterpret_cast<const char*> (m_pMap), 65536); 491 stream.write(reinterpret_cast<const char*> (&m_iCurrentWRAMBank), sizeof(m_iCurrentWRAMBank)); 492 stream.write(reinterpret_cast<const char*> (&m_iCurrentLCDRAMBank), sizeof(m_iCurrentLCDRAMBank)); 493 stream.write(reinterpret_cast<const char*> (m_pWRAMBanks), 0x8000); 494 stream.write(reinterpret_cast<const char*> (m_pLCDRAMBank1), 0x2000); 495 stream.write(reinterpret_cast<const char*> (&m_bHDMAEnabled), sizeof(m_bHDMAEnabled)); 496 stream.write(reinterpret_cast<const char*> (&m_iHDMABytes), sizeof(m_iHDMABytes)); 497 stream.write(reinterpret_cast<const char*> (m_HDMA), sizeof(m_HDMA)); 498 stream.write(reinterpret_cast<const char*> (&m_HDMASource), sizeof(m_HDMASource)); 499 stream.write(reinterpret_cast<const char*> (&m_HDMADestination), sizeof(m_HDMADestination)); 500} 501 502void Memory::LoadState(std::istream& stream) 503{ 504 using namespace std; 505 506 stream.read(reinterpret_cast<char*> (m_pMap), 65536); 507 stream.read(reinterpret_cast<char*> (&m_iCurrentWRAMBank), sizeof(m_iCurrentWRAMBank)); 508 stream.read(reinterpret_cast<char*> (&m_iCurrentLCDRAMBank), sizeof(m_iCurrentLCDRAMBank)); 509 stream.read(reinterpret_cast<char*> (m_pWRAMBanks), 0x8000); 510 stream.read(reinterpret_cast<char*> (m_pLCDRAMBank1), 0x2000); 511 stream.read(reinterpret_cast<char*> (&m_bHDMAEnabled), sizeof(m_bHDMAEnabled)); 512 stream.read(reinterpret_cast<char*> (&m_iHDMABytes), sizeof(m_iHDMABytes)); 513 stream.read(reinterpret_cast<char*> (m_HDMA), sizeof(m_HDMA)); 514 stream.read(reinterpret_cast<char*> (&m_HDMASource), sizeof(m_HDMASource)); 515 stream.read(reinterpret_cast<char*> (&m_HDMADestination), sizeof(m_HDMADestination)); 516} 517 518u8* Memory::GetROM0() 519{ 520 return m_pCurrentMemoryRule->GetRomBank0(); 521} 522 523u8* Memory::GetROM1() 524{ 525 return m_pCurrentMemoryRule->GetCurrentRomBank1(); 526} 527 528u8* Memory::GetVRAM() 529{ 530 if (m_bCGB) 531 return (m_iCurrentLCDRAMBank == 1) ? m_pLCDRAMBank1 : m_pMap + 0x8000; 532 else 533 return m_pMap + 0x8000; 534} 535 536u8* Memory::GetRAM() 537{ 538 return m_pCurrentMemoryRule->GetCurrentRamBank(); 539} 540 541u8* Memory::GetWRAM0() 542{ 543 return m_bCGB ? m_pWRAMBanks : m_pMap + 0xC000; 544} 545 546u8* Memory::GetWRAM1() 547{ 548 return m_bCGB ? m_pWRAMBanks + (0x1000 * m_iCurrentWRAMBank) : m_pMap + 0xD000; 549} 550 551std::vector<Memory::stDisassembleRecord*>* Memory::GetBreakpointsCPU() 552{ 553 return &m_BreakpointsCPU; 554} 555 556std::vector<Memory::stMemoryBreakpoint>* Memory::GetBreakpointsMem() 557{ 558 return &m_BreakpointsMem; 559} 560 561Memory::stDisassembleRecord* Memory::GetRunToBreakpoint() 562{ 563 return m_pRunToBreakpoint; 564} 565 566void Memory::SetRunToBreakpoint(Memory::stDisassembleRecord* pBreakpoint) 567{ 568 m_pRunToBreakpoint = pBreakpoint; 569} 570 571void Memory::EnableBootromDMG(bool enable) 572{ 573 m_bBootromDMGEnabled = enable; 574 575 if (m_bBootromDMGEnabled) 576 { 577 Log("DMG Bootrom enabled"); 578 } 579 else 580 { 581 Log("DMG Bootrom disabled"); 582 } 583} 584 585void Memory::EnableBootromGBC(bool enable) 586{ 587 m_bBootromGBCEnabled = enable; 588 589 if (m_bBootromGBCEnabled) 590 { 591 Log("GBC Bootrom enabled"); 592 } 593 else 594 { 595 Log("GBC Bootrom disabled"); 596 } 597} 598 599void Memory::LoadBootromDMG(const char* szFilePath) 600{ 601 Log("Loading DMG Bootrom %s...", szFilePath); 602 603 LoadBootroom(szFilePath, false); 604} 605 606void Memory::LoadBootromGBC(const char* szFilePath) 607{ 608 Log("Loading GBC Bootrom %s...", szFilePath); 609 610 LoadBootroom(szFilePath, true); 611} 612 613bool Memory::IsBootromEnabled() 614{ 615 return (m_bBootromDMGEnabled && m_bBootromDMGLoaded && !m_bCGB) || (m_bBootromGBCEnabled && m_bBootromGBCLoaded && m_bCGB); 616} 617 618void Memory::DisableBootromRegistry() 619{ 620 if (!m_bBootromRegistryDisabled && IsBootromEnabled()) 621 { 622 ResetBootromDisassembledMemory(); 623 } 624 625 m_bBootromRegistryDisabled = true; 626} 627 628bool Memory::IsBootromRegistryEnabled() 629{ 630 return !m_bBootromRegistryDisabled; 631} 632 633void Memory::ResetDisassembledMemory() 634{ 635 #ifndef GEARBOY_DISABLE_DISASSEMBLER 636 637 if (IsValidPointer(m_pDisassembledROMMap)) 638 { 639 for (int i = 0; i < MAX_ROM_SIZE; i++) 640 { 641 SafeDelete(m_pDisassembledROMMap[i]); 642 } 643 } 644 if (IsValidPointer(m_pDisassembledMap)) 645 { 646 for (int i = 0; i < 65536; i++) 647 { 648 SafeDelete(m_pDisassembledMap[i]); 649 } 650 } 651 652 #endif 653} 654 655void Memory::ResetBootromDisassembledMemory() 656{ 657 #ifndef GEARBOY_DISABLE_DISASSEMBLER 658 659 m_BreakpointsCPU.clear(); 660 661 if (IsValidPointer(m_pDisassembledROMMap)) 662 { 663 for (int i = 0; i < 0x0100; i++) 664 { 665 SafeDelete(m_pDisassembledROMMap[i]); 666 } 667 } 668 if (IsValidPointer(m_pDisassembledMap)) 669 { 670 for (int i = 0; i < 0x0100; i++) 671 { 672 SafeDelete(m_pDisassembledMap[i]); 673 } 674 } 675 676 if (m_bCGB) 677 { 678 if (IsValidPointer(m_pDisassembledROMMap)) 679 { 680 for (int i = 0x0200; i < 0x0900; i++) 681 { 682 SafeDelete(m_pDisassembledROMMap[i]); 683 } 684 } 685 if (IsValidPointer(m_pDisassembledMap)) 686 { 687 for (int i = 0x0200; i < 0x0900; i++) 688 { 689 SafeDelete(m_pDisassembledMap[i]); 690 } 691 } 692 } 693 694 #endif 695} 696 697void Memory::LoadBootroom(const char* szFilePath, bool gbc) 698{ 699 using namespace std; 700 701 int expectedSize = gbc ? 0x900 : 0x100; 702 u8* bootrom = gbc ? m_pBootromGBC : m_pBootromDMG; 703 704 ifstream file(szFilePath, ios::in | ios::binary | ios::ate); 705 706 bool ret = false; 707 708 if (file.is_open()) 709 { 710 int size = static_cast<int> (file.tellg()); 711 712 if (size == expectedSize) 713 { 714 file.seekg(0, ios::beg); 715 file.read(reinterpret_cast<char*>(bootrom), size); 716 file.close(); 717 718 ret = true; 719 720 Log("Bootrom %s loaded", szFilePath); 721 } 722 else 723 { 724 Log("Incompatible bootrom size (expected 0x%X): 0x%X", expectedSize, size); 725 } 726 } 727 else 728 { 729 Log("There was a problem opening the file %s", szFilePath); 730 } 731 732 if (gbc) 733 m_bBootromGBCLoaded = ret; 734 else 735 m_bBootromDMGLoaded = ret; 736}