Pisanie systemów operacyjnych - tryb rzeczywisty
Wolverine
Na początek chciałbym wyjaśnić co skłoniło mnie do napisania tego kursu, bo przecież w sieci jest tego pełno. Powodem jest to, że żaden kurs nie odpowie ci na możliwie dużą ilość pytań, które pojawiają się przy pierwszym kontakcie z pisaniem OSa. Omówię tu jedynie elementarne podstawy w sztuce pisania systemów operacyjnych, które mogą jedynie posłużyć tym, że łapiąc za inny kurs będziesz znał te podstawy. Jeśli jednak tekst będzie do bani - zawsze jest przycisk usuń :)
01h. Założenia.
Nasz mini OS nie będzie robił nic poza wyświetleniem komunikatu i restartem komputera po naciśnięciu klawisza Escape, więc nie, nie zostaniesz Billem tak szybko :), za to zrozumiesz każdą napisaną linijkę twojego OSa (mam nadzieję). Będzie posiadał bootloadera, który załaduje i uruchomi kernela, wszystko będzie pracowało w trybie Real Mode.
02h. Co nam będzie potrzebne?
Ponieważ nasz OS będzie w całości napisany w assemblerze potrzebujemy kompilatora. Ja wybrałem Netwide Assembler (http://nasm.sourceforge.net/), który jest prosty, dobry a co najważniejsze darmowy. Dodatkowo możesz zaopatrzyć w program Lizard, który zawiera NASMa i wygodny edytor (http://4programmers.net/file.php/id=1766). Będziemy potrzebować również aplikacji do zapisywania obrazów na dyskietkę, polecam Rawrite (http://uranus.it.swin.edu.au/~jn/linux/rawwrite.htm). Ponieważ nie będziemy implementować obsługi systemu plików (nawet minimalnej do znalezienia kernela na dyskietce) będziemy musieli potrafić łączyć dwa pliki w jeden (program, który to zrobi napiszemy w delphi). Oczywiście moglibyśmy połączyć bootloadera i kernela w jeden plik z poziomu kodu asm, ale chodzi tu o ukazanie istoty bootloadera i kernela jako oddzielnych programów.
program merge;
{$APPTYPE CONSOLE}
var
final, f: file of byte;
buf, q: byte;
begin
if (paramcount < 3) or (paramstr(1) = 'help') then begin
writeln('uzycie: merge.exe <1 plik> <2 plik> <3...> <plik wynikowy>');
readln;
end else begin
assignfile(final, paramstr(paramcount));
rewrite(final);
for q := 1 to paramcount -1 do begin
writeln(paramstr(q));
assignfile(f, paramstr(q));
reset(f);
while not eof(f) do begin
blockread(f, buf, sizeof(buf));
blockwrite(final, buf, sizeof(buf));
end;
closefile(f);
end;
closefile(final);
end;
writeln('-> ' + paramstr(paramcount));
end.
Myśle, że kodu nie trzeba objaśniać, między innymi dlatego, że nie jest to cześć naszego OSa a jedynie narzędzie przy jego tworzeniu.
03h. Budowa dyskietki 1.44.
Przed jakimikolwiek operacjami na nośniku danych musimy poznać jego budowę. My nasz system umieścimy na dyskietce 1.44 i dlatego w skrócie opisze tu jej budowę. Wielkiej filozofii tutaj nie ma, bo taka dyskietka posiada dwie strony (stacja ma dwie głowice), po 80 cylindrów po każdej stronie i 18 sektorów w każdym cylindrze. Każdy sektor może pomieścić 512b danych (28018*512 = 1474560b). Pierwszy sektor nazywamy bootsektorem, ponieważ to właśnie w nim znajduje się program inicjujący system operacyjny, jak również informacje o systemie plików itp. Kolejne sektory są tylko dla nas i możemy umieszczać tam co tylko chcemy, na przykład kernela.
04h. Basic Input/Output System.
BIOS (Basic Input Output System) jest najbardziej podstawowym systemem jaki mamy w naszych komputerach, to dzięki niemu będziemy mogli wyświetlić tekst na ekranie (oczywiście możemy odwołać się bezpośrednio do pamięci, ale mi zależy bardziej na prostocie), czy wczytać kernela z dyskietki - od razu zapomnij o wyświetlaniu np listy plików etc. to musielibyśmy zrobić sami (może innym razem), dzięki niemu będziemy mieli jedynie dostęp do poszczególnych części nośnika. I w końcu to właśnie BIOS rozpoznaje, ładuje do pamięci bootsektor i go uruchamia.
05h. W jaki sposób BIOS ładuje system, czyli wreście coś konkretnego.
Większość z nas wie, że w programie Setup (taka część BIOSa) możemy skonfigurować kolejność napędów, na których komputer będzie szukał systemu. BIOS ładuje pierwsze 512 bajtów z dysku (dyskietki, płyty CD/DVD czy czegokolwiek innego) do pamięci pod adresem 07C0:0000 i skacze w to miejsce (wykonuje znajdujący się tam kod). Aby to nastąpiło BIOS musi wiedzieć, czy pierwsze pół kilobajta jest na pewno bootloaderem (bo przecież komendy procesora /opcodes/ to przecież to samo co inne dane - bajty, a nie na każdym dysku musi być system), w tym celu sprawdzane są 511 i 512 bajt, które powinny wynosić odpowiednio 55h i AAh.
06h. Więc piszemy bootloadera!
Zacznijmy od najprostszego kodu, który po skompilowaniu da nam gotowy do zapisania bootsektor.
;tutaj zaczyna się nasz program
org 7C00h
;tworzymy nieskończoną pętlę
start:
jmp start
;dopełniamy program do 510 bajtów
times 510 - ($ - start) db 0
;tworzymy znacznik bootsektora
db 55h
db AAh
Taki program kompilujemy jako flat-binary (nasm.exe bootsector.asm -f bin -o bootsector.bin /program Lizard utworzy plik *.com/) i za pomocą np Rawrite umieszczamy na dyskietce (traktując skompilowany plik jako jej obraz). Teraz mamy na dyskietce najprawdziwszy bootsektor, który sami napisaliśmy :).
OK, ale obiecałem system wyświetlający napis więc jedziemy dalej. Jak już wiemy bootloader służy jedynie do załadowania kernela (w dzisiejszych systemach preloadera, a ten dopiero kernela, ale my zrobimy to łatwiej), tak więc co zrobimy? Otóż użyjemy funkcji BIOSa aby załadować kernela pod adres 0800:0000 (między bootloaderem a kernelem będziemy mieli 512 wolnych bajtów, które wykorzystamy jako stos, ale o tym później). Aby uprościć sobie sprawę nasz kernel nie będzie typowym plikiem, tylko kodem binarnym w 2 sektorze dyskietki (pamiętasz, w 1 jest nasz bootloader). Użyjemy do tego celu funkcji numer 2 przerwania 13h. Więc do dzieła, najpierw zobaczmy czego wymaga od nas ta funkcja (http://www.ctyme.com/intr/rb-0607.htm), tak więc
mov ah, 2 ;funkcja 2 przerwania 13h
mov al, 10 ;ilość sektorów do przeczytania (10*512 = 5kb)
mov ch, 0 ;cylinder
mov cl, 2 ;sektor (w 1 jest bootsector)
mov dh, 0 ;głowica
mov bx, 0800h ;gdzie załadowac kernel (es:bx)
mov es, bx ;dane do ES możemy umieścić tylko przez inny rejestr
xor bx, bx ;bx równy 0
int 13h ;wywołujemy przerwanie
Podsumowując załadowaliśmy 5kb z dyskietki pod adres 0800:0000, aby uruchomić znajdujący się tam kod, po prostu skaczemy do niego :)
jmp 0800h:0000h
Cały bootloader wygląda tak
org 7C00h
start:
mov ah, 2
mov al, 10
mov ch, 0
mov cl, 2
mov dh, 0
mov bx, 0800h
mov es, bx
xor bx, bx
int 13h
jmp 0800h:0000h
times 510 - ($ - start) db 0
db 55h
db 0AAh
07h. Szkielet jądra systemu.
Pisząc jądro systemu nie musimy się już martwić znacznikiem, ani tym, że nasz kod musi zmieścić się w 512b, po prostu kodujemy :). Jednak mamy pewien haczyk, otóż pisząc wlany system musimy pamiętać, że nie będziemy mieli dobrodziejstw wysokopoziomowych funkcji załadowanego systemu, bo - go nie ma. Czyli musimy zapomnieć np o przerwaniu 21h. Ale nie martw się, BIOS oferuje wystarczającym dla nas zestawem funkcji, a z resztą przecież obiecałem :). Pierw napiszemy najprostszy kod jądra, który jedyne co będzie robił to...działał
; skąd ja to znam
;pamiętasz, kernela załadowaliśmy pod adres 0800h:0000h, wiec
;zaczynamy od 0000h
org 0000h
start:
jmp start
Teraz kompilujemy bootloadera i kernela, i łączymy je w jeden plik używając programu merge.exe, który napisaliśmy i skompilowaliśmy na początku tego tekstu (skompilowaliśmy?)
merge.exe bootsector.bin kernel.bin image.img
Po czym zapisujemy otrzymany plik na dyskietkę programem Rawrite traktując go jako obraz.
Na pozór działanie naszego systemu nie różni się od samego szkieletu bootloadera, lecz wykonuje on następujące czynności: ładuje kernela z 2 sektora dyskietki do pamięci pod adres 0800:0000 i przekazuje mu kontrole, po czym ten zapętla komputer :). Należałoby tez wytłumaczyć dlaczego zapętamy komputer, więc gdybyśmy tego nie zrobili procesor powędrował by dalej i wykonywał przypadkowe komendy znajdujące się na jego drodze (czyli tam gdzie wskazuje IP) co jest nieprzewidywalne w skutkach.
08h. Co ze stosem?
Stos jest takim kawałkiem pamięci do którego mamy szybki dostęp, możemy w nim przechowywać parametry procedury, jej adres powrotny i zmienne lokalne. Bez stosu za wiele byśmy nie zrobili, wiec przydzielimy mu teraz pamięć. Rzecz ta jest trywialnie prosta, bo ogranicza się jedynie do ustawienia jego segmentu, czyli rejestru ss i jego wierzchołka, czyli rejestru sp. Przy ładowaniu kernela do pamięci zostawiliśmy lukę w pamięci o rozmiarze 512b, tam właśnie będzie znajdował się nasz stos.
+-------------------+
| 512b | BOOTLOADER |
|------+------------|
| 512b | STOS |
|------+------------|
| 5kb | KERNEL |
+-------------------+
Mapa pamięci naszego OSa
Powyższa mapa pozwoli ci lepiej zrozumieć podział pamięci w naszym systemie. Skoro juz wszystko wiadomo zajmijmy się kodowaniem.
mov ax, 07C0h
mov ss, ax ;segment stosu
mov sp, 03FEh ;wierzcholek stosu
Jak łatwo się domyślić po tym kroku mamy dostępny 512 bajtowy (256 slow) stos.
09h. Wybieramy tryb graficzny.
Co prawda interesujący tryb graficzny (tekstowy 80x25) jest juz wybrany, ale wybierzemy go jeszcze raz, z dwóch powodów. Pierwszy to taki, ze po wybraniu trybu bufor ekranu się wyczyści, a drugi - będziemy wiedzieć jak to się robi :). Aby to zrobić wystarczy wywołać przerwanie 10h.
xor ah, ah ;funkcja 0
mov al, 3 ;standardowy tryb tekstowy
int 10h ;jedziemy
0Ah. Wyświetlamy tekst.
Wyświetlanie tekstu jest chyba najbardziej skomplikowana rzeczą w naszym systemie, a to dlatego, ze musimy w pętli przenieść wszystkie znaki naszego łańcucha na ekran i przestać w odpowiednim momencie. Do tego musimy przesunąć kursor na ekranie. Do zapisania łańcucha użyjemy "formatu" PChar, który jest ciągiem bajtów zakończonym bajtem #0 (NULL). Pozwoli to nam na wykrycie końca naszego tekstu.
Na początek napiszemy procedurę wyświetlającą jeden znak na ekranie i przesuwająca kursor. Jak zwykle posłużymy sie przerwaniami, które są po prostu łatwe w obsłudze. Do wyświetlenia znaku skorzystamy z funkcji 9 przerwania 10h, za parametry przyjmuje ona wartość ASCII znaku (rejestr AL), numer strony (BH i u nas będzie to 0), atrybut znaku (jego kolor, kolor tła, czy znak ma migać - rejestr BL) i ilość powtórzeń (CX). Po wyświetleniu znaku przesuniemy kursor w prawo funkcjami 2 (zapisz pozycje) i 3 (odczytaj pozycje). Obie swoje parametry maja w DH (wiersz) i DL (kolumna), z jakim wyjątkiem, ze 2 je zapisuje a 3 pobiera.
char:
;procedura wyświetla znak i przesuwa kursor
;wejście: al: znak, bl: atrybut
push bx ;kładziemy BX na stos, aby na końcu procedury go przywrócić
mov ah, 9 ;numer funkcji wyświetlającej znak w miejscu kursora
xor bh, bh ;numer strony ustawiamy na 0
mov cx, 1 ;znak wyświetlimy 1 raz
int 10h ;do dzieła!
;pobierz pozycje
mov ah, 3 ;funkcja pobierania pozycji kursora
xor bh, bh ;numer strony (0)
int 10h ;odczytaj pozycje
;dodaj i zapisz pozycje
add dl, 1 ;dodajemy 1 kolumnie
mov ah, 2 ;funkcja zapisywania
int 10h ;zapisz pozycje
pop bx ;przywróć poprzedni stan BX
ret ;wyjdź z podprogramu
Teraz, gdy mamy juz procedurę do wyświetlania znaku napiszemy procedurę, która wyświetli nam cały łańcuch. Będzie ona polegała na tym, ze w pętli będziemy wyświetlać kolejne bajty danych zaczynając od adresu pierwszego znaku podanego w parametrze (u nas będzie to AX) aż do wystąpienia znaku pustego - NULL (to nie jest spacja!). Znamy juz teorie, teraz przeniesiemy ja do assemblera:
write:
;procedura wyświetla tekst na ekranie
;wejście: ax: wskaźnik do tekstu, bl: atrybut
mov si, ax ;musimy użyć rejestru segmentowego aby zaadresować wskaźnik
.next: ;poczatek petli
mov al, [cs:si] ;zapisz do al wartość aktualnego znaku (patrz parametry dla procedury char)
cmp al, 0 ;porównaj aktualny znak z NULL
je end ;jeśli są równe, skocz do wyjścia
call char ;jeśli nie, wyświetl znak
add si, 1 ;przesuń się w prawo do następnego znaku
jmp .next ;skocz do początku pętli
end: ;tutaj skoczymy, jeśli wystąpi NULL
ret ;wyjdź z podprogramu
I juz możemy zadeklarować łańcuch i go wyświetlić.
0Bh. "Obsługujemy klawiaturę"
Tytuł w cudzysłowie ponieważ nasza klawiatura nie będzie miała nawet własnego bufora, za to będziemy wiedzieli, kiedy użytkownik naciśnie klawisz, będziemy nawet wiedzieli jaki! A na to "wszystko" pozwoli nam funkcja 0 przerwania 16h (http://www.ctyme.com/intr/rb-1754.htm). Zalożenie będzie takie, że w pętli będziemy odczytywać klawisz i sprawdzać który to. Więc do dzieła
start:
xor ah, ah ;takie xorowanie jest szybsze od mov ah, 0
int 16h ;i w AH mamy scancode, w AL kod ASCII klawisza
cmp al, 1Bh ;porównaj al z 1Bh (kod ASCII klawisza ESC)
je reset ;jeśli równe, skocz do procedury resetowania (napiszemy później)
jmp start ;powracamy na początek
0Ch. Resetujemy komputer.
Resetowanie komputera będzie polegać na skoku w cześć BIOSu, którą wykonuje sie po starcie komputera. Jest to adres FFFF:0000. Po drodze do komórki 40:72 zapisujemy wartość, która "powie" BIOSowi, czy ma wykonywać ponownie testy pamięci etc. Aby to zrobił przypisujemy do niej wartość 0, jeśli nie (tzw. gracy reset) wartość 1234h. Po tym po prostu skaczemy do FFFF:0000. Jak zwykle kod
reset:
mov bx, 40h ;używamy BX do zapisania wartości w rejestrze segmentowym
mov ds, bx ;BX ładuje w DS
mov word [ds:72h], 1234h ;ustawiamy gorący reset
jmp 0FFFFh:0000h ;skaczemy do FFFF:0000
0Dh. Sklejamy wszystko w kupe.
Gdy juz mamy wszystko czego potrzebowaliśmy, możemy złożyć nasz pierwszy (pierwszy?) mini system operacyjny. Kompilowanie bootloadera i kernela opisałem wcześniej, wiec nie pozostaje mi nic innego jak podać kompletny kod jadra
org 0000h
;ustawiamy stos
mov ax, 07C0h
mov ss, ax ;segment stosu
mov sp, 03FEh ;wierzchołek stosu
;wybieramy tryb ekranu
xor ah, ah ;funkcja 0
mov al, 3 ;standardowy tryb tekstowy
int 10h ;jedziemy
;wyświetlamy komunikat
mov ax, welcome ;wskaźnik do tekstu
mov bl, 2 ;na zielono
call write ;wykonujemy procedurze
mov ax, name ;wskaźnik do tekstu
mov bl, 5 ;na fioletowo
call write ;wykonujemy procedurę
mov ax, last ;wskaźnik do tekstu
mov bl, 2 ;na zielono
call write ;wykonujemy procedurę
;główna petla
start:
xor ah, ah ;takie xorowanie jest szybsze od mov ah, 0
int 16h ;i w AH mamy scancode, w AL kod ASCII klawisza
cmp al, 1Bh ;porownaj al z 1Bh (kod ASCII klawisza ESC)
je reset ;jeśli równe, skocz do procedury resetowania (napiszemy później)
jmp start ;powracamy na początek
char:
;procedura wyświetla znak i przesuwa kursor
;wejście: al: znak, bl: atrybut
push bx ;kładziemy BX na stos, aby na końcu procedury go przywrócić
mov ah, 9 ;numer funkcji wyświetlającej znak w miejscu kursora
xor bh, bh ;numer strony ustawiamy na 0
mov cx, 1 ;znak wyświetlimy 1 raz
int 10h ;do dzieła!
;pobierz pozycje
mov ah, 3 ;funkcja pobierania pozycji kursora
xor bh, bh ;numer strony (0)
int 10h ;odczytaj pozycje
;dodaj i zapisz pozycje
add dl, 1 ;dodajemy 1 kolumnie
mov ah, 2 ;funkcja zapisywania
int 10h ;zapisz pozycje
pop bx ;przywróć poprzedni stan BX
ret ;wyjdź z podprogramu
write:
;procedura wyświetla tekst na ekranie
;wejście: ax: wskaźnik do tekstu, bl: atrybut
mov si, ax ;musimy użyć rejestru segmentowego aby zaadresować wskaźnik
.next: ;początek pętli
mov al, [cs:si] ;zapisz do al wartość aktualnego znaku (patrz parametry dla procedury char)
cmp al, 0 ;porównaj aktualny znak z NULL
je end ;jeśli są rożne, skocz do wyjścia
call char ;jeśli nie, wyświetl znak
add si, 1 ;przesuń się w prawo do następnego znaku
jmp .next ;skocz do początku pętli
end: ;tutaj skoczymy, jeśli wystąpi NULL
ret ;wyjdź z podprogramu
reset:
mov bx, 40h ;używamy BX do zapisania wartości w rejestrze segmentowym
mov ds, bx ;BX ładuje w DS
mov word [ds:72h], 1234h ;ustawiamy gorący reset
jmp 0FFFFh:0000h ;skaczemy do FFFF:0000
;zmienne
welcome: db 'Witaj w ',0
name: db 'Krzeslo Operating System',0
last: db ', wciśnij ESC aby zrestartowac komputer :)',0
Taki kod kompilujemy, wraz z bootloaderem umieszczamy na dyskietkę za pomocą Rawrite, restartujemy komputer i cieszymy się naszym dziełem.
0Eh. Co dalej?
Jeśli naprawdę myślisz o pisaniu systemów operacyjnych (właściwie jednego na całe życie) to ten artykuł jest dopiero jednym centymetrem w kilometrach, które cię czekają na drodze do napisania funkcjonalnego OSa. Czynność ta jest nie tylko pracochłonna, ale stwarza wiele problemów, których czasem nie można rozwiązać (jest to bardzo trudne).
Jeśli to cię nie zniechęciło pamiętaj o podstawowych zasadach pisania systemów. Po pierwsze nie myśl o stworzeniu super systemu, tylko o tym, żeby on działał. Wielu ludzi przedstawia bitmapy prezentujące bajeranckie GUI i myśli, ze polowa pracy za nimi, pierwsze co należy zrobić to działające jadro i powłokę tekstowa (konsole), która potrafi przyjmować komendy od użytkownika. Musisz tez zaplanować w jaki sposób twój system ma uruchamiać programy, musisz przydzielić im pamięć, stos, napisać mechanizm kolejkowania zadań. Do tego należy obsłużyć system plików (istniejący, lub wymyslec nowy) i mase innych rzeczy.
0Fh. Zakończenie
Mam nadzieję, że osiągnąłem cel, jakim było pokazanie jak napisać swój własny mini (mikro) system operacyjny podzielony na bootloadera i jądro. Postaram się w miarę nowych pomysłów i wiedzy dopisywać kolejne części :). Będę wdzięczny za wszelkie uwagi, komentarze odnośnie tego artykułu, które możesz kierować pod maila, ew. na ircu (#netsoft @ irc.ircnet.pl).
Pozdrowienia dla wszystkich użytkowników 4p i deweloperów grupy Netsoft.
Wolverine (wolverine at daminet dot pl).
Tylko u mnie w kodzie źr. nie ma enterów?
@Quarry038 Kody ASCII zprawdzisz w tablicy
Napisałeś jaki jest kod ASCII klawisza [Esc], a czy mógłbyś mi podać kod ASCII klawisza [D] oraz kod ASCII klawisza [A]?
pomozcie! czytanie sektora (bynajmniej u mnie) nie dziala
dyskietka! a nie lepiej płyta cd czy coś
rozwinołem to i umię jeszcze się wyłanczać :)
super brawo!!! tylko jakie są kody do innych klawiszy?????
czy to w assamblerze ???
Po wciśnięciu ESC wirtualny komp się cały czas resetuje, aż nacisnę drugi raz ESC :P Trzeba by to poprawić... Ja na assemblerze się za bardzo nie znam, bo dopiero się uczę go, ale mi się wydaje, że wystarczy wyczyścić gdzieś jeszcze rejestr AH z kodem klawisza, albo może w inny sposób pobrać klawisz. Odpalałem go na emulatorze Virtual PC z dyskietki. Macie jakieś pomysły? A może to ta maszyna wirtualna tak ma? Jak jest z tym u was?
A to w C++ (klasa ifstream) - http://nopaste.gamedev.pl/?id=2083
Mój program merge w C#: http://nopaste.gamedev.pl/?id=2025
//Tylko ja go Binder nazwałem :)
Tego IDE, Lizard, nie ma go już ??
darktemplar ~ yyy, ocb?
Artykuł bardzo fajny - imo dobrze, że taki szczegółowy, bo przedstawiając znany nam problem powinno się do niego podchodzić w sposób "hmm... o co ja bym zapytał, gdybym się na tym nie znał". Ja osobiście polecam stronkę http://www.nondot.org/sabre/os/articles chociaż tak naprawdę nie zagłębiałem się w nią (zamierzam :P), tylko pobieżnie obejrzałem.
Mam tylko pytanie co do ustalenia stosu: co ze spodem stosu? Rejestr bp nie powinien być ustawiony na początkową wartość sp?
Kochaniutki atrybut w trybie tekstowym jest przez Int10h/0xE zupełnie olewany! Polecam http://www.ctyme.com/intr/rb-0106.htm
Tu podaję źródło mojej implementacji programu merge dla C:
Użycie:
merge.c
errorlevel 0: wszystko OK
errorlevel 255: nie podano plików wejściowych
errorlevel 1...254: błąd otwierania pliku ... (np. plik nie istnieje)
Czym skompilować ten kod w Delphi???
kompiluje Borlandowskim Delphi Developer Studio 2006 - i wywala jakies syfisate błedy!!!
kompiluje Free Pascal'em - krzyczy że nie zna procedury assignfile!!!
NIE znam Delphi i NIE chcę się (przynajmniej narazie) w niego zagłębiać
POMOCY !!!
W sumie mozna, ale potem by bylo, ze jeden plik a cos tam jeszcze dogrywamy, tak jak jest teraz latwiej wytlumaczyc.
Ja sie tam nie znam więc moge sie mylić ale mam dwa spostrzeżenia.
Po pierwsze czy nie można pisać całości razem tj. bootloader i cała reszta, przecież tak czy siak sie skompiluje.
Dalej to czy nie można by połączyć procedur write i char? Co miało na celu rozbicie ich na dwie różne?
Jezeli naprawde chcecie poczytac o pisaniu OSow to zapraszam na POLSKI portal dla OS Developerow:
www.areoos.com/osdevpl/
MrKaktus
hmm to bardzo dziwne bo bios sam w dl zostawia numer napedu z ktorego odczytal bootloadera.
Gratuluje świetnego artukułu!
W kodzie BOOTLOADER'a można by dopisać nastepującą linijke:
MOV DL, 0 ;Numer dysku (0=A:, 1=B:, 80h=C:, 81h=D:, itd.)
U mnie bez tego nie chiało dzialać. Poza tym wszystko jest pięknie. Pierwszy tekst który rozumiem od początku do końca. Co prawda nie mam Delphi, ale MERGE po drobnych zmianach skompilowalo sie w Pascalu. Lece czytać drugą część!
Obrazek? Poczytaj o grafice w 13h, tam sie zazwyczaj korzysta tylko z biosa, wiec pojdzie, tylko musisz byc w RM.
poprawione thx
Znalazłem błąd w artykule i prosze o jego poprawienie :)
"Obie swoje parametry maja w DH (kolumna) i DL (wiersz)"
Obydwa parametry są ustawione odwrotnie...
DH - wiersz oraz DL - kolumna
Natknołem się na niego gdy potrzebowałem, aby tekst mi się wyświetlał w następnej linijce:)
A no bo tak mialo byc, zauwaz, ze na poczatku ten art nazywal sie "pisanie osa dla topornych", ale zostal przemianowany, z reszta na wiele wiecej mnie nie stac :P
Jeżeli patrzeć czy ten art realizuje swoje założenia to oceniam go na 5. Natomiast jeżeli mam oceniaś forme to juz nie jest tak super. Tekst jest kierowany jakby dla kogoś kto nigdy sie nawet assemblerem nie bawił. Jestem poglądu że trzeba postawić pewną poprzeczke bo nie każdy moze sie bawić w pisanie OS, trzeba mieć troche własnej wiedzy zdobytej wcześniej. Jeżli ktoś jest zainteresowany poważnie pisaniem OSa w assemblerze i tylko assemblerze to prosze o kontakt na redsoviet@wp.pl - poszukuje kogoś do pomocy z jakimiś dobrymi pomysłami, ale przedewszystkim z podstawową wiedzą odnośnie tematu :)
Jedyny kurs jaki rozumiem w całości:) Jest super!
Może by tak więcej... gdyż brak wiedzy mnie gryzie;)
Ocena końcowa - 6!
Ta strona (http://www.osdever.net/) też fajna, tylko ma 1 szkopuł - NIENAWIDZĘ czytać po angielsku :) Nie znaczy, że nie rozumiem, no ale... Jakoś dłużej to trwa :) No i tak nie odpręża już lektura :)
Super art !!! 6 !!!
dzięki tobie Wolverine mam o tym jakiekolwiek pojęcie ;)
czekam na arta o systemie plików :]
powodzenia.
Jak się trochę pouczę assemblera to spróbuję jakiś system na trybie 13h napisać ;P
hmmm, fajnie byłoby napisać jakąś grę na 13h z własnym systemem operacyjnym :D
może się nie doczytałem w arcie, ale taki plik com może mieć tylko 64 KB ;)
ale to i tak bardzo dużo skoro skompilowany kernel zajmuje tylko ok 240 bajtów ;)
Jak bys potrzebowal pomocy to mozesz na mnie liczyc : D. (GG/Email: 2366471 / ice_man11 (at) wp.pl). Aha, dla zainteresowanych http://www.osdever.net/. : )
taa, sam mysle o napisaniu czegos co bedzie mialo prosta konsole i potrafilo ladowac i uruchamiac programy :)
BTW warto zaopatrzyc sie w Bochs'a - emulator PC (http://bochs.sourceforge.net). No i co tu dodac, pisanie OS'a to cool sprawa i kupa radochy ze komp rusza z NASZEJ dyskietki : )....
i A20 :) imo wszystko ma sens, np zeby sie czegos nauczyc :)
No np GRUB :). W sumie przelaczyc sie do PM to raptem kilka linijek kodu asemblera (tylko jeszcze to GDT) Kurcze, jakos mnie ciagnie zeby pisac system :/. Tylko czy to ma jakikolwiek sens?...
wlasciwie to pmode zajmie sie juz dobry bootloader, jesli uzyjesz gotowca ...
szkoda ze nie ma nic o protected mode. Kiedys nawet mialem pomysl zeby napisac system, ale zatrzymalem sie gdzies przy IDT i przerwaniach :P... mialem format dysku i juz nie ma projektu :(. A nawet byla obsluga klawiatury :P . oczywiscie w protected mode ;).
a można to samo zrobić tyle że w C albo Pascalu? Fajny artykuł tylko jak dla mnie troszku za mały - mogłeś jeszcze jakąś funkcję dorzucić do tego Os'a
Wg mnie, niesamowity art. Bardzo dobrze napisany. Dosc szczegółowo. Mozna by bylo jeszcze zamieścić linka do gotowego obrazu z nowym systemem. Podobnie jak _ece czekam na kontynuacje.
Jako uzupełnienie http://4programmers.net/file.php/id=1677
Bardzo ciekawy art. Czekam na kolejne części
Jest jeszcze 1 literówka...
poprawione, mam nadzieje, ze wszystkie..
Popraw błędy!!! [sciana]
cze dziex za arta jest super ale mam pytanie jak w assamberze dodać obrazek ??
Brawo!!!