OS Programming

OS w Pascalu cz. 1

  • 2013-11-17 21:16
  • 11 komentarzy
  • 1799 odsłon
  • Oceń ten tekst jako pierwszy
Spis treści

     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 console.pas
     7 kernel.ld
     8 Makefile
     9 Zakończenie


Wstęp


Celem tego kursu będzie pokazanie jak napisać prosty system operacyjny w Pascalu. Oto lista narzędzi które będziemy potrzebowali:

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_Linux

Drzewo 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:
[BITS 32]
[SECTION .text]
mboot:
   dd 0x1BADB002            ; Header magic
   dd 0x00001               ; Flags (PAGE_ALIGN)
   dd -(0x1BADB002+0x00001) ; Checksum

Mamy tutaj nagłówek multiboot:
NazwaWartośćOpis
Magic0x1BADB002Magiczna stała wartość
Flags0x00000001Flagi. 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


{$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? ;)

console.pas


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#Scripts

Makefile


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.
       

Zakończenie


Efekt nie jest może widowiskowy, ale już wkrótce druga część kursu.


I źródła: 4pOS01.zip (109,22 KB)

11 komentarzy

killer12233 2015-07-11 16:45

jak ktoś nie ma linuxa to może użyć programu cygwin terminal

lukasz1235 2015-01-18 19:06

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.

pawel24pl 2015-01-17 22:30

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?

Brak avatara
JakubProgramer64Bit 2013-11-18 20:47

Hej, kiedy następna część bo to fajne :D bardzo mi się podoba ten kurs zwłaszcza gdy jestemmłodym linuxiarzem

lukasz1235 2013-11-17 21:11

2011. Gdzieś był ale zaginął w akcji. Jeśli Ci zależy to daj do siebie jakiś kontakt to podeślę.

Brak avatara
Bartek 2013-11-17 21:06

we wrześniu, którego roku?

Patryk27 2011-08-29 18:53

A to się będę mógł cieszyć podwójnie - Minecraft 1.8 oraz druga część kursu :)

lukasz1235 2011-08-29 18:50

We wrześniu :)

Patryk27 2011-08-29 16:21

@lukasz1235: Kiedy kolejna część? ;)

lukasz1235 2011-08-29 11:07

Jakiś bug w kojocie. Już zgłoszone na Redmine.

Patryk27 2011-08-27 11:35

Teraz jest znacznie lepiej :)
Nie mogę się doczekać kolejnych części ;)
BTW, nie można pobrać załącznika.