当前位置: 首页 > news >正文

在写setup时遇到的问题与思考

在写setup时遇到的问题与思考

首先遇到这个问题需要搞明白什么是setup。

在编写操作系统的过程中,我们首先要写汇编程序,然后将我们的代码编译为二进制之后写入软盘(或者硬盘),然后由BIOS将软盘(或者硬盘)中的内容加载到内存当中(0x7c00处,为什么是这里看另一篇文章)并执行,这样我们的操作系统内核就运行起来了。

但是这样我们面临一个问题。我们是将编译后的二进制文件(boot.o)写入的位置是0磁盘0磁道1扇区的位置,而且要求最后两个字节是0x55aa。(这是约定俗成的,不然谁知道上哪里去找你的代码呢?而且有这样的特征才能够被识别为一个操作系统)这样就有一个限制,因为一个扇区大小只有512字节,最后两个字节还必须要求是0x55aa,那么我们就只有510个字节的大小可以用了,这显然是不够我们来写一个操作系统内核的。这种情况改怎么办呢?

答案是我们写一个setup,按照与boot相同的方式写到内存当中就行了,当然不是一个随便的位置,而是参考实模式下1MB的内存布局表

image-20250412104004378

发现有两个空闲的可用区域,这就是我们可以放的地方,只要将setup加载到这两个位置即可。

BIOS完成任务之后会将CPU控制权移交给boot,所以只要在boot中写一个跳转到setup的逻辑,就能够摆脱512字节的限制了。

以下是我的实现代码(setup只是输出了一个"Hello World")

; boot.asm
[org 0x7c00]          ; 指定程序加载地址为 0x7C00(BIOS 加载引导扇区的地址)
[bits 16]             ; 16 位实模式section .text         ; 定义代码段
global _start         ; 全局入口标签_start:; 保存启动驱动器号mov [boot_drive], dl    ; 将 dl(启动驱动器号)保存到内存变量 boot_drive; 初始化段寄存器xor ax, ax          ; 将 ax 清零(ax = 0)mov ds, ax          ; 设置数据段寄存器 ds = 0mov es, ax          ; 设置附加段寄存器 es = 0mov ss, ax          ; 设置栈段寄存器 ss = 0mov sp, 0x7c00      ; 设置栈指针 sp = 0x7C00; 打印 "Booting..."mov si, boot_msg    ; 将 boot_msg 的地址加载到 si 寄存器call print_string   ; 调用 print_string 函数; 加载 setup 到内存 0x90000mov ah, 0x02        ; BIOS 中断功能号 ah = 0x02(读扇区)mov al, 1           ; al = 1(读取 1 个扇区)mov ch, 0           ; ch = 0(柱面号 0)mov cl, 2           ; cl = 2(扇区号 2)mov dh, 0           ; dh = 0(磁头号 0)mov dl, [boot_drive] ; dl = 启动驱动器号(从内存加载)mov bx, 0x9000      ; bx = 0x9000(目标段地址)mov es, bx          ; es = bx(设置目标段地址)xor bx, bx          ; bx = 0(偏移量清零)int 0x13            ; 调用 BIOS 中断 0x13(读磁盘)jc disk_error       ; 如果 CF 标志置位(错误),跳转到 disk_error; 跳转到 setupjmp 0x9000:0x0000   ; 远跳转到 0x9000:0x0000print_string:mov ah, 0x0e        ; ah = 0x0e(BIOS 打印字符功能)
.loop:lodsb               ; 从 ds:si 加载字节到 al,并递增 sicmp al, 0           ; 比较 al 和 0(检查字符串结束)je .done            ; 如果 al = 0,跳转到 .doneint 0x10            ; 调用 BIOS 中断 0x10(打印字符)jmp .loop           ; 跳转回 .loop 继续处理
.done:ret                 ; 返回disk_error:mov si, error_msg   ; 将 error_msg 地址加载到 sicall print_string   ; 调用 print_stringjmp $               ; 死循环boot_msg db 'Booting...', 0    ; 定义字符串 "Booting...",以 0 结尾
error_msg db 'Disk read error!', 0 ; 定义错误字符串
boot_drive db 0                ; 定义变量存储启动驱动器号times 510-($-$$) db 0          ; 填充到 510 字节
dw 0xaa55                      ; 引导扇区签名
; setup.asm
[org 0x0000]          ; 假设加载到 0x90000,偏移从 0 开始
[bits 16]             ; 16 位实模式section .text
global _start_start:; 初始化段寄存器mov ax, 0x9000      ; ax = 0x9000(段地址)mov ds, ax          ; ds = 0x9000mov es, ax          ; es = 0x9000mov ss, ax          ; ss = 0x9000mov sp, 0x1000      ; sp = 0x1000(栈指针); 打印 "Hello, World! from setup"mov si, hello_msg   ; si = hello_msg 地址call print_string   ; 调用 print_stringjmp $               ; 死循环print_string:mov ah, 0x0e        ; ah = 0x0e(BIOS 打印字符功能)
.loop:lodsb               ; 从 ds:si 加载字节到 al,并递增 sicmp al, 0           ; 比较 al 和 0je .done            ; 如果 al = 0,跳转到 .doneint 0x10            ; 调用 BIOS 中断 0x10jmp .loop           ; 继续循环
.done:ret                 ; 返回hello_msg db 'Hello, World! from setup', 0 ; 定义字符串

其实最重要的就是知道各种寄存器,然后语句就容易理解了(问GPT吧,这个还是自己哪里不会问哪里)

最后是我遇到的问题:

一开始我是将

boot_msg db 'Booting...', 0    ; 定义字符串 "Booting...",以 0 结尾
error_msg db 'Disk read error!', 0 ; 定义错误字符串
boot_drive db 0                ; 定义变量存储启动驱动器号

三行前面加了一个[section .data]数据段声明,希望将它们放到数据段当中,但是报错了。为什么会出现这样的问题呢?

因为分段是通过section或者segment语句将代码和数据分组,在现代的程序当中,比如说ELF格式,链接器会将这些段映射到内存的不同区域上。但是在引导扇区(boot)当中,使用的是NASM的-f bin格式编译成的纯二进制文件,而不是ELF等格式,-f bin是不支持多段链接的,所有的代码都必须连续存储的。实模式下的地址计算都是相当于0x7c00的偏移计算的(因为这个程序是从0x700开始的),如果进行了段分离之后,会导致mov si,boot_msg这些语句找不到正确的位置,从而导致程序出错。

相关文章:

  • 【2025软考高级架构师】——知识脑图总结
  • 管理配置信息和敏感信息
  • 【2025最新】Baichuan-M1-instruct部署教程
  • CPU缓存
  • 湖北理元理律师事务所:债务优化的合规化探索
  • 【大模型架构-Transformer、Mamba、Hyena】
  • 【day02】牛牛的快递 | 最小花费爬楼梯 | 数组中两个字符串的最小距离
  • UNet 改进(22):结合Transformer结构
  • 【RocketMQ Broker 相关源码】- broker 启动源码(1)
  • 「Mac畅玩AIGC与多模态17」开发篇13 - 条件判断与分支跳转工作流示例
  • 看图建模实战训练案例(上)
  • 无人机视觉:连接像素与现实世界 —— 像素与GPS坐标双向转换指南
  • K230的ISP(图像信号处理器)通常支持多通道输出,常见配置为3个独立通道
  • 文氏管-文丘里-旋风除尘组合装置JGQ531高效湿式除尘器实验装置平台
  • 51单片机入门教程——每个音符对应的重装载值
  • Winform(10.常用控件3)
  • 线程池的线程数配置策略
  • 12.Excel:查找替换
  • xx外卖知识补充
  • PostgreSQL 的 pg_stat_file 函数
  • 贵州黔西市游船倾覆事故最后一名失联人员被找到,但已无生命体征
  • 特朗普宣布对进口电影征收100%关税
  • 体坛联播|拜仁遭绝平未能提前夺冠,刘翔钻石联赛纪录作古
  • 解放日报头版:上海张江模力社区托举“年轻的事业”
  • 澎湃读报丨央媒头版五四青年节集中刊文:以青春之我,赴时代之约
  • 哈马斯:愿与以色列达成为期5年的停火协议