Se débarrasser de la gestion des pages de code.Bonjour à tous,
Je vous propose, ci-dessous, une technique pour se débarrasser du problème de gestion des pages de code.
Je suis conscient du fait que le problème ne se pose que pour les membres développant de gros programmes mais la technique peut intéresser d'autres membres même ceux qui comme moi n'ont jamais été gênés par la taille mémoire d'un petit 12F675.
L'intérêt majeur de la mise en oeuvre de cette technique est que le déplacement du sous-programme d'une page à une autre ne pose plus aucun soucis, il suffit de préciser l'adresse d'implantation du sous-programme à l'assembleur, inutile de rechercher les appels qui lui sont fait et de modifier le changement éventuel pour chaque appel.
Pour les anciens, une idée analogue avait été mise en oeuvre sur des MO5 petites machines (à base de 6805) développées pour les collèges début des années 1980, Elles disposaient d'une ROM de 4*16k mots et il avait été développé un langage pour l'enseignement le LSE. On imagine que ce soft (d'un niveau supérieur au Basic de l'époque, en particulier pour ses procédures nommées, mais arrêtons là, je vais me faire arracher les yeux, il y avait une vraie bagare entre les tenants de l'un et de l'autre des langages) qui comprenait un éditeur de texte, un compilateur, un décompilateur, un interpréteur était à l'étroit dans ses 4*16k et que ses auteurs devaient bien gérer les pages de code.
Le principe est simple, on écrit en fond de mémoire (mais ce pourrait être n'importe où ailleurs) de chaque page un relais qui gère le changement de page (il faut bien qu'il soit fait quelque part), appel le sous-programme et remet PCLATH en l'état.
J'ai fait deux versions
Dans la première, tout est écrit dans le même .ASM, les commentaires devraient suffire à comprendre.
Dans la deuxième, j'ai isolé la partie utile à la technique dans une macro qui en facilite le réemploi, voir dans la caisse à outils.
Quelques explications suivent les listings de ces programmes.
Première version
Code : Tout sélectionner
;**********************************************************************
; Programme de test de la gestion de relais *
; d'appel de sous-programme
; *
;**********************************************************************
; *
; NOM: Test relais *
; Date: 23/06/2018 début de projet *
; Version: 1 *
; Circuit: Tout Mid-range au moins *
; Auteur JJE
; *
;**********************************************************************
; *
; Fichier requis: *
; *
; *
; *
;**********************************************************************
; *
; Notes: Ce petit programme permet de tester les appels de
; sous-programmes par relais indépendant de la page de code d'où
; part l'appel
; sous le simulateur de MPLAB lancez en pas à pas et suivez
; l'avancement.
; Il peut être utile d'ouvrir une fenêtre Watch sur PCLATH
; *
; *
;**********************************************************************
; LIST p=16F876 ; Définition de processeur
; #include <p16F876.inc> ; Définitions des constantes
;
; __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON ; & _HS_OSC
;
;*********************************************************************
; ASSIGNATIONS *
;*********************************************************************
PCLATH EQU 0xA
;*********************************************************************
; DEFINE *
;*********************************************************************
; dernière adresse de mémoire programme
;#DEFINE __MAXFLASH 0x03FF ; pour un 12F675
;#DEFINE __MAXFLASH 0x0FFF ; pour un 16F87/88 ou 16F873/874
#DEFINE __MAXFLASH 0x1FFF ; pour un 16F876/877
; où installer le code du sous-programme
#DEFINE SSP_Code 0x100
; adresse de fin de la mémoire programme en page 0 plus 1
variable RelaisSSP = (__MAXFLASH & 0x7FF) +1
if (__MAXFLASH >= 0x1800)
TailleRelais EQU 6
else
if (__MAXFLASH >= 0x800)
TailleRelais EQU 4
else
TailleRelais EQU 2
endif
endif
;*********************************************************************
; DECLARATIONS DE VARIABLES *
;*********************************************************************
CBLOCK 0x020 ; début de la zone variables
ENDC ; Fin de la zone
;**********************************************************************
; DEMARRAGE SUR RESET *
;**********************************************************************
org 0x000 ; Adresse de départ après reset
goto init ; Adresse 0: initialiser
;*********************************************************************
; INITIALISATIONS *
;*********************************************************************
errorlevel -306
init
; appelle SSP depuis la page 0
call SSP
if (__MAXFLASH >= 0x800)
pagesel init2
call init2
endif
if (__MAXFLASH >= 0x1000)
pagesel init3
call init3
endif
if (__MAXFLASH >= 0x1800)
pagesel init4
call init4
endif
pagesel init
goto init
if (__MAXFLASH >= 0x800)
org 0x800
init2
; appelle SSP depuis la page 1
call SSP
return
endif
if (__MAXFLASH >= 0x1000)
org 0x1000
init3
; appelle SSP depuis la page 2
call SSP
return
endif
if (__MAXFLASH >= 0x1800)
org 0x1800
init4
; appelle SSP depuis la page 3
call SSP
return
endif
org SSP_Code
nop
nop
nop
return
RelaisSSP -= TailleRelais
SSP EQU RelaisSSP
ORG RelaisSSP
; si on arrive ici, sauf sur erreur de programme,
; on est sûr que PCLATH<4, 3> = b'00'
if (SSP_Code & 0x800) == 0x800
; SSP_Code est en page 0 ou 2
; il faut armer PCLATH,3
bsf PCLATH, 3
endif
if (SSP_Code & 0x1000) == 0x1000
; SSP_Code est en page 1 ou 3
; il faut armer PCLATH,4
bsf PCLATH, 4
endif
call SSP_Code
if (SSP_Code & 0x800) == 0x800
bcf PCLATH, 3
endif
if (SSP_Code & 0x1000) == 0x1000
bcf PCLATH, 4
endif
return
if (__MAXFLASH >= 0x800)
ORG RelaisSSP + 0x800
; si on arrive ici, sauf sur erreur de programme,
; on est sûr que PCLATH<4, 3> = b'01'
if (SSP_Code & 0x800) != 0x800
; SSP_Code n'est pas en page 0 ou 2
; il faut armer PCLATH,3
bcf PCLATH, 3
endif
if (SSP_Code & 0x1000) == 0x1000
; SSP_Code est en page 1 ou 3
; il faut armer PCLATH,4
bsf PCLATH, 4
endif
call SSP_Code
if (SSP_Code & 0x800) != 0x800
bsf PCLATH, 3
endif
if (SSP_Code & 0x1000) == 0x1000
bcf PCLATH, 4
endif
return
endif
if (__MAXFLASH >= 0x1000)
ORG RelaisSSP + 0x1000
; si on arrive ici, sauf sur erreur de programme,
; on est sûr que PCLATH<4, 3> = b'10'
if (SSP_Code & 0x800) == 0x800
; SSP_Code est en page 0 ou 2
; il faut armer PCLATH,3
bsf PCLATH, 3
endif
if (SSP_Code & 0x1000) != 0x1000
; SSP_Code n'est pas en page 1 ou 3
; il faut armer PCLATH,4
bcf PCLATH, 4
endif
call SSP_Code
if (SSP_Code & 0x800) == 0x800
bcf PCLATH, 3
endif
if (SSP_Code & 0x1000) != 0x1000
bsf PCLATH, 4
endif
return
endif
if (__MAXFLASH >= 0x1000)
ORG RelaisSSP + 0x1800
; si on arrive ici, sauf sur erreur de programme,
; on est sûr que PCLATH<4, 3> = b'11'
if (SSP_Code & 0x800) != 0x800
; SSP_Code n'est pas en page 0 ou 2
; il faut désarmer PCLATH,3
bcf PCLATH, 3
endif
if (SSP_Code & 0x1000) != 0x1000
; SSP_Code n'est pas en page 1 ou 3
; il faut désarmer PCLATH,4
bcf PCLATH, 4
endif
call SSP_Code
if (SSP_Code & 0x800) != 0x800
bsf PCLATH, 3
endif :-)
if (SSP_Code & 0x1000) != 0x1000
bsf PCLATH, 4
endif
return
endif
end
Quelques commentaires
Code : Tout sélectionner
; LIST p=16F876 ; Définition de processeur
; #include <p16F876.inc> ; Définitions des constantes
.../...
PCLATH EQU 0xA
comme ce programme n'est pas destiné à être installé dans un pic, point n'est besoin de LIST ni d'include,
par contre, on va travailler avec PCLATH et il faut bien dire à l'assembleur que c'est l'octet d'adresse 0x0A
Code : Tout sélectionner
; dernière adresse de mémoire programme
;#DEFINE __MAXFLASH 0x03FF ; pour un 12F675
;#DEFINE __MAXFLASH 0x0FFF ; pour un 16F87/88 ou 16F873/874
#DEFINE __MAXFLASH 0x1FFF ; pour un 16F876/877
Il y a bien une constante __MAXRAM qui donne l'adresse la plus grande adressable en mémoire de donnée définie dans chaque fichier p*.inc toujours inclus en début d'un code source. Mais je n'ai pas trouvé d'équivalent pour la mémoire programme, j'ai donc laissé à l'utilisateur le soin de donner cette information précieuse en particulier car on peut en déduire le nombre de pages de code, bits 12 et 11 de cette constante
; où installer le code du sous-programme
#DEFINE SSP_Code 0x100 C'est par de DEFINE que l'utilisateur précisera à l'assembleur l'adresse où il veut installer son sous-programme
En fait, ce sont les deux seules lignes que l'utilisateur aura à renseigner
Code : Tout sélectionner
; adresse de fin de la mémoire programme en page 0 plus 1
variable RelaisSSP = (__MAXFLASH & 0x7FF) +1
Comme j'ai décidé d'installer mes relais en fond de mémoire, je calcule ici, l'adresse de fond de mémoire de la page 0
Code : Tout sélectionner
; adresse de fin de la mémoire programme en page 0 plus 1
variable RelaisSSP = (__MAXFLASH & 0x7FF) +1
if (__MAXFLASH >= 0x1800)
TailleRelais EQU 6
else
if (__MAXFLASH >= 0x800)
TailleRelais EQU 4
else
TailleRelais EQU 2
endif
endif
La taille des relais dépend bien sûr du nombre de page cette séquence la calcule
Code : Tout sélectionner
init
; appelle SSP depuis la page 0
call SSP
if (__MAXFLASH >= 0x800)
pagesel init2
call init2
endif
if (__MAXFLASH >= 0x1000)
pagesel init3
call init3
endif
if (__MAXFLASH >= 0x1800)
pagesel init4
call init4
endif
pagesel init
goto init
C'est une simple boucle qui appelle le sous-programme SSP pour chaque page. bien sûr il dépend du nombre de pages.et les appels aux init* servent à se placer dans la bonne page.
Là, c'est le sous-programme à charge de l'utilisateur. Remarquez qu'il est installé là il a été spécifié en début de programme. Remarquez aussi qu'il est anonyme. En fait l'utilisateur n’appellera jamais, bien qu'il puisse le faire, l'adresse SSP_Code, ce sont les relais qui le feront.
Il faut bien qu'on sache où on va écrire les relais. Comme, au plus, ils demandent TailleRelais octets de mémoire programme, et qu'on a décidé de les mettre avant la fin de mémoire, on calcule l'adresse d'installation comme dit ci-dessus
et on s'en souvient dans la constante SSP, c'est cette adresse que l'utilisateur appellera quand il voudra appeler son sous-programme. à lui de lui donner un nom plus évocateur.
La fin du code est l'installation des relais eux-même. On voit bien dans la directive ORG qu'on se place au bon endroit dans chaque page, on voit bien aussi qu'on ne touche qu'aux bits de PCLATH utiles (contrairement à la macro pagesel fournie avec MPLAB et qu'on les remets en l'état où ils étaient en entrant . et on voit bien aussi l'appel du sous-programme.