Ich habe einen Code in Bezug auf die Schleife durchgesehen.
loopinner ....
SUBS R2,R2,#1 ; j--
BGT loopinner ;in this case, loop should continue when j>1
In diesem Fall bin ich mir nicht sicher, wie BGT wieder zum Loopinner verzweigt. Muss ich nicht angeben, was es größer ist als? Da SUBS die Flags aufruft, nehmen wir an, wenn j-- der Wert 1 wird. Woher weiß der Zweig, welcher Wert größer ist als er?
Aus ARM-Bedingungen können Sie leicht entnehmen, dass die Anweisung die Z-, N- und V-Status-Flags untersucht und verzweigt, wenn Z = 0 und N = V. Da es das V-Status-Flag und nicht das C-Status-Flag untersucht, ist dies eindeutig als signierter Test gedacht. ( Das bedeutet für mich, dass dies für die vorzeichenlose Schleifensteuerung nicht nützlich ist - FYI. )
Ich habe dies vor nicht allzu langer Zeit geschrieben , mit genügend Informationen, um zu verstehen, was vor sich geht. Aber ich kann es hier zusammenfassen.
Lassen Sie uns einfachere 4-Bit-Wörter verwenden, bei denen es nur 16 Symbole gibt:
Word Signed Subtrahend
0000 0 1111
0001 1 1110
0010 2 1101
0011 3 1100
0100 4 1011
0101 5 1010
0110 6 1001
0111 7 1000
1000 -8 0111
1001 -7 0110
1010 -6 0101
1011 -5 0100
1100 -4 0011
1101 -3 0010
1110 -2 0001
1111 -1 0000
Oben ist die dritte Spalte, was die ALU tatsächlich verwendet, wenn sie um diesen Wert subtrahiert. Es invertiert einfach jedes Bit vor dem Hinzufügen. (Die ALU subtrahiert niemals etwas. Sie weiß nicht einmal, wie.) Der SUB-Befehl führt also tatsächlich eine Addition durch, indem er beim Addieren die Subtrahend-Form des Werts verwendet. (Wenn Sie die Statusbit-Semantik verstehen möchten, ist es ziemlich wichtig, dass Sie dieses Konzept beherrschen, da es Ihnen hilft, wenn Sie sonst verwirrt wären.)
Stempeln Sie es auf Ihre Stirn –
Eine CPU fügt nur hinzu. ES KANN NICHT SUBTRAHIEREN .
Wenn Sie jemals die Versuchung verspüren, den Primrose-Pfad zu gehen und zu glauben, dass jede Art von Subtraktionsbefehl tatsächlich subtrahiert, und dazu gehören alle Vergleichsbefehle, die Statusbits setzen, aber keine Registerwerte ändern, treten Sie sich einfach wirklich hart und sehr schnell. Es passiert nicht.
Eine CPU fügt nur hinzu. ES KANN NICHT SUBTRAHIEREN .
Alles muss in Additionssemantik gegossen werden. Alles.
Ein SUBS R2, R2, #1 in diesem 4-Bit-Universum, das ich gerade erstellt habe, würde ebenfalls 1110 plus einen Übertrag von 1 hinzufügen. Es gibt nur 16 Möglichkeiten:
Actual Operation Operation Result Operation Comparison
R2 SUBS OP Z N V C ALU Semantics Semantics Z=0 & N=V?
0000 + 1110 + 1 0 1 0 0 1111 0 - 1 = -1 0 > 1 ? False
0001 + 1110 + 1 1 0 0 1 0000 1 - 1 = 0 1 > 1 ? False
0010 + 1110 + 1 0 0 0 1 0001 2 - 1 = 1 2 > 1 ? True
0011 + 1110 + 1 0 0 0 1 0010 3 - 1 = 2 3 > 1 ? True
0100 + 1110 + 1 0 0 0 1 0011 4 - 1 = 3 4 > 1 ? True
0101 + 1110 + 1 0 0 0 1 0100 5 - 1 = 4 5 > 1 ? True
0110 + 1110 + 1 0 0 0 1 0101 6 - 1 = 5 6 > 1 ? True
0111 + 1110 + 1 0 0 0 1 0110 7 - 1 = 6 7 > 1 ? True
1000 + 1110 + 1 0 0 1 0 0111 -8 - 1 = -9 E -8 > 1 ? False
1001 + 1110 + 1 0 1 0 1 1000 -7 - 1 = -8 -7 > 1 ? False
1010 + 1110 + 1 0 1 0 1 1001 -6 - 1 = -7 -6 > 1 ? False
1011 + 1110 + 1 0 1 0 1 1010 -5 - 1 = -6 -5 > 1 ? False
1100 + 1110 + 1 0 1 0 1 1011 -4 - 1 = -5 -4 > 1 ? False
1101 + 1110 + 1 0 1 0 1 1100 -3 - 1 = -4 -3 > 1 ? False
1110 + 1110 + 1 0 1 0 1 1101 -2 - 1 = -3 -2 > 1 ? False
1111 + 1110 + 1 0 1 0 1 1110 -1 - 1 = -2 -1 > 1 ? False
Unter Operationsergebnis habe ich eine Spalte für ALU . Das ALU- Feld geht zurück in R2, nachdem der SUBS-Befehl abgeschlossen ist. (Das V- Status-Flag wird durch ein XOR des Übertrags des zweithöchstwertigen Bits während der Operation und des Übertragsbits selbst generiert.) Beachten Sie auch, dass es einen mit E markierten Einzelfall gibt, bei dem ein vorzeichenbehafteter Überlauf aufgetreten ist .
Sie können jetzt leicht erkennen, warum der BGT-Befehl diese bestimmten Statusbits genau so anwendet, wie er es tut. Allerdings werden hier 4-Bit-Wörter verwendet. Aber die gleiche Idee gilt für viel größere Wortgrößen, ohne dass sich daran etwas ändert.
Wenn Sie sich die Tabelle noch einmal ansehen, können Sie sehen, dass die Bedingung genau dann wahr ist, wenn R2 vor der Subtraktion 2 oder größer war und nicht 1 oder 0 oder kleiner.
Ihre Frage:
Muss ich nicht angeben, was es größer ist als? Da SUBS die Flags aufruft, nehmen wir an, wenn j-- der Wert 1 wird. Woher weiß der Zweig, welcher Wert größer ist als er?
Beginnen wir mit der folgenden Tabelle aus dem ARMv6-M Architecture Reference Manual , Seite A6-99:
Die GT- Bedingung wird als „ Vorzeichenbehaftet größer als “ beschrieben . Der Grund, warum die Dokumentation keine Konstante angibt, ist, dass dieser Test nach einer vorherigen Anweisung erfolgt. Diese vorherige Anweisung definiert den Kontext. Aber ohne diesen Kontext kann man nur ein allgemein signiertes > sagen .
Also, wenn die vorherige Anweisung CMP wäre:
Dann wäre der Kontext der Vergleich zweier vorzeichenbehafteter Werte und die BGT-Anweisung würde dann bedeuten "verzweigen, wenn vorzeichenbehafteter Operand 1 größer als vorzeichenbehafteter Operand 2 ist".
Aber in Ihrem Fall ändert sich mit "SUBS R2, R2, #1" der Kontext und die BGT-Anweisung würde dann bedeuten "Verzweigung, während signiertes R2 immer noch größer als 0 bleibt".
Die bedingte Verzweigungsanweisung selbst weiß eigentlich nicht , was die vorherige Anweisung war. Es weiß auch nicht einmal, um welche Register es sich handelt. Dieses Wissen wird der Person (oder dem Compiler) überlassen, die den Befehlsstrom generiert. Der Verzweigungsbefehl hat also weder einen festen konstanten Wert noch ein Register, mit dem er verglichen werden kann. Es hängt vollständig davon ab, was frühere Befehle mit den Statusbits gemacht haben. Es untersucht nur den resultierenden Status und tut dann, was es tut. Es liegt an Ihnen, den Kontext zu kennen und ihn richtig zu verwenden.
(Apropos, der Kommentar zum Quellcode kann irreführend oder falsch sein.)
Elliot erhebt das Problem (siehe Diskussion unten) ohne Beweise. Er schreibt: „Ich könnte genauso argumentieren, dass eine CPU nur subtrahieren kann.“ Er kann dieses Argument vorbringen, aber es ist nur akademisch. Tatsache ist, dass CPUs nicht subtrahieren. Sie fügen hinzu.
Während dies also teilweise meine Antwort ist, die klare, eindeutige Beweise dafür liefert, dass sogar Elliot die Situation vor Ort verstehen kann, ist es heute auch ein ausgezeichneter Übergang. Daher freue ich mich sehr über die Gelegenheit, die Elliot mir bietet, um die Diskussion zu erweitern.
Meine erste CPU bestand aus 7400 Teilen, die ich 1974 gebaut und erfolgreich fertiggestellt hatte. Zu meiner Überraschung tauchten Zeitungsreporter auf und schrieben einen Artikel darüber. Das ist meine erste Erfahrung. Seitdem habe ich beruflich bei Intel gearbeitet und Chipsatztests für den BX-Chipsatz durchgeführt, und, was für das Unterrichten dieses Fachs relevant ist, habe ich in den 1990er Jahren als außerordentlicher Professor an der Portland State University Kurse über Computerarchitektur mit Klassengrößen von etwa 65-75 Studenten. Dies ist die größte 4-jährige Universität im Bundesstaat Oregon.
Ich empfinde Zweideutigkeit (wobei ich Ambivalenz darüber ausdrücke, wie Berechnungen durchgeführt werden könnten ) darüber, wie Prozessoren ihre Statusbits generieren und wie sie berechnen, was die Schüler nur zu unnötiger Unsicherheit, Verwirrung und Schwierigkeiten führt, deren Korrektur Stunden, Wochen, Monate und manchmal sogar Jahre dauern kann. So wie das Unterrichten von gruppentheoretischer abstrakter Algebra, bevor die Grundlagen vermittelt werden, die meisten Algebrastudenten im ersten Jahr verwirren würde, so würde auch das Unterrichten von akademischen Abstraktionen darüber, wie Computer Dinge tun können , verwirrend sein. Es würden mehr Schüler geschädigt, als geholfen.
Die einfache Wahrheit ist, dass die Anweisungsdecodierung ein ADD ausgibt, selbst wenn der Anweisungstext (es ist schließlich nur Text – es ist nicht das, was tatsächlich vor sich geht) SUB sagt. Die Dekodierung gibt immer noch ein ADD aus. Dabei werden lediglich einige Operandendetails geändert.
Ähnlich, wie es auch im Fall des ARM-Prozessors sein muss, ist die obige Theorie alles, was Sie brauchen, um zu verstehen, wie die Dinge tatsächlich gemacht werden.
Bitte verwirren Sie sich nicht! Computer hinzufügen. Sie subtrahieren nicht. Sie fummeln nur ein bisschen herum, damit es so aussieht, als würden sie subtrahieren.
Ob gut oder schlecht, es ist wichtig zu verstehen, was ein Computer tatsächlich tut, um bestimmte Statusbits zu verstehen; was sie tun und warum sie es tun. Es gibt keinen anderen Weg daran vorbei. Das obige theoretische Modell ist die Art und Weise, wie die Dinge in modernen Prozessoren funktionieren und wie man die Statusbits richtig ausarbeitet und versteht. Es gibt einen guten Grund, warum die Dinge so sind, wie sie sind.
Ich hoffe, dass diese Details oben und die, die ich unten schreiben werde, nützlich sein werden. Jedes Versäumnis, hier zu kommunizieren, ist mein Eigentum, und ich werde gerne daran arbeiten, dieses Dokument zu reparieren, zu ergänzen und zu verbessern, wo immer ich kann.
Im Folgenden verwende ich das ARMv6-M Architecture Reference Manual als Referenz.
Beginnen wir auf Seite A6-187 (Registerkoffer):
Hier können Sie sehen, dass sie dieses Verhalten eindeutig dokumentieren:
AddWithCarry(R[n], NOT(shifted), '1')
Dies ist eine Addition, bei der Operand 2 (der Subtrahend) invertiert und der Übertrag auf '1' gesetzt ist. Genauso, wie ich oben geschrieben habe. (So wird es gemacht.)
Gehen Sie im Fall von Erweiterungen mit mehreren Wörtern zu Seite A6-173 und suchen Sie nach SBCS:
Beachten Sie hier, dass sie wieder Addition verwenden:
AddWithCarry(R[n], NOT(shifted), APSR.C)
Anstatt dass der Carry-In eine hartcodierte „1“ ist, wie es bei der SUBS-Anweisung der Fall ist, verwendet er jetzt den zuletzt gespeicherten Carry-Out-Wert. In diesem Fall wird normalerweise erwartet, dass dies die Ausführung einer früheren SUBS- (oder SBCS-) Anweisung ist.
Für Mehrwortoperationen beginnt man mit SUBS (oder ADDS) und setzt dann den Prozess mit nachfolgenden SBCS (oder ADCS) fort, die die Ausführung früherer Befehle verwenden, um eine Mehrwortoperation zu unterstützen.
In der Mehrwortaddition kann dieses Carry-Out einfach als Carry-Out betrachtet werden , was es auch ist. Eine „1“ zeigt an, dass ein Übertrag aufgetreten ist und behandelt werden muss. Eine '0' zeigt an, dass kein Übertrag aufgetreten ist.
Im Falle einer Mehrwortsubtraktion wird dieser Übertrag besser als umgekehrter Übertrag angesehen . Eine '1' gibt an, dass keine Notwendigkeit bestand, von einem Wort höherer Ordnung zu leihen. Eine „0“ zeigt an, dass ein Kreditbedarf besteht. Da ein SUBS-Befehl dies immer auf „1“ setzt, bedeutet dies, dass es kein Borgen gibt (das Subtraktionsergebnis erfordert ein „Inkrement“, um den invertierten Operanden 2 zu kompensieren.) Aber für den SBCS-Befehl, wenn APSR.C ein „ 0', dann findet kein 'Inkrement' statt und dies ist dasselbe wie ein Ausleihen (da ein Inkrement erforderlich ist, wenn kein Ausleihen vorhanden ist.)
Der ADCS-Befehl, der auf Seite A6-106 zu finden ist, hier aber nicht angezeigt wird, verwendet ebenfalls die Durchführung früherer Befehlsausführungen. Es invertiert nicht den Carry-Out-Wert oder macht anderweitig etwas Seltsames oder Anderes, nur weil es ein ADCS-Befehl ist. Er macht genau dasselbe wie der SBCS-Befehl, außer und nur für ein kleines Detail – der SBCS-Befehl invertiert Operand 2 und ADCS nicht. Das ist es.
Das ist einer der wirklich coolen Aspekte der Funktionsweise dieser Details. Sehr wenig zusätzliche Logik ist erforderlich, um eine Addition in eine Subtraktion und/oder eine Mehrwortaddition in eine Mehrwortsubtraktion umzuwandeln.
Und schließlich, um die Geschichte zu vervollständigen, siehe Seite A2-35:
In Übereinstimmung mit meinen obigen Beschreibungen, wie die Dinge tatsächlich funktionieren.
Es ist wirklich eine Freude zu sehen, wie das alles funktioniert. Es lohnt sich, mit verschiedenen vorzeichenbehafteten und vorzeichenbehafteten Werten herumzuspielen und Status-Flags von Hand zu setzen und zu verwenden. Es vertieft diese Ideen wirklich. Und es sind sehr gute!
Bei all dem Obigen geht es darum, die Statusbits zu verstehen und wie sie generiert werden und warum sie so generiert werden, wie sie sind. Wenn Sie sich darauf konzentrieren, was tatsächlich in einer CPU passiert , fällt der Rest nur als notwendige Konsequenzen heraus und ist dann sehr leicht zu verstehen.
Eine CPU fügt nur hinzu. Es kann nicht subtrahieren.
SUBS
als AddWithCarry(R[n], NOT(imm32), '1')
(ich kann dies erweitern, wenn es hilfreich wäre), was diese Antwort abdeckt.In Ihrem Codebeispiel SUB
hat die Anweisung ein S
Suffix. Dies bedeutet, dass die Unteranweisung die Bedingungsflags setzt, die BGT
ausgewertet werden. Damit die Verzweigung genommen werden kann, Z
muss das Flag 0 sein und das N
Flag muss gleich seinV
Die Armdokumentation besagt eindeutig, dass GT ein vorzeichenbehaftetes größer als ist, es wird verzweigt, wenn Z==0,N==V.
Wenn r2 = 2. Erinnern Sie sich aus der Grundschule daran, dass x - y = x + (-y) und vom ersten Tag (oder kurz danach) in Computertechnik/Wissenschaft/was auch immer Zweierkomplement-Negation ist, invertieren und eins addieren, also x - y = x + (~y) + 1. Das spart Logik und ist die Art und Weise, wie wir subtrahieren
1 add one
0010
+ 1110 invert
==========
vier Bits sind mehr als genug, um zu sehen, was vor sich geht, das Ergebnis ist dasselbe wie 32 Bits.
11101
0010
+ 1110
==========
0001
Also N = 0 und Z = 0 aus dem Ergebnis. Der Übertrag in und der Übertrag des msbit sind gleich, also ist V = 0 (xor des Übertrags in und des übertrags des msbit, kann dies auch durch Inspektion der msbits der Operanden und des Ergebnisses tun).
Wir brauchen Z == 0 und N == V, um die Verzweigung durchzuführen, und das sind sie, also findet die Verzweigung statt.
Sie werden feststellen, dass dies bei positiven Zahlen der Fall ist, da dies ein vorzeichenbehafteter Größer als ist. Wenn Sie einen vorzeichenlosen Größer als verwenden möchten, verwenden Sie bcs / bhs. Die Logik funktioniert genauso, sie optimiert nur die Verwendung von Carry Out (kann dies auch sehen, wenn Sie sehen sich die von Jonk generierte Tabelle an oder erstellen selbst eine)
Wenn r2 = 1
11111
0001
+ 1110
==========
0000
Z = 1, N = 0, V = 0
N == V, aber Z != 0, sodass die Verzweigung nicht stattfindet.
Elliot Alderson
Miep
Peter Bennett
jkaron
Oldtimer
jonk
Miep