Tutorial → PIC18 →
Assembly → L'assemblatore
→ Ancora sulle variabili...
Stesura preliminare
In questa pagina sono mostrati alcuni usi avanzati della memoria.
Stack software
Prima di iniziare, assicuratevi di conoscere i contenuti di questa
pagina:
Cosa è
uno stack?
A differenza di altri processori, il PIC18 non possiede uno stack
dedicato per la memorizzazione di generiche variabili. Possiede invece uno
stack hardware, usato esclusivamente per gli indirizzi.
Per realizzare il cosiddetto stack software il PIC18 utilizza uno dei tre registri FSR, in genere il
numero 1. Se usato in questo modo è spesso chiamato stack pointer
(puntatore -alla cima- dello stack, come con altri
processori) in quanto punta alla prima cella libera dello stack, partendo
dall'alto.
Innanzitutto occorre riservare alcune celle di memoria RAM per uso esclusivo
dello stack. La soluzione più semplice è utilizzare un intero banco (256
byte) anche se ovviamente è possibile dedicare meno spazio (nota
1). Occorre:
- (righe 15-16) Riservare lo spazio necessario allo stack e alle
variabili. Nell'esempio 265 byte (0x100), un intero banco
- (riga 32) Inizializzare lo stack pointer
FSR1, facendolo puntare all'ultima cella dello stack. Questa
situazione indica che lo stack è vuoto
- Per salvare nello stack una variabile dobbiamo usare
l'indirizzamento indiretto che decrementa lo stack pointer dopo l'uso (POSTDEC1).
L'immagine seguente mostra nella finestra File Register
l'effetto del salvataggio di cinque variabili a partire da 0x1FF andando
verso sinistra. Nell'ordine
- WREG (0x55)
- STATUS (valore 0, non impostato
esplicitamente)
- BSR (valore 0x0F, valore impostato
automaticamente dalla direttiva BANKSEL)
- var1 (0x11)
- var2 (0x22)
- Il "recupero" delle variabili dallo stack è fatto "al contrario",
cioè si tolgono per prime le ultime variabili inserite. In questo modo
il contenuto delle variabili viene ripristinato al valore originale.
Il codice sorgente completo è scaricabile a fondo pagina. Si consiglia vivamente di simularlo con attenzione,
modificando il numero e l'ordine delle variabili salvate nello stack.
Variabili inizializzate
Le variabili sono memorizzate nella memoria volatile e quindi
all'accensione contengono un valore casuale. Per poter usare variabili con
un valore iniziale Microchip ha scelto una strada un po' complessa e
soprattutto non documentata, almeno nella versione attuale
- Le variabili, inclusi i loro valori iniziali, devono essere definite in una o
più sezioni indicate con la parola chiave IDATA
oppure IDATA_ACS, come nell'immagine
seguente. In particolare nell'esempio sono create tre variabili di
diversa lunghezza in un generico banco (righe 7-10) ed una nell'access
bank (nota
3):
- Il valore iniziale non può essere essere memorizzato in RAM, essendo
volatile. Il linker crea perciò una copia di queste variabili nella
memoria FLASH, inserendo il valore iniziale scritto dal programmatore in
una tabella descritta nel paragrafo successivo. Questa operazione viene
fatta automaticamente al momento della compilazione del codice
- All'inizio dell'esecuzione è necessario chiamare un'apposita routine
che copia il contenuto delle variabili dalla memoria FLASH alla memoria
RAM (riga 17). Un esempio di tale codice è disponibile a
fondo pagina.
Nell'insieme tale procedura è piuttosto complessa, ma disponendo del
codice opportuno non è necessario conoscere i dettagli di seguito descritti;
è sufficiente:
- creare le sezioni idata necessarie
- inserire nel progetto il file idata.asm,
senza modifiche se non nel file che specifica il PIC18 utilizzato
- invocare all'inizio del codice o quando necessario la
subroutine init_data.
La struttura della tabella dei valori iniziali
La struttura della tabella che contiene i dati iniziali e la procedura
sono piuttosto complesse, ma non è necessario conoscerle neppure
superficialmente se si usa il codice descritto nel precedente paragrafo. La
lettura di questo paragrafo è quindi riservato ai curiosi e agli
appassionati di enigmistica...
Quanto mostrato è relativo all'esempio sopra riportato, costituito da una
sezione idata_acs ed una sezione
idata. Il linker ha scelto di riservare lo spazio
alle variabili rispettivamente a partire dall'indirizzo
0x000 e 0xF22
Nella memoria FLASH sono presenti due o più tabelle. Quella qui riportata
a sinistra descrive gli indirizzi di tutte le variabili ed inizia a partire
dall'indirizzo _cinit, in questo caso pari a
0x0006 (nota 2)
Nell'ordine possiamo leggere nella tabella di sinistra (i colori giallo e
azzurro identificano rispettivamente le sezioni
idata_sample e idata_sample_ACS
dell'esempio):
- 0006 Numero di sezioni
idata (due nell'esempio)
- 0008 Indirizzo in FLASH a cui si trovano i
dati della prima sezione, probabilmente su quattro byte (0x0000008F
nell'esempio)
000C indirizzo in RAM in cui si trovano le
variabili della prima sezione, probabilmente su quattro byte (0x00000F22
nell'esempio)
0010 Dimensioni del primo blocco di dati,
probabilmente su quattro byte (0x0000000B
nell'esempio, 11 byte)
- 0014 Indirizzo in FLASH a cui si trovano i
dati della seconda sezione, probabilmente su quattro byte (0x00000084
nell'esempio)
0018 indirizzo in RAM in cui si trovano le
variabili della seconda sezione, probabilmente su quattro byte (0x00000000
nell'esempio)
001C Dimensioni del secondo blocco di dati,
probabilmente su quattro byte (ancora 0x0000000B
nell'esempio, 11 byte)
Nella tabella di destra, a partire dall'indirizzo
0x0084 possiamo leggere i codici ASCII corrispondente alla stringa "Hello
World": 0x48 0x65 0x6C... 0x64 (nota
4)
Sempre nella tabella di destra, a partire dall'indirizzo dispari
0x008F possiamo leggere la variabile a 16 bit
0x1234, la variabile a 8 bit
0x08 e la stringa "abcdefgh" (0x61 0x62... 0x68)
Il codice della subroutine init_data
sposta:
- 11 byte dall'indirizzo in FLASH 0x008F
all'indirizzo in RAM 0xF22
- 11 byte dall'indirizzo in FLASH 0x0084
all'indirizzo in RAM 0x000
Il suo esame è lasciato come esercizio...
Note
- Creare stack di dimensioni maggiori è invece cosa più complessa,
inefficiente e tutto sommato inutile
- Microchip non documenta la struttura della tabella e quanto qui
descritto è frutto di reverse engineering
- Le stringhe vengono spesso create inserendo alla fine il carattere
NULL (0x00), secondo la convenzione del linguaggio C. Per esempio:
hello db "Hello World\0"
- Le coppie di byte di ciascuna cella sono scritti con notazione
little endian, e quindi vanno "scambiati"
Codice
- stack.asm - Un esempio di uso dello
stack software
- idata.asm - Uso di
idata: copia i valori iniziali dalla memoria FLASH alla memoria
RAM
Data di creazione di questa pagina: luglio 2017
Ultima modifica: 2 agosto 2017