Avviare l'emulatore 8086 e creare un nuovo progetto basato sul template COM.
Nell'editor si presenterà la seguente pagina:
#make_COM# ORG 100h
Nella parte iniziale del segmento si inseriscono le dichiarazioni dei dati, quindi l'esecuzione deve iniziare dall'istruzione che è identificata con la label Inizio. Si deve notare che la dichiarazione EQU (Equates) definisce una costante, ovvero un'associazione tra un nome simbolico ed un valore che non occupa spazio di memoria, ma serve all'assemblatore per sostituire il valore nell'istruzione ogni volta che trova il nome simbolico associato:
JMP Inizio ; ------ sezione dichiarazioni ------ Lunghezza EQU 7
L'array Serpente contiene le coordinate (Riga e Colonna dello schermo) delle parti che compongono il serpente (dalla testa alla coda).
Il byte inferiore è la parte sinistra, il byte superiore è la testa - [testa, coda]
Serpente DW Lunghezza DUP(0) coda DW ?
Codici esacimali dei caratteri corrispondenti ai tasti cursore:
Sinistra EQU 4Bh Destra EQU 4Dh Su EQU 48h Giu EQU 50h
Direzione di movimento del serpente:
Direzione DB Destra Attesa DW 0
Segmento CODICE.
La prima istruzione chiama la procedura che stampa le istruzioni, al ritorno il programma resta in attesa di premere un tasto
Inizio: CALL Istruzioni ; Attesa che venga premuto un tasto: MOV AH, 00h INT 16h
La funzione 1, specificata in AH, dell'interrupt 10h fissa la dimensione, in numero di righe, del cursore. La prima riga è specificata in AH e l'ultima riga è specificata in CL. Se il numero di riga inizia con 2 si nasconde il cursore:
MOV AH, 1 MOV CH, 2Bh MOV CL, 0Bh INT 10h
Si giunge in questo punto dopo aver premuto un tasto.
La funzione 5, specificata in AH seleziona la pagina video (in modalità testo) specificata in AL:
gioco: ; === selezione della prima pagina video MOV AL, 0 ; Numero di Pagina. MOV AH, 05h INT 10h
Nell'array Serpente sono contenute le coordinate dello schermo in cui mostrare le 7 parti che compongono il serpente. Le coordinate sono contenute in due byte separati che la funzione 02h dell'interrupt 10h riceve nei registri DL e DH
; === Mostra la testa: MOV DX, Serpente[0] ; Posiziona il cursore alle coordinate DL,DH MOV AH, 02h INT 10h
In quella posizione la funzione 09 dell'interrupt 10h stampa il carattere '*' contenuto in AL con l'attributo di colore specificato in BL:
MOV AL, '*' MOV AH, 09h MOV BL, 0Eh ; attributi. MOV CX, 1 ; Numero di caratteri da stampare. INT 10h
Viene copiata la posizione occupata dalla coda del serpente perchè, dopo che le parti precedenti sono state fatte avanzare nella direzione determinata dal tasto cursore premuto, si dovrà cancellare dallo schermo il carattere corrispondente alla coda:
MOV AX, Serpente[Lunghezza * 2 - 2] MOV coda, AX CALL muoviSerpente === Elimina la coda dopo l'avanzamento: MOV DX, coda
la funzione 02 dell'interrupt 10h porta il cursore nelle coordinate DL,DH e poi, richiamando la funzione 09 dell'interrupt 10h si cancella la coda.
MOV AH, 02h INT 10h ; in quella posizione stampa ' ': MOV AL, ' ' MOV AH, 09h MOV BL, 0Eh ; attributi. MOV CX, 1 ; singolo carattere. INT 10h
Lettura della tastiera
La funzione 01 dell'interrupt 16h interroga il buffer della tastiera e restituisce un valore booleano, tramite la flag di Zero, per indicare se c'è un tasto. Se la flag Z=1 non è stato premuto nessun tasto.
leggiTasto: ; === riconoscimento del comando: MOV AH, 01h INT 16h JZ nessunTasto
Se invece è stato premuto un tasto la funzione 00 dell'interrupt 16h legge il tasto, eliminandolo dal buffer, e rende disponibile il suo codice nel registro AL. I tasti speciali (Tasti Funzione, Home, End, Canc, ecc..) sono formati da due byte, il primo è sempre il carattere NUL (0), il secondo è disponibile in AH:
MOV AH, 00h INT 16h CMP AL, 1Bh ; il tasto ESC termina il programma JE fine ;
Se si tratta di un tasto cursore, Il secondo valore del tasto premuto viene scritto nella variabile Direzione:
MOV Direzione, AH
Se non è stato premuto nessun tasto si ripete l'avanzamento del serpente nella direzione indicata nella variabile Direzione
nessunTasto: ; === ripetizione del ciclo: JMP gioco
Se è stato premuto il tasto ESC si ripristina la visualizzazione del cursore e si esce dal programma:
fine: ; Mostra nuovamente il cursore: MOV AH, 1 MOV CH, 0Bh MOV CL, 0Bh INT 10h RET
Questa procedura crea l'animazione muovendo le parti che compongono il serpente un passo in avanti. L'ultimo elemento dell'array Serpente (la coda)] si cancella, mentre le parti intermedie vengono copiate una posizione in avanti nell'array
Si inizializza ES per puntare al segmento 40h, dove sono contenute informazioni di sistema, in particolare il numero di righe e il numero di colonne del video:
muoviSerpente PROC NEAR MOV AX, 40h MOV ES, AX
Il registro DI viene inizializzato con l'indice dell'array che contiene il penultimo elemento dell'array Serpente, mentre nel registro CX si scrive il numero di elementi del serpente:
MOV DI, Lunghezza * 2 - 2 MOV CX, Lunghezza-1
Mediante un ciclo si muovono tutte le parti del corpo un passo in avanti.
Ogni elemento viene portato nell'elemento successivo:
avanza: MOV AX, Serpente[DI-2] MOV Serpente[DI], AX SUB DI, 2 LOOP avanza
Prima di stampare la testa del serpente si esamina la variabile Direzione:
CMP Direzione, Sinistra JE vaiSinistra CMP Direzione, Destra JE vaiDestra CMP Direzione, Su JE vaiSu CMP Direzione, Giu JE vaiGiu JMP fermaSerpente ; nessuna direzione scelta.
Se il serpente procede verso sinistra si deve decrementare il numero di colonna, prima di modificare le coordinate della testa contenute nell'elemento 0 dell'array Serpente.
Se però il serpente esce fuori dallo schermo (colonna < 0) allora si fa rientrare il serpente dall'altro lato. Occorre però conoscere il numero di colonne dello schermo per modificare il numero di colonna in cui deve comparire la testa del serpente. Questa informazione è disponibile nella variabile di sistema che si trova all'offset 4Ah nel segmento 40h.
vaiSinistra: MOV AL, byte ptr Serpente[0] DEC AL MOV byte ptr Serpente[0], AL CMP AL, -1 JNE fermaSerpente MOV AL, ES:[4Ah] ; numero colonna. DEC AL MOV byte ptr Serpente[0], AL ; rientra a destra. JMP fermaSerpente
Analogamente, se il serpente si muove verso destra si deve incrementare la coordinata di colonna della testa, ma quando raggiunge l'estremità destra dello schermo, si deve modificare la coordinata di colonna portandola a 0.
vaiDestra: MOV AL, byte ptr Serpente[0] INC AL MOV byte ptr Serpente[0], AL CMP AL, ES:[4Ah] ; numero colonna. JB fermaSerpente MOV byte ptr Serpente[0], 0 ; rientra a sinistra. JMP fermaSerpente
Le stesse considerazioni valgono quando il serpente si muove in verticale. Se il serpente sale si decrementa la coordinata di riga, altrimenti la si incrermenta, e quando giunge ad una delle estremità dello schermo deve rientrare dall'altra estremità. In questo caso il numero di righe dello schermo è specificato nell'offset 84h del segmento 40h
vaiSu: MOV AL, byte ptr Serpente[1] DEC AL MOV byte ptr Serpente[1], AL CMP AL, -1 JNE fermaSerpente MOV AL, ES:[84h] ; numero di riga -1. MOV byte ptr Serpente[1], AL ; ritorna in basso. JMP fermaSerpente vaiGiu: MOV AL, byte ptr Serpente[1] INC AL MOV byte ptr Serpente[1], AL CMP AL, ES:[84h] ; numero di riga -1. JBE fermaSerpente MOV byte ptr Serpente[1], 0 ; ritorna in alto. JMP fermaSerpente
La pressione di un tasto diverso da un tasto cursore esce dalla procedura
fermaSerpente: RET muoviSerpente ENDP
La seguente procedura stampa una stringa terminata con il carattere ZERO.
Istruzioni PROC NEAR push SI ; salva il contenuto del registro SI mov SI, offset CS:Help PUSH AX ; salva il contenuto del registro AX carattSucc: MOV AL, CS:[SI] INC SI ; indice del prossimo byte. CMP AL, 0 JZ stampato MOV AH, 0Eh ; funzione macchina da scrivere INT 10h JMP carattSucc stampato: pop SI ; ripristina i valori dei registri POP AX RET Istruzioni ENDP
Help DB "==== ISTRUZIONI ====", 13, 10 DB "Impostare la casella", 13, 10 DB '"step delay" a "0" prima di', 13, 10 DB "eseguire il programma, altrimenti", 13, 10 DB "il serpente avanza lentamente.", 13, 10, 13, 10 DB "Il controllo del movimento del serpente", 13, 10 DB "avviene con i tasti cursore", 13, 10, 13, 10 DB "Qualsiasi altro tasto", 13, 10 DB "ferma il serpente.", 13, 10, 13, 10 DB "Il tasto ESC termina.", 13, 10 DB "====================", 13, 10, 13, 10 DB "Premere un tasto per iniziare...", 0 END
La direttiva END al termine del programma informa l'assemblatore che non ci sono altre istruzioni da tradurre.
Dopo aver premuto il pulsante Compila ed Emula,prima di premere il pulsante RUN, impostare la casella "step delay" a "0".
Per controllare la direzione del movimento del serpente agire sui tasti cursore. La pressione di altri tasti ferma l'avanzamento del serpente
Premere ESC per terminare.
Eseguire il programma e osservare nel codice assembler che le costanti non sono state memorizzate in nessuna locazione, ma in tutte le istruzioni che le riferiscono è stato sostituito il loro valore.
Dopo ciascuna chiamata di procedura osservare il valore dei registri SP e IP e leggere i valori depositati sullo stack.