sábado, 4 de marzo de 2017

Manual de Instrucciones de ensamblador, dentro de lenguaje C

ADVERTENCIA

Debo advertir al lector sobre este documento,  la traducción NO ha sido revisada por los autores originales, esta hecha basándose en el traductor de Google, y con algunas modificaciones hechas por el autor de este blog, y ya que tanto mi ingles, como mi conocimiento de lenguaje C no es muy amplio, algunas palabras y/o sentencias están fueras de mi entendimiento por lo que la traducción puede ser mala en algunas partes, y aceptable en otras, aún así bajo esta advertencia, espero que al lector, le pueda servir como base para entender el uso instrucciones de ensamblador dentro de lenguaje C, escrito en idioma Español. 

La version original del idioma Ingles puede ser vista en:


Inline Assembler Cookbook (traducción)


Manual para  insertar codigo ensamblador dentro del código C de
AVR-GCC
Recopilador en línea del ensamblador

Acerca de este documento

El compilador C de GNU para los procesadores Atmel AVR RISC ofrece, para incrustar código de lenguaje ensamblador en programas C. Esta es una característica genial puede utilizarse para optimizar manualmente las partes críticas, del tiempo en ejecución de un software, o para utilizar instrucciones específicas del procesador, que no están disponibles en el lenguaje C.

Debido a la falta de documentación, especialmente para la versión AVR del compilador, y que puede tomar algún tiempo para averiguar los detalles de la implementación,  al estudiar  el código fuente del compilador y del ensamblador. Debido a que hay algunos pocos programas de ejemplo disponibles en la red. Esperemos que este documento ayude a aumentar su número.

Este manual supone que usted está familiarizado con la escritura en ensamblador de programas AVR , ya que este no es un tutorial de programación de ensamblador AVR. Y tampoco es un tutorial sobre lenguaje C.

Tenga en cuenta que este documento no cubre el archivo escrito completamente en lenguaje ensamblador, consulte avr-libc y los programas de ensamblador para esto.

Copyright (C) 2001-2002 por egnite Software GmbH

Permission is granted to copy and distribute verbatim copies of this manual provided that the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.

Se concede permiso para copiar y distribuir copias literales de este manual siempre y cuando el aviso de copyright y este aviso de permiso se conserven en todas las copias. Se concede permiso para copiar y distribuir versiones modificadas de este manual, siempre que el trabajo derivado resultante sea distribuido bajo los términos de un aviso de permiso idéntico a éste.

Este documento describe la versión 3.3 del compilador. Puede haber algunas partes, que no habían sido completamente entendidas por el propio autor y no todas las muestras habían sido probadas hasta el momento. Debido a que el autor es alemán y no está familiarizado con el idioma Inglés, definitivamente hay algunos errores de ortografía y sintaxis en el texto. Como programador, el autor sabe que una documentación errónea a veces puede ser peor que ninguna. De todos modos, decidió ofrecer su pequeño conocimiento al público, con la esperanza de obtener suficiente respuesta para mejorar este documento. No dude en ponerse en contacto con el autor por correo electrónico. Para la última revisión, consulte  http://www.ethernut.de/.

Herne, 17 de mayo de 2002 Harald Kipp harald.kipp-at-egnite.de

Nota
A partir del 26 de julio de 2002, este documento se ha fusionado en la documentación de avr-libc. La última versión ya está disponible en http://savannah.nongnu.org/projects/avr-libc/

Declaración de asm en GCC

Comencemos con un ejemplo simple, la lectura del valor que hay en el puerto D:

asm("in %0, %1" : "=r" (valor) : "I" (_SFR_IO_ADDR(PORTD)) );


Cada declaración asm se divide por dos puntos en (hasta) cuatro partes:



1. Las instrucciones del ensamblador, son definidas como una constante de cadena única: 

               "in %0, %1"

2. Una lista de operandos de salida, separados por comas. Nuestro ejemplo utiliza sólo uno:

               "= R" (valor)

3. Una lista separada por comas de operandos de entrada. Una vez más nuestro ejemplo utiliza un solo operando:

             "I" (_SFR_IO_ADDR (PORTD))
4. Registros desvencijados, dejados vacíos en nuestro ejemplo.

Puede escribir instrucciones de ensamblador de la misma manera que escribiría programas de ensamblador. Sin embargo, los registros y las constantes se utilizan de una manera diferente si se refieren a las expresiones de su programa C. La conexión entre los registros y los operandos C se especifica en la segunda y tercera parte de la instrucción asm, la lista de operandos de entrada y salida, respectivamente. La forma general es

asm(código : lista de operandos de salida : lista de operandos de entrada [: lista  clobber]);

En la sección de código, los operandos son referenciados por un signo de porcentaje seguido de un solo dígito. % 0 se refiere al primer operando, % 1 al segundo operando y así sucesivamente. Del ejemplo anterior:
%0 se refiere a "=r" (valor) y
%1 se refiere a "I" (_SFR_IO_ADDR(PORTD)).

Esto todavía puede parecer un poco extraño ahora, pero la sintaxis de una lista de operandos se explicará pronto. Examinemos primero la parte de una lista de compiladores que puede haber sido generada a partir de nuestro ejemplo:
lds r24,valor
/* #APP */
in r24, 12/*
 #NOAPP */
sts valor,r24

Los comentarios han sido agregados por el compilador para informar al ensamblador que el código incluido no fue generado por la compilación de las sentencias C, sino por las instrucciones del ensamblador en línea. El compilador seleccionó el registro r24 para el almacenamiento del valor leído de PORTD. Aunque el compilador podría haber seleccionado cualquier otro registro. Puede no explícitamente cargar o almacenar el valor, e incluso puede decidir no incluir su código de ensamblador en absoluto. Todas estas decisiones forman parte de la estrategia de optimización del compilador. Por ejemplo, si nunca usa el valor de la variable en la parte restante del programa C, el compilador probablemente eliminará su código a menos que haya desactivado la optimización. Para evitar esto, puede agregar el atributo volatile a la instrucción asm:

asm volatile("in %0, %1" : "=r" (valor) : "I" (_SFR_IO_ADDR(PORTD)));

Alternativamente, se pueden dar nombres a los operandos. El nombre se adjunta entre paréntesis a las restricciones de la lista de operandos y las referencias al operando nombrado usan el nombre entre corchetes en lugar de un número después del signo%. Por lo tanto, el ejemplo anterior también podría escribirse como
asm("in %[retval], %[puerto]" :
[retval] "=r" (valor) :
[puerto] "I" (_SFR_IO_ADDR(PORTD)) );

La última parte de la instrucción asm, la lista clobber, se utiliza principalmente para informar al compilador acerca de las modificaciones realizadas por el código del ensamblador. Esta parte puede ser omitida, todas las demás partes son necesarias, pero puede dejarse vacía. Si su rutina de ensamblador no usará ningún operando de entrada o salida, dos dos puntos deben seguir la cadena de código del ensamblador. Un buen ejemplo es una simple declaración para desactivar las interrupciones:
asm volatile("cli"::);

Código  ensamblador

Puede utilizar los mismos mnemónicos de instrucción de ensamblador que usaría con cualquier otro ensamblador de AVR. Y puede escribir tantas instrucciones de ensamblador en una cadena de código como desee y su memoria flash sea capaz de mantener

Nota

Las directivas de ensamblador disponibles varían de un ensamblador a otro.



Para hacer más legible el código, debe poner cada declaración en una línea separada:

asm volatile("nop\n\t"

"nop\n\t"

"nop\n\t"

"nop\n\t"
::);

Los caracteres de salto de línea y tabulador harán que el listado de ensamblador generado por el compilador sea más legible. Puede parecer un poco extraño por primera vez, pero esa es la forma en que el compilador crea su propio código de ensamblador.
También puede hacer uso de algunos registros especiales.
Símbolo                    Registro
__SREG__                  Registro de estado en la dirección 0x3f (status register)
__SP_H__                  Byte de la parte alta del puntero de pila( stack pointer)
__SP_L__                   Byte de la parte baja del puntero de pila( stack pointer)
__tmp_reg__               Registro r0 , usado para almacenamiento temporal
__zero_reg__              Registro r1, siempre cero

El registro r0 puede ser utilizado libremente por su código de ensamblador y no necesita ser restaurado al final de su código. Es una buena idea usar tmp_reg y zero_reg en lugar de r0 o r1, por si una nueva versión del compilador cambia las definiciones de uso del registro.

Operandos de entrada y salida

Cada operando de entrada y salida se describe mediante una cadena de restricción seguida de una expresión C entre paréntesis. AVR-GCC 3.3 conoce los siguientes caracteres de restricción:

Restricciones usadas para                                                               Rango
a     Registros superiores simples                                                         r16 a r23
b     Base de los pares de registros, de punteros base                          y, z
d     Registros superiores                                                                     r16 a r31
e     Pares de registros usados como punteros                                      x, y, z
q     Registro del puntero de pila                                                          SPH : SPL
r      Cualquier registro                                                                         r0 a r31
t      Registro temporal                                                                         r0
w    Pares de registros superiores, especiales                                       r24, r26, r28, r30
x     Par de registros punteros X                                                          x (r27: r26)
y     Par de registros punteros Y                                                          y (r29: r28)
z     Par de registros punteros Z                                                           z (r31: r30)
G    Constante de punto flotante                                                          0.0
I     Constante entero positivo de 6 bits                                                0 a 63
J     Constante entero negativo de 6 bits                                               -63 a 0
K    Constante entero                                                                         2
L    Constante entero                                                                          0
l      Registros inferiores                                                                       r0 a r15
M   Constante entera de 8 bits                                                            0 a 255
N    Constante entera                                                                          -1
O    Constante entera                                                                          8, 16, 24
P     Constante entera                                                                          1
Q    (GCC >= 4.2x) Una dirección de memoria en base a los punteros X ó Y con desplazamiento
R    (GCC >= 4.3x) Entero constante                                                  -6 a 5

La selección de la restriccion adecuada depende del rango de las constantes o registros, que deben ser aceptables para la instrucción AVR con la que se usan. El compilador C no comprueba ninguna línea de su código de ensamblador. Pero es capaz de comprobar la restricción contra su expresión C. Sin embargo, si especifica las restricciones incorrectas, el compilador puede pasar silenciosamente un código incorrecto al ensamblador. Y, por supuesto, el ensamblador fallará con alguna salida oculta o errores internos. Por ejemplo, si especifica la restricción "r" y está utilizando este registro con una instrucción "ori" en su código de ensamblador, entonces el compilador puede seleccionar cualquier registro. Esto fallará, si el compilador elige r2 a r15. (Nunca elegirá r0 o r1, porque estos son usados para propósitos especiales). Es por eso que la restricción correcta en ese caso es "d". Por otro lado, si utiliza la restricción "M", el compilador se asegurará de que no pase nada más que un valor de 8 bits. Más adelante veremos cómo pasar los resultados de expresión multibyte al código ensamblador.

La siguiente tabla muestra todos los mnemónicos del ensamblador AVR que requieren operandos, y las restricciones relacionadas. Debido a las definiciones de restricciones incorrectas en la versión 3.3, no son lo suficientemente estrictas. No hay, por ejemplo, ninguna restricción, que restrinja las constantes enteras al rango de 0 a 7 para las operaciones bit set y bit clear

Nemónico      Restricción            Nemónico      Restricción
adc               r,r                   add               r,r
adiww,Iandr,r
andid,Masrr
bclrIbldr,I
brbcI,etiquetabrbsI,etiqueta
bsetIbstr,I
cbiI,Icbrd,I
comrcpr,r
cpcr,rcpid,M
cpser,rdecr
elpmt,zeorr,r
inr,Iincr
ldr,elddr,b
ldid,Mldsr,etiqueta
lpmt,zlslr
lsrrmovr,r
movwr,rmulr,r
negrorr,r
orid,MoutI,r
poprpushr
rolrrorr
sbcr,rsbcid,M
sbiI,IsbicI,I
sbiww,Isbrd,M
sbrcr,Isbrsr,I
serdste,r
stdb,rstsetiqueta,r
subr,rsubid,M
swapr

Los caracteres de restricción pueden ser prefijados por un solo modificador de restricción. Las restricciones sin un modificador especifican los operandos de sólo lectura. Los modificadores son:

Modificador    Especifica
=                      Operando de Solo-Escritura, usado usualmente por todos los operandos de salida.
+                      Operando de Lectura-Escritura
&                      El registro debe ser usado solo para salida

Los operandos de salida deben ser de solo-escritura y el resultado de la expresión C debe ser un lvalor, lo que significa que los operandos deben ser válidos en el lado izquierdo de las asignaciones. Tenga en cuenta que el compilador no comprobará si los operandos son de tipo razonable para el tipo de operación utilizada en las instrucciones del ensamblador.

Los operandos de entrada son, como lo habras adivinado, de sólo lectura. Pero ¿qué pasa si necesita el mismo operando para entrada y salida? Como se ha indicado anteriormente, los operandos de lectura-escritura no están soportados en el código del ensamblador en línea. Pero hay otra solución. Para los operadores de entrada es posible utilizar un solo dígito en la cadena de restricción. Usando el n dígitos se le dice al compilador que use el mismo registro que para el n-ésimo operando, comenzando con cero. Aquí hay un ejemplo:
asm volatile("swap %0" : "=r" (valor) : "0" (valor));

Esta instrucción intercambiará los nibbles de una variable de 8 bits denominada valor. La restricción "0" indica al compilador que utilice el mismo registro de entrada que para el primer operando. Tenga en cuenta, sin embargo, que esto no implica automáticamente el caso inverso. El compilador puede elegir los mismos registros para entrada y salida, aunque no se le indique que lo haga. Esto no es un problema en la mayoría de los casos, pero puede ser fatal si el operador de salida es modificado por el código de ensamblador antes de utilizar el operador de entrada. En la situación en la que su código depende de los diferentes registros utilizados para los operandos de entrada y salida, debe agregar el modificador & restriccion a su operando de salida. El ejemplo siguiente muestra este problema

asm volatile("in %0,%1" "\n\t"
"out %1, %2" "\n\t"
"=&r" (input)
"I" (_SFR_IO_ADDR(port)), "r" (output)
);

En este ejemplo se lee un valor de entrada desde un puerto y luego se escribe un valor de salida en el mismo puerto. Si el compilador hubiera elegido el mismo registro para entrada y salida, entonces el valor de salida habría sido destruido en la primera instrucción del ensamblador. Afortunadamente, este ejemplo utiliza el modificador & restricción para indicar al compilador que no seleccione ningún registro para el valor de salida, que se utiliza para cualquiera de los operandos de entrada. Volver a cambiar. Aquí está el código para intercambiar bytes alto y bajo de un valor de 16 bits

asm volatile("mov __tmp_reg__, %A0" "\n\t"
"mov %A0, %B0" "\n\t"
"mov %B0, __tmp_reg__" "\n\t"
"=r" (value)
"0" (value)
);

Primero notará el uso del registro __tmp_reg__, que enumeramos entre otros registros especiales en la sección del Código del Ensamblador. Puede utilizar este registro sin guardar su contenido. Completamente nuevas son las letras A y B en% A0 y% B0. De hecho se refieren a dos registros de 8 bits diferentes, ambos que contienen una parte de valor.

Otro ejemplo para intercambiar bytes de un valor de 32 bits:

asm volatile("mov __tmp_reg__, %A0" "\n\t"
"mov %A0, %D0" "\n\t"
"mov %D0, __tmp_reg__" "\n\t"
"mov __tmp_reg__, %B0" "\n\t"

"mov %B0, %C0" "\n\t"
"mov %C0, __tmp_reg__" "\n\t"
"=r" (value)
"0" (value)
);

En lugar de enumerar el mismo operando que el operando de entrada y salida, también se puede declarar como un operando de lectura-escritura. Esto debe aplicarse a un operando de salida, y la lista de operandos de entrada respectiva permanece vacía:

asm volatile("mov __tmp_reg__, %A0" "\n\t"
"mov %A0, %D0" "\n\t"
"mov %D0, __tmp_reg__" "\n\t"
"mov __tmp_reg__, %B0" "\n\t"
"mov %B0, %C0" "\n\t"
"mov %C0, __tmp_reg__" "\n\t"
"+r" (value));

Si los operandos no encajan en un solo registro, el compilador asignará automáticamente suficientes registros para mantener todo el operando. En el código de ensamblador se utiliza % A0 para referirse al byte más bajo del primer operando, % A1 al byte más bajo del segundo operando y así sucesivamente. El byte siguiente del primer operando será % B0, el siguiente byte % C0 y así sucesivamente.

Esto también implica que a menudo es necesario emitir el tipo de un operando de entrada al tamaño deseado.

Un problema final puede surgir, al utilizar pares de registros de puntero. Si usted los define un operando de entrada

"e" (ptr)

Y el compilador selecciona el registro Z (r30: r31), entonces

%A0 refers to r30 and
%B0 refers to r31

Pero ambas versiones fallarán durante la etapa de ensamblado del compilador, si usted necesita explícitamente Z, como en

ld r24,Z

Si usted escribe

ld r24, %a0

Con una a minúscula después del signo de porcentaje, entonces el compilador creará la línea de ensamblador adecuada.

Clobbers


Como se indicó anteriormente, la última parte de la declaración asm, es la lista de clobbers, esta puede omitirse, incluyendo el separador de dos puntos. Sin embargo, si está utilizando registros, que no se habían pasado como operandos, debe informar al compilador acerca de esto. En el siguiente ejemplo se hará un incremento atómico. Incrementar un valor de 8 bits apuntado por una variable de puntero de una sola vez, sin ser interrumpido por una rutina de interrupción u otro subproceso, en un entorno multiproceso. Note que debemos utilizar un puntero, porque el valor incrementado debe almacenarse antes de que estén habilitadas las interrupciones
asm volatile(
cli" "\n\t"
"ld r24, %a0" "\n\t"
"inc r24" "\n\t"
"st %a0, r24" "\n\t"
"sei" "\n\t"
:
"e" (ptr)
"r24"
);

El compilador podría producir el siguiente código:

cli
ld r24, Z
inc r24
st Z, r24
sei

Una solución sencilla para evitar el registro de rastreo r24 es, hacer uso del registro temporal especial tmp_reg definido por el compilador.

asm volatile(
"cli" "\n\t"
"ld __tmp_reg__, %a0" "\n\t"
"inc __tmp_reg__" "\n\t"
"st %a0, __tmp_reg__" "\n\t"
"sei" "\n\t"
:
"e" (ptr)
);

El compilador está preparado para volver a cargar este registro la próxima vez que lo use. Otro problema con el código anterior es que no debe ser llamado en secciones de código, donde las interrupciones están deshabilitadas y deben mantenerse deshabilitadas, ya que habilitará las interrupciones al final. Podemos almacenar el estado actual, pero luego necesitamos otro registro. Una vez más podemos resolver esto sin clobbering un fijo, pero deje que el compilador lo seleccione. Esto podría hacerse con la ayuda de una variable C local.

{
uint8_t s;
asm volatile(
"in %0, __SREG__" "\n\t"
"cli" "\n\t"
"ld __tmp_reg__, %a1" "\n\t"
"inc __tmp_reg__" "\n\t"
"st %a1, __tmp_reg__" "\n\t"
"out __SREG__, %0" "\n\t"
"=&r" (s)
"e" (ptr)
);
}

Ahora todo parece correcto, pero no es realmente. El código de ensamblador modifica la variable, a la que ptr apunta. El compilador no reconocerá esto y puede mantener su valor en cualquiera de los otros registros. No sólo el compilador trabaja con el valor incorrecto, sino que el código ensamblador también. El programa C también puede haber modificado el valor, pero el compilador no actualizó la ubicación de memoria por razones de optimización. Lo peor que puedes hacer en este caso es:

{
uint8_t s;
asm volatile(
"in %0, __SREG__" "\n\t"
"cli" "\n\t"
"ld __tmp_reg__, %a1" "\n\t"
"inc __tmp_reg__" "\n\t"
"st %a1, __tmp_reg__" "\n\t"
"out __SREG__, %0" "\n\t"
"=&r" (s)
"e" (ptr)
"memory"
);
}


El clobber especial "memory" informa al compilador que el código de ensamblador puede modificar cualquier ubicación de memoria. Obliga al compilador a actualizar todas las variables para las cuales el contenido se mantiene actualmente en un registro antes de ejecutar el código del ensamblador. Y por supuesto, todo tiene que ser recargado de nuevo después de este código.

En la mayoría de las situaciones, una solución mucho mejor sería declarar el destino del puntero en sí mismo volátil:

volatile uint8_t *ptr;

De esta manera, el compilador espera que el valor apuntado por ptr sea cambiado y lo cargará cuando sea usado y lo almacenará cuando sea modificado.

Las situaciones en las que usted necesita clobbers son muy raras. En la mayoría de los casos habrá mejores maneras. Los registros de Clobbered obligarán al compilador a almacenar sus valores antes y volver a cargarlos después de su código de ensamblador. Evitar clobbers da al compilador más libertad mientras optimiza su código.

Macros del ensamblador

Para reutilizar las partes del lenguaje de ensamblador, es útil definirlas como macros y colocarlas en archivos de inclusión. AVR Libc viene con un montón de ellos, que se pueden encontrar en el directorio avr / include. El uso de estos archivos de inclusión puede producir advertencias de compilador, si se utilizan en módulos, que se compilan en el modo ANSI estricto. Para evitar eso, puede escribir _asm_ en lugar de asm y _volátil_ en lugar de volátil. Estos son aliases equivalentes.


Otro problema con las macros reutilizadas surge si está usando etiquetas. En estos casos, puede hacer uso del patrón especial  %=, que se reemplaza por un número único en cada instrucción asm. El siguiente código se ha tomado de  avr / include / iomacros.h:



#define loop_until_bit_is_clear(port,bit)  
        __asm__ __volatile__ (             
        "1: " "sbic %0, %1" "\n\t"      
                 "rjmp 1b"               
                 : /* no outputs */        
                 : "I" (_SFR_IO_ADDR(port)),  
                   "I" (bit)    
        )

Cuando se utiliza por primera vez, L _% = puede traducirse a L_1404, el siguiente uso podría crear L_1405 o lo que sea. En cualquier caso, las etiquetas se volvieron únicas también.

Otra opción es utilizar etiquetas numéricas de estilo Unix-ensamblador. Esto se explica en How do I trace an assembler file in avr-gdb?. El ejemplo anterior se vería así:

#define loop_until_bit_is_clear(port,bit)  
        __asm__ __volatile__ (             
        "1: " "sbic %0, %1" "\n\t"      
                 "rjmp 1b"               
                 : /* no outputs */        
                 : "I" (_SFR_IO_ADDR(port)),  
                   "I" (bit)    
        )

Talón de las funciónes C

Las definiciones de macro incluirán el mismo código ensamblador siempre que se haga referencia a ellas. Esto puede no ser aceptable para rutinas más grandes. En este caso, puede definir una función C "resguardo", que no contenga otra cosa que su código ensamblador


void delay(uint8_t ms)
{
uint16_t cnt;
asm volatile (
"\n"
"L_dl1%=:" "\n\t"
"mov %A0, %A2" "\n\t"
"mov %B0, %B2" "\n"
"L_dl2%=:" "\n\t"
"sbiw %A0, 1" "\n\t"
"brne L_dl2%=" "\n\t"
"dec %1" "\n\t"
"brne L_dl1%=" "\n\t"
"=&w" (cnt)
"r" (ms), "r" (delay_count)
);

}

El propósito de esta función es retrasar la ejecución del programa por un número especificado de milisegundos utilizando un bucle de recuento. La variable global delay_count de 16 bits debe contener la frecuencia de reloj de la CPU en Hertz dividida por 4000 y debe haber sido configurada antes de llamar a esta rutina por primera vez. Como se describe en la sección clobber, la rutina utiliza una variable local para contener un valor temporal.

Otro uso para una variable local es un valor devuelto. La siguiente función devuelve un valor de 16 bits leído de dos direcciones de puerto sucesivas.

uint16_t inw(uint8_t port)
{
uint16_t result;
asm volatile (
"in %A0,%1" "\n\t"
"in %B0,(%1) + 1"
"=r" (result)
"I" (_SFR_IO_ADDR(port))
);
return result;
}

Nota:
        inw () es suministrado por avr-libc.

Nombres en C, utilizados en el código del ensamblador

De forma predeterminada, AVR-GCC utiliza los mismos nombres simbólicos de funciones o variables en C y código ensamblador. Puede especificar un nombre diferente para el código de ensamblador utilizando una forma especial de la instrucción asm:

unsigned long value asm("clock") = 3686400;

Esta instrucción indica al compilador que utilice el nombre de símbolo clock en lugar de value. Esto tiene sentido sólo para variables externas o estáticas, porque las variables locales no tienen nombres simbólicos en el código ensamblador. Sin embargo, las variables locales pueden mantenerse en registros.

Con AVR-GCC usted puede especificar el uso de un registro específico:

void Count(void)
{
register unsigned char contador asm("r3");
... algun codigo...
asm volatile("clr r3");
... mas codigo...
}

La instrucción en ensamblador, "clr r3", borrará la variable contador. AVR-GCC no reservará completamente el registro especificado. Si el optimizador reconoce que la variable no se referenciará más, el registro puede ser reutilizado. Pero el compilador no puede comprobar si el uso de este registro, está en conflicto, con cualquier registro predefinido. Si reserva demasiados registros de esta manera, el compilador puede incluso quedarse sin registros durante la generación de código.



Para cambiar el nombre de una función, necesita una declaración de prototipo, porque el compilador no aceptará la palabra clave asm en la definición de la función:

extern long Calc(void) asm ("CALCULATE");

Llamar a la función Calc () creará instrucciones en ensamblador para llamar a la función CALCULATE.

Enlaces

Para una discusión más detallada sobre el uso del ensamblaje en línea, consulte el manual del usuario de gcc. La última versión del manual gcc está siempre disponible aquí:  http://gcc.gnu.org/onlinedocs/

_________________________________________________________________________________


Generado automáticamente por Doxygen 1.8.7 el Jueves  Agosto 12 de 2014



2 comentarios: