You are on page 1of 112

Lenguajes de Interfaz

Unidad I: Introducción al Lenguaje Ensamblador


 Importancia de la programación en lenguaje
ensamblador.
 El procesador y sus registros internos
 La memoria principal (RAM)
 El concepto de interrupciones
 Llamadas a servicios del sistema
 Modos de direccionamiento
 Proceso de ensamblado y ligado
 Desplegado de mensajes en el monitor
 Importancia de la programación en lenguaje
ensamblador.
Lenguaje ensamblador es un lenguaje de programación de
computadoras, considerado de bajo nivel, y constituye la
representación más directa del código máquina específico para
cada arquitectura de computadoras, que es legible para un
programador.

Fue usado principalmente en los inicios del desarrollo de


software, cuando aun no se contaba con los potentes lenguajes
de alto nivel. Actualmente se utiliza con frecuencia en ambientes
académicos y de investigación, especialmente cuando se
requiere la manipulación directa de hardware, se pretenden altos
rendimientos o un uso de recursos controlado y reducido.
Muchos dispositivos programables (como los
microcontroladores) aun cuentan con el lenguaje ensamblador
como la única manera de ser manipulados.
El código escrito en lenguaje ensamblador posee una cierta
dificultad de ser entendido directamente por un ser humano ya
que su estructura se acerca mas bien al lenguaje máquina, es
decir, lenguaje de bajo nivel.

El lenguaje ensamblador es difícilmente portable, es decir, un


código escrito para un microprocesador en particular necesita
ser modificado muchas veces en su totalidad para poder ser
usado en otro microprocesador.
Los programas hechos en lenguaje ensamblador son
generalmente más rápidos y consumen menos recursos del
sistema (memoria RAM y ROM). Al programar cuidadosamente
en lenguaje ensamblador se pueden crear programas que se
ejecutan más rápidamente y ocupan menos espacio que con
lenguajes de alto nivel.

Con el lenguaje ensamblador se tiene un control muy preciso de


las tareas realizadas por un microprocesador por lo que se
pueden crear segmentos de código difíciles de programar en un
lenguaje de alto nivel.
Un ensamblador (assembler en inglés) es un programa que crea
código objeto traduciendo instrucciones nemónicas de un
programa fuente escrito en lenguaje ensamblador a códigos
ejecutables e interpretando los nombres simbólicos para
direcciones de memoria y otras entidades (ensamblado).

El uso de referencias simbólicas es una característica básica del


lenguaje ensamblador, evitando tediosos cálculos y
direccionamiento manual después de cada modificación del
programa. La mayoría de los ensambladores también incluyen
facilidades para crear macros, a fin de generar series de
instrucciones cortas que se ejecutan en tiempo real, en lugar de
utilizar subrutinas.
Un programa escrito en lenguaje ensamblador consiste en una
serie de instrucciones que corresponden al flujo de órdenes
ejecutables que pueden ser cargadas en la memoria de un
sistema basado en microprocesador.

Por ejemplo, un procesador x86 puede ejecutar la siguiente


instrucción binaria como se expresa en código de máquina:

Binario: 10110000 01100001 (Hexadecimal: 0xb061)


La representación equivalente en lenguaje ensamblador es más
fácil de recordar:

MOV al, 061h

Esta instrucción significa:

Asigna el valor hexadecimal 61 (97 decimal) al registro "al".


El mnemónico "mov" es un código de operación u “opcode",
elegido por los diseñadores de la colección de instrucciones para
abreviar "move" (mover, pero en el sentido de copiar valores de
un sitio a otro). El opcode es seguido por una lista de
argumentos o parámetros, completando una instrucción de
ensamblador típica.

La transformación del lenguaje ensamblador en código máquina


la realiza un programa Ensamblador, y la traducción inversa la
puede efectuar un desensamblador.
A diferencia de los lenguajes de alto nivel, aquí hay usualmente
una correspondencia 1 a 1 entre las instrucciones simples del
ensamblador y el lenguaje de máquina.

Sin embargo, en algunos casos, un ensamblador puede proveer


"pseudo instrucciones" que se expanden en un código de
máquina más extenso a fin de proveer la funcionalidad
necesaria.

Por ejemplo, para un código máquina condicional como "si X


mayor o igual que" , un ensamblador puede utilizar una
pseudoinstrucción al grupo "haga si menor que" , y "si = 0"
sobre el resultado de la condición anterior.
Los Ensambladores más completos también proveen un rico
lenguaje de macros que se utiliza para generar código más
complejo y secuencias de datos.

El uso del ensamblador no resuelve definitivamente el problema


de cómo programar un sistema basado en microprocesador de
modo sencillo ya que para hacer un uso eficiente del mismo, hay
que conocer a fondo el microprocesador, los registros de trabajo
de que dispone, la estructura de la memoria, y muchas cosas
más referentes a su estructura básica de funcionamiento.
Cada arquitectura de microprocesadores tiene su propio
lenguaje de máquina, y en consecuencia su propio lenguaje
ensamblador ya que este se encuentra muy ligado al la
estructura del hardware para el cual se programa.

Los microprocesadores difieren en el tipo y número de


operaciones que soportan; también pueden tener diferente
cantidad de registros, y distinta representación de los tipos de
datos en memoria.

Aunque la mayoría de los microprocesadores son capaces de


cumplir esencialmente las mismas funciones, la forma en que lo
hacen difiere y los respectivos lenguajes ensamblador reflejan
tal diferencia.
Pueden existir múltiples conjuntos de mnemónicos o sintáxis de
lenguaje ensamblador para un mismo conjunto de instrucciones,
instanciados típicamente en diferentes programas en
ensamblador.

En estos casos, la alternativa más popular es la provista por los


fabricantes, y usada en los manuales del programa.
La mayoría de las aplicaciones que se ejecutan en una
computadora han sido desarrolladas empleando algún lenguaje
de alto nivel, de los cuales hay infinidad, tales como: C, C++,
Pascal, COBOL, Java, PHP, C#, Visual Basic, Perl, Python y un
largo etcétera más.

En una gran parte de estos desarrollos la elección de un lenguaje


u otro es casi una preferencia personal, puesto que en general
comparten una funcionalidad, e incluso una sintaxis, muy
similar.
En contraposición, el lenguaje ensamblador se dirige a tipos de
proyectos y situaciones muchos más concretos y específicos. En
muchos casos este lenguaje se utiliza no para desarrollar una
aplicación completa, sino para mejorar la velocidad de ciertas
partes de un programa o bien realizar operaciones que, desde el
lenguaje de alto nivel, no estén accesibles de otra forma.

En dispositivos con poca capacidad de memoria y potencia


limitada, como los antes mencionados, sí que suele recurrirse al
ensamblador como recurso principal para definir su
programación.
Asimismo la programación en lenguaje ensamblador es un
recurso, un medio más que un fin, utilizado en la mayoría de las
carreras relacionadas con la informática para acercar al
estudiante a las interioridades de los microprocesadores,
facilitándole el conocimiento de su arquitectura así como su
funcionamiento.

En este sentido la escritura de programas en ensamblador


fortalece el pensamiento analítico del programador, ya que se ve
forzado a dividir el problema a resolver en una multitud de
operaciones más sencillas, repitiendo el proceso hasta poder
expresar esas operaciones mediante los códigos de operación
con que cuente el microprocesador.
En cualquier caso, el aprendizaje de un lenguaje como el
ensamblador, tanto si en el futuro se utiliza allí donde resulte
adecuado como si no, es un proceso que le aportará una gran
cantidad de conocimientos útiles que van desde saber cómo se
almacenan ciertos tipos de datos en la memoria hasta aprender
a utilizar los servicios de la BIOS que incorpora toda
computadora.
Nos referimos a las características de los traductores de
lenguaje ensamblador a código máquina.

Una clasificación simple de los tipos de ensambladores sería


la siguiente:

 Ensambladores de una fase


 Ensambladores de dos fases
 Microensambladores
 Macroensabladores
 Ensambladores residentes
 Ensambladores cruzados
 Ensambladores de una fase: Estos ensambladores leen una
línea del programa fuente y la traducen directamente para
producir una instrucción en lenguaje máquina o la ejecuta
si se trata de una pseudoinstrucción.

 Ensambladores de dos fases: Los ensambladores de dos


fases se denominan así debido a que realizan la traducción
en dos etapas, en la primera fase, leen el programa fuente
y construyen una tabla de símbolos y en la segunda fase,
vuelven a leer el programa fuente y pueden ir traduciendo
totalmente, puesto que conocen la totalidad de los
símbolos utilizados y las posiciones que se les ha asignado
 Microensambladores: Generalmente, los procesadores
utilizados en las computadoras tienen un repertorio fijo de
instrucciones, es decir, que el intérprete de las mismas
interpretaba de igual forma un determinado código de
operación.

 Macroensambladores: Son ensambladores que permiten el


uso de macroinstrucciones (macros). Debido a su potencial,
normalmente son programas robustos que no permanecen
en memoria una vez generado el programa objeto.
 Ensambladores Residentes: Son aquellos que permanecen
en la memoria principal de la computadora y cargan, para
su ejecución, al programa objeto producido.

 Ensambladores Cruzados (Cross-Assembler): Se


denominan así los ensambladores que se utilizan en una
computadora que posee un procesador diferente al que
tendrán las computadoras donde va a ejecutarse el
programa objeto producido.
 El procesador y sus registros internos
El microprocesador está, conceptualmente, formado por tres
partes:

 Unidad de control

 Unidad aritmética lógica (ALU)

 Registros
Es el encargado de decodificar las instrucciones que le envía la
cola y enviarle las órdenes a la unidad aritmética y lógica según
una tabla que tiene almacenada en ROM llamada CROM (Control
Read Only Memory).
Es la encargada de realizar las operaciones aritméticas (suma,
suma con "arrastre", resta, resta con "préstamo" y
comparaciones) y lógicas (AND, OR, XOR y TEST). Las
operaciones pueden ser de 32 bits, 16 bits o de 8 bits.
Los registros de un microprocesador son componentes dentro
del microprocesador que nos permiten almacenar datos. Estos
datos pueden representar valores sobre los cuales se van a
realizar operaciones, resultados de las operaciones, direcciones
de localidades de memoria donde se encuentran datos e
instrucciones, direcciones de los dispositivos de entrada/salida
sobre los que deseamos escribir o leer, o los datos a escribir o
leídos de esos dispositivos.

El número, tamaño y uso de los registros de un microprocesador


así como su conjunto de instrucciones determina la eficiencia
con que el microprocesador realiza una tarea. A la descripción
del número, tamaño y uso de los registros de un
microprocesador se le conoce como el modelo de programación
del microprocesador.
Los registros de propósito general, como su nombre lo indica, se
utilizan en la forma en que lo desee el programador. Cada uno
de estos registros se puede direccionar como un registro de 32
bits (EAX, EBX, ECX, EDX), de 16 bits (AX, BX, CX, DX) o como un
registro de 8 bits (AH, AL, BH, BL, CH, CL, DH, DL). Cada uno de
los registros de 16 bits está formado por la concatenación de
dos registros de 8 bits: AX = AH:AL, BX = BH:BL, CX = CH:CL y
DX = DH:DL, donde el bit 0 del registro AH es el bit 8 del
registro AX, etc.

Adicionalmente, algunas de las instrucciones del


microprocesador emplean los registros de propósito general
para tareas específicas. Por esta razón, a cada uno se le da su
nombre (Acumulador, Base, Contador y Datos). Las funciones
primarias de los registros de propósito general incluyen:
EAX, AX, AH, AL (Acumulador): Se utiliza como registro principal
para la realización de operaciones aritméticas o lógicas.

EBX, BX, BH, BL (Base): Se utiliza para guardar la dirección base de


listas de datos en la memoria.

ECX, CX, CH, CL (Contador): Contiene el conteo para ciertas


instrucciones de corrimientos y rotaciones, de iteraciones en el
ciclo LOOP y operaciones repetidas de cadenas.

EDX, DX, DH, DL (Datos): Se utiliza como registro de datos en las


operaciones de entrada/salida de datos, además contiene la parte
más significativa de un producto después de una multiplicación;
la parte más significativa del dividendo antes de la división.
ESP (Apuntador de pila): Contiene la dirección de memoria del
tope de la pila del programa.

EBP (Apuntador de base): Contiene una dirección de memoria de


la pila de un programa.

ESI (Índice fuente): Contiene una dirección de memoria de un


elemento de un arreglo o cadena.

EDI (Índice destino): Contiene una dirección de memoria de un


elemento de una cadena o arreglo.
Los registros de segmentos contiene la dirección de memoria
donde se encuentran los selectores de segmento, que
especifican la dirección de inicio del segmento y la longitud del
mismo. El código y los datos de un programa en ejecución se
encuentran cada uno en uno o más segmentos de memoria. La
pila del programa ocupa otro segmento. Los registros de
segmento son:
CS (Código): Tiene la dirección del descriptor de segmento que indica
donde se encuentra el código de un programa..

DS (Datos): Tiene la dirección del descriptor de segmento en que se


encuentran los datos estáticos de un programa.

ES (Extra): Este registro también tiene la dirección de un descriptor de


segmento en que se encuentran los datos estáticos de un programa.

SS (Pila): Tiene la dirección lógica del descriptor de segmento en que se


encuentran la pila del sistema.

FS y GS: Son registros de segmento suplementarios disponibles en los


procesadores 80386 y superiores para permitir que los programas
accedan a dos segmentos de memoria adicional
EIP (Apuntador de instrucciones): Contiene la dirección de
memoria que contiene la siguiente instrucción que va a
ejecutar el microprocesador.

EFLAGS (Banderas): Cada bandera es un bit en el registro de


banderas, también llamado registro de código de condiciones.
En la siguiente figurase ilustra el registro de banderas.
Aunque el registro de banderas es de 32 bits sólo hay
dieciocho banderas. Los otros 14 bits no son usados.
C (Acarreo): Indica un acarreo después de una suma o un
préstamo después de una resta. La bandera de acarreo también
indica condiciones de error en ciertos programas y
procedimientos.

P (Paridad): Es un cero para una paridad impar y un 1 para una


paridad par. La paridad es un conteo de unos expresada como
un número par e impar.

A (Acarreo auxiliar): Indica un acarreo después de una suma o


un préstamo después de una resta del bit 3 al bit 4 en el
resultado. Esta bandera sólo se utiliza en las operaciones DAA y
DAS para ajustar el valor de AL después de una suma o resta
BCD.
Z (Cero): Indica que el resultado de una operación aritmética o lógica es
cero. Si Z = 1, el resultado es cero y si Z = 0, el resultado no es cero.

S (Signo): Indica el signo aritmético del resultado después de una suma


o una resta. Si S = 1, el resultado es negativo. Si S = 0, el resultado es
positivo. Se debe tener en cuenta que el valor del bit más significativo
aparece en el bit de signo en cualquier instrucción que afecte las
banderas.

O (Sobreflujo): Es una condición que ocurre cuando se suman o se


restan números con signo. Un sobreflujo indica que el resultado ha
excedido la capacidad de la máquina. Por ejemplo si se suma 7FH +
01H (127 + 1) el resultado es 80H (-128). Este resultado representa
una situación de sobreflujo señalado por la bandera para la suma con
signo. Para operaciones sin signo esta bandera se ignora.
T (Trampa): Activa, si se pone a 1, o desactiva, si se pone a 0, el modo
de ejecución paso a paso. Este modo es utilizado por los depuradores
para ejecutar las instrucciones una a la vez y permitir observar el efecto
de la instrucción sobre los registros y la memoria.

I (Interrupción): Habilita, si se pone a 1, o deshabilita, si se pone a 0,


las interrupciones al microprocesador. El estado de esta bandera se
controla con las instrucciones STI (habilitar interrupciones) y CLI
(desactivar las interrupciones).

D (Dirección): Controla la selección de autoincremento o


autodecremento de los registros DI o SI durante las instrucciones de
cadenas y arreglos. Si D = 1 hay autodecremento en los registros y si D
= 0 hay autoincremento. El estado de esta bandera se controla con las
instrucciones STD (habilitar dirección) y CLD (desactivar dirección).
IOPL (Nivel de privilegio de E/S): Es utilizado en las operaciones en
modo protegido para seleccionar el nivel de privilegio de los
dispositivos de E/S.

NT (Tarea anidada): Indica que la tarea actual está anidada dentro de


otra tarea en el modo de operación protegido. Esta bandera se pone a 1
cuando la tarea es anidada por software.

RF (Resumen): Es usada con el depurador para controlar la continuación


de la ejecución después de la siguiente instrucción.
VM (Modo virtual): Selecciona el modo virtual de operación en un
sistema en modo protegido.

AC (Verificación de alineación): Se activa si una palabra o doble palabra


están direccionados en una posición que no es palabra o doble palabra.
Solo utilizado por el procesador 86486SX.

ID (Identificación): Indica que los procesadores Pentium – Pentium 4


reconocen la instrucción CPUID.
VIF (Interrupción virtual): Es una copia de la bandera de interrupción
disponible a los microprocesadores Pentium – Pentium 4.

VIP (Interrupción virtual pendiente): Proporciona información acerca de


la interrupción en modo virtual para los procesadores Pentium –
Pentium 4.
 La memoria principal (RAM)
La memoria es un conjunto o arreglo de celdas binarias de
longitud definida (8, 16, 32 o 64 bits), en las cuales se puede
almacenar instrucciones codificadas y datos.

Cada uno de estos celdas se caracteriza por tener una dirección


única, por lo que generalmente cada una de estas localidades se
identifica por su dirección la cual es la posición que le toca a esa
celda dentro del arreglo.

Las direcciones se expresan siempre en hexadecimal.


 Los procesadores actuales tienen diferentes modos de operación. He
aquí las descripciones breves de cada modo:

◦ Modo protegido El modo protegido es el estado nativo del procesador, en el que


están disponibles todas las instrucciones y características. Los programas reciben
áreas separadas de memoria llamadas segmentos, y el procesador evita que los
programas hagan referencia a la memoria que se encuentra fuera de sus segmentos
asignados.

◦ Modo 8086 virtual Mientras se encuentra en modo protegido, el procesador puede


ejecutar en forma directa el software para modo de direccionamiento real, como los
programas de MS-DOS, en un entorno multitarea seguro. En otras palabras, si un
programa de MS-DOS falla o trata de escribir datos en el área de memoria del
sistema, no afectará a los otros programas que se ejecuten al mismo tiempo.
Windows XP puede ejecutar varias sesiones separadas en modo 8086 a la vez.

◦ Modo de direccionamiento real Este modo implementa el entorno de programación


del procesador 8086 de Intel, con unas cuantas características adicionales, como la
habilidad de cambiar a otros modos. Este modo está disponible en Windows 98 y
puede usarse para ejecutar un programa de MS-DOS que requiera el acceso directo a
la memoria del sistema y a los dispositivos de hardware. Los programas que se
ejecutan en modo de direccionamiento real pueden hacer que el sistema operativo
falle (que deje de responder a los comandos).
 Los procesadores nuevos administran la memoria de acuerdo a los
modos básicos de operación vistos anteriormente. El modo protegido es
el más simple y poderoso; los demás se utilizan, por lo general, cuando
los programas deben acceder directamente al hardware del sistema.

 En el modo de direccionamiento real sólo puede direccionarse 1MB de


memoria, del 00000 al FFFFF hexadecimal.

 El procesador sólo puede ejecutar un programa a la vez, pero puede


interrumpir en forma momentánea ese programa para procesar las
solicitudes (conocidas como interrupciones) de los periféricos.

 Los programas de aplicación pueden leer y modificar cualquier área de


la RAM (memoria de acceso aleatorio) y pueden leer pero no modificar
cualquier área de la ROM (memoria de sólo lectura). El sistema operativo
MS-DOS se ejecuta en modo de direccionamiento real, y Windows 95/98
puede cargarse en este modo.
 En el modo protegido, el procesador puede ejecutar varios programas al
mismo tiempo. A cada proceso (programa en ejecución) le asigna un
total de 4GB de memoria. A cada programa se le puede asignar su
propia área reservada de memoria, y los programas no pueden acceder
de manera accidental al código y los datos de los demás programas.
Windows y Linux se ejecutan en modo protegido.

 En el modo 8086 virtual, la computadora se ejecuta en modo protegido


y crea una máquina 8086 virtual con su propio espacio de direcciones de
1MB, que simula a una computadora 80×86 que se ejecuta en modo de
direccionamiento real.

 Por ejemplo, Windows NT y 2000 crean una máquina 8086 virtual


cuando abrimos una ventana de Comandos. Puede ejecutar muchas de
esas ventanas al mismo tiempo, y cada una está protegida contra las
acciones de las demás. Algunos programas de MS-DOS que hacen
referencias directas al hardware de la computadora no se ejecutarán en
este modo bajo Windows NT, 2000 y XP
 El modo protegido es el modo “nativo” más poderoso del procesador. Al
ejecutarse en modo protegido, un programa puede acceder a 4GB de memoria,
con direcciones desde 0 hasta FFFFFFFF hexadecimal.

 En el contexto de Microsoft Assembler, el modelo de memoria plano (flat) es


apropiado para la programación en modo protegido. El modelo plano es fácil de
usar, ya que sólo requiere un entero de 32bits para guardar la dirección de una
instrucción o variable.

 La CPU realiza el cálculo y la traducción de las direcciones en segundo plano, todo


lo cual es transparente para los programadores de aplicaciones. Los registros de
segmento (CS, DS, SS, ES, FS, GS) apuntan a tablas de descriptores de segmentos,
que el sistema operativo utiliza para llevar el registro de las ubicaciones de los
segmentos individuales de un programa. Un programa ordinario en modo
protegido tiene tres segmentos: código, datos y pila, y utiliza los registros de
segmento CS,DS y SS:

◦ CS hace referencia a la tabla de descriptores para el segmento de código.


◦ DS hace referencia a la tabla de descriptores para el segmento de datos.
◦ SS hace referencia a la tabla de descriptores para el segmento de pila.
 En este modelo, todos los segmentos se asignan al espacio completo de
direcciones físicas de 32 bits de la computadora. Se requieren por lo
menos dos segmentos, uno para código y uno para datos.

 Cada segmento se define mediante un descriptor de segmento, un


entero de 64 bits que se almacena en una tabla conocida como la tabla
de descriptores globales (GDT).

 La siguiente figura muestra un descriptor de segmento cuyo campo


dirección base apunta a la primera ubicación disponible en la memoria
(00000000).

 El campo límite del segmento puede indicar de manera opcional la


cantidad de memoria física en el sistema. En esta figura, el límite del
segmento es 0040.

 El campo acceso contiene bits que determinan cómo puede utilizarse el


segmento.
 NOTA: Suponga que una computadora tiene
256MB de RAM. El campo límite del segmento
contendría el número 10000 hex, ya que su
valor se multiplica en forma implícita por
1000 hex, produciendo el número 10000000
hex (256MB) como resultado.
 En el modelo multisegmentos, cada tarea o programa
recibe su propia tabla de descriptores de segmento,
conocida como tabla de descriptores locales (LDT). Cada
descriptor apunta a un segmento, que puede ser distinto
de los demás segmentos utilizados por otros procesos.
Cada segmento tiene su propio espacio de direcciones.

 En la siguiente figura, cada entrada en la LDT apunta a un


segmento distinto en la memoria. Cada descriptor de
segmento especifica el tamaño exacto de su segmento.
Por ejemplo, el segmento que empieza en 3000 tiene un
tamaño de 2000 hexadecimal, que se calcula como (0002
* 1000 hexadecimal). El segmento que empieza en 8000
tiene el tamaño de A000 hexadecimal.
 Los procesadores tienen soporte para la paginación, una característica
que permite dividir los segmentos en bloques de 4,096 bytes de
memoria, conocidos como páginas. La paginación permite que el total
de memoria utilizada por todos los programas que se ejecutan al mismo
tiempo sea mucho más grande que la memoria física de la computadora.
La colección completa de páginas asignadas por el sistema operativo se
llama memoria virtual. Los sistemas operativos tienen programas
utilitarios llamados administradores de memoria virtual.

 La paginación es una solución importante para un molesto problema al


que se enfrentan los diseñadores de software y hardware. Un programa
debe cargarse en la memoria principal para poder ejecutarse, pero la
memoria es costosa. Los usuarios desean cargar numerosos programas
en la memoria y cambiar de uno a otro según lo deseen. Por otro lado, el
almacenamiento en disco es económico y vasto. La paginación crea la
ilusión de que la memoria es casi ilimitada en tamaño. El acceso al disco
es mucho más lento que el acceso a la memoria principal, por lo que
entre más dependa un programa de la paginación, se ejecutará con más
lentitud.
 Cuando una tarea se ejecuta, algunas de sus partes pueden almacenarse
en disco si no se encuentran en uso en ese momento.

 Las partes de la tarea se paginan (intercambian) en el disco. Otras


páginas activas en ejecución permanecen en la memoria.

 Cuando el programa empieza a ejecutar código que se ha paginado


fuera de la memoria, produce un fallo de página para que la página o
páginas que contienen el código o datos requeridos se carguen de vuelta
en la memoria.

 Para ver cómo funciona esto, elija una computadora con memoria algo
limitada y ejecute muchas aplicaciones extensas al mismo tiempo.
Deberá observar un retraso al cambiar de un programa a otro, debido a
que el OS debe transferir las porciones paginadas de cada programa, del
disco a la memoria. Una computadora funciona a una mayor velocidad
cuando hay más memoria instalada, ya que los archivos y programas de
aplicaciones extensas pueden mantenerse por completo en la memoria,
con lo que se reduce la cantidad de paginación.
 El concepto de interrupciones
 Llamadas a servicios del sistema
Las interrupciones son un mecanismo por medio del cual
hacemos que el CPU deje de hacer la tarea que estaba
realizando para que atienda otra tarea distinta (la que realiza
la interrupción).

Es decir, es una forma de llamar la atención del CPU de


manera que cuando cada dispositivo necesita ser atendido por
el CPU, emite una interrupción o señal haciendo que el CPU
vaya a atenderlo de inmediato.
 En el modo protegido se usa un conjunto de 256 descriptores de
interrupción que están almacenados en una tabla de descriptores
de interrupciones (IDT: Interrupt Descriptor Table).

 La tabla de descriptores de interrupción tiene una longitud de


256 x 8 (2048) bytes, con cada descriptor conteniendo 8 bytes.
La tabla de descriptores de interrupciones está localizada en
cualquier localidad de memoria en el sistema especificada por el
registro de direcciones de la tabla de descriptores de
interrupciones (IDTR: Interrupt Descriptor Table Address
Register).

 Cada entrada en la IDT contiene la dirección del procedimiento


de servicio de interrupción en la forma de un selector de
segmento y una dirección de desplazamiento de 32 bits.
También contiene el bit P (presente) y los bits DPL para describir
el nivel de privilegio de la interrupción. La siguiente figura
muestra los contenidos del descriptor de interrupción.
Para que una interrupción pueda ser servida o atendida por el
procesador, es necesario que se ejecuten una serie de pasos:

1. Terminar de ejecutar la instrucción actual en proceso.


2. Meter en la pila el registro de banderas.
3. Meter en la pila el la dirección del descriptor de segmento de
la próxima instrucción a ejecutar (CS).
4. Meter en la pila el desplazamiento de la próxima instrucción
a ejecutar (IP).
5. Determinar la dirección del manejador de la interrupción,
accesando la tabla de descriptores de interrupción. Se lee el
número de la interrupción, multiplicar por 8, accesar la tabla
en la posición correspondiente y cargar los registros CS e IP
con los valores.
6. Iniciar la ejecución del manejador de interrupciones.
Todos los manejadores de interrupciones terminan con la
instrucción IRETD.

Después de ejecutar la instrucción IRETD se ejecutan los


siguientes pasos:

7. Recuperar el valor del registro IP desde la pila.


8. Recuperar el valor del registro CS desde la pila.
9. Recuperar el valor del registro de banderas desde la pila.
10. Continua la ejecución de la siguiente instrucción del
programa.
 En el modo de 64 bits del Pentium 4 y Core 2,
una instrucción IRETQ debe ser usada para
regresar de una interrupción. Esta es una de
las razones por las que hay diferentes
sistemas operativos y controladores para el
modo de 64 bits.
 Las llamadas al sistema son interfaces de programación que
sirven para poder invocar los servicios que el sistema operativo
nos ofrece.

 Estas llamadas se encuentran escritas en lenguajes de alto nivel


como C y C++.

 En general, las llamadas a sistemas son accesadas mediante una


API (interfaz de programación de aplicaciones), en vez de
invocarlas directamente, de ésta manera se hace más fácil el
trabajo para un programador de aplicaciones.

 Esta API especifica un conjunto de funciones que el programador


puede utilizar, incluyendo los parámetros que son pasados a
cada función y que retornan valores que el programador puede
esperar
 A un programador le resulta más fácil utilizar una API que
realizar las llamadas al sistema directamente debido a que posee
una mayor portabilidad (cuando se desea compilar el programa
en cualquier SO que pueda soportar la API utilizada), y a menudo
es más difícil trabajar con las propias llamadas al sistema.

 El sistema de soporte en tiempo de ejecución, nos provee de una


interfaz de llamadas al sistema que sirve como enlace con las
llamadas al sistema disponibles en el sistema operativo. Ésta se
encarga de interceptar las llamadas a funciones dentro de la API
e invoca a la llamada al sistema necesaria. Cada llamada al
sistema tiene asociado un número y la interfaz mantiene una
tabla indexada con dichos números. Esta tabla sirve para invocar
las llamadas al núcleo del SO y retorna el estado de ejecución de
la llamada al sistema y los valores posibles de retorno.
Los modos de direccionamiento indican la forma en que se obtiene el
operando de una instrucción. Los microprocesadores de la familia Intel
8086 tienen los siguientes modos de direccionamiento:

Direccionamiento por registro.- El operando se encuentra en uno de los


registros del CPU. Ejemplo: MOV EAX, EBX.

Direccionamiento inmediato.- El operando se encuentra


inmediatamente después de la instrucción. Ejemplo: MOV EAX,
12345678H.

Direccionamiento directo.- El operando se encuentra en la dirección de


memoria especificada directamente. Ejemplo: MOV EAX, [12345678H].
Direccionamiento indirecto por registro.- El operando se encuentra en la
dirección de memoria especificada en un registro. Ejemplo: MOV EAX, [EBX].

Direccionamiento base más índice.- El operando se encuentra en la


dirección de memoria especificada por la suma de un registro base (EBX,
EBP) más un registro índice (ESI, EDI). Ejemplo: MOV EAX, [EBX + ESI]

Direccionamiento relativo por registro.- El operando se encuentra en la


dirección de memoria especificada por la suma de un registro base o un
registro índice más un desplazamiento. Ejemplo: MOV EAX, [EBX + 4].

Direccionamiento relativo base más índice.- El operando se encuentra en la


dirección de memoria especificada por la suma de un registro base más un
registro índice más un desplazamiento. Ejemplo: MOV EAX, [EBX + EDI + 4].
Direccionamiento índice escalado.- Esta disponible en los procesadores
386 y superiores. El segundo registro de un par de ellos, el índice, se
modifica por el factor de escala 2X, 4X u 8X para generar la dirección
de memoria del operando. Ejemplo: MOV AL, [EAX + 4 * EBX].

Direccionamiento relativo RIP.- Este modo de direccionamiento sólo


está disponible al direccionamiento de extensión de 64 bits de los
procesadores actuales. Este modo permite el acceso a cualquier
localidad de memoria al agregar un desplazamiento de 32 bits al
contenido de 64 bits del puntero de instrucciones de 64 bits.
 Proceso de ensamblado y ligado

 Desplegado de mensajes en el monitor


 Instalar Visual C++ (o Visual Studio, cualquier
versión).

 Dentro de Visual Studio, crear un nuevo proyecto.

 Seleccionar el tipo de proyecto “Proyecto Vacío”,


de la plantilla “General” de Visual C++.

 Poner nombre al proyecto, seleccionar ubicación


del mismo y pulsar el botón Aceptar.
 En la ventana del explorador de soluciones, pulsar con el botón derecho el
nombre del proyecto recién creado.

 Seleccionar la opción “Personalizaciones de compilación” del menú contextual


mostrado.

 Seleccionar la opción “masm” y pulsar el botón Aceptar.

 Volver a mostrar el menú contextual del proyecto y seleccionar la opción de


“Propiedades”

 En la pestaña de “Vinculador” seleccionar la opción “Sistema”. Escoger como


Subsistema la opción “Console (/SUBSYSTEM:CONSOLE)”.

 En la pestaña “Microsoft MacroAssembler” seleccionar la opción “Listing File”.


Proporcionar un nombre de archivo con extensión .lst en la casilla “Assembled
Code Listing File”.

 Pulsar el botón Aceptar.


 En la ventana del explorador de soluciones, pulsar con el botón derecho
la opción de “Archivos de código fuente”.

 Seleccionar la opción de “Agregar” y “Nuevo elemento…” del menú


contextual mostrado.

 En la ventana de “Agregar nuevo elemento” seleccionar “Archivo C++


(.cpp)”, e introduzca un nombre de archivo con extensión .asm.

 Escribir el código del programa fuente.

 Pulsar el botón “Depurador local de Windows” (Nota: La pantalla de la


consola se abre y se cierra porque se inicia y termina el programa de
manera casi inmediata).

 Ejecutar el archivo .EXE generado desde la ventana de la consola de


Windows.
 Escribir un programa en lenguaje
ensamblador que muestre en la consola los
datos generales de los dos integrantes del
equipo.

 Entregar archivo .lst impreso

 Fecha:
 En la superficie, los programas en modo de consola de 32 bits se ven y
se comportan como los programas de MS-DOS de 16 bits que se
ejecutan en modo de texto.

 No obstante, hay diferencias: los primeros se ejecutan en modo


protegido de 32 bits, mientras que los programas de MS-DOS se
ejecutan en modo de direccionamiento real.

 Utilizan distintas bibliotecas de funciones. Los programas Win32 llaman


a las funciones de la misma biblioteca que utilizan las aplicaciones
Windows gráficas. Los programas de MS-DOS utilizan interrupciones de
BIOS y de MS-DOS que han existido desde la introducción de la IBM-PC.

 Una Interfaz de Programación de Aplicaciones (API) es una colección de


tipos, constantes y funciones que proporcionan la manera de manipular
directamente los objetos a través de la programación. Por lo tanto, la API
Win32 nos permite utilizar las funciones en la versión de 32 bits de MS-
Windows.
 Un programa de consola se ve y se comporta como
una ventana de MS-DOS, con algunas mejoras que
veremos más adelante. La consola tiene un solo búfer
de entrada y uno o más búferes de pantalla:

◦ El búfer de entrada contiene una cola de registros de


entrada, cada uno de los cuales contiene datos acerca de
un evento de entrada. Algunos ejemplos de eventos de
entrada son: entrada del teclado, clics del ratón y cuando el
usuario cambia el tamaño de la ventana de consola.

◦ Un búfer de pantalla es un arreglo bidimensional de datos


de caracteres y colores, que afectan la apariencia del texto
en la ventana de consola.
 Al llamar a las funciones en la API Win32, se utilizan dos tipos de
conjuntos de caracteres: el conjunto de caracteres ASCII/ANSI de 8 bits y
el conjunto Unicode de 16 bits (disponible en Windows NT, 2000 y XP).

 Las funciones de Win32 que tienen que ver con texto, por lo general, se
proporcionan en dos versiones, una que termina con la letra A (para los
caracteres ANSI de 8 bits) y la otra que termina en W (para los conjuntos
de caracteres extensos, incluyendo Unicode). Una de estas funciones es
WriteConsole:

◦ WriteConsoleA.
◦ WriteConsoleW.

 Windows 95 o 98 no soporta los nombres de las funciones que terminan


en W. Por otro lado, en Windows NT, 2000 y XP, Unicode es el conjunto
de caracteres nativo. Por ejemplo, si llamamos a una función como
WriteConsoleA, el sistema operativo convierte los caracteres de ANSI a
Unicode y llama a WriteConsoleW.
 Hay dos niveles de acceso a la consola, que permiten
concesiones entre simplicidad y un control completo:

◦ Las funciones de consola de alto nivel leen un flujo de


caracteres del búfer de entrada de la consola. Escriben
datos tipo carácter al búfer de pantalla de la consola. Tanto
la entrada como la salida puede redirigirse para leer o
escribir en/desde archivos de texto.

◦ Las funciones de consola de bajo nivel obtienen


información detallada acerca de los eventos de teclado y de
ratón, y las interacciones del usuario con la ventana de
consola (arrastrar, cambiar tamaño, etcétera). Estas
funciones también permiten un control detallado del
tamaño y la posición de la ventana, así como los colores del
texto.
 Las funciones Win32 se documentan usando las
declaraciones de funciones para programadores de
C/C++.

 En estas declaraciones, los tipos de todos los


parámetros de las funciones se basan en los tipos
estándar de C o en uno de los tipos predefinidos de
MS-Windows (en la siguiente tabla se muestra una
lista parcial).

 Es importante diferenciar los valores de datos de los


apuntadores a los valores. El nombre de un tipo que
empieza con las letras LP es un apuntador largo
(long) a algún otro objeto.
Tipo de MS- Tipo de Descripción
Windows MASM
BOOL, BOOLEAN DWORD Un valor booleano (TRUE o FALSE)
BYTE BYTE Un entero de 8 bits sin signo
CHAR BYTE Un carácter ANSI de Windows de 8 bits
COLORREF DWORD Un valor de 32 bits que se usa como valor de color
DWORD DWORD Un entero de 32 bits sin signo
HANDLE DWORD Manejador de un objeto
HFILE DWORD Manejador de un archivo abierto mediante OpenFile
INT SDWORD Un entero de 32 bits con signo
LONG SDWORD Un entero de 32 bits con signo
LPARAM DWORD Parámetro de mensaje, usado por los procedimientos de ventana y las funciones de devolución de
llamada (callback)
LPCSTR PTR BYTE Un apuntador de 32 bits a una cadena constante con terminación nula de caracteres Windows (ANSI) de
8 bits
LPCVOID DWORD Apuntador a una constante de cualquier tipo
LPSTR PTR BYTE Un apuntador de 32 bits a una cadena con terminación nula de caracteres Windows (ANSI) de 8 bits
LPCTSTR PTR WORD Un apuntador de 32 bits a una cadena de caracteres constante, que es portable para Unicode y los
conjuntos de caracteres de doble byte
LPTSTR PTR WORD Un apuntador de 32 bits a una cadena de caracteres que es portable para Unicode y los conjuntos de
caracteres de doble byte
LPVOID DWORD Un apuntador de 32 bits a un tipo no especificado
LRESULT DWORD Un valor de 32 bits devuelto de un procedimiento de ventana o función de devolución de llamada
(callback)
SIZE_T DWORD El número máximo de bytes a los que puede apuntar un apuntador
UINT DWORD Un entero de 32 bits sin signo
WNDPROC DWORD Un apuntador de 32 bits a un procedimiento de ventana
WORD WORD Un entero de 16 bits sin signo
WPARAM DWORD Un valor de 32 bits que se pasa como parámetro a un procedimiento de ventana o función de devolución
de llamada (callback)
 Casi todas las funciones de consola de Win32 requieren recibir un manejador
como primer argumento. Un manejador es un entero de 32 bits sin signo que
identifica en forma única a un objeto, como un mapa de bits, una pluma de dibujo
o cualquier otro dispositivo de entrada/salida:

◦ STD_INPUT_HANDLE entrada estándar (valor -10)


◦ STD_OUTPUT_HANDLE salida estándar (valor -11)
◦ STD_ERROR_HANDLE salida de error estándar (valor -12)

 Los últimos dos manejadores se utilizan al escribir en el búfer de pantalla activo


de la consola.

 La función GetStdHandle devuelve un manejador para un flujo de la consola:


entrada, salida o salida de error. Necesitamos un manejador para poder realizar
operaciones de entrada/salida en un programa basado en la consola.

 La función devuelve el manejador en EAX, que debe copiarse a una variable por
protección.
 La siguiente tabla contiene una referencia
rápida al conjunto completo de funciones de
consola Win32.

 Las funciones de la API Win32 no preservan


EAX, EBX, ECX y EDX, por lo que debemos
resguardar estos registros por nuestra
cuenta.
Función Descripción
AllocConsole Asigna una nueva consola para el proceso que hace la llamada
CreateConsoleScreenBuffer Crea un búfer de pantalla de consola
ExitProcess Termina un proceso y todos sus subprocesos
FillConsoleOutputAttribute Establece los atributos de texto y color de fondo para un número especificado de celdas de caracteres
FillConsoleOutputCharacter Escribe un carácter en el búfer de pantalla, un número especificado de veces
FlushConsoleInputBuffer Vacía el búfer de entrada de la consola
FreeConsole Desconecta el proceso que hizo la llamada de su consola
GenerateConsoleCtrlEvent Envía una señal especificada a un grupo de proceso de control que comparte la consola asociada con el
proceso que hizo la llamada
GetConsoleCP Obtiene la página de código de entrada utilizada por la consola asociada con el proceso que hizo la llamada
GetConsoleCursorInfo Obtiene información acerca del tamaño y la visibilidad del cursor para el búfer de pantalla de consola
especificado
GetConsoleMode Obtiene el modo de entrada actual del búfer de entrada de una consola, o el modo de salida actual de un
búfer de pantalla de consola
GetConsoleOutputCP Obtiene la página de código de salida que utiliza la consola asociada con el proceso que hizo la llamada
GetConsoleScreenBufferInfo Obtiene información acerca del búfer de pantalla de consola especificado
GetConsoleTitle Obtiene la cadena de la barra de título para la ventana de consola actual
GetConsoleWindow Obtiene el manejador de ventana utilizado por la consola asociada con el proceso que hizo la llamada
GetLargestConsoleWindowSize Obtiene el tamaño de la ventana de consola más grande posible
GetNumberOfConsoleInputEvents Obtiene el número de registros de entrada no leídos en el búfer de entrada de la consola
GetNumberOfConsoleMouseButtons Obtiene el número de botones en el ratón, utilizados por la consola actual
GetStdHandle Obtiene un manejador para la entrada estándar, la salida estándar o el dispositivo de error estándar
HandlerRoutine Una función definida por la aplicación, que se utiliza con la función SetConsoleCtrlHandler
PeekConsoleInput Lee datos del búfer de entrada de consola especificado, sin eliminarlos del búfer
Función Descripción
ReadConsole Lee la entrada de caracteres del búfer de entrada de la consola y la elimina del búfer
ReadConsoleInput Lee datos de un búfer de entrada de consola y los elimina del búfer
ReadConsoleOutput Lee los datos de los caracteres y atributos de color de un bloque rectangular de celdas de caracteres en un búfer
de pantalla de consola
ReadConsoleOutputAttribute Copia un número especificado de atributos de color de texto y de fondo, de las celdas consecutivas de un búfer
de pantalla de consola
ReadConsoleOutputCharacter Copia un número de caracteres de las celdas consecutivas de un búfer de pantalla de consola
ScrollConsoleScreenBuffer Mueve un bloque de datos en un búfer de pantalla
SetConsoleActiveScreenBuffer Establece el búfer de pantalla especificado para que sea el búfer de pantalla de consola que se muestra
actualmente
SetConsoleCP Establece la página de código de entrada utilizada por la consola asociada con el proceso que hizo la llamada
SetConsoleCtrlHandler Agrega o elimina una función HandlerRoutine definida por una aplicación, de la lista de funciones manejadoras
para el proceso que hizo la llamada
SetConsoleCursorInfo Establece el tamaño y la visibilidad del cursor para el búfer de pantalla de consola especificado
SetConsoleCursorPosition Establece la posición del cursor en el búfer de pantalla de consola especificado
SetConsoleMode Establece el modo de entrada del búfer de entrada de una consola, o el modo de salida de un búfer de pantalla
de consola
SetConsoleOutputCP Establece la página de código de salida utilizada por la consola asociada con el proceso que hizo la llamada
SetConsoleScreenBufferSize Modifica el tamaño del búfer de pantalla de consola especificado
SetConsoleTextAttribute Establece los atributos de color de texto y de fondo de los caracteres que se escriben en el búfer de pantalla
SetConsoleTitle Establece la cadena de la barra de título para la ventana de consola actual
SetConsoleWindowInfo Establece el tamaño y posición actuales de la ventana de un búfer de pantalla de consola
SetStdHandle Establece el manejador para la entrada estándar, la salida estándar o el dispositivo de error estándar
WriteConsole Escribe una cadena de caracteres en un búfer de pantalla de consola, empezando en la posición actual del cursor
WriteConsoleInput Escribe datos directamente en el búfer de entrada de consola
WriteConsoleOutput Escribe los datos de los caracteres y atributos de color en un bloque rectangular especificado de celdas de
caracteres de un búfer de pantalla de consola
WriteConsoleOutputAttribute Copia un número de atributos de color de texto y de fondo en las celdas consecutivas de un búfer de pantalla de
consola
WriteConsoleOutputCharacter Copia un número de caracteres en las celdas consecutivas de un búfer de pantalla de consola
Un formato de instrucción define la descripción en
bits de una instrucción, en términos de las distintas
partes que la componen.

Un formato de instrucción debe contener un código


de operación (cod-op) e implícita o explícitamente,
ningún o algunos operandos.

Existen diversos tipos de formatos de instrucción en


el 80x86.
Código de Operación.- El primer byte esta formado por tres campos:

 Codop.- Los primeros 6 bits forman el verdadero código de la instrucción.


 D.- El bit D significa dirección. Cuando D = 1, el campo REG contiene el primer
operando y el campo R/M contiene el segundo operando. Cuando D = 0, el
campo REG contiene el segundo operando y el campo R/M contiene el primer
operando.
 W.- El bit W significa el tamaño o el ancho del operando (width). Si W = 1 el
operando el de 16 o 32 bits. Si W = 0 el operando es de 8 bits. Cuando el
operando es de 16 bits se antepone el prefijo 66H

Mod r/m.- El segundo byte también esta formado por tres campos:

Mod.- Los primeros dos bits son el campo MOD. Este campo selecciona el
modo de direccionamiento y si hay desplazamiento en el modo seleccionado.
REG.- Una referencia, de tres bits, a un registro.
R/M.- Una referencia, de tres bits, a un registro o a memoria.
SIB.- El tercer byte esta formado por tres campos:

 Escala.- Especifica el factor de escala.


 Índice.- Especifica el registro índice.
 Base.- Especifica el registro base.

Desplazamiento.- Algunos modos de direccionamiento incluyen un


desplazamiento inmediatamente después de los bytes ModR/M (o SIB). Si se
requiere el desplazamiento puede ser de 1, 2 o 4 bytes.

Inmediato.- Si una instrucción especifica un operando inmediato, el operando


siempre sigue a cualquier byte de desplazamiento. Un operando inmediato
puede ser de 1, 2 o 4 bytes.
Tabla 1.- Bits REG
REG W=0 W=1 W=1
000 AL AX EAX
001 CL CX ECX
010 DL DX EDX
011 BL BX EBX
100 AH SP ESP
101 CH BP EBP
110 DH SI ESI
111 BH DI EDI

Tabla 2.- Los bits MD y R/M


MOD
00 01 10 11
000 M[EAX] M[EAX + DESP8] M[EAX + DESP32] AL o EAX
001 M[ECX] M[ECX + DESP8] M[ECX + DESP32] CL o ECX
010 M[EDX] M[EDX + DESP8] M[EDX + DESP32] DL o EDX
R/M 011 M[EBX] M[EBX + DESP8] M[EBX + DESP32] BL o EBX
100 SIB SIB con DESP8 SIB con DESP32 AH o ESP
101 Direc. Directo M[EBP + DESP8] M[EBP + DESP32] CH o EBP
110 M[ESI] M[ESI + DESP8] M[ESI + DESP32] DH o ESI
111 M[EDI] M[EDI + DESP8] M[EDI + DESP32] BH o EDI
Tabla 3.- Bits BASE
000 EAX
001 ECX
010 EDX
011 EBX
100 ESP
101 *
110 ESI
111 EDI
* Significa un DESP32 sin base cuando MD=00. En otro caso significa [EBP + DESP8] o [EBP + DESP32 ]

Tabla 4.- Los bits Scale e Index


Scale
00 01 10 11
000 M[EAX] M[EAX * 2] M[EAX * 4] M[EAX * 8]
001 M[ECX] M[ECX * 2] M[ECX * 4] M[ECX * 8]
010 M[EDX] M[EDX * 2 ] M[EDX * 4] M[EDX * 8]
Index 011 M[EBX] M[EBX * 2] M[EBX * 4] M[EBX * 8]
100 Ninguno Ninguno Ninguno Ninguno
101 M[EBP] M[EBP * 2] M[EBP * 4] M[EBP * 8]
110 M[ESI] M[ESI * 2] M[ESI * 4] M[ESI * 8]
111 M[EDI] M[EDI * 2] M[EDI * 4] M[EDI * 8]
1.- Instrucción de 1 byte con operandos implícitos.

COD-OP

Ejemplo: CLC
1.- Instrucción de 1 byte con operandos implícitos.

COD-OP

Ejemplo: CLC

11111000
2.- Instrucción de 1 byte – modo registro.

COD-OP REG

Ejemplo: DEC ECX


2.- Instrucción de 1 byte – modo registro.

COD-OP REG

Ejemplo: DEC ECX

01001rrr
2.- Instrucción de 1 byte – modo registro.

COD-OP REG

Ejemplo: DEC ECX

01001rrr

01001001
3.- Registro a registro.

COD-OP d w md reg r/m

Ejemplo: MOV EAX, ECX


3.- Registro a registro.

COD-OP d w md reg r/m

Ejemplo: MOV EAX, ECX

100010dw mdregr/m
3.- Registro a registro.

COD-OP d w md reg r/m

Ejemplo: MOV EAX, ECX

100010dw mdregr/m

10001001 11001000

10001011 11000001
4.- Registro a/de memoria sin desplazamiento.

COD-OP d w md reg r/m

Ejemplo: AND DL, [EBX]


4.- Registro a/de memoria sin desplazamiento.

COD-OP d w md reg r/m

Ejemplo: AND DL, [EBX]

001000dw mdregr/m
4.- Registro a/de memoria sin desplazamiento.

COD-OP d w md reg r/m

Ejemplo: AND DL, [EBX]

001000dw mdregr/m

00100010 00010011
5.- Registro a/de memoria con desplazamiento.

COD-OP d w md reg r/m Desp Desp Desp Desp

Ejemplo: SUB AL, [EBX + 1234H]


5.- Registro a/de memoria con desplazamiento.

COD-OP d w md reg r/m Desp Desp Desp Desp

Ejemplo: SUB AL, [EBX + 1234H]

001010dw mdregr/m desp desp desp desp


5.- Registro a/de memoria con desplazamiento.

COD-OP d w md reg r/m Desp Desp Desp Desp

Ejemplo: SUB AL, [EBX + 1234H]

001010dw mdregr/m desp desp desp desp

00101010 10000011 00110100 00010010 00000000 00000000


6.- Operando inmediato para registro.

COD-OP s w md cod r/m Dato Dato Dato Dato

Ejemplo: ADC ECX, 0FD812345H


6.- Operando inmediato para registro.

COD-OP s w md cod r/m Dato Dato Dato Dato

Ejemplo: ADC ECX, 0FD812345H

100000sw md010r/m dato dato dato dato


6.- Operando inmediato para registro.

COD-OP s w md cod r/m Dato Dato Dato Dato

Ejemplo: ADC ECX, 0FD812345H

100000sw md010r/m dato dato dato dato

10000001 11010001 01000101 00100011 10000001 11111101


7.- Operando inmediato a memoria con desplazamiento.
COD-OP s w md cod r/m Desp Desp Desp Desp

Dato Dato Dato Dato

Ejemplo: OR DWORD PTR [EBX + 2000H], 2345H


7.- Operando inmediato a memoria con desplazamiento.

COD-OP s w md cod r/m Desp Desp Desp Desp

Dato Dato Dato Dato

Ejemplo: OR DWORD PTR [EBX + 2000H], 2345H

100000sw md001r/m desp desp desp desp dato dato dato dato
7.- Operando inmediato a memoria con desplazamiento.
COD-OP s w md cod r/m Desp Desp Desp Desp

Dato Dato Dato Dato

Ejemplo: OR DWORD PTR [EBX + 2000H], 2345H

100000sw md001r/m desp desp desp desp dato dato dato dato

10000001 10001011 00000000 00100000 00000000 00000000


01000101 00100011 00000000 0000000
8.- Registro a/de memoria SIB

COD-OP d w md reg r/m ss ind bas

Ejemplo: AND [EBX + ESI], DL


8.- Registro a/de memoria SIB.

COD-OP d w md reg r/m ss ind bas

Ejemplo: AND [EBX + ESI], DL

001000dw mdregr/m ssindbas


8.- Registro a/de memoria SIB.

COD-OP d w md reg r/m ss ind bas

Ejemplo: AND [EBX + ESI], DL

001000dw mdregr/m ssindbas

00100000 00010100 00011110


 Obtener la representación en bits de las siguientes instrucciones:

1. AAA
2. CBW
3. HLT
4. INC EAX
5. POP EBX
6. PUSH ESI
7. ADD AL, BL
8. CMP ECX, ESI
9. OR CL, BL
10. SUB [EBX], CL
11. ADC [EDI], DX
12. CMP DL, [ESI]
13. SBB [ESI + 1234H], CL
14. XOR [EDI + 56H], DX
15. AND BL, [ESI + 78H]
16. CMP AL, 0FFH
17. MOV CX, 1234H
18. OR SI, 5678H
19. ADC DWORD PTR [EBX + EDI + 12H], 0FFEEH
20. SUB BYTE PTR [EBP + 2*ESI + 9876H], 0AH