Analyser une démo de 128 octets à partir des archives de 1997

C'est très agréable de réaliser mes désirs, surtout d'un passé lointain, si lointain que j'ai déjà oublié que je le voulais une fois. Je connais peu la démoscène et je n'ai certainement jamais suivi les auteurs ou leur travail, j'ai juste aimé regarder ce qui s'est passé. Parfois, je voulais comprendre, mais ensuite je manquais de connaissances et d'expérience, plus tard de persévérance, puis j'ai complètement perdu tout intérêt pour cela. Mais récemment, mon ami, avec qui nous avons étudié à l'époque et qui nous a fourni tous les nouveaux produits, y compris les démos, avec BBS et Fidonet, car il avait presque tous à la fois un téléphone et un modem et un ordinateur, a rendu visite à CAFePARTY avec son travail cela m'a fait ouvrir les archives de mon premier ordinateur, sélectionner une démo et la découvrir.

pentagra.com

Évaluant objectivement mes forces, j'ai pris une intro de 128 octets que j'ai aimé visuellement. Le fichier pentagra.com est signé par Mcm , 128 octets, dernière modification le 9/24/1996 18:10:14, vidage hexadécimal:

000000: b0 13 cd 10 68 00 a0 07 06 1f ac ba c8 03 ee 42
000010: b1 40 ee 40 6e 6e e2 fa b8 3f 3f bb 40 01 bf 40
000020: 05 57 b1 78 ab 03 fb e2 fb 5f b1 60 88 01 aa 03
000030: fb 03 fb e2 f7 b1 61 88 01 aa 2b fb 2b fb e2 f7
000040: bf d1 99 57 b1 78 ab 2b fb e2 fb 5f b1 8f f3 ab
000050: 81 fe 00 fa 73 12 ac 0a c0 74 0d 48 88 44 fe 88
000060: 04 88 40 ff 88 84 bf fe 03 f2 42 75 e3 e4 60 3c
000070: 01 75 a5 b8 03 00 cd 10 c3 00 00 00 00 4d 63 6d

De la même archive, je me suis retiré:

  • Hiew 6.11 ( 6.50 peut être trouvé sur le site) - Je l'ai utilisé comme démonteur
  • Package TASM - avec lequel j'ai récupéré le code reçu pour m'assurer que je n'ai rien gâché
  • Aide TECH de Flambeaux Software! 6.0 - référence en ligne modérément détaillée et complète pour l'API DOS, les fonctions du BIOS, le matériel et l'assembleur
  • Mayko G.V. Assembleur pour IBM PC - une référence de format presque au format de poche pour toutes les commandes de base Intel 8086 et les règles de formatage du texte du programme. Sans détails architecturaux et avec des exemples élémentaires, seules les choses les plus élémentaires. Il y a presque tout ce dont vous avez besoin ici, mais vous ne pouvez pas écrire dans l'assembleur en dehors de l'environnement.
  • Par conséquent, le deuxième livre Zubkov S.V. Assembleur. Pour DOS, Windows et Unix - Guide des coins matériels et DOS

De l'implémentation minimale extrême, il faut s'attendre à l'utilisation d'astuces et d'approches non standard, mais à part quelques hypothèses dans les conditions initiales, je n'ai pas vu d'astuces techniques, mais j'ai vu une astuce algorithmique. Et voici quelques mots sur l'expérience. Quelle pourrait être la difficulté? Soit dans l'implémentation, soit dans l'algorithme. Par exemple, dans la commande mov di, 099d1h , vous pourriez avoir peur d'une constante magique. Mais si vous êtes dans le contexte d'utilisation, il devient clair qu'il s'agit de l'adresse d'accès aux coordonnées d'écran X et Y, où X = 17, Y = 123, 320 est la résolution horizontale de l'écran en pixels. Ensemble, cela nous donne 17 + 123 * 320, la conversion des coordonnées bidimensionnelles en une dimension.

En regardant maintenant ce qui se passe à l'écran, je peux facilement imaginer comment je pourrais l'implémenter, même si ce n'est pas avec 128 octets, même s'il n'est pas 100% similaire, mais je le pourrais. Et il y a 20 ans, je ne le pouvais pas, bien que j'aie sorti tous les outils que j'utilisais des étagères poussiéreuses et que je n'étais pas obligé de naviguer sur Internet pour comprendre comment cela fonctionnait. Par conséquent, tout d'abord, c'est un contexte, une compréhension de ce qui se passe, et donc la question des astuces et COMMENT faire cela est en deuxième place.

Que voyons-nous:

  1. 5 lignes du pentagramme. Ce ne sont pas nécessairement des lignes directes inextricables selon tous les canons. Nous ne voyons que la figure générale, sans détails
  2. L'effet de flamme, qui se compose de deux parties importantes: une palette correctement sélectionnée et un algorithme pour changer constamment la couleur des points sur l'écran avec des éléments d'incertitude, mais en maintenant une séquence de palette continue pour les points voisins. Par exemple, vous pouvez calculer l'intégralité de l'écran actuel en faisant la moyenne des valeurs des pixels voisins de l'écran précédent, et ajouter plus de points «lumineux» à des endroits aléatoires, ou pas à des endroits aléatoires, mais de valeur aléatoire, ou pas du tout par hasard, éloignez-vous simplement de l'ordre linéaire. Une option est de savoir comment cela se fait dans DOOM . Le résultat doit se présenter sous la forme de couleurs qui s’écoulent les unes dans les autres, des zones lumineuses émergentes en permanence à la décoloration

Reste à comprendre comment cela a été fait. Une description plus détaillée ne remplacera pas les connaissances sur l'architecture informatique et les fonctions DOS ou assembleur, mais avoir ces connaissances vous permettra de comprendre et de vous concentrer sur l'essence de ce qui se passe. Ayant commencé à écrire, je me suis rendu compte que cela se passait quand même assez en détail, mais je ne pouvais pas le refuser pour ne pas perdre au sens de l'histoire.

DOS et chargement de programmes .COM


Le programme dans le fichier .com est un code propre, pas d'en-têtes, il vous suffit de le mettre au bon endroit. C'est ce que fait DOS, ou plutôt l'appel système 4Bh. Beaucoup d'actions se déroulent, arrêtons-nous sur le résultat:

  • Tous les registres de segments CS, DS, ES, SS chargés avec une seule valeur
  • 65536 octets sont réservés pour l'ensemble du programme, exactement un segment auquel tous les registres de segments indiquent. Les 256 premiers octets sont occupés par l'en-tête du système - PSP (Program Segment Prefix). À CS: 0, le premier champ de la PSP, la commande INT 20h se trouve - pour terminer le programme en cours et transférer le contrôle au processus parent. Le programme lui-même démarre avec l'adresse CS: 100h et occupe les 128 octets suivants
  • Le mot 0000h est poussé sur la pile, le registre SP est FFFEh. Cela signifie que les deux derniers octets de ce segment à l'adresse SS: FFFEh sont réinitialisés. En fait, c'est l'adresse de retour la plus proche de la procédure, ce qui nous mènera à la commande d'achèvement sur CS: 0
  • Les registres AL et AH contiennent un indicateur d'erreur pour déterminer les lettres de lecteur à partir du premier et du deuxième argument lorsque le programme est appelé. S'il n'y a pas d'erreurs, alors elles sont 0, s'il y a alors FFh

Je croyais sincèrement que dans le cas général le statut des registres n'est pas défini. Mais dans le code analysé, à mon avis, une hypothèse très audacieuse est faite sur leur état initial, en particulier sur les registres CX, SI et l'indicateur de direction DF. Je n'ai pas trouvé de confirmation de cela dans la liste des sources qui a résulté ci-dessus, alors je suis allé chercher dans les sources MS-DOS 2.0 :

  • Concernant DF, nous pouvons supposer qu'il a été réinitialisé par la commande cld , car cette dernière utilise la direction avant avant de transférer le contrôle aux sauts de ligne, par conséquent, DF est réinitialisé. Bien qu'il n'y ait pas d'utilisation explicite de cld à cet endroit, la commande pour effacer l'indicateur de direction est rencontrée assez souvent avant de nombreux autres transferts
  • SI contient 100h, car il est utilisé pour déterminer le décalage qui sera chargé dans le registre par le compteur de commandes IP
  • CX est égal à FFh, car il est utilisé comme compteur avec une valeur initiale de 80h pour transférer le contenu de la ligne de commande entière et, par conséquent, après le transfert, il est 0. Et après cela, CL, en tant que variable temporaire, charge FFh et est utilisé pour définir l'indicateur d'erreur de la lettre de lecteur en AL et AH

Il n'y a pas de sources de versions plus récentes, mais il existe des sources DOSBox :

 reg_ax=reg_bx=0;reg_cx=0xff; reg_dx=pspseg; reg_si=RealOff(csip); reg_di=RealOff(sssp); 

Autrement dit, cela coïncide avec ce que j'ai vu dans le code source MS-DOS (2e version!), Vous pouvez voir les valeurs initiales des autres registres, ici c'est une initialisation explicite et spéciale. Pour MS-DOS, les valeurs des registres autres que AX, segment et stack sont des rudiments de leur utilisation à d'autres fins; ce n'est pas un dogme ou une norme, par conséquent, elles ne sont mentionnées nulle part. Mais d'un autre côté, l'écosystème qui s'est formé et toute la peine de Microsoft à prendre en charge la compatibilité avec les anciennes versions, forçant à faire glisser toutes les valeurs générées aléatoirement, devient un peu compréhensible, car les programmeurs y sont tellement habitués.

Enfin, pour nous cette connaissance est suffisante, nous commençons à restaurer le programme à partir des en-têtes:

 .186 .model tiny .code .startup 

Nous déterminons le type de processeur 80186, car nous utilisons la commande outsb , qui n'apparaissait que dans ce modèle. Un segment de code et un point d'entrée au programme, qui, avec la définition du tiny modèle de mémoire, permettront au compilateur de calculer correctement tous les décalages de variables et de transitions. Lors de la création de tlink , le tlink /t tlink utilisé; sur la sortie, cela donnera un fichier .com .

Graphisme et palette


Pour passer en mode graphique, vous devez vous tourner vers la fonction BIOS, pour laquelle une interruption de 10h, AH = 0 est appelée, en AL nous mettons l'identifiant du mode souhaité - 13h:

 mov al, 13h ;b0 13 int 10h ;cd 10 

Veuillez noter que nous ne touchons pas AH, en supposant qu'il y a zéro, selon les conditions de chargement du programme. Le mode sélectionné correspond à une résolution graphique de 320 x 200 pixels avec une palette de 256 couleurs. Pour afficher un point à l'écran, vous devez écrire dans la zone mémoire, qui commence par l'adresse A000h: 0, l'octet correspondant à la couleur. Remplissez les registres de données de segment avec cette valeur:

 push 0a000h ;68 00 a0 pop es ;07 push es ;06 pop ds ;1f 

Logiquement, la mémoire est organisée comme un tableau bidimensionnel dans lequel les coordonnées de l'écran sont affichées, 0: 0 correspond au coin supérieur gauche. Après avoir changé de mode, il est rempli de zéros - noirs dans la palette par défaut. La formule pour traduire en déplacement linéaire est X + Y * L , où L est la résolution horizontale, dans notre cas 320. Dans ce formulaire, j'écrirai aux endroits où les constantes sont utilisées, lors de la traduction du texte du programme, elles sont calculées automatiquement.

Pour changer de palette, on accède directement à l'équipement en utilisant les ports d'entrée / sortie:

 lodsb ;ac mov dx, 03c8h ;ba c8 03 out dx, al ;ee 

La première commande charge dans AL l'octet de données situé dans DS: SI. Dans DS, nous avons chargé l'adresse de segment de la mémoire vidéo et nous savons qu'elle est remplie de zéros, dans SI - dans le cas général, on ne sait pas qu'au moins 0. Cela n'a pas d'importance pour nous où SI indique, nous entrons presque certainement dans la mémoire vidéo qui occupe avec cette résolution 320 * 200 = 64000 octets, presque tout le segment. Ainsi, nous nous attendons à ce qu'après cette commande AL = 0. Une unité est ajoutée ou soustraite à SI, cela dépend du réglage du drapeau de direction DF. Bien que cela ne soit pas particulièrement important pour nous, peu importe où le SI se déplace, nous restons toujours dans la zone de mémoire vidéo remplie de zéros.

Ensuite, chargez le DX avec le numéro de port 03C8h, dont la sortie détermine la couleur de 256 que nous allons remplacer. Dans notre cas, c'est 0 de AL.

La couleur est codée dans la palette RVB et pour cela, vous devez écrire sur le port 03C9h (un de plus de 3C8h) trois fois de suite, une fois pour chacun des composants. La luminosité maximale du composant est de 63, le minimum est de 0.

 inc dx ;42 mov cl, 64 ;b1 40 PALETTE: out dx, al ;ee inc ax ;40 outsb ;6e outsb ;6e loop PALETTE ;e2 fa(-6),    6   

Augmentez DX de un pour qu'il ait le numéro de port souhaité. CL est notre compteur de cycles de 64, et nous supposons que CH = 0, comme décrit précédemment en fonction des conditions de chargement initiales. Ensuite, nous sortons le premier composant sur le port - le rouge, dont la luminosité sera stockée dans AL, c'est lui que nous changerons, dans la première étape 0. Après cela, nous augmentons sa luminosité de un pour l'afficher dans la prochaine itération. Ensuite, nous outsb deux commandes outsb écrivent sur le port, dont le numéro est contenu dans DX, l'octet de la zone de mémoire DS: SI, rappelez-vous que nous avons des zéros là. SI à chaque fois change d'un.

Dès que nous avons déduit les trois composants, une unité est automatiquement ajoutée au numéro de couleur. Ainsi, il n'est pas nécessaire de redéfinir la couleur en sortie vers le port 3C8h si les couleurs sont dans une rangée, comme requis. La commande de loop réduira CX de un, si une valeur non nulle est obtenue, elle ira au début du cycle, si 0, puis à la commande suivante après le cycle.

Un total de 64 répétitions. À chaque répétition, nous déterminons pour la couleur, en commençant de 0 à 63, la composante rouge dont la luminosité coïncide avec le numéro de couleur actuel. Nous réinitialisons les composants vert et bleu pour obtenir une telle palette de luminosité rouge minimale à maximale:

palette


Lignes


Configurez la couleur et les coordonnées initiales:

 LINES: mov ax, 03f3fh ;b8 3f 3f mov bx, 0+1*320 ;bb 40 01 mov di, 64+4*320 ;bf 40 05 push di ;57 

Dans AL et AH, nous chargeons la couleur maximale possible (la plus brillante) 63 (3Fh), respectivement, AX définit deux points à la fois. BX - résolution horizontale maximale. À l'avenir, cela sera utilisé pour ajouter ou soustraire une ligne des coordonnées actuelles. DI - coordonnées 64: 4, enregistrez-les sur la pile.

Tracez la première ligne du coin supérieur gauche à l'extrême droite :

 mov cl, 120 ;b1 78 LINE1: stosw ;ab add di, bx ;03 fb loop LINE1 ;e2 fb(-5) 

Configurez le compteur - ce sera le nombre de lignes. Ensuite, enregistrez le mot (deux octets) de AX à l'adresse ES: DI. Cette action affichera deux points sur l'écran avec la couleur maximale de notre palette, car l'ES est configuré pour la mémoire vidéo et des coordonnées spécifiques sont définies dans DI. Après cette action, 2 seront ajoutés au DI, puisque deux octets ont été écrits. Nous ne définissons évidemment pas l'indicateur de direction DF et comptons sur le fait qu'il est réinitialisé, nous rappelons à nouveau nos conditions initiales de chargement du programme. Sinon, les deux seraient supprimés, ce qui ne permettrait pas de tracer la ligne souhaitée.

Ensuite, DI = DI + BX, ce qui équivaut à augmenter la coordonnée Y d'une unité. Ainsi, dans le corps du cycle, deux points sont tracés sur une même ligne, la coordonnée X est augmentée de 2, et la coordonnée Y de 1 et cette action est répétée 120 fois, l'image avec le résultat est légèrement inférieure.

La deuxième ligne va du haut à gauche vers le haut :

 pop di ;5f mov cl, 96 ;b1 60 LINE2: mov [bx+di], al ;88 01 stosb ;aa add di, bx ;03 fb add di, bx ;03 fb loop LINE2 ;e2 f7(-9) 

Nous restaurons les coordonnées initiales 64: 4 et mettons le compteur à 96 répétitions. Nous imprimons un point, mais une ligne sous les coordonnées actuelles. Comme précédemment, ceci est réalisé en ajoutant une valeur à partir de BX, uniquement sans enregistrer les nouvelles coordonnées. La construction [bx+di] ou [bx][di] est appelée adressage de base avec indexation et fonctionne au niveau du processeur, pas du traducteur. Le registre de segment par défaut avec BX est DS. Après quoi, nous affichons le deuxième point, mais déjà dans les coordonnées actuelles. DI, et donc X augmente d'une unité, car une seule commande de transfert d'octets est stosb - stosb . Les deux dernières commandes du corps du cycle sont une augmentation de Y de 2, pour laquelle nous utilisons à nouveau BX.

Après avoir tracé deux lignes, l'image suivante est obtenue près du coin supérieur gauche:

ligne 1,2


Coordonnées gauche et haut, droite de l'adresse de décalage de ligne dans la mémoire vidéo. Le point 64: 4 sera tiré deux fois.

La troisième ligne va du haut vers le coin supérieur droit :

 mov cl, 97 ;b1 61 LINE3: mov [bx+di], al ;88 01 stosb ;aa sub di, bx ;2b fb sub di, bx ;2b fb loop LINE3 ;e2 f7(-9) 

DI contient déjà la valeur de coordonnées souhaitée 160: 196, nous devons tracer une ligne à partir du haut où la ligne précédente s'est terminée, en remontant l'écran tout en conservant le même angle. En conséquence, le cycle est presque identique. CX est augmenté de 1, car la coordonnée Y actuelle est 2 de plus (inférieure) que là où se terminait la ligne précédente, elle a déjà été calculée pour l'itération suivante. Par conséquent, pour atteindre le coin supérieur, vous devez faire un pas supplémentaire. Le mouvement le long de X continue dans la même direction - plus un après chaque itération, et le long de Y, au lieu d'ajouter, nous soustrayons les deux. Les points sont affichés dans le même ordre, d'abord inférieur puis supérieur.

ligne 3


La quatrième ligne va du coin le plus à gauche au coin supérieur droit:

 mov di, 17+123*320 ;bf d1 99 push di ;57 mov cl, 120 ;b1 78 LINE4: stosw ;ab sub di, bx ;2b fb(-5) loop LINE4 

Nous sommes à nouveau dans les coordonnées nécessaires, mais cela n'est pas utilisé, apparemment pour ne pas changer le drapeau de direction DF. Par conséquent, de nouvelles coordonnées sont placées dans le DI et stockées sur la pile.

De plus, tout est identique à la première ligne, seule la coordonnée Y ne grandit pas, mais diminue, on monte.

La cinquième ligne est horizontale:

 pop di ;5f mov cl, 143 ;b1 8f rep stosw ;f3 ab 

Tout est simple ici, le mécanisme de retransmission du microprocesseur est utilisé, car la ligne horizontale correspond à une simple augmentation de l'adresse de chaque point suivant. Dans DI, l'adresse correspondant à la coordonnée du coin extrême gauche, stockée à l'étape précédente, est restaurée. Le nombre de répétitions dans CX est défini et le préfixe de répétition est appliqué avec la commande de transfert de mots.

Après cette action, nous avons un pentagramme entièrement dessiné dans la couleur la plus brillante. 80 octets utilisés et 48 en réserve.

Magie du feu


Nous définissons les conditions aux limites pour les calculs:

 FLAME: cmp si, 320*200 ;81 fe 00 fa jae NEXT_PIXEL ;73 12 lodsb ;ac or al,al ;0a c0 jz NEXT_PIXEL ;74 0d 

Dans SI, il y aura la coordonnée du point actuel pour les calculs, si nous allons au-delà des limites de l'écran, alors nous n'effectuons aucun calcul avec ce point, nous procédons au calcul du suivant.

lodsb charge un octet de la zone DS: SI dans AL, c'est-à-dire la couleur du point dans les coordonnées actuelles. Si c'est 0, alors nous ne faisons rien non plus et passons au point suivant.

Nouveau calcul des couleurs

Ceci est l'algorithme principal pour changer les valeurs de couleur à l'écran, ce n'est pas une flamme, c'est la base pour cela. Nous calculons les points voisins et réalisons la continuité des couleurs:

 dec ax ;48 mov [si-2], al ;88 44 fe mov [si], al ;88 04 mov [bx+si-1], al ;88 40 ff mov [si-1-1*320], al ;88 84 bf fe 

Soustrayez de AX, en fait de AL, une unité qui contient une valeur de couleur non nulle obtenue à partir des coordonnées actuelles. Ensuite, nous écrivons la valeur obtenue à tous les points voisins, par rapport à la coordonnée actuelle, c'est-à-dire un peu d'entre eux, en fonction de notre palette.

Étant donné qu'après lodsb , la valeur SI a augmenté de un et ne correspond plus au point dont nous lisons la couleur en AL, cela doit être ajusté. Notez que les commandes de transfert d'octets stosb ne sont plus utilisées; à la place, mov est utilisé pour localiser l'adresse où la valeur sera placée. Si nous acceptons que les coordonnées actuelles sont X: Y, pour elles SI-1, alors:

  • mov [si-2], al - enregistre une nouvelle couleur au point X-1: Y, à gauche de la couleur actuelle. 2 est soustrait de SI pour la raison décrite ci-dessus, car une unité supplémentaire lui a déjà été ajoutée
  • mov [si], al - enregistre une nouvelle couleur au point X + 1: Y, à droite de la couleur actuelle. SI a déjà X + 1
  • mov [bx+si-1], al - écrit une nouvelle couleur au point X: Y + 1, en dessous de la couleur actuelle. Utilisez à nouveau BX pour Y + 1
  • mov [si-1-1*320], al - écrit une nouvelle couleur au point X: Y-1, au-dessus de la couleur actuelle. Nous ne pourrons pas utiliser BX, car nous devons supprimer les coordonnées, l'architecture du processeur ne nous permet pas de le faire sous cette forme, donc une constante est utilisée conformément à la formule de réduction des coordonnées

Le registre de segment est DS, qui est utilisé par défaut avec SI et BX.

Nulle part la situation n'est vérifiée lorsque le point atteint le bord de l'écran. Cela ne peut pas conduire à un échec, car nous serons toujours dans les limites du segment vidéo. Un point voisin peut tomber soit dans une zone non déclarée avec des adresses supérieures à 64 000, soit sur une ligne adjacente, ce qui ne nous nuit pas et nous aide même un peu, comme on le verra dans la suite de la description.

La même magie, le calcul des coordonnées du point suivant

 NEXT_PIXEL: add si, dx ;03 f2 inc dx ;42 jnz FLAME ;75 e3(-29) 

Revenons un peu en arrière, nous n'avons pas défini spécifiquement la valeur SI initiale nulle part, et dans DX, nous avons toujours le numéro du port d'entrée de sortie que nous avons utilisé pour la palette. Nous n'effectuons que trois actions simples SI = SI + DX, cela va évidemment définir de nouvelles coordonnées, lesquelles? DX = DX + 1 et si DX n'est pas égal à 0, revenons à l'algorithme de base pour obtenir et calculer les points voisins, c'est-à-dire que DX est une sorte de compteur?

Nous savons que nous devons faire le tour de tous les points et calculer les changements de luminosité de leurs voisins. Si vous faites cela consécutivement, nous obtiendrons probablement un gradient statique, peut-être pas tout à fait même, mais inchangé autour de nos lignes.Nous connaissons la taille de notre écran et le nombre de points dont nous avons besoin pour nous déplacer, mais ici nous le négligeons plus précisément, choisissez la valeur de fermeture 65536 au lieu de l'exacte 64000. DX est vraiment un compteur, juste 65536. Mais pourquoi sa valeur initiale n'est pas importante et pourquoi nous prenons La valeur finale est-elle supérieure au nombre total de points à l'écran?

Parce que nous contournons des points pas dans une rangée et pas tous. Chaque coordonnée linéaire suivante est plus grande que la précédente de la valeur de DX. Soit, en SI, la somme des éléments DX d'une progression arithmétique simple: 0,1,2,3,4,5,6, ..., 362,363, ..., 65535. Cela nous donne déjà une non-linéarité, si vous commencez avec SI = 0 et DX = 0, alors dans SI nous obtenons: 0,1,3,4,6,10,15,21, ..., 65341,65703, ..., 2147450880.

Mais ce n'est pas tout, car la dimension SI est de 16 bits, nous ne pouvons pas obtenir une valeur supérieure à 65535, un débordement se produit et le reste en SI reste modulo 65536. La formule de calcul des coordonnées linéaires prend la forme SI = (SI + DX) MOD 65536, qui rompt complètement l'ordre continu: 0,1,3,4,6,10,15,21, ..., 65341,167,530,894, ...

Maintenant, nous rappelons que SI n'est pas initialisé de quelque façon que ce soit, c'est-à-dire la prochaine fois que nous reviendrons à ce cycle alors nous partirons de la coordonnée où nous nous sommes arrêtés, et non de 0 ou d'une donnée. Cela ajoutera du chaos à notre séquence - allongera le nombre d'éléments non répétitifs. Sinon, la traversée des points serait toujours la même, bien que non linéaire. Un effet de flamme serait présent, mais pas aussi clairement. Si nous parlons de l'astuce, alors c'est tout.DX, toujours, sauf pour la première utilisation, démarre implicitement à 0 à la suite d'un débordement inc dx.

Et un peu plus de chaos est ajouté par nos valeurs limites, car pour SI> = 64000 aucun point ne sera dessiné et la séquence de sortie est légèrement confuse. Et sauter tous les points avec une valeur nulle entraîne l'effet de l'allumage dans les premières secondes du programme. En effet, le cycle complet se termine plus rapidement, car la plupart des points ne sont pas traités. Mais surtout, parce que la luminosité de la plupart des points ne fera qu'augmenter, ils ne peuvent pas être masqués par les sections de gradateur voisines - ils n'existent tout simplement pas encore et les valeurs nulles ne sont pas calculées. Une fois les zones complètement noires disparues, l'équilibre est établi, certaines zones augmenteront la luminosité et d'autres diminueront.

En conséquence, nous ne pouvons plus parler d'aucun ordre ou gradient, les points sont contournés, chaque fois dans une nouvelle séquence, y compris en répétant plusieurs fois ou en sautant complètement. Cela conduit à la formation de régions de luminosité différente mélangées les unes aux autres, changeant à chaque nouvelle itération.

Mais ce n'est pas tout, si vous n'ajoutez pas de nouveaux points lumineux, ils seront finalement tous remboursés. Par conséquent, une fois que le DX a atteint sa valeur maximale, nous revenons pour dessiner cinq lignes lumineuses encore et encore comptons tous les points à l'écran:

 in al, 60h ;e4 60 cmp al, 01h ;3c 01 jne LINES ;75 a5(-91) 

Mais avant cela, nous lisons depuis le port 60h, c'est le clavier, le code de scan de la dernière touche enfoncée. Pour ESC, il est égal à 1. Si c'est le cas, la touche ESC a été enfoncée, on se dirige vers la sortie.

Achèvement


Il convient de noter que lors de la mise à jour de l'écran actuel, ce qui prend un certain temps, vous ne pouvez pas quitter le programme, c'est-à-dire que la réaction à ESC sera retardée. Si pendant l'attente et après avoir appuyé sur une touche ESC, nous resterons dans le programme, seul le dernier code de balayage peut être lu depuis le port. Encore une chose, nous ne remplaçons ni n'utilisons les fonctions système DOS et BIOS pour cela, indépendamment de ce que nous lisons depuis le port, la touche enfoncée est placée dans un tampon circulaire et sera probablement lue à partir de là par le programme suivant une fois notre introduction terminée, fichier le plus probable gestionnaire ou command.com. Cela conduira à son traitement, par exemple, Volkov Commander sur ESC masquera ses panneaux.

Reste à revenir au mode texte 3:

 mov ax, 03h ;b8 03 00 int 10h ;cd 10 

On suppose que nous étions dans ce mode avant le lancement du programme, mais dans le cas général, ce n'est peut-être pas le cas. Ici, nous mettons à jour l'ensemble de l'AX, car nous savons avec certitude que AH ne contient pas 0.

Vous pouvez maintenant quitter:

 retn ;c3 

Il s'agit d'une commande de sortie proche d'une procédure qui prend la valeur du mot placé (deux octets) dans la pile et la charge dans le compteur de commandes IP. Selon les conditions initiales, nous avons des zéros dans la pile, cela nous mènera à l'adresse CS: 0, où, comme nous le savons, le code de commande est situé int 20h- arrêt.

Et 7 octets pour le droit d'auteur:

 dd 0h ;00 00 00 00 db 'Mcm' ;4d 63 6d end 

On peut dire qu'il y a encore une place que je consacrerais à une initialisation plus rigoureuse, mais comme tout fonctionne dans la DOSBox moderne, l'auteur a probablement tout fait correctement.

Revoyons encore une fois:

  1. ,
  2. 4 , : X+1 Y+2, X+2 Y+1. , . ,
  3. SI=(SI+DX) MOD 65536, DX , , , SI. 1. 65536 , , . , — add si, dx inc dx , ,
  4. ESC ,

.
 .186 .model tiny .code .startup mov al, 13h int 10h push 0a000h pop es push es pop ds lodsb mov dx, 03c8h out dx, al inc dx mov cl, 040h PALETTE: out dx, al inc ax outsb outsb loop PALETTE LINES: mov ax, 03f3fh mov bx, 0+1*320 mov di, 64+4*320 push di mov cl, 120 LINE1: stosw add di, bx loop LINE1 pop di mov cl, 96 LINE2: mov [bx+di], al stosb add di, bx add di, bx loop LINE2 mov cl, 97 LINE3: mov [bx+di], al stosb sub di, bx sub di, bx loop LINE3 mov di, 17+123*320 push di mov cl, 120 LINE4: stosw sub di, bx loop LINE4 pop di mov cl, 143 rep stosw FLAME: cmp si, 320*200 jae NEXT_PIXEL lodsb or al,al jz NEXT_PIXEL dec ax mov [si-2], al mov [si], al mov [bx+si-1], al mov [si-1-1*320], al NEXT_PIXEL: add si, dx inc dx jnz FLAME in al, 60h cmp al, 01h jne LINES mov ax, 03h int 10h retn dd 0h db 'Mcm' end 

Pour compiler, vous devez faire: tasm pentagra.asmet tlink /t pentagra.obj.

Je ne sais pas s'il vous est apparu clairement QUOI et COMMENT cela a été mis en œuvre, mais il me semble qu'une approche belle et inhabituelle a été utilisée pour créer l'effet de flamme. Bien que je n'aie rien à comparer, peut-être que tout le monde l'a fait, et maintenant vous pouvez faire de même.

Source: https://habr.com/ru/post/fr482826/


All Articles