It is currently Tue Nov 21, 2017 10:43 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 52 posts ]  Go to page Previous  1, 2, 3, 4  Next
Author Message
PostPosted: Wed Jan 18, 2017 10:50 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5824
Location: Canada
tokumaru wrote:
Oh well, to each his own. FWIW, I never told him not to do it, I just said I don't do it.

I feel strongly about it. I could probably write quite a long essay about this topic, but I'm not sure this is the appropriate place, and it would take a lot of time to go into detail.

There's a lot of reasons but the most important one is that you should guarantee as much as you possibly can about the execution environment so that when you test your program it doesn't behave differently than when it's deployed to some other computer. If you intentionally leave portions of RAM up to chance, this is an exponentially large number of cases that you simply can't test.


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 11:37 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10117
Location: Rio de Janeiro - Brazil
rainwarrior wrote:
I feel strongly about it.

And I feel strongly about the opposite. Hence the "to each his own" part of my answer.


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 12:25 pm 
Offline

Joined: Wed Nov 30, 2016 4:45 pm
Posts: 93
Location: Southern California
The beginning of the program should initialize most variables [1] whether that's the reset routine or a program that is started after the reset routine runs. Initializing does not mean zeroing them all. Some will need to be flags that start out set, not clear. Some variables need to contain addresses, and having them point to 0000 will bring a malfunction just like a random value would. Some variables will need reasonable default values to use if the user does not enter his own.

I have never seen any reason to clear RAM in the RESET routine. The biggest job of the RESET routine will probably be to set up the I/O. But yes, there may be a list of flags that need to be cleared. When I was using the 2500AD assembler, one feature I liked was that it allowed macros to have varying numbers of input parameters. It let you say in essence, "If there's a fourth parameter, do this; if there's a fifth one, do that with it <etc.>" So you could clear a list of flags with a single line, like
Code:
        CLR_FLAG    <flag1>, <flag2>, <flag3>, <flag4>, <flag5>, <etc.>

filling in each <flagX> with the name, and you could do up to the limit of the macro you wrote for it. The above macro invocation would assemble,

Code:
        LDA  #0
        STA  <flag1>
        STA  <flag2>
        STA  <flag3>
        STA  <flag4>
        STA  <flag5>
        STA  <flag6>

for the 6502, or, if you have a 65c02 (ie, CMOS), you could omit the LDA #0 and make the other lines to be STZ <flagX>.



[1] I say most variables, not all, because there may be a few variables like a time reference that gets incremented every so many microseconds or milliseconds, where what matters is not the initial value but rather the amount of change from one reading to the next. IOW, there can actually be variables that don't need to be initialized at all.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 12:54 pm 
Offline

Joined: Sun Mar 27, 2011 10:49 am
Posts: 206
Location: NYC
It's not an NES emulator, but BGB has an option that traps and falls into the debugger on uninitialized RAM access. This is a best of both worlds situation - you don't need to write any code to initialize memory, and as long as you don't manually clear RAM, any unintended uninitialized accesses will be caught.


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 1:16 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10117
Location: Rio de Janeiro - Brazil
Garth wrote:
one feature I liked was that it allowed macros to have varying numbers of input parameters. It let you say in essence, "If there's a fourth parameter, do this; if there's a fifth one, do that with it <etc.>" So you could clear a list of flags with a single line, like
Code:
        CLR_FLAG    <flag1>, <flag2>, <flag3>, <flag4>, <flag5>, <etc.>

You can create macros like this in ca65 too, I do it all the time.

adam_smasher wrote:
It's not an NES emulator, but BGB has an option that traps and falls into the debugger on uninitialized RAM access.

Now that's a good reason NOT to clear all the RAM on reset.


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 1:56 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1825
Location: DIGDUG
Just my 2 ¢.

Random vs initialize to zero...underscores the necessity to test on hardware. Many of the early homebrew games/demos (I've heard) don't work on hardware, because they assumed that all RAM addresses were zero on start-up.

Zeroing out the RAM in your init routine fixes this discrepancy between hardware and emulator, making surprise bugs less likely. My opinion. Zero = fewer bugs.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 2:17 pm 
Offline

Joined: Wed Nov 30, 2016 4:45 pm
Posts: 93
Location: Southern California
tokumaru wrote:
You can create macros like this in ca65 too. I do it all the time.

I'm not familiar with ca65. Does it allow making macros that don't require a given number of input parameters? (just to confirm.) Most assemblers require that every macro invocation use the exact number of parameters specified in the macro definition. You cannot, for example, invoke a particular macro using three input parameters one time, five the next, and six the time after that.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 4:20 pm 
Offline

Joined: Tue May 28, 2013 5:49 am
Posts: 873
Location: Sweden
I haven't used ca65 enough to answer your question but it's quite versatile in the macro department. Here's the manual.

Garth wrote:
I have never seen any reason to clear RAM in the RESET routine. The biggest job of the RESET routine will probably be to set up the I/O. But yes, there may be a list of flags that need to be cleared. When I was using the 2500AD assembler, one feature I liked was that it allowed macros to have varying numbers of input parameters. It let you say in essence, "If there's a fourth parameter, do this; if there's a fifth one, do that with it <etc.>" So you could clear a list of flags with a single line, like
Code:
        CLR_FLAG    <flag1>, <flag2>, <flag3>, <flag4>, <flag5>, <etc.>

filling in each <flagX> with the name, and you could do up to the limit of the macro you wrote for it. The above macro invocation would assemble,

Code:
        LDA  #0
        STA  <flag1>
        STA  <flag2>
        STA  <flag3>
        STA  <flag4>
        STA  <flag5>
        STA  <flag6>


Once that list of flags grows enough, you loose the space you save by not clearing the RAM in a loop. Speed is probably not an issue since this code only runs once at boot and reset, and you can fit it in between the two mandatory vblank waits when you are doing nothing anyway. The wiki has a good init routine.


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 5:05 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19238
Location: NE Indiana, USA (NTSC)
adam_smasher wrote:
It's not an NES emulator, but BGB has an option that traps and falls into the debugger on uninitialized RAM access.

But it's different for Game Boy because there's no Reset button. A lot of NES and Super NES games intentionally read a small area of uninitialized memory at power-up to distinguish a power-on from a press of Reset, allowing some data to survive a Reset press.

I'll take the variadic macro thing to a new topic.


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 5:27 pm 
Offline

Joined: Tue May 28, 2013 5:49 am
Posts: 873
Location: Sweden
This is how I do this, using RAM page 7 as the reset-persistent area as suggested in the wiki.
I add a signature string "INIT" in the first four registers of page 7 to indicate that it has already been cleared:
Code:
init_reset_persistent_area:
  lda $0700+0
  cmp #"I"
  bne @clear
  lda $0700+1
  cmp #"N"
  bne @clear
  lda $0700+2
  cmp #"I"
  bne @clear
  lda $0700+3
  cmp #"T"              ;check for signature string "INIT"
  beq @exit             ;if signature found, do not clear
@clear:
  lda #$00
@loop:
  sta $0700,x           ;if signature not found, clear reset-persistent area
  inx
  bne @loop

  lda #"I"
  sta $0700+0
  sta $0700+2
  lda #"N"
  sta $0700+1
  lda #"T"              ;write "INIT" signature to mark as initialized so that
  sta $0700+3           ;it is not cleared on next reset
@exit:

No idea if this is the common thing to do, or if there is a better way. But it seems to work.


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 6:44 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5824
Location: Canada
tokumaru wrote:
rainwarrior wrote:
I feel strongly about it.

And I feel strongly about the opposite. Hence the "to each his own" part of my answer.

What I mean is I feel strongly enough about it that I disagree with "to each his own". I think leaving unallocated parts of RAM up to chance is bad form and feel a necessity to advise against it. (Summarizing again: you should not intentionally create conditions which aren't reliable from system to system.)

adam_smasher wrote:
It's not an NES emulator, but BGB has an option that traps and falls into the debugger on uninitialized RAM access. This is a best of both worlds situation - you don't need to write any code to initialize memory, and as long as you don't manually clear RAM, any unintended uninitialized accesses will be caught.

If you initialize RAM to 0 there isn't any uninitialized memory; all the uninitialized access just don't exist. What problem are you solving here?

I think emulator tools for finding that stuff or randomizing RAM on power up etc. are great, but they're for diagnosing/analyzing pre-existing software that has made a mistake. They're not for testing a problem you could easily solve in 10 lines of code at the start of your program.

What's so great about not "having" to manually clear RAM? How many of your variable initializations are 0 anyway? (Is it not your most common desired init value?) How much extra code does it require to manually put 0 in each of them instead of being able to rely on them all starting at 0?

If you're trying to catch use of unallocated space by accident, the most effective tool I can think of to diagnose this is to simply put a breakpoint on those regions of memory you are not using. That's a billion billion times more effective than leaving them up to chance, randomizing them every time, and hoping you get lucky enough to cause a problem randomly.


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 8:02 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10117
Location: Rio de Janeiro - Brazil
rainwarrior wrote:
What I mean is I feel strongly enough about it that I disagree with "to each his own".

You can't accept that someone else might prefer a different development methodology than your own? That's arrogant. It's simple: you feel like you create better programs if you clear all the RAM, I feel like I create better programs if I randomize the RAM. We're different programmers, and it's not at all surprising that we like to track bugs using different techniques. Bugs can hide no matter what approach towards memory initialization you use though, so there's no way to be 100% safe.

Quote:
If you're trying to catch use of unallocated space by accident, the most effective tool I can think of to diagnose this is to simply put a breakpoint on those regions of memory you are not using.

Access to unused memory areas is one bug, failing to (re)initialize memory you do use is another. Also, the more memory you have, the less practical it is to manually keep track of what's used and what's not and to maintain a huge list of breakpoints.

I'm gonna pull a koitsu now and inform you I don't plan on discussing this any further (it barely has anything to do with the topic, even). If you can't accept that I work better without the false sense of security provided by a blanked out RAM chip, that's your problem, not mine. I'm not even advocating one way or the other really, since like I said, each programmer has a different process and should do what makes them feel more secure.

Please note that I'm not offended/pissed off or anything like that, I just think this discussion is going nowhere.


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 8:35 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5824
Location: Canada
tokumaru wrote:
You can't accept that someone else might prefer a different development methodology than your own? That's arrogant.

If I think someone is giving bad advice, I feel necessary to attempt to correct it, rather than say "well both of these approaches are equally valid". I'm sorry that you might feel this is arrogant, but this is precisely what I meant by "feel strongly".

Obviously you can do whatever you like in your own project, but WheelInventor was asking for advice and I think it's very important that we lay out the argument for them. I'm addressing you, but it's a public debate.

tokumaru wrote:
...failing to (re)initialize memory you do use is another (type of bug)...

I don't understand how this is related to randomizing unallocated areas of RAM. I'm not saying this rhetorically, I'm trying to imagine the process where you have made a mistake in your code and having randomized RAM helps you, and I'm having trouble thinking of the steps where it would make a useful difference. Could you illustrate an example?

tokumaru wrote:
Also, the more memory you have, the less practical it is to manually keep track of what's used and what's not and to maintain a huge list of breakpoints.

If your static memory allocation is contiguous, it's not a "huge list", it's just two ranged breakpoints, one for ZP and one for the rest of RAM. Do you have heavily fragmented static allocations?

tokumaru wrote:
false sense of security

What's do you mean by false? If you initialize RAM to 0 those values will be 0. This is completely reliable.

tokumaru wrote:
Please note that I'm not offended/pissed off or anything like that, I just think this discussion is going nowhere.

I'm not angry either, but I definitely felt it necessary to state my opinion about it in the context of advice to a newcomer. I also would like to continue this discussion in the hope that you reveal to me some idea or perspective that I haven't considered, but if you've got nothing more to add then I probably don't either.


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 9:44 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10117
Location: Rio de Janeiro - Brazil
Since you're addressing specific points, let's see if anything productive can come out of this. I just don't want to be part of a pointless "my way is better" discussion.

rainwarrior wrote:
tokumaru wrote:
...failing to (re)initialize memory you do use is another (type of bug)...

I don't understand how this is related to randomizing unallocated areas of RAM. I'm not saying this rhetorically, I'm trying to imagine the process where you have made a mistake in your code and having randomized RAM helps you, and I'm having trouble thinking of the steps where it would make a useful difference. Could you illustrate an example?

Say I just finished coding my title screen state and I want to test it. If I do a few consecutive tests randomizing the RAM using a different seed each time, I have better chances of noticing something I might have forgotten to initialize for that code. Is this technique perfect? No, I might try 10 different seeds and get "lucky" enough for the logic to work (or appear to work) with 10 different random values in a variable I forgot to initialize, but the chances are still greater than if I always tested using the same value ($00).

Quote:
If your static memory allocation is contiguous, it's not a "huge list", it's just two ranged breakpoints, one for ZP and one for the rest of RAM. Do you have heavily fragmented static allocations?

The NES has very little RAM, so not all projects will allocate RAM linearly and just leave some of it unused at the end. I have a system where memory is shared between modules that don't run concurrently (i.e. the bonus stages and the main game use much of the same memory for completely different purposes), so while an address might be unused from the point of view of one module, it's used from the point of view of another. The memory map looks different as the program runs. But even if you do use RAM in a mostly linear way, you might still have holes if you feel the need to align large arrays so they don't cross page boundaries, for timing reasons or whatever.

Quote:
What's do you mean by false? If you initialize RAM to 0 those values will be 0. This is completely reliable.

The false sense of security comes from the fact that it's reliable the very first time a piece of code runs, but may not be on subsequent times the same code runs (because you corrupted the variables on the first run). Say I coded the tile screen for my game, booted up the ROM and it works. Yey! Let's move on to something else. Then, weeks later, you're still feeling very confident about your title screen, after all you've been using it for weeks and it never failed. Then you implement the game over functionality that sends you back to the title screen and *BAM*, it's glitched. Your first reaction will probably be to think you screwed up something you coded more recently, and it might take you longer to realize that the bug actually lies on code written weeks ago, code you might not even be particularly familiar with anymore since it's been a while since you coded it.

However, if you do the "randomize RAM a few times" approach while still testing the title screen for the first time, you increase your chances of caching any initialization bugs it might have while all the logic of that part is still fresh in your head, meaning it might even be easier for you to deduce what variable could be causing the undesired behavior you're experiencing. When you finally move on to code something else, the robustness of the title screen will be more real, since you put it to the test under different memory configurations and it survived all of them. Again, not 100% failproof, but better than testing under a single configuration.

Quote:
I definitely felt it necessary to state my opinion about it in the context of advice to a newcomer.

Believe me, I'm all against giving newcomers bad advice. Every time I see code with that dreadful STA $2003 ; set the low byte (00) of the RAM address comment that comes straight from the Nerdy Nights tutorials I cringe. On the topic of memory initialization in particular, I just happen to think things aren't as black-or-white as you seem to think. I do understand that zeroed out memory provides a consistent environment for your programs all across the board, but games aren't short-lived linear programs that do their job and end, they're very dynamic and do lots of state changes, so what good is it to provide a consistent environment in the beginning, if the same modules are going to run again after the RAM will have already been trashed since power up?

I'm not opposed to clearing the RAM as an extra safety measure on your release build, but during development, it may give you the false sense of security that your modules are robust when they're actually not, if they neglect to initialize something that just happens to work if it starts out as 0, but fails with other values and you won't know it until you happen to re-run that module after that variable was left in a "bad" state.


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 10:35 pm 
Offline

Joined: Wed Nov 30, 2016 4:45 pm
Posts: 93
Location: Southern California
Regarding the macro allowing varying numbers of input parameters to set or clear flags:
Pokun wrote:
Once that list of flags grows enough, you loose the space you save by not clearing the RAM in a loop.

That's if it's practical to keep them together, for loop indexing. You might want to keep them separate though, with the parts of the program that use them. The macro could still be made to see if there are enough to justify a loop, and see if they're at contiguous addresses, and if so, assemble the loop, rather than straightlining them.

Quote:
I haven't used ca65 enough to answer your question but it's quite versatile in the macro department. Here's the manual.

It looks like the assembler is pretty rich in features. If I understand it right, you don't have to use all the allowed input parameters, but if you don't, you have to leave space for them anyway, separated by commas; so using a CLR_FLAG macro to do three flags when it allows up to eight, you would have to do:
Code:
        CLR_FLAG   <flag1>, <flag2>, <flag3>,  ,  ,  ,  ,

_________________
http://WilsonMinesCo.com/ lots of 6502 resources


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 52 posts ]  Go to page Previous  1, 2, 3, 4  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: Google [Bot] and 9 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group