viernes, 16 de septiembre de 2016

Assembler: ¿y esto con qué se come?

En los lejanos '90 Calamaro decía "¿de qué hablamos cuando hablamos de amor?", hoy podría quasi-citarlo: ¿de qué hablamos cuando hablamos de assembler? 
El Assembly o Assembler es el lenguaje de programación más cercano al CPU. Cada instrucción tiene una equivalencia -en general 1:1- con un código binario que CPU comprende. Esto significa que es el lenguaje del procesador, un idioma bastante ajeno a los seres humamos normales.
A diferencia de los lenguajes de programación de alto nivel, que se han construido con términos del lenguaje cotidiano (tal vez en inglés, pero bueno, cotidiano para quienes hablan ese idioma), el assembly se compone de las instrucciones que entiende el procesador. ¿Cuáles serán? Generalmente tareas muy sencillas y puntuales que involucren operaciones aritméticas, lógicas, de carga o almacenamiento en memoria, manejo binario, control de flujo, etc.
Resulta más sencillo comprender la programación de bajo nivel si entendemos cómo funciona el procesador. He aquí que a diferencia del alto nivel en que el hardware en que se ejecutará el programa tiene poca o ninguna importancia para el programador (en términos generales obviamente), cuando desarrollamos en bajo nivel estamos trabajando para un procesador o familia de procesadores específica. Esto significa que debemos conocer el procesador, al menos la parte que el fabricante quiso que conozcamos, lo que normalmente denominamos la arquitectura de programación: set de instrucciones, modos de direccionamiento, registros accesibles (dedicados o no), espacio de direccionamiento, etc.
Un programa en bajo nivel puede apoyarse en rutinas preexistentes del BIOS (Basic Input Output System) de la computadora, o incluso en algunas provistas por el sistema operativo. Sin embargo, dada la característica del acceso directo al hardware, Assembler es el único lenguaje de programación en el que podemos prescindir por completo de cualquier plataforma y operar en forma directa con el hardware. Hacerlo así convierte cada tarea rutinaria en alto nivel en un desafío artesanal considerable, por ello verán que los algoritmos de este blog en general se enfocan en un problema sencillo y no invertimos esfuerzos en hacer una entrada o salida elegante de la información. Cuando queremos cargar variables o ver el resultado de un programa, siempre podemos usar el simulador y "mirar" la memoria.
El micro en el que centraremos los esfuerzos es el Motorola 68HC11, así que conozcámoslo un poco:
  • Cuenta con dos acumuladores o registros de propósito general de 8 bits denominados A y B
  • Brinda la posibilidad de usar los dos registros A y B como un solo registro de 16 bits denominado D (tener en cuenta que D no es un registro independiente, sino solo la agrupación de A y B, donde A es la parte alta de D y B es la parte baja de D)
  • Un bus de direcciones de 16 bits, por lo tanto 2^16 direcciones de memoria de 8 bits, un total de 65536 (64K) direcciones. Sin embargo no estarán todas implementadas.
  • Entrada/Salida mapeada en memoria (estructura de 3 buses), lo que significa que un intervalo de direcciones de memoria realmente no son tales, sino interfaces de E/S.
  • Cuenta con una palabra de estado de 8 bits, que podremos utilizar para tomar decisiones condicionales, cuyos flags indicarán si la última operación arrojó acarreo (Carry), acarreo entre el cuarto y quinto bit (Half carry), desbordamiento (oVerflow), fue negativa (Negative) o cero (Zero), además de permitirnos algunas cosillas respecto a las interrupciones que veremos más adelante.
  • Modos de direccionamiento: inmediato (relativo al PC, el operando se indica a continuación de la instrucción), indexado (cuenta con dos índices de 16 bits: IX e IY, este es el modo más práctico para recorrer vectores), directo (paginado pero limitado a la página cero, de hecho no hay registro de página para escoger otra), relativo (utilizado para los saltos), inherente (los operandos están en registros), extendido (se indica la DRO: dirección real del operando en 16 bits).
  • Manejo de pila mediante un registro puntero de pila (SP: Stack Pointer). La pila puede ubicarse en cualquier lugar de la memoria, pero obviamente debe ubicarse en un área RW. Crecerá hacia las direcciones de memoria inferiores a la indicada en el SP.
Aquí lo vemos sonriendo para la foto:
Esta belleza es el modelo A1FN del HC11, incluye 256 bytes de memoria R/W, 512 de EEPROM y 8 Kbytes de ROM. Existen diversos modelos, cada uno con distintas configuraciones de memoria y características particulares.

Un concepto básico de la programación es la ejecución en secuencia de las instrucciones que componen el programa. En otras palabras, luego de cada línea se ejecuta la siguiente, a menos que... lo que se haya ejecutado altere esa secuencia. Conociendo el funcionamiento de una CPU vemos la relación de esto con el PC (Program Counter) y su incansable incremento. En Assembly la siguiente instrucción a ejecutar será siempre la apuntada por el PC. Cuando querramos generar bucles o sentencias condicionales tendremos que generar estos comportamientos en base a comparaciones y saltos (go to) condicionales.
Los programas en assembler se almacenan como texto simple (plain text in english, puedo discutir varias horas respecto a que plain significa simple en este contexto y no plano), lo que significa que podemos abrirlo con cualquier editor de texto (desde el insípido Notepad hasta algo más profesional como el Vim o Notepad++).
Al programar en alto nivel no nos preocupamos por muchas cosas que en bajo nivel necesitan más atención. Tal es el caso de la ubicación en memoria del programa. En alto nivel no nos preocupa porque es tarea del sistema operativo cargar el programa en memoria (lo que incluye encontrar dónde cargarlo o avisar al usuario que compre más memoria o cierre programas si no lo puede lograr). Sin embargo aquí no hay sistema operativo... será nuestra tarea determinar dónde se debe cargar. Dado que trabajaremos en un entorno simulado, siempre ubicaremos el programa en un área de memoria de solo lectura (ROM), manteniendo por separado las variables en un área de lectura-escritura (RWM). Siendo que no todo el espacio direccionable está implementado -ni siquiera en el entorno simulado- deberemos prestar atención a los rangos de memoria disponibles.
Contaremos con un puñado de directivas al ensamblador: sentencias que no serán ejecutadas por el procesador sino que le indicarán al ensamblador -en nuestro caso el incluido en THRSim- por ejemplo dónde ubicar cada porción de programa para asegurarnos de que vaya al tipo de memoria que queremos. Otras directivas se pueden emplear para generar valores constantes, sean escalares o vectoriales. Incluso podremos reservar memoria, en la aproximación más asintótica que hallaremos al uso de variables en la aventura con el HC11.
Los programas tienen una estructura de cuatro columnas, que podemos generar con espacios o tabulaciones de forma indistinta. Las columnas contienen:
Sin embargo estas columnas son meramente lógicas: no espere verlas delimitadas como si de una planilla o tabla se tratara. Note que todo lo que escribamos a continuación de los operandos se considerará comentario, por tanto será ignorado por el ensamblador. No hay necesidad de utilizar un carácter especial para indicar el comienzo del comentario o su final, aunque a algunos se les escapa el // y no hay nada malo en eso. Tampoco se requiere el uso de punto y coma para delimitar sentencias. No se usan llaves para los bloques, de hecho no hay bloques en el sentido pleno de la palabra, aunque podremos generarlos mediante el control de flujo.
Asimismo es posible generar comentarios de línea completa iniciando con el caracter * (asterisco), lo que le dirá al ensamblador que ignore esa línea.
En una siguiente entrega veremos un programa sencillo en el que intentaremos distinguir algunos de los conceptos aquí vertidos.
Una aclaración final: lo expuesto aquí aplica al HC11, un microcontrolador que no está pensado en emplearse con un sistema operativo dada su simpleza y el tipo de implementación en que se utiliza. Cuando se trabaja en assembler de otros procesadores es posible utilizar rutinas del BIOS y/o del sistema operativo para simplificar algunas de las tareas tales como la entrada/salida.

11 comentarios:

  1. Hola profe tengo un problema, nada me funciona en el THSim11
    no se si es porque no lo entiendo bien o que pasa pero lo configure según el tutorial y nada. La pregunta es si cada vez que lo uso o tengo que configurar(configurar la memoria digo)

    ResponderBorrar
    Respuestas
    1. Hola Lore! No entres en pánico! Si la parte ejecutable del programa empieza en C000, entonces no tenes que tocar nada.

      Borrar
  2. Hicimos un trabajo en el aula, vah! Lo hizo ud. y lo pase tal cual y me tira 6 errores que ni se de que se tratan. El trabajo dice Se conoce la dir inicial y final, de un vector, contar los ceros en una posición llamada cantC. Bueno profe lo probé y nada , me tira error.

    ResponderBorrar
    Respuestas
    1. Lore! todavia no tengo poderes! si te da error, entonces decime cual es el error! ¿cargaste un juego de prueba para simularlo?

      Borrar
    2. A veces cuando tira muchos errores suele ser por problema en la tabulación. Asegurate de tener el formato de cuatro columnas como en la foto que esta mas arriba

      Borrar
  3. Otra cosa, baje los trabajos de asm del grupo , los ejecuté y nada DISASSEMBLER TOTALLLL, cuando le cambio el PC a la dir que pide el programa DISASSEMBLERRR POR TWOOO TOTALLLL!!!!!!
    O SEA, NO SE QUE LE PASA! jaja gracias profe !!!

    ResponderBorrar
    Respuestas
    1. Tomate un tilo y proba de nuevo. Asegurate que la seccion ejecutable empiece en C000 (no en 8000 a menos que reconfigures la memoria). Asegurate de cargar un juego de prueba. Contame que errores te da y lo sacamos adelante.

      Borrar
  4. ok profe gracis ya me pongo con eso y le digo que pasa

    ResponderBorrar
  5. listo profe! assembler no es lo mio!

    ResponderBorrar
  6. entretenido profe, se nota sus ganas. saludos

    ResponderBorrar
    Respuestas
    1. Gracias.
      Donde dijiste "ganas" yo leo "desórdenes mentales".

      Jaír

      Borrar

Aunque no es obligatorio, es de buena educación firmar los comentarios.