专注于操作系统11之保护模式进阶

      在上一篇中,我们已经完成了从实模式到保护模式的转变,并且还在保护模式下,输出了一个字符,但这在实模式下也能做到。这没体现出保护模式的优势,现在我们要充分利用保护模式的优势,完成向内存大地址(比如5M的地址)的读写(实模式只能寻址1M,保护模式能寻址4G)。下面的代码是参照了《自己动手写操作系统》,但我在这里把它更加的简化了,只保留了我们需要关心的部分,即完成向内存大地址的读写,只需在上一篇代码中加入几行就行了。


;;nasm 2.04 
;;nasm safemodelup.asm -o safemodelup.img
org 07c00H

%macro Descriptor 3             ;定义Descriptor结构体宏
	dw	%2 & 0FFFFh				; 段界限 1				(2 字节)
	dw	%1 & 0FFFFh				; 段基址 1				(2 字节)
	db	(%1 >> 16) & 0FFh			; 段基址 2				(1 字节)
	dw	((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)	; 属性 1 + 段界限 2 + 属性 2		(2 字节)
	db	(%1 >> 24) & 0FFh			; 段基址 3				(1 字节)
%endmacro ; 共 8 字节

DA_C		EQU	98h	; 只读代码段的属性值
DA_DRW		EQU	92h	; 允许读写的数据段的属性值
DA_32		EQU	4000h	;32位段的属性值


	jmp	LABEL_BEGIN         
	               ;现在进行第一步:建立GDT表
[SECTION .gdt]     ;用于放GDT表的段
;GDT
LABEL_GDT: Descriptor 0,0,0 ;空描述符,当一个任务没有LDT时,会把LDTR清空,这时,LDTR便指向空描述符。

LABEL_CODE32: Descriptor 0,SegCode32Len-1,DA_C + DA_32 ; 代码段的描述符

LABEL_VIDEO: Descriptor 0B8000h,0ffffh,DA_DRW    ;显存的描述符,0b8000h是显存的首地址

LABEL_TEXT: Descriptor 0500000h,0ffffh,DA_DRW     ;测试的数据段,即我们要读写的数据段(内存大地址)。

LABEL_DATA: Descriptor 0,0ffffh,DA_DRW+DA_32       ;存放数据的段,即我们要将这里的数据写到内存大地址。
;GDT 结束

GdtLenth equ $ - LABEL_GDT   ;计算GDT表的长度

GdtPtr dw GdtLenth - 1 ; 准备存入GDTR的信息
       dd 0    ;    用于存放GDT表的物理地址,后面会计算


;GDT选择子

SelectorCode32 equ LABEL_CODE32 - LABEL_GDT ;代码段的选择子
SelectorVideo  equ LABEL_VIDEO - LABEL_GDT  ;显存所在段的选择子
SelectorData equ LABEL_DATA - LABEL_GDT     ;数据段的选择子
SelectorText equ LABEL_TEXT - LABEL_GDT      ;测试数据段的选择子

;[SECTION .gdt]结束

[SECTION .data]  ;数据段,为了简单,我们只保存一个字符
LABEL_SEG_DATA:
db 'A'
;[SECTION .data]结束

[SECTION .s16]
[BITS 16]	;目标处理器模式
LABEL_BEGIN:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0100h

;初始化数据段的描述符
xor eax,eax   ; 同或运算,这里是将eax清零

mov ax,cs
shl eax,4     ;左移四位,相当于乘以16,实模式下计算物理地址
add eax,LABEL_SEG_DATA    ;eax中为32位段的物理地址

mov word [LABEL_DATA + 2],ax   ;将32位段的物理地址存入段的描述符中

shr eax,16
mov byte [LABEL_DATA + 4],al
mov byte [LABEL_DATA + 7],ah


;初始化32位段的描述符

xor eax,eax   ; 同或运算,这里是将eax清零

mov ax,cs
shl eax,4     ;左移四位,相当于乘以16,实模式下计算物理地址
add eax,LABEL_SEG_CODE32    ;eax中为32位段的物理地址

mov word [LABEL_CODE32 + 2],ax   ;将32位段的物理地址存入段的描述符中

shr eax,16
mov byte [LABEL_CODE32 + 4],al
mov byte [LABEL_CODE32 + 7],ah

;计算GDT表的物理地址,将GDT表的物理地址信息存入GdtPtr中
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_GDT
mov dword [GdtPtr + 2],eax

;现在进行第二步:将GDT表的信息加载到GDTR中
lgdt [GdtPtr]  ;装载GDTR

;关中断
cli

;现在进行第三步:打开地址线
in al,92h
or al,00000010b
out 92h,al

;现在进行第四步:置PE为1,表示CPU处于保护模式下
mov eax,cr0
or eax,1
mov cr0,eax
;现在进入第五步:跳到保护模式的代码

jmp dword SelectorCode32:0  ;这里将SelectorCode32存入CS中作为选择子
;[SECTION .s16] 结束

;保护模式下的代码,32位代码
[SECTION .s32]
[BITS 32]  



LABEL_SEG_CODE32:
mov ax,SelectorVideo ;视频选择子,用于找到显存段的描述符
mov gs,ax 
mov ax,SelectorText
mov es,ax
mov ax,SelectorData
mov ds,ax

mov edi,(80 * 10 + 0)*2  ;屏幕的第10行,第0列
mov ah,0Ch
mov al,[es:0]
mov [gs:edi],ax ;将测试段(内存大地址)的第一个字符输出到屏幕,在虚拟机中测试时输出为空。

mov al,[ds:0]  ;向测试段(内存大地址)写入字符(此字符为数据段的第一个字符,这里为‘A’)
mov [es:0],al

mov edi,(80 * 11 + 0)*2  ;屏幕的第11行,第0列
mov ah,0Ch
mov al,[es:0]
mov [gs:edi],ax ;再次将测试段(内存大地址)的第一个字符输出到屏幕,看写入是否成功


jmp $

SegCode32Len equ $ - LABEL_SEG_CODE32  ;计算32位代码段的长度
times 510-($-$) db 0
dw 0aa55H


将编译后的文件,safemodelup.img 装入虚拟机

下面是运行的结果图


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值