|
|
|
Cursos y Tutoriales Movimientos de datos. En un micro-procesador 8086, el juego de registros es muy reducido. Evidentemente esto no es suficiente para llevar en registros todas las variables del programa Además, algunos de estos registros presentan peculiaridades (Ej: el registro de bandera) que los hacen inservibles para almacenar valores. Por todo esto, las variables de un programa se almacenan en memoria y se traen al registro sólo cuando son necesarias para algún calculo. Después, si es necesario actualizar su valor se vuelve a llevar a memoria el contenido del registro que proceda. Algo ya mencionado es que muchos de los registros desempeñan papeles concretos en algunas instrucciones Ejemplo: para un bucle, el contador se suele llevar casi siempre en el registro CX. O para una multiplicación es necesario cargar uno de los factores en AX). Como ya vimos, para todas las operaciones de movimientos de datos existe una instrucción de lenguaje ensamblador denominada MOV, donde los operandos son 2, escritos separados por comas: el primero es el destino y el segundo el origen (Ej: MOV ax, bx). Evidentemente, los operandos deben de tener la misma longitud (= número de bits). Pueden ser registros, posiciones de memoria, etc. Hacemos aquí un pequeño inciso para anotar que tradicionalmente los ensambladores no distinguen entre minúsculas y mayúsculas. Tanto las instrucciones como los identificadores podemos escribirlos como queramos. mov BX,2 MOV CX,3 mov dx,4 Estas instrucciones cargan AX con 1, BX con 2, ... El dato que se carga en el registro va incluido en la propia instrucción siguiendo al código de operación que indica que se trata de una instrucción de transferencia y a que referencia hay que transferir. Así, los código de las 4 instrucciones en hexadecimal son:
La estructura de las 4 instrucciones es igual. De principio el primer byte es el código de operación que identifica la instrucción que se está tratando. Ya que con este código de operación es necesario un valor de este tamaño (16 bits) para completar la instrucción, como podemos apreciar en los otros 2 bytes. Estos bytes constituyen un valor de 16 bits almacenados en el orden INTEL, es decir, el bit menos significativo el primero y el más significativo después.
Cuando queremos especificar al ensamblador que un numero debe ser interpretado como una dirección de memoria a la que acceder escribimos el número entre corchetes. Así la siguiente instrucción carga en AX la palabra almacenada en la dirección DS:0000. Es decir, carga en AL el byte contenido en DS:0000 y en AH el contenido en DS:0001 MOV ax, [0]
En cambio la siguiente instrucción lo que hace es almacenar el valor 0 en el registro AX. MOV ax, 0 Es importante comprender la diferencia entre las 2 instrucciones. La primera lleva corchetes, se accede a la memoria y el valor recogido se almacena en AX. En la segunda se almacena en AX el valor 0 directamente sin acceder a memoria. Por lo tanto el valor de AX quedaría:
También podemos escribir en una posición de memoria el contenido de un registro. Con MOV [0], al almacenamos en el contenido de AL en DS:0000 Si AL es 25...
Las instrucciones que hemos visto no nos sirven porque la dirección a la que accede va incluida en los código de instrucción. Para ello existe un formato de la instrucción MOV que nos permite acceder a la posición de memoria indicada por BX. Así si BX contiene el valor 55AAh se accederá a esta posición de memoria mientras que si BX contiene el valor 1000h se accederá a la posición 1000h. Para esto escribimos entre corchetes BX el lugar de dar directamente el número. MOV bx, 2345h MOV ax, [bx] Con esto almacenamos en AX el contenido de la dirección 2345 y 2346 ya que AX es una palabra. Si fuera AL o AH solo se almacenaría 2345. Por tanto, en AH almacena 2345h y en AL 2346h. Por supuesto podemos acceder a memoria usando el registro BX para la escritura: MOV bx, 2345h MOV [bx], al En este caso, solo se modifica el contenido de la dirección 2345h y no el de la 2346h, ya que estamos escribiendo el contenido de un registros de 8 bits. Estos dos registros permiten todas las instrucciones vistas para BX, tanto para transferencia entre registros como para transferencia entre procesador y memoria. Pero no hay ninguna forma de acceder por separado a los dos bytes que lo componen.
Otro registro que se puede usar como BX, SI y DI es el registro de puntero de base BP. Al igual que en SI y Di no se puede acceder por separado a los 2 bytes de mayor y menor peso. Pero la mayor diferencia es que por defecto se usa el registro de segmento SS para completar la dirección de 20 bits.
Hay varios puntos que debemos explicar de la tabla:
Reg: representa cualquier registro de 8, 16 o 32 bits pero con la restricción de que cuando la operación es MOV reg, reg ambos deben ser iguales.
Mem: se refiere a posiciones de memoria.
Inmediato: especifica que se debe tomar un valor incluido en los código de la propia instrucción como en el caso de MOV ax, 5 en donde 5 se denomina valor inmediato.
Segmentoreg: puede ser cualquier registro de segmento.
Reg 16: indica cualquier registro de 16 bits.
Especificar el registro CS como destino de la instrucción MOV está prohibido, ya que haría que la ejecución saltase a otro punto. Esto solo se permite a la instrucción de salto. La mayoría de los 8086 paran la ejecución del programa al encontrarla. Uno de los formatos MOV interesantes es: MOV mem, inmediato. Permite introducir directamente en memoria un valor sin alterar ningún registro. Pero esto presenta un inconveniente, en los demás formatos siempre hay un registro como destino u origen de forma que el ensamblador deduce el tamaño del valor a mover. Así en las instrucciones. MOV ax, [4220h] MOV al, [4220h] MOV [4220h], 0 Nada indica al compilador si el 0 es de 1 o de 2 bytes. Hay dos posibles instrucciones máquina para esta línea de lenguaje ensamblador: 1ª Una instrucción de escritura de 16 bits poniendo a 0 los bits de las direcciones 4220h y 4221h. 2ª Una instrucción de escritura de 8 bits poniendo a 0 solo los bits de la dirección 4220h. Por ello se hace necesario indicar al ensamblador de cual de las dos se trata. La sintaxis para esto se comprende mejor así: El número 4220h es análogo a un puntero ya que es un valor que se interpreta como direcciones de memoria usándose en realidad como el desplazamiento que completa la dirección con DS. Este puntero puede ser de dos tipos:
- Puntero a 1 byte se debe generar una instrucción de escritura de 8 bits.
- Puntero a 1 palabra se debe generar una instrucción de escritura de 16 bits. Así para aclarar esto se usa: MOV byte ptr [4220h], 0 ; pone a 0 solo el byte de la dirección 4220h Hemos visto que podemos acceder a la memoria componiendo las direcciones de diferentes formas, especificándolas directamente, en cuyo caso, el valor se incluye en la codificación de la instrucción por medio de los registros BX, SI, DI o BP. Pero en realidad hay muchas formas de indicar como se deben componer el desplazamiento. Estas maneras de obtener la dirección se denominan Modos de Direccionamiento y cada uno tiene asignado un registro de segmento por defecto.
Puede apreciarse que todos los modos admiten que después de obtener el valor de desplazamiento de un registro o de la suma de dos, se añada una constante a éste, antes de generar la lectura, indicada por XX. Si se omite el valor XX se generan instrucciones que no añaden ningún valor al desplazamiento obtenido por los registros. Hemos visto que cada modo usa un registro de segmento por defecto para componer la dirección completa pero siempre es posible que le CPU use, no el registro de segmento por defecto, sino uno especificado por nosotros. A nivel de lenguaje máquina lo que se hace es añadir a la instrucción un prefijo de 1 byte que modifica el comportamiento de la siguiente instrucción haciendo que use el registro de segmento correspondiente al prefijo en lugar del registro por defecto. MOV al, cs:[bx+4] MOV word ptr ds:[bp-0fh], 0 MOV es:[di], al MOV word ptr ss:[bx], 0 Es interesante tener en cuenta que por ejemplo: MOV [bx], ax y MOV ds:[bx], ax son equivalentes. Ya que si no se indica nada el registro por defecto es DS. También es interesante resaltar que para acceder a una posición de un vector, que haya sido previamente definido en el segmento de datos, usamos los corchetes. Segmento de datos... tabla word 100 dup (0) ; Vector de 100 posición, cada posición sería de 1 palabra con valor inicial 0. Segmento de código... MOV ax, tabla[si] ; Donde SI actúa como índice del vector. MOV bx, 0 ; Esto sería equivalente MOV ax, tabla[5] ; todas las sentencias pasarían al registro AX MOV ax, tabla+5 ; el valor de la posición 5 de la tabla pasa AX. MOV ax, tabla[bx]+5 MOV ax, tabla[bx][di] ; Todas estas instrucciones también son equivalentes MOV ax, tabla[bx+di] ; indicada por BX+DI dentro de la tabla a AX. MOV ax, [tabla+bx+di] MOV ax, [bx][di]+tabla Constantes. Una constante entera es una serie de uno o más números seguidos de una base opcional especificada por una letra. MOV ax, 25 MOV ax, 0b3h. Los números 25 y 0b3 son constantes enteras. La h indica la base hexadecimal. Los distintos tipos de base...
Pueden indicarse en mayúsculas o minúsculas. Si no se indica, el ensamblador la interpreta directamente con la base actual. Por defecto es la decimal, pero puede cambiarse con la sentencia RADIX... radix 16 ; base=16 (hexadecimal) db 11 ; se interpreta como valor 11h=17 db 11t ; se interpreta como valor 11 (en decimal) =11 db 01at ; genera un error, por que a no es un dígito decimal
radix 2 ; base=2 (binaria) db 11 ; se interpreta como el valor 11 (binario) =3 radix 10 ; base=10 (decimal) db11 ; se interpreta como el valor 11d=11 abch V.S. 0abch abch es interpretado como una etiqueta y 0abch como un número hexadecimal Los dígitos hexadecimales desde A a F pueden ser mayúsculas y minúsculas. Se pueden definir constantes enteras simbólicas por algunas de las siguientes asignaciones de datos o con EQU (o con el signo "="). EQU.- Tiene como función la de asignar un nombre simbólico a valor de una expresión. El formato es: nombre EQU expresión. La utilidad de esta directiva reside en hacer más clara y legible las sentencias de un programa fuente realizado en ensamblador. Al contrario que en la directiva "=", el nombre no puede redefinirse, es decir permanece invariable la expresión asociada a lo largo de todo el programa fuente (al intentar redefinir crea un error). "Expresión" puede ser una constante numérica, una referencia de direcciones, cualquier combinación de símbolos y operaciones que pueda evaluarse como un valor numérico u otro nombre simbólico. Si un valor constante, o sea un nombre, usado en numerosos lugares del código fuente necesita ser cambiado se debe modificar la expresión en un lugar solo, en vez de por todas las partes del código fuente. Ejemplo: col EQU 80 ; columna de una pantalla (col=80) fil EQU 25 ; fila de una pantalla (fil=25) pantalla EQU col*fil ; tamaño de una pantalla (2000) línea EQU fil longitud dw 0 ; variable tipo palabra byte EQU ptr longitud ; byte=primer byte de longitud cr EQU 13 ; retorno de carro cf EQU 10 ; principio de línea Por defecto, el tamaño de palabra para expresiones de MASM 6.0 es de 32 bits. Se puede modificar con:
OPTION EXPRE 32 ; es erróneo cambiar el tamaño de palabra una vez ha sido fijada
OPTION EXPRE 16 ; la dos últimas usan el tamaño fijo de palabra de 16 bits
OPTION M510 ; opción para operaciones muy específicas del ensamblador Operadores. Un operador es un modificador que se usa en l campo de operandos de una sentencia en ensamblador. Se puede usar varios operadores con instrucciones del procesador. MOV ax, [bx+2] La palabra reservada "MOV" es una instrucción y el signo "+" es un operador. Entre los tipos de operadores hay:
- Operador Aritmético, opera sobre valores numéricos.
- Operador Lógico, opera sobre valores binarios bit a bit.
- Operador Relacional, compara 2 valores numéricos o 2 direcciones de memoria del mismo segmento y produce como resultado: 0 si la relación es falsa, 0ffffh si es verdadera. - Operador de Atributos, permite redefinir el atributo de una variable o etiqueta. Los atributos para variables de memoria pueden ser: byte (1 byte) sbyte (byte con signo) word (palabra) sword (palabra con signo) dword (doble palabra) sdword (doble palabra con signo) fword (6 bytes) qword (8 bytes) tbyte (10 bytes) Los atributos para etiquetas pueden ser: near cuando se puede referenciar desde dentro del segmento donde está definida la etiqueta. far cuando se puede referenciar desde fuera del segmento donde está definida la etiqueta. El ensamblador evalúa expresiones que contienen más de un operando según las siguientes reglas: 1ª Siempre se ejecutan antes las operaciones entre paréntesis que las adyacentes. 2ª Se ejecutan primero las operaciones binarias de más prioridad. 3ª Las operaciones de igual prioridad se ejecutan de izquierda a derecha. 4ª Operaciones unarias de igual prioridad se ejecutan de derecha a izquierda. El orden de prioridad de todos los operadores está en la tabla:
INSTRUCCIONES Instrucciones Aritméticas: ADD, ADC, SUB, SBB, MUL, IMUL, DIV, IDIV, INC, DEC * ADD: Realiza la suma de dos operandos identificándolos como origen y destino, quedando el resultado de la suma en el operando destino (ADD destino, origen). Los dos operandos deben ser del mismo tipo. Ejemplo: ADD ax, bx ADD si, di ADD [0], ax ADD ah, [bx] ADD byte ptr[di-2], 2 MOV ebx, 43981 ADD eax, ebx * ADC: Se utiliza para realizar una suma de 32 bits con registros de 16 bits. La suma se realizaría igual, pero operando sobre 32 bits. En realidad, podemos descomponer una suma de 32 bits en dos sumas de 16 bits. Primero, deberíamos sumar los 16 bits de menor peso de cada operando, almacenando los 16 bits resultantes como palabras baja del resultado. Después, deberíamos sumar los 16 bits de mayor peso de cada operando, pero además deberíamos sumar el acarreo del último bit de la palabras de menor peso. Así, llevamos efectivamente el acarreo del bit 15 de los operandos al bit 16 (esto se realiza utilizando el flag de acarreo CF). cx bx + dx ax dx ax ax=ax+bx si cf=1 (si hay acarreo), dx=dx+cx+1 sino dx=dx+cx Ejemplo: ADD ax, bx ; Suma los 16 bits más bajos, dejando el acarreo(si se produce) en CF ; (Flag de Acarreo) preparado para sumárselo a los 16 bits más altos. ADC dx, cx ; Suma los 16 bits más alto, y a ese resultado se le suma el acarreo (si existe) producido por la suma de bx + ax. El resultado de la suma queda en ax (la parte baja) y en dx (la parte alta). Nota: La utilización de el Flag de acarreo (CF) puede producir a veces algún problema. Si fuese así, una solución sería que, después de ejecutar alguna operación que utilice este flag, ejecutar la instrucción CLC (Borra la bandera de acarreo (CF) sin afectar a ninguna otra bandera). * SUB: Realiza la operación de resta de un operando origen sobre un operando destino. El resultado queda almacenado en el operando destino... Formato SUB destino, origen; (destino = destino - origen). Ejemplo: SUB cl, dl ; En Cl se almacena el valor resultante de CL - DL. ; resultante de restarle a la dirección indicada DX. SUB al, [bp+4] * SBB: Se utiliza para realizar una resta de 32 bits utilizando registros de 16 bits. El formato es el mismo que con ACD; debemos, descomponer los operandos en fragmentos de 16 bits y comenzar a restar por la derecha. En cada resta después de la primera, debemos calcular la resta y del resultado restar el contenido del flag de acarreo (en las restas el acarreo se representa mediante acarreo negativo). Así, en el siguiente ejemplo se le restaría a un valor de 32 bits compuesto por DXAX, un valor de 32 bits compuesto por CXBX. dx ax - cx bx dx ax ax=ax-bx si CF=-1 (si hay acarreo) entonces DX=DX-CX-1 sino DX=DX-CX Ejemplo: SUB ax, bx ; Resta los 16 bits más bajos, deja el acarreo preparado para restar ; los 16 bits más alto. SBB dx, cx ; Resta los 16 bits más altos y además resta el acarreo dejado por ; la anterior instrucción. ; Resultado de 32 bits en DX(palabras alta) y AX (palabras baja). * MUL: Formato MUL fuente. Si el operando fuente es de tipo doble palabra, el resultado se almacena en EAX (doble palabra inferior) y EDX (doble palabra superior). Si la mitad superior del resultado (AH para el caso de operando tipo byte, DX para el caso operando tipo palabra o EDX para el caso de operando tipo doble palabra) no es cero, se activan las banderas CF y OF, indicando que esta mitad superior contiene dígitos significativos del resultado. (fuente tipo byte) * AL = AHAL (AX) (AH parte más alta y AL parte más baja) (fuente tipo palabra) * AX = DXAX (DX parte más alta y AX parte más baja) (fuente tipo doble palabra) * EAX = EDXEAX (EDX parte más alta y EAX parte más baja) Ejemplo: MOV al, 10 ; Movemos a AL 10. MOV bl, 12 ; Movemos a BL 12. MUL bl ; Fuente de tipo byte; el resultado queda en AX (AX=AL*BL). ;La parte más alta queda en AH y la más baja en AL MOV ax, 20 MOV bx, 5 MUL bx ; Fuente de tipo palabra. ; El resultado queda en DXAX (DXAX=AX*BX). MOV eax, 5 MOV ebx, 2 MUL ebx ; Fuente de tipo doble palabra; el resultado queda en ; EDXEAX (EDXEAX=EAX*EBX). La parte alta queda en EDX ; y la más baja en EAX * IMUL: Realiza la misma operación que MUL, solo que en este caso se contempla el signo. * DIV: Formato DIV fuente. Divide, sin considerar el signo, el acumulador (AX (dividendo) si el operando fuente (divisor) es un byte, DXAX (dividendo) si el operando fuente (divisor) es un número de 16 bits o EDXEAX (dividendo) si el operando fuente (divisor) es un número de 32 bits) por el operando fuente. Si el operando fuente es de tipo byte el cociente se almacena el AL y el resto se almacena en AH. AX / fuente = AL (resto en AH) Si el operando fuente es de tipo palabra, el cociente se almacena en AX y el resto se almacena en DX. DXAX / fuente = AX (Resto en DX) Si el operando fuente es de tipo doble palabra, el cociente se almacena en EAX y el resto en EDX. EDXEAX / fuente = EAX (Resto en EDX) Ejemplo: MOV bl, 10 ; Movemos a BL 10. DIV bl ; fuente de tipo byte. ; el cociente queda en AL (AL=1), y el resto queda en AH(AH=2). MOV ax, es:[si] MOV bx, es:[si+2] ; Movemos en DXAX un valor almacenado en memoria, ; por ejemplo, 123567. MOV bx, 65000 DIV BX ; Fuente de tipo palabra; el cociente queda en AX y el resto ; queda almacenado en DX. MOV eax, es:[si] MOV ebx, es:[si+4] ; Movemos a EDXEAX un valor almacenado en memoria. MOV ebx, 50000 DIV ebx ; Fuente de tipo doble palabra; el cociente queda en EAX y ; el resto queda almacenado en EDX. * IDIV: Realiza la misma operación que DIV, sólo que en este caso se contempla el signo. * INC: Se utiliza para incrementar el contenido de un registro o de una posición de memoria. Ejemplo: MOV ax, 5 ; a AX se le pasa el valor 5. INC ax ; AX incrementa en una unidad su valor (AX=6). INC byte ptr[bp+si] ; EL byte al que apunta la suma de "BP + SI" en la memoria ; se ve incrementado en una unidad. INC word ptr[bp+si] ; Lo mismo pero para una palabra. * DEC: Se utiliza para decrementar el contenido de un registro o de una posición de memoria. Ejemplo: MOV ax, 5 ; a AX se le pasa el valor 5 DEC ax ; AX decrementado en una unidad su valor (AX=4) DEC byte ptr[bp+si] ; El byte al que apunta la suma de "BP+SI" en la memoria ; se ve decrementado en una unidad. DEC word ptr[bp+si] ; Lo mismo pero para una palabra. Instrucciones Lógicas: NEG, NOT, AND, OR, XOR * NEG: Esta instrucción lo que hace es calcular el complemento a dos del operando, y almacenando en el mismo lugar. Esto es, efectivamente, equivalente a cambiar de signo el operando de la instrucción. Ejemplo: MOV ax, 5 ; a AX se le pasa el valor 5. NEG ax ; Se haya el complemento a 2 de AX y se guarda en AX (AX= -5). NEG byte ptr es:[bx+si+2]; Se haya el complemento a 2 de la posición de memoria ; (dentro del Segmento Extra) indicada por el de "BX+SI+2" * NOT: Se realiza el NOT lógico del operando bit a bit. El NOT lógico bit a bit consiste en invertir cada bit del operando (pasar los 0 a 1 y los 1 a 0; 10100 -> 01011) Ejemplo: NOT si ; El valor que tenga SI pasa los 0 a 1 y los 1 a 0. NOT word ptr es:[0] ; Lo mismo pero en una posición de memoria. * AND: Operación "y lógico" a nivel de bit entre los dos operandos. El resultado se almacena en el destino. Formato AND destino, fuente. 0 0 - 0 0 1 - 0 1 0 - 0 1 1 - 1 Ejemplo: AND ax, bx ; AND lógico entre AX y BX. El resultado queda en AX. AND es:[0], dx ; Lo mismo pero con posiciones de memoria. AND di, es:[si] AND byte ptr[9], 3 ; Lo mismo pero con valores inmediatos. * OR: Operación "o lógico exclusivo" a nivel entre los dos operandos. El resultado se almacena en el destino. Formato OR destino, fuente. 0 0 - 0 0 1 - 1 1 0 - 1 1 1 - 1 Ejemplo: OR al, ah ; Las mismas operaciones que con AND pero utilizando el OR. OR [di], ch OR cl, [bp+4] OR byte ptr es:[si], 1
* XOR: Operación "o lógico exclusivo" a nivel de bit entre los dos operandos. El resultado se almacena en destino. Formato XOR destino, fuente. 0 0 - 0 0 1 - 1 1 0 - 1 1 1 - 0 Ejemplo: XOR ax, ax ; El XOR entre dos bits con el mismo valor es siempre 0, ; independientemente del valor previo de AX (AX=0). ; Las ventajas de hacerlo así son dos: la ejecución de XOR reg, reg es más ; rápida que la de MOV reg, o que la de MOV ax,0 , y la codificación de la ; primera ocupa menos bytes que la segunda; Esta técnica no puede utilizar ; se para poner a cero los registros de segmento. XOR byte ptr[55aah], 4 XOR al, 00aah * XCHG: Intercambia el contenido entre dos operandos. No pueden utilizarse registros de segmento como operandos. Ejemplo: XCHG si, di ; Si SI tiene valor 45 y DI tiene valor 68, ahora, DI se queda con ; valor 45 y SI con 68. XCHG al, [bx+4] XCHG ss:[si], bx * CMP: Formato CMP destino, origen. (destino - origen) Esta instrucción realiza una resta de un operando origen sobre un operando destino, pero con la particularidad de no almacenar el resultado y no modificar ninguno de los 2 operandos, pero si se modifican los bits de indicadores (Flags). Los operandos deben ser del mismo tipo. Esta modificación de los bits de indicadores, nos permitirá posteriormente, mediante la inspección de los mismos, poder realizar determinadas acciones. Normalmente después de una instrucción de comparación (CMP), hay una instrucción de salto. Ejemplo: CMP ax, bx ; Comparamos AX con BX JL menor ; Si AX es menor que BX saltamos a la etiqueta MENOR MENOR: CMP bl, cl CMP bx, cx CMP bl, byte ptr es:[si] CMP word ptr es[si], bx CMP bx, 30 CMP byte ptr es:[si], 01h ; Normalmente, después de cada instrucción de ;comparación, viene una instrucción de salto. |
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||