2011-11-07 25 views
6

Esto debe hacerse en ensamblaje puro (es decir, sin bibliotecas ni llamadas a C).Salida de enteros en ensamblado en Linux

Entiendo la esencia del problema: se necesita dividir el número entero por 10, convertir el resto de un dígito en ASCII, generarlo y luego repetir el proceso con el cociente.

Pero por alguna razón, simplemente no funciona. Estoy usando NASM en x86.

Aquí es lo que tengo hasta ahora (no hace nada de salida, pero no lanza ningún error de ensamblador tampoco):

; integer to output is stored in eax 
mov ecx, 10 ; for base 10 

loop: 
div ecx ;EAX contains the quotient, EDX the remainder 

; Do something to EDX to convert it to ASCII, not sure if this is correct 
add edx, '0' 

push eax ;We'll be playing with EAX to output EDX, save EAX to the stack 

mov eax, 4    ; sys_write 
mov ebx, 1    ; to STDOUT 
mov ecx, edx 
mov edx, 1 
int 0x80 

pop eax ;restore EAX 

cmp eax, 0 ;If EAX is 0, our job is done 
jnz loop 

Hay una serie de preguntas similares a ésta (es decir, this y this), pero estoy perdido en la implementación. This question (para DOS) también fue útil, pero todavía estoy confundido.

Me falta algo aquí. ¿Pensamientos?

+0

No veo el '10' constante en ninguna parte de este código, que sería necesario para imprimir los dígitos decimales. (Necesitará repetidamente 'div' y' mod' con '10', almacenará los dígitos, los invertirá y los generará. O bien irá desde el 'gran extremo' primero, y lo dividirá entre' 100,000,000', luego con '10,000,000' , etc., pero eso podría no ser menos complicado que el método de almacenar y revertir). – sarnold

+0

@sarnold Se olvidó de incluir una línea en la parte superior. Editado mi pregunta para agregarlo. –

+0

¡Excelente! Gracias por la actualización. – sarnold

Respuesta

3

Existen al menos otros dos problemas. más allá de la corrupción de ecx que @sarnold mencionó:

  1. div ecx divide el valor de 64 bits edx:eax por ecx, por lo que necesita para asegurarse de que se establece edx a 0 antes de la división.

  2. El segundo argumento de la llamada al sistema write (en ecx) debe ser un puntero a un búfer que contiene el carácter que desea imprimir, no el carácter en sí.

Una manera de resolver el segundo problema es empujar el registro que contiene el carácter que desea imprimir en la pila, y luego asignar el puntero de pila esp a ecx (el puntero de pila en el último elemento empujado y x86 almacena valores little-endian, por lo que el primer byte es 8 bits bajos). p.ej.

push edx   ; save value on stack 
mov eax, 4  ; sys_write 
mov ebx, 1  ; to STDOUT 
mov ecx, esp ; first byte on stack 
mov edx, 1  ; length = one byte 
int 0x80 
pop edx   ; remove what we pushed (or "add esp, 4" would do just as well here; 
       ;      we don't need the actual value again) 

Eso debería ser suficiente para conseguir un poco de salida ...

(Pero en ese momento, es posible que note una "característica" de su algoritmo, y que desee volver a pensar en la forma en que almacena los dígitos que son producidos por la división!)

+0

Esta es una gran explicación, gracias por aclararla. Como lo insinúa, entiendo por qué este algoritmo imprime los enteros en reversa. ¿Alguna idea sobre arreglar eso? –

+1

Reserve algo de espacio para los dígitos (el entero sin signo más grande de 32 bits es 4294967295, por lo que 10 bytes son suficientes), ya sea en una sección de datos (por ejemplo, usando la directiva 'resb') o en la pila (simplemente reste de' esp' , pero asegúrate de restar un múltiplo de 4 para mantener la pila alineada correctamente). A continuación, almacene los dígitos en el búfer yendo hacia adelante, luego recorra el búfer en la impresión inversa de cada carácter; o (¿mejor?) almacena los dígitos en el búfer comenzando desde el último byte del búfer y trabajando hacia atrás, luego puedes escribir todos los dígitos con un solo syscall 'write'. –

2

que configura adecuadamente ecx a 10 en la parte superior de su rutina, pero sobrescribir ecx más tarde:

mov eax, 4    ; sys_write 
mov ebx, 1    ; to STDOUT 
mov ecx, edx ;;; oops -- lost the 10 
mov edx, 1 
int 0x80 

Trate de mover la línea loop uno, por lo ecx se reinicializa a 10 cada vez a través del bucle .

+0

Buena captura, solucionado. Todavía no obtengo ningún resultado, supongo que 'add edx, '0'' no convierte correctamente el dígito simple a decimal. –