Daniel' A.Norton. Drajvery ustrojstv v sisteme WINDOWS
---------------------------------------------------------------
Date: 14 Apr 1997
From: Kazennov Vladimir
Perevod Vladimira Kazennova
---------------------------------------------------------------
Drajvery ustrojstv, kak pravilo, - naibolee kriticheskaya chast'
programmnogo obespecheniya komp'yuterov. Po ironii sud'by eto takzhe i
naibolee skrytaya chast' razrabotki programmnogo obespecheniya. Drajvery
ustrojstv sistemy Windows firmy Microsoft ne yavlyayutsya isklyucheniem.
Esli vy kogda-libo pisali obychnoe prilozhenie v sisteme Windows, to
vam izvestno, chto trebuetsya opredelennoe kolichestvo skrytyh sposobov,
chtoby prilozhenie rabotalo nadezhno. Kak podmnozhestvo prilozhenij
Windows, drajvery ustrojstv sistemy Windows sleduyut etomu zhe pravilu.
V dannoj stat'e avtor rassmatrivaet rabotayushchij drajver ustrojstva,
kotoryj obespechivaet dostup k portam vvoda-vyvoda i obrabatyvaet
preryvaniya, i virtual'nyj drajver ustrojstva (VxD), kotoryj imitiruet
tehnicheskie sredstva. Predpolagaetsya, chto chitatel' znaet osnovy
programmirovaniya v sisteme Windows, vklyuchaya biblioteki dinamicheskoj
svyazi (dynamic link libraries - DLLs).
Rassmatrivaemoe ustrojstvo - eto ne chast' tehnicheskih sredstv,
kotoraya byla razrabotana, chtoby prodemonstrirovat', kak pisat'
drajver ustrojstva v sisteme Windows. Skoree, eto virtual'noe
ustrojstvo, polnost'yu realizovannoe v programmnom obespechenii.
Programma-primer vypolnyaetsya tol'ko s virtual'nym ustrojstvom,
kotoroe avtor opredelil rabotaya s sistemoj Windows v rasshirennom
rezhime (Enhanced mode) processora 386, i pri uslovii, chto ustanovlen
virtual'nyj drajver ustrojstva (VxD). Dalee v stat'e bolee detal'no
budet opisan ishodnyj kod dlya etogo ustrojstva. Na dannyj moment
sleduet znat', chto ustrojstvo imeet dva porta: port sostoyaniya i port
upravleniya, oba na odnom i tom zhe adrese. Na ris. 1 pokazany bity,
ispol'zuemye v porte sostoyaniya. Bit 2 ukazyvaet, chto imela mesto
oshibka ustrojstva, bit 1 pokazyvaet, chto zapros na preryvanie
yavlyaetsya otlozhennym, a bit 0 ukazyvaet, chto ustrojstvo zanyato. Bit 7
govorit o tom, chto ustrojstvo est' v nalichii. V etom sluchae dannyj
bit raven nulyu. Esli zhe ustrojstvo ne ustanovleno ili k nemu net
dostupa, to bit prinimaet znachenie, ravnoe 1.
+--------------------------------------------------------+
| 7 6...3 2 1 0 |
+---------+-------------------+---------+-------+--------+
| PRESENT | | ERROR | IRQ | BUSY |
+---------+-------------------+---------+-------+--------+
PRESENT - ustrojstvo est' v nalichii;
ERROR - proizoshla oshibka ustrojstva;
IRQ - preryvanie otlozheno;
BUSY - ustrojstvo zanyato.
(Ostal'nye bity ignoriruyutsya dlya dal'nejshej sovmestimosti.)
Ris. 1. Bity porta sostoyaniya ustrojstva
Na ris. 2 pokazany bity, ispol'zuemye v porte upravleniya. Bit 1
ukazyvaet ustrojstvu, chto CPU zakonchilo obrabotku preryvaniya. Bit 0
pokazyvaet, chto ustrojstvo mozhet nachat' obrabotku vvoda-vyvoda. (V
dannyj moment ne sleduet zaostryat' vnimanie na tom, chto fakticheski
ustrojstvo delaet. Vmesto etogo, neobhodimo udelit' vnimanie tomu,
kak napisat' drajver dlya takogo ustrojstva, kotoroe obespechivaet
apparatnye preryvaniya.)
+--------------------------------------------------------+
| 7...2 1 0 |
+---------------------------------------+-------+--------+
| 1 1 1 1 1 1 | EOI | START |
+---------------------------------------+-------+--------+
EOI - signal dlya ustrojstva, podtverzhdayushchij priem preryvaniya;
START - signal dlya ustrojstva nachinat' peresylku vvoda-vyvoda.
(Ostal'nye bity dolzhny byt' ustanovleny v 1 dlya dal'nejshej
sovmestimosti.)
Ris. 2. Bity porta upravleniya ustrojstva
Drajver ustrojstva v sisteme MS-DOS
Na listinge 1 pokazana programma dostest.asm, predstavlyayushchaya
soboj obychnyj drajver ustrojstva dlya sistemy MS-DOS, kotoryj obshchaetsya
s ustrojstvom. Nesmotrya na prostotu i malyj razmer dannaya programma
soderzhit osnovnye komponenty drajvera ustrojstva, kotoryj
obrabatyvaet preryvaniya.
_____________________________________________________________________
page ,132
; masm tisr ; >err
.286p
.xlist
include ..\..\include\bogus.inc
.list
Words struc
LoWord dw ?
HiWord dw ?
Words ends
EOI equ 020h ; komanda EOI dlya kontrollera PIC
INTA00 equ 020h ; upravlenie glavnym kontrollerom PIC
INTA01 equ 021h ; registr maski glavnogo kontrollera PIC
INT_MASTER_0 equ 08h ;nomer INT glavn. kontrollera PIC
INTB00 equ 0A0h ; upravlenie podchinennym kontrollerom PIC
INTB01 equ 0A1h ;registr maski podchinennogo kontrollera PIC
INT_SLAVE_0 EQU 70h ; nomer INT podchinennogo kontrollera PIC
;
; Ustanovit' peremennye dlya nashego nomera preryvaniya
;
ife (FAKE_IRQ GE 8)
INT_DEV equ (INT_MASTER_0+(FAKE_IRQ AND 7))
PIC00 equ INTA00
PIC01 equ INTA01
else
INT_DEV equ (INT_SLAVE_0+(FAKE_IRQ AND 7))
INT_MASK equ 1 SHL (FAKE_IRQ AND 7)
PIC00 equ INTB00
PIC01 equ INTB01
endif
page
CONST SEGMENT DWORD PUBLIC 'DATA'
sdNoBogus db 'I do not see the bogus device.',Odh,Oah,'$'
sdPrompt db Odh,Oah,'S)tart, or Q)uit: ','$'
sdCRLF db Odh,Oah,'$'
sdDot db '.','$'
CONST ENDS
DATA SEGMENT DWORD PUBLIC 'DATA'
dwCount1 dw 0
dwCount2 dw 0
lpPrevISR dd 0 ; adres predydushchej programmy ISR
fStopping db 0 ; znachenie TRUE pri zavershenii
DATA ENDS
STACK SEGMENT DWORD STACK 'STACK'
db 512 dup (?)
STACK ENDS
DGroup GROUP CONST,DATA,STACK
page
;IP IntSvcRtn - The Interrupt Service Routine (Programma obsluzhivaniya
; preryvaniya)
; WARNINGS (preduprezhdeniya)
;
; NOTES (primechaniya)
; Dannaya programma ISR uvelichivaet schetchik preryvanij (dwCount1)
; i zanovo maskiruet ustrojstvo.
;
; Esli ustanovlen flag "fStopping", ustrojstvo ne maskiruetsya zanovo.
;
FIXED_TEXT SEGMENT PARA PUBLIC 'CODE'
segData1 dw DGroup
assume CS:FIXED_TEXT,DS:NOTHING
IntSvcRtn proc far
push ax
push dx
push ds
mov ds,segDatal
assume ds:DGroup
inc dwCount1
mov al,NOT FAKE_CTL_EOI
mov dx,FAKE_PORT
out dx,al ; poslat' EOI ustrojstvu
mov al,EOI
out PIC00,al ; poslat' EOI kontrolleru PIC
ife (PIC00 EQ INTA00)
out INTA00,al ; poslat' EOI glavn. kontrolleru PIC takzhe
endif
cmp fStopping,0 ; sushchestvuet?
jnz isr9 ; esli da, to ne nuzhen perezapusk
mov al,NOT FAKE_CTL_START
mov dx,FAKE_PORT
out dx,al ; perezapusk vvoda-vyvoda
isr9:
pop ds
assume ds:NOTHING
pop dx
pop ax
iret
IntSvcRtn endp
FIXED_TEXT ENDS
page
;IP_main - tochka vhoda v programmu
; NOTES (primechaniya)
; Drajver ustrojstva vydaet dlya pol'zovatelya priglashenie:
;S)tart(nachat') ili Q)uit(vyjti). Esli pol'zovatel' nazhimaet S,
;programma razreshaet preryvaniya i vooruzhaet ustrojstvo, pechataya tochku
;kazhdyj raz, kak ustrojstvo preryvaetsya.
;
_TEXT SEGMENT PARA PUBLIC 'CODE'
segData2 dw DGroup
segfixed dw FIXED_TEXT
assume cs:_TEXT,ds:NOTHING
_main label far
mov ds,segData2 ;inicializiruetsya segment dannyh po umolchaniyu
assume ds:DGroup
mov dx,FAKE_PORT
in al,dx ; prisutstvuet li fiktivnoe ustrojstvo?
or al,al
jns m10 ;propustit', esli da
mov dx,OFFSET DGroup:sdNoBogus
mov ah,9
int 21h ; v protivnom sluchae pechatat' soobshchenie ob oshibke
mov ax,4C01h
int 21h ; i vyjti iz sistemy
m10:
mov ax,3500h+INT_DEV
cli
int 21h ; zaprosit' tekushchuyu programmu ISR
mov lpPrevISR.LoWord,bx
mov lpPrevISR.HiWord,es ; sohranit' ee
mov dx,OFFSET FIXED_TEXT:IntSvcRtn
push ds
mov ds,segFixed
assume ds:NOTHING
mov ax,2500h+INT_DEV
int 21h ; ustanovit' nashu programmu ISR
pop ds
assume ds:DGroup
sti
mov dx,OFFSET DGroup:sdPrompt
mov ax,9
int 21h ; S)tart ili Q)uit
ml1:
mov dl,0PFh
mov ah,6
int 21h ; chitat' s konsoli, ne ozhidaya
jz ml3
or al,40h
cmp al,'q'
je ml8 ; propustit', esli nazhato "Q"
cmp al,'s'
jne ml3 ; propustit', esli ne nazhato "S"
cli
in al,PIC01 ; razmaskirovat' preryvanie
and al,NOT INT_MASK
out PIC01,al
sti
mov al,NOT FAKE_CTL_START
mov dx,FAKE_PORT
out dx,al ; nachat' vvod-vyvod s ustrojstva
ml3:
mov ax,dwCount1
cmp ax,dwCount2
je ml4 ; propustit', esli schetchik preryvanij ne izmenilsya
mov dwCount2,ax
mov dx,OFFSET DGroup:sdDot
mov ah,9
int 21,h ;v protivnom sluchae vydat' tochku
ml4:
jmp ml1 ; cikl
ml8:
mov fStopping,1 ; ukazanie dlya programmy ISR zavershit' rabotu
mov dx,FAKE_PORT
ml9:
in al,dx
rcr al,1
jnc ml9 ; cikl, esli zanyato
cli
in al,PIC01
or al,INT_MASK
out PIC01,al ; maskirovat' uroven' preryvaniya
sti
push ds
lds dx,lpPrevISR
assume ds:NOTHING
mov ax,2500h+INT_DEV
int 21h ; vosstanovit' predydushchuyu programmu ISR
pop ds
assume ds:DGroup
mov ax,4C00h
int 21h ; vyhod
_TEXT ENDS
end_main
; Konec fajla
_____________________________________________________________________
Listing 1. Programma dostest.asm
Drajver ustrojstva nachinaet rabotu s proverki starshego bita
porta sostoyaniya, chtoby ubedit'sya v nalichii ustrojstva. Zatem on
ustanavlivaet svyaz' s vektorom preryvaniya MS-DOS dlya preryvaniya 11.
Drajver sohranyaet predydushchee znachenie, hranimoe v etom vektore, tak
chtoby mozhno bylo zamenit' znachenie, esli programma sushchestvuet.
Dalee drajver ustrojstva vydaet priglashenie dlya pol'zovatelya :
Start(nachat') ili Quit(vyjti). Esli pol'zovatel' nazhimaet S,
programma nachinaet peresylku vvoda-vyvoda. Esli pol'zovatel' nazhimaet
Q, to programma otklyuchaet ustrojstvo, vosstanavlivaet vektor
preryvaniya i zavershaetsya.
CHtoby nachat' operaciyu vvoda-vyvoda, drajver MS-DOS snachada
razmaskiruet programmiruemyj kontroller preryvanij (programmable
interrupt controller - PIC) dlya urovnya preryvaniya ustrojstva (v
primere preryvanie 11). Zatem drajver nachinaet operaciyu vvoda-vyvoda
dlya ustrojstva putem zapisi 1 v bit 0 porta upravleniya. Tak kak
preryvaniya vklyucheny, to pri vozniknovenii preryvanij na ustrojstve
poluchit upravlenie programma obsluzhivaniya preryvanij (interrupt
service routine - ISR).
Esli proishodit preryvanie na ustrojstve, to programma ISR
podtverzhdaet priem preryvaniya, posylaya znachenie EOI ustrojstvu (t.e.
zapisyvaya 1 v bit 1 porta upravleniya ustrojstva) i kontrolleru PIC.
Esli programma, vypolnyayushchaya vvod-vyvod, sushchestvuet, to programma ISR
vypolnyaetsya. V protivnom sluchae programma ISR osushchestvlyaet
inicializaciyu peresylki vvoda-vyvoda vnov', zapisyvaya 1 v bit 0 porta
upravleniya ustrojstva. Itak, programma ISR vozobnovlyaet vvod-vyvod
vsyakij raz, kogda proishodit preryvanie, takim obrazom ustrojstvo
nepreryvno vypolnyaet operaciyu vvoda-vyvoda. Krome obespecheniya
nepreryvnogo vvoda-vyvoda programma ISR uvelichivaet schetchik (dwCount1)
vsyakij raz, kogda obrabatyvaet preryvanie.
V processe vypolneniya vvoda-vyvoda programma sledit za schetchikom
preryvanij, otobrazhaet tochku (".") dlya kazhdoj zakonchennoj peresylki
vvoda-vyvoda i prodolzhaet skanirovat' klaviaturu, chtoby opredelit',
hochet li pol'zovatel' ostanovit' peresylku.
CHtoby zavershit' programmu, pol'zovatel' nazhimaet klavishu Q.
Programma ustanavlivaet flag, kotoryj informiruet programmu ISR o
tom, chto sleduet ostanovit' obrabotku. Posle togo, kak operaciya
vvoda-vyvoda ostanovlena, programma maskiruet uroven' preryvaniya v
kontrollere PIC i vosstanavlivaet vektor preryvaniya.
Drajver ustrojstva v sisteme Windows
CHrezvychajno trivial'nyj drajver ustrojstva MS-DOS, opisannyj
v predydushchem razdele, po sushchestvu dovol'no slozhno realizovat' v
sisteme Windows. Pri napisanii drajvera ustrojstva v sisteme Windows,
obrabatyvayushchego preryvaniya, neobhodimo ispol'zovat' arhitekturu,
otlichnuyu ot toj, kotoraya byla ispol'zovana dlya drajvera MS-DOS. V
chastnosti, neobhodimo otdelit' komponentu obrabotki preryvaniya ot
komponenty prilozheniya. Vmesto edinstvennoj programmy, upravlyayushchej kak
programmoj ISR, tak i interfejsom pol'zovatelya, kak sdelano v sisteme
MS-DOS, v sisteme Windows neobhodimo vydelit' eti funkcii v otdel'nye
programmnye moduli, nazyvaemye bibliotekoj dinamicheskoj svyazi (DLL) i
interfejsom prikladnyh programm (Application Program Interface - API).
Biblioteka DLL dlya drajvera
Pri napisanii prilozhenij v sisteme Windows obychno v programmnom
module imeyut delo tol'ko s dvumya tipami segmentov: peremeshchaemym
(moveable) i vygruzhaemym (discardable). Segmenty dannyh programmy
yavlyayutsya peremeshchaemymi, t.e. ih linejnye adresa v pamyati mogut
izmenyat'sya, kogda programme upravleniya pamyat'yu sistemy Windows
trebuetsya organizovat' pamyat'. Selektor (selector) i smeshchenie,
ispol'zuemye dlya dostupa k opredelennoj yachejke pamyati, ostayutsya
fiksirovannymi, no pod shemoj selektor-smeshchenie sistema Windows
mozhet peremeshchat' fakticheskie dannye v linejnoj pamyati.
Segmenty programm-kodov takzhe peremeshchaemye, no imeyut
dopolnitel'nyj atribut - vygruzhaemye. Ih soderzhimoe mozhet byt'
vygruzheno polnost'yu, a pri neobhodimosti zagruzheno s diska, tak kak
nel'zya pisat' i (ili) modificirovat' informaciyu v segmente
programmy-koda. Esli pri obrashchenii k segmentu iz programmy Windows,
on okazalsya vygruzhennym, programma upravleniya pamyat'yu sistemy Windows
avtomaticheski obratitsya k disku i prochitaet ranee vygruzhennyj segment.
Itak, kakim obrazom eto obstoyatel'stvo vliyaet na kod dlya
programmy ISR? Tak kak preryvanie mozhet proizojti v lyuboe vremya, a
kod ISR mozhet okazat'sya vygruzhennym, to vozniknet problema zagruzit'
kod v pamyat', esli fiksiruetsya preryvanie. Vmesto etogo, mozhno
opisat' segment kak FIXED (fiksirovannyj), a ne kak MOVEABLE
(peremeshchaemyj) ili DISCARDABLE (vygruzhaemyj). Segment s atributom
FIXED budet ostavat'sya v edinstvennom meste linejnoj pamyati i ne
budet vygruzhat'sya, dazhe esli on soderzhit kod. V etom sluchae, esli
proizojdet preryvanie, kod budet dostupen i gotov k vypolneniyu.
Odnako sleduet otmetit' odin maloizvestnyj fakt, a imenno: v sisteme
Windows tol'ko te segmenty budut schitat'sya FIXED, kotorye byli
opisany v biblioteke DLL. Segment FIXED v obychnom programmnom module
budet rassmatrivat'sya kak MOVEABLE. Takim obrazom v sisteme Windows
nel'zya budet pomestit' programmu ISR v obychnyj programmnyj modul'.
Vmesto etogo ee neobhodimo pomestit' v biblioteku DLL.
Listing 2 predstavlyaet ishodnyj kod bogusa.asm na assemblere dlya
biblioteki DLL, kotoryj soderzhit programmu ISR i mozhet vypolnyat'sya v
okruzhenii Windows. Programma IntSvcRtn ochen' pohozha na svoj dublikat,
rabotayushchij v sisteme MS-DOS. Odnako krome uvelicheniya peremennoj-
schetchika dannaya programma ISR takzhe zapisyvaet v ochered' soobshchenie
Windows. CHtoby izbezhat' perepolneniya ocheredi, zapis' soobshcheniya
proizvoditsya tol'ko v sluchae, kogda peremennaya-schetchik wCount
izmenyaet znachenie ot 0 k 1. Funkciya obnuleniya schetchika wCount posle
togo, kak zakonchena obrabotka soobshcheniya, peredana vysokourovnevoj
programme sistemy Windows.
S pervogo vzglyada vse eti rassuzhdeniya kazhutsya prostymi, odnako
obrabotka preryvanij v sisteme Windows sovsem ne tak prosta, kak v
sisteme MS-DOS.
_____________________________________________________________________
page ,132
; masm tisr ; >err
.286p
.xlist
include bogus.inc
include pic.h
.list
WM_COMMAND =0111h
EXTRN POSTMESSAGE:FAR
Words struc
LoWord dw ?
HiWord dw ?
Words ends
;
; Ustanovit' peremennye dlya nashego nomera preryvaniya
;
ife (FAKE_IRQ GE 8)
INT_DEV equ (INT_MASTER_0+(FAKE_IRQ AND 7))
PIC00 equ INTA00
PIC01 equ INTA01
else
INT_DEV equ (INT_SLAVE_0+(FAKE_IRQ AND 7))
INT_MASK equ 1 SHL (FAKE_IRQ AND 7))
PIC00 equ INTB00
PIC01 equ INTB01
endif
page
FIXED_DATA SEGMENT DWORD PUBLIC 'DATA'
PUBLIC _hWndEvent,_wParamEvent,_wCount
_hWndEvent label word
hWndEvent dw 0 ; Okno dlya postirovaniya sobytij
_wParamEvent label word
wParamEvent dw 0 ; Znachenie wParam dlya postirovaniya
_wCount label word
wCount dw 0 ; Schetchik neobrabotannyh preryvanij
FIXED_DATA ENDS
page
; IP IntSvcRtn - programma obsluzhivaniya preryvanij
;
; WARNINGS (Preduprezhdeniya)
;
; NOTES (Primechaniya)
; Dannaya programma ISR uvelichivaet schetchik preryvanij i zanovo
; maskiruet ustrojstvo.
; Esli predydushchee znachenie schetchika bylo ravno 0, to zapisyvaetsya
; soobshchenie
; Esli ustanovlen flag "fStopping", ustrojstvo ne maskiruetsya zanovo.
;
FIXED_TEXT SEGMENT PARA PUBLIC 'CODE'
selData1 dw FIXED_DATA
assume CS:FIXED_TEXT,DS:NOTHING
PUBLIC _IntSvcRtn
_IntSvcRtn label far
IntSvcRtn proc far
push ax
push dx
push ds
mov ds,selDatal
assume ds:FIXED_DATA
inc wCount
mov al,NOT FAKE_CTL_EOI
mov dx,FAKE_PORT
out dx,al ; posylaem ustrojstvu EOI
mov al,EOI
out PIC00,al ; posylaem EOI kontrolleru PIC
ife (PIC00 EQ INTA00)
out INTA00,al ; posylaem EOI takzhe glavnomu kontrolleru PIC
endif
cmp hWndEvent,0 ; zavershat'?
jz isr9 ; esli da, to ne delaem perezapuska i
; postirovaniya
cmp wCount,1 ; trebuetsya postirovanie?
jne isr8 ; propuskaem, esli net
push bx ; sohranyaem ostavshiesya registry
push cx
push es
push hWndEvent
push WM_COMMAND
push wParamEvent
push 0 ; lParam ravno 0
push 0
call POSTMESSAGE ; registriruem sobytie
pop es
pop cx
pop bx
isr8:
mov al,NOT FAKE_CTL_START
mov dx,FAKE_PORT
out dx,al ; vozobnovlyaem vvod-vyvod
isr9:
pop ds
assume ds:NOTHING
pop dx
pop ax
iret
IntSvcRtn endp
; trebuetsya programme AllocIntReflector
PUBLIC _BogusCallback
_BogusCallback label far
BogusCallback proc far
pushf
call IntSvcRtn
ret
BogusCallback endp
FIXED_TEXT ENDS
end
; konec fajla
_____________________________________________________________________
Listing 2. Programma bogusa.asm
Krome otdel'nogo programmnogo modulya dlya programmy ISR (v forme
biblioteki DLL sistemy Windows), dlya raboty drajvera neobhodim takzhe
programmnyj modul' pol'zovatel'skogo interfejsa, nazyvaemyj interfejs
API. Na listinge 3 privedena programma bogus.h, predstavlyayushchaya soboj
primer interfejsa API. |ta programma soderzhit 4 tochki vhoda v
biblioteku DLL.
_____________________________________________________________________
#ifndef EXPORT
#define EXPORT
#endif
extern int EXPORT FAR PASCAL BogusCheck(void) ;
extern void EXPORT FAR PASCAL BogusStart(HWND hWnd,WPARAM wParam) ;
extern int EXPORT FAR PASCAL BogusGetEvent(void) ;
extern void EXPORT FAR PASCAL BogusStop(void) ;
/* Konec fajla */
______________________________________________________________________
Listing 3. Programma bogus.h.
V tochke vhoda BogusCheck prosto proveryaetsya nalichie ustrojstva.
Programma vozvrashchaet znachenie TRUE, esli ustrojstvo obnaruzheno (bit 7
porta sostoyaniya), i znachenie FALSE v protivnom sluchae.
Tochki vhoda BogusStart i BogusStop nachinayut i zavershayut rabotu
ustrojstva. Krome togo, tochka vhoda BogusStart razreshaet preryvaniya i
obespechivaet svyaz' s apparatnym preryvaniem, a tochka vhoda BogusStop
vyklyuchaet preryvaniya ustrojstva i vosstanavlivaet apparatnoe
preryvanie.
Tochka vhoda BogusGetEvent vozvrashchaet kolichestvo preryvanij,
obrabotannyh so vremeni pervogo starta ustrojstva, libo so vremeni
poslednego vyzova tochki vhoda BogusGetEvent. (Tochka vhoda
BogusGetEvent obnulyaet schetchik preryvanij pri kazhdom ee vyzove.)
Preryvaniya pri standartnom rezhime raboty sistemy Windows
Pri napisanii drajvera, kotoryj budet vypolnyat'sya v standartnom
rezhime raboty sistemy Windows, neobhodimo uchityvat' vozmozhnost'
poyavleniya preryvaniya, kogda processor rabotaet v real'nom rezhime.
Dazhe esli rabotayut tol'ko prilozheniya sistemy Windows, a ne prilozheniya
sistemy MS-DOS, processor chasto pereklyuchaetsya iz real'nogo v
zashchishchennyj rezhim. Tak kak sistema Windows 3.1 ne yavlyaetsya
operacionnoj sistemoj, a skoree predstavlyaet soboj okruzhenie
pol'zovatel'skogo interfejsa, ona vozlagaet vypolnenie opredelennogo
kolichestva osnovnyh funkcij, vklyuchaya funkciyu vvoda-vyvoda fajlov, na
operacionnuyu sistemu (a imenno MS-DOS).
Poetomu kogda prilozhenie sistemy Windows vypolnyaet funkciyu
MS-DOS vvoda-vyvoda fajla i processor pri etom rabotaet v real'nom
rezhime, ustrojstvo mozhet preryvat' CPU. Po umolchaniyu, esli biblioteka
DLL obespechila svyaz' s preryvaniem, to sistema Windows pereklyuchit CPU
v zashchishchennyj rezhim dlya obrabotki preryvaniya i, kak tol'ko programma
ISR zavershit rabotu, pereklyuchit CPU obratno v real'nyj rezhim dlya
prodolzheniya vypolneniya funkcij sistemy MS-DOS.
Hotya eto v men'shej mere otnositsya k CPU 80386, pereklyuchenie
processora iz zashchishchennogo rezhima v real'nyj rezhim, naprimer na
processore 80286, sozdaet ogromnye nakladnye rashody, trebuyushchie
kontroliruemogo sbrosa CPU, kotoryj vypolnyaetsya v techenii
millisekund. Esli neobhodimo uskorit' srednee vremya otveta, nuzhno
predotvratit' pereklyuchenie processora v zashchishchennyj rezhim, esli on
poluchaet preryvanie, rabotaya v real'nom rezhime.
Obespechenie svyazi s vektorom preryvaniya v zashchishchennom rezhime iz
biblioteki DLL sistemy Windows - trivial'no, chto i pokazano v
programme SetPMVector, predstavlennoj v listinge 4 (programma
bogus.c). Ustanovlenie svyazi s vektorom proizvoditsya takim zhe
sposobom, kak i v sisteme MS-DOS, - s pomoshch'yu funkcii setvector
sistemy MS-DOS. Odnako v otlichie ot vyzova v sisteme MS-DOS, v
sisteme Windows pri obrashchenii k funkcii peredayutsya selektor i
smeshchenie, a ne segment i smeshchenie. YAdro sistemy Windows sledit za
vsem. Funkcii sleduet peredat' normal'nyj selektor i smeshchenie
(natural'nyj ukazatel' far dlya sistemy Windows), a ne segment i
smeshchenie (natural'nyj ukazatel' far dlya sistemy MS-DOS).
Odnako, kak uzhe upominalos', ustanovleniya svyazi s vektorom
preryvaniya v zashchishchennom rezhime nedostatochno. Neobhodimo takzhe
obespechit' svyaz' s vektorom preryvaniya v real'nom rezhime, a eto ne
trivial'naya zadacha.
______________________________________________________________________
/*EM BOGUS.C - Drajver fiktivnogo ustrojstva biblioteki DLL
*
* SUMMARY (Rezyume)
* Bazovye funkcii LibMain, WEP
*
* COMMENTS (Kommentarii)
*
* WARNINGS (Preduprezhdeniya)
*
*/
#include
#include "bogusa.h"
#include "pic.h"
#include "dpmi.h"
#define EXPORT _export _loadds
#include "bogus.h"
#define FAKE_PORT 0x141 /* Uroven' fiktivnosti (bogosity) - 9.4 */
#define FAKE_IRQ 11 /* Uroven' fiktivnosti (bogosity) - 9.8 */
#define FAKE_CTL_START 0x01
/* komanda "nachat'" fiktivnogo porta (ustanavlivaetsya v nul') */
#define FAKE_CTL_EOI 0x02
/* EOI fiktivnogo porta */
#define FAKE_STAT_BUSY 0x01
/* indikaciya zanyatosti fiktivnogo porta (zero=>busy) */
#define FAKE_STAT_IRQ 0x02
/* IRQ fiktivnogo porta (zero=>IRQ) */
#define FAKE_STAT_ERROR 0x04
/* oshibka vvoda-vyvoda (zero=>error) (sbrasyvaetsya pri chtenii) */
/* Ustanovit' peremennye dlya nashego nomera preryvaniya */
#if (FAKE_IRQ<8)
#define INT_DEV (INT_MASTER_0+(FAKE_IRQ & 7))
#define PIC00 INTA00
#define PIC01 INTA01
#else
#define INT_DEV (INT_SLAVE_0+(FAKE_IRQ & 7))
#define PIC00 INTB00
#define PIC01 INTB01
#endif
#define INT_MASK (1 << (FAKE_IRQ & 7))
BOOL FAR PASCAL LibMain(HANDLE hInstance
/* obrabotchik bibliotechnogo ekzemplyara*/
,WORD wDataSeg
/* segment dannyh po umolchaniyu */
,WORD cbHeap
/* razmer dinamicheskoj oblasti po umolchaniyu */
,LPSTR lpszCmdLine) ;
/* komandnaya stroka */
int FAR PASCAL WEP(int fSystemExit) ;
#pragma alloc_text(INIT_TEXT,LibMain)
/* derzhat' vmeste s LIBENTRY.ASM */
#pragma alloc_text(FIXED_TEXT,WEP)
HANDLE hLibInstance ;
FARPROC lpfnPrevISR ; /* Sohranennaya predydushchaya programma ISR*/
DWORD lpfnPrevRMISR ;
/* Sohranennaya predydushchaya programma ISR real'nogo rezhima*/
HANDLE hReflector ;
DWORD DPMI_AllocateRMCallback(FARPROC lpfnCallback,
_RMCS FAR *lpRMCS)
{
DWORD dwRet ;
_asm {
push ds
lds si,lpfnCallback
les di,lpRMCS
mov ax,DPMI_ALLOCRMC
int IVEC_DPMI
pop ds
jc lbl1
mov word ptr dwRet,dx ; vozvrat adresa obratnogo vyzova
mov word ptr dwRet+2,cx
jmp short lbl2
lbl1:
mov word ptr dwRet,ax ; kod oshibki v registre ax
mov word ptr dwRet+2,0 ; vozvratit' seg=0,esli proizoshla oshibka
lbl2:
}
return dwRet ;
}
DWORD DPMI_FreeRMCallback(FARPROC lpfnCallback)
{
DWORD wRet ;
_asm {
mov dx,word ptr lpfnCallback
mov cx,word ptr lpfnCallback+2
mov ax,DPMI_FREERMC
int IVEC_DPMI
jc lbl1
xor ax,ax
lbl1:
mov wRet,ax
}
return wRet ;
}
DWORD DPMI_GetRMVector(int iVector)
{
DWORD dwRet ;
_asm {
mov ax,DPMI_GETRMVEC
mov bl,byte ptr iVector
int 31h
mov word ptr dwRet,dx
mov word ptr dwRet+2,cx
}
return dwRet ;
}
void DPMI_SetRMVector(int iVector, DWORD lpfnRMISR)
{
_asm {
mov ax,DPMI_SETRMVEC
mov bl,byte ptr iVector
mov dx,word ptr lpfnRMISR
mov cx,word ptr lpfnRMISR+2
int 31h
}
}
FARPROC GetPMVector(int iVector)
{
FARPROC dwRet ;
_asm {
mov bl,byte ptr iVector
mov ah,35h
int 21h
mov word ptr dwRet,bx
mov word ptr dwRet+2,es ; Sohranit'
}
return dwRet ;
}
void SetPMVector(int iVector, FARPROC lpfnISR)
{
_asm {
push ds
lds dx,lpfnISR
mov al,byte ptr iVector
mov ah,25h
int 21h ; Ustanovit' nashu programmu ISR
pop ds
}
}
HANDLE AllocIntReflector(int iVector, FARPROC lpfnCallback)
{
DWORD dwDosMem ;
LPSTR lpLowRMISR ;
DWORD lpfnRMCallback ;
_RMCS FAR *lpSaveRegs ;
/* Raspredelit' pamyat' DOS dlya programmy obsluzhivaniya preryvaniya ISR,
*rabotayushchej v real'nom rezhime */
dwDosMem = GlobalDosAlloc(16 + sizeof (int) + sizeof (_RMCS) ;
if (dwDosMem == 0)
return 0;
lpLowRMISR = (LPSTR) MAKELONG(0,LOWORD(dwDosMem)) ;
lpSaveRegs = (_RMCS FAR *) (&lpLowRMISR[16]) ;
/* Raspredelit' obratnyj vyzov (callback), rabotayushchij v real'nom
* rezhime */
lpfnRMCallback =
DPMI_AllocateRMCallback((FARPROC)lpfnCallback,
lpSaveRegs)
;
if (HIWORD((DWORD)lpfnRMCallback == 0)
{
GlobalDosFree(LOWORD(dwDosMem)) ;
return 0;
}
/* Sgenerirovat' kod v nizhnih adresah pamyati (tol'ko 6 bajtov)*/
lpLowRMISR[0] = 0x9A ; /* Vyzov ukazatelya na FAR */
*((DWORD FAR *)&(lpLowRMISR[1])) = lpfnRMCallback ;
lpLowRMISR[5] = 0xCF ; /*IRET */
*((int FAR *)&(lpLowRMISR[6])) = iVector ;
/* Ustanovit' svyaz' s vektorom preryvanij real'nogo rezhima */
DPMI_SetRMVector(iVector,MAKELONG(0,HIWORD(dwDosMem))) ;
return (HANDLE) LOWORD(dwDosMem) ;
/* vozvrat obrabotchika-otrazhatelya */
}
void FreeIntReflector(HANDLE hReflector)
{
LPSTR lpLowRMISR ;
DWORD lpfnRMCallback ;
/* Poluchit' adres nizhnego ISR v zashchishchennom rezhime */
lpLowRMISR = (LPSTR)MAKELONG(0,(WORD)hReflector) ;
/* Sleduet ubedit'sya, chto eto otrazhatel' */
if ((lpLowRMISR[0] != 0x9A) || (lpLowRMISR[5] != 0xCF))
return ; /* vyhod, esli ne otrazhatel' */
/* Vybrat' adres obratnogo vyzova i osvobodit' obratnyj vyzov */
lpfnRMCallback = *((DWORD FAR *)&((lpLowRMISR[1])) ;
DPMI_FreeRMCallback(lpfnRMCallback) ;
/* Osvobodit' programmu obsluzhivaniya preryvanij real'nogo rezhima*/
GlobalDosFree((WORD)hReflector) ;
}
/*XP< LibMain - osnovnaya bibliotechnaya tochka vhoda */
*
* ENTRY (vhod)
*
* EXIT (vyhod)
*
* RETURNS (vozvrat)
* Esli inicializaciya zavershaetsya uspeshno prinimaet znachenie, ravnoe
* TRUE, v protivnom sluchae - FALSE
*
* WARNINGS (preduprezhdeniya)
*
* CALLS (vyzovy)
*
* NOTES (primechanie)
* Nastoyashchaya bibliotechnaya tochka vhoda nahoditsya v assemblernom module
* LIBENTRY.ASM, a v dannuyu tochku prosto peredaetsya upravlenie
*
*/
BOOL FAR PASCAL LibMain(HANDLE hInstance
/* obrabotchik bibliotechnogo ekzemplyara*/
,WORD wDataSeg
/* segment dannyh po umolchaniyu */
,WORD cbHeap
/* razmer dinamicheskoj oblasti po umolchaniyu */
,LPSTR lpszCmdLine) ;
/* komandnaya stroka */
/*>*/
{
lpszCmdLine = lpszCmdLine ;
/* Izbegat' preduprezhdeniya -W4 */
wDataSeg = wDataSeg ;
cbHeap = cbHeap ;
hInstance = hInstance ;
/* |to mozhet ponadobit'sya pozzhe dlya dostupa k resursam iz nashego
*ispolnitel'nogo modulya */
return TRUE ;
}
/*XP< WEP - procedura vyhoda v sisteme Windows */
*
* ENTRY (vhod)
* fSystemExit ukazyvaet na zavershenie sessii v sisteme Windows. V
* protivnom sluchae proishodit tol'ko razgruzka dannoj biblioteki DLL.
* RETURNS (vozvrat)
* Vsegda vozvrashchaetsya znachenie 1
*
* WARNINGS (preduprezhdeniya)
* Iz-za oshibok v sisteme Windows 3.0 i bolee rannih versiyah (a
* vozmozhno i v bolee pozdnih versiyah) dannaya funkciya dolzhna byt'
*pomeshchena v fiksirovannyj segment. |ti zhe oshibki privodyat k tomu, chto
* znachenie DS somnitel'no, a poetomu nel'zya ego ispol'zovat' (takzhe
* kak i lyubye staticheskie dannye).
*
* V lyubom sluchae, nesomnenno ne nado nichego delat' v etoj tochke.
*
* CALLS (vyzovy)
* Net
* NOTES (primechaniya)
* |to standartnaya procedura vyhoda DLL.
*
*/
int FAR PASCAL WEP(int fSystemExit)
/*>*/
{
fSystemExit = fSystemExit
/* Izbegat' preduprezhdeniya -W4 */
return 1 ; /* vsegda ukazyvaet na uspeshnoe zavershenie */
}
int EXPORT FAR PASCAL BogusCheck(void)
{
BYTE bPortVal ;
_asm {
mov dx,FAKE_PORT
in al,dx ; Prisutsvuet fiktivnoe ustrojstvo ?
mov bPortVal,al
}
return ((bPortVal & 0x80) == 0) ;
/* Vozvrashchaet znachenie TRUE, esli ustrojstvo prisutstvuet */
}
void EXPORT FAR PASCAL BogusStart(HWND hWnd, WPARAM wParam)
{
wParamEvent = wParam ;
hWndEvent = hWnd ;
if (!lpfnPrevISR)
{
/* Sohranit' predydushchuyu programmu ISR i zagruzit' novuyu */
_asm cli
lpfnPrevISR = GetPMVector(INT_DEV) ;
SetPMVector(INT_DEV,(FARPROC)IntSvcRtn) ;
_asm sti
/* Sohranit' predydushchuyu programmu ISR real'nogo rezhima i
*otrazit' na novuyu */
lpfnPrevRMISR = DPMI_GetRMVector(INT_DEV) ;
hReflector = AllocIntReflector(INT_DEV,(FARPROC)BogusCallback) ;
/* Razreshit' preryvanie i nachat' operaciyu vvoda-vyvoda na
*ustrojstve */
_asm {
cli
in al,PIC01 ; razreshit' preryvanie
and al,NOT INT_MASK
out PIC01,al
sti
mov al,NOT FAKE_CTL_START
mov dx,FAKE_PORT
out dx,al ;nachat' operaciyu vvoda-vyvoda na ustrojstve
}
}
}
int EXPORT FAR PASCAL BogusGetEvent(void)
{
WORD wCountRet ;
_asm {
mov ax,SEG wCount
mov es,ax
xor ax,ax
xchg ax,es:wCount ;poluchit' schetchik, ustanovit' v nul'
mov wCountRet,ax
}
return wCountRet ;
}
void EXPORT FAR PASCAL BogusStop(void)
{
hWndEvent - 0x0000 ; /*komanda dlya ISR "zavershit' rabotu"*/
if (!lpfnPrevISR)
return ; /* vozvratit'sya, esli programma ne startovala */
_asm {
mov dx,FAKE_PORT
l1:
in al,dx
rcr al,1
jnc l1 ; cikl, esli zanyato
cli
in al,PIC01
or al,INT_MASK
out PIC01,al ; maskirovat' uroven' preryvaniya
sti
}
DPMI_SetRMVector(INT_DEV, lpfnPrevRMISR) ;
/* Vosstanovit' vektor real'nogo rezhima */
FreeIntReflector(hReflector) ;
/* Osvobodit' otrazhatel' */
SetPMVector(INT_DEV, lpfnPrevISR) ;
/* Vosstanovit' vektor zashchishchennogo rezhima */
lpfnPrevISR = NULL ;
}
/* konec fajla*/
_____________________________________________________________________
Listing 4. Programma bogus.c
Interfejs sistemy MS-DOS dlya zashchishchennogo rezhima
CHtoby ustanovit' svyaz' s vektorom real'nogo rezhima iz koda
sistemy Windows zashchishchennogo rezhima, neobhodimo rabotat' s interfejsom
sistemy MS-DOS dlya zashchishchennogo rezhima (MS-DOS Protected Mode
Interface - DPMI). (Tekushchaya versiya DPMI predstavlyaet soboj uroven'
1.0, no sistema Windows naibolee polno realizuet tol'ko uroven' 0.9.
Nekotorye funkcii urovnya 1.0 realizovany v sisteme Windows 3.1.)
Funkciya DPMI_SetRMVector vyzyvaet interfejs DPMI, chtoby
ustanovit' vektor real'nogo rezhima. Mozhno videt', chto interfejs DPMI
vzaimodejstvuet cherez registry (registr AX soderzhit funkcional'nyj
kod) i INT31h. Avtor vklyuchil vysokourovnevyj interfejs v dannuyu
i drugie funkcii DPMI (dostupen tol'ko na diske kodov ili v
interaktivnom rezhime), chtoby mozhno bylo imet' dostup k interfejsu
DPMI iz yazyka Si i vydelil kod, napisannyj na yazyke assembler, na
sluchaj, esli vozniknet neobhodimost' ispol'zovat' chto-to otlichnoe ot
kompilyatora Si firmy Microsoft.
Funkciya DPMI_AllocateRMCallback vyzyvaet interfejs DPMI, chtoby
raspredelit' obratnyj vyzov (callback), predstavlyayushchij soboj adres,
vyzyvaemyj iz real'nogo rezhima, kotoryj peredaet upravlenie kodu
zashchishchennogo rezhima. Naprimer, programma TSR sistemy MS-DOS mozhet
vyzvat' kod v biblioteke DLL sistemy Windows cherez obratnyj vyzov.
Funkciya DPMI_AllocateRMCallback prinimaet dva parametra: adres
koda zashchishchennogo rezhima, kotoryj budet vyzyvat'sya obratno, i
registrovuyu strukturu, kotoraya obnovlyaetsya pri vypolnenii real'nogo
obratnogo vyzova, takim obrazom kod zashchishchennogo rezhima mozhet
issledovat' soderzhimoe registrov real'nogo rezhima vo vremya obratnogo
vyzova.
Funkciya DPMI_FreeRMCallback osvobozhdaet vse struktury, kotorye
byli raspredeleny v rezul'tate obrashcheniya k funkcii
DPMI_AllocateRMCallback. Funkciya DPMI_FreeRMCallback dolzhna vyzyvat'sya
tol'ko togda, kogda bol'she net neobhodimosti v obratnom vyzove.
Programma ISR v real'nom rezhime
Nesmotrya na to, chto avtor rekomendoval obespechivat' razdel'nuyu
programmu ISR v real'nom rezhime, v dannom primere eta rekomendaciya ne
byla vypolnena. Vmesto etogo, avtor predostavil programmy,
neobhodimye pri realizacii programmy ISR na yazyke Si. Fakticheski,
dannyj primer ustanavlivaet svyaz' s preryvaniyami real'nogo rezhima
tol'ko dlya togo, chtoby pereklyuchit' CPU v zashchishchennyj rezhim dlya
obrabotki preryvaniya. Takovo po umolchaniyu povedenie sistemy Windows,
kogda s preryvaniyami real'nogo rezhima ne ustanavlivaetsya svyaz'
voobshche, takim obrazom avtor rassmatrivaet neskol'ko ciklov, kotorye
ne imeyut nikakogo drugogo naznacheniya, krome kak pokazat', kakim
obrazom vse rabotaet.
Rassmotrim kod dlya tochki vhoda BogusStart. Po sushchestvu on
rabotaet tak zhe, kak rabotal by v sisteme MS-DOS. Kod sohranyaet
staroe znachenie preryvaniya, obespechivaet svyaz' s tekushchim znacheniem i
podaet ustrojstvu znak nachat' rabotu. Odnako vmesto obespecheniya svyazi
tol'ko s vektorom zashchishchennogo rezhima, on ustanavlivaet svyaz' kak s
vektorom real'nogo rezhima, tak i s vektorom zashchishchennogo rezhima.
Ustanavlivaya svyaz' s vektorom real'nogo rezhima, kod vyzyvaet
AllocIntReflector, chtoby obespechit' ssylku vektora preryvanij
real'nogo rezhima na obratnyj vyzov, kotoryj prosto obrashchaetsya k
programme ISR zashchishchennogo rezhima. Tochka vhoda BogusStart podaet znak
ustrojstvu nachinat' rabotu odinakovym obrazom pri oboih rezhimah
raboty: zashchishchennom i real'nom. Ona razmaskiruet bit IRQ dlya
kontrollera PIC i podaet znak ustrojstvu nachinat' rabotu, zapisyvaya 1
v bit START porta upravleniya ustrojstvom. Kak tol'ko prilozhenie
obrashchaetsya k dannoj programme, nachinaetsya obrabotka preryvanij i
registraciya soobshchenij v sootvetstvii s programmoj ISR.
Programma BogusStop trivial'na i prosto otklyuchaet ustrojstvo i
razryvaet svyazi, ustanovlennye programmoj BogusStart. Itak, ostalos'
privesti primer prikladnoj programmy, chtoby pokazat' rabotu operacij
vvoda-vyvoda.
Prilozhenie wintest.c, pokazyvayushchee rabotu vvoda-vyvoda (sm.
listing 5), sostoit glavnym obrazom iz nemodul'nogo dialogovogo
bloka, v kotorom nepreryvno vysvechivaetsya kolichestvo preryvanij,
obrabotannyh s nachala raboty programmy.
Programma MainDlgProc vyzyvaet programmu BogusStart vo vremya
vypolneniya WM_INITDIALOG, peredavaya v kachestve parametra obrabotchik
okna dialogovogo bloka. Programma ISR registriruet soobshcheniya k
dannomu obrabotchiku v teh sluchayah, kogda schetchik preryvanij
izmenyaetsya ot nulya k edinice.
Programma MainDlgProc sohranyaet tekushchee summarnoe znachenie
schetchika v peremennoj wCountTotal. Vsyakij raz, kogda dialog poluchaet
soobshchenie WM_COMMAND s parametrom wParam, ravnym IDM_BOGUSEVENT,
programma obnovlyaet summarnyj schetchik, otobrazhaemyj v dialogovom
bloke. Sleduet otmetit', chto hotya programma ISR registriruet
soobshchenie tol'ko togda, kogda schetchik izmenyaetsya ot nulya k edinice,
vozmozhna (i ves'ma veroyatno) obrabotka kolichestva preryvanij do togo,
kak soobshchenie WM_COMMAND fakticheski budet peredano dialogovoj
procedure. Metodika, pokazannaya v dannoj programme, pri kotoroj
programma ISR registriruet soobshchenie tol'ko pri pervom perehode, a
programma BogusCheck chistit schetchik, obespechivaet tochnyj podschet
kolichestva preryvanij, dazhe esli na urovne prilozheniya nel'zya uchest'
kazhdoe preryvanie v moment ego vozniknoveniya.
Pri vypolnenii dannoj programmy mozhno nablyudat', chto schetchik
preryvaniya v dialogovom bloke nepreryvno uvelichivaetsya, ukazyvaya
kolichestvo vypolnennyh operacij vvod-vyvoda.
_____________________________________________________________________
#include
#include "bogus.h"
#include "wintest.h"
HANDLE hPgmInstance ;
#define IDM_BOGUSEVENT 0x3000
void CenterWindow(HWND hWnd)
{
int xSize, ySixe, xPos, yPos ;
RECT rc ;
xSize = GetSystemMetrics(SM_CXSCREEN) ;
ySize = GetSystemMetrics(SM_CYSCREEN) ;
GetWindowRect(hWnd, &rc) ;
xPos = (xSize - (rc.right - rc.left)) / 2 ;
yPos = (ySize - (rc.bottom - rc.top)) / 2 ;
SetWindowRect(hWnd, NULL, xPos, yPos, 0, 0,
SWP_DRAWFRAME | SWP_NOSIZE | SWP_NOZORDER) ;
}
LRESULT _loadds FAR PASCAL MainDlgProc(HWND hwndDlg,
UINT msg, WPARAM wParam, LPARAM lParam)
{
static WORD wCountTotal = 0;
WORD wCount ;
lParam = lParam ;
switch (msg)
{
case WM_INITDIALOG:
RemoveMenu(GetSystemMenu(hwndDlg,0),
SC_CLOSE,MF_BYCOMMAND) ;
BogusStart(hwndDlg, IDM_BOGUSEVENT) ;
break ;
case WM_SHOWWINDOW:
if (wParam)
CenterWindow(hwndDlg) ;
break ;
case WM_COMMAND:
switch (wParam)
{
case IDM_BOGUSEVENT:
wCount = BogusGetEvent() ;
while 9wCount)
{
wCountTotal += wCount ;
wCount = BogusGetEvent() ;
}
SetDlgItemInt(hwndDlg, IDM_COUNT, wCountTotal, FALSE);
break ;
case IDCANCEL:
EndDialog(hwndDlg, 0) ;
break ;
}
break ;
default:
return FALSE ;
}
return TRUE ;
}
int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpCmdLine, intnCmdShow)
{
hPgmInstance = hInstance ;
hPrevInstance = hPrevInstance ;
lpCmdLine = lpCmdLine ;
nCmdShow = nCmdShow ;
if (!hPrevInstance)
{
if (BogusCheck())
{
if (MessageBox(0, "Press OK to begin bogus I/O",
"WinTest", MB_OKCANCEL|MB_APPLMODAL) == IDOK)
{
DialogBox(hPgmInstance, "MainDlg", 0,
(FARPROC) MainDlgProc) ;
BogusStop() ;
}
}
else
MessageBox(0, "Bogus device not found", "WinTest",
MB_ICOMMAND|MB_OK|MB_APPLMODAL) ;
}
else
MessageBox(0, "Another instance already running",
"WinTest", MB_ICONEXCLAMATION|MB_OK|MB_APPLMODAL) ;
return 0 ;
}
/* konec fajla */
______________________________________________________________________
Listing 5. Programma wintest.c.
Drajver virtual'nogo ustrojstva
Fajl vxd2.asm (listingi 6 i 7) predstavlyaet soboj ishodnyj kod
drajvera fiktivnogo ustrojstva. Sleduet otmetit', chto dlya togo, chtoby
postroit' etot drajver, neobhodimo imet' komplekt drajverov ustrojstv
(Device Driver Kit - DDK) sistemy Windows firmy Microsoft, t.k. kod
napisan dlya 32-bitovogo assemblera, predusmotrennogo v komplekte DDK
(MASM5). Rezul'tiruyushchij modul' mozhet byt' skomponovan tol'ko
DDK-komponovshchikom LINK386 i utilitoj poslekomponovochnoj obrabotki
ADDHDR. Krome togo, dannyj ishodnyj kod ssylaetsya na opredelennoe
kolichestvo vklyuchaemyh fajlov (include files), kotorye vhodyat v sostav
tol'ko komplekta DDK.
Kak bylo ukazano, tipichnyj drajver VxD soderzhit obyazatel'nye
vklyuchaemye fajly, a krome togo on nachinaetsya s vyzova makrosa Declare
_Virtual_Device, kotoryj sozdaet blok dannyh, opisyvayushchij virtual'nyj
drajver dlya yadra sistemy Windows. |tot blok dannyh, fakticheski, -
edinstvennoe oboznachenie, eksportiruemoe iz drajvera VxD. Vse
ostal'nye tochki vhoda yavlyayutsya proizvodnymi ot dannyh, soderzhashchihsya
vnutri. Krome vsego prochego, dannyj makros opisyvaet imya ustrojstva,
poryadok ego inicializacii i ego tochki vhoda. Virtual'nyj drajver VxD
mozhet obsluzhivat' zaprosy prilozhenij kak v real'nom, tak i v zashchishchen-
nom rezhimah. Tochki vhoda dlya takogo obsluzhivaniya takzhe opisyvayutsya
dannym makrosom.
_____________________________________________________________________
PAGE ,132
title VxD2B.ASM - Primer drajvera ustrojstva #2b
;EM VxD2B - Primer drajvera ustrojstva #2b
;
; Copyright 1992, Cherry Hill Software
; All rights reserved
;
; SUMMARY (Rezyume)
; Dannyj drajver imitiruet preryvaemoe ustrojstvo. Port
; upravleniya (vyhod) imeet sleduyushchee naznachenie bitov:
;
; Bit 0 - Nachat' (Start) vvod-vyvod. Zapis' nulya v dannyj bit
; nachinaet peresylku vvoda-vyvoda. Peresylka dlitsya
; okolo 1/10 sekundy. Zapis' edinicy v etot bit ne
; daet rezul'tata.
;
; Bit 1 - Ustrojstvu posylaetsya EOI. Zapis' nulya v dannyj bit
; privodit k posylke priznaka "Konec preryvaniya" (End-
; of-interrupt - EOI) ustrojstvu i udalyaet lyuboj zapros
; na otlozhennoe preryvanie. Zapis' edinicy v etot bit ne
; daet rezul'tata.
; Vse ostal'nye bity: Vsegda zapisyvayutsya edinicy dlya dal'nejshej
; sovmestimosti.
;
; Pri chtenii porta sleduyushchie znacheniya vozvrashchayutsya:
;
; Bit 0 - Pervonachal'no prisvaivaetsya znachenie 1, bit
; sbrasyvaetsya, kogda bit 1 vyhodnogo porta sbrasyvaetsya,
; i ustanavlivaetsya, kogda dobavlyaetsya zapros na
; preryvanie. Dannyj bit raven nulyu, kogda ustrojstvo
; peredaet dannye i ustanavlivaetsya v 1, chtoby ukazat',
; kogda peredacha zavershena.
;
; Bit 1 - Pervonachal'no prisvaivaetsya znachenie 1, bit
; sbrasyvaetsya, kogda dobavlyaetsya zapros na preryvanie i
; ustanavlivaetsya, kogda ustrojstvo udalyaet zapros na
; preryvanie. Znachenie dannogo bita, ravnoe nulyu,
; ukazyvaet na otlozhennoe preryvanie, bit ustanavlivaetsya
; v 1, esli net otlozhennogo preryvaniya.
;
; Vse ostal'nye bity: vozvrashchaemoe znachenie ignoriruetsya dlya
; dal'nejshej sovmestimosti.
;
; WARNINGS (Preduprezhdeniya)
;
;
.386p
.xlist
include vmm.inc
include debug.inc
include v86mmgr.inc
include vpicd.inc
include ..\include\bogus.inc
.list
VM_Not_Executable equ VM_Not_Executeable ; acckk;
subttl VxD Declaration/Definition
page
VxD2B_Init_Order equ VNETBIOS_Init_Order+100 ; Dannaya operaciya
vypolnyaetsya posle zapuska virtual'noj seti
VxD2B_Device_ID equ Bogus_Device_ID
Declare_Virtual_Device VXD2, 1, 0, Vxd2B_Control, VxD2B_Device_ID, \
VxD2B_Init_Order
VxD_DATA_SEG
;
; Struktura deskriptora virtual'nogo preryvaniya
;
; Dannaya struktura peredaetsya VPIDC_Virtualize_IRQ. V dannoj
; strukture opisyvaetsya uroven' preryvaniya, procedura preryvaniya
; apparatnyh sredstv, i procedura, kotoruyu vyzyvaet VPICD, kogda
; preryvanie dispetchiruetsya v virtual'noj mashine VM i kogda VM
; vozvrashchaetsya iz preryvaniya.
;
IRQD VPICD_IRQ_Descriptor
hIRQ dd -1 ; obrabotchik IRQ
hOwner dd -1 ; obrabotchik, vladeyushchij VM
hTimeout dd 0 ; obrabotchik k obratnomu vyzovu po tajm-autu
bFakeData dd 01111111b ; imitirovat' dannye porta vvoda-vyvoda
VxD_DATA_ENDS
subttl Dispatch VxD Control
page
VxD_LOCKED_CODE_SEG
BeginProc CheckOwner, NO_LOG
cmp ebx,hOwner
jne short col
ret ; vyjti, esli vyzyvaetsya vladelec
col:
cmp hOwner,-1
jne short co2 ; propustit', esli vyzov ne vladel'ca
mov hOwner,ebx ;ustanovit' vladel'ca
ret
co2:
mov al,-1
ret
EndProc CheckOwner
BeginProc TimeoutProc
mov hTimeout,0 ;pochistit' obrabotchik
cmp edx,hOwner ; vse eshche tot zhe vladelec?
jne short tol ; propustit', esli net
test bFakeData,FAKE_STAT_BUSY ;otlozhennyj vvod-vyvod?
jnz short tol ; propustit', esli net
cmp hOwner,-1 ; imeetsya li vladelec?
je short tol ; propustit', esli net
mov eax,hIRQ
mov ebx,hOwner
VxDcall VPICD_Set_Int_Request ;dobavit' preryvanie
mov al,bFakeData
and al,NOT (FAKE_STAT_IRQ) ; ukazyvaet takzhe v porte sostoyaniya
or al,FAKE_STAT_BUSY ; ukazyvaet, chto bol'she ne zanyato
mov bFakeData,al
tol:
ret
End Proc TimeoutProc
;IP Port_IO_Callback - vypolnyaet dostup k FAKE_PORT
;
; ENTRY (vhod)
; EAX - vyhodnoe znachenie (dlya vyhodnyh operatorov)
; EBX - obrabotchik k tekushchemu VM
; ECX - tip operacii vvoda-vyvoda
; DS,ES - FLAT
;
; EXIT (vyhod)
; EAX - vhodnoe znachenie (dlya vhodnyh operatorov)
;
; WARNINGS (preduprezhdeniya)
;
; NOTES (primechaniya)
; Sleduet otmetit', chto my dazhe ne smotrim registrovyj frejm
; klienta.
;
; My prosto chitaem i uvelichivaem.
;
; CALLS (vyzovy)
BeginProc Port_IO_Callback, NO_LOG
Dispatch_Byte_IO Fall_through,Port_Output_Callback
Port_Input_Callback:
call CheckOwner
jc short ioexit
mov al,bFakeData
or bFakeData,FAKE_STAT_ERROR ; pochistit' otlozhennuyu oshibku
ioexit:
ret
Port_Output_Callback:
call CheckOwner
jc short ioexit ;ignorirovat' vvod-vyvod, esli ne vladelec
test al,FAKE_CTL_START
jnz short,poc1 ;propustit', esli ne nachinaetsya vvod-vyvod
test bFakeData,FAKE_START_BUSY
jz short,poc1 ;propustit', esli uzhe zanyato
test bFakeData,FAKE_START_IRQ
jz short,poc1 ;propustit', esli otlozhennoe IRQ
push eax
push edx
and bFakeData,NOT (FAKE_STAT_ERROR) ; predpolozhit' oshibku
mov eax,100 ; obratnyj vyzov v 1/10 sekundy
mov edx,hOwner ; peredat' vladel'ca obratnomu vyzovu
mov esi,OFFSET32 TimeoutProc
VMMcall Set_VM_Time_Out
pop edx
pop eax
or esi,esi
jz short,poc1 ;propustit', esli oshibka
and bFakeData,NOT (FAKE_STAT_BUSY) ; ukazat' na zanyatost'
or bFakeData,FAKE_STAT_ERROR ; v protivnom sluchae pochistit'
indikaciyu oshibki
mov hTimeout,esi ;sohranit' obrabotchik tajm-auta
poc1:
test al,FAKE_CTL_EOI
jnz short poc2 ; propustit', esli ne posylaetsya EOI
test bFakeData,FAKE_STAT_IRQ ;preryvanie otlozheno?
jnz short poc2 ; propustit', esli net
or bFakeData,FAKE_STAT_IRQ ;pokazat', chto preryvanie uzhe ne-
otlozhenoe
push eax
mov eax,hIRQ
VxDcall VPICD_Clear_Int_Request
pop eax
poc2:
ret
EndProc Port_IO_Callback
; ECX == 0 if unmasking (enabling), ECX != 0 if masking (disabling).
BeginProc VxD2_Mask_Change_Proc
call CheckOwner
jc short mcp9 ; ignorirovat', esli net vladel'ca
jcxz mcp9 ; propustit', esli ne maskirovano (vklyucheno)
;
; Vladelec osvobozhdaet upravlenie. Razreshaetsya drugoj VM vojti v
; sistemu.
;
mov hOwner,-1 ; pochistit' vladel'ca
mcp9:
ret
EndProc VxD2_Mask_Change_Proc
; Vyzyvaetsya, kogda vypolnyaetsya programma ISR
BeginProc VxD2_VInt_Proc
mov eax,High_Pri_Device_Boost
VMMCall Adjust_Exec_Priority ;povyshennyj prioritet dlya nachal'noj
obrabotki
ret
EndProc VxD2_VInt_Proc
; vyzyvaetsya pri vozvrate iz programmy ISR (IRETs)
BeginProc VxD2_IRET_Proc
mov eax,-(High_Pri_Device_Boost)
VMMCall Adjust_Exec_Priority ;vosstanovit' prioritet
ret
EndProc VxD2_IRET_Proc
ifdef DEBUG
BeginProc VxD2B_Debug_Query
Trace_Out "VxD2 has no debug command support."
clc
ret
End Proc VxD2B_Debug_Query
endif
;
; VxD2B_Control
;
CtlDisp macro x
Control_Dispatch x, VxD2B_&x
endm
Begin_Control_Dispatch VxD2B
CtlDisp Device_Init
ifdef DEBUG
CtlDisp Debug_Query
endif
End_Control_Dispatch VxD2B
VxD_LOCKED_CODE_ENDS
VxD_CODE_SEG
VxD_CODE_ENDS
subttl VxD Initialization
page
VxD_ICODE_SEG
page
; EP VxD2B_Device_Init - Nekriticheskaya inicializaciya ustrojstva
;
; ENTRY (vhod)
; EBP - frejm klienta
; EBX - sistemnyj obrabotchik VM
; DS,ES - FLAT
;
; EXIT (vyhod)
; SUCCESS (uspeshnyj)
; Carry clear ("net perenosa")
; FAILURE (avarijnyj)
; Carry set ("est' perenos")
;
;
; WARNINGS (preduprezhdeniya)
;
; NOTES (primechaniya)
;
; CALLS (vyzovy)
;
BeginProc VxD2B_Device_Init
Debug_Out "VxD2B_Device_Init"
mov edi,OFFSET32 IRQD
VxDcall VPICD_Virtualize_IRQ ; virtualizirovat' preryvanie
jc short vdi1 ; vyhod, esli oshibka
mov hIRQ,eax ; sohranit' obrabotchik
mov edx,FAKE_PORT
mov esi,OFFSET32 Port_IO_Callback
VMMCall Install_IO_Handler
VMMCall Enable_Global_Trapping ;
clc ; net oshibki
vdi1:
ret
EndProc VxD2B_Device_Init
VxD_ICODE_ENDS
VxD_REAL_INIT_SEG
VxD2B_Real_Init LABEL FAR ;vyzyvaetsya pered tem, kak sistema Windows
vhodit v zashchishchennyj rezhim
mov ax,Device_Load_OK ;pozvolyaet VxD zagruzit'sya
xor bx,bx ; net isklyuchennyh (Exclude) stranic EMM
xor si,si ; net elementov ekzemplyarov dannyh
; peredat' edx nemodificirovannym
ret
VxD_REAL_INIT_ENDS
END VxD2B_Real_Init
; Konec fajla
_____________________________________________________________________
Listing 6. Programma vxd2.asm
_____________________________________________________________________
LIBRARY VXD2
DESCRIPTION 'Enhanced Windows VXD2(B) Device (Version 1.0)'
EXETYPE DEV386
SEGMENTS
_LTEXT PRELOAD NONDISCARDABLE
_LDATA PRELOAD NONDISCARDABLE
_ITEXT CLASS 'ICODE' DISCARDABLE
_IDATA CLASS 'ICODE' DISCARDABLE
_TEXT CLASS 'PCODE' NONDISCARDABLE
_DATA CLASS 'PCODE' NONDISCARDABLE
EXPORTS
VXD2_DDB @1
_____________________________________________________________________
Listing 7. Programma vxd2.def
Sobytiya, upravlyayushchie ustrojstvom
Po mere togo, kak sistema Windows v svoej rabote prohodit
razlichnye stadii, nachinaya so stadii inicializacii samoj sistemy,
cherez inicializaciyu virtual'noj mashiny VM i tak dalee, kazhdyj
ustanovlennyj drajver VxD vyzyvaetsya neodnokratno, a imenno odin raz
na kazhduyu stadiyu. V tablice, privedennoj nizhe, perechislyayutsya fazy
sistemy Windows i glavnye sobytiya, dlya kotoryh vyzyvaetsya kazhdyj
drajver VxD.
Tablica
Upravlyayushchie soobshcheniya drajvera VxD
-------------------+--------------------------------------------------
Sys_Critical_Init | Pervoe upravlyayushchee sobytie; preryvaniya
| otklyuchayutsya. Drajver VxD opredelyaet gotovnost'
| ustrojstva.
-------------------+--------------------------------------------------
Device_Init | Preryvaniya razreshayutsya; drajver VxD inicializiru-
| et ustrojstvo; mogut byt' vyzvany programmy i
| drajvery sistemy DOS.
-------------------+--------------------------------------------------
Init_Complete | Ukazyvaet, chto vse drajvery VxD proshli stadiyu
| Device_Init.
-------------------+--------------------------------------------------
System_Exit | Ukazyvaet, chto sistema Windows gotovitsya k
| zakrytiyu i vozvratu v sistemu DOS. Pamyat' dlya
| sistemy DOS vosstanovlena v sostoyanie, kotoroe
| bylo do raboty sistemy Windows.
-------------------+--------------------------------------------------
Sys_Critical_Exit | Poslednee upravlyayushchee sobytie; preryvaniya
| otklyuchayutsya.
-------------------+--------------------------------------------------
Create_VM | Vyzyvaetsya pered momentom sozdaniya virtual'noj
| mashiny VM; drajver VxD ukazyvaet, dostupny li
| resursy dlya sozdaniya virtual'noj mashiny VM.
-------------------+--------------------------------------------------
VM_Critical_Init | Vtoraya faza sozdaniya virtual'noj mashiny VM.
-------------------+--------------------------------------------------
VM_Init | Tret'ya faza sozdaniya virtual'noj mashiny VM.
Sys_VM_Init | Drajver VxD mozhet avarijno zavershit' rabotu
| virtual'noj mashiny VM.
-------------------+--------------------------------------------------
Query_Destroy | Pozvolyaet drajveru VxD predupredit' pol'zovatelya
| o zatrudneniyah pri razrushenii virtual'noj mashiny
| VM.
-------------------+--------------------------------------------------
VM_Terminate | Pervaya stadiya uspeshnogo zaversheniya virtual'noj
Sys_VM_Terminate | mashiny VM. Esli eto sistemnaya virtual'naya
| mashina VM, to soobshchenie ukazyvaet, chto
| proizvoditsya normal'noe, vyzvannoe pol'zovatelem,
| zavershenie sistemy Windows.
-------------------+--------------------------------------------------
VM_Not_Executeable | Virtual'naya mashina VM zakryvaetsya. Pervaya stadiya
| avarijnogo zaversheniya virtual'noj mashiny VM.
-------------------+--------------------------------------------------
Drajver VxD primera vypolnyaet upravlenie tol'ko fazoj
Device_Init. Na etoj stadii ustanavlivaetsya svyaz' s portom vvoda-
vyvoda i urovnem preryvaniya 11, a takzhe proizvoditsya ih
virtualizaciya. Obychno drajver VxD virtualiziruet porty vvoda-vyvoda i
preryvanie v sootvetstvii s fizicheskim apparatnym oborudovaniem. No v
dannom sluchae drajver VxD mozhet virtualizirovat' i delaet eto s
portom i preryvaniem, kotorye ne imeyut sootvetstvuyushchego podklyuchennogo
apparatnogo oborudovaniya.
Kod Install_IO_Handler vyzyvaetsya, chtoby virtualizirovat'
edinstvennyj port vvoda-vyvoda. Zatem vsyakij raz, kogda
osushchestvlyaetsya dostup k opisannomu portu vvoda-vyvoda iz virtual'noj
mashiny VM, programma upravleniya virtual'noj mashinoj sistemy Windows
(Virtual Machine Manager - VMM) vyzyvaet obratno drajver VxD dlya
togo, chtoby razreshit' emu imitirovat' operacii vvoda-vyvoda.
Kod VPICD_Virtualize_IRQ vyzyvaetsya, chtoby virtualizirovat'
uroven' preryvaniya. Vypolnyaya ego, mozhno imitirovat' preryvanie
apparatnogo oborudovaniya (v chastnosti IRQ 11) v virtual'noj mashine.
Kogda k portu vvoda-vyvoda (141) ustrojstva osushchestvlyaetsya
dostup virtual'noj mashinoj VM (libo v real'nom, libo v zashchishchennom
rezhime), to mashina vyzyvaet programmu drajvera VxD Port_IO_Callback
(sm. Listing 6). V etoj programme podprogramma Dispatch_Byte_IO
svodit bol'shoe kolichestvo vozmozhnyh tipov dostupa vvoda-vyvoda (a
imenno: byte, word, dword, string i t.d.) k dvum: bajtovomu vvodu i
bajtovomu vyvodu.
Dlya ustrojstva iz primera bajtovyj vvod predstavlyaet soboj chtenie
iz registra sostoyaniya ustrojstva. On vozvrashchaet prosto peremennuyu,
kotoraya sohranyaetsya v pamyati.
Bajtovyj vyvod - nemnogo bolee slozhnaya operaciya, tak kak
predstavlyaet fakticheskuyu rabotu ustrojstva. Pri zapuske ustrojstva
takzhe zapuskaetsya tajmer, kotoryj vypolnyaet obratnyj vyzov (k kodu
TimeoutProc) v techenii 1/10 sekundy i ustanavlivaet sostoyanie BUSY.
Esli vyvod podtverzhdaet priem preryvaniya, to proizvoditsya ochistka
virtual'nogo zaprosa na preryvanie putem vyzova koda VPICD_Clear_Int_
Request i ochistka sostoyaniya v registre sostoyaniya.
Obratnyj vyzov koda TimeoutProc predstavlyaet zavershenie operacii
vvoda-vyvoda na ustrojstve i imenno v dannyj moment on modeliruet
preryvanie apparatnogo oborudovaniya k virtual'noj mashine VM putem
vyzova koda VPICD_Clear_Int_Request i ochistki sostoyaniya zanyatosti
ustrojstva. Drajver ustrojstva v prilozheniyah dostest i wintest budet
obychno obrabatyvat' preryvanie putem podtverzheniya priema ego (posylaya
EOI) i povtornogo zapuska processa na vsem protyazhenii snova.
Sleduet otmetit' procedury VxD2_VInt_Proc i VxD2_IRET _Proc. Na
dannye dve procedury sushchestvuet ssylka v strukture, kotoraya
peredaetsya kodu VPICD_Virtualize_IRQ. Oni vyzyvayutsya v nachale i konce
processa virtualizacii preryvaniya v virtual'nuyu mashinu VM. Vse ih
funkcii svodyatsya k uvelicheniyu i sohraneniyu prioriteta virtual'noj
mashiny VM, kotoraya vremenno obrabatyvaet dannoe preryvanie. Takim
sposobom drajver VxD mozhet upravlyat' prioritetom virtual'noj mashiny
VM, kotoraya schitaetsya sootvetstvuyushchej. (Vsegda zhelatel'no, chtoby
programma obsluzhivaniya preryvaniya v lyuboj virtual'noj mashine VM imela
prioritet vyshe, chem prioritet obychnoj obrabotki v drugih virtual'nyh
mashinah VM.)
Posle postroeniya drajvera VxD, do pervogo obrashcheniya k nemu
programmy Windows neobhodimo dobavit' ego kak stroku device= v
sekciyu [386Enh] koda system.ini. Sistema Windows dolzhna byt' zapushchena
zanovo, chtoby vklyuchit' drajver VxD i virtual'noe ustrojstvo. Posle
etogo, mozhno vypolnyat' i testirovat' prilozheniya dostest i wintest.
Hotya drajvery ustrojstv sistemy Windows kazhutsya v nastoyashchee
vremya ochen' slozhnymi, obychnye i virtual'nye drajvery ustrojstv
predostavlyayut ogromnoe kolichestvo vozmozhnostej. Odnako sleduet
uchityvat', naskol'ko bolee slozhnymi oni dolzhny byt' na mashine MIPS,
ekspluatiruyushchej sistemu Windows NT i kod emulyatora 80x86, chtoby
obespechit' rabotu virtual'noj mashiny sistemy MS-DOS.
Last-modified: Mon, 14 Feb 2000 17:32:34 GMT