Difference between revisions of "Z80 Good Programming Practices"

From WikiTI
Jump to: navigation, search
m
(improved)
 
(6 intermediate revisions by 4 users not shown)
Line 1: Line 1:
== Using IX ==
 
  
If you have objects represented by adjacent chunks of data in memory, you can use IX to easily manage them.
+
= Programming Techniques =
  
<table border="1" cellpadding="2">
+
== Lookup table ==
<tr><th>Without</th><th>With</th></tr>
+
<tr>
+
<td>
+
<nowiki>
+
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
+
</nowiki>
+
</td>
+
<td>
+
<nowiki>
+
COORDX equ. 0
+
COORDY equ. 1
+
ADDR1 equ. 2
+
ADDR2 equ. 3
+
  
ld b,10
+
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.
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
+
</nowiki>
+
</td>
+
</tr>
+
</table>
+
  
Defining constants for the offsets of each field of your "objects" makes the code more understandable.
+
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.
  
== Lookup table ==
+
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:
 
+
<nowiki>
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.
+
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
 +
</nowiki>
  
 +
Examples:
 
<table border="1" cellpadding="2">
 
<table border="1" cellpadding="2">
 
<tr><th>Without</th><th>With</th></tr>
 
<tr><th>Without</th><th>With</th></tr>
Line 58: Line 30:
 
  <nowiki>
 
  <nowiki>
 
  ld a,(SpriteNumber)
 
  ld a,(SpriteNumber)
 +
cp 0
 +
jp z,ChooseSprite0
 
  cp 1
 
  cp 1
 
  jp z,ChooseSprite1
 
  jp z,ChooseSprite1
Line 66: Line 40:
 
  cp 4
 
  cp 4
 
  jp z,ChooseSprite4
 
  jp z,ChooseSprite4
 +
cp 5
 +
jp z,ChooseSprite5
 
...
 
...
 +
ChooseSprite0
 +
ld hl,Sprite0
 +
jp DisplaySprite
 
ChooseSprite1
 
ChooseSprite1
 
  ld hl,Sprite1
 
  ld hl,Sprite1
Line 78: Line 57:
 
ChooseSprite4
 
ChooseSprite4
 
  ld hl,Sprite4
 
  ld hl,Sprite4
 +
jp DisplaySprite
 +
ChooseSprite5
 +
ld hl,Sprite5
 
  jp DisplaySprite
 
  jp DisplaySprite
 
...
 
...
Line 88: Line 70:
 
  <nowiki>
 
  <nowiki>
 
  ld a,(SpriteNumber)
 
  ld a,(SpriteNumber)
  add a,a ; a*2
+
  add a,a   ; a*2 (limits SpriteNumber to 128)
  ld h,O
+
  ld h,0
 
  ld l,a  
 
  ld l,a  
 
  ld de,SpriteAddressLUT
 
  ld de,SpriteAddressLUT
 
  add hl,de
 
  add hl,de
 +
ld a,(hl)
 +
inc hl
 +
ld h,(hl)
 +
ld l,a
 
  ld bc,(coordinates)
 
  ld bc,(coordinates)
  call SpriteRoutine
+
  jp SpriteRoutine
 
...
 
...
 
SpriteAddressLUT
 
SpriteAddressLUT
 +
.dw Sprite0
 
  .dw Sprite1
 
  .dw Sprite1
 
  .dw Sprite2
 
  .dw Sprite2
 
  .dw Sprite3
 
  .dw Sprite3
 
  .dw Sprite4
 
  .dw Sprite4
 +
.dw Sprite5
 
  </nowiki>
 
  </nowiki>
 
</td>
 
</td>
Line 113: Line 101:
 
  <nowiki>
 
  <nowiki>
 
  ld a,(MenuChoice)
 
  ld a,(MenuChoice)
 +
cp 0
 +
jp z,Choice0
 
  cp 1
 
  cp 1
 
  jp z,Choice1
 
  jp z,Choice1
Line 121: Line 111:
 
  cp 4
 
  cp 4
 
  jp z,Choice4
 
  jp z,Choice4
 +
cp 5
 +
jp z,Choice5
 +
cp 6
 +
jp z,Choice6
 +
cp 7
 +
jp z,Choice7
 
...
 
...
 
  </nowiki>
 
  </nowiki>
Line 129: Line 125:
 
  ld a,(MenuChoice)
 
  ld a,(MenuChoice)
 
  add a,a ; a*2
 
  add a,a ; a*2
  ld h,O
+
  ld h,0
 
  ld l,a
 
  ld l,a
 
  ld de,CodeBranchLUT
 
  ld de,CodeBranchLUT
 
  add hl,de
 
  add hl,de
  bcall(_LdHLInd)
+
  ld a,(hl)
 +
inc hl
 +
ld h,(hl)
 +
ld l,a
 
  jp (hl)
 
  jp (hl)
 
...
 
...
CodeBranchLUT
+
CodeBranchLUT:
 +
.dw Choice0
 
  .dw Choice1
 
  .dw Choice1
 
  .dw Choice2
 
  .dw Choice2
 
  .dw Choice3
 
  .dw Choice3
 
  .dw Choice4
 
  .dw Choice4
 +
.dw Choice5
 +
.dw Choice6
 +
.dw Choice7
 
  </nowiki>
 
  </nowiki>
 
</td>
 
</td>
 
</tr>
 
</tr>
 
</table>
 
</table>
{{stub}}
+
 
 +
= Source Code =
 +
Some advices to take into consideration:
 +
* split into various files in a logical way (header, main, subroutines, data) when it turns many pages long.
 +
* document every routine with input, output, destroyed registers and a short description when appropriate.
 +
* document well ugly and difficult parts of code
 +
 
 +
= Related topics =
 +
* [http://www.unitedti.org/forum/index.php?showtopic=8461 Common Mistakes and Good techniques]

Latest revision as of 12:53, 17 November 2010

Programming Techniques

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 0
 jp z,ChooseSprite0
 cp 1
 jp z,ChooseSprite1
 cp 2
 jp z,ChooseSprite2
 cp 3
 jp z,ChooseSprite3
 cp 4
 jp z,ChooseSprite4
 cp 5
 jp z,ChooseSprite5
...
ChooseSprite0
 ld hl,Sprite0
 jp DisplaySprite
ChooseSprite1
 ld hl,Sprite1
 jp DisplaySprite
ChooseSprite2
 ld hl,Sprite2
 jp DisplaySprite
ChooseSprite3
 ld hl,Sprite3
 jp DisplaySprite
ChooseSprite4
 ld hl,Sprite4
 jp DisplaySprite
ChooseSprite5
 ld hl,Sprite5
 jp DisplaySprite
...
DisplaySprite
 ld bc,(coordinates)
 call SpriteRoutine

 ld a,(SpriteNumber)
 add a,a   ; a*2 (limits SpriteNumber to 128) 
 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)
 jp SpriteRoutine
...
SpriteAddressLUT
 .dw Sprite0
 .dw Sprite1
 .dw Sprite2
 .dw Sprite3
 .dw Sprite4
 .dw Sprite5
 

And this one :

WithoutWith
 ld a,(MenuChoice)
 cp 0
 jp z,Choice0
 cp 1
 jp z,Choice1
 cp 2
 jp z,Choice2
 cp 3
 jp z,Choice3
 cp 4
 jp z,Choice4
 cp 5
 jp z,Choice5
 cp 6
 jp z,Choice6
 cp 7
 jp z,Choice7
...
 
 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 Choice0
 .dw Choice1
 .dw Choice2
 .dw Choice3
 .dw Choice4
 .dw Choice5
 .dw Choice6
 .dw Choice7
 

Source Code

Some advices to take into consideration:

  • split into various files in a logical way (header, main, subroutines, data) when it turns many pages long.
  • document every routine with input, output, destroyed registers and a short description when appropriate.
  • document well ugly and difficult parts of code

Related topics