85 MÓDULO III PROGRAMACIÓN DEL MICROPROCESADOR El módulo III está conformado por las unidades 7 y 8. El estudio de estas unidades, permite desarrollar en el estudiante las competencias necesarias en la solución de problemas, haciendo uso de los microprocesadores. El desarrollo de la solución implicará, la codificación de algoritmos en el lenguaje ensamblador del microprocesador Intel 80286 y la implementación del programa codificado en un microprocesador de este tipo. El programa así codificado, se ejecutará en un software simulador, el cual proporciona un modelo real de un microprocesador Intel 80286. Objetivo del Módulo III: Implementar con sentido lógico y creativo, programas específicos, haciendo uso de los microprocesadores. El módulo III está estructurado en dos unidades: Unidad 7: Unidad 8: Programación en Lenguaje Ensamblador Implementación de Programas en el Microprocesador 86 UNIDAD 7 Programación en Lenguaje Ensamblador Los programas que convierten un programa de usuario escrito en algún lenguaje, a otro lenguaje, se llaman traductores. El leguaje en el que está escrito el programa original se llama lenguaje fuente, y el lenguaje a que se convierte se llama lenguaje objeto o lenguaje de máquina. El lenguaje fuente es en lo esencial una representación simbólica (lenguaje mnemotécnico) de un lenguaje de máquina numérico, el traductor se llama lenguaje ensamblador. Las computadoras siguen utilizando el lenguaje de máquina para procesar los datos, pero los programas ensambladores traducen antes los símbolos de código de operación especificados a sus equivalentes en lenguaje de máquina. Este proceso es al que se le denomina ensamblado de código. Para facilitar la elaboración de programas a este nivel, se desarrollaron los Ensambladores y el Lenguaje Ensamblador. En la unidad 5 se presenta el uso del lenguaje ensamblador, específicamente el correspondiente al microprocesador 80286. El estudio de esta unidad afianzará en el estudiante los conocimientos obtenidos en el curso, especialmente en lo que respecta a la programación Objetivo de la Unidad 7: Codificar algoritmos, utilizando un lenguaje ensamblador y las técnicas apropiadas, para la resolución de un problema específico. Contenido de la Unidad 7: El contenido de la unidad contempla el estudio de los siguientes temas: Lenguaje ensamblador, Lenguaje de máquina Mnemónicos, Conjunto de instrucciones de un microprocesador específico Programación modular Codificación de algoritmos en un lenguaje ensamblador específico 87 Actividades recomendadas para el estudio del contenido de la unidad 7. 1.- Lea el capítulo 7 del texto Los Microprocesadores Intel, B. B. Brey. 2.- Lea los siguientes contenidos teóricos: Lenguaje de Máquina Es el sistema de códigos directamente interpretable por un circuito microprogramable, como el microprocesador de un ordenador o el microcontrolador de un autómata (un PIC). Este lenguaje está compuesto por un conjunto de instrucciones que determinan acciones a ser tomadas por la máquina. Un programa de computadora consiste en una cadena de estas instrucciones de lenguaje de máquina (más los datos). Estas instrucciones son normalmente ejecutadas en secuencia, con eventuales cambios de flujo causados por el propio programa o eventos externos. El lenguaje de máquina es específico de cada máquina o arquitectura de la máquina, aunque el conjunto de instrucciones disponibles pueda ser similar entre ellas. http://es.wikipedia.org/wiki/Lenguaje_m%C3%A1quina Mnemónico En Informática un Mnemónico es una palabra que sustituye a un código de operación (Lenguaje de máquina), con lo cual resulta más fácil la programación, es de aquí de donde resulta el concepto de lenguaje ensamblador. Un ejemplo común de mnemónico es la instrucción MOV, que le indica al microprocesador que debe de mover datos de un lugar a otro. El microprocesador no entiende palabras, sino que números binarios, por lo que es necesario la traducción del mnemónico a código objeto. http://es.wikipedia.org/wiki/Mnem%C3%B3nico Lenguaje Ensamblador. Una CPU puede interpretar y ejecutar instrucciones de máquina. Estas instrucciones son, simplemente, números binarios almacenados en el computador. Sin un programador quisiera programar directamente en lenguaje de máquina, necesitaría introducir los programas como datos binarios. Considere la sencilla sentencia BASIC: N=I+J+K Suponga que queremos programar esta sentencia en un lenguaje de máquina y dar a I, J y K los valores iniciales 2,3, y 4, respectivamente. La forma de hacer esto se muestra en la figura 7.1a. El programa empieza en la posición 101 88 (hexadecimal). Se reserva memoria parea las cuatro variables a partir de la posición 201. El programa consta de cuatro instrucciones: 1. 2. 3. 4. Cargar el contenido de la posición 201 en el acumulador (AC). Sumar a AC el contenido de la posición 202. Suma a AC el contenido de la posición 203. Memorizar el contenido de AC en la posición 204. Esto es claramente un proceso tedioso y muy susceptible de errores. Una ligera mejora consiste en redactar el programa en hexadecimal, e lugar de en binario, ver figura 7.1b. Para que la mejora sea más significativa, podemos hacer uso de nombres simbólicos o mnemotécnicos de las instrucciones. El resultado es el programa simbólico mostrado en la figura 7.1c. Cada línea sigue representando una posición de memoria, y consta de tres campos separados por espacios. El primer campo contiene la dirección de una posición. El segundo campo contiene el símbolo de tres letras que representa su código de operación. Si se trata de una instrucción que hace referencia a memoria, un tercer campo contiene la dirección. Para memorizar un dato concreto en una posición dada, nos inventamos una pseudoinstrucción con el símbolo DAT. Esta es meramente un indicador de que el tercer campo de la línea contiene un número en hexadecimal a memorizar en la posición que especifica el primer campo. El uso de programas simbólicos hace la vida mucho más fácil, pero es aun engorroso. En particular hay, hay que dar una dirección absoluta para cada palabra. Un procedimiento mejor, es emplear direcciones simbólicas. Esto se ilustra en la figura 7.1d. Cada línea sigue teniendo tres campos. El primero sigue siendo para la dirección, pero se utiliza un símbolo en lugar de una dirección numérica absoluta. Algunas líneas carecen de dirección, indicando que la dirección de dicha línea es uno más que la dirección precedente. Para las instrucciones que hacen referencia a memoria, el tercer campo contiene también una dirección simbólica. Con este último refinamiento, hemos inventado un lenguaje ensamblador. Los programas escritos en lenguaje ensamblador (programas en ensamblador) se traducen a lenguaje de máquina mediante un ensamblador. El desarrollo de los lenguajes ensambladores fue un logro importante en la evolución de la tecnología de computadores. Fue el primer paso hacia los lenguajes de alto nivel de hoy en día. 89 Dirección 101 102 103 104 0010 0001 0001 0011 Contenido 0010 0000 0010 0000 0010 0000 0010 0000 0001 0010 0011 0100 101 102 103 104 LDA ADD ADD STA 201 202 203 204 201 202 203 204 0000 0000 0000 0000 0000 0000 0000 0000 0010 0011 0100 0000 201 202 203 204 DAT DAT DAT DAT 2 3 4 0 0000 0000 0000 0000 (a) Programa en binario (c) Programa simbólico Dirección 101 102 103 104 Contenido 2201 1202 1203 3204 Etiqueta FORMUL Operación LDA ADD ADD STA Operando I J K N 201 202 203 204 0002 0003 0004 0000 I J K N DATA DATA DATA DATA 2 3 4 0 (a) Programa en hexadecimal (c) Programa en ensamblador Figura 7.1. Cálculo de la fórmula N = I + J + K. Stallings, 2000.pp. 346. Conjunto de instrucciones Un conjunto de instrucciones ó repertorio de instrucciones ó ISA (del inglés instruction set architecture -arquitectura del conjunto de instrucciones-) es una especificación que detalla las instrucciones que una CPU de un ordenador puede entender y ejecutar, o el conjunto de todos los comandos implementados por un diseño particular de una CPU. El término describe los aspectos del procesador generalmente visibles a un programador, incluyendo los tipos de datos nativos, las instrucciones, los registros, la arquitectura de memoria y las interrupciones, entre otros aspectos. La arquitectura del conjunto de instrucciones (ISA) se emplea a veces para distinguir este conjunto de características de la microarquitectura, que son los elementos y técnicas que se emplean para implementar el conjunto de instrucciones. Entre estos elementos se encuentras las microinstrucciones y los sistemas de caché. Procesadores con diferentes diseños internos pueden compartir un conjunto de instrucciones; por ejemplo el Intel Pentium y AMD Athlon implementan 90 versiones casi idénticas del conjunto de instrucciones x86, pero tiene diseños internos completamente opuestos. http://es.wikipedia.org/wiki/Conjunto_de_instrucciones Diseño del repertorio de instrucciones Uno de los aspectos más interesantes y más analizado del diseño de un computador, es el diseño del repertorio de instrucciones del lenguaje de máquina. El diseño de un repertorio de instrucciones es muy complejo, ya que afecta a muchos aspectos del computador. El repertorio de instrucciones define muchas de las funciones realizadas por la CPU y tiene, por tanto, un efecto significativo sobre la implementación de la misma. El repertorio de instrucciones es el medio que tiene el programador para controlar la CPU. Algunos de los aspectos más básicos relativos al diseño son los siguientes: • • • • • Repertorio de operaciones: Cuántas y qué operaciones considerar, y cuán complejas deben ser. Tipos de datos: Los distintos tipos de datos con los que se efectúan las operaciones. Formato de instrucciones: Longitud de la instrucción (bits), número de direcciones, tamaño de los distintos campos, etc. Registros: Número de registros de la CPU que pueden ser referenciados por instrucciones, y su uso. Direccionamiento: El modo o modos de direccionamiento mediante los cuales puede especificarse la dirección del operando. Stallings, 2000. pp. 318. El microprocesador Intel 80286 El Intel 80286 (llamado oficialmente iAPX 286, también conocido como i286 o 286) es un microprocesador de 16 bits de la familia x86, que fue lanzado al mercado por Intel el 1 de febrero de 1982. Las versiones iniciales del i286 funcionaban a 6 y 8 MHz, pero acabó alcanzando una velocidad de hasta 20 MHz. El i286 fue el microprocesador más empleado en los IBM PC y compatibles entre mediados y finales de los años 80. El i286 funciona el doble de rápido por ciclo de reloj que su predecesor (el Intel 8086) y puede direccionar hasta 16 Mbytes de memoria RAM, en contraposición a 1 Mbyte del i8086. En máquinas DOS, esta memoria adicional solo podía ser accedida a través de emulación de memoria extendida. De todos modos, pocos ordenadores basados en el i286 tuvieron más de 1 Mbyte de memoria. El i286 fue diseñado para ejecutar aplicaciones multitarea, incluyendo comunicaciones (como centralitas automatizadas), control de procesos en tiempo real y sistemas multiusuario. Para ello se le añadió un modo protegido, 91 en el cual existían cuatro anillos de ejecución y división de memoria mediante tablas de segmentos. En este modo trabajaban las versiones de 16 bits del sistema operativo OS/2. A pesar de su gran popularidad, hoy en día quedan pocos ordenadores con el i286 funcionando. El sucesor del i286 fue el Intel 80386, de 32 bits. http://es.wikipedia.org/wiki/Intel_80286 3.- En el ejemplo 7.1, se muestra un programa fuente que permite desplegar un mensaje en pantalla mediante llamadas al DOS. Ejemplo 7.1: Programa fuente que imprime un mensaje en la pantalla. Aquí se tratará todo lo concerniente con el lenguaje ensamblador y el conjunto de directivas del Microsoft Macro Assembler v4.0. Si bien esto puede resultar bastante extenso y complejo, aquí sólo se describirán las instrucciones y directivas básicas. Para comenzar se presenta un pequeño ejemplo que ilustra el formato del programa fuente. Este ejemplo está completamente desarrollado en lenguaje ensamblador que usa servicios o funciones de MS-DOS (system calls) para imprimir el mensaje Hola mundo en pantalla. ; HOLA.ASM ; Programa clasico de ejemplo. Despliega una leyenda en pantalla. STACK SEGMENT STACK ; Segmento de pila DW 64 DUP (?) ; Define espacio en la pila STACK ENDS DATA SEGMENT ; Segmento de datos SALUDO DB "Hola mundo",13,10,"$" ; Cadena DATA ENDS CODE SEGMENT ; Segmento de Código ASSUME CS:CODE, DS:DATA, SS:STACK INICIO: MOV AX,DATA MOV DS,AX MOV DX,OFFSET SALUDO MOV AH,09H INT 21H MOV AH,4CH INT 21H CODE ENDS END INICIO ; Punto de entrada al programa ; Pone dirección en AX ; Pone la direccion en los registros ; Obtiene direccion del mensaje ; Función: Visualizar cadena ; Servicio: Funciones alto nivel DOS ; Función: Terminar ; Marca fin y define INICIO 92 La descripción del programa es como sigue: • Las declaraciones SEGMENT y ENDS definen los segmentos a usar. • La variable SALUDO en el segmento DATA, define la cadena a ser desplegada. El signo de dólares al final de la cadena (denominado centinela) es requerido por la función de visualización de la cadena de MS-DOS. La cadena incluye los códigos para carriage-return y line-feed. • La etiqueta INICIO en el segmento de código marca el inicio de las instrucciones del programa. • La declaración DW en el segmento de pila define el espacio para ser usado por el stack del programa. • La declaración ASSUME indica que registros de segmento se asociarán con las etiquetas declaradas en las definiciones de segmentos. • Las primeras dos instrucciones cargan la dirección del segmento de datos en el registro DS. Estas instrucciones no son necesarias para los segmentos de código y stack puesto que la dirección del segmento de código siempre es cargada en el registro CS y la dirección de la declaración del stack segment es automáticamente cargada en el registro SS. • Las últimas dos instrucciones del segmento CODE usa la función 4CH de MS-DOS para regresar el control al sistema operativo. Existen muchas otras formas de hacer esto, pero ésta es la más recomendada. • La directiva END indica el final del código fuente y especifica a START como punto de arranque. 5.- El ejemplo 7.2 muestra la importancia del uso de las directivas PUBLIC y EXTRN en la programación modular. Ejemplo 7.2: Uso de directivas PUBLIC y EXTRN. A continuación se presentan dos módulos de programa: MAIN y TASK, el primer módulo corresponde al módulo principal, mientras que el segundo al módulo que contiene una rutina. Ambos módulos son archivos que se editan por separado, se ensamblan por separado, pero se ligan juntos. 93 MODULO PRINCIPAL: MAIN.ASM NAME MAIN PUBLIC EXIT EXTRN PRINT:NEAR STACK SEGMENT WORD STACK 'STACK' DW 64 DUP(?) STACK ENDS DATA DATA CODE SEGMENT WORD PUBLIC 'DATA' ENDS SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:CODE, DS:DATA START: MOV AX, DATA MOV DS, AX JMP PRINT ; carga localización del segmento ; en el registro DS ; va a PRINT en el otro modulo EXIT: MOV AH, 4CH INT 21H CODE ENDS END START SUBMODULO: TASK.ASM NAME TASK PUBLIC PRINT EXTRN EXIT:NEAR DATA SEGMENT WORD PUBLIC 'DATA' ENTRADA DB "Entrando a un submodulo....",13,10,"$" SALIDA DB ".......saliendo del submodulo.",01,07,13,10,"$" DATA ENDS CODE SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:CODE, DS:DATA PRINT: MOVE AH,06H ; Función para borrar pantalla MOV AL,0 ; todas las líneas MOV CX,0 ; de 0,0 MOV DH,24D MOV DL,79D MOV BH,0 ; atributo en líneas vacías INT 10H ; Ser vicio de e/s vídeo MOV DX, OFFSET ENTRADA MOV AH,09H INT 21H MOV DX, OFFSET SALIDA INT 21H 94 CODE JMP ENDS END EXIT ; Regresa al otro modulo Ejercicios propuestos 1.- Codifique una rutina en el lenguaje ensamblador del 80286 que permita el ingreso de caracteres, verificando si se ha oprimido una tecla pero sin esperar que ocurra. Si esto ha ocurrido entonces devuelve su codificación ASCII en un registro; en caso contrario devuelve cero. 2.- Considere una cadena como una secuencia de caracteres terminada por un byte cero, y se requiere calcular su longitud. Con base a lo antes planteado, codifique en el lenguaje ensamblador 80286 un algoritmo que permita calcular la longitud de la cadena de caracteres. Consulta en otros libros [Stallings 2000] Incluye el repertorio de instrucciones de dos familias de computadores: el Pentium II de Intel y el Power PC. [Englander 2002] Incluye la descripción del conjunto de instrucciones de la familia x86. Consulta en la Web http://proton.ucting.udg.mx/dpto/maestros/mateos/novedades/ensamblador/68H C11.html:Contiene información interesante relacionada con el lenguaje ensamblador. http://www.alpertron.com.ar/80286.HTM: Describe el microprocesador 80286 y las instrucciones adicionales que controlan el sistema de memoria virtual. Ejercicios de Autoevaluación 1.- Desarrolle un procedimiento llamado SUMS, que permita sumar el valor del contenido de los registros BX, CX y DX con el contenido de AX. Utilice la definición de procedimiento cercano (NEAR). 95 2.-Codifique un programa que despliegue OK en la pantalla del monitor, utilizando para ello el procedimiento DISP mostrado a continuación: DISP DISP PROC MOV INT RET ENDP NEAR AH, 2 21H Respuesta a los Ejercicios de Autoevaluación 1.- El procedimiento es el siguiente: SUMS SUMS PROC NEAR ADD AX, BX ADD AX, CX ADD AC, DX RET ENDP 2.- A continuación se muestra el programa: .MODEL .CODE STARTUP TINY MOV MOV CALL MOV CALL .EXIT END BX, OFFSET DISP DL, ‘O’ BX0 DL, ‘K’ BX