gba: saveram support. not tested too much because controller isn't hooked up yet. (and path config appears to be dumping them in the wrong place). also remove the silly system where the core tracks timing on when to write the saveram file to disk.

This commit is contained in:
goyuken 2012-11-24 02:25:47 +00:00
parent af355f6d5b
commit 235d7a9a03
11 changed files with 187 additions and 55 deletions

View File

@ -55,7 +55,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.GBA
/// <param name="datalen">length of data in bytes</param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_loadrom(byte[] data, uint datalen);
/// <summary>
/// load a bios image
/// </summary>
@ -85,16 +85,16 @@ namespace BizHawk.Emulation.Consoles.Nintendo.GBA
[Flags]
public enum Buttons : ushort
{
BTN_A = 0x001,
BTN_B = 0x002,
BTN_A = 0x001,
BTN_B = 0x002,
BTN_SELECT = 0x004,
BTN_START = 0x008,
BTN_RIGHT = 0x010,
BTN_LEFT = 0x020,
BTN_UP = 0x040,
BTN_DOWN = 0x080,
BTN_R = 0x100,
BTN_L = 0x200
BTN_START = 0x008,
BTN_RIGHT = 0x010,
BTN_LEFT = 0x020,
BTN_UP = 0x040,
BTN_DOWN = 0x080,
BTN_R = 0x100,
BTN_L = 0x200
}
/// <summary>
@ -146,5 +146,43 @@ namespace BizHawk.Emulation.Consoles.Nintendo.GBA
/// <param name="callback">null to clear</param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_settracecallback(TraceCallback callback);
/// <summary>
/// load saveram from a byte buffer
/// </summary>
/// <param name="data"></param>
/// <param name="size"></param>
/// <returns>success</returns>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool libmeteor_loadsaveram(byte[] data, uint size);
/// <summary>
/// save saveram to a byte buffer
/// </summary>
/// <param name="data">buffer generated by core. coyp from, but do not modify</param>
/// <param name="size">length of buffer</param>
/// <returns>success</returns>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool libmeteor_savesaveram(ref IntPtr data, ref uint size);
/// <summary>
/// destroy a buffer previously returned by libmeteor_savesaveram() to avoid leakage
/// </summary>
/// <param name="data"></param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_savesaveram_destroy(IntPtr data);
/// <summary>
/// return true if there is saveram installed on currently loaded cart
/// </summary>
/// <returns></returns>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool libmeteor_hassaveram();
/// <summary>
/// resets the current cart's saveram
/// </summary>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_clearsaveram();
}
}

View File

@ -64,18 +64,31 @@ namespace BizHawk.Emulation.Consoles.Nintendo.GBA
public byte[] ReadSaveRam()
{
return new byte[0];
if (!LibMeteor.libmeteor_hassaveram())
return null;
IntPtr data = IntPtr.Zero;
uint size = 0;
if (!LibMeteor.libmeteor_savesaveram(ref data, ref size))
throw new Exception("libmeteor_savesaveram() returned false!");
byte[] ret = new byte[size];
Marshal.Copy(data, ret, 0, (int)size);
LibMeteor.libmeteor_savesaveram_destroy(data);
return ret;
}
public void StoreSaveRam(byte[] data)
{
if (!LibMeteor.libmeteor_loadsaveram(data, (uint)data.Length))
throw new Exception("libmeteor_loadsaveram() returned false!");
}
public void ClearSaveRam()
{
LibMeteor.libmeteor_clearsaveram();
}
public bool SaveRamModified { get { return false; } set { } }
public bool SaveRamModified
{ get { return LibMeteor.libmeteor_hassaveram(); } set { } }
#endregion

View File

@ -2041,20 +2041,18 @@ namespace BizHawk.MultiClient
//zero says: this is sort of sketchy... but this is no time for rearchitecting
try
{
/*
var sram = new byte[Global.Emulator.ReadSaveRam.Length];
using (var reader = new BinaryReader(new FileStream(PathManager.SaveRamPath(Global.Game), FileMode.Open, FileAccess.Read)))
reader.Read(sram, 0, Global.Emulator.ReadSaveRam.Length);
if (Global.Emulator is LibsnesCore)
((LibsnesCore)Global.Emulator).StoreSaveRam(sram);
else if (Global.Emulator is Gameboy)
((Gameboy)Global.Emulator).StoreSaveRam(sram);
byte[] sram;
// GBA core might not know how big the saveram ought to be, so just send it the whole file
if (Global.Emulator is GBA)
{
sram = File.ReadAllBytes(PathManager.SaveRamPath(Global.Game));
}
else
Array.Copy(sram, Global.Emulator.ReadSaveRam, Global.Emulator.ReadSaveRam.Length);
*/
var sram = new byte[Global.Emulator.ReadSaveRam().Length];
using (var reader = new BinaryReader(new FileStream(PathManager.SaveRamPath(Global.Game), FileMode.Open, FileAccess.Read)))
{
sram = new byte[Global.Emulator.ReadSaveRam().Length];
using (var reader = new BinaryReader(new FileStream(PathManager.SaveRamPath(Global.Game), FileMode.Open, FileAccess.Read)))
reader.Read(sram, 0, sram.Length);
}
Global.Emulator.StoreSaveRam(sram);
}
catch (IOException) { }

View File

@ -30,8 +30,9 @@ else
CXXFLAGS += -DNO_MEMMEM
endif
#__LIBRETRO__ enables a slightly different saveram mechanism that doesn't seem to serve any useful purpose?
#CXXFLAGS += -Wall -pedantic -I. -I../ameteor/include -pipe -D__LIBRETRO__ -Wno-parentheses -fno-exceptions -fno-rtti
CXXFLAGS += -Wall -pedantic -I. -Iinclude -pipe -D__LIBRETRO__ -DX86_ASM -Wno-parentheses -fno-exceptions -fno-rtti
CXXFLAGS += -Wall -pedantic -I. -Iinclude -pipe -DX86_ASM -Wno-parentheses -fno-exceptions -fno-rtti
ifeq ($(DEBUG), 1)
CFLAGS += -O0 -g

View File

@ -120,8 +120,6 @@ EXPORT void libmeteor_init()
static bool first = true;
if (first)
{
// TODO: saveram stuff
//AMeteor::_memory.LoadCartInferred();
AMeteor::_lcd.GetScreen().GetRenderer().SetFrameSlot(syg::ptr_fun(videocb));
AMeteor::_sound.GetSpeaker().SetFrameSlot(syg::ptr_fun(soundcb));
first = false;
@ -150,5 +148,30 @@ EXPORT uint8_t *libmeteor_getmemoryarea(int which)
return AMeteor::_memory.GetMemoryArea(which);
}
EXPORT int libmeteor_loadsaveram(const void *data, unsigned size)
{
return AMeteor::_memory.LoadCart((const uint8_t*)data, size);
}
EXPORT int libmeteor_savesaveram(void **data, unsigned *size)
{
return AMeteor::_memory.SaveCart((uint8_t **)data, size);
}
EXPORT void libmeteor_savesaveram_destroy(void *data)
{
AMeteor::_memory.SaveCartDestroy((uint8_t *)data);
}
EXPORT int libmeteor_hassaveram()
{
return AMeteor::_memory.HasCart();
}
EXPORT void libmeteor_clearsaveram()
{
AMeteor::_memory.DeleteCart();
}
// TODO: cartram and system bus memory domains

View File

@ -81,15 +81,15 @@ namespace AMeteor
return m_timer[num];
}
void SetBattery (uint32_t cycles)
{
m_battery = cycles;
}
void DisableBattery ()
{
m_battery = INT_MAX;
//void SetBattery (uint32_t cycles)
//{
// m_battery = cycles;
//}
//void DisableBattery ()
//{
// m_battery = INT_MAX;
// no need to SetFirst since battery will be disabled only in TimeEvent
}
//}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
@ -100,7 +100,7 @@ namespace AMeteor
unsigned short m_cycles;
unsigned short m_first;
int m_lcd, m_timer[4], m_sound, m_battery;
int m_lcd, m_timer[4], m_sound;//, m_battery;
unsigned int m_count;

View File

@ -60,10 +60,10 @@ namespace AMeteor
// erases cartridge memory
void SetCartTypeFromSize (uint32_t size);
void SetCartType (uint8_t type);
void SetCartFile (const char* filename)
{
m_cartfile = filename;
}
//void SetCartFile (const char* filename)
//{
// m_cartfile = filename;
//}
void Reset (uint32_t params = ~0);
void ClearWbram ();
@ -85,10 +85,23 @@ namespace AMeteor
}
bool LoadRom (const char* filename);
void LoadRom (const uint8_t* data, uint32_t size);
CartError LoadCart ();
//CartError LoadCart ();
bool LoadCart (const uint8_t* data, uint32_t size);
bool SaveCart (uint8_t** data, uint32_t* size);
void SaveCartDestroy(uint8_t* data);
#ifdef __LIBRETRO__
bool LoadCartInferred ();
#endif
bool HasCart () const
{
return m_cart;
}
void DeleteCart()
{
if (m_cart)
delete m_cart;
}
bool HasBios () const
{
@ -141,7 +154,7 @@ namespace AMeteor
uint8_t m_carttype;
CartMem* m_cart;
std::string m_cartfile;
//std::string m_cartfile;
uint8_t ReadCart (uint16_t add);
void WriteCart (uint16_t add, uint8_t val);

View File

@ -51,7 +51,7 @@
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBMETEOR_EXPORTS;%(PreprocessorDefinitions);METDEBUG;METDEBUGLOG;_ITERATOR_DEBUG_LEVEL=0</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBMETEOR_EXPORTS;%(PreprocessorDefinitions);METDEBUG;METDEBUGLOG;_ITERATOR_DEBUG_LEVEL=0;NO_MEMMEM</PreprocessorDefinitions>
<DisableSpecificWarnings>4396;4800</DisableSpecificWarnings>
</ClCompile>
<Link>
@ -67,7 +67,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBMETEOR_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBMETEOR_EXPORTS;%(PreprocessorDefinitions);NO_MEMMEM</PreprocessorDefinitions>
<DisableSpecificWarnings>4396;4800</DisableSpecificWarnings>
</ClCompile>
<Link>

View File

@ -27,7 +27,7 @@ namespace AMeteor
// lcd is enabled by default
m_first = m_count = m_cycles = m_lcd = m_sound = 0;
// timers and battery are disabled by default
m_battery = m_timer[0] = m_timer[1] = m_timer[2] = m_timer[3] =
/*m_battery =*/ m_timer[0] = m_timer[1] = m_timer[2] = m_timer[3] =
INT_MAX;
}
@ -67,7 +67,7 @@ namespace AMeteor
COMMIT(timer[1], TIMER1)
COMMIT(timer[2], TIMER2)
COMMIT(timer[3], TIMER3)
COMMIT(battery, MEM)
//COMMIT(battery, MEM)
#undef COMMIT
SetFirst();
@ -91,7 +91,7 @@ namespace AMeteor
SETFIRST(timer[2]);
SETFIRST(timer[3]);
SETFIRST(sound);
SETFIRST(battery);
//SETFIRST(battery);
}
#undef SETFIRST
@ -101,7 +101,7 @@ namespace AMeteor
SS_WRITE_VAR(m_first);
SS_WRITE_VAR(m_lcd);
SS_WRITE_VAR(m_sound);
SS_WRITE_VAR(m_battery);
//SS_WRITE_VAR(m_battery);
SS_WRITE_ARRAY(m_timer);
@ -114,7 +114,7 @@ namespace AMeteor
SS_READ_VAR(m_first);
SS_READ_VAR(m_lcd);
SS_READ_VAR(m_sound);
SS_READ_VAR(m_battery);
//SS_READ_VAR(m_battery);
SS_READ_ARRAY(m_timer);

View File

@ -99,29 +99,39 @@ namespace AMeteor
void Memory::SetCartType (uint8_t type)
{
if (m_cart)
{
delete m_cart;
print_bizhawk("Cart Memory unloaded.\n");
}
switch (type)
{
case CTYPE_UNKNOWN:
m_cart = NULL;
print_bizhawk("Cart Memory set to <empty>.\n");
break;
case CTYPE_FLASH64:
m_cart = new Flash(false);
print_bizhawk("Cart Memory set to FLASH64\n");
break;
case CTYPE_FLASH128:
m_cart = new Flash(true);
print_bizhawk("Cart Memory set to FLASH128\n");
break;
case CTYPE_EEPROM512:
m_cart = new Eeprom(false);
print_bizhawk("Cart Memory set to EEPROM512\n");
break;
case CTYPE_EEPROM8192:
m_cart = new Eeprom(true);
print_bizhawk("Cart Memory set to EEPROM8192\n");
break;
case CTYPE_SRAM:
m_cart = new Sram();
print_bizhawk("Cart Memory set to SRAM\n");
break;
default:
met_abort("Unknown cartridge memory type");
print_bizhawk("Problem setting Cart Memory. This is bad.\n");
break;
}
m_carttype = type;
@ -168,7 +178,7 @@ namespace AMeteor
if (params & UNIT_MEMORY_ROM)
std::memset(m_rom , 0, 0x02000000);
SetCartType(CTYPE_UNKNOWN);
m_cartfile.clear();
//m_cartfile.clear();
}
void Memory::ClearWbram ()
@ -204,6 +214,7 @@ namespace AMeteor
void Memory::TimeEvent ()
{
/*
if (!m_cartfile.empty())
{
// FIXME, this may fail, we should do something to inform user
@ -211,6 +222,7 @@ namespace AMeteor
m_cart->Save(f);
}
CLOCK.DisableBattery();
*/
}
bool Memory::LoadBios (const char* filename)
@ -251,6 +263,38 @@ namespace AMeteor
std::memset(m_rom+until, 0, 0x02000000-until);
}
bool Memory::LoadCart(const uint8_t* data, uint32_t size)
{
SetCartTypeFromSize(size);
if (!m_cart)
return false;
std::stringstream ss = std::stringstream(std::string((const char*)data, size), std::ios_base::in | std::ios_base::binary);
return m_cart->Load(ss);
}
bool Memory::SaveCart(uint8_t** data, uint32_t* size)
{
if (!m_cart)
return false;
if (!data || !size)
return false;
std::stringstream ss = std::stringstream(std::ios_base::out | std::ios_base::binary);
if (!m_cart->Save(ss))
return false;
std::string s = ss.str();
uint8_t *ret = (uint8_t *)std::malloc(s.length());
std::memcpy(ret, s.data(), s.length());
*data = ret;
*size = s.length();
return true;
}
void Memory::SaveCartDestroy(uint8_t* data)
{
std::free(data);
}
/*
Memory::CartError Memory::LoadCart ()
{
struct stat buf;
@ -262,6 +306,7 @@ namespace AMeteor
return CERR_FAIL;
return CERR_NO_ERROR;
}
*/
#ifdef __LIBRETRO__
bool Memory::LoadCartInferred ()
@ -761,8 +806,9 @@ namespace AMeteor
else
met_abort("Unknown size for EEPROM DMA");
if (eeprom->Write((uint16_t*)GetRealAddress(src), size))
CLOCK.SetBattery(CART_SAVE_TIME);
//if (eeprom->Write((uint16_t*)GetRealAddress(src), size))
// CLOCK.SetBattery(CART_SAVE_TIME);
eeprom->Write((uint16_t*)GetRealAddress(src), size);
}
#if 0
@ -777,7 +823,6 @@ namespace AMeteor
}
#endif
#define NO_MEMMEM
#ifdef NO_MEMMEM // memmem() is a GNU extension, and does not exist in at least MinGW.
#define memmem memmem_compat
// Implementation from Git.
@ -817,8 +862,9 @@ namespace AMeteor
}
else
SetCartType(CTYPE_SRAM);
if (m_cart->Write(add, val))
CLOCK.SetBattery(CART_SAVE_TIME);
//if (m_cart->Write(add, val))
// CLOCK.SetBattery(CART_SAVE_TIME);
m_cart->Write(add, val);
}
uint8_t *Memory::GetMemoryArea(int which)