In this section I explain a program which demonstrates the use of the joystick routines and how to display sprites. You can control a dot with the joystick. The dot changes into an arrow and moves if you move the joystick. By pressing fire you can double the size of the dot/arrow. This program is useful if you want to test a joystick, for example after some contact cleaning.
This program uses three new routines. The first one called
waitvsync waits for the next VSYNC. I use it, because I only want
to check for joystick movement once per frame. The routine
getjoystick tests the position of one joystick. The number which
joystick to test has to be put into R1. The outputs of getjoystick
are 1/0/0FFh in register R2 for X axis and R3 for Y axis. All numbers are in
2's complement, so 0FFh is really a -1. If the button is pressed, F0 is set.
This output is direct usable for changing positions, simply add R2/R3 to the
X/Y position.
If your sprite has a front side which always points into the direction it
moves, you can use decodejoystick. It takes the offset data in
R2/R3 and converts them into a direction in R1. This direction can be used to
change the shape of the sprite.
Before I start to explain the program in details, some general remarks.
All VDC accesses are done via tables. The program uses some variables in
internal RAM, they are prefixed with iram_. All position data is
first changed in internal RAM and then transferred into the sprite
registers. There are no checks to make sure that the position is still on
the screen. I don't explain every line of code here, only the main loop.
This starts at the beginning of the main loop, I simply call
getjoystick, test for fire, call decodejoystick and
initialise my table pointer.
loop
call waitvsync ; execute only once per frame
mov r1,#0 ; joystick 0
call getjoystick ; get offsets
; test, if fire
mov r1,#iram_colctrl
mov a,#col_spr_white | spr_double
jf0 firepressed ; fire ?
mov a,#col_spr_white
firepressed
mov @r1,a ; store color/control
call decodejoystick ; get direction from offsets
call extramenable ; enable extram
mov r0,#07Fh ; start of table
Before I can test if the shape has changed, I have to test for the neutral
position, because decodejoystick maps neutral to right. I map the
neutral position to 8 if the X and Y offsets are both 0. Then I can test if the
shape I need is already set.
; test, if joystick is in neutral position
mov a,r2 ; x-offset
jnz shapetest ; left/right
mov a,r3 ; y-offset
jnz shapetest ; up/down
mov r1,#8 ; shape: neutral
shapetest
; test, if shape has change since last frame
mov a,r1 ; we need r1 as pointer
mov r7,a ; so put contents into r7
mov r1,#iram_shape ; last shape
mov a,@r1 ; get it
xrl a,r7 ; compare
jz setpos ; no need to set shape,
; skip that part
Now I set the new shape, which number I have to use is stored in R7. This part is skipped completely, if the shape is already set correctly. In a real program the part which copies the data should be put into a separate page together with the data, because the 8048 can only access data in ROM which is in the same page as the code. To maximise the space usable for shapes, the only code in the same page should be the one which copies the data. But this is only a small example, everything fits into one page, so this is not really necessary. Look at the program from the chapter about collision checks to see how to handle this more correctly. When creating shape data you should know that the displayed data is mirrored by the VDC, bit 0 is displayed left.
; set new value of iram_shape
mov a,r7 ; the number of the shape
mov @r1,a ; put into iram_shape
; init table to copy shape data
mov a,#8 ; copy 8 bytes
movx @r0,a
dec r0
mov a,#vdc_spr0_shape
movx @r0,a
dec r0
; now copy the data
mov a,r7 ; number of shape
rl a
rl a
rl a ; 3*rl = a*8
add a,#spritedata & 0FFh
mov r1,a ; start of shape data
mov r7,#8 ; 8 bytes
copyspriteloop
mov a,r1
movp a,@a ; get byte
movx @r0,a ; store in table
dec r0
inc r1
djnz r7,copyspriteloop
Now I add the offsets to the old position. The Y position is no problem. But the X position is 9 bit wide, so I have to use the carry flag. Another problem is that the X offset is only 8 bit wide. I am dealing with signed numbers in 2's complement, so to expand the X offset to 9 bits I have to reuse bit 7 as bit 8. Because the X offset is only +1/0/-1, bits 1-7 are always the same, so I can reuse bit 1 as bit 8.
setpos
; adjust sprite positions in iram
; y is simple
mov r1,#iram_y ; y position
mov a,@r1 ; get it
add a,r3 ; add offset
mov @r1,a ; store y position
; x is a 9 bit add using carry, we need to
; expand r2 to 9 bit also
mov r1,#iram_xl ; low byte of x
mov a,@r1 ; get it
add a,r2 ; add offset, sets
; carry if necessary
mov @r1,a ; store low byte of x
mov r1,#iram_xh ; high bit of x
mov a,@r1 ; get it
addc a,#0 ; add the carry
mov r7,a ; we need this later
mov a,r2 ; get offset
rr a ; reuse bit 1 of offset
; as bit 8. we need it,
; because we are dealing with
; 2s complement if R2=-1=01ffh
add a,r7 ; add result from above to
; bit 8 of offset
anl a,#001h ; we only need 1 bit
mov @r1,a ; store it as high bit
The only thing that is left to do is to put the sprite position into the VDC. For this I use the table routine in the VSYNC. First I have to prepare the table. The Y position can be put directly into the table. The X position is 9 bit wide, but this time the lowest bit is separated from the rest. In internal RAM the highest bit is separated from the rest. So I have to re-split the X position and combine the lowest bit with the color/control value stored at the beginning of the main loop when testing for fire.
; prepare table for sprite positions
mov a,#3 ; copy 3 bytes
movx @r0,a
dec r0
mov a,#vdc_spr0_ctrl
movx @r0,a
dec r0
; set sprite positions from iram using table
mov r1,#iram_y ; y position
mov a,@r1 ; get it
movx @r0,a ; put it into table
dec r0
; x position: recombine xh and xl and split
; it into 8-1/0
mov r1,#iram_xh ; highest bit of x position
mov a,@r1
rrc a ; highest bit of sprite_x
; into carry
mov r1,#iram_xl ; low byte of x position
mov a,@r1
rrc a ; lowest bit into carry,
; highest bit into 7
movx @r0,a ; put bit 8-1 of sprite_x into
; sprite control 1
dec r0
mov a,#0 ; we only need carry (lowest
; bit of sprite_x)
rlc a ; lowest bit of sprite_x
; into bit 0
mov r7,a ; store in r7
mov r1,#iram_colctrl; color/control
mov a,@r1 ; get it
orl a,r7 ; put it together
movx @r0,a ; put it into sprite control 2
; via table
dec r0
call tableend ; thats all
jmp loop