Pascal: Difference between revisions
[unchecked revision] | [unchecked revision] |
No edit summary |
mNo edit summary |
||
Line 6: | Line 6: | ||
The successors of Pascal were developed to address the weaknesses of Pascal in this regard. However, with the widespread adoption of the Object Pascal extensions (e.g.. unit, bitwise operators), many of these weaknesses (most specifically the lack of support for separate compilation) were eliminated. The reputation of Pascal as a toy language has unfairly persisted in many places however. |
The successors of Pascal were developed to address the weaknesses of Pascal in this regard. However, with the widespread adoption of the Object Pascal extensions (e.g.. unit, bitwise operators), many of these weaknesses (most specifically the lack of support for separate compilation) were eliminated. The reputation of Pascal as a toy language has unfairly persisted in many places however. |
||
=== Past uses in OS development === |
|||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
== Commonly used tools == |
== Commonly used tools == |
||
The most popular pascal compilers today seems to be Delphi and FreePascal Compiler, and to a smaller extent Turbo Pascal. To date no operating system has been written in Delphi. Although it may be theoretically possible, it seems to be too much work hacking the resulting executable into a working OS |
The most popular pascal compilers today seems to be Delphi and FreePascal Compiler, and to a smaller extent Turbo Pascal. To date no operating system has been written in Delphi. Although it may be theoretically possible, it seems to be too much work hacking the resulting executable into a working OS |
||
FPC(FreePascal Compiler) is more suited to the job as it's highly configurable and generates code to a great number of platforms |
FPC (FreePascal Compiler) is more suited to the job as it's highly configurable and generates code to a great number of platforms |
||
== Interfacing Pascal with Assembler == |
== Interfacing Pascal with Assembler == |
Revision as of 21:04, 1 February 2009
Historical note
The original Standard Pascal was in many ways a different language from the Object Pascal that most people today are familiar with, being much simpler but also more limited.
Dr. Wirth himself did not intend Pascal for systems programming, and his ongoing language evolution were the languages Modula-2 and Oberon. Both languages are related to Pascal in a similar way as C++, C# and Java are related to C. Oberon-2 supports all concepts of object-oriented programming. In contrast to the C language family, in the course from Pascal to Oberon the language definition got a lot more compact, but the language itself got more powerful.
The successors of Pascal were developed to address the weaknesses of Pascal in this regard. However, with the widespread adoption of the Object Pascal extensions (e.g.. unit, bitwise operators), many of these weaknesses (most specifically the lack of support for separate compilation) were eliminated. The reputation of Pascal as a toy language has unfairly persisted in many places however.
Past uses in OS development
Pascal was used in early Apple Macs as implementation language.
Oberon as a successor of Pascal has been used extensively to develop and research Operating Systems (see Native Oberon and BlueBottle).
Commonly used tools
The most popular pascal compilers today seems to be Delphi and FreePascal Compiler, and to a smaller extent Turbo Pascal. To date no operating system has been written in Delphi. Although it may be theoretically possible, it seems to be too much work hacking the resulting executable into a working OS
FPC (FreePascal Compiler) is more suited to the job as it's highly configurable and generates code to a great number of platforms
Interfacing Pascal with Assembler
Just like when Doing a kernel in C++, Pascal compilers mangle functions name to make them convey more information (such as arguments and return types). That means if you just write
unit KernelMain; interface implementation procedure kernel_main; begin ... end; end.
You may end up with "THREADVARLIST_P$KERNEL_MAIN" rather than just "kernel_main" as a function name. If you're using FreePascal, the tool objdump can show you the symbol table of the .o file generated by the compiler, which will give you the "real" name of the function.
Alternatively, you could use compiler extra statements to enforce a "public name" to your function:
unit KernelMain; interface implementation procedure kernel_main; [public, alias: 'KERNEL_MAIN']; begin ... end; end.
Finally, simply declaring a program like you would when writing Pascal code for any normal platform will create a main routine named PASCALMAIN.
program Kernel; uses Console,Stuff,Etc; var stuff: type; begin {Your kernel here.} end.
Note, too, that C and PASCAL doesn't share the same calling convention. Most notably, arguments in PASCAL are pushed from left to right while C push them from right to left. If this gets you in trouble, you can use cdecl modifier to force the compiler considering that your PASCAL procedure works like a C function (that should mainly be useful to interface pascal code with C code). Moreover, in PASCAL, the callee function is responsible from stack cleaning, while this is typically the job of the caller in C/C++ environment.
Pascal BareBones
Credit flies to De Deyn Kim for the freepascal, public domain version of BareBones.
Tools needed to build the project:
- FPC
- NASM
- binutils(ld) built with 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 -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;
- -Rintel - sets the inline assembly syntax to intel style;
- -Tlinux - specifies that the target operating system is Linux. (Don't even ask!)
ppc32 -a -Aas -n -O3 -Op3 -Si -Sc -Sg -Xd -Tlinux -Rintel kernel.pas
Then link the whole thing with:
ld -Tlinker.script -o kernel.obj stub.o kernel.o multiboot.o system.o console.o