Si. Es una comparacion bastante bien aproximada...
Y a lo segundo también. Cada vez que llames a una subrutina que no esté en la misma página que desde donde la estás llamando, necesitás configurar la página antes de llamarla, y volver a configurar la página actual después de haberlo hecho.
No necesitás hacer ninguna macro. Era solo para que no tengas que hacer la tediosa tarea de agregar las macros antes y después de cada llamada a subrutinas de otras páginas.
Te comento el motivo del por qué es necesario el pagesel $ después de cada llamada a subrutinas de otras páginas.(He hablado sobre este tema en el foro extensamente, aunque no recuerdo bien en qué tema...tal vez si haces una busqueda en el foro logres encontrar algo).
La MACRO pagesel setea los bits necesarios del registro PCLATH. Estos bits se utilizan a la hora de ejecutar una instrucción call. Cuando por ejemplo haces:
pagesel subrutina_pag1
call subrutina_pag1
la primer linea(pagesel) setea los bits para la pagina donde se encuentre la subrutina "subrutina_pag1"(que supuestamente esta en la pagina 1). Luego se realiza el call correctamente. Al volver de la subrutina, los bits del PCLATH siguen cargados con la página 1, entonces acá surgen los problemas si intentás llamara normalmente a otra subrutina,ya que deberías volver a la página actual antes de hacerlo.
Por ello, es necesario la instrucción pagesel $, que hará que los bits del PCLATH vuelvan a configurarse para la página actual.
Vamos con un ejemplo más real:
org 0x000
clrf PCLATH
pagesel pag1
call pag1
call pag0
goto $
pag0 nop
nop
retlw 0x00
org 0x800 ;vector de pagina 1
pag1 nop
retlw 0xAA
Este codigo no se va a comportar bien...Veamos en profundidad el por qué.
Primero vamos a anotar dónde se guarda cada línea en la memoria FLASH:
LINEA: | DIRECCION FLASH |
org 0x000 | - |
clrf PCLATH | 0x000 |
pagesel pag1 | 0x001 0x002 |
call pag1 | 0x003 |
call pag0 | 0x004 |
goto $ | 0x005 |
pag0 nop | 0x006 |
retlw 0x00 | 0x007 |
org 0x800 | - |
pag1 nop | 0x800 |
retlw 0xAA | 0x801 |
Ahora vamos a empezar a ensamblar de a poquito las líneas tal cual lo haría el MPASM( te recuerdo que todo esto lo estoy haciendo solo teoricamente, por lo que no está probado y puede que me equivoque en algunos detalles)
LINEA: | DIRECCION FLASH | Código pseudo-ensamblado: |
org 0x000 | - | - |
clrf PCLATH | 0x000 | clrf 0x0A |
pagesel pag1 | 0x001 0x002 | pagesel 0x800 |
call pag1 | 0x003 | call 0x00 |
call pag0 | 0x004 | call 0x06 |
goto $ | 0x005 | goto 0x05 |
pag0 nop | 0x006 | nop |
retlw 0x00 | 0x007 | retlw 0x00 |
org 0x800 | - | - |
pag1 nop | 0x800 | nop |
retlw 0xAA | 0x801 | retlw 0xAA |
Ahora otra pasada mas, un poco más profunda:
LINEA: | DIRECCION FLASH | Código pseudo-ensamblado: |
org 0x000 | - | - |
clrf PCLATH | 0x000 | clrf 0x0A |
pagesel pag1 | 0x001 0x002 | bsf PCLATH,3 bcf PCLATH,4 |
call pag1 | 0x003 | call 0x00 |
call pag0 | 0x004 | call 0x06 |
goto $ | 0x005 | goto 0x05 |
pag0 nop | 0x006 | nop |
retlw 0x00 | 0x007 | retlw 0x00 |
org 0x800 | - | - |
pag1 nop | 0x800 | nop |
retlw 0xAA | 0x801 | retlw 0xAA |
Un poco más...
LINEA: | DIRECCION FLASH | Código pseudo-ensamblado: |
org 0x000 | - | - |
clrf PCLATH | 0x000 | clrf 0x0A |
pagesel pag1 | 0x001 0x002 | bsf 0x0A,3 bcf 0x0A,4 |
call pag1 | 0x003 | call 0x00 |
call pag0 | 0x004 | call 0x06 |
goto $ | 0x005 | goto 0x05 |
pag0 nop | 0x006 | nop |
retlw 0x00 | 0x007 | retlw 0x00 |
org 0x800 | - | - |
pag1 nop | 0x800 | nop |
retlw 0xAA | 0x801 | retlw 0xAA |
Ahora creo que vamos a poder hablar con un poco más de prioridad.
La instrucción call(en los uC de 14bits de largo de palabra) sólo puede direccion
11 bits. Esto significa que puede llamar sólo dentro del rango(decimal): [0;2047].
En los uC que tienen más de 2KWords esto(11 bits) no alcanzaría. Y aquí cobran importancia los bits 3 y 4 el registro PCLATH. Ellos son los 2 bits adicionales que necesitamos para poder direccionar los 13 bits completos que necesitamos(es decir, direccionar un rango de 8KWords).
Como se puede apreciar más arriba, la instrucción call pag1 se transforma en un call 0x00...Esto parecería que la llamada está mal ya que nosotros queremos llamar a la subrutina pag1(ubicada en la posición 0x800). Pero como expliqué previamente, al ejecutarse una instrucción call, el uC también utiliza los bits 3 y 4 del PCLATH para completar los 13 bits que necesita. Como hemos sido inteligentes, los hemos precargado con el valor binario: 01
Ahora, armando lo que tenemos entre los bits PCLATH,4 PCLATH,3 y la dirección a la que llama la instrucción call tendríamos:
PCLATH,4 PCLATH,3 call 0x00
En binario: 0 1 000 00000000
Todos juntos: 01000000000000
lo que en hexadecimal resulta: 0x800
¡Qué barbaro,¿no?! Resulta que hemos llamado realmente a la posición 0x800.
Ahora una vez realizada la llamada,el nop y el retlw, surge el problema...cuando queremos llamar a la subrutina "pag0". ¿Por que? Porque el PCLATH[4,3] continúa cargado con el valor binario 01.
Entonces, en lugar de llamar a la posición 0x006(que es donde se encuentra la subrutina pag0) estaríamos haciendo:
PCLATH,4 PCLATH,3 call 0x06
En binario: 0 1 000 00000110
Todos juntos: 0100000000110
lo que en hexadecimal resulta: 0x806
Por lo que estaríamos yendonos a una posición completamente alejada de la que queríamos. Es por ello, que debemos utilizar la instrucción pagesel $, que acomodará nuevamente los bits 4 y 3 del PCLATH para asegurar que en futuras llamadas no nos suceda esto.
Espero que haya quedado claro.
Un saludo.