LENGUAJE ENSAMBLADOR Conjunto de instrucciones que entiende el microprocesador y que ejecuta una serie de operaciones. Es un lenguaje de bajo nivel cercano al hardware. Índice: 1.- Organización interna. 2.- Segmentación de memoria. 3.- Modos de direccionamiento. 4.- Formato de instrucciones. 5.- La familia Intel 80x86. ¿Qué es arquitectura software? Son los atributos visibles por el programador. El compilador como generador automático de código. No tiene que haber una correspondencia real con la capa física. Es el “contrato” entre el fabricante del procesador y el programador. La arquitectura software del 80x86 data de 1978 y no ha evolucionado casi nada. Las cosas que se han añadido son: Extensiones en el tamaño de la palabra. Extensiones 386 para el modo protegido. Anexos al repertorio para multimedia (computación vectorial). - MMX - SSE y SSE2 Típico repertorio CISC, el repertorio de instrucciones es del tipo CISC: juego de instrucciones complejo, es algo antiguo. 1.-Organización interna: Registros de propósito general: Los ve el programador y el compilador, hay mas registros pero no son accesibles. 15 8 0 15 0 AH AL AX BP BH BL BX SI CH CL CX DI DH DL DX SP H-> parte alta del registro. L -> parte baja del registro. Pág. 2 AX [registro acumulador] = sirve para operaciones de producto, división, entrada y salida y transferencia optimizadas. BX [registro base] = puntero a dirección base (datos). CX [registro contador] = es el contador de bucles, desplazamientos, rotaciones y repetición de cadenas. DX [registro de datos] = Producto, división y entrada y salida. BP [puntero base] = Puntero a direcciones base (pila). SI [índice fuente] = Fuente en manejo de cadenas. DI [índice destino] = Destino en manejo de cadenas. SP [puntero a pila] = Puntero a la cima de la pila. Estos registros no son de propósito general pero los llamamos así. Registros de propósito general: Almacenamiento temporal de variables. Algunos se pueden tratar en ancho palabra (16 bits) o byte. - Registro X, 16 bits. - Registro L y H, 8 bits. Usos dedicados que generan las limitaciones. Modelo híbrido (acumulador y banco de registros) heredado de arquitecturas anteriores. Las operaciones entre operandos que residen en memoria no están permitidas con lo que algunos de los operandos debe residir temporalmente en estos registros. BP utilizado para pasar argumentos a pila. Las máquinas actuales tienen 128 registros, pero la máquina se encarga de mapear y asignarlos a los 8. Registros de segmento: 15 0 CS Segmento de código. DS Segmento de datos. SS Segmento de pila. ES Segmento extra (datos). Sirven para diferenciar las diferentes porciones de memoria de un programa. Los programas manejan diferentes áreas de memoria: Datos (DS, ES) -> hacen referencia a esta parte de memoria. Pág. 3 Código (texto) -> programa propiamente dicho, lo que escribimos (DS). Pila (SS). Heap (SS). Todo esto está derivado del modelo de Von Neuman. Todas las direcciones son relativas a alguno de los registros de segmento, esto viene por defecto. Es una idea importante. CS = Las direcciones de este segmento contiene instrucciones. [Code Segment] DS = Estas direcciones contienen datos declarados por el programa (reserva de memoria estática). [Data Segment] SS = es la pila -> almacenamiento temporal de datos (parámetros de función). [Stack Segment] ES = datos de cadenas. [Extra Segment] Otros registros: Flags Estado del procesador. IP Puntero de instrucción -> indica por donde vamos en el programa. 15 0 O D I T S Z A P C Acarreo (C) = si es 1 el resultado de una operación genera acarreo. Paridad (P) = es 1 si el resultado contiene un número par de bits a 1. Acarreo Auxiliar (A) = es 1 si el resultado genera acarreo en los 4 bits de menos pero. Se usa en aritmética BCD. Cero (Z) = es 1 si el resultado es cero. Signo (S) = copia el bit de mayor pero del resultado (independientemente de la interpretación del resultado). Trap (T) = si es 1 el procesador genera una interrupción de paso a paso después de ejecutar cada instrucción. Interrupción (I) = si es 1 las interrupciones serán reconocidas. Dirección (D) = si es 1 las operaciones con cadenas se realizan de las posiciones altas a las bajas. Desbordamiento (O) = overflow. Es 1 si el resultado es demasiado grande (o pequeño) para los limites de representación. 2.-Segmentación de memoria: Pág. 4 El mapa de memoria que “ve” el 80x86 NO es “plano”. Los registros de 16 bits solo pueden direccional 216 posiciones de memoria (65635 posiciones = 64 K) ¿Cómo alcanzar un espacio de direcciones mayor? El acceso a memoria se realiza en segmentos de 64 K. Esto provoca complicaciones excesivas en los accesos a memoria. La capacidad de direccionamiento es de 1 M (220). La dirección completa se consigue mediante la combinación de dos direcciones: segmento y desplazamiento (offset): Segmento: desplazamiento. Base: desplazamiento. La dirección física se calcula: Base x 16 + desplazamiento -> añado 4 ceros a la parte mas baja de la dirección (x16) y luego le sumo el desplazamiento. Ejemplo: La dirección 53C2:107A es: 53C20 h + 107A h 54C9A h x16 (x10 h) dirección física La dirección B100:046C es: B1000 h + 046C h B146C h x16 (x10 h) dirección física Características: La misma dirección física puede ser accedida con diferentes combinaciones base:desplazamiento. Ejemplo: La dirección física 7A26B h puede ser: Pág. 5 7A26 : 000B 7A00 : 026B 751C : 50AB Esto es una complicación para el programador y tiene problemas de seguridad (se puede borrar cosas ya escritas) pantallas azules del Windows 98. La base (segmento) apunta a párrafos (de 16 posiciones). Corresponde a las bases que se les añaden los 0 al final. En ensamblador no se representan las direcciones como base:desplazamiento, aunque algunos depuradores lo admitan. Los desplazamientos llevan asociados una base por defecto. Cuando es necesario especificar se hace de manera simbólica (usando el nombre del registro segmento) -> prefijo. Tabla de segmentos empleados por defecto para cada uno de los registros de desplazamiento que se encuentran en la primera columna: Offset IP SP BP BX SI DI Base CS Sí No Prefijo Prefijo Prefijo Prefijo SS No Sí Por defecto Prefijo Prefijo Prefijo DS No No Prefijo Por defecto Por defecto Por defecto ES No No Prefijo Prefijo Prefijo Por defecto (cadenas) Representación en ensamblador: Las direcciones pueden ser Near o Far: Near: solo el desplazamiento; la base toma el valor en curso de la dada por defecto. Far: se requiere la especificación de la base y del desplazamiento. 3.- MODOS DE DIRECCIONAMIENTO: Son los mecanismos que determinan la ubicación de los operandos. Hay tres posibles ubicaciones: Inmediatos (en la propia instrucción) En registros En memoria: - Directos - Absolutos - Inmediatos - Relativos. INTEL. ESTRUCTURA DE COMPUTADORES. Pág. 6 (Implícitos) -> sobreentendidos. Momento en que se actualiza el modo de direccionamiento: Inmediatos: tiempo de ensamblado. En registros: tiempo de diseño(programación o compilación) En memoria: - Absolutos: tiempo de carga -> interviene el S.O. - Relativos: tiempo de ejecución. Absoluto DIRECTO Relativo A registro A memoria A página base A registro Base Base + desplazamiento Índice Contador de programa Puntero a pila INDIRECTO IMPLÍCITO 3.1. – Inmediato: MOV AL, 15h IP 0000 CS DS 0100 0000 Dirección 01000 01001 8B IP 0002 CS DS 0100 0000 … … AX BX AX XX15 BX … … Banco de registros Memoria Dirección 01000 01001 01002 Banco de registros 8B Memoria Antes Después 3.2. – Directo absoluto a registro: MOV AX, BX IP 0000 CS DS 0100 0000 Dirección 01000 01001 8B IP 0002 CS DS 0100 0000 … … AX XXXX BX 7EA6 AX 7EA6 BX 7EA6 … … Banco de registros Memoria Banco de registros Antes Después Dirección 01000 01001 01002 8B … Memoria Pág. 7 3.3. – Directo absoluto a memoria: MOV CX, ETIQUETA IP 0000 CS DS 0100 0200 Dirección 01000 01001 01002 01003 8B 0E 34 12 IP 0002 CS DS 0100 0200 … … AX BX AX BX CX XXXX 03234 03235 Banco de registros ED BE Dirección 01000 01001 01002 01003 01004 8B 0E 34 12 … CX BEED Memoria Banco de registros Memoria Antes Después ETIQUETA = 1234h DS = 0200h · 16h = 02000h + ETIQUETA = 1234h ------------------------------------------03234h 3.4. – Directo relativo a registro (indirecto a memoria): MOV [BX] + ARTICULO, AL IP 0000 CS DS 0100 0500 Dirección 01000 01001 01002 01003 8B 87 00 50 IP 0002 CS DS 0100 0500 … … AX XXFC BX 1000 CX AX XXFC BX 1000 CX 0B000 0B001 Banco de registros XX XX Memoria Banco de registros Antes Después DS = 0500h · 16h = 05000h Artículo = 5000h + BX = 1000h ------------------------------------------0B000h Dirección 01000 01001 01002 01003 01004 0B000 0B001 8B 87 00 50 … FC XX Memoria Pág. 8 3.5. – Directo relativo a registro (indirecto a memoria): MOV DL, VECTOR[SI] IP 0000 CS DS 0100 B000 Dirección 01000 01001 01002 01003 01004 8A 94 00 AD XX IP 0002 CS DS 0100 B000 … … AX BX XXXX CX AX BX XXED CX ED BA000 Banco de registros Memoria Dirección 01000 01001 01002 01003 01004 8A 94 00 AD XX ED BA000 Banco de registros Memoria Antes Después DS = B000h · 16h = B0000h + ETIQUETA = A000h ------------------------------------------BA000h Vector = A000h 3.6. – Directo relativo a registro (indirecto a memoria): MOV AH, [BX][SI] + ARRAY IP 0000 CS DS 0100 0200 Dirección 01000 01001 01002 01003 01004 IP 0002 CS DS 0100 0200 … … AX BX AX BX 1000 CX Banco de registros 06234 1000 06234 CX Memoria Dirección 01000 01001 01002 01003 01004 Banco de registros Memoria Antes Después DS = 0200h · 16h = 02000h Array = 1234h + BX = 1000h SI = 2000h ------------------------------------------06234h 4.- FORMATO DE INSTRUCCIONES: Es la codificación binaria de las instrucciones de máquina. El formato debe proporcionar información acerca de: - Operación a realizar. - Operandos y resultado. - Siguiente instrucción (secuencia implícita). El 80x86 cuenta con dos formatos: formato general y formato especial (con campo de extensión o con prefijo). Formato general: Pág. 9 Lo tienen todos: Byte 1 Código Byte 2 d w Mod Byte 3 reg Byte 4 r/m Desplazamiento o dato inmediato r/m y Mod = especifica un operando registro o memoria. Reg = especifica un operando registro. W = determina el tamaño de los operandos. D = determina el operando fuente y destino. Código = código de operación. El primer byte contiene tres clases de información: código de operación -> los 6 primeros bits contienen el código de la operación a realizar. El bit de dirección de registro (D) especifica si el operando dado por el campo de registro (operando Reg) en el segundo byte es el operando fuente o destino: si D = 1 tengo que Reg = operando destino, si D = 0 tengo que Reg = operando fuente. El bit de tamaño de dato (W) especifica si la operación será realizada sobre datos de 8 o 16 bits. W = 0 8 bits, W = 1 16 bits. El segundo byte contiene los siguientes operandos: una siempre es un registro que viene dado por reg, el otro puede ser registro o memoria que viene dado por Mod y r/m. No se advierten las operaciones entra dos operandos ubicados en memoria. Campo Reg: El tamaño W nos dice si es byte (8 bits) W = 0. Word (16 bits) W = 1. Campo Mod y R/M Mod = 11 referenciamos registro Mod 01 -> se le suma desplazamiento 8 bits Mod 10 -> se le suma desplazamiento 16 bits MOD = 11 REG – R/M W=0 000 001 010 011 100 101 110 111 AL CL DL BL AH CH DH BH W=1 AX CX DX BX SP BP SI DI MOD = 00 MOD = 01 MOD = 10 DS:[BX]+[SI] DS: [BX]+[DI] SS:[BP]+[SI] SS:[BP]+[DI] DS: [SI] DS: [DI] Dirección DS: [BX] DS:[BX]+[SI]+D8 DS:[BX]+[DI]+D8 SS:[BP]+[SI]+D8 SS:[BP]+[DI]+D8 DS: [SI] + D8 DS: [DI] + D8 SS: [BP] + D8 DS: [BX] + D8 DS:[BX]+[SI]+D16 DS:[BX]+[DI]+D16 SS:[BP]+[SI]+D16 SS:[BP]+[DI]+D16 DS: [SI] + D16 DS: [DI] + D16 SS: [BP] + D16 DS: [BX] + D16 Pág. 10 Dirección -> ese código se reserva para dar direcciones absolutas a memoria. Formato especial con campo de extensiones: Se necesitan 2 bytes para determinar la operación a realizar. Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 MOD REG R/M Campo de extensión Desplazamiento y/o dato inmediato. Se usa para aumentar el número de operaciones. Tiene que esperar a leer el segundo byte para saber cuál es la operación concreta que tiene que realizar. Los campos Mod y R/M dan lugar al operando (registro o memoria). El campo REG da el código de operación completando el campo de extensión. Campo de extensión 80, 81, 82, 83 D0, D1, D2, D3 F6, F7 FE, FF Grupo Inmediatos Desplazamientos Grupo I Grupo II Formato especial con prefijo: Byte 0 Byte 1 Byte 2 Prefijo PREFIJO CS DS SS ES REP REPZ / REPE REPNZ / REPENE LOCK Ejemplo: EFECTO Modificamos la base por defecto Repetición de instrucción de cadena Sincronización con coprocesador Pág. 11 MOV AX, [BX] [SI] DS x 16 + [BX] [SI] CS : MOV AX, [BX][SI] CS x 16 + [BX] [SI] Ejercicio: La instrucción MOV BL, AL mueve el byte contenido en el registro fuente AL al registro BL. Escribir el código máquina de la instrucción sabiendo que el código de operación es 1000102. En el primer byte los primeros 6 bits especifican la operación y debe ser: Código de operación = 1000102 El bit D indica si el registro que señala el campo REG del segundo byte es el operando fuente o el destino. Codificamos el registro AL en el campo REG y por tanto D será igual a 0. El bit W debe indicar una operación de tamaño = 0 (byte valor 0). Primer byte 1000 10002 = 88 h En el segundo byte, REG, indica el operando limite que es AL. Su código correspondiente es: REG = 000. Como el segundo operando es también un registro MOD debe valer 11. El campo R/M debe especificar que el registro destino es BL y su codificación es 011. Esto da: MOD = 11 R/M = 011 Segundo byte = 1100 00112 = C3 h El código hexadecimal completo para la instrucción es: MOV BL, AL = 88 C3 h También se podría haber puesto D = 1, REG = 011 y R/M = 000. 5.- LA FAMILIA INTEL 80x86 1971 -> Llamado 4004 Fecha de comercialización: noviembre 1971. Frecuencia de reloj: 108 Khz. Número de transistores: 2300 (tecnología 10 micras) Diseñado para calcular. 1972 -> 8008 (solo tiene el registro AX acumulador). 1974 -> 8880 Para diseñar control de semáforos y Altaír (primer PC). Memoria direccionable 64 Kbytes. Añade bus de datos 8 bits. Tamaño registros 8 bits. 1976 -> 8085 Pág. 12 1978 -> 8086 Es híbrido (acumulador y banco de registros) porque hereda las características de los anteriores. Ancho bus de datos16 bits Memoria direccionable 1 Mbyte. 1979 -> 8088 1985 -> Intel 386 1989 -> Intel 486 1993 -> Intel Pentium Processor 1995 -> Intel Pentium Pro Processor 1997 -> Intel Pentium MMX (multimedia extensions) 1998 -> Intel Pentium II 1999 -> Intel Pentium III El desarrollo de las tecnologías de fabricación permite que el número de transistores integrados de los microprocesadores aumente cada 18 meses. La progresión del desarrollo de procesadores puede visualizarse en una gráfica de acuerdo a la ley de Moore. Pág. 13 INSTRUCCIONES DE TRANSFERENCIA 80x86: 1.- Generalidades. 2.- Movimiento de datos. 3.- Estructura de signo. 4.- Transferencia de puntero. 5.- Transferencia con la pila. 6.- Entrada / Salida. 1.- Generalidades: El primer operando es el operando destino. El segundo operando es el operando fuente. No se pueden realizar operaciones donde ambos operandos residan en memoria. Las instrucciones de transferencia NO alteran el registro de estado. Los ejemplos son de dos clases: - En ensamblador puro [RO88] - En ensamblador con directivas [Mi87], directivas-> instrucciones para el programa ensamblador que suponen una ayuda para el programador: soporte de etiquetas, punteros a direcciones que resuelve el sistema operativo en tiempo de carga, etc... 2.- Movimiento de datos: MOV: {registro/memoria}, {registro/memoria/inmediato} Transfiere un byte o una palabra desde fuente a destino. El operando fuente no se destruye. Ambos operandos deben ser del mismo tamaño. Ejemplo: ; AX = FFFF h ; BX = 1234 h MOV AX, BX ; AX = 1234 h ; BX = 1234 h Restricciones: No se puede mover datos entre dos posiciones de memoria; hay que utilizar un registro intermedio: MOV AX, mem1 MOV mem1, AX No se puede mover un intermedio a un registro de segmento; hay que usar un registro intermedio. (Método de seguridad) MOV AX, 1234 h ; AX = 1234 h Pág. 14 MOV DS, AX ; DS = 1234 h El registro de segmento CS no puede ser destino. XCHG:{registro/memoria}, {registro/memoria} Intercambia el contenido de los operandos. Es útil para evitar el uso de una variable temporal. Ejemplo: ; AX = FFFF h ; BX = 0000 h XCHG AX, BX ; AX = 0000 h ; BX = FFFF h XLAT: {memoria} En este caso memoria es el desplazamiento sobre DS. Prefijo XLAT memoria. En este caso el segmento viene dado por el prefijo y el puntero a memoria será: Prefijo : memoria La instrucción XLAT carga en AL un valor de una tabla de memoria. Es útil para traducir entre sistemas de codificación. La tabla debe ser de de bytes y no puede tener mas de 256 bytes; el primero tiene desplazamiento 0. La base de la tabla se coloca en BX y el puntero en AL. AL <- [BX+AL] Ejemplo: TABLA DB 1, 2, 3, 4, 5, 6, 7 MOV BX, OFFSET TABLA MOV AL, 4 XLAT TABLA ; declaración ; carga BX ; 5º valor ; AL = 5 Esto es equivalente a: MOV AL TABLA [4] Ejemplo: Tabla que permite traducir ASCII a EBCDIC. El código ASCII sirve de índice, por ejemplo el índice 32 apunta al “2” en EBCDIC. Valor XX XX … F0 F1 … Dirección TABLA[0] TABLA[1] … TABLA[30] TABLA[31] … Pág. 15 LAHF: Carga los 8 bits más bajos del registro de estado (banderas de estado) en AH. AH <- banderas de estado (bits 0, 2, 4, 6, 7) SAHF: Recupera las banderas de estado desde AH. Banderas de estado<-AH Las instrucciones de transferencia de las banderas de estado se suelen usar para mover dicho byte de estado entre coprocesadores. Para manejar el conjunto completo del registro de estado se deben usar instrucciones de transferencia con la pila: PUSHF, POPF. 3.- Extensión de signo: Antes de poder mover datos de diferentes tamaño es necesario extender adecuadamente el signo. El procedimiento es distinto si el número es considerado con signo o sin signo, pero es el programador el que debe tenerlo en cuenta ya que la máquina no advierte la diferencia. Cuando el valor tiene signo se usa CBW o CWD. Cuando el valor es sin signo se rellena con ceros. CBW: Sirve para convertir byte en palabra. Copia el bit 7 del registro AL en todo el registro AH. CWD: Sirve para convertir palabra en doble palabra. Copia el bit 15 del registro AX en el registro DX. Doble palabra -> DX: AX Ejemplo con signo: mem8 mem16 .DATA DB -5 DW -5 .CODE MOV AL, mem8 CBW MOV AX, mem16 CWD ; declaración ; declaración ; carga AL = F8 h ; AX = FFFB h (-5) ; Carga AX = FFFB h ; DX = FFFF h ; DX : AX = (-5) Pág. 16 Ejemplo sin signo: mem8 mem16 .DATA DB 251 DW 251 ; declaración (FB h) ; declaración (FFFB h) .CODE MOV AL, mem8 XOR AH, AH MOV AX, mem16 XOR DX, DX ; carga AL = F8 h (251) ; AX = 00FB h (251) ; Carga AX = FFFB h ; DX : AX = 0000 FFFF h 4.- Transferencia de punteros: Usamos instrucciones para cargar punteros a memoria en registros. Los punteros pueden ser: Near -> dentro de un registro, no excede 64 k posiciones; basta conocer el desplazamiento. El comando es LEA. Far -> entre segmentos excede los límites del segmento y se requiere una base y un desplazamiento. Los comandos son LES y LDS. LEA: {registro, memoria} Carga un puntero near en un registro, el puntero es la dirección efectiva de la posición de memoria especificada en el operando fuente. El operando destino puede ser cualquiera de propósito general. No están permitidos los registros de segmento. El operando fuente es una posición de memoria especificada por cualquier modo de direccionamiento. Ejemplo: Transfiere el desplazamiento del operando fuente al destino LEA AX, 1234 (SI) ; si SI = 1000 h ; AX = 1234 +1000 ; AX = 2234 h Advertencias respecto a LEA: LEA DX, cadena MOV DX, OFFSET cadena Dan el mismo resultado pero es más rápida la segunda ya que la posición de cadena en el área de datos es conocida en tiempo de ensamblado. Usaremos LEA cuando queremos transferir un desplazamiento no conocido en tiempo de diseño: LEA DX, cadena [SI] Pág. 17 MOV DX, OFFSET cadena [SI] : no funciona. LES: {registro, memoria} Transfiere un puntero far (32 bits ) especificado como base y desplazamiento al registro dado en el operando destino y el registro ES. ES -> salva la base. El registro destino salva el desplazamiento, no se aceptan los registros de segmento. El operando fuente es una posición de memoria de tamaño doble palabra (32 bits). Los punteros en memoria se salvan por este orden: Palabra de menor peso -> desplazamiento Palabra de mayor peso -> base. Ejemplo: Dirección VALOR PTR -> 1234 : 5678 LES DI, PTR ; DI = 5678 h ; ES = 1234 h PTR (0) PTR (1) PTR (2) PTR (3) 78 56 34 12 LDS: {registro, memoria} Transfiere un puntero far (32 bits ) especificado como base y desplazamiento al registro dado en el operando destino y el registro DS. DS -> salva la base Registro destino-> salva el desplazamiento (no se aceptan los registros de segmento) El operando fuente es una posición de memoria de tamaño doble palabra (32 bits) Ejemplo: cadena puntero array .DATA DB “Esto es una cadena” DD cadena DD 100 DUP (?) .CODE ; cadena ; salvo PTR ; reservo array Pág. 18 LES DI, puntero LDS SI, array (BX) ; ES : DI <- ptr ; DS : SI <- array (BX) 5.- Transferencia con pila: La pila es un área de memoria para almacenar datos temporalmente; dos causas: paso de parámetros a procedimientos y salvar el valor de registro cuando deben ser utilizados por otra variable. El acceso a la pila no es aleatorio sino secuencial según marca el puntero de pila (SP); es del tipo LIFO. En el 8086 el SP comienza en las posiciones más altas y avanza hacia las más bajas. Las instrucciones que trabajan con la pila solo especifican un operando ya que el otro es implícito (la cima de la pila). Estas instrucciones también actúan de manera implícita sobre el puntero de pila (SP): decrementándolo cuando introducen datos y incrementándolo cuando sacan datos y aumentándolo al sacarlos. Las transferencias son de tamaño palabra (16 bits). El SP se actualiza de dos en dos. Si queremos realizar operaciones e tamaño byte seremos nosotros los que actualizaremos SP. PUSH: {registro/memoria} Pone una palabra nueva en la pila. Decrementa el SP en 2 y coloca el operando en la pila. El operando nunca puede ser CS. SP <- SP – 2 SS : SP <- operando Posiciones altas SP SP AH AX Antes de PUSH AX Después de PUSH AX Posiciones bajas Ejemplo: PUSH AX ; pone AX en la cima de la pila. Es equivalente a: SUB SP, 2 MOV [SP], AX ; SP <- SP + 2 ; SS : SP <- AX No funcionaría, daría error sintáctico. Habría que hacer: SUB SP, 2 MOV BP, SP MOV [BP], AX Pág. 19 POP: {registro /memoria} Saca palabra de la pila. Copia el dato que esta en la cima de la pila en el operando especificado e incrementa el SP en dos. El operando nunca puede ser CS. Operando <- SS : SP SP <- SP + 2 Ejemplo: POP AX ; carga AX con el valor cima de la pila Es equivalente a: MOV AX, [SP] ADD SP, 2 ; AX <- SS : SP ; SP <- SP + 2 Las instrucciones PUSH y POP se usan casi siempre por parejas cuando el uso de la pila se debe a falta de almacenamiento temporal. Las variables salvadas en la pila con PUSH normalmente se sacan de la misma, en orden inverso, con POP. Así normalmente tendré el mismo número de PUSH que de POP. PUSH AX PUSH BX … POP BX POP AX Ejemplo: MOV CX, 10 EXTERNO: INTERNO: ….. ….. ….. ….. PUSH CX MOV CX, 20 ….. ….. ; salvamos contador externo ….. ….. LOOP INTERNO ….. POP CX LOOP EXTERNO ; se ejecuta 20 veces ; recupera el bucle del contador ; se ejecuta 10 veces Pág. 20 Sin embargo es posible devolver la pila a la situación original actualizando el valor de SP adecuadamente. Para acceder a parámetros en la pila podemos usar BP. Ejemplo: MOV BP, SP PUSH AX PUSH BX PUSH CX ….. ….. MOV AX, [BP - 6] MOV BX, [BP - 4] MOV CX, [BP - 2] ….. ….. ADD SP, 6 ; salvo el marco de la pila ; SP = BP - 2 ; SP = BP - 4 ; SP = BP - 6 ; recupero el tercer parámetro ; BX <- SS : BP - 4 ; CX <- SS : BP - 2 ; devuelve el SP a su valor inicial PUSHF: Transfiere el registro de estado completo a la pila. SP <- SP – 2 SS : SP <- registro de estado POPF: Carga el registro de estado completo con el contenido de la cima de la pila. Registro de estado <- SS : SP SP <- SP +2 Sirve para modificar flags que de otra forma no se podrían tocar, como el Trap (paso a paso)… 6.- Entrada / Salida: Los mapas de memoria y de entrada / salida en las maquinas 80x86 son disjuntos. Cuando se emite una dirección en el bus de direcciones es necesario especificar si es de memoria o de entrada / salida. Por esto contamos con instrucciones especificas de entrada / salida. IN -> Señal IO / M = 1, señal RD activa; OUT -> señal IO / M = 1, señal WR activa. IN: Acc, (puerto / DX) Carga el acumulador con un valor leído en el puerto de entrada / salida especificado por el operando fuente. El puerto puede ser un incremento de tamaño byte Pág. 21 (puertos 0 - 255). Por encima de este punto hay que darlo como DX (puertos 0 – 65536). El tamaño de la transferencia viene dado por Acc. Si es AX -> tamaño palabra. Si es AL -> tamaño byte. Ejemplos: IN AX, 12h ; leer sobre AX una palabra del puerto 12h. IN AL, DX ; leer sobre AL un byte del puerto especificado en DX. OUT: (puerto / DX), Acc Escribe el contenido del acumulador en el puerto especificado del procesador. El puerto puede ser un incremento de tamaño byte (puertos 0 - 255). Por encima de este punto hay que darlo como DX (puertos 0 – 65536). El tamaño de la transferencia viene dado por Acc. Si es AX = tamaño palabra. Si es AL = tamaño byte. Normalmente las entradas y salidas se realizan mediante llamadas al sistema operativo (que a su vez realiza llamadas a la BIOS). INT 21 h -> bajo D.O.S. API -> bajo Windows Sin embargo estas instrucciones proporcionan un método para entrada salida directamente. ¡OJO! Pueden dar problemas de portabilidad. Ejemplo: OUT 12h, AX ; Transferir el valor de AX al puerto 12h. OUT DX, AL ; Transferir el valor de AL al puerto especificado en DX. Pág. 22 INSTRUCCIONES DE PROCESO ARITMETICAS: 1.- Generalidades. 2.- Suma. 3.- Resta. 4.- Multiplicación. 5.- División. 6.- Operaciones en BCD. 1.- Generalidades: El primer operando es el destino de la operación realizada. Reemplaza su valor inicial por el resultado. No se pueden realizar operaciones donde ambos operandos residan en memoria. Todas las operaciones aritméticas afectan al registro de estado. 2.- Sumas: Realizan sumas: ADD (registro / memoria), (registro / memoria / inmediato) ADC (registro / memoria), (registro / memoria / inmediato) INC (registro / memoria) Por si mismo suman valores de tamaño byte o palabra. En conjunción pueden realizar sumas de 32 bits. Con las instrucciones de ajuste ASCII pueden operar con números BCD. ADD: (registro/memoria) (registro/memoria/inmediato) Suma los operandos y salva el resultado en el operando destino. Los operandos deben ser del mismo tipo (byte o palabra). Las banderas afectadas son: CF, PF, AZ, ZF, SF y OF. La operación puede ser interpretada tanto con signo como sin signo. Es responsabilidad del programador interpretar correctamente el resultado. Si hay desbordamiento es posible numero con signo OF = 1. Si hay desbordamiento es posible numero sin signo CF = 1. Ejemplo: Programa: .DATA mem8 DB 29 .CODE AL MOV AL,26 1A=00011010 INC AL ADD AL,76 1B=00011011 67=01100111 OF CF NV 0 = no NC 0 = overflow no carry NV 0 NC 0 NV 0 NC 0 Sin signo Con signo 26 26 27 103 27 103 Pág. 23 ADD AL, mem8 8E=10001110 OV 1 = NC 0 overflow MOV AH, AL ADD AL, AH 1C=00011100 OV 1 142 CY 1 = 28 carry carry** * Porque el rango con signo es [-128,127] estamos trabajando con 8 bits. En realidad lo que quiere decir este resultado es que hay un error. ** rango = [0, 255]. Me e excedido 28 sobre 256. -114 + desbordamiento* + 27: 11011 + 76: 1001100 01100111 + 39: 100111 10001110 + 10001110 100011100 En el ejemplo anterior el programador puede gobernar la secuencia del programa en función del estado que considere oportuno. Usará el estado condicional que lee el estado deseado -> desbordamiento si opera con signo, acarreo si opera sin signo. ADC: (registro/memoria) (registro/memoria/inmediato) Suma igual que ADD pero incluye en la suma la bandera de acarreo CF. Las banderas afectadas son: CF, PF, AF, ZF, SF y OF. Sirve para realizar sumas de tamaño 32 bits. Utilizaremos dos registros, es conveniente que sean DX : AX. La suma de menor peso se realiza con ADD y la suma de mayor pero se realiza con ADC. Suma los dos operandos e incrementa el resultado en uno si está activada la bandera de acarreo CF. Los operandos deben ser del mismo tipo (byte o palabra). Ejemplo: suma de números de 32 bits: Fuente = DX, CX Destino = BX, AX ADD AX, CX ; sumar palabras inferiores ADC BX, DX ; sumar palabras superiores con acarreo Ejemplo: mem32 .DATA DD 316423 .CODE MOV AX, 43981 XOR DX, DX ADD AX, WORD PTR mem32[0] ADC DX, WORD PTR mem32[2] DX:AX = 43981 + 316423 360404 INC: (registro / memoria) Las banderas afectadas son PF, AF, ZF, SF y OF. No modifica la bandera de acarreo. Funciona como un contador no saturado (da “vueltas” al contador). Pág. 24 3.- Restas: Las instrucciones que realizan restas son: SUB (registro/memoria) (registro/memoria/inmediato) SBB (registro/memoria) (registro/memoria/inmediato) DEC (registro/memoria) NEG (registro/memoria) Por si mismas restan valores de tamaño byte o palabra. En conjunción pueden realizar restas de 32 bits. Con las instrucciones de ajuste ASCII pueden trabajar con números BCD. SUB: (registro/memoria) (registro/memoria/inmediato) Resta los operandos y salva el resultado en el operando destino. Operando destino <- operando destino – fuente. Las banderas que se usan son CF, PF, AF, ZF, SF y OF. La operación puede ser interpretada tanto sin signo como con signo. Es responsabilidad del programador interpretarlo correctamente. Ejemplo: Programa AL OF SF Sin Signo Con Signo .DATA mem8 DB 122 .CODE MOV AL, 95 5F = 01011111 NV 0 PL 0 95 95 DEC AL 5E = 01010110 NV 0 PL 0 94 94 SUB AL, 23 47 = 01000111 NV 0 PL 0 71 71 SUB AL, mem8 CD = 11001101 NV 0 NG 1 205 + signo** -51 5E = 01010110 OV 1 NG 1 MOV AH, 119 SUB AL, AH 85 + desbordamiento* * -51 -119 = -170; 256 -170 = 86 ** 71 – 122 = -51 → 256 -51 = 205 SBB: (registro/memoria) (registro/memoria/inmediato) Resta igual que SUB pero incluye en la suma la bandera de acarreo CF. Las banderas que utiliza son = CF, PF, AF, ZF, SF y OF. La resta de menor peso se realiza con SUB, y la resta de mayor peso con SBB. Sirve para realizar restas de tamaño 32 Pág. 25 bits. Utiliza dos registros, es conveniente que sean DX : AX. Si el flag de carry CF está activo decrementa una unidad al resultado final de la operación. Ejemplo: mem32a mem32b .DATA DD 316423 DD 156739 .CODE MOV AX, WORD PTR, mem32a[0] MOV DX, WORD PTR, mem32a[2] SUB AX, WORD PTR, mem32b[0] SUB DX, WORD PTR, mem32b[2] DX : AX = 316423 - 156739 159684 DEC: (registro/memoria) Las banderas que usa es PF, AF, ZF, SF y OF. No modifica la bandera de acarreo ya que trata al operando como un número sin signo. Funciona como un contador no saturado (da “vueltas” al contador). NEG: (registro/memoria) Calcula el negativo de operando en complemento a dos, es decir, resta el operando de cero y devuelve el resultado en el mismo operando. Es equivalente a: NOT (registro/memoria). INC (registro/memoria). Afecta a todas las banderas de estado. 4.- Multiplicación: Las instrucciones que realizan multiplicaciones son: MUL (registro/memoria) -> Números sin signo. IMUL (registro/memoria) -> Números con signo. AX <- operando fuente * AL <- 8 bits, resultado 16 DX : AX <- operando fuente * AX <- 16 bits, resultado 32 Si la mitad superior es no cero se indica con CF y OF. Operando en 16 bits, CF = OF = 1 si la parte alta de DX ≠ 0. Ejemplo: mem16 .DATA DW -30000 .CODE ; multiplicar 8 bits sin signo. Pág. 26 MOV AL, 23 MOV BL, 24 ; carga AL 23 ; carga BL 24 MUL BL ; AX = 522, CF, OF ; multiplicar 16 bits con signo ; carga AX 50 ; DX : AX = -150000, CF, OF MOV AX, 50 IMUL, mem16 Excede AL Excede AX Multiplicar es una operación muy lenta. Muchas veces es conveniente sustituir por: Desplazamientos a la izquierda si el factor es potencia de dos. Salto condicional para evaluar si es: 0 -> sustituir por cero. 1-> sustituir por el multiplicando. 5.- División: Realizan divisiones: DIV {registro / memoria} -> números sin signo IDIV {registro / memoria} -> números con signo Operados: Dividendo -> AX o DX : AX Divisor -> operando fuente (excepto AX y/o DX) Operaciones de 16 bits entre 8 bits: AH (resto) / AL (cociente) <- AX ÷ operando fuente. Operaciones de 32 bits entre 16 bits: DX (resto) / AX (cociente) <- DX : AX ÷ operando fuente. Operaciones de 16 bits entre 16 bits: Es necesario extender el dividendo a 32 bits (con o sin signo). Si el divisor es 0 o el cociente excede el registro acumulador el procesador genera una interrupción cero (INT 0). Bajo DOS por defecto el programa termina y devuelve el control al DOS. Para solucionarlo se debe comprobar si es cero antes de operar (mandar a rutina – se pierde tiempo-) o reescribir la rutina de atención a la INT 0. Ejemplo: mem16 mem32 .DATA DW -2000 DD 500000 ; 11110100001001000002 .CODE MOV AX, 700 ; carga el dividendo Pág. 27 MOV BL, 36 DIV BL ; carga el divisor ; divide entre BL (cociente : AL = 19) (resto : AH = 16) MOV AX, WORD PTR mem32[0] ; cargamos dividendo en DX : AH MOV DX, WORD PTR mem32[2] IDIV mem16 ; cociente : AX = -250; ; resto : DX = 0; Ejemplo: mem16 mem32 .DATA DW -2000 DD 500000 .CODE MOV AX, WORD PTR mem16 CWD MOV BX, -421 IDIV BX ; divide 16 bits con signo entre 16 bt ; extendemos a DX : AX ; cociente : AX = 4 ; resto : DX = -326 Dividir es una operación muy lenta. Muchas veces es conveniente sustituir por: desplazamientos a derecha si el factor es potencia de dos, salto condicional para evaluar si el divisor es 1 que se sustituiría por el dividendo o cambiar divisiones por multiplicaciones: numero / 5 es lo mismo que el número + 0.2. No siempre se puede hacer. Hay que saber el divisor en tiempo de ensamblado. 6.- Operaciones en BCD: Hay dos tipos de instrucciones. Una de ellas son las de ajuste ASCII que es el BCD desempaquetado y las operaciones que se pueden realizar son la suma, la resta, la multiplicación y la división. El otro tipo de instrucciones son las de ajuste decimal, son el BCD empaquetado, utiliza la suma y la resta. El BCD son números decimales codificados en binario. Ejemplo: 109 ‘9’ BCD -> 1001 ‘0’ BCD -> 0000 ‘1’ BCD -> 0001 En binario no tiene por qué ser así la codificación. Trabaja con 4 bits, por tanto en 1 byte se pueden escribir dos dígitos BCD. Cuando se aprovechan los dos dígitos se llama empaquetado. Si no, entonces en el primer dígito se pone un 0 y en el segundo se pone el carácter BCD, entones se llama BCD desempaquetado. El BCD se inventó para procesar los números en calculadoras pequeñas. AAA: Ajuste ASCII en suma. Ajusta el resultado después de una suma Pág. 28 Ejemplo: Resultado en AL MOV AX, 9 MOV BX, 3 ADD AL, BL AAA ; AL = 0C h ; AL = O2 h ; AH = 01 h, cf (Desempaquetado) Se activa el flag de acarreo porque al sumar dos dígitos BCD me he pasado de 9, que es el límite de representación. El ajuste ASCII se hace solo sobre resultados que estén el AL. AAS: ajusta el resultado después de una resta. Ejemplo: MOV AX, 103 h MOV BX, 4 SUB AL, BL AAS ; AL = 0FF h (-1) ; AL = 09 h ; AH = O h ; cf AAM: ajusta el resultado después de una multiplicación. Ejemplo: MOV AX, 903 h MUL AH AMM ; AL = 1B h ; AL = 02 h ; AH = 07 h No usar IMUL AAD: ajusta el resultado antes de una división. Ejemplo: MOV AX, 205 h MOV BL, 2 AAD DIV BL AAM ; dividendo BCD desempaquetado ; divisor ; AX = 19 h ; AL = 0C h ; AH = 01 h ; AX = 0102h ; resto perdido Con BCD empaquetado: DAA: ajusta el resultado después de una suma. Pág. 29 Ejemplo: MOV AX, 8833 h ADD AL, AH DAA ; AL = 0BB h ; 121 -> CF = 1 ; AL = 21 h DAS: ajusta el resultado después de una resta. Ejemplo: MOV AX, 3883 h SUB AL, AH DAS ; AL = 04B h ; 45 -> CF = 0 ; AL = 45 h Pág. 30 INTRUCCIONES DE PROCESO (LÓGICAS): Índice: 1.- Generalidades. 2.- Operaciones lógicas. 3.- Desplazamientos y rotaciones. 1.- Generalidades: El primer operando es el destino de la operación realizada reemplazando su valor inicial por el resultado. Las instrucciones lógicas alteran el registro de estado. No se pueden realizar operaciones donde ambos operandos residan en memoria. Las instrucciones lógicas realizan operaciones booleanas sobre bits individuales. Cada resultado en el bit i-ésimo solo depende de los bits i-ésimos de los operandos de entrada que son los más rápidos. No conllevan propagaciones de acarreos, etc., no son funciones del peso (≠ f(peso)). El repertorio del 80x86 da soporte a las operaciones lógicas AND, OR, XOR entre dos operandos (diádicas) y a la NOT de un operando (monárica). Se suelen usar combinando un operando con una “máscara”. La máscara tiene diferentes formas dependiendo de la operación, se utiliza para modificar o extraer información de unos bits y obviar la de otras. Las instrucciones no han de confundirse con los operadores, las primeras operan en tiempo de ejecución, las segundas son órdenes para el ensamblador, se distinguen por el contexto. 2.- Operaciones Lógicas: AND (registro/memoria) (registro/memoria/inmediato) Realiza la operación lógica AND. Se puede usar para poner a cero un bit independientemente de su valor actual, la máscara contendrá un 0 allá donde queramos colocar un cero y un 1 donde queramos dejar intacto el bit original. Ejemplo: Convertir un character a mayúsculas y comprobar si es Y MOV AX, 035h AND AX, 0FBh AAND AX, 0FBh MOV AH, 7 INT 21 h AND AL, 1101 1111b CMP AL, ‘Y’ JE YES YES: . . . ; 0011 0101 ; 1111 1011 ; 0011 0001 ← FBh es una mascara, pone a 0 el bit ; 0011 0001 ; 1111 1000 ; 0011 0000 ; Servicio 7 de INT 21 h ; entrada carácter sin eco ; convierte a mayúsculas. ; ¿es ‘Y’? ; si es ‘Y’ salta a la rutina ; si no es ‘Y’ continúa ; rutina Pág. 31 OR (registro/memoria) (registro/memoria/inmediato) Realiza la operación lógica OR. Se puede usar para poner a 1 un bit independientemente de su valor actual. La máscara contendrá un 1 allá donde queramos colocar un 1 y un 0 donde queramos dejar intacto el bit original. También se usa para comparar con 0. Ejemplo: MOV AX, O35 h OR AX, 08h OR AX, 07h OR BX, BX = JG JL CMP BX, 0 ; 0011 0101 ; or 0000 1000 ; 0011 1101 ; 0011 1101 ; or 0000 0111 ; 0011 1111 ; ¿es BX = 0? ; ocupa dos bytes, tarda 2 ciclos ; si BX positivo ; si BX negativo ; si BX cero ; ocupa tres bytes, tarda tres ciclos Es más sencillo hacer OR que hacer la comparación. XOR (registro/memoria) (registro/memoria/inmediato) Realiza la operación lógica XOR. Se puede usar para conmutar el valor de bits específico. La máscara tendrá un 1 allá donde quieras conmutar. También se usa para poner a cero un registro. Ejemplo: MOV AX, 035 h XOR AX, 08h ; 0011 0101 ; xor 0000 1000 ; 0011 1101 ; ; 0011 1101 XOR AX, 07 ; xor 0000 0111 ; 0011 1010 XOR CX, CX ; ocupa dos bytes, tarda 3 ciclos ; actualiza el estado MOV CX, 0 ; ocupa 3 bytes, tarda 4 ciclos. ; no actualiza el estado SUB CX, CX ; ocupa 2 bytes tarda 3 ciclos ; actualiza el estado. Normalmente se usa XOR aunque es igual que SUB. NOT (registro/memoria) Complementa todos los bits del operador. Un uso típico es invertir el significado de una máscara. Pág. 32 Ejemplo: mascara .DATA DB 0001 0000 b . CODE MOV AX, 0D743 OR AL, mascara NOT mascara AND AH, mascara ; AL = 0100 0011 ; or 0001 0000 ; 0101 0011 ; invierte el significado ; AH = 1100 0111 ; and 1110 1111 ; 1100 0111 Si el destino es de tipo byte: destino = FFh – destino Si el destino es de tipo palabra: destino = FFFFh – destino Ejemplo: AL = F2h = 1111 00102 NOT AL ; AL = ODh = 0000 11012 3.- Desplazamientos y rotaciones: Conjunto de operaciones para desplazar y rotar bits a derecha e izquierda. Algunas conservan el signo si es necesario. Pasan por el acarreo. El operando fuente es el contador, es decir, es el número de bits que se van a desplazar. El 1 es privilegiado, se hace en menos tiempo porque la máquina solo sabe desplazar de uno en uno. * Desplazamientos: SHL {registro/memoria}, {CL/1}; Desplazamiento a la izquierda. 7 0 CF ← ← 0 Shift Left SHR {registro/memoria}, {CL/1}; Desplazamiento a la derecha. 7 0 0 → → CF Shift Right SAL {registro/memoria}, {CL/1}; Desplazamiento aritmético a la izquierda. 7 0 CF ← ← 0 Shift Arithmetic Left SAR {registro/memoria}, {CL/1}; Desplazamiento aritmético a la derecha. Respeta el signo, al copiar el 7º bit sobre sí mismo. 7 0 Shift Arithmetic → CF Right Pág. 33 SHL y SAL son la misma operación: desplazar los bits del operando destino a la izquierda el número de veces indicado en el operando fuente. Los bits de la derecha se rellenan con ceros. Si el número de bits a desplazar es 1, se puede especificar directamente, en caso contrario, su valor se guarda en CL. * Rotaciones: ROL {registro/memoria}, {CL/1}; Rotación a la izquierda. 7 0 CF ← ROR {registro/memoria}, {CL/1}; Rotación a la derecha. 7 0 → CF RCL {registro/memoria}, {CL/1}; Rotación a través del carry a la izquierda. 7 0 ← CF ← RCR {registro/memoria}, {CL/1}; Rotación a la derecha a través del carry. 7 0 → CF → * Multiplicación y división por constantes: Desplazar un bit a la derecha es igual a dividir entre dos, desplazar dos bits es dividir entre cuatro, etc… Desplazar un bit a la izquierda es igual a multiplicar por dos, etc… Este hecho se puede aprovechar para hacer algunas operaciones más rápidas (en un factor de 10 incluso). SHR divide número sin signo. SAR divide números con signo. DIV redondea por defecto. IDIV redondea por exceso. SHL y SAL funcionan igual para números con y sin signo. Ejemplo: XOR AH, AH SHL AX, 1 MOV BL, 2 MUL BL ; 0 en AH ; multiplico por 2 ; 4 ciclos en 8086 ; ; de 74 a 81 ciclos en 8086 ; 15 ciclos en 80286 ; de 11 a 16 ciclos en 80386. Pág. 34 Ejemplos con MACROS: Inicio de la Macro Multiplicar x 10 Nombre de la Macro Fin de la Macro Parámetro MACRO factor MOV AX, factor SHL AH, 1 MOV BX, AX SHL AX, 1 SHL AX, 1 ADD AX, BX ENDM ; factor es el nº por el que multiplico ; factor es un nº sin signo ; multiplico por 2 ; salvo el resultado en BX ; 2º desplazamiento (x4) ; multiplico por 8 ; factor x 8 + factor x 2 = factor x 10 Factor es un número sin signo. La macro no es un procedimiento, es una fracción de código que se copia cuando se la llama en el programa. Ejemplos con MACROS: Dividir entre 512 MACRO dividendo MOV AX, dividendo SHR AX, 1 XCHG AL, AH CBW ; dividiendo = parámetro ; salvo dividendo ; AX / 2 ; = a desplazar 8 ; (AX/2)/256 = AX / 512 ; pongo a 0 el byte AH ; seguro que es 0 ya que ; divido entre 512 ENDM Dividendo es un número sin signo. Desplazamientos multipalabra: cuando la variable a desplazar es demasiado grande para un registro hay que usar varias ubicaciones pasando a través del flag de acarreo CF. Ejemplo: . DATA mem32 DD 500.000 .CODE MOV CX, 4 nuevo: ; vamos a dividir 32 bits sin signo entre 16 bits ; 4 iteraciones en el bucle ; dividir entre 2 en cada pasada SHR WORD PTR mem32[2],1 RCR WORD PTR mem32[0],1 LOOP nuevo ; desplazo a través de CF ; el CF entra por la izquierda Pág. 35 INSTRUCCIONES DE CONTROL DE FLUJO 80x86 ÍNDICE: 1.- Bifurcaciones. 2.- Bucles 3.- Procedimientos 4.- Interrupciones. 1.- Bifurcaciones: Son el método más directo para cambiar el flujo de control de una posición a otro del programa. Internamente actúan sobre el IP (y CS veces). Los saltos pueden ser: SHORT -> 128 bytes. NEAR -> 32 Kbytes. FAR -> otro segmento (> 32 Kbytes) La actualización del puntero IP se realiza: SHORT -> IP = IP + desplazamiento (byte). NEAR -> IP = IP + desplazamiento (2 byte). FAR -> IP = desplazamiento, CS = segmento. Dos modos: Directo: etiqueta de memoria resuelve en tiempo de compilación o ensamblado. Indirecto: se da un puntero que tiene la dirección de salto (se resuelve en tiempo de ejecución). JMP: (registro/memoria) Transfiere el control incondicionalmente al operando. La bifurcación puede ser: Dentro del mismo segmento: En este caso, IP se sustituye por el valor del desplazamiento de la instrucción referenciada. El desplazamiento, a su vez, puede estar: Entre -128 y 127: Se genera una instrucción de 2 bytes. Inferior a -128 ó superior a 127: Se genera una instrucción de 3 bytes. A distinto segmento: CS e IP se sustituyen por los valores correspondientes a la instrucción referenciada. Directa: especificando una etiqueta. Indirecta: Especificando una dirección. Los saltos pueden ser: Short: IP = IP + desplazamiento (byte) Near: IP = IP + desplazamiento (2 bytes) Far: IP = desplazamiento, CS = segmento Pág. 36 Dos modos: Directo: etiqueta de memoria resuelta en tiempo de compilación o ensamblado. (Se reserva memoria). Indirecto: Se da un puntero que tiene la dirección de salto (se resuelve en tiempo de ejecución). No podemos dar saltos cortos con el modo indirecto. Tamaño del formato (modo directo) SHORT -> 2 bytes NEAR -> 3 bytes FAR -> 5 bytes 11101011 (EBh) desplazamiento_8 11101001 (E9h) desplazamiento_16 11101010 (EAh) desplazamiento_16 segmento Tamaño del formato (modo indirecto) SHORT ->no existe NEAR -> 2 bytes FAR -> 2 bytes 11111111 (FFh) mod 100 r/m 11111111 (FFh) mod 100 r/m Cuando el salto es FAR el operando no puede ser un registro ya que no caben los 32 bits de base : desplazamiento. El modo indirecto se emplea para convertir saltos incondicionales en condicionales. Ejemplo: JMP despl. 8 15 ciclos psw O D I T S Z A P C Data Memory AX BX CX DX SP BP SI DI PC Program (Relative to the Memory CS Register ppppm EB mm mm nn nn CS DS SS ES Sign extend KK to KKKK ppppm + 1 ppppm + 2 ppppm + 3 0KKKK 0mmmm + 2 0rrrr + Ejemplo: KK 0mmmm nnnn0 ppppm Pág. 37 JMP despl. 16 15 ciclos JMP dirección 15 ciclos JMP BX 11 ciclos JMP [BX] 16 + cálculo EA ciclos JMP far ptr [DI] 24 + calculo EA ciclos. EA = dirección efectiva. Ejemplo: Captura tecla Validación Es 0 SI Rutina 0 NO Es 1 SI Rutina 1 NO Es 2 SI NO NO Programa en C: Switch (tecla) { case 0 /* rutina 0 */ Break; Rutina 2 Pág. 38 case 1 /* rutina 1 */ Break; Case 2 /* rutina 2 */ Break; En ensamblador: .CODE ….. JMP switch LABEL WORD DW (caso 0) DW (caso 1) DW (caso 2) MOV AH, O8h INT 21h CBW MOV BX, AX Casos switch ; saltar datos guarda en las etiqueta las ; puntero casos[0] posiciones de ; puntero casos[1] mem. en las que ; puntero casos[2] se encuentran ; captura tecla los apartados de código con el ; AL -> AX mismo nombre ; salvo tecla ; VALIDACIÓN ;comprueba que sea 0, 1 ó 2 SHL BX, 1 JMP casos [BX] ; multiplico por 2 ; salto indirecto al caso Caso 0 ….. ….. JMP seguir …... ….. ;break Caso 1 JMP seguir …... ….. ;break Caso 2 JMP seguir ….. ….. ;break Seguir ; fin switch. Referencias a etiquetas adelantadas: Ejemplo: Etiqueta JMP etiqueta ….. ….. ….. Pág. 39 El ensamblador procesa los ficheros fuente secuencialmente (en varias pasadas). Cuando encuentra “etiqueta” no sabe a que distancia se encuentra (no puede determinar que espacio reservar para el desplazamiento). Reserva por defecto para una palabra (salto near) y espera a la segunda pasada para colocar el valor. El código resultante puede ser incorrecto (si far) o ineficiente (si short). Solución -> marcar la longitud del salto como short(si acierta el programador se emite un mensaje de error o de aviso). Si al ejecutar funciona, el salto era short; si da error, habrá que cambiar el código y poner el salto como far. Saltos condicionales: JCC desplazamiento. Salta o no en función de la condición. Primero evalúa la condición y segundo actualiza o no IP. CC = true -> salta CC = false -> no salta El desplazamiento se da como una etiqueta que se resuelve en tiempo de compilación. Los saltos condicionales siempre son SHORT ( 128 bytes); si queremos saltar mas lejos hemos de usar JMP. Ejemplo: Construcción de un salto condicional mayor de 128 bytes. Cerca: Lejos: CMP AX, 7 JE cerca ….. ….. ….. …... JMP lejos ….. ….. ….. ; código si AX = 7 ; salto -> 128 bytes ; Código si AX = 7 Las condiciones se expresan sobre el registro de estado excepto en el caso de JCXZ que se hace sobre el registro contador CX (buscando un cero). Los mnemónicos son muy variados pero la funcionalidad se repite: Mnemónicos basados en flags y mnemónicos basados en comparaciones previas. Mnemónicos basados en flags: INSTRUCCIONES: FUNCIONES: JO salta con overflow JNO salta sin overflow Salta si OF = 1 Salta si OF = 0 Pág. 40 JC salta si carry JNC JZ salta si cero JNZ JS JNS JP JNP JPE JPO JCXZ Salta si CF = 1 (= JB) Salta si CF = 0 (= JAE) Salta si ZF = 1 (= JE) Salta si ZF = 0 (= JNE) Salta si SF = 1 Salta si SF =0 Salta si PF = 1 (= JPE) Salta si PF = 0 (= JPO) Salta si PF = 1 (paridad par) Salta si PF = 0 (paridad impar) Salta si CX = 0 Ejemplos: ADD AX, BX JO desbordamiento ….. ….. ….. Desbordamiento SUB AX, DX JNZ seguir CALL caso_cero ….. ….. Seguir: ….. Mnemónicos basados en comparaciones: Se usan después de una comparación. CMP = SUB sin salvar el resultado. TEST = AND sin salvar el resultado. CMP (registro/memoria) (registro/memoria/inmediato) Compara dos operandos. Resta fuente de destino, pero no almacena el resultado. Los operadores quedan inalterados, pero las banderas se actualizan, pudiéndose consultar a continuación por una instrucción de bifurcación condicional. TEST (registro/memoria) (registro/memoria/inmediato) Comparación lógica. Como AND, pero no almacena el resultado en destino. Mnemónicos basados en comparaciones: LETRA: SIGNIFICADO: G L A B E N Mayor que (número sin signo) Menor que (número sin signo) Por encima (número con signo) Por debajo (número con signo) Igual No Pág. 41 IF (valor 1) operador_relacional (valor 2) THEN GOTO etiqueta si true CMP valor 1, valor 2 Jrelación etiqueta (que corresponde al sí verdadero) Igual = CMP signo JE Distinto ≠ JNE Condición Salta si ZF = 1 CMP sin signo JE JNE Salta si: ZF = 1 Mayor que > JG o JNLE Menor o igual ≤ JLE o JNG ZF = 0 ZF = 0 and SF = OF ZF = 1 SF ≠ OF JA o JNBE JBE o JNA ZF = 0 CF = 0 and ZF = 0 CF = 1 o ZF = 1 Menor que < JL o JNGE SF ≠ OF JB o JNAE CF = 1 Mayor o igual ≥ JGE o JNL SF = OF JAE o JNB CF = 0 Ejemplos: Las comparaciones hacen referencial con “if … else” Si CX es menor que -20 entonces DX = 30 sino DX = 20. Menor: Seguir: CMP CX, -20 JL menor MOV DX, 20 JMP seguir MOV DX, 30 ….. ; if ; then ; else Si CX es mayor o igual que -20 entonces DX = 20 sino DX = 30. Nomenor Seguir CMP CX, -20 JNL nomenor MOV DX, 30 JMP seguir MOV DX, 20 ….. ; if ; else ; then DX = 20 a no ser que CX sea menor que -20 en cuyo caso DX = 30. Mayor que MOV DX, 20 CMP CX, -20 JGE mayor que MOV DX, 30 …. ; then ; if ; else Pág. 42 Este código es el más eficiente, evita el JMP. Ejemplos: Bits .DATA DB ? .CODE ….. ….. ; si bit 2 ó bit 4 están a 1 entonces ejecutar el procedimiento A. TEST bits, 10100b JZ seguir CALL procA Seguir: ; si bit 2 y bit 4 están a 0 entonces ejecutar el procedimiento B TEST bits, 10100b JNZ continuar CALL procB Continuar: 2.- Bucles: Instrucciones para crear bucles: Bucles por contador: LOOP etiqueta, es equivalente al for. Bucles por condición: LOOPE / LOOPZ etiqueta. LOOPNE / LOOPNZ etiqueta. Es equivalente al while. Otros (por ejemplo condición al principio en lugar de al final) JCXZ etiqueta. Funcionalidad: Bucles por contador: LOOP etiqueta Contador = contador – 1 IF contador ≠ 0 THEN GOTO etiqueta LOOPE / LOOPZ etiqueta Contador = contador -1 IF contador ≠ 0 AND ZF = 1 THEN GOTO etiqueta LOOPNE / LOOPNZ etiqueta Contador = contador -1 IF contador ≠ 0 AND ZF = 0 THEN GOTO etiqueta Pág. 43 Operandos: Actualiza el contador CX automáticamente (decrementa en 1). LOOP etiqueta → CX, salto short. LOOPE / LOOPZ etiqueta → CX, ZF, salto short LOOPNE / LOOPNZ etiqueta → CX, ZF, salto short. No actualiza el contador CX automáticamente: JCXZ etiqueta → CX salto short. Ejemplo: Siguiente MOV CX, 200 ….. ….. ….. LOOP siguiente Siguiente MOV CX, 200 ….. ….. ….. DEC CX redundante CMP, 0 * JNE siguiente. No actualiza el estado El primero es más compacto pero el segundo es necesario para comprobar varias condiciones. Hoy se usa mas el segundo ya que evita el uso dedicado de operando (mas paralelismo potencial). * Redundante en código externo, internamente DEC CX no afecta a los flags. Ejemplo: MOV CX, 200 ….. ….. ….. ; Op. Aritmética* DEC CX ** JNZ salir ;depende de la op. CMP CX, 0 JNE siguiente Salir: ….. * Operación aritmética o lógica o comparación (en general cualquiera que altere el flag de cero) Siguiente MOV CX, 200 ….. ….. ….. LOOPE siguiente ** Internamente no altera el estado. Siguiente Pág. 44 Precauciones: Si CX es variable hay que comprobar primero que no es vero ya que es en ese caso se decrementará a -1 y hará 65535 iteraciones. Repetir: Seguir JCXZ seguir ….. ….. LOOP repetir ….. ; while AX ≠ 128 do tarea MOV CX, 0FFFFh ….. CMP AX, 128 LOOPNE repetir. Repetir ; CX cargado previamente ; comprobar si es 0 ; tarea a realizar ; salto del bucle. ; si se le asigna el máximo valor ; al contador, éste no interfiere. Tiempos: LOOP: Se hace el salto -> 17 ciclos. No se hace el salto -> 5 ciclos. LOOPE: Se hace el salto -> 18 ciclos. No se hace el salto -> 6 ciclos. LOOPNE: Se toma el salto -> 19 ciclos. No se toma el salto -> 5 ciclos. 3.- Procedimientos: Fracción de código que realiza una tarea especifica. Se escribe una vez y se llama siempre que haga falta. Forma parte de la imagen del ejecutable. Facilita la modularidad del código. Algunos compiladores detectan cuantas veces se llama y si son pocas sustituyen por el código normal para evitar saltos y pasos de parámetros. No es lo mismo que un objeto. Éste se instancia en memoria cada vez que se utilice (carga) y se saca de ella cuando se descarga. Los procedimientos permanecen en el área de código (forman parte de la imagen del ejecutable). Instrucciones que se relacionan con los procedimientos: CALL -> salva el contexto (CS : IP o IP en curso) en la pila y salta al procedimiento. RET -> recupera el contexto (información relativa al entorno de procesamiento principal) y vuelve al programa principal. Pág. 45 Las directivas que utiliza son: PROC ENDP Marcan el principio y el fin de los procedimientos. CALL: (registro/memoria) Salva en la pila la dirección de la siguiente instrucción. Salta a la dirección especificada. De tipo far [SP = SP - 2] [CS -> PILA] [CS = nuevo CS] Método para conseguir el CS : IP en curso de la pila. SP = SP – 2 De tipo near IP -> pila IP = nuevo IP Se salta a la instrucción siguiente (así no altera nada) La dirección de salto se puede dar de forma dos formas: Directa -> etiqueta Indirecta -> puntero en registro en memoria. Los saltos pueden ser: Near -> solo se da y se salva en la pila IP Far -> la dirección de salto se muestra como CS : IP. Ejemplo: CALL far ptr TAREA -> salto far Problemática de las etiquetas adelantadas. Pág. 46 Definición de procedimientos: 1.etiqueta etiqueta PROC [NEAR / FAR] ….. ….. ; código ….. RET [constante] ;RET= return ENDP. Forma más antigua de declarar un procedimiento 2.etiqueta: ….. ….. RETN [constante] ; código Retorno de tipo near. Aquí hay dos puntos tras la etiqueta, como si fuera un salto. Forma Moderna. 3.etiqueta: LABEL FAR ….. ….. RETF [constante] ; código Retorno de tipo far. Ejemplos: CALL ….. ….. Tarea PROC ….. ….. RET * Tarea ENDP tarea CALL NEAR PTR tarea ..... ….. tarea: ….. ….. RETN** * Asume el tipo de salto del CALL ** Supone un CALL near, si fuera far provocaría fallos. RET [constante] Devuelve el contexto original de la secuencia de código en curso sacando de la pila CS : IP o IP dependiendo de si es far o near. Pág. 47 Como operando relacional tenemos una constante cuyo significado es el número de bytes a sumar a SP después de retornar. RET (far) POP IP, SP + 2 POP CS, SP + 2 RET (near) POP IP, SP + 2 RETN 3 POP IP, SP + 2 SP + 3 Se suman 3 bytes adicionales a la cima de la pila. (Nota: POP IP y POP CS son operaciones que no estás permitidas). Paso de argumentos: Hay dos posibles maneras: en registros o variables (memoria) -> variables globales. Inconvenientes: pocos registros, no reentrantes o anidables. Ventajas: los registros son rápidos, generan pocas dependencias. La otra posibilidad es a través de la pila (Mejor método). Inconvenientes: sobrecarga de dependencias y cálculo de direcciones, acceso lento a memoria. Ventajas: anidable, versátil y gran extensión. Convenciones de Microsoft: los argumentos se pasan a la pila antes de la llamada al procedimiento. Después de la llamada, el procedimiento recupera los argumentos y los procesa. Finalmente se ajusta el puntero de pila para soslayar los argumentos, se puede hacer en la secuencia principal o con RET n. Ejemplo: MOV AX, 10 PUSH AX PUSH arg2 PUSH CX CALL sumar ADD SP, 6 Sumar PROC NEAR PUSH BP MOV BP, SP MOV AX, [BP + 4] ADD AX, [BP + 6] ADD AX, [BP + 8] POP BP RET Sumar ENDP ; doy valor al tercer parámetro ; paso el tercer parámetro a la pila ; paso el segundo (de memoria) ; paso el primero ; llamo al procedimiento ; ajusto la pila “destruyendo” los argumentos ; salvo el marco de la pila antigua ; actualizo el nuevo marco ( para este procedimiento). ; recupera el primer argumento ; sumo el primero con el segundo ; sumo con el tercero ; recupero el marco antiguo ; devuelvo el control al programa principal ; el resultado se devuelve en AX. Uso de variables locales: las variables locales tienen un tiempo de vida limitado a la ejecución del procedimiento, se reserva memoria para ellas en la pila al comenzar el procedimiento, se opera con ellas mientras se está ejecutando el procedimiento, se Pág. 48 destruyen al finalizar el procedimiento. Se crean en la pila y se acceden por su posición en la pila (respecto al macro). Ejemplo: Arg PUSH AX CALL Tarea EQU < [BP + 4] > Loc EQU < [BP - 2] > Tarea PROC NEAR PUSH BP MOV BP, SP SUB SP, 2 MOV Loc, 3 ADD AX, Loc SUB Arg, AX MOV SP, BP POP BP RET 2 Tarea ENDP ; argumento ; llamada al procedimiento ; doy nombre a la posición de la pila ocupada por ; el argumento ; idem con la variable local ; salvo el macro de la pila antigua ; cargo el macro del proceso en curso ; reservo espacio para la variable local ; inicializo la variable local ; ; ; destruyo la variable local ; recupero el macro antiguo ; devuelvo el control al programa principal ; destruyendo el argumento Posiciones Bajas Argumento SP IP Argumento SP Old BP IP Argumento SP BP+4 Old BP IP Argumento SP BP Posiciones Altas Posiciones Bajas BP-2 Loc = 3 Old BP IP Argumento SP BP Loc = 3 Old BP IP Arg – (Arg + 3) SP Loc = 3 Old BP IP Argumento SP Loc = 3 IP Argumento Posiciones Altas 4.- Interrupciones: Las interrupciones son procedimientos solicitados por números en lugar de por dirección (o ‘nombre’). Una interrupción puede dispararse por: Software: servicios BIOS, S.O., etc. Dispositivos hardware: E/S, eventos hardware. SP Pág. 49 Las interrupciones pueden tener cualquier número entre 0 y 255. Los números más bajos están reservados para el procesador, la BIOS y el sistema operativo. La llamada de interrupciones por programa se realiza con la instrucción INT n, siento n cualquier número entre 0 y 255. En la parte mas baja del mapa de memoria (00000 h) se guarda una tabla que contiene los “vectores de interrupción”. Vectores de interrupción: punteros de 32 bits (base y desplazamiento) al comienzo de las rutinas de servicio a cada interrupción (procedimientos). Los vectores se guardan secuencialmente indexados por el número de interrupción. Si cada vector ocupa 4 bytes, el vector de la interrupción n estará en la posición n x 4. En total la tabla de vectores de interrupción ocupa: 256 * 4 bytes = 1Kbyte. Es responsabilidad del programador asegurarse de que existe una rutina de servicio para cada interrupción que utilice. Para cambiar una rutina de servicio basta con dejar residente en memoria la nueva y apuntar el vector correspondiente al comienzo de ésta. Cada programa puede “atender” las instrucciones de forma diferente si así lo requiere variando el vector (el método es muy flexible). Ejemplos: INT 0 división por 0 INT 1 paso a paso Algunas interrupciones especiales: INTERRUPCIÓN 0 DESCRIPCIÓN División por cero. Solicita por el procesador cuando el cociente De una división es demasiado grande o cuando se intenta Dividir por cero. 3 Breakpoint. Punto de ruptura. Interrumpe la ejecución de un programa. Se usa para la depuración. 4 Overflow. Solicitada por INTO si OF=1. La rutina de servicio por defecto es IRET. 10 h Servicios BIOS de vídeo. 21h Llamada a servicios del sistema operativo DOS. Antes de hacer la llamada con INT 21h se carga en AH el número del servicio deseado. Los parámetros se pasan en registros. Pág. 50 INT número: Cuando se llama a una interrupción se siguen estos pasos: 1) 2) 3) 4) 5) Búsqueda del vector en la tabla PUSH flags, CS e IP TF = 0; IF = 0 Salta la rutina de atención Ejecuta el código hasta encontrar IRET-> devolvemos un proceso más pesado ya que con RET solo devolvemos CS e IP y aquí también tenemos que devolver flags. 6) POP IP, CS y flags. INT activa el procedimiento de interrupción especificado por el operando. La dirección del vector de interrupción se calcula multiplicando por 4 el operando, que es un valor entre 0 y 255. El vector de interrupción se compone de dos palabras : desplazamiento y segmento. Se bifurca a la dirección especificada por el vector de interrupción salvando previamente las banderas y la dirección de retorno (CS, IP). El código generado es de 2 bytes, excepto cuando la interrupción es de tipo 3 (parar la ejecución en un punto del programa : breakpoint) en la que el código generado es de un solo byte. Para retornar de una interrupción se utiliza la instrucción IRET. Lógica de la interrupción : SP = SP – 2 Flags -> Pila IF = 0 TF = 0 SP = SP – 2 CS-> Pila CS = (tipo_interrupción * 4) + 2 SP = SP – 2 IP -> Pila IP = (tipo_interrupción * 4) INTO Interrupción si existe desbordamiento. Es equivalente a: JO rutina … rutina: INT 4 JO rutina … rutina: ; código si OF = 1 IRET Devuelve el control al programa principal y restaura el estado. Devuelve el control a la dirección de retorno salvada en la pila por una operación de interrupción Pág. 51 previa y restaura los registros de banderas (con en la instrucción POPF). Se utiliza para finalizar un procedimiento de interrupción. Es equivalente a la instrucción RET en un procedimiento. CLI Desactiva la bandera de activación de interrupciones IF y desactiva las interrupciones enmascaradas. Las interrupciones no enmascaradas NO se pueden desactivar. IF = 0 -> no se atienden interrupciones hardware. STI Activa las interrupciones enmascaradas IF = 1 -> se atienden las interrupciones hardware. Definición de rutinas de interrupción: Etiqueta Etiqueta PROC FAR . . . IRET ENDP ;código Si la rutina no hace nada sólo tiene una instrucción que es IRET. Cambio de rutinas: Cambiar el vector -> hay llamadas al sistema para hacerlo (INT 21 h, 25h – set y INT 21h, 35h – get). Es conveniente salvar la dirección antigua y volver a colocarla cuando se finalice. Se pueden cambiar todas pero es usual hacerlo con 00h, 04h, 24h (error crítico), 23h (control-c). Los drivers de los dispositivos pueden ser rutinas de atención a interrupciones (dejar residente INT 27h). Cuando arranca el ordenador hay vectores que no apuntan a nada, es responsabilidad del programador que apunten a una rutina útil. Ejemplos: ; llamada al DOS para mostrar cadenas MOV AH, 09h MOV DX, OFFSET cadena INT 21 h ; servicio a ejecutar ; argumento ; llamada al DOS ; llamada al BIOS para leer carácter del teclado XOR AH, AH INT 16 h ; servicio a ejecutar ; llamada al BIOS ; devuelve ASCII en AL Pág. 52 Ejemplo: Cambio de vector de interrupción mensaje vector inicio: Overflow Overflow .DATA DB “desbordamiento”,13,10,”$” DD ? .CODE MOV AX, @DATA MOV DS, AX MOV AX, 3504h ;obtener vector de interrupción de la rutina en AL INT 21h ;ES:BX dirección de la rutina de interrupción. MOV WORD PTR VECTOR [2], ES ;salvar dirección de la rutina de MOV WORD PTR VECTOR [0], BX ;interrupción 4 PUSH DS MOV AX, CS MOV DS, AX ;DS:DX apunta al proc Overflow en el código. MOV DX, OFFSET Overflow MOV AX, 2504 h ;Carga el vector interrupción apuntado por DS:DX INT 21 h ;de la interrupción indicada en AL = 4 POP DS ADD AX, BX INTO ;llamar a rutina de overflow LDS DX, vector ;DS:DX = offset vector MOV AX, 2504 h INT 21h ;restaurar la rutina de la interrupción de overflow MOV AX, 4C00h INT 21h PROC FAR STI ; activa interrupciones enmascaradas MOV AH, 09h MOV DX, OFFSET mensaje INT 21 h ;imprimir por pantalla la cadena apuntada por DS:DX XOR AX, AX ; XOR DX, DX IRET ENDP END INICIO La nueva rutina de servicio o atención a la interrupción 4 emite un mensaje y pone a cero el registro desbordado. Antes de terminar el programa se restaura el vector original. Pág. 53 OTRAS INSTRUCCIONES 80x86: Índice: 1.- Procesamiento de cadenas. 1.1 Configuración 1.2 Movimiento de cadenas 1.3 Búsqueda 1.4 Comparaciones 1.5 Paso de caracteres a cadenas 1.6 Lectura de caracteres desde cadenas 1.7 E/S con cadenas 2.- Instrucciones de control. 1.- Procesamiento de cadenas: El repertorio ofrece un conjunto de instrucciones para la manipulación de cadenas de caracteres. Están pensadas para disminuir el salto existente entre los lenguajes de alto nivel y el ensamblador. También permiten disminuir el tamaño de los ejecutables. Pueden unirse sin restricciones para estructuras de datos que no sean precisamente cadenas (bytes o palabras). Las instrucciones de manejo de cadenas son: Instrucciones MOVS SCAS CMPS LODS STOS INS OUTS Descripción Copia un carácter de una cadena a otro lugar. Busca un carácter en una cadena Compara un carácter de una cadena con los de otra Carga un carácter de una cadena en Acc Transfiere un carácter de Acc a una cadena Transfiere un carácter desde un puerto E/S a una cadena Transfiere un carácter desde una cadena a un puerto de E/S Tienen una sintaxis similar. Pueden utilizar prefijos de repetición para trabajar como bucles. Así el programa será mas compacto y ocupará menos. El número de caracteres que se quieren repetir vendrá dado en CX. Los prefijos de repetición son: Pág. 54 Prefijo Descripción REP Repite un número de iteraciones especificadas en CX REPE y REPZ Repite mientras igual. CX indica el máximo número de Iteraciones. REPENE y REPNZ Repite mientras no igual. CX indica el máximo número de iteraciones. Sintaxis I: [prefijo de repetición] instrucción [ES:[destino]], [[segmento:]fuente] Las intrucciones de manejo de cadenas pueden especificar el operando fuente y/o el destino aunque no es obligatorio. El tamaño del (los) operando (s) indica el tamaño de los objetos a procesar. Si no se especifican operandos estos son los valores señalados por los punteros: DS : SI para el fuente y ES : DI para el destino. El puntero destino siempre tiene como base ES. Nomalmente se usa prefijo de repetición para completar el procesamiento de la cadena de manera iterativa (igual que un bucle). Sintaxis II: [prefijo de repetición] instrucción B [prefijo de repetición] instrucción W Las letras B o W identifican el tamaño de los operandos. Con esta sintaxis no se permite especificar operandos. Todo junto: instrucciones, prefijos y operandos: Instrucción MOVS SCAS CMPS LODS STOS INS OUTS Prefijo REP REPE/REPNE REPE/REPNE ---REP REP REP 1.1 Configuración: Fuente/Destino ambos destino ambos fuente destino destino fuente Puntero DS : SI, ES : DI ES : DI DS : SI, ES : DI DS : SI ES : DI ES : DI DS : SI Pág. 55 1. Dar el valor deseado al flag de dirección. DF = 0 las cadenas se procesan hacía arriba (de drecciones bajas a altas); instrucciones CLD. DF = 1 las cadenas se procesan hacia abajo (de direcciones altas a bajas); instrucciones STD. Si no se especifica se pondrá a cero por defecto. 2. cargar el registro contador CX con el número de caracteres a procesar (si se quiere un bucle implícito). 3. Cargar la dirección de comienzo de la cadena, fuente en DS : SI y la de la cadena destino en ES : DI. La cadena fuente puede no estar en el segmento DS. La cadena destino siempre debe estar en ES. 4. Seleccionar el prefijo de repetición adecuado (si bucle) 5. Seleccionar la instrucción deseada y colocarla junto al prefijo (si bucle). 1.2 Movimiento de cadenas: MOVS copia datos de un área de memoria a otra. [REP] MOVS [ES:] destino, [registro de segmento: ] fuente [REP] MOVSB [REP] MOVSW Transfiere un byte o una palabra de la cena fuente (direccionada por SI), en el segmento de datos, a la cadena destino (direccionada por DI), en el segmento extra. Actualiza los registros SI y DI para que apunten al siguiente elemento de la cadena. Si los operadores son de tipo byte, se transfiere un byte y los registros SI y DI cambian una unidad; si son de tipo word cambian dos unidades. Si la bandera de dirección DF = 0, SI y DI se incrementan; si DF=1 se decrementan. Ejemplo: fuente destino .MODEL Small .DATA DB 10 DUP (‘0123456789’) DB 100 DUP (?) .CODE MOV AX, @DATA MOV DS, AX MOV ES, AX . . CLD MOV CX, 100 MOV SI, OFFSET fuente MOV DI, OFFSET destino REP MOVSB -------------------. . ; solapa los 2 ; segmentos de datos. Pág. 56 Repetir: MOV CX, 100 MOV SI, OFFSET fuente MOV DI, OFFSET destino MOV AL, [SI] MOV [DI], AL INC SI INC DI LOOP repetir Este último ocupa mas en memoria y e sequivalente al anterior. A veces es mas rápido mover palabras en lugar de bytes: ¡OJO! Con números impares de bytes (no completa palabra). La solución es: MOV CX, contador SHR CX, 1 REP MOVSW RCL CX, 1 REP MOVSB ; divide entre 2 -> CF = 1 si es impar. ; copiamos palabras (más eficiente) ; si CF = (impar) -> CX = 1 ; copia el último byte si lo hay. 1.3 Búsqueda: SCAS explora una cadena buscando un carácter específico. El carácter o valor se almacena en AL o AX. Cada vez que se encuentra un emparejamiento el flag de cero se pone a 1 (IF [ES : DI] = Acc THEN ZF = 1). Solo tiene sentido utilizr los prefijos que evalúan ZF. [REPE / REPNE] SCAS [ES:] destino [REPE / REPNE] SCASB [REPE / REPNE] SCASW Ejemplo: cadena longitud puntero noesta: .DATA DB “Más vale pájaro en mano que ciento volando ” EQU $-cadena ; longitud cadena DD cadena .CODE . . CLD ; bandera de dirección a 0 (hacia arriba) MOV CX, longitud ; longitud de la cadena al contador LES DI, puntero ; inicializo ES : DI MOV AL, ‘v’ ; valor a buscar (ASCII de la ‘v’) REPNE SCASB ; repetir mientras no sea igual JNZ muestra ; tratar el caso de que no esté (si ZF = 0) . ; no se ha encontrado salto a la etiqueta . ; noesta Pág. 57 . . ; para cuando encuentra ‘v’ 1.4. Comparaciones: CMPS compara dos cadenas y apunta a la dirección donde swe produce un emparejamiento o un desemparejamiento. Compara carácter a carácter y si los valores son iguales ZF = 1. Solo tiene sentido utilizar los prefijos que evaluan ZF. Los punteros SI y DI apuntan al siguiente carácter. Si finaliza la cuenta sin emparejamientos con REPNE CMPS entonces ZF = 0. Si finaliza la cuenta sin emparejamientos con REPE CMPS entonces ZF = 1. [REPE / REPNE] CMPS [registro de segmento : fuente], [ES:] destino [REPE / REPNE] CMPSB [REPE / REPNE] CMPSW Es la única insrucción que escribe los operandos al revés (aunque no tiene mayor importancia). Ejemplo: cadena1 cadena2 longitud todos=: .MODEL large .DATA DB “Más vale pájaro en mano que ciento volando” . FARDATA ; declaración de un segmento extra. DB “ Más vale pájaro en mano que ciento saltando ” EQU $ - cadena2 .CODE MOV AX, @DATA ; vargo los segmentos. MOV DS, AX MOV AX, @FARDATA MOV ES : AX . . CLD ; bandera de dirección a cero MOV CX, longitud ; longitud de la cadena al contador MOV SI, OFFSET cadena1 ; inicializo SI MOV DI, OFFSET cadena2 ; inicializo DI REPE CMPSB ; repetir mientras igual JZ todos= ; si toda la cadena es igual DEC SI ; caso primer carácter no igual DEC DI . . También JCXZ; si CX = . 0 he llegado al final sin . desemparejamiento. Pág. 58 1.5. Paso de caracteres a cadenas: STOS almacena un valor en cada posición de una cadena destino. El valor a almacenar debe estar en ACC. El prefijo a utilizar es REP. [REP] STOS [ES:] destino [REP] STOSB [REP] STOSW Ejemplo: .MODEL small . DATA DB 100 dup ? .CODE . . CLD MOV AX, ‘aa’ MOV CX, 50 MOV DI, OFFSET destino REP STOSW destino 1.6 Lectura de caracteres desde cadenas: LODS carga un valor de una cadena en el Acc. No se usa con prefijos de repetición. LODS [registro de segmento: ] fuente LODSB LODSW Ejemplo: cadena Otro: .DATA DB 0,1,2,3,4,5,6,7,8,9 .CODE . . CLD MOV CX, 10 MOV SI, OFFSET cadena MOV AH, 2 LODSB ADD AL, 48 MOV DL, AL INT 21 h LOOP otro ; servicio de la INT 21h ; sumar 30h es pasar a ASCII ; parámetro del servicio Pág. 59 1.7. Entrada/Salida con cadenas: INS lee un puerto y escribe una cadena. OUTS lee una cadena y la escribe en un puerto. El puerto se da en DX (nunca inmediato) ya que sea implícita o explícitamente. INS [ES:] destino, DX INSB INSW OUTS DX, [registro de segmento: ] fuente OUTSB OUTSW Ejemplo: contador buffer .DATA EQU 100 DB contador DUP (?) .CODE . . CLD MOV CX, contador MOV DI, OFFSET buffer MOV DX, puerto REP INSB ; transfiere la cadena desde el puerto. 2.- Instrucciones de control: De manejo de las banderas: CLC, CLD, CLI, CMC: borrar bandera de acarreo, dirección, interrupción o complementar acarreo. STC, STD, STI: poner a 1 la bandera de acarreo, dirección o interrupción. De sincronización con coprocesadores (fp, E/S). ESC: indica al coprocesador que comience una tarea, el procesador principal no se detiene. WAIT: detiene al procesador principal hasta que, por ejemplo, se finalice el trabajo del coprocesador. HLT: detiene el proceso hasta que se resetea el sistema o se recibe una NMI (interrupción no enmascarable) o una IRQ (interrupción hardware) (si permitidas). NOP: no hace nada; se pierden tres ciclos y pasa a la siguiente instrucción (en realidad es XCHG AX, AX). Se introduce un retardo. Pág. 60 PROGRAMACIÓN EN ENSAMBLADOR 80x86 Índice: 1.- Desarrollo de un programa. 2.- Organización de memoria. 3.- Entrada / Salida. 1.- Desarrollo de un programa: 2.- Organización de memoria: ROM Memoria de video RAM Aplicación Drivers de dispositivos Controladores del sistema BIOS y DOS Tabla CODE DATA HEAP 1. 2. 3. 4. Pág. 61 STACK CODE: Código del programa. DATA: Datos globales y estáticos. HEAP: Variables ubicadas dinámicamente STACK: Datos locales y argumentos de funciones. Modelos de memoria: Tiny: código y datos caben en un mismo segmento. Small: código y datos usan un segmento cada uno. Esos segmentos no tienen por qué ser disjuntos, normalmente están solapados. Médium: datos usa un segmento, pero código usa más de uno. Compact: código usa un segmento, pero datos usa más de uno (los arrays tiene que ser menores de 64 K, es decir, solo pueden ocupar un segmento). Large: código y datos usan más de un segmento (los arrays tienen que ser menores de 64K). Huge código y datos usan más de un segmento (se usa para arrays mayores de 64K). Definición de segmentos simplifica con DOSSEG: sólo es posible a partir de la versión 5.0 del programa ensamblador. Es necesario declarar un modelo de memoria a utilizar para especificar el tamaño de datos y código a usar (directiva .MODEL {modelo}). El uso de la directiva DOSSEG permite ordenar os segmentos de forma consistente. Segmentos básicos: Definición usando la directiva DOSSEG . STACK [tamaño] . DATA ? . CODE [nombre] . FARDATA [nombre] .DATA . FARDATA ? [nombre] . CONST Inicialización de los registros de segmento: los programas en código ensamblador deben inicializar los registros de segmento antes de que las instrucciones… Inicialización de CS e IP. Etiqueta “dirección de inicio” que identifica la dirección de comienzo cuando el programa se carga y marcando el final con END [dirección de inicio]. CS se inicializa a dicho valor en la imagen del ejecutable. El registro IP normalmente se inicializa a 0. No hay ninguna transferencia explícita en código (MOV CS, …<- ¡PROHIBIDO!). Cuando el S.O. carga el programa en memoria, sustituye el valor de CS por el del puntero asignado. Sólo una directiva END debe tener dirección de inicio. Inicialización de DS. Se debe inicializar con la dirección del segmento que se usará para datos. Se debe realizar en dos pasos, porque un registro de segmento no se puede cargar directamente con un inmediato. MOV AX, @DATA MOV DS, AX Pág. 62 @DATA es una constante predefinida que contiene un puntero al segmento de datos en la imagen del ejecutable. También podemos usar la directiva DGROUP MOV AX, DGROUP MOV DS, AX DGROUP Hace referencia a un grupo de segmentos que comparten la misma dirección base, es decir, están solapados. El mismo segmento de datos es accesible desde diversos segmentos con nombres distintos. Los mismos datos tienen “vistas” distintas. Inicialización de SS y SP. El registro segmento de SS se inicia automáticamente con el valor del último segmento de tipo STACK. El registro SP se inicia automáticamente al tamaño del segmento de pila. Así SS:SP apuntan inicialmente al final de la pila. La pila se puede reinicializar a mano: MOV AX, @DATA MOV SS, AX MOV SP, OFFSET STACK ; cima de la pila También podríamos usar @STACK en vez de @DATA. Inicialización de ES. No se inicializa automáticamente. Hay que indicar, primero al ensamblador y luego al procesador que las variables de tipo FAR se van a acceder utilizando por defecto el registro ES: ASSUME ES:@FARDATA ; informo al ensamblador MOV AX, @FARDATA ; prepara al procesador MOV ES, AX Si no se indica nada el procesador utiliza el registro DX. Estructura de un programa en ensamblador: TITLE Ejemplo DOSSEG .MODEL small .STACK 100h mensaje longitud Inicio: ; uso de convenciones de segmentos de Microsoft ; Definición de modelo de memoria SMALL ; Reserva de pila de 256 bytes .DATA DB “Hola a todos”, 13, 10 EQU $-mensaje .CODE MOV AX, @DATA MOV DS, AX ; mensaje a escribir ; localización del segmento de datos ; en el registro DS MOV CX, longitud ; carga la longitud del mensaje MOV DX, OFFSET mensaje ; carga la dirección del mensaje MOV AH, 40h ; carga el nº de servicio de la función DOS INT 21h ; Llama al MS-DOS para hacer E/S Pág. 63 MOV AX, 4C00 INT 21h END Inicio ; Servicio Exit (devolver el control a MSDOS) Pág. 64 3: ENTRADA / SALIDA Las operaciones de entrada/salida con dispositivos periféricos a controladores del sistema se podrían realizar en cada programa. Esta solución no genera aplicaciones portables. Sobrecarga el desarrollo de aplicaciones. Es más eficiente confiar la entrada/salida a servicios BIOS o del SO. Un ejecutable, por tanto, esta orientado: - A un procesador (código nativo) - A un modelo de memoria dado por el SO - A un procedimiento de E/S dado por el SO En definitiva: Una aplicación tiene una imagen ejecutable orientada a una plataforma por el par: PROCESADOR – SO. La herramienta de desarrollo (ensamblador o compilador) también afecta al resultado final. Las llamadas a la BIOS o al SO se pueden implementar como interrupciones: - Interrupciones de la BIOS - Interrupciones del DOS - APIs Este sistema proporciona una “máquina virtual” que trabaja de una manera normalizada. Funciona del mismo modo independientemente de la capa física. Ejemplos de llamadas BIOS: - INT 10h: Servicios de video - INT 13h: Servicios de discos - INT 14h: Servicios de comunicaciones - INT 16h: Servicios de teclado - INT 17h: Servicios del puerto de impresión Ejemplos de llamadas DOS: - INT 21h: Servicios del S.O. - INT 23h: Control+C - INT 25h: Lectura de disco - INT 33h: Servicios de Ratón - INT 67h: Servicios de memoria expandida EMS Son API’s Ejemplos llamadas API’s: Mediante interrupciones: - INT 33h: Servicios de ratón - INT 67h. Servicios de memoria expandida EMS - INT 2Fh: Servicios DPMI (DOS Protected Mode Interface) - INT 31h Mediante funciones (llamadas a procedimientos externos) Servicios de memoria extendida XMS MOV AX, 0410h - Driver XMS_CONTROL INT 2Fh MOV word ptr CS:[XMS_CONTROL], BX MOV word ptr CS:[XMS_CONTROL+2], ES - Call CS[XMS_CONTROL] con la función en AH