Cleanup 'debuginfo' variable in output; add comment.
[seabios.git] / src / output.c
index 6665b6316479db0179595490e72f65b2fb8523ee..e860b0b2c6247d6b6ecaf54e2ca644931ac53433 100644 (file)
@@ -1,6 +1,6 @@
 // Raw screen writing and debug output code.
 //
-// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2008,2009  Kevin O'Connor <kevin@koconnor.net>
 //
 // This file may be distributed under the terms of the GNU LGPLv3 license.
 
 #include "config.h" // CONFIG_*
 #include "biosvar.h" // GET_GLOBAL
 
+struct putcinfo {
+    void (*func)(struct putcinfo *info, char c);
+};
+
+
+/****************************************************************
+ * Debug output
+ ****************************************************************/
+
 #define DEBUG_PORT PORT_SERIAL1
 #define DEBUG_TIMEOUT 100000
 
@@ -48,7 +57,7 @@ debug_serial(char c)
     outb(c, DEBUG_PORT+SEROFF_DATA);
 }
 
-// Write a character to the serial port.
+// Make sure all serial port writes have been completely sent.
 static void
 debug_serial_flush()
 {
@@ -61,45 +70,81 @@ debug_serial_flush()
             return;
 }
 
-// Show a character on the screen.
+// Write a character to debug port(s).
 static void
-screenc(u8 c)
+putc_debug(struct putcinfo *action, char c)
 {
-    if (MODE16)
-        // printf is only used in 32bit code.
+    if (! CONFIG_DEBUG_LEVEL)
         return;
+    if (! CONFIG_COREBOOT)
+        // Send character to debug port.
+        outb(c, PORT_BIOS_DEBUG);
+    if (c == '\n')
+        debug_serial('\r');
+    debug_serial(c);
+}
+
+// In 16bit mode just need a dummy variable (putc_debug is always used
+// anyway), and in 32bit mode need a pointer to the 32bit instance of
+// putc_debug().
+#if MODE16
+static struct putcinfo debuginfo VAR16;
+#else
+static struct putcinfo debuginfo = { putc_debug };
+#endif
+
+
+/****************************************************************
+ * Screen writing
+ ****************************************************************/
+
+// Show a character on the screen.
+static void
+screenc(char c)
+{
     struct bregs br;
     memset(&br, 0, sizeof(br));
+    br.flags = F_IF;
     br.ah = 0x0e;
     br.al = c;
     call16_int(0x10, &br);
 }
 
+// Handle a character from a printf request.
+static void
+putc_screen(struct putcinfo *action, char c)
+{
+    if (CONFIG_SCREEN_AND_DEBUG)
+        putc_debug(action, c);
+    if (c == '\n')
+        screenc('\r');
+    screenc(c);
+}
+
+static struct putcinfo screeninfo = { putc_screen };
+
+
+/****************************************************************
+ * Xprintf code
+ ****************************************************************/
+
 // Output a character.
 static void
-putc(u16 action, char c)
+putc(struct putcinfo *action, char c)
 {
-    if (CONFIG_DEBUG_LEVEL) {
-        if (! CONFIG_COREBOOT)
-            // Send character to debug port.
-            outb(c, PORT_BIOS_DEBUG);
-        // Send character to serial port.
-        if (c == '\n')
-            debug_serial('\r');
-        debug_serial(c);
+    if (MODE16) {
+        // Only debugging output supported in 16bit mode.
+        putc_debug(action, c);
+        return;
     }
 
-    if (action) {
-        // Send character to video screen.
-        if (c == '\n')
-            screenc('\r');
-        screenc(c);
-    }
+    void (*func)(struct putcinfo *info, char c) = GET_GLOBAL(action->func);
+    func(action, c);
 }
 
 // Ouptut a string.
 static void
-puts(u16 action, const char *s)
+puts(struct putcinfo *action, const char *s)
 {
     for (; *s; s++)
         putc(action, *s);
@@ -107,10 +152,11 @@ puts(u16 action, const char *s)
 
 // Output a string that is in the CS segment.
 static void
-puts_cs(u16 action, const char *s)
+puts_cs(struct putcinfo *action, const char *s)
 {
-    for (;; s++) {
-        char c = GET_GLOBAL(*(u8*)s);
+    char *vs = (char*)s;
+    for (;; vs++) {
+        char c = GET_GLOBAL(*vs);
         if (!c)
             break;
         putc(action, c);
@@ -119,7 +165,7 @@ puts_cs(u16 action, const char *s)
 
 // Output an unsigned integer.
 static void
-putuint(u16 action, u32 val)
+putuint(struct putcinfo *action, u32 val)
 {
     char buf[12];
     char *d = &buf[sizeof(buf) - 1];
@@ -136,7 +182,7 @@ putuint(u16 action, u32 val)
 
 // Output a single digit hex character.
 static inline void
-putsinglehex(u16 action, u32 val)
+putsinglehex(struct putcinfo *action, u32 val)
 {
     if (val <= 9)
         val = '0' + val;
@@ -147,7 +193,7 @@ putsinglehex(u16 action, u32 val)
 
 // Output an integer in hexadecimal.
 static void
-puthex(u16 action, u32 val, int width)
+puthex(struct putcinfo *action, u32 val, int width)
 {
     if (!width) {
         u32 tmp = val;
@@ -183,7 +229,7 @@ isdigit(u8 c)
 }
 
 static void
-bvprintf(u16 action, const char *fmt, va_list args)
+bvprintf(struct putcinfo *action, const char *fmt, va_list args)
 {
     const char *s = fmt;
     for (;; s++) {
@@ -257,7 +303,6 @@ bvprintf(u16 action, const char *fmt, va_list args)
         }
         s = n;
     }
-    debug_serial_flush();
 }
 
 void
@@ -266,8 +311,9 @@ panic(const char *fmt, ...)
     if (CONFIG_DEBUG_LEVEL) {
         va_list args;
         va_start(args, fmt);
-        bvprintf(0, fmt, args);
+        bvprintf(&debuginfo, fmt, args);
         va_end(args);
+        debug_serial_flush();
     }
 
     // XXX - use PANIC PORT.
@@ -281,37 +327,83 @@ __dprintf(const char *fmt, ...)
 {
     va_list args;
     va_start(args, fmt);
-    bvprintf(0, fmt, args);
+    bvprintf(&debuginfo, fmt, args);
     va_end(args);
+    debug_serial_flush();
 }
 
 void
 printf(const char *fmt, ...)
 {
+    ASSERT32();
+    va_list args;
+    va_start(args, fmt);
+    bvprintf(&screeninfo, fmt, args);
+    va_end(args);
+    if (CONFIG_SCREEN_AND_DEBUG)
+        debug_serial_flush();
+}
+
+
+/****************************************************************
+ * snprintf
+ ****************************************************************/
+
+struct snprintfinfo {
+    struct putcinfo info;
+    char *str, *end;
+};
+
+static void
+putc_str(struct putcinfo *info, char c)
+{
+    struct snprintfinfo *sinfo = container_of(info, struct snprintfinfo, info);
+    if (sinfo->str >= sinfo->end)
+        return;
+    *sinfo->str = c;
+    sinfo->str++;
+}
+
+void
+snprintf(char *str, size_t size, const char *fmt, ...)
+{
+    ASSERT32();
+    if (!size)
+        return;
+    struct snprintfinfo sinfo = { { putc_str }, str, str + size };
     va_list args;
     va_start(args, fmt);
-    bvprintf(1, fmt, args);
+    bvprintf(&sinfo.info, fmt, args);
     va_end(args);
+    char *end = sinfo.str;
+    if (end >= sinfo.end)
+        end--;
+    *end = '\0';
 }
 
+
+/****************************************************************
+ * Misc helpers
+ ****************************************************************/
+
 void
-hexdump(void *d, int len)
+hexdump(const void *d, int len)
 {
     int count=0;
-    while (len) {
+    while (len > 0) {
         if (count % 8 == 0) {
-            putc(0, '\n');
-            puthex(0, count*4, 8);
-            putc(0, ':');
+            putc(&debuginfo, '\n');
+            puthex(&debuginfo, count*4, 8);
+            putc(&debuginfo, ':');
         } else {
-            putc(0, ' ');
+            putc(&debuginfo, ' ');
         }
-        puthex(0, *(u32*)d, 8);
+        puthex(&debuginfo, *(u32*)d, 8);
         count++;
         len-=4;
         d+=4;
     }
-    putc(0, '\n');
+    putc(&debuginfo, '\n');
     debug_serial_flush();
 }
 
@@ -327,15 +419,15 @@ dump_regs(struct bregs *regs)
             , regs->ds, regs->es, GET_SEG(SS));
     dprintf(1, "  si=%08x di=%08x bp=%08x sp=%08x cs=%04x ip=%04x  f=%04x\n"
             , regs->esi, regs->edi, regs->ebp, (u32)&regs[1]
-            , regs->cs, regs->ip, regs->flags);
+            , regs->code.seg, regs->code.offset, regs->flags);
 }
 
 // Report entry to an Interrupt Service Routine (ISR).
 void
 __debug_isr(const char *fname)
 {
-    puts_cs(0, fname);
-    putc(0, '\n');
+    puts_cs(&debuginfo, fname);
+    putc(&debuginfo, '\n');
     debug_serial_flush();
 }