(SSRG mirror) Over the past few days, I've been learning how to work with SRAM (Save RAM), and I think I've gotten it down. For those who don't know, SRAM is used for saving data once the Mega Drive is turned off. Sonic 3 uses it to store 6 entries that contain: Your selected character, your current zone, your Chaos Emeralds. And with Sonic 3 and Knuckles, you get 2 more save slots, and your lives and continues are saved. Implementing and using SRAM is surprisingly easy. And only requires a few steps. Initialization First your project needs to be able to actually use SRAM. There's a part of the header that determines this information. Code (Text): SRAM_Support dc.l $20202020 ; Null SRAM Start_of_SRAM dc.l $20202020 ; Start of SRAM memory space End_of_SRAM dc.l $20202020 ; End of SRAM memory space As is, the SRAM is using a null value and is therefore inactive. To make it active you'll need to change the contents of SRAM support. The format is pretty simple: "RA",XX,$20. XX is based on the type of SRAM you want. Here are your options: $A0 - No saving, 16-bit addresses $B0 - No saving, even 8-bit addresses $B8 - No saving, odd 8-bit addresses $E0 - Saving, 16-bit addresses $F0 - Saving, even 8-bit addresses $F8 - Saving, odd 8-bit addresses You'd most likely want to use $F8 (Saving with odd 8-bit addresses) so here's what that would look like: Code (Text): SRAM_Support dc.b "RA",$F8,$20 You can then choose your space for SRAM memory. The header is now set up to allow for SRAM. From here on out you can do whatever you please. But I still need to go over a few things. If you were to attempt and load SRAM data that doesn't exist, you'd most likely get a crash. This can be prevented with a routine to check if SRAM exists. Code (Text): InitSRAM: move.b #1,($A130F1).l ; Enable SRAM writing lea ($200001).l,a0 ; Load SRAM memory into a0 (Change the last digit to 0 if you're using even SRAM) movep.l 0(a0),d0 ; Get the existing string at the start of SRAM move.l #"SRAM",d1 ; Write the string "SRAM" to d1 cmp.l d0,d1 ; Was it already in SRAM? beq.s @Continue ; If so, skip movep.l d1,0(a0) ; Write string "SRAM" ; Here is where you initialize values like lives or level. If you're using 8 bit values, you can only use every other byte. ; Example - 8(a0) => $A(a0) @Continue: move.b #0,($A130F1).l ; Disable SRAM writing I recommend you place this before the main game loop routine. Using SRAM Everything is ready to be used. How you use it is up to you, here are just some notes. You must enable and disable SRAM writing mode before and after you do SRAM writing. You have to lea the SRAM memory into an address register when using 8-bit SRAM and long word values. You must use movep to move memory into SRAM when using 8-bit SRAM and long word values. You cannot directly move values into SRAM, you'd have to do it via a register when using 8-bit SRAM and long word values. You can just directly use move.b into the SRAM memory you want when using byte values. If you have suggestions, fixes, additions, or even some cool results, let me know. (Seriously, I got very little feedback on this, and it concerns me because I know I'm not perfect at SRAM yet). Other than that, enjoy saving!
I believe I read somewhere that there are no emulators or carts that use the SRAM header entry, but I've never tested that myself. It's still best practice to make it accurate, though, so $F8 is the safest bet. Also, I think I've had 16-bit saving working on my EverDrive x5, and I believe most other carts and emus support that, but it's been a while since I did anything with SRAM. Something I'll be trying with my current project is "formatting" the SRAM on first boot, to determine the size and format and writing both to the start of the SRAM as a header. Beyond that, your understanding looks sound. PS. Sik recommends doing a parity check.
For KEH, I ported the save file checksum code from Sonic Adventure and used that as an extra verification/anti-tamper layer. The code for the hack is on the wiki, so you can just take it if you want. I also encrypted the save data with an XOR using the play time as the key, and byteswapped it.
"You must enable and disable SRAM writing mode before and after you do SRAM writing." You only have to do that if the rom is over 2MB. If it is under 2MB, you can always leave it enabled (which this is done by default). There was an article that I backed up that goes into more details. I will post it later on.
Indeed, this is how Sonic 3 works, leading to this issue with the S3 disasm. S3K added proper gating of the SRAM flag to all the save game code.
Interesting about the Sram. I don't know much about "Sonic 3", but your description has coincidence with the pager I decided with. It's the 74ls612 which lists the page table through 16 registers (each is essentially a partition, region or segment). Mars CD memory space being four megabytes divided by 16 provides 256 kilobyte size pages. Since 74ls612 has an 8bit page table, that's about 64 megabytes of paged ram for CD programs (e.g. page table size * region). It's a bit heavier than Sega's 315-5755 6bit pager (nicknamed SSFII Mapper) but at least it's well documented, accessible, easy to follow, and it has a useful passthrough feature which truncates the pager all together via register to access contiguous memory directly (four megabytes on Mars CD). MarsCD System mode 2 with Function 2 reciprocates program startup location (e.g. I.P S.P.) so becomes 4ffff to 7ffff. https://segaxtreme.net/threads/exte...for-mars-cd-developers-and-programming.24477/ https://www.sega-16.com/forum/showt...formance-myths&p=850115&viewfull=1#post850115