Lenguajes de Interfaz Unidad - Unidad IV
Unidad IV - Programación Básica.
2.1 Ensamblador (y ligador) a utilizar.
Ensamblador.
El lenguaje ensamblador es un lenguaje de bajo nivel que se comunica
directamente con el hardware de la máquina.
El termino ensamblador se refiere a un tipo de programa
informático que se encarga de traducir un fichero fuente escrito en un lenguaje
ensamblador, a un fichero objeto que contiene código máquina, ejecutable
directamente por el microprocesador.
El programa lee el fichero escrito en lenguaje ensamblador y
sustituye cada uno de los códigos nemotécnicos que aparecen por su código de
operación correspondiente en sistema binario para la plataforma que se eligió
como destino en las opciones especificas del ensamblador.
Características
El código escrito en lenguaje ensamblador posee una cierta
dificultad de ser entendido ya que su estructura se acerca al lenguaje máquina,
es decir, es un lenguaje de bajo nivel.
El lenguaje ensamblador es difícilmente portable, es decir,
un código escrito para un microprocesador, puede necesitar ser modificado, para
poder ser usado en otra máquina distinta. Al cambiar a una máquina con
arquitectura diferente, generalmente es necesario reescribirlo completamente.
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 y/o muy ineficientes de programar en un lenguaje
de alto nivel, ya que, entre otras cosas, en el lenguaje ensamblador se dispone
de instrucciones del CPU que generalmente no están disponibles en los lenguajes
de alto nivel.
Podemos distinguir entre dos tipos de ensambladores:
Ensambladores modulares 32 bits o de alto nivel, son ensambladores
que aparecieron como respuesta a una nueva arquitectura de procesadores de 32bits,
muchos de ellos teniendo compatibilidad hacia atrás pudiendo trabajar con
programas con estructuras de 16 bits.
Ensambladores básicos. Son de muy bajo nivel, y su tarea
consiste básicamente en ofrecer nombres simbólicos a las distintas
instrucciones, parámetros y cosas tales como los modos. Un ligador, es un
programa que enlaza todos los programas o módulos obteniendo lo que denominamos
programa ejecutable. Es un programa que enlaza distintos módulos o programas
que poseen subprogramas. Además, incorporan las denominadas rutinas de librerías
en caso de solicitarlas el propio programa.
La generación de un módulo ejecutable a partir de una
colección de procedimientos traducidos independientemente requiere un ligador.
Los editores ligados pueden efectuar varias funciones últimas
además de la simple preparación de un programa objeto para su ejecución estos
también se pueden utilizar para construir paquetes de subrutinas u otras cosas secciones
que suelen utilizar juntas. Esto puede ser útil a tratar con bibliotecas de
subrutinas que manejan lenguajes de programación de alto nivel. Comparados con
los cargadores deligadores los editores de ligado en general tienden a ofrecer
mayor flexibilidad y control con el correspondiente incremento de complejidad y
sobrecarga.
Su tarea principal del enlazador es resolver referencias
externas que lleva a cabo la siguiente etapa del proceso de traducción
enlazando los módulos ensambladores y los acervos para formar un programa
completo. En algunos sistemas el cargador simplemente copia el programa
ejecutable a las posiciones de memorias apropiadas y enlazar código intermedio
compilado independientemente en un solo módulo de carga resolviendo las
diferencias entre Tokens.
Al construir un programa algunos de sus módulos pueden
colocarse en el mismo módulo fuente y ensamblarse juntos, otros pueden estar en
módulos diferentes y ser ensamblados separadamente. En cualquier caso, los módulos objeto
resultantes, algunos de los cuales pueden estar agrupados en librerías, deben
ser enlazados para formar el módulo de carga, antes de que se pueda ejecutar el
programa. Además, de dar como salida el módulo de carga, el linker o enlazador
imprime un mapa de memoria que indica donde serán cargados los módulos objeto
en la memoria.
Después de crearse el módulo de carga, éste es cargado por
el cargador en la memoria del ordenador y comienza la ejecución. Para linkar
con TASM escribimos:
tlink /v nombre
Tras esto se
nos creará el
fichero ejecutable (.exe
o .com) que el
sistema operativo se
encargará de cargar
en memoria cuando
lo ejecutemos. Denuevo, la extensión no es necesaria ponerla
y el parámetro /v sólo es útil para el TurboDebugger.
2.2 Ciclos numéricos.
Hay ocasiones en las que es necesario hacer que el programa
no siga una secuencia lineal, sino que repita varias veces una misma
instrucción o bloque de instrucciones antes de continuar con el resto del programa,
es para esto que se utilizan los ciclos.
Existen 5 tipos de ciclos predefinidos en ensamblador,
aunque también se pueden crear ciclos personalizados por medio de instrucciones
de salto las cuales se verán en la sección 2.6 de este manual.
Los ciclos predefinidos de ensamblador son los siguientes:
LOOP: Esta función decrementa el valor del registro contador CX,
si el valor contenido en CX es cero ejecuta la siguiente instrucción, en caso
contrario transfiere el control a la ubicación definida por la etiqueta
utilizada al momento de declarar el ciclo.
Ejemplo:
mov cx,25 : Número
de veces que se repetirá el ciclo, en este caso 25.
ciclo: Etiqueta que
se utilizará como referencia para el ciclo loop.
int 21h: Instrucción contenida dentro del ciclo (puede
contener más de una instrucción).
loop: Ciclo loop que
transferirá el control a la línea de la etiqueta ciclo en caso de que CX no sea
cero.
LOOPE: Esta función decrementa el valor del registro contador CX,
si el valor contenido en CX es cero y ZF es diferente de uno ejecuta la
siguiente instrucción, en caso contrario transfiere el control a la ubicación
definida por la etiqueta utilizada al momento de declarar el ciclo.
Ejemplo:
ciclo: Etiqueta que se utilizará como referencia para el ciclo
loope.
int 21h: Instrucción contenida dentro del ciclo (puede
contener más de una instrucción).
loope: Ciclo loope que transferirá el control a la línea de
la etiqueta ciclo en caso de que CX no sea cero y ZF sea igual a uno.
LOOPNE: Esta función decrementa el valor del registro contador CX,
si el valor contenido en CX es cero y ZF es diferente de cero ejecuta la
siguiente instrucción, en caso contrario transfiere el control a la ubicación
definida por la etiqueta utilizada al momento de declarar el ciclo, esta es la
operación contraria a loope.
Ejemplo:
ciclo: Etiqueta que se utilizará como referencia para el
ciclo loopne.
int 21h: Instrucción contenida dentro del ciclo (puede
contener más de una instrucción).
loopne: Ciclo loopne que transferirá el control a la línea
de la etiqueta ciclo en caso de que CX no sea cero y ZF sea igual a cero.
LOOPZ: Esta función decrementa el valor del registro contador CX,
si el valor contenido en CX es cero y ZF es diferente de uno ejecuta la
siguiente instrucción, en caso contrario transfiere el control a la ubicación
definida por la etiqueta utilizada al momento de declarar el ciclo.
Ejemplo:
ciclo: Etiqueta que se utilizará como referencia para el
ciclo loopz.
int 21h: Instrucción contenida dentro del ciclo (puede
contener más de una instrucción).
loopz: Ciclo loopz que transferirá el control a la línea de
la etiqueta ciclo en caso de que CX no sea cero y ZF sea igual a uno.
LOOPNZ: Esta función decrementa el valor del registro contador CX,
si el valor contenido en CX es cero y ZF es diferente de cero ejecuta la
siguiente instrucción, en caso contrario transfiere el control a la ubicación
definida por la etiqueta utilizada al momento de declarar el ciclo, esta es la
operación contraria a loopz.
Ejemplo:
ciclo: Etiqueta que se utilizará como referencia para el
ciclo loopnz.
int 21h: Instrucción contenida dentro del ciclo.
loopnz: Ciclo loopnz que transferirá el control a la línea
de la etiqueta ciclo en caso de que CX no sea cero y ZF sea igual a cero.
2.3 Captura básica de cadenas.
Dentro del lenguaje ensamblador no
existe el tipo de dato cadena (string en otros lenguajes), por lo que para
utilizarla es necesario tratar a las cadenas como un conjunto de caracteres
reservados bajo el nombre de una sola variable.
El lenguaje ensamblador cuenta con
instrucciones que por su naturaleza sirven para el manejo de cadenas, estas
son:
MOVSB: Mueve un byte desde una localidad de memoria hasta otra.
MOVSW: Mueve una palabra desde una localidad de memoria hasta otra.
LODSB: Carga en la parte baja del registro acumulador (AL) el valor
de la localidad de memoria determinada por DS:SI.
LODSW: Carga en el registro acumulador (AX) el valor de la
localidad de memoria determinada por DS:SI.
Ejemplo:
2.4 Comparación y prueba.
La instrucción CMP pro lo común
es utilizada para comparar dos campos de datos, uno de los cuales están
contenidos en un registro. El formato general para CMP es:| [etiqueta:] | CMP |
{registro/memoria}, {registro/memoria/inmediato} |
Observe que la operación compara
el primer operando con el segundo; por ejemplo, el valor del primer operando es
mayor que, igual o menor que el valor del segundo operando.
La instrucción CMPS compara el
contenido de una localidad de memoria (direccionada por DS:SI). Dependiendo de
la bandera de dirección, CMPS incrementa o disminuye también losregistros SI y
DI en 1 para bytes, en 2 para
palabras y en 4 para palabras
dobles. La operación establece las banderas AF,CF, OF, PF, SF y ZF.
Cuando se combinan con un prefijo
REP y una longitud en el CX, de manera sucesiva CMPS puede comparar cadenas de
cualquier longitud.
Pero observe que CMPS proporciona
una comparación alfanumérica, esto es, una comparación de acuerdo a con los
valores ASCII. Considere la comparación de dos cadenas que contienen JEAN y
JOAN.
Algunas derivaciones de CMPS son las siguientes:
· CMPSB. Compara bytes
· CMPSD. Compara palabras dobles
· CMPSW. Compara palabras
2.5 Saltos
2.6 Ciclos condicionales.
La mayoría de los programas
constan de varios ciclos en los que una serie de pasos se repite hasta alcanzar
un requisito específico y varias pruebas para determinar qué acción se realiza
de entre varias posibles.
Una instrucción usada comúnmente
para la transferencia de control es la instrucción JMP (jump, salto,
bifurcación). Un salto es incondicional, ya que la operación transfiere el
control bajo cualquier circunstancia. También JMP vacía el resultado de la instrucción
previamente procesada; por lo que, un programa con muchas operaciones de salto
puede perder velocidad de procesamiento.
La instrucción LOOP, requiere un
valor inicial en el registro CX. En cada iteración, LOOP de forma automática
disminuye 1 de CX. Si el valor en el CX es cero, el control pasa a la
instrucción que sigue; si el valor en el CX no es cero, el control pasa a la
dirección del operando. La distancia debe ser un salto corto, desde -128 hasta
+127 bytes. Para una operación que exceda este límite, el ensamblador envía un
mensaje como "salto relativo fuera de rango".
2.6 Ciclos condicionales.
Dentro de la programación existen
ocasiones en la que es necesario ejecutar una misma instrucción un cierto
número de veces, el cual no siempre es conocido por el programador o puede
cambiar durante la ejecución del programa, para lo que existen los ciclos
condicionales, los cuales una vez se cumpla la condición que tienen
establecida, dejaran de ejecutarse como ciclo y permitirán que el programa
continúe con su flujo normal.
En ensamblador no existen de forma
predefinida estos ciclos, pero pueden crearse haciendo uso de los saltos
incondicionales, generando ciclos que se repetirán hasta que se cumpla la
condición definida por el programador.
Ejemplo:
mov al, 0: Asigna el valor cero
al registro al.
ciclo: Etiqueta a la que se hará
referencia para el ciclo condicional.
INC al: Aumenta en 1 el valor del
registro al.
CMP al, bl : Comparación entre el
valor almacenado en al y el almacenado en bl.
JL ciclo: Instrucción que indica
que el flujo del programa continuara desde la ubicación de la etiqueta ciclo si
el valor de al es menor al de bl.
Sintaxis:
LOOP etiqueta
La instrucción loop decrementa CX
en 1, y transfiere el flujo del programa a la etiqueta dada como operando si CX
es diferente a 1.
Instrucción LOOPE
Propósito: Generar un ciclo en el
programa considerando el estado de ZF
Sintaxis:
LOOPE etiqueta
Esta instrucción decrementa CX en
1. Si CX es diferente a cero y ZF es igual a 1, entonces el flujo del programa
se transfiere a la etiqueta indicada como operando.
Instrucción LOOPNE
Propósito: Generar un ciclo en el
programa, considerando el estado de ZF
Sintaxis:
LOOPNE etiqueta
Esta instrucción decrementa en uno a CX y
transfiere el flujo del programa solo si ZF es diferente a 0.
2.7 Incremento y decremento.
Son las instrucciones más básicas
a la hora de hacer operaciones con registros:
INC: incrementa el valor de un registro, o de cualquier posición en
memoria, en una Unidad.
DEC: Reduce en uno el valor contenido dentro del registro que se le
dé como parámetro.
Instrucción INC
INC AX: Incrementa en uno el
valor de AX
IN WORD PTR: Incrementa la
palabra situada en CS.
INC AL: Aumenta en 1 el valor del registro al.
Instrucción DEC
DEC AX: Decremento AX, le resta uno.
DEC WORD PTR: Decrementa la palabra situada en CS.
DEC AL: Reduce en 1 el valor del registro al.
2.8 Captura de cadenas con formato.
Formato de instrucciones para lenguaje ensamblador:
MOV: Transfiere datos entre celdas de memoria y registros.
Sintaxis: MOV Destino, Fuente
Ejemplo:
MOV AX,0006h
MOV DX, AX
MOVS (MOVSB) (MOVSW): Mueve
cadenas de bytes o palabras desde la fuente, direccionada por SI, hasta el destino direccionado por DI.
Sintaxis:
MOVS: Este comando no necesita parámetros
ya que toma como dirección fuente el contenido del registro SI y como destino
el contenido de DI.
Ejemplo:
MOV SI, OFFSET VARIABLE1
MOV DI, OFFSET VARIABLE2
MOVS: Primero se inicializan los valores de SI y DI con las
direcciones de las variables VARIABLE1
y VARIABLE2 respectivamente, después al ejecutar MOVS se copia el contenido de VARIABLE1 a VARIABLE2.
Los comandos MOVSB y MOVSW se
utilizan de la misma forma que MOVS, el primero mueve un byte y el segundo una
palabra.
LODS (LODSB) (LODSW): Carga cadenas de un byte o palabra al
acumulador.
Sintaxis:
LODS: Toma la cadena que se encuentre
en la dirección especificada por SI, la carga al registro AL (o AX) y suma o
resta 1 (según el estado de DF) a SI, si la transferencia es de bytes o 2 si la
transferencia es de palabras.
Ejemplo:
MOV SI, OFFSET VARABLE1
LODS: La primera línea carga la dirección de VARIABLE1 en SI y la
segunda línea lleva el contenido de
esa localidad al registro AL.
Los comandos LODSB y LODSW se
utilizan de la misma forma, el primero carga un byte y el segundo una palabra
(utiliza el registro completo AX).
LAHF: Transfiere al registro
AH el contenido de las banderas
Sintaxis:
LAHF: Se utiliza para verificar el
estado de las banderas durante la ejecución de un programa.
Las banderas quedan en el siguiente orden dentro del registro:
SF ZF __ AF __ PF __ CF
LEA: Carga la dirección del operando fuente.
Sintaxis: LEA destino, fuente
El operando fuente debe estar ubicado
en memoria, y se coloca su desplazamiento en el registro índice o apuntador
especificado en destino.
Ejemplo: MOV SI, OFFSET VAR1
Que es equivalente a: LEA SI,
VAR1
POP: Recupera un dato de la pila
Sintaxis: POP destino
Transfiere el último valor
almacenado en la pila al operando destino y después incrementa en dos el
registro SP.
Este incremento se debe a que la
pila va creciendo desde la dirección más alta de memoria del segmento hacia la más
baja, y la pila solo trabaja con palabras (2 bytes), entonces al incrementar en
dos el registro SP realmente se le está restando dos al tamaño real de la pila.
POPF: Extrae las banderas almacenadas en la pila.
Sintaxis: POPF
Transfiere bits de la palabra
almacenada en la parte superior de la pila hacia el registro de banderas.
La forma de transferencia es la siguiente:
BIT BANDERA 0 CF ___ 2 PF ___ 4
AF ___ 6 ZF 7 SF 8 TF 9 IF 10 DF 11
OF: Estas localizaciones son las mismas para el comando PUSHF.
Una vez hecha la transferencia se
incrementa en 2 el registro SP disminuyendo así el tamaño de la pila.
PUSH: Coloca una palabra en la pila.
Sintaxis: PUSH fuente
La instrucción PUSH decrementa en
dos el valor de SP y luego transfiere el contenido del operando fuente a la
nueva dirección resultante en el registro recién modificado.
El decremento en la dirección se
debe a que al agregar valores a la pila ésta crece de la dirección mayor a la
dirección menor del segmento, por lo tanto, al restarle 2 al valor del registro
SP lo que hacemos es aumentar el tamaño de la pila en dos bytes, que es la
única cantidad de información que puede manejar la pila en cada entrada y salida
de datos.
PUSHF: Coloca el valor de las banderas en la pila
Sintaxis: PUSHF
Decrementa en 2 el valor del
registro SP y luego se transfiere el contenido del registro de banderas a la
pila, en la dirección indicada por SP.
Las banderas quedan almacenadas
en memoria en los mismos bits indicados en el comando POPF
2.9 Instrucciones aritméticas.
ADC: Adición con acarreo.
Sintaxis: ADC destino, fuente.
Lleva a cabo la suma de dos
operandos y suma uno al resultado en caso de que la bandera CF esté activada,
esto es, en caso de que exista acarreo.
El resultado se guarda en el
operando destino.
ADD: Adición de los operandos.
Sintaxis: ADD destino, fuente
Suma los dos operandos y guarda
el resultado en el operando destino.
DIV: División sin signo.
Sintaxis: DIV fuente.
El divisor puede ser un byte o
palabra y es el operando que se le da a la instrucción.
- Si el divisor es de 8 bits se toma como
dividendo el registro de 16 bits AX y si el divisor es de 16 bits se tomará
como dividendo el registro par DX:AX, tomando como palabra alta DX y como baja
AX.
- Si el divisor fue un byte el cociente se
almacena en el registro AL y el residuo en AH, si fue una palabra el cociente
se guarda en AX y el residuo en DX.
IDIV: División con signo.
Sintaxis: IDIV fuente
Consiste básicamente en lo mismo
que la instrucción DIV, solo que esta última realiza la operación con signo.
MUL: Multiplicación sin signo
Sintaxis: MUL fuente
El ensamblador asume que el
multiplicando será del mismo tamaño que el del multiplicador, por lo tanto,
multiplica el valor almacenado en el registro que se le da como operando por el
que se encuentre contenido en AH si el multiplicador es de 8 bits o por AX si
el multiplicador es de 16 bits.
Cuando se realiza una
multiplicación con valores de 8 bits el resultado se almacena en el registro AX
y cuando la multiplicación es con valores de 16 bits el resultado se almacena
en el registro par DX:AX.
IMUL: Multiplicación de dos enteros con signo.
Sintaxis: IMUL fuente
Este comando hace lo mismo que el
anterior, solo que si toma en cuenta los signos de las cantidades que se
multiplican.
Los resultados se guardan en los
mismos registros que en la instrucción MUL.
SBB: Abstracción con acarreo
Sintaxis: SBB destino, fuente
Esta instrucción resta los
operandos y resta uno al resultado si CF está activada.
El operando fuente siempre se
resta del destino.
Este tipo de substracción se
utiliza cuando se trabaja con cantidades de 32 bits.
SUB: Substracción.
Sintaxis: SUB destino, fuente.
Resta el operando fuente del
destino.
2.10 Manipulación de la pila.
La pila es un grupo de
localidades de memoria que se reservan para contar con un espacio de
almacenamiento temporal cuando el programa se está ejecutando.
La pila es una estructura de
datos del tipo LIFO (Last In First Out), esto quiere decir que el último dato
que es introducido en ella, es el primero que saldrá al sacar datos de la pila.
Para la manipulación de la pila
ensamblador cuenta con dos instrucciones específicas, las cuales son las
siguientes:
Push: Esta instrucción permite almacenar el contenido del operando
dentro de la última posición de la pila.
Ejemplo: Push ax El valor contenido en ax es almacenado en
el último espacio de la pila.
Pop: Esta instrucción toma el último dato almacenado en la pila y
lo carga al operando.
Ejemplo: Pop bx El valor contenido en el último espacio
de la pila se almacena en el registro.
Dentro de la manipulación de las pilas también existen los siguientes comandos:
2.11 Obtención de cadena con representación decimal.
En este modo, los datos son
proporcionados directamente como parte de la instrucción.
Ejemplo:
Mov AX,34h ;Copia en AX el número
34h hexadecimal
Mov CX,10 ;Copia en CX el número
10 en decimal
Ejemplo:
Mov AX,34h ;
Copia en AX el número 34h
hexadecimal Mov CX,10 ;
Copia en CX el número 10 en
decimal
.COMMENT
Programa: PushPop.ASM
Descripción: Este programa
demuestra el uso de las instrucciones para el manejo de la pila, implementando
la instrucción XCHG con Push y Pop
MODEL tiny
.CODE
Inicio: ;Punto de entrada al
programa
Mov AX,5 ;AX=5
Mov BX,10 ;BX=10
Push AX ;Pila=5
Mov AX,BX ;AX=10
Pop BX ;BX=5
Mov AX,4C00h ;Terminar programa y
salir al DOS
Int 21h ;
END Inicio
END
2.12 Instrucciones lógicas.
AND: Realiza la conjunción de los operandos bit por bit.
Sintaxis: AND destino, fuente
Con esta instrucción se lleva a
cabo la operación "y" lógica de los dos operandos:
NEG: Genera el complemento a 2.
Sintaxis: NEG destino
Genera el complemento a 2 del
operando destino y lo almacena en este mismo operando.
Ejemplo, si AX guarda el valor de
–2 (FFFE), entonces:
NEG AX: Dejaría como resultado en AX el valor 0002.
NOT: Lleva a cabo la negación bit por bit del operando destino.
Sintaxis: NOT destino
El resultado se guarda en el
mismo operando destino.
OR: OR inclusivo lógico.
Sintaxis: OR destino, fuente
La instrucción OR lleva a cabo,
bit por bit, la disyunción inclusiva lógica de los dos operandos:
TEST: Compara lógicamente los operandos.
Sintaxis: TEST destino, fuente
Realiza una conjunción, bit por
bit, de los operandos, pero a diferencia de AND esta instrucción no coloca el resultado
en el operando destino, solo tiene efecto sobre el estado de las banderas.
XOR: OR exclusivo
Sintaxis: XOR destino, fuente
Su función es efectuar bit por
bit la disyunción exclusiva lógica de los dos operandos:
2.13 Desplazamiento y rotación.
Las instrucciones de corrimiento,
que son parte de la capacidad lógica de la computadora, pueden realizar las
siguientes acciones:
1. Hacer referencia a un registro
o dirección de memoria.
2. Recorre bits a la izquierda o
a la derecha.
3. Recorre hasta 8 bits en un
byte, 16 bits en una palabra y 32 bits en una palabra doble.
4. Corrimiento lógico (sin signo)
o aritmético (con signo).
El segundo operando contiene el
valor del corrimiento, que es una constante (un valor inmediato) o una
referencia al registro CL. Para los procesadores 8088/8086, la constante
inmediata solo puede ser 1; un valor de corrimiento mayor que 1 debe estar
contenido en el registro CL. Procesadores posteriores permiten constantes de
corrimiento inmediato hasta 31.
El formato general para el
corrimiento es:
DESPLAZAMIENTO O CORRIMIENTO DE BITS HACIA LA DERECHA.
Los corrimientos hacia la derecha
(SHR y SAR) mueven los bits hacia la derecha en el registro designado. El bit
recorrido fuera del registro mete la bandera de acarreo. Las instrucciones de
corrimiento a la derecha estipulan datos lógicos (sin signo) o aritméticos (con
signo):
Las siguientes instrucciones relacionadas ilustran SHR y datos con
signo:
INSTRUCCION COMENTARIO
MOV CL, 03
MOV AL, 10110111B ; AL = 10110111
SHR AL, 01 ; AL = 01011011 Un corrimiento a la
derecha
SHR AL, CL ; AL = 00001011 Tres corrimientos
adicionales a la Derecha
El primer SHR desplaza el
contenido de AL un bit hacia la derecha. El bit de más a la derecha es enviado
a la bandera de acarreo, y el bit de más a la izquierda se llena con un cero.
El segundo SHR desplaza tres bits más a AL. La bandera de acarreo contiene de
manera sucesiva 1, 1 y 0; además, tres bits 0 son colocados a la izquierda del
AL.
SAR se difiere de SHR en un punto
importante: SAR utiliza el bit de signo para llenar el bit vacante de más a la
izquierda. De esta manera, los valores positivos y negativos retienen sus
signos. Las siguientes instrucciones relacionadas ilustran SAR y datos con
signo en los que el signo es un bit 1:
En especial, los corrimientos a
la derecha son útiles para (dividir entre 2) obtener mitades de valores y son
mucho más rápidas que utilizar una operación de división. Al terminar una
operación de corrimiento, puede utilizar la instrucción JC (Salta si hay
acarreo) para examinar el bit desplazado a la bandera de acarreo.
DESPLAZAMIENTO O CORRIMIENTO DE BITS A LA IZQUIERDA.
Los corrimientos hacia la
izquierda (SHL y SAL) mueven los bits a la izquierda, en el registro designado.
SHL y SAL son idénticos en su operación. El bit desplazado fuera del registro
ingresa a la bandera de acarreo. Las instrucciones de corrimiento hacia la
izquierda estipulan datos lógicos (sin signo) y aritméticos (con signo):
SHL: Desplazamiento lógico a la
izquierda SAL: Desplazamiento aritmético a la izquierda.
Las siguientes instrucciones relacionadas ilustran SHL para datos sin
signo:
INSTRUCCION COMENTARIO
MOV CL, 03
MOV AL, 10110111B ; AL = 10110111
SHL AL, 01 ; AL = 01101110 Un corrimiento a la
izquierda
SHL AL, CL ; AL = 01110000 Tres corrimientos
mas
El primer SHL desplaza el
contenido de AL un bit hacia la izquierda. El bit de más a la izquierda ahora
se encuentra en la bandera de acarreo, y el último bit de la derecha del AL se
llena con cero. El segundo SHL desplaza tres bits más a AL. La bandera de
acarreo contiene en forma sucesiva 0, 1 y 1, y se llena con tres ceros a la
derecha del AL.
Los corrimientos a la izquierda
llenan con cero el bit de más a la derecha. Como resultado de esto, SHL y SAL
don idénticos. Los corrimientos a la izquierda en especial son útiles para
duplicar valores y son mucho más rápidos que usar una operación de
multiplicación.
Al terminar una operación de
corrimiento, puede utilizar la instrucción JC (Salta si hay acarreo) para
examinar el bit que ingreso a la bandera de acarreo.
ROTACIÓN DE BITS (DESPLAZAMIENTO CIRCULAR)
Las instrucciones de rotación,
que son parte de la capacidad lógica de la computadora, pueden realizar las
siguientes acciones:
1. Hacer referencia a un byte o a
una palabra.
2. Hacer referencia a un registro
o a memoria.
3. Realizar rotación a la derecha
o a la izquierda. El bit que es desplazado fuera llena el espacio vacante en la
memoria o registro y también se copia en la bandera de acarreo.
4. Realizar rotación hasta 8 bits
en un byte, 16 bits en una palabra y 32 bits en una palabra doble.
5. Realizar rotación lógica (sin
signo) o aritmética (con signo).
El segundo operando contiene un
valor de rotación, el cual es una constante (un valor inmediato) o una
referencia al registro CL. Para los procesadores 8088/8086, la constante
inmediata solo puede ser 1; un valor de rotación mayor que 1 debe estar
contenido en el registro CL. Procesadores posteriores permiten constantes inmediatas
hasta el 31. El formato general para la rotación es:
ROTACIÓN A LA DERECHA DE BITS
Las rotaciones a la derecha (ROR
y RCR) desplazan a la derecha los bits en el registro designado. Las
instrucciones de rotación a la derecha estipulan datos lógicos (sin signo) o
aritméticos (con signo):
Las siguientes instrucciones relacionadas ilustran ROR:
INSTRUCCIÓN COMENTARIO
MOV CL, 03
MOV BH, 10110111B ; BH = 10110111
ROR BH, 01 ; BH = 11011011 Una
rotación a la derecha
ROR BH, CL ; BH = 00001011 Tres
rotaciones a la derecha
El primer ROR desplaza el bit de
más a la derecha del BH a la posición vacante de más a la izquierda. La segunda
y tercera operaciones ROR realizan la rotación de los tres bits de más a la
derecha.
RCR provoca que la bandera de
acarreo participe en la rotación. Cada bit que se desplaza fuera de la derecha
se mueve al CF y el bit del CF se mueve a la posición vacante de la izquierda.
ROTACIÓN A LA IZQUIERDA DE BITS
Las rotaciones a la izquierda
(ROL y RCL) desplazan a la izquierda los bits del registro designado. Las
instrucciones de rotación a la izquierda estipulan datos lógicos (sin signo) y
aritméticos (con signo):
Las siguientes instrucciones relacionadas ilustran ROL:
INSTRUCCIÓN COMENTARIO
MOV CL, 03
MOV BL, 10110111B ; BL = 10110111
SHR BL, 01 ; BL = 11011011 Una
rotación a la izquierda
SHR BL, CL ; BL = 00001011 Tres
rotaciones a la izquierda
El primer ROL desplaza el bit de más
a la izquierda del BL a la posición vacante de más a la derecha. La segunda y tercera
operaciones ROL realizan la rotación de los tres bits de más a la izquierda.
De manera similar a RCR, RCL
también provoca que la bandera de acarreo participe en la rotación. Cada bit
que se desplaza fuera por la izquierda se mueve al CF, y el bit del CF se mueve
a la posición vacante de la derecha.
Puede usar la instrucción JC
(salta si hay acarreo) para comprobar el bit rotado hacia la CF en el extremo
de una operación de rotación.
2.14 Obtención de una cadena con la representación hexadecimal.
La conversión entre numeración
binaria y hexadecimal es sencilla. Lo primero que se hace para una conversión
de un número binario a hexadecimal es dividirlo en grupos de 4 bits, empezando
de derecha a izquierda. En caso de que el último grupo (el que quede más a la
izquierda) sea menor de 4 bits se rellenan los faltantes con ceros.
Tomando como ejemplo el número
binario 101011 lo dividimos en grupos de 4 bits y nos queda:
10; 1011
Rellenando con ceros el último
grupo (el de la izquierda):
0010; 1011
después tomamos cada grupo como
un número independiente y consideramos su valor en decimal:
0010 = 2; 1011 = 11
Pero como no podemos representar
este número hexadecimal como 211 porqué sería un error, tenemos que sustituir
todos los valores mayores a 9 por su respectiva representación en hexadecimal,
con lo que obtenemos:
2BH (Donde la H representa la
base hexadecimal)
Para convertir un número de
hexadecimal a binario solo es necesario invertir estos pasos: se toma el primer
dígito hexadecimal y se convierte a binario, y luego el segundo, y así
sucesivamente hasta completar el número.
2.15 Captura y almacenamiento de datos numéricos.
Las variables numéricas son muy
útiles en ensamblador de la misma forma que en otros lenguajes de programación,
ya que permiten al programador hacer operaciones aritméticas con datos que se
desconocen al momento de la compilación.
La utilización de datos numéricos
es similar a la de cadenas, con la diferencia de que en vez de declarar las
variables como db, se declaran como dw, lo cual significa que son variables
numéricas.
Esta representación está basada
en la notación científica, esto es, representar un número en dos partes: su
mantisa y su exponente.
Poniendo como ejemplo el número
1234000, podemos representarlo como 1.123*10^6, en esta última notación el exponente
nos indica el número de espacios que hay que mover el espacio hacia la derecha
para obtener el resultado original.
En caso de que el exponente fuera
negativo nos estaría indicando el número de espacios que hay que recorrer el
punto decimal hacia la izquierda para obtener el original.
Proceso de creación de un programa
Para la creación de un programa
es necesario seguir cinco pasos: diseño del algoritmo, codificación del mismo,
su traducción a lenguaje máquina, la prueba del programa y la depuración.
En la etapa de diseño se plantea
el problema a resolver y se propone la mejor solución, creando diagramas esquemáticos
utilizados para el mejor planteamiento de la solución.
La codificación del programa
consiste en escribir el programa en algún lenguaje de programación; en este
caso específico en ensamblador, tomando como base la solución propuesta en el
paso anterior.
La traducción al lenguaje máquina
es la creación del programa objeto, esto es, el programa escrito como una
secuencia de ceros y unos que pueda ser interpretado por el procesador.
La prueba del programa consiste
en verificar que el programa funcione sin errores, o sea, que haga lo que tiene
que hacer.
2.16 Operaciones básicas sobre archivos de disco.
Un archivo está identificado por un nombre y contiene datos
en formato binario.
En lenguajes de alto nivel existen funciones especializadas
que permiten el manejo de archivos de forma directa, con instrucciones como
crear, leer o escribir archivo. En ensamblador el manipular un archivo es más complejo,
pero también es posible.
Hay dos formas diferentes de utilizar archivos en
ensamblador, la primera y más antigua es mediante bloques de control de archivo
o FCB por sus siglas en inglés. La segunda es mediante handles o canales de
comunicación.
FCB (File Control
Block): Permite tener un gran número de archivos abiertos y crear volúmenes
en dispositivos de almacenamiento.
El manejo de archivos con FCB utiliza el servicio 0FH de la
instrucción 21h para abrir un archivo.
Ejemplo:
mov ah, 0FH: Servicio para abrir un archivo.
mov dx, OFFSET Archivo:Se carga la dirección del
archivo a dx, ‘Archivo’ es una variable que
contiene la dirección del archivo al que se desea acceder.
Int 21h: Se llama la interrupcion 21h la cual ejecutara el servicio 0FH.
Handles: Permite
un manejo de errores más simple, utiliza la estructura de directorio del
sistema operativo y es más sencillo para el programador.
El manejo de archivos por Handles utiliza 3 servicios
principales para ello:
3CH: Crea un
nuevo archivo.
40H: Escribe
sobre un archivo existente.
3EH: Cierra un
archivo que se encuentre abierto.
También hay servicios de la interrupción 16h para manejo del teclado.
Función 00h. Lee un carácter. Esta función maneja las teclas
del teclado de 83 teclas, pero no acepta entrada de las teclas adicionales en
el teclado ampliado de 101 teclas. Retorna en al carácter, ah el código de
rastreo si al=0 es una tecla de función extendida.
Función 01h. Determina si un carácter está presente.
Función 02h. Regresa el estado actual de las teclas shift.
Función 10h. Lectura de un carácter del teclado.
Función 11h. Determina si está presente un carácter.
MOVS. Mueve un byte, palabra o palabra doble desde una
localidad en memoria direccionada por SI a otra localidad direccionada por DI.
LODS. Carga desde una localidad de memoria direccionada por
SI un byte en AL, una palabra en AX o una palabra doble en EAX.
STOS. Almacena el contenido de los registros AL, AX, o EAX
en la memoria direccionada por SI.
CMPS. Compara localidades de memoria de un byte, palabra o
palabra doble direccionadas por SI, DI.
SCAS. Compara el contenido de AL, AX o EAX con el contenido
de una localidad de memoria direccionada por SI.
Modularización
Los programas pueden escribirse en módulos, los que permiten
que un problema general pueda descomponerse en una serie de subproblemas
independientes (Divide y vencerás). Se puede repartir la tarea entre varias
personas, y concentrarse en la resolución de cada subproblema.
Cuando una tarea debe realizarse más de una vez en un mismo
programa, la modularización evita la programación redundante, ya que una vez
definida la tarea como un módulo independiente, puede ser invocada desde
cualquier parte del código; se aprecia también una menor longitud del programa.
Otra ventaja de importancia es la claridad que resulta de la
descomposición de un programa en módulos concisos e independientes,
representando cada uno de estos una parte bien definida del problema en su
conjunto, permitiendo escribir y depurar el código más fácil. Su estructura
lógica es más clara, lo cual es sumamente útil si el programa es largo y
complicado.
3.1 Procedimientos.
Un procedimiento es una secuencia de instrucciones que en
conjunto llevan a cabo una tarea específica.
En programación un procedimiento es un segmento de código
que cuenta con instrucciones a las cuales se puede acceder desde cualquier
parte del programa y una vez se termina la ejecución de estas, el programa
continuo con su ejecución normal, tomando el control la siguiente línea después
de la llamada al procedimiento. Los procedimientos tienden a ser grupos de
instrucciones que se necesitara ejecutar más de una vez dentro de un programa,
ya que un procedimiento puede ser llamado en cualquier momento durante la
ejecución del programa principal, la cantidad de veces que sea necesario sin
necesidad de reescribir el código.
En ensamblador los procedimientos están conformados por las
siguientes partes:
Declaración del procedimiento:
Los procedimientos en ensamblador se declaran mediante la
sintaxis nombreprocedimiento Proc [far/near] dependiendo de si es un
procedimiento cercano o lejano.
Código del procedimiento:
Dentro del procedimiento se escribe el código de ensamblador
que se quiere utilizar.
Directiva de regreso:
Antes del final de un procedimiento en ensamblador se
escribe la directiva de regreso ret,, la cual regresa el control a la línea
desde donde fue llamado el procedimiento.
Terminación del procedimiento:
Para terminar un procedimiento se escribe el nombre del
procedimiento seguido de la palabra reservaba endp.
Existen dos tipos de procedimientos que pueden utilizarse
dentro de ensamblador, estos son los internos y los externos.
Procedimientos Internos:
Estos procedimientos son aquellos que son declarados dentro
del mismo archivo de programa que serán llamados, también se les llama
procedimientos locales.
Para utilizarlos basta con escribir la palabra reservada
call seguida del nombre del procedimiento a utilizar.
Procedimientos Externos:
Los procedimientos externos se crean de la misma forma que
los internos pero tienen la diferencia de que están en archivos separados al
programa de donde el procedimiento es llamado, por lo que se necesitan
instrucciones extra para poder utilizarlos, las cuales son las siguientes:
PUBLIC: Es necesario declarar como publico el procedimiento que se
desea utilizar para que sea posible acceder a él desde otro programa.
EXTRN: Permite abrir procedimientos desde otro programa aunque no
se encuentre enlazado directamente.
INCLUDE: Enlaza el programa que llama el procedimiento con el que lo
contiene, permitiendo utilizarlo como si fuera un procedimiento propio.
Ejemplo:
public imprime: Procedimiento dentro del
primero archivo declarado como publico.
imprime proc far: Declaración del
procedimiento.
mov ah,09h: Código del procedimiento int 21h
ret: Directiva de regreso.
imprime endp: Fin del
procedimiento.
extrn imprime:near: Se incluye el
procedimiento externo imprime en el segundo
archivo.
call imprime: Se llama al procedimiento como si
fuera local.
3.2 Macros.
Una macro es un conjunto de instrucciones que pueden ser
llamadas utilizando su nombre para ejecutarse dentro de un programa, estas solo
se escriben una vez dentro del código y pueden utilizarse las veces que sea
necesario.
En ensamblador la diferencia entre los procedimientos y las
macros es que las macros tienen la posibilidad de utilizar parámetros por lo
que pueden llevar a cabo tareas que los procedimientos no podrían.
Las macros constan de tres partes que las definen:
Declaración: El inicio de una macro se declara escribiendo el nombre que
tendrá, seguido de la palabra reservada MACRO y opcionalmente, puede contener
parámetros después.
Cuerpo: Contiene todas las instrucciones que ejecutara la macro
cuando sea llamada dentro del programa en ejecución.
Fin: Toda macro debe terminar con la palabra reservada ENDM para
indicar el fin de la misma.
Al igual que con los procedimientos, existen dos tipos de
macros que son externas e internas, pero son muy fáciles de utilizar de
cualquiera de las dos formas, si se desea utilizar una macro externa se escribe
la palabra Include seguida del nombre del archivo de texto donde están
guardadas las macros antes del código del programa.
Ejemplo:
Include Macro.txt → Se enlaza con el archivo Macro.txt.
.model small → Declaración del tamaño del programa.
.stack 64 → Declaración de la pila.
.Data → Inicio del segmento de datos.
.Code
→ Inicio del segmento de
código.
Macro1 → Se llama a la macro Macro1.
.Exit
→ Inicio del segmento
final.
End
→ Fin del programa.
Referencias:
Consultado en: itpn.mx
Año de publicación: s.f.
Título del artículo: Unidad II - Programación básica
Fecha de recuperación del documento: 27-04-2020
Consultado en: wordpress.com
Año de publicación: s.f.
Título del artículo: Unidad 2 - Programación básica
Fecha de recuperación del documento: 27-04-2020
Comentarios
Publicar un comentario