1 Wstęp
1.1 fpc i systemy 64-bitowe
1.2 Drzewo katalogów
2 BIOS
3 Co robi GRUB?
4 start.asm
5 kernel.pas
6 kernel.ld
7 Makefile
Wstęp
Celem tego kursu będzie pokazanie jak napisać prosty system operacyjny w Pascalu. Oto lista narzędzi które będziemy potrzebowali: * system operacyjny (najlepiej Linux) * Free Pascal (http://www.freepascal.org) * NASM (http://sourceforge.net/projects/nasm/) * GNU Make (http://www.gnu.org/software/make/) * GNU LD (http://www.gnu.org/software/binutils/) * QEMU (http://www.qemu.org/) lub Bochs (http://bochs.sourceforge.net/) * mount (w Windowsie można użyć VFD (http://chitchat.at.infoseek.co.jp/vmware/vfd.html#download))fpc i systemy 64-bitowe
Ponieważ nasz system będzie 32-bitowy, pisząc pod Linuksem 64-bitowym w repozytorium mamy standardowo 64-bitowy kompilator. W takim przypadku musimy zastosować się do wskazówek ze strony: http://wiki.lazarus.freepascal.org/Cross_compiling#To_LinuxDrzewo katalogów
Powinno wyglądać mniej więcej tak: ``` 4pOS + img + output + src + kernel console.pas kernel.pas start.asm system.pas kernel.ld 4pos.ima Makefile ```BIOS
Po uruchomieniu komputera pierwszym programem który jest uruchamiany jest BIOS. Na początku sprawdza sprzęt, a następnie skanuje dyski i napędy w poszukiwaniu systemu. O tym czy na nośniku jest zapisany system operacyjny świadczą ostatnie dwa bajty pierwszego sektora. Są wtedy równe 0x55 i 0xAA. Po znalezieniu systemu BIOS ładuje pierwszy sektor pod adres 0x7C00 i skacze tam. W przypadku naszego systemu BIOS załaduje GRUBa.Co robi GRUB?
GRUB po uruchomieniu odblokowuje linię A20, ustawia GDT, włącza tryb chroniony procesora i ładuje nasz kernel do pamięci. Więc nie musimy się teraz martwić o te rzeczy, ale pisanie sterowników do dysków i obsługi systemów plików i tak nas nie ominie.start.asm
Na początku króciutki kod w asmie: ```asm [BITS 32] [SECTION .text] mboot: dd 0x1BADB002 ; Header magic dd 0x00001 ; Flags (PAGE_ALIGN) dd -(0x1BADB002+0x00001) ; Checksum ``` Mamy tutaj nagłówek multiboot: Nazwa | Wartość | Opis Magic | 0x1BADB002 | Magiczna stała wartość Flags | 0x00000001 | Flagi. Zapalamy tylko pierwszy bit flagi PAGE_ALIGN. Oznacza ona, że adres kernela jest wyrównany do rozmiaru strony (4KiB) Checksum | -(0x1BADB002+0x00001) | Suma kontrolna. Musi być równa sumie pól `magic` i `flags`. Oczywiście pól jest o wiele więcej, ale tylko te trzy są wymagane.Więcej informacji: http://www.gnu.org/software/grub/manual/multiboot/multiboot.html
kernel.pas
```pascal {$ASMMODE INTEL}unit kernel;
interface
uses console;
procedure StartKernel;
implementation
procedure StartKernel;[public,alias:'StartKernel'];
begin
ClearScreen;
SetColor(Green,Black);
PrintStr('4pOS 0.1');
while true do begin end; //Nieskonczona petla
end;
end.
Ten plik też długi nie jest. Mamy tu jedynie czyszczenie ekranu i wypisanie zielonego napisu `4pOS 0.1`. Następnie procesor wskakuje do nieskończonej pętli. Proste prawda? ;)
<h1>console.pas</h1>
```pascal
unit Console;
interface
const
Black = 0;
Blue = 1;
Green = 2;
Cyan = 3;
Red = 4;
Magenta = 5;
Brown = 6;
LightGray = 7;
DarkGray = 8;
LightBlue = 9;
LightGreen = 10;
LightCyan = 11;
LightRed = 12;
LightMagenta = 13;
Yellow = 14;
White = 15;
var
Screen: PChar = PChar ($B8000);
CursorX, CursorY: integer;
Color: char;
Background: integer = 0;
procedure ClearScreen;
procedure PrintStr(text: PChar);
procedure PrintInt(number:integer);
procedure PrintChar(ch: Char);
procedure SetColor(txt,back:integer);
procedure SetXY(x,y: integer);
procedure Scroll;
implementation
procedure ClearScreen;
var
i: integer;
begin
for i:=0 to 2000 do
begin
Screen[i*2]:=#0;
Screen[i*2-1]:=char(white);
end;
end;
procedure PrintStr(text: PChar);
var
address: Word;
i: integer;
begin
i:=0;
if (CursorX > 79) or (text[i] = #13) then
begin
SetXY(0, CursorY+1);
end
else
begin
if (text[i] = #10) then
begin
SetXY(CursorX, CursorY+1);
end
else
begin
if (CursorY > 24) then
begin
SetXY(CursorX, 24);
Scroll;
end;
repeat
address:= CursorX*2 + CursorY * 160;
Screen[address]:= text[i];
Screen[address+1]:= color;
SetXY(CursorX+1, CursorY);
i:=i + 1;
until text[i] = #0
end;
end;
end;
procedure PrintInt(number: integer);
var
chars: array [0..11] of Char;
negative: Boolean;
i: integer;
txt: PChar;
begin
txt:= @chars[11];
i:=11;
negative:= False;
if number<0 then
begin
number:=-number;
negative:= True;
end;
repeat
chars[i]:= char((number mod 10) + byte('0'));
number:= number div 10;
number-=1;
Dec(txt);
until number=0;
if negative=True then
begin
PrintStr('-');
end;
PrintStr(txt);
end;
procedure PrintChar(ch: Char);
var
address: Word;
begin
if (CursorX > 79) or (ch = #13) then
SetXY(0, CursorY+1)
else
if ch = #10 then
SetXY(CursorX, CursorY+1)
else
if CursorY > 24 then
begin
SetXY(CursorX, 24);
Scroll;
end;
address:= CursorX*2 + CursorY * 160;
Screen[address]:= ch;
Screen[address+1]:= color;
SetXY(CursorX+1, CursorY);
end;
procedure SetColor(txt,back: integer);
begin
color:=char(txt+back*16);
end;
procedure Scroll;
var
address : word;
begin
for address:=0 to 1920 do
begin
Screen[address*2] := Screen[address*2+160];
Screen[address*2+1] := Screen[address*2+1+160];
end;
for address:=1921 to 2000 do
begin
Screen[address*2] := #0;
Screen[address*2+1]:= char(15);
end;
end;
procedure SetXY(x,y: integer);
var
temp: integer;
begin
CursorX := x;
CursorY := y;
temp:=y*80+x;
outportb($3D4, 14);
outportb($3D5, temp>>8);
outportb($3D4, 15);
outportb($3D5, temp);
end;
end.
Tu mamy już dłuższy kod. Screen jest to adres pamięci ekranu. Wszystko co tam zapiszemy zostanie wyświetlone przez kartę graficzną na ekranie. GRUB ustawił kolorowy tryb tekstowy 80x25, gdzie na każdy znak przypadają dwa bajty. Pierwszy to poprostu znak, a drugi to kolor. Bity 7..4 to kolor tła, a bity 3..0 to kolor znaku. Myślę, że kod nie wymaga większego tłumaczenia.
kernel.ld
``` OUTPUT_FORMAT("elf32-i386") ENTRY(StartKernel) SECTIONS { .text 0x100000 : { text = .; _text = .; __text = .; *(.text) . = ALIGN(4096); } .data : { data = .; _data = .; __data = .; *(.data) kimage_text = .; LONG(text); kimage_data = .; LONG(data); kimage_bss = .; LONG(bss); kimage_end = .; LONG(end); . = ALIGN(4096); } .bss : { bss = .; _bss = .; __bss = .; *(.bss) . = ALIGN(4096); } end = .; _end = .; __end = .; } ``` Powyższy kod to skrypt linkera. Tłumaczenie tego odbiega od tematu, więc dam tylko linka: http://sourceware.org/binutils/docs-2.21/ld/Scripts.html#ScriptsMakefile
``` 4pOS.bin: output/start.o kernel ld -m elf_i386 output/start.o output/kernel.o output/system.o output/console.o -T"src/kernel.ld" -o "4pOS.bin"output/start.o: src/kernel/start.asm
nasm -f elf32 src/kernel/start.asm -o output/start.o
kernel:
/sciezka/do/ppc386 -a -Aelf src/kernel/kernel.pas -Fu"src/kernel" -FE"output" -O3 -Op3 -Si -Sc -Sg -Xd -Tlinux -Rintel
install: 4pOS.bin
sudo mount 4pos.ima img -o loop
sudo cp 4pOS.bin img
sudo umount img
clean:
rm output/*
run: install
qemu -fda 4pos.ima
Aby skompilować OSa należy wydać polecenie `make`, a obraz dyskietki stworzymy poleceniem `make run`.
<h1>Zakończenie</h1>
Efekt nie jest może widowiskowy, ale już wkrótce druga część kursu.

I źródła: [4pOS01.zip](//4programmers.net/Download/111569/211)
jak ktoś nie ma linuxa to może użyć programu cygwin terminal
Podejrzewam, że WinDos korzysta z przerwań DOSa (int 21h), więc żeby zadziałało musiałbyś zaimplementować te przerwania we własnym kernelu. O przerwaniach było w kolejnej części, która zaginęła w akcji. Jeśli jesteś zainteresowany daj znać - podeślę na PW.
Ewentualnie można przepisać te unity tak by nie korzystały z mechanizmów DOSa/Windowsa.
Jednak obie opcje według mnie mijają się trochę z celem, chyba, że chcesz utrzymywać kompatybilność z tymi systemami.
Przede wszystkim jednak nie mieszaj w to TP - jest mało warty podczas pisania kernela.
A tak teoretycznie (i praktycznie), jak skopiuje pliki PAS z folderu systemowego TurboPascala (np.: WinDos), to będę miał więcej komend/możliwości?
A to się będę mógł cieszyć podwójnie - Minecraft 1.8 oraz druga część kursu :)
We wrześniu :)
@lukasz1235: Kiedy kolejna część? ;)
Jakiś bug w kojocie. Już zgłoszone na Redmine.
Teraz jest znacznie lepiej :)
Nie mogę się doczekać kolejnych części ;)
BTW, nie można pobrać załącznika.