/*
 * console.c
 *
 * Copyright 2002, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 */
/*
 * ToDo: ディスプレイ出力でエスケープシーケンスに対応させる。
 */


#include<types.h>
#include<sys/param.h>
#include<lib.h>
#include<lock.h>
#include<device.h>
#include<wait.h>
#include<interrupt.h>
#include<signal.h>
#include<keymap.h>
#include<term.h>
#include<errno.h>
#include<mm.h>
#include<debug.h>
#include<console.h>


//#define DEBUG_CONSOLE 1

#ifdef DEBUG_CONSOLE
	#define STATIC
	#define INLINE
#else
	#define STATIC static
	#define INLINE	inline
#endif


/***************************************************************************************
 *
 * 端末機能
 *
 ***************************************************************************************/


//================================== パブリック ========================================


static struct termios termio={
	ICRNL | IXOFF | IXON,	/* c_iflag. */
	OPOST,					/* c_oflag. */
	CS8 | CREAD | B9600,	/* c_cflag. */
	ISIG | ICANON | ECHO,	/* c_lflag. */
};

static LINE_BUF lbuf;


/*
 * ターミナルの初期化
 */
STATIC void initTerm()
{
	/* termiosの設定。 */
	termio.c_cc[VINTR]=	CINTR;
	termio.c_cc[VQUIT]=	CQUIT;
	termio.c_cc[VSTART]=CSTART;
	termio.c_cc[VSTOP]=	CSTOP;
	termio.c_cc[VSUSP]=	CDSUSP;
	termio.c_cc[VEOF]=	CEOF;
	termio.c_cc[VEOL]=	CEOL;
	termio.c_cc[VERASE]=CERASE;
	termio.c_cc[VKILL]=	CKILL;
}


/***************************************************************************************
 *
 * ディスプレイ
 *
 ***************************************************************************************/


enum{
	/* VGA Video ram. */
	VRAM_BASE_ADDR=0xb8000,	/* Start address of video memory */
	VRAM_SIZE=0x8000,		/* Size of video memory */

	/* CRT IO registers. */
	CTL_ADDR=0x3d4,			/* Addres register */
	CTL_DATA=0x3d5,			/* Data IO register */
	START_ADDR_HIGH=0xc,	/* High byte of print start address */
	START_ADDR_LOW=0xd,		/* Low  byte of print start address  */
	CURSOR_ADDR_HIGH=0xe,	/* High byte of cursor address */
	CURSOR_ADDR_LOW=0xf,	/* Low  byte of cursor address */

	SCR_LINES=25,			/* Number of lines. */
	SCR_COLUMNS=80,			/* Number of columns. */
	SCR_LAST=				/* Video ram last address. */
		VRAM_SIZE / 2 - SCR_LINES * SCR_COLUMNS - SCR_COLUMNS * 3,
	TABSIZE = 8,
	ESC = 0x1b,				// エスケープコード
};


typedef struct{
	int viewTop;	/* ビデオメモリー上の画面の開始位置（相対値）。 */
	int start;		/* 印字開始位置（相対値）。 */
}SCREEN;

typedef struct{
	uchar ch;		/* 文字 */
	uchar attr;		/* 属性 */
}VRAM;


//================================== プライベート ========================================


static VRAM *vram = (VRAM*)VRAM_BASE_ADDR;
static SCREEN screen;


/*****************************************************
static void testPrint(int pos,int uvalue)
{
	char tmp_buf[12];
	int i;


	for(i=0;;++i)
	{
		tmp_buf[i]=uvalue%10+'0';
		if(!(uvalue/=10))break;
	}
	for(;i>=0;--i,++pos)vram[pos*2]=tmp_buf[i];
}
*******************************************************/


/*
 * Update cursor position.
 */
STATIC INLINE void updateCursor()
{
	outb(CTL_ADDR,CURSOR_ADDR_HIGH);
	outb(CTL_DATA,screen.start>>8);
	outb(CTL_ADDR,CURSOR_ADDR_LOW);
	outb(CTL_DATA,screen.start);
}


/*
 * Hard scroll up.
 */
STATIC void scrollup()
{
	VRAM *p;
	int i,last;


	screen.viewTop = ROUNDUP(screen.start-SCR_COLUMNS*SCR_LINES+1,SCR_COLUMNS);

	if (screen.viewTop >= SCR_LAST)
	{
		/* Video ramの先頭に移動 */
		p = &vram[screen.viewTop];
		last = screen.start - screen.viewTop;
		for (i = 0; i < last; ++i )
			vram[i] = p[i];

		/* Video ramを0クリアー */
		for (; i < SCR_LAST + SCR_COLUMNS * SCR_LINES; ++i)
		{
			vram[i].ch = 0;
			vram[i].attr = 0x07;
		}

		screen.start -= screen.viewTop;
		screen.viewTop = 0;
	}

	/* Hard scroll up */
	outb(CTL_ADDR,START_ADDR_HIGH);
	outb(CTL_DATA,screen.viewTop>>8);
	outb(CTL_ADDR,START_ADDR_LOW);
	outb(CTL_DATA,screen.viewTop);
}


/*
 * 複数文字削除
 * parameters : start,size
 */
STATIC INLINE void deleteScreen(int start,size_t size)
{
	VRAM *p = &vram[start];
	int i; 
	
	
	/* Video ramを0クリアー */
	for(i = 0; i < size; ++i)
	{
		p[i].ch = 0;
		p[i].attr = 0x7;
	}
}

/*
 * 1文字印字。
 * parameters : buffer,size
 */
STATIC INLINE void putIntoScreen(char c)
{
	vram[screen.start].ch = c;
	++screen.start;
	updateCursor();
}


/*
 * 複数文字印字。
 * parameters : buffer,size
 */
STATIC INLINE void putNumIntoScreen(char *buf,int size)
{
	int i,last;


	for (i = screen.start,last = i + size; i < last; ++i)
		vram[i].ch = *buf++;
}

STATIC INLINE void leftCursor(int n)
{
	screen.start -= n;
	updateCursor();
}

STATIC INLINE void rightCursor(int n)
{
	screen.start += n;
	if(screen.start - screen.viewTop >= SCR_COLUMNS * SCR_LINES)
		scrollup();
	updateCursor();
}

STATIC INLINE void newLine()
{
	screen.start = ROUNDUP(screen.start + 1,SCR_COLUMNS);
	if (screen.start - screen.viewTop >= SCR_COLUMNS * SCR_LINES)
		scrollup();
	updateCursor();
}

STATIC INLINE void carriageRetun()
{
	screen.start = ROUNDDOWN(screen.start,SCR_COLUMNS);
	updateCursor();
}

STATIC INLINE void tab()
{
	screen.start = ROUNDUP(screen.start + 1, TABSIZE);
	if (screen.start - screen.viewTop >= SCR_COLUMNS * SCR_LINES)
		scrollup();
	updateCursor();
}


/************* 端末機能用表示関数 ************************/


STATIC void leftCursorTerm(int n)
{
	if (lbuf.crt < lbuf.pastCrt)
		leftCursor(n);
}

STATIC void rightCursorTerm(int n)
{
	if (lbuf.pastCrt < lbuf.crt)
		rightCursor(n);
}

STATIC void inputStrTerm()
{
	putNumIntoScreen(lbuf.buf + lbuf.pastCrt,lbuf.last - lbuf.pastCrt);
	rightCursor(lbuf.crt - lbuf.pastCrt);
}

STATIC void newLineTerm()
{
	newLine();
}

STATIC void carriageRetunTerm()
{
	carriageRetun();
}

STATIC void eraseLineTerm()
{
	leftCursor(lbuf.pastCrt - lbuf.lineTop);
	putNumIntoScreen(lbuf.buf + lbuf.lineTop,lbuf.pastLast - lbuf.lineTop);
}

STATIC void deleteTerm()
{
	putNumIntoScreen(lbuf.buf + lbuf.crt,lbuf.pastLast - lbuf.crt);
}

STATIC void backSpaceTerm()
{
	leftCursor(lbuf.pastCrt - lbuf.crt);
	putNumIntoScreen(lbuf.buf + lbuf.crt,lbuf.pastLast - lbuf.crt);
}

STATIC void tabTerm()
{
	tab();
}


/************* エスケープシーケンス ************************/


STATIC void setCursolInside(int x,int y)
{
	screen.start = screen.viewTop + (y - 1) * SCR_COLUMNS + (x - 1);
	if (screen.viewTop + SCR_LINES * SCR_COLUMNS < screen.start)
		screen.start = screen.viewTop + SCR_LINES * SCR_COLUMNS;
	updateCursor();
}


STATIC void leftCursolInside(int x)
{
	if (screen.start - x < screen.viewTop)
		screen.start = screen.viewTop;
	else
		screen.start -= x;
	updateCursor();
}


STATIC void rightCursolInside(int x)
{
	if (screen.viewTop + SCR_LINES * SCR_COLUMNS < screen.start + x)
		screen.start = screen.viewTop + SCR_LINES * SCR_COLUMNS;
	else
		screen.start += x;
	updateCursor();
}


STATIC void leftCursolLine(int x)
{
	if (screen.start - x < ROUNDDOWN(screen.start,SCR_COLUMNS))
		screen.start = ROUNDDOWN(screen.start,SCR_COLUMNS);
	else
		screen.start -= x;
	updateCursor();
}


STATIC void rightCursolLine(int x)
{
	if (ROUNDUP(screen.start,SCR_COLUMNS) < screen.start + x)
		screen.start = ROUNDUP(screen.start,SCR_COLUMNS);
	else
		screen.start += x;
	updateCursor();
}


STATIC void upLine(int y)
{
	int src = ROUNDUP(screen.start,SCR_COLUMNS);
	int dst = src - y * SCR_COLUMNS;
	
	
	if (dst  < screen.viewTop)
		dst = screen.viewTop;
	memmove(&vram[dst],&vram[src],(SCR_LINES * SCR_COLUMNS - src) * sizeof(VRAM));
}


STATIC void downLine(int y)
{
	int size = ROUNDDOWN(screen.start,SCR_COLUMNS) - screen.viewTop;
	int start_size = y * SCR_COLUMNS;
	
	
	if (SCR_LINES * SCR_COLUMNS - size < start_size)
		start_size = SCR_LINES * SCR_COLUMNS - size;
	memmove(&vram[screen.viewTop + start_size],&vram[screen.viewTop],size * sizeof(VRAM));
}


STATIC void deleteAll()
{
	deleteScreen(screen.viewTop,SCR_COLUMNS * SCR_LINES);
	screen.start = screen.viewTop;
	updateCursor();
}


// エスケープシーケンス変換テーブル	
static const uchar sequence[]={
//	 0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,	// 0
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,	// 1
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0, '*', 0,  0,  0,  0,  0,	// 2
	'n','n','n','n','n','n','n','n','n','n', 0, ';', 0,  0,  0,  0,	// 3
	 0, 'A','B','C','D','E', 0,  0, 'H', 0, 'J','K','L','M', 0,  0,	// 4
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,	// 5
	 0,  0,  0,  0,  0,  0, 'H', 0,  0,  0,  0,  0,  0, 'm', 0,  0,	// 6
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,	// 7
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,	// 8
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,	// 9
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,	// a
	 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,  0,  0,  0,  0,  0,	// c
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,	// d
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,	// e
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,	// f
};

// escape_sequence()終了コード
enum{
	ESC_DONE = 0,	// 変換完了
	ESC_ERR,		// 変換エラー
	ESC_NOTDONE,	// 変換途中
};

// エスケープシーケンス変換
STATIC int escapeSequence(const uchar *str, const int num)
{
	uint xy[2] = {0,0};
	int xy_idx = 0;
	int c_str;

	if (num == 0){
		return ESC_NOTDONE;
	}
	
	switch (*str){
		case '[':
			break;
		case 'c':
			deleteAll();
			return ESC_DONE;
		case 'D':	// カーソルラインダウン、スクロールあり
			rightCursor(SCR_COLUMNS);
			return ESC_DONE;
		case 'E':	// カーソルラインダウン、スクロールあり
			rightCursor(ROUNDUP(screen.start + 1,SCR_COLUMNS));
			return ESC_DONE;
		case 'M':	// カーソルラインアップ　＋　ラインダウン
			leftCursolInside(SCR_COLUMNS);
			downLine(1);
			return ESC_DONE;
		default:
			return ESC_ERR;
	}
	for (c_str = 1; c_str < num; ++c_str){
		switch (sequence[(int)str[c_str]]){
			case 'n':
				xy[xy_idx] *= 10;
				xy[xy_idx] += str[c_str] - '0';
				break;
			case ';':
				if (1 < ++xy_idx){
					return ESC_ERR;
				}
				break;
			case 'A':	// カーソルラインアップ
				if (xy_idx == 0){
					leftCursolInside(((xy[0] == 0)? 1 : xy[0]) * SCR_COLUMNS);
					return ESC_DONE;
				}
				return ESC_ERR;
			case 'B':	// カーソルラインダウン
				if (xy_idx == 0){
					rightCursolInside(((xy[0] == 0)? 1 : xy[0]) * SCR_COLUMNS);
					return ESC_DONE;
				}
				return ESC_ERR;
			case 'C':	// カーソルライト
				if (xy_idx == 0){
					rightCursolLine((xy[0] == 0)? 1 : xy[0]);
					return ESC_DONE;
				}
				return ESC_ERR;
			case 'D':	// カーソルレフト
				if (xy_idx == 0){
					leftCursolLine((xy[0] == 0)? 1 : xy[0]);
					return ESC_DONE;
				}
				return ESC_ERR;
			case 'L':	// ライン上移動
				if (xy_idx == 0){
					upLine((xy[0] == 0)? 1 : xy[0]);
					return ESC_DONE;
				}
				return ESC_ERR;
			case 'M':	// ライン下移動
				if (xy_idx == 0){
					downLine((xy[0] == 0)? 1 : xy[0]);
					return ESC_DONE;
				}
				return ESC_ERR;
			case 'J':
				if (xy_idx == 0){
					switch (xy[0]){
						case 0:	// カーソルから画面最後まで消去
							deleteScreen(screen.start,screen.viewTop + SCR_LINES * SCR_COLUMNS - screen.start);
							break;
						case 1:	// 画面最初からカーソルまで消去
							deleteScreen(screen.viewTop,screen.start - screen.viewTop);
							break;
						case 2:	// 画面全消去
							deleteAll();
							break;
					}
					return ESC_DONE;
				}
				return ESC_ERR;
			case 'K':
				if (xy_idx == 0){
					switch (xy[0]){
						case 0:	// カーソルから右端まで消去
							deleteScreen(screen.start,ROUNDUP(screen.start + 1,SCR_COLUMNS) - screen.start);
							break;
						case 1:	// 左端からカーソルまで消去
							deleteScreen(ROUNDDOWN(screen.start,SCR_COLUMNS),ROUNDDOWN(screen.start,SCR_COLUMNS) - screen.start);
							break;
						case 2:	// ライン消去
							deleteScreen(ROUNDDOWN(screen.start,SCR_COLUMNS),SCR_COLUMNS);
							break;
					}
					return ESC_DONE;
				}
				return ESC_ERR;
			case 'H':	// カーソルセット
				if (xy_idx == 1){
					setCursolInside((xy[1] == 0)? 1 : xy[1],(xy[0] == 0)? 1 : xy[0]);
					return ESC_DONE;
				}
				return ESC_ERR;
			case '*':	// 画面全消去
				if (xy[0] == 0 && xy_idx == 0){
					deleteAll();
					return ESC_DONE;
				}
				return ESC_ERR;
			case 'm':
				// 未定義
				// 下へ
			default:
				return ESC_ERR;
		}
	}

	return ESC_NOTDONE;
}


// キーコード変換画面表示
// return : FALSE or ESCコードあり = TRUE
STATIC INLINE int printScreen(const char c)
{
	switch(c){
		case '\b':
			leftCursor(1);
			break;
		case '\n':
	   		newLine();
			break;
		case '\r':
	   		carriageRetun();
			break;
		case '\t':
	   		tab();
			break;
		case '\a':
			// 未定義
			break;
		case ESC:
			return TRUE;
		default:
			putIntoScreen(c);
	}
	
	return FALSE;
}


//================================== パブリック ========================================


/*
 * 初期化。
 */
STATIC void initScreen()
{
	short *p;

	/* Start position 0 */
	outb(CTL_ADDR,START_ADDR_HIGH);
	outb(CTL_DATA,0);
	outb(CTL_ADDR,START_ADDR_LOW);
	outb(CTL_DATA,0);

	/* Clear screen */
	for (p = (short*)VRAM_BASE_ADDR; p < (short*)(VRAM_BASE_ADDR+VRAM_SIZE); ++p){
		*p=0x700;
	}
}


//================================== グローバル ========================================


/*
 * コンソールに書き込む
 * return : write size
 */
int writeConsole(void *buf, size_t size, size_t start)
{
	enum{ESC_BUF_SIZE = 10,};			// エスケープシーケンス用バッファサイズ
	static uchar escBuf[ESC_BUF_SIZE];	// エスケープシーケンス用バッファ
	static int escSw = FALSE;			// エスケープシーケンス書き出し中
	static int escNum = 0;				// エスケープシーケンス用バッファ現在保存文字数

	char *str = buf;
	int c_str;

	for (c_str = 0; c_str < size; ++c_str){
		if (escSw == TRUE){
			int i;

			if (escNum < ESC_BUF_SIZE){
				escBuf[escNum++] = str[c_str];		// エスケープシーケンスバッファに格納
				switch (escapeSequence(escBuf, escNum)){
					case ESC_DONE:
						escNum = 0;
						escSw = FALSE;
						break;
					case ESC_ERR:
						// バッファに蓄えた文字をコンソールに吐き出す
						for (i = 0; i < escNum; ++i){
							escSw = printScreen(escBuf[i]);
						}
						escNum = 0;
						escSw = FALSE;
						break;
					case ESC_NOTDONE:
						break;
					default:
						ASSERT(0);
				}
			}
			else{
				// バッファに蓄えた文字をコンソールに吐き出す
				for (i = 0; i < escNum; ++i){
					printScreen(escBuf[i]);
				}
				escNum = 0;
				escSw = FALSE;
				escSw = printScreen(str[c_str]);
			}
		}
		else{
			escSw = printScreen(str[c_str]);
		}
	}

	return c_str;
}


/***************************************************************************************
 *
 * キーボード
 *
 ***************************************************************************************/

enum{
	IRQ_NO = 1,		/* IRQ number */
};


//================================== プライベート ========================================


static int waitLock;		/* ウエイト操作用ロック。 */
static PROC *waitProc;		/* 入力待ちプロセス。 */

static TERM_CTL termCtl = {
	NULL,
	0,
	leftCursorTerm,
	rightCursorTerm,
	inputStrTerm,
	newLineTerm,
	carriageRetunTerm,
	eraseLineTerm,
	deleteTerm,
	backSpaceTerm,
	tabTerm
};


/*
 * Turn on/off LED.
 */
STATIC INLINE void switchLed(uchar value)
{
	outb(KEYBOARD_OUTPUT_REG,0xed);		/* Command turn on/of LED. */
	while(inb(KEYBOARD_STAT_REG)&2);
	outb(KEYBOARD_OUTPUT_REG,value);	/* LED on/off. */
}


/*
 * キーボードハンドラー
 * parameters : scan key coad
 */
STATIC void keyboardHnadler(uchar key_coad)
{
	enum{
		KEY_BREAK=0x80,	/* key break bit. */

		/* Add key bit. */
		SHT_BIT=1<<0,
		CTR_BIT=1<<1,

		/* LED bit. */
		LED_SCRL=1<<0,	/* ScrollLock LED. */
		LED_NMLC=1<<1,	/* NumLock LED. */
		LED_CPSL=1<<2,	/* CapsLock LED. */
	};

	static int add_key=0;				/* normal=0,add shift=1,add ctrl=2. */
	static int func_key=0;				/* 機能キーフラグ。(スキャンコード0xe0) */
	static int shift_key=0;				/* Shift key flag. */
	static int led=0;					/* LED flag, */
	static int ctr_key=0;				/* Ctrl key. */
	static uchar add_map[]={0,1,2,2};	/* 付加キー変換マップ。 */

	uchar map_coad;


	/* 機能キーの処理。 */
	if(func_key)
	{
		if ((key_coad >= VMAP_START_COAD) && (key_coad <= VMAP_END_COAD))
			key_coad += VMAP_ADD;
		func_key = 0;
	}
	else if (key_coad == KEY_FUNC)
	{
		func_key=1;
		return;
	}

	/* マップコード取得。 */
	map_coad = keymap[key_coad&~KEY_BREAK][add_key];
	if (map_coad == 0)
		return;

	/* ブレークコードの検査、Ctrl Shift以外は捨てる。 */
	if (key_coad & KEY_BREAK)
	{
		switch(map_coad)
		{
			case MAP_SHT:
				shift_key=0;
				break;
			case MAP_CTR:
				ctr_key=0;
				break;
			default:
				return;
		}
		add_key = add_map[(shift_key|ctr_key) ^ (led >> 2)];

		return;
	}

	/* ファンクションキーの処理 */
	switch(map_coad)
	{
		case MAP_SHT:
			shift_key=SHT_BIT;
			add_key=add_map[(shift_key|ctr_key)^(led>>2)];
			return;
		case MAP_CPSL:
			switchLed(led^=LED_CPSL);
			add_key=add_map[(shift_key|ctr_key)^(led>>2)];
			return;
		case MAP_CTR:
			ctr_key=CTR_BIT;
			add_key=add_map[(shift_key|ctr_key)^(led>>2)];
			return;
	}
	
	handlTermChr(map_coad, &termio, &termCtl, &lbuf);
}


/*
 * 割り込みハンドラー
 * return : task switch on off
 */
STATIC int keyboardIntrHandler()
{
	int rest = 0;

	keyboardHnadler(inb(KEYBOARD_OUTPUT_REG));
	if (isExistTermBuf(&termio, &lbuf) == TRUE){
		enter_spinlock(&waitLock);
		{
			if(waitProc != NULL){
				sys_wake(waitProc);
				waitProc = NULL;
				rest = 1;
			}
		}
		exit_spinlock(&waitLock);
	}

	return rest;
}


//================================== グローバル ========================================


/*
 * キーボードドライバーの初期化
 */
void initKeyboard()
{
	/* Turn off LED. */
	switchLed(0);

	/* 割り込みの設定 */
	irq_entry[IRQ1]=keyboardIntrHandler;

	release_irq_mask(IRQ_NO);

	/* clear data register */
	inb(KEYBOARD_OUTPUT_REG);
}


/***************************************************************************************
 *
 * システムコール
 *
 ***************************************************************************************/


STATIC int con_open(dev_t dev, int oflags, int devtype, d_thread_t *proc)
{
	return 0;
}


STATIC int con_close(dev_t dev, int fflag, int devtype, d_thread_t *proc)
{
	return 0;
}


/* size = 転送可能バイト数。 */
STATIC int con_read(dev_t dev, struct uio *uio, int ioflag)
{
	int len;

	/* フォアグラウンドグループか? */
	while (isForeground(get_current_task()) == 0){
		sys_sleep(-1);								/* ウエイト。 */
	}

	/* 先行入力があるかコンソールバッファを確認。 */
/*	enter_spinlock(&waitLock);*/
	if ((len = readTermBuf(uio->uio_iov->iov_base, uio->uio_iov->iov_len, &termio, &lbuf)) == -1){
		waitProc = get_current_task();			/* キー入力待ち。 */
/*		exit_spinlock(&waitLock);*/
		sys_sleep(-1);							/* ウエイト。 */

		len = readTermBuf(uio->uio_iov->iov_base, uio->uio_iov->iov_len, &termio, &lbuf);
	}
/*	else
		exit_spinlock(&waitLock);*/

	return len;
}


STATIC int con_write(dev_t dev, struct uio *uio, int ioflag)
{
	/* フォアグラウンドグループか? */
/*	while(isForeground(get_current_task())==0)
	{
		del_from_schedule(TASK_SIGNAL_WAIT, PROC_SCHEDULE_DEL);
		wait_task();
	}
*/

	return writeConsole(uio->uio_iov->iov_base, uio->uio_iov->iov_len, 0);
}


STATIC int con_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, d_thread_t *p)
{
	PROC *proc,*grp_proc;
	pid_t *ppgid,*psid;
	struct winsize *win;
	struct termios *tio;

	switch (cmd){
		case TIOCSCTTY:
			if (termCtl.ctlProc == NULL){
				termCtl.ctlProc = get_current_task();
				termCtl.foregrnd = termCtl.ctlProc->pgid;
				termCtl.ctlProc->ctlterm = &termCtl;
			}
			else{
				return -EPERM;
			}
			break;
		case TIOCSPGRP:		/* set foregrand gid */
			ppgid = (gid_t*)data;
			if (checkMem(ppgid, sizeof(gid_t)) == ERR){
				return -EFAULT;
			}
			proc = get_current_task();

			/* 端末制御プロセスか？ */
			if (proc != termCtl.ctlProc){
				return -ENOTTY;
			}
			if (proc->pgid != *ppgid){
				/* 与えられたグループが同セッション内のものか？ */
				grp_proc = searchProcList(*ppgid);
				if (grp_proc == NULL){
					return -EINVAL;
				}
				if (grp_proc->sid != proc->sid){
					return -EPERM;
				}
			}
			termCtl.foregrnd = *ppgid;
			break;
		case TIOCGPGRP:		/* get foregrand gid */
			if(get_current_task()->ctlterm != &termCtl){
				return -ENOTTY;
			}
			ppgid = (pid_t*)data;
			if (checkMem(ppgid,sizeof(pid_t)) == ERR){
				return -EFAULT;
			}
			*ppgid = termCtl.foregrnd;
			break;
		case TIOCGSID:		/* get session id */
			psid = (pid_t*)data;
			if (checkMem(psid,sizeof(pid_t)) == ERR){
				return -EFAULT;
			}
			*psid = termCtl.ctlProc->sid;
			break;
		case TIOCGWINSZ:
			win=(struct winsize*)data;
			if (checkMem(win,sizeof(struct winsize)) == ERR){
				return -EFAULT;
			}
			win->ws_row=SCR_LINES;
			win->ws_col=SCR_COLUMNS;
			break;
		case TIOCSWINSZ:
			/* 未定義 */
			break;
		case TCGETS:
			tio = (struct termios*)data;
			if (checkMem(tio,sizeof(struct termios)) == ERR){
				return -EFAULT;
			}
			*tio = termio;
			break;
		case TCSETS:
		case TCSETSW:
		case TCSETSF:
			tio = (struct termios*)data;
			if (checkMem(tio,sizeof(struct termios)) == ERR){
				return -EFAULT;
			}
			termio = *tio;
			break;
		case TIOCFLUSH:
		case TCFLSH:
		default:
			break;
	}

	return 0;
}


STATIC int con_poll(dev_t dev, int events, d_thread_t *p)
{
	int rest;

	enter_spinlock(&waitLock);
	{
		if (isExistTermBuf(&termio, &lbuf) == TRUE){
			rest = POLLIN | POLLRDNORM;
		}
		else{
			// キー入力待ち
			waitProc = get_current_task();
			rest = 0;
		}
	}
	exit_spinlock(&waitLock);

	return rest | POLLOUT | POLLWRNORM;
}


/***************************************************************************************
 *
 * 初期化
 *
 ***************************************************************************************/


/* Device interface */
struct cdevsw devInfo={
	con_open, 
	con_close, 
	con_read, 
	con_write, 
	con_ioctl, 
	con_poll, 
	NULL, 
	NULL, 
	"console", 
	0,
	NULL, 
	NULL, 
	D_TTY,
};


//================================== グローバル ========================================


/*
 * デバイス登録をする。
 */
int registConsole()
{
	if (make_dev(&devInfo, 0, 0, 0, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, "console") == NODEV){
		return -ENODEV;
	}
	
	return 0;
}


/*
 * コンソールの初期化
 */
int initConsole()
{
	/* スクリーンの初期化。 */
	initScreen();

	/* 端末機能の初期化。 */
	initTerm();

	return 0;
}


/***************************************************************************************
 *
 * デバッグ用
 *
 ***************************************************************************************/


#ifdef DEBUG
/*
 * 常に画面の先頭からの任意の桁から表示する
 */
void displayTop(int column, const char *bytes, int size)
{
	VRAM *position = vram + screen.viewTop + column;
	int i;
	
	
	for (i = 0; i < size; ++i){
		position->ch = bytes[i];
		++position;
	}
}
#endif
