16 bit array iteration

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
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

16 bit array iteration

Post by instantaphex »

I'm working on a simple sprite animation system and I'm not sure the best way to accomplish it. I am storing my meta sprite frame data like this:

Code: Select all

player_walking_1:
	.byte $00,$32,%00000000,$00
	.byte $00,$33,%00000000,$08
	.byte $08,$34,%00000000,$00
	.byte $08,$35,%00000000,$08

player_walking_2:
	.byte $00,$36,%00000000,$00
	.byte $00,$37,%00000000,$08
	.byte $08,$38,%00000000,$00
	.byte $08,$39,%00000000,$08

 etc...

player_walk_animation:
  .byte $04 ; number of frames
  .word player_walking_1, player_walking_2, player_walking_3, player_walking_4
And I'm using a macro that gets called on a timer set for every half second:

Code: Select all

.macro play_animation animation, counter, sprite_ptr
    ; load sprite frame counter
   ldx counter
    ; increase x to put offset at the begginning of frame data
    inx
    ; set current sprite to current animation frame
    lda animation, x
    sta sprite_ptr + 0
    inx 
    lda animation, x
    sta sprite_ptr + 1

    ; get number of frames and store it in temp1
    lda animation
    sta temp1

    ; check if frame counter is equal to number of frames
    ldx counter
    cpx temp1
    beq reset_counter
    ; advance frame counter if counter < number of frames
    inx
    inx
    stx counter
    jmp callback_done
reset_counter:
    lda #$00
    sta counter
callback_done:
.endmacro
My problem is that my array of metasprite frames is an array of 16 bit addresses. Because of that, indexing into it means that I have to increment my counter variable twice to get to the next slot. Because of that my animation data needs an increased frame count in order to work correctly. As I've defined it above, my walk animation won't work, I need to actually store the number of frames as $06 to make it work. How do people usually solve this type of problem?
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Re: 16 bit array iteration

Post by Kasumi »

asl a essentially multiples by two.

Code: Select all

lda counter;The current animation frame
clc
adc #1;Add one to it to advance the frame
cmp animation;compare to the total number of animation frames
bcc skipreset;If it's less than that, we don't need to set it to zero
lda #0;Otherwise we do
skipreset:
sta counter;Store either the new frame (old frame + 1) or zero if we looped
;the current frame number is in A. After all, we just stored it
asl a;now the current frame number *2 is in A
;Incidentally, the number of bytes after player_walk_animation-1 we need to skip
;Minus one because the frame number is the first byte
tax;Transfer that to X
inx;Because we also need to skip the frame count. 
lda animation,x
sta sprite_ptr + 0
inx
lda animation,x
sta sprite_ptr + 1

jsr drawMetasprite;Using the address in the sprite_ptr RAM.
The way to think about this sort of thing is "What information needs to be kept across game frames?" The current frame of animation for the character needs to be stored. So that's counter. How far you index doesn't need to be stored or kept track of at all. The function to draw the metasprites don't even care, it just wants sprite_ptr + 0 and sprite_ptr + 1.

Just transform your long term RAM into short term RAM when you need to. Edit: I guess the primary difference between this approach and yours is this doesn't try to store where we are in the animation data. That's only needed short term. So this stores which frame we're on (needed long term) and we get where we are in the animation data from that every frame on an as needed basis.
Last edited by Kasumi on Tue Feb 27, 2018 9:46 pm, edited 1 time in total.
User avatar
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

Re: 16 bit array iteration

Post by instantaphex »

I think I understand. Maybe this isn't important, but why increment the frame before loading the pointer? Won't that skip the first frame?
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Re: 16 bit array iteration

Post by Kasumi »

Maybe this isn't important, but why increment the frame before loading the pointer? Won't that skip the first frame?
It would assuming you started the animation at zero. (A way to avoid this would be to set counter to 255 rather than 0 to start an animation.) I thought yours did the same, but you were inx-ing to skip the frame count, I guess.

Edit: As for why, it avoids multiple loads of counter.
Last edited by Kasumi on Tue Feb 27, 2018 9:58 pm, edited 1 time in total.
User avatar
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

Re: 16 bit array iteration

Post by instantaphex »

Kasumi wrote:
Maybe this isn't important, but why increment the frame before loading the pointer? Won't that skip the first frame?
It would assuming you started the animation at zero. (A way to avoid this would be to set counter to 255 to start an animation.) I thought yours did the same, but you were inx-ing to skip the frame count, I guess.

Edit: As for why, it avoids multiple loads of counter.
I guess that's a fair assumption considering I had my frame count listed as $04 as opposed to $03. I actually had it at $06 before I pasted it in here because that is how I was able to make it work with my current code.
User avatar
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

Re: 16 bit array iteration

Post by instantaphex »

Ok I think I've got it worked out now. I did a couple of things differently. First of all I ditched the idea of keeping the number of frames at the beginning of the frame data. That wasn't a great idea to begin with and just made the code weird. Second, I used your approach and I now I'm understanding what you meant with the asl a comment, which was the actual answer to my question of how to iterate over a 16 bit array. Essentially, multiplying the iterator by 2 allows you keep it local and removes the need that I had for having the counter variable play double duty keeping track of the current frame as well as where I was in the data.

Code: Select all

.macro play_animation animation, counter, sprite_ptr
    ; load sprite frame counter
    lda counter
    ; advance frame (initially $ff and rolls over to 0)
    clc
    adc #1
    ; if (current_frame == num frames) { reset() }
    cmp player_walk_num_frames
    bcc skip_reset
    lda #0
skip_reset:
    ; store frame + 1
    sta counter
    ; multiply a by 2 in order to get correct byte offset
    asl a
    ; transfer a to x for Absolute,X addressing mode
    tax
    ; set current sprite to current animation frame
    lda animation, x
    sta sprite_ptr + 0
    inx 
    lda animation, x
    sta sprite_ptr + 1
.endmacro
I really appreciate you taking the time yet again to fix my noob code. Programming in assembly is really putting me at the outer limits of my coginitive abilities.
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Re: 16 bit array iteration

Post by Kasumi »

No problem, and it seems you're really getting it!

One other suggestion: You can maybe add the frame count as a parameter to your macro which would make it more fully generic, I think.
User avatar
slembcke
Posts: 172
Joined: Fri Nov 24, 2017 2:40 pm
Location: Minnesota

Re: 16 bit array iteration

Post by slembcke »

As a random aside. This:

Code: Select all

    lda animation, x
    sta sprite_ptr + 0
    inx
    lda animation, x
    sta sprite_ptr + 1
Can become this:

Code: Select all

    lda animation + 0, x
    sta sprite_ptr + 0
    lda animation + 1, x
    sta sprite_ptr + 1
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: 16 bit array iteration

Post by rainwarrior »

There's also striped arrays, where you put the two bytes of each entry in a separate 8-bit array. This skips the need to use ASL, and increases your maximum array size to 256, if you need it.

Code: Select all

array0: .byte <ptr1, <ptr2, <ptr3
array1: .byte >ptr1, >ptr2, >ptr3

lda array0, X
sta temp+0
lda array1, X
sta temp+1
If you're typing this by hand it's a bit tedious to do everything twice with different prefixes, but if you're using an animation tool to generate your sprite information it's more or less trivial to output striped .byte arrays instead of .word arrays. You can also fudge it with macros.

Skipping the ASL also means you can do everything in X to begin with, and INX is faster and smaller than CLC, ADC #1, and there's a CPX instead of CMP, etc.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: 16 bit array iteration

Post by tokumaru »

In ca65 you can easily create striped arrays using .define, .lobytes, and .hibytes:

Code: Select all

  .define Things Thing0, Thing1, Thing2, Thing3

ThingsLow:
  .lobytes Things

ThingsHigh:
  .hibytes Things
User avatar
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

Re: 16 bit array iteration

Post by instantaphex »

Kasumi wrote:No problem, and it seems you're really getting it!

One other suggestion: You can maybe add the frame count as a parameter to your macro which would make it more fully generic, I think.
It's coming along ok I think. Work has been busy so I only have relatively small windows of time to mess around with it.

I originally had the frame count being passed in as a parameter but I though it may be easier to use a more data driven approach. This way my player update can be consistently calling the play_animation macro without having to know any of the specifics of the animation. I may have a walk animation that's two frames, an attack animation thats 3 frames, etc... if I have to pass that in as a parameter I'll need to have special cases for each state transition.
User avatar
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

Re: 16 bit array iteration

Post by instantaphex »

slembcke wrote:As a random aside. This:

Code: Select all

    lda animation, x
    sta sprite_ptr + 0
    inx
    lda animation, x
    sta sprite_ptr + 1
Can become this:

Code: Select all

    lda animation + 0, x
    sta sprite_ptr + 0
    lda animation + 1, x
    sta sprite_ptr + 1
Oh cool! I'm gonna use this.
User avatar
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

Re: 16 bit array iteration

Post by instantaphex »

rainwarrior wrote:There's also striped arrays, where you put the two bytes of each entry in a separate 8-bit array. This skips the need to use ASL, and increases your maximum array size to 256, if you need it.

Code: Select all

array0: .byte <ptr1, <ptr2, <ptr3
array1: .byte >ptr1, >ptr2, >ptr3

lda array0, X
sta temp+0
lda array1, X
sta temp+1
If you're typing this by hand it's a bit tedious to do everything twice with different prefixes, but if you're using an animation tool to generate your sprite information it's more or less trivial to output striped .byte arrays instead of .word arrays. You can also fudge it with macros.

Skipping the ASL also means you can do everything in X to begin with, and INX is faster and smaller than CLC, ADC #1, and there's a CPX instead of CMP, etc.
Interesting. I hadn't thought of doing it that way. That certainly seems like it would be annoying to do by hand but maybe not prohibitively so. I'm developing on a macbook pro so tools are a bit hard to come by as everything seems to be windows only. Even the tools that I can get to run in wine are just really buggy and more frustrating than doing it by hand. I keep thinking maybe I should invest some time in developing my own tools but honestly in the time it would take to do that, I'm pretty sure I would lose interest and move onto other things.


With that said I may make a couple of different macros and try out this approach as well now that I understand the term "striped arrays". I had read that in the nesdev wiki several times but was never really sure what it meant.
User avatar
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

Re: 16 bit array iteration

Post by instantaphex »

tokumaru wrote:In ca65 you can easily create striped arrays using .define, .lobytes, and .hibytes:

Code: Select all

  .define Things Thing0, Thing1, Thing2, Thing3

ThingsLow:
  .lobytes Things

ThingsHigh:
  .hibytes Things
Will this have the same effect? Will it expand out into an array of the low bytes and high bytes? If so that is awesome. I'm really liking ca65 so far. The documentation just leaves me scratching my head quite a bit. I saw the other day that it had structs and got really excited but I couldn't for the life of me get the assembler to stop complaining at me when I tried to use it.

I've noticed some people using some "dot" type notation in their code, is this just by convention or is there some syntactic sugar for that type of thing in ca65?
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: 16 bit array iteration

Post by rainwarrior »

The . begins all control commands in ca65. See here: http://cc65.github.io/doc/ca65.html

If you .define a comma separated list, .lobytes is like .byte with < before every element in that list, and .hibytes does the same but with >. I don't find it's very usable for longer lists, but YMMV, and there's a bunch of other alternatives.
Post Reply