/*
**	$Id: BTXService.cpp 1.3 1995/09/26 16:48:58 olsen Exp olsen $
**
**	:ts=4
*/

/*
 * Amiga changes copyright © 1995 by Olaf Barthel, All Rights Reserved
 *
 * Copyright (c) 1992, 1993 Arno Augustin, Frank Hoering, University of
 * Erlangen-Nuremberg, Germany.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	Erlangen-Nuremberg, Germany.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
 * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software has not been validated by the ``Bundesamt fuer Zulassungen in
 * der Telekommunikation'' of the ``Deutsche Bundepost Telekom'' and thus
 * must not be used for accessing the BTX-Network of the Telekom in Germany.
 *
 * Diese Software hat keine Zulassung durch das Bundesamt fuer Zulassungen in
 * der Telekommunikation der Deutschen Bundespost Telekom und darf daher nicht
 * am Netz der Deutschen Bundespost Telekom in Deutschland betrieben werden.
 */

#define USE_PROTOCOL

#include "BTXService.hpp"
#include <string.h>
#include <ctype.h>
#include <stdio.h>

#define LOG(x)		log()
#define ERRLOG(x)	errlog()

/******************************************************************************/

BTXService::~BTXService()
{
	Close();
}

BTXService::BTXService()
{
	memset(screen,0,sizeof(screen));
	memset(&t,0,sizeof(t));
	memset(&backup,0,sizeof(backup));
	memset(data,0,sizeof(data));

	rows = fontheight = reachedEOF = 0;
	pushback = -1;

	telefile = NULL;
	teledownload = FALSE;
	telename[0] = 0;
	telemode = MODE_Unknown;

#ifdef USE_PROTOCOL
	protocolfile = NULL;
#endif
}

VOID BTXService::Close(VOID)
{
	if(telefile)
	{
		fclose(telefile);

		telefile = NULL;
	}

#ifdef USE_PROTOCOL
	if(protocolfile)
	{
		fclose(protocolfile);
		protocolfile = NULL;
	}
#endif
}

LONG BTXService::Open(BTXDisplay *Disp,Application *Appl)
{
	AppDisplay	= Disp;
	App			= Appl;

	Disp->MonitorData(&rows,&fontheight);

#ifdef USE_PROTOCOL
	protocolfile = fopen("t:protocol","wb");
#endif

	init_layer6();
	return(0);
}

int BTXService::ProcessInput(VOID)
{
	return(process_BTX_data());
}

UBYTE BTXService::ConvertChar(int c,int s,int d)
{
   static UBYTE supp_map[96] =
      { ' ', 0xa1, 0xa2, 0xa3, '$', 0xa5, '#', 0xa7, 0xa4, '`', '\"', 0xab,
	0, 0, 0, 0, 0xb0, 0xb1, 0xb2, 0xb3, 0xd7, 0xb5, 0xb6, 0xb7, 0xf7,
	'\'', '\"', 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0, 0x60, 0x27, 0, '~',
	0xaf, 0, 0, 0x22, 0x22, 0xb0, 0, 0, 0x22, 0xb8, 0, 0xad, 0xb9, 0xae,
	0xa9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc6, 0, 0, 0, 0, 0,
	0, 0, 0xd8, 0, 0, 0xfe, 0, 0, 0, 0, 0xe6, 0, 0, 0, 0, 0, 0, 0, 0xf8,
	0, 0xdf, 0xde, 0, 0, 0 };

   static UBYTE diacritical_map[26*2][16] = {
    { 0, 0xc0, 0xc1, 0xc2, 0xc3, 0, 0, 0, 0xc4, 0xc4, 0xc5, 0, 0, 0xc4, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },     /* B */
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc7, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },  /* D */
    { 0, 0xc8, 0xc9, 0xca, 0, 0, 0, 0, 0xcb, 0xcb, 0, 0, 0, 0xcb, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },  /* H */
    { 0, 0xcc, 0xcd, 0xce, 0, 0, 0, 0, 0xcf, 0xcf, 0, 0, 0, 0xcf, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0xd1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },  /* N */
    { 0, 0xd2, 0xd3, 0xd4, 0xd5, 0, 0, 0, 0xd6, 0xd6, 0, 0, 0, 0xd6, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* T */
    { 0, 0xd9, 0xda, 0xdb, 0, 0, 0, 0, 0xdc, 0xdc, 0, 0, 0, 0xdc, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* X */
    { 0, 0, 0xdd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0xe0, 0xe1, 0xe2, 0xe3, 0, 0, 0, 0xe4, 0xe4, 0xe5, 0, 0, 0xe4, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xe7, 0, 0, 0, 0 },  /* c */
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0xe8, 0xe9, 0xea, 0, 0, 0, 0, 0xeb, 0xeb, 0, 0, 0, 0xeb, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* h */
    { 0, 0xec, 0xed, 0xee, 0, 0, 0, 0, 0xef, 0xef, 0, 0, 0, 0xef, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0xf1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* n */
    { 0, 0xf2, 0xf3, 0xf4, 0xf5, 0, 0, 0, 0xf6, 0xf6, 0, 0, 0, 0xf6, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* t */
    { 0, 0xf9, 0xfa, 0xfb, 0, 0, 0, 0, 0xfc, 0xfc, 0, 0, 0, 0xfc, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0xfd, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 0, 0, 0xff, 0, 0 },  /* y */
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } };

	UBYTE Char;

	/* ASCII out of  1st supplementary set of mosaic characters */
	if(s==SUP1 && c>=0x40 && c<=0x5f)
		Char = (UBYTE)c;
	else
		Char = (UBYTE)' ';

	/* supplementary set of graphic characters */
	if(s==SUPP && supp_map[c-0x20])
		Char = (UBYTE)supp_map[c-0x20];
	else
	{
		/* composed characters, diacritical marks (page 123) */
		if(s==PRIM)
		{
			if(!d)
				Char = (UBYTE)c;  /* ASCII character */
			else
			{
				if(d>=0x40 && d<=0x4f)
				{
					if(c>=0x41 && c<=0x5a && diacritical_map[c-0x41][d&0xf])
						Char = (UBYTE)diacritical_map[c-0x41][d&0xf];
					else
					{
						if(c>=0x61 && c<=0x7a && diacritical_map[c-0x61+26][d&0xf])
							Char = (UBYTE)diacritical_map[c-0x61+26][d&0xf];
					}
				}
			}
		}
	}

	return(Char);
}

VOID BTXService::GetMatrix(UBYTE *Matrix,int *CursorX,int *CursorY)
{
	if(Matrix)
	{
		int x,y;

		memset(Matrix,' ',40 * 24);

		for(y = 0 ; y < rows ; y++)
		{
			for(x = 0 ; x < 40 ; x++)
				Matrix[y * 40 + x] = ConvertChar(screen[y][x].chr & 0x7f,screen[y][x].set,(screen[y][x].chr>>8) & 0x7f);
		}
	}

	if(CursorX)
	{
		if(t.cursor_on)
			*CursorX = t.cursorx;
		else
			*CursorX = -1;
	}

	if(CursorY)
	{
		if(t.cursor_on)
			*CursorY = t.cursory;
		else
			*CursorY = -1;
	}
}

/******************************************************************************/

/* all 'page' references refer to 'FTZ 157 D2 E' */

#include "Control.h"
#include "Font.h"
#include "Attributes.h"

/*
 * initialize layer6 variables
 */
void BTXService::init_layer6()
{
   int y;

   fontheight = 10;
   rows = 24;
   t.cursorx = t.cursory = 1;
   t.wrap = 1;
   t.service_break = 0;
   t.scroll_area = 0;
   t.scroll_impl = 1;
   t.cursor_on = 0;
   t.par_attr = 0;
   t.par_fg = WHITE;
   t.par_bg = TRANSPARENT;
   for(y=0; y<24; y++)	AppDisplay->define_fullrow_bg(y, BLACK);
   clearscreen();
   default_sets();
}

/*
 * Read and process one layer6 code sequence.
 * Returns 1 when DCT (terminate data collect 0x1a) is received, else 0.
 */

int BTXService::process_BTX_data()
{
	int set, c1, c2, dct=0;

	c1 = layer2getc();

	if(c1>=0x00 && c1<=0x1f)
		dct = primary_control_C0(c1);
	else
	{
		if(c1>=0x80 && c1<=0x9f)
			supplementary_control_C1(c1, 0);
		else
		{
			if(t.sshift)
				set = t.G0123L[ t.sshift ];
			else
				set = t.G0123L[ t.leftright[ (c1&0x80) >> 7 ] ];

			if( set == SUPP  &&  (c1 & 0x70) == 0x40 )	 /* diacritical ??? */
			{
				LOG(("diacritical mark %d\n", c1 & 0x0f));

				c2 = layer2getc();

				if(c2&0x60)
					c1 = (c1<<8) | c2;

				t.sshift = 0;
			}

			LOG(("OUTPUT 0x%02x '%c'\n", c1&0xff, isprint(c1&0xff) ? c1&0xff : '.'));

			output(c1);

			t.lastchar = c1;
		}
	}

	return dct;
}


/*
 * get one byte from protocol layer 2.
 */

int BTXService::layer2getc()
{
	LONG Value;
	UBYTE c;

	/* is there a pushed back character ? */
	if(pushback >= 0)
	{
		Value = App->DispatchDisplayEvent();

		if(Value == CHANNELERR_EOF)
			reachedEOF = 1;

		c = (UBYTE)pushback;
		pushback = -1;
	}
	else
	{
		while(!reachedEOF)
		{
			Value = App->DispatchEvent();

			switch(Value)
			{
				case CHANNELERR_EOF:

					reachedEOF = 1;
					return(US);

				case CHANNELERR_Timeout:
				case CHANNELERR_Nothing:

					App->WaitEvent();
					continue;
			}

			if(Value < 0)
				reachedEOF = 1;
			else
			{
				c = (UBYTE)Value;

#ifdef USE_PROTOCOL
				if(protocolfile)
					fputc(c,protocolfile);
#endif
			}

			break;
		}

		if(reachedEOF)
			c = US;
	}

	if(reachedEOF)
		App->EventExit();

	LOG(("(%c %03x %2d/%2d %2x/%2x) (0x%02x)   ", t.serialmode ? 'S' : 'P',
		t.serialmode ? screen[t.cursory-1][t.cursorx-1].attr : t.par_attr,
		t.cursory, t.cursorx,
		t.serialmode ? screen[t.cursory-1][t.cursorx-1].fg : t.par_fg,
		t.serialmode ? screen[t.cursory-1][t.cursorx-1].bg : t.par_bg, c));

	return c;
}


/*
 * unget character 'c'. Only one character of pushback is possible !
 */
void BTXService::layer2ungetc(int c)
{
	if(pushback < 0)
	{
		pushback = c;
		LOG(("<-- character pushed back\n"));
	}
	else
		ERRLOG(("XBTX: internal error: ungetc-buffer overflow !\n"));
}


/*
 * initialize default character sets (page 113)
 */
void BTXService::default_sets()
{
   t.G0123L[G0] = PRIM;
   t.G0123L[G1] = SUP2;
   t.G0123L[G2] = SUPP;
   t.G0123L[G3] = SUP3;
   t.G0123L[ L] = L;	   /* always L !!! (cannot be changed) */
   t.leftright[0] = G0;
   t.leftright[1] = G2;
   t.sshift = 0;
   t.save_left = G0;
   t.prim = G0;
   t.supp = G2;
}

/*
 * move the cursor, perform automatic wraparound (page 97)
 *					and implicite scrolling (page 101)
 */
void BTXService::move_cursor(int cmd, int y, int x)
{
   int up=0, down=0;

   /* erase old cursor */
   if(t.cursor_on) AppDisplay->xcursor(t.cursorx-1, t.cursory-1);

   /* move & wrap */
   switch(cmd) {
	  case APF:
		 if(++t.cursorx > 40)
			if(t.wrap) { t.cursorx-=40; down=1; }
			else		 t.cursorx=40;
		 break;

	  case APB:
		 if(--t.cursorx < 1)
			if(t.wrap) { t.cursorx+=40; up=1; }
			else		 t.cursorx=1;
		 break;

	  case APU:  up=1;		   break;
	  case APD:  down=1;	   break;
	  case APR:  t.cursorx=1;  break;

	  case APA:
		 t.hold_mosaic = 0;
		 if(t.wrap) {	/* wrap !! (SKY-NET *200070000000004a#) */
			if(x <	1)	{ x += 40;	y--; }
			if(x > 40)	{ x -= 40;	y++; }
			if(y <	1)	  y += rows;
			if(y > rows)  y -= rows;
		 }
		 else {
			if(x <	1)	x =  1;
			if(x > 40)	x = 40;
			if(y <	1)	y =  1;
			if(y > rows)  y = rows;
		 }

		 t.cursorx=x;
		 t.cursory=y;
		 break;
   }

   if(up) {
	  t.hold_mosaic = 0;
	  if(t.scroll_area && t.scroll_impl &&
		 t.cursory == t.scroll_upper)  scroll(0);
	  else
		 if(--t.cursory < 1)
			if(t.wrap)	t.cursory += rows;
			else		t.cursory  = 1;
   }

   if(down) {
	  t.hold_mosaic = 0;
	  if(t.scroll_area && t.scroll_impl &&
		 t.cursory == t.scroll_lower)  scroll(1);
	  else
		 if(++t.cursory > rows)
			if(t.wrap)	t.cursory -= rows;
			else		t.cursory  = rows;
   }

   /* draw new cursor */
   if(t.cursor_on) AppDisplay->xcursor(t.cursorx-1, t.cursory-1);
}


/*
 * process a code from the primary control set C0 (0x00 - 0x1f).
 * Returns 1 when DCT is received, else 0.
 */

int BTXService::primary_control_C0(int c1)	/* page 118, annex 6 */
{
   int c2, x, y;

   switch(c1) {

	  case APB:
		 LOG(("APB active position back\n"));
		 move_cursor(APB);
		 break;

	  case APF:
		 LOG(("APF active position forward\n"));
		 move_cursor(APF);
		 break;

	  case APD:
		 LOG(("APD active position down\n"));
		 move_cursor(APD);
		 break;

	  case APU:
		 LOG(("APU active position up\n"));
		 move_cursor(APU);
		 break;

	  case CS:
		 LOG(("CS  clear screen\n"));
		 t.leftright[0] = t.save_left;
		 clearscreen();
		 break;

	  case APR:
		 LOG(("APR active position return\n"));
		 move_cursor(APR);
		 break;

	  case LS1:
	  case LS0:
		 c2 = (c1==LS1) ? 1 : 0;
		 LOG(("LS%d locking shift G%d left\n", c2, c2));
		 t.leftright[0] = c2;  /* G0 or G1 !! */
		 t.save_left = c2;
		 break;

	  case CON:
		 LOG(("CON cursor on\n"));
		 if(!t.cursor_on) {
			t.cursor_on = 1;
			AppDisplay->xcursor(t.cursorx-1, t.cursory-1);
		 }
		 break;

	  case RPT:
		 LOG(("RPT repeat last char\n"));
		 c2 = layer2getc() & 0x3f;
		 LOG(("    %d times\n", c2));
		 while(c2--)  output(t.lastchar);
		 break;

	  case COF:
		 LOG(("COF cursor off\n"));
		 if(t.cursor_on) {
			t.cursor_on = 0;
			AppDisplay->xcursor(t.cursorx-1, t.cursory-1);
		 }
		 break;

	  case CAN:
		 LOG(("CAN cancel\n"));
		 y = t.cursory-1;
		 screen[y][t.cursorx-1].chr = ' ';
		 screen[y][t.cursorx-1].set = PRIM;
		 for(x=t.cursorx; x<40; x++) {	/* clear to the right */
			screen[y][x].chr	= ' ';
			screen[y][x].set	= PRIM;
			screen[y][x].mark	= 0;
			screen[y][x].attr	= screen[y][t.cursorx-1].attr;
			screen[y][x].fg 	= screen[y][t.cursorx-1].fg;
			screen[y][x].bg 	= screen[y][t.cursorx-1].bg;
		 }
		 for(x=t.cursorx-1; x<40; x++)	redrawc(x+1, y+1);
		 break;

	  case SS2:
		 LOG(("SS2 single shift G2 left\n"));
		 t.sshift = G2;
		 break;

	  case ESC:
		 LOG(("ESC escape sequence\n"));
		 do_ESC();
		 break;

	  case SS3:
		 LOG(("SS3 single shift G3 left\n"));
		 t.sshift = G3;
		 break;

	  case APH:
		 LOG(("APH active position home\n"));
		 move_cursor(APA, 1, 1);
		 t.par_attr = 0;
		 t.par_fg = WHITE;
		 t.par_bg = TRANSPARENT;
		 break;

	  case US:
		 LOG(("US  unit separator (or APA)\n"));
		 do_US();
		 break;

	  default:
		 LOG(("??? unprocessed control character 0x%02x - ignored\n", c1));
		 if(c1 == DCT)	return 1;
   }

   return 0;
}


/*
 * process a code from the supplementary control set C1 (0x80 - 0x9f).
 * 'fullrow' indicates attribute application to the complete row.
 * Most codes advance active cursor position by one !
 */

void BTXService::supplementary_control_C1(int c1, int fullrow)	/* page 121, annex 6 */
{
   int adv, mode = fullrow ? 2 : t.serialmode;

   switch(c1) {
					 /* serial	 parallel */
	  case 0x80:	 /*  ABK	   BKF	  */
	  case 0x81:	 /*  ANR	   RDF	  */
	  case 0x82:	 /*  ANG	   GRF	  */
	  case 0x83:	 /*  ANY	   YLF	  */
	  case 0x84:	 /*  ANB	   BLF	  */
	  case 0x85:	 /*  ANM	   MGF	  */
	  case 0x86:	 /*  ANC	   CNF	  */
	  case 0x87:	 /*  ANW	   WHF	  */
		 LOG(("set foreground to color #%d %s\n", t.clut*8+c1-0x80,
		 (mode==1) ? "(+ unload L set)" : ""));
	 set_attr(ATTR_FOREGROUND, 1, t.clut*8+c1-0x80, mode);
	 if(mode==1) {
		t.leftright[0] = t.save_left;
	 }
		 break;

	  case FSH:
		 LOG(("FSH flashing begin\n"));
	 /* set_attr(ATTR_FLASH, 1, 0, mode); */
		 break;

	  case STD:
		 LOG(("STD flashing steady\n"));
	 /* set_attr(ATTR_FLASH, 0, 0, mode); */
		 break;

	  case EBX:
		 LOG(("EBX end of window\n"));
	 /* set_attr(ATTR_WINDOW, 0, 0, mode); */
		 break;

	  case SBX:
		 LOG(("SBX start of window\n"));
	 /* set_attr(ATTR_WINDOW, 1, 0, mode); */
		 break;

	  case NSZ:
		 LOG(("NSZ normal size\n"));
	 set_attr(ATTR_NODOUBLE, 1, 0, mode);
		 break;

	  case DBH:
		 LOG(("DBH double height\n"));
	 set_attr(ATTR_YDOUBLE, 1, 0, mode);
		 break;

	  case DBW:
		 LOG(("DBW double width\n"));
	 set_attr(ATTR_XDOUBLE, 1, 0, mode);
		 break;

	  case DBS:
		 LOG(("DBS double size\n"));
	 set_attr(ATTR_XYDOUBLE, 1, 0, mode);
		 break;

					 /* serial	 parallel */
	  case 0x90:	 /*  MBK	   BKB	  */
	  case 0x91:	 /*  MSR	   RDB	  */
	  case 0x92:	 /*  MSG	   GRB	  */
	  case 0x93:	 /*  MSY	   YLB	  */
	  case 0x94:	 /*  MSB	   BLB	  */
	  case 0x95:	 /*  MSM	   MGB	  */
	  case 0x96:	 /*  MSC	   CNB	  */
	  case 0x97:	 /*  MSW	   WHB	  */
	 /* at fullrow control the parallel set is used ! */
		 LOG(("set %s to color #%d\n", (mode==1) ?
		 "mosaic foreground (+ invoke L set)" : "background",
		 t.clut*8+c1-0x90));
	 if(mode==1) {
		set_attr(ATTR_FOREGROUND, 1, t.clut*8+c1-0x90, 1);
		t.save_left = t.leftright[0];
		t.leftright[0] = L;
	 }
	 else set_attr(ATTR_BACKGROUND, 1, t.clut*8+c1-0x90, mode);
		 break;

	  case CDY:
		 LOG(("CDY conceal display\n"));
	 set_attr(ATTR_CONCEALED, 1, 0, mode);
		 break;

	  case SPL:
		 LOG(("SPL stop lining\n"));
	 set_attr(ATTR_UNDERLINE, 0, 0, mode);
		 break;

	  case STL:
		 LOG(("STL start lining\n"));
	 set_attr(ATTR_UNDERLINE, 1, 0, mode);
		 break;

	  case CSI:
		 LOG(("CSI control sequence introducer\n"));
		 adv = do_CSI();
		 break;

	  case 0x9c:
		 if(mode==1) {
			LOG(("BBD black background\n"));
		set_attr(ATTR_BACKGROUND, 1, t.clut*8+BLACK, 1);
		 } else {
			LOG(("NPO normal polarity\n"));
		set_attr(ATTR_INVERTED, 0, 0, mode);
		 }
		 break;

	  case 0x9d:
		 if(mode==1) {
			LOG(("NBD new background\n"));
		set_attr(ATTR_BACKGROUND, 1,
			 screen[t.cursory-1][t.cursorx-1].fg, 1);
		 } else {
			LOG(("IPO inverted polarity\n"));
		set_attr(ATTR_INVERTED, 1, 0, mode);
		 }
		 break;

	  case 0x9e:
		 if(mode==1) {
			LOG(("HMS hold mosaic\n"));
		t.hold_mosaic = 1;
		 } else {
			LOG(("TRB transparent background\n"));
		set_attr(ATTR_BACKGROUND, 1, TRANSPARENT, mode);
		 }
		 break;

	  case 0x9f:
		 if(mode==1) {
			LOG(("RMS release mosaic\n"));
		t.hold_mosaic = 0;
		 } else {
			LOG(("STC stop conceal\n"));
		set_attr(ATTR_CONCEALED, 0, 0, mode);
		 }
		 break;
   }

   /* serial attribute controls advance cursor 1 char forwards !  (page 90) */
   if(mode==1 && (c1!=CSI || adv) )
	  if(t.hold_mosaic)  output(t.lastchar);  /* HMS/RMS (page 96) */
	  else				 move_cursor(APF);
}


void BTXService::do_US()  /* page 85/86 */
{
   static UBYTE TFI_string[] = { SOH, US, 0x20, 0x7f, 0x40, ETB };
   int c2, c3, alphamosaic = 0;;

   /* implicite return from service break !!! (any US sequence !?!) */
   if(t.service_break) {
	  t = backup;
	  move_cursor(APA, t.cursory, t.cursorx);
   }

   for(;;)
   {
       c2 = layer2getc();
       if(c2 != 0x1f)
          break;
   }

   switch(c2) {
	  case 0x20:  /* annex 7.3 */
	 LOG(("    TFI Terminal Facility Identifier\n"));
	 c3 = layer2getc();
	 if(c3==0x40) {
		LOG(("       TFI request\n"));
		write(TFI_string, 6);
	 }
	 else {
		LOG(("       TFI echo 0x%02x\n", c3));
		do {
		   c3 = layer2getc();
		   LOG(("       TFI echo 0x%02x\n", c3));
		}
		while(c3 & 0x20);  /* extension bit */
	 }
	 break;

	  case 0x23:
		 LOG(("    define DRCS\n"));
		 do_DRCS();
		 break;

	  case 0x26:
		 LOG(("    define color\n"));
		 do_DEFCOLOR();
		 break;

	  case 0x2d:  /* page 155 */
		 LOG(("    define Format\n"));
		 do_DEFFORMAT();
		 break;

	  case 0x2f:  /* page 157 */
		 LOG(("    Reset sequence\n"));
		 do_RESET();
	 alphamosaic = 1;
		 break;

	  case 0x3e:  /* annex 7.4 */
         LOG(("    telesoftware\n"));
         do_Telesoftware();
         alphamosaic = 0;
         break;

	  default:	  /* APA active position addressing */
	 if(c2<0x40)  LOG(("    unknown US sequence\n"));
	 else {
		alphamosaic = 1;
		LOG(("    new row    %2d\n", c2 & 0x3f));
		c3 = layer2getc();
		LOG(("    new column %2d\n", c3 & 0x3f));
		move_cursor(APA, c2 & 0x3f, c3 & 0x3f);
		t.par_attr = 0;
		t.par_fg = WHITE;
		t.par_bg = TRANSPARENT;
	 }
		 break;
   }

   /* VPDE = US x data. Each VPDE has to be followed by the next VPDE
	* immediately (page 85). The alphamosaic VPDE is introduced by APA or
	* by one of the reset functions ! Any remaining data (errors) after all
	* other VPDE's has to be skipped (*17420101711a#).
	*/
   if(!alphamosaic) {
	  while( (c2 = layer2getc()) != US )  LOG(("skipping to next US\n"));
	  LOG(("\n"));
	  layer2ungetc(US);
   }
}

void BTXService::do_ESC()
{
   int y, c2, c3, c4;

   c2 = layer2getc();

   switch(c2) {

	  case 0x22:
		 LOG(("    invoke C1\n"));
		 c3 = layer2getc();
		 LOG(("       (%s)\n", c3==0x40 ? "serial" : "parallel"));
	 if(c3==0x40)  t.serialmode = 1;
	 else {
		t.serialmode = 0;
		t.leftright[0] = t.save_left;
	 }
		 break;

	  case 0x23:
		 LOG(("    set attributes\n"));
		 c3 = layer2getc();
		 switch(c3) {
			case 0x20:
			   LOG(("       full screen background\n"));
			   c4 = layer2getc();
			   LOG(("          color = %d\n",
		   c4==0x5e ? TRANSPARENT : t.clut*8+c4-0x50));
		   for(y=0; y<24; y++)
			  AppDisplay->define_fullrow_bg(y, c4==0x5e ?
					TRANSPARENT : t.clut*8+c4-0x50);
			   break;
			case 0x21:
			   LOG(("       full row\n"));
			   c4 = layer2getc();
		   LOG(("          "));
		   supplementary_control_C1(c4+0x40, 1);
			   break;
		 }
		 break;

	  case 0x28:

	  case 0x29:
	  case 0x2a:
	  case 0x2b:
		 LOG(("    load G%d with\n", c2 - 0x28));
		 c3 = layer2getc();
		 switch(c3) {
			case 0x40:
			   LOG(("       'primary graphic'\n"));
			   t.G0123L[c2 - 0x28] = PRIM;
			   t.prim = c2 - 0x28;
			   break;
			case 0x62:
			   LOG(("       'supplementary graphic'\n"));
			   t.G0123L[c2 - 0x28] = SUPP;
			   t.supp = c2 - 0x28;
			   break;
			case 0x63:
			   LOG(("       '2nd supplementary mosaic'\n"));
			   t.G0123L[c2 - 0x28] = SUP2;
			   break;
			case 0x64:
			   LOG(("       '3rd supplementary mosaic'\n"));
			   t.G0123L[c2 - 0x28] = SUP3;
			   break;
			case 0x20:
		   LOG(("       DRCS\n"));
			   c4 = layer2getc();
			   if(c4 != 0x40)  LOG(("HAEH  (ESC 0x%02x 0x20 0x%02x)\n", c2, c4));
		   else 		   LOG(("\n"));
			   t.G0123L[c2 - 0x28] = DRCS;
			   break;
		 }
		 break;


	  case 0x6e:
		 LOG(("    LS2 locking shift G2 left\n"));
		 t.leftright[0] = G2;
	 t.save_left = G2;
		 break;

	  case 0x6f:
		 LOG(("    LS3 locking shift G3 left\n"));
		 t.leftright[0] = G3;
	 t.save_left = G3;
		 break;

	  case 0x7c:
		 LOG(("    LS3R locking shift G3 right\n"));
		 t.leftright[1] = G3;
		 break;

	  case 0x7d:
		 LOG(("    LS2R locking shift G2 right\n"));
		 t.leftright[1] = G2;
		 break;

	  case 0x7e:
		 LOG(("    LS1R locking shift G1 right\n"));
		 t.leftright[1] = G1;
		 break;
   }
}


/*
 * Process one control sequence. Return whether or not the cursor position
 * has to be advanced by one char (in case of serialmode).
 *
 * Anscheinend sollen nur die FLASH-controls den Cursor eins weiterstellen -
 * steht zwar nirgends, sieht aber am besten aus !!!
 */

int BTXService::do_CSI()
{
   int c2, c3, upper, lower;

   c2 = layer2getc();
   if(c2 == 0x42) {
	  LOG(("    STC stop conceal\n"));
	  set_attr(ATTR_CONCEALED, 0, 0, t.serialmode);
	  return 0;
   }

   LOG(("\n"));
   c3 = layer2getc();

   /* protection only available as fullrow controls ?? (page 135) */
   if(c2 == 0x31 && c3 == 0x50) {
	  LOG(("       PMS protected mode start\n"));
	  set_attr(ATTR_PROTECTED, 1, 0, 2);
	  return 0;
   }
   if(c2 == 0x31 && c3 == 0x51) {
	  LOG(("       PMC protected mode cancel\n"));
	  set_attr(ATTR_PROTECTED, 0, 0, 2);
	  return 0;
   }
   if(c2 == 0x32 && c3 == 0x53) {
	  LOG(("       MMS marked mode start\n"));
	  /* set_attr(ATTR_MARKED, 1, 0, t.serialmode); */
	  return 0;
   }
   if(c2 == 0x32 && c3 == 0x54) {
	  LOG(("       MMT marked mode stop\n"));
	  /* set_attr(ATTR_MARKED, 0, 0, t.serialmode); */
	  return 0;
   }

   switch(c3) {

	  case 0x40:
		 LOG(("       invoke CLUT%d\n", c2 - 0x2f));


	 t.clut = c2 - 0x30;
		 return 0;

	  case 0x41:
		 switch(c2) {
			case 0x30:
			   LOG(("       IVF inverted flash\n"));
			   return 1;
			case 0x31:
			   LOG(("       RIF reduced intesity flash\n"));
			   return 1;
			case 0x32:
			case 0x33:
			case 0x34:
			   LOG(("       FF%c fast flash %c\n", c2-1, c2-1));
			   return 1;
			case 0x35:
			   LOG(("       ICF increment flash\n"));
			   return 1;
			case 0x36:
			   LOG(("       DCF decrement flash\n"));
			   return 1;
		 }
		 break;

	  case 0x60:
		 switch(c2) {
			case 0x30:
			   LOG(("       SCU explicit scroll up\n"));
		   if(t.scroll_area) scroll(1);
			   return 0;
			case 0x31:
			   LOG(("       SCD explicit scroll down\n"));
		   if(t.scroll_area) scroll(0);

			   return 0;
			case 0x32:
			   LOG(("       AIS activate implicite scrolling\n"));
		   t.scroll_impl = 1;
			   return 0;
			case 0x33:
			   LOG(("       DIS deactivate implicite scrolling\n"));
		   t.scroll_impl = 0;
			   return 0;
		 }
		 break;

	  default:	/* definition of scrolling area (page 137) */
	 upper = c2 & 0x0f;
	 if(c3>=0x30 && c3<=0x39)  upper = upper*10 + (c3&0x0f);
	 LOG(("       upper row: %2d\n", upper));
	 if(c3>=0x30 && c3<=0x39)  { c3 = layer2getc(); LOG(("\n")); }

	 if(c3!=0x3b)  ERRLOG(("XBTX: scrolling area - protocol !\n"));

	 lower = layer2getc() & 0x0f;
	 LOG(("\n"));
	 c3 = layer2getc();
	 if(c3>=0x30 && c3<=0x39)  lower = lower*10 + (c3&0x0f);
	 LOG(("       lower row: %2d", lower));
	 if(c3>=0x30 && c3<=0x39)  { LOG(("\n")); c3=layer2getc(); LOG(("    ")); }

	 if(c3==0x55) {
		LOG(("   CSA create scrolling area\n"));
		if(upper>=2 && lower<rows && lower>=upper) {
		   t.scroll_upper = upper;
		   t.scroll_lower = lower;
		   t.scroll_area = 1;
		}
	 }
	 if(c3==0x56) {
		LOG(("   CSD delete scrolling area\n"));
		t.scroll_area = 0;
	 }
		 return 0;
   }
   return 0;
}


void BTXService::do_DRCS()	 /* (page 139ff) */
{
   int c3, c4, c5, c6, c7, c8;

   c3 = layer2getc();
   if(c3 == 0x20) {
	  LOG(("       DRCS header unit\n"));
	  c4 = layer2getc();
	  if(c4==0x20 || c4==0x28) {
	 LOG(("          %s existing DRCS\n", (c4==0x20) ? "keep" : "delete"));

	 if(c4==0x28)  AppDisplay->free_DRCS();
	 c5 = layer2getc();
	  } else c5 = c4;
	  if(c5 == 0x20) {
	 LOG(("\n"));
	 c6 = layer2getc();
	  } else c6 = c5;
	  if(c6 == 0x40) {
	 LOG(("\n"));
	 c7 = layer2getc();
	  } else c7 = c6;

	  switch(c7 & 0xf) {
		 case 6:
		LOG(("          12x12 pixel\n"));
		t.drcs_w = 12;
		t.drcs_h = 12;
		break;
	 case 7:
		LOG(("          12x10 pixel\n"));
		t.drcs_w = 12;
		t.drcs_h = 10;
		break;
	 case 10:
		LOG(("          6x12 pixel\n"));
		t.drcs_w = 6;
		t.drcs_h = 12;
		break;
	 case 11:
		LOG(("          6x10 pixel\n"));
		t.drcs_w = 6;
		t.drcs_h = 10;
		break;
	 case 12:
		LOG(("          6x5 pixel\n"));
		t.drcs_w = 6;
		t.drcs_h = 5;
		break;
	 case 15:
		LOG(("          6x6 pixel\n"));
		t.drcs_w = 6;
		t.drcs_h = 6;
		break;
	  }

	  c8 = layer2getc();
	  LOG(("          %d bit/pixel\n", c8 & 0xf));
	  t.drcs_bits = c8 & 0xf;
	  t.drcs_step = (t.drcs_h>=10 && t.drcs_w*t.drcs_bits==24)	?  2 : 1;
   }
   else {
	  LOG(("       DRCS pattern transfer unit (char: 0x%02x)\n", c3));
	  do_DRCS_data(c3);
   }
}


/*
 * load and define dynamic characters, first character is 'c'.
 * more than one bitplane of a character may be loaded simultaneously !
 */

void BTXService::do_DRCS_data(int c)
{
   int c4, i, n, planes=0, planemask=0, byte=0;
   int maxbytes, start = c;

   maxbytes = 2*t.drcs_h;
   if(t.drcs_h<10)	maxbytes *= 2;

   memset(data,0,sizeof(data));

   do {
	  c4 = layer2getc();

	  switch(c4) {
		 case 0x20:  /* S-bytes */
		 case 0x2f:
		LOG(("          fill rest of char with %d\n", c4 & 1));
		for(; byte<maxbytes; byte++)
		   for(n=0; n<4; n++)
			  if(planemask & (1<<n)) data[n][byte] = (UBYTE)((c4==0x20) ? 0 : 0xff);
		break;
	 case 0x21:
	 case 0x22:
	 case 0x23:
	 case 0x24:
	 case 0x25:
	 case 0x26:
	 case 0x27:
	 case 0x28:
	 case 0x29:
	 case 0x2a:
		LOG(("          repeat last row %d times\n", c4 & 0xf));
		if(byte&1) byte++;	 /* pad to full row (assume 0) */
			for(i=0; i<(c4 & 0xf); i++) {
		   for(n=0; n<4; n++)
			  if(planemask & (1<<n)) {
			 data[n][byte]	 = (UBYTE)(byte ? data[n][byte-2] : 0);
			 data[n][byte+1] = (UBYTE)(byte ? data[n][byte-2+1] : 0);
			 if(t.drcs_h<10) {
			data[n][byte+2] = (UBYTE)(byte ? data[n][byte-2] : 0);
			data[n][byte+3] = (UBYTE)(byte ? data[n][byte-2+1] : 0);
			 }
		  }
		   byte += 2;
		   if(t.drcs_h<10)	byte += 2;
		}
			break;
	 case 0x2c:
	 case 0x2d:
		LOG(("          full row %d\n", c4 & 1));
		if(byte&1) byte++;	 /* pad to full row (assume 0) */
			for(n=0; n<4; n++)
		   if(planemask & (1<<n)) {
		  data[n][byte]   = (UBYTE)((c4==0x2c) ? 0 : 0xff);
		  data[n][byte+1] = (UBYTE)((c4==0x2c) ? 0 : 0xff);
		  if(t.drcs_h<10) {
			 data[n][byte+2] = (UBYTE)((c4==0x2c) ? 0 : 0xff);
			 data[n][byte+3] = (UBYTE)((c4==0x2c) ? 0 : 0xff);
		  }
		   }
			byte += 2;
		if(t.drcs_h<10)  byte += 2;
			break;
	 case 0x2e:
		LOG(("          fill rest of char with last row\n"));
		if(byte&1) byte++;	 /* pad to full row (assume 0) */
			while(byte<maxbytes) {
		   for(n=0; n<4; n++)
			  if(planemask & (1<<n)) {
			 data[n][byte]	 = data[n][byte-2];
			 data[n][byte+1] = data[n][byte-2+1];
		  }
		   byte += 2;
		}
			break;

		 case 0x30:  /* B-bytes */
		 case 0x31:
		 case 0x32:
		 case 0x33:
		if(byte) {
		   LOG(("          new plane ahead - filling up\n"));
		   byte = maxbytes;   /* pad to full plane */
		   layer2ungetc(c4);
		}
		else {
		   LOG(("          start of pattern block (plane %d)\n", c4 & 0xf));
		   for(i=0; i<2*FONT_HEIGHT; i++)  data[c4 & 0xf][i] = 0;
		   planemask |= 1 << (c4 & 0xf);
		}
		break;

	 default:
			if(c4<0x20 || c4>0x7f) {
		   LOG(("          end of pattern data\n"));
		   layer2ungetc(c4);
		   if(byte)  byte = maxbytes;
		}


		else {	 /* D-bytes */
		   LOG(("          pattern data\n"));
		   if(t.drcs_w==6) {   /* low res */
		  for(n=0; n<4; n++)
			 if(planemask & (1<<n))  {
			data[n][byte] =
				 (UBYTE)(((c4&32)?0x30:0) | ((c4&16)?0x0c:0) | ((c4&8)?0x03:0));
			data[n][byte+1] =
				 (UBYTE)(((c4&4)?0x30:0) | ((c4&2)?0x0c:0) | ((c4&1)?0x03:0));
			 }
		  byte += 2;
		   }
		   else {
		  for(n=0; n<4; n++)
			 if(planemask & (1<<n))  data[n][byte] = (UBYTE)(c4 & 0x3f);
		  byte++;
		   }

		   if(!(byte&1) && t.drcs_h<10) {  /* duplicate row ? */
		  for(n=0; n<4; n++)
				 if(planemask & (1<<n)) {
			data[n][byte]	= data[n][byte-2];
			data[n][byte+1] = data[n][byte-2+1];
			 }
		  byte += 2;
		   }
		}
		break;

	  } /* switch */

	  if(byte == maxbytes) {  /* plane is complete */
	 for(n=0; n<4; n++)  if(planemask & (1<<n))  planes++;
	 planemask = 0;
	 byte = 0;
	 if(planes == t.drcs_bits) {  /* DRC is complete */
		log_DRC(c, t.drcs_bits);
		AppDisplay->define_raw_DRC(c, &data[0][0], t.drcs_bits);
		planes = 0;
		c += t.drcs_step;
	 }
	  }


   } while(c4>=0x20 && c4<=0x7f);

   update_DRCS_display(start, c, t.drcs_step);
}


/*
 * pretty print the received character to the LOG file
 */
void BTXService::log_DRC(int c, int bits)
{
   int n, i, j, k;

   LOG(("\n                      DRC # 0x%2x\n", c));
   for(n=0; n<bits; n++) {
	  LOG(("                      (plane %d)\n", n));
	  LOG(("                      --------------------------\n"));
	  for(j=0; j<fontheight; j++) {
	 LOG(("                      |"));
	 for(k=0; k<2; k++)
		for(i=5; i>=0; i--)
		   if(data[n][j*2+k] & (1<<i)) LOG(("* "));
		   else 					   LOG(("  "));
	 LOG(("|\n"));
	  }
	  LOG(("                      --------------------------\n\n"));
   }
}


/*
 * load and define dynamic colors.
 */
void BTXService::do_DEFCOLOR()	/* (page 150ff) */
{
   int c3, c4, c5, c6, c7, index, r, g, b;

   c3 = layer2getc();

   switch(c3) {


	  case 0x20:   /*  US 0x26 0x20 <ICT> <SUR> <SCM>  */
		 LOG(("       color header unit\n"));
		 t.col_modmap = 1;	  /* by default modify colormap */
		 c4 = layer2getc();
		 if((c4 & 0xf0) == 0x20) {
		if(c4!=0x20 && c4!=0x22) {
		   LOG(("*** <ICT>: bad value !\n"));
		   ERRLOG(("XBTX: do_DEFCOLOR(): ICT1 = 0x%02x\n", c4));
		}
			LOG(("          <ICT>: load %s\n", c4==0x20 ? "colormap" : "DCLUT"));
		t.col_modmap = (c4==0x20);
			c5 = layer2getc();
		 } else c5 = c4;
		 if((c5 & 0xf0) == 0x20) {
			LOG(("          <ICT>: (unit %d)\n", c5&0xf));
		if(c5!=0x20) {
		   LOG(("*** <ICT>: bad value !\n"));
		   ERRLOG(("XBTX: do_DEFCOLOR(): ICT2 = 0x%02x\n", c5));
		}
			c6 = layer2getc();
		 } else c6 = c5;
		 if((c6 & 0xf0) == 0x30) {
			LOG(("          <SUR>: %d bits\n", c6&0xf));
		if(c6!=0x34 && c6!=0x35) {
		   LOG(("*** <SUR>: bad value !\n"));
		   ERRLOG(("XBTX: do_DEFCOLOR(): SUR = 0x%02x\n", c6));
		}
			c7 = layer2getc();
		 } else c7 = c6;
		 if((c7 & 0xf0) == 0x40) {
			LOG(("          <SCM>: 0x%02x\n", c7));
		if(c7!=0x40 && c7!=0x41) {
		   LOG(("*** <SCM>: bad value !\n"));
		   ERRLOG(("XBTX: do_DEFCOLOR(): SCM = 0x%02x\n", c7));
		}
		 } else {
			LOG(("          default header\n"));
			layer2ungetc(c7);
		 }
		 break;

	  case 0x21:
		 LOG(("       color reset unit\n"));
	 AppDisplay->default_colors();
		 break;

	  default:
		 LOG(("       color transfer unit  (1.Stelle: %d)\n", c3&0xf));
	 index = c3&0xf;
		 c4 = layer2getc();
		 if((c4 & 0xf0) == 0x30) { /* c3 zehner, c4 einer */
			LOG(("                            (2.Stelle: %d)\n", c4&0xf));
		index = (c3&0xf)*10 + (c4&0xf);
			c5 = layer2getc();
		 } else c5 = c4;

	 if(t.col_modmap) {  /* load colormap */
		while(c5>=0x40 && c5<=0x7f) {
		   LOG(("          color #%2d:  R G B\n", index));
		   c6 = layer2getc();
		   r = (c5&0x20)>>2 | (c5&0x04)    | (c6&0x20)>>4 | (c6&0x04)>>2;
		   g = (c5&0x10)>>1 | (c5&0x02)<<1 | (c6&0x10)>>3 | (c6&0x02)>>1;
		   b = (c5&0x08)	| (c5&0x01)<<2 | (c6&0x08)>>2 | (c6&0x01);

		   LOG(("                      %1x %1x %1x\n", r, g, b));
		   if(index>=16 && index<=31)  AppDisplay->define_color(index++, r, g, b);
		   c5 = layer2getc();
		}
	 }
	 else {  /* load DCLUT */
		while(c5>=0x40 && c5<=0x7f) {
		   LOG(("          DCLUT[%2d] = %2d\n", index, c5&0x1f));
		   if(index>=0 && index<=3)  AppDisplay->define_DCLUT(index++, c5&0x1f);
		   c5 = layer2getc();
		}
	 }

	 LOG(("          end of color data\n"));
		 layer2ungetc(c5);
		 break;
   }
}


void BTXService::do_DEFFORMAT()   /* format designation  (page 155) */
{
   int c3, c4;

   rows = 24;
   fontheight = 10;
   t.wrap  = 1;

   c3 = layer2getc();
   if((c3&0xf0) == 0x40) {
	  switch(c3) {
		 case 0x41:
			LOG(("       40 columns by 24 rows\n"));
		break;
		 case 0x42:
		LOG(("       40 columns by 20 rows\n"));
		rows = 20;
		fontheight = 12;
		break;
		 default:
		LOG(("       unrecognized format (using default)\n"));
		break;
	  }
	  c4 = layer2getc();
   }
   else c4 = c3;

   if((c4&0xf0) == 0x70) {
	  LOG(("       wraparound %s\n", (c3&1) ? "inactive" : "active"));
	  t.wrap = (c3 == 0x70) ? 1 : 0;
   }
   else {
	  LOG(("       default format\n"));
	  layer2ungetc(c4);
   }
}


void BTXService::do_RESET()  /* reset functions  (page 157) */
{
   int y, c3, c4;

   c3 = layer2getc();

   switch(c3) {

	  case 0x40:   /* (page 158) */
		 LOG(("       service break to row\n"));
		 c4 = layer2getc();
		 LOG(("          #%d\n", c4 & 0x3f));
		 backup = t;  /* structure copy */
		 t.leftright[0] = t.prim;  /* PFUSCH !!! */
		 t.leftright[1] = t.supp;
	 t.save_left = t.prim;
		 t.wrap = 0;
	 t.cursor_on = 0;
	 move_cursor(APA, c4 & 0x3f, 1);
		 t.serialmode = 1;
	 t.clut = 0;
	 t.service_break = 1;
		 break;

	  case 0x41:
	  case 0x42:
		 LOG(("       defaults (%s C1)\n", c3&1 ? "serial" : "parallel"));
		 default_sets();
		 t.serialmode = c3 & 1;
		 t.wrap = 1;
	 t.cursor_on = 0;
	 rows = 24;
	 fontheight = 10;
	 for(y=0; y<24; y++)  AppDisplay->define_fullrow_bg(y, BLACK);
		 clearscreen();  /* clearscr resets CLUT, par_attr, cursor pos */
		 break;

	  case 0x43:
	  case 0x44:
		 LOG(("       limited defaults (%s C1)\n", c3&1 ? "serial":"parallel"));
		 default_sets();
		 t.serialmode = c3 & 1;
		 break;

	  case 0x4f:
		 LOG(("       reset to previous state\n"));
		 t = backup;
	 move_cursor(APA, t.cursory, t.cursorx);
		 break;
   }
}


/*
 * set/reset parallel, serial or fullrow attributes (+ set/delete markers)
 * (mode: 0=parallel, 1=serial, 2=fullrow)
 *
 * MARKER:	(page 91 / annex 1, page 29)
 *
 *	 parallel: (set in output())
 *		set wherever an attr is changed
 *		set at attr changes in writing a continuous string
 *		delete existing markers in overwritten part of "
 *		delete when printing and attrs do not change
 *
 *	 serial:   (set here)
 *		set at this position
 */

void BTXService::set_attr(int a, int set, int col, int mode)
{
   int x, y = t.cursory-1, refresh, mattr = a;

   /* set fullrow background */
   if(mode==2 && a==ATTR_BACKGROUND) {
	  AppDisplay->define_fullrow_bg(y, col);
	  return;
   }

   if(a & ATTR_ANYSIZE)  mattr = ATTR_SIZE;

   /* serial mode controls set a marker */
   if(mode==1)	screen[y][t.cursorx-1].mark |= mattr;

   /* serial/fullrow controls apply to parts of the row */
   if(mode) {  /* serial || fullrow */

	  /* receiption of serial DBS/DBH in last row of scrolling area 	   */
	  /* forces a scroll up + a cursor up before writing (page 101) */
	  if( (t.service_break || !(screen[y][0].attr & ATTR_PROTECTED))  &&
	 (a==ATTR_YDOUBLE || a==ATTR_XYDOUBLE) &&
	 t.scroll_area && t.cursory==t.scroll_lower) {
	 scroll(1);
	 move_cursor(APU);
	 y = t.cursory-1;
	  }

	  x = (mode==2) ? 0 : t.cursorx-1;

	  do {
	 refresh = 0;

	 /* fullrow controls delete the marker in the complete row */
	 if(mode==2)  screen[y][x].mark &= ~mattr;

	 if( t.service_break || !(screen[y][x].attr & ATTR_PROTECTED) ||
		(a==ATTR_PROTECTED && set==0) ) {
		switch(a) {
		   case ATTR_NODOUBLE:
			  if(screen[y][x].attr & (ATTR_XDOUBLE|ATTR_YDOUBLE))
			 refresh = 1;
		  screen[y][x].attr &= ~(ATTR_XDOUBLE|ATTR_YDOUBLE);
		  break;
		   case ATTR_XYDOUBLE:
		  if((screen[y][x].attr & (ATTR_XDOUBLE|ATTR_YDOUBLE)) !=
			 (ATTR_XDOUBLE|ATTR_YDOUBLE) )	refresh = 1;
		  screen[y][x].attr |= (ATTR_XDOUBLE|ATTR_YDOUBLE);
		  break;
		   case ATTR_XDOUBLE:
		  if((screen[y][x].attr & (ATTR_XDOUBLE|ATTR_YDOUBLE)) !=
			 ATTR_XDOUBLE )  refresh = 1;
		  screen[y][x].attr &= ~ATTR_YDOUBLE;
		  screen[y][x].attr |= ATTR_XDOUBLE;
		  break;
		   case ATTR_YDOUBLE:
		  if((screen[y][x].attr & (ATTR_XDOUBLE|ATTR_YDOUBLE)) !=
			 ATTR_YDOUBLE )  refresh = 1;
		  screen[y][x].attr &= ~ATTR_XDOUBLE;
		  screen[y][x].attr |= ATTR_YDOUBLE;
		  break;
		   case ATTR_FOREGROUND:
		  if(screen[y][x].fg!=col && screen[y][x].chr!=' ') refresh=1;
		  screen[y][x].fg = (UBYTE)col;
		  break;
		   case ATTR_BACKGROUND:
		  if(screen[y][x].bg != col)  refresh = 1;
		  screen[y][x].bg = (UBYTE)col;
		  break;

		   default:
		  if( ((screen[y][x].attr & a)>0) != set )	refresh = 1;
		  if(set)  screen[y][x].attr |= a;
		  else	   screen[y][x].attr &= ~a;
		  break;
		} /* switch */

		if(refresh)  redrawc(x+1, y+1);

		/* (DBH/DBS chars must not cross borders of a protected area) */
		if(a==ATTR_PROTECTED && y && realattr(y, x+1, ATTR_YDOUBLE) &&
		   attrib(y, x+1, a) != attrib(y+1, x+1, a) )  redrawc(x+1, y);

	 } /* if !protected */

	 x++;
	  } 		/* while x<40 && (serialmode --> no marker set) */
	  while(x<40  && (mode!=1 || !(screen[y][x].mark & mattr)) );
   }
   else {  /* parallel */
	  switch(a) {
		 case ATTR_NODOUBLE:
			t.par_attr &= ~(ATTR_XDOUBLE|ATTR_YDOUBLE);
		break;
	 case ATTR_XYDOUBLE:
		t.par_attr |= (ATTR_XDOUBLE|ATTR_YDOUBLE);
		break;
	 case ATTR_XDOUBLE:
		t.par_attr &= ~ATTR_YDOUBLE;
			t.par_attr |= ATTR_XDOUBLE;
		break;
	 case ATTR_YDOUBLE:
		t.par_attr &= ~ATTR_XDOUBLE;
			t.par_attr |= ATTR_YDOUBLE;
		break;
		 case ATTR_FOREGROUND:
		t.par_fg = col;
		break;
	 case ATTR_BACKGROUND:
		t.par_bg = col;
		break;
	 default:
		if(set)  t.par_attr |= a;
		else	 t.par_attr &= ~a;
		break;
	  }
   }
}


/*
 * output character c from current set at current cursor position.
 */

void BTXService::output(int c)
{
   int xd, yd, set, mattr, x = t.cursorx, y = t.cursory;

   /* select active character set */
   if(t.sshift) set = t.G0123L[ t.sshift ];
   else 		set = t.G0123L[ t.leftright[ (c&0x80) >> 7 ] ];

   if(t.serialmode) {
	  xd = attrib(y, x, ATTR_XDOUBLE);
	  yd = attrib(y, x, ATTR_YDOUBLE);

	  /* update screen memory (if not protected) */
	  if( t.service_break || !attrib(y, x, ATTR_PROTECTED) ) {
	 screen[y-1][x-1].chr = (unsigned short)(c & ~0x80);
	 screen[y-1][x-1].set = (UBYTE)set;
	 redrawc(x, y);
	  }
   }
   else {  /* parallel */
	  xd = (t.par_attr & ATTR_XDOUBLE) > 0;
	  yd = (t.par_attr & ATTR_YDOUBLE) > 0;
	  if(y<2)  yd = 0;

	  /* if not protected, (scroll +) update memory */
	  if( t.service_break || !attrib(y, x, ATTR_PROTECTED) ) {



	 /* parallel DBS/DBH chars in first row of scrolling area		  */
	 /* force a scroll down + a cursor down before writing (page 101) */
	 if(yd && t.scroll_area && y==t.scroll_upper) {
		scroll(0);
		move_cursor(APD);
		y = t.cursory;
	 }

	 /* update screen memory
	  * if no DBS/DBH in the first row or in the row below a protected area
	  * if no DBS/DBH in the row below a scrolling area (page 99)
	  */
	 if( !(yd && !t.service_break && attrib(y-1, x, ATTR_PROTECTED))  &&
		!(yd && t.scroll_area && y==t.scroll_lower+1)  ) {

		/* parallel DBH/DBS chars extent upwards, write in the row above */
		if(yd) y--;

		screen[y-1][x-1].chr  = (unsigned short)(c & ~0x80);
		screen[y-1][x-1].set  = (UBYTE)set;
		screen[y-1][x-1].fg   = (UBYTE)t.par_fg;
		screen[y-1][x-1].bg   = (UBYTE)t.par_bg;
		screen[y-1][x-1].attr = (unsigned short)t.par_attr;

		/* par. DBS/DBW attrs apply also to the next char ! (page 106) */
		if(xd && x<40)	screen[y-1][x].attr = (unsigned short)t.par_attr;

		mattr = t.par_attr & ~ATTR_ANYSIZE;
		if(t.par_attr & ATTR_ANYSIZE)  mattr |= ATTR_SIZE;

		if(x>1) {  /* set/reset markers */
		   screen[y-1][x-1].mark = (unsigned short)(screen[y-1][x-1-1].attr ^ mattr);
		   screen[y-1][x-1].mark &= ~(ATTR_FOREGROUND|ATTR_BACKGROUND);
		   if(screen[y-1][x-1-1].fg!=t.par_fg)
			  screen[y-1][x-1].mark |= ATTR_FOREGROUND;
		   if(screen[y-1][x-1-1].bg!=t.par_bg)
			  screen[y-1][x-1].mark |= ATTR_BACKGROUND;
		}
		if(x<40) {	/* set/reset markers */
		   screen[y-1][x].mark = (unsigned short)(mattr ^ screen[y-1][x].attr);
		   screen[y-1][x].mark &= ~(ATTR_FOREGROUND|ATTR_BACKGROUND);
		   if(screen[y-1][x].fg!=t.par_fg)
			  screen[y-1][x].mark |= ATTR_FOREGROUND;
		   if(screen[y-1][x].bg!=t.par_bg)
			  screen[y-1][x].mark |= ATTR_BACKGROUND;
		}

		redrawc(x, y);
	 } /* scrolling/protected area */
	  } /* if not protected */
   }  /* if serial / parallel */

   if(xd && t.cursorx<40)  move_cursor(APF);
   move_cursor(APF);
   t.sshift = 0;
}


/*
 * redraws character at screen position x, y (1 based !),
 * if not concealed or obscured
 */

void BTXService::redrawc(int x, int y)
{
//	 extern int reveal;
   int c, set, xd, yd, up_in=0, dn_in=0;
   unsigned int real, xreal, yreal, xyreal;

	  /* check whether attributes are valid */
	  xd = attrib(y, x, ATTR_XDOUBLE);
	  yd = attrib(y, x, ATTR_YDOUBLE);
	  if(x>=40)    xd = 0;
	  if(y>=rows)  yd = 0;

	  /* check whether origin is obscured */
	  if( (y>1 && realattr(y-1, x, ATTR_YDOUBLE)) ||
		  (x>1 && realattr(y, x-1, ATTR_XDOUBLE)) ||
	  (y>1 && x>1 && realattr(y-1, x-1, ATTR_XDOUBLE) &&
		  realattr(y-1, x-1, ATTR_YDOUBLE)) ) {
	 screen[y-1][x-1].real = 0;
	  }
	  else {
	 /* check whether DBW char is partially obscured */
	 if(xd && y>1 && x<40 && realattr(y-1, x+1, ATTR_YDOUBLE))	xd = 0;

	 /* check whether char crosses borders of scrolling area (page 99) */
	 if(yd && t.scroll_area) {
		if(y  >=t.scroll_upper && y  <=t.scroll_lower)	up_in=1;
		if(y+1>=t.scroll_upper && y+1<=t.scroll_lower)	dn_in=1;
		if(up_in != dn_in)	yd = 0;
	 }

	 /* check whether char crosses borders of protected area (page 110) */
	 if(yd &&  attrib(y, x, ATTR_PROTECTED) !=
			   attrib(y+1, x, ATTR_PROTECTED) )  yd=0;

	 /* concealed characters are drawn as SPACES (page 109) */
//	 if(!reveal && attrib(y, x, ATTR_CONCEALED)) { c=' ';	set=PRIM; }
//	 else {
		c	= screen[y-1][x-1].chr & 0x7f;
		set = screen[y-1][x-1].set;
//	 }

	 /* now really display the character in the remaining size */
	 AppDisplay->xputc(c, set, x-1, y-1, xd, yd,
		   attrib(y, x, ATTR_UNDERLINE),
		   (screen[y-1][x-1].chr>>8) & 0x7f,
		   attrib(y, x, ATTR_INVERTED) ? attr_bg(y, x) : attr_fg(y, x),
		   attrib(y, x, ATTR_INVERTED) ? attr_fg(y, x) : attr_bg(y, x) );
	 if(t.cursor_on && x==t.cursorx && y==t.cursory)  AppDisplay->xcursor(x-1, y-1);

	 /* update 'real' attributes */
	 real = screen[y-1][x-1].real;
	 screen[y-1][x-1].real = (unsigned short)(screen[y-1][x-1].attr & ~ATTR_ANYSIZE);
	 if(xd) {
		screen[y-1][x-1].real |= ATTR_XDOUBLE;
		xreal = screen[y-1][x].real;
		screen[y-1][x].real = 0;
	 }
	 if(yd) {
		screen[y-1][x-1].real |= ATTR_YDOUBLE;
		yreal = screen[y][x-1].real;
		screen[y][x-1].real = 0;
	 }
	 if(xd && yd) {
		xyreal = screen[y][x].real;
		screen[y][x].real = 0;
	 }

	 /* redraw (indirect recursion !!) neighbours */
	 updatec(real, x, y);
	 if(xd)  updatec(xreal, x+1, y);
	 if(yd)  updatec(yreal, x, y+1);
	 if(xd && yd)  updatec(xyreal, x+1, y+1);

	 /* redraw an enlarged char to the left (now partially obscured !) */
	 if(yd && x>1 && realattr(y+1, x-1, ATTR_XDOUBLE))	redrawc(x-1, y+1);
	  } /* origin obscured */
}


/*
 * this character has been obscured. check whether it was an enlarged char
 * and therefor its neighbours have to be redrawn.
 */
void BTXService::updatec(int real, int x, int y)
{
   if(real & ATTR_XDOUBLE)	redrawc(x+1, y);
   if(real & ATTR_YDOUBLE)	redrawc(x, y+1);
   if( (real & ATTR_XDOUBLE) && (real & ATTR_YDOUBLE) )  redrawc(x+1, y+1);
}


/*
 * redraw rectangle of screen (for exposure handling)
 */


void BTXService::redraw_screen_rect(int x1, int y1, int x2, int y2)
{
   int x, y;

   for(y=y1; y<=y2; y++)
	  for(x=x1; x<=x2; x++)  redrawc(x+1, y+1);
}


/*
 * DRC's in the range start-stop with step have been (re)defined.
 * This routine checks whether this character is currently visible and so
 * needs to be redisplayed.
 */

void BTXService::update_DRCS_display(int start, int stop, int step)
{
   int x, y, c;

   for(y=0; y<24; y++)
	  for(x=0; x<40; x++)
	 if(screen[y][x].set==DRCS)
		for(c=start; c<stop; c+=step)
		   if(screen[y][x].chr==c)	redrawc(x+1, y+1);
}


/*
 * initialize screen memory and clear display
 */

void BTXService::clearscreen()
{
   int x, y;

   t.clut = 0;
   t.par_attr = 0;
   t.par_fg = WHITE;
   t.par_bg = TRANSPARENT;
   t.scroll_area = 0;
   t.scroll_impl = 1;
   t.cursorx = t.cursory = 1;

   for(y=0; y<24; y++)
	  for(x=0; x<40; x++) {
	 screen[y][x].chr = ' ';
	 screen[y][x].set = PRIM;
	 screen[y][x].attr = 0;
	 screen[y][x].real = 0;
	 screen[y][x].mark = 0;
	 screen[y][x].fg = WHITE;
	 screen[y][x].bg = TRANSPARENT;
	  }

   AppDisplay->xclearscreen();
   if(t.cursor_on) AppDisplay->xcursor(0, 0);
}

void BTXService::scroll(int up)
{
   int y, x, savereal[40];

   if(up) {
	  /* save real attributes of first row */
	  for(x=0; x<40; x++)  savereal[x] = screen[t.scroll_upper-1][x].real;

	  /* scroll screen memory up */
	  for(y=t.scroll_upper-1; y<t.scroll_lower-1; y++) {
	 for(x=0; x<40; x++)  screen[y][x] = screen[y+1][x];
	  }
   }
   else {
	  /* scroll screen memory down */
	  for(y=t.scroll_lower-1; y>t.scroll_upper-1; y--) {
	 for(x=0; x<40; x++)  screen[y][x] = screen[y-1][x];
	  }
   }

   /* clear row */
   for(x=0; x<40; x++) {
	  screen[y][x].chr = ' ';
	  screen[y][x].set = PRIM;
	  screen[y][x].attr = 0;
	  screen[y][x].real = 0;
	  screen[y][x].mark = 0;
	  screen[y][x].fg = WHITE;
	  screen[y][x].bg = TRANSPARENT;
   }

   /* fast scroll the area */
   for(y=t.scroll_upper; y<=t.scroll_lower; y++)
	  for(x=1; x<=40; x++)	redrawc(x, y);

   /* redraw cursor */
   if(t.cursor_on && t.cursory>=t.scroll_upper && t.cursory<=t.scroll_lower)
	  AppDisplay->xcursor(t.cursorx-1, t.cursory-1);
}

void BTXService::do_Telesoftware(void)
{
	int c,i,j,len,total,size,temp;
	UBYTE *data,*final;

	c = layer2getc();

	if(reachedEOF)
		return;

	switch(c)
	{
		case 0x27:			// D-Set mode, A7.4, p5

			for(i = 0 ; i < 4 ; i++)
				layer2getc();

			c = layer2getc();

			if(reachedEOF)
				return;

			telemode = MODE_Unknown;

			if(c == 0x42)
				telemode = MODE_3in4;

			if(c == 0x41)
				telemode = MODE_8Bit;

			telesequence = 0x41;

			LOG(("d-set mode, mode = %ld\n",telemode));

			break;

		case 0x33:			// D-End group, A7.4, p6

			LOG(("d-end group\n"));

			if(telefile)
			{
				fclose(telefile);
				telefile = NULL;
			}

			telemode = MODE_Unknown;

			teledownload = FALSE;

			telename[0] = 0;

			break;

		default:			// D-Data, A7.4, p3

			telesequence = c;

			LOG(("sequence code 0x%02lx\n",c));

			if(telemode != MODE_3in4)	// Hier ist nur 3in4 zugelassen
				break;

			total = 0;

			for(;;)
			{
				c = layer2getc();

				if(reachedEOF)
					return;
				else
				{
					if(c == US)
					{
						layer2ungetc(c);
						break;
					}
					else
						telebuffer[total++] = (UBYTE)c;
				}
			}

			len		= decode_3in4(telebuffer,telebuffer,total);
			data	= telebuffer;

			while(len > 0)
			{
				switch(*data)
				{
					case 0x23:			// T-Associate, A7.4, p7

						len--;
						data++;

						LOG(("t-associate\n"));

						teledownload = TRUE;

						len--;
						size = *data++;

						len -= size;
						data += size;

						break;

					case 0x63:			// T-Filespec, A7.4, p9

						len--;
						data++;

						LOG(("t-filespec\n"));

						len--;
						size = *data++;
						final = data + size;
						len -= size;

						data++;
						size--;

						while(size > 0)
						{
							c = *data++;
							size--;

							switch(c)
							{
								case 0x65:		// Filename

									total = *data++;
									size--;

									for(j = 0 ; j < total ; j++)
									{
										telename[j] = (char)*data++;
										size--;
									}

									telename[j] = 0;

									LOG(("filename |%s|\n",telename));

									break;

								case 0x7F:		// Date/time

									total = *data++;
									size--;

									for(j = 0 ; j < total ; j++)
									{
										teledate[j] = (char)*data++;
										size--;
									}

									teledate[j] = 0;

									LOG(("date |%s|\n",teledate));

									break;

								case 0x67:		// File length

									total = *data++;
									size--;

									temp = 0;

									for(j = 0 ; j < total ; j++)
									{
										temp = (temp * 256) + (*data++);
										size--;
									}

									telesize = temp;

									LOG(("size %ld\n",temp));

									break;

								default:		// Was auch immer...

									total = *data++;
									size--;

									data += total;
									size -= total;
									break;
							}
						}

						data = final;

						break;

					case 0x43:	// T-Write-Start, A7.4, p10

						len--;
						data++;

						LOG(("t-write-start\n"));

						len--;
						size = *data++;

						len -= size;
						data += size;

						if(telefile)
						{
							fclose(telefile);
							telefile = NULL;
						}

						if(!telefile && teledownload)
						{
							if(telename[0])
								telefile = fopen((const char *)telename,"wb");
							else
								telefile = fopen((const char *)"telesoftware","wb");
						}

						if(len)
						{
							LOG(("\t%ld bytes in this block\n",len));

							if(telefile)
							{
								if(fwrite(data,len,1,telefile) != 1)
									return;
							}

							len = 0;
						}

						break;

					case 0x45:	// T-Write, A7.4, p11

						len--;
						data++;

						LOG(("t-write\n"));

						len--;
						size = *data++;

						len -= size;
						data += size;

						if(len)
						{
							LOG(("\t%ld bytes in this block\n",len));

							if(telefile)
							{
								if(fwrite(data,len,1,telefile) != 1)
									return;
							}

							len = 0;
						}

						break;

					case 0x47:	// T-Write-End, A7.4, p11

						len--;
						data++;

						LOG(("t-write-end\n"));

						len--;
						size = *data++;

						len -= size;
						data += size;

						if(len)
						{
							LOG(("\t%ld bytes in this block\n",len));

							if(telefile)
							{
								if(fwrite(data,len,1,telefile) != 1)
									return;
							}


							len = 0;
						}

						if(telefile)
						{
							fclose(telefile);
							telefile = NULL;
						}

						break;

					default:

						LOG(("unknown sequence 0x%02lx\n",c));

						len--;
						data++;

						len--;
						size = *data++;

						len -= size;
						data += size;

						break;
				}
			}

			break;
	}
}

void BTXService::write(const UBYTE *what,int len)
{
	App->AppChannel->PutString((CONST STRPTR)what,len);
}

int BTXService::decode_3in4(const UBYTE *data,UBYTE *out,int total)
{
	UBYTE buf[4];
	int len,eaten,done = 0;

	while(total > 0)
	{
		if(total == 2)
		{
			buf[0] = (UBYTE)(((data[0] & 0x30) << 2) | (data[1] & 0x3f));
			len = 1;
			eaten = 2;
		}
		else
		{
			if(total == 3)
			{
				buf[0] = (UBYTE)(((data[0] & 0x30) << 2) | (data[1] & 0x3f));
				buf[1] = (UBYTE)(((data[0] & 0x0c) << 4) | (data[2] & 0x3f));
				len = 2;
				eaten = 3;
			}
			else
			{
				if(total >= 4)
				{
					buf[0] = (UBYTE)(((data[0] & 0x30) << 2) | (data[1] & 0x3f));
					buf[1] = (UBYTE)(((data[0] & 0x0c) << 4) | (data[2] & 0x3f));
					buf[2] = (UBYTE)(((data[0] & 0x03) << 6) | (data[3] & 0x3f));
					len = 3;
					eaten = 4;
				}
				else
					break;
			}
		}

		memcpy(out,buf,len);

		total -= eaten;
		data += eaten;

		out += len;
		done += len;
	}

	return(done);
}
