📄 386pi6.asm
字号:
;**********************************************************************
; 保护方式下的编程示例 --- 中断(386中断门) 陈家祺 1996.6
;
; 1 保护方式下的软中断调用方法
; 2 保护方式下的硬中断处理方法(时钟,键盘)
; 3 保护方式下的异常中断(13)处理方法
; * 环境: MASM6.0,TASM3.0, DOS6.2, 80486主机, Himem.sys,
; 不能装载Emm386.exe,因为Emm386.exe装载后,cpu处于虚拟8086方式下
;**********************************************************************
.MODEL large;small
.386p
jumpfar MACRO _sel,_offset ;保护方式跳转指令
db 0eah ;功能与目的:
dw _offset ; 1. 清除指令队列
dw _sel ; 2. 选择字sel -> CS
ENDM
set_ba MACRO _seg,_gdt_seg
mov di,offset _gdt_seg ; 设置_gdt_seg段描述符的基地址
mov ax,_seg ; 将实方式DOS的段地址_seg换算为线性基地址
mov cx,10h ; 将实方式DOS的段地址
mul cx ; 换算为24位线性地址
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
idesc struc ; 中断门描述符结构
off_15_0 dw ? ; 入口偏移量15-0
sel dw ? ; 目标选择字
no db 0 ;
access db ? ; 访问权
off_31_16 dw ? ; 入口偏移量31-16
idesc ends
.DATA ; 数据段
tn dw 0 ; 中断次数计数变量
; ***** 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,00h,00h>
gdt_uds desc <0ffffh,8000h,0bh,92h,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_r_sel equ 28h ; DS/SS选择字(DOS)
; ***** IDT 描述符表 *****
idt_def LABEL BYTE
idesc 8 dup (<int_nn,0008h,00h,8eh,0000h>) ; 缺省中断门(386)
idt_08 idesc <int_08,0008h,00h,8eh,0000h> ; 时钟中断门(386)
idt_09 idesc <int_09,0008h,00h,8eh,0000h> ; 键盘中断门(386)
idesc 3 dup (<int_nn,0008h,00h,8eh,0000h>) ; 缺省中断门(386)
idt_13 idesc <int_13,0008h,00h,8fh,0000h> ; 异常13中断门(386)
idesc 18 dup (<int_nn,0008h,00h,8eh,0000h>) ; 缺省中断门(386)
idt_32 idesc <int_32,0008h,00h,8fh,0000h> ; 软中断门(386)
idesc 256-21h dup (<int_nn,0008h,00h,8eh,0000h>) ; 缺省中断门(386)
idt_end LABEL BYTE
; 设置保护方式下的IDTR
idtsize dw idt_end-idt_def ; IDT描述符表的长度
idtaddr dd ? ; IDT描述符表的线性基地址31-0
; 设置实方式下的IDTR
idtsize_r dw 03ffh ; IDT描述符表的长度
idtaddr_r dd 00000000h ; IDT描述符表的线性基地址31-0
.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描述符的基地址
mov eax,ds ; 计算GDT描述符表的线性基地址31-0
shl eax,4 ; 段地址ds*16
add eax,offset gdt_def ; 线性基地址=段地址ds*16+偏移地址
mov gdtaddr,eax ; 存入gdtaddr中
mov eax,ds ; 计算IDT描述符表的线性基地址31-0
shl eax,4 ; 段地址ds*16
add eax,offset idt_def ; 线性基地址=段地址ds*16+偏移地址
mov idtaddr,eax ; 存入idtaddr中
LGDT fword ptr gdtsize ; 装载GDT给GDTR寄存器
LIDT fword ptr idtsize ; 装载IDT给IDTR寄存器
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 ; 设置保护方式的堆栈段
mov_s ds,gdt_ds_sel ; 设置保护方式的数据段
sti
conti:
cmp tn, 100 ; 如果tn<100,循环等待中断
jne conti
cli
int 20h ; 调用保护方式软中断
mov bx,10h
mov [bx],al ; 产生异常中断13,因为数据段的界限为0fh
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堆栈段的段地址
LIDT fword ptr idtsize_r ; 装载DOS的IDT给IDTR寄存器
ret
prot endp
int_p MACRO ch1,m ; 显示字符ch1
pusha ; m=0为内中断,否则为外中断
push ds
mov_s ds,gdt_ds_sel ; 设置参数数据段
inc tn ; 中断计数变量增一
mov bx,tn ; 计数值作为VRAM位置的自变量
mov_s ds,gdt_uds_sel ; 设置VRAM数据段
shl bx,1 ; VRAM位置=bx*2
mov ah,1eh ; 字符颜色属性
mov al,ch1 ; 显示字符ch1
mov [bx],ax ; 直接写屏显示字符ch1
IF m
mov al,20h ; 通知8259结束中断
out 20h,al
ENDIF
pop ds
popa
ENDM
; 时钟中断08h - 中断处理程序
int_08 proc near
int_p '*',1 ; 显示'*'
iretd
int_08 endp
; 键盘中断09h - 中断处理程序
int_09 proc near
in al,60h ; 输入键盘扫描码
int_p 'K',1 ; 显示'K'
iretd
int_09 endp
; 软中断20h - 中断处理程序
int_32 proc near
int_p '!',0 ; 显示'!'
iretd
int_32 endp
; 异常中断13 - 中断处理程序
int_13 proc near
int_p '#',0 ; 显示'#'
add esp,4 ; 跳过堆栈中的错误码
pop eax ; 修改返回地址
add eax,2 ; mov [bx],al为2字节指令
push eax ; 因为,在产生异常中断的指令时,
iretd ; 压入堆栈的断点并不指向下一条指令!
int_13 endp
; 缺省中断 - 中断处理程序
int_nn proc near
int_p '?',1 ; 显示'?'
iretd
int_nn endp
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -