/*
 * console.c
 *
 * Copyright 2002, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 *
 * Low lebel screen print routines
 */


#include"types.h"
#include"lib.h"
#include"lock.h"


enum{
	BASE_ADDR=0xb8000,		/* Start address of video memory */
	VMEM_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 */

	LINES=25,				/* Character lines */
	COLUMNS=80,				/* Character columns */
	LAST_POS=ROUNDDOWN(VMEM_SIZE,COLUMNS*2)
};


static int current_pos;			/* Current screen position */
static int screen_end;			/* End screen position */


static void update_cursor();
static void scrollup();


/* 改行 */
extern inline void newline()
{
	current_pos=ROUNDUP(current_pos+1,COLUMNS*2);
}


/*
 *
 */
extern inline void del()
{
}


/*
 *
 */
extern inline void backspace()
{
}


/*
 *
 */
extern inline void tab()
{
}


/*
 * Init console
 */
int init_console()
{
	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);

	/* Init screen positions */
	current_pos=0;
	screen_end=COLUMNS*LINES*2;

	/* Clear screen */
	for(p=(short*)BASE_ADDR;p<(short*)(BASE_ADDR+VMEM_SIZE);++p)*p=0x700;

	return 0;
}


/*
 * Print buf
 * parameters : Begin position,Print bytes
 * returns : Number of printing characters
 */
int write_console(const char *buf,size_t n)
{
	static char *p=(char*)BASE_ADDR;
	static int lock_gate=0;
	int intr_flag;

	int i;


	enter_intrlock(&intr_flag);
	enter_spinlock(&lock_gate);
	{
		for(i=0;i<n;++i)
	    	switch(buf[i])
	    	{
	    		case '\n':
	    			newline();
	    			++i;
	    			break;
	    		case '\b':
	    			backspace();
	    			break;
	    		case '\t':
	    			tab();
	    			break;
	    		default:
	    			p[current_pos]=buf[i];
	    			current_pos+=2;
	    	}

	    /* 必要ならscroll up */
	    if(current_pos>=screen_end)
	    {
	    	screen_end=ROUNDUP(current_pos+1,COLUMNS*2);
	    	scrollup();
	    }

	    update_cursor();
	}
	exit_spinlock(&lock_gate);
	exit_intrlock(&intr_flag);

	return n;
}


/*
 * Update cursor position
 * parameters :
 */
static void update_cursor()
{
	outb(CTL_ADDR,CURSOR_ADDR_HIGH);
	outb(CTL_DATA,current_pos/2>>8);
	outb(CTL_ADDR,CURSOR_ADDR_LOW);
	outb(CTL_DATA,current_pos/2);
}


/*
 * Hard scroll up
 */
static void scrollup()
{
	char *p,*q;
	int top;
	int i;


	top=screen_end-COLUMNS*LINES*2;

	if(screen_end>=LAST_POS)
	{
		current_pos-=top;
		screen_end=COLUMNS*LINES*2;

		/* Video ramの先頭に移動 */
		p=(char*)BASE_ADDR;
		q=(char*)&p[top];
		for(i=0;i<current_pos;i+=2)p[i]=q[i];

		/* Video ramを0クリアー */
		for(;i<LAST_POS;i+=2)p[i]=0;

		top=0;
	}

	/* Hard scroll up */
	top/=2;
	outb(CTL_ADDR,START_ADDR_HIGH);
	outb(CTL_DATA,top>>8);
	outb(CTL_ADDR,START_ADDR_LOW);
	outb(CTL_DATA,top);
}
