mirror of
https://github.com/libretro/fixGB.git
synced 2024-11-23 09:09:40 +00:00
251e12cf98
-added support for one of the multicarts I own because why not -various small updates regarding the libretro hook, including better handling of rom switching and saving
598 lines
11 KiB
C
598 lines
11 KiB
C
/*
|
|
* Copyright (C) 2017 FIX94
|
|
*
|
|
* This software may be modified and distributed under the terms
|
|
* of the MIT license. See the LICENSE file for details.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include "mem.h"
|
|
#include "mbc.h"
|
|
|
|
uint8_t Ext_Mem[0x20000];
|
|
set8FuncT mbcSet8;
|
|
set8FuncT mbcSetRAM8;
|
|
get8FuncT mbcGetRAM8;
|
|
|
|
//multicart regs
|
|
uint16_t tBank0;
|
|
uint16_t tBank1;
|
|
uint16_t oBank;
|
|
uint16_t iBank;
|
|
uint16_t oBankAnd;
|
|
uint16_t iBankAnd;
|
|
uint8_t mcState;
|
|
bool mcLocked;
|
|
|
|
//normal regs
|
|
uint8_t rtcReg;
|
|
uint16_t cBank;
|
|
uint16_t extBank;
|
|
uint16_t bankMask;
|
|
uint16_t extMask;
|
|
uint16_t extAddrMask;
|
|
size_t extTotalSize;
|
|
|
|
bool RamIOAllowed;
|
|
bool extMemEnabled;
|
|
bool bankUsed;
|
|
bool extSelect;
|
|
bool rtcUsed;
|
|
bool rtcEnabled;
|
|
|
|
uint8_t lastRTCval;
|
|
|
|
//used in for example VBA
|
|
static struct RTCSave_t {
|
|
int32_t secs;
|
|
int32_t mins;
|
|
int32_t hours;
|
|
int32_t days;
|
|
int32_t ctrl;
|
|
int32_t lsecs;
|
|
int32_t lmins;
|
|
int32_t lhours;
|
|
int32_t ldays;
|
|
int32_t lctrl;
|
|
int64_t lastTime;
|
|
} RTCSave;
|
|
|
|
static void noSet8(uint16_t addr, uint8_t val);
|
|
static void mbc1Set8(uint16_t addr, uint8_t val);
|
|
static void mbc1mcSet8(uint16_t addr, uint8_t val);
|
|
static void mbc2Set8(uint16_t addr, uint8_t val);
|
|
static void mbc3Set8(uint16_t addr, uint8_t val);
|
|
static void mbc5Set8(uint16_t addr, uint8_t val);
|
|
static void gbsSet8(uint16_t addr, uint8_t val);
|
|
static void mbcRTCUpdate();
|
|
|
|
static uint8_t mbcGetExtRAMBank8(uint16_t addr);
|
|
static void mbcSetExtRAMBank8(uint16_t addr, uint8_t val);
|
|
static uint8_t mbcGetExtRAMNoBank8(uint16_t addr);
|
|
static void mbcSetExtRAMNoBank8(uint16_t addr, uint8_t val);
|
|
static uint8_t mbcGetExtRAMRtc8(uint16_t addr);
|
|
static void mbcSetExtRAMRtc8(uint16_t addr, uint8_t val);
|
|
static uint8_t mbc2GetExtRAM8(uint16_t addr);
|
|
static void mbc2SetExtRAM8(uint16_t addr, uint8_t val);
|
|
static uint8_t mbcGetNoExtRAM8(uint16_t addr);
|
|
static void mbcSetNoExtRAM8(uint16_t addr, uint8_t val);
|
|
|
|
extern bool gbIsMulticart;
|
|
void mbcInit(uint8_t type)
|
|
{
|
|
if(gbIsMulticart)
|
|
mbcSet8 = mbc1mcSet8;
|
|
else if(type == MBC_TYPE_1)
|
|
mbcSet8 = mbc1Set8;
|
|
else if(type == MBC_TYPE_2)
|
|
mbcSet8 = mbc2Set8;
|
|
else if(type == MBC_TYPE_3)
|
|
mbcSet8 = mbc3Set8;
|
|
else if(type == MBC_TYPE_5)
|
|
mbcSet8 = mbc5Set8;
|
|
else if(type == MBC_TYPE_GBS)
|
|
mbcSet8 = gbsSet8;
|
|
else
|
|
mbcSet8 = noSet8;
|
|
|
|
if(rtcUsed)
|
|
{
|
|
mbcGetRAM8 = mbcGetExtRAMRtc8;
|
|
mbcSetRAM8 = mbcSetExtRAMRtc8;
|
|
printf("MBC: Set RAM+RTC Functions\n");
|
|
}
|
|
else if(extMemEnabled)
|
|
{
|
|
if(type == MBC_TYPE_2)
|
|
{
|
|
mbcGetRAM8 = mbc2GetExtRAM8;
|
|
mbcSetRAM8 = mbc2SetExtRAM8;
|
|
printf("MBC: Set Special MBC2 RAM Functions\n");
|
|
}
|
|
else if(extTotalSize < 0x2000)
|
|
{
|
|
mbcGetRAM8 = mbcGetExtRAMNoBank8;
|
|
mbcSetRAM8 = mbcSetExtRAMNoBank8;
|
|
printf("MBC: Set RAM (No Bank) Functions\n");
|
|
}
|
|
else if(type == MBC_TYPE_GBS)
|
|
{
|
|
//GBS has 0x2000 bytes RAM but has no Bank or I/O Regs!
|
|
mbcGetRAM8 = mbcGetExtRAMNoBank8;
|
|
mbcSetRAM8 = mbcSetExtRAMNoBank8;
|
|
printf("MBC: Set GBS RAM (No Bank) Functions\n");
|
|
}
|
|
else
|
|
{
|
|
mbcGetRAM8 = mbcGetExtRAMBank8;
|
|
mbcSetRAM8 = mbcSetExtRAMBank8;
|
|
printf("MBC: Set Normal RAM Functions\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mbcGetRAM8 = mbcGetNoExtRAM8;
|
|
mbcSetRAM8 = mbcSetNoExtRAM8;
|
|
printf("MBC: No RAM Functions\n");
|
|
}
|
|
mbcExtRAMInit(type);
|
|
}
|
|
|
|
void mbcResetRegs()
|
|
{
|
|
//multicart regs
|
|
tBank0 = 0, tBank1 = 1;
|
|
oBank = 0, iBank = 1;
|
|
oBankAnd = 0x20, iBankAnd = 0x3F;
|
|
mcState = 0, mcLocked = false;
|
|
//normal regs
|
|
rtcReg = 0;
|
|
cBank = 1;
|
|
bankMask = 1;
|
|
extBank = 0;
|
|
extMask = 0;
|
|
extAddrMask = 0;
|
|
extTotalSize = 0;
|
|
RamIOAllowed = false;
|
|
extMemEnabled = false;
|
|
bankUsed = false;
|
|
extSelect = false;
|
|
rtcUsed = false;
|
|
rtcEnabled = false;
|
|
lastRTCval = 0;
|
|
}
|
|
|
|
static void noSet8(uint16_t addr, uint8_t val)
|
|
{
|
|
(void)addr;
|
|
(void)val;
|
|
}
|
|
|
|
static void mbc1Set8(uint16_t addr, uint8_t val)
|
|
{
|
|
if(addr < 0x2000)
|
|
RamIOAllowed = ((val&0xF) == 0xA);
|
|
else if(addr >= 0x2000 && addr < 0x4000)
|
|
{
|
|
if(bankUsed)
|
|
{
|
|
//printf("%02x\n",val);
|
|
cBank &= ~0x1F;
|
|
cBank |= val&0x1F;
|
|
if((cBank&0x1F) == 0)
|
|
cBank |= 1;
|
|
cBank &= bankMask;
|
|
}
|
|
}
|
|
else if(addr >= 0x4000 && addr < 0x6000)
|
|
{
|
|
if(extSelect)
|
|
{
|
|
if(extMemEnabled)
|
|
{
|
|
extBank = val&3;
|
|
extBank &= extMask;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(bankUsed)
|
|
{
|
|
//printf("%02x\n",val);
|
|
cBank &= 0x1F;
|
|
cBank |= ((val&3)<<5);
|
|
if((cBank&0x1F) == 0)
|
|
cBank |= 1;
|
|
cBank &= bankMask;
|
|
}
|
|
}
|
|
}
|
|
else if(addr >= 0x6000 && addr < 0x8000)
|
|
extSelect = !!val;
|
|
}
|
|
|
|
static void mbc1mcSet8(uint16_t addr, uint8_t val)
|
|
{
|
|
if(addr >= 0x2000 && addr < 0x4000)
|
|
{
|
|
iBank = val&0x3F;
|
|
if(iBank == 0)
|
|
iBank |= 1;
|
|
tBank1 = ((oBank&oBankAnd)<<1)+(iBank&iBankAnd);
|
|
}
|
|
else if(addr >= 0x6000 && addr < 0x8000 && !mcLocked)
|
|
{
|
|
if(mcState == 0)
|
|
{
|
|
oBank = val&0x3F;
|
|
tBank0 = ((oBank&oBankAnd)<<1);
|
|
tBank1 = ((oBank&oBankAnd)<<1)+(iBank&iBankAnd);
|
|
}
|
|
else
|
|
{
|
|
iBankAnd=(~(val<<1))&0x3F;
|
|
oBankAnd=0x20|(val&0x1F);
|
|
tBank0 = ((oBank&oBankAnd)<<1);
|
|
tBank1 = ((oBank&oBankAnd)<<1)+(iBank&iBankAnd);
|
|
mcLocked = !!(val&0x20);
|
|
}
|
|
mcState^=1;
|
|
}
|
|
}
|
|
|
|
static void mbc2Set8(uint16_t addr, uint8_t val)
|
|
{
|
|
if(addr < 0x2000 && ((addr&0x100) == 0))
|
|
RamIOAllowed = ((val&0xF) == 0xA);
|
|
else if(addr >= 0x2000 && addr < 0x4000 && ((addr&0x100) == 0x100))
|
|
{
|
|
if(bankUsed)
|
|
{
|
|
cBank = val&0x7F;
|
|
if(cBank == 0)
|
|
cBank |= 1;
|
|
cBank &= bankMask;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mbc3Set8(uint16_t addr, uint8_t val)
|
|
{
|
|
if(addr < 0x2000)
|
|
RamIOAllowed = ((val&0xF) == 0xA);
|
|
else if(addr >= 0x2000 && addr < 0x4000)
|
|
{
|
|
if(bankUsed)
|
|
{
|
|
//printf("%02x\n",val);
|
|
cBank = val&0x7F;
|
|
if(cBank == 0)
|
|
cBank |= 1;
|
|
cBank &= bankMask;
|
|
}
|
|
}
|
|
else if(addr >= 0x4000 && addr < 0x6000)
|
|
{
|
|
if((val & 0xC) == 0)
|
|
{
|
|
if(extMemEnabled)
|
|
{
|
|
extBank = val&3;
|
|
extBank &= extMask;
|
|
}
|
|
rtcEnabled = false;
|
|
}
|
|
else if(rtcUsed)
|
|
{
|
|
rtcReg = val&0xF;
|
|
rtcEnabled = true;
|
|
}
|
|
}
|
|
else if(addr >= 0x6000 && addr < 0x8000)
|
|
{
|
|
//update latched regs with current vals
|
|
if(lastRTCval == 0 && val == 1)
|
|
{
|
|
mbcRTCUpdate();
|
|
RTCSave.lsecs = RTCSave.secs;
|
|
RTCSave.lmins = RTCSave.mins;
|
|
RTCSave.lhours = RTCSave.hours;
|
|
RTCSave.ldays = RTCSave.days;
|
|
RTCSave.lctrl = RTCSave.ctrl;
|
|
}
|
|
lastRTCval = val;
|
|
}
|
|
}
|
|
|
|
static void mbc5Set8(uint16_t addr, uint8_t val)
|
|
{
|
|
if(addr < 0x2000)
|
|
RamIOAllowed = ((val&0xF) == 0xA);
|
|
else if(addr >= 0x2000 && addr < 0x3000)
|
|
{
|
|
if(bankUsed)
|
|
{
|
|
cBank &= ~0xFF;
|
|
cBank |= val;
|
|
cBank &= bankMask;
|
|
}
|
|
}
|
|
else if(addr >= 0x3000 && addr < 0x4000)
|
|
{
|
|
if(bankUsed)
|
|
{
|
|
cBank &= 0xFF;
|
|
cBank |= (val&1)<<8;
|
|
cBank &= bankMask;
|
|
}
|
|
}
|
|
else if(addr >= 0x4000 && addr < 0x6000)
|
|
{
|
|
if(extMemEnabled)
|
|
{
|
|
extBank = val&0xF;
|
|
extBank &= extMask;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void gbsSet8(uint16_t addr, uint8_t val)
|
|
{
|
|
if(addr >= 0x2000 && addr < 0x3000)
|
|
{
|
|
//Some GBS files seem to follow VERY strange behaviour
|
|
//similar to the MBC1 but mixing in bank mask
|
|
cBank = (val&bankMask);
|
|
if(cBank == 0)
|
|
cBank |= 1;
|
|
}
|
|
}
|
|
|
|
void mbcExtRAMInit(uint8_t type)
|
|
{
|
|
if(extTotalSize == 0)
|
|
printf("MBC: No RAM Cleared\n");
|
|
else if(type == MBC_TYPE_2)
|
|
{
|
|
printf("MBC: Cleared MBC2 RAM\n");
|
|
memset(Ext_Mem,0xF0,0x200);
|
|
}
|
|
else
|
|
{
|
|
printf("MBC: Cleared Normal RAM\n");
|
|
memset(Ext_Mem,0,extTotalSize);
|
|
}
|
|
}
|
|
|
|
void mbcExtRAMGBSClear()
|
|
{
|
|
memset(Ext_Mem,0,0x2000);
|
|
}
|
|
|
|
void mbcExtRAMLoad(FILE *f)
|
|
{
|
|
fread(Ext_Mem,1,extTotalSize,f);
|
|
printf("MBC: Read in saved game\n");
|
|
}
|
|
|
|
void mbcExtRAMStore(FILE *f)
|
|
{
|
|
printf("MBC: Saved game\n");
|
|
fwrite(Ext_Mem,1,extTotalSize,f);
|
|
}
|
|
|
|
//Regular RAM for regular Controllers
|
|
uint8_t mbcGetExtRAMBank8(uint16_t addr)
|
|
{
|
|
if(RamIOAllowed)
|
|
return Ext_Mem[((extBank<<13)|(addr&extAddrMask))];
|
|
return 0xFF;
|
|
}
|
|
|
|
void mbcSetExtRAMBank8(uint16_t addr, uint8_t val)
|
|
{
|
|
if(RamIOAllowed)
|
|
Ext_Mem[((extBank<<13)|(addr&extAddrMask))] = val;
|
|
}
|
|
|
|
//Allow Only 4 Bits to read/write
|
|
static uint8_t mbc2GetExtRAM8(uint16_t addr)
|
|
{
|
|
if(RamIOAllowed)
|
|
return Ext_Mem[addr&0x1FF] | 0xF0;
|
|
return 0xFF;
|
|
}
|
|
|
|
static void mbc2SetExtRAM8(uint16_t addr, uint8_t val)
|
|
{
|
|
if(RamIOAllowed)
|
|
Ext_Mem[addr&0x1FF] = val | 0xF0;
|
|
}
|
|
|
|
//No Banks and No RAM IO Regs to be set
|
|
static uint8_t mbcGetExtRAMNoBank8(uint16_t addr)
|
|
{
|
|
return Ext_Mem[addr&extAddrMask];
|
|
}
|
|
|
|
static void mbcSetExtRAMNoBank8(uint16_t addr, uint8_t val)
|
|
{
|
|
Ext_Mem[addr&extAddrMask] = val;
|
|
}
|
|
|
|
//No RAM, just dummy functions
|
|
static uint8_t mbcGetNoExtRAM8(uint16_t addr)
|
|
{
|
|
(void)addr;
|
|
return 0xFF;
|
|
}
|
|
|
|
static void mbcSetNoExtRAM8(uint16_t addr, uint8_t val)
|
|
{
|
|
(void)addr;
|
|
(void)val;
|
|
}
|
|
|
|
size_t mbcRTCSize()
|
|
{
|
|
return sizeof(RTCSave);
|
|
}
|
|
|
|
void mbcRTCInit()
|
|
{
|
|
rtcUsed = true;
|
|
//Set default values already in
|
|
//case no save exists
|
|
time_t curtime;
|
|
time(&curtime);
|
|
struct tm *lt = localtime(&curtime);
|
|
RTCSave.secs = lt->tm_sec;
|
|
RTCSave.mins = lt->tm_min;
|
|
RTCSave.hours = lt->tm_hour;
|
|
RTCSave.days = lt->tm_yday & 255;
|
|
RTCSave.ctrl = (lt->tm_yday > 255 ? 1: 0);
|
|
RTCSave.lastTime = curtime;
|
|
printf("MBC: RTC allowed\n");
|
|
}
|
|
|
|
static void mbcRTCUpdate()
|
|
{
|
|
if(RTCSave.ctrl & 0x40) //Halted!
|
|
return;
|
|
|
|
time_t curtime;
|
|
time(&curtime);
|
|
time_t diff = curtime - ((time_t)RTCSave.lastTime);
|
|
if(diff == 0) //No Time Diff!
|
|
return;
|
|
|
|
RTCSave.secs += diff % 60;
|
|
if(RTCSave.secs > 59)
|
|
{
|
|
RTCSave.secs -= 60;
|
|
RTCSave.mins++;
|
|
}
|
|
|
|
diff /= 60;
|
|
|
|
RTCSave.mins += diff % 60;
|
|
if(RTCSave.mins > 60)
|
|
{
|
|
RTCSave.mins -= 60;
|
|
RTCSave.hours++;
|
|
}
|
|
|
|
diff /= 60;
|
|
|
|
RTCSave.hours += diff % 24;
|
|
if(RTCSave.hours > 24)
|
|
{
|
|
RTCSave.hours -= 24;
|
|
RTCSave.days++;
|
|
}
|
|
diff /= 24;
|
|
|
|
RTCSave.days += diff;
|
|
if(RTCSave.days > 511)
|
|
{
|
|
RTCSave.days %= 512;
|
|
RTCSave.ctrl |= (RTCSave.ctrl&0x7E)|0x80|(RTCSave.days > 255 ? 1 : 0);
|
|
}
|
|
RTCSave.lastTime = curtime;
|
|
}
|
|
|
|
void mbcRTCLoad(FILE *f)
|
|
{
|
|
fread(&RTCSave,1,mbcRTCSize(),f);
|
|
printf("MBC: Read in RTC Save\n");
|
|
//refresh timestamps
|
|
mbcRTCUpdate();
|
|
}
|
|
|
|
void mbcRTCStore(FILE *f)
|
|
{
|
|
//update regs one last time before saving
|
|
mbcRTCUpdate();
|
|
fwrite(&RTCSave,1,mbcRTCSize(),f);
|
|
printf("MBC: Saved RTC\n");
|
|
}
|
|
|
|
uint8_t mbcGetExtRAMRtc8(uint16_t addr)
|
|
{
|
|
if(!RamIOAllowed)
|
|
return 0xFF;
|
|
if(rtcEnabled)
|
|
{
|
|
uint8_t ret = 0xFF;
|
|
//return currently latched regs
|
|
switch(rtcReg)
|
|
{
|
|
case 0x8:
|
|
ret = RTCSave.lsecs;
|
|
break;
|
|
case 0x9:
|
|
ret = RTCSave.lmins;
|
|
break;
|
|
case 0xA:
|
|
ret = RTCSave.lhours;
|
|
break;
|
|
case 0xB:
|
|
ret = RTCSave.ldays;
|
|
break;
|
|
case 0xC:
|
|
ret = RTCSave.lctrl;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
else if(extMemEnabled)
|
|
{
|
|
uint8_t ret = Ext_Mem[((extBank<<13)|(addr&extAddrMask))];
|
|
return ret;
|
|
}
|
|
return 0xFF;
|
|
}
|
|
|
|
void mbcSetExtRAMRtc8(uint16_t addr, uint8_t val)
|
|
{
|
|
if(!RamIOAllowed)
|
|
return;
|
|
if(rtcEnabled)
|
|
{
|
|
//refresh time to set time of write
|
|
time_t curtime;
|
|
time(&curtime);
|
|
RTCSave.lastTime = curtime;
|
|
//write into rtc regs
|
|
switch(rtcReg)
|
|
{
|
|
case 0x08:
|
|
RTCSave.secs = val;
|
|
break;
|
|
case 0x09:
|
|
RTCSave.mins = val;
|
|
break;
|
|
case 0x0A:
|
|
RTCSave.hours = val;
|
|
break;
|
|
case 0x0B:
|
|
RTCSave.days = (RTCSave.days&0x100)|val;
|
|
break;
|
|
case 0x0C:
|
|
RTCSave.ctrl = val;
|
|
RTCSave.days = (RTCSave.days&0xFF)|((val&1)<<8);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if(extMemEnabled)
|
|
Ext_Mem[((extBank<<13)|(addr&extAddrMask))] = val;
|
|
}
|