📄 386idt.asm
字号:
;**********************************************************************
; 保护模式下的编程示例 --- 中断 陈家祺 1996.6
;
; 1 实模式进入保护模式及保护模式返回实模式方法
; 2 保护模式下的指令操作(清屏)
; 3 保护模式下的软中断调用方法
; 4 保护模式下的硬中断处理方法(时钟,键盘)
; 5 保护模式下的异常中断(13)处理方法
; * 环境: MASM6.0,TASM3.0, DOS6.2, 80486主机, Himem.sys, 不能装载Emm386.exe
; 不能装载Emm386.exe,因为Emm386.exe装载后,cpu处于虚拟8086方式下
;**********************************************************************
jumpfar MACRO segf,offsetf ; 保护模式跳转指令
db 0eah ; 功能与目的:
dw offsetf ; 1. 清除指令队列
dw segf ; 2. segf -> CS
ENDM
desc struc ; 存储段描述符结构
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
dos_data segment ; 数据段
tc db 0 ; 时钟中断计数单元,用于显示更新
tn dw 0 ; 时钟中断计数单元,用于程序延时
save_idt dw 4 dup (?) ; IDT保存区
save_ss dw ? ; SS保存区
; ***** GDT 描述符表 *****
gdt_def LABEL BYTE
desc < > ; 空描述符
gdt_kcs desc <0ffffh, ? , ? ,9ah,0fh,00h> ;00000 - fffffH (1Mb)
gdt_kds desc <00002h, ? , ? ,92h,00h,00h> ;00000 - 00002H (3 b)
gdt_kss desc <00000h, ? , ? ,96h,00h,00h>
gdt_uds desc <0ffffh,8000h,0bh,92h,00h,00h> ;00000 - 0ffffH (64k)
gdt_end LABEL BYTE
; ***** IDT 描述符表 *****
idt_def LABEL BYTE
idesc 8 dup (<int_nn,0008h,00h,8eh,0000h >) ; 缺省中断门
idt_08 idesc <int_08h,0008h,00h,8eh,0000h> ; 时钟中断门
idt_09 idesc <int_09h,0008h,00h,8eh,0000h> ; 键盘中断门
idesc 3 dup (<int_nn,0008h,00h,8eh,0000h >) ; 缺省中断门
idt_13 idesc <int_13,0008h,00h,8fh,0000h> ; 异常13中断门
idesc 18 dup (<int_nn,0008h,00h,8eh,0000h >) ; 缺省中断门
idt_20h idesc <int_20h,0008h,00h,8fh,0000h> ; 软中断门
idesc 256-21h dup (<int_nn,0008h,00h,8eh,0000h >); 缺省中断门
idt_end LABEL BYTE
; 设置GDTR用
gdtsize dw gdt_end-gdt_def ; GDT描述符表的长度
gdtload dw ? ; GDT表的首地址(线性地址)
dw ?
; 设置IDTR用
idtsize dw idt_end-idt_def ; IDT描述符表的长度
idtload dw ? ; IDT表的首地址(线性地址)
dw ?
dos_data ends
dos_stack segment stack ; 堆栈段
db 1024 dup (0)
dos_stack_sp dw 0
dos_stack ends
dos_code segment ; 代码段
assume cs:dos_code, ds:dos_data, ss:dos_stack
gdt_kcs_sel equ 08h ; CS选择子
gdt_kds_sel equ 10h ; DS选择子
gdt_kss_sel equ 18h ; SS选择子
gdt_uds_sel equ 20h ; 用户DS选择子
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
call prot ; 调用保护模式操作子程
sti
mov ah,0
int 16h ; BIOS键盘中断调用,等待按键!
mov ah,4ch
int 21h ; 返回DOS!
main endp
.386p
prot proc near
SIDT fword ptr save_idt ; 保存原IDT,必须!
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
mov ax,ds ; 计算IDT表的线性地址
mov cx,10h
mul cx
add ax,offset idt_def
adc dx,0
mov idtload,ax
mov idtload+2,dx
LGDT fword ptr gdtsize ; 装载GDT
LIDT fword ptr idtsize ; 装载IDT
mov ax,gdt_kss_sel ; 设置保护模式的SS
mov ss,ax
mov eax,cr0
or al,1 ; 设置控制寄存器CR0的PE位
mov cr0,eax ; 进入保护模式
jumpfar gdt_kcs_sel,wait_intr ; 清除指令队列,CS=gdt_kcs_sel(保护模式码段)
wait_intr:
sti
conti: mov ax,gdt_kds_sel
mov ds,ax
cmp tn, 18*5 ; 如果tn<18*5(5秒),循环等待中断
jnz conti
cli
int 20h ; 调用保护模式软中断
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 ; 恢复DOS的DS
mov ds,ax
LIDT fword ptr save_idt ; 恢复原IDT
mov ss,save_ss ; 恢复DOS的SS
ret
prot endp
.8086
copy_desc proc near ; 设置GDT描述符的基地址
mov cx,10h ; 将实模式DOS的段地址
mul cx ; 换算为线性地址
mov [di].base_15_0,ax ; 存入GDT描述符中
mov [di].base_23_16 ,dl
ret
copy_desc endp
.386p
; 保护模式时钟中断08h - 中断处理程序
int_08h proc near ; 每次中断tc=tc+20,
pushad ; 如果tc>127显示'*',否则显示' ';
mov ax,gdt_kds_sel ; 效果: '*'闪烁.
mov ds,ax
inc tn ; tn增1
add tc,20
cmp tc,127
jb char0
mov cl,'*' ; 显示'*'
jmp char1
char0: mov cl,' ' ; 显示' ',即清除'*'
char1: mov ch,1eh ; 字符颜色属性
mov ax,gdt_uds_sel
mov ds,ax
mov ebx,000000h ; 屏幕第0行
mov [ebx],cx
mov al,20h ; 通知8259结束中断
out 20h,al
popad
iretd
int_08h endp
KEY_PA equ 60h
KEY_PB equ 61h
; 保护模式键盘中断09h - 中断处理程序
int_09h proc near
pushad
in al,KEY_PA ; 输入键盘扫描码
mov al,10000000b ; 复位键盘,发送应答
out KEY_PB,al
mov al,01000000b ; 允许键盘工作
out KEY_PB,al
mov ax,gdt_uds_sel
mov ds,ax
mov bx,160*3 ; 屏幕第3行
mov ah,1eh ; 字符颜色属性
mov al,'K' ; 显示'K'
mov [bx],ax
mov al,20h ; 通知8259结束中断
out 20h,al
popad
iretd
int_09h endp
; 保护模式软中断20h - 中断处理程序
int_20h proc near
pushad
mov ax,gdt_uds_sel
mov ds,ax
mov bx,0000h ; 屏幕第0行
mov ah,1eh ; 字符颜色属性
mov al,'!' ; 显示'!'
mov [bx],ax
popad
iretd
int_20h endp
; 保护模式异常中断13 - 中断处理程序
int_13 proc near
pushad
mov ax,gdt_uds_sel
mov ds,ax
mov bx,160*4 ; 屏幕第4行
mov ah,1eh ; 字符颜色属性
mov al,'?' ; 显示'?'
mov [bx],ax
popad
iretd
int_13 endp
; 保护模式缺省中断 - 中断处理程序
int_nn proc near
pushad
mov ax,gdt_uds_sel
mov ds,ax
mov bx,160*5 ; 屏幕第5行
mov ah,1eh ; 字符颜色属性
mov al,'#' ; 显示'#'
mov [bx],ax
mov al,20h
out 20h,al
popad
iretd
int_nn endp
dos_code ends
end start
;****************************** 源程序结束 ******************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -