Está en la página 1de 18

Lenguaje Ensamblador

El lenguaje ensamblador es un tipo de lenguaje de bajo nivel utilizado para escribir programas informticos, y constituye la representacin ms directa del cdigo mquina especfico para cada arquitectura de computadoras legible por un programador. Fue usado ampliamente en el pasado para el desarrollo de software, pero actualmente slo se utiliza en contadas ocasiones, especialmente cuando se requiere la manipulacin directa del hardware o se pretenden rendimientos inusuales de los equipos.

Caracteristicas
Programar en lenguaje ensamblador es difcil de aprender,

entender, leer, escribir, depurar y mantener, por eso surgi la necesidad de los lenguajes compilados.
A pesar de perder rendimiento en un proceso de compilacin, en

la

actualidad

la

mayora

de

las

computadoras

son

suficientemente rpidas.
El lenguaje ensamblador no es portable. Programar en lenguaje ensamblador lleva mucho tiempo.

Los

programas

hechos

en

lenguaje

ensamblador

son

generalmente ms rpidos. Al programar cuidadosamente en lenguaje ensamblador se pueden crear programas de 5 a 10 veces ms rpidos que con lenguajes de alto nivel.
Los programas hechos en lenguaje ensamblador generalmente

ocupan menos espacio. Un buen programa en lenguaje ensamblador puede ocupar casi la mitad de espacio que su contraparte en lenguaje de alto nivel.
Con el lenguaje ensamblador se pueden crear segmentos de

cdigo imposibles de formar en un lenguaje de alto nivel.

Ensambladores
Un ensamblador crea cdigo objeto traduciendo instrucciones

mnemnicas a cdigos operativos e interpretando los nombres simblicos para direcciones de memoria y otras entidades. El uso de referencias simblicas es una caracterstica bsica de los ensambladores, evitando tediosos clculos y direccionamiento manual despus de cada modificacin del programa. La mayora de los ensambladores tambin incluyen facilidades para crear macros, a fin de generar series de instrucciones cortas que se ejecutan en tiempo real, en lugar de utilizar subrutinas. Los ensambladores son por lo general ms fciles de programar que los compiladores de lenguajes de alto nivel, y han estado disponibles desde la dcada de 1950. Los ensambladores modernos, especialmente para arquitecturas basadas en RISC, como por ejemplo MIPS, SPARC y PA-RISC optimizan las instrucciones para explotar al mximo la eficiencia de segmentacin del CPU. Los ensambladores de alto nivel ofrecen posibilidades de abstraccin que incluyen:
Control avanzado de estructuras. Procedimientos de alto nivel, declaracin de funciones.

Tipos de datos que incluyen estructuras, registros, uniones, clases

y conjuntos.
Sofisticado procesamiento de macros.

Lenguaje
Un programa escrito en lenguaje ensamblador consiste en una serie de instrucciones que corresponden al flujo de rdenes ejecutables que pueden ser cargadas en la memoria de una computadora. Por ejemplo, un procesador x86 puede ejecutar la siguiente instruccin binaria como se expresa en cdigo de mquina:

Binario: 10110000 01100001 (Hexadecimal: 0xb061)

La representacin equivalente en lenguaje ensamblador es ms fcil de recordar:

MOV al, 061h

Esta instruccin significa; Mueve

el

valor

hexadecimal

61

(97

decimal) al registro "al".


El mnemnico mov es un cdigo de operacin u "opcode", elegido por los diseadores de la coleccin de instrucciones para abreviar move (mover). El opcode es seguido por una lista de argumentos o parmetros, completando una instruccin de ensamblador tpica. La transformacin del lenguaje ensamblador en cdigo mquina la realiza un programa ensamblador, y la traduccin inversa la puede efectuar un desensamblador. A diferencia de los lenguajes de alto nivel, aqu hay usualmente una correspondencia 1 a 1 entre las instrucciones simples del ensamblador y el lenguaje de mquina. Sin embargo, en algunos casos, un ensamblador puede proveer "pseudo instrucciones" que se expanden en

un cdigo de mquina ms extenso a fin de proveer la funcionalidad necesaria. Por ejemplo, para un cdigo mquina condicional como "si X mayor o igual que" , un ensamblador puede utilizar una pseudoinstruccin al grupo "haga si menor que" , y "si = 0" sobre el resultado de la condicin anterior. Los ensambladores ms completos tambin proveen un rico lenguaje de macros que se utiliza para generar cdigo ms complejo y secuencias de datos. Cada arquitectura de computadoras tiene su propio lenguaje de mquina, y en consecuencia su propio lenguaje ensamblador. Los ordenadores difieren en el tipo y nmero de operaciones que soportan; tambin pueden tener diferente cantidad de registros, y distinta representacin de los tipos de datos en memoria. Aunque la mayora de las computadoras son capaces de cumplir esencialmente las mismas funciones, la forma en que lo hacen difiere y los respectivos lenguajes ensambladores reflejan tal diferencia. Pueden existir mltiples conjuntos de mnemnicos o sintxis de lenguaje ensamblador para un mismo conjunto de instrucciones, instanciados tpicamente en diferentes programas ensamblador. En estos casos, la alternativa ms popular es la provista por los fabricantes, y usada en los manuales del programa.

Cdigo mquina (o lenguaje de mquina)


El lenguaje de mquina est formado por instrucciones sencillas, que dependiendo de la estructura del procesador- pueden especificar:
Registros

especficos

para

operaciones

aritmticas,

direccionamiento o control de funciones.


Posiciones de memoria especficas (offset). Modos de direccionamiento usados para interpretar operandos.

Las

operaciones

ms

complejas

se

realizan

combinando

estas

instrucciones sencillas, que pueden ser ejecutadas secuencialmente o mediante instrucciones de control de flujo. Las operaciones disponibles en la mayora de los conjuntos de instrucciones incluye:
MOVER Llenar un registro con un valor constante Mover datos de una posicin de memoria a un registro o viceversa COMPUTAR Sumar, restar, multiplicar o dividir los valores de dos registros,

colocando el resultado en uno de ellos o en otro registro


Realizar operaciones binarias, incluyendo operaciones lgicas

(AND/OR/XOR/NOT)
Comparar valores entre registros (mayor, menor, igual) AFECTAR EL FLUJO DEL PROGRAMA Saltar a otra posicin en el programa y ejecutar instrucciones all Saltar si se cumplen ciertas condiciones (IF) Saltar a otra posicin, pero guardar el punto de salida para

retornar (CALL, llamada a subrutinas)


Algunas computadoras incluyen instrucciones complejas dentro de sus capacidades. Una sola instruccin compleja hace lo mismo que en otras computadoras puede requerir una larga serie de instrucciones, por ejemplo:
Salvar varios registros en la pila de una sola vez Mover grandes bloques de memoria Operaciones aritmticas complejas o de punto flotante (seno,

coseno, raz cuadrada )

<

Requerimientos para la Programacin en ensamblador.

Software Necesario Para poder crear un programa se requieren varias herramientas: Primero un editor para crear el programa fuente. Segundo un compilador que no es mas que un programa que "traduce" el programa fuente a un programa objeto. Y tercero un enlazador o linker, que genere el programa ejecutable a partir del programa objeto. El editor puede ser cualquier editor de textos que se tenga a la mano como Bloc de notas o Notepad ++, como compilador utilizaremos el TASM, y como enlazador utilizaremos el programa Tlink. Descargar kit. La extensin usada para que TASM reconozca los programas fuente en ensamblador es .ASM; una vez traducido el programa fuente, el TASM crea un archivo con la extensin .OBJ, este archivo contiene un "formato intermedio" del programa, que aun no es ejecutable pero tampoco es ya un programa en lenguaje fuente. El enlazador genera, a partir de un archivo .OBJ o la combinacin de varios de estos archivos, un programa executable, cuya extensin es usualmente .EXE Utilizacin del TASM Una vez creado nuestro cdigo fuente guardado en extensin .asm, procedemos a abrir el Simbolo del Sistema o consola de comandos DOS, posteriormente nos dirigimos al directorio donde esta nuestro cdigo fuente, debo decir que el kit de ensamblaje TASM, TLINK y TD deben estar en la misma carpeta donde esta nuestro codigo fuente para poder crear nuestros archivos .OBJ y .EXE Tecleamos lo siguiente:

TASM [nombre_Archivo].asm

Donde [nombre_Archivo] es el nombre del programa fuente con extensin .ASM del cual se crear un archivo con extensin .OBJ Uso del enlazador TLINK El TASM nicamente puede crear programas en formato .OBJ, los cuales no son ejecutables por si solos, es necesario un enlazador que genere el cdigo ejecutable.La Utilizacin del enlazador es muy parecida a la del TASM, nicamente se teclea en el indicador del DOS:

TLINK [nombre_Archivo].obj

Donde [nombre_Archivo] es el nombre del programa intermedio (OBJ). Esto generar directamente un archivo con el nombre del programa intermedio y la extensin .EXE Formato interno de un programa Para poder comunicarnos en cualquier lenguaje, incluyendo los lenguajes de Programacin, es necesario seguir un conjunto de reglas, de lo contrario no podramos expresar lo que deseamos. En este apartado veremos algunas de las reglas que debemos seguir para escribir un programa en lenguaje ensamblador, enfocandonos a la forma de escribir las instrucciones para que el ensamblador sea capaz de interpretarlas. Bsicamente el formato de una lnea de cdigo en lenguaje ensamblador consta de cuatro partes:
Etiqueta, variable o constante: No siempre es definida, si se define

es necesario utilizar separadores para diferenciarla de las otras partes, usualmente espacios, o algn smbolo especial.

Directiva o instruccin: es el nombre con el que se conoce a la

instruccin que queremos que se ejecute.


Operando(s): la mayora de las instrucciones en ensamblador

trabajan con dos operandos, aunque hay instrucciones que funcionan slo con uno. El primero normalmente es el operando destino, que es el depsito del resultado de alguna operacin; y el segundo es el operando fuente, que lleva el dato que sera procesado. Los operandos se separan uno del otro por medio de una coma ",".
Comentario: como su nombre lo indica es tan slo un escrito

informativo, usado principalmente para explicar que est haciendo el programa en determinada linea; se separa de las otras partes por medio de un punto y coma ";". Esta parte no es necesaria en el programa, pero nos ayuda a depurar el programa en caso de errores o modificaciones. Formato Externo de un programa Adems de definir ciertas reglas para que el ensamblador pueda entender una instruccin es necesario darle cierta informacin de los recursos que se van a utilizar, como por ejemplo los segmentos de memoria que se van a utilizar, datos iniciales del programa y tambien donde inicia y donde termina nuestro cdigo. El siguiente cdigo es un ejemplo del programa clsico "Hola mundo" escrito para la arquitectura de procesador x86 (bajo el sistema operativo DOS).

.model small .stack .data Cadena1 DB 'Hola Mundo.$'

.code

programa: mov ax, @data mov ds, ax mov dx, offset Cadena1 mov ah, 9h mov ah, 4ch int 21h end programa

La directiva .model define el tipo de memoria que se utilizar,la directiva .stack le pide al ensamblador que reserve un espacio de memoria para las operaciones de la pila;la directiva.code indica que lo que esta a continuacin es nuestro programa; la etiqueta Programa: indica al ensamblador el inicio del programa; la instruccin end Programa marca el final del programa. A continuacin dentro de la etiqueta Programa se coloca @data en el registro ax para despus pasarlo al registro ds ya que no se puede copiar directamente una constante a un registro de segmento. El contenido de @data es el nmero del segmento que ser utilizado para los datos. Luego se guarda en el registro dx un valor dado por offset

Cadena1 que nos da la direccin donde se encuentra la cadena de


caracteres en el segmento de datos. Luego utiliza la opcin 9h (Dada por el valor de ah) de la interrupcin 21h para desplegar la cadena posicionada en la direccin que contiene dx. Por ltimo utiliza la opcin 4ch de la interrupcin 21h para terminar la ejecucin del programa y devolver el control al usuario.

La directiva .data le indica al ensamblador que lo que est escrito a continuacin debe almacenarlo en el segmento de memoria destinado a los datos. La directiva DB es utilizada para Definir Bytes, sto es, asignar a cierto identificador (en este caso "Texto") un valor, ya sea una constante o una cadena de caracteres, en este ltimo caso deber estar entre comillas sencillas ' ' y terminar con el smbolo $. Segmentos La arquitectura de los procesadores x86 obliga al uso de segmentos de memoria para manejar la informacin, el tamao de estos segmentos es de 64kb. La razn de ser de estos segmentos es que, considerando que el tamao mximo de un nmero que puede manejar el procesador esta dado por una palabra de 16 bits o registro, no sera posible accesar a ms de 65536 localidades de memoria utilizando uno solo de estos registros, ahora, si se divide la memoria de la PC en grupos o segmentos, cada uno de 65536 localidades, y utilizamos una direccin en un registro exclusivo para localizar cada segmento, y entonces cada direccin de una casilla especfica la formamos con dos registros, nos es posible accesar a una cantidad de 4294967296 bytes de memoria, lo cual es, en la actualidad, ms memoria de la que veremos instalada en una PC. Para que el ensamblador pueda manejar los datos es necesario que cada dato o instruccin se encuentren localizados en el rea que corresponde a sus respectivos segmentos. El ensamblador accesa a esta informacin tomando en cuenta la localizacin del segmento, dada por los registros DS, ES, SS y CS, y dentro de dicho registro la direccin del dato especfico. Es por ello que cuando creamos un programa empleando el Debug en cada lnea que vamos ensamblando aparce algo parecido a lo siguiente:

1CB0:0102 MOV AX,BX

En donde el primer nmero, 1CB0, corresponde al segmento de memoria que se est utilizando, el segundo se refiere la la direccin dentro de

dicho segmento, y a continuacin aparecen las instrucciones que se almacenaran a partir de esa direccin. La forma de indicarle al ensamblador con cuales de los segmentos se va a trabajar es por medio de las directivas .CODE, .DATA y .STACK. El ensamblador se encarga de ajustar el tamao de los segmentos tomando como base el nmero de bytes que necesita cada instruccin que va ensamblando, ya que sera un desperdicio de memoria utilizar los segmentos completos. Por ejemplo, si un programa nicamente necesita 10kb para almacenar los datos, el segmento de datos nicamente sera de 10kb y no de los 64kb que puede manejar. Movimiento de Datos En todo programa es necesario mover datos en la memoria y en los registros de la CPU; existen diversas formas de hacer esto: puede copiar datos de la memoria a algun registro, de registro a registro, de un registro a una pila, de la pila a un registro, transmitir datos hacia dispositivos externos as como recibir datos de dichos dispositivos. Este movimiento de datos est sujeto a reglas y restricciones. Algunas de ellas son las que se citan a continuacin. .- No es posible mover datos de una localidad de memoria a otra directamente, es necesario primero mover los datos de la localidad origen hacia un registro y luego del registro a la localidad destino. .- No se puede mover una constante directamente a un registro de segmentos, primero se debe mover a un registro de la CPU .- Es posible mover bloques de datos por medio de las instrucciones movs, que copia una cadena de bytes o palabras; movsb que copia n bytes de una localidad a otra; y movsw copia n palabras de una localidad a otra. Las dos ltimas instrucciones toman los valores de las

direcciones definidas por DS:SI como grupo de datos a mover y ES:DI como nueva localizacin de los datos. .- Para mover los datos tambin existen las estructuras llamadas pilas, en este tipo de estructuras los datos se introducen con la instruccin push y se extraen con la instruccin pop. En una pila el primer dato introducido es el ltimo que podemos sacar, esto es, si en nuestro programa utilizamos las instrucciones:

push ax push bx push cx

Para devolver los valores correctos a cada registro al momento de sacarlos de la pila es necesario hacerlo en el siguiente orden:

pop cx pop bx pop ax

Para la comunicacin con dispositivos externos se utilizan el comando out para mandar informacin a un puerto y el comando in para leer informacin recibida desde algun puerto. La sintxis del comando out es:

out dx,ax

Donde dx contiene el valor del puerto que se utilizar para la comunicacin y axcontiene la informacin que se mandar. La sintxis del comando in es:

in ax,dx

Donde ax es el registro donde se guardar la informacin que llegue y dx contiene la direccin del puerto por donde llegar la informacin. Operaciones lgicas y aritmticas Las instrucciones de las operaciones lgicas son: AND, NOT, OR y

XOR, stas trabajan sobre los bits de sus operandos.


Las instrucciones utilizadas para las operaciones algebraicas son: para sumar add, para restar sub, para multiplicar mul y para dividir div. Saltos, ciclos y procedimientos Los saltos incondicionales en un programa escrito en lenguaje ensamblador estn dados por la instruccin jmp, un salto es alterar el flujo de la ejecucin de un programa enviando el control a la direccin indicada. Un ciclo, conocido tambin como iteracin, es la repeticin de un proceso un cierto nmero de veces hasta que alguna condicin se cumpla. En estos ciclos se utilizan los brincos condicionales basados en el estado de las banderas. Por ejemplo la instruccin jnz que salta solamente si el resultado de una operacin es diferente de cero y la instruccin jz que salta si el resultado de la operacin es cero. Por ltimo tenemos los procedimientos o rutinas, que son una serie de pasos que se usaran repetidamente en el programa y en lugar de escribir

todo el conjunto de pasos unicamente se les llama por medio de la instruccin call. Un procedimiento en ensamblador es palabra Proc y termine con la palabra ret. aquel que inicie con la

Realmente lo que sucede con el uso de la instruccin call es que se guarda en la pila el registro IP y se carga la direccin del procedimiento en el mismo registro, conociendo que IPcontiene la localizacin de la siguiente instruccin que ejecutara la cpu, entonces podemos darnos cuenta que se desva el flujo del programa hacia la direccin especificada en este registro. Al momento en que se llega a la palabra ret se saca de la pila el valor de IP con lo que se devuelve el control al punto del programa donde se invoc al procedimiento. Es posible llamar a un procedimiento que se encuentre ubicado en otro segmento, para sto el contenido de CS (que nos indica que segmento se est utilizando) es empujado tambin en la pila.

El lenguaje ensamblador es el lenguaje de programacin mas bsico para cualquier procesador como sabemos un sistema de arquitectura de computadoras comunmente se divide en cinco capaz las cuales son: hardware, firmware, ensamblador, kernel, sistema operativo y aplicaciones. La capa de ensamblador comnmente define un grupo de instrucciones relativamente fcil de entender por el ser humano estndar para un grupo de procesadores. Este lenguaje casi no contiene instrucciones abstractas lgicas lo que hace es trabajar directamente con operaciones del CPU y estas instrucciones de forma mas directa con los componentes de las computadoras. Otra caracterstica del lenguaje ensamblador es que es bastante poderoso pues no tiene las limitaciones que vienen con lenguajes de programacin de capaz superiores aunque resulta mas tedioso trabajar con este.

Breve repaso historico


En la dcada del 40, surgen representaciones para estas instrucciones. Ya no eran unos y ceros, sino que se los representaba con palabras ms fciles de recordar (instrucciones mnemnicas) como MOVE, LDA, ADD, etc.

La relacin con el cdigo binario era directa, por cada instruccin MNEMONICA, existe una instruccin en binario y viceversa. As se construyen los primeros traductores que pasarn la instruccin MNEMONICA a Binario. Estos traductores recibieron el nombre de ensambladores, y se convirtieron en el primer lenguaje de los ordenadores. De todas maneras, la programacin era complicada y difcil, porque se acercaba mucho a la forma de operar de las computadoras y no al lenguaje humano (llamados lenguajes de programacin de alto nivel).

Por que usar ensamblador?


En primer lugar Por que debera yo estar aprendiendo un lenguaje relativamente complicado si hoy en da existen lenguajes que con menor esfuerzo producen operaciones mucho mas complejas?

Para entender esto lo que hice fue empezar a pensar como funcionaba una arquitectura desde las capas mas bajas, a grandes rasgos junta varias operaciones de su cerebro(CPU) para procesar nmeros guardados en sus registros o memorias, para operaciones mas complicadas necesita juntar un mayor nmero de instrucciones o repetir muchas veces una misma, esto para ahorrar recursos al fabricar una maquina. Por ejemplo si se necesitara un procesador cuya nica operacin fuera sumar un nmero "A" a un nmero "B" se podra construir el procesador para que sepa hacer eso, otra forma sera si tuvieramos una operacin mas sencilla que sume uno a un valor "A" que recibe y este proceso lo repetimos "B" veces

obtendremos los mismos resultados, de forma mas lenta pero ahorrariamos puertos de entrada para el procesador y adems sera mas fcil de construir.

De la misma forma en capas superiores se juntan un grupo de operaciones que hacen mas fcil implementar las instrucciones para la maquina, pero que no siempre son las mas ptimas.

Ejemplo
Ahora para ejemplificar esto y entender mejor las caractersticas de este y porque es tan importante que un desarrollador de software entienda las mismas utilizare el lenguaje ensamblador versin x86, debido a que paso gran tiempo trabajando en linux y Ubuntu ya trae un compilador de ensamblador x86 a cdigo maquina y tambin para convertirlo a cdigo ensamblador. Este es un cdigo en Ansi C que parece bastante sencillo, todo lo que hace es guardar dos valores "a" y "b" en la memoria despues los divide e imprime los resultados en la terminal: ?
1 2 3 4 5 6 7 8 9
} </stdio.h> printf("%d",(a/b)); return 0; int main(int argv, char** argc){ int a = 5, b = 4; #include <stdio.h>

Una vez que constru este cdigo utilice el compilador "gcc" el cual ya viene por defecto en ubuntu para generar cdigo ensamblador, lo cual lo hice agregando el parmetro "-S" y la direccin del archivo: ?
1
gcc -S ejemplo01.c

Lo cual me genero el archivo ejemplo01.s con el cdigo en ensamblador x86. Para compilarlo en un ejecutable utilice el comando "cc" ?
1
cc ejemplo01.s

Que me gener un ejecutable con el nombre a.out. El cdigo generado es el siguiente(quitando algunas lineas de encabezado y cosas innecesarias y agregando comentarios): ?
1 2 3 4 5 main: 6 .LFB0: 7 pushq %rbp 8 movq %rsp, %rbp ;Mueve la posicion de la cima de la pila a otro registro 9 10 subq $32, %rsp 11 movl %edi, -20(%rbp) ;Recibe valores pararametro y los pone en la memoria principal 12 movq %rsi, -32(%rbp) ; 13 14 15 16 17 movl $10, -4(%rbp) movl $2, -8(%rbp) movl -4(%rbp), %eax movl %eax, %edx sarl $31, %edx ;Mueve el valor 10 a una posicin en la memoria principal ;hace lo mismo con el siguiente valor ;Mueve el valor 10 de la memoria principal a un registro ;Ahora mueve el valor 10 a un registro diferente ;Mueve algunos bits menos significativos ;Reduce la direccin de la posicin para asignar los valores a trabajr ;Busca la cima de la pila para manejar la memoria principal(RAM) .LC0: .string "%d" .text .globl main

18 19 20 21 22 23 24 25 26 27

idivl -8(%rbp) movl %eax, %esi

;Divide el valor 10 entre el valor que esta en la memoria principal en este caso 2 ;La funcion anterior pone el resultado de la division en el registro %eax luego este ; Se mueve a el registro %esi para mandarlo como parametro a la funcion printf de C

movl $.LC0, %edi movl $0, %eax call printf movl $0, %eax ; leave ret

;Otro parametro a la funcion en este caso un puntero hacia el string que imprimira en pantalla ;Otro parametro para definir cantidad de parametros

;Manda a llamar a la funcion printf y en este momento se escribe el valor de la division en pan

;Pierde el control del programa ;Regresa a la funcion donde fue llamado si no es el caso termina la ejecucion del programa

Como podemos observar la cantidad de instrucciones que tenemos que definir es mayor que en Ansi c esto puede ser una ventaja o una desventaja. La desventaja de esto es obvia aumenta la complejidad para generar cdigo til. La ventaja esta en que nos deja definir caracteristicas que en otros niveles computacionales el los lenguajes nos esconden por ejemplo el manejo directo de la memoria y o la capacidad de utilizar los registros como queramos los cuales son mas faciles y rapidos de acceder por el procesador. Al pasar el lenguaje de cdigo Ansi C el cual es mas facl de construir no genera un cdigo completamente timo para utilizarse por ejemplo en este caso mueve distintos valores a la memoria principal cuando podra haber movido uno solo y el otro solamente definirlo como parametro en la divisin ahorrandonos una posicin en la memoria principal. Esto nos da la dea de que los lenguajes de programacin no tienen los algoritmos para traducir un lenguaje a otro nivel (Si es que existe uno) desperdiciando recursos que en algnas ocaciones pueden ser bastante valiosos(otra ventaja de ensamblador). Tomando en cuenta que en sistemas integrados trabajare con microcontroladores con una capacidad de almacenamiento y procesamiento muy limitada esta es una de esas ramas en las que nos es muy conveniente conocer las caracteristicas del lenguaje ensamblador.

También podría gustarte