mirror of
https://github.com/libretro/Mu.git
synced 2026-09-22 14:45:57 +00:00
Add sdcard delta save/load states
This commit is contained in:
@@ -17,4 +17,4 @@ endif
|
||||
include $(EMU_PATH)/makefile.all
|
||||
|
||||
SOURCES_C := $(CORE_DIR)/libretro.c \
|
||||
$(EMU_SOURCES)
|
||||
$(EMU_SOURCES_C)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
TARGET_NAME ?= snes9x2010
|
||||
TARGET_NAME ?= mu
|
||||
|
||||
ifeq ($(STATIC_LINKING), 1)
|
||||
TARGET = $(TARGET_NAME)_libretro_$(platform).a
|
||||
|
||||
21
src/bps/crc32.c
Executable file
21
src/bps/crc32.c
Executable file
@@ -0,0 +1,21 @@
|
||||
//Module name: crc32
|
||||
//Author: Alcaro
|
||||
//Date: June 3, 2015
|
||||
//Licence: GPL v3.0 or higher
|
||||
|
||||
#include "crc32.h"
|
||||
|
||||
static const uint32_t crctable_4bits[]={
|
||||
0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC, 0x76DC4190, 0x6B6B51F4, 0x4DB26158, 0x5005713C,
|
||||
0xEDB88320, 0xF00F9344, 0xD6D6A3E8, 0xCB61B38C, 0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, 0xBDBDF21C,
|
||||
};
|
||||
uint32_t crc32_update(const uint8_t* data, size_t len, uint32_t crc)
|
||||
{
|
||||
crc = ~crc;
|
||||
for (size_t i=0;i<len;i++)
|
||||
{
|
||||
crc = crctable_4bits[(crc^ data[i] )&0x0F] ^ (crc>>4);
|
||||
crc = crctable_4bits[(crc^(data[i]>>4))&0x0F] ^ (crc>>4);
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
10
src/bps/crc32.h
Executable file
10
src/bps/crc32.h
Executable file
@@ -0,0 +1,10 @@
|
||||
//Module name: crc32
|
||||
//Author: Alcaro
|
||||
//Date: June 3, 2015
|
||||
//Licence: GPL v3.0 or higher
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
uint32_t crc32_update(const uint8_t* data, size_t len, uint32_t crc);
|
||||
static inline uint32_t crc32(const uint8_t* data, size_t len) { return crc32_update(data, len, 0); }
|
||||
34
src/bps/global.h
Normal file → Executable file
34
src/bps/global.h
Normal file → Executable file
@@ -1,6 +1,6 @@
|
||||
//Module name: Floating IPS, global header
|
||||
//Author: Alcaro
|
||||
//Date: June 18, 2015
|
||||
//Date: See Git history
|
||||
//Licence: GPL v3.0 or higher
|
||||
|
||||
#ifndef struct_mem
|
||||
@@ -15,38 +15,8 @@
|
||||
#endif
|
||||
|
||||
struct mem {
|
||||
unsigned char * ptr;
|
||||
uint8_t * ptr;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
#if defined(FLIPS_WINDOWS)
|
||||
#define LPCWSTR const wchar_t *
|
||||
#else
|
||||
#define LPCWSTR const char *
|
||||
#endif
|
||||
|
||||
class file {
|
||||
public:
|
||||
static file* create(LPCWSTR filename);
|
||||
|
||||
virtual size_t len() = 0;
|
||||
virtual bool read(uint8_t* target, size_t start, size_t len) = 0;
|
||||
|
||||
static struct mem read(LPCWSTR filename); // provided by Flips core
|
||||
struct mem read(); // provided by Flips core
|
||||
|
||||
virtual ~file() {}
|
||||
};
|
||||
|
||||
class filewrite {
|
||||
public:
|
||||
static filewrite* create(LPCWSTR filename);
|
||||
|
||||
virtual bool append(const uint8_t* data, size_t len) = 0;
|
||||
|
||||
static bool write(LPCWSTR filename, struct mem data); // provided by Flips core
|
||||
|
||||
virtual ~filewrite() {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
201
src/bps/libbps.cpp → src/bps/libbps.c
Normal file → Executable file
201
src/bps/libbps.cpp → src/bps/libbps.c
Normal file → Executable file
@@ -1,6 +1,6 @@
|
||||
//Module name: libbps
|
||||
//Author: Alcaro
|
||||
//Date: December 20, 2014
|
||||
//Date: November 7, 2015
|
||||
//Licence: GPL v3.0 or higher
|
||||
|
||||
#include "libbps.h"
|
||||
@@ -22,6 +22,37 @@ static uint32_t read32(uint8_t * ptr)
|
||||
|
||||
enum { SourceRead, TargetRead, SourceCopy, TargetCopy };
|
||||
|
||||
static bool try_add(size_t* a, size_t b)
|
||||
{
|
||||
if (SIZE_MAX-*a < b) return false;
|
||||
*a+=b;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool try_shift(size_t* a, size_t b)
|
||||
{
|
||||
if (SIZE_MAX>>b < *a) return false;
|
||||
*a<<=b;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decodenum(const uint8_t* ptr, size_t* out)
|
||||
{
|
||||
*out=0;
|
||||
unsigned int shift=0;
|
||||
while (true)
|
||||
{
|
||||
uint8_t next=*ptr++;
|
||||
size_t addthis=(next&0x7F);
|
||||
if (shift) addthis++;
|
||||
if (!try_shift(&addthis, shift)) return false;
|
||||
// unchecked because if it was shifted, the lowest bit is zero, and if not, it's <=0x7F.
|
||||
if (!try_add(out, addthis)) return false;
|
||||
if (next&0x80) return true;
|
||||
shift+=7;
|
||||
}
|
||||
}
|
||||
|
||||
#define error(which) do { error=which; goto exit; } while(0)
|
||||
#define assert_sum(a,b) do { if (SIZE_MAX-(a)<(b)) error(bps_too_big); } while(0)
|
||||
#define assert_shift(a,b) do { if (SIZE_MAX>>(b)<(a)) error(bps_too_big); } while(0)
|
||||
@@ -42,20 +73,7 @@ enum bpserror bps_apply(struct mem patch, struct mem in, struct mem * out, struc
|
||||
#define read8() (*(patchat++))
|
||||
#define decodeto(var) \
|
||||
do { \
|
||||
var=0; \
|
||||
unsigned int shift=0; \
|
||||
while (true) \
|
||||
{ \
|
||||
uint8_t next=read8(); \
|
||||
assert_shift(next&0x7F, shift); \
|
||||
size_t addthis=(next&0x7F)<<shift; \
|
||||
assert_sum(var, addthis); \
|
||||
var+=addthis; \
|
||||
if (next&0x80) break; \
|
||||
shift+=7; \
|
||||
assert_sum(var, 1U<<shift); \
|
||||
var+=1<<shift; \
|
||||
} \
|
||||
if (!decodenum(patchat, &var)) error(bps_too_big); \
|
||||
} while(false)
|
||||
#define write8(byte) (*(outat++)=byte)
|
||||
|
||||
@@ -197,6 +215,8 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define write(val) \
|
||||
do { \
|
||||
out[outlen++]=(val); \
|
||||
@@ -264,7 +284,7 @@ enum bpserror bps_create_linear(struct mem sourcemem, struct mem targetmem, stru
|
||||
{
|
||||
size_t numunchanged=0;
|
||||
while (source+numunchanged<sourceend && source[numunchanged]==target[numunchanged]) numunchanged++;
|
||||
if (numunchanged>1)
|
||||
if (numunchanged>1 || numunchanged == (uintptr_t)(targetend-target))
|
||||
{
|
||||
//assert_shift((numunchanged-1), 2);
|
||||
writenum((numunchanged-1)<<2 | 0);//SourceRead
|
||||
@@ -367,155 +387,8 @@ enum bpserror bps_create_linear(struct mem sourcemem, struct mem targetmem, stru
|
||||
#undef write
|
||||
#undef writenum
|
||||
|
||||
enum bpserror bps_get_checksums(file* patch, uint32_t * inromsum, uint32_t * outromsum, uint32_t * patchsum)
|
||||
{
|
||||
size_t len = patch->len();
|
||||
if (len<4+3+12) return bps_broken;
|
||||
|
||||
uint8_t verify[4];
|
||||
if (!patch->read(verify, 0, 4) || memcmp(verify, "BPS1", 4)) return bps_broken;
|
||||
|
||||
uint8_t checksums[12];
|
||||
if (!patch->read(checksums, len-12, 12)) return bps_broken;
|
||||
if (inromsum) *inromsum =read32(checksums+0);
|
||||
if (outromsum) *outromsum=read32(checksums+4);
|
||||
if (patchsum) *patchsum =read32(checksums+8);
|
||||
return bps_ok;
|
||||
}
|
||||
|
||||
void bps_free(struct mem mem)
|
||||
{
|
||||
free(mem.ptr);
|
||||
}
|
||||
|
||||
#if 0
|
||||
#warning Disable this in release versions.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//Congratulations, you found the undocumented feature! It compares two equivalent BPS patches and
|
||||
// tells where each one is more compact. (It crashes or gives bogus answers on invalid or
|
||||
// non-equivalent patches.) Have fun.
|
||||
void bps_compare(struct mem patch1mem, struct mem patch2mem)
|
||||
{
|
||||
const uint8_t * patch[2]={patch1mem.ptr, patch2mem.ptr};
|
||||
size_t patchpos[2]={0,0};
|
||||
size_t patchlen[2]={patch1mem.len-12, patch2mem.len-12};
|
||||
size_t patchoutpos[2]={0,0};
|
||||
|
||||
size_t patchcopypos[2][4]={0,0};//[0] and [1] are unused, but this is just debug code, it doesn't need to be neat.
|
||||
|
||||
#define read8(id) (patch[id][patchpos[id]++])
|
||||
#define decodeto(id, var) \
|
||||
do { \
|
||||
var=0; \
|
||||
int shift=0; \
|
||||
while (true) \
|
||||
{ \
|
||||
uint8_t next=read8(id); \
|
||||
size_t addthis=(next&0x7F)<<shift; \
|
||||
var+=addthis; \
|
||||
if (next&0x80) break; \
|
||||
shift+=7; \
|
||||
var+=1<<shift; \
|
||||
} \
|
||||
} while(false)
|
||||
|
||||
size_t lastmatch=0;
|
||||
size_t patchposatmatch[2]={0,0};
|
||||
|
||||
size_t outlen;
|
||||
patch[0]+=4; patch[1]+=4;//BPS1
|
||||
size_t tempuint;
|
||||
decodeto(0, tempuint); decodeto(1, tempuint);//source-size
|
||||
decodeto(0, outlen); decodeto(1, outlen);//target-size
|
||||
decodeto(0, tempuint); patch[0]+=tempuint;//metadata
|
||||
decodeto(1, tempuint); patch[1]+=tempuint;//metadata
|
||||
|
||||
bool show=false;
|
||||
while (patchpos[0]<patchlen[0] && patchpos[1]<patchlen[1])
|
||||
{
|
||||
bool step[2]={(patchoutpos[0]<=patchoutpos[1]), (patchoutpos[0]>=patchoutpos[1])};
|
||||
char describe[2][256];
|
||||
for (int i=0;i<2;i++)
|
||||
{
|
||||
if (step[i])
|
||||
{
|
||||
size_t patchposstart=patchpos[i];
|
||||
decodeto(i, tempuint);
|
||||
size_t len=(tempuint>>2)+1;
|
||||
patchoutpos[i]+=len;
|
||||
int action=(tempuint&3);
|
||||
//enum { SourceRead, TargetRead, SourceCopy, TargetCopy };
|
||||
const char * actionnames[]={"SourceRead", "TargetRead", "SourceCopy", "TargetCopy"};
|
||||
if (action==TargetRead) patchpos[i]+=len;
|
||||
if (action==SourceCopy || action==TargetCopy)
|
||||
{
|
||||
decodeto(i, tempuint);
|
||||
int delta = tempuint>>1;
|
||||
if (tempuint&1) delta=-delta;
|
||||
patchcopypos[i][action]+=delta;
|
||||
sprintf(describe[i], "%s from %i (%+i) for %i in %i", actionnames[action], patchcopypos[i][action], delta, len, patchpos[i]-patchposstart);
|
||||
patchcopypos[i][action]+=len;
|
||||
}
|
||||
else sprintf(describe[i], "%s from %i for %i in %i", actionnames[action], patchoutpos[i], len, patchpos[i]-patchposstart);
|
||||
if (!step[i^1])
|
||||
{
|
||||
printf("%i: %s\n", i+1, describe[i]);
|
||||
show=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (step[0] && step[1])
|
||||
{
|
||||
if (!strcmp(describe[0], describe[1])) /*printf("3: %s\n", describe[0])*/;
|
||||
else
|
||||
{
|
||||
printf("1: %s\n2: %s\n", describe[0], describe[1]);
|
||||
show=true;
|
||||
}
|
||||
}
|
||||
if (patchoutpos[0]==patchoutpos[1])
|
||||
{
|
||||
size_t used[2]={patchpos[0]-patchposatmatch[0], patchpos[1]-patchposatmatch[1]};
|
||||
char which='=';
|
||||
if (used[0]<used[1]) which='+';
|
||||
if (used[0]>used[1]) which='-';
|
||||
if (show)
|
||||
{
|
||||
printf("%c: %i,%i bytes since last match (%i)\n", which, used[0], used[1], patchoutpos[0]);
|
||||
show=false;
|
||||
}
|
||||
patchposatmatch[0]=patchpos[0];
|
||||
patchposatmatch[1]=patchpos[1];
|
||||
lastmatch=patchoutpos[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct mem ReadWholeFile(const char * filename)
|
||||
{
|
||||
struct mem null = {NULL, 0};
|
||||
|
||||
FILE * file=fopen(filename, "rb");
|
||||
if (!file) return null;
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t len=ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
unsigned char * data=(unsigned char*)malloc(len);
|
||||
size_t truelen=fread(data, 1,len, file);
|
||||
fclose(file);
|
||||
if (len!=truelen)
|
||||
{
|
||||
free(data);
|
||||
return null;
|
||||
}
|
||||
|
||||
struct mem ret = { (unsigned char*)data, len };
|
||||
return ret;
|
||||
}
|
||||
int main(int argc,char**argv)
|
||||
{
|
||||
bps_compare(ReadWholeFile(argv[1]),ReadWholeFile(argv[2]));
|
||||
}
|
||||
#endif
|
||||
#undef error
|
||||
48
src/bps/libbps.h
Normal file → Executable file
48
src/bps/libbps.h
Normal file → Executable file
@@ -1,6 +1,6 @@
|
||||
//Module name: libbps
|
||||
//Author: Alcaro
|
||||
//Date: June 18, 2015
|
||||
//Date: November 30, 2015
|
||||
//Licence: GPL v3.0 or higher
|
||||
|
||||
#include "global.h"
|
||||
@@ -23,13 +23,14 @@ enum bpserror {
|
||||
bps_to_output,//You attempted to apply a patch to its output.
|
||||
bps_not_this, //This is not the intended input file for this patch.
|
||||
bps_broken, //This is not a BPS patch, or it's malformed somehow.
|
||||
bps_io, //The patch could not be read.
|
||||
|
||||
bps_identical, //The input files are identical.
|
||||
bps_too_big, //Somehow, you're asking for something a size_t can't represent.
|
||||
bps_out_of_mem,//Memory allocation failure.
|
||||
bps_canceled, //The callback returned false.
|
||||
|
||||
bps_shut_the_fuck_up_gcc//This one isn't used, it's just to kill a stray comma warning.
|
||||
bps_shut_up_gcc//This one isn't used, it's just to kill a stray comma warning.
|
||||
};
|
||||
|
||||
//Applies the given BPS patch to the given ROM and puts it in 'out'. Metadata, if present and
|
||||
@@ -41,30 +42,35 @@ enum bpserror bps_apply(struct mem patch, struct mem in, struct mem * out, struc
|
||||
// {NULL,0} as metadata.
|
||||
enum bpserror bps_create_linear(struct mem source, struct mem target, struct mem metadata, struct mem * patch);
|
||||
|
||||
//Very similar to bps_create_linear; the difference is that this one takes longer to run, but
|
||||
// generates smaller patches.
|
||||
//Because it can take much longer, a progress meter is supplied; total is guaranteed to be constant
|
||||
// between every call until this function returns, done is guaranteed to increase between each
|
||||
// call, and done/total is an approximate percentage counter. Anything else is undefined; for
|
||||
// example, progress may or may not be called for done=0, progress may or may not be called for
|
||||
// done=total, done may or may not increase by the same amount between each call, and the duration
|
||||
// between each call may or may not be constant. In fact, it can
|
||||
//To cancel the patch creation, return false from the callback.
|
||||
//It is safe to pass in NULL for the progress indicator if you're not interested. If the callback is
|
||||
// NULL, it can obviously not be canceled that way (though if it's a CLI program, you can always
|
||||
// Ctrl-C it).
|
||||
//The 'moremem' flag makes it use about twice as much memory (9*(source+target) instead of 5*), but is usually slightly faster.
|
||||
enum bpserror bps_create_delta(file* source, file* target, struct mem metadata, struct mem * patch,
|
||||
bool (*progress)(void* userdata, size_t done, size_t total), void* userdata,
|
||||
bool moremem);
|
||||
|
||||
enum bpserror bps_get_checksums(file* patch, uint32_t * inromsum, uint32_t * outromsum, uint32_t * patchsum);
|
||||
|
||||
//Frees the memory returned in the output parameters of the above. Do not call it twice on the same
|
||||
// input, nor on anything you got from anywhere else. bps_free is guaranteed to be equivalent to
|
||||
// calling stdlib.h's free() on mem.ptr.
|
||||
void bps_free(struct mem mem);
|
||||
|
||||
struct bpsinfo {
|
||||
enum bpserror error; // If this is not bps_ok, all other values are undefined.
|
||||
|
||||
size_t size_in;
|
||||
size_t size_out;
|
||||
|
||||
uint32_t crc_in;
|
||||
uint32_t crc_out;
|
||||
uint32_t crc_patch;
|
||||
|
||||
size_t meta_start;
|
||||
size_t meta_size;
|
||||
|
||||
//Tells approximately how much of the input ROM is changed compared to the output ROM.
|
||||
//It's quite heuristic. The algorithm may change with or without notice.
|
||||
//As of writing, I believe this is accurate to 2 significant digits in base 10.
|
||||
//It's also more expensive to calculate than the other data, so it's optional.
|
||||
//If you don't want it, their values are undefined.
|
||||
//The denominator is always guaranteed nonzero, even if something else says it's undefined.
|
||||
//Note that this can return success for invalid patches.
|
||||
size_t change_num;
|
||||
size_t change_denom;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "hardwareRegisters.h"
|
||||
#include "memoryAccess.h"
|
||||
#include "sed1376.h"
|
||||
#include "sdcard.h"
|
||||
#include "silkscreen.h"
|
||||
#include "emuFeatureRegistersSpec.h"
|
||||
|
||||
@@ -69,6 +70,7 @@ void emulatorInit(uint8_t* palmRomDump, uint32_t specialFeatures){
|
||||
memcpy(&palmFramebuffer[160 * 160], silkscreenData, SILKSCREEN_WIDTH * SILKSCREEN_HEIGHT * (SILKSCREEN_BPP / 8));
|
||||
resetAddressSpace();
|
||||
sed1376Reset();
|
||||
sdCardInit();
|
||||
|
||||
//input
|
||||
palmInput.buttonUp = false;
|
||||
@@ -107,8 +109,12 @@ void emulatorInit(uint8_t* palmRomDump, uint32_t specialFeatures){
|
||||
m68k_pulse_reset();
|
||||
}
|
||||
|
||||
void emulatorExit(){
|
||||
sdCardExit();
|
||||
}
|
||||
|
||||
void emulatorReset(){
|
||||
//reset doesnt clear ram, all programs are stored in ram
|
||||
//reset doesnt clear ram or sdcard, all programs are stored in ram or on sdcard
|
||||
resetHwRegisters();
|
||||
resetAddressSpace();//address space must be reset after hardware registers because it is dependant on them
|
||||
sed1376Reset();
|
||||
@@ -137,10 +143,16 @@ uint32_t emulatorGetStateSize(){
|
||||
}
|
||||
|
||||
void emulatorSaveState(uint8_t* data){
|
||||
//save sdcard here
|
||||
|
||||
uint32_t offset = 0;
|
||||
m68k_get_context(data + offset);
|
||||
|
||||
//update sdcard struct, save sdcard data
|
||||
if(allSdCardCallbacksPresent() && palmSdCard.type != CARD_NONE){
|
||||
uint64_t stateId = emulatorGetSysTime();
|
||||
sdCardSaveState(palmSdCard.sessionId, stateId);
|
||||
palmSdCard.stateId = stateId;
|
||||
}
|
||||
|
||||
m68k_get_context(data + offset);//not endian safe
|
||||
offset += m68k_context_size();
|
||||
memcpy(data + offset, palmRam, RAM_SIZE);
|
||||
offset += RAM_SIZE;
|
||||
@@ -148,32 +160,30 @@ void emulatorSaveState(uint8_t* data){
|
||||
offset += REG_SIZE;
|
||||
memcpy(data + offset, bankType, TOTAL_MEMORY_BANKS);
|
||||
offset += TOTAL_MEMORY_BANKS;
|
||||
memcpy(data + offset, &palmMisc, sizeof(misc_hw_t));
|
||||
memcpy(data + offset, &palmMisc, sizeof(misc_hw_t));//not endian safe
|
||||
offset += sizeof(misc_hw_t);
|
||||
memcpy(data + offset, &palmSdCard, sizeof(sdcard_t));
|
||||
memcpy(data + offset, &palmSdCard, sizeof(sdcard_t));//not endian safe
|
||||
offset += sizeof(sdcard_t);
|
||||
//input is not part of savestates
|
||||
memcpy(data + offset, &palmSpecialFeatures, sizeof(uint32_t));
|
||||
memcpy(data + offset, &palmSpecialFeatures, sizeof(uint32_t));//not endian safe
|
||||
offset += sizeof(uint32_t);
|
||||
memcpy(data + offset, &palmCrystalCycles, sizeof(double));
|
||||
memcpy(data + offset, &palmCrystalCycles, sizeof(double));//not endian safe
|
||||
offset += sizeof(double);
|
||||
memcpy(data + offset, &palmCycleCounter, sizeof(double));
|
||||
memcpy(data + offset, &palmCycleCounter, sizeof(double));//not endian safe
|
||||
offset += sizeof(double);
|
||||
memcpy(data + offset, &clk32Counter, sizeof(uint32_t));
|
||||
memcpy(data + offset, &clk32Counter, sizeof(uint32_t));//not endian safe
|
||||
offset += sizeof(uint32_t);
|
||||
memcpy(data + offset, &timer1CycleCounter, sizeof(double));
|
||||
memcpy(data + offset, &timer1CycleCounter, sizeof(double));//not endian safe
|
||||
offset += sizeof(double);
|
||||
memcpy(data + offset, &timer2CycleCounter, sizeof(double));
|
||||
memcpy(data + offset, &timer2CycleCounter, sizeof(double));//not endian safe
|
||||
offset += sizeof(double);
|
||||
data[offset] = lowPowerStopActive;
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
void emulatorLoadState(uint8_t* data){
|
||||
//load sdcard here
|
||||
|
||||
uint32_t offset = 0;
|
||||
m68k_set_context(data + offset);
|
||||
m68k_set_context(data + offset);//not endian safe
|
||||
offset += m68k_context_size();
|
||||
memcpy(palmRam, data + offset, RAM_SIZE);
|
||||
offset += RAM_SIZE;
|
||||
@@ -181,27 +191,32 @@ void emulatorLoadState(uint8_t* data){
|
||||
offset += REG_SIZE;
|
||||
memcpy(bankType, data + offset, TOTAL_MEMORY_BANKS);
|
||||
offset += TOTAL_MEMORY_BANKS;
|
||||
memcpy(&palmMisc, data + offset, sizeof(misc_hw_t));
|
||||
memcpy(&palmMisc, data + offset, sizeof(misc_hw_t));//not endian safe
|
||||
offset += sizeof(misc_hw_t);
|
||||
memcpy(&palmSdCard, data + offset, sizeof(sdcard_t));
|
||||
memcpy(&palmSdCard, data + offset, sizeof(sdcard_t));//not endian safe
|
||||
offset += sizeof(sdcard_t);
|
||||
//input is not part of savestates
|
||||
memcpy(&palmSpecialFeatures, data + offset, sizeof(uint32_t));
|
||||
memcpy(&palmSpecialFeatures, data + offset, sizeof(uint32_t));//not endian safe
|
||||
offset += sizeof(uint32_t);
|
||||
memcpy(&palmCrystalCycles, data + offset, sizeof(double));
|
||||
memcpy(&palmCrystalCycles, data + offset, sizeof(double));//not endian safe
|
||||
offset += sizeof(double);
|
||||
memcpy(&palmCycleCounter, data + offset, sizeof(double));
|
||||
memcpy(&palmCycleCounter, data + offset, sizeof(double));//not endian safe
|
||||
offset += sizeof(double);
|
||||
memcpy(&clk32Counter, data + offset, sizeof(uint32_t));
|
||||
memcpy(&clk32Counter, data + offset, sizeof(uint32_t));//not endian safe
|
||||
offset += sizeof(uint32_t);
|
||||
memcpy(&timer1CycleCounter, data + offset, sizeof(double));
|
||||
memcpy(&timer1CycleCounter, data + offset, sizeof(double));//not endian safe
|
||||
offset += sizeof(double);
|
||||
memcpy(&timer2CycleCounter, data + offset, sizeof(double));
|
||||
memcpy(&timer2CycleCounter, data + offset, sizeof(double));//not endian safe
|
||||
offset += sizeof(double);
|
||||
lowPowerStopActive = data[offset];
|
||||
offset += 1;
|
||||
|
||||
refreshBankHandlers();
|
||||
|
||||
//update sdcard data from sdcard struct
|
||||
if(allSdCardCallbacksPresent() && palmSdCard.type != CARD_NONE){
|
||||
sdCardLoadState(palmSdCard.sessionId, palmSdCard.stateId);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t emulatorInstallPrcPdb(uint8_t* data, uint32_t size){
|
||||
|
||||
@@ -12,6 +12,7 @@ enum{
|
||||
EMU_ERROR_NONE = 0,
|
||||
EMU_ERROR_NOT_IMPLEMENTED,
|
||||
EMU_ERROR_CALLBACKS_NOT_SET,
|
||||
EMU_ERROR_OUT_OF_MEMORY,
|
||||
EMU_ERROR_NOT_DOCUMENTED
|
||||
};
|
||||
|
||||
@@ -89,8 +90,7 @@ typedef struct{
|
||||
#define INACCURATE_SYNCED_RTC 0x00000010//rtc always equals host system time
|
||||
#define INACCURATE_HLE_APIS 0x00000020//memcpy, memcmp, wait on timer will be replaced with the hosts function
|
||||
|
||||
//config options
|
||||
#define EMU_FPS 60.0
|
||||
//cpu
|
||||
#define CRYSTAL_FREQUENCY 32768.0
|
||||
#define CPU_FREQUENCY (palmCrystalCycles * CRYSTAL_FREQUENCY)
|
||||
|
||||
@@ -115,6 +115,10 @@ typedef struct{
|
||||
#define SED1376_REG_SIZE 0xB4//it has 0x20000 used address space entrys but only 0xB4 registers
|
||||
#define SED1376_FB_SIZE 0x20000//0x14000 in size, likely also has 0x20000 used address space entrys, using 0x20000 to prevent speed penalty of checking validity on every access
|
||||
|
||||
//config options
|
||||
#define EMU_FPS 60.0
|
||||
#define SDCARD_STATE_CHUNKS_VECTOR_SIZE 100
|
||||
|
||||
//emulator data
|
||||
extern uint8_t palmRam[];
|
||||
extern uint8_t palmRom[];
|
||||
@@ -136,6 +140,7 @@ void (*emulatorSetSdCardChunk)(uint64_t sessionId, uint64_t chunkId, uint8_t* da
|
||||
|
||||
//functions
|
||||
void emulatorInit(uint8_t* palmRomDump, uint32_t specialFeatures);
|
||||
void emulatorExit();
|
||||
void emulatorReset();
|
||||
void emulatorSetRtc(uint32_t days, uint32_t hours, uint32_t minutes, uint32_t seconds);
|
||||
uint32_t emulatorSetSdCard(uint64_t size, uint8_t type);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
EMU_SOURCES := $(EMU_PATH)/emulator.c \
|
||||
EMU_SOURCES_C := $(EMU_PATH)/emulator.c \
|
||||
$(EMU_PATH)/memoryAccess.c \
|
||||
$(EMU_PATH)/hardwareRegisters.c \
|
||||
$(EMU_PATH)/sed1376.c \
|
||||
@@ -10,4 +10,6 @@ EMU_SOURCES := $(EMU_PATH)/emulator.c \
|
||||
$(EMU_PATH)/m68k/m68kopdm.c \
|
||||
$(EMU_PATH)/m68k/m68kopac.c \
|
||||
$(EMU_PATH)/m68k/m68kdasm.c \
|
||||
$(EMU_PATH)/m68k/m68kcpu.c
|
||||
$(EMU_PATH)/m68k/m68kcpu.c \
|
||||
$(EMU_PATH)/bps/libbps.c \
|
||||
$(EMU_PATH)/bps/crc32.c
|
||||
|
||||
143
src/sdcard.c
143
src/sdcard.c
@@ -1 +1,144 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "emulator.h"
|
||||
#include "bps/libbps.h"
|
||||
|
||||
|
||||
static uint8_t* sdCardData;
|
||||
static uint8_t* sdCardOldData;
|
||||
static uint64_t* sdCardChunks;//0 terminated array
|
||||
static uint64_t sdCardChunkIndex;
|
||||
static uint64_t sdCardChunkMaxIndex;
|
||||
static uint64_t sdCardSize;
|
||||
|
||||
|
||||
static inline void updateChunkBufferLength(){
|
||||
uint64_t newChunkLength = 0;
|
||||
while(true){
|
||||
newChunkLength++;
|
||||
if(sdCardChunks[newChunkLength] == 0x0000000000000000)
|
||||
break;
|
||||
}
|
||||
|
||||
sdCardChunkIndex = newChunkLength;
|
||||
sdCardChunkMaxIndex = newChunkLength;
|
||||
}
|
||||
|
||||
|
||||
void sdCardInit(){
|
||||
sdCardData = NULL;
|
||||
sdCardOldData = NULL;
|
||||
sdCardChunks = NULL;
|
||||
sdCardChunkIndex = 0;
|
||||
sdCardChunkMaxIndex = 0;
|
||||
sdCardSize = 0;
|
||||
}
|
||||
|
||||
void sdCardExit(){
|
||||
if(sdCardData)
|
||||
free(sdCardData);
|
||||
if(sdCardOldData)
|
||||
free(sdCardOldData);
|
||||
sdCardData = NULL;
|
||||
sdCardOldData = NULL;
|
||||
sdCardChunks = NULL;
|
||||
sdCardChunkIndex = 0;
|
||||
sdCardChunkMaxIndex = 0;
|
||||
sdCardSize = 0;
|
||||
}
|
||||
|
||||
uint32_t sdCardReconfigure(uint64_t size){
|
||||
if(sdCardData)
|
||||
free(sdCardData);
|
||||
if(sdCardOldData)
|
||||
free(sdCardOldData);
|
||||
if(sdCardChunks)
|
||||
free(sdCardChunks);
|
||||
|
||||
sdCardData = malloc(size);
|
||||
if(!sdCardData)
|
||||
return EMU_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
sdCardOldData = malloc(size);
|
||||
if(!sdCardOldData)
|
||||
return EMU_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
sdCardChunks = malloc(sizeof(uint64_t) * SDCARD_STATE_CHUNKS_VECTOR_SIZE);
|
||||
if(!sdCardChunks)
|
||||
return EMU_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
memset(sdCardData, 0x00, size);
|
||||
memset(sdCardOldData, 0x00, size);
|
||||
sdCardChunks[0] = 0x0000000000000000;//0 terminated array
|
||||
|
||||
sdCardSize = size;
|
||||
sdCardChunkIndex = 0;
|
||||
|
||||
return EMU_ERROR_NONE;
|
||||
}
|
||||
|
||||
void sdCardSaveState(uint64_t sessionId, uint64_t stateId){
|
||||
//make single bps if sdcard data has changed
|
||||
if(memcmp(sdCardOldData, sdCardData, sdCardSize) != 0){
|
||||
struct mem source;
|
||||
struct mem target;
|
||||
struct mem metadata;
|
||||
struct mem patch;
|
||||
|
||||
source.ptr = sdCardOldData;
|
||||
source.len = sdCardSize;
|
||||
|
||||
target.ptr = sdCardData;
|
||||
target.len = sdCardSize;
|
||||
|
||||
metadata.ptr = NULL;
|
||||
metadata.len = 0;
|
||||
|
||||
bps_create_linear(source, target, metadata, &patch);
|
||||
emulatorSetSdCardChunk(sessionId, stateId, patch.ptr, patch.len);
|
||||
bps_free(patch);
|
||||
|
||||
if(sdCardChunkIndex + 2 >= sdCardChunkMaxIndex){
|
||||
//resize vector if needed
|
||||
sdCardChunks = realloc(sdCardChunks, sizeof(uint64_t) * (sdCardChunkMaxIndex + SDCARD_STATE_CHUNKS_VECTOR_SIZE));
|
||||
sdCardChunkMaxIndex += SDCARD_STATE_CHUNKS_VECTOR_SIZE;
|
||||
}
|
||||
sdCardChunks[sdCardChunkIndex] = stateId;
|
||||
sdCardChunkIndex++;
|
||||
sdCardChunks[sdCardChunkIndex] = 0x0000000000000000;
|
||||
|
||||
emulatorSetSdCardStateChunkList(sessionId, stateId, sdCardChunks);
|
||||
|
||||
memcpy(sdCardOldData, sdCardData, sdCardSize);
|
||||
}
|
||||
}
|
||||
|
||||
void sdCardLoadState(uint64_t sessionId, uint64_t stateId){
|
||||
//rebuild sdcard from bps chain
|
||||
free(sdCardChunks);
|
||||
sdCardChunks = emulatorGetSdCardStateChunkList(sessionId, stateId);
|
||||
updateChunkBufferLength();
|
||||
|
||||
memset(sdCardData, 0x00, sdCardSize);
|
||||
for(uint64_t index = 0; index < sdCardChunkIndex; index++){
|
||||
struct mem patch;
|
||||
struct mem input;
|
||||
struct mem output;
|
||||
|
||||
patch.ptr = emulatorGetSdCardChunk(sessionId, sdCardChunks[index]);
|
||||
patch.len = sdCardSize;
|
||||
|
||||
input.ptr = sdCardData;
|
||||
input.len = sdCardSize;
|
||||
|
||||
output.ptr = sdCardData;
|
||||
output.len = sdCardSize;
|
||||
|
||||
|
||||
//bps_apply(patch, input, output, NULL, true);//bps_apply needs to be modified to patch the existing buffer instead of making a new one
|
||||
}
|
||||
|
||||
memcpy(sdCardOldData, sdCardData, sdCardSize);
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void sdCardInit();
|
||||
void sdCardExit();
|
||||
uint32_t sdCardReconfigure(uint64_t size);
|
||||
void sdCardSaveState(uint64_t sessionId, uint64_t stateId);
|
||||
void sdCardLoadState(uint64_t sessionId, uint64_t stateId);
|
||||
|
||||
@@ -44,8 +44,11 @@ void sed1376Reset(){
|
||||
}
|
||||
|
||||
void sed1376Render(){
|
||||
//only render if lcd on and backlight on
|
||||
if(0){
|
||||
if(palmMisc.lcdOn && palmMisc.backlightOn){
|
||||
//only render if lcd on and backlight on
|
||||
|
||||
}
|
||||
else{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ All of DRAMMC
|
||||
Chip select registers
|
||||
Setting the battery dead bit(PDDATA 0x80) if the emulated battery percent gets too low
|
||||
endian safe savestates
|
||||
sdcard can't be read or written to
|
||||
sdcard can't be inserted or removed
|
||||
sdcard has no error handling in save/load state
|
||||
|
||||
Fixed:
|
||||
System Control Register, 0xXXFFF000 all upper banks are registers mode
|
||||
|
||||
Reference in New Issue
Block a user