操作系统之loader的实现

来源:互联网 发布:上海二手住宅成交数据 编辑:程序博客网 时间:2024/06/07 06:13

上一章已经讲解了笔记本从开始到bios加载MBR(主引导扇区)的相关内容.

这章将介绍MBR跳转到loader的执行,以及用显卡直接输出到字符.有可能会有疑问为何还要写loader.因为mbr只能是512字节.这么小的空间没法做啥.只能作为跳板所以写个loader用来加载内核.


从图可知.显卡的文本模式映射到了B8000处.只要把字符移到此处.就可以在屏幕显示字符,并且每个字符占用2个字节(一个显示字母一个控制颜色).比如

mov byte [B800:0x04],'M' ,mov byte [B800:0x05] 0xA4 将显示一个 一个闪烁的M .字符由字母跟属性控制.前一个是字母显示.第二个是显示的属性比如颜色闪烁之类的

具体文本模式的第二个字节显示属性.请自行google

section MBR vstart=0x7c00jmp start;ds:si指向数据源.es:di指向显存 Message  db 'ZYW_OS'start:mov ax,0xb800mov es,axmov di,0mov sp,0x7c00mov si,Message; 清屏(摘自百度);利用0x06号功能,上卷全部行,则可清屏。; -----------------------------------------------------------;INT 0x10   功能号:0x06   功能描述:上卷窗口;------------------------------------------------------;输入:;AH 功能号= 0x06;AL = 上卷的行数(如果为0,表示全部);BH = 上卷行属性;(CL,CH) = 窗口左上角的(X,Y)位置;(DL,DH) = 窗口右下角的(X,Y)位置mov ax,0x0600mov bx,0x0700mov cx,0           ; 左上角: (0, 0)mov dx,0x184f      ;  右下角(80,25),int 10h    mov cx,start-Messages:    movsb   mov byte [es:di],0xA4   inc di   loop s   jmp $  times 510-($-$$) db 0   db 0x55,0xaa



显示效果如图


------------------------------------以上介绍了如何使用显存来显示字符而非bios系统调用-------------------------------------------

接下来介绍如何从硬盘读取loader并加载到内存中..

CPU与外设、存储器的连接和数据交换都需要通过接口设备来实现。
每个连接到I/O总线上的设备都由自己的I/O地址集,即所谓的I/O端口(I/O port)。
每个设备的I/O端口都被组织成一组专用的寄存器,CPU可给控制寄存器发命令对设备进行控制、从状态寄存器读取设备状态、可以向输出寄存器写入数据来把数据输出到设备、可通过读取输入寄存器的内容来从设备取得数据。
总之就是通过读写端口来控制设备。

一个普通的PC主板上通常有两个IDE口,分别对应两个IDE通道:primary和secondary有时也成IDE0和IDE1。
每个IDE通道又能连接两个设备,称为主设备(Master)和从设备(Slave),对不同的IDE通道的访问是通过I/O端口来区分的。
IDE(integrated drive electronics)即电子集成驱动器,主要接硬盘和光驱。
接到主设备上的硬盘称为0号硬盘。
与0号硬盘有关的I/O端口:
1F0H   0号硬盘数据寄存器
1F1H   0号硬盘错误寄存器(读时)、0号硬盘Features寄存器(写时)
1F2H   0号硬盘数据扇区计数
1F3H   0号硬盘扇区数
1F4H   0号硬盘柱面(低字节)
1F5H   0号硬盘柱面(高字节)
1F6H   0号硬盘驱动器/磁头寄存器
1F7H   0号硬盘状态寄存器(读时)、0号硬盘命令寄存器(写时)


步骤如下:

1.先选择通道,往该通道的sector count寄存器0x1f2处写入待操作的扇区数目

2.往该通道的三个LBA寄存器(0x1f3-0x1f6)写入扇区起始地址的低24位

3.往0x1f6(device)寄存器写入LBA的24-27位.并置第6位为1,使其成为LBA模式,设置第4位.选择操作的硬盘

4.向该通道的command寄存器写入命令

(5)读取此通道的status寄存器.判断硬盘是否完工

(6)将数据读取出来



接下来改造MBR.并且实现loader.

配置文件(定义地址信息)

loader_base_addr equ 0x900 loader_start_sector equ 0x2

MBR.S 源代码

%include "boot.inc"section MBR vstart=0x7c00jmp start;ds:si指向数据源.es:di指向显存 Message  db 'ZYW_OS'start:mov ax,0xb800mov es,axmov di,0mov sp,0x7c00mov si,Message; 清屏(摘自百度);利用0x06号功能,上卷全部行,则可清屏。; -----------------------------------------------------------;INT 0x10   功能号:0x06   功能描述:上卷窗口;------------------------------------------------------;输入:;AH 功能号= 0x06;AL = 上卷的行数(如果为0,表示全部);BH = 上卷行属性;(CL,CH) = 窗口左上角的(X,Y)位置;(DL,DH) = 窗口右下角的(X,Y)位置mov ax,0x0600mov bx,0x0700mov cx,0           ; 左上角: (0, 0)mov dx,0x184f      ;  右下角(80,25),int 10h mov cx,start-Messages:    movsb mov byte [es:di],0xA4 inc di   loop smov eax,loader_start_sector   ;eax保存加载程序的位置mov bx,loader_base_addr       ;bx保存加载到哪个区域mov cx,1                      ;cx保存加载几个扇区(从eax开始)call read_disk jmp  loader_base_addr ;跳转到那加载程序那,+300.这个是我自己设定好跳过的数据大小(数据跟代码..混在一起了)read_disk:     push eaxmov dx,0x1f2   ;硬盘端口0x1f2 ,设置要读取多少数据mov al,cl      ;cl控制读取多少个out dx,al      ;写入到寄存器中pop eax;将lba地址存入0x1f3-0x1f6处mov dx,0x1f3out dx,al  push cx  mov cl,8shr eax,clmov dx,0x1f4out dx,alshr eax,clmov dx,0x1f5out dx,alshr eax,cland al,0x0for al,0xe0mov dx,0x1f6out dx,al  pop cx;向0x1f7写入命令mov dx,0x1f7mov al,0x20out dx,al        noready:nopin al,dxand al,0x88 cmp al,0x08jnz noready       ;开始从0x1f0端口读取数据mov ax,cx  mov dx,256mul dxmov cx,axmov dx,0x1f0readdata:   in ax,dx   mov [bx],ax   add bx,2   loop readdata   ret  times 510-($-$$) db 0  db 0x55,0xaa      


loader.S源代码

%include "boot.inc"section loader vstart=loader_base_addrjmp startMessage db 'I am Loader'start:mov si,Messagemov ax,0xb800mov es,axmov di,0mov cx,start-Messages:movsbmov byte [es:di],0xA4inc diloop sjmp $














0 0