Difference between revisions of "Z80 Optimization"

From WikiTI
Jump to: navigation, search
(General)
(Small Tricks)
Line 43: Line 43:
  
 
== Small Tricks ==
 
== Small Tricks ==
 +
Note that the following tricks act much like a peephole optimizer and are the last optimization step : remember to first optimize your algorithm and register allocation before applying any of the following if you really want the fastest speed and the smallest code.
 +
 +
<nowiki>
 +
;Instead of:
 +
cp 0
 +
;Use
 +
or a
 +
; -> save 1 byte and 3 T-states
 +
</nowiki>
 +
 
  <nowiki>
 
  <nowiki>
 
;Instead of:
 
;Instead of:
Line 50: Line 60:
 
;or
 
;or
 
  sub a    ;disadvantages: changes flags
 
  sub a    ;disadvantages: changes flags
 +
; -> save 1 byte and 3 T-states
 +
</nowiki>
 +
 +
<nowiki>
 +
; Instead of :
 +
ld a, (hl)
 +
ld (de), a
 +
inc hl
 +
inc de
 +
; Use :
 +
ldi
 +
inc bc
 +
; -> save 1 byte and 4 T-states
 
  </nowiki>
 
  </nowiki>
  

Revision as of 01:56, 4 November 2009

This article is a stub. You can help WikiTI by expanding it.


Introduction

Sometimes it is needed some extra speed in ASM or make your game smaller to fit on the calculator.

General

General algorithm improvements and correct use of registers.

Shadow registers

In some rare cases, when you run out of registers and cannot to either refactor your algorithm(s) or to rely on RAM storage you may want to use the shadow registers : af', bc', de' and hl'

These registers behave like their "standard" counterparts (af, bc, de, hl) and you can swap the two register sets at using the following instructions :

 ex af, af'  ; swaps af and af' as the mnemonic indicates

 exx         ; swaps bc, de, hl and bc', de', hl'
 

Shadow registers can be of a great help but they come with two drawbacks :

  • they cannot coexist with the "standard" registers : you cannot use ld to assign from a standard to a shadow or vice-versa. Instead you must use nasty constructs such as :
 ; loads hl' with the contents of hl
 push hl
 exx
 pop hl
 
  • they require interrupts to be disabled since they are originally intended for use in Interrupt Service Routine. There are situations where it is affordable and others where it isn't. Regardless, it is generally a good policy to restore the previous interrupt status (enabled/disabled) upon return instead of letting it up to the caller. Hopefully it s relatively easy to do (though it does add 4 bytes and 29/33 T-states to the routine) :
  ld a, i  ; this is the core of the trick, it sets P/V to the value of IFF so P/V is set iff interrupts were enabled at that point
  push af  ; save flags
  di       ; disable interrupts
  
  ; do something with shadow registers here

  pop af   ; get back flags
  ret po   ; po = P/V reset so in this case it means interrupts were disabled before the routine was called
  ei       ; re-enable interrupts
  ret
 

Small Tricks

Note that the following tricks act much like a peephole optimizer and are the last optimization step : remember to first optimize your algorithm and register allocation before applying any of the following if you really want the fastest speed and the smallest code.

;Instead of:
 cp 0
;Use
 or a
; -> save 1 byte and 3 T-states
 
;Instead of:
 ld a,0
;Try this:
 xor a    ;disadvantages: changes flags
;or
 sub a    ;disadvantages: changes flags
; -> save 1 byte and 3 T-states
 
; Instead of :
 ld a, (hl)
 ld (de), a
 inc hl
 inc de
; Use :
 ldi
 inc bc
; -> save 1 byte and 4 T-states
 

Setting flags

In some occassion you might want to selectively set/reset a flag.

Here are the most common uses :

; set Carry flag
scf

; reset Carry flag (alters Sign and Zero flags as defined)
or a

; alternate reset Carry flag (alters Sign and Zero flags as defined)
and a

; set Zero flag (resets Carry flag, alters Sign flag as defined)
cp a

; reset Zero flag (alters a, reset Carry flag, alters Sign flag as defined)
or 1

; set Sign flag (negative) (alters a, reset Zero and Carry flags)
or $80

; reset Sign flag (positive) (set a to zero, set Zero flag, reset Carry flag)
xor a
 

Other possible uses (much rarer) :

; Set parity/overflow (even):
xor a

Reset parity/overflow (odd):
sub a

; set half carry (hardly ever useful but still...)
and a

; reset half carry (hardly ever useful but still...)
or a
 

As you can see these are extremely simple, small and fast ways to alter flags which make them interesting as output of routines to indicate error/success or other status bits that do not require a full register.

Were you to use this, remember that these flag (re)setting tricks frequently overlap so if you need a special combination of flags it might require slightly more elaborate tricks. As a rule of a thumb, always alter the carry last in such cases because the scf and ccf instructions do not have side effects.