Ser capaz de leer y entender el código fuente es una habilidad importante para cualquier ingeniero de software. Además de ser una habilidad necesaria para los gerentes de contratación, leer y entender el código fuente te convierte en un mejor ingeniero al mejorar tus habilidades de depuración, evitando que cometas errores lógicos comunes, y ayudándote a escribir un código más limpio en futuros proyectos. Esta guía se centrará en los inicios de la programación en ensamblador, cubriendo los principios del lenguaje ensamblador vs código máquina en relación con la ciencia de la computación, ejemplos de cuándo es aplicable, y tutoriales prácticos para empezar a usarlo hoy mismo. ¡Empecemos!
Índice de contenidos
Quizás también te interese conocer:
¿Qué es un lenguaje de programación de alto nivel?
Los lenguajes de programación de alto nivel son más fáciles de usar y entender que los de bajo nivel, porque utilizan conceptos más abstractos. He aquí algunos ejemplos.
- Abstracción – Una llamada a una función es una abstracción del código que realmente ejecuta la función. Una variable es una abstracción de una ubicación de memoria. Una clase es una abstracción de un conjunto de funciones y variables. Un archivo de código fuente es una abstracción del archivo binario que se ejecuta.
- Consistencia – Las mismas reglas se aplican en contextos similares. No hay que recordar reglas diferentes para cada tipo de dato o instrucción. Las mismas reglas de asignación, por ejemplo, se aplican a los números, las variables y los miembros de la clase. La misma sintaxis se utiliza en contextos similares. La sintaxis de una llamada a una función, por ejemplo, es la misma, independientemente de si la función es local o global, o de si toma algún parámetro.
¿Qué es un lenguaje de programación de bajo nivel?
Los lenguajes de programación de bajo nivel son más complejos y técnicos que los de alto nivel. Utilizan conceptos formales y siguen reglas estrictas, por lo que hay menos margen de error. Hay menos reglas que recordar, por lo que hay menos margen de error, pero hay más que aprender. El código escrito en un lenguaje de bajo nivel se ejecutará más rápido que el código escrito en un lenguaje de alto nivel. Un archivo de código fuente escrito en C (puede considerarse de medio nivel, ya que se puede programar a bajo nivel también con C, al igual que se pueden escribir programas a alto nivel), por ejemplo, se ejecutará más rápido que un archivo de código fuente escrito en Python (lenguaje interpretado).
¿Qué es un lenguaje de programación compilado?
Un lenguaje de programación compilado es un lenguaje que se transforma en un archivo ejecutable y se entrega al ordenador para que lo lea en tiempo de ejecución. Los compiladores son programas que leen el código fuente, lo transforman en otro formato de archivo llamado ejecutable, y luego escriben el código ejecutable en un archivo. Este archivo compilado puede entregarse al ordenador para que lo lea y lo ejecute. Los lenguajes compilados suelen ser más rápidos que los interpretados. Algunos ejemplos de lenguajes compilados son C, C++, C#, Java, Fortran y Go.
¿Qué es un lenguaje de programación interpretado?
Un lenguaje de programación interpretado es un lenguaje que se lee y ejecuta como una única traducción. Los intérpretes son programas que leen el código fuente y luego lo transforman en otro formato de archivo llamado flujo de ejecución. Este flujo de ejecución se lee y se ejecuta en tiempo de ejecución. Los intérpretes son más lentos que los lenguajes compilados porque deben leer y ejecutar el código fuente cada vez que se utiliza el programa. Algunos ejemplos de lenguajes interpretados son Python, Ruby y JavaScript.
La diferencia entre lenguajes compilados e interpretados
Los lenguajes compilados son más rápidos que los interpretados porque pueden ser leídos directamente por el ordenador sin ser transformados primero en otra forma. Hay dos formas de compilar los lenguajes: Compiladores estáticos y compiladores dinámicos. Los compiladores estáticos leen y analizan el código fuente para determinar la mejor manera de transformarlo en un archivo ejecutable antes de escribirlo. Los compiladores dinámicos leen y transforman el código fuente a medida que se escribe el archivo ejecutable. La diferencia entre los lenguajes compilados e interpretados es que los lenguajes compilados se escriben primero en un archivo y luego son leídos por el ordenador, mientras que los lenguajes interpretados son leídos y ejecutados directamente por el ordenador.
¿Qué es el lenguaje ensamblador?
El lenguaje ensamblador, o ASM, es un lenguaje de programación de bajo nivel que permite escribir programas que interactúan con el hardware del ordenador. Se utiliza en una amplia gama de aplicaciones, incluyendo sistemas operativos, controladores de dispositivos, sistemas embebidos y sistemas de tiempo real. El lenguaje ensamblador es una representación textual del lenguaje de máquina, que es el que entienden realmente los ordenadores. El lenguaje ensamblador es una versión más legible para el ser humano que resulta más fácil de escribir y entender para los programadores. Es importante tener en cuenta que la programación en lenguaje ensamblador no es algo que se necesite hacer de forma habitual, pero conocer los fundamentos puede ser útil.
Por otro lado, hay que decir que es diferente para cada tipo de arquitectura o ISA. Es decir, el lenguaje ensamblador de x86 será diferente al de ARM, o al de SPARC, PPC, etc. Por tanto, habrá que aprender un nuevo ensamblador por cada arquitectura en la que se desee programar. Todo lo contrario a los lenguajes de alto nivel, que pueden escribirse programas capaces de ser ejecutados en cualquier arquitectura, tan solo hay que obtener el binario para dicha arquitectura una vez compilado.
¿Por qué aprender a programar en lenguaje ensamblador?
Los beneficios de aprender lenguaje ensamblador dependen de quién seas tú como ingeniero de software. Para empezar, aprender lenguaje ensamblador puede ayudarte a entender cómo funcionan los ordenadores a un nivel muy bajo. También puede ayudarte a depurar y optimizar tu código en lenguajes de alto nivel. Para los ingenieros de software que escriben, prueban o mantienen el código que se ejecuta en el propio hardware (como los controladores de dispositivos o los sistemas embebidos), aprender el lenguaje ensamblador es a menudo un requisito. A estos ingenieros se les pide a menudo que hagan algo en ensamblador, y necesitan entender cómo hacerlo. Aprender el lenguaje ensamblador puede ser fundamental para estas tareas. Para los ingenieros de software que escriben código en lenguajes de alto nivel, aprender el lenguaje ensamblador puede ayudarles a depurar y optimizar su código. El aprendizaje del lenguaje ensamblador permite comprender la relación entre el código de alto nivel y el hardware del ordenador. Esto puede ayudarte a depurar problemas rastreando las instrucciones hasta su origen y a optimizar tu código entendiendo cómo se ejecuta.
¿Cuándo utilizar el lenguaje ensamblador?
El lenguaje ensamblador es un lenguaje de programación que te permite interactuar con el hardware del ordenador a un nivel muy bajo. El lenguaje ensamblador depende de la máquina, lo que significa que tendrá que aprender un dialecto diferente para cada tipo de ordenador en el que quiera ejecutar su código. Los lenguajes ensambladores se utilizan normalmente para escribir programas que tratan con hardware específico o que necesitan ejecutarse lo más rápidamente posible. Por ejemplo, puede utilizar el lenguaje ensamblador para escribir controladores de dispositivos para dispositivos de hardware específicos, traducir lenguajes de alto nivel a código de máquina u optimizar secciones de código para que sean más rápidas utilizando el procesamiento en paralelo.
Breve historia de los lenguajes informáticos
A medida que los ordenadores se generalizaban en los años 40 y 50, los ingenieros empezaron a discutir cómo programarlos. De hecho, el primer lenguaje de programación de ordenadores se escribió en 1946, pero no fue muy popular. En los años 50, los fabricantes de ordenadores crearon los primeros ordenadores comerciales y quisieron hacerlos accesibles a las empresas. En esa década, se introdujo el lenguaje ensamblador como forma de interactuar con el hardware del ordenador. Los lenguajes de alto nivel, como Fortran, COBOL y BASIC, aparecieron en los años 60 como una alternativa más accesible al lenguaje ensamblador. Sin embargo, estos lenguajes no eran realmente más fáciles de usar. En cambio, eran más accesibles porque estaban escritos en código similar al inglés, lo que reducía la cantidad de trabajo necesario para aprender un nuevo lenguaje. Más tarde, en la década de 1980, aparecieron lenguajes como el C. El C se considera un lenguaje de «bajo nivel» porque ofrece más control sobre el hardware del ordenador, lo que resulta útil para tareas como la depuración y la optimización.
Fundamentos del lenguaje ensamblador: Terminología y conceptos
El lenguaje ensamblador es un lenguaje de programación de bajo nivel que permite escribir programas que interactúan con el hardware del ordenador. En otras palabras, el lenguaje ensamblador no te permite escribir código a nivel de 1s y 0s, pero sí acercarse bastante a eso. El lenguaje ensamblador es de «bajo nivel» porque está más cerca del hardware. Es mucho más específico de la máquina que los lenguajes de alto nivel, como C y Python. Por ejemplo, no se puede hacer entrada o salida con el lenguaje ensamblador. Sólo puedes realizar acciones que interactúen con el hardware del ordenador.
Al comenzar a aprender el lenguaje ensamblador, hay algunos conceptos y terminología que son importantes de entender. Repasémoslos ahora.
Tipos de datos
Para escribir código en ensamblador, primero necesita entender los tipos de datos disponibles en ensamblador y cómo se comparan con los de los lenguajes de alto nivel. En ensamblador, los principales tipos de datos son números y caracteres. Los números incluyen enteros (números enteros positivos o negativos) y números de punto flotante (números positivos o negativos con decimales). Los caracteres son letras, símbolos y caracteres especiales. En ensamblador, también se puede controlar el tamaño de los números con la longitud de palabra (el número de bits de cada número). El lenguaje ensamblador tiene longitudes de palabra de 16 y 32 bits, lo que significa que los números enteros y de punto flotante tienen una longitud de 16 y 32 bits, respectivamente.
Memoria
El lenguaje ensamblador permite acceder directamente a la memoria del ordenador. Esto significa que puedes escribir programas que almacenen y recuperen datos de la memoria. Las posiciones de memoria en ensamblador se denominan nombres. Se denominan según la función que realizan, como una sentencia if o una variable. Los nombres y su propósito son diferentes para cada lenguaje ensamblador.
Flujo del programa y bifurcación
El flujo del programa en lenguaje ensamblador consiste en dividir el código en sentencias «si-entonces». El lenguaje ensamblador utiliza las bifurcaciones para permitir que el código tome decisiones. Estas sentencias de flujo y ramificación son mucho más precisas que las sentencias «si-entonces» de los lenguajes de alto nivel.
Operadores y expresiones
El lenguaje ensamblador también es diferente de los lenguajes de alto nivel en lo que respecta a las matemáticas. En ensamblador, hay que indicar explícitamente la longitud de los números. Esto se llama longitud de palabra. Además, puedes utilizar operadores, como la suma, la resta y la división, en tu código. El lenguaje ensamblador también permite combinar números y caracteres en expresiones.
Direccionamiento de la memoria
El direccionamiento de la memoria es uno de los conceptos más importantes al escribir código en lenguaje ensamblador. En ensamblador, se utiliza el direccionamiento de memoria para obtener o almacenar datos en la memoria. En ensamblador, se utilizan dos tipos de direccionamiento: directo e indirecto. El direccionamiento directo hace referencia a una posición de memoria directamente. Se utiliza cuando no se conoce la posición de memoria en la que se quiere poner u obtener datos.
Ejemplo de Hola Mundo en Esnamblador
Este es un ejemplo para arquitectura x86-64 de Hola Mundo en ensamblador:
global _start section .text _start: mov rax, 1 ; write( mov rdi, 1 ; STDOUT_FILENO, mov rsi, msg ; "Hello, world!\n", mov rdx, msglen ; sizeof("Hello, world!\n") syscall ; ); mov rax, 60 ; exit( mov rdi, 0 ; EXIT_SUCCESS syscall ; ); section .rodata msg: db "Hello, world!", 10 msglen: equ $ - msg
Este es otro ejemplo de Hola Mundo para arquitectura ARM:
.data /* Data segment: define our message string and calculate its length. */ msg: .ascii "Hello, ARM!\n" len = . - msg .text /* Our application's entry point. */ .globl _start _start: /* syscall write(int fd, const void *buf, size_t count) */ mov %r0, $1 /* fd := STDOUT_FILENO */ ldr %r1, =msg /* buf := msg */ ldr %r2, =len /* count := len */ mov %r7, $4 /* write is syscall #4 */ swi $0 /* invoke syscall */ /* syscall exit(int status) */ mov %r0, $0 /* status := 0 */ mov %r7, $1 /* exit is syscall #1 */ swi $0 /* invoke syscall */
¿Qué es el código máquina?
El código máquina es el lenguaje de programación de más bajo nivel que existe. Son los 0s y 1s que un ordenador utiliza directamente para «ejecutar» y «correr» un programa. La mayoría de los lenguajes de programación de «alto nivel» -como C, C++, Java, Python, JavaScript, etc.- están diseñados para que los humanos puedan leerlos. El código máquina no es legible para los humanos. Está pensado para ser interpretado por los ordenadores. Por eso a veces se llama «código informático». Como está pensado para ser interpretado por los ordenadores, no tiene que estar escrito de forma que tenga sentido para los humanos. Cada 0 o 1 representa una instrucción, como «suma el número de la posición de memoria X con el número de la posición de memoria Y y pon la suma en la posición de memoria Z».
¿Por qué aprender código máquina?
Puedes escribir programas directamente en código máquina. Eso significa que puedes escribir un programa para hacer cualquier cosa – al igual que puedes hacerlo en cualquier otro lenguaje de programación. Pero estos programas son muy simples porque sólo utilizan unas pocas instrucciones. Aprender código máquina te ayudará a entender cómo funcionan los ordenadores a un nivel más profundo. También te ayudará a entender cómo funcionan otros lenguajes de programación. Una vez que sepas programar a este nivel, entenderás conceptos como las direcciones de memoria y los registros. Estas son piezas esenciales del lenguaje que no puedes entender hasta que conozcas el código máquina. El código máquina no sólo es útil para entender cómo funcionan los ordenadores. También agiliza la escritura de programas. A menudo es más rápido escribir un programa en código máquina y luego «traducirlo» a un lenguaje de alto nivel que escribir el programa directamente en un lenguaje de alto nivel.
Ventajas del aprendizaje del código máquina
Como hemos visto, aprender código máquina tiene muchas ventajas. Pero, ¿cuáles son algunos de esos beneficios con más detalle? Echemos un vistazo a algunos de los más importantes.
- Te da una comprensión más profunda de cómo funcionan los ordenadores. Escribir programas en código máquina te ayudará a comprender los fundamentos del funcionamiento de los ordenadores a un nivel mucho más profundo. Esto también facilitará el aprendizaje de nuevos lenguajes de programación en el futuro.
- Te da una mejor comprensión de cómo funcionan los lenguajes de programación. La mayoría de los lenguajes de programación se basan en el código máquina. Una vez que entiendas los fundamentos del código máquina, será más fácil entender cómo funcionan esos otros lenguajes de programación.
- Esto hace que sea más rápido escribir programas. Como entenderás el ordenador a un nivel mucho más profundo, será más fácil escribir programas optimizados para la velocidad. También podrás escribir programas optimizados para el tamaño.
- Esto facilita la depuración de los programas. Una vez que entiendas cómo funciona un ordenador a un nivel profundo, serás capaz de entender por qué los programas funcionan como lo hacen. Esto facilitará la depuración de los programas si no funcionan correctamente.
Inconvenientes del aprendizaje del código máquina
Como hemos visto, hay muchos beneficios en el aprendizaje de código máquina. Pero, ¿cuáles son algunos de esos inconvenientes con más detalle? Veamos algunos de los más importantes.
- Lleva mucho tiempo. Es una habilidad muy difícil de aprender. Requiere mucha práctica y mucho tiempo para desarrollarla. Si esperas llegar a ser bueno en código máquina rápidamente, es probable que te decepcione. Se trata de una habilidad a largo plazo que tardará mucho tiempo en desarrollarse.
- Es muy difícil. Como ya hemos visto, el código máquina está pensado para ser leído por los ordenadores. No está pensado para que lo lean los humanos. Como resultado, es muy difícil de entender. Tendrás que pasar mucho tiempo estudiando el código máquina para llegar a ser bueno en él.
- Sólo ayuda con ciertos tipos de programación. Dado que la mayoría de los lenguajes de programación de alto nivel se basan en el código máquina, aprenderlo te ayudará a escribir programas y a entender cómo funcionan otros lenguajes de programación. Sin embargo, no te ayudará a escribir programas en todos los lenguajes. No podrás escribir programas en Ruby, Fortran o Java directamente en código máquina.
Diferencias entre el código máquina y el lenguaje ensamblado
Aunque hay muchas similitudes entre el código máquina y el lenguaje ensamblador, también hay muchas diferencias. La principal diferencia es que la sintaxis del lenguaje de programación es diferente. La sintaxis del código máquina es diferente a la del lenguaje ensamblador. Concretamente, el lenguaje ensamblador utiliza códigos mnemotécnicos, por lo que se le llama «mnemónico». El código máquina no utiliza códigos mnemotécnicos. En su lugar, utiliza dígitos binarios. Por eso se le llama «código binario» o «lenguaje binario» o «lenguaje informático binario». Otra diferencia es que el lenguaje ensamblador se escribe para el procesador, mientras que el código máquina se escribe para el ordenador. Se puede escribir en lenguaje ensamblador para un procesador, pero sólo se puede escribir en código máquina para un ordenador. El ordenador es el sistema operativo, la unidad central de procesamiento (CPU) y todo lo demás que hay dentro de un ordenador.
Ejemplo de Hola Mundo en código máquina
b8 21 0a 00 00 #moving "!\n" into eax a3 0c 10 00 06 #moving eax into first memory location b8 6f 72 6c 64 #moving "orld" into eax a3 08 10 00 06 #moving eax into next memory location b8 6f 2c 20 57 #moving "o, W" into eax a3 04 10 00 06 #moving eax into next memory location b8 48 65 6c 6c #moving "Hell" into eax a3 00 10 00 06 #moving eax into next memory location b9 00 10 00 06 #moving pointer to start of memory location into ecx ba 10 00 00 00 #moving string size into edx bb 01 00 00 00 #moving "stdout" number to ebx b8 04 00 00 00 #moving "print out" syscall number to eax cd 80 #calling the linux kernel to execute our print to stdout b8 01 00 00 00 #moving "sys_exit" call number to eax cd 80 #executing it via linux sys_call
Con esto terminamos nuestro artículo sobre las diferencias entre un ensamblador y un código de máquina. ¿Te ha parecido interesante?