Asembler: DOS: Sterowniki .SYS
Pisanie plików .SYS
Sterowniki w postaci plików .SYS dzielą się na 2 rodzaje:
Nie-DOS-owe pliki .SYS
(ładowane z config.sys poleceniem DEVICE=...)
zazwyczaj zawierają sterowniki takich urządzeń zewnętrznych jak np. CD-ROM.
Pliki .SYS systemu DOS
- np. MSDOS.SYS czy IO.SYS. Te pliki zawierają sterowniki urządzeń standardowych, jak konsola
CON czy drukarka PRN.
Wszystkie te pliki łączy wspólna struktura, którą postaram się tutaj przedstawić.
Informacje podane przeze mnie są wycinkiem z dokumentu
Programmer's Technical Reference for MSDOS and the IBM PC,
którego kopię można znaleźć na stronach
systemu O3one (720 kB).
Pliki .SYS zaczynają się od adresu 0 (org 0), a nagłówek takiego pliku składa się z pięciu elementów:
(przeskocz elementy nagłówka)
DWORD - pełny adres (najpierw offset, potem segment)
do następnego takiego nagłówka jak ten, jeśli
nasz plik .SYS obsługuje więcej niż jedno urządzenie. Jeśli mamy tylko jeden sterownik w
naszym pliku, wpisujemy tutaj wartość -1, czyli FFFF:FFFF.
WORD - atrybut urządzenia (opisany dalej)
WORD - offset procedury strategii danego sterownika (opisane dalej)
WORD - offset procedury przerwania danego sterownika (opisane dalej)
8 bajtów - nazwa (urządzenie znakowe) dopełniana w razie potrzeby spacjami
do ośmiu znaków lub ilość jednostek (urządzenie blokowe)
Urządzenie znakowe to takie,
które może wysyłać/odbierać pojedyncze bajty, np. CON, PRN, AUX.
Można je otwierać jak normalne pliki.
Urządzenie blokowe to takie, które operują na blokach danych i są to zazwyczaj dyski.
(przeskocz tabelę atrybutów urządzenia)
Bity atrybutu i ich znaczenie
Numer bitu Znaczenie
0 =0 - to urządzenie nie jest standardowym urządzeniem wejścia
=1 - to urządzenie jest standardowym urządzeniem wejścia
1 =0 - to urządzenie nie jest standardowym urządzeniem wyjścia
=1 - to urządzenie jest standardowym urządzeniem wyjścia
2 =0 - to urządzenie nie jest urządzeniem NUL
=1 - to urządzenie jest urządzeniem NUL
3 =0 - to urządzenie nie jest urządzeniem CLOCK
=1 - to urządzenie jest urządzeniem CLOCK
4 =0 - należy używać standardowych procedur we/wy CON
=1 - należy używać szybkich procedur we/wy ekranu (int 29h)
5-10 zarezerwowane, muszą być równe 0
11 =0 - to urządzenie nie obsługuje wymiennych nośników (domyślne dla DOS 2.x)
=1 - to urządzenie obsługuje wymienne nośniki (tylko dla DOS 3.0+)
12 zarezerwowane, musi być równy 0
13 =0 - format IBM (urządzenia blokowe)
=1 - format nie-IBM (urządzenia blokowe)
=1 - obsługuje funkcję zapisywania danych aż do stanu zajętości
(output till busy, urządzenia znakowe)
14 =0 - nie obsługuje IOCTL
=1 - obsługuje IOCTL
15 =0 - urządzenie blokowe
=1 - urządzenie znakowe
Ostatnie pole w nagłówku
to nazwa urządzenia (w przypadku urządzeń znakowych) lub ilość
jednostek/dysków obsługiwanych przez ten sterownik (urządzenia blokowe).
Procedura strategii (strategy routine).
(przeskocz procedurę strategii)
Za każdym razem, jak DOS chce coś od naszego sterownika, uruchamia procedurę strategii,
podając w parze rejestrów ES:BX adres nagłówka żądania (request header).
Zawiera on informacje o tym, co mamy zrobić.
Jedynym obowiązkowym zadaniem tej procedury jest zachowanie adresu z ES:BX w zmiennej
lokalnej, aby można było potem odczytywać żądania w procedurze przerwania, która uruchamiana jest
zaraz po procedurze strategii. Jeśli chcemy zrobić coś więcej, musimy
zachować wszystkie rejestry (łącznie z flagami), które zmieniamy.
Procedura kończy się wywołaniem RETF, gdyż DOS uruchamia nasz sterownik wykonując
CALL FAR.
Tak więc najprostszy przykład sprowadza się do:
mov word cs:[nagl_zad], bx ; NASM : [cs:nagl_zad]
mov word cs:[nagl_zad+2], es ; NASM : [cs:nagl_zad+2]
retf
Procedura przerwania
(interrupt routine).
(przeskocz procedurę przerwania)
Ta procedura jest odpowiedzialna za wykonywanie poleceń od systemu. Polecenia te są zawarte w
nagłówku żądania, który teraz omówię.
W procedurze przerwania również należy zachować wszystkie modyfikowane rejestry i wrócić do DOSa
poleceniem RETF. Procedura przerwania jest uruchamiana przez DOS tuż po powrocie z
procedury strategii, która musi zachować bieżący adres nagłówka żądania.
(przeskocz opis nagłówka żądania)
Nagłówek żądania
Odległość od początku
Długość
Zawartość
0 1 bajt Długość w bajtach całego nagłówka i ewentualnych danych
1 1 Kod podjednostki w urządzeniach blokowych.
Nieistotne dla urządzeń znakowych
2 1 Kod rozkazu
3 2 Status wykonania
5 8 zarezerwowane dla DOSa
0Ch różna Dane odpowiednie dla operacji
Kod podjednostki w urządzeniach blokowych
jest istotny, gdy nasz sterownik obsługuje więcej niż 1 urządzenie.
(przeskocz listę rozkazów)
Kod rozkazu
Kod Nazwa
Funkcja
0 - INIT
- Inicjalizacja sterownika. Używane tylko raz.
1 - MEDIA CHECK
- Sprawdzanie, czy zmieniono dysk od ostatniego sprawdzenia.
Używane tylko w urządzeniach blokowych. Urządzenia znakowe nic nie robią.
2 - BUILD BPB
- Stworzenie nowego BIOS Parameter Block (BPB).
Używane tylko w urządzeniach blokowych. Urządzenia znakowe nic nie robią.
3 - IOCTL INPUT
- Odczyt IOCTL.
Uruchamiane tylko wtedy, gdy urządzenie ma ustawiony bit IOCTL.
4 - INPUT
- Odczyt danych.
5 - NONDESTRUCTIVE INPUT NO WAIT
- Odczyt danych.
6 - INPUT STATUS
- Stan odczytu
7 - INPUT FLUSH
- Opróżnienie kolejki wejściowej
8 - OUTPUT
- Zapis danych.
9 - OUTPUT
- Zapis danych z weryfikacją.
10 - OUTPUT STATUS
- Stan zapisu
11 - OUTPUT FLUSH
- Opróżnienie kolejki wyjściowej
12 - IOCTL OUTPUT
- Zapis IOCTL. Uruchamiane tylko wtedy, gdy urządzenie ma ustawiony bit IOCTL.
13 - DEVICE OPEN
- Uruchamiane tylko wtedy, gdy urządzenie ma ustawiony bit OPEN/CLOSE/RM.
14 - DEVICE CLOSE
- Uruchamiane tylko wtedy, gdy urządzenie ma ustawiony bit OPEN/CLOSE/RM.
15 - REMOVEABLE MEDIA
- Uruchamiane tylko wtedy, gdy urządzenie blokowe ma ustawiony bit OPEN/CLOSE/RM.
16 - OUTPUT UNTIL BUSY
- Uruchamiane tylko wtedy, gdy urządzenie znakowe ma ustawiony bit 13.
Najważniejsze rozkazy są opisane dalej.
(przeskocz listę wyników działania)
Status wykonania zadania
bit Znaczenie
0-7 Kod błędu, gdy bit15 = 1
8 =1 oznacza Operacja zakończona
9 =1 oznacza Urządzenie zajęte
10-14 Zarezerwowane dla DOSa
15 =1 oznacza błąd
(przeskocz listę błędów sterownika)
Znaczenie numerów błędów
numer Typ błędu
0 naruszenie ochrony przed zapisem
1 nieznana jednostka
2 urządzenie nie jest gotowe
3 nieznana komenda
4 błąd CRC
5 nieprawidłowa długość struktury żądania dostępu do dysku
6 błąd wyszukania (seek error)
7 nieznany nośnik
8 sektor nie znaleziony
9 koniec papieru w drukarce
10 błąd zapisu
11 błąd odczytu
12 błąd ogólny
13 zarezerwowane
14 zarezerwowane
15 nieprawidłowa zmiana dysku
Rozkazy
(przeskocz listę rozkazów sterownika)
INIT.
(przeskocz rozkaz init)
ES:BX -> struktura zawierająca nagłówek żądania i dane. Ta struktura wygląda tak:
Nagłówek żądania
Odległość od początku Długość
Zawartość
0 13 bajtów Nagłówek żądania
0Dh 1 Liczba jednostek w urządzeniach blokowych.
Nieistotne dla urządzeń znakowych
0Eh? 4 Offset i segment końca kodu naszego sterownika.
Mówi DOSowi, ile pamięci można zwolnić (wymieniony wcześniej dokument podaje
tutaj offset 11h, który nie jest prawidłowy).
12h? 4 Wskaźnik na tablicę BPB (nieistotne dla urządzeń znakowych)
/ wskaźnik na resztę argumentów (wymieniony wcześniej dokument podaje
tutaj offset 15h).
16h? 1 numer dysku (DOS 3.0+) (wymieniony wcześniej dokument podaje
tutaj offset 19h).
W czasie inicjalizacji należy:
ustawić liczbę jednostek (tylko w urządzeniach blokowych). Wpisać 0, jeśli nie można
uruchomić urządzenia.
ustawić wskaźnik na tablicę BPB (tylko w urządzeniach blokowych)
wykonać czynności inicjalizacyjne (np. modemów, drukarek)
ustawić adres końca rezydentnego kodu. Wstawić CS:0, jeśli nie można
uruchomić urządzenia.
ustawić odpowiedni status w nagłówku żądania
Odczyt/Zapis (funkcje: 3, 4, 8, 9, 12, 16).
(przeskocz rozkazy odczytu i zapisu)
ES:BX -> struktura zawierająca nagłówek żądania i dane. Ta struktura wygląda tak:
Nagłówek żądania
Odległość od początku Długość
Zawartość
0 13 bajtów Nagłówek żądania
0Dh 1 Bajt deskryptora nośnika z BPB
(Media Descriptor Byte)
0Eh 4 Offset i segment bufora, z którego dane będą odczytywane/
do którego dane będą zapisywane.
12h 2 Ilość bajtów/sektorów do zapisania/odczytania.
14h 1 Początkowy numer sektora (tylko urządzenia blokowe).
Nie ma znaczenia dla urządzeń znakowych.
16h 4 Offset i segment identyfikatora napędu
(volume ID), gdy zwrócono kod błędu 0Fh.
W czasie tej operacji należy:
ustawić odpowiedni status w nagłówku żądania
wykonać zadanie
ustawić rzeczywistą liczbę przeniesionych bajtów/sektorów
NONDESTRUCTIVE INPUT NO WAIT.
(przeskocz rozkaz NONDESTRUCTIVE INPUT NO WAIT)
Ten odczyt różni się od innych tym, że nie usuwa odczytanych danych z bufora.
ES:BX -> struktura zawierająca nagłówek żądania i dane. Ta struktura wygląda tak:
Nagłówek żądania
Odległość od początku Długość
Zawartość
0 13 bajtów Nagłówek żądania
0Dh 1 Bajt odczytany z urządzenia
W czasie tej operacji należy:
zwrócić bajt odczytany z urządzenia
ustawić odpowiedni status w nagłówku żądania
INPUT FLUSH
(przeskocz rozkaz INPUT FLUSH)
Wymuszenie wykonania wszystkich operacji odczytu, o których wie sterownik.
ES:BX -> nagłówek żądania.
W czasie tej operacji należy:
ustawić odpowiedni status w nagłówku żądania
OUTPUT FLUSH
(przeskocz rozkaz OUTPUT FLUSH)
Wymuszenie wykonania wszystkich operacji zapisu, o których wie sterownik.
ES:BX -> nagłówek żądania.
W czasie tej operacji należy:
ustawić odpowiedni status w nagłówku żądania
Przykład
Składając razem powyższe informacje, napisałem taki oto przykładowy plik .SYS.
Jest to sterownik wymyślonego urządzenia znakowego MYSZKA1, który obsługuje tylko
funkcję INIT (oczywiście) i pobieranie danych z urządzenia, które sprowadza się do
zwrócenia starego znacznika EOF (1Ah).
Aby było widać, że mój sterownik się ładuje (dzięki linii DEVICE=...
w config.sys), dorobiłem kod wyświetlający na ekranie informację o ładowaniu.
Resztę zobaczcie sami:
(przeskocz przykładowy kod)
; Przykład sterownika typu .SYS
; Autor: Bogdan D.
; kontakt: bogdandr (małpka) op (kropka) pl
;
; kompilacja:
; nasm -O999 -w+orphan-labels -o protosys.sys -f bin protosys.asm
dd 0FFFFFFFFh ; wskaźnik na następny sterownik
; -1, bo mamy tylko 1 urządzenie
dw 08000h ; atrybuty (urz. znakowe), output till busy (A000)
dw strategia ; adres procedury strategii
dw przerwanie ; adres procedury przerwania
db "MYSZKA1 " ; nazwa urządzenia (8 znaków, dopełniane spacjami)
przerwanie:
pushf
push es
push bx
push ax
les bx, [cs:request_header] ; ES:BX -> nagłówek żądania
mov al, [es:bx + 2] ; kod rozkazu
test al, al ; 0 = INIT
jz .init
cmp al, 4 ; czy ktoś chce czytać dane?
je .czytanie
cmp al, 5
je .czytanie2
; innych żądań nie obsługujemy
.koniec_przer:
; słowo wyniku w [es:bx+3]
mov word [es:bx + 3], 100h ; mówimy, że wszystko zrobione
pop ax
pop bx
pop es
popf
retf
.init:
; podajemy adres końca kodu, który ma
; zostać w pamięci
; można usunąć niepotrzebny już kod
mov word [es:bx + 0eh], koniec
mov [es:bx + 10h], cs
pusha
push es
mov ah, 3 ; pobranie aktualnej pozycji kursora
xor bx, bx
int 10h ; DH, DL - wiersz, kolumna kursora
inc dh
xor dl, dl ; idziemy o 1 wiersz niżej,
; od lewej krawędzi
push cs
mov ax, 1301h ; AH=funkcja pisania na ekran.
; AL=przesuwaj kursor
mov bx, 7 ; normalne znaki (szary na czarnym)
mov cx, init1_dl ; długość napisu
mov bp, init1 ; adres napisu
pop es ; segment napisu = CS
int 10h ; napis na ekran.
; DH, DL wskazują pozycję.
pop es
popa
jmp short .koniec_przer
.czytanie: ; jak ktoś chce czytać, zwracamy mu EOF
push es
push ax
push cx
push di
mov cx, [es:bx + 12h] ; liczba żądanych bajtów
les di, [es:bx + 0Eh] ; adres czytania/zapisywania
mov al, 1Ah ; 1ah = EOF
rep stosb ; zapisujemy
pop di
pop cx
pop ax
pop es
jmp short .koniec_przer
.czytanie2: ; jak ktoś chce czytać, zwracamy mu EOF
mov byte [es:bx+0Dh], 1Ah
jmp short .koniec_przer
request_header dd 0 ; wskaźnik na nagłówek żądania
strategia:
pushf
mov [cs:request_header], bx ; zapisujemy adres nagłówka żądania
mov [cs:request_header+2], es
cmp byte [cs:pierwsze], 1
jne .nie_pisz
mov byte [cs:pierwsze], 0
pusha
push es
mov ah, 3 ; pobranie aktualnej pozycji kursora
xor bx, bx
int 10h ; DH, DL - wiersz, kolumna kursora
inc dh
xor dl, dl ; idziemy o 1 wiersz niżej,
; od lewej krawędzi
push cs
mov ax, 1301h ; AH=funkcja pisania na ekran.
; AL=przesuwaj kursor
mov bx, 7 ; normalne znaki (szary na czarnym)
mov cx, info1_dl ; długość napisu
mov bp, info1 ; adres napisu
pop es ; segment napisu = CS
int 10h ; napis na ekran.
; DH, DL wskazują pozycję.
pop es
popa
.nie_pisz:
popf
retf
info1 db "*** Uruchamianie sterownika MYSZKA1...",10,13,10,13
info1_dl equ $ - info1
init1 db "*** INIT", 13, 10, 13, 10
init1_dl equ $ - init1
pierwsze db 1
; wszystko od tego miejsca zostanie wyrzucone z pamięci
koniec:
Jak widać, było tu o wiele więcej
opisu niż samej roboty i wcale nie okazało się to takie straszne.
Aby zobaczyć, czy nasz sterownik rzeczywiście został załadowany i ile zajmuje miejsca w pamięci,
należy wydać polecenie mem /c/p.
Miłej zabawy.
Spis treści off-line (Alt+1)
Spis treści on-line (Alt+2)
Ułatwienia dla niepełnosprawnych (Alt+0)
Wyszukiwarka
Podobne podstrony:
SYS TUTDOS SYS TUTSYS TUTART121 tut 2sys akw?nych dodatek aphys tut 08SYSUłamki z sys binsys i sieciphys tut 12DOS DIOD TUTdet systhe jeff dunham show 103 hdtv xvid sysART121 tut 3SYS SKR1MYSZ TUTwięcej podobnych podstron