Frage

Das ist etwas seltsam, aber ich habe heute mit dem GNU-Assembler herumgestöbert (ich möchte zumindest die Syntax lesen können) und versucht, dieses kleine, konstruierte Beispiel von mir zum Laufen zu bringen.Ich möchte nämlich einfach von 0 auf 100 gehen und dabei die Zahlen ausdrucken.Ein paar Minuten später fiel mir Folgendes ein:

# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    movl    $0, %eax # The starting point/current value.
    movl    $100,   %ebx # The ending point.

_loop:
    # Display the current value.
    pushl   %eax
    pushl   $string
    call     _printf
    addl     $8, %esp

    # Check against the ending value.
    cmpl    %eax, %ebx
    je    _end

    # Increment the current value.
    incl    %eax
    jmp _loop   

_end:

Das Einzige, was ich davon bekomme, sind 3, die immer wieder gedruckt werden.Wie ich schon sagte, es ist nur ein leicht erfundenes Beispiel, also machen Sie sich darüber keine allzu großen Sorgen, es geht nicht um Leben oder Tod.

(Die Formatierung ist etwas durcheinander, aber nichts Großes).

War es hilfreich?

Lösung

Sie können nicht darauf vertrauen, was eine aufgerufene Prozedur mit einem der Register macht.Schieben Sie die Register entweder auf den Stapel und entfernen Sie sie nach dem Aufruf von printf wieder, oder halten Sie die Inkrement- und Endpunktwerte im Speicher und lesen/schreiben Sie sie nach Bedarf in die Register.

Ich hoffe, dass das Folgende funktioniert.Ich gehe davon aus, dass pushl ein gleichwertiges popl hat und Sie ein paar zusätzliche Zahlen auf den Stapel legen können.

# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    movl    $0, %eax # The starting point/current value.
    movl    $100,       %ebx # The ending point.

_loop:
    # Remember your registers.
    pushl   %eax
    pushl   %ebx

    # Display the current value.
    pushl   %eax
    pushl   $string
    call     _printf
    addl     $8, %esp

    # reinstate registers.
    popl   %ebx
    popl   %eax

    # Check against the ending value.
    cmpl    %eax, %ebx
    je    _end

    # Increment the current value.
    incl    %eax
    jmp _loop   

_end:

Andere Tipps

Ich bin mit _printf nicht so vertraut, aber könnte es sein, dass es eax ändert?Printf sollte die Anzahl der gedruckten Zeichen zurückgeben, in diesem Fall zwei:'0' und ' '.Ich denke, es gibt dies in eax zurück, und wenn Sie es erhöhen, erhalten Sie 3, was Sie mit der Ausgabe fortfahren.Möglicherweise ist es besser, ein anderes Register für den Zähler zu verwenden.

Sie können Register, die vom Aufrufer gespeichert wurden, bedenkenlos verwenden, ohne sie selbst speichern zu müssen.Auf x86 sind dies EDI, ESI und EBX;andere Architekturen haben mehr.

Diese sind in den ABI-Referenzen dokumentiert: http://math-atlas.sourceforge.net/devel/assembly/

Gut geschriebene Funktionen verschieben normalerweise alle Register auf den Stapel und laden sie dann, wenn sie fertig sind, so dass sie während der Funktion unverändert bleiben.Die Ausnahme wäre eax, das den Rückgabewert enthält.Bibliotheksfunktionen wie printf sind höchstwahrscheinlich auf diese Weise geschrieben, daher würde ich nicht tun, was Wedge vorschlägt:

Sie müssen dasselbe für jede andere Variable tun, die Sie haben.Die Verwendung von Registern zum Speichern lokaler Variablen ist weitgehend Architekturen vorbehalten, die über genügend Register verfügen, um dies zu unterstützen (z. B.EPIC, amd64 usw.)

Soweit ich weiß, kompilieren Compiler Funktionen normalerweise auf diese Weise, um genau dieses Problem zu lösen.

@seanyboy, deine Lösung ist übertrieben.Sie müssen lediglich eax durch ein anderes Register wie ecx ersetzen.

Nathan ist auf dem richtigen Weg.Sie können nicht davon ausgehen, dass Registerwerte nach dem Aufruf einer Unterroutine unverändert bleiben.Tatsächlich ist es am besten, davon auszugehen, dass sie geändert werden, sonst wäre die Unterroutine nicht in der Lage, ihre Arbeit zu erledigen (zumindest für Architekturen mit geringer Registeranzahl wie x86).Wenn Sie einen Wert beibehalten möchten, sollten Sie ihn im Speicher speichern (z. B.schieben Sie es auf den Stapel und behalten Sie den Überblick über seinen Standort).

Sie müssen dasselbe für jede andere Variable tun, die Sie haben.Die Verwendung von Registern zum Speichern lokaler Variablen ist weitgehend Architekturen vorbehalten, die über genügend Register verfügen, um dies zu unterstützen (z. B.EPIC, amd64 usw.)

Sie könnten es beispielsweise so umschreiben, dass Sie Register verwenden, die sich nicht ändern sollen %ebp.Stellen Sie einfach sicher, dass Sie sie zu Beginn auf den Stapel schieben und am Ende Ihrer Routine wieder herausnehmen.

# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    push    %ecx
    push    %ebp
    movl    $0, %ecx # The starting point/current value.
    movl    $100,       %ebp # The ending point.

_loop:
    # Display the current value.
    pushl   %ecx
    pushl   $string
    call     _printf
    addl     $8, %esp

    # Check against the ending value.
    cmpl    %ecx, %ebp
    je    _end

    # Increment the current value.
    incl    %ecx
    jmp _loop   

_end:
    pop     %ebp
    pop     %ecx
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top