Introducción al lenguaje Ensamblador

| 2013-04-27 | No hay comentarios »

1.1. Lenguaje de Bajo Nivel.

Se denomina lenguaje máquina a la serie de datos que la parte física de la computadora o hardware, es capaz de interpretar.

Una computadora digital o, mejor dicho, su parte física, sólo distingue datos de tipo binario, es decir, constituidos por dos únicos valores a los que se denomina valor 0 y valor 1 y que, físicamente, se materializan con tensiones comprendidas entre 0 y 4.0 voltios y entre 4 y 5 voltios, respectivamente. Para representar datos que contengan una información se utilizan una serie de unos y ceros cuyo conjunto indica dicha información.

La información que hace que el hardware de la computadora realice una determinada actividad de llama instrucción. Por consiguiente una instrucción es un conjunto de unos y ceros. Las instrucciones así formadas equivalen a acciones elementales de la máquina, por lo que al conjunto de dichas instrucciones que son interpretadas directamente por la máquina se denomina lenguaje máquina.

El lenguaje máquina fue el primero que empleo el hombre para la programación de las primeras computadoras. Una instrucción en lenguaje máquina puede representarse de la siguiente forma:

011011001010010011110110

Esta secuencia es fácilmente ejecutada por la computadora, pero es de difícil interpretación, siendo aun mas difícil la interpretación de un programa (conjunto de instrucciones) escrito de esta forma. Esta dificultad hace que los errores sean frecuentes y la corrección de los mismos costosa, cuando no imposible, al igual que la verificación y modificación de los programas.

La anterior secuencia de dígitos binarios (bits) puede indicar a la computadora que:

<<Traslade el contenido de la posición de memoria X a la posición de memoria Y.>>

Si lo vemos escrito de esta forma, lo entenderemos fácilmente, ya que está en nuestro lenguaje natural, pero la máquina elemental será incapaz de entender nada. Vemos, pues, que la forma de indicar a la máquina lo que debe hacer es totalmente diferente de la indicar a un ser humano lo mismo, por lo que deben emplearse sistemas de traducción de una forma a otra.

Ya se ha dicho que en un principio el programador empleaba directamente el lenguaje máquina. En este caso el traductor era el programador; pero vimos también los problemas que esto causaba.

Con la práctica en el manejo de la máquina se cayó en la cuenta de que se podría utilizar la propia máquina para ayudar en la traducción de estos programas. Es decir, que si a una máquina elemental se le dotaba de un programa, también elemental, que tradujera un número determinado de caracteres de caracteres alfabéticos en una secuencia de unos y ceros, se podría escribir un programa constituido por una secuencia de grupos de caracteres alfabéticos, en la que cada uno de los grupos indicaría una acción a realizar por el ordenador y, una vez escrito el programa, sería la propia máquina la que pasaría los grupos de caracteres a bits.

Las ventajas de esto son evidentes, ya que para el hombre resulta mas fácil manipular grupos de caracteres y la traducción se haría de manera automática. Por ejemplo, se podría escribir:

TRASLADAR 11010110, 00011101

Esto indicaría que el contenido de la posición 11010110 había que pasarlo a la posición 00011101 si se sabe que al grupo alfabético TRASLADAR le corresponde la secuencia de bits 11110101. La máquina traduciría la anterior instrucción como:

11110101 11010110 00011101

Al grupo alfabético se le denomina mnemotécnico, y existirá un mnemotécnico por cada instrucción. Se le da este nombre porque sirve para recordar con mayor facilidad el conjunto de instrucciones de una determinada máquina.

De esta forma aparecieron los lenguajes ensambladores (Assembler, en inglés). Poco a poco, con el avance de la programación (Software), estas primeras y sencillas ayudas se fueron haciendo más complejas, permitiendo que, además de los mnemotecnias correspondientes a la operación a realizar, se pudieran emplear otros para indicar, por ejemplo, los operandos. La anterior instrucción se podría escribir de la siguiente forma:

TRASLADAR POS-A POS-B

Que nos resulta de más fácil comprensión.

También se introdujo la posibilidad de indicar a la computadora la dirección de un salto en la secuencia de ejecución de un programa mediante la utilización de etiquetas.

A los programas que permiten pasar del programa escrito de esta manera (programa fuente, en ensamblador) al lenguaje máquina también se les denomina normalmente ensambladores. Estos traductores, como ya se ha dicho, se fueron complicando cada vez más para que la labor del programador fuera más fácil, incluyendo los denominados directivos del ensamblador, que son órdenes o informaciones que el programador da al traductor, no instrucciones de lenguaje máquina.

Aun con todas estas sotisficaciones y ayudas, el programador de lenguaje ensamblador debe conocer perfectamente el sistema físico (Hardware) de la máquina con que trabaja, pues aunque emplee mnemotécnicos, etiquetas, etc., éstas sirven para indicar una posición de memoria determinada, un registro o cualquier otra parte de la máquina.

Por eso se dice que el lenguaje ensamblador es un lenguaje de bajo nivel, es decir, ligado con el <<hard>> concreto de una determinada máquina. Aquí radica la diferencia importante con los lenguajes más complejos, llamados de alto nivel, como el Basic, Pascal, Cobol, etc., ya que en éstos el programador no tiene porque reconocer el <<hard>> de la máquina. Trabaja con variables, constantes e instrucciones simbólicas, y es el traductor quien las transforma en las direcciones apropiadas.

1.2. VENTAJAS DE LOS LENGUAJES ENSAMBLADORES

El corazón de la computadora es el microprocesador, éste maneja las necesidades aritméticas, de lógica y de control de la computadora.

El microprocesador tiene su origen en la década de los sesenta, cuando se diseño el circuito integrado (IC por sus siglas en ingles) al combinar varios componentes electrónicos en un solo componente sobre un «chip» de silicio.

Los fabricantes colocaron este diminuto chip en un dispositivo parecido a un ciempiés y lo conectaron a un sistema en funcionamiento. A principios de los años setenta Intel introdujo el chip 8008 el cual, instalado en una computadora terminal, acompaño a la primera generación de microprocesadores.

En 1974 el 8008 evoluciono en el 8080, un popular microprocesador de la segunda generación para propósitos generales. En 1978 Intel produjo la tercera generación de procesadores 8086, para proporcionar alguna compatibilidad con el 8080 y que representan un avance significativo de diseño.

Después, Intel desarrollo una variación del 8086 para ofrecer un diseño sencillo y compatibilidad con los dispositivos de entrada/salida de ese momento. Este nuevo procesador, el 8088, fue seleccionado por IBM para su computadora personal en 1981. Una versión mejorada del 8088 es el 80188, y versiones mejoradas del 8086, son los 80186, 80286, 80386, 80486 y el Pentium, cada uno de ellos permite operaciones adicionales y más procesamiento.

La variedad de micro computadoras también ocasiono un renovado interés en el lenguaje ensamblado, cuyo uso conlleva a diferentes ventajas:

∑Un programa escrito en el lenguaje ensamblador requiere considerablemente menos memoria y tiempo de ejecución que un programa escrito en los conocidos lenguajes de alto nivel, como Pascal y C.

∑El lenguaje ensamblador da a un programador la capacidad de realizar tareas muy técnicas que serian difíciles, si no es que imposibles de realizar en un lenguaje de alto nivel.

∑El conocimiento del lenguaje ensamblador permite una comprensión de la arquitectura de la maquina que ningún lenguaje de alto nivel puede ofrecer.

∑Aunque la mayoría de los especialistas en Software desarrolla aplicaciones en lenguajes de alto nivel, que son más fáciles de escribir y de dar mantenimiento, una practica común es re codificar en lenguaje ensamblador aquellas rutinas que han causado cuellos de botella en el procesamiento.

∑Los programas residentes y rutinas de servicio de interrupción casi siempre son desarrollados en el lenguaje ensamblador.

Los lenguajes de alto nivel fueron diseñados para eliminar las particularidades de una computadora especifica, mientras que un lenguaje ensamblador esta diseñado para una computadora especifica, o, de manera más correcta, para una familia especifica de microprocesadores.

A continuación se listan los requisitos para aprender el lenguaje ensamblador de la PC:

∑Tener acceso a una computadora personal de IBM (cualquier modelo) o una compatible.

∑Una copia del sistema operativo MS-DOS o PC-DOS y estar familiarizados con su uso.

∑Una copia de un programa ensamblador. Las versiones de Microsoft son conocidas como MASM y QuickAssembler: TASM es de Borland y OPTASM es de System.

Para el aprendizaje de lenguaje ensamblador no es necesario lo siguiente:

∑Conocimiento previo de un lenguaje de programación, aunque tenerlo puede ayudarle a comprender algunos conceptos de programación más rápido.

∑Conocimiento previo de electrónica o circuiteria.

2.1.1.UNIDAD CENTRAL DE PROCESO.

La CPU constituye el cerebro de una computadora digital, pues realiza todas las operaciones aritméticas y lógicas sobre los datos y además controla todos los procesos que se desarrollan en la computadora. Por ejemplo, para que se ejecute un a instrucción, ésta debe estar en el interior de la CPU, concreta mente en la UC y si hay que realizar cálculos, interviene la UAL. Veamos como funciona cada una de ellas.

∑Unidad de Control.

Para realizar su tarea la UC necesita conocer, por un lado, la instrucción y, por otro, una serie de informaciones adicionales que deberá tener en cuenta para coordinar, de forma correcta, la ejecución de la instrucción. El resultado de la interpretación de dichas informaciones son una serie de órdenes a los diferentes elementos de la computadora.

La UC no emite todas las órdenes a la vez, sino siguiendo una determinada secuencia. Para ello utiliza un elemento que le va indicando el instante en que debe ejecutar una determinada fase de la instrucción. A este elemento se le denomina Reloj, y se dice que sincroniza las acciones de la UC; cuanto más rápido marque el tiempo, más rápida será la ejecución de la instrucción. Sin embargo, hay un limite, ya que, si marca excesivamente rápido, es posible que no puedan cumplir adecuadamente las órdenes de los diferentes elementos, por lo que se producirán errores.

En la figura 2.2 se esquematiza el conjunto de señales que utiliza la UC y las que genera. Como informaciones adicionales a las instrucciones podemos ver los impulsos de reloj y los indicadores de estado. Los indicadores de estado son una serie de bits que se modifican según resultados de las operaciones anteriores guardando una memoria histórica de los acontecimientos precedentes para que, en función de dichos acontecimientos, pueda la UC tomar decisiones.

*ASM = Ensamblador

¿POR QUE APRENDER ENSAMBLADOR?

He escuchado comentarios en todos lados de gente que habla de este lenguaje como algo del pasado, algunos dicen que programar en ASM ya no vale la pena.

La verdad es que el lenguaje ASM estará presente mientras existan las computadoras, todas las computadoras utilizan ASM aunque no lo puedas ver, cada procesador utiliza un lenguaje ensamblador diferente.

Si haces ingeniería inversa a cualquier programa de computadora veras que el código ASM esta allí. Los compiladores de lenguajes de programación de alto nivel como C++, Pascal… lo que hacen es traducir tu código a ASM. Es lo único que la computadora entiende.

¿Pero por que aprender Ensamblador?

Hay diferentes razones por las que alguien aprender ASM, aquí hay una lista de las mas comunes: + Necesitas acceder directamente al Hardware de la computador+ Quieres aprender mas sobre el funcionamiento de tu computadora

+ Planeas trabajar en la creación de programas o sistemas operativos
para Sistemas Empotrados y necesitas un poco de experiencia
+ Quieres impresionar a tus amigos programadores mostrando les código
que no entiendan
+ Necesitas crear algún programa gráfico que sea muy muy rapido
+ Piensas dedicarte a programar para la Demoscene y hacer tus propios
demos de 256 bytes
+ Estudias Ingenieria y te toca un curso sobre el tema que no puedes
evitar
+ Te divierte la ingeniería inversa y quieres aprender sobre el cracking
+ Quieres crear un compilador de tu propio lenguaje de programación
+ Quieres seguir el consejo de algunos Gurus que dicen que los Gurus
programan en ASM
+ Se te hace interesante y te diviertes moviendo bytes en la memoria RAM
+ ETC

¿EN DONDE GUARDO MIS DATOS? (8086 16 bit)

En ensamblador existen 4 registros de propósito general, puedes pensar en estos registros como variables que siempre están disponibles para que guardes datos en ellas. Los registros se llaman:

+ AX (Acumulador)
+ BX (Base)
+ CX (Contador)
+ DX (Datos)

Estos registros vamos a utilizarlos todo el tiempo. La razón por la que existen es la velocidad. En lugar de que la computadora tenga que buscar en la memoria la dirección de una variable, estos registros son parte del mismo CPU lo que permite que el acceso a ellos sea muy muy rápido.

Los registros pueden contener un rango de números enteros positivos entre 0 y 65535 que en hexadecimal es 0x0000 a 0xFFFF

Si lo que necesitas es guardar un numero entero negativo puedes guardar números en el rango -32768 a 32768. Mas adelante veremos como es posible distinguir si un dato guardado es negativo o positivo, veras que se hace sencillo.

La razón por la que solo disponemos de esos rangos de números es porque los registros tienen capacidad solo para 16 bit (2 bytes).

PARTIENDO LOS REGISTROS EN DOS

Cada uno de los cuatro registros puede dividirse en dos partes, una baja (low) y una alta (high) quedando de esta manera:

AX (16 bit) se puede dividir = AH, AL (8 bit cada registro)
BX (16 bit) se puede dividir = BH, BL (8 bit cada registro)
CX (16 bit) se puede dividir = CH, CL (8 bit cada registro)
DX (16 bit) se puede dividir = DH, DL (8 bit cada registro)

Simplemente nos estamos refiriendo a el mismo registro en dos partes, una baja y una alta. Esto nos permite utilizar cada registro como si fueran dos. Como dije antes los 4 registros pueden guardar un valor de 16 bit (2 byte), entonces si solo necesitas guardar 8 bit (1 byte) puedes utilizar una de las divisiones del registro.

Supongamos que hemos guardado en AX el numero 0xAABB (hexadecimal):

AX = 0xAABB = 43707 en decimal

podemos referirnos a cada uno de los bytes guardados en AX de esta manera:

AH = 0xAA = 170 en decimal
AL = 0xBB = 187 en decimal

y completo AX = 0xAABB = 43707 en decimal

Como puedes ver el registro puede utilizarse para diferentes propósitos
dependiendo del valor que deseamos utilizar. Mas adelante profundizaremos un
poco en esto y veremos que otros usos tienen los registros. Recuerda
aprenderlos de memoria ya que son indispensables para programar en ASM.

¿COMO GUARDO DATOS EN LOS REGISTROS?

En lenguajes como C, C++, Java… para guardar un valor en una variable lo hacemos de esta manera:

variable = dato

En ensamblador esto es diferente ya que estamos trabajando directamente con la
memoria de la computadora. Para copiar datos desde una «fuente» a un «destino»
utilizamos la instrucción de ensamblador MOV (mover) de esta forma:

mov [destino],[fuente]

Es similar a escribir en otro lenguaje de programación:

[destino] = [fuente]

Por ejemplo, si queremos guardar el valor 0xAA en AH hacemos lo siguiente:

mov ah,0xAA

Suponiendo que AL contiene 0x00 entonces AX queda de esta manera:

AX = 0xAA00
AH = 0xAA
AL = 0x00

Si queremos guardar el valor 0xAABB en BX escribimos:

mov bx,0xAABB

Si ahora queremos copiar el valor de BX en AX escribimos:

mov ax,bx

AX contiene ahora el mismo valor que BX.

La instruccion MOV copia el valor de la fuente al destino pero no elimina el
dato de la fuente, por ejemplo:

mov ax,0x10
mov bx,ax
mov cx,bx

COMENTAR EL CÓDIGO

En ensamblador es muy importante comentar el código, los comentarios se utilizan para indicar lo que hace una linea de código, para dividir secciones del programa, etc. Los comentario se empiezan con «;» y terminan al final de la linea, por ejempo:

mov cx,0x4433 ; Este es un comentario

La importancia de los comentarios se demuestra por ejemplo si te encuentras
con esto:

mov ax,0x13

En este codigo sabemos que 0x13 esta siendo guardado en AX pero no sabemos el por que. En cambio en este código:

mov ax,0x13 ; Modo Grafico VGA de 320x200x256 el comentario ahora nos explica la razón del 0x13.

Si haces un programa con 200 lineas de código ensamblador y no lo comentas, puede que se te haga muy difícil entenderlo 3 semanas después, si trabajas con un equipo de programadores ellos lo agradecerán mucho.

Acerca del autor: Rodrigo Paszniuk

Ingeniero Informático, amante de la tecnología, la música, el ciclismo y aprender cosas nuevas.

Posts Relacionados

  • Ejercicios resueltos en Ensamblador 8086
  • Ejemplos de programas en Ensamblador 8086



SEGUÍNOS EN FACEBOOK


GITHUB