Signed-off-by: Gerd Hoffmann kraxel@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);