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
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...
Ah, wer lesen kann ist klar im Vorteil... mein vorschlag von eben funktioniert natürlich nicht, damit vertauschst du nur die pointer
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?
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)
Such mal nach Befehlen die so ähnlich wie PUSH und POP heißen! Tschau bwolf!
@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...
POP PUSH ist schon okay. Aber das werden wohl eher dwords sein. Achte auf die Adressierungsarten ist (war früher) nicht alles erlaubt.
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.
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!
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?
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!
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!
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!
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?
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.
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?
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!
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!
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 :)
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!
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.
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
@ 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!
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.
@Helmut Lenzen Im 32 Bit Modell sind alle 8 Basis-Register indirekt mit Displacement verwendbar. Die Adressierung über ESP ist also zulässig.
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.