Академический Документы
Профессиональный Документы
Культура Документы
El PUSH AX equivale a:
SUB SP,02
MOV [SP],AX
El POP AX equivale a:
MOV AX,[SP]
ADD SP,02
Nota: hay veces en que se desea liberar n posiciones de la pila, sin alterar ningún registro, por lo que se
le agrega al apuntador de pila SP dos posiciones por cada 16 bits. Si se hizo un PUSH (16 bits) se debe
hacer:
ADD SP,02
Como ya se mencionó con anterioridad, la pila se utiliza automáticamente cuando se invoca una
función. Es por ello que no se debe intentar almacenar un dato en la pila con un PUSH antes de invocar
una función, y reclamar el dato dentro de la función con el respectivo POP, pues estando dentro de la
función, el dato disponible en la pila viene siendo la dirección de retorno, en lugar del dato deseado. La
excepción a advertencia, viene siendo el paso de parámetros a una función, que utiliza una técnica muy
ingeniosa con el uso de la memoria, y que será explicada a continuación.
32
Apuntes de Lenguaje Ensamblador Luis A. Zarza López, UTM, 2017
Hasta ahora, para pasar un parámetro, se ha utilizado DX y algún otro registro ocasional. Pero esto
limita el número y tamaño de parámetros que se pueden pasar.
Para paso de múltiple de parámetros, por convención, se utiliza la pila, de la siguiente manera:
mov ax,03
push ax ;pila: 0003
mov ax,05
push ax ;pila: 0005 0003
call funcion
;pila: 0005 0003 (después de volver)
add sp,04 ;pila: (vacía)
Se puede ver que los dos parámetros se ponen en AX (puede ser cualquier registro) y se introducen en
la pila, y se invoca la función. Al salir, podemos suponer que la pila sigue ocupada por esos datos. Para
restaurar la pila sin copiar los datos en ninguna parte (no se les requiere), se le agrega 2 al SP por cada
16 bits introducidos a la pila. En este caso, puesto que fueron dos PUSH de 16 bits, se suma 4 al
apuntador SP, y la pila quedará vacía (en lo que respecta a nuestro código, pues la pila podría traer
datos introducidos antes).
La función invocada debe incluir código adicional para poder acceder a los parámetros y para poder
usar variables locales. Se hará uso de un apuntador especial llamado base pointer (BP). Dentro de una
función, se debe respaldar el valor que traía (usado por alguna otra función previa) y crear un nuevo
valor (dirección) que servirá como base para acceder a los parámetros y variables locales. También se
restarán n posiciones al SP para disponer de n bytes para variables locales. Al bajar el apuntador del SP,
esos espacios en la pila no serán utilizados por los posteriores PUSH y POP hechos dentro de la
función. La función quedará así:
fun: ;pila: retH retL 0005 0003 (al entrar)
32
Apuntes de Lenguaje Ensamblador Luis A. Zarza López, UTM, 2017
Como puede observarse, para el acceso a parámetros y variables locales se utiliza el apuntador BP, el
cual se encuentra definido dentro de la función. Sin importar el tamaño de la pila, usando posiciones a
partir de BP, se pueden acceder esas posiciones en la pila. Este uso de la pila para parámetros y
variables locales es muy ingenioso porque la asignación de esos espacios es dinámica y se libera de
manera muy simple al salir de las funciones. Esta técnica es utilizada por casi todos los lenguajes de
programación estructurada y orientada a objetos (si no, es que todos) para el paso de parámetros y
variables locales. Es muy importante saber cómo se reserva este espacio, para no esperar que los datos
ahí almacenados aún estén disponibles después de salir de la función. Devolver el apuntador a una
variable local para acceder al dato desde afuera es una práctica terrible.
Algoritmo SumatoriaRecursiva( n )
1. Si n es 1, entonces
1.1 Devolver 1 (salir)
2. Devolver n + SumatoriaRecursiva( n-1 )
32
Apuntes de Lenguaje Ensamblador Luis A. Zarza López, UTM, 2017
3 + (2 + SumatoriaRecursiva( 1 ) ) =
3 + (2 + 1) =
6
Es importante darse cuenta de que muchas personas no alcanzan a entender la recursividad, su alcance
y aplicaciones. Conviene revisar la explicación anterior varias veces para estar seguro de que se puede
entender.
A continuación se muestra el código del programa que calcula la sumatoria recursiva.
;Sumatoria recursiva
main: call lee2 ;dato en AL
call reto
push ax ;Parámetro en la pila.
;Dato de 8 bits, se ignorará parte alta
call sumat ;Resultado en AL .
add sp,2 ;Restauración de pila
mov dl,al
call des2 ;Despliegue de resultado.
.exit 0
sumat: push bp
mov bp,sp ;Se crea nuevo BP.
;Detectar condición de salida
mov ax,[bp+4]
cmp al,1 ;Si parámetro es 1
je salida ; proceder a la salida
; ya teniendo 1 en AL.
;Calcular resultado para actual invocación
mov ax,[bp+4] ;Obtener parámetro, ignorar parte alta.
dec al ; decrementarle 1
push ax ; y prepararlo como parámetro.
call sumat ;Se invoca la misma función,
; tendrá su propio espacio.
;Resultado de sumat(n-1) en AL.
add sp,2 ;Se restaura la pila.
mov bx,[bp+4] ;Se carga parámetro para operación,
add al,bl ; y se efectúa la suma, que será:
; n + sumat(n-1)
32
Apuntes de Lenguaje Ensamblador Luis A. Zarza López, UTM, 2017
Ejercicios:
1. Modificar el ejemplo de sumatoria para que efectúe el factorial de un número. No exceder de 16 bits
en el resultado.
2. Hacer un programa que calcule el valor de fibonacci(x), donde el algoritmo viene dado por:
Algoritmo FibonacciRecursivo( n )
1. Si n es menor o igual a 2, entonces
1.1 Devolver 1 (salir)
2. Devolver FibonacciRecursivo( n-1 ) + FibonacciRecursivo( n-2 )
3. Hacer un programa que determine los movimientos para resolver el problema de Las Torres de Hanoi
mediante una función recursiva. Este problema es particularmente difícil de entender pero muy fácil de
implementar. Se deberá investigar con cuidado las características del problema y sus reglas, y probarlo
manualmente utilizando monedas de tamaños diferentes, para torres de una moneda, dos monedas, tres
monedas y hasta cuatro monedas.
Si ya se entiende el problema se puede proseguir con la explicación siguiente:
El algoritmo de Hanoi recursivo inicia considerando el problema de mover una pila de discos como
sólo mover dos partes: 1) n-1 discos (como una sola cosa), y 2) el disco de abajo, utilizando tres
movimientos (como resolver el problema con 2 monedas). Mover n-1 discos tal cual es una violación a
las reglas, pero esta operación se resuelve invocando recursivamente el algoritmo, pero ahora con un
disco menos. Cada invocación al algoritmo indica de dónde a dónde hay que mover, cuál es el poste
auxiliar, y cuántos discos tiene el problema).
Algoritmo HanoiRecursivo(poste_origen,poste_auxiliar,poste_destino,n)
1. Si n es 0, salir (no hay nada que mover).
2. HanoiRecursivo(poste_origen,poste_destino,poste_auxiliar, n-1 )
3. Mover disco de poste_origen a poste_destino (indicar movimiento)
4. HanoiRecursivo(poste_auxiliar,poste_origen,poste_destino, n-1 )
Es importante notar que en los pasos 2 y 4 se están intercambiando los postes, pues para los
movimientos internos de discos, se intercambian los roles de los postes. En el paso 2, se utiliza el poste
destino como auxiliar, y el auxiliar como destino. También se hacen intercambios en el paso 4. En el
paso 3, el programa deberá desplegar en pantalla el movimiento a realizar, del origen al destino (de
acuerdo a los parámetros recibidos en esa invocación específica). Como parámetros, se pueden pasar
los valores ASCII de las letras A, B y C.
32