TUTORIAL ASSEMBLEUR - chapitre 9
---------------------------------

Le starfield - Les fichiers de donnes
---------------------------------------

Son histoire
------------

Je ne sais pas qui est le premier  avoir cre le concept du champ d'toile - starfield.
Le principe est simple mais il se classe en deux catgories.
La premire est celle des starfields 2D -  les toiles se
dplacent latralement de gauche vers la droite ou vice-versa. Bref, en 2D.
Le second type de starfields est ceux qui utilisent la perspective.
Les toiles viennent du fond de l'cran et se dirigent vers nous en donnant
l'impression aux spectateurs de voyager dans l'espace. Le meilleur exemple
est le starfield conomiseur d'cran de Windows. Il existe des variantes de
ces effets. Les toiles peuvent tre remplaces par des sprites qui
grossissent au fur et  mesure de leur rapprochement.

Dans ce chapitre, nous verrons la conception d'un starfield en 3D
utilisant la perspective et des points en guise d'etoiles.

Le code pour A86
----------------

Le fichier qui suit ne peut pas tre compil comme tel.
Il s'agit juste du programme principal mais pour pouvoir tre excut,
il faut que vous dezippiez STRFLD.zip. Ce fichier contient l'image de
fond, le code ASM et le fichier COM prcompil.

Les explications sur le fonctionnement du starfield se trouvent aprs
cette squence de code.

mov ax,013h                  ;MODE 013h
int 10h
push 0a000h                  ;on se place sur la vido
pop es

call CLS                     ;on appelle la procdure qui affiche l'image de fond

Initialisation:              ;on va crer les coordonnes de nos toiles
xor bp,bp                    ;bp sert de compteur
lea si,stars                 ;on se place sur l'adresse de STARS 
                             ;qui se trouve maintenant en DS:SI

Coordo:                      ;cette routine trouve des valeurs alatoires
                             ;pour les coordonnes X,Y,Z des toiles
                             ;X,Y,Z sont des WORDS !!!


mov dx,320                   ;valeur X maximale => 320                 
call RND                     ;on cherche un nb alatoire

mov ds:[si],dx               ;qui sort dans DX et que l'on place au premier emplacement
                             ;de ds:[si]

mov dx,200                   ;le Y => max. 200 
call RND                     ;un petit nombre alatoire
mov ds:[si+2],dx             ;qu'on place  DS:[SI+2] car on travaille avec des WORDS
                             ;(si c'tait des bytes, on aurait DS:[SI+1])

mov dx,700                   ;la taille Z est fixe, toutes les toiles sont  la mme
                             ;distance de l'observateur
mov ds:[si+4],dx             ;DS:[SI+4] car c'est un word
add si,6                     ;on augmente SI de 6 car 3 words = 6 bytes
                             ;et la plus petite valeur d'incrmentation de S
                             ;est le byte

inc bp                       ;on dit  BP qu'on a fait une toile
cmp bp,100                   ;est-ce qu'on a nos 100 toiles ?
jna Coordo                   ;si on les a pas, on continue 


Go_Etoiles:                 
xor bp,bp                    ;BP est un compteur comme toujours
lea si,stars                 ;on se place sur les donnes des toiles    

Start:                       ;dans cette routine, on transforme les coordonnes 3D
                             ;en 2D 

mov ax,ds:[si]               ;dans AX, le X de l'toile
mov bx,ds:[si+4]             ;dans BX, le Z de l'toile
shl ax,9                     ;on multiplie AX par 512
cwd                          ;CWD ! Nombre sign
idiv bx                      ;IDIV car nombre sign
                             ; ce stade, nous avons (512*X)/Z
mov w[XT],ax                 ;c'est la position X de l'toile en 2D

mov ax,ds:[si+2]             ;on fait pareil avec Y
shl ax,9                     ;multipli par 512
cwd              
idiv bx                      ;Z est rest dans BX donc pas besoin de le recharger      
mov w[YT],ax                 ;position Y de l'toile en 2D    

add w[XT],160                ;nous devons centrer par rapport  l'cran
add w[YT],100                ;car les coordonnes taient relatives  la position (0,0)

cmp w[YT],200                ;quelques tests pour voir si le pixel ne dpasse pas de l'cran
ja No_Show                   ;YT>200 = pas de pixel
cmp w[XT],320
ja No_show                   ;XT>320 = pas de pixel

Trace:                       ;Ici, nous calculons l'offset du pixel sur l'cran
                             ;car nous n'avons que des coord. X et Y.

mov ax,w[YT]                 ;on prend Y
mov bx,320                   ;on multiplie par la largeur (Y*320)
mul bx                       ;voila...
add ax,w[XT]                 ;et on rajoute le X        
mov di,ax                    ;cet offset part chez DI qui pointe sur l'cran

mov al,14                    ;couleur 14 de la palette standard du DOS = jaune
mov es:[di],al               ;et on place le point dans la vido (stosb marche aussi)

No_Show:                     ;ce label se trouve l, si jamais l'toile tait en dehors
                             ;de l'cran    
in al,60h
cmp al,1                     ;test pour la touche "ESC"
jz Endo

sub ds:w[si+4],4             ;si on a pas de touche ESC, on rduit la distance de l'toile
                             ;par rapport  l'observateur
cmp ds:w[si+4],0             ;on teste si on a zro dans Z car si on fait (X/Z) avec Z=0
                             ;on obtient un DIVID ERROR (on ne peut pas diviser par 0 !!!)        

jz Restart                   ;si jamais on avait zro et bien on remet le Z de l'toile  500

Resto:                       
add si,6                     ;on augmente SI de 6 car on passe  l'toile suivante
inc bp                       ;on augmente le compteur d'toiles 
cmp bp,100                   ;on les a toutes faites ?
jz ReS                       ;alors il faut effacer l'cran avec l'image et recommencer

jmp Start                    ;autrement, on continue

Res:
call CLS                     ;la proc. CLS efface l'cran avec l'image
jmp Go_Etoiles               ;et on fait la nouvelle vague d'toiles qui est plus proche
                             ;maintenant

Endo:                        ;si on a la touche "ESC" et bien on quitte
mov ax,03h
int 10h                      ;par le mode texte et un RET qui retourne au DOS
ret

Restart:                     ;on vient voir RESTART si le Z de l'toile est gal  0     
mov ax,700                   ;on remet  700
mov ds:[si+4],ax             ;le Z de l'toile en cours de calcul
jmp Resto                    ;et on retourne d'o on est venu


SYNC PROC                
mov dx, 3DAh
retrace1:
in al, dx
test al, 8
jz retrace1                  ;attente du retour du faisceau lumineux.
retrace2:
in al, dx
test al, 8
jnz retrace2
ret
SYNC ENDP

CLS PROC
lea si,hello
xor di,di
mov cx,32000
rep movsw
ret
CLS ENDP

RND PROC                ;voir code du feu - chapitre 9 - pour plus de commentaires                          
random: push ax                  
push dx                      
mov ax,31421               
mov bx,w[seed]
mul bx                    
add ax,6927     
mov seed,ax         
pop dx                     
mul dx                  
add dx,1
pop ax                        
ret
RND ENDP

seed    dw 0                            ;donne du crateur de nb alatoires     

include hello.db                        ;une image dans un fichier externe est
                                        ;lie  notre fichier principal

stars dw 300 dup(?)                     ;300 words (300/3=100 toiles) dont la valeur
                                        ;est (?) => inconnue - DUP sert  dire
                                        ;que nous voulons une suite de DW ou DB

xt dw ?                                 ;les WORDS qui sont XT avec valeur inconnue               
yt dw ?                                 ;et YT avec valeur inconnue (le ? indique 
                                        ;une valeur inconnue) 

Les procdures
---------------

En observant le code, vous aurez probablement aperu les parties du programme
qui commencent par des PROC et finissent par des RET et ENDP. Il s'agit des
procdures. Ce sont des mini-programmes qui effectuent un travail lorsqu'on
les appelle avec un CALL. Pour dclarer une procdure, il faut d'abord crer
une ligne avec les instructions :

PROC Nom_de_la_Procdure
Par exemple : PROC Additionne

Ensuite, vous placez les diffrentes instructions ncessaires et lorsque 
vous avez fini, vous mettez l'instruction RET, par exemple : 

PROC Additionne
MOV AX,14
ADD AX,BX
RET

Il ne faut pas oublier de mettre aprs le RET, la commande ENDP
(End of Procedure) :

PROC Additionne
MOV AX,14
ADD AX,BX
RET
Additionne ENDP

Voila, pour appeller votre procdure, vous faites un CALL
Nom_de_la_procdure. Ici, ce sera CALL Additionne. Attention, le RET de la
procdure, redonne la main au programme et ne le fait pas revenir au DOS.
Si vous voulez utiliser une procdure pour sortir du programme, il faudra
utiliser MOV AX,04ch - INT 10h.

Comment a marche ?
-------------------

L'effet du starfield repose sur le principe de la perspective. Plus un objet
est loign, plus il est petit. Le programme doit donc considrer cela et
non pas simplement afficher les toiles  l'cran. Pour avoir cette
troisime dimension, qu'est la profondeur, nous introduisons en plus
des coordonnes X et Y habituelles, la coordonne Z qui indique la distance
de l'toile par rapport  l'observateur.

Pour passer de coordonnes 3D vers des positions X,Y en 2D, il faut
appliquer les formules suivantes :

X_2D = (Facteur*X_3D)/Z_3D
Y_2D = (Facteur*Y_3D)/Z_3D

C'est trs simple, nous multiplions le X (coordonne 3D) de l'toile par un
facteur qui doit tre naturellement une puissance de 2, afin de faciliter
les calculs. Pour un cran de 320x200, les valeurs 256 ou 512 sont un bon
choix. Ensuite, nous divisons le X*facteur par le Z de l'toile.
Nous obtenons la position X sur l'cran. Nous faisons de mme avec Y.
Attention au Z negatif et egal a 0. Il faut absolument eliminer les
etoiles se trouvant a de telles positions. Elles conduiraient a des
resultats errones.

Pour trouver l'offset dans la mmoire vido, nous appliquons la formule
dj connue :

OFFSET_MEMOIRE=(Y*320)+X

Il ne reste plus qu' placer un point color  cet emplacement.
Nous rapprochons les toiles en enlevant  leur Z, une valeur
multiple de 2 (afin d'arriver pile  zro). Si nous arrivons  0, il
faut arrter de calculer cette toile car nous ne pouvons diviser
pas 0. Et on recommence en remettant la valeur Z telle qu'elle tait au
dbut.

Utilisation des images
----------------------

Au lieu de btement effacer l'cran  chaque nouvelle "avance" des toiles,
j'ai inclus un fond qui reprsente une image (hmmm...pas terrible, resultat
de 2 secondes  sous Paint Shop Pro).
La taille est videmment de 320x200 pour recouvrir l'ensemble de l'cran.
Elle a t sauvegarde sous le format "RAW" ou format BINAIRE. Ce format
retranscrit directement les couleurs en octets. Par exemple, un suite de
pixels comme "15 10 13" sera enregistre sans changement ni compression.
Cela nous donne une image de 320*200=64000 bytes. Il faut ensuite convertir
cette image au format que j'appelle BYTES ou WORDS. Cette conversion
transforme les suites de BYTES binaires du fichier en fichier compos de DB
ou de DW afin que l'assembleur puissent les comprendre. J'utilise le
programme INC-PRO III.

Pour obtenir l'aide, il faut taper "INCPRO /?". Les conversions s'oprent
trs facilement (ex : INCPRO image.raw image.db image). Le dernier paramtre
est dans cet exemple, le label. Il ne reste plus qu' introduire le
paramtre "INCLUDE" dans votre programme principal pour dire que vous avez
un fichier de donne externe qu'il faut inclure dans le fichier principal
lors de la compilation. Cela donnera par exemple "INCLUDE IMAGE.DB". Ensuite,
cette image sera accessible comme n'importe quelle autre donne.
Un "MOV AX,OFFSET IMAGE" sera tout  fait valable et il chargera la
valeur de l'offset de votre image.

Les fichiers COM sont cependant astreints  une taille de 64k au m
aximum (65535 bytes). Ils ne peuvent pas "aller" dans un autre segment
(souvenez-vous qu'un segment=64k). Nous pouvons donc juste stocker une
image de 320x200. Il nous reste ainsi un peu d'espace pour notre code et
d'autres donnes. Les EXE rsolvent le problme mais ils sont plus
difficiles  grer (en tout cas avec A86, TASM evite bien des problemes
mais le mieux est le passage au mode protege).

Le code source du starfied ne devrait pas poser de problmes,
cependant je vais quand mme expliquer l'affichage de l'image.
Nous nous plaons tout d'abord sur l'adresse de l'image avec "LEA SI,HELLO".
HELLO est le label de l'image. Le programme place l'adresse dans DS:SI.
Nous mettons DI  zro car nous voulons afficher l'image ds le dbut de
l'cran bien entendu. Ensuite, nous mettons dans CX, la valeur de 32000.
CX sert de compteur pour l'instruction REP (voir chapitre sur la
mmoire). Nous faisons un REP MOVSW, qui copie des WORDS de DS:SI vers ES:DI.
Nous avons mis 32000 car nous travaillons avec des WORDS et c'est plus
rapide que les BYTES. Cependant avec les bytes, nous aurions "MOV CX,64000
et REP MOVSB". On peut copier des DWORDS mais pas avec A86 qui se limite
aux instructions 16 bits (il existe une astuce, nous la verrons dans un
prochain chapitre).

L'instruction DUP
-----------------

Un autre type de donne est celle qui utilise les DUP. Il s'agit d'une
simple reprsentation pour dcrire une srie de BYTES ou
de WORDS. Cela permet d'conomiser du temps au lieu de taper
300 fois "DB ?" ou "DB 200". La syntaxe est :

Nom_Donnee  /  DB/DW  /  Nombre_Rpetitions / DUP / (Valeur) 

par exemple, nous voulons 300 bytes de valeur 12 :
DONNEE db 300 dup(12) 

pour 300 words de valeur 12 :
DONNEE dw 300 dup(12)

si nous voulons 300 words, d'une valeur pouvant tre modifie (comme un tableau) :
DONNEE dw 300 dup(?)

vous pouvez faire de mme avec des BYTES.

Cette instruction permet de rendre le code plus clair mais cela
n'empche pas que l'assembleur va la considrer comme une
suite de donnes. La taille du fichier sera donc la mme que si vous aviez
tap  la main les 300 "DB ?".

Notre starfield aura une taille de plus de 64000 bytes car il contient le
code + l'image + quelques autres donnes. Pour rendre plus petit
l'excutable, il faut utiliser un compresseur d'EXE ou de COM. PKLITE est
une bonne rference mme s'il n'est pas le plus performant. Il permet de
compresser les fichiers COM et EXE avec parfois plus de 80 %.
Dans notre cas, la compression sera efficace car l'image est trs simple et
comporte peu de variations. 

### Chapitre 9 - dake / c a l o d o x ###
### http://www.space.ch/scene/calodox ###
   

