Assembleur Z80 sur RC2014 - épisode 3 - Données en mouvement

Bonsoir à tous,

Voici la suite des découvertes de l'assembleur Z80 sur RC2014 en utilisant SCM (Small Computer Monitor, voir sa configuration), il est possible de faire de l'assembleur directement sur le RC2014-Pro.

Nous continuons le visionnage des vidéos de Matt Heffernan relatif à la programmation Z80 sur Spectrum pour adapter les exemples au RC2014.


Episode 3: données en mouvement

L'épisode 2 sur l'adressage avait déjà abordé la transfert de données entre la mémoire et les registres. Ces points seront forcement revu et complété avec de nouvelles instructions dédiés au transfert de données.


Les registres du Z80

bien que déjà abordé à l'épisode précédent, cette petite révision apportera des éléments complémentaires.


  • A <-- transfert 8bits uniquement
  • BC, DE, HL <-- transfert 16bits ou 8bits
  • SP <-- Stack Pointer: transfert 16bits uniquement

A noter que:

  • I: Interrupt Vector peut uniquement être chargé dans A
  • R: Memory Refesh Rate peut uniquement être chargé dans A

LD : LOAD

Cette instruction couvre pas moins de 184 opcodes, c'est dire si elle est importante!

LD prend deux opérantes:

  1. destination
  2. source
Operands utilisables avec la commande LD (load)

Il faut noter que A est le seule à pouvoir être chargé avec I et R.
HL ne peut pas être utilisé conjointement avec les registres d'index IX et IY.

Registres 8bits-vers-8bits

ld A, I
ld R, A

Registres 16bits-vers-16bits

ld SP, HL ; charger le stack pointer
ld SP, IX
ld SP, IY

Chargement immédiat

ld A, 42
ld B, 42 -> aussi pour C, D, H, L, IXh, IXl, IYh, IYl

Chargement immédiat en mémoire

ld (HL), 42 -> stocker 42 en mémoire à l'adresse indiquée par HL.
ld (IX+3), 42
ld (IY-9) 42

Chargement immédiat étendu (16 bits)

ld BC, 1234 --> aussi poir DE, HL, IX, IY

Chargement directement depuis la mémoire:

8bits ---vers---> A pour tous les registres 16 bits (BC, DE, HL, IX, IY)

8bits ---vers---> B,C,D, ... pour HL, IX, IY uniquement

ld a, ($A234)

Il est donc impossible de faire un LD B, (DE) .
 
16bits ---vers---> BC, DE, HL, IX, IY, SP (mais pas PC!)

ld DE, ($A234)
ld ($A234), BC

EX : EXCHANGE

Cette instruction permet d'échanger deux registres 16 bits. L'instruction dispose de 5 opcodes!

L'instruction ex af, af' permet d'échanger l'accumulateur et les flags pour les préserver (sans charger la pile/stack)

ex af, af' 

L'instruction ex de, hl est utile lorsque l'on utilise hl pour faire du calcul.

ex  de, hl

L'instruction ex (sp), hl permet d'échanger la valeur de hl avec la mémoire pointée par le stack pointer. Les opérations sur le stack pointer doivent être envisagés avec prudence (il faut savoir ce que l'on fait.

ex (sp), hl
ex (sp), ix
ex (sp), iy

EXX : EXCHANGE eXtended

Cette instruction permet d'échanger les 3 paires de registres BC <--> BC' , DE <--> DE'  ,  HL <--> HL' en une seule instruction!

LDI : Load Increment

Copie un octet d'un emplacement mémoire (indiqué par le registre HL) à un autre emplacement (indiqué par les registres DE). Puis incrémente la valeur des deux registres. 

Le registre BC est également décrémenté de 1. Celui-ci sert de compteur de fin (une fois à 0, on peut arrêter une routine de copie).

Pour résumé:

(DE) <-- (HL)
DE = DE+1
HL = HL+1
BC = BC-1

LDIR : Load Increment Repeat

Copie Nth octets d'un emplacement à un autre à l'aide de la commande LDI.
L'opération est répétée jusqu'à ce que BC = 0.

Par exemple, pour copier les 6912 octets de $E000 à $4000, on utilise le code suivant:

ld DE,$4000 ; destin
ld HL,$E000 ; source
ld BC, +6192 ; longueur
ldir

LDD : Load Decrement

Comme LDI, l'instruction LDD permet de copier le contenu d'un emplacement mémoire (pointé par HL) vers un autre emplacement mémoire (pointé par DE). Ensuite HL et DE sont diminué de 1.
Le registre BC (compteur) est également diminué de 1. Une fois arrivé à 0, il permet de d'arrêter une boucle de copie.

(DE) <--- (HL)
DE = DE -1
HL = HL -1
BC = BC -1

LDDR : Load Decrement Repeat

Similaire à LDDI avec la seule exception que la copie de fait d'une adresse haute vers une adresse basse (puisqu'il y a des soustractions).
Avec des éléments bien organisés en mémoire, il y a moyen de gagner du temps opérationnel (puisqu'on serait déjà au bon emplacement mémoire pour préparer la salve de copie suivante).

Voici comment copier les 6912 bytes de $E000 à $4000.

Destin = $4000 + 6911 = 0x5AFF
Source = $E000 + 6911 = 0xFAFF

Voici le code

ld DE,$5AFF ; destin
ld HL,$FAFF ; source
ld BC, $1b00 ; 6912 bytes
lddr

Lorsque l'exécution est terminée, BC = 0 et l'adresse source (comme la destination) à passée la valeur de fin de 1.
Donc:

  • DE (la destination) = $4000-1 = $3FFFF 
  • HL (la source) = $E000-1 = $DFFF

Exercice

Ecrire un programme en $8000 qui copie la chaine de caractère "RC2014 is awesome! " (stocké en $8500).
La chaîne est copiée de $A000 (40960) à $FFFF (65535).

Préparation de la mémoire

Commençons par effacer la mémoire du programme, mémoire de stockage et la mémoire de destination (du moins la fin de celle-ci).
 
fill 8000 8100 00
fill 8500 8600 00
fill ff00 ffff 00

Copie simple et exécution

Commençons par copier une fois le contenu du message dans la mémoire.  Ce que permet le programme ci-dessous qui a été assemblé sur le RC2014.

8000: 11 FF FF     ...   LD DE,$FFFF
8003: 21 12 85     !..   LD HL,$8512
8006: 01 13 00     ...   LD BC,$0013
8009: ED B8        ..    LDDR
800B: C9           .     RET
800C: 00           .     NOP
800D: 00           .     NOP
Une fois exécuté avec G 8000 , il est possible de constater le contenu de la mémoire avec M ff80 .
*g 8000
*m ff80
FF80:  ED B0 C9 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
FF90:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
FFA0:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
FFB0:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
FFC0:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
FFD0:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
FFE0:  FF FF FF FF FF FF FF FF  FF FF FF FF FF 52 43 32  .............RC2
FFF0:  30 31 34 20 69 73 20 61  77 65 73 6F 6D 65 21 20  014 is awesome!
A noter que c'est a partir de $FFFF que la stack (la pile) est généralement placée. Cette pile est, entre autre, utilisé par le moniteur. Il est donc normal d'y voir des informations modifiées après quelques opérations dans le moniteur.

Copie multiples et exécution

Et bien, je me rends compte qu'il me manque encore des instructions pour y arriver. En effet, je dois pouvoir comparer la valeur du registre DE (destination adresse) pour tester quand elle arrive à $A000 pour arrêter le programme de copie.

La soustraction $FFFF - $0xA000 = 0x5FFF soit 24575.
Dans cet espace de 24575 caractère, il est possible de dupliquer 24575/19= 1293.42 fois la chaîne de caractère.
Ou 24575//19=1293 fois et 24575%19=8 caractères.

A tout bientôt pour la suite

Aucun commentaire