x86保护模式-六 控制转移
控制转移可以分为两大类 :同一任务内的控制转移 和 任务间的控制转移(任务切换)
;名称:ASM3.ASM
;功能:演示任务内无特权级变换的转移
;编译:TASM ASM3.ASM
;连接:TLINK ASM3.OBJ
;----------------------------------------------------------------------------
INCLUDE 386SCD.INC //包含的预定义的文件
;----------------------------------------------------------------------------
GDTSeg SEGMENT PARA USE16 'GDT' ;全局描述符表数据段(16位) //gdt初始化
;----------------------------------------------------------------------------
GDT LABEL BYTE ;全局描述符表
DUMMY Desc <> ;空描述符
Normal Desc <0ffffh,,,ATDW,,> ;规范段描述符
CodeK Desc <0ffffh,,,ATCE,,> ;代码段K的描述符
LDTable Desc <LDTLen-1,,,ATLDT,,> ;局部描述符表段的描述符
;----------------------------------------------------------------------------
GDTLen = $-GDT ;全局描述符表长度 $代表当前的位置 以字节为单位
;----------------------------------------------------------------------------
Normal_Sel = Normal-GDT ;规范段描述符选择子 相对于gdt表首部的偏移位置即选择子的值
CodeK_Sel = CodeK-GDT ;代码段K的选择子
LDT_Sel = LDTable-GDT ;局部描述符表段的选择子
;----------------------------------------------------------------------------
GDTSeg ENDS ;全局描述符表段定义结束
;----------------------------------------------------------------------------
LDTSeg SEGMENT PARA USE16 'LDT' ;局部描述符表数据段(16位) ldt初始化
LDT LABEL BYTE ;局部描述符表
;代码段L的描述符
CodeL Desc <CodeLLen-1,CodeLSeg,,ATCE,,>
;代码段C的描述符
CodeC Desc <CodeCLen-1,CodeCSeg,,ATCE,,>
;显示缓冲区段描述符
VideoBuf Desc <0ffffh,0b800h,,ATDW,,>
;LDT别名段描述符(DPL=3)
ToLDT Desc <LDTLen-1,LDTSEG,,ATDR+DPL3,,> //注意别名的使用 对ldt段行驶不同的访问方式
;显示信息缓冲区数据段描述符(DPL=3)
MData Desc <MDataLen-1,MDataSeg,,ATDW+DPL3,,> //指向mdata段
;堆栈段描述符
StackS Desc <TopOfS-1,StackSeg,,ATDWA,,> //指向堆栈段
;----------------------------------------------------------------------------
LDTLen = $-LDT ;LDT所占字节数
LDNum = ($-LDT)/(SIZE Desc) ;LDT含描述符项数
;----------------------------------------------------------------------------
CodeL_Sel = CodeL-LDT+TIL ;代码段L的选择子 til在386SCD.INC中定义 04h 即ti=1
CodeC_Sel = CodeC-LDT+TIL ;代码段C的选择子
Video_Sel = VideoBuf-LDT+TIL ;显示缓冲区选择子
ToLDT_Sel = ToLDT-LDT+TIL ;LDT别名段选择子
MData_Sel = MData-LDT+TIL+RPL3 ;显示信息数据段选择子 +rpl3的原因是什么?指示访问级别
Stack_Sel = StackS-LDT+TIL ;堆栈段选择子
;----------------------------------------------------------------------------
LDTSeg ENDS ;局部描述符表段定义结束
;----------------------------------------------------------------------------
MDataSeg SEGMENT PARA USE16 'MDATA' ;显示信息缓冲区数据段 初始化信息数据段
;----------------------------------------------------------------------------
Message DB 'Value=',0 //?
Buffer DB 80 DUP(0) //占80个字节长度
MDataLen = $ //?
;----------------------------------------------------------------------------
MDataSeg ENDS ;显示缓冲区数据段结束
;----------------------------------------------------------------------------
StackSeg SEGMENT DWORD USE16 'STACK' ;堆栈段 初始化堆栈段
;----------------------------------------------------------------------------
DW 512 DUP(?) //占用1024个字节
TopOfS = $ //栈顶
;----------------------------------------------------------------------------
StackSeg ENDS ;堆栈段结束
;----------------------------------------------------------------------------
CodeCSeg SEGMENT PARA USE16 'CODEC' ;任务代码段C //codec代码段
ASSUME CS:CodeCSeg
;----------------------------------------------------------------------------
;显示信息子程序
;入口参数:fs:si指向要显示的以0结尾的字符串,es:di指向显示缓冲区
;----------------------------------------------------------------------------
DispMsg PROC FAR //子程序1
mov ah,01001110b //ah=0x4eh
Disp1: mov al,BYTE PTR fs:[si] //源数据字节
inc si
or al,al //al=0结束
jz Disp2 //返回 终结条件
mov WORD PTR es:[di],ax //字传送到es指向的显示段
inc di //di指针自加2
inc di
jmp Disp1 //循环显示
Disp2: ret
DispMsg ENDP
;----------------------------------------------------------------------------
;把AL寄存器低4位二进制数(一位16进制数)转换成ASCII码
;----------------------------------------------------------------------------
HToASCII PROC FAR //转换成ascii码 子程序2
and al,00001111b //al 高4位为0 低位不变
add al,90h //al=高位变9
daa //十进制数加法调整 用于调整al的值
adc al,40h //
daa // 调整 bcd码
ret //返回
HToASCII ENDP
;----------------------------------------------------------------------------
CodeCLen = $
;----------------------------------------------------------------------------
CodeCSeg ENDS ;代码段C定义结束 代码段c仅仅定义了两个被调用的子程序
;----------------------------------------------------------------------------
CodeLSeg SEGMENT PARA USE16 'CODEL' ; codeL段的定义 被跳转
ASSUME CS:CodeLSeg
;----------------------------------------------------------------------------
Virtual2 PROC FAR
mov ax,Video_Sel ;设置显示缓冲区指针 es指向
mov es,ax //将选择子赋值给段寄存器 硬件自动计算指向段的段基地址
mov di, //1986从何而来 es:di
mov ax,MData_Sel ;设置提示信息缓冲区指针
mov fs,ax
mov si,OFFSET Message fs:si
CALL16 CodeC_Sel,DispMsg ;显示提示信息 value=0 此字符串
mov ax,ToLDT_Sel ;把演示任务的LDT的别名
mov gs,ax ;段的描述符选择子装入GS
mov dx,WORD PTR gs:CodeL.LimitL
mov si,OFFSET Buffer ;取代码段L的段界限值
mov cx,4 ;并转成对应可显示字符串
Vir: rol dx,4 //循环左移 移除的位进入cf 还要填补空出的位 4位移转换 从高开始
mov al,dl //赋值给al
CALL16 CodeC_Sel,HToASCII //调用子程序 十六进制转换到ascii码利用al的低四位进行运算 并返回
mov BYTE PTR fs:[si],al //将rol循环出的高4位 变为对应字符后填写到buufer区域
inc si //buffer 指针自加
loop Vir //循环到vir 重新开始运算下个ascii码
mov WORD PTR fs:[si],'H' //在循环结束后 ascii码串的末尾加上h字符 进行十六进制字符标识
mov si,OFFSET Buffer //si复位 指向buffer的基地址
CALL16 CodeC_Sel,DispMsg //调用显示提示信息子程序 重新显示十进制对应的字符串
JUMP16 CodeK_Sel,Virtual3 //调用子程序回到实模式前的准备工作 利用normal_sel重置寄存器
CodeLLen = $
Virtual2 ENDP
;----------------------------------------------------------------------------
CodeLSeg ENDS
;----------------------------------------------------------------------------
CodeKSeg SEGMENT PARA USE16 'CODEK' ;codeK 代码段的定义
ASSUME CS:CodeKSeg
;----------------------------------------------------------------------------
Virtual1 PROC FAR //被调用
mov ax,LDT_Sel //下面为将ldt的位于gdt中的选择子送给ldtr寄存器
LLDT ax ;加载局部描述符表寄存器LDTR 选择子进入ldtr寄存器
mov ax,Stack_Sel //将堆栈的选择子送给ss寄存器
mov ss,ax ;建立演示任务堆栈
mov sp,OFFSET TopOfS
JUMP16 CodeL_Sel,Virtual2 //跳转到16位代码段 选择子为codeL_SEL 指向的段
Virtual3: mov ax,Normal_Sel
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
mov eax,cr0
and al,11111110b
mov cr0,eax
JUMP16 <SEG Real>,<OFFSET Real>
CodeKLen = $
Virtual1 ENDP
;----------------------------------------------------------------------------
CodeKSeg ENDS ;codeK段定义结束
;============================================================================
RDataSeg SEGMENT PARA USE16 ;实方式数据段 定义 初始化
VGDTR PDesc <GDTLen-1,> ;GDT伪描述符
SPVar DW ? ;用于保存实方式下的SP
SSVar DW ? ;用于保存实方式下的SS
RDataSeg ENDS
;----------------------------------------------------------------------------
RCodeSeg SEGMENT PARA USE16 ;实方式代码段定义 程序的入口
ASSUME CS:RCodeSeg
;----------------------------------------------------------------------------
Start PROC
ASSUME DS:GDTSeg //ds定位到GDT表的首部
;-----------------
mov ax,GDTSeg
mov ds,ax
;初始化全局描述符表 需要实模式下进行
mov bx,16
mov ax,CodeKSeg //codek段段基地址
mul bx //ax左移四位;高位在dx,低16位在ax
mov CodeK.BaseL,ax
mov CodeK.BaseM,dl
mov CodeK.BaseH,dh //32位段基地址分别赋值
mov ax,LDTSeg //LDTSeg 表格所在的数据段的基地址给ax
mul bx
mov LDTable.BaseL,ax //32位段基地址赋值
mov LDTable.BaseM,dl
mov LDTable.BaseH,dh
;设置GDT伪描述符
ASSUME DS:RDataSeg //rdataseg 为实模式数据段 存放gdt的伪描述符
mov ax,RDataSeg //
mov ds,ax //ds寄存器存放此段基地址
mov ax,GDTSeg
mul bx
mov WORD PTR VGDTR.Base,ax //段基地址赋值 将GDT表的基地址赋值为伪描述符
mov WORD PTR VGDTR.Base+2,dx //相应字段 以至加载GDTR
;初始化演示任务LDT //初始化
cld //清除si di的方向
call Init_MLDT //调用 ldt表中各项初始化后返回
;保存实方式堆栈指针
mov SSVar,ss
mov SPVar,sp
;装载GDTR
lgdt QWORD PTR VGDTR
cli //关闭中断
;切换到保护方式
mov eax,cr0
or al,1
mov cr0,eax
JUMP16 <CodeK_Sel>,<OFFSET Virtual1> //跳转到codek代码段 16位 偏移为virtual1
Real: ;又回到实方式
mov ax,RDataSeg
mov ds,ax
lss sp,DWORD PTR SPVar
sti
mov ax,4c00h
int 21h
Start ENDP
;----------------------------------------------------------------------------
Init_MLDT PROC //初始化MLDT 仅初始化各个ldt表中各个描述符的基地址
push ds
mov ax,LDTSeg
mov ds,ax
mov cx,LDNum
mov si,OFFSET LDT
InitL: mov ax,[si].BaseL //初始化L 注意此方式为实模式下进行初始化 16位*4 变为20位
movzx eax,ax
shl eax,4
shld edx,eax,16 //双精度移位指令 三个操作数 edx左移16位 空出的位有eax.
mov [si].BaseL,ax //的高16位进行填充 eax值不变
mov [si].BaseM,dl //32位基地址
mov [si].BaseH,dh
add si,SIZE Desc // 指针加上描述符的长度 8个字节
loop InitL //当cx为0时终止
pop ds
ret
Init_MLDT ENDP
;----------------------------------------------------------------------------
RCodeSeg ENDS
END Start
上面程序的说明
;----------------------------------------------------------------------------
INCLUDE 386SCD.INC //包含文件
;----------------------------------------------------------------------------
GDTSeg SEGMENTPARAUSE16;全局描述符表数据段(16位) //gdt数据段开始;----------------------------------------------------------------------------;全局描述符表
GDT LABELBYTE;空描述符
DUMMY Desc <> //空描述符;规范段描述符
Normal Desc <0ffffh,,,ATDW,,> //描述符1 规范描述符;视频缓冲区段描述符(DPL=3)
VideoBuf Desc <07fffh,8000h,0bh,ATDW+DPL3,,>//描述符2 显示缓冲区;----------------------------------------------------------------------------
EFFGDT LABELBYTE;任务状态段TSS描述符
DemoTSS Desc <DemoTssLen-1,DemoTSSSeg,,AT386TSS,,> //演示任务描述符;局部描述符表段的描述符
DemoLDTD Desc <DemoLDTLen-1,DemoLDTSeg,,ATLDT,,> //演示局部描绘符表的描述符;临时代码段描述符
TempCode Desc <0ffffh,TempCodeSeg,,ATCE,,> //演示代码描述符;----------------------------------------------------------------------------
GDTLen =$-GDT ;全局描述符表长度 //gdt表占用空间 字节为单位
GDNum =($-EFFGDT)/(SIZE Desc);需特殊处理的描述符数;----------------------------------------------------------------------------
Normal_Sel = Normal-GDT ;规范段描述符选择子
Video_Sel = VideoBuf-GDT ;视频缓冲区段描述符选择子;----------------------------------------------------------------------------
DemoTSS_Sel = DemoTSS-GDT ;任务状态段描述符选择子
DemoLDT_Sel = DemoLDTD-GDT ;局部描述符表段的选择子
TempCode_Sel = TempCode-GDT ;临时代码段的选择子;----------------------------------------------------------------------------
GDTSeg ENDS;全局描述符表段定义结束
;----------------------------------------------------------------------------
DemoLDTSeg SEGMENT PARA USE16 ;局部描述符表数据段(16位)
;----------------------------------------------------------------------------
DemoLDT LABEL BYTE ;局部描述符表
;0级堆栈段描述符(32位段)
DemoStack0 Desc <DemoStack0Len-1,DemoStack0Seg,,ATDW+DPL0,D32,>
;1级堆栈段描述符(32位段)
DemoStack1 Desc <DemoStack1Len-1,DemoStack1Seg,,ATDW+DPL1,D32,>
;3级堆栈段描述符(16位段)
DemoStack3 Desc <DemoStack3Len-1,DemoStack3Seg,,ATDW+DPL3,,>
;代码段描述符(32位段,DPL=3)
DemoCode Desc <DemoCodeLen-1,DemoCodeSeg,,ATCE+DPL3,D32,>;过渡代码段描述符(32位段)
T32Code Desc <T32CodeLen-1,T32CodeSeg,,ATCE,D32,>;显示子程序代码段描述符(32位段,DPL=1)
EchoSubR Desc <EchoSubRLen-1,EchoSubRSeg,,ATCER+DPL1,D32,>;----------------------------------------------------------------------------
DemoLDNum =($-DemoLDT)/(SIZE Desc);----------------------------------------------------------------------------;0级堆栈描述符选择子(RPL=0)
DemoStack0_Sel = DemoStack0-DemoLDT+TIL+RPL0
;1级堆栈描述符选择子(RPL=1)
DemoStack1_Sel = DemoStack1-DemoLDT+TIL+RPL1
;3级堆栈描述符选择子(RPL=3)
DemoStack3_Sel = DemoStack3-DemoLDT+TIL+RPL3
;代码段描述符选择子(RPL=3)
DemoCode_Sel = DemoCode-DemoLDT+TIL+RPL3
;过渡代码段描述符选择子
T32Code_Sel = T32Code-DemoLDT+TIL
;显示子程序代码段描述符选择子(RPL=1)
Echo_Sel1 = EchoSubR-DemoLDT+TIL+RPL1
;显示子程序代码段描述符选择子(RPL=3)
Echo_Sel3 = EchoSubR-DemoLDT+TIL+RPL3
;----------------------------------------------------------------------------;指向过渡代码段内T32Begin点的调用门(DPL=0)
ToT32GateA Gate <T32Begin,T32Code_Sel,,AT386CGate,>;指向过渡代码段内T32End点的调用门(DPL=3)
ToT32GateB Gate <T32End,T32Code_Sel,,AT386CGate+DPL3,>;指向显示子程序代码段的调用门(DPL=3)
ToEchoGate Gate <EchoSub,Echo_Sel3,,AT386CGate+DPL3,>;----------------------------------------------------------------------------
DemoLDTLen =$-DemoLDT
;----------------------------------------------------------------------------;指向过渡代码段内T32Begin点的调用门的选择子
ToT32A_Sel = ToT32GateA-DemoLDT+TIL
;指向过渡代码段内T32End点的调用门的选择子
ToT32B_Sel = ToT32GateB-DemoLDT+TIL
;显示子程序调用门的选择子
ToEcho_Sel = ToEchoGate-DemoLDT+TIL
;----------------------------------------------------------------------------
DemoLDTSeg ENDS;局部描述符表段定义结束
;----------------------------------------------------------------------------
DemoTSSSeg SEGMENT PARA USE16 ;任务状态段TSS
;----------------------------------------------------------------------------
DD 0 ;Back
DD DemoStack0Len ;0级堆栈指针
DD DemoStack0_Sel ;初始化
DD DemoStack1Len ;1级堆栈指针
DD DemoStack1_Sel ;初始化
DD 0 ;2级堆栈指针
DD 0 ;未初始化
DD 0 ;CR3
DD 0 ;EIP
DD 0 ;EFLAGS
DD 0 ;EAX
DD 0 ;ECX
DD 0 ;EDX
DD 0 ;EBX
DD 0 ;ESP
DD 0 ;EBP
DD 0 ;ESI
DD 0 ;EDI
DD 0 ;ES
DD 0 ;CS
DD 0 ;SS
DD 0 ;DS
DD 0 ;FS
DD 0 ;GS
DD DemoLDT_Sel ;LDT
DW 0 ;调试陷阱标志
DW $+2 ;指向I/O许可位图
DW 0ffffh ;I/O许可位图结束标志
;----------------------------------------------------------------------------
DemoTSSLen = $
;----------------------------------------------------------------------------
DemoTSSSeg ENDS ;任务状态段TSS结束
;----------------------------------------------------------------------------
DemoStack0Seg SEGMENT DWORD STACK USE32 ;0级堆栈段(32位段)
DemoStack0Len = 512
DB DemoStack0Len DUP(?)
DemoStack0Seg ENDS ;0级堆栈段结束
;----------------------------------------------------------------------------
DemoStack1Seg SEGMENT DWORD STACK USE32 ;1级堆栈段(32位段)
DemoStack1Len = 512
DB DemoStack1Len DUP(?)
DemoStack1Seg ENDS ;1级堆栈段结束
;----------------------------------------------------------------------------
DemoStack3Seg SEGMENT DWORD STACK USE16 ;3级堆栈段(16位段)
DemoStack3Len = 512
DB DemoStack3Len DUP(?)
DemoStack3Seg ENDS ;3级堆栈段结束
;----------------------------------------------------------------------------
EchoSubRSeg SEGMENT PARA USE32 ;显示子程序代码段(32位,1级)
ASSUME CS:EchoSubRSeg
;----------------------------------------------------------------------------
Message DB 'CPL=',0 ;显示信息(该代码段可读) 当前运行的级别 不是dpl
;----------------------------------------------------------------------------
EchoSub PROC FAR
cld //清除方向 正向操作
push ebp //esp一直指向栈顶 ebp指向存储堆栈中值的位置
mov ebp,esp //1级堆栈
mov ax,Echo_Sel1 ;该代码段是可读段
mov ds,ax ;采用RPL=1的选择子 ds:esi源指针 显示子程序代码段的message位置
mov ax,Video_Sel
mov es,ax
mov edi,1996 //es:edi 目的指针 指向显示缓冲区段
mov esi,OFFSET Message
mov ah,4eh ;置显示属性(红底黄字) 中断功能号码
EchoSub1: lodsb //加载ds:si字节到al
or al,al
jz EchoSub2
stosw //写ax字到es:di
jmp EchoSub1
EchoSub2: mov eax,[ebp+8] ;从堆栈中取调用程序的选择子 加8的原因是什么?
and al,3 ;调用程序的CPL在CS的RPL字段
add al,'0'
mov ah,4eh ;置显示属性(红底黄字)
stosw
pop ebp
retf
EchoSub ENDP
;----------------------------------------------------------------------------
EchoSubRLen = $
;----------------------------------------------------------------------------
EchoSubRSeg ENDS ;显示子程序代码段结束
;----------------------------------------------------------------------------
DemoCodeSeg SEGMENTPARAUSE32;32位代码段(3级) 关键位置ASSUMECS:DemoCodeSeg
;----------------------------------------------------------------------------
DemoBegin PROCFARCALL32 ToEcho_Sel,0;显示当前特权级(变换到1级)CALL32 ToT32B_Sel,0;转到过渡代码段(变换到0级)
DemoBegin ENDP
DemoCodeLen =$;----------------------------------------------------------------------------
DemoCodeSeg ENDS;32位代码段结束
;----------------------------------------------------------------------------
T32CodeSeg SEGMENTPARAUSE32;32位过渡代码段(0级)ASSUMECS:T32CodeSeg
;----------------------------------------------------------------------------
T32Begin PROCFAR //创建一个 返回的环境 返回到3级权限的代码段movax,DemoStack0_Sel ;建立0级堆栈movss,axmovesp,DemoStack0Len
pushDWORDPTR DemoStack3_Sel ;压入3级堆栈指针push DemoStack3Len
pushDWORDPTR DemoCode_SEL ;压入入口点pushOFFSET DemoBegin
retf;利用RET实现转3级的演示代码
T32Begin ENDP;----------------------------------------------------------------------------
T32End PROCFARJUMP32 TempCode_Sel,<OFFSET ToReal> //0级
T32End ENDP
T32CodeLen =$;----------------------------------------------------------------------------
T32CodeSeg ENDS
;----------------------------------------------------------------------------
TempCodeSeg SEGMENTPARAUSE16;16位临时代码段(0级)ASSUMECS:TempCodeSeg
;----------------------------------------------------------------------------
Virtual PROCFARmovax,DemoTSS_Sel ;装载TRltrax //选择子装载到tr寄存器movax,DemoLDT_Sel ;装载LDTRlldtax //选择子进ldt寄存器JUMP16 ToT32A_Sel,0 ;通过调用门转过渡段 偏移无用ToReal:movax,Normal_Sel ;准备切换回实模式 准备工作movds,axmoves,axmovfs,axmovgs,axmovss,axmoveax,cr0andal,11111110bmovcr0,eaxJUMP16<SEG Real>,<OFFSET Real>
Virtual ENDP
TempCodeLen =$;----------------------------------------------------------------------------
TempCodeSeg ENDS
;============================================================================
RDataSeg SEGMENT PARA USE16 ;实方式数据段
VGDTR PDesc <GDTLen-1,> ;GDT伪描述符
SPVar DW ? ;用于保存实方式下的SP
SSVar DW ? ;用于保存实方式下的SS
RDataSeg ENDS
;----------------------------------------------------------------------------
RCodeSeg SEGMENT PARA USE16
ASSUME CS:RCodeSeg,DS:RDataSeg 入口
;----------------------------------------------------------------------------
Start PROC
mov ax,RDataSeg
mov ds,ax
cld
CALL InitGDT ;初始化全局描述符表GDT
mov ax,DemoLDTSeg
mov fs,ax
mov si,OFFSET DemoLDT
mov cx,DemoLDNum
CALL InitLDT ;初始化局部描述符表LDT
mov SSVar,ss
mov SPVar,sp
lgdt QWORD PTR VGDTR ;装载GDTR并切换到保护方式
cli
mov eax,cr0
or al,1
mov cr0,eax
JUMP16 <TempCode_Sel>,<OFFSET Virtual>
Real: mov ax,RDataSeg
mov ds,ax
lss sp,DWORD PTR SPVar ;又回到实方式
sti
mov ax,4c00h
int 21h
Start ENDP
;----------------------------------------------------------------------------
InitGDT PROC
push ds
mov ax,GDTSeg
mov ds,ax
mov cx,GDNum
mov si,OFFSET EFFGDT
InitG: mov ax,[si].BaseL
movzx eax,ax
shl eax,4
shld edx,eax,16
mov WORD PTR [si].BaseL,ax
mov BYTE PTR [si].BaseM,dl
mov BYTE PTR [si].BaseH,dh
add si,SIZE Desc
loop InitG
pop ds
mov bx,16
mov ax,GDTSeg
mul bx
mov WORD PTR VGDTR.Base,ax
mov WORD PTR VGDTR.Base+2,dx
ret
InitGDT ENDP
;----------------------------------------------------------------------------
;入口参数:FS:SI=第一个要初始化的描述符,CX=要初始化的描述符数
;----------------------------------------------------------------------------
InitLDT PROC
ILDT: mov ax,WORD PTR FS:[si].BaseL
movzx eax,ax
shl eax,4
shld edx,eax,16
mov WORD PTR fs:[si].BaseL,ax
mov BYTE PTR fs:[si].BaseM,dl
mov BYTE PTR fs:[si].BaseH,dh
add si,SIZE Desc
loop ILDT
ret
InitLDT ENDP
;----------------------------------------------------------------------------
RCodeSeg ENDS
END Start
:切换后显示原任务的挂起点eip的值。该实例演示内容包括:直接通过TSS段的任务切换,通过任务门的任务切换,任务内
权级变换及参数传递。
;名称:ASM5.ASM
;功能:演示任务切换和任务内有特权级变换的转移
;编译:TASM ASM5.ASM
;链接:TLINK /32 ASM5.OBJ
;----------------------------------------------------------------------------
INCLUDE 386SCD.INC
;----------------------------------------------------------------------------
GDTSeg SEGMENTPARAUSE16;全局描述符表数据段(16位);----------------------------------------------------------------------------;全局描述符表
GDT LABELBYTE;空描述符
DUMMY Desc <> ;规范段描述符及选择子
Normal Desc <0ffffh,,,ATDW,,>
Normal_Sel = Normal-GDT
;视频缓冲区段描述符(DPL=3)及选择子
VideoBuf Desc <0ffffh,8000h,0bh,ATDW+DPL3,,>
Video_Sel = VideoBuf-GDT
;----------------------------------------------------------------------------
EFFGDT LABELBYTE ;演示任务的局部描述符表段的描述符及选择子
DemoLDTab Desc <DemoLDTLen-1,DemoLDTSeg,,ATLDT,,>
DemoLDT_Sel = DemoLDTab-GDT
;演示任务的任务状态段描述符及选择子
DemoTSS Desc <DemoTSSLen-1,DemoTSSSeg,,AT386TSS,,>
DemoTSS_Sel = DemoTSS-GDT
;临时任务的任务状态段描述符及选择子
TempTSS Desc <TempTSSLen-1,TempTSSSeg,,AT386TSS+DPL2,,>
TempTSS_Sel = TempTSS-GDT
;临时代码段描述符及选择子
TempCode Desc <0ffffh,TempCodeSeg,,ATCE,,>
TempCode_Sel = TempCode-GDT
;子程序代码段描述符及选择子
SubR Desc <SubRLen-1,SubRSeg,,ATCE,D32,>
SubR_Sel = SubR-GDT
;----------------------------------------------------------------------------
GDNum =($-EFFGDT)/(SIZE Desc);需处理基地址的描述符个数 $指什么 gdnum变量位置 还是gdt表中
描述符分配指示的下一个位置 后面的说明似乎说的通
GDTLen =$-GDT ;全局描述符表长度;----------------------------------------------------------------------------
GDTSeg ENDS;全局描述符表段定义结束
;----------------------------------------------------------------------------
DemoLDTSeg SEGMENTPARAUSE16;局部描述符表数据段(16位);----------------------------------------------------------------------------
DemoLDT LABELBYTE;局部描述符表 //演示任务用到的ldt;0级堆栈段描述符(32位段)及选择子
DemoStack0 Desc <DemoStack0Len-1,DemoStack0Seg,,ATDW,D32,> //演示任务的两个堆栈
DemoStack0_Sel = DemoStack0-DemoLDT+TIL
;2级堆栈段描述符(32位段)及选择子
DemoStack2 Desc <DemoStack2Len-1,DemoStack2Seg,,ATDW+DPL2,D32,>
DemoStack2_Sel = DemoStack2-DemoLDT+TIL+RPL2
;演示任务代码段描述符(32位段,DPL=2)及选择子
DemoCode Desc <DemoCodeLen-1,DemoCodeSeg,,ATCE+DPL2,D32,> //演示任务的代码
DemoCode_Sel = DemoCode-DemoLDT+TIL+RPL2
;演示任务数据段描述符(32位段,DPL=3)及选择子
DemoData Desc <DemoDataLen-1,DemoDataSeg,,ATDW+DPL3,D32,> //演示任务的数据
DemoData_Sel = DemoData-DemoLDT+TIL ;把LDT作为普通数据段描述的描述符(DPL=2)及选择子
ToDLDT Desc <DemoLDTLen-1,DemoLDTSeg,,ATDW+DPL2,,> //ldt表作为普通数据段的别名
ToDLDT_Sel = ToDLDT-DemoLDT+TIL
;把TSS作为普通数据段描述的描述符(DPL=2)及选择子
ToTTSS Desc <TempTSSLen-1,TempTSSSeg,,ATDW+DPL2,,> //临时任务作为普通数据段的别名
ToTTSS_Sel = ToTTSS-DemoLDT+TIL
;----------------------------------------------------------------------------
DemoLDNum =($-DemoLDT)/(SIZE Desc);需处理基地址的LDT描述符数;----------------------------------------------------------------------------;指向子程序SubRB代码段的调用门(DPL=3)及选择子
ToSubR Gate <SubRB,SubR_Sel,,AT386CGate+DPL3,> //两个门
ToSubR_Sel = ToSubR-DemoLDT+TIL+RPL2
;指向临时任务Temp的任务门(DPL=3)及选择子 //任务门 选择子指向任务
ToTempT Gate <,TempTSS_Sel,,ATTaskGate+DPL3,>
ToTempT_Sel = ToTempT-DemoLDT+TIL
;----------------------------------------------------------------------------
DemoLDTLen =$-DemoLDT
;----------------------------------------------------------------------------
DemoLDTSeg ENDS;局部描述符表段定义结束
;----------------------------------------------------------------------------
DemoTSSSeg SEGMENT PARA USE16 ;任务状态段TSS
;----------------------------------------------------------------------------
DD 0 ;链接字
DD DemoStack0Len ;0级堆栈指针
DW DemoStack0_Sel,0 ;0级堆栈选择子
DD 0 ;1级堆栈指针(实例不使用)
DW 0,0 ;1级堆栈选择子(实例不使用)
DD 0 ;2级堆栈指针
DW 0,0 ;2级堆栈选择子
DD 0 ;CR3
DW DemoBegin,0 ;EIP
DD 0 ;EFLAGS
DD 0 ;EAX
DD 0 ;ECX
DD 0 ;EDX
DD 0 ;EBX
DD DemoStack2Len ;ESP
DD 0 ;EBP
DD 0 ;ESI
DD 1986 ;EDI
DW Video_Sel,0 ;ES
DW DemoCode_Sel,0 ;CS
DW DemoStack2_Sel,0 ;SS
DW DemoData_Sel,0 ;DS
DW ToDLDT_Sel,0 ;FS
DW ToTTSS_Sel,0 ;GS
DW DemoLDT_Sel,0 ;LDTR
DW 0 ;调试陷阱标志
DW $+2 ;指向I/O许可位图
DB 0ffh ;I/O许可位图结束标志
DemoTSSLen = $
;----------------------------------------------------------------------------
DemoTSSSeg ENDS ;任务状态段TSS结束
;----------------------------------------------------------------------------
DemoStack0Seg SEGMENT PARA USE32 ;演示任务0级堆栈段(32位段)
DemoStack0Len = 1024
DB DemoStack0Len DUP(0)
DemoStack0Seg ENDS ;演示任务0级堆栈段结束
;----------------------------------------------------------------------------
DemoStack2Seg SEGMENT PARA USE32 ;演示任务2级堆栈段(32位段)
DemoStack2Len = 512
DB DemoStack2Len DUP(0)
DemoStack2Seg ENDS ;演示任务2级堆栈段结束
;----------------------------------------------------------------------------
DemoDataSeg SEGMENT PARA USE32 ;演示任务数据段(32位段)
Message DB 'Value=',0
DemoDataLen = $
DemoDataSeg ENDS ;演示任务数据段结束
;----------------------------------------------------------------------------
SubRSeg SEGMENT PARA USE32 ;子程序代码段(32位)
ASSUME CS:SubRSeg
;----------------------------------------------------------------------------
SubRB PROC FAR
push ebp
mov ebp,esp
pushad ;保护现场
mov esi,DWORD PTR [ebp+12] ;从0级栈中取出显示串偏移
mov ah,4ah ;设置显示属性
jmp SHORT SubR2
SubR1: stosw
SubR2: lodsb
or al,al
jnz SubR1
mov ah,4eh ;设置显示属性
mov edx,DWORD PTR [ebp+16] ;从0级栈中取出显示值
mov ecx,8
SubR3: rol edx,4
mov al,dl
call HToASCII
stosw
loop SubR3
popad
pop ebp
ret 8
SubRB ENDP
;----------------------------------------------------------------------------
HToASCII PROC
and al,0fh
add al,90h
daa
adc al,40h
daa
ret
HToASCII ENDP
;----------------------------------------------------------------------------
SubRLen = $
SubRSeg ENDS ;子程序代码段结束
;----------------------------------------------------------------------------
DemoCodeSeg SEGMENT PARA USE32 ;演示任务的32位代码段
ASSUME CS:DemoCodeSeg,DS:DemoDataSeg
;----------------------------------------------------------------------------
DemoBegin PROC FAR
;把要复制的参数个数置入调用门
mov BYTE PTR fs:ToSubR.DCount,2
;向2级堆栈中压入参数
push DWORD PTR gs:TempTask.TREIP
push OFFSET Message
;通过调用门调用SubRB
CALL32 ToSubR_Sel,0
;把指向规范数据段描述符的选择子填入临时任务TSS
ASSUME DS:TempTSSSeg
push gs
pop ds
mov ax,Normal_Sel
mov WORD PTR TempTask.TRDS,ax
mov WORD PTR TempTask.TRES,ax
mov WORD PTR TempTask.TRFS,ax
mov WORD PTR TempTask.TRGS,ax
mov WORD PTR TempTask.TRSS,ax
;通过任务门切换到临时任务
JUMP32 ToTempT_Sel,0
jmp DemoBegin
DemoBegin ENDP
DemoCodeLen = $
;----------------------------------------------------------------------------
DemoCodeSeg ENDS ;演示任务的32位代码段结束
;----------------------------------------------------------------------------
TempTSSSeg SEGMENT PARA USE16 ;临时任务的任务状态段TSS
TempTask TSS <>
DB 0ffh ;I/O许可位图结束标志
TempTSSLen = $
TempTSSSeg ENDS
;----------------------------------------------------------------------------
TempCodeSeg SEGMENTPARAUSE16;临时任务的代码段ASSUMECS:TempCodeSeg
;----------------------------------------------------------------------------
Virtual PROCFARmovax,TempTSS_Sel ;装载TRltrax JUMP16 DemoTSS_Sel,0 ;直接切换到演示任务clts;清任务切换标志moveax,cr0;准备返回实模式andal,11111110bmovcr0,eaxJUMP16<SEG Real>,<OFFSET Real>
Virtual ENDP;----------------------------------------------------------------------------
TempCodeSeg ENDS
;============================================================================
RDataSeg SEGMENT PARA USE16 ;实方式数据段
VGDTR PDesc <GDTLen-1,> ;GDT伪描述符
SPVar DW ? ;用于保存实方式下的SP
SSVar DW ? ;用于保存实方式下的SS
RDataSeg ENDS
;----------------------------------------------------------------------------
RCodeSeg SEGMENT PARA USE16
ASSUME CS:RCodeSeg,DS:RDataSeg,ES:RDataSeg
;----------------------------------------------------------------------------
Start PROC //程序入口位置
mov ax,RDataSeg
mov ds,ax
cld
call InitGDT ;初始化全局描述符表GDT
mov ax,DemoLDTSeg
mov fs,ax
mov si,OFFSET DemoLDT
mov cx,DemoLDNum
call InitLDT ;初始化局部描述符表LDT
mov SSVar,ss
mov SPVar,sp
lgdt QWORD PTR VGDTR ;装载GDTR并切换到保护方式
cli
mov eax,cr0
or al,1
mov cr0,eax
JUMP16 <TempCode_Sel>,<OFFSET Virtual>
Real: mov ax,RDataSeg
mov ds,ax
lss sp,DWORD PTR SPVar ;又回到实方式
sti
mov ax,4c00h
int 21h
Start ENDP
;----------------------------------------------------------------------------
InitGDT PROC
push ds
mov ax,GDTSeg
mov ds,ax
mov cx,GDNum
mov si,OFFSET EFFGDT
InitG: mov ax,[si].BaseL
movzx eax,ax
shl eax,4
shld edx,eax,16
mov WORD PTR [si].BaseL,ax
mov BYTE PTR [si].BaseM,dl
mov BYTE PTR [si].BaseH,dh
add si,SIZE Desc
loop InitG
pop ds
mov bx,16
mov ax,GDTSeg
mul bx
mov WORD PTR VGDTR.Base,ax
mov WORD PTR VGDTR.Base+2,dx
ret
InitGDT ENDP
;----------------------------------------------------------------------------
;入口参数:FS:SI=第一个要初始化的描述符,CX=要初始化的描述符数
;----------------------------------------------------------------------------
InitLDT PROC
mov ax,WORD PTR fs:[si].BaseL
movzx eax,ax
shl eax,4
shld edx,eax,16
mov WORD PTR fs:[si].BaseL,ax
mov BYTE PTR fs:[si].BaseM,dl
mov BYTE PTR fs:[si].BaseH,dh
add si,SIZE Desc
loop InitLDT
ret
InitLDT ENDP
;----------------------------------------------------------------------------
RCodeSeg ENDS
END Start
x86保护模式-六 控制转移的更多相关文章
- x86保护模式-七中断和异常
x86保护模式-七中断和异常 386相比较之前的cpu 增强了中断处理能力 并且引入了 异常概念 一 80386的中断和异常 为了支持多任务和虚拟存储器等功能,386把外部中断称为中断 ...
- x86保护模式 任务状态段和控制门
x86保护模式 任务状态段和控制门 每个任务都有一个任务状态段TSS 用于保存任务的有关信息 在任务内权变和任务切换时 需要用到这些信息 任务内权变的转移和任务切换 一 ...
- x86保护模式 实模式与保护模式切换实例
x86保护模式 实模式与保护模式切换实例 实例一 逻辑功能 以十六进制数的形式显示从内存地址110000h开始的256个字节的值 实现步骤: 1 切换保护方式的准备 2. 切换到保 ...
- x86 保护模式 十 分页管理机制
x86 保护模式 十 分页管理机制 8.386开始支持分页管理机制 段机制实现虚拟地址到线性地址的转换,分页机制实现线性地址到物理地址的转换.如果不启用分页,那么线性就是物理地址 一 分页管 ...
- X86保护模式 八操作系统类指令
X86保护模式 八操作系统类指令 通常在操作系统代码中使用,应用程序中不应用这些指令 指令分为三种:实模式指令,任何权级下使用的指令.实模式权级0下可执行的指令和仅在保护模式下执行的指令 一 实模 ...
- x86保护模式 二 分段管理机制
分段管理机制 段选择子和偏移地址的二维虚拟地址转换为一维的线性地址 一 段定义和虚拟地址到线性地址的转换 三个参数定义段:段基地址 段界限 和段属性 同时也是段描述符的结构 段基地址为 ...
- x86保护模式 控制寄存器和系统地址寄存器
控制寄存器和系统地址寄存器 控制寄存器 crx cr0 指示cpu工作方式的控制位 包含启用和禁止分页管理机制的控制位 包含控制浮点协处理器操作的控制位 注意必须为0的位 cr2和c ...
- 硬盘和显卡的访问与控制(一)——《x86汇编语言:从实模式到保护模式》读书笔记01
本文是<x86汇编语言:从实模式到保护模式>(电子工业出版社)的读书实验笔记. 这篇文章我们先不分析代码,而是说一下在Bochs环境下如何看到实验结果. 需要的源码文件 第一个文件是加载程 ...
- ASM:《X86汇编语言-从实模式到保护模式》第16章:Intel处理器的分页机制和动态页面分配
第16章讲的是分页机制和动态页面分配的问题,说实话这个一开始接触是会把人绕晕的,但是这个的确太重要了,有了分页机制内存管理就变得很简单,而且能直接实现平坦模式. ★PART1:Intel X86基础分 ...
随机推荐
- arcengine,c# 二次开发
plication.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); ESRI.ArcGIS.Ru ...
- nodejs 快要变成爬虫界的王者
nodejs 快要变成爬虫界的王者 爬虫这东西是很多数据采集必须要的东西. 但是现在随着网页不断发展,已经出现了出单纯的网页,到 ajax 网页, 再到 spa , 再到 websocket 应用,一 ...
- Sass基本特性
Sass扩展/继承@extend 代码的继承,声明方式:.class;调用方式:@extend 如: .btn { border: 1px solid #ccc; padding: 6px 10px; ...
- vue+webpack+VS Code入门简单的项目配置
为了方便,这边的编译器选择的是VS Code (Visual Studio Code); 打开VS Code,选择好自己的工作空间,然后新建一个文件夹作为我们项目的文件夹,然后,show time: ...
- m_pConnection.CreateInstance( "ADODB.Connection ") 执行错误 结果总是为NULL
今天下午搞了下项目 数据库操作模块,总是出现m_pConnection.CreateInstance( "ADODB.Connection ") 执行错误,即m_pConnecti ...
- 小白学phoneGap《构建跨平台APP:phoneGap移动应用实战》连载二(生命周期)
4.1 什么是生命周期 想要真正地理解PhoneGap应用开发的内涵,首先需要理解什么是生命周期.这在字面上其实非常容易理解,一个应用从开始运行被手机加载到应用被退出之间的过程就称之为一个生命周期.为 ...
- SQL 视图、事务
假设看多个不同的表 select *from student ,score,course,teacher 有重复的 改为select student.Sno,sname,ssex,sbirthd ...
- 打通C/4HANA和S/4HANA的一个原型开发:智能服务创新案例
今年6月SAP发布C/4HANA之后,有顾问朋友们在微信公众号后台留言,询问C/4HANA如何同SAP的数字化核心S/4HANA系统结合起来,从而打通企业的前后端业务,帮助企业实现数字化转型. 有的顾 ...
- 【page-monitor 前端自动化 中篇】 源码分析
转载文章:来源(靠谱崔小拽) 上篇中初探了page-monitor的一些功能和在前端自动化测试方面的可行性,本篇主要分析下page-monitor的实现方式和源码. mode-module简介 pag ...
- 【page-monitor 前端自动化 上篇】初步调研
转载文章:来源(靠谱崔小拽) 前端自动化测试主要在于:变化快,不稳定,兼容性复杂:故而,想通过较低的成本维护较为通用的自动化case比较困难.本文旨在通过page-monitor获取和分析dom结构, ...