It is currently Thu Dec 14, 2017 10:18 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Tue Aug 01, 2017 2:53 pm 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1515
Today I noticed a little issue with my RAM initialization code (the code that sets all RAM values to 0 at the start).
Since I use a pointer variable to iterate through the RAM addresses, this means the pointer variable itself is eventually also overwritten. A side effect I haven't considered until now.

So, I checked the RAM in fceux: When resetting the game and after fceux itself sets everything to $00 and $FF, I simply fill everything with the character X. Then I check which parts are overwritten with 0 by my code.

For some reason, the whole RAM still gets set to 0. There isn't a chunk of Xs anywhere in the code because of pointer misalignment or something like that, nor does the game run into an infinite loop because of the pointer's high byte value always being set back to 0.
I can set the pointer to any arbitrary address and it always worked.

Why is that the case? Why doesn't the below code produce chunks of RAM that remain untouched?

Code:
   ; The value to
   ; write into RAM.
   LDA #0

   ; The X and Y registers are
   ; used as counters for the
   ; loops and initialized with 0.
   TAX
   TAY

   ; Pointer + 0 always remains 0.
   ; The address is calculated
   ; by Pointer + 1 and by Y.
   STA Pointer + 0
   STA Pointer + 1

@initializeRamLoop:

   ; Set the value of 0
   ; to the current address.
   STA (Pointer), Y

   ; Increment the low byte part
   ; of the address.
   INY

   ; If it is set back to 0,
   ; increment the high byte part
   ; and, of course, the counter.
   ; Otherwise, continue with
   ; the inner loop.
   BNE @initializeRamLoop
   INC Pointer + 1
   INX

   ; The outer loop ends
   ; just before address $0800.
   CPX #$08
   BNE @initializeRamLoop

   ; Set the pointer itself
   ; to 0 as well.
   ; Pointer + 0
   ; is always 0. So, we only
   ; need to set
   ; Pointer + 1.
   ; A is still 0 from above.
   STA Pointer + 1

   ; The RAM has now been
   ; initialized with all zeroes.

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Tue Aug 01, 2017 2:58 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 6520
Location: Seattle
You actually have the answer in your own comments:
DRW wrote:
; Pointer + 0
; is always 0.


The writes go
Y=0, $0=0, $1=0 thus 0→$0
Y=1, $0=0, $1=0 thus 0→$1

And after that, nothing writes to the pointer memory again.

Since the pointer has to be stored in zero page, and you're initializing RAM with 0, it won't miss anything.


Top
 Profile  
 
PostPosted: Tue Aug 01, 2017 3:09 pm 
Offline
User avatar

Joined: Sun May 27, 2012 8:43 pm
Posts: 1311
Why such a complicated RAM-clearing routine?

Adapted from the wiki:

Code:
   ldx #0
   txa $0

@clrmem_top:
   sta $000, x
   sta $100, x
   ; Reserving $200 for OAM display list, sta $200, x if you want
   sta $300, x
   sta $400, x
   sta $500, x
   sta $600, x
   sta $700, x
   inx
   bne @clrmem_top


Top
 Profile  
 
PostPosted: Tue Aug 01, 2017 3:24 pm 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1515
@lidnariq:

Oh, that's right.

Let me reiterate:

Pointer + 0 is always 0, so that one doesn't matter since Y is the actual low byte value.

The value in Pointer + 1 will go from $00 to $07 during the algorithm.

But since the pointer is in zeropage, its own location in memory will always be guaranteed to be $00xx.
And this means Pointer + 1 is only overwritten with 0 when when Pointer + 1 has the value 0 anyway.

Or to say it like this:
The loop cannot suddenly jump from $1234 to $0034 due to Pointer + 1 being overwritten with a 0 because the pointer will never be in a memory location $12xx, only $00xx.

Is that correct?


mikejmoffitt wrote:
Why such a complicated RAM-clearing routine?

Two reasons:

1. I find a loop more elegant than manually writing 0, 100, 300, 400, 500, 600, 700.

2. There's an additional piece of code in my initialization routine that doesn't have anything to do with my original question, so I left it out, but it's required if you want to keep, for example, the highscore when pressing reset:
Code:
   ; There is a certain RAM area that
   ; shall not be initialized with zeroes
   ; because this area shall be persistent
   ; when the Reset button is pressed.
   ; We check if we reached that RAM area.

   ; The high byte of the address is checked.
   LDA Pointer + 1
   CMP #>__NO_RESET_LOAD__
   BNE @noNoResetSkip

   ; The low byte is checked.
   CPY #<__NO_RESET_LOAD__
   BNE @noNoResetSkip

   ; If we reached the
   ; reset-persistent area,
   ; we change the address to
   ; the first value after the area.
   ; This way, the reset-persistent
   ; area doesn't get changed.
   LDY #<(__NO_RESET_LOAD__ + __NO_RESET_SIZE__)
   LDA #>(__NO_RESET_LOAD__ + __NO_RESET_SIZE__)
   STA Pointer + 1

@noNoResetSkip:

   ; Set the value of 0
   ; to the current address.
   LDA #0
   STA (Pointer), Y

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Tue Aug 01, 2017 4:08 pm 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1515
I just noticed: The "reset-persistent area" code is missing the adjustment of X, am I right?

In the usual code, these values get updated in pairs:
Code:
   INC CommonPointer1AsVar + 1
   INX


So, this needs to be done here as well, right?

Code:
LDA #>(__NO_RESET_LOAD__ + __NO_RESET_SIZE__)
   STA CommonPointer1AsVar + 1

New code after the STA: TAX, right? (Or I guess I could completely do without the X.)

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Tue Aug 01, 2017 6:09 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1868
Location: DIGDUG
I have no idea what your last question is asking.

How about this...the memory persistent area is zeropage 00-03

Start your loop with LDY #4.

EDIT-Alternatively, you could use $7fc-7ff as the memory persistent area, and reverse your loop, starting at $7fb, going down to $000

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


Last edited by dougeff on Wed Aug 02, 2017 4:37 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Wed Aug 02, 2017 12:23 am 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1515
The last question was just a discovery of mine: X and Pointer + 1 always need to have the same value. Because X counts the loop and Pointer accesses the RAM. And I just discovered that I missed to set X accordingly in one situation.
And I wanted to check if other people confirm this.

It didn't make a practical difference in the past. Because the reset-persistent area is only a few bytes big, so I was lucky that the high byte didn't change between before and after the reset-persistent area. But it could happen, so I'll have to fix it.

Setting this area to address $0000 wouldn't be good. This means I waste a bunch of my precious zeropage variables.

$07FF might work though. I'll have to declare the reset-persistent segment at the end of RAM and then I move the software stack some bytes to the front.
Since I use a CFG file for this, accidental overlapping shouldn't be possible.
Then I'll simply end the loop as soon as the reset-persistent area is reached.

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Wed Aug 02, 2017 12:35 am 
Offline

Joined: Tue Feb 07, 2017 2:03 am
Posts: 262
The code will take a while, a long while to run, all interrupts/nmis are disabled while it runs ?


Top
 Profile  
 
PostPosted: Wed Aug 02, 2017 12:38 am 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1515
Yes.

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Wed Aug 02, 2017 12:41 am 
Offline

Joined: Tue Feb 07, 2017 2:03 am
Posts: 262
Yes in this case you will need X and Pointer+1 to be equal otherwise you will always write $0800 bytes, and since the NES repeats RAM again after $0800 you end up writing to $0000 again.

so for example $0200 + $0700 = $0900 which is the same as $0100 which will explain why you don't see any X's in the case where you try to only clear part of it.


Top
 Profile  
 
PostPosted: Wed Aug 02, 2017 1:24 am 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1515
I was lucky that it works in my game "City Trouble" simply because the reset-persitent area is only 13 bytes big and it isn't positioned in a location where the low byte overflows and the high byte gets incremented. So, Pointer + 1 remains the same before and after that check anyway.
But programming-wise it's of course a bug and I'll correct it for the next game.

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Wed Aug 02, 2017 7:37 am 
Offline
User avatar

Joined: Fri Nov 19, 2004 7:35 pm
Posts: 3968
Using a pointer variable means that everything was cleared to zeroes, except for the pointer itself.

_________________
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!


Top
 Profile  
 
PostPosted: Wed Aug 02, 2017 7:41 am 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1515
Huh? What are you referring to in this specific context?

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Wed Aug 02, 2017 7:50 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19343
Location: NE Indiana, USA (NTSC)
Because read-modify-write operations (ASL, LSR, ROL, ROR, DEC, INC) still set the NZ flags, your RAM clearing routine can be golfed by using the pointer's high byte as the loop counter:
Code:
   LDA #0  ; The value to write to RAM, and the start of a page
   LDY #$07  ; The first page to clear, in reverse order
   STA Pointer+0  ; Write the pointer
   STY Pointer+1

   TAY  ; Y is also the loop counter
   ; If you have reset-persistent data at, say, $700-$70F,
   ; do LDY #$10 instead

@initializeRamLoop:

   STA (Pointer), Y  ; Write 0 to to the current address.
   INY  ; Move to the next address in the page
   BNE @initializeRamLoop  ; If page not done, continue
   
   DEC Pointer+1  ; Go to previous page
   BPL @initializeRamLoop  ; If not wrapped around into ROM, continue
   
   ; Pointer+0 is always 0, Pointer+1 is now $FF
   STA Pointer+1  ; So write 0 there


Top
 Profile  
 
PostPosted: Wed Aug 02, 2017 10:47 am 
Offline
Formerly ~J-@D!~
User avatar

Joined: Sun Mar 12, 2006 12:36 am
Posts: 445
Location: Rive nord de Montréal
mikejmoffitt wrote:
Why such a complicated RAM-clearing routine?

Adapted from the wiki:

Code:
   ldx #0
   txa $0

@clrmem_top:
   sta $000, x
   sta $100, x
   ; Reserving $200 for OAM display list, sta $200, x if you want
   sta $300, x
   sta $400, x
   sta $500, x
   sta $600, x
   sta $700, x
   inx
   bne @clrmem_top

His version is not very complicated, in fact it's pretty much standard, and note the fact that while your version is faster, his version is more compact: your algorithm takes 27 bytes, his takes 22 bytes, and could take only 20 bytes if we didn't care of the pointer value at the end. For some code initializing things, ran once at the beginning, speed is not important, but size is.


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 6 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