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

OSASK学习第3天 进入32位模式并导入C语言

1 制作真正的IPL

BIOS 在启动时,会自动将引导扇区(512字节)读取到内存的 0x7c00 地址处,然后跳转到 0x7c00 开始执行。

将软盘上的第二个扇区(512字节)加载到内存的 0x8200 地址处,为下一步执行更大的程序做准备。

MOV AX,0x0820 MOV ES, AX MOV CH,0 ; 柱面0 MOV DH,0 ; 磁头0 MOV CL,2 ; 扇区2 MOV AH,0x02 ; AH=0x02 : 读盘 MOV AL,1 ; 1个扇区 MOV BX,0 MOV DL,0x00 ; A驱动器 INT 0x13 ; 调用磁盘BIOS JC error

JC: 是“jump if carry”的缩写,意思是如果进位标志(carry flag)是1的话,就跳转。

磁盘读、写,扇区校验(verify)​,以及寻道(seek)
AH=0x02;(读盘)
AH=0x03;(写盘)
AH=0x04;(校验)
AH=0x0c;(寻道)
AL=处理对象的扇区数;​(只能同时处理连续的扇区)

CH=柱面号 &0xff;
CL=扇区号(0-5位)|(柱面号&0x300)>>2;
DH=磁头号;
DL=驱动器号;
ES:BX=缓冲地址;(校验及寻道时不使用)

返回值:
FLACS.CF == 0:没有错误,AH == 0
FLAGS.CF == 1:有错误,错误号码存入AH内(与重置(reset)功能一样)
我们这次用的是AH=0x02,是“读盘”的意思。

x86 实模式(1MB 空间)内存布局速查表

地址范围空间大小归属 / 主要用途引导程序能否写入?
0x00000-0x003FF1 KB中断向量表(IVT)
CPU 处理键盘、时钟、磁盘中断时的跳转表。
绝对禁区
覆盖会导致中断机制崩溃,系统立即死机。
0x00400-0x004FF256 BBIOS 数据区(BDA)
存放键盘状态、启动盘号、显存模式等。
绝对禁区
BIOS 依赖它维护硬件状态,乱写会导致外设异常。
0x00500-0x7BFFF~495 KB常规可用内存(用户区)
可自由加载内核、文件系统缓存等。
安全地带
常见加载地址如0x500(DOS 常驻)、0x8000等。
0x7C00-0x7DFF512 B引导扇区运行区
你当前这 512 字节代码正驻留在此。
⚠️极度危险
这就是你自己!覆盖等于“开枪自杀”,程序立即跑飞。
0x7E00-0x9FBFF~150 KB常规可用内存(用户区)
引导扇区之后的预留加载区。
最推荐地带
你的0x8200就落在这里,完美避开了栈(向下增长)和当前代码。
0x9FC00-0x9FFFF1 KBEBDA(扩展 BIOS 数据区)
通常由 BIOS 用作高级电源管理(ACPI)等。
⚠️建议回避
虽然物理可写,但可能覆盖 BIOS 运行时变量,导致关机异常。
0xA0000-0xBFFFF128 KBVGA 显存映射区
显卡帧缓冲(文本模式0xB8000,图形模式0xA0000)。
硬件冲突
写入会直接改变屏幕显示(花屏或乱码),无法存储程序代码。
0xC0000-0xFFFFF256 KBBIOS 固件 / 扩展 ROM
主板 BIOS、显卡 BIOS、硬盘控制器固件。
物理只读(或映射)
写入无效或直接触发硬件异常(#GP 保护错误)。

📌 记忆口诀

  • 底部(1KB):BIOS/CPU 内部表,打死别碰
  • 中部(640KB - 1KB)你家菜地,随便种(只要你记得种子埋哪了)。
  • 顶部(384KB):硬件厂房(显存、固件),只看不摸
  • 0x7C00(512B)你自己脚下,挪窝之前别踩空。

harib00a:

; hello-os ; TAB=4 ORG 0x7c00 ; 指明程序装载地址 ; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy code JMP entry DB 0x90 DB "HELLOIPL" ; 启动扇区名称(8字节) DW 512 ; 每个扇区(sector)大小(必须512字节) DB 1 ; 簇(cluster)大小(必须为1个扇区) DW 1 ; FAT起始位置(一般为第一个扇区) DB 2 ; FAT个数(必须为2) DW 224 ; 根目录大小(一般为224项) DW 2880 ; 该磁盘大小(必须为2880扇区1440*1024/512) DB 0xf0 ; 磁盘类型(必须为0xf0) DW 9 ; FAT的长度(必??9扇区) DW 18 ; 一个磁道(track)有几个扇区(必须为18) DW 2 ; 磁头数(必??2) DD 0 ; 不使用分区,必须是0 DD 2880 ; 重写一次磁盘大小 DB 0,0,0x29 ; 意义不明(固定) DD 0xffffffff ; (可能是)卷标号码 DB "HELLO-OS " ; 磁盘的名称(必须为11字?,不足填空格) DB "FAT12 " ; 磁盘格式名称(必??8字?,不足填空格) RESB 18 ; 先空出18字节 ; 程序主体 entry: MOV AX,0 ; 初始化寄存器 MOV SS,AX MOV SP,0x7c00 MOV DS,AX MOV ES,AX ; 读磁盘 MOV AX,0x0820 MOV ES, AX MOV CH,0 ; 柱面0 MOV DH,0 ; 磁头0 MOV CL,2 ; 扇区2 MOV AH,0x02 ; AH=0x02 : 读盘 MOV AL,1 ; 1个扇区 MOV BX,0 MOV DL,0x00 ; A驱动器 INT 0x13 ; 调用磁盘BIOS JC error JMP fin error: MOV SI,msg putloop: MOV AL,[SI] ADD SI,1 ; 给SI加1 CMP AL,0 JE fin MOV AH,0x0e ; 显示一个文字 MOV BX,15 ; 指定字符颜色 INT 0x10 ; 调用显卡BIOS JMP putloop fin: HLT ; 让CPU停止,等待指令 JMP fin ; 无限循环 msg: DB 0x0a, 0x0a ; 换行两次 DB "load error" DB 0X07 DB 0x0a ; 换行 DB 0 RESB 0x7dfe-$ ; 填写0x00直到0x001fe DB 0x55, 0xaa

2 试错

这一节就是添加了判断是否读磁盘出错的代码,防止读出错误的数据。

harib00b

; hello-os ; TAB=4 ORG 0x7c00 ; 指明程序装载地址 ; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy code JMP entry DB 0x90 DB "HELLOIPL" ; 启动扇区名称(8字节) DW 512 ; 每个扇区(sector)大小(必须512字节) DB 1 ; 簇(cluster)大小(必须为1个扇区) DW 1 ; FAT起始位置(一般为第一个扇区) DB 2 ; FAT个数(必须为2) DW 224 ; 根目录大小(一般为224项) DW 2880 ; 该磁盘大小(必须为2880扇区1440*1024/512) DB 0xf0 ; 磁盘类型(必须为0xf0) DW 9 ; FAT的长度(必??9扇区) DW 18 ; 一个磁道(track)有几个扇区(必须为18) DW 2 ; 磁头数(必??2) DD 0 ; 不使用分区,必须是0 DD 2880 ; 重写一次磁盘大小 DB 0,0,0x29 ; 意义不明(固定) DD 0xffffffff ; (可能是)卷标号码 DB "HELLO-OS " ; 磁盘的名称(必须为11字?,不足填空格) DB "FAT12 " ; 磁盘格式名称(必??8字?,不足填空格) RESB 18 ; 先空出18字节 ; 程序主体 entry: MOV AX,0 ; 初始化寄存器 MOV SS,AX MOV SP,0x7c00 MOV DS,AX MOV ES,AX ;读磁盘 MOV AX,0x0820 MOV ES, AX MOV CH,0 ; 柱面0 MOV DH, 0 ; 磁头0 MOV CL, 2 ; 扇区2 MOV SI,0 ; 记录失败次数的寄存器 retry: MOV AH,0x02 ; AH=0x02 : 读入磁盘 MOV AL,1 ; 1个扇区 MOV BX,0 MOV DL,0x00 ; A驱动器 INT 0x13 ; 调用磁盘BIOS JNC fin ; 没出错的话跳转到fin ADD SI,1 ; 往SI加1 CMP SI,5 ; 比较SI与5 JAE error ; SI >= 5时,跳转到error MOV AH,0x00 MOV DL,0x00 ; A驱动器 INT 0x13 ; 重置驱动器 JMP retry error: MOV SI,msg putloop: MOV AL,[SI] ADD SI,1 ; 给SI加1 CMP AL,0 JE fin MOV AH,0x0e ; 显示一个文字 MOV BX,15 ; 指定字符颜色 INT 0x10 ; 调用显卡BIOS JMP putloop fin: HLT ; 让CPU停止,等待指令 JMP fin ; 无限循环 msg: DB 0x0a, 0x0a ; 换行两次 DB "load error" DB 0X07 DB 0x0a ; 换行 DB 0 RESB 0x7dfe-$ ; 填写0x00直到0x001fe DB 0x55, 0xaa

JNC是另一个条件跳转指令,是“Jump if not carry”的缩写。也就是说进位标志是0的话就跳转。
JAE也是条件跳转,是“Jump if above or equal”的缩写,意思是大于或等于时跳转。

AH=0x00
DL=0x00
INT 0x13
// 这是“系统复位”​。它的功能是复位软盘状态,再读一次。

3 读到18扇区

这一节添加的代码通过一个循环连续读出了18个扇区

; hello-os ; TAB=4 ORG 0x7c00 ; 指明程序装载地址 ; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy code JMP entry DB 0x90 DB "HELLOIPL" ; 启动扇区名称(8字节) DW 512 ; 每个扇区(sector)大小(必须512字节) DB 1 ; 簇(cluster)大小(必须为1个扇区) DW 1 ; FAT起始位置(一般为第一个扇区) DB 2 ; FAT个数(必须为2) DW 224 ; 根目录大小(一般为224项) DW 2880 ; 该磁盘大小(必须为2880扇区1440*1024/512) DB 0xf0 ; 磁盘类型(必须为0xf0) DW 9 ; FAT的长度(必??9扇区) DW 18 ; 一个磁道(track)有几个扇区(必须为18) DW 2 ; 磁头数(必??2) DD 0 ; 不使用分区,必须是0 DD 2880 ; 重写一次磁盘大小 DB 0,0,0x29 ; 意义不明(固定) DD 0xffffffff ; (可能是)卷标号码 DB "HELLO-OS " ; 磁盘的名称(必须为11字?,不足填空格) DB "FAT12 " ; 磁盘格式名称(必??8字?,不足填空格) RESB 18 ; 先空出18字节 ; 程序主体 entry: MOV AX,0 ; 初始化寄存器 MOV SS,AX MOV SP,0x7c00 MOV DS,AX MOV ES,AX ;读磁盘 MOV AX,0x0820 MOV ES, AX MOV CH,0 ; 柱面0 MOV DH, 0 ; 磁头0 MOV CL, 2 ; 扇区2 readloop: MOV SI,0 ; 记录失败次数的寄存器 retry: MOV AH,0x02 ; AH=0x02 : 读入磁盘 MOV AL,1 ; 1个扇区 MOV BX,0 MOV DL,0x00 ; A驱动器 INT 0x13 ; 调用磁盘BIOS JNC next ; 没出错的话跳转到fin ADD SI,1 ; 往SI加1 CMP SI,5 ; 比较SI与5 JAE error ; SI >= 5时,跳转到error MOV AH,0x00 MOV DL,0x00 ; A驱动器 INT 0x13 ; 重置驱动器 JMP retry next: MOV AX,ES ;把内存地址后移0x0200 ADD AX,0X0020 ; MOV ES,AX ;因为没有ADD ES,0X0020指令,座椅这里稍微绕个弯 ADD CL,1 ;往CL里加1 CMP CL,18 ;比较CL和18 JBE readloop ;如果 CL <= 18 跳转至readloop读下一个扇区 fin: HLT ; 让CPU停止,等待指令 JMP fin ; 无限循环 error: MOV SI,msg putloop: MOV AL,[SI] ADD SI,1 ; 给SI加1 CMP AL,0 JE fin MOV AH,0x0e ; 显示一个文字 MOV BX,15 ; 指定字符颜色 INT 0x10 ; 调用显卡BIOS JMP putloop msg: DB 0x0a, 0x0a ; 换行两次 DB "load error" DB 0X07 DB 0x0a ; 换行 DB 0 RESB 0x7dfe-$ ; 填写0x00直到0x001fe DB 0x55, 0xaa

JBE:这也是个条件跳转指令,是“jump if below or equal”的缩写,意思是小于等于则跳转。

要读下一个扇区,只需给CL加1,给ES加上0x20就行了。CL是扇区号,ES指定读入地址。0x20是十六进制下512除以16的结果。

就结果而言,写成AL=17结果也是完全一样的。但这样的方式在下一次的程序中,就会成为问题,因此为了能够循序渐进,这里特意用循环来一个扇区一个扇区地读盘。

此时已经把磁盘上C0-H0-S2到C0-H0-S18的512×17=8704字节的内容,装载到了内存的0x8200~0xa3ff处。

4 读入10个柱面

C0-H0-S18扇区的下一扇区,是磁盘反面的C0-H1-S1。这次从0xa400读入,按顺序读到C0-H1-S18后,接着读下一个柱面C1-H0-S1。保持这个势头,一直读到C9-H1-S18。

; hello-os ; TAB=4 ORG 0x7c00 ; 指明程序装载地址 CYLS EQU 10 ; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy code JMP entry DB 0x90 DB "HELLOIPL" ; 启动扇区名称(8字节) DW 512 ; 每个扇区(sector)大小(必须512字节) DB 1 ; 簇(cluster)大小(必须为1个扇区) DW 1 ; FAT起始位置(一般为第一个扇区) DB 2 ; FAT个数(必须为2) DW 224 ; 根目录大小(一般为224项) DW 2880 ; 该磁盘大小(必须为2880扇区1440*1024/512) DB 0xf0 ; 磁盘类型(必须为0xf0) DW 9 ; FAT的长度(必??9扇区) DW 18 ; 一个磁道(track)有几个扇区(必须为18) DW 2 ; 磁头数(必??2) DD 0 ; 不使用分区,必须是0 DD 2880 ; 重写一次磁盘大小 DB 0,0,0x29 ; 意义不明(固定) DD 0xffffffff ; (可能是)卷标号码 DB "HELLO-OS " ; 磁盘的名称(必须为11字?,不足填空格) DB "FAT12 " ; 磁盘格式名称(必??8字?,不足填空格) RESB 18 ; 先空出18字节 ; 程序主体 entry: MOV AX,0 ; 初始化寄存器 MOV SS,AX MOV SP,0x7c00 MOV DS,AX MOV ES,AX ;读磁盘 MOV AX,0x0820 MOV ES, AX MOV CH,0 ; 柱面0 MOV DH,0 ; 磁头0 MOV CL,2 ; 扇区2 readloop: MOV SI,0 ; 记录失败次数的寄存器 retry: MOV AH,0x02 ; AH=0x02 : 读入磁盘 MOV AL,1 ; 1个扇区 MOV BX,0 MOV DL,0x00 ; A驱动器 INT 0x13 ; 调用磁盘BIOS JNC next ; 没出错时跳转到next ADD SI,1 ; SI加1 CMP SI,5 ; 比较SI与5 JAE error ; SI >= 5时,跳转到error MOV AH,0x00 MOV DL,0x00 ; A驱动器 INT 0x13 ; 重置驱动器 JMP retry next: MOV AX, ES ; 把内存地址后移0x200 ADD AX,0x0020 MOV ES, AX ; 因为没有ADD ES,0x020指令,所以这里稍微绕个弯 ADD CL,1 ; CL加1 CMP CL,18 ; 比较CL与18 JBE readloop ; 如果CL <= 18,则跳转至readloop MOV CL,1 ADD DH,1 CMP DH,2 JB readloop ; 如果DH < 2,则跳转到readloop MOV DH,0 ADD CH,1 CMP CH, CYLS JB readloop ; 如果CH < CYLS,则跳转到readloop fin: HLT ; 让CPU停止,等待指令 JMP fin ; 无限循环 error: MOV SI,msg putloop: MOV AL,[SI] ADD SI,1 ; 给SI加1 CMP AL,0 JE fin MOV AH,0x0e ; 显示一个文字 MOV BX,15 ; 指定字符颜色 INT 0x10 ; 调用显卡BIOS JMP putloop msg: DB 0x0a, 0x0a ; 换行两次 DB "load error" DB 0X07 DB 0x0a ; 换行 DB 0 RESB 0x7dfe-$ ; 填写0x00直到0x001fe DB 0x55, 0xaa

JB。这也是条件跳转指令,是“jump if below”的缩写。翻译过来就是:​“如果小于的话,就跳转。​”
EQU。这相当于C语言的#define命令,用来声明常数。​“CYLS EQU 10”意思是“CYLS = 10”​。EQU是“equal”的缩写。只将它定义成常数是因为以后我们可能修改这个数字。现在我们先随意定义成10个柱面,以后再对它进行调整(CYLS代表cylinders)​。

5 着手开发操作系统

一般向一个空软盘保存文件时 :
(1) 文件名会写在0x002600以后的地方;
(2) 文件的内容会写在0x004200以后的地方。

6 从启动区执行操作系统

那么,要怎样才能执行磁盘映像上位于0x004200号地址的程序呢?
现在的程序是从启动区开始,把磁盘上的内容装载到内存0x8000号地址,所以磁盘0x4200处的内容就应该位于内存0x8000+0x4200=0xc200号地址。

这样的话,我们就往haribote.nas里加上ORG 0xc200,然后在ipl.nas处理的最后加上JMP 0xc200这个指令。

7 确认操作系统的执行情况

这节就是切换一下画面模式。

设置显卡模式(video mode)
❏ AH=0x00;
❏ AL=模式:​(省略了一些不重要的画面模式)

  • 0x03:16色字符模式,80× 25
  • 0x12:VGA图形模式,640× 480× 4位彩色模式,独特的4面存储模式
  • 0x13:VGA图形模式,320× 200× 8位彩色模式,调色板模式
  • 0x6a:扩展VGA图形模式,800× 600× 4位彩色模式,独特的4面存储模式(有的显卡不支持这个模式)

❏ 返回值:无

8 32位模式前期准备

; haribote-os ; TAB=4 ; 有关BOOT_INFO CYLS EQU 0x0ff0 ; 设定启动区 LEDS EQU 0x0ff1 VMODE EQU 0x0ff2 ; 关于颜色数目的信息。颜色的位数。 SCRNX EQU 0x0ff4 ; 分辨率的X(screen x) SCRNY EQU 0x0ff6 ; 分辨率的Y(screen y) VRAM EQU 0x0ff8 ; 图像缓冲区的开始地址 ORG 0xc200 ; 这个程序将要被装载到内存的什么地方呢? MOV AL,0x13 ; VGA显卡,320x200x8位彩色 MOV AH,0x00 INT 0x10 MOV BYTE [VMODE],8 ; 记录画面模式 MOV WORD [SCRNX],320 MOV WORD [SCRNY],200 MOV DWORD [VRAM],0x000a0000 ;用BIOS取得键盘上各种LED指示灯的状态 MOV AH,0x02 INT 0x16 ; keyboard BIOS MOV [LEDS], AL fin: HLT JMP fin

9 - 10 开始导入C语言

这部分突然添加了很多莫名其妙的代码,有的没看懂,还有makefile里面的一些流程,也没搞清楚,几个obj怎么链接的小白看不太明白,总之跟着代码跑就完了

http://www.cnnetsun.cn/news/4057841.html

相关文章:

  • WSL2与Docker在Windows开发环境中的集成与实践指南
  • 异音检测系统产线部署全流程:从方案设计到验收的六个阶段
  • 学工管理系统-高校学工信息管理系统 - 学工管理系统信息修改
  • Windows Server防火墙IP拦截实战:从原理到四种配置方法详解
  • Gitee开源项目创建与托管全流程指南:从零到协作
  • 华为OD机试真题 新系统 2026-08-05 C++ 实现【IPv4等长子网划分与自动分配系统】
  • Draw.io 高阶技巧:从绘图工具到架构设计与团队协作的生产力引擎
  • LoRA+ControlNet+IP-Adapter:AI绘画精准控制实战工作流详解
  • AIGC+PlantUML:用自然语言生成技术图表,重构高效文档工作流
  • SynWeaver:基于网站与轨迹协同学习的网页智能体泛化新范式
  • 基于 PlantUML 的软件系统行为建模:图表选型、描述规范与乙方交付要求
  • 从“烫手山芋”到“香饽饽”:流拍资产盘活方法论
  • 本地生活系统架构拆解:统一后台、订单索引与私有化交付
  • 80-版本列表分页与历史治理:为什么版本越多越要重视列表管理
  • 慈溪婚嫁习俗浅谈:新式婚嫁礼饰走红,金包银为什么更适合年轻人
  • 我回测了A 股10 年的”追涨停”策略,结果可能和你想的不一样
  • NVIDIA-SMI通信失败:3分钟定位驱动加载与内核兼容性问题
  • OpenClaw爬虫框架配置全景指南:从核心原理到实战调优
  • 从URL全角空格报错看开源项目错误处理与社区协作
  • 云服务器部署Web服务公网访问全攻略:安全组、防火墙与绑定配置
  • 基于Redis实现直播间在线人数、点赞、实时热度统计
  • 嵌入式系统C语言资源分类与内存分布分析
  • 第33篇 STL之stack与queue:BFS/DFS的标配数据结构,面试手写不过分吧
  • FastApi进阶
  • 一文速通GPU版FFmpeg视频转码的安装使用
  • 传统BIOS引导黑苹果实战:老硬件安装macOS Monterey完整指南
  • 基于大模型的PLC编程自动化:从自然语言到工业控制代码的智能生成
  • 无线传输模块概述
  • Python 快速上手(Java 开发者版)
  • 碰到文档色差别发愁,AI 导出鸭帮你搞定 DeepSeek 导出 pdf 颜色不一样怎么办的实操解决技巧