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 ###