一、开机进入实模式--BIOS->MBR(硬盘扇区复制函数)->LOADER

说明:记录的大部分是我掌握不熟悉的内容,所以知识点不是那么全面。

实模式

实模式是指8086cpu的cpu工作原理、内存分段方式、寻址方式、寄存器大小、指令用法等。
cpu工作原理:借助L1,L2这些SRAM缓存器来工作。。。
内存分段方式:实模式下的地址是真实的物理地址,地址线20根,内存1M,所以“段基址:段内偏移地址”=将段基址左移一位加上段内偏移地址记得到真实的物理地址
寄存器大小:实模式下面都是16位宽的,2的16次方为64K,8086是20根地址线,内存大小为2的20次方为1M,cpu的cs和ip都是16位的,只能表示访问到内存的64k处,所以访问内存形式是  段基址:段内偏移地址  来访问内存。将段基址乘以16,左移4位后加上段内偏移地址来访问内存就能够访问到1M大小了。
寻址方式:寄存器寻址、立即数寻址、直接寻址、基址寻址、变址寻址、基址变址寻址等等。ds:bx  ss:bp  ds:si/di
指令用法:ip不能直接赋值     call   ret   jmp  flags  
jnz :ZF不等于0时候转移,即上一条指令cmp ax,cx ;ax-cx后,当若ZF不等于0,则jmp到后面的指令
jc :CF=1时候转移,即上一条指令操作有进位,则jmp到后面的指令
jge :SF=OF时候转移,即上一条指令:cmp ax,cx ;当ax-cx后,ax大于等于cx时候则jmp到后面的指令
shl ax,1:表示ax乘以2的1次方
xor ax,ax ;表示清空ax
and:可以清0寄存器的值,也可以保留寄存器的原值,所以最常用的就是将32位的寄存器高16位清零,低16位保留原值
or:,可以置1存储器的置,也可以保留寄存器的原值。可以当作加法,最常用的就是一个寄存器的高16位和另一个寄存器的低16位相加
一般mul shl and or  这四个指令组合起来用:乘法之后,高16位低16位不在一个寄存器中,所以要移到一个32位寄存器中,先移位,在清零,再相加
循环:用loop和cmp都可以构成循环,loop的话是cx与0比较,每执行一次循环体后cx-1,然后要执行下一次循环时候判断cx与0的大小(循环条件),若大于0则执行下一次循环,若等于0则终止循环,往下面的代码执行。   cmp是相当于减法操作,但是不改变操作数的大小,只影响flag位(循环条件),可以利用zf来判断时候相等,利用cf来判断是否有进位来设置循环条件。所以loop与cx相当于for,而cmp相当于do ..while
在Pentium及之后的处理器上,比较+条件转移的循环效率通常要比loop高一些,而且loop指令有转移距离8位偏移的限制。不过在循环次数非常多的情况下,loop指令的效率要高一些,因为对loop指令的分支预测成功率是100%(loop指令执行前cx/ecx的值是已知的)。所以我们尽量用cmp拉设置循环

实模式下面的内存布局

    实模式为了兼容8086,所以只支持20条地址线,2的20次方=1MB:0x00000--0xFFFFF。
下图为1M的内存如何布局的:
0--0x9FFFF是DRAM,大小640KB,也就是我们往主板上面插的内存条,特点是漏点快,需要刷新。
0xF0000-0xFFFFF是主板内存ROM,大小64KB,里面存的就是BIOS的代码。
0xA0000-C7FFFF是显卡的区域,包括了显卡的ROM和显存。
0xC8000-0xEFFFF是硬件适配器的ROM或内存映射式IO,每个外设都有自己的内存,包括键盘(8048、8042)、各种控制器(硬盘控制器、中断控制器)等都有自己的内存。
一、开机进入实模式--BIOS->MBR(硬盘扇区复制函数)->LOADER_第1张图片
一、开机进入实模式--BIOS->MBR(硬盘扇区复制函数)->LOADER_第2张图片

BIOS--Base Input & Output System(基本输入输出系统)

    按下BIOS的电源键后,便进入了实模式,首先运行的第一个程序是BIOS。BIOS代码的作用:检测、初始化硬件、建立中断向量表、加载MBR等。BIOS被写进ROM里面,0xF0000-0xFFFFF。开机一瞬间,CPU的 cs:ip 被初始化为0xF000:0xFFF0,段基址乘以16即左移4位,得到0xFFFF0,此地址是BIOS的入口地址,此地址离1M顶端只有16B,所以需要再次跳转,在0xFFFF处有代码 jmp far f000:e05b,跳转到0xfe05b地址处。然后开始检测内存、显卡等外设信息,然后初始化硬件,开始在0x000-0x3FF处建立中断向量表,并填写中断向量信息,然后加载MBR。长度为1024B,可以容纳256个中断向量。

    BIOS如何检测和初始化硬件:刚上面说过了,显卡、键盘、硬盘等这些外设或它的控制器(IO接口)有自己的处理器、内存(ROM和RAM)和寄存器,这些外设出厂时内存的ROM中就写入了自我检测程序、初始化程序、硬件操作访问程序,BIOS找到这些程序的地址直接调用即可。BIOS中建立的中断向量表对应的中断服务程序也是在这些硬件出厂时候就写入ROM的,这些中断程序的主要功能提供了硬件的访问方法,本质就是给出这些硬件的IO端口提供值,用 int+中断号调用后,在cpu的寄存器中提供参数,然后硬件ROM里面的中断程序返回一个结果。如显卡,要在屏幕上面显示时,int 0x10,便开始在中断向量表中找到了显存中对应的程序的入口地址,提供了参数后,CPU执行即可。

    BIOS的最后一项工作便是校验启动盘0盘0道1扇区的内容(001),当发现这个扇区(512B)最后两个字节是0x55和0xaa时候,BIOS便认定这是主引导程序MBR,便开始启动BISO的加载程序将001扇区的内容加载到内存0x07c00处,然后执行跳转指令:jmp 0:0x7c00,cs由0xf000变成了0x0000。(小段字节序 0xaa55)

MBR--Main Boot Record(主引导程序)

    当BIOS将MBR加载到内存地址为0:0x7c00后,cpu便开始在此地址开始运行。MBR位于硬盘的001扇区。MBR里面代码包括446字节的引导程序、64字节的硬盘分区表、2字节的结束标记0x55和0xaa。然后MBR将OBR(操作系统加载程序)加载到内存后跳转到OBR的代码执行,OBR将操作系统的代码加载到内存中。MBR工作在实模式下面。我们写的MBR简单一些,没有包括那么多东西。

IO接口--cpu以此和外设进行通信

   cpu只和IO接口进行数据交换,从来不和外设接触, io接口是cpu与外部设备的逻辑控制部件,起到桥梁的作用。显卡是显示器和cpu的IO接口,声卡是音响和cpu的IO接口,键盘鼠标硬盘都有IO接口。IO接口功能有:1,设置数据缓冲,解决cpu与外设的熟读不匹配问题。cpu处理数据速度很快,但是外设很慢,就可以先把数据存到IO接口的缓冲中,等需要时候传输过去。2,设置信号电平的转换。CPU的信号是TTL,外设一般为电机设备,需要转换信号。3,数据格式转换,数字信号经过D/A转换成模拟信号发送到外设。等等功能。

    cpu通过总线来访问到IO接口。同一时刻,cpu只能和一个IO接口通信。同一时刻有很多请求时候cpu要考虑需要访问哪个IO接口,所以要仲裁模块--I/O control hub(输入输出控制中心--南桥).IO接口中都有一组端口(寄存器),cpu通过操作这些端口来控制访问硬件。有些端口独立编制,从0开始,同一个IO接口的端口号连续。cpu通过in/out指令和IO接口中的端口号通信。

一、开机进入实模式--BIOS->MBR(硬盘扇区复制函数)->LOADER_第3张图片

IO接口中端口的操作方法:
in指令中,端口号只能用dx寄存器
out指令中,可以选用dx寄存器或立即数充当端口号。
一律用ax储存16位数据,al储存8为数据。

显示器与cpu的IO接口--显卡

     当我们在实模式下让屏幕显示东西时候,有两种方法:1,通过BIOS中断,int 0x10来调用显存出厂时在显存ROM写入的操作程序来显示信息 2,可以自己在显存DRAM中写入显示的内容。显卡是PCI设备,PCI总线是并行的,但是有缺陷,现在的显卡是串行的,一般连接在PCIE总线上面,一位一位传输,到时候在将数据组合在一起。(串行频率很快,显卡、硬盘都是串行的)。
    显卡的cpu叫做GPU,显卡的内存叫做显存,显卡的工作就是不断地读取这段内存,随后将内容发送给显示器。ASCII码分为两大类,一类是不可见字符,0x0-0x1F、0x7F,共33个控制字符:退格、回车、换行、删除等,二类是可见字符,0x20-0x7E,共95个可见字符:数字、字母、标点符号。0地ASCII码为48,a地ASCII码为97。

一、开机进入实模式--BIOS->MBR(硬盘扇区复制函数)->LOADER_第4张图片
    我们只用到了0xB8000-0xBFFFF的文本模式,显卡加电后,默认设置的模式为80*25,表示一行80个字符,共25行,2000个字符。每个字符在屏幕上面由2个字节来表示,低字节是字符的ASCII码,高字节是字符属性元信息。这样一屏实际占用了4000字节。
一、开机进入实模式--BIOS->MBR(硬盘扇区复制函数)->LOADER_第5张图片 一、开机进入实模式--BIOS->MBR(硬盘扇区复制函数)->LOADER_第6张图片


硬盘与cpu的IO接口--硬盘控制器

    显卡与显示器是分开的,但是硬盘控制器和硬盘一般是连在一起的。硬盘控制器一般有两种:PATA接口--并行ATA,SATA接口--并行ATA。我们讨论老式的PATA,PATA接口的线缆一般也成为IDE线,一个IDE线一般可以挂两块硬盘,一个主盘,一个从盘。一个主板上支持4块IDE(PATA)硬盘,所以主板一般有两个IDE插槽,叫做IDE0(Primary通道)和IDE1(Secondary通道),即一个主板有两个通道,每个通道上面分别有主盘和从盘。
    端口是位于在IO接口上面的,而不是位于硬盘上面。
一、开机进入实模式--BIOS->MBR(硬盘扇区复制函数)->LOADER_第7张图片
可以注意到:端口是按通道给出的,是针对于硬盘控制器的而不是硬盘。两个通道上面的端口对应的功能相同,只是端口号不同,如0x1F0和0x170功能一样,只是位于不同的通道而已。同一个通道的主从两块硬盘都用这些端口,如Primary通道的主从硬盘都可以用0x1F0这个端口。有些端口的用途在读和写时候还有区别。
data寄存器:16位。读硬盘时,硬盘准备好数据后,硬盘控制器将其放在内部的缓冲区中,不断读此寄存器便可以读出缓冲区中的全部数据。在写硬盘时,cpu把数据快速的输送到此端口中,放在硬盘控制器的缓冲区中,硬盘便可以慢慢的将数据读入相应扇区了。(硬盘控制器有内存当作缓冲区,一般是16-64M)
error和feature寄存器:8位,指的是同一个寄存器,只是在不同的环境下有不同的用途。
Sector count寄存器:8位用来制定带读取或待写入的扇区数。扇区编号有CHS(Cylinder-Head-Sector)和LBA(Logical Block Address)两种。我们使用LBA编号。LBA有LBA28和LBA48两种。后面的数字是描述这么多位的比特来表示一个扇区的地址。28位比特是128GB,我们采取的是LBA28。
LBAlow、LBAmid、LBAhigh:8位,共8*3=24,剩下的4位在device寄存器中。
Device寄存器:8位,第4位来描述这个硬盘是主盘还是从盘,0是主盘。
一、开机进入实模式--BIOS->MBR(硬盘扇区复制函数)->LOADER_第8张图片
Status寄存器:8位。在读硬盘,用来给出硬盘的状态信息。在写硬盘,寄存器名字是Command,我们一般用三个命令:identify:0xEC,即硬盘识别。read sector:0x20,即读扇区。write sector:0x30,即写扇区。一旦command寄存器被写入后,硬盘就开始工作了:读扇区命令后硬盘就开始从扇区往硬盘缓冲区里面装数据,写扇区命名后cpu就开始往硬盘缓冲区里面装数据。然后在读status寄存器,硬盘缓冲区准备好后,cpu就可以从缓冲区中读数据或者硬盘就可以从缓冲区中往扇区写数据了。
一、开机进入实模式--BIOS->MBR(硬盘扇区复制函数)->LOADER_第9张图片

总结从硬盘中读数据的操作流程:
1,先选择通道(根据寄存器名字可以知道不同的通道),往该通道的sector count寄存器中写入带操作的扇区数。
2,往三个LBA寄存器中写入扇区起始地址的低24位。
3,往device寄存器中写入LBA的24-27位,并置第6位为1,使其为LBA模式,并设置第4位,选择操作的硬盘是master硬盘还是slave硬盘。
4,往该通道上的command寄存器写入读扇区操作命令后,硬盘开始往缓冲区中传数据。
5,读取该通道的status寄存器,判断硬盘往缓冲区中传数据的工作是否完成。
6,如果status寄存器中硬盘已经准备好了,开始从硬盘缓冲区中往内存中读数据。如果没有准备好,继续检测status寄存器。
MBR放在硬盘的第0扇区(LBA),我们把LOADER放在第2扇区。我们将loader的加载地址选为0x900。

我们的MBR程序功能:将硬盘上的第2扇区加载到内存地址的0x900处
;主引导程序 
;------------------------------------------------------------
%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

; 输出字符串:MBR
   mov byte [gs:0x00],'M'
   mov byte [gs:0x01],0xA4
	 
   mov eax,LOADER_START_SECTOR	 ; 起始扇区lba地址:0x2
   mov bx,LOADER_BASE_ADDR       ; 写入的地址:0x900
   mov cx,1			 ; 待读入的扇区数:1个扇区
   call rd_disk_m_16		 ; 以下读取程序的起始部分(一个扇区)
  
   jmp LOADER_BASE_ADDR          ;跳转到0x900处执行程序
       
;功能:读取硬盘n个扇区,将此定义为一个全局变量
rd_disk_m_16:	   
      mov esi,eax	  ;备份eax,并且16位实模式下的扩展寻址方式,机器码加上反转指令0x67
      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                  ;空操作,相当于sleep一下
      in al,dx
      and al,0x88	   ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙
      cmp al,0x08
      jnz .not_ready	   ;al与0x08不相等,则表示未准备好,继续等。

;第5步:从0x1f0端口读数据
      mov ax, di
      mov dx, 256
      mul dx
      mov cx, ax	   ; di为要读取的扇区数,一个扇区有512字节,每次读入一个字,
			   ; 共需di*512/2次,所以di*256  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

boot.inc程序:
;-------------	 loader和kernel   ----------
LOADER_BASE_ADDR equ 0x900 
LOADER_START_SECTOR equ 0x2


loader程序:被dd命令写入虚拟硬盘
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR		;作用是:编址时候将标号的地址加上这个地址开始编起,而不是说将程序加载到这个地址

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

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


总结:开机后进入实模式:先运行BIOS,BIOS最后的工作是将001扇区(CHS)的MBR加载到内存0x7c00处开始运行,MBR中的程序又将loader从硬盘中加载到内存(我们设定为0x900)开始运行!

你可能感兴趣的:(操作系统)