Gameboy Development Forum

Discussion about software development for the old-school Gameboys, ranging from the "Gray brick" to Gameboy Color
(Launched in 2008)

You are not logged in.


#1 2022-05-09 07:38:17

Registered: 2022-03-30
Posts: 12

Jump table

Is this the general idea of a jump table? Is there a better more standard way of doing it?
Im still new to asm and while I knew of the concept im not sure of its implementation.


    ld hl, .jump_table                                            ; point to the jump table
    ld a, [g_player + ENTITY_STATE]                                ; load register a with the player state
    ld b, 0                                                        ; high byte is 0
    ld c, a                                                        ; load low byte with the state id
    add hl, bc                                                    ; each jump table entry is 2 bytes  (jr e8 = 2 bytes, jp n16 = 3 bytes)
    add hl, bc                                                    ; so add the offset 2 times
    ; add hl, bc                                                ; to get the correct jump table entry
    jp hl                                                        ; jump to that entry

    jr .entity_state_idle
    jr .entity_state_walk
    jr .entity_state_idle_w_shield_out
    jr .entity_state_walk_w_shield_out
    jr .entity_state_jump
    jr .entity_state_glide
    jr .entity_state_carry
    jr .entity_state_showing_item
    jr .entity_state_showing_special_item
    jr .entity_state_swim
    jr .entity_state_swim_underwater
    jr .entity_state_swim_push
    jr .entity_state_swim_pull
    jr .entity_state_swim_riding
    jr .entity_state_swim_digging

    ld a, 7
    ld a, 7
    ld a, 7
    jr .entity_state_end
    ld a, 7
    ld a, 7
    ld a, 7
    jr .entity_state_end
    ld a, 7
    ld a, 7
    ld a, 7
    jr .entity_state_end



#2 2022-05-09 11:01:46

Registered: 2008-02-22
Posts: 245

Re: Jump table

There isn't really a standard. If it works (and is reasonably efficient) it's good. You've got the concept down, although there are a couple of thing that could be improved.

You could define ENTITY_STATE to always be an even number, to be able to skip the x2 multiplication step, to save some small amount of space and CPU cycles. The number is already pre-calculated to point to the right table entry. This halves the available number of different states you can store in a byte, but this shouldn't be a problem.

Using jr is problematic in this case because it only allows up to 128 bytes of code for states, or some number below 256 if you allow state handlers to be stored before the calculation code as well. Using jp would solve this, and I can tell from the commented out add hl, bc that you tried this before. But this still wastes one byte per entry. A better way is to store just the address and load it and jump to it.

Another thing is that if you have multiple jump tables, you need to duplicate the code for calculating the target (with your current method). This is also not optimal.

With all those things in mind, I suggest using one of the rst opcodes with code that looks something like this. rst is a one byte version of call, with a fixed target of one of a few places in the bottom of the memory map. With this method, the table contains target addresses and follows directly after the rst opcode. This is because the code is using (what would otherwise be) the return address of the rst to find the table.


        pop     HL      ; Get the return address, which is the base of the table.
        add     A,L     ; Add A to the table base address.
        jr      nc,.nocarry
        inc     H       ; If the add produced a carry, increment H to correct for it.
        ld      L,A

        ; HL now points to one entry in the jump table.
        ld      A,[HL+] ; Load low byte of target address and increment HL.
        ld      H,[HL]  ; Load high byte of target address.
        ld      L,A     ; Load low byte of target address into L.
        jp      HL      ; Off we go!

; Code somewhere else using the jump table.
        ld      A,[g_player + ENTITY_STATE]
        rst     INVOKE_JUMP_TABLE
        dw      .entity_state_idle
        dw      .entity_state_walk
        dw      .entity_state_idle_w_shield_out
        dw      .entity_state_walk_w_shield_out
        dw      .entity_state_jump
        dw      .entity_state_glide
        ; Etc

Another issue is keeping track of the indexes of states, which can be automated somewhat using macros, but I won't cover that here and now.

Blog: Gameboy Genius
"A journey of a thousand miles begins with one small step"
Old Chinese Proverb



#3 2022-05-09 13:21:34

Registered: 2022-03-30
Posts: 12

Re: Jump table

Ahh ty ty for the information.
I havn't actually looked at the rst stuff yet, but i think i get it.
Will have to try some of this out!
TY again :]

Just so im 100% on the vector thing, thats 8 bytes of storage. and the code is 10 bytes, so its basically taking 2 slots up?

I currently automate my state indexes with


RSSET ENTITY_STATE_COUNT                                                    ; player states continuing entities
DEF PLAYER_STATE_IDLE_SHIELD_OUT            RB        1                        ; idle with shield out
DEF PLAYER_STATE_WALK_SHIELD_OUT            RB        1                        ; walk with shield out
DEF PLAYER_STATE_JUMP                        RB        1                        ; jumping
DEF PLAYER_STATE_GLIDE                        RB        1                        ; gliding
DEF PLAYER_STATE_CARRY                        RB        1                        ; carrying
DEF PLAYER_STATE_SHOWING_ITEM                RB        1                        ; showing item
DEF PLAYER_STATE_SHOWING_SPECIAL_ITEM        RB        1                        ; showing item with 2 hands
DEF PLAYER_STATE_SWIM                        RB        1                        ; swimming
DEF PLAYER_STATE_SWIM_UNDERWATER            RB        1                        ; swimming underwater
DEF PLAYER_STATE_PUSH                        RB        1                        ; pushing
DEF PLAYER_STATE_PULL                        RB        1                        ; pullinh
DEF PLAYER_STATE_RIDING                        RB        1                        ; riding
DEF PLAYER_STATE_DIGGING                    RB        1                        ; digging
DEF PLAYER_STATE_COUNT                        RB        0                        ; player state count

im guessing making RB = RW will spread them by 2. although  PLAYER_STATE_COUNT wouldnt be valid, but u know what i mean
I know the RB stuff is more for like structs, but i think it works ok for this too

Last edited by Azenris (2022-05-09 16:04:56)



Board footer

Powered by PunBB
© Copyright 2002–2005 Rickard Andersson