/***********************************************************************************
 **
 ** Atari++ emulator (c) 2002 THOR-Software, Thomas Richter
 **
 ** $Id: interfacebox.cpp,v 1.31 2014/03/16 14:54:08 thor Exp $
 **
 ** In this module: Emulation of the 850 interface box.
 **********************************************************************************/

/// Includes
#include "types.h"
#include "types.hpp"
#include "new.hpp"
#include "unistd.hpp"
#include "sio.hpp"
#include "interfacebox.hpp"
#include "machine.hpp"
#include "monitor.hpp"
#include "snapshot.hpp"
#include "serialstream.hpp"
///

/// Boot850
// The 850 boot code
static const UBYTE Boot850[] = 
{
	0x00,0x03,0x00,0x05,0x65,0x05,0xa2,0x03,0xbd,0x76,0x05,0x9d,0x00,0x03,0xca,0x10,
	0xf7,0xad,0xe7,0x02,0x8d,0x04,0x03,0xad,0xe8,0x02,0x8d,0x05,0x03,0xa9,0x08,0x8d,
	0x06,0x03,0xad,0x00,0x06,0x8d,0x08,0x03,0xad,0x01,0x06,0x8d,0x09,0x03,0x20,0x59,
	0xe4,0x30,0x41,0xad,0xe7,0x02,0x85,0x32,0xad,0xe8,0x02,0x85,0x33,0xa2,0x00,0xbd,
	0x02,0x06,0xf0,0x21,0x18,0x65,0x32,0x85,0x32,0x90,0x02,0xe6,0x33,0xa0,0x00,0x18,
	0xb1,0x32,0x6d,0xe7,0x02,0x91,0x32,0xc8,0xb1,0x32,0x6d,0xe8,0x02,0x38,0xe9,0x07,
	0x91,0x32,0xe8,0xd0,0xda,0xa5,0x08,0xf0,0x08,0xad,0xf5,0x03,0x09,0x01,0x8d,0xf5,
	0x03,0x6c,0xe7,0x02,0x38,0x60,0x50,0x01,0x26,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0xef,0x05,0x01,0x07,0x05,0x03,0x05,0x05,0x1c,0x03,0x09,0x03,0x07,0x03,0x05,0x0f,
	0x03,0x09,0x03,0x1a,0x03,0x0a,0x03,0x32,0x05,0x05,0x05,0x12,0x15,0x0c,0x06,0x13,
	0x07,0x03,0x0d,0x05,0x03,0x05,0x04,0x03,0x03,0x03,0x03,0x0b,0x08,0x0d,0x0e,0x05,
	0x05,0x05,0x10,0x14,0x03,0x05,0x04,0x08,0x0d,0x09,0x15,0x04,0x0c,0x03,0x07,0x0a,
	0x05,0x07,0x09,0x22,0x08,0x07,0x14,0x09,0x25,0x0a,0x09,0x07,0x0c,0x08,0x05,0x05,
	0x03,0x09,0x08,0x07,0x05,0x0f,0x03,0x0c,0x08,0x05,0x07,0x03,0x06,0x11,0x03,0x08,
	0x03,0x1e,0x18,0x09,0x06,0x13,0x04,0x08,0x07,0x17,0x05,0x19,0x05,0x05,0x05,0x13,
	0x03,0x08,0x03,0x0e,0x06,0x06,0x0a,0x0d,0x0e,0x03,0x09,0x07,0x05,0x04,0x06,0x09,
	0x05,0x0d,0x0c,0x09,0x0b,0x03,0x03,0x03,0x05,0x03,0x06,0x03,0x06,0x05,0x03,0x08,
	0x03,0x03,0x1e,0x06,0x08,0x03,0x03,0x06,0x06,0x17,0x03,0x05,0x02,0x02,0x02,0x02,
	0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,
	0x02,0x00
};
///

/// Handler850
// The 850 handler code
static const UBYTE Handler850[] = 
{
	0x20,0xf4,0x0b,0xb0,0x18,0xa5,0x0c,0x8d,0x95,0x0c,0xa5,0x0d,0x8d,0x96,0x0c,0xad,
	0x82,0x0c,0x85,0x0c,0xad,0x83,0x0c,0x85,0x0d,0x20,0x31,0x0c,0x18,0x60,0xa5,0x11,
	0xf0,0x5f,0x98,0x48,0xa0,0x00,0xad,0x0d,0xd2,0x91,0x32,0xad,0x0f,0xd2,0x8d,0x0a,
	0xd2,0x49,0xff,0x29,0xc0,0x0d,0x99,0x0c,0x8d,0x99,0x0c,0x68,0xa8,0x8a,0x48,0xa2,
	0x00,0x20,0xc5,0x07,0x20,0xba,0x07,0xd0,0x0f,0xa2,0x02,0x20,0xc5,0x07,0xad,0x99,
	0x0c,0x09,0x10,0x8d,0x99,0x0c,0xd0,0x06,0xe6,0x36,0xd0,0x02,0xe6,0x37,0x68,0xaa,
	0x68,0x40,0xad,0x9b,0x0c,0xcd,0x9a,0x0c,0xd0,0x1b,0xa9,0xe7,0xd0,0x0a,0xad,0x9b,
	0x0c,0xcd,0x9a,0x0c,0xd0,0x0f,0xa9,0xef,0x25,0x10,0x85,0x10,0x8d,0x0e,0xd2,0x68,
	0x40,0xa9,0xc7,0xd0,0xf3,0xa5,0x11,0xf0,0xf8,0x98,0x48,0xac,0x9b,0x0c,0xb9,0xaf,
	0x0c,0x8d,0x0d,0xd2,0xc8,0x98,0x29,0x1f,0x8d,0x9b,0x0c,0xce,0x9c,0x0c,0x68,0xa8,
	0x68,0x40,0xa6,0x21,0xe0,0x01,0xf0,0x04,0x68,0x68,0xa0,0xa0,0x60,0xa2,0x00,0x4a,
	0x90,0x01,0xe8,0xc9,0x00,0xd0,0xf8,0x8a,0x4a,0x60,0xa5,0x32,0xc5,0x34,0xd0,0x04,
	0xa5,0x33,0xc5,0x35,0x60,0xf6,0x32,0xd0,0x02,0xf6,0x33,0xb5,0x32,0xcd,0xa4,0x0c,
	0xb5,0x33,0xed,0xa5,0x0c,0x90,0x0a,0xad,0xa2,0x0c,0x95,0x32,0xad,0xa3,0x0c,0x95,
	0x33,0x60,0xa4,0x2a,0x8c,0x0a,0x03,0xa4,0x2b,0x8c,0x0b,0x03,0xa0,0x00,0x2c,0x98,
	0x0c,0x50,0x19,0x8d,0x02,0x03,0x8c,0x03,0x03,0xa2,0x01,0x8e,0x01,0x03,0xa0,0x50,
	0x8c,0x00,0x03,0xad,0x86,0x0c,0x8d,0x06,0x03,0x4c,0x59,0xe4,0xa0,0x99,0x60,0xad,
	0x78,0x0c,0x8d,0x04,0x03,0xad,0x79,0x0c,0x8d,0x05,0x03,0xa9,0x40,0x8d,0x08,0x03,
	0xa9,0x00,0x8d,0x09,0x03,0x8d,0x0b,0x03,0xad,0x9a,0x0c,0x8d,0x0a,0x03,0x60,0x20,
	0xa2,0x07,0xad,0x97,0x0c,0x10,0x4b,0xa4,0x2a,0x98,0x29,0x0c,0xf0,0x47,0x98,0x8d,
	0x97,0x0c,0xa0,0xff,0x8c,0x98,0x0c,0x8c,0xa1,0x0c,0xa9,0x20,0x8d,0x9e,0x0c,0xc8,
	0x8c,0x9a,0x0c,0x8c,0x99,0x0c,0x8c,0x9f,0x0c,0x8c,0x9d,0x0c,0x8c,0xa0,0x0c,0x8c,
	0x0a,0x03,0x8c,0x0b,0x03,0xa9,0x57,0x20,0xee,0x07,0x30,0x1b,0x88,0xa9,0x42,0x20,
	0xee,0x07,0x30,0x13,0xa9,0xff,0x8d,0x0a,0x03,0x88,0xa9,0x41,0x20,0xee,0x07,0x30,
	0x06,0x2c,0xa0,0x96,0x2c,0xa0,0xb1,0x60,0xa0,0x01,0xad,0x97,0x0c,0x30,0x39,0x2c,
	0x98,0x0c,0x50,0x0c,0xad,0x9a,0x0c,0xf0,0x1a,0x20,0x0f,0x08,0xa0,0x80,0x30,0x18,
	0xa5,0x10,0x29,0x08,0xd0,0xfa,0x78,0xa2,0x05,0xbd,0x8f,0x0c,0x9d,0x0a,0x02,0xca,
	0x10,0xf7,0x58,0xa0,0x00,0x8c,0x0a,0x03,0x8c,0x03,0x03,0xa2,0xff,0x8e,0x98,0x0c,
	0x8e,0x97,0x0c,0xa9,0x57,0x20,0xee,0x07,0x60,0x2c,0x98,0x0c,0x50,0x06,0xa0,0x9a,
	0x60,0x4c,0x04,0x0a,0xa5,0x11,0xf0,0xf9,0xa5,0x10,0x29,0x20,0xd0,0x03,0x20,0xbe,
	0x0a,0x58,0xa5,0x11,0xf0,0xeb,0x78,0x20,0xba,0x07,0xf0,0xf5,0xa0,0x00,0xb1,0x34,
	0x85,0x2f,0xa5,0x36,0xd0,0x02,0xc6,0x37,0xc6,0x36,0xa2,0x02,0x20,0xc5,0x07,0x58,
	0xad,0x9d,0x0c,0x29,0x0c,0xf0,0x2e,0xc9,0x0c,0xf0,0x21,0xa8,0xad,0xa0,0x0c,0x2d,
	0xa1,0x0c,0x49,0xff,0x25,0x2f,0x20,0xad,0x07,0x98,0x29,0x08,0xf0,0x0c,0x90,0x0c,
	0xad,0x99,0x0c,0x09,0x20,0x8d,0x99,0x0c,0xd0,0x02,0x90,0xf4,0xad,0xa0,0x0c,0x49,
	0xff,0x25,0x2f,0x85,0x2f,0xad,0x9d,0x0c,0x29,0x30,0xc9,0x20,0xa8,0xa5,0x2f,0xb0,
	0x19,0xc9,0x0d,0xd0,0x04,0xa9,0x9b,0xd0,0x11,0xc0,0x00,0xf0,0x0d,0x29,0x7f,0xc9,
	0x20,0x90,0x04,0xc9,0x7d,0x90,0x03,0xad,0x9e,0x0c,0xa0,0x01,0x60,0x85,0x2f,0xad,
	0x97,0x0c,0x29,0x08,0xf0,0x3d,0xad,0x9d,0x0c,0x29,0x30,0xa8,0xf0,0x06,0xa5,0x2f,
	0xc0,0x20,0xb0,0x32,0xa5,0x2f,0xc9,0x9b,0xd0,0x16,0xad,0x9d,0x0c,0x29,0x40,0xf0,
	0x0b,0xa9,0x0d,0x20,0xa6,0x09,0x30,0x1d,0xa9,0x0a,0xd0,0x1a,0xa9,0x0d,0xd0,0x16,
	0xc0,0x10,0xf0,0x04,0x29,0x7f,0x10,0x0e,0xc9,0x20,0x90,0x04,0xc9,0x7d,0x90,0x06,
	0xa0,0x01,0x2c,0xa0,0x87,0x60,0x85,0x2f,0xad,0x9d,0x0c,0x29,0x03,0xf0,0x27,0xa8,
	0xa5,0x2f,0x0d,0xa0,0x0c,0x85,0x2f,0xc0,0x03,0xf0,0x1b,0xad,0xa0,0x0c,0x49,0xff,
	0x25,0x2f,0x20,0xad,0x07,0x98,0x29,0x01,0xd0,0x0a,0xb0,0x0a,0xa5,0x2f,0x2d,0xa1,
	0x0c,0x85,0x2f,0x18,0xb0,0xf6,0x2c,0x98,0x0c,0x50,0x3f,0xac,0x9a,0x0c,0xa5,0x2f,
	0x99,0xaf,0x0c,0xee,0x9a,0x0c,0xc9,0x0d,0xf0,0x0a,0xa9,0x20,0xcd,0x9a,0x0c,0xf0,
	0x03,0xa0,0x01,0x60,0x20,0x0f,0x08,0xa0,0x80,0xa9,0x57,0x20,0xee,0x07,0xa9,0x00,
	0x8d,0x9a,0x0c,0x60,0xa5,0x10,0x29,0xc7,0x85,0x10,0x8d,0x0e,0xd2,0xa9,0x00,0x8d,
	0x9a,0x0c,0x8d,0x9b,0x0c,0xa0,0x80,0x84,0x11,0x60,0xa5,0x11,0xf0,0xe6,0xad,0x9c,
	0x0c,0xc9,0x1f,0xb0,0xf5,0x78,0xac,0x9a,0x0c,0xa5,0x2f,0x99,0xaf,0x0c,0xc8,0x98,
	0x29,0x1f,0x8d,0x9a,0x0c,0xee,0x9c,0x0c,0x58,0xa5,0x10,0xac,0x9f,0x0c,0x30,0x02,
	0x09,0x10,0x09,0x08,0x85,0x10,0x8d,0x0e,0xd2,0xa0,0x01,0x60,0x20,0xa2,0x07,0xad,
	0x99,0x0c,0x8d,0xea,0x02,0xa0,0x00,0x8c,0x99,0x0c,0x2c,0x98,0x0c,0x50,0x26,0x48,
	0xa9,0xea,0x8d,0x04,0x03,0xa9,0x02,0x8d,0x05,0x03,0xa9,0x02,0x8d,0x08,0x03,0xa9,
	0x00,0x8d,0x09,0x03,0xa0,0x40,0xa9,0x53,0x20,0xee,0x07,0x68,0x0d,0xea,0x02,0x8d,
	0xea,0x02,0xc0,0x00,0x60,0x78,0xa5,0x36,0x8d,0xeb,0x02,0xa5,0x37,0x8d,0xec,0x02,
	0xad,0x9c,0x0c,0x8d,0xed,0x02,0x58,0xc8,0x60,0xad,0x97,0x0c,0x85,0x2a,0x60,0x20,
	0xa2,0x07,0xa5,0x22,0xc9,0x20,0x90,0x13,0xc9,0x29,0xb0,0x0f,0x29,0x0f,0xa8,0x4a,
	0xb0,0x09,0xb9,0x6b,0x0c,0x48,0xb9,0x6a,0x0c,0x48,0x60,0xa0,0x84,0x60,0xad,0xa2,
	0x0c,0x85,0x32,0x85,0x34,0xad,0xa3,0x0c,0x85,0x33,0x85,0x35,0xa9,0x00,0x85,0x36,
	0x85,0x37,0xa5,0x10,0x09,0x20,0x85,0x10,0x8d,0x0e,0xd2,0x60,0xad,0x97,0x0c,0x30,
	0x36,0x2c,0x98,0x0c,0x50,0x2e,0x4a,0x90,0x28,0x4a,0x4a,0x90,0x38,0xa5,0x2a,0xf0,
	0x29,0xa5,0x28,0x05,0x29,0xf0,0x23,0x18,0xa5,0x24,0x8d,0xa2,0x0c,0x65,0x28,0x8d,
	0xa4,0x0c,0xa5,0x25,0x8d,0xa3,0x0c,0x65,0x29,0x8d,0xa5,0x0c,0x90,0x17,0xa0,0x98,
	0x2c,0xa0,0x97,0x2c,0xa0,0x99,0x2c,0xa0,0x85,0x60,0xa2,0x03,0xbd,0x74,0x0c,0x9d,
	0xa2,0x0c,0xca,0x10,0xf7,0xa9,0x00,0x8d,0x9a,0x0c,0x8d,0x9b,0x0c,0x8d,0x09,0x03,
	0x8d,0x0b,0x03,0xa9,0x09,0x8d,0x08,0x03,0xad,0x7a,0x0c,0x8d,0x04,0x03,0xad,0x7b,
	0x0c,0x8d,0x05,0x03,0xad,0x97,0x0c,0x8d,0x0a,0x03,0xa0,0x40,0xa9,0x58,0x20,0xee,
	0x07,0x30,0xc6,0x78,0xa9,0x73,0x8d,0x0f,0xd2,0xa2,0x08,0xbd,0xa6,0x0c,0x9d,0x00,
	0xd2,0xca,0x10,0xf7,0xa2,0x05,0xbd,0x0a,0x02,0x9d,0x8f,0x0c,0xbd,0x7c,0x0c,0x9d,
	0x0a,0x02,0xca,0x10,0xf1,0xad,0x97,0x0c,0x29,0x04,0xf0,0x03,0x20,0xbe,0x0a,0xa2,
	0x00,0x8e,0x98,0x0c,0x58,0x20,0x99,0x0a,0xa0,0x01,0x60,0xad,0x97,0x0c,0x30,0x20,
	0x29,0x08,0xf0,0x19,0xad,0x9a,0x0c,0xf0,0x11,0x2c,0x98,0x0c,0x70,0x08,0xa5,0x10,
	0x29,0x08,0xd0,0xfa,0xf0,0x04,0x20,0xf4,0x09,0x2c,0xa0,0x01,0x2c,0xa0,0x87,0x2c,
	0xa0,0x85,0x20,0x99,0x0a,0x60,0xa5,0x2a,0xa8,0x29,0x80,0x8d,0x9f,0x0c,0x98,0x4a,
	0x4a,0x4a,0x4a,0x29,0x03,0xaa,0xbd,0x87,0x0c,0x8d,0xa0,0x0c,0xbd,0x8b,0x0c,0x8d,
	0xa1,0x0c,0xa9,0x42,0x20,0xe2,0x07,0x20,0x99,0x0a,0x60,0xa9,0x41,0x20,0xe2,0x07,
	0x20,0x99,0x0a,0x60,0xa5,0x2a,0x8d,0x9d,0x0c,0xa5,0x2b,0x8d,0x9e,0x0c,0x20,0x99,
	0x0a,0xa0,0x01,0x60,0xa0,0xff,0x8c,0x98,0x0c,0x8c,0x97,0x0c,0x20,0x5f,0x0a,0x30,
	0x12,0xa2,0x00,0xbd,0x1a,0x03,0xf0,0x0d,0xc9,0x52,0xf0,0x07,0xe8,0xe8,0xe8,0xe0,
	0x20,0x90,0xf0,0x38,0x60,0xa9,0x52,0x9d,0x1a,0x03,0xad,0x68,0x0c,0x9d,0x1b,0x03,
	0xad,0x69,0x0c,0x9d,0x1c,0x03,0x18,0x60,0x20,0xf4,0x0b,0x20,0x31,0x0c,0x20,0x51,
	0x0c,0xad,0xe7,0x02,0xcd,0x84,0x0c,0xad,0xe8,0x02,0xed,0x85,0x0c,0xb0,0x10,0xa9,
	0xef,0x6d,0xe7,0x02,0x8d,0xe7,0x02,0xa9,0x05,0x6d,0xe8,0x02,0x8d,0xe8,0x02,0x18,
	0x60,0xad,0x95,0x0c,0x0d,0x96,0x0c,0xf0,0xf7,0x6c,0x95,0x0c,0x2e,0x08,0x87,0x08,
	0xc8,0x08,0x5c,0x09,0x4b,0x0a,0x9e,0x0a,0x5c,0x0c,0x8a,0x0b,0xda,0x0b,0xb5,0x0b,
	0xe3,0x0b,0xdb,0x0a,0xcf,0x0c,0xef,0x0c,0xaf,0x0c,0xa6,0x0c,0x1e,0x07,0x6e,0x07,
	0x62,0x07,0x28,0x0c,0xef,0x0c,0x08,0x00,0x80,0xc0,0xe0,0xff,0x7f,0xbf,0xdf,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
///

/// InterfaceBox::InterfaceBox
// Setup the printer interface here.
InterfaceBox::InterfaceBox(class Machine *mach)
  : SerialDevice(mach,"850",'P'), 
    Saveable(mach,"850"),
    SerialStream(NULL),
    DeviceName(new char[11]), BoxOn(false)
{ 
  strcpy(DeviceName,SerialStream::SuggestName());

  //
  // Initial state of DTR: Off: The terminal is not ready
  DTRState         = false;
  // Initial state of RTS: Off: No sending requested
  RTSState         = false;
  // Initial state of XMT: Well, we cannot really control this.
  XMTState         = false;
  // Handshaking parameters: Since the 850 does not have any kind
  // of hardware handshake, keep this disabled.
  // The 850 checks the state of these lines only when entering
  // the concurrent mode, not otherwise.
  DSRHandshake     = false;
  CTSHandshake     = false;
  CRXHandshake     = false;
  DSRState         = false;
  CTSState         = false;
  CDState          = false;
  StopBits         = 1;
  DataBits         = 8;
  BaudRate         = 300;  
  // Clear all the error flags
  FramingError     = false;
  ByteOverrun      = false;
  ParityError      = false;
  BufferOverrun    = false;
  DevError         = false;
  ConcurrentActive = false;  
  // Reset error counters
  FramingErrorCnt  = 0;
  ByteOverrunCnt   = 0;
  ParityErrorCnt   = 0;
  BufferOverrunCnt = 0;
}
///

/// InterfaceBox::~InterfaceBox
// Cleanup the printer. Dispose the timer and the print buffer.
InterfaceBox::~InterfaceBox(void)
{
  if (SerialStream)
    delete SerialStream;
  delete[] DeviceName;
}
///

/// InterfaceBox::OpenChannel
// Open the serial device file descriptor
// and install the parameters. Must get
// called before using the file descriptor.
void InterfaceBox::OpenChannel(void)
{
  if (DevError == false && (SerialStream == NULL || !SerialStream->isOpen())) {
    if (SerialStream) {
      delete SerialStream;
      SerialStream = NULL;
    }
    SerialStream = new class SerialStream;
    SerialStream->Open(DeviceName);
    // State line history must now be all false
    DSRState     = false;
    CTSState     = false;
    CDState      = false;
    // In case of an error, do not report this, but rather
    // simulate an apropriate serial error. Output is then silently
    // ignored, input does not happen. This simulates a non-connected
    // device.
    if (!SerialStream->isOpen()) {
      machine->PutWarning("Unable to open the serial device %s, "
			  "emulating a non-connected port.\n",DeviceName);
      DevError = true;
    } else {    
      // Now install the serial parameters by termios.  
      // Reset error counters
      FramingErrorCnt  = 0;
      ByteOverrunCnt   = 0;
      ParityErrorCnt   = 0;
      BufferOverrunCnt = 0;
      InstallParameters();
      SetModemLines();
      // Reset errors to get the current error counters
      UpdateErrors();
      // Reset errors
      FramingError     = false;
      ByteOverrun      = false;
      ParityError      = false;
      BufferOverrun    = false;
    }
  }
}
///

/// InterfaceBox::SignalDeviceError
// Signal a device error and close the channel,
// first sending the warning text to the user
void InterfaceBox::SignalDeviceError(const char *msg)
{
  if (SerialStream) {
    delete SerialStream;
    SerialStream = NULL;
    DevError = true;
  }
  machine->PutWarning("%s",msg);
}
///

/// InterfaceBox::InstallParameters
// By using termios calls, install or modify
// the requested parameters.
void InterfaceBox::InstallParameters(void)
{
  // Ignore silently if the stream is not yet open.
  if (SerialStream == NULL || !SerialStream->isOpen())
    return;
  //
  if (!SerialStream->SetDataBits(DataBits)) {
    SignalDeviceError("Unable to set the number of data bits "
		      "for serial device emulation, "
		      "emulating a non-connected port now.\n");
    return;
  }
  if (!SerialStream->SetStopBits(StopBits)) {
    SignalDeviceError("Unable to set the number of stop bits "
		      "for serial device emulation, "
		      "emulating a non-connected port now.\n");
    return;
  }
  if (!SerialStream->SetHardwareHandshake(CTSHandshake)) {
    SignalDeviceError("Unable to set the hardware handshake "
		      "for serial device emulation, "
		      "emulating a non-connected port now.\n");
    return;
  }
  if (!SerialStream->SetBaudRate(BaudRate)) {
    SignalDeviceError("Unable to set the baud rate "
		      "for serial device emulation, "
		      "emulating a non-connected port now.\n");
    return;
  }
}
///

/// InterfaceBox::SetModemLines
// Install the requested parameters of the modem
// lines if possible. Not all systems may support
// this, though.
void InterfaceBox::SetModemLines(void)
{
  if (SerialStream && SerialStream->isOpen()) {
    SerialStream->SetDTRState(DTRState);
    SerialStream->SetRTSState(RTSState);
  }
}
///

/// InterfaceBox::MonitorModemLines
// Monitor the selected modem lines and return
// false in case they are not set.
bool InterfaceBox::MonitorModemLines(void)
{
  if (SerialStream && SerialStream->isOpen()) {
    // Otherwise, don't bother and signal "fine" on all lines.
    if (!SerialStream->GetCTSState(CTSState))
      CTSState = true;
    if (!SerialStream->GetDSRState(DSRState))
      DSRState = true;
    if (!SerialStream->GetCDState(CDState))
      CDState  = true;
  } else {
    DSRState = false;
    CTSState = false;
    CDState  = false;
  }
  // Now check DSR, CTS, CD. 
  if (CTSHandshake && CTSState == false) {
    return false;
  }
  if (DSRHandshake && DSRState == false) {
    return false;
  }
  if (CRXHandshake && CDState  == false) {
    return false;
  }
  return true;
}
///

/// InterfaceBox::UpdateErrors
// Update the error state box by reading
// the current/active error counters from
// the ttyS device.
void InterfaceBox::UpdateErrors(void)
{
  if (SerialStream && SerialStream->isOpen()) {
    int frame,overrun,parity,buf_overrun;
    if (SerialStream->GetFramingErrors(frame)) {
      // Worked. Compare the error counters here with
      // the accumulated errors, then update error
      // flags.
      if (frame > FramingErrorCnt) {
	FramingErrorCnt  = frame;
	FramingError     = true;
      }
    }
    if (SerialStream->GetPortOverrunErrors(overrun)) {
      if (overrun > ByteOverrunCnt) {
	ByteOverrunCnt   = overrun;
	ByteOverrun      = true;
      }
    }
    if (SerialStream->GetParityErrors(parity)) {
      if (parity > ParityErrorCnt) {
	ParityErrorCnt   = parity;
	ParityError      = true;
      }
    }
    if (SerialStream->GetBufferOverrunErrors(buf_overrun)) {
      if (buf_overrun > BufferOverrunCnt) {
	BufferOverrunCnt = buf_overrun;
	BufferOverrun    = true;
      }
    }
  }
}
///

/// InterfaceBox::ReadDCB
// Read the boot parameters for the 850 interface
// and place them into the buffer. This returns
// the twelve bytes to be placed into the DCB for
// booting the boot code.
UBYTE InterfaceBox::ReadDCB(UBYTE *buffer)
{
  buffer[0x00] = 0x50;                   // device
  buffer[0x01] = 0x01;                   // unit
  buffer[0x02] = '!';                    // the read-boot-code command
  buffer[0x03] = 0x40;                   // the transfer direction: into the computer
  buffer[0x04] = (0x0500) & 0xff;        // the position of the boot code: page 5
  buffer[0x05] = (0x0500) >> 8;          // high-byte
  buffer[0x06] = 2;                      // timeout
  buffer[0x07] = 0;                      // this does not matter
  buffer[0x08] = sizeof(Boot850) & 0xff; // size, low-byte
  buffer[0x09] = sizeof(Boot850) >> 8;   // high-byte
  buffer[0x0a] = 0;                      // Aux1 does not matter
  buffer[0x0b] = 0;                      // Aux2 later

  return 'C';
}
///

/// InterfaceBox::ReadBootCode
// Provide the boot code of the 850 interface
UBYTE InterfaceBox::ReadBootCode(UBYTE *buffer)
{
  // This copies the boot code into the host
  // system. The SIO buffer must be large
  // enough.
  memcpy(buffer,Boot850,sizeof(Boot850));
  //
  return 'C';
}
///

/// InterfaceBox::ReadHandler
// This bootstraps the handler main code
// which gets relocated by the boot code
UBYTE InterfaceBox::ReadHandler(UBYTE *buffer)
{
  // Copies the handler code into the
  // host system. Again, the SIO buffer
  // must be large enough.
  memcpy(buffer,Handler850,sizeof(Handler850));
  //
  return 'C';
}
///

/// InterfaceBox::SendData
// Send data out to the serial stream
// Returns an error indicator, possibly.
UBYTE InterfaceBox::SendData(const UBYTE *data,int size)
{
  // If the data size is zero, then this is an indicator
  // that the buffer is complete and we need to leave
  // the concurrent mode.
  if (size == 0) {
    ConcurrentActive = false;
    delete SerialStream;
    SerialStream = NULL;
    return 'C';
  } else {
    // Otherwise, we really have to write data out in block
    // mode. Open the device if not done so already and
    // try to write the buffer. We are open in block
    // mode and hence return as soon as all data has been
    // written.
    OpenChannel();
    if (SerialStream && SerialStream->isOpen()) {
      int written;
      written = SerialStream->Write(data,size);
      // Update the error counters/flags
      if (written == size) {
	return 'C';
      }
      return 'E';
    }
    // Otherwise, we emulate an "unconnected" port and ignore
    // the write silently.
    return 'C';
  }
}
///

/// InterfaceBox::ConcurrentRead
// Check whether we have a concurrent byte to deliver. Return true and the byte
// if so. Return false otherwise.
bool InterfaceBox::ConcurrentRead(UBYTE &data)
{
  if (ConcurrentActive && BoxOn) {
    OpenChannel();
    if (SerialStream && SerialStream->isOpen()) {
      int res;
      // The termios has been configured such that read only
      // returns the available bytes here and does not block.
      res = SerialStream->Read(&data,sizeof(data));
      // Check whether we have something here.
      if (res == sizeof(data)) {
	return true;
      }
    }
  }
  return false;
}
///

/// InterfaceBox::ConcurrentWrite
// Check whether this device is able to accept a concurrently written byte. 
// Return true if so, and then deliver the byte to the backend of this device.
// Otherwise, return false.
bool InterfaceBox::ConcurrentWrite(UBYTE data)
{
  if (ConcurrentActive && BoxOn) {
    OpenChannel();
    if (SerialStream && SerialStream->isOpen()) {
      if (SerialStream->Write(&data,sizeof(data)) == sizeof(data)) {
	return true;
      }
    }
  }
  return false;
}
///

/// InterfaceBox::SetBaudRate
// Set the baud rate and lines to monitor
// Returns a status indicator.
UBYTE InterfaceBox::SetBaudRate(UBYTE aux1,UBYTE aux2)
{
  static const int BaudArray[16] = {
    300,45,50,57,75,110,134,150,300,600,1200,1800,2400,4800,9600,9600
  };
  // This works only if the concurrent mode is not
  // yet active.
  if (ConcurrentActive) {
    return 'N';
  } else {
    // Set number of stop bits
    if (aux1 & 0x80) {
      StopBits = 2; 
    } else {
      StopBits = 1;
    }
    // Set number of data bits
    switch(aux1 & 0x30) {
    case 0x00:
      DataBits = 8;
      break;
    case 0x10:
      DataBits = 7;
      break;
    case 0x20:
      DataBits = 6;
      break;
    case 0x30:
      DataBits = 5;
      break;
    }
    // Set the baud rate. We insert the requested baud
    // rate, though not all rates will be accepted by
    // the backend.
    // FIX: Assume a "do not change" if aux1 is zero.
    if (aux1)
      BaudRate   = BaudArray[aux1 & 0x0f];
    //
    // Check which lines we shall monitor if concurrent mode is
    // entered. This also modifies the hardware handshake which
    // we offer as an additional service.
    DSRHandshake = (aux2 & 0x04)?true:false;
    CTSHandshake = (aux2 & 0x02)?true:false;
    CRXHandshake = (aux2 & 0x01)?true:false; // actually, unsupported.
    //
    // Install the parameters into the backend.
    OpenChannel();
    InstallParameters();
    return 'C';
  }
}
///

/// InterfaceBox::SetDTR
// Set the DTR and RTS lines. This command also
// works only in concurrent mode off and
// returns the status code for SIO.
UBYTE InterfaceBox::SetDTR(UBYTE aux)
{
  if (ConcurrentActive) {
    return 'N';
  } else {
    if (aux & 0x80) {
      // Modify the DTR line.
      DTRState = (aux & 0x40)?true:false;
    }
    if (aux & 0x20) {
      // Modfify the RTS line.
      RTSState = (aux & 0x10)?true:false;
    }
    if (aux & 0x02) {
      // Modify the XMT = TxD line. We cannot emulate this
      // but nevertheless set the flags correctly.
      XMTState = (aux & 0x01)?true:false;
    }
    //
    // Try to install these.
    OpenChannel();
    SetModemLines();
    return 'C';
  }
}
///

/// InterfaceBox::ReadStatusLines
// Read the two status bytes (only in non-concurrent mode)
// and return them.
UBYTE InterfaceBox::ReadStatusLines(UBYTE *buffer)
{ 
  UBYTE errorflag  = 0;
  UBYTE lineflag   = 0;
  //
  // This also disables the concurrent mode
  // since it is called first upon a reset
  //
  ConcurrentActive = false;
  //
  // Collect the error flags from the /dev/ttyS0
  // first (if possible).
  UpdateErrors();
  //
  // Transmit the error codes.
  if (FramingError) {
    errorflag    |= 0x80;
    FramingError  = false;
  }
  if (ByteOverrun) {
    errorflag    |= 0x40;
    ByteOverrun   = false;
  }
  if (ParityError) {
    errorflag    |= 0x20;
    ParityError   = false;
  }
  if (BufferOverrun) {
    errorflag    |= 0x10;
    BufferOverrun = false;
  }
  // The second status byte is the state of the modem lines.
  // first set the previous state.
  if (DSRState) {
    lineflag  |= 0x40;
  }
  if (CTSState) {
    lineflag  |= 0x10;
  }
  if (CDState) {
    lineflag  |= 0x08;
  }
  // Now monitor the modem lines, fill in the states now.
  MonitorModemLines();
  // And insert the new flags.
  if (DSRState) {
    lineflag |= 0x80;
  }
  if (CTSState) {
    lineflag |= 0x20;
  }
  if (CDState) {
    lineflag |= 0x04;
  }
  buffer[0] = errorflag;
  buffer[1] = lineflag;
  //
  // Two bytes read
  return 'C';
}
///

/// InterfaceBox::ReadPokeyStatus
// Read the pokey settings for entering the
// concurrent mode, check the modem control
// lines for proper settings and enter the
// concurrent mode.
UBYTE InterfaceBox::ReadPokeyStatus(UBYTE *buffer)
{
  UWORD pokeytimer;
  //
  if (ConcurrentActive) {
    // This must not happen again
    return 'E';
  } else {
    OpenChannel();
    if (MonitorModemLines() == false) {
      // The status lines are not set correctly,
      // do not enter concurrent mode.
      return 'N';
    }
    // Now get the pokey timer value for the selected baud rate.
    switch(BaudRate) {
    case 45:
      pokeytimer = 0x4ccd;
      break;
    case 50:
      pokeytimer = 0x45e3;
      break;
    case 57:
      pokeytimer = 0x3d6f;
      break;
    case 75:
      pokeytimer = 0x2e95;
      break;
    case 110:
      pokeytimer = 0x1fc0;
      break;
    case 134:
      pokeytimer = 0x19f6;
      break;
    case 150:
      pokeytimer = 0x1747;
      break;
    case 300:
      pokeytimer = 0x0ba0;
      break;
    case 600:
      pokeytimer = 0x05cc;
      break;
    case 1200:
      pokeytimer = 0x02e3;
      break;
    case 1800:
      pokeytimer = 0x01ea;
      break;
    case 2400:
      pokeytimer = 0x016e;
      break;
    case 4800:
      pokeytimer = 0x00b3;
      break;
    case 9600:
      pokeytimer = 0x0056;
      break;
    default:
      // This should not happen as we picked all possible baud rates from the
      // baud array.
      Throw(InvalidParameter,"InterfaceBox::ReadPokeyStatus",
	    "found invalid/unsupported baudrate");
    }
    // Insert the settings for the pokey now.
    buffer[0] = UBYTE(pokeytimer);      // AUDF1
    buffer[1] = 0xa0;                   // AUDCTL1
    buffer[2] = UBYTE(pokeytimer >> 8); // AUDF2
    buffer[3] = 0xa0;                   // AUDCTL2
    buffer[4] = UBYTE(pokeytimer);      // AUDF3
    buffer[5] = 0xa0;                   // AUDCTL3
    buffer[6] = UBYTE(pokeytimer >> 8); // AUDF4
    buffer[7] = 0xa0;                   // AUDCTL4
    buffer[8] = 0x78;                   // AUDIOCTL
    ConcurrentActive = true;
    return 'C';
  }
}
///

/// InterfaceBox::CheckCommandFrame
// The following methods come from the SerialDevice interface
// and have to be implemented in order to make this work:
// Check whether this device accepts the indicated command
// as valid command, and return the command type of it.
SIO::CommandType InterfaceBox::CheckCommandFrame(const UBYTE *CommandFrame,int &datasize,UWORD speed)
{
  // If the box is not turned on, return an error.
  if (!BoxOn || speed != SIO::Baud19200)
    return SIO::Off;
  //
  // Otherwise, check the command frame for valid commands.
  switch(CommandFrame[1]) {
  case 'W': // Write block data
    if (CommandFrame[2]) {
      // We always expect 64 bytes data to be written
      // here, even though only a part of it is used.
      datasize = 64;
      return SIO::WriteCommand;
    } else {
      // For zero bytes, no data frame is expected
      return SIO::StatusCommand;
    }
  case 'S': // Return 850 status
    datasize = 2;
    return SIO::ReadCommand;
  case 'X': // Read pokey settings for concurrent mode
    datasize = 9;
    return SIO::ReadCommand;
  case 'B': // Set baud rate
    return SIO::StatusCommand;
  case 'A': // Set DTR and other output data
    return SIO::StatusCommand;
  case '?': // Read DeviceControlBlock parameters for boot-off
    datasize = 12;
    return SIO::ReadCommand;
  case '!': // Read interface box boot code  
    datasize = sizeof(Boot850);
    return SIO::ReadCommand;
  case '&': // Read interface code  
    datasize = sizeof(Handler850);
    return SIO::ReadCommand;
  }
  // Nothing else known here.
  return SIO::InvalidCommand;
}
///

/// InterfaceBox::ReadBuffer
// Read bytes from the device into the system. Returns the number of
// bytes read.
UBYTE InterfaceBox::ReadBuffer(const UBYTE *CommandFrame,UBYTE *buffer,int &,UWORD &,UWORD &speed)
{
  //
  // All these are commands in the non-concurrent mode.
  speed = SIO::Baud19200;
  //
  switch(CommandFrame[1]) {  
  case 'S': // Return 850 status
    return ReadStatusLines(buffer);
  case 'X': // Read pokey settings for concurrent mode
    return ReadPokeyStatus(buffer);
  case '?': // Read DeviceControlBlock parameters for boot-off
    return ReadDCB(buffer);
  case '!': // Read interface box boot code
    return ReadBootCode(buffer);
  case '&': // Read interface code
    return ReadHandler(buffer);
  }
  //
  // Warn if something unexpected is here
  machine->PutWarning("Unknown command frame: %02x %02x %02x %02x\n",
		      CommandFrame[0],CommandFrame[1],CommandFrame[2],CommandFrame[3]);
  return 0;
}
///

/// InterfaceBox::WriteBuffer
// Write the indicated data buffer out to the target device.
// Return 'C' if this worked fine, 'E' on error.
UBYTE InterfaceBox::WriteBuffer(const UBYTE *commandframe,const UBYTE *buffer,
				int &size,UWORD &,UWORD speed)
{
  int aux;
  
  //
  // This is the write command in the short-block mode.
  // Communications to the interface box run at 19200 baud here.
  if (speed != SIO::Baud19200)
    return 'E';
  
  switch (commandframe[1]) { 
  case 'W':
    // Get the number of valid bytes in the buffer.
    aux = commandframe[2];
    // The one and only known write command
    if (size != 64 || aux > 64) {
      // This cannot work!
      return 'E';
    }
    // Otherwise, the amount of data to be written is given by AUX1.
    return SendData(buffer,aux);
  }
  //  
  machine->PutWarning("Unknown command frame: %02x %02x %02x %02x\n",
		      commandframe[0],commandframe[1],commandframe[2],commandframe[3]);

  // No other command is known
  return 'E';  
}
///

/// InterfaceBox::ReadStatus
// Execute a status-only command that does not read or write any data except
// the data that came over AUX1 and AUX2
UBYTE InterfaceBox::ReadStatus(const UBYTE *commandframe,UWORD &,UWORD &speed)
{
  //
  // This all only works in the non-concurrent mode
  // and thus at 19200 baud.
  speed = SIO::Baud19200;
  //
  switch(commandframe[1]) {
  case 'B': // Set baud rate
    return SetBaudRate(commandframe[2],commandframe[3]);
  case 'A': // Set DTR and other output data
    return SetDTR(commandframe[2]);
  case 'W': // Write buffer can also be a status command.    
    // Get the number of valid bytes in the buffer: Must
    // be zero.
    if (commandframe[2]) {
      // This cannot work!
      return 'E';
    }
    // Otherwise, the amount of data to be written is given by AUX1,
    // which has been checked to be zero.
    return SendData(NULL,0);
  }
  //  
  machine->PutWarning("Unknown command frame: %02x %02x %02x %02x\n",
		      commandframe[0],commandframe[1],commandframe[2],commandframe[3]);
  // All others are unknown
  return 'N';
}
///

/// InterfaceBox::ColdStart
// Coldstart the interface box
void InterfaceBox::ColdStart(void)
{
  WarmStart();
}
///

/// InterfaceBox::WarmStart
// Warm start the interface box. There is
// nothing similar in the real world since the
// warm start signal is not passed thru the
// box, but we'll do this nevertheless.
void InterfaceBox::WarmStart(void)
{
  // Close the channel
  delete SerialStream;
  SerialStream = NULL;
  //
  // Initialize now all state flags.
  DTRState         = false;
  RTSState         = false;
  XMTState         = false;
  DSRHandshake     = false;
  CTSHandshake     = false;
  CRXHandshake     = false;
  DSRState         = false;
  CTSState         = false;
  CDState          = false;
  StopBits         = 1;
  DataBits         = 8;
  BaudRate         = 300;  
  FramingError     = false;
  ByteOverrun      = false;
  ParityError      = false;
  BufferOverrun    = false;
  // Try to re-open the device.
  DevError         = false;
  ConcurrentActive = false;  
  // Reset error counters
  FramingErrorCnt  = 0;
  ByteOverrunCnt   = 0;
  ParityErrorCnt   = 0;
  BufferOverrunCnt = 0;
}
///

/// InterfaceBox::ParseArgs
// Argument parser stuff: Parse off command line args
void InterfaceBox::ParseArgs(class ArgParser *args)
{
  args->DefineTitle("850Interface");
  //
  // Define the name of the game.  
  args->DefineBool("Enable850","enable or disable the 850 interface box",BoxOn);
  args->DefineString("SerialName","name of the serial terminal device",DeviceName);
  //
  // Force-close the interface to have it re-opened.
  if (SerialStream) {
    delete SerialStream;
    SerialStream = NULL;
  } else {
    // The box has been off. Ensure that it is reset properly because it is
    // set to use.
    ColdStart();
  }
  DevError     = false;
}
///

/// InterfaceBox::DisplayStatus
// Status display for the monitor.
void InterfaceBox::DisplayStatus(class Monitor *mon)
{
  mon->PrintStatus("850 Interface Box Status:\n"
		   "\tInterface device name    : %s\n"
		   "\tInterface power          : %s\n"
		   "\tSerial connection        : %s\n"
		   "\trequested DTR line state : %s\n"
		   "\trequested RTS line state : %s\n"
		   "\trequested XMT line state : %s\n"
		   "\tDSR handshaking          : %s\n"
		   "\tCTS handshaking          : %s\n"
		   "\tCRX handshaking          : %s\n"
		   "\tlast DSR line state      : %s\n"
		   "\tlast CTS line state      : %s\n"
		   "\tlast CD line state       : %s\n"
		   "\tNumber of stop bits      : %d\n"
		   "\tNumber of data bits      : %d\n"
		   "\tBaud rate                : %d\n"
		   "\tCollected framing error  : %s\n"
		   "\tCollected overrun error  : %s\n"
		   "\tCollected parity error   : %s\n"
		   "\tDevice disabled          : %s\n"
		   "\tConcurrent mode          : %s\n",
		   DeviceName,BoxOn?("on"):("off"),(SerialStream && SerialStream->isOpen())?("open"):("closed"),
		   DTRState?("on"):("off"),
		   RTSState?("on"):("off"),
		   XMTState?("on"):("off"),
		   DSRHandshake?("enabled"):("disabled"),
		   CTSHandshake?("enabled"):("disabled"),
		   CRXHandshake?("enabled"):("disabled"),
		   DSRState?("on"):("off"),
		   CTSState?("on"):("off"),
		   CDState?("on"):("off"),
		   StopBits,DataBits,BaudRate,
		   FramingError?("yes"):("no"),
		   ByteOverrun?("yes"):("no"),
		   ParityError?("yes"):("no"),
		   DevError?("yes"):("no"),
		   ConcurrentActive?("yes"):("no")
		   );
}
///

/// InterfaceBox::State
// Read and write the state into a snapshot class
// This is the main purpose why we are here. This method
// should read the state from a snapshot, providing the current
// settings as defaults. Hence, we can also use it to save the
// configuration by putting the defaults into a file.
void InterfaceBox::State(class SnapShot *sn)
{
  LONG baud   = BaudRate;
  LONG dtbits = DataBits;
  LONG sbits  = StopBits;
  
  const struct ArgParser::SelectionVector baudrates[] = {
    { "50" ,   50 },
    { "75" ,   75 },
    { "110",  110 },
    { "134",  134 },
    { "150",  150 },
    { "300",  300 },
    { "600",  600 },
    { "1200",1200 },
    { "1800",1800 },
    { "2400",2400 },
    { "4800",4800 },
    { "9600",9600 },
    { NULL  , 0   }
  };
  //
  //
  sn->DefineTitle("850");
  //
  // States of various modem output lines.
  //
  sn->DefineBool("DTRState","state of the DTR line",DTRState);
  sn->DefineBool("RTSState","state of the RTS line",RTSState);
  sn->DefineBool("XMTState","state of the XMT line",XMTState);
  //
  // Handshaking parameters.
  //
  sn->DefineBool("DSRHandshake","handshaking by DTR line",DSRHandshake);
  sn->DefineBool("CTSHandshake","handshaking by CTS line",CTSHandshake);
  sn->DefineBool("CRXHandshake","handshaking by CD line",CRXHandshake);
  //
  // Serial parameters
  //
  sn->DefineLong("StopBits","number of stop bits",1,2,sbits);
  sn->DefineLong("DataBits","number of data bits",5,8,dtbits);
  sn->DefineSelection("BaudRate","transmission baud rate",baudrates,baud);
  //
  // Concurrent mode currently active?
  //
  sn->DefineBool("ConcurrentActive","concurrent mode running?",ConcurrentActive);
  //
  //
  BaudRate = baud;
  DataBits = dtbits;
  StopBits = sbits;
  // Re-open the device if it was previously open.
  if (SerialStream) {
    delete SerialStream;
    SerialStream = NULL;
    DevError     = false;
    OpenChannel();
  }
}
///

/// InterfaceBox::Drain
// Drain the buffer, i.e. wait until all its data has been written out.
// Returns false on error.
bool InterfaceBox::Drain(void)
{
  if (SerialStream)
    return SerialStream->Drain();
  return true;
}
///
