This article is reprinted from the April 1990 edition of
TechNotes/dBASE IV.  Due to the limitations of this media, certain
graphic elements such as screen shots, illustrations and some tables
have been omitted.  Where possible, reference to such items has been
deleted.  As a result, continuity may be compromised.  

TechNotes is a monthly publication from the Ashton-Tate Software
Support Center.  For subscription information, call 800-545-9364.


Reading the Keys
Jeffrey B. McCrimon

When designing dBASE IV applications, it is sometimes necessary for
your application to know what state the computer keyboard is in.  For
example, knowing whether the status of the NumLock key is on, and if
it is on, turning it off so then a particular routine of your
application can use the cursor-movement keys of the numeric keypad.

Keystate.BIN is a binary file that returns the current keyboard status
of the NumLock, Insert, or CapsLock key.  Keystate.BIN also controls
the keyboard status by turning the NumLock or CapsLock on or off, or
switching its current keyboard status of NumLock or CapsLock from off
to on, or vice-versa and returning the previous status.

Using Keystate.BIN

To use Keystate.BIN, first use the dBASE IV LOAD command to load it
into memory.  For example,

LOAD Keystate

After Keystate.BIN is loaded, you have the following capabilities:
       Retrieving the current keyboard status of Insert, NumLock, or
        CapsLock.
       Turning on or off the current keyboard status of NumLock or
        CapsLock.
       Toggling the current keyboard status of NumLock or CapsLock
        from on to off and  vice versa, then returning the previous
        keyboard status.        

Keystate.BIN expects to receive two parameters before it is executed. 
For example, to execute Keystate.BIN use the CALL command or CALL()
function.

CALL Keystate WITH <key>,<status>

or

<status> = CALL("Keystate",<key>,<status>)

where <key> is a literal character string or memory variable which
holds the first letter of the key ("I" for Insert, "C" for CapsLock,
or "N" for NumLock) as the value of the key you wish to control. 
<Status> is a character memory variable that contains the value of the
operation for Keystate.BIN to perform on the specified <key>.  After
Keystate.BIN has finished its execution, <status> contains the result
of its operation.  The examples that follow describe using
Keystate.BIN.

Returning the Current Keyboard Status

To use Keystate.BIN for retrieving the current keyboard status,
initialize the <status> memory variable with the value "?".  For
example, to retrieve the current keyboard status of the Insert key
using the CALL command, type

k_status = "?"
CALL Keystate WITH "I",k_status

or, using the CALL() function, type

k_status = "?"
k_status = CALL("Keystate","I",k_status)

After execution, the <status> memory variable (k_status, in our
example) contains the value of "1" if the Insert keyboard status is on
or "0" if the Insert keyboard status is off.

Turning the Keyboard Status On or Off

To use KeyState.BIN to turn the NumLock or CapsLock keyboard status on
or off, initialize the <status> memory variable with the value "1" to
turn on the key or "2" to turn off the key.  For example, to turn off
the CapsLock key using the CALL command, type

k_status = "0"
CALL KeyState WITH "C", k_status
or using the CALL() function, type

k_status = "0"
k_status = CALL("Keystate", "C", k_status)

After execution, the <status> memory variable (k_status) retains its
initial value of "1" or "0".

To use Keystate.BIN to toggle the current keyboard status from on to
off or off to on and return its previous status, initialize the
<status> memory variable with the value " " (the space character). 
For example, to toggle the current keyboard status of the NumLock key
and return its previous status using the CALL command, type
        
k_status = " "
CALL Keystate WITH "N",k_status

or, using the CALL() function, type
        
k_status = " "
k_status = CALL("Keystate","N",k_status)

After execution, the keyboard status of NumLock is toggled which can
be verified by the "Num" marker on the status bar or scoreboard. 
Also, the <status> memory variable (k_status) contains the value of
"1" if the NumLock keyboard status was previously on or "0" if the
NumLock keyboard status was previously off.

Retrieving the Number of Parameters

The description of the CALL command and CALL() function in the dBASE
IV Language Reference documents that up to seven expressions can be
passed to a binary file.  However, it does not include some important
and useful information as to where these parameters are located and
how they are processed.

When a binary file is executed with the CALL command or CALL()
function, the CX register is loaded with the number of parameters that
were specified.  So now you can control the logic as to when your
binary file expects a certain number of parameters.  For example,
Keystate.BIN, requires two parameters, so it checks to see if the CX
register is less than two, and if so, Keystate.BIN immediately returns
to dBASE IV without any further processing.

Accessing the Parameters

The description of the CALL command and CALL() function in the dBASE
IV Language Reference does tell us that the Data Segment (DS) and Base
(BX) registers contains the address of the ASCII representation of the
first parameter.  But it does not document where the remaining
parameters, if they exist, are located.

The seven parameters can be found in the CALLed binary file through
the Extra Segment (ES) and Destination Index (DI) registers.  The
ES:DI register pair points to a 28 byte block of memory that contains
seven 4-byte pointers.  Each one of these point to the zero terminated
ASCII representation of the data for the seven parameters. 
Furthermore, unspecified parameters point to a null string.

Keystate.BIN shows how the ES:DI pair is used to process its two
required parameters.

Setting up Keystate.BIN

To use Keystate.BIN, you must first enter the source code from Listing
1 into a text file named Keystate.ASM.  Then enter the lines from
Listing 2 into Assemble.BAT.  Assemble.BAT is a generic DOS batch file
that you can use to assemble and create any binary file.

It is also assumed that you have Microsoft Assembler (MASM) Version
5.1, which includes the macro assembler (Masm.EXE) and the linker
(Link.EXE).  You will also need Exe2bin.EXE, which is supplied with
DOS.  Assemble.BAT also expects Masm.EXE, Link.EXE, and Exe2bin.EXE to
be available in the current directory or in your DOS path.
To assemble Keystate.BIN after the files Keystate.ASM and Assemble.BAT
have been created, type

ASSEMBLE Keystate

at the DOS prompt.  You only need to specify the .ASM filename, not
the extension.

Keystate.BIN does not support toggling of the Insert key.  This is
because dBASE IV internally controls the keyboard status of the Insert
key.


; Program ...: Keystate.ASM
; Author ....: Jeffrey B. McCrimon
; Date ......: January 27, 1990
; Notes .....: .BIN to be LOADed and CALLed from within dBASE IV
;              to set, toggle, return the state of the Insert,
;              NumLock, or CapsLock keyboard status.
; 
; Syntax ....: st_keyst = "<stat>"
;              * ---If <stat> is a "1", turn ON Keystate
;              * ---If <stat> is a "0", turn OFF Keystate
;
;              * ---If <stat> is a "?", Keystate returns
;              * ---          "1" if <key> is currently "ON"
;              * ---          "0" if <key> is currently "OFF"
;
;              * ---If <stat> is a space, Keystate
;              *              toggles the current keyboard status and
;              * ---          returns "1" if <key> was previously "ON"
;              * ---          returns "0" if <key> was previously
"OFF"
;
;              LOAD Keystate
;              CALL Keystate WITH "<key>",st_keyst
;              * ---Where <key> is "I" for Insert
;              * ---               "C" for CapsLock
;              * ---               "N" for NumLock
;
;*************************************************
SPACE   EQU     " "
QMARK   EQU     "?"
ZERO    EQU     "0"
ONE     EQU     "1"
PLUS    EQU     "+"
NUMSTAT EQU     1101111100100000B
INSSTAT EQU     0111111110000000B
CAPSTAT EQU     1011111101000000B
;-------------------------------------------------
ROM     SEGMENT AT 0040H
        ORG 0017H
        KEYSTATE LABEL BYTE
ROM     ENDS
;*************************************************
CODE    SEGMENT BYTE PUBLIC 'CODE'
        ASSUME  CS:CODE

KSTAT   PROC    FAR
        ORG     0

START:
        JMP     SHORT ENTRY             ; Skip over the data area.
;=================================================
        DB      13, "Keystate.BIN by Jeffrey B, McCrimon", 13, 10
        DB      "(c) Copyright Ashton-Tate 1990", 13, 10
        DB      26
K_STATE DB      " "
;=================================================
ENTRY:  
        PUSH    ES
        PUSH    DS                      ; Save dBASE's Data Segment.
        CMP     CX,2                            ; Do we have two
parameters?
        JNE     THE_END                 ; No, let's quit.
        LDS     SI,DWORD PTR ES:[DI+4]
        MOV     AX,ROM
        MOV     ES,AX
        XOR     CX,CX                           ; Zero CX register
pair.
        MOV     CL,BYTE PTR ES:KEYSTATE ; Save Keyboard Status Word.
        MOV     CH,BYTE PTR [SI]        ; Save key_stat memvar
parameter.
GETKEY:
        MOV     AX,NUMSTAT              ; Is <key> NumLock?
        CMP     BYTE PTR [BX],"N"
        JZ      SHORT DO_IT
        MOV     AX,CAPSTAT              ; Is <key> CapsLock?
        CMP     BYTE PTR [BX],"C"
        JZ      SHORT DO_IT
        MOV     AX,INSSTAT              ; Is <key> Insert?
        CMP     BYTE PTR [BX],"I"
        JZ      SHORT DO_IT
        MOV     K_STATE,PLUS            ; No, we have error.
        JMP     SHORT K_EXIT
DO_IT:
        CMP     CH,ONE                          ; Is parameter "1"?
        JZ      SHORT SET_ON            ; Yes, turn ON Keystat.
        CMP     CH,ZERO                 ; Is parameter "0"?
        JZ      SHORT SET_OFF           ; Yes, turn OFF Keystat.
        TEST    CL,AL                   ; Is Keystat OFF?
        JZ      SHORT SET_ON            ; Yes, turn ON Keystat.
        MOV     K_STATE,ONE             ; Keystat is ON.
        CMP     CH,QMARK                        ; Is parameter "?"?
        JZ      SHORT K_EXIT            ; Yes, let's get out.
SET_OFF:
        AND     CL,AH                           ; Turn OFF Keystat.
        CMP     CH,ZERO                 ; Was parameter "0"?
        JZ      SHORT K_MAIN            ; Yes, enter main routine.
        MOV     CH,ONE                          ; Keystat was ON.
        JMP     SHORT K_MAIN
SET_ON:
        MOV     K_STATE,ZERO            ; Keystat is OFF.
        CMP     CH,QMARK        ; Is parameter "?"?
        JZ      SHORT K_EXIT            ; Yes, let's get out.
        OR      CL,AL                           ; No, Turn ON Keystat.
        CMP     CH,ONE                          ; Was parameter "1"?
        JZ      SHORT K_MAIN            ; Yes, enter main routine.
        MOV     CH,ZERO                 ; Keystat was OFF.
K_MAIN:
        MOV     BYTE PTR ES:KEYSTATE,CL ; Store new keyboard status.
        MOV     AL,CH                           ; Save keyboard status
in memvar.
        MOV     BYTE PTR [SI],AL                
        JMP     SHORT THE_END
K_EXIT:
        MOV     AL,K_STATE
        MOV     BYTE PTR [SI],AL
THE_END:
        POP     DS                              ; Restore dBASE's Data
Segment.
        POP     ES
        RET
KSTAT   ENDP
;-------------------------------------------------
CODE    ENDS
        END     START

ASSEMBLE.BAT

echo off
if "%1"=="" goto error3
masm %1.ASM;
if errorlevel 1 goto error1
link %1.OBJ;
if errorlevel 1 goto error2
exe2bin %1
del %1.EXE
del %1.OBJ
goto end
:error1
echo Assembler error!

goto end
:error2
echo Linker error!
goto end
:error3
echo Please try again, Assemble requires a filename.
echo Example: ASSEMBLE Keystate
:end


