|
|
Guida al Linguaggio C
II Parte
no_2400:
cmp lsb,18H
jne no_4800
lea dx,baud4800
jmp end_spy
no_4800:
cmp lsb,0CH
jne non_so
lea dx,baud9600
jmp end_spy
non_so:
lea dx,boh
end_spy:
call print
mov dx,port_cl
in al,dx
and al,127
out dx,al
ret
spy_baud endp
;
;---------------------------------------------------------
; Cerca il tipo di video per settare segmento buffer
;---------------------------------------------------------
;
get_video proc near
push es
mov ax,40H
mov es,ax
mov al,es:[10H]
and al,00110000b
cmp al,48
je mono
mov cs:video_type,0B800H
jmp end_get
mono:
mov cs:video_type,0B000H
end_get:
pop es
ret
get_video endp
;
;---------------------------------------------------------
; Salva la posizione del cursore per poi ripristinarla
;---------------------------------------------------------
;
save_cur proc near
mov bh,0
mov ah,3
int 10H
mov cs:cur_pos_x,dl
mov cs:cur_pos_y,dh
mov cs:cur_type_ch,ch
mov cs:cur_type_cl,cl
ret
save_cur endp
;
;---------------------------------------------------------
; Legge i bit della porta 3FEH
;---------------------------------------------------------
;
f_read proc near
mov dx,cs:port_address
in al,dx
mov cs:status,al
mov dh,4
mov dl,3
test status,128
jz a_1
mov al,1
jmp a_2
a_1:
mov al,0
a_2:
call at_print
mov dh,4
mov dl,8
test status,64
jz a_3
mov al,1
jmp a_4
a_3:
mov al,0
a_4:
call at_print
mov dh,4
mov dl,13
test status,32
jz a_7
mov al,1
jmp a_8
a_7:
mov al,0
a_8:
call at_print
mov dh,4
mov dl,18
test status,16
jz a_11
mov al,1
jmp a_12
a_11:
mov al,0
a_12:
call at_print
ret
f_read endp
;
;----------------------------------------------------------
; Stampa a una certa posizione flag (0 o 1)
;----------------------------------------------------------
;
at_print proc near
call at
cmp al,1
jne off
lea dx,cs:attivo
jmp e_a
off:
lea dx,cs:non_att
e_a:
call print
ret
at_print endp
;
;---------------------------------------------------------
; Stampa stringa
;---------------------------------------------------------
;
print proc near
push cs
pop ds
mov bl,112
mov si,dx
p_1:
mov al,[si]
cmp al,'$'
je p_end
mov ah,0EH
int 10H
inc si
jmp p_1
p_end:
ret
print endp
;
;---------------------------------------------------------
; Posiziona il cursore
;---------------------------------------------------------
;
at proc near
push ax
mov ah,2
mov bh,0
int 10H
pop ax
ret
at endp
;
;----------------------------------------------------------
; Memorizza video
;----------------------------------------------------------
;
leggi proc near
mov ax,cs:video_type
mov ds,ax
mov si,0000H
push cs
pop es
mov di,OFFSET buffer
mov cx,2000
cld
rep movsw
ret
leggi endp
;
;-----------------------------------------------------------
; Riscrive video
;-----------------------------------------------------------
;
scrivi proc near
push cs
pop ds
mov si,offset buffer
mov ax,cs:video_type
mov es,ax
mov di,0000H
mov cx,2000
cld
rep movsw
ret
scrivi endp
;
;-----------------------------------------------------------
; Cancella la finestra
;-----------------------------------------------------------
;
clear proc near
mov al,0
mov ah,7
mov ch,0
mov cl,0
mov dh,7
mov dl,26
mov bh,112
int 10H
ret
clear endp
;
;-----------------------------------------------------------
; Inizializzazione vettore interrupt 16H
;-----------------------------------------------------------
;
init:
assume ds:code
call verify_port
cmp cs:num_port,0
jne ok
lea dx,error
call print
int 20H
ok:
push cs
pop ds
;
mov ah,35H
mov al,16H
int 21H
mov WORD PTR old_int_16,bx
mov WORD PTR old_int_16[2],es
;
mov ah,25H
mov al,16H
mov bx,SEG new_int_16
mov ds,bx
mov dx,OFFSET new_int_16
int 21H
;
lea dx,copyright
call print
;
mov dx,OFFSET init+100H
int 27H
copyright db 10,10,13
db '***> RESIDENT COM MONITOR <***'
db 10,13
db '(C) Copyright Opus Free Soft'
db 10,13
db 'by F.Bernardotti Montecastello'
db 10,10,13
db 'Premi : <Ctrl-F1> per COM1'
db 10,13
db ' : <Ctrl-F2> per COM2'
db 10,10,13
db '$'
;
;------------------------------------------------------------
; Verifica il numero di porte seriali installate
;------------------------------------------------------------
;
verify_port proc near
mov ax,40H
mov es,ax
mov al,es:[11h]
and al,00000110b
shr al,1
mov cs:num_port,al
ret
verify_port endp
code ends
end
Utilizzando per l'esecuzione l'interrupt 16H ci troviamo di fronte ad altre problematiche.
Ad esempio si dovrebbe testare prima di ogni chiamata a un interrupt che non ce ne siano altri, di quelli hardware, pendenti.
Il risultato si ottiene nel seguente modo
cli
mov al,11
out 20H,al
in al,20H
sti
Da questa ultima routine e' possibile rilevare se qualche interrupt hardware e' attivo testando il byte contenuto in AL.
Se il valore e' 0 significa che non c'e' nessun interrupt attivo.
In ambedue i casi precedenti, per quanto ci e' possibile, conviene usare chiamate al Bios anche se questo, ad esempio per servizi del disco, ci potrebbe risultare complicato.
Un altro accorgimento e' quello di non memorizzare i dati allocandoli in memoria e di utilizzare al suo posto i registri del processore.
Ad ogni call dovra' corrispondere un operazione di salvataggio di questi nello stack e una di ripristino prima della fine.
Il discorso dello stack e' interessante in quanto si dovra' fornire al nostro programma uno di questi sufficentemente grande.
Il meccanismo di chiamata di un interrupt salva nello stack molti valori.
Quando avevamo parlato dell' interrupt 1CH si era detto che un problema era costituito da una routine abbastanza lunga in quanto si sarebbe potuto verificare una successiva chiamata mentre la prima era ancora in esecuzione.
Lo stesso problema potrebbe presentarsi con i programmi residenti.
Supponiamo di aver ridiretto l'interrupt 16H per fare in modo che questo testi i codici dei caratteri inputati da tastiera alla ricerca di quello relativo all'attivazione della nostra routine.
Una volta attivata si potrebbe verificare che vengano premuti nuovamente i tasti di richiamo.
La call al nostro programma residente non dovrebbe avvenire in quando questo e' gia' attivo e quindi in esecuzione.
Il metodo per superare questo ostacolo ci viene offerto dall'inserimento di un flag con il compito di segnalare se la nostra routine e' attiva o meno.
Nel programma esempio precedente questo compito e' assegnato alla
variabile FLAG.
In caso affermativo si dovrebbe verificare un salto al vecchio vettore d'interrupt senza piu' eseguire l'attivazione.
Un altro pasticcio riguardante la rientranza e' dovuto all' utilizzo
degli
stacks da parte, ad esempio, del Dos.
Il Dos al termine di una chiamata a un servizio ripristina il contenuto dello stack.
Supponendo che sia in esecuzione una call e' chiaro che una successiva rischia di mandare in overflow il contenuto dello o degli, piu' di uno, stacks utilizzati senza contare che il ripristino potrebbe fare perdere il contenuto presente prima della chiamata stessa.
Questo induce a pensare che i registri SS e SP devono essere salvati.
Notate che alcune calls del Dos utilizzano due stack mentre altre arrivano fino a tre.
Bisogna cercare di minimizzare l'utilizzo dello stack da parte dei nostri programmi TSR ed in ogni caso salvare quelli del Dos.
I problemi riguardanti l'installazione di un programma residente non sono finiti.
Una delle necessita' in alcuni casi e' quella di settare il PSP.
Quando vengono eseguiti dei servizi su disco e' conveniente in fase d' installazione del nostro programma salvare l'indirizzo del PSP per poi, nella parte residente, eseguire lo switch sostituendo quello del programma su cui siamo "apparsi" con quello relativo alla nostra routine.
Chiaramente in uscita si dovra' ripristinarlo.
Questi scopi possono essere ottenuti mediante le call gia' viste in precedenza con i servizi del Dos e precisamente la 50H per settare il nuovo PSP e la 51H per salvare l'indirizzo del vecchio.
E' anche conveniente settare, se il servizio esegue operazioni su disco, un nuovo DTA.
Nell'esempio precedente ho omesso una parte di codice relativo alla segnalazione della gia' avvenuta installazione nel caso che si tenti di rileggere una seconda volta il programma.
Come avrete notato TSR salva inizialmente i vecchi vettori di interrupts (in questo caso solo il 16H) per poterli richiamare per lo svolgimento delle funzioni ad essi associate.
Se si rileggesse la seconda volta TSR e' chiaro che l'indirizzo della routine dell' interrupt 16H non sarebbe piu' l'originale in quanto questa e' gia' stata modificata dalla prima installazione.
Per raggirare il problema si puo' utilizzare una particolare scritta di riconoscimento collegata a un vettore di interrupt libero (ad esempio dal 60H al 67H).
In fase di installazione si dovrebbe ricavare l'indirizzo della routine di questo interrupt leggendo poi se esiste gia' a questo offset l'identificatore dell'installazione.
In caso affermativo si dovrebbe uscire senza rinstallare il tutto segnalandolo con un messaggio.
Precedentemente avevo detto che si doveva salvare prima dell'attivazione tutti i parametri riguardanti al programma su cui compare il nostro TSR.
Tra questi parametri ritroviamo il modo video, il tipo di cursore e la posizione di questo.
Bisogna anche prestare attenzione durante l'esecuzione della nostra utility a non modificare variabili del Bios (0040:xxxx) o il settaggio delle porte.
E' chiaro che se noi modifichiamo questi parametri al rientro nel programma questo potrebbe trovarsi nei pasticci a causa di valori non opportuni.
Se dovete svolgere le operazioni appena dette salvate il contenuto
originale prima di eseguire modifiche e ripristinate il tutto prima di uscire per fare in modo che "nessuno si accorga che voi siete passati di li'".
Un ultima precauzione non adottata dall'esempio precedente e' dato dalla necessita' di disattivare durante l'esecuzione del nostro programma il Ctrl Break.
E' chiaro che nel caso che questo sia attivo e premuto non si potrebbe verificare un ripristino dell'ambiente precedente all'attivazione correndo seri pericoli di crash.
Precedentemente ho generalizzato parlando delle call's al Dos.
In effetti quelle che procurano i maggiori inconvenienti sono le precedenti alla 0CH e la 50H e 51H.
Nell'ultimo esempio che riportero' viene aggiunto un servizio, se cosi si puo' dire, all'int 16H allo scopo di vedere se il programma e' stato gia' letto.
Il seguente esempio risiede in memoria e legge alla pressione di ALT F1 la directory corrente.
;
; Il programma dopo essere caricato termina e rimane residente.
; Mediante la pressione di Ctrl F1 e' possibile da qualsiasi
; programma richiedere la Directory senza uscire sa questo.
; Rilascio anche i sorgenti per chi fosse interessato ai prg.
; residenti.
;
TITLE READ_DIR
attivable equ 0
noattiva equ 1
request equ "SD"
respond equ "sd"
;
; Segmento codice
;
code segment para public 'code'
assume cs:code,ds:code
;
; Salta a routine installazione
;
showdir: jmp init
;
; Vecchi vettori e varie
;
stop db 0
att db 0
schifi db ' .......',0
masch1 db '+--------[ DIRECTORY 2.1 ]-------+',0
masch2 db ': :',0
masch3 db '+--------------------------------+',0
blank db '--------------------------------',0
premi db '[Premi un tasto o ESC per fine]',0
righe db 0
old_dta dw ?,?
cur_pos_x db 0
cur_pos_y db 0
cur_type_ch db 0
cur_type_cl db 0
dta db 512 dup(0)
utile db 0
this_psp dw ?
old_psp dw ?
old_int_1c dw ?,?
old_int_16 dw ?,?
buffer dw 2000 dup(?)
screen dw 0
path db '*.*',0
flag db 0
nessuno db 'Nessun File',0
;
; Routine interrupt 16H
;
new_int_16 proc far
sti
cmp ax,request
je ok_re
cmp ah,0
jne old_int_jmp
pushf
cli
call DWORD PTR cs:old_int_16
cmp ax,5E00H
jne end_int
cmp cs:flag,attivable
jne end_int
mov cs:flag,noattiva
sti
jmp activate
end_int:
iret
ok_re:
mov ax,respond
jmp end_int
old_int_jmp:
jmp DWORD PTR cs:old_int_16
activate:
push ax
push ss
push bp
push bx
push cx
push dx
push si
push di
push es
push ds
;
assume cs:code,ds:code
push cs
pop ds
mov stop,0
call set_psp
call set_dta
call screenproc
call save_cur
call leggi
call clear
call print_masc
call print_dir
cmp stop,1
je basta
mov dh,22
mov dl,1
call at
lea dx,premi
call print
mov ah,0
int 16H
basta:
call restore_cur
call scrivi
call re_set_dta
call re_set_psp
mov cs:flag,attivable
;
a_21:
pop ds
pop es
pop di
pop si
pop dx
pop cx
pop bx
pop bp
pop ss
pop ax
jmp end_int
new_int_16 endp
;
; Stampa maschera
;
print_masc proc near
mov dh,0
mov dl,0
call at
lea dx,masch1
call print
mov cx,21
mov dh,1
mov dl,0
a_2_1:
call at
push dx
lea dx,masch2
call print
pop dx
inc dh
loop a_2_1
mov dh,22
mov dl,0
call at
lea dx,masch3
call print
ret
print_masc endp
;
; Stampa riga ............................
;
rig proc near
mov dh,21
mov dl,2
call at
lea dx,schifi
call print
mov dh,21
mov dl,2
call at
ret
rig endp
;
; Stampa dir
;
print_dir proc near
mov cs:righe,0
push dx
push cx
push ax
push cs
pop ds
lea dx,path
mov cx,00FFH
mov ah,4EH
int 21H
jc errore
call rig
line:
lea bx,dta
add bx,30
char:
mov dl,[bx]
cmp dl,0
je finita
call pr_char
inc bx
jmp char
finita:
call attrib
call clear_one
mov dh,21
mov dl,2
call at
cmp righe,18
jne ancora
call set_righe
cmp stop,1
je ritorna
mov cs:righe,0
ancora:
call rig
inc righe
lea dx,path
mov ah,4FH
int 21H
jc ritorna
jmp line
errore:
mov dh,10
mov dl,12
call at
lea dx,nessuno
call print
ritorna:
pop ax
pop cx
pop dx
ret
print_dir endp
;
; Scrive attributi
;
attrib proc near
mov dh,21
mov dl,24
call at
mov dl,'['
call pr_char
lea bx,dta+21
mov ah,BYTE PTR [bx]
mov att,ah
test att,01
jz bb_1
mov dl,'r'
jmp bb_2
bb_1:
mov dl,'-'
bb_2:
call pr_char
test att,02
jz bb_3
mov dl,'h'
jmp bb_4
bb_3:
mov dl,'-'
bb_4:
call pr_char
test att,04
jz bb_5
mov dl,'s'
jmp bb_6
bb_5:
mov dl,'-'
bb_6:
call pr_char
test att,08
jz bb_7
mov dl,'v'
jmp bb_8
bb_7:
mov dl,'-'
bb_8:
call pr_char
test att,10H
jz bb_9
mov dl,'d'
jmp bb_10
bb_9:
mov dl,'-'
bb_10:
call pr_char
test att,20H
jz bb_11
mov dl,'a'
jmp bb_12
bb_11:
mov dl,'-'
bb_12:
call pr_char
mov dl,']'
call pr_char
mov dh,21
mov dl,02
call at
ret
attrib endp
;
; Setta la pausa alla 20a riga
;
set_righe proc near
mov dh,22
mov dl,1
call at
lea dx,premi
call print
mov ah,0
int 16H
cmp al,27
jne con
mov stop,1
jmp end_scroll
con:
mov dh,22
mov dl,1
call at
lea dx,blank
call print
mov dh,21
mov dl,2
call at
end_scroll:
ret
set_righe endp
;
; Stampa un carattere
;
pr_char proc near
push ax
push bx
mov bl,7
mov ah,14
mov al,dl
int 10H
pop bx
pop ax
ret
pr_char endp
;
; Esegue lo scroll di una riga
;
clear_one proc near
mov al,1
mov ah,6
mov ch,2
mov cl,2
mov dh,21
mov dl,32
mov bh,7
int 10H
ret
clear_one endp
;
; Cerca scheda video e setta buffer relativo
;
screenproc proc near
push es
mov ax,40H
mov es,ax
mov al,es:[10H]
and al,00110000b
cmp al,48
je mono
mov cs:screen,0B800H
jmp end_screen
mono:
mov cs:screen,0B000H
end_screen:
pop es
ret
screenproc endp
;
; Legge memoria video per poterla salvare alla fine
;
leggi proc near
mov ax,cs:screen
mov ds,ax
mov si,0000H
push cs
pop es
mov di,OFFSET buffer
mov cx,2000
cld
rep movsw
ret
leggi endp
;
; Salva la posizione e il tipo di cursore
;
save_cur proc near
mov bh,0
mov ah,3
int 10H
mov cs:cur_pos_x,dl
mov cs:cur_pos_y,dh
mov cs:cur_type_ch,ch
mov cs:cur_type_cl,cl
mov ah,1
mov ch,13H
mov cl,13H
int 10H
ret
save_cur endp
;
; Riscrive la schermata originale
;
scrivi proc near
push es
push cs
pop ds
mov si,OFFSET buffer
mov ax,cs:screen
mov es,ax
mov di,0000H
mov cx,2000
cld
rep movsw
pop es
ret
scrivi endp
;
; Ripristina posizione e tipo cursore fine programma residente
;
restore_cur proc near
mov dh,cs:cur_pos_y
mov dl,cs:cur_pos_x
call at
mov ah,1
mov ch,cs:cur_type_ch
mov cl,cs:cur_type_cl
int 10H
ret
restore_cur endp
;
; Stampa le stringhe
;
print proc near
push cs
pop ds
mov bl,112
mov si,dx
p_1:
mov al,[si]
cmp al,0
je end_p
mov ah,0EH
int 10H
inc si
jmp p_1
end_p:
ret
print endp
;
; Cancella zona video per finestra
;
clear proc near
mov al,0
mov ah,7
mov ch,0
mov cl,0
mov dh,22
mov dl,33
mov bh,7
int 10H
ret
clear endp
;
; Posiziona il cursore
;
at proc near
mov ah,2
mov bh,0
int 10H
ret
at endp
;
; Setta DTA
;
set_dta proc near
mov ah,47
int 21H
mov old_dta,bx
mov old_dta[2],es
mov ah,26
push cs
pop ds
lea dx,dta
int 21H
ret
set_dta endp
;
; Risetta il vecchio DTA
;
re_set_dta proc near
push ds
mov dx,cs:old_dta
mov ax,cs:old_dta[2]
mov ds,ax
mov ah,26
int 21H
pop ds
ret
re_set_dta endp
;
; Salva corrente PSP e setta quello dell'utility
;
set_psp proc near
mov ah,98
int 21H
mov WORD PTR cs:old_psp,bx
mov bx,WORD PTR cs:this_psp
mov ah,80
int 21H
ret
set_psp endp
;
; Rinstalla PSP del programma originale
;
re_set_psp proc near
mov ah,80
mov bx,WORD PTR cs:old_psp
int 21H
ret
re_set_psp endp
;
;----------------------------------------------------------
; PARTE NON RESIDENTE
;----------------------------------------------------------
;
;
; Salva e installa vettori interrupts
;
init:
push cs
pop ds
;
; Controlla se gia' installato SD
;
mov ax,request
int 16H
cmp ax,respond
jne no_in
lea dx,gia_in
mov ah,9
int 21H
mov ax,4C00H
int 21H
no_in:
;
; Salva vettore interrupt 16H
;
mov ah,35H
mov al,16H
int 21H
mov old_int_16,bx
mov old_int_16[2],es
;
; Setta nuovo vettore interrupt 16H
;
mov ah,25H
mov al,16H
lea dx,new_int_16
int 21H
;
; Salva indirizzo PSP
;
mov ah,98
int 21H
mov cs:this_psp,bx
;
; Stampa la maschera
;
lea dx,m_1
mov ah,09H
int 21H
;
; Termina e rimani residente
;
mov dx,OFFSET init + 100H
int 27H
;
; Maschera
;
gia_in db 10,13
db 'RESIDENT SHOW DIR Ver. 2.1'
db 10,10,13
db 'ERR : Show Dir installata'
db 10,13
db ' : <Ctrl F1> per attivarla'
db 10,13
db '$'
m_1 db 10,10,13
db '+---------------------------------+'
db 10,13
db ': RESIDENT SHOW DIR Ver. 2.1 :'
db 10,13
db '+---------------------------------+'
db 10,13
db ': Writen by F.Bernardotti :'
db 10,13
db ': Opus 33/3 Tel. 0131-35.55.06 :'
db 10,13
db ': Premi <Ctrl-F1> per attivare :'
db 10,13
db '+---------------------------------+'
db 10,10,13
db '$'
code ends
end showdir
Voglio a questo punto, prima di terminare, riportare un breve riassunto di tutti i consigli per la scrittura di programmi residenti.
* Testate che la routine non sia gia' stata installata.
* Se eseguite call al Dos collegate una routine all'interrupt 8H che testi lo stato dell'active byte e che setti in modo opportuno un flag che testerete prima della call al Dos.
* Settate un flag anche per indicare che la routine e' gia' attiva
in modo che non possa essere richiamata.
* Disattivate il ctrl break agendo sulla tavola dei vettori di interrupt.
* Minimizzate l'uso dello stack.
* Salvate prima dell'attivazione tutto quello che modificherete durante l'esecuzione.
* Ridirigete PSP e DTA se e' necessario.
* Preferite in ogni caso le chiamate al Bios al posto di quelle al Dos.
Per ora, a causa della mia prossima partenza per la seconda fase delle ferie estive, concludo il tutto.
A settembre rilasciero' un update con la continuazione sui programmi residenti in memoria e con le funzioni aggiunte del Dos 3.3.
Per tale periodo, tra gli esempi, riportero' una routine che permette di far diventare residente un programma scritto in linguaggio C.
Non ho avuto il tempo di rileggere il tutto, o meglio di farlo rileggere a mia moglie, alla ricerca di eventuali erroracci di grammatica.
Purtroppo questa non e' il mio forte.
Spero che non ci facciate molto caso e che capiate il fatto che mia moglie, se l'avessi costretta a rileggere circa 350 pagine, mi avrebbe eleminato fisicamente.
Ho cercato in questo testo di trattare andando alla ricerca di materiale un po' tutti gli aspetti del sistema Ms Dos.
Spero che in qualche caso potra' risultarvi utile.
Una eventuale collaborazione da parte vostra sara' gradita.
Sperando che non consideriate sciupate le 350 pagine utilizzate per stampare il file vi auguro
Buon intrafficamento !!!!
Fine
del manuale
Torna
al menù principale
Nuova pagina 1
|
|