Page 1

Desarrollo de programas bรกsicos El lenguaje ensamblador. Las instrucciones ejecutables en ensamblador Las directivas en ensamblador. Las subrutinas. Ejemplos de subrutinas. Errores al programar en ensamblador. Ejercicios.


microprocesadores microprocesador electronica instrucciones set direcciones datos bus 8085 intel foto macro olympus zuiko potencia direccionamiento decodificador salto bucle logicas aritmeticas condicionales programa ensamblador

http://micros.myblog.es/ http://issuu.com/microprocesadores/ © 2010 Celestino Benítez Vázquez Todos los contenidos e imágenes publicados en la revista “microprocesadores” tienen los derechos reservados quedando prohibida la reproducción total o parcial de esta publicación sin el consentimiento expreso de su autor.


6. DESARROLLO DE PROGRAMAS BÁSICOS. Por todo lo expuesto hasta el momento, sabemos que para poder llevar a cabo el desarrollo de programas de aplicación, es decir, programas que cargados en la memoria del sistema realicen una tarea específica y particular, será preciso planificar dicho desarrollo de forma estructurada, empleando el máximo de recursos disponibles a nuestro alcance, como diagramas de flujo, etc. Además debemos emplear para ello determinados programas llamados de desarrollo que sirven para construir los programas de aplicación, corregirlos y ejecutarlos. Nosotros emplearemos para confeccionar nuestros programas el “ensamblador”. Debemos tener en cuenta además, como punto muy importante y que nunca debemos olvidar, que los programas de aplicación que construyamos, deben ir acompañados siempre de la máxima documentación posible, este es un punto de considerable importancia. Además sabemos que en las aplicaciones de sistemas basados en microprocesadores, la integración entre software y hardware debe ser total, por lo que para poder realizar nuestros programas, debemos conocer lo más profundamente posible todos los componentes de nuestro sistema, sus direcciones, capacidades, etc. En nuestro caso los programas son desarrollados para el sistema cuyo hardware se ha diseñado en temas anteriores, aunque será muy sencillo realizar las modificaciones que fueran necesarias para trasladarlo a otro sistema basado en el mismo microprocesador.


6.1 EL LENGUAJE ENSAMBLADOR. Como ya se ha mencionado en varias ocasiones, resulta muy problemático tener que desarrollar programas directamente en hexadecimal e incluso en binario dado que el código máquina es muy propicio para cometer errores y dificulta el desarrollo de programas. Por esto, surgen los programas ensambladores, que nos proporcionan un nivel superior de programación. Son programas simbólicos que emplean código mnemotécnico para referirse a las instrucciones y permiten la utilización de nombres simbólicos para señalar direcciones de memoria, constantes, etc. Una vez construido el programa el ensamblador lo convertirá en código máquina (binario) ejecutable por el microprocesador. Además el ensamblador, permite la división de un programa en módulos, que nos facilitarán considerablemente la creación de programas cada vez más complejos. También será posible la detección de errores en la sintaxis del programa, aunque no se detectan errores lógicos. El programa que se escribe en ensamblador recibe el nombre de “programa fuente” y su escritura se realiza mediante un editor de textos. Los programas ensambladores suelen disponer de su propio editor, de no ser así, podremos emplear el editor del sistema operativo MS-DOS. Todos los programas fuente deben llevar la extensión “ASM”. Cuando el ensamblador traduce los mnemónicos del programa fuente a valores binarios (código máquina), se obtiene el “programa objeto” que podrá ser ejecutado por el microprocesador una vez almacenado en la memoria del sistema. Todos los programas objeto deben llevar la extensión “OBJ”.

También debemos indicar que se pueden desarrollar varios ficheros constituidos por diferentes módulos de programas objetos que posteriormente serán unidos entre sí para formar el programa completo. Esta función de unión de módulos la realizan el programa “enlazador” o “linker”, que es otra utilidad del ensamblador. El proceso fundamental de la programación, requiere llevar a cabo una serie de pasos organizados, que nos hagan llegar a una solución correcta del problema planteado. Estos pasos o fases, pueden resumirse en: •

Definición, estudio y comprensión del problema que se afronta.

Encontrar un algoritmo que nos resuelva el problema, establecer las estructuras de datos, las entradas necesarias, el flujo de control y las salidas deseadas.

Codificar el algoritmo anterior en función del lenguaje empleado, en este caso el ensamblador. Es conveniente expresar de forma muy clara la diferencia entre el grupo de instrucciones y los bloques de datos que se vayan a emplear.

Corregir y verificar el programa, para lo cual podemos servirnos de las ayudas que nos proporciona el ensamblador.

Realizar una prueba que nos permita afirmar que el programa responde de forma adecuada a lo requerido en la definición del problema.

Documentar de forma adecuada cada trabajo realizado. Este paso es muy importante, debe tenerse en cuenta que cuanta más información sea aportada junto al programa más fácil resultará en el transcurso del tiempo poder realizar alguna modificación o reforma si fuera preciso.


6.2 LAS INSTRUCCIONES ENSAMBLADOR.

EJECUTABLES

EN

Evidentemente son aquellas que ejecuta el microprocesador, pero expresadas según su representación simbólica. Como sabemos cada ensamblador posee unas características propias que lo diferencian del resto, aunque todos son muy parecidos entre sí. Nosotros, emplearemos el “2500 A.D. 8085 CROSS ASSEMBLER FOR MSDOS/CPM 86”, que empleará en su ejecución las extensiones de los siguientes ficheros: ASM

Entrada del ensamblador (programa fuente).

OBJ

Salida del ensamblador (programa objeto).

LST

Fichero que contiene el listado.

OBJ

Fichero de entrada del enlazador.

TSK

Salida del Link1.

HEX

Salida del Link2.

El ensamblador posee además una serie de comandos, activos durante el proceso de ensamblado, que nos permiten actuar sobre el mismo pudiendo detener el proceso, finalizar el proceso, mostrar los datos en pantalla o sacarlos en impresora, llevar los datos a un disco, etc. Para llamar al ensamblador, se creará un programa fuente (con extensión ASM) compuesto por una serie de líneas, cada una de las cuales contendrá una sola sentencia. Cada línea estará compuesta de los siguientes campos: 1.

Campo de etiqueta.- Es únicamente de carácter identificativo. Puede utilizarse si es necesario o podemos dejarlo en blanco en caso contrario. El empleo de etiquetas proporciona gran comodidad para hacer referencia a determinadas zonas del programa. Las etiquetas pueden tener cualquier número de caracteres, aunque solo serán tenidos en cuenta los 8 primeros. Deben situarse al principio de la línea en la columna 1 y su nombre debe comenzar por una letra aunque también puede llevar números. Al final de la etiqueta debemos colocar dos puntos (:). Es importante asignar a las etiquetas nombres significativos que nos identifiquen determinadas partes del programa por sí mismos.

2.

Campo de código.- A continuación de la etiqueta, se sitúa el código de la instrucción, en este caso el mnemónico de la instrucción que se desea ejecutar. Este campo es de uso obligatorio, siempre debe haber un mnemónico en él.

3.

Campo de operandos.- Aparece a continuación del campo de código y su sintáxis varia según la instrucción que se emplee. Puede llevar uno o más operandos y en este caso irán separados por una coma (,). Se sitúa en primer lugar el operando destino y el operando origen en segundo lugar. También pueden ser expresados mediante nombres simbólicos, nombres de registros, valores numéricos, expresiones aritméticas, etc. Los operadores disponibles en el ensamblador para expresar operaciones son entre otros: + * / ** .NOT. .MOD. .SHR. .SHL. .AND. .OR. .XOR. .EQ. .GT. .LT.

Suma Resta Multiplicación División Potenciación Complementación Desplazamiento a la derecha Desplazamiento a la izquierda Operación lógica AND Operación lógica OR Operación exclusiva OR Igual Mayor que Menor que


4.

Campo de comentario.- Se sitúa después del campo de operandos. No es obligatoria su utilización, es decir, podemos dejarlo en blanco. Su función es exclusivamente informativa. Debe comenzar con el símbolo punto y coma (;) seguido del comentario que se desee. Los comentarios no formarán parte del código objeto que proporcionará el ensamblador, sin embargo pueden ser muy útiles cuando intentemos modificar un programa que ya ha sido escrito hace algún tiempo o bien ha sido escrito por otra persona. (Todas las aclaraciones acerca del funcionamiento de un programa son siempre positivas y bien recibidas por la persona encargada de revisarlo).

Además de las líneas de instrucciones ejecutables, en ensamblador son muy empleadas las líneas completas para comentarios, que comienzan necesariamente por el símbolo punto y coma (;) y normalmente son situadas al principio del programa o al principio de cada módulo o subrutina. Nosotros emplearemos estas líneas para documentar nuestras subrutinas y programas, incluyendo en ellas el nombre, función, precondiciones, etc. El final del programa debe estar marcado siempre mediante la instrucción especial del lenguaje ensamblador (pseudoinstrucción o también llamada directiva de ensamblador) END. Por todo esto, nuestros programas en ensamblador estarán constituidos en principio bajo la siguiente estructura:

; NOMBRE: ; FUNCION: ; PRECONDICIONES: ; POSTCONDICIONES: ETIQUETA

INSTRUCCIÓN OPERANDOS Instrucción 1 Instrucción 2 Instrucción 3 ..................... ..................... ..................... END

COMENTARIOS

6.3 LAS DIRECTIVAS EN ENSAMBLADOR. Además de las instrucciones básicas, propias de cada microprocesador (en este caso el 8085) que son transformadas por el ensamblador en lenguaje máquina, hay otro tipo de instrucciones, disponibles en el lenguaje ensamblador y denominadas “directivas de ensamblador” o también pseudoinstrucciones, que sirven para facilitar las tareas de programación, pero que no son tenidas en cuenta por el ensamblador para la obtención del código máquina. Por tanto las directivas no formaran parte del código objeto. Puede decirse que su misión principal es indicar al programa ensamblador cómo deseamos que lleve a cabo las tareas de traducción del programa fuente para obtener el programa objeto. Entre las directivas más importantes pueden destacarse: ORG X1 Nos marca la posición inicial de memoria (mediante X1) a partir de la cual deseamos cargar las instrucciones y los datos que podamos establecer a continuación. Esta directiva consta de dos partes perfectamente diferenciadas, el código mnemotécnico (ORG) y un valor numérico (X1) normalmente en hexadecimal o una expresión que representa la dirección de comienzo. Si no empleamos esta directiva, el ensamblador situará el programa a partir de la dirección 0000H, que es la dirección establecida por defecto.


END Marca el final del programa fuente. Debe colocarse siempre al final del programa y puede incluir un valor que señale al inicio de ejecución del programa. El empleo de esta directiva es obligatorio. Nombre EQU X2 Esta pseudoinstrucción sirve para establecer la igualdad entre un Nombre y un valor numérico (X2). Es una directiva de asignación que nos dará la posibilidad de poder emplear dicho Nombre en cualquier punto del programa donde deba ser utilizado. Estos nombres son llamados “constantes de ensamblado” y nos proporcionan la posibilidad de cambiar el valor de dicha constante en una única posición del programa (en su lugar de definición) sin necesidad de tener que recorrer todo el programa cambiando dicho valor en todos los puntos donde se utilice. La directiva EQU debe ir precedida del Nombre de la constante y seguida del valor que deseamos asignar a dicha constante. El valor asignado a una constante de ensamblado, no puede cambiar a lo largo del desarrollo del programa y por tanto, será el mismo durante todo el proceso de ensamblado. Nombre VAR X3 Esta directiva iguala el valor de X3 al Nombre que precede a la propia directiva VAR, sin embargo, puede cambiarse según se desee a lo largo del programa. Un Nombre definido como una variable no puede ser definido posteriormente por una directiva EQU. Nombre DB X4 Es una directiva del ensamblador mediante la cual podemos reservar espacio en la memoria de nuestro sistema para almacenar datos, y además cargar datos concretos en dichas posiciones de memoria. La directiva puede ir precedida de un Nombre y seguida de los valores que deseamos colocar en las posiciones de memoria, debidamente separados por el signo coma (,). Los valores a colocar pueden ser de cualquier tipo, tales como decimales, hexadecimales, ASCII, etc. Los valores ASCII deben ir colocados entre comillas.


Nombre DW X5 Es una directiva que opera de forma similar a la directiva anterior DB, pero en esta ocasión se emplea para reservar espacio en memoria para palabras de dos bytes (16 bits). Por tanto los datos que se colocan después de una directiva DW serán palabras de dos bytes (por ejemplo 1234H) separados por comas. Se almacena en primer lugar el byte menos significativo de la palabra y a continuación el byte más significativo. Nombre LWORD X6 Trabaja exactamente igual que DB pero con palabras de una longitud de 32 bits (cuatro bytes). Por tanto esta directiva va precedida de un Nombre y seguida de palabras de cuatro bytes separadas por comas. Nombre ASCII X7 La directiva ASCII sirve para almacenar una cadena de caracteres ASCII en memoria. Esta directiva va precedida de un Nombre y seguida de una cadena de caracteres ASCII, al final de la cual se debe introducir un retorno de carro o el símbolo (:). Nombre DS X8 La directiva DS reserva en memoria el número de bytes especificados en la expresión X8. Los bytes reservados en la memoria del sistema contendrán a partir de ese momento el valor 00H. Nombre BLKW X9 Reserva el número de palabras especificadas por la expresión X9. Las palabras reservadas se cargan con el dato 00H (son puestas a cero) y la etiqueta Nombre es opcional. Además de todas estas pseudoinstrucciones enumeradas hasta el momento, existen otras que nos permiten las definiciones de “macros”, es decir, nos permiten crear funciones que se utilizan para ejecutar una serie de operaciones especificadas dentro de la macro, así como distintas directivas que no presentaremos aquí y que dependen básicamente del programa ensamblador empleado. El ensamblador nos ofrece como ayuda adicional la posibilidad de corregir una serie de errores de tipo sintáctico, cometidos al crear el programa fuente (.ASM). Aunque estas ayudas o indicaciones de errores dependen del programa ensamblador empleado, podemos señalar aquí algunas comunes a la gran mayoría de ellos: •

• • •

• • • •

Illegal Register Pair: Nos indica el empleo incorrecto o ilegal de un par de registros para una instrucción determinada. Por ejemplo, la instrucción "ADD B,H" nos daría este error, ya que la forma correcta sería "ADD reg" que realiza la operación de suma entre el acumulador y el registro especificado (reg) guardando el resultado en el acumulador. Multiply Defined Symbol: Nos indica que hemos escrito un símbolo (una etiqueta) en más de una ocasión con el mismo nombre. Illegal Mnemonic: Este mensaje de error se presenta cuando empleamos una instrucción (mnemónico) que no existe para el microprocesador, ni esta definido previamente en una macro. Hex # And Symbol Are Identical: Se produce cuando empleamos una etiqueta idéntica a un número expresado en hexadecimal. Por ejemplo ABCH: NOP LXI H,ABCH Undefined Symbol: Surge cuando en una instrucción se emplea una etiqueta que no ha sido previamente definida y por tanto el ensamblador no la reconoce. Illegal ASCII Designator: Nos indica que hemos intentado incluir un carácter ASCII y se ha cometido un error de puntuación. Por ejemplo MVI A,"T´. Attemped Division By Zero: Nos señala que ha encontrado una división en la que el divisor es cero y por tanto es una operación ilegal. Missing End Statement: Señala que el ensamblador no encuentra la directiva END que indica el final del programa.


6.4 LAS SUBRUTINAS. Como sabemos, el lenguaje ensamblador ofrece además, la posibilidad de crear partes del programa independientes, denominadas subrutinas, que podrán ser llamadas posteriormente desde cualquier parte del programa principal e incluso desde otra subrutina, mediante la instrucción CALL. De esta forma el programa completo estará constituido esencialmente por una serie de pequeños programas llamados subrutinas, que pueden ser llamadas en repetidas ocasiones, evitándonos por tanto, escribir código repetido. Como se ha mencionado anteriormente, las subrutinas son pequeños programas y como tal debemos aportar a cada una de ellas la documentación o información suficiente para que puedan ser tratadas independientemente, e incluso puedan ser empleadas en programas distintos. Por esto, las subrutinas llevarán el encabezamiento propio de un programa y requerirán el estudio detallado de todas las condiciones en que pueden ser ejecutadas, es decir, la estructura inicial de toda subrutina será:

;NOMBRE: ;FUNCION: ;PRECONDICIONES: ;POSTCONDICIONES:


A continuación comenzará el cuerpo de la subrutina propiamente dicho, que llevará al principio una etiqueta con el nombre de la subrutina, es decir, el parámetro que deberemos especificar en la instrucción de llamada a subrutina CALL. Normalmente el nombre asignado a las subrutinas es suficientemente significativo como para darnos una idea o sugerir la función que desarrollarán las mismas. A continuación de la etiqueta se sitúan las instrucciones que componen la subrutina y finalmente es imprescindible concluir la subrutina con una instrucción RET que devuelve el control del programa a la posición desde donde se efectuó la llamada CALL a subrutina. Debemos recordar que además de la llamada CALL a subrutinas, disponemos también de otro tipo de llamadas a subrutinas con condiciones como CC LABEL, CNC LABEL, CZ LABEL, etc. (que podemos recordar revisando el tema 4) que efectuarán la llamada a la subrutina cuyo nombre venga especificado en LABEL si se cumple la condición que lleva implícita la instrucción que en cada caso se emplee. También disponemos de varias instrucciones para salir de una subrutina, además de la instrucción RET. Estas instrucciones (que pueden analizarse en el tema 4) son entre otras RM, RNC, RNZ, RP, etc. y provocan el retorno de la subrutina si se cumple la condición que lleva implícita la instrucción empleada en cada caso concreto. En definitiva, una subrutina tendrá para nosotros la siguiente estructura: ;NOMBRE: ;FUNCION: ;PRECONDICIONES: ;POSTCONDICIONES: NOMBRE: Instrucción 1 Instrucción 2 Instrucción 3 .................... Instrucción N RET Las subrutinas deben ser vistas desde el programa principal como un bloque o una caja a la que se accede proporcionándole unos datos de entrada que llamaremos “parámetros de entrada” y después de ser ejecutada nos proporciona uno o más resultados. Hay varias formas de comunicarse con las subrutinas pasando y recibiendo datos, entre las que podemos destacar: •

El empleo de registros.- Su empleo está basado en la utilización de los registros del microprocesador libres para su uso en ese momento. Se almacenarán en ellos los datos que precise la subrutina para desarrollar su función y una vez ejecutada la subrutina, nos devolverá los resultados en los registros precisos. Este método está limitado por el bajo número de registros de que disponemos en el microprocesador 8085. Además se producen modificaciones en el contenido de los registros que deben ser tenidas en cuenta y en caso de que no nos interese modificarlos, tendremos que guardar previamente su contenido en la pila y recuperarlo al finalizar la subrutina.

Empleo de memoria RAM.- En este caso el programa debe colocar en posiciones consecutivas de la memoria RAM del sistema, todos los parámetros que necesite la subrutina para desarrollar su función. A continuación se llama a la subrutina, la cual busca en la RAM los parámetros introducidos para trabajar con ellos. Es importante tener en cuenta el orden en que se situaron dichos parámetros para no cometer errores en el tratamiento de los datos. Ejecutadas las tareas precisas por parte de la subrutina, nos devolverá los resultados en las mismas posiciones de memoria en que le pasamos los datos. El programa principal buscará ahora los resultados en la RAM para proseguir con su desarrollo normal.


6.5 EJEMPLOS DE SUBRUTINAS. En este apartado se tratarán de desarrollar de la forma más precisa y clara, una serie de subrutinas que podrán ser utilizadas posteriormente en otros programas más amplios, sin más que tener en cuenta las precondiciones y las postcondiciones de las mismas. Con ánimo de agilizar la construcción de varios ejemplos, nos limitaremos a adaptar los programas desarrollados con pseudocódigo en el tema anterior, para lo cual deberemos convertirlos a lenguaje ensamblador y ajustarlos a las necesidades y capacidades de nuestro sistema básico. Como veremos, cuando se realiza un programa fuente en ensamblador, es necesario colocarle la extensión ASM al nombre que deseemos asignarle y una vez que llamemos al ensamblador nos generará un fichero con extensión OBJ que representa el código objeto correspondiente. Además también se genera un fichero con extensión LST que representa el listado del programa. Este último, es el que se presentará aquí. En él podrán apreciarse con claridad los cuatro campos (etiqueta, código, operandos y comentarios) que se han mencionado con anterioridad, pero además se nos muestra una primera columna en la que nos indican el número de línea de código en que nos encontramos, seguida de la columna de direcciones y a continuación los valores hexadecimales generados durante el ensamblado, correspondientes a las instrucciones. También podremos observar que todas las subrutinas aquí presentadas, se sitúan a partir de la dirección 0000H. Esto se ha hecho por comodidad, pero en realidad pueden situarse en cualquier otra posición de memoria que nos interese. Para entender de una forma más clara y precisa el desarrollo y construcción de las subrutinas, es importante compararlas con los desarrollos de pseudocódigo realizados en el tema anterior. Ejemplo 1.- Se desarrollará la subrutina correspondiente al ejemplo 1 del tema anterior y que denominaremos también MAYOR12. Se inicia la subrutina guardando en la pila el contenido de los registros señalados por PSW, por lo que debemos tener la precaución de recuperar dichos datos antes de salir de ella mediante el empleo de la instrucción RET. La última instrucción END sirve para indicar al ensamblador que se ha llegado al final del programa, pero no forma parte de la subrutina. 2500 A.D. 8085 CROSS ASSEMBLER VERSION 3.41a -------------------------------------------------INPUT FILENAME : OUTPUT FILENAME :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

;-----------------------------------------;NOMBRE : MAYOR12 ;FUNCION : Si Dato>12 va a B ;PRECONDICION : HL = Dirección del Dato ;POSTCONDICION : Resultado en B si Dato>12 ;-----------------------------------------0000 0000 0001 0002 0004 0007 000A 000B 000C 000D

*********** FIN

MAYOR12.ASM MAYOR12.OBJ

MAYOR12: F5 7E FE 0C CA 0B 00 DA 0B 00 46 F1 C9

S Y M B O L I C 000B

FIN

R E F E R E N C E

MAYOR12

LINES ASSEMBLED :

PUSH PSW MOV A,M CPI 12 JZ FIN JC FIN MOV B,M POP PSW RET END T A B L E

*************

0000 17

ASSEMBLY ERRORS :

0


Es preciso darse cuenta que, según las precondiciones planteadas, para acceder a esta subrutina, habrá que cargar en el par de registros HL la dirección del Dato que deseamos tratar. Además, al obtener el resultado de la subrutina en el registro B, debemos tener presente que la información que temporalmente pudiéramos tener depositada en el mismo podría perderse si no tenemos la precaución de ponerla a salvo antes de acceder a la subrutina. •

En la línea 10, se carga en el acumulador el contenido de la dirección de memoria (M) apuntada por el par HL. A continuación (línea 11) se establece una comparación con el valor 12 (fijarse cómo el ensamblador transforma 12 a su equivalente hexadecimal 0C).

En la línea 12 se efectúa un salto a FIN (línea 15) si se cumple que el Dato del acumulador es igual a 12. En caso contrario ejecutaría la línea 13, en la que se produce un salto a FIN si se cumple que el Dato es menor que 12. Si no se cumple tampoco la condición anterior es claro que el Dato es mayor que 12 por lo que se pasa a ejecutar la línea 14, en la que se pasa el valor del Dato almacenado en la memoria al registro B.

En la línea 16, se produce el retorno de la subrutina al programa principal, desde el que se ha efectuado la llamada.

Las líneas 9 y 15, en las que tenemos las instrucciones PUSH y POP, sirven para almacenar en la pila los valores de los registros en ellas mencionados y recuperarlos al abandonar la subrutina, con los mismos valores que tenían en el momento de efectuar la llamada. Debemos tener en cuenta, que por cada instrucción PUSH insertada, debemos colocar una instrucción POP correspondiente al mismo registro necesariamente.


Suponiendo que la dirección del Dato fuera 8100H, para llamar a la subrutina MAYOR12 desde el programa principal, tendríamos que proceder del modo siguiente: LXI H,8100H CALL MAYOR12 Observando el listado del programa ensamblado, vemos cómo el programa ensamblador calcula y convierte a hexadecimal las instrucciones y las direcciones de salto.

Ejemplo 2.- En este caso se desarrollará la subrutina correspondiente al ejemplo 2 del tema anterior y la denominaremos del mismo modo MENOR12. 2500 A.D. 8085 CROSS ASSEMBLER VERSION 3.41a -------------------------------------------------INPUT FILENAME: OUTPUT FILENAME:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

;-----------------------------------------;NOMBRE : MENOR12 ;FUNCION : Si Dato>12 va a B si no va a C ;PRECONDICION : HL=Dirección Dato, B=0, C=0 ;POSTCONDICION : Resultado en B o en C ;-----------------------------------------0000 0000 0001 0002 0004 0007 000A 000B 000C 000D 000E 0011

MENOR12: F5 7E FE CA DA 46 F1 C9 4E C3

************* FIN

MENOR12.ASM MENOR12.OBJ

0C 0D 00 0D 00 FIN SALT1 0B 00

S Y M B O L I C

000B

R E F E R E N C E

MENOR12

LINES ASSEMBLED :

PUSH PSW MOV A,M CPI 12 JZ SALT1 JC SALT1 MOV B,M POP PSW RET MOV C,M JMP FIN END

0000 19

SALT1

T A B L E ************* 000D

ASSEMBLY ERRORS :

0

El desarrollo de esta subrutina es muy parecido a la anterior, salvo que en este caso, tendremos el resultado en el registro B o en el registro C según sea el valor del Dato contenido en la dirección marcada por el par HL. Para acceder a esta subrutina, suponiendo que tenemos el Dato situado en la dirección 8200H, procederemos del modo siguiente:


LXI B,0000H LXI H,8200H CALL MENOR12 •

Al igual que en el ejercicio anterior, podemos ver en las primeras líneas, los comentarios que nos documentan la subrutina: Nombre, Función, etc.

Las primeras líneas (9,10 y 11) son similares al ejemplo anterior.

En las líneas 12 y 13 se analizan las condiciones Dato<12 y Dato =12. Si es afirmativa alguna de ellas, se produce un salto a la etiqueta SALT1 (línea 17), donde se almacena el dato en el registro C, para luego (línea 18) saltar a la etiqueta FIN. Si no se cumplen ninguna de las condiciones anteriores, entonces será lógicamente Dato>12, por lo que las instrucciones JC y JZ no serán tenidas en cuenta, pasando a ejecutarse la línea 14, en la que se almacena el Dato en el registro B. A continuación se ejecutan secuencialmente las líneas 15 y 16, en las que se da por finalizada la subrutina.


Ejemplo 3.- Se desarrollará la subrutina correspondiente al ejemplo 3 del tema anterior y será denominada del mismo modo ASCII1. 2500 A.D. 8085 CROSS ASSEMBLER VERSION 3.41a -------------------------------------------------INPUT FILENAME : OUTPUT FILENAME :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

;-----------------------------------------;NOMBRE: ASCII1 ;FUNCION: Convertir byte decimal a ASCII ;PRECONDICION: DIREDAT y DIRERES en HL y DE ;POSTCONDICION: Result DIRERES y DIRERES+1 ;-----------------------------------------0000 0000 0001 0002 0004 0005 0006 0007 0008 000A 000B 000C 000D 000F 0011 0012 0013 0014 0014

ASCII1: F5 7E E6 0F 0F 0F 0F C6 12 13 7E E6 C6 12 F1 C9

PUSH PSW MOV A,M ANI F0H RRC RRC RRC RRC ADI 30H STAX D INX D MOV A,M ANI 0FH ADI 30H STAX D POP PSW RET

F0

30

0F 30

END

*************

ASCII1

ASCII1.ASM ASCII1.OBJ

S Y M B O L I C

R E F E R E N C E

T A B L E

************

0000

LINES ASSEMBLED :

26

ASSEMBLY ERRORS :

0

Esta subrutina, que nos convierte un byte decimal almacenado en memoria a su equivalente ASCII, necesita conocer como valores de entrada, la dirección del byte decimal a convertir y la dirección a partir de la cual se almacena el resultado (DIREDAT y DIRERES respectivamente). Estos valores, son pasados a través de los registros pares HL y DE, antes de efectuar la llamada a la subrutina desde el programa principal, del modo siguiente: DIREDAT DIRERES

EQU EQU

8000H 8100H

LXI D,DIRERES LXI H,DIREDAT CALL ASCII1 Aquí se ha empleado la pseudoinstrucción EQU para ilustrar su funcionamiento, aunque podríamos efectuar la llamada de modo similar a lo hecho en ejercicios anteriores.


Como siempre, en las primeras líneas, incluimos la documentación de la subrutina a modo de comentarios.

En la línea 10 cargamos en el acumulador, el Dato de memoria (M) direccionado por el par de registros HL.

En la línea 11 se efectúa una operación AND entre el acumulador y el dato F0. De esta forma ponemos a cero los cuatro bits menos significativos del Dato del acumulador y permanecen inalterados los cuatro bits más significativos. A continuación (líneas 12, 13, 14 y 15) se efectúan cuatro rotaciones del contenido del acumulador, con lo que pasamos los cuatro bits de mayor peso a las posiciones de menor peso del acumulador.

En la línea 16 sumamos el valor hexadecimal 30 (0011 0000) al acumulador. Con esto tendremos el equivalente ASCII de los cuatro bits de mayor peso del Dato, que a continuación (línea 17) almacenamos en la posición de memoria especificada por el par de registros DE.

En la línea 18 se incrementa el par DE para apuntar a la siguiente dirección de memoria, donde almacenaremos el equivalente ASCII de los cuatro bits de menor peso del Dato.

A continuación volvemos a cargar el Dato original en el acumulador, para luego realizar una operación AND con el valor hexadecimal 0F. De esta forma ponemos a cero los cuatro bits de mayor peso del dato y permanecen inalterados los cuatro bits de menor peso. En la línea 21, sumamos el valor hexadecimal 30 al contenido del acumulador, con lo que obtenemos el equivalente ASCII de la parte baja del Dato, que a continuación (línea 22) almacenamos en la memoria apuntada por el par DE.


Ejemplo 4.- Se desarrollarรก la subrutina correspondiente al ejemplo 4 del tema anterior y serรก denominada del mismo modo ASCII2. 2500 A.D. 8085 CROSS ASSEMBLER VERSION 3.41a -------------------------------------------------INPUT FILENAME : OUTPUT FILENAME :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

;-----------------------------------------;NOMBRE : ASCII2 ;FUNCION : Pasa byte hexadecimal a ASCII ;PRECONDICION: DIREDAT y DIRERES en HL y DE ;POSTCONDICION : Result DIRERES y DIRERES+1 ;-----------------------------------------0000 0000 0001 0002 0004 0005 0006 0007 0008 000B 000C 000D 000E 0010 0013 0014 0015

ASCII2: F5 7E E6 0F 0F 0F 0F CD 12 13 7E E6 CD 12 F1 C9

PUSH PSW MOV A,M ANI F0H RRC RRC RRC RRC CALL ASCII STAX D INX D MOV A,M ANI 0FH CALL ASCII STAX D POP PSW RET

F0

16 00

0F 16 00

;-----------------------------------------;NOMBRE : ASCII ;FUNCION : Calcula el equivalente ASCII ;PRECONDICION : Dato en acumulador ;POSTCONDICION : Resultado en acumulador ;-----------------------------------------0016 0016 0017 0018 001A 001D 0020 0021 0023 0024 0025 0026 0028

ASCII: C5 47 FE CA D2 78 C6 C1 C9 78 C6 C3

0A 25 00 25 00 30 FIN SALT1 37 23 00

002B

PUSH B MOV B,A CPI 10 JZ SALT1 JNC SALT1 MOV A,B ADI 30H POP B RET MOV A,B ADI 37H JMP FIN END

************* ASCII

ASCII2.ASM ASCII2.OBJ

S Y M B O L I C

0016

R E F E R E N C E

ASCII2

LINES ASSEMBLED :

0000

47

FIN

T A B L E 0024

************

SALT1

ASSEMBLY ERRORS :

0027

0


La subrutina que ahora se analiza, es similar a la anterior, pero ésta realiza la transformación a valores ASCII de un byte hexadecimal almacenado en una dirección de memoria (DIREDAT). El resultado será cargado también en memoria (DIRERES). Los valores de las direcciones serán pasados a la subrutina, a través de los pares de registros HL y DE del modo siguiente:

DIREDAT DIRERES

EQU EQU

8100H 8500H

LXI D,DIRERES LXI H,DIREDAT CALL ASCII2

También podemos apreciar cómo en esta subrutina se hacen dos llamadas a otra subrutina (ASCII) que proporciona el valor ASCII. La primera vez que se efectúa la llamada, calcula el equivalente ASCII de la parte alta del byte. La segunda llamada sirve para proporcionar el equivalente ASCII de la parte baja del byte (Dato). •

El programa sigue un desarrollo similar al ejercicio anterior, por lo que podemos ver en las líneas 10 a 15, donde se eliminan los cuatro bits menos significativos del Dato, se producen cuatro rotaciones para preparar el dato y a continuación (línea 16) se llama a una subrutina denominada ASCII, que nos calcula el valor equivalente en ASCII de la parte del Dato tratada.

En la línea 17, se almacena el resultado en la memoria direccionada por el par DE, y posteriormente se incrementa dicho par, para apuntar a la siguiente dirección de memoria.

En las líneas 18 y 19, se realiza un proceso similar con la parte BAJA del Dato, para luego llamar a la subrutina ASCII de cálculo.

En la línea 22, se almacena el resultado en memoria para a continuación dar por finalizada la subrutina y salir de ella mediante la instrucción RET.

Vemos además, que dentro de esta subrutina, existe otra subrutina (denominada ASCII) que efectúa el cálculo del valor equivalente en ASCII. Para ello, debe recibir el dato que se desea tratar en el acumulador y una vez realizado el cálculo, proporciona el resultado en el mismo acumulador.

En esta subrutina, se comprueba en primer lugar, si el dato es menor que 10 (línea 36) en cuyo caso se suma el hexadecimal 30 (0011 0000) y se finaliza la subrutina. Si el dato es mayor o igual que 10, se hace un salto a SALT1, donde se suma el hexadecimal 37 (0011 0111) y se da por finalizada la subrutina con un salto incondicional a FIN.


Ejemplo 5.- Se desarrollarรก la subrutina correspondiente al ejemplo 5 del tema anterior y serรก denominada del mismo modo LLENARAM. 2500 A.D. 8085 CROSS ASSEMBLER VERSION 3.41a -------------------------------------------------INPUT FILENAME : OUTPUT FILENAME : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

;-----------------------------------------;NOMBRE : LLENARAM ;FUNCION : Llena zona RAM con un Dato ;PRECONDICION : DATO=C,PRIRAM=HL y NUMDAT=B ;POSTCONDICION : Zona RAM con mismo Dato ;-----------------------------------------0000 0000 0001 0002 0003 0004 0007 0008

LLENARAM: PUSH PSW SALT1 MOV M,C INX H DCR B JNZ SALT1 POP PSW RET

F5 71 23 05 C2 01 00 F1 C9

0009

************* LLENARAM

LLENARAM.ASM LLENARAM.OBJ

END S Y M B O L I C

0000 SALT1 LINES ASSEMBLED :

R E F E R E N C E

T A B L E

************

0001 17

ASSEMBLY ERRORS :

0


La llamada a esta subrutina, se realiza como siempre, mediante una instrucción CALL. Previamente deben haberse cargado los registros HL, B, y C con los datos correspondientes, según se pide en las precondiciones. Por tanto procederemos del modo siguiente: PRIRAM NUMDAT DATO

• • • •

EQU 8200H EQU 60H EQU 3AH LXI H,PRIRAM MVI B,NUMDAT MVI C,DATO CALL LLENARAM

Podemos ver cómo en la línea 10, se almacena en memoria RAM (direccionada por el par de registros HL) el valor que hay en el registro C (C = Dato). A continuación se apunta a la siguiente dirección de memoria, incrementando el par HL. En la línea 12, se decrementa el contador B (B = Número de datos). En la línea 13, se produce un salto a SALT1 si el contador B no es cero. En el instante en que se alcance B=0 se pasará a ejecutar las líneas 14 y 15 secuencialmente, finalizándose la subrutina.

Ejemplo 6.- Se desarrollará la subrutina correspondiente al ejemplo 6 del tema anterior y será denominada del mismo modo SUMA. 2500 A.D. 8085 CROSS ASSEMBLER VERSION 3.41a -------------------------------------------------INPUT FILENAME : OUTPUT FILENAME : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

20 80 30 80 0000 0000 0001 0002 0003 0004 0007 000A 000B 000D 000E 000F 0010 0011 0012 0013 0014 0017 0018 0019 001A 001B 001C

SUMA.ASM SUMA.OBJ

;-----------------------------------------;NOMBRE : SUMA ;FUNCION : Suma dos núm decimales 8 cifras ;PRECONDICION : SUM1 Y SUM2 conocidos ;POSTCONDICION : Resultado en SUM1 ;-----------------------------------------SUM1 EQU 8020H SUM2 EQU 8030H SUMA:

F5 E5 C5 D5 21 11 A7 06 1A 8E 27 77 23 13 05 C2 D1 C1 E1 F1 C9

20 80 30 80 04 SALT1

0D 00

PUSH PSW PUSH H PUSH B PUSH D LXI H,SUM1 LXI D,SUM2 ANA A MVI B,04 LDAX D ADC M DAA MOV M,A INX H INX D DCR B JNZ SALT1 POP D POP B POP H POP PSW RET END

************ S Y M B O L I C R E F E R E N C E T A B L E ************* SALT1 000D SUM1 = 8020 SUM2 = 8030 SUMA 0000 LINES ASSEMBLED : 33 ASSEMBLY ERRORS : 0


Como vemos, esta subrutina suma dos números decimales de 8 cifras cada uno y nos proporciona el resultado de la suma en uno de los sumandos. En este caso, la subrutina necesita los valores de las direcciones de los sumandos, que serán pasados mediante los pares de registros HL y DE. Aquí se han incluido los valores SUM1 y SUM2 mediante la directiva EQU, al objeto de que se aprecie el modo de ser tratada por el ensamblador, sin embargo este procedimiento no es el adecuado para trabajar con las subrutinas. Para llamar a esta subrutina, tendremos que almacenar los dos números a sumar en RAM y luego efectuar la llamada del modo siguiente: CALL SUMA •

• • •

Cuando el resultado de las subrutinas, no es almacenado en ningún registro, puede emplearse el procedimiento mostrado aquí, para proteger los datos de los registros. Vemos cómo en las líneas 11, 12, 13 y 14, se guardan sus contenidos en la pila y en las líneas 27, 28, 29 y 30 se recuperan de nuevo en sentido inverso, antes de abandonar la subrutina. (Si no nos interesa proteger el contenido de algún registro, podemos eliminar sus instrucciones correspondientes). Notar también que en la línea 17, se ejecuta la instrucción ANA A, con el único objeto de poner el flag CARRY a cero. En la línea 18 se inicia un contador en el registro B, que nos permitirá realizar un determinado número de veces el lazo del bucle marcado por SALT1. En las líneas 19 y 20, podemos ver como se realiza la suma entre los bytes señalados por los pares DE y HL. Posteriormente, en la línea 21 se realiza el ajuste decimal, para transformar el resultado de la suma anterior en un valor decimal.

Ejemplo 7.- Se desarrollará la subrutina correspondiente al ejemplo 7 del tema anterior y será denominada del mismo modo TEMPORIZ. 2500 A.D. 8085 CROSS ASSEMBLER VERSION 3.41a -------------------------------------------------INPUT FILENAME : OUTPUT FILENAME :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

;-----------------------------------------;NOMBRE : TEMPORIZ ;FUNCION : Realiza un tiempo de espera ;PRECONDICION : Contador=CONTA ;POSTCONDICION : Ninguna ;-----------------------------------------0000 0000 0001 0002 0003 0004 0005 0006 0007 000A 000B

TEMPORIZ: PUSH PSW SALT1 DCX D MOV A,D ORA E NOP NOP NOP JNZ SALT1 POP PSW RET

F5 1B 7A B3 00 00 00 C2 01 00 F1 C9

000C

END

************* SALT1

TEMPORIZ.ASM TEMPORIZ.OBJ

S Y M B O L I C

0001

R E F E R E N C E

TEMPORIZ

LINES ASSEMBLED :

T A B L E

************

0000 20

ASSEMBLY ERRORS :

0


Para acceder a esta subrutina, tendremos que proporcionar a través del par DE, el valor del contador, que determina el número de veces que se realizará el bucle iniciado con SALT1. Por tanto, podremos llamar a esta subrutina del modo siguiente: CONTA EQU

7000H LXI D,CONTA CAL TEMPORIZ

Puede verse que dentro del bucle hay tres líneas en las que aparece la instrucción NOP. Con ella el microprocesador no ejecuta ninguna operación. Estas instrucciones se emplean para aumentar el tiempo de espera, que viene marcado por el valor que se le proporcione al par de registros DE, al acceder a la subrutina. El tiempo de permanencia en esta subrutina, puede encontrarse fácilmente sin mas que calcular el número total de ciclos que emplean las instrucciones del bucle y multiplicarlos por el número de veces que se realiza el bucle y posteriormente añadir el número de ciclos de las instrucciones que se ejecutan una sola vez (fuera del bucle).


Ejemplo 8.- Se desarrollarรก la subrutina correspondiente al ejemplo 8 del tema anterior y serรก denominada del mismo modo MOVERDATOS. 2500 A.D. 8085 CROSS ASSEMBLER VERSION 3.41a -------------------------------------------------INPUT FILENAME : OUTPUT FILENAME :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

MOVERDAT.ASM MOVERDAT.OBJ

;-------------------------------;NOMBRE : MOVERDATOS ;FUNCION : Mueve Datos en RAM ;PRECONDICION:ORIGEN,DESTINO,NUM ;POSTCONDICION : Datos en DESTINO ;--------------------------------

0000 0000 0001 0002 0003 0004 0007 000A 000C 000D 000F 0012 0013 0014 0017 0018 0019 001C 001D 001E 001F 0020 0021 0022 0024 0027 0028 0029 002C 002F 0030 0031 0032 0033 0035 0036 0037 0038 0039 003A 003C 003D 003E 003F 0040

00 84 00 88 3C 00

ORIGEN EQU DESTINO EQU NUM EQU

F5 E5 D5 C5 21 11 06 78 FE C2 7D BB C2 7C BA CA 7B 95 6F 7A 9C 67 FE C2 7D 90 D2 21 7D 80 6F 7C CE 67 7B 80 5F 7A CE 57 2B 1B 7E 12

MOVERDATOS: PUSH PSW PUSH H PUSH D PUSH B LXI H,ORIGEN LXI D,DESTINO MVI B,NUM MOV A,B CPI 00H JNZ FIN MOV A,L CMP E JNZ CONTI MOV A,H CMP D JZ FIN CONTI MOV A,E SUB L MOV L,A MOV A,D SBB H MOV H,A CPI 00H JNZ NOSOLAP MOV A,L SUB B JNC NOSOLAP SISOLAP LXI H,ORIGEN MOV A,L ADD B MOV L,A MOV A,H ACI 00H MOV H,A MOV A,E ADD B MOV E,A MOV A,D ACI 00H MOV D,A SALT1 DCX H DCX D MOV A,M STAX D

00 84 00 88 3C 00 53 00

1C 00

53 00

00 48 00

48 00 00 84

00

00

8400H 8800H 60


57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 ******** CONTI NOSOLAP SALT2

0041 0042 0045 0048 004B 004C 004D 004E 004F 0050 0053 0054 0055 0056 0057

05 C2 C3 21 7E 12 23 13 05 C2 C1 D1 E1 F1 C9

DCR B JNZ SALT1 JMP FIN NOSOLAP LXI H,ORIGEN SALT2 MOV A,M STAX D INX H INX D DCR B JNZ SALT2 FIN POP B POP D POP H POP PSW RET

3D 00 53 00 00 84

4B 00

0058

END

S Y M B O L I C 001C 0048 004B

DESTINO NUM SISOLAP

LINES ASSEMBLED :

R E F E R E N C E = 8800 = 003C 002C 73

T A B L E

FIN ORIGEN

0053 = 8400

********* MOVERDATOS SALT1

ASSEMBLY ERRORS :

0

0000 003D


Para acceder a la subrutina MOVERDAT, debemos pasar en los registros HL, DE y B los valores correspondientes al ORIGEN de Datos, DESTINO de Datos y NUMero de Datos que se deben mover, respectivamente. Por tanto tendremos que proceder del modo siguiente: ORIGEN DESTINO NUM

EQU EQU EQU

8400H 8800H 60

LXI H,ORIGEN LXI D,DESTINO MVI B,NUM CALL MOVERDAT Para seguir el desarrollo de la subrutina, es conveniente analizar el pseudocódigo presentado en el tema anterior. • • •

Podemos ver como en las líneas 17, 18 y 19 se toman los datos iniciales con los que la subrutina debe ponerse en marcha. Entre las líneas 20 a 28 se establecen las comparaciones y decisiones de saltos pertinentes según proceda finalizar la subrutina o continuar con el proceso. En las líneas siguientes podemos apreciar los dos bucles a ejecutar según se produzca solapamiento o no del bloque de memoria.

6.6 ERRORES CORRIENTES PROGRAMANDO EN ENSAMBLADOR. Queremos en este apartado enumerar algunos de los errores que corrientemente se cometen, cuando se desarrollan programas en ensamblador. Pueden establecerse, además, determinadas categorías de errores y estudiar los errores típicos dentro de cada una de ellas. Nosotros nos limitaremos a enumerar los más elementales y que con más frecuencia se presentan. Sabemos que los ensambladores son programas preparados para reconocer algún tipo de error como: Códigos de operación indefinidos, etiquetas indefinidas, etiquetas o nombres definidos en más de un lugar, caracteres ilegales, operandos ilegales, formatos ilegales, etc. Estos errores son comunes y fáciles de corregir al ser detectados por el ensamblador que nos proporcionará como salida determinados mensajes de error que nos orientarán sobre el error cometido. Sin embargo el problema se complica cuando un error confunde al ensamblador dando como salida unos mensajes de error equivocados, o bien, ni tan siquiera emite mensajes de error. Podemos destacar una serie de errores que se cometen al tratar con elementos de entrada/salida o periféricos de entrada/salida como son: •

Confundir un puerto de entrada con uno de salida. Debemos tener presente que no se pueden sacar datos por un puerto de entrada, ni se pueden introducir datos por un puerto de salida.

Leer o escribir datos sin comprobar previamente el estado del componente empleado, ya que algunos de ellos permiten el intercambio de información, sólo cuando una línea de estado así lo indica.

Recordar que para trabajar con elementos periféricos, es preciso en primer lugar programarlos. De no ser así podríamos encontrarnos con un intercambio de información no autorizado.


Olvidarnos que algunos componentes trabajan con lógica negativa.

Confundir los registros que representan los puertos con los registros de programación o control de los componentes periféricos.

Otro tipo de errores corrientes que se producen cuando se desarrolla el programa fuente son: •

Errores producidos al invertir el orden de los operandos o registros en las instrucciones. Estos errores se producen normalmente en el empleo de instrucciones del tipo MOV reg,reg, que carga el contenido del segundo registro en el primero, sin alterar el contenido del segundo. Pero no al contrario como se interpreta en ocasiones, ni tampoco se intercambian los valores de los registros entre sí. También se cometen errores cuando se trabaja con direcciones de 16 bits y datos de 16 bits, si no se tiene presente que se almacenan en memoria situando en primer lugar el byte menos significativo y a continuación el byte más significativo.

Errores producidos debido a un tratamiento incorrecto de los flags. Sabemos que las instrucciones del 8085 afectan de diferentes formas al registro de flags. Hay determinadas instrucciones que requieren una atención especial , como pueden ser las instrucciones de transferencia de datos MOV, MVI, LDA, STA, etc. que “no afectan” al registro de flags del microprocesador por lo que no deben ser empleadas con este objetivo. Debe tenerse en cuenta que el flag CARRY actúa en las operaciones aritméticas cuando se produce un desbordamiento y es aconsejable tener precaución en su uso. Después de una operación de comparación (CMP, CPI) el flag ZERO nos marca la igualdad de los operandos, si ZERO=1 los operandos son iguales, si ZERO=0 son distintos. Un error común se produce al ejecutar las instrucciones JZ (salta si ZERO=1) y JNZ (salta si ZERO=0) si no se tienen en cuenta las condiciones previas con exactitud. También podemos cometer errores cuando se intenta salir de un bucle apoyándonos en instrucciones de salto con condición, cuando dicha condición se fundamenta en el estado de un flag y no se ha estudiado convenientemente. Hay que tener en cuenta que hay instrucciones que afectan a determinados flags, pero no a todos, como por


ejemplo las instrucciones INR o DCR que no actúan sobre el CARRY pero sí afectan al flag ZERO. Por otro lado instrucciones como las de desplazamiento afectan únicamente al CARRY. •

Errores producidos al confundir registros simples con registros dobles (o pares de registros). Debemos recordar que las instrucciones que trabajan con pares de registros son LHLD, LXI, POP, PUSH, XCHG y XTHL. Además cuando nos referimos al contenido de una dirección de memoria apuntada por el par HL, debemos emplear la referencia simbólica M. Un error típico es escribir MOV A,H en lugar de emplear MOV A,M.

Errores producidos al confundir datos con direcciones. Hay instrucciones del microprocesador 8085, que requieren solamente direcciones y otras solamente datos.

Errores producidos al emplear formatos equivocados. El ensamblador interpreta los siguientes formatos: H como hexadecimal, B como binario, D como decimal (si no se indica nada, también interpreta decimal por defecto), Q como octal y caracteres entre comillas como representaciones ASCII. La letra que identifica el formato debe colocarse después del valor en cuestión. Suelen ser errores comunes omitir la H cuando trabajamos con valores hexadecimales, omitir la B cuando se trabaja con valores binarios, confundir representaciones en BCD con representaciones binarias, confundir representaciones binarias o decimales con representaciones ASCII, etc.

Errores producidos en la ejecución de iteraciones, realizando un recorrido de más o de menos. Hay que considerar que un proceso iterativo realizado a partir de un número Base, tal que sea Base+N el número de repeticiones, contiene N+1 recorridos y no N recorridos.

Otros errores corrientes pueden ser: a. b. c. d. e. f.

Olvidarse de la estructura LIFO de la pila del sistema. Marcar incorrectamente los arrays o bloques de datos (principio y fin). No tener en cuenta de forma apropiada las precondiciones y las postcondiciones de las subrutinas. Realizar una organización incorrecta del programa, con repetición de rutinas de inicialización, dañando la eficiencia del mismo. Salir de una subrutina sin haber alcanzado el resultado deseado. Llevar a cabo un tratamiento incorrecto de las interrupciones, no habilitándolas adecuadamente mediante la instrucción EI, no guardando los registros necesarios en la pila cuando se accede a la subrutina de tratamiento de la interrupción, no deshabilitándolas en transferencias multi-byte o durante la ejecución de secuencias de instrucciones que no deben ser interrumpidas, etc.

6.7 EJERCICIOS. 1. 2. 3. 4. 5. 6. 7. 8. 9.

El lenguaje ensamblador. Características. Definir el programa fuente y el programa objeto. Escribir los pasos que deben seguirse normalmente, para desarrollar un programa. Explicar los campos de que debe constar cada línea del programa fuente. ¿Qué son las directivas de ensamblador?. Explicar cómo funcionan las directivas ORG, END y EQU. Definir una subrutina. Escribir las instrucciones del microprocesador 8085 que sirven para efectuar llamadas a subrutinas. Formas de comunicarse o de pasar datos a otras subrutinas. Refiriéndonos a la subrutina ASCII1.ASM, si en el byte señalado por DIREDAT, tenemos el valor 67 ¿qué valores nos mostrarán DIRERES y DIRERES+1 al finalizar la subrutina?. Trabajando con la subrutina LLENARAM.ASM, preparar los datos de entrada necesarios antes de la llamada para almacenar el Dato 7FH desde la dirección 810FH hasta la dirección 813FH.


En nuestro próximo número: Transferencia de datos al exterior. Descripción física del PPI 8255. Descripción funcional del PPI 8255. Modos de funcionamiento. Interconexión del PPI 8255 con el microprocesador 8085. Aplicaciones prácticas con el PPI 8255 Ejercicios

Microprocesador 8085 (6) INTEL  

Construcción de subrutinas, el ensamblador, programas básicos y ejemplos

Read more
Read more
Similar to
Popular now
Just for you