Page 1 of 2

SRAM read / write

Posted: Sun Sep 29, 2019 11:31 pm
by cloudstrifer
How to use SRAM_readByte and SRAM_writeByte?

:(
Spy:28464 want to write a Byte at 00600001

Thank you!

Code: Select all

//write score
SRAM_writeWord(0x00200000, score);
...
//read score
score= SRAM_readWord(0x00200000);

Re: SRAM read / write

Posted: Mon Sep 30, 2019 8:14 am
by Stef
SGDK uses SEGA SRAM mapper so it assumes base SRAM address to be 0x020000 and just require an offset (that is why the argument is called offset and not address).
So in your case just use offset 0x0000.
Also don't forget to enable SRAM (SRAM_enable()) before trying to read/write from/to it, then when it's done disable it otherwise ROM located at 0x020000 won't be accessible anymore.

Re: SRAM read / write

Posted: Tue Oct 01, 2019 2:56 pm
by Chilly Willy
And remember that the code to read/write the sram should be in either work ram or rom below 0x200000, and if you have any interrupts that use code or data above 0x200000, you need to disable ints before you enable the sram, then re-enable ints after you disable the sram.

EDIT: Stef, that reminds me. I don't see anything in SGDK to keep the sram routines from being above 0x200000. They're part of the library, and libraries are normally linked to the end of program code. So if the game is bigger than 0x200000, there's nothing to keep those functions from being above 0x200000 and crash when you enable the sram.

I'd suggest making all sram functions assembly, and put the code in the sega.s file to ensure it's not too high in the rom. Make the library functions call the functions in sega.s... which would mean each function would need to disable ints, enable sram, do the read/write, disable sram, then enable ints, just to be safe. So the SRAM functions need some changing to avoid issues on big games.

If you don't want to change the library that much, your other choice is to force the SRAM functions into work ram. You do that like this:

Code: Select all

void sd_op_delay() __attribute__ ((section (".data")));
void sd_op_delay()
{
    short i;

    for (i=0; i<16; i++)
    {
        asm("nop\n");
    }
}
That will still need the game programmer to ensure that his code that calls SRAM functions aren't above 0x200000. Changing the SRAM functions to work from sega.s makes them slower, but safe regardless of how they're used.

Re: SRAM read / write

Posted: Tue Oct 01, 2019 3:43 pm
by cloudstrifer
I want to make a 32Mbit rom.
There is a way to SGDK or compiler skip addresses between SRAM start and end?

Thank you!

Re: SRAM read / write

Posted: Tue Oct 01, 2019 4:39 pm
by Stef
@Chilly Willy> Yeah indeed nothing prevent it. Even worse looking at OBJ building in makefile it looks like resources is placed before code, not a good idea. I may move SRAM methods in sega.s file (at least enable and disable functions), i don't like that solution too much but it's important to take care of that. I will also change OBJ order to get resources at the end, there is no way to get 2 MB of code :p

Re: SRAM read / write

Posted: Tue Oct 01, 2019 5:19 pm
by Chilly Willy
Having any of the enable, read, write, or disable code not below 0x200000 or in ram would be fatal. What good is enable/disable is read/write still bombs? That said, if code always comes first (even for libraries linked at the end), it's unlikely you'll have any programs with more than 2M of code. Possible, but not likely. But there should at least be notes to programmers informing them of the dangers in case anyone does something like megs of unwound code for speed in rendering or the like.

Re: SRAM read / write

Posted: Tue Oct 01, 2019 5:54 pm
by cero
The linker script puts code first, then rodata. It's very unlikely for you to ever have 2mb of code.

Re: SRAM read / write

Posted: Tue Oct 01, 2019 8:20 pm
by Miquel
Another solution is to create a new section and place those functions in this new section. Or reuse other previously created section. The only thing to look after is how sections are ordered.

There is no relationship between sections and files, or n<->m relationship if you want.

All this is specific to gcc.

Re: SRAM read / write

Posted: Tue Oct 01, 2019 10:03 pm
by Chilly Willy
It can be controlled with the linker script and custom sections, or putting the functions in ram, or in sega.s, or any number of ways. Or ignored since no one is going to make an SGDK game with more than 2M of code... until someone does. :lol:

But that's why I was mentioning the issue now. Better to deal with it now and clear it up than to ignore it and wonder what's going on years later when someone does make a game with more than 2M of code and uses the sram.

If we all throw out enough ideas, Stef will have a bunch of ideas to figure out what he thinks will work best with SGDK.

Re: SRAM read / write

Posted: Wed Oct 02, 2019 7:52 am
by Stef
Oh why i was think having enable / disable was enough ? i guess i need more sleep :lol:
But if linker script always put code *before* data then it's really unlikely to meet the problem i guess.. I maybe move them in sega.s for security.

Re: SRAM read / write

Posted: Wed Oct 02, 2019 9:56 am
by Miquel
How code (and if applies data) is structured depends on optimization flags, mostly related to cache hit and miss, there is no way to guarantee anything.

Re: SRAM read / write

Posted: Wed Oct 02, 2019 10:45 am
by Sik
Chilly Willy wrote: Tue Oct 01, 2019 2:56 pmThat will still need the game programmer to ensure that his code that calls SRAM functions aren't above 0x200000.
Or enable SRAM from work RAM and disable before returning? :​P
Chilly Willy wrote: Tue Oct 01, 2019 2:56 pmChanging the SRAM functions to work from sega.s makes them slower, but safe regardless of how they're used.
Wouldn't it be as slow as now? (bonus points if they can be optimized to a word-sized jump, since they'd be below the 32KB mark actually)
Stef wrote: Tue Oct 01, 2019 4:39 pmEven worse looking at OBJ building in makefile it looks like resources is placed before code, not a good idea.
Huh, yeah, that may be a problem. When a game reaches over 2MB it'll be likely because of lots of data (not lots of code), so it's generally safe to assume that code is below the 2MB mark save extreme freak cases… but only if things are organized correctly.

One thing I wonder is about things like look-up tables and such, as those may be written as C source files and hence be lumped with the rest of the code (when they should really go with the data blobs instead).

Re: SRAM read / write

Posted: Wed Oct 02, 2019 2:28 pm
by Chilly Willy
On my 32X stuff, I have the SRAM functions in 68k ram, so I don't have an issue. I also disable ints, and set RV to halt the SH2s if they attempt to read the cart. Having the functions in ram isn't a big deal with 32X stuff if it's more 32X and less MD in the coding. :lol:

If you're making a MD game with some 32X enhancement, it might be more of an issue as you'll probably be using more 68k ram and not have the space for code in ram. But SRAM functions are small, so it's not an issue unless you're really scrimping for every byte of ram.

What I meant by slower but safer code was to have no enable/disable functions, but rather every call to read or write sram enables the sram, does the read/write, then disables the sram every time. You only have two functions (read and write), and it's safer since you can guarantee everything is safe for sram access during access, then turn it all off before returning, but you can see where that would be slower as you'd have more overhead on reading and writing. I do it that way, myself, as I don't see the issue on making reading/writing less than 32K of sram slower. It's not something the user will notice.

The one type of game I could see having more than 2M of code would be something like Wolf3D where column rendering at different scales is unrolled into different routines for best speed, or a game that scales/rotates sprites on the fly rather than using pre-scaled/rotated images in vram/rom, and unrolls a bunch of different scaling and rotating code at different scales and angles. If something is going to use more than 2M of code, it would be something like that.

Re: SRAM read / write

Posted: Mon Apr 27, 2020 4:55 pm
by nemezes
Stef wrote: Mon Sep 30, 2019 8:14 am SGDK uses SEGA SRAM mapper so it assumes base SRAM address to be 0x020000 and just require an offset (that is why the argument is called offset and not address).
So in your case just use offset 0x0000.
Also don't forget to enable SRAM (SRAM_enable()) before trying to read/write from/to it, then when it's done disable it otherwise ROM located at 0x020000 won't be accessible anymore.
still dont get what is this offset.

without an example is hard to understand the documentation of SGDK if you dont know how everything works.

if I want to save an u8 array[3] to SRAM I should use this kind of code:

Code: Select all

u32 offset0;
u32 offset1;
u32 offset2;

offset0 = 0;
offset1 = 1;
offset2 = 2;

array[0] = 1;
array[1] = 2;
array[2] = 3;

SRAM_enable();

SRAM_writeByte(offset0,array[0]);
SRAM_writeByte(offset1,array[1]);
SRAM_writeByte(offset2,array[2]);

SRAM_disable();
then, to read it, I just do it?

Code: Select all

u8 test;

SRAM_enable();

test = SRAM_readByte(offset0);

SRAM_disable();

if (test == 1)
{
	... do stuff;
}
it will be good if it has a really simple sample, just showing how to use the SRAM.

thanks in advance.

Re: SRAM read / write

Posted: Mon Apr 27, 2020 5:56 pm
by cloudstrifer
Example
I don't know if it's the best way to do, but it works.

Code: Select all

//SAVE
SYS_disableInts();
SRAM_enable ();
waitMs(1000);
u32 sRamOffSet = 0x0000;

//Save ID
SRAM_writeByte(sRamOffSet, -99);
//KLog_S1("sRamOffSet:", sRamOffSet);
sRamOffSet ++;

for (u8 screenIndex = 0; screenIndex < 150; screenIndex ++)
{
	for (u8 objectIndex = 0; objectIndex < 20; objectIndex ++)
	{
		SRAM_writeByte(sRamOffSet, arrDBObjects[screenIndex][objectIndex]);
		//KLog_S1("sRamOffSet:", sRamOffSet);
		sRamOffSet ++;
	}
}	

[code]
SRAM_disable ();
SYS_enableInts();

Code: Select all

//LOAD
SYS_disableInts();
SRAM_enableRO ();
u32 sRamOffSet = 0x0000;

//Verify Save ID
s8 saveTest = SRAM_readByte(sRamOffSet);
//KLog_S1("sRamOffSet:", sRamOffSet);
sRamOffSet ++;

if(saveTest != -99)
{
	SRAM_disable();
	SYS_enableInts();
	VDP_drawText("FAILED. ", 6, 24);
	SPR_update();	
	waitMs(1000);	
	return FALSE;
}

for (u8 screenIndex = 0; screenIndex < 150; screenIndex ++)
{
	for (u8 objectIndex = 0; objectIndex < 20; objectIndex ++)
	{
		arrDBObjects[screenIndex][objectIndex] = SRAM_readByte(sRamOffSet);
		//KLog_S1("sRamOffSet:", sRamOffSet);
		sRamOffSet ++;
	}
}	

SRAM_disable ();
SYS_enableInts();