Programmation Assembleur Z80/Intermédiaire

Programmation intermédiaire en Z80.
Création d'un code compatible ROM.
La création d'un code en ROM doit absolument se faire en respectant les règles suivantes:
Exemple: (à faire)
Fonctions mathématiques.
La virgule fixe.
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
Multiplication non signée 8 bits x 8 bits = résultat 16 bits.
 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.
Ce code est équivalent à l'instruction du motorola 68000 MULUW
 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.
Ce code est un équivalent de la fonction C atoi
 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.
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