⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 386pi6.asm

📁 汇编学习经典,DOS环境可在 win2000下使用
💻 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 + -