注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

编程学习

我的网上家园

 
 
 

日志

 
 

16章分页学习的小测试  

2013-06-01 15:28:48|  分类: 《x86汇编语言: |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
开启分页模式,首先要进入保护模式.
进入保护模式以后,先要找一块4KB的内存做为页目录表使用,首先清空这块4KB的内存,然后在最后的一个4字节中写入当前页目录表的物理地址,再找一块4KB内存作为页表使用,在页目录表的开始4字节,写入页表的物理地址,填写页表的前256项与物理地址一 一 对应,因为我们的内核已经在运行中,而且我们的内核很小,1MB内存足够用了. 
填写好页目录表与页表以后,接下来把页目录表的地址传送到CR3寄存器,再设置CR0寄存器,开启分页模式,这样内核就工作在分页模式下了.

为了让内核工作在高端的线性地址,需要修改GDT中的段描述符,修改完以后,刷新一下.

以下代码实际上是书中代码的简化版.

内核代码如下(主要的测试部分):

start:
mov eax,core_data_seg_sel
mov ds,eax
mov ebx,message
call sys_routine_seg_sel:put_string ;显示内核正在运行.
mov eax,mem_0_4_gb_seg_sel
mov es,eax

mov ebx,0x20000     ;设定0x20000为页目录表
mov ecx,1024
xor esi,esi
page_clr:
mov dword [es:ebx+esi*4],0
inc esi
loop page_clr     ;清空页目录表
mov dword [es:ebx+4092],0x20003     ;设置页目录本身的物理地址
mov dword [es:ebx],0x21003     ;设置0x21000为页表的物理地址
mov ebx,0x21000
xor esi,esi
xor eax,eax
page_set:
mov edx,eax
or edx,3
mov [es:ebx+esi*4],edx
add eax,0x1000
inc esi
cmp esi,256
jl page_set
page_clr2:
mov dword [es:ebx+esi*4],0
inc esi
cmp esi,1024
jl page_clr2

mov eax,0x20000
mov cr3,eax
mov eax,cr0
or eax,0x80000000
mov cr0,eax

mov ebx,message2
call sys_routine_seg_sel:put_string ;显示分页已经开启

mov ebx,0xfffff000
mov esi,0x80000000
shr esi,22
mov dword [es:ebx+esi*4],0x21003    ;修改0x800000000线性地址对应的页表地址

sgdt [pgdt] ;保存当前的GDT
mov ebx,[pgdt+2] ;GDT基地址

or dword [es:ebx+0x18+4],0x80000000 ;修改内核栈描述符
or dword [es:ebx+0x20+4],0x80000000 ;修改视频显示缓冲区描述符
or dword [es:ebx+0x28+4],0x80000000 ;修改公共例程段描述符
or dword [es:ebx+0x30+4],0x80000000 ;修改内核数据段描述符
or dword [es:ebx+0x38+4],0x80000000 ;修改内核代码段描述符

or dword [pgdt+2],0x80000000    ;修改GDT线性基地址
lgdt [pgdt]
jmp core_code_seg_sel:newgdt

newgdt:
mov eax,core_stack_seg_sel
mov ss,eax
mov eax,core_data_seg_sel
mov ds,eax
mov ebx,message3
call sys_routine_seg_sel:put_string

hlt

下面是完整的内核源代码(编译以后写入虚拟硬盘第二扇区):
内核代码需要MBR引导代码加载,可以直接用第13章的MBR代码编译后写入虚拟硬盘第一扇区:


core_code_seg_sel     equ  0x38    ;内核代码段选择子
         core_data_seg_sel     equ  0x30    ;内核数据段选择子 
         sys_routine_seg_sel   equ  0x28    ;系统公共例程代码段的选择子 
         video_ram_seg_sel     equ  0x20    ;视频显示缓冲区的段选择子
         core_stack_seg_sel    equ  0x18    ;内核堆栈段选择子
         mem_0_4_gb_seg_sel    equ  0x08    ;整个0-4GB内存的段的选择子
;以下是系统核心的头部,用于加载核心程序
;---------------------------------------------------------------------
section header
 
         core_length      dd core_end       ;核心程序总长度#00

         sys_routine_seg  dd section.sys_routine.start
                                            ;系统公用例程段位置#04

         core_data_seg    dd section.core_data.start
                                            ;核心数据段位置#08

         core_code_seg    dd section.core_code.start
                                            ;核心代码段位置#0c


         core_entry       dd start          ;核心代码段入口点#10
                          dw core_code_seg_sel
;--------------------------------------------------------------------
[bits 32]
section sys_routine vstart=0
put_string:                                 ;显示0终止的字符串并移动光标 
                                            ;输入:DS:EBX=串地址
         push ecx
  .getc:
         mov cl,[ebx]
         or cl,cl
         jz .exit
         call put_char
         inc ebx
         jmp .getc

  .exit:
         pop ecx
         retf                               ;段间返回

;-------------------------------------------------------------------------------
put_char:                                   ;在当前光标处显示一个字符,并推进
                                            ;光标。仅用于段内调用 
                                            ;输入:CL=字符ASCII码 
         pushad

         ;以下取当前光标位置
         mov dx,0x3d4
         mov al,0x0e
         out dx,al
         inc dx                             ;0x3d5
         in al,dx                           ;高字
         mov ah,al

         dec dx                             ;0x3d4
         mov al,0x0f
         out dx,al
         inc dx                             ;0x3d5
         in al,dx                           ;低字
         mov bx,ax                          ;BX=代表光标位置的16位数

         cmp cl,0x0d                        ;回车符?
         jnz .put_0a
         mov ax,bx
         mov bl,80
         div bl
         mul bl
         mov bx,ax
         jmp .set_cursor

  .put_0a:
         cmp cl,0x0a                        ;换行符?
         jnz .put_other
         add bx,80
         jmp .roll_screen

  .put_other:                               ;正常显示字符
         push es
         mov eax,video_ram_seg_sel          ;0xb8000段的选择子
         mov es,eax
         shl bx,1
         mov [es:bx],cl
         pop es

         ;以下将光标位置推进一个字符
         shr bx,1
         inc bx

  .roll_screen:
         cmp bx,2000                        ;光标超出屏幕?滚屏
         jl .set_cursor

         push ds
         push es
         mov eax,video_ram_seg_sel
         mov ds,eax
         mov es,eax
         cld
         mov esi,0xa0                       ;小心!32位模式下movsb/w/d 
         mov edi,0x00                       ;使用的是esi/edi/ecx 
         mov ecx,1920
         rep movsd
         mov bx,3840                        ;清除屏幕最底一行
         mov ecx,80                         ;32位程序应该使用ECX
  .cls:
         mov word[es:bx],0x0720
         add bx,2
         loop .cls

         pop es
         pop ds

         mov bx,1920

  .set_cursor:
         mov dx,0x3d4
         mov al,0x0e
         out dx,al
         inc dx                             ;0x3d5
         mov al,bh
         out dx,al
         dec dx                             ;0x3d4
         mov al,0x0f
         out dx,al
         inc dx                             ;0x3d5
         mov al,bl
         out dx,al

         popad
         ret 
;------------------------------------------------------
section core_data vstart=0

message  db 0xd,0xa,0xd,0xa,'Core is running.',0xd,0xa,0
message2 db 'Page mode is running.',0xd,0xa,0
message3 db 'GDT is Modify.',0
pgdt dw 0
dd 0

;------------------------------------------------------
section core_code vstart=0

start:
mov eax,core_data_seg_sel
mov ds,eax
mov ebx,message
call sys_routine_seg_sel:put_string ;显示内核正在运行.
mov eax,mem_0_4_gb_seg_sel
mov es,eax

mov ebx,0x20000     ;设定0x20000为页目录表
mov ecx,1024
xor esi,esi
page_clr:
mov dword [es:ebx+esi*4],0
inc esi
loop page_clr     ;清空页目录表
mov dword [es:ebx+4092],0x20003     ;设置页目录本身的物理地址
mov dword [es:ebx],0x21003     ;设置0x21000为页表的物理地址
mov ebx,0x21000
xor esi,esi
xor eax,eax
page_set:
mov edx,eax
or edx,3
mov [es:ebx+esi*4],edx
add eax,0x1000
inc esi
cmp esi,256
jl page_set
page_clr2:
mov dword [es:ebx+esi*4],0
inc esi
cmp esi,1024
jl page_clr2

mov eax,0x20000
mov cr3,eax
mov eax,cr0
or eax,0x80000000
mov cr0,eax

mov ebx,message2
call sys_routine_seg_sel:put_string ;显示分页已经开启

mov ebx,0xfffff000
mov esi,0x80000000
shr esi,22
mov dword [es:ebx+esi*4],0x21003    ;修改0x800000000线性地址对应的页表地址





sgdt [pgdt] ;保存当前的GDT
mov ebx,[pgdt+2] ;GDT基地址

or dword [es:ebx+0x18+4],0x80000000 ;修改内核栈描述符
or dword [es:ebx+0x20+4],0x80000000 ;修改视频显示缓冲区描述符
or dword [es:ebx+0x28+4],0x80000000 ;修改公共例程段描述符
or dword [es:ebx+0x30+4],0x80000000 ;修改内核数据段描述符
or dword [es:ebx+0x38+4],0x80000000 ;修改内核代码段描述符

or dword [pgdt+2],0x80000000    ;修改GDT线性基地址
lgdt [pgdt]
jmp core_code_seg_sel:newgdt

newgdt:
mov eax,core_stack_seg_sel
mov ss,eax
mov eax,core_data_seg_sel
mov ds,eax
mov ebx,message3
call sys_routine_seg_sel:put_string

hlt

section end
core_end:
  评论这张
 
阅读(38)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017