Programmation Assembleur Z80/Intermédiaire

Programmation intermédiaire en Z80

modifier

Création d'un code compatible ROM

modifier

La création d'un code en ROM doit absolument se faire en respectant les règles suivantes:

  • La ROM étant en lecture seule, il ne faut pas y mettre des variables volatiles. Les variables volatiles doivent être stockées hors de l'espace d'adressage de la ROM.
  • Le point de sortie du programme ROM doit être soit hors de l'espace d'adressage de la ROM, soit dans le même espace, en s'assurant qu'il commence exactement après l'instruction de désactivation de la ROM.

Exemple: (à faire)

Fonctions mathématiques

modifier

La virgule fixe

modifier

Le Z80 est un processeur qui ne sait gérer que des nombres entiers. Si on souhaite utiliser des nombres flottant, il faut simuler la partie décimale avec une valeur entière qu'on divisera au dernier moment.

Par exemple, le programme suivant incrémente de 1/4 à chaque itération en utilisant une virgule fixe de 8 bits et retourne la valeur dans A. Il est plus simple d'utiliser une virgule flottante de 8 bits car on récupèrera directement le résultat avec le poids fort de HL. Si on utilisait seulement 2 bits de précision pour simuler notre 1/4, il faudrait alors réaliser plusieurs décalages couteux en place comme en temps.

virgule=256*1/4 ; 64
ld hl,(valincrement)
ld bc,virgule
add hl,bc
ld (valincrement),hl
ld a,h
ret
valincrement defw 0

Le registre HL prendra successivement les valeurs suivantes

HL (décimal) HL (hexa) H (HL/256)
0 #0000 #00
64 #0040 #00
128 #0080 #00
192 #00C0 #00
256 #0100 #01
320 #0140 #01
... ... ...


Multiplication non signée 8 bits x 8 bits = résultat 16 bits

modifier
  • Entrée: H multiplicande, L multiplieur
  • Sortie: HL produit
LD E,H
LD L,0
LD D,L
SLA H
JR NC,$+3
LD L,E
repeat 7
ADD HL,HL
JR NC,$+3
ADD HL,DE
rend


Multiplication non signée 16 bits x 16 bits = résultat 32 bits

modifier

Ce code est équivalent à l'instruction du motorola 68000 MULUW

  • Entrée: BC multiplicande, DE multiplieur
  • Sortie: DE:HL produit de BC et DE
LD HL,0
LD A,16
muluw_loop
 ADD HL,HL
 RL E
 RL D
 JR NC,muluw_cont
  ADD HL,BC
  JR NC,muluw_cont
   INC DE
 muluw_cont
 DEC A
 JR NZ,muluw_loop

Conversion d'une chaine de caractères en nombre entier

modifier

Ce code est un équivalent de la fonction C atoi

  • Entrée: HL contient un pointeur sur une chaine de caractères contenant des chiffres
  • Sortie: DE:HL est un nombre 32 bits qui contient la valeur
atoi
exx
xor a
ld h,a
ld l,a
ld d,a
ld e,a ; on initialise le résultat DE:HL à zéro dans les registres secondaires grâce à l'instruction EXX qui les permute
exx
ld a,(hl)
inc hl
sub 48         ; code ASCII du 0
jr c,atoiexit  ; si on est plus petit ce n'est pas un nombre on quitte
cp 10
jr nc,atoiexit ; si on est plus grand que 9 alors on quitte aussi
exx
ld l,a         ; on initialise la première unité
exx
; itérations suivantes
atoiloop
ld a,(hl)
inc hl
sub 48
jr c,atoiexit
cp 10
jr nc,atoiexit
exx
; multiplication par 10 du nombre 32 bits DE:HL
add hl,hl
rl e
rl d       ; d'abord on le multiplie par deux
push de
push hl    ; et on le sauvegarde
add hl,hl
rl e
rl d       ; on re-multiplie par deux (x4)
add hl,hl
rl e
rl d       ; on remultiplie par deux (x8)
pop bc
add hl,bc
ex hl,de
pop bc
adc hl,bc ; on a additionné notre valeur de départ x2 et notre valeur de départ x8 -> multiplication par 10
ex hl,de
ld b,0
ld c,a
add hl,bc ; on peut ajouter la nouvelle unité au nombre
exx
jr atoiloop
atoiexit
exx   ; quand on sort on active le jeu de registres secondaires car c'est là qu'est stocké notre nombre DE:HL
ret

Optimisation de code

modifier

De niveau intermédiaire, voici quelques gammes à connaitre par cœur lors de la réalisation de code. La plupart optimisent à la fois pour la taille et la vitesse.

XOR A ; 4 cycles, 1 octet, A=0, reset de la Carry et mise à 1 de Z

plutôt que

LD A,0 ; 7 cycles, 2 octets, A=0 et ne touche pas aux flags

__________

OR A

plutôt que

CP 0

__________

DEC A ; modifie le registre A

plutôt que

CP 1 ; ne modifie pas le registre A

__________

pour comparer n'importe quel autre registre 8 bits que A à zéro

INC registre
DEC registre

__________

JP label

plutôt que

CALL label
RET

__________

RRA              ; modifie le registre A
CALL NC,label

plutôt que

BIT 0,A          ; ne modifie pas le registre A
CALL Z,label

__________

RLA              ; modifie le registre A
CALL NC,label

plutôt que

BIT 7,A
CALL Z,label

__________

Soustraire une valeur 16 bits

LD DE,-valeur
ADD HL,DE

plutôt que

LD DE,valeur
XOR A
SBC HL,DE

__________

Faire une boucle avec le registre BC et un index qui utilise HL

 LD HL,source
 LD BC,compteur
boucle
 ; code
 CPI
 JP PE,boucle

plutôt que

 LD HL,source
 LD BC,compteur
boucle
 ; code
 INC HL
 DEC BC
 LD A,B
 OR C
 JR NZ,boucle

__________

Reculer pour mieux sauter (multiplication par 64 d'une valeur pointée par HL)

LD H,(HL)
LD L,0
SRL H
RR L
SRL H
RR L

plutôt que

LD L,(HL)
LD H,0
ADD HL,HL
ADD HL,HL
ADD HL,HL
ADD HL,HL
ADD HL,HL
ADD HL,HL

__________

Faire une boucle quand on utilise tous les registres

LD A,compteur
boucle
LD (moncompteur+1),A ; écrire la valeur du compteur dans l'opcode situé à moncompteur
; code
moncompteur LD A,0
DEC A
JR NZ,boucle

Si on n'utilise pas tous les registres (registres secondaires inutilisés)

LD B,compteur
boucle
EXX
; code
EXX
DJNZ boucle

Si on n'utilise pas l'un des deux registres d'index IX ou IY, ne pas hésiter à les utiliser (solution plus rapide que le EXX)

LD XL,compteur
boucle
; code
DEC XL
JR NZ,boucle

__________

Dérouler ses boucles

LD A,64/8
LD BC,2
boucle
repeat 8
POP HL
ADD HL,DE
LD (IX+0),L
LD (IX+1),H
ADD IX,BC
rend
DEC A
JR NZ,boucle

plutôt que

LD A,64
LD BC,2
boucle
POP HL
ADD HL,DE
LD (IX+0),L
LD (IX+1),H
ADD IX,BC
DEC A
JR NZ,boucle

__________

Si vous avez besoin de deux compteurs, groupez vos opcodes

LD BC,5*256+40

plutôt que

LD B,5
LD C,40

__________

nope