Lezione 36 di 43

Lezione 35

🎓 PIC16 — From Zero to Hero · Lezione 35 · Modulo 6

RAM a Pagine, Puntatori e Buffer 🗂️

Indirizzamento indiretto con FSR e INDF. Array, ricerca e buffer circolari.
IRP per accedere ai banchi 2 e 3. Stack software a 8 livelli nell’area shared.

🎯 Cosa impari in questa lezione

  • FSR (File Select Register) come puntatore a 8 bit + IRP (bit STATUS) per il 9° bit → 512 locazioni indirizzabili
  • INDF: accede alla RAM puntata da FSR — leggere/scrivere INDF equivale a dereferenziare un puntatore C
  • Array in RAM: riempimento, somma e ricerca del massimo con loop FSR/INDF senza indici fissi
  • Buffer circolare (ring buffer): HEAD per scrittura ISR, TAIL per lettura main, wrap-around via AND 0x0F
  • Sezione critica: BCF GIE prima di aggiornare TAIL/COUNT, BSF GIE dopo — evita race condition con ISR
  • Banchi 2 e 3 con IRP=1: 256 byte extra per buffer grandi (0x110–0x18F e 0x190–0x1EF)
  • Stack software LIFO a 8 livelli nell’area shared (0x70–0x77): PUSH/POP con FSR, overflow e underflow detection

📋 Scheda lezione

Lezione: 35 / 42
Modulo: 6 — Tecniche avanzate
Device: PIC16F877A
Prerequisiti: Lez. 04 (banchi RAM), Lez. 21 (ISR)
Difficoltà: ★★★★☆
Tecnica: FSR/INDF + IRP, buffer circolare
Applicazioni: UART buffering, stack, array


1 · La mappa RAM e l’indirizzamento indiretto

Il PIC16F877A ha 368 byte di RAM organizzati in 4 banchi da 128 locazioni ciascuno. L’accesso diretto tramite istruzioni come MOVF 0x25,W usa un indirizzo a 7 bit embeddato nell’opcode — limitato al banco selezionato da RP0/RP1. L’accesso indiretto tramite FSR e INDF usa invece 9 bit (IRP + FSR a 8 bit) e può raggiungere qualsiasi delle 512 locazioni senza cambiare banco. È lo strumento fondamentale per loop su array, buffer e strutture dati dinamiche.

Mappa RAM PIC16F877A — 4 banchi × 128 byte = 512 locazioni Bank 0 (IRP=0, RP0=0) 0x00 SFR (INDF,TMR0…) 0x00-0x0B SFR Bank0 0x20-0x6F GPR (80 byte) variabili utente ARRAY_BASE, BUF, ecc. 0x70-0x7F SHARED (16 byte) visibili da tutti i banchi IRP=0, FSR=0x00-0x7F Bank 1 (IRP=0, RP0=1) 0x80-0x8B SFR Bank1 0xA0-0xEF GPR (80 byte) variabili aggiuntive 0xF0-0xFF SHARED IRP=0, FSR=0x80-0xFF Bank 2 (IRP=1, RP0=0) 0x110-0x16F GPR 128 byte disponibili buffer grandi, array BIG_BUF in questa lezione IRP=1, FSR=0x10-0x6F Bank 3 (IRP=1, RP0=1) 0x190-0x1EF GPR 128 byte disponibili IRP=1, FSR=0x90-0xEF IRP:FSR → 9 bit → 512 locazioni — INDF legge/scrive *FSR senza usare opcodes indiretti

2 · Codice — array con FSR/INDF

🔑 Analogia C ↔ Assembly: MOVWF FSR è come ptr = &array[0]. MOVWF INDF è come *ptr = valore. INCF FSR,F è come ptr++. Il loop di riempimento in assembly è esattamente equivalente a for(i=0; i<16; i++) array[i] = i; — solo espresso con istruzioni a 8 bit.

; ============================================================
; Lezione 35 — Indirizzamento indiretto: FSR e INDF
; PIC16F877A @ 4 MHz
; FSR (File Select Register, 0x04) = puntatore a RAM
; INDF (0x00) = accesso al registro puntato da FSR
; IRP (bit7 di STATUS) = seleziona banco 0/1 (IRP=0)
;                        o banco 2/3 (IRP=1)
; Indirizzo fisico = IRP:FSR (9 bit totali → 512 locazioni)
; Esempio: riempire array di 16 byte con valori 0-15
; ============================================================
    LIST    P=16F877A
    #INCLUDE <p16f877a.inc>
    __CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BOREN_OFF & _LVP_OFF

    CBLOCK  0x20
ARRAY_BASE      ; 16 byte: 0x20-0x2F
    ENDC        ; (CBLOCK alloca solo la prima)
; Le 15 locazioni successive sono 0x21..0x2F

    CBLOCK  0x30
LOOP_CNT
VALUE
PTR_SAVE        ; salva FSR se serve
    ENDC

    ORG     0x0000
    GOTO    INIT

    ORG     0x0020
INIT
    BSF     STATUS,RP0
    CLRF    TRISB
    BCF     STATUS,RP0
    BCF     STATUS,IRP         ; IRP=0: banchi 0/1 (0x00-0xFF)

; ============================================================
; Riempimento array: ARRAY[0..15] = 0..15
; Tecnica: FSR punta alla base, incrementa FSR ad ogni passo
; ============================================================
    MOVLW   ARRAY_BASE         ; FSR = indirizzo base array
    MOVWF   FSR
    CLRF    VALUE              ; valore da scrivere
    MOVLW   D'16'
    MOVWF   LOOP_CNT

FILL_LOOP
    MOVF    VALUE,W
    MOVWF   INDF               ; scrivi in *FSR
    INCF    FSR,F              ; puntatore++
    INCF    VALUE,F            ; valore++
    DECFSZ  LOOP_CNT,F
    GOTO    FILL_LOOP
    ; Ora 0x20=0, 0x21=1, ... 0x2F=15

; ============================================================
; Calcola somma array (dovrebbe essere 0+1+...+15 = 120)
; ============================================================
    MOVLW   ARRAY_BASE
    MOVWF   FSR
    CLRF    VALUE              ; useremo VALUE come accumulatore
    MOVLW   D'16'
    MOVWF   LOOP_CNT

SUM_LOOP
    MOVF    INDF,W             ; leggi *FSR
    ADDWF   VALUE,F            ; accumula
    INCF    FSR,F
    DECFSZ  LOOP_CNT,F
    GOTO    SUM_LOOP
    ; VALUE = 120 = 0x78

    MOVF    VALUE,W
    MOVWF   PORTB              ; mostra somma (0x78)

; ============================================================
; Ricerca massimo nell'array
; ============================================================
    MOVLW   ARRAY_BASE
    MOVWF   FSR
    MOVF    INDF,W             ; primo elemento come massimo iniziale
    MOVWF   VALUE
    INCF    FSR,F
    MOVLW   D'15'
    MOVWF   LOOP_CNT

MAX_LOOP
    MOVF    INDF,W
    SUBWF   VALUE,W            ; VALUE - *FSR
    BTFSS   STATUS,C           ; C=0 → *FSR &gt; VALUE
    MOVF    INDF,W             ; nuovo massimo
    BTFSS   STATUS,C
    MOVWF   VALUE
    INCF    FSR,F
    DECFSZ  LOOP_CNT,F
    GOTO    MAX_LOOP
    ; VALUE = 15 (massimo)

DONE
    MOVF    VALUE,W
    MOVWF   PORTB
    GOTO    DONE

    END

3 · Buffer circolare per UART RX

Il buffer circolare è la struttura dati fondamentale per disaccoppiare produttore e consumatore che lavorano a velocità diverse. La ISR UART (produttore) scrive al ritmo dei byte in arrivo — fino a 9600 byte/s — mentre il main loop (consumatore) li elabora quando può. Senza il buffer, ogni byte che arriva mentre il main loop è occupato andrebbe perso. Con 16 slot e il wrap-around via AND 0x0F, la gestione del puntatore non richiede nessun IF: 15+1=16, 16 AND 0x0F=0, il buffer ricomincia automaticamente.

Buffer circolare (ring buffer) — logica HEAD/TAIL con wrap-around 0x41 [1] 0x42 [2] 0x43 [3] 0x44 — 0x45 0x46 0x47 0x48 0x49 0x4A 0x4B 0x4C 0x4D 0x4E 0x4F 0x4F — TAIL=0 lettura HEAD=3 scrittura BUF_COUNT = HEAD – TAIL = 3 (3 byte in attesa) pieno quando COUNT = BUF_SIZE-1 = 15 Wrap-around via AND HEAD = (HEAD + 1) AND 0x0F 15→0, 0→1, … nessun IF Condizioni Vuoto: HEAD == TAIL (COUNT=0) Pieno: COUNT >= BUF_SIZE-1

⚠️ Sezione critica — race condition: BUF_TAIL e BUF_COUNT sono scritti sia dalla ISR (su BUF_HEAD e BUF_COUNT) sia dal main loop (su BUF_TAIL e BUF_COUNT). Se il main loop viene interrotto a metà dell’aggiornamento di BUF_COUNT, la ISR potrebbe trovare un valore incoerente. Soluzione: BCF INTCON,GIE prima di modificare la variabile condivisa, BSF INTCON,GIE dopo. La finestra protetta deve essere minima (2–3 istruzioni) per non perdere dati in ingresso.

; ============================================================
; Lezione 35 — Buffer circolare (ring buffer) per UART RX
; PIC16F877A @ 4 MHz, 9600 baud
; Buffer: 16 byte in RAM, puntatori HEAD (scrittura) e TAIL (lettura)
; ISR UART scrive in HEAD, main loop legge da TAIL
; Full quando (HEAD+1) mod 16 == TAIL
; Empty quando HEAD == TAIL
; ============================================================
    LIST    P=16F877A
    #INCLUDE <p16f877a.inc>
    __CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BOREN_OFF & _LVP_OFF

#DEFINE BUF_SIZE  D'16'
#DEFINE BUF_MASK  0x0F         ; maschera per wrap-around (size-1)
#DEFINE BUF_BASE  0x40         ; indirizzo base buffer in RAM

    CBLOCK  0x30
W_SAVE
STATUS_SAVE
BUF_HEAD        ; indice scrittura ISR (0-15)
BUF_TAIL        ; indice lettura main (0-15)
BUF_COUNT       ; numero byte presenti
RX_CHAR         ; carattere letto dal buffer
TX_CHAR
ECHO_FLAG       ; 1 = fai echo del carattere ricevuto
    ENDC

    ORG     0x0000
    GOTO    INIT

    ORG     0x0004
; ============================================================
; ISR — UART RX: salva in buffer circolare
; ============================================================
ISR
    MOVWF   W_SAVE
    SWAPF   STATUS,W
    MOVWF   STATUS_SAVE

    BCF     STATUS,RP0         ; Bank 0
    BTFSS   PIR1,RCIF          ; RX interrupt?
    GOTO    ISR_EXIT

    ; Controlla se buffer pieno
    MOVF    BUF_COUNT,W
    SUBLW   BUF_SIZE - 1       ; BUF_SIZE-1 - COUNT
    BTFSS   STATUS,C           ; C=0 → COUNT &gt;= BUF_SIZE-1 → pieno
    GOTO    BUF_FULL

    ; Scrivi in BUF[HEAD]
    BCF     STATUS,IRP         ; Bank 0/1
    MOVLW   BUF_BASE
    ADDWF   BUF_HEAD,W         ; BUF_BASE + HEAD
    MOVWF   FSR
    MOVF    RCREG,W            ; leggi byte ricevuto (azzera RCIF)
    MOVWF   INDF               ; salva nel buffer

    ; Avanza HEAD con wrap
    INCF    BUF_HEAD,F
    MOVF    BUF_HEAD,W
    ANDLW   BUF_MASK           ; mod 16
    MOVWF   BUF_HEAD

    INCF    BUF_COUNT,F
    GOTO    ISR_EXIT

BUF_FULL
    MOVF    RCREG,W            ; leggi comunque per azzerare RCIF
    ; carattere scartato (buffer overflow)

ISR_EXIT
    SWAPF   STATUS_SAVE,W
    MOVWF   STATUS
    SWAPF   W_SAVE,F
    SWAPF   W_SAVE,W
    RETFIE

    ORG     0x0060
INIT
    BSF     STATUS,RP0
    MOVLW   b'00000100'        ; RC2=input, resto output
    MOVWF   TRISC
    MOVLW   D'25'
    MOVWF   SPBRG              ; 9600 baud @ 4MHz
    MOVLW   b'00100100'        ; TXEN=1, BRGH=1
    MOVWF   TXSTA
    MOVLW   b'00100000'        ; RCIE=1 (bit5 PIE1)
    MOVWF   PIE1
    BCF     STATUS,RP0

    MOVLW   b'10010000'        ; SPEN=1, CREN=1
    MOVWF   RCSTA

    CLRF    BUF_HEAD
    CLRF    BUF_TAIL
    CLRF    BUF_COUNT
    MOVLW   1
    MOVWF   ECHO_FLAG

    MOVLW   b'11000000'        ; GIE=1, PEIE=1
    MOVWF   INTCON

; ============================================================
; MAIN — elabora caratteri dal buffer
; ============================================================
MAIN
    ; Buffer vuoto?
    MOVF    BUF_COUNT,F
    BTFSC   STATUS,Z
    GOTO    MAIN               ; empty → attendi

    ; Leggi da BUF[TAIL]
    BCF     STATUS,IRP
    MOVLW   BUF_BASE
    ADDWF   BUF_TAIL,W
    MOVWF   FSR
    MOVF    INDF,W
    MOVWF   RX_CHAR

    ; Avanza TAIL con wrap (disabilita interrupt per atomicità)
    BCF     INTCON,GIE
    INCF    BUF_TAIL,F
    MOVF    BUF_TAIL,W
    ANDLW   BUF_MASK
    MOVWF   BUF_TAIL
    DECF    BUF_COUNT,F
    BSF     INTCON,GIE

    ; Elabora: se ECHO_FLAG, ritrasmettti il carattere
    BTFSS   ECHO_FLAG,0
    GOTO    PROCESS_CHAR
    MOVF    RX_CHAR,W
    CALL    UART_TX

PROCESS_CHAR
    ; Azione base: se ricevuto 'L', accendi PORTB
    MOVF    RX_CHAR,W
    SUBLW   'L'
    BTFSC   STATUS,Z
    BSF     PORTB,0
    ; Se ricevuto 'O' (off), spegni
    MOVF    RX_CHAR,W
    SUBLW   'O'
    BTFSC   STATUS,Z
    BCF     PORTB,0

    GOTO    MAIN

UART_TX
    MOVWF   TX_CHAR
    BSF     STATUS,RP0
UTX BTFSS   TXSTA,TRMT
    GOTO    UTX
    BCF     STATUS,RP0
    MOVF    TX_CHAR,W
    MOVWF   TXREG
    RETURN

    END

4 · Stack software e IRP per banchi 2/3

Stack software a 8 livelli — PUSH/POP tramite FSR e area shared Stack (0x70-0x77) 0x77 — (SP=8, mai usato) 0x76 — 0x75 — 0x74 — 0x72 7 ← SP=2 (TOP) 0x71 3 0x70 (base) già letto SP=2 PUSH W (salva valore) MOVLW STACK_BASE ADDWF SP,W ; STACK_BASE + SP → FSR MOVWF FSR ; poi MOVWF INDF ; INCF SP (3 istruzioni chiave) POP → W (recupera valore) DECF SP,F ; SP– prima di leggere MOVLW STACK_BASE ADDWF SP,W ; MOVWF FSR ; MOVF INDF,W (stack LIFO: DECF prima, poi leggi) Area 0x70-0x7F è SHARED: visibile da tutti i banchi senza cambiare RP0/RP1 — ideale per stack e variabili ISR
; ============================================================
; Lezione 35 — Stack software e IRP: accesso ai banchi 2/3
; PIC16F877A @ 4 MHz
; Stack software: 8 livelli in RAM (0x70-0x77, bank0 shared)
; Operazioni: PUSH W, POP → W
; Uso IRP: buffer da 128 byte nel banco 2 (0x110-0x18F)
; ============================================================
    LIST    P=16F877A
    #INCLUDE <p16f877a.inc>
    __CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BOREN_OFF & _LVP_OFF

; Stack in area shared (banchi 0-3 tutti vedono 0x70-0x7F)
#DEFINE STACK_BASE  0x70
#DEFINE STACK_SIZE  D'8'

    CBLOCK  0x20
SP              ; stack pointer (0=vuoto, 8=pieno)
STACK_TMP       ; temp per push/pop
; Buffer grande banco 2 (dichiarato qui come indirizzo)
; BIG_BUF_BASE = 0x110 (bank 2, IRP=1, FSR=0x10)
BIG_IDX         ; indice nel buffer grande
BIG_LEN         ; lunghezza dati nel buffer
CALC_A
CALC_B
    ENDC

    ORG     0x0000
    GOTO    INIT

    ORG     0x0030
INIT
    BSF     STATUS,RP0
    CLRF    TRISB
    BCF     STATUS,RP0
    CLRF    SP                 ; stack vuoto

; ============================================================
; Demo stack: calcola (3 + 4) × 2 con stack
; ============================================================
    ; PUSH 3
    MOVLW   D'3'
    CALL    STACK_PUSH
    ; PUSH 4
    MOVLW   D'4'
    CALL    STACK_PUSH
    ; POP → CALC_B, POP → CALC_A, somma, PUSH risultato
    CALL    STACK_POP
    MOVWF   CALC_B
    CALL    STACK_POP
    MOVWF   CALC_A
    MOVF    CALC_B,W
    ADDWF   CALC_A,W           ; 3 + 4 = 7
    CALL    STACK_PUSH         ; PUSH 7
    ; PUSH 2
    MOVLW   D'2'
    CALL    STACK_PUSH
    ; POP → CALC_B, POP → CALC_A, moltiplica semplice
    CALL    STACK_POP
    MOVWF   CALC_B             ; 2
    CALL    STACK_POP
    MOVWF   CALC_A             ; 7
    ; Moltiplicazione semplice: loop di somme
    CLRF    STACK_TMP
MUL_LOOP
    MOVF    CALC_A,W
    ADDWF   STACK_TMP,F        ; STACK_TMP += CALC_A
    DECFSZ  CALC_B,F           ; conta CALC_B volte
    GOTO    MUL_LOOP
    ; STACK_TMP = 7 × 2 = 14
    MOVF    STACK_TMP,W
    MOVWF   PORTB              ; mostra 14

; ============================================================
; Demo IRP: riempimento buffer nel banco 2 (0x110-0x18F)
; Indirizzo banco 2: FSR=0x10..0x8F, IRP=1
; ============================================================
    BSF     STATUS,IRP         ; seleziona banchi 2/3
    MOVLW   0x10               ; FSR = 0x10 → indirizzo fisico 0x110
    MOVWF   FSR
    MOVLW   D'128'
    MOVWF   BIG_LEN
    CLRF    BIG_IDX

FILL_BIG
    MOVF    BIG_IDX,W
    MOVWF   INDF               ; BIG_BUF[i] = i
    INCF    FSR,F
    INCF    BIG_IDX,F
    DECFSZ  BIG_LEN,F
    GOTO    FILL_BIG

    ; Torna a banco 0/1
    BCF     STATUS,IRP

    ; Leggi back: BIG_BUF[64] deve valere 64
    BSF     STATUS,IRP
    MOVLW   0x10 + D'64'       ; FSR = 0x50 → 0x150 (indice 64)
    MOVWF   FSR
    MOVF    INDF,W             ; leggi valore
    BCF     STATUS,IRP
    MOVWF   PORTB              ; deve mostrare 64

MAIN
    GOTO    MAIN

; ============================================================
; STACK_PUSH — salva W nello stack, incrementa SP
; Se stack pieno, non fa nulla (overflow silenzioso)
; ============================================================
STACK_PUSH
    MOVWF   STACK_TMP
    ; Controlla overflow
    MOVF    SP,W
    SUBLW   STACK_SIZE - 1
    BTFSS   STATUS,C           ; C=0 → SP &gt;= STACK_SIZE
    RETURN                     ; overflow: ignora
    ; Calcola indirizzo: STACK_BASE + SP
    MOVLW   STACK_BASE
    ADDWF   SP,W
    MOVWF   FSR
    MOVF    STACK_TMP,W
    MOVWF   INDF               ; salva valore
    INCF    SP,F               ; SP++
    RETURN

; ============================================================
; STACK_POP — carica W dallo stack, decrementa SP
; Se stack vuoto, restituisce 0xFF (underflow marker)
; ============================================================
STACK_POP
    ; Controlla underflow
    MOVF    SP,F
    BTFSC   STATUS,Z           ; Z=1 → SP=0 → vuoto
    GOTO    STACK_UNDERFLOW
    DECF    SP,F               ; SP--
    MOVLW   STACK_BASE
    ADDWF   SP,W
    MOVWF   FSR
    MOVF    INDF,W             ; carica valore
    RETURN
STACK_UNDERFLOW
    MOVLW   0xFF               ; marker underflow
    RETURN

    END

🧪 Prova tu

  1. Carica il codice array. Dopo il loop di riempimento, ispeziona la RAM: 0x20=0, 0x21=1, …, 0x2F=15. Verifica che VALUE=120 (0x78) dopo la somma — controlla su PORTB. Poi modifica il loop: scrivi valori decrescenti (ARRAY[0]=15, ARRAY[1]=14, …) e verifica che il massimo rimanga 15.
  2. Carica il buffer circolare. Simula l’arrivo di 3 byte via UART (imposta RCREG=0x41, 0x42, 0x43 e triggera RCIF manualmente). Verifica BUF_HEAD=3, BUF_COUNT=3, BUF_TAIL=0. Il main loop deve leggere 0x41 dal buffer, avanzare TAIL a 1, inviare 0x41 via UART (echo). Ripeti per gli altri due byte.
  3. Carica lo stack. Fai PUSH di 5, 10, 20. SP deve valere 3. POP: ottieni 20 (LIFO), SP=2. Poi POP: 10, SP=1. Poi POP: 5, SP=0. Poi ancora POP: deve restituire 0xFF (underflow) senza crash. Testa anche overflow: fai 9 PUSH → l’ottavo viene ignorato silenziosamente.
  4. Sfida: implementa memcpy in assembly. Funzione MEMCPY con parametri: SRC_PTR (FSR sorgente), DST_PTR in una seconda locazione, LEN. Copia LEN byte da *SRC a *DST usando FSR per entrambi — ma FSR è uno solo! Strategia: copia un byte alla volta salvando il valore in W tra le due operazioni di puntatore, o usa l’area shared come buffer intermedio di 1 byte.

❓ Quiz rapido

1. Cosa succede se si legge INDF quando FSR=0x00 (INDF stesso)?

Mostra risposta

INDF è un registro speciale che implementa l’indirizzamento indiretto. Se FSR=0x00, punta ad INDF stesso — creando un accesso ricorsivo. Il comportamento è definito dal datasheet: leggere INDF quando FSR=0 restituisce 0x00 (non il contenuto di FSR). Scrivere in INDF quando FSR=0 non fa nulla. Questo è un caso limite da evitare: mai usare 0x00 come indirizzo base di un array. Un’altra trappola: se FSR punta a 0x00 accidentalmente (es. dopo un CLRF FSR sbagliato), tutte le scritture via INDF vengono silenziosamente scartate — un bug difficile da trovare.


2. Nel buffer circolare, perché si usa BUF_SIZE=16 invece di 15 o 17, e perché la condizione “pieno” è COUNT >= BUF_SIZE-1 (non BUF_SIZE)?

Mostra risposta

BUF_SIZE=16 (potenza di 2) permette il wrap-around tramite AND senza divisione: (idx+1) AND 0x0F funziona perfettamente per size=16. Con size=15 o 17 servirebbe una SUBWF con confronto, molto più lenta. La condizione “pieno” a BUF_SIZE-1 (=15) invece di BUF_SIZE (=16) serve per lasciare sempre uno slot libero: quando HEAD e TAIL coincidono il buffer è vuoto (COUNT=0). Se si riempisse fino all’ultimo slot, non si potrebbe distinguere tra “vuoto” e “pieno” usando solo HEAD==TAIL. Sacrificare uno slot elimina questa ambiguità senza introdurre flag aggiuntivi.


3. L’area shared (0x70–0x7F) è visibile da tutti i banchi senza cambiare RP0/RP1. Quali sono i vantaggi e i rischi di usarla per lo stack?

Mostra risposta

Il vantaggio principale è che lo stack è sempre accessibile dalla ISR senza dover salvare e ripristinare RP0/RP1 — il risparmio è 2 istruzioni per push e 2 per pop nell’ISR, cruciale quando la latenza conta. Anche il codice in Bank 1 (con RP0=1) può fare push/pop senza cambiare banco. I rischi: l’area shared è piccola (solo 16 byte), quindi lo stack è limitato a 8 livelli. Se il codice usa anche altre variabili nell’area shared (comuni per W_SAVE e STATUS_SAVE nella ISR), i 16 byte si saturano in fretta. Bisogna pianificare attentamente: tipicamente 2 byte per W_SAVE/STATUS_SAVE, 8 per lo stack, 6 rimasti per altre variabili ISR.

Prossima lezione →

Lezione 36 — Ottimizzazione e profiling del codice
Contare i cicli macchina, ridurre la latenza ISR, ottimizzare loop critici. Bank switching efficiente, uso dei registri shadow, inline vs subroutine.

© Prof. D. Bertolino · PIC16 From Zero to Hero · WebPicSimulator · Lezione 35