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.

Illustration of an Amiga joystick controlling a sprite

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

  1. Copy the Code: Click the Copy button on the code block above to copy the assembly code to your clipboard.
  2. Open Amiga Playground: Launch Amiga Playground on your Mac and create or open a 68k Assembly document.
  3. Paste & Run: Paste the code into the source editor and press Cmd + R (or click Build & Run).
  4. Control the Sprite: Amiga Playground will compile the source code with vasm and 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

  1. Save the Code: Save the complete code above into a file named joystick_sprite.asm.
  2. Assemble: Open your Terminal, navigate to the folder where you saved the file, and run: vasmm68k_mot -Fhunk -o joystick_sprite joystick_sprite.asm
  3. 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_sprite executable as a hard drive (e.g., DH0:).
  4. Run in Emulator: Boot into Workbench, open the Shell, and run your program by typing joystick_sprite.
  5. 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.