NESASM absolute addressing on STA instruction

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
NESASM absolute addressing on STA instruction
by on (#187583)
I scoured this (and many other) pages for the past two hours so I apologize if this is already answered somewhere, but I'm at my wit's end. I'm trying to load more than 256 bytes to a memory blob (for collision detection). I load the high and low bytes of the collision data blob (728 bytes) and want to store accumulator values in that data using indirect addressing. The problem, however, is that I can't stick my data into it. I've tried several things (some below), but feel like I'm missing a fundamental syntax here...

Code:
collision_map .rs 768 ; gets assigned $0005
collision_low  .rs 1 
collision_high .rs 1
...
LoadData:
  LDA #low(collision_map)
  STA collision_low             ; confirmed puts $05 in collision_low
  LDA #high(collision_map)
  STA collision_high            ; confirmed puts $00 in collision_high
  LDX $00
  LDY $00
  LDA #$24                     ; for simplicity sake, lets say i just want to load the value '24'
LoadDataLoop:
  ; These are things I've tried
  STA (collision_low), Y ; this will store A at the address of collision_low and not collision_map
  STA [collision_low], Y ; Doesn't build: Incorrect zero page address!
  STA collision_low, Y ; this will store A at the address of collision_low and not collision_map
  STA $0005, Y ; works, but whole point is to avoid hard coding the address and trying to use vars bc this won't work for over 256 bytes
  ; End things i've tried (I also tried a combination of putting '#', '$', '<', '>' in front of collision_low in a desperate attempt to get lucky
  INY
  CPY #$00
  BNE LoadDataLoop
  LDA collision_high
  CLC
  ADC #$01
  STA collision_high
  LDA #$24                      ; reset back to value I want
  INX
  CPX #$03
  BNE LoadDataLoop
Re: NESASM absolute addressing on STA instruction
by on (#187584)
1. NESASM has nonstandard syntax for indirect addressing. Use square brackets instead:
Code:
sta [collision_low], Y


2. collision_low is not on the zero page. There are only 256 bytes of ZP and you've already reserved 768 bytes on it before trying to add collision_low. (The two bytes of an indirect address pointer must be on the ZP.)
Re: NESASM absolute addressing on STA instruction
by on (#187585)
As stated above, when I try that (and just retried with copy pasting your code), it doesn't build with error: Incorrect zero page address!
Re: NESASM absolute addressing on STA instruction
by on (#187586)
As a quick fix, try swapping the position of the pointer and the array in memory:
Code:
collision_low  .rs 1 
collision_high .rs 1
collision_map .rs 768

The problem seems to be that the 768-byte array is bumping the pointer to a memory position that's not in page 0, and indirect indexed addressing only works if the pointer is in ZP.

You will have to do something about that 768-byte array though, because starting it in ZP will make it occupy the rest of ZP, the entirity of pages 1 (the stack!) and 2 (normally the OAM buffer), and a bit of page 3. You should probably start this array on page 3 at least, and have it occupy 3 consecutive pages (3, 4 and 5).
Re: NESASM absolute addressing on STA instruction
by on (#187587)
mitch3a wrote:
As stated above, when I try that (and just retried with copy pasting your code), it doesn't build with error: Incorrect zero page address!

Sorry, I think you caught my post before I edited it with the second point. (I thought I could get it in there quick, but you were quicker!)
Re: NESASM absolute addressing on STA instruction
by on (#187588)
Switching the order fixed it. TY TY TY TY. Totally misunderstood that the zero page was for the pointer itself and not for what it was pointing to, which was why I was trying for absolute indexing.

Also thanks for the pro-tip on moving that giant blob. Will do. Thanks for the ridiculously quick responses and not immediately hating on me for using Nesasm :D
Re: NESASM absolute addressing on STA instruction
by on (#187589)
There's a lot of wrong things here. Where to begin?

Subject says "absolute addressing", when that isn't what you're wanting to use at all. What you're wanting to use is indirect addressing, so that you have a way to handle ranges of >256 bytes.

Per the official nesasm documentation: http://www.nespowerpak.com/nesasm/usage.txt indirect addressing in nesasm uses brackets [] not parenthesis ().

You've omitted the relevant .rsset. I'm going to assume it defaults to $0000, but I would rather not assume anything.

Now, this code:

Code:
collision_map .rs 768 ; gets assigned $0005
collision_low  .rs 1 
collision_high .rs 1
...
LoadData:
  LDA #low(collision_map)
  STA collision_low             ; confirmed puts $05 in collision_low
  LDA #high(collision_map)
  STA collision_high            ; confirmed puts $00 in collision_high

Let's assume for a moment collision_map really does start at $0005.

The above code will stick the low byte of the address of the collision_map variable, i.e. value $05, into variable collision_low. It'll then put the high byte of the address of the collision_map variable, i.e. $00, into variable collision_high.

Using indirect addressing, you can then begin accessing data stored in collision_map like so:

Code:
ldy #0
lda [collision_low], y

This would access the first byte of what's contained at collision_map.

Now, let's talk about this, because it'll shed light on the situation:

Code:
  STA [collision_low], Y ; Doesn't build: Incorrect zero page address!


And here we have our answer: this assembler error is valid. It means that collision_low is not within zero page. For indirect addressing to work, the variables (for the low and high byte) need to be stored in zero page ($00 to $FF).

What this means is that collision_low and collision_high are outside of zero page. And that's certainly the case, because, again:

Code:
collision_map .rs 768 ; gets assigned $0005
collision_low  .rs 1 
collision_high .rs 1


If collision_map is $0005, that means $0005+768 will be the address of collision_low, which is $0305. That isn't in zero page, is it? :-)

Please refer to your assembler manual, looking at directives .zp and .bss for further details.

Edit: quintuple sniped.
Re: NESASM absolute addressing on STA instruction
by on (#187590)
mitch3a wrote:
Switching the order fixed it.

Don't keep it that way though. Big arrays don't belong in zero page, which should be reserved for pointers and commonly used variables as much as possible, but way more importantly than that, the array is so big that it completely overlaps page 1, where the stack lies. This means that when the stack is used, the array will get corrupted, and when the array is written to, the stack might get corrupted, possibly crashing the program.

Quote:
Also thanks for the pro-tip on moving that giant blob. Will do.

:wink:
Re: NESASM absolute addressing on STA instruction
by on (#187592)
Definitely heard you all on the problems with putting the blob in the zero page and of course once I loaded my blob, I saw the program crash. Still getting a handle on this so I appreciate your patience. Anyway, finally have it all loading as desired without crashing so I figured I'd post what finally worked for me. I'm sure it's still far from perfect, but figured it might be helpful for the next guy. Thanks again everyone for the quick/informative responses!

Code:
  .rsset $0000          ; Set the internal counter of the RS directive to start of zero-page
collision_low  .rs 1 
collision_high .rs 1
  .rsset $0300          ; Set the internal counter of the RS directive to a free blob after the stack, etc
collision_map .rs 768
...
LoadData:
  LDA #low(collision_map)
  STA collision_low             ; confirmed puts $05 in collision_low
  LDA #high(collision_map)
  STA collision_high            ; confirmed puts $00 in collision_high
  LDX #$00
  LDY #$00
  LDA #$24                     ; for simplicity sake, lets say i just want to load the value '24'
LoadDataLoop:
  STA [collision_low], Y      ; store the value
  INY
  CPY #$00
  BNE LoadDataLoop
  LDA collision_high           ; reached max y, so add one to high ptr to move it up 256 bytes and start y back at 0
  CLC
  ADC #$01
  STA collision_high
  LDA #$24                      ; reset A back to value I want
  INX
  CPX #$03
  BNE LoadDataLoop
Re: NESASM absolute addressing on STA instruction
by on (#187594)
mitch3a wrote:
Definitely heard you all on the problems with putting the blob in the zero page and of course once I loaded my blob, I saw the program crash. Still getting a handle on this so I appreciate your patience. Anyway, finally have it all loading as desired without crashing so I figured I'd post what finally worked for me. I'm sure it's still far from perfect, but figured it might be helpful for the next guy. Thanks again everyone for the quick/informative responses!

Code:
  LDX $00
  LDY $00


These are loading contents into X and Y, respectively, from zero page location $00, which is going to be the first byte of collision_map. You meant to use immediate addressing, ex.:

Code:
ldx #0
ldy #0

...or alternately, if you prefer:

ldx #$00
ldy #$00


Edit: P.S. -- You don't need to cpy #$00 after iny. iny modifies the both the CPU flags N and Z, so you don't need the comparison for comparing against zero here. You can just do iny, bne ....
Re: NESASM absolute addressing on STA instruction
by on (#187595)
werps. Good catch with the $00 vs #$00. fixed it in place. As for the compare. will fix that in my code, but I'll leave it above. Guessing for anyone at my level that this'll help for, the extra logic is worth making it easier to understand.
Re: NESASM absolute addressing on STA instruction
by on (#187597)
mitch3a wrote:
werps. Good catch with the $00 vs #$00. fixed it in place. As for the compare. will fix that in my code, but I'll leave it above. Guessing for anyone at my level that this'll help for, the extra logic is worth making it easier to understand.

Purely an optimisation thing; has nothing to do with fixing a bug. If the explicit compare is helpful for you, keep it. :-)
Re: NESASM absolute addressing on STA instruction
by on (#187598)
As for the rest of the code: it should "function", but it probably won't work right once Y wraps from $FF to $00. Hint: think about collision_low. You're increasing the high byte of the address, but collision_low will still contain whatever address it did prior to the wrap. Think about the implications if, say, collision_map is at $03B4 (or any non-zero low byte of the address).
Re: NESASM absolute addressing on STA instruction
by on (#187599)
koitsu wrote:
You're increasing the high byte of the address, but collision_low will still contain whatever address it did prior to the wrap. Think about the implications if, say, collision_map is at $03B4 (or any non-zero low byte of the address).

Actually, this kind of data transfer loop works just fine and is very common in 6502 ASM (you'd normally just use INC on the high byte of the pointer though, not LDA + CLC + ADC + STA). Look:
Code:
$03B4 + $FF = $04B3
$04B4 + $00 = $04B4 (which is indeed one byte after the previous)

There' will be more penalty cycles for crossing pages though, when compared to page-aligned data transfers, but that's hardly a concern in most cases.
Re: NESASM absolute addressing on STA instruction
by on (#187601)
Also, while there is no convenient way to increment A, there is a convenient instruction for incrementing a byte of RAM directly (INC).
Code:
  LDA collision_high
  CLC
  ADC #$01
  STA collision_high

  ; the following does the same job:

  INC collision_high

Aside from being faster and shorter, the INC instruction does not modify A, and does not modify the carry bit (like ADC does), though it does still modify the zero bit. (However, an increment that should "carry" would have a result of zero anyway, so that flag is sufficient if you need it.)

Edit: sorry, was redundant to tokumaru's post, though maybe a little more explicit.
Re: NESASM absolute addressing on STA instruction
by on (#187602)
Ah yeah, I didn't work all the math out in my head, you're absolutely right. I really ought to just leave this forum by now.