Assembly Language Fundamentals

Intermediate
Version:
2.0

Sprite Movement

Introduction

Now that we can create and colour detailed sprites, the next step is to bring them to life through movement. On the Tatung Einstein, sprite positions can be easily changed by updating their X and Y coordinates in memory, allowing them to move smoothly across the screen.

In this lesson, we’ll build on the previous example by making our saucer (or smiley) sprite move in four directions using keyboard input. The program uses the ZKSCAN routine to detect key presses and updates the sprite’s position accordingly. A simple delay loop is also introduced to control speed, preventing the sprite from moving too quickly.

This forms the foundation for player-controlled animation, an essential part of any interactive game.

How It Works

The key to sprite movement lies in changing the X and Y coordinates stored for the sprite. The program checks for keyboard input, adjusts the relevant coordinate, and redraws the sprite at the new position.

Here’s how it works:

  1. Sprite attributes store the X and Y positions of each part of the sprite.
  2. The main loop repeatedly calls routines to update and redraw the sprite.
  3. The CheckKeys routine detects which key has been pressed (W, A, S, or D) and updates the position accordingly.
  4. The Delay subroutine creates a short pause between movements, slowing down the animation to a smooth and controllable speed.

Without this delay, the sprite would move too quickly to see individual steps because the Z80 executes instructions far faster than the screen refresh rate.

Full Program

The following code moves a sprite in all four directions using the W, A, S, and D keys. It also demonstrates how a simple delay loop can make movement visually smooth and manageable.

       ORG 256

      ; -----------------------
      ; Enable Graphics Mode II
      ; ----------------------

       ; Set Register 0 values

       LD   A,2
       OUT  (9),A
       LD   A,128
       OUT  (9),A

       ; Set Register 1 values

       LD   A,224
       OUT  (9),A
       LD   A,129
       OUT  (9),A


      ; ---------------------------
      ; Set Foreground / Background
      ; ---------------------------

      LD   A, 240        ; 11110000 (white foreground, black background)
      OUT  (9), A        ; Send to Port 9
      LD   A, 135        ; 128 (bit 7 set) + 7 (register 7)
      OUT  (9), A        ; Send to Port 9

      ; -----------------
      ; Write Blank Tiles
      ; -----------------

      ; Top Third Blank Tile

      LD   A, 0            ; Load zero into A register
      OUT  (9), A          ; Send to Port 9
      LD   A, 64           ; Load 64 into A register
      OUT  (9), A          ; Send to Port 9

      LD A, 0            ; Load 170 into A
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8

      ; Middle Third Blank Tile

      LD   A, 0            ; Load zero into A register
      OUT  (9), A          ; Send to Port 9
      LD   A, 72           ; Load 72 into A register
      OUT  (9), A          ; Send to Port 9

      LD A, 0            ; Load 170 into A
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8

      ; Bottom Third Blank Tile

      LD   A, 0            ; Load zero into A register
      OUT  (9), A          ; Send to Port 9
      LD   A, 80           ; Load 80 into A register
      OUT  (9), A          ; Send to Port 9

      LD A, 0              ; Load 170 into A
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8


      ; --------------------------------
      ; Clear Name Table with Blank Tile
      ; --------------------------------

      LD A, 0            ; Low Byte
      OUT (9), A         ; Send to Port 9
      LD A, 120          ; High Byte
      OUT (9), A         ; Send to Port 9

      LD BC, 768         ; Set Counter for table address space

ClearLoop:
      LD A, 0            ; Load zero into A register
      OUT (8), A         ; Send to Port 8
      DEC BC             ; Decrement BC Counter
      LD A, B            ; Load B into A register
      OR C               ; Bitwise OR to compare
      JR NZ, ClearLoop    ; Jump to start unless BC is zero


       ; ---------------------
       ; Define Sprite Pattern
       ; ---------------------

       LD A,0
       OUT (9),A
       LD A,88
       OUT (9),A

       LD A,%00001111 : OUT (8),A
       LD A,%00011111 : OUT (8),A
       LD A,%00111000 : OUT (8),A
       LD A,%11111000 : OUT (8),A
       LD A,%00111111 : OUT (8),A
       LD A,%00011111 : OUT (8),A
       LD A,%00001111 : OUT (8),A
       LD A,%00000000 : OUT (8),A

       LD A,%11110000 : OUT (8),A
       LD A,%11111000 : OUT (8),A
       LD A,%00011100 : OUT (8),A
       LD A,%00011111 : OUT (8),A
       LD A,%11111100 : OUT (8),A
       LD A,%11111000 : OUT (8),A
       LD A,%11110000 : OUT (8),A
       LD A,%00000000 : OUT (8),A

       LD A,%00000000 : OUT (8),A
       LD A,%00000000 : OUT (8),A
       LD A,%00000000 : OUT (8),A
       LD A,%00000111 : OUT (8),A
       LD A,%11000000 : OUT (8),A
       LD A,%00111111 : OUT (8),A
       LD A,%00011111 : OUT (8),A
       LD A,%00001111 : OUT (8),A

       LD A,%00000000 : OUT (8),A
       LD A,%00000000 : OUT (8),A
       LD A,%00000000 : OUT (8),A
       LD A,%11100000 : OUT (8),A
       LD A,%00000011 : OUT (8),A
       LD A,%11111100 : OUT (8),A
       LD A,%11111000 : OUT (8),A
       LD A,%11110000 : OUT (8),A

       ; ------------------------
       ; Define Sprite Attributes
       ; ------------------------

UpdateSprite:

       LD A,0
       OUT (9),A
       LD A,123
       OUT (9),A

       ; Sprite 0

       LD A,(SpriteY)
       OUT (8),A
       LD A,(SpriteX)
       OUT (8),A
       LD A,0
       OUT (8),A
       LD A,15
       OUT (8),A

       ; Sprite 1

       LD A,(SpriteY)
       OUT (8),A
       LD A,(SpriteX)
       ADD A, 8
       OUT (8),A
       LD A,1
       OUT (8),A
       LD A,15
       OUT (8),A

       ; Sprite 2

       LD A,(SpriteY)
       OUT (8),A
       LD A,(SpriteX)
       OUT (8),A
       LD A,2
       OUT (8),A
       LD A,14
       OUT (8),A

       ; Sprite 3

       LD A,(SpriteY)
       OUT (8),A
       LD A,(SpriteX)
       ADD A, 8
       OUT (8),A
       LD A,3
       OUT (8),A
       LD A,14
       OUT (8),A


CheckKeys:
      CALL Delay

      RST 8
      DEFB 181

      CP 0          ; No Key Pressed
      JR Z, MainLoop

      ; W Pressed
      CP 87
      JR Z, MoveUp

      ; A Pressed
      CP 65
      JR Z, MoveLeft

      ; S Pressed
      CP 83
      JR Z, MoveDown

      ; D Pressed
      CP 68
      JR Z, MoveRight

      RET


MoveUp:
      LD A, (SpriteY)
      DEC A
      LD (SpriteY), A
      JR MainLoop

MoveLeft:
      LD A, (SpriteX)
      DEC A
      LD (SpriteX), A
      JR MainLoop

MoveDown:
      LD A, (SpriteY)
      INC A
      LD (SpriteY), A
      JR MainLoop

MoveRight:
      LD A, (SpriteX)
      INC A
      LD (SpriteX), A
      JR MainLoop

Delay:
   LD BC,3000      ; adjust this number for smooth speed
DLoop:
   DEC BC
   LD A,B
   OR C
   JR NZ,DLoop
   RET


MainLoop:

      CALL UpdateSprite
      CALL CheckKeys
      JR MainLoop

SpriteX:
      DEFB 100
SpriteY:
      DEFB 120


       JP $            ; JP Idefinitely

Result

When you run this program, the sprite can be moved up, down, left, or right using the W, A, S, and D keys. The delay routine ensures smooth, visible movement at a controlled speed.

Try adjusting the value in the Delay subroutine (LD BC,3000) to make the sprite move faster or slower.

Why This Technique Is Useful

The Tatung Einstein’s Z80 processor runs extremely quickly compared to the visible frame rate, so without delay control, sprites would jump instantly across the screen. By using a delay loop, you can finely tune the movement speed, keeping gameplay smooth and natural.

The lesson also introduces an important concept about keyboard handling. The ZKSCAN call is convenient but limited — it can only register one key press at a time. This means you can’t press two movement keys together (for diagonal movement, for instance).

A more advanced approach, called keyboard matrix scanning, overcomes this limitation. It allows multiple keys to be detected simultaneously and will be explored in a later lesson.

Summary

Previous Module
Next Module