Arqui Sim, error en el codigo de factRec?

Arqui Sim, error en el codigo de factRec?

de Gianluca Giordano Pignatta -
Número de respuestas: 1

Hola, ¿Todo bien?
Este es el segmento de código que esta subido al Eva a modo de ejemplo

; Procedimiento RECURSIVO para calcular el factorial de un numero entre 0 y 9 recibido en AX.

; El resultado se devuelve en DX::AX.

factRec proc

cmp ax, 0

jbe paso_base_factRec

push bx

push ax

dec ax

call factRec

pop bx ; ----> Aca no tendria que ser pop ax?, dado que antes del call lo ultimo en pushearse es ax y no bx

mul bx

pop bx; ----> ademas aca se vuelve a hacer pop de bx, lo cual seria correcto si no hubiera perdido el valor de ax al sobrescribirlo

jmp fin_factRec

paso_base_factRec:

mov ax, 1


fin_factRec:

ret

factRec endp

Gracias de antemano

En respuesta a Gianluca Giordano Pignatta

Re: Arqui Sim, error en el codigo de factRec?

de Gustavo Brown -

Gianluca,

  Lo primero que hay que tener claro es que los PUSH y POP son independientes, o sea uno no está obligado a hacer POP del mismo registro que hizo PUSH. 

La instrucción PUSH reg16 lo que hace es "pushear" en el stack el contenido del registro reg16. Luego que se hace con eso es otro tema. De la misma manera, la instrucción POP reg16 lo que hace es "popear" una palabra del stack y reemplazar el contenido del registro reg16 con esa palabra.

Lo que hacemos a veces para visualizar el stack de dibujar las palabras que allí hay y poner un registro es solo a efectos de visualización, pero lo que hay en el stack son palabras de 16 bits. Luego uno hace PUSH o POP de los registros cuyo contenido quiera respaldar o recuperar pero no tienen por qué ser los mismos.

Luego para entender qué hace un programa escrito en assembler tenés varias opciones:

 - Intentar "decompilar" el programa a otro lenguaje de más alto nivel, como C

 - Ejecutar el programa con distintas entradas y ver las salidas que produce. Esto se puede hacer ejecutando a mano o con el simulador o con una CPU real.

 - Una mezcla de ambas, y por ejemplo ejecutar paso a paso el programa para ir viendo exactamente que va haciendo y cómo modifica los registros y el stack (y eventualmente otras partes de memoria)


Tomemos el ejemplo y tratemos de decompilarlo a C. Ojo que puede no ser una decompilación exacta 1 a 1 porque tal vez el que hizo el programa en assembler no arrancó desde un programa en C, o se tomó ciertas libertades al pasarlo a assembler, como en este caso . Hay que tener en cuenta cómo se llama la rutina y cómo retorna los resultados. En este caso el n viene en AX y el resultado en DX:AX


int factRec(short n)        ; factRec proc
{
  int resultado;
  if(n > 0)                 ; cmp ax, 0
  {                         ; jbe paso_base_factRec
    // este push es a efecto de no perder el contenido de BX 
    	                    ; push bx
short res1 = (short)factRec(n-1) ; push ax
; dec ax
; call factRec // este pop coloca en BX el valor n (que fue pusheado antes de la llamada recursiva)
; pop bx resultado = n * res1 ; mul bx // este pop recupera el valor de BX original
; pop bx;  } ; jmp fin_factRec
else { ; paso_base_factRec:
resultado = 1 ; mov ax, 1 } return resultado ; fin_factRec:
; ret
} ; factRec endp

El programa en alto nivel parece correcto (calcula el factorial), pero solo cuando los resultados intermedios entran en 16 bits. Por eso al decompilarlo puse short res1 asignando el resultado de factRec(n-1), porque el DX que contiene la parte alta de la llamada no es tenido en cuenta en la subsiguiente multiplicación e incluso es sobrescrito por la misma.

Al esamblarlo faltó un detalle, que en el paso base falta poner DX en 0. O sea cuando se pone mov ax,1 faltaria poner un mov dx,0 también. Esto solo tiene problemas si la llamada inicial a la rutina es con n=0.

Además el programa solo funciona para entradas de 0 a 9, con valores mayores no funciona porque un dato intermedio sobrepasa los 16 bits. 

Saludos,
  Gustavo