Discussion:
A few questions about custom keyboard interrupt routine
(too old to reply)
Johann 'Myrkraverk' Oskarsson
2019-10-21 10:31:44 UTC
Permalink
Dear c.o.m.programmer,

Instead of overwriting int 15 and hook the keyboard translator, I
decided to experiment with my own keyboard interrupt handler. So far,
it seems to work OK apart from a long beep in DOSBox-X on first use. I
believe that's something to do with my code rather than DOSBox-X itself.

As an experimental code, I cannot recommend anyone trying it on real
hardware. I would like to know why the continuous beep happens.

The interrupt routine is somewhat black art to me. Why am I reading and
then writing different bits to the keyboard controller?

What is the significance of using sti() at the start of the function,
and outp( 0x20, 0x20 ) at the end?


/* file: kb.c -*- c-default-style: "k&r" -*- */

/* A simple program to demonstrate keyboard driver in protected mode.
* Hopefully.
*
* Compile with:
* wcl386 -l=causeway -za99 kb.c
* for the CauseWay version, or adjust for DOS4GW.
*
* Alternatively compile as 16bit program with:
* wcl -za99 kb.c
*
* Known side effects: beeps steadily on first run in DOSBox-X but
* otherwise appears to work.
*/

#include <dos.h>

int quit = 0;

void sti( void );
#pragma aux sti = "sti" modify exact [] nomemory;

void ( _interrupt _far *dos_kbd)( void );

/*
* This routine is translated from assembly into C, from the book
* Black Art of 3D Game Programming, by Andre Lamothe, page 224.
*/
void _interrupt _far kbd( void ) {
sti(); // Why do I need STI here?

/* What exactly is the purpose of the inp() and outp() routines
* below? Getting the scan code is obvious, but what exactly am
* I doing with the control bits?
*/

int scancode = inp( 0x60 ); // raw keyboard buffer
int control = inp( 0x61 ); // raw keyboard control
control |= 0x82; // mask off the right bits?
outp( 0x61, control ); // output the control bits
outp( 0x61, 0x7f ); // finish the keyboard song and dance

if ( scancode == 1 ) quit = 1; // scan code 1 = escape

/*
* For now we just ignore every input, and set the quit flag to
* true if the user pressed ESC.
*/

outp( 0x20, 0x20 ); // PIC enable interrupts
}

int
main( int argc, char *argv[] )
{
dos_kbd = _dos_getvect( 0x09 ); // keyboard interrupt

_dos_setvect( 0x09, kbd );

while ( !quit )
;

_dos_setvect( 0x09, dos_kbd );
return 0;
}

/* end of file: kb.c */
--
Johann | email: invalid -> com | www.myrkraverk.com/blog/
I'm not from the Internet, I just work there. | twitter: @myrkraverk
T. Ment
2019-10-21 16:11:13 UTC
Permalink
Post by Johann 'Myrkraverk' Oskarsson
Instead of overwriting int 15 and hook the keyboard translator, I
decided to experiment with my own keyboard interrupt handler.
You can. If you know what you're doing.
Post by Johann 'Myrkraverk' Oskarsson
it seems to work OK apart from a long beep in DOSBox-X on first use. I
believe that's something to do with my code rather than DOSBox-X itself.
I would like to know why the continuous beep happens.
Don't know about DOSBox. Don't want to know. Sorry.
Post by Johann 'Myrkraverk' Oskarsson
Why am I reading and then writing different bits to the keyboard
controller?
Because you took control of INT 9 without chaining to the BIOS INT 9
handler. The port I/O is how you get the scan code. It's a bad example
no one should follow.

An OS like Windows can get away with that, because they eliminate the
BIOS altogether, and take full responsibility for the keyboard port I/O.
They can make that work, because a company like Microsoft has access to
hardware data sheets of all the keyboard controllers on the market. But
the average programmer does not. There's some sketchy documentation out
there, and some authors do it, but I would not.

At least not with DOS. Instead, let the BIOS INT 9 code do the keyboard
port I/O for you. Trust the BIOS writers to get it right, and leave the
black magic to them.

You can still hook INT 9, but don't read the keyboard port yourself.
First, call the BIOS INT 9 handler and let him do that for you. Don't
_chain to him, a _chain never returns. Call him, so that he returns to
you. I don't have code handy, but I think it's as simple as pushf/call
to set up the stack right, so he thinks he was called by INT instead of
you.

He will put something in the keyboard buffer, or set some shift state
flags. Once he returns to you, You can look to see what he did. You can
override his work and mess with the keyboard buffer if you want to.

This method may be adequate for your needs. If not, you may need to hook
15/4F too. I would rather use 15/4F alone. It's easier.
Post by Johann 'Myrkraverk' Oskarsson
What is the significance of using sti() at the start of the function
It allows higher priority interrupts to interrupt you. It won't be
necessary if you follow the method above. When you call the BIOS INT 9
handler, he will do that himself (if he thinks it's a good idea). No
need for you to do it too.
Post by Johann 'Myrkraverk' Oskarsson
and outp( 0x20, 0x20 ) at the end?
That's an EOI. Every hardware interrupt handler must do it. But again,
the BIOS INT 9 handler takes care of it.

Let the BIOS do what it does best, the hard work.
T. Ment
2019-10-21 16:57:12 UTC
Permalink
Post by T. Ment
You can still hook INT 9, but don't read the keyboard port yourself.
First, call the BIOS INT 9 handler and let him do that for you. Don't
_chain to him, a _chain never returns. Call him, so that he returns to
you. I don't have code handy, but I think it's as simple as pushf/call
to set up the stack right
Borland C++ 3.1 does pushf for you, provided you tell him the function
you're calling is an interrupt. Don't know about other compilers.

Now I remember an example, the interrupt latency tester I posted some
time ago. In ticktock_hook(), it uses a _chain_intr which never returns,
and a call which does. Different methods for different purposes.



# include <ctype.h>
# include <dos.h>
# include <limits.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>

typedef unsigned UINT;
typedef unsigned long ULONG;
typedef void interrupt (INTR) (void);

# define TERMS 8

# define TICKTOCK_INT 8

# define PIT_PORT_0 0x40
# define PIT_PORT_2 0x42
# define PIT_PORT_3 0x43
# define PIT_CHAN_0 (0 << 6)
# define PIT_CHAN_2 (2 << 6)
# define PIT_MODE_0 (3 << 4 | 0 << 1)
# define PIT_MODE_2 (3 << 4 | 2 << 1)

struct LATENCY {
UINT min; /* beware initializer */
UINT max;
ULONG neg;
ULONG zero;
ULONG recur;
ULONG high[4];
ULONG sum;
volatile ULONG count;
long now;
};

struct SERIES {
UINT hertz;
UINT divisor;
int lohi; /* 0=low 1=high */
int terms;
int reset[TERMS];
int count[TERMS];
};

struct LATENCY latency, latency0 = { UINT_MAX };

/* *INDENT-OFF* */

struct SERIES series_250 = { 250, 4772, 1, 4, {15, 10, 2, 2}, {1, 1, 1, 1} };
struct SERIES series_500 = { 500, 2386, 0, 4, {10, 5, 3, 2}, {1, 1, 1, 1} };
struct SERIES series_1000 = { 1000, 1193, 0, 3, {12, 10, 5}, {1, 1, 1} };
struct SERIES series_2000 = { 2000, 596, 1, 4, {12, 10, 5, 2}, {1, 1, 1, 1} };
struct SERIES series_4000 = { 4000, 298, 0, 7, { 4, 2, 2, 5, 2, 5, 3}, {1, 1, 1, 1, 1, 1, 1} };
struct SERIES series_8000 = { 8000, 149, 0, 7, { 4, 2, 2, 5, 2, 5, 6}, {1, 1, 1, 1, 1, 1, 1} };
struct SERIES series_15625 = {15625, 76, 0, 8, { 5, 2, 5, 10, 10, 5, 3, 2}, {1, 1, 1, 1, 1, 1, 1, 1} };
struct SERIES series_25000 = {25000, 47, 1, 6, {15, 10, 10, 10, 2, 2}, {1, 1, 1, 1, 1, 1} };

struct SERIES series;

/* *INDENT-ON* */

INTR *ticktock;

void interrupt
ticktock_hook (void)
{
UINT timer0, timer2;
int lohi, x;
static UINT divisor = UINT_MAX, sum = 1, extra;
static int recur, same = 2;

if (recur) {
latency.recur++;
_chain_intr (ticktock);
}

_AL = PIT_CHAN_0; /* read timer0 */
asm out PIT_PORT_3, al;
asm in al, PIT_PORT_0;
asm mov ah, al;
asm in al, PIT_PORT_0;
asm xchg ah, al;
timer0 = _AX;

_AL = PIT_CHAN_2; /* read timer2 */
asm out PIT_PORT_3, al;
asm in al, PIT_PORT_2;
asm mov ah, al;
asm in al, PIT_PORT_2;
asm xchg ah, al;
timer2 = _AX;
timer2 = 0 - timer2;

asm in al, 0x61; /* read timer2 output */
asm xor ah, ah;
x = _AX;

asm xor al, al; /* reload timer2 */
asm out PIT_PORT_2, al;
asm out PIT_PORT_2, al;

if (x & 0x20) { /* latency exceeds measuring limit */

latency.high[0]++;

} else if (timer2 > extra + divisor * 0.8) {

latency.high[1]++;
if (timer2 > latency.high[2])
latency.high[2] = timer2;
latency.high[3] += timer2;

} else {

latency.now = (long) divisor - timer0;

if (latency.now < 0) {
latency.neg++;

} else if (latency.now == 0) {
latency.zero++;

} else {
latency.sum += latency.now;

if (latency.now < latency.min)
latency.min = latency.now;

if (latency.now > latency.max)
latency.max = latency.now;
}

}

latency.count++;

extra = timer0;

if ((sum += divisor) < divisor) {
recur = 1;
(*ticktock) (); /* let BIOS do its work */
recur = 0;
} else
outportb (0x20, 0x20); /* EOI */

lohi = series.lohi;
for (x = series.terms; x;) {
if (--series.count[--x])
break; /* found lohi */
series.count[x] = series.reset[x];
lohi ^= 1; /* toggle lohi prior to */
} /* next term or series end */

if (lohi != same) {
same = lohi;
_AX = divisor = series.divisor + lohi;
asm out PIT_PORT_0, al;
asm mov al, ah;
asm out PIT_PORT_0, al;
}
}

void
wait_bios_tick (void)
{
asm mov ax, 0x40;
asm mov es, ax;
asm mov ax, word ptr es:[0x6c];

look:;

asm cmp word ptr es:[0x6c], ax;
asm je short look;
}

void
restore_ticktock (void)
{
if (!ticktock)
return;

wait_bios_tick ();

asm cli;

_AL = PIT_CHAN_0 | PIT_MODE_2; /* restore timer0 */
asm out PIT_PORT_3, al;
asm xor al, al;
asm out PIT_PORT_0, al;
asm out PIT_PORT_0, al;

_AL = PIT_CHAN_2 | PIT_MODE_0; /* stop timer2 */
asm out PIT_PORT_3, al;

asm in al, 0x61; /* close timer2 and speaker gates */
asm and al, 0xfc;
asm out 0x61, al;

setvect (TICKTOCK_INT, ticktock); /* unhook */

asm sti;

ticktock = 0; /* never repeat */
}

char *
psp_tail (void)
{
char far *tail = (char far *) MK_FP (_psp, 0x80);
int size = *tail;
char buff[126], *copy;

if (size > sizeof buff || size < 0) {
fprintf (stderr, "failure inspecting PSP tail size\n");
exit (1);
}

while (size && isspace (*++tail)) /* discard leading whitespace */
size--;

if (!size)
return 0;

*(tail + size) = 0; /* convert to C string */

_fstrcpy (buff, tail); /* copy to stack storage */

copy = strdup (buff); /* and then to heap storage */

if (!copy) {
fprintf (stderr, "failure allocating space: psp_tail copy\n");
exit (1);
}

return copy; /* C will free it, why bother */
}

void
main ()
{
char *command = psp_tail ();

if (!command) {
fprintf (stderr, "nothing to do\n");
exit (0);
}

series = series_1000;

asm cli;

ticktock = getvect (TICKTOCK_INT);

asm sti;

if (atexit (restore_ticktock)) {
fprintf (stderr, "failure installing restore_ticktock\n");
exit (1);
}

wait_bios_tick ();

asm cli;

_AL = PIT_CHAN_0 | PIT_MODE_2; /* setup timer0 */
asm out PIT_PORT_3, al;
asm xor al, al;
asm out PIT_PORT_0, al;
asm out PIT_PORT_0, al;

_AL = PIT_CHAN_2 | PIT_MODE_0; /* setup timer2 */
asm out PIT_PORT_3, al;

asm in al, 0x61; /* open timer2 gate, close speaker */
asm and al, 0xfc;
asm or al, 0x01;
asm out 0x61, al;

setvect (TICKTOCK_INT, ticktock_hook);

asm sti;

while (latency.count < 2) /* hook needs 2 iterations */
; /* to self initialize */

asm cli;

latency = latency0; /* throw out the garbage */

asm sti;

system (command);

restore_ticktock ();

if (latency.neg)
printf ("neg = %lu, ", latency.neg);

if (latency.zero)
printf ("zero = %lu, ", latency.zero);

if (latency.recur)
printf ("recur = %lu, ", latency.recur);

if (latency.high[0] || latency.high[1])
printf ("high = %lu/%lu/%lu/%lu, ", latency.high[0], latency.high[1], latency.high[2], latency.high[1] ? latency.high[3] / latency.high[1] : 0);

if (latency.count)
printf ("avg = %.3f, ", 1.0 * latency.sum / latency.count);

printf ("min = %u, max = %u, count = %lu\n", latency.min, latency.max, latency.count);

}
Johann 'Myrkraverk' Oskarsson
2019-10-22 00:18:37 UTC
Permalink
Post by T. Ment
This method may be adequate for your needs. If not, you may need to hook
15/4F too. I would rather use 15/4F alone. It's easier.
Thanks, though it feels easier to replace the keyboard driver entirely
to me. Different strokes for differnt folk and all that.
--
Johann | email: invalid -> com | www.myrkraverk.com/blog/
I'm not from the Internet, I just work there. | twitter: @myrkraverk
src153
2019-10-22 01:20:30 UTC
Permalink
it feels easier to replace the keyboard driver entirely to me
By "entirely" do you mean port I/O too? As noted, that involves possible
multi byte scan code sequences.
Different strokes for differnt folk and all that
If doing your own port I/O, have fun. Much too tedious for me.
Ross Ridge
2019-10-21 17:27:15 UTC
Permalink
Post by Johann 'Myrkraverk' Oskarsson
sti(); // Why do I need STI here?
It's not necessary and is a mistake. It reenables interrupts, but
that would be undisirable. MS-DOS applications have limited stack
space and allowing nested interrupts could easily overflow the stack.
It also introduces possible concurency issues with other interrupt
handlers. Interrupts will automatically be reinabled when the interrupt
handler returns. There's no benefit to reenabling them early, not in
a short and simple interrupt routine.
Post by Johann 'Myrkraverk' Oskarsson
/* What exactly is the purpose of the inp() and outp() routines
* below? Getting the scan code is obvious, but what exactly am
* I doing with the control bits?
*/
...
Post by Johann 'Myrkraverk' Oskarsson
int control = inp( 0x61 ); // raw keyboard control
control |= 0x82; // mask off the right bits?
outp( 0x61, control ); // output the control bits
outp( 0x61, 0x7f ); // finish the keyboard song and dance
It's meant to reset the shift register of an PC or XT keyboard controller
so the next scancode can be read. It's completely unecessary with a AT
or PS/2 controller.

On an original IBM PC or XT ports 0x60-0x63 are a 8255 PPI (Programable
Peripheral Interface) chip, a simple parallel interface. (With an
AT or PS/2 keyboard controller, ports 0x60-0x64 access a i8041/i8042
microcontroller.) I/O port 0x61 accessed the second 8-bit parallel port
of the PPI, and the different bits of the port were used various different
functions. Only bit 7 (0x80) had any effect on the XT keyboard controller
and only when writing to the port. The bits of this port served two
different purposes depending on whether they were being read or writen.

You can see all these different purposes and functionality in Ralf
Brown's Interrupt List's description of them:

Bitfields for KB controller port B (system control port) [output]:
Bit(s) Description (Table P0392)
7 pulse to 1 for IRQ1 reset (PC,XT)
6-4 reserved
3 I/O channel parity check disable
2 RAM parity check disable
1 speaker data enable
0 timer 2 gate to speaker enable
SeeAlso: PORT 0061h-W,#P0393

Bitfields for KB ctrller port B control register (system control port) [input]:
Bit(s) Description (Table P0393)
7 RAM parity error occurred
6 I/O channel parity error occurred
5 mirrors timer 2 output condition
4 toggles with each refresh request
3 NMI I/O channel check status
2 NMI parity check status
1 speaker data status
0 timer 2 clock gate to speaker status
Note: also supported by OPTi 82C392
SeeAlso: PORT 0061h-R,#P0392

It's actually not necessary to read the value of port 0x61 beforing
writing to it because reading port 0x61 doesn't retrieve the last value
written to port 0x61.

All you really need to on a computer with a PC or XT keyboard controller
is to write a value with bit 7 set to port 0x61 and then write a value
with bit 7 unset. This pulses the reset line of the keyboard
controller's shift register, which allows it to read further data from
the keyboard. The other bits should be set to whatever is correct for
that machine and/or your program.

If you don't have an actual IBM PC or IBM PC/XT (or a 100% compatible
clone) to test your code on, I wouldn't bother trying to support
PC/XT keyboard controllers. Without testing it you can't be sure it's
actually going to work. You can't depend on the code in the book you're
copying from actually working. Aside from the general unreliability
of code in books, it's unlikely that the author of the "Black Art 3D
Game Programming" ever tested his code on anything other than an AT or
later PC. XT class machines were long obsolete by the time the book
was written.

Here's an example keyboard interrupt handler suitable for use in a game
I wrote for an answer on Stack Overflow:

unsigned char normal_keys[0x60];
unsigned char extended_keys[0x60];

static void interrupt
keyb_int() {
static unsigned char buffer;
unsigned char rawcode;
unsigned char make_break;
int scancode;

rawcode = inp(0x60); /* read scancode from keyboard controller */
make_break = !(rawcode & 0x80); /* bit 7: 0 = make, 1 = break */
scancode = rawcode & 0x7F;

if (buffer == 0xE0) { /* second byte of an extended key */
if (scancode < 0x60) {
extended_keys[scancode] = make_break;
}
buffer = 0;
} else if (buffer >= 0xE1 && buffer <= 0xE2) {
buffer = 0; /* ingore these extended keys */
} else if (rawcode >= 0xE0 && rawcode <= 0xE2) {
buffer = rawcode; /* first byte of an extended key */
} else if (scancode < 0x60) {
normal_keys[scancode] = make_break;
}

outp(0x20, 0x20); /* must send EOI to finish interrupt */
}

Note that it makes no attempt to support XT keyboard controllers.
You can see the full source in my answer here:

https://stackoverflow.com/a/40963633/3826372
--
l/ // Ross Ridge -- The Great HTMU
[oo][oo] ***@csclub.uwaterloo.ca
-()-/()/ http://www.csclub.uwaterloo.ca/~rridge/
db //
T. Ment
2019-10-21 17:53:39 UTC
Permalink
Post by Johann 'Myrkraverk' Oskarsson
sti(); // Why do I need STI here?
It's not necessary and is a mistake. It reenables interrupts, but
that would be undisirable.
Only higher priority interrupts get past the PIC, prior to EOI. And only
the timer interrupt is higher than the keyboard.
MS-DOS applications have limited stack space and allowing nested
interrupts could easily overflow the stack.
It's not a big danger, as with application recursion. The PIC priority
prevents that.
It also introduces possible concurency issues with other interrupt
handlers.
In a keyboard handler, only the timer interrupt.
Interrupts will automatically be reinabled when the interrupt
handler returns. There's no benefit to reenabling them early, not in
a short and simple interrupt routine.
Provided it's quick enough. But many aren't. You can see that with my
latency tester.
Johann 'Myrkraverk' Oskarsson
2019-10-22 00:02:40 UTC
Permalink
Post by T. Ment
Post by Johann 'Myrkraverk' Oskarsson
sti(); // Why do I need STI here?
It's not necessary and is a mistake. It reenables interrupts, but
that would be undisirable.
Only higher priority interrupts get past the PIC, prior to EOI. And only
the timer interrupt is higher than the keyboard.
I am looking at the interrupt routine in the XT bios, and it calls STI
at start and CLI just before sending end-of-interrupt.

It also has the keyboard control logic I found in the book written
slightly differently. I've seen people use inp() for timing related
functions. Without knowing any better, I can guess reading the control
port port first is a delaying tactic, but I can't know for certain.
Post by T. Ment
MS-DOS applications have limited stack space and allowing nested
interrupts could easily overflow the stack.
It's not a big danger, as with application recursion. The PIC priority
prevents that.
It also introduces possible concurency issues with other interrupt
handlers.
In a keyboard handler, only the timer interrupt.
Armed with that knowledge, I'm inclined to keep the STI/CLI bits from
the XT bios routine.
Post by T. Ment
Interrupts will automatically be reinabled when the interrupt
handler returns. There's no benefit to reenabling them early, not in
a short and simple interrupt routine.
Provided it's quick enough. But many aren't. You can see that with my
latency tester.
I haven't explored the latency tester yet, but I will.
--
Johann | email: invalid -> com | www.myrkraverk.com/blog/
I'm not from the Internet, I just work there. | twitter: @myrkraverk
Ross Ridge
2019-10-22 05:43:47 UTC
Permalink
Post by Johann 'Myrkraverk' Oskarsson
I am looking at the interrupt routine in the XT bios, and it calls STI
at start and CLI just before sending end-of-interrupt.
It's still not a good idea. It's justifiable with the XT BIOS because
it's significantly more complicated code that can take a significant
amount of time in certain circumstances like when it beeps because the
keyboard buffer is full. In the the worst case, when the print screen
button is pressed, it can take a quite a lot time.

In a simple keyboard handler for game, like in my example code or the
book's, there's no need to reenable interrupts early.
Post by Johann 'Myrkraverk' Oskarsson
It also has the keyboard control logic I found in the book written
slightly differently. I've seen people use inp() for timing related
functions. Without knowing any better, I can guess reading the control
port port first is a delaying tactic, but I can't know for certain.
It's reseting the keyboard controller's shift register like I described
in my previous post. There are no reads or writes of I/O ports for
delay purposes. It's reading the "old" value of port 0x61, setting
bit 7 in the value, writing the new modified value and then writing the
"old" value. From the BIOS listing we at least know this will work on
IBM PC XTs, but if Ralf Brown's Interrupt Listing is correct then the
"old" value isn't really the previous value written to port 0x61.

The only significant difference is that the book's code is also setting
bit 1 in the value, which is one of the two bits in port 0x61 that
control the PC speaker. I don't know why its doing this, but this is
probably where your beep in DOSBox is coming from. On a real PC this
should just cause the PC speaker to click once.
--
l/ // Ross Ridge -- The Great HTMU
[oo][oo] ***@csclub.uwaterloo.ca
-()-/()/ http://www.csclub.uwaterloo.ca/~rridge/
db //
T. Ment
2019-10-22 07:31:52 UTC
Permalink
Post by Ross Ridge
Post by Johann 'Myrkraverk' Oskarsson
I am looking at the interrupt routine in the XT bios, and it calls STI
at start and CLI just before sending end-of-interrupt.
It's still not a good idea.
Your reasons are not convincing.
Post by Ross Ridge
In a simple keyboard handler for game, like in my example code or the
book's, there's no need to reenable interrupts early.
If that simple, why bother with a custom keyboard handler at all. What
does it add that BIOS lacks.
Johann 'Myrkraverk' Oskarsson
2019-10-22 13:09:17 UTC
Permalink
Post by Ross Ridge
Post by Johann 'Myrkraverk' Oskarsson
I am looking at the interrupt routine in the XT bios, and it calls STI
at start and CLI just before sending end-of-interrupt.
It's still not a good idea. It's justifiable with the XT BIOS because
it's significantly more complicated code that can take a significant
amount of time in certain circumstances like when it beeps because the
keyboard buffer is full. In the the worst case, when the print screen
button is pressed, it can take a quite a lot time.
I see. I also checked (an old version of) the VirtualBox BIOS. It
doesn't use STI and CLI.
Post by Ross Ridge
In a simple keyboard handler for game, like in my example code or the
book's, there's no need to reenable interrupts early.
I think I'll keep to your example and the VirtualBox BIOS and not do
this either.
Post by Ross Ridge
Post by Johann 'Myrkraverk' Oskarsson
It also has the keyboard control logic I found in the book written
slightly differently. I've seen people use inp() for timing related
functions. Without knowing any better, I can guess reading the control
port port first is a delaying tactic, but I can't know for certain.
It's reseting the keyboard controller's shift register like I described
in my previous post. There are no reads or writes of I/O ports for
delay purposes.
Yet I've seen that done for SoundBlaster code. Are you absolutely sure
this is not a delaying tactic for the keyboard circuitry?
Post by Ross Ridge
It's reading the "old" value of port 0x61, setting
bit 7 in the value, writing the new modified value and then writing the
"old" value. From the BIOS listing we at least know this will work on
IBM PC XTs, but if Ralf Brown's Interrupt Listing is correct then the
"old" value isn't really the previous value written to port 0x61.
I'll take your word for it. I haven't seen this chapter in the RBIL yet.
Post by Ross Ridge
The only significant difference is that the book's code is also setting
bit 1 in the value, which is one of the two bits in port 0x61 that
control the PC speaker. I don't know why its doing this, but this is
probably where your beep in DOSBox is coming from. On a real PC this
should just cause the PC speaker to click once.
Ah, yes. That's probably the explanation. Could be a leftover from a
debug interface: click means my new interrupt handler is working. Could
be just a misunderstanding as well.
--
Johann | email: invalid -> com | www.myrkraverk.com/blog/
I'm not from the Internet, I just work there. | twitter: @myrkraverk
T. Ment
2019-10-22 17:10:52 UTC
Permalink
Post by Johann 'Myrkraverk' Oskarsson
I think I'll keep to your example
I wouldn't. He neglects details. https://en.wikipedia.org/wiki/Scancode
Post by Johann 'Myrkraverk' Oskarsson
Most character keys have a single byte scancode; keys that perform
special functions have 2-byte or 3-byte scancodes, usually beginning
with the byte (in hexadecimal) E0, E1, or E2. In addition, a few keys
send longer scancodes
https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
Post by Johann 'Myrkraverk' Oskarsson
The keys PrtSc/SysRq and Pause/Break are special. The former produces
scancode e0 2a e0 37 when no modifier key is pressed
Wyatt calls port reads "keycodes." On AT keyboards IBM started over and
renumbered the keycodes. But BIOS INT 9 translates them to match the old
XT values. You really want to handle all that? Rod gave the only reason
I would, writing your own OS. If that's what you want, go for it. But I
thought you wanted to write a DOS extender application.
Post by Johann 'Myrkraverk' Oskarsson
Are you absolutely sure this is not a delaying tactic for the
keyboard circuitry?
I didn't analyze that, don't know. But Ridges's code overlooks status
port 64h. Wyatt checks it, and delay loops until the data port is ready
(p. 45).
Johann 'Myrkraverk' Oskarsson
2019-10-22 20:06:45 UTC
Permalink
Post by T. Ment
Wyatt calls port reads "keycodes." On AT keyboards IBM started over and
renumbered the keycodes. But BIOS INT 9 translates them to match the old
XT values. You really want to handle all that? Rod gave the only reason
I would, writing your own OS. If that's what you want, go for it. But I
thought you wanted to write a DOS extender application.
Thank you. I have Wyatt in delivery, it should be here in a few days.
As for handling "all that" -- I don't think it's too onerous -- and I
don't really need backward compatible scan codes. The new ones should
do just fine.

For now, I do want to write a DOS extender application, and a keyboard
driver isn't too much of a yak-shaving project. I do enjoy learning new
things.
--
Johann | email: invalid -> com | www.myrkraverk.com/blog/
I'm not from the Internet, I just work there. | twitter: @myrkraverk
T. Ment
2019-10-22 20:50:19 UTC
Permalink
Post by Johann 'Myrkraverk' Oskarsson
I have Wyatt in delivery
It has details I've not seen elsewhere. But even he goes wrong at times.
The code where he busy loops to check status port 64h, he does that with
interrupts disabled. That's bad enough when things work right. But what
if the keyboard malfunctions, or a worn cable has a bad wire? The status
may never go ready and your PC locks up.

These are problems professional BIOS programmers spend a lot of time and
testing to solve. But if a hobby programmer can do it better, good luck.
Johann 'Myrkraverk' Oskarsson
2019-10-22 21:06:43 UTC
Permalink
Post by T. Ment
It has details I've not seen elsewhere. But even he goes wrong at times.
The code where he busy loops to check status port 64h, he does that with
interrupts disabled. That's bad enough when things work right. But what
if the keyboard malfunctions, or a worn cable has a bad wire? The status
may never go ready and your PC locks up.
Thanks for the warning.
Post by T. Ment
These are problems professional BIOS programmers spend a lot of time and
testing to solve. But if a hobby programmer can do it better, good luck.
I'm not so sure about that. BIOSes are notorious for being buggy. For
now I'm having fun with a keyboard driver. I'll stop when I'm not
having fun anymore.
--
Johann | email: invalid -> com | www.myrkraverk.com/blog/
I'm not from the Internet, I just work there. | twitter: @myrkraverk
T. Ment
2019-10-22 21:13:56 UTC
Permalink
Post by Johann 'Myrkraverk' Oskarsson
Post by T. Ment
These are problems professional BIOS programmers spend a lot of time and
testing to solve. But if a hobby programmer can do it better, good luck.
I'm not so sure about that. BIOSes are notorious for being buggy. For
now I'm having fun with a keyboard driver. I'll stop when I'm not
having fun anymore.
If you can do it better than BIOS, you don't need my help anymore. Have
a barrel of fun.
Ross Ridge
2019-10-22 23:38:56 UTC
Permalink
Post by Johann 'Myrkraverk' Oskarsson
Thank you. I have Wyatt in delivery, it should be here in a few days.
As for handling "all that" -- I don't think it's too onerous -- and I
don't really need backward compatible scan codes. The new ones should
do just fine.
By default the i8041/2 controller will translate AT keyboard scan codes
into XT scan codes. You can disable this translation, but either way
there's no need to convert scan codes in your own code, you can just
use them as is.
Post by Johann 'Myrkraverk' Oskarsson
For now, I do want to write a DOS extender application, and a keyboard
driver isn't too much of a yak-shaving project. I do enjoy learning new
things.
Note that my example code is meant for something like a game (presumably
so is the code in the book you're using). In a game you're generally
using the keyboard as a big controller, and so you're only interested
in the current state of the keyboard. So the code just updates a big
array with one element for every possible scancode (in the normal range
of PC keyboard scancodes), each element indicating whether the key is
currently being pressed or not.

Depending what sort of application you're writing then this may or not
be what yo want. For normal keyboard text input, you want to buffer
the keystrokes instead of updating an array, just like the BIOS does.
--
l/ // Ross Ridge -- The Great HTMU
[oo][oo] ***@csclub.uwaterloo.ca
-()-/()/ http://www.csclub.uwaterloo.ca/~rridge/
db //
T. Ment
2019-10-23 00:45:46 UTC
Permalink
Post by Ross Ridge
By default the i8041/2 controller will translate AT keyboard scan codes
into XT scan codes. You can disable this translation, but either way
there's no need to convert scan codes in your own code, you can just
use them as is.
Right. My mistake. But 8042 port I/O still not easy.

http://www.os2museum.com/wp/ibm-pcat-8042-keyboard-controller-commands/
Johann 'Myrkraverk' Oskarsson
2019-10-23 00:49:27 UTC
Permalink
Post by Ross Ridge
Depending what sort of application you're writing then this may or not
be what yo want. For normal keyboard text input, you want to buffer
the keystrokes instead of updating an array, just like the BIOS does.
At the very least I want to implement a line editor; think high score
input, login or what have you. So I will implement some sort of
keystroke buffer in addition to my other needs, if any.
--
Johann | email: invalid -> com | www.myrkraverk.com/blog/
I'm not from the Internet, I just work there. | twitter: @myrkraverk
Johann 'Myrkraverk' Oskarsson
2019-10-26 03:51:23 UTC
Permalink
Post by T. Ment
I didn't analyze that, don't know. But Ridges's code overlooks status
port 64h. Wyatt checks it, and delay loops until the data port is ready
(p. 45).
Yes, Wyatt does something interesting in his XT keyboard handler.

CLI ; Disable interrupts
GetStat: IN AL, StatusPort ; Get keyboard status
TEST AL, InputFull ; Is the coding complete?
LOOPNZ GetStat ; No, so continue waiting
IN AL, DataPort ; Yes, so get code
STI ; Enable interrupts

Where StatusPort, InputFull, and DataPort are given as

DataPort EQU 60h
StatusPort EQU 64h
InputFull EQU 02h

The above code is inside his int 09 handler. I'm not certain it's
correct, nor necessary. The XT BIOS doesn't do this. Could be
necessary for some clones, I wouldn't know.
--
Johann | email: invalid -> com | www.myrkraverk.com/blog/
I'm not from the Internet, I just work there. | twitter: @myrkraverk
T. Ment
2019-10-26 04:51:19 UTC
Permalink
Post by Johann 'Myrkraverk' Oskarsson
Wyatt does something interesting in his XT keyboard handler.
Like KEYLOOK2, KEYLOOK1 is AT only. That must be true, given the ports.
Post by Johann 'Myrkraverk' Oskarsson
CLI ; Disable interrupts
GetStat: IN AL, StatusPort ; Get keyboard status
TEST AL, InputFull ; Is the coding complete?
LOOPNZ GetStat ; No, so continue waiting
IN AL, DataPort ; Yes, so get code
STI ; Enable interrupts
Where StatusPort, InputFull, and DataPort are given as
DataPort EQU 60h
StatusPort EQU 64h
InputFull EQU 02h
The above code is inside his int 09 handler. I'm not certain it's
correct, nor necessary. The XT BIOS doesn't do this. Could be
necessary for some clones, I wouldn't know.
I mentioned this earlier. It's a bad example. He's doing a busy wait on
the status bit with interrupts disabled. That's wrong

You must check the status bit

https://wiki.osdev.org/%228042%22_PS/2_Controller#Status_Register

But don't keep interrupts disabled while waiting. If you need to make
two I/O instructions atomic, use a CLI/STI pair once per loop, NOT
outside the loop as Wyatt did. And don't follow STI immediately with a
CLI, that won't release the interrupt lock, as discussed elsewhere.

If it takes more than a millisecond, give up and error out. Don't leave
the PC locked up like Wyatt's code could.
Johann 'Myrkraverk' Oskarsson
2019-10-26 05:09:07 UTC
Permalink
Post by T. Ment
Post by Johann 'Myrkraverk' Oskarsson
Wyatt does something interesting in his XT keyboard handler.
Like KEYLOOK2, KEYLOOK1 is AT only. That must be true, given the ports.
Yes. He does say "if you are using an AT-class machine, chances are
that you can use the program in [..] keylook2.asm instead." [1] I took
that as meaning keylook1.asm is for XT-class machines. But I could be
misinterpreting something.

[1] Page 47, last paragraph.
--
Johann | email: invalid -> com | www.myrkraverk.com/blog/
I'm not from the Internet, I just work there. | twitter: @myrkraverk
T. Ment
2019-10-26 12:12:49 UTC
Permalink
Post by Johann 'Myrkraverk' Oskarsson
Post by T. Ment
Post by Johann 'Myrkraverk' Oskarsson
Wyatt does something interesting in his XT keyboard handler.
Like KEYLOOK2, KEYLOOK1 is AT only. That must be true, given the ports.
Yes. He does say "if you are using an AT-class machine, chances are
that you can use the program in [..] keylook2.asm instead." [1] I took
that as meaning keylook1.asm is for XT-class machines. But I could be
misinterpreting something.
[1] Page 47, last paragraph.
I read it too. Not all authors write clearly. His keylook examples are
only intended to teach keyboard buffer operation, not provide a working
INT 9 replacement.
Ross Ridge
2019-10-29 01:01:18 UTC
Permalink
Post by Johann 'Myrkraverk' Oskarsson
The above code is inside his int 09 handler. I'm not certain it's
correct, nor necessary. The XT BIOS doesn't do this. Could be
necessary for some clones, I wouldn't know.
Out of context, I'm not entirely sure what the code you posted is supposed
to do, but it's unnecessary to check the status register to see if there's
a scancode available to be read from port 0x60. The keyboard controller
will only generate an interrupt when there's is a scancode available.

However the code you posted don't actually check to see if there's a
scancode avaiable. What is actually does is check to see if whether
its possible to write a data byte to port 0x60 or command byte to
port 0x64. Bit 0 of port 0x64 is what the controller sets when there's
a scancode availible. Bit 1 of port 0x64 is what it sets when somthing
has been written to port 0x60 or 0x64, but the controller hasn't finished
processing it yet. In other words, bit 1 being set means that it's not
ready for a byte to be written to port 0x60 or 0x64.

This is actually a fairly common mistake. Documention for the keyboard
controller is often written from the perspective of the keyboard
controller itself. So "input" means input to the keyboard controller,
that is writes to port 0x60 or 0x64. While on the other hand "output"
means output from the controller, which is what can be read from port
0x60.

Another strange thing the code does is uses the LOOPNE instruction.
This instruction both checks the status of the ZF flag and decrements CX.
If ZF is clear and CX isn't zero after the decrement then it jumps to the
label given, otherwise it continues to the next instruction. This means
that the loop will only check at most CX times, or 65536 times if CX was
initially 0. If CX is initialized to something before the start of the
loop then this was probably intended implement a timeout, though one of
a highly variable length given the vast range of speeds ot x86 CPUs.
If CX wasn't initialized then author probably didn't know that LOOPNE
instruction actually does.

It's possible the full context of the code makes it reasonable, maybe
later on it does write something to port 0x60 or 0x64, but it seems
likely the code you posted was a mistake.

btw. None of this applies original IBM PC and XT, as their much more
primitive controller doesn't have a port 0x64 (nor could you write to
port 0x60). The only way you could tell if a new scancode was received
was if a keyboard interrupt had occured. The AT controller was designed
to be compatible with interrupt handlers written for the XT, and so it
was designed for it to be unnecessary to read port 0x64 in a keyboard
interrupt handler.
--
l/ // Ross Ridge -- The Great HTMU
[oo][oo] ***@csclub.uwaterloo.ca
-()-/()/ http://www.csclub.uwaterloo.ca/~rridge/
db //
T. Ment
2019-10-29 01:39:54 UTC
Permalink
Post by Ross Ridge
it's unnecessary to check the status register to see if there's
a scancode available to be read from port 0x60. The keyboard controller
will only generate an interrupt when there's is a scancode available
.
Post by Ross Ridge
However the code you posted don't actually check to see if there's a
scancode avaiable. What is actually does is check to see if whether
its possible to write a data byte to port 0x60 or command byte to
port 0x64. Bit 0 of port 0x64 is what the controller sets when there's
a scancode availible. Bit 1 of port 0x64 is what it sets when somthing
has been written to port 0x60 or 0x64, but the controller hasn't finished
processing it yet. In other words, bit 1 being set means that it's not
ready for a byte to be written to port 0x60 or 0x64.
This is actually a fairly common mistake. Documention for the keyboard
controller is often written from the perspective of the keyboard
controller itself. So "input" means input to the keyboard controller,
that is writes to port 0x60 or 0x64. While on the other hand "output"
means output from the controller, which is what can be read from port
0x60.
Another strange thing the code does is uses the LOOPNE instruction.
This instruction both checks the status of the ZF flag and decrements CX.
If ZF is clear and CX isn't zero after the decrement then it jumps to the
label given, otherwise it continues to the next instruction. This means
that the loop will only check at most CX times, or 65536 times if CX was
initially 0. If CX is initialized to something before the start of the
loop then this was probably intended implement a timeout, though one of
a highly variable length given the vast range of speeds ot x86 CPUs.
If CX wasn't initialized then author probably didn't know that LOOPNE
instruction actually does.
It's possible the full context of the code makes it reasonable, maybe
later on it does write something to port 0x60 or 0x64, but it seems
likely the code you posted was a mistake.
btw. None of this applies original IBM PC and XT, as their much more
primitive controller doesn't have a port 0x64 (nor could you write to
port 0x60). The only way you could tell if a new scancode was received
was if a keyboard interrupt had occured. The AT controller was designed
to be compatible with interrupt handlers written for the XT, and so it
was designed for it to be unnecessary to read port 0x64 in a keyboard
interrupt handler.
T. Ment
2019-10-29 02:02:31 UTC
Permalink
Post by Ross Ridge
it's unnecessary to check the status register to see if there's
a scancode available to be read from port 0x60. The keyboard controller
will only generate an interrupt when there's is a scancode available.
The 64h status bit indicates coding complete. Maybe some keyboard
controllers jump the gun and signal interrupt before coding is complete.
That would create a race condition.
Post by Ross Ridge
The AT controller was designed to be compatible with interrupt
handlers written for the XT, and so it was designed for it to be
unnecessary to read port 0x64 in a keyboard interrupt handler.
Show me datasheets to back your claim.
.
T. Ment
2019-10-29 10:09:14 UTC
Permalink
Post by Ross Ridge
The AT controller was designed to be compatible with interrupt
handlers written for the XT, and so it was designed for it to be
unnecessary to read port 0x64 in a keyboard interrupt handler.
Ridge is wrong. See "Warnings" on page 275 of The Undocumented PC.

Gilluwe corroborates what I posted earlier. You must check port 64h bit
0 before reading port 60h. And that's not all. It gets worse:

"MCA systems with a Type 1 controller ... must wait at
least 7 microseconds after bit 0 transitions from 0 to 1,
before reading the data from port 60h."

You can't beat professional BIOS programmers with homegrown port 60h
code. Doesn't matter how smart you are. You don't have the resources.
T. Ment
2019-10-29 10:13:38 UTC
Permalink
Post by T. Ment
Ridge is wrong. See "Warnings" on page 275 of The Undocumented PC.
Page 273. Sample code starts on page 275.

Ross Ridge
2019-10-22 17:42:49 UTC
Permalink
Post by Johann 'Myrkraverk' Oskarsson
Yet I've seen that done for SoundBlaster code. Are you absolutely sure
this is not a delaying tactic for the keyboard circuitry?
It's a simple read/modify/write sequence followed by writing the "old"
value back. There's rarely any need for I/O delays on a IBM PC XT where
everything runs on the same 4.77 MHz clock.

In case you don't understand what the assembly code is actually doing,
here's the relevant IBM PC XT BIOS code with hopefully better comments:

IN AL, 61H ; read the "old" value of PPI port B
MOV AH, AL ; save the "old" value to AH
OR AL, 80H ; set bit 7 in the value, creating a new value in AL
; other bits are left unchanged
OUT 61H, AL ; write the new value with bit 7 set to PPI port B
XCHG AH, AL ; swap AH and AL, movin the "old" value back into AL
OUT 61H, AL ; write the "old" value to PPI port B
; presumably the "old" value has bit 7 clear

The point of the operation to "pulse" the output of the PB7 pin of the
XT's 8255 PPI. The PB7 pin is breifly driven high and then driven low
again, creating a signal that looks like this:

______
| |
__________| |___________________________________


If you look at page D-11 of the IBM PC XT Technical Reference you can
see on the "System Board (Sheet 9 of 10)" schematic how the PB7 pin
is connected to both the 74LS322 shift register's !CLR pin and to a
flip flop that latches the IRQ1 line. Pulsing the PB7 line both clears
the shift register so that it can receive the next keyboard scan code,
and clears the latch so that IRQ1 is no longer asserted.

(Note to be clear this something that is only meaningful on IBM PCs
and IBM PC XTs. IBM PC AT and later PCs have no 8255 PPI chip and no
PB7 line. Instead they have an i8041/2 microcontroller that partially
emulates the functionality that the PPI chip provided. Setting or
clearing bit 7 of port 0x61 has no effect on a AT class computer. Reading
the scancode from port 0x60 automatically causes the microcontroller to
deassert IRQ1 and become ready to receive the next scan code.)

The only question about the XT BIOS code is if reading the value of I/O
port 0x61, the port B data register of the PPI, actually does anything
useful. Ralf Brown's Interrupt List (in PORTS.LST) describes port 0x61
on XTs as being write-only, and the 8255 data sheet doesn't say what
happens when you try to read from port B when it's configured for output.
Presumably it either reads the last written to the port or returns all
0s, otherwise the XT BIOS code wouldn't work.

Not that it really matters for your code, since you don't need to support
IBM PCs and XTs and so don't need to do anything with port 0x61.
--
l/ // Ross Ridge -- The Great HTMU
[oo][oo] ***@csclub.uwaterloo.ca
-()-/()/ http://www.csclub.uwaterloo.ca/~rridge/
db //
Johann 'Myrkraverk' Oskarsson
2019-10-22 20:02:00 UTC
Permalink
Post by Ross Ridge
Not that it really matters for your code, since you don't need to support
IBM PCs and XTs and so don't need to do anything with port 0x61.
Indeed, I don't need to care about this in code, but I do like to know
things; thank you for the detailed description.
--
Johann | email: invalid -> com | www.myrkraverk.com/blog/
I'm not from the Internet, I just work there. | twitter: @myrkraverk
T. Ment
2019-10-22 08:10:40 UTC
Permalink
Post by Johann 'Myrkraverk' Oskarsson
I haven't explored the latency tester yet, but I will.
Don't know about Watcom. I used Borland C++ 3.1 because I know it best.

I use it for testing disk BIOS latency, by copying a 40meg file. But it
will attempt to run any command line you give it. For example:

ilat copy ..\z.dat .
avg 4.430, min = 2, max = 13, count = 2142

The avg is microseconds, the count is milliseconds. So this PC copied
the 40meg file in 2.1 seconds with average latency under 5 microseconds,
which is very good. But results can be much higher with a poor quality
BIOS.

Don't try to understand the series in the code. It's an algorithm taken
from PCTIM003.TXT, a document which covers more than you ever wanted to
know about PC timers. The point of the algorithm is to keep a precise
tick count without drift error. I understand hardware drift. But I fix
software problems and let engineers fight hardware problems.

I hardcoded it to run the timer at 1,000 Hz. I made other frequencies
available, but then you must change a line and recompile. That could be
improved, but so far I'm the only user. and I since I rarely change it,
I never bothered.

I didn't bother with a license either. If that matters to you, consider
it Boost licensed. I agree with Walter Bright, it's the closest thing to
public domain.
Johann 'Myrkraverk' Oskarsson
2019-10-22 00:13:13 UTC
Permalink
Post by Ross Ridge
If you don't have an actual IBM PC or IBM PC/XT (or a 100% compatible
clone) to test your code on, I wouldn't bother trying to support
PC/XT keyboard controllers. Without testing it you can't be sure it's
actually going to work. You can't depend on the code in the book you're
copying from actually working. Aside from the general unreliability
of code in books, it's unlikely that the author of the "Black Art 3D
Game Programming" ever tested his code on anything other than an AT or
later PC. XT class machines were long obsolete by the time the book
was written.
Thank you. I don't have an original XT to test my code, so I've ditched
the keyboard controller logic. In any case, I'm writing for 386+ with
CauseWay.

The beep is gone from DOSBox-X when I run my test code, so I guess the
controller logic was at fault, but I don't know. I don't know why it
was there in the first place.

I'm going to experiment now with scan code interpretations.
--
Johann | email: invalid -> com | www.myrkraverk.com/blog/
I'm not from the Internet, I just work there. | twitter: @myrkraverk
Johann 'Myrkraverk' Oskarsson
2019-10-22 00:15:04 UTC
Permalink
Post by Ross Ridge
Here's an example keyboard interrupt handler suitable for use in a game
Based on your code, I assume the interrupt is called separately for
every byte in a multi-byte scan code sequence from the keyboard. Am I
right?
--
Johann | email: invalid -> com | www.myrkraverk.com/blog/
I'm not from the Internet, I just work there. | twitter: @myrkraverk
Ross Ridge
2019-10-22 05:51:31 UTC
Permalink
Post by Johann 'Myrkraverk' Oskarsson
Based on your code, I assume the interrupt is called separately for
every byte in a multi-byte scan code sequence from the keyboard. Am I
right?
Yes.
--
l/ // Ross Ridge -- The Great HTMU
[oo][oo] ***@csclub.uwaterloo.ca
-()-/()/ http://www.csclub.uwaterloo.ca/~rridge/
db //
Rod Pemberton
2019-10-22 02:50:23 UTC
Permalink
On Mon, 21 Oct 2019 18:31:44 +0800
Post by Johann 'Myrkraverk' Oskarsson
Instead of overwriting int 15 and hook the keyboard translator, I
decided to experiment with my own keyboard interrupt handler.
I implemented my own keyboard interrupt handler for my operating
system, but never for DOS.

For IBM AT clones (not PC or XT), they use an XT scancode set which is
translated to the AT scancode set. This was needed for XT software
compatibility on ATs. This XT->AT scancode set uses make-break
sequences, instead of a single code for a keypress, like USB scancode
set. Unfortunately, the USB scancode set is/was defectively
implemented on many keyboards. Decoding these XT->AT sequences is
somewhat complicated, because they're interruptable by other key
presses on many keyboards, but not every keyboard. So, you'll have a
sequence for one keypress right in the middle of a sequence for another
keypress. For my OS, this required a large lookup table, a ring
buffer, and plenty of code to decode the sequences properly. Of
course, you can take short-cuts, i.e., simplified, e.g., ignore
complicated stuff, like interrupted sequences.

You'll want to read section 10 here for scancodes. Table 10.6 A shows
the four scancode sets both with and without AT translation. If you're
reading or writing to the keyboard controller directly (port 0x60,
0x61), you probably want to read section 11 too.

https://www.win.tue.nl/~aeb/linux/kbd/scancodes.html

That is a great reference but not perfect or complete. If you're going
that in-depth, i.e., more suited to alt.os.development, you may need
other sources, i.e., keyboard controller .pdfs.


Rod Pemberton
--
The U.S. government can't allow a government employee to anonymously
attack the President of the U.S.
Johann 'Myrkraverk' Oskarsson
2019-10-26 03:48:20 UTC
Permalink
Post by Rod Pemberton
https://www.win.tue.nl/~aeb/linux/kbd/scancodes.html
That is a great reference but not perfect or complete. If you're going
that in-depth, i.e., more suited to alt.os.development, you may need
other sources, i.e., keyboard controller .pdfs.
Do you have the names or links for these pdfs? All I have so far is the
XT and AT technical manuals from I believe archive.org.

1502494_PC_AT_Technical_Reference_Mar84.pdf (forgot where I found it)

and

PC-XT.pdf (where I got the BIOS listing from).

I also have the book by Wyatt, Advanced Assembly Language, which also
has scan code tables and examples of a keyboard interrupt handler.

I also have The Undocumented PC by Frank van Gilluwe on the way.
--
Johann | email: invalid -> com | www.myrkraverk.com/blog/
I'm not from the Internet, I just work there. | twitter: @myrkraverk
Rod Pemberton
2019-10-26 08:32:35 UTC
Permalink
On Sat, 26 Oct 2019 11:48:20 +0800
(added a.o.d. to archive links, redirects set to c.o.m.p.)
Post by Johann 'Myrkraverk' Oskarsson
Post by Rod Pemberton
https://www.win.tue.nl/~aeb/linux/kbd/scancodes.html
That is a great reference but not perfect or complete. If you're
going that in-depth, i.e., more suited to alt.os.development, you
may need other sources, i.e., keyboard controller .pdfs.
Do you have the names or links for these pdfs? All I have so far is
the XT and AT technical manuals from I believe archive.org.
I thought I posted some to a.o.d. (probably stale links ...), but I'm
only finding USB pdfs via Google Group search. USB has its own set of
standards for keyboards. My post that I found was 2007 and the links
are stale. Let me know if you need these and I'll try to locate the
files. Since USB stuff has it's own standards organization, there is
no guarantee they're still around for free, but Google will probably
find them.

Also, Microsoft has a scancode specification.
https://babbage.cs.qc.cuny.edu/courses/cs345/Manuals/ms_scancode.pdf

This is interesting. Someone disassembled a keyboard ROM and
listed the keyboard commands. Some were undocumented, apparently.
http://www.os2museum.com/wp/ibm-pcat-8042-keyboard-controller-commands/

I usually start with bitsavers, as other large PC related archives seem
to go offline, but I don't see an 8042 pdf. I've not read the PC/XT/AT
Technical references on the keyboard, but they do seem to have a section
on the keyboard including scancodes and commands.
http://bitsavers.org/pdf/ibm/pc/

You can use Google with searches like:

8042 datasheet filetype:pdf
8042 keyboard controller filetype:pdf
101/102 keyboard datasheet filetype:pdf
"set 1" "set 2" keyboard controller filetype:pdf

The latter provides pdf's for a number of keyboard controller chips
which are circuit clones of 8042 based AT 101/102 key keyboards. These
searches will likely also find some PC-on-chip datasheets too.

https://www.farnell.com/datasheets/79189.pdf
https://www.mouser.com/datasheet/2/268/00002121A-962573.pdf
http://read.pudn.com/downloads365/sourcecode/embedded/1585630/AmiKbc%20V1.2.pdf
http://www.zilog.com/appnotes_download.php?FromPage=DirectLink&dn=AN0087&ft=Application%20Note&f=YUhSMGNEb3ZMM2QzZHk1NmFXeHZaeTVqYjIwdlpHOWpjeTloY0hCdWIzUmxjeTloYmpBd09EY3VjR1Jt

If you look through enough of them, you should find at least one which
describes how to decode the keycodes or make/break sequences, or
whatever it is you're after. They usually attempt to preserve required
historical behavior, which should be discussed.

If you don't find what you're looking for, you just tweak the search
terms until you do, or until you conclude you can't find anything
better.


Rod Pemberton
--
The U.S. government can't allow a government employee to anonymously
attack the President of the U.S.
Scott Lurndal
2019-10-26 14:23:56 UTC
Permalink
Post by Rod Pemberton
On Sat, 26 Oct 2019 11:48:20 +0800
(added a.o.d. to archive links, redirects set to c.o.m.p.)
Post by Johann 'Myrkraverk' Oskarsson
Post by Rod Pemberton
https://www.win.tue.nl/~aeb/linux/kbd/scancodes.html
That is a great reference but not perfect or complete. If you're
going that in-depth, i.e., more suited to alt.os.development, you
may need other sources, i.e., keyboard controller .pdfs.
Do you have the names or links for these pdfs? All I have so far is
the XT and AT technical manuals from I believe archive.org.
I thought I posted some to a.o.d. (probably stale links ...), but I'm
only finding USB pdfs via Google Group search. USB has its own set of
standards for keyboards. My post that I found was 2007 and the links
are stale. Let me know if you need these and I'll try to locate the
files. Since USB stuff has it's own standards organization, there is
no guarantee they're still around for free, but Google will probably
find them.
https://www.usb.org/documents
Loading...