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
x 00000001

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