Check if a key was pressed. If so, return the ascii value of the key.
Otherwise, returns 0.
I made this code, but I'm can't capturing the ascii value of the
arrows.
org 100h
mov ah, 11h
int 16h
je pre_quit
mov ah,8h
int 21h
cmp al,0 ; if not extended key
jnz quit
int 21h
jmp quit
pre_quit:
mov al,0
quit:
mov ah,4ch
int 21h
Those lines seems to have no effect on my machine. So, I commented them
out:
; mov ah, 11h
; int 16h
; je pre_quit
> mov ah,8h
After trying to figure out why your code didn't work for a while, I realized
that you probably confused ah=8h with ah=6h. It seems that ah=8h doesn't
check for extended keys. I think what you want is:
kbwait:
mov ah,6h
mov dl,0ffh ; needed to select input
> int 21h
It seems you are missing a wait loop. The function returns ZF set for no
key available. It sets AL=0, if not an extended key. But, you must wait
until a key is available (ZF clear) to check for AL=0... So:
int21h
jz kbwait ; wait for key
> cmp al,0 ; if not extended key
> jnz quit
> int 21h
> jmp quit
>
> pre_quit:
> mov al,0
> quit:
> mov ah,4ch
> int 21h
>
In total, I get this:
org 100h
; mov ah, 11h
; int 16h
; je pre_quit
kbdwait:
; mov ah,8h
mov ah,6h ; read w/extended
mov dl,0ffh ; input
int 21h
jz kbdwait ; wait for key
cmp al,0 ; if not extended key
jnz quit
int 21h
jmp quit
pre_quit:
mov al,0
quit:
mov ah,4ch
int 21h
HTH,
Rod Pemberton
PS. Threw on comp.os.msdos.programmer in case someone there knows how to do
it with ah=8h, or has a better method.
I don't think arrow keys... or function keys, "editing keys" (home, end,
etc.) actually *have* an "ascii code" (it's a "bios scan code", which
looks the same). Been a while since I've done this, so I may misremember
(my current install of dosemu is gefukt, and I don't feel like rebooting
right now... I may have to...)
> org 100h
>
> mov ah, 11h
> int 16h
> je pre_quit
Rod says this does nothing on his machine. As I remember it, this should
"check for" (not remove from keyboard buffer) a key. It should return
immediately, with the zero-flag set if no key is available... so your
program would end before it's begun! I must misremember this...
One way to get "extended" keys is...
mov ah, 10h
int 16h
cmp al, 0
jnz not_extended
cmp ah, 48h
je up_arrow
cmp ah, 50h
je down_arrow
; etc...
not_extended:
; it's a "regular" key
Note that we check ah, if al is zero. I'm pretty sure I remember this
correctly...
> mov ah,8h
> int 21h
> cmp al,0 ; if not extended key
> jnz quit
> int 21h
> jmp quit
But I thought I remembered that this should work, too - if it returns
zero in al, call it again to get the "extended" key (in al). You *might*
have to reload ah with 8 before the second int 21h. I don't think you
should have to, but... easy to check, anyway. :)
> pre_quit:
> mov al,0
> quit:
> mov ah,4ch
> int 21h
As I recall, if you do get the "extended key" on the second call, it'll
look like 'M', 'H', 'P', ??? So you may not be able to distinguish the
return value from a "regular" key. You might want to return 0FFh or
something if it's an extended key... depending on what you intend to do
with this.
Rod's idea of using ah=6 (instead of 1, 7, or 8) may work out better for
you, but I think int 16h is easier (just check ah, instead of calling it
again).
Your description of the program indicates that you want to return zero
if no key is pressed. Generally, there won't be one - unless you're a
*really* fast typist (or do myprog<sometext). Is this really what you want?
Best,
Frank
According to my docs the jz kbdwait should not be required as int 21
func 08h is supposed to wait for the next key press or return the last
key pressed. My dos machine is lent out so I can't test.
The cmp al, 0 is required to check for extended keys.
int 16h, func 00h also waits.
This func returns scan codes in ah and ascii in al. al will be zero if a
non-ascii key was pressed. This func doesn't require a second call to
get the extended keys
int21 and int16 both have funcs to check for a key being ready to
prevent waits.
Alex
The arrow keys, F1..F10, home, end, delete, pgup, pgdn and insert do
return unique AH values (scan codes) but no ascii codes in AL, for
example...
Here's the main loop of my program..
;;--------------------------------------------------------60
[SECTION .cseg vstart=0100h] ;; for .com
Key_Scan:
mov ax, 0000h ;; waits for keypress
Int 16h ;; ZF is none awaits.
;; jz Key_Scan_Lp ;; .else. AH`scan code, AL`ascii chr
;; -= ck Xit chr; Q, q =-
cmp ax, 1071h ;; q
je Key_Scan_Xit
cmp ax, 1051h ;; Q
je Key_Scan_Xit
;; -= .else. conout AX =-
call conout_AX
nop
nop ;; space for trap
nop
Key_Scan_Lp:
jmp Key_Scan
Key_Scan_Xit:
call conout_AX ;; emit Q,q vals
mov ah, 0 ;; pause for keypress..
int 16h
int 19h ;; done
;;--------------------------------------------------------60
;; S U B R O U T I N E S
;;--------------------------------------------------------60
My notes say Function 11h is a keyboard Check function, which does not
remove the word from the keyboard buffer, you still need to 'remove'
it by a 'read' Function 0h. I dunno, this might require
experimentation. There were 3 keyboard base types. PC, XT, and AT
class.
For example: up arrow is AX=4800h, F10 is AX=4400h,'a' is AX=1E61h,
shift+'a' is AX=1E41h (A). -on this Dell I'm banging on..
hth.
Steve
> > org 100h
>
> > mov ah, 11h
> > int 16h
> > je pre_quit
>
> Rod says this does nothing on his machine. As I remember it, this should
> "check for" (not remove from keyboard buffer) a key. It should return
> immediately, with the zero-flag set if no key is available... so your
> program would end before it's begun! I must misremember this...
>
Yeah, _Check Keyboard_
Hi again Carlos,
Due to some wind, the power went out, so I took the opportunity to boot
to dos and refresh my memory. Your program does what you say... since no
key has been pressed, it returns zero.
> I made this code, but I'm can't capturing the ascii value of the
> arrows.
... or any other ascii value, that I can see...
> org 100h
>
> mov ah, 11h
> int 16h
> je pre_quit
Since no key has been pressed, this goes right to "pre_quit" and returns
zero, as described. The rest of it never gets to run. This probably
isn't what you really want to do.
> mov ah,8h
> int 21h
> cmp al,0 ; if not extended key
> jnz quit
> int 21h
> jmp quit
This would work, if it got to run.
> pre_quit:
> mov al,0
> quit:
> mov ah,4ch
> int 21h
Rod gave an example of this using ah=6 instead of 8 - which is not the
problem, I don't think. Steve gave an example with int 16h/0 - int
16h/10h is the same thing, but for "103-key" keyboards, it catches a few
"exotic" keys that int 16h/0 misses. Here's still another example...
using int 21h/7 (same as int 21h/8, but it ignores control-c... so we
can see its code). This prints out a hex value and goes back for more
(hit "ESC" to quit).
Best,
Frank
; nasm -f bin -o wotkey.com wotkey.asm
org 100h
top:
mov ah, 7 ; no echo - change to 1 if you want
int 21h ; get a key
call byte2ha ; show it
or al, al ; if it was 0
jz top ; call again
call newline
cmp al, 1Bh ; was it escape?
jnz top ; no? do more.
ret
;---------------------------
;------------------------
; thanks Ben
; thanks TAD
byte2ha:
push ax
aam 16 ; 2
hex:
xchg ah,al ; 2
cmp al,0Ah ; 2
sbb al,69h ; 2
das ; 1
int 29h
mov al,89h ; 2 (thanks Ruud)
jc short hex ; 2
pop ax
ret
;------------------------
;----------------------
newline:
push ax
mov al, 13
int 29h
mov al, 10
int 29h
pop ax
ret
;-------------------
RBIL doesn't document that 8h returns extended. That may be reason enough
to avoid 8h. But, I rechecked. 8h does return extended, at least for arrow
keys, on this machine.
> - which is not the
> problem, I don't think.
I think the real problem was that he had no keypresses in the keyboard input
buffer and no wait for a keypress... He was missing a loop on ZF for a
wait, or he needed to fill the keyboard buffer.
Rod Pemberton
This is c source code:
int main()
{
int c = 0;
if (_kbhit()) //if key was pressed.
{
c = _getch(); //get key
if (c == 0 || c == 0xE0) //if is extended key
{
c = _getch();//get extended key
}
}
return c;//return 0 o value of key.
}
When you asked for 16-bit assembler, you're asking for an environment that
supports 16-bit x86 cpu mode. These are real-mode DOS, v86 mode emulated
DOS environments like a Windows "console" application (or dosbox) or DOS
running under Linux's DOSEMU, DOSBox application, or your own 16-bit
bootloader or a 16-bit operating system, etc. If you used a DOS compiler,
e.g., DJGPP, you could see what BIOS and DOS function calls are used.
> If anyone thinks [example C code] can be done with 16-bit assembler,
> please show me how to.
We already showed you that.
*Where* are you trying to use the 16-bit code? By "where", I mean what OS,
what cpu mode, what environment? You can only use it in 16-bit mode, on
x86, in an environment that supports 16-bit x86 cpu mode.
You can't be asking for 16-bit code that works with an existing modern OS
like Windows 7, Vista, XP or Linux. These OSes limit or prohibit use of
16-bit code, like BIOS calls, and don't support DOS calls. Supposedly, x86
64-bit mode doesn't support 16-bit code natively, so a 64-bit OS might not
have support for 16-bit mode.
> In the end I did but at c using the Windows API calling to functions
> _kbhit and _getch of msvcrt.dll
You can't use DOS and BIOS calls in pure Windows. You can use DOS and BIOS
calls in real-mode DOS or a Windows "console" application (or dosbox). A
Windows "console" is supported in Windows 95, 98, SE, ME, NT, 2K. AFAIK, a
Windows "console" is *not* supported in Windows 7 or Vista. Windows XP
"console" has limited support.
> ... Windows API ... msvcrt.dll
It seems you're using a Windows C compiler which calls Windows C functions.
It's very likely Windows functions are *not* using BIOS or DOS. You might
be able to produce Windows "console" applications with your Windows C
compiler, but these won't use DOS or BIOS calls either. The last version
that supported DOS was MSVC 1.52c. This version hasn't been available for
many years.
> int main()
> {
> int c = 0;
> if (_kbhit()) //if key was pressed.
> {
> c = _getch(); //get key
> if (c == 0 || c == 0xE0) //if is extended key
> {
> c = _getch();//get extended key
> }
> }
> return c;//return 0 o value of key.
> }
DJGPP (a DOS compiler using GCC and custom C library) calls:
bioskey() - int 16h, ah=00h, 01h, 02h, 10h, 11h, or 12h
kbhit() - int 16h, ah=11h - after comparing memory 40:1Ah (pointer to first
character in BIOS keyboard buffer) and 40:1Ch (pointer to first free
character slot in BIOS keyboard buffer)
getch() - int 21h, ax=0700h *or* buffered character - after checking isatty
and line buffering on stdout & stderr and flushing output
You could also look at the OpenWatcom code for the DOS versions of these
functions. OW will also have Windows versions.
> This is c source code:
I understand what the C code you posted is supposed to do. It does what I
expect.
But, I want you to explain to me what this actually does do with your
compiler. Did you run it? Does it immediately exit? Does it ever return a
letter or extended key?
You posted C code to demonstrate the issue. But, it's unlikely I'm using
the same C compiler in the same environment. So, I may be experiencing
different results. E.g., I'm not using a Windows C compiler. Functions
like getch, kbhit, bioskey, are non-portable and can be implemented
differently from compiler to compiler.
When I change _kbhit() and _getch() to kbhit() and getch() and compile with
*multiple* DOS compilers, this has the exact same problem as your assembly
code in real mode DOS and Windows "console". There are *NO* keys in the
keyboard buffer. There is *NO* wait for someone to press a key to put a key
in the keyboard buffer. This causes the code to immediately exit because
if(kbhit()) fails (is false). You can wrap the if(_kbhit()) in a while(1)
loop, adding a break statement inside to get it to wait for a keypress.
Rod Pemberton
Horsefeathers! As Rod points out, your OS may not support 16-bit code,
but that's not your code's fault. ( http://www.dosbox.com/ may help?)
> In the end I did but at c using the Windows API calling to functions
> _kbhit and _getch of msvcrt.dll
Sure. But that's 32-bit code (or 64-bit code, perhaps?). (we can do this
in asm, too, of course!)
> If anyone thinks that can be done with 16-bit assembler, please show
> me how to.
Something like...
org 100h
mov ah, 11h
int 16h
je pre_quit
mov ah,8h
int 21h
cmp al,0 ; if not extended key
jnz quit
int 21h
jmp quit
pre_quit:
mov al,0
quit:
mov ah,4ch
int 21h
Oh, wait! That's your original code! In what way does it not do what you
want?
> This is c source code:
> int main()
> {
> int c = 0;
> if (_kbhit()) //if key was pressed.
> {
> c = _getch(); //get key
> if (c == 0 || c == 0xE0) //if is extended key
Anyone ever actually seen 0xE0 at this level? I don't recall.
> {
> c = _getch();//get extended key
> }
> }
> return c;//return 0 o value of key.
> }
Or, put another way, what does an executable made from this C code do
different from your asm code?
What does (either program) do if you feed it some input - "myprog<some.txt"?
Do you have a convenient way to see the return value in dos? IIRC, we
need to run the thing from a batch file and check "errorlevel". PITA.
(in Linux, we can do "echo $?" to see the exit code from the last
program!) We could arrange to "instrument" your code so you could more
easily see what it's doing, if that would help...
What might be more "useful" is to wrap the first three lines of your
code in a "timer" so we could "return zero if no key is hit within ten
seconds" or something...
Honestly, Carlos, I don't see anything wrong with your original code...
except that it isn't very useful to return zero immediately if^H^H since
no key is hit...
Best,
Frank
Yes, with function 11H, not 1, the gray keys (the separate arrow
and navigation keys) use 0E)H.
Regards,
Steve N.