[comp.sys.atari.st] st keyboard buffering

newman@dasys1.UUCP (Ken Newman) (05/02/88)

Sorry if this has whizzed by in this group before, but I would like to
know if someone has a solution to the problem of buffering raw keyboard
input on the ST, such as obtained by using the C routines Crawcin() or
Bconin(). There doesn't seem to be any buffering at all with these
routines, although I believe there is using the standard getchar (which
is no good for my purposes). I would like perhaps a full 80-char line of
typeahead and haven't been able to make sense of the keyboard queue
routines in the Megamax docs. Thnx in advance.
-- 
Ken Newman
Big Electric Cat Public UNIX
..!cmcl2!phri!dasys1!newman

leo@philmds.UUCP (Leo de Wit) (05/04/88)

In article <4215@dasys1.UUCP> newman@dasys1.UUCP (Ken Newman) writes:
>
>Sorry if this has whizzed by in this group before, but I would like to
>know if someone has a solution to the problem of buffering raw keyboard
>input on the ST, such as obtained by using the C routines Crawcin() or
>Bconin(). There doesn't seem to be any buffering at all with these
>routines, although I believe there is using the standard getchar (which
>is no good for my purposes). I would like perhaps a full 80-char line of
>typeahead and haven't been able to make sense of the keyboard queue
>routines in the Megamax docs. Thnx in advance.
>-- 
>Ken Newman
>Big Electric Cat Public UNIX
>..!cmcl2!phri!dasys1!newman

Here's how keyboard buffering is done (at least I thought so ...) :
The keyboard ACIA chip reads the keyboard and makes the characters available
at an I/O address (0xFF????). The processor, as part of the VBL routine,
reads the byte at that address (if the status, another I/O address is O.K.)
and places it in a buffer, let's call it the terminal I/O buffer.
This buffer is used by the BIOS for reading a single character and testing
availability of a character; also GEMDOS uses it to read a single character
with/without echo and to read a complete line with editing capabilities (I
think these are your Crawcin() and Bconin()). I don't quite see why you
should need direct access to the terminal I/O buffer; you can use bios
calls to check availability of a character (or use something like kb_hit())
and if there's a character, store it in your buffer. It could however be
possible that you just want to look at characters in the buffer, and not
mark them read, so here is some code (I used it to trap control C's: I can
now use stdio without control C terminating the program - a shell):

/* sh.prg */
.
.
.
#define TRUE  1
#define FALSE 0

#define CONTROLC 0x2e0003
#define CONTROLQ 0x100011
#define CONTROLS 0x1f0013

typedef struct iorec {
    char *ibuf;
    short ibufsize, ibufhead, ibuftail, ibuflow, ibufhigh;
} iorec;

typedef unsigned char bool;
.
.
.
iorec *iop;
.
.
.
    iop = (iorec *)xbios(14,1);    /* get ptr. to terminal I/O buffer structure */
.
.
.
bool test_ctrlc()  /* test for control C; discard buffer if you see one */
{
    int i;

    for (i = iop->ibufhead; i != iop->ibuftail; ) { 
        i += 4;
        if (i == iop->ibufsize) {
            i = 0;
        }
        if (*(int *)(iop->ibuf + i) == CONTROLC) { /* had one */
            iop->ibufhead = iop->ibuftail; /* effectively clears the buffer */
            return (bool)TRUE;
        }
    }
    return (bool)FALSE;
}

Some remarks:
    a) Lattice has int == 4 bytes, short == 2 (Megamax I thought int == 2).
       So you should replace the *(int *) by *(long *), and the short by int.
    b) The buffer struct contains:
        ibuf: start of the buffer
        ibuftail: index of write position
        ibufhead: index of read position
        ibuflow : index of low water mark
        ibufhigh: index of high water mark
       Each character is stored as a (4-byte) int, the scancode in the high
       word, the ASCII code in the low word. This is the same int as you get
       from gemdos(7) (character in without echo). Note that old BIOS versions
       used 2 bytes int to store a character (I know, I had TOS in RAM!), so
       there may be portability problems (i.e. between Ataris).
    c) An empty buffer is indicated by ibufhead == ibuftail.
    d) The xbios call returns a pointer to the structure.
    e) It would have been possible to define ibuf as (int *); the trouble is
       that then the indexes have to be converted (being byte-offsets).
    f) Of course, you can also use gets() or fgets(ptr,size,stdin) to read 
       a line or use gemdos(10) (READLINE), but that is not very raw; several
       characters are interpreted and there must be a termination character
       (mostly Newline).

I hope this helps and is raw enough? Have a nice byte ( %-) !
    (please mail me if not).

           Leo. (What you C is what you get).

braner@batcomputer.tn.cornell.edu (braner) (05/05/88)

[]

In article <478@philmds.UUCP> leo@philmds.UUCP (L.J.M. de Wit) writes:

>Here's how keyboard buffering is done (at least I thought so ...) :
>The keyboard ACIA chip reads the keyboard and makes the characters available
>... The processor, as part of the VBL routine, reads the byte...

- the keyboard (and mouse and joystick) events have their own interrupt.

>#define CONTROLC 0x2e0003
> ...
>    for (i = iop->ibufhead; i != iop->ibuftail; ) { 

- note that what "they" call head and tail may be the reverse of what you
 (or I, anyway) would call them...

>        if (*(int *)(iop->ibuf + i) == CONTROLC) {

- this may fail if "conterm" is set so that the kbdshift info is in the top
 byte of the top word.  Better use:
	if (((*(int *)(iop->ibuf + i)) & 0xFF00FF) == CONTROLC)
	                               ^^^^^^^^^^
>    a) Lattice has int == 4 bytes, short == 2 (Megamax I thought int == 2).
>       So you should replace the *(int *) by *(long *), and the short by int.

- yup, in Megamax (and Laser) int==16 bits, long==32 bits.  Avoid shorts.
  (I _think_ they're 8 bits in old Megamax, 16 in Laser.)
  Better to write code like this:

	#define WORD int	/* this is for megamax */
	...
	... WORD ...		/* all through the code where it matters */

  (Makes it a lot easier to port to another compiler.)

>I hope this helps and is raw enough? Have a nice byte ( %-) !

- thanks, Leo!

>           Leo. (What you C is what you get).

- Moshe Braner.  (YAFIYGI) (You asked for it, you got it.) (TeX)

(Still looking for a used 520ST system)

STOP CONTRA AID:  BOYCOTT COCAINE

leo@philmds.UUCP (Leo de Wit) (05/06/88)

In article <4694@batcomputer.tn.cornell.edu> braner@tcgould.tn.cornell.edu (braner) writes:
>In article <478@philmds.UUCP> leo@philmds.UUCP (L.J.M. de Wit) writes:
>>Here's how keyboard buffering is done (at least I thought so ...) :
>> ...[stuff deleted]...
>>    for (i = iop->ibufhead; i != iop->ibuftail; ) { 
>- note that what "they" call head and tail may be the reverse of what you
> (or I, anyway) would call them...

Agree, but I got (a hint for) the names from Data Becker's Atari ST Intern.
I think with a circular buffer, a reader and a writer it's anyway a question
whose side you're on.

>>    for (i = iop->ibufhead; i != iop->ibuftail; ) { 
>>        if (*(int *)(iop->ibuf + i) == CONTROLC) {
>- this may fail if "conterm" is set so that the kbdshift info is in the top
> byte of the top word.  Better use:
>	if (((*(int *)(iop->ibuf + i)) & 0xFF00FF) == CONTROLC)
>	                               ^^^^^^^^^^

Still better maybe:
    for (i = iop->ibufhead; i != iop->ibuftail; ) { 
        if (((*(int *)(iop->ibuf + i)) & 0xFF0000) == CONTROLC)
the ascii bindings may be changed by an xbios call, it's safer to use just
the scancode. But thanks for the correction, anyway!

>  Better to write code like this:
>	#define WORD int	/* this is for megamax */
>	... WORD ...		/* all through the code where it matters */
>  (Makes it a lot easier to port to another compiler.)

or to use:
#include "portab.h"
which just includes such definitions (at least for Lattice there's such a file).

>- Moshe Braner.  (YAFIYGI) (You asked for it, you got it.) (TeX)
>(Still looking for a used 520ST system)

I use one. A plus that is (520 ST++ :=)

        Leo (C-ing is believing).

apratt@atari.UUCP (Allan Pratt) (05/07/88)

Leo@philmds's description of keyboard input on the ST is imaginitive,
but totally off the wall.  The ST has INTERRUPT-DRIVEN keyboard input:
when a character is pending at the keyboard port, the processor is
INTERRUPTED (no Vblank).  The character is read from the port, and
placed in the keyboard input buffer (the one whose address you can get
from the Iorec Xbios call).  You don't need to use this buffer, however,
if you understand the interactions between BIOS and GEMDOS I/O.

The potentially confusing part is that if you do GEMDOS cooked input or
output, the BIOS buffer is COMPLETELY EMPTIED into a GEMDOS buffer.  So
if you follow GEMDOS output with BIOS input you will get "no characters
waiting." GEMDOS "cooked" I/O is Cconin, Cnecin, Cconout, Cconws, and
Cconrs, and Fread and Fwrite on handles which refer to the console, such
as handles 0 and 1 (stdin, stdout), handles DUP'ed from those, or -1,
which always refers to the console, even if handles 0 and 1 are
redirected.

If you don't want ^C to terminate your program, you can either
	(A) use only BIOS calls, or
	(B) use only GEMDOS raw calls (Crawcin, Crawio, Cconis), or
	(C) use a terminate-vector handler which longjmp's to your
	    recovery code.

The best reason to use the buffer structure returned by the Iorec call
is as a quick check for input keys pending.  Checking to see if in ==
out (meaning the buffer is empty) is lots quicker than the equivalent
BIOS call.  This only works if you use NO GEMDOS console I/O calls.  (I
haven't forgotten about critical sections: I investigated, and they
don't matter here.)

============================================
Opinions expressed above do not necessarily	-- Allan Pratt, Atari Corp.
reflect those of Atari Corp. or anyone else.	  ...ames!atari!apratt

apratt@atari.UUCP (Allan Pratt) (05/10/88)

From article <480@philmds.UUCP>, by leo@philmds.UUCP (Leo de Wit):
>>>        if (*(int *)(iop->ibuf + i) == CONTROLC) {
>         if (((*(int *)(iop->ibuf + i)) & 0xFF0000) == CONTROLC)
> the ascii bindings may be changed by an xbios call, it's safer to use just
> the scancode. But thanks for the correction, anyway!

It is NOT safer to use the scan code, becuase the scan code for the
ninth letter from the right of the next-to-bottom row of the keyboard is
constant, while the position of the letter 'C' is not.  INTERNATIONALIZE
your programs.  Don't make people hit ^B if that happens to be the key
there.  (I don't know if the letter C does, in fact, float...)

Also note that European keyboards have a key (called the ISO key)
between 'Z' and left-shift...  The left-shift key is extra wide to cover
this up on USA keyboards.  That's why I didn't call the USA 'C' "the
fourth key from the left."

Use ASCII codes: check for ((value & 0x000000FFL) == 3).  If your
document says "^C," check this way.  If your document says "^(ninth from
right)" you should use the scan code. 

============================================
Opinions expressed above do not necessarily	-- Allan Pratt, Atari Corp.
reflect those of Atari Corp. or anyone else.	  ...ames!atari!apratt