diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 94bab7befb..a3553bc139 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -20,6 +20,7 @@ #define GAMBATTE_H #include "gbint.h" +#include "loadres.h" #include #include #include @@ -48,7 +49,7 @@ class GB { public: GB(); ~GB(); - + enum LoadFlag { FORCE_DMG = 1, /**< Treat the ROM as not having CGB support regardless of what its header advertises. */ GBA_CGB = 2, /**< Use GBA intial CPU register values when in CGB mode. */ @@ -61,7 +62,7 @@ public: * @param flags ORed combination of LoadFlags. * @return 0 on success, negative value on failure. */ - int load(const char *romfiledata, unsigned romfilelength, std::uint32_t now, unsigned flags, unsigned div); + LoadRes load(const char *romfiledata, unsigned romfilelength, std::uint32_t now, unsigned flags, unsigned div); int loadGBCBios(const char* biosfiledata); int loadDMGBios(const char* biosfiledata); diff --git a/libgambatte/include/loadres.h b/libgambatte/include/loadres.h new file mode 100644 index 0000000000..6ee5e370b5 --- /dev/null +++ b/libgambatte/include/loadres.h @@ -0,0 +1,23 @@ +#ifndef GAMBATTE_LOADRES_H +#define GAMBATTE_LOADRES_H + +#include + +namespace gambatte { + +enum LoadRes { + LOADRES_BAD_FILE_OR_UNKNOWN_MBC = -0x7FFF, + LOADRES_IO_ERROR, + LOADRES_UNSUPPORTED_MBC_HUC3 = -0x1FE, + LOADRES_UNSUPPORTED_MBC_TAMA5, + LOADRES_UNSUPPORTED_MBC_POCKET_CAMERA, + LOADRES_UNSUPPORTED_MBC_MBC7 = -0x122, + LOADRES_UNSUPPORTED_MBC_MBC6 = -0x120, + LOADRES_UNSUPPORTED_MBC_MBC4 = -0x117, + LOADRES_UNSUPPORTED_MBC_MMM01 = -0x10D, + LOADRES_OK = 0 +}; + +} + +#endif diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 427847e95d..8d3d7c99a7 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -104,7 +104,7 @@ public: memory.setLinkCallback(callback); } - int load(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat) { + LoadRes load(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat) { return memory.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat); } diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index 818c09c48d..0c05ed4e1a 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -140,13 +140,13 @@ void GB::setLinkCallback(void(*callback)()) { p_->cpu.setLinkCallback(callback); } -int GB::load(const char *romfiledata, unsigned romfilelength, const std::uint32_t now, const unsigned flags, const unsigned div) { +LoadRes GB::load(const char *romfiledata, unsigned romfilelength, const std::uint32_t now, unsigned const flags, const unsigned div) { //if (p_->cpu.loaded()) // p_->cpu.saveSavedata(); - const int failed = p_->cpu.load(romfiledata, romfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT); + LoadRes const loadres = p_->cpu.load(romfiledata, romfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT); - if (!failed) { + if (loadres == LOADRES_OK) { SaveState state; p_->cpu.setStatePtrs(state); p_->loadflags = flags; @@ -155,7 +155,7 @@ int GB::load(const char *romfiledata, unsigned romfilelength, const std::uint32_ //p_->cpu.loadSavedata(); } - return failed; + return loadres; } int GB::loadGBCBios(const char* biosfiledata) { @@ -223,7 +223,7 @@ const std::string GB::romTitle() const { if (p_->cpu.loaded()) { char title[0x11]; std::memcpy(title, p_->cpu.romTitle(), 0x10); - title[(title[0xF] & 0x80) ? 0xF : 0x10] = '\0'; + title[title[0xF] & 0x80 ? 0xF : 0x10] = '\0'; return std::string(title); } diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index e062fb0c96..63e48171f3 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -525,7 +525,23 @@ static unsigned pow2ceil(unsigned n) { return n; } -int Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) { +static bool isMbc2(unsigned char h147) { return h147 == 5 || h147 == 6; } + +static unsigned numRambanksFromH14x(unsigned char h147, unsigned char h149) { + switch (h149) { + case 0x00: return isMbc2(h147) ? 1 : 0; + case 0x01: + case 0x02: return 1; + } + + return 4; +} + +static bool presumedMulti64Mbc1(unsigned char const header[], unsigned const rombanks) { + return header[0x147] == 1 && header[0x149] == 0 && rombanks == 64; +} + +LoadRes Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, bool const forceDmg, bool const multicartCompat) { //const std::auto_ptr rom(newFileInstance(romfile)); //if (rom->fail()) @@ -542,39 +558,39 @@ int Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, const bo if (romfilelength >= sizeof header) std::memcpy(header, romfiledata, sizeof header); else - return -1; + return LOADRES_IO_ERROR; switch (header[0x0147]) { - case 0x00: std::puts("Plain ROM loaded."); type = PLAIN; break; - case 0x01: std::puts("MBC1 ROM loaded."); type = MBC1; break; - case 0x02: std::puts("MBC1 ROM+RAM loaded."); type = MBC1; break; - case 0x03: std::puts("MBC1 ROM+RAM+BATTERY loaded."); type = MBC1; break; - case 0x05: std::puts("MBC2 ROM loaded."); type = MBC2; break; - case 0x06: std::puts("MBC2 ROM+BATTERY loaded."); type = MBC2; break; - case 0x08: std::puts("Plain ROM with additional RAM loaded."); type = PLAIN; break; - case 0x09: std::puts("Plain ROM with additional RAM and Battery loaded."); type = PLAIN; break; - case 0x0B: std::puts("MM01 ROM not supported."); return -1; - case 0x0C: std::puts("MM01 ROM not supported."); return -1; - case 0x0D: std::puts("MM01 ROM not supported."); return -1; - case 0x0F: std::puts("MBC3 ROM+TIMER+BATTERY loaded."); type = MBC3; break; - case 0x10: std::puts("MBC3 ROM+TIMER+RAM+BATTERY loaded."); type = MBC3; break; - case 0x11: std::puts("MBC3 ROM loaded."); type = MBC3; break; - case 0x12: std::puts("MBC3 ROM+RAM loaded."); type = MBC3; break; - case 0x13: std::puts("MBC3 ROM+RAM+BATTERY loaded."); type = MBC3; break; - case 0x15: std::puts("MBC4 ROM not supported."); return -1; - case 0x16: std::puts("MBC4 ROM not supported."); return -1; - case 0x17: std::puts("MBC4 ROM not supported."); return -1; - case 0x19: std::puts("MBC5 ROM loaded."); type = MBC5; break; - case 0x1A: std::puts("MBC5 ROM+RAM loaded."); type = MBC5; break; - case 0x1B: std::puts("MBC5 ROM+RAM+BATTERY loaded."); type = MBC5; break; - case 0x1C: std::puts("MBC5+RUMBLE ROM not supported."); type = MBC5; break; - case 0x1D: std::puts("MBC5+RUMBLE+RAM ROM not suported."); type = MBC5; break; - case 0x1E: std::puts("MBC5+RUMBLE+RAM+BATTERY ROM not supported."); type = MBC5; break; - case 0xFC: std::puts("Pocket Camera ROM not supported."); return -1; - case 0xFD: std::puts("Bandai TAMA5 ROM not supported."); return -1; - case 0xFE: std::puts("HuC3 ROM not supported."); return -1; - case 0xFF: std::puts("HuC1 ROM+RAM+BATTERY loaded."); type = HUC1; break; - default: std::puts("Wrong data-format, corrupt or unsupported ROM."); return -1; + case 0x00: type = PLAIN; break; + case 0x01: + case 0x02: + case 0x03: type = MBC1; break; + case 0x05: + case 0x06: type = MBC2; break; + case 0x08: + case 0x09: type = PLAIN; break; + case 0x0B: + case 0x0C: + case 0x0D: return LOADRES_UNSUPPORTED_MBC_MMM01; + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: type = MBC3; break; + case 0x15: + case 0x16: + case 0x17: return LOADRES_UNSUPPORTED_MBC_MBC4; + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: type = MBC5; break; + case 0xFC: return LOADRES_UNSUPPORTED_MBC_POCKET_CAMERA; + case 0xFD: return LOADRES_UNSUPPORTED_MBC_TAMA5; + case 0xFE: return LOADRES_UNSUPPORTED_MBC_HUC3; + case 0xFF: type = HUC1; break; + default: return LOADRES_BAD_FILE_OR_UNKNOWN_MBC; } /*switch (header[0x0148]) { @@ -591,40 +607,15 @@ int Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, const bo case 0x53: rombanks = 80; break; case 0x54: rombanks = 96; break; default: return -1; - } + }*/ - std::printf("rombanks: %u\n", rombanks);*/ - - 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 = !forceDmg; - std::printf("cgb: %d\n", cgb); - } - - std::printf("rambanks: %u\n", rambanks); - - const std::size_t filesize = romfilelength; //rom->size(); - rombanks = std::max(pow2ceil(filesize / 0x4000), 2u); - std::printf("rombanks: %u\n", static_cast(filesize / 0x4000)); + rambanks = numRambanksFromH14x(header[0x147], header[0x149]); + cgb = !forceDmg; + } + std::size_t const filesize = romfilelength; //rom->size(); + rombanks = std::max(pow2ceil(filesize / 0x4000), 2u); + mbc.reset(); memptrs.reset(rombanks, rambanks, cgb ? 8 : 2); rtc.set(false, 0); @@ -641,8 +632,7 @@ int Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, const bo switch (type) { case PLAIN: mbc.reset(new Mbc0(memptrs)); break; case MBC1: - if (!rambanks && rombanks == 64 && multicartCompat) { - std::puts("Multi-ROM \"MBC1\" presumed"); + if (multicartCompat && presumedMulti64Mbc1(memptrs.romdata(), rombanks)) { mbc.reset(new Mbc1Multi64(memptrs)); } else mbc.reset(new Mbc1(memptrs)); @@ -654,7 +644,7 @@ int Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, const bo case HUC1: mbc.reset(new HuC1(memptrs)); break; } - return 0; + return LOADRES_OK; } static bool hasBattery(const unsigned char headerByte0x147) { diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h index 511c4438ae..934d44df7e 100644 --- a/libgambatte/src/mem/cartridge.h +++ b/libgambatte/src/mem/cartridge.h @@ -19,6 +19,7 @@ #ifndef CARTRIDGE_H #define CARTRIDGE_H +#include "loadres.h" #include "memptrs.h" #include "rtc.h" #include "savestate.h" @@ -98,7 +99,7 @@ public: bool getMemoryArea(int which, unsigned char **data, int *length) const; - int loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); + LoadRes loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); const char * romTitle() const { return reinterpret_cast(memptrs.romdata() + 0x134); } void setRTCCallback(std::uint32_t (*callback)()) { diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 0b92460ca5..657a647fcf 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -1056,14 +1056,14 @@ void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsig ioamhram[P - 0xFE00] = data; } -int Memory::loadROM(const char *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) { - if (const int fail = cart.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat)) +LoadRes Memory::loadROM(const char *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) { + if (LoadRes const fail = cart.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat)) return fail; sound.init(cart.isCgb()); display.reset(ioamhram, cart.vramdata(), cart.isCgb()); - return 0; + return LOADRES_OK; } unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) { diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 3ea2eec6ac..fcd7981730 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -296,7 +296,7 @@ public: unsigned long event(unsigned long cycleCounter); unsigned long resetCounters(unsigned long cycleCounter); - int loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); + LoadRes loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); void setInputGetter(unsigned (*getInput)()) { this->getInput = getInput; diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 980a355abd..4fac65dc3a 100644 Binary files a/output/dll/libgambatte.dll and b/output/dll/libgambatte.dll differ