Cerca nel sito:
ricerca
avanzata

Frasi Celebri...

Controllore: "Lei e' un ignorante". Ragazzo: "Ignorante sara' sua sorella!". Controllore: "Ma no, non ha capito: ignorante nel senso che ignora...". Ragazzo: "Allora lei e' un imbecille, nel senso che imbelle".

da "Tre uomini e una gamba" 

Sondaggio:

Cosa pensi della pena di morte?

Dovrebbero abolirla in tutto il mondo
A volte è necessaria
Ammazzateli tutti quei b... delinquenti
Va contro ogni principio umano
Non mi interessa

visualizza risultati


 

Programmi residenti in memoria

Trattiamo oggi un argomento non troppo facile ma interessante: i programmi TSR, Terminate but Stay Resident, si tratta di quei programmi che rimangono in memoria e si attivano solo in determinate situazioni (di solito al verificarsi di particolari interrupt). Analizzeremo in particolare un programmino che a ogni pressione di un tasto emette un beep.

Per la realizzazione di un programma TSR serve conoscere l'int 27h che ha per parametri l'indirizzo dell'ultimo byte del programma più uno e il DS del programma.

Il procedimento e il seguente:

  • Memorizza l'indirizzo della vecchia routine di interrupt
  • Rimpiazza la routine di interrupt con una nuova
  • Chiama l'interrupt 27h

Ma vediamo subito il programma e poi lo commentiamo:

;Beep.asm - by b0nu$, 1997

.286c
.MODEL SMALL

INTERRUPT_NUM EQU 9 ;Interrupt da intercettare

              .CODE
              ORG 100H
FIRST:        JMP LOAD_PROG         ;Carico in memoria il prg.
              OLD_KEYBOARD_INT DD ? ;memorizza l'indirizzo al
                                    ;vecchio vettore di int.

PROG          PROC
              pusha                 ;salvo i registri
              pushf

              call OLD_KEYBOARD_INT ;chiamo la vecchia routine
                                    ;di int.

; QUI CI VA IL PROGRAMMA: In questo esempio ho deciso di
; emettere un BEEP ma si può fare qualunque cosa.
;----------------------------------
              in al,61h         ;Per il BEEP programmo il Timer
              test al,3
              jne skippa
              or al,3
              out 61h,al
              mov al,0B6h
              out 43h,al

skippa:       mov al,06h        ;frequenza LSB
              out 42h,al
              mov al,01h        ;frequenza MSB
              out 42h,al

              mov cx,0FFFFh
wait_loop:
              loop wait_loop    ;ciclo di attesa

              in al,61h         ;silenzio
              and al,0FCh
              out 061h,al
;----------------------------------

EXIT:
              popa
              iret
PROG          ENDP

LOAD_PROG     PROC     ;Precedura che carica in memoria il prg.
              mov ah,35h
              mov al,INTERRUPT_NUM
              int 21h              ;Prelevo il vecchio vettore
              mov WORD PTR OLD_KEYBOARD_INT,bx
              mov WORD PTR OLD_KEYBOARD_INT[2],es

              mov al,INTERRUPT_NUM
              mov ah,25h
              lea dx,PROG
              int 21h                 ;Imposto quello nuovo

              mov dx,OFFSET LOAD_PROG ;in DX ci va l'ultimo
                                      ;byte del prg. + 1
              int 27h             ;Termina ma rimani in memoria
LOAD_PROG     ENDP
              END FIRST

Come potete vedere la prima operazione svolta dal programma è quella di chiamare la procedura LOAD_PROG. Questa memorizza il vecchio vettore di interrupt e imposta quello nuovo, infine chiama l'int 27h per rimanere residente in memoria.

In questo modo tutte le volte che viene generato un int 09h (in pratica tutte le volte che viene premuto un tasto) verrà eseguita la procedura PROG scritta da noi. Essa come prima cosa salva il valore dei registri (cosa da fare sempre in questi casi), poi chiama la vecchia routine di int; in questo caso ci serve per visualizzare il carattere relativo al tasto premuto in pratica a questo livello lasciamo tutto come era prima.

Il programma vero e proprio arriva subito dopo e nel nostro caso emette un BEEP dallo speaker e lo fa andando a programmare direttamente il timer.

Il timer consiste in un dispositivo che puo lavorare in diverse modalità a seconda dei valori che immetto nella porta 43h. Non sto qui a spiegarvi tutti i dettagli del timer, vi dico solo nel programma attivo il timer tramite la porta 61h, immetto nella porta 43h la modalità di funzionamento (Square Wave Generator) e nella porta 42h la frequenza del suono sotto forma di due byte, prima quello meno significativo poi quello più significativo, infine spengo tutto tramite la porta 61h.

Dopo aver emesso il suono la procedura ripristina i registri e rilascia il controllo. Come vedete non è poi cosi difficile e i passi per la realizzazione sono abbastanza standard. In questo modo la parte residente è solo la procedura PROG tutto il resto viene scaricato dopo l'int 27h. Naturalmente la parte residente deve stare nei 64Kb di un segmento e cosi pure il programma deve essere un file .COM.

Questo programma non è però tanto utile se non a livello di folklore. Infatti sarebbe più interessante sapere quale tasto è stato premuto per poter intercettare una particolare combinazione di tasti. Per far ciò devo spendere due parole per dirvi dove vengono memorizzati i tasti premuti.

Una parte del BIOS viene memorizzato in RAM a partire dall'indirizzo 400h fino all'indirizzo 4FFh; in quest'area sono memorizzate numerose informazioni riguardanti l'hardware del PC come gli indirizzi delle porte seriali e parallele il numero di dischi il tipo di computer la modalità video ecc... tra le tante cose all'indirizzo 41Ah (0040:001A) c'è un puntatore (2 byte) alla testa del buffer dei caratteri arrivati dalla tastiera, subito dopo (41Ch) un puntatore alla coda dello stesso buffer e all'indirizzo 41Eh c'è il buffer circolare composto da 32 bytes (0040:001E --> 0040:003E) che contiene i codici ASCII e gli SCAN CODE dei tasti premuti. Bene ora che sappiamo dove sono basta andare a prenderli. Ecco un programma che lo fa...

;Beep2.asm - by b0nu$, 1997
.286c
.MODEL SMALL

INTERRUPT_NUM EQU 9 ;Interrupt da intercettare

ROM_BIOS_DATA SEGMENT AT 40H   ;Questi sono dati memorizzati
              ORG 1AH          ;nel BIOS all'ind. 0040:001A
              HEAD DW ?        ;Puntatore alla testa del buffer
              TAIL DW ?        ;Puntatore alla coda del buffer
              BUFF DW 16 DUP(?);Buffer
              BUFF_END LABEL WORD
ROM_BIOS_DATA ENDS

              .CODE
              ORG 100H
FIRST:        JMP LOAD_PROG         ;Carico in memoria il prg.
              OLD_KEYBOARD_INT DD ? ;memorizza l'indirizzo al
                                    ;vecchio vettore di int.

PROG          PROC
              pusha                 ;salvo i registri
              pushf

              call OLD_KEYBOARD_INT ;chiamo la vecchia routine
                                    ;di int.

              ASSUME ds:ROM_BIOS_DATA
              push ds
              mov bx,ROM_BIOS_DATA ;Questo gruppo di istruzioni
              mov ds,bx            ;serve per gestire il buffer
              mov bx,TAIL          ;dei caratteri letti
              cmp bx,HEAD
              je EXIT              ;Non ci sono caratteri
              sub bx,2             ;si sposta di due bytes
              cmp bx,OFFSET BUFF   ;controlla di non uscire
              jae NO_WRAP
              mov bx,OFFSET BUFF_END
              sub bx,2            ;BX punta al carattere
NO_WRAP:      mov dx,[bx]         ;in DL c'è il carattere letto

; QUI CI VA IL PROGRAMMA: In questo esempio ho deciso di
; emettere un BEEP ma si può fare qualunque cosa.
; tranne che chiamare un interrupt del DOS!!
;----------------------------------
              ;routine di beep col timer vista prima
              in al,61h         ;Per il BEEP programmo il Timer
              test al,3
              jne skippa
              or al,3
              out 61h,al
              mov al,0B6h
              out 43h,al

skippa:       mov al,06h         ;frequenza LSB
              out 42h,al
              mov al,01h         ;frequenza MSB
              out 42h,al

              mov cx,0FFFFh
wait_loop:
              loop wait_loop     ;ciclo di attesa

              in al,61h          ;silenzio
              and al,0FCh
              out 061h,al
;----------------------------------

EXIT:         pop ds
              popf
              popa
              iret
PROG          ENDP

LOAD_PROG     PROC     ;Precedura che carica in memoria il prg.
              mov ah,35h
              mov al,INTERRUPT_NUM
              int 21h               ;Prelevo il vecchio vettore
              mov WORD PTR OLD_KEYBOARD_INT,bx
              mov WORD PTR OLD_KEYBOARD_INT[2],es

              mov al,INTERRUPT_NUM
              mov ah,25h
              lea dx,PROG
              int 21h                 ;Imposto quello nuovo

              mov dx,OFFSET LOAD_PROG ;in DX ci va l'ultimo
                                      ;byte del prg.+1
              int 27h             ;Termina ma rimani in memoria
LOAD_PROG     ENDP
              END FIRST

La variabile ROM_BIOS_DATA memorizza i due puntatori e il buffer e le istruzioni aggiunte dopo la chiamata al vecchio int si occupano di prelevare il codice ASCII del tasto premuto. Alla fine di quelle istruzioni avremo in DH lo SCAN CODE e in DL il codice ASCII e possiamo confrontare il carattere letto con quello da intercettare e fare quello che vogliamo. Nell'esempio si controlla se e stata premuta le lettera b e in tal caso di emette un lungo BEEP.

OK spero che sia tutto chiaro so che non è facilissimo ma provate a scrivere qualcosa magari per cominciare modificate uno dei due programmi in modo che intercettino altri tasti o che facciano qualcos'altro.

L'argomento è abbastanza complicato e richiede un codice molto pulito per non andare ad interferire con altri programmi residenti o altri driver, ma non scoraggiatevi e continuate......

 

successivo
–«  INDICE  »–

 

 

 

 
Powered by paper&pencil (carta&matita ) - Copyright © 2001-2009 Cataldo Sasso