我们从实模式进入保护模式时直接用一个跳转就可以了,但是返回的时候却稍稍复杂一些。因为在准备结束保护模式回到实模式之前,需要加载一个合适的描述符选择子到有关段寄存器,以使对应段描述符高速缓冲寄存器中含有合适的段界限和属性。而且,我们不能从32位代码段返回实模式,只能从16位代码段中返回。这是因为无法实现从32位代码段返回时cs高速缓冲寄存器中的属性符合实模式的要求(实模式不能改变段属性)。所以,在这里,我们新增一个Normal描述符(代码3.4第15行)。在返回实模式之前把对应选择子SelectorNormal加载到ds、es和ss。具体看[SECTION .s16code]
;pmtest2.asm
%include "pm.inc"
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
; 段基址,段界限,属性
LABEL_GDT: Descriptor 0,0,0
LABEL_DESC_NORMAL: Descriptor 0,0ffffh,DA_DRW;NORMAL描述符
LABEL_DESC_CODE32: Descriptor 0,SegCode32Len-1,DA_C+DA_32;非一致代码段
LABEL_DESC_CODE16: Descriptor 0,0ffffh,DA_C
LABEL_DESC_DATA: Descriptor 0,DataLen-1,DA_DRW;Data
LABEL_DESC_STACK: Descriptor 0,TopOfStack,DA_DRWA+DA_32;Stack,32位
LABEL_DESC_TEST: Descriptor 0500000h,0ffffh,DA_DRW;;一个以5MB为基址的段,用于测试大地址内存的读写
LABEL_DESC_VIDEO: Descriptor 0B8000h,0ffffh,DA_DRW;显存首地址
;GDT结束
GdtLen equ $-LABEL_GDT
GdtPtr dw GdtLen-1 ;GDT界限
dd 0 ;GDT基址
;GDT选择子
SelectorNormal equ LABEL_DESC_NORMAL-LABEL_GDT
SelectorCode32 equ LABEL_DESC_CODE32-LABEL_GDT
SelectorCode16 equ LABEL_DESC_CODE16-LABEL_GDT
SelectorData equ LABEL_DESC_DATA-LABEL_GDT
SelectorStack equ LABEL_DESC_STACK-LABEL_GDT
SelectorTest equ LABEL_DESC_TEST-LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO-LABEL_GDT
;END OF [SECTION .gdt]
[SECTION .data1];数据段
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode dw 0
;字符串
PMMessage: db "In.Protext.Mode.Now.^-^",0 ;保护模式中显示
OffsetPMMessage equ PMMessage-$$
StrTest: db "ABCDEFGHIJKLMNOPQRSTUVWXYZ",0
OffsetStrTest equ StrTest-$$
DataLen equ $-LABEL_DATA
;END OF [SECTION .data1]
;全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 512 db 0
TopOfStack equ $-LABEL_STACK-1
;END OF [SECTION .gs]
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0100h
mov [LABEL_GO_BACK_TO_REAL+3],ax
;初始化16位的代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_SEG_CODE16
mov word [LABEL_DESC_CODE16 +2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE16 +4], al
mov byte [LABEL_DESC_CODE16 +7], ah
;初始化32位代码段描述符
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_CODE32
mov word [LABEL_DESC_CODE32+2],ax
shr eax,16
mov byte [LABEL_DESC_CODE32+4],al
mov byte [LABEL_DESC_CODE32+7],ah
; 初始化数据段描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_DATA
mov word [LABEL_DESC_DATA + 2], ax
shr eax, 16
mov byte [LABEL_DESC_DATA + 4], al
mov byte [LABEL_DESC_DATA + 7], ah
;初始化堆栈段描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_STACK
mov word [LABEL_DESC_STACK + 2], ax
shr eax, 16
mov byte [LABEL_DESC_STACK + 4], al
mov byte [LABEL_DESC_STACK + 7], ah
;为加载gdtr做准备
xor eax,eax
mov ax,ds
shl eax,4
add eax,LABEL_GDT ;eax <-- gdt基地址
mov dword [GdtPtr+2],eax ;[GdtPtr+2] <-- gdt基地址
;加载gdtr
lgdt [GdtPtr]
;关中断
cli
;打开地址线A20
in al,92h
or al,00000010b
out 92h,al
;准备切换到保护模式
mov eax,cr0
or eax,1
mov cr0,eax
;进入保护模式
jmp dword SelectorCode32:0
;把SelectorCode32装入cs,并转到SelectorCode32:0处
LABEL_REAL_ENTRY:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,[SPValueInRealMode]
in al,92h
and al,11111101b ;关闭A20地址线
out 92h,al
sti;开中断
mov ax,4c00h
int 21h;回到DOS
;END OF [SECTION .s16]
[SECTION .s32]
[BITS 32]
LABEL_SEG_CODE32:
mov ax,SelectorData
mov ds,ax ;数据段选择子
mov ax,SelectorTest
mov es,ax ;测试段选择子,es指向新增的基址为5MB内存的段
mov ax,SelectorVideo
mov gs,ax ;视频段选择子,gs指向显存
mov ax,SelectorStack
mov ss,ax ;堆栈段选择子
mov esp,TopOfStack
;显示字符串
mov ah,0Ch ;0000黑底 1100红字
xor esi,esi
xor edi,edi
mov esi,OffsetPMMessage ;源数据偏移
mov edi,(80*10+0)*2 ;目的数据偏移,屏幕第10行第0列
cld
.1:
lodsb
test al,al
jz .2
mov [gs:edi],ax
add edi,2
jmp .1
.2: ;显示完毕
call DispReturn
call TestRead
call TestWrite
call TestRead
;到此停止
jmp SelectorCode16:0
;------------------------------
TestRead:
xor esi,esi
mov ecx,8
.loop:
mov al,[es:esi];新增的以5MB为基址的段开始处
call DispAL
inc esi
loop .loop
call DispReturn
ret
;TestRead结束-------------------
;-------------------------------
TestWrite:
push esi
push edi
xor esi,esi
xor edi,edi
mov esi,OffsetStrTest ;源数据偏移
cld
.1:
lodsb
test al,al
jz .2
mov [es:edi],al
inc edi
jmp .1
.2:
pop edi
pop esi
ret
;TestWrite结束-------------------
;--------------------------------
;使用16进制显示AL中的数字
;默认地:
; 数字已经存在AL中
; edi始终指向要显示的下一个字符的位置
;被改变的寄存器:
; edi,ax
;--------------------------------
DispAL:
push edx
push ecx
mov ah,0Ch
mov dl,al
shr al,4
mov ecx,2
.begin:
and al,01111b
cmp al,9 ;01001b
ja .1
add al,'0'
jmp .2
.1:
sub al,0Ah
add al,'A'
.2:
mov [gs:edi],ax
add edi,2
mov al,dl
loop .begin
add edi,2
pop edx
pop ecx
ret
;DispAL结束-----------------
;-------------------------------
DispReturn:
push eax
push ebx
mov eax,edi
mov bl,160
div bl
and eax,0FFh
inc eax
mov bl,160
mul bl
mov edi,eax
pop ebx
pop eax
ret
;disreturn 结束------------------------------------------
SegCode32Len equ $-LABEL_SEG_CODE32
;END OF [SECTION .s32]
;从保护模式返回实模式需要用到一个16位的段
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
mov ax,SelectorNormal
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
mov eax,cr0
and al,11111110b
mov cr0,eax
LABEL_GO_BACK_TO_REAL:
jmp 0:LABEL_REAL_ENTRY;段地址会在程序开始处被设置成正确的值
Code16Len equ $-LABEL_SEG_CODE16
;END OF [SECTION .s16code]
nasm pmtest2.asm -o pmtest2.com
sudo mount -o loop pm.img /mnt/floppy
sudo cp pmtest2.com /mnt/floppy/
sudo umount /mnt/floppy
bochs -f bochsrc