mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
1cf24ad58a
2
CHANGES
2
CHANGES
|
@ -47,11 +47,13 @@ Features:
|
|||
- Cheat code support in homebrew ports
|
||||
- Acclerometer and gyro support for controllers on PC
|
||||
- Support for combo "Super Game Boy Color" SGB + GBC ROM hacks
|
||||
- Improved support for HuC-3 mapper, including RTC
|
||||
- Support for 64 kiB SRAM saves used in some bootlegs
|
||||
- Discord Rich Presence now supports time elapsed
|
||||
- Additional scaling shaders
|
||||
- Support for GameShark Advance SP (.gsv) save file importing
|
||||
- Support for multiple saves per game using .sa2, .sa3, etc.
|
||||
- New unlicensed GB mappers: NT (newer type)
|
||||
Emulation fixes:
|
||||
- ARM7: Fix unsigned multiply timing
|
||||
- GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032)
|
||||
|
|
|
@ -34,7 +34,7 @@ Features
|
|||
- Remappable controls for both keyboards and gamepads.
|
||||
- Loading from ZIP and 7z files.
|
||||
- IPS, UPS and BPS patch support.
|
||||
- Game debugging via a command-line interface and GDB remote support, compatible with IDA Pro.
|
||||
- Game debugging via a command-line interface and GDB remote support, compatible with Ghidra and IDA Pro.
|
||||
- Configurable emulation rewinding.
|
||||
- Support for loading and exporting GameShark and Action Replay snapshots.
|
||||
- Cores available for RetroArch/Libretro and OpenEmu.
|
||||
|
@ -54,6 +54,7 @@ The following mappers are fully supported:
|
|||
- MBC5+Rumble
|
||||
- MBC7
|
||||
- Wisdom Tree (unlicensed)
|
||||
- NT "new type" (unlicensed MBC5-like)
|
||||
- Pokémon Jade/Diamond (unlicensed)
|
||||
- BBD (unlicensed MBC5-like)
|
||||
- Hitek (unlicensed MBC5-like)
|
||||
|
@ -65,7 +66,7 @@ The following mappers are partially supported:
|
|||
- Pocket Cam
|
||||
- TAMA5 (missing RTC support)
|
||||
- HuC-1 (missing IR support)
|
||||
- HuC-3 (missing RTC and IR support)
|
||||
- HuC-3 (missing IR support)
|
||||
|
||||
### Planned features
|
||||
|
||||
|
@ -299,7 +300,7 @@ Missing features on DS are
|
|||
Copyright
|
||||
---------
|
||||
|
||||
medusa is Copyright © 2013 – 2021 Jeffrey Pfau. It is distributed under the [Mozilla Public License version 2.0](https://www.mozilla.org/MPL/2.0/). A copy of the license is available in the distributed LICENSE file.
|
||||
medusa is Copyright © 2013 – 2022 Jeffrey Pfau. It is distributed under the [Mozilla Public License version 2.0](https://www.mozilla.org/MPL/2.0/). A copy of the license is available in the distributed LICENSE file.
|
||||
|
||||
medusa contains the following third-party libraries:
|
||||
|
||||
|
|
|
@ -182,6 +182,7 @@ struct mCoreCallbacks {
|
|||
void (*shutdown)(void* context);
|
||||
void (*keysRead)(void* context);
|
||||
void (*savedataUpdated)(void* context);
|
||||
void (*alarm)(void* context);
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(mCoreCallbacksList, struct mCoreCallbacks);
|
||||
|
|
|
@ -41,6 +41,7 @@ enum GBMemoryBankControllerType {
|
|||
GB_MBC5_RUMBLE = 0x105,
|
||||
GB_UNL_WISDOM_TREE = 0x200,
|
||||
GB_UNL_PKJD = 0x203,
|
||||
GB_UNL_NT_NEW = 0x212,
|
||||
GB_UNL_BBD = 0x220, // Also used as a mask for MBCs that need special read behavior
|
||||
GB_UNL_HITEK = 0x221,
|
||||
};
|
||||
|
|
|
@ -42,9 +42,18 @@ struct GBMBCRTCSaveBuffer {
|
|||
uint32_t latchedDaysHi;
|
||||
uint64_t unixTime;
|
||||
};
|
||||
|
||||
struct GBMBCHuC3SaveBuffer {
|
||||
uint8_t regs[0x80];
|
||||
uint64_t latchedUnix;
|
||||
};
|
||||
|
||||
void GBMBCRTCRead(struct GB* gb);
|
||||
void GBMBCRTCWrite(struct GB* gb);
|
||||
|
||||
void GBMBCHuC3Read(struct GB* gb);
|
||||
void GBMBCHuC3Write(struct GB* gb);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -102,6 +102,41 @@ enum GBTAMA5Register {
|
|||
GBTAMA5_READ_HI = 0xD,
|
||||
};
|
||||
|
||||
enum GBHuC3Register {
|
||||
GBHUC3_RTC_MINUTES_LO = 0x10,
|
||||
GBHUC3_RTC_MINUTES_MI = 0x11,
|
||||
GBHUC3_RTC_MINUTES_HI = 0x12,
|
||||
GBHUC3_RTC_DAYS_LO = 0x13,
|
||||
GBHUC3_RTC_DAYS_MI = 0x14,
|
||||
GBHUC3_RTC_DAYS_HI = 0x15,
|
||||
GBHUC3_RTC_ENABLE = 0x16,
|
||||
GBHUC3_SPEAKER_TONE = 0x26,
|
||||
GBHUC3_SPEAKER_ENABLE = 0x27,
|
||||
GBHUC3_ALARM_MINUTES_LO = 0x58,
|
||||
GBHUC3_ALARM_MINUTES_MI = 0x59,
|
||||
GBHUC3_ALARM_MINUTES_HI = 0x5A,
|
||||
GBHUC3_ALARM_DAYS_LO = 0x5B,
|
||||
GBHUC3_ALARM_DAYS_MI = 0x5C,
|
||||
GBHUC3_ALARM_DAYS_HI = 0x5D,
|
||||
GBHUC3_ALARM_TONE = 0x5E,
|
||||
GBHUC3_ALARM_ENABLE = 0x5F,
|
||||
};
|
||||
|
||||
enum GBHuC3Mode {
|
||||
GBHUC3_MODE_SRAM_RO = 0x0,
|
||||
GBHUC3_MODE_SRAM_RW = 0xA,
|
||||
GBHUC3_MODE_IN = 0xB,
|
||||
GBHUC3_MODE_OUT = 0xC,
|
||||
GBHUC3_MODE_COMMIT = 0xD,
|
||||
};
|
||||
|
||||
enum GBHuC3Command {
|
||||
GBHUC3_CMD_LATCH = 0x0,
|
||||
GBHUC3_CMD_SET_RTC = 0x1,
|
||||
GBHUC3_CMD_RO = 0x2,
|
||||
GBHUC3_CMD_TONE = 0xE,
|
||||
};
|
||||
|
||||
struct GBMBC1State {
|
||||
int mode;
|
||||
int multicartStride;
|
||||
|
@ -110,11 +145,7 @@ struct GBMBC1State {
|
|||
};
|
||||
|
||||
struct GBMBC6State {
|
||||
int currentBank1;
|
||||
uint8_t* romBank1;
|
||||
bool sramAccess;
|
||||
int currentSramBank1;
|
||||
uint8_t* sramBank1;
|
||||
bool flashBank0;
|
||||
bool flashBank1;
|
||||
};
|
||||
|
@ -145,10 +176,21 @@ struct GBTAMA5State {
|
|||
uint8_t registers[GBTAMA5_MAX];
|
||||
};
|
||||
|
||||
struct GBHuC3State {
|
||||
uint8_t index;
|
||||
uint8_t value;
|
||||
uint8_t mode;
|
||||
uint8_t registers[256];
|
||||
};
|
||||
|
||||
struct GBPKJDState {
|
||||
uint8_t reg[2];
|
||||
};
|
||||
|
||||
struct GBNTNewState {
|
||||
bool splitMode;
|
||||
};
|
||||
|
||||
struct GBBBDState {
|
||||
int dataSwapMode;
|
||||
int bankSwapMode;
|
||||
|
@ -161,6 +203,8 @@ union GBMBCState {
|
|||
struct GBMMM01State mmm01;
|
||||
struct GBPocketCamState pocketCam;
|
||||
struct GBTAMA5State tama5;
|
||||
struct GBHuC3State huc3;
|
||||
struct GBNTNewState ntNew;
|
||||
struct GBPKJDState pkjd;
|
||||
struct GBBBDState bbd;
|
||||
};
|
||||
|
@ -176,6 +220,11 @@ struct GBMemory {
|
|||
union GBMBCState mbcState;
|
||||
int currentBank;
|
||||
int currentBank0;
|
||||
int currentBank1;
|
||||
uint8_t* romBank1;
|
||||
int currentSramBank1;
|
||||
uint8_t* sramBank1;
|
||||
|
||||
unsigned cartBusDecay;
|
||||
uint16_t cartBusPc;
|
||||
uint8_t cartBus;
|
||||
|
|
|
@ -168,7 +168,8 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
|
|||
* 0x003FF: Interrupts enabled
|
||||
* 0x00400 - 0x043FF: VRAM
|
||||
* 0x04400 - 0x0C3FF: WRAM
|
||||
* 0x0C400 - 0x0C77F: Reserved
|
||||
* 0x0C400 - 0x0C6FF: Reserved
|
||||
* 0x0C700 - 0x0C77F: Reserved
|
||||
* 0x0C780 - 0x117FF: Super Game Boy
|
||||
* | 0x0C780 - 0x0C7D9: Current attributes
|
||||
* | 0x0C7DA: Current command
|
||||
|
@ -393,6 +394,12 @@ struct GBSerializedState {
|
|||
uint8_t locked;
|
||||
uint8_t bank0;
|
||||
} mmm01;
|
||||
struct {
|
||||
uint64_t lastLatch;
|
||||
uint8_t index;
|
||||
uint8_t value;
|
||||
uint8_t mode;
|
||||
} huc3;
|
||||
struct {
|
||||
uint8_t dataSwapMode;
|
||||
uint8_t bankSwapMode;
|
||||
|
@ -421,7 +428,9 @@ struct GBSerializedState {
|
|||
uint8_t vram[GB_SIZE_VRAM];
|
||||
uint8_t wram[GB_SIZE_WORKING_RAM];
|
||||
|
||||
uint32_t reserved2[0xC4];
|
||||
uint32_t reserved2[0xA4];
|
||||
|
||||
uint8_t huc3Registers[0x80];
|
||||
|
||||
struct {
|
||||
uint8_t attributes[90];
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include <libavcodec/version.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 58
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 59
|
||||
#include <libavcodec/bsf.h>
|
||||
#endif
|
||||
|
||||
|
|
12
src/gb/gb.c
12
src/gb/gb.c
|
@ -143,8 +143,12 @@ void GBYankROM(struct GB* gb) {
|
|||
static void GBSramDeinit(struct GB* gb) {
|
||||
if (gb->sramVf) {
|
||||
gb->sramVf->unmap(gb->sramVf, gb->memory.sram, gb->sramSize);
|
||||
if (gb->memory.mbcType == GB_MBC3_RTC && gb->sramVf == gb->sramRealVf) {
|
||||
GBMBCRTCWrite(gb);
|
||||
if (gb->sramVf == gb->sramRealVf) {
|
||||
if (gb->memory.mbcType == GB_MBC3_RTC) {
|
||||
GBMBCRTCWrite(gb);
|
||||
} else if (gb->memory.mbcType == GB_HuC3) {
|
||||
GBMBCHuC3Write(gb);
|
||||
}
|
||||
}
|
||||
gb->sramVf = NULL;
|
||||
} else if (gb->memory.sram) {
|
||||
|
@ -163,6 +167,8 @@ bool GBLoadSave(struct GB* gb, struct VFile* vf) {
|
|||
|
||||
if (gb->memory.mbcType == GB_MBC3_RTC) {
|
||||
GBMBCRTCRead(gb);
|
||||
} else if (gb->memory.mbcType == GB_HuC3) {
|
||||
GBMBCHuC3Read(gb);
|
||||
}
|
||||
}
|
||||
return vf;
|
||||
|
@ -246,6 +252,8 @@ void GBSramClean(struct GB* gb, uint32_t frameCount) {
|
|||
}
|
||||
if (gb->memory.mbcType == GB_MBC3_RTC) {
|
||||
GBMBCRTCWrite(gb);
|
||||
} else if (gb->memory.mbcType == GB_HuC3) {
|
||||
GBMBCHuC3Write(gb);
|
||||
}
|
||||
if (gb->sramVf == gb->sramRealVf) {
|
||||
if (gb->memory.sram && gb->sramVf->sync(gb->sramVf, gb->memory.sram, gb->sramSize)) {
|
||||
|
|
260
src/gb/mbc.c
260
src/gb/mbc.c
|
@ -38,6 +38,7 @@ static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value);
|
|||
static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value);
|
||||
static void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value);
|
||||
static void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value);
|
||||
static void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value);
|
||||
static void _GBBBD(struct GB* gb, uint16_t address, uint8_t value);
|
||||
static void _GBHitek(struct GB* gb, uint16_t address, uint8_t value);
|
||||
|
||||
|
@ -47,6 +48,7 @@ static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address);
|
|||
static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value);
|
||||
|
||||
static uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address);
|
||||
static uint8_t _GBHuC3Read(struct GBMemory*, uint16_t address);
|
||||
static uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address);
|
||||
static uint8_t _GBBBDRead(struct GBMemory*, uint16_t address);
|
||||
static uint8_t _GBHitekRead(struct GBMemory*, uint16_t address);
|
||||
|
@ -85,7 +87,10 @@ void GBMBCSwitchBank0(struct GB* gb, int bank) {
|
|||
|
||||
void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank) {
|
||||
size_t bankStart = bank * GB_SIZE_CART_HALFBANK;
|
||||
bool isFlash = half ? gb->memory.mbcState.mbc6.flashBank1 : gb->memory.mbcState.mbc6.flashBank0;
|
||||
bool isFlash = false;
|
||||
if (gb->memory.mbcType == GB_MBC6) {
|
||||
isFlash = half ? gb->memory.mbcState.mbc6.flashBank1 : gb->memory.mbcState.mbc6.flashBank0;
|
||||
}
|
||||
if (isFlash) {
|
||||
if (bankStart + GB_SIZE_CART_HALFBANK > GB_SIZE_MBC6_FLASH) {
|
||||
mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid Flash bank: %0X", bank);
|
||||
|
@ -112,11 +117,11 @@ void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank) {
|
|||
gb->memory.currentBank = bank;
|
||||
} else {
|
||||
if (isFlash) {
|
||||
gb->memory.mbcState.mbc6.romBank1 = &gb->memory.sram[bankStart];
|
||||
gb->memory.romBank1 = &gb->memory.sram[bankStart];
|
||||
} else {
|
||||
gb->memory.mbcState.mbc6.romBank1 = &gb->memory.rom[bankStart];
|
||||
gb->memory.romBank1 = &gb->memory.rom[bankStart];
|
||||
}
|
||||
gb->memory.mbcState.mbc6.currentBank1 = bank;
|
||||
gb->memory.currentBank1 = bank;
|
||||
}
|
||||
if (gb->cpu->pc < GB_BASE_VRAM) {
|
||||
gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
|
||||
|
@ -219,8 +224,8 @@ void GBMBCSwitchSramHalfBank(struct GB* gb, int half, int bank) {
|
|||
gb->memory.sramBank = &gb->memory.sram[bankStart];
|
||||
gb->memory.sramCurrentBank = bank;
|
||||
} else {
|
||||
gb->memory.mbcState.mbc6.sramBank1 = &gb->memory.sram[bankStart];
|
||||
gb->memory.mbcState.mbc6.currentSramBank1 = bank;
|
||||
gb->memory.sramBank1 = &gb->memory.sram[bankStart];
|
||||
gb->memory.currentSramBank1 = bank;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -373,6 +378,7 @@ void GBMBCInit(struct GB* gb) {
|
|||
break;
|
||||
case GB_HuC3:
|
||||
gb->memory.mbcWrite = _GBHuC3;
|
||||
gb->memory.mbcRead = _GBHuC3Read;
|
||||
break;
|
||||
case GB_TAMA5:
|
||||
mLOG(GB_MBC, WARN, "unimplemented MBC: TAMA5");
|
||||
|
@ -401,6 +407,13 @@ void GBMBCInit(struct GB* gb) {
|
|||
case GB_UNL_WISDOM_TREE:
|
||||
gb->memory.mbcWrite = _GBWisdomTree;
|
||||
break;
|
||||
case GB_UNL_NT_NEW:
|
||||
gb->memory.mbcWrite = _GBNTNew;
|
||||
break;
|
||||
case GB_UNL_PKJD:
|
||||
gb->memory.mbcWrite = _GBPKJD;
|
||||
gb->memory.mbcRead = _GBPKJDRead;
|
||||
break;
|
||||
case GB_UNL_BBD:
|
||||
gb->memory.mbcWrite = _GBBBD;
|
||||
gb->memory.mbcRead = _GBBBDRead;
|
||||
|
@ -411,10 +424,6 @@ void GBMBCInit(struct GB* gb) {
|
|||
gb->memory.mbcState.bbd.dataSwapMode = 7;
|
||||
gb->memory.mbcState.bbd.bankSwapMode = 7;
|
||||
break;
|
||||
case GB_UNL_PKJD:
|
||||
gb->memory.mbcWrite = _GBPKJD;
|
||||
gb->memory.mbcRead = _GBPKJDRead;
|
||||
break;
|
||||
}
|
||||
|
||||
gb->memory.currentBank = 1;
|
||||
|
@ -438,6 +447,8 @@ void GBMBCInit(struct GB* gb) {
|
|||
|
||||
if (gb->memory.mbcType == GB_MBC3_RTC) {
|
||||
GBMBCRTCRead(gb);
|
||||
} else if (gb->memory.mbcType == GB_HuC3) {
|
||||
GBMBCHuC3Read(gb);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -786,7 +797,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
case 0x2E:
|
||||
case 0x2F:
|
||||
if (memory->sramAccess) {
|
||||
memory->mbcState.mbc6.sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
|
||||
memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -803,7 +814,7 @@ uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) {
|
|||
case 0xA:
|
||||
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)];
|
||||
case 0xB:
|
||||
return memory->mbcState.mbc6.sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)];
|
||||
return memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)];
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
@ -814,7 +825,7 @@ static void _GBMBC6MapChip(struct GB* gb, int half, uint8_t value) {
|
|||
GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank);
|
||||
} else {
|
||||
gb->memory.mbcState.mbc6.flashBank1 = !!(value & 0x08);
|
||||
GBMBCSwitchHalfBank(gb, half, gb->memory.mbcState.mbc6.currentBank1);
|
||||
GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1101,9 +1112,123 @@ void _GBHuC1(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
}
|
||||
}
|
||||
|
||||
static void _latchHuC3Rtc(struct mRTCSource* rtc, uint8_t* huc3Regs, time_t* rtcLastLatch) {
|
||||
time_t t;
|
||||
if (rtc) {
|
||||
if (rtc->sample) {
|
||||
rtc->sample(rtc);
|
||||
}
|
||||
t = rtc->unixTime(rtc);
|
||||
} else {
|
||||
t = time(0);
|
||||
}
|
||||
t -= *rtcLastLatch;
|
||||
t /= 60;
|
||||
|
||||
if (!t) {
|
||||
return;
|
||||
}
|
||||
*rtcLastLatch += t * 60;
|
||||
|
||||
int minutes = huc3Regs[GBHUC3_RTC_MINUTES_HI] << 8;
|
||||
minutes |= huc3Regs[GBHUC3_RTC_MINUTES_MI] << 4;
|
||||
minutes |= huc3Regs[GBHUC3_RTC_MINUTES_LO];
|
||||
minutes += t % 1440;
|
||||
t /= 1440;
|
||||
if (minutes >= 1440) {
|
||||
minutes -= 1440;
|
||||
++t;
|
||||
} else if (minutes < 0) {
|
||||
minutes += 1440;
|
||||
--t;
|
||||
}
|
||||
huc3Regs[GBHUC3_RTC_MINUTES_LO] = minutes & 0xF;
|
||||
huc3Regs[GBHUC3_RTC_MINUTES_MI] = (minutes >> 4) & 0xF;
|
||||
huc3Regs[GBHUC3_RTC_MINUTES_HI] = (minutes >> 8) & 0xF;
|
||||
|
||||
int days = huc3Regs[GBHUC3_RTC_DAYS_LO];
|
||||
days |= huc3Regs[GBHUC3_RTC_DAYS_MI] << 4;
|
||||
days |= huc3Regs[GBHUC3_RTC_DAYS_HI] << 8;
|
||||
|
||||
days += t;
|
||||
|
||||
huc3Regs[GBHUC3_RTC_DAYS_LO] = days & 0xF;
|
||||
huc3Regs[GBHUC3_RTC_DAYS_MI] = (days >> 4) & 0xF;
|
||||
huc3Regs[GBHUC3_RTC_DAYS_HI] = (days >> 8) & 0xF;
|
||||
}
|
||||
|
||||
static void _huc3Commit(struct GB* gb, struct GBHuC3State* state) {
|
||||
size_t c;
|
||||
switch (state->value & 0x70) {
|
||||
case 0x10:
|
||||
if ((state->index & 0xF8) == 0x10) {
|
||||
_latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch);
|
||||
}
|
||||
state->value &= 0xF0;
|
||||
state->value |= state->registers[state->index] & 0xF;
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 read: %02X:%X", state->index, state->value & 0xF);
|
||||
if (state->value & 0x10) {
|
||||
++state->index;
|
||||
}
|
||||
break;
|
||||
case 0x30:
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 write: %02X:%X", state->index, state->value & 0xF);
|
||||
state->registers[state->index] = state->value & 0xF;
|
||||
if (state->value & 0x10) {
|
||||
++state->index;
|
||||
}
|
||||
break;
|
||||
case 0x40:
|
||||
state->index &= 0xF0;
|
||||
state->index |= (state->value) & 0xF;
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 index (low): %02X", state->index);
|
||||
break;
|
||||
case 0x50:
|
||||
state->index &= 0x0F;
|
||||
state->index |= ((state->value) & 0xF) << 4;
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 index (high): %02X", state->index);
|
||||
break;
|
||||
case 0x60:
|
||||
switch (state->value & 0xF) {
|
||||
case GBHUC3_CMD_LATCH:
|
||||
_latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch);
|
||||
memcpy(state->registers, &state->registers[GBHUC3_RTC_MINUTES_LO], 6);
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 RTC latch");
|
||||
break;
|
||||
case GBHUC3_CMD_SET_RTC:
|
||||
memcpy(&state->registers[GBHUC3_RTC_MINUTES_LO], state->registers, 6);
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 set RTC");
|
||||
break;
|
||||
case GBHUC3_CMD_RO:
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unimplemented read-only mode");
|
||||
break;
|
||||
case GBHUC3_CMD_TONE:
|
||||
if (state->registers[GBHUC3_SPEAKER_ENABLE] == 1) {
|
||||
for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c);
|
||||
if (callbacks->alarm) {
|
||||
callbacks->alarm(callbacks->context);
|
||||
}
|
||||
}
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 tone %i", state->registers[GBHUC3_SPEAKER_TONE] & 3);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown command: %X", state->value & 0xF);
|
||||
break;
|
||||
}
|
||||
state->value = 0xE1;
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown mode commit: %02X:%02X", state->index, state->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int bank = value & 0x3F;
|
||||
struct GBHuC3State* state = &memory->mbcState.huc3;
|
||||
int bank = value & 0x7F;
|
||||
if (address & 0x1FFF) {
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown value %04X:%02X", address, value);
|
||||
}
|
||||
|
@ -1119,6 +1244,7 @@ void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
memory->sramAccess = false;
|
||||
break;
|
||||
}
|
||||
state->mode = value;
|
||||
break;
|
||||
case 0x1:
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
|
@ -1126,6 +1252,18 @@ void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
case 0x2:
|
||||
GBMBCSwitchSramBank(gb, bank);
|
||||
break;
|
||||
case 0x5:
|
||||
switch (state->mode) {
|
||||
case GBHUC3_MODE_IN:
|
||||
state->value = 0x80 | value;
|
||||
break;
|
||||
case GBHUC3_MODE_COMMIT:
|
||||
_huc3Commit(gb, state);
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown mode write: %02X:%02X", state->mode, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value);
|
||||
|
@ -1133,6 +1271,20 @@ void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t _GBHuC3Read(struct GBMemory* memory, uint16_t address) {
|
||||
struct GBHuC3State* state = &memory->mbcState.huc3;
|
||||
switch (state->mode) {
|
||||
case GBHUC3_MODE_SRAM_RO:
|
||||
case GBHUC3_MODE_SRAM_RW:
|
||||
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
|
||||
case GBHUC3_MODE_IN:
|
||||
case GBHUC3_MODE_OUT:
|
||||
return 0x80 | state->value;
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int bank = value & 0x3F;
|
||||
|
@ -1432,6 +1584,29 @@ static uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) {
|
|||
}
|
||||
}
|
||||
|
||||
void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
if (address >> 8 == 0x14) {
|
||||
memory->mbcState.ntNew.splitMode = true;
|
||||
return;
|
||||
}
|
||||
if (memory->mbcState.ntNew.splitMode) {
|
||||
int bank = value;
|
||||
if (bank < 2) {
|
||||
bank = 2;
|
||||
}
|
||||
switch (address >> 10) {
|
||||
case 8:
|
||||
GBMBCSwitchHalfBank(gb, 0, bank);
|
||||
return;
|
||||
case 9:
|
||||
GBMBCSwitchHalfBank(gb, 1, bank);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_GBMBC5(gb, address, value);
|
||||
}
|
||||
|
||||
static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) {
|
||||
uint8_t newbyte = 0;
|
||||
int i;
|
||||
|
@ -1549,6 +1724,21 @@ uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) {
|
|||
}
|
||||
}
|
||||
|
||||
static void _appendSaveSuffix(struct GB* gb, const void* buffer, size_t size) {
|
||||
struct VFile* vf = gb->sramVf;
|
||||
if ((size_t) vf->size(vf) < gb->sramSize + size) {
|
||||
// Writing past the end of the file can invalidate the file mapping
|
||||
vf->unmap(vf, gb->memory.sram, gb->sramSize);
|
||||
gb->memory.sram = NULL;
|
||||
}
|
||||
vf->seek(vf, gb->sramSize, SEEK_SET);
|
||||
vf->write(vf, buffer, size);
|
||||
if (!gb->memory.sram) {
|
||||
gb->memory.sram = vf->map(vf, gb->sramSize, MAP_WRITE);
|
||||
GBMBCSwitchSramBank(gb, gb->memory.sramCurrentBank);
|
||||
}
|
||||
}
|
||||
|
||||
void GBMBCRTCRead(struct GB* gb) {
|
||||
struct GBMBCRTCSaveBuffer rtcBuffer;
|
||||
struct VFile* vf = gb->sramVf;
|
||||
|
@ -1592,15 +1782,41 @@ void GBMBCRTCWrite(struct GB* gb) {
|
|||
STORE_32LE(gb->memory.rtcRegs[4], 0, &rtcBuffer.latchedDaysHi);
|
||||
STORE_64LE(gb->memory.rtcLastLatch, 0, &rtcBuffer.unixTime);
|
||||
|
||||
if ((size_t) vf->size(vf) < gb->sramSize + sizeof(rtcBuffer)) {
|
||||
// Writing past the end of the file can invalidate the file mapping
|
||||
vf->unmap(vf, gb->memory.sram, gb->sramSize);
|
||||
gb->memory.sram = NULL;
|
||||
_appendSaveSuffix(gb, &rtcBuffer, sizeof(rtcBuffer));
|
||||
}
|
||||
|
||||
void GBMBCHuC3Read(struct GB* gb) {
|
||||
struct GBMBCHuC3SaveBuffer buffer;
|
||||
struct VFile* vf = gb->sramVf;
|
||||
if (!vf) {
|
||||
return;
|
||||
}
|
||||
vf->seek(vf, gb->sramSize, SEEK_SET);
|
||||
vf->write(vf, &rtcBuffer, sizeof(rtcBuffer));
|
||||
if (!gb->memory.sram) {
|
||||
gb->memory.sram = vf->map(vf, gb->sramSize, MAP_WRITE);
|
||||
GBMBCSwitchSramBank(gb, gb->memory.sramCurrentBank);
|
||||
if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < 0x80; ++i) {
|
||||
gb->memory.mbcState.huc3.registers[i * 2] = buffer.regs[i] & 0xF;
|
||||
gb->memory.mbcState.huc3.registers[i * 2 + 1] = buffer.regs[i] >> 4;
|
||||
}
|
||||
LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
|
||||
}
|
||||
|
||||
void GBMBCHuC3Write(struct GB* gb) {
|
||||
struct VFile* vf = gb->sramVf;
|
||||
if (!vf) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct GBMBCHuC3SaveBuffer buffer;
|
||||
size_t i;
|
||||
for (i = 0; i < 0x80; ++i) {
|
||||
buffer.regs[i] = gb->memory.mbcState.huc3.registers[i * 2] & 0xF;
|
||||
buffer.regs[i] |= gb->memory.mbcState.huc3.registers[i * 2 + 1] << 4;
|
||||
}
|
||||
STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
|
||||
|
||||
_appendSaveSuffix(gb, &buffer, sizeof(buffer));
|
||||
}
|
||||
|
|
|
@ -95,14 +95,14 @@ static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) {
|
|||
break;
|
||||
}
|
||||
cpu->memory.cpuLoad8 = GBCartLoad8;
|
||||
if (gb->memory.mbcType != GB_MBC6) {
|
||||
if (gb->memory.mbcType != GB_MBC6 && !(gb->memory.mbcType == GB_UNL_NT_NEW && gb->memory.mbcState.ntNew.splitMode)) {
|
||||
cpu->memory.activeRegion = memory->romBank;
|
||||
cpu->memory.activeRegionEnd = GB_BASE_VRAM;
|
||||
cpu->memory.activeMask = GB_SIZE_CART_BANK0 - 1;
|
||||
} else {
|
||||
cpu->memory.activeMask = GB_SIZE_CART_HALFBANK - 1;
|
||||
if (address & 0x2000) {
|
||||
cpu->memory.activeRegion = memory->mbcState.mbc6.romBank1;
|
||||
cpu->memory.activeRegion = memory->romBank1;
|
||||
cpu->memory.activeRegionEnd = GB_BASE_VRAM;
|
||||
} else {
|
||||
cpu->memory.activeRegion = memory->romBank;
|
||||
|
@ -252,8 +252,8 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) {
|
|||
return memory->cartBus;
|
||||
case GB_REGION_CART_BANK1 + 2:
|
||||
case GB_REGION_CART_BANK1 + 3:
|
||||
if (memory->mbcType == GB_MBC6) {
|
||||
memory->cartBus = memory->mbcState.mbc6.romBank1[address & (GB_SIZE_CART_HALFBANK - 1)];
|
||||
if (gb->memory.mbcType == GB_MBC6 || (gb->memory.mbcType == GB_UNL_NT_NEW && gb->memory.mbcState.ntNew.splitMode)) {
|
||||
memory->cartBus = memory->romBank1[address & (GB_SIZE_CART_HALFBANK - 1)];
|
||||
memory->cartBusPc = cpu->pc;
|
||||
return memory->cartBus;
|
||||
}
|
||||
|
@ -726,6 +726,7 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
|||
state->memory.cartBus = memory->cartBus;
|
||||
STORE_16LE(memory->cartBusPc, 0, &state->cartBusPc);
|
||||
|
||||
int i;
|
||||
switch (memory->mbcType) {
|
||||
case GB_MBC1:
|
||||
state->memory.mbc1.mode = memory->mbcState.mbc1.mode;
|
||||
|
@ -734,7 +735,7 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
|||
state->memory.mbc1.bankHi = memory->mbcState.mbc1.bankHi;
|
||||
break;
|
||||
case GB_MBC3_RTC:
|
||||
STORE_64LE(gb->memory.rtcLastLatch, 0, &state->memory.rtc.lastLatch);
|
||||
STORE_64LE(memory->rtcLastLatch, 0, &state->memory.rtc.lastLatch);
|
||||
break;
|
||||
case GB_MBC7:
|
||||
state->memory.mbc7.state = memory->mbcState.mbc7.state;
|
||||
|
@ -746,6 +747,16 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
|||
STORE_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr);
|
||||
STORE_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable);
|
||||
break;
|
||||
case GB_HuC3:
|
||||
STORE_64LE(memory->rtcLastLatch, 0, &state->memory.huc3.lastLatch);
|
||||
state->memory.huc3.index = memory->mbcState.huc3.index;
|
||||
state->memory.huc3.value = memory->mbcState.huc3.value;
|
||||
state->memory.huc3.mode = memory->mbcState.huc3.mode;
|
||||
for (i = 0; i < 0x80; ++i) {
|
||||
state->huc3Registers[i] = memory->mbcState.huc3.registers[i * 2] & 0xF;
|
||||
state->huc3Registers[i] |= memory->mbcState.huc3.registers[i * 2 + 1] << 4;
|
||||
}
|
||||
break;
|
||||
case GB_MMM01:
|
||||
state->memory.mmm01.locked = memory->mbcState.mmm01.locked;
|
||||
state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0;
|
||||
|
@ -808,6 +819,7 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
memory->cartBus = state->memory.cartBus;
|
||||
LOAD_16LE(memory->cartBusPc, 0, &state->cartBusPc);
|
||||
|
||||
int i;
|
||||
switch (memory->mbcType) {
|
||||
case GB_MBC1:
|
||||
memory->mbcState.mbc1.mode = state->memory.mbc1.mode;
|
||||
|
@ -824,7 +836,7 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
}
|
||||
break;
|
||||
case GB_MBC3_RTC:
|
||||
LOAD_64LE(gb->memory.rtcLastLatch, 0, &state->memory.rtc.lastLatch);
|
||||
LOAD_64LE(memory->rtcLastLatch, 0, &state->memory.rtc.lastLatch);
|
||||
break;
|
||||
case GB_MBC7:
|
||||
memory->mbcState.mbc7.state = state->memory.mbc7.state;
|
||||
|
@ -836,6 +848,16 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
LOAD_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr);
|
||||
LOAD_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable);
|
||||
break;
|
||||
case GB_HuC3:
|
||||
LOAD_64LE(memory->rtcLastLatch, 0, &state->memory.huc3.lastLatch);
|
||||
memory->mbcState.huc3.index = state->memory.huc3.index;
|
||||
memory->mbcState.huc3.value = state->memory.huc3.value;
|
||||
memory->mbcState.huc3.mode = state->memory.huc3.mode;
|
||||
for (i = 0; i < 0x80; ++i) {
|
||||
memory->mbcState.huc3.registers[i * 2] = state->huc3Registers[i] & 0xF;
|
||||
memory->mbcState.huc3.registers[i * 2 + 1] = state->huc3Registers[i] >> 4;
|
||||
}
|
||||
break;
|
||||
case GB_MMM01:
|
||||
memory->mbcState.mmm01.locked = state->memory.mmm01.locked;
|
||||
memory->mbcState.mmm01.currentBank0 = state->memory.mmm01.bank0;
|
||||
|
|
|
@ -668,11 +668,21 @@ static const struct GBCartridgeOverride _sgbOverrides[] = {
|
|||
|
||||
static const struct GBCartridgeOverride _overrides[] = {
|
||||
// Pokemon Spaceworld 1997 demo
|
||||
{ 0x232a067d, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (debug)
|
||||
{ 0x630ed957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug)
|
||||
{ 0x5aff0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug)
|
||||
{ 0xa61856bd, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-debug)
|
||||
{ 0x30f8f86c, GB_MODEL_AUTODETECT, GB_UNL_PKJD, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg)
|
||||
{ 0x232A067D, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (debug)
|
||||
{ 0x630ED957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug)
|
||||
{ 0x5AFF0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug)
|
||||
{ 0xA61856BD, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-debug)
|
||||
{ 0x30F8F86C, GB_MODEL_AUTODETECT, GB_UNL_PKJD, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg)
|
||||
{ 0xB289D95A, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Capcom vs SNK - Millennium Fight 2001
|
||||
{ 0x688D6713, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 02 4
|
||||
{ 0x8931A272, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 2
|
||||
{ 0x79083C6B, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon Pocket
|
||||
{ 0x0C5047EE, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Harry Potter 3
|
||||
{ 0x8AC634B7, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Pokemon Diamond (Special Pikachu Edition)
|
||||
{ 0x8628A287, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Pokemon Jade (Special Pikachu Edition)
|
||||
{ 0xBC75D7B8, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Pokemon - Mewtwo Strikes Back
|
||||
{ 0xFF0B60CC, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Shuma Baolong 02 4
|
||||
{ 0x14A992A6, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // /Street Fighter Zero 4
|
||||
|
||||
{ 0, 0, 0, { 0 } }
|
||||
};
|
||||
|
|
|
@ -34,9 +34,10 @@ static const QList<GBMemoryBankControllerType> s_mbcList{
|
|||
GB_HuC1,
|
||||
GB_HuC3,
|
||||
GB_UNL_WISDOM_TREE,
|
||||
GB_UNL_PKJD,
|
||||
GB_UNL_NT_NEW,
|
||||
GB_UNL_BBD,
|
||||
GB_UNL_HITEK,
|
||||
GB_UNL_PKJD,
|
||||
};
|
||||
|
||||
static QMap<GBModel, QString> s_gbModelNames;
|
||||
|
@ -87,6 +88,7 @@ QString GameBoy::mbcName(GBMemoryBankControllerType mbc) {
|
|||
s_mbcNames[GB_POCKETCAM] = tr("Pocket Cam");
|
||||
s_mbcNames[GB_TAMA5] = tr("TAMA5");
|
||||
s_mbcNames[GB_UNL_WISDOM_TREE] = tr("Wisdom Tree");
|
||||
s_mbcNames[GB_UNL_NT_NEW] = tr("NT (new)");
|
||||
s_mbcNames[GB_UNL_PKJD] = tr("Pokémon Jade/Diamond");
|
||||
s_mbcNames[GB_UNL_BBD] = tr("BBD");
|
||||
s_mbcNames[GB_UNL_HITEK] = tr("Hitek");
|
||||
|
|
Loading…
Reference in New Issue