Aligned Sprites

by Ciaran McCreesh
Created: 13th November 1999
Last Modified: 5th December 1999

This page will show how the most popular aligned sprite routine works.

The Routine

The fastest and most popular routine was posted on the assembly-86 mailing list in 1997 by Dan Eble. It takes three inputs:

It destroys af, bc, de, hl and ix (ix is one of the two index registers - it has some useful instructions associated with it, such as offsets. I'll explain more later).

;===========================================================
; GridPutSprite:                                  [Dan Eble]
;  Puts an aligned sprite at (E, D), where HL -> Sprite
;===========================================================
; IN : D  = y (0-7 inclusive)
;      E  = x (0-15 inclusive)
;      HL-> sprite
;
; OUT: AF, BC, DE, HL, IX modified
; Current total : 28b/567t (not counting ret)
;===========================================================
GridPutSprite:
  push hl                ; push hl to stack...                
  pop ix                 ; ...and pop it into ix
  srl d                  ; de = (128 * y) + x
  rra                    ; carry flag -≶ msb of a
  and $80                ; a is either $00 or $80   
  or e                   ; add x offset (remember x <= 15)
  ld e,a                 ; de = (128 * y) + (x * 8)
  ld hl,$fc00            ; hl-> video memory
  add hl,de              ; hl-> video memory + offset
  ld b,8                 ; initialize loop counter
  ld de,16               ; screen is 16 bytes wide
GPS_Loop:
  ld a,(ix + 0)          ; get byte from sprite
  ld (hl),a              ; put byte on screen
  inc ix                 ; move to next byte in sprite
  add hl,de              ; move to next line on screen
  djnz GPS_Loop          ; loop until sprite is drawn
  ret                    ; return

This routine is quite complicated - I've commented it (the original comments were a bit cryptic) but it is still hard to understand, in particular the usage of rra. This and the and $80 are used to calculate the offset correctly. Leaving these out would prevent certain coordinates from being drawn correctly.

Using It

Now we'll look at a (very) simple program that uses sprites. It will draw a picture of a smiley face in the top left of the screen and a sad face in the bottom right.

Source: faces.asm
Compiled: faces.86p

#include "ti86asm.inc"
.org _asm_exec_ram

  call _clrScrn          ; Clear screen
  call _homeup           ; Cursor to top left

  ld hl,SmileySprite     ; hl points to start of sprite
  ld de,$0000            ; d contains y, e contains x
                         ; so coordinate is (0, 0)
  call GridPutSprite     ; Draw smiley in top left

  ld hl,SadSprite        ; hl points to start of sprite
  ld de,$070f            ; d contains y, e contains x
                         ; so coordinate is (15, 7)
  call GridPutSprite     ; Draw smiley in top left

  call _pause            ; Pause calc
  call _clrScrn          ; Clear screen
  call _homeup           ; Cursor to top left
  ret                    ; Exit

GridPutSprite:
  push hl                ; push hl to stack...                
  pop ix                 ; ...and pop it into iy
  srl d                  ; de = (128 * y) + x
  rra                    ; carry flag -< msb of a
  and $80                ; a is either $00 or $80   
  or e                   ; add x offset (remember x <= 15)
  ld e,a                 ; de = (128 * y) + (x * 8)
  ld hl,$fc00            ; hl-> video memory
  add hl,de              ; hl-> video memory + offset
  ld b,8                 ; initialize loop counter
  ld de,16               ; screen is 16 bytes wide
GPS_Loop:
  ld a,(ix + 0)          ; get byte from sprite
  ld (hl),a              ; put byte on screen
  inc ix                 ; move to next byte in sprite
  add hl,de              ; move to next line on screen
  djnz GPS_Loop          ; loop until sprite is drawn
  ret                    ; return
  
SadSprite:
.db %01111110
.db %10000001
.db %10100101
.db %10000001
.db %10011001
.db %10100101
.db %10000001
.db %01111110

SmileySprite:
.db %01111110
.db %10000001
.db %10100101
.db %10000001
.db %10100101
.db %10011001
.db %10000001
.db %01111110

.end

As you can see, it's writing the routine that's difficult, using it is dead easy. Next we'll take a look at non-aligned sprites.