操作系统编写 -- 进入保护模式

操作系统编写 – 进入保护模式

进入保护模式,是操作系统必行的一关,今天抽出一点时间,将这个巨大的bug给大家揪出来。
话说我在进入保护模式的时候碰到了一堆bug,调试了几天几夜也没有成果,几乎被气坏了,我写操作系统是跟着一本书学的,结果我几乎不照着书上的代码写,自己写了一通,完全达不到效果。于是我把书上的代码复制了下来,一试居然书上的代码大错特错,完全没有达到效果,一闪一闪的,退回了BIOS界面。
为了解决这个问题,我翻了好多资料,终于找到方法了,现在跟大家讲一讲怎么进入保护模式。

具体代码

MBR
boot.asm

section MBR vstart=0x7c00:  ;从BIOS进入MBR
	mov ax, cs  ;cs=0, ax=cs=0
	mov ds, ax  ;ds=es=fs=ss=ax=0
	mov es, ax
	mov fs, ax
	mov ss, ax
	mov sp, 0x7c00  ;sp=0x7c000x7c00是BIOS的起始地址
	mov ax, 0xb800  ;ax=0xb8000xb800是文字显存的起始地址
	mov gs, ax  ;gs=ax=0xb800
	
	;清屏
	mov ax, 0600h
	mov bx, 0700h
	mov cx, 0
	mov dx, 184fh
	
	int 10h  ;调用BIOS中断
	
	;输出hello
	mov byte [gs:0x00], 'h'
	mov byte [gs:0x02], 'e'
	mov byte [gs:0x04], 'l'
	mov byte [gs:0x06], 'l'
	mov byte [gs:0x08], 'o'
	
	LOADER_START_SECTOR equ 0x2
	LOADER_BASE_ADDR equ 0x900
	
	mov eax, LOADER_START_SECTOR  ;加载器的起始扇区
	mov bx, LOADER_BASE_ADDR  ;加载器的起始地址
	mov cx, 4  ;要读取的扇区
	call read_loader  ;读取加载器
	
	jmp LOADER_BASE_ADDR  ;跳转到加载器

read_loader:  ;读取加载器
	mov esi, eax  ;备份eax
	mov di, cx  ;备份cx
	
	mov dx, 0x1f2  ;存入要读的扇区数
	mov al, cl  ;将cl(要读的扇区数)的值存入al
	out dx, al  ;写入0x1f2
	
	mov eax, esi  ;恢复eax中的数据
	
	mov dx, 0x1f3  ;操作LBA低8位的端口
	out dx, al  ;写入LBA低八位
	
	mov cl, 8  ;8存入cl
	shr eax, cl  ;将eax右移8位
	mov dx, 0x1f4  ;操作LBA中8位的端口
	out dx, al  ;写入0x1f4
	
	shr eax, cl  ;将eax右移八位
	mov dx, 0x1f5  ;操作LBA高8位的端口
	out dx, al  ;写入0x1f5
	
	shr eax, cl  ;将eax右移8位
	and al, 0x0f  ;与命令
	or al, 0xe0  ;或命令
	
	mov dx, 0x1f6  ;操作设备的端口
	out dx, al  ;写入0x1f6
	
	mov dx, 0x1f7  ;命令端口
	mov al, 0x20  ;写入读取指令(0x20)
	out dx, al  ;写入命令
	
	.while_not_ready:  ;当硬盘没有准备好的时候
		nop  ;什么都不做
		in al, dx  ;读取dx(0x1f7)
		and al, 0x88  ;和运算
		cmp al, 0x08  ;做减法
		jnz .while_not_ready  ;如果没有准备好就继续运行
		
		mov ax, di
		mov dx, 256
		mul dx
		mov cx, ax
		mov dx, 0x1f0  ;0x1f0是读取数据的端口
	.read_loader_data:
		in ax, dx  ;读取数据
		mov [bx], ax  ;将加载器加载到内存
		add bx, 2  ;将bx加2
		loop .read_loader_data  ;重复运行
		ret
	
	times 510-($-$$) db 0  ;将文件填充到510字节,$是当前的地址,$$是当前扇区的起始地址
	db 0x55, 0xaa  ;填充最后两个字节,将文件填充到512字节


loader
boot-loader.asm

section loader vstart=0x900:
	;输出hello
	mov byte [gs:0x00], 'h'
	mov byte [gs:0x02], 'e'
	mov byte [gs:0x04], 'l'
	mov byte [gs:0x06], 'l'
	mov byte [gs:0x08], 'o'
	
	LOADER_STACK_TOP equ 0x7c00
	jmp loader_start  ;跳转到进入保护模式的代码

gdt:
	dd 0x00000000
	dd 0x00000000
	dd 0x0000ffff
	dd 0x00cf9800
	dd 0x0000ffff
	dd 0x00cf9200
	dd 0x80000007
	dd 0x00c0920b
lgdt_value:
	dw $-gdt-1
	dd gdt
selector_code equ 0x0001<<3
selector_data equ 0x0002<<3
selector_video equ 0x0003<<3

[bits 16]

loader_start:
	cli  ;阻止CPU级别的中断
	
	;加载GDT
	lgdt [lgdt_value]
	
	;打开A20
	in al, 0x92
	or al, 00000010b  ;or是或运算,要让某一位是1就在哪一位上写1(or 1, 0=1, or 1, 1=1)
	out 0x92, al
	
	;cr1 PE
	cli
	mov eax, cr0
	or eax, 1
	mov cr0, eax  ;or是或运算,要让某一位是1就在哪一位上写1(or 0, 1=1, or 1, 1=1)
	
	;jmp $
	
	jmp dword selector_code:protection_mode_start  ;刷新流水线并跳转到protection_mode_start

[bits 32]
protection_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:0xa0], '#'
	
	jmp $

如果以上代码运行出错,请在文件开头加上

LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
BIOS_BASE_ADDR equ 0x7c00
CUI_BASE_ADDR equ 0xb800

编译代码

先下载msys2和qemu,这在以前的文章中讲过
新建一个文件夹,在这个文件夹下建立一个文件夹bin

'''
C:/
_
|---| 操作系统
    |---| bin
    |- boot.asm
    |- boot-loader.asm
    |- OS.img
'''

创建OS.img的光盘文件,可以使用bochs的bximage来创建,bochs可以自己找到官网下载
创建光盘的过程如下所示


圈出的地方是要输入的,其它的地方直接回车就可以了
然后在msys2中输入编译命令

nasm boot.asm -o bin/boot.bin;nasm boot-loader.asm -o bin/boot-loader.bin;dd if=bin/boot.bin of=OS.img bs=512 count=1 conv=notrunc;dd if=bin/boot-loader.bin of=OS.img bs=512 seek=2 count=4 conv=notrunc

然后使用qemu启动系统

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值