Linguaggio assembly - parte II
Operazioni logiche
AND logico (ANDWF e ANDLW)
Per l'operazione logica di AND sono messe a disposizione le due istruzioni ANDWF e ANDLW.
Sintassi delle istruzioni:
ANDWF F,D
ANDLW K
ANDLW
permette di effettuare l'AND tra il registro di lavoro W ed un valore
letterale K (un byte); il risultato viene posto di nuovo in W ed
inoltre se questo (dopo l'esecuzione dell'istruzione) vale zero, viene
settato ad 1 il bit Z del registro STATUS, altrimenti viene settato a 0.
ANDWF permette invece di effettuare l'AND tra il registro di lavoro W
ed un registro F qualsiasi;. Anche in questo caso viene modificato il
bit Z del registro STATUS, esattamente come sopra.
Esempio:
AND logico tra W ed il valore 0xAA.
ANDLW 0xAA,1
Esempio:
AND logico tra W ed il contenuto del registro 0x34. Porre il risultato in W.
ANDWF 0x34,0
|
OR logico (IORWF e IORLW)
Per l'operazione logica di OR sono messe a disposizione le due istruzioni IORWF e IORLW.
Sintassi delle istruzioni:
IORWF F,D
IORLW K
Funzionano esattamente come le istruzioni di AND, ma in questo caso si effettua l'OR.
Esempio:
OR logico tra W ed il valore 0xAA
IORLW 0xAA
Esempio:
OR logico tra W ed il contenuto del registro 0x34. Porre il risultato in W.
IORWF 0x34,0
|
XOR logico (XORWF e XORLW)
Per l'operazione logica di XOR sono messe a disposizione le due istruzioni XORWF e XORLW.
Sintassi delle istruzioni:
XORWF F,D
XORLW K
Funzionano esattamente come le istruzioni di AND e OR, ma in questo caso si effettua l'operazione di XOR.
Esempio:
XOR logico tra W ed il valore 0xAA
XORLW 0xAA
Esempio:
XOR logico tra W ed il contenuto del registro 0x34. Porre il risultato in W.
XORWF 0x34,0
|
NOT logico (COMF)
L'operazione di NOT si effettua con l'istruzione COMF
Sintassi della istruzione:
COMF F,D
L'istruzione
inverte tutti i bit del registro puntato da F. Se D=0 il risultato
invertito viene posto in W (senza modificare F), altrimenti viene
scritto in F. Come le altre istruzioni logiche, anche questa setta il
bit Z del registro STATUS in funzione del risultato dell'operazione.
Esempio:
Invertire i bit del registro alla locazione 0x30 e porre il risultato in W senza modificare il primo.
COMF 0x30,0
|
|
Operazioni aritmetiche
Addizione (ADDWF e ADDLW)
Per l'operazione di somma sono messe a disposizione le due istruzioni ADDWF e ADDLW.
Sintassi delle istruzioni:
ADDWF F,D
ADDLW K
ANDLW
effettua la somma tra un byte K ed il contenuto del registro di lavoro
W. Il risultato viene posto di nuovo in W. L'istruzione inoltre
modifica opportunamente i bit Z,C e DC del registro STATUS.
ANDWF e' simile ed effettua la somma tra il contenuto del registro
puntato da F e quello del registro di lavoro W. Il risultato viene
posto in W se D=0, altrimenti viene posto in F. Anche in questo caso
vengono impostate opportunamente i bit Z,C e DC del registro STATUS.
Esempio:
Sommare il contenuto di W con 12
ADDLW 0x12
Esempio:
Sommare il contenuto del registro alla locazione 0x0E con W e porre il risultato nel primo.
ADDWF 0x0E,1
|
Sottrazione (SUBWF e SUBLW)
Per la sottrazione sono messe a disposizione le due istruzioni SUBWF e SUBLW.
Sintassi delle istruzioni:
SUBWF F,D
SUBLW K
Funzionano esattamente come le relative operazioni di somma; l'unica differenza e' ovviamente l'operazione effettuata.
Decremento (DECF)
Operazione di decremento unitario.
Sintassi dela istruzione:
DECF F,D
Decrementa
di una unita' il contenuto del registro puntato da F. Il risultato
viene posto in W se D=0, altrimenti viene posto in F. L'istruzione
imposta ad 1 il bit Z del registro STATUS se il risultato
dell'operazione di decremento e' zero, altrimenti lo imposta a 0.
Esempio:
Decrementare di una unita' il registro 0x32 e porre il risultato in W
DECF 0x32,0
|
Incremento (INCF)
Operazione di decremento.
Sintassi dela istruzione:
INCF F,D
Decrementa
di una unita' il contenuto del registro puntato da F. Il risultato
viene posto in W se D=0, altrimenti viene posto in F. L'istruzione
imposta ad 1 il bit Z del registro STATUS se il risultato
dell'operazione di decremento e' zero, altrimenti lo imposta a 0.
Esempio:
Decrementare di una unita' il registro 0x32 e porre il risultato in W
DECF 0x32,0
|
|
Operazioni di modifica dei registri
Rotazione (RRF e RLF)
Il
microcontrollore possiede due istruzioni di rotazione: RRF e RLF. La
rotazione consiste nel "traslare di un posto" tutti i bit all'interno
di un dato registro. La rotazione puo' quindi essere destra (RRF) o
sinistra (RLF) a seconda che la traslazione avvenga appunto verso
destra o verso sinistra. La rotazione ovviamente e' ciclica, cioe' il
bit che esce rientra "dall'altra parte", ma non
subito...prima "passa attraverso" il bit C del registro STATUS. Un
esempio chiarira' le idee. Supponiamo che il contenuto di un dato
registro F sia il seguente (sopra ai bit del registro poniamo i
rispettivi indici):
76543210
00000001
Affianchiamo
alla sinistra del registro il bit C (bit carry) del registro
STATUS. All'inizio tale bit potra' trovarsi in uno stato qualsiasi
(dipendera' da quello che e' successo prima nel programma, il suo stato
non e' noto) quindi, anziche' scrivere 0 oppure 1, scriveremo una
'x'.
C 76543210
Ora, effettuando in sequeza tante istruzioni RLF (Rotate Left) ecco come cambia il contenuto del nostro registro e del bit C:
C 76543210
x 00000001
RLF 0 00000010
RLF 0 00000100
RLF 0 00001000
RLF 0 00010000
RLF 0 00100000
RLF 0 01000000
RLF 0 10000000
RLF 1 00000000
RLF 0 00000001
RLF 0 00000010
e cosi' via...
Analogamente, ecco cosa succede con RRF (Rotate Right) partendo sempre dal medesimo contenuto di registro:
C 76543210
x 00000001
RRF 1 00000000
RRF 0 10000000
RRF 0 01000000
RRF 0 00100000
RRF 0 00010000
RRF 0 00001000
RRF 0 00000100
RRF 0 00000010
RRF 0 00000001
RRF 1 00000000
RRF 0 10000000
RRF 0 01000000
RRF 0 00100000
RRF 0 00010000
e cosi' via...
Ecco
perche' abbiamo detto che la rotazione avviene "attraverso" il bit C.
Questo sistema permette la lettura in sequenza di tutti i bit di un dato
registro semplicemente effettuando le rotazioni e
controllando lo stato del bit C.
Sintassi delle istruzioni:
RRF F,D
RLF F,D
RRF
effettua la rotazione destra di un dato registro F. Il risultato viene
posto in F se D=1, altrimenti viene posto in W. Il parametro D ci viene
in aiuto nel caso in cui volessimo lasciare inalterato il registro F;
infatti se D=1 tutte le versioni traslate del registro F si susseguono
in W. Ovviamente il bit C viene aggiornato opportunamente sia con D=0
che con D=1.
RLF effettua la rotazione sinistra di un dato registro F. Valgono le stesse considerazioni viste per RRF.
Esempio:
Inviare
in forma seriale e sulla linea RB0 della PORTA B tutti e otto i bit del
registro alla locazione 0x45. Il primo bit inviato deve essere quello
meno significativo. L'invio dei bit deve avvenire ogni 2us. Si suppone
un clock di 4MHz
STATUS EQU 0x03
PORT_B EQU 0x06
TRIS_B EQU 0x06
BSF STATUS,5
;selezione
del banco RAM 1
MOVLW 0xFF ;imposta tutte le linee B come output
MOVWF TRIS_B
BCF STATUS,5 ;selezione del banco RAM 0
RRF 0x45
MOVF STATUS,0 ;carica il registro STATUS in W
MOVWF PORT_B ;scrive W in PORT_B (il bit C va in RB0)
NOP
NOP
RRF 0x45
MOVF STATUS,0 ;carica il registro STATUS in W
MOVWF PORT_B ;scrive W in PORT_B (il bit C va in RB0)
NOP
NOP
RRF 0x45
MOVF STATUS,0 ;carica il registro STATUS in W
MOVWF PORT_B ;scrive W in PORT_B (il bit C va in RB0)
NOP
NOP
RRF 0x45
MOVF STATUS,0 ;carica il registro STATUS in W
MOVWF PORT_B ;scrive W in PORT_B (il bit C va in RB0)
NOP
NOP
RRF 0x45
MOVF STATUS,0 ;carica il registro STATUS in W
MOVWF PORT_B ;scrive W in PORT_B (il bit C va in RB0)
NOP
NOP
RRF 0x45
MOVF STATUS,0 ;carica il registro STATUS in W
MOVWF PORT_B ;scrive W in PORT_B (il bit C va in RB0)
NOP
NOP
RRF 0x45
MOVF STATUS,0 ;carica il registro STATUS in W
MOVWF PORT_B ;scrive W in PORT_B (il bit C va in RB0)
NOP
NOP
RRF 0x45
MOVF STATUS,0 ;carica il registro STATUS in W
MOVWF PORT_B ;scrive W in PORT_B (il bit C va in RB0)
NOP
NOP
|
Nibble-swap (SWAPF)
Effettua
uno scambio dei quattro bit piu' significativi con i quattro meno
significativi. In gergo il termine "nibble" significa "gruppo di
quattro bit". L'istruzione scambia tra loro il nibble alto ed il nibble
basso di un dato registro.
Sintassi della istruzione:
SWAPF F,D
Effettua
lo scambio delle due meta' del registro F. Se D=1 il risultato viene
posto in F, altrimenti viene posto in W senza toccare F.
Esempio:
Scambiare tra loro le due meta' del registro 0x32 e porre il risultato nel registro 0x33 lasciando inalterato 0x32.
SWAPF 0x32,0
MOVWF 0x33
|
|
Chiamata a funzione
Il
microcontrolle 16F84 mette a disposizione del programmatore anche
l'utilissimo sistema di chiamata a funzione, un classico per qualsiasi
CPU. In pratica tramite apposite istruzioni e' possibile
richiamare una pezzo di codice di programma; in questo senso il sistema
e' simile ad un GOTO con ritorno, cioe' si salta in un altro punto
della memoria di programma, si esegue un certo pezzo di codice e poi si
ritorna all'istruzione immediatamente successiva a quella dove abbiamo
saltato. L'istruzione di salto e' CALL, mentre l'istruzione di ritorno
puo' essere o RETURN, oppure RETLW. Il pezzo di codice eseguito e'
detto "subroutine". In pratica, quando si vuole richiamare una
subroutine, e' sufficiente invocare la CALL specificando
l'indirizzo della prima istruzione di tale subroutine (o una label,
come visto per le istruzioni di salto): il codice viene quindi eseguito
dal punto specificato in poi sino a che non si giunge ad una istruzione
di RETURN o RETLW. Giunti a questo punto si ritorna ad eseguire il
programma dall'istruzione immediatamente successiva a CALL. Si
capisce facilmente quindi che l'ultima istruzione di una data
subroutine deve essere o RETURN o RETLW; ovviamente se tali istruzioni
non sono presenti il ritorno non avra' luogo. Inoltre, piu' CALL possono
riferirsi ad una stessa subroutine, esattamente come piu GOTO possono
riferirsi ad uno stesso punto di codice.
La figura sottostante mostra il sistema di chiamata a funzione:
La
subroutine e' in rosso, con in evidenza la prima
istruzione e l'ultima (RETURN). Il programma principale non e' mostrato
per intero ma puo' trovarsi ovunque, prima della subroutine, dopo,
oppure tutt'attorno alla subroutine stessa.
Quando nel programma principale si incontra una CALL viene eseguito il
salto verso la locazione specificata (cioe' alla prima istruzione della
subroutine), si esegue il corpo della subroutine e poi si ritorna verso
l'istruzione immediatamente successiva alla CALL grazie a RETURN (o
RETLW).
La CALL richiede sempre due cicli macchina (e' un salto).
In che modo il microcontrollore riesce a "ricordarsi" il punto al
quale tornare indietro? Esiste una piccola memoria ausiliaria,
detta stack, in grado di memorizzare l'indirizzo di ritorno
(l'indirizzo della istruzione immediatamente successiva alla CALL);
quando si esegue un RETURN tale indirizzo viene ripescato dallo stack e
si puo' ritornare al programma chiamante. Lo stack e' una memoria di
tipo LIFO (Last In,
First Out) che puo' contenere sino ad un massimo di otto indirizzi di
programma; grazie a cio' e' possibile effettuare una CALL anche dentro
una subroutine e ripetere tale processo ricorsivamente sino ad un
massimo di otto volte (cioe' otto CALL consecutive). In sostanza, ogni
volta che si effettua una CALL si salta e viene inserito
automaticamente nello stack l'indirizzo di ritorno, ogni volta che si
effettua un RETURN si "toglie" un indirizzo dallo stack e si salta a
quello (la rimozione consiste nel togliere l'ultimo elemento inserito) .
Chiamata a funzione (CALL)
Effettua la chiamata a funzione (subroutine).
Sintassi della istruzione:
CALL addr
Salta
all'indirizzo di programma "addr" e carica nello stack l'indirizzo
della prossima istruzione per rendere possibile il ritorno. "addr" puo'
essere direttamente un indirizzo esadecimale della memoria di programma
che punta alla prima istruzione della subroutine, oppure piu'
semplicemente una label testuale come visto per il GOTO. Come detto in
precedenza, per eseguire l'istruzione CALL occorrono due cicli macchina.
Ritorno semplice (RETURN)
Ritorna indietro verso la CALL chiamante.
Sintassi della istruzione:
RETURN
L'istruzione
non ha parametri. L'esecuzione comporta il recupero dell'ultimo
indirizzo di programma inserito nello stack ed il salto verso tale
locazione. L'istruzione prevede due cicli macchina per essere eseguita.
Esempio:
Chiamare una subroutine che azzera il contenuto del registro 0x30, poi incrementare di una unita' tale registro.
CALL azzera ;vai alla subroutine "azzera"
INCF 0x30
azzera: ;label di inizio della subroutine "azzera"
CLRF 0x30 ;azzera 0x30
RETURN ;ritorna indietro al programma principale
|
Ritorno con parametro (RETLW)
Questa
istruzione e' simile a RETURN ma con un'aggiunta significativa: oltre a
tornare indietro viene inizializzato il registro di lavoro W con un
valore prefissato.
Sintassi della istruzione:
RETLW K
Si
effettua il ritorno all'istruzione immediatamente successiva alla CALL
chiamante (esattamente come RETURN) ma in piu' provvede a caricare il
registro W con il valore K che e' stato specificato nell'istruzione
RETLW. Queso tipo di ritorno puo' essere molto utile in tutti quei casi
in cui occorre passare un byte di risultato al programma
chiamante.
Esempio:
Effettuare
la somma dei due registri alle locazioni 0x20 e 0x21 utilizzando una
apposita subroutine. Passare il risultato al programma chiamante
tramite il registro W.
MOVLW 0x10 ;carica i registri 0x20 e 0x21 con i valori
MOVWF 0x20 ;da sommare
MOVLW 0x3A
MOVWF 0x21
CALL somma ;vai alla subroutine "somma"
somma: ;label di inizio della subroutine "somma"
MOVF 0x20,0 ;copia il contenuto di 0x20 in W
ADDWF 0x21,0 ;effettua la somma
RETLW ;ritorna indietro al programma principale
|
|
|