NESASM absolute addressing on STA instruction

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

User avatar
mitch3a
Posts: 50
Joined: Mon Jan 23, 2017 8:08 pm
Location: Boston, MA
Contact:

NESASM absolute addressing on STA instruction

Post by mitch3a »

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: Select all

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
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: NESASM absolute addressing on STA instruction

Post by rainwarrior »

1. NESASM has nonstandard syntax for indirect addressing. Use square brackets instead:

Code: Select all

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.)
User avatar
mitch3a
Posts: 50
Joined: Mon Jan 23, 2017 8:08 pm
Location: Boston, MA
Contact:

Re: NESASM absolute addressing on STA instruction

Post by mitch3a »

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!
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: NESASM absolute addressing on STA instruction

Post by tokumaru »

As a quick fix, try swapping the position of the pointer and the array in memory:

Code: Select all

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).
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: NESASM absolute addressing on STA instruction

Post by rainwarrior »

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!)
User avatar
mitch3a
Posts: 50
Joined: Mon Jan 23, 2017 8:08 pm
Location: Boston, MA
Contact:

Re: NESASM absolute addressing on STA instruction

Post by mitch3a »

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
Last edited by mitch3a on Mon Jan 23, 2017 8:51 pm, edited 1 time in total.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: NESASM absolute addressing on STA instruction

Post by koitsu »

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: Select all

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: Select all

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: Select all

  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: Select all

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.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: NESASM absolute addressing on STA instruction

Post by tokumaru »

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.
Also thanks for the pro-tip on moving that giant blob. Will do.
:wink:
User avatar
mitch3a
Posts: 50
Joined: Mon Jan 23, 2017 8:08 pm
Location: Boston, MA
Contact:

Re: NESASM absolute addressing on STA instruction

Post by mitch3a »

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: Select all

  .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
Last edited by mitch3a on Mon Jan 23, 2017 9:51 pm, edited 1 time in total.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: NESASM absolute addressing on STA instruction

Post by koitsu »

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: Select all

  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: Select all

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 ....
User avatar
mitch3a
Posts: 50
Joined: Mon Jan 23, 2017 8:08 pm
Location: Boston, MA
Contact:

Re: NESASM absolute addressing on STA instruction

Post by mitch3a »

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.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: NESASM absolute addressing on STA instruction

Post by koitsu »

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. :-)
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: NESASM absolute addressing on STA instruction

Post by koitsu »

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).
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: NESASM absolute addressing on STA instruction

Post by tokumaru »

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: Select all

$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.
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: NESASM absolute addressing on STA instruction

Post by rainwarrior »

Also, while there is no convenient way to increment A, there is a convenient instruction for incrementing a byte of RAM directly (INC).

Code: Select all

  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.
Post Reply