Una subroutine (o anche routine o sottoprogramma) è una sequenza di istruzioni assembly che realizza un determinato compito. Questa sequenza non è autonoma, ma viene chiamata (call) all'interno di un generico programma; al termine della esecuzione della subroutine, il Program Counter ritorna (return) al suo valore precedente, per eseguire l'istruzione immediatamente seguente la call.
Una subroutine è generalmente identificata da una label e deve terminare con l'istruzione return.
Per una migliore comprensione di questo esempio, si consiglia di aver prima completato gli esempi ed esercizi da 4a a 4d e di aver provato a far lampeggiare un LED.
Il seguente esempio d'uso mostra il programma principale main al cui interno, per due volte, è invocata la stessa subroutine delay:
Il funzionamento è intuitivo:
Alcune osservazioni:
Vediamo nel dettaglio il funzionamento dell'istruzione call, con riferimento ai fogli tecnici (nota 4):
Vediamo nel dettaglio il funzionamento dell'istruzione return, con riferimento ai fogli tecnici (nota 4):
Prima di proseguire, completare l'esercizio 1 e l'esercizio 2
La soluzione proposta nel primo esempio ha vari difetti:
L'esercizio 2 ha creato, a livello umano, un ritardo molto breve. Un'idea potrebbe essere quella di scrivere una subroutine che richiama decine di volte ritardo_breve, per ottenere un ritardo più lungo. Per esempio il codice seguente mostra ritardo_lungo_1, ovviamente dalla durata di circa 30 ms
Si tratta di una soluzione molto rigida e ben poco elegante; sarebbe più comodo usare un loop che richiama ritardo_breve qualche decina di volte, decrementando per esempio da venti a zero un'apposita variabile contatore:
Ancora meglio sarebbe usare WREG invece che contatore, per non sprecare una cella di memoria. Peccato che tale soluzione non funziona (perché? La risposta poco più avanti, ma vale la pena provare a rispondere usando la propria intelligenza...).
Una subroutine, per essere davvero utilizzabile, non deve come effetto collaterale modificare i registri che descrivono lo stato del processore, cioè i registri WREG, STATUS e BSR.
Una possibile soluzione è quella di creare alcuni registri temporanei che permettono di salvare i valori di tali registri all'inizio del codice della subroutine e di recuperarli al termine.
Questa tecnica è mostrata nella codice che potete scaricare a fondo pagina. Questo codice contiene due subroutines:
Il salvataggio nei registri temporanei di WREG e STATUS è reso necessario dal fatto che entrambe le subroutines usano gli stessi registri: non salvarli avrebbe causato un'interferenza tra le due subroutines e, in definitiva, il loro mancato funzionamento.
Si noti che per quanto riguarda ritardo_lungo non è stato implementato alcun meccanismo di registri temporanei; sarebbe stata necessaria la creazione di ulteriori registri temporanei, con ulteriore spreco di spazio e potenziali rischi difficili da controllare nel caso di omonimie. (nota 2)
L'ultimo esempio mostra l'utilizzo dello stack software per il salvataggio dei registri: in pratica ogni variabile che verrà modificata all'interno della subroutine viene salvata all'inizio e recuperata prima del return. Il codice lo potete scaricare a fondo pagina
Come prassi tutte le subroutines iniziano con il salvataggio del contesto nello stack software (nota 6):
movwf POSTDEC1
; salva WREG nello stack
movff STATUS, POSTDEC1 ; salva i flags nello stack
movff BSR, POSTDEC1 ; salva BSR nello stack
Terminano con il ripristino del contesto, prelevando i contenuti originali dei registri dalla stack software:
movff PREINC1, BSR ; ripristina il
registro BSR
movff PREINC1, STATUS ; ripristina i flags
movf PREINC1, W ; ripristina il
registro WREG
Si noti la simmetria tra l'ordine di salvataggio e quello di ripristino; non ha invece particolare importanza l'ordine di salvataggio, purché venga mantenuta la simmetria al momento del ripristino.
Questa può essere considerata la soluzione definitiva, utilizzabile anche in altri contesti, come quello delle interruzioni.
Data di creazione di questa pagina: maggio 2016
Ultima modifica: 5 luglio 2017
Assembly PIC18 - Versione 0.5 - aprile 2018
Copyright 2016-2018, Vincenzo Villa (https://www.vincenzov.net)
Assembly PIC18 di Vincenzo Villa è distribuito con Licenza Creative Commons Attribuzione 4.0 Internazionale