Pascal: Difference between revisions
[unchecked revision] | [unchecked revision] |
m (Bot: Replace deprecated source tag with syntaxhighlight) |
|||
(18 intermediate revisions by 10 users not shown) | |||
Line 1: | Line 1: | ||
== Historical note == |
|||
Is it because it's taught in many schools ? Several people seems to prefer using Pascal to C for their hobby OS, and, even if it asks a bit more setup than C, it seems it can be done ... |
|||
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 [http://www.oberon.ethz.ch/ Native Oberon] and [http://bluebottle.ethz.ch BlueBottle]). |
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]). |
||
== Commonly used tools == |
|||
Pascal had been used in early Apple Macs as implementation language. |
|||
The most popular pascal compilers today seems to be Delphi and FreePascal Compiler, and to a smaller extent Turbo Pascal. |
|||
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) seems more suited to develop a portable operating system 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 informations (such as arguments and return types). That means if you just write |
|||
== Interfacing Pascal with Assembler == |
|||
<pre> |
|||
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"> |
|||
unit KernelMain; |
unit KernelMain; |
||
Line 23: | Line 34: | ||
end. |
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. |
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 29: | Line 40: | ||
Alternatively, you could use compiler extra statements to enforce a "public name" to your function: |
Alternatively, you could use compiler extra statements to enforce a "public name" to your function: |
||
<syntaxhighlight lang="pascal"> |
|||
<pre> |
|||
unit KernelMain; |
unit KernelMain; |
||
Line 42: | Line 53: | ||
end. |
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. |
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; |
program Kernel; |
||
Line 57: | Line 68: | ||
{Your kernel here.} |
{Your kernel here.} |
||
end. |
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. |
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. |
||
== |
== See Also == |
||
credit flies to De Deyn Kim for the freepascal, public domain version of BareBones. |
|||
=== 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; |
|||
{The two variables below *can* be declared as a single qword variable, if your compiler supports qwords.} |
|||
mem_lower: DWORD; |
|||
mem_upper: DWORD; |
|||
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; |
|||
{Again, you can declare these as a qword.} |
|||
base_addr_low: DWORD; |
|||
base_addr_high: DWORD; |
|||
{And once again, these can be made into one qword variable.} |
|||
length_low: DWORD; |
|||
length_high: DWORD; |
|||
mtype: DWORD; |
|||
end; |
|||
implementation |
|||
end. |
|||
</pre> |
|||
===linker script=== |
|||
* [[Pascal Bare Bones]] |
|||
<pre> |
|||
* [[BOOTBOOT]] loader has an example 64 bit higher half kernel in Pascal |
|||
OUTPUT_FORMAT("elf32-i386") |
|||
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> |
|||
[[Category:Languages]] |
[[Category:Languages]] |
Latest revision as of 04:40, 9 June 2024
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.
There is a commercial (proprietary license, apparently) operating system being developed in Delphi, ClassiOS, a Windows clone, (formerly 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 produce Windows device drivers too.
Anyway, FPC (FreePascal Compiler) seems more suited to develop a portable operating system 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.
See Also
- Pascal Bare Bones
- BOOTBOOT loader has an example 64 bit higher half kernel in Pascal