fbpx
Wikipedia

Lenguaje ensamblador x86

El lenguaje ensamblador x86 es la familia de los lenguajes ensambladores para los procesadores de la familia x86 introducida en abril de 1972, que incluye desde los procesadores Intel 8086 y 8088, pasando por los Pentium de Intel y los Athlon de AMD y llegando hasta los últimos procesadores x86 de estas compañías. Como el resto de lenguajes ensambladores, usa una serie de mnemotécnicos para representar las operaciones fundamentales que el procesador puede realizar. Los compiladores a menudo producen código ensamblador como un paso intermedio cuando traducen un programa de alto nivel a código máquina. Considerado como un lenguaje de programación de bajo nivel y específico para cada máquina. Aunque algunas veces es usado para software de aplicación de sistemas de ventanas, los lenguajes ensambladores son utilizados principalmente en aplicaciones críticas como sistemas de arranque, Sistemas Operativos, núcleos y en controladoras de dispositivos, así como en sistemas en tiempo real o pequeños sistemas embebidos.

Historia

Los procesadores Intel 8086 y 8088 fueron los primeros de 16 bits en tener un conjunto de instrucciones conocido actualmente como x86. Fueron un paso evolutivo en comparación con la generación anterior de CPUs de 8 bits, como el 8080 y heredaron muchas características e instrucciones, las cuales fueron extendidas para trabajar con 16 bits. Ambos CPUs contenían un bus de direcciones de 20 bits y un grupo de registros internos de 16 bits. El 8086 tenía un bus de datos externo de 16 bits y el 8088 uno de 8 bits. El 8088 estaba previsto como una versión de bajo coste del 8086. El lenguaje ensamblador del x86 también cubre las diferentes versiones de CPU que siguieron, como el 80188 y 80186, 80286, 80386, 80486, Pentium, etc, de Intel, también como los CPU de AMD y Cyrix como los procesadores 5x86 y K6, y el NEC V20 de NEC. El término x86 aplica a cualquier CPU pueda correr el lenguaje ensamblador original (usualmente también correrá por lo menos algunas de las extensiones.)

El moderno conjunto de instrucciones x86 es un superconjunto de las instrucciones del 8086 y el 8088 y una serie de extensiones a este conjunto de instrucciones que comenzaron con el microprocesador Intel 8008. Existe casi una completa compatibilidad binaria desde los chips Intel 8088 y 8086 con los modernos procesadores Intel Pentium 4, Intel Core Duo, Intel Core i7, AMD Athlon 64, AMD Opteron, hasta la generación actual de microprocesadores x86, aunque existen algunas excepciones. Esta compatibilidad se logra gracias al uso de 2 conjuntos de instrucciones de arquitecturas, lo cual es comúnmente criticado. La compatibilidad de los programas en lenguaje ensamblador con procesadores más antiguos sólo es posible cuando el programa no incluye instrucciones solo disponibles en los procesadores nuevos.

Generalmente, cada nuevo procesador de la serie tiene unas cuantas instrucciones adicionales y más capacidades y mejor desempeño que los anteriores. El 286 agregó unas cuantas instrucciones. el modo protegido y capacidad multitarea, el 386 extendió la plataforma de 16 a 32 bits, añadió algunas instrucciones e hizo al conjunto de instrucciones más ortogonal, haciéndolo la base de los procesadores siguientes hasta que aparecieron los de 64 bits. Con el 486 se incorporó el coprocesador numérico en el propio chip, otros procesadores posteriores agregaron instrucciones para acelerar el procesamiento multimedia, multithreading, 2 o más núcleos, 64 bits, etc.

Mnemotécnicos y códigos de operación

Cada instrucción del x86 está representada por un mnemotécnico, que traduce directamente a una serie de bytes la representación de la instrucción, llamada código de operación. Por ejemplo, la instrucción NOP se codifica como 0x90 y la instrucción HLT como 0xF4. Algunos códigos de operación no tienen nombres mnemotécnicos y no están documentados. Diferentes procesadores en la familia del x86 pueden interpretar códigos de operación indocumentados de forma distinta, haciendo que un mismo programa se comporte de forma distinta en diferentes procesadores.

Sintaxis

El lenguaje ensamblador x86 tiene 2 vertientes diferentes en cuanto a su sintaxis de programación: sintaxis Intel, usada en sus inicios para la documentación de la plataforma x86, y sintaxis AT&T .[1]​ La sintaxis Intel es la dominante en la plataforma Windows, mientras que en Unix/Linux ambas son utilizadas aunque GCC solo soportaba la sintaxis AT&T en sus primeras versiones.

La mayoría de los ensambladores x86 utilizan la sintaxis de Intel, como MASM, TASM, NASM, FASM and YASM. GAS ha soportado ambas sintaxis desde la versión 2.10 a través de la directiva “.intel_sintax”.[1][2][3]

Registros

Los procesadores x86 tienen una serie de registros disponibles para almacenar información. Este conjunto de registros son conocidos como registros de propósito general o GPR (del inglés General Purpose Register).

Además de los GPR, existen adicionalmente:

El registro IP apunta a la posición del programa en la que el procesador está ejecutando el código. EL registro no puede ser accedido por el programador directamente.

Los registros del x86 pueden ser usados mediante la instrucción MOV. Por ejemplo:

 mov ax, 1234h mov bx, ax 

copia el valor 1234h en el registro ax y en la siguiente línea copia el valor de ax en el registro bx.

Direccionamiento segmentado

La arquitectura x86 utiliza el método de segmentación para direccionar memoria, en lugar del método lineal usado en otras arquitecturas. La segmentación implica descomponer una dirección lineal en dos partes – un “segmento” y un “desplazamiento”. El segmento apunta al inicio de un bloque de 64K direcciones y el desplazamiento indica la diferencia entre el lugar apuntado y el inicio del segmento.

Este modo de direccionamiento se utiliza para aprovechar las características del procesador. El problema estaba en que los registros internos del procesador eran de 16 bits, mientras que el bus de direcciones era de 20. Faltaban por tanto 4 bits para poder aprovechar al máximo las capacidades de direccionamiento del procesador. Para resolver esto, cada dirección de memoria será especificada como un segmento y un desplazamiento dentro de ese segmento. Esta solución divide la memoria en segmentos de 64 K, lo cual limitó bastante los diseños de los procesadores posteriores de la familia (Intel 80286, Intel 80386, etc.); aunque posteriormente se idearon métodos para resolver este problema, como la memoria extendida (no compatible con el x86/x88). Con esto se consigue que el procesador sea capaz de direccionar 1,048,576 direcciones de 1 byte, o lo que es lo mismo, 1Mbyte.

Se utilizan dos registros para el direccionamiento: uno para indicar el segmento, y el otro para indicar el desplazamiento.

Para obtener la dirección de memoria (dirección efectiva): se toma el valor de registro de segmento, se desplaza 4 bits a la izquierda (multiplicación por 16), y se le suma el valor del desplazamiento.

Ejemplo: Si DS contiene 0x000A y DX contiene 0x5F0A, apuntarían a la dirección de memoria: 0x000A * 0x10 + 0x5F0A = 0x5FAA

Para referirse a una dirección con un segmento y un desplazamiento, se utiliza la notación segmento:desplazamiento . En el ejemplo anterior, la dirección lineal 0x5FAA se nombraría como 0x000A:0x5F0A, o si las dos partes se encuentran almacenadas en los registros mencionados, se podría utilizar el par DS:DX. Hay una serie de combinaciones especiales entre registros de segmentos y registros generales que apuntan a direcciones importantes:

  • CS:IP apunta a la siguiente dirección de código en la que se posicionará el procesador.
  • SS:SP apunta al último elemento apilado en la pila.
  • DS:SI se suele usar para apuntar información que va a ser copiada a ES:DI.

Modos de ejecución

El procesador soporta numerosos modos de operación para código x86, en los cuales no todas las instrucciones están disponibles. Un sub-repertorio de instrucciones de 16-bit está disponible en “modo real” (disponible en todos los procesadores x86), “modo protegido 16-bit” (disponible desde el Intel 80286), o en el “modo v86” (disponible desde el Intel 80386). Por su parte, las instrucciones de 32-bits están disponibles para el “modo protegido 32-bit” y para el “modo heredado” (disponible con las extensiones de 64 bits). El repertorio de instrucciones parte de ideas similares en cada modo, pero da lugar a distintas formas de acceso a memoria y de este modo emplea estrategias de programación diferentes.

Los modos en los que el código x86 puede ser ejecutado son:

Tipos de instrucciones

En general, las características del repertorio de instrucciones x86 son:

  • Una codificación compacta
    • Longitud variable y alineación independiente (codificación en formato little endian)
    • Instrucciones de una y dos direcciones, en las que el primer operando es también el destino.
    • Operandos de memoria como origen y destino compatibles (normalmente utilizados para leer/escribir elementos de la pila usando pequeños desplazamientos inmediatos)
    • Uso de los registros generales e implícitos; a pesar de que los siete registros generales (contando 'ebp') pueden ser utilizados como acumuladores o para direccionar, la mayoría de ellos son también usados implícitamente por algunas instrucciones especiales; los registros afectados deben conservar temporalmente la información, si están siendo utilizados durante el uso de estas instrucciones (normalmente mediante el uso de la pila).
  • Produce flags condicionales implícitamente mediante el uso de la mayoría de instrucciones de la ALU
  • Soporta varios modos de direccionamiento
  • Incluye punto flotante en una pila de registros
  • Contiene soporte especial para instrucciones atómicas (XCHG, CMPXCHG(8B), XADD e instrucciones enteras combinadas con el prefijo LOCK)
  • Instrucciones SIMD (instrucciones que aplican una misma operación sobre un conjunto más o menos grande de datos)

Instrucciones de pila

La pila es un segmento que es de suma utilidad en estos microprocesadores. En él se almacenan valores temporales como las variables locales de las funciones, o las direcciones de retorno de estas. Una función no es más que una subrutina, o un fragmento de código al que se le llama generalmente varias veces desde el programa principal, o desde una función jerárquicamente superior. Cuando se llama a una función se hace un mero salto al punto donde empieza ese código. Sin embargo esa subrutina puede ser llamada desde distintos puntos del programa principal, por lo que hay que almacenar en algún sitio la dirección desde donde se hace la llamada, cada vez que esa llamada tiene lugar, para que al finalizar la ejecución de la función se retome el programa donde se dejó. Esta dirección puede almacenarse en un sitio fijo (como hacen algunos microcontroladores), pero eso tiene el inconveniente de que si esa función a su vez llama a otra función (¡o a sí misma!) podemos sobreescribir la dirección de retorno anterior, y al regresar de la segunda llamada, no podríamos volver desde la primera. Además, es deseable que la función guarde los valores de todos los registros que vaya a usar en algún sitio, para que el que la llame no tenga que preocuparse de ello (pues si sabe que los registros van a ser modificados, pero no sabe cuáles, los guardará todos por si acaso). Todas estas cosas, y algunas más, se hacen con la pila.

El segmento de pila está indicado por SS, y el desplazamiento dentro del segmento, por SP.

Cuando arranca el programa, SP apunta al final del segmento de pila. Para almacenar información en la pila se decrementa SP para que apunte un poco más arriba y se copia a esa posición de memoria, SS:SP. Para sacarlo, copiamos lo que haya en SS:SP a nuestro destino, e incrementamos el puntero.

Como con todo lo que se hace con frecuencia, hay dispuestas instrucciones propias para el manejo de la pila. Las dos básicas son PUSH origen (empujar) y POP destino (sacar). La primera decrementa el puntero de pila y copia a la dirección apuntada por él (SS:SP) el operando origen (de tamaño múltiplo de 16 bits), mientras que la segunda almacena el contenido de la pila (elemento apuntado por SS:SP) en destino y altera el puntero en consecuencia. Si el operando es de 16 bits se modifica en 2 unidades, de 32 en 4, etc. Lo que se incrementa/decrementa es siempre SP, claro, porque SS nos indica dónde está ubicado el segmento de pila.

La instrucción ret size se utiliza para recuperar de la pila los valores de IP o de CS e IP dependiendo del caso. Al salir de un procedimiento es necesario dejar la pila como estaba; para ello podemos utilizar la instrucción pop, o bien ejecutar la instrucción ret size donde size es el número de posiciones que deben descartarse de la pila.

Instrucciones de la ALU con enteros

El ensamblador x86 tiene las operaciones matemáticas estándar, como add, sub, mul, y idiv; los operadores lógicos and, or, xor, neg; desplazamientos, sal/sar, shl/shr; rotación con/sin acarreo, rcl/rcr, rol/ror, un complemento de instrucciones aritméticas BCD, aaa, aad, daa y otras.

Instrucciones en coma flotante

El ensamblador x86 incluye instrucciones para pila basada en unidades en coma flotante. Entre ellas se encuentran la suma, resta, negación, multiplicación, división, resto, raíces cuadradas, truncamiento entero y truncamiento fraccionado. Las operaciones también incluyen instrucciones de conversión con las que se puede cargar o almacenar un valor desde memoria a cualquiera de los siguientes formatos: BCD, entero de 32 bits, entero de 64 bits, punto flotante de 32 bits, punto flotante de 64 bits u 80 bits. El x86 también incluye funciones como seno, coseno, tangente, arco tangente, exponente con base 2 y logaritmos de base 2, 10 o e.

La conversión de instrucciones al formato del registro de pila es normalmente F (OP) st, st(*) o F (OP) st(*), st, donde st es equivalente a st(0), y st(*) es uno de los 8 registros de pila (st(0), st(1), ..., st(7)). Como con los enteros, el primer operando actúa como primera fuente y como operando destino. La suma, resta, multiplicación, división, almacenamiento y comparación de instrucciones incluye modos de instrucción que se encargan de desapilar una vez completada la operación.

En el caso de que no exista ningún operando, supone destino = ST(1), fuente = ST y se hace además pop sobre la pila, de modo que el resultado se sitúa en lo alto de la pila. Por ejemplo, FADD calcula ST(1)=ST(1)+ST y hace pop sobre la pila (incrementando en uno el puntero de pila), con lo que el nuevo elemento en lo alto de la pila contiene el resultado.

Instrucciones SIMD

Los procesadores x86 modernos tienen instrucciones SIMD, que permiten realizar la misma operación en paralelo sobre diversos valores codificados en un registro SIMD. Varias tecnologías de instrucciones soportan diferentes operaciones sobre distintos repertorios de registros, pero todos (desde MMX hasta SSE4,2) incluyen cálculo general sobre aritmética entera o en coma flotante (suma, resta, multiplicación, desplazamiento, minimización, maximización, comparación, división o raíz cuadrada). Por ejemplo, PADDW MM0, MM1 aplica 4 sumas paralelas de enteros de 16 bits (debido a la W que indica que son palabras) de los valores de mm0 hasta mm1, y los almacena en mm0. SSE también incluye el modo en coma flotante en el que el primer valor de los registros está modificado (expandido en el SSE2).

Instrucciones de manipulación de datos

El procesador x86 también incluye modos de direccionamiento complejo para direccionar memoria con un desplazamiento inmediato, un registro, un registro con desplazamiento, un registro escalado con o sin desplazamiento y un registro con desplazamiento opcional y otro registro escalado. Entonces por ejemplo, uno puede codificar mov eax, [Table + ebx + esi*4] como una instrucción simple que carga 32 bits de datos desde la dirección localizada en el desplazamiento (Table + ebx + esi * 4) desde el segmento DS, y almacenarla en el registro eax. En general, los procesadores x86 pueden cargar y usar memoria ajustada al tamaño del cualquier registro sobre el que está operando.

Los repertorios de instrucciones x86 incluyen instrucciones de carga, almacenamiento y movimiento de cadenas (LODS, STOS and MOVS) que representan cada operación con un tamaño especificado (B para bytes, W para palabras de 16-bits, D para dobles palabras de 32 bits) e incrementan/decrementan el registro de dirección implícito (SI para LODS, DI para STOS y ambos para MOVS). Para la carga y almacenamiento, el registro destino/fuente implícito es el AL, AX o EAX, dependiendo del tamaño. El segmento usado implícitamente es DS para LODS, ES para STOS y ambos para MOVS.

La pila está implementada con un puntero que disminuye (push) y aumenta (pop) implícitamente. En el modo de 16 bits, este puntero se corresponde a la dirección SS:[SP], en 32- bits sería SS:[ESP] y en 64-bits [RSP]. El puntero de pila se encarga de apuntar al último valor almacenado, asumiendo que su tamaño coincide con el modo del procesador (12, 32 o 64 bits) para que coincida con el ancho por defecto de las instrucciones PUSH/POP/CALL/RET. Otras instrucciones para manipular la pila son PUSHF y POPF, que se utilizan para almacenar y recuperar el registro de FLAGS, almacenándolo o retirándolo de la parte alta de la pila.

Flujo del programa

El ensamblador x86 tiene una operación de salto incondicional, jmp, que admite una dirección inmediata, un registro o una dirección indirecta mediante registro como parámetro.

También se permiten los saltos condicionales, como je (saltar cuando hay igualdad), jne (saltar en desigualdad), jg (saltar si 'mayor que', con signo), jl (saltar si 'menor que', con signo), ja (saltar si 'superior/mayor que', sin signo), jb (saltar si 'inferior/menor que', sin signo). Estas operaciones condicionales se basan en el estado específico de los bits del registro de FLAGS. La mayoría de las operaciones aritméticas se encargan de actualizar estos flags según su resultado. Las instrucciones de comparación cmp y test modifican el estado del registro de flags sin modificar los operandos. Las comparaciones en coma flotante se realizan mediante las instrucciones FCOM o FICOM.

Cada operación de salto tiene tres formas diferentes, dependiendo del tamaño del operando. Un salto sort usa un operando con signo de 8bits, que se corresponde con el desplazamiento relativo a la instrucción actual. El salto near es similar al corto pero usa un operando de 16 o 32 bits con signo. Un salto far utiliza el segmento entero base:desplazamiento como una dirección total. También hay forma indirecta e indexada para cada uno de ellos.

Además de las operaciones de salto, existen las instrucciones call y ret para llamar y regresar de una subrutina. Antes de transferir el control a la subrutina, call apila el desplazamiento de la instrucción siguiente a la llamada en la pila; ret desapila este valor de la pila, y transfiere el control a la dirección que indicaba el valor desapilado. En el caso de que se trate de una llamada a una función lejana, far call, la base también se apila siguiendo al desplazamiento.

Existen algunas instrucciones similares, como la interrupción int. Ésta activa el procedimiento de interrupción especificado por el operando y guarda el valor del registro de flags en la pila . La activación de la interrupción se realiza mediante una llamada a un procedimiento lejano (far call), pero en lugar de una dirección, utiliza ese vector de interrupción. La dirección de este vector se calcula multiplicando por 4 el operando, que es un valor entre 0 y 255. Normalmente, el manejador de interrupciones guarda todos los registros que el procesador está usando, a menos que estén siendo usados para almacenar el resultado de una operación. Por otro lado, para volver al programa desde una interrupción se utiliza iret, que se encarga de restablecer el valor de los flags. Las interrupciones ligeras descritas anteriormente son usadas por algunos sistemas operativos para las llamadas del sistema, y pueden también ser usadas para depurar los manejadores de interrupciones fuertes. Estas son provocadas por eventos hardware externos, y deben mantener los valores de todos los registros ya que el estado del programa en ejecución se desconoce. En el Modo Protegido, las interrupciones pueden ser activadas por el Sistema Operativo para realizar un cambio de tarea, almacenando automáticamente todos los registros de la tarea en ejecución.

Véase también

Ensambladores

Desensambladores

Depuradores

Microprocesadores

Antecesores (las raíces de la arquitectura x86):

  • 1971 Datapoint 2200. Terminal de computadora programable. Su conjunto de instrucciones es la base de los procesadores Intel desde el 8008 al 8085, los cuales a su vez son los antecesores de la arquitectura x86
  • 1972 Intel 8008
  • 1974 Intel 8080
  • 1977 Intel 8085

Algunos microprocesadores de la Arquitectura x86:

Referencias

  1. Ram Narayam (17 de octubre de 2007). . Archivado desde el original el 14 de junio de 2008. Consultado el 2 de julio de 2008. 
  2. Randall Hyde. . Archivado desde el original el 15 de mayo de 2008. Consultado el 18 de mayo de 2008. 
  3. «GNU Assembler News, v2.1 supports Intel syntax». 4 de abril de 2008. Consultado el 2 de julio de 2008. 

Seguir leyendo

  • Intel 64 and IA-32 Software Developer Manuals
  • (PDF)
  • (PDF)
  • (PDF)
  • (PDF)
  • (PDF)

Enlaces externos

  • Novice and Advanced Assembly resources for x86 Platform
  • 80x86 instruction set
  • - A comparison of x86 assemblers
  •   Datos: Q297868

lenguaje, ensamblador, lenguaje, ensamblador, familia, lenguajes, ensambladores, para, procesadores, familia, introducida, abril, 1972, incluye, desde, procesadores, intel, 8086, 8088, pasando, pentium, intel, athlon, llegando, hasta, últimos, procesadores, es. El lenguaje ensamblador x86 es la familia de los lenguajes ensambladores para los procesadores de la familia x86 introducida en abril de 1972 que incluye desde los procesadores Intel 8086 y 8088 pasando por los Pentium de Intel y los Athlon de AMD y llegando hasta los ultimos procesadores x86 de estas companias Como el resto de lenguajes ensambladores usa una serie de mnemotecnicos para representar las operaciones fundamentales que el procesador puede realizar Los compiladores a menudo producen codigo ensamblador como un paso intermedio cuando traducen un programa de alto nivel a codigo maquina Considerado como un lenguaje de programacion de bajo nivel y especifico para cada maquina Aunque algunas veces es usado para software de aplicacion de sistemas de ventanas los lenguajes ensambladores son utilizados principalmente en aplicaciones criticas como sistemas de arranque Sistemas Operativos nucleos y en controladoras de dispositivos asi como en sistemas en tiempo real o pequenos sistemas embebidos Indice 1 Historia 2 Mnemotecnicos y codigos de operacion 2 1 Sintaxis 3 Registros 4 Direccionamiento segmentado 5 Modos de ejecucion 6 Tipos de instrucciones 6 1 Instrucciones de pila 6 2 Instrucciones de la ALU con enteros 6 3 Instrucciones en coma flotante 6 4 Instrucciones SIMD 6 5 Instrucciones de manipulacion de datos 7 Flujo del programa 8 Vease tambien 8 1 Ensambladores 8 2 Desensambladores 8 3 Depuradores 8 4 Microprocesadores 9 Referencias 10 Seguir leyendo 11 Enlaces externosHistoria EditarLos procesadores Intel 8086 y 8088 fueron los primeros de 16 bits en tener un conjunto de instrucciones conocido actualmente como x86 Fueron un paso evolutivo en comparacion con la generacion anterior de CPUs de 8 bits como el 8080 y heredaron muchas caracteristicas e instrucciones las cuales fueron extendidas para trabajar con 16 bits Ambos CPUs contenian un bus de direcciones de 20 bits y un grupo de registros internos de 16 bits El 8086 tenia un bus de datos externo de 16 bits y el 8088 uno de 8 bits El 8088 estaba previsto como una version de bajo coste del 8086 El lenguaje ensamblador del x86 tambien cubre las diferentes versiones de CPU que siguieron como el 80188 y 80186 80286 80386 80486 Pentium etc de Intel tambien como los CPU de AMD y Cyrix como los procesadores 5x86 y K6 y el NEC V20 de NEC El termino x86 aplica a cualquier CPU pueda correr el lenguaje ensamblador original usualmente tambien correra por lo menos algunas de las extensiones El moderno conjunto de instrucciones x86 es un superconjunto de las instrucciones del 8086 y el 8088 y una serie de extensiones a este conjunto de instrucciones que comenzaron con el microprocesador Intel 8008 Existe casi una completa compatibilidad binaria desde los chips Intel 8088 y 8086 con los modernos procesadores Intel Pentium 4 Intel Core Duo Intel Core i7 AMD Athlon 64 AMD Opteron hasta la generacion actual de microprocesadores x86 aunque existen algunas excepciones Esta compatibilidad se logra gracias al uso de 2 conjuntos de instrucciones de arquitecturas lo cual es comunmente criticado La compatibilidad de los programas en lenguaje ensamblador con procesadores mas antiguos solo es posible cuando el programa no incluye instrucciones solo disponibles en los procesadores nuevos Generalmente cada nuevo procesador de la serie tiene unas cuantas instrucciones adicionales y mas capacidades y mejor desempeno que los anteriores El 286 agrego unas cuantas instrucciones el modo protegido y capacidad multitarea el 386 extendio la plataforma de 16 a 32 bits anadio algunas instrucciones e hizo al conjunto de instrucciones mas ortogonal haciendolo la base de los procesadores siguientes hasta que aparecieron los de 64 bits Con el 486 se incorporo el coprocesador numerico en el propio chip otros procesadores posteriores agregaron instrucciones para acelerar el procesamiento multimedia multithreading 2 o mas nucleos 64 bits etc Mnemotecnicos y codigos de operacion EditarCada instruccion del x86 esta representada por un mnemotecnico que traduce directamente a una serie de bytes la representacion de la instruccion llamada codigo de operacion Por ejemplo la instruccion NOP se codifica como 0x90 y la instruccion HLT como 0xF4 Algunos codigos de operacion no tienen nombres mnemotecnicos y no estan documentados Diferentes procesadores en la familia del x86 pueden interpretar codigos de operacion indocumentados de forma distinta haciendo que un mismo programa se comporte de forma distinta en diferentes procesadores Sintaxis Editar El lenguaje ensamblador x86 tiene 2 vertientes diferentes en cuanto a su sintaxis de programacion sintaxis Intel usada en sus inicios para la documentacion de la plataforma x86 y sintaxis AT amp T 1 La sintaxis Intel es la dominante en la plataforma Windows mientras que en Unix Linux ambas son utilizadas aunque GCC solo soportaba la sintaxis AT amp T en sus primeras versiones La mayoria de los ensambladores x86 utilizan la sintaxis de Intel como MASM TASM NASM FASM and YASM GAS ha soportado ambas sintaxis desde la version 2 10 a traves de la directiva intel sintax 1 2 3 Registros EditarLos procesadores x86 tienen una serie de registros disponibles para almacenar informacion Este conjunto de registros son conocidos como registros de proposito general o GPR del ingles General Purpose Register Ademas de los GPR existen adicionalmente registros de segmento CS DS ES FS GS SS otros registros IP Registro de estado registros extra MMX 3DNow SSE etc El registro IP apunta a la posicion del programa en la que el procesador esta ejecutando el codigo EL registro no puede ser accedido por el programador directamente Los registros del x86 pueden ser usados mediante la instruccion MOV Por ejemplo mov ax 1234h mov bx ax copia el valor 1234h en el registro ax y en la siguiente linea copia el valor de ax en el registro bx Direccionamiento segmentado EditarLa arquitectura x86 utiliza el metodo de segmentacion para direccionar memoria en lugar del metodo lineal usado en otras arquitecturas La segmentacion implica descomponer una direccion lineal en dos partes un segmento y un desplazamiento El segmento apunta al inicio de un bloque de 64K direcciones y el desplazamiento indica la diferencia entre el lugar apuntado y el inicio del segmento Este modo de direccionamiento se utiliza para aprovechar las caracteristicas del procesador El problema estaba en que los registros internos del procesador eran de 16 bits mientras que el bus de direcciones era de 20 Faltaban por tanto 4 bits para poder aprovechar al maximo las capacidades de direccionamiento del procesador Para resolver esto cada direccion de memoria sera especificada como un segmento y un desplazamiento dentro de ese segmento Esta solucion divide la memoria en segmentos de 64 K lo cual limito bastante los disenos de los procesadores posteriores de la familia Intel 80286 Intel 80386 etc aunque posteriormente se idearon metodos para resolver este problema como la memoria extendida no compatible con el x86 x88 Con esto se consigue que el procesador sea capaz de direccionar 1 048 576 direcciones de 1 byte o lo que es lo mismo 1Mbyte Se utilizan dos registros para el direccionamiento uno para indicar el segmento y el otro para indicar el desplazamiento Para obtener la direccion de memoria direccion efectiva se toma el valor de registro de segmento se desplaza 4 bits a la izquierda multiplicacion por 16 y se le suma el valor del desplazamiento Ejemplo Si DS contiene 0x000A y DX contiene 0x5F0A apuntarian a la direccion de memoria 0x000A 0x10 0x5F0A 0x5FAAPara referirse a una direccion con un segmento y un desplazamiento se utiliza la notacion segmento desplazamiento En el ejemplo anterior la direccion lineal 0x5FAA se nombraria como 0x000A 0x5F0A o si las dos partes se encuentran almacenadas en los registros mencionados se podria utilizar el par DS DX Hay una serie de combinaciones especiales entre registros de segmentos y registros generales que apuntan a direcciones importantes CS IP apunta a la siguiente direccion de codigo en la que se posicionara el procesador SS SP apunta al ultimo elemento apilado en la pila DS SI se suele usar para apuntar informacion que va a ser copiada a ES DI Modos de ejecucion EditarEl procesador soporta numerosos modos de operacion para codigo x86 en los cuales no todas las instrucciones estan disponibles Un sub repertorio de instrucciones de 16 bit esta disponible en modo real disponible en todos los procesadores x86 modo protegido 16 bit disponible desde el Intel 80286 o en el modo v86 disponible desde el Intel 80386 Por su parte las instrucciones de 32 bits estan disponibles para el modo protegido 32 bit y para el modo heredado disponible con las extensiones de 64 bits El repertorio de instrucciones parte de ideas similares en cada modo pero da lugar a distintas formas de acceso a memoria y de este modo emplea estrategias de programacion diferentes Los modos en los que el codigo x86 puede ser ejecutado son Modo real 16 bit Modo protegido 16 bit y 32 bit Modo 8086 virtual 16 bit System Management Mode 16 bit Modo largo 64 bit Tipos de instrucciones EditarEn general las caracteristicas del repertorio de instrucciones x86 son Una codificacion compacta Longitud variable y alineacion independiente codificacion en formato little endian Instrucciones de una y dos direcciones en las que el primer operando es tambien el destino Operandos de memoria como origen y destino compatibles normalmente utilizados para leer escribir elementos de la pila usando pequenos desplazamientos inmediatos Uso de los registros generales e implicitos a pesar de que los siete registros generales contando ebp pueden ser utilizados como acumuladores o para direccionar la mayoria de ellos son tambien usados implicitamente por algunas instrucciones especiales los registros afectados deben conservar temporalmente la informacion si estan siendo utilizados durante el uso de estas instrucciones normalmente mediante el uso de la pila Produce flags condicionales implicitamente mediante el uso de la mayoria de instrucciones de la ALU Soporta varios modos de direccionamiento Incluye punto flotante en una pila de registros Contiene soporte especial para instrucciones atomicas XCHG CMPXCHG 8B XADD e instrucciones enteras combinadas con el prefijo LOCK Instrucciones SIMD instrucciones que aplican una misma operacion sobre un conjunto mas o menos grande de datos Instrucciones de pila Editar La pila es un segmento que es de suma utilidad en estos microprocesadores En el se almacenan valores temporales como las variables locales de las funciones o las direcciones de retorno de estas Una funcion no es mas que una subrutina o un fragmento de codigo al que se le llama generalmente varias veces desde el programa principal o desde una funcion jerarquicamente superior Cuando se llama a una funcion se hace un mero salto al punto donde empieza ese codigo Sin embargo esa subrutina puede ser llamada desde distintos puntos del programa principal por lo que hay que almacenar en algun sitio la direccion desde donde se hace la llamada cada vez que esa llamada tiene lugar para que al finalizar la ejecucion de la funcion se retome el programa donde se dejo Esta direccion puede almacenarse en un sitio fijo como hacen algunos microcontroladores pero eso tiene el inconveniente de que si esa funcion a su vez llama a otra funcion o a si misma podemos sobreescribir la direccion de retorno anterior y al regresar de la segunda llamada no podriamos volver desde la primera Ademas es deseable que la funcion guarde los valores de todos los registros que vaya a usar en algun sitio para que el que la llame no tenga que preocuparse de ello pues si sabe que los registros van a ser modificados pero no sabe cuales los guardara todos por si acaso Todas estas cosas y algunas mas se hacen con la pila El segmento de pila esta indicado por SS y el desplazamiento dentro del segmento por SP Cuando arranca el programa SP apunta al final del segmento de pila Para almacenar informacion en la pila se decrementa SP para que apunte un poco mas arriba y se copia a esa posicion de memoria SS SP Para sacarlo copiamos lo que haya en SS SP a nuestro destino e incrementamos el puntero Como con todo lo que se hace con frecuencia hay dispuestas instrucciones propias para el manejo de la pila Las dos basicas son PUSH origen empujar y POP destino sacar La primera decrementa el puntero de pila y copia a la direccion apuntada por el SS SP el operando origen de tamano multiplo de 16 bits mientras que la segunda almacena el contenido de la pila elemento apuntado por SS SP en destino y altera el puntero en consecuencia Si el operando es de 16 bits se modifica en 2 unidades de 32 en 4 etc Lo que se incrementa decrementa es siempre SP claro porque SS nos indica donde esta ubicado el segmento de pila La instruccion ret size se utiliza para recuperar de la pila los valores de IP o de CS e IP dependiendo del caso Al salir de un procedimiento es necesario dejar la pila como estaba para ello podemos utilizar la instruccion pop o bien ejecutar la instruccion ret size donde size es el numero de posiciones que deben descartarse de la pila Instrucciones de la ALU con enteros Editar El ensamblador x86 tiene las operaciones matematicas estandar como add sub mul y idiv los operadores logicos and or xor neg desplazamientos sal sar shl shr rotacion con sin acarreo rcl rcr rol ror un complemento de instrucciones aritmeticas BCD aaa aad daa y otras Instrucciones en coma flotante Editar El ensamblador x86 incluye instrucciones para pila basada en unidades en coma flotante Entre ellas se encuentran la suma resta negacion multiplicacion division resto raices cuadradas truncamiento entero y truncamiento fraccionado Las operaciones tambien incluyen instrucciones de conversion con las que se puede cargar o almacenar un valor desde memoria a cualquiera de los siguientes formatos BCD entero de 32 bits entero de 64 bits punto flotante de 32 bits punto flotante de 64 bits u 80 bits El x86 tambien incluye funciones como seno coseno tangente arco tangente exponente con base 2 y logaritmos de base 2 10 o e La conversion de instrucciones al formato del registro de pila es normalmente F OP st st o F OP st st donde st es equivalente a st 0 y st es uno de los 8 registros de pila st 0 st 1 st 7 Como con los enteros el primer operando actua como primera fuente y como operando destino La suma resta multiplicacion division almacenamiento y comparacion de instrucciones incluye modos de instruccion que se encargan de desapilar una vez completada la operacion En el caso de que no exista ningun operando supone destino ST 1 fuente ST y se hace ademas pop sobre la pila de modo que el resultado se situa en lo alto de la pila Por ejemplo FADD calcula ST 1 ST 1 ST y hace pop sobre la pila incrementando en uno el puntero de pila con lo que el nuevo elemento en lo alto de la pila contiene el resultado Instrucciones SIMD Editar Los procesadores x86 modernos tienen instrucciones SIMD que permiten realizar la misma operacion en paralelo sobre diversos valores codificados en un registro SIMD Varias tecnologias de instrucciones soportan diferentes operaciones sobre distintos repertorios de registros pero todos desde MMX hasta SSE4 2 incluyen calculo general sobre aritmetica entera o en coma flotante suma resta multiplicacion desplazamiento minimizacion maximizacion comparacion division o raiz cuadrada Por ejemplo PADDW MM0 MM1 aplica 4 sumas paralelas de enteros de 16 bits debido a la W que indica que son palabras de los valores de mm0 hasta mm1 y los almacena en mm0 SSE tambien incluye el modo en coma flotante en el que el primer valor de los registros esta modificado expandido en el SSE2 Instrucciones de manipulacion de datos Editar El procesador x86 tambien incluye modos de direccionamiento complejo para direccionar memoria con un desplazamiento inmediato un registro un registro con desplazamiento un registro escalado con o sin desplazamiento y un registro con desplazamiento opcional y otro registro escalado Entonces por ejemplo uno puede codificar mov eax Table ebx esi 4 como una instruccion simple que carga 32 bits de datos desde la direccion localizada en el desplazamiento Table ebx esi 4 desde el segmento DS y almacenarla en el registro eax En general los procesadores x86 pueden cargar y usar memoria ajustada al tamano del cualquier registro sobre el que esta operando Los repertorios de instrucciones x86 incluyen instrucciones de carga almacenamiento y movimiento de cadenas LODS STOS and MOVS que representan cada operacion con un tamano especificado B para bytes W para palabras de 16 bits D para dobles palabras de 32 bits e incrementan decrementan el registro de direccion implicito SI para LODS DI para STOS y ambos para MOVS Para la carga y almacenamiento el registro destino fuente implicito es el AL AX o EAX dependiendo del tamano El segmento usado implicitamente es DS para LODS ES para STOS y ambos para MOVS La pila esta implementada con un puntero que disminuye push y aumenta pop implicitamente En el modo de 16 bits este puntero se corresponde a la direccion SS SP en 32 bits seria SS ESP y en 64 bits RSP El puntero de pila se encarga de apuntar al ultimo valor almacenado asumiendo que su tamano coincide con el modo del procesador 12 32 o 64 bits para que coincida con el ancho por defecto de las instrucciones PUSH POP CALL RET Otras instrucciones para manipular la pila son PUSHF y POPF que se utilizan para almacenar y recuperar el registro de FLAGS almacenandolo o retirandolo de la parte alta de la pila Flujo del programa EditarEl ensamblador x86 tiene una operacion de salto incondicional jmp que admite una direccion inmediata un registro o una direccion indirecta mediante registro como parametro Tambien se permiten los saltos condicionales como je saltar cuando hay igualdad jne saltar en desigualdad jg saltar si mayor que con signo jl saltar si menor que con signo ja saltar si superior mayor que sin signo jb saltar si inferior menor que sin signo Estas operaciones condicionales se basan en el estado especifico de los bits del registro de FLAGS La mayoria de las operaciones aritmeticas se encargan de actualizar estos flags segun su resultado Las instrucciones de comparacion cmp y test modifican el estado del registro de flags sin modificar los operandos Las comparaciones en coma flotante se realizan mediante las instrucciones FCOM o FICOM Cada operacion de salto tiene tres formas diferentes dependiendo del tamano del operando Un salto sort usa un operando con signo de 8bits que se corresponde con el desplazamiento relativo a la instruccion actual El salto near es similar al corto pero usa un operando de 16 o 32 bits con signo Un salto far utiliza el segmento entero base desplazamiento como una direccion total Tambien hay forma indirecta e indexada para cada uno de ellos Ademas de las operaciones de salto existen las instrucciones call y ret para llamar y regresar de una subrutina Antes de transferir el control a la subrutina call apila el desplazamiento de la instruccion siguiente a la llamada en la pila ret desapila este valor de la pila y transfiere el control a la direccion que indicaba el valor desapilado En el caso de que se trate de una llamada a una funcion lejana far call la base tambien se apila siguiendo al desplazamiento Existen algunas instrucciones similares como la interrupcion int Esta activa el procedimiento de interrupcion especificado por el operando y guarda el valor del registro de flags en la pila La activacion de la interrupcion se realiza mediante una llamada a un procedimiento lejano far call pero en lugar de una direccion utiliza ese vector de interrupcion La direccion de este vector se calcula multiplicando por 4 el operando que es un valor entre 0 y 255 Normalmente el manejador de interrupciones guarda todos los registros que el procesador esta usando a menos que esten siendo usados para almacenar el resultado de una operacion Por otro lado para volver al programa desde una interrupcion se utiliza iret que se encarga de restablecer el valor de los flags Las interrupciones ligeras descritas anteriormente son usadas por algunos sistemas operativos para las llamadas del sistema y pueden tambien ser usadas para depurar los manejadores de interrupciones fuertes Estas son provocadas por eventos hardware externos y deben mantener los valores de todos los registros ya que el estado del programa en ejecucion se desconoce En el Modo Protegido las interrupciones pueden ser activadas por el Sistema Operativo para realizar un cambio de tarea almacenando automaticamente todos los registros de la tarea en ejecucion Vease tambien EditarArquitectura x86 Anexo Listados de instrucciones x86 Lenguaje ensamblador Ensamblador Macro ensamblador Ensamblador de alto nivel Lenguaje ensamblador tipeado Desensamblador Compilador Decompilador Interprete informatica Depurador Lenguaje de alto nivel Lenguaje de bajo nivel Lenguaje de maquina Conjunto de instrucciones Diseno CPU DOSEnsambladores Editar Comparacion de ensambladores High Level Assembly Netwide Assembler Flat assembler GNU Assembler Microsoft Macro Assembler RosASM GoASM A86 y A386 assemblers Turbo Assembler GNU toolchainDesensambladores Editar Interactive DisassemblerDepuradores Editar SoftICE GNU Debugger OllyDbg Valgrind RosASM A86 y A386 assemblers Data Display Debugger debug comando Microprocesadores Editar Antecesores las raices de la arquitectura x86 1971 Datapoint 2200 Terminal de computadora programable Su conjunto de instrucciones es la base de los procesadores Intel desde el 8008 al 8085 los cuales a su vez son los antecesores de la arquitectura x86 1972 Intel 8008 1974 Intel 8080 1977 Intel 8085Algunos microprocesadores de la Arquitectura x86 1978 y 1979 Intel 8086 y 8088 Primeros microprocesadores de la arquitectura x86 1980 Intel 8087 Primer coprocesador numerico de la arquitectura x86 inicio de la serie x87 1982 Intel 80186 y 80188 Mejoras del 8086 y 8088 1982 Intel 80286 Aparece el modo protegido tiene capacidad para multitarea 1985 Intel 80386 Primer microprocesador x86 de 32 bits 1989 Intel 80486 Incorpora el coprocesador numerico en el propio chip 1993 Intel Pentium Mejor desempeno arquitectura superescalar 2003 AMD Opteron Primer microprocesador x86 de 64 bits con el conjunto de instrucciones AMD64 Referencias Editar a b Ram Narayam 17 de octubre de 2007 Linux assemblers A comparison of GAS and NASM Archivado desde el original el 14 de junio de 2008 Consultado el 2 de julio de 2008 Randall Hyde Which Assembler is the Best Archivado desde el original el 15 de mayo de 2008 Consultado el 18 de mayo de 2008 GNU Assembler News v2 1 supports Intel syntax 4 de abril de 2008 Consultado el 2 de julio de 2008 Seguir leyendo EditarIntel 64 and IA 32 Software Developer Manuals AMD64 Architecture Programmer s Manual Volume 1 Application Programming PDF AMD64 Architecture Programmer s Manual Volume 2 System Programming PDF AMD64 Architecture Programmer s Manual Volume 3 General Purpose and System Instructions PDF AMD64 Architecture Programmer s Manual Volume 4 128 Bit Media Instructions PDF AMD64 Architecture Programmer s Manual Volume 5 64 Bit Media and x87 Floating Point Instructions PDF Enlaces externos EditarAn Introduction to Writing 32 bit Applications Using the x86 Assembly Language Roby s Assembly Programming Tutorial Novice and Advanced Assembly resources for x86 Platform x86 Instruction set 80x86 instruction set Which Assembler is the Best A comparison of x86 assemblers El universo digital del IBM PC AT y PS 2 Ciriaco Garcia de Celis Datos Q297868 Obtenido de https es wikipedia org w index php title Lenguaje ensamblador x86 amp oldid 134475300, wikipedia, wiki, leyendo, leer, libro, biblioteca,

español

, española, descargar, gratis, descargar gratis, mp3, video, mp4, 3gp, jpg, jpeg, gif, png, imagen, música, canción, película, libro, juego, juegos