Copy Link
Add to Bookmark
Report
Tutorial Assembleur - Chapitre 09
TUTORIAL ASSEMBLEUR - chapitre 9
---------------------------------
Le starfield - Les fichiers de données
---------------------------------------
Son histoire
------------
Je ne sais pas qui est le premier à avoir crée le concept du champ d'étoile - starfield.
Le principe est simple mais il se classe en deux catégories.
La première est celle des starfields 2D - les étoiles se
déplacent latéralement 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 remplacées 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 exécuté,
il faut que vous dezippiez STRFLD.zip. Ce fichier contient l'image de
fond, le code ASM et le fichier COM précompilé.
Les explications sur le fonctionnement du starfield se trouvent après
cette séquence de code.
mov ax,013h ;MODE 013h
int 10h
push 0a000h ;on se place sur la vidéo
pop es
call CLS ;on appelle la procédure qui affiche l'image de fond
Initialisation: ;on va créer les coordonnées 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 aléatoires
;pour les coordonnées X,Y,Z des étoiles
;X,Y,Z sont des WORDS !!!
mov dx,320 ;valeur X maximale => 320
call RND ;on cherche un nb aléatoire
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 aléatoire
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 même
;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'incrémentation 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 données des étoiles
Start: ;dans cette routine, on transforme les coordonnées 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 coordonnées étaient relatives à la position (0,0)
cmp w[YT],200 ;quelques tests pour voir si le pixel ne dépasse 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 vidéo (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 réduit la distance de l'étoile
;par rapport à l'observateur
cmp ds:w[si+4],0 ;on teste si on a zéro 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 zéro 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 ;donnée du créateur de nb aléatoires
include hello.db ;une image dans un fichier externe est
;liée à 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 procédures
---------------
En observant le code, vous aurez probablement aperçu les parties du programme
qui commencent par des PROC et finissent par des RET et ENDP. Il s'agit des
procédures. Ce sont des mini-programmes qui effectuent un travail lorsqu'on
les appelle avec un CALL. Pour déclarer une procédure, il faut d'abord créer
une ligne avec les instructions :
PROC Nom_de_la_Procédure
Par exemple : PROC Additionne
Ensuite, vous placez les différentes instructions nécessaires 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 après le RET, la commande ENDP
(End of Procedure) :
PROC Additionne
MOV AX,14
ADD AX,BX
RET
Additionne ENDP
Voila, pour appeller votre procédure, vous faites un CALL
Nom_de_la_procédure. Ici, ce sera CALL Additionne. Attention, le RET de la
procédure, redonne la main au programme et ne le fait pas revenir au DOS.
Si vous voulez utiliser une procédure 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 considérer cela et
non pas simplement afficher les étoiles à l'écran. Pour avoir cette
troisième dimension, qu'est la profondeur, nous introduisons en plus
des coordonnées X et Y habituelles, la coordonnée Z qui indique la distance
de l'étoile par rapport à l'observateur.
Pour passer de coordonnées 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 très simple, nous multiplions le X (coordonnée 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 même 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 mémoire vidéo, nous appliquons la formule
déjà 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 à zéro). Si nous arrivons à 0, il
faut arrêter de calculer cette étoile car nous ne pouvons diviser
pas 0. Et on recommence en remettant la valeur Z telle qu'elle était au
début.
Utilisation des images
----------------------
Au lieu de bêtement effacer l'écran à chaque nouvelle "avancée" des étoiles,
j'ai inclus un fond qui représente 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é sauvegardée 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 enregistrée 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'opèrent
très facilement (ex : INCPRO image.raw image.db image). Le dernier paramètre
est dans cet exemple, le label. Il ne reste plus qu'à introduire le
paramètre "INCLUDE" dans votre programme principal pour dire que vous avez
un fichier de donnée 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 donnée.
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 données. Les EXE résolvent le problème mais ils sont plus
difficiles à gérer (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 problèmes,
cependant je vais quand même expliquer l'affichage de l'image.
Nous nous plaçons 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 à zéro car nous voulons afficher l'image dès le début 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
mémoire). 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 donnée est celle qui utilise les DUP. Il s'agit d'une
simple représentation pour décrire une série 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_Répetitions / 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 modifiée (comme un tableau) :
DONNEE dw 300 dup(?)
vous pouvez faire de même avec des BYTES.
Cette instruction permet de rendre le code plus clair mais cela
n'empêche pas que l'assembleur va la considérer comme une
suite de données. La taille du fichier sera donc la même 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 données. Pour rendre plus petit
l'exécutable, il faut utiliser un compresseur d'EXE ou de COM. PKLITE est
une bonne réference même 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 très simple et
comporte peu de variations.
### Chapitre 9 - dake / c a l o d o x ###
### http://www.space.ch/scene/calodox ###