Markus Pitha: Zwei Zahlen mit Assembler addieren und ausgeben

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.

--
http://www.apostrophitis.at
STANDAR_D_  - ist das wirklich so schwer?
     
  1. 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

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

      --
      http://www.apostrophitis.at
      STANDAR_D_  - ist das wirklich so schwer?
           
  2. 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 aufrufen

    mov 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

    --
    Das Gehirn ist schon eine tolle Sache: Es fängt ganz von allein an zu arbeiten, wenn man morgens aufsteht, und hört erst damit auf, wenn man in der Schule ankommt.
      (alte Schülererkenntnis)
    Schon Urlaubspläne für 2006?
  3. 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

    --
    - Wissen ist gut
    - Können ist besser
    - aber das Beste und Interessanteste ist der Weg dahin!
    1. 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

      --
      Das Gehirn ist schon eine tolle Sache: Es fängt ganz von allein an zu arbeiten, wenn man morgens aufsteht, und hört erst damit auf, wenn man in der Schule ankommt.
        (alte Schülererkenntnis)
      1. 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

        --
        - Wissen ist gut
        - Können ist besser
        - aber das Beste und Interessanteste ist der Weg dahin!
        1. 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

          --
          Ein guter Lehrer muss seinen Schülern beibringen können,
          eine Frage so zu stellen, dass auch der Lehrer lernen muss,
          um die Frage beantworten zu können.
            (Hesiod, griech. Philosoph, um 700 v.Chr.)
          Schon Urlaubspläne für 2006?
    2. 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.

      --
      http://www.apostrophitis.at
      STANDAR_D_  - ist das wirklich so schwer?
           
      1. 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

      2. 你好 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  
        
        

        再见,
         克里斯蒂安

        --
        Block-Installation Nr. 5 | Renovation der Mensa-Nord
        Sein oder nicht sein, das ist hier die Frage!
        http://wwwtech.de/
        1. 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.

          --
          http://www.apostrophitis.at
          STANDAR_D_  - ist das wirklich so schwer?
               
          1. 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 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?

            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

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

              --
              http://www.apostrophitis.at
              STANDAR_D_  - ist das wirklich so schwer?
              1. 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

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

                --
                Nicht jeder, der aus dem Rahmen fällt, war vorher im Bilde.
                1. 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.

                  --
                  http://www.apostrophitis.at
                  STANDAR_D_  - ist das wirklich so schwer?
                       
                2. 你好 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. ^^

                  再见,
                   克里斯蒂安

                  --
                  Block-Installation Nr. 5 | Renovation der Mensa-Nord
                  Swen Wacker: Denn wer 'ne Blacklist hat, muss halt daran denken, dass er manches nicht sieht... und vor dem posten die Realitaet einschalten
                  http://wwwtech.de/
                  1. Hallo 克里斯蒂安,

                    ^^

                    Ist das jetzt ne neue Macke von Dir? ;-)

                    Viele Grüße,
                    Christian

                    1. 你好 Christian,

                      ^^

                      Ist das jetzt ne neue Macke von Dir? ;-)

                      Jo ^^

                      再见,
                       克里斯蒂安

                      --
                      Block-Installation Nr. 5 | Renovation der Mensa-Nord
                      Echte Hacker benutzen Aexte. (Thomas Walter in de.org.ccc)
                      http://wwwtech.de/
                      1. Hallo Christian,

                        ^^

                        Ist das jetzt ne neue Macke von Dir? ;-)

                        Jo ^^

                        WoW!

                        Grüße
                         Roland ;-)

                        1. 你好 Orlando,

                          ^^

                          Ist das jetzt ne neue Macke von Dir? ;-)

                          Jo ^^

                          WoW!

                          *grins* Du sagst es.

                          再见,
                           克里斯蒂安

                          --
                          Block-Installation Nr. 5 | Renovation der Mensa-Nord
                          Keine Schneeflocke faellt je auf die falsche Stelle.
                          http://wwwtech.de/
                    2. Hallo.

                      ^^

                      Ist das jetzt ne neue Macke von Dir? ;-)

                      Nein, zwei.
                      MfG, at

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

            --
            Viele Fachleute vertreten die Ansicht, jedes Feature eines Programms, das sich nicht auf Wunsch abstellen lässt, sei ein Bug.
            Außer bei Microsoft. Da ist es umgekehrt.
  4. Ich werde mich anhand eurer Erklärungen nochmal durchackern und hoffentlich ein Ergebnis zusammenbringen :)

    Markus

    --
    http://www.apostrophitis.at
    STANDAR_D_  - ist das wirklich so schwer?