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 


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

Entradas más populares de este blog

Lista de mnemónicos para lenguaje ensamblador

Programas en lenguaje ensamblador.

1.4 Aspectos matemáticos de la graficación (geometría fractal)