简介
实验:编写boot.inc和loader.s,完成在内存里写好3个段描述符(代码段、数据段、显存段)、赋值好GDTR寄存器、创建好表示3个选择子的字段后,进入保护模式
注意:此时loader.bin大于512字节,mbr要读取2个分区,dd也要复制2个分区
代码
boot/mbr.s
; boot/mbr.s
; 功能:读取磁盘,加载loader到内存并跳转到loader
%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
; 清屏利用0x06号功能,上卷全部行,则可清屏。
; 输入:
; AH 功能号=0x06
; AL = 上卷的行数(如果为0,表示全部)
; BH = 上卷行属性
; (CL,CH) = 窗口的左上角的(X,Y)位置
; (DL,DH) = 窗口的右上角的(X,Y)位置
; 无返回值:
mov ax,0x600
mov bx,0x700
mov cx,0
mov dx,0x184f
int 0x10
mov eax,LOADER_START_SECTOR
mov bx,LOADER_BASE_ADDR
mov cx,2
call rd_disk_m_16
jmp LOADER_BASE_ADDR
; 读取硬盘n个扇区
; eax = LBA扇区号
; bx = 将数据写入的内存地址
; cx=读入的扇区数
rd_disk_m_16:
; 备份
mov esi,eax
mov di,cx
; 设置要读取的扇区数
mov cl,0x8
mov dx,0x1f2
mov al,cl
out dx,al
mov eax,esi
; 设置要读取的地址
mov dx,0x1f3 ; 写入0-7位
out dx,al
mov cl,0x8
shr eax,cl
mov dx,0x1f4 ; 写入8-15位
out dx,al
shr eax,cl
mov dx,0x1f5 ; 写入16-23位
out dx,al
shr eax,cl
and al,0x0f ; 24~27位
or al,0xe0 ; 设置7~4位为1110,表示lba模式
mov dx,0x1f6
out dx,al
; 写入读命令
mov dx,0x1f7
mov al,0x20
out dx,al
; 检测硬盘状态
.not_ready:
nop
in al,dx
and al,0x88
cmp al,0x08
jnz .not_ready
; 读数据
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
times 510-($-$$) db 0
db 0x55,0xaa
boot/loader.s
; boot/loader.s
; 功能:完成在内存里写好3个段描述符(代码段、数据段、显存段)、赋值好GDTR寄存器、创建好表示3个选择子的字段后,进入保护模式,在保护模式下执行最后三句代码
%include "boot.inc"
SECTION loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start
; ---构建gdt---
GDT_BASE:
dd 0x0000_0000
dd 0x0000_0000
CODE_DESC:
dd 0x0000_ffff
dd DESC_CODE_HIGH4
DATA_STARK_DESC:
dd 0x0000_ffff
dd DESC_DATA_HIGH4
VEDIO_DESC:
dd 0x8000_0007 ; limit=(0xbffff-0xb8000)/4k=0x7
dd DESC_VIDEO_HIGH4
GDT_SIZE equ $ - GDT_BASE
GDT_LIMIT equ GDT_SIZE - 1
times 60 dq 0
SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0
SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0
SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0
gdt_ptr dw GDT_LIMIT
dd GDT_BASE
loadermsg db '2 loader in real.'
loader_start:
mov sp,LOADER_BASE_ADDR
; 调用13号子功能打印字符串
; 输入:
; ah 功能号13
; al设置写字符方式 ah=01:显示字符串,光标跟随移动
; bh设置要显示的页号,此处是第0页
; ;bl中是字符属性,属性是黑底绿字(bl=02h)
; es:bp为串首地址
; cx为串长度,不包括结束符0
; dh 行
; dl 列
mov bp,loadermsg
mov cx,17
mov ax,0x1301
mov bx,0x001f
mov dx,0x1800
int 0x10
; ---进入保护模式---
; 打开A20
in al,0x92
or al,0000_0010B
out 0x92,al
; 加载gdt
lgdt [gdt_ptr]
; cr0第0位给1
mov eax,cr0
or eax,0x00000001
mov cr0,eax
; 刷新流水线
jmp SELECTOR_CODE:p_mode_start
[bits 32]
p_mode_start:
mov ax,SELECTOR_DATA
mov ds,ax
mov es,ax
mov ss,ax
mov esp,LOADER_STACK_TOP
mov ax,SELECTOR_VIDEO
mov gs,ax
mov byte [gs:160], 'P'
jmp $
编译
Makefile
BUILD_DIR = ./build
.PHONY : mk_dir bootloader clean all
mk_dir: #创建build目录
if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fi
bootloader: #编译启动内核的文件 bootloader
nasm -I include/ -o $(BUILD_DIR)/mbr.bin boot/mbr.s
nasm -I include/ -o $(BUILD_DIR)/loader.bin boot/loader.s
dd if=/home/c/tityos/build/mbr.bin of=/home/c/tityos/hd60M.img bs=512 count=1 conv=notrunc
dd if=/home/c/tityos/build/loader.bin of=/home/c/tityos/hd60M.img bs=512 count=2 seek=2 conv=notrunc
clean: #删除build目录里的全部文件
cd $(BUILD_DIR) && rm -f ./*
# 创建build目录。编译启动内核的文件。
all: mk_dir bootloader
运行
start.sh
# !/bin/bash
# 功能:启动bochs
bin/bochs -f bochsrc.disk