Unidad 2 Programación básica - JoseCorreaMorales/lenguajes-de-interfaz GitHub Wiki

2.1 Ensamblador (y ligador) a utilizar

El ensamblador es un lenguaje de bajo nivel que se utiliza para escribir programas que se ejecutan directamente en el hardware de la computadora. En este lenguaje, se escriben las instrucciones en un formato que la CPU pueda entender. El ensamblador es una herramienta poderosa que le permite al programador tener un control total sobre la computadora, lo que puede ser útil para tareas de bajo nivel, como la programación de controladores de dispositivos.

Para escribir programas en ensamblador, se necesita un ensamblador y un ligador. El ensamblador es un programa que traduce el código fuente escrito en ensamblador en código máquina. El ligador, por su parte, se encarga de combinar diferentes archivos de código objeto en un solo archivo ejecutable.

Uno de los ensambladores más populares es NASM (Netwide Assembler), que es un ensamblador gratuito y de código abierto disponible en varios sistemas operativos, incluidos Windows, Linux y macOS. NASM es muy flexible y admite diferentes formatos de salida, lo que lo hace adecuado para diferentes sistemas operativos y arquitecturas de CPU.

2.2 Ciclos numéricos

Los ciclos numéricos, también conocidos como bucles, son una técnica común en programación para repetir un conjunto de instrucciones varias veces. En ensamblador, se pueden utilizar ciclos numéricos para repetir un conjunto de instrucciones mientras se cumpla una condición específica.

En ensamblador, existen diferentes tipos de ciclos numéricos, como el ciclo FOR y el ciclo WHILE. En el ciclo FOR, se define un contador y se establece un límite superior. El ciclo se repetirá hasta que el contador alcance el límite superior. En el ciclo WHILE, se evalúa una condición y el ciclo se repite hasta que la condición deje de ser verdadera.

Para implementar un ciclo en ensamblador, se utilizan las instrucciones de salto condicional, como JNZ (Jump if Not Zero) o JE (Jump if Equal). Estas instrucciones permiten saltar a una etiqueta específica si se cumple una condición determinada.

Tipos

En ensamblador con sintaxis Intel, existen diferentes tipos de ciclos numéricos, los cuales se pueden implementar utilizando instrucciones de salto condicional y contadores.

A continuación, se describen los tipos de ciclos numéricos más comunes en ensamblador con sintaxis Intel:

Ciclo FOR

Este ciclo se utiliza para repetir un conjunto de instrucciones un número determinado de veces. En la sintaxis Intel, el ciclo FOR se implementa utilizando la instrucción "loop", que repite el conjunto de instrucciones hasta que un contador específico (almacenado en el registro ECX) llega a cero. La sintaxis básica de un ciclo FOR en ensamblador con sintaxis Intel es la siguiente:

mov ecx, limite
for_loop:
; conjunto de instrucciones a repetir
loop for_loop

En este ejemplo, "limite" es una constante o variable que establece el número de veces que se debe repetir el conjunto de instrucciones. La etiqueta "for_loop" se utiliza para marcar el inicio del ciclo.

Ciclo WHILE

Este ciclo se utiliza para repetir un conjunto de instrucciones mientras una condición específica sea verdadera. En la sintaxis Intel, el ciclo WHILE se implementa utilizando instrucciones de salto condicional, como "jmp", "je", "jne", "jg", "jl", entre otras. La sintaxis básica de un ciclo WHILE en ensamblador con sintaxis Intel es la siguiente:

while_loop:
; conjunto de instrucciones a repetir
jmp while_loop

En este ejemplo, "while_loop" es una etiqueta que se utiliza para marcar el inicio del ciclo. La instrucción "jmp" salta de nuevo al inicio del ciclo mientras la condición no se cumpla.

Ciclo DO-WHILE

Este ciclo se utiliza para repetir un conjunto de instrucciones al menos una vez, y luego repetirlo mientras una condición específica sea verdadera. En la sintaxis Intel, el ciclo DO-WHILE se implementa utilizando instrucciones de salto condicional, como "jmp", "je", "jne", "jg", "jl", entre otras. La sintaxis básica de un ciclo DO-WHILE en ensamblador con sintaxis Intel es la siguiente:

do_while_loop:
; conjunto de instrucciones a repetir
jmp condicion
while_loop:
; conjunto de instrucciones a repetir
condicion:
; condición a evaluar
jnz while_loop

En este ejemplo, "do_while_loop" es una etiqueta que se utiliza para marcar el inicio del ciclo. El conjunto de instrucciones se repite al menos una vez antes de evaluar la condición. La etiqueta "while_loop" se utiliza para marcar el inicio del ciclo mientras la condición sea verdadera. La instrucción "jnz" salta al inicio del ciclo mientras la condición sea verdadera.

2.3 Captura básica de cadenas

La captura de cadenas en ensamblador se refiere a la operación de leer una serie de caracteres desde la entrada del usuario, almacenarlos en memoria y luego procesarlos. Es un proceso importante en la programación de sistemas ya que se utiliza para leer la entrada del usuario y procesarla según los requisitos del programa.

En ensamblador, la captura de cadenas se puede realizar utilizando las llamadas al sistema (syscalls) proporcionadas por el kernel del sistema operativo. Estas llamadas al sistema permiten al programa interactuar con el sistema operativo y realizar tareas como la lectura de la entrada del usuario y la escritura en la salida estándar.

La captura de cadenas se realiza en dos pasos: primero se reserva espacio de memoria para almacenar la cadena capturada y luego se utiliza una llamada al sistema para leer la entrada del usuario y almacenarla en la memoria reservada.

a continuación las partes esenciales (no todo el programa completo), para el proceso de captura de cadenas)
section .data
    mensaje db 'Ingrese una cadena: '
    cadena db 100, 0

    ; Leer entrada del usuario
    mov eax, 3
    mov ebx, 0
    mov ecx, cadena
    mov edx, 100
    int 0x80

    ; Imprimir la cadena capturada
    mov eax, 4
    mov ebx, 1
    mov ecx, cadena
    int 0x80

2.4 Comprobación y prueba

En ensamblador, la comprobación y prueba (check and test) se refieren a las operaciones que se realizan para verificar si un valor cumple ciertas condiciones o para comprobar el estado de una bandera o registro.

La comprobación se utiliza comúnmente en estructuras de control de flujo, como condicionales y bucles, para verificar si se cumple una condición y decidir qué acción tomar en consecuencia. Por ejemplo, en un condicional, se puede verificar si un valor es igual a cero antes de tomar una decisión.

La prueba se utiliza para comprobar el estado de una bandera o registro, como el registro de estado de la CPU (EFLAGS) en x86. Las pruebas suelen realizarse después de una operación aritmética o lógica para comprobar si se ha establecido una bandera determinada. Por ejemplo, después de realizar una operación de suma, se puede comprobar si el resultado es cero o si se ha producido un acarreo.

Las instrucciones más comunes utilizadas para la comprobación y prueba son las siguientes:

CMP: compara dos valores y establece las banderas en función del resultado de la comparación.

TEST: realiza una operación lógica AND entre dos valores y establece las banderas en función del resultado.

Jcc: instrucciones de salto condicional que permiten al programa saltar a diferentes ubicaciones en función del estado de las banderas.

Ejemplo
section .data
    valor db 5

section .text
    global _start

_start:
    ; Comprobar si el valor es cero
    cmp byte [valor], 0
    je valor_es_cero

    ; Si el valor no es cero, imprimir un mensaje
    mov eax, 4
    mov ebx, 1
    mov ecx, mensaje_no_es_cero
    mov edx, 18
    int 0x80

    ; Finalizar el programa
    mov eax, 1
    xor ebx, ebx
    int 0x80

valor_es_cero:
    ; Si el valor es cero, imprimir un mensaje
    mov eax, 4
    mov ebx, 1
    mov ecx, mensaje_es_cero
    mov edx, 16
    int 0x80

    ; Finalizar el programa
    mov eax, 1
    xor ebx, ebx
    int 0x80

section .data
    mensaje_es_cero db 'El valor es cero', 0
    mensaje_no_es_cero db 'El valor no es cero', 0

2.5 Saltos

En lenguaje ensamblador, los saltos son una herramienta importante para controlar el flujo del programa. Los saltos permiten que el programa pueda realizar diferentes acciones según una condición o realizar una iteración en un ciclo.

Los saltos son instrucciones que permiten transferir el control del programa a una dirección específica en la memoria del equipo. En lenguaje ensamblador, se utilizan etiquetas para indicar las direcciones de memoria a las que se desea saltar.

Existen varios tipos de saltos en ensamblador, entre ellos están:

JMP: salto incondicional que siempre se ejecuta. Se utiliza para transferir el control del programa a otra parte del código sin importar el resultado de una comparación o prueba.

JZ o JE: salto si cero o salto si igual. Se utiliza para saltar a una dirección de memoria específica si el valor de un registro es cero o si dos registros son iguales.

JNZ o JNE: salto si no cero o salto si no igual. Se utiliza para saltar a una dirección de memoria específica si el valor de un registro no es cero o si dos registros no son iguales.

JA, JAE, JB, JBE: saltos si por encima, por encima o igual, por debajo, por debajo o igual. Estos saltos se utilizan para comparar valores numéricos en registros y saltar a una dirección de memoria específica según el resultado de la comparación.

LOOP: salto utilizado para realizar una iteración en un ciclo. Este salto decrementa el valor de un registro contador y salta a una dirección de memoria específica hasta que el valor del contador sea cero.

section .data
    valor1 dd 10
    valor2 dd 20

section .bss
    resultado resd 1

section .text
    global _start

_start:
    ; Almacenar los valores en la pila
    PUSH DWORD [valor1]
    PUSH DWORD [valor2]

    ; Sumar los dos valores
    POP EBX
    POP EAX
    ADD EAX, EBX

    ; Almacenar el resultado en la variable "resultado"
    MOV DWORD [resultado], EAX

    ; Salir del programa
    MOV EAX, 1
    XOR EBX, EBX
    INT 0x80

2.6 Ciclos condicionales

Los ciclos condicionales son estructuras de control que permiten que el programa repita un bloque de instrucciones hasta que se cumpla una condición específica

En el lenguaje ensamblador, los ciclos condicionales se implementan utilizando dos instrucciones: la instrucción de comparación y la instrucción de salto condicional. La instrucción de comparación se utiliza para comparar dos valores y establecer las banderas de estado, mientras que la instrucción de salto condicional se utiliza para saltar a una dirección específica en función del estado de las banderas.

La sintaxis general de un ciclo condicional en lenguaje ensamblador con sintaxis Intel es la siguiente:

etiqueta:
    ; Cuerpo del ciclo
    ; ...
    ; Comparar dos valores
    cmp operando1, operando2

    ; Saltar a la etiqueta si se cumple la condición
    jcond etiqueta

En este código, "etiqueta" es una etiqueta que se utiliza como un punto de referencia para el salto condicional. El cuerpo del ciclo se ejecuta una vez y luego se realiza la comparación. Si se cumple la condición, el programa salta de vuelta a la etiqueta y repite el ciclo. Si la condición no se cumple, el programa continúa con la instrucción que sigue al ciclo.

Las condiciones que se pueden utilizar con la instrucción de salto condicional son las siguientes:

  • je: (jump if equal) salta si los operandos son iguales
  • jne: (jump if not equal) salta si los operandos son diferentes
  • jg: (jump if greater) salta si el primer operando es mayor que el segundo
  • jge: (jump if greater or equal) salta si el primer operando es mayor o igual que el segundo
  • jl: (jump if less) salta si el primer operando es menor que el segundo
  • jle: (jump if less or equal) salta si el primer operando es menor o igual que el segundo

2.7 Incremento y decremento

En el lenguaje ensamblador, el incremento y decremento son operaciones aritméticas básicas que se utilizan para aumentar o disminuir el valor de una variable en una unidad

En el lenguaje ensamblador, el incremento y decremento se implementan utilizando las instrucciones inc y dec, respectivamente. Estas instrucciones tienen la sintaxis siguiente:

inc operando
dec operando

En estas instrucciones, "operando" es la variable a la que se le va a aplicar la operación de incremento o decremento. El operando puede ser un registro o una posición de memoria.

La instrucción inc incrementa el valor del operando en una unidad, mientras que la instrucción dec decrementa el valor del operando en una unidad. Ambas instrucciones modifican directamente el valor del operando, sin necesidad de cargar o almacenar valores adicionales.

Ejemplo
section .data
    mensaje db 'Número: ',0
section .bss
    contador resb 1
section .text
    global _start

_start:
    ; Inicializar el contador en 1
    mov byte [contador], 1

bucle:
    ; Imprimir el número
    mov eax, 4
    mov ebx, 1
    mov ecx, mensaje
    mov edx, 8
    int 0x80

    mov eax, 4
    mov ebx, 1
    mov ecx, contador
    mov edx, 1
    int 0x80

    ; Incrementar el contador
    inc byte [contador]

    ; Salir del bucle si el contador llega a 11
    cmp byte [contador], 11
    jge fin

    ; Saltar al inicio del bucle
    jmp bucle

fin:
    ; Salir del programa
    mov eax, 1
    xor ebx, ebx
    int 0x80

2.8 Captura de cadenas con formato

En el lenguaje ensamblador, la captura de cadenas con formato es una técnica que se utiliza para leer una cadena de caracteres de entrada y convertirla en una representación numérica en la memoria.

Para capturar cadenas con formato en lenguaje ensamblador, se puede utilizar la función scanf de la biblioteca C estándar. Esta función permite leer una cadena de caracteres desde la entrada estándar (por ejemplo, la consola), y convertirla en una representación numérica en la memoria.

La función scanf tiene la siguiente sintaxis:

scanf formato, operando

2.9 Instrucciones aritméticas

Las instrucciones aritméticas son un conjunto de instrucciones que permiten realizar operaciones matemáticas en los registros y la memoria. Estas instrucciones son fundamentales para el procesamiento de datos en lenguaje ensamblador y se utilizan en una amplia variedad de aplicaciones

Algunas de las instrucciones aritméticas más comunes son:

ADD Esta instrucción se utiliza para sumar dos valores y almacenar el resultado en un registro o en la memoria. Su sintaxis es la siguiente:

ADD destino, fuente

En esta sintaxis, "destino" es el registro o la dirección de memoria donde se almacenará el resultado de la suma, y "fuente" es el registro o la dirección de memoria que contiene el valor a sumar.

SUB Esta instrucción se utiliza para restar dos valores y almacenar el resultado en un registro o en la memoria. Su sintaxis es similar a la de la instrucción ADD:

SUB destino, fuente

En esta sintaxis, "destino" es el registro o la dirección de memoria donde se almacenará el resultado de la resta, y "fuente" es el registro o la dirección de memoria que contiene el valor a restar.

MUL Esta instrucción se utiliza para multiplicar dos valores y almacenar el resultado en un registro. Su sintaxis es la siguiente:

MUL fuente

En esta sintaxis, "fuente" es el registro o la dirección de memoria que contiene el valor a multiplicar. El resultado se almacena en los registros AX y DX.

DIV Esta instrucción se utiliza para dividir dos valores y almacenar el resultado en un registro. Su sintaxis es la siguiente:

DIV fuente

En esta sintaxis, "fuente" es el registro o la dirección de memoria que contiene el valor a dividir. El cociente se almacena en el registro AX y el resto se almacena en el registro DX.

2.10 Manipulación de la pila

La pila es una estructura de datos en la que los elementos se agregan y eliminan siguiendo el principio LIFO (Last In, First Out). En lenguaje ensamblador, la pila se utiliza para almacenar temporalmente los valores de los registros y las variables en la memoria, lo que permite ahorrar espacio en los registros y facilita la transferencia de datos entre diferentes partes del programa

Para manipular la pila en lenguaje ensamblador con sintaxis Intel, se utilizan las siguientes instrucciones:

  • PUSH Esta instrucción se utiliza para insertar un valor en la pila. Su sintaxis es la siguiente:
push valor
  • POP Esta instrucción se utiliza para eliminar un valor de la pila. Su sintaxis es la siguiente:
POP registro

Ejemplo

section .data
    valor1 dd 10
    valor2 dd 20

section .bss
    resultado resd 1

section .text
    global _start

_start:
    ; Almacenar los valores en la pila
    PUSH DWORD [valor1]
    PUSH DWORD [valor2]

    ; Sumar los dos valores
    POP EBX
    POP EAX
    ADD EAX, EBX

    ; Almacenar el resultado en la variable "resultado"
    MOV DWORD [resultado], EAX

    ; Salir del programa
    MOV EAX, 1
    XOR EBX, EBX
    INT 0x80

2.11 Obtención de cadena con representación decimal

El tema de Obtención de cadena con representación decimal en lenguaje ensamblador se refiere a la técnica para convertir un número entero en una cadena de caracteres que representan su valor en formato decimal. Esta técnica es muy común en la programación de bajo nivel, y es necesaria para mostrar valores numéricos en formato legible para el usuario en interfaces de usuario y en registros de archivo de registro.

En lenguaje ensamblador, la obtención de cadena con representación decimal se logra a través de una serie de operaciones aritméticas y de conversión de datos. El algoritmo típico consiste en dividir el número por 10 repetidamente y almacenar el resto de cada división en una variable de caracteres. Luego, los caracteres se invierten y se convierten en una cadena que representa el número en formato decimal.

El proceso comienza dividiendo el número original por 10 para obtener el primer dígito del número. Luego, el resto de la división se almacena en una variable de caracteres. El proceso se repite para obtener los dígitos restantes del número, cada vez dividiendo el resultado anterior por 10 y almacenando el resto. Una vez que se han obtenido todos los dígitos, se invierte el orden de los caracteres para que la cadena represente correctamente el número en formato decimal.

2.12 Instrucciones lógicas

El tema de las instrucciones lógicas en lenguaje ensamblador se refiere a un conjunto de instrucciones que realizan operaciones lógicas sobre bits o bytes de datos en una CPU. Estas operaciones incluyen operaciones lógicas AND, OR, XOR y NOT.

Las instrucciones lógicas son utilizadas comúnmente en aplicaciones de programación de bajo nivel para la manipulación de bits, máscaras y banderas de control. También se utilizan en la creación de algoritmos de codificación y decodificación de datos, así como en la manipulación de datos en formato binario.

La instrucción lógica AND se utiliza para comparar dos valores binarios, y devuelve un valor binario que tiene un 1 en todas las posiciones en las que ambos valores tienen un 1, y un 0 en todas las demás posiciones. La instrucción lógica OR devuelve un valor binario que tiene un 1 en todas las posiciones en las que al menos uno de los dos valores tiene un 1, y un 0 en todas las demás posiciones. La instrucción lógica XOR devuelve un valor binario que tiene un 1 en todas las posiciones en las que sólo uno de los dos valores tiene un 1, y un 0 en todas las demás posiciones. La instrucción lógica NOT se utiliza para invertir el valor de los bits de un registro o memoria, convirtiendo todos los 0s en 1s y todos los 1s en 0s.

2.13 Desplazamiento y rotación

Las instrucciones de desplazamiento y rotación son utilizadas comúnmente en aplicaciones de programación de bajo nivel para la manipulación de bits, máscaras y banderas de control. También se utilizan en la creación de algoritmos de codificación y decodificación de datos, así como en la manipulación de datos en formato binario.

Desplazamiento

El desplazamiento es una operación en la que se mueven los bits de un valor hacia la izquierda o hacia la derecha. En el desplazamiento hacia la izquierda, los bits se mueven hacia la izquierda y se rellena el espacio vacío con ceros. En el desplazamiento hacia la derecha, los bits se mueven hacia la derecha y se rellena el espacio vacío con ceros o con el bit más significativo (en el caso de un desplazamiento aritmético). El desplazamiento se realiza mediante las instrucciones SHL (shift left) y SHR (shift right).

Rotacion

La rotación es una operación similar al desplazamiento, pero en la que los bits que se desplazan se "rota" de modo que el bit más significativo se desplaza hacia el lugar vacío en el lado opuesto del registro. En la rotación hacia la izquierda, los bits se mueven hacia la izquierda y el bit más significativo se coloca en el lugar vacío en el lado derecho del registro. En la rotación hacia la derecha, los bits se mueven hacia la derecha y el bit más significativo se coloca en el lugar vacío en el lado izquierdo del registro. Las instrucciones utilizadas para la rotación son ROL (rotate left) y ROR (rotate right).

2.14 Obtención de una cadena con la representación hexadecimal

se refiere a la forma en que se pueden representar datos binarios en formato hexadecimal en un programa de lenguaje ensamblador.

La representación hexadecimal utiliza una base de 16 en lugar de una base de 2 como en la representación binaria. En la representación hexadecimal, cada dígito puede tomar uno de los 16 valores posibles, que van desde 0 a 9 y de A a F. Cada grupo de cuatro bits de un valor binario se representa con un solo dígito hexadecimal. Por ejemplo, el número binario 10101101 se puede representar como AD en formato hexadecimal.

Para obtener una cadena con la representación hexadecimal de un valor en un programa de lenguaje ensamblador, se pueden utilizar las instrucciones de desplazamiento y máscaras de bits para separar cada grupo de cuatro bits en el valor y luego convertirlo en su equivalente hexadecimal

2.15 Captura y almacenamiento de datos numéricos

se refiere al proceso de entrada de datos numéricos en un programa de lenguaje ensamblador y su almacenamiento en memoria o en registros

La Obtención de Captura y almacenamiento de datos numéricos en lenguaje ensamblador es un proceso que implica la captura de datos numéricos a través de instrucciones de entrada/salida, su almacenamiento en registros de propósito general o en memoria, y en algunos casos, la conversión de su representación en una base numérica diferente

En lenguaje ensamblador, la entrada de datos se realiza a través de las instrucciones de entrada/salida (I/O), que permiten la transferencia de datos entre el programa y dispositivos de entrada/salida, como teclados, pantallas y dispositivos de almacenamiento externo. Para capturar datos numéricos, se utilizan instrucciones de entrada/salida específicas, como INT 21h para la captura de entrada desde el teclado.

Una vez que se ha capturado el dato numérico, se almacena en un registro de propósito general o en una ubicación de memoria específica. El registro de propósito general más utilizado para el almacenamiento de datos numéricos es el registro AX (para valores de 16 bits) o el registro EAX (para valores de 32 bits).

2.16 Operaciones básicas sobre archivos de disco

Las operaciones básicas sobre archivos de disco en lenguaje ensamblador implican el uso de un conjunto de instrucciones específicas para acceder, leer y escribir datos en archivos de disco en sistemas operativos que utilizan el sistema de archivos FAT o NTFS

Alguna operaciones basicas son:

  1. Acceso a un archivo
  2. Lectura de datos de un archivo
  3. Escritura de datos en un archivo

En resumen, las operaciones básicas sobre archivos de disco en lenguaje ensamblador implican el uso de instrucciones específicas para acceder, leer y escribir datos en archivos de disco en sistemas operativos que utilizan el sistema de archivos FAT o NTFS. Se utilizan instrucciones como MOV, LEA y INT para mover