Programación en Lenguaje Ensamblador

-El Verdadero Lenguaje de las Máquinas-

La Porrista de 100 Kilos

–Direccionamiento Base con Indice Escalado y Desplazamiento–

Direccionamiento de memoria. Esta es la parte mas confusa a la hora de programar en Ensamblador. Pues los registros generales no solo sirven para contener bits. También se usan para mover datos entre la memoria y el CPU. Del mismo modo que podemos usar una mano para sostener algo o podemos señalar con el dedo. Sin embargo, un CPU de Intel es capaz de combinar sus registros generales de forma que puede guardar y recuperar datos de las estructuras mas extrañas con gran eficiencia.

Esto es muy similar a esas pirámides humanas que hacen las porristas de los eventos deportivos. Donde las mas fuertes se apoyan en cuatro en el suelo. Las de mayor equilibrio se paran sobre sus espaldas y la mas agil de todas trepa hasta la punta y salta realizando una vistosa pirueta en el aire…

Bueno. Como ya me está comenzando a sangrar la nariz mejor le sigo con el Ensamblador. Los registros del CPU pueden ser usados para ‘apuntar’ a la memoria. Quienes han programado en C de seguro escucharon alguna vez el concepto de ‘punteros‘. Los punteros son variables que contienen posiciones de memoria (que al fin y al cabo siguen siendo bits). Un CPU de Intel puede hacer aritmética de punteros ¡Por Hardware!. Aunque puede verse como algo muy complicado. Solo consiste de 3 partes: Base, indice y desplazamiento. Aunque estos nombres son los oficiales de Intel no necesariamente son los mas acertados y veremos porqué.

BASE.- Es el registro sobre el que se apoya el índice. En la pirámide, sería la porrista que está enmedio. Su contenido puede ser cambiado y con ello se pueden lograr direccionamientos relativos. Esto es lo que hace funcionar las variables locales en un Stack Frame.

INDICE.- Es el que está hasta el final. Se apoya sobre el registro base. La porrista que está en la punta de la pirámide y hace las acrobacias. Su contenido puede cambiarse y avanzar por las partes mas recónditas de las estructuras.

DESPLAZAMIENTO.- Es la parte constante del direccionamiento. Se trata de un número que puede ser una posición absoluta de la memoria o relativa a otro registro. Sin embargo, su valor no puede cambiarse sin cambiar el código máquina de la instrucción. El mejor uso de esta parte es para apuntar a posiciones fijas de la memoria como son las variables globales o el principio de un gran arreglo de estructuras. Por analogía con las porristas. El desplazamiento sería: ¡La Infame Porrista de Cien Kilos! Quien por su peso no puede moverse ni mucho menos pararse sobre la pirámide. Por seguridad, la posición que le toca a esta gorda es la de soporte de la piramide.

Un direccionamiento completo con estos tres elementos se vería así:

                        MOV    EAX, [EBX + ESI + 1234h]

Esta instrucción suma el contenido de los registros EAX, ESI y el valor 1234 hexadecimal. Esto da un número que corresponde a una posición de memoria. Entonces, el valor de 32 bits almacenado en esa posición de memoria se almacena en el registro EAX. Pero aún hay mas. Algo llamado factor escalar.

Como recordarán, el valor de las posiciones de memoria es una cantidad en bytes. Los bytes son celdas de 8 bits. No de 16, 32 ni 64. Solo 8 Entonces, ¿Que pasa cuando tenemos que trabajar con valores que no son de 8 bits? Un programador inexperto y desconfiado usaría operaciones aritméticas como ADD; Uno igual de inexperto pero con mas seguridad en si mismo recurriría a la instrucción SHL. Pero alguien que realmente separ de programación sabe que puede usar factores escalares. Un factor escalar en un direccionamiento de memoria multiplica por 2 (16 bits), 4(32 bits) u 8 (64 bits) única y exclusivamente al registro índice. De este modo, es posible recorrer todos los valores internos de una estructura.

De nuevo, con el ejemplo. El valor índice marca que tan alto es el salto que debe de dar la porrista que está hasta arriba de la pirámide.

Ahora al código máquina:

Esto parece demasiado complicado. Sin embargo. Cuando vemos el formato del código máquina todo se aclara. Y aquí es donde entra el byte SIB. Que es el acrónimo de Scalar Index Base. He aquí los bits:

Bits[7:6].- 2 bits que indican el factor escalar. 00 es 0; 01 es 2; 10 es 4 y 11 es 8. Para quien le entienda a las matemáticas, estos son las potencias de 2: 2 ^ 0= 1; 2^1 = 2; 2^2 = 4 y 2^4 = 8.

Bits [5:3].- 3 bits que indican el registro índice. Se interpretan de manera CASI igual que el campo REG de ModR/M. Pero hay pequeñas diferencias que pueden meternos en lios. Recuerden que solo el Indice se puede escalar. Nótese que ESP no puede ser un Indice escalado.

Bits[2:0].- 3 bits que representan la base. De nuevo se interpreta CASI igual que REG en ModR/M. Sin embargo. La combinación binaria 101 (5 decimal) y que correspondría a ESP, se interpreta diferente dependiendo de los 2 bits [5:7] Del byte ModR/M. Para mas detalles vean el dibujo.

Para terminar, cabe mencionar que los bytes ModR/M y SIB trabajan juntos. La existencia e interpretación de SIB depende directamente de ModR/M y este a su vez del OpCode. Además, algunas instrucciones utilizan una parte de ModR/M como parte del código de operación. Un ejemplo de esto es la instrucción DEC cuyo equivalente en C sería el “- -”. El campo REG del byte ModR/M de esta instrucción siempre es 001. Aunque tiene una forma que solo usa un byte.

Mas adelante explicaré como conectar OpCode, ModR/M y SIB. Pero por ahora, ya tienen suficiente para explorar por cuenta propia el código máquina de un programa para los procesadores de Intel. Y ahí si, si logran comprender, no necesitarán a nadie que les eche porras (espero que los programadores brasileños no se ofendan por esta expresión), y mucho menos a una porrista de cien kilos.

abril 7, 2009 Posted by | Uncategorized | , , , , , | 4 comentarios