Paradoxon ? "PUSHA" Mnemonic sichert Register auf den Stack, auch den Stackpointer (SP), aber sinnlos ?

  • Stehe momentan vor einem Rätsel, bezogen auf den Intel x86 Assembler-Befehl (Mnemonic) PUSHA (den gibt es ab 80186/V20).

    Der rettet tatsächlich die Register AX bis DX sowie BP,SI und DI. Allerdings auch den Stackpointer SP, was irgendwie aus folgendem Grund keinen Sinn macht:

    Ich habe versucht, den PUSHA Befehl durch einzelne PUSH Register Befehle zu ersetzen.

    Das wollte ich dann auch testen.

    Also alle oben genannten Register auf einen bestimmten (vordefinierten) Wert gesetzt, dann PUSHA ausführen lassen.

    Danach die oben genannten Register auf Null setzen und danach POPA ausführen.

    Von der Theorie her wären dann die ursprünglichen Registerwerte wieder wie vor dem PUSHA.

    Stimmt aber nicht, den Stackpointer (SP) darf man ja gar nicht verändern, weil der ja von PUSHA und POPA selbst verändert wird.

    Warum wird also das Register SP überhaupt vom PUSHA Befehl gerettet, wenn man den gar nicht anfassen darf (weil sonst das POPA ins Nirwana abdriftet) ?

    Was haben sich die Intel Ingenieure dabei gedacht ??

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.

  • Für ALGOL-artige Programmiersprachen (also auch PASCAL) ist es durchaus sinnvoll den Stackpointer gleich mit zu speichern. Für C macht das eher wenig Sinn.

    Die Kette der Stackpointer wird benötigt um auf Variablen übergeordneter Funktionen/Prozeduren zuzugreifen.

    Beim 68000 gab es da schon immer den LINK bzw. UNLINK Befehl.

    (Einen Vorlesungsauschnitt zum Thema "Compilerbau" spare ich mir jetzt aber :)

  • Zitat von Peter z80.eu

    Stimmt aber nicht, den Stackpointer (SP) darf man ja gar nicht verändern, weil der ja von PUSHA und POPA selbst verändert wird.

    Laut i486 Programmer's Reference Manual (Seite 3-4):


    POPA (Pop All Regsiters) pops the data saved on the stack by PUSHA into the general registers, except for the ESP register. The ESP register is restored by the action of reading the stack (popping).

  • PUSHA ist so definiert, damit es symmetrisch zu POPA ist. POPA macht Sinn für Kontextwechsel und so (z.B. durch POPA; RET). PUSHA wird von Kompilern eigentlich nicht verwendet (für Display-Zeiger in Algol und Pascal gibt es den selten verwendeten Befehl ENTER), sondern ist mehr ein Bequemlichkeitsbefehl für Assemblerprogrammierer, die nicht raten wollen, welche Register sie jetzt sichern müssen.

  • Bei Prozessoren > 8086 ist im Übrigen überhaupt nicht (mehr) definiert, was als Wert für SP tatsächlich auf den Stack gepusht wird ([also Wert von SP vor PUSHA, Wert, wenn SP selbst gepusht wird, oder Wert nach PUSHA] beim 8086 steht das noch im Handbuch, ab dem 80286 ist es nicht mehr spezifiziert, weil bei POPA sowieso SP+=16 gesetzt wird).


    Man könnte hier "weniger scharf nachgedacht" vermuten. Wahrscheinlicher war es aber in der Hardware einfach simpler, alles zu pushen und die 2 Bytes Verschwendung zu schlucken, und das (vielleicht) deshalb, weil SP ungeschickterweise mitten in der Registerbank zwischen BX und BP rumliegt statt am Ende oder Anfang, was wahrscheinlich sinnvoller gewesen wäre. Der Grund dafür könnte wiederum sein, dass man gedacht hat, dass man die vom 8080 "übernommenen" Register zusammenhalten wollte und die neuen (BP, SI, DI) hinten anhängt.

    2 Mal editiert, zuletzt von tofro ()

  • Es ist dokumentiert: 80286_and_80287_Programmers_Reference_Manual_1987.pdf:

    The processor pushes the general registers on the stack in the following order: AX, ex, DX, BX, the initial value of SP before AX was pushed, BP, SI, and DI.


    Also eher das Gegenteil ist der Fall. Der Wert des SP wird vorher per Mikrocode in einem temp-Register gespeichert und dann mittendrin auf den Stack gelegt.

  • Bei Prozessoren > 8086 ist im Übrigen überhaupt nicht (mehr) definiert, was als Wert für SP tatsächlich auf den Stack gepusht wird ([also Wert von SP vor PUSHA, Wert, wenn SP selbst gepusht wird, oder Wert nach PUSHA] beim 8086 steht das noch im Handbuch, ab dem 80286 ist es nicht mehr spezifiziert, weil bei POPA sowieso SP+=16 gesetzt wird).


    Man könnte hier "weniger scharf nachgedacht" vermuten. Wahrscheinlicher war es aber in der Hardware einfach simpler, alles zu pushen und die 2 Bytes Verschwendung zu schlucken, und das (vielleicht) deshalb, weil SP ungeschickterweise mitten in der Registerbank zwischen BX und BP rumliegt statt am Ende oder Anfang, was wahrscheinlich sinnvoller gewesen wäre. Der Grund dafür könnte wiederum sein, dass man gedacht hat, dass man die vom 8080 "übernommenen" Register zusammenhalten wollte und die neuen (BP, SI, DI) hinten anhängt.

    Der 8086 unterstützt PUSHA überhaupt nicht. Ich vermute, dass Du das mit PUSH SP verwechselst, wo es in der Tat einen Unterschied zwischen 8086 und Folgeprozessoren gibt. Aber das ist wohldefiniert..

  • Mir ging's eigentlich nicht um Compiler und wo PUSHA/POPA da Sinn macht. Mir ging's darum wie man bei einem Maschinenprogramm PUSHA durch Einzelbefehle ersetzen kann, das war auch soweit gelungen, nur das SP Register macht da eine Ausnahme, in der Tat muss man das SP Register beim Ersatz durch Einzelbefehle im Prinzip ignorieren. Ich habe das via DEBUG/SYMDEB mal ausprobiert und nicht im Manual nachgelesen. Wenn ich das SP auch auf den Stack lege, darf beim Restore (also den äquivalenten POP xx Befehlen) der SP nicht auf einem anderen Wert stehen, als direkt nach dem PUSHA Befehl... sonst geht das Zurücksetzen schief, da der SP dann ja ganz woanders steht und andere Werte zurückgerettet würden. Probiert's einfach mal aus. Und daher ist das Retten des SP einfach Quatsch, wenn ich den nicht verändern darf (wozu dann retten?).

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.