Skip navigation
NintendoAge
Welcome, Guest! Please Login or Join
Loading...

Nerdy Nights week 8 16 bit math, pointers, nested loops

May 15, 2010 at 2:39:33 PM
bunnyboy (81)
avatar
(Funktastic B) < Master Higgins >
Posts: 7704 - Joined: 02/28/2007
California
Profile
This Week: The NES is an 8 bit machine, but sometimes you need more! Learn to handle 16+ bit numbers, and use them for bigger loops.

16 Bit Math
Doing 16 bit addition and subtraction is fairly simple because of the carry flag that we had previously been clearing. First the normal add is done using the clc/adc pair. This add is for the lower 8 bits of the 16 bit number. For the upper 8 bits the adc instruction is used again, but without the clc. You want to keep the carry from the first add, in case it overflowed. To only add in the carry the second adc value is just 0.

Here are some examples in decimal. One digit column is added at a time. The carry (1) is added to the next column as needed.
    0 3
+   0 4
    0 7  (no carry needed, top digit = 0)

    0 4
+   0 8
    1 2  (carry only, top digit = 1)

    2 2
+   1 9
    4 1  (carry plus 2 plus 1, top digit = 4)

And the code to do it on the NES, adding 1 to a 16 bit number:
  LDA lowbyte      ; load low 8 bits of 16 bit value
  CLC              ; clear carry
  ADC #$01         ; add 1
  STA lowbyte      ; done with low bits, save back
  LDA highbyte     ; load upper 8 bits
  ADC #$00         ; add 0 and carry from previous add
  STA highbyte     ; save back

The same process of adding 0 without clearing the carry can be continued to do 24 bit, 32 bit, or higher numbers. It is also the same process to do 16 bit subtraction:
  LDA lowbyte      ; load low 8 bits of 16 bit value
  SEC              ; set carry
  SBC #$01         ; subtract 1
  STA lowbyte      ; done with low bits, save back
  LDA highbyte     ; load upper 8 bits
  SBC #$00         ; subtract 0 and carry from previous sub
  STA highbyte     ; save back


Pointers and Indirect Indexed Mode
Previously when loading background tiles the x register was used as an 8 bit offset. Now that we can handle 16 bit numbers a different addressing mode can be used. The 16 bit address is saved into two 8 bit variables, which are then used as a "pointer" which points to the background data we want. The LDA instruction then uses the "Indirect Indexed" addressing mode. This takes the 16 bit variable inside the brackets and uses it as an address. For the address to be correct, the low byte must be first and the high byte must come immediately after. Then the value in the Y register is added to the address. This forms the final address to load from. Both variables must also be in the first 256 bytes of RAM, called "Zero Page", and the X register cannot be used with this addressing mode.
  .rsset $0000       ; put pointers in zero page
pointerLo  .rs 1   ; pointer variables are declared in RAM
pointerHi  .rs 1   ; low byte first, high byte immediately after
  LDA #$D0
  STA pointerHi
  LDA #$12
  STA pointerLo       ; pointer now says $D012
  LDY #$00            ; no offset from Y
  LDA [pointerLo], y  ; load data from the address pointed to by the 16 bit pointer variable plus the value in the Y register

That last line is the same as
LDA $D012, y
Because we kept Y = 0, that is the same as
LDA $D012

Copy Loops
Now using your 16 bit math the pointer address can be incremented. Instead of being limited to 256 background tiles like when using the x offset, the whole background can be copied in one loop. First the address of the background data is put into the pointer variable. The high and low bytes of the address are each copied individually. Then the number of tiles to copy is put into the loop counter, which will count down to 0. Each time through the loop one byte will be copied, the 16 bit pointer address will be incremented, and the 16 bit loop counter will be decremented. The Y offset is always kept at 0, because the pointer always points to the correct byte. When the loop counter reaches 0 everything is done.
  LDA #LOW(background)
  STA pointerLo       ; put the low byte of the address of background into pointer
  LDA #HIGH(background)
  STA pointerHi       ; put the high byte of the address into pointer
  LDA #$00
  sta counterLo       ; put the loop counter into 16 bit variable
  LDA #$04
  sta counterHi       ; count = $0400 = 1KB, the whole screen at once including attributes
  
  LDY #$00            ; put y to 0 and don't change it
LoadBackgroundLoop:
  LDA [pointerLo], y
  STA $2007           ; copy one background byte
  LDA pointerLo
  CLC
  ADC #$01
  STA pointerLo
  LDA pointerHi
  ADC #$00
  STA pointerHi       ; increment the pointer to the next byte
  
  LDA counterLo
  SEC
  SBC #$01
  STA counterLo
  LDA counterHi
  SBC #$00
  STA counterHi       ; decrement the loop counter
  
  LDA counterLo
  CMP #$00
  BNE LoadBackgroundLoop
  LDA counterHi
  CMP #$00
  BNE LoadBackgroundLoop  ; if the loop counter isn't 0000, keep copying
 
That is a lot of code to copy just one byte!

Nested Loops
To avoid using so much code, we can use both the X and Y registers as loop counters. By putting one loop inside another loop we create a "nested loop". First the inside loop counts all the way up. Then the outside loop counts up once, and the inside loop counts all the way again. Normally using only X or Y would only give a maximum of 256 times through a loop like we have previously done. With nested loops using both X and Y the maximum is the inside counter multiplied by the outside counter, or 256*256 = 65536.
  LDX #$00
  LDY #$00
OutsideLoop:
  
InsideLoop:
  ;
  ; this section runs 256 x 256 times
  ;
  
  INY                 ; inside loop counter
  CPY #$00
  BNE InsideLoop      ; run the inside loop 256 times before continuing down
  
  INX
  CPX #$00
  BNE OutsideLoop     ; run the outside loop 256 times before continuing down

First the Inside Loop runs and Y will count from 0 to 256. When that finishes X will count 0 to 1, and branch back to the beginning of the loops. Then the Inside Loop runs again, Y 0 -> 256. X now goes 1 -> 2 and the process continues. Everything ends when both X and Y have each counted to 256.

When we are using nested loops to copy entire backgrounds we want 256 x 4 = 1KB. The Y code from above can be unchanged, but the X code is changed to CPX #$04.

Because we are changing the Y register our previous pointer copying code also needs to be modified. Instead of incrementing the pointer every time, the incrementing Y register is doing the same thing. The low byte of the pointer will be kept at 0. This means your background data needs to be aligned to where the low byte of the address is $00. However the high byte of the pointer still needs to change. By always making the inside loop count 256 times, that will end at the same time that the high byte needs to change. This time 16 bit math isn't needed because only the high byte is incremented.

No loop counter is used because X and Y are used instead. If you cannot align your data so the low byte of the address is $00, you will have to use the CopyLoop above.
  LDA #$00
  STA pointerLo       ; put the low byte of the address of background into pointer
  LDA #HIGH(background)
  STA pointerHi       ; put the high byte of the address into pointer
  
  LDX #$00            ; start at pointer + 0
  LDY #$00
OutsideLoop:
  
InsideLoop:
  LDA [pointerLo], y  ; copy one background byte from address in pointer plus Y
  STA $2007           ; this runs 256 * 4 times
  
  INY                 ; inside loop counter
  CPY #$00
  BNE InsideLoop      ; run the inside loop 256 times before continuing down
  
  INC pointerHi       ; low byte went 0 to 256, so high byte needs to be changed now
  
  INX
  CPX #$04
  BNE OutsideLoop     ; run the outside loop 256 times before continuing down

Putting It All Together
Download and unzip the background3.zip sample files. All the code is in the background.asm file. Make sure that file, mario.chr, and background.bat is in the same folder as NESASM, then double click on background.bat. That will run NESASM and should produce background3.nes. Run that NES file in FCEUXD SP to see the full background.

The new nested loop is used to copy a whole background to the screen instead of only 128 bytes.  The background is aligned using the .org directive so the low address byte is $00.  The attributes are also placed directly after the background data so it is are copied at the same time.

Your task is to separate out the code that sets the pointer variables from the code that copies the loop. That way you can have multiple backgrounds that use different pointer loading code, but the same copy code.

If you are using a different assembler, the Indirect Indexed mode may use () instead of []. The LOW() and HIGH() syntax may also be different.



Edited: 05/26/2010 at 08:35 PM by bunnyboy

May 16, 2010 at 12:20:10 PM
Mario's Right Nut (352)
avatar
(Cunt Punch) < Bowser >
Posts: 6636 - Joined: 11/21/2008
Texas
Profile
This carry stuff is pretty neat. I saw the ADC used in MetalSlime's tutorials. I have a couple questions, if you have a second.

-You say that all pointer variables must come from the first 256 Bytes in RAM. Why is this? I'm sure I'm mistaken, but I thought that the music stuff was all kept in the $0300 block.

-Wouldn't it be better to set your inside loop to CPY #$F0 rather than $00? When you leave it at $00 aren't you just overwriting your attributes anyway? If attributes are kept in a seperate loop, you would be running the loop like $40 extra times. Or are you planning on putting attributes in the loop eventually?

-Might just note that you can use just "pointer .rs 2" and then use the variables "pointer" and "pointer+1". This makes it simpler to keep track of variables when you have like 20 pointers.

I thought that I had another ? but now I can't remember what it was. But I'm having the same paragraph issue I was having in your MMC1 thread.

Thanks for this. Keep 'em comming!

-------------------------

This is my shiny thing, and if you try to take it off me, I may have to eat you.

Check out my dev blog.


May 16, 2010 at 1:02:23 PM
bunnyboy (81)
avatar
(Funktastic B) < Master Higgins >
Posts: 7704 - Joined: 02/28/2007
California
Profile
Originally posted by: Mario's Right Nut

-You say that all pointer variables must come from the first 256 Bytes in RAM. Why is this? I'm sure I'm mistaken, but I thought that the music stuff was all kept in the $0300 block. 

To use the Indirect Indexed mode, only the low byte of the location in the brackets is used, so your pointer location needs to have the high byte be zero.  The high byte is just thrown away:
  .rsset $0004
pointer .rs 2
  lda [pointer], y   ; this is the same as lda [$04], y
  .rsset $0304
pointer .rs 2
  lda [pointer], y   ; this is also the same as lda [$04], y

MetalSlime uses $0300 for some variables like sound_disable_flag, but also uses zero page for pointers like the sound_ptr and jmp_ptr.




Originally posted by: Mario's Right Nut

-Wouldn't it be better to set your inside loop to CPY #$F0 rather than $00? When you leave it at $00 aren't you just overwriting your attributes anyway? If attributes are kept in a seperate loop, you would be running the loop like $40 extra times. Or are you planning on putting attributes in the loop eventually?

For that example I have the attribute data right after the background data, so both are copied in one loop.  That also means the next set of background/attributes will be aligned with the address low byte = $00.

But for a real game yes separating out the bg and attributes is usually a good idea.




Originally posted by: Mario's Right Nut

-Might just note that you can use just "pointer .rs 2" and then use the variables "pointer" and "pointer+1". This makes it simpler to keep track of variables when you have like 20 pointers.

Yup!  But for beginners remembering the Lo then Hi part is important.  You could also do pointerLo .rs 2 and use pointerLo and pointerLo+1.  

Apr 11, 2013 at 9:09:57 AM
LucasWeatherby (42)
avatar
(Lucas Weatherby) < El Ripper >
Posts: 1284 - Joined: 03/18/2011
Florida
Profile
Your task is to separate out the code that sets the pointer variables from the code that copies the loop. That way you can have multiple backgrounds that use different pointer loading code, but the same copy code.
 

Ok, question on this. I made a subroutine that is called from within in the "LoadBackground" section that will read Accumulator A and draw a background based off of the Accumulators value. It works fine and dandy if I hardcode the value of the Accumulator right before calling the subroutine.

I then made my "ReadA" section(functions, whatever you call these bad boys) change the value of the Accumulator when pressed. But the background never changes.

This leads me to believe that this is because the call is from within the Reset section of code and only called on startup. Therefore to solve my problem (and the question I am really asking is) I need to make my LoadBackground and Subroutine call from the NMI? Does that sound right? This is probably a dumb question, just want to make sure(I am away from my personal computer to try this method out. This was the idea that came to me in my dreams last night, though I would ask you guys about it).

And is it ok to redraw the background from within the NMI?



Edited: 04/11/2013 at 09:28 AM by LucasWeatherby

Apr 11, 2013 at 10:02:15 PM
MetalSlime (0)
avatar
(Thomas Hjelm) < Crack Trooper >
Posts: 140 - Joined: 08/14/2008
Japan
Profile
Originally posted by: LucasWeatherby

Your task is to separate out the code that sets the pointer variables from the code that copies the loop. That way you can have multiple backgrounds that use different pointer loading code, but the same copy code.
 

Ok, question on this. I made a subroutine that is called from within in the "LoadBackground" section that will read Accumulator A and draw a background based off of the Accumulators value. It works fine and dandy if I hardcode the value of the Accumulator right before calling the subroutine.

I then made my "ReadA" section(functions, whatever you call these bad boys) change the value of the Accumulator when pressed. But the background never changes.

This leads me to believe that this is because the call is from within the Reset section of code and only called on startup. Therefore to solve my problem (and the question I am really asking is) I need to make my LoadBackground and Subroutine call from the NMI? Does that sound right? This is probably a dumb question, just want to make sure(I am away from my personal computer to try this method out. This was the idea that came to me in my dreams last night, though I would ask you guys about it).

And is it ok to redraw the background from within the NMI?
 
Not a dumb question at all.  And good job diagnosing your problem!  That's exactly right.  The LoadBackground section is only run on reset, and that is before the controller is ever read.

The NMI isn't long enough to do a full background rewrite, but you can definitely draw some tiles and then continue drawing more the next NMI.  So your idea is on the right track, just don't expect to be able to copy a whole background's worth of tiles in one NMI.  Try writing a new subroutine called 'DrawTwoRows' or something and call that in your NMI after the controller read.



-------------------------
MetalSlime runs away

My nesdev blog: http://tummaigames.com/blog...

Jul 3, 2013 at 8:26:17 PM
SoleGooseProductions (129)
avatar
(Beau ) < King Solomon >
Posts: 3506 - Joined: 04/22/2013
Michigan
Profile
After the concrete goals of Lesson 7 (finally worked through most of them!), the assignment for this lesson baffles me. Primarily, what exactly is the task in lesson 8? The assignment is given of "separating the code that sets the pointer variables from the code that copies the loop," with the goal being to devise a way to "have multiple backgrounds that use different pointer loading code, but the same copy code." One respondent in the thread seemed to give indication that this was primarily asking one to turn part of the code into a subroutine, which would minimize the need to rewrite the copy code each time. Is this what is being asked for? A nudge in the right direction, or even a different phrasing for the task would be greatly appreciated. I am a bit unclear on the whole lesson to be honest, despite reading through it many times, so that may have something to do with my inability to figure out what is being asked.

-------------------------
"The light that burns twice as bright burns half as long..." ~ Blade Runner

SoleGooseProductions.com


Jul 3, 2013 at 8:37:05 PM
removed04092017 (0)
This user has been banned -- click for more information.
< Bowser >
Posts: 7316 - Joined: 12/04/2010
Other
Profile
Basically the idea is that. Using a look up table pointing to data and then using on program to handle the multiple datum and do stuff with it. And obviously 16-bit math with the carry stuff.

Jul 14, 2013 at 10:15:54 AM
SoleGooseProductions (129)
avatar
(Beau ) < King Solomon >
Posts: 3506 - Joined: 04/22/2013
Michigan
Profile
Sorry to post on this one again, but I still cannot seem to get what is going on with the lesson. I moved onto lesson 9 and finished that with minimal problems (other than forgetting to JSR a subroutine, that took way too long to track down!), so I am anxious to get moving onto something else besides Pong.

For this lesson, even basic things, such as why we are doing 16-bit math in the first place would be helpful, not to mention 32, 64, and higher math.

Regarding the "pointing" to background data I am a bit too lost to even ask a good question. I have tried various ways of separating the background code from the pointer variables all to no avail. If anybody would be willing to dumb it down a shade I would greatly appreciate it. Perhaps part of the problem lies in the following questions, though perhaps not: in the way the lesson's code is structured, where exactly would the initialization for further backgrounds be inserted? I understand that the nametables come at the end, but the initialization code for the background itself occurs before the NMI. This probably leads into further questions about how to structure code, where NMI's are placed, what needs to be repeated for each game state and or room, etc. I am guessing that most of my problems surrounding switching game states and backgrounds are to be found in this lesson. Oh, and I have read and reread the article over at NESDEV regarding Frames and NMI's, and while interesting I could not find a way to implement it. Any help would be greatly appreciated.

-------------------------
"The light that burns twice as bright burns half as long..." ~ Blade Runner

SoleGooseProductions.com


Jul 14, 2013 at 11:00:25 AM
DoNotWant (1)

(Ham Sammich) < Eggplant Wizard >
Posts: 441 - Joined: 12/08/2011
Sweden
Profile
Originally posted by: NESHERO27

Sorry to post on this one again, but I still cannot seem to get what is going on with the lesson. I moved onto lesson 9 and finished that with minimal problems (other than forgetting to JSR a subroutine, that took way too long to track down!), so I am anxious to get moving onto something else besides Pong.

For this lesson, even basic things, such as why we are doing 16-bit math in the first place would be helpful, not to mention 32, 64, and higher math.

Regarding the "pointing" to background data I am a bit too lost to even ask a good question. I have tried various ways of separating the background code from the pointer variables all to no avail. If anybody would be willing to dumb it down a shade I would greatly appreciate it. Perhaps part of the problem lies in the following questions, though perhaps not: in the way the lesson's code is structured, where exactly would the initialization for further backgrounds be inserted? I understand that the nametables come at the end, but the initialization code for the background itself occurs before the NMI. This probably leads into further questions about how to structure code, where NMI's are placed, what needs to be repeated for each game state and or room, etc. I am guessing that most of my problems surrounding switching game states and backgrounds are to be found in this lesson. Oh, and I have read and reread the article over at NESDEV regarding Frames and NMI's, and while interesting I could not find a way to implement it. Any help would be greatly appreciated.

Have you read MRN's tutorials?
http://nintendoage.com/forum/mess...


-------------------------
 

Jul 14, 2013 at 11:59:47 AM
KHAN Games (89)
avatar
(Kevin Hanley) < Master Higgins >
Posts: 8126 - Joined: 06/21/2007
Florida
Profile
Originally posted by: NESHERO27

Sorry to post on this one again, but I still cannot seem to get what is going on with the lesson. I moved onto lesson 9 and finished that with minimal problems (other than forgetting to JSR a subroutine, that took way too long to track down!), so I am anxious to get moving onto something else besides Pong.

For this lesson, even basic things, such as why we are doing 16-bit math in the first place would be helpful, not to mention 32, 64, and higher math.

Regarding the "pointing" to background data I am a bit too lost to even ask a good question. I have tried various ways of separating the background code from the pointer variables all to no avail. If anybody would be willing to dumb it down a shade I would greatly appreciate it. Perhaps part of the problem lies in the following questions, though perhaps not: in the way the lesson's code is structured, where exactly would the initialization for further backgrounds be inserted? I understand that the nametables come at the end, but the initialization code for the background itself occurs before the NMI. This probably leads into further questions about how to structure code, where NMI's are placed, what needs to be repeated for each game state and or room, etc. I am guessing that most of my problems surrounding switching game states and backgrounds are to be found in this lesson. Oh, and I have read and reread the article over at NESDEV regarding Frames and NMI's, and while interesting I could not find a way to implement it. Any help would be greatly appreciated.

There's not necessarily any reason he's using 16-bit math other than to show you how it's done.  I've still yet to use it in any of my projects, mostly because I haven't needed anything even remotely complicated for the simple stuff I've done.

What do you mean by this:

"Regarding the "pointing" to background data I am a bit too lost to even ask a good question. I have tried various ways of separating the background code from the pointer variables all to no avail."

How (in what way) are you trying to separate it?

-------------------------

gauauu: look, we all paid $10K at some point in our lives for the privilege of hanging out with Kevin


Jul 14, 2013 at 12:49:43 PM
SoleGooseProductions (129)
avatar
(Beau ) < King Solomon >
Posts: 3506 - Joined: 04/22/2013
Michigan
Profile
Ok, I will not worry too much about the math thing until I need it. Regarding the separation of code, BunnyBoy's assignment was to:

"Your task is to separate out the code that sets the pointer variables from the code that copies the loop. That way you can have multiple backgrounds that use different pointer loading code, but the same copy code."

I have tried to separate it out several different ways, in order to create some sort of subroutine (per 3Gens advice), but it never seems to load properly (lots of random tiles, flashing, etc.). I would not be so hung up on finishing it, but when I try to do a multi screen Pong (title, playing field, game over), I am unable to isolate the reusable portion of code and have to fully re-label each individual section in order to avoid assembler errors.

-------------------------
"The light that burns twice as bright burns half as long..." ~ Blade Runner

SoleGooseProductions.com


Aug 15, 2013 at 9:30:39 AM
LucasWeatherby (42)
avatar
(Lucas Weatherby) < El Ripper >
Posts: 1284 - Joined: 03/18/2011
Florida
Profile
Originally posted by: MetalSlime

Originally posted by: LucasWeatherby

Your task is to separate out the code that sets the pointer variables from the code that copies the loop. That way you can have multiple backgrounds that use different pointer loading code, but the same copy code.
 

Ok, question on this. I made a subroutine that is called from within in the "LoadBackground" section that will read Accumulator A and draw a background based off of the Accumulators value. It works fine and dandy if I hardcode the value of the Accumulator right before calling the subroutine.

I then made my "ReadA" section(functions, whatever you call these bad boys) change the value of the Accumulator when pressed. But the background never changes.

This leads me to believe that this is because the call is from within the Reset section of code and only called on startup. Therefore to solve my problem (and the question I am really asking is) I need to make my LoadBackground and Subroutine call from the NMI? Does that sound right? This is probably a dumb question, just want to make sure(I am away from my personal computer to try this method out. This was the idea that came to me in my dreams last night, though I would ask you guys about it).

And is it ok to redraw the background from within the NMI?
 
Not a dumb question at all.  And good job diagnosing your problem!  That's exactly right.  The LoadBackground section is only run on reset, and that is before the controller is ever read.

The NMI isn't long enough to do a full background rewrite, but you can definitely draw some tiles and then continue drawing more the next NMI.  So your idea is on the right track, just don't expect to be able to copy a whole background's worth of tiles in one NMI.  Try writing a new subroutine called 'DrawTwoRows' or something and call that in your NMI after the controller read.

 


Ok, back from my hiatus and ready to pick up where I left off. I had the same problems NESHERO27 is having until I tried your 2 row method. That seemed to fix the glitchy looking mess of a screen. I have attempted to make my screen change between two backgrounds when either A or B is pressed. I have sucessfully made the first 2 rows switch correctly. But I am struggling with picking up where I left off during the last NMI. I was hoping someone could help me pointerLo variable setup to the right value.


Here is an overview of my NMI:

NMI:
  LDA #$00
  STA $2003       ; set the low byte (00) of the RAM address
  LDA #$02
  STA $4014       ; set the high byte (02) of the RAM address, start the transfer
  
;;Background Control

;;LatchController

;;ReadA:

;;ReadB: 
 

;;This is the PPU clean up section, so rendering the next frame starts properly.
  LDA #%10010000   ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 1
  STA $2000
  LDA #%00011110   ; enable sprites, enable background, no clipping on left side
  STA $2001
  LDA #$00        ;;tell the ppu there is no background scrolling
  STA $2005
  STA $2005
  
  RTI      

In the Background Control I have the following:


;;Background Control
LoadBackgroundNMI:  
  LDA changeBG ;;load A with changeBackground boolean to see if a background change is needed
  AND #$01 ;;AND with 1 to see if boolean is set
  BEQ LoadBackgroundNMIDone ;;if not, branch to the end of NMI Background Load
 
  JSR SetBackGroundPointer                      ;Jump to subroutine that establishes background to load
 
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$20
  STA $2006             ; write the high byte of $2000 address
  LDA #$00
  STA $2006             ; write the low byte of $2000 address
 
  LDA pointerY;Attempting to load where I left off with previous NMI
  STA pointerLo ; put the low byte of the address of background into pointer
 
  LDY #$00;initializing y offset at 0
  
InsideLoopNMI:
  LDA [pointerLo], y  ; copy one background byte from address in pointer plus Y
  STA $2007 ; this runs 256 * 4 times (What the code used to do on this line)
  
  INY                 ; inside loop counter
  CPY #$80
  BNE InsideLoopNMI      ; run the inside loop 256 times before continuing down (What the code used to do on this line)
  
  TYA;Transfering y to Accumulator A
  STA pointerY;Attempting to store where I left off in pointerY for next NMI loop
  
  INC pointerHi       ; low byte went 0 to 256, so high byte needs to be changed now (What the code used to do on this line)
  
 
  ;;IF(Background is completely loaded){changeBG=FALSE;}
  ;LDA #$01
  ;STA changeBG
LoadBackgroundNMIDone:
;;End Background Control

And Finally, my setbackgroundpointer Function:


SetBackGroundPointer:
  LDA screenNumber
  and #$01   ;isolate the bit representing "up", by clearing all the other bits
  beq LoadScreen1Done   
LoadScreen1:  
  LDA #HIGH(background)
  STA pointerHi       ; put the high byte of the address into pointer
LoadScreen1Done:
 
  LDA screenNumber
  and #$02   ;isolate the bit representing "up", by clearing all the other bits
  beq LoadScreen2Done   
LoadScreen2:  
  LDA #HIGH(backgroundtwo)
  STA pointerHi       ; put the high byte of the address into pointer
LoadScreen2Done:
  
  RTS  



Edited: 08/15/2013 at 09:39 AM by LucasWeatherby

Aug 15, 2013 at 9:44:36 AM
Mario's Right Nut (352)
avatar
(Cunt Punch) < Bowser >
Posts: 6636 - Joined: 11/21/2008
Texas
Profile
You're not pointing to the right place in $2006 in the different times through the loop. If you're going to do it this way, the LDA #$20 and LDA #$00 will have to be variables linked to the time you are within that loading loop.

Or, simply switch off the background and do it all at once.

-------------------------

This is my shiny thing, and if you try to take it off me, I may have to eat you.

Check out my dev blog.


Aug 15, 2013 at 10:33:09 AM
LucasWeatherby (42)
avatar
(Lucas Weatherby) < El Ripper >
Posts: 1284 - Joined: 03/18/2011
Florida
Profile
Originally posted by: Mario's Right Nut

Or, simply switch off the background and do it all at once.


I was under the impression you couldnt change the entire background in one NMI?

Aug 15, 2013 at 10:37:36 AM
Mario's Right Nut (352)
avatar
(Cunt Punch) < Bowser >
Posts: 6636 - Joined: 11/21/2008
Texas
Profile
Originally posted by: LucasWeatherby

Originally posted by: Mario's Right Nut

Or, simply switch off the background and do it all at once.


I was under the impression you couldnt change the entire background in one NMI?
Not in NMI, but if you turn off the background, you can do whatever you want. 

It's buried in here or one of my other tutorials somewhere.

http://nintendoage.com/forum/messageview.cfm?catid=22&th...


-------------------------

This is my shiny thing, and if you try to take it off me, I may have to eat you.

Check out my dev blog.


Aug 15, 2013 at 11:36:13 AM
LucasWeatherby (42)
avatar
(Lucas Weatherby) < El Ripper >
Posts: 1284 - Joined: 03/18/2011
Florida
Profile
Originally posted by: Mario's Right Nut

Originally posted by: LucasWeatherby

Originally posted by: Mario's Right Nut

Or, simply switch off the background and do it all at once.


I was under the impression you couldnt change the entire background in one NMI?
Not in NMI, but if you turn off the background, you can do whatever you want. 

It's buried in here or one of my other tutorials somewhere.

http://nintendoage.com/forum/messageview.cfm?catid=22&th...
 

Hmm... I have yet to go thru your tutorials. I was hoping to finish with this tutorial 8 and then start fresh with yours. It appears that they build onto eachother. Should I just save my question until I make it through all of your tutorials? I have the pong game almost completely finished besides having a title screen and a game over screen...



Edited: 08/15/2013 at 01:50 PM by LucasWeatherby

Aug 15, 2013 at 2:48:57 PM
KHAN Games (89)
avatar
(Kevin Hanley) < Master Higgins >
Posts: 8126 - Joined: 06/21/2007
Florida
Profile
If you are changing to a completely different screen, just turn the screen off and load the entire new nametable. That way you won't have to worry about nmi or vblank or anything like that.

Updating background tiles with the screen on (during vblank) is only needed if you're updating some sort of gameplay background graphics while one is playing the game.

-------------------------

gauauu: look, we all paid $10K at some point in our lives for the privilege of hanging out with Kevin


Mar 31, 2014 at 5:18:28 PM
Mega Mario Man (63)
avatar
(Tim ) < Ridley Wrangler >
Posts: 2743 - Joined: 02/13/2014
Nebraska
Profile
Questions in the code below. Actaully, more like clarification.

Question(s) 1: In the line of "LDA #HIGH(background)", can I assume that #HIGH and (background) is understood by the system? Did we tell the system to understand this in our code somewhere or does it just know? Is there a correct time to use #LOW(background) instead of #$00, or are they the same?

Question 2: The brackets [] are confusing me around the pointerLo. Why does that pointer get brackets and pointerHi not? I keep reading the Indirect Indexed Mode, but I get more confused each time.

LDA #$00
STA pointerLo ; put the low byte of the address of background into pointer
LDA #HIGH(background)
STA pointerHi ; put the high byte of the address into pointer

LDX #$00 ; start at pointer + 0
LDY #$00
OutsideLoop:

InsideLoop:
LDA [pointerLo], y ; copy one background byte from address in pointer plus Y
STA $2007 ; this runs 256 * 4 times

INY ; inside loop counter
CPY #$00
BNE InsideLoop ; run the inside loop 256 times before continuing down

INC pointerHi ; low byte went 0 to 256, so high byte needs to be changed now

INX
CPX #$04
BNE OutsideLoop ; run the outside loop 256 times before continuing down

-------------------------
Current Project
Isometric Survival Horror

Older Projects
Tailgate Party, Power Pad Demo, Happy Hour

Links
Store, Facebook, Twitter

Mar 31, 2014 at 5:22:45 PM
removed04092017 (0)
This user has been banned -- click for more information.
< Bowser >
Posts: 7316 - Joined: 12/04/2010
Other
Profile
1: They are not the same. HIGH() is an ASSEMBLER FUNCTION and goes to the label we used, and pulls the high and low bytes with the function to get them so in our code we don't have to use random numbers, when it moves our code is automatically right.

2: [] addressing mode means "Look at where zeropage (variable name in []) points to and adds Y to it. The INC doesn't need brackets because we don't care wtf it points to, it just need to be incremented so our data is pulled right from the pointer location. Pointer is a 2 byte variable. low and high attached just say which byte it is we are accessing, with the low byte being the pointer it's self.

Apr 1, 2014 at 8:12:37 AM
thefox (0)
avatar
(Kalle Immonen) < Meka Chicken >
Posts: 533 - Joined: 07/08/2008
Finland
Profile
Originally posted by: Mega Mario Man

Is there a correct time to use #LOW(background) instead of #$00, or are they the same?
In this case it doesn't matter much, because the code expects the lowbyte to be $00, otherwise it doesn't work. But it would be preferable to use LOW(background) for clarity. In some assemblers you can define an ASSERT, so that if "background" is moved to some other place and is no more aligned to a 256-byte page, the code would no longer compile (which lets you know that there's a problem and you have to fix it).

LOW() and HIGH() are NESASM specific operations, other assemblers most often use <background and >background for the same purpose ("<" = low, ">" = high).


-------------------------
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: kkfos.aspekt.fi

Apr 1, 2014 at 9:18:48 AM
Mega Mario Man (63)
avatar
(Tim ) < Ridley Wrangler >
Posts: 2743 - Joined: 02/13/2014
Nebraska
Profile
Thanks 3GenGames and thefox! It makes a bit more sense now. Don't have the full grasp of the [] yet, but what 3Gen mentions make sense to me. Adding a value to an address requires brackets, incrementing does not. IT's kind of like how Excel distinguishes a string from a value with quotes "". Obviously, it not exactly the same, but the theory is the same and it's something I can relate the code to so it makes sense to me.

-------------------------
Current Project
Isometric Survival Horror

Older Projects
Tailgate Party, Power Pad Demo, Happy Hour

Links
Store, Facebook, Twitter

Apr 2, 2014 at 1:05:09 PM
thefox (0)
avatar
(Kalle Immonen) < Meka Chicken >
Posts: 533 - Joined: 07/08/2008
Finland
Profile
Originally posted by: Mega Mario Man

Thanks 3GenGames and thefox! It makes a bit more sense now. Don't have the full grasp of the [] yet, but what 3Gen mentions make sense to me.
Here's an example:

Let's say the memory (RAM) is laid out as follows:
Address    Value
-------    -----
$0010      $12
$0011      $34
$0066      $78
$3468      $9A
Let's also start with Y = $56 for the sake of the example.

Now, LDA $10,Y would load A from the address $10+Y = $10+$56 = $66, so A = $78.

If you do LDA [$10],Y, 6502 first fetches a 16-bit address from memory at $10 and $11. The address, in this case, is $3412 (6502 is little-endian, meaning the low byte of address is stored first in memory). It then takes that address and adds Y to it. Result is $3412 + Y = $3412 + $56 = $3468. Finally, it fetches the byte from this address, so in this case, A = $9A.

Note that even though you can use a 16-bit address with the LDA aaaa,Y addressing mode, LDA (aa),Y only accepts 8-bit addresses (in other words, the address must be on the zero page).

Also note that many other assemblers use the notation LDA ($10),Y for the exact same thing that is LDA [$10],Y in NESASM.

Finally note that names like pointerLo, pointerHi, background, etc. are just fancy names for memory addresses to make the code easier to write and understand. In this case, you could create symbols like pointerLo=$10 and pointerHi=$11, but I left them out to make the example easier to grasp.

-------------------------
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: kkfos.aspekt.fi

Apr 2, 2014 at 1:51:35 PM
Mega Mario Man (63)
avatar
(Tim ) < Ridley Wrangler >
Posts: 2743 - Joined: 02/13/2014
Nebraska
Profile
Originally posted by: thefox

Originally posted by: Mega Mario Man

Thanks 3GenGames and thefox! It makes a bit more sense now. Don't have the full grasp of the [] yet, but what 3Gen mentions make sense to me.
Here's an example:

Let's say the memory (RAM) is laid out as follows:
Address    Value
-------    -----
$0010      $12
$0011      $34
$0066      $78
$3468      $9A
Let's also start with Y = $56 for the sake of the example.

Now, LDA $10,Y would load A from the address $10+Y = $10+$56 = $66, so A = $78.

If you do LDA [$10],Y, 6502 first fetches a 16-bit address from memory at $10 and $11. The address, in this case, is $3412 (6502 is little-endian, meaning the low byte of address is stored first in memory). It then takes that address and adds Y to it. Result is $3412 + Y = $3412 + $56 = $3468. Finally, it fetches the byte from this address, so in this case, A = $9A.

Note that even though you can use a 16-bit address with the LDA aaaa,Y addressing mode, LDA (aa),Y only accepts 8-bit addresses (in other words, the address must be on the zero page).

Also note that many other assemblers use the notation LDA ($10),Y for the exact same thing that is LDA [$10],Y in NESASM.

Finally note that names like pointerLo, pointerHi, background, etc. are just fancy names for memory addresses to make the code easier to write and understand. In this case, you could create symbols like pointerLo=$10 and pointerHi=$11, but I left them out to make the example easier to grasp.
The pointers I get, I have used them in other languages. In the case of #HIGH(background), I wasn't for sure if background was a term that NES 6502 knew itself or was it actually refering to the subroutine holding the background data.

I like your example showing the difference betweem LDA [$10],Y and LDA $10,Y. Visualizing the results is a perfect way to explain this. In fact, I really don't understand a lot of the tutorials until I dig into the source code from the tutorial and change values. Then I can see the results of what I just changed in the emulator. That's when the light bulb turns on!

'm very much a visual learner as opposed to reading. Seeing the code on screen and reading the explanations of what it does confuses me. Actually tinkering with the code and checking the results is where I learn the most.



-------------------------
Current Project
Isometric Survival Horror

Older Projects
Tailgate Party, Power Pad Demo, Happy Hour

Links
Store, Facebook, Twitter

Apr 2, 2014 at 6:40:48 PM
KHAN Games (89)
avatar
(Kevin Hanley) < Master Higgins >
Posts: 8126 - Joined: 06/21/2007
Florida
Profile
background is generally a data map with a bunch of values of tile numbers that make up the nametable. It isn't anything that the assembler knows natively.

-------------------------

gauauu: look, we all paid $10K at some point in our lives for the privilege of hanging out with Kevin