Interactive Sprites: Joystick Control
Moving a sprite automatically is cool, but letting the user control it is what makes a game! This tutorial builds on our animation example by adding joystick support. We'll read input from the joystick port to move our sprite around the screen and use the fire button to trigger a simple explosion effect.
How the Amiga Reads Joysticks
The Amiga reads the state of the standard joystick (connected to Port 1) through one of its custom chips and the CIA (Complex Interface Adapter). Specifically, we read direction data from the 16-bit custom chip register JOY1DAT at address $DFF00C (Port 1), and fire button status from bit 7 of CIA register CIAAPRA at address $BFE001.
Because JOY1DAT uses 2-bit quadrature encoding, directional movement is decoded by XORing the raw register bits. We read this register during our vertical blank interrupt 50 times per second (on a PAL Amiga) for smooth, responsive input.
The "Explosion" Effect
How do we make the sprite "explode"? In this example, we'll use a simple but effective trick. We will define two different sets of sprite data: one for the normal ship and another that looks like a burst or explosion. When the fire button is pressed, our interrupt code will simply point the hardware to the explosion sprite data instead of the ship data. This instantly changes the sprite's appearance on screen.
The Complete Code:
;-----------------------------------------------------
; joystick_sprite.asm
; Joystick Controlled Sprite with Explosion
; - Moves with joystick in Port 1
; - "Explodes" on fire button press
;-----------------------------------------------------
CUSTOM equ $DFF000
VPOSR equ $004
DMACON equ $096
INTENA equ $09A
INTREQ equ $09C
JOY1DAT equ $00C ; Joystick 1 Data (Port 1)
SPR0PTH equ $120
SPR0POS equ $140
SPR0CTL equ $142
SPRCOLOR17 equ $1A2
SPRCOLOR18 equ $1A4
SPRCOLOR19 equ $1A6
CIAAPRA equ $BFE001 ; CIA Port A (fire button bit 7)
EXEC_BASE equ $4
LVL3_INT_VECTOR equ $6C
start:
lea CUSTOM,a5
move.l EXEC_BASE,a6
move.l LVL3_INT_VECTOR(a6),old_int_vector
move.w #$C000,INTENA(a5)
.waitvb:
move.w VPOSR(a5),d0
btst #8,d0
beq.s .waitvb
lea vblank_interrupt(pc),a0
move.l a0,LVL3_INT_VECTOR(a6)
lea sprite_ship(pc),a0
move.l a0,SPR0PTH(a5)
move.w #$0f80,SPRCOLOR17(a5)
move.w #$0ff0,SPRCOLOR18(a5)
move.w #$0fff,SPRCOLOR19(a5)
move.w #$8100,DMACON(a5)
move.w #$C020,INTENA(a5)
forever_loop:
btst #6,CIAAPRA ; Left mouse click to exit back to Workbench
bne.s forever_loop
exit:
move.w #$C020,INTENA(a5)
move.l old_int_vector,LVL3_INT_VECTOR(a6)
move.w #$7FFF,DMACON(a5)
rts
vblank_interrupt:
movem.l d0-d2/a0-a1/a5,-(sp)
lea CUSTOM,a5
; --- Read Joystick 1 ---
move.w JOY1DAT(a5),d0
move.w d0,d1
lsr.w #1,d1
eor.w d0,d1
; Vertical Movement
btst #8,d1
beq.s .no_y
btst #9,d0
bne.s .down
subq.w #1,sprite_y
bra.s .no_y
.down:
addq.w #1,sprite_y
.no_y:
; Horizontal Movement
btst #0,d1
beq.s .no_x
btst #1,d0
bne.s .right
subq.w #1,sprite_x
bra.s .no_x
.right:
addq.w #1,sprite_x
.no_x:
; --- Check Fire Button (Port 1 = CIAAPRA bit 7) ---
btst #7,CIAAPRA
bne.s .no_fire
lea sprite_explosion(pc),a0 ; Point to explosion data
move.l a0,SPR0PTH(a5)
bra.s .update_pos
.no_fire:
lea sprite_ship(pc),a0 ; Point back to ship data
move.l a0,SPR0PTH(a5)
.update_pos:
move.w sprite_y,d1
move.w sprite_x,d2
lsl.w #8,d1
add.b d2,d1
move.w d1,SPR0POS(a5)
move.w sprite_y,d1
add.w #16,d1 ; Sprite height is 16 lines
lsl.w #8,d1
add.b d2,d1
move.w d1,SPR0CTL(a5)
move.w #$0020,INTREQ(a5)
move.w #$0020,INTREQ(a5)
movem.l (sp)+,d0-d2/a0-a1/a5
rte
; --- Data Section ---
old_int_vector: dc.l 0
sprite_x: dc.w $88
sprite_y: dc.w $64
sprite_ship:
dc.w $0000,$0000 ; First two words ignored, set by VBlank
dc.w $0180,$0180, $03C0,$03C0
dc.w $07E0,$07E0, $0FF0,$0FF0
dc.w $1FF8,$1FF8, $3FFC,$3FFC
dc.w $7FFE,$7FFE, $FFFF,$FFFF
dc.w $FFFF,$FFFF, $7FFE,$7FFE
dc.w $3FFC,$3FFC, $1FF8,$1FF8
dc.w $0FF0,$0FF0, $07E0,$07E0
dc.w $03C0,$03C0, $0180,$0180
dc.w $0000,$0000
sprite_explosion:
dc.w $0000,$0000 ; Ignored control words
dc.w $1008,$1008, $4892,$2442
dc.w $2442,$4892, $9004,$8221
dc.w $9004,$4118, $27C2,$4118
dc.w $13C8,$8221, $0FF0,$0FF0
dc.w $0FF0,$0FF0, $13C8,$8221
dc.w $27C2,$4118, $9004,$4118
dc.w $9004,$8221, $2442,$4892
dc.w $4892,$2442, $1008,$1008
dc.w $0000,$0000
How to Run in Amiga Playground
- Copy the Code: Click the Copy button on the code block above to copy the assembly code to your clipboard.
- Open Amiga Playground: Launch Amiga Playground on your Mac and create or open a 68k Assembly document.
- Paste & Run: Paste the code into the source editor and press Cmd + R (or click Build & Run).
- Control the Sprite: Amiga Playground will compile the source code with
vasmand run the emulator automatically. Use your joystick or arrow keys to move the sprite, and press the fire button to trigger the explosion!
How to Compile and Run with vasm
- Save the Code: Save the complete code above into a file named
joystick_sprite.asm. - Assemble: Open your Terminal, navigate to the folder where you saved the file, and run:
vasmm68k_mot -Fhunk -o joystick_sprite joystick_sprite.asm - Set up Emulator: Make sure your joystick is enabled in the emulator's input settings (usually for Port 1). Mount the folder containing your new
joystick_spriteexecutable as a hard drive (e.g.,DH0:). - Run in Emulator: Boot into Workbench, open the Shell, and run your program by typing
joystick_sprite. - See the Result: You can now move the sprite with the joystick. Pressing the fire button will change it to an explosion pattern, and releasing it will change it back to the ship. Click the left mouse button to exit.