DOS程序员参考手册内容介绍

DOS程序员参考手册内容介绍

109页
第6章输入设备
在大多数人看来,输入与输出结合得这样紧,以致人们常常说“I/O”或“输入/输出”,
而很少单独提到其中的一个。在这一章里,要补充第5章的内容,我们要讨论输入设备。
本章重点介绍两种使用最普遍的输入设备:键盘和鼠标。键盘由系统所包含的DOS
和BIOS功能操纵,而要使鼠标的功能发挥作用,则必须在系统中添加一个驱动程序。
键盘比大多数人所意识到的要复杂得多。作为整个pc工程的精致片断,键盘能适应
BIOS中的低级功能,并借助缓冲字符输入和提供中断来使键盘操作对于程序几乎是看不
到的,这样就让BIOS能在敲击键盘字符时操纵它们。
因为中断直到第11章"中断处理程序”才讨论,所以本章仅以简单的方式涉及到鼠
标。这也是为一个有趣的程序才这样作的。通过使用鼠标功能(当它们存在时),可以在许
多程序中建立由鼠标所控制的功能。
将键盘及鼠标与中断处理功能结合起来能使键盘和鼠标功能都变得更加有趣。使用热
键来启动的TSR程序,或中断鼠标输入的鼠标驱动程序都对用户的需要进行快速反应。
第11章将更详细地讨论中断。但是,目前需要了解基本的输入功能。
6.1键 盘
人们总是依赖于pC机有键盘作为输入设备——每台Pc机都有,并且大多数程序都
使用它们。尽管替代性的输入设备如鼠标已普遍用于一些类型的程序,但很少有程序不需
键盘输入而进行操作。
本节先看BIOS和DOS功能怎样用于键盘输入。这些功能用来替代高级语言的通常
输入功能时,会带来下列优点:
.对输入的最大控制。例如,可以添加用户指定的编辑性能和特殊的帮助功能。
.比使用高级语言所提供的键盘输入例程更乙、的程序。这些例程,用来提供每一种
可能的输入环境和条件,但执行了大量的“开销代码”。
.更敏捷、反应更迅速的输入,因为可以使程序对无论怎样的选择都能产生反应。
在讨论编程之前,先看看键盘的工作方式。
6.1.1了解键盘的工作方式
对程序员来说,键盘可能是计算机上最熟悉但却最不了解的设备。大多数人了解文件

110页
系统和磁盘操作,因为阅读了这方面的书籍。但最少讨论键盘。只有很少的专家了解键盘。
本书不打算描述使键盘工作的硬件,而着重看看如果要使用与键盘相关的DOS和
BIOS设备时用户必须了解的各种事件的顺序。
首先,想象你在通过联系计算机和键盘的电缆看计算机。按键时,可以看到数字(键盘
“扫描码”)由键盘通过电缆到达计算机。
每按一次键,就产生一个独一无二的8位数(最明显的位总是0,以示按键和后面的
显示之间的区别),即使分列左边和右边的Shift键也由不同的数字代表。这些扫描码精确
地指示按的是哪一个键。
无论什么时候放开一个键,都会产生另一个扫描码。这个代码与前面的相同,只是设
置了高序位(即扫描码加上128)。扫描码就以这种方式用信号告诉ROM BIOS:这个键已
经放开了。
尽管有许多不同类型的键盘配置,但大部分键盘都遵循三种基本设计中的一种。这些
设计则都源于IBM为这些不同的计算机开发的键盘设计。这三种设计是PC机(83键)键
盘、个人机AT(84键)键盘,以及增强(101键)键盘。虽然大多数扫描码在三种设计中保
持相同,但还是有些区别。图6.1、6.2和6.3分别显示了PC机、个人机AT及增强键盘上
的扫描码(十六进制)。注意增强键盘利用多个扫描码来识别它的一些附加键。
111页
图6.3十六进制的增强型键盘扫描码
当按住一个键超过半秒钟时,键盘就会产生一系列的扫描码以响应按键动作,并以特
助速率重复出现。 BIOS能够说出哪个键正被按住(不是反复按)。因为在这个系列中键
没有产生任何放开键的代码。
键盘只识别所按的键(不是原符),并且只提供与该键对应的扫描码。(扫描码只是指
示已经按住了某个特定的键)。BIOS翻译扫描码以确定哪个ASCII码字符对应于所按的
键。为旧的85键和84键键盘设计的BIOS ROM忽略了101键键盘上额外的键,所以许
多程序都没有利用这些附加键。
每当按一个键,键盘都会产生扫描码和Int 09h,它告诉ROM BIOS已经按住了一个
键。系统的控制马上传递给中断处理程序,它读端口96(60h)以确定按住了哪个键。中断
服务例程读扫描码,并将它转换成代表所按键的16位代码,该代码的较低字节是所按键
的ASCII码值;较高字节则是扫描码。表6.1就是这些代码的列表。
表6.1 AscII码和扫描代码
键 增强型 键 增强型
Esc 01 01 01 ?/ 35 35 35
!1 02 02 02 右shift 36 36 36
@2 03 03 03 PrtSc * 37 37 E0 12
#3 04 04 04 左Alt 38 38 38
$4 05 05 05 空格 39 39 39
%5 06 06 06 CapsLock 3A 3A 3A
^ 6 07 07 07 F1 3B 3B 3B
&7 08 08 08 F2 3C 3C 3C
* 8 09 09 09 F3 3D 3D 3D
(9 0A 0A 0A F4 3E 3E 3E
)0 0B 0B 0B F5 3F 3F 3F
_- 0C 0C 0C F6 40 40 40
112页
(续)
键 增强型 键 增强型
PC AT 增强型 PC AT 增强型
+= 00 0D 00 F7 41 41 41
Backspace 0E 0E 0E F8 42 42 42
Tab 0F 0F 0F F9 43 43 43
Qq 10 10 10 F10 44 44 44
Ww 11 11· 11 Num Lock 45 45 45
Ee 12 12 12 ScrollLOck 46 46 46
Rr 13 13 13 7 Home 47 47 47
Tt 14 14 14 8向上箭头 48 48 48
Yy 15 15 15 9 PgUp 49 49 49
Uu 16 16 16 Gray- 4A 4A 4A
Ii 17 17 17 4向左箭头 48 48 48
00 18 18 18 5 4C 4C 4C
Pp 19 19 19 6向右箭头 4D 4D 4D
{[ 1A 1A 1A Gray+ 4E 4E 4E
}] 1B 1B 1B 1End 4F 4F 4F
Enter 1C 1C 1C 2向下箭头 50 50 50
左Ctrl 1D 1D 1D 3 PsDn 51 51 51
Aa 1E 1E 1E 0 Ins 52 52 52
Ss 1F 1F 1F .Del 53 53 53
Dd 20 20 20 仅AT键盘
Ff 21 21 21 SysRq N/A 54 N/A
Gg 22 22 22 仅增强型键盘
Hh 23 23 23 GrayEnter N/A N/A E01C
Jj 24 24 24 右Ctrl N/A N/A E010
Kk 25 25 25 Gray/ N/A N/A E035
Ll 26 26 26 Gray * N/A N/A 37
:; 27 27 27 Right Alt N/A N/A E038
"' 28 28 28 Gray Home N/A N/A E0 47
~ 29 29 29 GrayC向上 N/A N/A E048
Shift 2A 2A 2A GaryPgUp N/A N/A E049
|/ 2B 2B 2B GrayC向左 N/A N/A E04B
Zz 2C 2C 2C GrayC向右 N/A N/A E04D
Xx 2D 2D 2D GrayEnd N/A N/A E04F
Cc 2E 2E 2E GrayC向下 N/A N/A E050
Vv 2F 2F 2F Gray PgDn N/A N/A E051
Bb 30 30 30 Gray Ins N/A N/A E052
Nn 31 31 31 Gray De N/A N/A E053
Mm 32 32 32 F11 N/A N/A 57
<, 33 33 33 F12 N/A N/A 58
>. 34 34 34 Pause/Break N/A N/A E1

113页
特殊键,如功能键和数字小键盘上的键,在低字节中有一个零来指示所按的键不是一
个标准的ASCII字符,并且必须被特殊处理。
当BIOs键盘中断处理程序结束处理所按的键时,它将代码放进键盘缓冲区中保存
起来,直到某个程序需要它时。两个特别的键,Ctrl-Break和Shift,printScreen,不以这种方
法处理;相反它们产生的中断请求马上由BIOS的另外部分处理了。Ctrl-Break调用了Int
1Bh,它是DOS设置来迫使Ctrl-C进入缓冲区,以便进行后面的“正常”处理。 Shift-
PrintScreen则调用了Int 05h,它执行屏幕打印动作。
在这个水平上,不用书写任何程序去访问键盘数据。相反,本章中的代码只依赖BIOS
或DOS去预处理所有键盘输入数据,以便用户只需要处理通常键的ASCII码和特殊键的
扫描码。
6.1.2用BASIC读键盘
在开始利用DOS和BIOS调用去读键盘之前,让我们先看看使用标准BASIC功能来
进行键盘输入的一个BASIC程序。 KEYBD.BAS程序使用了BASIC解释程序中有用的
功能(见列表6.1)
列表6.1
10 REM...KEYBD.BAS Basic keybOard demonstration
20 c$S=INKEY$:IF C$=" " THEn 20
30 gosub 100:GOTo 20
100 REM···Display Keyboard input
110 IF LEN(C$)>1 THEN GOSUB 200 ELSE GOSUB 300
120 PRINT USING "Character:! ### ###"; CH$;c;SC
130 RETURN
200 REM···KeyStrOke haS SCan code included
210 CH$=".":C=ASC(MID$(c$,1,1)):SC=Asc(MID$(C$,2,1))
220 RETURN
300 REM···Keystroke is Character only
310 CH$2C$:c=ASC(c$):SC=0
320 RETURN
为达到与BASIC解释程序兼容,该程序书写时带有行号。下面逐步显示这个程序的
简单结构:
1.等待击键。
2.打印键击字符、ASCII码和扫描码。
3.从第一步开始重复。
INKEY$(放在列表6.1第20行的紧密循环中)提供了读键盘的一种方式,并且不
必等待击键。根据字符串是否超过一个字符长,100行的子例程翻译从INKEY$返回的
内容,第一个字符是字符的ASCII值,第二个则是扫描码(起始于第200行的子例程用于
处理所按键的大小写)。通常字符串只有一个字符长。第300行的子例程序则用于翻译字
符串的代码。
如果运行列表6.1中的程序,就会注意到扫描码只在有限的程度上与单个的键对应
(参见图6.1和6.2)。KEYBD.BAS程序存在以下不足:

114页
·INKEY$只返回“有意义的”字符(在这个例子中字符“有意义”只是针对BASIC
解释程序而言的)。
·如果完成的击键不是ASCII字符,则只为此击键返回扫描码,不用看Shift键、Ctrl
键或Alt键
从现在开始,本书不继续介绍解释程序兼容的BASIC程序,而只介绍QuickBASIC
编译程序。列表6.2显示了为QuickBASIC而修改的程序,以充分利用编译程序的性能。
列表6.2
KEYBD2.BAS
QUickBASIC Keyboard Demonstration
Start:
C$=INKEY$:IF C$=“” THEN GOTO Start
GOSUB Display:GOTO Start
Display:
Display Keyboard Input
IF LEN( C$) > 1 THEN GOSUB Scan ELSE GOSUB Char
PRINT USING " Character: ! ### ###" ; CH$;c; SC
RETURN
Scan:
Keystroke includes scan code
CH$=".":c=ASC( MID$(C$,1,1)):SC = ASC( MID$( C$,2,1))
RETURN
char :
Keystroke is Character only
CH$=c$:C=ASC(c$) : SC=0
RETURN
从列表6.2中的样本程序开始,将改而采用BIOS功能调用来执行输入。然后我们就
能在键盘上看到更多的击键。
6.1.3使用Int 16h来访问键盘
当准备使用BIOS功能时,要注意到的第一件事是Int 16h的功能0(BIOS键盘输入
功能),它的作用是等待击键(见列表6.3)。
列表 6.3
/* waitkey.c
Listing 6.3 of DOS Programmer's Reference*/
#include<stdio.h>
#include<dos.h>
void main()
{
unsigned char scancode;
unsigned char charcode;
union REGS regs;
printf("Test of Int 16 keyboard services\n");
printf("press Esc to exit program\n";
while(Charcode != 27){
regs.h.ah = 0;
int86(0x16, &regs, &regs);

115页
scancode=regs.h.ah;
charcode = regs.h.al;
printf("Scan: %.3d ASCII : %.3d [%C] \n",
scancode, charcode,charcode);
}
}
仅当有键按下时,读出击键的内容,而不是等待击键。Int 16h,功能1指示一个击键是
否正在等待。如果没有按键在等待,那么就在处理器的进位标记寄存器中设置0进位标
记。可按照列表6.4所示测试该寄存器。
列表6.4
/* waitkey2.C
Listing 6.4 of DOS Programmer'S Reference*/
#include<stdio.h>
#include<dos.h>
void main()
{
unsigned char scancode;
unsigned char Charcode;
union REGS regs;
printf("Test of Int 16 keyboard services\n");
printf("press Esc to exit program\n";
while(charcode != 27){
while(1) {
putchar('.');
regs.h.ah=1;
int86(0x16, &regs, &regs);
if((regs.x.flags & 0x40) == 0)
break;
}
regs.h.ah=0;
int86(0x16, &regs, &regs);
scancode = regs.h.ah;
charcode = regs.h.al;
printf("\nScan: %.3d ASCII: %.3d [%C] \n",
scancode, Charcode, charcode);

}
从键盘获取键之前,该程序首先看看是否有键准备好了。若没有,程序只在屏幕上打
印一个点。不论什么时候按键,程序都会停止打印点并显示信息,告诉按的是哪个键。然
后程序又开始打印点。
当程序有某种途径来检查键盘输入的程序在等待击键时,它们不会“闲”着,而要去完
成其它的任务。例如,可以使用这类例程来移动屏幕上的数据,而在击一个键时,终止此操
作。
Int 16h功能2用于确定键的状态:Ins、Caps Lock、Num Lock、Scroll Lock、Alt、Ctrl,
以及左边和右边的Shift键。键盘输入程序的下一个版本则让用户检查这些键的状态(见
列表6.5)。

116页
列表6.5
/* Keystat.c
Listing 6.5 of DOS Programmer's Reference */
#include <stdio.h>
#include <dos.h>
#define VIDEO 0x10
/* Prototypes */
void gotoxy(int r, int c);
void cls(void);
void main()
{
unsigned char scancode;
unsigned char charcode;
union REGS regs;
int ins, caps, num, scroll;
int alt, ctrl, left, right;
cls();
gotoxy(0,0);
printf("Test of Int 16 keyboard services/n");
printf("Press Esc to exit program/n");
gotoxy(10, 12);
puts("INS CAPS NUM SCRL ALT CTRL LEFT RiGHT");
while(charcode != 27) {
while(1) {
regs.h.ah = 2;
int86(0x16, &regs, &regs);
ins = regs.h.al&0x80;
caps = regs.h.al&0x40;
num = regs.h.al&0x20;
scroll = regs.h.al&0x10;
alt = regs.h.al&0x08;
ctrl = regs.h.al&0x04;
left=regs.h.al&0x02;
right = regs.h.al&0x01;
gotoxy(11, 12);
printf("%S %S %s %s %S %S %S %S/n" ,
ins ? " ON " : " OFF",
caps ? "ON " : " OFF",
num ? " ON " : " OFF",
Scroll ? " ON " : " OFF",
alt ? " ON " : " OFF",
ctrl ? " ON " : " OFF",
left ? " ON " : " OFF",
right ? " ON " : " OFF");
regs.h.ah = 1;
int86(0x16, &regs, &regs);
if((regs.x.flags&0x40)==0) break;
}
regs.h.ah = 0;
int86(0x16, &regs, &regs);
Scancode = regs.h.ah;
charcode = regs.h.al;
printf("Scan: %.3d ASCII: %.3d [%C]/n",
scancode, charcode, charcode);
}
cls();
gotoxy(0,0);

117页
}
void gotoxy(row,col)
int row, col;

union REGS regs;
if(row<0||row>24) return;
if (col<0||col>79) return;
regs.h.ah=2;
regs.h.bh=0;
regs.h.dh=row;
regs.h.dl=col;
int86(VIDEO,&regs,&regs);

void cls()
{
union REGS regs;
regs.h.ah = 0x06;
regs.h.al = 0;
regs.h.bh=7;
regs.h.Ch=0;
regs.h.cl=0;
regs.h.dh = 25;
regs.h.dl = 80;
int86(VIDEO,&regs,&regs);
}
在列表6.5中的程序里,putchar('.')已经被用于检查特殊键的状态,并将此状态显
示在屏幕中心的代码所代替。这种状态信息给用户一个完整的画面,用来说明键盘上正在
发生什么。如果希望自己的程序去检查一个突发事件,如左边和右边的Shift键盘同时被
按住,Int 16h的功能2便能指出这个事件的发生时间。
Int 16h的功能2将单个字节返回到AL寄存器中。每个位都对应于一个特定的击
键。表6.2显示该功能的位赋值。
表6.2键标记字节中各位的含义

意义
76543210
0······0 没按住右边的Shift键
·······1 按住了右边的Shift键
······0· 没按住左边的Shift键
···. ··1· 按住了左边的Shift键
·····1·· 没按住Ctrl键
·····1·· 按住了Ctrl键
····0··· 没按住Alt键
·. ··1··· 按住了Alt键
···0···· Scroll Lock关闭
···1···· Scroll Lock打开
··0····· Num Lock关闭
··1···. . Num Lock打开
·0····· . Caps Lock关闭
·1····· . Caps Lock打开
0. ·····. Insert关闭
1···. ·. · Insert打开
处理键盘输入的BIOS功能代表了不用进行机器访问的最基本的途径。对键盘而言,

118页
BIOS 功能是能够利用的最低层,并且它也能保证某个程序尽可能地与各种PC机兼容。
6.1.4使用Int 21h来访问键盘
当以常规方法在DOS水平上获取键盘输入时,会失去对每个可能的击键的立即访
问。 DOS输入功能指示特殊键是否已被按住(如果第1个字节返回的是0,第2个是扫描
码)。但DOS功能不能告诉用户已有ASCII码的键的扫描码,也不报告Alt、shift等键的
状态。另外,最常使用的DOS输入功能要等待一次击键,这会强迫在一些程序中使用
BIOS接口。在本章稍后的部分,我们看到一个DOS输入功能克服了这种局限性。
Int 21h的功能1(有回显字符输入)是用户可能想用的最基本的DOS字符输入功能
(见列表6.6)。
列表 6.6
/* Keyin.c
Listing 6.6 of DOS Programmer's Reference*/
#include<stdio.h>
#include<dos.h>
#define CTRL_D 0x04
#define LF 0x0a
#define ENTER 0x0d
/*prototypes*/
Unsigned int keyin(void);
void main()
{
int c;
printf("Testing Int 21h input functions\n");
printf("press Ctrl-D to exit program\n");
while((c=keyin())!=CTRL_D)
if(c>=256)
printf("SPECIAL: %d\n",c-256);
else if(c==ENTER)
putchar(LF);
}
unsigned int keyin()
{
union REGS regs;
int offset;
offset = 0;
regs.h.ah = 0x01;
intdos(&regs,&regs);
if(regs.h.al==0){
offset = 256;
regs.h.ah = 0x01;
intdos(&regs,&regs);
}
return (regs.h.al+offset);
}
Keyin.c程序采用下列步骤:

119页
1.等待来自键盘的一个字符。
2.如果该字符为特殊字符(例如,功能键或光标键),则打印APECIAt及该键的扫
描码。
3.如果该字符是Enter键,字符输入功能就会在没有换行的情况下输出一个Re-
turn;用户必须输出一个换行字符到屏幕上,才会使显示内容换行。
4.从第1步开始重复。
如果用户在没有能将换行字符打印到屏幕上的专门部分的帮助下运行该程序,那么
就会看到这些字符在敲入时会被DOS功能回显出来。但是,当按Enter键时,光标不会移
向下一行。记住:该功能回显所敲的字符,并且Enter键只代表回车—而不是换行(Line
feed)字符。
该程序的一个有趣的功能是:它采用的特殊编码能将功能键与普通键区分开。因为
ASCII字符总是小于或等于255,所以这个功能产生一个扩展的字符设置,它是通过将一
个返回0的ASCII码键的扫描码值加上256来达到的。实际上,这个方法常能简单化译出
击键代码的过程,并且不会带来问题。许多程序员不用此方法,因为他们将击键与变量联
系在一起。将返回的字符与整数联系起来,用户就可以有更大范围的特殊代码来代表特殊
击键。
当需要特殊键(如功能键和箭头)时,上述读字符的方法有一个问题,即当进行第二次
调用想获得特殊键的扫描码时,DOS功能会打印出与扫描码相等的ASCII码,就如象想
获得的是ASCII码而不是扫描码。为防止这种情况发生,可以使用Int 21h的功能8,并且
由用户自己回显字符(见列表6.7)。
列表6.7
/* Keyin2.c
Listing 6.7 of DOS Programmer's Reference*/
#include <stdio.h>
#include <dos.h>
#define CTRL_D 0x04
#define LF 0x0a
#define ENTER 0x0d
/* prototypes*/
unsigned int keyin(void);
void main()
{
int c;
printf("Testing Int 21h input functions\0");
printf("press Ctrl-D to exit program\n");
while((c=keyin())!=CTRL_D)
if(c>=256){
printf("SPECIAL: %d\n",c-256);
}else{
putchar(c);
if(C==ENTER)
putchar(LF);
}
}
unsigned int keyin()
120页
{
union REGS regs;
int offset;
offset=0;
regs.h.ah = 0x08;
intdos(&regs, &regs);
if(regs.h.al==0){
offset = 256;
regs.h.ah = 0x08;
intdos(&regs,&regs);
}
return (regs.h.al+offset);
}
这个程序与列表6.6中的程序之间的区别非常细微,如果不仔细看就会忽略。首先,
在Keyin()函数中,设置程序去调用Int 21h的功能8而不是功能1。主例程序必须在敲击
字符是将每个字符回显给屏幕(因为DOS的内核并没为用户回显示字符)。因为特殊键独
立操纵,所以不用为回显假的功能键代码而烦恼—只会回显正在敲的键。
每个用于本章的DOS功能都等待一次击键,但与BIOS功能一起使用时,就可以周
期性地检查,看看是否有一个字符正在等待读取(这个过程叫做查询)。中断21h的功能
0Bh指示一个字符是否正等着被读龋列表6.8显示了当操作者等待一次击键时,怎样使
用这个功能去执行任务。
列表6.8
/* Keyin3.c
Listing 6.8 of DOS Programmer's Reference*/
#include<stdio.h>
#include<dos.h>
#define CTRL_D 0x04
#define LF 0x0a
#define ENTER 0x0d
/* prototypes*/
unsigned int keyin(void);
unsigned int charwait(void);
void main()
{
int c;
printf("Testing Int 21h input functions\n");
printf("press Ctrl-D to exit program\n");
while((c =keyin())!=CTRL_D)
if(c>=256) {
printf("SPECIAL: %d\n",c-256);
} else{
putchar(c);
if(c==ENTER)
putchar(LF);
}

121页
unsigned int keyin()
{
union REGS regs;
int offset;
while(!charwait())
putchar('.');
offset = 0;
regs.h.ah = 0x08;
intdos(&regs, &regs);
if(regs.h.al==0) {
offset = 256;
regs.h.ah = 0x08;
intdos(&regs, &regs);

return (regs.h.al+offset);

unsigned int charwait()
{
union REGS regs;
regs.h.ah=0x0b;
intdos(&regs, &regs);
return (regs.h.al);

这个程序介绍了一个新的函数,charwait(),由它去检查键盘上的字符。如果已有字
符正在等待读取,它就会返回TRUE;否则就返回FALSE。
该函数以Int 21h的功能0Bh为基础,它设置AL寄存器的值,如果某字符在等待,它
就设置为FFh(255),否则为0。通过返回AL寄存器的值,该函数指示一个字符是否在等
待读取(FALSE即是0,TRUE是非0的值)。
要估价程序能怎样快速地检查字符,可以运行它,并看看程序等待敲入一个字符时经
过屏幕的时间。该程序对输入反应相当迅速。如果用一个长的、复杂的操作来代替简单的
putchar('.'),程序就会变得极端迟钝了。
本章的前面提到了一个克服常规DOS输入的许多局限性的特殊DOS输入功能:普
通的I/O功能—Int 21h的功能06h。
如果调用Int 21h的功能06h时,DL寄存器包含0FFh,那么该功能将从键盘缓冲区
中获得输入(并且如果没有字符可用,就返回0,且进位标志被设置)。
如果在DL寄存器中是其它的内容,它就被输出给CRT并且不进行检查。于是,这个
单一功能提供了使用直接的BIOS输入和输出时所能获得的大部分功能;并且还保持完
整的DOS兼容性。因为它是原始的“CP/M遗产”功能之一,所以很遗憾它没有象它应该
有的那样得到普及。
列表6.9是列表6.8从C语言到TurboPascal的翻译。经过修改,可利用Int 21h的
功能06h来输入和输出。功能和过程名没变,行为也是同样的。该程序没有使用Int 21h的
功能0Bh来检查键的就绪状态,而只是使用由Turbo Pascal的DOS单元提供的ZFlag(零
进位标志)常量来简单地测试零进位标志,这样就消除了charwait函数;该程序不使用
putchar来作单字节输出,它加上了putch函数,借助功能06h来执行输出。

122页
列表6.9
{keyin4.pas }
program keyin4;
uses DOS; { run-time DOS library }
const
CTRL_D = $04;
LF = $0a;
ENTER = $0d;
var
c : integer;
reg : registers; { declared in DOS unit }
procedure putch( i : integer );
begin
reg.ah := $06; { general I/O function }
reg.dl := byte(i) ; { OUTPUT the character }
MsDos (reg);
end ;
function keyin : integer;
var
i : integer;
offset : integer;
begin
repeat { wait for keystroke }
putch($2E) ; { put '.' on CRT }
reg. ah := $06; {general I/O function}
reg.dl := $FF; {flag for INPUT use }
MsDos (reg) ;
i := reg.flags AND FZero;
until i <> FZero; { declared in DOS unit }
if (reg.al = 0) then { flag as.extended key }
begin
offset := 256;
reg.ah := $06; { and go get scan code }
reg.dl := $FF;
MsDos (reg ) ;
end
else { set as normal key }
offset := 0;
keyin := reg.al + offset;
end ;
begin
writelnt('Testing int 21h input functions' ) ;
writeln( ' Press Ctrl-D to exit program' ) ;
c := keyin;
while (keyin <> CTRL_D) do
begin
if (c >= 256) then
begin
writeln (' SPECIAL : ',c-256) ;
end
else
begin
putch (c) ;
if (c= ENTER) then
putch (Lr) ;

123页
end;
c:= keyin;
end;
end.
(Int 21h,功能0Ah),并让它获得用户的输入,回显它,而且允许进行行编辑。DOS编程方
面的一些书籍指出:该功能通过返回一个两字节序列(包括零字节和扫描码)来返回功能
键、箭头以及其它的特殊键。keyin5.c 程序(见列表6.10)在特殊键返回时会显示它们
—但它们没有返回。 IBM技术手册没有包括两字节的键代码。
列表6.10
/* Keyin5.c
Listing 6.10 of DOS Programmer's Reference*/
#include <stdio.h>
#include<dos.h>
#define CTRL_D 0x04
#define LF 0x0a
#define ENTER 0x0d
/* prototypes*/
char getline(char*b,int n);
void main()

char buffer[11];
printf("