MBC3MemoryRule.cpp (13566B)
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 "MBC3MemoryRule.h" 21#include "Video.h" 22#include "Memory.h" 23#include "Processor.h" 24#include "Input.h" 25#include "Cartridge.h" 26 27MBC3MemoryRule::MBC3MemoryRule(Processor* pProcessor, 28 Memory* pMemory, Video* pVideo, Input* pInput, 29 Cartridge* pCartridge, Audio* pAudio) : MemoryRule(pProcessor, 30pMemory, pVideo, pInput, pCartridge, pAudio) 31{ 32 m_pRAMBanks = new u8[0x8000]; 33 Reset(false); 34} 35 36MBC3MemoryRule::~MBC3MemoryRule() 37{ 38 SafeDeleteArray(m_pRAMBanks); 39} 40 41void MBC3MemoryRule::Reset(bool bCGB) 42{ 43 m_bCGB = bCGB; 44 m_iCurrentRAMBank = 0; 45 m_iCurrentROMBank = 1; 46 m_bRamEnabled = false; 47 m_bRTCEnabled = false; 48 for (int i = 0; i < 0x8000; i++) 49 m_pRAMBanks[i] = 0xFF; 50 m_RTC.Seconds = 0; 51 m_RTC.Minutes = 0; 52 m_RTC.Hours = 0; 53 m_RTC.Days = 0; 54 m_RTC.Control = 0; 55 m_RTC.LatchedSeconds = 0; 56 m_RTC.LatchedMinutes = 0; 57 m_RTC.LatchedHours = 0; 58 m_RTC.LatchedDays = 0; 59 m_RTC.LatchedControl = 0; 60 m_RTC.LastTime = static_cast<s32>(m_pCartridge->GetCurrentRTC()); 61 m_RTC.padding = 0; 62 m_iRTCLatch = 0; 63 m_RTCRegister = 0; 64 m_RTCLastTimeCache = m_RTC.LastTime; 65 m_CurrentROMAddress = 0x4000; 66 m_CurrentRAMAddress = 0; 67} 68 69u8 MBC3MemoryRule::PerformRead(u16 address) 70{ 71 switch (address & 0xE000) 72 { 73 case 0x4000: 74 case 0x6000: 75 { 76 u8* pROM = m_pCartridge->GetTheROM(); 77 return pROM[(address - 0x4000) + m_CurrentROMAddress]; 78 } 79 case 0xA000: 80 { 81 if (m_iCurrentRAMBank >= 0) 82 { 83 if (m_bRamEnabled) 84 { 85 return m_pRAMBanks[(address - 0xA000) + m_CurrentRAMAddress]; 86 } 87 else 88 { 89 Log("--> ** Attempting to read from disabled ram %X", address); 90 return 0xFF; 91 } 92 } 93 else if (m_pCartridge->IsRTCPresent() && m_bRTCEnabled) 94 { 95 switch (m_RTCRegister) 96 { 97 case 0x08: 98 return m_RTC.LatchedSeconds; 99 break; 100 case 0x09: 101 return m_RTC.LatchedMinutes; 102 break; 103 case 0x0A: 104 return m_RTC.LatchedHours; 105 break; 106 case 0x0B: 107 return m_RTC.LatchedDays; 108 break; 109 case 0x0C: 110 return m_RTC.LatchedControl; 111 break; 112 default: 113 return 0xFF; 114 } 115 } 116 else 117 { 118 Log("--> ** Attempting to read from disabled RTC %X", address); 119 return 0xFF; 120 } 121 } 122 default: 123 { 124 return m_pMemory->Retrieve(address); 125 } 126 } 127} 128 129void MBC3MemoryRule::PerformWrite(u16 address, u8 value) 130{ 131 switch (address & 0xE000) 132 { 133 case 0x0000: 134 { 135 if (m_pCartridge->GetRAMSize() > 0) 136 { 137 bool previous = m_bRamEnabled; 138 m_bRamEnabled = ((value & 0x0F) == 0x0A); 139 140 if (IsValidPointer(m_pRamChangedCallback) && previous && !m_bRamEnabled) 141 { 142 (*m_pRamChangedCallback)(); 143 } 144 } 145 m_bRTCEnabled = ((value & 0x0F) == 0x0A); 146 break; 147 } 148 case 0x2000: 149 { 150 m_iCurrentROMBank = value & 0x7F; 151 if (m_iCurrentROMBank == 0) 152 m_iCurrentROMBank = 1; 153 m_iCurrentROMBank &= (m_pCartridge->GetROMBankCount() - 1); 154 m_CurrentROMAddress = m_iCurrentROMBank * 0x4000; 155 break; 156 } 157 case 0x4000: 158 { 159 if ((value >= 0x08) && (value <= 0x0C)) 160 { 161 // RTC 162 if (m_pCartridge->IsRTCPresent() && m_bRTCEnabled) 163 { 164 m_RTCRegister = value; 165 m_iCurrentRAMBank = -1; 166 } 167 else 168 { 169 Log("--> ** Attempting to select RTC register when RTC is disabled or not present %X %X", address, value); 170 } 171 } 172 else if (value <= 0x03) 173 { 174 m_iCurrentRAMBank = value; 175 m_iCurrentRAMBank &= (m_pCartridge->GetRAMBankCount() - 1); 176 m_CurrentRAMAddress = m_iCurrentRAMBank * 0x2000; 177 } 178 else 179 { 180 Log("--> ** Attempting to select unkwon register %X %X", address, value); 181 } 182 break; 183 } 184 case 0x6000: 185 { 186 if (m_pCartridge->IsRTCPresent()) 187 { 188 // RTC Latch 189 if ((m_iRTCLatch == 0x00) && (value == 0x01)) 190 { 191 UpdateRTC(); 192 m_RTC.LatchedSeconds = m_RTC.Seconds; 193 m_RTC.LatchedMinutes = m_RTC.Minutes; 194 m_RTC.LatchedHours = m_RTC.Hours; 195 m_RTC.LatchedDays = m_RTC.Days; 196 m_RTC.LatchedControl = m_RTC.Control; 197 } 198 199 m_iRTCLatch = value; 200 } 201 break; 202 } 203 case 0xA000: 204 { 205 if (m_iCurrentRAMBank >= 0) 206 { 207 if (m_bRamEnabled) 208 { 209 m_pRAMBanks[(address - 0xA000) + m_CurrentRAMAddress] = value; 210 } 211 else 212 { 213 Log("--> ** Attempting to write on RAM when ram is disabled %X %X", address, value); 214 } 215 } 216 else if (m_pCartridge->IsRTCPresent() && m_bRTCEnabled) 217 { 218 switch (m_RTCRegister) 219 { 220 case 0x08: 221 m_RTC.Seconds = value; 222 break; 223 case 0x09: 224 m_RTC.Minutes = value; 225 break; 226 case 0x0A: 227 m_RTC.Hours = value; 228 break; 229 case 0x0B: 230 m_RTC.Days = value; 231 break; 232 case 0x0C: 233 m_RTC.Control = (m_RTC.Control & 0x80) | (value & 0xC1); 234 break; 235 } 236 } 237 else 238 { 239 Log("--> ** Attempting to write on RTC when RTC is disabled or not present %X %X", address, value); 240 } 241 break; 242 } 243 default: 244 { 245 m_pMemory->Load(address, value); 246 break; 247 } 248 } 249} 250 251void MBC3MemoryRule::UpdateRTC() 252{ 253 s32 now = static_cast<s32>(m_pCartridge->GetCurrentRTC()); 254 255 if (!IsSetBit(m_RTC.Control, 6) && (m_RTCLastTimeCache != now)) 256 { 257 m_RTCLastTimeCache = now; 258 s32 difference = now - m_RTC.LastTime; 259 m_RTC.LastTime = now; 260 261 if (difference > 0) 262 { 263 m_RTC.Seconds += (s32) (difference % 60); 264 265 if (m_RTC.Seconds > 59) 266 { 267 m_RTC.Seconds -= 60; 268 m_RTC.Minutes++; 269 } 270 271 difference /= 60; 272 m_RTC.Minutes += (s32) (difference % 60); 273 274 if (m_RTC.Minutes > 59) 275 { 276 m_RTC.Minutes -= 60; 277 m_RTC.Hours++; 278 } 279 280 difference /= 60; 281 m_RTC.Hours += (s32) (difference % 24); 282 283 if (m_RTC.Hours > 23) 284 { 285 m_RTC.Hours -= 24; 286 m_RTC.Days++; 287 } 288 289 difference /= 24; 290 m_RTC.Days += (s32) (difference & 0xffffffff); 291 292 if (m_RTC.Days > 0xFF) 293 { 294 m_RTC.Control = (m_RTC.Control & 0xC1) | 0x01; 295 296 if (m_RTC.Days > 511) 297 { 298 m_RTC.Days %= 512; 299 m_RTC.Control |= 0x80; 300 m_RTC.Control &= 0xC0; 301 } 302 } 303 } 304 } 305} 306 307void MBC3MemoryRule::SaveRam(std::ostream & file) 308{ 309 Log("MBC3MemoryRule save RAM..."); 310 311 for (int i = 0; i < 0x8000; i++) 312 { 313 u8 ram_byte = m_pRAMBanks[i]; 314 file.write(reinterpret_cast<const char*> (&ram_byte), 1); 315 } 316 317 if (m_pCartridge->IsRTCPresent()) 318 { 319 file.write(reinterpret_cast<const char*> (&m_RTC), sizeof(m_RTC)); 320 } 321 322 Log("MBC3MemoryRule save RAM done"); 323} 324 325bool MBC3MemoryRule::LoadRam(std::istream & file, s32 fileSize) 326{ 327 Log("MBC3MemoryRule load RAM..."); 328 329 bool loadRTC = m_pCartridge->IsRTCPresent(); 330 331 if (fileSize > 0) 332 { 333 if (fileSize < 0x8000) 334 { 335 Log("MBC3MemoryRule incorrect RAM size. Expected: %d Found: %d", 0x8000, fileSize); 336 return false; 337 } 338 339 if (loadRTC) 340 { 341 s32 minExpectedSize = 0x8000 + 44; 342 s32 maxExpectedSize = 0x8000 + 48; 343 344 if ((fileSize != minExpectedSize) && (fileSize != maxExpectedSize)) 345 { 346 Log("MBC3MemoryRule incorrect RTC size. MinExpected: %d MaxExpected: %d Found: %d", minExpectedSize, maxExpectedSize, fileSize); 347 } 348 349 if (fileSize < minExpectedSize) 350 { 351 Log("MBC3MemoryRule ignoring RTC data"); 352 loadRTC = false; 353 } 354 } 355 } 356 357 for (int i = 0; i < 0x8000; i++) 358 { 359 u8 ram_byte = 0; 360 file.read(reinterpret_cast<char*> (&ram_byte), 1); 361 m_pRAMBanks[i] = ram_byte; 362 } 363 364 if (loadRTC) 365 { 366 file.read(reinterpret_cast<char*> (&m_RTC), 44); 367 } 368 369 Log("MBC3MemoryRule load RAM done"); 370 371 return true; 372} 373 374size_t MBC3MemoryRule::GetRamSize() 375{ 376 return 0x8000; 377} 378 379size_t MBC3MemoryRule::GetRTCSize() 380{ 381 return m_pCartridge->IsRTCPresent() ? sizeof(m_RTC) : 0; 382} 383 384u8* MBC3MemoryRule::GetRamBanks() 385{ 386 return m_pRAMBanks; 387} 388 389u8* MBC3MemoryRule::GetCurrentRamBank() 390{ 391 return m_pRAMBanks + m_CurrentRAMAddress; 392} 393 394int MBC3MemoryRule::GetCurrentRamBankIndex() 395{ 396 return m_iCurrentRAMBank > 0 ? m_iCurrentRAMBank : 0; 397} 398 399u8* MBC3MemoryRule::GetRomBank0() 400{ 401 return m_pMemory->GetMemoryMap() + 0x0000; 402} 403 404int MBC3MemoryRule::GetCurrentRomBank0Index() 405{ 406 return 0; 407} 408 409u8* MBC3MemoryRule::GetCurrentRomBank1() 410{ 411 u8* pROM = m_pCartridge->GetTheROM(); 412 return &pROM[m_CurrentROMAddress]; 413} 414 415int MBC3MemoryRule::GetCurrentRomBank1Index() 416{ 417 return m_iCurrentROMBank; 418} 419 420u8* MBC3MemoryRule::GetRTCMemory() 421{ 422 return m_pCartridge->IsRTCPresent() ? reinterpret_cast<u8*>(&m_RTC) : NULL; 423} 424 425void MBC3MemoryRule::SaveState(std::ostream& stream) 426{ 427 using namespace std; 428 429 stream.write(reinterpret_cast<const char*> (&m_iCurrentRAMBank), sizeof(m_iCurrentRAMBank)); 430 stream.write(reinterpret_cast<const char*> (&m_iCurrentROMBank), sizeof(m_iCurrentROMBank)); 431 stream.write(reinterpret_cast<const char*> (&m_bRamEnabled), sizeof(m_bRamEnabled)); 432 stream.write(reinterpret_cast<const char*> (&m_bRTCEnabled), sizeof(m_bRTCEnabled)); 433 stream.write(reinterpret_cast<const char*> (m_pRAMBanks), 0x8000); 434 stream.write(reinterpret_cast<const char*> (&m_iRTCLatch), sizeof(m_iRTCLatch)); 435 stream.write(reinterpret_cast<const char*> (&m_RTCRegister), sizeof(m_RTCRegister)); 436 stream.write(reinterpret_cast<const char*> (&m_RTCLastTimeCache), sizeof(m_RTCLastTimeCache)); 437 stream.write(reinterpret_cast<const char*> (&m_CurrentROMAddress), sizeof(m_CurrentROMAddress)); 438 stream.write(reinterpret_cast<const char*> (&m_CurrentRAMAddress), sizeof(m_CurrentRAMAddress)); 439 stream.write(reinterpret_cast<const char*> (&m_RTC), sizeof(m_RTC)); 440} 441 442void MBC3MemoryRule::LoadState(std::istream& stream) 443{ 444 using namespace std; 445 446 stream.read(reinterpret_cast<char*> (&m_iCurrentRAMBank), sizeof(m_iCurrentRAMBank)); 447 stream.read(reinterpret_cast<char*> (&m_iCurrentROMBank), sizeof(m_iCurrentROMBank)); 448 stream.read(reinterpret_cast<char*> (&m_bRamEnabled), sizeof(m_bRamEnabled)); 449 stream.read(reinterpret_cast<char*> (&m_bRTCEnabled), sizeof(m_bRTCEnabled)); 450 stream.read(reinterpret_cast<char*> (m_pRAMBanks), 0x8000); 451 stream.read(reinterpret_cast<char*> (&m_iRTCLatch), sizeof(m_iRTCLatch)); 452 stream.read(reinterpret_cast<char*> (&m_RTCRegister), sizeof(m_RTCRegister)); 453 stream.read(reinterpret_cast<char*> (&m_RTCLastTimeCache), sizeof(m_RTCLastTimeCache)); 454 stream.read(reinterpret_cast<char*> (&m_CurrentROMAddress), sizeof(m_CurrentROMAddress)); 455 stream.read(reinterpret_cast<char*> (&m_CurrentRAMAddress), sizeof(m_CurrentRAMAddress)); 456 stream.read(reinterpret_cast<char*> (&m_RTC), sizeof(m_RTC)); 457}