www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Assembler und Stack


Autor: Patrick Lehmann (muffi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab ein Problem, bei dieser aAufgabe:

------------------------------------------------------------------------ 
-----

Es soll ein Assemblerprogramm entwickelt werden, mit dem sich die Werte 
von zwei
Parametern vertauschen lassen. Die Werte sind in der Hochsprache „C“ als 
Integer
definiert und werden dem Assemblerprogramm als Adressen übergeben. Beim 
Aufruf
des Assemblerprogramms ist der Stack wie folgt vorbereitet:

-------------------
|  Zeiger auf b   |
|  Zeiger auf a   |
|     return      |
-------------------

Jeder Eintrag auf dem Stack umfasst 4 Byte. „Zeiger auf int A“ und 
„Zeiger auf int B“
sind die beiden Adressen, deren Werte vertauscht werden sollen. Notieren 
Sie die
Assemblerbefehle, sodass die gewünschte Funktion erfüllt wird!

------------------------------------------------------------------------ 
-----

Kann mir vielleicht jemand sagen, wie ich die Zeiger vom Stack bekomme? 
Muss ich da was beachten wegen dem return? Komm da echt ned klar. Bitt 
:)

LG Patrick

Autor: Sumynona (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ganz quick & dirty wäre, den Stackpointer zu nehmen und damit die 
Adressen beider Parameter zu errechnen. Nun noch vier 
Vertauschoperationen direkt auf dem stackspeicher und fertig bist du...

Autor: Sumynona (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ah, wer lesen kann ist klar im Vorteil... mein vorschlag von eben 
funktioniert natürlich nicht, damit vertauschst du nur die pointer

Autor: Patrick Lehmann (muffi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für den Versuch, freu mich schon, dass sich jemand meldet :)


Könnte ich so an die Pointer rankommen?
%define pointer_a [esp+4]
%define pointer_b [esp+8]

weil dann müsste doch nen einfaches

XCHG pointer_a,pointer_b

gehen oder?

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich würde die Zeiger mit pop vom Stack holen. Der Rest ist dann nur 
herumkopieren:
- Ziel Zeiger 1 in Temp kopieren.
- Ziel Zeiger 2 in Ziel Zeiger 2 kopieren.
- Temp in Ziel Zeiger 2 kopieren.
Danach noch return vom Stack holen und dorthin springen (ich tippe mal 
return ist die Rücksprungadresse an der es weitergehen soll)

Autor: bwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Such mal nach Befehlen die so ähnlich wie PUSH und POP heißen!

Tschau bwolf!

Autor: Patrick Lehmann (muffi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@bwolf

ja schon klar, mit POP kann ich die Pointer runterholen vom Stack, aber 
weiß ned so recht was ich mit dem return soll, solange der aufm Stack 
liegt, kann ich ja ohne Probleme wieder mit "Ret" aus dem Unterprogramm 
raus.

aber wäre es auch möglich, einfach nur alles vom Stack zu holen, also so 
zum Beispiel:

POP return
POP int_b
POP int_a

dann kurz vertauschen mit

XCHG int_b,int_a

dann wieder drauf

PUSH int_a
PUSH int_b
PUSH return

ret

wäre ja alles nen bisschen zu einfach, ich weiß da echt nicht weiter...

Autor: Skua (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
POP PUSH ist schon okay.
Aber das werden wohl eher dwords sein.
Achte auf die Adressierungsarten ist (war früher) nicht alles erlaubt.

Autor: BlubBlub (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du musst zwei indirekte Adressierungen pro Pointer vornehmen (insgesmt 
sechs.) Dazu musst du über die Register arbeiten. Ich glaub' da hast du 
mächtig nicht aufgepasst.

Autor: bwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es kommt natürlich immer auf den Speziellen Prozessor und 
Assembler/Compiler an wie im Detail mit Stacks 
(Hardware-/Softwarestacks) gearbeitet wird.

Prinzipiell ist es aber so, daß bei einem Aufruf eines Unterprogramms 
eine Rücksprungadresse auf den Stack gepackt wird. Ich vermute mal, daß 
das Return in Deinem Stack das verdeutlichen soll. Das Return ist also 
vermutlich nur eine Rücksprungadresse, an die Stelle von der das 
Unterprogramm aufgerufen wurde. Diese Rücksprungadresse brauchst du 
nicht manuell vom Stack holen. Wird das Return des Unterprogramms 
erreicht, wird bei der Ausführung des Befehls Return auf den Stack 
zugegriffen und die Rücksprungadresse geholt und in den Befehlszähler 
(Program counter) übernommen.

Tschau bwolf!

Autor: Patrick Lehmann (muffi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
so hab nochmal überlegt, da mir ja in der AUfgabe gesagt wird, dass 
jeder Eintrag auf dem Stack 4 Byte groß ist, geht dann auch des?

MOV AX,[EBP+4] <-- für A

MOV BX,[EBP+8] <-- für B

dann hätte ich die Daten ja immernoch so auf dem Stack bzw kann ich die 
dann nicht auch direkt mit

XCHG [EBP+4],[EBP+8]

tauschen und mit "ret" wieder raus?

Autor: bwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mir fällt gerade noch etwas auf. Ist es so wie ich vermutet habe, 
interpretierst Du die Reihenfolge der POP's falsch. Denke daran das der 
Stack ein LIFO ist (Last In First Out).

Es müßte folgende Reihenfolge beim Schreiben in den Stack gelten:
1) PUSH Rücksprungadresse
2) PUSH Pointer auf A
3) PUSH Pointer auf B

Dann liest man in der folgenden Reihenfolge die
1) POP Pointer auf B
2) POP Pointer auf A
3) Rücksprungadresse bei erreichen von "Return"

Tschau bwolf!

Autor: bwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich würde die Pointer mit POP's vom Stack holen. Anderenfalls ist es so 
wie Du es denkst,  nämlich das die Pointer auf dem Stack verbleiben. Das 
Problem dabei ist aber, daß dann der Rücksprung mit Return auf eine 
falsche Adresse erfolgt, da dann der Pointer B als Rücksprungadresse 
interpretiert wird.

Tschau bwolf!

Autor: bwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Halt! Ich denke ich habe zu viel in die Aufgabe reininterpretiert. Die 
Aufgabe wird darauf abzielen, zu test ob Du das Prinzip der Arbeitsweise 
des Stacks verstanden hast (Stichwort LIFO). Das heißt, die Aufgabe ist 
mit POP und PUSH Befehlen zu lösen. Aber Du mußt auf die Reihenfolge der 
Befehle achten.

Tschau bwolf!

Autor: Patrick Lehmann (muffi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
POP return
POP Pointer auf A
POP Pointer auf B

Pointer tauschen und wieder hoch damit

PUSH Pointer auf B
PUSH Pointer auf A
PUSH Rücksprungadresse

so wäre es dann gelöst oder? Weil dann hätte ich ja wieder alles so 
trauf wies sein soll und kann mit dem return wieder raus aus dem 
unterprogramm?

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wenn du die beiden Pointer tauscht dann passiert aber zum schluss 
nichts. Der Sinn ist ja die Beiden Werte auf die die Pointer Zeigen zu 
tauschen.

Autor: Patrick Lehmann (muffi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok Peter,

POP return
POP Pointer auf A
POP Pointer auf B

mov ax,[Pointer auf A]
mov bx,[Pointer auf A]

dann hab ich ja die Werte, durch

mov [Pointer auf A],bx
mov [Pointer auf B],ax

tausche ich die.

PUSH Pointer auf B
PUSH Pointer auf A
PUSH Rücksprungadresse

Ist des dann so richtig?

Autor: Thomas Pircher (tpircher) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Patrick Lehmann wrote:
> MOV AX,[EBP+4] <-- für A
> MOV BX,[EBP+8] <-- für B

Das scheint mir der beste Ansatz zu sein. Nun hast du in AX, BX die 
pointer zu den Integer Werten.

Du must nun den Inhalt der beiden Pointer vertauschen. Ob ein
XCHG [EBP+4],[EBP+8]
funktioniert, weiss ich nicht; ich kenne den x86 Befehlssatz nicht 
genuegend.

Mein Ansatz waere (im Pseudo-Assembler code):

mov ax, [sp + 4]
mov bx, [sp + 8]
mov cx, [ax]
mov dx, [bx]
mov [ax], dx
mov [bx], cx
ret

Ob ax..dx "frei" verfuegbar sind, haengt von den "calling convention" 
deinesr Plattform und deines Compilers ab (Stichwort Caller/Callee saved 
Registers). Wenn du die verwendeten Register speichern musst, dann musst 
du am Beginn der Funktion noch ein paar push und vor dem ret die 
dazugehoerigen pop anbringen (Bitte dann auch die Offsets zum Stack 
pointer neu berechnen!)

HTH
Thomas

EDIT:
Die Offsets zum SP koennten +8 und +12 sein, bitte nachpruefen!

Autor: bwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Knackpunkt der Aufgabe ist sicher das du die Parameter vertauschen 
sollst, aber nicht die eizelnen Bytes aus dem diese Parameter bestehen. 
Deshalb ist die Reihenfolge der POP's und PUSH's wichtig. Das "Return" 
würde ich in Ruhe lassen, den auf C-Ebene ist dieses "Return" kein 
Parameter.

Dein ursprüngliches Bild interpretiere ich so, daß der Stackpointer 
aktuell auf "Pointer auf B" zeigt. Das heißt dieser Pointer ist der 
letzte Wert der auf den Stack gelandet ist.

Lesen:
1) POP('s) um B zu holen (Zwischenspeichern!)
2) POP('s) um A zu holen (Zwischenspeichern!)

Schreiben:
Die Zwischengespeicherten Pointer mit Push's vertauscht auf den Stack 
legen.

Tschau bwolf!

Autor: Patrick Lehmann (muffi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also wenn ich die Aufgabe nochmal durchlese, glaube ich, dass Thomas es 
gelöst hat. Denn im Stack liegen ja Adressen, deren Werte ich tauschen 
soll. mehr ist ja auch nicht verlengt. Daher danke an alle und besonders 
Thomas :)

Autor: bwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also die Aufgabe ist auch eine Interpretationssache. OK Du hast das 
folgende Programm.

fkt_A(int *X, int *Y)
{
}

main()
{
int A;
int B;
...
fkt_A(&A,&B);
...
}

In dem Moment wenn die Verarbeitung mit der Funktion fkt_A() beginnt, 
werden per POP-Befehl die Parameter vom Stack geholt. Am Anfang der 
fkt_A() hast du also 2 Pointer X und Y die auf bestimmte Adressen 
zeigen. Um die Parameter in der fkt_A() zu vertauschen kannst Du die 
Inhalte der Adressen in X und Y tauschen oder die Pointeradressen X und 
Y selber vertauschen.

Der elegantere Weg ist das Tauschen der Pointer. Hier tritt das zwar 
nicht so zu Tage, da A und B nur einfache Interger sind. Bei größeren 
Datenmengen kopiert man aber beim Tauschen der Pointer weniger Bytes als 
beim Tauschen der Dateninhalte.

Zum Vertauschen kannst Du den Stack benutzen (LIFO!).

PUSH X
PUSH Y
POP X
POP Y

Tschau bwolf!

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich glaube nicht das das geht, ob die Pointer dann andersrum auf dem 
Stack liegen ist doch egal, der STack hat noch dem Return eh keine 
Bedeutung mehr.

fkt_A(int *X, int *Y)
{
}

main()
{
int A;
int B;
...
fkt_A(&A,&B);
...
}

man soll ja erreichen das sich die Werte in A und B tauschen, das geht 
leider nicht mit pointer tausch, weil A und B keine Pointer sind.

Autor: Helmut Lenzen (helmi1)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Werte auf dem Stack werden mit dem Base Ptr addressiert
Zuesrt wird der Frameptr gesichert und dann auf die Spitze des Stacks 
gesetzt. Danach kann du damit die einzelnen Werte auf dem Stack 
addressieren



push ebp           ;Frame Ptr sichern
mov  ebp,esp       ;Frame Ptr setzen

mov   edi,[ebp+8]  ;Zeiger auf A laden    die +8 und + 12 kommen daher 
weil
mov   esi,[ebp+12] ;Zeiger auf B laden    sich noch ebp und Ret auf dem
                                          Stack befinden
mov   eax,[edi]    ;wert von A Laden
mov   ebx,[esi]    ;wert von B laden
mov   [edi],ebx    ;B in stelle von A speichern
mov   [esi],eax    ;A in stelle von B speichern

pop  ebp           ;Frame Ptr wieder herstellen
ret                ;und Tschuess

Gruss Helmi

Autor: bwolf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Peter

In meinem letzen Beispiel schreibe ich die Pointer nocheinmal zurück auf 
den Stack (nachdem diese bereits vom Stack geholt wurden sind). Danach 
werden sie in umgekehrter Reihenfolge wieder vom Stack geholt. Damit 
sind dann die Pointer X und Y vertauscht.

Muß mich dann ausklinken! Tschau bwolf!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
bwolf wrote:
> @ Peter
>
> In meinem letzen Beispiel schreibe ich die Pointer nocheinmal zurück auf
> den Stack (nachdem diese bereits vom Stack geholt wurden sind).

On du das überhaupt tun musst, hängt vom C-Compiler ab, der den Aufruf 
macht. Das hast du nämlich die ganze Zeit übersehem, dass in der 
Aufgabenstellung davon die Rede ist, dass der Aufruf von einem C 
Programm aus erfolgt.

Autor: BlubBlub (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Helmut Lenzen
Im 32 Bit Modell sind alle 8 Basis-Register indirekt mit Displacement 
verwendbar. Die Adressierung über ESP ist also zulässig.

Autor: Gast Posti (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

Hab jetzt nicht Alles gelesen, ahbe sowas Ähnliches damals auf 'nem 
286er gemacht.
Damals (und ich denke heute auch noch) waren die optional übergebenen 
Werte VOR dem RETURN auf dem Stack ...
ungefähr so:
'Werte vom Stack im Programm sichern - natürlich noch in Register o.Ä. kopieren
POP Off_Return
POP Seg_Return
POP Off_Addy_a
POP Seg_Addy_a
POP Off_Addy_b
POP Seg_Addy_b

'DATA- und EXTRA-Segment sichern
PUSH DS 'aktuelles DS sichern
PUSH ES 'aktuelles ES sichern

'DATA-Segment auf Segment von 'a' setzen
PUSH Seg_Addy_a
POP DS

'und Wert auslesen (bei 0-255 reicht AL)
MOV AX,[Off_Addy_a]

'aktuelles DATA-Segment sichern
PUSH DS

'DATA- und EXTRA-Segment auf Segment von 'b' setzen
PUSH Seg_Addy_b
PUSH Seg_Addy_b
POP ES
POP DS

'Wert von 'b' auslesen
MOV BX,[Off_Addy_b]

'Wert von 'a' in Speicher von 'b' schreiben
MOV [Off_Addy_b],AX

'EXTRA-Segment auf Seg_Addy_a setzen
POP ES

'und Wert von 'b' in den SPeicher schreiben, wo 'a' steht
MOV [Off_Addy_a],BX

'gesicherte DS und ES Segmant zurück holen
POP ES
POP DS

'da wir die Adressen der Variablen nicht mehr benötigen
'reicht es, die Rücksprung-Addy auf den Stack zu legen
'bzw. dürften die Adressen der Variablen bei weiteren Maschinenroutienen stören / zum Absturz führen.
PUSH Seg_Return
PUSH Off_Return

'und zurück, zum aufrufendem Programm
RETF

Das RETF müsste, meiner Erinnerung nach, ein RETURN für einen FAR-CALL 
(z.B CALL 1234:0100) sein.
Ein normaler CALL war glaube nur segmentintern, weshalb nur der Offset 
im Stack gesichert wird (1 Word), welches durch RETURN wieder 
angesprungen wird.

Sollte das Alles hier nicht mehr 'up to date' sein ... zumindest meiner 
Erinnerung nach lief das mal so ab - vor 25 Jahren oder so.

MfG

Autor: Gast Posti (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ups, Nachtrag:

Das PUSH und POP mit DS und ES hat die Bewandnis, daß, laut meiner 
Erinnerung, aus dem DS-Segment gelesen wird, aber in das ES-Segment 
geschrieben wird.

Nun aber

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.