Arquitectura x86
El procesador Intel x86 usa una arquitectura compleja del equipo del conjunto de instrucciones (CISC), lo que significa que hay un número modesto de registros de propósito especial en lugar de grandes cantidades de registros de uso general. También significa que las instrucciones complejas de propósito especial prevalecerán.
El procesador x86 traza su patrimonio al menos hasta el procesador Intel 8080 de 8 bits. Muchas peculiaridades del conjunto de instrucciones x86 se deben a la compatibilidad con versiones anteriores con ese procesador (y con su variante Zilog Z-80).
Microsoft Win32 usa el procesador x86 en modo plano de 32 bits. Esta documentación solo se centrará en el modo plano.
Registros
La arquitectura x86 consta de los siguientes registros enteros sin privilegios.
eax |
Acumulador |
ebx |
Registro base |
ecx |
Registro de contadores |
edx |
Registro de datos: se puede usar para el acceso a puertos de E/S y funciones aritméticas |
esi |
Registro de índice de origen |
edi |
Registro de índice de destino |
ebp |
Registro de puntero base |
esp |
Puntero de pila |
Todos los registros enteros son de 32 bits. Sin embargo, muchos de ellos tienen subregistros de 16 bits o 8 bits.
ax |
16 bits bajos de eax |
bx |
16 bits bajos de ebx |
cx |
16 bits bajos de ecx |
dx |
16 bits bajos de edx |
si |
16 bits bajos de esi |
di |
16 bits bajos de edi |
bp |
16 bits bajos de ebp |
sp |
16 bits bajos de esp |
al |
8 bits bajos de eax |
ah |
8 bits altos de ax |
bl |
8 bits bajos de ebx |
bh |
8 bits altos de bx |
cl |
8 bits bajos de ecx |
ch |
8 bits altos de cx |
dl |
8 bits bajos de edx |
dh |
8 bits altos de dx |
El funcionamiento en un subregistro afecta solo al subregistro y a ninguna de las partes fuera del subregistro. Por ejemplo, el almacenamiento en el registro ax no modifica los 16 bits altos del registro eax.
Al usar el ? (Evaluar expresión) comando, los registros deben tener como prefijo un signo "at" ( @ ). Por ejemplo, debe usar ? @ax en lugar de ? ax. Esto garantiza que el depurador reconozca ax como un registro en lugar de un símbolo.
Sin embargo, el comando (@) no es necesario en el comando
Otros dos registros son importantes para el estado actual del procesador.
eip |
puntero de instrucción |
marcas |
Banderas |
El puntero de instrucción es la dirección de la instrucción que se está ejecutando.
El registro de marcas es una colección de marcas de un solo bit. Muchas instrucciones modifican las marcas para describir el resultado de la instrucción. Estas marcas pueden ser comprobadas por instrucciones de salto condicional. Vea Marcas x86 para más detalles.
Convenciones de llamada
La arquitectura x86 tiene varias convenciones de llamada diferentes. Afortunadamente, todas siguen las mismas reglas de preservación de registros y de retorno de funciones:
Las funciones deben conservar todos los registros, excepto eax, ecx y edx, que pueden modificarse en una llamada a función, y esp, que debe actualizarse según la convención de llamada.
El registro eax recibe los valores de retorno de la función si el resultado es de 32 bits o menor. Si el resultado es de 64 bits, el resultado se almacena en el par edx:eax.
A continuación se muestra una lista de las convenciones de llamada que se usan en la arquitectura x86:
Win32 (__stdcall)
Los parámetros de la función se pasan a la pila, empujados de derecha a izquierda, y el llamante limpia la pila.
Llamada de método nativa de C++ (también conocida como thiscall)
Los parámetros de la función se pasan a la pila, se empujan de derecha a izquierda, el puntero "this" se pasa al registro ecx y el destinatario limpia la pila.
COM (__stdcall para llamadas de método de C++)
Los parámetros de función se pasan en la pila, se insertan de derecha a izquierda, luego se inserta el puntero "this" en la pila y, a continuación, se llama a la función. El receptor limpia la pila.
__fastcall
Los dos primeros argumentos DWORD-o-más-pequeños se pasan en los registros ecx y edx. El resto de parámetros se pasan a la pila, empujados de derecha a izquierda. El receptor limpia la pila.
__cdecl
Los parámetros de la función se pasan a la pila, empujados de derecha a izquierda, y el invocador limpia la pila. La convención de llamada __cdecl se usa para todas las funciones con parámetros de longitud variable.
Visualización de registros y marcas en el depurador
A continuación se muestra un ejemplo de visualización de registros del depurador:
eax=00000000 ebx=008b6f00 ecx=01010101 edx=ffffffff esi=00000000 edi=00465000
eip=77f9d022 esp=05cffc48 ebp=05cffc54 iopl=0 nv up ei ng nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000286
En la depuración en modo usuario, puede ignorar el iopl y toda la última línea de la pantalla del depurador.
Banderas x86
En el ejemplo anterior, los códigos de dos letras al final de la segunda línea son marcas. Estos son registros de un solo bit y tienen una variedad de usos.
En la tabla siguiente se enumeran las marcas x86:
Código de marca | Nombre de marca | Valor | Estado de la bandera | Descripción |
---|---|---|---|---|
of | Marca de desbordamiento | 0 1 | nvov | Sin desbordamiento - Desbordamiento |
df | Bandera de dirección | 0 1 | updn | Dirección arriba - Dirección abajo |
si | Marca de interrupción | 0 1 | diei | Interrupciones desactivadas - Interrupciones activadas |
sf | Marca de señal | 0 1 | plng | Positivo (o cero): negativo |
zf | Marca cero | 0 1 | nzzr | No cero - Cero |
af | Marca de transporte auxiliar | 0 1 | naac | Sin transporte auxiliar - transporte auxiliar |
pf | Marca de paridad | 0 1 | pepo | Paridad par - Paridad impar |
cf | Marca de transporte | 0 1 | nccy | Sin transporte - Transporte |
tf | Marca de trampa | Si tf es igual a 1, el procesador generará una excepción STATUS_SINGLE_STEP después de la ejecución de una instrucción. Esta marca es utilizada por un depurador para implementar el rastreo de un solo paso. No debe usarse en otras aplicaciones. | ||
iopl | Nivel de privilegios de E/S | Nivel de privilegios de E/S Este es un entero de dos bits, con valores entre cero y 3. El sistema operativo lo usa para controlar el acceso al hardware. Las aplicaciones no deben usarla. |
Cuando los registros se muestran como resultado de algún comando en la ventana de comandos del depurador, es el estado del indicador lo que se muestra. Sin embargo, si desea cambiar una bandera mediante el comando r (Registers), debe hacer referencia a ella mediante el código de bandera .
En la ventana Registros de WinDbg, el código de marca se usa para ver o modificar marcas. No se admite el estado de marca.
Este es un ejemplo. En la visualización del registro anterior, aparece el estado de marca ng. Esto significa que la marca de signo está establecida actualmente en 1. Para cambiar esto, use el siguiente comando:
r sf=0
Esto pone la marca de signo a cero. Si realiza otra visualización de registro, el código de estado ng no aparecerá. En su lugar, se mostrará el código de estado pl.
La marca de signo, la marca cero y la marca de transporte son las marcas más usadas.
Condiciones
Una condición describe el estado de una o varias marcas. Todas las operaciones condicionales de x86 se expresan en términos de condiciones.
El ensamblador usa una o dos abreviaturas de letra para representar una condición. Una condición se puede representar mediante varias abreviaturas. Por ejemplo, AE ("mayor o igual") es la misma condición que NB ("no inferior"). En la tabla siguiente se enumeran algunas condiciones comunes y su significado.
Nombre de condición | Banderas | Significado |
---|---|---|
Z |
ZF=1 |
El resultado de la última operación era cero. |
NZ |
ZF=0 |
El resultado de la última operación no fue cero. |
C |
CF=1 |
La última operación requirió un transporte o un préstamo. (Para enteros sin signo, esto indica desbordamiento). |
NC |
CF=0 |
La última operación no requirió un transporte o un préstamo. (Para enteros sin signo, esto indica desbordamiento). |
S |
SF=1 |
El resultado de la última operación tiene el bit alto activado. |
NS |
SF=0 |
El resultado de la última operación tiene el bit alto despejado. |
O |
OF=1 |
Cuando se trata como una operación de número entero con signo, la última operación provocó un desbordamiento o un subdesbordamiento. |
NO |
OF=0 |
Cuando se trata como una operación de número entero con signo, la última operación no causó un desbordamiento o subdesbordamiento. |
Las condiciones también se pueden usar para comparar dos valores. La instrucción cmp compara sus dos operandos y, a continuación, establece los indicadores como si restara un operando del otro. Las condiciones siguientes se pueden usar para comprobar el resultado de cmpvalue1, value2.
Nombre de la condición | Banderas | Significado después de una operación CMP. |
---|---|---|
E |
ZF=1 |
value1 == value2. |
NE |
ZF=0 |
value1 != value2. |
GE NL | SF=OF |
value1>= value2. Los valores se tratan como enteros con signo. |
LE NG | ZF=1 o SF!=OF |
value1<= value2. Los valores se tratan como enteros con signo. |
G NLE | ZF=0 y SF=OF |
value1>value2. Los valores se tratan como enteros con signo. |
L NGE | SF!=OF |
value1<value2. Los valores se tratan como enteros con signo. |
AE NB | CF=0 |
value1>= value2. Los valores se tratan como enteros sin signo. |
BE NA | CF=1 o ZF=1 |
value1<= value2. Los valores se tratan como enteros sin signo. |
Una dirección NBE | CF=0 y ZF=0 |
value1>value2. Los valores se tratan como enteros sin signo. |
B NAE | CF=1 |
value1<value2. Los valores se tratan como enteros sin signo. |
Las condiciones se utilizan normalmente para actuar sobre el resultado de una instrucción cmp o de prueba. Por ejemplo
cmp eax, 5
jz equal
compara el registro eax con el número 5 calculando la expresión (eax - 5) y activando las marcas según el resultado. Si el resultado de la resta es cero, entonces se activará la marca zr, y la condición jz será verdadera por lo que se realizará el salto.
Tipos de datos
byte: 8 bits
word: 16 bits
dword: 32 bits
qword: 64 bits (incluye doubles de punto flotante)
tword: 80 bits (incluye doubles extendidos de punto flotante)
oword: 128 bits
Notación
En la tabla siguiente se indica la notación utilizada para describir las instrucciones del lenguaje de ensamblado.
Notación | Significado |
---|---|
r, r1, r2... |
Registros |
m |
Dirección de memoria (consulte la sección sobre modos de direccionamiento que sigue para obtener más información). |
#n |
Constante inmediata |
r/m |
Registro o memoria |
r/#n |
Registro o constante inmediata |
r/m/#n |
Registro, memoria o constante inmediata |
cc |
Código de condición enumerado en la sección Condiciones anteriores. |
T |
"B", "W" o "D" (byte, word o dword) |
accT |
Tamaño T acumulador: al si T = "B", ax si T = "W", o eax si T = "D" |
Modos de direccionamiento
Hay varios modos de direccionamiento diferentes, pero todos toman la forma T ptr [expr], donde T es algún tipo de datos (vea la sección Tipos de datos anteriores) y expr es una expresión que implica constantes y registros.
La notación para la mayoría de los modos se puede deducir sin mucha dificultad. Por ejemplo, BYTE PTR [esi+edx*8+3] significa "tomar el valor del registro esi, sumarle ocho veces el valor del registro edx, sumar tres, y a continuación, acceder al byte en la dirección resultante".
Canalización
El Pentium es dual-issue, lo que significa que puede realizar hasta dos acciones en un tick de reloj. Sin embargo, las reglas sobre cuándo es capaz de realizar dos acciones a la vez (lo que se conoce como emparejamiento) son muy complicadas.
Como x86 es un procesador CISC, no tiene que preocuparse por las ranuras de retardo de salto.
Acceso a memoria sincronizada
Las instrucciones de carga, modificación y almacenamiento pueden recibir un prefijo de bloqueo , que modifica la instrucción como sigue:
Antes de emitir la instrucción, la CPU vaciará todas las operaciones de memoria pendientes para garantizar la coherencia. Se abandonan todos los prefijados de datos.
Al ejecutar la instrucción, la CPU tendrá acceso exclusivo al bus de datos. Esto garantiza la atomicidad de la operación de carga/modificación/almacenamiento.
La instrucción xchg cumple automáticamente las reglas anteriores siempre que intercambia un valor con memoria.
Todas las demás instrucciones, por defecto, no son bloqueantes.
Predicción de saltos
Se predice que se realizarán saltos incondicionales.
Se predice que los saltos condicionales se realizarán o no, dependiendo de si se realizaron la última vez que se ejecutaron. La caché para registrar el historial de saltos tiene un tamaño limitado.
Si la CPU no tiene un registro de si se tomó o no el salto condicional la última vez que se ejecutó, predice los saltos condicionales hacia atrás como tomados y los saltos condicionales hacia adelante como no tomados.
Alineación
El procesador x86 corregirá automáticamente el acceso a la memoria no alineado, con una penalización en el rendimiento. No se genera ninguna excepción.
Un acceso a memoria se considera alineado si la dirección es un entero múltiplo del tamaño del objeto. Por ejemplo, todos los accesos BYTE están alineados (todo es un entero múltiplo de 1), los accesos de WORD a direcciones uniformes están alineados y las direcciones DWORD deben ser un múltiplo de 4 para alinearse.
El prefijo de bloqueo no debe usarse para accesos a memoria desalineados.