FLOATING POINT PACKAGE (F.P.P.)

 

 

Le FPP est un ensemble de routines permettant d'effectuer des calculs "complexes" que l'on ne peut donc pas faire "simplement" avec les instructions arithmétiques du 6502. Ces routines permettent de travailler sur des nombres à virgules. Ils peuvent être bien plus grand que ce que peuvent contenir les registres du 6502. Le FPP va donc mettre en oeuvre des traitements plutôt lourds. Pour cette raison, il vaut mieux éviter d'utiliser ces routines autant que possible dans un jeu, car le traitement peut-être très long et le jeu d'autant plus lent.

 

Représentation Interne d'un nombre:

Un nombre est codé sur 6 octets.

Le premier octet se décompose de la manière suivante:

7

6

5

4

3

2

1

0

0 - Positif

1 - Négatif

Signe de l'exposant

(1 si positif, 0 si négatif)

 

 

Exposant pour une puissance de 100

 

L'exposant va permettre de placer la virgule sur le nombre. A noter que si l'exposant est inférieur à 64, ce nombre est alors inférieur à 1 et inversement. 64 est l'origine du repère (le 0). Tous nombres supérieurs à 64 est un exposant positif, tous nombres inférieurs est un nombre négatif.

L'exposant +2 s'écriera donc 64 + 2 = 66.

L'exposant -2 s'écriera 64 - 2 = 62.

Les 5 octets suivants sont la mantisse de du nombre au format BCD (Binary Coded Decimal):

Par exemple 25 sur 1 octet serait codé 2 sur les 4 bits fort de l'octet, et 5 sur les 4 bits de poids faibles.

Astuce: pour lire le nombre sans difficultés, il suffit d'afficher la valeur de la mantisse en hexadécimal (4 bits occupés par chiffre, c'est exactement ce qui se passe en hexa !).

Voici un exemple que j'ai récupéré, je ne sais plus où:

Number: 0.02 2 * 100**-l
Format: 3F 02 00 00 00 00 (exponent= 40-1)

Number: -0.02 -2 * 100**-l
Format: BF 02 00 00 00 00 (exponent= 80+40-1)

Number: 37.0 = 37 * 100**0
Format: 40 37 00 00 00 00 (exponent= 40+0)

Number: -460312 = -46.0312 * 100**2
Format: C2 46 03 12 00 00 (exponent= 80+40+2)

The number zero is handled as a special case, and is represented as a zero exponent and zero mantissa. Either the exponent or the first mantissa byte may be tested for zero.

The dynamic range of numbers that can be represented by this scheme is 10**-98 to 10**+98.

 

Implantation en mémoire:

Le code FPP est localisé aux adresses $D800 à $DFFF.

Il utilise ses propres registres (ou plutôt des pseudos registres) et comme nous l'avons vu ci-dessus, sa propre méthode d'écriture d'un nombre. Il a donc besoin de mémoire pour tout cela:

Deux zones mémoires qui sont:

$00D4 - $00FF

$057E - $05FF

Image non trouvée !Si vous n'utilisez pas le FPP, cette zone mémoire est alors libre. Attention, ceci n'est vrai que si vous n'utilisez pas le Basic avec votre programme machine. Car sinon, Basic lui utilisera cette zone pour les variables, lire une chaîne de caractères...

 

Il y a deux pseudos-registres nommés FR0 et FR1. Ces registres pouvant stocker des nombres à virgules flottants, ils occupent donc 6 octets.

FR0 est localisé en $D4 à D9 et FR1 est localisé en $E0 à $E5.

Il existe aussi un pointeur nommé FLPTR (sur 2 octets) sur une zone mémoire contenant un nombre flottant. Ce pointeur est situé à l'adresse $FC et $FD. Ce pointeur permettra de charger l'un des deux pseudos-registres avec un nombre flottant stocké dans une zone mémoire différente ou de stocker un nombre flottant du registre FR0 vers une zone mémoire.

De même, on peut avoir besoin d'afficher des nombres flottants à l'écran. Dans ce cas, il faudra utiliser des routines de conversion de nombres flottants en ATASCII (qui, heureusement, existent déjà).

La zone mémoire utilisée s'appelle LBUFF, et occupe 128 octets situés entre $0580 à $05FF.

Image non trouvée !Cette zone mémoire servant aussi au BASIC, ne pas y laisser un nombre en espérant le récupérer plus tard.

Un pointeur est utilisé pour pointer sur cette zone mémoire : INBUFF en $F3 et $F4.

Enfin, un index nommé CIX, situé en $F2, utilisé comme offset avec le pointeur INBUFF. Ne sert normalement pas pour nous et est à mettre à zéro.

Routines du FPP:

AFP: Conversion ATASCII en nombre flottant

Il faut indiquer dans INBUFF la zone mémoire où se trouve le nombre au format ATASCII. La chaîne de caractères devra se terminer par le code CR au format ATASCII:$9B.

CIX doit être à 0 si vous pointez déjà au début de votre nombre dans INBUFF...

FASC: Conversion d'un nombre flottant en ATASCII

Conversion d'un nombre flottant contenu dans FR0 en ATASCII. Le résultat est stocké dans LBUFF, INBUFF est initialisé.

Image non trouvée ! Le bit 7, dans le cas où vous comptez les bits de 0 à 7, sinon se sera le bit 8 (1 à 8 !) du dernier caractère est positionné à 1. De plus, il n'y a pas de code CR.

IFP: Conversion d'un entier non signé (16 bits) en nombre flottant

FR0 contient ce nombre entier (les 2 premiers octets de FR0).

En sortie, FR0 est un nombre flottant.

FPI: Conversion d'un nombre flottant en entier (16 bits)

FR0 contient un nombre flottant.

En sortie, les deux premiers octets de FR0 représentent un nombre sur 16 bits.

CARRY est positionné si overflow (si résultat > 65535).

FADD: Addition sur deux nombres flottants

FR0 et FR1 contiennent des nombres flottants

L'opération est FR0 + FR1

Le résultat de cette addition sera stocké dans FR0.

CARRY est positionné si les valeurs sont hors limites (out of range).

FSUB: soustraction entre deux nombres flottants

FR0 et FR1 contiennent les deux nombres flottant à soustraire.

L'opération est FR0 - FR1

Le résultat de cette soustraction sera stocké dans FR0.

CARRY est positionné si les valeurs sont hors limites (out of range).

FMUL: Multiplication de deux nombres flottants

FR0 et FR1 contiennent les deux nombres flottant à multiplier.

L'opération est FR0 * FR1

Le résultat de cette opération sera stocké dans FR0.

CARRY est positionné si les valeurs sont hors limites (out of range).

FDIV: Division de deux nombres flottants

FR0 et FR1 contiennent les deux nombres flottant à Diviser.

L'opération est FR0 / FR1

Le résultat de cette opération sera stocké dans FR0.

CARRY est positionné si les valeurs sont hors limites ou si le diviseur (FR1) = 0.

Je n'ai jamais lancé les routines de calcules qui suivent...

LOG: Logarithme

LOG10: logarithme base 10

FR0 contient le nombre flottant

En sortie, FR0 est le résultat de l'opération.

CARRY est positionné si le nombre est négatif ou dépasse la valeur maximale.

EXP: Exponentiel

EXP10: idem en base 10

FR0 contient le nombre flottant

En sortie, FR0 est le résultat de l'opération.

 

PLYEVL: Evaluation polynomiale

Le registre X doit contenir la partie basse d'un tableau sur des nombres flottants.

Le registre Y doit contenir la partie haute de ce même tableau.

Le registre A Contient le nombre d'élément dans le tableau.

En sortie: Le résultat est dans FR0.

CARRY est positionné s'il y a une erreur de dépassement capacité (overflow).

De mémoire (très vieille) de mes cours de math., il existe une fonction polynome pour avoir une valeur proche de la fonction cosinus. Je suppose que c'est cette fonction PLYEVL qu'il faut utiliser.

Cependant, les calculs seront alors bien lourd. Alors autant utiliser la fonction Basic COS. En assembleur, si vous voulez l'utiliser pour afficher du graphisme, il vaut mieux passer par une table des cosinus, se sera bien plus rapide.

 

ZFR0: Effacer le contenu de FR0

En sortie, les octets de FR0 sont à 0

 

ZF1: Effacement de la zone d'un nombre flottant en page zéro

En entrée, le registre X contient l'adresse en page 0 (la partie basse, On ne renseigne rien d'autre puisque le poids fort est à 0)

 

FLD0R: Charger FR0 avec un nombre flottant.

En entrée, Les registres X et Y pointent sur une zone mémoire contenant un nombre flottant (X = poids faible, Y = poids fort).

En sortie, FLPTR pointe sur cette zone mémoire et FR0 contient le nombre flottant.

 

FLD0P: Charger FR0 avec un nombre flottant.

En entrée, FLPTR pointe sur une zone mémoire contenant un nombre flottant.

En sortie, FR0 contient le nombre flottant.

 

FLD1R: Charger FR1 avec un nombre flottant.

En entrée, Les registres X et Y pointent sur une zone mémoire contenant un nombre flottant (X = poids faible, Y = poids fort).

En sortie, FLPTR pointe sur cette zone mémoire et FR1 contient le nombre flottant.

 

FLD1P: Charger FR1 avec un nombre flottant.

En entrée, FLPTR pointe sur une zone mémoire contenant un nombre flottant.

En sortie, FR1 contient le nombre flottant.

 

FST0R: Stocker le contenu de FR0 dans une zone mémoire.

En entrée, Les registres X et Y pointent sur une zone mémoire (X = poids faible, Y = poids fort).

En sortie, FLPTR pointe sur cette zone mémoire et cette zone contient ce nombre flottant.

 

FST0P: Stocker le contenu de FR0 dans une zone mémoire.

En entrée, FLPTR pointe sur une zone mémoire qui contiendra le nombre flottant.

En sortie, la zone contient ce nombre flottant.

 

FMOVE: Déplacer le contenu de FR0 dans FR1

En entrée, FR0 contient un nombre flottant qui sera transféré dans FR1.

En sortie, FR1 est chargé...

 

Tableau récapitulatif:

LISTE DES ROUTINES DU F.P.P.

NOM

ADDRESSE

FONCTION

RESULTAT

TEMPS MAX. OCCUPATION DU CPU
(micro sec.)

AFP D800 Conversion ATASCII en nbre flottant FR0 3500
FASC D8E6 Conversion nbre flottant en ATASCII LBUFF 1000
IFP D9AA Conversion d'un entier (16bits) en nbre flottant. FR0 1300
FPI D9D2 Conversion nbre flottant en entier FR0 2400
FSUB DA60 Soustraction : FR0-FR1 FR0 750
FADD DA66 Addition : FR0+FR1 FR0 750
FMUL DADB Multiplication : FR0*FR1 FR0 12000
FDIV DB28 Division : FR0/FR1 FR0 10000
FLD0R DD89 Charger un nombre flottant avec X,Y FR0 70
FLD0P DD8D Charger un nombre flottant avec FLPTR
FR0 60
FLD1R DD98 Charger un nombre flottant avec X,Y
FR1 70
FLD1P DD9C Charger un nombre flottant avec FLPTR FR1 60
FSTOR DDA7 Stocker un nombre flottant avec X,Y FR0 70
FSTOP DDAB Stocker un nombre flottant avec FLPTR FR0 70
FMOVE DDB6 Déplacer le contenu de FR0 FR1 60
ZFR0 DA44 Initialiser FR0 FR0 80
ZF1 DA46 Vider une zone mémoire d'un nombre flottant Zone mémoire indiquée 80
PLYEVL DD40 Evaluation Polynomiale FR0 88500
EXP DDC0 Exponentiel FR0 116000
EXP1O DDCC Exponentiel base 10 FR0 109000
LOG DECD Logarithme FR0 136000
LOG10 DED1 Logarithme Base 10 FR0 125400

 

Exemple:

Voici un exemple en assembleur:

On part d'une chaîne de caractères (dont le nombre est 3) et d'un entier sur 16 bits dont la valeur est 5

Opération 3+5

Le résultat est multiplié à un nombre flottant (20). Ce résultat de l'opération est converti en entier puis retourné dans y du y = usr().

Le source:

Image non trouvée !

 

Le binaire:

Image non trouvée !

 

Et le programme pour le charger en Basic:

Image non trouvée !

 

Voici un second exemple. Dans celui-ci, on calcul un score:il démarre arbitrairement à 123 et on ajoute 1 10 fois de suite...

Ce code va en plus afficher le résultat du score à l'écran (Utilisation de FASC) directement à l'aide d'une procédure perso. dans la zone mémoire de l'écran.

Le source:

Image non trouvée !

 

Le binaire:

Image non trouvée !

 

Et le programme pour le charger en Basic:

Image non trouvée !