Assembly Language Fundamentals

Intermediate
Version:
2.0

Sprite Projectiles

Introduction

In the previous lesson, we brought our sprite to life with simple animation. Now that the character can walk and look animated, it’s time to let it interact with the environment — by firing projectiles.

Projectiles form the basis of many action games. Whether it’s a fireball, bullet, or energy blast, the concept is the same — an object that moves independently across the screen after being launched by the player.

In this lesson, we’ll build a system that allows our sprite to shoot a projectile whenever the space bar is pressed. The projectile will travel across the screen until it goes off-screen, where it will deactivate automatically.

This introduces important ideas such as independent object motion, activation flags, and basic collision principles.

How It Works

The projectile system uses similar logic to the moving sprite, but with its own variables and behaviour. The main difference is that projectiles move automatically once fired rather than responding directly to key presses.

Here’s what happens step by step:

A Projectile Sprite Is Defined

A new sprite pattern (for example, a small square or bullet) is stored in video memory just like the walking frames.

The Projectile Has Its Own Variables

Separate memory locations (ProjX, ProjY, and ProjActive) keep track of its position and whether it’s active (visible on-screen).

Firing the Projectile

When the player presses the space bar, the Fire routine checks if a projectile is already active.

Updating the Projectile

Once active, the UpdateProjectile routine moves the projectile forward by increasing its X coordinate on every frame.
If the projectile moves beyond the screen boundary, the program calls DeactProj to deactivate it and reset its position off-screen.

Keeping Player and Projectile Separate

The player’s sprite is updated independently of the projectile. Both routines are called in the MainLoop, ensuring the player can move freely while the projectile continues to move automatically.

This creates the illusion of the player firing a moving object across the screen.

Full Program

The following program expands upon the walking animation example.
In addition to movement and animation, pressing the space bar fires a small projectile that travels across the screen.

       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
      JP NZ, ClearLoop    ; Jump to start unless BC is zero


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

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

       ; Sprite Frame 0

       LD A,%00011000 : OUT (8),A
       LD A,%00111100 : OUT (8),A
       LD A,%00011000 : OUT (8),A
       LD A,%00011111 : OUT (8),A
       LD A,%00011010 : OUT (8),A
       LD A,%00011000 : OUT (8),A
       LD A,%00100100 : OUT (8),A
       LD A,%01000010 : OUT (8),A

       ; Sprite Frame 1

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

       LD A,%00011000 : OUT (8),A
       LD A,%00111100 : OUT (8),A
       LD A,%00011000 : OUT (8),A
       LD A,%00011111 : OUT (8),A
       LD A,%00011010 : OUT (8),A
       LD A,%00011000 : OUT (8),A
       LD A,%00011000 : OUT (8),A
       LD A,%00011000 : OUT (8),A

       ; Sprite Projectile

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

       LD A,%00000000 : OUT (8),A
       LD A,%00000000 : OUT (8),A
       LD A,%00000000 : OUT (8),A
       LD A,%00111111 : 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


MainLoop:

      CALL UpdatePlayerSprite
      CALL UpdateProjectile
      CALL CheckKeys
      JP MainLoop


UpdatePlayerSprite:

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

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

       RET

UpdateProjectile:

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

       LD A,(ProjY)
       OUT (8),A
       LD A,(ProjX)
       ADD A, 1
       LD (ProjX), A
       OUT (8),A
       LD A, 2
       OUT (8),A
       LD A,12
       OUT (8),A

       ; Check if off screen
       LD A, (ProjX)
       CP 249
       CALL Z, DeactProj

       RET


CheckKeys:
      CALL Delay

      RST 8
      DEFB 181

      CP 0          ; No Key Pressed
      JP Z, MainLoop

      ; W Pressed
      CP 87
      JP Z, MoveUp

      ; A Pressed
      CP 65
      JP Z, MoveLeft

      ; S Pressed
      CP 83
      JP Z, MoveDown

      ; D Pressed
      CP 68
      JP Z, MoveRight

      ; Space Pressed
      CP 32
      JP Z, Fire

      RET


MoveUp:
      LD A, (SpriteY)
      DEC A
      LD (SpriteY), A
      CALL FlipFrame
      JP MainLoop

MoveLeft:
      LD A, (SpriteX)
      DEC A
      LD (SpriteX), A
      CALL FlipFrame
      JP MainLoop

MoveDown:
      LD A, (SpriteY)
      INC A
      LD (SpriteY), A
      CALL FlipFrame
      JP MainLoop

MoveRight:
      LD A, (SpriteX)
      INC A
      LD (SpriteX), A
      CALL FlipFrame
      JP MainLoop

Fire:
      ; Return is Projectile already active
      LD A, (ProjActive)
      CP 1
      RET Z

      ; Active Projectile
      LD A, 1
      LD (ProjActive), A

      ; Set Initial Position
      LD A, (SpriteY)
      LD (ProjY), A
      LD A, (SpriteX)
      LD (ProjX), A

      RET

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

DeactProj:
      LD A, 0
      LD (ProjActive), A
      LD A, 216
      LD (ProjY), A
      LD (ProjX), A
      RET


FlipFrame:
      LD A,(SpriteFrm)
      XOR 1
      LD (SpriteFrm), A
      RET

SpriteX:
      DEFB 100
SpriteY:
      DEFB 120
SpriteFrm:
      DEFB 1
ProjY:
      DEFB 216
ProjX:
      DEFB 216
ProjActive:
      DEFB 0


       JP $            ; JP Idefinitely

   

Result

When you run the program, you’ll see your familiar walking sprite. Use W, A, S, and D to move around as before.
Now, when you press the space bar, the character fires a projectile that travels horizontally across the screen.

Once the projectile moves past the right edge, it disappears automatically and can be fired again.

This demonstrates how multiple independent objects can be updated and managed within the same main loop — a key principle of game logic.

Why This Technique Is Useful

The projectile system introduces the idea of independent entities in motion. Each projectile has its own position, behaviour, and life cycle, making it possible to manage multiple moving elements on screen simultaneously.

In more advanced games, this principle expands into:

This simple example provides the foundation for more complex interactions and game mechanics.

Summary

With this system in place, your sprite can now not only move and animate but also interact with the world, taking your Tatung Einstein game to the next level.

Previous Module
Next Module