进入32位保护模式之路

; haribote-os boot asm
; TAB=4

BOTPAK	EQU		0x00280000		
DSKCAC	EQU		0x00100000		 
DSKCAC0	EQU		0x00008000		 

; BOOT_INFO
CYLS	EQU		0x0ff0			; 设定启动区
LEDS	EQU		0x0ff1
VMODE	EQU		0x0ff2			; 关于颜色数目的信息  颜色的位数
SCRNX	EQU		0x0ff4			; 分辨率的X 
SCRNY	EQU		0x0ff6			; 分辨率的Y
VRAM	EQU		0x0ff8			; 图像缓冲区的开始地址

		ORG		0xc200			; 程序装载地址

		MOV		AL,0x13			; VGA显卡   320*200*8位色
		MOV		AH,0x00
		INT		0x10
		MOV		BYTE [VMODE],8	; 记录画面模式
		MOV		WORD [SCRNX],320
		MOV		WORD [SCRNY],200
		MOV		DWORD [VRAM],0x000a0000

; 用BIOS取得键盘上 各种LED指示灯的状态

		MOV		AH,0x02
		INT		0x16 			; keyboard BIOS
		MOV		[LEDS],AL

; PIC关闭一切中断
;	AT兼容机的规格 如果要初始化PIC
;	必须在cli之前进行 否则有可能被挂起
;	随后进行PIC的初始化

		MOV		AL,0xff
		OUT		0x21,AL
		NOP						; 让CPU在此等待一下 
		OUT		0xa1,AL

		CLI						; 禁止CPU级别的中断

; CPU为了访问1M以上的内存,设定A20Gate
;A20Gate 是由键盘控制器来操控的

		CALL	waitkbdout
		MOV		AL,0xd1			
		;准备写OutPut端口 随后通过60h写入的数据,放置在OutPut Port
		OUT		0x64,AL
		CALL	waitkbdout
		MOV		AL,0xdf			; enable A20
		OUT		0x60,AL
		CALL	waitkbdout

; 切换到保护模式

[INSTRSET "i486p"]				; 想要使用486指令的叙述 

		LGDT	[GDTR0]			; 设定临时的GDT
		MOV		EAX,CR0
		AND		EAX,0x7fffffff	; 设置PG位为1 禁止分页
		OR		EAX,0x00000001	; 设置PE位为1 保护模式
		MOV		CR0,EAX
		JMP		pipelineflush	;模式发生变化 重新解释指令
pipelineflush:
		MOV		AX,1*8			;  可读写的段 32bit
		MOV		DS,AX			;0x0008 相当于GDT+1
		MOV		ES,AX
		MOV		FS,AX
		MOV		GS,AX
		MOV		SS,AX

; bootpack的传送

		MOV		ESI,bootpack	; 传送源
		MOV		EDI,BOTPAK		; 传送目的
		MOV		ECX,512*1024/4
		CALL	memcpy

; 磁盘数据最后送回它本来的位置

; 首先从启动扇区开始

		MOV		ESI,0x7c00		; 传送源
		MOV		EDI,DSKCAC		; 传送目的
		MOV		ECX,512/4
		CALL	memcpy

; 所有剩下的

		MOV		ESI,DSKCAC0+512	; 传送源
		MOV		EDI,DSKCAC+512	; 传送目的
		MOV		ECX,0
		MOV		CL,BYTE [CYLS]
		IMUL	ECX,512*18*2/4	; 从柱面数变化成字节数
		SUB		ECX,512/4		; 减去IPL的
		CALL	memcpy

; asmhead的任务全部完成
;	下面由bootpack来完成

; bootpack的启动

		MOV		EBX,BOTPAK
		MOV		ECX,[EBX+16]
		ADD		ECX,3			; ECX += 3;
		SHR		ECX,2			; ECX /= 4;
		JZ		skip			; 没有要转送的东西时
		MOV		ESI,[EBX+20]	; 传送源
		ADD		ESI,EBX
		MOV		EDI,[EBX+12]	; 传送目的
		CALL	memcpy
skip:
		MOV		ESP,[EBX+12]	;栈初始化
		JMP		DWORD 2*8:0x0000001b

waitkbdout:
		IN		 AL,0x64
		AND		 AL,0x02
		JNZ		waitkbdout		; 空读 为了清空接受缓冲区中的垃圾
		RET

memcpy:
		MOV		EAX,[ESI]
		ADD		ESI,4
		MOV		[EDI],EAX
		ADD		EDI,4
		SUB		ECX,1
		JNZ		memcpy			; 
		RET
; 

		ALIGNB	16
GDT0:	;特殊的GDT 不能在这里设置段
		RESB	8				; NULL SELECTOR
		DW		0xffff,0x0000,0x9200,0x00cf	; 可以读写的段 32bits
		DW		0xffff,0x0000,0x9a28,0x0047	; 可以执行的段 32bits

		DW		0
GDTR0:
		DW		8*3-1
		DD		GDT0

		ALIGNB	16
bootpack:

### 从16实模式切换到16保护模式,再进入32保护模式 在x86架构中,从16实模式切换到16保护模式,再进入32保护模式的过程涉及到多个关键步骤。这一流程是操作系统开发中的基础内容,通常用于引导程序(bootloader)的编写。 #### 1. 从16实模式切换到16保护模式 实模式是x86处理器启动时的默认模式,运行在16模式下,只能访问1MB的内存空间。要切换到16保护模式,需要完成以下步骤: - **加载全局描述符表(GDT)** GDT是一个描述内存段属性的数据结构,用于定义保护模式下的段描述符。需要在内存中设置GDT,并使用`lgdt`指令加载其地址和界限。 示例代码: ```assembly gdt_start: .quad 0x0000000000000000 # 空描述符 .quad 0x00CF9A0000000FFF # 代码段描述符 .quad 0x00CF920000000FFF # 数据段描述符 gdt_end: gdt_desc: .word gdt_end - gdt_start - 1 # GDT界限 .long gdt_start # GDT起始地址 ``` 加载GDT的指令为: ```assembly lgdt gdt_desc ``` - **设置控制寄存器CR0的PE** CR0寄存器的第0(PE)用于启用保护模式。设置该后,处理器将进入16保护模式。 示例代码: ```assembly movl %cr0, %eax orl $0x00000001, %eax movl %eax, %cr0 ``` - **远跳转到保护模式代码段** 由于段寄存器仍处于实模式下的段地址格式,需要通过远跳转(far jump)更新代码段寄存器(CS),使其指向GDT中的代码段描述符。 示例代码: ```assembly ljmpl $0x08, $protected_mode_entry ``` 其中,`0x08`是GDT中代码段描述符的索引值,`protected_mode_entry`是进入保护模式后的入口地址。 #### 2. 从16保护模式切换到32保护模式 进入16保护模式后,需要进一步切换到32保护模式,以便使用更大的内存空间和32指令集。 - **更新GDT以支持32段描述符** 在16保护模式下,可以重新加载一个包含32段描述符的GDT,以支持更大的地址空间和32操作。 示例代码: ```assembly gdt_32: .quad 0x0000000000000000 # 空描述符 .quad 0x00CF9A0000000FFF # 32代码段描述符 .quad 0x00CF920000000FFF # 32数据段描述符 gdt_32_end: gdt_32_desc: .word gdt_32_end - gdt_32 - 1 .long gdt_32 ``` 使用`lgdt`指令加载新的GDT: ```assembly lgdt gdt_32_desc ``` - **更新CR0寄存器以启用32模式** 在16保护模式下,需要设置CR0寄存器的PE(已设置)和PG(页表启用),并清除TS、EM等以启用FPU支持。 示例代码: ```assembly movl %cr0, %eax orl $0x80000000, %eax # 设置PG启用分页 movl %eax, %cr0 ``` - **长跳转到32代码段** 类似于从实模式切换到16保护模式,需要通过远跳转更新代码段寄存器(CS),使其指向新的32代码段描述符。 示例代码: ```assembly ljmpl $0x08, $protected_mode_32_entry ``` 其中,`protected_mode_32_entry`是32保护模式的入口地址。 #### 3. 初始化段寄存器 进入32保护模式后,需要初始化其他段寄存器(如DS、ES、SS、FS、GS)以指向GDT中的32数据段描述符。 示例代码: ```assembly movw $0x10, %ax movw %ax, %ds movw %ax, %es movw %ax, %ss movw %ax, %fs movw %ax, %gs ``` #### 4. 设置堆栈指针 在32保护模式下,需要设置堆栈指针(ESP),以便后续的函数调用和中断处理。 示例代码: ```assembly movl $0x90000, %esp ``` #### 5. 进入C语言环境 完成上述步骤后,可以跳转到用C语言编写的内核代码中,继续操作系统开发的后续流程。 --- ###
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值