This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

RAM initialization

RAM initialization
by on (#155234)
When the NES is started (with the Power button, not Reset), can I be sure that all RAM values are set to 0 or can they have an arbitrary value?

If they are not guaranteed to be 0, what happens when I declare a variable in Assembly and don't specify a start value?
MyVariable: res 1
Is it set to 0 by the compiler or does it get the value that is currently in memory at this address?
(As far as I know, C initializes all global variables with 0, but leaves local variables uninitialized unless you explicitly do it yourself. How does Assembly handle it?)


Also, another problem:
If the RAM values do not get set to 0 at startup, how do I keep a highscore value when the Reset button is pressed?

There's the same interrupt for Reset and for Power on.
If the RAM isn't guaranteed to be all zeroes when I switch on the NES (or any clone system), how can I make sure that my score gets set to 0 at startup, but not at a reset.

Not overwriting the score at a reset is easy: Just exclude the corresponding memory when you initialize the RAM with 0.
But how do I do set it to 0 when the NES is switched on?
Re: RAM initialization
by on (#155241)
DRW wrote:
When the NES is started (with the Power button, not Reset), can I be sure that all RAM values are set to 0 or can they have an arbitrary value?
They can have random values, although in practice they are often mostly $FF.

Quote:
If they are not guaranteed to be 0, what happens when I declare a variable in Assembly and don't specify a start value?
Is it set to 0 by the compiler or does it get the value that is currently in memory at this address?
(As far as I know, C initializes all global variables with 0, but leaves local variables uninitialized unless you explicitly do it yourself. How does Assembly handle it?)
Assembly does no initialization that you didn't put there.

For cc65/ca65, the difference between the BSS segment and the DATA segment is that values in the DATA segment are given a simple structure that you can use as part of your crt0 equivalent to initialize them.

Quote:
If the RAM values do not get set to 0 at startup, how do I keep a highscore value when the Reset button is pressed?
[...]
If the RAM isn't guaranteed to be all zeroes when I switch on the NES (or any clone system), how can I make sure that my score gets set to 0 at startup, but not at a reset.
Use some kind of signature to indicate that the memory contents are valid. Choices include simple flags (preferably at least one byte, more is better), checksums, &c.
Re: RAM initialization
by on (#155244)
lidnariq wrote:
Assembly does no initialization that you didn't put there.

But when I write:
MyVariable: .res 1,$00
then it is intialized, right?

lidnariq wrote:
Use some kind of signature to indicate that the memory contents are valid. Choices include simple flags (preferably at least one byte, more is better), checksums, &c.

Isn't there a more simple solution?

If the RAM was initialized with 0 when the NES starts, then I could have just avoided setting those values to 0. Therefore, in the beginning, the score would have had the value 0 and later it would have had whatever value was set.

But when the values can be arbitrary at startup, I cannot know whether this value is an arbitrary value at power on or the current score at reset.

I think checksums are a bit overkill. Was this actually done in real video games?

How about this:
When I declare a variable and set a default value right there (like in the code above, setting the variable to 0), then the score should have the value 0 at startup.
And when I press Reset, this variable doesn't automatically get set back to 0 or an arbitrary value, but keeps the current value until I overwrite it.
Is that correct?

So, when I'm right with my assumption, the only thing that needs to be done is declare a score variable and immediately initialize it with 0 right in the declaration. This way it gets set to 0 at startup, but doesn't lose its value at reset.
Of course I'm aware that when I iterate through the whole RAM to set everything to 0 in the beginning, I have to omit the memory location where the score is stored.


Apropos RAM initialization:
Is setting the RAM to 0 in the Reset interrupt necessary?

What I mean is: If I just leave the RAM as it is, with arbitrary values instead of all zeroes, can there be any problems apart from pure stupidity of the programmer?

I know that when I forget to initialize one of my variables, it may have an arbitrary value if I haven't initialized the RAM with zeroes before. But in this case, I have a bug in my program anyway.
What I'd like to know: Can there be any problems that aren't my own mistakes when I don't initialize the RAM?
Does anything in an NES program rely on the fact that the RAM is full of zeroes at startup and after a reset?
Re: RAM initialization
by on (#155245)
DRW wrote:
But when I write:
MyVariable: .res 1,$00
then it is intialized, right?

Nope, the assembler has no means to initialize the variable (it would need code to do so, and emitting code that the programmer didn't ask for is not nice). You have to clear the memory segment where that variable is defined if you want to ensure that it's 0. You can of course clear entire RAM as well, which takes care of any segments you have within the RAM.

Note that "0" is a special value in ca65 -- it's the only value that is allowed in BSS segments without warnings. Example:
Code:
.segment "BSS" ; assuming this segment has the type "bss" in linker config
myVar1: .res 1 ; OK
myVar2: .res 1, $00 ; OK
myVar3: .res 1, $23 ; gives a warning about initialized data in a BSS segment
myVar4: .byte 0 ; OK
myVar5: .byte $55 ; gives a warning

But this doesn't mean that the assembler takes care of initialization. It's only assuming that the programmer will initialize all BSS segments to zero.

As for reset detection, what's usually done is that you write some known identifier in some known location of memory. For example, you could write "DRW" at $7FD..$7FF. If you find the string in there in your startup code, you know this was a soft reset. If you don't, it was a power-on (or you got astronomically unlucky). It's still possible that something could go wrong (what if the user powers off the machine, and the score gets trampled but the identifier stays intact, then user powers on the machine again?), but I think many games did this nevertheless without any checksums.
Re: RAM initialization
by on (#155247)
Thanks for the clarification. I wasn't aware that initialization with any non-zero value even gives a warning.
In this case, I will use the version with the signature in RAM to check the score.


There's one more thing that's related to the C language that I need to clarify:

Alright, so, let me get this straight:

Let's say I don't initialize the RAM at the beginning. Or let's say that, just for fun, I initialize the whole RAM with the value 99. In the reset interrupt, the whole RAM gets filled with the value 99.

Now I declare a global variable in C, without an initialization value. That means, it is a normal BSS variable, not one from the DATA section:
Code:
unsigned char test;


During compilation this gets transformed to something like:
Code:
.segment "BSS"
    _test: .res 1,$00


Now, did I understand this correctly? The value $00 after the initialization is not actually set to the variable. If I check this global variable without explicitly giving it a value first, it would now return 99 because I intialized the whole RAM with 99?
(Even though, as far as I know, the C language standard specifies that uninitialized global variables (unlike local variables) are always guaranteed to be initialized with 0.)
Is that correct?
Re: RAM initialization
by on (#155248)
DRW wrote:
Is that correct?

Yes, the compiler/assembler has no way of knowing that you decided to initialize the RAM to 99.
Re: RAM initialization
by on (#155250)
You could add code to initialize the value, at the end of the startup code.

_test = 0x99;

Or in assembly...

LDA #$99
STA _test
Re: RAM initialization
by on (#155254)
DRW wrote:
When the NES is started (with the Power button, not Reset), can I be sure that all RAM values are set to 0 or can they have an arbitrary value?

If they are not guaranteed to be 0, what happens when I declare a variable in Assembly and don't specify a start value?
MyVariable: res 1

The value at power-on is unspecified. In practice, it's more likely to be non-zero than zero on hardware.

Quote:
Is it set to 0 by the compiler or does it get the value that is currently in memory at this address?

The startup code (commonly called crt0.s) is expected to zero-fill the BSS segment.

Quote:
If the RAM values do not get set to 0 at startup, how do I keep a highscore value when the Reset button is pressed?

The values in RAM are unchanged. Write a two- or three-byte unique value to RAM near your high score table. But before writing it, compare it to the values already there.

Quote:
But when I write:
MyVariable: .res 1,$00
then it is intialized, right?

That depends on in which segment you write that and what your program's startup code is. None of RAM is initialized when your program's reset vector is used for the first time. Put it in RODATA, and it'll be initialized, but you won't be able to change it at runtime. Put it in the DATA segment and a typical C startup code will copy it from ROM to RAM before jumping to main. Put it in the BSS segment and it will not be so copied.

Quote:
Is setting the RAM to 0 in the Reset interrupt necessary?

If your program contains C functions that access statically allocated variables without an explicit initializer, then yes, it is necessary to set RAM to 0 in order to give those functions an execution environment that conforms to the C standard.
Re: RAM initialization
by on (#155257)
For comparison, C code on the GBA works differently.
There is a .data section for pre-initialized global variables, and a .bss section for zero-filled global variables.
There is boot code (crt0.s) responsible for copying the .data section from ROM into RAM, then zero-filling the .bss section. This initializes all the global variables. Then it calls the main() function.

On the NES using assembly, you don't have a linker that makes values for a .data section that gets copied from ROM into RAM, and there's no pre-defined boot code that will zero-fill the .bss section.

Some NES games will put a signature into RAM to see if it needs to be cleared or not. For example, Super Mario Bros keeps your high score and A+Start continue level between resets.
Re: RAM initialization
by on (#155261)
Dwedit wrote:
For comparison, C code on the GBA works differently.
There is a .data section for pre-initialized global variables, and a .bss section for zero-filled global variables.
There is boot code (crt0.s) responsible for copying the .data section from ROM into RAM, then zero-filling the .bss section. This initializes all the global variables. Then it calls the main() function.

I don't see how that differs from cc65's approach.

Quote:
On the NES using assembly, you don't have a linker that makes values for a .data section that gets copied from ROM into RAM, and there's no pre-defined boot code that will zero-fill the .bss section.

Unless you're using cc65, whose linker is ld65.
Re: RAM initialization
by on (#155262)
Dwedit wrote:
For comparison, C code on the GBA works differently.

How is this different at all than CC65's C code on the NES?
Re: RAM initialization
by on (#155301)
Alright, thank you all for answering my questions.
Re: RAM initialization
by on (#158582)
I'll add that it's a good idea to explicitly fill all RAM (except for anywhere you're storing persistent data) with a certain value at the start of your program. A lot of people fill it with zero, but I don't like using zero. Initializing RAM to zero is more likely to hide mistakes (caused by failure to initialize variables) than setting it to something like $ff, and $ff is also useful for filling OAM.