Assembler
Recordamos, siempre que trabajamos a bajo nivel es importante entender que todo lo que se almacena esta convertido a un sistema numerico, a cual sistema numerico, depende de la arquitectura, o como le decimos que almacene a nivel de registro de memoria.
Tenemos diferentes mecanismos de conversión entre sistemas,podemos utilizar ciertas operaciones, como sumas o restas para obtener algunos caracteres.
Dependiendo de lo que quiera realizar, podemos utilizar que siempre hay n cantidad de caracteres entre un simbolo y otro, entones sumando o restando podemos obtener el caracter deseado, Ejemplo, de "a" hasta mayusculas debemos sumar 22.
¿Como podemos limitar que solo obtengamos números?
Estableciendo rangos validos según lo que se necesita, por ejemplo de 30 a 39 para solo recibir numeros.
Directivas
Se ejecutan antes del programa
analizaremos las partes del código, incluyendo Directivas y luego la subrutina principal.
.model small
.stack 100h ;tamaño de pila que va a tener
CR equ 13d ; se declara una constante
LF equ 10d
.data
msg1 db 'Enter an uppercase letter: $' ; Cuando utilizamos, offset, el busca hasta obtener el simbolo de dolar,porque offset le decimos donde empieza la variable.
.code
;main
;sección donde comienza el programa
start: ;subrutina principal (porque es la única con end start)
mov ax,@data ;aqui obtenemos todo lo que esta guardado en la sección de data
;aqui va mas código
end start
Registros e interacciones con el usuario
- "al" es el registro donde nuestra subprograma (1h) que nos permite obtener datos guarda lo ingresado por el usuario.
- "dl" es el registro desde donde obtiene nuestro subprograma (2h) para mostrar un caracter.
- int 21h Es una interrupción, para luego ejecutar un subprograma.
Los Programas
Son programas, previamente escritos los cuales utilizamos para realizar ciertas operaciones dentro de nuestro programa assembler, como 1h, que nos permite obtener un dato ingresado por memoria, todos estos programas se ejecutan siempre dentro del procesador.
Subrutinas y pila.
Podemos tener muchas subrutinas que se declaran dentro o fuera de la rutina principal, estas poseen un IP, Instruction Pointer, tiene la siguiente instrucción o subrutina a ser ejecutada, las subrutinas pueden tener incluidas un ret el cual les permite volver al punto donde fueron llamadas luego de realizar sus operaciones.
La pila o stack, tiene un valor llamada SP, Stack Pointer, contiene el último dato ingresado a la pila, este es gestionado por las instrucciones, call (push) agrega un elemento a la stack , ret (pop) elimina el último elemento de la pila.
putC:
mov ah,2h ;muestra por pantalla el caracter guardado en dl
int 21h ;una interrupción
ret ; volvemos al punto donde fue llamada
Debemos tener mucho cuidado utilizando la pila para almacenar valores ya que puede resultar en comportamientos inesperados junto con las subrutinas.
Este ejemplo fue ideado con fines de demostrar posibles problemas que pueden ser creados por el mal uso de la pila, no refleja necesariamente el comportamiento real.
comparar:
mov ax,2
push ax
ret
call comparar ;linea 1
ret ;linea 2
En este caso estamos haciendo un push del valor 2, pero luego al hacer ret estamos eliminandolo de la pila, por ende se perdera nuestro valor que queriamos guardar.
Para ret ese ultimo elemento que esta en SP, que seria 2, deberia ser una dirección siempre, en este caso el ret querra volver a la linea 2, donde ese ret tendra como valor de dirección en la pila el 1, haciendo otro pop, volviendo al call y asi iniciando un bucle.
Por cuestiones de practicidad el concepto de Pila no estara explicado en este articulo, se espera que el lector ya conozca este concepto de semestres anteriores.
Corrección del código
comparar:
mov ax,2
push ax
pop ax
ret
call comparar ;linea 1
Guardando Registros
Existe la desventaja, en caso de utilizar subprogramas que podrian modificar los registros y asi modificando lo que teniamos almacenado originalmente en el registro, por ende se recomienda mover de registros, copiando el dato obtenido, para asi en la siguiente utilización de suprogramas no correr riesgos.
No importa que hace el subprograma, lo que importa es con que registro lo hacen, pero debo encargarme de evitar perdidades datos ya que los subprogramas comparten registros.
Nuevas subrutinas.
Nos encargamos de no perder datos luego de realizar la subrutina.
El orden es importante, por la forma en que se almacenan los datos dentro de la pila, lo que se ingresa primero va quedando al final.
puts:
;display a string terminated by S
;dx contains address of string
push ax ;save ax
push bx
push cx
push dx
mov dx,ax
mov ah,9h ;call ms-dos to output string
int 21h
pop dx
pop cx
pop bx
pop ax ;restore ax
ret
Control de flujo: Instrucciones Jump.
Instrucciones de jump incondicionales:
Estan definidos por la instrucción jump, solo necesita saber a que parte del programa va a "saltar", debo decirle a la subrutina a la cual quiero saltar.
Es muy diferente llamar a una subrutina o saltar a una subrutina, cuando hacemos un call, el ingresa datos a la pila luego al terminar y el ret eliminan datos de la pila, ademas de continuar con la ejecución secuencial del programa.
El jumo no ingresa ni extrae datos de la pila, el llega ejecuta, encuentra un ret y no va a funcionar, porque el no agrego nada a la pila.
Instrucciones de jump condicionales:
Evaluan el estado de los flags antes de saltar. Existen muchos tipos, depende de lo que nos gustaria evaluar, en la materia nos enfocamos en los de "Signo" y "Cero".
Los posibles valores de una flag es, 1 o 0, encendido o apagado.
Cada jump esta asociado a un flag.
Es lo mas parecido que tenes a una estructura if-else-then
tenemos el operador encargado de comparar dos direcciones de memoria y según esa operación setea los flags
cmp ax,bx
je equals ; jump if ax==bx
Se realizan todas las operaciones para cada flag de una vez, luego realizamos el jump condicional en donde preguntamos si son iguales las cosas que habiamos comparado.
No es obligatorio que despues de cada cmp utilicemos la llamada de un jump condicional pero si es recomendable para evitar problemas con flags que sean modificadas.
Lista las instrucciones con condiciones. Estas tienen nombres alternativos,además.
Nombre | Jump si | Flag testeado |
---|---|---|
je/jz | equal/zero | zf=1 |
jne/jnz | not equal/ not zero | zf=0 |
Operando con números sin signo
Nombre | Jump si | Flag testeado |
---|---|---|
ja/jnbe | above/not below or equal | (cf or zf) = 0 |
jae/jnb | above or equal/not below | cf = 0 |
jb/jnae/je | below/not above or equal/carry | cf = 1 |
jbe/jna | below or equal/not above | (cf or zf) = 1 |
Instrucción loop
Verifica el estado de cx, solo se utiliza con ese registro, si es diferente a 0, llama a la subrutina que necesitamos, controla el estado,a la vez decrementa y por ultimo actualiza.
mov al, '*'
mov cx,60d ;loop count
disp_char:
call putc
loop disp_char ;display "*"
jump disp_char ; cx = cx -1, if (cx != 0)