02-04-2026, 02:58 AM
So, I had a look at your project and couldn't really figure out where you wanted to insert your load/save - so, what I've done in DDMR etc is to abstract the save code into it's own functions
And then changed (a little bit by trial and error) the original library.c functions like so:
The magic number can be anything you like, it's literally just a sequence of bytes that you wouldn't expect to see normally. Thor's original one was to use 0xcafebabe and I didn't see the need to change that
The last piece of the puzzle, at least when you flash to flashmasta/retrohq is to pad the ROM file to 2mb - winteriscoming used a .py script for that, but I used the following windows command script to do the same thing (so I don't have to have python installed) - certutil and fsutil should be available on any standard Win10/11 installation
Code:
void GetDDMRData(u16 *Palette, u8 *Difficulty, SCORE *scoretable)
{
u32 data[256];
u8 iScoreLoop;
GetFlash(&data);
// Set defaults
*Palette=RGB(0,15,15);
*Difficulty=1;
switch(data[0])
{
case MAGIC_NB:
{
// Restore from
SetRandomCounterSeed((u16)data[1]);
*Palette=(u16)data[2];
*Difficulty=(u8)data[3];
for(iScoreLoop=0;iScoreLoop<(HST_SIZE*3);iScoreLoop++)
{
scoretable[iScoreLoop].Initial1=(data[4+(iScoreLoop<<1)]&0xff000000)>>24;
scoretable[iScoreLoop].Initial2=(data[4+(iScoreLoop<<1)]&0x00ff0000)>>16;
scoretable[iScoreLoop].Initial3=(data[4+(iScoreLoop<<1)]&0x0000ff00)>>8;
scoretable[iScoreLoop].Score=(u16)(data[5+(iScoreLoop<<1)]&0x0000ffff);
}
}
break;
case MAGIC_NB+1:
{
// Set defaults
InitialiseQRandom();
for(iScoreLoop=0;iScoreLoop<(HST_SIZE*3);iScoreLoop++)
{
scoretable[iScoreLoop].Initial1=65;
scoretable[iScoreLoop].Initial2=65;
scoretable[iScoreLoop].Initial3=65;
scoretable[iScoreLoop].Score=0;
}
}
break;
default:
// Shouldn't be anything else
break;
}
}
/**********************************
* Saves data into flash memory:
* - QRandom() seed
* - Mr Robot's palette
* - High score table
*
* First byte will be the MAGIC_NB
* and subsequent bytes will be the stored data
* data[1] == Random Seed
* data[2] == Mr Robot Palette
* data[3]-data[3+(HST_SIZE*2)] == HighScore Table
* High Score entry
* byte0=INITIALS
* byte1=SCORE
**********************************/
void SaveDDMR(u16 MrRobotPalette, u8 Difficulty, SCORE HighScoreTable[])
{
u32 data[256];
u8 iScoreLoop;
GetFlash(&data);
data[0]=MAGIC_NB;
data[1]=GetRandomCounterSeed();
data[2]=MrRobotPalette;
data[3]=Difficulty;
for(iScoreLoop=0;iScoreLoop<(HST_SIZE*3);iScoreLoop++)
{
data[4+(iScoreLoop<<1)]=((u32)HighScoreTable[iScoreLoop].Initial1<<24)+((u32)HighScoreTable[iScoreLoop].Initial2<<16)+((u32)HighScoreTable[iScoreLoop].Initial3<<8);
data[5+(iScoreLoop<<1)]=((u32)HighScoreTable[iScoreLoop].Score);
}
SetFlash(data);
}And then changed (a little bit by trial and error) the original library.c functions like so:
Code:
void SetFlash(void *data) {
// The "data" array needs to have the magic number (MAGIC_NB)
// in position zero
// Calling function needs to set this as follows:
// data[0]=MAGIC_NB
// What you do with the remaining elements is up to you
// Set the save offset and block number
// These need to be equal to the ptr number in GetFlash()
// SAVEOFFSET 0x1FA000
// +
// BLOCK_NB 0x21
// = 0x3FA000
// No, I don't quite get it either, but these are the numbers that
// seem to work...
__ASM("SAVEOFFSET EQU 0x1FA000");
__ASM("BLOCK_NB EQU 0x21");
// Other variables are fixed and refer to the various system calls required by the flash write
__ASM("VECT_FLASHWRITE EQU 6");
__ASM("VECT_FLASHERS EQU 8");
__ASM("rWDCR EQU 0x6f");
__ASM("WD_CLR EQU 0x4e");
// Erase block first (mandatory) : 64kb for only 256 bytes
__ASM(" ld ra3,0");
__ASM(" ld rb3,BLOCK_NB");
__ASM(" ld rw3,VECT_FLASHERS");
__ASM(" ld (rWDCR),WD_CLR");
__ASM(" swi 1");
// Then write data
// Set low/high data size (0 low + 2 high=512 bytes, I think?)
__ASM(" ld ra3,0");
__ASM(" ld rbc3,2"); // 512 bytes
// Call the Flash write routine
__ASM(" ld xhl,(xsp+4)");
__ASM(" ld xhl3,xhl");
__ASM(" ld xde3,SAVEOFFSET");
__ASM(" ld rw3,VECT_FLASHWRITE");
__ASM(" ld (rWDCR),WD_CLR");
__ASM(" swi 1");
__ASM(" ld (rWDCR),WD_CLR");
}
void GetFlash(void *data) {
// Set the pointers up to the flash memory address
// Needs to be the same as address used in SetFlash()
u32 *ptr = (u32*)(0x3FA000);
u32 *ptrData = (u32*)data;
u8 i;
// Check the read data for the "magic" number
// Basically, an arbitary number that we have in data[0]
// If this isn't set then the data hasn't been initialised
if (*ptr == MAGIC_NB) // Data saved
{
for (i=0;i<128;i++)
ptrData[i] = ptr[i];
}
else // No data
{
// If the magic number is not set, then set MAGIC_NB+1 to the first element of the array
// and then set everything else to zero
// The calling routine can then set the array up as you see fit (default values etc)
// and then set data[0]=MAGIC_NB to enable writing
ptrData[0] = MAGIC_NB+1;
for (i=1;i<128;i++)
ptrData[i] = 0x00;
}
}The magic number can be anything you like, it's literally just a sequence of bytes that you wouldn't expect to see normally. Thor's original one was to use 0xcafebabe and I didn't see the need to change that
The last piece of the puzzle, at least when you flash to flashmasta/retrohq is to pad the ROM file to 2mb - winteriscoming used a .py script for that, but I used the following windows command script to do the same thing (so I don't have to have python installed) - certutil and fsutil should be available on any standard Win10/11 installation
Code:
ECHO Padding:
echo ff ff ff ff ff ff ff ff>ff.txt
certutil -decodehex ff.txt ff.pad
FOR /L %%f IN (1,1,18) DO (
ECHO PASS %%f
COPY /b ff.pad+ff.pad ff.pad2 >NUL
DEL ff.pad
REN ff.pad2 ff.pad
)
copy /b MRROBOT.ngp+ff.pad TEMP.ngp
del MRROBOT.ngp
del ff.txt
del ff.pad
ren TEMP.ngp MRROBOT.ngp
fsutil file setEOF MRROBOT.ngp 2097152
