Pascal Bare Bones: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
m (Compiling and Linking the modules: added -CX -XXs to compiler command and --gc-sections -s to linker command)
(Clarified after 64-bit toolchains got used, which was a pretty bad idea.)
Line 5: Line 5:
Tools needed to build the project:
Tools needed to build the project:


* FPC (You'll need the i386 version as that's the architecture we're targeting)
* FPC
* NASM
* NASM
* binutils(ld) built with elf support
* binutils(ld) built for [[GCC Cross-Compiler|32-bit elf support]]


=== stub.asm ===
=== stub.asm ===
Line 452: Line 452:


Then link the whole thing with:
Then link the whole thing with:
ld --gc-sections -s -Tlinker.script -o kernel.obj stub.o kernel.o multiboot.o system.o console.o
i386-elf-ld --gc-sections -s -Tlinker.script -o kernel.obj stub.o kernel.o multiboot.o system.o console.o


* --gc-sections -s, in combination with -CX -XXs above, eliminates RTTI symbols from resulting binary
* --gc-sections -s, in combination with -CX -XXs above, eliminates RTTI symbols from resulting binary

Revision as of 08:10, 15 September 2015

Difficulty level

Medium

Credit flies to De Deyn Kim for the freepascal, public domain version of BareBones.

Tools needed to build the project:

  • FPC (You'll need the i386 version as that's the architecture we're targeting)
  • NASM
  • binutils(ld) built for 32-bit elf support

stub.asm

;/////////////////////////////////////////////////////////
;//                                                     //
;//               Freepascal barebone OS                //
;//                      stub.asm                       //
;//                                                     //
;/////////////////////////////////////////////////////////
;//
;//     By:             De Deyn Kim <kimdedeyn@skynet.be>
;//     License:        Public domain
;//

;
; Kernel stub
;

;
; We are in 32bits protected mode
;
[bits 32]

;
; Export entrypoint
;
[global kstart]

;
; Import kernel entrypoint
;
[extern kmain]

;
; Posible multiboot header flags
;
MULTIBOOT_MODULE_ALIGN          equ     1<<0
MULTIBOOT_MEMORY_MAP            equ     1<<1
MULTIBOOT_GRAPHICS_FIELDS       equ     1<<2
MULTIBOOT_ADDRESS_FIELDS        equ     1<<16

;
; Multiboot header defines
;
MULTIBOOT_HEADER_MAGIC          equ     0x1BADB002
MULTIBOOT_HEADER_FLAGS          equ     MULTIBOOT_MODULE_ALIGN | MULTIBOOT_MEMORY_MAP
MULTIBOOT_HEADER_CHECKSUM       equ     -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)

;
; Kernel stack size
;
KERNEL_STACKSIZE                equ     0x4000

section .text

;
; Multiboot header
;
align 4
dd MULTIBOOT_HEADER_MAGIC
dd MULTIBOOT_HEADER_FLAGS
dd MULTIBOOT_HEADER_CHECKSUM

;
; Entrypoint
;
kstart:
        mov esp, KERNEL_STACK+KERNEL_STACKSIZE  ;Create kernel stack
        push eax                                ;Multiboot magic number
        push ebx                                ;Multiboot info
        call kmain                              ;Call kernel entrypoint
        cli                                     ;Clear interrupts
        hlt                                     ;Halt machine

section .bss

;
; Kernel stack location
;
align 32
KERNEL_STACK:
        resb KERNEL_STACKSIZE

kernel.pas

{
/////////////////////////////////////////////////////////
//                                                     //
//               Freepascal barebone OS                //
//                      kernel.pas                     //
//                                                     //
/////////////////////////////////////////////////////////
//
//      By:             De Deyn Kim <kimdedeyn@skynet.be>
//      License:        Public domain
//
}

unit kernel;

interface

uses
        multiboot,
        console;

procedure kmain(mbinfo: Pmultiboot_info_t; mbmagic: DWORD); stdcall;

implementation

procedure kmain(mbinfo: Pmultiboot_info_t; mbmagic: DWORD); stdcall; [public, alias: 'kmain'];
begin
        kclearscreen();
        kwritestr('Freepascal barebone OS booted!');
        xpos := 0;
        ypos += 1;

        if (mbmagic <> MULTIBOOT_BOOTLOADER_MAGIC) then
        begin
                kwritestr('Halting system, a multiboot-compliant boot loader needed!');
                asm
                        cli
                        hlt
                end;
        end
        else
        begin
                kwritestr('Booted by a multiboot-compliant boot loader!');
                xpos := 0;
                ypos += 2;
                kwritestr('Multiboot information:');
                xpos := 0;
                ypos += 2;
                kwritestr('                       Lower memory  = ');
                kwriteint(mbinfo^.mem_lower);
                kwritestr('KB');
                xpos := 0;
                ypos += 1;
                kwritestr('                       Higher memory = ');
                kwriteint(mbinfo^.mem_upper);
                kwritestr('KB');
                xpos := 0;
                ypos += 1;
                kwritestr('                       Total memory  = ');
                kwriteint(((mbinfo^.mem_upper + 1000) div 1024) +1);
                kwritestr('MB');
        end;

        asm
                @loop:
                jmp @loop
        end;
end;

end.

console.pas

{
/////////////////////////////////////////////////////////
//                                                     //
//               Freepascal barebone OS                //
//                       console.pas                   //
//                                                     //
/////////////////////////////////////////////////////////
//
//      By:             De Deyn Kim <kimdedeyn@skynet.be>
//      License:        Public domain
//
}

unit console;

interface

var
        xpos: Integer = 0;
        ypos: Integer = 0;

procedure kclearscreen();
procedure kwritechr(c: Char);
procedure kwritestr(s: PChar);
procedure kwriteint(i: Integer);
procedure kwritedword(i: DWORD);

implementation

var
        vidmem: PChar = PChar($b8000);

procedure kclearscreen(); [public, alias: 'kclearscreen'];
var
        i: Integer;
begin
        for i := 0 to 3999 do
                vidmem[i] := #0;
end;

procedure kwritechr(c: Char); [public, alias: 'kwritechr'];
var
        offset: Integer;
begin
        if (ypos > 24) then
                ypos := 0;

        if (xpos > 79) then
                xpos := 0;

        offset := (xpos shl 1) + (ypos * 160);
        vidmem[offset] := c;
        offset += 1;
        vidmem[offset] := #7;
        offset += 1;

        xpos := (offset mod 160);
        ypos := (offset - xpos) div 160;
        xpos := xpos shr 1;
end;

procedure kwritestr(s: PChar); [public, alias: 'kwritestr'];
var
        offset, i: Integer;
begin
        if (ypos > 24) then
                ypos := 0;

        if (xpos > 79) then
                xpos := 0;

        offset := (xpos shl 1) + (ypos * 160);
        i := 0;

        while (s[i] <> Char($0)) do
        begin
                vidmem[offset] := s[i];
                offset += 1;
                vidmem[offset] := #7;
                offset += 1;
                i += 1;
        end;

        xpos := (offset mod 160);
        ypos := (offset - xpos) div 160;
        xpos := xpos shr 1;
end;

procedure kwriteint(i: Integer); [public, alias: 'kwriteint'];
var
        buffer: array [0..11] of Char;
        str: PChar;
        digit: DWORD;
        minus: Boolean;
begin
        str := @buffer[11];
        str^ := #0;

        if (i < 0) then
        begin
                digit := -i;
                minus := True;
        end
        else
        begin
                digit := i;
                minus := False;
        end;

        repeat
                Dec(str);
                str^ := Char((digit mod 10) + Byte('0'));
                digit := digit div 10;
        until (digit = 0);

        if (minus) then
        begin
                Dec(str);
                str^ := '-';
        end;

        kwritestr(str);
end;

procedure kwritedword(i: DWORD); [public, alias: 'kwritedword'];
var
        buffer: array [0..11] of Char;
        str: PChar;
        digit: DWORD;
begin
        for digit := 0 to 10 do
                buffer[digit] := '0';

        str := @buffer[11];
        str^ := #0;

        digit := i;
        repeat
                Dec(str);
                str^ := Char((digit mod 10) + Byte('0'));
                digit := digit div 10;
        until (digit = 0);

        kwritestr(@Buffer[0]);
end;

end.

multiboot.pas

unit multiboot;

interface

const
        KERNEL_STACKSIZE = $4000;

        MULTIBOOT_BOOTLOADER_MAGIC = $2BADB002;

type
        Pelf_section_header_table_t = ^elf_section_header_table_t;
        elf_section_header_table_t = packed record
          num: DWORD;
          size: DWORD;
          addr: DWORD;
          shndx: DWORD;
        end;

        Pmultiboot_info_t = ^multiboot_info_t;
        multiboot_info_t = packed record
          flags: DWORD;
          mem_lower: DWORD; { Amount of memory available below 1mb }
          mem_upper: DWORD; { Amount of memory available above 1mb }
          boot_device: DWORD;
          cmdline: DWORD;
          mods_count: DWORD;
          mods_addr: DWORD;
          elf_sec: elf_section_header_table_t;
          mmap_length: DWORD;
          mmap_addr: DWORD;
        end;

        Pmodule_t = ^module_t;
        module_t = packed record
          mod_start: DWORD;
          mod_end: DWORD;
          name: DWORD;
          reserved: DWORD;
        end;

        Pmemory_map_t = ^memory_map_t;
        memory_map_t = packed record
          size: DWORD;
          { You can declare these two as a single qword if your compiler supports it }
          base_addr_low: DWORD;
          base_addr_high: DWORD;
          { And again, these can be made into one qword variable. }
          length_low: DWORD;
          length_high: DWORD;
          mtype: DWORD;
        end;

implementation

end.

system.pas

unit system;

interface

type
 cardinal = 0..$FFFFFFFF;
 hresult = cardinal;
 dword = cardinal;
 integer = longint;
 
 pchar = ^char;

implementation

end.

Linker script

linker.script

ENTRY(kstart)
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 = .;
}

Compiling and Linking the modules

Assemble stub.asm with:

nasm -f elf stub.asm -o stub.o

The Pascal modules with:

fpc -Aelf -n -O3 -Op3 -Si -Sc -Sg -Xd -CX -XXs -Rintel -Tlinux kernel.pas
  • -Aelf - instructs the internal fpc assembler to output an ELF object.;
  • -n - ignores fpc.cfg;
  • -O3 - perform level 3 optimizations;
  • -Op3 - tune code for PentiumPro / P-II / Cyrix 6x86 / K6 (TM);
  • -Si - enable C++ style INLINE keyword;
  • -Sc - enable C style operators;
  • -Sg - enable support for labels;
  • -Xd - tells the compiler to forget the standard library path;
  • -CX - tells the compiler to create smartlinkable units
  • -XXs - tells the compiler to do smartlinking (-XX) and debugging symbols stripping (-Xs)
  • -Rintel - sets the inline assembly syntax to intel style;
  • -Tlinux - specifies that the target operating system is Linux. (Don't even ask!)

Then link the whole thing with:

i386-elf-ld --gc-sections -s -Tlinker.script -o kernel.obj stub.o kernel.o multiboot.o system.o console.o
  • --gc-sections -s, in combination with -CX -XXs above, eliminates RTTI symbols from resulting binary