Salta ai contenuti

UDA 3 — Esempio di prova scritta n. 1

Durata: 1 ora · Punteggio: 10 punti · Argomenti: UDA 1-3 (istruzioni e flag, controllo del flusso, memoria, stack, sottoprogrammi)

Nota per il docente: questa è una prova di esempio (con soluzioni richiudibili in fondo). Per la classe si consiglia la consueta prassi: varianti con valori diversi.


Dato il listato:

ld sp, 0xFFFF
ld a, 0x3C
ld b, 0x0A
sub b
push af
pop bc
halt

A1. Completa la tabella (valori esadecimali; usa ”—” quando il registro non cambia):

IstruzioneABCSP
ld sp, 0xFFFF
ld a, 0x3C
ld b, 0x0A
sub b
push af
pop bc

A2. Dopo push af, che cosa contiene la cella 0xFFFE? E la cella 0xFFFD?

A3. Quali flag documentati (S Z H V N C) si attivano con sub b?

A4. Perché SP passa da 0xFFFF a 0xFFFD con un solo push af?


B1. Copia 20 byte da 0x0800 a 0x0900 usando LDIR:

ld hl, ______
ld de, ______
ld bc, ______
______

B2. Scrivi la subroutine somma che riceve due valori in A e B e restituisce la somma in A, senza alterare B:

somma:
; ...
ret

B3. Nel programma principale, chiama somma con A = 5 e B = 7 e metti il risultato in C:

ld a, 5
ld b, 7
; ...

C1. Cos’è lo stack? Che cosa indica SP e in quale direzione “cresce”?

C2. Che cosa mette esattamente sullo stack l’istruzione CALL? E che cosa fa RET?

C3. Una subroutine usa A come registro di lavoro e restituisce il risultato in A. Il chiamante ha in B un dato che gli serve dopo la chiamata: chi deve salvarlo, e come?

C4. Descrivi i passi di LDIR a ogni iterazione e quanto vale BC alla fine.

C5. Perché può servire LDDR al posto di LDIR? Fai un esempio concreto.


Soluzioni (per il docente)

A1.

IstruzioneABCSP
ld sp, 0xFFFF———FFFF
ld a, 0x3C3C——FFFF
ld b, 0x0A3C0A—FFFF
sub b320A—FFFF
push af320A—FFFD
pop bc323222FFFF

A2. 0xFFFE = 0x32 (il valore di A); 0xFFFD = 0x22 (il byte dei flag dopo sub b). Nota: 0x22 = flag N=1 più il bit 5 interno che il simulatore mostra ma che non si considera; i flag documentati attivi sono solo N.

A3. Solo N = 1 (è una sottrazione): S = Z = H = V = C = 0.

A4. Perché PUSH scrive due byte (prima A, poi F): ogni byte decrementa SP di 1, quindi SP scende di 2 (da FFFF a FFFD).

B1.

ld hl, 0x0800
ld de, 0x0900
ld bc, 20
ldir

B2.

somma:
add a, b ; ADD A,B non modifica B
ret

B3.

ld a, 5
ld b, 7
call somma ; A = 12
ld c, a ; risultato in C

C1. Zona di memoria gestita LIFO (ultimo entrato, primo uscito); SP punta alla cima; cresce verso il basso (le PUSH decrementano SP).

C2. CALL impila l’indirizzo di ritorno (l’istruzione da cui riprendere) e salta al sottoprogramma; RET estrae quell’indirizzo e ci salta.

C3. Il chiamante: deve salvare B prima della chiamata (push bc) e ripristinarlo dopo (pop bc), perché la subroutine non ha obblighi verso i registri del chiamante. (In alternativa: se il contratto della subroutine prevede “preservo B”, è lei a salvarlo.)

C4. Copia il byte (HL) in (DE); incrementa HL e DE; decrementa BC; ripete finché BC ≠ 0. Alla fine BC = 0 (e HL/DE puntano dopo le due zone).

C5. Quando le due zone si sovrappongono e la copia “in avanti” sovrascriverebbe byte non ancora copiati. Esempio: spostare in alto di 2 posizioni un blocco da 4 byte: 0x0800→0x0802 (con LDIR si perderebbero i primi byte).