[eros-cvs] cvs commit: eros/src/base/sys/kerninc KernStream.hxx

shap@eros.cs.jhu.edu shap@eros.cs.jhu.edu
Sun, 25 Nov 2001 20:31:11 -0500


shap        01/11/25 20:31:11

  Added:       src/base/sys/arch/i486/kernel ConsoleStream.cxx
                        SerialStream.cxx
               src/base/sys/kernel kern_KernStream.cxx kern_LogStream.cxx
                        kern_printf.cxx
               src/base/sys/kerninc KernStream.hxx
  Log:
  These should have been checked in with the last commit

Revision  Changes    Path
1.1                  eros/src/base/sys/arch/i486/kernel/ConsoleStream.cxx

Index: ConsoleStream.cxx
===================================================================
/*
 * Copyright (C) 2001, Jonathan S. Shapiro.
 *
 * This file is part of the EROS Operating System.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2,
 * or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/* This is a (poor) implementation of a text-mode kernel frame buffer
 * for use in diagnostics and debugger output. It assumes that the
 * display has been left in the standard VGA text mode by the
 * bootstrap code. */

#include <kerninc/kernel.hxx>
#include <kerninc/KernStream.hxx>
#include <kerninc/Machine.hxx>
#include <kerninc/IRQ.hxx>
#include <kerninc/util.h>
#include <eros/i486/io.h>
#include <eros/i486/fixregs.h>
#include "IDT.hxx"

#define SCREEN_START PTOV(0xb8000u)
#define SCREEN_ROWS 25
#define SCREEN_COLS 80
#define SCREEN_END (SCREEN_START + 2*SCREEN_COLS*SCREEN_ROWS)

/* This class has a singleton instance! */
struct ConsoleStream: public KernStream {
  void Init();
  void Put(uint8_t c);

#ifdef OPTION_DDB
  uint8_t Get();
  void SetDebugging(bool onoff);
  void EnableDebuggerInput();
#endif
};

struct ConsoleStream TheConsoleStream;
KernStream* KernStream::ConsoleStream = &TheConsoleStream;

static void ClearTextConsole();

static unsigned long offset;
  
static uint32_t StartAddressReg = 0;	/* as seen by the display hardware */
static uint16_t *screen = (uint16_t *) SCREEN_START;

void
ConsoleStream::Init()
{
  outb(0xc, 0x3d4);		/* start address hi register addr */
  uint8_t hi = inb(0x3d5);
  outb(0xd, 0x3d4);		/* start address lo register addr */
  uint8_t lo = inb(0x3d5);

  StartAddressReg = hi << 8 | lo;

  offset = 0;

  ClearTextConsole();
}

enum VGAColors {
  Black = 0,
  Blue = 1,
  Green = 2,
  Cyan = 3,
  Red = 4,
  Magenta = 5,
  Brown = 6,
  White = 7,
  Gray = 8,
  LightBlue = 9,
  LightGreen = 10,
  LightCyan = 11,
  LightRed = 12,
  LightMagenta = 13,
  LightBrown = 14,	/* yellow */
  BrightWhite = 15,

  /* Combinations used by the console driver: */
  WhiteOnBlack = 0x7,
  blank = 0,
};

static void
SetPosition(uint32_t pos, uint8_t c)
{
  uint16_t vgaAttrs = WhiteOnBlack << 8;

  screen[pos] = ((uint16_t) c) | vgaAttrs;
}

static void
ShowCursorAt(uint32_t pos)
{
  uint32_t cursAddr = (uint32_t) pos;

  cursAddr += StartAddressReg;

  outb(0xE, 0x3D4);
  outb((cursAddr >> 8) & 0xFFu, 0x3D5);
  outb(0xF, 0x3D4);
  outb((cursAddr & 0xFFu), 0x3D5);
}

static void
Scroll(uint32_t startPos, uint32_t endPos, int amount)
{
  if (amount > 0) {
    uint32_t gap = amount;
    for (uint32_t p = startPos + gap; p < endPos; p++)
      screen[p] = screen[p - gap];

    for (uint32_t p = startPos; p < startPos + gap; p++)
      screen[p] = (WhiteOnBlack << 8);
  }
  else {
    uint32_t gap = -amount;

    for (uint32_t p = startPos; p < endPos - gap; p++)
      screen[p] = screen[p + gap];

    for (uint32_t p = endPos - gap; p < endPos; p++)
      screen[p] = (WhiteOnBlack << 8);
  }
}

static void
ClearTextConsole()
{
  for (uint32_t wpos = 0; wpos < SCREEN_ROWS * SCREEN_COLS; wpos++)
    SetPosition(wpos, ' ');
}


static void Beep() {}

/* FIX: This is NOT RIGHT!! */
void
ConsoleStream::Put(uint8_t c)
{
  const unsigned cols = SCREEN_COLS;
  const unsigned rows = SCREEN_ROWS;

  const int TABSTOP = 8;
  uint32_t posCol = offset % cols;

  /* On newline, clear to EOL: */
  if (c == ASCII::CR)
    if (offset % cols) Scroll (offset, offset + (cols - posCol), (cols - posCol));

  if (IsPrint(c)) {
    SetPosition(offset, c);
    offset++;
  }
  else {
    /* Handle the non-printing characters: */
    switch(c) {
    case ASCII::BEL:
      Beep();
      break;
    case ASCII::BS:		/* backspace is NONDESTRUCTIVE */
      if ( offset % cols )
	offset--;
      break;
    case ASCII::TAB:		/* NONDESTRUCTIVE until we know how */
      while (offset % TABSTOP) {
	SetPosition(offset, ' ');
	offset++;
      }
      break;
    case ASCII::LF:
      offset += cols;
      break;
    case ASCII::VT:		/* reverse line feed */
      if (offset > cols)
	offset -= cols;
      break;
#if 0
    case ASCII::FF:		/* reverse line feed */
      offset = 0;
      ClearScreen();
      break;
#endif
    case ASCII::CR:
      offset -= (offset % cols);
      break;
    }
  }
    
  if (offset >= rows * cols) {
    Scroll(0, rows * cols, - (int) cols);
    offset -= cols;
  }

  assert (offset < rows * cols);
  ShowCursorAt(offset);
  return;
}

/*************************************************************************
 *
 * EVERYTHING FROM HERE DOWN IS KEYBOARD DRIVER!!!!
 *
 * The keyboard logic is enabled only if the debugger is running on
 * the console.
 *
 *************************************************************************/

#if defined(OPTION_DDB_ON_CONSOLE)

const uint8_t KbdDataPort    = 0x60u;
const uint8_t KbdCtrlPort   = 0x64u;
const uint8_t KbdStatusPort = 0x64u;

struct KeyCmd {
  enum {
    SetLed = 0xedu,
  } ;
};

struct KbdStatus {
  enum {
    BufFull = 0x1,
    Ready   = 0x2,
  };
};

struct KeyMod {
  enum {
    /* Note that the first three values correspond to the bitmask for the
     * keyboard LED's -- this is not an accident!
     */
    ScrlLock  = 0x01u,
    NumLock   = 0x02u,
    AlphaLock = 0x04u,

    Shift     = 0x10u,
    Ctrl      = 0x20u,
    Alt       = 0x40u,


    Extended  = 0x100u,		/* key is an "extended" key */
    IsAlpha   = 0x200u,		/* key is modified by alpha lock key */
    IsPad     = 0x400u,		/* key is modified by num lock key */
    Meta      = 0x800u,		/* key can be meta'd */
  } ;
};

static uint32_t ShiftState = 0;

/* kbd_wait -- wait for a character to be available from the keyboard. */
static void 
KbdWait(void)
{
  int i = 100;

  while (i--) {
    if ((inb(KbdStatusPort) & KbdStatus::Ready) == 0) 
      break;
    Machine::SpinWaitUs(10);
  }

#if 0
  printf("KbdWait fails\n");
#endif
}

static void 
KbdCmd(uint8_t command)
{
  int retry = 5;
  do {
    int i = 100000;

    KbdWait();

    old_outb(KbdDataPort, command);

    while (i--) {
      if (inb(KbdStatusPort) & KbdStatus::BufFull) {
	int val;
	/* DELAY(10); */
	val = inb(KbdDataPort);
	if (val == 0xfa)
	  return;
	if (val == 0xfe)
	  break;
      }
    }

  } while (retry--);
  printf("KbdCmd fails\n");
}

static bool
ReadKbd(uint8_t& c)
{
  KbdWait();

  while ( (inb(KbdStatusPort) & KbdStatus::BufFull) == 0 )
    return false;
  
  c = inb(KbdDataPort);
  return true;
}

static void
UpdateKbdLeds()
{
  KbdCmd(KeyCmd::SetLed);
  KbdCmd(ShiftState & 0x7u);
}
	       
/* Keyboard interpretation proceeds in two phases.  First, the scan
 * code is converted into a virtual key code, performing any necessary
 * keyboard escape translation.  Then the key code translation table
 * is consulted to decide what character to return and whether to
 * update the shift state (if at all).
 */

#define NOP 256

#define NOCHAR(name) {{ NOP, NOP, NOP, NOP },   0 }
#define ALPHA(X) {{ X+32, X, X - 64, X - 64 }, KeyMod::Meta|KeyMod::IsAlpha }
#define KEY(X, Y) {{ X, Y, X, Y },   KeyMod::Meta }
#define PAD(X, Y) {{ X, Y, X, Y },   KeyMod::IsPad }
#define F(X) (X + 256)

const uint32_t num_scan = 0x59;
struct KeyInfo {
  uint16_t value[4];		/* base, shift, ctrl, shift-ctrl */
  uint16_t flags;
} key_table[num_scan] = {
  NOCHAR(None),			/* 0x00 */
  KEY('\027', '\027'),		/* 0x01 */
  KEY('1', '!'),		/* 0x02 */
  { { '2', '@', '\0', '\0'}, 0 }, /* 0x03 -- generate NUL */
  KEY('3', '#'),		/* 0x04 */
  KEY('4', '$'),		/* 0x05 */
  KEY('5', '%'),		/* 0x06 */
  KEY('6', '^'),		/* 0x07 */
  KEY('7', '&'),		/* 0x08 */
  KEY('8', '*'),		/* 0x09 */
  KEY('9', '('),		/* 0x0a */
  KEY('0', ')'),		/* 0x0b */
  KEY('-',  '_'),		/* 0x0c */
  KEY('=',  '+'),		/* 0x0d */
  KEY(0x08, 0x08),		/* 0x0e */
  KEY(0x09, 0x08),		/* 0x0f -- is back tab right? */
  ALPHA('Q'),			/* 0x10 */
  ALPHA('W'),			/* 0x11 */
  ALPHA('E'),			/* 0x12 */
  ALPHA('R'),			/* 0x13 */
  ALPHA('T'),			/* 0x14 */
  ALPHA('Y'),			/* 0x15 */
  ALPHA('U'),			/* 0x16 */
  ALPHA('I'),			/* 0x17 */
  ALPHA('O'),			/* 0x18 */
  ALPHA('P'),			/* 0x19 */
  KEY('[',  '{'),		/* 0x1a */
  KEY(']',  '}'),		/* 0x1b */
  KEY('\r',  '\r'),		/* 0x1c -- enter */
  { { NOP, NOP, NOP, NOP }, KeyMod::Ctrl }, /* 0x1d -- lctrl */
  ALPHA('A'),			/* 0x1e */
  ALPHA('S'),			/* 0x1f */
  ALPHA('D'),			/* 0x20 */
  ALPHA('F'),			/* 0x21 */
  ALPHA('G'),			/* 0x22 */
  ALPHA('H'),			/* 0x23 */
  ALPHA('J'),			/* 0x24 */
  ALPHA('K'),			/* 0x25 */
  ALPHA('L'),			/* 0x26 */
  KEY(';',  ':'),		/* 0x27 */
  KEY('\'', '"'),		/* 0x28 */
  KEY('`',  '~'),		/* 0x29 */
  { { NOP, NOP, NOP, NOP }, KeyMod::Shift }, /* 0x2a -- lshift */
  KEY('\\', '|'),		/* 0x2b */
  ALPHA('Z'),			/* 0x2c */
  ALPHA('X'),			/* 0x2d */
  ALPHA('C'),			/* 0x2e */
  ALPHA('V'),			/* 0x2f */
  ALPHA('B'),			/* 0x30 */
  ALPHA('N'),			/* 0x31 */
  ALPHA('M'),			/* 0x32 */
  KEY(',',  '<'),		/* 0x33 */
  KEY('.',  '>'),		/* 0x34 */
  KEY('/',  '?'),		/* 0x35 */
  { { NOP, NOP, NOP, NOP }, KeyMod::Shift }, /* 0x36 -- rshift */
  KEY('*',  '*'),		/* 0x37 */
  { { NOP, NOP, NOP, NOP }, KeyMod::Alt }, /* 0x38 -- lalt */
  KEY(' ',  ' '),		/* 0x39 -- space */
  { { NOP, NOP, NOP, NOP }, KeyMod::AlphaLock }, /* 0x3a -- alpha lock */
  KEY( F(1), NOP ),		/* 0x3b -- F1 */
  KEY( F(2), NOP ),		/* 0x3c -- F2 */
  KEY( F(3), NOP ),		/* 0x3d -- F3 */
  KEY( F(4), NOP ),		/* 0x3e -- F4 */
  KEY( F(5), NOP ),		/* 0x3f -- F5 */
  KEY( F(6), NOP ),		/* 0x40 -- F6 */
  KEY( F(7), NOP ),		/* 0x41 -- F7 */
  KEY( F(8), NOP ),		/* 0x42 -- F8 */
  KEY( F(9), NOP ),		/* 0x43 -- F9 */
  KEY( F(10), NOP ),		/* 0x44 -- F10 */
  { { NOP, NOP, NOP, NOP }, KeyMod::NumLock }, /* 0x45 -- num lock */
  { { NOP, NOP, NOP, NOP }, KeyMod::ScrlLock }, /* 0x46 -- scroll-lock */

  /* Keypad character mappings -- these assume that num-lock is NOT set! */
  PAD( F(15), '7' ),		/* 0x47 -- keypad 7 */
  PAD( F(16), '8' ),		/* 0x48 -- keypad 8 */
  PAD( F(17), '9' ),		/* 0x49 -- keypad 9 */
  KEY( '-',   NOP ),		/* 0x4a -- keypad - */
  PAD( F(19), '4' ),		/* 0x4b -- keypad 4 */
  PAD( NOP,   '5' ),		/* 0x4c -- keypad 5 */
  PAD( F(20), '6' ),		/* 0x4d -- keypad 6 */
  KEY( '+',   '+' ),		/* 0x4e -- keypad + */
  PAD( F(22), '1' ),		/* 0x4f -- keypad 1 */
  PAD( F(23), '2' ),		/* 0x50 -- keypad 2 */
  PAD( F(24), '3' ),		/* 0x51 -- keypad 3 */
  PAD( F(25), '0' ),		/* 0x52 -- keypad 0 */
  PAD( 0x7f,  '.' ),		/* 0x53 -- keypad ./DEL */
  KEY( NOP, NOP ),		/* 0x54 -- unused! */
  KEY( NOP, NOP ),		/* 0x55 -- unused! */
  KEY( NOP, NOP ),		/* 0x56 -- unused! */
  KEY( F(11), NOP ),		/* 0x57 -- F11 */
  KEY( F(12), NOP ),		/* 0x58 -- F12 */
#if 0
  /* CHARACTERS BELOW THIS POINT ARE RECODED!!! */

  { { NOP, NOP, NOP, NOP }, KeyMod::Ctrl }, /* 0x59 rctrl */
  { { NOP, NOP, NOP, NOP }, KeyMod::Alt }, /* 0x5a ralt */

e0,1c	kpd-enter
e0,1d	rctrl		SUPPRESSED
e0,35	kpd-/
e0,37	print-screen
e0,38   ralt		SUPPRESSED
e0,47	home		
e0,48	uparrow
e0,49	PgUp
e0,4b	left-arrow		
e0,4d	right-arrow
e0,4f	end		
e0,50	downarrow
e0,51	PgDn
e0,52	insert		
e0,53	delete		
e0,5b	lwindow
e0,5c	rwindow
e0,5d	menu
e1,1d,68 pause
#endif
};

static uint32_t
FetchInputFromKeyboard()
{
  static uint8_t esc_code = 0;

  uint8_t scanCode = 0;
  
  while ( ReadKbd(scanCode) ) {
    /* printf("<kc = %x>", scanCode); */
    bool shift = ShiftState & KeyMod::Shift;
#if 0
    bool alt = ShiftState & KeyMod::Alt;
#endif
    bool ctrl = ShiftState & KeyMod::Ctrl;
      
    /* If this is a break character, we need to know: */
    bool isBreak = scanCode & 0x80u;

    uint32_t keyCode = scanCode & 0x7fu;
      
    switch (esc_code) {
    case 0x0:
      {
	/* printf("esc_code==0\n"); */
	switch (scanCode) {
	case 0xe0:
	case 0xe1:
	  esc_code = scanCode;
	  continue;
	default:
	  if (keyCode >= num_scan) {
	    printf("<? 0x0 \\x%x>", keyCode);
	    return NOP;
	  }
	}
	break;
      }
    case 0xe0:
      {
	switch (keyCode) {
	case 0x2a:		/* shift hack used by some keyboards */
				/* for extra keys! */
	  shift = ! shift;

	case 0x1c:		/* kpd-enter */
	case 0x1d:		/* rctrl */
	case 0x35:		/* kpd-/ */
	case 0x38:		/* ralt */
	case 0x47:		/* home */
	case 0x48:		/* uparrow */
	case 0x49:		/* pgup */
	case 0x4b:		/* left-arrow */
	case 0x4d:		/* right-arrow */
	case 0x4f:		/* end */
	case 0x50:		/* down-arrow */
	case 0x51:		/* pgdn */
	case 0x52:		/* insert */
	case 0x53:		/* del */
	  esc_code = 0;
	  break;

	case 0x5b:		/* lwindow */
	case 0x5c:		/* rwindow */
	case 0x5d:		/* menu */
	  /* consume these transparently: */
	  esc_code = 0;
	  return NOP;
	default:
	  printf("<? 0xe0 \\x%x>", scanCode);
	  esc_code = 0;
	  return NOP;
	}
	break;
      }

    case 0xe1:
      {
	if (keyCode == 0x1d) {
	  esc_code = scanCode;
	  continue;
	}
	else {
	  esc_code = 0;
	  printf("<? 0xe1 \\x%x>", scanCode);
	  return NOP;
	}
	break;
      }
    case 0x1d:
      {
	if (keyCode == 0x68) {
	  /* consume transparently */
	  esc_code = 0;
	  return NOP;
	}
	else {
	  printf("<? 0x1d \\x%x>", scanCode);
	  esc_code = 0;
	  return NOP;
	}
	break;
      }
    default:
      printf("Unknown escape 0x%x\n", esc_code); 
      break;
    }
      
    /* printf("Key code is %d (0x%x)\n", keyCode, keyCode);  */
    KeyInfo& ki = key_table[keyCode];

    /* printf("<kf=\\x%x,0x%x>", keyCode, ki.flags); */

    if ( (ki.flags & KeyMod::IsAlpha) &&
	 (ShiftState & KeyMod::AlphaLock) )
      shift = !shift;

    if ( (ki.flags & KeyMod::IsPad) &&
	 (ShiftState & KeyMod::NumLock) )
      shift = !shift;

    uint32_t ndx = (shift ? 1 : 0) + (ctrl ? 2 : 0);
    uint32_t ascii = ki.value[ndx];

      /* keep track of shift, ctrl, alt */
    if (isBreak)
      ShiftState &= ~ (ki.flags & 0xf0u);
    else {
      ShiftState |= (ki.flags & 0xf0u);
    }

    /* keep track of the various lock keys on the break, not the
       * keypress - the break doesn't repeat.  These are toggles, thus
       * the XOR:
       */

    if (isBreak && (ki.flags & 0xfu)) {
      ShiftState ^= (ki.flags & 0xfu);
      UpdateKbdLeds();
    }

    if (isBreak || ascii >= NOP)
      return NOP;

#if 0
      /* Check for three-fingered salute: */
    if ( keyCode == 0x53u && ctrl && alt)
      Reboot();
#endif

#if 0
#ifdef OPTION_DDB
    /* Check for kernel debugger: */
    if ( keyCode == 0x20u && ctrl && alt)/* 20u == 'd' */
      Debugger();

    if ( keyCode == 0x2eu && ctrl ) /* 1fu == 'c' */
      Debugger();
#endif
#endif

#ifdef CORNER_HACK
    *((uint16_t *) 0x000b8000) = 0x8f00 | (ascii & 0xffu);
#endif
    return ascii;
  }

  return NOP;
}

static void
KeyboardInterrupt(fixregs_t *sa)
{
  uint32_t c;
  uint32_t irq = IRQ_FROM_EXCEPTION(sa->ExceptNo);

  assert(irq == 1);

  if (KernStream::debuggerIsActive)
    return;

#ifdef CORNER_HACK
  *((uint16_t *) 0x000b8000) = 0x8f00 | 'I';
#endif
  c = FetchInputFromKeyboard();
  if (c == ASCII::ETX)
    Debugger();

  IRQ::Enable(irq);
}

uint8_t
ConsoleStream::Get()
{
  uint32_t c;

#ifdef CORNER_HACK
  *((uint16_t *) 0x000b8000) = 0x8f00 | 'P';
#endif

  do {
    c = FetchInputFromKeyboard();
  } while (c == NOP);

#ifdef CORNER_HACK
  *((uint16_t *) 0x000b8000) = 0x8f00 | 'G';
#endif

  return c;
}

void
ConsoleStream::SetDebugging(bool onOff)
{
  debuggerIsActive = onOff;
  
  if (debuggerIsActive == false)
    IRQ::Enable(IRQ386::Keyboard);
}

void
ConsoleStream::EnableDebuggerInput()
{
  IRQ::SetHandler(IRQ386::Keyboard, KeyboardInterrupt);
  printf("Set up keyboard (console) interrupt handler!\n");

#if 0
  /* Establish initial keyboard state visibly: */
  UpdateKbdLeds();
#endif

  /* FIX: This may prove a problem, as I need to check exactly where
   * the interrupt vectors are set up in the boot sequence... */
  IRQ::Enable(IRQ386::Keyboard);
}

#elif defined(OPTION_DDB)
void
ConsoleStream::EnableDebuggerInput()
{
  fatal("EnableDebuggerInput on console when not DDB_ON_CONSOLE\n");
}

void
ConsoleStream::SetDebugging(bool onOff)
{
  fatal("SetDebugging() on console when not DDB_ON_CONSOLE\n");
}


uint8_t
ConsoleStream::Get()
{
  fatal("Get() on console when not DDB_ON_CONSOLE\n");
  return 0;
}

#endif /* OPTION_DDB_ON_CONSOLE */



1.1                  eros/src/base/sys/arch/i486/kernel/SerialStream.cxx

Index: SerialStream.cxx
===================================================================
/*
 * Copyright (C) 2001, Jonathan S. Shapiro.
 *
 * This file is part of the EROS Operating System.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2,
 * or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/* This is a serial debugging stream based closely on the earlier
 * logic introduced by Mike Hilsdale. */

#include <kerninc/kernel.hxx>
#include <kerninc/KernStream.hxx>
#include <kerninc/IRQ.hxx>
#include <eros/i486/io.h>
#include <eros/i486/fixregs.h>
#include "IDT.hxx"

/* This class has a singleton instance! */
struct SerialStream: public KernStream {
  void Init();
  void Put(uint8_t c);

#ifdef OPTION_DDB
  uint8_t Get();
  void SetDebugging(bool onoff);
  void EnableDebuggerInput();
#endif
};

#define COMIRQ   IRQ386::Serial0
#define COMBASE  0x3f8

struct SerialStream TheSerialStream;
KernStream* KernStream::SerialStream = &TheSerialStream;

void
SerialStream::Init()
{
  uint16_t dlab = COMBASE + 3;

  /* Initialize COM port */

  /* Currently: 9600, 8N1 */
  /* (This seemes to not be needed when using VMware
   * and psuedo terminal pipes, but it's here for
   * completeness and for real hardware.)
   */

  /* set DLAB bit      */
  /* b7=1              */
  old_outb(dlab, inb(dlab) ^ 0x80);

  /* set 16-bit divisor to 12 (0Ch = 9600 baud) */
  old_outb(COMBASE, 0x0C);
  old_outb(COMBASE + 1, 0);

  /* set data bits to 8 */
  /* b0=1 b1=1          */
  old_outb(dlab, inb(dlab) ^ 0x03);

  /* set parity to None */
  /* b3=0 b4=4 b5=0     */
  old_outb(dlab, inb(dlab) & 0xC7);

  /* set stop bits to 1 */
  /* b2=0               */
  old_outb(dlab, inb(dlab) & 0xFB);

  /* unset DLAB bit     */
  /* b7=0               */
  old_outb(COMBASE + 3, inb(COMBASE + 3) & 0x70);

  /* tell COM port to report INTs on received char */
  old_outb(COMBASE + 1, 1);
}

uint8_t
SerialStream::Get()
{
  uint8_t c;

  /* This is incorrect: Should be spinning waiting for a valid
   * character! */
  c = inb(COMBASE);

  return c;
}

void
SerialStream::Put(uint8_t c)
{
  /* This *may* be incorrect. Should *probably* be spinning waiting
   * for room in the output fifo! */
  old_outb(COMBASE, c);

  return;
}


void
SerialStream::SetDebugging(bool onOff)
{
  debuggerIsActive = onOff;
  
  if (debuggerIsActive == false)
    IRQ::Enable(COMIRQ);
}

static void
SerialInterrupt(fixregs_t *sa)
{
  char c;
  uint32_t irq = IRQ_FROM_EXCEPTION(sa->ExceptNo);

  /* look at interrupt id to quell UART */
  /* may want to actually do something here sometime later */
  inb(COMBASE + 2);

  if (KernStream::debuggerIsActive)
    return;

  assert(irq == COMIRQ);

  c = TheSerialStream.Get();

  if (c == ASCII::ETX)
    Debugger();

  IRQ::Enable(irq);
}

void
SerialStream::EnableDebuggerInput()
{
  IRQ::SetHandler(COMIRQ, SerialInterrupt);
  printf("Set up keyboard (console) interrupt handler!\n");

#if 0
  /* Establish initial keyboard state visibly: */
  UpdateKbdLeds();
#endif

  /* FIX: This may prove a problem, as I need to check exactly where
   * the interrupt vectors are set up in the boot sequence... */
  IRQ::Enable(COMIRQ);
}



1.1                  eros/src/base/sys/kernel/kern_KernStream.cxx

Index: kern_KernStream.cxx
===================================================================
/*
 * Copyright (C) 2001, Jonathan S. Shapiro.
 *
 * This file is part of the EROS Operating System.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2,
 * or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <kerninc/kernel.hxx>
#include <kerninc/util.h>
#include <kerninc/KernStream.hxx>

#ifdef OPTION_DDB
bool KernStream::debuggerIsActive = false;
KernStream *KernStream::dbg_stream = 0;
#endif
KernStream *KernStream::syscon_stream = 0;
KernStream *KernStream::log_stream = 0;

void
KernStream::InitStreams()
{
  LogStream->Init();
  ConsoleStream->Init();
#ifdef OPTION_DDB_ON_TTY0
  SerialStream->Init();
#endif

  syscon_stream = ConsoleStream;
  log_stream = LogStream;

#if defined(OPTION_DDB_ON_CONSOLE)
  dbg_stream = ConsoleStream;
#elif defined(OPTION_DDB_ON_TTY0)
  dbg_stream = SerialStream;
#endif
    
#ifdef OPTION_DDB
  /* If the debugger stream is set to point to the console device,
   * turn off the syscon_stream so that we do not see duplicated
   * output. */
  if (dbg_stream == ConsoleStream)
    syscon_stream = 0;
#endif /* OPTION_DDB */

  printf("Kernel streams initialized...\n");
}

bool
KernStream::IsPrint(uint8_t c)
{
  if (c > 127)
    return false;
  
  static uint8_t isPrint[16] = {	/* really a bitmask! */
    0x00,			/* BEL ACK ENQ EOT ETX STX SOH NUL */
    0x00,			/* SI  SO  CR  FF  VT  LF  TAB BS */
    0x00,			/* ETB SYN NAK DC4 DC3 DC2 DC1 DLE */
    0x00,			/* US  RS  GS  FS  ESC SUB EM  CAN */
    0xff,			/* '   &   %   $   #   "   !   SPC */
    0xff,			/* /   .   -   ,   +   *   )   ( */
    0xff,			/* 7   6   5   4   3   2   1   0 */
    0xff,			/* ?   >   =   <   ;   :   9   8 */
    0xff,			/* G   F   E   D   C   B   A   @ */
    0xff,			/* O   N   M   L   K   J   I   H */
    0xff,			/* W   V   U   T   S   R   Q   P */
    0xff,			/* _   ^   ]   \   [   Z   Y   X */
    0xff,			/* g   f   e   d   c   b   a   ` */
    0xff,			/* o   n   m   l   k   j   i   h */
    0xff,			/* w   v   u   t   s   r   q   p */
    0x7f,			/* DEL ~   }   |   {   z   y   x */
  } ;

  uint8_t mask = isPrint[(c >> 3)];
  c &= 0x7u;
  if (c)
    mask >>= c;
  if (mask & 1)
    return true;
  return false;
}

void
KernStream::do_putc(char c)
{
#ifdef OPTION_DDB
  if (dbg_stream) dbg_stream->Put(c);
#endif
  if (syscon_stream) syscon_stream->Put(c);
#ifdef OPTION_DDB
  if (log_stream && !debuggerIsActive) log_stream->Put(c);
#else
  if (log_stream) log_stream->Put(c);
#endif
}

void
KernStream::PutBuf(uint8_t *s, uint32_t len)
{
  for (uint32_t i = 0; i < len; i++)
    nl_putc(s[i]);
}

void
KernStream::nl_putc(char c)
{
  if (c == '\n')
    do_putc('\r');

  do_putc(c);
}

#ifdef OPTION_DDB
uint8_t
KernStream::Get()
{
  fatal("Fatal call to KernStream::Get() made.\n");
  return ASCII::SPC;
}

void
KernStream::SetDebugging(bool onOff)
{
  fatal("Fatal call to KernStream::SetDebugging() made.\n");
}
#endif

extern "C" void __pure_virtual(void);
void
__pure_virtual(void)
{
  fatal("pure virtual function called\n");
}

void 
KernStream::BeginUserThreads()
{
  printf("Starting first thread. Console output now disabled...\n");
  syscon_stream = 0;
}



1.1                  eros/src/base/sys/kernel/kern_LogStream.cxx

Index: kern_LogStream.cxx
===================================================================
/*
 * Copyright (C) 2001, Jonathan S. Shapiro.
 *
 * This file is part of the EROS Operating System.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2,
 * or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/* The LogStream is a consolidation of the various previous logging
 * mechanisms.
 */

#include <kerninc/kernel.hxx>
#include <kerninc/util.h>
#include <kerninc/KernStream.hxx>
#include <eros/i486/io.h>

/* This class has a singleton instance! */
struct LogStream: public KernStream {
  void Init();
  void Put(uint8_t c);

#ifdef OPTION_DDB
  uint8_t Get();
  void SetDebugging(bool onoff);
  void EnableDebuggerInput();
#endif
};

struct LogStream TheLogStream;
KernStream* KernStream::LogStream = &TheLogStream;

#define DIAG_BUF_PAGES	1u

static char diagbuf[DIAG_BUF_PAGES * EROS_PAGE_SIZE];

static char *const logbuf = diagbuf;
static char *nextin = diagbuf;
#if 0
/* Eventually to be used for user-level extraction of log output. */
static char *nextout = diagbuf;
#endif
static char *const logtop = diagbuf + (DIAG_BUF_PAGES * EROS_PAGE_SIZE);

void
LogStream::Init()
{
}

void
LogStream::Put(uint8_t c)
{
  if ((unsigned)(nextin - logbuf) > (DIAG_BUF_PAGES * EROS_PAGE_SIZE))
    halt('p');
  
  if (nextin == logtop)
    nextin = logbuf;
  
  *nextin++ = c;
}

#ifdef OPTION_DDB
uint8_t
LogStream::Get()
{
  fatal("LogStream::Get() should never be called!\n");
  return 0;
}

void
LogStream::SetDebugging(bool onOff)
{
  fatal("LogStream::SetDebugging() should never be called!\n");
}

void
LogStream::EnableDebuggerInput()
{
  fatal("LogStream::EnableDebuggerInput() should never be called!\n");
}
#endif



1.1                  eros/src/base/sys/kernel/kern_printf.cxx

Index: kern_printf.cxx
===================================================================
/* 
 * 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.
 */

/* The bulk of this code originated as the Mach debugger printf()
 * code. While I could replace it with the equivalent code from
 * MsgLog::printf(), this version is well tested and mature.
 */

#include <kerninc/kernel.hxx>
#include <kerninc/util.h>
#include <kerninc/KernStream.hxx>
#include <eros/stdarg.h>

#ifdef OPTION_DDB
#include <arch-kerninc/db_machdep.hxx>
#include <ddb/db_command.hxx>
#endif

/*	$NetBSD: db_output.c,v 1.8 1994/06/29 22:41:41 deraadt Exp $	*/

#ifndef	DB_MAX_LINE
#define	DB_MAX_LINE		23	/* maximum line */
#define DB_MAX_WIDTH		80	/* maximum width */
#endif	/* DB_MAX_LINE */

#define DB_MIN_MAX_WIDTH	20	/* minimum max width */
#define DB_MIN_MAX_LINE		3	/* minimum max line */
#define CTRL(c)			((c) & 0xff)

int	db_output_position = 0;		/* output column */
int	db_output_line = 0;		/* output line number */
int	db_last_non_space = 0;		/* last non-space character */
int	db_tab_stop_width = 8;		/* how wide are tab stops? */
#define	NEXT_TAB(i) \
	((((i) + db_tab_stop_width) / db_tab_stop_width) * db_tab_stop_width)
int	db_max_line = DB_MAX_LINE;	/* output max lines */
int	db_max_width = DB_MAX_WIDTH;	/* output line width */


extern void	db_check_interrupt();

#define cngetc() KernStream::dbg_stream->Get()
#define cnputc(c) KernStream::nl_putc(c)

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

#define NBBY 8			/* for all modern machines */

/*
 * Printing
 */
long	db_radix = 10;

/*
 * End line if too long.
 */
void
db_end_line()
{
	if (db_output_position >= db_max_width)
	    printf("\n");
}

#ifdef OPTION_DDB
/*
 * Return output position
 */
int
db_print_position()
{
	return (db_output_position);
}
#endif

/*
 * Put a number (base <= 16) in a buffer in reverse order; return an
 * optional length and a pointer to the NULL terminated (preceded?)
 * buffer.
 */
static char *
db_ksprintn(register u_long ul, register int base, register int *lenp)
{					/* A long in base 8, plus NULL. */
  static char buf[sizeof(long) * NBBY / 3 + 2];
  register char *p;

  p = buf;
  do {
    *++p = "0123456789abcdef"[ul % base];
  } while (ul /= base);
  if (lenp)
    *lenp = p - buf;
  return (p);
}

static char *
db_ll_ksprintn(register unsigned long long ull, register int base,
	    register int *lenp)
{
  static char buf[sizeof(long long) * NBBY / 3 + 2];
  register char *p;

  p = buf;
  do {
    *++p = "0123456789abcdef"[ull % base];
  } while (ull /= base);
  if (lenp)
    *lenp = p - buf;
  return (p);
}

/*
 * Force pending whitespace.
 */
void
db_force_whitespace()
{
	register 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;
}

#ifdef OPTION_DDB
static void
db_more()
{
	register  char *p;
	int quit_output = 0;

	if (KernStream::debuggerIsActive == false) {
	  db_output_line = 0;
	  return;
	}

	for (p = "--db_more--"; *p; p++)
	    cnputc(*p);
	switch(cngetc()) {
	case ' ':
	    db_output_line = 0;
	    break;
	case 'q':
	case CTRL('c'):
	    db_output_line = 0;
	    quit_output = 1;
	    break;
	default:
	    db_output_line--;
	    break;
	}
	p = "\b\b\b\b\b\b\b\b\b\b\b           \b\b\b\b\b\b\b\b\b\b\b";
	while (*p)
	    cnputc(*p++);
	if (quit_output) {
	    db_error(0);
	    /* NOTREACHED */
	}
}
#endif

/*
 * Output character.  Buffer whitespace.
 */
void
db_putchar(int c)
{
#ifdef OPTION_DDB
	if (db_max_line >= DB_MIN_MAX_LINE && db_output_line >= db_max_line-1)
	    db_more();
#endif
	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++;
	    if (db_max_width >= DB_MIN_MAX_WIDTH
		&& db_output_position >= db_max_width-1) {
		/* auto new line */
		cnputc('\n');
		db_output_position = 0;
		db_last_non_space = 0;
		db_output_line++;
	    }
	    db_last_non_space = db_output_position;
	}
	else if (c == '\n') {
	    /* Return */
	    cnputc(c);
	    db_output_position = 0;
	    db_last_non_space = 0;
	    db_output_line++;
#ifdef OPTION_DDB
	    db_check_interrupt();
#endif
	}
	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 */
}

void
db_printf_guts(register const char *fmt, va_list ap)
{
  register char *p;
  register int ch, n;
  u_long ul;
  unsigned long long ull;
  int base, lflag, tmp, width;
  char padc;
  int ladjust;
  int sharpflag;
  int neg;

  for (;;) {
    padc = ' ';
    width = 0;
    while ((ch = *(u_char *)fmt++) != '%') {
      if (ch == '\0')
	return;
      db_putchar(ch);
    }
    lflag = 0;
    ladjust = 0;
    sharpflag = 0;
    neg = 0;
  reswitch:	switch (ch = *(u_char *)fmt++) {
  case '0':
    padc = '0';
    goto reswitch;
  case '1': case '2': case '3': case '4':
  case '5': case '6': case '7': case '8': case '9':
    for (width = 0;; ++fmt) {
      width = width * 10 + ch - '0';
      ch = *fmt;
      if (ch < '0' || ch > '9')
	break;
    }
    goto reswitch;
  case 'l':
    lflag = 1;
    goto reswitch;
  case '-':
    ladjust = 1;
    goto reswitch;
  case '#':
    sharpflag = 1;
    goto reswitch;
  case 'b':
    ul = va_arg(ap, int);
    p = va_arg(ap, char *);
    for (p = db_ksprintn(ul, *p++, NULL); (ch = *p--);)
      db_putchar(ch);

    if (!ul)
      break;

    for (tmp = 0; (n = *p++);) {
      if (ul & (1 << (n - 1))) {
	db_putchar(tmp ? ',' : '<');
	for (; (n = *p) > ' '; ++p)
	  db_putchar(n);
	tmp = 1;
      } else
	for (; *p > ' '; ++p);
    }
    if (tmp)
      db_putchar('>');
    break;
  case '*':
    width = va_arg (ap, int);
    if (width < 0) {
      ladjust = !ladjust;
      width = -width;
    }
    goto reswitch;
  case 'c':
    db_putchar(va_arg(ap, int));
    break;
  case 's':
    p = va_arg(ap, char *);
    width -= strlen (p);
    if (!ladjust && width > 0)
      while (width--)
	db_putchar (padc);
    while ((ch = *p++))
      db_putchar(ch);
    if (ladjust && width > 0)
      while (width--)
	db_putchar (padc);
    break;
  case 'r':
    ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
    if ((long)ul < 0) {
      neg = 1;
      ul = -(long)ul;
    }
    base = db_radix;
    if (base < 8 || base > 16)
      base = 10;
    goto number;
  case 'n':
    ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
    base = db_radix;
    if (base < 8 || base > 16)
      base = 10;
    goto number;
  case 'd':
    ul = lflag ? va_arg(ap, long) : va_arg(ap, int);
    if ((long)ul < 0) {
      neg = 1;
      ul = -(long)ul;
    }
    base = 10;
    goto number;
  case 'o':
    ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
    base = 8;
    goto number;
  case 'u':
    ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
    base = 10;
    goto number;
  case 'U':
    ull = va_arg(ap, unsigned long long);
    base = 10;
    goto ll_number;
  case 'X':
    ull = va_arg(ap, unsigned long long);
    base = 16;
    goto ll_number;
  case 'z':
    ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
    if ((long)ul < 0) {
      neg = 1;
      ul = -(long)ul;
    }
    base = 16;
    goto number;
  case 'x':
    ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
    base = 16;
#if 0
  number:			p = (char *)db_ksprintn(ul, base, &tmp);
    if (sharpflag && ul != 0) {
      if (base == 8)
	tmp++;
      else if (base == 16)
	tmp += 2;
    }
    if (neg)
      tmp++;

    if (!ladjust && width && (width -= tmp) > 0)
      while (width--)
	db_putchar(padc);
    if (neg)
      db_putchar ('-');
    if (sharpflag && ul != 0) {
      if (base == 8) {
	db_putchar ('0');
      } else if (base == 16) {
	db_putchar ('0');
	db_putchar ('x');
      }
    }
    if (ladjust && width && (width -= tmp) > 0)
      while (width--)
	db_putchar(padc);

    while ((ch = *p--))
      db_putchar(ch);
    break;

#endif
  number:                 ull = ul;
  ll_number:		p = (char *)db_ll_ksprintn(ull, base, &tmp);
    if (sharpflag && ull != 0) {
      if (base == 8)
	tmp++;
      else if (base == 16)
	tmp += 2;
    }
    if (neg)
      tmp++;

    if (!ladjust && width && (width -= tmp) > 0)
      while (width--)
	db_putchar(padc);
    if (neg)
      db_putchar ('-');
    if (sharpflag && ull != 0) {
      if (base == 8) {
	db_putchar ('0');
      } else if (base == 16) {
	db_putchar ('0');
	db_putchar ('x');
      }
    }
    if (ladjust && width && (width -= tmp) > 0)
      while (width--)
	db_putchar(padc);

    while ((ch = *p--))
      db_putchar(ch);
    break;
  default:
    db_putchar('%');
    if (lflag)
      db_putchar('l');
    /* FALLTHROUGH */
  case '%':
    db_putchar(ch);
  }
  }
}


/*VARARGS1*/
void
printf(const char *fmt, ...)
{
  va_list	listp;
  va_start(listp, fmt);
  db_printf_guts (fmt, listp);
  va_end(listp);
}

/*VARARGS1*/
void dprintf(unsigned shouldStop, const char* fmt, ...)
{
  va_list	listp;
  va_start(listp, fmt);
  db_printf_guts (fmt, listp);
  va_end(listp);

  if (shouldStop) Debugger();
}

/*VARARGS1*/
void
fatal(const char *fmt, ...)
{
  va_list	listp;
  va_start(listp, fmt);
  db_printf_guts (fmt, listp);
  va_end(listp);

#ifdef OPTION_DDB
  Debugger();
#else
#if 0
  Debug::Backtrace(0, false);
  
  /* This routine will never return. I personally guarantee it! */
  Flush();
#endif
  halt('F');
#endif
}

void printOid(OID oid)
{
  printf("0x%04x%08x", (uint32_t) (oid >> 32), (uint32_t) oid);
}

void printCount(ObCount count)
{
  printf("0x%08x", count);
}



1.1                  eros/src/base/sys/kerninc/KernStream.hxx

Index: KernStream.hxx
===================================================================
#ifndef __KERNSTREAM_HXX__
#define __KERNSTREAM_HXX__
/*
 * Copyright (C) 2001, Jonathan S. Shapiro.
 *
 * This file is part of the EROS Operating System.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2,
 * or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <kerninc/kernel.hxx>

/** KernStream is really an interface specification. The kernel
 * maintains three KernStreams: ConsoleStream, SerialStream,
 * LogStream. LogStream is global, and present in all
 * kernels. ConsoleStream is present if a console is
 * configured. SerialStream is present if the kernel debugger has been
 * configured for serial mode.
 *
 * Pointers to the ConsoleStream and SerialStream objects are in
 * turn conditionally assigned to two variables syscon_stream and
 * dbg_stream.
 */

struct ASCII {
  enum  {
    NUL = 0x00,
    SOH = 0x01,
    STX = 0x02,
    ETX = 0x03,
    EOT = 0x04,
    ENQ = 0x05,
    ACK = 0x06,
    BEL = 0x07,
    BS  = 0x08,
    TAB = 0x09,
    LF  = 0x0a,
    VT  = 0x0b,
    FF  = 0x0c,
    CR  = 0x0d,
    SO  = 0x0e,
    SI  = 0x0f,
    DLE = 0x10,
    DC1 = 0x11,
    DC2 = 0x12,
    DC3 = 0x13,
    DC4 = 0x14,
    NAK = 0x15,
    SYN = 0x16,
    ETB = 0x17,
    CAN = 0x18,
    EM  = 0x19,
    SUB = 0x1a,
    ESC = 0x1b,
    FS  = 0x1c,
    GS  = 0x1d,
    RS  = 0x1e,
    US  = 0x1f,
    SPC = 0x20,
    DEL = 0x7f,
  };
};

struct KernStream {
  virtual void Init() = 0;
  virtual void Put(uint8_t c) = 0;

#ifdef OPTION_DDB
  /* Following only called via dbg_stream */
  virtual uint8_t Get() = 0;
  virtual void SetDebugging(bool onoff) = 0;
  virtual void EnableDebuggerInput() = 0;
#endif

  /*** END OF MEMBERS -- REST IS JUST NAMESPACE TRICKERY ***/

#ifdef OPTION_DDB
  static bool debuggerIsActive;
#endif

  /* I would like to make these references, but the global constructor
   * code generator is screwing up the pointer initialization. */
  static KernStream* LogStream;
  static KernStream* ConsoleStream;
#ifdef OPTION_DDB_ON_TTY0
  static KernStream* SerialStream;
#endif


#ifdef OPTION_DDB
  static KernStream *dbg_stream;
#endif
  static KernStream *syscon_stream;
  static KernStream *log_stream;

  static bool IsPrint(uint8_t c);
  static void KernStream::InitStreams();

  static void PutBuf(uint8_t *s, uint32_t len);

  static void nl_putc(char c);
  static void do_putc(char c);

  static void BeginUserThreads();
};

#endif /* __KERNSTREAM_HXX__ */