Analysieren einer 128-Byte-Demo aus dem Archiv von 1997

Es ist sehr angenehm, meine Wünsche zu erfüllen, besonders aus der fernen Vergangenheit, so fern, dass ich schon vergessen habe, dass ich es einmal wollte. Ich weiß wenig über die Demoszene und bin den Autoren oder ihrer Arbeit auf keinen Fall gefolgt. Ich habe nur gern zugesehen, was passiert ist. Manchmal wollte ich es herausfinden, aber dann fehlte mir das Wissen und die Erfahrung, später die Ausdauer, und dann verlor ich das Interesse daran vollständig. Aber kürzlich besuchte mein Freund, mit dem wir zu dieser Zeit studierten und der uns alle neuen Produkte, einschließlich Demos, mit BBS und Fidonet versorgte, weil er fast alle gleichzeitig ein Telefon und ein Modem und einen Computer besaß, CAFePARTY mit seinen Arbeiten Das hat mich dazu gebracht, das Archiv meines ersten Computers zu öffnen, eine Demo auszuwählen und es herauszufinden.

pentagra.com

Um meine Stärken objektiv beurteilen zu können, habe ich ein 128-Byte-Intro aufgenommen, das mir visuell gefallen hat. Die pentagra.com Datei ist von Mcm signiert, 128 Bytes, zuletzt geändert am 24.09.1996 18:10:14, Hex-Dump:

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

Aus demselben Archiv, das ich herausgezogen habe:

  • Hiew 6.11 ( 6.50 ist auf der Site zu finden) - Ich habe es als Disassembler verwendet
  • TASM- Paket - mit dem ich den erhaltenen Code zurückerhob, um sicherzustellen, dass ich nichts vermasselt habe
  • Die TECH-Hilfe von Flambeaux Software! 6.0 - Mäßig detaillierte und umfassende Online-Referenz für DOS-API, BIOS-Funktionen, Hardware und Assembler
  • Mayko G.V. Assembler für IBM PC - eine fast taschengroße Formatreferenz für alle grundlegenden Intel 8086-Befehle und Programmtextformatierungsregeln. Ohne architektonische Details und mit elementaren Beispielen nur die grundlegendsten Dinge. Hier gibt es fast alles, was Sie brauchen, aber Sie können in Assembler nur in der Umgebung schreiben.
  • Daher ist das zweite Buch Zubkov S.V. Assembler. Für DOS, Windows und Unix - Eine Anleitung zu Hardware-Nooks und DOS

Von der extrem minimalen Implementierung sollte man die Verwendung von Tricks und nicht-standardmäßigen Ansätzen erwarten, aber abgesehen von einigen Annahmen in den Anfangsbedingungen sah ich keine technischen Tricks, aber ich sah einen algorithmischen Trick. Und hier sollten ein paar Worte über die Erfahrung gesagt werden. Was könnte die Schwierigkeit sein? Entweder in der Implementierung oder im Algorithmus. Im Befehl mov di, 099d1h , haben Sie möglicherweise Angst vor einer magischen Konstante. Wenn Sie sich jedoch im Nutzungskontext befinden, wird deutlich, dass dies die Adresse für den Zugriff auf die Bildschirmkoordinaten X und Y ist, wobei X = 17, Y = 123, 320 die horizontale Auflösung des Bildschirms in Pixel ist. Zusammen ergibt dies 17 + 123 * 320, die Umwandlung zweidimensionaler Koordinaten in eindimensionale.

Wenn ich mir jetzt anschaue, was auf dem Bildschirm passiert, kann ich mir leicht vorstellen, wie ich dies implementieren könnte, wenn auch nicht mit 128 Bytes, auch wenn es nicht 100% ähnlich ist, aber ich könnte. Und vor 20 Jahren konnte ich es nicht, obwohl ich alle Werkzeuge, die ich verwendete, aus staubigen Regalen holte und nicht im Internet surfen musste, um zu verstehen, wie es funktioniert. Daher ist dies zuallererst ein Kontext, ein Verständnis dessen, was geschieht, und daher steht die Frage nach Tricks und WIE dies zu tun ist an zweiter Stelle.

Was sehen wir:

  1. 5 Zeilen des Pentagramms. Dies sind nicht unbedingt direkte, untrennbare Linien nach allen Kanonen. Wir sehen nur die allgemeine Figur ohne Details
  2. Der Flammeneffekt, der aus zwei wichtigen Teilen besteht: einer korrekt ausgewählten Palette und einem Algorithmus zum ständigen Ändern der Farbe von Punkten auf dem Bildschirm mit Unsicherheitselementen, wobei jedoch eine kontinuierliche Palettensequenz für benachbarte Punkte beibehalten wird. Sie können beispielsweise den gesamten aktuellen Bildschirm berechnen, indem Sie den Durchschnitt der Werte benachbarter Pixel des vorherigen Bildschirms berechnen und an zufälligen oder nicht zufälligen Stellen, aber zufälligen oder nicht zufälligen Werten weitere „helle“ Punkte hinzufügen. Entfernen Sie sich einfach von der linearen Reihenfolge. Eine Möglichkeit ist, wie es in DOOM gemacht wird . Das Ergebnis sollte in Form von ineinander fließenden Farben sein, von ständig auftauchenden hellen Bereichen bis hin zum Ausbleichen

Es bleibt zu verstehen, wie dies getan wurde. Eine weitere Beschreibung ersetzt nicht das Wissen über Computerarchitektur und DOS- oder Assembler-Funktionen. Mit diesem Wissen können Sie jedoch das Wesentliche des Geschehens verstehen und sich darauf konzentrieren. Nachdem ich mit dem Schreiben angefangen hatte, wurde mir klar, dass es sich immer noch als ausreichend detailliert herausstellte, aber ich konnte es nicht ablehnen, um nicht im Sinne der Geschichte zu verlieren.

DOS und Laden von .COM-Programmen


Das Programm in der .com Datei ist sauberer Code, keine Überschriften, Sie müssen es nur an der richtigen Stelle platzieren. Das macht DOS, oder besser gesagt der 4Bh-Systemaufruf. Viele Aktionen finden statt, lassen Sie uns das Ergebnis näher betrachten:

  • Alle Segmentregister CS, DS, ES, SS werden mit einem einzigen Wert geladen
  • 65536 Bytes sind für das gesamte Programm reserviert, genau ein Segment, auf das sich alle Segmentregister beziehen. Die ersten 256 Bytes werden vom Systemheader PSP (Program Segment Prefix) belegt. Bei CS: 0, dem ersten Feld der PSP, befindet sich der Befehl INT 20h, um das aktuelle Programm zu beenden und die Steuerung an den übergeordneten Prozess zu übertragen. Das Programm selbst startet mit der Adresse CS: 100h und belegt die folgenden 128 Bytes
  • Das Wort 0000h wird auf den Stapel geschoben, das SP-Register ist FFFEh. Dies bedeutet, dass die letzten zwei Bytes in diesem Segment an der Adresse SS: FFFEh zurückgesetzt werden. Tatsächlich ist dies die nächste Rücksprungadresse aus der Prozedur, die uns zum Beendigungsbefehl bei CS: 0 führt
  • Die Register AL und AH enthalten ein Fehlerflag zum Bestimmen der Laufwerksbuchstaben aus dem ersten und zweiten Argument, wenn das Programm aufgerufen wird. Wenn es keine Fehler gibt, sind sie 0, wenn es dann FFh gibt

Ich bin fest davon überzeugt, dass der Status der Register im Allgemeinen nicht definiert ist. Aber in dem analysierten Code wird meiner Meinung nach eine sehr kühne Annahme über ihren Anfangszustand getroffen, insbesondere über die CX-, SI-Register und das DF-Richtungsflag. Ich habe keine Bestätigung dafür in der Liste der Quellen gefunden, die sich oben ergeben haben, also habe ich die MS-DOS 2.0- Quellen durchgesehen:

  • Bei DF können wir davon ausgehen, dass es durch den Befehl cld , da dieser die Vorwärtsrichtung verwendet, bevor die Steuerung auf cld übertragen wird. Daher wird DF zurückgesetzt. Obwohl cld an dieser Stelle nicht explizit verwendet wird, wird der Befehl zum Löschen des Richtungsflags häufig vor vielen anderen Übertragungen ausgeführt
  • SI enthält 100h, da es verwendet wird, um den Offset zu bestimmen, der vom IP-Befehlszähler in das Register geladen wird
  • CX ist gleich FFh, weil es als Zähler mit einem Anfangswert von 80h zum Übertragen des Inhalts der gesamten Befehlszeile verwendet wird und dementsprechend nach dem Übertragen 0 ist. Danach lädt CL als temporäre Variable FFh und setzt das Fehlerflag des Laufwerksbuchstabens in AL und AH

Es gibt keine Quellen für neuere Versionen, aber DOSBox-Quellen :

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

Das heißt, es stimmt mit dem überein, was ich im MS-DOS-Quellcode (2. Version!) Gesehen habe. Sie können die Anfangswerte anderer Register sehen, hier handelt es sich um eine explizite, spezielle Initialisierung. Für MS-DOS sind die Werte anderer Register als AX, Segment und Stack Ansätze für ihre Verwendung für andere Zwecke, dies ist kein Dogma oder Standard, daher werden sie nirgendwo erwähnt. Andererseits wird das Ökosystem, das sich gebildet hat, und der ganze Schmerz von Microsoft, die Kompatibilität mit alten Versionen zu unterstützen und alle zufällig generierten Werte mitzureißen, ein wenig verständlich, weil Programmierer so an sie gewöhnt sind.

Schließlich reicht uns dieses Wissen, und wir beginnen, das Programm aus den Headern wiederherzustellen:

 .186 .model tiny .code .startup 

Wir bestimmen den Prozessortyp 80186, da wir den Befehl outsb , der nur in diesem Modell vorkommt. Ein Codesegment und ein Einstiegspunkt in das Programm ermöglichen es dem Compiler zusammen mit der Definition des tiny Speichermodells, alle Offsets von Variablen und Übergängen korrekt zu berechnen. Beim tlink wird der tlink /t verwendet, der bei der Ausgabe eine .com Datei ergibt.

Grafik und Palette


Um in den Grafikmodus zu wechseln, müssen Sie sich an die BIOS-Funktion wenden, für die eine Unterbrechung von 10h, AH = 0, aufgerufen wird. In AL geben wir die Kennung des gewünschten Modus ein - 13h:

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

Bitte beachten Sie, dass wir AH nicht berühren, wenn wir davon ausgehen, dass es gemäß den Programmladebedingungen Null gibt. Der ausgewählte Modus entspricht einer Grafikauflösung von 320 x 200 Pixel mit einer 256-Farben-Palette. Um einen Punkt auf dem Bildschirm anzuzeigen, müssen Sie in den Speicherbereich schreiben, der mit der Adresse A000h: 0 beginnt, wobei das Byte der Farbe entspricht. Segmentdatenregister mit diesem Wert füllen:

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

Logischerweise ist der Speicher als zweidimensionales Array organisiert, in dem Bildschirmkoordinaten angezeigt werden. 0: 0 entspricht der oberen linken Ecke. Nach dem Umschalten des Modus wird er in der Standardpalette mit Nullen gefüllt - schwarz. Die Formel für die Übersetzung in eine lineare Verschiebung lautet X + Y * L , wobei L die horizontale Auflösung ist, in unserem Fall 320. In dieser Form schreibe ich an den Stellen, an denen die Konstanten verwendet werden, wenn der Programmtext übersetzt wird, werden sie automatisch berechnet.

Um die Palette zu ändern, greifen wir über die Eingabe- / Ausgabeports direkt auf das Gerät zu:

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

Der erste Befehl lädt das bei DS: SI befindliche Datenbyte in AL. In DS haben wir die Segmentadresse des Videospeichers geladen und wissen, dass sie mit Nullen gefüllt ist, in SI - im Allgemeinen ist mindestens 0 nicht bekannt. Es spielt für uns keine Rolle, wo immer SI angibt, dass wir mit ziemlicher Sicherheit in den Videospeicher gelangen, der belegt mit dieser auflösung 320 * 200 = 64000 bytes fast das gesamte segment. Daher erwarten wir, dass nach diesem Befehl AL = 0 ist. Eine Einheit wird zu SI addiert oder subtrahiert. Dies hängt von der Einstellung des DF-Richtungsflags ab. Auch wenn dies für uns nicht besonders wichtig ist, bleiben wir in dem mit Nullen gefüllten Videospeicherbereich, egal wo sich der SI bewegt.

Laden Sie als nächstes den DX mit der Portnummer 03C8h, deren Ausgang bestimmt, welche der 256 Farben überschrieben werden soll. In unserem Fall ist es 0 von AL.

Die Farbe wird in der RGB-Palette codiert. Dazu müssen Sie dreimal hintereinander einmal für jede der Komponenten auf Port 03C9h schreiben (einer mehr als 3C8h). Die maximale Helligkeit der Komponente beträgt 63, die minimale 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   

Erhöhen Sie DX um eins, damit es die gewünschte Portnummer hat. CL ist unser Zykluszähler von 64, und wir gehen davon aus, dass CH = 0 ist, wie zuvor beschrieben, basierend auf den anfänglichen Ladebedingungen. Als nächstes geben wir die erste Komponente an den Port aus - die rote, deren Helligkeit in AL gespeichert wird. In Schritt 0 ändern wir sie. Danach erhöhen wir ihre Helligkeit um eins, um sie in der nächsten Iteration anzuzeigen. Als nächstes führen wir zwei outsb Befehle aus outsb die auf den Port schreiben, dessen Nummer in DX, dem Byte aus dem DS-Speicherbereich, enthalten ist: SI, denken Sie daran, dass wir dort Nullen haben. SI ändert sich jedes Mal um eins.

Sobald wir die drei Komponenten abgeleitet haben, wird der Farbnummer automatisch eine Einheit hinzugefügt. Daher ist es nicht erforderlich, die Farbe durch Ausgabe an den 3C8h-Port neu zu definieren, wenn die Farben nach Bedarf in einer Reihe liegen. Der loop reduziert CX um eins. Wenn ein Wert ungleich Null erhalten wird, springt er zum Beginn des Zyklus, bei 0 zum nächsten Befehl nach dem Zyklus.

Insgesamt 64 Wiederholungen. Bei jeder Wiederholung bestimmen wir für die Farbe, beginnend von 0 bis 63, die Rotkomponente mit der Helligkeit, die mit der aktuellen Farbnummer übereinstimmt. Wir setzen die grünen und blauen Komponenten zurück, um eine solche Palette von minimaler zu maximaler roter Helligkeit zu erhalten:

Palette


Linien


Richten Sie die anfänglichen Farb- und Koordinatenwerte ein:

 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 

In AL und AH laden wir die maximal mögliche (hellste) Farbe 63 (3Fh), AX definiert zwei Punkte gleichzeitig. BX - maximale horizontale Auflösung. In Zukunft wird dies verwendet, um eine Zeile zu den aktuellen Koordinaten zu addieren oder von diesen zu subtrahieren. DI - Koordinaten 64: 4, speichern Sie sie auf dem Stapel.

Zeichne die erste Linie von der oberen linken Ecke bis zum rechten Ende :

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

Konfigurieren Sie den Zähler - dies ist die Anzahl der Zeilen. Speichern Sie als nächstes das Wort (zwei Bytes) von AX unter der Adresse ES: DI. Diese Aktion zeigt zwei Punkte auf dem Bildschirm mit der maximalen Farbe aus unserer Palette an, da der ES für den Videospeicher konfiguriert ist und bestimmte Koordinaten in DI festgelegt werden. Nach dieser Aktion wird 2 zum DI hinzugefügt, da zwei Bytes geschrieben wurden. Wir setzen das DF-Richtungs-Flag offensichtlich nicht und verlassen uns auf die Tatsache, dass es zurückgesetzt wird. Wir erinnern uns erneut an unsere ursprünglichen Bedingungen für das Laden des Programms. Andernfalls würden die beiden weggenommen, was nicht erlauben würde, die gewünschte Linie zu zeichnen.

Als nächstes ist DI = DI + BX, was einer Erhöhung der Y-Koordinate um eins entspricht. Im Hauptteil des Zyklus werden also zwei Punkte in einer Linie gezeichnet, die X-Koordinate wird um 2 und die Y-Koordinate um 1 erhöht, und diese Aktion wird 120 Mal wiederholt. Das Bild mit dem Ergebnis ist geringfügig niedriger.

Die zweite Zeile verläuft von links oben nach oben :

 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) 

Wir stellen die Anfangskoordinaten 64: 4 wieder her und setzen den Zähler auf 96 Wiederholungen. Wir drucken einen Punkt, aber eine Linie unter den aktuellen Koordinaten. Dies wird nach wie vor durch Hinzufügen eines Wertes aus BX erreicht, nur ohne die neuen Koordinaten zu speichern. Die Konstruktion [bx+di] oder [bx][di] wird Basisadressierung mit Indexierung genannt und arbeitet auf Prozessorebene, nicht auf Übersetzerebene. Das Standardsegmentregister bei BX ist DS. Danach zeigen wir den zweiten Punkt an, jedoch bereits in den aktuellen Koordinaten. DI und damit X um eins, da nur ein Byte-Übertragungsbefehl stosb - stosb . Die letzten beiden Befehle des Zykluskörpers sind eine Erhöhung von Y um 2, für die wir wieder BX verwenden.

Nach dem Zeichnen von zwei Linien wird das folgende Bild in der Nähe der oberen linken Ecke erhalten:

Zeile 1.2


Linke und obere Koordinaten, rechts von der Zeilenversatzadresse im Videospeicher. Punkt 64: 4 wird zweimal gezogen.

Die dritte Zeile verläuft von oben nach rechts oben :

 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 enthält bereits den gewünschten Koordinatenwert 160: 196. Wir müssen eine Linie von oben an der Stelle zeichnen, an der die vorherige Linie geendet hat. Dabei müssen wir den Bildschirm nach oben bewegen und den gleichen Winkel beibehalten. Dementsprechend ist der Zyklus nahezu identisch. CX wird um 1 erhöht, da die aktuelle Y-Koordinate 2 mehr (niedriger) ist als am Ende der vorherigen Zeile. Sie wurde bereits für die nächste Iteration berechnet. Um in die obere Ecke zu gelangen, müssen Sie daher einen zusätzlichen Schritt machen. Die Bewegung entlang X setzt sich in derselben Richtung fort - plus eins nach jeder Iteration, und entlang Y subtrahieren wir die beiden, anstatt sie zu addieren. Die Punkte werden in derselben Reihenfolge angezeigt, zuerst unten, dann oben.

Zeile 3


Die vierte Zeile befindet sich ganz links in der oberen rechten Ecke:

 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 

Wir sind wieder in den nötigen Koordinaten, aber dies wird anscheinend nicht verwendet, um das DF-Richtungs-Flag nicht zu ändern. Daher werden neue Koordinaten in das DI gestellt und auf dem Stapel gespeichert.

Weiterhin ist alles identisch mit der ersten Zeile, nur die Y-Koordinate wächst nicht, sondern nimmt ab, wir steigen auf.

Die fünfte Zeile ist horizontal:

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

Hier ist alles einfach, es wird der Mikroprozessor-Neuübertragungsmechanismus verwendet, da die horizontale Linie einer einfachen Erhöhung der Adresse jedes nächsten Punktes entspricht. In DI wird die Adresse wiederhergestellt, die der im vorherigen Schritt gespeicherten Koordinate der linken äußersten Ecke entspricht. Die Anzahl der Wiederholungen in CX wird festgelegt und das Wiederholungspräfix wird mit dem Wortübertragungsbefehl angewendet.

Nach dieser Aktion haben wir ein vollständig gezeichnetes Pentagramm in der hellsten Farbe. 80 Bytes verwendet und 48 in Reserve.

Feuermagie


Wir setzen die Randbedingungen für die Berechnungen:

 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 

In SI gibt es die Koordinate des aktuellen Punktes für Berechnungen. Wenn wir über die Grenzen des Bildschirms hinausgehen, führen wir keine Berechnungen mit diesem Punkt durch und fahren mit der Berechnung des nächsten Punktes fort.

lodsb lädt ein Byte aus dem DS: SI-Bereich in AL, lodsb die Farbe des Punkts in den aktuellen Koordinaten. Wenn es 0 ist, dann tun wir auch nichts und fahren mit dem nächsten Punkt fort.

Neue Farbberechnung

Dies ist der Hauptalgorithmus zum Ändern der Farbwerte auf dem Bildschirm, dies ist keine Flamme, dies ist die Basis dafür. Wir berechnen Nachbarpunkte und erzielen Farbkontinuität:

 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 

Subtrahieren Sie von AX, tatsächlich von AL, eine Einheit, die einen von Null verschiedenen Farbwert enthält, der aus den aktuellen Koordinaten erhalten wird. Als nächstes schreiben wir den erhaltenen Wert auf alle benachbarten Punkte, relativ zur aktuellen Koordinate, das heißt ein bisschen von ihnen, basierend auf unserer Palette.

Da nach lodsb der SI-Wert um eins ansteigt und nicht mehr dem Punkt entspricht, dessen Farbe wir in AL lesen, muss dieser angepasst werden. Beachten Sie, dass stosb Byte-Übertragungsbefehle mehr verwendet werden, sondern mov , um die Adresse zu bestimmen, an der der Wert platziert wird. Wenn wir akzeptieren, dass die aktuellen Koordinaten X: Y sind, für sie SI-1, dann:

  • mov [si-2], al - mov [si-2], al eine neue Farbe am Punkt X-1: Y links von der aktuellen auf. 2 wird aus dem oben beschriebenen Grund von SI subtrahiert, da bereits eine zusätzliche Einheit hinzugefügt wurde
  • mov [si], al eine neue Farbe am Punkt X + 1: Y rechts von der aktuellen auf. SI hat bereits X + 1
  • mov [bx+si-1], al - Schreiben einer neuen Farbe an den Punkt X: Y + 1 unter der aktuellen. Verwenden Sie wieder BX für Y + 1
  • mov [si-1-1*320], al - Schreiben einer neuen Farbe an den Punkt X: Y-1 über der aktuellen. Wir können BX nicht verwenden, da wir die Koordinate entfernen müssen. Aufgrund der Prozessorarchitektur ist dies in dieser Form nicht möglich. Daher wird eine Konstante gemäß der Koordinatenreduktionsformel verwendet

Das Segmentregister ist DS, das standardmäßig mit SI und BX verwendet wird.

Nirgendwo wird die Situation überprüft, wenn der Punkt den Rand des Bildschirms berührt. Dies kann nicht zu einem Ausfall führen, da wir uns immer innerhalb der Grenzen des Videosegments befinden. Ein benachbarter Punkt kann entweder in einen nicht gemeldeten Bereich mit Adressen über 64.000 fallen oder auf eine benachbarte Linie, die uns nicht schadet und sogar ein wenig hilft, wie aus der weiteren Beschreibung hervorgeht.

Dieselbe Magie, die Berechnung der Koordinaten des nächsten Punktes

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

Gehen wir noch ein bisschen zurück, wir haben den anfänglichen SI-Wert nirgendwo speziell festgelegt und in DX haben wir immer noch die Nummer des Ausgabe-Eingangsports, den wir für die Palette verwendet haben. Wir führen nur drei einfache Aktionen durch: SI = SI + DX. Dies setzt natürlich neue Koordinaten. Welche? DX = DX + 1 und wenn DX nicht gleich 0 ist, dann zurück zum Basisalgorithmus zum Erhalten und Berechnen benachbarter Punkte, das heißt, ist DX eine Art Zähler?

Wir wissen, dass wir alle Punkte umgehen und die Helligkeitsänderungen ihrer Nachbarn berechnen müssen. Wenn Sie dies in einer Reihe tun, erhalten Sie wahrscheinlich einen statischen Gradienten, vielleicht nicht ganz gleichmäßig, aber unverändert um unsere Linien.Wir kennen die Größe unseres Bildschirms und wissen, wie viele Punkte wir benötigen, aber hier vernachlässigen wir sie fast, genauer gesagt, wählen Sie den Schlusswert 65536 anstelle des exakten 64000. DX ist wirklich ein Zähler, nur 65536. Aber warum ist sein Anfangswert nicht wichtig und warum nehmen wir Ist der Endwert größer als die Gesamtpunktzahl auf dem Bildschirm?

Weil wir Punkte nicht hintereinander und nicht alle umgehen. Jede nachfolgende lineare Koordinate ist um den Wert von DX größer als die vorherige. Das heißt, in SI die Summe der DX-Elemente einer einfachen arithmetischen Folge: 0,1,2,3,4,5,6, ..., 362,363, ..., 65535. Dies gibt uns bereits Nichtlinearität, wenn Sie mit SI = 0 und DX = 0 beginnen, dann erhalten wir in SI: 0,1,3,4,6,10,15,21, ..., 65341,65703, ..., 2147450880.

Das ist aber noch nicht alles, da die SI-Dimension 16 Bit beträgt, wir keinen Wert größer als 65535 erhalten können, ein Überlauf auftritt und der Rest in SI modulo 65536 bleibt. Die lineare Koordinatenberechnungsformel hat die Form SI = (SI + DX) MOD 65536, Dies unterbricht die kontinuierliche Reihenfolge vollständig: 0,1,3,4,6,10,15,21, ..., 65341,167,530,894, ...

Nun erinnern wir uns, dass SI in keiner Weise initialisiert wird, das heißt, wenn wir das nächste Mal zu diesem Zyklus zurückkehren dann fangen wir bei der Koordinate an, bei der wir aufgehört haben, und nicht bei 0 oder einer bestimmten. Dies wird unsere Sequenz chaotisch machen - die Anzahl der sich nicht wiederholenden Elemente erhöhen. Andernfalls wäre die Durchquerung der Punkte immer gleich, wenn auch nicht linear. Ein Flammeneffekt wäre vorhanden, aber nicht so deutlich. Wenn wir über den Trick sprechen, dann ist dies genau das Richtige.DX beginnt mit Ausnahme der ersten Verwendung immer implizit bei 0 als Ergebnis eines Überlaufs inc dx.

Ein wenig mehr Chaos bringen unsere Grenzwerte, da für SI> = 64000 keine Punkte gezogen werden und die Ausgabereihenfolge leicht verwirrt ist. Und das Überspringen aller Punkte mit einem Nullwert führt in den ersten Sekunden des Programms zum Zündeffekt. Dies liegt daran, dass der vollständige Zyklus schneller endet, da die meisten Punkte nicht verarbeitet werden. Da die Helligkeit für die meisten Punkte jedoch nur zunimmt, können sie nicht durch benachbarte Dimmerabschnitte verdeckt werden. Sie sind einfach noch nicht vorhanden und es werden keine Nullwerte berechnet. Nachdem die vollständig schwarzen Bereiche verschwunden sind, wird das Gleichgewicht hergestellt, einige Bereiche erhöhen die Helligkeit und einige verringern sich.

Infolgedessen können wir nicht mehr über eine Reihenfolge oder einen Gradienten sprechen. Die Punkte werden jedes Mal in einer neuen Reihenfolge umgangen, einschließlich mehrmaliger Wiederholung oder Überspringen. Dies führt zur Bildung von Regionen unterschiedlicher Helligkeit, die miteinander vermischt sind und sich bei jeder neuen Iteration ändern.

Aber das ist noch nicht alles, wenn Sie keine neuen hellen Punkte hinzufügen, werden sie am Ende alle zurückgezahlt. Nachdem der DX seinen Maximalwert erreicht hat, zeichnen wir erneut fünf helle Linien und zählen erneut alle Punkte auf dem Bildschirm:

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

Aber vorher haben wir von Port 60h gelesen, das ist die Tastatur, der Scan-Code der zuletzt gedrückten Taste. Für ESC ist es gleich 1. Wenn die ESC-Taste gedrückt wurde, bewegen wir uns in Richtung Ausgang.

Fertigstellung


Es ist zu beachten, dass Sie während der Aktualisierung des aktuellen Bildschirms, die einige Zeit in Anspruch nimmt, das Programm nicht beenden können, dh die Reaktion auf ESC verzögert sich. Wenn wir während des Wartens und nach dem Drücken einer Taste im Programm bleiben, kann nur der letzte Scan-Code vom Port gelesen werden. Eine weitere Sache ist, dass wir die DOS- und BIOS-Systemfunktionen dafür nicht ersetzen oder verwenden, unabhängig davon, was wir vom Port lesen. Die gedrückte Taste befindet sich in einem Umlaufpuffer und wird wahrscheinlich vom nächsten Programm gelesen, nachdem unser Intro abgeschlossen ist, höchstwahrscheinlich die Datei Manager oder command.com. Dies wird zu seiner Verarbeitung führen, zum Beispiel wird Volkov Commander auf ESC seine Panels verstecken.

Es bleibt zu Textmodus 3 zurückzukehren:

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

Es wird davon ausgegangen, dass wir uns vor dem Start des Programms in diesem Modus befanden, aber im Allgemeinen ist dies möglicherweise nicht der Fall. Hier aktualisieren wir die gesamte AXT, da wir sicher sind, dass AH keine 0 enthält.

Jetzt können Sie beenden:

 retn ;c3 

Dies ist ein Near-Exit-Befehl aus einer Prozedur, die den Wert des dort platzierten Wortes (zwei Bytes) vom Stapel nimmt und in den IP-Befehlszähler lädt. Unter den Anfangsbedingungen haben wir Nullen im Stapel, dies führt uns zur Adresse CS: 0, wo wir den Befehlscode kennen int 20h- Herunterfahren.

Und 7 Bytes für das Urheberrecht:

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

Wir können sagen, dass es noch einen Platz gibt, den ich für eine strengere Initialisierung ausgeben würde, aber da alles in der modernen DOSBox funktioniert, hat der Autor wahrscheinlich alles richtig gemacht.

Gehen wir noch einmal durch:

  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 

Zum Kompilieren müssen Sie Folgendes ausführen: tasm pentagra.asmund tlink /t pentagra.obj.

Ich weiß nicht, ob WAS und WIE es für Sie realisiert wurde, aber es scheint mir, dass ein schöner und ungewöhnlicher Ansatz verwendet wurde, um den Flammeneffekt zu erzeugen. Obwohl ich nichts zu vergleichen habe, haben es vielleicht alle getan, und jetzt können Sie dasselbe tun.

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


All Articles