do not edit — generated by btf.
git.druid.rocksindexdruid520kaboomsrc/drivers/kbd.nsc

src/drivers/kbd.nsc


/*
 * ps/2 keyboard, scancode set 1, us qwerty, make codes only (break
 * codes -- key release, scancode | 0x80 -- are read and mostly
 * discarded so the controller doesn't stall, not acted on, except
 * for shift's own release which has to be tracked). every mapped
 * keypress is echoed straight to vga+serial (local echo) AND pushed
 * into a small circular buffer that kbd_getchar() drains -- the
 * read() syscall (idt.nsc, by way of fd.nsc's fd_read_stdin) is the
 * actual reader.
 *
 * scancode-to-ascii mapping, shift, and caps lock are a real port of
 * tape-kernel's own kb.c (scntasci/gtchr) -- not reinvented, ported
 * as a lookup-driven if-chain instead of its switch (the actual
 * scancode->char table is identical) since this is irq-driven, not
 * gtchr's own polling loop. two things this deliberately does NOT
 * copy from tape-kernel: extended (0xE0-prefixed) keys are still
 * just discarded (tape-kernel dropped them too, "for now"), and
 * backspace's actual erase behavior lives in fd.nsc's
 * fd_read_stdin, not here -- see that file's own note on why an
 * irq handler that echoes immediately, with no idea how many
 * characters the current read has accepted, is the wrong place to
 * decide whether there's anything to erase.
 */
 
u8 inb(u16 port);
void vga_putc(u8 ch);
void serial_putc(u8 ch);
 
global i8 kbd_buf[256];
global u64 kbd_head;
global u64 kbd_tail;
global u64 kbd_count;
 
global u8 kbd_shift;
global u8 kbd_caps;
 
/* scancode -> ascii, given the current shift/caps state -- a direct
 * port of tape-kernel's scntasci, as an if-chain instead of a switch
 * (same mapping, same shift ^ caps rule for letters, plain shift for
 * everything else that has a shifted form). returns 0 for anything
 * unmapped (function keys, extended keys, ...), same as the
 * original. */
i8
kbd_scancode_to_ascii(u8 sc, u8 shift, u8 caps)
{
	u8 up;
 
	up = shift ^ caps;
 
	if(sc == (u8)0x1E) { if(up != (u8)0) { return 65; } return 97; }  /* a/A */
	if(sc == (u8)0x30) { if(up != (u8)0) { return 66; } return 98; }  /* b/B */
	if(sc == (u8)0x2E) { if(up != (u8)0) { return 67; } return 99; }  /* c/C */
	if(sc == (u8)0x20) { if(up != (u8)0) { return 68; } return 100; } /* d/D */
	if(sc == (u8)0x12) { if(up != (u8)0) { return 69; } return 101; } /* e/E */
	if(sc == (u8)0x21) { if(up != (u8)0) { return 70; } return 102; } /* f/F */
	if(sc == (u8)0x22) { if(up != (u8)0) { return 71; } return 103; } /* g/G */
	if(sc == (u8)0x23) { if(up != (u8)0) { return 72; } return 104; } /* h/H */
	if(sc == (u8)0x17) { if(up != (u8)0) { return 73; } return 105; } /* i/I */
	if(sc == (u8)0x24) { if(up != (u8)0) { return 74; } return 106; } /* j/J */
	if(sc == (u8)0x25) { if(up != (u8)0) { return 75; } return 107; } /* k/K */
	if(sc == (u8)0x26) { if(up != (u8)0) { return 76; } return 108; } /* l/L */
	if(sc == (u8)0x32) { if(up != (u8)0) { return 77; } return 109; } /* m/M */
	if(sc == (u8)0x31) { if(up != (u8)0) { return 78; } return 110; } /* n/N */
	if(sc == (u8)0x18) { if(up != (u8)0) { return 79; } return 111; } /* o/O */
	if(sc == (u8)0x19) { if(up != (u8)0) { return 80; } return 112; } /* p/P */
	if(sc == (u8)0x10) { if(up != (u8)0) { return 81; } return 113; } /* q/Q */
	if(sc == (u8)0x13) { if(up != (u8)0) { return 82; } return 114; } /* r/R */
	if(sc == (u8)0x1F) { if(up != (u8)0) { return 83; } return 115; } /* s/S */
	if(sc == (u8)0x14) { if(up != (u8)0) { return 84; } return 116; } /* t/T */
	if(sc == (u8)0x16) { if(up != (u8)0) { return 85; } return 117; } /* u/U */
	if(sc == (u8)0x2F) { if(up != (u8)0) { return 86; } return 118; } /* v/V */
	if(sc == (u8)0x11) { if(up != (u8)0) { return 87; } return 119; } /* w/W */
	if(sc == (u8)0x2D) { if(up != (u8)0) { return 88; } return 120; } /* x/X */
	if(sc == (u8)0x15) { if(up != (u8)0) { return 89; } return 121; } /* y/Y */
	if(sc == (u8)0x2C) { if(up != (u8)0) { return 90; } return 122; } /* z/Z */
 
	if(sc == (u8)0x02) { if(shift != (u8)0) { return 33; } return 49; }  /* 1/! */
	if(sc == (u8)0x03) { if(shift != (u8)0) { return 64; } return 50; }  /* 2/@ */
	if(sc == (u8)0x04) { if(shift != (u8)0) { return 35; } return 51; }  /* 3/# */
	if(sc == (u8)0x05) { if(shift != (u8)0) { return 36; } return 52; }  /* 4/$ */
	if(sc == (u8)0x06) { if(shift != (u8)0) { return 37; } return 53; }  /* 5/% */
	if(sc == (u8)0x07) { if(shift != (u8)0) { return 94; } return 54; }  /* 6/^ */
	if(sc == (u8)0x08) { if(shift != (u8)0) { return 38; } return 55; }  /* 7/& */
	if(sc == (u8)0x09) { if(shift != (u8)0) { return 42; } return 56; }  /* 8/* */
	if(sc == (u8)0x0A) { if(shift != (u8)0) { return 40; } return 57; }  /* 9/( */
	if(sc == (u8)0x0B) { if(shift != (u8)0) { return 41; } return 48; }  /* 0/) */
 
	if(sc == (u8)0x0C) { if(shift != (u8)0) { return 95; } return 45; }  /* -/_ */
	if(sc == (u8)0x0D) { if(shift != (u8)0) { return 43; } return 61; }  /* =/+ */
	if(sc == (u8)0x1A) { if(shift != (u8)0) { return 123; } return 91; } /* [/{ */
	if(sc == (u8)0x1B) { if(shift != (u8)0) { return 125; } return 93; } /* ]/} */
	if(sc == (u8)0x2B) { if(shift != (u8)0) { return 124; } return 92; } /* \/| */
	if(sc == (u8)0x27) { if(shift != (u8)0) { return 58; } return 59; }  /* ;/: */
	if(sc == (u8)0x28) { if(shift != (u8)0) { return 34; } return 39; }  /* '/" */
	if(sc == (u8)0x29) { if(shift != (u8)0) { return 126; } return 96; } /* `/~ */
	if(sc == (u8)0x33) { if(shift != (u8)0) { return 60; } return 44; }  /* ,/< */
	if(sc == (u8)0x34) { if(shift != (u8)0) { return 62; } return 46; }  /* ./> */
	if(sc == (u8)0x35) { if(shift != (u8)0) { return 63; } return 47; }  /* //? */
 
	if(sc == (u8)0x39) { return 32; } /* space */
	if(sc == (u8)0x1C) { return 10; } /* enter */
	if(sc == (u8)0x0E) { return 8; }  /* backspace */
	if(sc == (u8)0x0F) { return 9; }  /* tab */
 
	return 0;
}
 
global void
kbd_init(void)
{
	/* the ps/2 controller and keyboard device are already enabled and
	 * translating scancodes by the time a real bios/uefi hands off to
	 * a bootloader -- nothing to program here for a first working
	 * driver. a real driver would probe/reset the controller itself
	 * (port 0x64 command interface) rather than trust that, but nsc
	 * had no way to write structured hardware descriptors either
	 * before that gap was found (see gdt.nsc's note on the same
	 * theme), so keeping this minimal for now is the same trade every
	 * other driver here has made: correctness of what exists over
	 * completeness of everything that could exist.
	 */
	kbd_shift = 0;
	kbd_caps = 0;
	return;
}
 
global void
kbd_handle_irq(void)
{
	u8 scancode;
	i8 ch;
 
	scancode = inb((u16)0x60);
 
	if(scancode == (u8)0xE0)
	{
		/* extended scancode prefix -- the real byte follows as a
		 * second inb, discarded the same as tape-kernel's own gtchr
		 * does ("for now"): no arrow keys/home/end handling exists
		 * yet, just don't let the second byte fall through and get
		 * misread as an ordinary key. */
		inb((u16)0x60);
		return;
	}
 
	if(scancode == (u8)0x2A || scancode == (u8)0x36)
	{
		kbd_shift = 1;
		return;
	}
	if(scancode == (u8)0xAA || scancode == (u8)0xB6)
	{
		kbd_shift = 0;
		return;
	}
	if(scancode == (u8)0x3A)
	{
		/* caps lock toggles on the PRESS, same as a real keyboard's
		 * own latch -- the matching release code (0xBA) carries no
		 * new information and is just an ordinary break code. */
		kbd_caps = kbd_caps ^ (u8)1;
		return;
	}
 
	if(scancode >= (u8)128)
	{
		return; /* break code (key release) -- discard */
	}
 
	ch = kbd_scancode_to_ascii(scancode, kbd_shift, kbd_caps);
	if(ch == (i8)0)
	{
		return; /* unmapped key */
	}
 
	kbd_push((u8)ch);
}
 
/* echoes and buffers one input byte, from EITHER source -- a real
 * ps/2 keystroke (kbd_handle_irq, above) or a byte arriving over
 * serial (serial.nsc's own irq handler, once -nographic is the only
 * console and there's no ps/2 keyboard generating scancodes at all,
 * only a raw byte stream from whatever real terminal the user is
 * actually typing into). one shared buffer, one shared echo rule, so
 * fd_read_stdin/kbd_getchar never need to know or care which source
 * a given byte came from.
 *
 * backspace is echoed by fd_read_stdin instead, once it knows
 * whether there's actually anything on the current line to erase --
 * see that function's own note. every other character is still
 * local-echoed here, the same as always. */
global void
kbd_push(u8 ch)
{
	if(ch != (u8)8)
	{
		vga_putc(ch);
		serial_putc(ch);
	}
 
	if(kbd_count < (u64)256)
	{
		kbd_buf[kbd_head] = (i8)ch;
		kbd_head = (kbd_head + (u64)1) % (u64)256;
		kbd_count = kbd_count + (u64)1;
	}
	/* buffer full: drop the keystroke rather than overwrite an
	 * unread one -- no reader has fallen behind by 256 keystrokes in
	 * practice, and silently corrupting older unread input would be
	 * worse than losing the newest one. */
}
 
/* pops one byte, or -1 if the buffer is empty. non-blocking --
 * read() (idt.nsc) is what actually blocks (busy-waits on halt())
 * when it wants data and there isn't any yet. */
global i32
kbd_getchar(void)
{
	i8 ch;
 
	if(kbd_count == (u64)0)
	{
		return -1;
	}
	ch = kbd_buf[kbd_tail];
	kbd_tail = (kbd_tail + (u64)1) % (u64)256;
	kbd_count = kbd_count - (u64)1;
	return (i32)(u8)ch;
}
powered by btf.