;--------------------------------------------------;
; file keyboard.asm                                ; 
; copyright (C) Ra.M. Software                     ;
; intercetta la IRQ1 - INT 09h keyboard controller ;
;--------------------------------------------------;
; nasm -f obj keyboard.asm                         ;
; tlink /t keyboard.obj + comlib.obj               ;
; (oppure link /tiny keyboard.obj + comlib.obj)    ;
;--------------------------------------------------;                  

;########### direttive per l'assembler ############

CPU 386                             ; set di istruzioni a 32 bit
%include "comlib.inc"               ; inclusione libreria di I/O

;######### dichiarazione tipi e costanti ##########

%assign  INT09h         09h         ; INT 09h - keyboard ISR
%assign  KBD_BUFF       60h         ; porta buffer dati tastiera
%assign  KBD_COMM       64h         ; porta comandi tastiera
%assign  MPICP0         20h         ; porta P0 PIC Master
%assign  MPICP1         21h         ; porta P1 PIC Master

%assign  IRQ1_MASK      00000010b   ; mask-on per la IRQ1
%assign  IRQ1_UNMASK    11111101b   ; mask-off per la IRQ1

;################ segmento unico ##################

SEGMENT     COMSEGM ALIGN=16 PUBLIC USE16 CLASS=CODE

   resb     0100h                   ; libera 256 byte per il PSP
   
..start:                            ; entry point

;------- inizio blocco principale istruzioni ------

   call     hideCursor              ; nasconde il cursore
   call     clearScreen             ; pulisce lo schermo
   
; visualizza il titolo del programma

   mov      di, title_str           ; DS:DI punta a title_str
   mov      dx, 000Ah               ; riga, colonna di output
   call     writeString             ; visualizza la stringa
   
; installazione nuova ISR per la INT 09h
   
   in       al, MPICP1              ; AL = IMR PIC Master
   or       al, IRQ1_MASK           ; pone a 1 il bit 1 (mask)
   out      MPICP1, al              ; scrive OCW1 nel PIC Master

   xor      ax, ax                  ; AX = 0
   mov      es, ax                  ; ES = paragrafo 0000h
   mov      eax, [es:(INT09h * 4)]  ; legge il vecchio vettore 09h
   mov      [old_int09h], eax       ; e lo salva in old_int09h
   
   mov      ax, cs                  ; AX = Seg(new_int09h)
   shl      eax, 16                 ; sposta nella WORD alta di EAX
   mov      ax, new_int09h          ; AX = Offset(new_int09h)
   mov      [es:(INT09h * 4)], eax  ; installa il nuovo vettore 09h
   
   in       al, MPICP1              ; AL = IMR PIC Master
   and      al, IRQ1_UNMASK         ; pone a 0 il bit 1 (unmask)
   out      MPICP1, al              ; scrive OCW1 nel PIC Master
   
   mov      dx, 0400h               ; inizializza riga, colonna
   
; loop principale del programma
   
keyboard_loop:

   mov      al, [key_buffer]        ; nuovo scan code letto dalla ISR
   cmp      al, 01h                 ; tasto [Esc] premuto?
   jne      keyboard_loop           ; controllo loop
   
; ripristino vecchia ISR per la INT 09h
   
   in       al, MPICP1              ; AL = IMR PIC Master
   or       al, IRQ1_MASK           ; pone a 1 il bit 1 (mask)
   out      MPICP1, al              ; scrive OCW1 nel PIC Master

   xor      ax, ax                  ; AX = 0
   mov      es, ax                  ; ES = paragrafo 0000h
   mov      eax, [old_int09h]       ; EAX = indirizzo vecchio vettore 09h
   mov      [es:(INT09h * 4)], eax  ; ripristina il vecchio vettore 09h
   
   in       al, MPICP1              ; AL = IMR PIC Master
   and      al, IRQ1_UNMASK         ; pone a 0 il bit 1 (unmask)
   out      MPICP1, al              ; scrive OCW1 nel PIC Master
   
   call     showCursor              ; ripristina il cursore
   
;-------- fine blocco principale istruzioni -------
   
   mov      ah, 4ch                 ; servizio Terminate Program
   mov      al, 00h                 ; exit code = 0
   int      21h                     ; chiama i servizi DOS

;----- inizio definizione variabili statiche ------

   align 4, db    0                 ; allinea alla DWORD
   
old_int09h  dd    0                 ; indirizzo vecchia ISR
key_buffer  db    0                 ; scan code tasto premuto
irq_counter db    0                 ; contatore IRQ

title_str   db    "CODICI DI SCANSIONE DELLA TASTIERA"
            db    " (premere [Esc] per uscire)", 0
clear_str   db    "                        ", 0

;------- fine definizione variabili statiche ------

;---------- inizio definizione procedure ----------

; new_int09h: nuova ISR per la INT 09h
; una ISR deve sempre preservare tutti i registri che utilizza!

new_int09h:

   push     ax                      ; preserva AX
   push     bx                      ; preserva BX
   push     cx                      ; preserva CX
   push     di                      ; preserva DI
   
   cmp      byte [irq_counter], 0   ; fine scan code ?
   ja       next_scancode           ; se "no" salta a next_scancode
   mov      di, clear_str           ; DS:DI punta a clear_str
   mov      dx, 0400h               ; riga 4, colonna 0
   call     writeString             ; visualizza la stringa
   
   xor      cx, cx                  ; CX = 0
wait1:
   in       al, KBD_COMM            ; legge lo Status Byte
   test     al, 00000010b           ; buffer pronto per la lettura ?
   loopnz   wait1                   ; controllo loop
   
   in       al, KBD_BUFF            ; legge il prossimo codice
   mov      [key_buffer], al        ; e lo salva in key_buffer
   call     writeHex8               ; visualizza il codice

   mov      byte [irq_counter], 0   ; assume "normal key" (1 codice)
   
test_for_E0h:
   cmp      al, 0E0h                ; codice == E0h ?
   jne      test_for_E1h            ; se "no" salta a test_for_E1h
   mov      byte [irq_counter], 1   ; "extended key" da 2 codici
   add      dl, 4                   ; incremento colonna
   jmp      short send_eoi          ; salta a send_eoi
   
test_for_E1h:
   cmp      al, 0E1h                ; codice == E1h ?
   jne      send_eoi                ; se "no" salta a send_eoi
   mov      byte [irq_counter], 5   ; "extended key" da 6 codici
   add      dl, 4                   ; incremento colonna
   jmp      short send_eoi          ; salta a send_eoi

next_scancode:
   xor      cx, cx                  ; CX = 0
wait2:
   in       al, KBD_COMM            ; legge lo Status Byte
   test     al, 00000010b           ; buffer pronto per la lettura ?
   loopnz   wait2                   ; controllo loop

   in       al, KBD_BUFF            ; legge il prossimo codice
   mov      [key_buffer], al        ; e lo salva in key_buffer
   call     writeHex8               ; visualizza il codice
   add      dl, 4                   ; incremento colonna
   dec      byte [irq_counter]      ; decremento contatore
 
send_eoi:
   mov      al, 20h                 ; OCW3 = specific EOI
   out      MPICP0, al              ; scrive OCW3 nel PIC Master
   
   pop      di                      ; ripristina DI
   pop      cx                      ; ripristina CX
   pop      bx                      ; ripristina BX
   pop      ax                      ; ripristina AX
   iret                             ; return from interrupt
   
;----------- fine definizione procedure -----------

;##################################################