操作系统之loader的实现
Posted 隐无影
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了操作系统之loader的实现相关的知识,希望对你有一定的参考价值。
上一章已经讲解了笔记本从开始到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=0x7c00
jmp start
;ds:si指向数据源.es:di指向显存
Message db 'ZYW_OS'
start:
mov ax,0xb800
mov es,ax
mov di,0
mov sp,0x7c00
mov si,Message
; 清屏(摘自百度)
;利用0x06号功能,上卷全部行,则可清屏。
; -----------------------------------------------------------
;INT 0x10 功能号:0x06 功能描述:上卷窗口
;------------------------------------------------------
;输入:
;AH 功能号= 0x06
;AL = 上卷的行数(如果为0,表示全部)
;BH = 上卷行属性
;(CL,CH) = 窗口左上角的(X,Y)位置
;(DL,DH) = 窗口右下角的(X,Y)位置
mov ax,0x0600
mov bx,0x0700
mov cx,0 ; 左上角: (0, 0)
mov dx,0x184f ; 右下角(80,25),
int 10h
mov cx,start-Message
s:
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=0x7c00
jmp start
;ds:si指向数据源.es:di指向显存
Message db 'ZYW_OS'
start:
mov ax,0xb800
mov es,ax
mov di,0
mov sp,0x7c00
mov si,Message
; 清屏(摘自百度)
;利用0x06号功能,上卷全部行,则可清屏。
; -----------------------------------------------------------
;INT 0x10 功能号:0x06 功能描述:上卷窗口
;------------------------------------------------------
;输入:
;AH 功能号= 0x06
;AL = 上卷的行数(如果为0,表示全部)
;BH = 上卷行属性
;(CL,CH) = 窗口左上角的(X,Y)位置
;(DL,DH) = 窗口右下角的(X,Y)位置
mov ax,0x0600
mov bx,0x0700
mov cx,0 ; 左上角: (0, 0)
mov dx,0x184f ; 右下角(80,25),
int 10h
mov cx,start-Message
s:
movsb
mov byte [es:di],0xA4
inc di
loop s
mov 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 eax
mov dx,0x1f2 ;硬盘端口0x1f2 ,设置要读取多少数据
mov al,cl ;cl控制读取多少个
out dx,al ;写入到寄存器中
pop eax
;将lba地址存入0x1f3-0x1f6处
mov dx,0x1f3
out dx,al
push cx
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al
shr eax,cl
mov dx,0x1f5
out dx,al
shr eax,cl
and al,0x0f
or al,0xe0
mov dx,0x1f6
out dx,al
pop cx
;向0x1f7写入命令
mov dx,0x1f7
mov al,0x20
out dx,al
noready:
nop
in al,dx
and al,0x88
cmp al,0x08
jnz noready
;开始从0x1f0端口读取数据
mov ax,cx
mov dx,256
mul dx
mov cx,ax
mov dx,0x1f0
readdata:
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_addr
jmp start
Message db 'I am Loader'
start:
mov si,Message
mov ax,0xb800
mov es,ax
mov di,0
mov cx,start-Message
s:
movsb
mov byte [es:di],0xA4
inc di
loop s
jmp $
以上是关于操作系统之loader的实现的主要内容,如果未能解决你的问题,请参考以下文章