Ambiente di simulazione EMU8086

// obiettivi di apprendimento
Conoscere l’ambiente EMU8086 e le sue funzionalità di simulazione e debug
Distinguere i modelli di memoria .COM e .EXE e scegliere quello appropriato
Strutturare un programma Assembly con direttive standard (4 segmenti) o semplificate
Dichiarare variabili e costanti nel Data Segment usando DB, DW, DD, EQU
Implementare le tre strutture fondamentali (sequenza, selezione, ciclo) in Assembly 8086
📄
Slides
Struttura di un programma Assembly
📚
Risorse
Esercizi svolti .asm
Vedi →

EMU8086 — l’ambiente di simulazione

EMU8086 è un assemblatore e simulatore che emula il microprocessore Intel 8086 (compatibile x86 e AMD). Non è un semplice editor di testo: è un ambiente completo di sviluppo e debug che consente di osservare l’esecuzione del codice Assembly istruzione per istruzione.

🔍 ISPEZIONE
Visualizza il contenuto di tutti i registri (AX, BX, CX, DX, SI, DI, SP, BP, IP, FLAGS) dopo ogni singola istruzione eseguita
💾 MEMORIA
Mostra il contenuto della memoria in esadecimale — è possibile osservare come le variabili vengono scritte e lette nelle locazioni del Data Segment
⚙️ CODICE MACCHINA
Per ogni istruzione Assembly mostra il corrispondente opcode in binario/esadecimale — il ponte diretto tra mnemonico e bit

I programmi che coinvolgono l’I/O aprono automaticamente la finestra del DOS, rendendo interattiva l’esecuzione: è possibile inserire dati da tastiera e visualizzare l’output sullo schermo — esattamente come un programma reale.

// workflow in EMU8086
1. Scrivi il sorgente .asm nell’editor integrato
2. Assembla (Assemble) → genera il file .com o .exe e segnala eventuali errori di sintassi
3. Esegui passo-passo (Single Step) → osserva registri, flag e memoria ad ogni istruzione
4. Esegui in continuo (Run) → il programma gira fino al termine o a un breakpoint

Modelli di memoria — .COM e .EXE

L’8086 gestisce la memoria attraverso segmenti da 64 KB ciascuno. Il formato del file eseguibile determina come questi segmenti vengono organizzati prima dell’esecuzione.

FILE .COM
Modello tiny — segmento unico
Un solo segmento da 64 KB per codice e dati
I primi 256 byte (0x00–0xFF) sono riservati al PSP (Program Segment Prefix)
Il codice inizia obbligatoriamente a 100h
La direttiva ORG 100h imposta IP = 100h
Stack pointer inizializzato a FFFEh (fondo del segmento)
Dimensione massima programma: ~64 KB
; template .COM
ORG 100h

start:
  ; codice qui
  MOV AH, 4Ch
  INT 21h
FILE .EXE
Modello standard — segmenti separati
Segmenti separati per codice, dati, stack ed extra
IP inizializzato a 0000h — il CS punta al code segment
DS deve essere inizializzato esplicitamente via AX
Stack segment dichiarato con dimensione esplicita
Programmi più grandi e strutturati — uso preferito in corso
Richiede intestazione più verbosa ma è più flessibile
; template .EXE (semplificato)
.model small
.stack 100h
.data
  ; variabili
.code
start:
  MOV AX, @data
  MOV DS, AX
  ; codice
  MOV AH, 4Ch
  INT 21h
END start
// perché DS non si può caricare direttamente
L’8086 non permette MOV DS, valore_immediato — i registri di segmento accettano solo un registro generale come sorgente. La sequenza obbligatoria è sempre: MOV AX, data seguito da MOV DS, AX.

Struttura di un programma Assembly

Direttive Standard — 4 segmenti

Le direttive standard prevedono la dichiarazione esplicita di fino a quattro segmenti distinti. È il modello più dettagliato e più vicino al funzionamento reale dell’8086.

DATA
SEGMENT
Variabili e costanti — puntato da DS
STACK
SEGMENT
Area per indirizzi di ritorno — puntato da SS:SP
CODE
SEGMENT
Istruzioni Assembly — puntato da CS:IP
EXTRA
SEGMENT
Area extra — puntato da ES — uso facoltativo
; ── TEMPLATE COMPLETO .EXE CON DIRETTIVE STANDARD ──────────────────────

data SEGMENT                    ; inizio Data Segment
  variabile  DB  42d            ; variabile byte inizializzata
  messaggio  DB  'Ciao!', '$'   ; stringa terminata da $
  risultato  DB  ?              ; variabile byte non inizializzata
  COSTANTE   EQU 10h            ; costante simbolica
data ENDS                       ; fine Data Segment

stack SEGMENT STACK             ; inizio Stack Segment
  DB 100h DUP(?)                ; riserva 256 byte per lo stack
stack ENDS

code SEGMENT                    ; inizio Code Segment
ASSUME CS:code, DS:data, SS:stack

start:                          ; label di inizio programma
  MOV AX, data                  ; carica indirizzo Data Segment in AX
  MOV DS, AX                    ; inizializza DS (obbligatorio)

  ; ── codice del programma ────────────────────────────────────────────
  MOV AL, variabile
  ADD AL, COSTANTE
  MOV risultato, AL

  ; ── fine programma ──────────────────────────────────────────────────
  MOV AH, 4Ch                   ; INT 21h servizio 4Ch: termina programma
  INT 21h

code ENDS
END start                       ; fine file — label di inizio = start

Direttive Semplificate — .model

Le direttive semplificate sostituiscono le dichiarazioni esplicite dei segmenti con macro predefinite. Il codice risulta più compatto — l’assembler gestisce automaticamente i registri di segmento.

ModelloSegmenti codiceSegmenti datiUso tipico
tiny1 (condiviso)1 (condiviso)File .COM — tutto in un segmento
small11Programmi piccoli — il più comune in corso
compact1multipliDati grandi, codice piccolo
mediummultipli1Codice grande, dati piccoli
largemultiplimultipliApplicazioni grandi e complesse

Il Data Segment — variabili e costanti

In Assembly ogni variabile deve essere dichiarata esplicitamente nel Data Segment prima che il codice possa usarla. La dichiarazione specifica nome, dimensione e valore iniziale (oppure ? se non inizializzata).

Direttive di definizione dati

DB
DEFINE BYTE
1 byte
8 bit
0–255
DW
DEFINE WORD
2 byte
16 bit
0–65535
DD
DEFINE DWORD
4 byte
32 bit
DQ
DEFINE QWORD
8 byte
64 bit
DT
DEFINE TBYTE
10 byte
BCD packed
; ── esempi di dichiarazione variabili ────────────────────────────────────

; variabili numeriche
a         DB  67d          ; 1 byte — valore 67 decimale
b         DB  53o          ; 1 byte — valore 53 ottale = 43 decimale
n1        DW  345o         ; 2 byte — valore 345 ottale = 229 decimale
n2        DW  76h          ; 2 byte — valore 76 esadecimale = 118 decimale

; variabili non inizializzate
x         DB  ?            ; 1 byte — valore da definire a runtime
y         DB  ?
ris       DB  ?
somma     DW  ?            ; 2 byte — non inizializzato
differenza DW ?

; costanti — dichiarate con EQU (non occupano memoria)
C         EQU 4h           ; costante = 4 esadecimale
D         EQU 1001b        ; costante = 1001 binario = 9 decimale

; array e stringhe
voti      DB  28d, 30d, 25d, 27d   ; array di 4 byte
buffer    DB  10 DUP(?)             ; 10 byte non inizializzati (buffer)
msg       DB  'Risultato: ', '$'    ; stringa terminata da $ per INT 21h
carattere DB  'A'                   ; singolo carattere ASCII
// basi numeriche in EMU8086
67d o 67 → decimale
43h o 0x43 → esadecimale
53o → ottale
1001b → binario
// EQU vs DB — differenza fondamentale
EQU è una costante simbolica — l’assembler sostituisce il nome con il valore a tempo di assemblaggio. Non occupa memoria. DB/DW è una variabile — occupa locazioni reali nel Data Segment.

Il teorema di Böhm-Jacopini — le tre strutture

Il Teorema di Böhm-Jacopini dimostra che qualsiasi algoritmo computabile può essere espresso usando solo tre strutture di controllo fondamentali. Anche in Assembly — dove tutto è esplicito — si implementano le stesse strutture, ma con istruzioni di confronto e salto.

SEQUENZA
Istruzioni eseguite in ordine lineare — una dopo l’altra
SELEZIONE
Biforcazione condizionale — if, if-else, switch
CICLO
Ripetizione condizionale — while, do-while, for

Esercizio 1 — Sequenza

// problema
Date le variabili a = 67d, b = 53o e le costanti C = 4h, D = 1001b, calcolare:
  1. x = a + D
  2. y = b + C
  3. ris = x - y
; ── ESERCIZIO 1: SEQUENZA ─────────────────────────────────────────────────

data SEGMENT
  a    DB  67d          ; a = 67 decimale
  b    DB  53o          ; b = 53 ottale = 43 decimale
  C    EQU 4h           ; costante C = 4 esadecimale (non occupa memoria)
  D    EQU 1001b        ; costante D = 1001 binario = 9 decimale
  x    DB  ?            ; x = a + D → risultato intermediario
  y    DB  ?            ; y = b + C → risultato intermediario
  ris  DB  ?            ; ris = x - y → risultato finale
data ENDS

stack SEGMENT STACK
  DB 100h DUP(?)
stack ENDS

code SEGMENT
ASSUME CS:code, DS:data, SS:stack

start:
  MOV AX, data          ; inizializza DS
  MOV DS, AX

  ; ── calcolo x = a + D ────────────────────────────────────────────────
  MOV AL, a             ; AL ← a = 67d
  ADD AL, D             ; AL ← 67 + 9 = 76d
  MOV x, AL             ; x ← 76d

  ; ── calcolo y = b + C ────────────────────────────────────────────────
  MOV AL, b             ; AL ← b = 43d (53 ottale)
  ADD AL, C             ; AL ← 43 + 4 = 47d
  MOV y, AL             ; y ← 47d

  ; ── calcolo ris = x - y ──────────────────────────────────────────────
  MOV AL, x             ; AL ← x = 76d
  SUB AL, y             ; AL ← 76 - 47 = 29d
  MOV ris, AL           ; ris ← 29d = 1Dh

  MOV AH, 4Ch           ; termina programma
  INT 21h
code ENDS
END start

Esercizio 2 — Selezione (if-else)

// problema
Dati n1 = 345o e n2 = 76h: se n1 > n2 calcola la somma e memorizzala in somma, altrimenti calcola la differenza e memorizzala in differenza.
// conversione basi — verifica manuale
n1 = 345o → 3×64 + 4×8 + 5×1 = 192+32+5 = 229d
n2 = 76h → 7×16 + 6×1 = 112+6 = 118d
→ n1 = 229 > n2 = 118 → il ramo “somma” verrà eseguito · somma = 347d
; ── ESERCIZIO 2: SELEZIONE ────────────────────────────────────────────────

data SEGMENT
  n1         DW  345o      ; 229 decimale — DW perché potrebbe superare 255
  n2         DW  76h       ; 118 decimale
  somma      DW  ?
  differenza DW  ?
data ENDS

stack SEGMENT STACK
  DB 100h DUP(?)
stack ENDS

code SEGMENT
ASSUME CS:code, DS:data, SS:stack

start:
  MOV AX, data
  MOV DS, AX

  MOV AX, n1            ; AX ← n1 = 229d
  CMP AX, n2            ; confronta AX con n2 — imposta i flag
  JG  ramo_maggiore     ; se n1 > n2 salta a ramo_maggiore

  ; ── ramo else: n1 ≤ n2 → calcola differenza ─────────────────────────
  SUB AX, n2            ; AX ← n1 - n2
  MOV differenza, AX
  JMP fine              ; salta oltre il ramo if (evita di eseguirlo)

ramo_maggiore:          ; ── ramo if: n1 > n2 → calcola somma ──────────
  ADD AX, n2            ; AX ← n1 + n2 = 229 + 118 = 347d
  MOV somma, AX

fine:
  MOV AH, 4Ch
  INT 21h
code ENDS
END start

Esercizio 3 — Ciclo FOR (fattoriale di 5)

// problema
Calcolare 5! (fattoriale di 5 = 5×4×3×2×1 = 120) simulando un ciclo FOR. Il contatore decrementa da 5 a 1, moltiplicando ad ogni passo.
; ── ESERCIZIO 3: CICLO FOR — fattoriale di 5 ─────────────────────────────
; Equivalente C: for(int i=5; i>=1; i--) { ris *= i; }

data SEGMENT
  numero    DB  5d        ; valore di partenza del contatore
  risultato DW  1d        ; accumulatore inizializzato a 1
data ENDS

stack SEGMENT STACK
  DB 100h DUP(?)
stack ENDS

code SEGMENT
ASSUME CS:code, DS:data, SS:stack

start:
  MOV AX, data
  MOV DS, AX

  MOV AX, risultato     ; AX = 1 (accumulatore)
  MOV CL, numero        ; CL = 5 (contatore)

ciclo_for:              ; ── inizio corpo del ciclo ─────────────────────
  MUL CL                ; AX ← AX × CL  (5,4,3,2,1)
  DEC CL                ; CL-- (contatore decrementa)
  CMP CL, 1d            ; CL == 1? (condizione di uscita)
  JGE ciclo_for         ; se CL >= 1 → continua il ciclo

  MOV risultato, AX     ; salva 120d = 78h in risultato

  MOV AH, 4Ch
  INT 21h
code ENDS
END start
// traccia esecuzione — iterazione per iterazione
IterazioneCL primaAX = AX × CLCL dopo DEC
151 × 5 = 54
245 × 4 = 203
3320 × 3 = 602
4260 × 2 = 1201
Fine1CL = 1 → JGE non salta

Esercizio 4 — Ciclo WHILE (fattoriale di 5)

// problema
Stesso risultato dell’esercizio precedente (5!) ma implementando un ciclo WHILE — la condizione viene verificata prima del corpo del ciclo.
; ── ESERCIZIO 4: CICLO WHILE — fattoriale di 5 ───────────────────────────
; Equivalente C: while(CL > 1) { ris *= CL; CL--; }

data SEGMENT
  numero    DB  5d
  risultato DW  1d
data ENDS

stack SEGMENT STACK
  DB 100h DUP(?)
stack ENDS

code SEGMENT
ASSUME CS:code, DS:data, SS:stack

start:
  MOV AX, data
  MOV DS, AX

  MOV AX, risultato     ; AX = 1
  MOV CL, numero        ; CL = 5

controllo_while:        ; ── test condizione PRIMA del corpo ────────────
  CMP CL, 1d            ; CL == 1? (condizione: CL > 1 per continuare)
  JLE fine_while        ; se CL ≤ 1 → esci dal ciclo

  ; ── corpo del ciclo ──────────────────────────────────────────────────
  MUL CL                ; AX ← AX × CL
  DEC CL                ; CL--
  JMP controllo_while   ; torna al controllo

fine_while:
  MOV risultato, AX     ; AX = 120d = 78h

  MOV AH, 4Ch
  INT 21h
code ENDS
END start
// ciclo FOR — struttura Assembly
inizio:
  corpo;
  DEC CL;
  CMP CL, 1;
  JGE inizio
Il corpo viene eseguito almeno una volta — condizione verificata alla fine
// ciclo WHILE — struttura Assembly
controllo:
  CMP CL, 1;
  JLE fine;
  corpo;
  DEC CL;
  JMP controllo
Condizione verificata prima — il corpo potrebbe non eseguire mai (se CL = 1 all’inizio)

Riepilogo

  • EMU8086 emula l’8086 permettendo di osservare registri, memoria e codice macchina istruzione per istruzione — strumento fondamentale per il debug e la comprensione del ciclo fetch-decode-execute
  • Il formato .COM usa un segmento unico da 64 KB (codice da ORG 100h), il formato .EXE usa segmenti separati con DS da inizializzare esplicitamente
  • DS non può essere caricato direttamente — si passa sempre per AX: MOV AX, dataMOV DS, AX
  • Ogni programma .EXE termina con MOV AH, 4Ch / INT 21h per restituire il controllo al SO
  • Le variabili si dichiarano nel Data Segment con DB (1B), DW (2B), DD (4B), DQ (8B) — le costanti con EQU (non occupano memoria)
  • Le stringhe per INT 21h devono terminare con il carattere $
  • La sequenza è l’esecuzione lineare di istruzioni — la struttura più semplice
  • La selezione si implementa con CMP + salto condizionato (JG, JL, JE…) + JMP per saltare il ramo alternativo
  • Il ciclo FOR in Assembly: corpo → DEC → CMP → salto condizionato — la condizione è in fondo (do-while)
  • Il ciclo WHILE in Assembly: CMP → salto uscita → corpo → DEC → JMP indietro — la condizione è in testa

Lascia un commento