Latest Entries »

Saltos Incondicionales

Empecemos por el salto sin condiciones, con el que podremos cambiar el control a cualquier punto del programa.

Sería como el “Goto” del Basic, simplemente transferir el control a otro punto del programa. La orden es

JMP (de Jump, salto)

El formato mas sencillo para el salto sería JMP 03424h

La única instrucción que existe para éste fin es JMP (Abreviatura de JUMP, que significa en inglés SALTAR). La sintaxis es la siguiente:

JMP XXXXXXXX: Es la ubicación de la instrucción en donde se continuará el programa (A partir de ésta se ejecutan las siguientes).

Ejemplo:

XXXX: 0100 MOV AX, 1000
XXXX: 0103 JMP 0107
XXXX: 0105 XOR AX, A
XXXXX: 0107 INT 20

Saltos Condicionales

Son similares a JMP en la sintaxis, pero la diferencia es el nombre.
Las instrucciones son las siguientes
JE o JZ: Salta si está prendido el bit cero del registro de banderas.+
Objetivo: Saltar si la última comparación realizada da igual.

JA o JNBE: Salta si el bit carry (CF) o el bit cero (ZF) del registro de banderas está desactivado.
Objetivo: Saltar si la última comparación realizada con números naturales da mayor

EDICION
Los archivos fuente de código ensamblador deben estar en formato ASCII standard. Para esto puede usarse cualquier editor que permita crear archivos sin formato, e.g. Edlin, Edit, Write, El editor del Turbo Pascal, Works, Word, WordStar, etcétera. Las declaraciones pueden ser introducidas en mayúsculas y/o minúsculas. Una buena práctica de programación es poner todas las palabras reservadas (directivas e instrucciones) en mayúsculas y todo lo del usuario en minúsculas para fines de facilidad de lectura del código.

Las sentencias pueden comenzar en cualquier columna, no pueden tener más de 128 caracteres, no se permiten lineas múltiples ni códigos de control, y cada línea debe ser terminada con una combinación de line-feed y carriage-return. Los comentarios se declaran con ; y terminan al final de la línea.

ENSAMBLADO
El ensamblado se lleva a cabo invocando al MASM. Este puese ser invocado, usando una línea de comando, de la siguiente manera:

MASM archivo [,[objeto][,[listado][,[cross]]]]][opciones][;]

donde:

objeto.- Es el nombre para el archivo objeto.
listado.- Nombre del archivo de listado de ensamblado.
cross.Es un archivo de referencias cruzadas.

LINK
De la misma forma que el ensamblado, la fase de liga se lleva a cabo con el LINK. Este puede ser invocado de la misma forma que el MASM. Los parámetros que este requiere son:

LINK objeto [,[ejecutable][,[mapa][,[librería]]]]][opciones][;]

donde:

objeto.- Es el nombre para el archivo .OBJ
ejecutable.- Nombre del archivo .EXE
mapa.- Nombre del archivo mapa
librería.- Nombre del archivo biblioteca de rutinas

EJECUCION
Para la ejecución del programa simplemente basta teclear su nombre en el prompt de MS-DOS y teclear ENTER. Con esto el programa será cargado en memoria y el sistema procederá a ejecutarlo. El proceso completo para poder crear un programa ejecutable con el Microsoft Macro Assembler se muestra abajo.

estruct estruc

Directivas

Son ordenes para el ensamblador y no tienen traduccion a codigo maquina. y utilizaremos:
ORG:
Inicializa el contador de programa con el valor del operando. Si no se utiliza un directiva ORG, se asume que el PC vale 0.

Sintaxis:   ORG valor

EQU:
Asigna al simbolo contenido enb el campo etiqueta el valor de la expresion en el campo operando.
Sintexis:   etiqueta EQU valor

DC:
Define constantes en memoria. Permite asignar valores numericos, tablas, caracteres a variables de memoria. y se pueden asignar hasta 256 bytes de datos en una unica direcdtiva.

Sintaxis:   etiqueta DC.t const1,const2…

Directivas simplificadas

Son formas abreviadas para definir segmentos, para usarlas primero se debe definir el modelo de memoria antes de definir algún segmento

La directiva simplificada .MODEL crea por omisión a los segmentos y los enunciados ASSUME y GROUP necesarios. Formato de definición de memoria:

.MODEL modelo de mem

Los formatos generales para las directivas de segmento son:
.STACK [tamaño]
.DATA
.CODE [nombre]
Cada una de estas directivas hace que el ensamblador genere el enunciado SEGMENT necesario y su correspondiente ENDS. El tamaño de la pila es de 1024 bytes, el cual puede pasarse por alto. Los nombres de los segmentos (que no se tienen que definir) por omisión son STACK, DATA y TEXT (código).

Normal
0

21

false
false
false

ES
X-NONE
X-NONE

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:»Tabla normal»;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-parent:»»;
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin-top:0cm;
mso-para-margin-right:0cm;
mso-para-margin-bottom:10.0pt;
mso-para-margin-left:0cm;
line-height:115%;
mso-pagination:widow-orphan;
font-size:11.0pt;
font-family:»Calibri»,»sans-serif»;
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:minor-latin;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin;
mso-bidi-font-family:»Times New Roman»;
mso-bidi-theme-font:minor-bidi;
mso-fareast-language:EN-US;}

Definición de Segmentos
Para realizar esta acción se utilizan las denominadas directivas de segmento. Se pueden utilizar las directivas largas o las simplificadas.
Directivas Largas Directivas de Segmento:    SEGMENT,ENDS y END
SEGMENT : Directiva para definir un segmento, el tamaño máximo de un segmento es de 64kb
ENDS: Directiva para finalizar un segmento

Formato:

Nombre    Directiva    alineación    [combinación]    [clase]

Alineación: Indica el límite en el que inicia el segmento, por omisión alinea el segmento al límite de un párrafo, de manera que la dirección inicial es divisible entre 16 0 10h, el requerimiento típico es PARA.

Combinación: Indica si el segmento se combina con otros segmentos cuando son enlazados después de ensamblar, los tipos son STACK, COMMON, PUBLIC y la expresión AT.
Para la pila es:

Nombre SEGMENT PARA STACK

Se puede utilizar PUBLIC y COMMON en donde tenga el propósito de combinar de forma separada programas ensamblados cuando los enlaza.

Clase: Es encerrada entre apóstrofes, es utilizada para agrupar segmentos cuando se enlazan.
Se utilizan ´code´ con CS, ‘data’ para DS, ‘stack’ para SS.
END: : Directiva para definir el fin del programa

Ejemplo:

STACKSG segment para stack ‘stack’
Definición de la pila
STACKSG ENDS
DATASG SEGMENT PARA ‘DATA’
Definición de datos
DATASG ENDS
CODESG SEGMENT PARA ‘CODE’
Definición de codigo
CODESG ENDS
END

  • Software Necesario
  • Utilización del MASM
  • Uso del Enlazador (LINKER)

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 compilador utilizaremos el MASM (macro ensamblador de Microsoft) ya que es el mas común, y como enlazador utilizaremos el programa link .

La extensión usada para que MASM reconozca los programas fuente en ensamblador es .ASM ; una vez traducido el programa fuente, el MASM crea un archivo con la extensión .OBJ , este archivo contiene un «formato intermedio» del programa, llamado así porque aún no es ejecutable pero tampoco es ya un programa en lenguaje fuente. El enlazador genera, a partir de un archivo .OBJ o la combinación de varios de estos archivos, un programa executable, cuya extensión es usualmente .EXE aunque también puede ser .COM , dependiendo de la forma en que se ensambló.

UTILIZACION DEL MASM

Una vez que se creó el programa objeto se debe pasar al MASM para crear el código intermedio, el cual queda guardado en un archivo con extensión .OBJ . El comando para realizar esto es:

MASM Nombre_Archivo; [Enter]

Donde Nombre_Archivo es el nombre del programa fuente con extensión .ASM que se va a traducir. El punto y coma utilizados despues del nombre del archivo le indican al macro ensamblador que genere directamente el código intermedio, de omitirse este caracter el MASM pedirá el nombre del archivo a traducir, el nombre del archivo que se generará así como opciones de listado de información que puede proporcionar el traductor.

Es posible ejecutar el MASM utilizando parámetros para obtener un fin determinado, toda la lista de los mismos se encuentra en el manual del programa. Solo recordaré en este tutorial la forma de pasar dichos parámetros al MASM:

Todo parámetro va despues del simbolo » / «. Es posible utilizar varios parámetros a la vez. Una vez tecleados todos los parámetros se escribe el nombre del archivo a ensamblar.

Por ejemplo, si queremos que el MASM ensamble un programa llamado prueba , y ademas deseamos que despliege el número de lineas fuente y símbolos procesados ( eso lo realiza con el parametro /v ), y si ocurre un error que nos diga en que linea ocurrió ( con el parametro /z ), entonces tecleamos:

MASM /v /z prueba;

USO DEL ENLAZADOR (LINKER)

El MASM unicamente puede crear programas en formato .OBJ , los cuales no son ejecutables por si solos, es necesario un enlazador que genere el código ejecutable.

La utilización del enlazador es muy parecida a la del MASM, unicamente se teclea en el indicador del DOS:

LINK Nombre_Archivo ;

Donde Nombre_Archivo es el nombre del programa intermedio (OBJ). Esto generara directamente un archivo con el nombre del programa intermedio y la extensión .EXE

Normal
0

21

false
false
false

ES
X-NONE
X-NONE

 

 

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:»Tabla normal»;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-parent:»»;
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin-top:0cm;
mso-para-margin-right:0cm;
mso-para-margin-bottom:10.0pt;
mso-para-margin-left:0cm;
line-height:115%;
mso-pagination:widow-orphan;
font-size:11.0pt;
font-family:»Calibri»,»sans-serif»;
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:minor-latin;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin;
mso-bidi-font-family:»Times New Roman»;
mso-bidi-theme-font:minor-bidi;
mso-fareast-language:EN-US;}

Los COMPILADORES como los desarrollados para Fortran, Clipper, COBOL, Pascal o C, que en vez de llamar y ejecutar una rutina en lenguaje de máquina, éstos juntan esas rutinas para formar el código objeto que, después de enlazar las rutinas de run-time y llamadas a otros programas y servicios del sistema operativo, se transformará en el programa ejecutable.
Los ENSAMBLADORES son como una versión reducida y elemental de un compilador (pero que de ninguna manera deben considerarse como tales), ya que lo único que tienen que hacer es cambiar toda referencia simbólica por la dirección correspondiente, calcular los saltos, resolver referencias y llamadas a otros programas, y realizar el proceso de enlace. Los ensambladores son programas destinados a realizar el ensamblado de un determinado código.

 

Prólogo

El lenguaje ensamblador es un lenguaje de programación que es una traducción directa del código de máquina (Éste código es interpretado por el microprocesador), para que pueda ser entendible por los seres humanos, por lo tanto es un lenguaje de bajo nivel. El lenguaje ensamblador permite una optimización que no se consigue con lenguajes de medio y alto nivel. Permite la creación de programas muy rápidos y muy pequeños, además se tiene un control que no se consigue con un lenguaje de alto nivel. Hay que tener en cuenta que éste libro está orientado a la arquitectura x86 (Es la arquitectura que utiliza la PC IBM y las compatibles) y no es compatible con otra arquitectura.

Interrupción 21H 
Propósito: Llamar a diversas funciones del DOS. 

Funciones para desplegar información al video 02H Exhibe salida 
09H Impresión de cadena (video) 
40H Escritura en dispositivo/Archivo 

Funciones para leer información del teclado 01H Entrada desde teclado 
0AH Entrada desde teclado usando buffer 
3FH Lectura desde dispositivo/archivo 

Método FCB 
0FH Abrir archivo 
14H Lectura secuencial 
15H Escritura secuencial 
16H Crear archivo 
21H Lectura aleatoria 
22H Escritura aleatoria 

Handles 
3CH Crear archivo 
3DH Abrir archivo 
3EH Cierra manejador de archivo 
3FH Lectura desde archivo/dispositivo 
40H Escritura en archivo/dispositivo 
42H Mover apuntador de lectura/escritura en archivo 

Función 02H 
Despliega un caracter a la pantalla. 

Función 09H 
Despliega una cadena de carateres en la pantalla. 

Función 01H 
Leer un caracter del teclado y desplegarlo.

Posiblemente sea el debug el depurador más rudimentario que existe; pero el hecho que desde el principio haya sido provisto con el sistema operativo, nos permite encontrarlo hoy en cualquier máquina DOS o Windows. Muchas tareas elementales pueden realizarse sin otra ayuda que el Debug y por eso vamos a ver algunos comandos básicos. Incluso es posible correr programas cargados en memoria utilizando breakpoints elementales, ejecutar paso a paso, saltar sobre procedimientos, editar programas en hexa y muchas más cosas. Ya hemos dicho cómo podemos arrancarlo desde una ventana DOS, y usando el comando R (mostrar registros) nos mostrará algo similar a esto:

AX=0000   BX=0000   CX=0000   DX=0000   SP=0000   BP=0000   SI=0000   DI=0000

DS=1332    ES=1332    SS=1332    CS=1332    IP=0100    NV UP EI PL NZ NA PO NC

1332:0100 C3 RET .

Esto muestra el contenido de los registros del procesador incluyendo varias banderas: en el ejemplo, y en el mismo orden tenemos: V=0, D=0, I=1, S=0, Z=0, AC=0, P=0 y C=0

Si ponemos después de la R el nombre de un registro, es posible modificar su contenido. Por ejemplo, para editar el contenido de CX, hay que poner el comando RCX. Debug nos presenta el contenido actual del registro y la posibilidad de ingresar un nuevo valor para sustituirlo.

Los comandos L y W se utilizan para leer y escribir en archivos de disco. La cantidad de bytes transferida en cada operación es el contenido de BX:CX. Previamente es necesario darle un nombre al archivo con el comando N. Se puede especificar la dirección a partir de la que se desea transferir datos o bien usar el vector por defecto DS:DX.

Los comandos más útiles y más usados en Debug son:

A dirección Ensamblar (ingresar código assembly)
D dirección cantidad Mostrar en pantalla direcciones de memoria en presentación hexa
E dirección Editar memoria desde dirección
F direc1 direc2 valor Llenar memoria desde direc1 hasta direc2 con el dato valor
G dirección Ir (durante la ejecución) a la dirección dirección
H valor1 valor2 Muestra el resultado de la suma y resta hexadecimal entre valor1 valor2
I puerto Obtiene una entrada desde el puerto puerto
M direc1 direc2 direc3 Mueve el bloque de memoria direc1- direc2 a partir de direc3
P cant Salta sobre procedimientos cant de veces o hasta dirección direc
Q   Sale de Debug
S direc1 direc2 valores Busca en bloque de memoria desde direc1 hasta direc2 los bytes valores
T cant Igual que P pero son instrucciones simples
U direc cant Desensambla cant bytes a partir de la dirección direc
XS   Muestra estado de memoria expandida
?   Presenta pantalla de ayuda

 

Nuestro primer programa

Usaremos el Debug para ensamblar un programa que realice algo tan útil (?) como dejar en alguna parte de la memoria el nombre de nuestra escuela ECCE. Para sacar algo a pantalla, debemos leer el tutorial de +gthorne, que será nuestro paso siguiente. Por ahora sólo queremos practicar  de manera que abramos una ventana DOS y escribamos DEBUG (enter). Nos proponemos hacer que ECCE sea escrito en memoria, en el offset 200h de nuestro segmento de datos DS. Sabemos que los códigos ASCII son E=45h y C=43h, de manera que nuestro programa puede lucir así:

a 100

1322:0100 mov ax,4543 ;cargamos el registro AX con el dato 4543 (EC en ASCII)
1322:0103 mov bx,4345 ;cargamos BX con «CE» en ASCII
1322:0106 mov [200],ax ;ponemos AX en la dirección de memoria 200
1322:0109 mov [202],bx ;idem para BX, pero en la 202 (AX ocupó la 200 y 201)
1322:010D int 20 ;finalizar y salir a Debug

1322:010F

Al apretar «enter» una vez más, Debug nos devuelve su prompt «-» y ya estamos listos para nuestro próximo comando. Podemos ver algunas curiosidades del listado anterior: 1) Debug asume que los números que le damos, sean direcciones o datos, son hexadecimales. 2) A medida que vamos ingresando el programa, nos va devolviendo la dirección de almacenamiento de la próxima instrucción que escribiremos. 3) Las tres primeras instrucciones MOV ocuparon de memoria de programa 3 bytes cada una, pero la cuarta ocupó 4 bytes y la INT 20 sólo ocupó 2 bytes. 4) Aunque nada se ha hablado de la INT 20, es lo que por el momento usaremos para terminar el programa . 5) Cuando hacemos referencia al contenido de una posición de memoria, encerramos la dirección entre corchetes []. Es muy importante saber distinguir entre la dirección y el valor almacenado en esa dirección de memoria.

Nuestra lógica es muy simple: cargamos el ASCII «EC» en AX y lo dejamos en la dirección 200. Luego cargamos «CE» en BX y lo dejamos en la 202. Tanto AX como BX han sido meros vehículos para cargar la memoria con datos y sólo a los efectos didácticos porque también está permitido :

MOV word ptr [200],4543 ; cargar la word de memoria 200 directamente con el dato 4543

Esta instrucción ocupa 6 bytes, de modo que no ganamos espacio poniéndola en lugar del más elíptico procedimiento de cargar AX y con éste escribir en 200. El prefijo «word ptr» es para que el procesador sepa que lo que moveremos a 200 es una word y no un byte o double-word.

Veamos cómo se ve nuestro programa usando el comando desensamblar:

-u 100 (desensamble a partir de la CS:100)

(Nótese que Debug listará usando sólo mayúsculas, sin importar cómo escribimos nuestro código)

1322:0100 B84345 MOV AX,4543
1322:0103 BB4543 MOV BX,4345
1322:0106 A30002 MOV [200],AX
1322:0109 891E0202 MOV [202],BX
1322:010D CD20 INT 20

NOTA: el valor de 1322 (el contenido del registro CS) es válido para la PC donde se escribió este ensayo. Por lo general los valores no coinciden de una a otra PC, salvo que las instalaciones de software sean idénticas y en ambas estén corriendo previamente al DEBUG los mismos programas. El listado es más largo, pero las líneas que siguen hacia abajo son alguna cosa que estaba en memoria, ya que Debug desensambla por defecto los 20h primeros bytes desde la dirección indicada (o desde la que esté apuntando), y en nuestro programa sólo hemos usado 0Fh bytes (15 en decimal). Echémosle un vistazo:

Ajá!!, Debug no deja de sorprendernos, en una columna entre la dirección y el listado en lenguaje assembly puso unos números hexadecimales. Son los códigos de operación (opcodes) que es lo que en definitiva se almacena en memoria y lo que nuestro Pentium debe interpretar y ejecutar. Debug compiló nuestro programa ingresado en assembly y produjo ese código binario con representación hexadecimal para que el Pentium lo interprete.

Antes de correr el fabuloso programa que hemos escrito, tenemos que ver qué hay en la posición de memoria 200. Para ello usamos el comando D 200, que nos muestra la basura que hay en nuestra RAM desde DS:0200 hasta DS:027F. Como deseamos leer claramente nuestro nombre ECCE, vamos a llenar este espacio con ceros usando el comando

– F 200 23F 00

con lo que le indicamos a Debug que debe llenar el bloque de memoria que comienza en 200 y termina en 23F con «00». Para estar seguros, escribamos nuevamente el comando D 200. Debemos ver las cuatro primeras filas del listado con los datos en 00. Estamos listos para correr nuestra maravilla.

Con el comando R nos aseguramos que CS:IP esté apuntando al inicio de nuestro programa (o sea a CS:0100). Para nuestro caso CS vale 1322, pero como ya se ha dicho, puede que en otra PC tenga otro valor. Corramos el programa con el comando G. Debug nos debe informar:

 El programa ha finalizado con normalidad.

Bien! todo fue de maravillas. Veamos si nuestras siglas brillan en las posiciones 200 a 203 con el comando D 200

Esperábamos los hexa 45,43,43,45 a partir de la 200 (miremos además en la columna ASCII del Debug, en donde claramente nos dice CEEC) y están al revés. Qué habrá pasado? Será que hemos escrito BX en 200 y AX en 202?. Usemos al Debug para depurar , que para eso Bill Gates lo ha puesto donde está. Repitamos el comando F 200 23F 00 para dejar nuevamente en cero la memoria y ejecutemos nuestro programa paso a paso.

Primero el comando R. Nos debe decir que IP apunta a 0100:

1322:0100 B84345 MOV AX,4543

-T (comando para ejecutar una sola instrucción). Lo relevante es:

AX=4543 e IP=0103

1322:0103 BB4543 MOV BX,4345 es la próxima instrucción. Ejecutemos con T:

1322:0106 A30002 MOV [200],AX

Ejecutemos el comando D 200 para ver qué hay en la memoria: hasta ahora 00 de la dirección 200 a la 203. Todo ok, porque hasta aquí sólo hemos cargado los registros AX y BX. Hagamos otro T.

1322:0109 891E0202 MOV [0202],BX es la próxima instrucción

Hemos guardado AX en la dirección 200 y por lo tanto debería haber un 4543 («EC» en ASCII) en las direcciones 200 y 201. Verifiquemos con el comando D 200:

1322:0200 43 45 00 00 …….. CE…………….

QUE PASO???? Está al revés. Tengo «CE» en lugar de «EC». Mmmmm!! Mr Intel tiene algo que ver con esto: Resulta que lo que leemos en AX como «EC», en la realidad lo debemos asumir como : En AL tengo un 43 («C») y en AH un 45 («E»). Y el procesador hace algo sumamente lógico, a la porción más baja del registro (AL) la almacena en la dirección de memoria más baja (200) y a la porción más alta del registro (AH) la almacena en la dirección de memoria más alta (201). Todo parece bien pero no funciona?

Pero está bien tal como lo hizo Intel. Si leemos la memoria en sentido de direcciones ascendentes, debemos acostumbrarnos a leer los registro (y a cargarlos, ahí fue donde nos equivocamos!) desde la porción más baja hacia la más alta. Por lo tanto, debemos rescribir nuestro programa para que en AL se almacene la primera letra («E») y en AH la segunda («C»), y lo mismo para BX:

a 100

1322:0100 MOV AX,4345

1322:0103 MOV BX,4543

(enter) nuevamente para salir del comando A.

Ahora debemos modificar el registro IP, que nos quedó apuntando a la mitad del programa:

RIP (enter) nuestro comando

IP 0109 respuesta de Debug

:100 (enter) este valor lo ingresamos nosotros para decirle que queremos a IP=0100

Ejecutamos el programa nuevamente con G y examinamos la memoria con D 200 para ver nuestro hermosa sigla ECCE ya en su lugar y en el orden debido.