Proyecto para Software de Base

El objetivo del proyecto es la escritura de un ensamblador y un desensamblador simples para un procesador de 8 bits: el 8051 de Intel. El ensamblador se irá construyendo en diversas etapas. Cada etapa estará especificada en esta página y tendrá una fecha de entrega. La evaluación de la etapa se hará en base a que lo entregado satisfaga exactamente las especificaciones requeridas, es decir, ni más ni menos. Finalmente, cada una de las etapas tendrá un valor que será anunciado junto con la especificación.

Primera Etapa : Convertidor de archivo binario a formato objeto hexadecimal : 10 puntos : 16 de junio de 2011.

En el documento Intel Hexadecimal Object File se describen dos tipos de registros del formato objeto hexadecimal de 8 bits, a saber el registro de datos y el registro de fin de archivo. Su tarea será escribir un programa llamado bintohex.c o bintohex.cpp o bintohex.java que transformen un archivo binarios a este formato. El programa bintohex deberá recibir dos parámetros en la línea de instrucciones: fuente y destino (en ese orden), donde fuente es el nombre de un archivo binario existente y destino es el nombre del archivo donde se escribirá el archivo en hexadecimal. Además, podrá recibir un tercer parámetro opcional desplazamiento (que será la dirección de inicio del primer byte del archivo fuente y cuyo valor por omisión será 0x0000) y un cuarto parámetro opcional longitud (cuyo valor por omisión es 0x20). El desplazamiento y la longitud podrán aparecer escritos en binario, cuaternario, octal, decimal o hexadecimal en los formatos descritos aquí. Si no se dan dos parámetros el programa deberá imprimir en stderr el mensaje "uso: bintohex fuente destino [desplazamiento] [longitud]" y terminar. Si el archivo fuente no existe deberá imprimir en stderr el mensaje "error: el archivo fuente no existe" y terminar. Si el archivo destino no se puede crear deberá imprimir en stderr el mensaje "error: el archivo destino no se puede crear" y terminar. Si el desplazamiento no está en el rango 0x0000 a 0xffff o está en formato equivocado deberá imprimir en stderr el mensaje "error: desplazamiento ilegal" y terminar. Si la longitud no está en el rango 0x01 a 0xff o está en formato equivocado deberá imprimir en stderr el mensaje "error: longitud ilegal" y terminar. Si no ocurrió ninguno de estos errores, entonces su programa deberá comenzar a leer bytes del archivo fuente y a escribir registros de datos en el archivo destino. Cada registro de datos debe ser tan largo como sea posible según el valor de longitud (es decir, sólo el último registro de datos puede medir menos que longitud). Al terminar de leer los datos de fuente se deberá escribir un registro de fin de archivo en destino. Si ocurrió algún error al escribir al archivo destino se deberá imprimir en stderr el mensaje "error: no se puede escribir en destino" y terminar. Si todo salió bien, el programa deberá terminar sin emitir ningún mensaje. Ejemplo: Supongamos que quieren ejecutar el programa bintohex con el archivo fuente entrada.txt, el archivo destino salida.txt, el desplazamiento 0x1000 (en hexadecimal) y la longitud 0d10 (en decimal) entonces la línea de instrucción deberá ser bintohex entrada.txt salida.txt 0x1000 0d10 después de lo cual el archivo destino deberá contener salida.txt. Nota: Para ver cómo se le pueden enviar parámetros a un programa en la línea de instrucciones consulte la sección 5.10 del libro El lenguaje de programación C (Segunda edición) de Kernighan y Ritchie. Aquí hay un ejemplo.

Segunda Etapa : Tabla de códigos de operación : 10 puntos : 23 de junio de 2011.

En el documento MCS(R) 51 Microcontroller Family User's Manual aparece el conjunto de instrucciones del 8051 (páginas 2-21 a 2-27). En la página 2-21 aparecen los nueve modos de direccionamiento del 8051, pero sólo trabajaremos con seis de ellos: Rn (por registros), #data (inmediato de 8 bits), #data16 (inmediato de 16 bits), addr16 (directo de 16 bits), rel (relativo) e (implícito). De los 256 posibles códigos de operación, el 8051 sólo usa 255 (el código A5 está reservado) y muchos de los demás no usan ninguno de los seis modos de direccionamiento listados. Genere un archivo de texto ASCII que incluya un renglón por cada uno de los códigos de operación que sí usan uno de los seis modos listados. Cada renglón contendrá cuatro campos separados por espacios: un nemónico, un código de operación, una longitud de instrucción y un modo de direccionamiento. Cada nemónico debe de ser una etiqueta (como se describe aquí) basada en el nombre de la instrucción y el modo de direccionamiento. No debe de haber dos códigos de operación con el mismo nemónico. El código de operación, la longitud de la instrucción y el modo de direccionamiento son tres números escritos en alguno de los formatos descritos aquí. La longitud de la instrucción es el número de bytes de la misma. El modo de direccionamiento debe de ser un número entero como sigue: 0 para los modos implícito y por registros, 1 para el modo inmediato de 8 bits, 2 para el modo inmediato de 16 bits, 3 para el modo directo de 16 bits y 4 para el modo relativo. Aquí hay un ejemplo de cómo se vería la parte de esa tabla que involucra a la instrucción ADD.

Tercera Etapa : Ensamblador de un paso : 20 puntos : 7 de julio de 2011

Deberán escribir un programa de nombre ensambla que reciba dos parámetros en la línea de instrucciones. El primer parámetro fuente será el nombre de un archivo de texto que contiene el código fuente de un programa escrito en ensamblador para el 8051 usando los nemónicos obtenidos en la segunda etapa del proyecto. El segundo parámetro objeto será el nombre de un archivo de texto que contendrá el código objeto correspondiente al programa fuente usando el formato descrito en la primera etapa del proyecto. En esta etapa, el código fuente consistirá de una sucesión de líneas de texto y terminará con el fin de archivo. Cada línea de texto podrá (1) estar en blanco (los definidos por isspace), (2) contener además de blancos el nemónico de una instrucción sin parámetro, (3) contener además de blancos el nemónico de una instrucción separada por blancos de su parámetro numérico, (4) contener además de blancos la pseudoinstrucción ORIGEN separada por blancos de su parámetro numérico, (5) contener además de blancos la pseudoinstrucción BYTE seguida de un parámetro numérico con valor N seguida de N parámetros numéricos separados por espacios o (6) contener además de blancos la pseudoinstrucción END. Los nemónicos deberán ser aquellos definidos en las etapas previas del proyecto y deberán de estar contenidos en una tabla de símbolos implementada como una tabla de dispersión. Los parámetros numéricos podrán estar escritos en cualquiera de los formatos numéricos definidos aquí. El parámetro de la pseudoinstrucción ORIGEN es la dirección de la siguiente instrucción en el código fuente (si éste no comienza con ORIGEN se debe considerar que la primera instrucción tiene dirección 0x0000) y por lo tanto debe de estar en el rango 0x0000 a 0xffff. El primer parámetro de la pseudoinstrucción BYTE deberá ser un parámetro numérico constante N en el rango 0x01 a 0xff, seguido por N parámetros númericos en el rango 0x00 a 0xff. En este caso, el contador de localidades deberá incrementarse en N unidades y el código objeto generado serán los N bytes definidos por los últimos N parámetros de BYTE. La pseudoinstrucción END indica que el código fuente ha terminado y que el resto se debe de ignorar. Los parámetros de las instrucciones deben de estar en los rangos que les correspondan (es decir, en ocasiones deberán ser de 8 bits y en otras ocasiones de 16 bits). Su programa deberá detectar los siguientes tipos de errores, los cuales deberá señalar en el error estándar (stderr) junto con el número y contenido de la línea fuente en que ocurren: (1) instrucción desconocida, (2) falta parámetro, (3) sobra parámetro, (4) parámetro fuera de rango, (5) parámetro sin instrucción y (6) formato desconocido de parámetro. Su programa deberá terminar en cuanto se lea la pseudoinstrucción END o se detecte el fin de archivo o el primer error. Las líneas sin errores en el código fuente que no estén en blanco ni contengan la pseudoinstrucciones ORIGEN o END producirán código objeto. Cada registro de texto del código objeto deberá ser tan largo como sea posible pero nunca deberá tener más de 0x20 bytes de datos (que era el valor por omisión en la primera etapa del proyecto). Cuando se detecte el primer error se deberá imprimir en stderr un mensaje que consiste de la cadena "ERROR ", seguida del número de línea donde se detectó el error, seguido de una de las palabras (INSTRUCCION, FALTA, SOBRA, RANGO, PARAMETRO, FORMATO) según el tipo de error seguido de la línea que contiene el error. Abajo mostramos un ejemplo de funcionamiento de este programa:

Código fuente
Código objeto
Error estándar
ORIGEN 0x1000
ADDR3
ADDIN8 0d32
ADDR0
ORIGEN 0x2000
BYTE 0d3 0b1 0c2 0d3
END
:041000002B24202855
:03200000010203D7
:00000001FF

Cuarta Etapa : Desensamblador : 10 puntos : 21 de julio de 2011

Deberán escribir un programa de nombre desensambla que reciba dos parámetros en la línea de instrucciones. El primer parámetro objeto será el nombre de un archivo de texto que contendrá el código objeto de un programa usando el formato descrito en la primera etapa del proyecto. El segundo parámetro fuente será el nombre de un archivo de texto que contendrá el código fuente de un programa escrito en ensamblador para el 8051 usando los nemónicos obtenidos en la segunda etapa del proyecto y correspondiente al programa objeto. En esta etapa, el código objeto consistirá en una sucesión de líneas de texto y terminará con el fin de archivo. Cada línea de texto podrá contener un registro de datos de cualquier longitud válida o un registro de fin. El código fuente a la salida será una sucesión de líneas de texto la cual podrá (1) contener el nemónico de una instrucción sin parámetro, (2) contener el nemónico de una instrucción separada por un espacio de su parámetro numérico en hexadecimal, (3) contener la pseudoinstrucción ORIGEN separada por un espacio de su parámetro numérico en hexadecimal, (4) contener la pseudoinstrucción BYTE seguida de un parámetro numérico con valor N seguida de N parámetros numéricos separados por espacios y todos ellos en hexadecimal o (5) contener la pseudoinstrucción END. Note que no se permiten líneas en blanco ni tampoco separar los parámetros por más espacios de los estrictamente necesarios. No se permite usar la pseudoinstrucción ORIGEN de forma indiscriminada: los únicos usos permitidos son (1) al principio del código fuente si el primer registro de datos no comienza en la dirección 0x000 y (2) cuando el primer byte de un registro de texto no es el inmediato consecutivo del último byte del registro de texto anterior. Tampoco se permite usar la pseudoinstrucción BYTE de forma indiscriminada: los únicos usos permitidos son (1) cuando se haya leído en el archivo objeto algún byte que no corresponda con el código de operación de alguna instrucción del 8051 y (2) cuando no existan suficientes bytes en el archivo objeto como para terminar una instrucción. Su programa deberá detectar los siguientes tipos de errores, los cuales deberá señalar en el error estándar (stderr) junto con el número y contenido de la línea fuente en que ocurren: (1) formato de registro incorrecto cuando se encuentre un caracter inesperado, (2) suma de control incorrecta y (3) tipo de registro incorrecto si el campo de tipo no es 00 ó 01. Cuando se detecte el primer error se deberá imprimir en stderr un mensaje que consiste de la cadena "ERROR ", seguida del número de línea donde se detectó el error, seguido de una de las palabras (FORMATO, CONTROL, TIPO) según el tipo de error seguido de la línea que contiene el error. Abajo mostramos un ejemplo de funcionamiento de este programa. Observe que el código objeto es el mismo que en el ejemplo de arriba pero el código fuente no. Esto se debe a que el byte 0x01 no corresponde a ninguna instrucción válida (AJMP no lo es en este proyecto) y 0x02 aunque sí corresponde a una instrucción válida (LJMP) necesitaría un parámetro de dos bytes y sólo queda un byte en la entrada. Finalmente, el byte 0x03 sí corresponde con una instrucción válida (RRA) sin parámetros.

Código objeto
Código fuente
Error estándar
:041000002B24202855
:03200000010203D7
:00000001FF
ORIGEN 0x1000
ADDR3
ADDIN8 0x20
ADDR0
ORIGEN 0x2000
BYTE 0x02 0x01 0x02
RRA
END

Última nota

El día de la revisión final del proyecto estén listos a responder preguntas sobre el funcionamiento de sus programas y a hacer pequeñas modificaciones a los mismos. También estén listos para realizar al menos la siguiente prueba: usar su programa bintohex para obtener un código objeto a partir de un archivo cualesquiera, usar su programa desensambla para obtener un código fuente a partir de ese código objeto y luego usar su programa ensambla para obtener a partir de ese código fuente el mismo código objeto que en el primer paso.