Incremento da 0 a 100 in linguaggio assembly
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).
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