Download text file
  Back in days of old, when Commodore's were bold and "no one needed
more than 640k" I was shown a simple idea that has remained with me
through several languages and countless version changes. As the
concept is so important I would like to share my experience with those
of you who wish to continue reading. As these ideas were "handed down"
to me from fellow programmers and/or from this news group I feel it
only right that I pass it back to others just BASICally getting
started.

  On the Commodores and in GW-BASIC it looked something like this:

100 G$ = INKEY$ : IF G$<>"" THEN RETURN ELSE GOTO 100

One might ask; "if it's a one-liner then why not just type it in the
code each time you need it?" and that would be a valid question. On
the Commodore we had all of 32k for the program so we counted bytes
and bits. Things were a bit better on the super PCs of the time, we
had 64k but even 64k fills up real quick. GOSUB 100 was cheaper than
all that code. It is also easier to type, takes less text space, etc.

  A couple years later my trusty old XT and I got QuickBASIC and
TurboBASIC (TB won out). One of the first functions I wrote was:

FUNCTION GetKey$()
  LOCAL G$

  DO : G$ = INKEY$ : LOOP UNTIL G$<> ""
  GetKey$ = G$

END FUNCTION

Not much had changed, just the "shape", did the same thing, wasn't
much smarter, but I slowly realized that NOW with all the other
goodies that the modern BASICs offered that GetKey$ was far superior
to the old GW version.

GetKey$ stayed virtually unchanged for many years until a month ago
when version 1,342.02g was built, accepted, and integrated throughout
my libraries.

FUNCTION fGetKey% ()
  LOCAL G$

  DO
    G$ = INKEY$
  LOOP UNTIL LEN(G$) > 0
  G$ = G$ + CHR$(0)
  fGetKey% = CVI( G$ )

END FUNCTION

As you can see, still not much has changed but the 2 little changes do
make a world of difference. First of all LEN(G$) is faster and cheaper
than G$ = "" and passing/using INTEGERs instead of STRINGs is just
good business!

Before you can fully understand fGetKey% there are few things you must
know:

CVI (n$) converts the FIRST 2 BYTES of a string into an INTEGER
STR$(n%) converts an INTEGER into a STRING
CHR$(n%) converts the FIRST BYTE of an INTEGER into a STRING
INKEY$   returns 0, 1 or 2 byte strings only

An INTEGER is stored LOWBYTE:HIBYTE in memory.
  eg: INTEGER = HIBYTE*256 + LOWBYTE
   = CHR$(32)         : CVI( CHR$(32)+CHR$(00) ) =    32
     = CHR$(0)+CHR$(68) : CVI( CHR$(00)+CHR$(68) ) = 17408

An EQUATE (name may change from BASIC to BASIC) is a number, in the
range of an INTEGER that can be assigned only ONCE in a program and is
like a command to the compiler to replace the "text" with the value
assigned to it. These little rascals are EXTREMELY useful and their
full use should be explored at your earliest convenience.

  So, now that we've got everyone up to scratch, let's get back into
fGetKey%. Using fGetKey% makes many, many, seemingly difficult tasks
so easy it almost feels like cheating to use them (..NOT..). fGetKey%
creates one place in the program were you KNOW the user's key-presses
are going to be available. EVERY KEY-PRESS, think about that!!!

 1) Trap ANY key you want without generating thousands of bytes of
    extra code with ON KEY.
 2) On-screen clocks can be run from here without creating a flock of
    extra code.
 3) Instant Pop-up goodies no matter where the user is.
 4) Automatic screen blanking after x seconds with no user input
 5) Make keys "illegal" and disallow there use program wide.
 6) Run the whole main menu with hot-keys trapped by fGetKey%
 7) Run demos from stored key-presses & elapsed times.
 8) Translate incoming key-presses into UCASE$, LCASE$, or even
    other languages.
 9) and on, and on, and on, and on.......

 So, how do you make this work for you? Well, let's get into that now.
First of all there are a group of key-presses that are pretty constant
in every program. Thinks like , , , , ,
, etc. so you'll want to create an include file of EQUATES and
keep it handy. I have translated all mine into HEX so the numbers are
all the same length and display a cleaner list but that's a personal
thing. Here are some of the more common ones:

%HOME_key   = &h4700 : %UP_key     = &h4800 : %PGUP_key   = &h4900
%LEFT_key   = &h4B00 :                      : %RIGHT_key  = &h4D00
%END_key    = &h4F00 : %DOWN_key   = &h5000 : %PGDN_key   = &h5100
%ENTER_key  = &h000D : %CTRL_Enter = &h000A
%ESC_key    = &h001B : %F01_key    = &h3B00 : %F10_key    = &h4400

These represent the values that fGetKey% will be returning when these
keys are pressed. They also make the task of writing a menu much
easier as you only have to remember the key name and, of course the
code is self explanatory. It all looks like this:

DO
  G% = fGekKey%
  SELECT CASE G%
    CASE %HOME_key
    CASE %UP_key
    CASE %PGUP_key
    CASE %LEFT_key
    CASE %RIGHT_key
    CASE %END_key
    CASE %DOWN_key
    CASE %PGND_key
    CASE %ENTER_key
  END SELECT
LOOP UNTIL G% = %ESC_key

_THAT_, my friends, is legible code. It also happens to be faster than
its cousin which uses STRINGs and CHR$(0)+CHR$(70). I'll grant that it
takes a bit of work to create your list of key-presses but it's worth
the one time effort, believe me!

  Conversely, there are values that the keyboard can't produce, like
-1 CHR$(255)+CHR$(255), and these can be just as important as the
legal ones when sending signals to routines. Like %BailOut = -1. Think
about it!

If you're going to do some key and/or time trapping your function is
going to have to get a bit bigger. So, let's play with this idea that
does just about everything:

ON TIMER (60) GOSUB CLOCK

$EVENT ON    ' Turn on event trapping (it was off)

FUNCTION fGetKey% ()
  LOCAL G%, G$, T!

  TIMER ON
  DO
   T! = TIMER + 180  ' 3 minutes from now
    DO
      G$ = INKEY$
      IF TIMER > T! THEN
        CALL BlankScreen
        G% = 0
        EXIT LOOP
      END IF
    LOOP UNTIL LEN( G$ ) > 0
    G$ = G$ + CHR$(0)
    G% = CVI( G$ )
    SELECT CASE G%
      CASE %F01_key  : CALL PopHelp   ' trap F1 for help
      CASE %ALT_x    : GOTO ByeBye    ' trap X to end program
      CASE 124       : G% = 0         ' trap "|" so it can't be used
      CASE 97 TO 122 : G% = G% - 32   ' English UCASE
    END SELECT
  LOOP UNTIL G% <> 0

  TIMER STOP
  fGetKey% = G%

END FUNCTION

$EVENT OFF     ' no more event trapping needed!

As I said, MOST everything is here. The on screen clock is running, a
3 minute screen blanker is in force, the pipe character has been
removed from the user, instant pop-up help is activated for the whole
program, an instant bail-out key is standing by, the English alphabet
will ALWAYS return the UCASEd version of the character, thousands of
bytes of code have been saved because we haven't used ON KEY, and ON
TIMER only affects a few lines of code. The best part is that if
anything needs changing or maybe something needs to be added like
%ALT_F1 for more help then it all happens in one place for the whole
program. You won't have to search every routine in every file, debug,
or worry that you missed one.

  If you were observant then you noticed one other important aspect of
programming; consistency. The variable "G" has followed me for over a
decade now and has ALWAYS meant the same thing. "The incoming (gotten)
key." It has changed from G$ of old to the G% of today but it is still
a "G" and anyone who reads ANY of my code will soon become familiar
with its value. The "G", itself, isn't important ("K" works pretty
well too and I've even seen OTHER variables!) but the consistency IS
important.

  Another trick showed up a few years back that made things easier
too. Notice that the name changed from GetKey to fGetKey? Well, back
in the old days the only functions we had were DEF fnGetKey type
things and "fn" is still a reserved word in most BASICs. When you used
a function that started with "fn" you could see at a glance what was
happening but today, with the more open format allowed one can not
tell a function from a variable: eg: G% = GetKey%. GetKey%, here,
could be a function or a variable so how do you tell?  Well, what a
couple of friends of mine and I came up with were a few simple rules
to alleviate the confusion. Here's my list of "rules".

   MySub        - subs start with a capital letter
  fMyFunction   - functions start with a small "f"
  sSharedVar    - shared variables start with a small "s"
  pPublicVar    - public variables start with a small "p"
  cCommonVar    - common variables start with a small "c"
  tTYPEvar      - DIMed type variables start with a small "t"
   AllOtherVars - all other variables start with a capital letter
                  !!ALL variables are signed!!

  Well, thanks for hanging in there! I hope this information helps
some of you. To those of you who feel like I've wasted your time
and/or band width I apologize but I've noticed that quite a few people
keep asking questions about ON KEY, INKEY$, etc. and I would think
this little idea will help those people quite a bit. Like I said at
the top, I've been helped by others known and unknown and, in the
spirit of things, feel like I can now repay those debts.

  A parting shot here. I don't mind this tutorial being reproduced
EXCEPT where it is done for a profit. That is meant to include "how
to" books but exclude web pages and collections like ABC packets, etc.

Any questions?