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

Re: 16 bit array iteration

Post by tokumaru »

instantaphex wrote:Will this have the same effect? Will it expand out into an array of the low bytes and high bytes?
This is just combining 2 separate functionalities of ca65: the .define command creates an identifier that will expand into the list of things every time you use it, while .lobytes and .hibytes will extract only the low or high bytes of the list of values you give them. So yeah, this will create two tables, one with only the low bytes and the other with only the high bytes.
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?
Sounds like you're talking about scopes. Scopes encapsulate symbols so they're not visible to parent scopes, unless you use named scopes and access their children using "::". For example: jsr Video::UpdatePalette will call the subroutine "UpdatePalette" that was defined inside the "Video" scope. Scopes are useful for organization purposes, but also allow you to reuse labels (e.g. if each subroutine has its own scope, all of the can have a label called "Return").
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:
instantaphex wrote:Will this have the same effect? Will it expand out into an array of the low bytes and high bytes?
This is just combining 2 separate functionalities of ca65: the .define command creates an identifier that will expand into the list of things every time you use it, while .lobytes and .hibytes will extract only the low or high bytes of the list of values you give them. So yeah, this will create two tables, one with only the low bytes and the other with only the high bytes.
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?
Sounds like you're talking about scopes. Scopes encapsulate symbols so they're not visible to parent scopes, unless you use named scopes and access their children using "::". For example: jsr Video::UpdatePalette will call the subroutine "UpdatePalette" that was defined inside the "Video" scope. Scopes are useful for organization purposes, but also allow you to reuse labels (e.g. if each subroutine has its own scope, all of the can have a label called "Return").

Ok that makes sense. As far as the scopes are concerned, that isn't quite what I was referring to. I was referring to seeing things like:

Code: Select all

player.animations.walk
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.
I had forgotten that I moved my frame count data outside of my sprite pointer array. You're right I need to pass it in. I've made a couple of updates now so that I can pass in a pointer to an animation and a pointer to a number of frames. This way I can transition between frames based on the state of the player.

Code: Select all

.macro play_animation animation, num_frames, 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() }
    ldx #0
    cmp (num_frames,x)
    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
    tay
	; set current sprite to current animation frame
	lda (animation), y
	sta sprite_ptr + 0
    iny
	lda (animation), y
	sta sprite_ptr + 1
.endmacro
It feels a little clunky to have to use:

Code: Select all

ldx #0
cmp (num_frames,x)
but I didn't see another option.

Now I'm trying to figure out how to register button releases so I can transition in and out of walking and idle states. Right now I'm just storing a single frame of controller state. I assume I probably will want to store 2 frames and compare. If the previous frame's state shows that left has been pressed but the current frame's state shows that it's not pressed, then I can transition from the walking state back to the idle state. Hopefully that will work.
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: 16 bit array iteration

Post by Oziphantom »

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
This is getting more advanced so once you have worked out the above, the next step it to store the attributes as arrays.
So

Code: Select all

Frame_X             .byte $00,$08,$00,$08,$00,$08,$00,$08
Frame_Tile          .byte $32,$33,$34,$35,$36,$37,$38,$39
Frame_Atrribute     .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000
Frame_Y             .byte $00,$00,$08,$08,$00,$00,$08,$08
Then you put Frame in X

Code: Select all

lda Frame_X,x
sta Dest,y
lda Frame_Tile,x
sta Dest+1,y
lda Frame_Attribute,x
sta Dest+2,y
lda Frame_Y,x
sta Dest+3,y
tya
clc
adc #4 ; move to next sprite slot
tay 
; while still frame left, get next frame index and loop
You then stack N animations in the main tables. Then your Walk animation either becomes a list of frames, or if you know you only want a range then you have
Start,End
So you keep a frame index in RAM, load it with Start, inc it each time until it hits End, then reset it to start.

Putting the data stripped in this way is a pain, but if you upgrade to Tass64 (which works on Mac Intel and PPC ) over CA65 you can start to do things like

Code: Select all

My_Animations := (($00,$32,%00000000,$00),($00,$33,%00000000,$08),($08,$34,%00000000,$00),($08,$35,%00000000,$08),($00,$36,%00000000,$00)....)
PlayerWalkEnd = len(My_Animations)
You can add addition animations with 
My_Animations ..= ( anims ) 

Then you do
Frame_X             .byte My_Animations[:0]
Frame_Tile          .byte My_Animations[:1]
Frame_Atrribute     .byte My_Animations[:2]
Frame_Y             .byte My_Animations[:3]

PlayerWalk .byte $04,$00,PlayerWalkEnd ; frames,start,end
Word to the wise.
Don't hard code tile numbers, it is going to cause your a lot of pain latter on when you need to move things, and you will need to move things ;) Also they are not very readable

Code: Select all

kTileIndex .block
    PlayerHead .block
        L = $34
        R = $35
    .bend
    PlayerLegWalk
        f1 .block 
            L = $36
            R = $37
    .bend
.bend
Yes its a lot more typing, but then in 3 months when you need to change an animation how much more sense does
Frame_Tile .byte $32,$33,PlayerHead.(L,R),PlayerLegWalk.f1.(L,R),$37,$38,$39 make?
User avatar
slembcke
Posts: 172
Joined: Fri Nov 24, 2017 2:40 pm
Location: Minnesota

Re: 16 bit array iteration

Post by slembcke »

Oh Interesting. I didn't know about .lobytes/hibytes. That certainly makes striped arrays more usable.
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: 16 bit array iteration

Post by thefox »

It's also possible to duplicate the .lobytes/.hibytes functionality by using macros. This is sometimes useful if you need to do more complicated transforms, or extract some auxiliary data such as the bank byte.

Code: Select all

.macro transform v, func
    .repeat .tcount({v}), i
        func {.mid(i, 1, {v})}
    .endrepeat
.endmacro

.macro lobyte v
    .byte .lobyte(v)
.endmacro

.macro hibyte v
    .byte .hibyte(v)
.endmacro

.macro bank v
    .byte .lobyte(.bank(v))
.endmacro
Usage:

Code: Select all

.define maps map_foo map_bar map_xyzzy
maps_lo: transform {maps}, lobyte
maps_hi: transform {maps}, hibyte
maps_bank: transform {maps}, bank
Note though, that since this operates on tokens you can't have more complex expressions within the .define list itself. This won't work: .define maps map_foo+123 map_bar*2. However, it would be possible to modify the transform macro to support that as well (delimit the parameters by ,), I just wanted to keep it simple.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
Post Reply