Lezione 11 di 43

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 specifico
  • EQU — definisce costanti simboliche (registri, bit, valori)
  • CBLOCK / ENDC — alloca variabili in RAM senza contare indirizzi a mano
  • #DEFINE — alias testuali, utile per macro semplici
  • DW / DT / RES — dati diretti in program memory
  • LIST, __CONFIG, END — metadata del progetto
  • La struttura completa di un file .asm professionale

📋 Scheda lezione

Modulo1 — Assembly Base
Lezione10 di 42
Livello⭐⭐ Principiante+
Tempo~30 minuti
Nessuna istruzione CPUSolo direttive assembler
OutputTemplate 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.

Direttiva vs Istruzione — chi la processa? sorgente .asm STATUS EQU 0x03 ORG 0x00 MOVLW 0x05 MOVWF STATUS END ASSEMBLER (2 passate) elaborate dall’assembler file .hex ; EQU, ORG, END ; → NON compaiono 0000: 3005 0001: 0083 CPU PIC16 esegue solo le istruzioni
Figura 1 — Le direttive (viola) vengono consumate dall’assembler e non appaiono nel .hex. Le istruzioni (blu) diventano opcode che la CPU esegue.

① ORG — posiziona il codice in memoria

Sintassi
ORG indirizzo
Effetto
Imposta il program counter dell’assembler a indirizzo. Le istruzioni successive vengono emesse a partire da lì.

ORG 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

Sintassi
SIMBOLO EQU valore
Effetto
Ogni occorrenza di SIMBOLO nel sorgente viene sostituita da valore prima della traduzione. Non alloca RAM né program memory.

EQU è 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

Sintassi
CBLOCK indirizzo
  NOME1
  NOME2
ENDC
Effetto
Assegna automaticamente indirizzi RAM sequenziali a partire da indirizzo. Equivalente a scrivere N direttive EQU consecutive.

CBLOCK (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.

CBLOCK — assegnazione automatica degli indirizzi GPR sorgente CBLOCK 0x0C CONTATORE TEMP DELAY1 ENDC equivale equivalente EQU CONTATORE EQU 0x0C TEMP EQU 0x0D DELAY1 EQU 0x0E generato automaticamente RAM Bank 0 (GPR) 0x0C: CONTATORE 0x0D: TEMP 0x0E: DELAY1 ← ← ←
Figura 2 — CBLOCK alloca automaticamente indirizzi consecutivi a partire da 0x0C (primo byte GPR del Bank 0).

; 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.

DirettivaSintassiGeneraUso tipico
DWDW val1, val2, …Word a 14 bit grezzeDati binari, opcode custom
DTDT val1, val2, …Una RETLW k per ogni valoreLookup table compatta
RESRES nN 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

DirettivaSintassiScopoNel simulatore
LISTLIST p=16F84ASpecifica il processore targetIgnorata (device scelto nel wizard)
__CONFIG__CONFIG _XT_OSC & _WDT_OFFFuse bits: oscillatore, WDT, protezioneIgnorata (configurata separatamente)
ENDENDMarca la fine del file sorgenteIgnorata (l’assembler finisce al termine del testo)
#include#include <p16f84a.inc>Include file con definizioni SFRIgnorata (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!

  1. 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?
  2. Aggiungi una variabile STATO_LED al CBLOCK esistente. Qual è il suo indirizzo? Verifica che le variabili successive si siano spostate di 1 ciascuna. Ora aggiungi un EQU SOGLIA 100 per una soglia di confronto.
  3. 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