Pascal: Difference between revisions

9,184 bytes removed ,  26 days ago
m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
No edit summary
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(12 intermediate revisions by 8 users not shown)
Line 5:
 
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 [http://www.oberon.ethz.ch/ Native Oberon] and [http://bluebottle.ethz.ch BlueBottle]).
 
Pascal had been used in early Apple Macs as implementation language.
 
== 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
 
There is a commercial (proprietary license, apparently) operating system being developed in Delphi, [http://www.tattsoft.com/index.php/products/9-operating-systems/10-classios-an-object-pascal-operating-system.html ClassiOS], a Windows clone, (formerly [http://www.tattsoft.com/index.php/products/petrosr.html PetrOS®], developed with an internal Pascal compiler) by Peter Tattam (best known for his TCP/IP stack for DOS, the Trumpet line of products). He customized Delphi to [http://web.archive.org/web/20110219051243/http://petertattam.com/?p=19 produce Windows device drivers] too.
 
Anyway, FPC (FreePascal Compiler) isseems more suited to thedevelop a portable operating jobsystem 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
 
<syntaxhighlight lang="pascal">
<pre>
unit KernelMain;
 
Line 31 ⟶ 34:
 
end.
</syntaxhighlight>
</pre>
 
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 <tt>objdump</tt> can show you the symbol table of the .o file generated by the compiler, which will give you the "real" name of the function.
Line 37 ⟶ 40:
Alternatively, you could use compiler extra statements to enforce a "public name" to your function:
 
<syntaxhighlight lang="pascal">
<pre>
unit KernelMain;
 
Line 50 ⟶ 53:
 
end.
</syntaxhighlight>
</pre>
 
Finally, simply declaring a program like you would when writing Pascal code for any normal platform will create a main routine named PASCALMAIN.
 
<syntaxhighlight lang="pascal">
<pre>
program Kernel;
 
Line 65 ⟶ 68:
{Your kernel here.}
end.
</syntaxhighlight>
</pre>
 
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#Stack example on the X86 architecture|stack cleaning]], while this is typically the job of the caller in C/C++ environment.
 
== PascalSee BareBonesAlso ==
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 ===
Assemble with nasm -f elf stub.asm -o stub.o
 
<pre>
;/////////////////////////////////////////////////////////
;// //
;// 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
</pre>
===kernel.pas===
 
compile with ppc32 -a -Aas -n -O3 -Op3 -Si -Sc -Sg -Xd -Tlinux -Rintel kernel.pas
 
or
 
fpc -Aelf -n -O3 -Op3 -Si -Sc -Sg -Xd -Rintel -Tlinux kernel.pas
 
<pre>
{
/////////////////////////////////////////////////////////
// //
// 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.
</pre>
 
=== console.pas ===
 
<pre>
{
/////////////////////////////////////////////////////////
// //
// 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.
</pre>
 
 
===multiboot.pas===
<pre>
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.
</pre>
 
=== system.pas ===
<pre>
unit system;
 
interface
 
type
cardinal = 0..$FFFFFFFF;
hresult = cardinal;
dword = cardinal;
integer = longint;
pchar = ^char;
 
implementation
 
end.
</pre>
 
=== Linker script ===
linker.script
<pre>
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 = .;
}
</pre>
 
* [[Pascal Bare Bones]]
=== Linking ===
* [[BOOTBOOT]] loader has an example 64 bit higher half kernel in Pascal
Link the whole thing with:
ld -Tlinker.script -o kernel.obj stub.o kernel.o multiboot.o system.o console.o
 
[[Category:Languages]]