EL MICROPROCESADOR Z-80

En el primer capítulo de este curso, ideamos un microprocesador imaginario con el fin de introducir fácilmente al lector en el código máquina. Ahora, vamos a explicar a fondo lo que es un microprocesador y más concretamente, el microprocesador Z-80 de ZILOG, que es el que equipa el Spectrum, y el que utilizaremos en este curso.

Qué es un microprocesador

Un ordenador es una máquina fundamentalmente secuencial. Esto quiere decir que realiza sus tareas una detrás de otra, siguiendo el orden en el que están las instrucciones para realizarlas.

Sus componentes básicos serán, por tanto, un lugar donde almacenar las instrucciones y datos (Memoria) y un elemento encargado de ejecutar los procesos indicados por esas instrucciones (Unidad Central de Proceso o CPU).

La CPU debe incluir todos los componentes necesarios para leer la memoria, decodificar las instrucciones y ejecutar cálculos aritméticos y lógicos. En los ordenadores de pequeño tamaño (minis y micros), la CPU está integrada dentro de un solo chip de silicio, a este chip se le conoce por el nombre de microprocesador.

Un microprocesador consta, normalmente, de una serie de registros, una Unidad Aritmética-Lógica (ALU) y los circuitos de control para la comunicación interna y externa. En la FIGURA 1 se puede ver el diagrama de bloques del microprocesador Z-80.

[Figura 1]
Figura 1. Diagrama de bloques del Z-80.
Registros

Los registros constituyen una especie de pequeña memoria interna al microprocesador. El Z-80 tiene registros de 8 y 16 bits, si bien los de 8 bits se pueden agrupar de 2 en 2 para formar uno de 16 bits. Todas las operaciones que realiza el Z-80 se hacen entre números contenidos en los registros, o bien, entre un registro y una posición de memoria; por eso se dice que el Z-80 es un microprocesador orientado hacia los registros. La posibilidad de agrupar dos registros de 8 bits para formar uno de 16, permite al Z-80 realizar operaciones de 16 bits a pesar de ser un microprocesador de 8 bits.

El Z-80 tiene, en total, 18 registros de 8 bits y 4 registros de 16 bits. Algunos son de uso general y otros tienen asignadas funciones específicas.

Como se ve, los registros cumplen en Código Máquina una función similar a la de las variables en Basic. La configuración de registros del Z-80 se muestra en la FIGURA 2.

[Figura 2]
Figura 2. Registros del Z-80.
Registros especiales de 16 bits

Los cuatro registros especiales de 16 bits son: el Contador de programa (PC), el Puntero de pila (SP), el registro índice "X" (IX) y el registro índice "Y" (IY). A continuación vamos a verlos de uno en uno.

CONTADOR DE PROGRAMA (Program Counter "PC"):

Es el registro que contiene la dirección de memoria desde donde hay que leer la instrucción en curso, tras la ejecución el registro se incrementa para continuar con la siguiente, o se sustituye su valor por otro si se ha de ejecutar un salto o una llamada a subrutina. En el momento de conectar el ordenador, la señal de RESET pone este registro a "cero", por lo que la ejecución comienza desde la primera dirección de memoria.

PUNTERO DE PILA (Stack Pointer "SP"):

Una pila es una zona reservada de memoria cuyos datos están organizados como "último en entrar, primero en salir" (LIFO: Last In First Out), y sirve para almacenar determinados datos, como por ejemplo, la dirección de retorno tras una llamada a subrutina. De una pila sólo se puede recuperar cada vez el último dato que se ha introducido. El registro SP es el puntero de la Pila de Máquina. Apunta siempre al último dato introducido, los datos que se introducen en la pila de máquina tienen siempre dos bytes de longitud. Durante la rutina de inicialización, se carga este registro con un valor (inmediatamente debajo de RAMTOP) y cada vez que se mete un dato en la pila, el puntero (SP) se decrementa dos veces (la pila se expande hacia abajo).

La existencia de una pila permite la ejecución de llamadas a subrutinas, cada vez que se llama a una subrutina, se introduce en la pila el contenido actual del PC, se decrementa dos veces el SP y se carga el PC con la nueva dirección de la subrutina. Para retornar, se carga el PC con el contenido superior de la pila y se incrementa dos veces el SP. Este sistema permite la anidación de subrutinas hasta el límite de la memoria disponible para la pila.

Cuando se escribe un programa, hay que tener sumo cuidado para que la pila no crezca indefinidamente, ya que destrozaría todos los datos almacenados en memoria, incluido el propio programa. Por otro lado, hay que tener cuidado para recuperar de la pila todos los datos almacenados durante una subrutina antes de intentar retornar, ya que de lo contrario habríamos "corrompido" la pila y el retorno no sería posible.

Nuestros programas en C/M se llaman desde Basic como una subrutina (con la función USR) de forma que para retornar a Basic mediante una instrucción RET, deberemos tener la pila en perfectas condiciones. No obstante, el Sistema Operativo del Spectrum permite un retorno a Basic, incluso con la pila "corrompida", mediante el uso de la instrucción RST 8 que se explicará más adelante.

REGISTROS INDICE (Index X e Index Y "IX" e "IY"):

Estos registros sirven para manipular tablas, contienen las direcciones de base a las que se sumará un entero en complemento a dos cuando se utilice direccionamiento indexado (esta forma de direccionamiento se verá más adelante).

El Sistema Operativo del Spectrum utiliza el registro IY como dirección de base para acceder a las variables del sistema, por lo que deberemos tener sumo cuidado si utilizamos este registro en nuestros programas.

Registros especiales de 8 bits VECTOR DE INTERRUPCION (Interrupt "I"):

El Z-80 utiliza el dato contenido en este registro como octeto de orden alto de la dirección a la que deberá saltar cuando reciba una petición de interrupción enmascarable en "modo 2" (las interrupciones del Z-80 se estudiarán más adelante en este mismo capítulo).

REGISTRO DE REGENERACION (Refresh "R"):

Como casi todos los lectores sabrán, el Spectrum utiliza memoria RAM dinámica, este tipo de memoria tiene que ser leida y vuelta a escribir continuamente. El Z-80 utiliza este registro de 7 bits como dirección para regenerar la memoria durante el tiempo de decodificación de cada instrucción.

Registros alternativos

El Z-80 tiene dos grupos de 8 registros de 8 bits cada uno, que pueden ser usados de forma alternativa mediante una instrucción de intercambio de contenidos. Cada uno de estos grupos lleva un acumulador, un indicador de estado y 6 registros de uso general.

ACUMULADOR (Acumulator "A"):

El Acumulador recibe los resultados de todas las operaciones aritméticas y lógicas que realiza en microprocesador que es, de hecho, el registro más usado del Z-80. Existen dos acumuladores, uno en cada grupo de registros alternativos (ver FIGURA 2) que se denominan respectivamente A y A'.

REGISTRO DE ESTADO (Flags "F"):

El registro de estado indica la ocurrencia de determinadas condiciones, tales como: paridad, cero, signo, acarreo, desbordamiento, que se producen tras una operación aritmética o lógica y que serán de gran utilidad en los saltos condicionales.

En la FIGURA 3 se puede ver la disposición de los distintos indicadores dentro del registro de estado.

[Figura 3]
Figura 3. Indicadores de estado en el registro "F".

Existen dos registros de estado, uno en cada grupo de registros alternativos, se denominan respectivamente F y F'.

REGISTROS DE USO GENERAL ("B", "C", "D", "E", "H", "L"):

Cada grupo de registros alternativos tiene 6 registros de uso general que se denominan respectivamente B, C, D, E, H, L y B', C', D', E', H', y L'. Pueden agruparse de dos en dos para formar los registros BC, DE, HL y BC', DE' y HL'. Una instrucción de intercambio de contenidos, permite seleccionar entre parejas de registros de uno u otro grupo.

Su aplicación es de uso general, si bien, algunos tienen funciones específicas asignadas en determinadas instrucciones, por ejemplo "HL" actúa como acumulador en las operaciones aritméticas de 16 bits, "B" actúa como contador en los bucles de iteración (instrucción DJNZ) y finalmente, en las transferencias de bloques, "HL" indica el origen, "DE" el destino y "BC" el número de bytes a transferir.

En el Sistema Operativo del Spectrum, el registro "BC" actúa como un puente de comunicación con el Basic, ya que cada vez que ejecutamos la función USR, lo que obtenemos como resultado es, precisamente, el contenido del registro "BC" en el momento de retornar, lo que nos permitirá pasar datos con facilidad desde Código Máquina a Basic.

Unidad Aritmética-Lógica

Otro componente fundamental del microprocesador es la ALU o Unidad Aritmética-Lógica que es la encargada de realizar todas las operaciones en el interior del microprocesador. Las operaciones que puede realizar son:

Desplazamiento
Comparación
Puesta a uno de bit
Puesta a cero de bit
Prueba de bit
AND
OR
OR exclusivo (EXOR)
Incremento
Decremento
Suma
Resta
Ajuste decimal

El desplazamiento consiste en una rotación, bit a bit, de un registro o una posición de memoria, puede incluir el indicador de acarreo del registro F. El efecto de rotar a la izquierda es el de multiplicar el número por 2, y el de rotarlo a la derecha es el de dividirlo por 2.

La comparación consiste en cotejar el acumulador con otro número y alterar los indicadores del registro F de acuerdo con el resultado de la comparación, permaneciendo inalterado el contenido del acumulador.

Probar un bit consiste en ver si es "uno" o "cero" y anotar el resultado en el indicador de cero del registro F.

Incrementar es sumar "1", decrementar es restar "1".

La suma y la resta pueden ser con o sin acarreo.

El ajuste decimal consiste en transformar el número Hexa contenido en el acumulador y comprendido entre "00" y "FF", en un número decimal codificado en binario (BCD) comprendido entre "00" y "99".

Registro de instrucciones

El registro de instrucciones no es accesible por el programador, se carga durante la lectura de una instrucción, con el contenido de la posición de memoria direccionada por el "PC", y retiene la instrucción hasta que es decodificada por el microprocesador.

Buses

Para comunicarse con la memoria y los periféricos, el Z-80 utiliza una serie de líneas eléctricas denominadas BUSES. Cada una de estas líneas se corresponde con una patilla del chip Z-80. Existen tres buses:

Bus de direcciones de 16 bits formado por 16 líneas eléctricas denominadas A0-A15.

Bus de datos de 8 bits, formado por 8 líneas eléctricas denominadas D0-D7.

Bus de control de 13 bits formado por 13 líneas eléctricas denominadas: M1,MREQ,IORQ,RD,WR,RFSH,HALT,WAIT,INT,NMI,RESET,BUSRQ y BUSAK.

Las tres patillas restantes hasta las 40 del chip son: la entrada de la señal de "reloj" (3,500,000 impulsos por segundo), la entrada de alimentación eléctrica (+5 voltios) y la conexión común a MASA.

Se dice que una entrada o salida está a nivel alto ("1") cuando su tensión con respecto a MASA es de +5V. Y se dice que está a nivel bajo ("0") cuando su tensión con respecto a MASA es de 0V.

Cuando el nombre de una línea tiene una raya encima, indica que es activa a nivel bajo, si no, se considera activa a nivel alto. Todas las salidas del Z-80 son Triestado, esto quiere decir que cuando no se están utilizando permanecen en un estado de alta impedancia que tiene el mismo efecto que si estuvieran desconectadas del circuito. A continuación veremos una a una todas las señales eléctricas del Z-80, representadas en la FIGURA 4.

[Figura 4]
Figura 4. Configuración de patillas del Z-80.
A0-A15

Constituyen un bus de direcciones que permite acceder a 65536 posiciones de memorias, o a 256 ports de entrada/salida. En las operaciones de entrada/salida, los port se direccionan con los ocho bits inferiores del bus. Durante el tiempo de regeneración de memoria, los siete bits inferiores contienen una dirección de regeneración.

D0-D7

Constituyen un bus de datos bidireccional que permite al microprocesador tanto enviar datos como recibirlos. Se utiliza para el intercambio de datos con la memoria o con dispositivos de entrada/salida (ports).

Salida M1 (Machine 1)

Se utiliza para indicar que el ciclo de máquina en curso es el de búsqueda de instrucción. También se activa junto con IORQ para indicar un acuse de recibo a una petición de interrupción.

Salida MREQ (Memory Request)

Se utiliza para indicar que el microprocesador desea acceder a la memoria.

Salida IORQ (Input/Output Request)

Se utiliza para indicar que el microprocesador desea acceder a un port de entrada/salida. También se utiliza junto con M1 para indicar un acuse de recibo a una petición de interrupción.

Salida RD (Read)

Se utiliza para indicar que se desea leer una posición de memoria o un port de entrada/salida.

Salida WR (Write)

Se utiliza para indicar que se desea escribir en una posición de memoria o en un port de entrada/salida.

Salida RFSH (Refresh)

Se utiliza para indicar que se está en un ciclo de regeneración de memoria, y la dirección presente en los siete bits inferiores del bus de direcciones junto con la señal MREQ se deben usar para una lectura de refresco de memoria.

Salida HALT

Se utiliza para indicar que el microprocesador ha ejecutado una instrucción "HALT" y está esperando una petición de interrupción para atenderla. Durante este tiempo, se ejecuta continuamente la instrucción NOP con el fin de mantener la lógica de regeneración de memoria.

Entrada WAIT

Le indica al microprocesador que tiene que esperar, ya que la memoria o el dispositivo de entrada/salida direccionado, no está listo para recibir la transferencia de datos solicitada.

Esta señal y la anterior, tienen la finalidad de sincronizar el funcionamiento del microprocesador con el de otros dispositivos.

Entrada INT (Interrupt)

Petición de interrupción enmascarable, la interrupción sólo es atendida si se encuentra activado el flip/flop de aceptación de interrupción. Si la interrupción es aceptada, se envía el acuse de recibo a través de IORQ y M1 y se salta a la rutina de servicio correspondiente al modo de interrupción seleccionado.

En el Spectrum, la ULA se encarga de efectuar una petición de interrupción enmascarable cada 20 milisegundos, justo antes de empezar a barrer la pantalla del televisor. Esta interrupción se utiliza normalmente para leer el teclado, pero es posible utilizarla en nuestras propias rutinas para sincronizar el funcionamiento de nuestros programas con el barrido de la pantalla, lo que puede ser útil en caso de animación de figuras.

Entrada NMI (Non Maskable Interrupt)

Petición de interrupción no enmascarable, esta interrupción se acepta siempre (salvo que haya presente una señal en BUSRQ y obliga al microprocesador a saltar a la dirección 0066h independientemente del flip/flop de aceptación y del modo de interrupción seleccionado.

Entrada RESET

Esta entrada obliga al microprocesador a inicializarse, cargando todos los registros con "cero", incluido el "PC", por lo que la ejecución comienza desde la posición de memoria "0000".

En el Spectrum, esta señal se produce cada vez que se conecta el ordenador, o cada vez que se pulsa el botón de RESET en el Plus.

Entrada BUSRQ (Bus Request)

Constituye una señal de petición de bus, al recibirla, el microprocesador responde activando la línea BUSAK y desconectándose de los buses de direcciones y datos para permitir el acceso directo a memoria de un dispositivo más rápido que él. Durante este tiempo, el microprocesador no regenera la memoria, por lo que el dispositivo que ha hecho la petición debe encargarse de esta tarea.

Salida BUSAK (Bus Aknoledge)

La utiliza el microprocesador para indicar el acuse de recibo a una petición de bus. Cuando se genera esta señal, el microprocesador se haya totalmente desconectado de los buses de direcciones y datos, con lo que no interfiere el acceso a memoria del dispositivo que ha pedido el bus.

Las interrupciones en el Z-80

Cualquier microprocesador que valga el plástico que lo envuelve, tiene una posibilidad de interrumpir lo que está haciendo para atender inmediatamente a un dispositivo de alta prioridad que lo solicite, retornando a su tarea principal en el punto donde la dejó, cuando el servicio a este dispositivo haya finalizado.

El Z-80 como buen microprocesador que es, tiene varias posibilidades de interrupción que permiten el acceso con distinta prioridad.

INTERRUPCION NO ENMASCARABLE (NMI)

Es la petición de interrupción de más alta prioridad, se acepta siempre, y se responde siempre de la misma forma: saltando a la posición de memoria 0066h.

En el Spectrum esta forma de interrupción no se utiliza, es más, se encuentra ingeniosamente anulada para facilitar la protección del software comercial; si activáramos a través del slot posterior, la línea NMI, nos encontraríamos con la desagradable sorpresa de que el ordenador ejecuta un salto a la posición de memoria "cero", reinicializándose y borrando toda la memoria. Esto se debe a que la rutina de servicio a la interrupción que se encuentra a partir de 0066h salta a cero si el contenido de las posiciones de memoria 5CB0h y 5CB1h es "cero"; podemos evitar el salto a cero, almacenando un número distinto de cero en estas posiciones, pero en este caso, se produciría un simple retorno y la interrupción sería ignorada.

INTERRUPCION ENMASCARABLE (INT)

Se trata de la interrupción más usada en el Z-80 ya que permite definir el vector de interrupción, y lo que es más importante, decidir por software si se atiende o no la petición.

Se denomina vector de interrupción a la dirección de memoria a que se salta para ejecutar la rutina de servicio a la interrupción.

En el Z-80 existe un "miniregistro" de un solo bit que se denomina flip/flop de aceptación de interrupción. Si este registro está a "1", la petición de interrupcion es aceptada, y si está a "0" es ignorada. Cuando el flip/flop de aceptación está a "0", se dice que la interrupción está enmascarada.

Existen dos instrucciones en el Z-80 que nos permiten enmascarar o habilitar la interrupción, estas interrupciones son: "DI" (Disable Interrupt) y "EI" (Enable Interrupt), se verán detalladamente cuando se estudien las instrucciones de control de la CPU.

Si la interrupción está habilitada, y el microprocesador decide aceptarla, podrá responder de tres modos distintos. Estos tres modos de interrupción, también se seleccionan por software, mediante las instrucciones: "IM0", "IM1" e "IM2" (Interrupt Mode 0, 1 y 2). Estos modos de respuesta se denominan respectivamente: MODO 0, MODO 1 y MODO 2.

MODO 0

En este modo de interrupción, el microprocesador deja libre el bus de datos para permitir que el dispositivo que ha silicitado la interrupción, inserte el código de operacion correspondiente a una instrucción que será ejecutada seguidamente por el microprocesador.

En el Spectrum, este modo de interrupción es redundante, ya que si lo seleccionamos, cuando se vaya a ejecutar, no habrá ningún dispositivo que inserte ningún código de operación, por lo que el bus de datos contendrá "FF", que es precisamente el código de operación de "RST 38", instrucción que obliga al microprocesador a saltar a la posición de memoria 0038h, que es, como veremos ahora, lo que hace en el MODO 1.

MODO 1

En este modo de interrupción, el microprocesador responde a la interrupción, simplemente, saltando a la posición de memoria 0038h. En este caso se dice que el vector de interrupción es fijo.

En el Spectrum, se trabaja normalmente en MODO 1, ya que a partir de la posición de memoria 0038h se encuentra la rutina que lee el teclado.

MODO 2

Es el modo de interrupción más complejo del Z-80, y el que deberemos utilizar para nuestros fines. En este caso, el microprocesador responde de una forma bastante compleja que conviene analizar detenidamente: primero coje el contenido del registro "I", lo considera como un octeto superior de una dirección, el octeto inferior deberá suministrarlo el dispositivo que ha solicitado la interrupción (si no lo suministra, se entiende que es FFh). Acto seguido, lee el número almacenado en esa dirección y la siguiente, lo carga en el "PC", y continúa la ejecución desde ese punto.

Este modo de interrupción permite un salto indirecto a cualquier posición de memoria, hay que tener en cuenta que en el Spectrum el octeto de orden bajo de la dirección será siempre FFh y por tanto, la lectura de la dirección a la que hay que saltar se producirá desde una posición de memoria cuya dirección sea xxFFh, siendo xx el contenido del registro "I", que por motivos evidentes, se denomina Vector de página de interrupción.

Teniendo en cuenta que en el Spectrum se produce una petición de interrupción enmascarable cada 20 milisegundos, podemos desactivar la interrupción (con "DI") para que nuestros programas corran más deprisa, o bien, utilizar la instrucción HALT para sincronizarnos con el barrido de la pantalla. Otra posibilidad es cambiar a MODO 2 y utilizar un vector de interrupción que salte a una rutina nuestra, con lo que ésta se ejecutará 50 veces por segundo.

Palabra de datos del Z-80

Como hemos visto anteriormente, el Z-80 es un microprocesador de 8 bits, esto quiere decir que cada vez que se accede a la memoria, lee un octeto completo, que puede ser un código de operación o un dato.

Un octeto puede almacenar 256 números distintos (2 elevado a 8) pero el Z-80 tiene más de 256 instrucciones diferentes, por lo que algunos códigos de operación ocupan más de un byte. Por otro lado, en un gran número de instrucciones, el operando se ensambla como uno o varios bytes que siguen al código de operación. En la FIGURA 5 se pueden ver los distintos formatos de instrucción del Z-80.

[Figura 5]
Figura 5. Formatos de instrucción del Z-80.
Ciclos o tiempos

Para realizar las operaciones secuencialmente, el Z-80 necesita sincronizar todas sus señales internas y externas y disponer, por tanto, de un patrón de tiempo. Es lo que se denomina: Reloj del microprocesador.

El reloj del microprocesador está constituido por un oscilador electrónico controlado por un cristal de cuarzo, que entrega tres millones y medio de impulsos por segundo (3.5 MHz). Estos impulsos se introducen en el Z-80 a través de la patilla 6 denominada "RELOJ", y el microprocesador utiliza un número determinado de estos impulsos para cada operación.

La primera versión del Z-80 no aceptaba señales de reloj superiores a 2.5 MHz.

En el Spectrum se ha utilizado una versión más moderna denominada Z-80A, que admite señales de reloj de hasta 4 MHz, con lo que se consigue una mayor velocidad de ejecución.

En el Spectrum se ha utilizado una señal de reloj de 3.5 MHz en vez de los 4 tolerados, para evitar llevar al microprocesador al límite de su frecuencia de trabajo, lo que podría dar lugar a errores.

CICLOS DE MAQUINA Y CICLOS DE INSTRUCCION

Se denomina Ciclo de instrucción al tiempo durante el cual el microprocesador ejecuta una instrucción completa.

El ciclo de instrucción se subdivide a su vez, en ciclos de máquina. Un ciclo de máquina es el tiempo durante el cual el microprocesador realiza una operación elemental. Cada ciclo de máquina emplea varios ciclos (impulsos) de reloj.

Se denomina "M1" al ciclo de máquina correspondiente a la búsqueda del código de operación, durante el cual, la pata M1 del microprocesador se coloca a nivel bajo. El ciclo de máquina M1 ocupa 4 ciclos de reloj; un ciclo de reloj dura aproximadamente 0.29 microsegundos (millonésimas de segundo), por lo que el ciclo M1 dura 1.14 microsegundos.

Un ciclo de memoria es una operación de lectura o escritura en memoria, emplea 3 ciclos de reloj, y dura 0.86 microsegundos.

En la FIGURA 6 se puede apreciar el cronograma (diagrama de tiempo) de una instrucción típica.

[Figura 6]
Figura 6. Cronograma de un ciclo típico de instrucción.
TIEMPOS DE EJECUCION

Como el lector habrá deducido ya, es posible calcular el tiempo de ejecución de una determinada rutina en C/M, a condición de conocer el número de ciclos de reloj que emplea cada una de sus instrucciones.

En lo sucesivo, cada vez que veamos una determinada instrucción, indicaremos el número de ciclos de reloj que emplea el microprocesador para ejecutarla, así como el número de veces que accede a memoria (ciclos de memoria).

Como ejemplo, veamos lo que se tarda en cargar el registro "A" con un número. Podemos utilizar la instrucción: LD A,#FF que carga el número 255 (FFh) en el acumulador, esta instrucción accede 2 veces a memoria (2 ciclos de memoria), una para buscar el código de operación (4 ciclos de reloj) y otra para buscar el número que ha de cargar en "A" (3 ciclos de reloj); lo que hace un total de 7 ciclos de reloj, es decir, unos 7 x 0.29 = 2 microsegundos. Este ejemplo ilustra la enorme velocidad del código máquina, el microprocesador es capaz de cargar el acumulador medio millón de veces en un segundo.

Supongamos que queremos sumar en el acumulador una lista de números, y usaremos el registro HL para movernos a lo largo de esa lista, el bucle podría ser:

BUCLE


ADC A, (HL)
INC HL
JR BUCLE

El tiempo de ejecución por cada pasada, sería de 25 ciclos de relo, es decir 7.14 microsegundos. Con este bucle, nuestro ordenador podría sumar 140000 números por segundo. Por supuesto, este pequeño bucle no es operativo, no existe condición de salida del bucle, por lo que el ordenador se quedaría eternamente atrapado dentro de él, y por otro lado, se produciría un rebosamiento en el acumulador, ya que el resultado de las sumas excedería su capacidad, a menos que la mayor parte de los números fueran ceros. La única finalidad de este ejemplo es mostrar la enorme velocidad de ejecución del código máquina.

Para calcular el tiempo de ejecución de sus rutinas, sume los números de ciclos de reloj de cada instrucción y multiplique por 0.29 para obtener el resultado aproximado en microsegundos.

Modos de direccionamiento

En la mayor parte de las operaciones, el Z-80 utiliza datos almacenados en sus registros o en posiciones de memoria. Las formas posibles de indicarle la situación de estos datos, constituyen los diversos modos de direccionamiento.

DIRECCIONAMIENTO INMEDIATO

En este modo de direccionamiento, el byte que sigue al código de operación en memoria, contiene el operando.

CODIGO DE OPERACION

uno o dos bytes

OPERANDO

un byte

Un ejemplo podría ser cargar el acumulador con una constante, donde la constante es el byte que sigue al código de operación.

DIRECCIONAMIENTO INMEDIATO EXTENDIDO

Es igual que el anterior, salvo que el operando ocupa dos bytes, el primer byte es el octeto de orden bajo, y el segundo, el de orden alto.

CODIGO DE OPERACION

uno o dos bytes

OPERANDO (LSB)

octeto de menos peso

OPERANDO (MSB)

octeto de más peso

Un ejemplo podría ser la carga de un registro doble con una constante que, lógicamente ocuparía dos bytes de memoria.

DIRECCIONAMIENTO RAPIDO DE PAGINA CERO

El Z-80 tiene unas instrucciones de salto rápido a una dirección de página cero. Hay ocho direcciones posibles donde se colocan las rutinas de uso más frecuente, de esta forma se puede llamar a estas rutinas empleando un solo byte.

En el Spectrum, estas direcciones se encuentran utilizadas por la ROM para las rutinas de más uso, y son las siguientes:

RST 00h : Inicialización
RST 08h : Salida de error
RST 10h : Imprimir un caracter
RST 18h : Leer un caracter
RST 20h : Leer el siguiente caracter
RST 28h : Entrada al calculador
RST 30h : Hacer espacio en memoria
RST 38h : Leer el teclado

DIRECCIONAMIENTO RELATIVO

En este caso, el byte que sigue al código de operación se emplea como un entero en complemento a dos, que se suma a la dirección actual almacenada en el «PC».

CODIGO DE OPERACION

un byte (salto relativo)

OPERANDO

entero en complemento a 2

Este modo de direccionamiento permite efectuar saltos relativos, con lo que las rutinas pueden ser reubicables es decir, correr de igual forma en cualquier dirección de memoria.

DIRECCIONAMIENTO INDEXADO

En esta forma de direccionamiento, el byte de datos sigue al código de operación contiene un entero de desplazamiento en complemento a dos, que se suma al contenido actual del registro índice correspondiente, para apuntar a una dirección de memoria. El código de operación tiene siempre dos bytes, el primer byte es «DDh» siempre que se utilice el registro «IX» y «FDh» siempre que se utilice el «IY».

DD o FD

índice usado

CODIGO DE OPERACION

un byte

DESPLAZAMIENTO

entero en compl. a dos

DIRECCIONAMIENTO DE REGISTROS

En muchos de los códigos de operación, hay ciertos bits que especifican a qué registro se refiere la instrucción, permaneciendo inalterados el resto de bits. Un ejemplo podría ser la instrucción:

LD C,B

Que significa: «cargar en el registro "C" el contenido del registro "B"».

DIRECCIONAMIENTO IMPLICITO

En este caso, la situación de los datos está implícita en el código de operación. Por ejemplo, en las operaciones aritméticas de 8 bits, el registro «A&rqauo; (acumulador) es siempre el que recibe los resultados.

DIRECCIONAMIENTO INDIRECTO

En esta forma de direccionamiento, el contenido de un registro doble se utiliza como dirección a partir de la cual hay que cargar el dato. Un ejemplo podría ser:

LD A, (HL)

Que significa: «carga el registro A con el contenido de la dirección de memoria apuntada por el registro HL». Es este caso el registro HL se utiliza como puntero para «apuntar» a una dirección de memoria, siempre que un registro se utilice como puntero, su nombre aparecerá, el en código simbólico, encerrado entre paréntesis, significando: «donde apunta el contenido de».

También se puede utilizar como puntero, una constante de dos bytes, ensamblada a continuación del código objeto, por ejemplo:

LD A, (5C37)

Que significa: «carga el registro A con el contenido de la dirección de memoria 5C37h». Cuando hagamos esto en Assembler, normalmente utilizaremos una «etiqueta» de la siguiente forma:

LD A, (ETIQUE)
ETIQUE EQU #5C37

De esta forma, solo tendremos que definir la etiqueta una vez, pero podremos usarla todas las veces que queramos sin tener que recordar de memoria los números. Los nombres de variables del Sistema en el Spectrum son, precisamente, etiquetas del código fuente del Sistema Operativo. El uso de las etiquetas se verá en profundidad cuando estudiemos el manejo de ensambladores.

En algunos casos, el direccionamiento indirecto se utiliza para especificar operandos de 16 bits (dos bytes), en este caso, el puntero apunta al byte de menos peso, siendo el de más peso el siguiente. Por ejemplo:

LD HL, (5C37)

Que significa: «carga el registro L con el contenido de la posición de memoria 5C37h, y el registro H con el contenido de la posición de memoria siguiente (5C38h)».

DIRECCIONAMIENTO DE BITS

Un gran número de instrucciones del Z-80 trabajan directamente sobre bits individuales de registros o posiciones de memoria. En este caso, se utiliza uno de los métodos de direccionamiento anteriores para indicar el registro o posición de memoria en concreto, y tres bits del código de operación para indicar a qué bit de ese registro o posición de memoria nos referimos.

MODOS DE DIRECCIONAMIENTO COMBINADOS

Muchas instrucciones incluyen más de un operando, en estos casos, se pueden combinar más de un modo de direccionamiento dentro de una misma instrucción, por ejemplo:

LD (IX+7), A

Que utiliza direccionamiento indexado para el destino y direccionamiento inmediato para la fuente. Significa: «carga en la posición de memoria apuntada por el contenido del registro IX más 7, el contenido del registro A (acumulador)».

Instrucciones del Z-80

El Z-80 puede ejecutar un gran número de instrucciones, podemos ordenarlas en los siguientes grupos:

CARGA E INTERCAMBIO

Permiten desplazar datos entre registros, o entre estos y posiciones de memoria. También se puede intercambiar el contenido de dos registros, o el de dos grupos alternativos.

ARITMETICAS Y LOGICAS

Permiten realizar operaciones aritméticas o lógicas entre el acumulador y un registro o posición de memoria. Los resultados se almacenan en el acumulador, y los indicadores del registro «F» se ponen a «1» o a «0» en función del resultado de la operación.

BUSQUEDA Y TRANSFERENCIA DE BLOQUES

Se trata de las más poderosas instrucciones del Z-80, es posible transferir todo un bloque de memoria con una sola instrucción; también es posible examinar todo un bloque de memoria para buscar un determinado dato de un byte.

ROTACION Y DESPLAZAMIENTO

Permiten la rotación bit a bit del dato almacenado en un registro o una posición de memoria, las rotaciones pueden incluir el indicador de acarreo del registro «F».

MANIPULACION DE BITS

Permiten tratar de forma independiente cada bit de un registro o una posición de memoria, es posible poner un bit a «1», ponerlo a «0» o examinar si es «1» o «0».

SALTO LLAMADA Y RETORNO

Permite alterar la secuencia normar del programa para saltar a otro lugar de la memoria oejecutar una subrutina. También es posible retornar desde una subrutina al punto donde se la llamó.

ENTRADA Y SALIDA

Permiten leer y escribir datos en los ports de entrada/salida, con lo cual se comunica el ordenador con el mundo exterior.

CONTROL CPU

Se utilizan para controlar el propio funcionamiento del microprocesador, inhibir o habilitar interrupciones, cambiar el modo de interrupción, detener el funcionamiento del microprocesador, etc.

En los capítulos posteriores de este curso, se irán viendo detenidamente, una a una, todas las instrucciones de cada uno de estos grupos y la forma de utilizarlas en nuestros programas

Antes, en el capítulo siguiente, se verán los conceptos básicos de la programación en Assembler, y las formas de almacenar y ejecutar nuestros programas en Código Máquina.

[Foto]