Debe introducir al menos 3 caracteres en el buscador.
Inicio / Wikis / Tutoriales / Inline Assembly - How to - Extended Inline Assembly

Inline Assembly - How to - Extended Inline Assembly

 ***** (1 opiniones)
Copyright Tutorial de Martin Candurra - 24 de Febrero de 2006
Temas Relacionados: Lenguaje C
3. Extended Inline Assembly
Con lo que vimos anteriormente no logramos ningún tipo de interacción con los operadores definidos por el resto del código. Es esta versión "extendida" la que nos permitirá pasar valores inmediatos a registros, cargar registros con el contenido de variables, cargar variables con el contenido de registros, etc.

La sintaxis genérica es:

asm ( "instrucciones" : lista_salida : lista_entrada : lista_destruida);



Donde:

lista_salida(o output list): contiene los registros, variables donde se guardara un dato.

lista_entrada (o input list): contiene los registros, variables o datos inmediatos que se utilizaran como entradas en una instrucción.

lista_destruida (o clobber list): contiene los registros que se ven modificados por las "instrucciones". Esta lista es necesaria para que el gcc sepa que registros debe utilizar, cuales debera resguardar, etc.

Si bien esto puede parecer un poco confuso, espero ir evacuando las dudas a través de ejemplos.

Ejemplo 1


int main(void)
{
int i=100;
asm ("movl %0,
eax" : : "g" (i));
}

##

En ese fragmento lo que hacemos no es más que cargar el valor de i en el registro eax. Nótese que al utilizarse la versión "extendida" es necesario agregar otro operador "%" a los registros (Como ya en la versión basica del //Inline Assembly// anteponiamos el operador "%" a los registros, ahora deberemos agregar otro más, con lo que cada registro tendrá un "
" antes de su nombre.

Volviendo al ejemplo anterior, si observamos, el modificador "g" (i) se encuentra en la lista de entrada, mientras que las lista de salida esta vacía al igual que la clobber list.

Podriamos utilizar otro ejemplo levemente mas avanzado, para quien todavía se encuentra perdido.

Ejemplo 2


#define MAX_VALOR 200
int main(void)
{
int i=100;
asm ("movl %0,
eax; movl %1, 
ebx" : : "g" (i) , "g" (MAX_VALOR));
}



Aca nuevamente no utilizamos ni la lista de salida, ni la lista de registros destruidos. Simplemente cargamos en eax y en ebx el valor de i y 200 respectivamente.

Que función cumple el %0 y el %1 ? Estos son los valores que serán reemplazados dinámicamente por Gcc. En caso de que agregaramos más instrucciones con mas parámetros a cargar, continuarán con %2, %3, etc.

Que representa el "g" ? Este modificador le indica a Gcc que el parametro que se pasa entre paréntesis puede ser tanto una variable en memoria como un valor inmediato. Si bien el modificador "g" es uno de los más utilizados (al menos por mi) hay muchos otros. Sólo intentare explicar algunos, pueden consultar el resto en el manual.

Modificadores


A continuación se listan los modificadores más usados en el ensamblado extendido. Existen otros más, incluyendo algunos propios de ciertas arquitecturas.

  1. "a" eax
  2. "b" ebx
  3. "c" ecx
  4. "d" edx
  5. "S" esi
  6. "D" edi
  7. "q" o "r" cualquier registro de propósito general (a conveniencia de gcc)
  8. "I" valor inmediato (entre 0 y 31)

Voy a intentar clarificar un poco esto a través de unos ejemplos. Empecemos por uno sin mucha utilidad práctica, pero que a nuestros fines nos sirve:

Ejemplo 3


int main(void)
{
int i=100;
asm ("mov %0,
ebx" : : "c" (i));
}

##

Aqui el modificador "c" indica al Gcc que el valor de i deberá ser cargado en el registro ECX, el cual será colocado en el lugar de %0. En pocas palabras, en este caso, cargamos el valor de i en EBX, utilizando a ECX como registro de paso.

Pasemos a un ejemplo más real y útil.

===== Ejemplo 4 =====

La siguiente es una implementación de la función memcpy utilizando //inline assembly// (Sacada de ##[[http://routix.sourceforge.net/ Routix]]##).

##void *memcpy( void *dest, const void *src, unsigned int n)
{
    __asm__("cld ; rep ; movsb":  : "c" ((unsigned int) n), "S" (src), "D" (dest));

    return dest;
}

##

Recordemos que la instrucción movsb mueve el contenido de esi a edi, e incrementa o decrementa los valores de ESI y EDI (según el flag de dirección, modificado en este caso por CLD). La instrucción REP hace que se repita esto mientras ECX no sea 0. Tampoco en esta ocasión utilizamos la lista de salida, ni la de registros destruidos. El modificador "c" indica que el registro de destino será ECX, mientras que "S" y "D" lo hacen con ESI y EDI respectivamente.

===== Modificador "r" =====

El modificador "r" como ya comentamos con anterioridad le indica a Gcc que puede utilizar cualquier registro. Esto es útil ya que en muchas oportunidades no es necesario usar un registro en particular, y cualquiera que se utilice puede realizar bien la tarea. Quizá para alguien con no mucha experiencia mezclando código C y assembly le venga bien la siguiente explicación. Como vimos antes, podemos ver el código ensamblador generado por Gcc utilizando la opción -S. Allí pudimos observar que los accesos a variables se realizan utilizando registros de propósito general. Que pasaría si el código en C guardara un valor en EAX para un futuro acceso a una variable y nosotros muy despreocupadamente utilizamos un

##__asm__ ("movl %0, 
cr3" : : "a" (i) )


? Tal cual lo imaginamos, vamos a estar pisando el valor que pensaba utilizar Gcc con lo cual el comportamiento del programa es ahora totalmente impredecible. Es en este (y en otros) casos donde el modificador "r" nos ayuda a evitar esos problemas, ya que deja que Gcc elija un registro que no esta utilizando. Otra forma de evitar este tipo de inconvenientes es usar la denominada "clobber list" (la cual nombre anteriormente como lista de registros destruidos).

Veamos otro ejemplo sacado del código fuente de
Routix, el cual muestra el uso del modificador "r"

#define load_cr3(x) asm ("movl %0,
cr3" : : "r" (x) )
##

Esta macro carga un nuevo directorio de paginas. No importa realmente si usted no sabe que es la paginación, simplemente piense en CR3 como un registro más, el cual no puede ser cargado de forma inmediata (es decir, requiere ser cargado a través de otro registro). Este caso es un perfecto ejemplo el cuál deja a Gcc utilizar el registro de propósito general que tenga disponible.

===== Clobber List =====

Ya hemos dado una explicación de porque es importante incluir los registros que modifica nuestro código en la clobber list, por lo que ahora vamos a apoyar esa teoría con algunos ejemplos:

El siguiente ejemplo esta sacado también del código fuente de ##[[http://routix.sourceforge.net/ Routix]]##. Es la interfaz en modo usuario de la llamada al sistema exec (su comportamiento es ligeramente diferente a la del estandar Unix). Elegí este fragmento de código porque aporta información sobre el uso no solo de la clobber list (o lista de registros destruidos) si no también de la lista de salida.

##int exec (char *tarea)
{
    __asm__ __volatile__ ("movl %0, 
eax ; movl %1,
ebx" :  : "g" \
                (SYS_PROCESS | SYS_EXEC) , "g" (tarea) : "eax" , "ebx");
    __asm__ __volatile__ ("int $0x50");

     int retorno;
    __asm__ __volatile__ ("movl 
eax, %0" : "=g" (retorno) );

return retorno;
}


La primera linea no hace más que cargar en el registro EAX el número de llamada al sistema correspondiente, y en EBX la dirección de un string que posee el path del programa a ejecutar. Lo que aporta de nuevo esta función es que al final incluye un: "eax", "ebx" que corresponde a la clobber list. Con este agregado le estoy diciendo explícitamente a gcc que los registros EAX y EBX estan siendo modificados para que él pueda tomar los recaudos necesarios. (Tanto SYS_PROCESS como SYS_EXEC son macros, las cuales son consideradas como valores inmediatos).

La segunda línea hace pasar al modo kernel vía la interrupción 0x50.

El último fragmento agrega el modificador "=g" en la lista de salida. Hasta ahora habíamos pasado valores de variables a registros, pero nunca en sentido inverso, es en este último caso donde la output list (o lista de salida) entra en juego. La linea

asm volatile ("movl %%eax, %0" : "=g" (retorno) )


mueve el valor del registro EAX a lo que representa el %0, que en este caso es la variable retorno. Es evidente el agregado del signo "=" antes de la "g", el cual es necesario en el modificador usado en la lista de salida.

Veamos algún ejemplo más de la lista de salida.

Este ejemplo es la implementación de la función inportb en
Routix:

unsigned char inportb(word puerto )
{
unsigned char valor;

asm volatile("inb %w1,%b0" : "=a" (valor) : "d" (puerto) );
return valor;
}


Aca podemos ver algunos agregados más. No se si habrán notado que siempre nos manejamos con datos de 4 bytes, es decir con el registro completo. No siempre es necesario esto, en algunos casos alcanza con un word (AX) y en otro incluso con un byte (AL). El caso de la instrucción INB (o IN) es un claro ejemplo. El número de puerto es un entero de 2 bytes, mientras que el valor leído de él es de tan solo 1. El operador %w le dice al gcc que el dato que recibe es un word (2 bytes), mientras que el %b hace lo respectivo a 1 byte.

Si compilamos este ejemplo con gcc -S obtenemos

_inportb:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movw %ax, -2(%ebp)
movw -2(%ebp), %dx
/APP
inb %dx,%al
/NO_APP
movb %al, -3(%ebp)
movl $0, %eax
movb -3(%ebp), %al
leave
ret


donde podemos comprobar que realmente el gcc utiliza los registros parciales DX y AL.
Autor y licencia de 'Inline Assembly - How to - Extended Inline Assembly'
Martin Candurra Extraído de: http://es.tldp.org/Manuales-LuCAS/doc-gcc-inline/doc-gcc-inline-html/ Copyright
Puede ser copiado o reproducido en forma total o parcial, lo único que pido es que se mantenga el copyright del autor, y que se me informe de la utilización, con el fin de que pueda ver el contexto en el que es usado y la utilidad que le encontraron.
Este contenido ha sido recopilado por el equipo de Wikilearning. Todo el contenido recopilado se ha obtenido respetando y comunicando en nuestro site la licencia de cada fuente.
Wikilearning tiene permiso expreso por escrito de los autores para publicar los contenidos que ha extraído de otras webs, incluyendo su uso comercial.

Wikis relacionados con 'Inline Assembly - How to - Extended Inline Assembly'

Según Philip Kotler, la relación comercial puede ser de diferentes tipos. Con la terminología de... Más »
Esta es una traducción al español de los "Uniform Requirements for Manuscripts Submitted to Biomedical... Más »
Este controlador es para la interfaz del adaptador Ethernet Digital ''Tulip'' Debería de trabajar con... Más »
Este tutorial presenta los conceptos básicos de líneas de transmisión (Transmission lines), así como una... Más »
Este tutorial presenta los conceptos básicos de líneas de transmisión (Transmission lines), así como una... Más »
¿Estás seguro de que deseas eliminar este capítulo?