Here is Turbo Pascal code that gives instant
notice of any non-character-code key being pressed or
released. The GOTOs are used to give fast cycling
and response.
uses Dos, Crt;
var Regs :registers;
KeyCode,Regular_Key,Extended_Key :Byte;
var Attr,bite,bit0,bit1,bit2,bit3,
bit4,bit5,bit6,bit7 :integer;
Procedure Test_Bits;
begin
if (bite AND 1) = 1 then bit0:= 1 ELSE bit0:= 0;
if (bite AND 2) = 2 then bit1:= 1 ELSE bit1:= 0;
if (bite AND 4) = 4 then bit2:= 1 ELSE bit2:= 0;
if (bite AND 8) = 8 then bit3:= 1 ELSE bit3:= 0;
if (bite AND 16) = 16 then bit4:= 1 ELSE bit4:= 0;
if (bite AND 32) = 32 then bit5:= 1 ELSE bit5:= 0;
if (bite AND 64) = 64 then bit6:= 1 ELSE bit6:= 0;
if (bite AND 128) = 128 then bit7:= 1 ELSE bit7:= 0;
end;
Procedure Get_Shift_Status;
begin
with Regs do
begin
Regs.AH:= 02;
Intr($16,Regs);
end;
Bite:= Regs.AL;
end;
Procedure Get_Key;
begin
with Regs do
begin
Regs.AH:= 00;
Intr($16,Regs);
end;
Regular_Key:= Regs.AL;
Extended_Key:= Regs.AH;
end;
Procedure Is_KeyPressed;
begin
with Regs do
begin
Regs.AH:= 01;
Intr($16,Regs);
end;
Get_Key;
end;
Procedure Extended;
begin
KeyCode:= Extended_Key;
end;
Procedure Loop;
begin
Is_KeyPressed;
If Regular_Key = 0 then Extended ELSE KeyCode:= Regular_Key;
end; (* In the case of TSRs ...... *)
var RightShift,LeftShift,NumLock,CapsLock,Insert,Control,ALT :Boolean;
LABEL CYCL,BYE;
BEGIN (*** Main ***)
RightShift:= False; LeftShift:= False; NumLock:= False;
CapsLock:= False; Insert:= False; Control:= False; ALT:= False;
CYCL:
while NOT (KeyPressed) do
begin
Get_Shift_Status;
Test_Bits;
if bit0 = 1 then RightShift:= True ELSE RightShift:= False;
if bit1 = 1 then Write('Left Shift key pressed ');
if bit2 = 1 then Control:= True ELSE Control:= False;
if bit3 = 1 then ALT:= True ELSE ALT:= False;
if bit4 = 1 then Write('ScrollLock is ON ');
if bit5 = 1 then Write('NunLock is ON ');
if bit6 = 1 then CapsLock:= True ELSE CapsLock:= False;
if bit7 = 1 then Write('Insert is ON ');
if RightShift = True then write('RightShift ');
if CapsLock = True then Write(' CAPS ON ');
if Control = True then Write(' CONTROL Pressed ');
if ALT = True then Write(' ALT Pressed ') ELSE Write('ALT IS UP ');
end;
Is_KeyPressed;
if Regular_Key = 27 then GOTO BYE;
GOTO CYCL;
BYE:
end.
(* If you are writing TSRs, a BIOS listing is needed *)
INT 16 - KEYBOARD - READ CHAR FROM BUFFER, WAIT IF EMPTY
Sub-Function AH = 00h
Return: AH = scan code AL = character
INT 16 - KEYBOARD - CHECK BUFFER, DO NOT CLEAR
Sub-Function AH = 01h
Return: ZF = 0, character in buffer
AH = scan code AL = character
ZF = 1, no character in buffer
INT 16 - KEYBOARD - GET SHIFT STATUS
Sub-Function AH = 02h
AL = shift status bits
0 = right shift key depressed 1 = left shift key depressed
2 = CTRL depressed 3 = ALT depressed
4 = SCROLL LOCK active 5 = NUM LOCK active
6 = CAPS LOCK active 7 = INSERT state active
Joe Fischer
It gets the value from port 0x60.
If value > 128, then that means that the key with scancode
(value - 128) is unpressed.
if value < 128, then that means that the key with scancode (value)
is pressed.
If that is correct, it is something many people
will be happy to learn.
But does it work on ALT, SHIFT, etc.? Let me
state what I think you are saying, and let me know
if I understand.
If you press a key, the port returns a value.
If the value is less than 128, then the key represented
by that value is pressed.
What I am not clear on, is, if _no_ key is
pressed, what value does the port return? Is it
just the value of the key that was pressed, but
just released?
And are you using the port read to get instant
action on both presses and releases? The code I
posted was for the keys which do not display characters,
are those included in the values received from reading
the port? I thought you asked about the ScrollLock
and NumLock keys, and the data received from the
interrupt call has the information from eight keys
packed in one byte, if a particular bit is 1, the
key is pressed, and if it is 0, the key is not
pressed. But the routine has to detect when the
key is released.
I will read you original message again and
try to write a routine using the port values, I
really appreciate ibtant response when a key is
pressed, and some compilers do not have a function
that is fast enough.
Regards,
Joe Fischer
Look into the BIOS services.
Int 15h, Function 4Fh - Keyboard Intercept.
Int 16h, Function 02h - Return Shift Flag Status
Int 16h, Function 12h - Return Extended Shift Flags.
Some compilers provide an API to these BIOS service. For instance,
in Borland C++ (Version 5), see the routine bioskey().
Jim Boyle
Yep!
> What I am not clear on, is, if _no_ key is
>pressed, what value does the port return? Is it
>just the value of the key that was pressed, but
>just released?
well, if nothing happens, the port keeps returning the same action.
I mean, if the last action was a key release, then it keeps returning
128 + scancode of that key, and if the last action was a key press,
it keeps returning the scancode of that key
> And are you using the port read to get instant
>action on both presses and releases?
no, you've got to put the 'key_get ()' routine in an timer-interrupt.
You can also create a loop in a program that keeps looping, but
I prefer the timer-interrupt. When you use a timer-interrupt,
you've got to speed up the hardware timer speed, because the
default speed is 18.2 ticks per second (Hz) and this is very slow, so
you will miss fast keystrokes.
You can speed up the timer by using the folowing routine :
void settimerspeed (int Hz)
{
long v;
if (Hz > 0)
v = ((long)1193180 / (long)Hz);
else
v = 0xFFFF;
outportb (0x43, 0x36);
outportb (0x40, v & 0xFF);
outportb (0x40, v >> 8);
}
Sylvester Hesp
What you need to do is to put the above code in the keyboard interrupt:
(int 9). Remember to chain the interrupt. Or if this is an application
program and not a TSR you could use the interrupt that Int 9 calls and
is for applications to hook. I do not remember its number now. With
that one does not have to hook. If one uses HLL one has to however, make
sure that the values of registers are kept.
Osmo
No can't do...
If you put the function in int9, it's only called when a user presses
a key, then it wait's the typematic rate and delay. Let's say you
are writing a game and you put the key-function in int9. If you
must move your character with the keyboard, it's pretty hard.
If you press a key (i.e. left) your character moves left, wait's for
about 250 ms. and then starts moving again.
You must put it in the timer interrupt (8 or 0x1C), so the function
always get's called, whether you touch a key, release a key or
simply do nothing.
I haven't done this myself, but how about this...
Set up a table with 128 entries, one for each scan code. Hook the the
keyboard interrupt. When the interrupt routine gets a make code for a
key set the table entry to true, for a release code set the entry to
false. In your program you just check the table to see which keys are
pressed.
B&R Batz
That IS what my pgrogram does! (except for the int9 part; I use
int8, the timer-interrupt)
no input data
% related memory:
40:17 = updates keyboard flag byte 0
40:18 = updates keyboard flag byte 1
40:1A = queue head ptr is set to buffer start if Ctrl-Break is hit
40:1C = updates buffer tail pointer for each keystroke; sets
queue tail ptr is set to queue start if Ctrl-Break is hit
40:1E = updates keyboard buffer (32 bytes)
40:71 = updates bit 7 of the BIOS break flag if Ctrl-Break is hit
40:72 = updates reset flag with 1234H if Ctrl-Alt-Del pressed
40:96 = indicates keyboard type (AT,PS/2)
40:97 = updates keyboard LED flags (AT,PS/2)
FFFF:0 = reboot code called if Ctrl-Alt-Del pressed
% related interrupts:
~INT 5~ invoked if print screen key pressed
~INT 1B~ invoked if Ctrl-Break key sequence pressed
~INT 15,85~ invoked on AT if system request key is pressed
~INT 15,4F~ invoked on machines after PC/AT with AL = scan code
- records key press and key release via IRQ1/8259 and
stores scan codes in the BIOS buffer located at 40:1C
- keyboard controllers also buffer data when interrupts are
disabled at the ~8259~ interrupt controller
- keyboard controller is capable of storing 16 keystrokes
even when interrupts are disabled at the 8259
- normal INT 9 execution takes approximately 500 microseconds;
at least one standard XT BIOS is known to take up to 1.3
milliseconds to execute
^INT 15,4F - Keyboard Intercept (BIOS date specific)
AH = 4F
AL = scan code
CF = set to 1 (via STC instruction)
on return
AH = 80h, CF set (PC, PCjr)
= 86h, CF set (XT BIOS 11/8/82, AT BIOS 1/10/84)
AL = CF set, new scan code
= CF clear, original scancode
- available with XT BIOS after 11/8/82, AT BIOS after 1/10/84
- called by ~INT 9~, makes allowance for keyboard translation
- normally returns the scan code in AL, with CF set
- if function returns with CF clear, INT 9 ignores keystroke
- do not rely on this function being called for each INT 9 since
any user INT 9 handler can exit prematurely and circumvent
this function
^INT 15,85 - System Request Key Pressed
AH = 85h
AL = 00 key pressed
= 01 key released
on return:
CF = 0 if successful
= 1 if error
AH = 80h for PC or PCjr
= 86h for XT (BIOS after 11/8/82)
- called by BIOS when the System Request key is pressed/released
- available on machines with newer BIOS and keyboards
BDA - BIOS Data Area - PC Memory Map
Address Size Description
00:00 256 dwords Interrupt vector table
30:00 256 bytes Stack area used during post and bootstrap
40:00 word COM1 port address
40:02 word COM2 port address
40:04 word COM3 port address
40:06 word COM4 port address
40:08 word LPT1 port address
40:0A word LPT2 port address
40:0C word LPT3 port address
40:0E word LPT4 port address (except PS/2: Extended BIOS Data Area
segment) (PS/2, see ~EBDA~)
40:10 2 bytes Equipment list flags (see ~INT 11~)
40:10 (value in INT 11 register AL)
0 IPL diskette installed
1 math coprocessor
2 \old PC system board RAM < 256K pointing device installed (PS/2)
3 / not used (PS/2)
4 \initial video mode
5 /
6 \# of diskette drives, less 1
7 /
40:11 (value in INT 11 register AH)
0 0 if DMA installed
1 \
2 |number of serial ports
3 /
4 game adapter
5 not used internal modem (PS/2)
6 \number of printer ports
7 /
40:12 byte PCjr: infrared keyboard link error count
40:13 word Memory size in Kbytes (see ~INT 12~)
40:15 byte Reserved
40:16 byte PS/2 BIOS control flags
40:17 byte Keyboard flag byte 0 (see ~KB FLAGS~)
0 right shift key depressed
1 left shift key depressed
2 CTRL key depressed
3 ALT key depressed
4 scroll-lock is active
5 num-lock is active
6 caps-lock is active
7 insert is active
40:18 byte Keyboard flag byte 1 (see ~KB FLAGS~)
0 left CTRL key depressed
1 left ALT key depressed
2 system key depressed and held
3 suspend key has been toggled
4 scroll lock key is depressed
5 num-lock key is depressed
6 caps-lock key is depressed
7 insert key is depressed
40:19 byte Storage for alternate keypad entry
40:1A word Offset from 40:00 to keyboard buffer head
40:1C word Offset from 40:00 to keyboard buffer tail
40:1E 32 bytes Keyboard buffer (circular queue buffer)
40:3E byte Drive recalibration status
0 1=recalibrate drive 0
1 1=recalibrate drive 1
2 1=recalibrate drive 2
3 1=recalibrate drive 3
4 \
5 |unused
6 /
7 1=working interrupt flag
40:3F byte Diskette motor status
0 1=drive 0 motor on
1 1=drive 1 motor on
2 1=drive 2 motor on
3 1=drive 3 motor on
4 \
5 |unused
6 /
7 1=write operation
40:40 byte Motor shutoff counter (decremented by ~INT 8~)
40:41 byte Status of last diskette operation (see ~INT 13,1~)
0 invalid diskette command
1 diskette address mark not found
2 sector not found
3 diskette DMA error
4 CRC check / data error
5 diskette controller failure
6 seek to track failed
7 diskette time-out
40:42 7 bytes NEC diskette controller status (see ~FDC~)
40:49 byte Current video mode (see ~VIDEO MODE~)
40:4A word Number of screen columns
40:4C word Size of current video regen buffer in bytes
40:4E word Offset of current video page in video regen buffer
40:50 8 words Cursor position of pages 1-8, high order byte=row low
order byte=column; changing this data isn't reflected immediately on the
display
40:60 byte Cursor ending (bottom) scan line (don't modify)
40:61 byte Cursor starting (top) scan line (don't modify)
40:62 byte Active display page number
40:63 word Base port address for active ~6845~ CRT controller
(3B4h=mono,3D4h=color)
40:65 byte 6845 CRT mode control register value (port 3x8h) (EGA/VGA
values emulate those of the MDA/CGA)
40:66 byte CGA current color palette mask setting (port 3d9h) (EGA and
VGA values emulate the CGA)
40:67 dword CS:IP for 286 return from protected mode
dword Temp storage for SS:SP during shutdown
dword Day counter on all products after AT
dword PS/2 Pointer to reset code with memory preserved
5 bytes Cassette tape control (before AT)
40:6C dword Daily timer counter, equal to zero at midnight; incremented
by INT 8; read/set by ~INT 1A~
40:70 byte Clock rollover flag, set when 40:6C exceeds 24hrs
40:71 byte BIOS break flag, bit 7 is set if ~Ctrl-Break~ was ever hit;
set by ~INT 9~
40:72 word Soft reset flag via Ctl-Alt-Del or JMP FFFF:0
1234h Bypass memory tests & CRT initialization
4321h Preserve memory
5678h System suspend
9ABCh Manufacturer test
ABCDh Convertible POST loop
????h many other values are used during POST
40:74 byte Status of last hard disk operation (see ~INT 13,1~)
40:75 byte Number of hard disks attached
40:76 byte XT fixed disk drive control byte
40:77 byte Port offset to current fixed disk adapter
40:78 4 bytes Time-Out value for LPT1,LPT2,LPT3(,LPT4 except PS/2)
40:7C 4 bytes Time-Out value for COM1,COM2,COM3,COM4
40:80 word Keyboard buffer start offset (seg=40h,BIOS 10-27-82)
40:82 word Keyboard buffer end offset (seg=40h,BIOS 10-27-82)
40:84 byte Rows on the screen (less 1, EGA+)
40:85 word Point height of character matrix (EGA+)
byte PCjr: character to be repeated if the typematic repeat key takes
effect
40:86 byte PCjr: initial delay before repeat key action begins
40:87 byte PCjr: current Fn function key number
byte Video mode options (EGA+)
0 1=alphanumeric cursor emulation enabled
1 1=video subsystem attached to monochrome
2 reserved
3 1=video subsystem is inactive
4 reserved
5 \video RAM 00-64K 10-192K 01-128K 11-256K
6 /
7 video mode number passed to ~INT 10~, function 0
40:88 byte PCjr: third keyboard status byte
EGA feature bit switches, emulated on VGA
0 EGA SW1 config (1=off)
1 EGA SW2 config (1=off)
2 EGA SW3 config (1=off)
3 EGA SW4 config (1=off)
4 Input FEAT0 (ISR0 bit 5) after output on FCR0
5 Input FEAT0 (ISR0 bit 6) after output on FCR0
6 Input FEAT1 (ISR0 bit 5) after output on FCR1
7 Input FEAT1 (ISR0 bit 6) after output on FCR1
40:89 byte Video display data area (MCGA and VGA)
0 1=VGA is active
1 1=gray scale is enabled
2 1=using monochrome monitor
3 1=default palette loading is disabled
4 (see table below)
5 reserved
6 1=display switching enabled
7 alphanumeric scan lines (see table below)
Bit7 Bit4 Scan Lines
0 0 350 line mode
0 1 400 line mode
1 0 200 line mode
1 1 reserved
40:8A byte Display Combination Code (DCC) table index (EGA+)
40:8B byte Last diskette data rate selected
0 \
1 |reserved
2 |
3 /
4 \last floppy drive step rate selected
5 /
00 step rate time of 0C
01 step rate time of 0D
10 step rate time of 0A
11 reserved
6 \last floppy data rate selected
7 /
00 500Kbps
01 300Kbps
10 250Kbps
11 reserved
40:8C byte Hard disk status returned by controller
40:8D byte Hard disk error returned by controller
40:8E byte Hard disk interrupt control flag(bit 7=working int)
40:8F byte Combination hard/floppy disk card when bit 0 set
40:90 4 bytes Drive 0,1,2,3 media state
0 \
1 |drive/media state
2 /
000 360Kb diskette/360Kb drive not established
001 360Kb diskette/1.2Mb drive not established
010 1.2Mb diskette/1.2Mb drive not established
011 360Kb diskette/360Kb drive established
100 360Kb diskette/1.2Mb drive established
101 1.2Mb diskette/1.2Mb drive established
110 Reserved
111 None of the above
3 reserved
4 1=media/drive established
5 double stepping required
6 \data rate
7 /
00 500Kbps
01 300Kbps
10 250Kbps
11 reserved
40:94 byte Track currently seeked to on drive 0
40:95 byte Track currently seeked to on drive 1
40:96 byte Keyboard mode/type
0 last code was the E1 hidden code
1 last code was the E0 hidden code
2 right CTRL key depressed
3 right ALT key depressed
4 101/102 enhanced keyboard installed
5 force num-lock if Rd ID & KBX
6 last char was first ID char
7 read ID in process
40:97 byte Keyboard LED flags
0 scroll lock indicator
1 num-lock indicator
2 caps-lock indicator
3 circus system indicator
4 ACK received
5 re-send received flag
6 mode indicator update
7 keyboard transmit error flag
40:98 dword Pointer to user wait complete flag
40:9C dword User wait Time-Out value in microseconds
40:A0 byte RTC wait function flag
~INT 15,86~ RTC wait function flag
0 1= wait pending
1 \
2 |
3 | і АДБДБДБДБДБДДДД not used
4 |
5 |
6 /
7 1=INT 15,86 wait time elapsed
40:A1 byte LANA DMA channel flags
40:A2 2 bytes Status of LANA 0,1
40:A4 dword Saved hard disk interrupt vector
40:A8 dword BIOS Video Save/Override Pointer Table address (see ~VIDEO
TABLES~)
40:AC 8 bytes Reserved
40:B4 byte Keyboard NMI control flags (convertible)
40:B5 dword Keyboard break pending flags (convertible)
40:B9 byte Port 60 single byte queue (convertible)
40:BA byte Scan code of last key (convertible)
40:BB byte NMI buffer head pointer (convertible)
40:BC byte NMI buffer tail pointer (convertible)
40:BD 16 bytes NMI scan code buffer (convertible)
40:CE word Day counter (convertible and after)
40:F0 16 bytes Intra-Applications Communications Area
(IBM Technical Reference incorrectly locates this at 50:F0-50:FF)
(BIOS/DOS Data Area)
Address Size Description
50:00 byte Print screen status byte:
00=PrtSc not active, 01=PrtSc in progress, FF- error
50:01 3 bytes Used by BASIC
50:04 byte DOS single diskette mode flag, 0=A:, 1=B:
50:05 10 bytes POST work area
50:0F byte BASIC shell flag; set to 2 if current shell
50:10 word BASICs default DS value (DEF SEG)
50:12 dword Pointer to BASIC ~INT 1C~ interrupt handler
50:16 dword Pointer to BASIC ~INT 23~ interrupt handler
50:1A dword Pointer to BASIC ~INT 24~ disk error handler
50:20 word DOS dynamic storage
50:22 14 bytes DOS diskette initialization table (~INT 1E~)
50:30 4bytes MODE command
70:00 I/O drivers from IO.SYS/IBMBIO.COM
The following map varies in size and locus
07C0:0 Boot code is loaded here at startup (31k mark)
A000:0 EGA/VGA RAM for graphics display mode 0Dh & above
B000:0 MDA RAM, Hercules graphics display RAM
B800:0 CGA display RAM
C000:0 EGA/VGA BIOS ROM (thru C7FF)
C400:0 Video adapter ROM space
C600:0 256 bytes PGA communication area
C800:0 16K Hard disk adapter BIOS ROM
C800:5 XT Hard disk ROM format, AH=Drive, AL=Interleave
D000:0 32K Cluster adapter BIOS ROM
D800:0 PCjr conventionalsoftware cartridge address
E000:0 64K Expansion ROM space (hardwired on AT+)
128K PS/2 System ROM (thru F000)
F000:0 System monitor ROM
PCjr: software cartridge override address
F400:0 System expansion ROMs
F600:0 IBM ROM BASIC (AT)
F800:0 PCjr software cartridge override address
FC00:0 BIOS ROM
FF00:0 System ROM
FFA6:E ROM graphics character table
FFFF:0 ROM bootstrap code
FFFF:5 8 bytes ROM date (not applicable for all clones)
FFFF:E byte ROM machine id (see ~MACHINE ID~)
Aggh!! I missed the earlier posts in this thread and didn't see your
original code and question. I've looked it up on DejaNews. Let's see
if I can make up for it.
The problem maybe that at the hardware level, some keys produce a
2-byte scan code. They first send a 0xe0 code followed by the keys
make/break code. For example the left-ctrl key generates a make code
of 0x1d, the right-ctrl key produces 0xe01d. In your code you process
a right-ctrl make code as release of the 0x60 key (there ain't one)
and a press of the left-ctrl key.
I would almost bet that someone has done something like this before.
I'm going to dig around some ftp sites to see what I can find.
B&R Batz
p.s. I tried posting the above and found that the news server was
down. While I waited for it to come back up, I checked around. If you
don't mind some assembly language take a look at
ftp://ftp.simtel.com/simtelnet/msdos/c/kbdhandl.zip
Sylvester Hesp wrote:
> Hi there!
> Does anyone know how to get EVERY key that is pressed.
> I'm now using this sort of function :
> char key[128];
> void timer_handler ()
> {
> int k;
> k = inportb (0x60)
> if (k > 128)
> key[k - 128] = 0;
> else
> key[k] = 1;
> }
> If I want to know if a user is pressing [esc] I just check if the value
> in key[SCAN_CODE_ESC] == 1. But now, I can't read the lock-keys and I can't
> distinguish the left CTRL/ALT with the right CTRL/ALT and the 2 cursor keys
> (grey (those 4 in the middle) and white)
> I know there is a way, but how?
There are two ways, that are somewhat the same.
Method 1: Hook interrupt 16h (or 9h)
Method 2: Read the port
From what I have seen, you have figured this out by yourself, but when you read
a key from the port or from int 16h (these are somewhat linked), you must
akknowledge to the port (keyb), that the value has been substracted, otherwise
you'll keep getting the same value (buffer is not advanced).
To get the CTRL/ALT/SHIFT key states, you can call the int 16h 12h (get
extended shift stats), but the hardware keycode does also contain these keys.
Obtaining a copy of ralph browns interrupt list, would help you a lot here.
Good luck (and write if u need help)
RayMan
> Hi there!
> Does anyone know how to get EVERY key that is pressed.
> I'm now using this sort of function :
> char key[128];
> void timer_handler ()
> {
> int k;
> k = inportb (0x60)
> if (k > 128)
> key[k - 128] = 0;
> else
> key[k] = 1;
> }
> If I want to know if a user is pressing [esc] I just check if the
> value in key[SCAN_CODE_ESC] == 1. But now, I can't read the lock-
> keys and I can't distinguish the left CTRL/ALT with the right
> CTRL/ALT and the 2 cursor keys (grey (those 4 in the middle) and
> white) I know there is a way, but how?
It depends on what you need to do. Do you want to be able to
detect several keys being pressed simultaneously? If you just
want to be able to detect all possible keypresses, and monitor
all the modifier key states, just use int 16h functions 10h,
11h and 12h. These are extended versions of the old functions
0, 1 and 2 (get keypress, test for keypress ready, get modifier
states). Check Ralf Brown's Interrupt List for the full
specification for these functions.
If you need to be able to monitor all the keys, and tell which
keys are currently up and which are down, read on.
The proper way to monitor scancodes is through int 15h function
4Fh. Check Ralf Brown's Interrupt List for full details. That
method is much safer and more reliable than polling port 60h from
a timer interrupt handler.
Int 15h function 4Fh is called by the BIOS's int 9 handler each
time the keyboard interface hardware on the motherboard generates
an interrupt (int 9, IRQ1). Int 15h function 4Fh is supported
on all machines except the original PC and early XTs.
You should intercept int 15h function 4Fh and use your intercepter
to maintain the buffer of up/down keys. Your intercepter can tell
the BIOS whether to continue processing the scancode, or whether
to throw it out. Unless you want to check for a whole lot of
specific scancodes and decide accordingly, it's easiest to always
tell the BIOS to process the code. This means that the BIOS can
still respond to Ctrl-Break, Ctrl-Alt-Del etc, and the BIOS can
keep track of all the shift states.
This means that the BIOS's type-ahead buffer will fill up, and
each new keypress will cause the BIOS int 9 handler to issue a
beep. There are many ways to handle this - the easiest are to
poll int 16h function 1 in your mainline and grab any keys from
the buffer as soon as you detect them, or to modify the keyboard
buffer head and tail pointers directly, in your int 15h function
4Fh intercepter, to stop the buffer from filling up.
The BIOS keeps three shift-state bytes in low memory - the two
at 0040:0017 and 0040:0018 and the extra flags byte at 0040:0096.
See the file MEMORY.LST that is part of RB's Interrupt List for
the bit assignments. You can use these status bytes to keep
track of the modifier key states, or you can use int 16h function
12h.
The keyboard interface micro signals a grey key with a prefix,
either 0E0h, 0E1h or 0E2h (possibly depends on the key). There
are flags for this in the status byte at 0040:0096, or you could
keep track of them yourself in your int 15h function 4Fh
intercepter. The BIOS stores a lobyte of 0E0h, 0E1h or 0E2h with
the extended key code in the type-ahead buffer, and will return
these extended key codes if you use the extended key request
functions with int 16h (functions 10h and 11h, mentioned earlier).
You can write a program to use int 16h function 10h and display
the codes returned, and try each key in turn, to get a good idea
of what the BIOS will and won't tell you. There could be slight
variations in behaviour between different BIOSes, too :-)
If you like, you can pick the full codes out of the buffer. This
is how we implemented an event manager. I think we intercepted
int 9, not int 15h function 4Fh, and chained to the BIOS handler
first, then looked at the head and tail pointers and if there was
new data in there, and if so, picked the data out and reset the
pointers. This allows the BIOS to do the messy conversion of
printable characters to ASCII, as well.
Obviously there are a few different ways to do it. If you want
to email me a more detailed description of what you need, I will
suggest the best method.
Kris
--
Kris Heidenstrom Electronic designer and programmer
khei...@clear.net.nz http://home.clear.net.nz/pages/kheidens/
Work: kr...@abbey.co.nz Wellington, New Zealand
Example code in C and some ASM is in common.zip at
http://users.uniserve.com/~alexad3
I'm only new to this interupt stuff; but I found a snippet that
does what you're already got working in the SNIPPETS collection: keywatch.c
It also handles the extended AT keyboard, as keys that are preceded
by a 0x0e (I think).
: But now, I can't read the lock-keys and I can't
: distinguish the left CTRL/ALT with the right CTRL/ALT and the 2 cursor keys
: (grey (those 4 in the middle) and white)
There is another program in the collection that does read the lock keys
etc (forgotten the name, sorry, but easy to find in the index).
--
Brendan....@fcit.monash.edu.au http://www.cs.monash.edu.au/~bren
Voice + 61 03 9905 5194 AUSTRALIA law ecommerce thesis submitted!
Hi
I am looking for an at home position working as a programmer. I have
experience using C, C++, COBOL and Access. Please email me information if you
have any to Eupho...@hotmail.com.
Thanks
In article <6tkse1$ca1$1...@towncrier.cc.monash.edu.au>,
-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp Create Your Own Free Member Forum
> : But now, I can't read the lock-keys and I can't
> : distinguish the left CTRL/ALT with the right CTRL/ALT and the 2 cursor keys
> : (grey (those 4 in the middle) and white)
Try this prog called "multikey.c"
Call instkb() first
do all things you want to do
Call uninstkb() before leaving to DOS,... !!! IMPORTANT !!!
if you forget, system will crash (DOS surely, who knows with WIN)
but:
there are 2 arrays key and ekey each of 128 bytes
every key has 1 entry in one array for itself
If it's set, that key is pressed, if not it's not.
Now you can detect up to 8 keys pressed simultaneously (!!),
depending on your keyboard-IC. (3 keys work almost on
every keyboard) to find out the right element & array,
run this program, it will show all pressed keys with
xxx for key[xxx] and Exxx for ekey[xxx].
Thats it, I hope it helps you.
#include <stdio.h>
#include <dos.h>
#include <conio.h>
volatile char key[128],ekey[128];
#define instkb() (oldkb=getvect(0x09),setvect(0x09,newkb))
#define uninstkb() setvect(0x09,oldkb)
void interrupt (far *oldkb)(void);
void interrupt newkb(void){
static extkey;
char sc=inportb(0x60);
if(sc==0xe0)extkey=1; else
{(extkey?ekey:key)[sc&0x7f]=!(sc&0x80);extkey = 0;};
oldkb();};
void main(void){
instkb(); /* install interrupt handler */
while(1){
int i;
gotoxy(1,1);
for(i = 0; i < 128; i++){
if(key[i])printf(" %3d ",i);
if(ekey[i])printf("E%3d ",i);};
printf(" ");
if (kbhit() && getch()==27) /* terminate when Esc pressed */
break;}
uninstkb(); /* remove interrupt handler */
};