Z80 Good Programming Practices

From WikiTI
Revision as of 10:41, 24 September 2006 by Burntfuse (Talk | contribs)

Jump to: navigation, search

Using IX

If you have objects represented by adjacent chunks of data in memory, you can use IX to easily manage them.

WithoutWith
 ld b,10
 ld hl,SpritesData
DisplaySpritesLoop
 ld b,(hl) ; coordx
 inc hl
 ld c,(hl) ; coordy
 inc hl
 ld d,(hl) ; first part of address
 inc hl
 ld e,(hl) ; end of address
 inc hl
 call DisplaySprite
 djnz DisplaySpritesLoop
 
COORDX .equ 0
COORDY .equ 1
ADDR1 .equ 2
ADDR2 .equ 3

 ld b,10
 ld ix,SpritesData
DisplaySpritesLoop
 ld b,(ix+COORDX)
 ld c,(ix+COORDY)
 ld d,(ix+ADDR1)
 ld e,(ix+ADDR2)
 call DisplaySprite
 ld hl,4
 add ix,hl
 djnz DisplaySpritesLoop
 

Defining constants for the offsets of each field of your "objects" makes the code more understandable. However this particular example is not the best, being that the original code is faster and smaller. Typically in sequential access using the HL register gives better performance, but if random access of different objects and different elements in an object is required throughout a particular iteration, then ix would be the better choice.


Lookup table

If you have a place in your code where a value is tested to choose between a lot of things, like subroutines or data, it can be a good idea to use lookup tables instead of a series of tests. It makes the code more readable, concise and extensible.

In terms optimisation though it should be used when the data is not sequentially ordered or when the objects being pointed to are not the same size. For example, using LUTs (Look Up Tables) to find a tile in a block of memory that is only tiles would both slower and cost more memory. Using LUTs to find a particular string would be quicker but would waste more memory than a linear search. Using LUTs as a jump table to different code blocks located through out a program would be faster and smaller compared to the alternative.

However, if there aren't many jumps and many of the values of a are sequential, it would be more efficient to do something like:

 ld a,(Number)
 or a
 jp z,A_is_0
 dec a
 jp z,A_is_1
 dec a
 jp z,A_is_2
 sub 2
 jp z,A_is_4
 dec a
 jp z,A_is_5
 

Examples:

WithoutWith
 ld a,(SpriteNumber)
 cp 1
 jp z,ChooseSprite1
 cp 2
 jp z,ChooseSprite2
 cp 3
 jp z,ChooseSprite3
 cp 4
 jp z,ChooseSprite4
...
ChooseSprite1
 ld hl,Sprite1
 jp DisplaySprite
ChooseSprite2
 ld hl,Sprite2
 jp DisplaySprite
ChooseSprite3
 ld hl,Sprite3
 jp DisplaySprite
ChooseSprite4
 ld hl,Sprite4
 jp DisplaySprite
...
DisplaySprite
 ld bc,(coordinates)
 call SpriteRoutine

 ld a,(SpriteNumber)
 add a,a ; a*2
 ld h,0 
 ld l,a 
 ld de,SpriteAddressLUT
 add hl,de
 ld a,(hl)
 inc hl
 ld h,(hl)
 ld l,a
 ld bc,(coordinates)
 call SpriteRoutine
...
SpriteAddressLUT
 .dw Sprite1
 .dw Sprite2
 .dw Sprite3
 .dw Sprite4
 

And this one :

WithoutWith
 ld a,(MenuChoice)
 cp 1
 jp z,Choice1
 cp 2
 jp z,Choice2
 cp 3
 jp z,Choice3
 cp 4
 jp z,Choice4
...
 
 ld a,(MenuChoice)
 add a,a ; a*2
 ld h,0
 ld l,a
 ld de,CodeBranchLUT
 add hl,de
 ld a,(hl)
 inc hl
 ld h,(hl)
 ld l,a
 jp (hl)
...
CodeBranchLUT
 .dw Choice1
 .dw Choice2
 .dw Choice3
 .dw Choice4
 
This article is a stub. You can help WikiTI by expanding it.