summaryrefslogtreecommitdiff
path: root/kernel/old/cio.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/old/cio.c')
-rw-r--r--kernel/old/cio.c1028
1 files changed, 1028 insertions, 0 deletions
diff --git a/kernel/old/cio.c b/kernel/old/cio.c
new file mode 100644
index 0000000..deb6b76
--- /dev/null
+++ b/kernel/old/cio.c
@@ -0,0 +1,1028 @@
+/*
+** 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