Veamos a continuación un segundo programa hipotético elaborado directamente en lenguaje de máquina para nuestra pequeña computadora ficticia, desde la perspectiva de un programa típico almacenado en la memoria RAM de la computadora:
Al empezar la ejecución de este programa almacenado en la memoria RAM, la unidad de control tomando el domicilio 0 indicado por el Contador del Programa (PC) busca (ciclo fetch) la primera instrucción precisamente en el domicilio "0" de la memoria. Esta es una instrucción del tipo 001XXXXXX que ordena copiar la secuencia binaria almacenada en el domicilio 5 (XXXXXX=000101) al acumulador. Con la ejecución de varias microinstrucciones abriendo y cerrando compuertas, esta instrucción es puesta en el Registro de Instrucción (IR) para ser decodificada. Y como el número binario almacenado en el domicilio 5 es el número binario "010000111", o sea el número 135, mediante la ejecución de varias microinstrucciones adicionales que abren y cierran varias compuertas y mueven los datos de un lado a otro (ciclo execute) este número es puesto en el acumulador.
Hecho esto, el Contador del Programa aumenta en conteo binario ascendente a 1, y en vez de apuntar al domicilio 0 ahora apunta al domicilio 1, en donde la segunda instrucción del programa debe ser buscada (ciclo fetch). Esta es una instrucción del tipo 010XXXXXX que ordena sumar la secuencia binaria almacenada en el domicilio 6 (XXXXXX=000110) al número que se encuentra en el acumulador. Con la ejecución de varias microinstrucciones abriendo y cerrando compuertas, esta instrucción es puesta en el Registro de Instrucciones para ser decodificada. Y como el número binario almacenado en el domicilio 6 es el número binario "000100110", o sea el número 38, mediante la ejecución de varias microinstrucciones adicionales que abren y cierran varias compuertas y mueven los datos de un lado a otro (ciclo execute) este número binario es sumado al número que se encuentra en el acumulador, dejando el resultado 173 ("010101101") en el acumulador.
Nuevamente, el Contador del Programa aumenta en conteo binario ascendente, y en vez de apuntar al domicilio 1 ahora apunta al domicilio 2, en donde la tercera instrucción del programa debe ser buscada (ciclo fetch). Esta es una instrucción del tipo 011XXXXXX que ordena copiar la secuencia binaria que se encuentra en el acumulador al domicilio 7 (XXXXXX=000111) de la memoria, el cual anteriormente estaba vacío. Con la ejecución de varias microinstrucciones abriendo y cerrando compuertas, esta tercera instrucción es puesta en el Registro de Instrucciones para ser decodificada. Y como el número que se encuentra en el acumulador es el número binario "010101101", o sea el número 173 (el resultado de la suma), este número es almacenado en el domicilio 7 de la memoria.
Ahora el Contador del Programa aumentando en conteo binario ascendente en vez de apuntar al domicilio 2 apunta al domicilio 3, en donde la cuarta instrucción del programa debe ser buscada. Esta es una instrucción del tipo 100XXXXXX que ordena imprimir la secuencia binaria que se encuentra en el domicilio 7 (XXXXXX=000111) de la memoria. Con la ejecución de varias microinstrucciones abriendo y cerrando compuertas, esta tercera instrucción es puesta en el Registro de Instrucciones para ser decodificada. Y como el número que se encuentra en el domicilio 7 de la memoria es el número binario "010101101", o sea el número 173 (el resultado de la suma), este es el resultado aritmético que es impreso en alguna impresora.
El Contador del Programa aumentando en conteo binario ascendente en vez de apuntar al domicilio 3 apunta ahora al domicilio 4, en donde la quinta instrucción del programa debe ser buscada. Esta es una instrucción del tipo 101XXXXXX que ordena un salto incondicional al domicilio 8 de la memoria (ya que XXXXXX=001000, que equivale al 8 decimal). Esto significa que el contenido del Contador del Programa es borrado por completo y el Contador del Programa que a fin de cuentas no es más que un contador binario ascendente cuyo contenido puede ser "cargado" con cualquier otro número será puesto en el estado "101001000". Aquí es en donde la sexta instrucción deberá ser buscada. Y como la sexta instrucción almacenada en el domicilio 8 de la memoria es una instrucción del tipo 000XXXXXX, o sea una instrucción de ALTO, hemos llegado a la conclusión del programa. Obsérvese que la porción XXXXXX es ignorada, ya que en una instrucción de ALTO resulta completamente irrelevante.
Este segundo programa fue lo suficientemente pequeño para caber en la memoria RAM de nuestra máquina hipotética. El tamaño de la memoria RAM disponible desde siempre ha sido una preocupación y una limitante fundamental para todas las tareas que se quieran llevar a cabo en una computadora, razón por la cual una de las principales prioridades ha sido desde siempre el tratar de aumentar el tamaño de las memorias RAM recurriendo a todas las tecnologías disponibles.
Todos los programas de cómputo trabajan de la misma manera al nivel del lenguaje de máquina a los programas pequeños que acabamos de ver, desde los más sencillos hasta los más complejos.
Refiriéndonos al diagrama de arriba de nuestra pequeña computadora hipotética, repasemos nuevamente con mayor detalle las acciones que se llevan a cabo en nuestra pequeña máquina hipotética conforme se ejecuta el primer programa de cuatro instrucciones dado al principio de este suplemento en lenguaje de máquina. Supóngase que estas cuatro instrucciones han sido puestas en los domicilios de la memoria RAM 1, 2, 3 y 4, respectivamente (en notación binaria, serían los domicilios 000001, 000010, 000011, y 000100). Entonces:
(1) Al iniciar, el número 000001 debe estar puesto en el registro que guarda el "domicilio de la siguiente instrucción", y el flip-flop tiene que estar en el estado "set" con un "1" puesto en la terminal S.
(2) El domicilio 000001 pasa a través de la compuerta hacia el decodificador de domicilios de la memoria RAM, que a su vez encuentra el contenido 001010000 bajo tal domicilio y se prepara para enviar la primera instrucción al registro de instrucciones.
(3) El flip-flop es "reseteado" con un "1" puesto en la terminal R. Los primeros tres bits de la primera instrucción bajada de la memoria RAM, 001, pasan al decodificador de instrucciones que activa la terminal de salida que corresponde a tal instrucción en preparación para un copiado de la memoria. Al mismo tiempo, los últimos seis bits de la instrucción pasan al decodificador de domicilios. Puesto que estos bits representan el número binario 010000, el contenido en el domicilio 16 de la memoria RAM es localizado. La ejecución a partir de este punto es controlada por las microinstrucciones -no visibles al programador- activadas por la instrucción 001, las cuales deben llevar a cabo todas las acciones necesarias para copiar de tal domicilio de la memoria RAM sus contenidos hacia el acumulador. Después de que esto ocurre se debe producir una señal de "ejecución terminada", aumentándose el registro del "domicilio de la siguiente instrucción" en 1, o sea a 000010.
(4) El flip-flop es ajustado de nuevo al estado "set" con un "1" puesto en la terminal S. El domicilio 000010 (domicilio 2) pasa al decodificador de domicilios de la memoria RAM, que a su vez encuentra el contenido 010010011 bajo tal domicilio y se prepara para enviar la segunda instrucción al registro de instrucciones.
(5) El flip-flop es "reseteado" con un "1" puesto en la terminal R. Los primeros tres bits de la segunda instrucción bajada de la memoria RAM, 010, pasan al decodificador de instrucciones que activa la terminal de salida que corresponde a tal instrucción en preparación para una suma de números en el acumulador.
(6) El mismo proceso se repite una y otra vez, en forma casi monótona.
El ejemplo que se acaba de dar podría parecer risible para una aplicación práctica, y puede dar una idea equivocada de que los microprocesadores en realidad son máquinas muy primitivas capaces de hacer muy pocas cosas. Pero con un conjunto ampliado de instrucciones y con la capacidad de poder ejecutar cientos de miles de instrucciones por segundo con la ayuda de una memoria RAM de gran capacidad el microprocesador adquiere entonces su verdadera estatura de un gigante pequeño, capaz de competir en torneos de ajedrez con buenas probabilidades de éxito o capaz de pilotear por sí solo las misiones espaciales tripuladas y no-tripuladas.
Un programa contiene todas las instrucciones y todos los datos necesarios para que un microprocesador pueda llevar a cabo todas las tareas que se le están encomendando. El proceso de elaborar un programa para un microprocesador (o cualquier computadora) se llama programación. Esto es de lo que trata el software, los programas de la máquina, a diferencia del hardware que consiste en la electrónica de la máquina implementada mediante sus circuitos lógicos. Cuando un programa ha sido elaborado y terminado, es puesto dentro de la memoria RAM de la máquina, y de ésta manera puede ser ejecutado por el microprocesador a su propia velocidad sin ser retardado por intervención humana alguna.
Aunque en principio podemos "programar" cualquier microprocesador directamente en su lenguaje de máquina, usando "unos" y "ceros" la desventaja de hacer tal cosa es que en un programa grande que contenga miles de instrucciones es muy fácil para un humano equivocarse escribiendo un "1" en lugar de un "0" o viceversa. Y basta con que en un programa grande haya un sólo "1" en lugar de un "0", ó un solo "0" en lugar de un "1", para "reventar" la ejecución del programa. En este sentido, los microprocesadores y las computadoras son completamente intolerantes; no admiten un solo error. Es por ello que en lugar de las instrucciones
001010000
010010011
011010000
000101101
010010011
011010000
000101101
sería mucho más cómodo y menos susceptible de errores para nosotros los humanos recurrir a la ayuda de algún lenguaje simbólico en el cual escribiríamos algo como lo siguiente:
copiar xEn tal caso, el microprocesador, o mejor dicho, el programa para "ensamblar" una tras otra las instrucciones en el lenguaje binario de "unos" y "ceros" que puede ser entendido por la máquina, el programa ensamblador (assembler) ejecutándose por el mismo microprocesador tendría que ir traduciendo estos caracteres alfabéticos al lenguaje de "unos" y "ceros" de la máquina. Esto requeriría que la máquina tuviera a su disposición en su memoria RAM una "tabla" de conversión como la siguiente:
sumar y
guardar x
alto
000 - altoPero estos son ya detalles "extra" que conciernen a la programación del microprocesador. La arquitectura básica del microprocesador sigue siendo exactamente la misma, no ha cambiado en nada. Corresponde al progrmador saber lo que quiere y puede hacer con el conjunto de instrucciones que la máquina le pone a su disposición. (Véase el "Suplemento 4a: El conjunto de instrucciones del microprocesador 8086".)
001 - copiar
010 - sumar
011 - guardar
100 - imprimir
101 - saltar
110 - comparar
111 - restar
Abandonaremos ahora nuestra pequeña máquina hipotética para adentrarnos en los "intestinos" del microprocesador real. La explicación que se dió nos dará una mejor idea del por qué algunas cosas son requeridas del modo que son requeridas.
Cuando se enciende una computadora (¡cualquier computadora!) que había estado apagada por algún tiempo, todos los registros internos de su microprocesador así como todas las localidades de su memoria RAM contienen una combinación completamente al azar de "unos" y "ceros". No hay absolutamente nada con qué empezar a trabajar, ni instrucciones ni datos. Por ello, la primera prioridad del día es "limpiar" todos los registros internos del microprocesador rellenándolos con "ceros" a través de su terminal de "reajuste" R (reset). Este es precisamente el objetivo de la terminal RESET en todo microprocesador, la cual recibe un "1" sostenido por breve tiempo durante el encendido de la máquina, ya sea de un componente especializado como el circuito integrado MAX809:
o de alguna combinación de circuitos eléctricos implementada por el diseñista. Un registro especial que durante el proceso de inicialización de la máquina puede ser "limpiado" cargándolo con "ceros" o con cierta combinación especial de "unos" y "ceros" es el puntero de instrucciones (instruction pointer ó IP) de la máquina. Esta combinación especial de "unos" y "ceros" es el domicilio en la memoria en donde el microprocesador buscará precisamente la primera instrucción que deberá ser ejecutada, la cual puede estar situada al principio de la memoria (al principio de la memoria RAM en un domicilio como "0000000000000000" ó inclusive hasta el final de la memoria en un domicilio como "FFFF" hexadecimal). Esta primera instrucción le indicará al microprocesador las acciones que deberán llevarse a cabo tras el encendido durante el proceso de inicialización.
Así pues, un microprocesador, por sí solo, no es capaz de hacer absolutamente nada. Se necesita alguna manera de poder meterle, paso a paso, las combinaciones de unos y ceros que vayan a ser procesadas dentro del mismo. Estas combinaciones de unos y ceros consisten tanto en las instrucciones binarias como en los datos binarios que vayan a ser manipulados dentro del microprocesador de acuerdo con las instrucciones que se le dán en lenguaje de máquina. Para que la información pueda ser procesada rápidamente, de manera electrónica, se vuelve deseable tener una memoria externa al microprocesador en la cual se puedan depositar, en forma de palabras binarias de unos y ceros, tanto los datos como las instrucciones. Esta es la memoria RAM (acrónimo de la expresión en inglés Random Access Memory) de la que tanto se habla. De este modo, la operación del microprocesador y la operación de cualquier computadora desde las más pequeñas hasta las más poderosas se puede resumir en una secuencia sencilla de pasos:
(1) Al recibir el primer pulso de la señal de reloj CLK (la misma señal de reloj que vimos en el estudio de circuitos secuenciales como el flip-flop J-K y los contadores binarios), el microprocesador busca en la memoria la primera instrucción que deberá ser ejecutada.
(2) En el siguiente pulso de la señal de reloj o en los siguientes pulsos de la señal de reloj, el microprocesador siguiendo la primera instrucción que ha recogido de la memoria busca en la misma memoria el dato o los datos sobre los cuales se llevará a cabo alguna operación lógica o aritmética.
(3) Siempre bajo la acción secuencial de los pulsos en la señal de reloj, efectuada la operación el microprocesador deposita en la memoria el resultado intermedio o lo mantiene temporalmente en alguno de sus registros internos mientras se prepara para buscar la siguiente instrucción de la memoria RAM.
(4) El microprocesador busca en la memoria la siguiente instrucción que deberá ser ejecutada.
(5) Si la segunda instrucción lo pide, el microprocesador busca en la memoria el dato o los datos sobre los cuales se llevará a cabo algún procesamiento.
(6) Efectuada la segunda operación, el microprocesador deposita el resultado en la memoria o lo mantiene temporalmente en alguno de sus registros internos mientras se prepara para buscar la siguiente instrucción de la memoria RAM.
(7) El proceso se repite mecánicamente de la misma manera, hasta que el microprocesador encuentra una instrucción que le dice que el procesamiento ha terminado.
No hay comentarios:
Publicar un comentario