Este artículo analiza dos lenguajes de programación, a saber, C y ensamblado, y presenta la necesidad de conocer el lenguaje ensamblador para programar sistemas integrados.

Lenguaje ensamblador y el auge de la memoria económica

Actualmente, la mayoría de la programación de sistemas integrados se realiza en C; si no es C, entonces otro lenguaje de alto nivel como C ++.

No siempre fue así. En los primeros días de los sistemas integrados, todo el código estaba escrito en lenguaje ensamblador; Esa era la única opción. En aquellos días, la memoria era extremadamente limitada, por lo que era esencial un control muy estricto de su uso y el ensamblaje proporcionaba ese control. Pero, aparte de eso, no había herramientas de lenguaje de alto nivel disponibles.

Pasaron algunos años antes de que las herramientas llegaran al mercado y bastantes años antes de que su calidad fuera realmente lo suficientemente buena como para desarrollar un código serio. Las herramientas aparecieron en el momento justo, a medida que los procesadores se volvían más potentes (los dispositivos de 16 y 32 bits se volvieron viables), la memoria se volvía más barata y más densa y la complejidad de las aplicaciones aumentaba.

Entonces, ¿qué hay de hoy? Tenemos procesadores enormemente potentes que pueden recibir enormes cantidades de memoria, ejecutando aplicaciones extremadamente complejas, desarrolladas por grandes equipos de programadores.

¿Dónde encajan las habilidades de lenguaje ensamblador?

¿Por qué aprender ensamblaje? Habilidades de programación de sistemas integrados

Realmente hay dos habilidades, cada una de las cuales puede ser valiosa: la capacidad de leer / entender lenguaje ensamblador y la capacidad de escribir eso.

Por qué debería saber leer el lenguaje ensamblador

La mayoría de los desarrolladores de software embebido deberían tener alguna capacidad de leer lenguaje ensamblador. Esto es necesario por dos razones.

Primero, la eficiencia del código en un sistema embebido es casi siempre crítica. Los compiladores modernos generalmente hacen un gran trabajo al optimizar el código. Sin embargo, es importante poder comprender qué grandes cosas ha hecho el compilador. De lo contrario, puede haber confusión durante la depuración.

Los compiladores tienden a no solo traducir C al lenguaje ensamblador. Un buen compilador moderno toma un algoritmo expresado en C y genera un algoritmo funcionalmente equivalente expresado en ensamblador. No es lo mismo Es por eso que la depuración puede ser un desafío.

También es posible que el compilador no haya hecho un trabajo perfecto (tal vez el código C no fue escrito de la manera más clara) y el desarrollador necesita poder entender lo que salió mal. La inspección del código generado por el compilador debe ser una parte rutinaria del proceso de desarrollo. Esto brinda la oportunidad de garantizar que la salida del compilador realmente haga lo que el programador pretendía y no haya sido malinterpretada por un optimizador excesivamente entusiasta.

La segunda razón por la cual algunos Los desarrolladores necesitan poder leer el ensamblaje, ya que es esencial cuando se codifica "cerca del hardware". Los controladores no están necesariamente escritos en 100% ensamblados hoy en día, pero algunos contenidos en lenguaje ensamblador son casi inevitables. Ser capaz de comprender lo que hace un controlador, en detalle, es necesario para usarlo de manera más efectiva y para realizar la resolución de problemas.

¿Por qué debería saber cómo escribir lenguaje ensamblador?

¿Qué hay de escribir lenguaje ensamblador? Hoy en día, sería muy inusual que una aplicación completa se escribiera en lenguaje ensamblador; la mayor parte del código, al menos, está escrito en C. Entonces, las habilidades de programación en C son el requisito clave para el desarrollo de software integrado. Sin embargo, algunos desarrolladores necesitan tener una idea de la programación en lenguaje ensamblador. Por supuesto, esta habilidad es específica de un procesador en particular; sin embargo, si un diseñador ha dominado el lenguaje ensamblador de una CPU, la migración a otra no será un gran desafío.

Hay dos razones para escribir lenguaje ensamblador. La primera y más importante razón es implementar alguna funcionalidad que no es posible expresar en C. Un ejemplo simple podría ser desactivar interrupciones. Esto podría lograrse escribiendo una subrutina en lenguaje ensamblador y llamándola como si fuera una función C. Para hacer eso, se debe conocer el protocolo de llamada / retorno del compilador de C en uso, pero esto es generalmente fácil de entender. Podrías simplemente mirar el código generado por el compilador, por ejemplo.

La otra forma de implementar el código del lenguaje ensamblador es insertarlo en línea en el código C, normalmente utilizando asm palabra clave de extensión. Esto tiene un sentido particular cuando se necesita una sola o solo unas pocas instrucciones de ensamblador, ya que se elimina la sobrecarga de llamada / retorno. La implementación de esta extensión varía de un compilador a otro, pero comúnmente asm declaración toma este tipo de forma:

  asm ("trampa # 0");

Por lo general, los únicos lugares donde se requiere funcionalidad que no se puede expresar en C son el código de inicio y los controladores de dispositivo. Esta parte del desarrollo de una aplicación de software integrada involucra a un pequeño número de desarrolladores. Por lo tanto, la necesidad de habilidades de escritura de ensamblaje se limita, como se mencionó anteriormente, a un grupo selecto de ingenieros.

Algunos desarrolladores sienten que necesitan saber cómo escribir el lenguaje ensamblador para implementar el código de una manera "más eficiente" de lo que administrará el compilador. Es posible que, en algunas ocasiones muy raras, tengan razón. Sin embargo, la mayoría de los compiladores modernos hacen un trabajo notable de optimización y generación de código eficiente (tenga en cuenta que "eficiente" puede significar rápido o compacto, usted elige, aunque a veces puede obtener ambos).

Aquí hay un ejemplo:

#define ARRAYSIZE 4

char aaa[ARRAYSIZE];

int main ()

{

        int i;

        para (i = 0; i

                aaa = 0;

};>

Esto parece un bucle simple que establece cada elemento de la matriz en cero. Si compila esto con una cantidad razonable de optimización activada e intenta depurar el código, obtendrá un resultado extraño: saltará directamente a través del ciclo (es decir, se comportará como si no hubiera ningún ciclo). Esto se debe a que el compilador determina que un movimiento de 32 bits de cero en la matriz haría el trabajo de manera mucho más eficiente que un bucle.

El código resultante (en este caso para un procesador Arm) se parece a esto:


    mov r3, # 0

    ldr r2, .L3

    mov r0, r3

    str r3, [r2]

    bx lr

.L3:

    .word .LANCHOR0

Ajustar el valor de TAMAÑO produce algunos resultados interesantes Configurándolo en 5 5 da esto:


    mov r3, # 0

    ldr r2, .L3

    mov r0, r3

    str r3, [r2]

    strb r3, [r2, #4]

Todavía no hay bucle. Haciendolo 8 continúa en esta línea:


   mov r3, # 0

  ldr r2, .L3

  mov r0, r3

  str r3, [r2]

  str r3, [r2, #4]

Luego, construir este código para una CPU de 64 bits es aún mejor:


    mov w0, 0

    str xzr, [x1, #:lo12:.LANCHOR0]

Y así continúa. Los tamaños de matriz más grandes resultan en bucles eficientes o tal vez simplemente llamando a una función de biblioteca como memset(, una función de biblioteca C estándar que se puede llamar desde el ensamblado

La conclusión es que las habilidades de lenguaje ensamblador están lejos de ser obsoletas, pero muchos desarrolladores de software embebidos altamente calificados y muy productivos pueden estar limitados a una lectura competente de código ensamblador.


Si desea obtener más información sobre el otro lado de este concepto, consulte el artículo de Robert Keim sobre lenguaje C para la programación integrada.

Comparta sus pensamientos y experiencias con respecto a la utilidad del lenguaje ensamblador en los comentarios a continuación.

Dejar respuesta

Please enter your comment!
Please enter your name here