mirror of
https://github.com/libretro/gambatte-libretro.git
synced 2024-11-27 01:40:23 +00:00
Backport 'refactor ancient mbc code somewhat. '
This commit is contained in:
parent
363679c20d
commit
43b5e83faf
@ -1328,7 +1328,6 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
|
||||
|
||||
state.rtc.baseTime = std::time(0);
|
||||
state.rtc.haltTime = state.rtc.baseTime;
|
||||
state.rtc.index = 5;
|
||||
state.rtc.dataDh = 0;
|
||||
state.rtc.dataDl = 0;
|
||||
state.rtc.dataH = 0;
|
||||
|
@ -24,17 +24,11 @@
|
||||
namespace gambatte
|
||||
{
|
||||
|
||||
Cartridge::Cartridge()
|
||||
: rombank_(1),
|
||||
rambank_(0),
|
||||
enableRam_(false),
|
||||
rambankMode_(false),
|
||||
multi64rom_(false)
|
||||
static unsigned toMulti64Rombank(const unsigned rombank)
|
||||
{
|
||||
return (rombank >> 1 & 0x30) | (rombank & 0xF);
|
||||
}
|
||||
|
||||
enum Cartridgetype { PLAIN, MBC1, MBC2, MBC3, MBC5 };
|
||||
|
||||
static inline unsigned rambanks(MemPtrs const &memptrs)
|
||||
{
|
||||
return (memptrs.rambankdataend() - memptrs.rambankdata()) / 0x2000;
|
||||
@ -45,13 +39,350 @@ namespace gambatte
|
||||
return (memptrs.romdataend() - memptrs.romdata() ) / 0x4000;
|
||||
}
|
||||
|
||||
static unsigned adjustedRombank(unsigned bank, const Cartridgetype romtype)
|
||||
{
|
||||
if ((romtype == MBC1 && !(bank & 0x1F)) || (romtype == MBC5 && !bank))
|
||||
++bank;
|
||||
class DefaultMbc : public Mbc {
|
||||
public:
|
||||
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
|
||||
return (addr< 0x4000) == (bank == 0);
|
||||
}
|
||||
};
|
||||
class Mbc0 : public DefaultMbc {
|
||||
MemPtrs &memptrs;
|
||||
bool enableRam;
|
||||
public:
|
||||
explicit Mbc0(MemPtrs &memptrs)
|
||||
: memptrs(memptrs),
|
||||
enableRam(false)
|
||||
{
|
||||
}
|
||||
virtual void romWrite(const unsigned P, const unsigned data) {
|
||||
if (P < 0x2000) {
|
||||
enableRam = (data & 0xF) == 0xA;
|
||||
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
|
||||
}
|
||||
}
|
||||
virtual void saveState(SaveState::Mem &ss) const {
|
||||
ss.enableRam = enableRam;
|
||||
}
|
||||
virtual void loadState(const SaveState::Mem &ss) {
|
||||
enableRam = ss.enableRam;
|
||||
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
return bank;
|
||||
}
|
||||
class Mbc1 : public DefaultMbc {
|
||||
MemPtrs &memptrs;
|
||||
unsigned char rombank;
|
||||
unsigned char rambank;
|
||||
bool enableRam;
|
||||
bool rambankMode;
|
||||
static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; }
|
||||
void setRambank() const { memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, rambank & (rambanks(memptrs) - 1)); }
|
||||
void setRombank() const { memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1))); }
|
||||
public:
|
||||
explicit Mbc1(MemPtrs &memptrs)
|
||||
: memptrs(memptrs),
|
||||
rombank(1),
|
||||
rambank(0),
|
||||
enableRam(false),
|
||||
rambankMode(false)
|
||||
{
|
||||
}
|
||||
virtual void romWrite(const unsigned P, const unsigned data) {
|
||||
switch (P >> 13 & 3) {
|
||||
case 0:
|
||||
enableRam = (data & 0xF) == 0xA;
|
||||
setRambank();
|
||||
break;
|
||||
case 1:
|
||||
rombank = rambankMode ? data & 0x1F : (rombank & 0x60) | (data & 0x1F);
|
||||
setRombank();
|
||||
break;
|
||||
case 2:
|
||||
if (rambankMode) {
|
||||
rambank = data & 3;
|
||||
setRambank();
|
||||
} else {
|
||||
rombank = (data << 5 & 0x60) | (rombank & 0x1F);
|
||||
setRombank();
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
// Pretty sure this should take effect immediately, but I have a policy not to change old behavior
|
||||
// unless I have something (eg. a verified test or a game) that justifies it.
|
||||
rambankMode = data & 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
virtual void saveState(SaveState::Mem &ss) const {
|
||||
ss.rombank = rombank;
|
||||
ss.rambank = rambank;
|
||||
ss.enableRam = enableRam;
|
||||
ss.rambankMode = rambankMode;
|
||||
}
|
||||
virtual void loadState(const SaveState::Mem &ss) {
|
||||
rombank = ss.rombank;
|
||||
rambank = ss.rambank;
|
||||
enableRam = ss.enableRam;
|
||||
rambankMode = ss.rambankMode;
|
||||
setRambank();
|
||||
setRombank();
|
||||
}
|
||||
};
|
||||
class Mbc1Multi64 : public Mbc {
|
||||
MemPtrs &memptrs;
|
||||
unsigned char rombank;
|
||||
bool enableRam;
|
||||
bool rombank0Mode;
|
||||
static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; }
|
||||
void setRombank() const {
|
||||
if (rombank0Mode) {
|
||||
const unsigned rb = toMulti64Rombank(rombank);
|
||||
memptrs.setRombank0(rb & 0x30);
|
||||
memptrs.setRombank(adjustedRombank(rb));
|
||||
} else {
|
||||
memptrs.setRombank0(0);
|
||||
memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1)));
|
||||
}
|
||||
}
|
||||
public:
|
||||
explicit Mbc1Multi64(MemPtrs &memptrs)
|
||||
: memptrs(memptrs),
|
||||
rombank(1),
|
||||
enableRam(false),
|
||||
rombank0Mode(false)
|
||||
{
|
||||
}
|
||||
virtual void romWrite(const unsigned P, const unsigned data) {
|
||||
switch (P >> 13 & 3) {
|
||||
case 0:
|
||||
enableRam = (data & 0xF) == 0xA;
|
||||
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
|
||||
break;
|
||||
case 1:
|
||||
rombank = (rombank & 0x60) | (data & 0x1F);
|
||||
memptrs.setRombank(adjustedRombank(rombank0Mode ? toMulti64Rombank(rombank) : rombank & (rombanks(memptrs) - 1)));
|
||||
break;
|
||||
case 2:
|
||||
rombank = (data << 5 & 0x60) | (rombank & 0x1F);
|
||||
setRombank();
|
||||
break;
|
||||
case 3:
|
||||
rombank0Mode = data & 1;
|
||||
setRombank();
|
||||
break;
|
||||
}
|
||||
}
|
||||
virtual void saveState(SaveState::Mem &ss) const {
|
||||
ss.rombank = rombank;
|
||||
ss.enableRam = enableRam;
|
||||
ss.rambankMode = rombank0Mode;
|
||||
}
|
||||
virtual void loadState(const SaveState::Mem &ss) {
|
||||
rombank = ss.rombank;
|
||||
enableRam = ss.enableRam;
|
||||
rombank0Mode = ss.rambankMode;
|
||||
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
|
||||
setRombank();
|
||||
}
|
||||
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
|
||||
return (addr < 0x4000) == ((bank & 0xF) == 0);
|
||||
}
|
||||
};
|
||||
class Mbc2 : public DefaultMbc {
|
||||
MemPtrs &memptrs;
|
||||
unsigned char rombank;
|
||||
bool enableRam;
|
||||
public:
|
||||
explicit Mbc2(MemPtrs &memptrs)
|
||||
: memptrs(memptrs),
|
||||
rombank(1),
|
||||
enableRam(false)
|
||||
{
|
||||
}
|
||||
virtual void romWrite(const unsigned P, const unsigned data) {
|
||||
switch (P & 0x6100) {
|
||||
case 0x0000:
|
||||
enableRam = (data & 0xF) == 0xA;
|
||||
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
|
||||
break;
|
||||
case 0x2100:
|
||||
rombank = data & 0xF;
|
||||
memptrs.setRombank(rombank & (rombanks(memptrs) - 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
virtual void saveState(SaveState::Mem &ss) const {
|
||||
ss.rombank = rombank;
|
||||
ss.enableRam = enableRam;
|
||||
}
|
||||
virtual void loadState(const SaveState::Mem &ss) {
|
||||
rombank = ss.rombank;
|
||||
enableRam = ss.enableRam;
|
||||
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
|
||||
memptrs.setRombank(rombank & (rombanks(memptrs) - 1));
|
||||
}
|
||||
};
|
||||
class Mbc3 : public DefaultMbc {
|
||||
MemPtrs &memptrs;
|
||||
Rtc *const rtc;
|
||||
unsigned char rombank;
|
||||
unsigned char rambank;
|
||||
bool enableRam;
|
||||
void setRambank() const {
|
||||
unsigned flags = enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0;
|
||||
if (rtc) {
|
||||
rtc->set(enableRam, rambank);
|
||||
if (rtc->getActive())
|
||||
flags |= MemPtrs::RTC_EN;
|
||||
}
|
||||
memptrs.setRambank(flags, rambank & (rambanks(memptrs) - 1));
|
||||
}
|
||||
public:
|
||||
Mbc3(MemPtrs &memptrs, Rtc *const rtc)
|
||||
: memptrs(memptrs),
|
||||
rtc(rtc),
|
||||
rombank(1),
|
||||
rambank(0),
|
||||
enableRam(false)
|
||||
{
|
||||
}
|
||||
virtual void romWrite(const unsigned P, const unsigned data) {
|
||||
switch (P >> 13 & 3) {
|
||||
case 0:
|
||||
enableRam = (data & 0xF) == 0xA;
|
||||
setRambank();
|
||||
break;
|
||||
case 1:
|
||||
rombank = data & 0x7F;
|
||||
memptrs.setRombank(rombank & (rombanks(memptrs) - 1));
|
||||
break;
|
||||
case 2:
|
||||
rambank = data;
|
||||
setRambank();
|
||||
break;
|
||||
case 3:
|
||||
if (rtc)
|
||||
rtc->latch(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
virtual void saveState(SaveState::Mem &ss) const {
|
||||
ss.rombank = rombank;
|
||||
ss.rambank = rambank;
|
||||
ss.enableRam = enableRam;
|
||||
}
|
||||
virtual void loadState(const SaveState::Mem &ss) {
|
||||
rombank = ss.rombank;
|
||||
rambank = ss.rambank;
|
||||
enableRam = ss.enableRam;
|
||||
setRambank();
|
||||
memptrs.setRombank(rombank & (rombanks(memptrs) - 1));
|
||||
}
|
||||
};
|
||||
class HuC1 : public DefaultMbc {
|
||||
MemPtrs &memptrs;
|
||||
unsigned char rombank;
|
||||
unsigned char rambank;
|
||||
bool enableRam;
|
||||
bool rambankMode;
|
||||
void setRambank() const {
|
||||
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : MemPtrs::READ_EN,
|
||||
rambankMode ? rambank & (rambanks(memptrs) - 1) : 0);
|
||||
}
|
||||
void setRombank() const { memptrs.setRombank((rambankMode ? rombank : rambank << 6 | rombank) & (rombanks(memptrs) - 1)); }
|
||||
public:
|
||||
explicit HuC1(MemPtrs &memptrs)
|
||||
: memptrs(memptrs),
|
||||
rombank(1),
|
||||
rambank(0),
|
||||
enableRam(false),
|
||||
rambankMode(false)
|
||||
{
|
||||
}
|
||||
virtual void romWrite(const unsigned P, const unsigned data) {
|
||||
switch (P >> 13 & 3) {
|
||||
case 0:
|
||||
enableRam = (data & 0xF) == 0xA;
|
||||
setRambank();
|
||||
break;
|
||||
case 1:
|
||||
rombank = data & 0x3F;
|
||||
setRombank();
|
||||
break;
|
||||
case 2:
|
||||
rambank = data & 3;
|
||||
rambankMode ? setRambank() : setRombank();
|
||||
break;
|
||||
case 3:
|
||||
rambankMode = data & 1;
|
||||
setRambank();
|
||||
setRombank();
|
||||
break;
|
||||
}
|
||||
}
|
||||
virtual void saveState(SaveState::Mem &ss) const {
|
||||
ss.rombank = rombank;
|
||||
ss.rambank = rambank;
|
||||
ss.enableRam = enableRam;
|
||||
ss.rambankMode = rambankMode;
|
||||
}
|
||||
virtual void loadState(const SaveState::Mem &ss) {
|
||||
rombank = ss.rombank;
|
||||
rambank = ss.rambank;
|
||||
enableRam = ss.enableRam;
|
||||
rambankMode = ss.rambankMode;
|
||||
setRambank();
|
||||
setRombank();
|
||||
}
|
||||
};
|
||||
class Mbc5 : public DefaultMbc {
|
||||
MemPtrs &memptrs;
|
||||
unsigned short rombank;
|
||||
unsigned char rambank;
|
||||
bool enableRam;
|
||||
static unsigned adjustedRombank(const unsigned bank) { return bank ? bank : bank | 1; }
|
||||
void setRambank() const { memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, rambank & (rambanks(memptrs) - 1)); }
|
||||
void setRombank() const { memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1))); }
|
||||
public:
|
||||
explicit Mbc5(MemPtrs &memptrs)
|
||||
: memptrs(memptrs),
|
||||
rombank(1),
|
||||
rambank(0),
|
||||
enableRam(false)
|
||||
{
|
||||
}
|
||||
virtual void romWrite(const unsigned P, const unsigned data) {
|
||||
switch (P >> 13 & 3) {
|
||||
case 0:
|
||||
enableRam = (data & 0xF) == 0xA;
|
||||
setRambank();
|
||||
break;
|
||||
case 1:
|
||||
rombank = P < 0x3000 ? (rombank & 0x100) | data
|
||||
: (data << 8 & 0x100) | (rombank & 0xFF);
|
||||
setRombank();
|
||||
break;
|
||||
case 2:
|
||||
rambank = data & 0xF;
|
||||
setRambank();
|
||||
break;
|
||||
case 3:
|
||||
break;
|
||||
}
|
||||
}
|
||||
virtual void saveState(SaveState::Mem &ss) const {
|
||||
ss.rombank = rombank;
|
||||
ss.rambank = rambank;
|
||||
ss.enableRam = enableRam;
|
||||
}
|
||||
virtual void loadState(const SaveState::Mem &ss) {
|
||||
rombank = ss.rombank;
|
||||
rambank = ss.rambank;
|
||||
enableRam = ss.enableRam;
|
||||
setRambank();
|
||||
setRombank();
|
||||
}
|
||||
};
|
||||
|
||||
static bool hasRtc(unsigned headerByte0x147)
|
||||
{
|
||||
@ -73,222 +404,14 @@ namespace gambatte
|
||||
|
||||
void Cartridge::saveState(SaveState &state) const
|
||||
{
|
||||
state.mem.rombank = rombank_;
|
||||
state.mem.rambank = rambank_;
|
||||
state.mem.enableRam = enableRam_;
|
||||
state.mem.rambankMode = rambankMode_;
|
||||
|
||||
mbc->saveState(state.mem);
|
||||
rtc_.saveState(state);
|
||||
}
|
||||
|
||||
static Cartridgetype cartridgeType(const unsigned headerByte0x147)
|
||||
{
|
||||
static const unsigned char typeLut[] = {
|
||||
/* [0xFF] = */ MBC1,
|
||||
/* [0x00] = */ PLAIN,
|
||||
/* [0x01] = */ MBC1,
|
||||
/* [0x02] = */ MBC1,
|
||||
/* [0x03] = */ MBC1,
|
||||
/* [0x04] = */ 0,
|
||||
/* [0x05] = */ MBC2,
|
||||
/* [0x06] = */ MBC2,
|
||||
/* [0x07] = */ 0,
|
||||
/* [0x08] = */ PLAIN,
|
||||
/* [0x09] = */ PLAIN,
|
||||
/* [0x0A] = */ 0,
|
||||
/* [0x0B] = */ 0,
|
||||
/* [0x0C] = */ 0,
|
||||
/* [0x0D] = */ 0,
|
||||
/* [0x0E] = */ 0,
|
||||
/* [0x0F] = */ MBC3,
|
||||
/* [0x10] = */ MBC3,
|
||||
/* [0x11] = */ MBC3,
|
||||
/* [0x12] = */ MBC3,
|
||||
/* [0x13] = */ MBC3,
|
||||
/* [0x14] = */ 0,
|
||||
/* [0x15] = */ 0,
|
||||
/* [0x16] = */ 0,
|
||||
/* [0x17] = */ 0,
|
||||
/* [0x18] = */ 0,
|
||||
/* [0x19] = */ MBC5,
|
||||
/* [0x1A] = */ MBC5,
|
||||
/* [0x1B] = */ MBC5,
|
||||
/* [0x1C] = */ MBC5,
|
||||
/* [0x1D] = */ MBC5,
|
||||
/* [0x1E] = */ MBC5
|
||||
};
|
||||
|
||||
return static_cast<Cartridgetype>(typeLut[(headerByte0x147 + 1) & 0x1F]);
|
||||
}
|
||||
|
||||
static unsigned toMulti64Rombank(const unsigned rombank)
|
||||
{
|
||||
return (rombank >> 1 & 0x30) | (rombank & 0xF);
|
||||
}
|
||||
|
||||
void Cartridge::loadState(const SaveState &state)
|
||||
{
|
||||
rtc_.loadState(state, hasRtc(memptrs_.romdata()[0x147]) ? state.mem.enableRam : false);
|
||||
|
||||
rombank_ = state.mem.rombank;
|
||||
rambank_ = state.mem.rambank;
|
||||
enableRam_ = state.mem.enableRam;
|
||||
rambankMode_ = state.mem.rambankMode;
|
||||
memptrs_.setRambank(enableRam_, rtc_.getActive(), rambank_);
|
||||
|
||||
if (rambankMode_ && multi64rom_)
|
||||
{
|
||||
const unsigned rb = toMulti64Rombank(rombank_);
|
||||
memptrs_.setRombank0(rb & 0x30);
|
||||
memptrs_.setRombank(adjustedRombank(rb, cartridgeType(memptrs_.romdata()[0x147])));
|
||||
}
|
||||
else
|
||||
{
|
||||
memptrs_.setRombank0(0);
|
||||
memptrs_.setRombank(adjustedRombank(rombank_ & (rombanks(memptrs_) - 1), cartridgeType(memptrs_.romdata()[0x147])));
|
||||
}
|
||||
}
|
||||
|
||||
void Cartridge::mbcWrite(const unsigned P, const unsigned data)
|
||||
{
|
||||
const Cartridgetype romtype = cartridgeType(memptrs_.romdata()[0x147]);
|
||||
|
||||
switch (P >> 12 & 0x7)
|
||||
{
|
||||
case 0x0:
|
||||
case 0x1: //Most MBCs write 0x?A to addresses lower than 0x2000 to enable ram.
|
||||
if (romtype == MBC2 && (P & 0x0100))
|
||||
break;
|
||||
|
||||
enableRam_ = (data & 0x0F) == 0xA;
|
||||
|
||||
if (hasRtc(memptrs_.romdata()[0x147]))
|
||||
rtc_.setEnabled(enableRam_);
|
||||
|
||||
memptrs_.setRambank(enableRam_, rtc_.getActive(), rambank_);
|
||||
break;
|
||||
//MBC1 writes ???n nnnn to address area 0x2000-0x3FFF, ???n nnnn makes up the lower digits to determine which rombank to load.
|
||||
//MBC3 writes ?nnn nnnn to address area 0x2000-0x3FFF, ?nnn nnnn makes up the lower digits to determine which rombank to load.
|
||||
//MBC5 writes nnnn nnnn to address area 0x2000-0x2FFF, nnnn nnnn makes up the lower digits to determine which rombank to load.
|
||||
//MBC5 writes bit8 of the number that determines which rombank to load to address 0x3000-0x3FFF.
|
||||
case 0x2:
|
||||
switch (romtype)
|
||||
{
|
||||
case PLAIN:
|
||||
return;
|
||||
case MBC5:
|
||||
rombank_ = (rombank_ & 0x100) | data;
|
||||
memptrs_.setRombank(adjustedRombank(rombank_ & (rombanks(memptrs_) - 1), romtype));
|
||||
return;
|
||||
default:
|
||||
break; //Only supposed to break one level.
|
||||
}
|
||||
case 0x3:
|
||||
switch (romtype)
|
||||
{
|
||||
case MBC1:
|
||||
rombank_ = rambankMode_ && !multi64rom_ ? data & 0x1F : (rombank_ & 0x60) | (data & 0x1F);
|
||||
|
||||
if (rambankMode_ && multi64rom_)
|
||||
{
|
||||
memptrs_.setRombank(adjustedRombank(toMulti64Rombank(rombank_), romtype));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case MBC2:
|
||||
if (P & 0x0100)
|
||||
{
|
||||
rombank_ = data & 0x0F;
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
case MBC3:
|
||||
rombank_ = data & 0x7F;
|
||||
break;
|
||||
case MBC5:
|
||||
rombank_ = (data & 0x1) << 8 | (rombank_ & 0xFF);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
memptrs_.setRombank(adjustedRombank(rombank_ & (rombanks(memptrs_) - 1), romtype));
|
||||
break;
|
||||
//MBC1 writes ???? ??nn to area 0x4000-0x5FFF either to determine rambank to load, or upper 2 bits of the rombank number to load, depending on rom-mode.
|
||||
//MBC3 writes ???? ??nn to area 0x4000-0x5FFF to determine rambank to load
|
||||
//MBC5 writes ???? nnnn to area 0x4000-0x5FFF to determine rambank to load
|
||||
case 0x4:
|
||||
case 0x5:
|
||||
switch (romtype)
|
||||
{
|
||||
case MBC1:
|
||||
if (rambankMode_)
|
||||
{
|
||||
if (multi64rom_)
|
||||
{
|
||||
rombank_ = (data & 0x03) << 5 | (rombank_ & 0x1F);
|
||||
|
||||
const unsigned rb = toMulti64Rombank(rombank_);
|
||||
memptrs_.setRombank0(rb & 0x30);
|
||||
memptrs_.setRombank(adjustedRombank(rb, romtype));
|
||||
return;
|
||||
}
|
||||
|
||||
rambank_ = data & 0x03;
|
||||
break;
|
||||
}
|
||||
|
||||
rombank_ = (data & 0x03) << 5 | (rombank_ & 0x1F);
|
||||
memptrs_.setRombank(adjustedRombank(rombank_ & (rombanks(memptrs_) - 1), romtype));
|
||||
return;
|
||||
case MBC3:
|
||||
if (hasRtc(memptrs_.romdata()[0x147]))
|
||||
rtc_.swapActive(data);
|
||||
|
||||
rambank_ = data & 0x03;
|
||||
break;
|
||||
case MBC5:
|
||||
rambank_ = data & 0x0F;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
memptrs_.setRambank(enableRam_, rtc_.getActive(), rambank_ & (rambanks(memptrs_) - 1));
|
||||
break;
|
||||
//MBC1: If ???? ???1 is written to area 0x6000-0x7FFFF rom will be set to rambank mode.
|
||||
case 0x6:
|
||||
case 0x7:
|
||||
switch (romtype)
|
||||
{
|
||||
case MBC1:
|
||||
rambankMode_ = data & 0x01;
|
||||
|
||||
if (multi64rom_)
|
||||
{
|
||||
if (rambankMode_)
|
||||
{
|
||||
const unsigned rb = toMulti64Rombank(rombank_);
|
||||
memptrs_.setRombank0(rb & 0x30);
|
||||
memptrs_.setRombank(adjustedRombank(rb, romtype));
|
||||
}
|
||||
else
|
||||
{
|
||||
memptrs_.setRombank0(0);
|
||||
memptrs_.setRombank(adjustedRombank(rombank_ & (rombanks(memptrs_) - 1), romtype));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MBC3:
|
||||
rtc_.latch(data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
rtc_.loadState(state);
|
||||
mbc->loadState(state.mem);
|
||||
}
|
||||
|
||||
static void enforce8bit(unsigned char *data, unsigned long sz)
|
||||
@ -319,6 +442,7 @@ namespace gambatte
|
||||
unsigned rambanks = 1;
|
||||
unsigned rombanks = 2;
|
||||
bool cgb = false;
|
||||
enum Cartridgetype { PLAIN, MBC1, MBC2, MBC3, MBC5, HUC1 } type = PLAIN;
|
||||
|
||||
{
|
||||
unsigned char header[0x150];
|
||||
@ -326,35 +450,35 @@ namespace gambatte
|
||||
|
||||
switch (header[0x0147])
|
||||
{
|
||||
case 0x00: printf("Plain ROM loaded.\n"); break;
|
||||
case 0x01: printf("MBC1 ROM loaded.\n"); break;
|
||||
case 0x02: printf("MBC1 ROM+RAM loaded.\n"); break;
|
||||
case 0x03: printf("MBC1 ROM+RAM+BATTERY loaded.\n"); break;
|
||||
case 0x05: printf("MBC2 ROM loaded.\n"); break;
|
||||
case 0x06: printf("MBC2 ROM+BATTERY loaded.\n"); break;
|
||||
case 0x08: printf("Plain ROM with additional RAM loaded.\n"); break;
|
||||
case 0x09: printf("Plain ROM with additional RAM and Battery loaded.\n"); break;
|
||||
case 0x00: printf("Plain ROM loaded.\n"); type = PLAIN; break;
|
||||
case 0x01: printf("MBC1 ROM loaded.\n"); type = MBC1; break;
|
||||
case 0x02: printf("MBC1 ROM+RAM loaded.\n"); type = MBC1; break;
|
||||
case 0x03: printf("MBC1 ROM+RAM+BATTERY loaded.\n"); type = MBC1; break;
|
||||
case 0x05: printf("MBC2 ROM loaded.\n"); type = MBC2; break;
|
||||
case 0x06: printf("MBC2 ROM+BATTERY loaded.\n"); type = MBC2; break;
|
||||
case 0x08: printf("Plain ROM with additional RAM loaded.\n"); type = MBC2; break;
|
||||
case 0x09: printf("Plain ROM with additional RAM and Battery loaded.\n"); type = MBC2;break;
|
||||
case 0x0B: printf("MM01 ROM not supported.\n"); return 1;
|
||||
case 0x0C: printf("MM01 ROM not supported.\n"); return 1;
|
||||
case 0x0D: printf("MM01 ROM not supported.\n"); return 1;
|
||||
case 0x0F: printf("MBC3 ROM+TIMER+BATTERY loaded.\n"); break;
|
||||
case 0x10: printf("MBC3 ROM+TIMER+RAM+BATTERY loaded.\n"); break;
|
||||
case 0x11: printf("MBC3 ROM loaded.\n"); break;
|
||||
case 0x12: printf("MBC3 ROM+RAM loaded.\n"); break;
|
||||
case 0x13: printf("MBC3 ROM+RAM+BATTERY loaded.\n"); break;
|
||||
case 0x0F: printf("MBC3 ROM+TIMER+BATTERY loaded.\n"); type = MBC3; break;
|
||||
case 0x10: printf("MBC3 ROM+TIMER+RAM+BATTERY loaded.\n"); type = MBC3; break;
|
||||
case 0x11: printf("MBC3 ROM loaded.\n"); type = MBC3; break;
|
||||
case 0x12: printf("MBC3 ROM+RAM loaded.\n"); type = MBC3; break;
|
||||
case 0x13: printf("MBC3 ROM+RAM+BATTERY loaded.\n"); type = MBC3; break;
|
||||
case 0x15: printf("MBC4 ROM not supported.\n"); return 1;
|
||||
case 0x16: printf("MBC4 ROM not supported.\n"); return 1;
|
||||
case 0x17: printf("MBC4 ROM not supported.\n"); return 1;
|
||||
case 0x19: printf("MBC5 ROM loaded.\n"); break;
|
||||
case 0x1A: printf("MBC5 ROM+RAM loaded.\n"); break;
|
||||
case 0x1B: printf("MBC5 ROM+RAM+BATTERY loaded.\n"); break;
|
||||
case 0x1C: printf("MBC5+RUMBLE ROM not supported.\n"); break;
|
||||
case 0x1D: printf("MBC5+RUMBLE+RAM ROM not suported.\n"); break;
|
||||
case 0x1E: printf("MBC5+RUMBLE+RAM+BATTERY ROM not supported.\n"); break;
|
||||
case 0x19: printf("MBC5 ROM loaded.\n"); type = MBC5; break;
|
||||
case 0x1A: printf("MBC5 ROM+RAM loaded.\n"); type = MBC5; break;
|
||||
case 0x1B: printf("MBC5 ROM+RAM+BATTERY loaded.\n"); type = MBC5; break;
|
||||
case 0x1C: printf("MBC5+RUMBLE ROM not supported.\n"); type = MBC5; break;
|
||||
case 0x1D: printf("MBC5+RUMBLE+RAM ROM not suported.\n"); type = MBC5; break;
|
||||
case 0x1E: printf("MBC5+RUMBLE+RAM+BATTERY ROM not supported.\n"); type = MBC5; break;
|
||||
case 0xFC: printf("Pocket Camera ROM not supported.\n"); return 1;
|
||||
case 0xFD: printf("Bandai TAMA5 ROM not supported.\n"); return 1;
|
||||
case 0xFE: printf("HuC3 ROM not supported.\n"); return 1;
|
||||
case 0xFF: printf("HuC1 ROM not supported.\n"); return 1;
|
||||
case 0xFE: printf("HuC3 ROM+RAM+BATTERY loaded.\n"); return 1;
|
||||
case 0xFF: printf("HuC1 ROM+BATTERY loaded.\n"); type = HUC1; break;
|
||||
default: printf("Wrong data-format, corrupt or unsupported ROM.\n"); return 1;
|
||||
}
|
||||
|
||||
@ -379,35 +503,26 @@ namespace gambatte
|
||||
std::printf("rombanks: %u\n", rombanks);*/
|
||||
#endif
|
||||
|
||||
switch (header[0x0149])
|
||||
{
|
||||
case 0x00:
|
||||
/*std::puts("No RAM");*/
|
||||
rambanks = cartridgeType(header[0x0147]) == MBC2;
|
||||
break;
|
||||
case 0x01:
|
||||
/*std::puts("2kB RAM");*/ /*rambankrom=1; break;*/
|
||||
case 0x02:
|
||||
/*std::puts("8kB RAM");*/
|
||||
rambanks = 1;
|
||||
break;
|
||||
case 0x03:
|
||||
/*std::puts("32kB RAM");*/
|
||||
rambanks = 4;
|
||||
break;
|
||||
case 0x04:
|
||||
/*std::puts("128kB RAM");*/
|
||||
rambanks = 16;
|
||||
break;
|
||||
case 0x05:
|
||||
/*std::puts("undocumented kB RAM");*/
|
||||
rambanks = 16;
|
||||
break;
|
||||
default:
|
||||
/*std::puts("Wrong data-format, corrupt or unsupported ROM loaded.");*/
|
||||
rambanks = 16;
|
||||
break;
|
||||
}
|
||||
switch (header[0x0149])
|
||||
{
|
||||
case 0x00: /*std::puts("No RAM");*/ rambanks = type == MBC2; break;
|
||||
case 0x01: /*std::puts("2kB RAM");*/ /*rambankrom=1; break;*/
|
||||
case 0x02: /*std::puts("8kB RAM");*/
|
||||
rambanks = 1;
|
||||
break;
|
||||
case 0x03: /*std::puts("32kB RAM");*/
|
||||
rambanks = 4;
|
||||
break;
|
||||
case 0x04: /*std::puts("128kB RAM");*/
|
||||
rambanks = 16;
|
||||
break;
|
||||
case 0x05: /*std::puts("undocumented kB RAM");*/
|
||||
rambanks = 16;
|
||||
break;
|
||||
default: /*std::puts("Wrong data-format, corrupt or unsupported ROM loaded.");*/
|
||||
rambanks = 16;
|
||||
break;
|
||||
}
|
||||
|
||||
cgb = header[0x0143] >> 7 & (1 ^ forceDmg);
|
||||
printf("cgb: %d\n", cgb);
|
||||
@ -418,20 +533,35 @@ namespace gambatte
|
||||
rombanks = pow2ceil(rom.size() / 0x4000);
|
||||
printf("rombanks: %u\n", static_cast<unsigned>(rom.size() / 0x4000));
|
||||
|
||||
ggUndoList_.clear();
|
||||
mbc.reset();
|
||||
memptrs_.reset(rombanks, rambanks, cgb ? 8 : 2);
|
||||
rtc_.set(false, 0);
|
||||
|
||||
rom.rewind();
|
||||
rom.read(reinterpret_cast<char*>(memptrs_.romdata()), (rom.size() / 0x4000) * 0x4000ul);
|
||||
// In case rombanks isn't a power of 2, allocate a disabled area for invalid rombank addresses.
|
||||
std::memset(memptrs_.romdata() + (rom.size() / 0x4000) * 0x4000ul, 0xFF, (rombanks - rom.size() / 0x4000) * 0x4000ul);
|
||||
enforce8bit(memptrs_.romdata(), rombanks * 0x4000ul);
|
||||
|
||||
if ((multi64rom_ = !rambanks && rombanks == 64 && cartridgeType(memptrs_.romdata()[0x147]) == MBC1 && multiCartCompat))
|
||||
printf("Multi-ROM \"MBC1\" presumed");
|
||||
|
||||
if (rom.fail())
|
||||
return 1;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case PLAIN: mbc.reset(new Mbc0(memptrs_)); break;
|
||||
case MBC1:
|
||||
if (!rambanks && rombanks == 64 && multiCartCompat) {
|
||||
std::puts("Multi-ROM \"MBC1\" presumed");
|
||||
mbc.reset(new Mbc1Multi64(memptrs_));
|
||||
} else
|
||||
mbc.reset(new Mbc1(memptrs_));
|
||||
break;
|
||||
case MBC2: mbc.reset(new Mbc2(memptrs_)); break;
|
||||
case MBC3: mbc.reset(new Mbc3(memptrs_, hasRtc(memptrs_.romdata()[0x147]) ? &rtc_ : 0)); break;
|
||||
case MBC5: mbc.reset(new Mbc5(memptrs_)); break;
|
||||
case HUC1: mbc.reset(new HuC1(memptrs_)); break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -440,11 +570,6 @@ namespace gambatte
|
||||
return c >= 'A' ? c - 'A' + 0xA : c - '0';
|
||||
}
|
||||
|
||||
static bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank)
|
||||
{
|
||||
return (addr< 0x4000) == (bank == 0);
|
||||
}
|
||||
|
||||
void Cartridge::applyGameGenie(const std::string &code)
|
||||
{
|
||||
if (6 < code.length())
|
||||
@ -461,7 +586,7 @@ namespace gambatte
|
||||
|
||||
for (unsigned bank = 0; bank < static_cast<std::size_t>(memptrs_.romdataend() - memptrs_.romdata()) / 0x4000; ++bank)
|
||||
{
|
||||
if (isAddressWithinAreaRombankCanBeMappedTo(addr, bank)
|
||||
if (mbc->isAddressWithinAreaRombankCanBeMappedTo(addr, bank)
|
||||
&& (cmp > 0xFF || memptrs_.romdata()[bank * 0x4000ul + (addr & 0x3FFF)] == cmp))
|
||||
{
|
||||
ggUndoList_.push_back(AddrData(bank * 0x4000ul + (addr & 0x3FFF), memptrs_.romdata()[bank * 0x4000ul + (addr & 0x3FFF)]));
|
||||
@ -473,20 +598,23 @@ namespace gambatte
|
||||
|
||||
void Cartridge::setGameGenie(const std::string &codes)
|
||||
{
|
||||
//if (loaded()) {
|
||||
for (std::vector<AddrData>::reverse_iterator it = ggUndoList_.rbegin(), end = ggUndoList_.rend(); it != end; ++it)
|
||||
#if 0
|
||||
if (loaded())
|
||||
#endif
|
||||
{
|
||||
if (memptrs_.romdata() + it->addr < memptrs_.romdataend())
|
||||
memptrs_.romdata()[it->addr] = it->data;
|
||||
for (std::vector<AddrData>::reverse_iterator it = ggUndoList_.rbegin(), end = ggUndoList_.rend(); it != end; ++it)
|
||||
{
|
||||
if (memptrs_.romdata() + it->addr < memptrs_.romdataend())
|
||||
memptrs_.romdata()[it->addr] = it->data;
|
||||
}
|
||||
|
||||
ggUndoList_.clear();
|
||||
|
||||
std::string code;
|
||||
for (std::size_t pos = 0; pos < codes.length()
|
||||
&& (code = codes.substr(pos, codes.find(';', pos) - pos), true); pos += code.length() + 1)
|
||||
applyGameGenie(code);
|
||||
}
|
||||
|
||||
ggUndoList_.clear();
|
||||
|
||||
std::string code;
|
||||
for (std::size_t pos = 0; pos < codes.length()
|
||||
&& (code = codes.substr(pos, codes.find(';', pos) - pos), true); pos += code.length() + 1)
|
||||
applyGameGenie(code);
|
||||
//}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,12 +21,23 @@
|
||||
|
||||
#include "memptrs.h"
|
||||
#include "rtc.h"
|
||||
#include "savestate.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "file/file.h"
|
||||
|
||||
namespace gambatte
|
||||
{
|
||||
struct SaveState;
|
||||
class Mbc
|
||||
{
|
||||
public:
|
||||
virtual ~Mbc() {}
|
||||
virtual void romWrite(unsigned P, unsigned data) = 0;
|
||||
virtual void saveState(SaveState::Mem &ss) const = 0;
|
||||
virtual void loadState(const SaveState::Mem &ss) = 0;
|
||||
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned address, unsigned rombank) const = 0;
|
||||
};
|
||||
|
||||
class Cartridge
|
||||
{
|
||||
@ -34,7 +45,8 @@ namespace gambatte
|
||||
void setStatePtrs(SaveState &);
|
||||
void saveState(SaveState &) const;
|
||||
void loadState(const SaveState &);
|
||||
Cartridge();
|
||||
|
||||
bool loaded() const { return mbc.get(); }
|
||||
|
||||
const unsigned char * rmem(unsigned area) const
|
||||
{
|
||||
@ -100,7 +112,7 @@ namespace gambatte
|
||||
memptrs_.setOamDmaSrc(oamDmaSrc);
|
||||
}
|
||||
|
||||
void mbcWrite(unsigned addr, unsigned data);
|
||||
void mbcWrite(unsigned addr, unsigned data) { mbc->romWrite(addr, data); }
|
||||
|
||||
bool isCgb() const
|
||||
{
|
||||
@ -143,11 +155,7 @@ namespace gambatte
|
||||
MemPtrs memptrs_;
|
||||
Rtc rtc_;
|
||||
|
||||
unsigned short rombank_;
|
||||
unsigned char rambank_;
|
||||
bool enableRam_;
|
||||
bool rambankMode_;
|
||||
bool multi64rom_;
|
||||
std::auto_ptr<Mbc> mbc;
|
||||
|
||||
std::vector<AddrData> ggUndoList_;
|
||||
|
||||
|
@ -73,7 +73,7 @@ namespace gambatte
|
||||
rmem_[0xE] = wmem_[0xE] = wramdata_[0] - 0xE000;
|
||||
|
||||
setRombank(1);
|
||||
setRambank(false, false, 0);
|
||||
setRambank(0, 0);
|
||||
setVrambank(0);
|
||||
setWrambank(1);
|
||||
}
|
||||
@ -92,19 +92,15 @@ namespace gambatte
|
||||
disconnectOamDmaAreas();
|
||||
}
|
||||
|
||||
void MemPtrs::setRambank(const bool enableRam, const bool rtcActive, const unsigned rambank)
|
||||
void MemPtrs::setRambank(const unsigned flags, const unsigned rambank)
|
||||
{
|
||||
rsrambankptr_ = rdisabledRam_ - 0xA000;
|
||||
wsrambankptr_ = wdisabledRam_ - 0xA000;
|
||||
|
||||
if (enableRam)
|
||||
{
|
||||
if (rtcActive)
|
||||
rsrambankptr_ = wsrambankptr_ = 0;
|
||||
else if (rambankdata() != rambankdataend())
|
||||
rsrambankptr_ = wsrambankptr_ = rambankdata_ + rambank * 0x2000ul - 0xA000;
|
||||
}
|
||||
unsigned char *const srambankptr = flags & RTC_EN
|
||||
? 0
|
||||
: (rambankdata() != rambankdataend()
|
||||
? rambankdata_ + rambank * 0x2000ul - 0xA000 : wdisabledRam_ - 0xA000);
|
||||
|
||||
rsrambankptr_ = (flags & READ_EN) && srambankptr != wdisabledRam_ - 0xA000 ? srambankptr : rdisabledRam_ - 0xA000;
|
||||
wsrambankptr_ = flags & WRITE_EN ? srambankptr : wdisabledRam_ - 0xA000;
|
||||
rmem_[0xB] = rmem_[0xA] = rsrambankptr_;
|
||||
wmem_[0xB] = wmem_[0xA] = wsrambankptr_;
|
||||
disconnectOamDmaAreas();
|
||||
|
@ -125,7 +125,7 @@ namespace gambatte
|
||||
|
||||
void setRombank0(unsigned bank);
|
||||
void setRombank(unsigned bank);
|
||||
void setRambank(bool enableRam, bool rtcActive, unsigned rambank);
|
||||
void setRambank(unsigned ramFlags, unsigned rambank);
|
||||
void setVrambank(unsigned bank) { vrambankptr_ = vramdata() + bank * 0x2000ul - 0x8000; }
|
||||
void setWrambank(unsigned bank);
|
||||
void setOamDmaSrc(OamDmaSrc oamDmaSrc);
|
||||
|
@ -99,7 +99,6 @@ namespace gambatte
|
||||
{
|
||||
state.rtc.baseTime = baseTime_;
|
||||
state.rtc.haltTime = haltTime_;
|
||||
state.rtc.index = index_;
|
||||
state.rtc.dataDh = dataDh_;
|
||||
state.rtc.dataDl = dataDl_;
|
||||
state.rtc.dataH = dataH_;
|
||||
@ -108,13 +107,10 @@ namespace gambatte
|
||||
state.rtc.lastLatchData = lastLatchData_;
|
||||
}
|
||||
|
||||
void Rtc::loadState(const SaveState &state, const bool enabled)
|
||||
void Rtc::loadState(const SaveState &state)
|
||||
{
|
||||
enabled_ = enabled;
|
||||
|
||||
baseTime_ = state.rtc.baseTime;
|
||||
haltTime_ = state.rtc.haltTime;
|
||||
index_ = state.rtc.index;
|
||||
dataDh_ = state.rtc.dataDh;
|
||||
dataDl_ = state.rtc.dataDl;
|
||||
dataH_ = state.rtc.dataH;
|
||||
|
@ -56,21 +56,15 @@ namespace gambatte
|
||||
}
|
||||
|
||||
void saveState(SaveState &state) const;
|
||||
void loadState(const SaveState &state, bool enabled);
|
||||
void loadState(const SaveState &state);
|
||||
|
||||
void setEnabled(const bool enabled)
|
||||
{
|
||||
enabled_ = enabled;
|
||||
|
||||
doSwapActive();
|
||||
}
|
||||
|
||||
void swapActive(unsigned bank)
|
||||
void set(const bool enabled, unsigned bank)
|
||||
{
|
||||
bank &= 0xF;
|
||||
bank -= 8;
|
||||
|
||||
index_ = bank;
|
||||
this->enabled_ = enabled;
|
||||
this->index_ = bank;
|
||||
|
||||
doSwapActive();
|
||||
}
|
||||
|
@ -185,7 +185,6 @@ namespace gambatte
|
||||
struct RTC {
|
||||
unsigned long baseTime;
|
||||
unsigned long haltTime;
|
||||
unsigned char index;
|
||||
unsigned char dataDh;
|
||||
unsigned char dataDl;
|
||||
unsigned char dataH;
|
||||
|
@ -412,7 +412,6 @@ SaverList::SaverList() {
|
||||
{ static const char label[] = { c,NO4,m,a,s,t,r, NUL }; ADD(spu.ch4.master); }
|
||||
{ static const char label[] = { r,t,c,b,a,s,e, NUL }; ADD(rtc.baseTime); }
|
||||
{ static const char label[] = { r,t,c,h,a,l,t, NUL }; ADD(rtc.haltTime); }
|
||||
{ static const char label[] = { r,t,c,i,n,d,x, NUL }; ADD(rtc.index); }
|
||||
{ static const char label[] = { r,t,c,d,h, NUL }; ADD(rtc.dataDh); }
|
||||
{ static const char label[] = { r,t,c,d,l, NUL }; ADD(rtc.dataDl); }
|
||||
{ static const char label[] = { r,t,c,h, NUL }; ADD(rtc.dataH); }
|
||||
|
Loading…
Reference in New Issue
Block a user