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:
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:
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 :
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:
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: