mirror of
https://github.com/kenshineto/kern.git
synced 2025-04-10 12:37:26 +00:00
1028 lines
20 KiB
C
1028 lines
20 KiB
C
/*
|
|
** SCCS ID: @(#)cio.c 2.10 1/22/25
|
|
**
|
|
** @file cio.c
|
|
**
|
|
** @author Warren R. Carithers
|
|
**
|
|
** Based on: c_io.c 1.13 (Ken Reek, Jon Coles, Warren R. Carithers)
|
|
**
|
|
** Console I/O routines
|
|
**
|
|
** This module implements a simple set of input and output routines
|
|
** for the console screen and keyboard on the machines in the DSL.
|
|
** Refer to the header file comments for complete details.
|
|
**
|
|
** Naming conventions:
|
|
**
|
|
** Externally-visible functions have names beginning with the
|
|
** characters "cio_".
|
|
**
|
|
*/
|
|
|
|
#include <cio.h>
|
|
#include <lib.h>
|
|
#include <support.h>
|
|
#include <x86/arch.h>
|
|
#include <x86/pic.h>
|
|
#include <x86/ops.h>
|
|
|
|
/*
|
|
** Bit masks for the lower five and eight bits of a value
|
|
*/
|
|
#define BMASK5 0x1f
|
|
#define BMASK8 0xff
|
|
|
|
/*
|
|
** Video parameters
|
|
*/
|
|
#define SCREEN_MIN_X 0
|
|
#define SCREEN_MIN_Y 0
|
|
#define SCREEN_X_SIZE 80
|
|
#define SCREEN_Y_SIZE 25
|
|
#define SCREEN_MAX_X (SCREEN_X_SIZE - 1)
|
|
#define SCREEN_MAX_Y (SCREEN_Y_SIZE - 1)
|
|
|
|
/*
|
|
** Video state
|
|
*/
|
|
static unsigned int scroll_min_x, scroll_min_y;
|
|
static unsigned int scroll_max_x, scroll_max_y;
|
|
static unsigned int curr_x, curr_y;
|
|
static unsigned int min_x, min_y;
|
|
static unsigned int max_x, max_y;
|
|
|
|
// pointer to input notification function
|
|
static void (*notify)(int);
|
|
|
|
#ifdef SA_DEBUG
|
|
#include <stdio.h>
|
|
#define cio_putchar putchar
|
|
#define cio_puts(x) fputs(x, stdout)
|
|
#endif
|
|
|
|
/*
|
|
** VGA definitions.
|
|
*/
|
|
|
|
// calculate the memory address of a specific character position
|
|
// within VGA memory
|
|
#define VIDEO_ADDR(x, y) \
|
|
(unsigned short *)((VID_BASE_ADDR + 2 * ((y) * SCREEN_X_SIZE + (x))) | \
|
|
0x80000000)
|
|
|
|
// port addresses
|
|
#define VGA_CTRL_IX_ADDR 0x3d4
|
|
#define VGA_CTRL_CUR_HIGH 0x0e // cursor location, high byte
|
|
#define VGA_CTRL_CUR_LOW 0x0f // cursor location, low byte
|
|
#define VGA_CTRL_IX_DATA 0x3d5
|
|
|
|
// attribute bits
|
|
#define VGA_ATT_BBI 0x80 // blink, or background intensity
|
|
#define VGA_ATT_BGC 0x70 // background color
|
|
#define VGA_ATT_FICS 0x80 // foreground intensity or char font select
|
|
#define VGA_ATT_FGC 0x70 // foreground color
|
|
|
|
// color selections
|
|
#define VGA_BG_BLACK 0x0000 // background colors
|
|
#define VGA_BG_BLUE 0x1000
|
|
#define VGA_BG_GREEN 0x2000
|
|
#define VGA_BG_CYAN 0x3000
|
|
#define VGA_BG_RED 0x4000
|
|
#define VGA_BG_MAGENTA 0x5000
|
|
#define VGA_BG_BROWN 0x6000
|
|
#define VGA_BG_WHITE 0x7000
|
|
|
|
#define VGA_FG_BLACK 0x0000 // foreground colors
|
|
#define VGA_FG_BLUE 0x0100
|
|
#define VGA_FG_GREEN 0x0200
|
|
#define VGA_FG_CYAN 0x0300
|
|
#define VGA_FG_RED 0x0400
|
|
#define VGA_FG_MAGENTA 0x0500
|
|
#define VGA_FG_BROWN 0x0600
|
|
#define VGA_FG_WHITE 0x0700
|
|
|
|
// color combinations
|
|
#define VGA_WHITE_ON_BLACK (VGA_FG_WHITE | VGA_BG_BLACK)
|
|
#define VGA_BLACK_ON_WHITE (VGA_FG_BLACK | VGA_BG_WHITE)
|
|
|
|
/*
|
|
** Internal support routines.
|
|
*/
|
|
|
|
/*
|
|
** setcursor: set the cursor location (screen coordinates)
|
|
*/
|
|
static void setcursor(void)
|
|
{
|
|
unsigned addr;
|
|
unsigned int y = curr_y;
|
|
|
|
if (y > scroll_max_y) {
|
|
y = scroll_max_y;
|
|
}
|
|
|
|
addr = (unsigned)(y * SCREEN_X_SIZE + curr_x);
|
|
|
|
outb(VGA_CTRL_IX_ADDR, VGA_CTRL_CUR_HIGH);
|
|
outb(VGA_CTRL_IX_DATA, (addr >> 8) & BMASK8);
|
|
outb(VGA_CTRL_IX_ADDR, VGA_CTRL_CUR_LOW);
|
|
outb(VGA_CTRL_IX_DATA, addr & BMASK8);
|
|
}
|
|
|
|
/*
|
|
** putchar_at: physical output to the video memory
|
|
*/
|
|
static void putchar_at(unsigned int x, unsigned int y, unsigned int c)
|
|
{
|
|
/*
|
|
** If x or y is too big or small, don't do any output.
|
|
*/
|
|
if (x <= max_x && y <= max_y) {
|
|
unsigned short *addr = VIDEO_ADDR(x, y);
|
|
|
|
/*
|
|
** The character may have attributes associated with it; if
|
|
** so, use those, otherwise use white on black.
|
|
*/
|
|
c &= 0xffff; // keep only the lower bytes
|
|
if (c > BMASK8) {
|
|
*addr = (unsigned short)c;
|
|
} else {
|
|
*addr = (unsigned short)c | VGA_WHITE_ON_BLACK;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Globally-visible support routines.
|
|
*/
|
|
|
|
/*
|
|
** Set the scrolling region
|
|
*/
|
|
void cio_setscroll(unsigned int s_min_x, unsigned int s_min_y,
|
|
unsigned int s_max_x, unsigned int s_max_y)
|
|
{
|
|
scroll_min_x = bound(min_x, s_min_x, max_x);
|
|
scroll_min_y = bound(min_y, s_min_y, max_y);
|
|
scroll_max_x = bound(scroll_min_x, s_max_x, max_x);
|
|
scroll_max_y = bound(scroll_min_y, s_max_y, max_y);
|
|
curr_x = scroll_min_x;
|
|
curr_y = scroll_min_y;
|
|
setcursor();
|
|
}
|
|
|
|
/*
|
|
** Cursor movement in the scroll region
|
|
*/
|
|
void cio_moveto(unsigned int x, unsigned int y)
|
|
{
|
|
curr_x = bound(scroll_min_x, x + scroll_min_x, scroll_max_x);
|
|
curr_y = bound(scroll_min_y, y + scroll_min_y, scroll_max_y);
|
|
setcursor();
|
|
}
|
|
|
|
/*
|
|
** The putchar family
|
|
*/
|
|
void cio_putchar_at(unsigned int x, unsigned int y, unsigned int c)
|
|
{
|
|
if ((c & 0x7f) == '\n') {
|
|
unsigned int limit;
|
|
|
|
/*
|
|
** If we're in the scroll region, don't let this loop
|
|
** leave it. If we're not in the scroll region, don't
|
|
** let this loop enter it.
|
|
*/
|
|
if (x > scroll_max_x) {
|
|
limit = max_x;
|
|
} else if (x >= scroll_min_x) {
|
|
limit = scroll_max_x;
|
|
} else {
|
|
limit = scroll_min_x - 1;
|
|
}
|
|
while (x <= limit) {
|
|
putchar_at(x, y, ' ');
|
|
x += 1;
|
|
}
|
|
} else {
|
|
putchar_at(x, y, c);
|
|
}
|
|
}
|
|
|
|
#ifndef SA_DEBUG
|
|
void cio_putchar(unsigned int c)
|
|
{
|
|
/*
|
|
** If we're off the bottom of the screen, scroll the window.
|
|
*/
|
|
if (curr_y > scroll_max_y) {
|
|
cio_scroll(curr_y - scroll_max_y);
|
|
curr_y = scroll_max_y;
|
|
}
|
|
|
|
switch (c & BMASK8) {
|
|
case '\n':
|
|
/*
|
|
** Erase to the end of the line, then move to new line
|
|
** (actual scroll is delayed until next output appears).
|
|
*/
|
|
while (curr_x <= scroll_max_x) {
|
|
putchar_at(curr_x, curr_y, ' ');
|
|
curr_x += 1;
|
|
}
|
|
curr_x = scroll_min_x;
|
|
curr_y += 1;
|
|
break;
|
|
|
|
case '\r':
|
|
curr_x = scroll_min_x;
|
|
break;
|
|
|
|
default:
|
|
putchar_at(curr_x, curr_y, c);
|
|
curr_x += 1;
|
|
if (curr_x > scroll_max_x) {
|
|
curr_x = scroll_min_x;
|
|
curr_y += 1;
|
|
}
|
|
break;
|
|
}
|
|
setcursor();
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
** The puts family
|
|
*/
|
|
void cio_puts_at(unsigned int x, unsigned int y, const char *str)
|
|
{
|
|
unsigned int ch;
|
|
|
|
while ((ch = *str++) != '\0' && x <= max_x) {
|
|
cio_putchar_at(x, y, ch);
|
|
x += 1;
|
|
}
|
|
}
|
|
|
|
#ifndef SA_DEBUG
|
|
void cio_puts(const char *str)
|
|
{
|
|
unsigned int ch;
|
|
|
|
while ((ch = *str++) != '\0') {
|
|
cio_putchar(ch);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
** Write a "sized" buffer (like cio_puts(), but no NUL)
|
|
*/
|
|
void cio_write(const char *buf, int length)
|
|
{
|
|
for (int i = 0; i < length; ++i) {
|
|
cio_putchar(buf[i]);
|
|
}
|
|
}
|
|
|
|
void cio_clearscroll(void)
|
|
{
|
|
unsigned int nchars = scroll_max_x - scroll_min_x + 1;
|
|
unsigned int l;
|
|
unsigned int c;
|
|
|
|
for (l = scroll_min_y; l <= scroll_max_y; l += 1) {
|
|
unsigned short *to = VIDEO_ADDR(scroll_min_x, l);
|
|
|
|
for (c = 0; c < nchars; c += 1) {
|
|
*to++ = ' ' | 0x0700;
|
|
}
|
|
}
|
|
}
|
|
|
|
void cio_clearscreen(void)
|
|
{
|
|
unsigned short *to = VIDEO_ADDR(min_x, min_y);
|
|
unsigned int nchars = (max_y - min_y + 1) * (max_x - min_x + 1);
|
|
|
|
while (nchars > 0) {
|
|
*to++ = ' ' | 0x0700;
|
|
nchars -= 1;
|
|
}
|
|
}
|
|
|
|
void cio_scroll(unsigned int lines)
|
|
{
|
|
unsigned short *from;
|
|
unsigned short *to;
|
|
int nchars = scroll_max_x - scroll_min_x + 1;
|
|
int line, c;
|
|
|
|
/*
|
|
** If # of lines is the whole scrolling region or more, just clear.
|
|
*/
|
|
if (lines > scroll_max_y - scroll_min_y) {
|
|
cio_clearscroll();
|
|
curr_x = scroll_min_x;
|
|
curr_y = scroll_min_y;
|
|
setcursor();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
** Must copy it line by line.
|
|
*/
|
|
for (line = scroll_min_y; line <= scroll_max_y - lines; line += 1) {
|
|
from = VIDEO_ADDR(scroll_min_x, line + lines);
|
|
to = VIDEO_ADDR(scroll_min_x, line);
|
|
for (c = 0; c < nchars; c += 1) {
|
|
*to++ = *from++;
|
|
}
|
|
}
|
|
|
|
for (; line <= scroll_max_y; line += 1) {
|
|
to = VIDEO_ADDR(scroll_min_x, line);
|
|
for (c = 0; c < nchars; c += 1) {
|
|
*to++ = ' ' | 0x0700;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int mypad(int x, int y, int extra, int padchar)
|
|
{
|
|
while (extra > 0) {
|
|
if (x != -1 || y != -1) {
|
|
cio_putchar_at(x, y, padchar);
|
|
x += 1;
|
|
} else {
|
|
cio_putchar(padchar);
|
|
}
|
|
extra -= 1;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
static int mypadstr(int x, int y, char *str, int len, int width, int leftadjust,
|
|
int padchar)
|
|
{
|
|
int extra;
|
|
|
|
if (len < 0) {
|
|
len = strlen(str);
|
|
}
|
|
extra = width - len;
|
|
if (extra > 0 && !leftadjust) {
|
|
x = mypad(x, y, extra, padchar);
|
|
}
|
|
if (x != -1 || y != -1) {
|
|
cio_puts_at(x, y, str);
|
|
x += len;
|
|
} else {
|
|
cio_puts(str);
|
|
}
|
|
if (extra > 0 && leftadjust) {
|
|
x = mypad(x, y, extra, padchar);
|
|
}
|
|
return x;
|
|
}
|
|
|
|
static void do_printf(int x, int y, char **f)
|
|
{
|
|
char *fmt = *f;
|
|
int *ap;
|
|
char buf[12];
|
|
char ch;
|
|
char *str;
|
|
int leftadjust;
|
|
int width;
|
|
int len;
|
|
int padchar;
|
|
|
|
/*
|
|
** Get characters from the format string and process them
|
|
*/
|
|
|
|
ap = (int *)(f + 1);
|
|
|
|
while ((ch = *fmt++) != '\0') {
|
|
/*
|
|
** Is it the start of a format code?
|
|
*/
|
|
|
|
if (ch == '%') {
|
|
/*
|
|
** Yes, get the padding and width options (if there).
|
|
** Alignment must come at the beginning, then fill,
|
|
** then width.
|
|
*/
|
|
|
|
leftadjust = 0;
|
|
padchar = ' ';
|
|
width = 0;
|
|
|
|
ch = *fmt++;
|
|
|
|
if (ch == '-') {
|
|
leftadjust = 1;
|
|
ch = *fmt++;
|
|
}
|
|
|
|
if (ch == '0') {
|
|
padchar = '0';
|
|
ch = *fmt++;
|
|
}
|
|
|
|
while (ch >= '0' && ch <= '9') {
|
|
width *= 10;
|
|
width += ch - '0';
|
|
ch = *fmt++;
|
|
}
|
|
|
|
/*
|
|
** What data type do we have?
|
|
*/
|
|
switch (ch) {
|
|
case 'c':
|
|
// ch = *( (int *)ap )++;
|
|
ch = *ap++;
|
|
buf[0] = ch;
|
|
buf[1] = '\0';
|
|
x = mypadstr(x, y, buf, 1, width, leftadjust, padchar);
|
|
break;
|
|
|
|
case 'd':
|
|
// len = cvtdec( buf, *( (int *)ap )++ );
|
|
len = cvtdec(buf, *ap++);
|
|
x = mypadstr(x, y, buf, len, width, leftadjust, padchar);
|
|
break;
|
|
|
|
case 's':
|
|
// str = *( (char **)ap )++;
|
|
str = (char *)(*ap++);
|
|
x = mypadstr(x, y, str, -1, width, leftadjust, padchar);
|
|
break;
|
|
|
|
case 'x':
|
|
// len = cvthex( buf, *( (int *)ap )++ );
|
|
len = cvthex(buf, *ap++);
|
|
x = mypadstr(x, y, buf, len, width, leftadjust, padchar);
|
|
break;
|
|
|
|
case 'o':
|
|
// len = cvtoct( buf, *( (int *)ap )++ );
|
|
len = cvtoct(buf, *ap++);
|
|
x = mypadstr(x, y, buf, len, width, leftadjust, padchar);
|
|
break;
|
|
|
|
case 'u':
|
|
len = cvtuns(buf, *ap++);
|
|
x = mypadstr(x, y, buf, len, width, leftadjust, padchar);
|
|
break;
|
|
}
|
|
} else {
|
|
/*
|
|
** No - just print it normally.
|
|
*/
|
|
|
|
if (x != -1 || y != -1) {
|
|
cio_putchar_at(x, y, ch);
|
|
switch (ch) {
|
|
case '\n':
|
|
y += 1;
|
|
/* FALL THRU */
|
|
|
|
case '\r':
|
|
x = scroll_min_x;
|
|
break;
|
|
|
|
default:
|
|
x += 1;
|
|
}
|
|
} else {
|
|
cio_putchar(ch);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cio_printf_at(unsigned int x, unsigned int y, char *fmt, ...)
|
|
{
|
|
do_printf(x, y, &fmt);
|
|
}
|
|
|
|
void cio_printf(char *fmt, ...)
|
|
{
|
|
do_printf(-1, -1, &fmt);
|
|
}
|
|
|
|
/*
|
|
** These are the "standard" IBM AT "Set 1" keycodes.
|
|
*/
|
|
|
|
static unsigned char scan_code[2][128] = { { // unshifted characters
|
|
/* 00-07 */ '\377',
|
|
'\033',
|
|
'1',
|
|
'2',
|
|
'3',
|
|
'4',
|
|
'5',
|
|
'6',
|
|
/* 08-0f */ '7',
|
|
'8',
|
|
'9',
|
|
'0',
|
|
'-',
|
|
'=',
|
|
'\b',
|
|
'\t',
|
|
/* 10-17 */ 'q',
|
|
'w',
|
|
'e',
|
|
'r',
|
|
't',
|
|
'y',
|
|
'u',
|
|
'i',
|
|
/* 18-1f */ 'o',
|
|
'p',
|
|
'[',
|
|
']',
|
|
'\n',
|
|
'\377',
|
|
'a',
|
|
's',
|
|
/* 20-27 */ 'd',
|
|
'f',
|
|
'g',
|
|
'h',
|
|
'j',
|
|
'k',
|
|
'l',
|
|
';',
|
|
/* 28-2f */ '\'',
|
|
'`',
|
|
'\377',
|
|
'\\',
|
|
'z',
|
|
'x',
|
|
'c',
|
|
'v',
|
|
/* 30-37 */ 'b',
|
|
'n',
|
|
'm',
|
|
',',
|
|
'.',
|
|
'/',
|
|
'\377',
|
|
'*',
|
|
/* 38-3f */ '\377',
|
|
' ',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
/* 40-47 */ '\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'7',
|
|
/* 48-4f */ '8',
|
|
'9',
|
|
'-',
|
|
'4',
|
|
'5',
|
|
'6',
|
|
'+',
|
|
'1',
|
|
/* 50-57 */ '2',
|
|
'3',
|
|
'0',
|
|
'.',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
/* 58-5f */ '\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
/* 60-67 */ '\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
/* 68-6f */ '\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
/* 70-77 */ '\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
/* 78-7f */ '\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377' },
|
|
|
|
{ // shifted characters
|
|
/* 00-07 */ '\377',
|
|
'\033',
|
|
'!',
|
|
'@',
|
|
'#',
|
|
'$',
|
|
'%',
|
|
'^',
|
|
/* 08-0f */ '&',
|
|
'*',
|
|
'(',
|
|
')',
|
|
'_',
|
|
'+',
|
|
'\b',
|
|
'\t',
|
|
/* 10-17 */ 'Q',
|
|
'W',
|
|
'E',
|
|
'R',
|
|
'T',
|
|
'Y',
|
|
'U',
|
|
'I',
|
|
/* 18-1f */ 'O',
|
|
'P',
|
|
'{',
|
|
'}',
|
|
'\n',
|
|
'\377',
|
|
'A',
|
|
'S',
|
|
/* 20-27 */ 'D',
|
|
'F',
|
|
'G',
|
|
'H',
|
|
'J',
|
|
'K',
|
|
'L',
|
|
':',
|
|
/* 28-2f */ '"',
|
|
'~',
|
|
'\377',
|
|
'|',
|
|
'Z',
|
|
'X',
|
|
'C',
|
|
'V',
|
|
/* 30-37 */ 'B',
|
|
'N',
|
|
'M',
|
|
'<',
|
|
'>',
|
|
'?',
|
|
'\377',
|
|
'*',
|
|
/* 38-3f */ '\377',
|
|
' ',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
/* 40-47 */ '\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'7',
|
|
/* 48-4f */ '8',
|
|
'9',
|
|
'-',
|
|
'4',
|
|
'5',
|
|
'6',
|
|
'+',
|
|
'1',
|
|
/* 50-57 */ '2',
|
|
'3',
|
|
'0',
|
|
'.',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
/* 58-5f */ '\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
/* 60-67 */ '\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
/* 68-6f */ '\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
/* 70-77 */ '\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
/* 78-7f */ '\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377',
|
|
'\377' } };
|
|
|
|
/*
|
|
** Scan code masks
|
|
*/
|
|
|
|
// 'release' bit
|
|
#define REL_BIT 0x80
|
|
#define CODE_BITS 0x7f
|
|
|
|
#define IS_PRESS(c) (((c) & REL_BIT) == 0)
|
|
#define IS_RELEASE(c) (((c) & REL_BIT) != 0)
|
|
|
|
/*
|
|
** Scan codes for some special characters
|
|
*/
|
|
|
|
// escape code - followed by another code byte
|
|
#define SCAN_ESC 0xe0
|
|
|
|
// shift keys: press, release
|
|
#define L_SHIFT_DN 0x2a
|
|
#define R_SHIFT_DN 0x36
|
|
#define L_SHIFT_UP 0xaa
|
|
#define R_SHIFT_UP 0xb6
|
|
|
|
// control keys
|
|
#define L_CTRL_DN 0x1d
|
|
#define L_CTRL_UP 0x9d
|
|
|
|
/*
|
|
** I/O communication constants
|
|
*/
|
|
#define KBD_DATA 0x60
|
|
#define KBD_STATUS 0x64
|
|
#define READY 0x1
|
|
|
|
/*
|
|
** Circular buffer for input characters. Characters are inserted at
|
|
** next_space, and are removed at next_char. Buffer is empty if
|
|
** these are equal.
|
|
*/
|
|
#define C_BUFSIZE 200
|
|
|
|
static char input_buffer[C_BUFSIZE];
|
|
static volatile char *next_char = input_buffer;
|
|
static volatile char *next_space = input_buffer;
|
|
|
|
static volatile char *increment(volatile char *pointer)
|
|
{
|
|
if (++pointer >= input_buffer + C_BUFSIZE) {
|
|
pointer = input_buffer;
|
|
}
|
|
return pointer;
|
|
}
|
|
|
|
static int input_scan_code(int code)
|
|
{
|
|
static int shift = 0;
|
|
static int ctrl_mask = BMASK8;
|
|
int rval = -1;
|
|
|
|
/*
|
|
** Do the shift processing
|
|
*/
|
|
code &= BMASK8;
|
|
switch (code) {
|
|
case L_SHIFT_DN:
|
|
case R_SHIFT_DN:
|
|
shift = 1;
|
|
break;
|
|
|
|
case L_SHIFT_UP:
|
|
case R_SHIFT_UP:
|
|
shift = 0;
|
|
break;
|
|
|
|
case L_CTRL_DN:
|
|
ctrl_mask = BMASK5;
|
|
break;
|
|
|
|
case L_CTRL_UP:
|
|
ctrl_mask = BMASK8;
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
** Process ordinary characters only on the press (to handle
|
|
** autorepeat). Ignore undefined scan codes.
|
|
*/
|
|
if (IS_PRESS(code)) {
|
|
code = scan_code[shift][(int)code];
|
|
if (code != '\377') {
|
|
volatile char *next = increment(next_space);
|
|
|
|
/*
|
|
** Store character only if there's room
|
|
*/
|
|
rval = code & ctrl_mask;
|
|
if (next != next_char) {
|
|
*next_space = code & ctrl_mask;
|
|
next_space = next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (rval);
|
|
}
|
|
|
|
static void keyboard_isr(int vector, int code)
|
|
{
|
|
int data = inb(KBD_DATA);
|
|
int val = input_scan_code(data);
|
|
|
|
// if there is a notification function, call it
|
|
if (val != -1 && notify)
|
|
notify(val);
|
|
|
|
outb(PIC1_CMD, PIC_EOI);
|
|
}
|
|
|
|
int cio_getchar(void)
|
|
{
|
|
char c;
|
|
int interrupts_enabled = r_eflags() & EFL_IF;
|
|
|
|
while (next_char == next_space) {
|
|
if (!interrupts_enabled) {
|
|
/*
|
|
** Must read the next keystroke ourselves.
|
|
*/
|
|
while ((inb(KBD_STATUS) & READY) == 0) {
|
|
;
|
|
}
|
|
(void)input_scan_code(inb(KBD_DATA));
|
|
}
|
|
}
|
|
|
|
c = *next_char & BMASK8;
|
|
next_char = increment(next_char);
|
|
if (c != EOT) {
|
|
cio_putchar(c);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
int cio_gets(char *buffer, unsigned int size)
|
|
{
|
|
char ch;
|
|
int count = 0;
|
|
|
|
while (size > 1) {
|
|
ch = cio_getchar();
|
|
if (ch == EOT) {
|
|
break;
|
|
}
|
|
*buffer++ = ch;
|
|
count += 1;
|
|
size -= 1;
|
|
if (ch == '\n') {
|
|
break;
|
|
}
|
|
}
|
|
*buffer = '\0';
|
|
return count;
|
|
}
|
|
|
|
int cio_input_queue(void)
|
|
{
|
|
int n_chars = next_space - next_char;
|
|
|
|
if (n_chars < 0) {
|
|
n_chars += C_BUFSIZE;
|
|
}
|
|
return n_chars;
|
|
}
|
|
|
|
/*
|
|
** Initialization routines
|
|
*/
|
|
void cio_init(void (*fcn)(int))
|
|
{
|
|
/*
|
|
** Screen dimensions
|
|
*/
|
|
min_x = SCREEN_MIN_X;
|
|
min_y = SCREEN_MIN_Y;
|
|
max_x = SCREEN_MAX_X;
|
|
max_y = SCREEN_MAX_Y;
|
|
|
|
/*
|
|
** Scrolling region
|
|
*/
|
|
scroll_min_x = SCREEN_MIN_X;
|
|
scroll_min_y = SCREEN_MIN_Y;
|
|
scroll_max_x = SCREEN_MAX_X;
|
|
scroll_max_y = SCREEN_MAX_Y;
|
|
|
|
/*
|
|
** Initial cursor location
|
|
*/
|
|
curr_y = min_y;
|
|
curr_x = min_x;
|
|
setcursor();
|
|
|
|
/*
|
|
** Notification function (or NULL)
|
|
*/
|
|
notify = fcn;
|
|
|
|
/*
|
|
** Set up the interrupt handler for the keyboard
|
|
*/
|
|
install_isr(VEC_KBD, keyboard_isr);
|
|
}
|
|
|
|
#ifdef SA_DEBUG
|
|
int main()
|
|
{
|
|
cio_printf("%d\n", 123);
|
|
cio_printf("%d\n", -123);
|
|
cio_printf("%d\n", 0x7fffffff);
|
|
cio_printf("%d\n", 0x80000001);
|
|
cio_printf("%d\n", 0x80000000);
|
|
cio_printf("x%14dy\n", 0x80000000);
|
|
cio_printf("x%-14dy\n", 0x80000000);
|
|
cio_printf("x%014dy\n", 0x80000000);
|
|
cio_printf("x%-014dy\n", 0x80000000);
|
|
cio_printf("%s\n", "xyz");
|
|
cio_printf("|%10s|\n", "xyz");
|
|
cio_printf("|%-10s|\n", "xyz");
|
|
cio_printf("%c\n", 'x');
|
|
cio_printf("|%4c|\n", 'y');
|
|
cio_printf("|%-4c|\n", 'y');
|
|
cio_printf("|%04c|\n", 'y');
|
|
cio_printf("|%-04c|\n", 'y');
|
|
cio_printf("|%3d|\n", 5);
|
|
cio_printf("|%3d|\n", 54321);
|
|
cio_printf("%x\n", 0x123abc);
|
|
cio_printf("|%04x|\n", 20);
|
|
cio_printf("|%012x|\n", 0xfedcba98);
|
|
cio_printf("|%-012x|\n", 0x76543210);
|
|
}
|
|
|
|
int curr_x, curr_y, max_x, max_y;
|
|
#endif
|