📄 386p6a.asm
字号:
;**********************************************************************
; 保护方式下的编程示例 陈家祺 1996.6
; ---全局描述符的应用方法
;
; 1 实方式进入保护方式及保护方式返回实方式方法
; 2 保护方式下的指令操作(直接写屏,满屏显示'A')
; * 环境: MASM6.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
set_ba MACRO _seg,_gdt_seg ; 设置_gdt_seg段描述符的基地址
mov ax,_seg ; 将实方式DOS的段地址_seg换算为24位线性基地址
mov cx,10h
mul cx ; dl.ax = _seg*16
mov di,offset _gdt_seg ; di = _gdt_seg描述符的偏移地址
mov [di].base_15_0,ax ; 存入GDT描述符中
mov [di].base_23_16 ,dl
ENDM
mov_s MACRO _seg,_sel ; 设置段寄存器
mov ax,_sel
mov _seg,ax
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' ; 要显示的字符
; ***** GDT 描述符表 *****
gdt_def LABEL BYTE
desc < > ;必须为空描述符
gdt_cs desc <_end+100h, ?, ? ,9ah,00h,00h> ;00000 - 001D9H ( )
gdt_ds desc < 000fh, ? , ? ,92h,00h,00h> ;00000 - 0000fH (16b)
gdt_ss desc < 0000h, ? , ? ,96h,40h,00h>
gdt_uds desc <00fffh,8000h,0bh,92h,00h,00h> ;00000 - 00fffH ( 4Kb)
gdt_cs_r desc <0ffffh, ? , ? ,9ah,00h,00h> ;00000 - 0ffffH (64Kb)
gdt_r desc <0ffffh, ? , ? ,92h,00h,00h> ;00000 - 0ffffH (64Kb)
gdt_end LABEL BYTE
; 设置GDTR用
gdtsize dw gdt_end-gdt_def ; GDT描述符表的长度
gdtaddr dd ? ; GDT描述符表的线性基地址31-0
gdt_cs_sel equ 08h ; CS选择字
gdt_ds_sel equ 10h ; DS选择字
gdt_ss_sel equ 18h ; SS选择字
gdt_uds_sel equ 20h ; 用户DS选择字
gdt_cs_r_sel equ 28h ; CS选择字 (具有实方式的界限与属性)
gdt_r_sel equ 30h ; DS/SS选择字(具有实方式的界限与属性)
.STACK ; 堆栈段, 堆栈空间为1024
.CODE ; 代码段
.STARTUP ; 初始化DS,SS和SP
cli
call prot ; 调用保护方式操作子程
sti
mov ah,0
int 16h ; BIOS键盘中断调用,等待按键!
.EXIT 0 ; 返回DOS!
prot proc near
set_ba cs, gdt_cs ; 设置CS描述符的基地址
set_ba ds, gdt_ds ; 设置DS描述符的基地址
set_ba ss, gdt_ss ; 设置SS描述符的基地址
set_ba cs, gdt_cs_r ; 设置CS_r描述符的基地址
mov eax,ds ; 计算GDT描述符表的线性基地址31-0
shl eax,4
add eax,offset gdt_def
mov gdtaddr,eax
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_s ss,gdt_ss_sel ; 设置保护方式的堆栈段
call vram_disp ; 调用保护方式下显示子程
jumpfar gdt_cs_r_sel,next ; CS=gdt_cs_r_sel
next: ; 设置具有实方式代码段的界限与属性
mov_s ds,gdt_r_sel ; 设置实方式数据段的界限与属性
mov_s ss,gdt_r_sel ; 设置实方式堆栈段的界限与属性
mov eax,cr0
and al,0feh ; 清除控制寄存器CR0的PE位
mov cr0,eax ; 切换到实方式
jmp far ptr real_mode ; 清除指令队列,CS=_TEXT(实方式码段)
real_mode:
mov_s ds,DGROUP ; 恢复DOS数据段的段地址
mov_s ss,DGROUP ; 恢复DOS堆栈段的段地址
ret
prot endp
vram_disp proc near ; 保护方式下直接写屏子程
pushad
mov_s ds,gdt_ds_sel ; 设置保护方式的堆栈段
mov dl,char ; 显示字符'A'
mov_s ds,gdt_uds_sel ; 设置保护方式的堆栈段
mov cx,80*25 ; 满屏字符数.
mov ebx,00000000h
mov ah,17h ; 字符颜色属性,蓝底白字
mov al,dl ; 显示字符'A'
l1: mov [ebx],ax
add ebx,2
loop l1
popad
ret
vram_disp endp
_end LABEL BYTE
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -