从裸机启动开始运行一个C++程序(七)

前序文章请看:
从裸机启动开始运行一个C++程序(六)
从裸机启动开始运行一个C++程序(五)
从裸机启动开始运行一个C++程序(四)
从裸机启动开始运行一个C++程序(三)
从裸机启动开始运行一个C++程序(二)
从裸机启动开始运行一个C++程序(一)

重新写一份MBR代码

前面我们花了不少的篇幅来介绍保护模式,以及通过汇编指令进入保护模式的方法。那么这一节,我们就来上一个完整的实例,首先在实模式下加载Kernel,然后配置GDT,之后进入保护模式,跳转至Kernel,再在Kernel里再打印一些文字用以区分。

前面的实例中我们已经把工程代码分为了mbr和kernel,在mbr中读盘,加载到内存中,然后再通过跳转指令运行kernel文件。虽然照理来说,在kernel中再配置GDT然后进入保护模式也没什么问题,但这样就会使得kernel中同时夹杂两种模式的指令代码(后续我们介绍完386模式以后,就还可能会同时混有16位和32位指令),不方便管理和维护。因此,我们在mbr中就做好这一些,最后以保护模式跳转至kernel部分,这样我们的kernel就会纯粹许多。

下面是我们重新写的一份MBR代码:

; 调用0x10号BIOS中断,清屏
mov al, 0x03
mov ah, 0x00
int 0x10 ; LBA28模式,逻辑扇区号28位,从0x00000000xFFFFFFF
; 设置读取扇区的数量
mov dx, 0x01f2
mov al, 2 ; 读取连续的几个扇区,每读取一个al就会减1
out dx, al
; 设置起始扇区号,28位需要拆开
mov dx, 0x01f3
mov al, 0x02 ; 从第2个扇区开始读(1起始,0留空),扇区号0~7out dx, al
mov dx, 0x01f4 ; 扇区号8~15mov al, 0
out dx, al
mov dx, 0x01f5 ; 扇区号16~23mov al, 0
out dx, al
mov dx, 0x01f6
mov al, 111_0_0000b ;4位是扇区号24~27位,第4位是主从盘(01从),高3位表示磁盘模式(111表示LBA)
; 配置命令
mov dx, 0x01f7
mov al, 0x20 ; 0x20命令表示读盘
out dx, alwait_finish:
; 检测状态,是否读取完毕
mov dx, 0x01f7
in al, dx ; 通过该端口读取状态数据
and al, 1000_1000b ; 保留第7位和第3cmp al, 0000_1000b ; 要检测第7位为0(表示不在忙碌状态)和第3位是否是1(表示已经读取完毕)
jne wait_finish ; 如果不满足则循环等待; 从端口加载数据到内存
mov cx, 512 ; 一共要读的字节除以2(表示次数,因为每次会读2字节所以要除以2mov dx, 0x01f0
mov ax, 0x0800
mov ds, ax
xor bx, bx ; [ds:bx] = 0x08000
read:
in ax, dx ; 16位端口,所以要用16位寄存器
mov [bx], ax
add bx, 2 ; 因为ax是16位,所以一次会写2字节
loop read; 下面配置GDT
mov ax, 0x07e0
mov es, ax; 空白段
mov [es:0x00], dword 0
mov [es:0x04], dword 0; 1号段
; 基址0x8000,大小8KB
mov [es:0x08], word 0x1fff ; Limit=0x1fff
mov [es:0x0a], word 0x8000 ; Base=0x008000,这是低16位
mov [es:0x0c], byte 0      ; 这是Base的高8位
mov [es:0x0d], byte 1_00_1_100_0b ; P=1, DPL=0, S=1, Type=100b, A=0
mov [es:0x0e], word 0      ; 保留位都置0; 下面是gdt信息的配置(暂且放在0x07f00的位置)
mov ax, 0x07f0
mov es, ax
mov [es:0x00], word 15      ; 因为目前配了2个段,长度为16,所以limit为15
mov [es:0x02], dword 0x7e00 ; GDT配置表的首地址
; 把gdt配置进gdtr
lgdt [es:0x00]mov eax, cr0
or eax, 0x01 ; PE位置1,启动保护模式
mov cr0, eaxjmp 00001_00_0b:0 ; 远跳指令可以刷新cs,使用1号段,正好跳转至kernel的加载位置(0x8000)times 510-($-$$) db 0 ; MBR剩余部分用0填充
dw 0xaa55

可以看到最后有一个远跳指令jmp 00001_00_0b:0,由于这个时候我们已经通过控制cr0寄存器来进入保护模式了,所以前面段的部分就已经不是段基址而是段选择子了。通过远跳指令我们就可以刷新cs寄存器,让他表示选择子1号段,而根据GDT的配置,1号段的段基址就是0x8000,也就正好是我们Kernel加载的位置。

所以接下来,我们只需要在Kernel中,输出一下信息,观察程序能否正常运行即可。但有个严重的问题是,要想输出信息我们需要写显存,可是显存在0xb8000~0xb8f9f,而1号段的界限在0x1fff,所以,我们当前的情况是没法操作显存的。那怎么办?我相信有读者可能会想到,那要不我们把1号段配长一点,包括到显存,是不是就可以操作了?

答案是:不可以!因为除了界限问题,保护模式下段是具有属性的,也就是GDT中的TypeXEW这3位,我们配置的时候配置的是100W0的时候是不可写的,所以我们不可以向这个段里写数据。

那,把Type配成101不就可以解决了么?理论上来说是的,但并不推荐大家这样去做,因为我们不希望指令段在执行时轻易被更改,这样程序的风险极大。当然了,如果大家仅仅是自己做实验方便的话,倒也无妨。

不过更稳妥的做法,是再配一个段,专门去管理显存。这样我们就要回到MBR里加一个GDT,把显存区域划分成一个段,然后再在显存中写数据即可。

; 2号段
; 基址0xb8000,上限0xb8f9f,覆盖所有显存
mov [es:0x10], word 0x0f9f ; Limit=0x0f9f
mov [es:0x12], word 0x8000 ; Base=0x0b8000,这是低16位
mov [es:0x14], byte 0x0b   ; 这是Base的高8位
mov [es:0x15], byte 1_00_1_001_0b ; P=1, DPL=0, S=1, Type=001b, A=0
mov [es:0x16], word 0

别忘了对应的GDT的配置也要变,要让2号段生效才行:

; 下面是gdt信息的配置(暂且放在0x07f00的位置)
mov ax, 0x07f0
mov es, ax
mov [es:0x00], word 23      ; 因为目前配了3个段,长度为24,所以limit为23
mov [es:0x02], dword 0x7e00 ; GDT配置表的首地址

下面给出修正后的完整MBR代码:

; 调用0x10号BIOS中断,清屏
mov al, 0x03
mov ah, 0x00
int 0x10 ; LBA28模式,逻辑扇区号28位,从0x00000000xFFFFFFF
; 设置读取扇区的数量
mov dx, 0x01f2
mov al, 2 ; 读取连续的几个扇区,每读取一个al就会减1
out dx, al
; 设置起始扇区号,28位需要拆开
mov dx, 0x01f3
mov al, 0x02 ; 从第2个扇区开始读(1起始,0留空),扇区号0~7位
out dx, al
mov dx, 0x01f4 ; 扇区号8~15位
mov al, 0
out dx, al
mov dx, 0x01f5 ; 扇区号16~23位
mov al, 0
out dx, al
mov dx, 0x01f6
mov al, 111_0_0000b ;4位是扇区号24~27位,第4位是主从盘(01从),高3位表示磁盘模式(111表示LBA)
; 配置命令
mov dx, 0x01f7
mov al, 0x20 ; 0x20命令表示读盘
out dx, alwait_finish:
; 检测状态,是否读取完毕
mov dx, 0x01f7
in al, dx ; 通过该端口读取状态数据
and al, 1000_1000b ; 保留第7位和第3位
cmp al, 0000_1000b ; 要检测第7位为0(表示不在忙碌状态)和第3位是否是1(表示已经读取完毕)
jne wait_finish ; 如果不满足则循环等待; 从端口加载数据到内存
mov cx, 512 ; 一共要读的字节除以2(表示次数,因为每次会读2字节所以要除以2)
mov dx, 0x01f0
mov ax, 0x0800
mov ds, ax
xor bx, bx ; [ds:bx] = 0x08000
read:
in ax, dx ; 16位端口,所以要用16位寄存器
mov [bx], ax
add bx, 2 ; 因为ax是16位,所以一次会写2字节
loop read; 下面配置GDT
mov ax, 0x07e0
mov es, ax; 空白段
mov [es:0x00], dword 0
mov [es:0x04], dword 0; 1号段
; 基址0x8000,大小8KB
mov [es:0x08], word 0x1fff ; Limit=0x1fff
mov [es:0x0a], word 0x8000 ; Base=0x008000,这是低16位
mov [es:0x0c], byte 0      ; 这是Base的高8位
mov [es:0x0d], byte 1_00_1_100_0b ; P=1, DPL=0, S=1, Type=100b, A=0
mov [es:0x0e], word 0; 2号段
; 基址0xb8000,上限0xb8f9f,覆盖所有显存
mov [es:0x10], word 0x0f9f ; Limit=0x0f9f
mov [es:0x12], word 0x8000 ; Base=0x0b8000,这是低16位
mov [es:0x14], byte 0x0b   ; 这是Base的高8位
mov [es:0x15], byte 1_00_1_001_0b ; P=1, DPL=0, S=1, Type=001b, A=0
mov [es:0x16], word 0; 下面是gdt信息的配置(暂且放在0x07f00的位置)
mov ax, 0x07f0
mov es, ax
mov [es:0x00], word 23      ; 因为目前配了3个段,长度为24,所以limit为23
mov [es:0x02], dword 0x7e00 ; GDT配置表的首地址
; 把gdt配置进gdtr
lgdt [es:0x00]mov eax, cr0
or eax, 0x01 ; PE位置1,启动保护模式
mov cr0, eaxjmp 00001_00_0b:0 ; 远跳指令可以刷新cs,使用1号段,正好跳转至kernel的加载位置(0x8000)times 510-($-$$) db 0 ; MBR剩余部分用0填充
dw 0xaa55

然后我们在Kernel中打印随便打印点东西,查看效果即可。注意,要写显存的话,需要将段寄存器调整到2号选择子。

以下是Kernel例程:

begin:mov ax, 00010_00_0b ; 选择2号段,以操作显存
mov ds, ax
; 打印Hello
mov [0x0000], byte 'H'
mov [0x0001], byte 0x0f
mov [0x0002], byte 'e'
mov [0x0003], byte 0x0f
mov [0x0004], byte 'l'
mov [0x0005], byte 0x0f
mov [0x0006], byte 'l'
mov [0x0007], byte 0x0f
mov [0x0008], byte 'o'
mov [0x0009], byte 0x0fhlttimes 1024-($-begin) db 0 ; 补满2个扇区

以下是运行效果图:
运行效果

小结

由此,我们成功在保护模式下写入数据,并完成的打印。

我会将这一节的整个工程文件打包上传到附件中,读者可以自行使用。

下一篇开始我们会继续进军386模式,也就是后来非常成熟的IA-32架构,并介绍对应的32位指令。要知道IA-32架构的代码就已经可以跟C语言代码做链接了哟!离我们的目标就近了许多,大家敬请期待~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/154985.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Httpd(一)

介绍 httpd是apache超文本传输协议(HTTP)服务器的主程序。被设计为一个独立运行的后台进程,它会建立一个处理请求的子进程或线程的池。 特性 高度模块化:core modules DSO:Dynamic Shared Object 动态加载/卸载 MPM:multi-p…

【工作流引擎】Activiti的使用03

流程定义查询 // 获取部署时的信息ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();RepositoryService repositoryService processEngine.getRepositoryService();ProcessDefinitionQuery processDefinitionQuery repositoryService.createProcessDe…

人工智能在教育上的应用2-基于大模型的未来数学教育的情况与实际应用

大家好,我是微学AI ,今天给大家介绍一下人工智能在教育上的应用2-基于大模型的未来数学教育的情况与实际应用,随着人工智能(AI)和深度学习技术的发展,大模型已经开始渗透到各个领域,包括数学教育。本文将详细介绍基于大模型在数学…

【ARM AMBA5 CHI 入门 12.1 -- CHI 链路层详细介绍 】

文章目录 CHI 版本介绍1.1 CHI 链路层介绍1.1.1 Flit 切片介绍1.1.2 link layer credit(L-Credit)机制1.1.3 Channel1.1.4 Port1.1. RN Node 接口定义1.1.6 SN Node 接口定义1.2 Channel interface signals1.2.1 Request, REQ, channel1.2.2 Response, RSP, channel1.2.3 Snoop…

MongoDB 笔记

1 insert 、create、save区别 insert: 主键不存在则正常插入;主键已存在,抛出DuplicateKeyException 异常 save: 主键不存在则正常插入;主键已存在则更新 insertMany:批量插入,等同于批量执行 insert create&#x…

PowerShell pnpm : 无法加载文件 C:\Users\lenovo\AppData\Roaming\npm\pnpm.ps1

1、右键点击【开始】,打开Windows PowerShell(管理员) 2、运行命令set-ExecutionPolicy RemoteSigned 3、根据提示,输入A,回车 此时管理员权限已经可以运行pnpm 如果vsCode还报该错误 继续输入 4、右键点击【开始】,打…

【gmail注册教程】手把手教你注册Google邮箱账号

手把手教你注册Google邮箱账号 写在前面: 要注意,注册Google邮箱必须要确保自己能够 科学上网,如果暂时做不到,请先进行相关学习。使用的手机号是大陆(86)的。 在保证自己能够科学上网后,在浏…

基于ChatGPT+词向量/词嵌入实现相似商品推荐系统

最近一个项目有个业务场景是相似商品推荐,给一个商品描述(比如 WIENER A/B 7IN 5/LB FZN ),系统给出商品库中最相似的TOP 5种商品,这种单纯的推荐系统用词向量就可以实现,不过,这个项目特点是商品库巨大,有…

上海亚商投顾:沪指探底回升 华为汽车概念股集体大涨

上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 一.市场情绪 三大指数昨日探底回升,早盘一度集体跌超1%,随后震荡回暖,深成指、创业板指…

idea 插件推荐(持续更新)

文章目录 Material Theme UIcodeium(建议有梯子的使用)Key Promoter XCodeGlanceRainbow BracketsMarkdown NavigatorRestfulToolkitString Manipulation Material Theme UI 谁不想拥有一款狂拽炫酷 吊炸天 的编码主题呢,给你推荐Material Theme UI Plugin Material Theme UI是…

地产三维实景vr展示的功能及特点

随着科技的不断发展,VR(虚拟现实)技术也越来越成熟。VR技术的广泛应用,已经逐渐渗透到各个领域,其中引人注目的就是虚拟展馆。虚拟展馆是一种利用VR技术构建的线上展示空间,让观众可以在家中就能参观展览,带来了极大地…

那些年,我们一起发现的Bug

一、背景 在这篇文章中,分享一些自己在工作中或别人发现的一些常见Bug,与大家共同成长~ 二、常见Bug分类 1、前后命名不一致 举个例子 接口入参中的名称是:aslrboot Java代码中使用的名称是:aslrBoot Codis中存储的名称是&…

[硬件基础]-双稳态多谐振荡器配置

双稳态多谐振荡器配置 文章目录 双稳态多谐振荡器配置1、概述2、双稳态多谐振荡器的内部运行原理 在上一篇文章中,我们深入了解了555定时器在单稳态模式下的内部工作原理。 如果您已经理解了上一篇文章,那么本文对您来说将会非常简单。 我们将研究 555 定…

京东数据接口|电商运营中数据分析的重要性

在电商运营中,数据分析是非常重要的一环,它可以帮助电商企业更好地了解市场、了解消费者、了解产品、了解销售渠道等各种信息,从而制定更为科学有效的运营策略,提高销售效益。 数据方面用户可以直接选择使用数据接口来获取&#…

95740-26-4|用于体内DNA合成的探针F-ara-EdU

产品简介:(2S)-2-Deoxy-2-fluoro-5-ethynyluridine,一种用于体内DNA合成的探针,其毒性比EdU和BrdU都小。当需要延长细胞存活时间和不受干扰的细胞周期进展时,非常适合进行代谢DNA标记。 CAS号:95740-26-4 分子式&…

AIGC|利用大语言模型实现智能私域问答助手

随着ChatGPT的爆火,最近大家开始关注到大语言模型(LLM)这个领域。像雨后春笋一样,国内外涌现出了很多LLM。作为开发者,我们通常会关注LLM各自擅长的领域和能力,然后思考如何利用它们的能力来解决某个场景或…

ARM day5

三盏灯流水 .text .global _start _start: 1.LDR R0,0X50000A28LDR R1,[R0]ORR R1,R1,#(0X1<<4)STR R1,[R0] 1.LDR R0,0X50000A28LDR R1,[R0]ORR R1,R1,#(0X1<<5)STR R1,[R0] 2.LDR R0,0X50006000LDR R1,[R0]BIC R1,R1,#(0X3<<20)ORR R1,R1,#(0X1<<…

【数据结构】二叉树的顺序结构及实现

目录 1. 二叉树的顺序结构 2. 堆的概念及结构 3. 堆的实现 3.1 堆向下调整算法 3.2 堆的创建 3.3 建堆时间复杂度 3.4 堆的插入 3.5 堆的删除 3.6 堆的代码实现 4. 堆的应用 4.1 堆排序 4.2 TOP-K问题 1. 二叉树的顺序结构 普通的二叉树是不适合用数组来存储的&…

【算法-动态规划】不同路径

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

智慧电力物联网系统引领电力行业数字化发展

智慧电力物联网系统是以提高用户侧电力运行安全、降低运维成本为目的的一套电力运维管理系统。综合分析采用智慧物联网、人工智能等现代化经济信息网络技术&#xff0c;配置智能采集终端、小安神童值班机器人或边缘网关&#xff0c;实现对企事业用户供配电系统的数字化远程监控…