Lenguaje Ensamblador Unidad Temas 1 Fundamentos. 1.1 1.2 1.3 1.4 1.5 1.6 2 Elementos del lenguaje 3 Modularización Subtemas Introducción. 1.1.1 Uso y aplicaciones del lenguaje ensamblador. 1.1.2 Escalabilidad de los microprocesadores. 1.1.3 Tipos de lenguajes ensambladores. 1.1.4 Clasificación de Memorias. 1.1.5 Unidades de entrada / salida. El microprocesador. 1.2.1 Buses. 1.2.2 Registros. 1.2.3 Modos de direccionamiento. Interrupciones. 1.3.1 Hardware. 1.3.2 Software. Estructura de un programa en ensamblador. 1.4.1 Data segment. 1.4.2 Snack segment. 1.4.3 Code segment. 1.4.4 Instrucciones del programa. 1.4.5 Directivas. Procedimiento de ensamble, enlace y ejecución. Entorno de programación. 2.1 Instrucciones lineales. 2.1.1 Movimiento. 2.1.2 Pila. 2.1.3 Matemáticos. 2.1.4 Ajustes. 2.1.5 Comparación. 2.2 Saltos. 2.2.1 Incondicional. 2.2.2 Condicional. 2.3 Tipos de ciclos. 2.4 Operadores Lógicos. 2.5 Desplazamiento. 2.5.1 Lineal. 2.5.2 Circular. 2.6 Procesos de control. 2.6.1 Banderas. 2.6.2 Cadenas. 2.6.3 Carga. 3.1 Macros. 3.2 4 Programación híbrida. 4.1 4.2 4.3 4.4 4.5 3.1.1 Internas. 3.1.2 Externas. Procedimientos. 3.2.1 Internos. 3.2.2 Externos. Directivas para compilación híbrida. Funciones en ensamblador. Bloques en ensamblador. Operadores. Integrar módulos de ensamblador en lenguajes de alto nivel. Unidad1. Fundamentos. 1.1 Introducción. 1.1.1 Uso y aplicaciones del lenguaje ensamblador. Importancia del Estudio y Uso de Ensamblador Ayuda a conocer a detalle cómo trabaja un CPU Util para Electrónica y Sistemas Desarrolla Habilidades de Programación Avanzada Mejores Oportunidades de Empleo en la Industria Aplicaciones del Lenguaje Ensamblador Sistemas embebidos:impresoras, cámaras, autos, armas, juguetes, etc. Tiempo Real: en la industria y manufactura, e.g. adquisición datos, control robots Transporte: barcos, aviones, sondas espaciales, etc. Entretenimiento: Graficación, Multimedia, Cine y VideoJuegos DSP: Procesamiento de Señales, Voz e Imágenes Otras: Medicina, Aeronaútica, Armamento, etc. Uso de lenguaje ensamblador Hay algún debate encima de la utilidad del lenguaje ensamblador. En muchos casos, compiladores modernos pueden rendir lenguajes de alto nivel en el código como eso corre tan rápido como la mano que escribe en ensamblador. Sin embargo, algunos cálculos discretos aun pueden darse mas rápidamente corriendo código en ensamblador, y alguna programación de bajo nivel es simplemente mas fácil de hacer en ensamblador. Algunas tareas de sistemas-dependientes realizadas por sistemas operativos simplemente no pueden ser expresadas en lenguajes de alto nivel. Muchos compiladores también rinden lenguajes de alto nivel en ensamblador antes de compilar completamente, permitiendo visualizar el código de ensamblador para depurar [debug] y propósito de optimización. Muchos dispositivos integrados son también programados en ensamblador a exprimir la funcionalidad máxima absoluta fuera de los que es frecuente recursos computacionales muy limitados, aunque esto esta gradualmente cambiando en algunas áreas como mas chips poderosos volviendo disponible para el mismo mínimo costo. http://www.geocities.com/SiliconValley/Haven/2037/documentos/Lenguaje_Ensamblador.htm 1.1.2 Escalabilidad de los microprocesadores. Al escribir un programa para ser ejecutado por un microprocesador se tienen tres alternativas: lenguaje de máquina, lenguaje ensamblador y lenguaje de alto nivel. Sólo programas escritos en lenguaje máquina pueden ser ejecutados por el procesador. Los programas escritos en lenguaje ensamblador o en lenguaje de alto nivel tienen que ser traducidos primeramente a lenguaje máquina a fin de que puedan ser ejecutados. Escribir un programa en lenguaje ensamblador requiere de conocimientos acerca del hardware de la computadora, su conjunto de instrucciones, de sus reglas y usos. Un estudio de programación en lenguaje ensamblador puede ser una de las tareas personalmente más remuneradas y técnicamente más desafiantes que puedan emprender un ingeniero de software. El ensamblador básico es la puerta interior de la PC, el principio de lo que se puede realizar con el lenguaje ensamblador, se encuentra especialmente diseñado para proporcionar las bases teórico - práctico que le permitirán más adelante programar el hardware (vídeo, teclado, discos, puertos, etc.) reconocer y utilizar las memoria del BIOS (Sistema Básico de Entrada y Salida), aprovechar las estructuras internas del DOS (Sistema Operativo en Disco), y afirmar y dominar el uso de debugueando codeview y mucho más. El disponer de herramientas y conocimientos para combinar sabiamente los lenguajes de alto nivel en diversas configuraciones del hardware le convierten en el maestro de la máquina, el lenguaje ensamblador será un segundo lenguaje más potente ya que da al programador acceso directo a registro a memoria y a la únicas instrucciones orientadas al bit, y es con frecuencia la única solución a las tareas de programación que prolonga el alcance de la mayoría de los lenguajes de alto nivel. Un programa en lenguaje ensamblador producen un código ejecutable de forma más rápida ya que circunvala el paso de interpretar el lenguaje y el paso de compilar el lenguaje. Un programa en lenguaje ensamblador controla al microprocesador en su propio lenguaje sin la ayuda de comprobaciones del compilador. HISTORIA DE LOS MICROPROCESADORES. 1971, Intel anunció el primer microprocesador denominado 1001. Este era de 4 bits, construido con tecnología PMOS. Tenía 45 instrucciones, y ejecutaba 60,000 operaciones por segundo. 1972, Intel introdujo el microprocesador 8008, con longitud de palabra de 8 bits; implantado con tecnología PMOS. Tenía 48 instrucciones, ejecutaba 30,000 operaciones por segundo y direccionaba 16 K bytes de memoria. Requería casi de 20 circuitos de soporte. El microprocesador contenían compuertas SSI (Small Scale Integration) y MSI (Medium Scale Integration). 1974, Intel crea el microprocesador 8080, de 8 bits. El 8080 tenía 78 instrucciones, con una velocidad de operación diez veces mayor que la del 8008 y direcionaba hasta 64 Kbytes de memoria. La tecnología de fabricación usada fue la NMOS y gran parte de la lógica de soporte se incluyo en el mismo circuito de microprocesador; por lo que fue posible construir un sistema con sólo seis circuitos integrados. 1974, Motorola crea un microprocesador de 8 bits con 72 instrucciones, el 6800. Al mismo tiempo apareció una familia de circuitos periférico diseñados especialmente para conectarse al microprocesador. 1975, Mostechnology anuncio dos microprocesadores, el 6501 que era compatible pata a pata con el 6800 y el 6202, cuyo circuito integrado incluía, además de un 6501, toda la circuitería para generar la señal de reloj. Hasta entonces, la señal de reloj se había generado en circuitos externos al microprocesador. 1976, Zilog introdujo el Z-80, un microprocesador NMOS de 8 bits, requería una fuente de alimentación de 5 volts y toda la circuíteria de soporte estaba incluida en el circuito integrado. Contenía 158 instrucciones. Junto con el microprocesador Z-80 (Z80 CPU) Zilog introdujo varios circuitos periféricos, tales como el controlador de puertos en paralelos (Z80 PIO), el controlador de puertos en serie (Z80 SIO) y el circuito timer/contador (Z80 CTC). 1977, Intel anunció el microprocesador 8085, con longitud de palabra de 8 bits, este combinaba el 8080, el circuito de reloj y el controlador del sistema en un solo circuito integrado. Fabricado con tecnología NMOS, y requería un voltaje único de 5 volts. El 8085 se optimizo para que pudiera formar un sistema completo utilizando dos circuitos periféricos especial, uno de ellos con memoria RAM, puertos de Entradas y Salida y timer (8155 u 8156) y el otro con memoria ROM o EPROM y puertos (8355 u 8755). 1978, nace la tercera generación de microprocesadores cuando Intel desarrolla el 8086. Este fue un diseño más avanzado con características nuevas. Además Intel desarrollo el microprocesador 8088 como una variación al 8086. Casi al mismo tiempo apareció un primo del 8088/808; la pastilla del coprocesador matemático de números reales 8087. Este procesador de datos numéricos estaba dedicado a alta velocidad y cálculos matemáticos de alta precisión. En 1984 se desarrolla el microprocesador 8086, este es compatible con el 8088/8086, soportaba diferentes tipos de datos muy potentes como cadenas BCD y formatos en puntos flotantes. Posteriormente se introdujo el 80186, una versión altamente integrada del 8086, es un microprocesador de 16 bits. El 80286, es una versión mejorada del 8086, que contiene una unidad de administración de memoria y direcciona a una memoria de 18 Mbytes. Además la velocidad del reloj fue aumentada. Otro ingreso de Intel es el 80386, con un bus de datos externos de 32 bits del 80386, doble que el del 80286, puede direcionar 4 gigabytes de memoria. Uno de los últimos desarrollos de Intel, es el procesador Pentium II, que es el procesador más potente de la familia X86, estos cuentan con una velocidad de 233, 266 y 300 Mhz. Añadido con tecnología MMX y un caché LI de 32 K, y está compuesta por 12 sistemas MMX a 200 Mhz de II fabricantes. Cada sistema tiene por lo menos 32 MB en RAM, un disco de 2GB o superior, una unidad de CD-ROM 6X o más rápido, un sistema de sonido de 16 bits y una tarjeta gráfica super VGA. 1.1.3 Tipos de lenguajes ensambladores. Este lenguaje da la facilidad y las herramientas necesarias para tomar el control de todo lo que la PC puede realizar físicamente. Como resultado de operaciones muy básicas suministradas por el ensamblador que realiza tareas simples de transferencia de datos y operaciones lógicas, una página de códigos en lenguaje ensamblador palidece en comparacion con una página de código en lenguaje de alto nivel. Hablar del lenguaje máquina requiere comprender muchos conceptos extraños para los programadores de alto nivel, el programador de lenguaje ensamblador debe considerar la segmentación de memoria; cuando hay control directo del acceso de memoria deben ser tomadas decisiones al minuto, se debe decidir el tamaño y tipo de cada dato, muchas de estas consideraciones son únicas a la programación en lenguaje ensamblador. ENSAMBLADOR. Un ensamblador es un software que traduce un programa en memoria escrito en mnemónicos por el usuario, a lenguaje máquina que pueda ser ejecutado por el microprocesador. Al programa en mnemónicos se le llama PROGRAMA FUENTE y al programa en lenguaje máquina se le denomina PROGRAMA OBJETO. Por lo tanto, la entrada al ensamblador es un programa fuente y la salida es un programa objeto. FUNCIONES DEL ENSAMBLADOR. A demás de su tarea principal que es traducir mnemónicos a lenguaje máquina, un ensamblador generalmente realiza las siguientes funciones: o o o o o o Permite al usuario asignar nombres a localidades de memoria, constantes numéricas, dispositivo de E/S y una secuencia de instrucciones. Acepta datos o direcciones en varios sistemas numéricos (decimal y hexadecimal) y las convierte en binario. Ejecuta algunas operaciones aritméticas en expresiones como parte del proceso de ensamblador. Permite al usuario designar las áreas de memoria donde será colocados el programa a los datos en el momento de ejecución. Proporciona la información requerida para incluir otros programas o subrutinas de biblioteca dentro del programa que sé esta realizando. Permite al usuario controlar el formato del listado del programa resultante del ensamblador. El lenguaje ensamblador utiliza mnemónicos para representar los códigos de operación de las instrucciones y símbolos alfanuméricos para representar las direcciones y los datos del programa. Entonces el programa se puede escribir como: MOV AX, (150) MOV BX, AX MOV AX, (151) ADD AX, BX MOV (152), AX Las direcciones y los datos del programa anterior se expresaron directamente en hexadecimal. A continuación se muestra el mismo programa utilizando todas las propiedades del lenguaje ensamblador. MOV AX, NUM1 MOV BX, AX MOV AX, NUM2 ADD AX, B MOV SUMA, AX La ventaja de utilizar nombres simbólicos en las instrucciones, en lugar de su valor numérico, es facilitar la escritura del programa, y también simplifica la inserción y eliminación de instrucciones, haciendo más fácil el redireccionamiento. La desventaja de un programa en lenguaje ensamblador, es que requiere un programa especial llamado ENSAMBLADOR, que se encarga de traducir los mnemónicos y los símbolos alfanuméricos a lenguaje máquina. Además sigue siendo necesario un conocimiento detallado de la arquitectura del microprocesador. El lenguaje ensamblador es una variante legible para el ser humano del lenguaje máquina que usan las computadoras para ejecutar programa. Al mismo tiempo la mejor manera de comunicarse con la PC y con el lenguaje máquina de programación que utilice. El lenguaje ensamblador resulta indispensable cuando se desea escribir programas que controlen las E/S de la PC, agregar nuevas interfaces de E/S, escribir rutinas que aprovechen y maximicen el uso del hardware y en general realizar cualquier tarea que no pueden llevar acabo los demás lenguajes de programación. El lenguaje ensamblador le brinda la oportunidad de conocer más afondo la operación de su PC esto le permite implementar software o hardware de una manera más consiente. El lenguaje ensamblador hace que se conserve el control total de lo que desea hacer su PC. Una de las mayores importancias del ensamblador es que uno puede optimizar al máximo sus programas (tanto tamaño, como velocidad de ejecución). Otra importancia es la gran velocidad a la que ejecuta el código. El lenguaje ensamblador permite a los ingenieros de software hacer interfaces con el sistema operativo y les da control directo de las operaciones de entrada y salida a monitores, impresoras, y a los importantes dispositivos de memoria de disco duro/flotante. Los programadores de aplicaciones con frecuencia tienen que hacer también interfaces directamente con el sistema operativo. Estas rutinas se escriben en lenguaje ensamblador. El lenguaje ensamblador es simplemente una representación simbólica del lenguaje máquina asociado, lo cual permite una programación menos tediosa que con el anterior. Sin embargo es necesario un conocimiento de la arquitectura mecánica subyacente para realizar una programación efectiva en cualquiera de estos niveles de lenguaje. La mayoría de los ensambladores son ENSAMBLADORES DE DOS ETAPAS (two-pass Asemblers). La PRIMERA ETAPA determina la dirección de memoria en la cual es ensamblado el primer byte de cada instrucción y genera una tabla para los valores de todos los nombres simbólicos y etiquetas definidos en el programa. El ensamblador posee una tabla de códigos de operacióncon una entrada por cada código de operación, cada entrada contiene el mnemónico de la instrucción, su equivalente en lenguaje máquina y el número de bytes de la intrucción. También el ensamblador tiene un Contador de localización (Location Counter, LC), el cuál al inicio se carga con 0 ó con el valor especificado por una directiva ORG, ésta corresponde a la dirección inicial u origen del programa y se le asigna el primer byte de la primera instrucción. Si un programa tiene una etiqueta, esta se añade a la tabla de símbolos y se le asocia el valor conteniendo del LC en ese momento. El código de operación de la instrucción se extrae de la tabla de código de operación incrementando el contador de localización igual al número de bytes de la instrucción. Esta etapa termina cuando el ensamblador detecta la directiva END. En la SEGUNDA ETAPA, el ensamblador hace un recorrido por el programa, utilizando la tabla de símbolos de los códigos de operación, sustituye el mnemónico de cada instrucción por su equivalente en el lenguaje de máquina. 1.1.4 Clasificación de Memorias. Las terminales de entrada/salida de un procesador proporcionan un modo eficiente de comunicación entre el sistema central y el ambiente exterior. Los programas y los datos, deben entrarse al computador para el procesamiento y los resultados deben registrarse o exhibirse para el usuario. Para que esto suceda, la información pasa a través de la memoria central. MEMORIA CENTRAL También denominada memoria principal, es la parte de la CPU de una computadora donde están almacenadas las instrucciones y los datos necesarios para que un determinado proceso pueda ser realizado. La memoria central está constituida por multitud de celdas o posiciones de memoria, numeradas de forma consecutiva, capaz de retener mientras la computadora esta conectada. La memoria es el centro de actividad, el lugar en donde todo se mantiene cuando se está trabajando. La memoria proporciona un lugar donde pueden realizarse los cálculos, para la memoria de la computadora no hay diferencia entre programas y datos, ambos son información que debe ser registrada, almacenada o manipulada. Esta memoria consiste en una serie de microcircuitos que sirven de soporte generalmente transitorio de la información. Sus características principales son: Gran rapidez. Componentes fijos. Capacidad mediana. Reutilizable y de acceso directo. Su velocidad de proceso se mide en microsegundos (millonésima de segundo), nanosegundo (milmillonésima de segundo) e incluso picosegundos (billonésima de segundo). Es una memoria de acceso directo, puede accederse a una de sus celdas con sólo conocer su posición, para esta memoria el tiempo de acceso es más corto que las memorias auxiliares, por tanto, los datos que manejan los procesos deben residir en ella en el momento de su ejecución. La memoria central tiene asociados 2 registros para la realización de operaciones de lectura o escritura y un dispositivo encargado de seleccionar una celda de memoria en cada operación de acceso sobre la misma. REGISTRO DE DIRECCIÓN DE MEMORIA (RDM). Contiene la dirección de memoria donde se encuentra o va a ser almacenada la información (instrucción o dato), tanto si se trata de una lectura como de una escritura. REGISTRO DE INTERCAMBIO DE MEMORIA (RIM). Si se trata de una operación de lectura, el RIM es quién recibe el dato de la memoria señalado por el RDM, para su posterior envío a uno de los registros de la Unidad Aritmética y Lógica. Si se trata de una operación de escritura, la operación a grabar tiene que estar en el RIM, para que desde el se transfiera a la posición de memoria indicada por el RDM. SELECTOR DE MEMORIA (SM). Es el registro que tras una orden de lectura o escritura conecta la celda de memoria cuya dirección figura en el RDM con el RIM, posibilitando la transferencia de los datos en un sentido o en otro. La Memoria Central suele ser direccionable por octeto o Byte, por tanto; una celda o posición de memoria contiene 8 bits. Una de las características fundamentales de una computadora es su Capacidad de Memoria Interna (Memoria Central), la cual se mide en un múltiplo del Byte denominado Kilobyte, Kbyte, KB O k, y que equivale a 1024 bytes (1024 = 210). Otro múltiplo utilizado ampliamente en los últimos tiempos es le Megabyte o mega que equivale a 1024 * 1024 Bytes. Las computadoras utilizan 2 tipos de memoria interna: 1. MEMORIA DE SOLO LECTURA (READ ONLY MEMORY: ROM). Esta almacena ciertos programas e información que necesita la computadora. Estas instrucciones están grabadas permanentemente en el chip de ROM y no pueden ser modificados por el operador, por eso es de sólo lectura. Se conocen también como memoria No Volátil por que no desaparece o se borra cuando se desconecta la electricidad. Las instrucciones básicas que se necesitan para arrancar una computadora están almacenadas en ROM. Algunos programas de utilería y paquetes de software también lo están. Las posiciones altas de la memoria superior son: ROMBIOS ROM DE INICIO ROM EXTENDIDA Existen otras variedades que permiten algunas manipulaciones: PROM: Permite programarse una sola vez, una vez decididas sus características e instalada, se convierte en una ROM normal. EPROM: Puede borrarse y reprogramarse varias veces, para ello se necesitan técnicas especiales de borrado y escritura. 2. MEMORIA DE ACCESO ALEATORIO (RANDOM ACCESS MEMORY: RAM). Aquí también podemos almacenar ciertos programas para el funcionamiento de la computadora. Sin embargo, en RAM el usuario puede cambiar la información, almacenarla o borrarla. La capacidad de la RAM afecta la forma en que se corren los programas del software y la cantidad de datos que pueden procesarse. Cuando más fácil de usar sea un programa tanta más RAM necesitará generalmente. La RAM es una memoria volátil, a menos que se guarden en discos, se pierde cuando la computadora se desconecta excepto en algunos que están provistos de pilas especiales para mantener el contenido de la RAM. Cuando se crea un programa o un documento de aplicación, el programa que carga y los datos que se introducen mediante el teclado son almacenados en forma temporal en la RAM. El término aleatorio proviene de la forma en que la computadora localiza o da acceso a los datos que se encuentran en la memoria. Si se pasa por todos los datos para encontrar alguna información deseada, esto es acceso secuencial. Si se brinca todos los datos y selecciona directamente la información deseada, sin pasar por los otros datos, a esto se le llama Acceso Aleatorio o Directo. La RAM es un dispositivo de estado sólido que no tiene partes en movimiento. Se puede tener acceso a los datos de la RAM a velocidades electrónicos comparables a la velocidad de la luz. A la memoria RAM también se le llama memoria principal y es un almacenamiento temporal. Todos los programas y datos introducidos a un dispositivo de entrada (teclado) o a una memoria magnética (disco) se debe transferir a la RAM antes que se puedan ejecutar los programas o procesar los datos. La capacidad en RAM se define en función del número de caracteres que puede almacenar. Memoria Convencional. Se le denomina así a la RAM comprendida entre los 0 y 60 KB. Memoria Superior. Se le llama así al resto de la memoria RAM hasta 1 MB, es decir al espacio comprendido entre los 640 y 1.024 B, es decir a los 384 KB. Memoria Extendida. Es la superior a 1 MB, que llegará a los 16 MB, en los 286 (AT) y a4.096 MB o 4 GB, en los 386 y 486. Estas dos últimas clases de memorias RAM se subdividen en otros tipos: Memoria Expandida, Shadow y de Vídeo (la superior) y en memoria alta (extendida), dependiendo del tipo de procesador. Cada memoria está formada por bloques de octetos consecutivos, de tamaño de 16 bytes o un múltiplo de ese número (16, 32, 48, 64 octetos, etc.). SALIDA. Son los dispositivos cuya misión es la de recoger y proporcionar al exterior los datos de salida de cada uno de los trabajos que se realicen en el sistema. También se denominan Periféricos o Unidades de Salida. La computadora comunica sus resultados al usuario; por ejemplo, Desplegando la información en un monitor o imprimiéndola. Estos dispositivos incluyen: ENTRADA. Monitores Impresoras Plotters Graficadores Son los dispositivos periféricos que permiten introducir la información a la computadora para su procesamiento. Algunos de estos dispositivos son: Teclado Mouse Tabletas gráficas Lápiz óptico Entrada de Voz Pantallas sensibles al tacto Lectores ópticos ENTRADA/SALIDA Son dispositivos que efectúan tanto tareas de entrada como de salida, estos incluyen: Discos duros CD´s Discos flexibles 1.1.5 Unidades de entrada / salida. Periféricos: Unidades o dispositivos externos que se conectan a la computadora central CPU-Mem, e.g. ratón, monitor, disco-duro, CD, etc. Interfaz: Es el medio que permite conectar un periférico a la computadora, e.g. conector, puerto, tarjeta, etc. Existen diferentes tipos de interfaces: Puerto Paralelo (LPT): cable corto, grueso que transmite arriba de 16 bits, e.g. impresora, discos externos, etc. Puerto Serie (COM): cable largo, delgado que transmite 1 bit, e.g. mouse, teclado, modem, etc. Puertos USB, FireWire: tecnología veloces y compactas que posiblemente reemplacen al cable serie/paralelo Interfaz IDE, SCSI, PCI, EISA: interfaces de bajo nivel para conectar tarjetas y dispositivos internos, e.g. discos duros, CD, video, etc. http://pantera.itchihuahua.edu.mx/apacheco/expo/view. php?f=asm_11#page9 1.2 El microprocesador. Un microprocesador debe contener al menos: una unidad de control, unidad de aritmética y lógica y algunos registros. Los componentes internos de un microprocesador son: a) REGISTROS GENERALES b) REGISTROS APUNTADORES (ESP Y EBP). c) REGISTROS INDICES (ESI Y EDI). d) REGISTRO DE SEGMENTOS (SS, CS, DS, ES.) e) REGISTRO APUNTADOR DE INSTRUCCIONES (IP). f) REGISTRO DE ESTADO (BANDERAS) g) COLA DE INSTRUCCIONES h) UNIDAD DE CONTROL DE LA UNIDAD DE EJECUCIÓN i) BUSES INTERNOS j) UNIDAD ARITMETICA Y LOGICA (ALU) g) COLA DE INSTRUCCIONES Una cola cuya abreviatura es Q de "queue", es una línea de espera como la que forman en la caja del supermercado. Algunos de los microprocesadores de 16 bits como el 8086/8088 del Intel o Motorola MC 68000 utilizan tales líneas de espera para sus instrucciones. Dicho de otra manera sus instrucciones que han de ejecutarse llegan al microprocesador antes de lo necesario y "esperan" en una cola de instrucciones en este sistema, poseen la ventaja de que cada instrucción puede extraerse de memoria mientras otras se están ejecutando reduciéndose en consecuencia el tiempo de proceso, por ejemplo las instrucciones que incluyen directamente a la velocidad de aquellas otras que utilizan datos de los registros de la CPU, las colas de instrucciones son normalmente cartas de 496 octetos en concreto la del 8086 es de 6 octetos, (tres palabras y la del 8088 es de 4 octetos). La cola de instrucciones es de un área de almacenamiento de tipo PEPS (Primero en entrar, primero en salir) para instrucciones decodificadas y operandos ya disponibles. h) UNIDAD DE CONTROL DE LA UNIDAD DE EJECUCIÓN La función principal de la UC es dirigir la secuencia de pasos de modo que la computadora lleve a cabo un ciclo completo de ejecución de cada una de las intrusiones del programa. Utiliza señales de reloj, por lo que se considera que el microprocesador es un dispositivo síncrono. Su actividad es cíclica y consiste en la búsqueda y obtención de datos e instrucciones, y en la ejecución secuencial de éstas últimas. El corazón de la unidad de control lo constituye el GENERADOR DE CICLO DE LA MAQUINA (GCM), que se encarga de producir las señales de control, derivándolas de un reloj u oscilador maestro. Pasos para ejecutar una instrucción cualquiera: a) Ir a la memoria y extraer el código de la siguiente instrucción. Este paso se llama "ciclo fetch". b) Decodificar la instrucción ya leída. c) Ejecutar la instrucción. d) Prepararse para leer la siguiente casilla de memoria y continuar con el paso a). Las principales funciones de esta unidad son: a) Leer e interpretar las instrucciones del programa. b) Dirigir la operación de los elementos internos del procesador. c) Controlar el flujo de datos y programas que entran y salen de RAM. Para realizar su función consta de los siguientes elementos: Registro de control de secuencia (RCS). También denominado contador de programa (CP), contiene permanentemente la dirección de memoria de la próxima instrucción a ejecutar. Si la instrucción que se esta ejecutando en un instante determinado es de salto o de ruptura de secuencia, el RCS tomara la dirección de la instrucción que se tenga que ejecutar a continuación, esta instrucción la traerá de la propia instrucción en curso. Registro de instrucción (RI). Contiene la intrusión que se esta ejecutando a cada momento. Esta instrucción llevara consigo el código de operación (CO), ación de que se trata y en su caso los operandos o las direcciones de memoria de los mismos. Decodificador (D). Se encarga de extraer y analizar el código de operación de la instrucción en curso (que está en RI) y dar señales necesarias al resto de los elementos para su ejecución. Reloj (R). Proporciona una sucesión de impulsos eléctricos a intervalos constantes (frecuencia constante), que marca los instantes en que se han de comenzar los distintos pasos de que consta cada instrucción. Secuencia (S). En este dispositivo se encargan ordenes muy elementales (microordenes), que sincronizadas por los impulsos del reloj, hacen que se vaya ejecutando poco a poco la instrucción que esta encargada en el RI. j) UNIDAD ARITMETICA Y LOGICA (ALU) La unidad de aritmética y lógica es un circuito digital que realiza un conjunto de micro-operaciones aritméticas y lógicas. Operaciones que realiza una ALU: 1. Suma aritmética. 2. Funciones lógicas AND, OR y XOR. 3. Complemento. 4. Rotación hacia la izquierda o derecha. Esta unidad es un grupo de circuitos electrónicos encargada de realizar las operaciones elementales de tipo aritmético (+, -, *, /) y de tipo lógico (comparaciones), también hace comparaciones alfabéticas, por ejemplo; Soto, Sánchez. Para realizar su función consta de los siguientes elementos: Banco de Registros. (BR): Está constituido por 8, 16, 32 registros de tipo general que sirve para situar datos antes de cada operación, para almacenar datos intermedios en las operaciones y para operaciones internas del procesador. Circuito de Operadores. (CIROP): Compuesto de uno o varios circuitos electrónicos que realizan operaciones elementales aritméticas y lógicas (sumador, complementador, desplazador, etc.). Registro de Resultados. (RR): Se trata de un registro especial, en el que se depositan los resultados que producen los circuitos operadores. Señalizador de Estado. (SE): Registro con un conjunto de biestables en los que se deja constancia de algunas condiciones que se dieron en la última operación realizada. 1.2.1 i) Buses. BUSES INTERNOS (DATOS, DIRECCIONES). BUS. Es un canal o ruta común de comunicación entre dispositivos del hardware, ya sea internamente entre componentes del computador o externamente entre estaciones de una red de comunicaciones. Los buses se asemejan a una carretera por la que circulan los bits físicamente, los buses son varios hilos paralelos, uno para la transmisión de cada bit. Cuando la arquitectura del bus es utilizado en un computador, el procesador o procesadores, los bancos de memoria y las unidades de control periférica están todos interconectados mediante el bus. El bus está dividido en dos canales, uno para seleccionar donde esta localizado el dato (bus de direcciones) y otro para transferir el dato (bus de datos). BUS DE DATOS. Circulan los datos con los que va a trabajar el ordenador, debe estar conectado a la memoria principal, puesto que en ella se encuentran almacenados los datos, al banco de registro y por supuesto a la unidad aritmética y lógica, el bus de datos tiene 8 conectores y es capaz de transportar 8 señales en paralelo, esto significa que el bus de datos puede llevar unidades de información de 8 dígitos binarios, sólo una unidad cada vez. BUS DE DIRECCIONES. Se emplea para transmitir la información que ha de reaccionar la memoria. Si el bus de direcciones tiene 16 hilos, se podrá direccionar Z16 direcciones de memoria, es decir, se puede acceder a cualquier posición de memoria de 64 KB. Si se considera la cuestión de forma inversa se precisan dos palabras de 8 bits para conseguir una dirección de memoria. Llevar señales de control especial que provocan la selección de la información a través de la computadora. Esta información se utiliza para distinguir a la vez entre varios dispositivos de E/S y las miles de celdas de la memoria de la computadora. EL BUS DE CONTROL: Sincroniza el sentido de la transferencia de información en el bus de datos (hacia adentro o hacia afuera). Cada una de las señales del bus de control son unidireccionales. 1.2.2 Registros. a) REGISTROS GENERALES Los registros de propósito general son capaces de soportar operandos de datos de1, 8, 16 y 32 bits, estos registros también soportan operandos de direcciones de 16 y 32 bits. Los 8 registros son AX (acumulador), BX (en base), CX (contador), DX (datos), SP (puntero de pila), BP (puntero base), SI (índice fuente) y DI (índice destino). Para acceder a los 32 bits de un registro todas las referencias a registros debe comenzar con la letra "E". Cada uno de los 8 registros de propósito general pueden ser compuestos en sus equivalentes de 16 bits de 8086 bits/80286 referenciando los registros sin usar el prefijo E. REGISTRO EAX (Acumulador). Generalmente se usa para almacenar resultados de operaciones aritméticas o lógicas, lectura o escritura desde o hacia los puertos y como un área de memoria principal temporal (SCRATCH PAD). También se direcciona como AX, AH, o AL. REGISTRO EBX ( Base). Sirve como registro apuntadores base o índice. Conserva la dirección base (desplazamiento) de los datos que hay en la memoria o la dirección base de una tabla de datos. También se direcciona como BX, BH, o BL. REGISTRO ECX (Contador). Se utiliza constantemente en operaciones de interacción, como un contador que automáticamente se incrementa o decrementa de acuerdo con el tipo de cada instrucción usada. También es usado para corrimientos (CL) y rotaciones y ciclos y para las operaciones de cadena y un contador. También se direcciona como CX, CH, CL. REGISTRO EDX ( De Datos). Comúnmente se usa como fuente para el acceso de datos. También se direcciona como DX, DH y DL. b) REGISTROS APUNTADORES (ESP Y EBP). La dirección física de cualquier elemento dado en un segmento seleccionado se obtiene por la combinación de la dirección del segmento y el desplazamiento, este desplazamiento puede estar contenido en uno de los registros puntero, base o índice. Las operaciones de la pila son facilitadas por el sector del segmento de pila (SS) y el par de registros de puntero de pila (SP) o puntero base (BP). REGISTRO ESP O APUNTADOR DE PILA. Apunta a un área específica de memoria que sirve para almacenar datos bajo la estructura LIFO (LAST, IN, FRIST OUT) mejor conocido como PILA STACK. Esto ocurre cuando se ejecutan las instrucciones PUSH y POP cuando se llama (CALL) o se regresa (RET) de una subrutina de un programa principal. También se direcciona como SP. REGISTRO EBP O BASE. Se usa para manipular la pila sin afectar el registro de segmentos SS, así como para direccionar una matriz de datos en una pila de memoria. También se direcciona como BP. c) REGISTROS INDICES (ESI Y EDI). Manipulaciones de datos más complicadas pueden obtenerse utilizando los registros índice fuente (ESI) e índice destino (EDI) junto al segmento de datos actualmente activo. REGISTRO SI. Se emplea para direccionar datos de fuente en forma indirecta para utilizarlos con las instrucciones de cadenas o arreglos. REGISTRO DI. Se emplea para direccionar datos destino en forma indirecta para utilizarlos con las instrucciones de cadenas o arreglos. d) REGISTRO DE SEGMENTOS (SS, CS, DS, ES.) Un segmento es un módulo de código que puede ser accesible simultáneamente con los demás. El microprocesador 80386 y 80486 contienen 6 registros segmento de 16 bits, estos mantienen los valores del selector en las posiciones de memoria actualmente direccionables. En modo de dirección real, un segmento puede variar desde un byte hasta un tamaño de segmento máximo de 64 Kb. La figura muestra la representación gráfica de los registros de segmento. CS (código). El segmento de código es una sección de la memoria que tiene los programas y procedimientos utilizados por los programas. Este define la dirección inicial de la sección de memoria que tiene el código. El segmento de código direcciona a 4Gbytes en el 80386 y 80486. DS (datos). Es una sección de memoria que contiene la mayor parte de los datos utilizados por un programa. ES (extra o adicional). El segmento extra o adicional de datos lo utilizan algunas instruucciones para cadenas. SS (pila). El segmento de pila define la superficie de la memoria utilizada para la pila. La ubicación del punto inicial de entrada a la pila, se determina por el registro apuntador de la pila. FS y GS. Estos registros de segmento adicionales están disponibles en los microprocesadores 80386 y 80486 a fin de contar con dos segmentos adicionales de memoria para acceso con los programas. e) REGISTRO APUNTADOR DE INSTRUCCIONES (IP). El puntero de instrucciones (IP) contiene desplazamiento necesario para direccionar la siguiente instrucción que se va a ejecutar en el segmento de código actualmente activo. Para formar la localidad real de la siguiente instrucción se suma el contenido de IP con CS (por) 10H. En general un puntero de 32 bits a la siguiente instrucción secuencial del programa. REGISTRO IP (INSTRUCCIÓN POINTER). Apunta a la siguiente instrucción que será ejecutada en memoria. f) REGISTRO DE ESTADO (BANDERAS) Las banderas o señalizadores indican la dirección del microprocesador a la vez que controlan su movimiento. Los bits de banderas cambian después de ejecutar muchas de las instalaciones aritméticas y lógicas. En la figura se muestran claramente. Registro de banderas 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 AC VM RF 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 NT IOP IOP OF DF IF TF SF ZF AF PF 0 CF Las banderas indican la condición del microprocesador a la vez que controlan su miento. Los bits de bandera cambian después de ejecutar muchas de las instrucciones aritméticas y lógicas. El señalizador de arrastre (CF) se pone a 1 cuando se genera un arrastre en una operación aritmética realizada sobre un operando de 8 ó 16 bits. En cualquier otro caso, está a 0 CF también se utiliza en instrucciones de desplazamiento y rotación y contiene el bit desplazado o rotado fuera del registro. El señalizador de paridad (PF) se utiliza principalmente para aplicaciones de comunicaciones de datos y se pone a 1 para generar paridad impar a 0 para generar paridad par. El señalizador de arrastre auxiliar (AF) se utiliza en aritmética BCD para indicar si ha habido arrastre o préstamo en los dígitos correspondientes a los 4 bits menos significativos de un valor BCD. El señalizador de cero (ZF). Indica que el resultado de una operación aritmética o lógica es cero. Si ZF=1, el resultado es cero, y si ZF=0 el resultado no es cero. El señalizador de signo (SF) se pone a 1 para resultados negativos y a 0 para resultados positivos. Toma el valor que tiene el registro más significativo. El señalizador de rebose (OF) indica si una operación ha generado arrastre en el bit de orden superior del resultado pero no de un arrastre de otro bit diferente del orden superior. Tres de los once señalizadores (TF, IF y DF) se utilizan para controlar ciertas operaciones del procesador. El señalizador de trampa - TAP (TF). Cuando sé inicializa pone al microprocesador en modo de paso simple y habilita la depuración de un programa. El señalizador de habilitación de interrupción (IF) habilita interrupciones externas cuando se pone uno, y las habilita cuando se pone cero. La dirección de las operaciones con cadenas está controlada por el señalizador de dirección (DF). Con DF a cero, si y/o DI son incrementados automáticamente con DF a 1, si IO, DI son decrementados automáticamente. Los señalizadores IOP y NT son dos señalizadores nuevos no suministrados anteriormente y solamente son usados cuando el microprocesador está en modo protegido, el señalizador de niveles E/S de privilegio (10 P) se utiliza para garantizar que una instrucción realiza todas aquellas operaciones que está autorizada a realizar. El señalizador de tareas (NT) se utiliza para indicar si la ejecución de la tarea actual está anidada en otras tareas. Si NT está a uno, la tarea actual anidada tiene un enlace a la tarea previa. El señalizador RF (de resumen) se usa junto con los "break points" del registro de operación a pasos simples. Cuando RF está en uno todos los fallos de depuración se ignoran en la siguiente instrucción, entonces, RF se pondrá automáticamente a cero (RESET) cuando complete con éxito cada instrucción. El VM (señalizador modo virtual) si VM está a uno y el 80386 está en modo protegido, el microprocesador conmutará a la operación en modo virtual del 8086, haciendo que todas las operaciones de segmentos se ejecuten como si estuvieran corriendo en un 8086. Señalizador (AC) comprobación de alineación. 1.2.3 Modos de direccionamiento. TECNICAS DE DIRECCIONAMIENTO. Una instrucción del 80286/80386/80486 no solamente contiene información sobre la operación en particular a realizar, sino que también incluye las especificaciones para el tipo de operandos a manipular, así como también la posición de estos. La manera en que los operandos se escogen durante la ejecución del programa depende del modo de direccionamiento de la instrucción. El modo de direccionamiento especifica una regla para interpretar o modificar el campo de dirección de la instrucción antes de que el operando sea en realidad referenciado. Se empleará la instrucción MOV (mover datos) para describir los modos de direccionamiento de datos. En el ejemplo se ilustra la instrucción MOV y define la dirección del flujo de datos a la fuente a la derecha y el destino a la izquierda. MOV MOV destino, fuente AX, BX Modo Implicado En este modo los operandos son especificados implícitamente en la definición de la instrucción. Por ejemplo, la instrucción “complemente el acumulador” es una instrucción de modo implicado debido a que el operando en el registro acumulador está implicado en la definición de la instrucción. DIRECCIONAMIENTO DE DATO INMEDIATO El termino inmediato significa que los datos siguen inmediatamente al código hexadecimal de operación en la memoria. El direccionamiento inmediato actúa en byte o palabra de datos. A continuación se muestra el funcionamiento del direccionamiento de dato inmediato. MOV MOV MOV MOV AH,00 AL,04 AX,0FFFFFH AX,302 ;AH se pone a cero (00) ;AL se carga con 0000 0100 en binario En este modo él operando es especificado en la instrucción misma. Esto es, una instrucción de modo inmediato tiene un campo de operando en vez de un campo de dirección. En la modalidad inmediata, el operando se incluye como parte de la instrucción. Por ejemplo MOV AX, 5. Aquí el número 5 forma parte de instrucción en su totalidad, en otras palabras, el número se especifica como una constante numérica en la misma instrucción y no hay necesidad de accesar la memoria, la instrucción mueve el valor de 5 al registro AX. Ejemplos: MOV AX, 60 MOV BX, 01A MOV AX, 083 MOV CX, 2BC MOV DX, 0123 DIRECCIONAMIENTO DIRECTO Con el direccionamiento directo, el desplazamiento del segmento de operando está contenido en la instrucción como una cantidad de 16 bits. Este desplazamiento se suma al contenido desplazado del registro del segmento de datos (DS) y se devuelve a la EA de 20 bits, o dirección física real. Habitualmente, el operando de direccionamiento directo es un rótulo. Ejemplo: MOV AX, MYDATA MYDATA CONT DIR. 0005 0004 FF 0003 00 0002 0001 0000 AX 1111111100000000 En la mayor parte de las instrucciones se puede emplear el modo de direccionamiento directo de datos, el cual se aplica en muchas instrucciones en un programa típico. Hay dos formas básicas de direccionamiento directo de datos (1) direccionamiento directo y (2) direccionamiento por desplazamiento; en este caso el direccionamiento directo solo se permite con una instrucción MOV que transfiera datos entre una localidad en la memoria, situada dentro de datos y un registro AL (8 bits), AX (16 bits) o EAX (32 bit). Esta instrucción siempre tiene una longitud de 3 bytes. Ejemplo: La instrucción MOV CX, LIST copia el contenido tamaño palabra de la localidad LIST de la memoria y lo coloca en el registro CX. MOV AX, DDDW MOV BX, DDDY MOV AX, DDDU DIRECCIONAMIENTO DE REGISTRO Transfiere un byte o palabra desde el registro fuente o localidad en la memoria, hasta el registro o localidad destino en la memoria. Es fácil comprender el direccionamiento por registro una vez que se aprende los numerosos registros que hay en los 8086-080486. Los 8086-80286 contienen los siguiente registros de 8 bits, utilizando con el direccionamiento por registro AH, AL, BH, BL, CH, CL, DH, y DL. También contienen los siguientes registros de 16 bits AX, BX, CX, DX, SP, BP, SI y DI. En los 80380/80486 los registros, ampliados de 32 bits son EAX, EBX, ECX, EDX, ESP, EBP, EDI y ESI. Pero no se debe mezclar un registro de 8 bits con uno de 16 bits, uno de 8 bits con uno de 32 bits, o uno de 16 bits con uno de 32 bits porque no lo permite el conjunto de instrucciones de 8086-80486. La tabla presenta algunas versiones de instrucción MOV entre registros. Es importante mostrar todas las variables por las numerosas combinaciones posibles. Lenguaje ensamblador Operación MOV AL, BL Copia BL en AL MOV CH, CL Carga CL en CH MOV AX, CX Carga CX en AX MOV SP, BP Carga BP en SP MOV DS, AX Carga AX en DS MOV DI, SI Carga SI en DS MOV BX, ES Carga ES en BX MOV ECX, EBX Carga EBX en ECX MOV ESP, EDX Carga EDX en ESP MOV ES, DS No se permite (segmento a segmento) MOV CS, AX No se permite ( registro de segmento ) MOV BL, BX No se permite (tamaño mixtos) DIRECCIONAMIENTO DE REGISTRO INDIRECTO Transfiere un byte o palabra entre un registro y una localidad de memoria direccionada por un registro índice o base. En lugar de referenciar por un rótulo la dirección del operando fuente, el valor del operando es señalado por una dirección de desplazamiento almacenada en uno de los registros siguientes: SI, DI,BX, o en algunas veces en el BP. La ventaja de la instrucción de modo de registro indirecto es que el campo de direccionamiento en la instrucción utiliza menos bits para seleccionar un registro que lo que hubiera requerido para especificar una dirección. El microprocesador reconoce el direccionamiento indirecto de registros por la sintaxis de la instrucción. El designador del operando fuente es reconocido por corchetes. EJEMPLOS MOV BP, [DX] LEA AX, [CS] LEA DI, [SS] DIRECCIONAMIENTO DE REGISTRO RELATIVO En este modo el contenido del controlador del programa se suma a la parte de dirección de la instrucción para obtener una dirección efectiva. El direccionamiento relativo por registro es similar al direccionamiento base más índice y al direccionamiento por desplazamiento. En el direccionamiento relativo por registro, para direccionar datos en un segmento de memoria se agrega un desplazamiento al contenido de un registro base índice (BP, BX, DI o SI). Ejemplo: MENSAJE1 DB 'EL LENGUAJE ENSAMBLADOR ES', '#' MENSAJE2 DB 'RAPIDO Y EFICIENTE´','#' . . LEA BX, MENSAJE1 MOV AL,[BX]+4 La orden LEA carga la dirección de desplazamiento en el registro BX. Al referencias el cuarto elemento de MENSAJE1 se realiza añadiendo la dirección base (BX) de MENSAJE1 al desplazamiento, +4 en la cadena. El ensamblador reconoce los tres métodos siguientes para indicar el direccionamiento relativo base. LEA [BX]+4 LEA 4[BX] LEA [BX+4] DIRECCIONAMIENTO DE BASE INDEXADO REGISTRO Con el direccionamiento base indexado, el operando se localiza en el segmento seleccionado en un desplazamiento determinado por la suma de los contenidos del registro base, registro índice y opcionalmente un desplazamiento Ejemplos: LEA BX MYDATA MOV SI,25 MOV AX MYDATA [BX] [SI] DIRECCIONAMIENTO BASE INDEXADO Con el direccionamiento base indexado, el direccionamiento se localiza en el segmento seleccionado en un desplazamiento determinado por la suma de dos contenidos del registro base, registro índice y, opcionalmente un desplazamiento. Ejemplo: LEA BX MY DATA MOV SI, 25 MOV AX MYDATA DIRECCIONAMIENTO INDEXADO DIRECTO En el direccionamiento indexado directo, la dirección de desplazamiento del operando se calcula sumando el desplazamiento a un registro índice (SI o DI) en el segmento seleccionado, frecuentemente, el direccionamiento indexado directo se utiliza para acceder a los elementos de un ARRAY estático. El valor del desplazamiento localiza el comienzo del ARRAY el valor almacenado en el registro índice selecciona uno de modo simple en la estructura. Como los elementos son del mismo tipo de datos y tamaño, por moverse a través del ARRAY basta en incrementar o decrementar sistemáticamente en el desplazamiento. Ejemplo: MOV SI, 4 MOV AL, ARRAY 1 [SI] LEA BX MYDATA MOV 51,25 MOV AX MYDATA [BX] [SI] Internamente el microprocesador tiene 7 registros rotulados A(para el acumulador );B, C, D, E, F H y L estos registros tiene 8 bits de ancho y puede acomodar un bvte . el código de operación de una instrucción leída de la memoria durante el ciclo de fetch es transferido al registro de instrucción. Es entonces decodificada por el control para determinar la secuencia de microoperaciones necesarias ejecutar la instrucción. 1.3 Interrupciones. Una interrupción es una detensión de la ejecución del microprocesador para dar oportunidad a la maquina de comunicarse con los dispositivos perifericos. Puede decirse que una interrupción es el elemento más importante para ejecutar un programa debido a que estas permiten la interacción entre el usuario y la maquina. Una interrupción es un mecanismo de control; básicamente ocasiona que la CPU detenga la ejecución del programa actual transfiera el control a una rutina de servicio y al termino de esta prosigue con la ejecución del programa interrumpido a otros mecanismos para transferencia de control tales como las instrucciones JMPCALL/ RED siempre se encuentran bajo el control del programa, la familia de las microcomputadoras y compatibles aceptan dos tipos de interrupciones las del HARDWARE Y SOFTWARE. EJECUCION DE UNA INTERRUPCION Para que una interrupción se ejecute correctamente es necesario almacenar el número de función en el registro acumulador AH (y demas registros de propósito general, si así se requiere), y posteriormente mandar a llamar el tipo de interrupción requerido. A continuación se mostrara un fragmento de un programa para ejemplificar la ejecución de una interrupción. ;CODIGO PARA BORRAR PANTALLA MOV CX, 0000 MOV DX, 2479H MOV BH, 07 MOV AH, 06 MOV AL, 00 INT 10H RET END Como puede verse en este ejemplo, para ejecutar la interrupción 10H, primero fue necesario cargar el valor de la función en el registro acumulador y los demás atributos en los demás registros de propósito general. El funcionamiento de una interrupción existe cuando el microprocesador concluye al ejecutar la instrucción en curso, entonces para determinar si una interrupción esta activa se comprueba: 1. Alguna instrucción en ejecución 2. Trampa 3. NMI 4. Sobreflujo del segmento del procesador 5. INTR 6. La instrucción INT en el orden citado si está presente una o más de estas condiciones de interrupciones ocurre lo siguiente: 1.- Se salva el contenido de registro de banderas en pila. 2.- Se desactivan las banderas de interrupciones IF y TF esto deshabilita la terminal INTR y la característica de trampa, 3.- Se salva el contenido de registro CS de segmentos de código hacia la pila, 4.- Se salva el contenido del apuntador de instrucción IP en la pila, 5.- Se recupera el contenido del vector de la interrupción y se coloca en IP y en CS de modo que la siguiente instrucción se ejecuta en el procedimiento de servicio de la interrupción diseccionada por el vector. TIPOS DE INTERRUPCIONES Recordemos que las interrupciones internas y externas se inician a partir de señales que ocurren en la CPU. Y esta se encuentra lista para atender a la interrupción a quien le interrumpió, pasa el control ala rutina llamada manejador de interrupciones, esta rutina define la causa de las interrupciones, proporciona el servicio solicitado y regresa el control a la CPU para que esta pueda proseguir a lo que estaba haciendo. INTERNAS (DEL PROGRAMA) Surgen debido a la utilización ilegal o errónea de una instrucción de datos. La interrupción interna también se llama trampa. Algunos ejemplos de las interrupciones preocupadas o condiciones de error internas son los sobre flujos de registro, intentar dividir entre cero un código de operación no valido, desbordamiento de pila y violación de la Protección. Por lo general estas condiciones de error ocurren, como resultado de una determinación prematura de la ejecución de una instrucción. El programa de servicio que procesa la interrupción interna determina la medida correctiva que se debe tomar. La diferencia entre las interrupciones internas y externas, es que las internas se inician por una condición excepcional causada por el programa mismo, mas bien que por un evento externo. Las interrupciones internas son sincronas con el programa en tanto que las externas no lo son. VECTORES PARA INTERRUPCION NUM. DIRECCION MICROPROCESADOR FUNCION 0 OH – 3H 8086-80486 Error al dividir 1 4H-7H 8086-80486 Paso a paso 2 8H –BH 8086-80486 Nmi Int. Hardware 3 CH-FH 8086-80486 Punto de ruptura 4 10H-13H 8086-80486 Int. Por sobre flujo 5 14H-17H 80286-80486 Int. de BOUND 6 18H-1BH 80286-80486 Código invalido 7 1CH-1FH 80286-80486 Int. De emulación 8 20H-23H 80386 -80486 Doble falta 9 24H-27H 80386- 80486 Desbordamiento de segmento 10 28H-2BH 80386- 80486 Segmento de estado de tarea 11 2CH-2FH 80386- 80486 No hay segmento 12 30H-33H 80386- 80486 Falla de pila 13 34H-37H 80386- 80486 Falla general de protección 14 38H-3BH 80386- 80486 Falla de página 15 3CH-3FH 80386- 80486 Reservado 16 40H-43H 80386- 80486 Error de punto decimal 17 44H-47H 80486SX int De verificación de alineación 18-31 48H-7FH 8086-80486 Reservado 32-255 80H-3FFH 8086-80486 Int. Por el usuario. INTERRUPCIONES EXTERNAS Las interrupciones internas provienen de dispositivos de entrada y salida (E/S). De un dispositivo de temporizacion, de un circuito que monitorea la fuente de alimentación de cualquier otra fuente externa. Algunos ejemplos que producen las interrupciones externas son dispositivos de entrada y salida que terminan la trasferencia de datos, tiempo transcurrido de un evento o falla de energía. Puede ocurrir una interrupción por tiempo ilimitado de un programa que este en un ciclo que no termina y que, por lo tanto excede su tiempo asignado una interrupción por falla de energía puede tener como su rutina de servicio a un programa que transfiere el estado completo de la CPU. A una memoria no volátil en pocos segundos anteriores a una falla de energía. UTILIZACION DE LAS INTERRUPCIONES DEL BIOS Y DEL MS DOS Si la pastilla del microprocesador forma el núcleo de la computadora ciertamente la rutina del BIOS Y del DOS son los cerebros de la máquina cada computadora esta programada con una cierta cantidad de información antes de dejar la factoría, esta información esta almacenada en el HARDWARE ROM(memoria sólo de lectura). Típicamente la mayoría de las rutinas del BIOS ( sistema básico de entrada y salida), esta almacenada en ROM y es permanente. EL DOS. Sistema Operativo de discos puede ser considerado como una memoria, añadida en una fecha posterior, realmente es memoria que se añade cada vez que la computadora se conecta o sé inicializa con el DOS, es memoria no permanente, memoria que puede ser actualizada y cambiada con cada nueva liberación del DOS típicamente las operaciones s del DOS son almacenadas en RAM(memoria de acceso aleatorio o residen en el disco hasta que se necesiten). INT 1AH La interrupcion de BIOS, 1AH, permite leer y escribir la fecha y hora actuales desde y al reloj del sistema. La tabla siguiente es un listado de las opciones disponibles con la interrupción del BIOS tipo 1AH. INTERRUPCION 1AH Valor AH Función AH=0 Lee inicializacion reloj actual AH=1 Inicializa reloj actual AH=2 Lee hora--reloj de tiempo real AH=3 AH=4 Entrada Salida CX--Bytes superiores del reloj DX--Bytes inferiores del reloj AL--0,si temporizador no ha pasado 24 horas CX= DX= Bytes superiores del reloj Bytes inferiores del reloj CH--BCD horas CL--BCD minutos DH--BCD segundos Inicializa hora--reloj de tiempo real CH= CL= DH= DL= BCD horas BCD minutos BCD segundos 1--hora del diaundos 0--hora estandar CH--BCD siglo CL--BCD año DH--BCD mes DL--BCD dia Lee fecha-- reloj de tiempo real. AH=5 Inicialioza Fecha--reloj de tiempo real CH= CL= DH= DL= AH=6 Inicializa alarma (hasta 23:59:59) CH= CL= DH= AH=7 Reinicializa alarma. BCD BCD BCD BCD siglo año mes dia BCD horas BCD minutos BCD segundos INT 10H Muchas veces cuando se programa, la entrada o salida de la computadora necesita ser mostrada en la pantalla del monitor. Con frecuencia esto requerirá una simple operación de borrado de pantalla; en otras ocasiones se podrá desear cambiar los colores o atributos de la pantalla. El control de la pantalla puede conseguirse fácilmente con las interrupciones del BIOS tipo 10H . La tabla siguiente es un listado de las posibles variaciones programables con las interrupciones tipo 10H. INTERRUPCION 10H Valor AH Función Salida Entrada AL=0 AL=1 AL=2 AL=3 AL=4 40x25 B/W 40x25 COLOR 80x25 B/W 80x25 COLOR 320x200 color gráficas Control de interfaz del CRT AH=0 Inicializa el modo de visualizar AL=5 AL=6 AL=10 322x200 B/W gráficas 640x200 B/W gráficas 640x350E.G.A.Graficas AH=1 Inicializa tipo de cursor CH= CL= Bits 4-0 principio de línea para el cursor Bits 4-0 fin de linea para cursor AH=2 Inicializa posición de cursor DH= DL= BH= Fila Columna Número página de pantalla Lee posición de cursor (valores hasta ejecusion) DH= DL= CH= CL= BH= Fila Columna Modo cursor Modo cursor Número página de pantalla AH=4 Obtiene posicion lapiz luminoso (valores hasta ejecusión) AH=0 AH=1 DH= DL= CH= BX= Conmutacion no abajo/disparo Respuestas validas; sigue: Fila Columna Grafo linea (0-199) Columna Grafica AH=5 Inicializa página pantalla activa AL= Valor nueva página Modos 0 y 1 (0-7) Modos 2 y 3 (0-3) Enrrola página activa hacia arriba AL= CH= CL= DH= DL= BH= Número de lineas; 0 para pantalla entera Fila, esquina superior izquierda Columna, esquina superiorzquierda Fila,esquina inferior derecha Columna, esquina inferior derecha Atributo a ser usado Enrrola página activa hacia abajo AL= CH= CL= DH= DL= BH= Número de lineas; 0 para pantalla entera Fila, esquina superior izquierda Columna, esquina superior izquierda Fila,esquina inferior derecha Columna, esquina inferior derecha Atributo a ser usado AH=3 AH=6 AH=7 Manipulacion de caracteres AH=8 Lee atributo/caracter en posición cursor BH= AL= AH= Página pantalla Caracter leido Atributo de caracter AH=9 Escribe atributo/caracter en posicion cursor BH= CX= AL= BL= Página pantalla Cuenta de caracteres a escribir Caracter a escribir Atributo de caracter AH=10 Escribe caracter en posición cursor BH = CX = AL = Visualiza página Cuenta de caracteres a escribir Caracter a escribir BH = BL = Paleta ID (0-127) Color para paleta ID 0--Fondo (0-15) 1--paleta 0--Verde(1),rojo(2),amarillo(3) 1-- Cyan (1), magenta (2), Interfaz de gráficos AH=11 Selecciona paleta colores AH=12 Dibuja punto en pantalla DX= CX= AL= Fila (0-199) Columna (0-319/639) Color del punto AH=13 Lee informacion punto DX= CX= AL= Fila (0-199) Columna (0-319/639) Valor del punto Salida teletipo ASCII AH=14 Escribe a página activa AL= BL= Caracter a escribir Color primer plano AH=15 Devuelto a estado video AL= AH= BH= Modo actual Número de columnas de pantalla Página pantalla actual AH=16 Reservado AH=17 Reservado AH=18 Reservado ES:BP= CX= DX= BH= AL=0 Señala a cadena Longitud de cadena Posicion de cursor para comienzo Número de página BL= atributo(car,car,car...car) Cursor no transferido BL= atributo (car,car,car...car) Cursor es transferido (car,atr,car...atr) Curso no transferido (car,atr,car...atr) Cursor es transferido. AH=19 Escribe cadena AL=1 AL=2 AL=3 INTERRUPCION DEL DOS ( 21H) La interrupcion 21H del DOS es una interrupcion de llamada a funcion. Las especificaciones y requerimientos para utilizar esta interrupcion se muestran en la siguiente tabla: INTERRUPCION 21H Valor AH Función Entrada Salida AH = 1 Espera y visualiza caracter de teclado con comprobación CTRL-BREAK AH = 2 Visualiza caracter de teclado con comprobación CTRL-BREAK AH = 3 Entrada asíncrona de caracter AH = 4 Salida asíncrona de caracter DL = Caracter a enviar AH = 5 Caracter a escribir DL = Caracter a escribir AH = 6 Entrada caracter de teclado DL = 0FFH Caracter introducido AH = 7 Espera para caracter de teclado (no visualiza) AL - Caracter introducido AH = 8 Espera para caracter de teclado (no visualiza-comprueba CTRL-BREAK) AL - Caracter introducido AH = 9 Visualiza cadena DS:DX= Dirección de cadena. Debe finalizar con centinela $ AH = A Cadena teclado a buffer DS:DX= Dirección de buffer. Primer byte=tamaño, segundo byte=número de caracteres leidos AH = B Status de entrada de teclado AH = C Borra buffer teclado y llama función AL = 1,6,7,8,0A - Número función AH = D Unidad implícita disco (reset) Ninguna Ninguna AL - Cacter introducido DL = Caracter a visualizar AL - Caracter introducido AL-No. caracter=0FFH Caracter = 0 AH = E Unidad implícita disco (seleccionada) DL = AL - Núm. unidades 0 - Unidad A 1 - Unidad B, etc. AH = 19 Código unidad (ímplicito) AH = 25 Interrupción (inicializa) AH=2A Fecha (lee) AH=2B Fecha (inicializa) AH=2C Hora (lee) AH=2D Hora (inicializa) AH=2E Verifica estado(inicializa) AH=35 Direccion interrupcion (lee) AL - Núm. unidades 0 - Unidad A 1 - Unidad B, etc. DS:DX = Dirección del vector de interrupción AL= Número de interrupción CX--Año(80 a 99) DH--Mes DL--Dia(1 a 31) CX:DX Igual que antes AL--0 si valido OFF si no valido CH--Horas (0-23) CL--Minutos(0-59) CX:DX DL= AL= Igual que antes AL--0 si valido OFF--si no valido 0 AL= 0 0;verifica off 1;verifica on Número de interrupción ES:BX--apunta a direccion vector DL= Unidad (0--implicita,1--A,2--B,etc.) AX--sectores/apiñamiento(FFFF si invalido) BX--Número apiñamiento libres CX--Bytes por sector DX--Número total de apiñamientos AH=36 Disco espacio disponible AH=39 Hace directorio DS:DX Dirección de cadena para directorio AH=3A Eliminar directorio DS:DX Dirección de cadena para directorio AH=3B Cambia directorio DS:DX Dirección de cadena para nuevo directorio DS:DX AH=3C Archivo (crea) Dirección de cadena para archivo AX--Retorno maipula archivo Atributo archivo CX= DS:DX AL= Dirección de cadena para archivo 0--Abre para leer 1--Abre para escribir 2--Abre para ambos AX--Retorno manipula archivo AH=3D Archivo (abre) AH=3E Manipula archivo(cierra) BX= Manipula archivo Archivo o dispositivo(lee) BX= CX= DS:DX Manipula archivo Número de bytes a leer Direccion de buffer AX--Número de bytes a leer AH=40 Archivo o dispositivo(escribe) BX= CX= DS:DX Manipula archivo Número de bytes a escribir Dirección de datos a escribir AX--Número de bytes a escribir AH=41 Archivo (suprime) DS:DX Dirección de archivos cadena Archivo (pone atributo) CX:DX AL=0 AL=1 AL=2 Desplazamiento en bytes Puntero desplazado (CX:DX) bytes desde el comienzo del archivo Puntero a posición actual más desplazamiento Puntero a EOF más desplazamiento DS:DX Dirección de archivo cadena AH=3F AH=42 AH=43 Archivo (Inicializa atributo) SI AL=0, atributo devuelto a CX SI AL=1, inicializa archivo con atributo de CX DL= Número Unidad (0--implicita,1--unidad A,2-unidad B) Dirección buffer DS:DI--Devuelve dirección de cadena AH=47 Direccion actual AH=54 Verifica estado Ninguna AL--0 si verifica off 1 si verifica on AH=56 Archivo(renombra) DS:DX ES:DI Dirección de cadena para información antigua Dirección de cadena para información nueva DS:DI= Definición y Tipos de Interrupciones. La primera cosa de todas, ¿qué son las interrupciones?, bien, las interrupciones son un "mecanísmo" por medio del cual hacemos que la CPU deje la tarea en la que estaba para que se vaya a ocupar de otra cosa distinta, es decir, es una forma de llamar la atención de la CPU de tal forma que cada dispositivo cuando necesita ser atendido por la CPU, emite una interrupción o señal haciendo que la CPU vaya a atenderla de inmediato. Esto es importantísimo ya que de no existir interrupciones, la CPU debería de ir preguntando, cada cierto tiempo, a los dispositivos para ver si necesitan de su intervención y como podeis suponer, eso significaría lentitud, mucha lentitud. Por tanto, quedaros con que las interrupciones sirven para controlar el hardware, ya que son las que llaman a la CPU cuando este, el hardware, necesita la intervención de la misma. Las interrupciones se pueden dividir en 2 grupos: 1. Interrupciones de Software. También son conocidas como "falsas interrupciones" ya que se producen como consecuencia de la ejecución de otra instrucción al no ser el hardware las que las produce. Otra forma de entender estas interrupciones, es verlas desde el punto de vista de llamadas a subrutinas, lógicamente, la gracia está en que esas subrutinas no son nuestras, sino que son las propias de cada sistema operativo, driver o similar tiene. Quedaros pues, con que somos nostros los que hacemos invocamos a la interrupción. Este tipo de interrupción es el más habitual en la programación. 2. Interrupciones de Hardware. Este tipo de interrupción es invocado directamente por los dispositivos hardware de nuestro ordenador, por lo que "son bastante más auténticas" que las anteriores. Al producir algún dispositivo hardware la interrupción el controlador de interrupciones o PIC se encarga de gestionarla determinando, en el caso de producirse más de una interrupción a la vez, cual de ellas tiene más prioridad y debe de ser gestionada por la CPU. El funcionamiento de este tipo de interrupciones es bastante similar y se suele utilizar mucho para la programación de sistemas de comunicaciones. La Tabla de Vectores de Interrupciones. Seguro que alguno se ha preguntado, "si cuando llamamos a una interrupción se ejecuta una determinada rutina...¿dónde narices se encuentra esta rutina?, es más, ¿cómo sabe nuestra CPU dónde encontrarla?." Bien, la respuesta a estas dos preguntas (y muchas más) se encuentra en la tabla de vectores de interrupción. Dicha tabla es una estructura que se crea durante la inicialización del ordenador y se coloca, en el principio de nuestra memoria (segmento 0 y desplazamiento 0). Dicha estrucutura ocupa y de forma justa 1Kb, ya que dispone de 256 entradas de 4 bytes cada una. Lo importante de todo esto es que, dicha estructura, almacena la dirección, en memoria, de las distintas rutinas que van ligadas a las distintas interrupciones, de tal modo que cuando invocamos, mediante interrupción software, o se invoca, mediante hardware, una interrupción, lo que se hace es utilizar un índice que va ligado a la interrupción, de tal manera que, con dicho índice, se acude a la tabla de vectores de interrupción para que el sistema encuentre la dirección de la rutina en dónde se encuentra el verdadero tratamiento a la interrupción, es decir, si se produce la interrupción x, lo que se hace es acudir a la tabla con el índice x para encontrar la entrada (recordar que eran 256 entradas con 4 bytes cada una) que contiene la dirección en memoria de la rutina que sirve para tratar a la interrupción de índice x. Como cada entrada es de 4 bytes, es fácil adivinar que dichos 4 bytes forman la dirección, ya que 2 de esos bytes se utilizan como segmento y los otros 2 como desplazamiento, total, que ya tenemos la dirección de memoria con la rutina que hay que utilizar. Ahora pensar por un momento que somos nostros los que, conociendo la dirección de memoria de una de esas rutinas, cambiamos dicha rutina por una nuestra para que al producirse una determinada interrupción ocurra lo que a nosotros nos interese...Como podéis observar, las posibilidades son muy interesantes, tan sólo tenéis que mirar el efecto de luz del Visualizator v:2.0 (ir a sencción de Bájate algo interesante), dicho efecto se consiguió poniendo en la rutina que se encargaba de controlar la interrupción de reloj del sistema, el código necesario para el efecto de luz, con lo que al producirse la interrupción de reloj se ejecutaba también el código del efecto y como la interrupción de reloj se produce contínuamente, los efectos son muy buenos, vamos, que si queréis hacer ese efecto acudiendo a un bucle While lo llevais claro :-(. Espero haber despertado, con este artículo, el interés por saber dominar las interrupciones. Si queréis saber más, tan sólo teneis que buscar información en libros sobre programación de dispositivos tales como ratón, VGA, etc, en los que se acude siempre a ellas. Ahora mismo estoy pensando el hacer un artículo sobre como controlar el ratón así que puede que aparezca en esta actualización de la revista, además, es la mejor forma de entender las interrupciones ¿o no?. ;). http://usuarios.lycos.es/macedoniamagazine/varios2.htm 1.3.1 Hardware. Ver archivo: Interrupciones 2005.pft Las interrupciones del HARDWARE son invocadas asincronamente es decir pueden ocurrir en cualquier momento que no se encuentran bajo el control del programa con un dispositivo o evento interno. Interrupciones internas de hardware Las interrupciones internas son generadas por ciertos eventos que surgen durante la ejecución de un programa. Este tipo de interrupciones son manejadas en su totalidad por el hardware y no es posible modificarlas. Un ejemplo claro de este tipo de interrupciones es la que actualiza el contador del reloj interno de la computadora, el hardware hace el llamado a esta interrupción varias veces durante un segundo para mantener la hora actualizada. Aunque no podemos manejar directamente esta interrupción (no podemos controlar por software las actualizaciones del reloj), es posible utilizar sus efectos en la computadora para nuestro beneficio, por ejemplo para crear un "reloj virtual" actualizado continuamente gracias al contador del reloj interno. Unicamente debemos escribir un programa que lea el valor actual del contador y lo traduzca a un formato entendible para el usuario. Interrupciones externas de hardware Las interrupciones externas las generan los dispositivos perifericos, como pueden ser: teclado, impresoras, tarjetas de comunicaciones, etc. También son generadas por los coprocesadores. No es posible desactivar a las interrupciones externas. Estas interrupciones no son enviadas directamente a la UCP, sino que se mandan a un circuito integrado cuya función es exclusivamente manejar este tipo de interrupciones. El circuito, llamado PIC 8259A, si es controlado por la UCP utilizando para tal control una serie de vias de comunicación llamadas puertos. http://members.tripod.com/~MoisesRBB/unidad6.htm 1.3.2 Software. Ver archivo: Interrupciones 2005.pft Por otro lado las interrupciones del SOFTWARE no interrumpen algo si no son variantes de las rutinas las cuales pueden invocarse a voluntad y son controladas por un programa en forma sincrónica o sea, que se conoce con todo lo relacionado con su ejecución puesto que el programa controla el momento y la manera en que son llamadas mediante la secuencia de instrucciones CALL y RET, cuando un programa ejecuta una instrucción CALL se conoce previamente la dirección de la rutina llamada de tal forma que la CPU pueda saltar al código, que se encuentra ahí y ejecutarle. En la mayoría de los casos el programa TLINK.EXE determina y asigna las direcciones de las rutinas es claro que el enlazador no puede saber de antemano donde se encuentran las rutinas MS DOS y del BIOS en la memoria de la otra cuando un programa ejecuta una instrucción INT # de interrupción en la CPU automáticamente accesa la memoria baja a fin de obtener el desplazamiento La interrupción INT n, llama al procedimiento de servicio de instrucción que comienza en la dirección representada en el vector número n. La interrupción IRET es una instrucción especial para retorno y se utiliza para retornar de las instrucciones del Software y Hardware. Interrupciones de software Las interrupciones de software pueden ser activadas directamente por el ensamblador invocando al número de interrupción deseada con la instrucción INT. El uso de las interrupciones nos ayuda en la creación de programas, utilizandolas nuestros programas son más cortos, es más fácil entenderlos y usualmente tienen un mejor desempeño debido en gran parte a su menor tamaño. Este tipo de interrupciones podemos separarlas en dos categorias: las interrupciones del sistema operativo DOS y las interrupciones del BIOS. La diferencia entre ambas es que las interrupciones del sistema operativo son más fáciles de usar pero también son más lentas ya que estas interrupciones hacen uso del BIOS para lograr su cometido, en cambio las interrupciones del BIOS son mucho más rápidas pero tienen la desventaja que, como son parte del hardware son muy específicas y pueden variar dependiendo incluso de la marca del fabricante del circuito. La elección del tipo de interrupción a utilizar dependerá unicamente de las caracteristicas que le quiera dar a su programa: velocidad (utilizando las del BIOS) o portabilidad (utilizando las del DOS). Interrupción 21H Propósito: Llamar a diversas funciones del DOS. Sintaxis: Int 21H Nota: Cuando trabajamos en MASM es necesario especificar que el valor que estamos utilizando es hexadecimal. Esta interrupción tiene varias funciones, para accesar a cada una de ellas es necesario que el el registro AH se encuentre el número de función que se requiera al momento de llamar a la interrupción. Funciones para desplegar información al video. 02H Exhibe salida 09H Impresión de cadena (video) 40H Escritura en dispositivo/Archivo Funciones para leer información del teclado. 01H Entrada desde teclado 0AH Entrada desde teclado usando buffer 3FH Lectura desde dispositivo/archivo Funciones para trabajar con archivos. En esta sección unicamente se expone la tarea específica de cada función, para una referencia acerca de los conceptos empleados refierase a la unidad 7, titulada: "Introducción al manejo de archivos". Método FCB 0FH Abrir archivo 14H Lectura secuencial 15H Escritura secuencial 16H Crear archivo 21H Lectura aleatoria 22H Escritura aleatoria Handles 3CH Crear archivo 3DH Abrir archivo 3EH Cierra manejador de archivo 3FH Lectura desde archivo/dispositivo 40H Escritura en archivo/dispositivo 42H Mover apuntador de lectura/escritura en archivo Función 02H Uso: Despliega un caracter a la pantalla. Registros de llamada: AH = 02H DL = Valor del caracter a desplegar. Registros de retorno: Ninguno Esta función nos despliega el caracter cuyo codigo hexagesimal corresponde al valor almacenado en el registro DL, no se modifica ningún registro al utilizar este comando. Es recomendado el uso de la función 40H de la misma interrupción en lugar de esta función. Función 09H Uso: Despliega una cadena de carateres en la pantalla. Registros de llamada: AH = 09H DS:DX = Dirección de inicio de una cadena de caracteres Registros de retorno: Ninguno. Esta función despliega los caracteres, uno a uno, desde la dirección indicada en el registro DS:DX hasta encontrar un caracter $, que es interpretado como el final de la cadena. Se recomienda utilizar la función 40H en lugar de esta función. Función 40H Uso: Escribir a un dispositivo o a un archivo. Registros de llamada: AH = 40H BX = Vía de comunicación CX = Cantidad de bytes a escribir DS:DX = Dirección del inicio de los datos a escribir Registros de retorno: CF = 0 si no hubo error AX = Número de bytes escritos CF = 1 si hubo error AX = Código de error El uso de esta función para desplegar información en pantalla se realiza dandole al registro BX el valor de 1 que es el valor preasignado al video por el sistema operativo MS-DOS. Función 01H Uso: Leer un caracter del teclado y desplegarlo. Registros de llamada: AH = 01H Registros de retorno: AL = Caracter leído Con esta función es muy sencillo leer un caracter del teclado, el código hexadecimal del caracter leído se guarda en el registro AL. En caso de que sea un caracter extendido el registro AL contendra el valor de 0 y será necesario llamar de nuevo a la función para obtener el código de este caracter. Función 0AH Uso: Leer caracteres del teclado y almacenarlos en un buffer. Registros de llamada: AH = 0AH DS:DX = Dirección del área de almacenamiento BYTE 0 = Cantidad de bytes en el área BYTE 1 = Cantidad de bytes leídos desde BYTE 2 hasta BYTE 0 + 2 = caracteres leídos Registros de retorno: Ninguno Los caracteres son leídos y almacenados en un espacio predefinido de memoria. La estructura de este espacio le indica que en el primer byte del mismo se indican cuantos caracteres serán leídos. En el segundo byte se almacena el número de caracteres que ya se leyeron, y del tercer byte en adelante se escriben los caracteres leídos. Cuando se han almacenado todos los caracteres indicados menos uno la bocina suena y cualquier caracter adicional es ignorado. Para terminar la captura de la cadena es necesario darle [ENTER]. Función 3FH Uso: Leer información de un dispositivo o archivo. Registros de llamada: AH = 3FH BX = Número asignado al dispositivo CX = Número de bytes a procesar DS:DX = Dirección del área de almacenamiento Registros de retorno: CF = 0 si no hay error y AX = número de bytes leidos. CF = 1 si hay error y AX contendra el código del error. Función 0FH Uso: Abrir archivo FCB Registros de llamada: AH = 0FH DS:DX = Apuntador a un FCB Registros de retorno: AL = 00H si no hubo problema, de lo contrario regresa 0FFH Función 14H Uso: Leer secuencialmente un archivo FCB. Registros de llamada: AH = 14H DS:DX = Apuntador a un FCB ya abierto. Registros de retorno: AL = 0 si no hubo errores, de lo contrario se regresara el código correspondiente de error: 1 error al final del archivo, 2 error en la estructura del FCB y 3 error de lectura parcial. Esta función lo que hace es que lee el siguiente bloque de información a partir de la dirección dada por DS:DX, y actualiza este registro. Función 15H Uso: Escribir secuencialmente a un archivo FCB Registros de llamada: AH = 15H DS:DX = Apuntador a un FCB ya abierto Registros de retorno: AL = 00H si no hubo errores, de lo contrario contendra el código del error: 1 disco lleno o archivo de solo lectura, 2 error en la formación o especificación del FCB. La función 15H después de escribir el registro al bloque actual actualiza el FCB. Función 16H Uso: Crear un archivo FCB. Registros de llamada: AH = 16H DS:DX = Apuntador a un FCB ya abierto. Registros de retorno: AL = 00H si no hubo errores, de lo contrario contendra el valor 0FFH Se basa en la información proveida en un FCB para crear un archivo en el disco. Función 21H Uso: Leer en forma aleatoria un archivo FCB. Registros de llamada: AH = 21H DS:DX = Apuntador a un FCB ya abierto. Registros de retorno: A = 00H si no hubo error, de lo contrario AH contendra el código del error: 1 si es fin de archivo, 2 si existe error de especificación de FCB y 3 si se leyó un registro parcial o el apuntador del archivo se encuentra al final del mismo. Esta función lee el registro especificado por los campos del bloque actual y registro actual de un FCB abierto y coloca la información en el DTA (área de transferencia de disco o Disk Transfer Area). Función 22H Uso: Escribir en forma aleatoria en un archivo FCB. Registros de llamada: AH = 22H DS:DX = Apuntador a un FCB abierto. Registros de retorno: AL = 00H si no hubo error, de lo contrario contendrá el código del error: 1 si el disco está lleno o es archivo de solo lectura y 2 si hay error en la especificación de FCB. Escribe el registro especificado por los campos del bloque actual y registro actual de un FCB abierto. Escribe dicha información a partir del contenido del DTA (área de transferencia de disco). Función 3CH Uso: Crear un archivo si no existe o dejarlo en longitud 0 si existe. (Handle) Registros de llamada: AH = 3CH CH = Atributo de archivo DS:DX = Apuntador a una especificaión ASCIIZ Registros de retorno: CF = 0 y AX el número asignado al handle si no hay error, en caso de haberlo CF será 1 y AX contendra el código de error: 3 ruta no encontrada, 4 no hay handles disponibles para asignar y 5 acceso negado. Esta función sustituye a la 16H. El nombre del archivo es especificado en una cadena ASCIIZ, la cual tiene como característica la de ser una cadena de bytes convencional terminada con un caracter 0. El archivo creado contendra los atributos definidos en el registro CX en la siguiente forma: Valor Atributos 00H Normal 02H Escondido 04H Sistema 06H Escondido y de sistema El archivo se crea con los permisos de lectura y escritura. No es posible crear directorios utilizando esta función. Función 3DH Uso: Abre un archivo y regrese un handle Registros de llamada: AH = 3DH AL = modo de acceso DS:DX = Apuntador a una especificación ASCIIZ Registros de retorno: CF = 0 y AX = número de handle si no hay errores, de lo contrario CF = 1 y AX = código de error: 01H si no es válida la función, 02H si no se encontró el archivo, 03H si no se encontr´o la ruta, 04H si no hay handles disponibles, 05H en caso de acceso negado, y 0CH si el código de acceso no es válido. El handle regresado es de 16 bits. El código de acceso se especifica en la siguiente forma: BITS 7654321 . . . . 0 0 0 Solo lectura . . . . 0 0 1 Solo escritura . . . . 0 1 0 Lectura/Escritura . . . X . . . RESERVADO Función 3EH Uso: Cerrar archivo (Handle). Registros de llamada: AH = 3EH BX = Handle asignado Registros de retorno: CF = 0 si no hubo errores, en caso contrario CF será 1 y AX contendrá el código de error: 06H si el handle es inválido. Esta función actualiza el archivo y libera o deja disponible el handle que estaba utilizando. Función 3FH Uso: Leer de un archivo abierto una cantdad definida de bytes y los almacena en un buffer específico. Registros de llamada: AH = 3FH BX = Handle asignado CX = Cantidad de bytes a leer DS:DX = Apuntador a un área de trabajo. Registros de retorno: CF = 0 y AX = número de bytes leidos si no hubo error, en caso contrario CF = 1 y AX = código de error: 05H si acceso negado y 06H si no es válido el handle. Función 40H Uso: Escribe a un archivo ya abierto una cierta cantidad de bytes a partir del buffer designado. Registros de llamada: AH = 40H BX = Handle asignado CX = Cantidad de bytes a escribir. DS:DX = Apuntador al buffer de datos. Registros de retorno: CF = 0 y AX = número de bytes escritos si no hay errores, en caso de existir CF = 1 y AX = código del error: 05H si el acceso es negado y 06H si el handle es inválido. Función 42H Uso: Mover apuntador al archivo (Handle) Registros de llamada: AH = 42H AL = método utilizado BX = Handle asignado CX = La parte más significativa del offset DX = La parte menos significativa del offset Registros de retorno: CF = 0 y DX:AX = la nueva posición del apuntador. En caso de error CF será 1 y AX = código de error: 01H si la función no es válida y 06H si el handle no es válido. El método utilizado se configura como sigue: Valor de AL Método 00H A partir del principio del archivo 01H A partir de la posición actual 02H A partir del final del archivo Interrupción 10H Propósito: Llamar a diversas funciones de video del BIOS. Sintaxis: Int 10H Esta interrupción tiene diversas funciones, todas ellas nos sirven para controlar la entrada y salida de video, la forma de acceso a cada una de las opciones es por medio del registro AH. En este tutorial unicamente veremos algunas de las funciones de esta interrupción. Funciones comunes de la interrupción 10H. 02H Selección de posición del cursor 09H Escribe atributo y caracter en el cursor 0AH Escribe caracter en la posición del cursor 0EH Escritura de caracteres en modo alfanumérico Función 02H Uso: Posiciona el cursor en la pantalla dentro de las coordenadas válidas de texto. Registros de llamada: AH = 02H BH = Página de video en la que se posicionará el cursor. DH = Fila DL = Columna Registros de retorno: Ninguno. Las posiciones de localización del cursor son definidas por coordenadas iniciando en 0,0, que corresponde a la esquina superior izquierda hasta 79,24 correspondientes a la esquina inferior derecha. Tenemos entonces que los valores que pueden tomar los registros DH y DL en modo de texto de 80 x 25 son de 0 hasta 24 y de 0 hasta 79 respectivamente. Función 09H Uso: Desplegar un caracter un determinado número de veces con un atributo definido empezando en la posición actual del cursor. Registros de llamada: AH = 09H AL = Caracter a desplegar BH = Página de video en donde se desplegará BL = Atributo a usar Número de repeticiones. Registros de retorno: Ninguno Esta función despliega un caracter el número de veces especificado en CX pero sin cambiar la posición del cursor en la pantalla. Función 0AH Uso: Desplegar un caracter en la posición actual del cursor. Registros de llamada: AH = 0AH AL = Caracter a desplegar BH = Página en donde desplegar BL = Color a usar (sólo en gráficos). CX = Número de repeticiones Registros de retorno: Ninguno. La única diferencia entre esta función y la anterior es que ésta no permite modificar los atributos, simplemente usa los atributos actuales. Tampoco se altera la posición del cursor con esta función. Función 0EH Uso: Deplegar un caracter en la pantalla actualizando la posición del cursor. Registros de llamada: AH = 0EH AL = Caracter a desplegar BH = Página donde se desplegara el caracter BL = Color a usar (solo en gráficos) Registros de retorno: Ninguno Interrupción 16H Propósito: Manejar la entrada/salida del teclado. Sintaxis: Int 16H Veremos dos opciones de la interrupción 16H, estas opciones, al igual que las de otras interrupciones, son llamadas utilizando el registro AH. Funciones de la interrupción 16H 00H Lee un caracter de teclado 01H Lee estado del teclado Función 00H Uso: Leer un caracter del teclado. Registros de llamada: AH = 00H Registros de retorno: AH = código de barrido (scan code) del teclado AL = Valor ASCII del caracter. Cuando se utiliza esta interrupción se detiene la ejecución del programa hasta que se introduzca un caracter desde el teclado, si la tecla presionada es un caracter ASCII su valor será guardado en el registro AH, de lo contrario el código de barrido será guardado en AL y AH contendrá el valor 00H. El código de barrido fué creado para manejar las teclas que no tienen una representación ASCII como [ALT], [CONTROL], las teclas de función, etc. Función 01H Uso: Leer estado del teclado. Registros de llamada: AH = 01H Registros de retorno: Si la bandera de cero, ZF, está apagada significa que hay información en el buffer, si se encuentra prendida es que no hay teclas pendientes. En caso de existir información el registro AH contendrá el código de la tecla guardada en el buffer. Interrupción 17H Propósito: Manejar la entrada/salida de la impresora. Sintaxis: Int 17H Esta interrupción es utilizada para escribir caracteres a la impresora, inicializarla y leer su estado. Funciones de la interrupción 16H 00H Imprime un caracter ASCII 01H Inicializa la impresora 02H Proporciona el estado de la impresora Función 00H Uso: Escribir un caracter a la impresora. Registros de llamada: AH = 00H AL = Caracter a imprimir DX = Puerto a utilizar Registros de retorno: AH = Estado de la impresora. El puerto a utilizar, definido en DX, se especifica así: LPT1 = 0, LPT2 = 1, LPT3 = 2 ... El estado de la impresora se codifica bit por bit como sigue: BIT 1/0 SIGNIFICADO ---------------------------------------0 1 Se agotó el tiempo de espera 123 1 Error de entrada/salida 4 1 Impresora seleccionada 5 1 Papel agotado 6 1 Reconocimiento de comunicación 7 1 La impresora se encuentra libre Los bits 1 y 2 no son relevantes. La mayoria de los BIOS unicamente soportan 3 puertos paralelos aunque existen algunos que soportan 4. Función 01H Uso: Inicializar un puerto de impresión. Registros de llamada: AH = 01H DX = Puerto a utilizar Registros de retorno: AH = Status de la impresora El puerto a utilizar, definido en DX, se especifica así: LPT1 = 0, LPT2 = 1, etc. El estado de la impresora se codifica bit por bit como sigue: BIT 1/0 SIGNIFICADO ---------------------------------------0 1 Se agotó el tiempo de espera 123 1 Error de entrada/salida 4 1 Impresora seleccionada 5 1 Papel agotado 6 1 Reconocimiento de comunicación 7 1 La impresora se encuentra libre Los bits 1 y 2 no son relevantes. La mayoria de los BIOS unicamente soportan 3 puertos paralelos aunque existen algunos que soportan 4. Función 02H Uso: Obtener el estado de la impresora. Registros de llamada: AH = 01H DX = Puerto a utilizar Registros de retorno: AH = Status de la impresora. El puerto a utilizar, definido en DX, se especifica así: LPT1 = 0, LPT2 = 1, etc. El estado de la impresora se codifica bit por bit como sigue: BIT 1/0 SIGNIFICADO ---------------------------------------0 1 Se agotó el tiempo de espera 123 1 Error de entrada/salida 4 1 Impresora seleccionada 5 1 Papel agotado 6 1 Reconocimiento de comunicación 7 1 La impresora se encuentra libre Los bits 1 y 2 no son relevantes. La mayoria de los BIOS unicamente soportan 3 puertos paralelos aunque existen algunos que soportan 4. Introducción Todo el semestre hemos usado interrupciones para hacer llamado a servicios de DOS y BIOS; hasta ahora, principalmente las hemos visto como formas de llamar al sistema operativo. La razón por la que este sistema es llamado "interrupción" es precisamente porque funcionan interrumpiendo la ejecución del programa en un punto dado, ejecutan una labor determinada y regresan a donde estaban. Es como estar trabajando en la tarea, recibir una llamada telefónica, atenderla y al terminar lograr regresar exactamente al estado previo para continuar con la tarea Existen dos tipos de interrupciones: Interrupciones de hardware: son las más primitivas y origen del concepto. Están diseñadas para que el procesador pueda atender a los dispositivos aún a mitad de la ejecución de un programa que no está detectándolos. Interrupciones de software: permiten al software emular las operaciones que se hacen al atender el hardware. También permiten contener una interfase universal a los servicios del sistema operativo, que son independientes de las versiones del sistema operativo. Cuando un dispositivo requiere atención del CPU, le avisa por medio de una señal (electrónica) que llamamos "Interrupción". Esta señal físicamente llega a un pin del CPU; cuando esto ocurre, el CPU termina la instrucción que estaba ejecutando, salva en la pila la dirección actual (CS, IP) para poder continuar más adelante en el punto donde se quedó, y salta a ejecutar el código de atención de la interrupción, que generalmente es parte del sistema operativo. Dado que el código que atiende la interrupción puede encontrarse en distintos lugares de memoria, se requiere algún mecanismo para que el CPU encuentre eficientemente donde continuar la atención de la interrupción. Este mecanismo es conocido como "tabla de interrupciones", y se basa en reconocer por un código a cada interrupción, el cual será usado para la búsqueda en esta tabla. Esta tabla de interrupciones la encuentra el 80x86 al inicio de la memoria, en el primer 1 Kb. Está formada por un arreglo de 256 direcciones, cada una de 32 bits, pues contiene al segmento y al desplazamiento (en orden inverso, como es costumbre en Intel). De modo que si se recibe el código de interrupción 0, se consulta la información en la localidad 0:0, y de ahí se obtienen el segmento y el desplazamiento correspondientes. Si se recibe el código de interrupción 8, se consulta la información en la localidad 0:20h (20h = 32 = 8 por 4, dado que cada entrada en el arreglo tiene 4 bytes), y de ahí se ejecuta el código correspondiente. Al terminar el código de atención de la interrupción, ejecuta una instrucción IRET, que regresa el punto de ejecución a donde se había quedado, Funcionan de un modo similar a las de hardware, solamente que en vez de que un dispositivo envíe la señal, lo hace el programa utilizando la instrucción INT. Esta instrucción causa que se ejecute todo el proceso anterior, pero señalada por un programa. Programas residentes / programas transientes La mayor parte de los programas con los que hemos trabajado son programas "transientes", es decir, se cargan en memoria, se ejecutan y al terminar su ejecución, regresan el control al sistema operativo y se eliminan de memoria. Un programa residente (Terminate and Stay Resident, de ahí las siglas TSR comúnmente usadas) consiste en que un programa se carga en memoria, se ejecuta, prepara la ejecución de la segunda sección (por medio de un control fino sobre las interrupciones), y libera solamente la memoria del código de preparación. De este modo, una sección de su código queda "latente", en espera de ser disparado por un evento: generalmente, una interrupción. Entre las aplicaciones de los programas residentes tenemos: El sistema operativo DOS: queda en memoria esperando a que otro programa lo llame, para entonces ejecutar un determinado código. Programas que esperen una combinación de teclas, para entonces realizar su acción. Por ejemplo, sacar un menú con acciones específicas. Programas que sustituyan la funcionalidad del sistema operativo. Por ejemplo, las versiones 2 a 3 de Novell Netware se cargaban después de DOS, y le anexaban la capacidad de acceder a unidades de red. Programación de TSRs Para programar un TSR, utilizaremos una serie de servicios de interrupciones que nos facilitan la capacidad de dejar una sección del programa activa, así como cambiar la dirección a la que apunta una interrupción. Resumiendo, tendremos dos grandes secciones en un programa residente: La sección transiente, que lleva a cabo la inicialización requerida y prepara los vectores de interrupción (las direcciones que están en la tabla), de modo que apunten a los procedimientos del programa residente que van a atenderlas. La sección residente, la cual contiene los procedimientos de atención a las interrupciones. Terminar un programa y dejar una sección residente en memoria. Cuando llamamos al servicio 4Ch de la INT 21h, toda la memoria que ocupó nuestro programa se marca como libre y el sistema operativo puede reutilizarla. Esto sería desastroso en el caso de un programa residente, pues el código que atiende interrupciones podría sobreescribirse, con resultados impredecibles. Por ello, se debe terminar el programa de modo que deje ocupada su memoria. Para ello, existen varias opciones: La interrupción 27h, que termina el programa y deja residente en memoria la zona indicada por DS:DX, donde DS apunta al segmento donde se encuentra el código, y DX al desplazamiento final del código que debe respetarse. De este modo, no se liberará memoria indebidamente. Servicios de la 21h, que no veremos en este curso. Interceptar una interrupción Para que se ejecute un procedimiento que nosotros desarrollamos, en atención de una interrupción, es común usar la técnica de "interceptar", que consiste en guardar el vector anterior de interrupción, cambiarlo para que apunte a nuestro programa residente, y al final de nuestra rutina residente, llamar al vector anterior. Esto conserva la funcionalidad regular, y al mismo tiempo nos permite realizar actividades adicionales. Cabe aclarar algunas de las interrupciones importantes que no hemos comentado por no ser tan usadas en software: INT 08h - es llamada por el reloj del sistema, 18.2 veces por segundo, y entre otras cosas lleva el tiempo de BIOS. Podemos interceptarla (no sustituirla) para que nuestro programa realice algún proceso periódicamente. INT 09h - es llamada por el teclado, cada vez que se recibe una tecla. INT 0Ch - de software, es llamada por la interrupción del reloj. Obviamente hay muchas otras, sirvan estas como ejemplo. Se pueden interceptar las interrupciones generadas por puertos seriales, paralelos, división entre 0, una instrucción inválida, etcétera. Sustituir una interrupción En ocasiones queremos reemplazar completamente el funcionamiento de una interrupción; en tal caso, sustituiremos el vector de interrupción para que apunte a nuestro código, y deberemos terminar la ejecución de nuestro procedimiento con un IRET, que permitirá regresar al punto donde se ejecutó nuestra interrupción. Por otro lado, es conveniente asegurarnos de conservar el valor de los registros (generalmente utilizando la pila) que modifiquemos, de este modo la interrupción no afecta la ejecución de otros procesos. Acciones válidas dentro de una interrupción En una interrupción podemos utilizar otros servicios, principalmente de acceso directo al hardware. Sin embargo, como DOS es un sistema operativo que no fue diseñado para ser multitarea, diversas funciones no soportan la capacidad de ser reentrantes; esto es, no pueden ser llamadas otra vez si están a medio ejecutarse. Pero como una interrupción puede ocurrir en cualquier momento, puede incluso ocurrir cuando está una función a medio ejecutarse. Por tanto, esto causaría inestabilidad del sistema. Existen dos métodos para evitar esta inestabilidad: No usar interrupciones, que es bastante común y apropiado pues también tiene grandes beneficios de desempeño; cuando un programa residente no requiere hacer funciones complejas que estén implementadas en el sistema operativo, sino funciones simples tales como pintar en pantalla, pueden codificarse estas funciones con acceso directo al hardware y de este modo se evita el problema. Revisar si está una función del sistema en ejecución, para no volver a llamarla a mitad de la ejecución. Esto en DOS se hace por medio de un método que durante mucho tiempo no estaba documentado: la bandera InDOS. Para obtenerla, se llama al servicio 34h de la interrupción 21h, y nos deja en ES:BX la dirección de un byte que es esta bandera InDOS. El funcionamiento de la bandera es sencillo: si está en 0, no estamos a media función de DOS, por lo tanto, podemos sin problemas ejecutar una función DOS. De lo contrario, si el valor es 1 o superior, no debemos ejecutar la función DOS en este momento y nuestro programa TSR deberá posponer el uso de esta función, por lo que probablemente deberemos manejar la interrupción del reloj para intentar después de un tiempo. Existen algunas precauciones adicionales que tomar en TSRs, sin embargo, como no siempre son requeridas, se hace la referencia al PC Interno y otros libros de la bibliografía. http://www.sinergia-web.com.mx/clases/asm9708/Temas/clase26.htm Ejemplos 1. Programa residente muy simple ; RESIDE.ASM: Pequeño programa residente ; Se ejecuta, y cuando se presiona Ctrl + N invierte los colores de fondo y frente ; en toda la pantalla, usando la memoria de video para modificar el atributo. ; PushA Macro PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI PUSH DS PUSH ES EndM POPA Macro POP ES POP DS POP DI POP SI POP DX POP CX POP BX POP AX EndM ; Buffer del teclado de BIOS BiosData Segment AT 40h ORG 1Ah Head DW ? Tail DW ? Buffer DW 16 DUP (?) Buffer_End Label Word BiosData EndS ; Segmento de código Code Segment Assume CS:Code Assume DS:Code Assume SS:Code Assume ES:Code ORG 100h Start: JMP Inicio ; Variables OldInt Label Word DirAnt DD BanderaLleno ? DB 0 ; Rutina de atención de Interrupción ParteRes Proc Near Assume CS:Code,DS:Code PushA PushF CALL CS:DirAnt rutina que ; IMPORTANTE: Es una llamada FAR a la ; anteriormente atendía al teclado; es necesaria, ; pues esa rutina se encarga de manejar el buffer ; de teclado y yo lo voy a usar. Otros programas ; posiblemente en vez de CALL hagan un ; JMP FAR [CS:DirAnt] ; DESPUÉS de realizar su función ASSUME DS:BiosData MOV BX,BiosData MOV DS,BX MOV BX,Tail CMP BX,Head JE Fin CALL ChecaTecla CMP AX,310Eh ; ^N JNE Fin MOV Tail,BX ; Invertir color de pantalla MOV CX,2000 MOV AX,0B800h MOV ES,AX MOV DI,0 Ciclo: MOV AX,ES:[DI] XOR AH,07Fh ; Invierte atributo, todos los bits correspondientes a colores MOV ES:[DI],AX INC DI INC DI LOOP Ciclo Fin:PopA IRET ParteRes EndP ChecaTecla Proc Near MOV DX,Tail SUB DX,2 CMP DX,OFFSET Buffer JAE OK MOV DX,OFFSET Buffer_End SUB DX,2 OK: MOV BX,DX MOV AX,[BX] RET ChecaTecla EndP Inicio: PUSH PUSH POP CLI PUSH XOR MOV MOV (reloj) LEA MOV MOV MOV MOV MOV MOV POP STI LEA INT RET Fin2: Code EndS END Start DS CS DS ES AX,AX ES,AX DI,36 ; 9*4 ; Pues estamos interceptando la interrupción 9 BX,ParteRes AX,ES:[DI] OldInt,AX ES:[DI],BX AX,ES:[DI+2] OldInt+2,AX ES:[DI+2],DS ES DX,Fin2 27h http://www.sinergiaweb.com.mx/clases/asm9708/programa/reside.asm 1. Programa de reloj residente ; Programa de reloj residente ; Bruno Guardia Robles. .MODEL TINY ; .COM ; Se ensambla con TASM RELOJRES ; TLINK RELOJRES /t = COM (ver TLINK para ayuda) .CODE PushA Macro PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI PUSH DS PUSH ES EndM POPA Start: Macro POP ES POP DS POP DI POP SI POP DX POP CX POP BX POP AX EndM ORG 100h JMP Inicializa ; Variables Horas DB Minutos DB Segundos DB PMFlag DB DivHi DW DivLo DW ResultHi DW ResultLo DW Actual DB Divisor DB Contador DB ? ? ? 0 0 0 0 0 0 0 0 LeeReloj Proc Near ; Regresa en DX:AX el valor actual del reloj contador interno de BIOS. PUSH ES MOV AX,40h MOV ES,AX MOV DI,6Ch MOV AX,ES:[DI] PUSH AX ADD DI,2 MOV AX,ES:[DI] POP DX POP ES RET LeeReloj EndP DobleDiv Proc Near ; Hace una división de Doble Palabra entre Byte dejando Doble Palabra ; Rutina para estar orgulloso, fue latosa y está basada en corrimientos, tal ; como se divide binario a mano. XOR AX,AX MOV Actual,AL MOV ResultLo,AX MOV ResultHi,AX MOV CX,33 ; 32 ciclos Ciclo: MOV AL,Actual CMP AL,Divisor PUSHF JB NoCarry STC ; Encender el Carry si es mayor o igual JMP SHORT Rotar NoCarry: CLC ; Apagar el Carry Rotar: RCL ResultLo,1 RCL ResultHi,1 POPF JB ContCiclos ; Si Actual es menor al Divisor, restar Divisor al Actual SUB AL,Divisor MOV Actual,AL ContCiclos: ; Rotar el dividendo ROL DivLo,1 RCL DivHi,1 ; El bit m s significativo entra a Actual RCL Actual,1 LOOP Ciclo ; En Actual qued¢ el residuo RET DobleDiv EndP ; Calcular la hora en base al valor del contador de reloj de BIOS Calcula2 Proc Near ; Primero multiplicar DX:AX por 5 XCHG AX,DX PUSH DX PUSH AX PUSH DX PUSH AX ROL AX,1 RCL DX,1 ROL AX,1 RCL DX,1 POP BX POP CX ADD AX,BX ADC DX,CX ; Ahora, dividir DX:AX entre 91 MOV DivHi,DX MOV DivLo,AX MOV Divisor,91 CALL DobleDiv ; Calcular el factor de correcci¢n como Ciclos/50269 ; Aproximadamente cada 50000 ciclos se adelanta un segundo ; Las pruebas que hice dan resultados entre 50200 y 50300 ; Como factor promedio usar‚ 50260 ; Con esto el error se reduce a 1 segundo, 1 segundo y medio ; El factor es m s exacto por la noche, mientras que el ; c lculo de Ciclos*5/91 es m s exacto temprano, lo que los ; equilibra bastante. POP AX POP DX MOV CX,50260 DIV CX MOV BX,AX ; Sacar el resultado de la doble divisi¢n MOV DX,ResultHi MOV AX,ResultLo CLC SUB AX,BX ; Restar factor de correcci¢n SBB DX,0 ; Dividir entre 60 para obtener minutos y segundos. MOV CX,60 DIV CX MOV Segundos,DL DIV CL MOV Minutos,AH MOV Horas,AL ; Establecer la bandera de AM/PM MOV PMFlag,0 CMP Horas,12 JL FinCalcula INC PMFlag CMP Horas,12 JE FinCalcula SUB Horas,12 FinCalcula: RET Calcula2 EndP ; Pintar el valor de dos dígitos en la posición actual ES:DI DespByte Proc Near MOV CL,10 XOR AH,AH DIV CL ADD AX,3030h MOV BL,AH MOV AH,31 ; Blanco sobre azul MOV ES:[DI],AX ADD DI,2 MOV AL,BL MOV ES:[DI],AX ADD DI,2 RET DespByte EndP ; Pintar el reloj por acceso directo a memoria Despliega Proc Near PUSH ES MOV AX,0B800h MOV ES,AX MOV DI,138 ; Columna 80-11 de ancho=69*2 bytes c/columna=138 MOV AL,Horas CALL DespByte MOV AL,':' MOV ES:[DI],AX ADD DI,2 MOV AL,Minutos CALL DespByte MOV AL,':' MOV ES:[DI],AX ADD DI,2 MOV AL,Segundos CALL DespByte MOV AL,' ' MOV ES:[DI],AX ADD DI,2 MOV AL,'a' CMP PMFlag,1 JNE MOV ImpAMPM: MOV ADD MOV MOV POP RET Despliega EndP ImpAMPM AL,'p' ES:[DI],AX DI,2 AL,'m' ES:[DI],AX ES ; Rutina residente!!! ; Podría ser PROC Inicio: PUSHA PUSHF CALL CALL CALL Salir: POPF POPA IRET porque es la ; Guardar todos los registros ; y además las banderas LeeReloj Calcula2 Despliega ; Restaurar banderas ; y registros ; Terminar rutina de interrupción; esto solamente ; interrupción 1Ch, que no tiene una función predefinida. Si fuera ; otra, habría que saltar a la rutina que anteriormente atendía la ; interrupción ; Parte no residente del programa; debe ponerse al final, para que se use la ; Interrupción 27h Inicializa: ; Ajustar DS PUSH DS PUSH CS POP DS PUSH ES ; Apuntar ES al segmento 0 XOR AX,AX MOV ES,AX ; Muy importante: Desactivar Interrupciones cuando vamos a ajustar el vector CLI LEA BX,Inicio MOV AX,CS ; Ajustar el vector de interrupción del Reloj: en BX el desplazamiento de la ; rutina que atiende al reloj, en AX el segmento. MOV ES:[1Ch*4],BX MOV ES:[1Ch*4+2],AX STI ; Restaurar ES POP ES ; DS:DX apuntan al final de la zona que va a quedar residente LEA DX,Inicializa INT 27h ; Terminar pero dejar parte residente END Start http://www.sinergiaweb.com.mx/clases/asm9708/programa/relojres.asm 1.4 Estructura de un programa en ensamblador. Un programa en lenguaje ensamblador se compone de las siguientes partes: Área de comentarios Definición del modelo de memoria Área de datos Cuerpo del programa El área de comentarios sirve para incluir comentarios acerca del programa que se está elaborando, comienza con la directiva .COMMENT y el comentario es colocado entre dos caracteres ‘*’. La definición del modelo de memoria es la parte donde se indica que tipo de código se va generar (16 o 32 bits). En este trabajo sólo se escribirán programas ejecutables .COM, por lo que siempre se usa la directiva .MODEL TINY. El área de datos es el lugar donde deben ser declaradas las constantes y variables del programa. Las variables son declaradas después de la directiva .DATA y las constantes después de .CONST. En el cuerpo del programa es donde se colocan las instrucciones en lenguaje ensamblador que se encargarán de realizar las tareas deseadas. El cuerpo del programa comienza con la directiva .CODE y termina con la directiva END. Esta parte corresponde al Begin y End de un programa en lenguaje Pascal. Adicionalmente se debe indicar un punto de entrada al programa. El punto de entrada se indica por medio de una etiqueta antes de la primer instrucción real del programa. En el ejemplo anterior el punto de entrada es INICIO: y el punto final de las instrucciones se indica por medio de la instrucción END INICIO. Cuando se requiere comentar las instrucciones de un programa, se debe colocar un punto y coma (;) y así el ensamblador interpreta todo lo que sigue como un comentario de una sola línea. Si requiere comentarios de más de una línea puede usar la directiva .COMMENT. http://www.monografias.com/trabajos11/lenen/lenen.shtml FORMATO DE INSTRUCCIONES. Un programa en lenguaje ensamblador es una serie de sentencias ejecutables que le dicen al ensamblador que operaciones tiene que realizar. Cada sentencia de lenguaje ensamblador esta compuesto de cuatro campos: CAMPO NOMBRE CAMPO OPERACIÓN CAMPO OPERANDO CAMPO COMENTARIO Sin embargo, ciertas instrucciones del ensamblador no utilizan todos los campos. El campo comentario existe para expresar propósitos o documentación de programación interna y es opcional. CAMPO NOMBRE: El campo del nombre, algunas veces denominado el campo de rótulo, asigna un nombre simbólico, a la dirección de comienzo de memoria real de una instrucción del ensamblador. Esto permite al programador que referencie una instrucción por nombre y elimine la necesidad de seguir la pista de direcciones de las instrucciones, esto es especialmente útil al generar código reubicable. Al utilizar una referencia simbólica, el programador permite al enlazador (linker) seleccionar en que sitio de memoria será cargado el programa en lenguaje ensamblador. Aunque a cualquier instrucción se le puede dar un nombre, este campo esta habitualmente reservado para aquellas instrucciones que serán reverenciadas en las definiciones de datos, constantes, lazos, bifurcaciones y llamadas a subrutinas. Un nombre debe comenzar con un carácter alfabético y puede contener hasta 31 caracteres, incluyendo: · Todas las letras de la (A a la Z, a a la z) · Dígitos numéricos del (0 al 9) · Los símbolos especiales siguientes (-$.?@%) Debe de tenerse precaución al seleccionar un nombre. No se puede utilizar un nombre que coincida con una palabra reservada o directivo del ensamblador. Si el nombre incluye un punto (.), entonces el punto debe ser el primer carácter. Un campo nombre puede ser un nombre simbólico que represente: · Nombre de etiqueta · Nombre de constantes · Nombre de variables · Nombre de procedimiento · Nombre de macro CAMPO OPERACION: El campo de operación contiene un mnemotécnico para una instrucción real del microprocesador. El mnemónico de operación hace el código más fácil de leer y comprender y es solamente una tabla de conversión interna del valor binario de código máquina real. Una operación o nemotécnico, puede representar una instrucción máquina, macroinstrucción o pseudo-operación. Ejemplo : INITAL: MOV AX, OH INITAL es un rótulo y MOV es una operación. Siguiendo al campo de operación se encuentra el campo de operando, cada operación no solo le dice al ensamblador que instrucción debe ejecutar sino cuantas operaciones se necesitan y de que tipo. Una operación puede contener una referencia a una macro tal referencia indica al ensamblador como procesar una secuencia predefinida de código. El campo operación, esta representado por un mnemónico. Se tienen dos tipos de mnemónicos: Pseudo - operación Código de operación Directivas = macros. Instrucciones = programa. (directivas) (instrucciones) la directiva nos sirve para definir, variables, constantes, son los códigos que se ejecutan durante la ejecución del CAMPO OPERANDO : El campo operando contiene la posición o posiciones donde están los datos que van hacer manipulados por la instrucción de la operación, si la instrucción requiere uno o dos operados. Los operados están separados de la instrucción por al menos un espacio en blanco. Si hay operaciones que no requieren operados. Cuando una operación requiere dos operados, el primer operando se denomina operando destino y el segundo operando fuente. Operaciones de transferencia de datos, registros, almacenamiento inmediato y almacenamiento de memoria. Son ejemplos de instrucciones que requieren dos operandos por ejemplo: MOV AX, 8 Este es un ejemplo de operando inmediato. Aquí, el dato a ser manipulado se incluye como operando fuente y se desplaza al registro AX, u operando destino. CAMPO COMENTARIO: El campo comentario es él ultimo de los cuatro campos y puede ser uno de los mas útiles. El campo comentario se utiliza para documentar internamente el código fuente del ensamblador. Los comentarios son ignorados por el ensamblador y son útiles solo al listar el código fuente. Si un comentario se incluye con una instrucción de operación entonces debe estar separado del último campo por al menos un espacio en blanco y comenzar con un punto y coma (;). Un comentario debe ser utilizado para describir aquellas líneas de código fuente que no son comprensibles inmediatamente. Por ejemplo: MOV AH, 45H ; PARAMETO PARA LEER UN CARACTER Como se muestra, el comentario explica por que el registro AH esta siendo cargado con 45H. En este caso, el 45H es utilizado para disparar la acción apropiada cuando se llame a una interrupción. Un programa bien documentado no tiene por que incluir comentarios en cada instrucción y se recomienda: No utilizar en los comentarios abreviados cuyo significado resulte poco claro. Comentar todas las definiciones de símbolos, reservas de memoria, definición de constantes, propósito del programa, los subprogramas, etc. Hacer comentarios escuetos, los detalles se adjuntan en documentos apartes. Comentar todos los puntos clave *no comentar instrucciones sin dificultad en su comprensión. Indicar en el comentario la tarea que se quiere ejecutar con una instrucción o secuencia de instrucciones. Los delimitadores que podemos usar en cada una de las líneas son: : (Dos puntos) después de una etiqueta (Espacio) entre el código de operación y operando , (Coma) entre operando del mismo tipo ; (Punto y coma) precede un comentario “FORMATO DE UN PROGRAMA” Existen ciertas normas para crear un programa en lenguaje ensamblador. La primera se refiere al formato interno de cada instrucción del lenguaje. Toda línea de código debe digitarse bajo ciertas reglas, las cuales pueden considerarse como la sintaxis propia del lenguaje. La segunda norma es el formato externo del programa, que puede equipararse con una capa que rodeara al programa para ayudar a definir su entorno. EL FORMATO EXTERNO El formato externo esta formado por varios comandos clave que permiten establecer el entorno operativo del programa. En el ensamblador se utilizan dos formatos básicos para desarrollar la programación. En un método se utilizan modelos de memoria y, en el otro definiciones de segmento completo. Los modelos de memoria son exclusivos del programa MASM para ensamblador. Las definiciones de segmento completo son comunes para casi todos los ensambladores. El método que utiliza los modelos de memoria son más fáciles de usar, pero las definiciones de segmento completo ofrecen mayor control de la tarea del lenguaje ensamblador. USO DE MODELOS: PROGRAMA QUE DESPLIEGA UNA CADENA EN LA PANTALLA ; Nombre del programa ; Fecha de creación ; Autor ; Objetivo ; COMANDO DE ENSAMBLE ; COMANDO DE ENLACE ; COMANDO DE EJECUCION ; Define el modelo de memoria ; . MODEL SMALL . CODE Empieza : MOV AX, @DATA MOV DS, AX MOV DX, OFFSET MENSAJE MOV AH, 9 INT 21H MOV AX, 4c00h INT 21h .DATA Mensaje DB 'Hola, .qué tal!.$' .STACK END Empieza : EJ02CA03.ASM : ABRIL 19 DE 1993 : J.A. Rojas Ponce : Desplegar una cadena : MASM EJ02CA03 ; : LINK EJ02CA03 ; : EJ02CA03 [ENTER] ; Modelo de memoria ; Area de código ; Etiqueta de comienzo de programa ; Inicializa Ds con la ; Dirección de @DATA ; Dirección de mensaje ; en DX para poder desplegarlo ; a través de la Int 21 de MS-DOS . ; regresa a MS-DOS por medio de la ; Función 4c. ; Segmento de datos ; Cadena a desplegar ; Se necesita una PILA. ; Fin del programa El primer comando .MODEL SMALL, es una directiva simplificada que le indica al ensamblador como serán accesadas las instrucciones y los datos (a través de sus segmentos específicos). El comando .CODE, también es una directiva simplificada, le indica al ensamblador que el siguiente grupo de instrucciones es el código a ejecutar. Las dos instrucciones siguientes lo único que hacen es regresar el control a MS-DOS (después de ejecutar el programa) siguiendo ciertas normas, que en este caso consisten en cargar el registro AX con el código 4C00H (código de terminación del programa) y después hacer la indicación a través de la interrupción 21H. El comando .STACK indica que se necesita reservar un espacio de memoria para las operaciones de pila, y finalmente el comando END Empieza señala el final del bloque de código y por END el final del programa. Para crear un programa ejecutable en MASM teclee los siguientes comandos en la línea de interacción de MS-DOS: C:\MASM Ej00ca03 C:\LINK Ej00ca03 DEFINICION DE SEGMENTO COMPLETO Aquí un programa parece ser más largo pero esta más estructurado que en el método de modelo para inicializar un programa. El primer segmento definido es el segmento de PILA, A continuación el segmento de DATOS y que sirve para definir cada uno de los datos a usar en el programa. Y por ultimo el segmento de CODIGO. ; PROGRAMA EJEMPLO STACK SEGMENT PARA ‘STACK’ DB 64 DUP STACK ENDS DATA SEGMENT PARA 'DATA' MESAGE DB ‘AHORA LO QUE ABUNDA ES EL AMOR’,’$’ DATA ENDS CODE SEGMENT PARA CODE MAIN PROC FAR ASSUME CS: CODE, DS: DATA, ES: DATA, SS: STACK PUSH DS SUB AX,AX PUSH AX MOV AX, DATA MOV DS, AX MOV ES, AX ; RUTINA PARA IMPRIMIR LA CADENA LEA DX, MESAGE MOV AH, 09 INT 21H RET MAIN ENDP CODE ENDS END Para crear un programa ejecutable se puede utilizar el ensamblador Turbo Asemblet, para esto teclee los siguientes comandos en la línea de interacción de MS-DOS: C:\TASM Ejemplo1 C:\TLINK Ejemplo1 Estructura de un programa en ensamblador Ya hemos estudiado algunas partes del programa en lenguaje ensamblador. Sólo nos falta conocer la parte más importante: las instrucciones. Pero antes de eso, veremos cual es la estructura básica de un algoritmo en código ensambladora con la finalidad de ener claro cual es la estructura de todos los elemento que acabamos de ver. Esto lo veremos más a fondo cuando veamos todo lo concerniente a la programación. Esta estructura contiene los siguientes elementos o partes que deben ser codificadas: - Comentario descriptivo del programa (opcional, pero recomendable) - Definir el microcontrolador que se usará (con las directrices LIST e INCLUDE). - Introducir las opciones de compilación (que serán vistas más adelante)(opcional) - Establecer las constantes que se usarán (con la directriz EQU). - Reservar espacios de memoria (directriz RES) (si es necesario) - Configurar los puertos - Desarrollar el programa - Poner comentarios Y su estructura es: Figura 41. Esquema de un programa Hemos visto la estructura general. Ahora veremos la posición de los elementos del código por columnas. Estas se dividen en cuatro: - Columna 1: Etiqueta. Las etiquetas se rigen por las siguientes normas: · Debe situarse en la primera columna · Debe contener únicamente caracteres alfanuméricos · El máximo de caracteres es de 31 - Columna 2: - Columna 3: Son los registros (f, l o k , b y w) donde se almacenarán los resultados y con los que se operará - Columna 4: Comentario. Aquí se situará cualquier comentario personalizado que deseemos. Estos son útiles para saber qué hace un programa sin tener que descifrar el código entero. El compilador (ensamblador) ignorará todo texto más allá del carácter punto y coma “;”. Estos comentarios generalmente se sitúan en la cuarta columna para describir la acción de una línea de código, pero pueden situarse en cualquier parte de programa para describir cualquier otro evento, siempre que estén después del carácter “;” (semicolon en inglés). Operación. En esta columna se situarán las instrucciones Veamos más detenidamente cual es su posición: Figura 42. Esquema de un programa por columnas Normalmente las columnas son separadas por una tabulación. El espacio mínimo entre dos columnas es de un carácter, que puede ser un espacio en vez de una tabulación. http://usuarios.lycos.es/sfriswolker/pic/seis.htm ver archivo: EstructuraEnsamblador.pdf 1.4.1 Data segment. DS, Data Segment: Es el segmento destinado a ser usado junto con BX, SI y DI para apuntar a una dirección de memoria. También puede ser usado con BP y SP, pero hace falta expresarlo concretamente. DS = Registro de segmento de datos. Contiene la dirección del segmento de datos, es decir, el área de memoria donde se encuentran los datos del programa. DS: Data Segment (Segmento de Datos) Este registro selecciona una sección de 64 Kb. que se dedica generalmente a colocar en ella nuestras variables, por lo cual toma su nombre: sección de memoria dedicada a datos. 1.4.2 Snack segment. SS, Stack Segment: El segmento de Stack; junto con SP apuntan a la última posición que fue utilizada para "depositar" datos en el Stack. SS = Registro de segmento de pila. Contiene la dirección del segmento de pila. http://www.pablin.com.ar/computer/cursos/varios/introasm.htm SS: Stack Segment (Segmento de Pila) Este registro selecciona la región de 64 Kb. que va a contener la pila del sistema. Como su nombre lo indica, tendremos una estructura de datos, con política LIFO (Last In, First Out = El último elemento en entrar, es el primero en salir), con instrucciones básicas PUSH y POP para su manejo. Esta estructura es usada por los programas de aplicación, pero también por el procesador para el control de instrucciones que lo requieren, tales como las llamadas a subrutinas yla atención de interrupciones. http://members.tripod.com/~MoisesRBB/arq80x86.html 1.4.3 Code segment. CS = Registro de segmento de código. Contiene la dirección del segmento de código, lo que son las instrucciones del programa. CS: Code Segment (Segmento de Código) Este registro selecciona el área de 64 Kb. que generalmente dedicamos al código. En este caso, el CPU (específicamente, el BIU), siempre toma las instrucciones de esta región de memoria; por lo que cuando requerimos más de 64 Kb. de código (instrucciones), este registro tendrá que moverse, tomando distintos valores según recorremos distintas regiones de memoria. http://victorsanchez2.net/tutoriales/asm/asm001.htm 1.4.4 Instrucciones del programa. La programación en lenguaje maquina es difícil, por ello se necesitan lenguajes que permitan simplificar este proceso. Los lenguajes de bajo nivel han sido diseñados para ese fin. Estos lenguajes dependen de la maquina, es decir, dependen de un conjunto de instrucciones especificas de la computadora. Un lenguaje típico de bajo nivel es el lenguaje ensamblador. En este lenguaje las instrucciones se escriben en códigos alfabéticos conocidos como nemotécnicos (abreviaturas de palabras inglesas o españolas). Así, por ejemplo, nemotécnicos típicos son: ADD suma MPY multiplicar LDA cargar acumulador SUB resta DIV dividir STO almacenar Las palabras nemotécnicas son mucho más fáciles de recordar que las secuencias de dígitos 0 y 1. Una instrucción típica en ensamblador puede ser: ADD X, Y, Z Esta instrucción significa que se deben sumar los números almacenados en las direcciones x, y y almacenar el resultado en la dirección z. El programa ensamblador traducirá la instrucción a código de maquina. Por ejemplo: ADD se puede traducir a 1110 x se puede traducir por 1001, y 1010, z por 1011. La instrucción traducida sería: 1110 1001 1010 1011 Después que un programa ha sido escrito en lenguaje ensamblador, se necesita un programa --llamado ensamblador-- que lo traduzca a código máquina: Programa fuente ( lenguaje ensamblador) Ensamblador Programa en lenguaje de maquina Figura 1.12. Programa ensamblador. http://www.monografias.com/trabajos15/el-software/el-software.shtml# 1.4.5 Directivas. Directivas del lenguaje ensamblador Además de los códigos para instrucciones de máquina, lenguajes de ensamblador tienen directivas para ensamblar bloques de datos, y asignar localidades de dirección para instrucciones o código. Estos usualmente tienen una capacidad simbólica simple para definir valores como expresiones simbólicas que se evalúan en tiempo de ensamble, haciendo posible escribir código que es mas fácil de leer y entender. Como la mayoría de lenguajes de computadora, comentarios textuales pueden ser agregados a el código fuente [source code] que es ignorado por la computadora. Estos también usualmente tienen un macro lenguaje integrado para hacer fácil generar piezas complejas de código o datos. En la práctica, la ausencia de comentarios y el reemplazo de símbolos con números actuales hace la interpretación humana de código desensamblado considerable mas difícil que el código fuente original. http://www.geocities.com/SiliconValley/Haven/2037/documentos/Lenguaje _Ensamblador.htm DIRECTIVAS PARA LISTAR: PAGE Y TITLE La directiva PAGE y TITLE ayudan a controlar el formato de un listado de un programa en ensamblador. Este es su único fin, y no tienen efecto sobre la ejecución subsecuente del programa. PAGE. Al inicio de un programa, la directiva PAGE designa el numero máximo de líneas para listar en una pagina y el numero máximo de caracteres en una línea. Su formato general es: PAGE [longitud][, ancho] El ejemplo siguiente proporciona 60 líneas por pagina y 132 caracteres por línea: PAGE 60, 132 El numero de líneas por pagina puede variar desde 10 hasta 255, mientras que el numero de caracteres por línea desde 60 hasta 132. La omisión de PAGE causa que el ensamblador tome PAGE 50, 80. TITLE. Se puede emplear la directiva TITLE para hacer que un titulo para un programa se imprima en la línea 2 de cada pagina en el listado del programa. Puede codificar TITLE de una vez, al inicio del programa. Su formato general es: TITLE Texto. Para el operando texto, una técnica recomendada es utilizar el nombre del programa como se registra en el disco. Por ejemplo: TITLE Prog1 Mi primer programa en ensamblador DIRECTIVA SEGMENT Un programa ensamblado en formato .EXE consiste en uno o mas segmentos. Un segmento de pila define el almacén de la pila, un segmento de datos define los elementos de datos y un segmento de código proporciona un código ejecutable. Las directivas para definir un segmento, SEGMENT y ENDS tienen el formato siguiente: El enunciado SEGMENT define el inicio de un segmento. El nombre del segmento debe estar presente, ser único y cumplir las convenciones para nombres del lenguaje. EL enunciado ENDS indica el final del segmento y contiene el mismo nombre del enunciado SEGMENT. El tamaño máximo de un segmento es de 64K. El operando de un enunciado SEGMENT puede tener tres tipos de opciones: alineación, combinar y clase, codificadas en este formato: nombre SEGMENT alineación combinar ' clase ' TIPO ALINEACION. La entrada alineación indica el limite en el que inicia el segmento. Para el requerimiento típico, PARA, alinea el segmento con el limite de un párrafo, de manera que la dirección inicial es divisible entre 16, o 10H. En ausencia de un operando hace que el ensamblador por omisión tome PARA. TIPO COMBINAR. La entrada combinar indica si se combina el segmento con otros segmentos cuando son enlazados después de ensamblar. Los tipos de combinar son STACK, COMMON, PUBLIC y la expresión AT. Por ejemplo, el segmento de la pila por lo común es definido como: nombre SEGMENT PARA STACK Puede utilizar PUBLIC y COMMON en donde tenga el propósito de combinar de forma separada programas ensamblados cuando los enlaza. En otros casos, donde un programa no es combinado con otros, puede omitir la opción o codificar NONE. TIPO CLASE. La entrada clase, encerrada entre apóstrofos, es utilizada para agrupar segmentos cuando se enlazan. Se utiliza la clase 'code' para el segmento de códigos, 'data' por segmento de datos y 'stack' para el segmento de la pila. El ejemplo siguiente define un segmento de pila con tipos alineación, combinar y clase: nombre SEGMENT PARA STACK 'Stack' DIRECTIVA ASSUME. Un programa utiliza el registro SS para direccionar la pila, al registro DS para direccionar el segmento de datos y el registro CS para direccionar el segmento de código. Para este fin, usted tiene que indicar al ensamblador el propósito de cada segmento en el programa. La directiva para este propósito es ASSEME, codificada en el segmento de código como sigue: OPERACION ASSUME . OPERANDO SS:nompila, DS:nomsegdatos, CS: nomsegcodigo,. . Los operandos pueden aparecer en cualquier orden. Al igual que otras directivas, ASSUME es solo un mensaje que ayuda al ensamblador a convertir código simbólico a código maquina; aun puede tener que codificar instrucciones que físicamente cargan direcciones en registros de segmentos en el momento de la ejecución. 1 PAGE 60,132 2 TITLE P04ASM1 ESTRUCTURA DE UN PROGRAMA .EXE 3;------------------------------------------------------------------------------4 STACKSG SEGMENT PARA STACK 'Stack' 5 ... 6 STACKSG ENDS 7;------------------------------------------------------------------------------8 DATASG SEGMENT PARA 'Data' 9 ... 10 DATASG ENDS 11;------------------------------------------------------------------------------12 CODESG SEGMENT PARA 'Code' 13 BEGIN PROC FAR 14 ASSUME SS:STACKSG, DS:DATASG,CS:CODESG 15 MOV AX, DATASG ;Obtiene la dirección del segmento de datos 16 MOV DS, AX ;Almacena dirección en DS 17 ... 18 MOV AX, 4C00H ;Peticion 19 INT 21H ;Salida al DOS 20 BEGIN ENDP 21 CODESG ENDS 22 END BEGIN http://www.itlp.edu.mx/publica/tutoriales/ensamblador/tem5_1_.htm DIRECTIVAS SIMPLIFICADAS DE SEGMENTOS Los ensambladores de MicroSoft y de Borland proporcionan algunas formas abreviadas para definir segmentos. Para usar estas abreviaturas, inicialice el modelo de memoria antes de definir algún segmento. El formato general (incluyendo el punto inicial) es: .MODEL modelo de memoria El modelo de memoria puede ser TINY, SMALL, MEDIUM, COMPACT o LARGE. Los requisitos para cada modelo son: Puede utilizar cualquiera de estos modelos para un programa autónomo (esto es, un programa que no este enlazado con algún otro). El modelo TINY esta destinado para uso exclusivo de programas .COM, los cuales tienen sus datos, código y pila en un segmento. El modelo SMALL exige que el código quepa en un segmento de 64K y los datos en otro segmento de 64K. La directiva .MODELL genera automáticamente el enunciado ASSUME necesario. Los formatos generales (incluyendo el punto inicial) para las directivas que define los segmentos de la pila, de datos y de código son: .STACK [tamaño] .DATA .CODE [nombre] Cada una de estas directivas hace que el ensamblador genere el enunciado SEGMENT necesario y su correspondiente ENDS. Los nombres por omisión de los segmentos (que usted no tiene que definir) son STACK, DATA y TEXT (para el segmento de código). La figura 4.3 proporciona un ejemplo haciendo uso de las directivas simplificadas de segmento. page 60,132 TITLE P04ASM2 (EXE) Operaciones de mover y sumar ;------------------------------------------------------------------------.MODEL SMALL .STACK .DATA 64 ;Se define la pila ;Se definen los datos FLDA DW 250 FLDB DW 125 FLDC DW ? ;------------------------------------------------------------------------.CODE BEGIN PROC MOV (Prog. anterior) MOV ADD MOV MOV INT BEGIN ENDP END ;Se define el segmento de código FAR AX, @data ;Se asigna la dirección de DATASG AX, FLDA AX, FLDB FLDC, AX ;Mover 0250 a AX ;Sumar 0125 a AX ;Almacenar suma en FLDC AX, 4C00H 21H ;Salida a DOS BEGIN ;Fin de procedimiento ;Fin de programa http://www.itlp.edu.mx/publica/tutoriales/ensamblador/tem5_2_.htm Directivas de ensamble (Seudo instrucciones) Pass32 cuenta con algunas palabras reservadas que cumplen tareas especiales para facilitar la programación en ensamblador, estas palabras son llamadas seudo instrucciones o directivas de ensamble. La siguiente es una lista de las directivas de ensamble más utilizadas en Pass32: DB Reserva un byte en memoria DW Reserva una palabra (Word) en memoria o 2 bytes DD Reserva una palabra doble (Double Word) .EQU Se utiliza para reemplazar símbolos por valores PROC-ENDP Se utilizan para declarar procedimientos en los programas .MACRO-ENDM Se utilizan para declarar macros DUP Sirve para inicializar cadenas de caracteres o arreglos numéricos .INCLUDE Se utiliza para obtener datos o subrutinas de otros programas .EXTERN Declara un símbolo como externo, trabaja en conjunto con .INCLUDE .PUBLIC Declara un símbolo como público Los programas incluidos como ejemplos muestran la forma de utilizar estas directivas. http://www.monografias.com/trabajos11/lenen/lenen.shtml 1.5 Procedimiento de ensamble, enlace y ejecución. Proceso de ensamble y ligado de un programa Este proceso es muy sencillo y se describe a continuación: Si está trabajando en MS-DOS siga estos pasos: 1.- Escriba el programa, tal y como aparece en el listado anterior, usando su editor de texto preferido. 2.- Guárdelo con algún nombre y la extensión .ASM. 3.- En el símbolo del MS-DOS escriba lo siguiente C:\PASS32\BIN\>PASS32 Nombre.ASM –t <Enter> 4.Ejecute el programa .COM que se genera. Para probar el programa abra una ventana de MS-DOS y seleccione el programa haciendo doble clic sobre el icono. http://www.monografias.com/trabajos11/lenen/lenen.shtml PROCESO DE ENSAMBLE Y LIGADO DE UN PROGRAMA. Las instrucciones simbólicas que codifica el lenguaje ensamblador son conocidas como el programa fuente. El programa ensamblador es utilizado para traducir el programa fuente en código máquina. Se debe de crear el programa fuente en un editor, por ejemplo el editor de pascal, el cual genera un archivo en código ASCII que debe ser grabado con la extensión .ASM. El archivo generado, es sólo un archivo de texto que no puede ejecutarse, para esto primero debe ensamblarlo y enlazarlo. 1.- El paso de ensamble consiste en la traducción del código fuente en código objeto y la generación de un archivo intermedio .OBJ ó módulo. Una de las tareas del ensamblador es calcular el desplazamiento de cada elemento en el segmento de datos y de cada instrucción en el segmento de código. El ensamblador también crea un encabezado al frente del módulo .OBJ generado, éste aún no está en forma ejecutable. 2.- El paso de enlace implica convertir el módulo .OBJ en un módulo de código de máquina .EXE (ejecutable). 3.- El último paso es cargar el programa para su ejecución. El cargador desecha el encabezado y crea un PSP inmediatamente antes del programa cargando en memoria. COMO ENSAMBLAR UN PROGRAMA FUENTE El programa ensamblador de Microsoft es MASM.EXE, mientras que el programa de Borland es TASM.EXE. Para teclear el comando para ejecutar MASM o TASM en una línea de comando o por medio de peticiones. El formato general para un comando o línea para ensamblar un programa es : MASM/TASM[opciones] fuente [,objeto] [,listado] [,refcruzada] [ ] = Son opcionales. fuente- Es el nombre del archivo que se encuentracon la extension .ASM . objeto.- Prergunta que nombre le voy a dar al archivo .OBJ. listado.-Si se desea crear un archivo que contenga codigo fuente y codigo objeto se debe especificar con la extensión LST. FORMATO GENERAL PARA ENLAZAR LINK/TLINK ARCHOBJ,ARCHEJE,[,ARCHAMPA] [,ARCHIBIB1] LINK=Microsoft TLINK=Borland ARCHOBJ.- Identifica el archivo objeto que ha generado el ensamblador. ARCHEJE.- Se utiliza para archivos ejecutables. DIRECTIVAS DE ENSAMBLE El lenguaje ensamblador permite usar diferentes enunciados que permiten controlar la manera en que un programa ensambla y lista, llamados directivas. Las directivas que usa el lenguaje ensamblador son: PAGE DD .LIST ASSUME END ERR DW COMMENT PROC DT EQU ENDP COMM LABEL DQ DATA DATA GROUP INCLUDE SEGMENT CREF ORG PROCED TITLE DFCONST .MODEL ENDS .ALPHA DIRECTIVAS DE DEFINICIÓN DE DATOS (DB,DW,DD,LABEL,EQU,ETC.) DB (definir byte) .- Inicializa un byte completo. Ejemplo : HOLA MESA CARACTER MENSAJE PERSONA HOY TABLA DIVERSOS DB DB DB DB DB DB DB DB 23 0AH ‘G’ ‘SOCIEDAD DE CERRO AZUL’ 0,1,2,3,4,5,6,7,8,9 45 DUP (‘STACK’) 50 DUP (03CH) ‘YES’,34,0FADH DW(Definir palabra).- Se utiliza para definir una variable o tabla (palabra). Ejemplo : TEST1 TEST2 TEST3 TEST4 TEST5 TEST6 DW DW DW DW DW DW 1234 0ABCDH 1,22,333,4444 23,112 23 DUP (100H) 50 DUP ( ?) DD(Definir doble palabra).- Se utitliza para definir una variable o tabla para inicializar una función de memoria. Ejemplo : CIELO DD ROSA DD FLOR DD SUMA DD REALES DD DECIMAL DD TABLA DD FRACC DD 0FFFFFFFFH 0H 1,22,333,4444,55555 45H + 23H 2.17654 4.567E12 50 DUP (03CH) 0.12345 DQ (Definir cuadruple palabra).-Iniacializa 4 palabras. Acepta valores de constantes,numéricos,tablas. Ejemplo : TABLA MULTI SUMA LARGO DQ DQ DQ DQ 100,2000,30000,400000 1000 DUP (45.678) -6.345E -5 + 5.0001E45 7989*12H DT (Definir decabyte).-Inicializa 10 bytes de asignación de mamoria. Ejemplo : TABLA MULTI SUMA LARGO DT DT DT DT 100,2000,30000,500000 1000 DUP (45.678) -6.345 E -5 + 5,0001E45 7989*12 DBIT (Bit).- Define tipo de bits. TEST1 TEST2 TEST3 TEST4 DBIT DBIT DW DW 0001B 011001B 1B,0B,1B 0B EQU.- Inicializa una variable con un valor especifico. Ejemplo : SUMA DIEZ ... EQU EQU CONTADOR 10 INC SUMA ADD SUMA,DIEZ ALPHA.- Ordena alfabeticamente los segementos. Se coloca al inicio del programa para listar los elementos en forma ordenada. PUBLIC.- Se utiliza para enlazar segmentos dentro de un programa o programas externos. Formato : Programa que llama : DSEG1 SEGMENT PARA ‘CODE’ PUBLIC CONVAL ... CONVAL DW ? DSEG1 ENDS Subprograma llamado : EXTRN (NOM : TIPO ) EXTRN CONVAL : FAR DSEG2 SEGMENT PARA ‘CODE’ ... MOV ... DSEG2 ENDS ORG.- Nos permite indicar la dirección especfica de segementos de memoria. Sirve para cambiar la dirección inicial de localidades, dandole una dirección especifica de los segmentos. Ejemplo : ORG FLDX FLDY FLDY2 ORG 0 DB ? DW ? DB ? $+5 00 01 02 04 COMMENT.- Sirve para delimitar comentarios. Ejemplo : COMMENT DELIMITADOR DE COMENTARIO ... DELIMITADOR DE COMENTARIO NOMBRE GROUP NOM-SEG,....- Ayuda a agrupar con un nombre a varios segmentos. Ejemplo : GROUP SEG1 ASSUME ... SEDG1 SEG2 ASSUME ... SEG GROUP NOM-SEG1SEG2 SEGMET PARA ‘DATA’ DS : GROUPX ENDS SEGMENT PARA ‘DATA’ DS : GROUPX ENDS LABEL.- Redefine el atributo de una variable. Formato : Nombre REDEFB REDEFW FIELDB LABEL Especificador DW 2532H LABEL WORD DB 25H DB 32H . . . MOV AL,REDEFB MOV BX,REDEFW DIRECTIVAS DE DEFINICIÓN DE MEMORIA (PROC, SEGMENT, ASSUME, END, ETC.) PROC.- Indica la definición de un procedimiento pude llevar un atributo FAR o NEAR. Formato : NOMSEGTO NOMPROC ... NOMPROC NOMSEGTO SEGMENT PROC FAR/NEAR ENDP ENDS ; CODE ; UN PROCEDIMIENTO ; ; DENTRO ; DEL SEGMENTO DE CÓDIGO SEGMET (Directiva de memoria).- Nos indica que tanta memoria va a ocupar y sirva para segmentar la memoria en pila, código, datos y extras. Formato : NOMBRE . . NOMBRE SEGMENT (OPCIONES) ENDS ; INICIA EL SEGMENTO ; FIN DE SEGMENTO END.-Indica fin de programa. Formato : END ENDS.- Indica fin de segmento. Formato : ENDS ENDP.- Indica fin de procedimiento. Formato : ENDP ASSUME.- Se utiliza para direccionar cada uno de los registros del segmento a la dirección del segmento (DS,SS,ES,FS,CS). Formato : ASSUME CS : CODESG, DS. :DATASG, SS : STACK ;... ES : DATASG DIRECTIVAS PARA LIGADO DE UN PROGRAMA TLINK .- Convierte un programa fuente a un archivo .OBJ en un archivo .EXE. Se puede teclear LINK o TLINK. Enlazando con una línea de comando o por medio de peticiones. La línea de comando es : LINK/TLINK archobj, archeje, [, archmapa] [,archbibl] Archobj.-Identifica al archivo objeto generado por el ensamblador. El enlazador supone la extensión .OBJ de modo que no tiene que introducirla. Unidad, subdirectorio y nombre de archivo pueden ser iguales o diferentes del archivo fuente. Archeje.- Estipula que se genera un archivo .EXE. Unidad, subdirectorio y nombre del archivo pueden ser iguales o diferentes del archivo fuente. Archmapa.- Estipula que se genera un archivo con extensión MAP que indica la ubicación relativa y el tamaño de cada segmento y cualquier error que LINK haya encontrado. Un error común es el fallo al definir un segmento de pila. Introducir CON (por consola) le indica al enlazador que muestre el mapa en la pantalla ( en lugar de escribirlo en un disco) de forma que se pueda ver el mapa inmediatamente para los errores. Archbibl.- Estipula la opción de bibliotecas, que no necesita en estos primeros pasos de programación en lenguaje ensamblador. DIRECTIVAS DE CONTROL DE LISTADO .LIST y .XLIST.- Se utilizan para controlar el listado del archivo. .LIST.- Permite listar los códigod fuente y objeto. .XLIST.-Indica hasta donde se detiene el listado del archivo. Formato : .LIST, .XLIST Ejemplo: .XLIST MYCODE MYPROC SEGMENT PARA ‘CODE’ PROC FAR ASSUME CS : MYCODE, DS : MYDATA,SS : STACK PUSH SUB PUSH PAGE.-Directiva para listar, inicializa la longitud y achura de cada página de listado. Formato: PAGE [LONGITUD] , [ANCHO] Ejemplo: PAGE 60,132 TITLE.- Permite que sea introducido un título en la segunda línea de cada página de listado. Formato : TITLE (cadena) Ejemplo: TITLE PROGRAMA EJEMPLO SUBTTL CHEQUEO DE CODIGOS DE ERROR STACK SEGMENT PARA STACK DB 64 DUP (´MIPILA´) STACK ENDS .... FUNCIONAMIENTO INTERNO. Enseguida se ilustra un ejemplo muy claro de cómo sería un programa en Lenguaje Ensamblador. Para sumar 5+ 7. 1. Necesitamos 3 casillas, dos para los datos 5 y 7 y uno para depositar el resultado. 2. Definir las operaciones y su orden, así como obtener una codificación adecuada 3. Introducir los datos en memoria. Ocupamos una instrucción para llevar el contenido de una celda al acumulador (o registro contenido en la CPU); otra para hacer la suma y otra para regresar el contenido del acumulador a una celda de la memoria. El formato de la instrucción para llevar el contenido de una celda al acumulador(le llamaremos CARGA_AC) será como sigue: CARGA_AC Dirección Donde: CARGA_AC = al nombre de la instrucción. Dirección =celda de memoria cuyo valor queremos llevar al acumulador CARGA_AC, es el nombre mnemónico que daremos a la instrucción, debemos asignarle un código numérico interno, sin importar ahora cual sea este, ocupará el contenido de una celda de la memoria. Así mismo, la dirección será un número que ocupará un lugar en otra celda. Es decir, CARGA_AC ocupará dos celdas: Una para el código de la operación y otra para la dirección a la que hace referencia. GUARDA_AC, Dirección; que deposita el valor del acumulador en una celda de la memoria(esta es la inversa de la anterior). SUMA Dirección; que suma de memoria descrita por la dirección: Ahora nuestro diccionario será como sigue: INSTRUCCIÓN CARGA_AC CODIGO LONG. DE LA INTERNO INSTRUCCIÓN 21 2 GUARDA_AC 96 2 SUMA 57 2 RESTA 42 2 Ahora escribiremos el programa en forma tabular. En la parte izquierda del renglón pondremos la instrucción mnemónica seguida de la dirección a la que haga referencia, luego pondremos su equivalente en el código interno extraído del diccionario y, por último, describiremos el renglón para lo que sigue suponemos que la celda 21 contiene un 5 y que la celda 22 contiene un 7. INSTRUCCIÓN DIRECCION CODIGO COMENTARIOS CARGA_AC 21 2121 Colocamos el 1er. Núm. En el acumulador SUMA 22 5722 Efectuamos la suma Dejamos el resultado en la casilla 23 GUARDA_AC 23 9623 ALTO ….. 70 finaliza ALTO= Instrucción nueva para lograr que cuando se ejecute la secuencia, llegue a su fin. Esta instrucción toma una sola casilla de memoria, ya que no requiere hacer referencia a algunas direcciones. Observemos dos nuevos programas; uno escrito en lenguaje mnemónico y otro escrito en lenguaje numérico. Llamaremos programa fuente al primero y programa objeto al segundo. PROGRAMA FUENTE: Esta escrito en un lenguaje cercano al del ser humano. PROGRAMA OBJETO: Este ya está traducido al lenguaje que habla la máquina. En este caso escribimos ambos programas, pero en un caso general, el programador escribirá el programa fuente y la computadora lo traducirá al lenguaje objeto. PASOS PARA EJECUCCION DE UN PROGRAMA El programa objeto, entonces es: "21215722962370" que, obviamente, ininteligible para un ser humano. Este programa funciona para sumar cualquier par de números, siempre y cuando resida en las casillas 21y22. Ahora introduciremos el programa en la memoria de la computadora, para que pueda ejecutarse luego. ¿en que sección de la memoria lo vamos a cargar?, decidimos hacerlo a partir de la celda 10 (o cualquiera que este desocupada y disponga de suficientes celdas secuenciales vacías). La forma en que quedaría nuestro programa objeto en la memoria es: Cod. 21 21 57 22 96 23 70 05 07 ¿ …. Dir. 10 11 12 13 14 15 16 …. 21 22 23 …. Mem. Ya cargado nuestro programa objeto a partir de la celda 10 de la memoria, tenemos que encontrar un procedimiento para lograr que la computadora comience la ejecución del mismo y así poder obtener los resultados deseados. La unidad de control (UC) ejecutará el programa de la siguente forma: 1. Cuando el programa empieza a partir de la celda 10, indicaremos a la unidad de control que esta celda contiene las primeras instrucciones por medio de un apuntador que recibe el nombre de CONTADOR DE PROGRAMA(CP). Este es un paso externo, no forma parte del programa. 2. La unidad de control ejecutará el paso a) e irá a la casilla 10 para leer su contenido que es 21. 3. La unidad de control ejecutará el paso b) con lo que decodifica el 21 recién leído y determina que se trata de una operación CARGA_AC. 4. La unidad de control ejecutará el paso c) con lo que efectuará la operación de carga. Para esto la computadora debe ir a la celda 11 y extraer su contenido, pero ahora ya no lo considerará como instrucción, sino como dirección, por lo cual irá a la celda 21 para extraer el valor que contenga(que es 5). Esta instrucción completa "21 21" puede leerse así: "Carga el acumulador con el valor que esté contenido en la celda cuya dirección aparece a la derecha de donde estás leyendo ahora. 5. La unidad de control ejecuta el paso d) para luego ejecutar todo el ciclo de nuevo. Esto es un ciclo infinito, que sólo terminará cuando se ejecute la instrucción ALTO. El CP=12, o sea apuntará a la celda 12. 6. Se ejecutará(por segunda vez) el paso a) de la unidad de control. Como CP=12, se leerá esa celda, que contiene un 57. 7. Se decodifica esa instrucción que resulta ser SUMA_AC, por lo que el CP se preparará para apuntar a la siguiente celda. 8. Se ejecuta la instrucción 57 con lo que se añade el contenido de la celda 22 al acumulador(la dirección 22 reside en la celda 13, que es a la que actualmente apunta el contador de programa como resultado del paso anterior).Ahora el acumulador contendrá un 12 (o sea 5+7). 9. El CP se actualiza para apuntar a la celda 14, en la cual reside el código de la siguiente instrucción. 10. Se lee la celda número 14 y se extrae su contenido, 96. 11. Se decodifica la instrucción, que resulta ser GUARDA_AC, por lo que CP se alista para apuntar a la siguiente celda, que contendrá la dirección de la celda en donde se va a guardar el contenido del acumulador. 1.6 Entorno de programación. Unidad 2. Elementos del lenguaje 2.7 Instrucciones lineales. 2.7.1 Movimiento. Movimiento de datos En todo programa es necesario mover datos en la memoria y en los registros de la UCP; existen diversas formas de hacer esto: puede copiar datos de la memoria a algun registro, de registro a registro, de un registro a una pila, de la pila a un registro, transmitir datos hacia dispositivos externos asi como recibir datos de dichos dispositivos. Este movimiento de datos está sujeto a reglas y restricciones. Algunas de ellas son las que se citan a continuación. No es posible mover datos de una localidad de memoria a otra directamente, es necesario primero mover los datos de la localidad origen hacia un registro y luego del registro a la localidad destino. No se puede mover una constante directamente a un registro de segmentos, primero se debe mover a un registro de la UCP. Es posible mover bloques de datos por medio de las instrucciones movs, que copia una cadena de bytes o palabras; movsb que copia n bytes de una localidad a otra; y movsw copia n palabras de una localidad a otra. Las dos últimas instrucciones toman los valores de las direcciones definidas por DS:SI como grupo de datos a mover y ES:DI como nueva localización de los datos. Para mover los datos tambien existen las estructuras llamadas pilas, en este tipo de estructuras los datos se introducen con la instrucción push y se extraen con la instrucción pop En una pila el primer dato introducido es el último que podemos sacar, esto es, si en nuestro programa utilizamos las instrucciones: PUSH AX PUSH BX PUSH CX Para devolver los valores correctos a cada registro al momento de sacarlos de la pila es necesario hacerlo en el siguiente orden: POP CX POP BX POP AX Para la comunicación con dispositivos externos se utilizan el comando out para mandar información a un puerto y el comando IN para leer información recibida desde algun puerto. La sintaxis del comando OUT es: OUT DX,AX Donde DX contiene el valor del puerto que se utilizará para la comunicación y AX contiene la información que se mandará. La sintaxis del comando IN es: IN AX,DX Donde AX es el registro donde se guardará la información que llegue y DX contiene la dirección del puerto por donde llegará la información. http://usuarios.lycos.es/patricio/ensam/ensam2.htm La orden MOV. La función de la orden MOV es, como su nombre da a entender, "mover" un valor. Pongamos un ejemplo: MOV AX,BX Esta orden en lenguaje Ensamblador, copiará el contenido de BX en AX, conservando el valor de BX. He aquí algún ejemplo más: MOV AX,DS MOV ES,AX MOV DX,AX MOV AL,DH Como se ve, no se puede realizar MOV AL,BX, ya que en AL no cabe BX También se puede introducir un valor directamente en un registro. Sería el caso de: MOV AX,0FEA2h MOV BL,255 MOV DH,01110101b Pongamos ejemplos de cómo se utiliza la numeración. El primero era un número hexadecimal, el segundo decimal, que no va acompañado por nada para indicarlo, y el tercero binario, con la b al final. A veces, para representar un número decimal, se pone una 'd' al final (p.ej., 10d) Más utilidades de MOV. Podemos transferir bytes que estén en memoria a un registro, o de un registro a memoria. Vayamos con los ejemplos: MOV AX,[BX] Y pongamos que en BX esté 0EEEEh. En vez de transferir a AX el valor 0EEEEh, le transferiremos el valor que haya en la posición de memoria CS:BX; si CS por ejemplo vale 0134h y BX 03215h, transferiríamos el byte que hay en 0134:03215h y el siguiente a AX. Se puede hacer también al revés; MOV [AX],CX Escribiríamos en la dirección de memoria CS:AX el valor de CX. Y también podremos usar valores numéricos: MOV AX,[2325h] (lo que hay en CS:2325h) MOV AX,DS:[2325h] (el valor en DS:2325h) MOV AX,DS:DX (el valor en DS:DX) MOV DX,CS:CX (a DX, valor en CS:CX) MOV BX,CS:1241h (a BX, valor en CS:1241h) Muchas veces, se utiliza Word Ptr o Byte Ptr, que aclaran el tamaño a transferir: MOV AL,BYTE PTR [BX+SI-30h] MOV AX,WORD PTR [BX+DI] Como acabamos de ver, es posible hacer "sumas" de valores al buscar una dirección en memoria. Otros ejemplos serían: MOV AX,[BX+3] MOV [BP+SI],AH Y para acabar este apartado, he aquí una tablilla de ejemplos sobre MOVs que se pueden hacer: º Formatos de la instrucción MOV º Ejemplos º º MOV reg,reg º MOV AX,BX º º MOV mem,reg º MOV [BX],AL º º MOV reg,mem º MOV CH,[40FFh] º º MOM mem,inmed º MOV BYTE PTR [DI],0 º º MOV reg,inmed º MOV BX,0FFFFh º º MOV segreg,reg16 º MOV DS,AX º º MOV mem,segreg º MOV [SI],ES º º MOV segreg,mem º MOV SS,[1234h] º reg: registro mem:memoria inmed:número inmediato segreg: registro de segmento reg16: registro de 16 bits Y vista la orden MOV, seguimos adelante. La instrucción de transferencia de datos por excelencia es: MOV destino, fuente entendiendo por fuente el contenido que se va a transferir a una determinada zona o registro de memoria denominada destino. Esta instrucción, por tanto, nos va a permitir transferir informacion entre registros y memoria, memoria y registros y entre los propios registros utilizando alguno de los diferentes modos de direccionamiento. Con la instrucción MOV diremos que se pueden realizar todo tipo de movimientos teniendo en cuenta las siguientes restricciones: 1.- No se puede realizar una transferencia de datos entre dos posiciones de memoria directamente, por esta razón, siempre que queramos efectuarlas tendremos que utilizar un registro intermedio que haga de puente. Por ejemplo, para hacer la operacion DATO1 <-- DATO2, la instrucción MOV DATO2,DATO1 sería incorrecta. Lo que sí sería correcto sería utilizar el registro DX, u otro, como puente y hacer: MOV DX,DATO1 MOV DATO2,DX 2.- Tampoco se puede hacer una transferencia directa entre dos registros de segmento. Por eso, como en el caso anterior, si fuera preciso se utilizaría un registro como puente. 3.- Asimismo, tampoco se puede cargar en los registros de segmento un dato utilizando direccionamiento inmediato, es decir, una constante, por lo que también habrá que recurrir a un registro puente cuando sea preciso. Una instrucción util pero no imprescindible es: XCHG DATO1, DATO2 que intercambia los contenidos de las posiciones de memoria o registros representadospor DATO1 y DATO2. Por ejemplo, si queremos intercambiar los contenidos de los registros AX y BX, podemos hacer: MOV AUX, AX MOV AX, BX MOV BX, AUX en donde AUX es una variable auxiliar que hace de puente, o simplemente utilizar: XCHG AX, BX Las restricciones que presenta esta operación es que no se pueden efectuar intercambios directamente entre posiciones de memoria ni tampoco entre registros de segmento. La instrucción XLAT tabla carga en el registro AL el contenido de la posición [BX][AL], en donde el registro BX ha de apuntar al comienzo de una tabla. Dichio de otra manera, AL hace de índice de la tabla y de almacén destino del contenido de la tabla. Por ejemplo, el siguiente programa: DATOS SEGMENT TABLA DB 2,3,5,8,16,23 DATOS ENDS CODIGO SEGMENT MOVE BX, OFFSET TABLA ; Inicializa BX con la dirección donde comienza la tabla MOVE AL, 5 XLAT TABLA CODIGO ENDS hace que al final el contenido de AL se 16 ya que es el 5to. elemento de la tabla y AL antes de XLAT TABLA contenia el valor 5. Para finalizar con las instrucciones de transferencia veremos un grupo de tres instrucciones: - LEA o cargar dirección efectiva. - LDS o cargar el puntero en DS. - LES o cargar el puntero en ES. denominadas de transferencia de direcciones. La primera, LEA, carga el desplazamiento u OFFSET correspondiente al operando fuente en el operando destino. Por ejemplo, la instrucción MOVE BX, OFFSET TABLA del ejemplo anterior sería equivalente a LEA BX, TABLA. La segunda, LDS, se utiliza para cargar el valor del segmento de una variable en el registro DS y el desplazamiento correspondiente en el registro o posición de memoria indicada en la instrucción. Por ejemplo, la instrucción LDS BX, NUM1 haría esquemáticamente lo siguiente: La tercera y ultima de las instrucciones, LES, es similar a LDS, con la única salvedad de que el valor del segmento se carga sobre el registro de segmento ES en vez del DS. 2.7.2 Pila. Instrucciones para la pila La pila es un grupo de localidades de memoria que se reservan con la finalidad de proporcionar un espacio para el almacenamiento temporal de información. La pila de los programas es del tipo LIFO (Last In First Out, Ultimo en entrar, Primero en salir). Para controlar la pila el microprocesador cuenta con dos instrucciones básicas: Push (Meter) y Pop (sacar). El formato de estas instrucciones es el siguiente: Push operando Pop operando Cuando se ejecuta la instrucción Push, el contenido del operando se almacena en la ultima posición de la pila. Por ejemplo, si AX se carga previamente con el valor 5, una instrucción Push AX almacenaría el valor 5 en la ultima posición de la pila. Por otro lado la instrucción Pop saca el último dato almacenado en la pila y lo coloca en el operando. Siguiendo el ejemplo anterior, la instrucción Pop BX obtendría el número 5 y lo almacenaría en el registro BX. El siguiente ejemplo muestra como implementar la instrucción XCHG por medio de las instrucciones Push y Pop. Recuerde que la instrucción XCHG intercambia el contenido de sus dos operandos. .COMMENT Programa: PushPop.ASM Autor: Juan Carlos Guzmán C. Descripción: Este programa demuestra el uso de las instrucciones para el manejo de la pila, implementando la instrucción XCHG con Push y Pop * .MODEL tiny .CODE Inicio: ;Punto de entrada al programa Mov AX,5 ;AX=5 Mov BX,10 ;BX=10 Push AX ;Pila=5 Mov AX,BX ;AX=10 Pop BX ;BX=5 Mov AX,4C00h ;Terminar programa y salir al DOS Int 21h ; END Inicio END INSTRUCCIONES DE MANEJO DE LA PILA. POP (extraer de la pila) Sintaxis: POP destino Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Transfiere el elemento palabra que se encuentra en lo alto de la pila (apuntado por SP) al operando destino que a de ser tipo palabra, e incrementa en dos el registro SP. La instrucción POP CS, poco útil, no funciona correctamente en los 286 y superiores. Ejemplos: pop pop ax pepe PUSH (introduce en la pila) Sintaxis: PUSH origen Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Decrementa el puntero de pila (SP) en 2 y luego transfiere la palabra especificada en el operando origen a la cima de la pila. El registro CS aquí sí se puede especificar como origen, al contrario de lo que afirman algunas publicaciones. Ejemplo: push cs POPF (extrae los indicadores de la pila) Sintaxis: POPF Indicadores: OF x DF x IF x TF x SF x ZF x AF x PF x CF x Traslada al registro de los indicadores la palabra almacenada en la cima de la pila; a continuación el puntero de pila SP se incrementa en dos. PUSHF (introduce los indicadores en la pila) Sintaxis: PUSHF Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Decrementa en dos el puntero de pila y traslada a la cima de la pila el contenido de los indicadores. http://meltingpot.fortunecity.com/uruguay/978/libro/04.html LA PILA La pila es una especie de "almacén de variables" que se encuentra en una dirección determinada de memoria. Nos encontramos con dos órdenes básicas respecto a la pila, que son PUSH y POP. La orden PUSH empuja una variable a la pila, y la orden POP la saca. Sin embargo, no podemos sacar el que queramos, no podemos decir "quiero sacar el valor de DX que he metido antes y que fue el cuarto que metí", por ejemplo. La estructura de la pila se denomina LIFO, siglas inglesas que indican 'Last In First Out'. Esto significa que al hacer un POP, se sacará el último valor introducido en la pila. Veamos esto con unos ejemplos: PUSH DX ; Mete en la pila el contenido de DX PUSH CX ; Y ahora el contenido de CX POP AX ; Ahora saca el último valor introducido ( CX ) ;y lo coloca en AX. POP BP ; Y ahora saca en valor anterior introducido, que ;es el contenido de DX cuando hicimos el PUSH DX ;y se lo asigna a BP. Ahora, una rutina algo más detallada: MOV DX,0301h ; DX vale ahora 0301 hexadecimal. PUSH DX ; Empuja DX a la pila. SP se decrementa en dos. MOV DX,044C4h ; Ahora DX vale 044C4h POP CX ; Y con esto, CX vale 0301 hexadecimal, el valor que habíamos introducido con anterioridad. Dijimos en la segunda línea: SP se decrementa en dos. Cuando por ejemplo ejecutamos un .COM, SS es el segmento del programa (o sea, igual que CS, y si no han sido modificados, DS y ES), y SP apunta al final, a 0FFFFh. Cuando empujamos un valor a la pila, SP se decrementa en dos apuntando a 0FFFDh, y en esta dirección queda el valor introducido. Cuando lo saquemos, se incrementará de nuevo en dos el valor de SP, y el valor se sacará de la pila. Se puede operar con esta instrucción con los registros AX, BX, CX, DX, SI, DI, BP, SP, CS, DS y ES; sin embargo no se puede hacer un POP CS, sólo empujarlo a la pila. He aquí un ejemplo de lo que hace en realidad un POP en términos de MOVs; aunque sea un gasto inútil de código, tiene su aplicación, por ejemplo, para saltarse la heurística en un antivirus, que busca un POP BP y SUB posterior. Partamos de que hay cierto valor en la pila que queremos sacar. MOV BP,SP ; Ahora BP es igual al offset al que apunta SP MOV BP,Word ptr [BP] ; Y ahora BP vale el contenido del offset al ;que apunta, que al ser el offset al que apunta ;el de pila, será el valor que sacaríamos ;haciendo un POP BP. ADD SP,2 ; Para acabarlo, sumamos dos al valor de offset ;de la pila. Y esto es lo que hace un POP BP, simplemente. Para ver lo que hace un PUSH no habría más que invertir el proceso. Veámoslo. SUB SP,2 MOV BP,SP MOV Word ptr[BP],DX Como última recomendación, hay que tener bastante cuidado con los PUSH y POP, sacar tantos valores de la pila como se metan, y estar pendiente de que lo que se saca es lo que se tiene que sacar. La pila bien aprovechada es fundamental para hacer programas bien optimizados, ya que entre otras cosas las instrucciones PUSH y POP solo ocupan un byte. Es por ejemplo mucho mejor usar un PUSH al principio y un POP al final en vez de dejar partes de código para almacenar variables, más velocidad y menos tamaño. Y finalmente, hay otras dos órdenes interesantes respecto a la pila, PUSHF y POPF, que empujan el registro (16 bits) de flags y lo sacan, respectivamente. 2.7.3 Matemáticos. Instrucciones aritméticas Las cuatro operaciones aritméticas básicas son suma, resta, multiplicación y división. La mayoría de las computadoras proporcionan instrucciones para las cuatro operaciones. Algunas computadoras pequeñas sólo tienen las instrucciones suma y, tal vez, resta. La multiplicación y la división deben generarse mediante subrutinas del software. Las cuatro operaciones aritméticas básicas son suficientes para formular soluciones a problemas científicos cuando se expresan en términos de métodos de análisis numérico. En la tabla 8-7 se proporciona una lista de las instrucciones aritméticas típicas. La instrucción de incrementar suma 1 al valor almacenado en un registro o palabra de memoria. Una característica común de las operaciones de incrementar, cuando se ejecutan en registros de procesador, es que un número binario que contiene sólo dígitos 1, produce un resultado de solo dígitos 0 cuando se incrementa. La instrucción para Decrementar resta 1 de un valor: almacenado en un registro o palabra de memoria. Un número con sólo dígitos 0, produce un número con sólo dígitos 1 cuando se decrementa. TABLA 8-7 Instrucciones aritméticas típicas NOMBRE MNEMONICO Incrementar INC Decrementar DEC Sumar ADD Restar SUB Multiplicar MUL Dividir DIV Sumar con acarreo ADDC Restar con préstamo SUBB Negar (complemento a 2) NEG Las instrucciones sumar, restar, multiplicar y dividir pueden estar disponibles para diferentes tipos de datos. Los tipos de datos que se considera que están en los registros del procesador durante la ejecución de estas operaciones aritméticas, se incluyen en la definición del código de operación. Una instrucción aritmética puede especificar datos de punto fijo o flotante. datos binarios o decimales, datos de precisión única o de doble precisión. Los diferentes tipos de datos se presentaron en el capítulo 3. No es extraño encontrar computadoras con tres o más instrucciones de suma: una para enteros binarios, una para operandos de punto flotante y una para operandos decimales. Enseguida, se muestran los mnemónicos para tres instrucciones de suma que especifican tipos de datos diferentes. ADDI Sumar dos números enteros de punto flotante ADDF Sumar dos números de punto flotante ADDD Sumar dos números decimales en BCD. Los algoritmos para operaciones con enteros, punto flotante y aritmética ~ decimal se desarrollan en el capítulo 10. La cantidad de bits en cualquier registro es de extensión finita y, por lo tanto, los resultados de las operaciones aritméticas son de precisión finita. Algunas computadoras proporcionan operaciones de hardware de doble precisión, en las cuales el tamaño de cada operando es de dos palabras de memoria. La mayoría de las computadoras pequeñas proporcionan instrucciones especiales para facilitar la aritmética de doble precisión. Un flip-flop especial de acarreo se utiliza para almacenar el acarreo de una operación. la instrucción "sumar con acarreo" realiza la suma de dos operandos más el valor del acarreo del cálculo previo. De igual manera, la instrucción "restar con préstamo" resta dos palabras y un préstamo que puede haberse producido de una operación de resta previa. La instrucción negar forma el complemento a 2 de un número, invirtiendo en forma efectiva el signo de un entero cuando se representa en forma de complemento a 2 con signo. http://members.fortunecity.es/roy8/cpu.htm Las instrucciones ADD y SUB Se trata de dos operadores que contiene cualquier lenguaje de programación: la suma y la resta. Tienen dos operandos, uno de destino y otro fuente. Para la suma, se suman los dos operandos y se almacena en el primero (destino), y para la resta, se resta al primero el segundo, almacenándose en destino, el primero. Aquí están algunos formatos de estas instrucciones: ADD AX,BX ; Sumaría AX y BX y lo guardaría en AX ADD [AX],BX ; Suma el contenido de la dirección de AX a BX, ;y se almacena en la dirección de AX ADD AX,[BX] ; Se suman AX y el contenido de la dirección de ;BX, y se almacena ‚esta suma en AX ADD AX,3 ; Lo mismo pero utilizando un valor inmediato ;en vez de la BX señalada anteriormente. SUB CL,DL ; Resta de CL el valor de DL, y se almacena en CL SUB [CX],DX ; Se resta al contenido de la dirección de CX ;el valor de DX, y se almacena en la dir. de CX SUB CX,23h ; Se resta de CX el valor 23h, y queda en CX el ;resultado ¿Y si el resultado excede lo que puede contener el byte, o la palabra?. Esto se puede saber mediante los flags, que trataremos más adelante. También es resaltable que separa con ; los comentarios. Bien, ésta es la manera en Ensamblador de poner comentarios, como sería en Basic la orden "REM", o en C la convención "/* [...] */" 5.-3.- NEG, NOT y operaciones lógicas Neg pone el registro o el lugar al que apunta en memoria en negativo según la aritmética de complemento a dos tal que : NEG AX o NEG [AX] Not es la que, como vimos, "invierte" los valores de los bits. Y el resto de operaciones lógicas también las vimos anteriormente. Pondremos ahora tan sólo su sintaxis: NOT SI ; (o Not AX, etc.,... o sea, con un registro) NOT Word ptr es:[ax] ; Lo realiza sobre la palabra ( 2 bytes ) ;que se encuentra en es:[ax] AND AX,BX ; Efectúa un AND entre AX y BX, almacenando ;el resultado en AX ( siempre en el primer ;término ) AND [AX],BX ; Lo dicho, pero AX apunta a un lugar de ;memoria AND AX,[BX] AND Byte ptr [15],3 ; Un AND en la dirección :0015 con lo que ;haya ah¡ y el valor "3" OR AX,BX OR [AX],BX OR Byte ptr [15],3 OR DH,55h ;También podría hacerse en el AND, se ;confrontan DH y 55h en un OR. Y todo lo dicho para OR y AND vale para XOR, de tal manera que las operaciones son realizables entre: Registro y registro CX,DX Lugar de memoria y registro [DX],BX Registro y lugar de memoria AX,[SI] Lugar de memoria y número word ptr ES:[AX],0D533h Registro y número AX,0CD32h 5.-4.- Multiplicación y división, MUL y DIV Estas operaciones multiplican al acumulador por el operando indicado. Si el operando es de 8 bits (1 byte), el acumulador es AL. Si el operando es de 16 bits, el acumulador es AX. El resultado se almacena en AX o en el par DX-AX respectivamente, si el operando es de 8 bits o 16 bits. También tendremos que diferenciar entre dos tipos de multiplicaciones y divisiones que entiende el procesador. Los que comienzan con una I operan con números con signo, si queremos usar números negativos, y los que no, con números sin signo. Visto esto, podremos decir que: MUL Byte Ptr [CX] Va a multiplicar el byte que hay en la dirección que marca CX por el contenido que hay en AL, y una vez hecho esto, va a almacenarlo en AX. MUL SI Multiplicaría SI por el contenido de AX, almacenándose en el par AX-DX. La palabra superior, de más valor, se devolvería en DX, y la inferior en AX. IMUL SI Esto y el ejemplo anterior sería lo mismo, sólo que operando con números con signo. Para la división, el dividendo ha de estar en AX, y ser 16 bits por tanto. El divisor se indica en el operando, por ejemplo en DIV BL; este divisor estaría en BL. Se dividiría AX entre BL y el resultado quedaría en AL, quedando el resto en AH. Vamos a ver algún ejemplo. En la división de un número de dieciséis bits entre otro de 8 bits, el cociente y resto serán de 8 bits (1 byte). El dividendo ha de estar en AX, y el divisor es el operando de la instrucción, que puede ser un registro o un sitio en la memoria, y se necesita poner lo de byte ptr. O sea, sería tal que: DIV CL o IDIV BYTE PTR ES:[BP] El resultado se devuelve en AL, y el resto en AH. Si por ejemplo AX valiese 501d y cl valiese 2, al hacer el DIV CL, en AL quedaría 255 y en AH quedaría 1. Se puede dividir también un número de 32 bits (4 bytes) entre otro de 16 bits (2 bytes), con lo que cociente y resto serían de 16 bits. El dividendo estaría formado por el par DX/AX. Al hacer por ejemplo un: DIV SI Se dividiría DX-AX entre SI, almacenándose el resultado en AX, y el resto en DX. Por ejemplo: Si en DX está el valor 003Fh y en AX 5555h, el par sería 3F5555h, con lo que al dividirlo por SI (que pongamos que vale 0CCC4h), se almacenaría en AX el resultado y en DX el resto. http://platea.pntic.mec.es/~jdelucas/ensamblador.htm SUMA Y RESTA. Las instrucciones ADD y SUB realizan sumas y restas sencillas de datos binarios. Los números binarios negativos están representados en la forma de complemento a dos: Invierta todos los bits del numero positivo y sume 1. Los formatos generales para las instrucciones ADD y SUB son: Como en otras instrucciones, no existen operaciones directas de memoria a memoria. El ejemplo siguiente utiliza el registro AX para sumar WORDA a WORDB: WORDA DW 123 ;Define WORDA WORDB DW 25 ;Define WORDB ... MOV AX, WORDA ;Mueve WORDA al AX ADD AX, WORDB ;Suma WORDB al AX MOV WORDB, AX ;Mueve AX a WORDB La figura 6.1. proporciona ejemplos de ADD y SUB para el procesamiento de valores en un byte y en una palabra. El procedimiento B10ADD utiliza ADD para procesar bytes y el procedimiento C10SUB utiliza SUB para procesar palabras. TITLE P13ADD (COM) Operaciones ADD y SUB .MODEL SMALL .CODE ORG 100H BEGIN: JMP SHORT MAIN ;---------------------------------------------------------------------------BYTEA DB 64H ;DATOS BYTEB DB 40H BYTEC DB 16H WORDA DW 4000H WORDB DW 2000H WORDC DW 1000H ;---------------------------------------------------------------------------MAIN PROC NEAR ;Procedimiento principal: CALL B10ADD ;Llama a la rutina ADD CALL C10SUB ;Llama a la rutina SUB INT 21H MAIN ENDP ; Ejemplos de suma (ADD) de bytes: ;---------------------------------------------------------------------------B10ADD PROC MOV AL, BYTEA MOV BL, BYTEB ADD AL, BL ;registro a registro ADD AL, BYTEC ;memoria a registro ADD BYTEA, BL ;registro a memoria ADD BL, 10H ;inmediato a registro ADD BYTEA, 25H ;inmediato a memoria RET B10ADD ENDP ; Ejemplos de resta (SUB) de palabras: ;---------------------------------------------------------C10SUB PROC MOV AX, WORDA MOV BX, WORDB SUB AX,BX ;Registro a registro SUB AX,WORDC ;Memora de registro SUB WORDA, BX ;Registro de memoria SUB BX, 1000H ;Inmediato de registro SUB WORDA, 256H ;Inmediato de memoria RET C10SUB ENDP END BEGIN Desbordamientos Este alerta con los desbordamientos en las operaciones aritméticas. Ya que un byte solo permite el uso de un bit de signo y siete de datos (desde -128 hasta +127), una operación aritmética puede exceder con facilidad la capacidad de un registro de un byte. Y una suma en el registro AL, que exceda su capacidad puede provocar resultados inesperados. *** SUMA *** AAA (ajuste ASCII para la suma) Sintaxis: AAA Indicadores: OF ? DF - IF - TF - SF ? ZF ? AF x PF ? CF x Convierte el contenido del registro AL en un número BCD no empaquetado. Si los cuatro bits menos significativos de AL son mayores que 9 ó si el indicador AF está a 1, se suma 6 a AL, 1 a AH, AF se pone a 1, CF se iguala a AF y AL pone sus cuatro bits más significativos a 0. Ejemplo: add aaa al,bl En el ejemplo, tras la suma de dos números BCD no empaquetados colocados en AL y BL, el resultado (por medio de AAA) sigue siendo un número BCD no empaquetado. ADC (suma con acarreo) Sintaxis: ADC destino, origen Indicadores: OF x DF - IF - TF - SF x ZF x AF x PF x CF x Suma los operandos origen, destino y el valor del indicador de acarreo (0 ó 1) y el resultado lo almacena en el operando destino. Se utiliza normalmente para sumar números grandes, de más de 16 bits, en varios pasos, considerando lo que nos llevamos (el acarreo) de la suma anterior. Ejemplo: adc ax,bx ADD (suma) Sintaxis: ADD destino, origen Indicadores: OF x DF - IF - TF - SF x ZF x AF x PF x CF x Suma los operandos origen y destino almacenando el resultado en el operando destino. Se activa el acarreo si se desborda el registro destino durante la suma. Ejemplos: add add ax,bx cl,dh DAA (ajuste decimal para la suma) Sintaxis: DAA Indicadores: OF ? DF - IF - TF - SF x ZF x AF x PF x CF x Convierte el contenido del registro AL en un par de valores BCD: si los cuatro bits menos significativos de AL son un número mayor que 9, el indicador AF se pone a 1 y se suma 6 a AL. De igual forma, si los cuatro bits más significativos de AL tras la operación anterior son un número mayor que 9, el indicador CF se pone a 1 y se suma 60h a AL. Ejemplo: add daa al,cl En el ejemplo anterior, si AL y CL contenían dos números BCD empaquetados, DAA hace que el resultado de la suma (en AL) siga siendo también un BCD empaquetado. INC (incrementar) Sintaxis: INC destino Indicadores: OF x DF - IF - TF - SF x ZF x AF x PF x CF - Incrementa el operando destino. El operando destino puede ser byte o palabra. Obsérvese que esta instrucción no modifica el bit de acarreo (CF) y no es posible detectar un desbordamiento por este procedimiento (utilícese ZF). Ejemplos: inc inc inc inc al es:[di] ss:[bp+4] word ptr cs:[bx+di+7] ***RESTA*** AAS (ajuste ASCII para la resta) Sintaxis: AAS Indicadores: OF ? DF - IF - TF - SF ? ZF ? AF x PF ? CF x Convierte el resultado de la sustracción de dos operandos BCD no empaquetados para que siga siendo un número BCD no empaquetado. Si el nibble inferior de AL tiene un valor mayor que 9, de AL se resta 6, se decrementa AH, AF se pone a 1 y CF se iguala a AF. El resultado se guarda en AL con los bits de 4 a 7 puestos a 0. Ejemplo: sub aas al,bl En el ejemplo, tras la resta de dos números BCD no empaquetados colocados en AL y BL, el resultado (por medio de AAS) sigue siendo un número BCD no empaquetado. CMP (comparación) Sintaxis: CMP destino, origen Indicadores: OF x DF - IF - TF - SF x ZF x AF x PF x CF x Resta origen de destino sin retornar ningún resultado. Los operandos quedan inalterados, paro los indicadores pueden ser consultados mediante instrucciones de bifurcación condicional. Los operandos pueden ser de tipo byte o palabra pero ambos de la misma dimensión. Ejemplo: cmp cmp bx, mem_pal ch,cl DAS (ajuste decimal para la resta) Sintaxis: DAS Indicadores: OF - DF - IF - TF - SF x ZF x AF x PF x CF x Corrige el resultado en AL de la resta de dos números BCD empaquetados, convirtiéndolo también en un valor BCD empaquetado. Si el nibble inferior tiene un valor mayor que 9 o AF es 1, a AL se le resta 6, AF se pone a 1. Si el nibble mas significativo es mayor que 9 ó CF está a 1, entonces se resta 60h a AL y se activa después CF. Ejemplo: sub das al,bl En el ejemplo anterior, si AL y BL contenían dos números BCD empaquetados, DAS hace que el resultado de la resta (en AL) siga siendo también un BCD empaquetado. DEC (decrementar) Sintaxis: DEC destino Indicadores: OF x DF - IF - TF - SF x ZF x AF x PF x CF - Resta una unidad del operando destino. El operando puede ser byte o palabra. Obsérvese que esta instrucción no modifica el bit de acarreo (CF) y no es posible detectar un desbordamiento por este procedimiento (utilícese ZF). Ejemplo: dec dec ax mem_byte NEG (negación) Sintaxis: NEG destino Indicadores: OF x DF - IF - TF - SF x ZF x AF x PF x CF x Calcula el valor negativo en complemento a dos del operando y devuelve el resultado en el mismo operando. Ejemplo: neg al SBB (resta con acarreo) Sintaxis: SBB destino, origen Indicadores: OF x DF - IF - TF - SF x ZF x AF x PF x CF x Resta el operando origen del operando destino y el resultado lo almacena en el operando destino. Si está a 1 el indicador de acarreo además resta una unidad más. Los operandos pueden ser de tipo byte o palabra. Se utiliza normalmente para restar números grandes, de más de 16 bits, en varios pasos, considerando lo que nos llevamos (el acarreo) de la resta anterior. Ejemplo: sbb sbb ax,ax ch,dh SUB (resta) Sintaxis: SUB destino, origen Indicadores: OF x DF - IF - TF - SF x ZF x AF x PF x CF x Resta el operando destino al operando origen, colocando el resultado en el operando destino. Los operandos pueden tener o no signo, siendo necesario que sean del mismo tipo, byte o palabra. Ejemplos: sub sub al,bl dx,dx *** MULTIPLICACION *** AAM (ajuste ASCII para la multiplicación) Sintaxis: AAM Indicadores: OF ? DF - IF - TF - SF x ZF x AF ? PF x CF ? Corrige el resultado en AX del producto de dos números BCD no empaquetados, convirtiéndolo en un valor BCD también no empaquetado. En AH sitúa el cociente de AL/10 quedando en AL el resto de dicha operación. Ejemplo: mul aam bl En el ejemplo, tras el producto de dos números BCD no empaquetados colocados en AL y BL, el resultado (por medio de AAA) sigue siendo, en AX, un número BCD no empaquetado. IMUL (multiplicación entera con signo) Sintaxis: IMUL origen (origen no puede ser operando inmediato en 8086, sí en 286) Indicadores: OF x DF - IF - TF - SF ? ZF ? AF ? PF ? CF x Multiplica un operando origen con signo de longitud byte o palabra por AL o AX respectivamente. Si origen es un byte el resultado se guarda en AH (byte más significativo) y en AL (menos significativo), si origen es una palabra el resultado es devuelto en DX (parte alta) y AX (parte baja). Si las mitades más significativas son distintas de cero, independientemente del signo, CF y OF son activados. Ejemplo: imul imul bx ch MUL (multiplicación sin signo) Sintaxis: MUL origen (origen no puede ser operando inmediato) Indicadores: OF x DF - IF - TF - SF ? ZF ? AF ? PF ? CF x Multiplica el contenido sin signo del acumulador por el operando origen. Si el operando destino es un byte el acumulador es AL guardando el resultado en AH y AL, si el contenido de AH es distinto de 0 activa los indicadores CF y OF. Cuando el operando origen es de longitud palabra el acumulador es AX quedando el resultado sobre DX y AX, si el valor de DX es distinto de cero los indicadores CF y OF se activan. Ejemplo: mul mul mul byte ptr ds:[di] dx cl *** DIVISION *** AAD (ajuste ASCII para la división) Sintaxis: AAD Indicadores: OF ? DF - IF - TF - SF x ZF x AF ? PF x CF ? Convierte dos números BCD no empaquetados contenidos en AH y AL en un dividendo de un byte que queda almacenado en AL. Tras la operación AH queda a cero. Esta instrucción es necesaria ANTES de la operación de dividir, al contrario que AAM. Ejemplo: aad div bl En el ejemplo, tras convertir los dos números BCD no empaquetados (en AX) en un dividendo válido, la instrucción de dividir genera un resultado correcto. DIV (división sin signo) Sintaxis: DIV origen (origen no puede ser operando inmediato) Indicadores: OF ? DF - IF - TF - SF ? ZF ? AF ? PF ? CF ? Divide, sin considerar el signo, un número contenido en el acumulador y su extensión (AH, AL si el operando es de tipo byte o DX, AX si el operando es palabra) entre el operando fuente. El cociente se guarda en AL o AX y el resto en AH o DX según el operando sea byte o palabra respectivamente. DX o AH deben ser cero antes de la operación. Cuando el cociente es mayor que el resultado máximo que puede almacenar, cociente y resto quedan indefinidos produciéndose una interrupción 0. En caso de que las partes más significativas del cociente tengan un valor distinto de cero se activan los indicadores CF y OF. Ejemplo: div div bl mem_pal IDIV (división entera) Sintaxis: IDIV origen (origen no puede ser operando inmediato) Indicadores: OF ? DF - IF - TF - SF ? ZF ? AF ? PF ? CF ? Divide, considerando el signo, un número contenido en el acumulador y su extensión entre el operando fuente. El cociente se almacena en AL o AX según el operando sea byte o palabra y de igual manera el resto en AH o DX. DX o AH deben ser cero antes de la operación. Cuando el cociente es positivo y superior al valor máximo que puede almacenarse (7fh ó 7fffh), o cuando el cociente es negativo e inferior al valor mínimo que puede almacenarse (81h u 8001h) entonces cociente y resto quedan indefinidos, generándose una interrupción 0, lo que también sucede si el divisor es 0. Ejemplo: idiv idiv bl bx *** CONVERSIONES*** CBW (conversión de byte en palabra) Sintaxis: CBW Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Copia el bit 7 del registro AL en todos los bits del registro AH, es decir, expande el signo de AL a AX como paso previo a una operación de 16 bits. CWD (conversión de palabra a doble palabra) Sintaxis: CWD Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Expande el signo del registro AX sobre el registro DX, copiando el bit más significativo de AH en todo DX. http://meltingpot.fortunecity.com/uruguay/978/libro/04.h tml 2.7.4 Ajustes. Las instrucciones INC y DEC: Son las más básicas a la hora de hacer operaciones con registros: INC incrementa el valor de un registro,o de cualquier posición en memoria, en una unidad, y DEC lo decrementa. Veamos: INC AX Incrementa en uno el valor de AX INC WORD PTR [BX+4] Incrementa la palabra situada en CS:[BX+4] en uno. DEC AX Decrementa AX, le resta uno. DEC WORD PTR [BX+4] Decrementa la palabra situada en CS:[BX+4] en una unidad. Estas dos instrucciones, equivalentes a "a++" en C, nos servirán bastante como contadores para bucles. http://platea.pntic.mec.es/~jdelucas/ensamblador.htm 2.7.5 Comparación. INSTRUCCIONES DE COMPARACION Nos van a servir para realizar las comparaciones, y son: CMP y TEST CMP compara dos registros, o un registro y una dirección de memoria. Tiene el mismo formato que el SUB (por ejemplo CMP AX,BX), tan sólo que ninguno de los registros es alterado. Si por ejemplo son iguales, el flag de cero se pondrá en uno. Es en realidad un SUB del que no se almacena el resultado. TEST, comprobar, se puede realizar con el mismo formato de AND, ya que es equivalente a ella, tan sólo que no se guarda el resultado, aunque sí se modifican los flags. Y en el próximo capítulo veremos cómo se aplican estos flags, y cómo realizar los saltos comparativos. LA INSTRUCCION CMP La instrucción CMP pro lo común es utilizada para comparar dos campos de datos, uno de los cuales están contenidos en un registro. El formato general para CMP es: | [etiqueta:] | CMP | {registro/memoria}, {registro/memoria/inmediato} | El resultado de una operación CMP afecta la banderas AF, CF, OF, PF, SF y ZF, aunque no tiene que probar estas banderas de forma individual. El código siguiente prueba el registro BX por un valor cero: X CMP BX, 00 ;Compara Bx con cero JZ B50 ;Si es cero salta aB50 . ;(Acción si es diferente de cero) . B50: ... ;Destino del salto, si BX es cero Si el BX tiene cero, cmp establece ZF a 1 y puede o no cambiar la configuración de otras banderas. La instrucción JZ (salta si es cero) solo prueba la bandera ZF. Ya que ZF tiene 1 (que significa una condición cero), JZ transfiere el control (salta) a la dirección indicada por el operando B50. Observe que la operación compara el primer operando con el segundo; por ejemplo, el valor del primer operando es mayor que, igual o menor que el valor del segundo operando? LA INSTRUCCION CMPS CMPS compara el contenido de una localidad de memoria (direccionada por DS:SI). Dependiendo de la bandera de dirección, CMPS incrementa o disminuye también los registros SI y DI en 1 para bytes, en 2 para palabras y en 4 para palabras dobles. La operación establece las banderas AF, CF, OF, PF, SF y ZF. Cuando se combinan con un prefijo REP y una longitud en el CX, de manera sucesiva CMPS puede comparar cadenas de cualquier longitud. Pero observe que CMPS proporciona una comparación alfanumérica, esto es, una comparación de acuerdo a con los valores ASCII. Considere la comparación de dos cadenas que contienen JEAN y JOAN. Una comparación de izquierda a derecha, tiene el resultado siguiente: J:J Iguales E:O Diferentes (E es menor) A:A Iguales N:N Iguales Una comparación de los 4 bytes termina con una comparación de N con N (iguales). Ahora ya que los dos nombres no son idénticos, la operación debe terminar tan pronto como la comparación entre 2 caracteres sea diferente. Algunas derivaciones de CMPS son las siguientes: CMPSB. Compara bytes. CMPSD. Compara palabras dobles. CMPSW. Compara palabras. A continuación se muestra la codificación del uso del CMPS y sus derivaciones: TITLE P12CMPST (COM) Uso de CMPS para operaciones en cadenas .MODEL SMALL .CODE ORG 100H BEGIN: JMP SHORT MAIN ;------------------------------------------------------------------------------------NOM1 DB 'Assemblers' ;Elementos de datos NOM2 DB 'Assemblers' NOM3 DB 10 DUP (' ') ;------------------------------------------------------------------------------------MAIN PROC NEAR ;Procedimiento principal CLD ;Izquierda a derecha MOV CX, 10 ;Iniciar para 10 bytes LEA DI, NOM2 LEA SI, NOM1 REPE CMPSB ;Compare NOM1:NOM2 JNE G20 ;No es igual, saltarlo MOV BH,01 ;Igual, fijar BH G20: MOV LEA LEA REPE CX, 10 DI, NOM3 SI, NOM2 CMPSB ;Iniciar para 10 bytes ;Compare NOM2:NOM3 JE MOV G30 BL, 02 ;Igual, salir ;No es igual, fijar BL G30: MOV INT MAIN ENDP END AX, 4C00H 21H ;Salir a DOS BEGIN 2.8 Saltos. Salto. Este término suele aplicarse sobre todo en programación, donde suele distinguirse entre salto incondicional y salto condicional. El salto incondicional sería aquella instrucción del programa que nos envía a otra parte del programa sin tener en cuenta ninguna condición. Al contrario, el salto condicional sería una, o varias instrucciones, que comprobarían primero una condición; si esta condición es cierta salta a una parte del programa, si es falsa a otra. Una instrucción muy conocida en programación (por aparecer en muchos lenguajes) es GOTO, que sería la instrucción de salto incondicional por excelencia. No se suele recomendar mucho su uso (o nada), pues su utilización implica la creación de un código fuente para el programa, poco estructurado. http://www.lawebdelprogramador.com/diccionario/mostrar.php?letra=J Ver archivo:saltos.doc Saltos, ciclos y procedimientos Los saltos incondicionales en un programa escrito en lenguaje ensamblador están dados por la instrucción jmp, un salto es alterar el flujo de la ejecución de un programa enviando el control a la dirección indicada. Un ciclo, conocido tambien como iteración, es la repetición de un proceso un cierto número de veces hasta que alguna condición se cumpla. En estos ciclos se utilizan los brincos condicionales basados en el estado de las banderas. Por ejemplo la instrucción jnz que salta solamente si el resultado de una operación es diferente de cero y la instrucción jz que salta si el resultado de la operación es cero. Por último tenemos los procedimientos o rutinas, que son una serie de pasos que se usaran repetidamente en el programa y en lugar de escribir todo el conjunto de pasos unicamente se les llama por medio de la instrucción call. Un procedimiento en ensamblador es aquel que inicie con la palabra Proc y termine con la palabra ret. Realmente lo que sucede con el uso de la instrucción call es que se guarda en la pila el registro IP y se carga la dirección del procedimiento en el mismo registro, conociendo que IP contiene la localización de la siguiente instrucción que ejecutara la UCP, entonces podemos darnos cuenta que se desv'a el flujo del programa hacia la dirección especificada en este registro. Al momento en que se llega a la palabra ret se saca de la pila el valor de IP con lo que se devuelve el control al punto del programa donde se invoc— al procedimiento. Es posible llamar a un procedimiento que se encuentre ubicado en otro segmento, para ésto el contenido de CS (que nos indica que segmento se está utilizando) es empujado también en la pila. http://usuarios.lycos.es/patricio/ensam/ensam2.htm Hasta este punto los programas que hemos examinado han sido ejecutados en forma lineal, esto es con una instrucción secuencialmente a continuación de otra. Sin embargo, rara vez un programa programable es tan sencillo. La mayoría de los programas constan de varios ciclos en los que una serie de pasos se repite hasta alcanzar un requisito especifico y varias pruebas para determinar que acción se realiza de entre varias posibles. Requisitos como este implican la transferencia de control a la dirección de una instrucción que no sigue de inmediato de la que se esta ejecutando actualmente. Una transferencia de control puede ser hacia adelante, para ejecutar una serie de pasos nuevos, o hacia atrás, para volver a ejecutar los mismos pasos. Ciertas instrucciones pueden transferir el control fuera del flujo secuencial normal añadiendo un valor de desplazamiento al IP. Direcciones Corta, cercana y lejana Una operación de salto alcanza una dirección corta por medio de un desplazamiento de un byte, limitado a una distancia de -128 a 127 bytes. Una operación de salto alcanza una dirección cercana por medio de un desplazamiento de una palabra, limitado a una distancia de -32, 768 a 32, 767 bytes dentro del mismo segmento. Una dirección lejana puede estar en otro segmento y es alcanzada por medio de una dirección de segmento y un desplazamiento; CALL es la instrucción normal para este propósito. La tabla siguiente indica las reglas sobre distancias para la operaciones JMP, LOOP y CALL. Hay poca necesidad de memorizar estas reglas, ya que el uso normal de estas instrucciones en rara ocasión causa problemas. Etiquetas de instrucciones Las instrucciones JMP, Jnnn (salto condicional) y LOOP requieren un operando que se refiere a la etiqueta de una instrucción. El ejemplo siguiente salta a A90, que es una etiqueta dada a una instrucción MOV: JMP A90 ... A90: MOV AH, 00 ... La etiqueta de una instrucción, tal como A90:, terminada con dos puntos (:) para darle atributo de cercana - esto es, la etiqueta esta dentro de un procedimiento en el mismo segmento de código. Cuidado: Un error común es la omisión de los dos puntos. Note que una etiqueta de dirección en un operando de instrucción (como JMP A90) no tiene un carácter de dos puntos. 2.8.1 Incondicional. Incondicional CALL (llamada a subrutina) Sintaxis: CALL destino Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Transfiere el control del programa a un procedimiento, salvando previamente en la pila la dirección de la instrucción siguiente, para poder volver a ella una vez ejecutado el procedimiento. El procedimiento puede estar en el mismo segmento (tipo NEAR) o en otro segmento (tipo FAR). A su vez la llamada puede ser directa a una etiqueta (especificando el tipo de llamada NEAR -por defecto- o FAR) o indirecta, indicando la dirección donde se encuentra el puntero. Según la llamada sea cercana o lejana, se almacena en la pila una dirección de retorno de 16 bits o dos palabras de 16 bits indicando en este último caso tanto el offset (IP) como el segmento (CS) a donde volver. Ejemplos: dir call proc1 dd call 0f000e987h dword ptr dir En el segundo ejemplo, la variable dir almacena la dirección a donde saltar. De esta última manera -conociendo su dirección- puede llamarse también a un vector de interrupción, guardando previamente los flags en la pila (PUSHF), porque la rutina de interrupción retornará (con IRET en vez de con RETF) sacándolos. JMP (salto) Sintaxis: JMP dirección o JMP SHORT dirección Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Transfiere el control incondicionalmente a la dirección indicada en el operando. La bifurcación puede ser también directa o indirecta como anteriormente vimos, pero además puede ser corta (tipo SHORT) con un desplazamiento comprendido entre -128 y 127; o larga, con un desplazamiento de dos bytes con signo. Si se hace un JMP SHORT y no llega el salto (porque está demasiado alejada esa etiqueta) el ensamblador dará error. Los buenos ensambladores (como TASM) cuando dan dos pasadas colocan allí donde es posible un salto corto, para economizar memoria, sin que el programador tenga que ocuparse de poner short. Si el salto de dos bytes, que permite desplazamientos de 64 Kb en la memoria sigue siendo insuficiente, se puede indicar con far que es largo (salto a otro segmento). Ejemplos: jmp jmp etiqueta far ptr etiqueta RET / RETF (retorno de subrutina) Sintaxis: RET [valor] o RETF [valor] Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Retorna de un procedimiento extrayendo de la pila la dirección de la siguiente dirección. Se extraerá el registro de segmento y el desplazamiento en un procedimiento de tipo FAR (dos palabras) y solo el desplazamiento en un procedimiento NEAR (una palabra). si esta instrucción es colocada dentro de un bloque PROC-ENDP (como se verá en el siguiente capítulo) el ensamblador sabe el tipo de retorno que debe hacer, según el procedimiento sea NEAR o FAR. En cualquier caso, se puede forzar que el retorno sea de tipo FAR con la instrucción RETF. Valor, si es indicado permite sumar una cantidad valor en bytes a SP antes de retornar, lo que es frecuente en el código generado por los compiladores para retornar de una función con parámetros. También se puede retornar de una interrupción con RETF 2, para que devuelva el registro de estado sin restaurarlo de la pila. http://meltingpot.fortunecity.com/uruguay/978/libro/04.html Condicional Las siguientes instrucciones son de transferencia condicional de control a la instrucción que se encuentra en la posición IP+desplazamiento (desplazamiento comprendido entre -128 y +127) si se cumple la condición. Algunas condiciones se pueden denotar de varias maneras. Todos los saltos son cortos y si no alcanza hay que apañárselas como sea. En negrita se realzan las condiciones más empleadas. Donde interviene SF se consideran con signo los operandos implicados en la última comparación u operación aritmetico-lógica, y se indican en la tabla como '±' (-128 a +127 ó -32768 a +32767); en los demás casos, indicados como '+', se consideran sin signo (0 a 255 ó 0 a 65535): JA/JNBE JAE/JNB Salto si mayor (above), si no menor o igual (not below or equal), si CF=0 y ZF=0. Salto si mayor o igual (above or equal), si no menor (not below), si CF=0. + + Salto si menor (below), si no superior ni igual (not above or equal), si acarreo, si + JBE/JNA Salto si menor o igual (not below or equal), si no mayor (not above), si CF=1 ó ZF=1. + JCXZ JE/JZ JG/JNLE JGE/JNL JL/JNGE JLE/JNG JNC JNE/JNZ JNO JNP/JPO JNS JO JP/JPE JS Salto si CX=0. JB/JNAE/JC CF=1. Salto si igual (equal), si cero (zero), si ZF=1. Salto si mayor (greater), si no menor ni igual (not less or equal), si ZF=0 y SF=0. Salto si mayor o igual (greater or equal), si no menor (not less), si SF=0. Salto si menor (less), si no mayor ni igual (not greater or equal), si SF<>OF. Salto si menor o igual (less or equal), si no mayor (not greater), si ZF=0 y SF<>OF. Salto si no acarreo, si CF=0. Salto si no igual, si no cero, si ZF=0. Salto si no desbordamiento, si OF=0. Salto si no paridad, si paridad impar, si PF=0. Salto si no signo, si positivo, si SF=0. Salto si desbordamiento, si OF=1. Salto si paridad, si paridad par, si PF=1. Salto si signo, si SF=1. ± ± ± ± SALTOS INCONDICIONALES Empecemos por el salto sin condiciones, con el que podremos cambiar el control a cualquier punto del programa. Sería como el "Goto" del Basic, simplemente transferir el control a otro punto del programa. La orden es JMP (de Jump, salto) Si se recuerdan los registros CS:IP, se podrá ver qué es lo que hace realmente la instrucción, y no es más que incrementar o decrementar IP para llegar a la zona del programa a la que queremos transferir el control (IP es el Offset que indica la zona de memoria que contiene la siguiente instrucción a ejecutar, y CS el segmento). El formato más sencillo para el salto sería JMP 03424h, lo que saltaría a esa zona. Pero es algo complejo calcular en qué dirección va a estar esa instrucción, con lo que utilizaremos etiquetas. He aquí un ejemplo: MOV AX,0CC34h MOV CL,22h JMP PALANTE VUELVE: CMP BX,AX JMP FIN PALANTE: MOV BX,AX JMP VUELVE FIN: XOR CX,CX Ahora comentaremos un poco el programa. Tras la primera instrucción, AX vale 0CC34h, y tras la segunda, CL vale 22h. Después se realiza un salto a la instrucción etiquetada con "PALANTE". La etiqueta ha de estar continuada por dos puntos ':', y puede ser llamada desde cualquier lugar del programa. También podremos hacer un MOV AX,[PALANTE], como hacíamos antes con un MOV AX,[BX], pero asignando a AX el valor que haya en la dirección en la que está "PALANTE". El caso es que tras el salto a "PALANTE", se copia el valor del registro BX en AX, y se vuelve a "VUELVE". Se realiza una comparación entre AX y BX, que pondrá el flag de cero a 1 (recordemos la anterior lección), se saltará a "FIN", donde tan sólo se realizará la orden Xor CX,CX cuyo resultado, por cierto, es poner CX a cero, tenga el valor que tenga. Volvamos con la sintaxis del JMP con algunos ejemplos de cómo utilizarlo: JMP 100h Salta a la dirección 100h. Un archivo .COM comienza normalmente en esa dirección, así que es posible verlo en algunos virus. JMP 542Ah:100h Salta a la dirección 100h pero del segmento 542Ah. JMP SHORT 223Ah Salto corto a la dirección 223Ah. JMP NEAR 55AAh Salto cercano, es diferente al corto JMP [100h] Salta a la dirección contenida en 100h. Sin embargo es un error, ya que no se especifica si es cercano, lejano, si se lee un solo byte,es decir, esta instrucción no es válida. JMP WORD PTR [BX] Ahora sí vale. Salta a la dirección contenida en la palabra (dos bytes) a la que apunta BX. O sea, si BX valiese 300h y en 300h los dos bytes fuesen 0CC33h, el JMP saltaría a esta dirección. JMP DWORD PTR [BX+SI+5] Dword son 32 bits, o sea, un salto lejano. Y saltaría al contenido en la dirección de memoria a la que apuntan la suma de BX,SI y 5. Veamos el significado de los saltos lejanos, cercanos y cortos. El salto corto se realiza entre el punto en el que se está y +127 o -128, o sea, la cantidad que se puede contener en un byte con signo. A veces es necesario indicar que se trata de salto corto, cercano o lejano. El salto cercano se realiza contando como distancia el contenido de dos bytes, o sea, que el rango sería desde 32767 a -32768 bytes de distancia. Y el lejano se realiza contando como distancia el contenido de cuatro Bytes. Por ejemplo, es incorrecto que haya en la dirección 100h una instrucción que diga JMP SHORT 500h, ya que la distancia no corresponde a un salto corto. Además, el salto dependiendo de que sea cercano, corto o largo se codifica de manera diferente en modo hexadecimal. La instrucción JMP (Salto incondicional) Una instrucción usada comúnmente para la transferencia de control es la instrucción JMP (jump, salto, bifurcación). Un salto es incondicional, ya que la operación transfiere el control bajo cualquier circunstancia. También JMP vacía el resultado de la instrucción previamente procesada; por lo que, un programa con muchas operaciones de salto puede perder velocidad de procesamiento. El formato general para JMP es: | [etiqueta] | JMP | dirección corta, cercana o lejana | Una operación JMP dentro del mismo segmento puede ser corta o cercana (o de manera técnica, lejana, si el destino es un procedimiento con el atributo FAR). En su primer paso por un programa fuente, el ensamblador genera la longitud de cada instrucción. Sin embargo, una instrucción JMP puede ser de dos o tres bytes de longitud. Una operación JMP a una etiqueta dentro de -128 a + 127 bytes es un salto corto. El ensamblador genera un byte para la operación (EB) y un byte para el operando. El operando actúa como un valor de desplazamiento que la computadora suma al registro IP cuando se ejecuta el programa. El ensamblador ya puede haber encontrado el operando designado (un salto hacia atrás) dentro de -128 bytes, como en: A50: ... JMP A50 En este caso, el ensamblador genera una instrucción de maquina de dos bytes. Una JMP que excede -128 a 127 bytes se convierte en un salto cercano, para que el ensamblador genere un código de maquina diferente (E9) y un operando de dos bytes (procesadores 8088/8086) o un operando de cuatro bytes (procesadores 80386 y posteriores). En un salto hacia adelante, el ensamblador aun no ha encontrado el operando designado: JMP A90 ... A90: Ya que algunas versiones del ensamblador no saben en este punto si el salto es corto o cercano, generan de forma automática una instrucción de tres bytes. TITLE Page 60,132 P08JUMP (COM) Uso de JMP para iterar .MODEL SMALL .CODE MAIN ORG PROC MOV MOV MOV 100H NEAR AX,01 BX,01 CX,01 ADD ADD SHL JMP ENDP END AX, 01 BX, AX CX, 1 A20 ;Iniciación de AX, ;BX y ;CX a 01 A20: MAIN ;Sumar 01 a AX ;Sumar AX a BX ;Multiplicar por dos a CX ;Saltar a la etiqueta A20 MAIN La instrucción LOOP La instrucción LOOP, requiere un valor inicial en el registro CX. En cada iteración, LOOP de forma automática disminuye 1 de CX. Si el valor en el CX es cero, el control pasa a la instrucción que sigue; si el valor en el CX no es cero, el control pasa a la dirección del operando. La distancia debe ser un salto corto, desde -128 hasta +127 bytes. Para una operación que exceda este limite, el ensamblador envía un mensaje como "salto relativo fuera de rango". El formato general de la instrucción LOOP es: | [etiqueta:] | LOOP | dirección corta | El siguiente programa muestra el funcionamiento de la instrucción LOOP. TITLE Page 60,132 P08LOOP (COM) Ilustración de LOOP .MODEL SMALL .CODE ORG 100H MAIN PROC MOV MOV MOV MOV NEAR AX,01 BX,01 CX,01 CX,10 ADD ADD SHL LOOP MOV ENDP END AX, 01 BX, AX DX, 1 A20 AX, 4C00H A20: MAIN ;Iniciación de AX, ;BX y ;CX a 01 ;Iniciar ;Número de iteraciones ;Sumar 01 a AX ;Sumar AX a BX ;Multiplicar por dos a DX ;Iterar si es diferente de cero ;Salida a DOS MAIN Existen dos variaciones de la instrucción LOOP, ambas también decrementan el CX en 1. LOOPE/LOOPZ (repite el ciclo mientras sea igual o repite el ciclo mientras sea cero) continua el ciclo mientras que el valor en el CX es cero o la condición de cero esta establecida. LOOPNE/LOOPNZ (repite el ciclo mientras no sea igual o repite el ciclo mientras sea cero) continua el ciclo mientras el valor en el CX no es cero o la condición de cero no esta establecida. 2.8.2 Condicional. SALTOS CONDICIONALES ¿Recuerdan el IF-THEN-ELSE, o el FOR, o el WHILE-DO ? Bien, pues aquí está lo que suple a estas instrucciones en lenguaje Ensamblador. Se basan completamente en los flags, pero están simplificados. Los saltos podrían resumirse en un modo "Basic" de la manera IF-THEN-GOTO de forma que cuando se cumple una condición se salta a un sitio determinado. He aquí los tipos de saltos condicionales (las letras en mayúsculas son las instrucciones): JO: Jump if overflow. Salta si el flag de desbordamiento está a uno JNO: Jump if not overflow. Salta si el flag de desbordamiento está a cero. JC, JNAE, JB: Los tres sirven para lo mismo. Significan: Jump if Carry, Jump if Not Above or Equal y Jump if Below. Saltan por lo tanto si al haber una comparación el flag de acarreo se pone a 1; es entonces equivalente a < en una operación sin signo. Vamos, que se compara así: CMP 13h,18h, saltará, ya que 13h es menor que 18h. También se suelen usar para detectar si hubo fallo en la operación, ya que muchas interrupciones al acabar en fallo encienden el carry flag. JNC, JAE, JNB: Otros tres que valen exactamente para lo mismo. Jump if not Carry, Jump if Above or Equal y Jump if Not Below. Saltan por tanto si al haber una comparación el flag de acarreo vale 0, o sea, es equivalente al operador >=. En la comparación CMP 0,0 o CMP 13h,12h saltará, ya que el segundo operando es MAYOR O IGUAL que el primero. JZ o JE: Jump if Zero o Jump if Equal. Salta si el flag de cero está a 1, o sea, si las dos instrucciones comparadas son iguales. Saltaría en el caso CMP 0,0 JNZ o JNE: Jump if Not Zero o Jump if Not Equal. Salta si el flag de cero est a 0, o sea, si las dos instrucciones comparadas no son iguales. JBE o JNA: Jump if Below or Equal o Jump if Not Above. Saltaría si en resultado de la comparación el primer miembro es menor o igual que el segundo ( <= ) JA o JNBE: Jump if Above o Jump if Not Below of Equal. Justo lo contrario que la anterior, salta si en el resultado de la comparación el primer miembro es mayor al segundo. JS: Jump if Sign. Salta si el flag de signo está a uno. JNS: Jump if Not Sign. Salta si el flag de signo está a cero. JP, JPE: Jump if Parity o Jump if Parity Even. Salta si el flag de paridad está a uno. JNP, JPO: Jump if Not Parity, Jump if Parity Odd. Salta si el flag de paridad está a cero. JL, JNGE: Jump if Less, Jump if Not Greater of Equal. Salta si en el resultado de la comparación, el primer número es inferior al segundo, pero con números con signo. JGE, JNL: Jump if Greater or Equal, Jump if Not Less. Salta si en el resultado de la comparación, el primer número es mayor o igual que el segundo, pero con números con signo. JLE, JNG: Jump if Lower or Equal, Jump if Not Greater. Salta si en el resultado de la comparación, el primer número es menor o igual que el segundo, pero con números con signo. JG, JNLE: Jump if Greater, Jump if Not Lower or Equal. Salta si en el resultado de la comparación, el primer número es mayor que el segundo, para números con signo. Veamos algunos ejemplos de los más utilizados: MOV AX,1111h MOV BX,1112h CMP AX,BX ; AX es menor que BX JB tirapalante ; Saltar a tirapalante HLT ; Esta orden bloquea el ordenador, halt tirapalante: DEC BX ; Ahora BX valdr 1111h CMP AX,BX ; Ahora valen igual JNE Acaba ; No saltará, ya que son iguales JE Continua ; Esta vez si Continua: DEC BX ; Ahora BX vale 1110h CMP AX,BX JE Acaba ; No son iguales, por tanto no saltará JB Acaba ; No es menor, tampoco salta JG Acaba ; Es mayor, ahora SI saltará Acaba: XOR AX,AX XOR BX,BX ; AX y BX valen ahora cero. Espero que con esto haya aclarado un poco la utilidad de los saltos. Evidentemente, ahora al escribir sabemos cuando uno es menor o mayor, pero a veces mediante interrupciones sacaremos valores que no conoceremos al ir a programar, o quizá lo hagamos de la memoria, y querremos comprobar si son iguales, etcétera. Por cierto, que en los saltos condicionales se puede hacer como en los incondicionales, o sea, formatos como: JE 0022h JNE 0030h JNO AL Sin embargo, estamos limitados a saltos cortos, o sea, de rango a 127 bytes hacia delante o 128 hacia atrás, no pudiendo superar esta distancia. INSTRUCCIONES DE SALTO CONDICIONAL El ensamblador permite usar una variedad de instrucciones de salto condicional que transfieren el control dependiendo de las configuraciones en el registro de banderas. Por ejemplo, puede comparar dos campos y después saltar de acuerdo con los valores de las banderas que la comparación establece. El formato general para el salto condicional es: | [etiqueta:] | Jnnn | dirección corta | Como ya se explico la instrucción LOOP disminuye el registro CX; si es diferente de cero, transfiere el control a la dirección del operando. podría reemplazar el enunciado LOOP A20 de la figura anterior con dos enunciados - uno que decremente el CX y otro que realice un salto condicional: DEC CX ;Equivalente a LOOP JNZ A20 ... DEC y JNZ realizan exactamente lo que hace LOOP. DEC decrementa en 1 CX y pone a 1 o a 0 la bandera de cero (ZF) en el registro de banderas. Después JNZ prueba la configuración de la bandera de cero; si el CX es diferente de cero, el control pasa a A20, y si el CX es cero el control pasa a la siguiente instrucción hacia abajo Datos con signo y sin signo Distinguir el propósito de los saltos condicionales debe clarificar su uso. El tipo de datos (sin signo o con signo) sobre los que se realizan las comparaciones o la aritmética puede determinar cual es la instrucción a utilizar. Un dato sin signo trata todos los bits como bits de datos; ejemplos típicos son las cadenas de caracteres, tal como nombres o direcciones, y valores numéricos tal como números de cliente. Un dato con signo trata el bit de mas a la izquierda como un signo, en donde 0 es positivo y 1 es negativo. En el ejemplo siguiente, el AX contiene 11000110 y el BX contiene 00010110. La siguiente instrucción CMP AX, BX compara el contenido de AX con el contenido del BX. Para datos sin signo, el valor AX es mayor; sin embargo, para datos con signo el valor AX es menor a causa del signo negativo. Saltos con base en datos sin signo Las instrucciones siguientes de salto condicional se aplican a datos sin signo: Cada una de estas pruebas las puede expresar en uno de dos códigos simbólicos de operación. Saltos con base en datos con signo Las instrucciones siguientes de salto condicional se aplican a datos con signo: Pruebas aritméticas especiales Las siguientes instrucciones de salto condicional tienen usos especiales: No espere memorizar todas estas instrucciones; sin embargo, como recordatorio note que un salto para datos sin signo es igual, superior o inferior, mientras que un salto para datos con signo es igual, mayor que o menor. Los saltos que prueban banderas de acarreo, de desbordamiento y de paridad tienen propósitos únicos. 2.9 Tipos de ciclos. Gestión de bucle LOOP (bucle) Sintaxis: LOOP desplazamiento Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Decrementa el registro contador CX; si CX es cero, ejecuta la siguiente instrucción, en caso contrario transfiere el control a la dirección resultante de sumar a IP + desplazamiento. El desplazamiento debe estar comprendido entre -128 y +127. Ejemplo: mov cx,10 bucle: ....... ....... loop bucle Con las mismas características que la instrucción anterior: LOOPE/LOOPZ Bucle si igual, si cero. Z=1 y CX<>0 LOOPNE/LOOPNZ Bucle si no igual, si no cero. Z=0 y CX<>0 BUCLES He aquí el equivalente al FOR-TO-NEXT en Ensamblador, se trata de la orden LOOP. Lo que hace esta orden es comparar CX con cero; si es igual, sigue adelante, si no lo es, vuelve al lugar que se indica en su operando decrementando CX en uno. Por lo tanto, CX será un contador de las veces que ha de repetirse el bucle. Veamos un ejemplo: MOV CX,0005h bucle: INC DX CMP DX,0000h JE Acaba LOOP bucle Acaba: ... Veamos como funciona este programa. Se mueve a CX el valor 5h, que van a ser las veces que se repita el bucle. Ahora, llegamos al cuerpo del bucle. Se incrementa DX y se compara con 0, cuando es igual salta a "Acaba". Si llega a la orden LOOP, CX se decrementará y saltará a bucle. Esto se repetirá cinco veces. En fin, que el programa acabará en el grupo de instrucciones de "Acaba" cuando la comparación dé un resultado positivo o cuando el bucle se haya repetido cinco veces. También tiene la limitación de que sólo realiza saltos cortos, así como puede usarse como el JMP, de la forma: LOOP 0003h LOOP [AL] En resumen, la orden LOOP es la equivalente a CMP CX,0/JNZ par metro, donde par metro es el operando de LOOP. 2.10 Operadores Lógicos. Operaciones lógicas y aritméticas Las instrucciones de las operaciones lógicas son: AND, not, or y xor, éstas trabajan sobre los bits de sus operandos. Para verificar el resultado de operaciones recurrimos a las instrucciones cmp y test. Las instrucciones utilizadas para las operaciones algebraicas son: para sumar add, para restar sub, para multiplicar mul y para dividir div. Casi todas las instrucciones de comparación están basadas en la información contenida en el registro de banderas. Normalmente las banderas de este registro que pueden ser directamente manipuladas por el programador son la bandera de dirección de datos DF, usada para definir las operaciones sobre cadenas. Otra que tambien puede ser manipulada es la bandera IF por medio de las instrucciones sti y cli, para activar y desactivar respectivamente las interrupciones. http://usuarios.lycos.es/patricio/ensam/ensam2.htm INSTRUCCIONES DE OPERACIONES LÓGICAS A NIVEL DE BIT. AND (y lógico) Sintaxis: AND destino, origen Indicadores: OF 0 DF - IF - TF - SF x ZF x AF ? PF x CF 0 Realiza una operación de Y lógico entre el operando origen y destino quedando el resultado en el destino. Son válidos operandos byte o palabra, pero ambos del mismo tipo. Ejemplos: and and ax,bx bl,byte ptr es:[si+10h] NOT (no lógico) Sintaxis: NOT destino Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Realiza el complemento a uno del operando destino, invirtiendo cada uno de sus bits. Los indicadores no resultan afectados. Ejemplo: not ax OR (O lógico) Sintaxis: OR destino, origen Indicadores: OF 0 DF - IF - TF - SF x ZF x AF ? PF x CF 0 Realiza una operación O lógico a nivel de bits entre los dos operandos, almacenándose después el resultado en el operando destino. Ejemplo: or ax,bx TEST (comparación lógica) Sintaxis: TEST destino, origen Indicadores: OF 0 DF - IF - TF - SF x ZF x AF ? PF x CF 0 Realiza una operación Y lógica entre los dos operandos pero sin almacenar el resultado. Los indicadores son afectados con la operación. Ejemplo: test al,bh XOR (O exclusivo) Sintaxis: XOR destino, origen Indicadores: OF 0 DF - IF - TF - SF x ZF x AF ? PF x CF 0 Operación OR exclusivo a nivel de bits entre los operandos origen y destino almacenándose el resultado en este último. Ejemplo: xor di,ax http://meltingpot.fortunecity.com/uruguay/978/libro/04.html Instrucciones lógicas y de manipulación de bits. Las instrucciones lógicas ejecutan operaciones binarias sobre series de bits almacenadas en registros. son útiles para manipular bits individuales o un grupo de bits que represente información en código binario. Las instrucciones lógicas consideran en forma separada cada bit del operando y la tratan como una variable booleana. Mediante una aplicación adecuada de las instrucciones lógicas, es posible cambiar los valores de bits, emplear un es que un grupo de bits o insertar nuevos valores de bits en los operandos almacenados en registros o palabras de memoria. Algunas instrucciones lógicas y de manipulación de bits típicas se listan en la tabla 8-8. La instrucción de borrar hace que el operando especificado se sustituya con 0. La instrucción complementar produce el complemento a 1 al invertir todos los bits del operando. Las instrucciones AND, OR y XOR producen las operaciones lógicas correspondientes sobre los bits individuales de los operandos. Aunque realizan operaciones booleanas, deben considerarse que las instrucciones ejecutan operaciones de manipulación de bits cuando se usan en instrucciones de computadora. Existen tres operaciones de manipulación de bits posibles: un bit seleccionado puede borrarse a 0, activarse en 1, o puede complementarse. Por lo general, se aplican las tres instrucciones lógicas para hacer sólo eso. La instrucción AND se utiliza para borrar un bit o un grupo seleccionado de bits de un operando. Para cualquier variable booleana x, las relaciones xb0 = 0 y xb1 = x dictan que una variable binaria ala que se le haya aplicado un AND con un 0 produce un 0; pero la variable no cambia de valor cuando se le aplica un AND con un 1. Por lo tanto, la instrucción AND puede utilizarse para borrar bits de un operando en forma selectiva al aplicar AND al operando con otro operando que tiene 0 en las posiciones de bit que deben borrarse. La instrucción AND también se llama máscara, y aritmética porque enmascara o inserta 0 en una parte seleccionada de un operando. La instrucción OR se utiliza para activar un bit o un grupo seleccionado de bits de un operando. Para cualquier variable booleana x, las relaciones x + 1 = 1 y x + 0 = x dictan que una variable binaria ala que se le aplica OR con un 1 produce un 1; pero la variable no cambia cuando se .le aplica OR con un 0. Por lo tanto, la instrucción OR puede utilizarse para activar bits de un operando en forma selectiva, al aplicar OR con otro operando Con dígitos 1 en las posiciones de bits que deben activarse en 1. TABLA 8-8 Instrucciones lógicas y de manipulación de bits típicas NOMBRE MNEMONICO Borrar CLR complementar COM Aplicar la función AND AND Aplicar la función OR OR Aplicar la función OR exclusiva XOR Desactivar acarreo CLRC Activar acarreo SETC Complementar acarreo COMC Habilitar interrupción EI Deshabilitar interrupción DI De igual forma, la instrucción XOR se utiliza para complementar bits de un operando en forma selectiva. Esto se debe a la relación boolena x(+) 1= x' y x (+) 0 = x. Por lo tanto, una variable binaria se complementa cuando se le aplica XOR con un 1, pero no cambia en valor cuando se le aplica XOR con un 0. En la sección 4-5 se muestran ejemplos numéricos de las tres operaciones lógicas. En la tabla 8-8 se incluyen algunas otras instrucciones de manipulación de bits. Los bits individuales (como los de un acarreo) pueden borrarse, activarse o complementarse con las instrucciones apropiadas. Otro ejemplo es un flip-flop que controla la opción de interrupción y se habilita o deshabilita mediante instrucciones de manipulación de bits. http://members.fortunecity.es/roy8/cpu.htm 2.11 Desplazamiento. Ver archivo: algo_desplazamientos.pdf INSTRUCCIONES DE ROTACIÓN Y DESPLAZAMIENTO. RCL (rotación a la izquierda con acarreo) Sintaxis: RCL destino, contador Indicadores: OF x DF - IF - TF - SF - ZF - AF - PF - CF x Rotar a la izquierda los bits del operando destino junto con el indicador de acarreo CF el número de bits especificado en el segundo operando. Si el número de bits a desplazar es 1, se puede especificar directamente, en caso contrario el valor debe cargarse en CL y especificar CL como segundo operando. No es conveniente que CL sea mayor de 7, en bytes; ó 15, en palabras. Ejemplos: rcl rcl rcl ax,1 al,cl di,1 RCR (rotación a la derecha con acarreo) Sintaxis: RCR destino, contador Indicadores: OF x DF - IF - TF - SF - ZF - AF - PF - CF x Rotar a la derecha los bits del operando destino junto con el indicador de acarreo CF el número de bits especificado en el segundo operando. Si el número de bits es 1 se puede especificar directamente; en caso contrario su valor debe cargarse en CL y especificar CL como segundo operando: Ejemplos: rcr rcr bx,cl bx,1 ROL (rotación a la izquierda) Sintaxis: ROL destino, contador Indicadores: OF x DF - IF - TF - SF - ZF - AF - PF - CF x Rota a la izquierda los bits del operando destino el número de bits especificado en el segundo operando, que puede ser 1 ó CL previamente cargado con el valor del número de veces. Ejemplos: rol rol dx,cl ah,1 ROR (rotación a la derecha) Sintaxis: ROR destino, contador Indicadores: OF x DF - IF - TF - SF - ZF - AF - PF - CF x Rota a la derecha los bits del operando destino el número de bits especificado en el segundo operando. Si el número de bits es 1 se puede poner directamente, en caso contrario debe ponerse a través de CL. Ejemplos: ror ror cl,1 ax,cl SAL/SHL (desplazamiento aritmético a la izquierda) Sintaxis: SAL/SHL destino, contador Indicadores: OF x DF - IF - TF - SF x ZF x AF ? PF x CF x Desplaza a la izquierda los bits del operando el número de bits especificado en el segundo operando que debe ser CL si es mayor que 1 los bits desplazados. SAR (desplazamiento aritmético a la derecha) Sintaxis: SAR destino, contador Indicadores: OF x DF - IF - TF - SF x ZF x AF ? PF x CF x Desplaza a la derecha los bits del operando destino el número de bits especificado en el segundo operando. Los bits de la izquierda se rellenan con el bit de signo del primer operando. Si el número de bits a desplazar es 1 se puede especificar directamente, si es mayor se especifica a través de CL. Ejemplos: sar sar SHR (desplazamiento lógico a la derecha) Sintaxis: SHR destino, contador ax,cl bp,1 Indicadores: OF x DF - IF - TF - SF x ZF x AF ? PF x CF x Desplaza a la derecha los bits del operando destino el número de los bits especificados en el segundo operando. Los bits de la izquierda se llena con cero. Si el número de bits a desplazar es 1 se puede especificar directamente en el caso en que no ocurra se pone el valor en CL: Ejemplos: shr shr ax,cl cl,1 http://meltingpot.fortunecity.com/uruguay/978/libro/04.html 2.11.1 Lineal. Instrucciones de corrimiento Las instrucciones para recorrer el contenido de un operando son muy útiles y se ofrecen con frecuencia en diversas variaciones. Los corrimientos son operaciones en las cuales los bits de una palabra se recorren a la izquierda o derecha. El bit que se recorre al extremo de la palabra determina el tipo de corrimiento que utiliza. Las instrucciones de corrimiento pueden especificar corrimientos lógicos, aritméticos u operaciones de tipo rotatorio. En cualquier caso, el corrimiento puede ser a la derecha o a la izquierda. , La tabla 8-9 lista cuatro tipos de instrucciones de corrimiento. El corrimiento lógico inserta un 0 al de la posición final de bit. La posición final es el bit al extremo izquierdo para el corrimiento a la derecha y el bit al extremo derecho para el corrimiento a la izquierda. Por lo general, los corrimientos aritméticos se apegan a las reglas para los números de complemento a 2 con signo. Estas reglas se proporcionan en la sección 4-6. La instrucción aritmética de corrimiento a la derecha debe preservar el bit de signo en la posición al extremo izquierdo. El bit de signo se desplaza ala derecha. TABLA 8-9 Instrucciones de corrimiento típicas NOMBRE MNEMONICO Corrimiento a la derecha lógico SHR Corrimiento a la izquierda lógico SHL Corrimiento a la derecha aritmético SHRA Corrimiento a la izquierda aritmético SHLA Rotar a la derecha ROR Rotar a la izquierda ROL Rotar a la derecha mediante acarreo RORC Rotar a la izquierda mediante acarreo ROLC Junto con el resto del número, pero el bit de signo no cambia. Esta es una operación de corrimiento a la derecha en la que el bit final permanece igual. La instrucción de corrimiento aritmético a la izquierda inserta 0 en la posición fina.! y es idéntica a la instrucción lógica de corrimiento a la izquierda. Por esta razón muchas computadoras no proporcionan una instrucción distinta de corrimiento aritmético a la izquierda cuando ya está disponible la instrucción de corrimiento lógico a la izquierda. Las instrucciones de rotación producen un corrimiento circular. Los bits recorridos en un extremo de la palabra no se pierden en un desplazamiento lógico, pero se hacen circular hasta el otro extremo. La instrucción de rotación a través del bit de acarreo trata al bit de acarreo como una extensión del registro cuya palabra se está rotando. Por lo tanto una instrucción de rotación ala izquierda a través del acarreo transfiere el. bit de acarreo a la posición de bit a la extrema derecha del registro, transfiere la posición de la extrema izquierda al acarreo y, al mismo tiempo, recorre todo el registro a la izquierda. Algunas computadoras tienen un formato de campo múltiple para las instrucciones de corrimiento. Un campo contiene el código de operación y los otros especifican el tipo de corrimiento y la cantidad de veces que se va a recorrer un operando. Un formato de código de instrucción posible de una instrucción de desplazamiento puede incluir cinco campos de la manera siguiente. OP REG TYPE RL COUNT Aquí OP es el campo de código de operación; REG es una dirección de registro que especifica la posición del operando; TYPE es un campo de 2 bits que especifica los cuatro diferentes tipos de corrimientos; LR es un campo de 1 bit que especifica un corrimiento a la derecha o ala izquierda y COUNT es un campo de k bits que especifica hasta 2k -1 corrimientos. Con tal formato es posible especificar el tipo de corrimiento, la dirección y la cantidad de corrimientos, todo en una sola instrucción. CORRIMIENTO DE BITS. Las instrucciones de corrimiento, que son parte de la capacidad lógica de la computadora, pueden realizar las siguientes acciones: 1. Hacer referencia a un registro o dirección de memoria. 2. Recorre bits a la izquierda o a la derecha. 3. Recorre hasta 8 bits en un byte, 16 bits en una palabra y 32 bits en una palabra doble. 4. Corrimiento lógico (sin signo) o aritmético (con signo). El segundo operando contiene el valor del corrimiento, que es una constante (un valor inmediato) o una referencia al registro CL. Para los procesadores 8088/8086, la constante inmediata solo puede ser 1; un valor de corrimiento mayor que 1 debe estar contenido en el registro CL. Procesadores posteriores permiten constantes de corrimiento inmediato hasta 31. El formato general para el corrimiento es | [etiqueta:] | Corrim. | {registro/memoria}, {CL/inmediato} | Corrimiento de bits hacia la derecha. Los corrimientos hacia la derecha (SHR y SAR) mueven los bits hacia la derecha en el registro designado. El bit recorrido fuera del registro mete la bandera de acarreo. Las instrucciones de corrimiento a la derecha estipulan datos lógicos (sin signo) o aritméticos (con signo): Las siguientes instrucciones relacionadas ilustran SHR y datos con signo: INSTRUCCION MOV MOV SHR SHR la derecha SHR posteriores CL, 03 AL, 10110111B AL, 01 AL, CL AX, 03 AL COMENTARIO ; 10110111 ; 11011011 Un corrimiento a la derecha ; 00001011 Tres corrimientos adicionales a ; Válido para 80186 y procesadores El primer SHR desplaza el contenido de AL un bit hacia la derecha. El bit de mas a la derecha es enviado a la bandera de acarreo, y el bit de mas a la izquierda se llena con un cero. El segundo SHR desplaza tres bits mas al AL. La bandera de acarreo contiene de manera sucesiva 1, 1 y 0; además, tres bits 0 son colocados a la izquierda del AL. SAR se difiere de SHR en un punto importante: SAR utiliza el bit de signo para llenar el bit vacante de mas a la izquierda. De esta manera, los valores positivos y negativos retienen sus signos. Las siguientes instrucciones relacionadas ilustran SAR y datos con signo en los que el signo es un bit 1: INSTRUCCION MOV MOV SHR SHR derecha SHR CL, 03 AL, 10110111B AL, 01 AL, CL AX, 03 AL COMENTARIO ;; 10110111 ; 11011011 Un corrimiento a la derecha ; 00001011 Tres corrimientos adicionales a la ; Válido para 80186 y procesadores posteriores En especial, los corrimientos a la derecha son útiles para (dividir entre 2) obtener mitades de valores y son mucho mas rápidas que utilizar una operación de división. Al terminar una operación de corrimiento, puede utilizar la instrucción JC (Salta si hay acarreo) para examinar el bit desplazado a la bandera de acarreo. Corrimiento de bits a la izquierda. Los corrimientos hacia la izquierda (SHL y SAL) mueven los bits a la izquierda, en el registro designado. SHL y SAL son idénticos en su operación. El bit desplazado fuera del registro ingresa a la bandera de acarreo. Las instrucciones de corrimiento hacia la izquierda estipulan datos lógicos (sin signo) y aritméticos (con signo): SHL: Desplazamiento lógico a la izquierda aritmético a la izquierda SAL: Desplazamiento Las siguientes instrucciones relacionadas ilustran SHL para datos sin signo: INSTRUCCION MOV CL, 03 MOV AL, 10110111B SHR AL, 01 SHR AL, CL SHR AX, 03 posteriores AL COMENTARIO ; 10110111 ; 01101110 Un corrimiento a la izquierda ; 01110000 Tres corrimientos mas ; Válido para 80186 y procesadores El primer SHL desplaza el contenido de AL un bit hacia la izquierda. El bit de mas a la izquierda ahora se encuentra en la bandera de acarreo, y el ultimo bit de la derecha del AL se llena con cero. El segundo SHL desplaza tres bits mas a el AL. La bandera de acarreo contiene en forma sucesiva 0, 1 y 1, y se llena con tres ceros a la derecha del AL. Los corrimientos a la izquierda llenan con cero el bit de mas a la derecha. Como resultado de esto, SHL y SAL don idénticos. Los corrimientos a la izquierda en especial son útiles para duplicar valores y son mucho mas rápidos que usar una operación de multiplicación. Al terminar una operación de corrimiento, puede utilizar la instrucción JC (Salta si hay acarreo) para examinar el bit que ingreso a la bandera de acarreo. http://www.itlp.edu.mx/publica/tutoriales/ensamblador/ 2.11.2 Circular. 3 ROTACION DE BITS (Desplazamiento circular) Las instrucciones de rotación, que son parte de la capacidad lógica de la computadora, pueden realizar las siguientes acciones: 1. Hacer referencia a un byte o a una palabra. 2. Hacer referencia a un registro o a memoria. 3. Realizar rotación a la derecha o a la izquierda. El bit que es desplazado fuera llena el espacio vacante en la memoria o registro y también se copia en la bandera de acarreo. 4. Realizar rotación hasta 8 bits en un byte, 16 bits en una palabra y 32 bits en una palabra doble. 5. Realizar rotación lógica (sin signo) o aritmética (con signo). El segundo operando contiene un valor de rotación, el cual es una constante (un valor inmediato) o una referencia al registro CL. Para los procesadores 8088/8086, la constante inmediata solo puede ser 1; un valor de rotación mayor que 1 debe estar contenido en el registro CL. Procesadores posteriores permiten constantes inmediatas hasta el 31. El formato general para la rotación es: | [etiqueta:] | Rotación | {registro/memoria}, {CL/inmediato} | Rotación a la derecha de bits Las rotaciones a la derecha (ROR y RCR) desplazan a la derecha los bits en el registro designado. Las instrucciones de rotación a la derecha estipulan datos lógicos (sin signo) o aritméticos (con signo): Las siguientes instrucciones relacionadas ilustran ROR: INSTRUCCION BH COMENTARIO MOV CL, 03 MOV BH, 10110111B SHR BH, 01 SHR BH, CL SHR BX, 03 posteriores ; 10110111 ; 11011011 Una rotación a la derecha ; 00001011 Tres rotaciones a la derecha ; Válido para 80186 y procesadores El primer ROR desplaza el bit de mas a la derecha del BH a la posición vacante de mas a la izquierda. La segunda y tercera operaciones ROR realizan la rotación de los tres bits de mas a la derecha. RCR provoca que la bandera de acarreo participe en la rotación. Cada bit que se desplaza fuera de la derecha se mueve al CF y el bit del CF se mueve a la posición vacante de la izquierda. Rotación a la izquierda de bits Las rotaciones a la izquierda (ROL y RCL) desplazan a la izquierda los bits del registro designado. Las instrucciones de rotación a la izquierda estipulan datos lógicos (sin signo) y aritméticos (con signo): Las siguientes instrucciones relacionadas ilustran ROL: INSTRUCCION MOV CL, 03 MOV BL, 10110111B SHR BL, 01 SHR BL, CL SHR BX, 03 posteriores BL COMENTARIO ; 10110111 ; 11011011 Una rotación a la izquierda ; 00001011 Tres rotaciones a la izquierda ; Válido para 80186 y procesadores El primer ROL desplaza el bit de mas a la izquierda del BL a la posición vacante de mas a la derecha. La segunda y tercera operaciones ROL realizan la rotación de los tres bits de mas a la izquierda. De manera similar a RCR, RCL también provoca que la bandera de acarreo participe en la rotación. Cada bit que se desplaza fuera por la izquierda se mueve al CF, y el bit del CF se mueve a la posición vacante de la derecha. Puede usar la instrucción JC (salta si hay acarreo) para comprobar el bit rotado hacia la CF en el extremo de una operación de rotación. http://www.itlp.edu.mx/publica/tutoriales/ensamblador/tem6_4_.htm 3.1 Procesos de control. 3.1.1 Banderas. FLAGS La explicación de los "flags" está relacionada con los saltos condicionales. Los que hayáis visto un mínimo de otros lenguajes recordaréis las sentencias FOR y NEXT (en Basic), o el IF/THEN/ELSE también en estilo Basic pero que también se encuentran en otros lenguajes. Pues bien, los flags y las instrucciones condicionales va a ser lo que os encontréis en este capítulo. Veamos el registro de flags. A las flags, "banderas", las agrupa un solo registro de 16 bits, aunque éste no esté utilizado por completo, ya que cada flag ocupa un solo bit. ¿Qué son los flags? Se trata de varios bits, que, como siempre, pueden valer uno o cero, y dependiendo de su valor indican varias cosas. El registro de flags es como sigue: ³±³±³±³±³O³D³I³T³S³Z³±³A³±³P³±³C³ O: Overflow D: Dirección I: Interrupciones rehabilitadas T: Trampa S: Signo Z: Cero A: Acarreo auxiliar P: Paridad C: Acarreo ±: No utilizado Cada cuadro representa un bit como es fácil adivinar. También está claro que cada bit que se utiliza tiene un nombre, y como se verá, también una utilidad. Aquí explico el significado de los más importantes: EL FLAG DE ACARREO Hay veces en la operaciones en las que el número se desborda, o sea, no cabe en el registro o en la posición de memoria. Imaginemos que tenemos en AX el número 0FFFFh y le sumamos 0CCCCh. Como es lógico, el resultado no nos cabrá en AX. Al realizar esta suma, tenemos que tener en cuenta que el siguiente número a 0FFFFh es 0000h, con lo que podremos ver el resultado. Igual pasará si a 0000h le restamos, por ejemplo, 1 (el resultado será 0FFFFh). Pero de alguna manera nos tenemos que DAR CUENTA de que esto ha sucedido. Cuando se opera y hay acarreo en el último bit sobre el que se ha operado, el flag de acarreo se pone a uno, es decir, cuando ese número se ha desbordado. Hay que recordar también que las instrucciones INC y DEC no afectan a este flag. Veamos los efectos de estas operaciones: MOV AX,0FFFFh INC AX ; AX vale ahora 0, el flag de acarreo también DEC AX ; AX vale 0FFFFh, y el flag sigue inalterado ADD AX,1 ; AX vale 0, y el flag de acarreo está a 1 MOV BX,0000h ADD BX,50h ; El flag de acarreo se pone a 0, no ha habido ;acarreo en esta operación SUB AX,1 ; Ahora AX vale otra vez 0FFFFh, y el flag de acarreo ;se pone de nuevo a uno. En resumen, se activa cuando tras una operación hay un paso del valor máximo al mínimo o viceversa. Este flag nos va a ser también útil al comprobar errores, etc. Por ejemplo, si buscamos el primer archivo del directorio y no hay ninguno, este flag se activará, con lo que podremos usar los saltos condicionales, pero esto ya se explicará más adelante. EL FLAG DE SIGNO A veces interesa conocer cuándo un número con signo es negativo o positivo. Evidentemente, esto sólo tiene efecto cuando estamos tratando con números enteros con signo, en complemento a dos. Indica cuando tras una operación aritmética (ADD, SUB, INC, DEC o NEG o lógica (AND, OR o XOR) el resultado es un número en complemento a dos. En realidad, es la copia del bit de mayor peso del byte, el que indica cuándo el número es negativo. Por lo tanto, cuando vale 1 es que el número es negativo y si vale 0 es que es positivo. EL FLAG DE DESBORDAMIENTO ("Overflow") Se trata de un flag bastante parecido al de acarreo, pero que actúa con números en complemento a dos y se activa cuando se pasa del mayor número positivo (127 en un solo byte) al menor negativo (-128 en tamaño de un byte). Este flag, al contrario que el de acarreo, SI es afectado por las instrucciones de decremento e incremento. EL FLAG DE CERO Es de los más sencillos de comprender. Simplemente se activa cuando el resultado de una operación aritmética o lógica es cero. Es evidente la gran utilidad del flag. Tenemos, por ejemplo, dos registros, AX y CX, que queremos comparar para saber si son iguales. Para saberlo, no tendríamos más que restar uno del otro, y si el resultado es cero (o sea, si el flag de cero se pone en uno), podremos hacer un salto condicional. O sea, de un SUB CX,AX Si son iguales, el flag de cero se pondrá a uno. EL FLAG DE PARIDAD Se utiliza especialmente en la transmisión de datos para la comprobación de errores, ya que comprueba si el resultado de la última operación aritmética o lógica realizada tiene un número par o impar de bits puestos a uno. Se pondrá a uno cuando haya un número par de bits, y a cero cuando sea impar. RESTO DE FLAGS No describiré más flags detalladamente, ya que su importancia es casi nula; por ejemplo está el flag de interrupción, que, cuando está activado, evita la posibilidad de interrupciones en secciones críticas de código, o el de trampa, que cuando está activado provoca una INT 1h cada vez que se ejecuta otra instrucción, pero creo que su interés es escaso, al menos por el momento. 3.1.2 Cadenas. INSTRUCCIONES DE CADENA Son un subconjunto de instrucciones muy útiles para diversas funciones: inicializar zonas de memoria, copiar datos de una zona a otra, encontrar valores determinados o comparar cadenas, etc., etc. Su comportamiento depende del flag de dirección del que hablábamos unas lecciones más atrás, y que se puede cambiar directamente con estas dos instrucciones: STD: SeT Direction flag, lo pone a uno. CLD: CLear Direction flag, lo pone a cero. Las instrucciones que vamos a usar como de cadena siempre tienen una S de String al final, y casi siempre además una B o una W indicando Byte o Word (el tamaño). Y estas son: LODSB/LODSW Lee un byte/palabra en la dirección de memoria dada por DS:SI y la almacena dependiendo de su tamaño en AL o AX. Si el flag de dirección está a cero, según sea byte o palabra, SI aumentará en 1 o 2 unidades (para poder continuar la operación de lectura). Si está a uno el flag, se decrementará en 1 o 2 unidades dependiendo del tamaño (byte/palabra) STOSB/STOSW Es el equivalente a "grabar" si lo anterior era "cargar". Almacenará el contenido de AL o AX (como siempre, dependiendo del tamaño ) en ES:DI, copiando según si es B o W uno o dos bytes cada vez que se ejecute. Si el flag de dirección está a cero, DI aumentará cada vez que se realice la orden en una o dos unidades (dependiendo del tamaño, B o W ). Si está a uno, decrecerá. MOVSB/MOVSW Mueve el byte o palabra contenido en la dirección de memoria a la que apunta DS:SI a la dirección de memoria de ES:DI. Si el flag de dirección está a 0, con cada MOVS que realicemos SI y DI aumentarán en una unidad (MOVSB) o dos (MOVSW). Si está a uno, se decrementarán de igual manera. REP Acabo de hablar sobre él. Pues bien, si se utiliza como operando suyo una de estas órdenes, la repetirá CX veces. Por ejemplo, si queremos copiar la tabla de vectores de interrupción a un lugar que hemos reservado: cld ; A asegurarnos de que el flag de dirección está. ;a cero. mov cx,400h xor dx,dx ; pone dx a 0 push dx pop ds ; No está permitido hacer xor ds,ds, por lo que ;metemos dx, que vale 0, en la pila, y sacamos ;DS valiendo 0. xor si,si ; SI que valga 0. push cs pop es ; Vamos a asegurarnos de que ES valga CS, o sea, ;el segmento en el que está el programa ahora. mov di,buffer ; DI apunta al lugar donde vamos a guardar la ;tabla. rep movsb ; Repite ‚esto 400h veces, y cada vez que lo hace ;incrementa DI y SI. int 20h ; Acaba la ejecución. buffer: db 400h dup (?) ; Esto deja un espacio de 400h bytes que nos ;va a servir para almacenar la tabla de ;vectores de interrupción. Podemos, para empezar, reducir el 400h a 200h en CX, y hacer un rep movsw, con lo que trasladaremos de palabra en palabra las instrucciones. INSTRUCCIONES DE MANIPULACIÓN DE CADENAS. CMPS/CMPSB/CMPSW (compara cadenas) Sintaxis: CMPS cadena_destino, cadena_origen CMPSB (bytes) CMPSW (palabras) Indicadores: OF x DF - IF - TF - SF x ZF x AF x PF x CF x Compara dos cadenas restando al origen el destino. Ninguno de los operandos se alteran, pero los indicadores resultan afectados. La cadena origen se direcciona con registro SI sobre el segmento de datos DS y la cadena destino se direcciona con el registro DI sobre el segmento extra ES. Los registros DI y SI se autoincrementan o autodecrementan según el valor del indicador DF (véanse CLD y STD) en una o dos unidades, dependiendo de si se trabaja con bytes o con palabras. Cadena origen y cadena destino son dos operandos redundantes que sólo indican el tipo del dato (byte o palabra) a comparar, es más cómodo colocar CMPSB o CMPSW para indicar bytes/palabras. Si se indica un registro de segmento, éste sustituirá en la cadena origen al DS ordinario. Ejemplo: lea lea cmpsb LODS/LODSB/LODSW (cargar cadena) Sintaxis: LODS cadena_origen si,origen di,destino LODSB (bytes) LODSW (palabras) Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Copia en AL o AX una cadena de longitud byte o palabra direccionada sobre el segmento de datos (DS) con el registro SI. Tras la transferencia, SI se incrementa o decrementa según el indicador DF (véanse CLD y STD) en una o dos unidades, según se estén manejando bytes o palabras. Cadena_origen es un operando redundante que sólo indica el tipo del dato (byte o palabra) a cargar, es más cómodo colocar LODSB o LODSW para indicar bytes/palabras. Ejemplo: cld lea lodsb si,origen MOVS/MOVSB/MOVSW (mover cadena) Sintaxis: MOVS cadena_destino, cadena_origen MOVSB (bytes) MOVSW (palabras) Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Transfiere un byte o una palabra de la cadena origen direccionada por DS:SI a la cadena destino direccionada por ES:DI, incrementando o decrementando a continuación los registros SI y DI según el valor de DF (véanse CLD y STD) en una o dos unidades, dependiendo de si se trabaja con bytes o con palabras. Cadena origen y cadena destino son dos operandos redundantes que sólo indican el tipo del dato (byte o palabra) a comparar, es más cómodo colocar MOVSB o MOVSW para indicar bytes/palabras. Si se indica un registro de segmento, éste sustituirá en la cadena origen al DS ordinario. Ejemplo: lea lea movsw si,origen di,destino SCAS/SCASB/SCASW (explorar cadena) Sintaxis: SCAS cadena_destino SCASB (bytes) SCASW (palabras) Indicadores: OF x DF - IF - TF - SF x ZF x AF x PF x CF x Resta de AX o AL una cadena destino direccionada por el registro DI sobre el segmento extra. Ninguno de los valores es alterado pero los indicadores se ven afectados. DI se incrementa o decrementa según el valor de DF (véanse CLD y STD) en una o dos unidades -según se esté trabajando con bytes o palabras- para apuntar al siguiente elemento de la cadena. Cadena_destino es un operando redundante que sólo indica el tipo del dato (byte o palabra), es más cómodo colocar SCASB o SCASW para indicar bytes/palabras. Ejemplo: lea mov scasb di,destino al,50 STOS/STOSB/STOSW (almacena cadena) Sintaxis: STOS cadena_destino STOSB (bytes) STOSW (palabras) Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Transfiere el operando origen almacenado en AX o AL, al destino direccionado por el registro DI sobre el segmento extra. Tras la operación, DI se incrementa o decrementa según el indicador DF (véanse CLD y STD) para apuntar al siguiente elemento de la cadena. Cadena_destino es un operando redundante que sólo indica el tipo del dato (byte o palabra) a cargar, es más cómodo colocar STOSB o STOSW para indicar bytes/palabras. Ejemplo: lea mov stosw di,destino ax,1991 REP/REPE/REPZ/REPNE/REPNZ (repetir) REP repetir operación de cadena REPE/REPZ repetir operación de cadena si igual/si cero REPNE/REPNZ repetir operación de cadena si no igual (si no 0) Estas instrucciones se pueden colocar como prefijo de otra instrucción de manejo de cadenas, con objeto de que la misma se repita un número determinado de veces incondicionalmente o hasta que se verifique alguna condición. El número de veces se indica en CX. Por sentido común sólo deben utilizarse las siguientes combinaciones: Prefijo Instrucciones -----------REP REPE/REPZ REPNE/REPNZ Ejemplos: Función ------------------------------Repetir CX veces Repetir CX veces mientras ZF=1 Repetir CX veces mientras ZF=0 -------------MOVS, STOS CMPS, SCAS CMPS, SCAS 1) Buscar el byte 69 entre las 200 primeras posiciones de tabla (se supone tabla en el segmento ES): LEA MOV MOV CLD REPNE JE DI,tabla CX,200 AL,69 SCASB encontrado 2) Rellenar de ceros 5000 bytes de una tabla colocada en datos (se supone datos en el segmento ES): LEA MOV MOV CLD REP DI,datos AX,0 CX,2500 STOSW 3) Copiar la memoria de pantalla de texto (adaptador de color) de un PC en un buffer (se supone buffer en el segmento ES): MOV MOV LEA MOV MOV CLD REP CX,0B800h DS,CX DI,buffer SI,0 CX,2000 ; ; ; ; ; ; ; MOVSW segmento de pantalla en DS destino en ES:DI copiar desde DS:0 2000 palabras hacia adelante copiar CX palabras http://meltingpot.fortunecity.com/uruguay/978/libro/04.html 3.1.3 Carga. Las primeras dos instrucciones permiten el intercambio de datos entre la memoria y el registro del procesador. La instrucci¶on LOAD copia un dato de la memoria al registro, mientras que la instruccion STORE lo hace en el sentido inverso. La instrucci¶on LOADI permite cargar un operando inmediato en el registro, es decir, un valor constante que se incluye en la instrucci¶on misma. Por ejemplo, LOADI 5 hace que el valor 5 se copie al registro. Observe la diferencia con LOAD 5, que copia al registro el contenido de la celda cuya direcci¶on es 5. INSTRUCCIONES DE CARGA DE REGISTROS Y DIRECCIONES. MOV (transferencia) Sintaxis: MOV dest, origen. Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Transfiere datos de longitud byte o palabra del operando origen al operando destino. Pueden ser operando origen y operando destino cualquier registro o posición de memoria direccionada de las formas ya vistas, con la única condición de que origen y destino tengan la misma dimensión. Existen ciertas limitaciones, como que los registros de segmento no admiten el direccionamiento inmediato: es incorrecto MOV DS,4000h; pero no lo es por ejemplo MOV DS,AX o MOV DS,VARIABLE. No es posible, así mismo, utilizar CS como destino (es incorrecto hacer MOV CS,AX aunque pueda admitirlo algún ensamblador). Al hacer MOV hacia un registro de segmento, las interrupciones quedan inhibidas hasta después de ejecutarse la siguiente instrucción (8086/88 de 1983 y procesadores posteriores). Ejemplos: mov mov mov ds,ax bx,es:[si] si,offset dato En el último ejemplo, no se coloca en SI el valor de la variable dato sino su dirección de memoria o desplazamiento respecto al segmento de datos. En otras palabras, SI es un puntero a dato pero no es dato. En el próximo capítulo se verá cómo se declaran las variables. XCHG (intercambiar) Sintaxis: XCHG destino, origen Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Intercambia el contenido de los operandos origen y destino. No pueden utilizarse registros de segmentos como operandos. Ejemplo: xchg xchg bl,ch mem_pal,bx XLAT (traducción) Sintaxis: XLAT tabla Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Se utiliza para traducir un byte del registro AL a un byte tomado de la tabla de traducción. Los datos se toman desde una dirección de la tabla correspondiente a BX + AL, donde bx es un puntero a el comienzo de la tabla y AL es un índice. Indicar tabla al lado de xlat es sólo una redundancia opcional. Ejemplo: mov mov xlat bx,offset tabla al,4 LEA (carga dirección efectiva) Sintaxis: LEA destino, origen Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Transfiere el desplazamiento del operando fuente al operando destino. Otras instrucciones pueden a continuación utilizar el registro como desplazamiento para acceder a los datos que constituyen el objetivo. El operando destino no puede ser un registro de segmento. En general, esta instrucción es equivalente a MOV destino,OFFSET fuente y de hecho los buenos ensambladores (TASM) la codifican como MOV para economizar un byte de memoria. Sin embargo, LEA es en algunos casos más potente que MOV al permitir indicar registros de índice y desplazamiento para calcular el offset: lea dx,datos[si] En el ejemplo de arriba, el valor depositado en DX es el offset de la etiqueta datos más el registro SI. Esa sola instrucción es equivalente a estas dos: mov add dx,offset datos dx,si LDS (carga un puntero utilizando DS) Sintaxis: LDS destino, origen Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Traslada un puntero de 32 bits (dirección completa de memoria compuesta por segmento y desplazamiento), al destino indicado y a DS. A partir de la dirección indicada por el operando origen, el procesador toma 4 bytes de la memoria: con los dos primeros forma una palabra que deposita en destino y, con los otros dos, otra en DS. Ejemplo: punt dd lds 12345678h si,punt Como resultado de esta instrucción, en DS:SI se hace referencia a la posición de memoria 1234h:5678h; 'dd' sirve para definir una variable larga de 4 bytes (denominada punt en el ejemplo) y será explicado en el capítulo siguiente. LES (carga un puntero utilizando ES) Sintaxis: LES destino, origen Esta instrucción es análoga a LDS, pero utilizando ES en lugar de DS. LAHF (carga AH con los indicadores) Sintaxis: LAHF Indicadores: OF - DF - IF - TF - SF - ZF - AF - PF - CF - Carga los bits 7, 6, 4, 2 y 0 del registro AH con el contenido de los indicadores SF, ZF, AF, PF Y CF respectivamente. El contenido de los demás bits queda sin definir. SAHF (copia AH en los indicadores) Sintaxis: SAHF Indicadores: OF - DF - IF - TF - SF x ZF x AF x PF x CF x Transfiere el contenido de los bits 7, 6, 4, 2 y 0 a los indicadores SF, ZF, AF, PF y CF respectivamente. http://ing.utalca.cl/~fmeza/cursos/cyp/tema3.pdf 1. La instrucción load (cargar) se ha usado principalmente para designar una transferencia de memoria aun registro de programación, por lo general un acumulador. La instrucción store (almacenar) representa una transferencia de un registro de procesador a la memoria. La instrucción mov (mover) se ha usado en las computadoras con registros múltiples de CPU para designar una transferencia de un registro a otro. También se ha usado para transferencias de datos entre registros de CPU y la memoria o entre dos palabras de memoria. La instrucción exchange (intercambiar) cambia la información entre dos registros o un registro y una palabra de memoria. Las instrucciones input (introducir) y output (sacar) transfieren datos entre registros del procesador y terminales de entrada o salida. Las instrucciones push (empujar) y pop (saltar) transfieren datos entres registros del procesador y una pila de memoria. Debe recordarse que las instrucciones que se listan en la tabla 8-5, al igual que en las tablas siguientes en esta sección, con frecuencia están asociadas con diversos modos de direccionamiento. Algunas normas del lenguaje ensamblador modifican el símbolo mnemónico para diferenciar entre los diversos modos de direccionamiento. Por ejemplo, el mnemónico para TABLA 8-5 Instrucciones de transferencia de datos típica Nombre Mnemónico Cargar LD Almacenar ST Mover MOV Intercambiar XCH Entrada IN Salida OUT Empujar PUSH Saltar POP TABLA 8-6 Ocho modos de direccionamiento para la introducción cargar Modo Convención en ensamblador Transferencia de registros Direccionamiento directo LD ADR AC = M[M[ADR]] Direccionamiento indirecto LD @ ADR AC = M[M[ADR]] Direccionamiento relativo LD $ ADR AC = M[PC +ADR] Operando inmediato LD # NBR AC = NBR Direccionamiento indexado LD ADR (X) AC = M[ADR+ XR] Registro LD R1 AC = R1 Indirecto por registro LD (R1) AC = M [R1] Autoincremento LD (R1) + AC = M[R1], R1 = R1 + 1 Cargar de inmediato se convierte en LDI. Otras normas del lenguaje ensamblador utilizan un carácter especial para asignar el modo de direccionamieto. Por ejemplo, el modo inmediato se reconoce por un signo # colocado antes del operando. En cualquier caso, lo importante es entender que cada instrucción puede ocurrir con diversos modos de direccionamiento. Como ejemplo, consideremos la instrucción cargar al acumulador cuando se usa con ocho modos de direccionamiento diferentes. La tabla 8-6 muestra la convención recomendada del lenguaje ensamblador y la transferencia real que se logra en cada caso. ADR representa una dirección, NBR es un número u operando, X es un registro de índice, Rl es un registro de procesador, y AC es el registro acumulador. El carácter 8 simboliza una dirección indirecta. El carácter $ antes de una dirección hace el direccionamiento relativo al contador de programa PC. El carácter $ precede al operando en una instrucción de modo inmediato. Una instrucción de modo indexado se reconoce por un registro que se coloca entre paréntesis después de la dirección simbólica. El modo de registro se simboliza al proporcionar el nombre de un registro de procesador. En el modo indirecto por registro, el nombre del registro que contiene la dirección de memoria se encierra entre paréntesis. El modo de autoincremento se distingue del modo indirecto por registro al colocar un signo de más después del registro entre paréntesis. A su vez el modo de autodecremento utilizaría un signo de menos. Para poder escribir programas del lenguaje ensamblador para una computadora es necesario conocer el tipo de instrucciones disponibles y también estar familiarizado con .1os modos de direccionamiento utilizados en la computadora particular. http://members.fortunecity.es/roy8/cpu.htm Unidad 3. Modularización 3.3 Macros. MACROS. Cuando un conjunto de instrucciones en ensamblador aparecen frecuentemente repetidas a lo largo de un listado, es conveniente agruparlas bajo un nombre simbólico que las sustituirá en aquellos puntos donde aparezcan. Esta es la misión de las macros; por el hecho de soportarlas el ensamblador eleva su categoría a la de macroensamblador, al ser las macros una herramienta muy cotizada por los programadores. No conviene confundir las macros con subrutinas: es estas últimas, el conjunto de instrucciones aparece una sola vez en todo el programa y luego se invoca con CALL. Sin embargo, cada vez que se referencia a una macro, el código que ésta representa se expande en el programa definitivo, duplicándose tantas veces como se use la macro. Por ello, aquellas tareas que puedan ser realizadas con subrutinas siempre será más conveniente realizarlas con las mismas, con objeto de economizar memoria. Es cierto que las macros son algo más rápidas que las subrutinas (se ahorra un CALL y un RET) pero la diferencia es tan mínima que en la práctica es despreciable en el 99,99% de los casos. Por ello, es absurdo e irracional realizar ciertas tareas con macros que pueden ser desarrolladas mucho más eficientemente con subrutinas: es una pena que en muchos manuales de ensamblador aún se hable de macros para realizar operaciones sobre cadenas de caracteres, que generarían programas gigantescos con menos de un 1% de velocidad adicional. 5.4.1. - DEFINICIÓN Y BORRADO DE LAS MACROS. La macro se define por medio de la directiva MACRO. Es necesario definir la macro antes de utilizarla. Una macro puede llamar a otra. Con frecuencia, las macros se colocan juntas en un fichero independiente y luego se mezclan en el programa principal con la directiva INCLUDE: IF1 INCLUDE fichero.ext ENDIF La sentencia IF1 asegura que el ensamblador lea el fichero fuente de las macros sólo en la primera pasada, para acelerar el ensamblaje y evitar que aparezcan en el listado (generado en la segunda fase). Conviene hacer hincapié en que la definición de la macro no consume memoria, por lo que en la práctica es indiferente declarar cientos que ninguna macro: nombre_simbólico MACRO [parámetros] ... ... ; instrucciones de la macro ENDM El nombre simbólico es el que permitirá en adelante hacer referencia a la macro, y se construye casi con las mismas reglas que los nombres de las variables y demás símbolos. La macro puede contener parámetros de manera opcional. A continuación vienen las instrucciones que engloba y, finalmente, la directiva ENDM señala el final de la macro. No se debe repetir el nombre simbólico junto a la directiva ENDM, ello provocaría un error un tanto curioso y extraño por parte del ensamblador (algo así como «Fin del fichero fuente inesperado, falta directiva END»), al menos con MASM 5.0 y TASM 2.0. En realidad, y a diferencia de lo que sucede con los demás símbolos, el nombre de una macro puede coincidir con el de una instrucción máquina o una directiva del ensamblador: a partir de ese momento, la instrucción o directiva machacada pierde su significado original. El ensamblador dará además un aviso de advertencia si se emplea una instrucción o directiva como nombre de macro, aunque tolerará la operación. Normalmente se las asignará nombres normales, como a las variables. Sin embargo, si alguna vez se redefiniera una instrucción máquina o directiva, para restaurar el significado original del símbolo, la macro puede ser borrada -o simplemente porque ya no va a ser usada a partir de cierto punto del listado, y así ya no consumirá espacio en las tablas de macros que mantiene en memoria el ensamblador al ensamblar-. No es necesario borrar las macros antes de redefinirlas. Para borrarlas, la sintaxis es la siguiente: PURGE nombre_simbólico[,nombre_simbólico,...] 5.4.2. - EJEMPLO DE UNA MACRO SENCILLA. Desde el 286 existe una instrucción muy cómoda que introduce en la pila 8 registros, y otra que los saca (PUSHA y POPA). Quien esté acostumbrado a emplearlas, puede crear unas macros que simulen estas instrucciones en los 8086: SUPERPUSH MACRO PUSH PUSH PUSH PUSH PUSH PUSH PUSH PUSH ENDM AX CX DX BX SP BP SI DI La creación de SUPERPOP es análoga, sacando los registros en orden inverso. El orden elegido no es por capricho y se corresponde con el de la instrucción PUSHA original, para compatibilizar. A partir de la definición de esta macro, tenemos a nuestra disposición una nueva instrucción máquina (SUPERPUSH) que puede ser usada con libertad dentro de los programas. 5.4.3. - PARÁMETROS FORMALES Y PARÁMETROS ACTUALES. Para quien no haya tenido relación previa con algún lenguaje estructurado de alto nivel, haré un breve comentario acerca de lo que son los parámetros formales y actuales en una macro, similar aquí a los procedimientos de los lenguajes de alto nivel. Cuando se llama a una macro se le pueden pasar opcionalmente un cierto número de parámetros de cierto tipo. Estos parámetros se denominan parámetros actuales. En la definición de la macro, dichos parámetros aparecen asociados a ciertos nombres arbitrarios, cuya única misión es permitir distinguir unos parámetros de otros e indicar en qué orden son entregados: son los parámetros formales. Cuando el ensamblador expanda la macro al ensamblar, los parámetros formales serán sustituidos por sus correspondientes parámetros actuales. Considerar el siguiente ejemplo: SUMAR MACRO a,b,total PUSH AX MOV AX,a ADD AX,b MOV total,AX POP AX ENDM .... SUMAR positivos, negativos, total En el ejemplo, «a», «b» y «total» son los parámetros formales y «positivos», «negativos» y «total» son los parámetros actuales. Tanto «a» como «b» pueden ser variables, etiquetas, etc. en otro punto del programa; sin embargo, dentro de la macro, se comportan de manera independiente. El parámetro formal «total» ha coincidido en el ejemplo y por casualidad con su correspondiente actual. El código que genera el ensamblador al expandir la macro será el siguiente: PUSH MOV AX AX,positivos ADD MOV POP AX,negativos total,AX AX Las instrucciones PUSH y POP sirven para no alterar el valor de AX y conseguir que la macro se comporte como una caja negra; no es necesario que esto sea así pero es una buena costumbre de programación para evitar que los programas hagan cosas raras. En general, las macros de este tipo no deberían alterar los registros y, si los cambian, hay que tener muy claro cuáles. Si se indican más parámetros de los que una macro necesita, se ignorarán los restantes. En cambio, si faltan, el MASM asumirá que son nulos (0) y dará un mensaje de advertencia, el TASM es algo más rígido y podría dar un error. En general, se trata de situaciones atípicas que deben ser evitadas. También puede darse el caso de que no sea posible expandir la macro. En el ejemplo, no hubiera sido posible ejecutar SUMAR AX,BX,DL porque DL es de 8 bits y la instrucción MOV DL,AX sería ilegal. 5.4.4. - ETIQUETAS DENTRO DE MACROS. VARIABLES LOCALES. Son necesarias normalmente para los saltos condicionales que contengan las macros más complejas. Si se pone una etiqueta a donde saltar, la macro sólo podría ser empleada una vez en todo el programa para evitar que dicha etiqueta aparezca duplicada. La solución está en emplear la directiva LOCAL que ha de ir colocada justo después de la directiva MACRO: MINIMO ya_esta: MACRO LOCAL MOV CMP JB MOV MOV ENDM dato1, dato2, ya_esta AX,dato1 AX,dato2 ya_esta AX,dato2 resultado,AX resultado ; ¿es dato1 el menor? ; sí ; no, es dato2 En el ejemplo, al invocar la macro dos veces el ensamblador no generará la etiqueta «ya_esta» sino las etiquetas ??0000, ??0001, ... y así sucesivamente. La directiva LOCAL no sólo es útil para los saltos condicionales en las macros, también permite declarar variables internas a los mismos. Se puede indicar un número casi indefinido de etiquetas con la directiva LOCAL, separándolas por comas. 5.4.5. - OPERADORES DE MACROS. * Operador ;; Indica que lo que viene a continuación es un comentario que no debe aparecer al expansionar la macro. Cuando al ensamblar se genera un listado del programa, las macros suelen aparecer expandidas en los puntos en que se invocan; sin embargo sólo aparecerán los comentarios normales que comiencen por (;). Los comentarios relacionados con el funcionamiento interno de la macro deberían ir con (;;), los relativos al uso y sintaxis de la misma con (;). Esto es además conveniente porque durante el ensamblaje son mantenidos en memoria los comentarios de macros (no los del resto del programa) que comienzan por (;), y no conviene desperdiciar memoria... * Operador & Utilizado para concatenar texto o símbolos. Es necesario para lograr que el ensamblador sustituya un parámetro dentro de una cadena de caracteres o como parte de un símbolo: SALUDO MACRO MOV etiqueta&c: CALL ENDM c AL,"&c" imprimir Al ejecutar SALUDO A se producirá la siguiente expansión: etiquetaA: MOV CALL AL,"A" imprimir Si no se hubiera colocado el & se hubiera expandido como MOV AL,"c" Cuando se utilizan estructuras repetitivas REPT, IRP o IRPC (que se verán más adelante) existe un problema adicional al intentar crear etiquetas, ya que el ensamblador se come un & al hacer la primera sustitución, generando la misma etiqueta a menos que se duplique el operador &: MEMORIA x&i MACRO IRP DB ENDM ENDM x i, <1, 2> i Si se invoca MEMORIA ET se produce el error de "etiqueta ETi repetida", que se puede salvar añadiendo tantos '&' como niveles de anidamiento halla en las estructuras repetitivas empleadas, como se ejemplifica a continuación: MEMORIA x&&i MACRO IRP DB ENDM ENDM x i, <1, 2> i Lo que con MEMORIA ET generará correctamente las líneas: ET1 ET2 DB 1 DB 2 * Operador ! o <> Empleado para indicar que el carácter que viene a continuación debe ser interpretado literalmente y no como un símbolo. Por ello, !; es equivalente a <;>. * Operador % Convierte la expresión que le sigue -generalmente un símbolo- a un número; la expresión debe ser una constante (no relocalizable). Sólo se emplea en los argumentos de macros. Dada la macro siguiente: PSUM MACRO %OUT mensaje, suma * mensaje, suma * ENDM (Evidentemente, el % que precede a OUT forma parte de la directiva y no se trata del % operador que estamos tratando) Supuesta la existencia de estos símbolos: SIM1 SIM2 EQU EQU 120 500 Invocando la macro con las siguientes condiciones: PSUM < SIM1 + SIM2 = >, (SIM1+SIM2) Se produce la siguiente expansión: %OUT * SIM1 + SIM2 = (SIM1+SIM2) * Sin embargo, invocando la macro de la siguiente manera (con %): PSUM < SIM1 + SIM2 = >, %(SIM1+SIM2) Se produce la expansión deseada: %OUT * SIM1 + SIM2 = 620 * 5.4.6. - DIRECTIVAS ÚTILES PARA MACROS. Estas directivas pueden ser empleadas también sin las macros, aumentando la comodidad de la programación, aunque abundan especialmente dentro de las macros. * REPT veces ... ENDM (Repeat) Permite repetir cierto número de veces una secuencia de instrucciones. El bloque de instrucciones se delimita con ENDM (no confundirlo con el final de una macro). Por ejemplo: REPT OUT ENDM 2 DX,AL Esta secuencia se transformará, al ensamblar, en lo siguiente: OUT OUT DX,AL DX,AL Empleando símbolos definidos con (=) y apoyándose además en las macros se puede llegar a crear pseudo-instrucciones muy potentes: SUCESION MACRO n num = 0 REPT n DB num num = num + 1 ENDM ENDM ; fin de REPT ; fin de macro La sentencia SUCESION 3 provocará la siguiente expansión: DB DB DB 0 1 2 * IRP simbolo_control, <arg1, arg2, ..., arg_n> ... ENDM (Indefinite repeat) Es relativamente similar a la instrucción FOR de los lenguajes de alto nivel. Los ángulos (<) y (>) son obligatorios. El símbolo de control va tomando sucesivamente los valores (no necesariamente numéricos) arg1, arg2, ... y recorre en cada pasada todo el bloque de instrucciones hasta alcanzar el ENDM (no confundirlo con fin de macro) sustituyendo simbolo_control por esos valores en todos los lugares en que aparece: IRP DB ENDM i, <1,2,3> 0, i, i*i Al expansionarse, este conjunto de instrucciones se convierte en lo siguiente: DB DB DB 0, 1, 1 0, 2, 4 0, 3, 9 Nota: Todo lo encerrado entre los ángulos se considera un único parámetro. Un (;) dentro de los ángulos no se interpreta como el inicio de un comentario sino como un elemento más. Por otra parte, al emplear macros anidadas, deben indicarse tantos símbolos angulares '<' y '>' consecutivos como niveles de anidamiento existan. Lógicamente, dentro de una macro también resulta bastante útil la estructura IRP: TETRAOUT MACRO PUSH PUSH MOV IRP MOV OUT ENDM POP POP ENDM p1, p2, p3, p4, valor AX DX AL,valor cn, <p1, p2, p3, p4> DX, cn DX, AL ; fin de IRP DX AX ; fin de macro Al ejecutar TETRAOUT 318h, 1C9h, 2D1h, 1A4h, 17 se obtendrá: PUSH PUSH MOV MOV OUT MOV OUT MOV OUT MOV OUT POP POP AX DX AL, 17 DX, 318h DX, AL DX, 1C9h DX, AL DX, 2D1h DX, AL DX, 1A4h DX,AL DX AX Cuando se pasan listas como parámetros hay que encerrarlas entre '<' y '>' al llamar, para no confundirlas con elementos independientes. Por ejemplo, supuesta la macro INCD: INCD MACRO IRP INC ENDM DEC ENDM lista, p i, <lista> i ; fin de IRP p Se comprende la necesidad de utilizar los ángulos: INCD AX, BX, CX, DX se expandirá: ; fin de macro INC DEC AX BX ; CX y DX se ignoran (4 parámetros) INCD <AX, BX, CX>, DX se expandirá: INC INC INC DEC AX BX CX DX ; (2 parámetros) * IRPC simbolo_control, <c1c2 ... cn> ... ENDM (Indefinite repeat character) Esta directiva es similar a la anterior, con una salvedad: los elementos situados entre los ángulos (<) y (>) -ahora opcionales, por cierto- son caracteres ASCII y no van separados por comas: IRPC DB ENDM i, <813> i El bloque anterior generará al expandirse: DB DB DB 8 1 3 Ejemplo de utilización dentro de una macro (en combinación con el operador &): INICIALIZA MACRO IRPC DB ENDM ENDM a, b, c, d iter, <&a&b&c&d> iter ; fin de IRPC ; fin de macro Al ejecutar INICIALIZA 7, 1, 4, 0 se produce la siguiente expansión: DB DB DB DB 7 1 4 0 * EXITM Sirve para abortar la ejecución de un bloque MACRO, REPT, IRP ó IRPC. Normalmente se utiliza apoyándose en una directiva condicional (IF...ELSE...ENDIF). Al salir del bloque, se pasa al nivel inmediatamente superior (que puede ser otro bloque de estos). Como ejemplo, la siguiente macro reserva n bytes de memoria a cero hasta un máximo de 100, colocando un byte 255 al final del bloque reservado: MALLOC MACRO n maximo=100 REPT n IF maximo EQ 0 ; ¿ya van 100? EXITM ; abandonar REPT ENDIF maximo = maximo - 1 DB 0 ; reservar byte ENDM DB 255 ; byte de fin de bloque ENDM 5.4.7. - MACROS AVANZADAS CON NUMERO VARIABLE DE PARÁMETROS. Como se vio al estudiar la directiva IF, existe la posibilidad de chequear condicionalmente la presencia de un parámetro por medio de IFNB, o su ausencia con IFB. Uniendo esto a la potencia de IRP es posible crear macros extraordinariamente versátiles. Como ejemplo, valga la siguiente macro, destinada a introducir en la pila un número variable de parámetros (hasta 10): es especialmente útil en los programas que gestionan interrupciones: XPUSH MACRO R1,R2,R3,R4,R5,R6,R7,R8,R9,R10 IRP reg, <R1,R2,R3,R4,R5,R6,R7,R8,R9,R10> IFNB <reg> PUSH reg ENDIF ENDM ; fin de IRP ENDM ; fin de XPUSH Por ejemplo, la instrucción: XPUSH AX,BX,DS,ES,VAR1 PUSH PUSH PUSH PUSH PUSH AX AX DS ES VAR1 Se expandirá en: El ejemplo anterior es ilustrativo del mecanismo de comprobación de presencia de parámetros. Sin embargo, este ejemplo puede ser optimizado notablemente empleando una lista como único parámetro: XPUSH MACRO lista IRP i, <lista> PUSH i ENDM ENDM XPOP MACRO lista IRP i, <lista> POP i ENDM ENDM La ventaja es el número indefinido de parámetros soportados (no sólo 10). Un ejemplo de uso puede ser el siguiente: XPUSH XPOP <AX, BX, CX> <CX, BX, AX> PUSH PUSH PUSH POP POP POP AX BX CX CX BX AX Que al expandirse queda: http://meltingpot.fortunecity.com/uruguay/978/libro/05.html#04 MACRO.- Sirve para definir una Macro. Una macro, es un conjunto de instrucciones que se pueden mandar a llamar desde cualquier parte del programa. Para definir una macro, se utiliza la directiva MACRO. El formato de esta directiva es : (nombre_de_macro) MACRO (lista_parámetros) La macro consta de tres partes esenciales: la cabecera, que contiene el nombre de la macro, la directiva MACRO y una lista opcional de parámetros; el cuerpo, que contiene el código de la macro, y el fin, que contiene simplemente la directiva ENDM. Ejemplo : INIT2 MACRO MOV MOV MOV ENDM AX,DATA DS,AX ES,AX La mayoría de las programas ejecutan conjunto de instrucciones repetitivas. Por ejemplo para desplegar un mensaje es necesario ejecutas la siguiente secuencia de instrucciones: MOV DX, FOCET MENSAJE MOV AH, 09H INT 21 Si su programa realiza frecuentemente funciones de entrada/salida, las macros ofrecen la facilidad de codificar una sola vez un conjunto de instrucciones y reutilizar el código cuantas veces desee dentro de su programa. Una macro es una pseudo-op que permite el establecimiento de operaciones únicas en ensamblador o la inclusión de frecuentes llamadas de código ensamblador. Una macro también se puede definir como un grupo de instrucciones repetitivas en un programa que se codifica sólo una vez y puede utilizarse cuantas veces sea necesario. Una macro consta de tres partes esenciales: a) CABECERA: Contiene el nombre de la macro, la pseudo-op MACRO y opcionalmente, variables ficticias que serán pasadas desde la macro. b) CUERPO: Contiene el código real que será insertado en cualquier programa que llame al nombre de la macro. c) FIN: Debe incluir la sentencia ENDM. El formato de definición de una macro es : NombreMacro MACRO (parámetro1, parámetro2,..., parámetroN) CUERPO DE LA MACRO ENDM Ejemplo de la definición de una macro. INICIO MACRO ;DEFINE MACRO MOV AX, DATA ;CUERPO DE MOV DS, AX ;LA DEFINICIÓN MOV ES, AX ;DE LA MACRO ENDM ;FIN DE LA MACRO El nombre de esta macro es INICIO, aunque es aceptable cualquier otro nombre válido que sea único. La directiva MACRO en la primer línea indica al ensamblador que las instrucciones que siguen, hasta ENDM (“fin de la macro”), son parte de la definición de la macro. La directiva ENDM termina la definición de la macro. Las instrucciones entre MACRO y ENDM comprenden el cuerpo de la definición de la macro. Los nombres a que se hace referencia en la definición de la macro, @datos, AX, DS y ES, deben estar definidos en alguna parte del programa o deben ser dados a conocer de alguna otra forma al ensamblador. .En forma subsecuente se puede usar la macroinstrucción INIICIO en el segmento de código en donde quiera inicializar los registros. Cuando el ensamblador encuentra la macroinstrucción INICIO, el ensamblador copia el cuerpo de la macro en la posición del programa dónde se encuentra la llamada a la macro. PARÁMETROS Un parámetro es una variable que puede tomar un valor diferente cada vez que se ejecute una subrutina en la que se utiliza tal variable. Los parámetros son reemplazados por los nombres o valores que especifique la instrucción que la invocó. La lista de parámetros es opcional y, si existe, cada parámetro deberá estar separado por una coma. Ejemplo: Despliega MACRO Mensaje Toda macro tiene une definición inicial que consta del nombre asignado a ella, el seudocódigo de operación MACRO y la lista de parámetros. En la definición anterior el nombre de la macro es despliega y su parámetro es mensaje (el mensaje que se desplegará cada vez que sea invocada la macro). Despliega MACRO Mensaje MOV DX, OFFSE Mensaje MOV AH, 09 INT 21H ENDM Para hacer una macro flexible, podemos definir nombres en ella como argumentos mudos (ficticios). La definición de la macro siguiente llamada PROMPT, proporciona el uso de la función 09H del DOS para desplegar cualquier mensaje, cuando se usa la macro instrucción, el programador tiene que proporcionar el nombre del mensaje, el cual hace referencia a un área de datos determinada por un signo de dólar. PROMPT MACRO MENSAJE ;ARGUMENTO MUDO MOV AH,09H LEA DX;MESSGE INT 21H ENDM ;FIN DE LA MACRO Un argumento mudo (ficticio) indica al ensamblador que haga coincidir su nombre con cualquier aparición del mismo nombre en el cuerpo de la macro. Por ejemplo el argumento mudo MENSAGE también aparece en la instrucción LEA. Cuando utiliza la macro instrucción PROMPT, usted proporciona un parámetro como el nombre real del mensaje que será desplegado, por ejemplo: PROMPT MESSAGE2 En este caso MESSAGE2 tiene que estar apropiadamente definido en el segmento de datos. El parámetro en la macroinstrucción corresponde al argumento mudo en la definición original de la macro: DEFINICIÓN DE MACRO: PROMPT MACRO MESSAGE (argumento) MACROINSTRUCCIÓN: PROMPT MESSAGE2 (parámetro) El ensamblador sustituye los parámetros de la macroinstrucción por los argumentos mudos en la definición de la macro, entrada por entrada, de izquierda a derecha. Ejemplo de parámetros de macros. TITLE P22MACR2 (EXE) USO DE PARÁMETROS. INITZ MACRO ;DEFINE MACRO MOV AX, DATA MOV DS,AX MOV ES,AX ENDM ;TERMINA MACRO PROMPT MACRO MESSGE ;DEFINE MACRO MOV AH, 09H LEA DS, MESSGE INT 21H ENDM ;TERMINA MACRO .MODEL SMALL .STACK 64 .DATA MESSGE1 DB ‘CUSTOMER NAME?’, ‘$’ MESSG2 DB ‘CUSTOMER ADDRES’, ‘$’ .CODE BEGIN PROC FAR INITZ MOV AX, DATA MOV DS, AX MOV ES, AX PROMPT MESSG2 MOV AH, 09H LEA DX, MESSG2 INT 21H MOV AX, 4C00H ;SALE AL DOS INT 21H BEGIN ENDP END BEGIN ETIQUETAS Una etiqueta es el primer campo de la izquierda de una línea. Este campo es opcional, consiste en un conjunto de caracteres y sirve para identificar dicha línea. Cuando en una línea de instrucción se encuentra una etiqueta el ordenador guardará dicha etiqueta junto con la dirección que le ha correspondido a la instrucción para posteriormente ser relacionada con una instrucción de salto a esa dirección. Con el uso de etiquetas el programador se despreocupa de las posiciones de memoria que van a ocupar las instrucciones y coloca etiquetas a las instrucciones a las que haga referencia en otros puntos del programa. A continuación se mencionan algunas ventajas que ofrece el empleo de etiquetas: a) Permite localizar y recordar fácilmente una determinada instrucción. b) Se puede modificar de forma sencilla, en la fase de corrección de un programa, el punto donde tienen que realizarse uno o más saltos cambiando la etiqueta de una instrucción a otra. c) En caso de que el programa objeto de debe colocar en posiciones de memoria diferentes de las que en un principio se había previsto. No hay que hacer ningún cambio en el programa fuente si se han utilizado etiquetas, pues al hacer la nueva traducción asigna automáticamente las nuevas posiciones a las etiquetas. Esto facilita la unión de varios programas que han sido desarrollados por separado. Ejemplo de una etiqueta: Despliega MACRO Mensaje, Veces MOV CX, Veces OTRA: MOV DX, OFFSET Mensaje MOV AH,9 INT 21H LOOP OTRA ENDM La macro anterior despliega el mensaje las veces especificadas. Sin embargo, ¿qué sucede si invoca usted la macro más de una vez?. El TASM genera un error de etiqueta redefinida porque no se puede definir una etiqueta con el mismo nombre más de una vez (la etiqueta de la macro anterior es OTRA). Las normas para definición de etiquetas nos dicen que éstas deben tener nombres únicos; lo anterior se debe a que si el TASM permitiese la duplicación de etiquetas, el programa se vería en un predicamento porque no podría decidir a cuál de las etiquetas debe hacerle caso. Ejemplo: ...CÓDIGO . . Despliega MACRO Mensaje, veces MOV CX, Veces OTRA: MOV DX,OFFSET Mensaje MOV AH,09 INT 21H LOOP OTRA ENDM ...MAS CÓDIGO . . . MOV CX,10 OTRA: LOOP OTRA ...RESTO DEL CÓDIGO ...END PROGRAMA Cuando el programa ejecuta la primera macro todo está bien, pero cuando intenta ejecutar el código que contiene de nuevo el mismo nombre de etiqueta se queda perplejo porque no puede decidir si la interacción se aplica a su etiqueta OTRA o a la etiqueta OTRA de la macro procesadora. Afortunadamente existe una solución al problema que consiste en usar un seudocódigo de operación llamado LOCAL, el cual enumera todas las etiquetas utilizadas dentro de una macro. Cada vez que la macro sea expandida, el TASM crea un símbolo único para cada etiqueta enumerada en la lista de LOCAL. El formato de LOCAL es: LOCAL lista de etiquetas Donde lista de etiquetas es la lista de los nombres de etiqueta que se usarán dentro de la macro. LOCAL debe ser la primera instrucción que aparezca tras la definición de la macro, ya que no pueden haber comentarios entre la definición y la especificación de LOCAL. Teniendo presente lo anterior redefinamos la macro anterior. Despliega MACRO Mensaje, Veces LOCAL OTRA MOV CX, VECES OTRA: MOV DX, OFFSET Mensaje MOV AH,9 INT 21H LOOP OTRA ENDM ENSAMBLE DE MACROS Al ensamblarse una macro de un programa en lenguaje ensamblador, lo que hace él turbo asembler (Tasm) de Borland turbo pascal es: buscar el código de la macro, copiarlo y pegarlo en cada una de las macros existentes del programa. Cuando se crea un programa no es necesario escribir los comentarios que van después de las comillas, sin embargo es una técnica recomendable para que en caso de errores o mejoras al código sea más sencillo encontrar la parte deseada. Para ensamblar este programa primero se guarda el formato ASCII con un nombre válido, por ejemplo: program1.asm Para ensamblarlo se utiliza el TASM, el comando de ensamble es: TASM Program1; para enlazarlo y hacerlo ejecutable tecleamos: LINK program1; Una vez terminados estos pasos es posible ejecutarlo tecleando: program1 [Enter] Para utilizar el programa directamente en su computadora guarde este archivo como ASCII o texto, llévelo a su PC, con algún editor elimine todos estos comentarios y los comentarios del principio y ensámblelo. PROGRAMA: ; INICIO DEL PR0GRAMA, DEFINIMOS EL MODELO DE MEMORIA A USAR Y EL SEGMENTO ; DE CÓDIGO .MODEL SMALL ;MODELO DE MEMORIA .CODE ;ÁREA DE CÓDIGO INICIO: ;ETIQUETA DE INICIO DEL PROGRAMA MOVE AX,DATA ;INICIALIZA EL REGISTRO DS CON LA DIRECCIÓN DADA MOV DS,AX ;POR DATA (SEGMENTO DE DATOS) MOV DX, OFFSET Titulo ;OBTIENE LA DIRECCIÓN DE LA CADENA DE CADENA DE CARACTERES MOV AH,09 ;USAMOS LA FUNCIÓN 09H DE LA INTERRUPCIÓN 21H INT 21H ;PARA DESPLEGAR LA CADENA CUYA DIRECCIÓN OBTUVIMOS MOV CX,16 ;CONTADOR DE CARACTERES QUE SE MOSTRARÁ MOV BX,OFFSET Cadena ;PERMITE ACCESO A LA CADENA DONDE SE ENCUENTRAN LOS VALORES A DESPLEGAR CICLO: ;ETIQUETA PARA GENERAR UN CICLO MOV AL,CL ;COLOCA EN AL EL NÚMERO A TRADUCIR Y LO TRADUCE XLAT ;USANDO LA INSTRUCCIÓN XLAT MOV DL,AL ;COLOCA EN DL EL VALOR A SER DESPLEGADO POR MEDIO DE LA MOV AH, 02 ;FUNCIÓN 2 DE LA INTERRUPCIÓN 21H INT 21H ;DESPLIEGA EL CARACTER MOV DL,10 ;SALTA UNA LÍNEA DESPLEGANDO EL CARÁCTER 10 INT 21H ;DESPLIEGA EL CARACTER MOV DL,13 ;PRODUCE UN RETORNO DE CARRO DESPLEGANDO EL CARACTER 13 INT 21H ;DESPLIEGA EL RETORNO DE CARRO LOOP CICLO ;DECREMENTA EN 1 A CX Y BRINCA LA ETIQUETA CICLO ;SIEMPRE Y CUANDO CX NO SEA IGUAL A CERO MOV AH, 4C ; UTILIZA LA FUNCIÓN 4C DE LA INTERRUPCIÓN 21H PARA INT 21H ;FINALIZAR EL PROGRAMA VENTAJAS DE LAS MACROS 1: Las macros son rápidas porque se ejecutan en línea en un programa. 2: Las macros pueden pasar y recibir parámetros que afecten solo la operación de las mismas. 3: Las macros pueden ser guardadas en una biblioteca en código fuente, que puede ser fácilmente editada. 4: La cabecera de programación puede utilizar macros simple; para una biblioteca de macros utilizar IF1...ENDIF. DESVENTAJAS DE LAS MACROS 1: Las macros hacen más grande el código fuente, ya que son expandidas cada vez que son llamadas. Definición Una de las principales desventajas de la programación en lenguaje ensamblador es la repetición constante de ciertos grupos de instrucciones. Por ejemplo el siguiente conjunto de instrucciones nos permite imprimir una variable de tipo cadena en la pantalla: Lea DX,Cadena ;Direccionar la cadena Mov AH,09h ;Usar la función 09h para imprimir cadenas Int 21h ;llamada a la interrupción 21h del DOS Si necesitamos que en nuestro programa se muestren mensajes constantemente, es obvio que debemos duplicar este conjunto de instrucciones por cada mensaje que se desea enviar a pantalla. El principal problema que esto nos ocasiona es que el tamaño de nuestro programa crece considerablemente, y mientras más grande sea el programa, más difícil será encontrar la causa de algún error cuando éste ocurra. La mejor solución en estos casos es el uso de las MACROS. Una macro es un conjunto de instrucciones que se agrupan bajo un nombre descriptivo (macroinstrucción) y que sólo es necesario declarar una vez (macrodefinición). Una vez que la macro ha sido declarada, sólo es necesario indicar su nombre en el cuerpo del programa y el ensamblador se encargara de reemplazar la macroinstrucción por las instrucciones de la macro (expansión de la macro). El formato general de una macro es el siguiente: .MACRO Nombre [(parametro1, parametro2, etc)] INSTRUCCIONES ENDM Nuevamente, lo que se encuentra entre paréntesis cuadrados es opcional. De acuerdo con esto, la macro para imprimir cadenas quedaría de la siguiente forma: .MACRO Imprime_Cad(Cadena) Lea DX,Cadena Mov Ah,09h Int 21h ENDM Parámetros y etiquetas Dentro de las propiedades más importantes de las macros se deben destacar la posibilidad de utilizar parámetros y etiquetas. Los parámetros permiten que una misma macro pueda ser usada bajo diferentes condiciones, por ejemplo, se puede crear una macro para posicionar el cursor en diferentes coordenadas de la pantalla e indicar sus coordenadas por medio de parámetros. La siguiente macro nos muestra esta propiedad: ;Esta macro posiciona el cursor en las coordenadas que se le indican como ;parámetros. Es el equivalente al GotoXY de Pascal. .MACRO gotoxy (x,y) xor bh,bh ;Seleccionar página cero de video mov dl,x ;Columna mov dh,y ;Renglón mov ah,02h ;Función 02h para posicionar cursor int 10h ;llamada a la int 10h del BIOS ENDM También existen situaciones en las que los parámetros no son necesarios, es por esta razón que los parámetros son opcionales en la declaración de la macro. ;Esta macro realiza una pausa en el programa hasta que una tecla es ;presionada. Es el equivalente del readkey en Pascal. .MACRO tecla mov ah,10h int 16h ENDM Por otro lado, las etiquetas también son útiles dentro de las macros. Suponga que se desea crear una macro que imprima una cadena un numero n de veces, esta macro podría ser declarada de la siguiente forma: .MACRO Imprime_nCad (Cadena, Cuantos) Mov CX,Cuantos ;Iniciar Contador Lea DX,Cadena ;Direccionar la cadena que se va a imprimir Mov Ah,09h ;Usar la función 09h Otra: ;Etiqueta interna Int 21h ;Imprimir la Cadena n veces Loop Otra ;Siguiente Impresión ENDM Ensamble de macros Como ya se mencionó antes, una macro es declarada una sola vez y puede ser llamada cuantas veces sea necesario dentro del cuerpo del programa. Cada vez que el ensamblador encuentra una macroinstrucción, verifica si ésta fue declarada; si esta verificación es exitosa, el ensamblador toma las instrucciones del cuerpo de la macro y las reemplaza en el lugar donde la macro fue llamada. El siguiente programa muestra la declaración y uso de las macros: .COMMENT Programa: Macros1.ASM Autor: Juan Carlos Guzmán C. Descripción: Este programa muestra el uso de macros. .MODEL TINY ; Declaración de variables .DATA cad db 'Ejemplo del uso de macros...',13,10,'$' cad1 db 'Presiona una tecla...','$' cad2 db 'Ejemplo del uso de la macro gotoxy...','$' ;Aquí se declaran las macros. ;************************************************************************ ;-----------------------------------------------------------------------;Esta macro imprime una cadena pasada como parámetro. ;Utiliza la función 09h de la Int 21h del DOS. .MACRO imprime_cad(cadena) lea dx,cadena mov ah,09h int 21h ENDM ;-----------------------------------------------------------------------;Esta macro realiza una pausa en el programa hasta que una tecla se ;presione. Es el equivalente del readkey en Pascal. .MACRO tecla mov ah,10h int 16h ENDM ;-----------------------------------------------------------------------;Esta macro posiciona el cursor en las coordenadas que se le indican como ;parámetros. Es el equivalente al GotoXY de Pascal. .MACRO gotoxy (x,y) xor bh,bh mov dl,x mov dh,y mov ah,02h int 10h ENDM ;------------------------------------------------------------;Esta macro limpia la pantalla. ;Utiliza la función 06h de la Int 10h del Bios. .MACRO limpiar_pantalla mov ax,0600h mov bh,17h mov cx,0000h mov dx,184fh int 10h ENDM ;------------------------------------------------------------;Aquí comienza el cuerpo del programa principal .CODE inicio: ;Declaración del punto de entrada limpiar_pantalla ;Llamada a la macro gotoxy (0,0) ;Colocar el cursor en 0,0 imprime_cad(cad) ;Imprime el primer mensaje imprime_cad(cad1) ;Imprime el segundo mensaje tecla ;Espera a que se presione una tecla gotoxy (30,12) ;Colocar el cursor en 30,12 imprime_cad(cad2) ;Imprimir el tercer mensaje gotoxy (50,24) ;Colocar el cursor en 50,24 imprime_cad(cad1) ;Imprimir el segundo mensaje tecla ;Esperar por una tecla mov ax,4c00h ;Fin del programa y regresar al DOS. int 21h END inicio END Ventajas y desventajas Si bien es cierto que las macros proporcionan mayor flexibilidad a la hora de programar, también es cierto que tienen algunas desventajas. La siguiente es una lista de la principales ventajas y desventajas del uso de las macros. Ventajas: Menor posibilidad de cometer errores por repetición. Mayor flexibilidad en la programación al permitir el uso de parámetros. Código fuente más compacto. Al ser más pequeño el código fuente, también es más fácil de leer por otros. Desventajas: El código ejecutable se vuelve más grande con cada llamada a la macro. Las macros deben ser bien planeadas para evitar la redundancia de código. http://www.monografias.com/trabajos11/lenen/lenen2.s html Conceptos básicos Definición y operación Una macro es un símbolo que representa un bloque de texto. El ensamblador usa las macros de tal manera que cada vez que encuentra al símbolo que define la macro, lo sustituye por el texto que representa. Cómo crear macros Sintaxis: <Nombre> MACRO [parametros] ... ENDM Ejemplo: DuplicaAL MACRO SHL AL, 1 ENDM Código que la usa: MOV AL, 2 DuplicaAL ; Esta es la "llamada" al macro El ensamblador sustituye cada vez que aparezca la macro No es como los procedimientos, que se "llaman" pero no repiten el código Ejemplo: MOV AL, 2 DuplicaAL ; CALL Duplica DuplicaAL ; CALL Duplica Este código queda MOV AL, 2 SHL AL, 1 ; CALL Duplica SHL AL, 1 ; CALL Duplica Cúando usarlas y cúando no Cuando se quiere el código más rápido y no importa ser repetitivo (código grande) Cuando se van a generar diversas variantes de código similar No cuando se quiere código compacto, evitando repetir el mismo (en este caso, usar procedimientos) Asignación de variables y constantes para el ensamblador Pseudoinstrucciones, pues solamente se usan durante el proceso de traducción Sintaxis: <Simbolo> EQU Valor <Simbolo> = Valor Permiten definir constantes y variables del ensamblador La diferencia es que = puede usarse varias veces sobre el mismo símbolo (como una variable) REPT Directiva para definir bloques repetitivos Permite repetir un código determinado, tantas veces como se desee Sintaxis: REPT <n> ... ENDM Ejemplo: Valor = 0 ; Variable de ensamblador REPT 10 DW Valor Valor = Valor + 1 ENDM Uso de parámetros en macros Los macros pueden ser adaptativos; por ejemplo: Potencia2 MACRO Registro, Valor REPT Valor SHL Registro, 1 ENDM ENDM Uso: Potencia2 CX, 4 Se convierte en: SHL CX, 1 SHL CX, 1 SHL CX, 1 SHL CX, 1 http://www.sinergia-web.com.mx/clases/asm9708/Temas/clase18.htm Para cada instrucción simbólica que usted codifica, el ensamblador genera una instrucción de lenguaje de maquina. El ensamblador tiene facilidades que el programador puede utilizar para definir macros. Primero hay que definir un nombre especifico para la macro, junto con el conjunto de instrucciones en lenguaje ensamblador que la macro va a generar. Después, siempre que necesite codificar el conjunto de instrucciones, solo hay que codificar el nombre de la macro y el ensamblador genera de forma automática las instrucciones que han sido definidas en la macro. Las macros son útiles para los siguientes propósitos: Simplificar y reducir la cantidad de codificación repetitiva. Reducir errores causados por la codificación repetitiva. Linealizar un programa en lenguaje ensamblador para hacerlo mas legible. Una definición de macro aparece antes de que cualquier definición de segmento. Examinemos una definición de una macro sencilla que inicializa los registros de segmento para un programa.EXE: INICIAREGS MACRO MOV AX, @data MOV DS, AX MOV ES, AX ENDM ;Define macro ; } Cuerpo de ; } la definición ; } de la macro ; Fin de la macro El nombre de esta macro es INICIAREGS, aunque es aceptable cualquier otro nombre valido que sea único. La directiva MACRO en la primer línea indica al ensamblador que las instrucciones que siguen, hasta ENDM ("fin de la macro"), son parte de la definición de la macro. La directiva ENDM termina la definición de la macro. Los nombres a que se hace referencia en la definición de la macro, @data, AX, DS y ES, deben estar definidos en alguna parte del programa o deben ser dados a conocer de alguna otra forma al ensamblador. En forma subsecuente se puede usar la macro-instruccion INICIAREGS en el segmento de código en donde quiera inicializar los registros. Cuando el ensamblador encuentra la macra-instruccion INICIAREGS, busca en una tabla de instrucciones simbólicas y, a falta de una entrada, busca macroinstrucciones. Ya que el programa contiene una definición de la macro INICIAREGS, el ensamblador sustituye el cuerpo de la definición generando instrucciones: la expansión de la macro. Un programa usaría la macroinstruccion INICIAREGS solo una vez, aunque otras macros están diseñadas para ser utilizadas cualquier numero de veces y cada vez el ensamblador genera la misma expansión. MANEJO DE PARAMETROS. Para hacer una macro flexible, puede definir nombres en ella como argumentos mudos (ficticios).La definición de la macro siguiente, llamada DESPLEGAR_MSG, proporciona el uso de la función 09H del DOS para desplegar cualquier mensaje. Cuando se usa la macroinstrucción el programador tiene que proporcionar el nombre del mensaje, el cual hace referencia a un área de datos terminada por un signo de dólar. DESPLEGAR_MSG MACRO MENSAJE MOV AH, 09H LEA DX, MENSAJE INT 21H ENDM ; Argumento mudo ; Fin de la macro Un argumento mudo en una definición de macro indica al ensamblador que haga coincidir su nombre con cualquier aparición del mismo nombre en el cuerpo de la macro. Por ejemplo, el argumento mudo MENSAJE también aparece en la instrucción LEA. Cuando utiliza la macroinstrucción DESPLEGAR_MSG, usted proporciona un parámetro como el nombre real del mensaje que será desplegado, por ejemplo: DESPLEGAR_MSG MENSAJE2 En este caso, MENSAJE2 tiene que estar apropiadamente definido en el segmento de dato. El parámetro en la microinstrucción corresponde al argumento mudo en la definición original de la macro: Definición de macro: DESPLEGAR_MSG MACRO MENSAJE (Argumento) Macroinstruccin: DESPLEGAR_MSG MENSAJE2 (Parametro) El ensamblador ya ha hecho corresponder el argumento en la definición original de la macro con la instrucción LEA en el cuerpo de la macro. Ahora sustituye el (los) parámetro(s) de la macroinstrucción MENSAJE2 por la presencia de MENSAJE en la instrucción LEA y la sustituye por cualquier otra aparición de MENSAJE. Un argumento mudo puede contener cualquier nombre valido, incluyendo un nombre de registro tal como CX. Puede definir una macro con cualquier numero de argumentos mudos, separados por coma, hasta la columna 120 de una línea. El ensamblador sustituye los parámetros de la macro instrucción por los argumentos mudos en la definición de la macro, entrada por entrada, de izquierda a derecha. MANEJO DE ETIQUETAS LOCALES. Algunas macros necesitan que se definan elementos de datos y etiquetas de instrucciones dentro de la definición de la macro. Si utiliza la macro mas de una vez en el mismo programa y el ensamblador define los elementos de datos para cada aparición, los nombres duplicados harían que el ensamblador genere un mensaje de error. Para asegurar que cada nombre generado es único, hay que codificar la directiva LOCAL inmediatamente después de la instrucción MACRO. Su formato general es: LOCAL Etiqueta1, Etiqueta2...Etiquetan. BIBLIOTECAS DE MACROS. Definir una macro y usarla solo una vez en un programa no es muy productivo. El enfoque habitual es catalogar las macros en una biblioteca en disco bajo un nombre descriptivo, como MACRO.LIB. Usted solo tiene que reunir todas las definiciones de sus macros en un archivo y almacenar el archivo en disco: Macro1 MACRO .... ENDM Macro2 MACRO .... ENDM Para usar cualquiera de las macros catalogadas, en lugar de codificar las definiciones MACRO al inicio del programa utilice la directiva INCLUDE así: INCLUDE C: MACRO.LIB Macro1 Macro2 El ensamblador accesa el archivo llamado MACRO en la unidad C e incluye ambas definiciones de macro, Macro1 y Macro2, en el programa. El listado ensamblado contendrá una copia de las definiciones de las macros. La Directiva PURGE. La ejecución de una instrucción INCLUDE hace que el ensamblador incluya todas las definiciones de macros que están especificadas en la biblioteca. Sin embargo, suponga que una biblioteca contiene las macros SUMA, RESTA, DIVIDE, pero que el programa solo necesita SUMA. La directiva PURGE permite que usted "elimine" la macros RESTA y DIVIDE que no necesita del ensamblado actual: IF1 INCLUDE C:\MACRO.LIB ENDIF PURGE RESTA, DIVIDE necesarias ;Incluye la biblioteca completa ;Elimina la macros no Una operación PURGE facilita solo el ensamblado de un programa y no tiene efecto sobre las macros almacenadas en la biblioteca. 3.3.1 Internas. 3.3.2 Externas. 3.4 Procedimientos. Definición de procedimientos Un procedimiento es un conjunto de instrucciones que tienen la finalidad de ejecutar una tarea especifica dentro de un programa. Los procedimientos son muy similares a las macros. Un procedimiento se declara una sola vez en el código fuente y cuando el programa se ensambla y ejecuta, el procedimiento se coloca en memoria para que pueda ser utilizado por el programa. Las principales ventajas en el uso de procedimientos son: permiten una codificación más limpia y compacta, es decir el código fuente es más pequeño; también permiten el ahorro de memoria, esto es porque un mismo procedimiento puede ser llamado varias veces en el mismo programa y sólo requiere memoria una vez. Los procedimientos tienen la desventaja de que reducen la velocidad de ejecución de los programas, esto se debe a la forma en que los procedimientos se ejecutan. A continuación se presentan los pasos necesarios para ejecutar un procedimiento: 1.- Se encuentra la llamada Call 2.- El microprocesador almacena en la Pila el contenido del IP 3.- Se coloca en el IP el valor del desplazamiento correspondiente al Procedimiento 4.- El microprocesador ejecuta las instrucciones del procedimiento 5.- El procedimiento termina cuando se encuentra la instrucción Ret 6.- Se saca de la pila el valor original del IP y se continua el flujo del programa Un procedimiento se declara de la siguiente forma: PROC nombre instrucción instrucción .... RET ENDP NOMBRE En donde PROC es una palabra reservada que indica el inicio de un procedimiento, RET es una instrucción que indica la terminación del conjunto de instrucciones de un procedimiento y finalmente ENDP es la palabra reservada para fin de procedimiento. Paso de parámetros Los procedimientos en lenguaje ensamblador no cuentan con un mecanismo para el paso de parámetros; por lo cual, la única forma de lograr esto es colocando los parámetros que nos interesan en los registros de propósito general antes de que el procedimiento sea ejecutado. El siguiente procedimiento coloca el cursor en las coordenadas establecidas en Dl y Dh. Proc GotoXY xor bh,bh mov ah,02h int 10h Ret Endp GotoXY En este ejemplo, las coordenadas XY se deben situar en el registro DX antes de que se llame al procedimiento. Llamada a procedimientos Los procedimientos son llamados por los programas por medio de la instrucción CALL, seguida del nombre del procedimiento. Ejemplo: Call GotoXY El siguiente programa muestra la forma de pasarle parámetros a los procedimientos por medio de los registros generales. Este programa declara tres procedimientos: GotoXY: Coloca el cursor en las coordenadas especificadas Limpia_Pantalla: Limpia la pantalla Imprime_Cad: Imprime una cadena en la posición actual del cursor .COMMENT * Programa: Proc2.ASM Autor: Juan Carlos Guzmán C. Descripción: Este programa ilustra la forma de utilizar procedimientos en los programas por medio de la instrucción Call y la forma de pasarles parámetros. .MODEL TINY .DATA Cad1 db 'Esta es una cadena de prueba...',13,10,'$' .CODE INICIO: ;Punto de entrada al programa Mov DL,20 ;X=20 Mov DH,10 ;Y=10 Call Gotoxy ;GotoXY 20,10 Lea DX,cad1 ;DX->Cad1 Call Imprime_Cad ;Imprimir Cad1 Mov Ax,04C00h ;Terminar y regresar al dos Int 21h ; END INICIO ;********************************************************************* ;Procedimiento: GotoXY ;Descripción: Coloca el cursor una posición especifica de la pantalla ;Parámetros: Dl=X, Dh=Y ;********************************************************************* PROC GotoXY Xor Bh,Bh Mov Ah,02h Int 10h Ret ENDP GotoXY ;*********************************************************************** ;Procedimiento: Limpia_Pantalla ;Descripción: Imprime una cadena de caracteres en la posición del cursor ;Parámetros: La dirección de la cadena en DX ;*********************************************************************** PROC Imprime_Cad Mov Ah,09h Int 21h Ret ENDP Imprime_Cad END http://www.monografias.com/trabajos11/lenen/lenen2.s html El segmento de código contiene el código ejecutable de un programa. También tiene uno o mas procedimientos, definidos con la directiva PROC. Un segmento que tiene solo un procedimiento puede aparecer como sigue: NOMBRE nomsegmento nomproc nomproc nomsegmento OPERACION SEGMENT PROC . . . ENDP ENDS OPERANDO PARA FAR COMENTARIO ;Un ;procedimiento ;dentro ;del segmento ;de código El nombre del procedimiento debe estar presente, ser único y seguir las reglas para la formación de nombres del lenguaje. El operando far en este caso esta relacionado con la ejecución del programa. Cuando usted solicita la ejecución de un programa, el cargador de programas del DOS utiliza este nombre de procedimiento como el punto de entrada para la primera instrucción a ejecutar. La directiva ENDP indica el fin de un procedimiento y contiene el mismo nombre que el enunciado PROC para permitir que el ensamblador relacione a los dos. Ya que los procedimientos deben estar por completo dentro de un segmento, ENDP define el final de un procedimiento antes que ENDS defina el final de un segmento. LLAMADA DE PROCEDIMIENTOS. Hasta ahora los segmentos de código han consistido solo en un procedimiento, codificado como: BEGIN PROC FAR . . . BEGIN ENDP En este caso el operador FAR informa al sistema que la dirección indicada es el punto de entrada para la ejecución del programa, mientras que la directiva ENDP define el final del procedimiento. Sin embargo, un segmento de código puede tener cualquier numero de procedimientos, todos distinguidos por PROC y ENDP. Un procedimiento llamado (o subrutina) es una sección de código que realiza una tarea definida y clara (tal como ubicar el cursor o bien obtener entrada del teclado). La organización de un programa en procedimientos proporciona los beneficios siguientes: 1. Reduce la cantidad de código, ya que un procedimiento común puede ser llamado desde cualquier lugar en el segmento de código. 2. Fortalece la mejor organización del programa. 3. Facilita la depuración del programa, ya que los errores pueden ser aislados con mayor claridad. 4. Ayuda en el mantenimiento progresivo de programas, ya que los procedimientos son identificados de forma rápida para su modificación. Operaciones CALL y RET La instrucción CALL transfiere el control a un procedimiento llamado, y la instrucción RET regresa del procedimiento llamado al procedimiento original que hizo la llamada. RET debe ser la ultima instrucción en un procedimiento llamado. Los formatos generales para CALL y RET son: El código objeto particular que CALL y RET generan depende de si la operación implica un procedimiento NEAR (cercano) o un procedimiento FAR (lejano). Llamada y regreso cercanos. Una llamada (CALL) a un procedimiento dentro del mismo segmento es cercana y realiza lo siguiente: Disminuye el SP en 2 (una palabra) Mete el IP (que contiene el desplazamiento de la instrucción que sigue al CALL) en la pila. Inserta la dirección del desplazamiento del procedimiento llamado en el IP (esta operación vacía el resultado de la instrucción previamente procesada), Un RET que regresa desde un procedimiento cercano realiza lo siguiente: Saca el antiguo valor de IP de la pila y lo envía al IP (lo cual también vacía el resultado de la instrucción previamente procesada). Incrementa el SP en 2. Ahora el CS:IP apunta a la instrucción que sigue al CALL original en la llamada del procedimiento, en donde se reasume la ejecución. Llamada y regreso lejanos. Una llamada (CALL) lejana llama a un procedimiento etiquetado con FAR, tal vez en un segmento de código separado. Un CALL lejano mete a la pila al CS y al IP, y RET los saca de la pila. page 60,132 TITLE P08CALLP (EXE) Llamada a procedimientos .MODEL SMALL .STACK 64 .DATA ;--------------------------------------------------------------------.CODE BEGIN PROC FAR CALL B10 ;Llama a B10 ; ... MOV AX,4C00H ;Salida a DOS INT 21H BEGIN ENDP ;--------------------------------------------------------------------B10 PROC NEAR CALL C10 ;Llama a C10 ; ... RET ;De regreso B10 ENDP ;Quien llama ;--------------------------------------------------------------------END BEGIN 3.4.1 Internos. Procedimientos internos Los procedimientos internos son aquellos que se declaran y se llaman dentro del mismo programa, también son llamados procedimientos locales. El listado anterior muestra la forma de utilizar procedimientos internos. 3.4.2 Externos. Procedimientos externos Los procedimientos externos, a diferencia de los internos, se declaran en módulos o programas separados al programa donde el procedimiento es llamado, en otras palabras, la llamada al procedimiento se encuentra en un programa y el procedimiento en otro. Para poder utilizar procedimientos externos, es necesario que sean declarados como públicos en el programa donde se encuentran y que sean llamados como externos en el programa donde serán usados. Para lograr esto, Pass32 cuenta con tres directivas de ensamble: .PUBLIC para declarar los procedimientos como públicos, .EXTERN para indicar que el procedimiento que se va a usar está fuera del programa y .INCLUDE para enlazar el programa que contiene los procedimientos con el programa que los llama. El siguiente programa muestra el uso de las directivas de inclusión. Primeramente, el archivo Proc2.ASM se modificó para que su variable Cad1 fuera declarada como publica, el programa Proc3.ASM contiene la línea .INCLUDE Proc2.ASM, lo cual indica al ensamblador que, en caso de que se soliciten datos, etiquetas o procedimientos externos, éstos se busquen en el archivo incluido. Pass32 proporciona grandes facilidades para el manejo de procedimientos; en este caso, solamente Cad1 debe ser declarada como pública, puesto que los procedimientos se buscan y anexan automáticamente al programa que los llama si es que existen. .COMMENT * Programa: Proc3.ASM Autor: Juan Carlos Guzmán C. Descripción: Este programa ilustra la forma de utilizar procedimientos y datos externos en los programas por medio de las directivas de inclusión include y public. .MODEL TINY .INCLUDE proc2.ASM ;Incluir el archivo proc2.asm ;el cual contiene la variable de cadena ;Cad1 y los procedimientos externos ;usados en este programa. .DATA Cad2 db 'Esta es una cadena de prueba 2...',13,10,'$' .CODE INICIO: ;Punto de entrada al programa Mov Dl,20 ;X=20 Mov Dh,10 ;Y=10 Call GotoXY ;GotoXY 20,10 Lea DX,Cad2 ;DX->Cad2 en Proc3.asm Call Imprime_Cad ;Imprime Cad2 Lea DX,Cad1 ;DX->Cad1 en Proc2.asm Call Imprime_Cad ;Imprime Cad1 Mov AX,04C00h ;Fin del programa Int 21h ; END INICIO END .COMMENT * Programa: Proc2.ASM Autor: Juan Carlos Guzmán C. Descripción: Este programa ilustra la forma de utilizar procedimientos en los programas por medio de la instrucción Call y la forma de pasarles parámetros. .MODEL TINY .DATA .PUBLIC Cad1 db 'Esta es una cadena de prueba...',13,10,'$' .CODE INICIO: ;Punto de entrada al programa Mov DL,20 ;X=20 Mov DH,10 ;Y=10 Call Gotoxy ;GotoXY 20,10 Lea DX,cad1 ;DX->Cad1 Call Imprime_Cad ;Imprimir Cad1 Mov Ax,04C00h ;Terminar y regresar al dos Int 21h ; END INICIO ;********************************************************************* ;Procedimiento: GotoXY ;Descripción: Coloca el cursor una posición especifica de la pantalla ;Parámetros: Dl=X, Dh=Y ;********************************************************************* PROC GotoXY Xor Bh,Bh Mov Ah,02h Int 10h Ret ENDP GotoXY ;*********************************************************************** ;Procedimiento: Limpia_Pantalla ;Descripción: Imprime una cadena de caracteres en la posición del cursor ;Parámetros: La dirección de la cadena en DX ;*********************************************************************** PROC Imprime_Cad Mov Ah,09h Int 21h Ret ENDP Imprime_Cad END Con estas capacidades, es fácil crear bibliotecas de procedimientos y macros que puedan ser utilizados constantemente por los demás programas, ahorrando con ello tiempo de programación al reutilizar código fuente. El siguiente programa muestra la forma de escribir una biblioteca de procedimientos y la forma de utilizarlos en los programas. .COMMENT * Programa: Proc3.ASM Autor: Juan Carlos Guzmán C. Descripción: Este programa ilustra la forma de utilizar procedimientos y datos externos en los programas por medio de las directivas de inclusión include y public. .MODEL TINY .INCLUDE proclib.inc ;Incluir el archivo proclib.inc ;el cual contiene la variable de cadena ;Cad1 y los procedimientos externos ;usados en este programa. .DATA Cad1 db 'Esta es una cadena de prueba 2...',13,10,'$' Cad2 db 'Presiona una tecla...','$' .CODE INICIO: ;Punto de entrada al programa Call limpia_Pantalla ; Mov Dl,20 ;X=20 Mov Dh,10 ;Y=10 Call GotoXY ;GotoXY 20,10 Lea DX,Cad1 ;DX->Cad1 Call Imprime_Cad ;Imprime Cad1 Mov Dl,40 ; Mov Dh,24 ; Call GotoXY ;GotoXY 40,25 Lea DX,Cad2 ; Call Imprime_Cad ;Imprime Cad2 Call Espera_Tecla ;Esperar por una tecla presionada Mov AX,04C00h ;Fin del programa Int 21h ; END INICIO END .COMMENT Biblioteca de Procedimientos en Lenguaje ensamblador .CODE ;********************************************************************* ;Procedimiento: GotoXY ; Descripción: Coloca el cursor una posición especifica de la pantalla ; Parámetros: Dl=X, Dh=Y ;********************************************************************* PROC GotoXY Xor Bh,Bh Mov Ah,02h Int 10h Ret ENDP GotoXY ;*********************************************************************** ;Procedimiento: Imprime_Cad ; Descripción: Imprime una cadena de caracteres en la posición del cursor ; Parámetros: La dirección de la cadena en DX ;*********************************************************************** PROC Imprime_Cad Int 21h Ret ENDP Imprime_Cad ;********************************************************************** ;Procedimiento: Limpia_Pantalla ; Descripción: Limpia la pantalla de la computadora y coloca el cursor ; en 0,0. ; Parámetros: Ninguno ;********************************************************************** PROC Limpia_Pantalla mov ax,0600h mov bh,17h mov cx,0000h mov dx,184fh int 10h Mov dx,0000h Call Gotoxy Ret ENDP Limpia_Pantalla ;********************************************************************** ;Procedimiento: Espera_Tecla ; Descripción: Detiene la ejecución de un programa hasta que se presiona ; una tecla ; Parámetros: Ninguno ;********************************************************************** PROC Espera_Tecla mov ah,10h int 16h Ret ENDP Espera_Tecla http://www.monografias.com/trabajos11/lenen/lenen2.s html Unidad 4. Programación híbrida. Este tipo de programación se da cuando el programador necesita emplear elementos que no estan contemplados dentro de los lenguajes de alto nivel que habitualmente usa. Por ejemplo, la mayor parte de los lenguajes de alto nivel, no soportan o no incluyen instrucciones para el manejo del mouse, lápiz ópticos, adaptadores de juegos, etc. Para solucionar este tipo de problemas, el programador tiene que recurrir a la escritura de parches en lenguaje ensamblador, los cuales actualmente son fáciles de implementar. El problema mayor, que el programador enfrenta al establecer la interfaz entre el lenguaje de alto nivel y el lenguaje ensamblador, es el de conectar realmente los dos programas y pasar las variables de un lado a otro. Afortunadamente, en la actualidad es muy sencillo realizar las interfaces entre dos lenguajes, el siguiente objetivo nos muestra mediante ejemplos, la interconexión entre el Lenguaje Pascal y el Lenguaje ensamblador. El lenguaje Pascal, y en particular el Turbo Pascal, es el lenguaje de programación más usado en la actualidad. Este lenguaje a partir de sus versiones 6.0 tiene la ventaja de que los parches que se realizan en lenguaje ensamblador, ya no sean fuera del código fuente del programa, sino que en el mismo código se introduzcan las instrucciones en lenguaje ensamblador, las cuales seran ensambladas al momento de la compilación. 4.6 Directivas para compilación híbrida. 4.7 Funciones en ensamblador. 4.8 Bloques en ensamblador. 4.9 Operadores. 4.10 Integrar módulos de ensamblador en lenguajes de alto nivel. Más información en el achivo: leng_ensamblador.pdf