Introducción al Macro Assembler

Ensamblador. Programa Fuente. Assembly. Assembler. Código Máquina. Intérpretes. Programación. Depuración. Debug. Debugger

  • Enviado por: Claudia Marchena
  • Idioma: castellano
  • País: República Dominicana República Dominicana
  • 39 páginas
publicidad
publicidad

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

     

    Introducción al Macro Assembler

    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:

    Introducción al Macro Assembler

    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