It is currently Tue Jun 27, 2017 1:52 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 11 posts ] 
Author Message
 Post subject: Loading backgrounds
PostPosted: Tue Dec 18, 2012 5:01 am 
Offline

Joined: Tue Dec 04, 2012 7:08 am
Posts: 6
Hi, I'm new to the forums, so I'll start by introducing myself a bit: I'm Vic, and I'm a retro-gaming enthousiast, so I recently started learning NES ASM so I could make my own game with a nice vintage feel.
I know a bit of programming, but I'm new to ASM. I'm starting to understand the logic behind it.
I'm currently learning with the Nerdy Nights tutorial, and the NESASM3 assembler.

However, I'm confused when it comes to loading the background for my game (or actually, when it comes to anything handling 16-bit addresses with 8-bit registers, but let's stick to BG loading for now).

I found a few codes for loading BG, some of which I got to work, but I can't understand how they work.

I'm currently using this code:

Code:
(...)

LoadBackground:
  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 #$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


(...)


 .bank 1
  .org $E000

background:
  .db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00  ;;row 1
  .db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00  ;
(...)
  .db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00  ;;row 30
  .db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00  ;


So this works, but I don't really understant how, so I can't modify it to do what I want with it.

Could you tell me what "#HIGH(background)" means? I think it gets the high byte of the address of the first value after the "background" label, but i'm not sure.
And why "LDA [pointerLo], y" has "pointerLo" in []?

Also, is the "INC pointerHi" instruction of any use, since pointerHi isn't recalled elsewhere?

And why is the .org $E000 instruction important? If I remove it, I just get a grey screen, but from what I understand, the "#HIGH(background)" should make it useless.


So, if anyone could let me see this a little better, it would be awesome.
Thanks


Top
 Profile  
 
 Post subject: Re: Loading backgrounds
PostPosted: Tue Dec 18, 2012 5:23 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 9757
Location: Rio de Janeiro - Brazil
Vic wrote:
Could you tell me what "#HIGH(background)" means?

"background" is an address, meaning it's a 16-bit value. LOW() and HIGH(), which are assembler functions, return the individual bytes of the 16-bit value, so you can load them into the accumulator (which is only 8 bits). This code assumes that "background" will be at the beginning of a memory page, so instead of using LOW() for the low byte it just uses $00.

Quote:
And why "LDA [pointerLo], y" has "pointerLo" in []?

In NESASM, [] means indirection (most 6502 assemblers use () for indirect addressing though). This instruction uses the byte at pointerLo and the one next to it as a pointer to an address, and adds the value in Y to that to find the actual target.

Quote:
Also, is the "INC pointerHi" instruction of any use, since pointerHi isn't recalled elsewhere?

It might not be written in the code, but like I said above, the indirect addressing mode will use the byte you tell it to and the one next to it, which in this case is "pointerHi".

Quote:
And why is the .org $E000 instruction important?

Apparently the purpose is to place the "background" data at the start of a memory page.

Quote:
from what I understand, the "#HIGH(background)" should make it useless.

Maybe if LOW() was used to set the low byte of the pointer instead of $00 the .org would be useless.


Top
 Profile  
 
 Post subject: Re: Loading backgrounds
PostPosted: Tue Dec 18, 2012 5:39 am 
Offline

Joined: Tue Dec 04, 2012 7:08 am
Posts: 6
Thanks for the answers!

I guess what I didn't understand is mostly inderect addressing. It gets a lot clearer now. :D


Top
 Profile  
 
 Post subject: Re: Loading backgrounds
PostPosted: Tue Dec 18, 2012 8:51 am 
Offline
User avatar

Joined: Sun Jan 02, 2011 11:50 am
Posts: 522
You should recognise that a nametable (background) is 960 bytes and the attribute table is 64 bytes for a total of 1024 bytes. Your code writes 1024 bytes, but it wasn't obvious that you have attribute data in your code - it looks like it stops at nametable data.


Top
 Profile  
 
 Post subject: Re: Loading backgrounds
PostPosted: Tue Dec 18, 2012 9:45 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2839
Location: Tampere, Finland
I want to point out a couple of flaws in the code you pasted:

Code:
  LDA #$00
  STA pointerLo       ; put the low byte of the address of background into pointer

This is bad practice, because if background moves, the low byte of pointer might not be 0 anymore. You should use LOW(background) instead of $00.

--

Some less critical performance optimizations:

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

CPY #$00 is not needed here, because INY will set the zero-flag anyways when Y gets set to 0.

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

If you initialized X to 4, and used DEX here, you could get rid of CPX #$04 (same reason as above).

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


Top
 Profile  
 
 Post subject: Re: Loading backgrounds
PostPosted: Tue Dec 18, 2012 11:56 am 
Offline

Joined: Tue Dec 04, 2012 7:08 am
Posts: 6
@Movax12: I also copy the attributes table using this loop, I find it easier that way than having a loop going 960 times for BG, and another 64 times for attribute. I just didn't paste the code for my attribute values

@thefox: Thanks for the advice :wink:
Just to be sure, you mean if backgroung moves within the code, not scrolling, right? Anyway, it sounds reasonable.


Top
 Profile  
 
 Post subject: Re: Loading backgrounds
PostPosted: Tue Dec 18, 2012 12:29 pm 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2839
Location: Tampere, Finland
Vic wrote:
Just to be sure, you mean if backgroung moves within the code, not scrolling, right?

Yes, in the code.

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


Top
 Profile  
 
 Post subject: Re: Loading backgrounds
PostPosted: Tue Dec 18, 2012 4:47 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 9757
Location: Rio de Janeiro - Brazil
Vic wrote:
Just to be sure, you mean if backgroung moves within the code, not scrolling, right?

Scrolling is a whole different issue... You'd hardly store scrolling backgrounds that way, uncompressed in the ROM, unless you only had a handful of screens. At 1KB per screen, an NROM program wouldn't even be able to hold 32 screens, since the space is also needed for code and other data. Scrolling games usually make use of metatiles, often combined with some other compression method (RLE, LZ, multiple layers of metatiles, etc.), to compress their maps.


Top
 Profile  
 
 Post subject: Re: Loading backgrounds
PostPosted: Wed Dec 19, 2012 1:41 pm 
Offline

Joined: Tue Dec 04, 2012 7:08 am
Posts: 6
OK, I'm starting to get a hold on this, but I still can't get things to work the way I want...

So now my background loading code looks like this

Code:
  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
  JSR LoadBackground

(...)

LoadBackground: ;Load background stored at address pointerHi PointerLo
  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;

  LDX #$04            ; 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
  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
 
  DEX
  BNE OutsideLoop     ; run the outside loop 256 times before continuing down
  RTS


Which is basically the same code as before, except using a sub-routine so I can use it multiple times and cleaned up following your advice, and it works... sometimes.

So my program looks like this:

Code:
(Variables & constants, Reset vector start, and some init code...)

  LDA #LOW(background1)
  STA pointerLo       ; put the low byte of the address of background into pointer
  LDA #HIGH(background1)
  STA pointerHi       ; put the high byte of the address into pointer
  JSR LoadBackground

(...)

(at some point wher I want to load a new BG)

  LDA #%00010000   ; disable NMI, sprites from Pattern Table 0, background from Pattern Table 1
  STA $2000

  LDA #LOW(background2)
  STA pointerLo       ; put the low byte of the address of background into pointer
  LDA #HIGH(background2)
  STA pointerHi       ; put the high byte of the address into pointer
  JSR LoadBackground  ;load new BG

  LDA #%10010000   ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 1
  STA $2000
  LDA #%00001111   ; Disable sprites, enable background, no clipping on left side, Greyscale
  STA $2001
  LDA #$00        ;;tell the ppu there is no background scrolling
  STA $2005
  STA $2005



The first background loads fine (during init), but the second one doesn't work, I have messy graphics in the upper left corner, and the rest of the screen isn't updated.
So if anyone has an idea why this doesn't work, or a suggestion for another code, I'd be glad.


Top
 Profile  
 
 Post subject: Re: Loading backgrounds
PostPosted: Wed Dec 19, 2012 1:56 pm 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2839
Location: Tampere, Finland
Just guessing here because you left out some of the code, but you have to disable rendering before loading the 2nd background.

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


Top
 Profile  
 
 Post subject: Re: Loading backgrounds
PostPosted: Wed Dec 19, 2012 2:32 pm 
Offline

Joined: Tue Dec 04, 2012 7:08 am
Posts: 6
That was the problem indeed. Many thanks!


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 1 guest


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