Forum: Mikrocontroller und Digitale Elektronik Assembler und Stack


von Patrick L. (muffi)


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

von Sumynona (Gast)


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...

von Sumynona (Gast)


Lesenswert?

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

von Patrick L. (muffi)


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?

von Gast (Gast)


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)

von bwolf (Gast)


Lesenswert?

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

Tschau bwolf!

von Patrick L. (muffi)


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...

von Skua (Gast)


Lesenswert?

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

von BlubBlub (Gast)


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.

von bwolf (Gast)


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!

von Patrick L. (muffi)


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?

von bwolf (Gast)


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!

von bwolf (Gast)


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!

von bwolf (Gast)


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!

von Patrick L. (muffi)


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?

von Peter (Gast)


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.

von Patrick L. (muffi)


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?

von Thomas P. (tpircher) Benutzerseite


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!

von bwolf (Gast)


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!

von Patrick L. (muffi)


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 :)

von bwolf (Gast)


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!

von Peter (Gast)


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.

von Helmut L. (helmi1)


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

von bwolf (Gast)


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!

von Karl H. (kbuchegg)


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.

von BlubBlub (Gast)


Lesenswert?

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

von Gast Posti (Gast)


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:
1
'Werte vom Stack im Programm sichern - natürlich noch in Register o.Ä. kopieren
2
POP Off_Return
3
POP Seg_Return
4
POP Off_Addy_a
5
POP Seg_Addy_a
6
POP Off_Addy_b
7
POP Seg_Addy_b
8
9
'DATA- und EXTRA-Segment sichern
10
PUSH DS 'aktuelles DS sichern
11
PUSH ES 'aktuelles ES sichern
12
13
'DATA-Segment auf Segment von 'a' setzen
14
PUSH Seg_Addy_a
15
POP DS
16
17
'und Wert auslesen (bei 0-255 reicht AL)
18
MOV AX,[Off_Addy_a]
19
20
'aktuelles DATA-Segment sichern
21
PUSH DS
22
23
'DATA- und EXTRA-Segment auf Segment von 'b' setzen
24
PUSH Seg_Addy_b
25
PUSH Seg_Addy_b
26
POP ES
27
POP DS
28
29
'Wert von 'b' auslesen
30
MOV BX,[Off_Addy_b]
31
32
'Wert von 'a' in Speicher von 'b' schreiben
33
MOV [Off_Addy_b],AX
34
35
'EXTRA-Segment auf Seg_Addy_a setzen
36
POP ES
37
38
'und Wert von 'b' in den SPeicher schreiben, wo 'a' steht
39
MOV [Off_Addy_a],BX
40
41
'gesicherte DS und ES Segmant zurück holen
42
POP ES
43
POP DS
44
45
'da wir die Adressen der Variablen nicht mehr benötigen
46
'reicht es, die Rücksprung-Addy auf den Stack zu legen
47
'bzw. dürften die Adressen der Variablen bei weiteren Maschinenroutienen stören / zum Absturz führen.
48
PUSH Seg_Return
49
PUSH Off_Return
50
51
'und zurück, zum aufrufendem Programm
52
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

von Gast Posti (Gast)


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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.