操作系统从0到1-----MBR的完善(简易操作显卡/硬盘)

本文详细介绍了MBR的完善过程及Loader的工作原理,包括地址、section、vstart等概念,探讨了实模式下的寄存器作用及硬盘读写操作,最后展示了如何利用MBR加载Loader并实现基本的打印功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MBR 完善

声明:因个人能力有限,本文仅是个人的学习记录笔记,有错误之处还望指出

地址

地址只是一个数字,用与记录各种描述符号在源文件中的位置,是偏移文件开头的距离。
第 n 个数据所在的位置是数据 n-1 的偏移 + n-1 的内存空间。

section

section 被称为节,是编译器提供的关键字,用于在程序中宣称一个区域。通过此关键字可以人为的将程序中的数据,指令放在不同的 section 中,使代码的结构更加明了。

用法

section.节名称(data\code).start

关键字 section 本身对于程序的地址没有任何影响,section 中的地址是整个文件的顺延续,仅仅用于逻辑上对开发人员梳理使用

vstart

vstart 是虚拟起始地址,为 section 内的数据指定一个虚拟起始地址,告诉编译器一新的地址作为后面数据的起始值,它本身并没有改变数据本身在文件中的地址,通过加载器才会将数据加载到 vstart 指定的位置。

编译器只负责为程序编址,并不负责加载,加载由 BISO 加载到对于的物理地址

CPU的实模式

实模式下的寄存器

通用寄存器

寄存器助记名称描述
ax累加器(accumulator)使用频率最高,算术运算,逻辑运算,保存和外设输入\输出数据
bx基址寄存器(base)存储内存地址,用此地址作为基址,用于遍历一片内存区域
cx计数器(counter)计数,循环指令中控制循环次数
dx数据寄存器(data)存放数据,通常之用于保存外设控制器的端口地址
si源变址寄存器(source index )常用于字符串操作中的数据源地址,即被传送的数据在哪里
di目的变址寄存器(destination index)和 si 一样,用于字符串操作,但是 di 是表示数据被传说到哪里
sp栈指针寄存器(stack pointer)其段基地址是 SS ,用来指向栈顶,随着栈中的数据 push 和 pop 会修改sp的值
bp基址指针(base pointer)两种访问栈的模式,一种是用 push 和 pop 指令操作栈,这个时候sp 指针的值会自动更新,但是我们只能获取栈顶指针 sp 指向的数据。 另一种是把栈当成数据段来访问,通过 bp 寄存器,bp 的默认段寄存器是 SS ,通过 SS:bp 的方式当成普通数据段来访问

显示器操作

IO接口

因为 CPU 与外设的速度不匹配,信号不同等原因,在 CPU 与外设之间 添加了一层 I/O 接口的设计方便了外设与 CPU 之间的联系

I/O接口功能

  1. 设置速度缓冲,解决 CPU 与外设之间速度不匹配的问题
  2. 设置电平转换信号
  3. 设置数据格式转换
  4. 设置时序控制电路来同步 CPU 和外设
  5. 提供地址译码

端口操作指令

  • 从端口读数据(in)

    1. in al,dx
    2. in ax,dx

    al (8位) 和 ax (16位)用于存储从端口获取的数据,dx 是端口号

  • 写数据到端口(out)

    1. out dx,al
    2. out dx,ax
    3. out 立即数,al
    4. out 立即数,ax

显卡

显存的地址分布

起始结束大小用途
C0000C7FFF32K显示适配器BIOS
B8000BFFFF32K文本模式
B0000B7FFF32K黑白模式
A0000AFFFF32K彩色模式

字符属性

每个在屏幕上显示的字符都占用两个字节,低字节是用于字符的 ASCII ,高字节是用于字符的属性信息 ;

高字节的低四位是字符的前景色(IRGB),高四位是字符的背景色(KRGB),I :控制是否高亮,K :控制是否闪烁

故可以通过RGB的组合来达到控制显示的字符颜色的功能(000:黑;001:蓝; …)

直接操作显卡

  • 对显卡的文本模式的地址进行操作,写入 “MY OS is Loading”

  • 结果图

在这里插入图片描述

图象闪烁显示,此处只截取了显示字符的时候的截图

硬盘介绍

硬盘控制器主要端口寄存器

IO 端口端口用途
Primary 通道Secondary 通道读操作写操作
Command Block registers(命令)
0x1F00x170DataData
0x1F10x171ErrorFeatures
0x1F20x172Sector countSector count
0x1F30x173LBA lowLBA low
0x1F40x174LBA midLBA mid
0x1F50x175LBA highLBA high
0x1F60x176Devicedevice
0x1F70x177StatusCommand
Control Block registers(控制)
0x3F60x376Alternate statusDevice Control
  • 硬盘读写
    • CHS mode: Cylinder / Head /Sector (柱面/磁头/扇区)—>对于磁头比较直观,但对于用户不直观
    • LBA mode: Logical Block Address(more convenience)—>对于用户更加直观
      • LBA28:用28bit来描述一个扇区的地址,最大寻址范围:2^28 = 268435456 个扇区,每个扇区大小为 512 Byte,故最大支持 128 GB
      • LBA48: 用48bit来描述一个扇区的地址,最大寻址范围:2^48 = 281474976710656个 扇区,最大支持 131072TB
  • LBA 寄存器
    • LBA low :存储 28 位地址的第 0~7 位
    • LBA mid :存储 28 位地址的第 8~15 位
    • LBA high :存储 28 位地址的第 16~23 位
      剩下的 24~27 位存储在 device 寄存器的低四位
  • device 寄存器

    这是一个杂项寄存器,不仅用于存储 LBA 的 24~27 位 并且存储通道上的主/从盘,启用 LBA 方式 …

    • 0~3: LBA 地址的 24~27 位
    • 4:指定通道上的主盘(0)/从盘(1)
    • 6:启用LBA 模式 LBA (1)/CHS(0)
    • 5和7:固定为 1 ,称为 MBS 位
  • status 寄存器

    用于给出硬盘的状态信息

    • 0:ERR位,置 1 表示命令出错
    • 3:DRQ位 ,置 1 表示准备好数据,随时可以输出
    • 6:DRDY位 ,置 1 表示设备就绪,等待指令
    • 7:BSY位 ,置 1 表示硬盘正忙
  • Command 寄存器

    让硬盘执行命令

    • identify: 0xEC,硬盘识别
    • read sector: 0x20,读扇区
    • write sector: 0x30,写扇区

常用硬盘操作

读写步骤

  1. 选择通道,往该通道的 sector count 寄存器中写入待操作的扇区数
  2. 往该通道上的三个 LBA 寄存器写入扇区起始地址的低 24 位
  3. 往 device 寄存器中写入 LBA 地址的第 24~27 位,并置第 6 位为 1 ,使其为 LBA 模式,设置第 4 位,选择操作的硬盘(master/slave)
  4. 往该通道上的 command 寄存器写入操作命令
  5. 读取该通道上的 status 寄存器,判断硬盘工作是否完成
  6. 如果是执行读硬盘,则执行下一步骤,否则结束
  7. 将硬盘数据读出

数据传输方式

  1. 无条件传送方式
  2. 查询传送方式
  3. 中断传送方式
  4. 直接存储器处理方式(DMA)
  5. I/O 处理机传送方式

利用 MBR 使用硬盘

负责从硬盘上把 loader 加载到内存,并将管理权交给 loader

代码详解

  • %include :nasm编译器的预处理指令<===> 同 c 中的 #include
  • LOADER_BASE_ADDR equ 0x900 宏定义,loader 会在内存地址 0x900 处 ; 宏名 equ 值 <===> c 中的#define 宏名 值
  • LOADER_START_SECTOR equ 0x2 宏定义,loader 放在第二个扇区
  • rd_disk_m_16(在16位模式下读硬盘) 函数详解
    • 先通过 eax,bx,cx 三个寄存器传递函数所需的参数表示:扇区开始处,扇区地址,扇区的个数

    • call ; 指令是对函数的调用 <===> ret 是函数的返回

    • esi 和 di ; 分别暂存扇区开始处和扇区数

    • mov dx,0x1f2 ; 选择通道

    • mov al,cl ; cl是cx的低八位,保存扇区数

    • out dx,al ; 往 sector count 写入扇区数

    • mov eax,esi ; 恢复 ax

    • mov dx,0x1f3 ; 操作端口 0x1f3(LBA low)

    • out dx,al ; al 是eax的低8位存入地址信息,写入端口

    • mov cl,8

    • shr eax,cl ; 逻辑右移8位(低 8 位的地址信息已经被写入寄存器,此处是写入8~15位地址信息),将8~15位的数据移到低 8 位

    • mov dx,0x1f4 ; 操作端口 0x1f4(LBA mid)

    • out dx,al

    • shr eax,cl ; 再次逻辑右移 8位,

    • mov dx,0x1f5 ; 操作端口 0x1f5(LBA high)写入16~23位地址信息

    • out dx,al

    • shr eax,cl ; 再次逻辑右移 8位,地址信息 24~27位 移动至 0~3 位

    • and al,0x0f ; 01111 | LBA的 24~27 位信息,===> 24~27 位信息存入al

    • or al,0xe0 ; 设置7~4 位为 1110,表示 LBA 模式

    • mov dx,0x1f6 ; 操作端口 0x1f6(device)写入 LBA 模式和 LBA 的 24~27 地址信息

    • out dx,al

    • mov dx,0x1f7 ; 操作端口 0x1f7(command端口)写入读扇区命令

    • mov al,0x20

    • out dx,al

内核加载器

  • 写内核加载器 loader
  • 将loader 写入硬盘
  • 在主引导扇区读入
  • 检测正确性
  • 跳转到 loader 执行

实模式下print函数

mov si,booting  ; 参数 si (打印的字符串)
call print  ; 调用 print
print:
    mov ah,0x0e    ; ah 为0xe al为字符 int 0x10 打印字符
.next:      ; 逐个打印字符
.next:
    mov al,[si]
    cmp al,0
    jz .done  ; 字符串没数字时结束
    int 0x10  ; 调用中断
    inc si  ; 自增
    jmp .next

.done :
  ret ; 字符串
; 填充打印的字符串
booting:
  db "Booting to Os...",10,13,0 ; \n\r《===》10和13(ASCII码)
  • 结果图

通过 MBR 执行到 loader,并且输出字符

在这里插入图片描述

实模式的内存布局

起始地址结束地址大小用途
0x0000x3ff1KB中断向量表
0x4000x4FF256BBIOS 数据区
0x5000x7BFF29.75KB可用区域
0x7C000x7DFF512BMBR 加载区域
0x7E000x9FBFF607.6KB可用区域
0x9FC000x9FFFF1KB扩展 BIOS 数据区
0xA00000xAFFFF64KB彩色显示适配器
0xB00000xB7FFF32KB黑白显示适配器
0xB80000xBFFFF32KB文本显示适配器
0xC00000xC7FFF32KB显示适配器 BIOS
0xC80000xEFFFF160KB映射内存
0xF00000xFFFEF64KB-16B系统 BIOS
0xFFFF00xFFFFF16B系统 BIOS 入口
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值