HYDRA Pacman demo code question.
Coolguy
Posts: 26
I've a question on the below code. This is from HYDRA book page 538. It is reference to pacman_tile_demo_002.spin. What is this code do?
tile_map2[noparse][[/noparse]ghost_tx + ghost_ty << 4]
I don't understand what is it do?
Below is the whole PUB
PUB Start
' This is the first entry point the system will see when the PChip starts,
' execution ALWAYS starts on the first PUB in the source code for
' the top level file
' star the game pad driver
game_pad.start
' initialize all vars
ghost_tx· := 4··················· ' set tile location of ghost to center of screen
ghost_ty· := 6··················· ' about (4,6)
ghost_dir := GHOST_TILE_LEFT····· ' start ghost out with eyes to left
······················
' set up tile and sprite engine data before starting it, so no ugly startup
' points ptrs to actual memory storage for tile engine
tile_map_base_ptr_parm······· := @tile_map2
tile_bitmaps_base_ptr_parm··· := @tile_bitmaps
tile_palettes_base_ptr_parm·· := @palette_map
' these control the sprite engine, all 0 for now, no sprites
tile_map_sprite_cntrl_parm··· := $00_00 ' 0 sprites, tile map set to 0=16 tiles wide, 1=32 tiles, 2=64 tiles, etc.
tile_sprite_tbl_base_ptr_parm := 0
tile_status_bits_parm········ := 0
' launch a COG with ASM video driver
gfx.start(@tile_map_base_ptr_parm)
' enter main event loop...
repeat while 1
·
· ' main even loop code here...
· ' draw the ghost tile by overwriting the screen tile word destructively, both
· ' the palette index and the tile index
· ' note the add of "ghost_dir", this modifies the tiles, so we can see the ghost
· ' turn right, left, up, down as he moves.
· ' test if player is trying to move ghost, if so write a blank tile at current location
· ' overwriting tile index and palette
· ' then move ghost
· if (game_pad.button(NES0_RIGHT))
··· ' clear tile
··· tile_map2[noparse][[/noparse]ghost_tx + ghost_ty << 4] := $02_00·
··· ' move ghost
··· ghost_tx++
··· ' set eye direction, which affects tile selected during rendering.
··· ghost_dir := GHOST_TILE_RIGHT
· if (game_pad.button(NES0_LEFT))
··· ' clear tile
··· tile_map2[noparse][[/noparse]ghost_tx + ghost_ty << 4] := $02_00·
··· ' move ghost
··· ghost_tx--
··· ' set eye direction, which affects tile selected during rendering.
··· ghost_dir := GHOST_TILE_LEFT
· if (game_pad.button(NES0_UP))
··· ' clear tile
··· tile_map2[noparse][[/noparse]ghost_tx + ghost_ty << 4] := $02_00·
··· ' move ghost
··· ghost_ty--
··· ' set eye direction, which affects tile selected during rendering.
··· ghost_dir := GHOST_TILE_UP
· if (game_pad.button(NES0_DOWN))
··· ' clear tile
··· tile_map2[noparse][[/noparse]ghost_tx + ghost_ty << 4] := $02_00·
··· ' move ghost
··· ghost_ty++
··· ' set eye direction, which affects tile selected during rendering.
··· ghost_dir := GHOST_TILE_DOWN
· ' bounds check ghost, keep it on screen, use SPIN operators for bounds testing
· ghost_tx <#= 9 ' if (ghost_tx > 9) then ghost_tx = 9
· ghost_tx #>= 0 ' if (ghost_tx < 0) then ghost_tx = 0·
· ghost_ty <#= 11 ' if (ghost_ty > 11) then ghost_tx = 11
· ghost_ty #>= 0· ' if (ghost_ty < 0) then ghost_ty = 0·
· ' draw ghost down
· tile_map2[noparse][[/noparse]ghost_tx + ghost_ty << 4] := $02_04 + ghost_dir
· ' delay a moment, otherwise everything will blur!
· repeat 20_000
···
· ' return back to repeat main event loop...
' parent COG will terminate now...if it gets to this point
tile_map2[noparse][[/noparse]ghost_tx + ghost_ty << 4]
I don't understand what is it do?
Below is the whole PUB
PUB Start
' This is the first entry point the system will see when the PChip starts,
' execution ALWAYS starts on the first PUB in the source code for
' the top level file
' star the game pad driver
game_pad.start
' initialize all vars
ghost_tx· := 4··················· ' set tile location of ghost to center of screen
ghost_ty· := 6··················· ' about (4,6)
ghost_dir := GHOST_TILE_LEFT····· ' start ghost out with eyes to left
······················
' set up tile and sprite engine data before starting it, so no ugly startup
' points ptrs to actual memory storage for tile engine
tile_map_base_ptr_parm······· := @tile_map2
tile_bitmaps_base_ptr_parm··· := @tile_bitmaps
tile_palettes_base_ptr_parm·· := @palette_map
' these control the sprite engine, all 0 for now, no sprites
tile_map_sprite_cntrl_parm··· := $00_00 ' 0 sprites, tile map set to 0=16 tiles wide, 1=32 tiles, 2=64 tiles, etc.
tile_sprite_tbl_base_ptr_parm := 0
tile_status_bits_parm········ := 0
' launch a COG with ASM video driver
gfx.start(@tile_map_base_ptr_parm)
' enter main event loop...
repeat while 1
·
· ' main even loop code here...
· ' draw the ghost tile by overwriting the screen tile word destructively, both
· ' the palette index and the tile index
· ' note the add of "ghost_dir", this modifies the tiles, so we can see the ghost
· ' turn right, left, up, down as he moves.
· ' test if player is trying to move ghost, if so write a blank tile at current location
· ' overwriting tile index and palette
· ' then move ghost
· if (game_pad.button(NES0_RIGHT))
··· ' clear tile
··· tile_map2[noparse][[/noparse]ghost_tx + ghost_ty << 4] := $02_00·
··· ' move ghost
··· ghost_tx++
··· ' set eye direction, which affects tile selected during rendering.
··· ghost_dir := GHOST_TILE_RIGHT
· if (game_pad.button(NES0_LEFT))
··· ' clear tile
··· tile_map2[noparse][[/noparse]ghost_tx + ghost_ty << 4] := $02_00·
··· ' move ghost
··· ghost_tx--
··· ' set eye direction, which affects tile selected during rendering.
··· ghost_dir := GHOST_TILE_LEFT
· if (game_pad.button(NES0_UP))
··· ' clear tile
··· tile_map2[noparse][[/noparse]ghost_tx + ghost_ty << 4] := $02_00·
··· ' move ghost
··· ghost_ty--
··· ' set eye direction, which affects tile selected during rendering.
··· ghost_dir := GHOST_TILE_UP
· if (game_pad.button(NES0_DOWN))
··· ' clear tile
··· tile_map2[noparse][[/noparse]ghost_tx + ghost_ty << 4] := $02_00·
··· ' move ghost
··· ghost_ty++
··· ' set eye direction, which affects tile selected during rendering.
··· ghost_dir := GHOST_TILE_DOWN
· ' bounds check ghost, keep it on screen, use SPIN operators for bounds testing
· ghost_tx <#= 9 ' if (ghost_tx > 9) then ghost_tx = 9
· ghost_tx #>= 0 ' if (ghost_tx < 0) then ghost_tx = 0·
· ghost_ty <#= 11 ' if (ghost_ty > 11) then ghost_tx = 11
· ghost_ty #>= 0· ' if (ghost_ty < 0) then ghost_ty = 0·
· ' draw ghost down
· tile_map2[noparse][[/noparse]ghost_tx + ghost_ty << 4] := $02_04 + ghost_dir
· ' delay a moment, otherwise everything will blur!
· repeat 20_000
···
· ' return back to repeat main event loop...
' parent COG will terminate now...if it gets to this point
Comments
Spin doesn't have 2 dimensional arrays, so you have to use a one dimensional array. You have to multiply the y coordinate by the width of the array. In this case the width must be 16. So you'd do this:
tile_map2[noparse][[/noparse]ghost_tx + ghost_ty*16]
However, a trick of doing a multiply by 16 quickly is to shift a value 4 binary places to the left.
00000001 binary = 1 in decimal.
00010000 binary = 16 in decimal.
In spin << is the left shift operator. So
tile_map2[noparse][[/noparse]ghost_tx + ghost_ty << 4] is the same as tile_map2[noparse][[/noparse]ghost_tx + ghost_ty*16], but possibly a little faster.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Help to build the Propeller wiki - propeller.wikispaces.com
Prop Room Robotics - my web store for Roomba spare parts in the UK
WOW! I understand this.
tile_map2[noparse][[/noparse]ghost_tx + ghost_ty*16]
But the rotate left trick just throw me off. Thanks now.
Coolguy
50 = 32 + 16 + 2, thus x*(50) = x*(32+16+2) = 32*x + 16*x + 2*x = x << 5 + x << 4 + x << 1
So on a microprocessor with no multiply that at least has a shift operator, or better yet a barrell roll operator you can do the multiply in 3 instructions basically.
Now in spin, this may work against you since its so slow compared to asm, the act of running the three different shift adds might be slower then just saying x*50, since once the i-code finally gets to the multiply its going to be pretty quick, but you can always try this technique and for exact powers of 2 its definitely faster, but if you have to do two lines of code, the overhead of the spin interpreter might kill the amount of actual "work" you save with the trick...
Andre'