• No se han encontrado resultados

INSTRUCCIÓN DESTINO, FUENTE

In document El Universo Digital Ibm Pc at y Ps2 (página 39-42)

Capítulo III: Microprocesadores 8086/88, 286, 386, 486 y Pentium.

INSTRUCCIÓN DESTINO, FUENTE

Donde destino indica dónde se deja el resultado de la operación en la que pueden participar (según casos) FUENTE e incluso el propio DESTINO. Hay instrucciones, sin embargo, que sólo tienen un operando, como la siguiente, e incluso ninguno:

INSTRUCCIÓN DESTINO

Como ejemplos, aunque no hemos visto aún las instrucciones utilizaremos un par de ellas: la de copia o movimiento de datos (MOV) y la de suma (ADD).

3.4.1. - ORGANIZACIÓN DE DIRECCIONES: SEGMENTACIÓN.

Como ya sabemos, los microprocesadores 8086 y compatibles poseen registros de un tamaño máximo de 16 bits que direccionarían hasta 64K; en cambio, la dirección se compone de 20 bits con capacidad para 1Mb, hay por tanto que recurrir a algún artificio para direccionar toda la memoria. Dicho artificio consiste en la segmentación: se trata de dividir la memoria en grupos de 64K. Cada grupo se asocia con un registro de segmento; el desplazamiento (offset) dentro de ese segmento lo proporciona otro registro de 16 bits. La dirección absoluta se calcula multiplicando por 16 el valor del registro de segmento y sumando el offset, obteniéndose una dirección efectiva de 20 bits. Esto equivale a concebir el mecanismo de generación de la dirección absoluta, como si se tratase de que los registros de segmento tuvieran 4 bits a 0 (imaginarios) a la derecha antes de sumarles el desplazamiento:

En la práctica, una dirección se indica con la notación SEGMENTO:OFFSET; además, una misma dirección puede expresarse de más de una manera: por ejemplo, 3D00h:0300h es equivalente a 3D30:0000h. Es importante resaltar que no se puede acceder a más de 64 Kb en un segmento de datos. Por ello, en los procesadores 386 y superiores no se deben emplear registros de 32 bit para generar direcciones (bajo DOS), aunque para los cálculos pueden ser interesantes (no obstante, sí sería posible configurar estos procesadores para poder direccionar más memoria bajo DOS con los registros de 32 bits, aunque no resulta por lo general práctico).

3.4.2. - MODOS DE DIRECCIONAMIENTO.

- Direccionamiento inmediato: El operando es una constante situada detrás del código de la instrucción. Sin embargo, como registro destino no se puede indicar uno de segmento (habrá que utilizar uno de datos como paso intermedio).

ADD AX,0fffh

El número hexadecimal 0fffh es la constante numérica que en el direccionamiento inmediato se le sumará al registro AX. Al trabajar con ensambladores, se pueden definir símbolos constantes (ojo, no variables) y es más intuitivo:

dato EQU 0fffh ; símbolo constante

MOV AX,dato

Si se referencia a la dirección de memoria de una variable de la siguiente forma, también se trata de un caso de direccionamiento inmediato:

dato DW 0fffh ; ahora es una variable

MOV AX,OFFSET dato ; AX = «dirección de memoria» de

dato

Porque hay que tener en cuenta que cuando traduzcamos a números el símbolo podría quedar:

17F3:0A11 DW FFF

MOV AX,0A11

- Direccionamiento de registro: Los operandos, necesariamente de igual tamaño, están contenidos en los registros indicados en la instrucción:

MOV DX,AX

MOV AH,AL

- Direccionamiento directo o absoluto: El operando está situado en la dirección indicada en la instrucción, relativa al segmento que se trate:

MOV AX,[57D1h]

MOV AX,ES:[429Ch]

Esta sintaxis (quitando la 'h' de hexadecimal) sería la que admite el programa DEBUG (realmente habría que poner, en el segundo caso, ES: en una línea y el MOV en otra). Al trabajar con ensambladores, las

variables en memoria se pueden referenciar con etiquetas simbólicas:

MOV AX,dato

MOV AX,ES:dato

En el primer ejemplo se transfiere a AX el valor contenido en la dirección apuntada por la etiqueta dato sobre el segmento de datos (DS) que se asume por defecto; en el segundo ejemplo se indica de forma explícita el segmento tratándose del segmento ES. La dirección efectiva se calcula de la forma ya vista con anterioridad: Registro de segmento * 16 + desplazamiento_de_dato (este desplazamiento depende de la posición al ensamblar el programa).

- Direccionamiento indirecto: El operando se encuentra en una dirección señalada por un registro de segmento*16 más un registro base (BX/BP) o índice (SI/DI). (Nota: BP actúa por defecto con SS).

MOV AX,[BP] ; AX = [SS*16+BP]

MOV ES:[DI],AX ; [ES*16+DI] = AX

- Indirecto con índice o indexado: El operando se encuentra en una dirección determinada por la suma de un registro de segmento*16, un registro de índice, SI o DI y un desplazamiento de 8 ó 16 bits. Ejemplos:

MOV AX,[DI+DESP] ó MOV AX,desp[DI]

ADD [SI+DESP],BX ó ADD desp[SI],BX

- Indirecto con base e índice o indexado a base: El operando se encuentra en una dirección especificada por la suma de un registro de segmento*16, uno de base, uno de índice y opcionalmente un desplazamiento de 8 ó 16 bits:

MOV AX,ES:[BX+DI+DESP] ó MOV AX,ES:desp[BX][DI]

MOV CS:[BX+SI+DESP],CX ó MOV CS:desp[BX][SI],CX

Combinaciones de registros de segmento y desplazamiento.

Como se ve en los modos de direccionamiento, hay casos en los que se indica explícitamente el registro de segmento a usar para acceder a los datos. Existen unos segmentos asociados por defecto a los registros de desplazamiento (IP, SP, BP, BX, DI, SI); sólo es necesario declarar el segmento cuando no coincide con el asignado por defecto. En ese caso, el ensamblador genera un byte adicional (a modo de prefijo) para indicar cuál es el segmento referenciado. La siguiente tabla relaciona las posibles combinaciones de los registros de segmento y los de desplazamiento:

CS SS DS ES ╔═══════════════╦═══════════════╦═══════════════╦═══════════════╗ IP ║ Sí ║ No ║ No ║ No ║ ╠═══════════════╬═══════════════╬═══════════════╬═══════════════╣ SP ║ No ║ Sí ║ No ║ No ║ ╠═══════════════╬═══════════════╬═══════════════╬═══════════════╣ BP ║ con prefijo ║ por defecto ║ con prefijo ║ con prefijo ║ ╠═══════════════╬═══════════════╬═══════════════╬═══════════════╣ BX ║ con prefijo ║ con prefijo ║ por defecto ║ con prefijo ║ ╠═══════════════╬═══════════════╬═══════════════╬═══════════════╣ SI ║ con prefijo ║ con prefijo ║ por defecto ║ con prefijo ║ ╠═══════════════╬═══════════════╬═══════════════╬═══════════════╣ DI ║ con prefijo ║ con prefijo ║ por defecto ║ con prefijo(1)║ ╚═══════════════╩═══════════════╩═══════════════╩═══════════════╝ (1) También por defecto en el manejo de cadenas.

Los 386 y superiores admiten otros modos de direccionamiento más sofisticados, que se verán en el próximo capítulo, después de conocer todas las instrucciones del 8086. Por ahora, con todos estos modos se

puede considerar que hay más que suficiente. De hecho, algunos se utilizan en muy contadas ocasiones.

3.5. - LA PILA.

La pila es un bloque de memoria de estructura LIFO (Last Input First Output: último en entrar, primero en salir) que se direcciona mediante desplazamientos desde el registro SS (segmento de pila). Las posiciones individuales dentro de la pila se calculan sumando al contenido del segmento de pila SS un desplazamiento contenido en el registro puntero de pila SP. Todos los datos que se almacenan en la pila son de longitud palabra, y cada vez que se introduce algo en ella por medio de las instrucciones de manejo de pila (PUSH y POP), el puntero se decrementa en dos; es decir, la pila avanza hacia direcciones decrecientes. El registro BP suele utilizarse normalmente para apuntar a una cierta posición de la pila y acceder indexadamente a sus elementos - generalmente en el caso de variables- sin necesidad de desapilarlos para consultarlos.

La pila es utilizada frecuentemente al principio de una subrutina para preservar los registros que no se desean modificar; al final de la subrutina basta con recuperarlos en orden inverso al que fueron depositados. En estas operaciones conviene tener cuidado, ya que la pila en los 8086 es común al procesador y al usuario, por lo que se almacenan en ella también las direcciones de retorno de las subrutinas. Esta última es, de hecho, la más importante de sus funciones. La estructura de pila permite que unas subrutinas llamen a otras que a su vez pueden llamar a otras y así sucesivamente: en la pila se almacenan las direcciones de retorno, que serán las de la siguiente instrucción que provocó la llamada a la subrutina. Así, al retornar de la subrutina se extrae de la pila la dirección a donde volver. Los compiladores de los lenguajes de alto nivel la emplean también para pasar los parámetros de los procedimientos y para generar en ella las variables automáticas -variables locales que existen durante la ejecución del subprograma y se destruyen inmediatamente después-. Por ello, una norma básica es que se debe desapilar siempre todo lo apilado para evitar una pérdida de control inmediata del ordenador.

Ejemplo de operación sobre la pila (todos los datos son arbitrarios):

Memoria SS:SP Memoria SS:SP Memoria SS:SP

├───────────┤ ├───────────┤ ├───────────┤ │ 66h 66h 66h ├───────────┤ ├───────────┤ ├───────────┤ │ 91h <-- 14C0:D022 91h 91h <-- 14C0:D022 ├───────────┤ ├───────────┤ ├───────────┤ │ F3h 12h 12h ├───────────┤ ├───────────┤ ├───────────┤ │ 21h 34h <-- 14C0:D020 34h ├───────────┤ ├───────────┤ ├───────────┤ Situación inicial después de PUSH AX después de POP BX AX = 1234h AX = 1234h AX = 1234h BX = 9D33h BX = 9D33h BX = 1234h

In document El Universo Digital Ibm Pc at y Ps2 (página 39-42)