// MSTREAM.CPP
// Defines the classes required to 
// access a secondary monitor for 
// debugging.

#ifdef _Windows
    #include <windows.h>
    extern WORD _B000H;        // pointer to global selector
#else
    #include <dos.h>
#endif

#include "mstream.h"

#define CPL 80                 // Characters Per Line
#define LPP 25                 // Lines Per Page
#define ATTR 0x700             // white on black character attribute

mstreambuf::mstreambuf(char* s, int n) : streambuf(s,n)
{
    setp(base(),ebuf());       // Initialize the put pointers
    if ( screen == 0 ) {       // screen pointer needs to be initialized
#ifdef _Windows                // Windows will use the predefined global selector
        screen = (unsigned int far*) MAKELONG( 0, &_B000H );
#else                          // DOS will point directly to the MDA memory area
        screen = (unsigned int far*) MK_FP( 0xB000, 0 );
#endif
                               // first time thru we clear the screen
        for ( int i = 0; i < (CPL*LPP); i++ )
            screen[i] = ' ' + ATTR;
        column = row = 0;      // reset the current screen character position
    }
}

mstreambuf::~mstreambuf()
{
    sync();                    // flush the put buffer before we leave.
}

void
mstreambuf::newline()
// This routine generates a new line on the mda monitor. It will scroll
// all of the lines on the screen up one line and erases the bottom line
// if it needs it.
{
    column = 0;                // reset the current column position
    row++;                     // increment the current row pointer
    if ( row == LPP ) {        // are we at the bottom of the screen?
                               // move lines 2 thru 25 (LPP) up one line
        _fmovmem( &screen[CPL], &screen[0], (CPL*(LPP-1))*2 );
                               // clear the last line
        for ( int i = CPL*(LPP-1); i < (CPL*LPP); i++ )
            screen[i] = ATTR + ' ';
        row--;                 // decrement the current row pointer
    }
}

int
mstreambuf::overflow( int nChar )
// This routine empties the put 
// area and consumes the "nChar" 
// character if it is not EOF.
{
    sync();                     // flush the buffer
    if ( nChar != EOF )         // is there a character to consume?
        return sputc( nChar );  // store it in the put area, returning the results
    else
        return 0;               // return success otherwise
}

int
mstreambuf::sync()
// This routine flushes the put area.
{
    int count = out_waiting();  // get the number of characters in the put area
    if ( count )    {
        for ( int i=0; i< count; i++ ) {
            char c = pbase()[i]; // get each character and send to mda
            if ( column == CPL ) // are we at the end of the line?
                newline();       // then generate a CR/LF
            if ( c == '\n' )     // is the character a CR/LF?
                newline();       // then generate a CR/LF here as well
            else                 // send character and attribute to monitor
                screen[(CPL*row) + column++] = c + ATTR;
        }
        setp(base(),ebuf());     // reset the put area pointers
    }
   return 0;
}

mstream::mstream() : ostream()
// The class constructor. It will 
// create a new streambuf using the 
// classes character buffer.
{
    strbuf = new mstreambuf(msgs,bsize);
                                 // do the actual initialization and associate
                                 // strbuf with ostream
    ostream::init(strbuf);
                                 // set flag so that the stream to be flushed after
                                 // each insertion
    setf(ios::unitbuf);
}

mstream::~mstream()
// The class destructor.
{
    delete strbuf;                // delete the streambuf used by the class.
}

// static class member allocation. 
// This allocates space and initializes.
unsigned int far* mstreambuf::screen=0; // mda screen memory area
int mstreambuf::column=0;               // current screen column position
int mstreambuf::row=0;                  // current screen line position

#ifndef __DLL__
    mstream mout; // create a global instance of mstream for everyone to use.
#endif


