Lezione 10
🎓 PIC16 — From Zero to Hero · Lezione 10
Direttive dell’assembler 🗂️
ORG
EQU
CBLOCK
DT
#DEFINE
Non sono istruzioni CPU — sono comandi per l’assembler. Fondamentali per scrivere codice leggibile e manutenibile.
🎯 Cosa impari in questa lezione
- La differenza tra direttiva e istruzione: cosa processa l’assembler vs la CPU
ORG— posiziona il codice in memoria a un indirizzo specificoEQU— definisce costanti simboliche (registri, bit, valori)CBLOCK / ENDC— alloca variabili in RAM senza contare indirizzi a mano#DEFINE— alias testuali, utile per macro sempliciDW / DT / RES— dati diretti in program memoryLIST, __CONFIG, END— metadata del progetto- La struttura completa di un file
.asmprofessionale
📋 Scheda lezione
| Modulo | 1 — Assembly Base |
| Lezione | 10 di 42 |
| Livello | ⭐⭐ Principiante+ |
| Tempo | ~30 minuti |
| Nessuna istruzione CPU | Solo direttive assembler |
| Output | Template file .asm completo |
🧠 Direttive vs istruzioni — la distinzione fondamentale
Le istruzioni (MOVLW, ADDWF, GOTO…) vengono tradotte in opcode a 14 bit e caricate in program memory: la CPU le esegue a runtime. Le direttive sono comandi per l’assembler stesso: influenzano come il testo viene tradotto, ma non generano opcode e non esistono nel file .hex finale. Sono invisibili alla CPU.
① ORG — posiziona il codice in memoria
ORG indirizzoORG non genera codice — dice all’assembler dove collocare il codice successivo. Ogni file PIC ne usa almeno due: uno per il reset vector (0x000) e uno per l’interrupt vector (0x004). Senza ORG il codice parte dall’indirizzo 0 per default, il che va bene solo per programmi semplicissimi.
; Struttura ORG obbligatoria per ogni progetto PIC16
ORG 0x0000 ; Reset vector — la CPU parte sempre da qui
GOTO START ; salta oltre il vettore interrupt
ORG 0x0004 ; Interrupt vector — il PIC salta qui ad ogni interrupt
GOTO ISR ; (o RETFIE se non usi interrupt)
ORG 0x0005 ; Il codice normale inizia qui (dopo 0x0004)
START:
; … inizializzazione …
; Puoi usare ORG anche per dati (lookup table)
ORG 0x0100 ; posiziona la tabella all’inizio di una pagina pulita
MY_TABLE:
ADDWF PCL, F
RETLW 0x10
RETLW 0x20
💡 Il GOTO a 0x000 è obbligatorio perché altrimenti l’istruzione a 0x000 sarebbe la prima riga del tuo START, e quando arriva un interrupt il PIC salta a 0x004 — che si troverebbe nel mezzo del tuo codice di inizializzazione. Il GOTO a START garantisce che 0x004 sia sempre libero per il vettore interrupt.
② EQU — costanti simboliche
SIMBOLO EQU valoreEQU è la “costante” del PIC assembly. Si usa per tre cose: nomi dei registri SFR (STATUS, PORTA…), nomi dei bit (RP0, Z, C…) e costanti numeriche del programma (frequenza, valori di soglia, ecc.). Senza EQU il codice è pieno di numeri magici incomprensibili.
; === Senza EQU (illeggibile) ===
BSF 0x03, 5 ; cosa fa?? Bank 1? Quale bit?
CLRF 0x86 ; 0x86? Chi è?
BCF 0x03, 5
; === Con EQU (leggibile) ===
STATUS EQU 0x03 ; registro STATUS
TRISB EQU 0x86 ; registro TRISB (Bank 1)
RP0 EQU 5 ; bit 5 di STATUS = bank select
SOGLIA EQU 0x64 ; 100 decimale — soglia temperatura
BSF STATUS, RP0 ; chiaro: seleziona Bank 1
CLRF TRISB ; chiaro: PORTB tutta output
BCF STATUS, RP0 ; chiaro: torna a Bank 0
; EQU può usare espressioni numeriche semplici
FOSC EQU 4000000 ; 4 MHz
LED_BIT EQU 0 ; RB0 = bit 0 di PORTB
BTN_BIT EQU 7 ; RB7 = bit 7 di PORTB (pulsante)
📌 Il file p16f84a.inc che includi con #include <p16f84a.inc> non è altro che un lungo elenco di EQU: STATUS, PORTA, PORTB, TRISA, TRISB, e tutti i nomi dei bit. Nel simulatore queste definizioni sono già built-in, quindi l’#include viene ignorato silenziosamente — ma è buona pratica includerlo comunque per compatibilità con MPLAB.
③ CBLOCK / ENDC — alloca variabili in RAM
CBLOCK indirizzo
NOME1
NOME2
ENDCCBLOCK (Constant BLOCK) è lo strumento più comodo per dichiarare le variabili GPR. L’assembler assegna l’indirizzo base alla prima variabile, base+1 alla seconda, e così via fino a ENDC. Se devi aggiungere una variabile in mezzo, non devi rinumerare tutto manualmente.
; Dichiarazione variabili con CBLOCK
CBLOCK 0x0C ; inizia dal primo byte GPR disponibile
CONTATORE ; → 0x0C loop counter
TEMP ; → 0x0D variabile temporanea
DELAY_HI ; → 0x0E delay loop esterno
DELAY_LO ; → 0x0F delay loop interno
FLAGS ; → 0x10 byte di flag software
ENDC
; Aggiungere NUOVA_VAR tra TEMP e DELAY_HI?
; Con CBLOCK: aggiungi la riga, il resto si sposta automatico.
; Con EQU manuali: dovresti rinumerare DELAY_HI, DELAY_LO, FLAGS.
④ DW, DT, RES — dati in program memory
Queste direttive inseriscono dati direttamente nel flusso di program memory, all’indirizzo corrente fissato da ORG.
| Direttiva | Sintassi | Genera | Uso tipico |
|---|---|---|---|
DW | DW val1, val2, … | Word a 14 bit grezze | Dati binari, opcode custom |
DT | DT val1, val2, … | Una RETLW k per ogni valore | Lookup table compatta |
RES | RES n | N word riservate (NOP) | Spazio riservato per versioni future |
; DT genera automaticamente una serie di RETLW
; Equivalente compatto della lookup table della lezione 09
TAB_QUADRATI:
ADDWF PCL, F
DT 0, 1, 4, 9, 16, 25, 36, 49 ; genera 8× RETLW
; RES: riserva spazio (es: per una ISR futura)
ORG 0x0004
RES 1 ; riserva 0x0004 senza mettere RETFIE ora
⑤ #DEFINE — alias testuali
#DEFINE funziona come EQU ma fa sostituzione testuale invece che numerica. Utile per macro semplici, alias di nomi lunghi, o per cambiare il comportamento del programma con una singola riga (feature flag).
; Alias per pin specifici del proprio hardware
#DEFINE LED PORTB, 0 ; LED sul RB0
#DEFINE PULSANTE PORTB, 7 ; pulsante sul RB7
; Ora puoi scrivere:
BSF LED ; invece di BSF PORTB, 0
BTFSC PULSANTE ; invece di BTFSC PORTB, 7
; Feature flag: DEBUG attivo/inattivo
#DEFINE DEBUG_MODE 1
⑥ LIST, __CONFIG, END — metadata del file
| Direttiva | Sintassi | Scopo | Nel simulatore |
|---|---|---|---|
LIST | LIST p=16F84A | Specifica il processore target | Ignorata (device scelto nel wizard) |
__CONFIG | __CONFIG _XT_OSC & _WDT_OFF | Fuse bits: oscillatore, WDT, protezione | Ignorata (configurata separatamente) |
END | END | Marca la fine del file sorgente | Ignorata (l’assembler finisce al termine del testo) |
#include | #include <p16f84a.inc> | Include file con definizioni SFR | Ignorata (built-in) |
✅ Includile comunque. Anche se il simulatore le ignora, inserire LIST, __CONFIG e END rende il file compatibile con MPLAB/MPLABX per chi vuole usare lo stesso sorgente su toolchain reali. È una questione di portabilità, non di funzionamento nel simulatore.
📄 Il template completo — copia e usa
Questo è il template che trovi già nei nuovi progetti del simulatore. Ogni sezione ha il suo posto preciso. Da ora in poi, tutti i tuoi programmi partiranno da questa struttura.
; ============================================================
; SEZIONE 1 — HEADER
; Titolo, autore, data, descrizione del progetto
; ============================================================
; File : nome_progetto.asm
; Autore : Prof. D. Bertolino
; Data : 2025-01-01
; Device : PIC16F84A @ 4 MHz
; Scopo : Descrizione del programma
; ============================================================
; SEZIONE 2 — METADATA ASSEMBLER
LIST p=16F84A
#include <p16f84a.inc>
__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF
; SEZIONE 3 — COSTANTI EQU (se non già in .inc)
FOSC EQU 4000000 ; frequenza oscillatore Hz
LED EQU 0 ; RB0 = LED
BTN EQU 7 ; RB7 = pulsante
; SEZIONE 4 — VARIABILI RAM (CBLOCK)
CBLOCK 0x0C
DELAY_HI ; → 0x0C
DELAY_LO ; → 0x0D
FLAGS ; → 0x0E bit-flags software
ENDC
; SEZIONE 5 — VETTORI
ORG 0x0000 ; Reset vector
GOTO START
ORG 0x0004 ; Interrupt vector
RETFIE ; (o GOTO ISR se usi interrupt)
; SEZIONE 6 — INIZIALIZZAZIONE (eseguita una volta)
START:
BSF STATUS, RP0 ; Bank 1
MOVLW 0x80 ; RB7=input, RB0-6=output
MOVWF TRISB
CLRF TRISA ; PORTA tutta output
BCF STATUS, RP0 ; Bank 0
CLRF PORTB ; output a 0
CLRF PORTA
; SEZIONE 7 — LOOP PRINCIPALE
MAIN:
; il tuo codice qui
GOTO MAIN
; SEZIONE 8 — SUBROUTINE
; (tutte le CALL-able qui sotto)
; SEZIONE 9 — LOOKUP TABLE (se presenti)
; (usare ORG per allineare a pagina pulita)
END ; fine file
🏋️ Prova tu!
- Apri un nuovo progetto nel simulatore. Osserva il template generato automaticamente: identifica le 9 sezioni descritte sopra. Quante variabili CBLOCK contiene? Dove si trovano in RAM?
- Aggiungi una variabile
STATO_LEDal CBLOCK esistente. Qual è il suo indirizzo? Verifica che le variabili successive si siano spostate di 1 ciascuna. Ora aggiungi unEQU SOGLIA 100per una soglia di confronto. - Sfida: riscrivi il programma blink della lezione 08 usando il template completo: header commentato, costanti EQU per LED_BIT e BTN_BIT, CBLOCK con nomi significativi, le 9 sezioni nell’ordine corretto. Il codice funzionale deve essere identico, ma il sorgente deve essere leggibile da qualcuno che non lo ha mai visto.
❓ Quiz rapido
1. DELAY_HI EQU 0x0C alloca RAM per DELAY_HI?
Mostra risposta
No. EQU è una sostituzione testuale — dice all’assembler “ogni volta che vedi DELAY_HI, usa 0x0C”. La RAM a 0x0C esiste già fisicamente nel PIC, EQU le dà solo un nome. Non c’è allocazione, non c’è inizializzazione, non c’è nulla di generato nel .hex. La RAM GPR è sempre tutta disponibile; sta al programmatore non usare lo stesso indirizzo per due variabili diverse.
2. Perché il GOTO START a 0x000 è necessario se il tuo START è a 0x005?
Mostra risposta
Perché all’accensione (o dopo un reset) la CPU parte sempre da 0x000. Se non c’è un GOTO, eseguirebbe l’istruzione a 0x000, poi 0x001, 0x002, 0x003 — che sono le istruzioni destinate al vettore interrupt a 0x004. Il GOTO a 0x000 salta oltre 0x004 direttamente al codice di inizializzazione. Questo lascia 0x004 libero e isolato per il vettore interrupt.
3. Qual è la differenza pratica tra DT 0,1,4,9 e quattro righe RETLW 0 / RETLW 1 / RETLW 4 / RETLW 9?
Mostra risposta
Nessuna differenza nel .hex generato: DT è zucchero sintattico che l’assembler traduce automaticamente in una serie di RETLW. Il codice macchina prodotto è identico bit per bit. La differenza è solo nella leggibilità e nella compattezza del sorgente: con DT una lookup table da 16 valori occupa 2 righe invece di 17.
4. Hai questo CBLOCK: CBLOCK 0x0C / A / B / C / ENDC. Aggiungi una variabile X tra A e B. Quali sono i nuovi indirizzi di B e C?
Mostra risposta
Dopo la modifica: A=0x0C, X=0x0D, B=0x0E, C=0x0F. Prima dell’aggiunta erano B=0x0D, C=0x0E. Questo è sia il vantaggio che il pericolo di CBLOCK: gli indirizzi si spostano automaticamente (nessun rinumerare manuale), ma se hai salvato dati in RAM prima di ricompilare e aggiungi variabili nel mezzo, i dati già in memoria non corrispondono più alle variabili. In fase di sviluppo non è un problema — al reset la RAM è comunque indefinita.
Fine Modulo 1 — e adesso? 🚀
Hai completato il Modulo 1 — Assembly Base. Conosci tutte le 35 istruzioni PIC16, lo stack, le subroutine e come strutturare un file sorgente.
Modulo 2 — I/O Digitale: PORTA, PORTB, TRISA, TRISB in dettaglio. Lettura di pulsanti, debounce, multiplexing LED, display 7 segmenti.
© Prof. D. Bertolino · PIC16 From Zero to Hero · WebPicSimulator · Lezione 10