0% encontró este documento útil (0 votos)
141 vistas35 páginas

Tutorial de Assembler en emu8086

Este documento explica conceptos básicos de ensamblador como acceso a memoria, tipos de datos, instrucción MOV y variables. Describe diferentes modos de direccionamiento de memoria, tipos de prefijos y cómo declarar y usar variables en ensamblador.

Cargado por

tomasbarenghi
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
141 vistas35 páginas

Tutorial de Assembler en emu8086

Este documento explica conceptos básicos de ensamblador como acceso a memoria, tipos de datos, instrucción MOV y variables. Describe diferentes modos de direccionamiento de memoria, tipos de prefijos y cómo declarar y usar variables en ensamblador.

Cargado por

tomasbarenghi
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

U.T.N.

Facultad Regional Venado Tuerto


Catedra: Arquitectura de Computadoras

Tutorial de
introducción al
lenguaje de bajo nivel
mediante el uso del
software emu8086

1
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

Tutorial Assembler

Revisar los conceptos de arquitectura X86 antes de comenzar este tutorial, la siguiente imagen es un
resumen de los registros y flags de al ALu que se utilizan en dicha arquitectura.

Acceso a Memoria

Para acceder a la memoria podemos utilizar estos cuatro registros:


BX, SI, DI, BP. Combinando estos registros dentro de los símbolos [], podemos obtener diferentes
ubicaciones de memoria. se admiten estas combinaciones (modos de direccionamiento):

BX + SI] [SI] [BX + SI + d8]


[BX + DI] [DI] [BX + DI + d8]
[BP + SI] d16 (variable offset only) [BP + SI + d8]
[BP + DI] [BX] [BP + DI + d8]
[SI + d8] [BX + SI + d16] [SI + d16]
[DI + d8] [BX + DI + d16] [DI + d16]
[BP + d8] [BP + SI + d16] [BP + d16]
[BX + d8] [BP + DI + d16] [BX + d16]

2
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

d8 – Se utiliza para el desplazamiento inmediato con signo de 8 bits (por ejemplo: 22, 55h, -1, etc...)

d16 – Se utiliza para desplazamiento inmediato con signo de 16 bits (por ejemplo: 300, 5517h, -259, etc...)
El desplazamiento puede ser un valor inmediato o un desplazamiento de una variable, o incluso ambos. si
hay varios valores, el ensamblador evalúa todos los valores y calcula un único valor inmediato.
El desplazamiento puede ser dentro o fuera de los símbolos [], el ensamblador genera el mismo código de
máquina para ambas formas.
El desplazamiento es un valor con signo, por lo que puede ser tanto positivo como negativo.
Generalmente, el compilador se preocupa por la diferencia entre d8 y d16 y genera el código de máquina
requerido.
Por ejemplo, supongamos que DS = 100, BX = 30, SI = 70.
El siguiente modo de direccionamiento: [BX + SI] + 25
lo calcula el procesador a esta dirección física: 100 * 16 + 30 + 70 + 25 = 1725.

De forma predeterminada, el registro de segmento DS se utiliza para todos los modos excepto aquellos con
registro BP, para estos se utiliza el registro de segmento SS.

Hay una manera fácil de recordar todas esas combinaciones posibles usando este cuadro:

puedes formar todas las combinaciones válidas tomando solo un elemento de cada columna o saltándote la
columna sin tomar nada de ella. Como puede ver, BX y BP nunca van juntos. SI y DI tampoco van juntos. Aquí
hay ejemplos de modos de direccionamiento válidos: [BX+5], [BX+SI], [DI+BX-4]

El valor en el registro de segmento (CS, DS, SS, ES) se llama segmento,


y el valor en el registro de propósito (BX, SI, DI, BP) se llama offset.
Cuando DS contiene el valor 1234h y SI contiene el valor 7890h, también se puede registrar como
1234:7890. La dirección física será 1234h * 10h + 7890h = 19BD0h.
Si a un número decimal se le suma cero se multiplica por 10, sin embargo 10h = 16, por lo que si a un valor
hexadecimal se le suma cero se multiplica por 16, por ejemplo:

7h = 7
70h = 112

3
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

Tipos de datos

para decirle al compilador sobre el tipo de datos,se deben utilizar estos prefijos:
byte ptr - para byte.
word ptr - para palabra (dos bytes).
Por ejemplo:
byte ptr [BX]; acceso a bytes.
o
palabra ptr [BX]; acceso a palabras.
El ensamblador también admite prefijos más cortos:

b. - para byte ptr


w. - para palabra ptr

A veces, el compilador puede calcular el tipo de datos automáticamente, pero no puede ni debe confiar en
eso cuando uno de los operandos es un valor inmediato.

Instrucción MOV

Copia el segundo operando (origen) al primer operando (destino).


El operando fuente puede ser un valor inmediato, un registro de propósito general o una ubicación de
memoria. El registro de destino puede ser un registro de propósito general o una ubicación de memoria.
Ambos operandos deben tener el mismo tamaño, que puede ser un byte o una [Link] admiten estos
tipos de operandos:

MOV REG, memory


MOV memory, REG
MOV REG, REG
MOV memory, immediate
MOV REG, immediate
REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.
memory: [BX], [BX+SI+7], variable, etc...
immediate: 5, -24, 3Fh, 10001101b, etc...

4
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

para registros de segmento solo se admiten estos tipos de MOV:


MOV SREG, memory
MOV memory, SREG
MOV REG, SREG
MOV SREG, REG
SREG: DS, ES, SS, and only as second operand: CS.

REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.

memory: [BX], [BX+SI+7], variable, etc...

La instrucción MOV no se puede utilizar para establecer el valor de los registros CS e IP.
Este es un pequeño programa de còmo usar MOV, (copia el código y probalo en eEMu)
ORG 100h ; Esta directiva es necesaria para generar un segmento de memoria en programa tipo COM
MOV AX, 0B800h ;setea AX con el valor hexadecimal B800h.
MOV DS, AX ; copia el valor de AX a DS.
MOV CL, 'A' ;setea CL con el código ASCII de 'A',es 41h.
MOV CH, 1101_1111b ; setea CH con un valor binario.
MOV BX, 15Eh ; setea BX con 15Eh.
MOV [BX], CX ; copia el contenido de CX en la dirección de memoria lógica B800:015E
RET ; retorna al Sistema operativo

Al correr el programa el resultado es:

De hecho, el programa anterior escribe directamente en la memoria de video, por lo que puede ver que
MOV es una instrucción muy poderosa.

5
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

VARIABLES

La variable es una ubicación de memoria. Para un programador es mucho más fácil mantener algún valor en
una variable llamada "var1" que en la dirección 5A73:235B, especialmente cuando tiene 10 o más variables.
Nuestro compilador admite dos tipos de variables: BYTE y WORD.

Sintaxis para una declaración de variable:

nombre valor DB
nombre valor DW

DB: Se utiliza para definir byte.


DW: Se utiliza para definir una palabra.

nombre: puede ser cualquier combinación de letras o dígitos, aunque debe comenzar con una letra. Es
posible declarar variables sin nombre sin especificar el nombre (esta variable tendrá una dirección pero
no un nombre).

valor: puede ser cualquier valor numérico en cualquier sistema de numeración admitido (hexadecimal,
binario o decimal) o "?" Símbolo para variables que no están inicializadas.

Veamos el siguiente ejemplo de programa con MOV:


ORG 100h

MOV AL, var1


MOV BX, var2

RET ; stops the program.

VAR1 DB 7
var2 DW 1234h

6
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

Como puede ver, esto se parece mucho a nuestro ejemplo, excepto que las variables se reemplazan con
ubicaciones de memoria reales. Cuando el compilador crea código de máquina, reemplaza automáticamente
todos los nombres de variables con sus offsets. De forma predeterminada, el segmento se carga en el
registro DS (cuando se cargan archivos COM, el valor del registro DS se establece en el mismo valor que el
registro CS: segmento de código).
En la lista de memoria, la primera fila es un desplazamiento, la segunda fila es un valor hexadecimal, la
tercera fila es un valor decimal y la última fila es un valor de carácter ASCII.

El compilador no distingue entre mayúsculas y minúsculas, por lo que "VAR1" y "var1" se refieren a la misma
variable.

El desplazamiento de VAR1 es 0108h y la dirección completa es 0700:0108.

El desplazamiento de var2 es 0109h y la dirección completa es 0700:0109, esta variable es una PALABRA por
lo que ocupa 2 BYTES. Se supone que el byte bajo se almacena en la dirección inferior, por lo que 34 h se
ubica antes de las 12 h.

Puede ver que hay algunas otras instrucciones después de la instrucción RET, esto sucede porque el
desensamblador no tiene idea de dónde comienzan los datos, simplemente procesa los valores en la
memoria y los entiende como instrucciones 8086 válidas (las aprenderemos más adelante).

7
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

Se puede escribir el programa con la directiva DB solamente:

ORG 100h

DB 0A0h
DB 08h
DB 01h

DB 8Bh
DB 1Eh
DB 09h
DB 01h

DB 0C3h

DB 7

DB 34h
DB 12h

Copiar este código en el emulador y ver el mismo resultado.


ORG 100h es una directiva del compilador (le indica al compilador cómo manejar el código fuente). Esta
directiva es muy importante cuando trabajas con variables. Le dice al compilador que el archivo ejecutable
se cargará en el desplazamiento de 100 h (256 bytes), por lo que el compilador debe calcular la dirección
correcta para todas las variables cuando reemplaza los nombres de las variables con sus desplazamientos.
Las directivas nunca se convierten a ningún código de máquina real.
¿Por qué el archivo ejecutable se carga con un desplazamiento de 100 h? El sistema operativo guarda
algunos datos sobre el programa en los primeros 256 bytes del CS (segmento de código), como parámetros
de línea de comando, etc.
Aunque esto es cierto sólo para archivos COM, los archivos EXE se cargan con un desplazamiento de 0000 y
generalmente utilizan un segmento especial para las variables. Quizás hablemos más sobre los archivos EXE
más adelante.

MATRICES(ARRAYS)

Las matrices pueden verse como cadenas de variables. Una cadena de texto es un ejemplo de una matriz de
bytes, cada carácter se presenta como un valor de código ASCII (0..255).

A continuación se muestran algunos ejemplos de definición de matrices:


a) DB 48h, 65h, 6Ch, 6Ch, 6Fh, 00h
b) DB 'Hello', 0

8
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

b) es una copia exacta de la matriz a), cuando el compilador ve una cadena entre comillas, la convierte
automáticamente en un conjunto de bytes. Este gráfico muestra una parte de la memoria donde se declaran
estos arreglos:

Puede acceder al valor de cualquier elemento de la matriz utilizando corchetes, por ejemplo:
MOV AL, a[3]
También puedes utilizar cualquiera de los registros índice de memoria BX, SI, DI, BP, por ejemplo:
MOV SI, 3
MOV AL, a[SI]

Si necesita declarar una matriz grande, puede utilizar el operador DUP.


La sintaxis de DUP:
número DUP ( valor(es) )
número: número de duplicados a realizar (cualquier valor constante).
valor: expresión que DUP duplicará.
Por ejemplo:
cDB 5 DUP(9)
es una forma alternativa de declarar:
c DB 9, 9, 9, 9, 9

un ejemplo más:
dDB 5 DUP(1, 2)
es una forma alternativa de declarar:
dDB 1, 2, 1, 2, 1, 2, 1, 2, 1, 2

Por supuesto, puede usar DW en lugar de DB si es necesario mantener valores mayores que 255 o menores
que -128. DW no se puede utilizar para declarar cadenas.

9
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

Obtener la dirección de una variable

Hay una instrucción LEA (Load Effective Address) y un operador OFFSET alternativo. Tanto OFFSET como LEA
se pueden utilizar para obtener la dirección de desplazamiento de la variable.
LEA es más poderosa porque también le permite obtener la dirección de variables indexadas. Obtener la
dirección de la variable puede resultar muy útil en algunas situaciones.
Recordatorio:
Para informarle al compilador sobre el tipo de datos,
se deben utilizar estos prefijos:
BYTE PTR - para byte.
WORD PTR: para palabra (dos bytes).
Por ejemplo:
BYTE PTR [BX] ; acceso a bytes.
o
PALABRA PTR [BX] ; acceso a palabras.
El ensamblador también admite prefijos más cortos:

b. - para BYTE PTR


w. - para PALABRA PTR

A veces, el compilador puede calcular el tipo de datos automáticamente, pero no puede ni debe confiar
en eso cuando uno de los operandos es un valor inmediato.

Este es un primer ejemplo

ORG 100h
MOV AL, VAR1 ; chequeamos el valor de VAR1 copiandolo a AL.
LEA BX, VAR1 ; obtenemos la direccion de VAR1 en BX.
MOV BYTE PTR [BX], 44h ; modificamos el valor VAR1.
MOV AL, VAR1 ; chequeamos el valor de VAR1 copiandolo a AL.
RET
VAR1 DB 22h

END
Ahora usamos OFFSET en vez de LEA:

ORG 100h

MOV AL, VAR1 ; chequeamos VAR1 copiandolo a AL.

MOV BX, OFFSET VAR1 ; obtenemos la dirección de VAR1 en BX.

MOV BYTE PTR [BX], 44h ; modificamos el contenido de VAR1.

MOV AL, VAR1 ; chequeamos el valor de VAR1copiandolo a AL.

RET

10
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

VAR1 DB 22h

END

Ambos ejemplos cumplen la misma función.


Estas líneas:
LEA BX, VAR1
MOV BX,OFFSET VAR1
incluso se compilan en el mismo código de máquina: MOV BX, num
num es un valor de 16 bits de la variable offset.

Tenga en cuenta que sólo estos registros se pueden utilizar entre corchetes (como punteros de memoria):
BX, SI, DI, BP.
(ver parte anterior del tutorial).

Constantes

Las constantes son como las variables, pero existen sólo hasta que se compila (ensambla) el programa.
Después de la definición de una constante, su valor no se puede cambiar. Para definir constantes se utiliza la
directiva EQU:

nombre EQU <cualquier expresión>


por ejemplo:
k EQU 5

MOV AX, k

Es lo mismo que

MOV AX, 5

Puede ver las variables mientras se ejecuta su programa seleccionando "Variables" en el menú "Ver" del
emulador.

11
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

Para ver matrices, debe hacer clic en una variable y establecer la propiedad Elementos en el tamaño de la
matriz. En lenguaje ensamblador no existen tipos de datos estrictos, por lo que cualquier variable se puede
presentar como una matriz.

La variable se puede ver en cualquier sistema de numeración:


HEX - hexadecimal (base 16).
BIN - binario (base 2).
OCT - octal (base 8).
FIRMADO: decimal con signo (base 10).
SIN FIRMAR: decimal sin signo (base 10).
CHAR: código de caracteres ASCII (hay 256 símbolos, algunos símbolos son invisibles).

Puede editar el valor de una variable cuando su programa se está ejecutando, simplemente haga doble clic
en él o selecciónelo y haga clic en el botón Editar.

Es posible ingresar números en cualquier sistema, los números hexadecimales deben tener el sufijo "h", el
sufijo binario "b", el sufijo octal "o", los números decimales no requieren sufijo. La cadena se puede ingresar
de esta manera:
'hola mundo', 0
(Esta cadena termina en cero).

Se pueden ingresar matrices de esta manera:


1, 2, 3, 4, 5

12
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

(la matriz puede ser una matriz de bytes o palabras, depende de si se selecciona BYTE o WORD para la
variable editada).

Las expresiones se convierten automáticamente, por ejemplo:


cuando se ingresa esta expresión:
5+2
se convertirá a 7, etc.

Interrupciones

Las interrupciones pueden verse como una serie de funciones. Estas funciones hacen que la programación
sea mucho más fácil, en lugar de escribir un código para imprimir un carácter, simplemente puedes llamar a
la interrupción y ella hará todo por ti. También hay funciones de interrupción que funcionan con unidades
de disco y otro hardware. A estas funciones las llamamos interrupciones de software.
Las interrupciones también son provocadas por diferentes hardware, lo que se denomina interrupciones de
hardware. Actualmente sólo nos interesan las interrupciones de software.
Para realizar una interrupción de software existe una instrucción INT, tiene una sintaxis muy simple:
INT valor
Donde el valor puede ser un número entre 0 y 255 (o 0 a 0FFh),
generalmente usaremos números hexadecimales.
Quizás pienses que sólo hay 256 funciones, pero eso no es correcto. Cada interrupción puede tener
subfunciones.
Para especificar una subfunción, se debe configurar el registro AH antes de llamar a la interrupción.
Cada interrupción puede tener hasta 256 subfunciones (por lo que obtenemos 256 * 256 = 65536
funciones). En general, se utiliza el registro AH, pero a veces es posible que se utilicen otros registros.
Generalmente se utilizan otros registros para pasar parámetros y datos a la subfunción.

El siguiente ejemplo utiliza la subfunción INT 10h 0Eh para escribir "¡Hola!" mensaje. Esta función muestra
un personaje en la pantalla, avanza el cursor y desplaza la pantalla según sea necesario.

ORG 100h ; Indica al compilador que cree un archivo .com simple de un solo segmento.

; La subfunción que estamos usando.


; no modifica el registro AH en
; return, por lo que podremos configurarlo solo una vez.

13
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

MOV AH, 0Eh ; seleccionamos la subfunción

;la subfunción INT 10h / 0Eh


; recibe un código ASCII del
; carácter que se imprimirá
; en el registro AL.

MOV AL, 'H' ; código ASCII: 72


INT 10h ; imrimirlo!

MOV AL, 'e' ; código ASCII: 101


INT 10h ; imprimirlo!

MOV AL, 'l' ; código ASCII : 108


INT 10h ; imprimirlo!

MOV AL, 'l' ; código ASCII : 108


INT 10h ; imrpimirlo!

MOV AL, 'o' ; código ASCII : 111


INT 10h ; imprimirlo!

MOV AL, '!' ; código ASCII: 33


INT 10h ; imprimirlo!

RET ; retorno al Sistema operativo

Copie y pegue el programa anterior en el editor de código fuente y presione el botón [Compilar y emular].
¡Ejecutarlo!

Biblioteca de funciones comunes - [Link]

Para facilitar la programación, existen algunas funciones comunes que se pueden incluir en su programa.
Para que su programa use funciones definidas en otro archivo, debe usar la directiva INCLUDE seguida de un
nombre de archivo. El compilador busca automáticamente el archivo en la misma carpeta donde se
encuentra el archivo fuente y, si no puede encontrar el archivo allí, busca en la carpeta Inc.
Actualmente es posible que no puedas entender completamente el contenido de [Link] (ubicado en
la carpeta Inc), pero está bien, ya que sólo necesitas entender lo que puede hacer.
Para utilizar cualquiera de las funciones en [Link], debe tener la siguiente línea al principio de su
archivo fuente:
include '[Link]'

14
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

[Link] define las siguientes macros:


PUTC char: macro con 1 parámetro, imprime un carácter ASCII en la posición actual del cursor.

GOTOXY col, fila - macro con 2 parámetros, establece la posición del cursor.

PRINT cadena: macro con 1 parámetro, imprime una cadena.

PRINTN cadena: macro con 1 parámetro, imprime una cadena. Lo mismo que PRINT pero agrega
automáticamente "retorno de carro" al final de la cadena.

CURSOROFF: desactiva el cursor de texto.

CURSORON: activa el cursor de texto.

Para utilizar cualquiera de las macros anteriores, simplemente escriba su nombre en algún lugar de su
código y, si es necesario, los parámetros, por ejemplo:

include [Link]

ORG 100h

PRINT 'Hola Mundo!'

GOTOXY 10, 5

PUTC 65 ; 65 – es el codigo ASCII de 'A'


PUTC 'B'

RET ; retorna al Sistema operativo


END ; directive de stop del compilador.

Cuando el compilador procesa su código fuente, busca en el archivo [Link] declaraciones de macros y
reemplaza los nombres de las macros con código real. Generalmente, las macros son partes de código
relativamente pequeñas; el uso frecuente de una macro puede hacer que el ejecutable sea demasiado
grande (los procedimientos son mejores para optimizar el tamaño).

15
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

[Link] también define los siguientes procedimientos:

PRINT_STRING: procedimiento para imprimir una cadena terminada en nulo en la posición actual del cursor,
recibe la dirección de la cadena en el registro DS:SI. Para usarlo, declare: DEFINE_PRINT_STRING antes de la
directiva END.

PTHIS: procedimiento para imprimir una cadena terminada en nulo en la posición actual del cursor (al igual
que PRINT_STRING), pero recibe la dirección de la cadena de la pila. La cadena TERMINADA EN CERO debe
definirse justo después de la instrucción CALL. Por ejemplo:

CALL PTHIS
db '¡Hola mundo!', 0

Para usarlo, declare: DEFINE_PTHIS antes de la directiva END.

GET_STRING: procedimiento para obtener una cadena terminada en nulo de un usuario, la cadena recibida
se escribe en el búfer en DS:DI, el tamaño del búfer debe estar en DX. El procedimiento detiene la entrada
cuando se presiona 'Entrar'. Para usarlo, declare: DEFINE_GET_STRING antes de la directiva END.

CLEAR_SCREEN: procedimiento para borrar la pantalla (que se realiza desplazándose por toda la ventana de
la pantalla) y establecer la posición del cursor en la parte superior de ella. Para usarlo, declare:
DEFINE_CLEAR_SCREEN antes de la directiva END.

SCAN_NUM: procedimiento que obtiene el número FIRMADO de varios dígitos del teclado y almacena el
resultado en el registro CX. Para usarlo, declare: DEFINE_SCAN_NUM antes de la directiva END.

PRINT_NUM: procedimiento que imprime un número firmado en el registro AX. Para usarlo, declare:
DEFINE_PRINT_NUM y DEFINE_PRINT_NUM_UNS antes de la directiva END.

PRINT_NUM_UNS: procedimiento que imprime un número sin firmar en el registro AX. Para usarlo, declare:
DEFINE_PRINT_NUM_UNS antes de la directiva END.

16
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

Para utilizar cualquiera de los procedimientos anteriores, primero debe declarar la función al final de su
archivo (pero antes de la directiva END) y luego usar la instrucción CALL seguida de un nombre de
procedimiento. Por ejemplo:
include '[Link]'

ORG 100h

LEA SI, msg1 ;pregunta por el número


CALL print_string ;
CALL scan_num ; pone el numero en CX.

MOV AX, CX ; copia el numero a AX.

; imprime la siguiente cadena


CALL pthis
DB 13, 10, 'Has anotado: ', 0

CALL print_num ; imprime el núemro en AX.

RET ; retorna al sistema operativo

msg1 DB 'Entre un nuemro: ', 0

DEFINE_SCAN_NUM
DEFINE_PRINT_STRING
DEFINE_PRINT_NUM
DEFINE_PRINT_NUM_UNS ; requerido para print_num.
DEFINE_PTHIS

END ; directiva que para el compilador

Primero el compilador procesa las declaraciones (estas son simplemente las macros normales que se
expanden a procedimientos). Cuando el compilador llega a la instrucción CALL, reemplaza el nombre del
procedimiento con la dirección del código donde se declara el procedimiento. Cuando se ejecuta la
instrucción CALL, el control se transfiere al procedimiento. Esto es bastante útil, ya que incluso si llamas al
mismo procedimiento 100 veces en tu código, seguirás teniendo un tamaño ejecutable relativamente
pequeño. Parece complicado, ¿no? Está bien, con el tiempo aprenderás más; actualmente es necesario que
comprendas el principio básico.

17
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

Instrucciones Aritméticas y Lógicas

La mayoría de las instrucciones aritméticas y lógicas afectan el registro de estado del procesador (o
banderas).

Como puede ver, hay 16 bits en este registro, cada bit se llama bandera y puede tomar un valor de 1 o 0.

Bandera de acarreo (CF): esta bandera se establece en 1 cuando hay un desbordamiento sin firmar. Por
ejemplo, cuando agrega bytes 255 + 1 (el resultado no está en el rango 0...255). Cuando no hay
desbordamiento, este indicador se establece en 0.

Bandera cero (ZF): se establece en 1 cuando el resultado es cero. Para ningún resultado cero, este indicador
se establece en 0.

Bandera de signo (SF): se establece en 1 cuando el resultado es negativo. Cuando el resultado es positivo, se
establece en 0. En realidad, este indicador toma el valor del bit más significativo.

Bandera de desbordamiento (OF): se establece en 1 cuando hay un desbordamiento firmado. Por ejemplo,
cuando agrega los bytes 100 + 50 (el resultado no está en el rango -128...127).

Bandera de paridad (PF): esta bandera se establece en 1 cuando hay un número par de bits uno en el
resultado y en 0 cuando hay un número impar de bits uno. Incluso si el resultado es una palabra, ¡sólo se
analizan 8 bits bajos!

18
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

Bandera auxiliar (AF): se establece en 1 cuando hay un desbordamiento sin firmar para cuarteto bajo (4
bits).

Indicador de habilitación de interrupción (IF): cuando este indicador se establece en 1, la CPU reacciona a
las interrupciones de dispositivos externos.

Bandera de dirección (DF): algunas instrucciones utilizan esta bandera para procesar cadenas de datos;
cuando esta bandera se establece en 0, el procesamiento se realiza hacia adelante; cuando esta bandera se
establece en 1, el procesamiento se realiza hacia atrás.

Hay 3 grupos de instrucciones.

Primer grupo: ADD, SUB,CMP, AND, TEST, OR, XOR


Se admiten estos tipos de operandos:

REG, memory
memory, REG
REG, REG
memory, immediate
REG, immediate
REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.

memory: [BX], [BX+SI+7], variable, etc...

immediate: 5, -24, 3Fh, 10001101b, etc...

Después de la operación entre operandos, el resultado siempre se almacena en el primer operando. Las
instrucciones CMP y TEST afectan únicamente a los indicadores y no almacenan un resultado (estas
instrucciones se utilizan para tomar decisiones durante la ejecución del programa).

Estas instrucciones afectan únicamente a estas banderas:


CF, ZF, SF, OF, PF, AF.

ADD: agrega el segundo operando al primero.

SUB: resta el segundo operando al primero.

19
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

CMP: resta el segundo operando del primero solo para indicadores.

AND: AND lógico entre todos los bits de dos operandos. Se aplican estas reglas:
1 AND 1 = 1
1 AND 0 = 0
0 AND 1 = 0
0 AND 0 = 0

TEST: Lo mismo que AND pero solo para banderas.

OR: OR lógico entre todos los bits de dos operandos. Se aplican estas reglas:

1 OR 1 = 1
1 OR 0 = 1
0 OR 1 = 1
0 OR 0 = 0

XOR: XOR lógico (OR exclusivo) entre todos los bits de dos operandos. Se aplican estas reglas:

1 XOR 1 = 0
1 XOR 0 = 1
0 XOR 1 = 1
0 XOR 0 = 0

Segundo grupo: MUL, IMUL, DIV, IDIV

Se admiten estos tipos de operandos:


REG
memory
REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.

memory: [BX], [BX+SI+7], variable, etc...

20
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

Las instrucciones MUL e IMUL afectan únicamente a estos indicadores:


CF, DE
Cuando el resultado supera el tamaño del operando, estos indicadores se establecen en 1, cuando el
resultado se ajusta al tamaño del operando, estos indicadores se establecen en 0.

Para DIV e IDIV, los indicadores no están definidos.

MUL - Multiplicación sin signo:


cuando el operando es un byte:
AX = AL * operando.
cuando el operando es una palabra:
(DX AX) = AX * operando.

IMUL - Multiplicación con signo:


cuando el operando es un byte:
AX = AL * operando.
cuando el operando es una palabra:
(DX AX) = AX * operando.

DIV - División sin signo:


cuando el operando es un byte:
AL = AX / operando
AH = resto (módulo). .
cuando el operando es una palabra:
AX = (DX AX) / operando
DX = resto (módulo). .

IDIV - División con signo:


cuando el operando es un byte:
AL = AX / operando
AH = resto (módulo). .

21
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

cuando el operando es una palabra:


AX = (DX AX) / operando
DX = resto (módulo). .

Tercer grupo: INC, DEC, NOT, NEG

Se admiten estos tipos de operandos:


REG
memory
REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.

memory: [BX], [BX+SI+7], variable, etc...


Las instrucciones INC, DEC afectan únicamente a estas banderas:
ZF, SF, DE, PF, AF.

La instrucción NOT no afecta ninguna bandera!

La instrucción NEG afecta únicamente a estos indicadores:


CF, ZF, SF, OF, PF, AF.

NOT: invierte cada bit del operando.

NEG: convierte el operando en negativo (complemento a dos). En realidad, invierte cada bit de operando y
luego le suma 1. Por ejemplo, 5 se convertirá en -5 y -2 se convertirá en 2.

Control de flujo del programa

Controlar el flujo del programa es algo muy importante, aquí es donde su programa puede tomar decisiones
de acuerdo con ciertas condiciones.
saltos incondicionales

La instrucción básica que transfiere el control a otro punto del programa es JMP.

22
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

La sintaxis básica de la instrucción JMP:


JMP etiqueta
Para declarar una etiqueta en su programa, simplemente escriba su nombre y agregue ":" al final, la etiqueta
puede ser cualquier combinación de caractéres pero no puede comenzar con un número; por ejemplo, aquí
hay 3 definiciones de etiquetas legales:
etiqueta1:
etiqueta2:
a:
La etiqueta se puede declarar en una línea separada o antes de cualquier otra instrucción, por ejemplo:
x1: MOV Ax, 1

x2: MOV Ax, 2

Aquí hay un ejemplo de instrucción JMP:


org 100h

mov ax, 5 ; seteo ax con 5.


mov bx, 2 ; seteo bx con 2.

jmp calc ; salto a 'calc'.

back: jmp stop ; salto a 'stop'.

calc:
add ax, bx ; sumo bx to ax.
jmp back ; salto a 'back'.

stop:

ret ; return to operating system.

Por supuesto, existe una forma más sencilla de calcular algunos de dos números, pero sigue siendo un buen
ejemplo de instrucción JMP.
Como puede ver en este ejemplo, JMP puede transferir el control tanto hacia adelante como hacia atrás.
Puede saltar a cualquier parte del segmento de código actual (65.535 bytes).

Saltos condicionales cortos

23
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

A diferencia de la instrucción JMP que realiza un salto incondicional, hay instrucciones que realizan saltos
condicionales (saltan solo cuando algunas condiciones están vigentes). Estas instrucciones se dividen en tres
grupos: el primer grupo simplemente prueba una sola bandera, el segundo compara los números con signo y
el tercero compara los números sin signo.

Instrucciones de salto que prueban una sola bandera

Instrucción Descripción Condición Instrucción opuesta

JZ , JE Salta si es cero (Igual). ZF = 1 JNZ, JNE

JC , JB, JNAE Salta si hay accarreo . CF = 1 JNC, JNB, JAE

JS Salta si tiene signo. SF = 1 JNS

JO Salta si hay desbordamiento. OF = 1 JNO

JPE, JP Salta si la paridad es par. PF = 1 JPO

JNZ , JNE Salta si no es cero (No igual). ZF = 0 JZ, JE

JNC , JNB, JAE Salta si no hay acarreo CF = 0 JC, JB, JNAE

JNS Salta si no hay signo. SF = 0 JS

JNO Salta si no hay paridad. OF = 0 JO

JPO, JNP Salta si la paridad es impar. PF = 0 JPE, JP

Como ya habrás notado, hay algunas instrucciones que hacen lo mismo, eso es correcto, incluso están
ensambladas en el mismo código de máquina, por lo que es bueno recordar que cuando compilas la
instrucción JE, la desensamblas como: JZ, JC. se ensambla igual que JB etc...
Se utilizan diferentes nombres para hacer que los programas sean más fáciles de entender, codificar y, lo
más importante, recordar.
Si emulas el código siguiente, verá que todas las instrucciones están ensambladas en JNB, el código
operativo (código de operación) para esta instrucción es 73h, esta instrucción tiene una longitud fija de dos
bytes, el segundo byte es el número de bytes para agregar al registro IP si la condición es verdadera. debido

24
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

a que la instrucción tiene solo 1 byte para mantener el desplazamiento, está limitada a pasar el control a -
128 bytes hacia atrás o 127 bytes hacia adelante, este valor siempre está con signo.
jnc a
jnb a
jae a

mov ax, 4
a: mov ax, 5
ret

Instrucciones de salto para números con signo

Instrucción Descripción Condición Instrucción opuesta

Salta si es igual (=).


JE , JZ ZF = 1 JNE, JNZ
Salta si es cero.

Salta si no es igual (<>).


JNE , JNZ ZF = 0 JE, JZ
Salta si no es cero.

ZF = 0
Salta si es mayor (>).
JG , JNLE y JNG, JLE
Salta si no es menor o igual (not <=).
SF = OF

Salta si es menor (<).


JL , JNGE SF <> OF JNL, JGE
Salta si no es mayor o igual (not >=).

Salta si es mayor o igual(>=).


JGE , JNL SF = OF JNGE, JL
S (not <).

ZF = 1
Salta si es menor o igual(<=).
JLE , JNG or JNLE, JG
Salta si no es mayor(not >).
SF <> OF

<> - significa no igual.

25
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

Instrucciones de salto para números sin signo

Instrucción Descripción Condición Instrucción opuesta

Jump if Equal (=).


JE , JZ ZF = 1 JNE, JNZ
Jump if Zero.

Jump if Not Equal (<>).


JNE , JNZ ZF = 0 JE, JZ
Jump if Not Zero.

CF = 0
Jump if Above (>).
JA , JNBE and JNA, JBE
Jump if Not Below or Equal (not <=).
ZF = 0

Jump if Below (<).


JB , JNAE, JC Jump if Not Above or Equal (not >=). CF = 1 JNB, JAE, JNC
Jump if Carry.

Jump if Above or Equal (>=).


JAE , JNB, JNC Jump if Not Below (not <). CF = 0 JNAE, JB
Jump if Not Carry.

CF = 1
Jump if Below or Equal (<=).
JBE , JNA or JNBE, JA
Jump if Not Above (not >).
ZF = 1

Generalmente, cuando se requiere comparar valores numéricos se usa la instrucción CMP (hace lo mismo
que la instrucción SUB (resta), pero no guarda el resultado, solo afecta las banderas).
La lógica es muy simple, por ejemplo:
es necesario comparar 5 y 2,
5-2=3
el resultado no es cero (el indicador cero se establece en 0).
Otro ejemplo:
es necesario comparar 7 y 7,
7-7=0
¡el resultado es cero! (Zero Flag se establece en 1 y JZ o JE harán el salto).

26
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

Aquí hay un ejemplo de instrucción CMP y salto condicional:

include "[Link]"

org 100h

mov al, 25 ; seteo al con 25.


mov bl, 10 ; seteo bl con 10.

cmp al, bl ; comparo al - bl.

je equal ; salta si al = bl (zf = 1).

putc 'n' ; si llega aquí, entonces al <> bl,


jmp stop ; luego imprime 'n', y salta a stop.

equal: ; si llega aquí,


putc 'y' ; entonces al = bl, luego imprime 'y'.

stop:

ret ; llega aquí pase lo que pase.

Pruebe el ejemplo anterior con números diferentes para AL y BL, abra las banderas haciendo clic en el botón
de banderas, use un solo paso y vea qué sucede. puede usar la tecla de acceso rápido F5 para recompilar y
recargar el programa en el emulador.

Loops

Instrucción Operacion y condicion de salto Instrucción opuesta

LOOP disminuir cx, saltar a la etiqueta si cx no es cero. DEC CX and JCXZ

LOOPE disminuir cx, saltar a la etiqueta si cx no es cero e igual (zf = 1). LOOPNE

LOOPNE disminuir cx, saltar a la etiqueta si cx no es cero y no es igual (zf = 0). LOOPE

LOOPNZ disminuir cx, saltar a la etiqueta si cx no es cero y zf = 0. LOOPZ

LOOPZ disminuir cx, saltar a la etiqueta si cx no es cero y zf = 1. LOOPNZ

27
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

JCXZ salte a la etiqueta si cx es cero. OR CX, CX and JNZ

Los bucles son básicamente los mismos saltos, es posible codificar bucles sin usar la instrucción de bucle,
simplemente usando saltos condicionales y comparar, y esto es exactamente lo que hace el bucle. todas las
instrucciones de bucle usan el registro CX para contar pasos, como sabes el registro CX tiene 16 bits y el
valor máximo que puede contener es 65535 o FFFF, sin embargo con cierta agilidad es posible meter un
bucle en otro, y otro en otros dos, y tres y etc... y recibir un buen valor de 65535 * 65535 * 65535....hasta el
infinito.... o el final de la memoria ram o pila. Es posible almacenar el valor original del registro cx usando la
instrucción push cx y devolverlo al original cuando el bucle interno finalice con pop cx, por ejemplo:

org 100h

mov bx, 0 ; contador total de pasos.


mov cx, 5
k1: add bx, 1
mov al, '1'
mov ah, 0eh
int 10h
push cx
mov cx, 5
k2: add bx, 1
mov al, '2'
mov ah, 0eh
int 10h
push cx
mov cx, 5
k3: add bx, 1
mov al, '3'
mov ah, 0eh
int 10h
loop k3 ; loop interno dentro de otro interno.
pop cx
loop k2 ; loop interno.
pop cx
loop k1 ; loop externo.

Ret

28
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

bx cuenta el número total de pasos, de forma predeterminada el emulador muestra los valores en
hexadecimal, puede hacer doble clic en el registro para ver el valor en todas las bases disponibles.
Al igual que todos los demás bucles de saltos condicionales, tienen un compañero opuesto que puede
ayudar a crear soluciones alternativas, cuando la dirección de la ubicación deseada está demasiado lejos,
assembelr ensambla automáticamente instrucciones de salto inverso y largo, lo que hace un total de 5 bytes
en lugar de solo 2, como se puede ver también en el desensamblador.
para obtener una descripción más detallada y ejemplos, consulte el conjunto completo de instrucciones
8086
Todos los saltos condicionales tienen una gran limitación: a diferencia de las instrucciones JMP, solo pueden
saltar 127 bytes hacia adelante y 128 bytes hacia atrás (tenga en cuenta que la mayoría de las instrucciones
se ensamblan en 3 o más bytes). Mediante técnicas de programación se puede evitar este inconveniente, no
son parte del curso por ahora .

PROCEDIMIENTOS

El procedimiento es una parte del código que se puede llamar desde su programa para realizar alguna tarea
específica. Los procedimientos hacen que el programa sea más estructural y más fácil de entender.
Generalmente el procedimiento regresa al mismo punto desde donde fue llamado.

La sintaxis para la declaración de procedimiento:


nombre PROC

; aqui va el codigo
; del procedimiento...

RET
nombre ENDP

nombre - es el nombre del procedimiento, el mismo nombre debe estar en la parte superior e inferior, esto
se utiliza para verificar el cierre correcto de los procedimientos.

Probablemente ya sepa que la instrucción RET se utiliza para regresar al sistema operativo. La misma
instrucción se utiliza para regresar del procedimiento (en realidad, el sistema operativo ve su programa
como un procedimiento especial).

PROC y ENDP son directivas de compilación, por lo que no están ensambladas en ningún código de máquina
real. El compilador simplemente recuerda la dirección del procedimiento.

29
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

La instrucción CALL se utiliza para llamar a un procedimiento.

Aquí hay un ejemplo:

ORG 100h

CALL m1

MOV AX, 2

RET ; retorna al sistema operativo.

m1 PROC
MOV BX, 5
RET ; retorna al lugar desde donde se invoco el procedimiento.
m1 ENDP

END

El ejemplo anterior llama al procedimiento m1, realiza MOV BX, 5 y regresa a la siguiente instrucción
después de CALL: MOV AX, 2.

Hay varias formas de pasar parámetros a un procedimiento, la forma más sencilla de pasar parámetros es
mediante registros, aquí hay otro ejemplo de un procedimiento que recibe dos parámetros en los registros
AL y BL, multiplica estos parámetros y devuelve el resultado en el registro AX:

ORG 100h

MOV AL, 1
MOV BL, 2

CALL m2
CALL m2
CALL m2
CALL m2

RET ;retorna al sistema operativo.

m2 PROC
MUL BL ; AX = AL * BL.
RET ; retorna al lugar desde donde se lo llamo.
m2 ENDP

END

30
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

En el ejemplo anterior, el valor del registro AL se actualiza cada vez que se llama al procedimiento, el
registro BL permanece sin cambios, por lo que este algoritmo calcula 2 en potencia de 4.
entonces el resultado final en el registro AX es 16 (o 10h).

PILA

La pila es un área de memoria para guardar datos temporales. La instrucción CALL utiliza la pila para
mantener la dirección de retorno del procedimiento, la instrucción RET obtiene este valor de la pila y
regresa a ese desplazamiento. Lo mismo sucede cuando la instrucción INT llama a una interrupción,
almacena en el registro de bandera de la pila, el segmento de código y el desplazamiento. La instrucción
IRET se utiliza para regresar de una llamada de interrupción.

También podemos usar la pila para guardar cualquier otro dato,


Hay dos instrucciones que funcionan con la pila:

PUSH: almacena un valor de 16 bits en la pila.

POP: obtiene un valor de 16 bits de la pila.

Sintaxis de la instruccion PUSH :

PUSH REG
PUSH SREG
PUSH memory
PUSH immediate
REG: AX, BX, CX, DX, DI, SI, BP, SP.

SREG: DS, ES, SS, CS.

memory: [BX], [BX+SI+7], 16 bit variable, etc...

immediate: 5, -24, 3Fh, 10001101b, etc...

Sintaxis de la instrucción POP:

31
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

POP REG
POP SREG
POP memory
REG: AX, BX, CX, DX, DI, SI, BP, SP.

SREG: DS, ES, SS, (except CS).

memory: [BX], [BX+SI+7], 16 bit variable, etc...


Notas:
¡PUSH y POP funcionan solo con valores de 16 bits!

Nota: ¡PUSH inmediato funciona solo en CPU 80186 y posteriores!


La pila utiliza el algoritmo LIFO (Last In First Out),
esto significa que si insertamos estos valores uno por uno en la pila:
1, 2, 3, 4, 5
El primer valor que obtendremos en pop será 5, luego 4, 3, 2 y solo después 1.

Es muy importante realizar la misma cantidad de PUSH y POP; de lo contrario, la pila podría dañarse y será
imposible regresar al sistema operativo. Como ya sabe, utilizamos la instrucción RET para regresar al sistema
operativo, por lo que cuando se inicia el programa hay una dirección de retorno en la pila (generalmente es
0000h).

Las instrucciones PUSH y POP son especialmente útiles porque no tenemos demasiados registros con los que
operar, así que aquí tienes un truco:
Almacene el valor original del registro en la pila (usando PUSH).

32
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

Utilice el registro para cualquier propósito.

Restaurar el valor original del registro desde la pila (usando POP).

Aquí hay un ejemplo:


ORG 100h

MOV AX, 1234h


PUSH AX ; guardamos el valor de AX en la pila.

MOV AX, 5678h ; modificamos el valor de AX .

POP AX ; restauramos el valor de AX.

RET

END

Otro uso de la pila es para intercambiar los valores,


Aquí hay un ejemplo:

ORG 100h

MOV AX, 1212h ; guardamos 1212h en AX.


MOV BX, 3434h ; guardamos 3434h en BX

PUSH AX ; guardamos el valor de AX en la pila.


PUSH BX ; guardamos el valor de BX en la pila.

POP AX ; seteamos AX con el valor original de BX.


POP BX ; seteamos BX con el valor original de AX.

RET

END

El intercambio ocurre porque la pila usa el algoritmo LIFO (Last In First Out), por lo que cuando presionamos
1212h y luego 3434h, en el pop obtendremos primero 3434h y solo después 1212h.
El área de memoria de la pila se establece mediante el registro SS (Segmento de pila) y el registro SP
(Puntero de pila). Generalmente, el sistema operativo establece los valores de estos registros al iniciar el
programa.

33
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

La instrucción "PUSH source" hace lo siguiente:


Resta 2 del registro SP.

Escriba el valor de la fuente en la dirección SS:SP.

La instrucción "Destino POP" hace lo siguiente:


Escriba el valor en la dirección SS:SP al destino.

Agregue 2 al registro SP.

La dirección actual señalada por SS:SP se denomina parte superior de la pila.

Para archivos COM, el segmento de pila es generalmente el segmento de código y el puntero de pila se
establece en el valor 0FFFEh. En la dirección SS:0FFFEh se almacena una dirección de retorno para la
instrucción RET que se ejecuta al final del programa.

Puede ver visualmente la operación de la pila haciendo clic en el botón [Apilar] en la ventana del emulador.
La parte superior de la pila está marcada con el signo "<".

34
U.T.N. Facultad Regional Venado Tuerto
Catedra: Arquitectura de Computadoras

Bibliografìa y fuente:
Link de descarga: [Link]
Fuente: tutorial de ayuda de emu8086

35

También podría gustarte