Non-Aligned Sprites

by Ciaran McCreesh
Created: 13th November 1999
Last Modified: 27th November 1999

This page will explain the basics behind findpixel and non-aligned sprite routines.

FindPixel

The idea behind a findpixel routine is that you give it a pair of coordinates and it returns a pointer to the relevant section in the video memory. The fastest (but not smallest) routine is the following:

FindPixel:
  ld a,c                  ;a contains y coordinate
  add a,a                 ;a contains y * 2
  add a,a                 ;a contains y * 4
  ld l,a                  ;l contains y * 4
  ld h,$3f                ;h contains ($fc >> 2)
  ld a,b                  ;a contains x coordinate
  rra                     ;a contains x / 2
  add hl,hl               ;hl = hl * 2
  rra                     ;a contains x / 4; carry flag -> msb
  add hl,hl               ;hl = hl * 4
  rra                     ;a contains x / 8; carry flag -> msb
  add a,l                 ;a contains x offset
  ld l,a                  ;l = l + a
  ld a,b                  ;a contains x coordinate
  and %00000111           ;a contains (x and %00000111) 
  ret                     ;we're finished

It takes the following inputs:

And gives the following outputs:

Using FindPixel

Now for a program that uses FindPixel. It's so simple I won't even put the source in a file - copy and paste it if you want to see it work.

.org $d748

  ld bc,$1234             ;x = $12, y = $34
  call FindPixel          ;get real memory
  ld b,(hl)               ;get current value
  or a                    ;or it with new value
  ld (hl),a               ;and put it back onto the screen
  ret                     ;we're finished
  
FindPixel:
  --FindPixel Routine goes here - left out to save space.--

.end

Non-Aligned Sprites

Now that we've got a findpixel routine we can write a non-aligned sprite routine. The following routine was written by me - it's a downgrade from the one I use in xenophon (coming soon, plug plug). The original uses undocumented opcodes (it won't run on certain emulators as officially some of the instructions I use don't exist). The following uses self-writing code, but only a bit...

PutSprite:
  ld a,c                  ;a contains y coordinate
  add a,a                 ;a contains y * 2
  add a,a                 ;a contains y * 4
  ld l,a                  ;l contains y * 4
  ld h,$3f                ;h contains $fc / 4
  ld a,b                  ;a contains x coordinate
  rra                     ;a contains x / 2
  add hl,hl               ;hl = hl * 2
  rra                     ;a contains x / 4; carry flag -> msb
  add hl,hl               ;hl = hl * 4
  rra                     ;a contains x / 8; carry flag -> msb
  add a,l                 ;a contains x offset
  ld l,a                  ;l = l + a
  ld a,b                  ;a contains x coordinate
  and %00000111           ;a contains (x and %00000111) 

  ld c,a                  ;c contains bit number
  ld (swmemplus-1),a      ;ld a,(ix + 0) becomes ld a,(ix + a) 
  ld ix,MaskTable         ;ix points to MaskTable
  ld a,(ix + 0)           ;ld a,(ix + a)
swmemplus:

  ld ix,TempMem           ;ix points to TempMem
  ld (ix + 0),a           ;(ix + 0) contains bitmask
  ld (ix + 1),c           ;(ix + 1) contains bit number

SpriteStart:
  ld b,8                  ;Sprite is 8 rows high
SpriteLoop:
  push bc                 ;Save loop counter
  ld a,(de)               ;a contains current row of sprite
  ld b,(ix + 1)           ;b contains bit number
  jr z,NoRotate           ;if b = 0 then don't rotate sprite

RotateLoop:
  rrca                    ;rotate sprite right
  djnz RotateLoop         ;until b = 0

NoRotate:
  ld c,a                  ;c contains rotated sprite
  or (hl)                 ;a contains sprite + current value
  and (ix + 0)            ;a contains new value
  ld (hl),a               ;place new value to screen
  inc hl                  ;move to next byte
  ld a,(ix + 0)           ;a contains bitmask
  cpl                     ;invert value of a
  and c                   ;a = a and rotated sprite
  or (hl)                 ;a contains new value
  ld (hl),a               ;put new value into memory

  ld bc,15                ;bc = Screen Width - 1
  add hl,bc               ;hl points to next row
  inc de                  ;de points to next row of sprite
  pop bc                  ;restore loop counter
  djnz SpriteLoop         ;loop if not done
  
  ret                     ;return

MaskTable:                ;table for converting bit to bitmask
.db %11111111,%01111111,%00111111,%00011111
.db %00001111,%00000111,%00000011,%00000001

TempMem:                  ;two bytes for temp storage 
.db 0, 0

This takes the following inputs:

It modifies af, bc, de, hl and ix. Note that this routine does not clip sprites, so using it too close to the edge will produce strange results. Also, this routine has been modified from the one used in xenophon, so it will OR the sprite to the screen rather than just draw it on.