π
<-
Chat plein-écran
[^]

[FR] Langage assembleur sur ez80 - Tutoriel

Assembleur, Axe, C/C++, ICE...

[FR] Langage assembleur sur ez80 - Tutoriel

Message non lude TheMachine02 » 26 Fév 2017, 18:23

Guide du langage assembleur pour calculatrices à processeur ez80
TI-83 Premium CE & TI84+CE
Version: 0.0


* * *



I / Préparation

1 - L'édition d'un programme

Il existe à l'heure actuelle de nombreuses plateformes pour l'édition. Vous pouvez utiliser n'importe quel éditeur de texte, du Bloc-notes, à WordPad en passant bien sûr par Notepad++. Notepad++ a l'avantage de pouvoir définir un highlight automatique des commandes ASM.
Créez un document texte et changez son extension en .z80, .ez80 ou .asm, l'extension n'ayant que peu d'importance. (Il faut seulement que ce fichier soit reconnu et ouvert par votre OS avec le bon programme que vous utilisez). Par la suite, j'utiliserai l'extension .ez80.

2 - L'assemblage

Pour créer le programme que vous pourrez par la suite envoyer sur la calculatrice, il faut effectuer une étape consistant à transformer ce que vous avez écrit en code compréhensible par le processeur de la calculatrice. Il s'agit de l'assemblage. Rassurez vous, cette étape n'est pas à faire à la main, mais un programme appelé assembleur va faire ça pour nous.

Le programme s'appelle SPASM, et est téléchargeable gratuitement ici.

Cependant, nous avons aussi besoin d'autres éléments. La plupart des programmes assembleurs reposent sur des fonctions toutes faites présentes dans l'OS de votre calculatrice, ou encore sur des nombres constants qui ont une signification particulière. Comme nous n'allons pas apprendre tout ces nombres par cœur, nous pouvons demander au compilateur de créer des noms de fonctions bien plus compréhensibles par un être humain. Le fichier qui permet d'effectuer ceci s'appelle un fichier include.
Vous pouvez trouver le fichier include correspondant à votre calculatrice ici.

À noter :
Ce fichier n'est pas fourni par TI. Les valeurs qui y sont présentes ont été trouvées par des utilisateurs chevronnés. Ceci n'exclut donc pas des erreurs (même si jusque là, aucune n'a été découverte), et surtout il n'est pas complet. Veillez à le mettre à jour de temps en temps.


Il n'est pas nécessaire pour l'instant de tenter de comprendre ce fichier, je vous assure que par la suite il vous semblera simple :)


Créez un nouveau fichier dans votre répertoire dédié à la programmation assembleur et appelez le
ti83pce.inc. Copiez-collez le contenu du lien ci-dessus et enregistrez : votre fichier est prêt à l'emploi.
Une fois ceci fait, nous allons voir comment compiler le programme avec SPASM.
Sous windows, il faut créer un fichier de commande .bat dans le même répertoire que votre fichier .ez80, votre .inc et spasm64.exe:
  • Créez un fichier texte
  • Copiez les deux lignes suivantes : spasm64 -E exemple.ez80 TEST.8xp et pause
  • Fichier, Enregistrer sous...
  • Renommez le fichier en "build.bat"
  • Et enregistrez le tout.
Votre fichier .bat est prêt à être utilisé !

Les deux lignes signifient d'exécuter le programme spasm64 sur le fichier "exemple.ez80" (nom que vous pouvez changer), et de créer le programme résultant "TEST.8xp". L'option -E permet de compiler pour le processeur ez80 de la TI-83PCE. Une pause est mise à la fin pour que vous puissiez voir les erreurs (présentes ou non) lors de la compilation.

3 - Transfert et émulation

Une fois un .8xp obtenu vous pouvez soit l'envoyer sur votre calculatrice (en suivant ce tuto ou utiliser un émulateur.)

Il est conseillé d'utiliser l'émulateur en premier lieu car il est très facile de faire une erreur en assembleur, ce qui peut conduire à un RAM clear. Sur l'émulateur vous pourrez en plus débugger vos programmes directement avec des fonctions telles que l'exécution ligne par ligne, avec une visualisation de la mémoire en temps réel.


Voyons donc comment utiliser l'émulateur.

Tout d'abord, allez le télécharger ici : http://jacobly.com/CEmu/master/latest/ Ce lien est régulièrement mis à jour, donc n'hésitez pas à aller le retélécharger de temps en temps.
Au premier lancement, il demande une ROM. Vous pouvez obtenir une ROM à partir de votre calculatrice en suivant la procédure suivante :

  • Sélectionnez "Create a ROM image from a real calculator" puis "continue"
  • Puis "Save Programme" donnez le nom que vous voulez .
  • "Enregistrer" vous pouvez mettre le nom que vous voulez .
  • Envoyez le programme sur la calculatrice
  • Exécutez le programme "DUMP" avec "Asm(" trouvable dans le catalogue.
  • Maintenant en réactualisant "Ti Connect" il faut récupérer les "AppVar" de "ROMData0" à la "ROMDataK" et les copier sur l'ordinateur (je vous conseille de les copier dans un sous dossier), vous devez en obtenir 12. Vous pouvez les supprimer de votre calculatrice.
  • De retour sur CEmu Setup, choisissez "Selects Segments ..." et là vous sélectionnez toutes les "ROMData" qui ont été copiées de la calculatrice.
  • "Browse..." vous mettez un nom et vous l'enregistrez.

Votre ROM a maintenant été créée. La suite du setup est plutôt simple, vous pouvez regarder toute les options disponibles de l'émulateur. Ce que nous allons utiliser majoritairement avec est l'onglet "debbuger" et "memory".

Une ROM est strictement personnelle et le partage de la ROM sur internet est illégal, alors ne la mettez pas en ligne !


4 - Hello World
Bon maintenant vous allez compiler et exécuter votre premier programme, le classique hello world :)

Code: Tout sélectionner
   #include "ti83pce.inc"
   
   .assume ADL=1
   .db tExtTok,tAsm84CeCmp
   .org userMem
   
   call _ClrScrnFull
   ld hl, $04 \ ld (curRow),hl
   ld hl, $06 \ ld (curCol),hl
   ld hl,Text
   call _PutS
   call _GetKey
   call _ClrScrnFull
   
   ret
text:
   .db "Hello world !",0


Expliquons donc le code pas à pas :)

La première ligne #include "ti83pce.inc permet d'inclure dans la compilation votre fichier .inc précédemment copié. Si vous vous en souvenez, il permet de faire le lien entre des listes de nombres et un texte particulier. Comme nous allons le voir par la suite, il est particulièrement utile pour ce programme.
.assume ADL=1 est une instruction destinée au compilateur pour signifier que le CPU est bien en mode ADL (adresse Long), soit en mode 24 bits, dans lequel nous allons travailler.
.db tExtTok,tAsm84CeCmp permet de signifier à l'OS qu'il ne s'agît pas d'un programme TI-Basic mais bien d'un programme assembleur, nécessitant Asm( pour être exécuté.
.org userMem indique au compilateur quelle est l'adresse initiale dans la mémoire du programme. L'OS copie le programme en userMem lorsqu'il est exécuté, donc tous les programmes assembleurs commencent ici.

Ces quatre premières lignes sont appelées header du programme. Elles sont essentielles au fonctionnement de ce dernier et doivent toujours être présentes.


Code: Tout sélectionner
   call _ClrScrnFull
   ld hl, $04 \ ld (curRow),hl
   ld hl, $06 \ ld (curCol),hl
   ld hl,Text
   call _PutS
   call _GetKey
   call _ClrScrnFull


Ceci est le corps de votre programme. Sans rentrer dans les détails, il appelle différentes fonctions système de la calculatrice afin d'afficher le fameux Hello World. Nous pouvons ici voir la puissance du fichier include : au lieu de devoir mettre l'adresse, c'est à dire la valeur littérale, des différents appels à l'OS, nous pouvons simplement mettre un nom bien plus facile à retenir.

ret Permet de retourner à l'OS et finit le programme.

Code: Tout sélectionner
text:
   .db "Hello world !",0

Ceci est encore une instruction au compilateur. Il permet d'inclure dans le programme des octets de données, ici sous forme de chaîne de caractères, qui sera par la suite utilisable par le programme à l'aide du label "text:". Nous vous expliquerons tout ceci plus en détail dans le chapitre sur la mémoire.

Il ne vous reste plus qu'à compiler votre programme à l'aide du fichier .bat et envoyer le programme sur l'émulateur ou votre calculatrice.
Vous obtenez le résultat suivant :
Image

Vérifiez que le compilateur ne revoie pas d'erreurs. Si c'est le cas vérifiez que le .inc est bien dans le bon répertoire, de même que spasm64, et votre fichier exemple.ez80. Un programme qui a renvoyé des erreurs à la compilation ne fonctionnera sans doute pas.


Vous connaissez donc maintenant la structure générale d'un (petit) programme assembleur. Apprenons donc maintenant à le remplir !

* * *


II / Le zilog ez80

1 - Les registres

Les registres sont de petites zones de mémoire présentes directement dans la puce du processeur. Le processeur effectue toutes ses opérations à l'aide de ces registres qui ne peuvent qu'enregistrer une valeur, dont la fonction et la taille dépend du registre.

Nous pouvons distinguer plusieurs classes de registres :

  • Les registres de travail. C'est ceux que vous utiliserez le plus souvent.
  • Les registres d'index. Très puissants, ils permettent de récupérer des données en mémoire très facilement.
  • Les registres de contrôle. Le processeur utilise ces registres pour diverses opérations, ils sont parfois réduit à l'état d'un simple bit.

Les registres sont en général des zones de mémoire de 8bits. Cependant, le ez80 permet de combiner ces zones pour former des zones de 24 bits. Il est cependant à noter que la combinaison est fixe, et que les 8bits les plus supérieurs (ou msb) du registre ne peuvent pas être accédés. Ceci est un ancien artefact du jeu de données du z80 (le prédécesseur du ez80), qui travaillait sur des registres 8 bits & combinés en 16 bits. Il est aussi à noter que certains registres peuvent effectuer des actions particulières que d'autres ne peuvent pas.

Registres CPU de travail

Registres individuels 8 bits
RegistreUtilisation
AAccumulateur, il est le registre opérande et résultat des opérations mathématiques sur 8 bits
Fflags. Ce registre ne peut pas être directement lu/écrit. Il sert à enregistrer des feedbacks des instructions
Lutilisateur défini
Hutilisateur défini
Lutilisateur défini
Dutilisateur défini
Eutilisateur défini
Butilisateur défini
Cutilisateur défini


Registres combinés 24 bits

Il est à noter que ces registres proviennent de la combinaison des registres 8 bits. Une modification des registres 8 bits entraînera une modification des registres 24 bits. Ainsi, pour le registre HL l'octet de poids faible correspond au registre 8 bits L tandis que l'octet de poids moyen correspond au registre H. Il en est de même pour les autres registres. L'octet de poids fort n'a pas d'existence propre en tant que registre 8 bits, mais possède le nom de HLU, DLU ou BLU.
RegistreUtilisation
HLAccumulateur 24 bits, il est le registre opérande et résultat des opérations mathématiques sur 24 bits. Il peut aussi servir à indexer des données en mémoire
DEDestination, mais surtout utilisateur défini. Il est utilisé par certaines instructions comme le registre de destination de copie. Il peut aussi servir à indexer des données, mais avec moins de possibilités que HL
BCByte Counter, mais surtout utilisateur défini. Il est utilisé par certaines instructions comme un compteur. Il peut aussi servir à indexer des données, mais avec moins de possibilités que HL


Registres CPU de contrôle

RegistreUtilisation
SPLStackPointerLong, est un pointeur vers le sommet du stack. Il peut dans certains cas être utilisé comme un registre 24 bits de travail
PCProgramCounter, est un pointeur vers l'instruction actuellement exécutée. Certaines instructions peuvent le modifier
IInterrupt, est un registre utilisé dans le cadre des interruptions. C'est un registre 16 bits
MBASEMemoryBASE, est un registre utilisé dans le cadre d'exécution de code en mode z80, nous en parlerons à la toute fin de ce tutoriel
RRefresh, est un compteur du nombre d'instructions fetchées par le processeur, très rarement utilisé
ADLADL est un bit indiquant que le mode dans lequel le processeur travaille (z80/ez80)
MADLMADL est un bit indiquant que le processeur travaille dans un mode mixé z80&ez80
IEF1IEF1 est un bit indiquant que les interruptions de niveau 1 sont actives
IEF2IEF2 est un bit indiquant que les interruptions de niveau 2 sont actives


2 - Les instructions

Maintenant que nous avons vu que l'on pouvait utiliser des registres, comment les utilise-t-on ? Et d'ailleurs, comment on fait concrètement pour programmer en assembleur ? On va voir ici les quelques instructions que vous utiliserez 99% du temps.

On a vu qu'il était possible d'utiliser un certain nombre de registres dans nos programmes, qui sont en quelque sorte les variables en assembleur. Pour cela il suffit d'une instruction ld :
  • Pour mettre une valeur dans un registre 8 bits ou 24 bits on fait ld r,n où r est n'importe quel registre parmi A, B, C, D, E, H, L et BC, DE, HL et où n est un nombre entre 0 et 255 (8 bits) pour un registre 8 bits et entre 0 et 2^24 pour un registre 24 bits (ex : ld bc,12345 met 12345 dans BC)
  • Pour mettre un registre 8 bits dans un autre registre 8 bits, on fait ld r,r'r et r' sont n'importe quels registres parmi A, B, C, D, E, H, L
  • /! Il n'est pas possible de mettre un registre 24 bits dans un autre registre 24 bits : exemple ld hl,de est interdit !
C'est tout ce qu'il y a à savoir ! Maintenant, vous savez donc vous servir des registres. Il ne manque grosso modo plus qu'à savoir faire des conditions et des boucles ! Mais avant ça, on a besoin des sauts...

Les sauts et les labels
Avant de faire des If et des While like a boss, on a besoin de s'intéresser aux sauts. Mais je vous rassure, les sauts c'est vraiment pas compliqué ! Commençons par un exemple d'utilisation :
Code: Tout sélectionner
   jr proche
   ; ...quelques instructions
proche:
   jp loin
   ; ...
   ; Plein d'instructions
   ; ...
loin:

Pour sauter quelque part dans votre programme, il vous suffit de mettre un label à l'endroit où vous voulez sauter (ici, "proche" ou "loin" suivi du caractère deux points ':' ) et de faire soit jp monLabel ou jr monLabel. En pratique, un label sert à donner un nom à un endroit de votre programme, un identifiant en quelque sorte. Il peut être composé des caractères lettres MAJ+min, chiffres et underscore "_".

C'est bien beau tout ça mais c'est quoi la différence entre JP et JR ?

Bonne question ! Voici les avantages et inconvénients de chacun :
  • JP peut sauter à n'importe quel endroit du programme et de la mémoire alors que JR ne peut sauter qu'à un label qui est 128 octets avant ou 127 octets après
  • JP est légèrement plus lent que JR
  • JP prend 4 octets en mémoire alors que JR n'en prend que 2
Show/Hide spoilerAfficher/Masquer le spoiler
Si vous êtes curieux, la vraie différence entre JP (JumP) et JR (Jump Relative) est que JP prend en argument une adresse (ex : jp $D1023A saute à l'adresse $D1023A) alors que JR prend en argument un offset ie une position relative (ex : jr 45 saute 45 octets plus loin). Cet offset est un entier 8 bits signé, c'est pour cela que l'on ne peut aller que 128o en arrière ou 127o en avant.

Maintenant qu'on a vu comment sauter quelque part dans notre programme, on va voir comment sauter seulement si une condition est vérifiée !


Les sauts conditionnels, les while et les if

Spoiler alert : l'assembleur ne contient ni instruction While, ni instruction If. Mais je vous rassure tout de suite : il est possible de faire des comparaisons, et donc d'en imiter le fonctionnement !
En assembleur, pour faire une comparaison, on fait cp registre8b ou cp nombre8b. Ces deux instructions comparent, pour le premier un registre 8 bits (A B C D E H L) au registre A et pour l'autre un nombre 8 bits à A. À noter qu'il n'est pas possible de comparer des registres 24 bits de cette façon (ex : cp hl n'existe pas). Toute seule, CP ne sert pas à grand chose, elle ne fait que comparer. Il faut donc se servir du résultat pour sauter à un endroit du programme "si la condition est vérifiée". Voici par exemple comment faire un If en assembleur :
Code: Tout sélectionner
   cp b ; comparons A avec B
   jr nz,faux ; si A!=B, on n'exécute pas le corps du IF
   ; et donc si on n'a pas sauté, c'est que la condition est vérifiée
faux:
   ; suite du programme

Peut-être que vous commencez à comprendre comment ça marche. Sinon voilà l'explication : pour faire un saut conditionnel, il faut mettre un CP puis un JP ou JR auquel on "ajoute un argument" tels que Z, NZ, C, NC (les plus utilisés). Imaginons que nous faisons cp b, à la ligne d'après on peu mettre :
  • jp z,label Saute si A=B
  • jp nz,label Saute si A≠B
  • jp c,label Saute si A<B
  • jp nc,label Saute si A≥B
A noter que j'ai utilisé JP ici, mais il en va de même pour JR.

C'est tout ?

Eh oui, c'est tout ! Pour l'instant ça peut paraître un peu magique mais on verra plus en détail comment ça marche lorsqu'on parlera du registre F !
En attendant, essayez de faire un If / Else, une boucle While (boucle qui s'exécute tant qu'une condition est vérifiée) et une boucle For (boucle qui s'exécute N fois) avec ce que vous avez appris. Je vous mets la réponse en spoiler mais essayez d'y réfléchir ;)
Le If/Else :
Show/Hide spoilerAfficher/Masquer le spoiler
Code: Tout sélectionner
   cp b
   jr nz,else
   ; A=B
   jr endif
else:
   ; A≠B
endif:

Le While :
Show/Hide spoilerAfficher/Masquer le spoiler
Code: Tout sélectionner
while:
   cp b
   jr nz,endWhile
   ; corps
   jr while
endWhile:

Le For : (pour faire l'opération r = r-1, on utilise l'instruction dec r ou r est n'importe quel registre)
Show/Hide spoilerAfficher/Masquer le spoiler
Code: Tout sélectionner
   ld a,8 ; le nombre de fois qu'on veut exécuter la boucle
for:
   ; corps
   dec a
   cp 0 ; tant que A n'est pas égal à 0, on boucle
   jr nz,for

Les rageux diront que ce code n'est pas optimisé mais n'écoutez pas les rageux

Il y a bien entendu plusieurs solution et vous pouvez avoir trouvé quelque chose de différent mais qui fait quand même ce que vous voulez ;)

Pour information, il y a un moyen beaucoup plus simple de faire un boucle For. En effet, le processeur possède une instruction 2 en 1 : DJNZ (Decrement Jump NonZero). djnz label fait à elle toute seule l'équivalent de :
Code: Tout sélectionner
   dec b
   ld a,0 ; Dans l'instruction elle-même, A n'est pas mis à 0
   cp b
   jr nz,label


À noter que ce qu'utilise DJNZ est un JR, donc vous ne pourrez pas sauter plus loin que 128 octets en arrière. À noter aussi qu'après l'instruction, la valeur de B sera décrémentée de 1. Par contre la valeur de A n'est pas modifiée : A n'est donc pas mis à 0, c'est juste pour que vous compreniez comment marche l'instruction.
Il suffit alors de mettre le nombre de tours de boucle souhaités dans B, de mettre un label au début de la boucle et de mettre un DJNZ à la fin de celle-ci !


Ouf, on a fini le chapitre le plus dense du tutoriel ! Prenez bien le temps d'assimiler ces notions : elles vous serviront tout le temps !
Une fois que vous êtes à peu près à l'aise avec tout ça, il est temps de passer à l'utilisation de la "vraie" mémoire : parce que 10 octets de mémoire avec les registres communs, ce n'est vraiment pas beaucoup !

3 - La mémoire

La mémoire sur ez80 est un espace linéaire de 24-bits. Bien entendu, toute cette mémoire n'est pas que de la RAM. Plus spécifiquement, sur 83PCE, le processeur est interfacé avec une puce RAM de 256Ko, ainsi que 153Ko de VRAM.
Le processeur accède au contenu de la mémoire à l'aide d'une adresse. Une adresse est juste un nombre indiquant la 'case' dans laquelle la valeur sur laquelle nous voulons travailler se trouve. La RAM se situe à l'adresse $D00000, tandis que la VRAM se situe à l'adresse $D40000. L'adresse est dénommée pointeur.

il est usuel de décrire les adresses avec leur valeur en hexadécimal. C'est plus concis et nous avons une notion plus simple de la taille des zones mémoires (100 en hexadécimal correspond à 256). Le préfixe $ permet de bien signifier au compilateur que cette valeur est en hexadécimal.


Le ez80 dispose de plusieurs moyens de travailler sur la mémoire. Nous pouvons distinguer :
  • un mode d'adressage direct
  • un mode d'adressage indirect
Dans tous les cas, le processeur peut ainsi copier le contenu d'une case mémoire dans un registre à l'aide de l'instruction ld, ou 'LOAD'.

Mode d'adressage direct

Dans ce mode, nous allons donner au processeur directement la valeur du pointeur sur lequel nous travaillons. Le pointeur est fixé en dur dans le code du programme et ne peut pas être changé (sauf utilisation avancée).
Pour ce faire, il faut utiliser la syntaxe suivante de l'instruction load: ld reg,(adresse) pour charger une valeur dans un registre ou ld (adresse),reg pour enregistrer le contenu d'un registre en mémoire. Mettre l'adresse entre parenthèses permet de signifier que nous voulons charger la valeur pointée par l'adresse et non pas la valeur absolue de l'adresse dans le registre.

Il faut noter que tous les registres ne peuvent pas être utilisés par cette instruction. En registre 8 bits, seul A peut être utilisé. En registre 24 bits, HL, DE, BC, SP et les registres index IX, IY peuvent être utilisés.


Mode d'adressage indirect

Cette fois ci, au lieu de donner l'adresse au processeur, nous allons lui indiquer quel registre contient la valeur du pointeur sur la mémoire. Le processeur va lire cette valeur, chercher l'octet à cette adresse et la charger dans un registre ou copier le registre en mémoire.
Ici, c'est la syntaxe suivante de ld qui est utilisée : ld reg,(reg) et ld (reg),reg. Nous pouvons constater l'utilisation encore présente des parenthèses.

Il faut noter que tous les registres ne peuvent pas servir de destination et de pointeur. En général, le registre HL peut tout le temps servir d'adresse et de destination. DE et BC eux, ne peuvent servir d'adresse que avec A comme destination. Il convient de regarder dans la liste des instructions si la combinaison voulue existe.


Instructions de copie de bloc

Ces instructions sont très utiles si nous voulons copier des données séquentielles d'une destination vers une adresse cible. Elles peuvent copier en incrémentant les adresses à chaque fois, ou alors copier les données de manière inversée – en décrémentant les adresses.
Ces instructions sont des outils très puissants, car ils permettent de faire très rapidement un grand nombre d'actions, et ce de manière quasi-automatisée.

La première est ldir. Elle copie l'octet présent à l'adresse contenue dans HL vers l'adresse contenue dans DE puis incrémente HL et DE. Elle décrémente ensuite BC et répète si le résultat de la décrémentation n'est pas zéro.
Ainsi, si le nombre d'octets à copier doit être placé dans BC pour que cette copie soit efficace.

Il est important de considérer l'ordre du test et de la décrémentation de BC. Le test BC=0 est effectué après la décrémentation. Si la valeur de BC était zéro au début de l'instruction, celle-ci va copier 16777216 octets et va sans doute provoquer un RAM clear. BC correspond ainsi vraiment au nombre d'octet à copier.


L'autre instruction est lddr. Elle agit de la même manière que la précédente mais HL et DE sont décrémentés au lieu d'être incrémentés.

Gestion de la mémoire

La méthode précédente a le fort fâcheux désavantage de devoir mettre à chaque fois l'adresse. Celle-ci est parfois très compliquée et le programme peut travailler sur plusieurs octets de mémoire (voire même quelques milliers ou plus...). Heureusement, le compilateur permet de définir un nom pour chacune de ces zones. Plusieurs commandes peuvent réaliser ceci :

Code: Tout sélectionner
nom   .equ  adresse
#define nom adresse
nom:


Les deux premières sont notamment celles utilisées par l'include, si vous vous en souvenez. Cependant la troisième est particulière : il n'y pas d'adresse incluse dans la syntaxe :o C'est parce que l'adresse définie par cette syntaxe est implicite. L'adresse correspond à l'adresse que possède cette ligne dans votre programme.

Ceci permet de faire un lien parfait avec les commandes suivantes, de création de zone libre de mémoire dans le programme :

Code: Tout sélectionner
.db valeur
.dl valeur
.fill size [,valeur]


.db place un octet dans le déroulement du programme ayant pour valeur celle spécifiée, tandis que .dl en place trois.

Vous pouvez tenter dès à présent l'expérience suivante : placez .db $C9 au début de votre programme de test. Vous constatez que le processeur l'interprète comme l'instruction 'RET'. Ceci est pour vous montrer que les instructions et les valeurs en mémoire forment un seul grand ensemble compris par le processeur selon l'utilisation que fait le programmeur de la mémoire.


La dernière commande .fill permet de placer size octet dans le programme à l'adresse correspondante dans le déroulement du programme. Si la valeur est spécifiée alors celle-ci est répétée, sinon, un 0 est placé.

En conclusion, pour travailler sur des octets en mémoire, nous utilisons l'instruction ld. Celle-ci supporte plusieurs modes d'adressage, qui ont leur utilisation dans des contextes différents. .db et .dl permettent de placer des octets dans le programme avec une valeur définie


1 - Les flags
Les flags sont stockés dans le registre f, comme vu précédemment. Chaque flag est représenté par un bit (0/1) et permet de signifier certains événements provoqués par les instructions.
Flag – bit - nomFonction
S – bit 7 - signSi la dernière instruction mathématique a rendu un résultat négatif alors, ce flag est SET. Sinon, il est RESET
Z – bit 6 - zeroSi le dernier calcul a causé un registre égal à zéro, alors il est SET. Sinon, il est RESET
H – bit 4 - half-carry
P/V – bit 2 – parity/overflowCe flag vérifie deux conditions. Si le registre change de signe à travers une opération alors il est SET. Sinon, il est SET quand le registre possède une valeur PAIRE ou RESET quand la valeur est IMPAIRE
N – bit 1 – add/subSi la précédente opération était une soustraction alors, il est SET. Si c'était une addition alors il est RESET
C – bit 0 - carrySi le résultat de la précédente opération est trop large pour tenir dans le registre de destination, alors ce flag est SET. Sinon il est RESET.


Il est très important de noter que toutes les instructions n'affectent pas les flags de la même façon, et ceci peut-être très important dans l'exécution du programme. Il convient de vérifier dans la documentation si les flags voulus sont bien affectés par l'instruction utilisée.


Deux instructions spécifiques permettent de travailler sur le carry uniquement, fonction qui peuvent se révéler utiles pour créer des codes d'erreurs par exemple :

scf met le carry flag à 1
ccf inverse le carry flag

D'autres instructions peuvent être utilisées pour travailler sur les flags :

or a,a RESET le carry, tous les autres flags sont modifiés en fonctions de A
xor a,a RESET le carry, SET le flag z, met le registre A à zéro.

1 - Mathématique de base

Voyons maintenant comment effectuer des opérations sur les registres.

Le ez80 est un processeur 24-bits. Son ALU (ArithmeticLogicUnit) ne gère que des nombres 8 bits ou des nombres 24 bits. Il faut prendre en compte que l'ALU de la famille des ez80 est assez limitée par rapport à une unité d'un processeur moderne (x86 ou ARM).
Ainsi, celle-ci gère l'addition, la soustraction et la multiplication, ainsi que des opérations logiques sur 8 bits.

L'addition

Cette opération peut être effectuée sur 8 bits ou 24 bits. Dans les deux cas, le ez80 additionne l'accumulateur et un registre opérande et stocke le résultat dans l'accumulateur.
En 8 bits, l'accumulateur est le registre A. En 24 bits, ce sont les registres HL, IX, et IY.
La syntaxe est la suivante : add accumulateur, opérande

Il est à noter qu'en mode 8 bits, l'opérande est un registre 8 bits, en 24 bits un registre 24 bits. Vous ne pouvez pas additionner des registres de tailles différentes. De plus, vous pouvez additioner l'accumulateur à lui même (ce qui représente une multiplication par deux de l'accumulateur).


Il existe aussi l'instruction adc accumulateur, opérande qui ajoute l'opérande et la valeur du flag carry (cf au chapitre suivant) à l'accumulateur.

L'addition normale add sur 24 bits ne modifie pas tous les flags selon leur définition. Les flags Z, P/V et S ne sont pas modifiés.


Soustraction

De la même manière, le ez80 soustrait un registre opérande à l'accumulateur et stocke le résultat dans l'accumulateur. La syntaxe est la suivante : sub accumulateur, opérande. Cependant cette instruction n'existe que pour l'accumulateur 8 bits.

Pour les registres 24 bits, seule l'instruction sbc accumulateur, opérande existe, et l'accumulateur n'est que le registre HL. Ici, cette instruction soustrait l'opérande et la valeur du carry (comme pour adc). Pour obtenir une soustraction pure (sans l'action du carry), il faut d'abord mettre le carry à zéro :
Code: Tout sélectionner
or a,a
sbc hl, reg

Ici, l'instruction logique or que nous verrons par la suite sert pour mettre la valeur du carry à zéro.
Les flags sont modifiés selon leur définition après sbc et sub.

Multiplication

Le ez80 a introduit par rapport au z80 une instruction de multiplication. La syntaxe est la suivante : mlt reg. reg est un registre 24 bits.
Cette instruction multiplie le registre bas 8 bits avec le registre haut 8 bits. Prenons un exemple pour éclaircir.
Code: Tout sélectionner
ld h, 8
ld l, 3
mlt hl

Le résultat obtenu est HL=H*L, soit 24.

Attention, le résultat de cette multiplication n'est pas signé, c'est à dire qu'il ne prend pas en compte le complément à deux. Vous devrez vous-même rajouter du code pour multiplier deux nombres signés. De plus, le registre U (le plus haut nous vous rappelons), est mis à zéro lors de l'opération.


La multiplication accepte les registres HL, DE, BC, et SP. De plus, elle n'altère pas les flags.

1 - Les routines

1 - Le stack

Le stack, pile en français, permet de stocker des données temporairement à partir notamment des registres. Elle est organisée sous la forme LIFO, Last in, first out, équivalent au dernier entré, premier sorti.
La pile utilise le registre de la pile soit SP, SPL en mode d'adressage ADL comme pointeur de la pile. Il permet de stocker l'adresse où se trouve la dernière valeur de la pile. En mode SPL, ce registre contient des adresses de 3 octets. Toute modification de ce registre induit un déplacement de la postion du stack.

Si le registre SP est utilisé comme un registre de travail, alors le stack n'est plus utilisable et toute instruction travaillant sur le stack corrompra la mémoire.


Deux instructions sont essentielles pour travailler sur la pile : push et pop.
Pour pouvoir placer des données dans la pile on utilise l'instruction push et pop pour récupérer la dernière valeur placée. Lors de l'utilisation de ces instructions, le registre SPL est affecté.
Il est décrémenté de 3 lors de l'instruction push et incréménté de 3 lors de l'instruction pop.

Le stack grandit avec des adresses décroissantes, ce qui justifie la décrémentation de l'adresse lorsqu'une nouvelle valeur est placée sur le stack.


Lors d'un travail sur le stack, il faut faire attention de bien avoir la même valeur de SP en entrée et en sortie d'une fonction ou d'un programme. Sinon, cela peut conduire à un crash.

1 - Les OS call
1 - Les ports

Les ports sont très importants en assembleur : ils permettent entre autre, de gérer la pression des touches sur le clavier, configurer l'écran, communiquer par USB avec une autre calculatrice... c'est une notion incontournable de l'assembleur ! Ces derniers servent donc à faire le lien entre les composants de la calculatrice et le programmeur. Concrètement, on va, par exemple, pouvoir dire à l'horloge de la calculatrice de changer l'heure, à l'écran de s'éteindre, au port USB d'envoyer quelque chose... tout cela grâce aux ports !
Cependant, un élément va compliquer leur utilisation sur les calculatrice eZ80. En effet, les ports des calculatrices eZ80 sont dits "memory-mapped". Pour faire court, sur les calculatrices Z80 (TI-83+/TI-84+...), les ports étaient accessibles directement et normalement grâce aux instructions IN a,(port) pour récupérer la valeur d'un port, et OUT (port),a pour la modifier. Néanmoins, sur TI-83 Premium CE l'utilisation des instructions IN et OUT est prohibée et tenter de les utiliser ne causera soit rien, soit un RAM CLEARED. Ces restrictions sont supposées protéger et donner un sens au mode examen qui aurait pu être facilement reproduit et contourné dans le cas où un programme avait pu utiliser le port de la LED du mode examen afin d'en imiter le fonctionnement.
Heureusement, il existe quand même une solution pour accéder aux ports que nous permet d'utiliser Texas Instruments. En effet, certains ports sont "mappés en mémoire" c'est-à-dire qu'ils vont pouvoir être modifiés seulement en modifiant une adresse mémoire. C'est le cas notamment pour le clavier, l'écran, les timers, l'USB... mais bien entendu pas la LED du mode examen !

Les ports sont mappés à partir de l'adresse mémoire $E00000. Par exemple, le port du clavier est mappé en mémoire à partir de l'adresse $F50000. Afin de connaître tous les ports qui peuvent être utilisés, je vous conseille de suivre ce lien vers le wikiti. Vous y trouverez tous les ports qui ont été découverts jusqu'à maintenant. Pour savoir si un port est "memory-mapped", il suffit de voir si le champ "Memory-mapped address:" existe en haut de la page décrivant le port, dans la section "Synopsis".

Afin de mieux comprendre comment cela fonctionne, nous allons aborder un exemple simple, récupérer les minutes de l'heure actuelle :
Code: Tout sélectionner
ld hl,($F30004) ; On récupère les minutes.
call _dispHL ; On affiche HL qui contient le nombre de secondes

Expliquons un petit peu. Nous voyons à cette adresse que le port "Real-Time Clock" est le port numéro $8000 et est mappé à l'adresse $F30000 en mémoire. De plus, les minutes sont stockées du port $8004 à $8007 (toutefois les minutes n'allant que de 0 à 59, seulement un octet sera utilisé sur les quatre dédiés aux minutes). L'adresse à récupérer est donc $F30004. Ensuite, on affiche les minutes avec la fonction système _dispHL qui, comme son nom le laisse présager, affiche la valeur du registre HL à l'écran.

Nous allons, en dernier lieu et partiellement, voir l'utilisation des ports les plus importants, c'est-à-dire le port du clavier et de l'écran.

Utilisation du clavier
Le clavier a une utilisation spéciale et un peu plus complexe malgré sa fréquente utilisation dans les programmes. Il nécessite quelques connaissances.
Le port du clavier a pour numéro $A000 et est mappé à l'adresse $F50000. Avant de détecter la pression des touches, nous avons besoin d'initialiser le clavier. Pour cela nous allons devoir d'abord choisir un mode pour le clavier. Il en existe quatre mais seulement trois peuvent éventuellement nous intéresser :
  • Le mode "repos" : le clavier ne fait rien et ne scanne rien
  • Le mode "unique scan" : le clavier est scanné une fois, puis il revient au mode "repos"
  • Le mode "scan continu" : le clavier est scanné encore et encore indéfiniment...
Le mode qui va être utilisé le plus souvent est le deuxième mode, l'unique scan. Le code minimal pour lire le clavier est par conséquent :
Code: Tout sélectionner
   di ; On désactive les interruptions (obligatoire)
   ld hl,$F50000
   ld (hl),2 ; On met le clavier en mode "un seul scan"
   xor a,a
scan_wait:
   cp a,(hl) ; On attend que le clavier soit retourné en mode repos ce qui voudra dire que la détection des touches est terminée
   jr nz,scan_wait

   ; Quand la détection est terminée, on peut commencer à lire les ports
   ld a,($F50012)
   cp a,32 ; si la touche 2NDE a été pressée...
   call z,routine
   ei ; On peut réactiver les interruptions

Ici, nous avons pris l'exemple de la touche 2NDE dont l'état (enfoncée ou non) se situe à l'adresse $F50012 sur le bit 5. Pour connaître la correspondance des touches, je vous conseille de jeter un œil au tableau sur cette page. Comme on peut le voir on fait une pause (scan_wait) avant de lire le clavier, sans elle la lecture serait erronée donc ne l'oubliez pas !

Utilisation de l'écran
Le port de l'écran nécessite un chapitre entier mais nous allons aborder brièvement les bases de son utilisation. L'écran est par défaut en 16bits, c'est-à-dire qu'il y a 2^16=65536 couleurs disponibles par pixel. Chaque pixel possède une nuance de rouge, de vert et de bleu, les trois couleurs primaires en informatique. Par défaut (et je dis par défaut car il est possible de mettre l'écran en mode 8bits) chaque pixels possède donc 16 bits. Les cinq premiers bits codent pour le rouge, les six suivants pour le vert, et enfin les cinq derniers pour le bleu. On remarque que nous avons :
%0011001110010110

Maintenant, l'objectif est de savoir comment modifier la valeur d'un pixel à l'écran. Notre TI-83 Premium CE possède une vRam autrement dit une Vidéo RAM qui est un espace mémoire destiné à faire l'intermédiaire entre la mémoire et l'écran. Grosso modo, il suffit tout simplement de modifier la vRam pour que les pixels changent instantanément de couleur sur l'écran.
Nous savons que l'écran fait 320*240 pixels, avec 16 bits par pixel (=2 octets). La vRam fait donc logiquement 320*240*2, soit 153,6Ko, ce qui est énorme quand on sait que la RAM disponible à l'utilisateur fait autant !
La vRam est située à partir de l'adresse $D40000 sur 153600 octets. Prenons un exemple tout simple, on veut changer le 187ème pixel pour qu'il affiche du rose :
Code: Tout sélectionner
ld hl,%1111100000010000
ld (vRam+(186*2)),hl

À noter que comme le registre HL fait 24 bits, les 16 bits du pixel 187 seront bien modifiés, toutefois la moitié du pixel suivant (8 bits), le 188, sera mise à 0.
Il est alors possible de modifier une grande partie de l'écran grâce à un LDIR, et avec un peu de réflexion, d'afficher des sprites et des images à l'écran.

En rappel, n'oubliez pas d'aller voir le wikiti pour connaître tous les ports et leur adresse !
Voir la liste des ports connus à ce jour.

1 - Mathématique avancé
1 - Les timers

* * *


En construction


Merci à Dark_Coco pour le début du tutoriel, merci à Wistaro pour la mise en page :) .
Dernière édition par TheMachine02 le 26 Fév 2017, 20:33, édité 8 fois.
Avatar de l’utilisateur
TheMachine02Modo
Niveau 16: CC2 (Commandeur des Calculatrices)
Niveau 16: CC2 (Commandeur des Calculatrices)
Prochain niv.: 29.4%
 
Messages: 341
Images: 0
Inscription: 16 Jan 2013, 18:27
Genre: Non spécifié
Calculatrice(s):
MyCalcs profile
Classe: Médecine

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non lude Epharius » 26 Fév 2017, 19:29

Ah ouais, right, fallait faire ce tutoriel :p
Bon par contre le problème c'est que si je veux apporter des modifs je ne peux pas, donc à part si vous voulez vous la jouer perso ce serait cool de mettre à un endroit où tout le monde (du moins ceux qui en sont autorisés) peuvent modifier (genre wiki).

Genre :
xor a,a RESET le carry, RESET le flag z, met le registre A à zéro.

J'aurais bien voulu modifier en
xor a,a RESET le carry, SET le flag z, met le registre A à zéro.
:p
Le projet Geometry Dash est terminé ! N'hésitez pas à aller jeter un coup d’œil au topic du projet ! Vous pouvez le télécharger ici.

Unis par la flèche sacrée de cupidon :favorite:
Image
Avatar de l’utilisateur
EphariusPremium
Niveau 16: CC2 (Commandeur des Calculatrices)
Niveau 16: CC2 (Commandeur des Calculatrices)
Prochain niv.: 7%
 
Messages: 1107
Images: 4
Inscription: 08 Déc 2014, 17:38
Genre: Non spécifié
Calculatrice(s):
MyCalcs profile
Classe: Ensimag
GitHub: MathisLav

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non lude TheMachine02 » 26 Fév 2017, 19:40

Bah on pourra le mettre sur le wiki après, tu me dis les modifications & je change hein ça pose pas trop de problème :p De même tu m'envoies les parties et j'inclus tout ça :)
Avatar de l’utilisateur
TheMachine02Modo
Niveau 16: CC2 (Commandeur des Calculatrices)
Niveau 16: CC2 (Commandeur des Calculatrices)
Prochain niv.: 29.4%
 
Messages: 341
Images: 0
Inscription: 16 Jan 2013, 18:27
Genre: Non spécifié
Calculatrice(s):
MyCalcs profile
Classe: Médecine

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non lude Epharius » 26 Fév 2017, 20:20

ok, faudra que tu penses à corriger les fautes d'orthographe du coup ;)
Je ferai une partie plus tard quand j'aurai le courage.
Le projet Geometry Dash est terminé ! N'hésitez pas à aller jeter un coup d’œil au topic du projet ! Vous pouvez le télécharger ici.

Unis par la flèche sacrée de cupidon :favorite:
Image
Avatar de l’utilisateur
EphariusPremium
Niveau 16: CC2 (Commandeur des Calculatrices)
Niveau 16: CC2 (Commandeur des Calculatrices)
Prochain niv.: 7%
 
Messages: 1107
Images: 4
Inscription: 08 Déc 2014, 17:38
Genre: Non spécifié
Calculatrice(s):
MyCalcs profile
Classe: Ensimag
GitHub: MathisLav

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non lude Wistaro » 26 Fév 2017, 20:54

Merci beaucoup pour ce tutoriel de qualité !

Ça manquait en français :)
Nouveau sur le site, Anonymous ? Avant de poster sur le chat et sur le forum, n'oublie pas de lire les règles. En cas de problème, tu peux m'envoyer un message, je réponds rapidement.

Liens utiles:
Image
Découvre mes programmes et mon site!
Avatar de l’utilisateur
WistaroSuper Modo
Niveau 15: CC (Chevalier des Calculatrices)
Niveau 15: CC (Chevalier des Calculatrices)
Prochain niv.: 87.5%
 
Messages: 3153
Images: 37
Inscription: 25 Fév 2013, 16:21
Localisation: Toulouse
Genre: Homme
Calculatrice(s):
MyCalcs profile
Classe: Ingénieur en électronique
YouTube: Wistaro
Twitter/X: Wistaro
GitHub: Wistaro

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non lude Adriweb » 26 Fév 2017, 21:28

Hm, c'est (très) bien, mais tu risques d'atteindre les limites de caractères pour le post.
Il faudrait voir si tu peux faire 2-3 réponses bidon à ce topic, et je changerais le timestamp pour qu'ils apparaissent juste après le post initial.

Et sinon, des sources d'info en anglais:
- http://media.taricorp.net/83pa28d/welcome.html (z80)
- https://ce-programming.github.io/documentation/
- https://ez80.readthedocs.io/en/latest/
- ...
Image

MyCalcs: Help the community's calculator documentations by filling out your calculators info!
MyCalcs: Aidez la communauté à documenter les calculatrices en donnant des infos sur vos calculatrices !
Inspired-Lua.org: All about TI-Nspire Lua programming (tutorials, wiki/docs...)
Avatar de l’utilisateur
AdriwebAdmin
Niveau 16: CC2 (Commandeur des Calculatrices)
Niveau 16: CC2 (Commandeur des Calculatrices)
Prochain niv.: 80.1%
 
Messages: 14606
Images: 1216
Inscription: 01 Juin 2007, 00:00
Localisation: France
Genre: Homme
Calculatrice(s):
MyCalcs profile
Twitter/X: adriweb
GitHub: adriweb

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non lude grosged » 28 Fév 2017, 10:55

:~o !! C'est chouette comme tuto !! 8-)
Et en plus, mentionner les "routines-système" à travers l'exemple "Hello world" , c'est bien vu ;)
( j'dois être maso : j'en utilise jamais :p )

ça me fait penser que j'avais fébrilement commencé un tuto sur le processeur ARM (dont je ne connais pas encore toutes les subtilités, d'ailleurs...)
Dernière édition par grosged le 28 Fév 2017, 14:08, édité 1 fois.
Avatar de l’utilisateur
grosgedVIP++
Niveau 14: CI (Calculateur de l'Infini)
Niveau 14: CI (Calculateur de l'Infini)
Prochain niv.: 30.2%
 
Messages: 770
Images: 75
Inscription: 14 Sep 2011, 12:29
Genre: Non spécifié
Calculatrice(s):
MyCalcs profile

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non lude GalacticPirate » 28 Fév 2017, 12:45

J'ai corrigé quelques fautes d'orthographe et amélioré la syntaxe au début du tuto. Bon travail !
Mon profil TI-PlanetDevenez premium !
ImageImage
Modérateur de TI-Planet.org depuis décembre 2015, et membre de la communauté depuis Mai 2015.Cliquez ci-dessus pour faire un don à TI-Planet.org via PayPal et devenir Donateur ou Premium (à partir de 3€) ! Vous aurez accès à des générations illimitées et prioritaires avec Mviewer GX Creator, ainsi qu'au forum secret ! :bj: En plus, vous pourrez être orange sur le chat :troll:

Image
Avatar de l’utilisateur
GalacticPirateModo
Niveau 16: CC2 (Commandeur des Calculatrices)
Niveau 16: CC2 (Commandeur des Calculatrices)
Prochain niv.: 1.3%
 
Messages: 1156
Images: 2
Inscription: 23 Mai 2015, 10:36
Localisation: Hauts-de-Seine, France
Genre: Homme
Calculatrice(s):
MyCalcs profile
Classe: L3 Informatique
GitHub: GalacticPirate-Prog

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non lude sautax » 28 Fév 2017, 18:13

Merci beaucoup ! j'attendais un tutoriel en français depuis longtemps parce que je comprenais pas des choses sur les tutos anglais ....
:bj:
Mes projets :
-Environnement numworks sur PC
-TILIB
-AGAME
Avatar de l’utilisateur
sautaxProgrammeur
Niveau 11: LV (Légende Vivante)
Niveau 11: LV (Légende Vivante)
Prochain niv.: 21.6%
 
Messages: 65
Inscription: 21 Sep 2016, 14:31
Localisation: Quelque part dans le sud
Genre: Homme
Calculatrice(s):
MyCalcs profile
Classe: Terminale S-SI
YouTube: sautax

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non lude Dark coco » 28 Fév 2017, 22:38

Je propose de m'attaquer au "stack" :) . Si quelqu'un est déjà dessus, merci de me le signaler . ;)
(21:48:26) Hamza.S: Dark_coco : tu a écris 2 phrases sans faute
(19:15:10) Hayleia: ah ça je confirme que t'as appris à écrire à l'école de baptiste_leprovost :troll:
Avatar de l’utilisateur
Dark cocoProgrammeur
Niveau 14: CI (Calculateur de l'Infini)
Niveau 14: CI (Calculateur de l'Infini)
Prochain niv.: 65.8%
 
Messages: 95
Inscription: 15 Jan 2017, 14:39
Genre: Homme
Calculatrice(s):
MyCalcs profile
Classe: dut gte

Suivante

Retourner vers Langages alternatifs

Qui est en ligne

Utilisateurs parcourant ce forum: Aucun utilisateur enregistré et 12 invités

-
Rechercher
-
Social TI-Planet
-
Sujets à la une
Comparaisons des meilleurs prix pour acheter sa calculatrice !
Aidez la communauté à documenter les révisions matérielles en listant vos calculatrices graphiques !
Phi NumWorks jailbreak
123
-
Faire un don / Premium
Pour plus de concours, de lots, de tests, nous aider à payer le serveur et les domaines...
Faire un don
Découvrez les avantages d'un compte donateur !
JoinRejoignez the donors and/or premium!les donateurs et/ou premium !


Partenaires et pub
Notre partenaire Jarrety Calculatrices à acheter chez Calcuso
-
Stats.
957 utilisateurs:
>943 invités
>10 membres
>4 robots
Record simultané (sur 6 mois):
6892 utilisateurs (le 07/06/2017)
-
Autres sites intéressants
Texas Instruments Education
Global | France
 (English / Français)
Banque de programmes TI
ticalc.org
 (English)
La communauté TI-82
tout82.free.fr
 (Français)