这里在实验之前需要下载 Bochs-win32-2.6.11 作者使用的是Linux版本的,在Linux写代码不太舒服,所以最好在Windows上做实验,下载好虚拟机以后还需要下载Nasm汇编器,以及GCC编译器,为了能够使用DD命令实现磁盘拷贝,这里你可以安装windows 10 下面的子系统Ubuntu,需要使用命令时可以直接切换。

**注释:**该系列笔记是在学习《操作系统真相还原》时通过阅读后简化并适当描述整理的学习笔记,首先,致敬作者郑刚博士,在读本书时能深刻的感觉到作者写书时一丝不苟的态度,书很厚写的,讲解细致幽默,很能让人愿意继续读下去,同时也不得不佩服作者计算机底层功力的深厚,转载本文请一并附带郑刚版权信息。

BIOS 软件接力第一棒

BIOS 基本输入输出系统,BIOS代码所做的工作是一成不变的,所以他是被固化到ROM中的一块只读区域中,在开机时此ROM会被映射到低端1MB内存的顶部,原因是系统在开启时默认是实地址模式(该模式最大寻址范围0-fffff),所以其寻址范围也就被限制在了0xF0000-x0xFFFFF区域中,这64KB的内存就是BIOS的执行代码.

在开机的一瞬间,CPU的CS:IP寄存器会被强制初始化为0xF000:0xFFF0,在实地址模式下该地址需要乘以16也就是左移四位加上偏移地址得到,于是0xF000:0xFFF0就等效于0xFFFF0此处的地址距离0xFFFFF只有16个字节的空间,里面存放着一条jmp far f000:e05b = fe05b的汇编指令,该指令将跳转到真正的BIOS开始的位置.

接着BIOS将会通过自身的代码对硬件进行自检测,在初始化硬件后,则开始向内存0x000-0x3ff中初始化数据结构以及拷贝中断向量表,紧接着BIOS将会通过调用int 19h中断,此中断用以检测计算机中的硬盘,如果检测到0盘0道1扇区末尾的两个字节是0x55,0xaa则认为此扇区确实存在,于是就会将此区域中的内容,加载到内存7c00的位置,并通过一条jmp far 0:0x7c00h的指令跳转到该位置执行,这样BIOS就将CPU控制权交给了MBR了,而BIOS将会再次睡去.

MBR 收到跳转来源,继续执行。

此处的7c000就是MBR代码的开始位置,之所以是7C00是因为,DOS中要求最小内存是32KB,而MBR大小必须是512字节(1KB),所以选择32kB中的最后1KB的位置最为合适,32KB(0x8000)-1KB(0x400)=>0x7c00,这就是7C00的由来,同时还需要保证第510-511字节必须为0x55,0xaa才可以.

保存以下汇编代码,并使用 nasm -o mbr.bin mbr.asm编译简易版MBR文件.

SECTION MBR vstart=0x7c00     ; 告诉编译器加载到7c00内存处
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00 mov ax,Message
mov bp,ax ; 保存字符串地址
mov cx,15 ; 保存字符串长度
mov ax,01301h ; 子功能号13是显示字符及属性
mov bx,000ch ; 页号位0,使用黑色为背景色,红色为字体颜色
mov dl,0
int 10h ; 10h中断,用来显示字符
ret Message: db "hello lyshark !"
times 510-($-$$) db 0 ; 填充510字节为0
db 0x55,0xaa ; mbr的结束标志

进入Bochs目录下执行bximage.exe生成一个映像文件,默认是a.img,你可以改名为其他的,这里我定义为linux.img

并将编译好的mbr.bin写入到镜像中

dd if=mbr.bin of=linux.img bs=512 count=1 conv=notrunc

在Bochs目录下新建并编辑bosh.src保存,然后执行bochs.exe -f bosh.src模拟执行MBR代码.

megs:32
romimage:file=$BXSHARE/BIOS-bochs-latest
vgaromimage:file=$BXSHARE/VGABIOS-lgpl-latest
floppya:1_44=linux.img,status=inserted
boot:floppy
log:bochsout.txt
mouse:enabled=0
keyboard: keymap=$BXSHARE/keymaps/x11-pc-de.map

上方屏幕会比较混乱,这里我们先来进行清屏操作,清屏中断调用也是int10

SECTION MBR vstart=0x7c00     ; 告诉编译器加载到7c00内存处
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00 mov ax,0x600 ; 清屏范围,也就是宽度
mov bx,0x0
mov cx,0x0 ; 清屏 左上角(0,0)
mov dx,0x184f ; 清屏 右下角(80=0x4f,25=0x18)
int 0x10
mov ax,Message
mov bp,ax ; 保存字符串地址
mov cx,15 ; 保存字符串长度
mov ax,01301h ; 子功能号13是显示字符及属性
mov bx,000ch ; 页号位0,使用黑色为背景色,红色为字体颜色
mov dl,0
int 10h ; 调用10h号中断,用来显示字符
ret Message: db "hello lyshark !"
times 510-($-$$) db 0 ; 填充510字节为0
db 0x55,0xaa ; mbr的结束标志

执行结果,如下,但是,打印字符串,在底部,因为光标在底部。

设置光标到顶部,这里百度一下光标中断,发现了。

接着改进代码

SECTION MBR vstart=0x7c00     ; 告诉编译器加载到7c00内存处
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00 mov ax,0x600 ; 清屏范围,也就是宽度
mov bx,0x0
mov cx,0x0 ; 清屏 左上角(0,0)
mov dx,0x184f ; 清屏 右下角(80=0x4f,25=0x18)
int 0x10
mov dh,0x0 ; 设置光标列号
mov dl,0x0 ; 设置光标行号
mov bh,0x0 ; 页码
int 0x10
mov ax,Message
mov bp,ax ; 保存字符串地址
mov cx,15 ; 保存字符串长度
mov ax,01301h ; 子功能号13是显示字符及属性
mov bx,000ch ; 页号位0,使用黑色为背景色,红色为字体颜色
mov dl,0
int 10h ; 调用10h号中断,用来显示字符
ret Message: db "hello lyshark !"
times 510-($-$$) db 0 ; 填充剩余的510字节的空间为0
db 0x55,0xaa ; mbr的结束标志

完美结果。

mbr.asm

SECTION MBR vstart=0x7c00     ; 告诉编译器加载到7c00内存处
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00 mov ax,0x600 ; 清屏范围,也就是宽度
mov bx,0x0
mov cx,0x0 ; 清屏 左上角(0,0)
mov dx,0x184f ; 清屏 右下角(80=0x4f,25=0x18)
int 0x10
mov dh,0x0 ; 设置光标列号
mov dl,0x0 ; 设置光标行号
mov bh,0x0 ; 页码
int 0x10
mov ax,Message
mov bp,ax ; 保存字符串地址
mov cx,15 ; 保存字符串长度
mov ax,01301h ; 子功能号13是显示字符及属性
mov bx,000ch ; 页号位0,使用黑色为背景色,红色为字体颜色
mov dl,0
int 10h ; 调用10h号中断,用来显示字符
hlt
ret Message: db "hello lyshark !"
times 510-($-$$) db 0 ; 填充剩余的510字节的空间为0
db 0x55,0xaa ; mbr的结束标志

mbr.src

megs:32
romimage:file=./BIOS-bochs-latest
vgaromimage:file=./VGABIOS-lgpl-latest
boot:disk
mouse:enabled=0
ata0-master: type=disk, path="linux.img", mode=flat, status=inserted
keyboard: keymap=./x11-pc-de.map

填充数据

dd if=mbr.bin of=linux.img bs=512 count=1 conv=notrunc
dd if=/dev/zero of=linux.img seek=1 bs=512 count=2879

运行

bochsdbg -q -f mbr.src
vb sp:0x7c00
c

让我们对显卡说点什么?

上面我们通过调用BIOS提供的int 0x10中断来实现打印字符操作,但我们在后期必须要借助显卡来输出图像,而显卡是外部设备,必须通过总线来操作。

由于CPU使用的信号是TTL电平,而外部设备都是机械设备,故他们不会使用该电平驱动,这就导致CPU与硬件设备没有办法实现沟通,硬件工程师们提供的方法是,在这两者之间架起一座桥,也就是在CPU和外设之间加上一层IO接口,该接口的作用就是实现CPU和外设之间相互做协调转换。

其次外部设备的种类也是多种多样的,其输出的信号可能是数字信号,也可能是模拟信号,而我们的CPU只能处理数字信号,数字信号需要经过数模转换器<D/A>成模拟量才能送到外设来驱动硬件工作,模拟量也同样需要经过模数转换器<A/D>转换成数字量才能被CPU直接处理,所以接口电路中需要包括A/D转换器和D/A转换器。

转换后的数字信号,会经过总线进行传递,总线的别名是BUS,之所以叫做BUS是因为其是公共线路,所有硬件设备都会走此线路,但同一时刻,CPU只能和一个IO接口(寄存器/端口)通信,当有多个IO接口同时想和CPU通信时,那么IO仲裁模块会对其进行竞争与选优,仲裁模块固化到,输入输出控制中心(ICH)也就是南桥芯片上的。

多数情况下,南桥和北桥是成对出现的,南桥主要负责连接PCI,PCI-Express,AGP等低速设备,而北桥则用于链接高速设备,如内存等。

IO接口都是串行口,其在设计之初就是负责与CPU进行通信的,我们想要与CPU通信,其实是向这些接口中写入数据,同时为了区别CPU中的寄存器,所以把IO接口叫做端口,某些外设可以通过内存映射来访问,即把某些端口映射到指定内存中,访问某个内存区域就相当于访问了指定的端口。

SECTION MBR vstart=0x7c00     ; 告诉编译器加载到7c00内存处
mov ax,cs
mov sp,0x7c00
mov ax,0xb800
mov gs,ax mov ax,0x600 ; 清屏范围,也就是宽度
mov bx,0x0
mov cx,0x0 ; 清屏 左上角(0,0)
mov dx,0x184f ; 清屏 右下角(80=0x4f,25=0x18)
int 0x10 mov dh,0x0 ; 设置光标列号
mov dl,0x0 ; 设置光标行号
mov bh,0x0 ; 页码
int 0x10 mov byte [gs:0x00],'M'
mov byte [gs:0x01],0xa4 ; 显示A=绿色闪烁 4=红色 mov byte [gs:0x02],'B'
mov byte [gs:0x03],0xa5 mov byte [gs:0x04],'R'
mov byte [gs:0x05],0xa6
ret times 510-($-$$) db 0 ; 填充剩余的510字节的空间为0
db 0x55,0xaa ; mbr的结束标志

Bochs调试命令基础

Bochs调试命令常用的有以下几种.

<bochs:1> vbreak 0x0000:0x7c000      7c000设置断点
<bochs:1> pb 0x7c000 设置物理断点
<bochs:1> vb sp:0x7c00 设置虚拟断点(保护模式)
<bochs:1> info break 显示所有断点状态
<bochs:1> delete num 删除一个断点 <bochs:1> c 运行遇到断点停下
<bochs:1> n 执行下一指令
<bochs:1> r 显示寄存器
<bochs:1> u/10 向下反汇编10条
<bochs:1> print-stack 打印堆栈 x /nuf addr 查看一个线性地址的内存
xp /nuf addr 查看一个物理地址的内存
n 显示多少个字节的内存
u 单位长度; one o单位f
b 字节
h 半字(2 字节)
w 字 (4 字节)
g 双字 (8 字节) F 打印格式:
x 打印十六进制
d 打印十进制
u 打印无符号十进制
o 打印八进制
t 打印二进制

SECTION MBR vstart=0x7c00     ; 告诉编译器加载到7c00内存处
mov ax,cs
mov sp,0x7c00
mov ax,0xb800
mov gs,ax mov ax,0x600 ; 清屏范围,也就是宽度
mov bx,0x0
mov cx,0x0 ; 清屏 左上角(0,0)
mov dx,0x184f ; 清屏 右下角(80=0x4f,25=0x18)
int 0x10 mov dh,0x0 ; 设置光标列号
mov dl,0x0 ; 设置光标行号
mov bh,0x0 ; 页码
int 0x10 mov byte [gs:0x00],'L'
mov byte [gs:0x01],0xa4 ; 显示A=绿色闪烁 4=红色 mov byte [gs:0x02],'y'
mov byte [gs:0x03],0xa5 mov byte [gs:0x04],'S'
mov byte [gs:0x05],0xa6 mov byte [gs:0x6],'h'
mov byte [gs:0x7],0xa7 mov byte[gs:0x8],'a'
mov byte [gs:0x9],0xa6 mov byte[gs:0xa],'r'
mov byte [gs:0xb],0xa5 mov byte[gs:0xc],'k'
mov byte [gs:0xd],0xa4
hlt
ret times 510-($-$$) db 0 ; 填充剩余的510字节的空间为0
db 0x55,0xaa ; mbr的结束标志

循环显存

_start:
; 清屏和设置光标位置
mov ax,0x600 ; 清屏范围,也就是宽度
mov bx,0x0
mov cx,0x0 ; 清屏 左上角(0,0)
mov dx,0x184f ; 清屏 右下角(80=0x4f,25=0x18)
int 0x10 mov dh,0x0 ; 设置光标列号
mov dl,0x0 ; 设置光标行号
mov bh,0x0 ; 页码
int 0x10 ;初始化数据段,使其指向段基址0X7C0处,即Boot代码被加载的地方
mov ax, 0x07c0 ; 设置加载基址
mov ds, ax ;将文本显示内存段基址 放在ES中,供后面显示字符使用
mov ax, 0xb800 ; 设置显存地址
mov es, ax mov cx, msglen ; 获取字符串长度
mov si, message ; 设置字符串基址
xor di, di loop_str:
mov al, [si] ; 每次取出一个字符
mov [es:di], al ; 将字符逐一赋值到显存中
inc si
inc di mov byte [es:di], 0x07 ; 设置字体颜色
inc di
loop loop_str hlt ; 程序在此处终止 message db "Loading MBR..."
msglen db $ - message times 510-($-$$) db 0 ; 填充剩余的510字节的空间为0
db 0x55,0xaa ; mbr的结束标志

Bochs 调试命令

CPU加电后,会跳转到 0xffff0 处,我们可以反汇编这段内存地址,向下反汇编10条。

分别显示,CPU模式,中断,call调用源。

设置vb虚拟地址断点,pb设置物理地址断点。blist显示所有断点。

bpd禁用断点,bpe启用断点。del删除断点。

info idt = 显示中断向量表 , gdt 全局描述符表,ldt 局部描述符表,tss 任务状态段,ivt 中断向量表

SECTION MBR vstart=0x7c00     ; 告诉编译器加载到7c00内存处
; 清屏和设置光标位置
mov ax,0x600 ; 清屏范围,也就是宽度
mov bx,0x0
mov cx,0x0 ; 清屏 左上角(0,0)
mov dx,0x184f ; 清屏 右下角(80=0x4f,25=0x18)
int 0x10 mov dh,0x0 ; 设置光标列号 左上角(0,0)
mov dl,0x0 ; 设置光标行号 右下角(0,0)
mov bh,0x0 ; 页码
int 0x10 ; 初始化,使SP寄存器指向段基址0X7C0处,GS指向显存基地址
mov ax,cs
mov sp,0x7c00
mov ax,0xb800
mov gs,ax ; 设置显存地址 ; 设置字符串长度与字符串基地址
mov cx, msglen ; 获取字符串长度
mov si, message ; 设置字符串基址
xor di, di ; 每次清空di寄存器 loop_str:
mov al, [si] ; 每次取出一个字符
mov [gs:di], al ; 将字符逐一赋值到显存中
inc si
inc di mov byte [gs:di], 000ch ; 设置字体颜色
inc di
loop loop_str hlt ; 程序在此处终止 ;message db "Hello LyShark...",0ah,0dh
message db "Hello LyShark..."
msglen equ $ - message times 510-($-$$) db 0 ; 填充剩余的510字节的空间为0
db 0x55,0xaa ; mbr的结束标志

操作系统开发:BIOS/MBR基础与调试的更多相关文章

  1. BIOS+MBR操作系统引导方式

    1. 主引导记录(Master Boot Record,缩写:MBR) 主引导记录又叫做主引导扇区,是计算机开机后启动操作系统时所必须要读取的硬盘首个扇区,它在硬盘上的三维地址为(柱面,磁头,扇区)= ...

  2. MongoDB实战开发 【零基础学习,附完整Asp.net示例】

    MongoDB实战开发 [零基础学习,附完整Asp.net示例] 阅读目录 开始 下载MongoDB,并启动它 在C#使用MongoDB 重构(简化)代码 使用MongoDB的客户端查看数据 使用Mo ...

  3. BIOS+MBR模式 VS UEFI+GPT模式

     EFI与MBR启动的区别 大硬盘和WIN8系统,让我们从传统的BIOS+MBR模式升级到UEFI+GPT模式,现在购买的主流电脑,都是预装WIN8系统,为了更好的支持2TB硬盘 ,更快速的启动win ...

  4. BIOS + MBR > UEFI + GPT

    BIOS + MBR > UEFI + GPT硬件接口系统与磁盘分区UEFI用于取代老旧的BIOS,而GPT则取代老旧的MBR. 名词解释: BIOS (Basic Input/Output S ...

  5. Chino 操作系统开发日志 (1) - 为 IoT 而生

    引言 很多人都听说过 IoT (物联网)这个词,越来越多的人在装修时开始选择智能家居,很多人也购买智能音箱做智能家居控制,想必未来一定是 AI + 物联网的时代. 一种技术要发展并走向成熟必须要降低门 ...

  6. BIOS/MBR UEFI/GPT关系与区别-资料整理

    ---恢复内容开始--- 关于 BIOS/MBR UEFI/GPT他们之间的关系一直比较疑惑, 首先一点前提 BIOS UEFI 是一类,是控制硬件,引导启动的:MBR GPT是硬盘的分区定义.. 后 ...

  7. 《Orange’s 一个操作系统的实现》1.搭建操作系统开发环境

    书中给出了两种环境:windows和linux,平台选择根据自己喜好.本人这里选择ubuntu10.04+virtualbox作为开发平台. 1.下载.安装VirtualBox     http:// ...

  8. ESP8266开发之旅 基础篇② 如何安装ESP8266的Arduino开发环境

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  9. ESP8266开发之旅 基础篇③ ESP8266与Arduino的开发说明

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  10. C# 开发的windows服务 不能调试——讨论整理

    CSDN的标题:C# 开发的windows服务 不能调试 System.Diagnostics.Debugger.Launch();在想加断点的地方加入这行,是进入断点的,可以进行调试,我的是xp系统 ...

随机推荐

  1. 解决ttrss(Tiny Tiny RSS)中fever无法使用的问题

    问题描述 在ttrss刚搭建好的时候,进行了如下操作: 随后键入了密码(fever密码) 最后,按照官方给的提示,在Fluent Reader中测试,弹出如下错误信息: 解决方案 复制官方给的链接,删 ...

  2. 一个NASA、Google都在用的开源CMS:wagtail

    说起开源CMS,你会想到哪些呢?WordPress?DoraCMS?joomla? 今天再给大家推荐一个非常好用的开源CMS:Wagtail 如果您正在选型的话,可以了解一下Wagtail的特点: 基 ...

  3. 如何在CSDN上如何快速转载博客

    复制粘贴应该是最显而易见的方法,但是不仅会有丢失内容,而且格式也会丢失.要想达到更好的效果,可以从html源码入手. 1.在chrome浏览器中打开要转载的文章,右键选择检查(or使用F12) 2.在 ...

  4. CodeForces - 651A Joysticks ( 不难 但有坑 )

    正式更换编译器为: VS Code 如何配置环境:click here 代码格式化工具:clang-format A. Joysticks 题目连接: http://www.codeforces.co ...

  5. 技术文档 | 在Jenkins及GitlabCI中集成OpenSCA,轻松实现CI/CD开源风险治理

    ​插播: OpenSCA-cli 现支持通过 homebrew 以及 winget 安装: Mac/Linux brew install opensca-cli Windows winget inst ...

  6. P4913【黄】

    这题好像可以用线段树什么的高级做法来做,但我感觉我这个简单做法不管是时间还是空间都和那些复杂的做法差不了太多.重点是很优雅,思路非常简单,而且代码很短,用OOP思想写成的代码可读性极高,不用注释估计都 ...

  7. 使用Swagger,在编写配置类时报错Caused by: java.lang.NullPointerException: Cannot invoke "org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getPatterns()" because "this.condition" is null

    1.问题 Caused by: java.lang.NullPointerException: Cannot invoke "org.springframework.web.servlet. ...

  8. Linux 中常用的基础命令

    by emanjusaka from https://www.emanjusaka.top/2024/01/linux-base-command 彼岸花开可奈何 本文欢迎分享与聚合,全文转载请留下原文 ...

  9. css - 使用 figure 和 figcaption 快速实现 图片加文字的垂直方向的布局 ( 不支持ie9以下版本 )

    一,属性介绍 1. 浏览器支持 注释:Internet Explorer 8 以及更早的版本不支持 <figure> 标签.Internet Explorer 9, Firefox, Op ...

  10. asp.net core 开启gzip压缩

    // 第一步: 配置gzip与br的压缩等级为最优 services.Configure<BrotliCompressionProviderOptions>(options => { ...