De C a Asamblea

16

Supongamos que tenemos el siguiente fragmento de código C para un avr-8bit:

int v1=1;
int v2=2;
v2=v2+v1;

Esperaba lo siguiente desmontar

ldi r18, 1;
ldi r19, 2;
add r19, r18;

pero después de correr:

avr-gcc -mmcu=atmega2560 Test.c -o Test.elf

y

avr-objdump -S Test.elf > Test.lss

Tengo el siguiente desmontaje

    ldi r24, 0x01   ; 1
    ldi r25, 0x00   ; 0
    std Y+2, r25    ; 0x02
    std Y+1, r24    ; 0x01
    ldi r24, 0x02   ; 2
    ldi r25, 0x00   ; 0
    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03
    ldd r18, Y+3    ; 0x03
    ldd r19, Y+4    ; 0x04
    ldd r24, Y+1    ; 0x01
    ldd r25, Y+2    ; 0x02
    add r24, r18
    adc r25, r19
    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

¿hay alguien que pueda ayudarme a comprender el resultado del desensamblador?

Editar: Usando char el ensamblado se convierte en:

ldi r24, 0x01
std Y+1, r24
ldi r24, 0x02
std Y+2, r24
ldd r25, Y+2
ldd r24, Y+1
add r24, r25
std Y+2, r24

¿Cuándo hay instrucción estándar?

    
pregunta DarkCoffee

3 respuestas

19

Respuesta corta: sus registros son de 8 bits y sus valores son de 16 bits. Por lo tanto, se trata de dos piezas.

Respuesta larga:

    ldi r24, 0x01   ; 1
    ldi r25, 0x00   ; 0

Almacene el valor de 16 bits 1 en los registros de 8 bits r24, r25.

    std Y+2, r25    ; 0x02
    std Y+1, r24    ; 0x01

Almacénelo en las ubicaciones de pila Y + 1, Y + 2.

    ldi r24, 0x02   ; 2
    ldi r25, 0x00   ; 0

Almacene el valor de 16 bits 2 en los registros de 8 bits r24, r25.

    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

Almacénelo en las ubicaciones de pila Y + 3, Y + 4.

    ldd r18, Y+3    ; 0x03
    ldd r19, Y+4    ; 0x04
    ldd r24, Y+1    ; 0x01
    ldd r25, Y+2    ; 0x02

Vuelve a copiarlos de la pila a (r18, r19) y (r24, r25)

    add r24, r18
    adc r25, r19

Agregue (r18, r19) a (r24, r25), incluido el carry en la segunda adición

    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

Guárdalo de nuevo en la pila.

Para obtener su ensamblaje original, intente dos cosas:

  • usar variables "char"
  • use la opción del compilador "-O2"

Editar : la razón por la que el compilador almacena las variables en la pila en lugar de mantenerlas en registros es porque están almacenadas con el tipo de almacenamiento "automático" predeterminado. puede optimizarlos en registros pero no tiene que hacerlo, incluso si los declara con clase de almacenamiento "registrarse".

Si bien este no es un requisito estricto del lenguaje, es el comportamiento normal del compilador. Si en algún momento toma la dirección de v1, a debe asignársele una ubicación de almacenamiento, y guárdela allí cuando cambie el valor de "v1". Así que para guardar la contabilidad de si v1 debe almacenarse o no en un registro o en la pila, la mantiene en la pila y trata cada línea de código por separado.

    
respondido por el pjc50
4

Cuando encontré un código de ejemplo, haré una respuesta a mi comentario; otros ya explicaron el problema.

Una gran cantidad de código incrustado con el que trabajo define tipos adicionales que son específicos de su tamaño, como "uint8, uint16, uint32" para los ints sin firmar, por ejemplo. De esa manera, siempre sabrás exactamente con qué tipo de variable estás tratando. Especialmente en los pequeños incrustados, firmados, flotados, "int" de tamaño / firma indefinidos todos le costarán ciclos de CPU en el mejor de los casos y causarán errores graves en el peor.

Aquí están nuestros #defines actuales:

/*
 * Example - the basic data types from our embedded code
 */
typedef unsigned char       uint8;  /*  8 bits */
typedef unsigned short int  uint16; /* 16 bits */
typedef unsigned long int   uint32; /* 32 bits */

typedef char                int8;   /*  8 bits */
typedef short int           int16;  /* 16 bits */
typedef int                 int32;  /* 32 bits */

typedef volatile int8       vint8;  /*  8 bits */
typedef volatile int16      vint16; /* 16 bits */
typedef volatile int32      vint32; /* 32 bits */

typedef volatile uint8      vuint8;  /*  8 bits */
typedef volatile uint16     vuint16; /* 16 bits */
typedef volatile uint32     vuint32; /* 32 bits */
    
respondido por el John U
2

Su código C usa variables enteras de 16 bits (int). El compilador no puede leer tu mente, por lo que compila exactamente lo que está en el archivo fuente. Por lo tanto, si desea variables de 8 bits, debe usar el tipo respectivo.

Como resultado, aún obtendrá el almacenamiento de los valores en la memoria (aunque más simple). No soy tan bueno en C, pero en mi humilde opinión, hay algunas opciones para asignar la variable a algún registro, si desea que algunas variables estén en los registros en lugar de RAM. Algo como:

register unsigned char VARNAME asm("r3");

Tenga en cuenta que no todos los registros están disponibles para tales trucos.

Entonces, ¿la conclusión? Escribe tus programas en ensamblador. Siempre serán más pequeños, más rápidos y fáciles de leer / apoyar.

    
respondido por el johnfound

Lea otras preguntas en las etiquetas