外中断02 - 零基础入门学习汇编语言70

第十五章:外中断02

 

让编程改变世界

Change the world by program


 

小甲鱼和大家谈谈心

  一个帖子引发小甲鱼的反省! 猫姐曾经说过,步子别迈太大,容易扯着蛋! 结果还真蛋疼了……   因此,小甲鱼要学会淡定面对,不能忘记当初的宗旨!做视频也好、做网站也好,对得住大家才对得住自己的良心! 最后:希望大家继续支持鱼C、支持小甲鱼,看到大家都能坦诚相待,很开心,很幸福!  

编写int 9 中断例程

 

复习一下前边的内容中,我们可以总结出键盘输入的处理过程:

(1)键盘产生扫描码; (2)扫描码送入60h 端口; (3)一旦侦测到60h端口有动静,引发9 号中断; (4)CPU执行int 9 中断例程处理键盘输入。 以上的过程,前三步都由硬件系统自动完成。我们能够改变的只有第四步,修改int 9 终端程序。 但是,在这门课程中,我们不准备完整地编写一个键盘中断的处理程序,因为要涉及到一些硬件细节,而这些内容脱离了我们的内容主线。 插入语:如果有兴趣想更为深入的学习汇编语言,探究汇编语言的奥妙,可以关注小甲鱼今后推出的《The Art of Assembly Language》。  

但是,我们却还要编写新的键盘中断处理程序,来进行一些特殊的工作,那么这些硬件细节如何处理呢?

如果单纯要完成这点还是相对比较简单的,因为BIOS 提供的int 9中断例程已经对这些硬件细节进行了处理。 我们只要在自己编写的中断例程中调用BIOS 的int 9中断例程就可以了。  

任务演示:在屏幕中间依次显示 “a”~“z” ,并可以让人看清。在显示的过程中,按下Esc键后,改变显示的颜色。

 

我们先来看一下如何依次显示“a”~“z”:

[codesyntax lang="asm"]
	assume cs:code
	code segment
	start:    
		mov ax,0b800h
		mov es,ax
		mov ah,'a'

 	s:   
		mov es:[160*12+40*2],ah
		inc ah
		cmp ah,'z'
		jna s

 		mov ax,4c00h
		int 21h

 	code ends
	end start
[/codesyntax]   我们发觉,因为一个字母刚显示到屏幕上,CPU执行几条指令后,就又变成了另一个字母,字母之间切换得太快,因此我们无法看清。 理想状况是:我们应该在每显示一个字母后,延时一段时间,让人看清后,再显示下一个字母。  

那么如何延时呢?

不如……我们让CPU 执行一段时间的空循环。有时候让它做点无用功哈~   请看源代码并试图分析作者的做法:相关代码下载   现在显示“a”~“z”的任务我们基本完成了,并做到可以让人看清,虽然做法有些无耻……   那么接下来将进一步来实现:按下 Esc 键后,改变显示的颜色!怎么办呢? 键盘输入到达60h 端口后,就会引发 9号中断,CPU 则转去执行int 9中断例程。   我们可以编写int 9中断例程,功能如下: (1)从60h端口读出键盘的输入; (2)调用BIOS 的int 9 中断例程,处理其他硬件细节; (3)判断是否为Esc的扫描码,如果是,改 变显示的颜色后返回;如果不是则直接返回。    

接下来,我们对这些功能的实现一一进行分析!

 

第一步:从端口60h读出键盘的输入

in al,60h  

第二步:调用BIOS的int 9中断例程

注:有一点要注意的是,我们写的中断处理程序要成为新的int 9中断例程,主程序必须要将中断向量表中的int 9中断例程的入口地址改为我们写的中断处理程序的入口地址。 那么在新的中断处理程序中调用原来的int 9中断例程时,中断向量表中的int 9中断例程的入口地址却不是原来的int 9 中断例程的地址。所以我们不能使用int 指令直接调用。   这里有必要解释一下:。。。。。。 对于我们现在的问题,假设我们将原来int 9中断例程的偏移地址和段地址保存在ds:[0]和ds:[2]单元中。 那么我们在需要调用原来的int 9中断例程时候,就可以在 ds:[0]、ds:[2] 单元中找到它的入口地址。  

那么,有了入口地址后,我们如何进行调用呢?

当然不能使用指令int 9来调用。我们可以用别的指令来对int指令进行一些模拟,从而实现对中断例程的调用。   我们来看,int 指令在执行的时候,CPU 进行下面的工作: (1)取中断类型码n; (2)标志寄存器入栈; (3) IF=0,TF=0; (4) CS 、IP 入栈; (5)(IP) = (n*4),(CS) = (n*4+2)。 取中断类型码是为了定位中断例程的入口地址,在我们的问题中,中断例程的入口地址已经知道。 所以,我们用别的指令模拟int 指令时候,不需要做第(1)步。   在假设要调用的中断例程的入口地址在ds:0和ds:2单元中的前提下,我们将int 过程用下面几步模拟: (1)标志寄存器入栈; (2)IF=0,TF=0; (3)CS、IP入栈; (4)(IP)=((ds)*16+0),(CS)=((ds)*16+2)。 可以注意到第(3)、(4)步和call dword ptr ds:[0]的功能一样。   call dword ptr ds:[0] 的功能也是: (1)CS 、IP 入栈; (2)(IP)=((ds)*16+0),(CS)=((ds)*16+2)。 如果这点上有疑问的童鞋,不妨可以复习下10.6节的内容。  

所以经过我们总结后,int 过程的模拟最终变为:

(1)标志寄存器入栈; (2)IF=0,TF=0; (3)call dword ptr ds:[0] 对于(1),可用pushf实现。 对于(2),我们又得动点歪脑筋,没办法,资源条件极其卑劣的8086 要么使人放弃,要么逼出天才!我们可用以下程序间接实现:   实现IF=0,TF=0步骤:

pushf

pop ax

and ah,11111100b ; IF和OF为标志寄存器的

; 第9位和第8位

push ax

popf

  这样,模拟int指令的调用功能,调用入口地址在ds:0、ds:2中的中断例程的程序如下:

pushf ;标志寄存器入栈

pushf

pop ax

and ah,11111100b ; IF和OF为标志寄存器的第9

; 位和第8位

push ax

popf ;IF=0、TF=0

call dword ptr ds:[0]

 

第三步:如果是Esc键的扫描码,改变显示的颜色后返回……

  那么,下一个问题:如何改变显示的颜色? [buy] 获得所有教学视频、课件、源代码等资源打包 [/buy] [Downlink href='http://kuai.xunlei.com/d/LWPDSCJPSSUD']视频下载[/Downlink]
posted @ 2011-05-27 23:36  我就爱小甲鱼  阅读(106)  评论(0编辑  收藏  举报