保护模式2:保护模式下32位代码段和16位代码段切换
为了兼容, 386的保护模式可以在16位代码段和32位代码段之间随意转换, 用于标示是32位或者16位代码段的是段描述符内第6字节的第6位标示出来的. 叫做D/B位.
这个D/B位还有些郁闷, 如果是描述代码段时, 该位被称为D位, 如果标志位1, 那么模式使用32位地址, 所有的16位指令都会加前缀, 如果为0则默认是16位地址, 和指令. 指令前缀66H可以指定操作符的长度而不使用缺省长度, 67H可以用来指定地址值的长度.
下面这个实例就做到了在16位代码段和32位代码段之间的切换. 当然这个代码在逻辑上的含义是先显示线性地址100000h处16个字节的内容, 然后再显示一个数据段中的一个字符串.. 相对上一篇进入保护模式并没有复杂太多..
代码功能首先是为切换到保护模式做准备, 初始化VGDTR描述符, 还有各个段描述符, 然后将PE置位, 开启分段切换到保护模式. 然后将线性地址100000h处的16个字节内容. 写入到B80000H处, 也就是显示在屏幕上. 然后切换到16位段. 又显示了一个字符串. 然后再切换回实模式.
你可以从这里下载这个源码, 也是MASM9+link5写的..
http://www.joenchen.com/JoenTools/CodeSwitch.rar
;============================================================================ ;保护模式下32位段和16位段的转换 ;编译方法参加makefile, MASM9 + link5.6 TAB=8 ;============================================================================ .686p Include pm.inc ;重要头文件定义 option casemap:none ;============================================================================ ;16位数据段, 存储了一些非常重要的数据 ;============================================================================ DataSeg Segment use16 GDT label byte ;全局描述符表 Dummy: Descriptor 0, 0, 0 ;空的描述符 ;---------------------------------------------------------------------------- ; ;段基址 ;段界限 ;属性 Normal: Descriptor 0, 0ffffh, DA_DRW ;规范选择字 Code16Desc: Descriptor 0, 0ffffh, DA_C ;非一致代码段16位 Code32Desc: Descriptor 0, Code32Len, DA_C or DA_32;非一致代码段32位 VideoDesc: Descriptor 0b8000h, 0ffffh, DA_DRW ;显存段(可读写) DataSDesc: Descriptor 100000h, 16, DA_DR ;只读数据段 DataMessDesc: Descriptor 0, sizeof SzMessage, DA_DRW ;保护模式下使用的数据段 StackDesc: Descriptor 0, 256, DA_DRW ;堆栈段(可读写) ;---------------------------------------------------------------------------- GDT_Len equ $ - GDT ;GDT长度 GDT_Ptr word GDT_Len-1 ;VGDT dword 0 _RegSs word 0 ;用于保存旧的SS:SP _RegSp word 0 ;---------------------------------------------------------------------------- NormalSelector equ Normal - GDT ;规范段描述符选择子 Code16Selector equ Code16Desc - GDT ;16位代码段选择子 Code32Selector equ Code32Desc - GDT ;32位代码段选种子 DataSrcSelector equ DataSDesc- GDT ;源数据段选择子 VideoSelector equ VideoDesc - GDT ;视频段选择子 DataMessSelector equ DataMessDesc - GDT ;显示消息段的选择子 StackSelector equ StackDesc - GDT ;堆栈段选择子 DataSeg Ends DataMess Segment use16 SzMessage byte "I entered a protected mode I'm Joen", 0 DataMess Ends ;============================================================================ ;保护模式下使用的堆栈段 ;============================================================================ StackSeg Segment para Stack use16 ;堆栈段 byte 256 dup( 0 ) StackSeg Ends ;============================================================================ ;16位代码段, 由保护模式跳入, 然后返回到实模式 ;============================================================================ Code16Seg Segment use16 _RetReal Proc ;---------------------------------------------------------------------------- ;在屏幕上显示一个字符串 mov ax, DataMessSelector mov ds, ax ;ds-->消息段 mov si, offset SzMessage mov ax, VideoSelector mov es, ax ;es-->视频段 mov di, 80*2*11+10*2 ;15行10列 mov cx, sizeof SzMessage @@: lodsb mov ah, 07h ;属性 stosw loop @b ;---------------------------------------------------------------------------- ;关闭保护模式, 准备返回实模式 mov ax, NormalSelector mov ds, ax mov es, ax mov ss, ax ;规范选择子 mov eax, cr0 and al, 0feh mov cr0, eax ;关闭分段标记 ;通过这个跳转刷新段选择子影子寄存器, 真正进入实模式 jmp far ptr _ToReal _RetReal Endp Code16Seg Ends ;============================================================================ ;32位代码段, 由实模式跳入 ;============================================================================ Code32Seg Segment use32 ;============================================================================ ;将_dwValue中的值最低位转换成ASCII并填写上属性, 在EAX中返回 ;============================================================================ _ToAscii Proc _dwValue local _byBuf[4]:byte mov eax, _dwValue and al, 0fh .if al > 9 add al, 'A' ;转换低位 .Else add al, '0' .Endif mov byte ptr [_byBuf], al mov byte ptr [_byBuf+1], 7 ;属性位 xchg ah, al .if al > 9 add al, 'A' ;转换高位 .Else add al, '0' .Endif mov byte ptr [_byBuf+2], al mov byte ptr [_byBuf+3], 7 ;属性 mov eax, dword ptr [_byBuf] ret _ToAscii Endp _Entry Proc ;由实模式跳入 mov ax, StackSelector mov ss, ax mov esp, 256 ;设置ss->esp mov ax, DataSrcSelector mov ds, ax ;设置源数据段 mov ax, VideoSelector mov es, ax ;设置目标视频段 xor esi, esi mov edi, 80*2*10+10*2 ;第10行10列 mov ecx, 16 ;限长是16 cld ;---------------------------------------------------------------------------- @@: lodsb ;转换100000h开始的20个字节数据 Invoke _ToAscii, eax stosd mov ah, 7 ;属性 mov al, 20h ;空格 stosw loop @b ;---------------------------------------------------------------------------- ;跳到16位代码段, 准备返回Dos Jmp32 Code16Selector, <offset _RetReal> _Entry Endp Code32Seg_Entry = _Entry - Code32Seg ;定义段内入口 Code32Len = $ - Code32Seg ;段长度 Code32Seg Ends ;============================================================================ ;16位段, 由这里进入保护模式 ;============================================================================ StartCode Segment use16 Jmain Proc ;---------------------------------------------------------------------------- mov eax, DataSeg mov ds, ax shl eax, 4 add eax, offset GDT mov dword ptr ds:[GDT_Ptr+2], eax ;初始化VGDT描述符 ;---------------------------------------------------------------------------- mov eax, Code32Seg ;初始化32位代码段 shl eax, 4 mov word ptr ds:[Code32Desc+2], ax ;段基址低地址 shr eax, 16 mov byte ptr ds:[Code32Desc+4], al ;段基址高地址低位 mov byte ptr ds:[Code32Desc+7], ah ;段基址高地址高位 ;---------------------------------------------------------------------------- mov eax, Code16Seg ;初始化16位代码段 shl eax, 4 mov word ptr ds:[Code16Desc+2], ax ;段基址低地址 shr eax, 16 mov byte ptr ds:[Code16Desc+4], al ;段基址高地址低位 mov byte ptr ds:[Code16Desc+7], ah ;段基址高地址高位 ;---------------------------------------------------------------------------- mov eax, DataMess ;初始化一个数据段, 用于显示消息 shl eax, 4 mov word ptr ds:[DataMessDesc+2], ax;段基址低地址 shr eax, 16 mov byte ptr ds:[DataMessDesc+4], al;段基址高地址低位 mov byte ptr ds:[DataMessDesc+7], ah;段基址高地址高位 ;---------------------------------------------------------------------------- mov word ptr ds:[_RegSs], ss mov word ptr ds:[_RegSp], sp ;保存下SS:SP mov eax, StackSeg ;初始化堆栈段 shl eax, 4 mov word ptr ds:[StackDesc+2], ax ;段基址低地址 shr eax, 16 mov byte ptr ds:[StackDesc+4],al ;段基址高地址低位 mov byte ptr ds:[StackDesc+7], ah ;段基址高地址高位 ;---------------------------------------------------------------------------- lgdt fword ptr ds:[GDT_Ptr] ;装在VGDTR cli _EnableA20 ;关中断 开A20地址线 mov eax, cr0 or eax, 1 mov cr0, eax ;开启分段进入保护模式 ;---------------------------------------------------------------------------- ;刷新指令预读取序列, 真正进入保护模式 Jmp16 Code32Selector, < Code32Seg_Entry > _ToReal::;从保护模式又回来了 mov ax, DataSeg mov ds, ax mov sp, ds:[_RegSp] mov ss, ds:[_RegSs] ;恢复SS:SP _DisableA20 ;关闭A20地址线 sti mov ax, 4c00h int 21h Jmain Endp StartCode Ends End Jmain
发表评论
Warning: Undefined variable $user_ID in /www/wwwroot/joenchen.com/wp-content/themes/x-agan/comments.php on line 66
您必须登录 才能进行评论。