CALL
Se digire a una dirección de la memoria de programa designado por el programador. En otras palabras, se utiliza para dirigirce a una rutina o tarea. Su principal ventaja es que una vez que finalizó la tarea, vuelve al punto siguiente desde dónde se llamo.
Ejemplo :
Para recordar,
CALL es llamada.
No afecta ningún bit del registro Status.
---------------------------------------------------------------------------------------------------------------------------------------------
Hacemos una exepción con respecto a ver las instrucciones por orden alfabético y veremos la instrucción GOTO.
---------------------------------------------------------------------------------------------------------------------------------------------
GOTO
Se digire a una dirección de la memoria de programa designado por el programador. En otras palabras, se utiliza para saltar instrucciones que no queremos que se ejecuten. A diferencia de la instrucción CALL, no hay forma de volver cuando se ejecuta la instrucción.
Ejemplo:
Para recordar
GO TO es ir a.
No afecta a ningún bit del registro Status.
Extendiendo la explicación.
Vamos a hablar del PC para entender bien sobre estas instrucciones. La excelente explicación que continua, por desgracia no es mía (ojalá fuera mi explicación), son de las personas Maunix y BrunoF (del foro
todopic)
En la siguiente imagen, vemos el diagrama de bloques del PC o CP.
El PC es de 13 bits en este caso(8kwords). 14 son los bits de cada "word" o instrucción que se graban en cada posición de la FLASH (memeria de programa).
El PC se reparte en: sus 8 bits de menor peso en el registro PCL, y los 5 restantes en el registro PCLATH.
Los pics al tener un set de instrucciones reducido no puede en una sola instrucción meter todos los bits necesarios para direccionar toda su memoria.
EL program counter son 2 registros, el PChigh y PCLow . Cuando haces un CALL o un GOTO, solo se rellenan 11 bits (los 8 del PClow y 3 del PChigh) y los restantes 2 los rellenas con el PCLATH (para completar los 13bits).
El STACK (pila) tiene toda la dirección, no solo parcial. Si haces un call desde la pagina 0 a la página 3 y luego un return el código
SI volverá a la página 0, pero el PCLATH sigue apuntando a la página 3, entonces si usas otro goto o call, debes tener en cuenta de modificar el PCLATH.
Entónces, dijimos que:
El PC = ProgramCounter o CP = Contador de Programa, tiene 13 bits; del 0 al 12.
Al ejecutar un call o un goto, se copian del 0 al 10, y los otros 2 bits se copian del registro PCLATH. El pclath solo estará allí para esa situacion.
En un return o retfie la microelectronica del PIC, pega la dirección del PC que estaba guardada.
Lo vemos con un ejemplo
1.
STACK = vacío
PC = 0x00A0
PCLATH = 0b0000
11000
Ejecutas un CALL 0x230
2. El STACK tiene en su posición 0 la dirección 0x00A0.
PC =
111000110000
3. Se ejecuta la subrutina y en ese punto el PC ya quedó en
PC =
111000110111
4. Viene un RETURN.
La microelectrónica del PIC copiará el stack tal cual en el program counter + 1
Valor STACK 0x00A0 + 1 --> PC = 0x00A1
5. EL código sigue ejecutandose en la página 0 pero hay que tener en cuenta que el PCLATH apunta a la página 3 por ello si harás otro CALL o GOTO, deberás cambiar de nuevo el PCLATH si la subrutina no está en la página 3.
Para cerrar el tema
Vamos a entrar a todo detalle en el Program Counter(PC) para que se vayan todas las dudas ya que es muy importante. Vayamos al tema tema del PC, computed goto(lo que algunos llaman "tabla"), call, returns y goto.
El Program Counter(PC) está conformado en esta familia de uC(y refiriendonos a la familia 16F, las otras poseen más o menos bits implementados) por 13 bits repartidos entre dos registros: PCH y PCL.
El PCL es legible/escribible directamente a traves del registro físico PCL(valga la redundancia). En cambio, el PCH no es directamente accesible. No puede ser leído, y sólo puede se grabado mediante un buffer que contiene el valor temporalmente(oh! aquí aparece nuestro famoso PCLATH). Entonces, recordar:
El PCLATH es sólo un buffer temporal que almacena los 5 bits de mayor peso del PC para ser escritos cuando se ejecute una instrucción que lo requiera.
Ahora, hay dos situaciones posibles en las que el PC debe ser cargado de manera distínta: una es cuando queremos trabajar con tablas y otra cuando realizamos un call o un goto que no esté en el mísmo banco.
1era situación: Tabla(Comuted Goto)
La tabla es una situación de uso del PC en la que se afecta directamente al registro PCL.
Cuando se afecte directamente al PCL mediante una instrucción, es necesario que el usuario asegure que PCLATH tenga sus 5 bits pre-cargados adecuadamente.
Hago un ejemplo:
Mal:
org 0x000
movlw 0x01
call tabla
org 0x300
tabla
addwf PCL,F
retlw 0x03
retlw 0x01
retlw 0x0F
.....
Bien:
org 0x000
movlw 0x03
movwf PCLATH
movlw 0x01
call tabla
org 0x300
tabla
addwf PCL,F
retlw 0x03
retlw 0x01
retlw 0x0F
.....
Mejor:
org 0x000
pageselw tabla
movlw 0x01
call tabla
org 0x300
tabla
addwf PCL,F
retlw 0x03
retlw 0x01
retlw 0x0F
.....
Pageselw es una instrucción del MPASM que genera dos instrucciones: un movlw literal y un movwf PCLATH. El valor del literal es automáticamente seleccionado por el ensamblador según la etiqueta(o posición de memoria) que se le especifique. En el caso anterior pageselw tabla generaría estas dos instrucciones:
movlw 0x03
movwf PCLATH
Si no aseguramos que los 5 bits del PCLATH estén correctamente seteados al momento de afectar al PCL mediante alguna instrucción(generalmente es la addwf, pero puede usarse subwf y muchas otras) entonces el programa saltará a una posición indeseada.
2da situación: CALL y GOTO
En esta familia de uC, cada instrucción es codificada en 14 bits. En el caso de las instrucciones CALL y GOTO, su estructura es la siguiente:
F2 F1 F0 K10 K9 K8 K7 K6 K5 K4 K3 K2 K1 K0
Donde las F indican cuál instrucción es la que debe ejecutarse(100 para la CALL 101 para la GOTO), y las k corresponden a la dirección a la cual queremos llamar(con un CALL) o saltar(con un GOTO).
Aquí se ve claramente un problema. Podemos ver que un CALL o un GOTO sólo almacena 11 bits de la dirección a la cual debe ir. 11 bits es un máximo 2048 posiciones. ¿Qué pasa cuando un uC posee más de 2k de memoria Flash entonces? Por ejemplo, un 16F877A posee 8k de memoria Flash. ¿Cómo haría para llamar a una subrutina que está más allá de la posición 2047 de la flash?
La solución nuevamente se encuentra en el PCLATH(y es nuevamente el usuario el que tiene el deber de pre-cargar el valor adecuado).
Entonces, dijimos que el PC contiene 13 bits de longitud. 13 bits son hasta 8kwords(una word es en esta familia un conjunto de 14 bits que conforman una instrucción la cual se aloja en la memoria FLASH del uC). Un CALL o un GOTO sólo contienen los 11 bits de menor peso de la dirección a la cual ir, por lo que los 2 bits restantes deberán ser pre-cargados en los bits 4 y 3 del registro PCLATH por el usuario programador.
Cuando se ejecuta una instrucción CALL o GOTO, es imprescindible que el registro PCLATH esté correctamente precargado. La instrucción a la que el uC irá estará conformada por 13 bits y ellos serán:
PCLATH,4 PCLATH,3 K10 K9 K8 K7 K6 K5 K4 K3 K2 K1 K0
Cabe mencionar que el uC carga a PC<10:0> con el valor pasado por los 11 bits de K, y a PC<12:11> con el valor de los bits PCLATH<4:3>. El registro PCLATH no es modificado de ninguna manera. Sólo se leen esos dos bits.
Por ejemplo, en un uC de 8kWords hay 4 páginas. Una página cada 2048 words. Si se está en una página y se quiere ir a otro es necesario precargar antes dichos bits del PCLATH para poder hacerlo.
El usuario no debe preocuparse por precargar el PCLATH en dos sitaciones:
Si el uC no posee más de 2kWords de memoria Flash;
O si en el código creado por el usuario, no se utiliza la memoria FLASH más allá de la posición 2047(0x7FF).
Si ocurre al menos uno de esos dos casos, es suficiente con asegurar que los bits PCLATH<4:3> se encuentren ambos en cero.
Vamos con un par de ejemplos:
Mal:
org 0x000 ;Esto es página0
call cruzo
org 0x800 ;Esto ya es el página1
cruzo
retlw 0xFF
.....
Bien:
org 0x000 ;Esto es página0
movlw 0x08
movwf PCLATH
call cruzo
org 0x800 ;Esto ya es el página1
cruzo
retlw 0xFF
.....
Mejor:
org 0x000 ;Esto es página0
pagesel cruzo ;automaticamente seleccionar banco
call cruzo
org 0x800 ;Esto ya es el página1
cruzo
retlw 0xFF
.....
Pagesel es una instrucción del MPASM que genera dos instrucciones: un bcf/bsf PCLATH,3 y un bcf/bsf PCLATH,4. El software ensamblador selecciona automáticamente la instrucción bcf o bsf según el banco en el cual se encuentra la etiqueta(o posición de memoria) que se le especifique. En el caso anterior pagesel cruzo generaría estas dos instrucciones:
bsf PCLATH,3
bcf PCLATH,4
Ya que la subrutina cruzo se encuentra en la página1.
Finalmente, cuando se ejecuta una instrucción CALL, se carga en el STACK el valor de la posición actual más 1(es decir, se guarda en el STACK el valor PC+1). Se guardan los 13 bits, por lo que durante las instrucciones RETURN,RETLW y RETFIE no es necesario precargar al PCLATH.
Para más información, ver el esquema sección 2.3 del datasheet de los PIC16F87XA que habla de cómo cargar al PC según cada situación.