martes, 24 de mayo de 2016

Desensamblemos: más bajo no podemos caer

Aunque es sabido que las computadoras operan en binario (al menos las que existen masivamente al día de hoy) es raro que uno se tope con unos y ceros en su uso cotidiano. Incluso quienes se dedican a la programación, salvo que operen en el bajo nivel, no suelen palpar mucho este tema. Ahora bien, cuando empezamos a trabajar cada vez más abajo... los unos y ceros nos llegan al cuello.
En otro post explicamos cómo ensamblar un programa, o sea cómo llevar a binario (hexadecimal si lo resumimos de a cuatro bits) lo que programamos en texto. Ahora veremos cómo hacer el paso inverso: partiendo de un programa en binario/hexadecimal, reconstituiremos el fuente en assembler. A esto le llamamos "desensamblar".

Aviso: Si en este punto de su existencia se da cuenta de que su vida social es prácticamente nula (asintóticamente cerca de cero), entonces tenga por seguro que comprenderá este tema y lo dominará sin problemas.

Este tipo de problemas deben plantearse en un contexto. Se nos debe informar el estado de los registros internos de CPU -el PC como mínimo- para saber cómo comenzar el desensamblado. Si acaso se solicita además que indiquemos la evolución de los registros internos o el efecto en una porción de memoria, el estado inicial de los mismos debe conocerse.

Por ejemplo supongamos se nos provee el estado del program counter (PC): $C000 y dos porciones de memoria: 
$C000: 7f 00 03 7f 00 04 de 00 4f e6 00 d3 03 dd 03 08 7a 00 02 26 f3 20 fe
$0000: 00 06 05 01 02 03 04 99 54 65 32
¿Qué hacemos con esto? Empezamos por el dato más importante: el PC. Sabemos que desde la posición de memoria que se indica en él tenemos que buscar el primer código de operación. Aquí toca hacer una búsqueda inversa en el set de instrucciones, en lugar de mirar la columna "mnemónic" buscaremos en la columna Opcode, eso lo encontramos aquí:

 Son dos las cosas que tenemos que utilizar al realizar estas búsquedas inversas:
  • instrucción (mnemonic)
  • modo de direccionamiento empleado (Addressing mode).
La instrucción es CLR y en modo EXTendido. ¿Por qué tan importante? Por el formato del campo de operandos. En este caso es "hh ll", o sea una dirección completa de 16 bits. Junto al código de operación 7f aparecen 00 03. Esos dos bytes componen el operando de 16 bits. 
¡Bien! Hemos logrado desensamblar la primera instrucción, podríamos expresarlo tal como lo hacemos cuando ensamblamos un programa:

$C000 7F 00 03         CLR $0003

por ende la porción de memoria que nos queda por delante es:

$C003: 7f 00 04 de 00 4f e6 00 d3 03 dd 03 08 7a 00 02 26 f3 20 fe

Note que el valor de la dirección en la que inicia "lo que resta" desensamblar la incrementé a $C003, porque la primer instrucción es de 3 bytes. Repetimos el procedimiento, nuevamente el código de operación es 7F, el mismo que antes, así que ya sabemos que se trata de otro CLR y que lo que sigue es la dirección del operando en 16 bits (extendido):

$C000 7F 00 03         CLR $0003
$C003 7f 00 04         CLR $0004

volvemos a expresar lo que nos queda:

$C006: de 00 4f e6 00 d3 03 dd 03 08 7a 00 02 26 f3 20 fe

 Busquemos DE en la columna de "código de operación" del set de instrucciones:

Recuerde los dos datos que buscamos: instrucción -> LDX y modo de direccionamiento -> DIRecto.
Por tanto nos quedamos con el primer byte que sigue al código de operación DE: 00. Ese será el operando en modo directo (o sea la parte baja de la dirección donde la parte alta es la página cero).

$C000 7F 00 03         CLR $0003
$C003 7f 00 04         CLR $0004
$C006 de 00            LDX $00

vamos avanzando, ahora nos queda esto (preste atención al avance de la dirección de inicio del bloque):

$C008: 4f e6 00 d3 03 dd 03 08 7a 00 02 26 f3 20 fe

Repetimos el mismo procedimiento, ahora sigue el código de operación 4F, que corresponde a CLRA. No hay operandos para esta instrucción, ya que solo opera en modo inherente. Eso significa que el byte que sigue también es un código de operación: E6. Este último corresponde a LDAB en modo indexado en IX. 

En este punto un par de detalles importantes:
  • Observe que LDAB en modo IND,X tiene como código de operación E6. Fíjese que en modo IND,Y el código es 18 E6. Es un caso de código de operación de 16 bits. ¡Cuidado! no olvide que el 18  es parte del código de operación, más allá de que aparezca alineado a la izquierda.
  • El modo indexado también tiene operando. Tanto IND,X como IND,Y tienen un operando de 8 bits, representado en el set de instrucciones por las letras ff. Corresponde al offset que se aplica al índice en el cálculo de la dirección real del operando.
Comprendemos entonces que E6 00 equivale a LDAB 0,x. Vayamos completando el programa:

$C000 7F 00 03         CLR $0003
$C003 7f 00 04         CLR $0004
$C006 de 00            LDX $00
$C008 4F               CLRA
$C009 E6 00            LDAB 0,x
$C00B

nos queda la última porción:

$C00B: d3 03 dd 03 08 7a 00 02 26 f3 20 fe

Luego de completar esta etapa del desensamblado todavía tendremos por delante:
  • asignar etiquetas de acuerdo a los saltos que pudieran existir
  • discernir qué hace el programa para en función de ello asignar nombres a las direcciones de memoria de los operandos.
Desafío: complete el desensamblado, trate de avanzar en la consigna final. En el próximo post vemos cómo termina y aprendemos a probar esto en el THRSim.


Si les gusta musicalizar su trabajo, sugiero "El reino del revés" de María Elena Walsh. ¿Alguna otra canción a tono con el desensamblaje?


3 comentarios:

  1. Cualquier música en 8 bits le viene bien a este tema xD

    ResponderBorrar
  2. Tengo una duda, si los códigos de operación son de longitud variable, ¿cómo puedo saber hasta que parte es código de operación y dónde comienzan los operandos o sus referencias durante el proceso de desensamblado?
    Por ejemplo, en el caso de la instrucción LDAB en modo IND,Y podría tener una instrucción 18 E6 00. 18 E6 corresponde al código de operación y el 00 al offset. Pero cómo podría saber que 18 por sí solo no es un código de operación de alguna otra instrucción y que el E6 sea, por ejemplo, la parte baja de un modo de direccionamiento directo? Tengo que revisar uno a uno todos los códigos? Podría darse esta ambigüedad? Gracias!

    ResponderBorrar
    Respuestas
    1. Hola!
      Te propongo lo siguiente: buscá todos los códigos de operación de dos bytes del HC11. Fijate qué tienen en común. Luego volvé a revisar el set de instrucciones y fijate si existe la chance de que "se confunda".
      Luego contanos!
      gracias por pasar
      Jaír

      Borrar

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