2 524224 Anweisungen
Mein Programm:
_start:
mov bl, _end
stc
iloop: adc [bx], al
inc bx
jnz iloop
jnc _start
a32
loop _start
hlt
_end:
(Technischer Hinweis: Dies ist für nasm geschrieben Die. a32
Syntax nasm ist für die alternative Adresse Größe Präfix - Byte Für masm Sie die ersetzen würde. a32
Mit defb 0x67
.)
Zur Verdeutlichung hier die Ausgabe der Auflistung:
1 _start:
2 0000 B310 mov bl, _end
3 0002 F9 stc
4 0003 1007 iloop: adc [bx], al
5 0005 43 inc bx
6 0006 75F9 jnz iloop
7 0008 73F4 jnc _start
8 000A 67 a32
9 000B E2F1 loop _start
10 000D F4 hlt
11 _end:
Das Programm geht davon aus, dass sich der Prozessor im Real-Modus befindet und dass sich das Programm am unteren Rand eines 64-KB-Speichersegments befindet, das ansonsten auf All-Bit-Null initialisiert wird. Das Design ist einfach: Behandeln Sie den Speicher als eine einzelne riesige Ganzzahl ohne Vorzeichen und erhöhen Sie ihn um alle möglichen Werte, bis er wieder auf Null gesetzt wird. Wiederholen Sie diese 2 32 Mal. Dann halt an.
Die innerste Schleife (Zeilen 4‒6) ist für die Inkrementierung der großen Ganzzahl verantwortlich. Jede Iteration fügt einem einzelnen Byte null oder eins hinzu, je nachdem, ob das vorherige Byte ausgeführt wurde. Beachten Sie, dass auf jedes Byte in der großen Ganzzahl zugegriffen wird, unabhängig davon, ob es sich geändert hat oder nicht. Daher wiederholt diese Schleife immer 2 16 - 14 Mal.
Übrigens, falls Sie sich fragen, dieser Code zeigt den Grund, warum die x86 inc
/ dec
-Anweisungen das Übertragsflag nicht beeinflussen: Nur um diese Art von Mehrbyte-Übertragungsmuster zu vereinfachen. (Dieses Muster trat in den Tagen von 8-Bit-Mikroprozessoren häufiger auf, als der ursprüngliche 8080-Befehlssatz definiert wurde.)
Zeile 7 bewirkt, dass der Inkrementierungsprozess wiederholt wird, bis eine Eins aus dem letzten Byte ausgeführt wird, was anzeigt, dass die große Ganzzahl auf alle Bits Null zurückgesetzt wurde. Dies dauert lange.
Die Zeilen 8 bis 9 markieren die äußerste Schleife und bewirken, dass dieser Vorgang 32 Mal wiederholt wird, bis das ecx
Register auf Null rollt. Dies entspricht effektiv dem Hinzufügen weiterer 32 Bit zur riesigen Ganzzahl.
Es wäre möglich, eine weitere äußere Schleife hinzuzufügen und dies erneut zu tun, indem (sagen wir) das edx
Register verwendet wird, und dann möglicherweise esi
und edi
für noch mehr Wiederholungen. Es lohnt sich jedoch nicht. Die Anweisungen zum Inkrementieren und Schleifen erfordern vier Bytes. Diese vier Bytes werden von der riesigen Ganzzahl entfernt. Wir verlieren also 32 Bit auf dem RAM-Zähler, nur um 32 Bit über ein Register hinzuzufügen: Es wird eine Wäsche. Der einzige Grund ecx
für eine Ausnahme ist, dass es einen speziellen loop
Befehl gibt, der nur in drei Bytes passt. Somit tauscht das Programm 24 Bit gegen 32 Bit, eine winzige, aber immer noch positive Verstärkung von 8 Bit.
Es ist nicht allzu schwer, die Anzahl der Anweisungen, die das Programm ausführt, direkt zu berechnen, bevor es angehalten wird. Es gibt jedoch eine viel einfachere Möglichkeit, diese Anzahl zu schätzen. Das Programm ändert seinen gesamten Speicher abzüglich der 14 Bytes, die das Programm enthalten, und der Register bx
und ecx
. Dies ergibt 2 16 - 14 + 2 + 4 = 65528 Bytes für insgesamt 524224 Bits. Die Verknüpfung beinhaltet die Erkenntnis, dass während des Ausführens jedes einzelne mögliche Muster von 524224 Bits genau einmal erscheint. Für den RAM und das ecx
Register ist dies leicht zu erkennen, da das Programm jeden einzelnen Wert inkrementiert. Zumbx
Dies ist etwas weniger offensichtlich, da es gleichzeitig mit der Aktualisierung des Speicherwerts geändert wird. Man kann jedoch zeigen, dass sich das Programm angesichts der Struktur des Programms in einer Endlosschleife befinden müsste, wenn ein vollständiges Bitmuster tatsächlich zweimal auftreten würde. Da dies nicht der Fall ist, muss jedes Bitmuster letztendlich nur einmal besucht werden. (Der vollständige Beweis wird natürlich dem Leser als Übung überlassen.)
Da jedes mögliche Bitmuster im Verlauf des Programms auftritt, muss das Programm mindestens 2 524224- Befehle ausführen , was ungefähr 1,4 × 10 157807 entspricht . (Die akute Zahl ist aufgrund der Sprunganweisungen sehr geringfügig höher, aber der Unterschied bei dieser Größe ist vernachlässigbar.)
Offensichtlich könnte dies durch die Verwendung von mehr als 64 KB RAM erheblich verbessert werden. Ich halte die nächste Version des Codes zurück, bis ich genau herausgefunden habe, auf wie viel RAM zugegriffen werden kann.