[SeaBIOS] [RFC PATCH 1/2] serial console, output
Gerd Hoffmann
kraxel at redhat.com
Fri Jul 1 12:54:30 CEST 2016
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
---
src/misc.c | 2 +
src/optionroms.c | 4 +-
src/serial.c | 340 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/util.h | 2 +
4 files changed, 347 insertions(+), 1 deletion(-)
diff --git a/src/misc.c b/src/misc.c
index f02237c..f4b656d 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -11,6 +11,7 @@
#include "output.h" // debug_enter
#include "stacks.h" // call16_int
#include "string.h" // memset
+#include "util.h" // serial_10
#define PORT_MATH_CLEAR 0x00f0
@@ -57,6 +58,7 @@ handle_10(struct bregs *regs)
{
debug_enter(regs, DEBUG_HDL_10);
// don't do anything, since the VGA BIOS handles int10h requests
+ sercon_10(regs);
}
// NMI handler
diff --git a/src/optionroms.c b/src/optionroms.c
index 65f7fe0..e6b308c 100644
--- a/src/optionroms.c
+++ b/src/optionroms.c
@@ -432,9 +432,11 @@ vgarom_setup(void)
run_file_roms("vgaroms/", 1, NULL);
rom_reserve(0);
- if (rom_get_last() == BUILD_ROM_START)
+ if (rom_get_last() == BUILD_ROM_START) {
// No VGA rom found
+ sercon_enable();
return;
+ }
VgaROM = (void*)BUILD_ROM_START;
enable_vga_console();
diff --git a/src/serial.c b/src/serial.c
index 88349c8..74b91bb 100644
--- a/src/serial.c
+++ b/src/serial.c
@@ -315,3 +315,343 @@ handle_17(struct bregs *regs)
default: handle_17XX(regs); break;
}
}
+
+/****************************************************************
+ * serial console output
+ ****************************************************************/
+
+VARLOW u16 sercon_port;
+VARLOW u8 sercon_mode;
+VARLOW u8 sercon_cols;
+VARLOW u8 sercon_rows;
+
+VARLOW u8 sercon_row;
+VARLOW u8 sercon_col;
+
+/*
+ * 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_col_lazy is the column of the terminal's cursor, typically
+ * a few positions left of sercon_col.
+ * sercon_attr_last is the most recent attribute sent to the terminal.
+ */
+VARLOW u8 sercon_attr_last;
+VARLOW u8 sercon_col_lazy;
+VARLOW u8 sercon_char[8];
+VARLOW u8 sercon_attr[8];
+
+VARLOW u8 sercon_cmap[8] = { '0', '4', '2', '6', '1', '5', '3', '7' };
+
+static void sercon_putchar(u8 chr)
+{
+ u16 addr = GET_LOW(sercon_port);
+ u32 end = irqtimer_calc_ticks(0x0a);
+
+ 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_init(void)
+{
+ /* reset */
+ sercon_putchar('\x1b');
+ sercon_putchar('c');
+ /* clear screen */
+ sercon_putchar('\x1b');
+ sercon_putchar('[');
+ sercon_putchar('2');
+ sercon_putchar('J');
+}
+
+static void sercon_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_LOW(sercon_cmap[fg & 7]));
+ }
+ if (bg != 0) {
+ sercon_putchar(';');
+ sercon_putchar('4');
+ sercon_putchar(GET_LOW(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_set_color((attr >> 0) & 7,
+ (attr >> 4) & 7,
+ attr & 0x08);
+}
+
+static void sercon_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_print_char(u8 chr, u8 attr)
+{
+ if (chr != 0x00) {
+ sercon_set_attr(attr);
+ sercon_putchar(chr);
+ } else {
+ /* move cursor right */
+ sercon_putchar('\x1b');
+ sercon_putchar('[');
+ sercon_putchar('C');
+ }
+}
+
+static void sercon_flush_lazy(void)
+{
+ u8 count = GET_LOW(sercon_col) - GET_LOW(sercon_col_lazy);
+ u8 pos = 0;
+
+ if (!count && !GET_LOW(sercon_attr[0]))
+ return;
+
+ while (count) {
+ sercon_print_char(GET_LOW(sercon_char[pos]),
+ GET_LOW(sercon_attr[pos]));
+ count--;
+ pos++;
+ }
+
+ if (pos < ARRAY_SIZE(sercon_char) && GET_LOW(sercon_char[pos])) {
+ sercon_print_char(GET_LOW(sercon_char[pos]),
+ GET_LOW(sercon_attr[pos]));
+ /* move cursor left */
+ sercon_putchar('\x1b');
+ sercon_putchar('[');
+ sercon_putchar('D');
+ }
+
+ SET_LOW(sercon_col_lazy, GET_LOW(sercon_col));
+ for (pos = 0; pos < ARRAY_SIZE(sercon_char); pos++) {
+ SET_LOW(sercon_attr[pos], 0x07);
+ SET_LOW(sercon_char[pos], 0x00);
+ }
+}
+
+/* Set video mode */
+static void sercon_1000(struct bregs *regs)
+{
+ SET_LOW(sercon_mode, regs->al);
+ switch (regs->al) {
+ case 0x03:
+ default:
+ SET_LOW(sercon_cols, 80);
+ SET_LOW(sercon_rows, 25);
+ regs->al = 0x30;
+ }
+ SET_LOW(sercon_row, 0);
+ SET_LOW(sercon_col, 0);
+ SET_LOW(sercon_col_lazy, 0);
+ sercon_init();
+}
+
+/* Set cursor position */
+static void sercon_1002(struct bregs *regs)
+{
+ u8 row = regs->dh;
+ u8 col = regs->dl;
+
+ if (row == GET_LOW(sercon_row) &&
+ col >= GET_LOW(sercon_col_lazy) &&
+ col < GET_LOW(sercon_col_lazy) + ARRAY_SIZE(sercon_char)) {
+ SET_LOW(sercon_col, col);
+ if (col+1 == GET_LOW(sercon_col_lazy) + ARRAY_SIZE(sercon_char))
+ sercon_flush_lazy();
+ } else {
+ sercon_flush_lazy();
+ if (row == GET_LOW(sercon_row) && col == 0) {
+ sercon_putchar('\r');
+ } else {
+ sercon_cursor_goto(row, col);
+ }
+ SET_LOW(sercon_row, row);
+ SET_LOW(sercon_col, col);
+ SET_LOW(sercon_col_lazy, col);
+ }
+}
+
+/* Get cursor position */
+static void sercon_1003(struct bregs *regs)
+{
+ regs->ax = 0;
+ regs->ch = 0;
+ regs->cl = 7;
+ regs->dh = GET_LOW(sercon_row);
+ regs->dl = GET_LOW(sercon_col);
+}
+
+/* Scroll up window */
+static void sercon_1006(struct bregs *regs)
+{
+ sercon_flush_lazy();
+ 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;
+ u8 pos;
+
+ if (count == 1) {
+ pos = GET_LOW(sercon_col) - GET_LOW(sercon_col_lazy);
+ if (pos < ARRAY_SIZE(sercon_char)) {
+ sercon_char[pos] = regs->al;
+ sercon_attr[pos] = regs->bl;
+ }
+ } else if (regs->al == 0x20 &&
+ GET_LOW(sercon_rows) * GET_LOW(sercon_cols) == count &&
+ GET_LOW(sercon_row) == 0 &&
+ GET_LOW(sercon_col) == 0) {
+ /* override everything with spaces -> this is clear screen */
+ sercon_flush_lazy();
+ sercon_putchar('\x1b');
+ sercon_putchar('[');
+ sercon_putchar('2');
+ sercon_putchar('J');
+ } else {
+ sercon_flush_lazy();
+ sercon_set_attr(regs->bl);
+ while (count) {
+ sercon_putchar(regs->al);
+ count--;
+ }
+ sercon_cursor_goto(GET_LOW(sercon_row),
+ GET_LOW(sercon_col));
+ }
+}
+
+/* Teletype output */
+static void sercon_100e(struct bregs *regs)
+{
+ u8 pos, row, col;
+
+ switch (regs->al) {
+ case 7:
+ // beep
+ break;
+ case 8:
+ sercon_flush_lazy();
+ sercon_putchar(regs->al);
+ col = GET_LOW(sercon_col);
+ if (col > 0) {
+ col--;
+ SET_LOW(sercon_col, col);
+ SET_LOW(sercon_col_lazy, col);
+ }
+ break;
+ case '\r':
+ sercon_flush_lazy();
+ sercon_putchar(regs->al);
+ SET_LOW(sercon_col, 0);
+ SET_LOW(sercon_col_lazy, 0);
+ break;
+ case '\n':
+ sercon_flush_lazy();
+ sercon_putchar(regs->al);
+ row = GET_LOW(sercon_row);
+ row++;
+ if (row >= GET_LOW(sercon_rows))
+ row = GET_LOW(sercon_rows)-1;
+ SET_LOW(sercon_row, row);
+ break;
+ default:
+ pos = GET_LOW(sercon_col) - GET_LOW(sercon_col_lazy);
+ sercon_char[pos] = regs->al;
+ SET_LOW(sercon_col, GET_LOW(sercon_col) + 1);
+ if (pos+1 == ARRAY_SIZE(sercon_char))
+ sercon_flush_lazy();
+ break;
+ }
+}
+
+/* Get current video mode */
+static void sercon_100f(struct bregs *regs)
+{
+ regs->al = GET_LOW(sercon_mode);
+ regs->ah = GET_LOW(sercon_cols);
+}
+
+/* VBE 2.0 */
+static void sercon_104f(struct bregs *regs)
+{
+ regs->ax = 0x0100;
+}
+
+void VISIBLE16
+sercon_10(struct bregs *regs)
+{
+ if (!GET_LOW(sercon_port))
+ return;
+
+ switch (regs->ah) {
+ case 0x00: sercon_1000(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:
+ dprintf(1, "%s: ah 0x%02x, not implemented\n",
+ __func__, regs->ah);
+ }
+}
+
+void sercon_enable(void)
+{
+ u16 addr = PORT_SERIAL1;
+
+ SET_LOW(sercon_port, addr);
+ outb(0x03, addr + SEROFF_LCR); // 8N1
+ outb(0x01, addr + 0x02); // enable fifo
+ enable_vga_console();
+}
diff --git a/src/util.h b/src/util.h
index 7b41207..29f17be 100644
--- a/src/util.h
+++ b/src/util.h
@@ -230,6 +230,8 @@ void code_mutable_preinit(void);
// serial.c
void serial_setup(void);
void lpt_setup(void);
+void sercon_10(struct bregs *regs);
+void sercon_enable(void);
// vgahooks.c
void handle_155f(struct bregs *regs);
--
1.8.3.1
More information about the SeaBIOS
mailing list