Tema II Programación Básica - Crypto-ch4r/Lenguajes-de-Interfaz- GitHub Wiki

2.1 - Ensamblador (y ligador) a utilizar

El lenguaje ensamblador es un lenguaje de programación de bajo nivel que se utiliza para escribir programas que interactúan directamente con el hardware de una computadora. Es importante tener en cuenta que cada procesador tiene su propio conjunto de instrucciones, por lo que el ensamblador utilizado debe ser específico para el procesador en el que se está trabajando. Algunos ejemplos de ensambladores populares son: NASM, FASM y GAS (GNU Assembler).

Aunque todos los ensambladores realizan básicamente las mismas tareas, podemos clasificarlos de acuerdo a sus características.

Clasificación de ensambladores

Ensambladores Cruzados (Cross-Assembler).

Se denominan así los ensambladores que se utilizan en una computadora que posee un procesador diferente al que tendrán las computadoras donde va a ejecutarse el programa objeto producido. El empleo de este tipo de traductores permite aprovechar el soporte de medios físicos (discos, impresoras, pantallas, etc.), y de programación que ofrecen las máquinas potentes para desarrollar programas que luego los van a ejecutar sistemas muy especializados en determinados tipos de tareas.

Ensambladores Residentes.

Son aquellos que permanecen en la memoria principal de la computadora y cargan, para su ejecución, al programa objeto producido. Este tipo de ensamblador tiene la ventaja de que se puede comprobar inmediatamente el programa sin necesidad de transportarlo de un lugar a otro, como se hacía en cross-assembler, y sin necesidad de programas simuladores. Sin embargo, puede presentar problemas de espacio de memoria, ya que el traductor ocupa espacio que no puede ser utilizado por el programador. Asimismo, también ocupará memoria el programa fuente y el programa objeto. Esto obliga a tener un espacio de memoria relativamente amplio. Es el indicado para desarrollos de pequeños sistemas de control y sencillos automatismo empleando microprocesadores. La ventaja de estos ensambladores es que permiten ejecutar inmediatamente el programa; la desventaja es que deben mantenerse en la memoria principal tanto el ensamblador como el programa fuente y el programa objeto.

Macroensambladores.

Son ensambladores que permiten el uso de macroinstrucciones (macros). Debido a su potencia, normalmente son programas robustos que no permanecen en memoria una vez generado el programa objeto. Puede variar la complejidad de los mismos, dependiendo de las posibilidades de definición y manipulación de las macroinstrucciones, pero normalmente son programas bastantes complejos, por lo que suelen ser ensambladores residentes.

Microensambladores.

Generalmente, los procesadores utilizados en las computadoras tienen un repertorio fijo de instrucciones, es decir, que el intérprete de las mismas interpretaba de igual forma un determinado código de operación. El programa que indica al intérprete de instrucciones de la UCP cómo debe actuar se denomina microprograma. El programa que ayuda a realizar esta microprograma se llama microensamblador. Existen procesadores que permiten la modificación de sus microprogramas, para lo cual se utilizan microensambladores.

Ensambladores de una fase.

Estos ensambladores leen una línea del programa fuente y la traducen directamente para producir una instrucción en lenguaje máquina o la ejecuta si se trata de una pseudoinstrucción. También va construyendo la tabla de símbolos a medida que van apareciendo las definiciones de variables, etiquetas, etc. Debido a su forma de traducción, estos ensambladores obligan a definir los símbolos antes de ser empleados para que, cuando aparezca una referencia a un determinado símbolo en una instrucción, se conozca la dirección de dicho símbolo y se pueda traducir de forma correcta. Estos ensambladores son sencillos, baratos y ocupan poco espacio, pero tiene el inconveniente indicado.

Ensambladores de dos fases.

Los ensambladores de dos fases se denominan así debido a que realizan la traducción en dos etapas. En la primera fase, leen el programa fuente y construyen una tabla de símbolos; de esta manera, en la segunda fase, vuelven a leer el programa fuente y pueden ir traduciendo totalmente, puesto que conocen la totalidad de los símbolos utilizados y las posiciones que se les ha asignado. Estos ensambladores son los más utilizados en la actualidad.

2.2 Ciclos numéricos

La arquitectura de los procesadores x86 obliga al uso de segmentos de memoria para manejar la información, el tamaño de estos segmentos es de 64kb. La razón de ser de estos segmentos es que, considerando que el tamaño máximo de un número que puede manejar el procesador esta dado por una palabra de 16 bits o registro, no sería posible accesar a más de 65536 localidades de memoria utilizando uno solo de estos registros, ahora, si se divide la memoria de la pc en grupos o segmentos, cada uno de 65536 localidades, y utilizamos una dirección en un registro exclusivo para localizar cada segmento, y entonces cada dirección de una casilla específica la formamos con dos registros, nos es posible acceder a una cantidad de 4294967296 bytes de memoria, lo cual es, en la actualidad, más memoria de la que veremos instalada en una PC. Para que el ensamblador pueda manejar los datos es necesario que cada dato o instrucción se encuentren localizados en el área que corresponde a sus respectivos segmentos. El ensamblador accede a esta información tomando en cuenta la localización del segmento, dada por los registros DS, ES, SS y CS, y dentro de dicho registro la dirección del dato específico

2.3 Captura básica de cadenas

En el lenguaje ensamblador el tipo de dato cadena (string) no está definido, pero para fines de programación, una cadena es definida como un conjunto de localidades de memoria consecutivas que se reservan bajo el nombre de una variable.

Instrucciones para el manejo de strings

MOVSB

Mueve un byte desde una localidad de memoria hasta otra.

MOVSW

Mueve una palabra desde una localidad de memoria hasta otra.

LODS (cargar un byte o palabra)

carga el registro acumulador (AX o AL) con el valor de la localidad de memoria determinada por DS:SI se incrementa tras la transferencia. En el siguiente ejemplo simplemente capturamos una cadena en un vector y la imprimimos cuantas veces quiera el usuario, en este programa aprendemos a usar vectores en MASM. Los vectores se pueden declarar de cualquier tipo de dato que una variable cualquiera solo que se especifica la longitud del vector y con que tipo de dato inicia al crearse para ello se utiliza la palabra reservada dup.

.model small
.stack 64
.data
inicio db ‘Repetir Texto..’,10,13,’$’
ingresar db 10,13,’Ingresa tu nombre’, 10,13,’$’
nombre db ‘Su nombre es: $’
repetir db 10,13,’Quiere repetir s/n?’,10,13,’$’
;Declaracion del vector
vtext db 100 dup(‘$’)

.code
mov ax,@data
MOV DS,AX
lea dx,inicio
mov ah,09
int 21h

lea dx,ingresar
mov ah,09
int 21h

;Iniciamos nuestro conteo de si en la posicion 0.
mov si,00h
leer:
mov ax,0000
mov ah,01h
int 21h
;Guardamos el valor tecleado por el usuario en la posicion si del vector.
mov vtext[si],al
inc si   ;Incrementamos nuestro contador
cmp al,0dh  ;Se repite el ingreso de datos hasta que se teclee un Enter.
ja leer
jb leer
lea dx,nombre
mov ah,09
int 21h

ver:
mov dx,offset vtext  ;Imprime el contenido del vector con la misma instrucción de una cadena
mov ah,09h
int 21h
lea dx,repetir  ;imprime si quiere ver de nuevo el texto escrito.
mov ah,09
int 21h
mov ah,01h
int 21h
cmp al,73h ;Si la tecla presiona es una s se repite la impresión del vector.
je ver

salir:
mov ah,4ch
int 21h

end

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

Los ciclos condicionales son estructuras de control de flujo que permiten a los programas tomar decisiones basadas en condiciones específicas. En esencia, un ciclo condicional es una construcción de programación que le indica al programa qué hacer si se cumple una condición determinada, y qué hacer si no se cumple.

En muchos lenguajes de programación, el ciclo condicional más básico es la instrucción "if". La instrucción "if" evalúa una condición y, si la condición es verdadera, ejecuta un bloque de código específico. Si la condición es falsa, el bloque de código no se ejecuta. En algunos lenguajes de programación, también hay una instrucción "else" que permite ejecutar un bloque de código diferente si la condición evaluada en el "if" es falsa. Otra estructura de control de flujo común en los lenguajes de programación es la instrucción "switch". La instrucción "switch" permite evaluar una expresión y ejecutar diferentes bloques de código dependiendo del valor de la expresión. En ensamblador, tenemos los siguientes ciclos condicionales:

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. Sintaxis y ejemplo:

  mov cx, 5       ; Establece el contador en 5
loop_start:
    ; Inserte aquí el código que se repetirá en el loop
    loop loop_start ; Repite el loop hasta que CX sea 0

Instrucción LOOPE

Propósito: Generar un ciclo en el programa considerando el estado de ZF. Sintaxis y ejemplo:

    mov ecx, 10     ; Establece el contador en 10
    mov esi, offset cadena ; Establece el puntero a la cadena
    mov al, 'A'     ; Establece el valor a buscar en la cadena
loop_start:
    cmp byte ptr [esi], al ; Compara el valor actual de la cadena con AL
    jne loop_exit   ; Salta fuera del loop si la comparación no es verdadera
    inc esi         ; Incrementa el puntero de cadena
    dec ecx         ; Decrementa el contador
    loope loop_start ; Repite el loop mientras ECX no sea cero y la comparación sea verdadera
loop_exit:

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. Sintaxis y ejemplo:

    mov ecx, 5      ; Establece el contador en 5
    mov esi, offset cadena ; Establece el puntero a la cadena
    mov al, 'A'     ; Establece el valor a buscar en la cadena
loop_start:
    cmpsb           ; Compara el valor actual de la cadena con AL
    loopne loop_start ; Repite el loop mientras ECX no sea cero y la comparación sea verdadera
    ; Inserte aquí el código que se ejecutará después del loop

Instrucción LOOPNE

Propósito: Generar un ciclo en el programa, considerando el estado de ZF. Sintaxis y ejemplo:

    mov ecx, 10     ; Establece el contador en 10
    mov esi, offset cadena ; Establece el puntero a la cadena
    mov al, 'A'     ; Establece el valor a buscar en la cadena
loop_start:
    cmp byte ptr [esi], al ; Compara el valor actual de la cadena con AL
    je loop_exit    ; Salta fuera del loop si la comparación es verdadera
    inc esi         ; Incrementa el puntero de cadena
    dec ecx         ; Decrementa el contador
    loopne loop_start ; Repite el loop mientras ECX no sea cero y la comparación no sea verdadera
loop_exit:

LOOPNE etiqueta

Esta instrucción decrementa en uno a CX y transfiere el flujo del programa solo si ZF es diferente a 0. Sintaxis y ejemplo

    mov ecx, 5      ; Establece el contador en 5
    mov esi, offset cadena ; Establece el puntero a la cadena
    mov al, 'A'     ; Establece el valor a buscar en la cadena
loop_start:
    cmpsb           ; Compara el valor actual de la cadena con AL
    loopne loop_start ; Repite el loop mientras ECX no sea cero y la comparación no sea verdadera
    ; Inserte aquí el código que se ejecutará después del loop

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, y DEC lo decrementa. A continuación podemos apreciar su sintaxis y ejemplo de uso:

INC

    mov eax, 5      ; Establece el valor inicial de EAX en 5
    inc eax         ; Incrementa el valor de EAX en 1
    ; EAX ahora tiene un valor de 6

DEC

    mov ebx, 6      ; Establece el valor inicial de EBX en 6
    dec ebx         ; Decrementa el valor de EBX en 1
    ; EBX ahora tiene un valor de 5

2.8 Captura de cadenas con formato

La captura de cadenas con formato es una técnica utilizada en programación para leer datos de entrada en un formato específico. El capturar cadenas con formato permite el movimiento, comparación o búsqueda rápida entre bloques de datos, las instrucciones en Ensamblador son las siguientes:

MOVC

Esta instrucción permite transferir un carácter de una cadena. Sintaxis y ejemplo:

    MOVC destino, origen, condición
    mov ecx, 0x12345678    ; Establece un valor inicial en el registro ECX
    add ecx, 0x9ABCDEF0    ; Realiza una operación que establece la bandera "carry"
    mov edx, [ebx]         ; Mueve el valor de la ubicación de memoria apuntada por EBX a EDX si se cumple la condición
    movc eax, edx, carry   ; Mueve el valor de EDX a EAX solo si la bandera "carry" está establecida

MOVW

Esta instrucción permite transferir una palabra de una cadena. Sintaxis y ejemplo:

    MOVW destino, origen
    movw ax, [0x1000]   ; Mueve un valor de 16 bits desde la ubicación de memoria 0x1000 al registro AX
    movw [0x2000], ax   ; Mueve el valor almacenado en AX a la ubicación de memoria 0x2000

CMPC

Este comando es utilizado para comparar un carácter de una cadena. Sintaxis y ejemplo:

    mov eax, 0x12345678    ; Establece un valor inicial en el registro EAX
    mov ebx, 0x87654321    ; Establece otro valor en el registro EBX
    cmpc eax, ebx, less    ; Compara los valores de EAX y EBX y establece la bandera "less" si EAX es menor que EBX

CMPW

Esta instrucción es utilizada para comparar una palabra de una cadena. Sintaxis y ejemplo:

    mov ax, 0x1234    ; Establece un valor inicial en el registro AX
    mov bx, 0x5678    ; Establece otro valor en el registro BX
    cmpw ax, bx       ; Compara los valores de AX y BX y establece las banderas de estado apropiadas

SCAC

Esta instrucción permite buscar un carácter de una cadena. Sintaxis y ejemplo:

    mov esi, offset cadena  ; Establece la dirección de la cadena en el registro ESI
    mov al, 'A'             ; Establece el byte buscado en el registro AL
    scac esi, al            ; Escanea la cadena de datos buscando el byte 'A'
    jnz no_encontrado       ; Salta a la etiqueta "no_encontrado" si el byte no se encontró
    ; El byte 'A' fue encontrado en la cadena de datos
    ; Código adicional aquí
no_encontrado:
    ; Código adicional aquí

SCAW

Esta instrucción se utiliza para buscar una palabra de una cadena. Sintaxis y ejemplo:

    mov esi, offset cadena  ; Establece la dirección de la cadena en el registro ESI
    mov ax, 'AB'            ; Establece la palabra buscada en el registro AX
    scaw esi, ax            ; Escanea la cadena de datos buscando la palabra "AB"
    jnz no_encontrado       ; Salta a la etiqueta "no_encontrado" si la palabra no se encontró
    ; La palabra "AB" fue encontrada en la cadena de datos
    ; Código adicional aquí
no_encontrado:
    ; Código adicional aquí

LODC

Esta instrucción permite cargar un carácter de una cadena. Sintaxis y ejemplo:

LODC AX, 10  ; Carga el valor 10 en el registro AX

LODW

Esta instrucción es utilizada para cargar una palabra de una cadena Sintaxis y ejemplo:

LODW AX, [memoria]  ; Carga el valor de la palabra de memoria en el registro AX

STOC

Esta instrucción permite guardar un carácter de una cadena. Sintaxis y ejemplo:

STOC var, 5  ; Almacena el valor 5 en la dirección de memoria "var"

STOW

Esta instrucción es utilizada para guardar una palabra de una cadena. Sintaxis y ejemplo:

STOW [memoria], AX  ; Almacena el valor del registro AX en la dirección de memoria "memoria"

2.9 Instrucciones aritméticas

ADC

Adición con carreo. 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. Sintaxis y ejemplo:

ADC destino, origen
ADC CX, BX   ; Suma el valor de BX con el valor de CX y el bit de acarreo, y almacena el resultado en CX

ADD

Adición de los operandos. Suma los dos operandos y guarda el resultado en el operando destino. Sintaxis y ejemplo:

section .data
    var1 dd 5
    var2 dd 10
    result dd 0

section .text
    global _start

_start:
    mov eax, [var1]
    add eax, [var2]
    mov [result], eax
    mov eax, 1
    xor ebx, ebx
    int 0x80

DIV

División sin signo. 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 tomara 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, sí fue una palabra el cociente se guarda en AX y el residuo en DX. Sintaxis y ejemplo:

DIV divisor
MOV BX, 4     ; Asigna el valor 4 al registro BX
DIV BX        ; Divide el valor de AX por el valor de BX, y almacena el resultado en AX y el residuo en DX

IDIV

División con signo. Consiste básicamente en lo mismo que la instrucción DIV, solo que esta última realiza la operación con signo. Sintaxis y ejemplo:

IDIV divisor
MOV BX, 4     ; Asigna el valor 4 al registro BX
IDIV BX       ; Divide el valor de AX por el valor de BX con signo, y almacena el resultado en AX y el residuo en DX

MUL

Multiplicación sin signo. El ensamblador asume que el multiplicando sera 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. Sintaxis y ejemplo:

MUL multiplicando
MOV BX, 10    ; Asigna el valor 10 al registro BX
MUL BX        ; Multiplica el valor de AX por el valor de BX sin signo, y almacena el resultado en DX:AX

IMUL

Multiplicación de dos enteros con signo. 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. Sintaxis y ejemplo:

IMUL multiplicando
MOV BX, -10    ; Asigna el valor -10 al registro BX con signo
IMUL BX        ; Multiplica el valor de AX por el valor de BX con signo, y almacena el resultado en DX:AX

SBB

Substracción con acarreo. 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. Sintaxis y ejemplo:

SBB destino, origen
MOV CX, 0     ; Asigna el valor 0 al registro CX
MOV AX, 10    ; Asigna el valor 10 al registro AX
MOV BX, 5     ; Asigna el valor 5 al registro BX
SBB CX, BX    ; Resta el valor de BX de CX y el acarreo de AX, y almacena el resultado en CX

SUB

Substracción. Resta el operando fuente del destino. Sintaxis y ejemplo:

SUB destino, origen
MOV CX, 0    ; Asigna el valor 0 al registro CX
MOV AX, 10   ; Asigna el valor 10 al registro AX
MOV BX, 5    ; Asigna el valor 5 al registro BX
SUB CX, BX   ; Resta el valor de BX de CX y almacena el resultado en CX

2.10 Manipulación de la pila

-ROT (a b c – c a b) Rota hacia atrás.
-2ROT (ab cd ef – ef ab cd) Rota hacia atrás.
NIP ( a b – b) Quita a de la pila.
OUTK (... n -- ..) Elimina el elemento n.
TUCK (a b -- b a b) Inserta una copia de b.
2?DUP (ab – ab ab) Duplica si ab <> 0.
2DROP (ab -- ) Elimina 2 de encima.
2DUP ( ab – ab ab) Duplica los elementos.
2NIP (ab cd – cd) Elimina elementos.
2OUTK (::: n -- ::) Elimina el elemento n
2OVER (ab cd – ab cd ab) Inserta una copia de ab.
2PICK (:: n -- :::) Copia el elemento n encima de la pila
2ROLL (::: n -- :::) Quita el elemento n y lo deja arriba de la pila.
2ROT (ab cd ef – cd ef ab) Rota los elementos
2TUCK (ab cd – cd ab cd) Inserta una copia de cd.
2SWAP (ab cd – cd ab) Rota los elementos.

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
.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 y ejemplo:

AND destino, fuente

AND ax, bx

NOT

Lleva a cabo la negación bit por bit del operando destino. Sintaxis y ejemplo:

NOT destino

NOT ax

OR

La instrucción OR lleva a cabo, bit por bit, la disyunción inclusiva lógica de los dos operandos. Sintaxis y ejemplo:

OR destino, fuente

OR ax,bx

XOR

Su función es efectuar bit por bit la disyunción exclusiva lógica de los dos operandos. Sintaxis y ejemplo:

XOR destino, fuente

XOR ax, bx

NEG

Genera el complemento a 2 del operando destino y lo almacena en este mismo operando. Sintaxis y ejemplo:

NEG destino

NEG ax

TEST

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.

TEST destino, fuente

TEST ax, bx

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).

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):

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

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).

Rotación a la derecha

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).

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):

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

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

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 el 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.