Temario lenguaje ensamblador

109
Centro Escolar “Felipe Carrillo Puerto” Licenciatura en Ciencias Computacionales MATERIA: LENGUAJE ENSAMBLADOR NIVEL: SEXTO SEMESTRE PROFESOR: L. C. C. MIGUEL ANGEL SUASTE ESCALANTE SEMESTRE: FEBRERO-JULIO DEL 2008 HORARIO: Martes y Jueves de 8:00-9:30 Objetivo General: Al finalizar el curso el alumno podrá aplicar los conceptos y características sobre la arquitectura de una computadora para elaborar y depurar programas escritos en lenguaje ensamblador. Criterios de Evaluación: 1er. Bimestre 2º. Bimestre Ordinario Criterio de Evaluación Punto s Criterio de Evaluación Punto s Criterio de Evaluación Puntos Examen escrito 15 Examen escrito 15 Examen escrito 15 Participación 5 Participación 5 Participación NA Trabajos y/o proyectos 5 Trabajos y/o proyectos 5 Trabajos y/o proyectos 15 Tareas o ensayos 10 Tareas o ensayos 10 Tareas o ensayos NA Exposiciones de trabajo NA Exposiciones de trabajo NA Exposiciones de trabajo NA Otros NA Otros NA Otros NA Total 35 Total 35 Total 30 Fechas de Evaluaciones Primer Parcial: Segundo Parcial: CONTENIDO 1. FUNDAMENTOS DE LA PROGRAMACIÓN EN ENSAMBLADOR El alumno comprenderá y aplicará los conceptos relacionados con la estructura de las computadoras y el Lenguaje Ensamblador. 1.1. La familia de computadoras IBM 1.2. Macroensamblador 1.3. Ventajas que se obtienen al aprender un Lenguaje Ensamblador 1.4. Sistemas Numéricos 1.4.1. Numeración binaria 1.4.2. Bits, nibbles, Bytes y Words 1.4.3. Representación de enteros 1.4.3.1. Magnitud con signo 1.4.3.2. Complemento a uno 1.4.3.3. Complemento a dos

description

temario del lenguaje ensamblador

Transcript of Temario lenguaje ensamblador

Page 1: Temario lenguaje ensamblador

Centro Escolar “Felipe Carrillo Puerto”

Licenciatura en Ciencias Computacionales

MATERIA: LENGUAJE ENSAMBLADORNIVEL: SEXTO SEMESTREPROFESOR: L. C. C. MIGUEL ANGEL SUASTE ESCALANTESEMESTRE: FEBRERO-JULIO DEL 2008HORARIO: Martes y Jueves de 8:00-9:30

Objetivo General: Al finalizar el curso el alumno podrá aplicar los conceptos y características sobre la arquitectura de una computadora para elaborar y depurar programas escritos en lenguaje ensamblador.

Criterios de Evaluación:1er. Bimestre 2º. Bimestre Ordinario

Criterio de Evaluación Puntos Criterio de Evaluación Puntos Criterio de Evaluación PuntosExamen escrito 15 Examen escrito 15 Examen escrito 15Participación 5 Participación 5 Participación NATrabajos y/o proyectos 5 Trabajos y/o proyectos 5 Trabajos y/o proyectos 15Tareas o ensayos 10 Tareas o ensayos 10 Tareas o ensayos NAExposiciones de trabajo NA Exposiciones de trabajo NA Exposiciones de trabajo NAOtros NA Otros NA Otros NA

Total 35 Total 35 Total 30

Fechas de EvaluacionesPrimer Parcial: Segundo Parcial:

CONTENIDO1. FUNDAMENTOS DE LA PROGRAMACIÓN EN ENSAMBLADOR

El alumno comprenderá y aplicará los conceptos relacionados con la estructura de las computadoras y el Lenguaje Ensamblador.

1.1.La familia de computadoras IBM1.2.Macroensamblador1.3.Ventajas que se obtienen al aprender un Lenguaje Ensamblador1.4.Sistemas Numéricos

1.4.1. Numeración binaria1.4.2. Bits, nibbles, Bytes y Words1.4.3. Representación de enteros

1.4.3.1. Magnitud con signo1.4.3.2. Complemento a uno1.4.3.3. Complemento a dos1.4.3.4. Exceso 2n-1

1.4.4. Representación de punto flotante1.4.4.1. Representación de números de punto flotante en la PDP-11 e IBM1.4.4.2. Bit Escondido

1.5.El Debug

2. INTRODUCCIÓN A LA PROGRAMACIÓN EN LENGUAJE ENSAMBLADOR

Page 2: Temario lenguaje ensamblador

Centro Escolar “Felipe Carrillo Puerto”

Licenciatura en Ciencias Computacionales

El alumno conocerá cómo representa la computadora de manera interna los datos e instrucciones que procesa mediante el debug.

2.1.El Debug y su uso2.2.Aritmética del 8088/80286/80386 mediante el DEBUG

2.2.1. Registros como variables2.2.2. Memoria del Procesador2.2.3. Estilos de adición y resta2.2.4. Números negativos2.2.5. Multiplicación y división

2.3.Imprimiendo caracteres2.3.1. INT 21h: El poder de las interrupciones2.3.2. INT 20h: una salida con gracia2.3.3. Programas enteros2.3.4. Moviendo datos entre registros2.3.5. Escribiendo strings de caracteres

2.4.Registro de Banderas2.5.Imprimiendo números binarios

2.5.1. Banderas de acarreo y rotación2.5.2. Adición con la bandera de acarreo2.5.3. Looping2.5.4. Escribiendo números binarios

2.6.Imprimiendo en hexadecimal2.6.1. Comparando y status de bits2.6.2. Imprimiendo un dígito en hexadecimal

2.7.Leyendo caracteres2.7.1. Leyendo un caracter2.7.2. Leyendo un número hexadecimal2.7.3. Leyendo dos dígitos hexadecimales

2.8.Procedimientos y pilas2.8.1. La pila y direcciones de retorno2.8.2. Leyendo números en hexadecimal2.8.3. Etiquetas

3. PROGRAMACIÓN EN LENGUAJE ENSAMBLADOREl alumno programará en Lenguaje Ensamblador para trabajar a bajo nivel en la

computadora y desarrollará diversas aplicaciones.3.1.Proceso de Ensamble3.2.Rutinas utilizadas en el Ensamblador3.3.Tipos de Instrucciones en Lenguaje Ensamblador3.4.Instrucciones en Macroensamblador3.5.Estructura de un programa3.6.Parámetros de Pseudo-Op Segment3.7.Programando con el MASM3.8.Modos de Direccionamiento3.9.Los Procedimientos y el Ensamblador3.10. Imprimiendo en Decimal3.11. Segmentos y Desplazamientos3.12. Mapa de la memoria RAM

Page 3: Temario lenguaje ensamblador

Centro Escolar “Felipe Carrillo Puerto”

Licenciatura en Ciencias Computacionales

3.13. Segmentos3.13.1. Seccionando la memoria del Microprocesador3.13.2. La Pila3.13.3. El priefijo del Segmento de Programa (PSP o Scratch área)3.13.4. La directiva DOSSEG3.13.5. Llamadas (NEAR y FAR)3.13.6. Vector de Interrupciones

3.14. Vaciando la memoria3.14.1. Instrucción group3.14.2. Rutinas varias

Page 4: Temario lenguaje ensamblador

Centro Escolar “Felipe Carrillo Puerto”

Licenciatura en Ciencias Computacionales

3.15. Diseño de software3.16. Instrucciones de control

3.16.1. Instrucciones de salto3.16.2. Instrucciones de comparación

3.17. Optimización del diseño3.17.1. Programación modular3.17.2. Diseño descendente

3.18. Diagramas de flujo y pseudocódigo3.19. Enfoque a la programación estructurada3.20. Estilo y forma3.21. Instrucciones de uso más frecuentes

3.21.1. Instrucciones aritméticas3.21.2. Instrucciones de transferencia3.21.3. Instrucciones de carga3.21.4. Instrucciones Loop3.21.5. Instrucciones de Stack3.21.6. Instrucciones de conteo3.21.7. Otras instrucciones3.21.8. Instrucciones de corrimiento3.21.9. Instrucciones de rotación3.21.10. Instrucciones de almacenamiento3.21.11. Instrucciones de manejo de cadenas3.21.12. Instrucciones de conversión3.21.13. Instrucciones de procedimiento y control3.21.14. instrucciones ASCII3.21.15. instrucciones de aritmética decimal3.21.16. Instrucciones de I/O3.21.17. Instrucciones diversas

3.22. Ejemplos y ejercicios de programación.

Bibliografía

1. Lenguaje ensamblador para Microcomputadores IBM para principiantes y avanzados; J. Terry Godfrey; Prentice Hall; 1991

2. System Programming; J. J. Donovan; Mc Graw-Hill3. Fundamental Concepto of Programming Systems; Jefrey D. Ullman;

Addisson-Wesley

Page 5: Temario lenguaje ensamblador

FUNDAMENTOS DE LA PROGRAMACIÓN EN ENSAMBLADOREl alumno comprenderá y aplicará los conceptos relacionados con la estructura de las

computadoras y el Lenguaje Ensamblador.

Introducción¿Qué es el Lenguaje Ensamblador?

Es un lenguaje de bajo nivel que traduce instrucciones en lenguaje máquina1. Este utiliza nemotécnicos (abreviaturas) que representan operaciones, nombres simbólicos, operadores y símbolos especiales.

Entonces, ¿Por que estudiar el Lenguaje Ensamblador si existen Lenguajes de Alto Nivel?La importancia del Lenguaje ensamblador es que trabaja directamente con el microprocesador.

Los programadores que emplean lenguajes de alto nivel para desarrollar aplicaciones donde el tiempo no es un factor crítico o que hacen uso de dispositivos estándar de entrada/salida (I/O), rara vez necesitan llamar rutinas que no formen parte de la librería del compilador. En otras palabras, las necesidades de programar en lenguaje ensamblador en este tipo de aplicaciones, son mínimas.

Sin embargo, alguien debe escribir las rutinas de librería que estos programadores emplean, con el fin de obtener la interfaz estándar. Estas rutinas forman la parte no transportable del lenguaje que utilizan y están escritas en lenguaje ensamblador.

La familia de computadoras IBMEn el presente curso se estudiará la programación en ensamblador basado en el ambiente del

sistema de la computadora personal IBM debido a que:1. Está bien definida la descripción de las interfaces del Hw. en el BIOS, tales como

interrupciones, rutinas de servicio, diagramas.2. Arquitectura del sistema claramente delineada.3. La presentación en pantalla es buena para el desarrollo de programas.4. Amplio soporte de Sw.5. El desarrollo de código es una tarea que se facilita, debido a:

a. Énfasis en el teclado y su sintaxis I/Ob. Líneas de 80 caracteresc. I/O orientado a textosd. Editores bien desarrolladose. Depuradores bien desarrolladosf. Enlazadores y ensambladores bien desarrolladosg. BIOS bien documentado.

6. Finalmente, el ambiente de IBM ofrece compatibilidad con otros modelos; por tanto, la experiencia y habilidad ganadas por el usuario aumentarán continuamente.

IBM desarrolló un conjunto de microcomputadoras basados en los microprocesadores 8088, 8086, 80286 y 80386 fabricados por INTEL, los tres primeros de 16 bits (es decir, tiene registros internos de 16 bits) y el último de 32 bits.

1 El Lenguaje de Máquina son aquellas instrucciones que son directamente entendidas por lo computadora y no necesitan traducción posterior para que la CPU pueda comprender y ejecutar el programa. Dichas instrucciones se expresan con bits.

1

Page 6: Temario lenguaje ensamblador

Los microprocesadores 8088 y 8086 se programan con un conjunto básico de instrucciones que son la base de la versión 1.0 de macroensamblador (que emplea un modelo de memoria pequeña). El 80286 se programa en uno de dos modos posibles: el modo de direcciones reales o el modo de direcciones virtuales (o modo protegido). Todo el código desarrollado para el 8088 y 8086 se ejecuta en el primero modo. De manera similar todo el código, salvo para unas instrucciones específicas desarrollado para el 80286 en el modo de direcciones reales será ejecutado sin ningún problema, tanto en el 8088 como en el 8086, La versión 2.0 del macroensamblador incluye también instrucciones específicas del 80286, 8087 y 80287.

En modo protegido el 80286 es muy diferente del 8088 y 80286. En este modo existen instrucciones orientadas a soportes de sistemas que no están disponibles en las versiones 1.0 y 2.0 del macroensamblador. Además, estas instrucciones no están diseñadas para que el programador de aplicaciones haga uso de ellas ya que proporcionan funciones para el manejo de memoria y multitarea. El 80386 tiene muchas instrucciones que son iguales al 80286 tanto en direcciones reales como en modo protegido. Además, el 80386 tiene un modo virtual para el 8086 que permite ejecutar los programas desarrollados para este microprocesador en el ambiente multitarea.

Registros del INTEL 8088/8086 y el 80286Registros Descripción

AX (AH, AL) Registro del acumuladorBX (BH, BL) Registro de baseCX (CH, CL) Registro para conteoDX (DH, DL) Registro de datos

SP Registro apuntador de pilaBP Registro apuntador de baseSI Registro de índice de fuenteDI Registro de índice de destinoCS Registro de segmento de códigoDS Registro de segmento de datosSS Registro de segmento de pilaES Registro de segmento extraIP Apuntador de instrucciones

FLAGS Registro de estado de banderas

El INTEL 80386/80486 tienen 14 registros de propósito general, un apuntador de instrucciones EIP y un registros de banderas EFLAGS.

2

Page 7: Temario lenguaje ensamblador

Registros del INTEL 80386/80486Registros Descripción

EAX (AH, AL) Registro del acumulador (32 bits)EBX (BH, BL) Registro de base (32 bits)ECX (CH, CL) Registro para conteo (32 bits)EDX (DH, DL) Registro de datos (32 bits)

ESP Registro apuntador de pila (32 bits)EBP Registro apuntador de base (32 bits)ESI Registro de índice de fuente (32 bits)EDI Registro de índice de destino (32 bits)ECS Registro de segmento de código (16 bits)EDS Registro de segmento de datos (16 bits)ESS Registro de segmento de pila (16 bits)EES Registro de segmento extra (16 bits)EIP Apuntador de instrucciones (16 bits)

EFLAGS Registro de estado de banderas (16 bits)

MacroensambladorEl macroensamblador es un programa de computadora que traduce programas escritos en

ensamblador en instrucciones en lenguaje máquina. Es un programa muy específico que guarda estrecha relación con la arquitectura del Hw específico de cada computadora. En la CPU’s de las computadoras personales se conectan circuitos electrónicos de memoria de propósito general para formar registros. Los registros son dispositivos (circuitos) de memoria muy sencillos ubicados dentro del microprocesador. El trabajo del macroensamblador es traducir las instrucciones en series de 1 y 0’s que causan que el contenido de los registros sean manejados de manera correcta.

Ejemplos de Ensamblador: MACRO (PDP-11) COMPASS (CYBER) ZILOGZ-80 (RADIO SHACK) MACROENSAMBLADOR (INTEL) IBM MACRO ASSEMBLER MICROSOFT MACROASSEMBLER, TURBO, EDITASAM ASSEMBLER

Ejemplo de instrucciones en Ensamblador:a) SUB AX, BX ;Se resta a AX el Valor de BXb) MOV AX, BAM[4]

Las ligas a continuación, muestra la forma en que se programaba en ensamblador usando una PDP-11: Parte 1 (http://www.youtube.com/watch?v=XV-7J5y1TQc&feature=related) Parte 2 (http://www.youtube.com/watch?v=7zaaD_xP6nU&feature=related) Parte 3 (http://www.youtube.com/watch?v=xiE2QldpQRQ&feature=related) Parte 4 (http://www.youtube.com/watch?v=NUSn59iY8U8&feature=related)

3

|08|716|15|31

AX

AH AL EAX

Page 8: Temario lenguaje ensamblador

En el macroensamblador de IBM las instrucciones no ejecutables que se emplean para estructurar el código fuente toma la forma de pseudoperaciones, el ensamblador permite que un programa que se ejecute en una CPU 8086, 8088 u 80286 haga uso de 4 tipos de segmentos: el de código, de datos, de pila y uno mas de datos o segmento extra de código.

El 80386 y el 80486 tiene dos segmentos adicionales de datos que fueron añadidos para la congestión de registro ES y para mejorar la coincidencia de los registros índice y base disponibles en el conjunto general de registros, estos segmentos son FS y GS.

Los programas con extensión .EXE pueden ser colocados en cualquier parte de la memoria RAM o el sistema operativo. El programa LINK únicamente añade cabeceras al programa.

Ventajas del programa .EXEa) El archivo es reubicable. Mas de un programa puede ser cargado a memoria.b) Los archivos .EXE permiten el uso de hasta 4 segmentos. Esto permite una buena modularidad

y la creación de grandes programas.

Ventajas del programa en .COMa) Ocupa menos memoria que el .EXE. Contiene solamente un segmento, este segmento incluye

toda la información necesaria que requiere el programa.

Desventaja del programa en .COMNo es reubicable y siempre debe comenzar en la dirección 0100H

El MASM es un ensamblador de dos pasadas:1ª pasada: Se realiza la traducción de tablas de símbolos, códigos, literales, etc.2ª pasada se crea el código objeto, listado de errores, etc.

Ejemplo:Etiqeta1: MOV AX, 01

CMP AX, BXJNZ Etiqueta2SUB BX, 10JMP Etiqueta1

Etiqueta2: ADD BX, AXHLTEND

Tabla de símbolosNombre Valor Longitud Reubicable Acceso Ext.

Etiqueta1 0 10 -- 1Etiqueta2 10 6 -- 0

4

Page 9: Temario lenguaje ensamblador

Tabla de códigosOP Code Cod. Hex Long Int Tipo de inst

MOV 7B 2 R/MCMP 3D 4 R/R

... ... ... ...R = registro y M = memoria

Tabla de literalesValor Localidad

01 500H10 502H

Ventajas que se obtienen al aprender un Lenguaje Ensamblador1. Habilidad para controlar el Hw.2. Habilidad para desarrollar fragmentos de programas que sean de rápida ejecución.3. Habilidad para accesar, de manera óptima y eficiente, el coprocesador2.4. Comprensión de los métodos utilizados para realizar la sintaxis asociada con lenguajes de

alto nivel.5. Conocimiento profundo de los sistemas basados en microcomputadoras y de interfaz de

Hw/Sw.6. Disciplina para programar de manera estructurada.7. Comprensión de la forma en que se manejan, a bajo nivel, diversas estructuras de datos.

Sistemas NuméricosNumeración binaria

Un bit representa un dígito que tiene uno de dos valores posibles: uno o cero. El dígito representa uno de dos estados y se define como aritmética binaria o de base-2.

N-bits pueden representarse de la siguiente manera, siendo el bit más significativo el que se encuentra mas a la izquierda.

En la memoria de la computadora, los números positivos se representan como enteros sin signo. En general, con palabras de 16 bits se puede representar cualquier entero positivo en el intervalo de 0-65,535 (216 - 1). Si el bit 16 se emplea para indicar el signo del número, entonces el mayor entero que puede representarse con los 15 bits restantes está en el intervalo de (-32,767, +32,767).

Las computadoras generalmente utilizan aritmética de complemento a 2. En esta, los números positivos se representan de manera normal, y los negativos con su complemento ya que esto permite que las restas se conviertan en suma.

2 El coprocesador es el que lleva a cabo operaciones de punto flotante a muy alta velocidad.

5

2n-1 2n-2 23 22 21 20......

Page 10: Temario lenguaje ensamblador

Bits, nibbles, Bytes, WordsBit = 1 ó 0Nibble = 4 bitsByte = 2 Nibbles = 8 bitsWord = 2 Bytes = 4 Nibbles = 16 bits

Representación de enterosMagnitud con signo

En éste método de numeración binaria los valores positivos empiezan con cero y los negativos con uno. El bit más significativo representa el signo.Ejemplo: Escribir con 3 bits los valores posibles

0|00 = 00|01 = 10|10 = 20|11 = 31|00 = -01|01 = -11|10 = -21|11 = -3

Complemento a unoLos positivos comienzan con cero (magnitud con signo) y para escribir los negativos se escribe

el valor positivo y se complementa lógicamente (es decir, los 0 se convierten en 1 y viceversa)Ejemplo: Escribir con 3 bits todos los valores positivos y negativos posibles

0|00 = 00|01 = 10|10 = 20|11 = 31|00 = -31|01 = -21|10 = -11|11 = -0

Complemento a dosLos positivos se escriben igual que en complemento a uno y magnitud con signo. Los negativos

provienen de sumarle 1 al valor del complemento a uno.Ejemplo: Escribir ±131 en complemento a dos con 9 bits131 = 0|10000011 => además es m.c.s., complemento a uno y complemento a dos

1|01111100 => -131 en complemento a uno+ 1

_______________1|01111101 => -131 en complemento a dos

Exceso 2n-1

Para escribir cualquier cantidad positiva o negativa bastará con sumarle 2n-1, donde n=número de bits empleados.Ejemplo: Escribir ±36 en exceso 2n-1 con 7 bits.

36 + 27-1= 36 + 26 = 36 + 64 = 100 => 1100100-36 + 27-1= -36 + 26 = -36 + 64 = 28 => 0011100

6

Page 11: Temario lenguaje ensamblador

Representación de punto flotanteLos números de punto flotante se representan en la forma a·be donde: a=es la mantiza normalizadab=es la base del sistema de numeracióne=es el exponente de la base

Ejemplo:Representar 1943 en punto flotante en base decimal.1943 x 104 donde a=.1943, b=10 y e=4

se dice que un número es de punto flotante normalizado cuando se cumple:

donde b=es la base del sistema de numeración, x=es el número en punto flotante

normalizado.

Ejemplo: en el sistema decimal debe cumplir o sea

En binario debe cumplir o sea

La familia PDP-11 de DEC representan sus números de punto flotante con 32 bits:

La IBM representa sus números así:

0.5x2=1.00.0x2=0.0

Bit escondidoLa mantiza se supondrá normalizada (siempre empezará con .1) y el primer 1 después del punto

binario se omitirá suponiendo que esté presente (escondido).

Ejemplo Escribir –135.5 como la PDP-11 y como la IBM135=10000111-135=110000111 mcs0.5 x 2 =1.0entonces –135.5=110000111.1=.1100001111x29, en PDP-11 es: 9 + 28-1(8 por el número de bits del exponente)=9 + 27=9 + 128=137 (en exceso 2n-1)

7

1 8 23

Signo de la mantiza

Exponente en Exceso

2n-1

Mantiza en M. C. S. y 1 bit escondido

1 7 24

Signo de la mantiza

Exponente en Exceso

2n-1

Mantiza en M. C. S. y 1 bit escondido

Page 12: Temario lenguaje ensamblador

137=10001001 (exponente)

Representar el punto flotante como la IBM el 237.57

El DebugEl Debug es una herramienta de edición y comprobación de programas el cual proporciona un

control de pruebas en un ambiente binario y de archivos ejecutables. Se puede ejecutar de dos maneras: Introduciendo DEBUG desde la línea de comandos (C:\>Debug <>) Para depurar un archivo ejecutable (C:\>Debug ejemplo.EXE <>)

DEBUG [[unidad:][ruta]archivo [parámetros_de_test]]Donde [unidad:][ruta]archivo Especifica el archivo que se desea comprobar.parámetros_de_test Especifica la información de línea de comandos que precisa el archivo que

se desea comprobar.

Si decide trabajar desde la línea de comandos del debug, al momento de presionar el enter aparecerá un nuevo prompt esperando instrucciones. La línea de comandos del debug consiste en una sola letra con uno a más parámetros. Después de iniciar Debug, escriba ? para visualizar una lista de los comandos de depuración. Si un error de sintaxis ocurriera se indicará mediante un ^Error

Lista de comandosComando Función

A [address] Assemble C range address Compare Intervalo de direccionesD [range] DumpE address [list] EnterF range list FillG [=address [address …]] GoH value value HexI value InputL [addres [drive record record]] LoadM range address MoveN file descriptor [file descriptor] NameO value byte OutputP [=dirección] [número] ProceedQ QuitR [register-name] RegisterS range list SearchT [=address] [value] TraceU [range] UnassembleW [address [drive record record]] WriteXA [N.páginas] allocate expanded memoryXD [identificador] deallocate expanded memory

8

1 10001001 .00001111000000000000000

0 1001001 .110110110010001111010111

Page 13: Temario lenguaje ensamblador

Comando FunciónXM [páginaL] [páginaP] [identificador] map expanded memory pages XS display expanded memory status

El debug permite colocar en memoria y ejecutar un grupo de instrucciones en lenguaje de máquina una a una en un tiempo, permitiendo observar cómo el programa trabaja. El debug utiliza números hexadecimales puesto que en términos de longitud es más fácil que con números binario.

Trace (T) ejecuta una o más instrucciones comenzando con la dirección actual del apuntador de instrucciones (CS:IP)

Dump (D) Muestra el contenido de la memoria comenzando con una determinada localidad (indicada ésta como segmento:desplazamiento)

Quit (Q) termina la ejecución del DEBUG

9

Page 14: Temario lenguaje ensamblador

INTRODUCCIÓN A LA PROGRAMACIÓN EN LENGUAJE ENSAMBLADOREl alumno conocerá cómo representa la computadora de manera interna los datos e instrucciones que

procesa mediante el debug.

EL DEBUG y su Uso

El término Debugging nació en los primeros días de la computación, en particular, un día en el cual la computadora Mark I de Harvard falló. Después de buscar el problema lo encontraron: una pequeña basura estaba entre los contactos de un relay (contacto electromagnético), la limpiaron y en el libro técnico de la Mark I escribieron acerca del Debugguing.

El debug permite colocar en memoria y ejecutar un grupo de instrucciones en lenguaje de máquina una a una en un tiempo, permitiendo observar cómo el programa trabaja. El debug utiliza números hexadecimales, puesto que en términos de longitud es más fácil que con números binarios.

El nombre hexadecimal proviene de hexa (6) y deca (10), lo cual combinado representa 16 dígitos (0 – 9, A - F). Con dos números hexadecimales se pueden representar 256 diferentes números con dos dígitos.

Para ejecutar el Debug deberá estar en el símbolo del sistema (DOS) e introducir:

C:\>Debug <>3 Para ejecutar el debugInmediatamente aparece un prompt ( - )Para salir del Debug deberá teclear Q- Q <> Para salir Q (Quit)

El comando H (Hexarithmetic) suma y resta dos números hexadecimales- H 3 2 <>0005 0001

- H 3D5C 2A10676C 134C- H 3A7 1ED0594 01BA

- H 9 1000A 0008

- H 9 6000F 0003

¿Qué pasa si nosotros usamos H 2 3?- H 2 30005 000F

El FFFFh es igual al 65,535 ó 64 Kb alias –1H 5 FFFF0004 0006

3 <> Significa Enter

10

Page 15: Temario lenguaje ensamblador

¿Qué pasa si nosotros utilizamos números de 5 dígitos?H 5CF00 4BC6

^ERROR

El 8088/8088/80286/80386/80486 puede usar números con signo o sin signo. En la forma binaria para números positivos el bit 5 es siempre 0, para los negativos 1. Si nosotros usamos instrucciones para números sin signo en nuestro programa, el procesador ignora el bit de signo, de tal manera que nosotros podemos usarlo a nuestra conveniencia. Los números negativos son conocidos como complemento a dos del número positivo.

11

Page 16: Temario lenguaje ensamblador

ARITMÉTICA DEL 8086/8088/80286 MEDIANTE EL DEBUG

Conociendo algo del Debug y la aritmética binaria del procesador, nosotros podemos aprender cómo el procesador trabaja y puede ejecutar órdenes internas llamadas instrucciones.

REGISTROS COMO VARIABLESDebug, nuestro guía e intérprete, conoce mucho acerca del procesador. Vamos a preguntar al Debug qué

podemos hacer respecto a esas pequeñas piezas llamadas registros que podemos usar como variables en donde podemos almacenar datos. El procesador contiene un número fijo de registros conocidos como registros de propósito general, los cuales no son parte de la memoria RAM de la PC.

Nosotros le podemos solicitar al Debug que despliegue la información contenida en los registros con el comando R (Register). Probablemente vea diferentes números en las líneas dos y tres del desplegado en la pantalla, éstos números reflejan la cantidad de memoria de su computadora.

Por ahora, el Debug nos ha proporcionado mucha información. Nos concentraremos en los registros AX, BX, CX y DX, los cuales deben ser iguales a 0000. Los números de cuatro dígitos siguientes para cada registro están en notación hexadecimal. Una palabra está compuesta por cuatro dígitos hexadecimales. Cada uno de estos registros son de 16 bits, esto explica porque 8086/8088/80286 son conocidas como máquinas de 16 bits.

Los otros registros, también son conocidos como de propósito especial: SP, BP, SI, DI, DS, ES, SS, CS, e IP.

El comando R hace más que desplegar los registros, también nos permite cambiarlos. Por ejemplo, nosotros podemos cambiar el valor de registro AX.

- R AX <>AX 0000: 3A7 <>R (Para comprobar)

Desde este momento utilizaremos el Debug como un intérprete, así que nosotros podremos trabajar directamente con el procesador.

Ahora, colocaremos un número en BX y otro en AX y, le pediremos al procesador que los sume y deje el resultado en AX. Coloque AX=3A7h y en BX=92A con el comando R. Verifíquelo.

LA MEMORIA DEL PROCESADOR¿Cómo le podemos decir al procesador que adicione BX a AX?. Nosotros colocaremos dos bytes de

código de máquina en algún lugar de su vasta memoria RAM, que le diga al procesador que sume los registros con la ayuda del debug.

La memoria está dividida en piezas de hasta 64Kb llamados segmentos. Nosotros colocaremos la instrucción en algún lugar de un segmento y luego le diremos dónde está y que la ejecute sin saber dónde inicia dicho segmento.

Todos los bytes de la memoria RAM están etiquetados con números iniciando con 0000h. Pero recuerda que la limitación de los números hexadecimales es de 4 dígitos. De esta manera, el número más alto que puede ser usado como etiqueta de memoria es de 65,535, lo cual, implica la longitud máxima de los segmentos.

12

Page 17: Temario lenguaje ensamblador

Sin embargo, el procesador puede llamar más de los 64 Kb de memoria. ¿Cómo puede ser esto?. Se usan dos números, uno para cada segmento de 64 Kb. y otro para el desplazamiento dentro del segmento. De tal manera, que los segmentos están traslapados pudiendo el procesador utilizar más de un millón de bytes en memoria.

Todas las etiquetas de dirección serán usadas como resultado del principio de un segmento. Por ejemplo: 3756:0100 significará que nosotros estamos en la dirección 0100h del segmento 3756.

Por ahora, confiaremos del debug para cuidar el segmento por nosotros. Así, que nosotros trabajaremos sin prestar atención a los números de segmento. Ahora, cada dirección se refiere a un byte de un segmento y las direcciones son consecutivas.

Colocaremos la instrucción de adición ADD AX, BX en la posición 0100h y 0101h del segmento. El código de la instrucción es 01D8h.

El Comando del Debug para examinar y cambiar los datos en la memoria es E (de Enter). Use éste comando para colocar la instrucción.

- E 1003756:100 B4.01<>

- E 1013756: 0101 85.D8<>

El número de segmento que observa, probablemente sea diferente pero eso no afecta la operación.

ESTILOS DE ADICIÓNSi damos R, verificaremos que está cargada la instrucción correcta. Los bytes 01h y D8h tal vez no

significan nada para nosotros, pero para la máquina sí; es el código para el nemotécnico ADD AX, BX.

Ahora, debemos decir al procesador dónde encontrar la instrucción (el segmento y el desplazamiento), lo cual lo encuentra a partir de los registros CS e IP. Al desplegar nuevamente los registros veremos el valor del segmento en CS, la segunda parte de la dirección se almacena en IP. Colocaremos IP= 0100, inicialmente, siempre apunta a 0100h.

Ahora le diremos al debug que utilice la instrucción mediante el comando T (Trace), el cual ejecuta la instrucción en un tiempo. Después de ejecutar una instrucción el IP se incrementa y apunta a la siguiente dirección, es decir, en 0102h (nosotros no hemos colocado ninguna instrucción en esa dirección, pero el procesador sí). AX contiene ahora el resultado CD1h. Repita la instrucción, con los valores que conservan los registros, el resultado en AX=15FBh y en BX=092Ah.

ESTILOS DE RESTAAhora, vamos a escribir una instrucción para restar BX a AX, con los datos que conservan. Así que, el

código para la resta es 29h y D8h. Cárguelo a partir de la dirección IP=0100h, ahora ejecute la instrucción T, el Resultado en AX es 0CD1h, repita nuevamente, ahora AX es 03A7h.

NÚMEROS NEGATIVOSEl procesador usa el complemento a dos para los números negativos. Ahora, trabajaremos con la

instrucción SUB para calcular números negativos. Le haremos una pequeña prueba al procesador para obtener el resultado FFFFh alias -1. Nosotros le restaremos un 1 a 0 colocando AX=0000 y BX=0001. Repitamos la instrucción SUB en la dirección 0100. ¿Cuál es el resultado?. Haga la prueba con otros datos.

13

Page 18: Temario lenguaje ensamblador

BYTESTodos los datos hasta ahora han sido representados en palabras. ¿El procesador sabe cómo representar la

aritmética con Byte?. La respuesta es sí.

Los registros de propósito general están divididos en dos bytes, conocidos como alto (High) y bajo (low). Ahora ejecutaremos la instrucción ADD AH, AL, lo cual es una adición de dos bytes del registro AX y el resultado quedará en AH. El código para esta instrucción es 00h y C4h.

Carguemos AX =0102h, es decir AH=01 y AL=02; almacene el código de la instrucción a partir de la dirección 100h y haga IP=0100h y ejecute la instrucción con el comando T. Ahora encontrará que AX=0302h. El resultado de 01h + 02h = 03h que está almacenado en AH.

Ahora, suponga que deseamos sumar 01h y 03h. ¿Podemos colocar 01h en AL?, la respuesta es no. Tendrá que colocar 0301 porque el debug sólo nos permite cambiar la palabra completa.

Ejecute nuevamente la instrucción, el resultado es 0401h, la suma de 03h + 01h está ahora en AH.

ESTILOS DE MULTIPLICACIÓN Y DIVISIÓNLa instrucción de multiplicación llamada MUL y su código de máquina para multiplicar AX y BX es

F7h y E3h. La instrucción MUL almacena su respuesta en los registros AX y DX, puesto que multiplicar dos números de 16 bits da como resultado uno de 32 bits. La parte alta de los 16 bits en DX y la parte baja en AX. Nosotros escribiremos esta combinación de registros como DX:AX.

Coloque el código de MUL a partir de la dirección 0100h y coloque AX=7C4Bh y BX=0100h. Compruebe con R la instrucción, la cual observará como MUL BX. El procesador siempre realiza la operación por default en AX.

0100h * 7C4B. Los tres dígitos del 100 tienen el mismo efecto que en haxadecimal. El resultado es 7C4B00h, es decir, se suman dos ceros a la derecha DX=007Ch y AX=4B00h. Multiplicar dos palabras juntas nunca pueden ser más de 2 palabras.

Cuando nosotros dividimos dos números, el procesador da como resultado el cociente y el resto. El código para la división es F7h y F3h. Colóquelo en la dirección 0100h y 010h. Como la instrucción MUL, DIV también usa DX:AX. Si desplegamos con R, veremos la instrucción DIV BX.

Ahora, dividamos el resultado de MUL que está en DX:AX, es decir, 007Ch:4B00h. Hagamos la división 7C4B00h/0100h, por lo tanto pongamos 0100h en BX. El resultado, el cociente en AX=7C4Bh y el resto en DX=0000h.

14

Page 19: Temario lenguaje ensamblador

IMPRIMIENDO CARACTERES

Vamos a iniciar con las interrupciones del DOS para enviar un carácter a la pantalla. Construiremos un pequeño programa y aprenderemos otra manera de poner datos en los registros. Ahora, veamos si podemos hablar con el DOS.

INT 21h (El poder de la Interrupción)Usaremos la instrucción llamada INT para interrumpir lo que está haciendo el procesador y decirle al

DOS que imprima el carácter A en la pantalla. La función 02h de la interrupción 21h del DOS imprime un carácter en la pantalla.

Entraremos al Debug y haremos AX= 0200h y DX=0041h. El código en hexadecimal para la instrucción INT 21h es CD21h, es una instrucción de dos bytes que iniciará en la dirección 100h, use R para confirmar que IP=100h.

No podemos usar el comando T para ejecutar esta instrucción (puesto que ejecuta una instrucción en un tiempo), pero la instrucción INT llama (invoca) a un programa largo “subroutine” del DOS, porque trazaríamos a través de una instrucción en un tiempo. Nosotros queremos ejecutar nuestra línea de programa, pero detenernos antes de ejecutar la instrucción de la localidad 102h. Lo anterior, podemos hacerlo con el comando G (GO), indicando la dirección en la cual queremos detenernos.

- G 102 <>

DOS imprimirá el carácter A y retornará el control a nuestro programa. En cierto sentido, nuestra línea de instrucción es en realidad dos instrucciones, la segunda instrucción está en 102h.

INT 21MOV SP BP

El registro 02h en AH le dijo al DOS que imprima un carácter. Otro número en AH, le dirá al DOS que realice una función diferente. El DOS usa el número en DL como el código ASCII para el carácter a imprimir. El código de A=41h.

Son muchas las operaciones que realiza el DOS para imprimir un carácter simple.

INT 20h (Una salida con gracia)Si nosotros usamos la interrupción 20h, INT 20h (cuyo código hexadecimal es CD20h), le decimos al

DOS que deseamos salir de nuestro programa, así que el DOS puede tomar el control de nuevo. En nuestro caso, INT 20h enviará el control de nuevo al Debug porque nosotros estamos ejecutando nuestro programa desde el Debug en vez del DOS.

Coloque la instruscción INT 20h iniciando en la localidad 100h, verifique con el comando R. Ahora, ejecute la instrucción con el comando

- G 102 <>Program terminated normally (El programa ha finalizado con normalidad)

UN PROGRAMA DE DOS LÍNEAS, PONIENDO LAS PIEZAS JUNTASAhora, juntemos los dos tipos de instrucciones colocándolas a partir de la dirección 0100h (sus códigos

son CD21h y CD20h).

15

Page 20: Temario lenguaje ensamblador

Para listar varias instrucciones (es decir, ver los nemónicos), necesitamos el comando U (Unassembler). Al proporcionar

- U 100 <>

La computadora desplegará varias instrucciones, nosotros reconoceremos las dos primeras. Posteriormente, coloque AX=0200h y DX= cualquier número ASCII del carácter que desee imprimir en pantalla. Para ejecutar las instrucciones

- G 104 <>(X) es la letra que se deseó imprimir en DXProgram terminated normally

PROGRAMAS ENTEROSHasta ahora, nosotros hemos cargado nuestras instrucciones con números (códigos de máquina). El

comando A (Assembler) nos permite cargar nemotécnicos directamente en memoria.

- A 100 <>3970:0100 INT 213970:0102 INT 203970:0104 <>

Aquí, el comando A le dijo al Debug que deseamos colocar instrucciones en forma nomotécnica a partir de la dirección 0100h.

MOVIENDO DATOS ENTRE REGISTROSColoque 1234h en AX (AH=12h y AL=34h) y ABCDh en DX (DH=ABh y DL=CDh).- A 100 <>3970:0100 MOV AH, DL3970:0102 MOV AL, DH3970:0104 <>

Verifique que IP=0100h y ejecute las instrucciones con- G 104Observe los datos almacenados en AX.

Ahora, almacene a partir de la dirección 0200h las siguientes instrucciones

MOV AH, 02 ; Carga tipo de funciónMOV DL, 2A ;Carga código ASCII del *INT 21 ;Solicita la INT 21h para imprimir un carácterINT 20 ;Retorna el control al Debug

Ejecútelo y observe qué pasa.

Para grabar el programa en disco, use primero el comando N (Name), el cual asigna un nombre a un archivo antes de grabarse.

- N imprime.com

Antes de grabar, debemos proporcionar el número de bytes a grabar a partir de la dirección contenida en IP. Para hacer esto, se calcula: 4 instrucciones * 2 bytes = 8 bytes de longitud. Otra manera, es observar la dirección final del programa y hacer

- H 208 200 <>

Siempre se debe proporcionar una localidad después de la última instrucción

16

Page 21: Temario lenguaje ensamblador

El debug utiliza los registros BX:CX para almacenar la longitud de un archivo a grabar, por lo tanto, colocaremos en CX el 08h y en BX el 00h. Por último, para grabar el programa, usaremos el comando W (Write)

- W <>Writing 0008 bytes

Para correrlo desde el DOS sólo se da el nombre del programa.A:\ imprime <>

ESCRIBIENDO STRINGS DE CARACTERESDebemos usar la interrupción 21h con una función diferente en AH para escribir una string en la

pantalla. Antes debemos almacenar en memoria y decirle al DOS dónde se encuentra.02= Imprime un carácter en pantalla.09 Imprime una string y se detiene al encontrar el carácter $

Colocaremos la string a partir de la localidad 0200h con E 20048 65 6C 6C6F 2C 20 444F 53 20 6865 72 65 2E24

La string termina con el número 24h, que indica fin de la string. Lo anterior, explica porqué el DOS nunca utiliza el $, es decir, no puede imprimirlo.

Con el comando D (Dump), podemos ver las cadenas de caracteres en memoria.- D 200 <>------------ ------------------------------------------------------ HELLO, DOS HERE

Vemos 16 bytes hexadecimales, seguidos de los mismos pero en ASCII. Donde vea un puntoen la ventana de ASCII, representa un carácter especial, por ejemplo la letra griega beta o theta. El comando D despliega 96 caracteres de los 256 caracteres usados en la computadora, es decir, que 160 caracteres son representados por un punto (.).

MOV AH, 09 ;Función imprime stringMOV DX, 0200 ;Dirección donde inicia la stringINT 21INT 20Al ejecutarlo,HELLO, DOS HERE.El programa ha finalizado con normalidad

Ahora, asigna un nombre al programa y grábalo en disco, puedes usar el comando H para auxiliarte a calcular la longitud del programa.

17

Page 22: Temario lenguaje ensamblador

Registro de banderasUsos comunes de los registros internos del 8088Registro DescripciónDatosAX Acumulador: usado para almacenamiento de programación en general, también para

algunas instrucciones como multiplicación, división, I/O, manejo de cadena de caracteres.BX Base: Cuando se accesa la memoria, con frecuencia se utiliza este registro para contener

valores de direcciones. Al hacer uso de rutinas de servicios de interrupción, este registro debe contener un valor que se usa para selección de opciones

CX Contador: durante la ejecución de un loop, este registro contiene el valor de un índice de conteo

DX Datos: usado para almacenamiento general y también para operaciones de multiplicación y división

SegmentoCS Registro de segmento de código: éste registro apunta al inicio del segmento donde el

programa en ejecución se encuentra situadoDS Registro de segmento de datos: Señala el inicio del segmento de datosSS Registro de segmento de pila: Señala el inicio del segmento de pilaES Registro de segmento extra: Señala el inicio del segmento de extraApuntadorSP Apuntador de pila: para algunas instrucciones este registro contiene valores de

desplazamiento para el stackBP Apuntador base: Similar a SP. Algunas instrucciones hacen uso de el con el fin de guardar

el valor de un desplazamientoÍndiceSI Índice fuente: para ciertas instrucciones, este registro contiene la dirección fuente con

frecuencia las instrucciones que hacen uso de este recurso no requieren de operandosDI Índice destino: Contraparte con SI y contiene la dirección destino para algunas

instrucciones.

IP Apuntador de instrucciones: apunta a la localidad de memoria donde se encuentra la próxima instrucción a ser ejecutada

SF Banderas o registro de estado de banderas: existen 9. estas proporcionan información con respecto al resultado de varias operaciones

Bits15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0X X X X OF DF IF TF SF ZF X AF X PF X CF

SF

Notas: AX, BX, CX, DX se subdividen en 2 bytes (p.e. AX = AH,AL)AX = 16 bits = 1 wordAH, AL = 8 bits cada uno = 1 byteLos demás registros requieren de 1 word o sea 16 bits

Todos los nombres de registros son palabras reservadas al efectuar un programa en ensamblador.

18

Page 23: Temario lenguaje ensamblador

Bandera DebugNo. Bit Designación Descripción S O

0 CF Bandera de acarreo: el valor de este bit es 1 si el resultado de una operación de adición o sustracción genera un acarreo o préstamo.

CY NC

1 No usado --------------------------------------------------------------------------------------------------------- ---- ----2 PF Bandera de paridad: es un 1 si el resultado de una operación de datos tiene un número

par de bits iguales a 1PE PO

3 No usado --------------------------------------------------------------------------------------------------------- ---- ----4 AF Bandera de auxiliar de acarreo: indica la presencia de un acarreo generado del cuarto

bit de 1 byte. Su mayor uso es durante operaciones aritméticas con números decimales codificados en binario

AC NA

5 No usado --------------------------------------------------------------------------------------------------------- ---- ----6 ZF Bandera de cero: es activada si el resultado de una operación es cero ZR NZ7 SF Bandera de signo: se activa si el resultado de una operación con números signados es

negativo.NG PL

8 TF Bandera de trampa: cuando este bit es activado, el 8088 ejecuta una instrucción a la vez (No se considera en el debug)

---- ----

9 IF Bandera de habilitación de interrupción: el 8088 atenderá a las interrupciones solo cuando este bit sea activado

EI DI

10 DF Bandera de dirección: Cuando es activada, causa que el contenido de los registros índice se decremente después de cada operación de una cadena de caracteres

DN UP

11 OF Bandera de sobreflujo: es activada cuando el resultado de una operación es mayor que el máximo valor que es posible representar con el número de bits del operando destino

OV VN

12-15 No usado --------------------------------------------------------------------------------------------------------- ---- ----

19

Page 24: Temario lenguaje ensamblador

IMPRIMIENDO NÚMEROS BINARIOS

Ahora nosotros construiremos un programa para escribir números binarios en la pantalla.

BANDERAS DE ACARREO Y ROTACIÓNSi le adicionamos 1h a FFFFh, el resultado debería ser 10000h; sin embargo, se produce un

overflow. Solamente los 4 dígitos más a la derecha encajan en una palabra, pero el 1 no. El 1 es un overflow y no está perdido. Se va a un lugar llamado bandera, en este caso, la bandera de acarreo o CF. Las banderas contienen un bit (0,1). Si nosotros necesitamos un acarreo de un 1 dentro del dígito quinto, éste va dentro de la bandera de acarreo.

Si hacemos AX = FFFFh y BX = 01h y ADD AX, BX. Al final de la segunda línea del debug al dar R, verá 8 pares de letras. Podrá leer NC o CY (acarreo). El resultado de la operación, resultará un overflow de 1, entonces, la bandera estará en CY (carry). El acarreo es de 1, es decir está activada.

Para comprobar que se ha almacenado el bit 17 (o el noveno para una operación de 8 bits), adicione 1 a 0 en AX, con IP = 0100h y repitiendo la instrucción de nuevo. La bandera estará afectada por cada instrucción ADD, y en este momento no hubo acarreo, por lo que se desactivará, lo que indica con NC (no carry) al dar R.

Al imprimir un número binario, la información del acarreo nos es útil. Imprimiremos solamente un caracter a un tiempo y sacaremos los bits de nuestro número uno por uno, desde la izquierda a la derecha. Por ejemplo, el primer caracter del número 1000 0000b es el uno.

Nosotros podemos mover un byte un lugar hacia la izquierda, almacenando el uno en la bandera de acarreo y adicionando un 0 a la izquierda, repitiendo el proceso para cada dígito sucesivo. Para hacer esto, usamos la instrucción RCL (rotación de acarreo hacia la izquierda). Ejemplo:

RCL BL, 1 ;Rota el byte en BL un lugar hacia la izquierda.

Esto lo hace a través de la bandera de acarreo. La instrucción es llamada rotación porque RCL mueve el bit de la izquierda a la bandera de acarreo. En este proceso, todos los demás bits son movidos o rotados a la izquierda. Después de cierto número de rotaciones suficientes (17 para una palabra, 9 para un byte), los bits serán movidos a la posición original y se regresará al número original.

Ponga B7 en BX y ejecute la instrucción 9 veces. Convirtiendo sus resultados a binario, observará:

Carry Registro BL No. Hexadecimal0 1011 0111 B71 011 01110 6E0 11 011101 DD… … …0 1011 0111 B7

20

Page 25: Temario lenguaje ensamblador

Ahora veremos cómo convertir el bit en la bandera de acarreo en un carácter 0 ó 1.

ADICIÓN CON LA BANDERA DE ACARREOLa instrucción ADC (adiciona con acarreo), adiciona tres números, los dos normales mas un bit

de acarreo. El 0 = 30h y el 1 = 31h en código ascii. Así que adicionando a la bandera de acarreo el 30 conseguiremos el 0 cuando el bit de acarreo está en 0 (desactivado) y 1 cuando el bit de acarreo está en 1 (activado).

Si DL = 0 y la bandera de acarreo está activada (1) y ejecutamos: ADC DL, 30 entonces conseguimos el 31h = 1b, es decir, nosotros podemos convertir el acarreo en un caracter que podemos imprimir.

Ahora, nosotros necesitamos un loop para ejecutar RCL, ADC e INT 21h 8 veces, una para cada bit del byte.

LOOPINGEl loop es como un for – next, pero no tan general. Loop decrementa CX y finaliza cuando

CX=0.

Aquí presentamos un programa simple que rotará BX 8 veces, moviendo BL en BH (pero no al revés), puesto que nosotros rotamos a través de la bandera de acarreo. El loop inicia en 106h y termina en la instrucción loop.

0100 MOV BX, A3C50103 MOV CX, 00080106 RCL BX, 10108 LOOP 0106010A INT 20

Para ejecutarlo, puede hacerse paso a paso con T o con G 010A (sin ejecutar INT 20 puesto que inicializa los registros). Podemos comprobar que CX = 0 y que BX = C551 o BX = C5D1, dependiendo del valor inicial de la bandera de acarreo.

ESCRIBIENDO NÚMEROS BINARIOS0100 MOV AH, 020102 MOV CX, 00080105 MOV DL, 000107 RCL BL, 10109 ADC DL, 30010C INT 21010A LOOP 01050110 INT 20

Ejecútese con G después de INT 20, BL contiene el número impreso en binario. Recuerde que no se puede ejecutar con T la instrucción INT 21 e INT 20.

21

Page 26: Temario lenguaje ensamblador

IMPRIMIENDO EN HEXADECIMAL

COMPARANDO CON LA AYUDA DEL REGISTRO DE BANDERA.Si el resultado de la última operación de la bandera del cero (ZF) es cero, entonces, la bandera

será ZR (Zero), en caso contrario, al activarse será NZ (Not Zero).

Si la bandera del signo (SF) es cero, entonces, la bandera será PL (Plus o positivo), en caso cotrario, al activarse será NG (Negative).

Si la bandera del overflow (OF) es cero, entonces, la bandera será VN (No overflow), en caso contrario, al activarse será OV (Overflow).

JZ (Salta si es cero), salta si el resultado de la última operación aritmética es cero, es decir, cuando la bandera ZF es ZR.

JNZ (Salta si no es cero), salta si el resultado de la última operación aritmética no es cero, es decir, si la bandera ZF es NZ.

Ejemplo:396F:0100 MOV AL,05396F:0102 SUB AL,01396F:0104 JNZ 0102396F:0106 INT 20

CMP (Compare), permite realizar comparaciones sin almacenar el resultado, es decir, puede activar únicamente el registro de banderas. Ejemplo:

CMP AX, BX

Si el resultado es cero se activa la bandera del cero en ZR = 1, pero los datos en los registros se conservan.

IMPRIMIENDO UN DÍGITO EN HEXADECIMAL.Iniciaremos colocando un pequeño número entre 0h y Fh en BL. Los caracteres ASCII del 0 al 9

son 30h a 39h, de la A a la F, son 41h a 46h. Estos dos grupos ASCII, están separados por 7 caracteres. Como resultado, la conversión ASCII será diferente para los dos grupos de números. Cada grupo se manejará en forma diferente.

If BL < 0AhThen BL = BL +30hElse BL = BL + 37h

22

Page 27: Temario lenguaje ensamblador

Otra maneraBL = BL +30hIf BL >= 3A

Then BL = BL +07h

El siguiente programa cargará en BL un simple dígito en hexadecimal y lo imprimirá en pantalla.

127B:0100 MOV AH,02127B:0102 MOV DL, BL127B:0104 ADD DL, 30127B:0107 CMP DL, 3A127B:010A JL 010F127B:010C ADD DL,07127B:010F INT 21127B:0111 INT 20

CMP compara DL con 3Ah y activa banderas pero no cambia DL. JL (Jump if less than, Salta si DL < 3Ah).

Mediante una operación lógica podemos aislar los 4 bits más bajos que representan el segundo dígito hexadecimal. Para rotaciones de más de un bit utilizamos el registro CL para llevar la cuenta. CL se usa para indicar el número de veces que va a rotar el byte o palabra.

¿Cómo podremos imprimir dos dígitos hexadecimales?Nuestro plan ahora, es rotar el byte en DL cuatro dígitos a la derecha, usando SHR (corrimiento

a la derecha). Moviendo los 4 bits más altos a la derecha. Si hacemos:

MOV CL, 04MOV DL, 5DSHR DL, CL

Entonces DL = 5D, o sea, el primer dígito de 5Dh, lo cual se imprimirá.

Escribiremos un programa para colocar un número en BL e imprimirlo.

MOV AH, 02MOV DL, BL ;Se inicializa con el número hexadecimal a imprimirMOV CL, 04SHR DL, CLADD DL, 30CMP DL, 3AJL BRINCO ; Aquí debe ir la dirección donde se encentra la INT 21hADD DL, 07

BRINCO: INT 21INT 20

Para aislar e imprimir el segundo dígito, dejando DL = a los 4 bits inferiores, se activan los 4 bits superiores en cero con la función lógica AND, herramienta de lógica formal.

23

Page 28: Temario lenguaje ensamblador

Por ejemplo, AND BL, CLBL 1011 0101CL 0111 0110

---------------AND 0011 0100

Usando AND BL, 0Fh tenemos:BL 1011 01010Fh 0000 1111

---------------AND 0000 1111

El programa que imprime el segundo dígito hexadecimal es:

MOV AH, 02MOV DL, BLAND DL, 0FADD DL, 30CMP DL, 3AJL BRINCOADD DL, 07

BRINCO: INT 21INT 20

24

Page 29: Temario lenguaje ensamblador

LEYENDO CARACTERES

Ahora realizaremos el proceso inverso, leeremos dos caracteres en hexadecimal del teclado y lo convertiremos a un byte.

LEYENDO UN CARACTERLa interrupción 21h puede ser utilizada con la función 01h para leer un caracter (con eco) del

teclado. Al ejecutarse, el cursor se detendrá parpadeando en espera de que presionemos una tecla. Al hacerlo, el DOS colocará el código ASCII del caracter leído en AL.

LEYENDO UN NÚMERO HEXADECIMALPara leer un carácter hexadecimal como 0, 1, …, 9, A, B, …, F, y convertirlo a un byte debemos

sustraer al código ASCII del caracter leído 30h (si el caracter es 0, 1, …, 9), o 37h (si el caracter es A, B, …, F).

MOV AH, 01h ;Función lee un caracterINT 21 ;Lee caracter y guarda su código ASCII en ALSUB AL, 30 ;Restar a AL 30hCMP AL, 09h ;¿Es número o letra?JLE Salto1 ; Si es número saltaSUB AL, 07h ; Como es letra restar al AL 07h

Salto1: INT 20 ; Regresa el control al DOS.

JLE (Jump if Less Than or Equal), salta si es menor o igual. Ocasiona que la ejecución de un programa se ramifique hacia la dirección del operando si la bandera de signo no es igual a la de sobreflujo o si la bandera de cero está activada. Esta instrucción es funcionalmente igual que JNG (Jump in Not Greater Than), salta si no es mayor que.

Nota: trabaja correctamente con números en hexadecimal válidos (o sea las letras en mayúsculas).

LEYENDO DOS NÚMEROS HEXADECIMALESSe lee el primer dígito colocando su valor en hexadecimal en DL y lo multiplicamos por 16.

para multiplicar haremos un SHL (corrimiento a la izquierda) en DL, poniéndole un cero hexadecimal (cuatro bits a la derecha). Al hacer SHL DL, CL con CL = 4 realizamos un corrimiento aritmético (ya que tiene el mismo efecto que una multiplicación aritmética por 2, 4, 8, 16, …, etc.), dependiendo del valor en CL. Posteriormente leeremos el segundo dígito hexadecimal y se lo adicionaremos al primero en DL.

25

Page 30: Temario lenguaje ensamblador

MOV AH, 01hINT 21MOV DL, ALSUB DL, 30hCMP DL, 09hJLE Salto1SUB DL, 07h

Salto1: MOV CL, 04hSHL DL, CLINT 21SUB AL, 30CMP AL, 09hJLE Salto2SUB AL, 07h

Salto2: ADD DL, ALINT 20

26

Page 31: Temario lenguaje ensamblador

PROCEDIMIENTOS Y PILAS

PROCEDIMIENTOSUn procedimiento es una lista de instrucciones que podemos ejecutar desde varios lugares de un

programa, en vez de tener que repetirlos cada vez que las necesitemos.

Llamaremos a un procedimiento con la instrucción CALL y regresaremos de este con la instrucción RET.

Programa Principal------ Procedimiento1------ ------------ ------CALL PROCEDIMIENTO1 ------------ ------------ RET------CALL PROCEDIMIENTO1------------------

End

Ejemplo de un programa en DEBUG que imprime las letras de la A a la J0100 MOV DL, 41h

0102 MOV CX, 000A

0105 CALL 0200

0108 LOOP 0105

010A INT 20

0200 MOV AH, 02

0202 INT 21

0204 INC DL

0206 RET

La primera instrucción coloca el código ASCII de la letra A (41h) en DL para imprimir el carácter mediante la INT 21, pero esta instrucción se ejecutará de forma lejana.

Cuando llamamos el procedimiento localizado en 0200 colocamos en AH el valor de 02 que es la función para imprimir el carácter contenido en DL usando la INT 21.

27

Page 32: Temario lenguaje ensamblador

INC DL, es una nueva instrucción que incrementa en uno el registro DL. RET, regresa al programa principal situándose en la primera instrucción ejecutable después de su llamada en este caso LOOP 0105.

Ejecute el programa para ver el resultado mediante el comando G.

LA PILA Y EL RETORNO DE DIRECCIONESLa instrucción CALL de nuestro programa necesita salvar la dirección de retorno en algún lugar

del microprocesador sabiendo qué instrucción ejecutar cuando regrese de la instrucción RET. Para poderlo almacenar, necesitaremos una porción de la memoria conocida como pila. Para poder seguirle el rastro de lo que hace la pila, existen dos registros que podremos observar al ejecutar R: estos son el SP (Stack Point, Apuntador de Pila) y el SS (Stack Segment, Segmento de Pila) los cuales tendrán el número del segmento.

Dirección Pila

0098:

0100: 0203

0102: 0103

0104:

En una Pila el último que entra será el primero en salir a esto es conocido como LIFO (Last In, First Out), esta secuencia es precisamente lo que necesitamos para revertir el regreso de direcciones después de que hacemos llamadas anidadas como en el presente ejemplo.

396F:0100 E8FD00 CALL 0200…

396F:0200 E8FD00 CALL 0300396F:0203 C3 RET

…396F:0300 E8FD00 CALL 0400396F:0303 C3 RET

…396F:0400 C3 RET

Aquí la instrucción en la dirección 0100h llama a uno en la dirección 0200h, la cual llama a otra en la dirección 0300h la cual llama a otra en la dirección 0400 donde finalmente vemos una instrucción de retorno (RET). Este RET, regresa a la instrucción siguiente previa a la instrucción CALL de la dirección 0300h o sea el microprocesador ejecuta la instrucción 0303h. pero encontrará otra instrucción RET en 303h la cual será empujada por la siguiente dirección (la 203h) dejándola fuera de la pila. De este modo el microprocesador retomará la instrucción ejecutando 2003h al inicio. Cada RET saca en

28

SP:0100

Page 33: Temario lenguaje ensamblador

que se encuentra al inicio regresando la dirección fuera de la pila de este modo cada RET siguiente el mismo camino volverá a las llamadas hechas atrás.

Dirección Pila

0098: 0303

0100: 0203

0102: 0103

0104:

METIENDO Y SACANDO (PUSHing and POPping)La pila es un útil lugar para guardar palabras (words) de datos momentáneamente,

proporcionándonos el cuidado de restablecer la pila después de una instrucción RET. Hemos visto que una instrucción CALL mete una dirección de retorno (una palabra, word) colocándolo al tope (principio) de la pila mientras que una instrucción RET saca esta palabra del tope de la pila. Cargándola dentro del registro IP y expone la palabra que se encontraba debajo de esta. Nosotros podremos hacer mucho entendiendo perfectamente las instrucciones de PUSH y POP.

Es conveniente salvar los valores de los registros de inicio de un procedimiento y restaurarlos al finalizar justo antes de la instrucción RET. Entonces liberaremos el uso de estos registros y de paso entender los procedimientos.

Los registros están compuestos por varios niveles de procedimientos, conduce al siguiente nivel de abajo. Por salvar los registros al inicio de un procedimiento y restaurarlo al final de este, no necesitamos remover instrucciones este procedimiento de los diferentes niveles, haciendo nuestra programación muy sencilla.

Ejemplo.0200 PUSH CX0201 PUSH DX0202 MOV CX,00080205 CALL 03000208 INC DL020A LOOP 0205020C POP DX020D POP CX020E RET

29

SP:0098

Page 34: Temario lenguaje ensamblador

Note que los POP’s están en orden inverso que los PUSH’s esto es porque un POP remueve una palabra situada más recientemente en la pila y el último valor de DX está sobre el último valor de CX.

Salvando y restaurando CX y DX nos permite cambiar los registros dentro del procedimiento iniciado en 0200h pero sin cambiar los valores usados por el procedimiento llamado y teniendo salvado CX y DX podremos usar estos registros como variables locales.

LEYENDO NÚMEROS HEXADECIMALES CON MÁS CLASEPodemos crear un procedimiento ocultando las lecturas de caracteres hasta recibir uno que

pueda convertirse en número hexadecimal entre 0 y Fh. No desplegaremos algún carácter inválido por lo tanto usaremos la función 08 de la Int 21h que lee caracteres pero no los coloca en pantalla y haremos un eco (desplegaremos) solo si es uno válido.

Coloque 8h en el registro AH y ejecute esta instrucción digitando A justo después de ejecutar G 102100 INT 21

El código ASCII de A (41h) está ahora en el registro AL, pero A no aparece en la pantalla. Usando esta función, nuestro programa puede leer caracteres sin eco hasta leer un dígito hexadecimal válido (0-9 o A-F) con eco.

Este es el procedimiento que hace eso y convierte un carácter hexadecimal a un número hexadecimal.

0200 PUSH DX0201 MOV AH,080203 INT 210205 CMP AL,300207 JB 02030209 CMP AL,46020B JA 0203020D CMP AL,39020F JA 21B0211 MOV AH,020213 MOV DL,AL0215 INT 210217 SUB AL,300219 POP DX021A RET021B CMP AL,41021D JB 0203021F MOV AH,020221 MOV DL,AL0223 INT 210225 SUB AL,370227 POP DX0228 RET

30

Page 35: Temario lenguaje ensamblador

El procedimiento lee un carácter en AL (con la INT 21 de 203h) y verifica que sea válido con las comparaciones (CMP) y los saltos condicionales. Si el carácter leído no es válido la instrucción es de salto condicional enviando al microprocesador atrás en la dirección 0203 donde la INT 21 lee otro caracter (JA, salta si está arriba; JB, Salta si está debajo; ambos tratos son para números sin signo hay instrucciones JL que usaremos para tratar a números con signo).

En la línea 211h sabremos si tenemos un dígito válido entre 0 y 9 por lo tanto sustraemos el código para colocar 0 y regresamos el resultado en el registro AL recordando sacar el registro DX cuando lo salvemos iniciando el procedimiento. El proceso del dígito hexadecimal de A - F es similar. Observe que tendremos 2 instrucciones RET en este procedimiento; tendremos varios o solamente uno.

Este es un simple programa de prueba del procedimiento.0100 CALL 02000103 INT 20

Como hemos hecho antes, use el comando G, con un punto de interrupción, o use el comando P. Ejecute la instrucción CALL 200h sin ejecutar la instrucción INT 20h, para que vea los registros antes de finalizar el programa y que se restablezcan los registros.

Usted verá el cursor al lado izquierdo de la pantalla, esperando un carácter. Teclee k que no es un carácter válido. Nada debe pasar. Ahora, teclee cualquiera de los caracteres del hexadecimal mayúsculos. Usted debe ver el valor del hexadecimal del carácter en AL y el propio carácter hechos eco de en la pantalla. Pruebe este procedimiento con las condiciones del límite: '\ ' (el carácter antes del cero), 0, 9, ': ' (el carácter sólo después de 9), y así sucesivamente.

Ahora que nosotros tenemos este procedimiento, el programa para leer un número hexadecimal de dos dígitos, con el tratamiento de errores, bastante aceptable:0100 CALL 02000103 MOV DL, AL0105 MOV CL, 040107 SHL DL, CL0109 CALL 0200010C ADD DL, AL010E MOV AH, 020110 INT 210112 INT 20

Podemos ejecutar este programa en el DOS, desde que lee en un número hexadecimal de dos dígitos y entonces muestra el carácter ASCII correspondiente al número tecleado. Aparte del procedimiento, el programa principal es mas simple que la versión escrita anteriormente, sin tener que duplicar las instrucciones para leer los caracteres. Nosotros agregamos tratamiento de errores, sin embargo, y aun cuando complicó nuestro procedimiento, también asegura que el programa acepta sólo entradas válidas.

Hemos visto la razón de salvar el registro DX en el procedimiento. El programa principal almacena el número hexadecimal en DL, para que nosotros no cambiemos DL en nuestro procedimiento situado en 200h. Por otro lado, el procedimiento situado en 200h usa el propio DL para hacer eco de los caracteres. Así, usando la instrucción PUSH DX de al principio del procedimiento, y POP DX al final, nos libramos de los problemas.

A partir de ahora, evitaremos las interacciones complicadas entre los procedimientos, nosotros seremos muy estrictos sobre guardar cualquier registro usado por un procedimiento.

31

Page 36: Temario lenguaje ensamblador

PROGRAMACIÓN EN LENGUAJE ENSAMBLADOREl alumno programará en Lenguaje Ensamblador para trabajar a bajo nivel en la computadora

y desarrollará diversas aplicaciones.

Proceso de ensamble1. Editar el programa con un editor de texto simple y que se guarde con extensión .ASM2. MASM <nombre del archivo>.ASM;3. LINK <nombre del archivo>;4. EXE2BIN <nombre del archivo>.EXE <nombre del archivo>.COM

Rutinas utilizadas en EnsambladorPrograma DescripciónEditor Programa que permite crear un código fuente. DOS proporciona el Edit.MASM Es el programa macroensamblador de IBM para cargarlo en memoria se requieren 96K

de RAM. Existe una versión más modesta denominada small assembler (ASM), que solo necesita 64K de memoria pero no ofrece muchas de las características de MASM (entre ellas el soporte a macros). MASM se emplea para ensamblar código fuente y generar código objeto. Durante su operación, MASM pide al usuario los nombres para el archivo fuente (extensión .ASM), el archivo objeto que se genera (OBJ), el listado de los nombres de archivo (LST), y finalmente un listado de referencias cruzadas (CRF)

LINK Este programa LINK se emplea para encadenar diversos módulos objetos generados ya sea pos MASM o por otros compiladores. El programa se encarga de asignar localidades de memoria absolutas para relocalizar al código objeto. El encadenador permite el desarrollo de código modular ya que con él es posible cambiar módulos individuales y para producir un programa completo. Cada módulo se puede depurar pr separado y después ser integrado al programa.

DEBUG El programa DEBUG es útil durante la fase de desarrollo de programas. Tienen características que permiten al usuario ejecutar, por ejemplo, un programa paso a paso y examinar dinámicamente cómo cambia la memoria, también observar las banderas y la ejecución del programa desde un punto determinado (break point) monitoreando los registros.

Tipos de instrucciones en lenguaje ensambladorLas instrucciones del lenguaje ensamblador pueden ser de diferentes estructuras dependiendo

del número de direcciones que maneje.

Instrucciones de 3 + 1 direcciones:

Código de operación

Dirección OP1

Dirección OP2

Dirección de destino

Dirección de la

siguiente instrucción

Ejemplos:ADD A,B,C,DMUL K,R,PZ

Instrucciones de 3 direcciones: Eliminando la dirección de la siguiente instrucción surgió la máquina con 3 direcciones. Se

ejecutaría la instrucción siguiente en orden físico:

32

Page 37: Temario lenguaje ensamblador

Código de operación

OP1 OP2

Dirección del

resultado destino

Máquinas con instrucciones de 2 direcciones: se eliminó la dirección del destino y el resultado se guardó en la localidad del segundo operando

OP1 OP2Código de operación

Dirección fuente

Dirección de destino

Ejemplo:MUL R1,R2 ; Multiplica R1 a R2 y guarda el resultado en R2

Máquinas con instrucciones de una dirección: Eliminando y usando un registro llamado acumulador en la cual se pueden realizar operaciones aritméticas, las instrucciones quedan con una sola dirección. Ésta podrá ser fuente o destino según sea lo que indique la instrucción usada.

Código de operación

Operando fuente / destino

Ejemplo:SUB AL, AL ;SUB AL se resta a ALADD AH, BH ; ADD BH se suma a AHDIV AX ; Se divide a AX el valor de AXDIV AX, CX ;DIV CX se divide AX entre CX

Máquinas de cero instrucciones: Manejando una pila se puede hablar de una máquina con cero instrucciones (aunque en verdad no son cero instrucciones).

Ésta máquina usa dos instrucciones PUSH (empuja un dato) POP (Jala un dato) para llevar a la pila y sacar de ella un dato.

Las operaciones se efectúan con los elementos superiores de la pila y el resultado se queda en ella. Por ejemplo Calcular C=(a+b)/(d-e). La pila es una estructura de datos en memoria en la cual el primer elemento que entra es el último en salir.

Apuntador de base (BP) indica la dirección base de la pila

.

.

.

Apuntador de pila (SP)

33

Page 38: Temario lenguaje ensamblador

BE A A A+B

D D D-E D-E D-E D-E CPush D Push E SUB Push A Push B ADD DIV Pop C

Instrucciones en Macroensamblador[etiqueta] mnemónico de instrucción [operando] [;comentario]

[ ] significa que es opcionaletiqueta: se usa como punto de entrada o regreso. El nombre se asocia con la dirección donde

comienza una instrucción y puede tener hasta 31 caracteres [A-Z, a-z, 0-9, ?, ., @, _, $] y debe iniciar con cualquier carácter distinto de 0-9.

mnemónico de instrucción: debe contener una de las 92 instrucciones posible. Por ejemplo:SUB destino, fuenteSUB AX, AX; AX AX-AXSUB AX, 18D

MOV to, fromMOV AX, 18

con D = Decimal; H = Hexadecimal O = Octal B = Binario

Atributos de distancia de los segmentosNEAR: Corresponden a etiquetas o procedimientos definidos en el mismo segmentoFAR: Corresponden a etiquetas o procedimientos definidos en otro segmento.

Cuando la referencia es NEAR, el registro IP cambia y si es FAR, IP y CS cambian.

Estructura de un programaEn el Macroensamblador es posible procesar hasta cuatro tipos de segmentos. El programa

mínimo requiere de dos segmentos: el de código y el de pila (CS, SS), el ensamblador busca la definición del segmento stack y genera un error, si el usuario olvida incluirlo. El pseudo-operador SEGMENT del macroensamblador sirve para definir un segmento.

Los pseudo-operadores son el medio por el que el programador le indica al ensamblador lo que debe hacer para preparar y estructurar el código en lenguaje de máquina. Este código de máquina está basado en los datos e interrupciones contenidos en el programa fuente. Los pseudo-operadores no generan código máquina. Existen cuatro tipos de pseudo-operadores: de datos (SEGMENT es incluido en este tipo), de ramificación condicional, macros y listados.

34

Page 39: Temario lenguaje ensamblador

Ejemplo de un programa en MASMPAGE 50, 132TITLE PRUEBACOMMENT *

ELABORADO POR: Sexto semestre de L. C. C. del Centro Escolar Felipe Carrillo PuertoFECHA: 1 de marzo de 2005DESCRIPCIÓN: Este módulo limpia el contenido del registro AX, dejando ceros.

Después coloca en AX el valor de 18. Todas las operaciones pueden ser observadas en el debug. *;;STACK SEGMENT PARA STACK ‘STACK’;;Se inicializará con cero el segmento del stack y se cargará con una cadena de caracteres: 64;valores de ‘stack ’;DB 64 DUP (‘STACK ’)STACK ENDSCSEG SEGMENT PARA PUBLIC ‘CODE’

ASUME CS:CSEG, SS:STACK;

SUB AX, AXMOV AX, 18SUB AX, 18

;CSEG ENDS

END

PAGE (pseudo-operador de listado): sirve para definir las dimensiones de la página y solo tiene efecto cuando se pone en lista el archivo general durante el ensamble.

PAGE operando1, operando2donde operando1 indica el número de líneas verticales por página en el listado producido en el

ensamblado (por default 66) y operando2 es el número de caracteres por línea (por default 80).

TITLE (pseudo-operador de listado): indica el título que será impreso en el encabezado de cada página de listado generado por el ensamblado.

; y COMMENT: El ‘;’ sirve para indicar un comentario de una línea y COMMENT para indicar comentarios multilínea. Para COMMENT el primer carácter diferente de un espacio se usa como delimitador; todos los demás caracteres que se encuentren entre los delimitadores son considerados comentarios y por lo tanto ignorados por el ensamblador.

Parámetros de Pseudo-Op SegmentSEGMENT: El pseudo-operador SEGMENT tiene el siguiente formato:

nombre-seg SEGMENT tipo-alineamiento tipo-combinación ‘clase’

donde:nombre-seg: indica el nombre del segmentotipo-alineamiento: señala al ensamblador la manera en la que comenzarán los segmentos en la

memoria. Existen 4 tipos:

35

Page 40: Temario lenguaje ensamblador

PARA: es el predeterminado e indica que el segmento comienza en los límites de un párrafo (dirección divisible por 16)

BYTE: el segmento puede comenzar en localidad de memoria WORD: el segmento debe iniciar en el límite de una palabra (donde la dirección sea par) PAGE: el segmento debe iniciar en una página (los últimos 8 bits de la dirección son

cero)tipo-combinación: indica la manera en la que los segmentos serán combinados o cargados

cuando se ejecute el encadenador (linker). Existen 5 formas: PUBLIC: todos los segmentos con el mismo nombre y con atributo PUBLIC serán

encadenados juntos. COMMON: todos los segmentos con el mismo nombre empezarán en la misma

dirección y se traslaparán en memoria. AT(exp): se utiliza para definir variables con un desplazamiento fijo de memoria. El

segmento se coloca en el párrafo indicado por el resultado obtenido después de evaluar “exp”.

STACK: Sirve Para indicar que el segmento es parte del STACK. MEMORY: todos los segmentos de este tipo se colocarán en direcciones de número

mayor que cualesquiera otros segmentos.‘clase’: se emplea para hacer referencia a una colección de segmentos. Los segmentos con el

mismo nombre de clase se colocan en memoria secuencial, siguiendo el orden en que los encontró el encadenador.

Cada segmento debe terminar con la siguiente sentencia:nombre-seg ENDS

DB(pseudo-operador de datos): sirve para definir una variable o para inicializar un área de memoria (DB = Define Byte). Tiene la siguiente forma:

[nombre-variable] DB expresion

DUP (duplica), en el ejemplo DB 64 DUP (‘stack ’) está inicializando un área de memoria con 64 duplicaciones de ‘stack ’

[nombre-variable]: es opcional y dependerá su uso si se desea asociar un nombre simbólico con un valor.

ASSUME (pseudo-operador de datos): le indica al ensamblador a cuál registro pertenece un determinado segmento. Tiene la forma

ASSUME segmento de registro: nombre de segmento, …

36

Page 41: Temario lenguaje ensamblador

PROGRAMANDO CON EL MASM

Ya estamos en condiciones de utilizar el ensamblador, este es un programa del DOS que hará nuestra programación más fácil. De aquí en adelante, escribiremos las instrucciones mnemónicas, directamente usando el ensamblador, para convertir nuestros programas en código máquina.

UN PROGRAMA SIN EL DEBUGActualmente hemos utilizado el DEBUG, tecleando instrucciones del programa. Ahora

escribiremos los programas sin él, y nosotros tendremos que usar un editor o un procesador de texto para crear el código, archivos que contienen nuestras instrucciones del lenguaje ensamblador.

Use el “edit”4 para ingresar las siguientes líneas de código el archivo se llamará WRITESTR.ASM (la extensión .ASM quiere decir éste es un archivo de origen ensamblador). Así como con el Debug, puede escribir el código con minúscula o con mayúscula sin embargo nosotros usaremos las mayúsculas para evitar la confusión entre el número 1 (uno) y la letra minúscula l (ele):.MODEL SMALL.CODE

MOV AH, 02hMOV DL,2AhINT 21hINT 20h

END

Ignore por ahora las tres nuevas líneas en nuestro archivo de origen, note que hay un ‘h’ después de cada número hexadecimal. Esta h le indica al ensamblador que los números son en hexadecimal. DEBUG supone que todos los números son en hexadecimal, en el ensamblador supone todos los números decimales. Nosotros le indicaremos que será un número hexadecimal poniendo una h después de cualquier número.

Nota: El ensamblador puede confundirse por los números, por ejemplo, ACh que se parece un nombre o una instrucción. Para evitar esto, siempre teclee un cero antes, para diferenciar el número hexadecimal que empieza con una letra. Por ejemplo, no teclee ACh.

Veamos lo que pasa cuando Ensamblemos un programa con ACh, en lugar de 0ACh. Aquí está el programa:.MODEL SMALL.CODE

MOV DL,AChINT 20h

END

Aquí está la salida

4 Sin embargo para facilitar nuestro trabajo usaremos el Programmer’s File Editor, ya que nos proporciona la ventaja de agregar números de línea que nos ayudará a localizar el número de línea que marque error al momento de compilar.

37

Page 42: Temario lenguaje ensamblador

A> MASM TEST;Microsoft (R) Macro Assembler Version 6.11Copyright (C) Microsoft Corp 1981, 1998. All rights reserved.

test.ASM(4) : error A2009: Symbol not defined: ACH49842 + 224473 Bytes symbol-space free0 Warning Errors1 Severe Errors

A> .

Pero cambiando el ACh a 0ACh se soluciona el ensamblador. También note el espacio de los comandos en nuestro programa ensamblador. Usaremos los tabuladores para alinear y hacer el texto más legible. Compare el programa en que usted entró con esta versión:

Ahora regresemos a las 3 líneas nuevas del archivo. Las tres nuevas líneas son todas las directivas (también llamadas pseudo-ops, o pseudo-operadores). Son llamadas directivas porque, en lugar de generar instrucciones, proporcionan información y direcciones al ensamblador. El pseudo-op END marca el fin del archivo, para que el ensamblador sepa qué hacer cuando encuentra un END. Después, veremos que este END es útil de otras maneras, también. Por ahora, apartemos cualquier discusión extensa de él o las otras dos directivas y vea cómo usar el ensamblador.

Creando archivos de origenAunque usted ha ingresado las líneas de WRITESTR.ASM, hay una consideración más. El

ensamblador puede usar archivos del origen que sólo contienen los carácteres de ASCII estándares. Si usted está usando un procesador de texto, tenga en cuenta que no todos los procesadores de texto guardan los caracteres ASCII estándares.

Antes de que probemos el archivo WRITESTR.ASM, asegúrese que todavía es ASCII. Haga Type del DOS:A>TYPE WRITESTR.ASM

Debe ver el mismo texto que ingresó. Si ve caracteres extraños en su programa, tendrá que usar un editor diferente para escribir los programas. Ahora, empecemos a ensamblar Writestr; teclee lo siguiente:A>MASM WRITESTR;Microsoft (R) Macro Assembler Copyright (C) Microsoft Corp 1981, 1988. A11 rights reserved.

49822 + 219323 Bytes symbol space free

0 Harning Errors0 Severe Errars

A>

Nosotros no hacemos nada todavía. El ensamblador ha producido un archivo llamado WRITESTR.OBJ que usted encontrará ahora en su disco. Éste es un archivo intermedio, llamado archivo objeto.

Linking

38

Page 43: Temario lenguaje ensamblador

Ahora queremos que nuestro LINK tome nuestro .OBJ y cree uno .EXE de él. Enlace WRITESTR.OBJ tecleando:A>LINK WRITESTR;Microsoft (R) Overlay LinkerCopyright (C) Microsoft Corp 1963-1988.All rights reserved. LINK: warning L4021: no stack segmentA>

Aunque el LINK nos advierte que no hay ningún segmento de la pila, nosotros no necesitamos uno ahora mismo. Después de que nosotros aprendemos a agregar más instrucciones, veremos porqué nosotros podríamos querer un segmento de la pila.

Ahora nosotros guardemos nuestro .EXE, pero este no es el último paso. Nosotros tenemos un archivo .COM tal y como lo creamos con el DEBUG. De nuevo, se verá después porqué nosotros necesitamos todos estos pasos. Para ahora, creemos un archivo .COM de Writestr.

Nosotros necesitamos el programa EXE2BIN.EXE del DOS. Exe2bin, como su nombre lo indica, convierte un .EXE a .COM, o archivo binario.A>EXE2BIN WRITESTR WRITESTR.COMA>

La contestación no nos dijo mucho. Para ver si Exe2bin trabajó, listemos todo el Writestr guardados hasta ahora: con DIR WRITESTR.*

Regresando al DEBUGLeamos nuestro archivo .COM (o .EXE) en el DEBUG y desensamble para ver cómo el

DEBUG reconstruye nuestro programa en código-máquina de WRITESTR.COMA>DEBUG WRITESTR.COM_ U397F:0100 8402 MOV AH, 02397F:0102 822A MOV DL, 2A397F:0104 CD21 INT 21397F:0106 CD20 INT 20

Los comentariosEn los programas del lenguaje ensamblador, nosotros ponemos los comentarios después de un

punto y coma El ensamblador ignora algo en la línea después de un punto y coma, para que nosotros podemos agregar algo que nosotros queremos..MODEL SMALL.CODE

MOV AH, 2h ;Selecciona la función 2 de DOS para sacar un caracterMOV DL, 2Ah ;Carga el código ASCII a ser impresoINT 21h ;Imprime con la NT 21hINT 20h ;y Sale del DOSEND

Las etiquetasCuando quisimos bifurcar (saltar) en una parte del programa a otro con uno de los comandos de

la bifurcación (JNZ, JLE, etc), nosotros teníamos que saber la dirección específica nosotros estábamos saltando a. Cada vez que programábamos, insertábamos nuevas instrucciones obligando a que cambiemos las direcciones en las instrucciones de salto. El ensamblador cuida de este problema con nombres de etiqueta que nosotros damos a las direcciones de cualquier instrucción o situaciones de la

39

Page 44: Temario lenguaje ensamblador

memoria. Una etiqueta tiene lugar. En cuanto el ensamblador vea una etiqueta, reemplaza la etiqueta con la dirección correcta antes de enviarlo delante del microprocesador.

Las etiquetas pueden ser de hasta 31 caracteres y puede contener letras, números, y algunos de los siguientes símbolos: un signo de interrogación (?), un punto (.), una arroba ( @ ) un guión bajo ( _ ) o un signo de peso ( $ ). Sin embargo no puede empezar con un dígito (0-9) y el punto puede ser usado solamente como el primer carácter.

Ejemplo: 0111

010C JLE DIGIT1010E SUB DL

DIGIT1: 0111 MOV CL0113 SHL DL,1

Tomemos como ejemplo práctico el código empleado para leer 2 dígitos hexadecimales.0100 MOV AH, 01h0102 INT 210104 MOV DL, AL0106 SUB DL, 30h0109 CMP DL, 09h010C JLE Salto1010E SUB DL, 07h0111 MOV CL, 04h0113 SHL DL, CL0115 INT 210117 SUB AL, 300119 CMP AL, 09h011B JLE Salto2011D SUB AL, 07h011F ADD DL, AL0121 INT 20

No será obvio lo que este programa hace, y si no está fresco en su mente, usted puede tener que trabajar un poco para entender el programa de nuevo. Agreguemos etiquetas y comentarios para clarificar su función:.MODEL SMALL.CODE

MOV AH, 01h ;Seleccione la función 1 del DOS, para introducir un carcaterINT 21 ;Lee un caracter, y retorna el código ASCII en el registro ALMOV DL, AL ;Mueve el código ASCII dentro de DLSUB DL, 30h ;Resta 30H para convertirlo en digito de 0 a 9CMP DL, 09h ;Está el dígito entre 0 y 9?JLE Salto1 ;Si, entonces tenemos el primer dígitoSUB DL, 07h ;No, Resta 7H para convertirlo en letra de la A a la F

Salto1: MOV CL, 04h ;Prepara para multiplicar por 16SHL DL, CL ;Realiza el corrimiento hacia la izquierdaINT 21 ;Solicita el siguiente caracterSUB AL, 30 ;Repite la operaciónCMP AL, 09h ;Es un dígito de 0-9?JLE Salto2 ;Si, tenemos el segundo dígitoSUB AL, 07h ;No, restamos 7H

Salto2: ADD DL, AL ;Agregamos el segundo dígitoINT 20 ;Finalizamos

40

Page 45: Temario lenguaje ensamblador

END

Las etiquetas, SALTO1 y SALTO2, son de un tipo conocido como Etiquetas Cercanas (NEAR), porque los dos puntos (:) aparecen después de las etiquetas cuando ellos están definidos. El término NEAR tiene que ver con segmentos que nosotros hablaremos más adelante junto con las directivas .MODEL, y .CODE. Si ensambla el programa y lo desensambla con el DEBUG, verá que SALTO1 se reemplazó por 0111h y SALTO2 se reemplazó por 011Fh.

41

Page 46: Temario lenguaje ensamblador

Modos de direccionamientoSe refiere a la manera en la que el procesador puede accesar un operando para realizar una

operación los cuales existen 7 modos básicos:

Direccionamiento inmediato: El valor del operando fuente está contenida en la instrucción. El usuario especifica un byte o palabra como operando fuente. Esta se ensambla como parte de la instrucción.

MOV AX, 00MOV AX, 04

Direccionamiento de registro: El valor del operando fuente está almacenado en algún registro de propósito general. Se interpreta la longitud del operando con el nombre del registro.

SUB AX, AXMOV AX, BXMOV DS, AX

Tomando como ejemplo la primera instrucción la unidad de ejecución (EU) toma el operando del registro AX, determina como destino del propio registro AX y la AUL lleva a cabo la operación.

Los dos modos de direccionamiento anteriores; tienen los ciclos de ejecución más pequeños y se evitan tiempos de acceso a la memoria. Solamente hay que tener en cuenta que al momento de hacer una operación las dos instrucciones sean del mismo número de bytes

MOV EBX, AX ;EBX es un registro del microprocesador 80386 y son registros extendidos

;AX es menor número de bytes que EBX ya que EBX es de 32 bits y AX de 16 por lo

;tanto AX si se le puede asignar a EBX pero no al revés

Direccionamiento directo: el procesador calcula la dirección efectiva (real o EA) de un dato en memoria y lo deposita generalmente en AX, a partir de un rótulo que representa un desplazamiento de 16 bits que es sumado a la dirección contenida en DS. (DS:AX)

MOV AX, MYDATOMOV AX, ETIQUETA1

Dirección efectiva (real) = Dirección de segmento (de datos) + desplazamiento...

.

.

.00060005 FFH MYDATO0004 F0H

Desplazamiento de MYDATO

0003000200010000 DS = 0000H

Direccionamiento de registro indirecto: el desplazamiento del dato es almacenado previamente en SI, DI, BX o BP haciendo referencia al mismo para calcular la dirección efectiva (EA). Para evitar confundir este modo de direccionamiento con el de registro, en la instrucción, los registros deben aparecer entre paréntesis rectangulares. Como EA es una dirección y no el contenido de una localidad de memoria, antes de utilizar los registrios mencionados, éstos deben contener direcciones. Una técnica

42

Page 47: Temario lenguaje ensamblador

para asegurar lo anterior es utilizar el operador OFFSET. Este modo de direccionamiento se puede usar para colocar el contenido de la localidad de memoria a la que apunta BX.

MOV BX, OFFSET DATO1MOV AX, [BX]

Se usa generalmente como matrices y/o vectores. Otra maneraLEA BX, DATA ;LEA = LOAD EFECTIVE ADDRESSMOV AX, [BX]

OFFSET calcula el desplazamiento de DATO1 y LEA carga la dirección efectiva de DATA. La dirección del operando se calcula sumando el desplazamiento de un registro índice en el segmento seleccionado. Se utiliza para accesar los elementos de un arreglo estático.

Relativo a la base: Al hacer uso de este modo de direccionamiento, la EA del operando se obtiene al sumar un desplazamiento a los siguientes registros: BP o BX. En este caso, los registros deben contener la dirección de desplazamiento. Un ejemplo del uso de este tipo de direccionamiento lo ofrece la siguiente instrucción:

MOV AX, [BX + 2]

Indexado directo: En este modo de direccionamiento, la EA es la suma del contenido de un registro índice (SI o DI) y un desplazamiento. Un ejemplo común lo constituye una secuencia de instrucciones donde primero se carga una dirección en un registro índice y después la misma se combina con una localidad de memoria.

MOV SI, 2MOV AX, DATO [SI]

En este caso, en el registro AX se coloca el contenido de la localidad de memoria cuya dirección es la de DATO + 2.

Direccionamiento base indexado: el operador de localiza en el segmento seleccionado en un desplazamiento determinado por la suma de los contenidos del registro base (BX), registro índice (SI o DI) y, opcionalmente, un desplazamiento. Se utiliza con frecuencia para accesar los elementos de un vector dinámico (vector cuya dirección base puede cambiar durante la ejecución de un programa).

Si se incluye un desplazamiento se puede accesar a un elemento de un vector, siendo, el vector un campo en un registro.

Ejemplo: si hacemos Elemento = 03H, BX = 0000H, DI = 0020H

0023 BFH

Registro 3

0013 B9H

Registro 2

0003 F3H

Registro 10022 90H 0012 3FH 0002 FFH0021 0011 12H 0001 00H0020 0010 0000

MYDATA

MOV AX, Elemento[BX][DI]Entonces AX = 90BFH

43

Page 48: Temario lenguaje ensamblador

Ejemplo de los diferentes modos de direccionamientoPAGE 40, 132TITLE DIRECCIONAMIENTOSCOMMENT *

ELABORADO POR: Sexto semestre de L. C. C. del Centro Escolar Felipe Carrillo PuertoFECHA: 15 de marzo de 2005DESCRIPCIÓN: Esta rutina muestra los diferentes modos de direccionamiento

disponibles en el Macro ensamblador. *

STACK SEGMENT PARA STACK ‘STACK’;DB 64 DUP (‘STACK ’)STACK ENDS

DATA SEGMENT PARA PUBLIC ‘DATA’DDDD DW 0DDDW DW 300DDDX DW 200DDDY DW 150DDDZ DW 125DDDQ DW 100DDDR DW 80DDDS DW 70DDDJ DW 60DDDU DW 50

DATA ENDS

CSEG SEGMENT PARA PUBLIC ‘CODE’

START PROC FARASUME CS:CSEG, DS:DATA, SS:STACK

PUSH DSSUB AX, AX ;estas tres instrucciones preservan el PSPPUSH AX

MOV AX, SEG DATA ;Localiza dirección del segmento DATAMOV DS, AX ;Carga en DS la dirección del segmento

MOV AX, DDDW ;Direccionamiento directo

MOV BX, OFFSET DDDX ;Direccionamiento indirecto de registroMOV AX, [BX]

MOV AX, [BX+2] ;Direccionamiento relativo de la base

MOV SI, 2 ;Direccionamiento indexado directoMOV AX, DDDZ [SI]

MOV BX, OFFSET DDDW ;Direccionamiento indexado de baseMOV SI, 8MOV AX, [BX] [SI+2]

RETSTART ENDPCSEG ENDS

END START

44

Page 49: Temario lenguaje ensamblador

En este programa incluimos el concepto de regresar el control al DOS una vez terminada su ejecución de instrucciones, así como introducir el concepto de procedimiento.

El programa muestra cinco de siete modos básicos de direccionamiento. El de registro y el inmediato ya fueron estudiados con anterioridad, además se incluye un segmento de datos que sirve para presentar las técnicas de direccionamiento. Para inicializar variables se emplea el pseudo-operador DW (define word o palabra).

Cuando DOS coloca el programa del usuario en memoria para su ejecución, guarda automáticamente, en el registro del segmento de datos DS, la dirección del segmento prefijo del programa PSP. Esta característica será importante cuando se estudie la manera de regresar el control a DOS una vez terminado el programa. El primer programa de ejemplo no cuenta con esta instrucción y su ejecución traerá como consecuencia que el sistema se pierda, sin embargo en este programa al finalizar regresa el control a DOS.

El programador debe asegurarse de que al término del programa el registro CS contenga la dirección del PSP (Program Segment Prefix) y que IP sea igual a 0000H.

Para tener acceso al segmento de datos (DS) del programa del usuario, debe cargarse en DS la dirección de inicio del segmento de datos de éste. Las siguientes instrucciones

MOV AX, SEG DATAMOV DS, AX

ubicadas en el segmento de código, se utilizan con este fin. El operador SEG determina la dirección del segmento DATA (la dirección de inicio del segmento de datos). La primera instrucción coloca esta dirección en AX. La segunda, coloca esta dirección en DS. Si estas instrucciones no se ejecutan entonces DS continuará con el contenido de la dirección de PSP y, por lo tanto todas las referencias que se hagan a las variables contenidas en el segmento de datos del usuario no serán válidas ya que sus direcciones serán incorrectas. Antes de hacer cualquier cosa, sin embargo, la dirección del PSP (segmento y desplazamiento) se deban guardar en el stack para que cuando el procedimiento STAR lleve a cabo un regreso FAR, al ejecutar la instrucción RET, pueda obtener del stack la dirección del PSP.

Los procedimientos son muy útiles para organizar y reducir el código de un programa. El pseudo-operador que permite definir un procedimiento es PROC es de tipo de datos y tiene la siguiente forma:

Nombre del procedimiento PROC NEAR(o

Nombre del procedimiento PROC FAR)…

RET…

nombre del procedimiento ENDP

El atributo NEAR es opcional. Por cada salida en el procedimiento, debe incluirse una instrucción de retorno RET. El procedimiento debe finalizar con ENDP. En el ejemplo el procedimiento START contiene todas las proposiciones ejecutables en el programa y es un procedimiento FAR, debido a que cuando termine cederá el control a DOS.

45

Page 50: Temario lenguaje ensamblador

Cuando el procedimiento comienza su ejecución, coloca en el stack la dirección contenida en el registro DS, la cual corresponde a la dirección del segmento que contiene el PSP, utilizando para ello la instrucción PUSH. Después se coloca en el registro AX el valor de 0000H y se coloca también en el stack.

PUSH DSSUB AX, AXPUSH AX

La secuencia anterior de instrucciones guarda en el stack la dirección necesaria para definir la dirección de inicio del área del PSP. Después de finalizado el procedimiento, la instrucción RET se encarga de restablecer esta dirección, colocando automáticamente la primera palabra en el stack (0000h) en IP y la segunda palabra en el stack (dirección contenida en DS al inicio del programa) en el registro CS.

46

Page 51: Temario lenguaje ensamblador

LOS PROCEDIMIENTOS Y EL ENSAMBLADOR

Ahora que hemos aprendido a ensamblar, escribiremos más programas en lenguaje

ensamblador. Para esto regresaremos al tema de los procedimientos. Veremos cómo escribir

procedimientos fácilmente con la ayuda de nuestro ensamblador. Empezaremos con dos

procedimientos para imprimir un byte en hexadecimal. Usaremos más directivas como MODEL,

CODE y END, pero las definiremos más adelante cuando aprendamos un poco más sobre los

segmentos.

LOS PROCEDIMIENTOS DEL ENSAMBLADOR

Cuando aprendimos sobre los procedimientos en el Debug, dejamos espacio de direcciones de

memoria grande entre el programa principal y sus procedimientos, para que tuviéramos sitio para los

cambios sin tener que preocuparnos por nuestra superposición (sobrescribir) del programa principal y

el procedimiento. Pero ahora contamos con el ensamblador, y desde que hace todo el trabajo de asignar

las direcciones las instrucciones, nosotros ya no necesitamos un espacio entre los procedimientos. Cada

vez que hacemos un cambio en el ensamblador, nosotros ensamblamos el programa de nuevo.

En las clases anteriores construimos en el DEBUG un programa con una llamada (CALL). El

programa no hizo nada más que la impresión de las letras de la A a la J, y se parecía a esto:

0100 MOV DL, 41h0102 MOV CX, 000A0105 CALL 02000108 INC DL010A LOOP 0105010C INT 20

0200 MOV AH, 020202 INT 210204 RET

Convirtamos este código en un programa para el ensamblador. Será difícil leerlo sin no le

agregamos tabuladores y comentarios, por lo que se lo agregaremos para hacer nuestro programa más

legible:

47

Page 52: Temario lenguaje ensamblador

.MODEL SMALL

.CODEIMPAJ PROC

MOV DL, ‘A’ ;Inicializa con el carácter AMOV CX, 10 ;Imprime 10 caracteres, inicializando con A

SALTO: CALL IMP_CAR ;imprime caracterINC DL ;mueve el siguiente carácter del alfabetoLOOP SALTO ;continua con los 10 caracteresMOV AH, 4CH ;Retorna el control del DOSINT 21H

IMPAJ ENDP

IMP_CAR PROCMOV AH, 2 ;Función para imprimir un caracterINT 21H ;imprime el carácter contenido en el registro DLRET ;Regresa de este procedimiento

IMP_CAR ENDPEND IMPAJ

Hay dos nuevas directivas: PROC, y ENDP. PROC y ENDP son las directivas que definen los

procedimientos. Como puede ver, el programa principal y el procedimiento en 200h son encerrados por

el par de directivas PROC y ENDP.

PROC define el principio de un procedimiento; ENDP define el fin. La etiqueta delante de cada

uno, es el nombre que nosotros damos al procedimiento que ellos definen. Así, en el procedimiento

principal, IMPAJ, podemos reemplazar la instrucción CALL 200H con la llamada mas legible de

CALL IMP_CAR. Simplemente inserte el nombre del procedimiento y el ensamblador asigna las

direcciones.

Desde que nosotros tenemos dos procedimientos, necesitamos decirle al ensamblador cuál debe

usar como procedimiento principal. La directiva END da indicación de este detalle. Escribiendo END

IMPAJ, le dijimos al ensamblador que IMPAJ es el procedimiento principal. Mas adelante veremos que

el procedimiento principal puede estar en cualquier parte. Ejecute una prueba a IMPAJ.

Nota: Si encuentra algún mensaje del error que no reconozca, verifique que ha tecleado correctamente

en el programa. Si eso falla, verifique la lista algunos errores comunes.

Cuando esté satisfecho del resultado del programa, use el Debug para desensamblar el programa

y vea cómo el ensamblador encaja los dos procedimientos juntos. La llamada para poder leer un

48

Page 53: Temario lenguaje ensamblador

archivo particular en el DEBUG teclee su nombre con. Por ejemplo, teclear DEBUG IMPAJ.EXE, y

una vez hecho, veremos:

c:\MASM611\BIN>DEBUG IMPAJ.EXE-U0D73:0000 B241 MOV DL,410D73:0002 B90A00 MOV CX,000A0D73:0005 E80800 CALL 00100D73:0008 FEC2 INC DL0D73:000A E2F9 LOOP 00050D73:000C B44C MOV AH,4C0D73:000E CD21 INT 210D73:0010 B402 MOV AH,020D73:0012 CD21 INT 210D73:0014 C3 RET-

Nuestro programa es bueno y cómodo, sin el hueco entre los dos procedimientos.

LOS PROCEDIMIENTOS DE SALIDA HEXADECIMAL

Hemos visto los procedimientos de salida hexadecimal dos veces antes: una vez cuando

aprendimos a imprimir un número en hexadecimal, y de nuevo cuando vimos cómo simplificar el

programa, usando un procedimiento para imprimir un dígito hexadecimal. Ahora vamos a agregar otro

procedimiento para imprimir un carácter. ¿Por qué? Bueno, simplemente lo llamaremos por previsión.

Para usar un procedimiento central escribiremos un carácter en la pantalla, nosotros podemos

cambiar la manera que este procedimiento escribe los caracteres sin afectar el resto del programa.

Nosotros cambiaremos varios tiempos.

Escriba el siguiente programa nombrando al archivo PIMHEX.ASM (Prueba de impresión en

hexadecimal):

.MODEL SMALL

.CODE

.STACKPRUEBA_IMP_HEX PROC

MOV DL,3Fh ;Prueba con 3FhCALL IMP_HEXMOV AH, 4CHINT 21h ;Retorna al DOS

PRUEBA_IMP_HEX ENDP

PUBLIC IMP_HEX;------------------------------------------------------------------------------------------------------------------;; Este procedimiento convierte el byte del registro DL a hexadecimal y escribe ;

49

Page 54: Temario lenguaje ensamblador

; dos dígitos hexadecimales en la posición actual del cursor ;; La entrada: DL el byte lo convertirá en hexadecimal ;; Use: IMP_DIGIT_HEX ;;------------------------------------------------------------------------------------------------------------------;IMP_HEX PROC ;Punto de entrada

PUSH CX ;Salva los registros usados en este procedimientoPUSH DXMOV DH, DL ;Hace una copia del byteMOV CX,4 ;Da el nibble superior en DLSHR DL, CLCALL IMP_DIGIT_HEX;Despliega el primer dígito hexadecima MOV DL, DH ;Da un nibble inferior dentro de DLAND DL, 0Fh ;Remueve el nibble superiorCALL IMP_DIGIT_HEX;Despliega el segundo dígito hexadecimalPOP DXPOP CXRET

IMP_HEX ENDP

PUBLIC IMP_DIGIT_HEX;------------------------------------------------------------------------------------------------------------------;; Este procedimiento convierte los 4 bits bajos de DL a un dígito hexadecimal y lo ;; escribe en la pantalla ;; La entrada: DL Los 4 bits bajos contiene un número para ser impreso en hexadecimal ;; Uses: IMP_CAR ;;------------------------------------------------------------------------------------------------------------------;IMP_DIGIT_HEX PROC

PUSH DX ;Salva los registros usadosCMP DL,10 ;Este nibble es <10?JAE LET_HEX ;No, convierte a letraADD DL,“0” ;Si, convierte a digitoJMP Short IMP_DIGIT ;Ahora escribe este carácter

LET_HEX: ADD DL, “A”-10 ;Convierte a letra hexadecimalIMP_DIGIT: CALL IMP_CAR ;Despliega la letra en la pantalla

POP DX ;Restaura los valores antiguos de DXRET

IMP_DIGIT_HEX ENDP

PUBLIC IMP_CAR;------------------------------------------------------------------------------------------------------------------;; Este procedimiento imprime un carácter en la pantalla usando la función de llamada del DOS ;; La entrada: DL Byte a imprimir en la pantalla. ;;------------------------------------------------------------------------------------------------------------------;IMP_CAR PROC

PUSH AXMOV AH,2 ;Función para imprimir un caracterINT 21h ;Saca el carácter que se encuentra en el registro DLPOP AX ;Restaura los valores antiguos de AXRET ;Y regresa

IMP_CAR ENDPEND PRUEBA_IMP_HEX

La función del DOS para imprimir los carácteres trata algunos carácteres especialmente. Por

ejemplo, usando la función del DOS a la salida 07 resultados en un pitido, sin imprimir el carácter para

07 que son un diamante pequeño. Nosotros veremos más adelante una nueva versión de IMP_CAR que

50

Page 55: Temario lenguaje ensamblador

imprimirá un diamante, dónde aprenderemos sobre las rutinas de ROM BIOS dentro de su PC. Por

ahora, sin embargo, usaremos la función del DOS para imprimir los carácteres.

La nueva directiva PUBLIC está aquí para el uso futuro: Nosotros lo usaremos, cuando

aprendemos sobre el diseño modular. PUBLIC simplemente le dice al ensamblador que genere un poco

más de información para el LINK. El LINK nos permite juntar partes separadas de nuestro programa,

ensamblándolos desde archivos fuente diferentes, uniéndolos en un solo programa. Y el PUBLIC

indica al ensamblador que el procedimiento nombrado después de la directiva PUBLIC debe hacerse

público o disponible a los procedimientos de otros archivos.

Ahora, PIMHEX contiene los tres procedimientos para escribir un byte como un número

hexadecimal, y un programa principal corto para probar estos procedimientos. Nosotros estaremos

agregando muchos procedimientos al archivo cuando desarrollamos Dskpatch, y al final,

PIMHEX.ASM se llenará de muchos procedimientos de uso general.

El procedimiento PRUEBA_IMP_HEX que hemos incluido hace lo que dice: Está aquí para

probar la impresión de un hexadecimal que, a su vez, usa IMP_HEX_DIGIT e IMP_CAR. En cuanto

nosotros hayamos verificado que estos tres procedimientos son correctos, nosotros borraremos

PRUEBA_IMP_HEX de PIMHEX.ASM.

Cree la versión del COM de Video_io, y use Depure para probar completamente ESCRIBA--

IEX. Cambie los 3Fh al lOlh de situación de memoria a cada uno de las condiciones del límite que

nosotros probamos en Capítulo 5, entonces use G para ejecutar TEST_WRITE--IEX.

Usaremos muchos programas de prueba simple para probar los nuevos procedimientos que

hemos escrito. De esta manera, podremos construir un programa pieza por pieza, en lugar de intenta

construir y depurar solo uno. Este método incremental es muy más rápido y fácil, desde que podemos

confinar los errores a sólo el nuevo código.

LOS PRINCIPIOS DE DISEÑO MODULAR

Note el encabezado de cada procedimiento en PIMHEX que hemos incluido, este es un bloque

de comentarios que describen la función de cada procedimiento brevemente. Es importante, estos

comentarios ya que dicen qué registros del procedimiento usa para pasar la información de un lado a

51

Page 56: Temario lenguaje ensamblador

otro, así como qué otros procedimientos usa. Como característica de nuestro diseño modular, el bloque

del comentario nos permite usar cualquier procedimiento mirando la descripción. No hay necesidad de

entender cómo el procedimiento hace su trabajo. Esto también lo hace bastante fácil de reescribir un

procedimiento sin tener que reescribir cualquiera de los procedimientos que lo llaman.

También hemos usado las instrucciones PUSH y POP para guardar y restaurar cualquier registro

que nosotros usemos dentro de cada procedimiento. Nosotros haremos esto para cada procedimiento

que escribimos, salvo nuestros procedimientos de prueba. Este diseño, también, es la parte del estilo

modular que nosotros usaremos.

Cada llamada guardaremos y restauraremos cualquier registro usado para que no tengamos que

preocuparnos por las interacciones complejas entre procedimientos que intentan luchar sobre el número

pequeño de registros en los microprocesadores. Cada procedimiento es libre usar tantos registros como

desee, siempre y cuando los restaura antes de la instrucción de RET. Es un precio pequeño para pagar

por la simplicidad agregada. Además, cuando guardamos y restauramos los registros, la tarea de

reescribir los procedimientos estaría mentalmente rasgada. Seguramente perdería mucho pelo en el

proceso.

Nosotros también intentamos usar muchos procedimientos pequeños, en lugar de uno grande.

Esto, también, hace nuestra tarea de programación más simple, aunque a veces escribiremos

procedimientos largos cuando el diseño se complique particularmente.

Estas ideas y métodos se usarán a partir de ahora. En la siguiente clase, por ejemplo,

agregaremos otro procedimiento a PIMHEX: un procedimiento para tomar una palabra del registro DX

e imprimir el número decimal en la pantalla.

UN ESQUELETO DEL PROGRAMA

Como hemos visto, el ensamblador impone una cierta cantidad de encabezados en cualquier

programa que escribamos. En otros términos, nosotros necesitamos escribir unas directivas que digan

los elementos esenciales al ensamblador. Para referencia futura, aquí está el mínimo absoluto que

necesitará para escribir programas:

.MODEL SMALL

.CODE

.STACK

52

Page 57: Temario lenguaje ensamblador

Algun_Procedimiento PROC

MOV AH, 4CHINT 21h

Algun_Procedimiento ENDEEND Algun_Procedimiento

Nosotros agregaremos nuevo directivas a este esqueleto del programa más adelante, pero por el

momento puede usar, lo aquí mostrado, como punto de partida para los nuevos programas que

escribirá.

53

Page 58: Temario lenguaje ensamblador

IMPRIMIENDO EN DECIMAL

Prometimos escribir un procedimiento que tome una palabra (word) y lo imprima en notación

decimal. IMP_DECIMAL usa algunos trucos nuevos -la manera de guardar aquí un byte, en unos

microsegundos allí. Quizás el truco pareciera no merecer la pena de ver el esfuerzo. Pero si usted los

memoriza, encontrará que puede usarlos para acortar y acelerar los programas. A través de los trucos,

aprenderemos también aproximadamente dos nuevos tipos de operaciones lógicas adicionales a la

instrucción AND. Primero, repasemos el proceso por convertir una palabra a dígitos decimales.

LLAMANDO A LA CONVERSIÓN

La División es la clave para convertir una palabra en dígitos decimales. La llamada a la

instrucción DIV calcula la respuesta del entero y su resto. Así que, calculando 12345/10 devuelve 1234

como la respuesta del entero, y 5 como el resto. En este ejemplo, 5 es simplemente el dígito más a la

derecha. Y si nosotros dividimos de nuevo por 10, obtendremos el siguiente dígito a la izquierda. La

división repetida por 10 saca los dígitos de derecha a izquierda, cada vez poniéndolos en el resto.

Claro, los dígitos salen en orden inverso, pero programando el lenguaje ensamblador, nosotros

tenemos que solucionarlo. ¿Recuerda la pila? Simplemente está como una pila de bandejas del

almuerzo: El primero en salir de la cima es la última que entró. Si nosotros sustituimos los dígitos

como las bandejas y ponemos los dígitos uno encima del otro cuando se obtenga cada resto cuando los

54

Page 59: Temario lenguaje ensamblador

saquemos de la pila obtendremos el número correcto. Nosotros podemos iniciar con los dígitos en el

orden correcto.

El dígito de la cima es el primer dígito en nuestro número, y los otros dígitos estarán debajo de

él. Así, si nosotros empujamos a la pila los restos cuando los calculamos y los imprimimos cuando los

saquemos de la pila, los dígitos estarán en el orden correcto. El siguiente programa es el procedimiento

completo para imprimir un número en la notación decimal. Como habrá notado, hay trucos ocultos en

este procedimiento. Nosotros pronto conseguiremos bastante de ellos, pero primero probemos

IMP_DEC para ver si funciona antes de que nos preocupemos sobre cómo funciona.

Agregue IMP_DEC en PIMHEX.ASM y renombremos este como VIDEO_IO.ASM, junto con

el resto de los procedimientos por escribir un byte en el hexadecimal. Asegúrese que el lugar de

IMP_DEC esté después de PRUEBA_IMP_HEX el cual estaremos reemplazando con

PRUEBA_IMP_DEC. Guarde el trabajo, IMP_DEC usa IMP_DIGT_HEX para convertir un nibble

(cuatro bits) en un dígito.

IMP_DEC;------------------------------------------------------------------------------------------------------------------;; Este procedimiento imprime 16-bit, un número sin signo en notación decimal ;; La Entrada: DX N: 16-bit, número sin signo. ;; Usa: IMP_DIGIT_HEX ;;------------------------------------------------------------------------------------------------------------------;IMP_DEC PROC NEAR

PUSH AX ;Guarda los registros usados aquíPUSH CXPUSH DXPUSH SIMOV AX, DXMOV SI, 10 ;para dividir por 10 usando SIXOR CX, CX ;cuenta los dígitos localizados en la pila

NO_CERO: XOR DX, DX ; Poner la palabra superior de N a 0DIV SI ;Calcula N/10 y (N mod 10)PUSH DX ;Empuja un dígito dentro de la pila INC CX ;Un dígito mas agregadoOR AX, AX ;N = 0 todavía?JNE NO_CERO ;Nope, continue

CICLO_IMP_DIGIT: POP DX ;Da el dígito en orden inversoCALL IMP_DIGIT_HEXLOOP CICLO_IMP_DIGIT

END_DECIMAL: POP SIPOP DXPOP CXPOP AXRET

IMP_DEC ENDP

55

Page 60: Temario lenguaje ensamblador

Observe que hemos incluido un nuevo registro, el registro SI (el Índice Fuente). Luego veremos

porqué se le ha dado ese nombre, y nos encontraremos a su hermano, el registro DI, o Índice Destino.

Ambos registros tienen usos especiales, pero también pueden usarse como fueran registros de uso

general. Desde que IMP_DEC necesite cuatro registros de uso general, nosotros usamos SI, aunque

nosotros pudiéramos usar BX, simplemente para mostrar que este SI (y DI) puede servir registros de

uso general si fuera necesario.

Antes de que probemos nuestro nuevo procedimiento, necesitamos hacer otros dos cambios a

VIDEO_IO.ASM. Primero, debemos borrar el PRUEBA_IMP_HEX del procedimiento y debemos

insertar este procedimiento de prueba en su lugar (o mejor aun mantenerlo como comentario mediante

la cláusula COMMENT):

PRUEBA_IMP_DEC PROCMOV DX,12345CALL IMP_DECMOV AH, 4CHINT 21h ;Regresa el control al DOS

PRUEBA_IMP_DEC ENDP

El procedimiento prueba IMP_DEC con el número 12345 (qué el ensamblador convierte a una

palabra 3039h).

Segundo, necesitamos cambiar la instrucción END al final de VIDEO_IO.ASM a leer

PRUEBA_IMP_DEC a END PRUEBA_IMP_DEC, porque el es ahora nuestro procedimiento

principal.

Haga estos cambios y déle una vuelta rápida a VIDEO_IO. Conviértalo a la versión .EXE y vea

si funciona. Si no lo hace, verifique su archivo fuente para los errores (y mire los errores comunes). Si

quiere aventurarse, intente encontrar su error con el Debug. Después de todos, para eso es el Debug.

ALGUNOS TRUCOS

El primero es una instrucción eficaz para establecer un registro para poner a cero. No es mucho

más eficaz que MOV AX, 0, y quizás no merece la pena el esfuerzo, pero es la clase de truco que

encontrará en las personas que lo usa, aquí. La instrucción:

OR AX, AX

56

Page 61: Temario lenguaje ensamblador

poner a cero los conjuntos el registro AX. ¿Cómo? Para entender, necesitamos aprender sobre la

operación lógica llamada OR Exclusivo, con el nombre de XOR.

El OR exclusivo es similar a un OR (qué veremos luego), pero el resultado de la arregla XOR

es:

XOR 0 10 0 11 1 0

es verdadero si sólo uno de sus bits es verdadero, no son verdaderos si ambos son verdaderos o

falsos. Así, si nosotros hacemos a sí mismo un OR exclusivo al mismo número, conseguimos un cero:

1 0 1 1 0 1 0 1XOR 1 0 1 1 0 1 0 1

0 0 0 0 0 0 0 0

Éste es el truco. Nosotros no encontraremos otros usos para la instrucción de XOR más

adelante, pero este uso es de bastante interés.

Por otro lado, usted encontrará a muchas personas que usan otro truco rápido para establecer un

registro a cero. En lugar de usar la instrucción XOR, nosotros podríamos usar:

SUB AX, AX

para establecer el registro del AX a cero.

Ahora para el otro truco. Es casi tan desviado como nuestro esquema XOR de borre un registro,

y usar al primo del OR Exclusivo -la función OR.

Verifiquemos el registro AX para ver si es cero. Para hacer esto, usaremos la instrucción CMP

AX, 0. Pero no usaremos un truco más bien usaremos una acción más eficaz. Así que, escribamos OR

AX, AX y seguimos esta instrucción la bifurcación condicional JNE (Salta si no es igual). También

podríamos usar el JNZ (Salta si no es cero).

La instrucción OR, es similar a una ecuación matemática, establece banderas, incluyendo la

bandera del cero. AND y OR son parecidos a un concepto lógico. Pero aquí, un resultado OR es verdad

si uno de los bits es verdad:

OR 0 1

57

Page 62: Temario lenguaje ensamblador

0 0 11 1 1

Si tomamos un número y hacemos un OR a sí mismo, volvemos el número original de nuevo:

1 0 1 1 0 1 0 1OR 1 0 1 1 0 1 0 1

1 0 1 1 0 1 0 1

La instrucción OR también es útil para establecer simplemente un bit en un byte. Por ejemplo:

1 0 1 1 0 1 0 1OR 1 0 1 1 1 1 0 1

1 0 1 1 1 1 0 1

LOS FUNCIONAMIENTOS INTERNOS

Para ver cómo IMP_DEC realiza su tarea, estudie el listado; porque no se cubrirán más detalles

aquí. Nosotros necesitamos señalar una cosa mas.

Primero, el registro CX se usa para contar cuántos dígitos hemos empujado hacia la pila, para

que nosotros sepamos cuántos debemos sacar. El registro CX es una opción particularmente

conveniente, porque nosotros podemos construir un bucle (ciclo) con la instrucción del LOOP y

podemos usar el registro CX para repita la cuenta para almacenar. Nuestra opción hace el ciclo de

dígito de salida (CICLO_IMP_DIGIT) sea trivial, porque la instrucción LOOP usa el registro CX

directamente. Usaremos CX muy a menudo cuando tengamos que contar un almacenaje.

Luego, tenga cuidado de verificar el límite condicional. La condición del límite con el 0 no es

un problema, como podrás verificar. La otra condición del límite es 65535, o FFFFh con el que se

puede verificar fácilmente con el Debug. Carga VIDEO_IO.EXE en el Debug tecleando DEBUG

VIDEO_IO.EXE y cambie los 12345 (3039h) por 101h a 65535 (FFFFh). IMP_DEC funciona con

números sin signo. Vea si puede escribir una versión para escribir números con signo.

58

Page 63: Temario lenguaje ensamblador

Habrá notado un punto importante aquí, mientras teniendo que hacer con los 8088, no nuestro

programa. Depure los trabajos principalmente con los bytes (por lo menos el comando de E hace) pero

nosotros queremos cambiar una palabra. Nosotros debemos tener el cuidado, desde las 8088 tiendas los

bytes en un orden diferente. Aquí es un unassemble para la instrucción de MOV:

Usted puede decir de la parte de BA3930 de esta pantalla que el byte a las 101h es 39h, y el uno a las

102h es 30h (BA es la instrucción de MOV). Los dos bytes son los dos bytes de 3039h, pero

aparentemente en orden inverso. ¿Confundiendo? Realmente, el orden es lógico, después de una

explicación corta.

Una palabra consiste en dos partes, el más bajo byte y el byte superior. El más bajo byte es el byte

significante (39h - en 3039h), mientras el byte superior es la otra parte (30h). tiene sentido, entonces,

para poner el más bajo byte a la más bajo dirección en la memoria. (Muchas otras arquitecturas de

computación, como la Motorola,

68000 en el Apple Macintosh, realmente invierta estos dos bytes, y éste puede ser un bit que confunde

si usted está escribiendo los programas en varios tipos diferentes de computadoras. )

Pruebe los números diferentes para el palabra arranque a las 101h, y usted verá cómo este

almacenamiento trabaja. Use TEST_WRITE-DECIMAL ver si usted le hiciera derecho, o unassemble

la primera instrucción.

59

Page 64: Temario lenguaje ensamblador

Segmentos y desplazamientosPara direccionar la memoria, las computadoras utilizan 20 bits, sin embargo la CPU procesa

palabras de 16 bits en sus registros de direcciones.

Las direcciones están divididas en dos componentes: segmentos y desplazamientos. Un segmento es un área continua de memoria que puede tener una longitud de 64Kb. La dirección de inicio de un segmento define su localización y puede estar contenida en uno de los cuatro registros de segmento (CS, DS, SS, ES)

CS: Contiene la dirección de inicio del segmento donde residen las instrucciones del programa en ejecución.

DS: Retiene (señala hacia) la dirección donde inicia el segmento en el que se definen las variablesSS: Señala hacia el segmento donde se encuentra el Stack (pila). Esta es una estructura de datos de

memoria donde pueden colocarse byte o palabras una después de otra y que, posteriormente se puede recuperar (PUSH, POP) (PUSH = mete o empuja, POP = jala o saca)

ES: Apunta a un segmento definido por el usuario y que regularmente contiene datos adicionales.

En el macroensamblador se utilizan palabras de 16 bits para definir la ubicación de los segmentos. Estas localidades quedan fijadas por el programa LINK antes de su ejecución.

Dado que los segmentos pueden ser de hasta 64Kb, se necesita especificar otro parámetro (una dirección de 16 bits) para accesar las localidades de memoria dentro del segmento.

La dirección completa (fulladdress) es el espacio de 1 Mb que se obtiene combinando las direcciones del segmento con el desplazamiento. Para esto primero se hace un corrimiento de la dirección del segmento 4 bits a la izquierda (introduciendo ceros a la derecha )y después se suma al resultado la dirección del desplazamiento con lo que se obtiene una dirección de 20 bits.

Ejemplo0001 0000 1010 1111 (0000) (dirección del segmento)

1111 0000 1111 1111 (dirección del desplazamiento)---------------------------------------------0001 1111 1011 1110 1111 (dirección de 20 bits)

En la memoria se almacena primero el byte menos significativo y luego el más significativo.

60

3

2

1

0

Dirección del segmento +

desplazamiento Segmentos de 64 Kb

Palabra de 16 Kb

Page 65: Temario lenguaje ensamblador

En hexadecimal0AFF

Mapa de la memoria RAMLa presente tabla muestra a grandes razgos la manera en que se encuentra distribuida la

memoria en la PC para una IBM PC y XT.Dirección de 20 bits

Inicio Fin Descripción00000 0FFFF

(64K)Esta área contiene los primeros 64K de memoria de acceso aleatorio (RAM), en los primeros sistemas fabricados por IBM, esta cantidad es la máxima que puede ser instalada sobre la tarjeta del sistema

10000 3FFFF (256K)

192K adicionales de memoria puede ocupar esta región. En versiones más nuevas de la PC, esta memoria puede estar también instalada sobre la tarjeta del sistema

40000 9FFFF (640K)

En esta región el usuario puede instalar hasta 384K de memoria RAM, adicionales lo que permite tener un total de 640K de memoria RAM para el usuario en versiones PC

A0000 A3FFF (656K)

Área de 16K reservada por IBM

A4000 BFFFF (768K)

Este espacio es un buffer de 112K para gráficas y visualización de vídeo

C0000 C7FFF (800K)

Área de 32K para expansión de memoria ROM

C8000 C9FFF (808K)

8K de memoria ROM que contiene el programa controlador del disco duro

CA000 F3FFF (976K)

Área de 168K reservada para el ROM de diversas tarjetas adaptadoras para soporte de aplicaciones

F4000 F5FFF (984K)

Área de 8K reservada para memoria ROM del usuario se relaciona con un socket de repuesto

F6000 FDFFF (1016K)

Espacio de 32K para cassette BASIC

FE000 FFFFF (1024K)

Área de 8K reservada para el sistema entrada/salida (I/O) de BASIC

61

FF

0A

0100

0101

Pala

bra

de

16

Kb

Palabra de 16 Kb

0102

Pala

bra

de

16

Kb

Palabra de 16 Kb

0103

Pala

bra

de

16

Kb

Palabra de 16 Kb

0104

Pala

bra

de

16

Kb

Palabra de 16 Kb

Notación0AFF:0100

Segmento:desplazamiento

Por lo tanto estamos en la dirección 0100 del segmento 0AFF

Page 66: Temario lenguaje ensamblador

SEGMENTOS

En las clases anteriores, nos encontramos varias veces con la directiva que se trata a los

segmentos. Ahora vamos a prestarle atención a los segmentos y cómo el microprocesador maneja una

dirección completa en un megabyte (1,048,576 bytes) de memoria. Para esto, empezaremos a entender

porqué los segmentos necesitan su propia directivas en el ensamblador, más adelante empezaremos a

usar los diferentes segmentos (como los lejos).

Empecemos por recordar cómo el microprocesador construye direcciones de 20-bit necesaria

para un megabyte completo de memoria.

SECCIONANDO LA MEMORIA DEL MICROPROCESADOR

Los segmentos son la única parte de los microprocesadores que no hemos cubierto todavía, y

ellos son, quizás, la parte más confusa de los microprocesadores para la mayoría de las personas.

El problema, en este caso, inicia con los direccionamientos mayores de 64K de memoria-el

límite con una palabra, la cual el 65535 es el número más grande que una sola palabra puede sostener.

A partir de los microprocesadores 8088 de Intel, se usaron los segmentos y registros del segmento para

“solucionar” este problema.

Hasta ahora, no hemos tenido relación con este problema. Nosotros hemos estado usando el

registro IP para tener la dirección de la próxima instrucción que el microprocesador ejecutará. Nosotros

hemos dicho que dirección real se forma de los registros CS y del IP. Recordemos.

Aunque la dirección completa se forma de dos registros, los microprocesadores no forman un

número de dos-palabras para la dirección. Si tomáramos CS:IP como un número de 32-bits (dos

números de 16-bits lado a lado), los microprocesadores podrían direccionar aproximadamente cuatro

billones de bytes –superior a un millón de bytes para direccionarse. El registro CS proporcina la

dirección de inicio del segmento de código dónde un segmento es de 64K de memoria.

62

Page 67: Temario lenguaje ensamblador

Como podrás observar en la siguiente Figura, los microprocesadores dividen la memoria en

muchos segmentos superpuestos; con un nuevo segmento de inicio cada 16 bytes.

El primer segmento (segmento 0) inicia en la memoria con posición; el segundo (segmento 1)

inicia en 10h (16); el tercero inicia en 20h (32), y así sucesivamente.

La dirección real es sólo CS * 16 + el IP. Por ejemplo, si el registro de CS contiene 3FA8 y el

IP contiene D017, la dirección absoluta es:CS*16

:

0 0 1 1 1 1 1 1 1 0 1 0 1 0 0 0 0 0 0 0

+ IP: 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1

0 1 0 0 1 1 0 0 1 0 1 0 1 0 0 1 0 1 1 1

Nosotros multiplicamos por 16 para mover CS cuatro bits a la izquierda e inyectando los ceros a

la derecha.

CS*16: 3 F A 8

+ IP: D 0 1 7

4 C A 9 7

63

Page 68: Temario lenguaje ensamblador

Ahora, esto puede parecer una forma extraña de direccional más de 64K de memoria, pero

funcionan. Pronto, veremos lo bien que trabaja.

Los microprocesadores tienen cuatro registros de segmento: CS (Segmento de Código), DS

(Segmento de Datos), SS (Segmento de Pila), y ES (Segmento Extra). El registro CS lo hemos estado

viendo usarse en el microprocesador donde la próxima instrucción se almacena en el segmento. De la

misma forma, DS es el segmento donde el microprocesador señala los datos, y SS es donde el

microprocesador localiza a la pila.

Antes de continuar, observemos un programa corto, diferente a cualquiera que hemos visto

anteriormente, eso usan dos segmentos diferentes. Capture este programa en el archivo llamado

P_SEG.ASM:

DOSSEG.MODEL SMALL .STACK ;Localiza 1K en la pila..CODEP_SEGMENT PROC

MOV AH,4Ch ;Función de pregunta para la salida-al-dosINT 21h ;Retorno a DOS

P_SEGMENT ENDPEND P_SEGMENT

Ensamble y vincule P_SEG, pero no genere un archivo .COM. El resultado será P_SEG.EXE el

cual es ligeramente diferente a un archivo .COM

Nota: Nosotros tenemos que usar un método diferente que INT 20h para finalizar los

archivos .EXE. Para los archivos .COM, INT 20h trabajas perfectamente bien, pero no funciona en

absoluto para los archivos .EXE porque la organización de segmentos es muy diferente, por lo que

veremos más adelante; más de estas diferencia después. De aquí en adelante usaremos la INT 21h,

función 4Ch para finalizar nuestros programas.

Cuando usemos Debug en un archivo .COM, Debug establece todo el registro de segmento al

mismo número, con el programa de inicio a un desplazamiento de 100h para el inicio de este segmento.

64

Page 69: Temario lenguaje ensamblador

Los primeros 256 bytes (100h) son usados para almacenar varias partes de información que no estamos

realmente interesados, pero que estaremos espiando la parte de esta área un poco.

Ahora, pruebe P_SEG.EXE en el Debug, para ver lo que pasa con los segmentos en un

archivo .EXE:

C:>DEBUG P_SEG.EXE_ RAX=0000 BX=0000 CX=0004 DX=0000 SP=0400 BP=0000 SI=0000 DI=0000DS=3985 ES=3985 SS=3995 CS=3995 IP=0000 NV UP DI PL NZ NA PO NC3995:0000 B44C MOV AH, 4C

Los valores de los registros SS y CS son diferentes de aquéllos de DS y ES.

LA PILA

En nuestro programa, definimos dos segmentos. El segmento STACK donde nosotros ponemos

la pila (.STACK), y el segmento de código (.CODE) que es en donde todas nuestras instrucciones se

almacenan. La directiva STACK le dice al ensamblador que crea una pila de 1024 bytes. (Nosotros

65

Page 70: Temario lenguaje ensamblador

podríamos crear una pila más grande o más pequeña poniendo un número después de .STACK. Por

ejemplo, .STACK 128 crearían una pila 128 bytes de largo.)

La dirección para la cima de la pila está dada por SS:SP. SP es el apuntador de la Pila,

semejante al IP y CS para el código, y es un desplazamiento dentro del Segmento de Pila actual.

Realmente, “cima de la pila” es un nombre equivocado, porque la pila crece de la memoria alta

hacia la memoria baja. Así, la cima de la pila realmente está en el fondo de la pila en la memoria, y se

ponen nuevas entradas a la pila progresivamente como la memoria baje. Aquí, SP es 400h que es igual

a 1024 en decimal (4*162) porque nosotros definimos una área de la pila de 1024 bytes de largo.

Nosotros no hemos puesto nada en la pila, para que la cima-de-pila está en la cima de la memoria

nosotros establecimos al lado para la pila: 400h.

Si recuerda los programas (.COM) realizados anteriormente, nosotros nunca declaramos un

segmento de pila por lo que surgen dos preguntas: ¿Porqué nosotros no teníamos que declarar un

segmento de la pila para los programas .COM? y ¿dónde estaba la pila en los programas .COM? Todos

los programas .COM fueron creados teniendo un sólo segmento, y todos los registros del segmento

(CS, DS, ES, y SS) apuntaban a este segmento. Desde que nosotros teníamos simplemente un

segmento, nosotros no necesitamos un segmento de la pila separado.

Acerca de donde estaba la pila, si usted mira la pantalla del registro (R) para WRITESTR.COM,

usted verá que la pila está en el mismo fin del segmento (SP=FFEE):

_ RAX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000DS=3995 ES 3995 SS=3995 CS=3995 IP=0100 NV UP EI PL NZ NA PO NC3995:0100 B402 MOV AH 02

El DOS siempre pone el puntero de la pila al mismo fin del segmento cuando carga un

archivo .COM en la memoria. Por esta razón, nosotros no necesitamos declarar un segmento de pila

(con . STACK) para los archivos .COM.

¿Qué pasarían si nosotros borramos la directiva .STACK director de P_SEG.ASM?

A>DEBUG P_SEG.EXE_ RAX=0000 BX=0000 CX=0004 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000

66

Page 71: Temario lenguaje ensamblador

DS=3985 ES=3985 SS=3995 CS=3995 IP=0000 NV UP EI PL NZ NA PO NC3D90:0000 B44C MOV AH ,4C

La pila está ahora en 3995:0 que es el inicio de nuestro programa (CS:0). Ésta es una mala

noticia. Nosotros no queremos a la pila en cualquier parte cercana al código de nuestro programa.

También, desde que el puntero de la pila esté en SS:0, no tiene ningún cuarto para crecer (desde que la

pila crece en la parte baja de la memoria). Por estas razones, nosotros debemos declarar un segmento

de pila en los programas .EXE.

Nota: usted debe declarar siempre un segmento de pila con .STACK en los programas .EXE.

Regresando a nuestro ejemplo de dos-segmentos, note que el Segmento de Pila (SS) es el

segmento número 3996 (esto probablemente será diferente para usted), mientras nuestro Segmento de

Código (CS) está en el segmento 3995 -uno menos que el SS, o simplemente 16 bytes bajos en la

memoria. Desde que nosotros no pusimos ningún dato en el segmento de la pila, desensamblando el

inicio a CS:0 mostrará nuestro programa (MOV AH, 4C e INT 21) seguido por cualquier cosa pasada

para estar en la memoria:

-U CS:03995:0000 B44C MOV AH,4C3995:0002 CD21 INT 213995:0004 65 DB 653995:0005 2028 AND [BX+SIJ, CH3995:0007 59 POP CX3995:0008 2F DAS3995:0009 4E DEC SI3995:000A 293F SUB [BX], DI

El Prefijo del Segmento de Programa (PSP)

Observando la pantalla del registro, puedo haber notado que los registros ES y DS contienen

3985h, 10h menos que el principio del segmento de programa 3995h. Multiplicando por 16 para

conseguir el número de bytes, nosotros podemos ver que hay 100h (o 256) bytes antes de nuestro

programa de inicio. Ésta es la misma área improvisada (SCRATCH AREA) puesta al principio de un

archivo .COM.

Nota: Esta “SCRATCH AREA” (área improvisada) se actualmente llama PSP (el Prefijo del

Segmento de Programa) y contiene la información para el uso por del DOS. En otras palabras, no debe

suponer que podrá hacer uso de esta área.

67

Page 72: Temario lenguaje ensamblador

Entre otras cosas, estos 256-bytes del PSP al inicio del programa contiene los caracteres que

tecleamos después del nombre de nuestro programa. Por ejemplo:

c:\MASM611\BIN>debug p_seg.exe Ahora veremos algunos caracteres en el vertedero de memoria-d ds:800D69:0080 3C 20 41 68 6F 72 61 20-76 65 72 65 6D 6F 73 20 < Ahora veremos0D69:0090 61 6C 67 75 6E 6F 73 20-63 61 72 61 63 74 65 72 algunos caracter0D69:00A0 65 73 20 65 6E 20 65 6C-20 76 65 72 74 65 64 65 es en el vertede0D69:00B0 72 6F 20 64 65 20 6D 65-6D 6F 72 69 61 0D 65 20 ro de memoria.e0D69:00C0 6D 65 6D 6F 72 69 61 0D-65 63 69 66 69 71 75 65 memoria.ecifique0D69:00D0 20 75 6E 61 20 64 69 72-65 63 63 69 A2 6E 20 64 una direcci.n d0D69:00E0 65 0D 20 32 2E 30 2E 0D-00 00 00 00 00 00 00 00 e. 2.0..........0D69:00F0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

El primer byte nos dice que tecleamos 3Ch (o 60) caracteres, incluso el primer espacio después

de P_SEG.EXE.

El PSP también contiene información que el DOS usa cuando terminamos la ejecución de un

programa, con las instrucciones INT 20h o INT 21h, función 4Ch,. Pero por razones que están del todo

claras, la instrucción INT 20h espera el registro CS el punto de inicio de este PSP cuando es un

programa .COM, pero no para un programa de .EXE. Ésta es una cuestión histórica. Y, en efecto, la

función de salida (INT 21h, función 4Ch) se agregó al DOS con la introducción a la versión 2.00 para

hacer más fácil la salida de los programas .EXE; la función 4Ch no espera el registro CS el punto de

inicio del PSP. Usaremos la INT 21h, función 4Ch de aquí en adelante para terminar nuestros

programas.

El código para los archivos .COM siempre deben iniciar en un desplazamiento de 100h en el

segmento de código dejando espacio para estos 256-byte del PSP al inicio. Esto es diferente al

archivo .EXE ya que este código inicia en IP = 0000, porque el segmento de código inició 100h bytes

después de iniciado del área en la memoria.

Antiguamente, la mayoría de los programas se escribían como programas .COM porque eran

más simples escribir. Pero Actualmente, la mayoría de los programas son escritos como .EXE. Por lo

que de aquí en adelante estaremos trabajando enteramente con los programas .EXE.

La Directiva DOSSEG

68

Page 73: Temario lenguaje ensamblador

Si observa de nuevo P_SEG.EXE, notará que el segmento de pila está en la memoria alta del el

segmento de código. En nuestro archivo fuente definimos la pila (.STACK) antes de cualquiera código

(.CODE). ¿Y porqué la pila está en la memoria alta que el código?

La diretiva DOSSEG al principio de nuestro programa ensamblador le dice que nosotros

queremos los segmentos de nuestro programa cargados en un orden muy específico, con el segmento de

código apareciendo de primero, y la pila de último.

Llamadas (Call’s) Cercanas (NEAR) y Lejanas (FAR)

Realicemos un vistazo a las instrucciones CALL usadas anteriormente. Específicamente,

miremos el programa corto dónde aprendimos primero sobre la instrucción CALL. Nosotros escribimos

lo siguiente (sin el procedimiento en 200h):

3985:0100 B241 MOV DL, 413985:0102 B90A00 MOV CX,000A3985:0105 E8F800 CALL 02003985:0108 E2FB LOOP 01053985:010A CD20 INT 20

Observe el código de máquina a la izquierda que la instrucción CALL, ocupa sólo tres bytes

(E8F800). El primer byte (E8h) es la instrucción CALL, y el segundo, dos bytes, forman un

desplazamiento. Los microprocesadores calculan la dirección de la rutina llamándola agregándole este

desplazamiento 00F8h a la dirección de la próxima instrucción (108h en nuestro programa). En este

caso, entonces, tenemos F8h + 108h = 200h. Precisamente lo esperado.

En efecto esta instrucción usa una sola palabra para el desplazamiento indicando que las

Llamadas (CALL) están limitadas a un solo segmento de 64K bytes de largo. ¿Así, cómo es que

69

Page 74: Temario lenguaje ensamblador

nosotros podemos escribir un programa que es más grande que 64K? Nosotros hacer uso de llamadas

FAR (lejanas) y NEAR cercanas.

Una llamada NEAR, como hemos visto, se limita a un solo segmento. En otras palabras, ellos

cambian el registro del IP sin afectar el registro de CS. Por esta razón estos son conocidos como

llamadas intrasegmentos.

Pero también podemos tener llamadas FAR que cambian los registros CS e IP. Estas llamadas

son conocidas llamadas intersegmentos porque llaman a los procedimientos en otros segmentos.

Las dos versiones de la instrucción CALL tienen dos versiones de la instrucción RET.

La Llamada NEAR, metemos una sola palabra en la pila para su dirección del retorno. La

correspondiente instrucción RET hace sacar esta palabra fuera de la pila y dentro del registro IP.

En el caso de las llamadas FAR y su retorno, una palabra no es suficiente, porque estamos

tratándonos con otro segmento. En otros palabras, necesitamos guardar una dirección de retorno de dos

palabras en la pila: una palabra para el apuntador de instrucción (IP) y otro para el segmento de código

(CS). El retorno FAR, entonces, saca dos palabras fuera de la pila -uno para el registro CS y el otro

para el IP.

¿Cómo sabe el ensamblador cuál de estas dos llamadas y retornos usar? ¿Cuándo debe usar la

llamada FAR, y cuándo debe usar la llamada NEAR? Respuesta: poniendo una directiva NEAR o un

FAR después de la directiva PROC.

Por ejemplo, mire el siguiente programa:

70

Page 75: Temario lenguaje ensamblador

PROC_UNO PROC FAR...RET

PROC_UNO ENDP

PROC_DOS PROC NEARCALL PROC_UNO...RET

PROC_DOS ENDP

Cuando el ensamblador ve la instrucción CALL PROC_UNO, busca en su tabla la definición

para PROC_UNO, la cual, en este caso, PROC_UNO PROC FAR. Esta definición le dice si el

procedimiento es un procedimiento cercano o lejano.

En el caso de un procedimiento NEAR, el ensamblador genera una llamada cercana.

Recíprocamente, genera una llamada lejana si el procedimiento que está llamando estaba definido

como un procedimiento lejano. En otras palabras, el ensamblador usa la definición del procedimiento

que está llamando para determinar el tipo de instrucción CALL que se necesita.

Para la instrucción RET, por otro lado, el ensamblador mira la definición del procedimiento que

contiene la instrucción de RET. En nuestro programa, la instrucción de RET para PROC_UNO será un

RET FAR, porque PROC_UNO se declara como un procedimiento FAR. De la misma manera, el RET

en PROC_DOS es un RET NEAR.

¿Qué pasa cuándo nosotros no ponemos una directiva NEAR o FAR después de PROC? El usa

la información de la directiva .MODEL para determinar si los procedimientos están NEAR o FAR, si

usted no declara un procedimiento explícitamente como NEAR o FAR. Nosotros estamos usando la

directiva .MODEL SMALL que le dice al ensamblador que nosotros sólo tenemos un segmento de

código, para que todos los procedimientos sean procedimientos cercanos (NEAR). Hay otras

directivas .MODEL (como el MEDIUM) esto le dice al ensamblador que haga los procedimientos

lejanos (FAR) si estos no se declaran explícitamente como cercanos (NEAR).

71

Page 76: Temario lenguaje ensamblador

Más sobre la instrucción INT

La instrucción de INT es como una instrucción CALL, pero con una diferencia menor. El

nombre INT viene de la palabra interrupción. Una interrupción es una señal externa que los

microprocesadores ejecuten un procedimiento y entonces regresar a lo que estaba haciendo antes de

que recibiera la interrupción. Una instrucción INT no interrumpe al microprocesador, pero se trata

como si lo hiciera.

Cuando los microprocesadores reciben una interrupción, necesita almacenar más información

sobre la pila que simplemente las dos palabras para la dirección de retorno. Tiene que almacenar los

valores del estado de banderas -la bandera de acarreo, la bandera del cero, y así sucesivamente. Estos

valores se almacenan en una palabra conocido como el Registro de Banderas, y el microprocesador

mete esta información en la pila antes de la dirección del retorno.

Su PC -regularmente responde a varias interrupciones diferentes. Los microprocesadores dentro

de su PC reciben una interrupción de reloj. Cada un de estas interrupciones provoca que los

microprocesadores detengan lo que estaban haciendo y ejecuta un procedimiento para contar los pulsos

del reloj.

Ahora, observe como una interrupción ocurre entre estas dos instrucciones del programa:

CMP AH,2JNE NOT_2

Supongamos AH=2, para que la bandera del cero se establecerá después de la instrucción CMP

lo que quiere decir que la instrucción de JNE no se bifurcará a NOT_2.

Ahora, imagine que el reloj interrumpe al microprocesador entre estas dos instrucciones. Eso

significa que el microprocesador detiene lo que está ejecutando para llevara cabo el procedimiento de

la interrupción antes de que verifique la bandera del cero (con la instrucción JNE). Si el

microprocesador no guarda y restaura la bandera del registro, la instrucción JNE usaría banderas

establecidos por el procedimiento de la interrupción, no de nuestra instrucción de CMP. Para prevenir

tales desastres, los microprocesadores guardan y restauran siempre el registro de banderas para las

interrupciones. Una interrupción guarda las banderas, y una instrucción IRET (Interrupt Return -

Retorno de la Interrupción) restaura las banderas al final del procedimiento de la interrupción.

72

Page 77: Temario lenguaje ensamblador

Lo mismo es verdad para una instrucción INT. Así, después de ejecutar la instrucción:

INT 21la pila del microprocesador se parecerá:

cima de la pila anterior IP (retorno de direcciones parte I)anterior CS (retorno de direcciones parte II)anterior registro de bandera

(La pila crece en la parte bajo de la memoria, para que el Registro de banderas anterior esté

realmente en la parte más alta de la memoria).

Cuando nosotros ponemos una instrucción INT en un programa, de cualquier modo, la

interrupción no es ninguna sorpresa. ¿Porqué, entonces, queremos guardar las banderas? ¿No se está

guardando las banderas útiles cuándo tenemos una interrupción externa que viene en un momento

imprevisible? Al salir, la respuesta es no. Hay una razón muy buena para guardar y restaurar las

banderas para la instrucción INT. De hecho, sin esta característica, Debug no sería posible.

Debug usos una bandera especial en el registro de bandera llamada bandera de Captura (Trap

Flag). Esta bandera pone al microprocesador en un modo especial conocido como modo del paso

simple el cual el Debug lo utiliza para analizar los programas una instrucción a la vez. Cuando la

bandera de captura es fija, el microprocesador arroja una INT 1 después de ejecutar cualquier

instrucción.

La INT 1 también borra la bandera de captura, para que el microprocesador no esté en el modo

del paso simple mientras estemos Depurando el procedimiento INT 1. Pero desde que INT 1 guarda las

banderas a la pila, arroja un IRET para devolver al programa lo que estábamos depurando restaurando

la bandera de captura. Entonces, nosotros recibiremos otra interrupción INT 1 después de la próxima

instrucción en nuestro programa. Éste es simplemente un ejemplo de cuanto es útil guardar los registros

de bandera. Pero, cuando veamos luego, esta característica de restaurar la bandera no siempre es

adecuada.

Algunos interrumpen los procedimientos desviando la restauración de los registros de bandera.

Por ejemplo, el procedimiento INT 21h en el DOS a veces cambia el registro de bandera poniendo en

cortocircuito el proceso de retorno normal. Muchos de los procedimientos de la INT 21h que leen o

73

Page 78: Temario lenguaje ensamblador

escriben el retorno de información de disco con la bandera de acarreo establecieron si había un error de

alguna clase (como ningún disco en la unidad).

Los Vectores de interrupción

¿Dónde éstas instrucciones de interrupciones consiguen las direcciones para los

procedimientos? Cada instrucción de interrupción tiene un número de interrupción, como los 21h en

INT 21h. El microprocesador encuentra procedimientos de interrupciones en una tabla de vectores de

interrupción que se localizan al final de la memoria. Por ejemplo, la dirección del dos palabra para el

procedimiento INT 21h está en las 0000:0084. Nosotros conseguimos esta dirección multiplicando el

número de la interrupción por 4 (4 * 21h = 84h), desde que necesitamos cuatro bytes, dos palabras,

para cada vector, o dirección del procedimiento.

Estos vectores son sumamente útiles para agregar las características al DOS, porque ellos nos

permiten que interceptemos las llamadas para interrumpir los procedimientos cambiando las

direcciones en la tabla del vector. Todas estas ideas y métodos deben ponerse más claros cuando vemos

más ejemplos.

74

Page 79: Temario lenguaje ensamblador

Vaciando la memoriaInstrucción groupRutinas varias

75

Page 80: Temario lenguaje ensamblador

PROGRAMACIÓN EN LENGUAJE ENSAMBLADOREl alumno programará en Lenguaje Ensamblador para trabajar a bajo nivel en la computadora y

desarrollará diversas aplicaciones.Diseño de software

Instrucciones de controlInstrucciones de saltoInstrucciones de comparación

Optimización del diseñoProgramación modularDiseño descendente

Diagramas de flujo y pseudocódigo

Enfoque a la programación estructurada

Estilo y forma

Instrucciones de uso más frecuentesInstrucciones aritméticasInstrucciones de transferenciaInstrucciones de cargaInstrucciones LoopInstrucciones de StackInstrucciones de conteoOtras instruccionesInstrucciones de corrimientoInstrucciones de rotaciónInstrucciones de almacenamientoInstrucciones de manejo de cadenasInstrucciones de conversiónInstrucciones de procedimiento y controlinstrucciones ASCIIinstrucciones de aritmética decimalInstrucciones de I/OInstrucciones diversas

Ejemplos y ejercicios de programación.

76