Revenir au plan du site
Comment réaliser des calculs en nombre flottants en assembleur?
L'Amstrad CPC est une machine très bien conçue qui intègre une gestion des nombres flottants plutôt performante (qui éclate presque tous les autres 8 bits). Seul problème, pour pouvoir l'utiliser, il faut que le système reste actif, c'est à dire TOUT le système et ça ne nous intéresse pas. Si toutefois vous souhaitez utiliser les nombres flottants avec le système, je ne saurais que vous conseiller de regarder la rubrique des liens ou n'importe quel ouvrage décrivant les vecteurs systèmes.
Vecteurs systèmes Mathématiques :
[CPC-Alive]
Une librairie mathématique classique intègre de nombreuses fonctions, à commencer par les outils de conversion!
- Il faut être capable de convertir des nombres entiers (ceux que le Z80 comprend nativement) dans le format flottant.
- Il faut être capable de convertir les nombres flottants en entiers que le processeur puisse traiter plus facilement.
- La librairie du BASIC Locomotive propose une conversion d'un nombre 32 bits signé en flottant et vice-verse (ouais, bof).
Enfin, la librairie doit être capable de calculer les opérations les plus courantes, multiplication, division, addition, soustraction, mais aussi les sinus, cosinus, racine, puissance et surtout la comparaison!
La librairie de Zeda va plus loin avec les fonctions "arc", des fonctions optimisées pour certaines valeurs remarquables, le NEG, la valeur absolue et niveau conversion des traitements depuis et vers l'ASCII. Enfin, l'utilisation des registres est optimisé avec de façon générale :
- AHL comme premier opérande
- CDE comme deuxième opérande
- AHL en résultat
L'utilisation d'une telle librairie consiste à écrire le code d'exécution en notation polonaise inverse. C'est à dire que contrairement aux programmes classiques dans lesquels vos expressions de calculs sont converties en notation polonaise inverse sans que vous en sachiez quoi que ce soit, en utilisant l'algorithme Shunting Yard de Edsger Dijkstra, cette fois ce sera à VOUS de faire le travail!
T'inquiète pas, ça va bien se passer ;)
Transformer vos expressions de calcul en notation polonaise inverse
L'idée de départ est de proposer une liste d'instructions à exécuter, permettant de se passer des parenthèses et intégrant par l'ordre d'exécution, les priorités.
Ainsi
...devient
3
4
* ; multiplier 3 et 4
2
10
* ; multiplier 2 et 10
- ; soustraire 2*10 à 3*4
5
+ ; ajouter 5
|
mais peut aussi s'écrire...
5
3
4
* ; multiplier 3 et 4
+ ; ajouter 5
2
10
* ; multiplier 2 et 10
- ; soustraire 2*10 à 5+3*4
|
Je vous laisse un lien vers un
[convertisseur d'expression en ligne] si vous êtes un peu perdus
Appliquer les calculs avec la librairie FLOAT24 - Cas pratique
Comme résumé plus haut, l'utilisation des fonctions mathématiques implique en général deux opérandes, la première dans AHL et la deuxième dans CDE, avec un résultat dans AHL, qui redevient la première opérande à utiliser avec une autre opérande et ainsi de suite.
En cas pratique, je propose de traduire le programme BASIC bubble universe que j'ai proposé au concours des 26 ans d'Amstrad.eu :)
Comme beaucoup de fractales, les calculs sont relativement simples mais nécessite l'usage de nombre flottants. Voici le source :

Nous allons maintenant organiser notre assembleur pour les calculs :
D'abord les valeurs d'initialisation, la variable R doit contenir 2*PI/235. Ça tombe bien, RASM Atlas gère les flottants 24 bits :) Les variables centx et centy serviront à calculer les coordonnées du pixel à afficher, nous en avons aussi besoin en flottant. Les valeurs sont plus faibles qu'en Basic car nous travaillerons en pixels là où le Basic calcule en coordonnées logiques qui n'ont pas de rapport direct avec la résolution en cours.
rval df24 2*3.1415926545/235
centx df24 50
centy df24 80
zero df24 0
|
Certaines valeurs doivent être initialisées à zéro explicitement. Le BASIC s'occupe tout seul de mettre zéro dans les variables utilisées avant d'être affectées. Nous devons de notre côté forcer ces variables à zéro.
valeursFloat
uval df24 0
vval df24 0
iv df24 0
rix df24 0
tval df24 0
xval df24 0
valeursFloatFin
|
On peut initialiser ces variables à zéro dans le cas où on veut exécuter plusieurs RUN avec notre programme. Notez qu'il est impératif que la variable zéro précède notre tableau de valeurs!
ld hl,zero : ld de,valeursFloat : ld bc,valeursFloatFin-zero : ldir
|
Enfin, nous avons une fonction de hasard, nous allons nous en servir pour initialiser la variable t (oui, j'ai directement mis la valeur float24 dans CDE)
call f24rand : ld c,#40 : ld de,0 : call f24mul : ld (tval),a : ld (tval+1),hl ; t=RND*4
|
Le reste du programme s'articule à l'intérieur d'une double boucle de deux nombres entiers I et J
Le compteur I peut s'écrire de cette façon, avec une automodification de A et une copie dans HL pour être converti avec la fonction
i16tof24 qui converti la valeur de HL dans un FLOAT24 représenté par AHL.
boucleCalcul
.icpt ld a,0 : ld l,a : ld h,0 : inc a : cp 250 : ret z : ld (.icpt+1),a : call i16tof24
|
Le compteur J ne sert pas aux calculs de la fractale mais seulement à la distribution des couleurs dans l'espace RGB, on le garde en nombre entier.
On tape dans le dur! Conversion des formules!
Le calcul de la coordonnées X
push af,hl ; AHL contient notre compteur I en float pour rappel
; iv=i+v
ld bc,(vval) : ld de,(vval+1) : call f24add : ld (iv),a : ld (iv+1),hl
pop hl,af
; rix=r*i+x
ld bc,(rval) : ld de,(rval+1) : call f24mul : ld bc,(xval) : ld de,(xval+1) : call f24add : ld (rix),a : ld (rix+1),hl
; u=sin(iv)+sin(rix)
call f24sin : push af,hl ; sin(rix)
ld a,(iv) : ld hl,(iv+1) : call f24sin : ld c,a : ex de,hl : pop hl,af : call f24add
; plotx=u*100 (en fait 80 dans notre source assembleur)
push af,hl ; backup u
ld bc,(centx) : ld de,(centx+1) : call f24mul : call f24toi16 : ld (plotx),hl
pop hl,af ; pop u
|
Puis celui de la coordonnée Y
; x=u+t
ld bc,(tval) : ld de,(tval+1) : call f24add : ld (xval),a : ld (xval+1),hl ; UPDATE X
; v=cos(i + v) + cos(r * i + x)
ld a,(iv) : ld hl,(iv+1) : call f24cos : push af,hl
ld a,(rix) : ld hl,(rix+1) : call f24cos : ld c,a : ex de,hl : pop hl,af : call f24add : ld (vval),a : ld (vval+1),hl ; UPDATE V
; ploty=v*100
ld bc,(centy) : ld de,(centy+1) : call f24mul : call f24toi16 : ld (ploty),hl
|
Maintenant qu'on a les coordonnées du pixel, il ne manque qu'à piocher une couleur et afficher le point. Je ne reviendrai pas sur les méthodes d'affichage d'un pixel. Ce qu'il faut comprendre ici dans le source BASIC est qu'on parcourt le spectre RGB en balayant les composantes. Le source BASIC réalise ça avec des multiplications et des divisions pour des raisons de compacité mais l'idée réelle est plutôt d'avoir des compteurs qui s'incrémentent ou se décrémentent régulièrement en fonction de la composante. Par exemple, +1 pour le vert tous les 256 pixels, +3 pour le bleu tous les pixels et -1 pour le rouge tous les pixels. Peu importe, l'idée est de varier les couleurs en suivant des rampes RGB.
Enfin, la petite subtilité du source BASIC, réduire les 27 couleurs de l'ensemble du spectre RGB à seulement 16 couleurs en enlevant les couleurs les moins significatives. Charge à une table de 27 entrées de pointer sur une des 16 couleurs retenues.
Les 27 couleurs possibles

Et les 16 couleurs retenues
Pour ceux qui veulent voir le programme final en action, il est disponible sur cpc-power
[ICI]
Sinon vous pouvez télécharger l'ensemble des sources (univers bulle + librairie) pour jouer avec le zoom, les couleurs, les seuils
[ICI]