De Wikipedia, la enciclopedia libre
Saltar a navegación Saltar a búsqueda

Un lenguaje de programación de bajo nivel es un lenguaje de programación que proporciona poca o ninguna abstracción de la arquitectura del conjunto de instrucciones de una computadora: comandos o funciones en el mapa del lenguaje que son estructuralmente similares a las instrucciones del procesador. Generalmente, esto se refiere a código máquina o lenguaje ensamblador . Debido a la baja abstracción (de ahí la palabra) entre el lenguaje y el lenguaje de máquina, los lenguajes de bajo nivel a veces se describen como "cercanos al hardware". Los programas escritos en lenguajes de bajo nivel tienden a ser relativamente no portátiles , debido a que están optimizados para cierto tipo de arquitectura de sistema.

Los lenguajes de bajo nivel se pueden convertir a código de máquina sin un compilador o intérprete ( los lenguajes de programación de segunda generación utilizan un procesador más simple llamado ensamblador ) y el código resultante se ejecuta directamente en el procesador. Se puede hacer que un programa escrito en un lenguaje de bajo nivel se ejecute muy rápidamente, con una pequeña huella de memoria . Un programa equivalente en un lenguaje de alto nivel puede ser menos eficiente y utilizar más memoria. Los lenguajes de bajo nivel son simples, pero se consideran difíciles de usar debido a numerosos detalles técnicos que el programador debe recordar. En comparación, un lenguaje de programación de alto nivel aísla la semántica de ejecución de una arquitectura de computadora de la especificación del programa, lo que simplifica el desarrollo.

Código de máquina [ editar ]

Panel frontal de un miniordenador PDP-8 / E. La fila de interruptores en la parte inferior se puede usar para alternar en un programa de lenguaje de máquina.

El código de máquina es el único lenguaje que una computadora puede procesar directamente sin una transformación previa. Actualmente, los programadores casi nunca escriben programas directamente en código máquina, porque requiere atención a numerosos detalles que un lenguaje de alto nivel maneja automáticamente. Además, requiere memorizar o buscar códigos numéricos para cada instrucción y es extremadamente difícil de modificar.

El verdadero código de máquina es un flujo de datos brutos, generalmente binarios . Un programador que codifica en "código de máquina" normalmente codifica instrucciones y datos en una forma más legible, como decimal , octal o hexadecimal, que se traduce a formato interno mediante un programa llamado cargador o se cambia a la memoria de la computadora desde un panel frontal .

Aunque pocos programas están escritos en lenguaje de máquina, los programadores a menudo se vuelven expertos en leerlos trabajando con volcados de núcleo o depurando desde el panel frontal.

Ejemplo: una función en representación hexadecimal de código de máquina x86 de 32 bits para calcular el número n de Fibonacci :

8B542408 83FA0077 06B80000 0000C383FA027706 B8010000 00C353BB 01000000B9010000 008D0419 83FA0376 078BD989C14AEBF1 5BC3

Lenguaje ensamblador [ editar ]

Los lenguajes de segunda generación proporcionan un nivel de abstracción además del código de máquina. En los primeros días de la codificación en computadoras como TX-0 y PDP-1 , lo primero que hicieron los piratas informáticos del MIT fue escribir ensambladores. [1] El lenguaje ensamblador tiene poca semántica o especificación formal, siendo solo un mapeo de símbolos legibles por humanos, incluidas direcciones simbólicas, códigos de operación , direcciones , constantes numéricas, cadenas , etc. Normalmente, una instrucción de máquina se representa como una línea de código ensamblador. Los ensambladores producen archivos de objeto que pueden vincularse con otros archivos de objeto o sercargado por su cuenta.

La mayoría de los ensambladores proporcionan macros para generar secuencias comunes de instrucciones.

Ejemplo: la misma calculadora de números de Fibonacci que la anterior, pero en lenguaje ensamblador x86-64 usando la sintaxis de AT&T :

_fib:  movl  $ 1 ,  % eax .fib_loop:  cmpl  $ 1 ,  % edi  jbe  .fib_done  movl  % eax ,  % ecx  addl  % ebx ,  % eax  movl  % ecx ,  % ebx  subl  $ 1 ,  % edi  jmp  .fib_loop .fib_done:  ret

En este ejemplo de código, las características de hardware del procesador x86-64 (sus registros ) se nombran y manipulan directamente. La función carga su entrada desde % edi de acuerdo con System V ABI y realiza su cálculo manipulando valores en los registros EAX , EBX y ECX hasta que finaliza y regresa. Tenga en cuenta que en este lenguaje ensamblador, no existe el concepto de devolver un valor. Habiendo almacenado el resultado en el registro EAX , el RETEl comando simplemente mueve el procesamiento del código a la ubicación del código almacenado en la pila (generalmente la instrucción inmediatamente después de la que llamó a esta función) y depende del autor del código de llamada saber que esta función almacena su resultado en EAX y recuperar desde allí. El lenguaje ensamblador x86-64 no impone ningún estándar para devolver valores de una función (y por eso, de hecho, no tiene el concepto de función); depende del código de llamada examinar el estado después de que el procedimiento regresa si necesita extraer un valor.

Compare esto con la misma función en C:

unsigned  fib ( unsigned  n )  {  if  ( ! n )  return  0 ;  de lo contrario,  si  ( n  <=  2 )  devuelve  1 ;  else  {  sin firmar  a ,  c ;  para  ( a  =  c  =  1 ;  ;  - n )  {  c  + =  una ;  si  ( n  <=  2 )  devuelve  c;  a  =  c  -  a ;  }  } }

Este código es muy similar en estructura al ejemplo del lenguaje ensamblador, pero existen diferencias significativas en términos de abstracción:

  • La entrada (parámetro n ) es una abstracción que no especifica ninguna ubicación de almacenamiento en el hardware. En la práctica, el compilador de C sigue una de las muchas convenciones de llamada posibles para determinar una ubicación de almacenamiento para la entrada.
  • La versión en lenguaje ensamblador carga el parámetro de entrada de la pila en un registro y en cada iteración del ciclo disminuye el valor en el registro, sin alterar nunca el valor en la ubicación de la memoria en la pila. El compilador de C podría cargar el parámetro en un registro y hacer lo mismo o podría actualizar el valor donde sea que esté almacenado. Cuál elige es una decisión de implementación completamente oculta al autor del código (y sin efectos secundarios , gracias a los estándares del lenguaje C).
  • Las variables locales a, byc son abstracciones que no especifican ninguna ubicación de almacenamiento específica en el hardware. El compilador de C decide cómo almacenarlos realmente para la arquitectura de destino.
  • La función de retorno especifica el valor a devolver, pero no dicta cómo se devuelve. El compilador de C para cualquier arquitectura específica implementa un mecanismo estándar para devolver el valor. Los compiladores para la arquitectura x86 normalmente (pero no siempre) usan el registro EAX para devolver un valor, como en el ejemplo del lenguaje ensamblador (el autor del ejemplo del lenguaje ensamblador ha optado por copiar la convención C, pero el lenguaje ensamblador no lo requiere).

Estas abstracciones hacen que el código C sea compilable sin modificaciones en cualquier arquitectura para la que se haya escrito un compilador C. El código del lenguaje ensamblador x86 es específico de la arquitectura x86.

Programación de bajo nivel en lenguajes de alto nivel [ editar ]

A fines de la década de 1960, los lenguajes de alto nivel como PL / S , BLISS , BCPL , ALGOL extendido (para sistemas grandes de Burroughs ) y C incluían cierto grado de acceso a funciones de programación de bajo nivel. Un método para esto es el ensamblaje en línea , en el que el código ensamblador está incrustado en un lenguaje de alto nivel que admite esta característica. Algunos de estos lenguajes también permiten que las directivas de optimización del compilador dependientes de la arquitectura ajusten la forma en que un compilador usa la arquitectura del procesador de destino.

Referencias [ editar ]

  1. ^ Levy, Stephen (1994). Hackers: héroes de la revolución informática . Libros de pingüinos. pag. 32. ISBN 0-14-100051-1.