Programación en Lenguaje Ensamblador

-El Verdadero Lenguaje de las Máquinas-

IF, FOR y Registro EFLAGS

–Como Hacer Comparaciones en Ensamblador–

Recientemente eché un vistazo a mi circo de pulgas y vi como un pobre ingenuo que se les acercó pidiendo consejo para aprender a programar se le dijo de una manera humillantemente amable que primero aprendiera a programar antes de tratar de unirse a ellos. Y que en su página tenían una sección de programación en C – – donde había ejercicios a nivel ‘Universitario’ (mis huevos). Me divierte ver como estas pulgas, aún con títulos universitarios, trabajos bien remunerados y una envidiable vida social (de esto daré pruebas en otra nota) no han podido resolver un sencillo problema de intersección de sprites ni aún con ayuda de una biblioteca que ya lo hace por ellos.

Bueno, esto viene al tema porque para quienes han estudiado programación de seguro alguna vez escucharon algo como: “El flujo de un lenguaje de programación es secuencial, condicional y cíclico.” O para resumir esto en 2 palabras: IF y FOR.

Un gran dolor de cabeza para quienes se inician en el Ensamblador es precisamente lo complicado que resulta hacer programación condicional y cíclica. Sobre todo si quieren hacerla como en lenguajes como Basic o C. La lógica condicional del Ensamblador es por completo diferente a estos lenguajes. Pero antes de continuar veamos un importante elemento del CPU, el Registro Banderas.

Conocido como FLAGS en su época de 16 bits. EFLAGS es un registro del mismo ancho que los registros generales, 32 bits. Pero su contenido se actualiza de manera automática dependiendo de lo que pase durante la ejecución del programa.


Eflags

En este dibujo vemos que el registro EFLAGS es de 32 bits. Abajo hay una descripción de lo que hace cada uno:

Bits RESERVADOS (BLANCOS).- Estos bits no están definidos, no debemos confiarnos en su valor y si llegamos a cambiarlos es importante dejarlos como estaban. Se supone que los nuevos procesadores tendrán funciones habilitadas en ellos.

Bits de SISTEMA (AZULES).- Estos bits indican condiciones de operación del CPU y del sistema operativo. Como la capacidad de identificación, el modo de operación actual, etc.

Bits de STATUS (ROJOS).- Estos son los que nos interesan. Cada que ciertas operaciones dan un resultado, afectan estas banderas, de acuerdo con su estado la computadora es capaz de tomar decisiones.

Bit de CONTROL (VERDE).- El único bit de control es el de dirección. Dependiendo de su estado es como van a operar las operaciones de manejo de cadenas como MOVS, STOS, LODS, ETC.

Expliquemos un poco este asunto del registro banderas: Cada que ocurre un determinado evento este registro pone en cero o uno cada uno de sus bits para indicarnos la naturaleza de un resultado. Por ejemplo, si restamos dos números y el resultado es cero, la bandera ZERO se va a activar. Si el resultado es diferente de cero, el bit correspondiente a la bandera ZERO va a apagarse. Lo mismo se aplica a todas las demás banderas. Por cierto, a diferencia de los lenguajes como C, no siempre es necesario realizar una comprobación directa, pues las propias instrucciones actualizan el registro EFLAGS automáticamente cuando son ejecutadas. El si una instrucción afecta o no al registro EFLAGS y como lo hace está plenamente documentado en los manuales de Intel.

Cabe aclarar que no es tan facil acceder al contenido de este registro como lo hacemos a los registros generales. En lugar de eso hay instrucciones que activan y desactivan banderas de manera individual, por ejemplo, STC y CLC activan y desactivan respectivamente la bandera de acarreo. También hay instrucciones condicionales cuyo comportamiento depende del estado de las banderas de EFLAGS. Como por ejemplo CMOV que es como un MOV normal pero puede o no ejecutarse dependiendo del estado de las banderas. O SET que pone en cero o uno un registro o celda de memoria basándose en el estado de una determinada bandera. Pero la instrucción que mas depende de este registro es sin lugar a dudas el salto condicional.

El salto condicional.- Esto es hasta ahora lo mas parecido al IF que tiene el Ensamblador. Cuando el CPU llega a un salto condicional revisa la condición determinada por la instrucción y la compara con el estado del registro banderas. El salto se efectúa solo si la condición se cumple. Esta lógica es inversa al IF tradicional en que un grupo de instrucciones se ejecuta cuando la condición se cumple. Otra desventaja aparente que tiene el salto condicional es qe es relativamente pequeño en comparación con la instrucción JMP, pues apenas puede saltar unos cuantos cientos de bytes y estos son relativos a la posición de la instrucción. Para hacer comparaciones complejas se usa una combinación de saltos condicionales, largos y operaciones a nivel de bits que trabajan con el registro banderas.

SUBCODIGO MNEMONICO ESTADO DE BANDERAS
0000 O OF = 1
0001 NO OF = 0
0010 B, NAE CF = 1
0011 NB, AE CF = 0
0100 E, Z ZF = 1
0101 NE, NZ ZF = 0
0110 BE, NA (CF or ZF)= 1
0111 NBE, A (CF or ZF)= 0
1000 S SF = 1
1001 NS SF = 0
1010 P, PE PF = 1
1011 NP, PO PF = 0
1100 L, NGE (SF xor OF) = 1
1101 NL, GE (SF xor OF) = 0
1110 LE, NG ((SF xor OF) or ZF) = 1
1111 NLE, G ((SF xor OF) or ZF) = 0

En esta tabla vemos algunos de esos bits. Por ejemplo. El salto condicional se escribe Jcc. Estas dos cc se sustituyen por letras que indican la condición. Dentro del código máquina las instrucciones condicionales usan 4 bits para evaluar la condición. Uno de esos bits es el ‘bit de negación’. 3 bits indican la condición y el cuarto niega la condición determinada por esos 3 bits: En la tabla la primera columna es el SubCode o Subcódigo Máquina Condicional, la segunda es el subnemónico y la tercera indica el estado de las banderas.

Como me da mucha flojera ordenar los subnemónicos en orden alfabético y poner su traducción al español. Voy a dejarle ahi y seguir con un par de consejos con esto de las banderas y las instrucciones condicionales.

*Supongamos que restamos el contenido de EDX al acumulador EAX. No nos importa el contenido de ninguno de los dos. Si el resultado de esta resta es cero. La bandera ZERO de Eflags se va a activar, Si el resultado es negativo, se activa la de signo SIGN. Si el resultado es divisible entre 2 se activa la bandera de paridad (PF). Si el resultado no puede ser representado como entero con signo, se activa Overflow y si se da el famoso Wrap-Around la de CARRY. Lo interesante es que esto es un proceso automático llevado a cabo por el CPU y no consume mas tiempo que el que toma realizar la propia resta. Sin necesidad de que hagamos una comparación explícita.

*CMP y TEST.- Estas 2 instrucciones se usan para hacer comparaciones explícitas entre 2 valores que pueden ser registros, celdas de memoria o constantes. CMP es una resta ‘de mentiritas’ que no resta al operando destino pero que actualiza las banderas como si lo hubiera hecho. TEST es exactamente lo mismo pero en lugar de restar los operandos, hace un AND entre ellos.

Existen otros ‘trucos’ de viejos programadores de Ensamblador. Como usar OR EAX, 0 para ver si hay un cero en el acumulador. En realidad es mas rápido hacer TEST EAX, EAX. O si se trata de una celda de memoria, TEST [memoria], 0. Y aunque hoy en día la mayoría de estas instrucciones pueden ejecutarse en un ciclo de reloj, Existe algo llamado Paralelismo que permite que mas de una instrucción se ejecute por ciclo de reloj en un procesador de Intel, pero eso es un tema mas avanzado que veremos cuando realmente programemos algo serio.

abril 29, 2009 - Posted by | Uncategorized | , , ,

3 comentarios »

  1. He visto en varios codigos que usen OR EAX,EAX para checar si el valor regresado por llamada a una funcion es cero, personalmente cuando creo algunas rutinas y si requiere regresar verdadero o falso uso la bandera de Carry con STC para verdadero o CLC para falso cuando regresa la funcion solo checo el resultado con JC o JNC,
    No se si estoy bien?
    o que es lo que recomiendan?

    Comentarios por blackpig | abril 30, 2009 | Responder

    • Lo de OR en lugar de CMP era mas eficiente en los tiempos del 386, pues era mucho mas rapido hacer un OR que un CMP (el cual implica un SUB). Hoy en dia, CMP, OR y TEST ocupan un solo ciclo de reloj, o menos si recurres a la truculenta tecnica conocida como RISC LIKE.

      En cuanto a si es mejor regresar el resultado en una bandera, un registro o el Stack, eso depende de con que otros lenguajes estes interactuando. Por ejemplo, las interrupciones del DOS regresaban resultados de acierto/fallo en la bandera CARRY y usaban los registros del CPU para intercambiar informacion. Esto era porque esas interrupciones estaban hechas para usarse en Ensamblador. Mientras que las llamadas al Windows usan solo EAX y el Stack porque es asi como trabaja el lenguaje C. (Y como ya saben los comepinguinos marinela que les gusta Linux, el formato ejecutable ELF de UNIX es el verdadero padre del formato ejecutable PE que usa Windows.
      Para acabar rapido, actualizar banderas es rapido pero solo funciona en Ensamblador, El Stack es mas adaptable a C pero es un poco mas lento. La decision es del sistema con el que trabajes.

      Comentarios por asm86 | May 1, 2009 | Responder

  2. hello

    Comentarios por creestiivo | May 1, 2013 | Responder


Deja un comentario