一个精小的操作系统一:mbr
知识基础
-
需要的计算机知识以及学习框架
微积分+大学物理+电路–>模电数电–>计算机组成原理–>c语言+汇编–>数据结构+单片机–>linux–>操作系统–>计算机网路–>安卓开发+web开发+后端维护等等应用
如果你想做算法:还需要学习离散数学+概率论+微积分–>python–>算法
-
视频资料—需要一些基础
如何从零开始写一个简单的操作系统?https://www.bilibili.com/video/BV19f4y1Y7Kq?p=1
-
本片文章也是在一定的计算机基础上(起码大致了解了汇编、c语言、数据结构、计组、os等大学基础课程)系统的完善一个小的操作系统,进而对操作系统有一个更为感性的认识而不是停留在课本上。
同时整个教程也是我学习的输出,所以我就计划是从计算机启动到加载os整个指令流程来阐述如何完成一个自己os,如果有错请大家多多指出,感谢。
计算机启动
-
开机上电,裸机
-
进入bios
计算机是冯·诺依曼结构(存储器、运算器、输入输出设备),也就是当上电后指令指针初始化为FFFFF。如果大家做过单片机开发,就会知道单片机上电后指令指针初始化为00000,即这就是他初始指令地址,然后pc自增不断向下运行其他指令,并且单片机是非易失性的内存(掉电后代码不丢失)。
计算机内存其实有两部分:RAM和ROM,ROM为非易失性内存。所以计算机最开始出产时内ROM刷有BIOS代码。(正如单片机一样,开机便有程序可以运行了)
总结
ROM内刷有BIOS启动代码
计算机上电后指针指令被初始化为FFFFF这样的
即直接从BIOS程序入口开始运行
BIOS运行:类似于运行单片机程序
-
此程序是计算机出场就已经烧好在内存里了,一般不会修改。
-
初始化:类似于单片机开发中寄存器归零、中断向量表等数据填充
汇编进行计算机(单片机)开发,此时计算机处于实模式也就是我们可以通过控制寄存器控制pc,简单的说所有的代码地址都是真实的物理地址。
-
自检:适配器是否有效等,这便是关于硬件外设是否可用的检查
-
循环:不断地搜索MBR代码进行加载到7C00处
MBR
-
BIOS程序最后会循环查找磁盘(或者是驱动、固态)里的MBR程序
-
并将程序放在7C00处
-
所以MBR程序,我们就要将他烧录到磁盘固态等其他非易失性的存储介质中去
-
此程序的特点:必须512字节、必须0xaa55结尾
-
在这个程序里我们就可以控制这个已经初始化的“大型单片机”了,例如:hello world
org 07c00h mov ax,cs ;代码段的基地址分别赋值给 mov ds,ax ;数据段寄存器和扩展寄存器 mov es,ax call Disp ;调用函数 jmp $ ;停止运行 Disp: mov ax,BootMsg mov bp,ax ;栈基址 mov cx,16 ;16进制 mov ax,01301h ;功能号,具体的配置可以看dos的手册 mov bx,000ch mov dl,0 int 10h ;调用中断命令 BootMsg: db "Hello, OS World!" times 510 - ($-$$) db 0 ;用0填充为510字节 dw 0xaa55 ;最后两个自己为0xaa55标志
-
由于512字节的大小控制,我们大多在MBR里做的是加载loader入内存可用区域内。以此让我们有更大的内存空间做加载os的事情。
所以真正的MBR程序如下:
- MBR程序定位在0x7c00处
- 文本显示设置,gs设置为0xb800(文本显示基地址)
- 加载loader入内存0x900,以lba方式读磁盘,loader存在第2块扇区
- 跳转到内存0x900处
;能够将第二个扇区里面的内容加载进入内存 ;mbr.asm loader.asm ;0 1 ;将loader放入0x900 LOADER_BASE_ADDR equ 0x900 LOADER_START_SECTOR equ 0x2 ;表示已LBA方式,我们的loader存在第2块扇区 ;------main-------------------------------------------------------- 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 ;gs为文本模式显示适配器的初始地址 ;利用0x06的功能,调用10号中断, ; AH = 0x06 ; AL = 0 表示全部都要清楚 ; BH = 上卷行的属性 ;(CL,CH) 左上角 x,y ;(DL,DH) 右下角 mov ax, 0600h mov bx, 0700h mov cx,0 mov dx,184fh ;(80,25) 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 mov byte [gs:0x06], 'B' mov byte [gs:0x07], 0xA4 mov byte [gs:0x08], 'R' mov byte [gs:0x09], 0xA4 ;加载loader,读扇区 mov eax,LOADER_START_SECTOR ;LBA 读入的扇区 mov bx,LOADER_BASE_ADDR ;写入的地址 mov cx,1 ;等待读入的扇区数 call rd_disk jmp LOADER_BASE_ADDR ;调到实际的物理内存 ;------end of main------------------------------------------------ rd_disk: ;eax LBA的扇区号 ;bx 数据写入的内存地址 ;cx 读入的扇区数 mov esi,eax ;备份eax mov di,cx ;备份cx ;读写硬盘(dos手册) mov dx, 0x1f2 mov al, cl out dx, al mov eax,esi ;将LBA的地址存入0x1f3,0x1f6 ;7-0位写入0x1f3 mov dx, 0x1f3 out dx,al ;15-8位写给1f4 mov cl,8 shr eax,cl mov dx,0x1f4 out dx,al ;23-16位写给1f5 shr eax,cl mov dx,0x1f5 out dx,al shr eax,cl and al,0x0f or al,0xe0 ;设置7-4位为1110,此时才是lBA模式 mov dx,0x1f6 out dx,al ;向0x1f7写入读命令 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 ;读数据 mov ax,di mov dx, 256 mul dx mov cx,ax mov dx,0x1f0 .go_on: in ax,dx mov [bx],ax add bx,2 loop .go_on ret ;------mbr标志----------------------------------------------------- times 510 - ($-$$) db 0 dw 0xaa55