Programmation Assembleur Z80/Intermédiaire
Programmation intermédiaire en Z80
modifierCréation d'un code compatible ROM
modifierLa 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
modifierLa virgule fixe
modifierLe 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
modifierCe 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
modifierCe 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
modifierDe 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