summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--src/Kconfig5
-rw-r--r--src/clock.c1
-rw-r--r--src/config.h1
-rw-r--r--src/misc.c8
-rw-r--r--src/optionroms.c9
-rw-r--r--src/post.c2
-rw-r--r--src/romlayout.S50
-rw-r--r--src/sercon.c673
-rw-r--r--src/util.h4
10 files changed, 739 insertions, 18 deletions
diff --git a/Makefile b/Makefile
index a0158ed..eb8ad58 100644
--- a/Makefile
+++ b/Makefile
@@ -29,8 +29,8 @@ LD32BIT_FLAG:=-melf_i386
# Source files
SRCBOTH=misc.c stacks.c output.c string.c block.c cdrom.c disk.c mouse.c kbd.c \
- system.c serial.c clock.c resume.c pnpbios.c vgahooks.c pcibios.c apm.c \
- cp437.c \
+ system.c serial.c sercon.c clock.c resume.c pnpbios.c vgahooks.c pcibios.c \
+ apm.c cp437.c \
hw/pci.c hw/timer.c hw/rtc.c hw/dma.c hw/pic.c hw/ps2port.c hw/serialio.c \
hw/usb.c hw/usb-uhci.c hw/usb-ohci.c hw/usb-ehci.c \
hw/usb-hid.c hw/usb-msc.c hw/usb-uas.c \
diff --git a/src/Kconfig b/src/Kconfig
index 77ec9c7..55a87cb 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -306,6 +306,11 @@ menu "Hardware support"
default y
help
Support serial ports. This also enables int 14 serial port calls.
+ config SERCON
+ bool "Serial console"
+ default y
+ help
+ Support redirecting vga output to the serial console.
config LPT
bool "Parallel port"
default y
diff --git a/src/clock.c b/src/clock.c
index e83e0f3..e44e112 100644
--- a/src/clock.c
+++ b/src/clock.c
@@ -295,6 +295,7 @@ clock_update(void)
floppy_tick();
usb_check_event();
ps2_check_event();
+ sercon_check_event();
}
// INT 08h System Timer ISR Entry Point
diff --git a/src/config.h b/src/config.h
index baca029..93c8dbc 100644
--- a/src/config.h
+++ b/src/config.h
@@ -76,7 +76,6 @@
#define DEBUG_ISR_08 20
#define DEBUG_ISR_09 9
#define DEBUG_ISR_0e 9
-#define DEBUG_HDL_10 20
#define DEBUG_HDL_11 2
#define DEBUG_HDL_12 2
#define DEBUG_HDL_13 10
diff --git a/src/misc.c b/src/misc.c
index f02237c..b511730 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -51,14 +51,6 @@ handle_05(struct bregs *regs)
debug_enter(regs, DEBUG_HDL_05);
}
-// INT 10h Video Support Service Entry Point
-void VISIBLE16
-handle_10(struct bregs *regs)
-{
- debug_enter(regs, DEBUG_HDL_10);
- // don't do anything, since the VGA BIOS handles int10h requests
-}
-
// NMI handler
void VISIBLE16
handle_02(void)
diff --git a/src/optionroms.c b/src/optionroms.c
index 65f7fe0..092393a 100644
--- a/src/optionroms.c
+++ b/src/optionroms.c
@@ -432,12 +432,9 @@ vgarom_setup(void)
run_file_roms("vgaroms/", 1, NULL);
rom_reserve(0);
- if (rom_get_last() == BUILD_ROM_START)
- // No VGA rom found
- return;
-
- VgaROM = (void*)BUILD_ROM_START;
- enable_vga_console();
+ if (rom_get_last() != BUILD_ROM_START)
+ // VGA rom found
+ VgaROM = (void*)BUILD_ROM_START;
}
void
diff --git a/src/post.c b/src/post.c
index 2a06d44..f93106a 100644
--- a/src/post.c
+++ b/src/post.c
@@ -208,6 +208,8 @@ maininit(void)
// Run vga option rom
vgarom_setup();
+ sercon_setup();
+ enable_vga_console();
// Do hardware initialization (if running synchronously)
if (!threads_during_optionroms()) {
diff --git a/src/romlayout.S b/src/romlayout.S
index 89b3784..c4a4635 100644
--- a/src/romlayout.S
+++ b/src/romlayout.S
@@ -414,6 +414,53 @@ __csm_return:
popfw
lretw
+// Serial console "hooked vga" entry point
+ DECLFUNC entry_sercon
+entry_sercon:
+ // Setup for chain loading to real vga handler
+ pushfw
+ pushl %cs:sercon_real_vga_handler
+
+ // Set %ds to varlow segment
+ cli
+ cld
+ pushw %ds
+ pushl %eax
+ movl $_zonelow_seg, %eax
+ movl %eax, %ds
+
+ // Test if the sercon handler can be called
+ movl %esp, %eax // Test for broken x86emu
+ pushl $1f
+ retl
+1: cmpl %esp, %eax
+ jne 4f
+ cmpb $0, sercon_enable // Test that sercon is enabled
+ je 3f
+
+ // call handle_sercon
+ popl %eax
+ popw %ds
+2: pushl $handle_sercon
+#if CONFIG_ENTRY_EXTRASTACK
+ jmp irqentry_arg_extrastack
+#else
+ jmp irqentry_arg
+#endif
+
+ // sercon disabled - check for legacy text modeset and otherwise exit
+3: popl %eax
+ popw %ds
+ cmpw $0x0007, %ax
+ jle 2b
+ iretw
+
+ // Running on broken x86emu - restore stack and exit
+4: movl %eax, %esp
+ popl %eax
+ popw %ds
+ iretw
+
/****************************************************************
* Interrupt entry points
@@ -597,7 +644,8 @@ entry_10_0x0f:
iretw
ORG 0xf065
- IRQ_ENTRY_ARG 10
+entry_10:
+ iretw
// 0xf0a4 - VideoParams in misc.c
diff --git a/src/sercon.c b/src/sercon.c
new file mode 100644
index 0000000..5d27051
--- /dev/null
+++ b/src/sercon.c
@@ -0,0 +1,673 @@
+// serial console support
+//
+// Copyright (C) 2016 Gerd Hoffmann <kraxel@redhat.com>
+//
+// This file may be distributed under the terms of the GNU LGPLv3 license.
+
+#include "biosvar.h" // SET_BDA
+#include "bregs.h" // struct bregs
+#include "stacks.h" // yield
+#include "output.h" // dprintf
+#include "util.h" // irqtimer_calc_ticks
+#include "string.h" // memcpy
+#include "romfile.h" // romfile_loadint
+#include "hw/serialio.h" // SEROFF_IER
+#include "cp437.h"
+
+static u8 video_rows(void)
+{
+ return GET_BDA(video_rows)+1;
+}
+
+static u8 video_cols(void)
+{
+ return GET_BDA(video_cols);
+}
+
+static u8 cursor_pos_col(void)
+{
+ u16 pos = GET_BDA(cursor_pos[0]);
+ return pos & 0xff;
+}
+
+static u8 cursor_pos_row(void)
+{
+ u16 pos = GET_BDA(cursor_pos[0]);
+ return (pos >> 8) & 0xff;
+}
+
+static void cursor_pos_set(u8 row, u8 col)
+{
+ u16 pos = ((u16)row << 8) | col;
+ SET_BDA(cursor_pos[0], pos);
+}
+
+/****************************************************************
+ * serial console output
+ ****************************************************************/
+
+VARLOW u16 sercon_port;
+VARLOW u8 sercon_split;
+VARLOW u8 sercon_enable;
+VARFSEG struct segoff_s sercon_real_vga_handler;
+
+/*
+ * We have a small output buffer here, for lazy output. That allows
+ * to avoid a whole bunch of control sequences for pointless cursor
+ * moves, so when logging the output it'll be *alot* less cluttered.
+ *
+ * sercon_char/attr is the actual output buffer.
+ * sercon_attr_last is the most recent attribute sent to the terminal.
+ * sercon_col_last is the most recent column sent to the terminal.
+ * sercon_row_last is the most recent row sent to the terminal.
+ */
+VARLOW u8 sercon_attr_last;
+VARLOW u8 sercon_col_last;
+VARLOW u8 sercon_row_last;
+VARLOW u8 sercon_char;
+VARLOW u8 sercon_attr = 0x07;
+
+static VAR16 u8 sercon_cmap[8] = { '0', '4', '2', '6', '1', '5', '3', '7' };
+
+static int sercon_splitmode(void)
+{
+ return GET_LOW(sercon_split);
+}
+
+static void sercon_putchar(u8 chr)
+{
+ u16 addr = GET_LOW(sercon_port);
+ u32 end = irqtimer_calc_ticks(0x0a);
+
+#if 0
+ /* for visual control sequence debugging */
+ if (chr == '\x1b')
+ chr = '*';
+#endif
+
+ for (;;) {
+ u8 lsr = inb(addr+SEROFF_LSR);
+ if ((lsr & 0x60) == 0x60) {
+ // Success - can write data
+ outb(chr, addr+SEROFF_DATA);
+ break;
+ }
+ if (irqtimer_check(end)) {
+ break;
+ }
+ yield();
+ }
+}
+
+static void sercon_term_reset(void)
+{
+ sercon_putchar('\x1b');
+ sercon_putchar('c');
+}
+
+static void sercon_term_clear_screen(void)
+{
+ sercon_putchar('\x1b');
+ sercon_putchar('[');
+ sercon_putchar('2');
+ sercon_putchar('J');
+}
+
+static void sercon_term_no_linewrap(void)
+{
+ sercon_putchar('\x1b');
+ sercon_putchar('[');
+ sercon_putchar('?');
+ sercon_putchar('7');
+ sercon_putchar('l');
+}
+
+static void sercon_term_cursor_goto(u8 row, u8 col)
+{
+ row++; col++;
+ sercon_putchar('\x1b');
+ sercon_putchar('[');
+ sercon_putchar('0' + row / 10);
+ sercon_putchar('0' + row % 10);
+ sercon_putchar(';');
+ sercon_putchar('0' + col / 10);
+ sercon_putchar('0' + col % 10);
+ sercon_putchar('H');
+}
+
+static void sercon_term_set_color(u8 fg, u8 bg, u8 bold)
+{
+ sercon_putchar('\x1b');
+ sercon_putchar('[');
+ sercon_putchar('0');
+ if (fg != 7) {
+ sercon_putchar(';');
+ sercon_putchar('3');
+ sercon_putchar(GET_GLOBAL(sercon_cmap[fg & 7]));
+ }
+ if (bg != 0) {
+ sercon_putchar(';');
+ sercon_putchar('4');
+ sercon_putchar(GET_GLOBAL(sercon_cmap[bg & 7]));
+ }
+ if (bold) {
+ sercon_putchar(';');
+ sercon_putchar('1');
+ }
+ sercon_putchar('m');
+}
+
+static void sercon_set_attr(u8 attr)
+{
+ if (attr == GET_LOW(sercon_attr_last))
+ return;
+
+ SET_LOW(sercon_attr_last, attr);
+ sercon_term_set_color((attr >> 0) & 7,
+ (attr >> 4) & 7,
+ attr & 0x08);
+}
+
+static void sercon_print_utf8(u8 chr)
+{
+ u16 unicode = cp437_to_unicode(chr);
+
+ if (unicode < 0x7f) {
+ sercon_putchar(unicode);
+ } else if (unicode < 0x7ff) {
+ sercon_putchar(0xc0 | ((unicode >> 6) & 0x1f));
+ sercon_putchar(0x80 | ((unicode >> 0) & 0x3f));
+ } else {
+ sercon_putchar(0xe0 | ((unicode >> 12) & 0x0f));
+ sercon_putchar(0x80 | ((unicode >> 6) & 0x3f));
+ sercon_putchar(0x80 | ((unicode >> 0) & 0x3f));
+ }
+}
+
+static void sercon_cursor_pos_set(u8 row, u8 col)
+{
+ if (!sercon_splitmode()) {
+ cursor_pos_set(row, col);
+ } else {
+ /* let vgabios update cursor */
+ }
+}
+
+static void sercon_lazy_cursor_sync(void)
+{
+ u8 row = cursor_pos_row();
+ u8 col = cursor_pos_col();
+
+ if (GET_LOW(sercon_row_last) == row &&
+ GET_LOW(sercon_col_last) == col)
+ return;
+
+ if (col == 0 && GET_LOW(sercon_row_last) <= row) {
+ if (GET_LOW(sercon_col_last) != 0) {
+ sercon_putchar('\r');
+ SET_LOW(sercon_col_last, 0);
+ }
+ while (GET_LOW(sercon_row_last) < row) {
+ sercon_putchar('\n');
+ SET_LOW(sercon_row_last, GET_LOW(sercon_row_last)+1);
+ }
+ if (GET_LOW(sercon_row_last) == row &&
+ GET_LOW(sercon_col_last) == col)
+ return;
+ }
+
+ sercon_term_cursor_goto(row, col);
+ SET_LOW(sercon_row_last, row);
+ SET_LOW(sercon_col_last, col);
+}
+
+static void sercon_lazy_flush(void)
+{
+ u8 chr, attr;
+
+ chr = GET_LOW(sercon_char);
+ attr = GET_LOW(sercon_attr);
+ if (chr) {
+ sercon_set_attr(attr);
+ sercon_print_utf8(chr);
+ SET_LOW(sercon_col_last, GET_LOW(sercon_col_last) + 1);
+ }
+
+ sercon_lazy_cursor_sync();
+
+ SET_LOW(sercon_attr, 0x07);
+ SET_LOW(sercon_char, 0x00);
+}
+
+static void sercon_lazy_cursor_update(u8 row, u8 col)
+{
+ sercon_cursor_pos_set(row, col);
+ SET_LOW(sercon_row_last, row);
+ SET_LOW(sercon_col_last, col);
+}
+
+static void sercon_lazy_backspace(void)
+{
+ u8 col;
+
+ sercon_lazy_flush();
+ col = cursor_pos_col();
+ if (col > 0) {
+ sercon_putchar(8);
+ sercon_lazy_cursor_update(cursor_pos_row(), col-1);
+ }
+}
+
+static void sercon_lazy_cr(void)
+{
+ sercon_cursor_pos_set(cursor_pos_row(), 0);
+}
+
+static void sercon_lazy_lf(void)
+{
+ u8 row;
+
+ row = cursor_pos_row() + 1;
+ if (row >= video_rows()) {
+ /* scrolling up */
+ row = video_rows()-1;
+ if (GET_LOW(sercon_row_last) > 0) {
+ SET_LOW(sercon_row_last, GET_LOW(sercon_row_last) - 1);
+ }
+ }
+ sercon_cursor_pos_set(row, cursor_pos_col());
+}
+
+static void sercon_lazy_move_cursor(void)
+{
+ u8 col;
+
+ col = cursor_pos_col() + 1;
+ if (col >= video_cols()) {
+ sercon_lazy_cr();
+ sercon_lazy_lf();
+ } else {
+ sercon_cursor_pos_set(cursor_pos_row(), col);
+ }
+}
+
+static void sercon_lazy_putchar(u8 chr, u8 attr, u8 teletype)
+{
+ if (cursor_pos_row() != GET_LOW(sercon_row_last) ||
+ cursor_pos_col() != GET_LOW(sercon_col_last)) {
+ sercon_lazy_flush();
+ }
+
+ SET_LOW(sercon_char, chr);
+ if (teletype)
+ sercon_lazy_move_cursor();
+ else
+ SET_LOW(sercon_attr, attr);
+}
+
+/* Set video mode */
+static void sercon_1000(struct bregs *regs)
+{
+ u8 clearscreen = !(regs->al & 0x80);
+ u8 mode = regs->al & 0x7f;
+ u8 rows, cols;
+
+ if (!sercon_splitmode()) {
+ switch (mode) {
+ case 0x00:
+ case 0x01:
+ case 0x04: /* 320x200 */
+ case 0x05: /* 320x200 */
+ cols = 40;
+ rows = 25;
+ regs->al = 0x30;
+ break;
+ case 0x02:
+ case 0x03:
+ case 0x06: /* 640x200 */
+ case 0x07:
+ default:
+ cols = 80;
+ rows = 25;
+ regs->al = 0x30;
+ break;
+ }
+ cursor_pos_set(0, 0);
+ SET_BDA(video_mode, mode);
+ SET_BDA(video_cols, cols);
+ SET_BDA(video_rows, rows-1);
+ SET_BDA(cursor_type, 0x0007);
+ } else {
+ /* let vgabios handle mode init */
+ }
+
+ SET_LOW(sercon_enable, mode <= 0x07);
+ SET_LOW(sercon_col_last, 0);
+ SET_LOW(sercon_row_last, 0);
+ SET_LOW(sercon_attr_last, 0);
+
+ sercon_term_reset();
+ sercon_term_no_linewrap();
+ if (clearscreen)
+ sercon_term_clear_screen();
+}
+
+/* Set text-mode cursor shape */
+static void sercon_1001(struct bregs *regs)
+{
+ /* show/hide cursor? */
+ SET_BDA(cursor_type, regs->cx);
+}
+
+/* Set cursor position */
+static void sercon_1002(struct bregs *regs)
+{
+ sercon_cursor_pos_set(regs->dh, regs->dl);
+}
+
+/* Get cursor position */
+static void sercon_1003(struct bregs *regs)
+{
+ regs->cx = GET_BDA(cursor_type);
+ regs->dh = cursor_pos_row();
+ regs->dl = cursor_pos_col();
+}
+
+/* Scroll up window */
+static void sercon_1006(struct bregs *regs)
+{
+ sercon_lazy_flush();
+ if (regs->al == 0) {
+ /* clear rect, do only in case this looks like a fullscreen clear */
+ if (regs->ch == 0 &&
+ regs->cl == 0 &&
+ regs->dh == video_rows()-1 &&
+ regs->dl == video_cols()-1) {
+ sercon_set_attr(regs->bh);
+ sercon_term_clear_screen();
+ }
+ } else {
+ sercon_putchar('\r');
+ sercon_putchar('\n');
+ }
+}
+
+/* Read character and attribute at cursor position */
+static void sercon_1008(struct bregs *regs)
+{
+ regs->ah = 0x07;
+ regs->bh = ' ';
+}
+
+/* Write character and attribute at cursor position */
+static void sercon_1009(struct bregs *regs)
+{
+ u16 count = regs->cx;
+
+ if (count == 1) {
+ sercon_lazy_putchar(regs->al, regs->bl, 0);
+
+ } else if (regs->al == 0x20 &&
+ video_rows() * video_cols() == count &&
+ cursor_pos_row() == 0 &&
+ cursor_pos_col() == 0) {
+ /* override everything with spaces -> this is clear screen */
+ sercon_lazy_flush();
+ sercon_set_attr(regs->bl);
+ sercon_term_clear_screen();
+
+ } else {
+ sercon_lazy_flush();
+ sercon_set_attr(regs->bl);
+ while (count) {
+ sercon_print_utf8(regs->al);
+ count--;
+ }
+ sercon_term_cursor_goto(cursor_pos_row(),
+ cursor_pos_col());
+ }
+}
+
+/* Teletype output */
+static void sercon_100e(struct bregs *regs)
+{
+ switch (regs->al) {
+ case 7:
+ sercon_putchar(0x07);
+ break;
+ case 8:
+ sercon_lazy_backspace();
+ break;
+ case '\r':
+ sercon_lazy_cr();
+ break;
+ case '\n':
+ sercon_lazy_lf();
+ break;
+ default:
+ sercon_lazy_putchar(regs->al, 0, 1);
+ break;
+ }
+}
+
+/* Get current video mode */
+static void sercon_100f(struct bregs *regs)
+{
+ regs->al = GET_BDA(video_mode);
+ regs->ah = GET_BDA(video_cols);
+}
+
+/* VBE 2.0 */
+static void sercon_104f(struct bregs *regs)
+{
+ if (!sercon_splitmode()) {
+ regs->ax = 0x0100;
+ } else {
+ // Disable sercon entry point on any vesa modeset
+ if (regs->al == 0x00)
+ SET_LOW(sercon_enable, 0);
+ }
+}
+
+static void sercon_10XX(struct bregs *regs)
+{
+ warn_unimplemented(regs);
+}
+
+void VISIBLE16
+handle_sercon(struct bregs *regs)
+{
+ if (!CONFIG_SERCON)
+ return;
+ if (!GET_LOW(sercon_port))
+ return;
+
+ switch (regs->ah) {
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x08:
+ case 0x0f:
+ if (sercon_splitmode())
+ /* nothing, vgabios handles it */
+ return;
+ }
+
+ switch (regs->ah) {
+ case 0x00: sercon_1000(regs); break;
+ case 0x01: sercon_1001(regs); break;
+ case 0x02: sercon_1002(regs); break;
+ case 0x03: sercon_1003(regs); break;
+ case 0x06: sercon_1006(regs); break;
+ case 0x08: sercon_1008(regs); break;
+ case 0x09: sercon_1009(regs); break;
+ case 0x0e: sercon_100e(regs); break;
+ case 0x0f: sercon_100f(regs); break;
+ case 0x4f: sercon_104f(regs); break;
+ default: sercon_10XX(regs); break;
+ }
+}
+
+void sercon_setup(void)
+{
+ if (!CONFIG_SERCON)
+ return;
+
+ struct segoff_s seabios, vgabios;
+ u16 addr;
+
+ addr = romfile_loadint("etc/sercon-port", 0);
+ if (!addr)
+ return;
+ dprintf(1, "sercon: using ioport 0x%x\n", addr);
+
+ vgabios = GET_IVT(0x10);
+ seabios = FUNC16(entry_10);
+ if (vgabios.seg != seabios.seg ||
+ vgabios.offset != seabios.offset) {
+ dprintf(1, "sercon: configuring in splitmode (vgabios %04x:%04x)\n",
+ vgabios.seg, vgabios.offset);
+ sercon_real_vga_handler = vgabios;
+ SET_LOW(sercon_split, 1);
+ } else {
+ dprintf(1, "sercon: configuring as primary display\n");
+ sercon_real_vga_handler = seabios;
+ }
+
+ SET_IVT(0x10, FUNC16(entry_sercon));
+ SET_LOW(sercon_port, addr);
+ outb(0x03, addr + SEROFF_LCR); // 8N1
+ outb(0x01, addr + 0x02); // enable fifo
+}
+
+/****************************************************************
+ * serial input
+ ****************************************************************/
+
+VARLOW u8 rx_buf[16];
+VARLOW u8 rx_bytes;
+
+static VAR16 struct {
+ char seq[4];
+ u8 len;
+ u16 keycode;
+} termseq[] = {
+ { .seq = "OP", .len = 2, .keycode = 0x3b00 }, // F1
+ { .seq = "OQ", .len = 2, .keycode = 0x3c00 }, // F2
+ { .seq = "OR", .len = 2, .keycode = 0x3d00 }, // F3
+ { .seq = "OS", .len = 2, .keycode = 0x3e00 }, // F4
+
+ { .seq = "[15~", .len = 4, .keycode = 0x3f00 }, // F5
+ { .seq = "[17~", .len = 4, .keycode = 0x4000 }, // F6
+ { .seq = "[18~", .len = 4, .keycode = 0x4100 }, // F7
+ { .seq = "[19~", .len = 4, .keycode = 0x4200 }, // F8
+ { .seq = "[20~", .len = 4, .keycode = 0x4300 }, // F9
+ { .seq = "[21~", .len = 4, .keycode = 0x4400 }, // F10
+ { .seq = "[23~", .len = 4, .keycode = 0x5700 }, // F11
+ { .seq = "[24~", .len = 4, .keycode = 0x5800 }, // F12
+
+ { .seq = "[2~", .len = 3, .keycode = 0x52e0 }, // insert
+ { .seq = "[3~", .len = 3, .keycode = 0x53e0 }, // delete
+ { .seq = "[5~", .len = 3, .keycode = 0x49e0 }, // page up
+ { .seq = "[6~", .len = 3, .keycode = 0x51e0 }, // page down
+
+ { .seq = "[A", .len = 2, .keycode = 0x48e0 }, // up
+ { .seq = "[B", .len = 2, .keycode = 0x50e0 }, // down
+ { .seq = "[C", .len = 2, .keycode = 0x4de0 }, // right
+ { .seq = "[D", .len = 2, .keycode = 0x4be0 }, // left
+
+ { .seq = "[H", .len = 2, .keycode = 0x47e0 }, // home
+ { .seq = "[F", .len = 2, .keycode = 0x4fe0 }, // end
+};
+
+static void shiftbuf(int remove)
+{
+ int i, remaining;
+
+ remaining = GET_LOW(rx_bytes) - remove;
+ SET_LOW(rx_bytes, remaining);
+ for (i = 0; i < remaining; i++)
+ SET_LOW(rx_buf[i], GET_LOW(rx_buf[i + remove]));
+}
+
+static int cmpbuf(int seq)
+{
+ int chr, len;
+
+ len = GET_GLOBAL(termseq[seq].len);
+ if (GET_LOW(rx_bytes) < len + 1)
+ return 0;
+ for (chr = 0; chr < len; chr++)
+ if (GET_GLOBAL(termseq[seq].seq[chr]) != GET_LOW(rx_buf[chr + 1]))
+ return 0;
+ return 1;
+}
+
+static int findseq(void)
+{
+ int seq;
+
+ for (seq = 0; seq < ARRAY_SIZE(termseq); seq++)
+ if (cmpbuf(seq))
+ return seq;
+ return -1;
+}
+
+void
+sercon_check_event(void)
+{
+ if (!CONFIG_SERCON)
+ return;
+
+ u16 addr = GET_LOW(sercon_port);
+ u16 keycode;
+ u8 byte, count = 0;
+ int seq, chr;
+
+ // check to see if there is a active serial port
+ if (!addr)
+ return;
+ if (inb(addr + SEROFF_LSR) == 0xFF)
+ return;
+
+ // flush pending output
+ sercon_lazy_flush();
+
+ // read all available data
+ while (inb(addr + SEROFF_LSR) & 0x01) {
+ byte = inb(addr + SEROFF_DATA);
+ if (GET_LOW(rx_bytes) < sizeof(rx_buf)) {
+ SET_LOW(rx_buf[rx_bytes], byte);
+ SET_LOW(rx_bytes, GET_LOW(rx_bytes) + 1);
+ count++;
+ }
+ }
+
+ for (;;) {
+ // no (more) input data
+ if (!GET_LOW(rx_bytes))
+ return;
+
+ // lookup escape sequences
+ if (GET_LOW(rx_bytes) > 1 && GET_LOW(rx_buf[0]) == 0x1b) {
+ seq = findseq();
+ if (seq >= 0) {
+ enqueue_key(GET_GLOBAL(termseq[seq].keycode));
+ shiftbuf(GET_GLOBAL(termseq[seq].len) + 1);
+ continue;
+ }
+ }
+
+ // Seems we got a escape sequence we didn't recognise.
+ // -> If we received data wait for more, maybe it is just incomplete.
+ if (GET_LOW(rx_buf[0]) == 0x1b && count)
+ return;
+
+ // Handle input as individual char.
+ chr = GET_LOW(rx_buf[0]);
+ keycode = ascii_to_keycode(chr);
+ if (keycode)
+ enqueue_key(keycode);
+ shiftbuf(1);
+ }
+}
diff --git a/src/util.h b/src/util.h
index da9059a..7a23b51 100644
--- a/src/util.h
+++ b/src/util.h
@@ -231,6 +231,10 @@ void startBoot(void);
void reloc_preinit(void *f, void *arg);
void code_mutable_preinit(void);
+// sercon.c
+void sercon_setup(void);
+void sercon_check_event(void);
+
// serial.c
void serial_setup(void);
void lpt_setup(void);