ATmega16 – Procedimientos

Un procedimiento es una secuencia de instrucciones que se quieren ejecutar en determinado momento para después seguir con el programa principal, por ejemplo si queremos convertir un número hexadecimal al BCD y este procedimiento se llevará a cabo en el programa principal tres veces, no vamos a tener que copiar el mismo código cada vez que necesitemos la conversión, lo que hacemos para ahorrar memoria y además hacer el programa más entendible es llamar a un procedimiento que realice esta operación.

Un concepto importante a tener en cuenta es la pila la cual es una parte de la memoria designada para poder realizar procedimientos. La característica importante de la pila es que el primer dato guardado es el último dato en ser sacado, por tal motivo es conveniente siempre colocar la pila al final de la RAM.

Un procedimiento funciona de la siguiente manera:

  • Primero: Guarda la dirección de retorno en la Pila
  • Segundo: Realiza el procedimiento.
  • Tercero: Retorna a ejecutar la siguiente instrucción en el programa principal.

Hay que recordar que todo procedimiento siempre termina con la instrucción RET (en assembler) ya que esta instrucción es la que nos da la dirección de retorno.

La pila también puede servir para ayudarnos a ahorrar registros de trabajo de la siguiente manera, una vez que ingresamos al procedimiento y antes de comenzar a ejecutarlo podemos guardar los datos del registro en la Pila y antes de salir recuperarlos.

Ejemplo:

// * Author: Victor Dueñas Guardia
// * Info: www.netzek.com
// @Ejemplo1:
// Contar cuantos bits tienen valor uno en los 100 primeros bytes de la memoria RAM.
// Pseudocódigo:
// – Coloco la Pila al final de la RAM.
// – Defino el número de bytes a contar.
// – Cargo el contenido de primer byte.
// – Llamo al procedimiento para que cuente los unos en este byte.
// – Retorno del procedimiento.
// – Sumo los unos contados.
// – Cargo el siguiente byte y continuo hasta que los "n" bytes sean leídos y sus unos hayan sido contados.
.INCLUDE "M16DEF.INC" //Incluye definición archivos ATmega16
LDI R16,HIGH(RAMEND) //Coloco la Pila al final de la RAM
OUT SPH,R16
LDI R16,LOW(RAMEND)
OUT SPL,R16
LDI R17,100 //Cargo en R17 el numero de bytes a contar
LDI ZH,$00
LDI ZL,$00 //Inicio el puntero Z en $0000
LDI R16,0 //Limpio el valor de R16
Otrobyte:
LD R16,Z+ //Cargo en R16 el contenido del Puntero Z
CALL CONTAR_UNOS //Inicio el procedimiento de contar unos
ADD YL,R16
LDI R16,0
ADC YH,R16 //Sumo el numero de unos al puntero Y
DEC R17 //Decremento el numero de bytes
BRNE Otrobyte
BUCLE: RJMP BUCLE //Bucle infinito
//……PROCEDIMIENTOS
CONTAR_UNOS:
PUSH R17 //Coloco en la Pila R17
LDI R17,8 //Numero de bits
LDI R18,0 //Numero de unos al inicio
Otrobit:
ROL R16 //Roto a la izquierda con acarreo
//Cada vez que haya un uno, se ira al acarreo
BRCC Escero //Salto si no existe acarreo
//Si no hay nada en el acarreo, es un cero
INC R18 //Incremento el numero de unos
Escero:
DEC R17 //Decremento el numero de bits
BRNE Otrobit //Salto si NO es cero
MOV R16,R18 //Muevo el valor contado a R16
POP R17 //Recupero el valor de R17 de la Pila
RET //Regreso del Procedimiento

Estamos reutilizando el registro R17 en el procedimiento ahorrando así un registro

Simulación:
Notemos que el resultado se guarda en el puntero Y (R28:R29)
La dirección está en el puntero Z (R30:R31)

Vemos que el número de unos contados son 34 ($22)
La última dirección en contar fue la 100 ($64)

Ejemplo:

// * Author: Victor Dueñas Guardia
// * Info: www.netzek.com
// @Ejemplo2:
// Colocar el valor $EE en los primeros 400 bytes de la memoria RAM.
// Pseudocódigo:
// – Cargo el valor que deseo colocar en los bytes correspondientes.
// – Inicio el procedimiento de "llenado" dos veces ya que cada "llenado" esta colocando el valor en 200 bytes.
.INCLUDE "M16DEF.INC" //Incluye definicion archivos ATmega16
LDI R16,HIGH(RAMEND)
OUT SPH,R16
LDI R16,LOW(RAMEND)
OUT SPL,R16 //Coloco la Pila al final de la RAM
LDI R20,$EE //Cargo el valor $EE en R20
LDI ZH,$00
LDI ZL,$60 //Ubico inicio de RAM en el puntero Z
CALL LLENADO
CALL LLENADO //Cada procedimiento solo puede llenar hasta 200 bytes
BUCLE: RJMP BUCLE //Bucle infinito
//……PROCEDIMIENTOS
LLENADO:
PUSH R21 //Coloco en la Pila R21
PUSH R16 //Coloco en la Pila R16
IN R16,SREG
PUSH R16 //Coloco en la Pila SREG
LDI R21,200 //Cargo el numero de bytes
Colocar:
ST Z+,R20 //Coloco $EE en la RAM y post_incremento Z
DEC R21 //Decremento el numero de bytes
BRNE Colocar
POP R16 //Recupero SREG de la Pila
OUT SREG,R16
POP R16 //Recupero R16 de la Pila
POP R21 //Recupero R21 de la Pila
RET //Retorno del procedimiento

Simulación:

Los 400 bytes fueron copiados exitosamente.

Ejemplo:

Para poder programar con mayor facilidad es recomendable etiquetar los registros ya que es más fácil recordar nuestras etiquetas a recordar números registros.

// * Author: Victor Dueñas Guardia
// * Info: www.netzek.com
// @Ejemplo2:
// Colocar el valor $EE en los primeros 400 bytes de la memoria RAM.
// Pseudocódigo:
// – Cargo el valor que deseo colocar en los bytes correspondientes.
// – Inicio el procedimiento de "llenado" dos veces ya que cada "llenado" esta colocando el valor en 200 bytes.
.INCLUDE "M16DEF.INC" //Incluye definicion archivos ATmega16
LDI R16,HIGH(RAMEND)
OUT SPH,R16
LDI R16,LOW(RAMEND)
OUT SPL,R16 //Coloco la Pila al final de la RAM
LDI R20,$EE //Cargo el valor $EE en R20
LDI ZH,$00
LDI ZL,$60 //Ubico inicio de RAM en el puntero Z
CALL LLENADO
CALL LLENADO //Cada procedimiento solo puede llenar hasta 200 bytes
BUCLE: RJMP BUCLE //Bucle infinito
//……PROCEDIMIENTOS
LLENADO:
PUSH R21 //Coloco en la Pila R21
PUSH R16 //Coloco en la Pila R16
IN R16,SREG
PUSH R16 //Coloco en la Pila SREG
LDI R21,200 //Cargo el numero de bytes
Colocar:
ST Z+,R20 //Coloco $EE en la RAM y post_incremento Z
DEC R21 //Decremento el numero de bytes
BRNE Colocar
POP R16 //Recupero SREG de la Pila
OUT SREG,R16
POP R16 //Recupero R16 de la Pila
POP R21 //Recupero R21 de la Pila
RET //Retorno del procedimiento

Simulación:

Como vemos la suma de una cifra nos dio el resultado en R18 (9+9=18)
La suma de dos cifras nos dio el resultado en R22:R21 (99+99=198)