[ EXELVISION ] initiation à l'assembleur pour TMS7020
Modérateurs : Papy.G, fneck, Carl
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
c'est la que l'on va se rendre compte si l'on a vraiment comprit ou pas
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
La synthèse vocale c'est le plus simple sur cette machine: il y a juste à appeler une fonction pour jouer, pause, arrêter... en renseignant où se trouve les données. Pour le reste tout se fait en interne. Le 7041 on s'en tape un peu 99.5% des fois.6502man a écrit :Je vais préparer la partie sur le VDP mais en restant très simple pour ne pas perdre tous le monde en route
Je fais l'impasse sur le 7041 et la synthèse vocale (on pourra l'aborder directement avec du code d'exemple).
Que la force soit avec toi pour expliquer le VDP... là c'est le double noeud du cerveau à tous les coups !
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
4) Le VDP
Video Display Processor de son nom complet, est le coprocesseur TMS3556 dédié à tout ce qui concerne l'affichage de l'EXL100.
Ce processeur vidéo dispose de 16 registres spécifiques et de sa propre RAM la VRAM.
3 modes d'affichages possible avec ce VDP : TEXTE, BITMAP, MIXTE.
TEXTE: résolution de 25 X 40 avec 2 couleurs par caractère (fond/forme) en mode alphanumérique ou mosaique, ce mode occupe 2000 octets de VRAM (caractères + attributs).
BITMAP: résolution de 320 X 250 avec 8 couleurs par pixels, ce mode occupe 30500 octets de VRAM (3 bits par pixels).
MIXTE: résolution paramétrable par ligne (TEXTE ou BITMAP) avec les contraintes de couleurs correspondantes, ce mode occupe de 2000 à 30500 octets de VRAM selon la configuration appliquée.
La VRAM installée sur l'EXL100 est de 32Ko, cette mémoire doit être configurée de manière à réserver les zones mémoires nécessaires pour chaque partie.
Cette opération d'initialisation doit être effectuée toujours en premier, dans tout programme assembleur autonome, ou utilisant un mode d'affichage différent du contexte existant.
Schéma de la répartition des différentes zones de la VRAM, l'ordre est totalement arbitraire puisque entièrement redéfinissable :
Code : Tout sélectionner
VDP
!
---------------------------------
! ! ! ! !
BAGC0 BAGC1 BAGC2 BAGC3 BAPA
BAGC0 : générateur de caractères N°0 Alphanumérique de 128 caractères
BAGC1 : générateur de caractères N°1 Alphanumérique de 128 caractères
BAGC2 : générateur de caractères N°2 Mosaique de 128 caractères
BAGC3 : générateur de caractères N°3 Alphanumérique ou Mosaique (1*) de 128 caractères
BAPA : début de la zone écran
Donc ces 5 zones doivent être définies en renseignant leurs adresses dans la VRAM, il faut bien entendu faire attention à ce que chaque zone ne se chevauche pas.
Chaque générateur de caractères occupe 1280 octets en VRAM, chaque caractère est défini sur 10 octets et chaque caractère à une résolution de 8 X 10.
Vous allez me dire mais comment on peut caser tout ça dans 32Ko si on utilise le mode BITMAP puisque 30500 + (4 * 1280) = 35620 octets alors que l'on à seulement 32768 octets de VRAM, et bien il faudra simplement définir 1 seul générateur de caractères et appliquer la même adresse aux 4 générateurs de caractères !!!
Définition d'un générateur de caractère:
Chaque caractère occupe 10 octets et sont enregistrés en VRAM de la manière suivante:
chaque dernière ligne des 128 caractères puis avant dernière ligne des 128 caractères, etc pour chaque ligne.
Exemple pour un générateur qui contiendrait les dessins de ABCD..Z, le numéro indique la ligne du caractère:
A9 B9 C9 D9 ... Z9
A8 B8 C8 D8 ... Z8
A7 B7 C7 D7 ... Z7
......................
A2 B2 C2 D2 ... Z2
A1 B1 C1 D1 ... Z1
A0 B0 C0 D0 ... Z0
Organisation des octets dans la zone affichable (2*):
Selon le mode choisi les données ne sont pas organisées de la même manières.
En mode Texte: pour chaque ligne on commence par placer l'attribut du 1er caractère (couleur, générateur,..) puis le code ASCII du 1er caractère, ensuite l'attribut du 2ème caractère puis le code ASCII du 2ème caractère, ainsi de suite sur 40 colonnes plus 2 octets de contrôles en répétant la même chose sur 25 lignes.
En mode BITMAP: 3 octets par bloc de 8 pixels BGR (bleu vert rouge) sur 120 octets + 2 octets de contrôles.
En mode MIXTE: chaque ligne est définie par les 2 octets de fin de la ligne précédente, donc si défini en mode texte 80 octets + 2 octets de contrôles si mode bitmap 120 octets + 2 octets de contrôles.
les 3 octets BGR successifs du mode bitmap sont définis de cette manière:
Code : Tout sélectionner
VRAM
1er octet : B B B B B B B B 1 1 1 1 0 0 0 0
2eme octet : G G G G G G G G 1 0 1 0 1 1 0 0
3eme octet : R R R R R R R R 1 1 0 0 1 0 1 0
= = = = = = = =
| | | | | | | |
| | | | | | | |-> Noir 0+0+0
| | | | | | |---> Rouge 0+0+1
| | | | | |-----> Vert 0+1+0
| | | | |-------> Jaune 0+1+1
| | | |---------> Bleu 1+0+0
| | |-----------> Cyan 1+1+0
| |-------------> Magenta 1+0+1
|---------------> Blanc 1+1+1
les attributs du mode texte(3*):
Alphanumérique=
Code : Tout sélectionner
Détail de l'octet attribut
BF GF RF CG1 CG0 INV DH DW
BF+GF+RF > défini la couleur du caractère en respectant le code BGR défini précédemment.
CG1+CG0 > défini le générateur de caractères à utiliser pour le caractère 00:BAGC0, 01:BAGC2, 10:BAGC1, 11:BAGC3.
INV > défini si inversion vidéo ou normal.
DH+DW > défini la taille du caractère 00= normal, 01= double largeur, 10= double hauteur, 11= double taille.
Code : Tout sélectionner
Détail de l'octet attribut
BF GF RF CG1 CG0 BB GB RB
BF+GF+RF > défini la couleur du caractère en respectant le code BGR défini précédemment.
CG1+CG0 > défini le générateur de caractères à utiliser pour le caractère.
BB+GB+RB > défini la couleur de fond du caractère en respectant le code BGR défini précédemment.
Les octets de contrôles:
Code : Tout sélectionner
121eme octet: BM GM RM C/G MR LR IR
BM+GM+RM > défini la couleur de la marge en respectant le code BGR défini précédemment.
C/G > pour le mode mixte permet de définir si la ligne est en mode texte ou bitmap.
MR > MASK permet de cacher l'affichage de la ligne (texte).
LR > active le soulignement (texte).
IR > active l'incrustation (texte).
122eme octet: I output > contrôle l'affichage (voir le datasheet pour plus de détails).
Les registres du VDP:
Il y a 8 registres 8 bits (0 à 7) et 8 registres 16 bits (8 à 15) dans le TMS3556.
Code : Tout sélectionner
0 Pointeur Pointeur de registre
1 COL Adresse LSB
2 ROW Adresse MSB
3 STATUS Etat du VDP
4 CM1 Paramétrage du VDP
5 CM2 Paramétrage du VDP
6 CM3 Paramétrage du VDP
7 CM4 Paramétrage du VDP
8 BAMT Adresse de début du buffer des données VDP
9 BAMP Adresse des accès VRAM du VDP (auto-incrémenté)
10 BAPA Adresse de début de l'écran (début des données affichées)
11 BAGC0 Adresse du générateur de caractères 0
12 BAGC1 Adresse du générateur de caractères 1
13 BAGC2 Adresse du générateur de caractères 2
14 BAGC3 Adresse du générateur de caractères 3
15 BAMTF Adresse de la fin du buffer des données VDP
Exemple pour écrire dans le registre 7 pour définir la couleur de bord en blanc:
Code : Tout sélectionner
MOVP #$07,P45
MOVP #$E0,P45
Exemple pour écrire dans le registre 10 et définir l’adresse de BAPA à $1000 (attention le VDP incrémente systématiquement l'adresse envoyée au registre par COL/ROW, donc pensez à envoyer l'adresse -1) :
Code : Tout sélectionner
MOVP #$01,P45 ; registre COL
MOVP #$FF,P45
MOVP #$02,P45 ; registre ROW
MOVP #$0F,P45
MOVP #$0A,P45 ; registre BAPA
MOVP #$00,P45 ; phase 1 LSB
MOVP #$00,P45 ; phase 2 MSB
Le VDP utilise certains registres comme pointeur pour faciliter l'accès à la VRAM, pour cela BAMP devient ACMP et COL + ROW deviennent ACMPxy, et le gros avantage c'est que ces pointeurs sont auto-incrémentés lors de chaque utilisation que ça soit en lecture ou en écriture, c'est très pratique
Pour écrire dans la VRAM on peut utilise le port P46, mais le CPU à aussi 2 instructions spécifique pour lire ou écrire dans la VRAM :
LVDP et WVDP.
Exelvision a aussi prévu des routines intégré dans la ROM du TMS7020, ces routines sont appelées par l'instruction TRAP x,ou x correspond au numéro de la routine dans la ROM, certaines de ces routines ont spécialement été crées pour la gestion du VDP, initialiser les générateurs de caractères, définir les adresses des pointeurs ACMP et ACMPxy ... (5*)
Voila pour la partie VDP, je ne pense pas avoir oublié de point important
Je vois déjà beaucoup de questions, mais j'ai volontairement condensé au maximum, pour que cela reste dans le cadre d'une initiation, je vais pas refaire la datasheet.
Par la suite nous verrons par les codes d'exemples plus en détails les points un peu plus complexe.
N’hésitez pas à consulter les docs sur le site de Daniel:
http://dcexel.free.fr/doc/
-----------------------------------------
(1*) Les générateurs Alphanumériques correspondent aux caractères texte classique.
Les générateurs mosaique correspondent aux caractéres semi graphique type Videotext.
(2*) En mode Texte 2 octets par caractère affiché soit (40*2) + 2 octets par ligne.
En mode Bitmap 3 octets par 8 pixels affichés soit (40*3) + 2 octets par ligne.
(3*) l'octet contenant le code ASCII à afficher est sur 7 bits le 8éme bit sert à definir si le caractère doit clignoter ou non.
(4*) Pour plus de détails sur les délimiteurs et leurs fonctionnement avec le TMS3556 voir le DATASHEET.
(5*) Pour la liste complète et détaillé voir page 46 à 57 du livre "Programmer en assembleur sur Exelvision".
L'adressage
C'est la manière dont le CPU va adresser la RAM, la VRAM ou les PORTS.
Le TMS7020 a plusieurs modes d'adressages, implicite, direct, immédiat, absolue, indexée, indirecte et relatif.
Lorsque l'on programme en assembleur nous allons préciser des adresses ou des registres, même des adresses contenues dans des registres, et le CPU va interpréter les différents modes d'adressages pour pouvoir accéder à ces adresses.
Implicite
Code : Tout sélectionner
pour toutes les instructions dont l'adressage n'est pas précisé car il est sous entendu.
Par exemple CLRC qui met à zéro la retenue, on ne précise pas ST.
Code : Tout sélectionner
pour toutes les instructions entre registres et ou les PORTS.
Par exemple ADD R10,R12 ou MOVP A,P46.
Code : Tout sélectionner
pour toutes les instructions ayant une donnée en source et un registre en destination et ou les PORTS.
Par exemple MOVP #$FF,P46 ou SUB #$10,R12 ou encore MOVD #$2536,R32.
Code : Tout sélectionner
pour toutes les instructions indiquant une adresse mémoire.
Par exemple LDA @$1000 ou STA @$2000 ou BR @C600.
Code : Tout sélectionner
pour toutes les instructions utilisant le registre B comme index d'adresse.
Par exemple LDA @$2000(B) [l'accumulateur seras chargé avec l'octet présent en ($2000+B)].
Code : Tout sélectionner
pour toutes les instructions utilisant 2 registres comme adresse.
Par exemple LDA *R12 (l'accumulateur seras chargé avec l'octet présent à l'adresse composé par la valeur en R11 et R12, si R11=$10 et R12=$00 l'adresse lu seras donc $1000).
Code : Tout sélectionner
pour toutes les instructions de saut conditionnel.
Par exemple JEQ,JNE, l'adresse de saut est calculé par rapport à l'adresse courante de PC dans la limite d'un déplacement de -128 par rapport à l'adresse courante et +127 par rapport à l'adresse courante.
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
Il va falloir que je relise tout. Je dois être un peu fatiguer pour ingurgiter le VDP a cette heure ci
j'ai aperçu une petit faute.
6502man a écrit : Exelvision à aussi prévu des routines intégré dans la ROM du TMS7020, ces routines sont appelées par l'instruction TRAP x,ou x correspond au numéro de la routine dans la ROM, certaines de ces routines ont spécialement étaient (été) crées pour la gestion du VDP, initialiser les générateurs de caractères, définir les adresses des pointeurs ACMP et ACMPxy ... (5*)
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
Malheureusement la logique fonctionnelle du VDP, la structuration des pixels en mémoire, et le mode de communication avec le CPU réduise beaucoup l'utilisabilité du mode graphique: si on veut avoir un affichage un peu rapide (animation) il faut des astuces qui prennent pas mal de mémoire, et ça reste impossible de faire un jeu d'action/arcade, même un jeu d'aventure animé.
Le mode texte par contre est très sympa... à utiliser en priorité pour un jeu/démo rapide.
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
Patience les premiers codes d'exemples arrivent
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
Affectation, addition, soustraction et muliplication.
Nous allons commencer par la base en programmation assembleur c'est à dires la manipulation des Accumulateurs ainsi que l'arithmétique de base.
A) L'affectation des Accumulateurs (A ou B) et/ou des registres (Rxx):
Code : Tout sélectionner
LDA @$C600 permet de charger la valeur présente en $C600 dans A.
MOV #$2F,B permet de charger la valeur $2F dans B.
MOV #$7C,R12 permet de charger la valeur $7F dans le registre R12.
MOV R12,A permet de copier la valeur présente dans le registre R12 vers A.
MOV A,B permet de copier la valeur de A dans B.
On peut aussi utiliser le mode d'adressage indirect:
Code : Tout sélectionner
MOV #$C6,R11 renseigne l'adresse de la case mémoire à lire
MOV #$00,R12 dans R11-R12.
LDA *R12 permet de charger la valeur présente en $C600 dans A.
Code : Tout sélectionner
CLR B met à zéro B
LDA @$2000(B) charge la valeur présente en ($2000+0) dans A.
B) Calculons avec les Accumulateurs et les registres.
Code : Tout sélectionner
ADD #$05,A addition $05 à A, le résultat est stocké dans A.
SUB #$4D,A soustrait $4D à A, le résultat est stocké dans A.
ADD A,R12 additionne la valeur contenu dans A et R12, le résultat
est stocké dans R12.
ADC #$11,A addition $11 à A et rajoute la retenue si positionnée à 1,
le resultat est stocké dans A.
SBB #$7F,R11 soustrait $7F à R11 et prend en compte la retenue si
positionné à 1, le resultat est stocké dans R11.
MPY R11,R12 multiplie R11 avec R12, le resultat est toujours dans A et B.
Nous implanterons nos programmes assembleur en $C700 et les donnèes en $C600.
Dans un premier temps les programmes seront utilisables avec le BASIC pour
appréhender plus facilement l'assembleur
a) l'addition 8bits:
Pour commencer nous allons écrire un programme d'addition 8bits et voir le résultat en RAM.
En $C600 et $C601 les 2 nombres à additionner, le résultat seras placé en $C602.
Code : Tout sélectionner
.ORG $C700
PUSH A ;on sauvegarde dans la pile l'état de A
PUSH B ;on sauvegarde dans la pile l'état de B
LDA @$C600 ;on charge l'octet présent en $C600 dans A
MOV A,B ;on copie A dans B
LDA @$C601 ;on charge l'octet présent en $C601 dans A
ADD B,A ;on addition B et A (le résultat est stocké dans A)
STA @$C602 ;on ecrit la valeur de A en $C602
POP B ;on restitue l'état de B
POP A ;on restitue l'état de A
RETS ;le programme est fini on laisse la main au système.
Code : Tout sélectionner
0003 C700 .ORG $C700
0004 C700 B8 PUSH A ;on sauvegarde dans la pile l'état de A
0005 C701 C8 PUSH B ;on sauvegarde dans la pile l'état de B
0006 C702 8A C6 00 LDA @$C600 ;on charge l'octet présent en $C600 dans A
0007 C705 C0 MOV A,B ;on copie A dans B
0008 C706 8A C6 01 LDA @$C601 ;on charge l'octet présent en $C601 dans A
0009 C709 68 ADD B,A ;on addition B et A (le résultat est stocké dans A)
0010 C70A 8B C6 02 STA @$C602 ;on ecrit la valeur de A en $C602
0011 C70D C9 POP B ;on restitue l'état de B
0012 C70E B9 POP A ;on restitue l'état de A
0013 C70F 0A RETS ;le programme est fini on laisse la main au système.
b) l'addition 16bits:
Cette fois nous allons faire une addition sur 16 bits, et vérifier le résultat en RAM.
En $C600 et $C601 le premier nombre 16 bits à additionner avec le deuxième nombre 16 bits présent en $C602 et $C603, le
résultat seras stocké en $C604 et $C605.
Code : Tout sélectionner
.ORG $C700
PUSH A ;on sauvegarde dans la pile l'état de A
PUSH B ;on sauvegarde dans la pile l'état de B
PUSH R30 ;on sauvegarde dans la pile l'état de R30
PUSH R31 ;on sauvegarde dans la pile l'état de R31
LDA @$c600 ;on charge l'octet présent en $C600 dans A
MOV A,R30 ;on copie A dans R30
LDA @$C601 ;on charge l'octet présent en $C601 dans A
MOV A,R31 ;on copie A dans R31
LDA @$C603 ;on charge l'octet présent en $C602 dans A
MOV A,B ;on copie A dans B
LDA @$C602 ;on charge l'octet présent en $C603 dans A
ADD R1,R31 ;on additionne A (R0) à R31 le résultat dans R31
ADC R0,R30 ;on additionne B (R1) à R30 en tenant compte de la retenue
; le résultat dans R30
MOV R30,A ;on copie R30 dans A
STA @$C604 ;on ecrit la valeur de A en $C604
MOV R31,A ;on copie R31 dans A
STA @$C605 ;on ecrit la valeur de A en $C605
POP R31 ;on restitue l'état de R31
POP R30 ;on restitue l'état de R30
POP B ;on restitue l'état de B
POP A ;on restitue l'état de A
RETS ;le programme est fini on laisse la main au système.
16 bits, mais cela ne permet pas de lire une valeur en mémoire mais uniquement des valeurs de registres.
Code : Tout sélectionner
0003 C700 .ORG $C700
0004 C700 B8 PUSH A ;on sauvegarde dans la pile l'état de A
0005 C701 C8 PUSH B ;on sauvegarde dans la pile l'état de B
0006 C702 D8 1E PUSH R30 ;on sauvegarde dans la pile l'état de R30
0007 C704 D8 1F PUSH R31 ;on sauvegarde dans la pile l'état de R31
0008 C706 8A C6 00 LDA @$c600 ;on charge l'octet présent en $C600 dans A
0009 C709 D0 1E MOV A,R30 ;on copie A dans R30
0010 C70B 8A C6 01 LDA @$C601 ;on charge l'octet présent en $C601 dans A
0011 C70E D0 1F MOV A,R31 ;on copie A dans R31
0012 C710 8A C6 03 LDA @$C603 ;on charge l'octet présent en $C603 dans A
0013 C713 C0 MOV A,B ;on copie A dans B
0014 C714 8A C6 02 LDA @$C602 ;on charge l'octet présent en $C602 dans A
0015 C717 48 01 1F ADD R1,R31 ;on additionne A (R0) à R31 le résultat dans R31
0016 C71A 49 00 1E ADC R0,R30 ;on additionne B (R1) à R30 en tenant compte de la retenue
0017 C71D ; le résultat dans R30
0018 C71D 12 1E MOV R30,A ;on copie R30 dans A
0019 C71F 8B C6 04 STA @$C604 ;on ecrit la valeur de A en $C604
0020 C722 12 1F MOV R31,A ;on copie R31 dans A
0021 C724 8B C6 05 STA @$C605 ;on ecrit la valeur de A en $C605
0022 C727 D9 1F POP R31 ;on restitue l'état de R31
0023 C729 D9 1E POP R30 ;on restitue l'état de R30
0024 C72B C9 POP B ;on restitue l'état de B
0025 C72C B9 POP A ;on restitue l'état de A
0026 C72D 0A RETS ;le programme est fini on laisse la main au système.
Technique utilisé pour le calcul sur 16bits:
Nous utiliserons la notation hexadécimal pour bien décomposer les opérations et bien comprendre la manière dont le CPU utilise
les donnèes, par exemple $2185 + $1791 est enregistré dans la mémoire sur 4 octets, le CPU calcul en 8 bits donc nous utilisons la retenue pour pouvoir
Code : Tout sélectionner
$21 $85
+ +
$17 $91
Vous aurez compris que l'on pouvait aussi grâce à la retenue avoir un résultat sur 3 octets dans le cas ou le total des 2 nombres dépasserais $FFFF.
Cela s'applique aussi pour les autres exemples de calculs 8 ou 16bits.
c) la soustraction 8bits:
Nous allons écrire un programme de soustraction 8bits et voir le résultat en RAM.
En $C600 et $C601 les 2 nombres à additionner, le résultat seras placé en $C602.
Code : Tout sélectionner
.ORG $C700
PUSH A ;on sauvegarde dans la pile l'état de A
PUSH B ;on sauvegarde dans la pile l'état de B
LDA @$c601 ;on charge l'octet présent en $C600 dans A
MOV A,B ;on copie A dans B
LDA @$C600 ;on charge l'octet présent en $C601 dans A
SUB B,A ;on soustrait B et A (le résultat est stocké dans A)
STA @$C602 ;on ecrit la valeur de A en $C602
POP B ;on restitue l'état de B
POP A ;on restitue l'état de A
RETS ;le programme est fini on laisse la main au système.
Code : Tout sélectionner
0003 C700 .ORG $C700
0004 C700 B8 PUSH A ;on sauvegarde dans la pile l'état de A
0005 C701 C8 PUSH B ;on sauvegarde dans la pile l'état de B
0006 C702 8A C6 01 LDA @$c601 ;on charge l'octet présent en $C600 dans A
0007 C705 C0 MOV A,B ;on copie A dans B
0008 C706 8A C6 00 LDA @$C600 ;on charge l'octet présent en $C601 dans A
0009 C709 6A SUB B,A ;on soustrait B et A (le résultat est stocké dans A)
0010 C70A 8B C6 02 STA @$C602 ;on ecrit la valeur de A en $C602
0011 C70D C9 POP B ;on restitue l'état de B
0012 C70E B9 POP A ;on restitue l'état de A
0013 C70F 0A RETS ;le programme est fini on laisse la main au système.
d) la soustraction 16bits:
Nous allons faire une soustraction sur 16 bits, et vérifier le résultat en RAM.
En $C600 et $C601 le premier nombre 16 bits à soustraire avec le deuxième nombre 16 bits présent en $C602 et $C603,
le résultat seras stocké en $C604 et $C605.
Code : Tout sélectionner
.ORG $C700
PUSH A ;on sauvegarde dans la pile l'état de A
PUSH B ;on sauvegarde dans la pile l'état de B
PUSH R30 ;on sauvegarde dans la pile l'état de R30
PUSH R31 ;on sauvegarde dans la pile l'état de R31
LDA @$c600 ;on charge l'octet présent en $C600 dans A
MOV A,R30 ;on copie A dans R30
LDA @$C601 ;on charge l'octet présent en $C601 dans A
MOV A,R31 ;on copie A dans R31
LDA @$C603 ;on charge l'octet présent en $C602 dans A
MOV A,B ;on copie A dans B
LDA @$C602 ;on charge l'octet présent en $C603 dans A
SUB R1,R31 ;on soustrait A à R31 le résultat dans R31
SBB R0,R30 ;on soustrait B à R30 en prenant en compte
; la retenue, le résultat est en R30
MOV R30,A ;on copie R30 dans A
STA @$C604 ;on ecrit la valeur de A en $C604
MOV R31,A ;on copie R31 dans A
STA @$C605 ;on ecrit la valeur de A en $C605
POP R31 ;on restitue l'état de R31
POP R30 ;on restitue l'état de R30
POP B ;on restitue l'état de B
POP A ;on restitue l'état de A
RETS ;le programme est fini on laisse la main au système.
Code : Tout sélectionner
0003 C700 .ORG $C700
0004 C700 B8 PUSH A ;on sauvegarde dans la pile l'état de A
0005 C701 C8 PUSH B ;on sauvegarde dans la pile l'état de B
0006 C702 D8 1E PUSH R30 ;on sauvegarde dans la pile l'état de R30
0007 C704 D8 1F PUSH R31 ;on sauvegarde dans la pile l'état de R31
0008 C706 8A C6 00 LDA @$c600 ;on charge l'octet présent en $C600 dans A
0009 C709 D0 1E MOV A,R30 ;on copie A dans R30
0010 C70B 8A C6 01 LDA @$C601 ;on charge l'octet présent en $C601 dans A
0011 C70E D0 1F MOV A,R31 ;on copie A dans R31
0012 C710 8A C6 03 LDA @$C603 ;on charge l'octet présent en $C602 dans A
0013 C713 C0 MOV A,B ;on copie A dans B
0014 C714 8A C6 02 LDA @$C602 ;on charge l'octet présent en $C603 dans A
0015 C717 4A 01 1F SUB R1,R31 ;on soustrait A à R31 le résultat dans R31
0016 C71A 4B 00 1E SBB R0,R30 ;on soustrait B à R30 en prenant en compte
0017 C71D ; la retenue, le résultat est en R30
0018 C71D 12 1E MOV R30,A ;on copie R30 dans A
0019 C71F 8B C6 04 STA @$C604 ;on ecrit la valeur de A en $C604
0020 C722 12 1F MOV R31,A ;on copie R31 dans A
0021 C724 8B C6 05 STA @$C605 ;on ecrit la valeur de A en $C605
0022 C727 D9 1F POP R31 ;on restitue l'état de R31
0023 C729 D9 1E POP R30 ;on restitue l'état de R30
0024 C72B C9 POP B ;on restitue l'état de B
0025 C72C B9 POP A ;on restitue l'état de A
0026 C72D 0A RETS ;le programme est fini on laisse la main au système.
e) la multiplication
Nous allons cette fois ci faire une multiplication 8 bits, et vérifier le résultat en RAM.
En $C600 et $C601 les deux nombres 8 bits à multiplier, le résultat seras stocké en $C604 et $C605.
Code : Tout sélectionner
.ORG $C700
PUSH A ;on sauvegarde dans la pile l'état de A
PUSH B ;on sauvegarde dans la pile l'état de B
PUSH R30 ;on sauvegarde dans la pile l'état de R30
PUSH R31 ;on sauvegarde dans la pile l'état de R31
LDA @$c600 ;on charge l'octet présent en $C600 dans A
MOV A,R30 ;on copie A dans R30
LDA @$C601 ;on charge l'octet présent en $C601 dans A
MOV A,R31 ;on copie A dans R31
MPY R30,R31 ;on multiplie les 2 nombres (le CPU met toujours le
résultat dans A et B)
STA @$C604 ;on ecrit la valeur de A en $C604
MOV B,A ;on copie B dans A
STA @$C605 ;on ecrit la valeur de A en $C605
POP R31 ;on restitue l'état de R31
POP R30 ;on restitue l'état de R30
POP B ;on restitue l'état de B
POP A ;on restitue l'état de A
RETS ;le programme est fini on laisse la main au système.
Code : Tout sélectionner
0003 C700 .ORG $C700
0004 C700 B8 PUSH A ;on sauvegarde dans la pile l'état de A
0005 C701 C8 PUSH B ;on sauvegarde dans la pile l'état de B
0006 C702 D8 1E PUSH R30 ;on sauvegarde dans la pile l'état de R30
0007 C704 D8 1F PUSH R31 ;on sauvegarde dans la pile l'état de R31
0008 C706 8A C6 00 LDA @$c600 ;on charge l'octet présent en $C600 dans A
0009 C709 D0 1E MOV A,R30 ;on copie A dans R30
0010 C70B 8A C6 01 LDA @$C601 ;on charge l'octet présent en $C601 dans A
0011 C70E D0 1F MOV A,R31 ;on copie A dans R31
0012 C710 4C 1E 1F MPY R30,R31 ;on multiplie les 2 nombres (le CPU met toujours le résultat dans A et B)
0013 C713 8B C6 04 STA @$C604 ;on ecrit la valeur de A en $C604
0014 C716 62 MOV B,A ;on copie B dans A
0015 C717 8B C6 05 STA @$C605 ;on ecrit la valeur de A en $C605
0016 C71A D9 1F POP R31 ;on restitue l'état de R31
0017 C71C D9 1E POP R30 ;on restitue l'état de R30
0018 C71E C9 POP B ;on restitue l'état de B
0019 C71F B9 POP A ;on restitue l'état de A
0020 C720 0A RETS ;le programme est fini on laisse la main au système.
Voila c'est fini pour la première leçon
Pour ceux qui suivent et qui ont compris, proposez moi un code pour une addition de 2 nombres de 32 bits,
( $1547FF15 + $EF971456 ) le résultat seras sur 5 octets
Les programmes ci dessus sont inclus dans l'archive LESSON1.ZIP
------------------------------------------------------------------------------------------------------------------------------------------------------------
Qu'est ce qui vous faut pour programmer en assembleur pour EXL100 sous windows :
1: configurer DCexel en mode EXL100 sans lecteur de disquette et avec la cartouche Basic.- DCexel version développeur (à demander à Daniel)
- TASM
- Le fichier de correspondance des mnémoniques TMS7020 pour TASM
- l'include spécifique pour le 7020 (merci Jester)
2: dans le fichier LESSON1.ZIP importer en mode "simulation de clavier" le fichier basic correspondant à l'exercice que vous voulez.
3: executer le programme Basic
4: Le mode mise au point dans DCexel : saisir l'adresse C700 dans la case adresse pour visualiser le code désassemblé ou C600 pour vérifier les donnèes utiliser par les programmes. 5: l'assemblage d'un fichier source en assembleur: allez dans le répertoire ASM et prenez un des fichier .ASM et faites le glisser sur "_goasm.bat", vous obtiendrez 2 nouveaux fichiers .LST et .BIN, sauf si il y a une erreur dans ce cas le .BIN ne seras pas crée.
le fichier .LST correspond au listing désassemblé avec les codes hexa des instructions, le .BIN est le binaire assemblé prêt à être utiliser dans l'EXL100.
6: Convertir des données binaires pour le BASIC: utiliser l'outil de Jester BIN2EXL, qui vous permettra de convertir vers une multitude de format, à télécharger ICI.
Je pense que je n'ai rien oublié et pas fait d'erreur
Amusez vous bien, la prochaine leçon seras sur les opérations logiques, et après on pourras passer à la partie la plus fun l'affichage
J'éditerais le post au fur et à mesure, donc à visiter régulièrement
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
C'est clair et ça donne rudement envie.
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
par contre je ne sais pas comment le transformer en DATA pour le listing BASIC
Code : Tout sélectionner
.ORG $C700
PUSH A ;on sauvegarde dans la pile l'état de A
PUSH B ;on sauvegarde dans la pile l'état de B
PUSH R30 ;on sauvegarde dans la pile l'état de R30
PUSH R31 ;on sauvegarde dans la pile l'état de R31
PUSH R32 ;on sauvegarde dans la pile l'état de R32
PUSH R33 ;on sauvegarde dans la pile l'état de R33
PUSH R34 ;on sauvegarde dans la pile l'état de R34
PUSH R35 ;on sauvegarde dans la pile l'état de R35
LDA @$c600 ;on charge l'octet présent en $C600 dans A
MOV A,R30 ;on copie A dans R30
LDA @$C601 ;on charge l'octet présent en $C601 dans A
MOV A,R31 ;on copie A dans R31
LDA @$C602 ;on charge l'octet présent en $C602 dans A
MOV A,R32 ;on copie A dans R32
LDA @$C603 ;on charge l'octet présent en $C603 dans A
MOV A,R33 ;on copie A dans R33
LDA @$C604 ;on charge l'octet présent en $C604 dans A
MOV A,R34 ;on copie A dans R34
LDA @$C605 ;on charge l'octet présent en $C605 dans A
MOV A,R35 ;on copie A dans R35
LDA @$C606 ;on charge l'octet présent en $C606 dans A
MOV A,B ;on copie A dans B
LDA @$C607 ;on charge l'octet présent en $C607 dans A
ADD R0,R33 ;on additionne A (R0) à R33 le résultat dans R33
ADD R1,R32 ;on additionne B (R1) à R32 le résultat dans R32
ADD R35,R31 ;on additionne R35 à R31 le résultat dans R31
ADC R34,R30 ;on additionne R34 à R30 en tenant compte de la retenue
; le résultat dans R30
MOV R30,A ;on copie R30 dans A
STA @$C608 ;on ecrit la valeur de A en $C608
MOV R31,A ;on copie R31 dans A
STA @$C609 ;on ecrit la valeur de A en $C609
MOV R32,A ;on copie R32 dans A
STA @$C60A ;on ecrit la valeur de A en $C60A
MOV R33,A ;on copie R33 dans A
STA @$C60B ;on ecrit la valeur de A en $C60B
POP R35 ;on restitue l'état de R35
POP R34 ;on restitue l'état de R34
POP R33 ;on restitue l'état de R33
POP R32 ;on restitue l'état de R32
POP R31 ;on restitue l'état de R31
POP R30 ;on restitue l'état de R30
POP B ;on restitue l'état de B
POP A ;on restitue l'état de A
RETS ;le programme est fini on laisse la main au système.
Si j'ai bien compris A=R0 et B=R1. si c'est le cas tu a fait une inversion dans tes exemples
Code : Tout sélectionner
ADD R1,R31 ;on additionne A (R0) à R31 le résultat dans R31
ADC R0,R30 ;on additionne B (R1) à R30 en tenant compte de la retenue
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
@kikich: Je vois que tu as suivi la première leçon
Pour ton premier code il y a 2 choses importantes à rectifier (à première vue) :
-dans la partie addition le premier ADD est correcte, par contre à partir du deuxième il faut impérativement utiliser ADC jusqu'au dernier octet, la retenue doit se reporter si il y lieu d'octet en octet.
Exemple = $BCEFF7FC + $DACEF4FE = $197BEECFA décomposé en opération 8 bits: (1) représente la retenue qui est prise en compte pour l'addition suivante
$FC + $FE = (1) $FA
$F7 + $F4 = (1) $EB >>> si tu n'utilises pas ADC le résultat seras faux $EB au lie de $EC
$EF + $CE = (1) $BD >>> si tu n'utilises pas ADC le résultat seras faux $BD au lieu de $BE
$BC+ $DA = (1) $96
Ensuite on arrive au deuxième point.
-Je demandais "le résultat seras sur 5 octets", bon d'accord c'était un peu vache car je n'ai pas donné d'exemple pour ça.
Donc il te faut en plus rajouter une ligne d'addition comme ceci:
ADC #00,R36 (va permettre de reporter la retenu si le résultat dépasse les 32bits)
et bien entendu penser à l’écrire en RAM
Et ne pas oublier de rajouter l'include nécessaire en début de fichier et le .END en fin de fichier pour l'assembler avec TASM
Pour l'importer dans Dcexel tu utilises soit la solution que j'ai indiqué dans le post ou directement dans l'émulateur avec la fonction "charger un fichier binaire" dans la mise au pont.
Et tu pourras tester ton programme et vérifier par toi même si il fonctionne (c'est la meilleur solution pour apprendre), bien entendu si tu n'y arrives pas demande et je t'aiderai
La semaine prochaine je prépare la partie opération logique ...
Pour te faciliter la tache voici le code asm en DATA pour le Basic:
Code : Tout sélectionner
10000 DATA 184,200,216,30,216,31,216,32,216,33,216,34,216,35,138,198
10010 DATA 0,208,30,138,198,1,208,31,138,198,2,208,32,138,198,3
10020 DATA 208,33,138,198,4,208,34,138,198,5,208,35,138,198,6,192
10030 DATA 138,198,7,72,0,33,72,1,32,72,35,31,73,34,30,18
10040 DATA 30,139,198,8,18,31,139,198,9,18,32,139,198,10,18,33
10050 DATA 139,198,11,217,35,217,34,217,33,217,32,217,31,217,30,201
10060 DATA 185,10
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
Arithmétique logique, décalage et comparaisons
Cette deuxième partie va aborder tout ce qui concerne les opérations d’arithmétiques logiques, qui est une partie très importante de la programmation en assembleur.
Dans la foulée nous verrons aussi les décalages binaires très utiles.
Et pour finir cette partie nous aborderons les comparaisons aussi essentiel en assembleur.
1) AND, OR, XOR, INV
OU EST XOR ???
(oui c'est un jeu de mot facile)
Code : Tout sélectionner
AND permet de faire un ET binaire.
AND #$21,A
OR permet de faire un OU binaire.
OR #$37,B
XOR permet de faire un OU eXclusif binaire.
XOR #$14,A
INV permet de faire une inversion binaire.
INV A
Explications:
10101101
AND 11111111
10101101 résultat
OR 11110101
11111101 résultat
XOR 01010101
10101000 résultat
INV
01010111 résultat
A noter qu'il existe aussi ANDP, ORP et XORP qui permettent de faire la même chose mais vers
les ports au lieu des registres.
2) RL, RLC, RR, RRC
Code : Tout sélectionner
RL effectue un décalage de tous les bits vers la gauche, le bit 7 est copié dans la retenue
et dans le bit 0.
RLC effectue un décalage de tous les bits vers la gauche, la retenue est copié dans le bit 0
et le bit 7 est copié dans la retenue.
RR effectue un décalage de tous les bits vers la droite,le bit 0 est copié dans la retenue
et dans le bit 7.
RRC effectue un décalage de tous les bits vers la droite,la retenue est copié dans le bit 7
et le bit 0 est copié dans la retenue.
Explications:
10101101
RL 01011011 résultat
RLC 10110111 résultat
RR 11011011 résultat
RRC 11101101 résultat
3) CMP, CMPA
Code : Tout sélectionner
CMP permet de comparer deux registres, ou une valeur et un registre.
CMPA permet de comparer l'accumulateur et l'octet présent à l'adresse indiquée en adressage
immédiat,indirect ou indéxé.
CMP A,B compare A et B ( B-A ) le STATUS (ST) du CPU est positionné en fonction du résultat de cette
comparaison, les registres ne sont pas modifiés.
CMPA *R20 compare l'accumulateur A avec l'octet présent à l'adresse indiqué par R19-R20
Les codes d'exemples:
Comme précédemment le programme en $C700 les données en $C600.
a) Convertir une valeur hexadécimal en 2 caractères ASCII.
En $C600 la valeur hexadécimal à convertir, $C602 et $C603 les 2 caractères ASCII correspondant.
Code : Tout sélectionner
PUSH A ;on sauvegarde dans la pile l'état de A
PUSH B ;on sauvegarde dans la pile l'état de B
PUSH R30 ;on sauvegarde dans la pile l'état de R30
PUSH R31 ;on sauvegarde dans la pile l'état de R31
LDA @$c600 ;on charge l'octet présent en $C600 dans A
MOV A,B ;on copie A dans B
AND #$F0,A ;on applique un ET pour ne conserver que la partie gauche
; de la valeur hexadécimal dans A.
CLRC ;on met le bit de retenue à zéro.
RR A ;on applique un décalage binaire à droite.
RR A ;on applique un décalage binaire à droite.
RR A ;on applique un décalage binaire à droite.
RR A ;on applique un décalage binaire à droite.
AND #$0F,B ;on applique un ET pour ne conserver que la partie droite
; de la valeur hexadécimal dans B.
MOVD #HexaAscii,R31 ;on met l'adresse de la chaîne ASCII dans R30/R31.
ADD R0,R31 ;on additionne la valeur hexa à l'adresse.
ADC #0,R30 ;on additionne avec retenue 0 à la partie basse de l'adresse.
LDA *R31 ;on charge le code ASCII dans A.
STA @$C602 ;on écrit le résultat à l'adresse $C602
MOVD #HexaAscii,R31 ;on met l'adresse de la chaîne ASCII dans R30/R31.
ADD R1,R31 ;on additionne la valeur hexa à l'adresse.
ADC #0,R30 ;on additionne avec retenue 0 à la partie basse de l'adresse.
LDA *R31 ;on charge le code ASCII dans A.
STA @$C603 ;on écrit le résultat à l'adresse $C603
POP R31 ;on restitue l'état de R31
POP R30 ;on restitue l'état de R30
POP B ;on restitue l'état de B
POP A ;on restitue l'état de A
RETS ;le programme est fini on laisse la main au système.
HexaAscii .TEXT "0123456789ABCDEF"
Code : Tout sélectionner
0003 C700 .ORG $C700
0004 C700 B8 PUSH A ;on sauvegarde dans la pile l'état de A
0005 C701 C8 PUSH B ;on sauvegarde dans la pile l'état de B
0006 C702 D8 1E PUSH R30 ;on sauvegarde dans la pile l'état de R30
0007 C704 D8 1F PUSH R31 ;on sauvegarde dans la pile l'état de R31
0008 C706 8A C6 00 LDA @$c600 ;on charge l'octet présent en $C600 dans A
0009 C709 C0 MOV A,B ;on copie A dans B
0010 C70A 23 F0 AND #$F0,A ;on applique un ET pour ne conserver que la partie gauche
0011 C70C ; de la valeur hexadecimal dans A.
0012 C70C B0 CLRC ;on met le bit de retenue à zéro.
0013 C70D BC RR A ;on applique un décalage binaire à droite.
0014 C70E BC RR A ;on applique un décalage binaire à droite.
0015 C70F BC RR A ;on applique un décalage binaire à droite.
0016 C710 BC RR A ;on applique un décalage binaire à droite.
0017 C711 53 0F AND #$0F,B ;on applique un ET pour ne conserver que la partie droite
0018 C713 ; de la valeur hexadecimal dans B.
0019 C713 88 C7 38 1F MOVD #HexaAscii,R31 ;on met l'adresse de la chaine ASCII dans R30/R31.
0020 C717 48 00 1F ADD R0,R31 ;on additionne la valeur hexa à l'adresse.
0021 C71A 79 00 1E ADC #0,R30 ;on additionne avec retenue 0 à la partie basse de l'adresse.
0022 C71D 9A 1F LDA *R31 ;on charge le code ASCII dans A.
0023 C71F 8B C6 02 STA @$C602 ;on ecrit le résultat à l'adresse $C602
0024 C722 88 C7 38 1F MOVD #HexaAscii,R31 ;on met l'adresse de la chaine ASCII dans R30/R31.
0025 C726 48 01 1F ADD R1,R31 ;on additionne la valeur hexa à l'adresse.
0026 C729 79 00 1E ADC #0,R30 ;on additionne avec retenue 0 à la partie basse de l'adresse.
0027 C72C 9A 1F LDA *R31 ;on charge le code ASCII dans A.
0028 C72E 8B C6 03 STA @$C603 ;on ecrit le résultat à l'adresse $C603
0029 C731 D9 1F POP R31 ;on restitue l'état de R31
0030 C733 D9 1E POP R30 ;on restitue l'état de R30
0031 C735 C9 POP B ;on restitue l'état de B
0032 C736 B9 POP A ;on restitue l'état de A
0033 C737 0A RETS ;le programme est fini on laisse la main au système.
0034 C738
0035 C738
0036 C738 30 31 32 33 HexaAscii .TEXT "0123456789ABCDEF"
0036 C73C 34 35 36 37
0036 C740 38 39 41 42
0036 C744 43 44 45 46
Attend l'appui d'une touche parmi 4 possibles.
le code ASCII de la touche est écrit en $C600.
pour réaliser cela nous allons simplement scruter l'adresse R3 (VALUE0)
cette adresse est mise à jour dès qu'une touche du clavier est enfoncée
ou dès que le joystick 0 est actionné.
Code : Tout sélectionner
PUSH A ;on sauvegarde dans la pile l'état de A.
attkey
MOV VALUE0,a ;on récupère la valeur de la touche saisie ou pas.
MOV #04,VALUE0 ;pour éviter une répétition de touches nous réinitialisons
; VALUE0 à $04 qui correspond à aucune action.
CMP #$31,a ;on compare A avec $31 (le code ASCII de la touche '1').
JNE NOT_1 ;si ce n'est pas égal alors on va à NOT_1.
JMP @Key ;si c'est égal on va à KEY.
NOT_1
CMP #$32,a ;on compare A avec $32 (code de la touche '2').
JNE NOT_2 ;si ce n'est pas égal alors on va à NOT_2.
JMP @Key ;si c'est égal on va à KEY.
NOT_2
CMP #$33,a ;idem avec la touche '3'.
JNE NOT_3
JMP @Key
NOT_3
CMP #$34,a ;idem avec la touche '4'.
JNE NotKey
JMP @Key
NotKey
JMP attkey ;si cela ne correspond à aucune des touches prévus on reboucle.
Key
STA @$c600
POP A ;on restitue l'état de A
RETS
Code : Tout sélectionner
0003 C700 .ORG $C700
0004 C700 B8 PUSH A ;on sauvegarde dans la pile l'état de A.
0005 C701 attkey
0006 C701 12 03 MOV VALUE0,a ;on récupére la valeur de la touche saisie ou pas.
0007 C703 72 04 03 MOV #04,VALUE0 ;pour éviter une répétition de touches nous réinitialisons
0008 C706 ; VALUE0 à $04 qui correspond à aucune action.
0009 C706 2D 31 CMP #$31,a ;on compare A avec $31 (le code ASCII de la touche '1').
0010 C708 E6 02 JNE NOT_1 ;si ce n'est pas égal alors on va à NOT_1.
0011 C70A E0 14 JMP @Key ;si c'est égal on va à KEY.
0012 C70C NOT_1
0013 C70C 2D 32 CMP #$32,a ;on compare A avec $32 (code de la touche '2').
0014 C70E E6 02 JNE NOT_2 ;si ce n'est pas égal alors on va à NOT_2.
0015 C710 E0 0E JMP @Key ;si c'est égal on va à KEY.
0016 C712 NOT_2
0017 C712 2D 33 CMP #$33,a ;idem avec la touche '3'.
0018 C714 E6 02 JNE NOT_3
0019 C716 E0 08 JMP @Key
0020 C718 NOT_3
0021 C718 2D 34 CMP #$34,a ;idem avec la touche '4'.
0022 C71A E6 02 JNE NotKey
0023 C71C E0 02 JMP @Key
0024 C71E NotKey
0025 C71E E0 E1 JMP attkey ;si cela ne correspond à aucune des touches prévus on reboucle.
0026 C720 Key
0027 C720 8B C6 00 STA @$c600
0028 C723 B9 POP A ;on restitue l'état de A
0029 C724 0A RETS
Les programmes ci dessus sont inclus dans l'archive LESSON2.ZIP
Je ne vous fourni pas de programme Basic, vous aurez vite adapter les précédents pour ceux la qui sont très simple
Dans l'archive les sources et les binaires directement importable en $C700 dans l'émulateur
Amusez vous bien
Je vais essayer d'être plus rapide pour la suite
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
Re: [ EXELVISION ] initiation à l'assembleur pour TMS7020
On peut également se rendre compte que de simples opérations sur 16 bits (addition et soustraction) semblent très compliquées à faire (de prim abord). Je n'ose même pas imaginer la longueur du programme pour une division
J'ai cependant une question. Peut-on modifier l'adresse de la pile?
A ce propos
Tout dépend si l'on prend la partie haute ou la partie basse de la zone d'empilage. Sur 680x0, on utilise traditionnellement le registre d'adresse A7 comme pile qui se situe par le système à l'extrémité haute de la ram [d'ou -(A7) pour empiler]. Mais si on définie une zone quelconque dans la ram (LEA $1000.W,A7 par exemple) on peut empiler en incrémentant l'adresse__sam__ a écrit :C'est marrant cette pile qui monte quand on push des trucs.. chez motorola (6809, 680x0) la pile descend quand on push (au niveau des adresses mémoire).