主题:[转帖]中断方面的文章
阿Ben
[专家分:2200] 发布于 2005-10-03 22:16:00
声明:这是我在QBasic讨论区看到的文章,但我看文章的内容竟然是讲Turbo Pascal的,并且对于QBasic却只字未提!!所以我替那个楼主把文章转移到PASCAL讨论区中来。
相关链接:[url]http://www.programfan.com/club/showbbs.asp?id=48150[/url]
回复列表 (共2个回复)
沙发
阿Ben [专家分:2200] 发布于 2005-10-03 22:17:00
调用中断
·中断
中断是一种使CPU中止正在执行的程序而转去处理特殊事件的操作。
这些引起中断的事件称为中断源,它们可能是来自外设的输入输出请求,也可能是计算机的一些异常事故或其它内部原因。
由外设控制器或协处理器(8087/80287)引起的中断一般称为外中断,由程序中安排的中断指令INT产生的中断,或由CPU的某些错误结果产生的中断称为内中断。
·中断向量表
我们给每种中断安排一个中断类型号。IBM PC中断系统能处理256种类型的中断,类型号为0-0FFH。例如:系统定时器的中断类型为08,键盘为09,内中断中的除法错误的中断类型为0,等等。
每种类型的中断都由相应的中断处理程序来处理,中断向量表就是各中断类型的处理程序的地址表。
存储器的低1.5K字节,地址从0到5FFH为系统占用,其中最低的1K字节,地址从0到3FFH存放中断向量。中断向量表中的256项中断向量对应256种中断类型,每项占用四个字节,其中两个字节存放中断处理程序的段地址,另两个字节存放偏移地址。
·调用中断
调用中断需要使用汇编指令INT n(n为中断类型)。
如:
asm
int 23H
end;
这段代码可以模拟按下Ctrl+Break键,结束程序运行。
又如:
asm
int 19H
end;
这段代码可以重启系统(Windows MS-DOS 方式下可以关闭窗口)。
内部扬声器
·内部扬声器
内部扬声器是计算机内置的一种简单发声设备,原本是为BIOS(基本输入输出系统)设计的,用于发生异常或其它情况时的报警发声,后来被DOS游戏和软件广泛用于音乐的播放。
·通用发声程序
ROM BIOS中有个BEEP子程序,它能根据BL中给出的时间计数值控制8254定时器,产生持续时间为1个或几个0.5秒,频率为896Hz的声音。它的用途是当“加电自测”系统发现硬件错误后,由ERROR_BEEP处理程序调用BEEP子程序,使扬声器发出“嘟嘟”的信号。BL中的时间计数值由 ERROR_BEEP程序设置为533H,BEEP子程序将此计数值送给8254的定时器2来产生896Hz的声音,然后BEEP又将AL的第0位和第1 位置1,并把AL的值送到8255的输出寄存器(I/O端口61H),使扬声器接通发出声音。
由此,可以得出产生任一频率声音的计数值应为:533H×896÷给定频率=1192576÷给定频率。
发声和关闭扬声器的程序如下:
procedure MakeSound(Freq:Word);
begin
if Freq<19 then CloseSound
else
begin
Port[66]:=1192576 div Freq and 255;
Port[66]:=1192576 div Freq shr 8;
Port[97]:=Port[97] or 3;
end;
end;
procedure CloseSound;
begin
Port[97]:=Port[97] and 252;
end;
·播放音乐
播放音乐的核心在于获得各个音符的频率。按照乐理基本知识,每八度为一音程,每一音程中共有12个不同的音调,即1,#1,2,·2,3,4,#4, 5,#5,6,#6,7。中央C调的1其频率为261.6Hz。每升高一个音程,频率为原先的两倍。在同一音程中,每升高一个音调,频率为原先的2^ (1/12)倍。根据这一原理,若选定一个基本音频,比如音程0音调1的频率为32.7Hz,则给定某一音程及音调的数值,即可算得该音的频率值。
procedure PlayNote(Octave,Tone:Integer);{Octive:音程,0..6;Tone:音调,1..12}
begin
MakeSound(Round(32.7*Exp(Ln(2)*(Octive+(Tone-1)/12))));
end;
设置光标位置
·设置光标位置
要设置光标位置,可以调用BIOS中断10H:
中断号 AH 功能 调用参数
10H 2H 设置光标位置 BH=页号 DH,DL=行,列
例如(设置为0行0列):
asm
mov ah,2H
mov bh,0
mov dh,0
mov dl,0
int 10H
end;
·获取光标位置
中断号 AH 功能 调用参数 返回参数
10H 3H 获取当前光标位置 BH=页号 CH=光标起始行DH,DL=行,列
例如:
var
Row,Col:Byte;
asm
mov ah,3H
mov bh,0
int 10H
mov Row,dh
mov Col,dl
end;
设置显示模式
·设置显示模式
要设置显示模式,可以调用BIOS中断10H:
中断号 AH 功能 调用参数
10H 0H 设置显示模式 AL=显示模式号
例如(设置为640*480 16 Colors):
asm
mov ah,0
mov al,12H
int 10H
end;
·获取显示模式
中断号 AH 功能 调用参数 返回参数
10H FH 获取当前显示模式 AL=显示模式号
例如:
var
Mode:Byte;
asm
mov ah,FH
int 10H
mov Mode,al
end;
·显示模式表
标准VGA显示模式表:
显示模式 文本/图形 分辨率 颜色数
00H 文本 40*25 2
01H 文本 40*25 16
02H 文本 80*25 2
03H 文本 80*25 16
04H 图形 320*200 4
05H 图形 320*200 4
06H 图形 640*200 2
0DH 图形 320*200 16
0EH 图形 640*200 16
0FH 图形 640*350 2
10H 图形 640*350 16
11H 图形 640*480 2
12H 图形 640*480 16
13H 图形 320*200 256
设置中断子程序
·设置中断子程序
设置自己的中断子程序,可以利用中断实现拦截Ctrl+Break、自动计时、实时响应键盘操作等诸多功能,在编写好的游戏和软件时必不可少。
一般在检查或设置任何中断向量时,总是避免直接使用中断向量的绝对地址,而是使用DOS功能调用(21H)存取中断向量。
DOS单元有两个过程分别执行设置中断向量和获取中断向量:SetIntVec和GetIntVec。其定义如下:
procedure SetIntVec(IntNo:Byte;Vector:Pointer);
procedure GetIntVec(IntNo:Byte;var Vector:Pointer);
这两个过程都是使用DOS功能调用(21H)存取中断向量。
如果新的中断功能只供自己使用,或用自己编写的中断处理程序代替系统中的中断处理功能时,要注意保存原中断向量,在设置自己的中断向量时,应先保存原中断向量再设置新的中断向量,再程序结束之前恢复原中断向量。
·编写自己的中断子程序
编写自己的中断子程序是系统级程序设计员常用的技术之一。编写这类程序,常常需要很复杂的技术,并且要很细心,一般都用汇编语言或C语言来实现。然而,Turbo Pascal提供了实现这种技术的简便方法。
中断过程可用Interrupt指令来声明:
procedure IntHandler(Flags,Cs,Ip,Ax,Bx,Cx,Dx,Si,Di,Ds,Es,Bp:word);interrupt;
begin
...
end;
Turbo Pascal在中断过程的入口处自动保存所有寄存器并初始化DS寄存器。所有寄存器都作为参数传递,因而在过程中可以使用并修改其值。不一定要列出上面所有寄存器参数,但对参数表有几个规定:
1.不能声明其它的参数;
2.可以不声明任何参数;
3.若前一个参数没有省略,则不能省略后一个参数。
例如:
procedure IntHandler; {正确}
procedure IntHandler(Si,Di,Ds,Es,Bp:Word);{正确}
procedure IntHandler(Si,Es,Bp:Word); {错误}
最后一例的错误在于省略了Di和Ds,而没有省略Si。虽然编译程序不会报告此类错误,但你自己应负责参数顺序的正确性,否则会发生意外的错误。
注意,Turbo Pascal并没有自动插入开中断指令----STI,因而不会产生进一步的中断。需要时可以用inline语句插入一条STI指令。
中断过程的出口码将恢复寄存器并执行中断返回指令。中断过程可以修改寄存器参数,中断返回时,会导致相应寄存器的修改。
值得注意的是,若是编写硬件中断处理程序,则不能使用Turbo Pascal的输入输出或动态内存分配子程序,因为它们是不可重入的。另外,由于DOS也是不可重入的,因而也不能使用DOS功能调用。
板凳
lzl1403 [专家分:1670] 发布于 2005-10-03 22:29:00
好帖!
我来回复