Board logo

标题: 在DOS下如何用C实现键盘某键的按下 [打印本页]

作者: hebecoco     时间: 2006-12-24 04:04    标题: 在DOS下如何用C实现键盘某键的按下

例如,我希望用C语言写一程序实现CAPS LOCK,NUM LOCK,SCR LOCK键的按下,然后对应的灯会自动亮起 大虾们知道的帮帮忙,小弟谢谢了,很急!!!

作者: bill2241     时间: 2006-12-24 10:28
你可以自己去测试啦 使用BIOSDISK函数可以 使用BIOS中断~~~~ 测试扫描码~~就可以啦

作者: zyl910     时间: 2006-12-24 23:08
BIOS内存区: 40:17 byte Keyboard flag byte 0 (see KB FLAGS) |7|6|5|4|3|2|1|0| keyboard flag byte 0 | | | | | | | `--- right shift key depressed | | | | | | `---- left shift key depressed | | | | | `----- CTRL key depressed | | | | `------ ALT key depressed | | | `------- scroll-lock is active | | `-------- num-lock is active | `--------- caps-lock is active `---------- insert is active 40:18 byte Keyboard flag byte 1 (see KB FLAGS) |7|6|5|4|3|2|1|0| keyboard flag byte | | | | | | | `--- left CTRL key depressed | | | | | | `---- left ALT key depressed | | | | | `----- system key depressed and held | | | | `------ suspend key has been toggled | | | `------- scroll lock key is depressed | | `-------- num-lock key is depressed | `--------- caps-lock key is depressed `---------- insert key is depressed

作者: hebecoco     时间: 2006-12-25 22:07
感谢大家,问题已经解决了~我是直接从0x00400017H中进行位操作来实现的 但现在我又面临一个新的测试,还是关于键盘测试的,有些特殊的键盘上面有一些很特别的键,如SONY笔记本中一些键盘上有播放/暂停,快近,后退,停止键等,这些键在WINDOWS下是可以的,现在我要在DOS下进行测试,在DOS中按下肯定是没有任何反应的,但是在BIOS中肯定有相应的响应,或者有相应的扫描码被存入到键盘缓冲区中,但是我现在不知道如何去抓,请教下有没有谁做过这样的测试. 用BC和TC都可以,如果有汇编也可以,不过我汇编不是熟 [ Last edited by hebecoco on 2006-12-25 at 03:16 PM ]

作者: hebecoco     时间: 2006-12-26 00:05
我有试过用bioskey()函数,抓不出来 现在主要的问题是我不知道在哪个地址位按那几个特殊键有变化,如果能知道哪个位的话,我直接用peek读出来就可以了,我有试过从键盘缓冲区0x401c中去读,也读不出来 有知道的麻烦告知下,谢谢了~~~~

作者: AlwaysInherit     时间: 2006-12-26 02:00
wikipedia ps/2 connector -> www.computer ... yboard/ -> www.computer ... s2.html

作者: hebecoco     时间: 2006-12-26 03:13
AlwaysInherit 感谢你提供的资料,我大概的看了一遍,其中提到MAKE/BREAK的问题,一个代表是键的按下,一个代表是键的释放,但我很想知道这2个值可以从从哪里读出来,是从0X64中吗?

作者: hebecoco     时间: 2006-12-26 03:17
还是要用到中断?我对中断还不是很明白,如果用中断那又是如何实现该值的获取

作者: AlwaysInherit     时间: 2006-12-26 03:55
kb 的 scancode ?? 那好像在kb buffer 中 我也不是清楚,你是问怎样读出kb 的 scancode吗? 还是加对应的scancode 到kb buffer中?

作者: hebecoco     时间: 2006-12-26 04:10
我有试过在kb buffer中抓,但抓不出来,kb buffer的地址为0x0040001CH 现在我是我想抓make code 和break code;

作者: AlwaysInherit     时间: 2006-12-26 04:53
看样子,我误会你的意思! www.nondot.org/sabre ... 有programming with kb tutorial,与一些接口用法 我觉得应该是处理 kb interrupt , 那个 BIOS data areas 中 40H:17H 是 IBM PC 时代定下的。 不能"看" ps/2 全部 data pin 进来的 data 。至于他家os 有没有定出一些比较方便的接口,我不清楚!看有没有人能顶上 [ Last edited by AlwaysInherit on 2006-12-26 at 05:04 AM ]

作者: hebecoco     时间: 2006-12-26 06:16
我得花点时间消化下,还是非常感谢你提供的资料

作者: hebecoco     时间: 2006-12-26 21:25
虽然理解了很多,但是还是没办法读那几个播放键,我有试过把键盘缓冲区里的值全部读出来,然后去按那几个BUTONN,发现BUFFER里面的32个字节没有任何变化,就是说那几个BUTONN是根本没有经过键盘BUFFER. 但现在那几个BUTONN在资料里面有相应的make code 和break code,那肯定是可以读出来的,只是不知道在哪里读,这步才是最关键的,

作者: hebecoco     时间: 2006-12-26 21:32
有人提到用键盘中断,本人还不是很会用中断,但我想就算是用中断,也要知道用什么值去中断,在DOS下,CPU是否又会响应这个中断,如果说CPU没做出相应的响应,我也是没办法去测的, 用中断我觉得模拟一个键的按下可能容易点,但要去测的话实现起来应该不是很简单 请教各路高手,有没有解决的办法,最主要的就是解决怎么去读那些BUTONN的make code和break code,

作者: zyl910     时间: 2006-12-26 22:22
那几个播放键不是PC标准,不会进键盘缓冲区的 你只有挂接键盘中断(IRQ1,INT9),从60h端口得到按键扫描码 而且注意,由于那几个播放键不是PC标准 所以在Windows系统不会为这几个按键触发中断的(还包括Win键、快捷菜单键等等) 也就是说,只有纯DOS下才能得知那几个按键的按下与释放 以前写的一个专用来研究按键扫描码的小程序:
#include <stdio.h>
#include <conio.h>
#include <dos.h>

#ifdef __cplusplus
    #define __CPPARGS ...
#else
    #define __CPPARGS
#endif


#define	MAXKEYQUEUE	0x40
static	char	kqQueue[MAXKEYQUEUE];	// 队列
static	int	kqFirst = 0;	// 指向首个数据
static	int	kqLast = 0;	// 指向首个空位
static	int	kqCount = 0;	// 该队列有多少元素

#define KSC_PRESS	0x80
#define KSC_DATAMASK	0x7F
#define	KSC_EXTEND	0xE0

#define	KSC_ESC	1

void interrupt get_out(__CPPARGS);    /* interrupt prototype */

void interrupt (*oldfunc)(__CPPARGS); /* interrupt function pointer */


int main(void)
{
	unsigned char	byKey;
	int	iEscCount=0;

	puts("Esc*3: Exit\n");

	/* save the old interrupt */
	oldfunc  = _dos_getvect(9);

	/* install interrupt handler */
	_dos_setvect(9,get_out);

	/* do nothing */
	while (iEscCount < 3){
		/* show key queue */
		while(kqCount > 0){
			/* get curent ScanCode */
			__asm	cli;
				byKey = kqQueue[kqFirst++];
				if (kqFirst >= MAXKEYQUEUE)	kqFirst -= MAXKEYQUEUE;
				kqCount--;
			__asm	sti;

			/* show */
			printf("%X\t", byKey);

			/* check Esc */
			if (byKey & KSC_PRESS)	{
				if (KSC_ESC == (byKey & KSC_DATAMASK))	{
					iEscCount++;
					if (2 == iEscCount){
						clrscr();
					}
				}else	{
					iEscCount = 0;
				}
			}
		}
	};

	/* restore to original interrupt routine */
	_dos_setvect(9,oldfunc);

	puts("Success");
	return 0;
}

void interrupt get_out(__CPPARGS)
{
	{
		unsigned char byKey;

		/* get ScanCode */
		byKey = inportb(0x60);

		/* add to key queue */
		if (kqCount < MAXKEYQUEUE){
			kqQueue[kqLast++] = byKey;
			if (kqLast >= MAXKEYQUEUE)	kqLast -= MAXKEYQUEUE;
			kqCount++;
		}
	}
	oldfunc(__CPPARGS);
}

作者: hebecoco     时间: 2006-12-27 03:00
dear zly910: 你好! 感谢你提供的宝贵资料! 我有将你这段代码进行编译和运行,可以抓到那几个键而且有相应的响应,因本人知识的贫乏对中断还不是很懂,所以有几个问题还要请教下 www.computer ... s1.html 你应该有看过,对于一般的按键makecode和breakcode都只有1个字节,读出来的值是正常的,但是有很多有2个字节的,比如说播放键是EO,22和E0,2A,但是这里只能抓出2个E0来,也就是说只能取出他们的前面一个字节,是否可以改成抓出2个字节的,因为我不知道你用中断是如何实现的,还请你指点一下.

作者: hebecoco     时间: 2006-12-27 03:49
我有试过将char换成int,将inportb(0x60)换成inport(0x60),效果还是一样

作者: hebecoco     时间: 2006-12-27 05:20
还是要修改中断?该如何改?

作者: AlwaysInherit     时间: 2006-12-27 06:13
偷偷google再来顶帖! www.osdev.org/osfaq2 ...
What's that E0 scancode i get all the time ? ...you should just store the info and wait for the next scancode to know what key even actually occured...

作者: hebecoco     时间: 2006-12-27 06:27
谢谢,在study中. 还想问下zly910,你那段代码中加入的那2行汇编是起什么作用? 如果有看到,麻烦解答下,谢谢 有知道的大虾也帮看下

作者: AlwaysInherit     时间: 2006-12-27 06:44
disable interrupt -> 避免被其他发生的interrupt切换走 , 应该是避免跟ISRs race

作者: hebecoco     时间: 2006-12-27 08:44
问题解决了,读2次0X60,因为中断程序没次只能是读1个字节,就算是换成inport()也是没用的,象那些有2个字节的键,会先进去高位,一般高位都是E0,然后再进去低,如果没有新的中断近来,低位会一直驻留在0X60的PORT中!! 很高兴问题解决,感谢AlwaysInherit和zly910的顶立支持,感谢CCTV,感谢ChinaV!!

作者: hebecoco     时间: 2007-3-23 04:57
提上来看下