news 2026/8/1 3:40:37

基于 Free Pascal 从零编写裸机操作系统(一)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于 Free Pascal 从零编写裸机操作系统(一)

前言

绝大多数入门操作系统教程都会选用 C 语言,很少有人尝试用 Pascal 开发裸内核。OSDev 官网提供了一套完整的 Free Pascal Bare Bones 最小操作系统示例,无需依赖 DOS、Linux、Windows 标准库,完全运行在 32 位保护模式下,兼容 Multiboot 引导规范,能直接被 GRUB、QEMU 加载。

本文仅拆解全部源码(汇编入口、控制台驱动、内核主程序、Multiboot 规范定义),编译、链接、镜像制作、QEMU 运行脚本不在本文介绍范围。 参考原文:https://wiki.osdev.org/Pascal_Bare_Bones

前置说明

  1. 编译器:Free Pascal(FPC),嵌入式裸机编译目标
  2. 架构:x86 32 位保护模式
  3. 显示:VGA 文本缓冲区0xB8000
  4. 引导:Multiboot v1 标准,兼容 GRUB
  5. 无标准库:不能使用WriteLnMemAlloc等系统相关函数,所有 IO、内存读写手动实现

一、汇编入口文件:stub.asm

作用

  1. 声明 Multiboot 头部,让 GRUB 识别这是合法内核镜像
  2. 切换 32 位保护模式环境,设置栈
  3. 导出符号供 Pascal 调用,跳转至 Pascal 内核主函数MainKernel

nasm

;///////////////////////////////////////////////////////// ;// // ;// 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

源码关键点解读

  1. Multiboot 头三行 magic/flags/checksum 是 GRUB 识别内核的核心,位置不能偏移;
  2. .bss段预留 16KB 静态栈,裸机无动态内存分配,栈必须静态声明;
  3. GRUB 会把内存、磁盘等硬件信息结构体地址存入ebx,入栈传递给 Pascal 内核;
  4. 内核执行完毕后死循环停机,防止指令越界。

二、VGA 控制台驱动单元:console.pas

作用

封装底层 VGA 文本缓冲区操作,实现清屏、打印字符、字符串、数字、十六进制,是内核唯一输出接口,对外导出可被汇编 / 内核主程序调用的过程。

pascal

{ ///////////////////////////////////////////////////////// // // // 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(str); end; end.

源码关键点解读

  1. VGA_BUFFER_ADDR = $B8000是 x86 实模式 / 保护模式通用文本显存地址,无需 BIOS 中断;
  2. 每个字符占用 2 字节:低字节 ASCII,高字节颜色属性;
  3. 内置自动滚动逻辑:打印超出 25 行时整体上移一行,清空底部;
  4. PChar兼容 C 字符串(0 终止符),方便和汇编交互;
  5. 数字打印不依赖标准库SysUtils,手动实现进制转换缓冲区。

三、Multiboot 规范常量定义:multiboot.pas

作用

定义 Multiboot v1 标准的内存信息结构体、魔数、标志位,内核主程序读取 GRUB 传入的硬件信息时使用。

pascal

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.

源码关键点解读

  1. packed record禁用内存对齐,严格匹配 Multiboot 二进制布局;
  2. mem_lower/mem_upper是本示例核心,用于计算整机物理内存大小;
  3. 结构体指针由汇编kernel_entry.asmebx传入内核主程序。

四、内核主程序:kernel.pas

作用

程序入口MainKernel,整合控制台、Multiboot 模块,完成初始化、硬件信息打印、停机逻辑,是整个操作系统顶层业务代码。

pascal

{ ///////////////////////////////////////////////////////// // // // 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.

源码关键点解读

  1. MainKernel[public]导出,汇编call MainKernel可直接调用;
  2. 入参PMultibootInfo接收 GRUB 传入的硬件信息指针;
  3. 先校验 Multiboot 魔数,防止非 GRUB 引导导致内存读取崩溃;
  4. 纯汇编内嵌cli+hlt关闭中断停机,裸机无操作系统异常处理;
  5. 末尾默认begin..end是 FPC 标准程序占位,裸机环境不会触发执行。

五、整体源码结构梳理

plaintext

Pascal-BareBones/ ├─ kernel_entry.asm 汇编引导入口+Multiboot头 ├─ multiboot.pas Multiboot硬件信息结构体定义 ├─ console.pas VGA控制台底层驱动单元 └─ kernel.pas 内核主程序,系统业务逻辑

六、源码运行逻辑完整流程

  1. GRUB 加载镜像,识别.multiboot头部,把硬件信息存入 EBX;
  2. 执行kernel_entry.asm,初始化栈,压入 EBX 指针,调用MainKernel
  3. Pascal 内核执行:清屏 → 校验 Multiboot 合法性 → 打印内存硬件信息 → 死循环停机;
  4. 所有屏幕输出均调用console.pas封装的 VGA 显存操作,无任何 BIOS/DOS 系统调用。

七、拓展改造方向(基于现有源码)

  1. 完善 VGA 颜色属性,实现彩色文字输出;
  2. 增加键盘中断 IRQ1 驱动,实现字符输入;
  3. 新增简单堆内存管理器,替代静态变量;
  4. 扩展 Multiboot 模块,读取磁盘、模块加载信息;
  5. 增加简单多任务调度框架,基于 Pascal 过程实现线程切换。

结尾

这套源码完整实现了最小可用 Pascal 裸内核,全程不依赖任何操作系统标准库,所有硬件交互代码自主可控。相比 C 语言 OS 教程,Pascal 强类型、单元模块化的特性让底层硬件结构体、驱动代码可读性更高。下一篇博文我会讲解编译脚本、链接脚本、GRUB 镜像制作与 QEMU 运行完整流程。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/1 3:40:25

CST同轴线仿真全流程:从建模优化到高频连接器设计实践

1. 从一根“线”说起&#xff1a;为什么同轴线仿真值得深究&#xff1f;在射频微波领域&#xff0c;同轴线&#xff08;Coaxial Cable&#xff09;大概是工程师们最熟悉也最“陌生”的元件之一。说熟悉&#xff0c;是因为它无处不在&#xff0c;从实验室的测试线缆到基站的天馈…

作者头像 李华
网站建设 2026/8/1 3:38:36

大模型思考过程加密:技术原理、行业影响与工程应对策略

1. 项目概述&#xff1a;当AI的“思考”成为黑箱最近&#xff0c;关于Claude“思考过程加密”的讨论在技术圈里炸开了锅。起因是一位教授在深入研究Anthropic的Claude模型时&#xff0c;提出了一个惊人的观点&#xff1a;我们看到的模型输出&#xff0c;可能只是其内部复杂“思…

作者头像 李华
网站建设 2026/8/1 3:38:31

PCB板HDI1/HDI2/HDI3/HDI…、ELIC指的是什么?

如果你从做普通的四层、六层PCB通孔板产品(如传统TV)转到做手机类产品,那么一定会接触到一个新概念——HDI高阶板,那什么是HDI板呢? HDI板,High Density Interconnect的缩写 ,高密度互连板,依靠激光微孔(Microvia)、积层压合实现细间距VFBGA扇出,阶数(HDI1/HDI2/HDI…

作者头像 李华
网站建设 2026/8/1 3:37:14

微信聊天记录AI分析:原理、应用与隐私安全实践指南

1. 项目概述&#xff1a;当你的聊天记录成为AI的“养料”最近&#xff0c;一个功能悄悄地在微信里上线了&#xff0c;它让不少深度用户和技术爱好者眼前一亮&#xff1a;微信聊天记录喂AI。这可不是什么第三方插件或者需要复杂操作的“黑科技”&#xff0c;而是腾讯官方推出的、…

作者头像 李华
网站建设 2026/8/1 3:30:48

主流 Agent 架构分析

主流 Agent 架构分析指南 当 ChatGPT 还停留在对话阶段时&#xff0c;一批"能干活的 AI"已经悄然崛起——它们能读写文件、执行命令、操作浏览器&#xff0c;甚至自我进化。本文将深入剖析五大主流 Agent 框架的架构设计&#xff0c;带你从源码层面理解 AI Agent 是如…

作者头像 李华
网站建设 2026/8/1 3:25:50

步进电机步距角与细分驱动详解:从原理到实战,告别抖动与丢步

1. 项目概述&#xff1a;从“会转”到“转得准”的步进电机之旅如果你玩过Arduino或者树莓派&#xff0c;大概率接触过那种小小的28BYJ-48步进电机&#xff0c;用它做过个时钟或者小云台。但当你真正想用它干点“正经活”&#xff0c;比如做个3D打印机、CNC雕刻机&#xff0c;或…

作者头像 李华