Informática
Introducción al Macro Assembler
TEMAS: PAGINA:
EDICIÓN 3
ENSAMBLADO 4
LINK 5
INSTRUCCIONES DEL MASM 6
EJECUCIÓN 25
DEPURACIÓN 26
LA UTILERIA EXE2BIN Y LOS ARCHIVOS
.EXE Y .COM 27
EL LENGUAJE DE MAQUINA 27
CREACIÓN Y DEPURACIÓN DE PROGRAMAS
CON UTILERIA DEBUG 28
PROGRAMA EJEMPLO 29
I. 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.
II. ENSAMBLADO
El ensamblado se lleva a cabo invocando al MASM. Este puede ser invocado, usando una línea de comando, de la siguiente manera:
MASM archivo [,[objeto][,[listado][,[cross]]]]][opciones][;]
donde:
-
Archivo. Corresponde al programa fuente. Por defecto se toma la extensión .ASM.
-
Objeto. Es el nombre para el archivo objeto.
-
Listado. Nombre del archivo de listado de ensamblado.
-
Cross. Es un archivo de referencias cruzadas.
-
Opciones. Pueden ser:
/A Escribe los segmentos en orden alfabético.
/S Escribe los segmentos en orden del fuente.
/Bnum Fija buffer de tamaño num.
/C Especifica un archivo de referencias cruzadas.
/L Especifica un listado de ensamble.
/D Crea listado del paso 1.
/Dsym Define un símbolo que puede usarse en el ensamble.
/Ipath Fija path para buscar archivos a incluir.
/ML Mantiene sensitividad de letras (mayús./minús) en nombre.
/MX Mantiene sensitividad en nombre públicos y externos.
/MU Convierte nombres a mayúsculas.
/N Suprime tablas en listados.
/P Chequea por código impuro.
/R Crea código para instrucciones de punto flotante.
/E Crea código para emular instrucciones de punto flotante.
/T Suprime mensajes de ensamble exitoso.
/V Despliega estadísticas adicionales en pantalla.
/X Incluye condicionales falsos en pantalla.
/Z Despliega líneas de error en pantalla.
Si el ; al final se omite es necesario poner todas las comas que se indican. Si no se quiere poner algún valor basta con dejar la coma.
La otra forma de invocar al ensamblador es sólo tecleando MASM y respondiendo a la información que se solicita. Para omitir algún valor sólo basta teclear ENTER sin dar ningún valor.
III. 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
-
Opciones. Pueden ser:
/HELP Muestra lista de opciones.
/PAUSE Pausa en el proceso.
/EXEPACK Empaca archivo ejecutable.
/MAP Crea mapa de símbolos públicos.
/LINENUMBERS Copia número de líneas al mapa.
/NOIGNORECASE Mantiene sensitividad en novres.
/NODEFAULTLIBRARYSEARC No usa bibliotecas por defecto.
/STACK:size Fija el tamaño del stack a usar.
/CPARMAXALLOC:número Fija alojación máxima de espacio.
/HIGH Fija la dirección de carga más alta.
/DSALLOCATE Aloja grupo de datos.
/NOGROUPASSOCIATION Ignora asociaciones para direcciones.
/OVERLAYINTERRUPT:número Asigna nuevo número a la INT 03Fh.
/SEGMENTS:número Procesa un número de segmentos.
/DOSSEG Sigue la convención de orden de DOS.
INSTRUCCIONES DEL MASM
Instrucciones de Transferencia de Datos
MOV dest,src: Copia el contenido del operando fuente (src) en el destino (dest).
Operación: dest <- src
Las posibilidades son:
1.MOV reg,{reg|mem|inmed}
2.MOV mem,{reg|inmed}
3.MOV {reg16|mem16},{CS|DS|ES|SS}
4.MOV {DS|ES|SS},{reg16|mem16}
PUSH src: Pone el valor en el tope del stack.
Operación: SP <- SP - 2, [SP+1:SP] <- src donde src = {reg16|mem16|CS|DS|ES|SS}.
POP dest: Retira el valor del tope del stack poniéndolo en el lugar indicado.
Operación: dest <- [SP+1:SP], SP <- SP + 2 donde dest = {reg16|mem16|DS|ES|SS}.
XCHG reg,{reg|mem}: Intercambia ambos valores.
IN {AL|AX},{DX|inmed (1 byte)}: Pone en el acumulador el valor hallado en el port indicado.
OUT {DX|inmed (1 byte)},{AL|AX}: Pone en el port indicado el valor del acumulador.
XLAT: Realiza una operación de traducción de un código de un byte a otro código de un byte mediante una tabla.
Operación: AL <- [BX+AL]
LEA reg,mem: Almacena la dirección efectiva del operando de memoria en un registro.
Operación: reg <- dirección mem
LDS reg,mem32: Operación: reg <- [mem], DS <- [mem+2]
LES reg,mem32: Operación: reg <- [mem], ES <- [mem+2]
LAHF: Copia en el registro AH la imagen de los ocho bits menos significativos del registro de indicadores.
Operación: AH <- SF:ZF:X:AF:X:PF:X:CF
SAHF: Almacena en los ocho bits menos significativos del registro de indicadores el valor del registro AH.
Operación: SF:ZF:X:AF:X:PF:X:CF <- AH
PUSHF: Almacena los flags en la pila.
Operación: SP <- SP - 2, [SP+1:SP] <- Flags.
POPF: Pone en los flags el valor que hay en la pila.
Operación: Flags <- [SP+1:SP], SP <- SP + 2
Instrucciones Aritméticas (Afecta flags AF, CF, OF, PF,SF, ZF)
ADD dest,src Operación: dest <- dest + src.
ADC dest,src Operación: dest <- dest + src + CF.
SUB dest,src Operación: dest <- dest - src.
SBB dest,src Operación: dest <- dest - src - CF.
CMP dest,src Operación: dest - src (sólo afecta flags).
INC dest Operación: dest <- dest + 1 (no afecta CF).
DEC dest Operación: dest <- dest - 1 (no afecta CF).
NEG dest Operación: dest <- - dest; donde dest = {reg|mem} y src = {reg|mem|inmed} no pudiendo ambos operandos estar en memoria.
DAA: Corrige el resultado de una suma de dos valores BCD empaquetados en el registro AL (debe estar inmediatamente después de una instrucción ADD o ADC). OF es indefinido después de la operación.
DAS: Igual que DAA pero para resta (debe estar inmediatamente después de una instrucción SUB o SBB).
AAA: Lo mismo que DAA para números BCD desempaquetados.
AAS: Lo mismo que DAS para números BCD desempaquetados.
AAD: Convierte AH:AL en BCD desempaquetado a AL en binario.
Operación: AL <- AH * 0Ah + AL, AH <- 0. Afecta PF, SF, ZF, mientras que AF, CF y OF quedan indefinidos.
AAM: Convierte AL en binario a AH:AL en BCD desempaquetado.
Operación: AH <- AL / 0Ah, AL <- AL mod 0Ah. Afecta PF, SF, ZF, mientras que AF, CF y OF quedan indefinidos.
MUL {reg8|mem8}: Realiza una multiplicación con operandos no signados de 8 por 8 bits.
Operación: AX <- AL * {reg8|mem8}. CF=OF=0 si AH = 0, CF=OF=1 en caso contrario. AF, PF, SF, ZF quedan indefinidos.
MUL {reg16|mem16}: Realiza una multiplicación con operandos no signados de 16 por 16 bits.
Operación: DX:AX <- AX * {reg16|mem16}. CF=OF=0 si DX = 0, CF=OF=1 en caso contrario. AF, PF, SF, ZF quedan indefinidos.
IMUL {reg8|mem8}: Realiza una multiplicación con operandos con signo de 8 por 8 bits.
Operación: AX <- AL * {reg8|mem8} realizando la multiplicación con signo. CF = OF = 0 si el resultado entra en un byte, en caso contrario valdrán 1. AF, PF, SF, ZF quedan indefinidos.
IMUL {reg16|mem16}: Realiza una multiplicación con operandos con signo de 16 por 16 bits.
Operación: DX:AX <- AX * {reg16|mem16} realizando la multiplicación con signo. CF =OF = 0 si el resultado entra en dos bytes, en caso contrario valdrán 1. AF, PF, SF, ZF quedan indefinidos.
CBW: Extiende el signo de AL en AX. No se afectan los flags.
CWD: Extiende el signo de AX en DX:AX. No se afectan flags.
Instrucciones Lógicas (Afectan AF, CF, OF, PF, SF, ZF)
AND dest,src: Operación: dest <- dest and src.
TEST dest,src: Operación: dest and src. Sólo afecta flags.
OR dest,src: Operación: dest <- dest or src.
XOR dest,src: Operación: dest <- dest xor src.
Las cuatro instrucciones anteriores ponen CF = OF = 0, AF queda indefinido y PF, SF y ZF dependen del resultado.
NOT dest: Operación: dest <- Complemento a 1 de dest. No afecta los flags.
SHL/SAL dest,{1|CL}: Realiza un desplazamiento lógico o aritmético a la izquierda.
SHR dest,{1|CL}: Realiza un desplazamiento lógico a la derecha.
SAR dest,{1|CL}: Realiza un desplazamiento aritmético a la derecha.
ROL dest,{1|CL}: Realiza una rotación hacia la izquierda.
ROR dest,{1|CL}: Realiza una rotación hacia la derecha.
RCL dest,{1|CL}: Realiza una rotación hacia la izquierda usando el CF.
RCR dest,{1|CL}: Realiza una rotación hacia la derecha usando el CF.
En las siete instrucciones anteriores la cantidad de veces que se rota o desplaza puede ser un bit o la cantidad de bits indicado en CL.
Instrucciones de Manipulación de Cadenas
MOVSB: Copia un byte de la cadena fuente al destino.
Operación:
1.ES:[DI] <- DS:[SI] (un byte)
2.DI <- DI±1
3.SI <- SI±1
MOVSW: Copia dos bytes de la cadena fuente al destino.
Operación:
1.ES:[DI] <- DS:[SI] (dos bytes)
2.DI <- DI±2
3.SI <- SI±2
LODSB: Pone en el acumulador un byte de la cadena fuente.
Operación:
1.AL <- DS:[SI] (un byte)
2.SI <- SI±1
LODSW: Pone en el acumulador dos bytes de la cadena fuente.
Operación:
1.AX <- DS:[SI] (dos bytes)
2.SI <- SI±2
STOSB: Almacena en la cadena destino un byte del acumulador.
Operación:
1.ES:[DI] <- AL (un byte)
2.DI <- DI±1
STOSW: Almacena en la cadena destino dos bytes del acumulador.
Operación:
1.ES:[DI] <- AX (dos bytes)
2.DI <- DI±2
CMPSB: Compara un byte de la cadena fuente con el destino.
Operación:
1.DS:[SI] - ES:[DI] (Un byte, afecta sólo los flags)
2.DI <- DI±1
3.SI <- SI±1
CMPSW: Compara dos bytes de la cadena fuente con el destino.
Operación:
1.DS:[SI] - ES:[DI] (Dos bytes, afecta sólo los flags)
2.DI <- DI±2
3.SI <- SI±2
SCASB: Compara un byte del acumulador con la cadena destino.
Operación:
1.AL - ES:[DI] (Un byte, afecta sólo los flags)
2.DI <- DI±1
SCASW: Compara dos bytes del acumulador con la cadena destino.
Operación:
1.AX - ES:[DI] (Dos byte, afecta sólo los flags)
2.DI <- DI±2
En todos los casos el signo + se toma si el indicador DF vale cero. Si vale 1 hay que tomar el signo (-).
Prefijo para las instrucciones MOVSB, MOVSW, LODSB, LODSW, STOSB y STOSW:
REP: Repetir la instrucción CX veces.
Prefijos para las instrucciones CMPSB, CMPSW, SCASB, SCASW:
REPZ/REPE: Repetir mientras que sean iguales hasta un máximo de CX veces.
REPNZ/REPNE: Repetir mientras que sean diferentes hasta un máximo de CX veces.
Instrucciones de Transferencia de Control (No afectan flags)
CALL label: Ir al procedimiento cuyo inicio es label.
RET: Retorno de procedimiento.
RET inmed: Retorno de procedimiento y SP <- SP + inmed.
Variaciones de la instrucción de retorno:
RETN [inmed]: En el mismo segmento de código.
RETF [inmed]: En otro segmento de código.
Instrucciones de Transferencia de Saltos
Saltos condicionales aritméticos (usar después de CMP):
Aritmética signada (con números positivos, negativos y cero)
JL etiqueta/JNGE etiqueta: Saltar a etiqueta si es menor.
JLE etiqueta/JNG etiqueta: Saltar a etiqueta si es menor o igual.
JE etiqueta: Saltar a etiqueta si es igual.
JNE etiqueta: Saltar a etiqueta si es distinto.
JGE etiqueta/JNL etiqueta: Saltar a etiqueta si es mayor o igual.
JG etiqueta/JNLE etiqueta: Saltar a etiqueta si es mayor.
Aritmética sin signo (con números positivos y cero)
JB etiqueta/JNAE etiqueta: Saltar a etiqueta si es menor.
JBE etiqueta/JNA etiqueta: Saltar a etiqueta si es menor o igual.
JE etiqueta: Saltar a etiqueta si es igual.
JNE etiqueta: Saltar a etiqueta si es distinto.
JAE etiqueta/JNB etiqueta: Saltar a etiqueta si es mayor o igual.
JA etiqueta/JNBE etiqueta: Saltar a etiqueta si es mayor.
Saltos condicionales según el valor de los indicadores
JC label: Saltar si hubo arrastre/préstamo (CF = 1).
JNC label: Saltar si no hubo arrastre/préstamo (CF = 0).
JZ label: Saltar si el resultado es cero (ZF = 1).
JNZ label: Saltar si el resultado no es cero (ZF = 0).
JS label: Saltar si el signo es negativo (SF = 1).
JNS label: Saltar si el signo es positivo (SF = 0).
JP/JPE label: Saltar si la paridad es par (PF = 1).
JNP/JPO label: Saltar si la paridad es impar (PF = 0).
Saltos condicionales que usan el registro CX como contador:
LOOP label: Operación: CX <- CX-1. Saltar a label si CX<>0.
LOOPZ/LOOPE label: Operación: CX <- CX-1. Saltar a label si CX <> 0 y ZF = 1.
LOOPNZ/LOOPNE label: Operación: CX <- CX-1. Saltar a label si CX <> 0 y ZF = 0.
JCXZ label ; Operación: Salta a label si CX = 0
Interrupciones
INT número: Salva los flags en la pila, hace TF=IF=0 y ejecuta la interrupción con el número indicado.
INTO: Interrupción condicional. Si OF = 1, hace INT 4.
IRET: Retorno de interrupción. Restaura los indicadores del stack.
Instrucciones de Control del Procesador
CLC: CF <- 0.
STC: CF <- 1.
CMC: CF <- 1 - CF.
NOP: No hace nada.
CLD: DF <- 0 (Dirección ascendente).
STD: DF <- 1 (Dirección descendente).
CLI: IF <- 0 (Deshabilita interrupciones enmascarables).
STI: IF <- 1 (Habilita interrupciones enmascarables).
HLT: Detiene la ejecución del procesador hasta que llegue una interrupción externa.
WAIT: Detiene la ejecución del procesador hasta que se active el pin TEST del mismo.
LOCK: Prefijo de instrucción que activa el pin LOCK del procesador
Operadores
Operadores aritméticos: +, -, *, /, MOD (resto de la división).
Operadores lógicos: AND, OR, XOR, NOT, SHR, SHL.
Para los dos últimos operadores, el operando derecho indica la cantidad de bits a desplazar hacia la derecha (para SHR) o izquierda (para SHL) el operando izquierdo.
Operadores relacionales: Valen cero si son falsos y 65535 si son verdaderos.
EQ: Igual a.
NE: Distinto de.
LT: Menor que.
GT: Mayor que.
LE: Menor o igual a.
GE: Mayor o igual a.
Operadores analíticos: Descomponen operandos que representan direcciones de memoria en sus componentes.
SEG memory-operand: Retorna el valor del segmento.
OFFSET memory-operand: Retorna el valor del offset.
TYPE memory-operand: Retorna un valor que representa el tipo de operando:
BYTE = 1,
WORD = 2, DWORD = 4 (para direcciones de datos) y NEAR = -1 y FAR = -2 (para direcciones de instrucciones).
LENGHT memory-operand: Se aplica solamente a direcciones de datos.
Retorna un valor numérico para el número de unidades (bytes, words o dwords) asociados con el operando. Si el operando es una cadena retorna el valor 1.
Ejemplo: Dada la directiva PALABRAS DW 50 DUP (0), el valor de LENGHT PALABRAS es 50, mientras que dada la directiva CADENA DB "cadena" el valor de LENGHT CADENA es 1.
SIZE memory-operand: LENGHT memory-operand * TYPE memory-operand.
Operadores sintéticos: Componen operandos de direcciones de memoria a partir de sus componentes.
Type PTR memory-operand: Compone un operando de memoria que tiene el mismo segmento y offset que el especificado en el operando derecho pero con el tipo (BYTE, WORD, DWORD, NEAR o FAR) especificado en el operando izquierdo.
THIS type: Compone un operando de memoria con el tipo specificado que tiene el segmento y offset que la próxima ubicación a ensamblar.
Operadores de macros: Son operadores que se utilizan en las definiciones de macros. Hay cinco: &, <>, !, % y ;;.
&parámetro: Reemplaza el parámetro con el valor actual del argumento.
<texto>: Trata una serie de caracteres como una sola cadena. Se utiliza cuando el texto incluye comas, espacios u otros símbolos especiales.
!carácter: Trata el carácter que sigue al operador ! como un carácter en vez de un símbolo o separador.
%texto: Trata el texto que sigue a continuación del operador % como una expresión. El ensamblador calcula el valor de la expresión y reemplaza el texto por dicho valor.
Sentencia Comentario: Permite definir comentarios que aparecerán en la definición de la macro pero no cada vez que éste se invoque en el listado fuente que genera el ensamblador.
Directivas
Definición de símbolos
EQU: Define nombres simbólicos que representan valores u otros valores simbólicos. Las dos formas son:
nombre EQU expresión.
nuevo_nombre EQU viejo_nombre.
Una vez definido un nombre mediante EQU, no se puede volver a definir.
=: Es similar a EQU pero permite que el símbolo se pueda redefinir. Sólo admite la forma:
nombre = expresión.
Definición de datos
Ubica memoria para un ítem de datos y opcionalmente asocia un nombre simbólico con esa dirección de memoria y/o genera el valor inicial para ese ítem.
[nombre] DB valor_inicial [, valor_inicial...]
donde valor_inicial puede ser una cadena o una expresión numérica cuyo resultado esté entre -255 y 255.
[nombre] DW valor_inicial [, valor_inicial...]
donde valor_inicial puede ser una expresión numérica cuyo resultado esté entre -65535 y 65535 o un operando de memoria en cuyo caso se almacenará el offset del mismo.
[nombre] DD valor_inicial [, valor_inicial...]
donde valor_inicial puede ser una constante cuyo valor esté entre 4294967295 y 4294967295, una expresión numérica cuyo valor absoluto no supere 65535, o bien un operando de memoria en cuyo caso se almacenarán el offset y el segmento del mismo (en ese orden). Si se desea que no haya valor inicial, deberá utilizarse el símbolo ?. Otra forma de expresar el valor inicial es:
cuenta DUP (valor_inicial [, valor_inicial...])
donde cuenta es la cantidad de veces que debe repetirse lo que está entre paréntesis.
Definición de segmentos
Organizan el programa para utilizar los segmentos de memoria del microprocesador 8088. Estos son SEGMENT, ENDS, DOSSEG, ASSUME, GROUP.
nombre_segm
SEGMENT [alineación][combinación]['clase']
sentencias
nombre_segm
ENDS
Alineación: Define el rango de direcciones de memoria para el cual puede elegirse el inicio del segmento. Hay cinco posibles:
1.BYTE: El segmento comienza en el siguiente byte.
2.WORD: El segmento comienza en la siguiente dirección par.
3.DWORD: Comienza en la siguiente dirección múltiplo de 4.
4.PARA: Comienza en la siguiente dirección múltiplo de 16.
5.PAGE: Comienza en la siguiente dirección múltiplo de 256.
Si no se indica la alineación ésta será PARA.
Combinación: Define cómo combinar segmentos que tengan el mismo nombre. Hay cinco posibles:
1.PUBLIC: Concatena todos los segmentos que tienen el mismo nombre para formar un sólo segmento. Todas las direcciones de datos e instrucciones se representan la distancia entre el inicio del segmento y la dirección correspondiente. La longitud del segmento formado será la suma de las longitudes de los segmentos con el mismo nombre.
2.STACK: Es similar a PUBLIC. La diferencia consiste que, al comenzar la ejecución del programa, el registro SS apuntará a este segmento y SP se inicializará con la longitud en bytes de este segmento.
3.COMMON: Pone el inicio de todos los segmentos teniendo el mismo nombre en la misma dirección de memoria. La longitud del segmento será la del segmento más largo.
4.MEMORY: Es igual a PUBLIC.
5.AT dirección_de_segmento: Hace que todas las etiquetas y direcciones de variables tengan el segmento especificado por la expresión contenida en dirección_de_segmento.
Este segmento no puede contener código o datos con valores iniciales. Todos los símbolos que forman la expresión dirección_de_segmento deben conocerse en el primer paso de ensamblado. Si no se indica combinación, el segmento no se combinará con otros del mismo nombre (combinación "privada").
Clase: Es una forma de asociar segmentos con diferentes nombres, pero con propósitos similares. Sirve también para identificar el segmento de código. Debe estar encerrado entre comillas simples. El linker pone los segmentos que tengan la misma clase uno a continuación de otro, si bien siguen siendo segmentos diferentes. Además supone que los segmentos de código tiene clase CODE o un nombre con el sufijo CODE.
DOSSEG: Esta directiva especifica que los segmentos deben ordenarse según la convención de DOS. Esta es la convención usada por los compiladores de lenguajes de alto nivel.
GROUP: Sirve para definir grupos de segmentos. Un grupo es una colección de segmentos asociados con la misma dirección inicial. De esta manera, aunque los datos estén en diferentes segmentos, todos pueden accederse mediante el mismo registro de segmento. Los segmentos de un grupo no necesitan ser contiguos.
Sintaxis: nombre_grupo GROUP segmento [, segmento...]
ASSUME: Sirve para indicar al ensamblador qué registro de segmento corresponde con un segmento determinado. Cuando el ensamblador necesita referenciar una dirección debe saber en qué registro de segmento lo apunta.
Sintaxis: ASSUME reg_segm:nombre [, reg_segm:nombre...]
donde el nombre puede ser de segmento o de grupo, una expresión utilizando el operador SEG o la palabra NOTHING, que cancela la selección de registro de segmento hecha con un ASSUME anterior.
Control del ensamblador
ORG expresión: El offset del código o datos a continuación será la indicada por la expresión. Todos los símbolos que forman la expresión deben conocerse en el primer paso de ensamblado.
EVEN: Hace que la próxima instrucción o dato se ensamble en la siguiente posición par.
END [etiqueta]: Debe ser la última sentencia del código fuente. La etiqueta indica dónde debe comenzar la ejecución del programa. Si el programa se compone de varios módulos, sólo el módulo que contiene la dirección de arranque del programa debe contener la directiva END etiqueta. Los demás módulos deberán terminar con la directiva END (sin etiqueta).
Definición de procedimientos
Los procedimientos son secciones de código que se pueden llamar para su ejecución desde distintas partes del programa.
etiqueta PROC {NEAR|FAR}
sentencias
etiqueta
ENDP
Ensamblado condicional
Verifican una condición determinada y si se cumple, ensambla una porción de código. Opcionalmente puede ensamblarse otra porción de código si la condición no se cumple. Son los siguientes: IF, IF1, IF2, IFB, IFDEF, IFDIF, IFE, IFIDN, IFNB, IFNDEF, ENDIF, ELSE.
{IF|IFE}
condición
sentencias
;Se ejecutan si es cierta (IF) o falsa (IFE).
[ELSE sentencias]
;Se ejecutan si es falsa (IF) o cierta (IFE).
ENDIF
La directiva ELSE y sus sentencias son opcionales. ENDIF termina el bloque y es obligatorio. Se pueden anidar directivas condicionales.
IF1 permite el ensamblado de las sentencias sólo en el primer paso, mientras que IF2 lo permite en el segundo paso.
IFDEF nombre permite el ensamblado de las sentencias si el nombre está definido, mientras que IFNDEF nombre lo permite si no está definido.
IFB <argumento permite el ensamblado si el argumento en una macro es blanco (no se pasó el argumento).
IFNB <argumento permite el ensamblado si el argumento en una macro no es blanco (se pasó el argumento).
IFIDN <argumento1, <argumento2 permite el ensamblado si los dos parámetros pasados a la macro son idénticos.
IFDIF <argumento1,<argumento2 permite el ensamblado si los dos parámetros pasados a la macro son diferentes.
Macros: Las macros asignan un nombre simbólico a un bloque de sentencias fuente. Luego se puede usar dicho nombre para representar esas sentencias. Opcionalmente se pueden definir parámetros para representar argumentos para la macro.
Definición de macros
nombre_macro
MACRO [parámetro [,parámetro...]]
[LOCAL nombre_local[,nombre_local...]
sentencias
ENDM
Los parámetros son opcionales. Si existen, entonces también aparecerán en algunas de las sentencias en la definición de la macro.
Al invocar la macro mediante:
nombre_macro [argumento [,argumento..]]
se ensamblarán las sentencias indicadas en la macro teniendo en cuenta que cada lugar donde aparezca un parámetro se reemplazará por el argumento correspondiente.
El nombre_local de la directiva LOCAL es un nombre simbólico temporario que será reemplazado por un único nombre simbólico (de la forma ??número) cuando la macro se invoque.
Todas las etiquetas dentro de la macro deberán estar indicadas en la directiva LOCAL para que el ensamblador no genere un error indicando que un símbolo está definido varias veces.
La directiva EXITM (usada dentro de la definición de la macro) sirve
para que no se ensamblen más sentencias de la macro (se usa dentro de bloques condicionales).
PURGE nombre_macro [,nombre_macro...]: Borra las macros indicadas de la memoria para poder utilizar este espacio para otros símbolos.
Definición de bloques de repetición
Son tres: REPT, IRP e IRPC. Como en el caso de la directiva MACRO, se puede incluir la sentencias LOCAL y EXITM y deben terminarse con la directiva ENDM.
REPT expresión
sentencias
ENDM
La expresión debe poder ser evaluada en el primer paso del ensamblado y el resultado deberá estar entre 0 y 65535. Esta expresión indica la cantidad de veces que debe repetirse el bloque.
IRP parámetro, <argumento [,argumento...]
sentencias
ENDM
El parámetro se reemplaza por el primer argumento y se ensamblan las sentencias dentro del bloque. Luego el parámetro se reemplaza por el segundo argumento y se ensamblan las sentencias y así sucesivamente hasta agotar los argumentos.
IRPC parámetro, cadena
sentencias
ENDM
Es similar a IRP con la diferencia que el parámetro se reemplaza por cada carácter de la cadena. Si ésta contiene comas, espacios u otros caracteres especiales deberá encerrarse con paréntesis angulares (<).
Procesador: Indican el tipo de procesador y coprocesador en el que se va a ejecutar el programa. Los de procesador son: .8086, .186, .286, .386, .486 y .586 para instrucciones en modo real, .286P, .386P, .486P y .586P para instrucciones privilegiadas, .8087, .287 y .387 para coprocesadores. Deben ubicarse al principio del código fuente. Habilitan las instrucciones correspondientes al procesador y coprocesador indicado. Sin estas directivas, sólo se pueden ensamblar instrucciones del 8086 y 8087.
Referencias externas al módulo
Sirve para poder particionar un programa en varios archivos fuentes o módulos. Son imprescindibles si se hace un programa en alto nivel con procedimientos en assembler. Hay tres: PUBLIC, EXTRN e INCLUDE.
PUBLIC nombre[, nombre...]: Estos nombres simbólicos se escriben en el archivo objeto. Durante una sesión con el linker, los símbolos en diferentes módulos pero con los mismos nombres tendrán la misma dirección.
EXTRN nombre:tipo [,nombre:tipo...]: Define una variable externa con el nombre y tipo (NEAR, FAR, BYTE, WORD, DWORD o ABS (número constante especificado con la directiva EQU o =)) especificado. El tipo debe ser el mismo que el del ítem indicado con la directiva PUBLIC en otro módulo.
INCLUDE nombre_de_archivo: Ensambla las sentencias indicadas en dicho archivo.
Segmentos simplificados
Permite definir los segmentos sin necesidad de utilizar las directivas de segmentos que aparecen más arriba.
.MODEL modelo: Debe estar ubicada antes de otra directiva de segmento. El modelo puede ser uno de los siguientes:
1.TINY: Los datos y el código juntos ocupan menos de 64 KB por lo que entran en el mismo segmento. Se utiliza para programas .COM. Algunos ensambladores no soportan este modelo.
2.SMALL: Los datos caben en un segmento de 64 KB y el código cabe en otro segmento de 64 KB. Por lo tanto todo el código y los datos se pueden acceder como NEAR.
3.MEDIUM: Los datos entran en un sólo segmento de 64 KB, pero el código puede ser mayor de 64 KB. Por lo tanto, código es FAR, mientras que los datos se acceden como NEAR.
4.COMPACT: Todo el código entra en un segmento de 64 KB, pero los datos no (pero no pueden haber matrices de más de 64 KB). Por lo tanto, código es NEAR, mientras que los datos se acceden como FAR.
5.LARGE: Tanto el código como los datos pueden ocupar más de 64 KB (pero no pueden haber matrices de más de 64 KB), por lo que ambos se acceden como FAR.
6.HUGE: Tanto el código como los datos pueden ocupar más de 64 KB (y las matrices también), por lo que ambos se acceden como FAR y los punteros a los elementos de las matrices también son FAR.
.STACK [size]: Define el segmento de pila de la longitud especificada.
.CODE [name]: Define el segmento de código.
.DATA: Define un segmento de datos NEAR con valores iniciales.
.DATA?: Define un segmento de datos NEAR sin valores iniciales.
.FARDATA [name]: Define un segmento de datos FAR con valores iniciales.
.FARDATA? [name]: Define un segmento de datos FAR sin valores iniciales.
.CONST: Define un segmento de datos constantes.
Los siguientes símbolos están definidos cuando se usan las directivas anteriores:
@curseg: Tiene el nombre del segmento que se está ensamblando.
@filename: Representa el nombre del archivo fuente (sin la extensión)
@codesize: Vale 0 para los modelos SMALL y COMPACT (código NEAR), y vale 1 para los modelos MEDIUM, LARGE y HUGE (código FAR).
@datasize: Vale 0 para los modelos SMALL y MEDIUM (datos NEAR), vale 1 para los modelos COMPACT y LARGE (datos FAR) y vale 2 para el modelo HUGE (punteros a matrices FAR).
@code: Nombre del segmento definido con la directiva .CODE.
@data: Nombre del segmento definido con la directivas .DATA, .DATA?, .CONST y .STACK (los cuatro están en el mismo segmento).
@fardata: Nombre del segmento definido con la directiva .FARDATA.
@fardata?: Nombre del segmento definido con la directiva .FARDATA?
Aqui se ejemplifica un programa que escribe una cadena en pantalla:
.MODEL SMALL
.CODE
Programa:
MOV AX, @DATA
MOV DS, AX
MOV DX, Offset Texto
MOV AH, 9
INT 21H
MOV AX,4C00H
INT 21H
.DATA
Texto DB 'Mensaje en pantalla.$'
.STACK
END Programa
Los primeros pasos son iguales a los del programa anterior: se define el modelo de memoria, se indica donde inicia el código del programa y en donde comienzan las instrucciones. A continuación se coloca @DATA en el registro AX para después pasarlo al registro DS ya que no se puede copiar directamente una constante a un registro de segmento. El contenido de @DATA es el número del segmento que sera utilizado para los datos. Luego se guarda en el registro DX un valor dado por "Offset Texto" que nos da la direccion donde se encuentra la cadena de caracteres en el segmento de datos. Luego utiliza la opción 9 (Dada por el valor de AH) de la interrupción 21H para desplegar la cadena posicionada en la dirección que contiene DX. Por último utiliza la opción 4CH de la interrupción 21H para terminar la ejecución del programa (aunque cargamos al registro AX el valor 4C00H la interrupcion 21H solo toma como opción el contenido del registro AH).
La directiva .DATA le indica al ensamblador que lo que esta escrito a continuacion debe almacenarlo en el segmento de memoria destinado a los datos. La directiva DB es utilizada para Definir Bytes, Esto es, asignar a cierto identificador (en este caso "Texto") un valor, ya sea una constante o una cadena de caracteres, en este œltimo caso debera estar entre comillas sencillas ' y terminar con el simbolo "$".
V. 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 en el siguiente cuadro:
y lo que se vería en pantalla sería lo siguiente:
C:\DATA\PROGRAMS\ASM>masm main
Microsoft (R) Macro Assembler Version 4.00
Copyright (C) Microsoft Corp 1981, 1983, 1984, 1985. All rights reserved.
Object filename [main.OBJ]:
Source listing [NUL.LST]:
Cross-reference [NUL.CRF]:
50966 Bytes symbol space free
0 Warning Errors
0 Severe Errors
C:\DATA\PROGRAMS\ASM>masm task
Microsoft (R) Macro Assembler Version 4.00
Copyright (C) Microsoft Corp 1981, 1983, 1984, 1985. All rights reserved.
Object filename [task.OBJ]:
Source listing [NUL.LST]:
Cross-reference [NUL.CRF]:
51034 Bytes symbol space free
0 Warning Errors
0 Severe Errors
C:\DATA\PROGRAMS\ASM>link main+task
Microsoft (R) 8086 Object Linker Version 3.05
Copyright (C) Microsoft Corp 1983, 1984, 1985. All rights reserved.
Run File [MAIN.EXE]:
List File [NUL.MAP]:
Libraries [.LIB]:
C:\DATA\PROGRAMS\ASM>main
Entrando a un submódulo....
.......saliendo del submódulo.
C:\DATA\PROGRAMS\ASM>
VI. DEPURACION.
Para la depuración de un programa en ensamblador tenemos disponibles dos herramientas. Por un lado tenemos el debuger que nos proporciona MS-DOS (DEBUG.EXE) y por otro lado tenemos el que nos proporciona Microsoft (SYMDEB.EXE). Este último trabaja igual que el de MS-DOS pero nos proporciona muchas ventajas más. Una de ellas es la facilidad de desplegar el código fuente correspondiente a la instrucción que se esta ejecutando (si el programa ejecutable fue ensamblado o compilado con un ensamblador o compilador compatible), nos permite ejecutar comandos del Sistema Operativo y nos permite obtener información de las interrupciones de manera simbólica.
VII. LA UTILERIA EXE2BIN Y LOS ARCHIVOS .EXE Y .COM.
Para MS-DOS sólo existen dos tipo de archivos ejecutables los .COM y .EXE. Ambos archivos difieren en los puntos siguientes:
Primero, las ventajas de los .EXE son dobles, nos permiten tener archivos reubicables y el uso de hasta cuatro segmentos (STACK, DATA, EXTRA y CODE) de hasta 64KB cada uno. Un archivo .COM sólo puede tener un segmento de 64KB, en el que se tiene tanto código como pila, y datos.
La desventaja de los .EXE es que agregan 512 bytes como cabecera con información para la reubicación del código. Un .COM no es reubicable, siempre inicia en la dirección 0100H.
Si nuestro programa no es muy grande 64KB son mas que suficientes. Por lo que conviene crearlo como .COM, para esto se cuenta con la utilería EXE2BIN.EXE que nos proporciona el sistema operativo y que nos permite crear .COM a partir de .EXE. Las restricciones para esto son las siguientes:
El archivo a convertir no debe estar empacado.
No debe tener segmento de stack.
Debe tener sólo segmento de código.
Su tamaño debe ser menor a 64KB.
VIII. EL LENGUAJE DE MAQUINA
Lo que se conoce como lenguaje o código de máquina es el conjunto de códigos numéricos (comúnmente expresados en hexadecimal) para cada una de las instrucciones que el microprocesador es capaz de reconocer y ejecutar. Aun cuando finalmente es con esta serie de números con los que el microprocesador trabaja, es muy difícil prrogramar a este nivel.
El lenguaje ensamblador fue desarrollado para liberar al programador de la dificil tarea de tener que recordar todos los códigos y de las tediosas labores de cálculo de localidades de memoria. Los códigos nemónicos del ensamblador son una sustitución de los códigos numéricos mejor que en una relación 1:1. Por ejemplo, resulta que mientras tenemos de manera general un nemónico para cargar un registro con un valor (MOV) internamente, para cada una de las modalidades de direccionamiento o registro afectado existe un código numérico.
La programación del computador en lenguaje de máquina, tanto la introducción de los programas, su ejecución y su depuración es posible realizarla usando alguna utilería provista por el sistema operativo y cabe mencionar que casi todos los sistemas operativos proveen al usuario con estas facilidades. DOS no es la excepción; éste provee al usuario de la utilería DEBUG.
IX. CREACION Y DEPURACION DE PROGRAMAS CON LA UTILERIA DEBUG
La utilería de MS-DOS DEBUG es una herramienta muy poderosa que es desconocida por muchos. Este provee una interfaz desde la cual es seguro no sólo explorar el contenido de la memoria del computador y de los registros del microprocesador sino que además provee los medios para la introducción de programas y su depuración, tanto en ensamblador como en lenguaje de máquina. La utilización de esta herramienta se ilustra con el siguiente ejemplo.
1.- Desde el prompt del sistema invoque la utilería.
C:\> debug
2.- Introducir, a través del prompt de la utilería, el siguiente programa en código de máquina.
-e 100 BA 40 00 8E DA BB 72 00 C7 07 34 12 EA 00 00 FF FF
3.- Guardar el programa en un archivo.
-rcx
:11
-n reset.com
-w
-q
4.- El paso anterior creará un archivo en el directorio vigente con el nombre RESET.COM. La extensión .COM es importante, el nombre pudo ser cualquiera. Este programa llama a las rutinas de inicialización del sistema, al ejecutarlo veremos que el computador se reinicializa, o si estamos trabajando en una ventana de DOS bajo Windows, ésta se cerrará.
C:\> reset.com
X. PROGRAMA EJEMPLO
Elaboración de un programa en lenguaje ensamblador que despliega en la pantalla de la PC un reloj en tiempo real y un cronómetro cuyo valor de carga inicial será proporcionado por el usuario, dicho valor será decrementado cada segundo y el programa finalizará en el momento en que la cuenta llegue a cero.
|
.MODEL SMALL ;Determina el estado y número de segmentos del programa
.DATA ;Indica al compilador donde empiezan las variables
;------------------------------------------------------------------------------
;Iniciación de Variables
PRESENT DB 'MACRO ASSEMBLER$'
PRESENT2 DB 'PRACTICA FINAL$'
PRESENT3 DB 'CLAUDIA MARCHENA.....980858$'
PRESENT5 DB '19 DE ABRIL DEL 2001$'
PRESENT6 DB 'PROF. JUAN GOMEZ$'
INGRESE1 DB 'HORA ACTUAL$'
INGRESE2 DB 'INGRESE LOS MIN. A CRONOMETRAR :$'
INGRESE3 DB 'INGRESE LOS SEG. A CRONOMETRAR :$'
RETURN DB 13,10,'$'
INTER_ANT DD ?
INTER_ACT DD ?
MASCARA_RELOJ DB '00:00:00$'
MASCARA_CRONO DB '00:00$'
POS_RELOJ DW ?
POS_CRONO DW ?
MINUTOS DB ?
SEGUNDOS DB ?
;------------------------------------------------------------------------------
;Programa Principal
.CODE ;El sistema operativo lo asigna
INICIO_PROG: MOV CX, @DATA ;Guarda en Cx la dir. de segmento donde empeze las variables
MOV DS, CX ;DS es el apuntador del segmento de datos
MOV BP, 0 ;Inicializa BP para ser usado en las interrupciones
CALL LIMPIA ;Llama a la subrutina de limpiar la pantalla
;guarda el IP (si es lejana tambien el CS)
MOV DH, 3 ;En DH coloco un 3 (Renglón 3)
MOV DL, 0 ;En DL coloco un 0 (Columna 0)
CALL POS_CURSOR ;Subrutina:posiciona el cursor en el lugar indicado
LEA SI, PRESENT ;Presentación del programa 1
CALL CENTRA ;Subrutina:centrado en el renglón en que se encuentra
;el cursor
LEA SI,RETURN ;Enter
CALL IMP_TEXTO ;Se imprime el texto
LEA SI, PRESENT2 ;Presentación del programa 2
CALL CENTRA ;Subrutina:centrado en el renglón en que se encuentra
;el cursor
LEA SI, PRESENT2 ;Presentación del programa 2
CALL CENTRA ;Subrutina:centrado en el renglón en que se
LEA SI, PRESENT3 ;Presentación del programa 3
CALL CENTRA ;Subrutina:centrado en el renglón en que se encuentra
;el cursor
LEA SI,RETURN ;Enter
CALL IMP_TEXTO ;Se imprime el texto
LEA SI, PRESENT4 ;Presentación del programa 4
CALL CENTRA ;Subrutina:centrado en el renglón en que se encuentra
;el cursor
LEA SI,RETURN ;Enter
CALL IMP_TEXTO ;Se imprime el texto
LEA SI, PRESENT5 ;Presentación del programa 5
CALL CENTRA ;Subrutina:centrado en el renglón en que se encuentra
;el cursor
LEA SI,RETURN ;Enter
CALL IMP_TEXTO ;Se imprime el texto
LEA SI, PRESENT6 ;Presentación del programa 6
CALL CENTRA ;Subrutina:centrado en el renglón en que se encuentra
;el cursor
LEA SI,RETURN ;Enter
CALL IMP_TEXTO ;Se imprime el texto
LEA SI,RETURN ;Enter
CALL IMP_TEXTO ;Se imprime el texto
LEA SI,RETURN ;Enter
CALL IMP_TEXTO ;Se imprime el texto
LEA SI,RETURN ;Enter
CALL IMP_TEXTO ;Se imprime el texto
LEA SI,INGRESE2 ;Título 2
CALL IMP_TEXTO
LEA SI,MINUTOS
CALL CRON ;Despliega el cronómetro
LEA SI,RETURN ;Enter
CALL IMP_TEXTO ;Se imprime el texto
LEA SI,INGRESE3 ;Título 3
CALL IMP_TEXTO
LEA SI,SEGUNDOS
CALL CRON ;Despliega el cronómetro
LEA SI,RETURN ;Enter
CALL IMP_TEXTO ;Se imprime el texto
LEA SI, MASCARA_RELOJ;Presentación del reloj
CALL CENTRA ;Subrutina:centrado en el renglón en que se encuentra
MOV POS_RELOJ,DX ;Guardo en POS_RELOJ, DX (centro)
CALL RELOJ ;Subrutina:Calculo del reloj e impresión
SUB DL,15
CALL POS_CURSOR
LEA SI,INGRESE1 ;Título 1
CALL IMP_TEXTO
LEA SI,RETURN ;Enter
CALL IMP_TEXTO ;Se imprime el texto
LEA SI, MASCARA_CRONO;Presentación del reloj
CALL CENTRA ;Subrutina:centrado en el renglón en que se encuentra
MOV POS_CRONO,DX ;Guardo en POS_RELOJ, DX (centro)
MOV AL, 1CH ;1C es el codigo de int del timer
LEA SI, INTER_ANT ;En INTER_ANT guardo la dirección original de
;atención de interrupciones que es 1C
CALL OB_INTER ;Subrutina:Obtener interrupciones
MOV AL, 1CH ;En AL guarda la interrupción 1CH
LEA DX, TIMER ;DX apunta al TIMER
LEA SI, INTER_ACT ;SI apunta a INTER_ACT
MOV [SI],DX ;En la dirección de SI se guarda el desplazamiento
;apartir el segmento de código(CS) de la rutina de
;atención al timer
MOV [SI+2],CS ;En la dirección de SI+2, se guarda el desplazamiento
;del segmento de código (CS)
CALL MOD_INTER ;Subrutina:Modificar Interrupciones
WHILE: CALL CLOCK
CMP SEGUNDOS,0
JNE WHILE
CMP MINUTOS,0
JNE WHILE
MOV AL, 1CH ;1C es el codigo de int del timer
LEA SI, INTER_ANT ;En INTER_ANT guardo la dirección original de
;atención de interrupciones (1C)
CALL MOD_INTER ;Subrutina:Modificar interrupciones
LEA SI,RETURN ;Enter
CALL IMP_TEXTO ;Se imprime el texto
MOV AH, 4CH ;Interrumpe el programa y devuelve control a SO
INT 21H ;Regreso al SO (Interrupción de SO)
;------------------------------------------------------------------------------
;Limpia la pantalla
LIMPIA PROC NEAR ;Proc(Define Subrutina), Near(Mismo Segmento)
PUSH AX ;Guarda todos los registros
PUSH BX ;
PUSH CX ;
PUSH DX ;
MOV AX, 0600H ;06-->rutina de recorrer pantalla,00-->pantalla completa
MOV BH, 07 ;0-->Fondo negro, 7-->Letras Blancas
MOV CX, 0000H ;00(renglón)00(columna)-->Esq.Sup.Izq.
MOV DX, 184FH ;18H(renglón 25)4FH(columna 80)-->Esq.inf.izq.
INT 10H ;Bios toma el control con estos parámetros
POP DX ;Restablece todos los regitros
POP CX ;
POP BX ;
POP AX ;
RET ;Regreso de Subrutina
LIMPIA ENDP ;Aviso al compilador que la subrutina ya acabó
;------------------------------------------------------------------------------
;Fija la posición del cursor (Obtiene DH y DL)
POS_CURSOR PROC NEAR ;Proc(Define Subrutina), Near(Mismo Segmento)
PUSH AX ;Guarda los registros
PUSH BX ;
MOV AH, 02H ;Coloca el cursor por medio de Bios
MOV BH, 00H ;Coloca el cursor en la página cero
INT 10H ;Bios toma el control con estos parámetros
POP BX ;Restablece todos los registros
POP AX ;
RET ;Regreso de Subrutina
POS_CURSOR ENDP ;Aviso al compilador que la subrutina ya acabó
;------------------------------------------------------------------------------
;Obtención de la posición del cursor (Devuelve DH y DL)
OBT_CURSOR PROC NEAR ;Proc(Define Subrutina), Near(Mismo Segmento)
PUSH AX ;Guarda los registros
PUSH BX ;
MOV AH, 03H ;Obtiene el cursor por medio de Bios
MOV BH, 00H ;Obtiene el cursor de la página cero
INT 10H ;Bios toma el control con estos parámetros, y guarda a
;a las coordenadas en DX
POP BX ;Restablece todos los registros
POP AX ;
RET ;Regreso de Subrutina
OBT_CURSOR ENDP ;Aviso al compilador que la subrutina ya acabó
;------------------------------------------------------------------------------
;Imprimir texto (SI-->dir de la cadena a imprimir)
IMP_TEXTO PROC NEAR ;Proc(Define Subrutina), Near(Mismo Segmento)
PUSH AX ;Guarda los registros
PUSH DX ;
MOV DX, SI ;Guarda la dir de la cadena en DX
MOV AH, 09H ;Coloca el cursor por medio de Bios
INT 21H ;SO toma el control con estos parámetros
POP DX ;Restablece todos los registros
POP AX ;
RET ;Regreso de Subrutina
IMP_TEXTO ENDP ;Aviso al compilador que la subrutina ya acabó
;------------------------------------------------------------------------------
;Centra el texto escrito (SI-->dir de la cadena a centrar, DX-->Posicón de texto centrado)
CENTRA PROC NEAR ;Proc(Define Subrutina), Near(Mismo Segmento)
PUSH AX ;Guarda todos los registros
PUSH DX ;
PUSH SI ;
DEC SI ;Decrementar SI(Así no se pierde el 1° valor de la cadena
MOV AX, -1 ;Guarda en AX el valor de -1
REGRESA: INC AX ;Incrementa AX (registro posicion)
INC SI ;Incrementa SI
CMP BYTE PTR [SI],'$';Compara SI (16 bits) con el final de la cadena ($)
;Se pone BYTE para especificar que SI va a ser de 8
;bits al igual que $
JNE REGRESA ;Si no son iguales salta a REGRESA, ya que no hemos
;llegado al final de la cadena
MOV BX, 80 ;Carga en el registro BX 80 (Máximo de caracteres)
SUB BX, AX ;Resta BX(80 caracteres)-AX(No.de caracteres de mi cadena)
SHR BX, 1 ;Hace un Shift a la derecha para dividirlo entre 2
;y así distribuir la cadena en el centro de la pantalla
CALL OBT_CURSOR ;Llama a la subrutina OBT_CURSOR
MOV DL, BL ;Carga la parte baja de B a la parte baja de D
CALL POS_CURSOR ;Llama a la subrutina POS_CURSOR
POP SI ;Restaura SI
CALL IMP_TEXTO ;Llama a la subrutina de IMP_Texto
POP BX ;Restaura los registros restantes
POP AX ;
RET ;Regreso de subrutina
CENTRA ENDP ;Aviso al compilador que la subrutina ya acabó
;------------------------------------------------------------------------------
;Obtiene la interrupción (SI-apuntador-, AL--> CODIGO DE LA INT)
OB_INTER PROC NEAR ;Proc(Define Subrutina), Near(Mismo Segmento)
PUSH ES ;Guarda todos los registros
PUSH AX ;
PUSH BX ;
MOV AH, 35H ;Manda llamar el servicio de obtencion del vector
;con código de interupción AL
INT 21H ;Manda llamar a la Interrupción 21H
MOV WORD PTR [SI],BX;Guardo lo que tiene Bx(desplazamiento a partir del
;segmento de código de la rutina de interrupción
;solicitada) en SI
MOV WORD PTR [SI+2],ES;Guardo lo que tiene ES(segmento a partir de donde
;está el código de la rutina de interrupción
;solicitada) en SI+2
POP BX ;Restaura todos los registros
POP AX ;
POP ES ;
RET ;Regreso de subrutina
OB_INTER ENDP ;Aviso al compilador que la subrutina ya acabó
;------------------------------------------------------------------------------
;Modifica la interrupción
MOD_INTER PROC NEAR ;Proc(Define Subrutina), Near(Mismo Segmento)
PUSH DS ;Guarda todos los registros
PUSH AX ;
PUSH BX ;
MOV DX,WORD PTR [SI];Lo que haya en SI (16 bits), lo guarda en el registro
;DX
MOV DS,WORD PTR [SI+2];Lo que haya en SI+2 (16 bits), lo guarda en el
;registro DX
MOV AH, 25H ;Manda llamar el servicio de modificación del vector
;con el código de interupción AH
INT 21H ;Llama a la interrupción 21H
POP BX ;Restaura todos los registros
POP AX ;
POP DS ;
RET ;Regreso de subrutina
MOD_INTER ENDP ;Aviso al compilador que la subrutina ya acabó
;------------------------------------------------------------------------------
;Cuenta incrementando 17 veces para completar un segundo
TIMER PROC NEAR ;Proc(Define Subrutina), Near(Mismo Segmento)
INC BP ;Incrementa el apuntador BP
IRET ;Regreso de subrutina y hace un POP a las banderas
TIMER ENDP ;Aviso al compilador que la subrutina ya acabó
;------------------------------------------------------------------------------
;Calcula e imprime el reloj del sistema
RELOJ PROC NEAR ;Proc(Define Subrutina), Near(Mismo Segmento)
PUSH AX ;Guardar todos los registros
PUSH BX ;
PUSH CX ;
PUSH DX ;
MOV DX,POS_RELOJ ;Guarda en DX la posición de SI (las coordenadas del reloj)
CALL POS_CURSOR ;Posiciona el cursor a donde imprimo el reloj
MOV AH,2CH ;Pide la hora al sistema (2CH)
INT 21H ;Llama a interrupción 21H
MOV AH,CH ;Guardo las horas en el registro AH
CALL CONV ;Convierto las horas y las imprimo
MOV DL,':' ;
MOV AH,06H ;Imprime separación de las horas
INT 21H ;Llama a la interrupción 21H
MOV AH,CL ;Guardo los minutos en el registro AH
CALL CONV ;Convierto los minutos y las imprimo
MOV DL,':' ;
MOV AH,06H ;Imprime separación de los minutos
INT 21H ;Llama a la interrupción 21H
MOV AH,DH ;Guardo los segundos en el registro AH
CALL CONV ;Convierto los segundos y las imprimo
POP DX ;Restaura todos los registros
POP CX ;
POP BX ;
POP AX ;
RET ;Regreso de subrutina
RELOJ ENDP ;Aviso al compilador que la subrutina ya acabó
;------------------------------------------------------------------------------
;Convierte de Binario a Ascii (Usamos el registro AH que contiene el dato a convertir)
CONV PROC NEAR ;Proc(Define Subrutina), Near(Mismo Segmento)
PUSH AX ;Guardar todos los registros
PUSH DX ;
MOV AL,'0' ;Pone el código Ascii en AL
INI_FOR: CMP AH,10 ;Compara con 10, para que en AH solo tenga unidades
JL FIN_FOR ;Si es menor que 10 salta al fin del for, sino es que
;todavía hay decenas
INC AL ;Incremeta AL (decenas en una unidad)
SUB AH,10 ;Le resto 10 al registro AH
JMP INI_FOR ;Salta a volver a comparar con 10
FIN_FOR: ADD AH,30H ;Suma lo que hay en AH+30H para convertir las unidades
;en Ascii
PUSH AX ;Guarda el regsitro AX
MOV AH,06H ;Guarda en el registro AH el servicio 06H (Impresión
;de un caracter)
MOV DL,AL ;Indica que caracter que va a imprimir (decenas)
INT 21H ;Llama a interrupción 21H
POP AX ;Restaura el registro AX
MOV DL,AH ;Indica el caracter que va a imprimir (unidades)
MOV AH,06H ;Guarda en el registro AH el servicio 06H (impresión)
INT 21H ;Llama a interrupción 21H
POP DX ;Restaura los registros
POP AX ;
RET ;Regreso de subrutina
CONV ENDP ;Aviso al compilador que la subrutina ya acabó
;------------------------------------------------------------------------------
CRON PROC NEAR ;Proc(Define Subrutina), Near(Mismo Segmento)
PUSH AX ;Guarda todos los registros
PUSH DX ;
MOV AH,06H ;Carga AH con el servicio de pedir un dato (06H)
MOV DL,0FFH ;Como en DL guardamos el valor de 0FFH, entonces pide
ESPERAD: INT 21H ;Llama a interrupción 21H
JZ ESPERAD ;Salta a Esperad si la bandera de cero está prendida,
;es decir que no ha llegadoun dato
CMP AL,'0' ;Compara el registro AL con cero
JB ESPERAD ;Si es menor que cero salta a ESPERAD
CMP AL,'9' ;Compara el registro AL con 9
JA ESPERAD ;Si es mayor que nueve salta a ESPERAD
MOV DL,AL ;Guarda en DL el dato de AL
INT 21H ;Llama a interrupción 21H
SUB AL,30H ;Resta el dato de Al- 30H
MOV DL,10 ;Guarda en DL, 10
MUL DL ;Multiplica AL*DL y lo guarda en AX
MOV BYTE PTR [SI],AL;Guarda en la dirección de SI el registro Al
MOV AH,06H ;Carga AH con el servicio de pedir un dato (06H)
MOV DL,0FFH ;Como en DL guardamos el valor de 0FFH, entonces pide
ESPERAU: INT 21H ;Llama a interrupción 21H
JZ ESPERAU ;Salta a ESPERAU si la bandera de cero está prendida,
;es decir que no ha llegado un dato
CMP AL,'0' ;Compara el registro Al con cero
JB ESPERAU ;Si es menor que cero salta a ESPERAU
CMP AL,'9' ;Compara el registro AL con 9
JA ESPERAU ;Si es mayor que nueve salta a ESPERAU
MOV DL,AL ;Guarda en DL el dato de AL
INT 21H ;Llama a interrupción 21H
SUB AL,30H ;Resta el dato de Al- 30H
ADD AL,[SI] ;Suma el registro Al con lo que haya en la dirección
;de SI
MOV [SI],AL ;Guarda el registro AL en la dirección de SI
POP DX ;
POP AX ;
RET ;Regreso de subrutina
CRON ENDP ;Aviso al compilador que la subrutina ya acabó
;------------------------------------------------------------------------------
CLOCK PROC NEAR ;Proc(Define Subrutina), Near(Mismo Segmento)
CMP BP,17 ;
JNE FIN ;
MOV BP,0 ;
CALL RELOJ ;
CMP SEGUNDOS,0 ;
JE DEC_MIN ;
DEC BYTE PTR SEGUNDOS;
CALL IMP_CRONO ;
JMP FIN ;
DEC_MIN: MOV SEGUNDOS,59 ;
CMP MINUTOS,0 ;
JE RESET ;
DEC BYTE PTR MINUTOS;
CALL IMP_CRONO ;
JMP FIN ;
RESET: MOV SEGUNDOS,0 ;
FIN: RET ;Regreso de subrutina
CLOCK ENDP ;Aviso al compilador que la subrutina ya acabó
;------------------------------------------------------------------------------
IMP_CRONO PROC NEAR ;Proc(Define Subrutina), Near(Mismo Segmento)
PUSH AX ;
PUSH DX ;
MOV DX,POS_CRONO ;Guarda en DX la posición de SI (las coordenadas del reloj)
CALL POS_CURSOR ;Posiciona el cursor a donde imprimo el reloj
MOV AH,MINUTOS ;
CALL CONV ;
CALL OBT_CURSOR ;
INC DL ;
CALL POS_CURSOR ;
MOV AH,SEGUNDOS ;
CALL CONV ;
POP DX ;
POP AX ;
RET ;Regreso de subrutina
IMP_CRONO ENDP ;Aviso al compilador que la subrutina ya acabó
;------------------------------------------------------------------------------
END INICIO_PROG
38
Descargar
Enviado por: | Claudia Marchena |
Idioma: | castellano |
País: | República Dominicana |