Copy Link
Add to Bookmark
Report

eMc2H 6 - CURSO DE ASM 1era parte <cocinando ASM con Zadkiel>

eZine's profile picture
Published in 
eMc2H
 · 29 Apr 2022

Episodio 1

Este tuto asume 3 cosas:

  1. El lector ya esta familiarizado con algun lenguaje de programacion (C, C++, Visual Basic, Python, el que sea)
  2. conoce el sistema de enumeracion Hexadecimal (base 16) o al menos tiene alguna herramienta para descifrarlo. Osea: no lo explico.
  3. conoce al menos lo mas basico de la organizacion de una computadora, es decir, que es memoria, disco, E/S, entre otros.

DISCLAIMER: Este texto esta hecho solo con fines de informarte acerca del lenguaje ASM. cualquier cosa que hagas o que te pase a ti, a tu computadora, a tu informacion, a tu cerebro o a tu perro es solamente tu culpa, yo no tengo nada que ver con ello asi que no me vengas llorando si tu perro se murio o perdiste todo tu trabajo. aunque si respondere preguntas sobre ASM o cualquier otra cosa.

Breve historia sobre que es ASM

Hace mucho, mucho tiempo, en un laboratorio de computacion, los ingenieros habian creado una computadora programable (de las primeras) cuyo programa estaba escrito en papel perforado. Los programadores tenian que perforar unas tarjetas segun lo que necesitaba el programa, y el mundo era feliz. Despues, los procesadores y las computadoras fueron evolucionando, y asi, los procesadores tuvieron demasiadas instrucciones como para seguir hacien- do los programas a mano (escribiendo el codigo maquina a mano) entonces alguien creo una forma de programar las maquinas mejor que escribir los bits a mano: Ensamblador. Y el mundo fue feliz. Despues de que los procesadores evolucionaran mas y se creeara unix, el mundo conocio el lenguaje C, y todos dieron la espalda a ASM. Pobre asm, se hecho a llorar. Pero entonces un grupo de valientes y nobles caballeros con espadas relucientes fueron al encuentro del conocimiento perdido para hacer las aplicaciones de ensueño tan fuertes, robustas, y en pocas palabras, mejores como se uedan. Y es por eso que asm se usa todavia.

Conceptos basicos

En ASM, cada comando que escribas (suponiendo que sea valido) se traduce a una instruccion unica que ejecutara el procesador; Todas las formas en que puedes escribir cierta instruccion cambian el codigo que interpretara el procesador. Una instruccion o comando o como quieras llamarle, esta formado por 2 portes diferentes:

  • nombre de MNEMONIC que significa algo como una palabra para ayudar a recordar, en otras palabras, el "nombre" de la instruccion
  • argumentos para la instruccion, que puede ser registros, direcciones de memoria, o constantes (mas adelante hay una descripcion de cada uno) esta es la forma de escribir sentencias en ASM:
  • label: instruccion arg1, arg2 ; comentario
    • label identifica alguna palabra que se utilizara como referencia del punto de ejecucion en el programa, para hacer saltos o bules o similares. Deben iniciar con una letra, y no pueden tener el mismo nombre que una palabra reservada ademas de terminar con ': '.
    • instruccion identifica una palabra clave que refiere a una instruccion que ejecutara el procesador, el compilador genera un "OPCODE" o codigo de operacion para que el procesador sepa que hacer.
    • arg1 y arg2 son los complementos de la instruccion puede ser un registro o una direccion de memoria o una constante.

LOS REGISTROS

hay 4 registros generales, en los cuales puedes almacenar libremente cualquier informacion (bueno, mas bien, cualquier numero...) ademas de tener una funcion particular, estos son:

  • EAX AX AH/AL: Acumulador, usado para... ps acumular...
  • EBX BX BH/BL: Base, para las direcciones de memoria
  • ECX CX CH/CL: Contador. para los bucles, las instrucciones de bucle aumentan/decrementan este registro automagicamente
  • EDX DX DH/DL: Datos, generalmente se usa para informacion que no corresponde a los 3 anteriores

E*X *X y *H/*L son parte del mismo registro, pero en varios sabores: 32 bit, 16 bit y 8bit respectivamente; por ejemplo AH y AL son parte de AX que son parte de EAX

aqui un peque diagrama:

32..........................................................0 
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 <--bits
[ EAX ] 32 bit
[ AX ] 16 bit
[ AH ][ AL ] 8 bit


desafortunadamente no hay una manera facil de usar los bits altos de EAX, mas adelante explicare como. Para ahorrarme problemas, empezare tomando en cuenta solo los *X, es decir, los de 16 bits e inferiores.

TIPOS DE DATOS

Se supone ya los conoces, pero como son llamados de diferentes formas, aqui te van:

  • BIT: un solo 0 o 1, switch, flag, o comoquieras llamare
  • BYTE: un conjunto de 8 bits, su valor maximo es 255
  • WORD: 16 bits, 2 bytes
  • DWORD (double word): 32 bits, 4 bytes, 2 words
  • QWORD (Quadruple word): 64 bits, 8 bytes, 4 words, 2 dwords

tambien hay de 80 bit, 24 bit, y mezclas, pero son usados muy poco (a excepcion del de 80, que se usa en operaciones de punto flotante (tipo 0.00e0) y otras cosillas mas)

asi que *H/*L = 1 byte/1 byte, *X = 1 word, E*X = 1 word.

ademas de estos 4 hay varios mas, 3 de ellos son los de SEGMENTO un segmento define una direccion base de memoria, calculando un OFFSET (no conosco una palabra en español que signifique lo mismo, asi que seguire usando indiscriminadamente esta ^^) bueno, un offset es un "extra" a un numero, por ejemplo si tenemos que la base es 378, y queremos el byte en 390, tenemos que tener un offset de 12, de igual manera haremos siempre, teniendo un segmento como base. Esto definitivamente es mas simple que usar direcciones de memoria absolutas...

bueno, los registros que contienen segmentos son los siguientes:

  • CS: Code Segment (Segmento de Codigo) apunta a donde esta el codigo, este no debe ser cambiado a mano, en lugar de eso se deja que la computadora se encarge de eso, pues el procesador siempre toma el codigo de CS: IP, y si modificas CS, pues tomara datos que no son para nada tu codigo. y si no tienes cuidado puedes crashear la maquina, o hacer cosas inesperadas...
  • DS: Data Segment (Segmento de Datos) apunta a donde estan los datos, este si se puede cambiar a como te plazca, de hecho, es casi imperativo que lo hagas muchas veces en un programa...
  • ES: Extra Segment (duh) este es igual que el data, solo que esta extra, para cuando tienes que usar 2 segmentos al mismo tiempo, por ejemplo...
  • SS: Stack Segment (luego explico que es stack) este segmento es importante por que tiene el stack, es mejor que por el momento no lo toques. mas adelante explicare con detalle que es el stack.

al parecer hay otros registros de segmento, pero como no los conosco, no los explicare...
Ademas existen los "Registros (de) punteros" estos se usan para los OFFSETS

  • SI (ESI) Source Index (Indice Fuente)
  • DI (EDI) Destination Index (Indice Destino)
  • IP Instruction Pointer (Puntero de Instrucciones)

El uso de los 2 primeros es un poco complicado; lo explicare en su respectiva seccion. IP, en cambio, deveria dejarse solo siempre, no hay necesidad de modificarlo, pues con los saltos y llamadas se hace automagic- amente. Este registro guarda la posicion en memoria de la instruccion que se ejecutara, no hay mucho mas que decir al respecto...

Los registros del Stack son:

  • SP: Stack Pointer (puntero del stack)
  • BP: Base Pointer (Puntero base)

estos se usan para tomar datos del stack, en la seccion del stack explicare su uso con detalle.

y, mi favorito:

FLAGS: Status .. literalmente Banderas, pero eso no tiene sentido. este es simplemente una bola de bits, donde cada bit especifica una cosa:

B A 9 8 7 6 5 4 3 2 1 0 
| | | | | | | | | | | +--- CF Carry Flag
¶ ¶ ¶ ¶ ¶ ¶ ¶ ¶ ¶ ¶ +--- 1
¶ ¶ ¶ ¶ ¶ ¶ ¶ ¶ ¶ +--- PF Parity Flag
¶ ¶ ¶ ¶ ¶ ¶ ¶ ¶ +--- 0
¶ ¶ ¶ ¶ ¶ ¶ ¶ +--- AF Auxiliary Flag
¶ ¶ ¶ ¶ ¶ ¶ +--- 0
¶ ¶ ¶ ¶ ¶ +--- ZF Zero Flag
¶ ¶ ¶ ¶ +--- SF Sign Flag
¶ ¶ ¶ +--- TF Trap Flag (Single Step)
¶ ¶ +--- IF Interrupt Flag
¶ +--- DF Direction Flag
+--- OF Overflow flag


Estos Flags se utilizan con ciertas instrucciones, por lo general controlan de sierta manera el flujo del programa, o mejor dicho, la forma en que actua el programa.

  • Carry (CF): Cuando una operacion aritmetica resulta en un valor mas grande (o mas chica) este flag esta puesto, indica que hubo un valor resultante. por ejempo suponiendo que al = 255; bl = 2; add al, bl resulta en carry, por que al no puede tener mas de 255.
  • Zero Flag (ZF): cuando una operacion resulta en 0, este flag se setea. en comparaciones indica que ambos valores son iguales.
  • SF (Signo): indica el signo de una operacion (2-4 = SF->1)
  • DF (Direccion): indica, en las operaciones de cadenas en memoria, que se movera a la unidad siguiente o anterior, dependiendo del valor de este flag
  • OF (Sobrecarga): Si algunas operaciones resultan en un valor muy grande este flag se setea.

DIRECCIONAMIENTO DE MEMORIA

Esta es una parte un poco complicada. primero explicare como esta organizada la memoria, despues como accesarla.

La memoria esta asi, suponiendo que tenga 16 MBs de memoria:

bytes 

16384 [ memoria alta ]
15360 [ ]
14336 [ ]
. [ ]
. [ ]
. [ ]
2 [ ]
1 [ ]
0 [ memoria baja ]


las operaciones en memoria funcionan mejor cuando estas estan en direcciones par. Cuando se requiere accesar memoria lo que el procesador hace es esto:

  1. tomar la direccion, si es no es par, tomar la unidad ANTERIOR
  2. si es par, no hace mas, en caso contrario, toma la siguiente
  3. voltea ambas unidades (1, 2 = 2, 1)
  4. quita los bits de mas y termina

Todo esto se hace transparentemente, es decir, nadie lo nota, solo el procesador en vista de los pasos extra, es mejor siempre tener ordenada la memoria en multiplos de 2, o 4.

Ahora veamos algunas instrucciones basicas de los procesadores x86 por ahora solo veremos las mas comunes dejando para otro texto un analisis de algunas otras porque son bastantes -_-

Aqui se usa 'reg' para indicar un registro general, y mem para una ubicacion de memoria cualquiera, siempre que sea valida

ADD: Su sintaxis basica es

add destino, fuente


Con esta instruccion el compilador agrega el contenido de la fuente al destino, es una instruccion binaria, no se puede usar para sumar mem a mem

Ejemplos:

add reg, reg 
add reg, mem


CALL: Sintaxis

call destino


Se usa para poner el IP (puntero de instruccion) en la pila y cargarlo con la direccion del nombre del procedimiento que se llama, el codigo continua en CS:IP efectivamente haciendo una llamada de funcion.

Ejemplo

call funcion: 
funcion:
mov ax, 4c00h
int 21h


INT:
Llama una interrupcion del software, tambien existen interrupciones del hardware pero por ahora no nos fijaremos en ellas solo en las interrupciones del sistema operativo (MS-DOS en este caso -_-) en otras palabras CALL nos sirve para llamar procedimientos de nuestro propio programa e INT para llamar procedimientos del sistema

Ejemplo:

mov ax, 4c00h 
int 21h


Cuando veamos nuestro primer programa de ejemplo hablaremos mas sobre que es exactamente lo que se hace (si estas muy desesperado te anticipo que lo que hacemos es llamar a la interrupcion 21 con la funcion del DOS 4c, subfuncion 00 que sirve para terminar un programa)


LEA:
Sintaxis:

lea destino, fuente


Acronimo de Load Effective Address. Carga un registro con la direccion efectiva de la locacion de memoria especificada.

Ejemplos:

lea ax, [bx] 
lea ax, 3[bx]

El primer ejemplo tan solo carga en ax la direccion a la que apunta bx. Es como utilizar punteros
Con el segundo ejemplo hacemos algo similar, solo que primero agregamos 3 al valor de bx y despues movemos la sumatoria en ax. Es como si hubieramos usado add y mov, pero con Lea lo hacemos en una sola instruccion


MOV:
Sintaxis:

mov destino, fuente 


Copia el operador fuente en el operador destino, siempre cuando sean tipos compatibles, y puede tomar distintas formas como:

mov reg, reg 
mov mem, reg
mov reg, mem
mov ax/al, mem
mov reg, immed


No permite mover de una fuente de 8 bits a un destino de 16 bits y viceversa

un ejemplo:

mov ax, 40h 
mov es, ax


Nota: no se puede modificar los registros con segmentos directamente por eso primero cargamos el 40 en ax y despues copiamos ax a es


POP:
Sintaxis:

POP destino


Saca de la pila (SS: SP) una doble word y lo pone en destino, despues incrementa SP en dos para que apunte de nuevo a lo mas alto de la pila, CS no es un destino valido

PUSH:

PUSH fuente 


Decrementa el valor de SP dependiendo del tamaño del operador y transfiere una palabra (WORD) fuente a lo mas alto de la pila (SS: SP)

Un ejemplo que usa POP y PUSH:

MOV AX, 1234H 
MOV BX, 5678H
PUSH AX
POP BX


al final, AX va a valer 1234h y BX tambien 1234h esto porque primero enviamos el contenido de AX a lo mas alto de la pila y despues lo sacamos y lo guardamos en BX usando pop


RET:
La direccion de retorno, transfiere el control de un procedimiento devuelta a la direccion de instruccion guardada en la pila.

Ahora que ya vimos algunas instrucciones importantes de ASM veamos nuestro primer ejemplo:

.MODEL SMALL 
.STACK 200H
.DATA
Text db "Hola emc2h world$"

.CODE

START:
Mov ax, SEG Text
Mov ds, ax
Mov dx, OFFSET Text
Mov ah, 09
Int 21h

mov ah, 4ch
mov al,00h


int 21h

END START


guardalo como prueba.asm y ensamblalo asi:

tasm prueba prueba.obj 
tlink prueba.obj


y con eso tendremos un ejecutable prueba.exe

analicemos brevemento esto ^.^ las primeras dos lineas solo son indicaciones para el ensamblador en nuestro caso tasm respecto al tipo y tamaño de memoria que ocuparemos.

.DATA debe ir antes de .CODE y es aqui donde declaramos nuestras variables como en el ejemplo que usamos

Text db "Homa emc2h world$"


como podras imaginar Text es el nombre de nuestra variable y db digamos el tipo significa "declare bytes" tambien existe dw "declare word" pero por ahora no nos meteremos en eso, algo importante es que nuestras cadenas deben terminar con el caracter $ ya que indican donde termina el texto (pero este caracter no se imprime)

mov ax, seg text | mov ds, ax | mov dx, offset text |mov ah, 09 | int21h


aqui lo primero que hacemos es mover a nuestra variable text a DS, pero como no podemos moverla directamente primero la copiamos a ax y despues a DS. Esta interrupcion requiere que tengamos el segmento de nuestra variable en DS y su offset en dx, por eso cargamos su offset en dx con nuestra 3er instruccion

Con las ultimas 2 instrucciones simplemente cargamos la funcion 09 de la interrupcion 21h que corresponde a imprimir una cadena. La interrupcion 21h tiene muchas funciones por eso debemos especificar cual queremos usar

mov ah, 4ch | mov al, 00h | int21h


tambien pudimos haber hecho:

mov ax, 4c00h | int 21h


Nuevamente la int21 esta vez con las funciones 4c y 00. simplemente indican que ya termino el programa

despues de todo esto trabajo solo fue para imprimir una cadena de caracteres en pantalla pero el inicie siempre es duro y aburrido -_- una vez que sabes las bases ya puedes hacer cosas mas interesantes

si no les gusto mi ejemplo tan chafa pues ni modo por ahora vamos a ver otro ejemplo chafa y dejaremos cosas mas avanzadas para el siguiente numero del tutor

.MODEL MEDIUM 
.STACK 200H
.data
Number db "1$"
.CODE
START:

mov cx,20
startloop:
Mov ax, SEG Number
Mov ds, ax
Mov dx, OFFSET Number
mov ah,09
int 21h
inc Number
loop startloop

mov ax, 4c00h
int 21h

END START


Este es muy similar al anterior grabalo y compilalo al igual que el otro lo que hace es imprimir una secuencia de 20 caracteres (por eso movemos el 20 a cx, recuerda que cx se usa para ciclos) iniciamos un ciclo que se repetira 20 veces y en cada ocasion se va a imprimir un caracter empezando por el 1 y terminando en D segun la tabla de valores ASCII( no es del 1 al 20 como muchos de seguro imaginaron) para que lo almacenado en nuestra variable vaya incrementado su valor usamos inc number. INC solamente incrementa en uno al operador que le indicamos, y el resto es igual a nuestro ultimo ejemplo.

pues bien con esto terminamos la primera parte de nuestro tutor aver que cosas se me ocurren poner para el siguiente tuto porque aun falta muuuuuuuuucho por ver estos solo fueron los primeros pasos de un largo camino que es el ASM, digamos que apenas estamos gateando en ASM.

Con el siguiente numero espero que ya podamos caminar aunque nos caigamos una que otra vez ^.^ espero que tenga tiempo para hacer el proximo tutorial y que pueda aprender mas de este lenguaje para poder explicarlo mejor

La proxima vez veremos mas instrucciones, algo de codigo interesante, que puedas presumir vaya ademas de explicaciones tan talentosas de la sintaxis de el lenguaje ASM

NO TE LO PIERDAS!! :)

enviame tus comentarios y mentadas de madre a zadkiel@ijustdontcare.com

by Zadkiel

← 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