viernes, 27 de enero de 2012

Curso de Ensamblador para C64: Cap. 2 - Arquitectura del sistema

Para programar el Commodore 64 en lenguaje ensamblador es preciso que conozcamos, aunque sólo sea de forma elemental, las interioridades que lo componen por dentro.



Y conforme más vayamos conociendo la arquitectura interna del C64, haremos cada vez cosas más y más complejas...

Pero antes de empezar a desentrañar las entrañas del pobre 64, me gustaria clarificar qué significa exactamente el lenguaje ensamblador (o los lenguajes ensambladores), ya que el curso va de eso precisamente, y es bueno clarificar conceptos desde un buen principio.

Cuando encendemos el ordenador, nos aparece la típica pantalla azul con la conocida pantalla de bienvenida de Commodore informándonos, entre otras cosas, que en la memoria se halla cargado el intérprete Basic. Tras el "Ready", un incansable cursor parpadea sin cesar, indicándonos que el ordenador esta listo para recibir nuestras instrucciones.



En realidad nuestro C64 no entiende el lenguaje Basic. Dicho lenguaje, al igual que muchos otros de alto nivel (categorizados asi por su alejamiento del lenguaje de la máquina) fue creado por seres humanos para que otros seres humanos se comunicasen más fácilmente con la máquina, en un lenguaje muy similar al inglés.

Si no entiende el lenguaje Basic, ¿como es que parece entendernos sin problemas cuando le escribimos sentencias en dicho lenguaje?

El C64, de forma nativa sólo entiende el lenguaje máquina (también llamado código máquina) para el que fue diseñado y es en dicho lenguaje en el que el intéprete Basic debe "hablarle" al ordenador. Asi es, Basic no hace más que traducir instrucciones Basic en instruciones en lenguaje máquina, perfectamente inteligibles para nuestro c64. Es por eso que el ordenador parece entender Basic, pero en realidad no es así.

Dicho de otra forma, Basic no es más que un programa escrito en lenguaje máquina para traducir sus propias instrucciones (en Basic) de modo que el ordenador pueda ejecutarlas (en código máquina).

Dicho todo esto ahora puedo continuar mi explicación acerca de que es en realidad el lenguaje ensamblador.

Debe quedar bien claro antes de continuar, que aunque se parezcan, no es lo mismo el lenguaje ensamblador que el lenguaje máquina.

El lenguaje máquina se compone de una serie de números (unos y ceros agrupados en grupos de 8, llamados bytes). Cada agrupación de esos números representa un código de operación, una instrucción o un operando sobre los que el componente encargado de ejecutar los programas (en la CPU) tiene muy claro lo que hay que hacer con esos números. Es el propio lenguaje nativo del c64.
El lenguaje ensamblador es un lenguaje de bajo nivel que nos ayudará a traducir nuestro programa al lenguaje máquina, sin que tengamos que memorizar o consultar en tablas, que número hay que poner para decir tal o cual instrucción para hacer tal o cual operación.

Todo esto lo veremos más adelante, a lo largo del curso con abundante detalle, incluida una sección donde aprenderemos a escribir directamente en código máquina (sin ensamblador).

El lenguaje máquina está fuertemente relacionado sobre la propia arquitectura de la máquina, por lo que, como apuntaba al inicio del capítulo, antes de conocer el propio lenguaje máquina o tambiém más tarde el propio lenguaje ensamblador, es de vital importancia primero aprender como está constituida, almenos básicamente, la arquitectura del sistema. Algo que seguramente resultara más sencillo para los usuarios de toda la vida de un c64, ya que su Basic nos ha estado acercando a las interioridades de la máquina, quizá más de lo que deberia haber hecho, con sus obligados PEEKS y POKES para realizar numerosas tareas (como cambiar simplemente el borde de la pantalla)...

Arquitectura del Commodore 64



A todas las partes que componen el ordenador, en su conjunto se les denomina sistema, por ello me refiero a este apartado de esta manera en el título del capítulo para hacer referencia a la arquitectura del c64. Cada componente del sistema recibe el nombre de subsistema. En el sistema se distinguen tres apartados básicos:

1. La CPU (Unidad central de proceso) o procesador del sistema
2. La Memoria (RAM y ROM)
3. Los Periféricos E/S


Este es un esquema común prácticamente a todos los ordenadores, no sólo es exclusivo del Commodore 64.

Todos los subsistemas nombrados, se comunican entre sí a través de buses. Los buses conectan cada subsistema uno con otro de manera que puedan comunicarse entre sí.

Pensad en los buses como "autobuses" (de ahi viene su nombre en inglés) que transportan datos de un lugar a otro. Concretamente el esquema hace referencia a las conexiones a nivel electrónico entre el hardware del ordenador.

He aquí un diagrama de bloques donde se puede observar la dependencia y conexión de estos tres subsistemas:



Las flechas indican la dirección. A veces como podéis ver, ésta puede ser unidireccional. Por ejemplo el bus de direcciones no tiene una conexión para enviarle datos directamente a la CPU, pero ésta si que puede enviarle datos a aquella, en todo caso se dice que ambos subsistemas están conectados.

Veamos brevemente uno a uno, los componentes básicos que componen el sistema del C64.

La CPU (o el Procesador)



La Unidad central de proceso es un chip que en el C64 se llama 6510, es una versión levemente modificada del popular 6502 (es totalmente compatible con éste). La CPU es el verdadero corazón del sistema. Realmente es el componente que controla a todos los demás. En él se decodifican y ejecutan todas las instrucciones en código máquina de los programas. Es capaz de almacenar y recoger datos a través de los buses correspondientes y operar con ellos mediante su unidad aritmético lógica (otro componente del chip miniaturizado en su interior). La CPU además de manejar datos de la memoria, puede manejar datos almacenados en registros internos. Dichos registros son una memoria interna (alojados internamente en el chip) de acceso mucho más rápido que un acceso a la memoria convencional, razón de ser de su existencia.



En esta tabla podemos ver todos los registros del 6510, cada celdilla representa 1 bit, y como puede verse todos los registros presentan 8 celdillas (1 byte) con la excepción del registro PC, Contador de Programa, que es el registro de 16 bits que lleva la dirección de memoria del siguiente comando a ejecutar.

Veamos ahora brevemente los demás registros de 8 bits:

El registro acumulador (a) es uno de los registros más usado y esto es debido a que es necesario usarlo en todas las instrucciones que realicen operaciones lógicas o aritméticas.

Los registros de indice x e y, son registros útiles para recorrer (o indexar) tablas de datos (también llamadas matrices, o listas).

El registro PSW (o P), Palabra de estado del Procesador, contiene un byte en el que cada uno de los 8 bits tiene un significado especial para el procesador. Se dice que cada bit es una bandera (de estado) y se activa o desactiva según el resultado de operaciones anteriores. Ya lo veremos con más calma próximamente.

El registro SP (Stack Pointer), es el Puntero de Pila, un registro que guarda la posición (en memoria) de la pila. Una pila es un almacén temporal de datos (de rápido acceso) en la que se pueden almacenar y recuperar el contenido de ciertos registros, dando lugar a interesantes posibilidades muy importantes para la programación, como las subrutinas, pero eso también lo dejaremos para más adelante, de momento sabed que existe un registro con esa funcionalidad.



La Memoria



La memoria de un ordenador, es un lugar donde se almacenan y recuperan datos. Los datos pueden ser cualquier número que quepa dentro de la celdilla de memoria que es capaz de albergar un chip de RAM, en este caso, los números deberán caber en el rango de 8 bits, lo cual nos da la posibilidad de almacenar en cada celda un número comprendido entre 0 y 255.
El c64 tiene instalados varios chips de memoria RAM ("Random access Memory", memoria de acceso aleatorio) y otros de memoria ROM ("Read only memory", memoria de sólo lectura), la RAM puede leerse y escribirse, no asi los datos contenidos en la ROM, que sólo puede leerse. En este último tipo de memoria, se hallan las rutinas fijas del sistema operativo y el programa del intérprete Basic, escritas en código máquina que podremos llamar desde nuestros programas como subrutinas.

Los chips de RAM están interconectados para proporcionar 65536 bytes (64K), el cual es un número de 16 bits. El bus de direcciones del C64 presenta una anchura de 16 bits, es por ello que no se instaló más memoria RAM en el sistema. Sólo podemos acceder 64K, es tambien por ello que el registro contador del programa (PC) también es, ni más ni menos, también un registro de 16 bits.

Sin embargo mediante una técnica conocida como la multiplexación (y el 6510 presenta facilidades para ello) se puede programar la circuiteria interna para acceder a zonas de otros chips de RAM o ROM, teniendo en cuenta esi si, que algunas partes seguirán quedando inaccesibles... ¡nunca podremos acceder a más de 64K!

Es por este motivo que la ROM se encuentra encima de zonas que podrian contener RAM. Y es que la ROM se presenta en unos chips aparte que juntos llegan a ocupar 20K.

Cuando hablamos de la memoria en el C64, es común referirse a ella también por páginas. Una página de memoria equivale a 256 bytes, siendo de especial interés la primera página de memoria, la página cero (direcciones 0-255). Hay numerosas instrucciones en código máquina que operan sobre esa página. Sin ellas no tendria sentido hablar de esta página. Motivo: Los primeros 256 bytes se acceden más rápido que el resto de la memoria ya que las instrucciones asociadas a esta página en particular, ocupan menos memoria al no tener que incluir información extra que una dirección absoluta si que debe aportar.

Las instrucciones de código máquina requieren ciertos ciclos para ser ejecutadas, algunas de ellas si la ejecución tiene lugar en un cambio de una de estas páginas gastará ciclos adicionales, enlenteciendo el programa en ese punto. A veces podremos ignorar estas pequeñas sobrecargas, por ejemplo si la rutina no era relevante para el bucle principal de ejecución, pero en momentos donde la velocidad y fluidez de ejecución sean vitales y necesitemos hasta el último ciclo de procesamiento disponible resulta necesario alinear nuestro código para que las partes problemáticas estén dentro de una misma página.

Más cosas sobre la página cero:



¡La página cero es como tener 256 registros extras! Sin embargo la página cero, es un lugar muy solicitado por el sistema operativo, el intérprete Basic y aplicaciones de otras personas, por lo que tendremos que tener mucho cuidado y no abusar de ese espacio, a no ser que lo tengamos muy claro. La página cero es un mecanismo super útil y lo encontraremos en todos los microprocesadores de la familia 6500. Los amantes de la optimización y la fluidez de ejecución amarán esta singular página.

También existe el concepto de bancos de memoria. El chip gráfico sólo puede ver una porción de memoria de 16K, por ello la memoria se puede ver como una división de 4 segmentos de 16K, cada una de ellas conformando un banco (y se nombran desde el 0 al 3). Para saber en que dirección de memoria comienza cada banco multiplicariamos el número de banco por 16384. Esto es algo que ya vimos en el tutorial acerca de los sprites, en este mismo blog.

En resumen, al encender nuestro c64 disponemos de 64K de RAM menos 20K de ROM que están solapando posiciones de RAM, los cuales podremos rehabilitar como RAM para acceder a todos los 64K de RAM disponibles, si no vamos a utilizar el sistema operativo o el Basic en ROM. Tambien debemos tener en cuenta que no toda la RAM está disponible para usar en nuestros programas: hay partes de la memoria RAM que son utilizadas por el sistema operativo, el generador de caracteres, el BASIC o los subsistemas de E/S (mapeados en la memoria).

También de pasada habéis aprendido que las instrucciones requieren distintos tiempos para ser ejecutadas (ciclos), y que es conveniente tener las subrutinas importantes alineadas en una misma página para conseguir una buena velocidad de ejecución.

Los Periféricos (E/S ó "Entrada/Salida")



Todos los demás componentes entran dentro de los llamados periféricos o dispositivos de E/S, como el monitor, el teclado, el joystick, el datasette, la unidad de discos... etc.

Estos se conectan mediante interfaces y acceden a los demás subsistemas según se puede ver en el diagrama. Las interfaces de los componentes de E/S están en la placa madre del ordenador, por ejemplo se cuentan con chips específicos para determinadas tareas como los chips VIC y SID, que controlan todo lo que tenga que ver con la salida por pantalla o por los altavoces respectivamente. Los perifericos se conectan al ordenador mediante los conectores de cartucho, puerto de usuario, el puerto serie, los ports del joystick... que por ejemplo en el caso de estos últimos enlazan con el segundo CIA (Complex interface Adapter), un avanzado chip programable con el que se puede controlar la operatividad de joysticks y paddles, entre otras muchas cosas.

Estos subsistemas de E/S están mapeados en la memoria, de tal forma que para comunicarnos con ellos lo haremos con simples instrucciones de transferencias de memoria. Por ello una buena parte de nuestra memoria está ocupada por direcciones que no debemos usar en nuestro programas. Ya lo veremos detenidamente cuando se comente el mapa de memoria del c64.

Pero todo eso lo dejamos para material avanzado, ya se verá en su momento.

Bueno amigos, en el capitulo de hoy habéis aprendido un montón de cosas. Ahora que ya sabéis que es un registro y tenemos claro que es el lenguaje ensamblador y que es el lenguaje máquina, en el próximo capítulo presentaremos unas pocas instrucciones de código máquina para que empecemos a ensamblar... ¡y comenzaremos haciéndolo a mano!

Que tengáis un buen fin de semana.

15 comentarios:

Anónimo dijo...

Genial :) Espero poder programar con algún emulador una demo.

Lobogris dijo...

Eso estaria muy bien ;) ¡ Ánimo que con ganas y entusiasmo todo se puede !

RuRouNiN dijo...

Vaya trabajazoooooo , seguro que uno que yo me sé está frotandose las manos y esperando los siguientes capitulo :)

Bieno64 dijo...

Muy buen artículo, de nuevo. Esas introducciones hacen que asimilemos los conceptos con mas facilidad..

Lobogris dijo...

Me alegra saber que os está gustando mi trabajo y agradezco vuestro reconocimiento ;)

Aeko dijo...

Te lo estás currando .. :)

climacus dijo...

Muy intetesante. No tardando estoy pensando en dar el salto del spectrum al commodore por cambiar un poco el chip (nunca mejor dicho) y estas cosas me azuzan el gusanillo

Lobogris dijo...

jeje nunca mejor dicho! ¡Bienvenido al 65xx Climacus! Un dia seré yo quien pegue el salto al z80 que tambien me parece apasionante... (y a parte me servirá para explotar el apartado z80 del C128)

Jose Zanni dijo...

Logo, cuando termines este curso podríamos crear un PDF con todos los capítulos! Para eso me ofrezco de ayuda.

Jose Zanni dijo...

Ups... "Logo" = Lobo :D

Lobogris dijo...

[Broma]
¿Como me dijiiisteee? jajajaja che! Locooooo. ay que lapsus, y esos teclados están locos a veces.
[/Broma]

Pues sí, me parece estupendo editarlo en .pdf al acabarlo, varias veces lo he pensado. Te ocuparás de eso entonces... gracias Jose :D

Jose Zanni dijo...

La cantidad de veces que tengo que editar los mensajes :D escribo rápido y no releo :D

Puedes contar conmigo para editar ese PDF.

Lobogris dijo...

jaja, a mi me pasa igual, muchas veces en los foros, verás que edito mucho, es que luego veo fallos garrafales.

Genial, gracias por tu contribución Pedefera :D

uctumi dijo...

Excelente curso hasta donde he podido leer, pero las imágenes no se ven, se podrá arreglar?

Josepzin dijo...

Lamentablemente Lobogris está desaparecido desde hace años, así que no creo que las imagenes reaparezcan... :(

Publicar un comentario