Sterowniki w postaci plików .SYS dzielą się na 2 rodzaje:
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:
procedury strategiidanego sterownika (opisane dalej)
procedury przerwaniadanego sterownika (opisane dalej)
Urządzenie znakowe to takie,
które może wysyłać/odbierać pojedyncze bajty, na przykład 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.
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 liczba jednostek/dysków obsługiwanych przez ten sterownik (urządzenia blokowe).
Procedura strategii(strategy routine).
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).
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.
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.
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.
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 |
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 |
ES:BX wskazuje na strukturę zawierającą nagłówek żądania i dane. Ta struktura wygląda tak:
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:
ES:BX wskazuje na strukturę zawierającą nagłówek żądania i dane. Ta struktura wygląda tak:
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 | Liczba 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:
Ten odczyt różni się od innych tym, że nie usuwa odczytanych danych z bufora.
ES:BX wskazuje na strukturę zawierającą nagłówek żądania i dane. Ta struktura wygląda tak:
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:
Wymuszenie wykonania wszystkich operacji odczytu, o których wie sterownik.
ES:BX wskazuje na nagłówek żądania.
W czasie tej operacji należy:
Wymuszenie wykonania wszystkich operacji zapisu, o których wie sterownik.
ES:BX wskazuje na nagłówek żądania.
W czasie tej operacji należy:
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:
; 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 wskazuje na 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.