CODING ★ Dr.Watson - Autoformation à l'assembleur par Micro Application ★

Dr.Watson - Autoformation à l'assembleur par Micro Application - Chapitre 08

CHAPITRE 8

MOUVEMENTS DE BLOCS ET COMPARAISONS

Le Z80 possède plusieurs instructions faites pour permettre une manipulation facile de sections de mémoire, et sans que le programme ait à inclure les adresses de chaque adresse mémoire. Ces instructions peuvent être partagées en deux catégories. Les mouvements de blocs permettent a des zones de mémoire d'être recopiées d'un endroit a un autre. Les comparaisons de blocs sont utilisées afin de rechercher une zone de mémoire pour un élément de donnée spécifique.

MOUVEMENTS DE BLOCS

La première instruction de mouvement de bloc que nous allons étudier dans ce chapitre est LDI :

LDI Charge (LoaD) dans la mémoire et Incrémente les pointeurs de données.

Pour utiliser cette instruction , HL doit contenir l'adresse de la case mémoire à partir de laquelle les données doivent arriver, et DE doit contenir l'adresse de la case mémoire où les données doivent être recopiées. Après exécution de cette instruction, le Z80 incrémente a la fois DE et HL. Il décrémente également BC; ce qui rend LDI particulièrement pratique dans les boucles - BC peut être utilisé comme un compteur de boucle. Quand BC est décrémenté a 1, le flag parité/dépassement est mis a zéro - pour d'autres valeurs de BC, LDI met ce flag a 1, afin qu'il puisse être testé pour terminer une boucle s'il est mis, en utilisant la condition PO. Nous détaillons cela ci-après; mais voyons d'abord le programme.

Comme exemple d'utilisation de LDL le programme 8.1 recopie de la mémoire sur l'écran.

Il recopie des données, d'une partie de la pile sur l'écran, ces données occupant les positions mémoire &B100 à &BFFF, qui correspondent aux positions mémoire &C000 à &FFFF inclues.

PROGRAMME 8.1

LD HL,&B992 Charge dans HL adresse de départ des données

LD DE,&C000 Charge dans DE la destination

LD BC,&A1 Charge dans BC la quantité de données + 1

LOOP: LDI ;Recopie un octet de données

JP PO,FINISH Sort de la boucle si BC = 1

JP LOOP ;Sinon continue la boucle

FINISH: RET

Cela devrait produire une ligne horizontale multicolore en travers de l'écran, avec une petite ligne multicolore aux extrémités opposées de chaque côté.

PARITE

Comme nous l'avons mentionné plus tôt, le flag de parité/dépassement est mis lorsqu'il est décrémenté à 1 plutôt qu'à 0, il est donc nécessaire, pour recopier correctement la mémoire, de charger dans BC le nombre d'octets à recopier + 1. Pour tester le flag parité/dépassement, les conditions sont PO -'parité impaire' (Odd) et PE 'parité paire' (Even)'.

'Parité' se réfère au nombre de bits mis dans l'octet ou au flag testé; parité paire signifie qu'un nombre pair de bits sont mis et parité impaire signifie qu'un nombre impair de bits sont mis. Donc, lorsque le flag de parité est mis, il contient un nombre impair (1) de bits mis, et peut être testé sur la condition PO, comme dans le programme 8.1 ci-dessus:

JP PO,FINISH:

Si Jamais il vous faut tester la parité d'un octet de données, vous pouvez le faire en chargeant 255 dans h, et en lui faisant une opération ET (AND) avec l'octet en question. Le flag de parité sera mis de façon appropriée et on peut le tester avec une instruction telle que le JP conditionnel ci-dessus. Les tests de parité sont généralement utilisés en systèmes de communications : un bit de chaque octet sera mis de côté pour 'être utilisé comme 'bit de parité'/ qui sera mis ou enlevé de façon appropriée pour chaque octet, afin que tous les octets transmis et reçus aient une parité identique. Si une interférence dénature les données, il y a de fortes chances que la parité de quelques octets aient été changée, et ceci peut être détecté.

EXERCICE 8.1

Ecrivez un programme similaire au programme 8,1 pour recopier &3FFF octets en commençant en &B100, et pour les afficher sur l'écran en commençant en &C000, ATTENTION : une erreur conduirait au 'plantage' de l'ordinateur, ce qui vous obligerait à recharger l'assembleur. Une réponse possible est donnée dans le chapitre solutions.

La solution de l'exercice 8.1 remplit sans problème l'écran de n'importe quoi, mais ce serait bien de voir tout cela se faire plus lentement. Essayez le programme 8.2 :

PROGRAMME 8.2

LD HL,&B100

LD DE,&C000

LD BC,&4000

LOOP: LDI

JP PO,FINISH:

LD A,&FF

DELAY: DEC A ; Instructions 'gaspillant' du temps

JP NZ,DELAY:

JR LOOP:

FINISH: RET

Une fois le programme lancé, l'écran se remplit ligne par ligne, les lignes tout d'abord séparées, puis les intervalles entre les lignes sont remplis.

La raison de ce type de remplissage de l'écran plutôt qu'un autre, par exemple, ligne par ligne sans séparation initiale entre les lignes, la raison en est la manière dont l'ordinateur contrôle l'écran. Des octets consécutifs de mémoire-écran (soit de &C000 à &FFFF) ne contrôlent pas des cases écran consécutives sur le moniteur. Les détails de l'organisation de la mémoire écran sont liés au 'système d'exploitation' de l'ordinateur plutôt qu'au code macnine ou au langage assembleur.

Le programme 8.2 illustre, indirectement, l'utilité principale de l'instruction de mouvement de bloc LDI : vous pouvez insérer d'autres instructions entre les opérations successives de l'instruction LDI.

Le programme 8.1 illustre l'inconvénient principal de LDI : si vous n'avez pas besoin d'autres instructions entre chaque opération LDI, il vous faut malgré tout un retour à LDI pour qu'il se répète. Cela gaspille de l'espace mémoire et du temps. Le Z80 a un truc pour cela :

LDIR Charge la mémoire dans la mémoire, Incrémente les pointeurs de

;données et se Répète.

Cette instruction agit de la même manière que LDI, sauf qu'elle se répète toute seule jusqu'à ce que BC ait été décrémenté à 0. Et aussi, elle met le flag de parité/dépassement à 0 quelle que soit la valeur actuelle de BC. De toute façon cela n'a bien sûr pas d'importance, puisqu'avec la répétition automatique, il n'est pas nécessaire de faire de tests pour savoir si elle a terminé ou pas. Le programme 8.3 est une autre version du programme 8.1, où l'on a remplacé LDI et JP par LDIR.

PROGRAMME 8,3

LD HL,&B992

LD DE,&C000

LD BC,&A0

LDIR

RET

Notez que dans ce cas, il n'est pas nécessaire de charger dans BC le nombre d'octets plus un à transférer, puisque LDIR s'arrête lorsque BC est à zéro, alors que dans le programme 8.1 et 8.2 la boucle se terminait quand BC=1. Il suffit de charger dans BC le nombre actuel d'octets à transférer. Il existe encore deux autres instructions de mouvement de bloc :

LDD Charge la mémoire de la mémoire. Décrémente les pointeurs de pile LDDR Charge la mémoire dans la mémoire. Décrémente les pointeurs de plies et se répète

Celles-ci sont respectivement semblables à LDI et LDIR, sauf que DE et HL sont décrémentés au lieu d'être incrémentés. Par exemple, essayez le programme 8.4 qui remplira l'écran de bas en haut :

PROGRAMME 8.4

LD HL,&BFFF

LD DE,&FFFF

LD BC,&4000

LOOP: LDD

JP PO,FINISH:

LD A,8FF

DELAY: DEC A

JP NZ,DELAY:

JP LOOP:

FINISH: RET

COMPARAISONS

C'est souvent pratique dans un programme de pouvoir comparer deux valeurs, puis de se décider en fonction du résultat - comme dans la construction BASIC IF...THEN. L'équivalent en langage assembleur est:

CP s ComPare s à A en soustrayant s de A et en laissant A inchangé

Ici, s est A, B, C, D, E, H, L, (IX,d), ou (HL), Après soustraction de s de A, le résultat n'est pas gardé (c'est-à-dire que A n'est pas altéré) bien que les conséquences de la soustraction aient été enregistrées par le flag de retenue (Carry), le flag zéro, le flag de dépassement, le flag de signe et le flag de demi-retenue. Les flags de signe, zéro et de demi-retenue sont étudiés dans un autre chapitre du livre, Ils sont utilisés lorsque l'on veut comparer des nombres en complément a deux.

Etant donné que la comparaison soustrait s de A, le flag de retenue sera mis si s>A, et annulé dans l'autre cas. Si s=A alors le flag zéro sera mis, sinon annulé. Le programme 8.5 illustre l'utilisation de CP.

PROGRAMME 8.5

LD A,&21 ; Met un nombre dans A

LD B,&40 ; Met un nombre dans B

; CP B ; Compare A à B

JP C,BIGGER: Si flag Carry mis, B>A

JP Z,EQUAL: ;Si zéro mis, B=A

LD A,&73 ; Sinon B<A; mettre 'S' ASCII dans A

CALL &BB5A ;Met sur l'écran

LD A,83C ; '<' ASCII

CALL &BB5A ; Met sur l'écran

; ID,&41 ; 'A' ASCII

CALL &BB5A ; Met sur l'écran

RET ; Sortie programme

BIGGER: LD A,&73 ; 'S' ASCII

CALL &BB5A ; Met sur l'écran

LD A,&3E ; '>' ASCII

CALL &BB5A ; Met sur l'écran

LD A,&41 ; 'A' ASCII

CALL &BB5A ; Met sur l'écran

RET ; Sortie programme

EQUAL: LD A,&73 ;'S' ASCII

CALL &BB5A ; Met sur l'écran

LD A,&3D ; /=/ ASCII

CALL &BB5A ; Met sur l'écran

LD A,&41 ; 'A' ASCII

CALL &BB5A ; Met sur l'écran

RET

Essayez encore ce programme avec les valeurs &40 en A et &21 en B, et avec &21 dans A et B pour vérifier que cela fonctionne comme vous le souhaitez.

Non seulement le Z80 possède l'instruction de comparaison ci-dessus, mais il possède aussi des instructions de comparaison de blocs. La première de celles-ci est CPI:

CPI Compare A avec la mémoire, Incréments le pointeur de données.

Comme avec LDI, cette instruction décrémente BC, le compteur d'octets (Byte Counter). Le flag de parité est également mis lorsque BC est décrémenté à 1, sinon il est annulé. Cette instruction n'utilise pas le flag de retenue, mais elle agit malgré tout sur le flag zéro. Cela veut dire que CPI ne peut être utilisée que pour tester l'égalité, pas supérieur à ou inférieur à. Le programme 8.6 cherche dans la mémoire une parenthèse '(' et met une '(' sur l'écran pour chaque parenthèse trouvée dans la mémoire.

PROGRAMME 8.6

LD A,&28 Met '(' dans A

LD HL,&0 Commence au début de la mémoire

LD BC,&0 Recherche sur 64k (BC=octets+1,0-1=FFFF)

LOOP: CPI

JP PO,FINISH: Fin si flag de parité mis

CALL Z,FOUND: Appelle 'found'si zéro mis

JP LOOP: Boucle encore

FINISH: CALL Z,FOUND: Vérifie flag zéro avant fin

RET ; Retour à l'assembleur

FOUND: CALL &BB5A Met '(' sur écran

RET ; RETour du sous-programme

Il faut noter dans ce programme que le sous-programme 'FOUND' doit être appelé a partir de 'FINISH' parce que si Z était testé avant PO, et si un '(' était trouvé, appeler BB5A altérerait le flag de parité. Si PO était testé d'abord, ce problème ne se produirait pas.

Une autre instruction de comparaison de blocs est :

CPIR ComPare A avec la mémoire, Incrémente le pointeur de données et se

Répète.

Cette instruction est semblable à CPI, sauf qu'elle continue automatiquement jusqu'à ce qu'une identité soit rencontrée ou que BC=0, Le programme 8.6 peut être réécrit en remplaçant simplement CPI par CPIR, Le nouveau programme sera plus rapide avec CPIR parce que les conditions n'ont cas a être testées après chaque comparaison.

Les deux dernières comparaisons de blocs sont les mêmes que CPI et CPIR sauf que le pointeur de données, HL est décrémenté au lieu d'être incrémenté :

CPD ComPare A avec la mémoire. Décrémente le pointeur de données

CPDR ComPare A avec la mémoire. Décrémente le pointeur de données et se

répète

A l'inverse de LDD et LDDR, ces instructions agissent sur le flag de parité.

Au cas où vous en ayez besoin, vous trouverez dans les annexes une table des effets des comparaisons sur les flags pouvant être testés C (retenue), S (signe) et V (débordement). Il faudra vous y référer si vous devez comparer des nombres en complément à deux (soit des nombres supérieurs à 128)

RESUME

Après ce chapitre, vous devriez connaître

LDI ; LDD

PE LDDR

PO ; CP s

parité ; CPI

une boucle de temporisation CPIR

LDIR ; CPD

; CPDR

★ ANNÉES: 1985
★ AUTEUR: T. Hebertson
★ CONVERTION: CRACKERS VELUS

Page précédente : Dr.Watson - Autoformation à l'assembleur par Micro Application - Chapitre 07
 
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.7-desktop/c
Page créée en 265 millisecondes et consultée 1404 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.