[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