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.
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 .jump_table 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 .entity_state_idle ld a, 7 ld a, 7 ld a, 7 jr .entity_state_end .entity_state_walk ld a, 7 ld a, 7 ld a, 7 jr .entity_state_end .entity_state_idle_w_shield_out ld a, 7 ld a, 7 ld a, 7 jr .entity_state_end .entity_state_walk_w_shield_out .entity_state_jump .entity_state_glide .entity_state_carry .entity_state_showing_item .entity_state_showing_special_item .entity_state_swim .entity_state_swim_underwater .entity_state_swim_push .entity_state_swim_pull .entity_state_swim_riding .entity_state_swim_digging .entity_state_end
Offline
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.
SECTION "RST08",ROM0[$08] INVOKE_JUMP_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. .nocarry 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.
Offline
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 :]
EDIT:
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?
ALSO:
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)
Offline