; 
; Compitino 2006-12-01
; Utilizzando il linguaggio Assembler 8086 e l'insieme di interrupt a
; disposizione sui processori compatibili x86, si realizzi il programma che
; faccia quanto segue:
; 1. stampa sullo schermo 10 volte il carattere '*' nelle posizioni di
;    coordinate definite nel vettore VETT;
; 2. attende che l'utente prema uno dei seguenti tasti: 'a','d','s','w', e 'z':
;    se il tasto premuto corrisponde al carattere 'z' il programma termina;
; 3. a seconda del tasto premuto sposta il cursore di una posizione rispetto a
;    quella attuale in direzione:
;     . tasto 'a': sinistra,
;     . tasto 'd': destra,
;     . tasto 's': basso,
;     . tasto 'w': alto,
; 4. controlla la nuova posizione del cursore sia posta all'interno dello
;    schermo (dimensioni 80x25);
; 5. controlla che nella nuova posizione sia gia' presente il carattere '*':
;    a. se e' presente passa al punto 6,
;    b. altrimenti scrive il carattere '*', quindi torna al punto 2;
; 6. stampi la stringa 'Game Over!', quindi stampi su linee successive dello
;    schermo il numero di spostamenti del cursore:
;    a. verso sinistra,
;    b. verso destra,
;    c. verso il basso,
;    d. verso l'alto;
; 7. invii sull'interfaccia di uscita, disponibile agli indirizzi TSR e TBR,
;    i caratteri ASCII necessari a stampare la somma totale di tali spostamenti.

.MODEL	small
.stack	100h

TBR EQU 61h
TSR EQU 65h

.data 
    ; Vettore posizioni iniziali
    ; nel formato x,y (colonna<80, riga<25)
    VETT db  0,0, 79,24, 79,0, 0,24, 25,1, 13,2, 66,19, 74,9, 15,21, 5,10
    x    db 40
    y    db 12
    mov_up      dw  0
    mov_left    dw  0
    mov_down    dw  0
    mov_right   dw  0
    gameOverStr db  0Dh, 0Ah, "GameOver!$"
    msgLeftStr  db  0Dh, 0Ah, "Spostamenti a sn.    : $"
    msgRightStr db  0Dh, 0Ah, "Spostamenti a dx.    : $"
    msgUpStr    db  0Dh, 0Ah, "Spostamenti in alto  : $"
    msgDownStr  db  0Dh, 0Ah, "Spostamenti in basso : $"
    msgTotStr   db  0Dh, 0Ah, "Spostamenti totali   : $"
    pkey db 0Dh,0Ah, "press any key...$"

.code
start:
	; set segment registers:
    mov ax,@data
    mov ds, ax
    
    ; hide blinking text cursor
    mov ch, 32
    mov cl, 7
    mov ah, 1
    int 10h
     	
    ; 1. Stampo 10 '*' nelle posizioni indicate dal vettore VETT.
    mov cx, 10  ; cx contiene le iterazioni
    mov si, 0   ; si serve per indiciare il vettore VETT
    
ciclo1:    
    ; Porto il cursore nella posizione VETT[si],VETT[si+1]
    mov dl, VETT[si]   ; colonna
    mov dh, VETT[si+1] ; riga
    call setCursorPosition        ; posiziono il cursore;
    mov al, '*'        ; stampo il carattere '*';
    call writeCharAtCursorPosition    
    add si, 2          ; passo alle coordinate successive del vettore;
    dec cx             ; decremento il contatore del ciclo;
    jnz ciclo1         ; se non e' zero continuo il ciclo1.
    
    
    ; Posiziono il cursore al centro dello schermo
    mov dh, y              ; riga    = y
    mov dl, x              ; colonna = x
    call setCursorPosition ; posiziono il cursore
    
    ; 2. Input da tastiera dell'utente
inKey:
    call getKey
    ;    Il carattere e' 'a','s','d', o 'w'
    cmp al, 'z'
    je gameover    ; Se il carattere e' 'z' fine del gioco.
    cmp al, 'a'
    je left
    cmp al, 'w'
    je up
    cmp al, 'd'
    je right
    cmp al, 's'
    je down
    jmp inKey       ; Se il carattere non e' tra quelli attesi
                    ; attendere un nuovo carattere

    ; Azioni da compiere per ogni movimento
    ;   i) incrementare il contatore di movimento corrispondente (punto 6)
    ;  ii) incrementare/decrementare la coordinata interessata.    
left:
    inc mov_left
    dec x
    jmp newPos

right:
    inc mov_right
    inc x
    jmp newPos

up:
    inc mov_up
    dec y
    jmp newPos

down:
    inc mov_down
    inc y
    jmp newPos
    
    ; Impostare la nuova posizione
newPos:
    mov dh, y              ; riga    = y
    mov dl, x              ; colonna = x
    call setCursorPosition ; posiziono il cursore
    
    ; 4. controllare se la nuova posizione e' contenuta nello schermo
    cmp x, 79
    ja  gameover
    cmp y, 24
    ja  gameover
    ; 5a. contollare se nella nuova posizione e' posto un carattere '*'
    call readCharAtCursorPosition
    cmp al, '*'
    je  gameover
    ; 5b. stampare il carattere '*'
    mov al, '*'        ; stampo il carattere '*';
    call output
    ; quindi tornare al punto 2.
    jmp inKey    
    
gameover:
    ; 6. stampi la stringa 'Game Over!',
    lea dx, gameOverStr
    call StampaStringa
    
    ; quindi stampi su linee successive dello
    ; schermo il numero di spostamenti del cursore:
    ;  a. verso sinistra,
    lea dx, msgLeftStr
    call StampaStringa
    mov ax, mov_left
    call bin2AAAA

    ; b. verso destra,
    lea dx, msgRightStr
    call StampaStringa
    mov ax, mov_right
    call bin2AAAA

    ; c. verso il basso,
    lea dx, msgUpStr
    call StampaStringa
    mov ax, mov_up
    call bin2AAAA

    ; d. verso l'alto;
    lea dx, msgDownStr
    call StampaStringa
    mov ax, mov_down
    call bin2AAAA
    
    ; 7. invii sull'interfaccia di uscita, disponibile agli indirizzi RSR e RBR,
    ;    i caratteri ASCII necessari a stampare la somma totale di tali spostamenti.
    lea dx, msgTotStr
    call StampaStringa
    mov ax, mov_left
    add ax, mov_right
    add ax, mov_up
    add ax, mov_down    ; il risultato e' <= 2000 numero di caratteri possibili
                        ; su schermo 80x25
    ; codice simile a bin2AAAA
    ; (occorre cambiare solo la 'call output' in 'call outTBR'
    ; qui e' lasciata la call output perche' il registro TBR non e'
    ; implementato nell'emulatore 8086.
    MOV DX, 0
    MOV BX, 1000
    DIV BX         ; AX = (DX AX) / operando (word) = 0 - 9
                   ; DX = resto                     = 0 - 999
    ADD AL, 30h    ; codifica ASCII del quoziente
    CALL output
    ; CALL outTBR
    MOV AX, DX     ; AX <- DX = 0 - 999
    MOV BL, 100    ; BL <- 100
    DIV BL         ; AL = AX / operando (byte) = 0 - 9
                   ; AH = resto                = 0 - 99
    ADD AL, 30h    ; codifica ASCII del quoziente
    CALL output
    ; CALL outTBR
    MOV AL, AH     ; AL <- AH = 0 - 99
    MOV AH, 0      ; AH <- 0  => AX = 0 - 99
    MOV BL, 10     ; BL <- 10
    DIV BL         ; AL = AX / operando (byte) = 0 - 9
                   ; AH = resto                = 0 - 9
    ADD AL, 30h    ; codifica ASCII del quoziente
    CALL output
    ; CALL outTBR
    MOV AL, AH     ; AL <- AH = resto
    ADD AL, 30h    ; codifica ASCII del resto
    CALL output
    ; CALL outTBR
   
    ; Fine esercizio
    lea dx, pkey
    call StampaStringa
    mov ah, 1     ; wait for any key....
    int 21h
    mov ax, 4c00h ; exit to operating system.
    int 21h

; Invia un carattere ASCII all'interfaccia di uscita TBR
outTBR proc
    PUSH AX
testFO:
    IN AL, TSR
    TEST AL, 20h
    JZ testFO
    POP AX
    OUT TBR, AL
    RET
outTBR endp

; Posiziona il cursore sullo schermo
; Parametri dh, dl (linea, col)
; Nessun valore ritornato
setCursorPosition proc
    push ax
    push bx
	mov bh, 0
	mov ah, 2
    int 10h
    pop bx
    pop ax
    ret
setCursorPosition endp

; Procedura getCursorPosition
; return DH = row.
;        DL = column.
getCursorPosition proc
    push ax
    push bx
    push cx
    mov ah, 03h
    int 10h
    pop cx
    pop bx
    pop ax
    ret
getCursorPosition endp

; Procedura readCharAtCursorPosition (Pagina video n. 0)
; return AH = attribute.
;        AL = character.
readCharAtCursorPosition proc
    push bx
    mov bh, 0
    mov ah, 08h
    int 10h
    pop bx
    ret
readCharAtCursorPosition endp

; Procedura writeCharAtCursorPosition (Pagina video n. 0)
; input: AL = character to display.
writeCharAtCursorPosition proc
    push ax
    push bx
    push cx
    mov bh, 0
    mov cx, 1
    mov ah, 0Ah
    int 10h
    pop cx
    pop bx
    pop ax
    ret
writeCharAtCursorPosition endp

; Procedura bin2AAAA
; Stampa un numero binario naturale <= 9999 in un numero codificato ASCII.
; input: AL = numero da convertire
bin2AAAA proc
    PUSH AX
    PUSH BX
    PUSH DX
    MOV DX, 0
    MOV BX, 1000
    DIV BX         ; AX = (DX AX) / operando (word) = 0 - 9
                   ; DX = resto                     = 0 - 999
    ADD AL, 30h    ; codifica ASCII del quoziente
    CALL output
    MOV AX, DX     ; AX <- DX = 0 - 999
    MOV BL, 100    ; BL <- 100
    DIV BL         ; AL = AX / operando (byte) = 0 - 9
                   ; AH = resto                = 0 - 99
    ADD AL, 30h    ; codifica ASCII del quoziente
    CALL output
    MOV AL, AH     ; AL <- AH = 0 - 99
    MOV AH, 0      ; AH <- 0  => AX = 0 - 99
    MOV BL, 10     ; BL <- 10
    DIV BL         ; AL = AX / operando (byte) = 0 - 9
                   ; AH = resto                = 0 - 9
    ADD AL, 30h    ; codifica ASCII del quoziente
    CALL output
    MOV AL, AH     ; AL <- AH = resto
    ADD AL, 30h    ; codifica ASCII del resto
    CALL output
    POP DX
    POP BX
    POP AX
    RET
bin2AAAA endp

; Procedura getKey
; Ritorna il tasto premuto da tastiera se appartiene all'insieme
; 'a','w','d','s','z'
getKey proc
inASCII:
    call input
    cmp al, 'a'
    je goodKey
    cmp al, 'w'
    je goodKey
     cmp al,'s'
    je goodKey 
    cmp al, 'd'
    je goodKey
    cmp al, 'z'
    je goodKey
    jmp inASCII      
goodKey:
    ret
getKey endp

; Procedura per prelevare un carattere da tastiera    
; Il carattere letto e' restituito in AL.
input proc
   	mov ah, 7
	int 21h
    ret
input endp

; Stampa il carattere contenuto in AL
output proc
    push ax
    push dx
    mov ah, 2
	mov dl, AL
	int 21h       ; output character.
	pop dx
	pop ax
    ret
output endp

; Stampa il newline
newline proc
    push ax
    mov al, 0Ah
    call output
    mov al, 0Dh
    call output
    pop ax
    ret
newline endp

; Stampa una stringa terminata da $ indirizzata dal registro DX
stampaStringa proc
    push ax
    mov ah, 9
    int 21h
    pop ax
    ret
stampaStringa endp

end start ; set entry point and stop the assembler.