Incremento de 0 a 100 en lenguaje ensamblador


7

Esto es un poco extraño, pero hoy estaba hurgando con el ensamblador GNU (quiero poder leer al menos la sintaxis), y estaba tratando de obtener este pequeño ejemplo artificial de mío para trabajar A saber, solo quiero ir de 0 a 100, imprimiendo números todo el tiempo. Así que a los pocos minutos me vienen con esto:

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

Todo lo que consigo de esta es de 3 a imprimir una y otra vez. Como dije, solo un pequeño ejemplo artificial, así que no te preocupes demasiado por eso, no es un problema de vida o muerte.

(El formateo es un poco desordenado, pero nada importante).

+2

'xorl% eax,% eax' es totalmente equivalente a' MOVL $ 0,% eax', y tarda de 3 bytes menos. Solo digo. :) 10 mar. 122012-03-10 00:38:24

12

No puede confiar en lo que hace cualquier procedimiento llamado a cualquiera de los registros. O bien inserte los registros en la pila y retírelos después de llamar a printf o tenga los valores de incremento y punto final guardados en la memoria y leídos/escritos en los registros cuando los necesite.

Espero que los siguientes trabajos. Supongo que pushl tiene una popul equivalente y puedes insertar un par de números extra en la pila.

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

btw - pusha y popa presionarán todos los registros y los mostrarán todos. He encontrado que bastante útil en el pasado 18 nov. 082008-11-18 17:08:06

  0

De las notas a continuación. Es importante tener en cuenta que ... "@seanyboy, su solución es exagerada. Todo lo que se necesita es reemplazar eax con algún otro registro como ecx". 13 ago. 092009-08-13 07:40:39

  0

@warren - 'pusha' y' popa' no son compatibles con el modo de 64 bits. 10 mar. 122012-03-10 00:37:57

  0

@ Daniel Kozar - nunca funcionó en el modo de 64 bits, pero esto es bueno saber :) 12 mar. 122012-03-12 13:52:36


6

No estoy muy familiarizado con _printf, pero ¿podría ser que modifique eax? Printf debe devolver el número de caracteres impresos, que en este caso son dos: '0' y '\ n'. Creo que devuelve esto en eax, y cuando lo incrementas, obtienes 3, que es lo que procedes a imprimir. Es mejor que uses un registro diferente para el contador.


1

Nathan está en el camino correcto. No puede asumir que los valores de registro no se modificarán después de llamar a una subrutina. De hecho, es mejor suponer que serán modificados, de lo contrario la subrutina no podría hacer su trabajo (al menos para arquitecturas de bajo conteo de registros como x86). Si desea conservar un valor, debe almacenarlo en la memoria (por ejemplo, empújelo en la pila y realice un seguimiento de su ubicación).

Tendrá que hacer lo mismo para cualquier otra variable que tenga. El uso de registros para almacenar variables locales está más o menos reservado a arquitecturas con suficientes registros para soportarlo (por ejemplo, EPIC, amd64, etc.)


3

Las funciones bien escritas generalmente empujarán todos los registros a la pila y luego los mostrarán cuando aparezcan ' re hecho para que permanezcan sin cambios durante la función. La excepción sería eax que contiene el valor de retorno. Las funciones de biblioteca como printf probablemente se escriban de esta manera, por lo que no haría lo que sugiere Wedge:

Deberá hacer lo mismo con cualquier otra variable que tenga. El uso de registros para almacenar las variables locales es más o menos reservado a las arquitecturas con suficientes registros para apoyarlo (por ejemplo, EPIC, amd64, etc.)

De hecho, por lo que sé, los compiladores suelen recopilar las funciones de esa manera de tratar con exactitud con este problema

@seanyboy, su solución es exagerada. Todo lo que se necesita es reemplazar eax con algún otro registro como ecx.


-1

Puede reescribirlo para que utilice registros que no se supone que cambien, por ejemplo %ebp. Solo asegúrate de empujarlos en la pila al principio, y retíralos al final de tu rutina.

# 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 

5

Puede usar de forma segura los registros que son "guardados en línea" sin tener que guardarlos usted mismo. En x86 estos son edi, esi y ebx; otras arquitecturas tienen más.

Estos están documentados en las referencias ABI: http://math-atlas.sourceforge.net/devel/assembly/