root/sys/ddb/db_output.c
/*
 * Mach Operating System
 * Copyright (c) 1991,1990 Carnegie Mellon University
 * All Rights Reserved.
 *
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie the
 * rights to redistribute these changes.
 *
 * $FreeBSD: src/sys/ddb/db_output.c,v 1.26 1999/08/28 00:41:09 peter Exp $
 */

/*
 *      Author: David B. Golub, Carnegie Mellon University
 *      Date:   7/90
 */

/*
 * Printf and character output for debugger.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/cons.h>
#include <sys/ctype.h>
#include <sys/thread2.h>
#include <sys/spinlock2.h>

#include <machine/stdarg.h>

#include <ddb/ddb.h>
#include <ddb/db_output.h>

/*
 *      Character output - tracks position in line.
 *      To do this correctly, we should know how wide
 *      the output device is - then we could zero
 *      the line position when the output device wraps
 *      around to the start of the next line.
 *
 *      Instead, we count the number of spaces printed
 *      since the last printing character so that we
 *      don't print trailing spaces.  This avoids most
 *      of the wraparounds.
 */
static int      db_output_position = 0;         /* output column */
static int      db_last_non_space = 0;          /* last non-space character */
db_expr_t       db_tab_stop_width = 8;          /* how wide are tab stops? */
#define NEXT_TAB(i)     rounddown((i) + db_tab_stop_width, db_tab_stop_width)
db_expr_t       db_max_width = 79;              /* output line width */

static void db_putchar (int c, void *arg);

/*
 * Force pending whitespace.
 */
void
db_force_whitespace(void)
{
        int last_print, next_tab;

        last_print = db_last_non_space;
        while (last_print < db_output_position) {
            next_tab = NEXT_TAB(last_print);
            if (next_tab <= db_output_position) {
                while (last_print < next_tab) { /* DON'T send a tab!!! */
                        cnputc(' ');
                        last_print++;
                }
            }
            else {
                cnputc(' ');
                last_print++;
            }
        }
        db_last_non_space = db_output_position;
}

/*
 * Output character.  Buffer whitespace.
 *
 * Parameters:
 *     arg:     character to output
 */
static void
db_putchar(int c, void *arg)
{
        /*
         * If not in the debugger, output data to both the console and
         * the message buffer.
         */
        if (!db_active) {
                if (c == '\r' || c == '\n' || c == '\t' ||
                    isprint(c)) {
                        kprintf("%c", c);
                } else {
                        kprintf("?");
                }
                if (!db_active)
                        return;
                if (c == '\r' || c == '\n')
                        db_check_interrupt();
                return;
        }

        crit_enter_hard();

        if (c > ' ' && c <= '~') {
            /*
             * Printing character.
             * If we have spaces to print, print them first.
             * Use tabs if possible.
             */
            db_force_whitespace();
            cnputc(c);
            db_output_position++;
            db_last_non_space = db_output_position;
        }
        else if (c == '\n') {
            /* Newline */
            cnputc(c);
            db_output_position = 0;
            db_last_non_space = 0;
            db_check_interrupt();
        }
        else if (c == '\r') {
            /* Return */
            cnputc(c);
            db_output_position = 0;
            db_last_non_space = 0;
            db_check_interrupt();
        }
        else if (c == '\t') {
            /* assume tabs every 8 positions */
            db_output_position = NEXT_TAB(db_output_position);
        }
        else if (c == ' ') {
            /* space */
            db_output_position++;
        }
        else if (c == '\007') {
            /* bell */
            cnputc(c);
        }
        /* other characters are assumed non-printing */
        crit_exit_hard();
}

/*
 * Return output position
 */
int
db_print_position(void)
{
        return (db_output_position);
}

/*
 * Printing
 *
 * NOTE: We bypass subr_prf's cons_spin here by using our own putchar
 *       function.
 */
void
db_printf(const char *fmt, ...)
{
        __va_list listp;

        __va_start(listp, fmt);
        kvcprintf (fmt, db_putchar, NULL, listp);
        __va_end(listp);
/*      DELAY(100000);*/
}

void
db_vprintf(const char *fmt, __va_list va)
{
        kvcprintf (fmt, db_putchar, NULL, va);
/*      DELAY(100000);*/
}

int db_indent;

void
db_iprintf(const char *fmt,...)
{
        int i;
        __va_list listp;

        for (i = db_indent; i >= 8; i -= 8)
                db_printf("\t");
        while (--i >= 0)
                db_printf(" ");
        __va_start(listp, fmt);
        kvcprintf (fmt, db_putchar, NULL, listp);
        __va_end(listp);
}

/*
 * End line if too long.
 */
void
db_end_line(int field_width)
{
        if (db_output_position + field_width > db_max_width)
            db_printf("\n");
}

/*
 * Simple pager
 */
int
db_more(int *nl)
{
        ++*nl;
        if (*nl == 20) {
                int c;

                db_printf("--More--");
                c = cngetc();
                db_printf("\r");
                /*
                 * A whole screenfull or just one line?
                 */
                switch (c) {
                case '\n':              /* just one line */
                        *nl = 19;
                        break;
                case ' ':
                        *nl = 0;        /* another screenfull */
                        break;
                default:                /* exit */
                        db_printf("\n");
                        return(-1);
                }
        }
        return(0);
}

/*
 * Replacement for old '%r' kprintf format.
 */
void
db_format_radix(char *buf, size_t bufsiz, quad_t val, int altflag)
{
        const char *fmt;

        if (db_radix == 16) {
                db_format_hex(buf, bufsiz, val, altflag);
                return;
        }

        if (db_radix == 8)
                fmt = altflag ? "-%#qo" : "-%qo";
        else
                fmt = altflag ? "-%#qu" : "-%qu";

        if (val < 0)
                val = -val;
        else
                ++fmt;

        ksnprintf(buf, bufsiz, fmt, val);
}

/*
 * Replacement for old '%z' kprintf format.
 */
void
db_format_hex(char *buf, size_t bufsiz, quad_t val, int altflag)
{
        /* Only use alternate form if val is nonzero. */
        const char *fmt = (altflag && val) ? "-%#qx" : "-%qx";

        if (val < 0)
                val = -val;
        else
                ++fmt;

        ksnprintf(buf, bufsiz, fmt, val);
}

/* #define TEMPORARY_DEBUGGING */
#ifdef TEMPORARY_DEBUGGING

/*
 * Temporary Debugging, only turned on manually by kernel hackers trying
 * to debug extremely low level code.  Adjust PCHAR_ as required.
 */
static void PCHAR_(int, void * __unused);

void
kprintf0(const char *fmt, ...)
{
        __va_list ap;

        __va_start(ap, fmt);
        kvcprintf(fmt, PCHAR_, NULL, ap);
        __va_end(ap);
}

static void
PCHAR_(int c, void *dummy __unused)
{
        const int COMC_TXWAIT = 0x40000;
        const int COMPORT = 0x2f8;              /* 0x3f8 COM1, 0x2f8 COM2 */
        const int LSR_TXRDY = 0x20;
        const int BAUD = 115200;
        const int com_lsr = 5;
        const int com_data = 0;
        int wait;
        static int setbaud;

        if (setbaud == 0) {
                setbaud = 1;
                outb(COMPORT+3, 0x83);    /* DLAB + 8N1 */
                outb(COMPORT+0, (115200 / BAUD) & 0xFF);
                outb(COMPORT+1, (115200 / BAUD) >> 8);
                outb(COMPORT+3, 0x03);    /* 8N1 */
                outb(COMPORT+4, 0x03);    /* RTS+DTR */
                outb(COMPORT+2, 0x01);    /* FIFO_ENABLE */
        }

        for (wait = COMC_TXWAIT; wait > 0; wait--) {
                if (inb(COMPORT + com_lsr) & LSR_TXRDY) {
                        outb(COMPORT + com_data, (u_char)c);
                        break;
                }
        }
}

#endif