第3章 完善MBR
-
编译器的工作就是给各个符号进行编址,地址通常是相对于基址的偏移量
-
地址 = 上一个内容的基址 + 上一个内容的大小
-
section是伪指令,cpu不执行,起到划分代码逻辑的作用
-
vstart的作用是为section内的数据指定一个虚拟的起始地址,具体物理地址由程序加载器决定
-
vstrat和org功能相同,告诉编译器后面数据的起始地址,但不改变内部数据的偏移地址
-
实模式是指8086CPU工作的配套环境,包含了CPU
-
CPU的组成和功能
- 控制单元:CPU将要做的事情
- 存储单元:存储待处理的数据,即指令中的操作数
- 运算单元:负责算数和逻辑运算
-
高速缓存程序员不可见
-
缓存解决了速度不匹配缓存之间的数据传输问题
-
操作系统初次访问内存的地址,使用的是程序初始化的寄存器的值
-
段基址寄存器:指定了内存中一段连续区域的起始地址
-
代码段:指令连续排放在一起的内存区域,使用CS寄存器指定其初始地址
-
数据段:数据(指令操作数)排放在一起的内存区域,使用DS寄存器指定其初始地址
-
flags寄存器表示指令存储过程中的细节和影响
-
通常,cx寄存器用作循环的次数控制,bx寄存器用于存储起始地址
-
通用寄存器的介绍
寄存器 助记名 功能描述 ax 累加器accumulator 使用频度最高,常用于算数逻辑运算和外设的输入输出数据 bx 基址寄存器base 常用来存储一片连续内存区域的基址 cx 计数器counter 计数,常用于循环指令的循环次数 dx 数据寄存器data 常用于保存外设控制器的端口号地址,也可用于存储数据 si 源变址寄存器source index 常用于存储字符串操作中的数据源地址 di 目的变址寄存器
destination index常用于存储字符串操作中的数据目的地址 sp 栈指针寄存器
stack pointer用于指向栈顶,push和pop两个对栈操作的指令会修改sp的值 bp 基址指针base pointer 将栈中的数据当成普通数据进行读取 -
8086开始支持了段的访问基址,大大加强了灵活性
-
mov [bx],0x5
表示将立即数0x5存入ds : bx指向的内存 -
段基址*16 + 偏移地址 的寻址方式将16位寄存器拓展成20位寻址方式,如果超出了20位最大寻址空间0xFFFFF,则称为高端内存区HMA,不使用和处理
-
寻址方式
- 寄存器寻址:指令读写操作数都在寄存器中
- 立即数寻址:立即数常用于进行赋初值
- 内存寻址
- 直接寻址:将直接在操作数中给出的数字作为内存地址
- 基址寻址
- 变址寻址
- 基址变址寻址
-
栈的访问方式
- ss : sp:按照栈的访问规则进行访问,sp始终指向栈顶
- ss : bp:按照数据段的访问规则进行访问
- 调用call指令时,会在栈中保留IP寄存器的值。如果是跨段访问则CS和IP的值都要保留
-
ret指令:将当前栈顶内容弹出,并用它为IP寄存器赋值
-
retf ( return far )指令:是从栈顶取得4 字节,栈顶处的2 字节用来替换IP寄存器,另外的2 字节用来替换CS寄存器
-
jmp是直接跳转,call保留了当前跳转的信息,可以调用ret返回
-
I/O接口功能:
- 设置数据缓冲,解决CPU和外设速度不匹配
- 设置信号的电平转换电路,CPU只能使用TTl电平
- 设置数据格式转换,CPU只能处理数字信号
- 设置时序控制电路来同步CPU和外部设备
- 提供地址译码:只能有一个端口和CPU 数据交换,这就需要IO 接口提供地址译码电路,使CPU 可以选中某个端口,使其可以访问数据总线
-
北桥用于连接高速设备,如内存,现在一般集成到cpu中
南桥通常用于连接低俗I/O设备 -
I/O接口通过寄存器的方式同CPU通信,这些寄存器称为端口
-
IA32 体系系统中,用于存储端口号的寄存器是16 位的,所以最大有65536 个端口,即0~65535
-
显卡的工作是不断读取显存的内容并发送到显示器上
-
直接操作显卡的MBR
; 主引导程序MBR
; SECTION是伪指令,cpu不运行,只是方便程序员规划程序分段使用
; `vstart=0x7c00`表示在程序编译时将起始地址编译为0x7c00
; SS存放栈顶的段地址,SP存放栈顶的偏移地址。在任何时刻 ,SS:SP都是指向栈顶元素
; CS存放内存中代码段入口的段基址,CS:IP表示下一条要运行的指令内存地址
; 初始化部分
SECTION MBR vstart=0x7c00 ; =前后不能有空格
mov ax,cs ; 由于BIOS是通过`jmp 0:Ox7c00`转到MBR的,故cs此时为0
mov ds,ax ; 段寄存器不能使用立即数进行赋值,可以使用通用寄存器ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
mov ax,0xb800
mov gs,ax
; 使用10号中断的0x06功能号,进行窗口上卷清屏,避免BIOS检测信息影响显示
mov ax,0600h ; ah存放将要调用的中断子功能号
mov bx,0700h
mov cx,0 ; (CL,CH)=窗口左上角的(X,Y)位置
mov dx,184fh ; (DL,DH)=窗口右下角的(X,Y)位置(80,25)
int 10h ; 调用中断
; 输出背景色是绿色,前景色是红色,并且跳动的字符串为“1 MBR”
mov byte [gs:0x00],'1' ;把字符1的ASCII写入以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
; $表示本行指令所在的地址,$$表示本section的起始地址,$-$$表示执行代码行到段首的偏移量
jmp $ ; 在本行代码死循环
times 510-($-$$) db 0 ; 将剩余字节用0进行填充
db 0x55,0xaa ; 最后两个字节填充MBR的标识
- 现在通常使用LBA48(Logical Block Address),即使用48位比特描述一个扇区地址,最大寻址范围是 $2^{48} $bit = 128PB
- 数据传送方式
- 无条件传送方式:数据随时为CPU的读取准备好,eg:寄存器,内存
- 查询传送方式:需要先获取硬盘工作状态,判断后在进行读取操作,常用于低速设备
- 中断传送方式:数据源设备准备好数据后通知CPU来取
- DMA工作方式:数据原设备和内存可以直接进行大数据传输
- I/O处理机传送方式:专用于数据处理的中央处理器,完全解放CPU
; 主引导程序MBR
; SECTION是伪指令,cpu不运行,只是方便程序员规划程序分段使用
; `vstart=0x7c00`表示在程序编译时将起始地址编译为0x7c00
; SS存放栈顶的段地址,SP存放栈顶的偏移地址。在任何时刻 ,SS:SP都是指向栈顶元素
; CS存放内存中代码段入口的段基址,CS:IP表示下一条要运行的指令内存地址
; 初始化部分
%include "boot.inc"
SECTION MBR vstart=0x7c00 ; =前后不能有空格
mov ax,cs ; 由于BIOS是通过`jmp 0:Ox7c00`转到MBR的,故cs此时为0
mov ds,ax ; 段寄存器不能使用立即数进行赋值,可以使用通用寄存器ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
mov ax,0xb800
mov gs,ax
; 使用10号中断的0x06功能号,进行窗口上卷清屏,避免BIOS检测信息影响显示
mov ax,0600h ; ah存放将要调用的中断子功能号
mov bx,0700h
mov cx,0 ; (CL,CH)=窗口左上角的(X,Y)位置
mov dx,184fh ; (DL,DH)=窗口右下角的(X,Y)位置(80,25)
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
; 硬盘扇区的写入
mov eax,LOADER_START_SECTOR ; 起始扇区lba地址
mov bx,LOADER_BASE_ADDR ; 写入的地址
mov cx,1 ; 待读入的扇区数
call rd_disk_m_16 ; 以下读取程序的起始部分(一个扇区)
jmp LOADER_BASE_ADDR
; 功能:读取硬盘的n个扇区,在16位模式下
rd_disk_m_16:
mov esi,eax ; 备份eax
mov di,cx ; 备份cx
; 读写硬盘
; 第一步:设置要读取的扇区数
mov dx,0x1f2 ; 存储端口号
mov al,cl
out dx,al ; 读取的扇区数(out指令用于向端口写数据)
mov eax,esi ; 恢复ax
; 第二步:将LBA地址存入端口0x1f3 ~ 0x1f6
; LBA地址的0~7位写入端口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 ; lab第24~27位
or al,0xe0 ; 设置7-4位为1110,表示lba模式
mov dx,0x1f6
out dx,al
; 第三步:向0x1f7端口写入读命令0x20
mov dx,0x1f7
mov al,0x20
out dx,al
; 第四步:检测硬盘状态
.not_ready: ; 同一端口写时表示写入命令字,读时表示读入硬盘状态
nop
in al,dx
and al,0x88 ; 第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙
cmp al,0x08
jnz .not_ready ; 若未准备好,继续等待
; 第5步:从0x1f0端口读数据
; di为要读取的扇区数,一个扇区有512字节,每读取一个字,共需di*512/2次
mov ax,di
mov dx,256
mul dx
mov cx,ax
mov dx,0x1f0
.go_on_read:
in ax,dx
mov [bx],ax
add bx,2
loop .go_on_read
ret
; $表示本行指令所在的地址,$$表示本section的起始地址,$-$$表示执行代码行到段首的偏移量
times 510-($-$$) db 0 ; 将剩余字节用0进行填充
db 0x55,0xaa ; 最后两个字节填充MBR的标识
; 内核加载器loader
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
; 输出背景色是绿色,前景色是红色,并且跳动的字符串为“2 LOADER”
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],'D'
mov byte [gs:0x09],0xA4
mov byte [gs:0x0a],'E'
mov byte [gs:0x0b],0xA4
mov byte [gs:0x0c],'R'
mov byte [gs:0x0d],0xA4
jmp $ ; 通过死循环使程序悬停在此
; 在bochs/include目录下的文件boot.inc,是自己编写的头文件
LOADER_BASE_ADDR equ Ox900
LOADER_START_SECTOR equ Ox2
脚本文件:
#!/bin/bash
rm -rf ./hd.img
bin/bximage -hd -mode="flat" -size=60 -q hd.img
echo "disk creat success!!"
nasm -I include/ -o mbr.bin mbr.s
dd if=mbr.bin of=hd.img bs=512 count=1 conv=notrunc
echo "disk write success!!"
nasm -I include/ -o loader.bin loader.s
dd if=loader.bin of=hd.img bs=512 count=1 seek=2 conv=notrunc
bin/bochs -f bochsrc
成功截图:
!!"
nasm -I include/ -o mbr.bin mbr.s
dd if=mbr.bin of=hd.img bs=512 count=1 conv=notrunc
echo “disk write success!!”
nasm -I include/ -o loader.bin loader.s
dd if=loader.bin of=hd.img bs=512 count=1 seek=2 conv=notrunc
bin/bochs -f bochsrc
成功截图:
[外链图片转存中...(img-lREavuw2-1644923609430)]