Domanda

È un po' strano, ma oggi stavo curiosando con l'assemblatore GNU (voglio essere in grado almeno di leggere la sintassi) e stavo cercando di far funzionare questo mio piccolo esempio artificioso.Voglio dire, voglio solo andare da 0 a 100, stampando continuamente i numeri.Quindi pochi minuti dopo mi viene in mente questo:

# 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:

Tutto quello che ottengo da questo è 3 stampato più e più volte.Come ho detto, è solo un piccolo esempio artificioso, quindi non preoccuparti troppo, non è un problema di vita o di morte.

(La formattazione è un po' confusa, ma niente di grave).

È stato utile?

Soluzione

Non puoi fidarti di ciò che fa una procedura chiamata a nessuno dei registri.Spingi i registri nello stack e rimuovili dopo aver chiamato printf oppure mantieni i valori di incremento e punto finale in memoria e leggi/scrivi nei registri quando ne hai bisogno.

Spero che quanto segue funzioni.Presumo che pushl abbia un popl equivalente e che tu possa inserire un paio di numeri in più nello stack.

# 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:

Altri suggerimenti

Non ho molta familiarità con _printf, ma potrebbe essere che modifichi eax?Printf dovrebbe restituire il numero di caratteri stampati, che in questo caso è due:'0' e ' '.Penso che lo restituisca in eax e quando lo incrementi ottieni 3, che è ciò che procedi a stampare.Potrebbe essere meglio utilizzare un registro diverso per il contatore.

È possibile utilizzare in sicurezza i registri "salvati dal destinatario della chiamata" senza doverli salvare manualmente.Su x86 questi sono edi, esi ed ebx;altre architetture ne hanno di più.

Questi sono documentati nei riferimenti ABI: http://math-atlas.sourceforge.net/devel/assembly/

Le funzioni ben scritte di solito inseriscono tutti i registri nello stack e poi li pop quando hanno finito in modo che rimangano invariati durante la funzione.L'eccezione sarebbe eax che contiene il valore restituito.Le funzioni di libreria come printf sono molto probabilmente scritte in questo modo, quindi non farei come suggerisce Wedge:

Dovrai fare lo stesso per qualsiasi altra variabile che hai.L'uso dei registri per memorizzare le variabili locali è praticamente riservato alle architetture con abbastanza registri per supportarlo (ad es.EPIC, amd64, ecc.)

In effetti, da quello che so, i compilatori di solito compilano le funzioni in questo modo per affrontare esattamente questo problema.

@seanyboy, la tua soluzione è eccessiva.Tutto ciò che serve è sostituire eax con qualche altro registro come ecx.

Nathan è sulla strada giusta.Non si può dare per scontato che i valori dei registri non verranno modificati dopo aver chiamato una subroutine.In effetti, è meglio presumere che verranno modificati, altrimenti la subroutine non sarebbe in grado di svolgere il suo lavoro (almeno per architetture con basso numero di registri come x86).Se vuoi preservare un valore dovresti memorizzarlo in memoria (es.inserirlo nello stack e tenere traccia della sua posizione).

Dovrai fare lo stesso per qualsiasi altra variabile che hai.L'uso dei registri per memorizzare le variabili locali è praticamente riservato alle architetture con abbastanza registri per supportarlo (ad es.EPIC, amd64, ecc.)

Potresti riscriverlo in modo da utilizzare registri che non dovrebbero cambiare, ad esempio %ebp.Assicurati solo di metterli in pila all'inizio e di toglierli alla fine della routine.

# 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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top