T&T - PIC

 

Uso del simbolo $

 


Il simbolo $

Nell' Assembly, il simbolo $ indica il valore corrente del Program Counter.

Nell' Assembly, un simbolo sostituisce un valore assoluto e $,  introdotto fin dagli albori della programmazione, ha lo scopo di far si che la compilazione richiami il contenuto corrente del Program Counter in quella data posizione.
Si tratta di una abbreviazione molto comoda, che rende possibile sintetizzare al massimo il contenuto delle righe del sorgente.

Ad esempio, in linguaggio x86

   jmp $

indica che la destinazione del salto è la locazione puntata dal Program Counter in quel momento, ovvero l' istruzione di salto stessa, originando un loop infinito.
L' istruzione:

   jmp $ + n

farà saltare il Program Counter ad una locazione pari al valore corrente a cui viene sommato l' offset indicato da n.

Ovvero, il simbolo, opportunamente calcolato dall' Assembler, consente di effettuare re-direzionamenti del Program Counter senza usare label.
Ne vediamo un esempio, scritto in Assembly per PIC Base/Mid, con istruzioni lunghe 1 bytes:

Delay	movlw	0xF2
	movwf	d1
	movlw	0x02
	movwf	d2
dl_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	dl_0

	goto	$+1
	nop
	return

Se volessimo usare label:

Delay	movlw	0xF2
	movwf	d1
	movlw	0x02
	movwf	d2
dl_0
	decfsz	d1, f
	goto	dl_1
	decfsz	d2, f
dl_1	goto	dl_0

	goto	dl_2
dl_2	nop
	return

introducendo così due label di uso esclusivamente locale, che l' Assembler deve processare.

Questa pratica, comune a chi programma con gli Assembly da lungo tempo, non è per nulla inelegante. 

In passato era necessaria per ridurre il volume dei sorgenti, evitando di usare label utili in un solo punto, per alleggerire il lavoro dei compilatori. Oggi, la potenza dei sistemi di sviluppo (leggi personal computer), anche nel caso dei più anzianotti, è tale che l' assemblaggio di un sorgente ben impegnativo non porta via più di qualche secondo, se non frazioni di secondo, mentre non c'è sicuramente problema di spazio per i grandi testi ASCII dei sorgenti. 

Quindi un aumento di volume di questi, dovuto ad un numero maggiore di label. è del tutto irrilevante.

Per cui l' uso di $ non è più necessario: è molto più chiaro e pratico e rende estremamente più semplice la rilettura dei listati l' uso di label. 

Infatti:

Come abbiamo detto, che l' Assembler può calcolare anche una espressione attorno a $, come ad esempio $*2 oppure $-1 o $+n o qualsiasi altra formula ammessa.
Il calcolo effettuato dal compilatore si limita a sostituire il simbolo $ con il valore assoluto contenuto nel Program Counter al momento dell' istruzione e lo elabora secondo l' operazione aritmetica che viene indicata. Ma esso non può verificare la coerenza di quanto ordinato nel sorgente!

Dobbiamo ricordare che esiste un ampio numero di famiglie di PIC, la cui lunghezza delle istruzioni può essere differente: solo nei PIC a 8 bit, un opcode può occupare 1, 2 o  4 bytes. 
Ad esempio, nei PIC16F una istruzione è tipicamente 1 byte, ma nei PIC18F è tipicamente 2 byte.

Il calcolo del valore del simbolo $ da parte dell' Assembler è sempre e solo relativo al valore del Program Counter in quel punto e non ha riferimento alla lunghezza dell' istruzione !

Questo comporta problemi nella portabilità di un sorgente che faccia uso del simbolo $.
Quindi, per chi voglia continuare ad usare il $, è opportuno che faccia alcune considerazioni assolutamente indispensabili.

Se per i PIC 16 

test   btfsc   target, bit  
       goto    $-1

è perfettamente corretto, visto che le istruzioni occupano 1 byte, la stessa cosa, scritta per i 18F non andrà per niente bene, dato che le istruzioni occupano 2 bytes. Si dovrà scrivere allora :

test   btfsc   target, bit  
       bra     $-2

ovvero tutti gli indirizzi calcolati con il $ dovranno essere moltiplicati per 2 nel caso di istruzioni a 16 bit.
Ma bisognerà ricordare che ci sono anche le istruzioni a 32 bit.

Dunque, è errata anche la scrittura :

      nop
      goto
   $-1
 

dato che anche nop, nel set istruzioni enhanced, occupa due bytes

Una scrittura del genere non sarà segnalata come errore in assemblaggio, ma potrà provocare problemi nell' esecuzione rinviando il PC ad una locazione che non è una istruzione valida.
Per cui sarà necessario modificare con:

      nop
      goto   $-
2 

Poichè le istruzioni enhanced possono essere a 2 o a 4 bytes, la scrittura $ per indicare il PC corrente è da evitare in quanto, altrimenti, occorrerà contare esattamente la distanza del salto, cosa che non è sempre immediata nè semplice ed è certamente causa di errori difficili da risolvere:

loop   nop
       call   procedure    
       goto  
$-4

è corretto per i mid-range, ma per gli enhanced deve diventare:

loop   nop
       call   procedure    
       goto  
$-6

dato che call è a 4 bytes. 

Detto questo,  perchè diavolo perdere tempo a calcolare distanze relative, con il rischio di sbagliare e diventare matti in fase di debug ?

Meglio sarà abolire il $ ed utilizzare una label

 e scrivere semplicemente:

loop   nop
       call   procedure    
       goto   loop

Con l' uso del simbolo invece che di un valore assoluto, si elimina qualsiasi possibilità di errore, lasciando all' assembler di farsi i calcoli corretti.  
Il che , tra l' altro, rende portabile il codice tra le varie famiglie, mentre, nel caso di uso intensivo del $ sarà richiesta una attenta revisione del sorgente.

Così, se dovessimo replicare l' esempio iniziale per i PIC18F, dovremmo modificarlo con:

Delay	movlw	0xF2
	movwf	d1
	movlw	0x02
	movwf	d2
dl_0
	decfsz	d1, f
	goto	$+6     ;<-----
	decfsz	d2, f
	goto	dl_0

	goto	$+4     ;<-----
	nop
	return

Quindi, la routine nella versione iniziale NON è portabile tra i vari processori e richiede un adattamento non sempre facile da farsi, sopratutto se la distanza del salto è di varie istruzioni, il che richiede un noioso calcolo a mano.

Invece, la versione con label è perfettamente portabile, dato che sarà l' Assembler a calcolare gli indirizzi corretti, corrispondenti alle label, a seconda della famiglia di processore usato.


Però talvolta...

Nonostante quello che abbiamo appena detto, non è che il segno $ sia da cancellare completamente: dove non è causa di ambiguità potrebbe essere utilizzato, essenzialmente allo scopo di non creare label "sotto utilizzate".
Ad esempio, nell' ambiente dei Baseline, le istruzioni occupano un solo byte e quindi un salto breve è facilmente valutabile. 

Ad esempio, se consideriamo la linea:

      goto    $

essa è identica a:

loop  goto   loop

Una istruzione goto $+1 fa avanzare il PC di una posizione, ovvero all' istruzione successiva . In sostanza, non ha alcuna funzione, dato che non altera la successione delle istruzioni, esattamente come potrebbe fare un nop. Quindi:

      goto    $+1                            
      retlw   0   

ha lo stesso valore di:

      goto    next                            
next  retlw   0   

risparmiando la dichiarazione di una label che serve unicamente per quel salto e, nella logica del flusso di istruzioni, ha lo stesso effetto di:

      nop                            
      retlw   0  

Ma solo nella logica, in quanto all' atto pratico, nop è eseguito in un ciclo, ma goto $+1  impiega due cicli per la sua esecuzione. Posso quindi usarlo in una routine di tempo per avere più ritardo con una sola linea di istruzione.

La riga goto $+2 fa avanzare il PC di due posizioni, quindi salta l' istruzione successiva. In pratica:

      goto    $+2                            
      decfsz  d3, f     
       goto   Delay1s_0 


è identico a scrivere

      goto    salta                            
      decfsz  d3, f     
salta  goto   Delay1s_0 

anche qui risparmiando la dichiarazione di una label che serve unicamente per quel salto.

In sostanza, il simbolo $ va usato solo ed esclusivamente se si è in grado di dare il giusto valore all' operazione.


Argomenti collegati:


 

Copyright © afg. Tutti i diritti riservati.
Aggiornato il 17/03/14.