From the monthly archives:

September 2009

En este post voy a poner el desarrollo sobre la Unidad Artitmético Lógica que es una de las secciones más interesantes de un procesador. De hecho, las veces que me he aproximado a estudiar por dentro los microprocesadores, siempre he empezado por el ALU. Tan es así, que lo he implementado en tres formas: la primera fue comprando (hace 20 años) el circuito integrado 74LS181, armándola con compuertas como lo hace Donn Stewart en su página o con la técnica de "micro-código" similar a la que utilicé para hacer la lógica de control para este CPU.

Diseño de la ALU

Al igual que en la lógica de control, se van a utilizar memorias EPROM de la familia 27XX. Podemos ver que al proponernos hacer una ALU de 8 bits, las líneas del bus de direcciones son muchas porque se necesitan ocho para el primer dato, ocho para el segundo y tres para el comando de la operación (Total: 19 bits). Para cumplir con el bus de entradas, se necesitaría por lo tanto una memoria de 512 MB.

La solución por lo tanto es dividir el problema en dos y mejor hacer dos ALUs de cuatro bits que estén conectadas en cascada. Para hacer una ALU de cuatro bits se necesitan cuatro bits para el primer dato, cuatro para el segundo, tres para el comando y uno para la bandera de acarreo (Total: 12 bits). Por lo tanto, es suficiente con dos memorias 2732 para nuestra ALU de 8 bits.

memoria
ALU implementada con dos memorias 2732

Al requerir dos memorias EPROM en cascada, se limita enormemente el rendimiento de la CPU. Para fines experimientales, esta bien; pero sin duda se recomienda las otras alternativas si se busca un CPU con mejor rendimiento.

Programando los EPROMs

Hacer una tabla a mano para programar las memorias es algo engorroso, por lo que en este caso se hizo un pequeño programa en C que la obtiene por nosotros. El código fuente es el siguiente:

#include <iostream>

#include <string>

using namespace std;

int main() {

    cout <<"¡Creando ALU EPROM!" <<endl;

    // Abre el archivo

    FILE *outstream;

    outstream=fopen("alu.bin","wb");

    if( outstream == NULL ) {

        cout <<"No se pudo abrir el archivo" <<endl;
        return -1;
    }

    // Ciclo principal

    char alu_command, a, b;
    bool carry_in, carry_out, borrow_out, zero_out;

    int salida;

    for( long address = 0; address<0x10000; ++address ) {

        // Datos de entrada

        a  = address & 0x0f;

        b  = ( address>>4 ) & 0x0f;

        alu_command= ( address>> 8 ) & 0x07;

        carry_in = ( (long) address & 0x800 ) ? true : false;

        salida = 0;

        carry_out = false;

        borrow_out = false;

        zero_out = false;

        switch( alu_command ) {

            case 0:

                // NOT

                salida = ~a & 0x0f;

                break;

            case 1:

                // LROT

                if ( carry_in )

                   salida = 0x01;

                salida |= ( a<<1 ) & 0x0e;

                if ( ( char ) a & 0x08 )

                   carry_out = true;

                break;

            case 2:

                // RROT

                if ( carry_in )

                   salida = 0x08;

                salida |= a>>1 & 0x07;

                if ( ( char ) a & 0x01 )

                   borrow_out = true;

                break;

            case 3:

                // ADD

                if ( carry_in )

                   salida = 0x01;

                salida += ( a + b ) & 0x0f;

                if ( a + b> 0x0f )

                   carry_out = true;

                break;

            case 4:

                // SUB

                if ( carry_in )

                   salida = 0x10;

                salida += ( a - b ) & 0x0f;

                break;

            case 5:

                // AND

                salida = a & b;

                break;

            case 6:

                // OR

                salida = a | b;

                break;

            case 7:

                // XOR

                salida = a ^ b;

                break;

        }

        // Checa condición de cero

        if ( salida == 0 )

            zero_out = true;

        //  Ajusta las banderas

        if ( carry_out )

            salida |= 0x10;

        if ( borrow_out )

            salida |= 0x20;

        if ( zero_out )

            salida |= 0x40;

        fputc( salida, outstream );

    }

    fclose(outstream);

    return 0;

}

Nota importante:

La construcción del CPU se está llevando justo ahorita, por lo que toda la información presentada en este post puede estar incompleta o incorrecta al ser de carácter provisional.

{ 0 comments }

Durante el transcurso de este post se va a desarrollar la Unidad de Control. Desde mi punto de vista, es la parte más interesante porque aunque sencilla en concepto, es el corazón del procesador y su diseño es lo que nos llevó a los humanos a crear un nuevo tipo de máquinas: las programables.

Unidad de control

La Unidad de control esta formada por varias partes: registro de estado, secuenciador, decodificador de estado y la lógica de control. Esta última nos produce las señales de control que son utilizadas para la implementación en hardware de cada una de las instrucciones. El diagrama de la unidad de Control es el siguiente:

UnidadControl
Figura 5.1 Unidad de Control

El trabajo de la Unidad de Control consiste en hacer pasar cada instrucción por varios "estados" en los cuales se va realizando por partes su operación. A pesar de tener diferentes instrucciones, los estados son compartidos entre ellas por tener funciones similares. Por ejemplo, todas las instrucciones al inicio deben cargar su operando e incrementar el contador de programa; todas las instrucciones matemáticas en nuestro diseño deben cargar un operando y guardar el resultado en el acumulador. Las operaciones lógicas también guardan el resultado en el acumulador.

Registro de Estado

Nuestro Registro de Estado va a ser de 4 bits, lo cual nos va a permitir manejar hasta 16 estados diferentes; lo cual es suficiente para un procesador de esta escala y a su vez, nos va a simplificar el desarrollo posterior de nuestro secuenciador al disminuir su número de entradas..

Estados de Control

El diseño de nuestros estados se debe pensar detenidamente porque de él depende en gran medida el hardware a implementar y la velocidad de las instrucciones. Si dividimos una orden en muchos ciclos, el microprocesador ejecutará menos instrucciones por segundo. Si juntamos muchas instrucciones en un sólo estado, es posible que aumentemos el hardware necesario.

En la siguiente tabla definimos los estados de control de nuestro procesador:

Número Acción Instrucciones Actividades
0 Obtención de la instrucción a ejecutar. Todas 1. Carga la instrucción en el registro de instruccón.
2. Incrementa el contador de programa.
1 Obtención de operando en instrucciones de 2 bytes "LD A, #", "LD IX, #", "LD A, $", "LD A, IX:$", "LD $, A", "LD IX:$, A", "JMP IX:$", "JPC IX:$", "JPZ IX:$", "JPM IX:$", "JMP", "JPC", "JPZ", "JPM", "LROT", "RROT", "AND", "OR", "XOR", "ADD", "SUB" 1. Carga el operando en el registro de datos.
2. Incrementa el contador de programa.
2 Carga al acumulador con dato inmediato. "LD A, #" 1. Carga al acumulador con dato inmediato.
3 Carga al registro índice con dato inmediato. "LD I, #" 1. Carga al registro índice con dato inmediato.
4 Carga indexada o de página al acumulador "LD A, $", "LD A, IX:$" 1. Selecciona a usar el registro IX o los últimos 4 bits del contador de programa.
2. Carga al acumulador con dato en memoria.
5 Carga indexada o de página del valor del acumulador "LD $, A", "LD IX:$, A" 1. Selecciona a usar el registro IX o los últimos 4 bits del contador de programa.
2. Guarda en la memoria el contenido del acumulador.
6 Brinco Indexado "JMP IX:$", "JPC IX:$", "JPZ IX:$", "JPM IX:$" 1. Si se cumple la condición, carga el Contador de programa con el operando y el registro indice.
7 Brinco Inmediato JMP, JMZ, JPC JPM 1. Si se cumple la condición, carga el Contador de programa con el operando
8 Realiza operación lógica sobre el acumulador. "NOT", "LROT", "RROT", "AND", "OR", "XOR" 1. Realiza operación lógica sobre el acumulador por medio de la ALU.
9 Realiza operación aritmética sobre el acumulador "ADD", "SUB" 1. Realiza operación aritmética sobre el acumulador por medio de la ALU.
10 Operaciones de entrada. "IN 0", "IN 1", "IN 2", "IN 3" 1. Realiza salida/entrada sobre el acumulador.
11 Operaciones de salida. "OUT 0", "OUT 1", "OUT 2", "OUT 3" 1. Realiza salida/entrada sobre el acumulador.
12 Retorno a estado inicial "LD A, #", "LD IX, #", "LD A, $", "LD A, IX:$", "LD $, A", "LD IX:$, A", "JMP IX:$", "JPC IX:$", "JPZ IX:$", "JPM IX:$", "JMP", "JPC", "JPZ", "JPM", "NOT", "LROT", "RROT", "AND", "OR", "XOR", "ADD", "SUB" 1. Termina cualquier ciclo pendiente.

Secuenciador

Ahora que tenemos definidos nuestros estados, el siguiente paso es diseñar la "secuencia" que estos estados van a seguir para ejecutar nuestro set de instrucciones. Para mi procesador el diagrama de estado es el siguiente:

Secuenciador
Figura 5.2 Máquina de estados de las instrucciones del procesador

Con el estado actual y la orden a ejecutarse es como se obtiene el siguiente estado con una simple tabla de correspondencia a la cual llamamos secuenciador. La implementación en hardware del secuenciador se puede obtener utilizando compuertas. Sin embargo, el resultado puede requerir una gran cantidad de circuitos integrados y una difícil escalabilidad a futuro, pues cada modificación requeriría cambios en el hardware. Es por eso que en esta parte he sacrificado el diseño con compuertas ( que podría hacer el proyecto más interesante ) por el uso de una memoria EPROM ( que lo hace escalable y sencillo de modificar y depurar ).

De hecho, en la década de los sesentas del siglo veinte, los fabricantes de computadoras empezaron a usar esta técnica no solamente para reducir el número de circuitos; sino también para poderlos actualizar, ampliar o corregir sin modificar el hardware. A este tipo de firmware se le conoce como "microcódigo" y es una técnica muy recurrida incluso en la actualidad.

En este diseño, sirven como entrada las banderas de estado para simplificar la implantación en hardware de los saltos condicionales.

Implementación en hardware

El diagrama esquemático de la parte principal de la lógica de control es el siguiente:

controlunit
Figura 5.3 Diagrama esquemático del sistema de control - 1

Aqui podemos apreciar que el secuenciador esta elaborado con una memoria 2732, el registro de estado al igual que el registro de instrucción esta fabricado con un circuito integrados 74175 para que al aplicar la señal de reinicio pongan como estado inicial el cero. El decodificador de estado esta hecho con un circuito 74LS154.

Registro de Datos

De igual forma que guardamos en un registro la instrucción, es conveniente agregar un registro para el dato que contiene las instrucciones de dos bytes para tenerlo disponible en los otros estados. En este caso como no nos interesa que el registro este inicializado a ceros podemos usar un solo integrado 74LS374 para esta función.


DataRegister
Figura 5.4 Circuito del Registro de datos

Contenido del EPROM

Con ayuda de la máquina de estados del diagrama 5.2, podemos armar la tabla que va ir contenida en la memoria EPROM:

Estado actual (4 bits) Operación (7 bits) Hex Siguiente estado (4 bits) Hex
0000 0000-0000 000 0000 00
0000 0000-0001->0000-1110 001->00E 0001 01
0000 0010-0000->0010-0011 020->023 1010 0A
0000 0011-0000->0011-0011 030->033 1011 0B
0000 0100-0000->0100-0010 040->042 1000 08
0000 0100-0011->0100-0111 043->047 0001 01
0001 0000-0001 101 0010 02
0001 0000-0010 102 0011 03
0001 0000-0011->0000-0100 103->104 0100 04
0001 0000-0101->0000-0110 105->106 0101 05
0001 0000-0111->0000-1010 107->10A 0110 06
0001 0000-1011->0000-1110 10B->10E 0111 07
0001 1000-0011->1000-0111 143->147 1001 09
0010 0000-0001 201 1100 0C
0011 0000-0010 302 1100 0C
0100 0000-0011->0000-0100 403->404 1100 0C
0101 0000-0101->0000-0110 505->506 1100 0C
0110 0000-0111->0000-1010 607->60A 1100 0C
0111 0000-1011->0000-1110 70B->70E 1100 0C
1000 0100-0000->0100-0010 840->842 0000 00
1001 0100-0011->0100-0111 943->847 1100 0C
1010 0010-0000->0010-0011 A20->A23 0000 00
1011 0011-0000->0011-0011 B30->B33 0000 00
1100 0000 00

Señales de control

Nota importante:

La construcción del CPU se está llevando justo ahorita, por lo que toda la información presentada en este post puede estar incompleta o incorrecta al ser de carácter provisional.

{ 0 comments }

En este post se va a tratar el diseño del contador de programa que es el encargado de señalar la instrucción a ejecutar y el sistema de memoria donde se almacenan tanto las instrucciones del programa que se esta ejecutando y la memoria de datos. Con estos dos sistemas se forman las dos troncales más importantes de un procesador: el bus datos y el bus de direcciones.

Contador de programa

El contador de programa es un registro que nos permite seguir la huella de la instrucción que estamos ejecutando. Se van a utilizar contadores para formar nuestro bus de direcciones de 12 bits capaz de direccionar un máximo de 4 KB. El circuito integrado 74LS161 es un contador en cascada de cuatro bits, el cual permite inicializarlo a cero (a la hora de un reset o al aplicar corriente) e incluso ponerlos a un número arbitrario. Esta característica última es la que nos va permitir implementar las instrucciones de salto.

El diagrama esquemático de nuestro contador de programa sería el siguiente:

contador programa
Figura 4.1 Contador de Programa

Un diseño avanzado sería conectarlo a la ALU para poder aplicarle operaciones aritméticas y así implementar instrucciones de brincos relativos al poder aplicarle sumas y restas. Por el momento esto último no se va hacer para simplificar la circuitería.

Sistema de Memoria

Realmente, el sistema de memoria, al igual que el sistema de reloj y de reinicio; pueden pertenecer internamente o no al diseño del procesador. Pero obviamente es una parte esencial sin la cual una computadora no funcionaría y por lo tanto la vamos a tratar porque no podríamos probar nuestra CPU sin él.

Al tener 12 bits de bus de direcciones, tan sólo podemos direccionar 4 KB de memoria, esto nos permite usar el último byte para seleccionar dos bloques de memoria de 2 KB. Uno sería una memoria EPROM 2716 (2K bytes) donde tendríamos grabado nuestro BIOS o "sistema operativo" y en el otro banco tendríamos la memoria RAM que se podría omitir para algunos que no requieran guardar o procesar datos. Para este proyecto de van usar dos memorias 2114 de 1 Kbyte x 4 cada una. El diagrama esquemático es el siguiente:

memory
Figura 4.2 Memoria RAM y ROM

Por el otro lado, el bus de direcciones estaría conectado directamente al contador de programa, al registro de dirección y a los puertos de entrada y salida.

Registro de Instrucción

Es importante recordar que las instrucciones que pueden llevar varios ciclos para ejecutarse y que el contador de programa no se incrementa con cada ciclo de señal de reloj, sino en el estado en el cual le corresponda incrementarse y puede moverse para adquirir un operador. Como la secuencia de los estados depende también de la instrucción es necesario tener un registro donde tengamos almacenada la instrucción actual. El diagrama esquemático es el siguiente:

CommandRegister
Figura 4.3 Registro de Instrucción

Se utilizan dos registros 74LS175 porque permite limpiar el registro a diferencia de usar solo un circuito integrado 74LS373. Para grabar el comando en el registro, se utiliza una compuerta para casarlo con el nivel alto de la señal de reloj. Por lo que es muy importante tener su valor con antelación durante el nivel bajo del ciclo.

Programa de prueba

Un programa de prueba adecuado que podemos grabar en la memoria EPROM, podría ser el siguiente:

00:0000 01 AA LD A, #AA
00:0002 44 77 SUB A, #77
00:0004 00    NOP
00:0005 02 03 LD IX, #03
00:0007 06 00 LD IX:$00,A
00:0009 80    NOT A
00:000A 31    OUT 1, A
00:000B 41    LROT A
00:000C 0B 0A JMP $0A

En el se carga el acumulador, se realiza una operación aritmética, se guarda información en la memoria y se rota un registro que es sacado a un puerto de salida.

Nota importante:

La construcción del microprocesador se está llevando justo ahorita, por lo que toda la información presentada en este post puede estar incompleta o incorrecta al ser de carácter provisional.

{ 0 comments }

En este post se van a publicar los primeros circuitos accesorios que necesita el CPU casero que estoy construyendo. Aunque sencillos, se presentan primero porque son muy útiles para probar las demás partes del procesador.

Sistema de Reloj

Para fines de depuración se han diseñado tres tipos de reloj: para "producción", uno con un cristal de 2 Mhz y para debugeo; uno muy lento con unos inversores schmitt triger y otro paso a paso usando un flip flop para revisar todo paso a paso. Estos tres circuitos electrónicos son los mismos que se utilizan con cualquier otro microprocesador y son idénticos a los que usaba hace años con el viejo Z80A.

Circuito con señal de reloj paso a paso

Este circuito es muy útil en etapas de desarrollo o cuando se esta cableando. Consiste en un "flip-flop" para alternar y mantener fijo el nivel actual de la señal de reloj hasta que se presione el interruptor opuesto:

reloj1
Figura 3.1

Señal de reloj de baja velocidad

Ya que se tienen construidas partes completas y se prueban la máquina de estado de las instrucciones o el contador de programa, es conveniente usar un reloj de baja velocidad para poder ver el flujo completo sin el procedimiento engorroso que puede representar presionar los botones del paso a paso. Además puede sacar a la luz problemas de sincronía.

reloj2
Figura 3.2

Esta construido con inversores schmitt trigger (circuito TTL 74LS14), un capacitor y una resistencia. Sin duda es una de las formas más sencillas y baratas de hacer una señal de reloj, la cual podemos modificar fácilmente cambiando los valores de los capacitores. La formula que nos da la frecuencia es la siguiente:

F = 1 / ( 2.2 * R * C )

Señal de reloj de alta velocidad con un cristal

Para usar el procesador con una señal de reloj que pueda ser para medir tiempos con mayor exactitud es conveniente usar un cristal. Un circuito muy empleado es el siguiente:

xtal
Figura 3.3

La frecuencia esta dada por el crystal.

Sistema de Reinicio

Al igual que el Sistema de Reloj, el circuito para crear la señal de reset es el mismo que se suele usar para microprocesadores. El diagrama es el siguiente:

reset
Figura 3.4

Cumple con dos funciones: la primera es presentar un nivel bajo cuando se presiona el botón de reinicio. La segunda es dar un pulso a nivel bajo cuando se da alimentación al sistema. Esto se hace por medio del condensador y la resistencia.

Nota importante:

La construcción del microprocesador se está llevando justo ahorita, por lo que toda la información presentada en este post puede estar incompleta o incorrecta al ser de carácter provisional.

{ 0 comments }

Una de las ventajas de diseñar nuestro propio "microprocesador" es que podemos hacerlo con las características que queramos, incluso si estas son muy caprichosas. Por supuesto que entre mayores sean las funcionalidades, es muy probable que la complejidad, el número de circuitos y su costo aumente drásticamente. Lo normal es que el diseño del microprocesador sea en función de la tarea que vaya a realizar. En este proyecto, he buscado que el CPU resultante no sea un elefante blanco, que quepa en unos cuantos protoboards pero que muestre los elementos más representativos de un microprocesador para que cubra una función didáctica (al menos para mí :) ). Bueno, debo confesar que otro de los propósitos es acabarlo lo más pronto posible :P .

cpu
Figura 2.1: Configuración propuesta antes del cableado

Partes del CPU

Por muchos años he estado acostumbrado a usar microprocesadores que utilizan en modelo de Von Newman, por lo que no es raro que este proyecto busque diseñar mi CPU con esta estrategia. En las siguientes secciones de este post, me pongo a soñar ( con los pies en la tierra ) sobre las partes que me gustaría que tuviera mi procesador casero y las instrucciones que va a soportar.

El diagrama de bloquesdel CPU sería a grandes rasgos así:

diag_cpu2

A continuación presento una breve descripción de sus componentes:

Contador de Programa

El contador de programa es un registro que se incrementa con cada instrucción a menos que se ejecute una instrucción de salto. Para el desarrollo de este microprocesador se va utilizar tres contadores de 4 bits que permitirán direccionar 4 KB de memoria.

Al encender el CPU o al aplicar un pulso de reset, este se inicializa a ceros, por lo que la primera dirección de memoria debe ser ocupada por una memoria no volátil como un EPROM en esa localidad de memoria.

Registro de estado de instrucción

Es un registro de 4 bits el cual indica en que estado de la instrucción estamos. Cada instrucción pasa por varios estados en cada ciclo de reloj. Es por eso que muchas de las instrucciones en realidad llevan varios ciclos de reloj en ejecutarse en este y en muchos microprocesadores. Que un microprocesador corra a 1 Mhz, no quiere decir que realice un millón de instrucciones por segundo.

Registro de instrucción

Como se van a manejar instrucciones de múltiples ciclos es necesario guardar la instrucción que se esta ejecutando para decodificar las señales de control necesarias durante todos los ciclos que esta lleven. Tentativamente será de 8 bits.

Registro de Datos

Al igual que el registro de instrucción, el registro de datos guarda el último operando de la instrucción en curso, si es el caso. Obviamente las instrucciones de un solo byte no llenan este registro.

Acumulador

Tener muchos registros puede ayudar aumentar la eficiencia de los programas que se realizan, pues los programas los aprovecharían como variables y así se evitaría en muchos casos el acceso a memoria para leer o modificar sus valores. Además de que el uso del ALU puede ser también directo.

Como el diseño es similar para n acumuladores, tanto solo se va a construir uno de 8 bits; pues uno es suficiente para entender el funcionamiento de este tipo de registros.

Registro Indice

Es un registro de 4 bits que va permitir que las instrucciones de brinco puedan ser "paginadas" por él.

Banderas de Estado

Las banderas de estado son muy útiles pues son las que pueden llevar la lógica de control con los brincos condicionales. Nuestro microprocesador tiene 3: Bandera de cero, Bandera de sobreflujo y bandera de signo.

Unidad Aritmético-Lógica

La unidad aritmético-lógica es el elemento del procesador donde se llevan acabo las operaciones que hacen muy útil al microprocesador. Después de la unidad de control, es la parte más interesante a mi forma de ver. Más adelante le voy a dedicar uno o dos posts tan sólo a esta unidad.

Memoria RAM y ROM

La memoria RAM y ROM sirve para almacenar programas y datos las cuales puedes ser accesibles por medio del contador de programa de 12 bits (4 KB ).

Puertos de entrada y salida

Los puertos de entrada y salida sirven para expandir el sistema con dispositivos periféricos,

Instrucciones

Ya que soñamos un poco al plantear como sería nuestro microprocesador, ahora sigue algo aún más divertido: el decidir que instrucciones va ser capaz de ejecutar nuestra creación. Después de pensar un buen rato, así es como quedo nuestro set de instrucciones:

Instrucciones especiales

Operador Mnemónico Operandos Ciclos Descripción
00 NOP 0 1 No operación

Instrucciones de carga

Sólo tenemos instrucciones para el acumulador y el registro índice. En el caso del registro indice, al ser de 4 bits; sólo los 4 bits menos significativos son los que se cargan.

Operador Mnemónico Operandos Ciclos Descripción
01 LD A, # 1 4 Carga inmediata al acumulador
02 LD IX, # 1 4 Carga inmediata al registro indice
03 LD A, $ 1 4 Carga desde memoria al acumulador
04 LD A, IX:$ 1 4 Carga desde memoria al acumulador
05 LD $, A 1 4 Carga desde el acumulador a la memoria
06 LD IX:$, A 1 4 Carga desde el acumulador a la memoria

Instrucciones de salto

Se cuenta con dos modos de direccionamiento: Indexado ( sólo página ) y directo ( no extendido sobre la misma página ).

Operador Mnemónico Operandos Ciclos Descripción
07 JMP IX:$ 1 4 Brinco indexado
08 JPC IX:$ 1 4 Brinco indexado si la bandera de acarreo es uno
09 JPZ IX:$ 1 4 Brinco indexado si la bandera de cero es uno
0A JPM IX:$ 1 4 Brinco indexado si la bandera de signo es uno
0B JMP 1 4 Brinco directo (en la misma página)
0C JPC 1 4 Brinco directo (en la misma página) si la bandera de acarreo es uno
0D JPZ 1 4 Brinco directo (en la misma página) si la bandera de cero es uno
0E JPM 1 4 Brinco directo (en la misma página)si la bandera de signo es uno

Instrucciones aritméticas y lógicas

El ALU es un bloque independiente que tiene sus propios operadores, por lo que se facilita enormemente el diseño si hace concordar los bytes de los operadores de las instrucciones de la ALU y del microprocesador; así no es necesaria una decodificación extra. Aqui esta la tabla de las operaciones propuestas:

Operador Operador ALU Mnemónico Operandos Ciclos Descripción
40 000 NOT 0 2 Negación del acumulador
41 001 LROT 0 2 Rotación del acumulador a la izquierda
42 010 RROT 0 2 Rotación del acumulador a la izquierda
43 011 ADD 1 4 Suma con el acumulador
44 100 SUB 1 4 Resta con el acumulador
45 101 AND 1 4 AND lógica del acumulador a la izquierda
46 110 OR 1 4 OR lógico con el acumulador
47 111 XOR 1 4 XOR lógico con el acumulador

Instrucciones de puertos de Entrada y Salida

Como se mencionó arriba, se va a emplear instrucciones especificas para los puertos de entrada y salida para evitar direcciones de memoria para este propósito. Las instrucciones de entrada y salida están basadas en las del viejo Z80.

Operador Mnemónico Operandos Ciclos Descripción
20 IN 0 0 2 Entrada del puerto 0 al acumulador
21 IN 1 0 2 Entrada del puerto 1 al acumulador
22 IN 2 0 2 Entrada del puerto 2 al acumulador
23 IN 3 0 2 Entrada del puerto 3 al acumulador
30 OUT 0 0 2 Salida del puerto 0 del acumulador
31 OUT 1 0 2 Salida del puerto 1 del acumulador
32 OUT 2 0 2 Salida del puerto 2 del acumulador
33 OUT 3 0 2 Salida del puerto 3 del acumulador

Para pruebas se va a conectar unos interruptores, unos leds y tal vez un puerto en serie y tal vez en el futuro, una pantalla de video usando un pic como VDG.

Cosas que se quedaron en el tintero (hasta ahora)

Puntero de Pila

El puntero de pila es un registro que apunta una posición de memoria donde se introducen datos. Es muy útil contar con él porque permite implementar el uso de subrutinas e interrupciones de software/hardware. Realmente se echa de menos y es probable que en el futuro lo agregue. Pero por el momento será el gran ausente.

Segmentación de instrucciones

Es una técnica de optimización que permite al ejecutar una instrucción, ir buscando el código de la siguiente o incluso ejecutar simultáneamente varias operaciones. Para una microprocesador de prueba no es necesaria, pero muchos de los microprocesadores modernos la incluyen que se ha vuelto un tema de estudio obligado.

Conectar el contador de programa a la ALU

Esto permitiría instrucciones de brinco y carga más poderosas al poder efectuar sumas y restas relativas a la dirección actual.

Más instrucciones

Las instrucciones iniciales que tiene este microprocesador casero son las mínimas necesarias y las representativas. Agregar más instrucciones no es más difícil de lo que se va a desarrollar en estos posts, pero aunque siempre deseable por el programador, su implementación aumenta el número de circuitos integrados, el costo y las horas de armado y salvo conceptos específicos no creo que se aporten más conocimientos.

Nota importante:

La construcción del microprocesador se está llevando justo ahorita, por lo que toda la información presentada en este post esta incompleta o incorrecta al ser de carácter provisional.

{ 0 comments }