Linux小型操作系统项目,《操作系统真象还原》第三章——完善MBR

news2024/9/23 11:20:35

前引

        上一章我们完成了MBR的雏形编写,但是只打印了几个字符,这一章我们才要真正地去完成MBR的功能。

        在完成MBR的功能之前我们要先了解一些知识,首先介绍什么是实模式。

        书上的内容实在繁杂,简单地说,实模式没有虚拟和抽象这一概念,程序员写的代码,只要写清楚放在什么地方就是放在内存的什么地方,没有系统进行保护,其他程序可以轻松覆盖、内存对于程序员来说是可随意访问的。因此会有很大的操作隐患。但是电脑开机后是先进入实模式的,然后再进入保护模式,这之间的过渡即MBR的作用。

        


        在上一章中BIOS建立了中断向量表,我们可以通过中断打印MBR几个字符;但是中断向量表只能在实模式下存在,等进入保护模式就不能运行了。所以我们需要换一种方式打印字符。

         这种方式即直接通过CPU控制显卡的显存来操作显卡;CPU通过操作这部分内存,就可以通过显示器打印字符了;

        

        显示器上每个字在内存中占2字节16位的内存,其中低八位是字符的ASCII码,高八位是控制字符颜色的;具体哪一位对应什么颜色可以搜索资料;

        有了这些基础知识,我们就可以对上一章的MBR代码进行改造了;

MBR程序编写

代码逻辑:

1.指定vstart=0x7c00,告诉编译器本程序起始地址请帮我放到0x7c00处

2.查询调用BIOS清屏(后面会直接通过显卡实现)

3.指定段基址,gs=0xb800

4.死循环,填入MBR规定510字节大小剩下的0;固定结尾两字节0x55 和0xaa

                                        ;主引导程序 
                                        ;
                                        ;LOADER_BASE_ADDR equ 0xA000 
                                        ;LOADER_START_SECTOR equ 0x2
                                        ;------------------------------------------------------------
SECTION MBR vstart=0x7c00         
    mov ax,cs      
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov fs,ax
    mov sp,0x7c00
    mov ax,0xb800
    mov gs,ax

                                        ; 清屏
                                        ;利用0x06号功能,上卷全部行,则可清屏。
                                        ; -----------------------------------------------------------
                                        ;INT 0x10   功能号:0x06	   功能描述:上卷窗口
                                        ;------------------------------------------------------
                                        ;输入:
                                        ;AH 功能号= 0x06
                                        ;AL = 上卷的行数(如果为0,表示全部)
                                        ;BH = 上卷行属性
                                        ;(CL,CH) = 窗口左上角的(X,Y)位置
                                        ;(DL,DH) = 窗口右下角的(X,Y)位置
                                        ;无返回值:
    mov ax, 0600h
    mov bx, 0700h
    mov cx, 0                           ; 左上角: (0, 0)
    mov dx, 184fh	                    ; 右下角: (80,25),
			                            ; 因为VGA文本模式中,一行只能容纳80个字符,共25行。
			                            ; 下标从0开始,所以0x18=24,0x4f=79
    int 10h                             ; int 10h

                                        ; 输出背景色绿色,前景色红色,并且跳动的字符串"1 MBR"
    mov byte [gs:0x00],'1'
    mov byte [gs:0x01],0xA4             ; A表示绿色背景闪烁,4表示前景色为红色

    mov byte [gs:0x02],' '
    mov byte [gs:0x03],0xA4

    mov byte [gs:0x04],'M'
    mov byte [gs:0x05],0xA4   

    mov byte [gs:0x06],'B'
    mov byte [gs:0x07],0xA4

    mov byte [gs:0x08],'R'
    mov byte [gs:0x09],0xA4

    jmp $		                        ; 通过死循环使程序悬停在此

    times 510-($-$$) db 0
    db 0x55,0xaa


        当然,这并没有完,只是换了一个方式重新打印了MBR字符而已;下面我们要让MBR做一点实事了;即加载操作系统的loader(因为MBR程序被锁死了只有510字节,无法完成太多功能),为进入保护模式做准备;

        简单地说,我们要用MBR这个加载器,去加载另一个加载器;另一个加载器(即loader)的功能是加载操作系统;

        如何实现呢?我们需要在实模式下,用in 和out 这两个指令与磁盘的一些寄存器进行交互;

这里放一下书上的磁盘IO的寄存器表

  在这里插入图片描述

 

操作磁盘的方式方法:

        在这里插入图片描述

 

MBR程序再编写

1、include boot.inc ,这里面定义了loader在磁盘中的位置(磁盘2号扇区)和loader加载进内存后的位置(0x900)

2、vstart=0x7c00,告诉编译器将这段代码放到0x7c00

3、按照上面操作磁盘的方法将磁盘数据取出放到指定内存区域

4、跳转到loader位置

5、填充剩下的0,定义结尾0x55和0xaa

                                    ;-------------	 loader和kernel   ----------
LOADER_BASE_ADDR equ 0x900 
LOADER_START_SECTOR equ 0x2
                                    ;主引导程序 
                                    ;------------------------------------------------------------
%include "boot.inc"
SECTION MBR vstart=0x7c00         
    mov ax,cs      
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov fs,ax
    mov sp,0x7c00
    mov ax,0xb800
    mov gs,ax

                                    ; 清屏
                                    ;利用0x06号功能,上卷全部行,则可清屏。
                                    ; -----------------------------------------------------------
                                    ;INT 0x10   功能号:0x06	   功能描述:上卷窗口
                                    ;------------------------------------------------------
                                    ;输入:
                                    ;AH 功能号= 0x06
                                    ;AL = 上卷的行数(如果为0,表示全部)
                                    ;BH = 上卷行属性
                                    ;(CL,CH) = 窗口左上角的(X,Y)位置
                                    ;(DL,DH) = 窗口右下角的(X,Y)位置
                                    ;无返回值:
    mov ax, 0600h
    mov bx, 0700h
    mov cx, 0                       ; 左上角: (0, 0)
    mov dx, 184fh		            ; 右下角: (80,25),
				                    ; 因为VGA文本模式中,一行只能容纳80个字符,共25行。
				                    ; 下标从0开始,所以0x18=24,0x4f=79
    int 10h                         ; int 10h

                                    ; 输出字符串:MBR
    mov byte [gs:0x00],'1'
    mov byte [gs:0x01],0xA4

    mov byte [gs:0x02],' '
    mov byte [gs:0x03],0xA4

    mov byte [gs:0x04],'M'
    mov byte [gs:0x05],0xA4	        ;A表示绿色背景闪烁,4表示前景色为红色

    mov byte [gs:0x06],'B'
    mov byte [gs:0x07],0xA4

    mov byte [gs:0x08],'R'
    mov byte [gs:0x09],0xA4
	 
    mov eax,LOADER_START_SECTOR	    ; 起始扇区lba地址
    mov bx,LOADER_BASE_ADDR         ; 写入的地址
    mov cx,1			            ; 待读入的扇区数
    call rd_disk_m_16		        ; 以下读取程序的起始部分(一个扇区)
  
    jmp LOADER_BASE_ADDR
       
                                    ;-------------------------------------------------------------------------------
                                    ;功能:读取硬盘n个扇区
rd_disk_m_16:	   
                                    ;-------------------------------------------------------------------------------
				                    ; eax=LBA扇区号
				                    ; ebx=将数据写入的内存地址
				                    ; ecx=读入的扇区数
    mov esi,eax	                    ;备份eax
    mov di,cx		                ;备份cx
                                    ;读写硬盘:
                                    ;第1步:选择特定通道的寄存器,设置要读取的扇区数
    mov dx,0x1f2
    mov al,cl
    out dx,al                       ;读取的扇区数

    mov eax,esi	                    ;恢复ax

                                    ;第2步:在特定通道寄存器中放入要读取扇区的地址,将LBA地址存入0x1f3 ~ 0x1f6
                                    ;LBA地址7~0位写入端口0x1f3
    mov dx,0x1f3                       
    out dx,al                          

                                    ;LBA地址15~8位写入端口0x1f4
    mov cl,8
    shr eax,cl
    mov dx,0x1f4
    out dx,al

                                    ;LBA地址23~16位写入端口0x1f5
    shr eax,cl
    mov dx,0x1f5
    out dx,al

    shr eax,cl
    and al,0x0f	                    ;lba第24~27位
    or al,0xe0	                    ; 设置7~4位为1110,表示lba模式
    mov dx,0x1f6
    out dx,al

                                    ;第3步:向0x1f7端口写入读命令,0x20 
    mov dx,0x1f7
    mov al,0x20                        
    out dx,al

                                    ;第4步:检测硬盘状态
.not_ready:
                                    ;同一端口,写时表示写入命令字,读时表示读入硬盘状态
    nop
    in al,dx
    and al,0x88	                    ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙
    cmp al,0x08
    jnz .not_ready	                ;若未准备好,继续等。

                                    ;第5步:从0x1f0端口读数据
    mov ax, di                      ;di当中存储的是要读取的扇区数
    mov dx, 256                     ;每个扇区512字节,一次读取两个字节,所以一个扇区就要读取256次,与扇区数相乘,就等得到总读取次数
    mul dx                          ;8位乘法与16位乘法知识查看书p133,注意:16位乘法会改变dx的值!!!!
    mov cx, ax	                    ; 得到了要读取的总次数,然后将这个数字放入cx中
    mov dx, 0x1f0
.go_on_read:
    in ax,dx
    mov [bx],ax
    add bx,2		  
    loop .go_on_read
    ret

    times 510-($-$$) db 0
    db 0x55,0xaa

Loader程序编写检验MBR功能

        然后我们随便写个loader检验MBR是否能成功运行

        我就直接复制粘贴后面的loader打印字符程序了

%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR

                                    ; 输出背景色绿色,前景色红色,并且跳动的字符串"1 MBR"
mov byte [gs:0x00],'2'
mov byte [gs:0x01],0xA4             ; A表示绿色背景闪烁,4表示前景色为红色

mov byte [gs:0x02],' '
mov byte [gs:0x03],0xA4

mov byte [gs:0x04],'L'
mov byte [gs:0x05],0xA4   

mov byte [gs:0x06],'O'
mov byte [gs:0x07],0xA4

mov byte [gs:0x08],'A'
mov byte [gs:0x09],0xA4

mov byte [gs:0x0a],'D'
mov byte [gs:0x0b],0xA4

mov byte [gs:0x0c],'E'
mov byte [gs:0x0d],0xA4

mov byte [gs:0x0e],'R'
mov byte [gs:0x0f],0xA4

jmp $		                        ; 通过死循环使程序悬停在此



编译运行

        用nasm编译我们的MBR程序和Loader程序

        nasm -o mbr mbr.s -I include/

        在include文件夹中放入我们的boot.inc

        用dd命令将mbr写入磁盘0号分区,loader写入2号分区

        dd if=loader of=/bochs/hd60M.img seek=2 bs=512 count=1 conv=notrunc

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【算法|数组】双指针

算法|数组——双指针 引入 给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。 示例 1: 输入:nums [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:…

77. 组合

题目链接&#xff1a;力扣 解题思路&#xff1a; AC代码 class Solution {List<Integer> tem new ArrayList<>();List<List<Integer>> result new ArrayList<>();public List<List<Integer>> combine(int n, int k) {process(n…

重试框架入门:Spring-RetryGuava-Retry

前言 在日常工作中&#xff0c;随着业务日渐庞大&#xff0c;不可避免的涉及到调用远程服务&#xff0c;但是远程服务的健壮性和网络稳定性都是不可控因素&#xff0c;因此&#xff0c;我们需要考虑合适的重试机制去处理这些问题&#xff0c;最基础的方式就是手动重试&#xf…

Vue3:页面A搜索后跳转到页面B,然后从页面B退回页面A重新搜索,但是得到的页面B得刷新一下才会显示正确的数据

问题 Vue3&#xff1a; 从页面A进行搜索后跳转到页面B&#xff0c;然后从页面B退回页面A重新搜索&#xff0c;但是得到的页面B得刷新一下才会显示正确的数据。 读取数据的代码格式大致如下&#xff08;代码做了一些删减&#xff09;&#xff1a; 解决 会出现上述情况&#…

DIY 一个 Docker Base Image

1. 我们先使用C语言写一个hello-world程序 vim hello.c # include <stdio.h>int main() {print("hello docker\n"); } 2. 将hello.c文件编译成二进制文件, 需要安装工具 yum install gcc yum install glibc-static 开始编译 gcc -static hello.c -o hello 编译…

低代码平台“高”效率开发的5个能力

迫于智改数转的压力&#xff0c;促使企业要不停地思考如何从低代码的角度释放一些重复枯燥又高成本的人力投入。历经多次重大重构及大量项目实战验证之后&#xff0c;个人认为一款高效率的低代码开发平台&#xff0c;应有以下几点能力。 一、低代码平台“高”效率开发的5个能力…

ES索引重建reindex详解

目录 一、使用场景 二、reindex介绍 三、使用手册 1、覆盖更新 2、创建丢失的文档并更新旧版本的文档 3、仅创建丢失的文档 4、冲突处理 5、source中添加查询条件 6、source中包含多个源索引 7、限制处理的记录数 8、从远程ES集群中重建索引 9、提取随机子集 10、…

Java实现轻量型Web服务器接收http协议提交的RFID读卡信息

示例使用的读卡器&#xff1a;RFID网络WIFI无线TCP/UDP/HTTP可编程二次开发读卡器POE供电语音-淘宝网 (taobao.com) import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSock…

Java-day07(面向对象-3,4)

继承 将多个类中共有的属性&#xff0c;方法剥离出来单独创建一个共有类&#xff0c;当需要使用共有的属性与方法时&#xff0c;就可以通过继承(extends)来调用共有的属性与方法。通过"class A extends B" 来实现类的继承&#xff08;子类&#xff1a;A 父类&#x…

手把手教你 使用SpringBoot 实现业务数据动态脱敏

文章目录 什么是数据脱敏静态数据脱敏动态数据脱敏 需求实现1. 切面AOP实现脱敏是否脱敏注解定义切入点测试单条记录结果多条记录结果分页记录结果 2. 自定义注解和自定义消息转换器实现数据脱敏自定义DataDesensitization注解定义脱敏类型枚举实现AnnotationFormatterFactory接…

SAP度量单位转换功能

针对今天N2项目提出业务痛点&#xff1a;物料30011110的基本单位是KG&#xff0c;在XXX的BOM里单位是G&#xff0c;由于物料没配单位转换关系&#xff0c;但系统又能正常进行转换&#xff0c;开发需要技术支持。 经专项调查&#xff0c;G和KG的转换是SAP相同量纲转换标准功能&…

econml双机器学习实现连续干预和预测

连续干预 在这个示例中&#xff0c;我们使用LinearDML模型&#xff0c;使用随机森林回归模型来估计因果效应。我们首先模拟数据&#xff0c;然后模型&#xff0c;并使用方法来effect创建不同干预值下的效应&#xff08;Conditional Average Treatment Effect&#xff0c;CATE&…

现在转行搞嵌入式找工作难不难啊?

对于应届生来说&#xff0c;嵌入式开发的经验不会有太多&#xff0c;所以要求也不会太高。 嵌入式开发常用的是C语言&#xff0c;所以需要你有扎实的功底&#xff0c;这一点很重要&#xff0c;数据结构算法&#xff0c;指针&#xff0c;函数&#xff0c;网络编程 有了上面的基…

微服务——数据同步

问题分析 mysql和redis之间有数据同步问题&#xff0c;ES和mysql之间也有数据同步问题。 单体项目可以在crud时就直接去修改&#xff0c;但在微服务里面不同的服务不行。 方案一 方案二 方案三 总结 导入酒店管理项目 倒入完成功启动后可以看见数据成功获取到了 声明队列和…

word横向页面侧面页码设置及转pdf后横线变竖线的解决方案

在处理材料的时候&#xff0c;会遇到同一个文档里自某一页开始&#xff0c;页面布局是横向的&#xff0c;这时候页码要设置在侧面&#xff0c;方法是双击页脚&#xff0c;然后在word工具栏上选择“插入”——>“文本框”——>“绘制竖版文本框”&#xff0c;然后在页面左…

Air001基于Arduino点灯实验

Air001基于Arduino点灯实验 &#x1f449;&#x1f3fb;Arduino平台环境搭建可以参考&#xff1a;https://wiki.luatos.com/chips/air001/Air001-Arduino.html &#x1f516;使用国内的json&#xff0c;安装不仅成功率高还非常的快速。&#xff08;当然取决于个人网络环境&…

Windows安装Redis

自己电脑做个测试&#xff0c;需要用到Redis&#xff0c;把安装过程记录下&#xff0c;方便有需要的人 1、找到下载地址&#xff1a;Releases microsoftarchive/redis GitHub Windows的Redis需要到GitHub上下载&#xff1a; 2、下载完后设置密码&#xff0c;打开文件夹&…

RemObjects Suite Subscription for Delphi Crack

RemObjects Suite Subscription for Delphi Crack Delphi的RemObjects套件订阅提供了一种方便且经济高效的方式&#xff0c;可以随时了解所有RemObjects Delphi产品的最新情况。它允许您访问所有当前和即将推出的产品&#xff0c;而不必担心错过重要的更新或新产品。当您的平台…

医院智慧运营管理数字化方案

近年来&#xff0c;随着医院的发展规模不断扩大&#xff0c;其人、财、物、技术等资源的配置活动愈加复杂&#xff0c;资产管理、成本管理、收支核算管理、资金预算管理、绩效管理等经济活动日常管理也日益繁重&#xff0c;医院对于数字化提升精细化管理的需求也愈发迫切。 国…

块、行内块水平垂直居中

1.定位实现水平垂直居中 <div class"outer"><div class"test inner1">定位实现水平垂直居中</div></div><style>.outer {width: 300px;height: 300px;border: 1px solid gray;margin: 100px auto 0;position: relative;}.te…