How to compare to a value larger than $ff?

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

User avatar
Canite
Posts: 26
Joined: Tue Jun 28, 2011 9:46 pm
Location: Las Vegas, NV, USA

How to compare to a value larger than $ff?

Post by Canite » Wed Jun 29, 2011 6:19 pm

This question is a lot different than my other questions, so I figured a new topic would be better, but it's basically just the title, how do I compare to a value larger than $ff? I'm loading my background in, and it's saved in a label, backgrounds: and has all the backgrounds stored in .db's. The only problem, is that for the full screen, there is 32x32, 8x8 tiles, which is 1024 bytes of data.. Here's how I'm loading the backgrounds:

Code: Select all

LoadBackgrounds:
  LDA $2002
  LDA #$20
  STA $2006
  LDA #$00
  STA $2006
  LDX $00
LoadBackgroundsLoop:
  LDA backgrounds, x
  STA $2007
  INX
  CPX #$FF ; Here's where I need to compare to 1024, or $0400, but it only allows 1 byte to be compared to
  BNE LoadBackgroundsLoop
So, is there a better to do this, or is there any to compare to a 2 byte value?
How do you guys normally load the background?

tepples
Posts: 22052
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples » Wed Jun 29, 2011 6:43 pm

If you want to access an array bigger than 256 bytes, you first need to store the starting address on zero page (lda #<label sta src_addr lda #>label sta src_addr+1). You may want to load the number of 256-byte "pages" into X, which in the case of an uncompressed nametable is four (ldx #4). Then use the (d),Y addressing mode and step through each page with Y. When Y wraps around to zero (hint: your loop will use a BNE instruction), increase the high byte of the address (inc src_addr+1) and decrease X until you've gone through all four pages.

User avatar
Canite
Posts: 26
Joined: Tue Jun 28, 2011 9:46 pm
Location: Las Vegas, NV, USA

Post by Canite » Wed Jun 29, 2011 7:06 pm

Ahhh I think I understand that, like a double for loop? But I may have trouble translating that to code.. Here's my attempt

Code: Select all

LoadBackgroundsLoopX:
  LDA backgrounds, x ; how do I add 1 to the high byte here?
  STA $2007
  INX
  CPX #$FF
  BNE LoadBackgroundsLoopX
LoadBackgroundsLoopY:
  DEY
  LDX #$00
  CPY #$00
  BNE LoadBackGroundsLoopX
But that's just going to load the first 256 bytes 4 times.. Any more tips? I'd still like to figure this out without someone handing me the code.

tepples
Posts: 22052
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples » Wed Jun 29, 2011 7:08 pm

You need to store the address of 'backgrounds' in a pointer on zero page, and then you need to look at what the (d),y indirect addressing mode does.

User avatar
tokumaru
Posts: 11858
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru » Wed Jun 29, 2011 7:10 pm

Since the 6502 is an 8-bit CPU, it can only manipulate 8 bits (which can hold values from 0 to 255) at a time. If you need numbers larger than that, you have to work with the individual bytes that form that larger number. For example, if you want to compare 2 16-bit numbers, you have to first compare the high byte and then the low byte.

But in this particular case, it's not a 16-bit compare you are looking for. Like tepples said, the 6502 has a very useful addressing mode that is meant to allow access data larger than 256 bytes. For this you need a pointer and an index. The pointer is stored in zero page RAM ($0000-$00FF), and the index is the Y register.

A pointer is a 16-bit value that indicates an address. Even though the 6502 can't manipulate 16-bit values internally, it can access 16-bit pointers in RAM. To read data using a pointer you need the instruction "LDA (Pointer), Y". When the CPU sees this instruction, it will go to the ZP location where the variable "Pointer" is, it will see what address is stored there and will then look for your data in that address. Y is used as an offset into that data, so you can access up to 256 bytes starting from the address indicated by the pointer.

If you need to access more than 256, just modify the pointer. By incrementing the high byte of the pointer you effectively add 256 to it, which will allow you to read 256 more bytes. If you keep doing this you can access as many bytes as you want.

User avatar
Canite
Posts: 26
Joined: Tue Jun 28, 2011 9:46 pm
Location: Las Vegas, NV, USA

Post by Canite » Wed Jun 29, 2011 8:15 pm

So like..

Code: Select all

.rsset $0000
pointer .rs 2 ; it needs 2 bytes right?

LDA #<backgrounds ; low byte
STA pointer
LDA #>backgrounds ; high byte
STA pointer + 1 ; now pointer is background's full address
LDX #$04
LDY #$00

BackgroundLoop:
  LDA (pointer), y ; y is the low byte
  STA $2007
  INY
  CPY #$FF ; increase until it hits $FF
  BNE BackgroundLoop
DecX:
  INC pointer +1 ; increase the high byte
  LDY #$00 ; reset the low byte
  DEX ; and decrese x by one
  CPX #$00 ; check if X is down to 0 yet (after 4 loops)
  BNE BackgroundLoop
Ignore the comments, I was just figuring it out one piece at a time.
Man my brain hurts after that.. haha. I think that would work though, right?

User avatar
tokumaru
Posts: 11858
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru » Wed Jun 29, 2011 8:25 pm

Canite wrote:I think that would work though, right?
Almost! You got the right idea, there are just a couple of logic issues:

1. The "CPY #$FF" is not necessary. If you use it, you will actually copy 255 bytes instead of 256. Just let Y wrap back to $00 with the INY, and because the Z flag will be set the branch will not happen, so there's no need to compare anything. As a bonus you also don't need to reset Y later, as it will already be 0.

2. Similar to the above, you don't need the "CPX #$00". This isn't an error though, it's just redundant. If the result of any operation is zero, the Z flag will be set, so there's no need to compare against #$00.

The status flags are kinda mystical for people who have never programmed in assembly before (for me at least that was one of the weirdest things). Just keep in mind that a lot of instructions modify the flags (specially the ones that manipulate values in any way), and after these modifications you can use the branch instructions to make decisions about what to do next. You don't have to "compare" all the time, as most of the time the operations themselves will set the flags for you.
Last edited by tokumaru on Wed Jun 29, 2011 8:30 pm, edited 1 time in total.

User avatar
Canite
Posts: 26
Joined: Tue Jun 28, 2011 9:46 pm
Location: Las Vegas, NV, USA

Post by Canite » Wed Jun 29, 2011 8:30 pm

tokumaru wrote: 2. Similar to the above, you don't need the "CPX #$00". This isn't an error though, it's just redundant. If the result of any operation is zero, the Z flag will be set, so there's no need to compare against #$00.
Ah, I didn't even know you could do that, thanks. But when I try to compile it, NESASM throws some errors at me :/ It says that the LDA #<backgrounds part is an unknown instruction. Is my syntax wrong?

User avatar
tokumaru
Posts: 11858
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru » Wed Jun 29, 2011 8:34 pm

NESASM uses weird syntax. All other assemblers use LDA (Pointer), Y, while NESASM uses LDA [Pointer], Y. For accessing the low and high bytes of a value you have to use LDA #LOW(Value) and LDA #HIGH(Value), I think. NEASM is realy weird.

User avatar
Canite
Posts: 26
Joined: Tue Jun 28, 2011 9:46 pm
Location: Las Vegas, NV, USA

Post by Canite » Wed Jun 29, 2011 8:45 pm

tokumaru wrote:NESASM uses weird syntax. All other assemblers use LDA (Pointer), Y, while NESASM uses LDA [Pointer], Y. For accessing the low and high bytes of a value you have to use LDA #LOW(Value) and LDA #HIGH(Value), I think. NEASM is realy weird.
Gah, I've tried everything, always gives an error..
I have this now:

Code: Select all

LDA #LOW(backgrounds) ; low byte 
STA pointer 
LDA #HIGH(backgrounds) ; high byte 
STA pointer + 1 ; now pointer is background's full address 
but it still gives an error?

User avatar
tokumaru
Posts: 11858
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru » Wed Jun 29, 2011 8:49 pm

Did you also change LDA (pointer), Y to LDA [pointer], Y? It should be working...

EDIT: Maybe NESASM doesn't support the "pointer + 1" part? Have you tried declaring your pointer as two individual HI and LO bytes? Or putting "pointer + 1" between parenthesis? I really can't see what's wrong now.

User avatar
Canite
Posts: 26
Joined: Tue Jun 28, 2011 9:46 pm
Location: Las Vegas, NV, USA

Post by Canite » Wed Jun 29, 2011 9:27 pm

tokumaru wrote:Did you also change LDA (pointer), Y to LDA [pointer], Y? It should be working...
Yeah, I did, here's there error
Image
EDIT: Maybe NESASM doesn't support the "pointer + 1" part? Have you tried declaring your pointer as two individual HI and LO bytes? Or putting "pointer + 1" between parenthesis? I really can't see what's wrong now.
If I do it this way, what will I put in in the LDA (pointer), y part?
Last edited by Canite on Wed Jun 29, 2011 9:52 pm, edited 1 time in total.

User avatar
Dwedit
Posts: 4352
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit » Wed Jun 29, 2011 9:33 pm

Time to switch to ASM6?
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!

User avatar
Canite
Posts: 26
Joined: Tue Jun 28, 2011 9:46 pm
Location: Las Vegas, NV, USA

Post by Canite » Wed Jun 29, 2011 9:40 pm

Dwedit wrote:Time to switch to ASM6?
I guess? Haha.
I just started learning assembly yesterday, so I didn't really want to be switching around like that.. How different are assemblers from each other?

User avatar
Kasumi
Posts: 1292
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi » Wed Jun 29, 2011 10:01 pm

Canite wrote: Image
My guess? You didn't indent those instructions.

Code: Select all

sta pointer+1
is different than

Code: Select all

 sta pointer+1;Note the space before the instruction
nesasm gives unknown instruction when you don't indent with whitespace characters. There's no other reason I can think of why STA pointer wouldn't work.
Edit:
tokumaru wrote:Did you also change LDA (pointer), Y to LDA [pointer], Y? It should be working...
As of the latest nesasm, either of those works fine.
Edit again several months later: Both ASSEMBLE fine, but it with paranthesis it gives the wrong addressing mode. Sorry, I can be so confident when I'm wrong.
Last edited by Kasumi on Wed Apr 11, 2012 3:51 pm, edited 1 time in total.

Post Reply