From dca4e4813424158e1c778a199a8a760381f71b65 Mon Sep 17 00:00:00 2001 From: meepingsnesroms Date: Tue, 10 Apr 2018 13:25:34 -0700 Subject: [PATCH] Prepare to add virtual sdcard access, add libretro button mappings --- libretroBuildSystem/libretro.c | 63 ++-- src/bps/global.h | 52 ++++ src/bps/libbps.cpp | 521 +++++++++++++++++++++++++++++++++ src/bps/libbps.h | 70 +++++ src/emulator.c | 41 ++- src/emulator.h | 49 +++- src/makefile.all | 1 + src/sdcard.c | 1 + src/sdcard.h | 1 + tests/HwRegTest17.txt | 25 ++ unimplementedFeatures.txt | 1 + 11 files changed, 780 insertions(+), 45 deletions(-) create mode 100644 src/bps/global.h create mode 100644 src/bps/libbps.cpp create mode 100644 src/bps/libbps.h create mode 100644 src/sdcard.c create mode 100644 src/sdcard.h create mode 100644 tests/HwRegTest17.txt diff --git a/libretroBuildSystem/libretro.c b/libretroBuildSystem/libretro.c index 635710f..ddd5d52 100644 --- a/libretroBuildSystem/libretro.c +++ b/libretroBuildSystem/libretro.c @@ -141,19 +141,24 @@ void retro_reset(void) void retro_run(void) { - /* - key_state_t ks; - input_poll_cb(); - ks.up = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP); - ks.right = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT); - ks.down = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN); - ks.left = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT); - ks.start = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START); - ks.select = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT); - - */ + //dpad + palmInput.buttonUp = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP); + palmInput.buttonRight = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT); + palmInput.buttonDown = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN); + palmInput.buttonLeft = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT); + palmInput.buttonSelect = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R); + + //app buttons + palmInput.buttonCalender = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y); + palmInput.buttonAddress = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X); + palmInput.buttonTodo = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B); + palmInput.buttonNotes = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A); + + //special buttons + palmInput.buttonPower = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START); + palmInput.buttonContrast = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT); emulateFrame(); video_cb(palmFramebuffer, LR_SCREENWIDTH, LR_SCREENHEIGHT, LR_SCREENWIDTH * sizeof(uint16_t)); @@ -167,16 +172,18 @@ bool retro_load_game(const struct retro_game_info *info) FILE* romFile; size_t bytesRead; struct retro_input_descriptor desc[] = { - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "OS Menu" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Calender" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Address Book" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "ToDo" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Notes" }, - { 0 }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Dpad Left" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Dpad Up" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Dpad Down" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Dpad Right" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Dpad Middle" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Power" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Contrast" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Notes" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "ToDo" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Address Book" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Calender" }, + { 0 } }; environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc); @@ -250,23 +257,13 @@ bool retro_unserialize(const void *data, size_t size) void *retro_get_memory_data(unsigned id) { - /* - if (id != RETRO_MEMORY_SAVE_RAM) - return NULL; - - return game_data(); - */ + //palm m515 is too complicated for sram, use savestates return NULL; } size_t retro_get_memory_size(unsigned id) { - /* - if (id != RETRO_MEMORY_SAVE_RAM) - return 0; - - return game_data_size(); - */ + //palm m515 is too complicated for sram, use savestates return 0; } diff --git a/src/bps/global.h b/src/bps/global.h new file mode 100644 index 0000000..1a86b41 --- /dev/null +++ b/src/bps/global.h @@ -0,0 +1,52 @@ +//Module name: Floating IPS, global header +//Author: Alcaro +//Date: June 18, 2015 +//Licence: GPL v3.0 or higher + +#ifndef struct_mem +#define struct_mem + +//the standard library can be assumed to exist +#include //size_t, SIZE_MAX +#include //uint8_t + +#ifndef SIZE_MAX +#define SIZE_MAX ((size_t)-1) +#endif + +struct mem { + unsigned char * 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 diff --git a/src/bps/libbps.cpp b/src/bps/libbps.cpp new file mode 100644 index 0000000..e8c1cec --- /dev/null +++ b/src/bps/libbps.cpp @@ -0,0 +1,521 @@ +//Module name: libbps +//Author: Alcaro +//Date: December 20, 2014 +//Licence: GPL v3.0 or higher + +#include "libbps.h" + +#include //malloc, realloc, free +#include //memcpy, memset +#include //uint8_t, uint32_t +#include "crc32.h"//crc32 + +static uint32_t read32(uint8_t * ptr) +{ + uint32_t out; + out =ptr[0]; + out|=ptr[1]<<8; + out|=ptr[2]<<16; + out|=ptr[3]<<24; + return out; +} + +enum { SourceRead, TargetRead, SourceCopy, TargetCopy }; + +#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) +enum bpserror bps_apply(struct mem patch, struct mem in, struct mem * out, struct mem * metadata, bool accept_wrong_input) +{ + enum bpserror error = bps_ok; + out->len=0; + out->ptr=NULL; + if (metadata) + { + metadata->len=0; + metadata->ptr=NULL; + } + if (patch.len<4+3+12) return bps_broken; + + if (true) + { +#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)<len=outlen; + out->ptr=(uint8_t*)malloc(outlen); + + const uint8_t * instart=in.ptr; + const uint8_t * inreadat=in.ptr; + const uint8_t * inend=in.ptr+in.len; + + uint8_t * outstart=out->ptr; + uint8_t * outreadat=out->ptr; + uint8_t * outat=out->ptr; + uint8_t * outend=out->ptr+out->len; + + size_t metadatalen; + decodeto(metadatalen); + + if (metadata && metadatalen) + { + metadata->len=metadatalen; + metadata->ptr=(uint8_t*)malloc(metadatalen+1); + for (size_t i=0;iptr[i]=read8(); + metadata->ptr[metadatalen]='\0';//just to be on the safe side - that metadata is assumed to be text, might as well terminate it + } + else + { + for (size_t i=0;i>2)+1; + int action=(thisinstr&3); + if (outat+length>outend) error(bps_broken); + + switch (action) + { + case SourceRead: + { + if (outat-outstart+length > in.len) error(bps_broken); + for (size_t i=0;ipatchend) error(bps_broken); + for (size_t i=0;i>1; + if ((encodeddistance&1)==0) inreadat+=distance; + else inreadat-=distance; + + if (inreadatinend) error(bps_broken); + for (size_t i=0;i>1; + if ((encodeddistance&1)==0) outreadat+=distance; + else outreadat-=distance; + + if (outreadat=outat || outreadat+length>outend) error(bps_broken); + for (size_t i=0;iptr, out->len); + + if (crc_out_a!=crc_out_e) + { + error=bps_not_this; + if (!accept_wrong_input) goto exit; + } + return error; +#undef read8 +#undef decodeto +#undef write8 + } + +exit: + free(out->ptr); + out->len=0; + out->ptr=NULL; + if (metadata) + { + free(metadata->ptr); + metadata->len=0; + metadata->ptr=NULL; + } + return error; +} + +#define write(val) \ + do { \ + out[outlen++]=(val); \ + if (outlen==outbuflen) \ + { \ + outbuflen*=2; \ + out=(uint8_t*)realloc(out, outbuflen); \ + } \ + } while(0) +#define write32(val) \ + do { \ + uint32_t tmp=(val); \ + write(tmp); \ + write(tmp>>8); \ + write(tmp>>16); \ + write(tmp>>24); \ + } while(0) +#define writenum(val) \ + do { \ + size_t tmpval=(val); \ + while (true) \ + { \ + uint8_t tmpbyte=(tmpval&0x7F); \ + tmpval>>=7; \ + if (!tmpval) \ + { \ + write(tmpbyte|0x80); \ + break; \ + } \ + write(tmpbyte); \ + tmpval--; \ + } \ + } while(0) + +enum bpserror bps_create_linear(struct mem sourcemem, struct mem targetmem, struct mem metadata, struct mem * patchmem) +{ + if (sourcemem.len>=(SIZE_MAX>>2) - 16) return bps_too_big;//the 16 is just to be on the safe side, I don't think it's needed. + if (targetmem.len>=(SIZE_MAX>>2) - 16) return bps_too_big; + + const uint8_t * source=sourcemem.ptr; + const uint8_t * sourceend=sourcemem.ptr+sourcemem.len; + if (sourcemem.len>targetmem.len) sourceend=sourcemem.ptr+targetmem.len; + const uint8_t * targetbegin=targetmem.ptr; + const uint8_t * target=targetmem.ptr; + const uint8_t * targetend=targetmem.ptr+targetmem.len; + + const uint8_t * targetcopypos=targetbegin; + + size_t outbuflen=4096; + uint8_t * out=(uint8_t*)malloc(outbuflen); + size_t outlen=0; + write('B'); + write('P'); + write('S'); + write('1'); + writenum(sourcemem.len); + writenum(targetmem.len); + writenum(metadata.len); + for (size_t i=0;i1) + { + //assert_shift((numunchanged-1), 2); + writenum((numunchanged-1)<<2 | 0);//SourceRead + source+=numunchanged; + target+=numunchanged; + } + + size_t numchanged=0; + if (lastknownchange>target) numchanged=lastknownchange-target; + while ((source+numchanged>=sourceend || + source[numchanged]!=target[numchanged] || + source[numchanged+1]!=target[numchanged+1] || + source[numchanged+2]!=target[numchanged+2]) && + target+numchanged=sourceend) numchanged=targetend-target; + } + lastknownchange=target+numchanged; + if (numchanged) + { + //assert_shift((numchanged-1), 2); + size_t rle1start=(target==targetbegin); + while (true) + { + if ( + target[rle1start-1]==target[rle1start+0] && + target[rle1start+0]==target[rle1start+1] && + target[rle1start+1]==target[rle1start+2] && + target[rle1start+2]==target[rle1start+3]) + { + numchanged=rle1start; + break; + } + if ( + target[rle1start-2]==target[rle1start+0] && + target[rle1start-1]==target[rle1start+1] && + target[rle1start+0]==target[rle1start+2] && + target[rle1start+1]==target[rle1start+3] && + target[rle1start+2]==target[rle1start+4]) + { + numchanged=rle1start; + break; + } + if (rle1start+3>=numchanged) break; + rle1start++; + } + if (numchanged) + { + writenum((numchanged-1)<<2 | TargetRead); + for (size_t i=0;iptr=out; + patchmem->len=outlen; + + //while this may look like it can be fooled by a patch containing one of any other command, it + // can't, because the ones that aren't SourceRead requires an argument. + size_t i; + for (i=mainContentPos;(out[i]&0x80)==0x00;i++) {} + if (i==outlen-12-1) return bps_identical; + + return bps_ok; +} + +#undef write_nocrc +#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 + +//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)<=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 (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 diff --git a/src/bps/libbps.h b/src/bps/libbps.h new file mode 100644 index 0000000..8894b98 --- /dev/null +++ b/src/bps/libbps.h @@ -0,0 +1,70 @@ +//Module name: libbps +//Author: Alcaro +//Date: June 18, 2015 +//Licence: GPL v3.0 or higher + +#include "global.h" +#include + +#ifndef __cplusplus +#include //bool; if this file does not exist (hi msvc), remove it and uncomment the following three lines. +//#define bool int +//#define true 1 +//#define false 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +enum bpserror { + bps_ok,//Patch applied or created successfully. + + 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_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. +}; + +//Applies the given BPS patch to the given ROM and puts it in 'out'. Metadata, if present and +// requested ('metadata'!=NULL), is also returned. Send both to bps_free when you're done with them. +//If accept_wrong_input is true, it may return bps_to_output or bps_not_this, while putting non-NULL in out/metadata. +enum bpserror bps_apply(struct mem patch, struct mem in, struct mem * out, struct mem * metadata, bool accept_wrong_input); + +//Creates a BPS patch that converts source to target and stores it to patch. It is safe to give +// {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); + +#ifdef __cplusplus +} +#endif diff --git a/src/emulator.c b/src/emulator.c index 43d66a4..2e0ec82 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -36,6 +36,19 @@ double palmCrystalCycles;//how many cycles before toggling the 32.768 kHz cry double palmCycleCounter;//can be greater then 0 if too many cycles where run double palmClockMultiplier;//used by the emulator to overclock the emulated palm +uint64_t (*emulatorGetSysTime)(); +uint64_t* (*emulatorGetSdCardStateChunkList)(uint64_t sessionId, uint64_t stateId);//returns the bps chunkIds for a stateId in the order they need to be applied +void (*emulatorSetSdCardStateChunkList)(uint64_t sessionId, uint64_t stateId, uint64_t* data);//sets the bps chunkIds for a stateId in the order they need to be applied +uint8_t* (*emulatorGetSdCardChunk)(uint64_t sessionId, uint64_t chunkId); +void (*emulatorSetSdCardChunk)(uint64_t sessionId, uint64_t chunkId, uint8_t* data, uint64_t size); + + +static inline bool allSdCardCallbacksPresent(){ + if(emulatorGetSysTime && emulatorGetSdCardStateChunkList && emulatorSetSdCardStateChunkList && emulatorGetSdCardChunk && emulatorSetSdCardChunk) + return true; + return false; +} + void emulatorInit(uint8_t* palmRomDump, uint32_t specialFeatures){ //cpu @@ -72,9 +85,10 @@ void emulatorInit(uint8_t* palmRomDump, uint32_t specialFeatures){ palmInput.touchscreenY = 0; palmInput.touchscreenTouched = false; - palmSdCard.getSdCardChunk = NULL; - palmSdCard.setSdCardChunk = NULL; + palmSdCard.sessionId = 0x0000000000000000; + palmSdCard.stateId = 0x0000000000000000; palmSdCard.size = 0; + palmSdCard.type = CARD_NONE; palmSdCard.inserted = false; palmMisc.powerButtonLed = false; @@ -105,11 +119,26 @@ void emulatorSetRtc(uint32_t days, uint32_t hours, uint32_t minutes, uint32_t se setRtc(days, hours, minutes, seconds); } +uint32_t emulatorSetSdCard(uint64_t size, uint8_t type){ + if(!allSdCardCallbacksPresent()) + return EMU_ERROR_CALLBACKS_NOT_SET; + + palmSdCard.sessionId = emulatorGetSysTime(); + palmSdCard.stateId = 0x0000000000000000;//set when saving state + palmSdCard.size = size; + palmSdCard.type = type; + palmSdCard.inserted = true; + + return EMU_ERROR_NONE; +} + uint32_t emulatorGetStateSize(){ - return m68k_context_size() + RAM_SIZE + REG_SIZE + sizeof(misc_hw_t) + TOTAL_MEMORY_BANKS + sizeof(double) * 5 + 1; + return m68k_context_size() + RAM_SIZE + REG_SIZE + TOTAL_MEMORY_BANKS + sizeof(misc_hw_t) + sizeof(sdcard_t) + sizeof(double) * 4 + sizeof(uint32_t) * 2 + 1; } void emulatorSaveState(uint8_t* data){ + //save sdcard here + uint32_t offset = 0; m68k_get_context(data + offset); offset += m68k_context_size(); @@ -121,6 +150,8 @@ void emulatorSaveState(uint8_t* data){ offset += TOTAL_MEMORY_BANKS; memcpy(data + offset, &palmMisc, sizeof(misc_hw_t)); offset += sizeof(misc_hw_t); + memcpy(data + offset, &palmSdCard, sizeof(sdcard_t)); + offset += sizeof(sdcard_t); //input is not part of savestates memcpy(data + offset, &palmSpecialFeatures, sizeof(uint32_t)); offset += sizeof(uint32_t); @@ -139,6 +170,8 @@ void emulatorSaveState(uint8_t* data){ } void emulatorLoadState(uint8_t* data){ + //load sdcard here + uint32_t offset = 0; m68k_set_context(data + offset); offset += m68k_context_size(); @@ -150,6 +183,8 @@ void emulatorLoadState(uint8_t* data){ offset += TOTAL_MEMORY_BANKS; memcpy(&palmMisc, data + offset, sizeof(misc_hw_t)); offset += sizeof(misc_hw_t); + memcpy(&palmSdCard, data + offset, sizeof(sdcard_t)); + offset += sizeof(sdcard_t); //input is not part of savestates memcpy(&palmSpecialFeatures, data + offset, sizeof(uint32_t)); offset += sizeof(uint32_t); diff --git a/src/emulator.h b/src/emulator.h index a786e05..6bc3fee 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -8,33 +8,62 @@ #include "emuFeatureRegistersSpec.h" //emu errors -enum emu_error_t{ +enum{ EMU_ERROR_NONE = 0, EMU_ERROR_NOT_IMPLEMENTED, + EMU_ERROR_CALLBACKS_NOT_SET, EMU_ERROR_NOT_DOCUMENTED }; +//sdcard types +enum{ + CARD_NONE = 0, + CARD_SD, + CARD_MMC +}; + +//memory banks +enum{ + EMPTY_BANK = 0, + RAM_BANK, + ROM_BANK, + REG_BANK, + SED1376_REG_BANK, + SED1376_FB_BANK +}; + //types typedef struct{ bool buttonUp; bool buttonDown; bool buttonLeft;//only used in hybrid mode bool buttonRight;//only used in hybrid mode + bool buttonSelect;//only used in hybrid mode + bool buttonCalender;//hw button 1 bool buttonAddress;//hw button 2 bool buttonTodo;//hw button 3 bool buttonNotes;//hw button 4 + bool buttonPower; bool buttonContrast; + uint16_t touchscreenX; uint16_t touchscreenY; bool touchscreenTouched; }input_t; typedef struct{ - void (*getSdCardChunk)(uint8_t* data, uint32_t size); - void (*setSdCardChunk)(uint8_t* data, uint32_t size); + //the sdcard is stored as a directory structure with an /(SESSIONID)/sdcard(STATEID).info and /(SESSIONID)/sdcard(CHUNKID).bps + //each emulator instance has its own sdcard SESSIONID directory if it has an sdcard + //sdcard(STATEID).info specifys the bps files to used + //every savestate a new bps file is created with the changes made to the sdcard since the last savestate + //the first bps is a patch over a buffer of sdcard size filled with 0x00 + //the frontend provides file access but the emulator does all the patching + uint64_t sessionId;//64 bit system time when emulator starts + uint64_t stateId;//64 bit system time when the savestate was taken uint64_t size; + uint8_t type; bool inserted; }sdcard_t; @@ -71,12 +100,6 @@ typedef struct{ #define END_BANK(address, size) (START_BANK(address) + NUM_BANKS(size)) #define BANK_IN_RANGE(bank, address, size) (bank >= START_BANK(address) && bank <= END_BANK(address, size)) #define TOTAL_MEMORY_BANKS 0x10000 -#define EMPTY_BANK 0 -#define RAM_BANK 1 -#define ROM_BANK 2 -#define REG_BANK 3 -#define SED1376_REG_BANK 4 -#define SED1376_FB_BANK 5 //memory chip addresses #define RAM_START_ADDRESS 0x00000000 @@ -104,10 +127,18 @@ extern double palmCrystalCycles; extern double palmCycleCounter; extern double palmClockMultiplier; +//callbacks +uint64_t (*emulatorGetSysTime)(); +uint64_t* (*emulatorGetSdCardStateChunkList)(uint64_t sessionId, uint64_t stateId);//returns the bps chunkIds for a stateId in the order they need to be applied +void (*emulatorSetSdCardStateChunkList)(uint64_t sessionId, uint64_t stateId, uint64_t* data);//sets the bps chunkIds for a stateId in the order they need to be applied +uint8_t* (*emulatorGetSdCardChunk)(uint64_t sessionId, uint64_t chunkId); +void (*emulatorSetSdCardChunk)(uint64_t sessionId, uint64_t chunkId, uint8_t* data, uint64_t size); + //functions void emulatorInit(uint8_t* palmRomDump, uint32_t specialFeatures); 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); uint32_t emulatorGetStateSize(); void emulatorSaveState(uint8_t* data); void emulatorLoadState(uint8_t* data); diff --git a/src/makefile.all b/src/makefile.all index ab94f9a..bd396d3 100644 --- a/src/makefile.all +++ b/src/makefile.all @@ -2,6 +2,7 @@ EMU_SOURCES := $(EMU_PATH)/emulator.c \ $(EMU_PATH)/memoryAccess.c \ $(EMU_PATH)/hardwareRegisters.c \ $(EMU_PATH)/sed1376.c \ + $(EMU_PATH)/sdcard.c \ $(EMU_PATH)/silkscreen.c \ $(EMU_PATH)/cpu32Opcodes.c \ $(EMU_PATH)/m68k/m68kops.c \ diff --git a/src/sdcard.c b/src/sdcard.c new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/sdcard.c @@ -0,0 +1 @@ + diff --git a/src/sdcard.h b/src/sdcard.h new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/sdcard.h @@ -0,0 +1 @@ + diff --git a/tests/HwRegTest17.txt b/tests/HwRegTest17.txt new file mode 100644 index 0000000..9a31646 --- /dev/null +++ b/tests/HwRegTest17.txt @@ -0,0 +1,25 @@ +New CPU frequency of:33161216.000000 cycles per second. +New clk32 cycle count of :1012.000000. +Cpu Wrote 8 bits of 0x00000000 to register 0x0439. +Cpu Wrote 8 bits of 0x00000020 to register 0x0449. +Cpu Wrote 8 bits of 0x00000000 to register 0x0A27. +Cpu Wrote 16 bits of 0x00008000 to register 0x0100. +Cpu Wrote 16 bits of 0x000001AB to register 0x0110. +Cpu Wrote 16 bits of 0x0000FFC0 to register 0x0102. +Cpu Wrote 16 bits of 0x000001F3 to register 0x0112. +Cpu Wrote 16 bits of 0x00008200 to register 0x0104. +Cpu Wrote 16 bits of 0x00000101 to register 0x0114. +Cpu Wrote 16 bits of 0x00000000 to register 0x0106. +Cpu Wrote 16 bits of 0x00001E83 to register 0x0116. +Cpu Wrote 16 bits of 0x00000040 to register 0x010A. +Cpu Wrote 16 bits of 0x00001A81 to register 0x0116. +Cpu Read 16 bits from register 0x0310. +Cpu Read 8 bits from register 0x0418. +Cpu Read 8 bits from register 0x0440. +Cpu Read 8 bits from register 0x0441. +Cpu Read 8 bits from register 0x0441. +New CPU frequency of:33161216.000000 cycles per second. +New clk32 cycle count of :1012.000000. +Cpu Read 16 bits from register 0x0C02. +New CPU frequency of:33161216.000000 cycles per second. +New clk32 cycle count of :1012.000000. diff --git a/unimplementedFeatures.txt b/unimplementedFeatures.txt index cdbf9ff..28698a2 100644 --- a/unimplementedFeatures.txt +++ b/unimplementedFeatures.txt @@ -15,6 +15,7 @@ DRAMC write enable All of DRAMMC Chip select registers Setting the battery dead bit(PDDATA 0x80) if the emulated battery percent gets too low +endian safe savestates Fixed: System Control Register, 0xXXFFF000 all upper banks are registers mode