Assembler + Stack

hasufel

Admiral Special
Mitglied seit
27.11.2002
Beiträge
1.039
Renomée
3
Standort
Bochum
Hi ho!

Ich versuche gerade mich in Assembler einzuarbeiten (IA32, nasm).
Leider scheinen die meisten Tutorials und mein Script für Leute mit C-Kentnissen zu sein. Habe bisher nur ein wenig Java programmiert. Ich scheitere schon an der ersten Aufgabe, da mir nicht klar ist wie ich bedingte Sprünge anwende und was dabei mit dem Stack und passiert:

Das Programm add64.asm soll 2 64bit Zahlen addieren. Der höherwertige Teil des ersten Summanden soll in EAX, der niederwertige in EBX gespeichert werden. Der 2. Summand entsprechend in ECX und EDX.
Das Ergebniss soll in EAX und EBX gespeichert werden.

Die Summanden habe ich als statische Variablen eingetragen, um mir erstmal i/o zu ersparen:

Section .data ; for static variables and buffers
a: dd 0x11111111 ; eax
b: dd 0x22222222 ; ebx
c: dd 0x33333333 ; ecx
d: dd 0x44444444 ;edx

Diese lade ich in die Register:
mov eax, a ; eax -> a
mov ebx, b ; ebx -> b
mov ecx, c ; usw.
mov edx, d

Anschließend addiere ich die niederwertigen Teile der Summanden:
add ebx, edx

Dann müsste ich das OverflowFlag abfragen und falls dieses gesetzt ist eax incrementieren.
Doch wie mache ich das? Ich nehme an das ich einen bedingten Sprung (jo [jump overflow]) machen muss. Aber wohin springe ich? Muss ich ein Unterprogramm schreiben um die Zahl zu erhöhen und ggf falls dabei wieder zum überlauf kommt ein weiteres Unterprogramm eine Fehlermeldung ausgeben lassen?
Und was mache ich dabei mit dem Stack? Kann ich den so lassen wenn das Unterprogramm nur eax um 1 erhöht und sonst nicht auf die Register zugreift?

Fragen über Fragen :]

mfg hasu
 
Da ist ein kapitaler Fehler drinnen, so müsste es aussehen:

Code:
MOV  EAX, [A]
MOV  EBX, [B]
MOV  ECX, [C]
MOV  EDX, [D]

Die eckigen Klammern sind wichtig, sonst wird nur das Offset in das Register geladen - nicht der Inhalt an diesem Offset.

Dann einfach weiter

Code:
ADD  EAX, EBX

Nun überprüfst du das Overflow Flag, mit dem bedingten Sprung JNO (Jump if No Overflow). Tritt ein Überlauf auf, setzt der ADD Befehl das Overflow Flag - JNO guckt, ob das Flag gesetzt ist - wenn nicht, wird zur angegebenen Adresse gesprungen.

Code:
JNO  NoOverflow

INC  ECX

NoOverflow  

ADD ECX, EDX

Die 64bit Zahl liegt jetzt in ECX:EAX

PS Den Stack brauchst du hier nicht angucken. Es sei denn, du willst ein echtes Unterprogramm draus machen, das du per CALL aufrufen kannst - dann greifst du aber auch nur indirekt auf den Stack zu, sprich hast damit keinen direkten Kontakt (nur über CALL und RET).
 
Ahh, danke für die erhellende Antwort!

Glaub ich fang doch noch an Spass daran zu entwickeln ;D

mfg hasu
 
Zum Verständnis was es mit den Labels etc. auf sich hat, kannst du ein paar einfachere Programme als binär assemblieren (Standardausgabeformat von nasm, die anderen Formate enthalten noch diverse Verwaltungsinformationen, die aber eher für einen linker wichtig sind), und dir per ndisasm angucken was da eigentlich rausgekommen ist.

Labels werden einfach zur Adresse aufgelößt, in der du das Label definiert hast. Also statt MOV EAX, [A] steht dann sowas im Programm wie MOV EAX, [0x0]; MOV EBX, [0x4] etc. - genauso bei Sprüngen, also im Endeffekt steht da nur noch JNO 0x12345, wobei 0x12345 die Adresse ist, an der du das Label NoOverflow definiert hast.

In dem Zusammenhang ist auch noch ORG interessant, ladt dir am besten einfach die nasm manual runter, da ist auch eine Referenz zu allen Befehlen bei.
 
Danke für die Tips! Werd ich wohl auch brauchen - in den nächsten Wochen kommt noch einiges auf mich zu: Aufgaben
Für die Ausgabe muss ich die Hexadezimalzahlen in Ascci umwandeln - das heisst wohl ich muss überprüfen ob 4bits einem Wert größer 9 entsprechen oder nicht, und anschließend die Differenz zum jeweiligen Ascii-Zeichen aufaddieren.

Wegen dem disassembler - das kann ich wohl nur, wenn die Programme auf den Intel-Rechnern installiert sind. Hab zuhause einen AMD-Rechner. Ich wusste erst garnicht wie ich die Aufgaben machen sollte, da ich in der uni auch keinen Zugang zu dem Rechnerraum habe. Aber dafür gibts ja ssh ; ) Das ist via DSL ganzschön langsam (bin kein Fan von vi und benutze einen grafischen debugger). Gott sei Dank gibts noch die langsamen SunTerminals - die Räume sind fast immer frei und wenn ich eh auf ner anderen Kiste angemeldet bin ist es ja egal wie langsam die sind.

mfg hasu

Edit: goil - alles da!
Code:
[gspaltho@bonete03 Assembler]$ ndisasm add64.bin 
00000000  66A13400          mov eax,[0x34] 
00000004  668B1E3800        mov ebx,[0x38] 
00000009  668B0E3C00        mov ecx,[0x3c] 
0000000E  668B164000        mov edx,[0x40] 
00000013  6601D3            add ebx,edx 
00000016  7302              jnc 0x1a 
00000018  6641              inc ecx 
0000001A  6601C8            add eax,ecx 
0000001D  CD80              int 0x80 
0000001F  C3                ret 
00000020  90                nop 
00000021  E8DCFF            call 0x0 
00000024  66B801000000      mov eax,0x1 
0000002A  66BB00000000      mov ebx,0x0 
00000030  CD80              int 0x80 
00000032  0000              add [bx+si],al 
00000034  1111              adc [bx+di],dx 
00000036  1111              adc [bx+di],dx 
00000038  2222              and ah,[bp+si] 
0000003A  2222              and ah,[bp+si] 
0000003C  3333              xor si,[bp+di] 
0000003E  3333              xor si,[bp+di] 
00000040  EE                out dx,al 
00000041  EE                out dx,al 
00000042  EE                out dx,al 
00000043  EE                out dx,al

Vielen Dank nochmal!
 
Zuletzt bearbeitet:
Öhm... was du da programmierst ist x86 Assembler, genauer IA32 Assembler - der läuf sowohl auf Intel, als auch auf AMD Plattformen. Sonst wären AMD CPUs keine x86 CPUs ;).

Für die Aufgaben fehlt username und passwort.
 
Hm, dachte das die Syntax prozessorspezifisch ist. Wenn ich die gleiche Menmonik anwenden kann wäre das sehr kewl. Hab schon überlegt ob ich nen alten Celeron wieder ausgrabe um auch nach dem Kurs weiter assemblieren zu können.

Sorry, der Link war falsch.
hier nochmal der richtige (nur falls es dich interessiert).
Die Scripte gibts leider nur mit PW. Ich nehme an, das liegt daran das da einiges aus Lehrbüchern übernommen wurde.
Die Scripte sind für dich warscheinlich eh uninteressant - du weißt das ja alles schon ;)

Den letzten Beitrag hatte ich nochmal editiert....

mfg hasufel

Edit: Wofür stehen die Nummern am Anfang der Zeile? Sind das relativen Adressen des ersten Bits eines Speicherplatzes im für das Programm reservierten Bereich?
Wenn ja, sollten die nicht durch 4 teilbare Startbits haben um am Stück ausgelesen werden zu können? Stichwort "Aligned"

btw: jetzt weiß ich endlich wo du wohnst *lol* Hatte es mir schon fast gedacht. Jetzt weiß ich es dank "/proc/ioports"
 
Zuletzt bearbeitet:
Ist nix spezifisch, mit x86 Assembler kannst du jede x86 CPU programmieren. Spezifisches geht eigentlich garnicht, mal von den diversen Erweiterungen von x86 abgesehen - da gibts aber auch nur ein paar Sachen:

8086 - sind alle Befehle, die der 8086 kennt
8087 - der Befehlssatz der 8086 FPU
286 - ein paar zusätzliche Befehle, die der 286 kennt, 16bit Protected Mode
386 - 32bit Register im Real Mode, ein größerer Batzen neuer Anweisungen und der komplette Protected Mode
387 - ein paar neue FPU Befehle
486 - ein oder 2 neue Befehle im Real- und Protected Mode
586 - wieder ein oder 2 neue Befehle im Real- oder Protected Mode
MMX
CMMX
686 - selbes Spielchen nochmal
SSE1/2/3
3DNow, 3DNow Prof

Seit dem Pentium Pro, dem Athlon und dem Cyrix 6x86MX kennen alle CPUs den 686 Befehlssatz. Und selbst einen 386er kannst du ohne Unterschiede zum 686 programmieren, die paar neuen Befehle braucht man eigentlich nur sehr sehr selten. Insgesammt sind im Protected Mode seit dem 386 vielleicht 10 neue Befehle aufgetaucht, die teilweise sehr komplexe Operationen ausführen.

Es gibt nur eine einzige CPU die man wirklich direkt programmieren kann, und das ist der NexGen 586. Alle anderen bekommen IA32 Assembler, der NexGen versteht sich natürlich auch auf IA32 Asm.
 
Oh man, da hatte ich ein Brett vorm Kopf. Hätte ich auch selber drauf kommen können, zumal ich bei jeder Linux-installation damit konfrontiert wurde - x86 kompatibel....

Das die Befehle sich SO wenig unterscheiden hätte ich aber auch nicht gedacht.

mfg hasu
 
Der 486 (oder wars 586?) bringt sogar nur einen neuen Befehl mit, nämlich CMPXCHG. In der Nasm Manual steht auch drinne, welche Generation den jeweiligen Befehl unterstützt - kann ich dir wirklich ans Herz legen, ist inzwischen auch mein Standard-Nachschlagewerk bei irgendwelchen Asm Fragen.
 
ok, danke nochmal!

Es war der 486. Da hat sich zwar hardwaremäßig ne Menge geändert aber die Instruktionen anscheinend nicht.

Malwieder ne gute Gelegenheit um den Abend bei wikipedia zu verbringen :]

mfg hasu
 
Oder du vertiefst dich in das Standartwerk schlechthin: The Art of Assembly Language ;)

Da sind auch die Schritte vom 8086 bis zum 586 relativ gut erläutert, genauso solche Sachen wie die Branch Prediction, Pipelining, Superskalare Architekturen, Prefetch Queue, und und und.
 
Zurück
Oben Unten