CODING ★ MICRONEWS - Z80 ASSEMBLEUR FACILE ★

Cours et initiation du Magazine Micro NewsMicro News n°11 : Z80 Assembleur Facile

En Basic, vous vous arrangerez pour amener Z80 à "pédaler dans le vide", For 1=1 to 100:Next.

Vous pouvez au même titre écrire une boucle en assembleur qui soit un simple décompte de n à zéro.

LD BC, n
P1 DEC BC
LD A, B
OR C
JR NZ,P1

Vous constaterez qu'une telle boucle ne froino à pou près rien, tant elle est rapidement exécutée, Souvenez vous que Basic interprète avant d'exécuter; alors qu'en Assembleur seule subsiste l'exécution. Il est donc indispensable d'introduire dans notre boucle quelques instructions que Z80 exécutera "pour du vent".

NOP ne rien faire et aller à l'instruction suivante
NOP
NOP

Ou encore :

PUSH HL Empiler HL
POP HL Dépiler HL

Attention : il faut rester cohérent. Si vous empilez, il faut aussi dépiler, sinon gare aux surprises I Ce thème de l'empilage-dépilage fera l'objet d'un prochain papier résumant tout ce qu'il convient de savoir à ce propos. Dans l'immédiat, permettez moi de me contenter de poser des barrières de sécurité. En définitive, dans une boucle de temporisation, n'importe quelle instruction, exécutée "pour du vent" fera l'affaire, sous réserve que cette ou que ces instructions, ne détruisent ni les données, ni la cohérence interne du programme. Évitez de réinitialiser BC, par exemple.

Voici donc quelques variantes qui vous familiariseront avec la technique de distribution des instructions selon les nécessités du moment, la disponibilité des registres, etc.

CALL RETARD
RETARD PUSH BC : Empiler BC (protection)
LD BC,n : Initialisé BC à n
BCLRET PÜSH HL : Pour faire durer
POP HL
DEC BC : Désincrémenter BC
LD A, B : Voir si BC=0
OR C
JR NZ,BCLRET : Reboucler si pas 0
POP BC : Retrouver BC du début
RET : Retour programme principal

On est là en présence d'une boucle de temporisation relativement pauvre, puisque BC est initialisé sur une constante, n. Une programmation de ce type conduirait à multiplier les sous-programmes. Voici donc une autre rédaction, beaucoup plus intéressante.

PUSH BC RETARD NOP
LD BC, n NOP
CALL RETARD NOP
POP BC DEC BC
*** Le reste comme d'habitude, avec bouclage directement sur Retard et sortie par RET.

Dans le cas présent, BC est protégé et initialisé au niveau du programme principal. Cette solution présente l'avantage d'être ouverte, puisque vous pouvez donner à BC la valeur que vous voulez au fur et à mesure des besoins.

Voici une troisième rédaction qui convient si, dans votre application, vous avez besoin de 3 ou 4 boucles initialisées sur des valeurs précises, mettons ni, n2, n3, n4.

*******
PUSH AF RETARD PUSH BC:Pour protection
LD A,x LD BC,n1 : Charger 1re valeur
CALL RETARD CP 0 : Accumulateur comparé à 0
POP AF JR Z,BCLRET : Égalité : sauter à la boucle
**********
LD BC,n2
CP 1
JR Z, BCLRET
LD BC,n3
CP 2
JR Z,BCLRET
LD BC,n4
BCLRET NOP
etc., avec bouclage sur BCLRET, POP BC et RET en sortie.

L'accumulateur est protégé au niveau du programme principal avant de recevoir la valeur 0,1, 2 ou quelconque. Dans le sous-programme, nous avons une sorte de distributeur de saut par comparaison de l'accumulateur à 0, 1, 2. Le début du sous-programme rappelle très précisément la formule Basic ON K goto ou ON K gosub. Vous remarquerez que le fait de sauter du programme principal en sous-programme n'altère pas le contenu de A, accumulateur, tout comme il n'altère pas le contenu de BC dans les exemples précédents. Notez enfin qu'une valeur de A supérieure à 3 entraine automatiquement le chargement de n4, par le fait

même que le programme continue en séquence.

Je concède à ceux de mes lecteurs déjà expérimentés en assembleur que ce distributeur présente un look quelque peu rudimentaire et mériterait à ce titre un "peut mieux faire". Il nous faut, pour en venir aux formules plus sophistiquées, assimiler quelques données complémentaires qui nous permettront d'élargir l'éventail des formules.

Regardez très attentivement la manière dont le programme d'assemblage encode les instructions qui suivent :

LD HL, 3B4FH 214F3B
LD BC, 3B4FH 014F3B
LD DE, 3B4FH 114F3B

On pourrait multiplier les exemples. A chaque fois, on note une transcription inversée de la valeur, en ce sens que 3B4F devient 4F3B. En d'autres termes, l'octet de poids faible est écrit avant l'octet de poids fort, L (de Low) est écrit avant H (de High), C est écrit avant B, etc.

Si on dissocie une paire de registres pour examiner séparément H et L, par exemple, on constate que H contient bien la valeur 3BH et L la valeur 4FH. Il s'agirait donc d'une simple convention d'écriture en encodage des mnémoniques.

En réalité les choses vont bien plus loin.

Supposez que vous désiriez ranger HL à l'adresse 9000H, HL étant une paire, il vous faut deux octets, 9000H et 9001 H.

LD HL, 3B4FH 114F3B
LD (9000H),HL 220090 (encore l'inversion)

Après exécution de ces deux instructions, on retrouve la valeur faible à l'adresse la plus bases (4FH en 9000H) et la valeur forte à l'adresse la plus haute (3BH en 9001H). Ainsi s'explique une formule du Basic qui vous est certainement familière : K=PEEK(I)+256*(PEEK(I+1)). C'est également en ordre inverse que Z80 relit les données. La convention d'écriture et de traitement jouant alors à l'opposé, tout rentre dans l'ordre.

J'aurais donc tort de fatiguer mon lecteur avec cette bizarrerie de l'assembleur Z80. Que non !

Vous serez bien souvent amené à préparer des tables que vous voudrez relire. Il conviendra de les organiser on tenant compte de ce phénomène. D'autres fois, ayant rangé une paire d'octets, vous voudrez relire l'un ou l'autre de ces octets isolément. Faut-il encore tomber sur le bon.

Pour relire les deux octets ci-dessus en DE, par exemple, vous écrirez simplement LD DE,(9000H). Mais bientôt nous verrons des cas plus complexes imposant un pointage, comme ci-dessous. Le contenu de HL est le résultat d'un calcul, et HL pointe de ce fait sur un octet dont on ne peut plus désigner physiquement l'adresse. La suite devient :

LD E,(HL) : Valeur basse dans registre bas de DE
INC HL : Décaler HL une fois vers le haut
LD D,(HL) : Valeur haute dans registre haut de DE

Je m'en voudrais de suivre certains ouvrages pour embrouiller votre esprit en démontrant la possibilité de ranger les octets en ordre traditionnel, pour peu que l'on se souvienne de les relire dans le même ordre. Mieux vaut prendre d'emblée le réflexe Z80 et ranger, par exemple, comme suit :

HL pointe sur un octet à la suite d'un calcul LD (HL),E : Ranger valeur basse de DE à l'adresse basse pointée par HL
INC HL : Décaler HL vers le haut
LD (HL),D : Ranger D, soit valeur haute de DE

Pour en revenir à nos boucles de temporisation, voici donc une table dont le contenu servira à initialiser BC.

RET1 DEFB 0
DEFB 40H : Pour un LD BC,4000H
RET2 DEFB 0
DEFB 80H : Pour un LD BC,8000H
RET3 DEFB 80 H
DEFB C0H : Pour un LD BC,C080H

Utilisons la valeur stockée sous RET2.

*********
PUSH HL RETARD PUSH BC
LD HL, RET2 LD C, (HL) : Attention, valeur basse
CALL RETARD INC HL
POP HL LD B, (HL)
BCLRET : Le reste comme d'habitude, finir par POP BC et RET.

Avant d'en finir avec ces variations sur les boucles, je voudrais attirer votre attention sur une technique inconnue en Basic, et qui me parait très importante. Il s'agit de la correction ou de la modification du programme par lui-même. Cette technique permet de développer les programmes les plus denses et les plus puissants.

Sous le thème "organisation de la mémoire", nous avons vu que rien ne différencie une zone de l'autre, qu'elle contienne des données ou des instructions de programme. Partant de là, un segment de programme peut à tout moment être modifié et remodifié. Bien souvent on évite la création d'un nouveau sous-programme en modifiant, par programme, l'une ou l'autre instruction d'un sous-programme existant.

Appliquons cette technique aux boucles de temporisations. Nous jouerons uniquement sur B, ce qui correspond bien à la réalité. Z80 travaille si vite qu'il faut raisonner en multiples de 256.

******
PUSH AF RETARD PUSH BC
PUSH HL BCLVAL LD BC,0 01 00 00
LD HL BCLVAL+2 RCLRET : Le reste comme d'habitude
LD A, n
LD (HL), A
CALL RETARD
POP HL
POP AF
**********

Attention : c'est bien l'octet situé en BCLVAL+2 qu'il faut corriger.

Je vous concède que dans ce contexte, cette technique n'apporte rien de bien nouveau.

Un avantage pourtant, vous pouvez faire cette correction bien avant d'appeler RETARD, et notamment à un moment ou HL et A sont libres, ce qui vous évite d'avoir à les protéger.

Ensuite il faut prendre tôt les bons réflexes.

Dans tous les exemples cités, les registres sont systématiquement protégés.

Ce n'est évidemment pas une obligation. Au niveau du sous-programme constitutif de votre boucle, il est souhaitable de le faire, car, plus loin, vous voudrez certainement réutiliser le sous-programme, dans d'autres conditions. Un instant d'inattention, un appel intempestif vers un sous-programme réduit au strict nécessaire, et vous introduisez la zizanie dans votre application.

André Schmitt , MICRONEWS n°11

★ REVUE: MICRONEWS
★ ANNÉE: 1988
★ AUTEUR: André Schmitt
 

Page précédente : Micro News n°09 : Z80 Assembleur Facile

★ AMSTRAD CPC ★ A voir aussi sur CPCrulez , les sujets suivants pourront vous intéresser...

Lien(s):
» Coding » Micro News n°06 : Z80 Assembleur Facile (1ere partie)
» Coding » Micro News n°09 : Z80 Assembleur Facile (4e partie : l'organisation de la mémoire)
» Coding » Micro News n°08 : Z80 Assembleur Facile (3e partie)
» Coding » Micro News n°12 : Z80 Assembleur Facile
» Coding » Micro News n°07 : Z80 Assembleur Facile - Les boucles (2eme partie)
Je participe au site:

» Vous avez remarqué une erreur dans ce texte ?
» Aidez-nous à améliorer cette page : en nous contactant via le forum ou par email.

CPCrulez[Content Management System] v8.73-desktop/c
Page créée en 216 millisecondes et consultée 1038 fois

L'Amstrad CPC est une machine 8 bits à base d'un Z80 à 4MHz. Le premier de la gamme fut le CPC 464 en 1984, équipé d'un lecteur de cassettes intégré il se plaçait en concurrent  du Commodore C64 beaucoup plus compliqué à utiliser et plus cher. Ce fut un réel succès et sorti cette même années le CPC 664 équipé d'un lecteur de disquettes trois pouces intégré. Sa vie fut de courte durée puisqu'en 1985 il fut remplacé par le CPC 6128 qui était plus compact, plus soigné et surtout qui avait 128Ko de RAM au lieu de 64Ko.