Zwei Zahlen mit Assembler addieren und ausgeben
Markus Pitha
- sonstiges
Hallo,
eigentlich ist es nicht das richtige Forum für Assembler, aber ich glaube hier die zufriedenstellendste Anwort zu bekommen.
Seit gestern versuche ich zwei Zahlen mit "nasm" auf Linux zu addieren und dann auszugeben, was ich folgendermaßen versucht habe, wobei mir noch viel unklar ist, und ich dabei einige Fragen habe (Kommentare):
section .data
zahl1 db 1000
zahl2 db 1500
section .text
global _start
_start:
mov eax, [zahl1] ;Zahl1 in Accumulator schieben
add eax, [zahl2] ;Zahl2 dazuaddieren
mov edx, eax ;Bekomme ich so den Wert aus dem Accumulator?
mov ebx,1 ;Ausgabekanal 1? (Warum eigentlich ebx, laut "Hello World"
mov eax,1 ;system call number (sys_exit)
int 0x80 ;Kernel benachrichtigen (muss das eigentlich geschehen)
Das Programm lässt sich zwar problemlos kompilieren und linken, aber es gibt einfach nichts aus.
Ich habe mich dabei an Hello World orientiert:
section .data
msg db "Hello World", 0x0A
len equ $ - msg
section .text
global _start
_start:
mov edx,len ;message length
mov ecx,msg ;message to write
mov ebx,1 ;file descriptor (stdout) <- Wozu ist das gut?, und warum ebx?
mov eax,4 ;system call number (sys_write) <- Wozu dient das?
int 0x80 ;call kernel
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
Wann muss eigentlich immer der Kernel aufgerufen werden?
Markus.
Moin,
Assembler ist bei mir zwar schon ein bisschen her, aber wenn ich dein Additionscode und dein HelloWorld-Code vergleiche,
fällt mir auf, dass du beim HelloWorld, deine Message in ecx schreibst, beim Additionscode schreibst du es aber in edx. Du musst es aber wahrscheinlich genauso in ecx schreiben. Evtl. muss du aus deiner Zahl auch noch ein ASCII-Wert machen, damit du sie ausgeben kannst.
MfG,
Juan
Hallo,
Assembler ist bei mir zwar schon ein bisschen her, aber wenn ich dein Additionscode und dein HelloWorld-Code vergleiche,
fällt mir auf, dass du beim HelloWorld, deine Message in ecx schreibst, beim Additionscode schreibst du es aber in edx. Du musst es aber wahrscheinlich genauso in ecx schreiben. Evtl. muss du aus deiner Zahl auch noch ein ASCII-Wert machen, damit du sie ausgeben kannst.
Hello World funktioniert aber (Bsp aus dem Netz), deswegen verwirrt es mich doch so, dass ich einmal ebx und einmal edx verwenden muss/soll (wie auch immer).
Markus.
Hallo Markus,
ich kenne mich zwar mit den Systemaufrufen unter Linux nicht aus, kann aber aus deinen Codebeispielen gewisse Schlüsse ziehen. Deswegen versuche ich jetzt zuerst mal das HelloWorld-Beispiel vorzuziehen und zu kommentieren.
Offensichtlich ist der int 0x80 ein allgemeiner Betriebssystemaufruf. Die Technik, Systemaufrufe über einen Software-Interrupt zu machen, ist uralt. So werden viele BIOS-Funktionen aufgerufen, und auch das gute alte DOS hat diese Technik verwendet (da war's der int 0x21).
In EAX wird dabei wohl eine Funktionsnummer erwartet, und aus dem Beispiel schließe ich:
Funktion #1: Prozess beenden
Funktion #4: String ausgeben
In weiteren Prozessorregistern werden dann, soweit nötig, zusätzliche Parameter übergeben.
section .data ; Beginn des Datensegments ("Variablenbereich")
msg db "Hello World", 0x0A ; String mit abschließendem LF
len equ $ - msg ; Stringlänge ($ steht für aktuelle Adresse)section .text ; Beginn des Codesegments
global _start ; _start wird extern aufrufbar deklariert_start:
mov edx,len ; Länge des Strings in EDX
mov ecx,msg ; Zeiger auf den String in ECX
mov ebx,1 ; File-Handle (stdout) in EBX
mov eax,4 ; Funktionsnummer (Stringausgabe) in EAX
int 0x80 ; Betriebssystemfunktion aufrufenmov eax,1 ; Funktionsnummer (exit) in EAX
int 0x80 ; Betriebssystemfunktion aufrufen
In Pseudocode geschrieben macht dieser Abschnitt also das hier:
fwrite(stdout, msg, len(msg))
exit()
So, jetzt zu deinem Beispiel.
section .data
zahl1 db 1000
zahl2 db 1500
Das geht schonmal nicht. "db" steht für "define byte", danach darf eine Sequenz von Bytes (z.B. 40,173,3,104) stehen oder auch ein String, der ja auch nur eine Folge von bytes ist. Du schreibst Zahlen hin, die nicht in den Zahlenbereich eines Bytes passen. Was dein Assembler _daraus_ macht, kann ich nicht sagen. Richtig wäre hier eher "dw" für "define word" oder gar "dd" für "define doubleword", je nachdem welchen Zahlenbereich du nutzen willst. Da du den Wert nachher in ein 32bit-Register laden willst, kommt eigentlich nur dd in Frage.
section .text
global _start_start:
mov eax, [zahl1] ;Zahl1 in Accumulator schieben
add eax, [zahl2] ;Zahl2 dazuaddieren
Gut. EAX enthält jetzt zahl1+zahl2.
mov edx, eax ;Bekomme ich so den Wert aus dem Accumulator?
Das kopiert den Inhalt von EAX (das Ergebnis der Addition) nach EDX.
mov ebx,1 ;
Sieht aus wie die Vorbereitung (Parameter laden) für die Stringausgabe, die du dann aber gar nicht ausführst: Der int 0x80 mit EAX=4 kommt nirgends mehr.
mov eax,1 ;
int 0x80 ;
Damit beendest du schließlich dein Programm, ohne dass mit dem Additionsergebnis noch irgendwas geschehen wäre.
Das Programm lässt sich zwar problemlos kompilieren und linken, aber es gibt einfach nichts aus.
Genau. Du scheiterst im Moment noch am Verständnis für den Aufruf von Betriebssystemfunktionen. Nochmal: Mit int 0x80 wird eine Systemfunktion aufgerufen. Welche das ist, entscheidet der Inhalt von EAX. Da muss es in irgendeiner Doku ein Verzeichnis der Systemfunktionen geben, in der die möglichen Funktionen je nach dem Wert von EAX aufgelistet sind, und was die übrigen Register dann für Werte/Parameter enthalten müssen. Das solltest du mal suchen und dir zu Gemüte führen. Denn egal ob DOS, Windows, Linux, oder welches OS auch immer: Ohne Betriebssystemfunktionen wird kein Programm auskommen. Die sind das A und O.
Wann muss eigentlich immer der Kernel aufgerufen werden?
Immer dann, wenn du eine Funktion des OS benutzen willst.
Es ist ein langer, steiniger Weg. Aber wenn man die ersten Steine überwunden hat und die ersten Blasen an den Füßen verheilt sind, macht es entweder mächtig Spaß, oder man hat die Schnauze voll und möchte nie wieder so einen primitiven Kram machen.
Schönen Abend noch,
Martin
Hallo Markus,
bei mir ist es zwar schon Ewigkeiten her, dass ich Assembler programmiert
habe (außerdem für Dos und nicht für Linux), trotzdem werde ich dir ein paar
Fragen beantworten können. Fangen wir mal zum Schluss an.
Wann muss eigentlich immer der Kernel aufgerufen werden?
Was meinst du, wer dein Programm zur Ausführung in den Speicher lädt, und
wem du auch mitteilen solltest, dass das Programm fertig ist und wieder aus
dem Speicher entfernt werden kann?
Und wer soll es auf dem Bildschirm ausgeben, oder programmierst du auch
selbst die Grafikkarte komplett?
mov edx,len ;message length
mov ecx,msg ;message to write
Also die Länge der _Zeichenkette_ in edx, die Adresse der _Zeichenkette_ in
ecx.
mov ebx,1 ;file descriptor (stdout) <- Wozu ist das gut?,
Weil due dem Kernel ja mitteilen musst, wohin er die Zeichen kette ausgeben
soll, hier die Standardausgabe, es gibt sicher auch andere Möglichkeiten.
und warum ebx?
Gehen wir davon aus, dass der Kernel genau dort diese Angabe beim
Interruptaufruf erwartet.
mov eax,4 ;system call number (sys_write) <- Wozu dient das?
Woher soll der Kernel sonst wissen, was du überhaupt von ihm willst, wenn du
ihn anschließend aufrufst.
int 0x80 ;call kernel
section .data
zahl1 db 1000
zahl2 db 1500
Hier fehlt der Speicherplatz, der dann das Ergebnis als Zeichenfolge
aufnehmen kann, damit du sie ausgeben kannst (oder willst du kryptische
Zeichen?)
section .text
global _start_start:
mov eax, [zahl1] ;Zahl1 in Accumulator schieben
add eax, [zahl2] ;Zahl2 dazuaddieren
mov edx, eax ;Bekomme ich so den Wert aus dem Accumulator?
Wozu willst du den Wert aus dem Akkumulator in edx haben?
ebx erwartet die Länge der Ausgabezeichenkette, du hast aber keine.
mov ebx,1 ;Ausgabekanal 1? (Warum eigentlich ebx, laut "Hello World"
s. oben
mov eax,1 ;system call number (sys_exit)
Du hast die Adresse der (nichtvorhanden) Zeichenkette noch nicht in ecx
geaschrieben.
int 0x80 ;Kernel benachrichtigen (muss das eigentlich geschehen)
Du sagst also:
Lieber Kernel gib die (nicht vorhandene) Zeichenkentte aus, von der ich dir
zwar die Länge (Ergebnis der Addition) mitgeteilt habe, ohne zu verraten,
wo diese steht.
Das Programm lässt sich zwar problemlos kompilieren und linken, aber es gibt einfach nichts aus.
Wenn es etwas ausgeben würde, dann irgendetwas, dass zufällig in irgendeinem
Speicherbereich steht, aber mit Sicherheit nicht das, was du erwartest.
Auf Wiederlesen
Detlef
Hallo Detlef,
bei mir ist es zwar schon Ewigkeiten her, dass ich Assembler programmiert
habe (außerdem für Dos und nicht für Linux), ...
ach, du auch? ;-)
Hier fehlt der Speicherplatz, der dann das Ergebnis als Zeichenfolge
aufnehmen kann, damit du sie ausgeben kannst (oder willst du kryptische
Zeichen?)
Hypothetische Annahme: er Linux-Kernel hat eine Systemfunktion zur Ausgabe eines ganzzahligen Werts, die die Umwandlung in einen String selbständig macht. Unwahrscheinlich, aber denkbar.
mov eax,1 ;system call number (sys_exit)
Du hast die Adresse der (nichtvorhanden) Zeichenkette noch nicht in ecx
geaschrieben.int 0x80 ;Kernel benachrichtigen (muss das eigentlich geschehen)
Du sagst also:
Lieber Kernel gib die (nicht vorhandene) Zeichenkentte aus, von der ich dir zwar die Länge (Ergebnis der Addition) mitgeteilt habe, ohne zu verraten, wo diese steht.
Nein, er sagt: Lieber Kernel, vergiss alles, was ich eben ausgerechnet habe und beende das Programm.
Wenn es etwas ausgeben würde, dann irgendetwas, dass zufällig in irgendeinem Speicherbereich steht, aber mit Sicherheit nicht das, was du erwartest.
Nö. Die Ausgabe wird ja gar nicht aufgerufen.
Schönen Abend noch,
Martin
PS: Ich war schneller! :-P
Hallo Martin,
deine Antwort habe ich erwartet.
Hypothetische Annahme: er Linux-Kernel hat eine Systemfunktion zur Ausgabe eines ganzzahligen Werts, die die Umwandlung in einen String selbständig macht. Unwahrscheinlich, aber denkbar.
Hypothetische Annahme: Diese hypothetische Kernelfunktion erwartet
natürlich die Adresse eines Puffers, in den sie die Zeichenfolge ablegen
soll. ;-)
Nein, er sagt: Lieber Kernel, vergiss alles, was ich eben ausgerechnet habe und beende das Programm.
Ich weiß.
PS: Ich war schneller! :-P
Ich sollte mir wirklich angewöhnen, die Seiten vor dem Antworten noch einmal
zu aktualisieren, dann hätte ich mir auch sparen können, die alten
Assemblerkenntnisse aus den hintersten Windungen meines Gehirns
hervorzukramen.
Auf Wiederlesen
Detlef
Hallo,
deine Antwort habe ich erwartet.
oh, bin ich so berechenbar?
Hypothetische Annahme: er Linux-Kernel hat eine Systemfunktion zur Ausgabe eines ganzzahligen Werts, die die Umwandlung in einen String selbständig macht. Unwahrscheinlich, aber denkbar.
Hypothetische Annahme: Diese hypothetische Kernelfunktion erwartet
natürlich die Adresse eines Puffers, in den sie die Zeichenfolge ablegen
soll. ;-)
Grmpfl. Spielverderber. ;-)
Ich sollte mir wirklich angewöhnen, die Seiten vor dem Antworten noch einmal zu aktualisieren, dann hätte ich mir auch sparen können, die alten Assemblerkenntnisse aus den hintersten Windungen meines Gehirns hervorzukramen.
Ach was, erstens ist es ein gutes Gefühl, wenn man die fast vergessenen Geheimnisse doch noch zusammenbekommt, und zweitens ist eine zweite unabhängige Darstellung meistens gut für ein ausgewogenes Gesamtbild.
Übrigens: Das mit dem Zuspätkommen, weil ich nicht nochmal nachgesehen habe, passiert mir auch häufig.
Schönen Abend noch,
Martin
Hallo,
ich habe nun etliche, letztendlich nahezu schon willkürliche Kombinationen, sogar mit einer "Ergebnis"-Variable ausprobiert, aber ich bekomme nicht mal _irgendeine_ Ausgabe. Ich verstehe das nicht. Könnte mir vielleicht jemand für diese Aufgabenstellund das richtige Codebeispiel angeben, damit ich inkl. "Hello World" zwei funktionierende Vorlagen habe. Ich verstehe noch immer nicht, warum ich manchmal eine 1 nach ebx schiebe, danach wieder eine 4 nach eax.
Markus.
Hallo Markus,
Ich verstehe noch immer nicht, warum ich manchmal eine 1 nach ebx schiebe, danach wieder eine 4 nach eax.
Die erste Ressource auf der von Christian Kruse verlinkten Tutorialübersicht erklärt Dir eigentlich die Linux-Konvention für Systemcalls recht gut, siehe http://asm.sourceforge.net/intro/hello.html#AEN55, insbesondere Abschnitt 2.3.
Freundliche Grüße
Vinzenz
你好 Markus,
ich habe das Hello-World-Beispiel mal leicht modifiziert, vielleicht wird
es dadurch klarer:
section .text
global _start ;must be declared for linker (ld)
msg db 'Hello, world!',0xa ;our dear string
len equ $ - msg ;length of our dear string
%define sys_write 4
%define sys_exit 1
%define STDOUT 1
_start: ;tell linker entry point
mov edx,len ;message length
mov ecx,msg ;message to write
mov ebx,1 ;file descriptor (stdout)
mov eax,sys_write ;system call number (sys_write)
int 0x80 ;call kernel to execute system call
mov eax,sys_exit ;system call number (sys_exit)
int 0x80 ;call kernel to execute system call
再见,
克里斯蒂安
Hallo,
Danke, aber mein Verständnisproblem ist hier begründet:
mov edx,len ;message length
mov ecx,msg ;message to write
mov ebx,1 ;file descriptor (stdout)
mov eax,sys_write ;system call number (sys_write)
int 0x80 ;call kernel to execute system call
Ich schiebe die Länge nach edx, die Nachricht aber nach ecx, wieso? Warum zwei verschiedene Register, und warum gerade edx und ecx. Warum nicht beispielsweise ebx und ecx?
Mit den nächsten Zeilen ist meine Verwirrung komplett: Ich schiebe "stdout" nach ebx. Nun stehen 3 verschiedene Dinge in 3 verschiedenen Registern, aber sys_write wird plötzlich wieder nach eax geschoben, also steht nun in allen Registern irgendetwas, so wie ich das sehe. Woher weiß überhaupt "stdout", dass es den Inhalt von ecx ausgeben muss? Was wäre, wenn ich etwas nach edx schreibe, und diesen Wert ausgeben will?
Ich verstehe den Zusammenhang zwischen den Registern eigentlich überhaupt nicht und wann ich welches wofür benutzen muss.
Markus.
Hallo Markus,
Danke, aber mein Verständnisproblem ist hier begründet:
mov edx,len ;message length
mov ecx,msg ;message to write
mov ebx,1 ;file descriptor (stdout)
mov eax,sys_write ;system call number (sys_write)
int 0x80 ;call kernel to execute system callIch schiebe die Länge nach edx, die Nachricht aber nach ecx, wieso? Warum zwei verschiedene Register, und warum gerade edx und ecx. Warum nicht beispielsweise ebx und ecx?
wie in http://asm.sourceforge.net/intro/hello.html#AEN55 erklärt, wird bei Systemcalls die Nummer des Systemcalls in Register eax geschrieben, die Parameter (solange es 6 oder weniger sind) in die weiteren Register.
man 2 write (ja es ist eine C-Funktion) informiert Dich, dass der Systemaufruf "write" drei Parameter erwartet:
1. Parameter: int fd, den Filedeskriptor
2. Parameter: const char *buf, Zeiger auf die zu schreibenden Daten
3. Parameter: size_t count, Anzahl der Bytes die zu schreiben sind
Schauen wir uns nun den Assembler-Systemcall an:
eax: Systemcall-Nummer (hier die 4 für syswrite)
ebx: 1. Parameter (hier den Filedeskriptor für STDOUT, die Standardausgabe)
ecx: 2. Parameter (die zu schreibenden Daten, Dir ist klar, dass in ein
Register nur ein Hinweis darauf passt, wo die Daten
stehen - und nicht die Daten selbst)
edx: 3. Parameter (Länge der Daten)
Die Systematik geht daraus doch klar hervor.
Mit den nächsten Zeilen ist meine Verwirrung komplett: Ich schiebe "stdout" nach ebx. Nun stehen 3 verschiedene Dinge in 3 verschiedenen Registern, aber sys_write wird plötzlich wieder nach eax geschoben, also steht nun in allen Registern irgendetwas, so wie ich das sehe.
Woher weiß überhaupt "stdout", dass es den Inhalt von ecx ausgeben muss?
STDOUT weiss gar nichts. Der Systemaufruf weiss somit, dass er nach STDOUT schreiben soll.
Was wäre, wenn ich etwas nach edx schreibe, und diesen Wert ausgeben will?
Das würde nichts bringen, da in edx die Länge der Nachricht erwartet wird und nicht die Nachricht selbst.
Ich verstehe den Zusammenhang zwischen den Registern eigentlich überhaupt nicht und wann ich welches wofür benutzen muss.
Nochmals: Die Systemcalls unter Linux erwarten in eax die Nummer des Systemcalls. Nach dem Aufruf steht in eax das Ergebnis des Systemcalls, der Rückgabewert.
In den weiteren Registern können bis zu 6 Parameter stehen. Bei mehr als 6 Parametern wird es etwas komplizierter :-)
Damit Du weißt, welche Parameter wo erwartet werden, benötigst Du die Dokumentation der Systemcalls, nicht mehr und nicht weniger.
Freundliche Grüße
Vinzenz
Hallo,
ok, danke, jetzt ist es mir schon mal teilweise klar. Wahrscheinlich wäre ich auf diese Vorgehensweise nie gekommen. Dennoch gibt es noch immer überhaupt nichts aus. Mein bisheriges Programm sieht so aus:
section .data
zahl1 dw 1000
zahl2 dw 1500
len equ $ - zahl1 ;Hierbei hab ich mich an Hello World orientiert
section .text
global _start
_start:
mov edx, len
mov ecx, [zahl1]
add ecx, [zahl2]
mov eax, 4
mov ebx, 1
int 0x80
mov eax, 1 ;system call number (sys_exit)
int 0x80 ;Kernel benachrichtigen
Was habe ich jetzt wieder falsch gemacht?
Markus.
Hallo Markus,
mov edx, len
mov ecx, [zahl1]
add ecx, [zahl2]
Dein Ergebnis steht jetzt (hoffentlich) in ecx, soweit ich die Doku verstanden habe. Problem: syswrite erwartet in ecx den Verweis auf eine Zeichenkette, keine Zahl.
Was habe ich jetzt wieder falsch gemacht?
Somit solltest Du Dir eine Routine schreiben, die eine Zahl in eine Zeichenkette umwandelt, was Du sehr wahrscheinlich ziffernweise erledigen musst. Darauf hatten Dich Martin und Detlef bereits hingewiesen.
Freundliche Grüße
Vinzenz
Hallo Markus,
ok, danke, jetzt ist es mir schon mal teilweise klar.
gut, das ist doch schon mal was. ;-)
section .data
zahl1 dw 1000
zahl2 dw 1500
len equ $ - zahl1 ;Hierbei hab ich mich an Hello World orientiert
; das ergibt den Wert 4 (den Speicherbedarf von 2 WORDs)
section .text
global _start_start:
mov edx, len
mov ecx, [zahl1]
add ecx, [zahl2]
; jetzt enthält ECX den Wert 2500
mov eax, 4
mov ebx, 1
int 0x80
; und das OS versucht nun, einen String mit 4 Zeichen Länge
; auszugeben, der an der Speicheradresse 2500 beginnt
mov eax, 1 ;system call number (sys_exit)
int 0x80 ;Kernel benachrichtigen
Was habe ich jetzt wieder falsch gemacht?
Du hast den Unterschied zwischen String und Zahl nicht beachtet. Die Systemfunktion #4, die du hier verwendest, gibt einen _String_ aus, dessen Adresse (->Pointer) in ECX übergeben wird.
Wenn du einen Zahlenwert ausgeben möchtest, wird's _richtig_ anspruchsvoll. Dann müsstest du den Zahlenwert nämlich "von Hand" in einen String umwandeln. Für einen Einsteiger ist das eine verdammt harte Nuss.
So long,
Martin
Hallo,
sehr gut, alles klar. Jetzt bin ich zufrieden. Ich weiß zwar noch nicht, wie ich in Assembler eine Zahl in einen String umwandeln kann (Mein Gott, ist die Sprache C leicht *g*), aber es ging mir ohnehin hauptsächlich nur um den Lösungsansatz. Dass 2500 am Ende rausgekommen wäre, nehme ich mal an :)
Markus.
你好 Martin,
; und das OS versucht nun, einen String mit 4 Zeichen Länge
; auszugeben, der an der Speicheradresse 2500 beginnt
Korrekter wäre: Das OS versucht nun 4 Byte auszugeben, ob es ein String
oder sonstwas ist, ist uninteressant. Nicht umsonst erwartet write() einen
void-Pointer. ^^
再见,
克里斯蒂安
Hallo 克里斯蒂安,
^^
Ist das jetzt ne neue Macke von Dir? ;-)
Viele Grüße,
Christian
你好 Christian,
^^
Ist das jetzt ne neue Macke von Dir? ;-)
Jo ^^
再见,
克里斯蒂安
Hallo Christian,
^^
Ist das jetzt ne neue Macke von Dir? ;-)
Jo ^^
WoW!
Grüße
Roland ;-)
你好 Orlando,
^^
Ist das jetzt ne neue Macke von Dir? ;-)
Jo ^^
WoW!
*grins* Du sagst es.
再见,
克里斯蒂安
Hallo.
^^
Ist das jetzt ne neue Macke von Dir? ;-)
Nein, zwei.
MfG, at
Hallo Markus,
Danke, aber mein Verständnisproblem ist hier begründet:
[...]
Ich verstehe den Zusammenhang zwischen den Registern eigentlich überhaupt nicht und wann ich welches wofür benutzen muss.
Aha, jetzt kommen wir zum Problem.
Betrachten wir's mal von der Hochsprache wie beispielsweise C. Jemand schreibt eine Funkion, die eine bestimmte Aufgabe erledigt, dafür aber ein paar Parameter braucht. In C ist das Aufrufen einer Funktion und die Übergabe der Parameter nur _eine einzige_ Anweisung.
In Assembler sind dazu mehrere Schritte nötig. Du musst erst die Parameter da hinschieben, wo die Funktion sie erwartet. Wo genau das ist, hat der Programmierer dieser Funktion zu verantworten.
In Assembler ist es gängige Praxis, Parameter in Prozessorregistern zu übergeben. Derjenige, der die Funktion mal geschrieben hat, hat sich entschieden, bestimmte Register zu verwenden. Welche das sind, und welchen Parameter er da erwartet, muss irgendwo dokumentiert sein.
Beim System Call mit int 0x80 (das ist auch eine Art Unterprogrammaufruf) ist die Sache insofern komplizierter, weil für sehr viele Aufgaben dieselbe Funktion aufgerufen wird. Der Inhalt von EAX entscheidet, welche konkrete Aufgabe das sein soll - das haben die Linux-Programmierer offensichtlich so festgelegt.
In Pseudocode formuliert, ist die Funktion, die sich hinter dem int 0x80 verbirgt, wahrscheinlich etwa so realisiert:
switch (EAX)
{ case 0:
// Programmcode für System Call #0
return;
case 1:
// Programmcode für System Call #1 (Programm beenden)
exit();
case 2:
// Programmcode für System Call #2
...
return;
case 3:
// Programmcode für System Call #3
...
return;
case 4:
// Programmcode für System Call #4 (String ausgeben)
write( (FILE*)EBX, (char *)ECX, (int)EDX);
return;
case 5:
// Programmcode für System Call #5
...
return;
Du musst die nötigen Parameter also in den Registern übergeben, die die Linux Kernel-Programmierer sich dafür herausgesucht haben. Und genau diese Übersicht (welche Funktionen kann man mit int 0x80 auslösen, welche Parameter brauchen sie, in welchen Registern werden die erwartet, und welche Information gibt die Funktion eventuell zurück) muss als "Linux Kernel API" irgendwo verfügbar sein. Na gut, vielleicht heißt es auch anders, ändert aber nichts am Prinzip. Diese API-Referenz ist bei der Assembler-Programmierung noch nötiger als in höheren Programmiersprachen, weil sich kaum jemand das alles merken kann.
Schönen Abend noch,
Martin
Ich werde mich anhand eurer Erklärungen nochmal durchackern und hoffentlich ein Ergebnis zusammenbringen :)
Markus