Práctica 4 : Lenguaje y Arquitectura

Introducción

¿Cuál es el objetivo de cada uno de esos proyectos con sus palabras y qué se debe hacer para desarrollarlo?


El primero proyecto se enfoca en la programación de bajo nivel en lenguaje de máquina "Hack", esto es conveniente para familiarizarnos con el sistema informático por medio de la construcción de algunos programas de bajo nivel escritos en el lenguaje de máquina. Además, nos ayuda a entender por qué su hardware fue diseñado de cierta manera. Dicho esto, en este proyecto se construirán dos programas en lenguaje de maquina Hack, el primero es un programa que calcula el producto de dos ubicaciones en la RAM y el segundo es un programa interactivo que permite borrar o ennegrecer la pantalla según ciertas condiciones. Para realizarlos necesitaremos primeramente el software Assembler proporcionado por The Nand2tetris Software Suite, el cual nos permitirá programar en lenguaje maquina Hack y traducirlo a binario generando un archivo .hack el cual se lo podremos pasar al software CPUEmulator. Pero, ¿por qué no traducir directamente el código máquina a binario? Bueno, diferentes máquinas tienen diferente interpretación del código máquina la información que transmite cada línea de binarios puede ser bastante diferente. Por ejemplo la CPU Intel y la CPU AMD pueden tener una traducción totalmente diferente de la misma línea de 101010101010101010.


Por otra parte, en el proyecto 5 vamos a tomar todos los chips que hemos construido en los proyectos 1-3 con el objetivo de integrarlos en un sistema informático capaz de ejecutar programas escritos en el lenguaje de máquina. La computadora que construiremos, llamada "Hack", tiene dos virtudes importantes. Por un lado, Hack es una máquina que se puede construir, utilizando chips construidos previamente y el simulador de hardware suministrado. Por el otro, construirlo nos dará una comprensión de cómo funcionan las computadoras en los niveles bajos de hardware y software.

Multiplication

El multiplication program calcula el producto de dos ubicaciones de la RAM y almacena el resultado en otra dirección. Por lo que las entradas de este programa son los valores almacenados en R[0] y R[1] ya que como sabemos, la primera instrucción se cargaría en la dirección 0 y la siguiente en la dirección 1 o en otras palabras, las dos ubicaciones superiores de la RAM y además de esto, almacena el resultado en R[2].

Mult.asm

// This file is part of www.nand2tetris.org

// and the book "The Elements of Computing Systems"

// by Nisan and Schocken, MIT Press.

// File name: projects/04/Mult.asm


// Multiplies R0 and R1 and stores the result in R2.

// (R0, R1, R2 refer to RAM[0], RAM[1], and RAM[2], respectively.)

//

// This program only needs to handle arguments that satisfy

// R0 >= 0, R1 >= 0, and R0*R1 < 32768.


@sum

M=0 // Inicializa sum en 0

@R1

D=M

@count

M=D // Inicializa el conteo al valor en RAM[1]

(LOOP)

@count

D=M

@END

D;JEQ // Verifique si el conteo es 0, si es así, vaya a termina (va a END)

@R0

D=M

@sum

M=M+D // Agrega RAM[0] a la suma

@count

M=M-1 // Resta el conteo en 1

@LOOP

0;JMP // Realiza el bucle de nuevo

(END)

@sum

D=M

@R2

M=D // Escribe sum en RAM[2]

Resultados obtenidos

Con el código listo que realice la multiplicación, tenemos que traducir el código ASM a código binario para preparar el código y sea realmente ejecutable en el CPUEmulator. Para ello utilizamos el Assembler suministrado para traducir el programa Mult.asm y producir un archivo Mult.hack

Mult.hack

0000000000010000

1110101010001000

0000000000000001

1111110000010000

0000000000010001

1110001100001000

0000000000010001

1111110000010000

0000000000010010

1110001100000010

0000000000000000

1111110000010000

0000000000010000

1111000010001000

0000000000010001

1111110010001000

0000000000000110

1110101010000111

0000000000010000

1111110000010000

0000000000000010

1110001100001000



Resultados obtenidos

Ahora con la herramienta CPUEmulator, se le asignarán los valores que queremos que se multipliquen. En nuestro caso, utilizamos para la dirección R[0] = 5 y para dirección R[1] = 10 para obtener en la dirección R[2] el valor 50

I/O Handling

Este es un programa interactivo el cual cuando se presiona una tecla (cualquier tecla), el programa ennegrece la pantalla (escribe "black" en cada cada pixel) y en el caso donde no se presiona ninguna tecla, la pantalla debe borrarse (escribe "white" en cada pixel).

Fill.asm

// This file is part of www.nand2tetris.org

// and the book "The Elements of Computing Systems"

// by Nisan and Schocken, MIT Press.

// File name: projects/04/Fill.asm


// Runs an infinite loop that listens to the keyboard input.

// When a key is pressed (any key), the program blackens the screen,

// i.e. writes "black" in every pixel;

// the screen should remain fully black as long as the key is pressed.

// When no key is pressed, the program clears the screen, i.e. writes

// "white" in every pixel;

// the screen should remain fully clear as long as no key is pressed.


// Put your code here.


(RESTART)

@SCREEN

D=A

@0

M=D //Coloca el inicio de la pantalla en RAM[0]


///////////////////////////

(KBDCHECK)


@KBD

D=M

@BLACK

D;JGT //Si se presiona alguna tecla, ennegrece

@WHITE

D;JEQ //De lo contrario blanquea la pantalla


@KBDCHECK

0;JMP


(BLACK)

@1

M=-1 //Con lo que se rellena la pantalla en el caso de que se presione tecla (-1=11111111111111)

@CHANGE

0;JMP


(WHITE)

@1

M=0 //Con lo que se rellena la pantalla en el caso contrario

@CHANGE

0;JMP


(CHANGE)

@1 //Comprueba con qué rellenar la pantalla

D=M //D contiene negro o blanco


@0

A=M //Obtiene la dirección del pixel de pantalla para rellenarlo

M=D //lo rellena


@0

D=M+1 //Siguiente pixel

@KBD

D=A-D


@0

M=M+1 //Siguiente pixel

A=M


@CHANGE

D;JGT //Si A=0 toda la pantalla se vuelve negra

@RESTART

0;JMP

Resultados obtenidos

Con el código listo que realice la función interactiva de la pantalla, tenemos que traducir el código ASM a código binario para preparar el código y sea realmente ejecutable en el CPUEmulator. Para ello utilizamos el Assembler suministrado para traducir el programa Fill.asm y producir un archivo Fill.hack


Fill.hack

0100000000000000

1110110000010000

0000000000000000

1110001100001000

0110000000000000

1111110000010000

0000000000001100

1110001100000001

0000000000010000

1110001100000010

0000000000000100

1110101010000111

0000000000000001

1110111010001000

0000000000010100

1110101010000111

0000000000000001

1110101010001000

0000000000010100

1110101010000111

0000000000000001

1111110000010000

0000000000000000

1111110000100000

1110001100001000

0000000000000000

1111110111010000

0110000000000000

1110000111010000

0000000000000000

1111110111001000

1111110000100000

0000000000010100

1110001100000001

0000000000000000

1110101010000111


Resultados obtenidos

Ahora con la herramienta CPUEmulator, probamos el programa mediante el archivo Fill.hack generado con el Assembler. El cual dependiendo de las entradas este nos permitirá visualizar la pantalla en negro o blanco

Proyecto 5. Arquitectura de Computadores

MEMORY

Este chip memory, está compuesto esencialmente de una fusión de otros tres chips de niveles inferiores: RAM16K, pantalla, teclado. Aunque estos están ya incorporados por lo que no hay necesidad de construirlos. Además también cuenta con las compuertas: DMux4Way, Mux4Way16 y or.

Memory.hdl


// This file is part of www.nand2tetris.org

// and the book "The Elements of Computing Systems"

// by Nisan and Schocken, MIT Press.

// File name: projects/05/Memory.hdl


/**

* The complete address space of the Hack computer's memory,

* including RAM and memory-mapped I/O.

* The chip facilitates read and write operations, as follows:

* Read: out(t) = Memory[address(t)](t)

* Write: if load(t-1) then Memory[address(t-1)](t) = in(t-1)

* In words: the chip always outputs the value stored at the memory

* location specified by address. If load==1, the in value is loaded

* into the memory location specified by address. This value becomes

* available through the out output from the next time step onward.

* Address space rules:

* Only the upper 16K+8K+1 words of the Memory chip are used.

* Access to address>0x6000 is invalid. Access to any address in

* the range 0x4000-0x5FFF results in accessing the screen memory

* map. Access to address 0x6000 results in accessing the keyboard

* memory map. The behavior in these addresses is described in the

* Screen and Keyboard chip specifications given in the book.

*/


CHIP Memory {

IN in[16], load, address[15];

OUT out[16];


PARTS:

// Put your code here:

RAM16K(in=in, load=RAMLoad, address=address[0..13], out=RAMOut);

Screen(in=in, load=ScreenLoad, address=address[0..12], out=ScreenOut);

Keyboard(out=KeyboardOut);


DMux4Way(in=load ,sel=address[13..14] ,a=RAMLoadA ,b=RAMLoadB ,c=ScreenLoad);

Or(a=RAMLoadA, b=RAMLoadB, out=RAMLoad);

Mux4Way16(a= RAMOut ,b=RAMOut ,c=ScreenOut ,d=KeyboardOut ,sel=address[13..14] ,out=out );

}


Resultados obtenidos

Por medio del simulador de hardware ejecutamos el chip "Memory.hdl" y podemos observar que al ingresarle el dato 19, decirle que lo cargue y lo guarde en la dirección 19, logra ejecutarse de manera correcta, almacenando el 19 en la dirección 19.

CPU

La unidad central de procesamiento (CPU) se construyó de acuerdo con la implementación dada en la siguiente figura,

usando los chips ALU y Register construidos en los proyectos 2 y 3, además de los ARegister y DRegister los cuales tienen la misma funcionalidad que el chip Register del proyecto 3.

CPU.hdl


// This file is part of www.nand2tetris.org

// and the book "The Elements of Computing Systems"

// by Nisan and Schocken, MIT Press.

// File name: projects/05/CPU.hdl


/**

* The Hack CPU (Central Processing unit), consisting of an ALU,

* two registers named A and D, and a program counter named PC.

* The CPU is designed to fetch and execute instructions written in

* the Hack machine language. In particular, functions as follows:

* Executes the inputted instruction according to the Hack machine

* language specification. The D and A in the language specification

* refer to CPU-resident registers, while M refers to the external

* memory location addressed by A, i.e. to Memory[A]. The inM input

* holds the value of this location. If the current instruction needs

* to write a value to M, the value is placed in outM, the address

* of the target location is placed in the addressM output, and the

* writeM control bit is asserted. (When writeM==0, any value may

* appear in outM). The outM and writeM outputs are combinational:

* they are affected instantaneously by the execution of the current

* instruction. The addressM and pc outputs are clocked: although they

* are affected by the execution of the current instruction, they commit

* to their new values only in the next time step. If reset==1 then the

* CPU jumps to address 0 (i.e. pc is set to 0 in next time step) rather

* than to the address resulting from executing the current instruction.

*/


CHIP CPU {


IN inM[16], // M value input (M = contents of RAM[A])

instruction[16], // Instruction for execution

reset; // Signals whether to re-start the current

// program (reset==1) or continue executing

// the current program (reset==0).


OUT outM[16], // M value output

writeM, // Write to M?

addressM[15], // Address in data memory (of M)

pc[15]; // address of next instruction


PARTS:

// Put your code here:

Not(in=instruction[15],out=ni);

Mux16(a=outtM,b=instruction,sel=ni,out=i);


Or(a=ni,b=instruction[5],out=intoA);

ARegister(in=i,load=intoA,out=A,out[0..14]=addressM);


And(a=instruction[15],b=instruction[12],out=AorM);

Mux16(a=A,b=inM,sel=AorM,out=AM);

ALU(x=D,y=AM,zx=instruction[11],nx=instruction[10],zy=instruction[9],ny=instruction[8],f=instruction[7],no=instruction[6],out=outtM,out=outM,zr=zr,ng=ng);


And(a=instruction[15],b=instruction[4],out=intoD);

DRegister(in=outtM,load=intoD,out=D);


And(a=instruction[15],b=instruction[3],out=writeM);


Not(in=ng,out=pos);

Not(in=zr,out=nzr);

And(a=instruction[15],b=instruction[0],out=jgt);

And(a=pos,b=nzr,out=posnzr);

And(a=jgt,b=posnzr,out=ld1);


And(a=instruction[15],b=instruction[1],out=jeq);

And(a=jeq,b=zr,out=ld2);


And(a=instruction[15],b=instruction[2],out=jlt);

And(a=jlt,b=ng,out=ld3);


Or(a=ld1,b=ld2,out=ldt);

Or(a=ld3,b=ldt,out=ld);


PC(in=A,load=ld,inc=true,reset=reset,out[0..14]=pc);

}

Resultados obtenidos

En el simulador de hardware ejecutamos el archivo "CPU.hdl" y obtenemos que el pc va a buscar una instrucción. Al momento de darle 1 a reset, el pc se resetea. Y por último, cuando ingresamos 0 nuevamente en reset el pc va a volver a buscar una instrucción.

COMPUTER

El chip de computer se realizó utilizando tres partes de chips construidos anteriormente: una CPU, una memoria de datos y una memoria de instrucciones llamada ROM32K.

Computer.hdl


// This file is part of www.nand2tetris.org

// and the book "The Elements of Computing Systems"

// by Nisan and Schocken, MIT Press.

// File name: projects/05/Computer.hdl


/**

* The HACK computer, including CPU, ROM and RAM.

* When reset is 0, the program stored in the computer's ROM executes.

* When reset is 1, the execution of the program restarts.

* Thus, to start a program's execution, reset must be pushed "up" (1)

* and "down" (0). From this point onward the user is at the mercy of

* the software. In particular, depending on the program's code, the

* screen may show some output and the user may be able to interact

* with the computer via the keyboard.

*/


CHIP Computer {


IN reset;


PARTS:

// Put your code here:

ROM32K(address=pc,out=instruction);


CPU(inM=inM,instruction=instruction,reset=reset,outM=outM,writeM=writeM,addressM=addressM,pc=pc);


Memory(in=outM,load=writeM,address=addressM,out=inM);

}


Resultados obtenidos

Gracias al simulador de hardware ejecutamos el archivo "Computer.hdl" y ejecutamos el archivo "Add.hack" que nos va a permitir sumar dos números, en este caso, primero carga el número 2, seguido carga el número 3 y nos dice que el resultado es 5, y por último lo almacena en la memoria en la posición 0.

ADD

El add suma las dos constantes 2 y 3 y escribe el resultado en RAM[0]

Add.hack


0000000000000010

1110110000010000

0000000000000011

1110000010010000

0000000000000000

1110001100001000


Resultados obtenidos

Gracias al simulador de hardware ejecutamos el archivo "Computer.hdl" y ejecutamos el archivo "ComputerAdd-external.tst" el cual nos permite realizar la respectiva operación.

MAX

El max calcula el máximo de RAM[0] y RAM[1] y escribe el resultado en RAM[2].

Max.hack


0000000000000000

1111110000010000

0000000000000001

1111010011010000

0000000000001010

1110001100000001

0000000000000001

1111110000010000

0000000000001100

1110101010000111

0000000000000000

1111110000010000

0000000000000010

1110001100001000

0000000000001110

1110101010000111


Resultados obtenidos

Gracias al simulador de hardware ejecutamos el archivo "Computer.hdl" y ejecutamos el archivo "ComputerMax-external.tst", siendo posible calcular el resultado deseado.

RECT

El rect dibuja en la pantalla un rectángulo de las filas de RAM[0] de 16 píxeles cada uno.

Rect.hack


0000000000000000

1111110000010000

0000000000010111

1110001100000110

0000000000010000

1110001100001000

0100000000000000

1110110000010000

0000000000010001

1110001100001000

0000000000010001

1111110000100000

1110111010001000

0000000000010001

1111110000010000

0000000000100000

1110000010010000

0000000000010001

1110001100001000

0000000000010000

1111110010011000

0000000000001010

1110001100000001

0000000000010111

1110101010000111


Resultados obtenidos

Gracias al simulador de hardware ejecutamos el archivo "Computer.hdl" y ejecutamos el archivo "ComputerRect-external.tst", en el cual se logra observar la figura dibujada, en este caso, el rectángulo.