📄 386sps5.asm
字号:
;**********************************************************************
; 保护模式下的编程示例 陈家祺 1996.6
;
; 1 实模式进入保护模式及保护模式返回实模式方法
; 2 保护模式下的指令操作(直接写屏,满屏显示'A')
; * 环境: MASM6.0,TASM3.0, DOS6.2, 80486主机, Himem.sys, 不能装载Emm386.exe
;**********************************************************************
jumpfar MACRO segf,offsetf ;保护模式跳转指令
db 0eah ;功能与目的:
dw (offset offsetf) ; 1. 清除指令队列
dw segf ; 2. segf -> 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
dos_data segment word public 'data' ;数据段
save_gdt dw 4 dup (?) ;GDT保存区
save_ss dw ? ;SS保存区
; ***** GDT 描述符表 *****
gdt_def LABEL BYTE
desc < > ;必须为空描述符
gdt_kcs desc <0ffffh,0000h,00h,9ah,0fh,00h> ;00000 - fffffH (1Mb)
gdt_kds desc <0ffffh,0000h,00h,92h,0fh,00h> ;00000 - fffffH (1Mb)
gdt_kss desc <00000h,0000h,00h,96h,00h,00h>
gdt_uds desc <0ffffh,0000h,00h,92h,0fh,00h> ;00000 - fffffH (1Mb)
gdt_end LABEL BYTE
;设置GDTR用
gdtsize dw gdt_end-gdt_def ;GDT描述符表的长度
gdtload dw ? ;GDT描述符表的线性基地址15-0,
dw ? ;GDT描述符表的线性基地址31-16
gdt_kcs_sel equ 08h ; CS选择器
gdt_kds_sel equ 10h ; DS选择器
gdt_kss_sel equ 18h ; SS选择器
gdt_uds_sel equ 20h ; 用户DS选择器
char db 'A' ; 要显示的字符
dos_data ends
dos_stack segment stack para 'stack' ;堆栈段
db 1024 dup (0)
dos_stack_sp dw 0
dos_stack ends
dos_code segment word public 'code' ;代码段
assume cs:dos_code, ds:dos_data, ss:dos_stack
main proc near
start :
mov ax,dos_data
mov ds,ax ; 设置DS
cli
mov ax,dos_stack
mov ss,ax ; 设置SS
mov save_ss,ax ; 保存SS
mov sp,offset dos_stack_sp ; 设置SP, 定义堆栈
call prot ; 调用保护模式操作子程
sti
mov ah,0
int 16h ; BIOS键盘中断调用,等待按键!
mov ah,4ch
int 21h ; 返回DOS!
main endp
.386p
prot proc near
SGDT fword ptr save_gdt ; 保存原GDT
mov di,offset gdt_kcs ; 设置CS描述符的基地址
mov ax,cs ; 将实模式DOS的段地址
call copy_desc ; 换算为线性基地址
mov di,offset gdt_kds ; 设置DS描述符的基地址
mov ax,ds ; 将实模式DOS的段地址
call copy_desc ; 换算为线性基地址
mov di,offset gdt_kss ; 设置SS描述符的基地址
mov ax,ss ; 将实模式DOS的段地址
call copy_desc ; 换算为线性基地址
mov ax,ds ; 计算GTD表的线性地址
mov cx,10h
mul cx
add ax,offset gdt_def
adc dx,0
mov gdtload,ax
mov gdtload+2,dx
LGDT fword ptr gdtsize ; 装载GDT
mov eax,cr0
or al,1 ; 设置控制寄存器CR0的PE位
mov cr0,eax ; 进入保护模式
jumpfar gdt_kcs_sel,prot_user ; 清除指令队列,
prot_user: ; CS=gdt_kcs_sel(保护模式码段)
call vram_disp ; 调用保护模式下显示子程
mov eax,cr0
and al,0feh ;清除控制寄存器CR0的PE位
mov cr0,eax ;切换到实模式
jumpfar dos_code,real_mode ;清除指令队列,CS=dos_code(实模式码段)
real_mode:
mov ax,dos_data
mov ds,ax
LGDT fword ptr save_gdt ;恢复原GDT
mov ss,save_ss ;恢复堆栈段
ret
prot endp
vram_disp proc near ;保护模式下直接写屏子程
pushad
mov ax,gdt_kds_sel
mov ds,ax
mov dl,char ;显示字符'A'
mov ax,gdt_uds_sel
mov ds,ax
mov cx,80*25 ;满屏字符数.
mov ebx,0b8000h
mov ah,17h ;字符颜色属性,蓝底白字
mov al,dl ;显示字符'A'
l1: mov [ebx],ax
add ebx,2
loop l1
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
dos_code ends
end start
;****************************** 源程序结束 ******************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -