MPASM per pagine e banchi
Va notato che MPASM dispone di alcune direttive per la gestione delle pagine
e dei banchi. Esse sono:
che vengono solitamente assemblate come un set di istruzioni BCF/BSF. |
E' comune per MPASM realizzare questi comandi, che interessano due bit, con
una coppia di BCF/BSF , da cui deriva
che, ad esempio, il codice generato dalla linea
PAGESEL Label
va ad occupare due locazioni di memoria programma:
PAGESEL
è sostanzialmente una macro. Questo è un
comportamento ragionevole in quanto MPASM non comprende meccanismi sofisticati
in grado di determinare se va cambiato un solo bit o entrambi ed agisce
modificandoli tutti e due in qualsiasi caso. L' appesantimento del codice è,
però, obiettivamente limitato (nel caso peggiore, 1 ciclo in più per ogni
volta che la direttiva è chiamata).
Va tenuto comunque conto di questa particolarità nel caso il programma
implementi salti o branch senza uso di label o con il simbolo $ (pessima
pratica da evitare).
Comunque, come vediamo ora, è possibile scrivere macro con funzione
analoga alle direttive ora viste.
MACRO attorno al PCLATH
Per ogni operazione di salto o chiamata che passi da una pagina ad un' altra
occorre prevedere, dunque, una qualche azione sulla componente alta del
Program Counter per permettere la formazione del giusto indirizzo di
destinazione.
Una prima strategia è quella di effettuare salti e chiamate solamente
nella pagina corrente; questo è possibile in genere se il programma è
contenuto in una sola pagina.
Per evitare questa limitazione, occorre un sistema di aggiornamento del
PCLATH.
Sicuramente, operando "manualmente" è possibile ottenere questo
risultato, ma se il programma ha una certa ampiezza e supera la pagina,
diventa improponibile. Occorre disporre di un sistema per cui l' Assebler
possa calcolare da se il giusto indirizzo di destinazione.
Da tempo vari autori hanno proposto l' uso di macro, del genere "longCall"
e "longGoto" che permettano il superamento della pagina.
Ovviamente, e questo è un limite della paginazione della memoria,
occorreranno varie linee di istruzione addizionali ogni volta che si
effettuerà una chiamata o un salto "lunghi", appesantendo il codice
generato e rallentando l' esecuzione.
Riportiamo qui le macro di Tony Kubek [tony.kubek a flintab.com], che
rappresentano una delle scritture più classiche e funzionali.
- LONG_CALL ROUTINE permette di raggiungere qualsiasi pagina da qualsiasi pagina
- SHORT_CALL ROUTINE permette di passare da pagina 0 a pagina 1 o da pagina 2
a 3
- PCALL ROUTINE: quando non si sa come è la situazione delle pagine,
questa procedura dà un errore nel caso in cui sia necessaria una delle
precedenti; se la chiamata è nella stessa pagina non genera alcun codice
aggiuntivo.
E', ovviamente, possibile utilizzare LONG_CALL
in tutti i casi, anche entro
la stessa pagina. Però va considerato che viene occupato spazio in memoria
programma. Preferibilmente si userà la LONG_CALL
durante il
debug, per poi passare nelle versione definitiva alle altre, dove la prima sia
inutile.
Si tratta di macro che utilizzano le funzioni matematiche dell' Assembler
per calcolare la distanza tra l' indirizzo di chiamata e quello di
destinazione. In funzione di questo, viene manipolato automaticamente il
PCLATH.
Alcuni messaggi indicano al programmatore in che situazione ci si stia
trovando.
; + + + + +
; SET_PCLATH macro per LONG_CALL
; Set o reset i bit di PCLATH 03:04 secondo
; la variabile PCLATH_34
;
SET_PCLATH MACRO PCLATH_34
#if (PCLATH_34 & 0x10)
bsf PCLATH, 4
#else
bcf PCLATH, 4
#endif
#if (PCLATH_34 & 0x08)
bsf PCLATH, 3
#else
bcf PCLATH, 3
#endif
ENDM
; + + + + +
; SET_PCLATH4 macro per LONG / SHORT_CALL
; Set o reset PCLATH secondo la variabile PCLATH_4
;
SET_PCLATH4 MACRO PCLATH_4
#if (PCLATH_4 & 0x10)
bsf PCLATH, 4
#else
bcf PCLATH, 4
#endif
ENDM
; + + + + +
; SET_PCLATH3 macro per LONG / SHORT_CALL
; Set o reset il bit PCLATH 3 secondo la variabile PCLATH_3
;
SET_PCLATH3 MACRO PCLATH_3
#if (PCLATH_3 & 0x08)
bsf PCLATH, 3
#else
bcf PCLATH, 3
#endif
ENDM
; + + + + +
; LONG_CALL chiamata lunga: imposta i bit di pagina PCLATH<5-4>
; per chiamare qualsiasi pagina da qualsiasi pagina.
; W-reg non viene modificato.
LONG_CALL MACRO label
LOCAL DEST_HIGH , SOURCE_HIGH, DIFF_HIGH
DEST_HIGH SET (HIGH (label) & 0x18) ; salva Bit5-4 dell'indirizzo dest
SOURCE_HIGH SET (HIGH ($) & 0x18) ; --- | | --- indirizzo sorgente
DIFF_HIGH SET DEST_HIGH ^ SOURCE_HIGH ; differenza (XOR)
#if (DIFF_HIGH == 0)
; Stessa pagina, non dovrebbe generare codice aggiuntivo
MESSG "Chiamate alla stessa pagina; sostituire LONG_CALL con PCALL" label
nop
nop
call label
nop
nop
#else
; Verifica se entrambi i bit devono essere impostati
; es. page0<->page3 or page2<->page3
#if (DIFF_HIGH == 0x18) ; differenza di entrambi i bit
MESSG "Set bit per long call"
SET_PCLATH DEST_HIGH ; imposta entrambi i bit in PCLATH
call label
SET_PCLATH SOURCE_HIGH ; azzerare entrambi i bit in PCLATH
#else
; solo bit alto
; delta 1 pagina, cioè page0 <-> 1 o pagina 2 <-> 3
MESSG "Chiamata attraverso una sola pagina, sostituire LONG_CALL con SHORT_CALL" label
#if (DIFF_HIGH == 0x10) ; diff nel bit alto
nop
SET_PCLATH4 DEST_HIGH ; PCLATH 4
call label
SET_PCLATH4 SOURCE_HIGH
nop
#else
; solo bit basso
nop
SET_PCLATH3 DEST_HIGH ; PCLATH 3
call label
SET_PCLATH3 SOURCE_HIGH
nop
#endif
#endif
#endif
ENDM
; + + + + +
; SHORT_CALL chiamata breve, tra page0 <-> 1 o pagina 2 <-> 3
SHORT_CALL MACRO label
DEST_HIGH LOCALE, SOURCE_HIGH, DIFF_HIGH
DEST_HIGH SET (HIGH (label) & 0x18) ; salvare bit 5-4 indirizzo dest.
SOURCE_HIGH SET (HIGH ($) & 0x18) ; --- | | --- indirizzo partenza
DIFF_HIGH SET DEST_HIGH ^ SOURCE_HIGH ; differenza (XOR)
#if (DIFF_HIGH == 0) ; stessa pagina, nessun codice aggiuntivo
MESSG "chiamate a pagina, è necessario sostituire SHORT_CALL con PCALL" label
call label
nop
#else
; verifica di sicurezza per LONG_CALL
#if ((DIFF_HIGH & 0x18) == 0x18)
MESSG "ATTENZIONE! Sostituire SHORT_CALL con LONG_CALL" label
#endif
MESSG "bit pagina Impostazione pagina è per breve traversata chiamata"
#if (DIFF_HIGH == 0x10); diff in po 'alto
SET_PCLATH4 DEST_HIGH ; bit 4 di PCLATH
call label
SET_PCLATH4 SOURCE_HIGH
#else
, Bit più basso solo
SET_PCLATH3 DEST_HIGH ; bit 3 di PCLATH
call label
SET_PCLATH3 SOURCE_HIGH
#endif
#endif
ENDM
; + + + + +
; PCALL chiamata nella pagina
; Messaggi di avviso se necessari LONG / SHORT
PCALL MACRO label
DEST_HIGH LOCALE, SOURCE_HIGH, DIFF_HIGH
DEST_HIGH SET (HIGH (label) & 0x18) ; salvare bit 5-4 indirizzo dest
SOURCE_HIGH SET (HIGH ($) & 0x18) ; --- | | --- indirizzo sorgente
DIFF_HIGH SET DEST_HIGH ^ SOURCE_HIGH ; differenza (XOR)
#if (DIFF_HIGH == 0) ; stessa pagina, ok
call label
#else
; verifica di sicurezza per LONG_CALL
#if ((DIFF_HIGH & 0x18) == 0x18)
ERROR "ATTENZIONE! Sostituire PCALL con LONG_CALL" label
#else
ERROR "ATTENZIONE! Sostituire PCALL con SHORT_CALL" label
#endif
#endif
ENDM
Ci
si può basare anche sulla direttiva PAGESEL
dell' Assembler, il che semplifica la struttura delle macro.
L_CALL MACRO label
#if((HIGH ($) & 0x18) == (HIGH (label) e 0x18))
; stessa pagina
MESSG "Chiamate alla stessa pagina, utilizzare CALL" label
nop
nop
call label
#else
MESSG "Utilizzo direttiva PAGESEL"
PAGESEL label
call label
#endif
ENDM
La
stessa cosa vale nel per il GOTO.
Ricordiamo ancora una volta che non ci sono problemi di manipolazione
del PCLATH per il ritorno dalle chiamate in quanto
a seguito delle istruzioni RETURN, RETLW e RETFIE il Program Counter viene
caricato con il contenuto del top dello stack.
Essendo lo stack a bit, esso conserva l' indirizzo di ritorno completo
della parte bassa e alta.
Però il
PCLATH resta settato sull' ultima modifica effettuata ed occorre
riposizionarlo se il sorgente contiene salti o chiamate a pagine
diverse. |
|