Le Subroutines nei Baseline
In base al codice a 12 bit utilizzato per le istruzioni e alle limitazioni
della famiglia, le subroutine nei Baseline risentono di un paio di
limitazione:
- come per i Midrange, la memoria programma può essere divisa in pagine
nel momento in cui supera 512 words (una pagina = 512)
- l' istruzione call
agisce solamente nelle prime 256 words di ogni pagina.
Ci si potrebbe chiedere, allora, che strategia utilizzare per decidere dove va scritta la subroutine nel sorgente.
Però per i Baseline, oltre a quelle indicate c'è ancora una difficoltà
supplementare:
- lo stack dei Baseline è minimo, solo due livelli, ovvero si può
chiamate una subroutine (la cui call
copia l' indirizzo di ritorno nello
stack), la quale piò a sua volta chiamare un' altra subruotine. Però a
questo punto lo stack è pieno e non è possibile una ulteriore call
a
meno di essere rientrati da una precedente.
Risulta quindi evidente che la possibilità di chiamare subroutines nei
Baseline è fortemente limitata. Certamente occorre cura particolare nell'
evitare l' annidamento delle
subroutine in modo da non superare mai i due livelli: va
tenuto presente che i Baseline non hanno sistemi di protezione per overflow o
underflow dello stack e un errore nella sua gestione provoca il crash del
programma.
Così, un ampio uso di subroutines è possibile anche in questi piccoli PIC.
Per quello che riguarda il problema delle pagine, ne sono esclusi solamente
i 18F e i PIC superiori, dove la memoria programma non è paginata. Per
Baseline e Midrange occorre forzatamente gestire le pagine ogni volta che il
programma supera l' ampiezza di 512 words.
Quindi, si ricorrerà al solito pagesel
in abbondanza.
La limitazione della prima metà della pagina si genera dal fatto che il
bit del PC è, nella call,
sempre a 0. Quindi è obbligatorio che l' indirizzo di destinazione dell'
istruzione sia entro i primi 256 indirizzi, in qualunque pagina ci si
trovi. Questa è una delle limitazioni sensibili per questi piccoli PIC, che, d'altro
canto, sono volutamente molto semplificati e quindi privi dei meccanismi e
delle prestazioni delle famiglie superiori.
Se il programma non supera in alcun modo il limite di 256 word, il problema
non si pone, ma questo, però, è un caso limitato ad un programma di poche righe. E'
ben possibile che si abbia a che fare con programmi più corposi, che sforano
il limite.
In tal caso sono implementabili due strategie:
- posizionare sempre le subroutines entro i primi 256 indirizzi della pagina
- utilizzare una salto per raggiungere la subroutine ovunque si trovi
Facciamo un esempio pratico: poniamo la subroutine nei
primi elementi del programma:
Vettore_Reset
goto
mainloop
;==================================================================
;=
SUBROUTINES =
===================================================================
subroutine:
......
retlw
0
===================================================================
mainloop:
......
call
subroutine
|
Osserviamo la necessità di inserire un salto (goto
mainloop) che permetta di oltrepassare
le istruzioni della subroutine. Se questa linea mancasse, il program counter,
dopo il salto al vettore del reset, proseguirebbe
eseguendo le istruzioni della subroutine, fino al retlw
dove sarebbe sostituito da un valore casuale preso dallo
stack, che non è stato caricato da alcuna call,
mandando in crash il programma.
Questa soluzione è semplice, ma lo spazio di 256 word non è poi tanto
grande e se utilizziamo varie subroutines si può riempire facilmente, anche
perchè è l' area richiesta per piazzarci altre cose, come ad esempio tabelle
(lookup tables). Possiamo comunque piazzare altre subroutines nei primi 256
indirizzi delle pagine seguenti, avendo cura di utilizzare il comando per il
cambio pagina:
pagesel
subroutine
call
subroutine
|
Non va dimenticato che gli switches di pagina restano inalterati fini alla
prossima ri scrittura e quindi, se occorre agire in una altra pagina, si
richiede ancora un pagesel.
La seconda opzione è quella di indirizzare alla subroutines con un sistema
indiretto.
Vettore_Reset
goto
mainloop
;==================================================================
;=
SUBROUTINES =
===================================================================
sub1 goto
rsub1
sub2 goto
rsub2
......
===================================================================
mainloop:
......
call
sub1
......
===================================================================
; qualsiasi posizione
rsub1:
......
retlw
0
rsub2:
......
retlw
0
|
Il meccanismo è semplice:
- call
sub1
invia non alla
subroutine, che è stata rinominata rsub1,
ma alla label sub1
che corrisponde ad un goto
rsub1
- la call
ha salvato l' indirizzo di ritorno nello stack; questo indirizzo
corrisponde all' istruzione successiva alla call
stessa
- il goto non comporta
alcuna modifica del contenuto dello stack, ma cambia solo il pc
indirizzandolo alla label rsub1
- una volta esaurite le istruzioni a rsub1,
il retlw
finale recupera dallo stack l' indirizzo di ritorno successivo alla call
Siccome il goto non ha
la limitazione dei 256 indirizzi, la subroutine si può trovare anche oltre
questo limite.
Questa soluzione consente di non affollare i primi 256 indirizzi. Il goto
mainloop è sempre necessario per
passare oltre le chiamate indirette alle subroutines.
Si può obiettare che il rimando indiretto con un salto richiede l'esecuzione
di una istruzione in più, ma, a meno di situazioni del tutto eccezionali,
questo non comporta che 1us in più nell' esecuzione a 4MHz di clock: del
tutto trascurabile.
Se le subroutines finiscono in pagine diverse, di nuovo, con pagesel
effettueremo il cambio pagina necessario.
Vettore_Reset
goto
mainloop
;==================================================================
;=
SUBROUTINES =
===================================================================
sub1 pagesel
rsub1
goto
rsub1
sub2 pagesel
rsub2
goto
rsub2
......
===================================================================
mainloop:
......
call
sub1
local pagesel
local
......
===================================================================
; qualsiasi posizione
rsub1:
......
retlw
0
rsub2:
......
retlw
0
|
Da notare che:
- non serve uno switch di pagina nel rientro dalla subroutine,
in quanto lo stack contiene tutti i bit dell' indirizzo di ritorno, mentre
- serve riportare la pagina a quella dove risiedono le istruzioni in corso di
esecuzione.
Sfortunatamente la paginazione della memoria programma costituisce il
"peccato originale" dei PIC e, a meno di utilizzare 18F o superiori,
occorre conviverci.
L' alternativa è l' uso di linguaggi superiori all' Assembly dove il problema
viene gestito dal compilatore.
|