📄 386pl6.asm
字号:
;**********************************************************************
; 保护方式下的编程示例 陈家祺 1996.6
; ---局部描述符的应用方法
;
; 1 实方式进入保护方式及保护方式返回实方式方法
; 2 保护方式下的指令操作(直接写屏,满屏显示'A')
; * 环境: MASM6.0,TASM3.0, DOS6.2,DOS7.0, 80486主机, Himem.sys,
; 不能装栽Emm386.exe,因为Emm386.exe装载后,cpu处于虚拟8086方式下
;**********************************************************************
.MODEL small
.386p
jumpfar MACRO _sel,_offset ;保护方式跳转指令
db 0eah ;功能与目的:
dw _offset ; 1. 清除指令队列
dw _sel ; 2. 选择字sel -> CS
ENDM
desc struc ;GDT全局描述符的结构
limit_15_0 dw ? ;界限15-0
base_15_0 dw ? ;基地址15-0
base_23_16 db ? ;基地址23-16
access db ? ;访问权
gran db ? ;粒度,类型,界限19-16
base_31_24 db ? ;基地址31-24
desc ends
.DATA ;数据段
char db 'A' ;要显示的字符
save_ss dw ? ;SS保存区
; ***** GDT 描述符表 *****
gdt_def LABEL BYTE
desc < > ;必须为空描述符
gdt_cs desc <0ffffh, ? , ? ,9ah,0fh,00h> ;00000 - fffffH (1Mb)
gdt_ds desc < 000fh, ? , ? ,92h,00h,00h> ;00000 - 0000fH (16b)
gdt_ss desc < 0000h, ? , ? ,96h,40h,00h>
gdt_rel desc < 0ffffh, ? , ? ,92h,00h,00h> ;00000 - 0ffffH (64Kb)
gdt_ldt1 desc <ldt1_end-ldt1_def, ?, ?,82h,00h, ? > ;LDT1表的描述符
gdt_ldt2 desc <ldt2_end-ldt2_def, ?, ?,82h,00h, ? > ;LDT2表的描述符
gdt_end LABEL BYTE
;设置GDTR用
gdtsize dw gdt_end-gdt_def ;GDT描述符表的长度
gdtaddr dd ? ;GDT描述符表的线性基地址31-0
; ***** LDT1 描述符表 *****
ldt1_def LABEL BYTE
ldt1_uds desc <0ffffh,8000h,0bh,92h,00h,00h> ;00000 - 0ffffH (64Kb)
ldt1_end LABEL BYTE
; ***** LDT2 描述符表 *****
ldt2_def LABEL BYTE
ldt2_uds desc <0ffffh,0000h,00h,92h,0fh,00h> ;00000 - fffffH (1Mb)
ldt2_end LABEL BYTE
gdt_cs_sel equ 08h ; CS选择字
gdt_ds_sel equ 10h ; DS选择字
gdt_ss_sel equ 18h ; SS选择字
gdt_rel_sel equ 20h ; DS/SS选择字(DOS)
ldt1_sel equ 28h ; LDT1表的选择字
ldt2_sel equ 30h ; LDT2表的选择字
ldt1_uds_sel equ 04h ; LDT1的用户DS选择字
ldt2_uds_sel equ 04h ; LDT2的用户DS选择字
.STACK ; 堆栈段, 堆栈空间为1024
.CODE ; 代码段
.STARTUP ; 初始化DS,SS和SP
cli
call prot ; 调用保护方式操作子程
sti
mov ah,0
int 16h ; BIOS键盘中断调用,等待按键!
.EXIT 0 ; 返回DOS!
prot proc near
mov save_ss,ss ; 保存DOS堆栈段的段地址
mov di,offset gdt_cs ; 设置CS描述符的基地址
mov ax,cs ; 将实方式DOS的段地址
call copy_desc ; 换算为线性基地址
mov di,offset gdt_ds ; 设置DS描述符的基地址
mov ax,ds ; 将实方式DOS的段地址
call copy_desc ; 换算为线性基地址
mov di,offset gdt_ss ; 设置SS描述符的基地址
mov ax,ss ; 将实方式DOS的段地址
call copy_desc ; 换算为线性基地址
mov eax,ds ; 计算GDT描述符表的线性基地址31-0
shl eax,4
add eax,offset gdt_def
mov gdtaddr,eax
; ***** 设置LDT1描述符的基地址 *****
mov di,offset gdt_ldt1 ; 设置DS描述符的基地址
mov bx,offset ldt1_def ; 将实模式DOS的段地址
call copy_ldt_desc ; 换算为线性基地址
; ***** 设置LDT1描述符的基地址 *****
mov di,offset gdt_ldt2 ; 设置DS描述符的基地址
mov bx,offset ldt2_def ; 将实模式DOS的段地址
call copy_ldt_desc ; 换算为线性基地址
LGDT fword ptr gdtsize ; 装载GDT给GDTR寄存器
mov eax,cr0
or al,1 ; 设置控制寄存器CR0的PE位
mov cr0,eax ; 进入保护方式
jumpfar gdt_cs_sel,prot_user ; 清除指令队列,
prot_user: ; CS=gdt_cs_sel(保护方式码段)
mov ax,gdt_ss_sel ; 设置保护方式的堆栈段
mov ss,ax ;
call vram_disp ; 调用保护方式下显示子程
mov ax,gdt_rel_sel ; 设置实方式数据段的界限与属性
mov ds,ax ;
mov ss,ax ; 设置实方式堆栈段的界限与属性
mov eax,cr0
and al,0feh ;清除控制寄存器CR0的PE位
mov cr0,eax ;切换到实方式
jmp far ptr real_mode ;清除指令队列,CS=_TEXT(实方式码段)
real_mode:
mov ax,_DATA
mov ds,ax ;恢复DOS数据段的段地址
mov ss,save_ss ;恢复DOS堆栈段的段地址
ret
prot endp
vram_disp proc near ; 保护模式下直接写屏子程
pushad
mov ax,gdt_ds_sel ; 设置GDT的数据段
mov ds,ax
mov dl,char ; 取出要显示字符'A'
mov ax,ldt1_sel ; 设置LDT1的选择子
LLDT ax ; 装载LDT1 -> LDTR
mov ax,ldt1_uds_sel ; 设置LDT1的数据段
mov ds,ax
mov ebx,000000h
mov ah,17h ; 字符颜色属性,蓝底白字
mov al,dl ; 显示字符'A'
mov [ebx],ax
mov ax,ldt2_sel ; 设置LDT2的选择子
LLDT ax ; 装载LDT2 -> LDTR
mov ax,ldt2_uds_sel ; 设置LDT1的数据段
mov ds,ax
mov ebx,0b8002h
mov ah,1eh ; 字符颜色属性,蓝底黄字
mov al,dl ; 显示字符'A'
mov [ebx],ax
popad
ret
vram_disp endp
copy_desc proc near ;设置GDT描述符的基地址
mov cx,10h ;将实方式DOS的段地址
mul cx ;换算为24位线性地址
mov [di].base_15_0,ax ;存入GDT描述符中
mov [di].base_23_16 ,dl
ret
copy_desc endp
copy_ldt_desc proc near ; 设置LDT描述符的基地址
mov ax,ds ; 将LDT表的基地址
mov cx,10h ; 换算为32位线性地址
mul cx
add ax,bx
adc dx,0
mov [di].base_15_0,ax ; 存入描述符中
mov [di].base_23_16 ,dl
mov [di].base_31_24 ,dh
ret
copy_ldt_desc endp
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -