Instrucciones para el examen

- Complete la implementación del módulo stack de la P1 en m16ex.circ
- Haga la interfaz para stack, pedida en P2.a, en el módulo cpu de m16ex.circ
- Programe en assembler de M16 las funciones push y pop en el archivo stack.c
  (pedido en P2.c).
- Compile con: make
- Cargue test-stack.rom en la ROM del módulo cpu de m16ex.circ.
- Ejecute con control-R control-K
- En la consola debe aparecer:
  Debe mostrar 2 5 4 3 1
  2 5 4 3 1
  ...
- Configure el debugger para detener la ejecución en push, pop, etc.

-----------------------------------------------------------

Este README.txt describe los archivos contenidos en este directorio y
explica cómo usar el debugger de M16.

Circuitos:

- m16beta<n>.circ: el circuito con la versión n de la cpu m16.
  Para probarla necesita lanzar logisim.  Luego resetee la simulación
  con control-R y active el reloj con control-K.
  El programa que está cargado en la ROM es fact.c.  Calcula el factorial
  de un número.

Estos son las ROMs que se pueden generar con: make sizemem.rom (por ejemplo)

- term.rom: otro programa minimal que hace echo en la pantalla de
  todo lo que se escriba en el teclado.
- rev.rom: la ROM del programa que invierte los dígitos del número
  ingresado.
- hello.rom: la ROM de un programa que muestra hello world! usando
  la función printStr
- hello2.rom: lo mismo que hello, pero para generar printStr usa
  la función printStrGen incluida en asm.c.  En asm.c también
  hay generadores para las funciones readInt, printInt y getchar.
  A futuro debería incluir en asm.c más generadores de funciones útiles.
- fact.rom: calcula el factorial de múltiples números ingresados por
  pantalla.  Se usa una función recursiva.
- sizemem.rom: determina los rangos de direcciones en donde hay RAM.
- test-ldwpp.rom: test de la instrucción experimental ldwpp (ldw++).
  Las instrucciones experimentales son solo para propósitos docentes.
  No se supone que se recomiende su inclusión en una arquitectura RISC real.

Programas:

- rev.c, count-hex.c, term.c, hello.c hello2.c, fact.c, sizemem.c, test-ldwpp:
  son los programas que generan las ROM de los programas.
  Cada instrucción de máquina se genera invocando
  una función.  Por ejemplo para generar la instrucción stw se
  invoca la función stw().  Leer el programa es similar a tratar
  de entender un programa en assembler.

  Para generar una rom, por ejemplo sizemem.rom, ejecute el comando:
        make sizemem.rom
  Se genera un listing con el contenido en hexadecimal de la ROM
  junto con su ubicación en hexadecimal y la instrucción almacenada
  en formato assembler de M16.
  Para ejecutar el programa hay que cargarlo primero en la memoria ROM
  del módulo CPU, luego control-R para resetear la simulación y control-K
  para activar el reloj.

  Para agregar por ejemplo un nuevo programa, por ejemplo mcd.c, agregue
  estas líneas al archivo Makefile:

#
mcd: mcd.o asm.o
mcd.o: asm.h inst.h
mcd.rom: mcd
	./mcd mcd.rom

Antes de ./mcd mcd.rom debe ir un tab, no 8 espacios en blanco.

Microcodigo ubicado en la unidad de control

- ucode.rom: Contiene el microcódigo de las señales de
  control para M16.  Esta ROM es generada por el programa
  ctlsigen.c.  Si hace modificaciones en ctlsigen.c, para agregar por
  ejemplo una nueva instrucción, genere el nuevo ucode.rom con:
      make ucode.rom
  El archivo ucode.rom debe ser cargado en la ROM ubicada en la unidad
  de control (módulo cu2). El programa ctlsigen.c genera un listing
  de la ubicación de cada u-instrucción en la ROM.

Otros

- Makefile: para compilar todos estos programas ejecute el comando make.
- asm.h y asm.c: Implementa las funciones que generan las instrucciones
  de máquina.  Además incluye funciones de biblioteca como printStr,
  printInt, getchar y readInt.
- inst.h: contiene las definiciones de los códigos de operación de las
  instrucciones de M16.  Es generado por ctlsigen.c.

Depuración de programas en M16
------------------------------

La CPU incluye un proto debugger muy simple, pero muy poderoso: permite
establecer watchpoints de lectura y/o escritura.  Un watchpoint de lectura
detiene la ejecución cuando la CPU lee la dirección especificada en la
entrada rdwatch del debugger.  Un watchpoint de escritura detiene la
ejecución cuando la CPU escribe en la dirección especificada en la entrada
wrwatch del debugger.  El debugger solo detiene la ejecución si la entrada
cont es 0.  Por lo tanto si se desea continuar con la ejecución hay que
cambiar esa entrada a 1.  La detención se logra llevando a 0 la señal CLK
que alimenta la CPU.

Junto al uso del debugger es importantísimo tener a mano el listing de la
generación de la ROM de un programa.  Por ejemplo, un fragmento del listing
al generar fact.rom es el código de la función recursiva fact:

            fact:
f030: ec30      bg  R1, 0, f038   (reccase)
0032: f038
f034: 0111      mov 1, R1
f036: 0080      ret
            reccase:
f038: 17f4      sub R7, 4, R7
f03a: 88e1      stw R0, R7[1]
f03c: 89e0      stw R1, R7[0]
f03e: 1131      sub R1, 1, R1
f040: 0060      call f030   (fact)
0042: f030
f044: 72e0      ldw R7[0], R2
f046: 5924      mul R1, R2, R1
f048: 70e1      ldw R7[1], R0
f04a: 0ff4      add R7, 4, R7
f04c: 0080      ret

La primera instrucción es:

f030: ec30      bg  R1, 0, f038   (reccase)

El f030 indica en qué dirección se ubica bg en la ROM.  ec30 es el contenido
de esa dirección en la ROM y corresponde a los primeros 2 bytes de la
codificación de la instrucción bg.  A continuación viene la representanción
en assembler, que es mucho más legible que simplemente ec30 f038.  Cuando
una instrucción referencia una etiqueta (label), se anota al lado el
nombre de la etiqueta (reccase en el ejemplo).  Cuando la ubicación de una
instrucción en la ROM corresponde a una etiqueta como f030 y f038, se
antepone una línea con el nombre de la etiqueta (fact: y recase: en el
ejemplo).

Entonces, supongamos que queremos detener la ejecución en la instrucción
call f030, que se ubica en la dirección f040 y llama a la función fact.
Entonces se define como read watchpoint (rdwatch) la dirección f030.
Si la ejecución pasa por esa instrucción tendrá que leerla de la memoria,
y el debugger detendrá la ejecución.

Para analizar detenidamente la ejecución a partir de ese instante, detenga
la simulación con control-K, coloque un 1 en la entrada cont y luego
simule ciclo por ciclo con control-T.

Los watchpoints también funcionan para direcciones que corresponden a datos
del programa.
