Pisanie plików .SYS
Pisanie plików .SYS
Sterowniki w postaci plików .SYS dzielą się na 2 rodzaje:
Nie-DOSowe 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źć tutaj:
http://www.o3one.org/hwdocs/bios_doc/dosref22.html (720 kB).
Pliki .SYS zaczynają się od adresu 0 (org 0), a nagłówek takiego pliku składa się z 5 elementów:
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ż 1 urządzenie. Jeśli mamy tylko 1 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)
8B nazwa (urządzenie znakowe) dopełniana w razie potrzeby spacjami do 8 znaków / 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.
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).
Za każdym razem, jak DOS chce coś od naszego sterownika, uruchamia go, podając mu w parze
rejestrów ES:BX tzw. nagłówek żądania (request header). On zawiera informacje o tym, co mamy
zrobić.
Procedura strategii jest uruchamiana tylko raz, przy uruchomieniu sterownika przez DOS, a
jedynym właściwie jej zadaniem jest zachowanie tego adresu z ES:BX w zmiennej
lokalnej, aby można było potem odczytywać żądania. 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.
Nagłówek żądania
Odległość od początku Długość w bajtach Zawartość
0 1 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 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.
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
7 INPUT FLUSH
8 OUTPUT Zapis danych.
9 OUTPUT Zapis danych z weryfikacją.
10 OUTPUT STATUS
11 OUTPUT FLUSH
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.
Status wykonania zadania
bit Znaczenie bitu(ów)
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"
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
INIT.
ES:BX -> struktura zawierająca nagłówek żądania i dane. Ta struktura wygląda tak:
Odległość od początku Długość w bajtach Zawartość
0 13 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).
11h? 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).
15h? 1 numer dysku (DOS 3.0+) (wymieniony wcześniej dokument podaje
tutaj ofset 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).
ES:BX -> struktura zawierająca nagłówek żądania i dane. Ta struktura wygląda tak:
Odległość od początku Długość w bajtach Zawartość
0 13 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.
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:
Odległość od początku Długość w bajtach Zawartość
0 13 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
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
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 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 0A000h ; atrybuty (urz. znakowe), "output till busy"
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:
; word wyniku w [es:bx+3]
mov word [es:bx + 3], 100h ; zwracamy "wszystko zrobione"
pop ax
pop bx
pop es
popf
retf
; --------------------------
.init:
; podajemy adres końca kodu, który ma zostać w pamięci
; celowo usuwamy niepotrzebny już kod.
mov word [es:bx + 0Eh], koniec
mov [es:bx + 10h], cs
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
; ==========================================================
; wszystko od tego miejsca zostanie wyrzucone z pamięci
koniec:
strategia:
mov [cs:request_header], bx ; zapisujemy adres nagłówka żądania
mov [cs:request_header+2], es
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
retf
info1 db "Uruchamianie sterownika MYSZKA1...", 10, 13, 10, 13
info1_dl equ $ - info1
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".
Miłej zabawy.
Wyszukiwarka
Podobne podstrony:
SYS TUTSYS TUTDOS SYS 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