从 01 开始 从 01 开始
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档

晓林

程序猿,自由职业者,博主,英语爱好者,健身达人
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档
  • 计算机简史

  • 数字电路

  • 计算机组成原理

  • 操作系统

    • 我的操作系统学习笔记

    • 我的操作系统实验笔记

      • 搭建实验环境
      • 操作系统的引导
        • 改写 bootsect.s
        • 2. 改写 setup.s
        • 扩展
        • 附录
      • 系统调用
    • 操作系统网课-王道考研

  • Linux

  • 计算机网络

  • 数据库

  • 编程工具

  • 装机

  • 计算机基础
  • 操作系统
  • 我的操作系统实验笔记
2022-10-29
目录

操作系统的引导

# 1. 操作系统的引导

此次实验的基本内容是:

  1. 阅读《Linux 内核完全注释》的第 6 章,对计算机和 Linux 0.11 的引导过程进行初步的了解;
  2. 按照下面的要求改写 0.11 的引导程序 bootsect.s
  3. 有兴趣同学可以做做进入保护模式前的设置程序 setup.s。

‍ ‍ ​

在实验报告中回答如下问题:

  1. 有时,继承传统意味着别手蹩脚。x86 计算机为了向下兼容,导致启动过程比较复杂。请找出 x86 计算机启动过程中,被硬件强制,软件必须遵守的两个“多此一举”的步骤(多找几个也无妨),说说它们为什么多此一举,并设计更简洁的替代方案。 评分标准
  2. bootsect 显示正确,30%
  3. bootsect 正确读入 setup,10%
  4. setup 获取硬件参数正确,20%
  5. setup 正确显示硬件参数,20%
  6. 实验报告,20%

# 改写 bootsect.s

bootsect.s 能在屏幕上打印一段提示信息“XXX is booting...”,其中 XXX 是你给自己的操作系统起的名字,例如 LZJos、Sunix 等(可以上论坛上秀秀谁的 OS 名字最帅,也可以显示一个特色 logo,以表示自己操作系统的与众不同。) ‍

# 简单版 v1.0

简单的话,我们直接改版原本的 bootsect.s 的字符串和字符串个数即可,步骤如下:

cd ~/oslab/linux-0.11/boot
vim bootsect.s
1
2

‍ 修改 msg1 的内容,同时修改 cx 的值

修改246的字符串为 "POS is runing"
第98行 mov cx,#23
1
2

然后保存并退出 vim ‍ 重新编译

as86 -0 -a -o bootsect.o bootsect.s
ld86 -0 -s -o bootsect bootsect.o
dd bs=1 if=bootsect of=Image skip=32
1
2
3

我们可以写个 shell 脚本,这样以后每次编译的时候,就不用写上述命令了,我这里新建一个 lab1shell.sh,里面放上面 3 条命令

生成内核

cd ~/oslab/linux-0.11
make all
1
2

‍ 可以看到 Image 的时间已经是被更新为你编译的时候的时间

$ ll
-rw-rw-r--  1 peterjxl peterjxl 128161 10月 28 07:54 Image
1
2

‍ 运行 Bochs

cd ~/oslab
./run
1
2

‍ 实验截图

​​ ‍

# 不太简单版 v2.0

接下来我们重写 bootsect.s ,只完成关键的功能即可

首先完成屏幕输出功能:屏幕输出的功能主要是用 10 号中断

! 首先读入光标位置
mov ah, #0x03 
xor bh, bh
int 0x10 
1
2
3
4

输入参数:bh 是页号,这里置 0

返回参数:CH=光标起始位置,CL=光标结束位置,DH=光标行号(0-based),DL=光标列号(0-based)

然后定义字符串:

msg1:	.byte 13, 10
	.ascii "Hello OS World, my name is JXL"
	.byte 13, 10, 13, 10

.org 510

boot_flag: .word 0xAA55
1
2
3
4
5
6
7

.byte 13,10 就是定义个回车和换行,第 4 行就是两个回车和换行

.org 510 伪指令,表示在它之后的指令从地址 510 开始存放。遇到.org,编译器会把其后的指令代码放到 org 伪指令指定的偏移地址。如 org 指定的地址和之前的指令地址有空洞,则用 0 填充。

boot_flag: .word 0xAA55:是启动盘具有有效引导扇区的标志。仅供 BI0S 中的程序加载引导扇区时识别使用。它必须位于引导扇区的最后两个字节中。

然后是显示字符串

! 显示字符串
mov cx, #36
mov bx, #0x0007
mov ax, #0x7c00
mov es, ax
mov bp, #msg1
mov ax, #0x1301
int 0x10
1
2
3
4
5
6
7
8

调用参数:cx 是字符串长度,BH=页号,BL=显示属性,,es:bp 是字符串的地址,ax 是功能号 显示字符串光标跟随移动

显示完字符串后,我们不再往下执行(例如载入 setup.s 和把自己挪到 0x9000 处),我们这里可以写个简单的死循环

inf_loop:
	jmp inf_loop
1
2

完整代码如下:

entry _start
_start:
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#36
    mov bx,#0x0007
    mov bp,#msg1
    mov ax,#0x07c0
    mov es,ax
    mov ax,#0x1301
    int 0x10
inf_loop:
    jmp inf_loop

msg1: 	.byte   13,10
    	.ascii  "Hello OS world, my name is LZJ"
    	.byte   13,10,13,10

.org 510

boot_flag: .word  0xAA55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

我们重新编译和运行,实验结果如下

​​

运行后,就不会继续往下有输出了,因为是死循环。

# 2. 改写 setup.s

改写 setup.s 主要完成如下功能:

  1. bootsect.s 能完成 setup.s 的载入,并跳转到 setup.s 开始地址执行。而 setup.s 向屏幕输出一行"Now we are in SETUP"。
  2. setup.s 能获取至少一个基本的硬件参数(如内存参数、显卡参数、硬盘参数等),将其存放在内存的特定地址,并输出到屏幕上。
  3. setup.s 不再加载 Linux 内核,保持上述信息显示在屏幕上即可。

# 加载 setup.s 版 v1.0

同样是显示字符串,因此可以直接复用 bootsect.s 的 2.0 版本,改改显示的字符串即可。我们可以直接删掉 setup.s,然后 cp 一份 bootsect.s

$ cat bootsect.s 
$ cp bootsect.s setup.s
1
2

然后有 4 个地方要修改:

​​

完整代码:

entry _start
_start:
    mov ah,#0x03
    xor bh,bh
    int 0x10

    mov cx,#25
    mov bx,#0x0007
    mov bp,#msg2
    mov ax,cs
    mov es,ax
    mov ax,#0x1301
    int 0x10
inf_loop:
    jmp inf_loop

msg2: 	.byte   13,10
    	.ascii  "Now we are in SETUP"
    	.byte   13,10,13,10

.org 510

boot_flag: .word  0xAA55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

修改 bootsect.s 加载 setup 模块 v3.0,我们可以参考原版的 bootsect.s 的模块,我们只需要将其 copy 到我们自己的 bootsect.s 即可……

load_setup:
! 设置驱动器和磁头(drive 0, head 0): 软盘 0 磁头
    mov dx,#0x0000
! 设置扇区号和磁道(sector 2, track 0): 0 磁头、0 磁道、2 扇区
    mov cx,#0x0002
! 设置读入的内存地址:BOOTSEG+address = 512,偏移512字节
    mov bx,#0x0200
! 设置读入的扇区个数(service 2, nr of sectors),
! SETUPLEN是读入的扇区个数,Linux 0.11 设置的是 4,
! 我们不需要那么多,我们设置为 2(因此还需要添加变量 SETUPLEN=2)
    mov ax,#0x0200+SETUPLEN
! 应用 0x13 号 BIOS 中断读入 2 个 setup.s扇区
    int 0x13
! 读入成功,跳转到 ok_load_setup: ok - continue
    jnc ok_load_setup
! 软驱、软盘有问题才会执行到这里。我们的镜像文件比它们可靠多了
    mov dx,#0x0000
! 否则复位软驱 reset the diskette
    mov ax,#0x0000
    int 0x13
! 重新循环,再次尝试读取
    jmp load_setup
ok_load_setup:
! 接下来要干什么?当然是跳到 setup 执行。
! 要注意:我们没有将 bootsect 移到 0x9000,因此跳转后的段地址应该是 0x7ce0
! 即我们要设置 SETUPSEG=0x07e0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

‍ 完整的 bootsect.s 代码如下:

SETUPLEN=2
SETUPSEG=0x07e0
entry _start
_start:
    mov ah,#0x03
    xor bh,bh
    int 0x10

    mov cx,#36
    mov bx,#0x0007
    mov bp,#msg1
    mov ax,#0x07c0
    mov es,ax
    mov ax,#0x1301
    int 0x10
load_setup:
    mov dx, #0x0000 ; 设置驱动器和磁头(drive 0, head 0): 软盘 0 磁头
    mov cx, #0x0002 ; 设置扇区号和磁道(sector 2, track 0): 0 磁头、0 磁道、2 扇区
    mov bx, #0x0200 ; 设置读入的内存地址:BOOTSEG+address = 512,偏移512字节
    mov ax, #0x0200 + SETUPLEN; 设置读入的扇区个数(service 2, nr of sectors),SETUPLEN是读入的扇区个数,Linux 0.11 设置的是 4,我们不需要那么多,我们设置为 2(因此还需要添加变量 SETUPLEN=2)
    int 0x13         ; 应用 0x13 号 BIOS 中断读入 2 个 setup.s扇区
    jnc ok_load_setup ; 读入成功,跳转到 ok_load_setup: ok - continue

    mov dx, #0x0000 ; 软驱、软盘有问题才会执行到这里。我们的镜像文件比它们可靠多了
    mov ax, #0x0000 ; 复位软驱 reset the diskette 
    int 0x13    ; 继续尝试加载setup.s模块
    jmp load_setup


; 接下来要干什么?当然是跳到 setup 执行。要注意:我们没有将 bootsect 移到 0x9000,因此跳转后的段地址应该是 0x7ce0, 即我们要设置 SETUPSEG=0x07e0  
ok_load_setup:
    jmpi 0, SETUPSEG


msg1: 	.byte   13,10
    	.ascii  "Hello OS world, my name is JXL"
    	.byte   13,10,13,10

.org 510

boot_flag: .word  0xAA55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

‍ 然后我们再次编译,setup.s 和 bootsect.s 都要编译和链接,效率较低,类似我只之前讲到的写个 shell 脚本来编译,这次我们不用自己写,用 Makefile 里自带的即可

$ cd ~/oslab/linux-0.11/

$ make BootImage 
as86 -0 -a -o boot/bootsect.o boot/bootsect.s
ld86 -0 -s -o boot/bootsect boot/bootsect.o
as86 -0 -a -o boot/setup.o boot/setup.s
ld86 -0 -s -o boot/setup boot/setup.o
tools/build boot/bootsect boot/setup none  > Image
Root device is (3, 1)
Boot sector 512 bytes.
Setup is 512 bytes.
Unable to open 'system'
make: *** [Makefile:54:BootImage] 错误 1
1
2
3
4
5
6
7
8
9
10
11
12
13

有 Error!这是因为 make 根据 Makefile 的指引执行了 tools/build.c​,它是为生成整个内核的镜像文件而设计的,没考虑我们只需要 bootsect.s​ 和 setup.s​ 的情况。它在向我们要 “系统” 的核心代码。为完成实验,接下来给它打个小补丁。 ‍ 我们可以简单的看看 linux-0.11/Makefile 的内容:

all:	Image

Image: boot/bootsect boot/setup tools/system tools/build
	cp -f tools/system system.tmp
	strip system.tmp
	objcopy -O binary -R .note -R .comment system.tmp tools/kernel
	tools/build boot/bootsect boot/setup tools/kernel $(ROOT_DEV) > Image
	rm system.tmp
	rm tools/kernel -f
	sync

BootImage: boot/bootsect boot/setup tools/build
	tools/build boot/bootsect boot/setup none $(ROOT_DEV) > Image
	sync
1
2
3
4
5
6
7
8
9
10
11
12
13
14

​build.c​ 从命令行参数得到 bootsect、setup 和 system 内核的文件名,将三者做简单的整理后一起写入 Image。其中 system 是第三个参数(argv[3])。当 “make all” 的时候,这个参数传过来的是正确的文件名,build.c​ 会打开它,将内容写入 Image。

而 “make BootImage” 时,传过来的是字符串 "none"。所以,改造 build.c 的思路就是当 argv[3] 是"none"的时候,只写 bootsect 和 setup,忽略所有与 system 有关的工作,或者在该写 system 的位置都写上 “0”。

修改工作主要集中在 build.c​ 的尾部,可以参考下面的方式,将圈起来的部分注释掉(也可以删掉)。

​

当按照前一节所讲的编译方法编译成功后再 run,实验结果如下图

$ cd ~/oslab/linux-0.11
$ make BootImage
$ ../run
1
2
3

‍ ​

# 获取硬件参数版 v2.0

在理论课里我们讲到过,setup.s 将获得硬件参数放在内存的 0x90000 处。

用 ah=#0x03​ 调用 0x10​ 中断可以读出光标的位置,用 ah=#0x88​ 调用 0x15​ 中断可以读出内存的大小。有些硬件参数的获取要稍微复杂一些,如磁盘参数表。在 PC 机中 BIOS 设定的中断向量表中 int 0x41​ 的中断向量位置(4*0x41 = 0x0000:0x0104)存放的并不是中断程序的地址,而是第一个硬盘的基本参数表。第二个硬盘的基本参数表入口地址存于 int 0x46​ 中断向量位置处。每个硬盘参数表有 16 个字节大小。下表给出了硬盘基本参数表的内容:

表 1 磁盘基本参数表

位移 大小 说明
0x00 字 柱面数
0x02 字节 磁头数
... ... ...
0x0E 字节 每磁道扇区数
0x0F 字节 保留

所以获得磁盘参数的方法就是复制数据。

下面是将硬件参数取出来放在内存 0x90000 的关键代码。

mov    ax,#INITSEG
! 设置 ds = 0x9000
mov    ds,ax
mov    ah,#0x03
! 读入光标位置
xor    bh,bh
! 调用 0x10 中断
int    0x10
! 将光标位置写入 0x90000.
mov    [0],dx

! 读入内存大小位置
mov    ah,#0x88
int    0x15
mov    [2],ax

! 从 0x41 处拷贝 16 个字节(磁盘参数表)
mov    ax,#0x0000
mov    ds,ax
lds    si,[4*0x41]
mov    ax,#INITSEG
mov    es,ax
mov    di,#0x0004
mov    cx,#0x10
! 重复16次
rep
movsb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

‍ 显示获得的参数

现在已经将硬件参数(只包括光标位置、内存大小和硬盘参数,其他硬件参数取出的方法基本相同,此处略去)取出来放在了 0x90000 处,接下来的工作是将这些参数显示在屏幕上。这些参数都是一些无符号整数,所以需要做的主要工作是用汇编程序在屏幕上将这些整数显示出来。

以十六进制方式显示比较简单。这是因为十六进制与二进制有很好的对应关系(每 4 位二进制数和 1 位十六进制数存在一一对应关系),显示时只需将原二进制数每 4 位划成一组,按组求对应的 ASCII 码送显示器即可。ASCII 码与十六进制数字的对应关系为:0x30 ~ 0x39 对应数字 0 ~ 9,0x41 ~ 0x46 对应数字 a ~ f。从数字 9 到 a,其 ASCII 码间隔了 7h,这一点在转换时要特别注意。为使一个十六进制数能按高位到低位依次显示,实际编程中,需对 bx 中的数每次循环左移一组(4 位二进制),然后屏蔽掉当前高 12 位,对当前余下的 4 位(即 1 位十六进制数)求其 ASCII 码,要判断它是 0 ~ 9 还是 a ~ f,是前者则加 0x30 得对应的 ASCII 码,后者则要加 0x37 才行,最后送显示器输出。以上步骤重复 4 次,就可以完成 bx 中数以 4 位十六进制的形式显示出来。

下面是完成显示 16 进制数的汇编语言程序的关键代码,其中用到的 BIOS 中断为 INT 0x10,功能号 0x0E(显示一个字符),即 AH=0x0E,AL=要显示字符的 ASCII 码。

! 以 16 进制方式打印栈顶的16位数
print_hex:
! 4 个十六进制数字
    mov cx,#4
! 将(bp)所指的值放入 dx 中,如果 bp 是指向栈顶的话
    mov dx,(bp)
print_digit:
! 循环以使低 4 比特用上 !! 取 dx 的高 4 比特移到低 4 比特处。
    rol dx,#4
! ah = 请求的功能值,al = 半字节(4 个比特)掩码。
    mov ax,#0xe0f
! 取 dl 的低 4 比特值。
    and al,dl
! 给 al 数字加上十六进制 0x30
    add al,#0x30
    cmp al,#0x3a
! 是一个不大于十的数字
    jl  outp
! 是a~f,要多加 7
    add al,#0x07
outp:
    int 0x10
    loop    print_digit
    ret
! 这里用到了一个 loop 指令;
! 每次执行 loop 指令,cx 减 1,然后判断 cx 是否等于 0。
! 如果不为 0 则转移到 loop 指令后的标号处,实现循环;
! 如果为0顺序执行。
!
! 另外还有一个非常相似的指令:rep 指令,
! 每次执行 rep 指令,cx 减 1,然后判断 cx 是否等于 0。
! 如果不为 0 则继续执行 rep 指令后的串操作指令,直到 cx 为 0,实现重复。

! 打印回车换行
print_nl:
! CR
    mov ax,#0xe0d
    int 0x10
! LF
    mov al,#0xa
    int 0x10
    ret
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

‍ 只要在适当的位置调用 print_bx 和 print_nl(注意,一定要设置好栈,才能进行函数调用)就能将获得硬件参数打印到屏幕上,完成此次实验的任务。但事情往往并不总是顺利的,前面的两个实验大多数实验者可能一次就编译调试通过了(这里要提醒大家:编写操作系统的代码一定要认真,因为要调试操作系统并不是一件很方便的事)。但在这个实验中会出现运行结果不对的情况(为什么呢?因为我们给的代码并不是 100% 好用的)。所以接下来要复习一下汇编,并阅读《Bochs 使用手册》,学学在 Bochs 中如何调试操作系统代码。

我想经过漫长而痛苦的调试后,大家一定能兴奋地得到下面的运行结果:

​​ ‍

图 4 用可以打印硬件参数的 setup.s 进行引导的结果 Memory Size 是 0x3C00KB,算一算刚好是 15MB(扩展内存),加上 1MB 正好是 16MB,看看 Bochs 配置文件 bochs/bochsrc.bxrc:

!……
megs: 16
!……
ata0-master: type=disk, mode=flat, cylinders=410, heads=16, spt=38
!……
1
2
3
4
5

这些都和上面打出的参数吻合,表示此次实验是成功的。

实验楼的环境中参数可能跟上面给出的不一致。大家需要根据自己环境中 bochs/bochsrc.bxrc​ 文件中的内容才能确定具体的输出信息。

下面是提供的参考代码,大家可以根据这个来进行编写代码:

INITSEG  = 0x9000
entry _start
_start:
! Print "NOW we are in SETUP"
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#25
    mov bx,#0x0007
    mov bp,#msg2
    mov ax,cs
    mov es,ax
    mov ax,#0x1301
    int 0x10

    mov ax,cs
    mov es,ax
! init ss:sp
    mov ax,#INITSEG
    mov ss,ax
    mov sp,#0xFF00

! Get Params
    mov ax,#INITSEG
    mov ds,ax
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov [0],dx
    mov ah,#0x88
    int 0x15
    mov [2],ax
    mov ax,#0x0000
    mov ds,ax
    lds si,[4*0x41]
    mov ax,#INITSEG
    mov es,ax
    mov di,#0x0004
    mov cx,#0x10
    rep
    movsb

! Be Ready to Print
    mov ax,cs
    mov es,ax
    mov ax,#INITSEG
    mov ds,ax

! Cursor Position
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#18
    mov bx,#0x0007
    mov bp,#msg_cursor
    mov ax,#0x1301
    int 0x10
    mov dx,[0]
    call    print_hex
! Memory Size
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#14
    mov bx,#0x0007
    mov bp,#msg_memory
    mov ax,#0x1301
    int 0x10
    mov dx,[2]
    call    print_hex
! Add KB
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#2
    mov bx,#0x0007
    mov bp,#msg_kb
    mov ax,#0x1301
    int 0x10
! Cyles
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#7
    mov bx,#0x0007
    mov bp,#msg_cyles
    mov ax,#0x1301
    int 0x10
    mov dx,[4]
    call    print_hex
! Heads
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#8
    mov bx,#0x0007
    mov bp,#msg_heads
    mov ax,#0x1301
    int 0x10
    mov dx,[6]
    call    print_hex
! Secotrs
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#10
    mov bx,#0x0007
    mov bp,#msg_sectors
    mov ax,#0x1301
    int 0x10
    mov dx,[12]
    call    print_hex

inf_loop:
    jmp inf_loop

print_hex:
    mov    cx,#4
print_digit:
    rol    dx,#4
    mov    ax,#0xe0f
    and    al,dl
    add    al,#0x30
    cmp    al,#0x3a
    jl     outp
    add    al,#0x07
outp:
    int    0x10
    loop   print_digit
    ret
print_nl:
    mov    ax,#0xe0d     ! CR
    int    0x10
    mov    al,#0xa     ! LF
    int    0x10
    ret

msg2:
    .byte 13,10
    .ascii "NOW we are in SETUP"
    .byte 13,10,13,10
msg_cursor:
    .byte 13,10
    .ascii "Cursor position:"
msg_memory:
    .byte 13,10
    .ascii "Memory Size:"
msg_cyles:
    .byte 13,10
    .ascii "Cyls:"
msg_heads:
    .byte 13,10
    .ascii "Heads:"
msg_sectors:
    .byte 13,10
    .ascii "Sectors:"
msg_kb:
    .ascii "KB"

.org 510
boot_flag:
    .word 0xAA55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162

# 扩展

你可以显示不同的字符串,例如修改显示的字符串的颜色等等,或者可以实现载入 system 模块等等,完全可以自己有想做的功能,就做着玩~

可以参考 hoverwinter/HIT-OSLab: S - 哈工大《操作系统》实验 (opens new window),这里实现了 system 模块的载入,并且实验手册也写的很棒

# 附录

‍

# int 0x10 显示服务

AH 功能 调用参数 返回参数
0x03 读光标位置和大小 BH=页号(0-based) CH = 光标起始位置,
CL = 光标结束位置,
DH = 光标行号(0-based),
DL = 光标列号(0-based)
0x0e 显示字符(光标前移) AL=字符 BL=前景色 None
0x0f 获取当前显示模式 None AL = 当前的显示模式,
AH = 屏幕宽度,以字符列,
BH = 当前页号(0-based)
0x1300 显示字符串光标留在起始位置 ES:BP = 字符串地址,
CX = 字符串长度,
BH = 页号,
BL = 显示属性,
DH,DL = 显示字符串的起始行号和列号
None
0x1301 显示字符串光标跟随移动 ES:BP = 字符串地址,
CX = 字符串长度,
BH = 页号,
BL = 显示属性,
DH,DL = 显示字符串的起始行号和列号
None
上次更新: 2025/5/9 14:55:39
搭建实验环境
系统调用

← 搭建实验环境 系统调用→

最近更新
01
学点统计学:轻松识破一本正经的胡说八道
06-05
02
2025 年 5 月记
05-31
03
《贫穷的本质》很棒,但可能不适合你
05-27
更多文章>
Theme by Vdoing | Copyright © 2022-2025 | 粤 ICP 备 2022067627 号 -1 | 粤公网安备 44011302003646 号 | 点击查看十年之约
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式