|
- Programming4Fun http://www.programming4fun.pun.pl/index.php - Poradniki http://www.programming4fun.pun.pl/viewforum.php?id=19 - Pisanie systemów operacyjnych cz. I - tryb rzeczywisty http://www.programming4fun.pun.pl/viewtopic.php?id=6 |
| arnon - 2014-07-10 11:06:02 |
Jak napisać własny, PRAWDZIWY SYSTEM OPERACYJNY - TAKI CO MA JĄDRO I MOŻNA NORMALNIE ZAINSTALOWAĆ NA KOMPUTERZE!!!!!! Kod: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. Kod:;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 :). Kod: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 :) Kod:jmp 0800h:0000h Cały bootloader wygląda tak Kod: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. Kod:; 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?) Kod:merge.exe bootsector.bin kernel.bin image.img Po czym zapisujemy otrzymany plik na dyskietkę programem Rawrite traktując go jako obraz. Kod:+-------------------+ | 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. Kod: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. Kod:xor ah, ah ;funkcja 0 mov al, 3 ;standardowy tryb tekstowy int 10h ;jedziemy 0Ah. Wyświetlamy tekst. Kod: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: Kod: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 podprogramuI juz możemy zadeklarować łańcuch i go wyświetlić. Kod: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. 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. Kod: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 :)',0Taki kod kompilujemy, wraz z bootloaderem umieszczamy na dyskietkę za pomocą Rawrite, restartujemy komputer i cieszymy się naszym dziełem. |