• No se han encontrado resultados

Instrucciones PUSH y POP

In document Lenguaje Ensamblador Irvine 5a Edición (página 164-167)

direccionamiento y aritmética

8. Copia de una cadena al revés

5.4.2 Instrucciones PUSH y POP

Instrucción PUSH

La instrucción PUSH primero decrementa a ESP y después copia un operando de origen de 16 o 32 bits en la pila. Un operando de 16 bits hace que ESP se decremente por 2. Un operando de 32 bits hace que ESP se decremente por 4. Hay tres formatos para la instrucción:

PUSH r/m16 PUSH r/m32 PUSH imm32

Si su programa llama a los procedimientos de la biblioteca Irvine32, siempre debe meter valores de 32 bits; si no es así, las funciones de consola Win32 utilizadas por esta biblioteca no funcionarán correctamente. Si su programa llama a los procedimientos de la biblioteca Irvine16 (en modo de direccionamiento real), puede meter valores de 16 o de 32 bits.

5.4 Operaciones de la pila 131

Los valores inmediatos siempre son de 32 bits en modo protegido. En modo de direccionamiento real, los valores inmediatos son de 16 bits de manera predeterminada, a menos que se utilice la directiva del procesa- dor .386 (o superior) (en la sección 3.2.1 hablamos sobre la directiva .386).

Instrucción POP

La instrucción POP primero copia el contenido del elemento de la pila al que apunta ESP, en un operando de destino de 16 o 32 bits, y después incrementa ESP. Si el operando es de 16 bits, ESP se incrementa por 2; si el operando es de 32 bits, ESP se incrementa por 4:

POP r/m16 POP r/m32

Instrucciones PUSHFD y POPFD

La instrucción PUSHFD mete el registro EFLAGS de 32 bits en la pila, y POPFD saca el valor de la pila y lo mete en EFLAGS:

pushfd popfd

Los programas de 16 bits utilizan la instrucción PUSHF para meter el registro FLAGS de 16 bits en la pila, y POPF para sacar un valor de la pila y meterlo en FLAGS.

La instrucción MOV no puede usarse para copiar las banderas a una variable, por lo que PUSHFD puede ser la mejor forma de guardar las banderas. Hay veces que es útil realizar una copia de respaldo de las ban- deras, para poder restaurarlas más adelante a los valores que tenían. A menudo, encerramos un bloque de código dentro de PUSHFD y POPFD:

pushfd ; guarda las banderas

;

; aquí va cualquier secuencia de instrucciones... ;

popfd ; restaura las banderas

Al utilizar instrucciones de este tipo, hay que asegurarnos de que la ruta de ejecución del programa no ignore a la instrucción POPFD. Cuando se modifi ca un programa después de cierto tiempo, puede ser difícil recordar en dónde se encuentran toda las instrucciones que meten y sacan del stack. ¡Es imprescindible tener una documentación precisa!

Una manera menos propensa a errores de guardar y restaurar las banderas es meterlas en la pila, e inme- diatamente después sacarlas y colocarlas en una variable:

.data

guardarBanderas DWORD ? .code

pushfd ; mete las banderas en la pila

pop guardarBanderas ; las copia en una variable

Las siguientes instrucciones restauran las banderas desde la misma variable:

push guardarBanderas ; mete los valores guardados de las banderas

popfd ; los copia a las banderas

PUSHAD, PUSHA, POPAD y POPA

La instrucción PUSHAD mete todos los registros de propósito general de 32 bits en la pila, en el siguiente orden: EAX, ECX, EDX, EBX, ESP (su valor antes de ejecutar PUSHAD), EBP, ESI y EDI. La instrucción POPAD saca los mismos registros de la pila, en orden inverso. De manera similar, la instrucción PUSHA, que se introdujo con el procesador 80286, mete los registros de propósito general de 16 bits (AX, CX, DX, BX, SP, BP, SI, DI) en la pila, en el orden listado. La instrucción POPA saca los mismos registros en orden inverso.

Si escribe un procedimiento para modifi car varios registros de 32 bits, use PUSHAD al principio del procedimiento y POPAD al fi nal para guardar y restaurar los registros. El siguiente fragmento de código es un ejemplo:

MiSub PROC

pushad ; guarda los registros de propósito general

. . mov eax,... mov edx,... mov ecx,... . .

popad ; restaura los registros de propósito general

ret MiSub ENDP

Debemos recalcar una importante excepción al ejemplo anterior: los procedimientos que devuelven resulta- dos en uno o más registros no deben utilizar PUSHA y PUSHAD. Suponga que el siguiente procedimiento

LeerValor devuelve un entero en EAX; la llamada a POPAD sobrescribe el valor de retorno de EAX:

LeerValor PROC

pushad ; guarda los registros de propósito general

. .

mov eax,valor_retorno .

.

popad ; ¡sobrescribe EAX!

ret LeerValor ENDP

Ejemplo: invertir una cadena

El programa InvCad.asm itera a través de una cadena y mete cada uno de sus caracteres en la pila. Después saca las letras de la pila (en orden inverso) y las almacena de vuelta en la misma variable de cadena. Como la pila es una estructura UEPS (último en entrar, primero en salir), se invierten las letras en la cadena:

TITLE Invertir una cadena (InvCad.asm)

; Este programa invierte una cadena. ; Última actualización: 06/01/2006 INCLUDE Irvine32.inc

.data

unNombre BYTE "Abraham Lincoln",0 tamanioNombre = ($ - unNombre) - 1 .code

main PROC

; Mete el nombre en la pila. mov ecx,tamanioNombre mov esi,0

L1: movzx eax,unNombre[esi] ; obtiene el carácter

push eax ; lo mete en la pila

inc esi loop L1

; Saca el nombre de la pila, en orden inverso, ; y lo almacena en el arreglo unNombre.

mov ecx,tamanioNombre mov esi,0

5.4 Operaciones de la pila 133

L2: pop eax ; obtiene el carácter

mov unNombre[esi],al ; lo almacena en la cadena

inc esi loop L2

; Muestra el nombre.

mov edx,OFFSET unNombre call Writestring call Crlf exit main ENDP END main 5.4.3 Repaso de sección

1. ¿Cuáles son los dos registros (en modo protegido) que manejan la pila?

2. ¿Qué diferencia hay entre la pila en tiempo de ejecución y el tipo de datos abstracto pila? 3. ¿Por qué a la pila se le conoce como estructura UEPS?

4. Cuando se mete un valor de 32 bits en la pila, ¿qué ocurre con ESP?

5. (Verdadero/Falso): cuando se utiliza la biblioteca Irvine32, sólo deben meterse valores de 32 bits en la pila.

6. (Verdadero/Falso): cuando se utiliza la biblioteca Irvine16, sólo deben meterse valores de 16 bits en la pila. 7. (Verdadero/Falso): las variables locales en los procedimientos se crean en la pila.

8. (Verdadero/Falso): la instrucción PUSH no puede tener un operando inmediato. 9. ¿Qué instrucción mete todos los registros de propósito general de 32 bits en la pila? 10. ¿Qué instrucción mete el registro EFLAGS de 32 bits en la pila?

11. ¿Qué instrucción saca elementos de la pila y los coloca en el registro EFLAGS?

12. Reto: otro ensamblador (llamado NASM) permite que la instrucción PUSH liste varios registros específi cos. ¿Por qué este método podría ser mejor que la instrucción PUSHAD en MASM? He aquí un ejemplo de NASM:

PUSH EAX EBX ECX

13. Reto: suponga que no existe la instrucción PUSH. Escriba una secuencia de otras dos instrucciones que rea- licen lo mismo que PUSH EAX.

5.5

Defi nición y uso de los procedimientos

Si ya ha estudiado un lenguaje de programación de alto nivel, entonces sabe lo útil que puede ser dividir los pro- gramas en subrutinas. Por lo general, un problema complicado se divide en tareas separadas para poder compren- derlo, implementarlo y probarlo con efectividad. En el lenguaje ensamblador, por lo regular, usamos el término

procedimiento para indicar una subrutina. En otros lenguajes, a las subrutinas se les llama métodos o funciones.

En términos de la programación orientada a objetos, las funciones o métodos de una clase individual son apenas equivalentes a la colección de procedimientos y datos encapsulados en un módulo en lenguaje en- samblador. Este lenguaje se creó mucho antes de la programación orientada a objetos, por lo que no tiene la estructura formal que se encuentra en los lenguajes orientados a objetos. Los programadores de ensamblador deben imponer su propia estructura formal en los programas.

In document Lenguaje Ensamblador Irvine 5a Edición (página 164-167)