Tutorials - PIC

 

 

Il Program Counter


MPASM per pagine e banchi

Va notato che MPASM dispone di alcune direttive per la gestione delle pagine e dei banchi. Esse sono:
  • PAGESEL
  • BANKSEL
  • BANKISEL

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.

 


 

    

Copyright © afg . Tutti i diritti riservati.
Aggiornato il 12/01/13 .