INSTRUCCIONES ARITMÉTICAS Y LÓGICAS | ||||||||||||||
El microprocesador Z-80 dispone de una unidad aritmética-lógica que le permite realizar una serie de operaciones, tanto aritméticas, como lógicas. Las aritméticas incluyen la suma y resta con o sin acarreo, incremento y decremento de un registro, comparaciones, ajuste decimal, complemento y negación. Las lógicas incluyen las operaciones que se realizan con los operadores "AND", "OR" y "XOR". Antes de adentrarnos en el estudio de las instrucciones concretas, daremos una serie de definiciones útiles: SUMA SIN ACARREO:Consiste en sumar al contenido del registro "A" un número y obtener el resultado en el registro "A". El indicador de acarreo no se tiene en cuenta para esta operación. Su esquema sería:
Exactamente igual que la anterior, pero se suma también el indicador de acarreo del registro "F". De esta forma, sepuede incluir en la suma el acarreo procedente de una suma anterior. Su esquema sería:
Consiste en restar un número del contenido del registro "A", y obtener el resultado en este mismo registro. El indicador de acarreo no interviene en la operación. Se consideran números negativos los superiores a 127 (7Fh) de la forma que se explicó en el capítulo relativo a los sistemas de numeración; es decir, el número 255 (FFh) se considera "-1", el 254 (FEh) se considera "-2" y así sucesivamente, hasta 128 (80h) que se considera "-128". El paso de 127 a 128 o viceversa se indica poniendo a "1" el flag de "overflow" (P/V) del registro "F". Su esquema sería:
Igual que el anterior, salvo que también se resta el indicador de acarreo (CF) del registro "F". Su esquema sería:
Consiste en sumar uno al contenido de un registro que se especifica en la instrucción. Su esquema es:
Donde "R" representa un registro cualquiera de 8 a 16 bits. Si se trata de un registro doble (de 16 bits) se incrementa el registro de orden bajo (por ejemplo, en el "BC" se incrementa "C"), y si ello hace que este pase a valer "0", se incrementa también el de orden alto. DECREMENTO:Es la inversa de la anterior, consiste en restar uno al contenido de un registro. Su esquema es:
Si se trata de un registro doble, se decrementa el de orden bajo y, si esto hace que pase a valer 255 (FFh), se decrementa también el de orden alto. Si el registro incrementado o decrementado es de 8 bits, resultan afectados los indicadores del registro "F". COMPARACIONES:Estas instrucciones permiten comparar el contenido del acumulador con un número. Para ello, se resta el número del contenido del acumulador, pero el resultado no se almacena en ninguna parte, simplemente, se alteran determinados flags del registro "F", lo que nos indica si el número era menor, igual o mayor que el contenido del acumulador. Si era igual, se pone a "1" el flag "Z" (indicador de "cero"). Si el número era mayor, se pone a "1" el flag "S" (indicador de "signo"). AJUSTE DECIMAL:Esta instrucción realiza un ajuste del contenido del acumulador para que, en vez de estar comprendido entre "00h" y "FFh", lo esté entre "00h" y "99h". Si se produce acarreo, se indica mediante el flag correspondiente. Para realizar esta operación se toma en cuenta el estado de los indicadores de "acarreo" (C) y "semi-acarreo" (H). Su finalidad es la de permitir realizar operaciones en "BCD" (Decimal Codificado en Binario). COMPLEMENTO:Consiste en realizar un "complemento a 1" del acumulador, es decir, cambiar los "unos" por "ceros" y los "ceros" por "unos". NEGACIÓN:Consiste en realizar un "complemento a 2" del acumulador, es decir, realizar un "complemento a 1" y, luego, sumarle "1". Lo que se obtiene es el "negativo" del número que teníamos en el acumulador. El efecto es el mismo que si restáramos el acumulador de "cero", es decir:
Existen dos instrucciones que afectan al indicador de acarreo del registro "F", es posible ponerlo a "1" o "complementarlo" (ponerlo a "1" si era "0" y viceversa). No se ha previsto una instrucción para poner a "0" el flag de acarreo, dado que esto se puede conseguir haciendo un "AND" o un "OR" del acumulador consigo mismo. Veamos ya las instrucciones: |
La actividad de los indicadores, de condición, tanto en la suma (ADD) como en la suma con acarreo (ADC), se hace según las siguientes condiciones:
|
SUB (SUBstract), "restar" en inglés. Básicamente esta función consiste en restar del registro acumulador el valor indicado por el operando. Esto es una resta binaria en la que el registro acumulador es el minuendo y el operando indica el sustraendo. La operación real que efectúa el microprocesador es: complementar a dos el sustraendo y sumarlo con el minuendo. Conocer esta operativa es interesante para entender cómo funciona el acarreo, pero no es necesario tenerla presente en el momento de construir el programa. En una resta algebraica el sustraendo es un número negativo y como se sabe para el microprocesador Z80, los números negativos se expresan con el complemento a 2. Por lo tanto la resta para el ordenador es la suma de un número positivo (minuendo) con un número negativo (sustraendo); y dependiendo de los valores absolutos, el resultado será un número negativo o positivo.
Con las instrucciones de restar vistas hasta el momento, la operación está limitada por el valor que puede contener un octeto, es decir, 255 considerando todos los valores como positivos. Para restar números más grandes, están las instrucciones cuyo nemotécnico es SBC. SBC (SuBstract with Carry), restar con acarreo. Se trata de una resta binaria de un octeto , con las mismas características que en las instrucciones SUB, sólo que al minuendo se le resta primero el bit de acarreo del indicador de condición (C). Este bit se activa en una operación de resta cuando no hay acarreo desde el bit 7 y esto ocurre cuando el sustraendo es mayor que el minuendo (ver ejemplos de las instrucciones SUB). En una resta convencional, operando con números decimales, cuando el valor del sustraendo es mayor que el minuendo en la unidad enfrentada, sumamos diez al valor del minuendo, restamos y "nos llevamos una" para la siguiente unidad, esto es, restamos el diez que habíamos sumado al minuendo:
Al restar 4-5 en realidad se hace 14-5 y nos llevamos una (acarreo), este acarreo lo sumamos a la decena 1 del sustraendo, que es lo mismo que restarlo en el minuendo a la decena 2 y resulta 2-2 ó 1-1.
Pues esto mismo ocurre al sumar octetos, cuando el octeto sustraendo es mayor que el octeto minuendo y activarse por tanto el bit de acarreo, al tenerlo en cuenta con los octetos de orden superior; es como si en el minuendo se sumara 256 al octeto inferior y se restara uno al octeto superior. Ver figura 6-2. Este es el uso más importante de la condición de acarreo para la resta.
|
La activación de los indicadores de condición, tanto en la resta (SUB) como en la resta con acarreo (SBC), se hace según las siguientes reglas:
|
Hasta aquí hemos visto las instrucciones que nos han de servir para sumar y restar en código máquina. A continuación veremos las que se encargan de realizar operaciones lógicas tales como AND, OR y XOR. Pero antes, realizaremos unos cuantos ejemplos que podamos ejecutar en el ordenador, y que sirvan para aclarar lo estudiado. También invitamos al lector a que intente resolver los ejercicios que se proponen, y que le darán una medida de cómo va asimilando los conocimientos. EJEMPLOS:Al igual que en el capítulo anterior, vamos a hacer algunos programas en código máquina que nos demuestren el funcionamiento de las instrucciones de suma y resta. Al mismo tiempo, iremos cogiendo práctica en la realización y ensamblado de programas en Assembler. Recomendamos al lector que no se limite a «leer por encima» este curso. Si desea, de verdad, aprender a programar en código máquina, debe seguir el curso encima de una mesa con lápiz y papel en la mano. Intente ensamblar cada programa usted mismo y no se limite a ver cómo lo hacemos nosotros; e incluso, atrévase a escribir sis propias rutinas. No se preocupe si el ordenador se le «cuelga» cincuenta veces, es totalmente normal, una rutina en código máquina rara vez funciona a la primera. Vamos con el primero de nuestros programas. Se trata de sumar dos números sin acarreo. Utilizaremos un programa en Basic que se encargará de gestionar la entrada de datos, llamar a la rutina en C/M e imprimir los resultados, pero la suma la realizaremos en código máquina. En principio, necesitamos POKEar los dos números que vamos a sumar en dos direcciones de memoria, desde donde serán leidos por la rutina C/M. Estas dos direcciones serán la 5CB0h (23728) para el primer operando, y la 5CB1h (23729) para el segundo; estas direcciones corresponden a una variable del sistema que no se usa. Primero escribiremos el programa en C/M y luego el Basic. En Assembler, nuestra rutina podría ser algo así:
Las líneas 10, 20 y 30 leen los dos operandos desde las posiciones de memoria donde los almacenó el Basic. La línea 40 realizará la suma equivalente a:
Las líneas 50 y 60 transfieren el resultado al registro "B" y los indicadores de estado del registro "F", al registro "C". Recuerde que el registro "BC" es lo que nos devuelve USR cuando retornamos a Basic. Mirando las tablas de codificación, podemos ensamblar el programa:
Habría sido interesante que el propio lector hubiera ensamblado el programa antes de mirar la tabla anterior. Prométase a sí mismo que la próxima vez lo intentará. Ya tenemos preparada la rutina en código máquina para sumar dos números. Seamos buenos con los que aún tienen solo 16K, y carguemos la rutina a partir de la dirección 31000. Ha llegado el momento de pasar al olvidado Basic. El PROGRAMA 1 se encarga de todo. La línea 10 baja RAMTOP, las líneas 20 y 30 introducen en memoria nuestra rutina que se encuentra en los DATA de la línea 40. Las líneas 50 a 100 nos piden los dos operandos y los POKEan en memoria tras comprobar si están dentro de rango.
La línea 110 llama a nuestra rutina en C/M de forma que, al retornar, el contenido del registro "BC" se almacena en la variable "a". En 120 llamamos a la rutina 3100 que nos pasa el número a binario, esta subrutina es la mism que usábamos en el programa para cambiar de base, del capítulo 3. Las líneas 130 y 140 completan el número con ceros a la izquierda para obtener, de nuevo, 16 bits. Finalmente, las líneas 200 a 220 imprimen en pantalla el valor que contenía el acumulador después de efectuar la suma y el estado de los indicadores en el registro "F". El significado de los indicadores es el siguiente:
Los indicadores marcados "x" presentan un estado indeterminado y no habrá que tomarlos en cuenta. Una vez que tenga el programa en memoria, pruebe a introducir distintos operandos comprendidos entre 0 y 255. Le sugerimos unos cuantos:
Puede utilizar este mismo programa para la resta cambiando "ADD A,B" por "SUB A,B", es decir, el "128" de la línea 40 por un "144". Haga el cambio y ejecute el programa de nuevo, esta vez restará el segundo operando del primero. Si el segundo operando es mayor que el primero (resultado negativo) el indicador "C" se pondrá a "1" y el resultado aparecerá en complemento a 2. Ahora vamos a complicar un poco más la cosa, se trata de hacer una rutina qeu permita sumar números superiores a 255. En este caso, usaremos la instrucción "ADC" (sumar con acarreo) para poder tener en cuenta, cuando sumemos un octeto, el acarreo procedente del anterior. Introduciremos el primer operando en las posiciones 5CB0h (23728) y 5CB1h (23729) (primero el octeto menos significativo y luego el más significativo), y el segundo operando en 5C76h (23670) y 5C77h (23677). El programa en Assembler puede ser algo como:
La línea 10 pone a «cero» el indicador de acarreo; se trata de un pequeño "truco" que consiste en realizar un "AND" lógico del acumulador consigo mismo, con lo que el contenido no varía, pero se pone a cero el indicador de acarreo. Más adelante, y dentro de este mismo capítulo, veremos las operaciones lógicas. Las líneas 20, 30 y 40 cargan los octetos de orden bajo de los dos operandos. La línea 50 los opera (suma) y, si hay acarreo, lo guarda para la suma siguiente. La línea 60 guarda el resultado en "C" (octeto bajo de "BC"). La operación se vuelve a repetir para los octetos altos; las líneas 70, 80 y 90 cargan los operandos. La línea 100 los suma tomando en cuenta el acarreo procedente de la operación anterior. Finalmente, la línea 110 transfiere el resultado al registro "B" (octeto alto de "BC"). La operación ya se ha realizado, tenemos el resultado en "BC" y, por tanto, será lo que obtengamos al retornar a Basic. Ahora sólo nos falta sacar, de alguna forma, el contenido del registro "F" (indicadores) de forma que lo podamos leer desde Basic. Para ello, las líneas 120 y 130 pasan los contenidos de "A" y "F" a "DE" y la línea 140 almacena el contenido de "E" en la posición de memoria 5CB0h (23728), desde donde será leido por el Basic. En esta operación, también se guarda en 23729 el contenido del registro "D" pero, en este caso, no nos interesa. Sería interesante que el lector intentara, ahora ensamblar por su cuenta este programa, para ello, deberá proceder como hicimos nosotros en el caso anterior. Primero, copie el programa en un papel, ahora, vaya buscando cada instrucción en las tablas ("AND A" se ensambla como A7h ó 167d). A continuación, escriba los operandos numéricos sin olvidar invertir el orden de los octetos y finalmente, acuérdese de ensamblar "RET" como C9h o 201d. ¿Ya lo tiene? Correcto, ahora compruebe si lo que ustes ha ensamblado coincide con lo nuestro.
No se preocupe si se ha equivocado en algo, sería mucho pedir que el primer programa que ensambla le saliera sin errores. Ahora, con los datos de la tercera columna (donde dice: "Decimal") podemos construir el programa en Basic que introduzca esta rutina en memoria, y la utilice para sumar dos números. Este programa es el listado que aparece con el nombre de PROGRAMA 2. No hace falta que lo copie entero, si lo desea, puede cargar el PROGRAMA 1 y reescribir las líneas 20, 40, 60, 70, 90, 100, 110, 120, 130, 210 y 220 ya que son las únicas que varían.
El funcionamiento es muy similar al del PROGRAMA 1, pero observe que en las líneas 70 y 100 partimos los operandos en dos octetos antes de meterlos en sus direcciones de memoria correspondiente. Pruebe con distintos operandos y verá que el programa suma correctamente; siempre que el resultado sea mayor de 65535, obtendrá ese resultado menos 65536 y el indicador de acarreo se pondrá a "1". Puede también restar números si cambia "ADC A,D" por "SBC A,D"; para ello, puede cambiar los dos "138" de la línea 40 (del Basic) por "154", o bien, detener el programa (con STOP) y teclear: POKE 31008,154: POKE 31017,154. Luego, no lo arranque con "RUN", sino, con "GO TO 50". Como verá, el hecho de POKEar en la zona de programa hace que éste se comporte de forma distinta; he aquí la razón de los famosos "POKES" de "vidas infinitas" y similares que se utilizan en juegos comerciales (sección "Patas Arriba" de la revista MICROMANIA). |