/*
 *  linux/kernel/console.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */

/*
 *	console.c
 *
 * This module exports the console io functions:
 * 
 *	'long con_init(long)'
 *	'int con_open(struct tty_struct *tty, struct file * filp)'
 * 	'void update_screen(int new_console)'
 * 	'void blank_screen(void)'
 * 	'void unblank_screen(void)'
 * 
 * Hopefully this will be a rather complete VT102 implementation.
 *
 * Beeping thanks to John T Kohl.
 * 
 * Virtual Consoles, Screen Blanking, Screen Dumping, Color, Graphics
 *   Chars, and VT100 enhancements by Peter MacDonald.
 *
 * Copy and paste function by Andrew Haylett.
 *
 * 680x0 LINUX support by Arno Griffioen (arno@usn.nl)
 * 680x0 doesn't have cut&paste support yet. Bit hard on a bitmapped
 * screen. Have to keep some sort of second (ASCII) screen in the
 * background for that. May even be used for VC's....
 *
 * 9-Apr-94:  Arno Griffioen: fixed scrolling and delete-char bug.
 *            Scrolling code moved to amicon.c
 *
 * 18-Apr-94: David Carter [carter@cs.bris.ac.uk]. 680x0 LINUX modified 
 *            Integrated support for new low level driver `amicon_ocs.c'
 *
 */

/*
 *  NOTE!!! We sometimes disable and enable interrupts for a short while
 * (to put a word in video IO), but this will work even for keyboard
 * interrupts. We know interrupts aren't enabled when getting a keyboard
 * interrupt, as we use trap-gates. Hopefully all is well.
 */

/*
 * Code to check for different video-cards mostly by Galen Hunt,
 * <g-hunt@ee.utah.edu>
 */

#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/tty.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/console.h>
#include <linux/kd.h>

#include "vt_kern.h"

#ifdef CONFIG_SELECTION
#include <linux/ctype.h>

/* Routines for selection control. */
int set_selection(const int arg);
int paste_selection(struct tty_struct *tty);
static void clear_selection(void);

/* Variables for selection control. */
#define SEL_BUFFER_SIZE TTY_BUF_SIZE
static int sel_cons;
static int sel_start = -1;
static int sel_end;
static char sel_buffer[SEL_BUFFER_SIZE] = { '\0' };
#endif /* CONFIG_SELECTION */

extern void vt_init(void);
extern void register_console(void (*proc)(const char *));
extern void compute_shiftstate(void);

static int console_blanked = 0;
static int can_do_color = 0;
static int printable = 0;

struct condata vc_cons[NR_CONSOLES];
struct consw *conswitchp;

#define	flags		(vc_cons[currcons].vc_flags)
#define	x		(vc_cons[currcons].vc_x)
#define	y		(vc_cons[currcons].vc_y)
#define top		(vc_cons[currcons].vc_top)
#define bottom		(vc_cons[currcons].vc_bottom)
/*
 * Arno:
 * On the 680x0 we could have screens with different sizes (at least
 * on the Amiga), so we have to store it with each console instead
 * of using a global variable like the original PC code.
 */
#define	video_num_columns	(vc_cons[currcons].vc_cols)
#define	video_num_lines		(vc_cons[currcons].vc_rows)
#define	state		(vc_cons[currcons].vc_state)
#define	npar		(vc_cons[currcons].vc_npar)
#define	par		(vc_cons[currcons].vc_par)
#define	ques		(vc_cons[currcons].vc_ques)
#define	attr		(vc_cons[currcons].vc_attr)
#define	saved_x		(vc_cons[currcons].vc_saved_x)
#define	saved_y		(vc_cons[currcons].vc_saved_y)
#define	translate	(vc_cons[currcons].vc_translate)
#define	G0_charset	(vc_cons[currcons].vc_G0_charset)
#define	G1_charset	(vc_cons[currcons].vc_G1_charset)
#define	saved_G0	(vc_cons[currcons].vc_saved_G0)
#define	saved_G1	(vc_cons[currcons].vc_saved_G1)
#define	video_erase_char (vc_cons[currcons].vc_video_erase_char)	
#define	decscnm		(vc_cons[currcons].vc_decscnm)
#define	decom		(vc_cons[currcons].vc_decom)
#define	decawm		(vc_cons[currcons].vc_decawm)
#define	deccm		(vc_cons[currcons].vc_deccm)
#define	decim		(vc_cons[currcons].vc_decim)
#define	need_wrap	(vc_cons[currcons].vc_need_wrap)
#define	color		(vc_cons[currcons].vc_color)
#define	s_color		(vc_cons[currcons].vc_s_color)
#define	def_color	(vc_cons[currcons].vc_def_color)
#define	foreground	(color & 0x0f)
#define	background	(color & 0xf0)
#define	charset		(vc_cons[currcons].vc_charset)
#define	s_charset	(vc_cons[currcons].vc_s_charset)
#define	intensity	(vc_cons[currcons].vc_intensity)
#define	underline	(vc_cons[currcons].vc_underline)
#define	blink		(vc_cons[currcons].vc_blink)
#define	reverse		(vc_cons[currcons].vc_reverse)
#define	s_intensity	(vc_cons[currcons].vc_s_intensity)
#define	s_underline	(vc_cons[currcons].vc_s_underline)
#define	s_blink		(vc_cons[currcons].vc_s_blink)
#define	s_reverse	(vc_cons[currcons].vc_s_reverse)
#define	ulcolor		(vc_cons[currcons].vc_ulcolor)
#define	halfcolor	(vc_cons[currcons].vc_halfcolor)
#define	kbdmode		(vc_cons[currcons].vc_kbdmode)
#define	tab_stop	(vc_cons[currcons].vc_tab_stop)
#define	sw		(vc_cons[currcons].vc_sw)

#define	vcmode		(vt_cons[currcons].vc_mode)
#define	vtmode		(vt_cons[currcons].vt_mode)
#define	vtpid		(vt_cons[currcons].vt_pid)
#define	vtnewvt		(vt_cons[currcons].vt_newvt)

#define decarm		VC_REPEAT
#define decckm		VC_CKMODE
#define kbdapplic	VC_APPLIC
#define kbdraw		VC_RAW
#define lnm		VC_CRLF

/*
 * this is what the terminal answers to a ESC-Z or csi0c query.
 */
#define VT100ID "\033[?1;2c"
#define VT102ID "\033[?6c"

static unsigned char * translations[] = {
/* 8-bit Latin-1 mapped to the PC charater set: '\0' means non-printable */
(unsigned char *)
	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
	"\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0"
	" !\"#$%&'()*+,-./0123456789:;<=>?"
	"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
	"`abcdefghijklmnopqrstuvwxyz{|}~\0"
	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
	"\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376"
	"\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250"
	"\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376"
	"\376\245\376\376\376\376\231\376\350\376\376\376\232\376\376\341"
	"\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
	"\376\244\225\242\223\376\224\366\355\227\243\226\201\376\376\230",
/* vt100 graphics */
(unsigned char *)
	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
	"\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0"
	" !\"#$%&'()*+,-./0123456789:;<=>?"
	"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ "
	"\004\261\007\007\007\007\370\361\007\007\331\277\332\300\305\304"
	"\304\304\137\137\303\264\301\302\263\363\362\343\330\234\007\0"
	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
	"\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376"
	"\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250"
	"\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376"
	"\376\245\376\376\376\376\231\376\376\376\376\376\232\376\376\341"
	"\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
	"\376\244\225\242\223\376\224\366\376\227\243\226\201\376\376\230",
/* IBM graphics: minimal translations (BS, CR, LF, LL, SO, SI and ESC) */
(unsigned char *)
	"\000\001\002\003\004\005\006\007\000\011\000\013\000\000\000\000"
	"\020\021\022\023\024\025\026\027\030\031\032\000\034\035\036\037"
	"\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
	"\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077"
	"\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117"
	"\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137"
	"\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157"
	"\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177"
	"\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"
	"\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"
	"\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"
	"\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"
	"\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
	"\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
	"\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
	"\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377"
};

#define NORM_TRANS (translations[0])
#define GRAF_TRANS (translations[1])
#define NULL_TRANS (translations[2])

static unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
				       8,12,10,14, 9,13,11,15 };

/*
 * gotoxy() must verify all boundaries, because the arguments
 * might also be negative. If the given position is out of
 * bounds, the cursor is placed at the nearest margin.
 */
static void gotoxy(int currcons, int new_x, int new_y)
{
	int max_y;

	if (new_x < 0)
		x = 0;
	else
		if (new_x >= video_num_columns)
			x = video_num_columns - 1;
		else
			x = new_x;
 	if (decom) {
		new_y += top;
		max_y = bottom;
	} else
		max_y = video_num_lines;
	if (new_y < 0)
		y = 0;
	else
		if (new_y >= max_y)
			y = max_y - 1;
		else
			y = new_y;
	need_wrap = 0;
}

static void hide_cursor(int currcons)
{
	sw->con_cursor(&vc_cons[currcons],CM_ERASE);
	return;
}

static void set_cursor(int currcons)
{
	if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
		return;
	if (deccm) {
		sw->con_cursor(&vc_cons[currcons],CM_DRAW);
	} else
		hide_cursor(currcons);
	return;
}

/*
 * Arno:
 * Why do we need these? The keyboard code doesn't seem to do anything
 * with them either...
 */
void scrollfront(int l)
{
	return;
}

void scrollback(int l)
{
	return;
}

static void scrup(int currcons, unsigned int t, unsigned int b)
{
	if (b > video_num_lines || t >= b)
		return;

/*
 * Arno:
 * Scrolling has now been moved to amicon.c where it should have
 * been all along.
 */
	sw->con_scroll(&vc_cons[currcons], t, b, SM_UP, 1);

	return;
	
}

static void scrdown(int currcons, unsigned int t, unsigned int b)
{
	if (b > video_num_lines || t >= b)
		return;

/*
 * Arno:
 * Scrolling has now been moved to amicon.c where it should have
 * been all along.
 */
	sw->con_scroll(&vc_cons[currcons], t, b, SM_DOWN, 1);

	return;
}

static void lf(int currcons)
{
	if (y+1<bottom) {
		y++;
		return;
	} else 
		scrup(currcons,top,bottom);
	need_wrap = 0;
}

static void ri(int currcons)
{
	if (y>top) {
		y--;
		return;
	} else
		scrdown(currcons,top,bottom);
	need_wrap = 0;
}

static inline void cr(int currcons)
{
	need_wrap = x = 0;
}

static inline void bs(int currcons)
{
	if (x) {
		x--;
		need_wrap = 0;
	}
}

static inline void del(int currcons)
{
#if 0
	if (x) {
		if (!need_wrap) {    /* this is not the right condition */
			x--;
		}
		need_wrap = 0;
	}
#endif
}

static void csi_J(int currcons, int vpar)
{
	switch (vpar) {
		case 0:	/* erase from cursor to end of display */
			/* 680x0 do in two stages */
			sw->con_clear(&vc_cons[currcons],y,x,1,video_num_columns-x);
			sw->con_clear(&vc_cons[currcons],y+1,0,video_num_lines-y-1, video_num_columns);
			break;
		case 1:	/* erase from start to cursor */
			/* 680x0 do in two stages */
			sw->con_clear(&vc_cons[currcons],0,0,y, video_num_columns);
			sw->con_clear(&vc_cons[currcons],y,0,1,x);
			break;
		case 2: /* erase whole display */
			sw->con_clear(&vc_cons[currcons],0,0,video_num_lines, video_num_columns);
			break;
		default:
			return;
	}
	need_wrap = 0;
}

static void csi_K(int currcons, int vpar)
{
	switch (vpar) {
		case 0:	/* erase from cursor to end of line */
			sw->con_clear(&vc_cons[currcons],y,x,1,video_num_columns-x);
			break;
		case 1:	/* erase from start of line to cursor */
			sw->con_clear(&vc_cons[currcons],y,0,1,x);
			break;
		case 2: /* erase whole line */
			sw->con_clear(&vc_cons[currcons],y,0,1,video_num_columns);
			break;
		default:
			return;
	}
	need_wrap = 0;
}

/*
 *  I hope this works. The monochrome part is untested.
 */

/*
 * Arno: 
 * On 680x0 attributes are currently not used. This piece of code
 * seems hardware independent, but uses the EGA/VGA way of representing
 * attributes. 
 * TODO: modify for 680x0 and add attribute processing to putc code.
 */
static void update_attr(int currcons)
{
	attr = color;
	if (can_do_color) {
		if (underline)
			attr = (attr & 0xf0) | ulcolor;
		else if (intensity == 0)
			attr = (attr & 0xf0) | halfcolor;
	}
	if (reverse ^ decscnm)
		attr = (attr & 0x88) | (((attr >> 4) | (attr << 4)) & 0x77);
	if (blink)
		attr ^= 0x80;
	if (intensity == 2)
		attr ^= 0x08;
	if (!can_do_color) {
		if (underline)
			attr = (attr & 0xf8) | 0x01;
		else if (intensity == 0)
			attr = (attr & 0xf0) | 0x08;
	}
	if (decscnm)
		video_erase_char = ((color & 0x88) | (((color >> 4) |
			(color << 4)) & 0x77) << 8) | ' ';
	else
		video_erase_char = (color << 8) | ' ';
}

static void default_attr(int currcons)
{
	intensity = 1;
	underline = 0;
	reverse = 0;
	blink = 0;
	color = def_color;
}

static void csi_m(int currcons)
{
	int i;

	for (i=0;i<=npar;i++)
		switch (par[i]) {
			case 0:	/* all attributes off */
				default_attr(currcons);
				break;
			case 1:
				intensity = 2;
				break;
			case 2:
				intensity = 0;
				break;
			case 4:
				underline = 1;
				break;
			case 5:
				blink = 1;
				break;
			case 7:
				reverse = 1;
				break;
			case 21:
			case 22:
				intensity = 1;
				break;
			case 24:
				underline = 0;
				break;
			case 25:
				blink = 0;
				break;
			case 27:
				reverse = 0;
				break;
			case 39:
				color = (def_color & 0x0f) | background;
				break;
			case 49:
				color = (def_color & 0xf0) | foreground;
				break;
			default:
				if (par[i] >= 30 && par[i] <= 37)
					color = color_table[par[i]-30]
						| background; 
				else if (par[i] >= 40 && par[i] <= 47)
					color = (color_table[par[i]-40]<<4)
						| foreground;
				break;
		}
	update_attr(currcons);
}

static void respond_string(char * p, int currcons, struct tty_struct * tty)
{
	while (*p) {
		put_tty_queue(*p, &tty->read_q);
		p++;
	}
	TTY_READ_FLUSH(tty);
}

static void respond_num(unsigned int n, int currcons, struct tty_struct * tty)
{
	char buff[3];
	int i = 0;

	do {
		buff[i++] = (n%10)+'0';
		n /= 10;
	} while(n && i < 3);	/* We'll take no chances */
	while (i--) {
		put_tty_queue(buff[i], &tty->read_q);
	}
	/* caller must flush */
}

static void cursor_report(int currcons, struct tty_struct * tty)
{
	put_tty_queue('\033', &tty->read_q);
	put_tty_queue('[', &tty->read_q);
	respond_num(y + (decom ? top+1 : 1), currcons, tty);
	put_tty_queue(';', &tty->read_q);
	respond_num(x+1, currcons, tty);
	put_tty_queue('R', &tty->read_q);
	TTY_READ_FLUSH(tty);
}

static inline void status_report(int currcons, struct tty_struct * tty)
{
	respond_string("\033[0n", currcons, tty);	/* Terminal ok */
}

static inline void respond_ID(int currcons, struct tty_struct * tty)
{
	respond_string(VT102ID, currcons, tty);
}

#if 0
/* 
 * Arno:
 * Not nice on a bit-mapped screen :-(
 * Won't implement it for now... Don't think anyone will care..
 */

static void invert_screen(int currcons) {
	return;
}
#endif

static void set_mode(int currcons, int on_off)
{
	int i;

	for (i=0; i<=npar; i++)
		if (ques) switch(par[i]) {	/* DEC private modes set/reset */
			case 1:			/* Cursor keys send ^[Ox/^[[x */
			/* Arno: Dunno about KBD stuff...
			 * Just ignire it for the moment.
				if (on_off)
					set_kbd(decckm);
				else
					clr_kbd(decckm); 
			*/
				break;
			case 3:	/* 80/132 mode switch unimplemented */
				csi_J(currcons,2);
				gotoxy(currcons,0,0);
				break;
			case 5:			/* Inverted screen on/off */
				if (decscnm != on_off) {
					decscnm = on_off;
					/*invert_screen(currcons);*/
					update_attr(currcons);
				}
				break;
			case 6:			/* Origin relative/absolute */
				decom = on_off;
				gotoxy(currcons,0,0);
				break;
			case 7:			/* Autowrap on/off */
				decawm = on_off;
				break;
			case 8:			/* Autorepeat on/off */
				/* 680x0: no kbd handling?
				if (on_off)
					set_kbd(decarm);
				else
					clr_kbd(decarm);
				*/
				break;
			case 25:		/* Cursor on/off */
				deccm = on_off;
				set_cursor(currcons);
				break;
		} else switch(par[i]) {		/* ANSI modes set/reset */
			case 4:			/* Insert Mode on/off */
				decim = on_off;
				break;
			case 20:		/* Lf, Enter == CrLf/Lf */
				/* 680x0: no kkbd handling?
				if (on_off)
					set_kbd(lnm);
				else
					clr_kbd(lnm);
				*/
				break;
		}
}

static void setterm_command(int currcons)
{
	switch(par[0]) {
		case 1:	/* set color for underline mode */
			if (can_do_color && par[1] < 16) {
				ulcolor = color_table[par[1]];
				if (underline)
					update_attr(currcons);
			}
			break;
		case 2:	/* set color for half intensity mode */
			if (can_do_color && par[1] < 16) {
				halfcolor = color_table[par[1]];
				if (intensity == 0)
					update_attr(currcons);
			}
			break;
		case 8:	/* store colors as defaults */
			def_color = attr;
			default_attr(currcons);
			update_attr(currcons);
			break;
		case 9:	/* set blanking interval */
			/* Arno:
			 * Well... How does blanking work? Can it
			 * function in the 680x0 kernel? 
			 * blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
			 */
			break;
	}
}

static void insert_char(int currcons)
{
	/* Arno:
	 * Move the remainder of the line (-1 character) one spot to the right
	 */
	sw->con_bmove(&vc_cons[currcons],y,x,y,x+1,1,(video_num_columns-x-1));
	/*
	 * Print the erase char on the current position
	 */
	sw->con_putc(&vc_cons[currcons],(video_erase_char & 0x00ff),y,x,DM_COPY);

	need_wrap = 0;
}

static void insert_line(int currcons)
{
	scrdown(currcons,y,bottom);
	need_wrap = 0;
}

static void delete_char(int currcons)
{
	/* Arno
	 * Move the remainder of the line one spot to the left 
	 */

	sw->con_bmove(&vc_cons[currcons],y,x+1,y,x,1,(video_num_columns-x-1));

	/* 
	 * Print the erase char at the end of the line to remove any 
	 * remaining junk
	 */
	sw->con_putc(&vc_cons[currcons],(video_erase_char & 0x00ff),y,video_num_columns-1,DM_COPY);

	need_wrap = 0;
}

static void delete_line(int currcons)
{
	scrup(currcons,y,bottom);
	need_wrap = 0;
}

static void csi_at(int currcons, unsigned int nr)
{
	if (nr > video_num_columns)
		nr = video_num_columns;
	else if (!nr)
		nr = 1;
	while (nr--)
		insert_char(currcons);
}

static void csi_L(int currcons, unsigned int nr)
{
	if (nr > video_num_lines)
		nr = video_num_lines;
	else if (!nr)
		nr = 1;
	while (nr--)
		insert_line(currcons);
}

static void csi_P(int currcons, unsigned int nr)
{
	if (nr > video_num_columns)
		nr = video_num_columns;
	else if (!nr)
		nr = 1;
	while (nr--)
		delete_char(currcons);
}

static void csi_M(int currcons, unsigned int nr)
{
	if (nr > video_num_lines)
		nr = video_num_lines;
	else if (!nr)
		nr=1;
	while (nr--)
		delete_line(currcons);
}

static void save_cur(int currcons)
{
	saved_x		= x;
	saved_y		= y;
	s_intensity	= intensity;
	s_underline	= underline;
	s_blink		= blink;
	s_reverse	= reverse;
	s_charset	= charset;
	s_color		= color;
	saved_G0	= G0_charset;
	saved_G1	= G1_charset;
}

static void restore_cur(int currcons)
{
	gotoxy(currcons,saved_x,saved_y);
	intensity	= s_intensity;
	underline	= s_underline;
	blink		= s_blink;
	reverse		= s_reverse;
	charset		= s_charset;
	color		= s_color;
	G0_charset	= saved_G0;
	G1_charset	= saved_G1;
	translate	= charset ? G1_charset : G0_charset;
	update_attr(currcons);
	need_wrap = 0;
}

enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey, 
	EShash, ESsetG0, ESsetG1, ESignore };

static void reset_terminal(int currcons, int do_clear)
{
	top		= 0;
	bottom		= video_num_lines;
	state		= ESnormal;
	ques		= 0;
	translate	= NORM_TRANS;
	G0_charset	= NORM_TRANS;
	G1_charset	= GRAF_TRANS;
	charset		= 0;
	need_wrap	= 0;

	decscnm		= 0;
	decom		= 0;
	decawm		= 1;
	deccm		= 1;
	decim		= 0;

#if 0
/* 
 * Arno:
 * Here's the kbd stuff again.. What part is PC specific?
 */
	set_kbd(decarm);
	clr_kbd(decckm);
	clr_kbd(kbdapplic);
	clr_kbd(lnm);
	kbd_table[currcons].flags =
		(kbd_table[currcons].flags & ~LED_MASK) |
		(kbd_table[currcons].default_flags & LED_MASK);
	kbdmode		= 0;
	set_leds();
#endif

	default_attr(currcons);
	update_attr(currcons);

	tab_stop[0]	= 0x01010100;
	tab_stop[1]	=
	tab_stop[2]	=
	tab_stop[3]	=
	tab_stop[4]	= 0x01010101;

	if (do_clear) {
		gotoxy(currcons,0,0);
		csi_J(currcons,2);
		save_cur(currcons);
	}
}

void con_write(struct tty_struct * tty)
{
	int c = 0;
	ushort buf_flag = 0;    /* DPC: Needed by putcs() */
	unsigned int currcons;

	wake_up_interruptible(&tty->write_q.proc_list);
	currcons = tty->line - 1;
	if (currcons >= NR_CONSOLES) {
		printk("con_write: illegal tty (%d)\n", currcons);
		return;
	}
#ifdef CONFIG_SELECTION
	/* clear the selection as soon as any characters are to be written
	   out on the console holding the selection. */
	if (!EMPTY(&tty->write_q) && currcons == sel_cons)
		clear_selection();
#endif /* CONFIG_SELECTION */
	/* Arno:
	 * huh? What's this?
	 * disable_bh(KEYBOARD_BH);
	 */

	while (!tty->stopped &&
               (c = (buf_flag) ? c : get_tty_queue(&tty->write_q)) >= 0) {
                static char buf[256]; /* DPC: XXX Limits line LENGTH XXX */
                buf_flag = 0;

		if (state == ESnormal && translate[c]) {
			if (need_wrap) {
				cr(currcons);
				lf(currcons);
			}

                        /* DPC: 1994-04-12
                         *   Speed up overstrike mode, using new putcs.
                         *
                         * P.S. I hate 8 spaces per tab! Use Emacs!
                        */

                        if (!decim) { 
                            char   *p     = buf;
                            ushort count  = 0;
                            ushort nextx  = x;
                            ushort cols   = video_num_columns;
                            
                            *p++ = c;

                            if (nextx == cols) {
                                sw->con_putc(&vc_cons[currcons],
                                             *buf, y, x, DM_COPY);
                                need_wrap = decawm;
                                continue;
                            }

                            /* TAB TAB TAB - Arghh!!!! */

                            while ((c=get_tty_queue(&tty->write_q)) >= 0 &&
                                   translate[c] && ++nextx < cols)
                                *p++ = c;

                            count = p-buf;
                            sw->con_putcs(&vc_cons[currcons],
                                          buf, count, y, x, DM_COPY);
                            if (nextx == cols) {
                                x         = cols-1;
                                need_wrap = decawm;
                            } else
                                x += count;
                            buf_flag = 1;
                            continue;
                        }

                        /* DPC: End of putcs support */

			if (decim)
				insert_char(currcons);
			/* Arno:
			 * Translation table not really correct
			 * for 680x0 charset. No translation yet.
			 * TODO: build VT100 charset and enable translation.
			 */

			/*c = translate[c];*/

			/* Arno:
			 * 680x0: ignore attributes for now, just 
			 * print character.
			 */
			sw->con_putc(&vc_cons[currcons],c,y,x,DM_COPY);
			if (x == video_num_columns - 1)
				need_wrap = decawm;
			else {
				x++;
			}
			continue;
		}

		/*
		 *  Control characters can be used in the _middle_
		 *  of an escape sequence.
		 */
		switch (c) {
			case 7:
				kd_mksound(0x637, HZ/8);
				continue;
			case 8:
				bs(currcons);
				continue;
			case 9:
				while (x < video_num_columns - 1) {
					x++;
					if (tab_stop[x >> 5] & (1 << (x & 31)))
						break;
				}
				continue;
			case 10: case 11: case 12:
				lf(currcons);
				/* KBD stuff...
				if (!is_kbd(lnm))
					continue;
				*/
			case 13:
				cr(currcons);
				continue;
			case 14:
				charset = 1;
				translate = G1_charset;
				continue;
			case 15:
				charset = 0;
				translate = G0_charset;
				continue;
			case 24: case 26:
				state = ESnormal;
				continue;
			case 27:
				state = ESesc;
				continue;
			case 127:
				del(currcons);
				continue;
			case 128+27:
				state = ESsquare;
				continue;
		}
		switch(state) {
			case ESesc:
				state = ESnormal;
				switch (c) {
				  case '[':
					state = ESsquare;
					continue;
				  case 'E':
					cr(currcons);
					lf(currcons);
					continue;
				  case 'M':
					ri(currcons);
					continue;
				  case 'D':
					lf(currcons);
					continue;
				  case 'H':
					tab_stop[x >> 5] |= (1 << (x & 31));
					continue;
				  case 'Z':
					respond_ID(currcons,tty);
					continue;
				  case '7':
					save_cur(currcons);
					continue;
				  case '8':
					restore_cur(currcons);
					continue;
				  case '(':
					state = ESsetG0;
					continue;
				  case ')':
					state = ESsetG1;
					continue;
				  case '#':
					state = EShash;
					continue;
				  case 'c':
					reset_terminal(currcons,1);
					continue;
				  case '>':  /* Numeric keypad */
					/* ignore kbd stuff
					clr_kbd(kbdapplic);
					*/
					continue;
				  case '=':  /* Appl. keypad */
					/* ignore kbd stuff
					set_kbd(kbdapplic);
					*/
				 	continue;
				}	
				continue;
			case ESsquare:
				for(npar = 0 ; npar < NPAR ; npar++)
					par[npar] = 0;
				npar = 0;
				state = ESgetpars;
				if (c == '[') { /* Function key */
					state=ESfunckey;
					continue;
				}
				ques = (c=='?');
				if (ques)
					continue;
			case ESgetpars:
				if (c==';' && npar<NPAR-1) {
					npar++;
					continue;
				} else if (c>='0' && c<='9') {
					par[npar] *= 10;
					par[npar] += c-'0';
					continue;
				} else state=ESgotpars;
			case ESgotpars:
				state = ESnormal;
				switch(c) {
					case 'h':
						set_mode(currcons,1);
						continue;
					case 'l':
						set_mode(currcons,0);
						continue;
					case 'n':
						if (!ques)
							if (par[0] == 5)
								status_report(currcons,tty);
							else if (par[0] == 6)
								cursor_report(currcons,tty);
						continue;
				}
				if (ques) {
					ques = 0;
					continue;
				}
				switch(c) {
					case 'G': case '`':
						if (par[0]) par[0]--;
						gotoxy(currcons,par[0],y);
						continue;
					case 'A':
						if (!par[0]) par[0]++;
						gotoxy(currcons,x,y-par[0]);
						continue;
					case 'B': case 'e':
						if (!par[0]) par[0]++;
						gotoxy(currcons,x,y+par[0]);
						continue;
					case 'C': case 'a':
						if (!par[0]) par[0]++;
						gotoxy(currcons,x+par[0],y);
						continue;
					case 'D':
						if (!par[0]) par[0]++;
						gotoxy(currcons,x-par[0],y);
						continue;
					case 'E':
						if (!par[0]) par[0]++;
						gotoxy(currcons,0,y+par[0]);
						continue;
					case 'F':
						if (!par[0]) par[0]++;
						gotoxy(currcons,0,y-par[0]);
						continue;
					case 'd':
						if (par[0]) par[0]--;
						gotoxy(currcons,x,par[0]);
						continue;
					case 'H': case 'f':
						if (par[0]) par[0]--;
						if (par[1]) par[1]--;
						gotoxy(currcons,par[1],par[0]);
						continue;
					case 'J':
						csi_J(currcons,par[0]);
						continue;
					case 'K':
						csi_K(currcons,par[0]);
						continue;
					case 'L':
						csi_L(currcons,par[0]);
						continue;
					case 'M':
						csi_M(currcons,par[0]);
						continue;
					case 'P':
						csi_P(currcons,par[0]);
						continue;
					case 'c':
						if (!par[0])
							respond_ID(currcons,tty);
						continue;
					case 'g':
						if (!par[0])
							tab_stop[x >> 5] &= ~(1 << (x & 31));
						else if (par[0] == 3) {
							tab_stop[0] =
							tab_stop[1] =
							tab_stop[2] =
							tab_stop[3] =
							tab_stop[4] = 0;
						}
						continue;
					case 'm':
						csi_m(currcons);
						continue;
					case 'r':
						if (!par[0])
							par[0]++;
						if (!par[1])
							par[1] = video_num_lines;
						/* Minimum allowed region is 2 lines */
						if (par[0] < par[1] &&
						    par[1] <= video_num_lines) {
							top=par[0]-1;
							bottom=par[1];
							gotoxy(currcons,0,0);
						}
						continue;
					case 's':
						save_cur(currcons);
						continue;
					case 'u':
						restore_cur(currcons);
						continue;
					case '@':
						csi_at(currcons,par[0]);
						continue;
					case ']': /* setterm functions */
						setterm_command(currcons);
						continue;
				}
				continue;
			case ESfunckey:
				state = ESnormal;
				continue;
			case EShash:
				state = ESnormal;
				if (c == '8') {
					/* DEC screen alignment test. kludge :-) */
					video_erase_char =
						(video_erase_char & 0x00ff) | 'E';
					/* Arno:
					 * Doesn't work, because csi_J(c,2)
					 * calls con_clear and doesn't print
					 * the erase char..
					 */
					csi_J(currcons, 2);
					video_erase_char =
						(video_erase_char & 0x00ff) | ' ';
				}
				continue;
			case ESsetG0:
				if (c == '0')
					G0_charset = GRAF_TRANS;
				else if (c == 'B')
					G0_charset = NORM_TRANS;
				else if (c == 'U')
					G0_charset = NULL_TRANS;
				if (charset == 0)
					translate = G0_charset;
				state = ESnormal;
				continue;
			case ESsetG1:
				if (c == '0')
					G1_charset = GRAF_TRANS;
				else if (c == 'B')
					G1_charset = NORM_TRANS;
				else if (c == 'U')
					G1_charset = NULL_TRANS;
				if (charset == 1)
					translate = G1_charset;
				state = ESnormal;
				continue;
			default:
				state = ESnormal;
		}
	}
	if (vcmode != KD_GRAPHICS)
		set_cursor(currcons);
	/*
	enable_bh(KEYBOARD_BH);
	*/
}

void do_keyboard_interrupt(void)
{
	TTY_READ_FLUSH(TTY_TABLE(0));
#if 0
/* 
 * Arno:
 * No blanking stuff yet. 
 * Haven't looked at it..
 */
	timer_active &= ~(1<<BLANK_TIMER);
	if (vt_cons[fg_console].vc_mode == KD_GRAPHICS)
		return;
	if (console_blanked) {
		timer_table[BLANK_TIMER].expires = 0;
		timer_active |= 1<<BLANK_TIMER;
	} else if (blankinterval) {
		timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
		timer_active |= 1<<BLANK_TIMER;
	}
#endif
}

#if 0
/* DPC: New version of console_print using putcs */

void console_print(const char * b)
{
   int currcons = fg_console;
   unsigned char c;
   const char *start = b;
   ushort count      = 0;
   ushort myx        = x;
   ushort cols       = video_num_columns;

   if (!printable || currcons<0 || currcons>=NR_CONSOLES)
      return;

   /* Contrived structure to try and emulate original need_wrap behaviour
    * Problems caused when we have need_wrap set on '\n' character */
   
   while ((c = *(b++)) != 0) {
       if (c == 10 || c == 13 || need_wrap) {
           if ((count = b - start - 1) > 0)
               sw->con_putcs(&vc_cons[currcons], start, count ,
                             y, x, DM_COPY);
           if (c != 13)
               lf(currcons);
           cr(currcons);

           if (c == 10 || c == 13) {
               myx = x ; start = b;    /* New line, starting afresh */
           } else {
               myx = x ; start = b-1;  /* Other need_wrap => got one */
           }
       } else if (myx++ >= cols)
           need_wrap = 1;
   }

   if ((count = b - start -1) > 0) {
       sw->con_putcs(&vc_cons[currcons], start, count ,
                     y, x, DM_COPY);
       x += count;
       if (x >= cols) {
           need_wrap = 1; x = cols;
       }
   }

   set_cursor(currcons);
}
#endif

#if 1   /* Arno's old putc based version */
void console_print(const char * b)
{
	int currcons = fg_console;
	unsigned char c;

	if (!printable || currcons<0 || currcons>=NR_CONSOLES)
		return;
	while ((c = *(b++)) != 0) {
		if (c == 10 || c == 13 || need_wrap) {
			if (c != 13)
				lf(currcons);
			cr(currcons);
			if (c == 10 || c == 13)
				continue;
		}
		sw->con_putc(&vc_cons[currcons],c,y,x,DM_COPY);
		if (x == video_num_columns - 1) {
			need_wrap = 1;
			continue;
		}
		x++;
	}
	set_cursor(currcons);
	return;
	if (vt_cons[fg_console].vc_mode == KD_GRAPHICS)
		return;
#if 0
	timer_active &= ~(1<<BLANK_TIMER);
	if (console_blanked) {
		timer_table[BLANK_TIMER].expires = 0;
		timer_active |= 1<<BLANK_TIMER;
	} else if (blankinterval) {
		timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
		timer_active |= 1<<BLANK_TIMER;
	}
#endif
}
#endif


/*
 *  long con_init(long);
 *
 * This routine initalizes console interrupts, and does nothing
 * else. If you want the screen to clear, call tty_write with
 * the appropriate escape-sequece.
 *
 * Reads the information preserved by setup.s to determine the current display
 * type and sets everything accordingly.
 */
long con_init(long kmem_start)
{
	char *display_desc = "????";
	unsigned int currcons = 0;

/* 
 * Arno:
 * We really should move this to sw->con_init or some other platform
 * specific init code instead of hard-coding this...
 */
	display_desc="ECS";
	can_do_color=1;

	sw=conswitchp;

#if 0
/* 
 * Arno:
 * first working INIT code... 'Borrowed' from original 680x0 console
 * code.. (hi Hamish!)
 */
	kmem_start = sw->con_init(&vc_cons[currcons],kmem_start);
	sw->con_cursor(&vc_cons[currcons], CM_DRAW);
	reset_terminal(currcons, currcons);
	register_console(console_print);
#endif

#if 0
	timer_table[BLANK_TIMER].fn = blank_screen;
	timer_table[BLANK_TIMER].expires = 0;
	if (blankinterval) {
		timer_table[BLANK_TIMER].expires = jiffies+blankinterval;
		timer_active |= 1<<BLANK_TIMER;
	}
#endif
	kmem_start = sw->con_init(&vc_cons[currcons],kmem_start);

#if 0
/* 
 * Arno:
 * Strange things happen when more than 1 console is inited... 
 * 
 * TODO: extend low-level code for multi-console support.
 * Question: how are we going to do VC's? Save/restore bitplanes/copperlists
 * etc.? Can get memory expensive with 4 bitplane screens (in the future..)
 */
	for (currcons = 0; currcons<NR_CONSOLES; currcons++) {
		vcmode		= KD_TEXT;
		vtmode.mode	= VT_AUTO;
		vtmode.waitv	= 0;
		vtmode.relsig	= 0;
		vtmode.acqsig	= 0;
		vtmode.frsig	= 0;
		vtpid		= -1;
		vtnewvt		= -1;
/*
		clr_kbd(kbdraw);
*/
		def_color	= 0x07;   /* white */
		ulcolor		= 0x0f;   /* bold white */
		halfcolor	= 0x08;   /* grey */
		reset_terminal(currcons, currcons);


	}
#endif


	currcons = fg_console = 0;

	def_color	= 0x07;   /* white */
	ulcolor		= 0x0f;   /* bold white */
	halfcolor	= 0x08;   /* grey */
	reset_terminal(currcons, currcons);

	gotoxy(currcons,0,0);
	save_cur(currcons);
	gotoxy(currcons,0,0);
/*
	gotoxy(currcons,orig_x,orig_y);
*/
	update_screen(fg_console);
	sw->con_cursor(&vc_cons[currcons], CM_DRAW);
	printable = 1;
	register_console(console_print);

	printk("Console: %s %s %ldx%ld, %d virtual consoles\n",
		can_do_color?"colour":"mono",
		display_desc,
		video_num_columns,video_num_lines,
		NR_CONSOLES);

	return kmem_start;
}

/*
 * kbdsave doesn't need to do anything: it's all handled automatically
 * with the new data structures..
 */
void kbdsave(int new_console)
{
}

void blank_screen(void)
{
	if (console_blanked)
		return;
	return;
#if 0
	timer_table[BLANK_TIMER].fn = unblank_screen;
	get_scrmem(fg_console);
	hide_cursor(fg_console);
	console_blanked = 1;
	memsetw((void *)video_mem_base, 0x0020, video_mem_term-video_mem_base );
#endif
}

void unblank_screen(void)
{
	if (!console_blanked)
		return;
	return;
#if 0
	timer_table[BLANK_TIMER].fn = blank_screen;
	if (blankinterval) {
		timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
		timer_active |= 1<<BLANK_TIMER;
	}
	console_blanked = 0;
	set_scrmem(fg_console);
	set_origin(fg_console);
	set_cursor(fg_console);
#endif
}

void update_screen(int new_console)
{
	static int lock = 0;

	if (new_console == fg_console || lock)
		return;
	return;
	lock = 1;
	fg_console = new_console;
	lock = 0;
#if 0
	kbdsave(new_console);
	get_scrmem(fg_console); 
	set_scrmem(fg_console); 
	set_origin(fg_console);
	set_cursor(new_console);
	set_leds();
	compute_shiftstate();
#endif
}

#if 0
int do_screendump(int arg)
{
	char *sptr, *buf = (char *)arg;
	int currcons, l;

	if (!suser())
		return -EPERM;
	l = verify_area(VERIFY_WRITE, buf,2+video_num_columns*video_num_lines);
	if (l)
		return l;
	currcons = get_fs_byte(buf+1);
	if ((currcons<0) || (currcons>NR_CONSOLES))
		return -EIO;
	put_fs_byte((char)(video_num_lines),buf++);	
	put_fs_byte((char)(video_num_columns),buf++);
	currcons = (currcons ? currcons-1 : fg_console);
	sptr = (char *) origin;
	for (l=video_num_lines*video_num_columns; l>0 ; l--, sptr++)
		put_fs_byte(*sptr++,buf++);	
	return(0);
}
#endif

/*
 * All we do is set the write and ioctl subroutines; later on maybe we'll
 * dynamically allocate the console screen memory.
 */
int con_open(struct tty_struct *tty, struct file * filp)
{
	tty->write = con_write;
	tty->ioctl = vt_ioctl;
	if (tty->line > NR_CONSOLES)
		return -ENODEV;

	tty->winsize.ws_row = vc_cons[tty->line-1].vc_rows;
	tty->winsize.ws_col = vc_cons[tty->line-1].vc_cols;

	return 0;
}

#ifdef CONFIG_SELECTION
/* correction factor for when screen is hardware-scrolled */
#define	hwscroll_offset (currcons == fg_console ? ((__real_origin - __origin) << 1) : 0)

/* set reverse video on characters s-e of console with selection. */
static void highlight(const int currcons, const int s, const int e)
{
	unsigned char *p, *p1, *p2;

	p1 = (unsigned char *)origin - hwscroll_offset + s + 1;
	p2 = (unsigned char *)origin - hwscroll_offset + e + 1;
	if (p1 > p2)
	{
		p = p1;
		p1 = p2;
		p2 = p;
	}
	for (p = p1; p <= p2; p += 2)
		*p = (*p & 0x88) | ((*p << 4) & 0x70) | ((*p >> 4) & 0x07);
}

/* is c in range [a-zA-Z0-9_]? */
static inline int inword(const char c) { return (isalnum(c) || c == '_'); }

/* does screen address p correspond to character at LH/RH edge of screen? */
static inline int atedge(const int p)
{
	return (!(p % video_size_row) || !((p + 2) % video_size_row));
}

/* constrain v such that l <= v <= u */
static inline short limit(const int v, const int l, const int u)
{
	return (v < l) ? l : ((v > u) ? u : v);
}

/* set the current selection. Invoked by ioctl(). */
int set_selection(const int arg)
{
	unsigned short *args, xs, ys, xe, ye;
	int currcons = fg_console;
	int sel_mode, new_sel_start, new_sel_end, spc;
	char *bp, *obp, *spos;
	int i, ps, pe;
	char *off = (char *)origin - hwscroll_offset;

	unblank_screen();
	args = (unsigned short *)(arg + 1);
	xs = get_fs_word(args++) - 1;
	ys = get_fs_word(args++) - 1;
	xe = get_fs_word(args++) - 1;
	ye = get_fs_word(args++) - 1;
	sel_mode = get_fs_word(args);

	xs = limit(xs, 0, video_num_columns - 1);
	ys = limit(ys, 0, video_num_lines - 1);
	xe = limit(xe, 0, video_num_columns - 1);
	ye = limit(ye, 0, video_num_lines - 1);
	ps = ys * video_size_row + (xs << 1);
	pe = ye * video_size_row + (xe << 1);

	if (ps > pe)	/* make sel_start <= sel_end */
	{
		int tmp = ps;
		ps = pe;
		pe = tmp;
	}

	switch (sel_mode)
	{
		case 0:	/* character-by-character selection */
		default:
			new_sel_start = ps;
			new_sel_end = pe;
			break;
		case 1:	/* word-by-word selection */
			spc = isspace(*(off + ps));
			for (new_sel_start = ps; ; ps -= 2)
			{
				if ((spc && !isspace(*(off + ps))) ||
				    (!spc && !inword(*(off + ps))))
					break;
				new_sel_start = ps;
				if (!(ps % video_size_row))
					break;
			}
			spc = isspace(*(off + pe));
			for (new_sel_end = pe; ; pe += 2)
			{
				if ((spc && !isspace(*(off + pe))) ||
				    (!spc && !inword(*(off + pe))))
					break;
				new_sel_end = pe;
				if (!((pe + 2) % video_size_row))
					break;
			}
			break;
		case 2:	/* line-by-line selection */
			new_sel_start = ps - ps % video_size_row;
			new_sel_end = pe + video_size_row
				    - pe % video_size_row - 2;
			break;
	}
	/* select to end of line if on trailing space */
	if (new_sel_end > new_sel_start &&
		!atedge(new_sel_end) && isspace(*(off + new_sel_end)))
	{
		for (pe = new_sel_end + 2; ; pe += 2)
		{
			if (!isspace(*(off + pe)) || atedge(pe))
				break;
		}
		if (isspace(*(off + pe)))
			new_sel_end = pe;
	}
	if (sel_cons != currcons)
	{
		clear_selection();
		sel_cons = currcons;
	}
	if (sel_start == -1)	/* no current selection */
		highlight(sel_cons, new_sel_start, new_sel_end);
	else if (new_sel_start == sel_start)
	{
		if (new_sel_end == sel_end)	/* no action required */
			return 0;
		else if (new_sel_end > sel_end)	/* extend to right */
			highlight(sel_cons, sel_end + 2, new_sel_end);
		else				/* contract from right */
			highlight(sel_cons, new_sel_end + 2, sel_end);
	}
	else if (new_sel_end == sel_end)
	{
		if (new_sel_start < sel_start)	/* extend to left */
			highlight(sel_cons, new_sel_start, sel_start - 2);
		else				/* contract from left */
			highlight(sel_cons, sel_start, new_sel_start - 2);
	}
	else	/* some other case; start selection from scratch */
	{
		clear_selection();
		highlight(sel_cons, new_sel_start, new_sel_end);
	}
	sel_start = new_sel_start;
	sel_end = new_sel_end;
	obp = bp = sel_buffer;
	for (i = sel_start; i <= sel_end; i += 2)
	{
		spos = (char *)off + i;
		*bp++ = *spos;
		if (!isspace(*spos))
			obp = bp;
		if (! ((i + 2) % video_size_row))
		{
			/* strip trailing blanks from line and add newline,
			   unless non-space at end of line. */
			if (obp != bp)
			{
				bp = obp;
				*bp++ = '\r';
			}
			obp = bp;
		}
		/* check for space, leaving room for next character, possible
		   newline, and null at end. */
		if (bp - sel_buffer > SEL_BUFFER_SIZE - 3)
			break;
	}
	*bp = '\0';
	return 0;
}

/* insert the contents of the selection buffer into the queue of the
   tty associated with the current console. Invoked by ioctl(). */
int paste_selection(struct tty_struct *tty)
{
	char *bp = sel_buffer;

	if (! *bp)
		return 0;
	unblank_screen();
	while (*bp)
	{
		put_tty_queue(*bp, &tty->read_q);
		bp++;
	}
	TTY_READ_FLUSH(tty);
	return 0;
}

/* remove the current selection highlight, if any, from the console holding
   the selection. */
static void clear_selection()
{
	if (sel_start != -1)
	{
		highlight(sel_cons, sel_start, sel_end);
		sel_start = -1;
	}
}
#endif /* CONFIG_SELECTION */
