Stephane-D / SGDK

SGDK - A free and open development kit for the Sega Mega Drive
https://www.patreon.com/SGDK
MIT License
1.74k stars 187 forks source link

flash-save sample report HW error on BlastEm #330

Closed skarab closed 5 months ago

skarab commented 5 months ago

Hello, as the title say the sample doesnt work with Blastem, I tried switching Blastem options but it does not work, am I missing something ? Regards

Stephane-D commented 5 months ago

I'm not the author of this sub module but i do believe that Blastem doesn't support flash saving, afaik no emulator support that feature still it's probably reasonable to ask the original author (@doragasu)

skarab commented 5 months ago

Ok. I just tested with Sonic 3 and the save slots works on Blastem. Will try to contact him.

skarab commented 5 months ago

I just wrote on it on Spritesmind, closing it here.

Stephane-D commented 5 months ago

This is different from the sonic 3 save with is just f-ram but using the classic SRAM mapper, here it uses the flash EPROM to save game into it.

skarab commented 5 months ago

Oh ok, i misanderstood, so it's completly different of this https://huguesjohnson.com/programming/genesis/save-load/ ? Maybe I may try to use this.

skarab commented 5 months ago

Maybe I could just use your sram.h helper

Stephane-D commented 5 months ago

Maybe I could just use your sram.h helper

Yeah, that's the API to use classic SRAM :)

skarab commented 5 months ago

Awesome it works fine with Blastem :)

Made helpers to load / save structs (32bits aligned) with a "checksum" test :

` bool smeSRAM_Load(u32 offset, void data, u32 length) { u32 i; u32 count = length >> 2; u32 dat = (u32*)data;

SYS_disableInts();
SRAM_enableRO();
u32 checksum = SRAM_readLong(offset);
if (checksum == CHECKSUM)
{
    for (i = 0; i < count; ++i)
    {
        dat[i] = SRAM_readLong(offset + 4 + (i << 2));
    }
}
SRAM_disable();
SYS_enableInts();
return checksum == CHECKSUM;

}

void smeSRAM_Save(u32 offset, void data, u32 length) { u32 i; u32 count = length >> 2; u32 dat = (u32*)data;

SYS_disableInts();
SRAM_enable();
SRAM_writeLong(offset, CHECKSUM);
for (i = 0; i < count; ++i)
{
    SRAM_writeLong(offset + 4 + (i << 2), dat[i]);
}
SRAM_disable();
SYS_enableInts();

}`

skarab commented 5 months ago

will add real checksum at the end i think, but having header number is cool to detect if slot is free or not fastly.

skarab commented 5 months ago

`

define MAGIC_NUMBER 1331

bool smeSRAM_Load(u32 offset, void data, u32 length) { u32 i; u32 count = length >> 2; u32 dat = (u32*)data;

SYS_disableInts();
SRAM_enableRO();
u32 magic_number = SRAM_readLong(offset);
u32 checksum = 0;
if (magic_number == MAGIC_NUMBER)
{
    for (i = 0; i < count; ++i)
    {
        dat[i] = SRAM_readLong(offset + 4 + (i << 2));
        checksum += dat[i];
    }
}
u32 stored_checksum = SRAM_readLong(offset + 4 + (count << 2));
SRAM_disable();
SYS_enableInts();
return magic_number == MAGIC_NUMBER && stored_checksum == checksum;

}

void smeSRAM_Save(u32 offset, void data, u32 length) { u32 i; u32 count = length >> 2; u32 dat = (u32*)data;

SYS_disableInts();
SRAM_enable();
SRAM_writeLong(offset, MAGIC_NUMBER);
u32 checksum = 0;
for (i = 0; i < count; ++i)
{
    SRAM_writeLong(offset + 4 + (i << 2), dat[i]);
    checksum += dat[i];
}
SRAM_writeLong(offset + 4 + (count << 2), checksum);
SRAM_disable();
SYS_enableInts();

}

bool smeSRAM_IsValid(u32 offset) { SYS_disableInts(); SRAM_enableRO(); u32 magic_number = SRAM_readLong(offset); SRAM_disable(); SYS_enableInts(); return magic_number == MAGIC_NUMBER; } `

skarab commented 5 months ago

then instead of offset, could be cool to have some api like the saveman, u32 slotId = AddSlot(u32 size), and base all this code on slots ids

skarab commented 5 months ago

A bit row but it seams to work :)

`

define MAGIC_NUMBER 1331

bool smeSRAM_Load(u32 offset, void data, u32 length) { u32 i; u32 count = length >> 2; u32 remaining = count >> 2 - length; u32 dat = (u32*)data;

SYS_disableInts();
SRAM_enableRO();
u32 magic_number = SRAM_readLong(offset);
u32 checksum = 0;
if (magic_number == MAGIC_NUMBER)
{
    for (i = 0; i < count; ++i)
    {
        dat[i] = SRAM_readLong(offset + 4 + (i << 2));
        checksum += dat[i];
    }
}
u32 stored_checksum = SRAM_readLong(offset + 4 + (count << 2));
SRAM_disable();
SYS_enableInts();
return magic_number == MAGIC_NUMBER && stored_checksum == checksum;

}

void smeSRAM_Save(u32 offset, void data, u32 length) { u32 i; u32 count = length >> 2; u32 dat = (u32*)data;

SYS_disableInts();
SRAM_enable();
SRAM_writeLong(offset, MAGIC_NUMBER);
u32 checksum = 0;
for (i = 0; i < count; ++i)
{
    SRAM_writeLong(offset + 4 + (i << 2), dat[i]);
    checksum += dat[i];
}
SRAM_writeLong(offset + 4 + (count << 2), checksum);
SRAM_disable();
SYS_enableInts();

}

bool smeSRAM_IsValid(u32 offset) { SYS_disableInts(); SRAM_enableRO(); u32 magic_number = SRAM_readLong(offset); SRAM_disable(); SYS_enableInts(); return magic_number == MAGIC_NUMBER; }

void smeSRAM_Erase(u32 offset) { SYS_disableInts(); SRAM_enable(); SRAM_writeLong(offset, 0); SRAM_disable(); SYS_enableInts(); }

typedef struct { u32 offset; u32 size; } smeSRAMSlot;

define MAXIMUM_SLOT_COUNT 8

smeSRAMSlot SRAMSlots[MAXIMUM_SLOT_COUNT]; u8 SRAMSlotCount = 0;

void smeSRAM_SLOTS_Initialize() { SRAMSlotCount = 0; }

u8 smeSRAM_SLOTS_Add(u32 size) { SRAMSlots[SRAMSlotCount].offset = SRAMSlotCount == 0 ? 0 : SRAMSlots[SRAMSlotCount - 1].offset + SRAMSlots[SRAMSlotCount - 1].size + 8; SRAMSlots[SRAMSlotCount].size = size; ++SRAMSlotCount; }

bool smeSRAM_SLOTS_Load(u8 slotId, void* data) { return smeSRAM_Load(SRAMSlots[slotId].offset, data, SRAMSlots[slotId].size); }

void smeSRAM_SLOTS_Save(u8 slotId, void* data) { smeSRAM_Save(SRAMSlots[slotId].offset, data, SRAMSlots[slotId].size); }

bool smeSRAM_SLOTS_IsValid(u8 slotId) { return smeSRAM_IsValid(SRAMSlots[slotId].offset); }

void smeSRAM_SLOTS_Erase(u8 slotId) { smeSRAM_Erase(SRAMSlots[slotId].offset); }

`

doragasu commented 5 months ago

Hi, it looks like for you the issue is worked around by using SRAM save, but I will add a bit here for completeness.

The Flash save module uses a completely different mechanism to save data, taking advantage of the flash chips used in most carts released nowadays. Unfortunately AFAIK no emulator supports this as of today. For this to work on emulators, emulator devs need to emulate the flash chip commands. So this issue should be better reported to BlastEm! author.

skarab commented 5 months ago

Hi Doragasu, ok I understand now, should work well with recent flash cards. I added a msg on the SpritesMind forum. Anyway I'll stick on SRAM I think, will contact dragonbox later on (free digital game but would like to have cool box later). But... it looks an API supporting both may be created in SGDK :) Cheers

doragasu commented 5 months ago

That could be great, Kusfo did something similar for his game Gotris on SEGA Master System, the game tries to initialize the Flash, and if it fails, falls back to SRAM (so it can save data on my flash-based FrugalMapper carts, but also on emulators).

skarab commented 5 months ago

Ok, I'll do this then. Awesome idea, and opens future doors.