# Programmation d'initiation en Z80
## Préambule

Cette section pour débutants présente des fonctions simples et utiles, de complexité abordable et dont l'exécution est détaillée pas à pas.

## Désactiver le système

Lorsqu'on exécute un programme Z80 à partir du système, on peut avoir besoin de toute la mémoire ou de toute la puissance du processeur. Le système est presque toujours située à l'adresse d'interruption du mode d'interruption IM 1, à savoir #38. Pour désactiver le système, il faut supprimer la routine située en #38, à minima l'appel à cette fonction.
La valeur #C9FB peut sembler obscure car nous n'avons pas encore parlé d'opcode
* #FB est l'opcode de l'instruction EI
* #C9 est l'opcode de l'instruction RET
Les deux valeurs sont inversées car l'écriture 16 bits est aussi inversée en mémoire (c'est le mode de fonctionnement en big-endian du processeur).
On aurait pu écrire de façon plus "lourde" le code suivant
Avec cette écriture, il est inutile de connaitre les opcodes des instructions ou leur longueur et on recopie de façon générique une portion de code à l'adresse #38

## Effacer une zone mémoire

Voici un sous-programme simple pour initialiser une zone mémoire. Nous assumons deux paramètres en entrée: HL pour l'adresse de la zone à effacer et BC pour la longueur.
Pour rappel, l'instruction LDIR peut êre assimilée au micro-code suivant:
Ce programme n'est pas très efficient puisque nous réalisons en réalité une copie mémoire pour effacer la zone. Comment pourrions-nous écrire une initialisation simple?
Ce programme est moins rapide que la boucle de copie car le LDIR est une instruction tout-en-un très performante.
Pour aller plus loin Il est possible de faire mieux en utilisant la pile, sous contrainte. La sauvegarde d'un registre dans la pile est très rapide et on y écrit deux octets à la fois. L'idée est de modifier l'adresse de la pile pour effacer la zone mémoire en exécutant autant de "sauvegarde" que nécessaire. En fait de sauvegarde de registre, on écrira en boucle un registre initialisé à zéro. Cette technique a été très utilisée dans les jeux vidéos pour effacer rapidement un écran. Comme on utilise la pile pour faire nos écritures, il est fortement conseillé de désactiver les interruptions.

## Manipulations de chaines de caractères

Un bon exercice est de réaliser un jeu de fonctions de manipulation de chaine. Les noms des fonctions suivantes sont dérivées du langage C. On commencera avec des fonctions de copie de chaine (pour rappel une chaine est une suite de caractères terminée par un octet à zéro), des fonctions de comparaison de chaine, de calcul de longueur de chaine puis on pourra se servir de ces fonctions pour créer des meta-fonctions, de recherche d'une chaine dans une autre, de concaténation de chaine, etc. L'idée est de partir d'une ou plusieurs fonctions simples et de les combiner pour réaliser des algorithmes de plus en plus complexes.

### Recopie d'une chaine de caractères

Le programme ci-dessus est très simple. L'instruction LDI copie un octet de (HL) vers (DE) puis incrémente HL et DE. Le registre BC est aussi décrémenté mais on ne s'en sert pas. L'instruction CP (HL) prendra la valeur de l'octet contenu dans (HL) pour le comparer au registre A qui vaut zéro. Ainsi, quand la valeur de (HL) sera nulle, le flag Z sera positionné. Notre programme boucle tant que NZ, soit non zéro.
Une particularité de l'instruction LDI est de ne pas modifier le flag Z. On peut donc réduire la taille de notre programme et même l'améliorer en cas de chaine vide. Car pour le moment, si on veut copier une chaine vide, HL est incrémenté avant la première comparaison et on ratera le zéro! La comparaison étant faite avant la copie, il n'y a plus besoin de copier le zéro après le dernier test. Notre programme devient donc:

### Concaténation d'une chaine de caractères à une autre

Pour concaténer une chaine de caractère, il faut trouver la fin de la première chaine, puis réaliser à partir de là une copie de la seconde.
L'instruction CPIR compare l'octet dans (HL) au registre A qui ici est nul, incrémente HL puis décrémente BC. L'instruction CPIR continue tant que BC est différent de zéro. Comme nous avons initialisé le registre BC à zéro, cela veut dire que l'instruction CPIR ne s'arrêtera pas avant d'avoir scanné toute la mémoire. Autrement dit, la condition de sortie est avec certitude le terminateur de la chaine. En sortie de l'instruction CPIR le registre HL contient donc la position du zéro plus un. Nous décrémentons alors HL et permutons les registres pour bien copier de HL vers DE.
Factorisons un peu notre code
Plutôt que d'avoir les deux fonctions séparées, on peut fusionner les deux fonctions et ajouter un label pour avoir un point d'entrée strcpy. Nous évitons de faire un CALL+RET et il n'est plus nécessaire d'initialiser le registre A deux fois de suite (lors du calcul de longueur de strcat puis au début du strcpy).
xor a ; obligé d'en mettre un deuxième en cas d'appel direct!

### Calcul de longueur d'une chaine

Dans la fonction strcat vous avons vu comment se positionner en fin de chaine mais cela ne nous donne pas sa longueur (c'est très pratique de le savoir, parfois). En modifiant légèrement cette fonction et en ajoutant un compteur, rien de plus simple.
Ce programme est une version décomposée de l'instruction CPIR. En effet, il faut à chaque itération incrémenter le registre DE qui contiendra en retour cette longueur. Le registre DE est initialisé à #FFFF ou -1 car sinon, l'octet qui contient le zéro sera compté.
Même si ce programme parait simple et optimal, il est dommage d'incrémenter un registre 16 bits et de faire un saut conditionnel à chaque octet lu quand l'instruction CPIR fait presque tout toute seule. L'idée est de conserver la valeur de départ et de soustraire la valeur destination.
L'instruction CPIR très rapide à réaliser de nombreuses comparaisons permet à cette fonction d'être plus rapide que la précédente, même pour des valeurs de chaine très courtes. Et si on veut avoir un comportement identique à la première fonction, il suffit d'ajouter un EX HL,DE avant le ret pour permuter les deux registres et renvoyer la longueur dans DE.
Par exemple avec une chaine de 4 caractères localisée en #8000 qui aura un zéro terminateur en #8004. L'instruction CPIR s'arrêtera avec HL=#8005 car pour rappel, l'incrémentation du registre HL se fait après la comparaison avec la valeur de l'octet dans (HL). #8005-1-#8000 donne bien 4.
| Itération de CPIR | Adresse de l'octet comparé | Valeur de HL en sortie |
| 1 | #8000 | #8001 |
| 2 | #8001 | #8002 |
| 3 | #8002 | #8003 |
| 4 | #8003 | #8004 |
| 5 | #8004 (comparaison ok!) | #8005 |

### Comparaison de deux chaines de caractères

Positionnement des flags en sortie de fonction
* flag Z positionné à 1 si les chaines sont identiques
* flag Z positionné à 0 et flag C à 1, alors la première chaine est plus "forte"
* flag Z positionné à 0 et flag C à 0, alors la seconde chaine est plus "forte"