UDA 2 — Il controllo del flusso
3 ore (1 di teoria + 2 di laboratorio) · Settimana 4
Obiettivi
Sezione intitolata “Obiettivi”Al termine di questa unità:
- usi i salti incondizionati (
JP) e condizionati (JR/JP+ condizione) leggendo i flag; - sai scegliere la condizione giusta dopo un
CP(minore / uguale / maggiore); - costruisci cicli con
DJNZe con contatori a 16 bit (BC); - traduci if/else, while e for dal pseudo-codice all’assembly;
- hai completato l’Esercizio 2 e sai spiegare la pausa dell’esempio
03-led-contatore.
1. Il flusso non è (solo) sequenziale
Sezione intitolata “1. Il flusso non è (solo) sequenziale”Finora i programmi eseguivano le istruzioni in ordine. Il registro PC punta alla prossima
istruzione e normalmente avanza da solo… ma noi possiamo cambiarlo:
| Tipo di salto | Istruzione | Effetto |
|---|---|---|
| incondizionato | jp etichetta / jr etichetta | salta sempre |
| condizionato | jr z, etichetta / jp nc, etichetta… | salta solo se la condizione è vera |
I salti leggono i flag (UDA 1) e non li modificano: questa è la chiave per concatenare confronti e decisioni.
2. Salti incondizionati: JP e JR
Sezione intitolata “2. Salti incondizionati: JP e JR” jp ciclo ; salta a 16 bit: arriva ovunque (3 byte) jr ciclo ; salto relativo: ±127 byte (2 byte, più compatto)JPcontiene un indirizzo completo: 3 byte, nessun limite di distanza.JRcontiene uno spiazzamento (offset) con segno: 2 byte, ma il bersaglio deve stare entro −128…+127 byte. Comodissimo per i salti vicini (cicli, rami if/else).- Se un
JRè troppo lontano, l’assembler lo segnala: “salto relativo fuori range” — in quel caso si usaJPo si riorganizza il codice.
| Istruzione | Byte | Nota |
|---|---|---|
jp etichetta | C3 lo hi | indirizzo a 16 bit |
jr etichetta | 18 offset | offset con segno |
djnz etichetta | 10 offset | relativo, come jr |
3. Salti condizionati: otto domande da fare alla CPU
Sezione intitolata “3. Salti condizionati: otto domande da fare alla CPU”Ogni condizione corrisponde a un flag (o a una coppia di flag):
| Mnemonico | Salta se… | Da usare dopo… |
|---|---|---|
Z | zero: Z = 1 | CP (uguale), logica (AND/OR/XOR) |
NZ | non zero: Z = 0 | come sopra |
C | carry: C = 1 | CP (primo minore, unsigned), addizioni |
NC | non carry: C = 0 | CP (primo ≥, unsigned) |
M | segno: S = 1 | numeri con segno negativi |
P | positivi: S = 0 | numeri con segno non negativi |
PE | parità pari: V = 1 | dopo operazioni logiche (parità) |
PO | parità dispari: V = 0 | come sopra |
Attenzione: JR esiste solo per Z, NZ, C, NC (le più usate); per le altre serve JP.
JR | Byte | JP | Byte |
|---|---|---|---|
jr z | 28 | jp z | CA lo hi |
jr nz | 20 | jp nz | C2 lo hi |
jr c | 38 | jp c | DA lo hi |
jr nc | 30 | jp nc | D2 lo hi |
| — | — | jp pe / jp po | EA / E2 |
| — | — | jp m / jp p | FA / F2 |
4. La struttura “confronta e salta”
Sezione intitolata “4. La struttura “confronta e salta””CP n esegue la sottrazione A − n senza salvare il risultato, solo per aggiornare i flag.
Con i numeri senza segno (0…255) il flag C dice tutto:
| Vuoi… | Scrivi | Perché |
|---|---|---|
salta se A == n | cp n + jr z, … | differenza zero |
salta se A < n | cp n + jr c, … | carry = “ho dovuto prendere in prestito” |
salta se A ≥ n | cp n + jr nc, … | nessun prestito |
salta se A == 0 | or a + jr z, … | OR di A con se stesso: 1 byte, comodo! |
E il classico if/else:
ld a, (0x0010) cp 0x20 jr nc, grande ; A >= 0x20 ? ld a, 0x01 ; ramo "piccolo" jr finegrande: ld a, 0x02 ; ramo "grande"fine: haltNumeri con segno:
CPconfronta “come se” i numeri fossero senza segno; per i numeri con segno servono le condizioniM/Pcon l’overflow (PE/PO). In questo corso useremo valori 0…255, doveC/NCbastano e avanzano.
5. I cicli
Sezione intitolata “5. I cicli”Il ciclo contato: DJNZ
Sezione intitolata “Il ciclo contato: DJNZ”DJNZ etichetta fa tre cose: decrementa B, e se B ≠ 0 salta; poi (non tocca i flag!).
ld b, 8 ; contatoreciclo: out (0x20), a inc a djnz ciclo ; ripeti 8 volteTrappola: se
Bvale 0, il decremento lo porta a 255… e il ciclo gira 256 volte! È un classico da tenere a mente (e da testare).
Il ciclo con contatore a 16 bit
Sezione intitolata “Il ciclo con contatore a 16 bit”DJNZ conta fino a 255. Per contare di più si usa una coppia (BC, DE, HL) e si testa
“è zero?” con un OR. È esattamente quello che fa la pausa dell’esempio 03-led-contatore:
attesa: ; pausa: consuma cicli push af ; (preserva A e BC del programma principale) push bc ld bc, 0x0800 ; 2048 giriatt1: dec bc ld a, b ; A = byte alto or c ; ...test insieme al byte basso jp nz, att1 ; ripeti finché BC != 0 pop bc pop af retPerché ld a, b + or c? Non esiste un’istruzione “confronta la coppia BC con zero”: si
mette il byte alto in A e si fa l’OR con quello basso — il risultato è zero solo se
entrambi sono zero.
Dal pseudo-codice all’assembly
Sezione intitolata “Dal pseudo-codice all’assembly”| Pseudo-codice | Assembly |
|---|---|
if (A == 0) { … } | or a + jr nz, dopo … dopo: |
if (A < 10) { … } else { … } | cp 10 + jr c, minore … jr fine minore: … fine: |
for (i = 8; i > 0; i--) { … } | ld b, 8 + etichetta + … + djnz |
while (BC != 0) { … ; BC-- } | etichetta + … + dec bc + ld a, b + or c + jp nz |
6. Laboratorio guidato
Sezione intitolata “6. Laboratorio guidato”Attività A — Capire la pausa dell’esempio 03
Sezione intitolata “Attività A — Capire la pausa dell’esempio 03”Apri 03-led-contatore.asm (il contatore sui LED che nella UDA 0 era “scatola nera”) e rispondi
con il Passo sotto gli occhi:
- quanti giri fa il ciclo
att1conBC = 0x0800? (2048) - cosa succede al primo giro a
BC? e quando si esce dal ciclo? - perché nella subroutine ci sono
push af/push bcall’inizio e ipopalla fine?
Consegna: modifica la costante (0x0800 → 0x2000) e descrivi come cambia il conteggio.
Attività B — Esercizio 2 (Massimo di tre)
Sezione intitolata “Attività B — Esercizio 2 (Massimo di tre)”Apri esercizi/02-massimo.asm: il ciclo deve leggere i valori e aggiornare il massimo.
- Verifica: con i valori
3, 9, 5→A = 9; prova anche9, 2, 7→9e1, 2, 3→3. - I tre valori stanno nelle celle
0x0010–0x0012: scrivili a mano in memoria (come nella UDA 1, attività D) oppure usa questa variante con i dati nel sorgente (richiede il segmento assoluto, come l’esempio del timer):
.seg CODE (abs) .org 0x0010valori: .db 3, 9, 5
.org 0x0100 ld a, (0x0010) ; massimo provvisorio ld hl, 0x0011 ld b, 2ciclo: ld c, (hl) ; nuovo valore cp c ; A >= C ? jr nc, avanti ld a, c ; no: nuovo massimoavanti: inc hl djnz ciclo haltCambia i tre valori nel .db e riassembla: il massimo deve seguire i dati.
Attività C — Sfida: il semaforo a due fasi
Sezione intitolata “Attività C — Sfida: il semaforo a due fasi”Realizza un semaforo: prima la fase “verde” (LED 0 acceso) per qualche istante, poi la fase
“rossa” (LED 1 acceso) per lo stesso tempo, in ciclo infinito. Usa un flag di fase in un
registro (es. C), un ciclo contato e la pausa della Attività A.
Mostra una soluzione
ld c, 0 ; fase corrente: 0 = verde, 1 = rossofase: ld a, c or a jr z, verde
; ---- fase rossa ---- ld a, 0x02 ; LED 1 = "rosso" ld b, 8rosso1: out (0x20), a call attesa djnz rosso1 ld c, 0 jr fase
verde: ld a, 0x01 ; LED 0 = "verde" ld b, 8verde1: out (0x20), a call attesa djnz verde1 ld c, 1 jr fase
attesa: ; pausa (preserva A e BC) push af push bc ld b, 0x10rout: ld c, 0x00rin: dec c jr nz, rin djnz rout pop bc pop af retRegola il numero di ripetizioni (ld b, 8) e la costante della pausa per rendere il semaforo
“credibile”.
7. Verifica formativa
Sezione intitolata “7. Verifica formativa”A. Scegli il salto giusto (completa la condizione):
| Obiettivo | Codice |
|---|---|
salta se A è zero | or a + jr ____ , posto |
dopo cp 10, salta se A < 10 | cp 10 + jr ____ , posto |
dopo cp c, salta se A ≥ C (il massimo resta) | cp c + jr ____ , posto |
djnz salta finché B… | ripete finché B ≠ 0 (dopo il decremento) |
B. Traccia i cicli — al termine, quanto vale A?
| Listato | A finale |
|---|---|
ld b, 4 / ld a, 0 / ciclo: inc a / djnz ciclo / halt | |
ld b, 0 / ld a, 0 / ciclo: inc a / djnz ciclo / halt |
Mostra la soluzione
A. 1) jr z · 2) jr c · 3) jr nc · 4) finché B ≠ 0 (attenzione: decrementa prima di
decidere, e con B = 0 conta 256).
B. Primo listato: inc a eseguito 4 volte → A = 4.
Secondo listato: B = 0 → il primo decremento lo porta a 255, quindi il ciclo gira 256
volte: A incrementa da 0 a 255 e poi torna a 0 (con carry) → A = 0.
8. Autoverifica
Sezione intitolata “8. Autoverifica”- Differenza tra
JPeJR? Quando non puoi usareJR? - Dopo un
CP, quale flag guardi per “primo minore” (senza segno)? E per “uguale”? - Cosa fa esattamente
jr nc, avantidopo uncp c? - Perché
DJNZconB = 0è insidioso? - Perché è comodo che i salti non modifichino i flag?
9. Compiti e materiali
Sezione intitolata “9. Compiti e materiali”📎 I file
.asmdi esempi ed esercizi si scaricano dalla pagina Materiali.
- Compito: completa l’Esercizio 2, le due tracce e la tabella “scegli il salto”; porta il semaforo funzionante con una pausa scelta da te.
- Pre-lettura: manuale, sezione Disassemblaggio (per vedere come
JRdiventa18 xx). - Materiali: esempio
03-led-contatore.asm; esercizioesercizi/02-massimo.asm(+ soluzione). - Prossima unità: memoria, stack e sottoprogrammi (
CALL/RET) — UDA 3.