La serie “El computador mágico” está disponible también en forma de libro. |
En el último capítulo de la serie vimos el “hola mundo” de los programas para el C16A. Hoy vamos a profundizar un poco en él, haciendo un programa un poco más complejo: vamos a hacer un temporizador como el que hicimos con lógica secuencial.
Como antes, vamos a pegar a continuación el diseño del ordenador y el juego de instrucciones:
Mnemotécnico | ¿Qué hace? |
NOP | No Operation. Operación que no hace nada. |
ST <addr> | Almacena (del inglés store) el contenido del acumulador en la posición de memoria <addr>. addr es del inglés address, dirección. |
LD <addr> | Carga (del inglés load) en el acumulador el contenido de la posición de memoria <addr>. |
ADD <addr> | Suma (del inglés add) el contenido del acumulador con el contenido de la posición de memoria <addr>, y deja el resultado en el acumulador. |
BR <addr> | Salta (del inglés branch) la ejecución a la posición <addr>. E decir, en vez de continuar ejecutando en PC+1, continúa ejecutando en la posición de memoria <addr> |
BZ <addr> | Del inglés branch if zero, salta si es cero. Es decir, lo mismo que BR, pero solo si el contenido del acumulador es un 0. |
CLR | Limpia (del inglés clear) el acumulador. Es decir, pone un 0 en él. |
DEC | Decrementa (del inglés decrease) el acumulador y deja el resultado en el propio acumulador. |
Recuerda también que estamos usando lenguaje ensamblador para escribir nuestro programa, de modo que lo que escribamos resulte legible con relativa facilidad.
Entonces, como decíamos, queremos hacer un programa que espere 3 segundos y luego escriba una “A” en pantalla . Desgraciadamente, para que la espera fuera de 3 segundos tendríamos que introducir muchas cosas que aún no hemos visto, de modo que tendremos que simplificarlo un poco: vamos a esperar 3000 “ciclos”. Veremos luego cómo ajustarlo para que realmente sean 3 segundos y veremos qué fácil es.
¿Quieres intentar hacer tú el programa? Si sabes programar, te resultará trivial. Si no, dale una pensada antes de seguir.
…
¿Ya?
El programa que lo logra es el siguiente:
0×000 | LD /TIEMPO | |
/ESPERA | DEC | # Espera durante TIEMPO ciclos |
BRZ /FIN | ||
BR /ESPERA | ||
/FIN | LD /LETRA | # Escribe LETRA… |
ST /PANTALLA1 | # … en PANTALLA1 | |
/HALT | NOP | |
BR /HALT | ||
/TIEMPO | 3000 | # Tiempo de espera |
/LETRA | ‘A’ | # Letra para el aviso |
… | ||
/PANTALLA1 0×800 | 0×0000 | # Zona de I/O |
Supongo que, como el programa es sencillito, lo entiendes sin problemas. Al empezar, carga en el acumulador el valor que hay en /TIEMPO (es decir, 3000). Luego lo decrementa, por lo que ya vale 2999. Fíjate ahora en el truco: el BRZ de la 3ª línea dice que, si el acumulador es 0, salte a la línea /FIN. Pero el acumulador no es 0, sino 2999, con lo cual no salta, y continúa por la línea 4ª. Pero la línea 4ª es un salto a /ESPERA… pues nada, allá que se va. Es decir, hemos vuelto atrás.
Por lo tanto, volverá a decrementar el acumulador, que pasará a valer 2998. Obviamente, volverá a pasar por el BRZ, y como sigue sin ser 0, volverá a continuar por el BR, que volverá a saltar a /ESPERA… así una y otra vez… ¿cuántas veces? Pues 3000 veces. Porque cuando haya pasado por ahí 3000 veces, esta vez sí, el acumulador valdrá 0, y cuando pase por el BRZ /FIN, como el acumulador sí que será 0, entonces saltará a /FIN. ¿Entendido?
Ya solo queda que, cuando llegue ahí, cargue en el acumulador lo que hay en la posición /LETRA, que es una ‘A’, y lo escribe en la posición /PANTALLA1, que es nuestra pantalla. Finalmente, entra en el mismo bucle infinito que veíamos en el capítulo anterior.
Fácil, ¿verdad?
Bueno, pues hemos conseguido lo que queríamos. Con un pequeño detalle: no sabemos cuánto va a tardar esto en realidad. Pero es más fácil de lo que parece, y nos sirve para demostrar la potencia de esta aproximación. Ahora vamos y encendemos el ordenador, y con un cronómetro medimos el tiempo que transcurre desde que encendemos hasta que se escribe la “A” en la pantalla. Supongamos, por ejemplo, que hemos medido 15s. Entonces ya sabemos que con 3000 “ciclos”, tenemos 15s. Con una simple regla de 3 sabemos que cada segundo son 200 ciclos.
¿Qué debemos hacer ahora para que nuestro diseño de verdad espere 3s? Muy sencillo: cambiamos el programa por el siguiente:
0×000 | LD /TIEMPO | |
/ESPERA | DEC | # Espera durante TIEMPO ciclos |
BRZ /FIN | ||
BR /ESPERA | ||
/FIN | LD /LETRA | # Escribe LETRA… |
ST /PANTALLA1 | # … en PANTALLA1 | |
/HALT | NOP | |
BR /HALT | ||
/TIEMPO | 600 | # Tiempo de espera |
/LETRA | ‘A’ | # Letra para el aviso |
… | ||
/PANTALLA1 0×800 | 0×0000 | # Zona de I/O |
Fíjate en que el único cambio es que en vez de poner 3000 en /TIEMPO, ahora pone 600.
¡Tachán! Ahora ya espera 3s.
¿Y si queremos que, en vez de 3s, espere 11s? Pues lo mismo: ponemos 2200 en la posición /TIEMPO y volvemos a encenderlo.
Date cuenta de la diferencia enorme que esto supone respecto al circuito secuencial que veíamos anteriormente. En aquel momento diseñamos con mucho cuidado un circuito muy sencillo que contaba hasta 3. Pero si queríamos contar hasta 11, por ejemplo, teníamos que cambiar el circuito de arriba a abajo: teníamos que añadir más biestables, cambiar la lógica de realimentación, cambiar la etapa de salida… en fin, había que rehacer el circuito por completo. Ahora, en cambio, tenemos un sistema que es muuuuuucho más complejo inicialmente. Date cuenta también de que ahora tenemos una ALU, una unidad de control, memoria… es mucho más complejo. Pero, a cambio, modificar el tiempo de espera es trivial. Solo hemos tenido que cambiar el valor inicial de una de las palabras de la memoria. Y no solo eso, sino que ahora podemos hacer programas mucho más complejos.
Vamos con un ejemplo. Ten presente que también tenemos un teclado… pues vamos a aprovecharlo. Lo que vamos a hacer es un programa que haga lo siguiente: al arrancar, que no haga nada. Cuando pulse una de las teclas, esperaremos tantos segundos como corresponda al valor decimal de la tecla pulsada y luego mostraremos la tecla pulsada. Por ejemplo, si el usuario pulsa la tecla “F”, esperaremos 70 segundos y luego mostraremos una “F” en pantalla. Si en cambio pulsa la “b”, esperaremos 98 segundos y luego mostraremos la “b” (recuerda que los caracteres de control los pintaremos simplemente como un cuadrado ). Vamos a recordar aquí el teclado para que no tengas que estar yendo a buscarlo.
Bueno, pues el programa que logra eso es el siguiente:
/TECLA 0×000 | LD /TECLADO | |
BRZ /TECLA | # Espero a que se pulse una tecla | |
ST /PULSADA | # Me guardo la tecla pulsada | |
ST /CONTADOR | ||
/MULTIPLICACION | LD /CONTADOR | # Bucle multiplicador PULSADA*SEGUNDO |
BRZ /FINMULTIPLICACION | ||
DEC | ||
ST /CONTADOR | ||
LD /SUMATORIO | ||
ADD /SEGUNDO | ||
ST /SUMATORIO | ||
BR /MULTIPLICACION | ||
/FINMULTIPLICACION | LD /SUMATORIO | # Al llegar aqui, SUMATORIO=PULSADA*SEGUNDO |
/ESPERA | DEC | # Espera durante SUMATORIO ciclos |
BRZ /FINESPERA | ||
BR /ESPERA | ||
/FINESPERA 0×010 | LD /PULSADA | # Al llegar aqui, han pasado PULSADA segundos |
ST /PANTALLA1 | ||
/HALT | NOP | # Fin |
BR /HALT | ||
/SEGUNDO | 200 | # Un segundo (los ciclos que tarda un segundo) |
/PULSADA | 0×0000 | # Donde guardare la tecla pulsada |
/SUMATORIO | 0×0000 | # Donde ire calculando la multiplicacion |
/CONTADOR | 0×0000 | # Donde ire almacenando el contador de la multiplicacion |
… | ||
/PANTALLA1 0×800 | 0×0000 | # Pantalla |
… | ||
/TECLADO 0×820 | 0×0000 | # Teclado |
… |
Si nunca has programado, es probable que entender esto te resulte complicado, y tengas que darle un par de vueltas. Lo que vamos a hacer es un programa asequible y voy a ayudarte, pero es posible que requiera que lo leas un par de veces hasta que lo entiendas. Haz el esfuerzo, porque una vez que entiendas esto habrás entendido buena parte del trabajo técnico de los programadores.
Empezamos casi por el final, definiendo las constantes y variables que vamos a usar. Definimos que /SEGUNDO es 200, porque ya hemos medido antes que con 200 ciclos gastamos 1 segundo. Definimos también /PULSADA, /SUMATORIO y /CONTADOR para almacenar variables: en /PULSADA guardaremos la tecla que se ha pulsado. /SUMATORIO y /CONTADOR los usaremos luego para unos bucles… luego los explicamos. /PANTALLA1 y /TECLADO son, como antes, para leer del teclado y escribir en la pantalla.
Bueno, pues empecemos a ejecutar por el principio. Las dos primeras líneas lo que hacen es leer /TECLADO, y si es un 0, volver a la línea 0×000, donde vuelve a leerlo… una y otra vez… hasta que /TECLADO no sea un 0. Si te acuerdas, el teclado empezaba con un 0, y se rellenaba luego el valor de la tecla pulsada. Así que lo que estamos haciendo aquí es esperar a que el usuario pulse una tecla. Cuando la pulse, ya no habrá un 0, y entonces seguirá por la línea 0×002. En las líneas 0×002 y 0×003 simplemente se guarda la tecla pulsada tanto en /PULSADA como en /CONTADOR.
Las líneas desde /MULTIPLICACION hasta /FINMULTIPLICACION, ¿qué hacen? Muy sencillo: sabemos que multiplicar 3*6 es sumar 3+3+3+3+3+3, ¿verdad? Pues eso es lo que hace: si ya he pasado /CONTADOR veces por el bucle, salgo del bucle hacia /FINMULTIPLICACION; y si no, decremento /CONTADOR en uno, para que a la siguiente pasada pueda volver a comprobarlo (líneas 0×004 a 0×007). Dentro del bucle, sumo /SUMATORIO + /SEGUNDO y guardo el resultado en /SUMATORIO (líneas 0×008 a 0x00A). Finalmente, en la 0x00B salto de vuelta a /MULTIPLICACION, para cerrar el bucle.
Es decir, lo que haré es /SUMATORIO = /SEGUNDO + /SEGUNDO +/ SEGUNDO + /SEGUNDO +… ¿cuántas veces? Tantas veces como valiera /CONTADOR inicialmente… es decir, tantas como el valor numérico de la tecla que haya pulsado.
El caso es que antes o después el BRZ de la línea 0×005 saltará a /FINMULTIPLICACION… y en ese momento /SUMATORIO contiene el resultado de la multiplicación. Lo que hay entre la 0x00C y la 0x00F es lo mismo que antes: una espera durante /SUMATORIO ciclos. Es lo mismo que vimos en el primer programa de esta capítulo, así que nos saltamos la explicación.
Finalmente, cuando llega a /FINESPERA, han pasado los segundos acordados,[1] así que pasamos el valor de /PULSADA (que, te recuerdo, contenía la tecla que habíamos leído del teclado en la línea 0×000) a /PANTALLA1, saliendo por la pantalla. Luego ya solo queda el bucle de fin que hemos usado siempre.
Vamos a repasarlo, de forma un poco más abstracta, a ver si te ayuda a entenderlo:
- Leemos del teclado hasta que lo que leamos sea distinto de 0.
- Guardamos el resultado de la tecla pulsada.
- Multiplicamos el valor de la tecla pulsada por el valor de /SEGUNDO, para saber cuántos ciclos debemos esperar. Pero como nuestra ALU no multiplica, solo suma, tenemos que implementar la multiplicación como sumar nveces:
- Si ya he sumado esas n veces, he terminado y salgo del bucle.
- Si no, añado /SEGUNDO al resultado y decremento en 1 el contador para que se vuelva a comprobar en el siguiente paso por el bucle.
- /SUMATORIO tiene el número ciclos que debo esperar. Así que hago una espera como la del primer programa de este capítulo.
- Finalmente, copio la tecla pulsada (que me la había guardado) en la pantalla y entro en la espera de final de programa.
Lo que hemos hecho aquí no está exento de problemas, pero eso lo veremos en próximos artículos. Dedícale una segunda lectura a estos párrafos, porque si no has programado nunca es normal que te resulte un poco complicado de entender.
En el próximo capítulo ampliaremos nuestro ordenador C16A para que haga alguna cosa más, y lo llamaremos C16B.
- Date cuenta de que hemos despreciado el tiempo que se tarda en calcular la multiplicación, y que eso podría no ser despreciable. Podríamos arreglarlo, simplemente estimando cuánto se tarda en hacer la multiplicación y restándolo antes de hacer la espera. Como lo importante aquí no es si yo sé programar o no, sino cómo funciona la máquina, dejamos eso como ejercicio, si queréis. Como pista, puedes darte cuenta de que el tiempo que se tarda en hacer la multiplicación es lineal con el valor numérico de la tecla pulsada, y eso también es lineal, por requisito, con el tiempo que queremos esperar. En fin, no sigo, que si no lo resuelvo entero. [↩]
The Computador mágico XXIV – Ordenador C16A IV: temporizador by , unless otherwise expressly stated, is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 2.5 Spain License.
{ 1 } Comentarios
Magnífico, J. Es bonito volver a recordar estas cosas. Yo estudié ingeniería industrial. Cuando las estudiaba, recuerdo que me parecía un poco duro, y era por el estrés de tener que estudiar muchas cosas a la vez. Todas las asignaturas. Ahora, relajadamente, se disfruta mucho más, desde luego.
Recuerdo una vez, hace mucho tiempo que mi profesor de historia nos dijo: “Ahora no os gusta estudiar historia, pero cuando seáis mayores, veréis cómo disfrutáis leyendo libros de historia”. Y tenía razón. Pero claro, ¡cuánto más se puede disfrutar leyendo un libro de electrónica ¿no?!
Ánimo con la serie. Es fantástica
Escribe un comentario