Copy Link
Add to Bookmark
Report

3 - Curso de Ensamblador II

eZine's profile picture
Published in 
NovaHack Zine
 · 1 year ago

[ Fyncher ]

CAPITULO 3 --> PRIMEROS PASOS. ¿COMO ES UN PROGRAMA EN ENSAMBLADOR?. HERRAMIENTAS

Hola a todo el mundo. Primero de todo deciros que he cambiado de correo
Ahora mi correo es fyncher@hotmail.com, antes era k_mbe_t@hotmail.com
Dicho esto empezamos

1.- CONTENIDO DE UN MODULO EN ENSAMBLADOR

Lo primero con lo que deber≠ais familiarizaros es con el aspecto de un programa escrito en ensamblador, mas que nada para que no os asusteis si abris uno a pelo. OJO con esto. Si abris un .EXE con el Visual C++, por ejemplo, lo que os encontrareis es el codigo ensamblador del fichero, pero sin los mnemonicos, o sea, que en vez de MOV, os aparecera el numero que corresponde a MOV para que el micro entienda la instruccion. Eso NO ES UN FICHERO EN ENSAMBLADOR COMO LOS QUE VAMOS A VER. En el curso se usan los mnemonicos.
Vamos a lo que interesa una vez aclarado esto. En un programa sencillo de ensamblador podreis diferenciar 3 partes basicas que se denominan SEGMENTOS. Son las siguientes:

  • SEGMENTO DE DATOS --> dentro se declaran las variables del programa
  • SEGMENTO DE PILA --> se define una pila para usar con el programa. La pila se usa para almacenar variables temporalmente, por ejemplo. De hecho en las llamadas a funciones de lenguajes como C, los argumentos de las mismas se almacenan en la pila y la funcion los saca.
  • SEGMENTO DE CODIGO --> dentro esta el codigo de nuestro programa. Podemos usar procedimientos y macros (parecido a las funciones y macros de C)

Bueno esto es la estructura basica, puede variar, pero con esto podreis hacer cualquier cosa.
Vamos a ver si os habiais estudiado el capi≠tulo 1. ¿De quÈ os suena eso de segmento?

.................................

Pues si≠, de la segmentacion, muy bien. Como recordareis la memoria de un 80x86 esta segmentada, es decir, "dividida" (pero es una division logica, no fi≠sica) en porciones de 64 KB, y dentro de cada una de estas porciones las direcciones de memoria se indexan usando un par SEGMENTO:OFFSET. Pues vamos a enlazar esto con lo que estamos explicando.
Hemos dicho que tenemos 3 segmentos basicos en nuestro programa. Entonces vamos a almacenar en los registros de segmento la direccion donde empiezan. En DS almacenaremos la direccion donde empiecen los datos, en CS la direccion donde empieza el codigo y en SS la direccion donde empieza la pila. ¿Como hacemos eso? Pues muy facil, con la directiva ASSUME. No os preocupeis, ahora mismo os pongo un esqueleto de programa en ensamblador y lo veis. Ah, y mas adelante explico lo que es una directiva.

Esqueleto:

; Segmento de datos 
DATOS SEGMENT
; Definiciones de variables
-------------------
-------------------
-------------------
DATOS ENDS

; Segmento de Pila
PILA SEGMENT
; Lo que se pone aqui ya lo veremos
PILA ENDS


; Segmento de codigo
CODIGO SEGMENT
; Procedimiento principal
INICIAL PROC
ASSUME CS:CODIGO, DS:DATOS, SS:PILA
; Codigo de nuestro programa
-------------------
-------------------
-------------------
INICIAL ENDP
CODIGO ENDS
END INICIAL

Uyuyuyuy que ya os empezais a liar. Venga vamos despacio a explicarlo todo. Lo primero es que habreis diferenciado los 3 segmentos de los que os hablaba: DATOS, PILA y CODIGO. El inicio de un segmento se marca con

	nombre-segmento SEGMENT

Y como habreis deducido su final se marca con

	nombre-segmento ENDS

Ah, en ensamblador podeis escribir todo con mayusculas o minusculas, es igual.
Asi≠ que pasamos rapido por los segmentos de datos y pila, que de momento estan claros, y vamos al de codigo. Lo primero que os llamara la atencion es ese:

	INICIAL PROC

Pues eso no es mas que el procedimiento principal. Para los que sepais C es como el main(). En ensamblador debereis dividir vuestro programa en procedimientos, al menos uno. Todos con el nombre que os de la gana. De como sabe el compilador cual es el principal os enterareis ahora. Bueno, os habris dado cuenta de que el inicio y fin de un procedimiento se marca asi≠:

	nombre-proc PROC 
......
......
......
nombre proc ENDP

NOTA: Despues de PROC le podeis añadir FAR o NEAR. Ya veremos la diferencia mas adelante. De momento si no se pone nada se toma como FAR. FAR y NEAR son directivas tambien.
Ahora vamos con el ASSUME. Esto es lo que os decia antes de almacenar en los registros de segmento la direccionn de comienzo de cada segmento. Ahi≠ lo teneis, es asi≠ de facil. Bueno el resto creo que lo entendeis todo. Hasta llegar a

	END INICIAL

Eso lo que hace es decir que ya se ha terminado el fichero, y es aqui≠ donde se marca el punto de entrada al programa, al procedimiento principal. La sintaxis es

	END procedimiento-de-entrada

¿Veis que facil? De momento vamos bien. Ahora os voy a explicar las 3 cosas que os faltan para poder empezar a escribir codigo: Como son las instrucciones, como se definen variables y que se pone en el segmento de pila. Al final os pondre un primer programa de ejemplo, que escribe "Hola Mundo!" por pantalla. A medida que avancemos os ire poniendo mas codigo, y asi≠ iremos aprendiendo. Vamos al ataque.


Un programa fuente ensamblador contiene dos tipos de sentencias:

  • Instrucciones --> Son los mnemonicos (el MOV, el ADD...)
  • Directivas --> Le indican al ensamblador que hacer con las operaciones y los datos

1.1 Instrucciones

El formato de una instruccion es el siguiente:

	[etiqueta]  instr  [operandos]  [comentario]

Los corchetes indican que el campo especificado entre ellos es opcional. Vamos a ver los campos por separado

Campo de etiqueta:
Es el nombre simbolico de la primera posicion de una instruccion, puntero o dato. Consta de hasta 31 caracteres que pueden ser las letras de la A a la Z, los numeros del 0 al 9 y algunos caracteres especialecomo @, _,. y $. Reglas:

  • Si hay un punto . debe colocarse como primer caracter de la etiqueta.
  • El primer caracter no puede ser un numero.
  • No se pueden utilizar los nombres de instrucciones o registros como nombres de etiquetas.

Veremos mas sobre las etiquetas (que son etiquetas NEAR o FAR) mas adelante.

Campo de instruccionn:
Contiene el mnemonico de una culquiera de las instrucciones. aprovecho para decir que no voy a dedicar uncapi≠tulo a poner todas las instrucciones, mas que nada porques eso es ti≠pico de una gui≠a de referencia, y no de aprendizaje. Las iremos viendo segunn vayan saliendo.

Campo de operandos:
Indica cuales son los datos implicados en la operacionn. Puede haber 0, 1 o 2; en el caso de que sean dos al 1∫ se le llama destino y al 2∫ -separado por una coma- fuente.

                mov ax, es:[di]     -->     ax         destino 
es:[di] origen

Los operandos pueden ser registros (de 8 o 16 bits), direcciones de memoria (de 8 o 16 bits) o valores inmediatos (de 8 o 16 bits). ¿En que se diferencia un valor inmediato de 8 bits de uno de 16? Pues muy facil. Con 8 bits solo podremos representar numeros entre 0 y 2exp8 -1 (255). El 256 y sucesivos ya necesitarian 16 bits para representarse

Campo de comentarios:
Los comentarios en ensamblador se ponen con punto y coma <<;>>. Cuando se encuentra este si≠mbolo todo lo que venga despues hasta el fin de linea no se toma en cuenta.

Bueno aqui≠ hay que pararse para hablar un poco mas de los operandos. Hemos dicho que pueden ser de 3 tipos, pero realmente son 7. ¿Como es eso? Pues para entenderlo habra que ver los MODOS DE DIRECCIONAMIENTO. Como concepto previo intuitivo pensad en el numero 150. Es un operando de tipo valor, ¿verdad? Pues bien, dependiendo de como lo escribamos puede representar al numero 150 o a la direccion de memoria numero 150. Asi≠, en realidad con el numero 150 tenemos 2 posibles formas de referirnos a un dato: un valor directamente o una posicion de memoria (la 150) que almacena un valor. Para simplificar vamos a ver las diferentes formas de referenciar un dato (los modos de direccionamiento).


NOTA: En los ejemplos los modos de direccionamiento se apreciaran en el segundo operando, el primero sera siempre un registro.

- Direccionamiento inmediato:
El operando es un numero, una constante

                 		ej.: mov ax, 69

- Direccionamiento de registro:
El operando es un registro. Por cierto, el operando fuente y el destino deben ser de igual tamañoo.

ej. : mov dx, ax
mov bh, cl

- Direccionamiento directo o absoluto:
El operando es un numero que representa una direccion de memoria. Para saber que es una direccion de memoria se pone un numero entre corchetes. Ahora una pregunta. La direccionn de memoria de la que hablamos, ¿relativa a que segmento es? ¿a CS, a DS o a SS? ¿ o a ES (que se puede utilizar como segmento extra auxiliar). Bueno si no se dice nada se refiere a DS. A ver no os perdais. ¿Que tenemos en DS? Pues variables, ya veremos como se definen, pero son variables. Si yo escribo DS:[0](SEGMENTO:OFFSET), ¿que es eso? Pues el primer byte de la primera variable definida. Si mi primera variable es un numero de tipo byte, pues lo que tenemos es la variable entera. Asi≠, si yo pongo DS:[59] estamos en el byte 59 del segmento de datos (de mis variables), que puede ser el inicio de una variable o el segundo byte de la misma, o el tercero....


Espero que me hayais entendido, quiero que tengais siempre presente el concepto de segmento. Pero si no entendeis esto ahora tranquilos, en el codigo del final lo vereis claro

ej. : MOV ax,[57D1h] --> mover a ax el valor almacenado en la dir. [57D1h] (offset de DS, en hexadecimal)
MOV ax,es:[429Ch]

En el segundo caso estamos usando ES, ¿ein?. Pues si, podeis definir, por ejemplo, un segundo segmento de datos y meter mas variables dentro. Ya sabeis, los basicos son 3, pero podeis hacer mas.


- Direccionamiento indirecto:
El operando se encuentra en una direccion señalada por un registro de segmento*16mas un registro base (BX/BP) o I≠ndice (SI/DI). (Nota: BP actua por defecto con SS).

ej. : mov ax,[bp] ; ax = [ss*16+bp]
mov es:[di],ax ; [es*16+di] = ax


- Indirecto con Indice o indexado:
El operando se encuentra en una dirección determinada por la suma de un
registro de segmento*16, un registro de índice, SI o DI y un desplazamiento de 8 ó 16 bits.

ej. : mov ax, tabla[di] ; es igual que mov ax, [tabla+di]. Como arrays en C
mov bx, es:tabla[si]

- Indirecto con base e indice o indexado a base:
El operando se encuentra en una direccion especificada por la suma de un registro de segmento*16, uno de base, uno de i≠ndice y opcionalmente un desplazamiento de 8 o 16 bits:

ej. : mov ax, tabla[bx][di]
mov cs:tabla[bx][si] ; si, CS puede almacenar variables, yo nunca dije que no XD

¿Habeis visto que movida con los modos de direccionamiento? Bueno no os preocupeis, al final usareis 2 o 3, y os acostumbrareis. Los 2 ultimos son muy parecidos a manejar tablas (arrays) en C o Pascal

1.2 - Directivas

Como ya dijimos sirven para darle informacion al ensamblador de como son o que hacer con los datos. La sintaxis es

		[nombre]  nombre_directiva  [operandos]  [comentario]

Solo es obligatorio el campo ´nombre_directivaª; los campos han de estar separados por al menos un espacio en blanco. La sintaxis de ´nombreª es analoga a la de la ´etiquetaª de las li≠neas de instrucciones, aunque nunca se pone el sufijo ´:ª. El campo de comentario cumple tambien las mismas normas.

Directivas hay muchas pero las que vamos a ver ahora son las de definicion de variables. Asi≠ entendereis lo que va dentro del DATOS SEGMENT ese. Podeis poner dos cosas:

- Definicion de si≠mbolos.
Se usa la directiva EQU. Sirve para asignar nombre simbolicos a expresiones fijas (constantes)
ej .: pi EQU 3.14 --> ahora podeis escribir "pi" en el codigo y sera 3.14 (#define en C)

Tambien se puede usar = para asignar nombres a expresiones variables
ej .: edad = 22 --> edad la podeis variar

- Definicion de datos.
Para definir variables. Hay 5 posibles segun el tamaño del tipo de datos que definamos:

  • DB --> definir byteej.: cont DB 0 (cont=0. Valdria cont DB ? para no asignar valor)
  • DW --> definir palabra (2 bytes) ej.: long DW 45425
  • DD --> definir doble palabra (ya paso de poner ejemplos, lo habeis pillado ¿no? XD)
  • DQ --> definir cuadruple palabra
  • DT --> definir 10 bytes

Aqui os pongo un ejemplo para que no os perdais y sigais con lo de segmentos en la cabeza

				msg DB "Hola que tal$"

Anda, os he pillado ¿eh? ¿creiais que lo habiais entendido bien eh? XDDD
No os preocupeis que os explico lo que significa. Lo anterior es una cadena, y cada uno de sus elementos es un byte. Es como sigue (suponemos que esta en DS, y q es la primera variable definida):

				DS:[0] --> "H" 
DS:[1] --> "o"
DS:[2] --> "l"
etc

Y lo de "$" es para marcar fin de cadena, como \0 en C.
Ahora entendeis lo que os contaba en el direccionamiento absoluto ¿verdad? Volvedlo a leer venga.

Seguro que ya no dudais con esta pregunta. Si yo tengo

			DATOS SEGMENT 
i DD 2
long DW 5
msg DB "Cadena$"
DATOS ENDS

¿Cuanto valen DS:[0], DS:[1]....?

DS[0] = 0
DS[1] = 0
DS[2] = 0
DS[3] = 2 ; 4 byte para la variable i

DS[4] = 0
DS[5] = 5 ; 2 bytes para la variable long

DS[6] = "C" (o lo que es lo mismo, 67, que es su valor ASCII)
DS[7] = "a"
etc

1.3 - La pila

Bueno, como os dije la pila sirve como almacen temporal auxiliar. ¿Que debeis poner en el segmento de pila de un programa? Pues solo debeis poner el espacio que vais a usar. Se hace asi:

	PILA SEGMENT STACK 
DB 128 (?)
PILA ENDS

Lo de STACK se pone siempre. Esto reserva 128 bytes para la pila. Tambien podriais haber puesto

	DB 128 ('PILA')

Que os reservaria 128 veces el espacio que ocupa la palabra 'PILA'. Que chorrada, ¿no?

Con que pongais lo primero vale. El espacio, bueno, vosotros vereis, 128 bytes esta bien, no creo q almaceneis a la vez mas de 128 bytes dentro. Ah es necesario que definais en vuestro programa un segmento de pila

2 .- PROGRAMA DE EJEMPLO

Hala no sufrais mas que ya os pongo un ejemplo, el ti≠pico "Hola Mundo!". Espero que lo entendais todo

;-------------------------------------------------- 
; Fichero : hola.asm

PILA SEGMENT STACK
DB 128 (?)
PILA ENDS


DATOS SEGMENT
msg DB "Hola Mundo!$"
DATOS ENDS


CODIGO SEGMENT
ASSUME CS:CODIGO, DS: DATOS, SS: PILA ; el assume puede ir antes del proc

Entrada PROC
mov ax, DATOS
mov ds, AX
; esto es para pasar la direccion de comienzo de datos a DS. Regla: no se puede utilizar un registro de
; segmento como destino de mov si la fuente es un valor. La fuente debe ser otro registro, de ahi el paso
; intermedio

mov dx, OFFSET msg
; movemos la direccion de comienzo de msg a DX. Lo de OFFSET es un operador que nos devuelve la posicion de
; una variable cualquiera dentro de un segmento. Aqui no haria falta, ya que si ponemos mov dx, msg
; cargamos en dx ds[0],pero de esta manera es mas elegante y estandar. Tambien valdria LEA DX, msg
;(LEA = Load Efective Address)


mov ah, 9
int 21h
; ahora llamamos a la INT 21 con AH=9, que corresponde a la funcion de sacar por pantalla lo que hay en dx.
; Hay otras 2 formas de sacar algo por pantalla ademas de la INT 21 (interrupcion del DOS), que son usar
; la INT 10 (int de la BIOS) o mover directamente lo que se quiera a una direccion igual o mayor a B800H
; que es donde comienza la memoria de pantalla. Ya veremos lo de memoria de pantalla mas adelante

mov ax, 4c00h
int 21h
; llamamos a INT 21 con AX=4c00h, que corresponde a salir al DOS

Entrada ENDP
CODIGO ENDS
END Entrada
;--------------------------------------------------

Hala ya esta. Para el proximo numero ya empezare a meter codigo mas "serio", que sera el codigo que ire haciendo en practicas.

Bueno para los sedientos de aprender mas os voy a pasar una direccion donde viene TODO sobre el ensamblador del 80x86 y ademas en español. Eso si≠, es bastante denso y requiere de algunas nociones en programacion y arquitectura. De todas formas aqui≠ lo teneis --> http://atc.ugr.es/docencia/udigital/

Venga un saludo y hasta el proximo capi≠tulo


Fyncher

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT