;--------------------------------------------------;
; file int01h.asm                                  ; 
; intercetta la INT 01h (single step)              ;
;--------------------------------------------------;
; nasm -f obj int01h.asm -l int01h.lst             ;
; tlink /t int01h.obj + comlib.obj                 ;
; (oppure link /map /tiny int01h.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  INT01h      01h               ; indice INT 01h
%assign  TF_MASKON   0000000100000000b ; maschera per TF = 1
%assign  TF_MASKOFF  1111111011111111b ; maschera per TF = 0

;################ 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     set80x25                ; modo video testo 80x25
   call     hideCursor              ; nasconde il cursore
   call     clearScreen             ; pulisce lo schermo

; mostra la stringa del titolo

   mov      di, strTitle            ; ds:di punta a strTitle
   mov      dx, 000Eh               ; riga 0, colonna 14
   call     writeString             ; mostra la stringa

; salva l'indirizzo originale della INT 01h

   xor      ax, ax                  ; ax = 0
   mov      fs, ax                  ; fs = Seg(0000h)
   mov      di, (INT01h * 4)        ; di = 1 * 4 = 4

   mov      eax, [fs:di]            ; eax = Seg:Offset INT 01h originale
   mov      [old_int01h], eax       ; salva in old_int01h

; installa la nuova ISR

   cli                              ; disabilita le interruzioni
   mov      ax, new_int01h          ; Offset nuova ISR
   mov      [fs:di], ax             ; salva nel vettore
   mov      ax, ds                  ; Seg nuova ISR
   mov      [fs:di+2], ax           ; salva nel vettore
   sti                              ; riabilita le interruzioni

; abilitazione Trap Flag (TF = 1)

   pushf                            ; copia il registro FLAGS
   pop      ax                      ; in ax
   or       ax, TF_MASKON           ; TF = 1
   push     ax                      ; copia ax
   popf                             ; nel registro FLAGS

;  Da questo momento in poi, la CPU genera una INT 01h per ogni istruzione
;  eseguita; tale INT viene intercettata dalla nostra ISR che visualizza lo
;  stato corrente del registro IP!

; istruzioni varie

   mov      ax, 3500
   mov      bx, 8350
   add      ax, bx
   sub      ax, 2

; disabilitazione Trap Flag (TF = 0)

   pushf                            ; copia il registro FLAGS
   pop      ax                      ; in ax
   and      ax, TF_MASKOFF          ; TF = 0
   push     ax                      ; copia ax
   popf                             ; nel registro FLAGS

;  Da questo momento in poi, la CPU non genera piu' una INT 01h per ogni
;  istruzione eseguita!

; ripristina la ISR originale (FS:DI puntano ancora a 0000:0004h)

   cli                              ; disabilita le interruzioni
   mov      eax, [old_int01h]       ; eax = Seg:Offset INT 01h originale
   mov      [fs:di], eax            ; copia in 0000h:0004h
   sti                              ; riabilita le interruzioni

   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_int01h  dd    0                 ; indirizzo originale INT 01h
row_counter dw    0400h             ; contatore riga
   
strTitle    db    'ESECUZIONE DI UN PROGRAMMA IN SINGLE STEP MODE', 0
strDebug    db    'INT 01h ISR - IP =', 0

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

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

   align 16,   db    0              ; allinea al paragrafo

;  La nostra ISR per la INT 01h, visualizza lo stato corrente di IP ogni
;  volta che viene chiamata; ovviamente, appena entriamo in new_int01h,
;  tale informazione si trova in cima allo stack (return Offset). Siccome
;  new_int01 salva nello stack il contenuto di 4 registri a 16 bit, lo
;  stato corrente di IP si viene a trovare a SS:(SP+8)

new_int01h:

; preserva il contenuto di tutti i registri utilizzati

   push     ax
   push     dx
   push     di
   push     bp

; visualizza strDebug

   mov      di, strDebug            ; ds:dx punta a strDebug
   mov      dx, [row_counter]       ; dx = coordinate di output
   call     writeString             ; stampa la stringa

; visualizza il return Offset (IP corrente)

   mov      bp, sp                  ; ss:bp = ss:sp
   mov      ax, [bp+8]              ; ax = ss:[bp+8]
   mov      dl, 13h                 ; colonna 19
   call     writeHex16              ; mostra ax
   add      word [row_counter], 0100h  ; incremento riga

   call     waitChar                ; premere un tasto

; ripristina il contenuto di tutti i registri utilizzati

   pop      bp
   pop      di
   pop      dx
   pop      ax
   
   iret                             ; interrupt return

;----------- fine definizione procedure -----------

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