/* -*- linux-c -*- ------------------------------------------------------- *
 *
 *   Copyright (C) 1991, 1992 Linus Torvalds
 *   Copyright 2007 rPath, Inc. - All Rights Reserved
 *
 *   This file is part of the Linux kernel, and is made available under
 *   the terms of the GNU General Public License version 2.
 *
 * ----------------------------------------------------------------------- */

/*
 * arch/i386/boot/tty.c
 *
 * Very simple screen I/O
 * XXX: Probably should add very simple serial I/O?
 */

#include "boot.h"

/*
 * These functions are in .inittext so they can be used to signal
 * error during initialization.
 */

void __attribute__((section(".inittext"))) putchar(int ch)
{
	unsigned char c = ch;

	if (c == '\n')
		putchar('\r');	/* \n -> \r\n */

	/* int $0x10 is known to have bugs involving touching registers
	   it shouldn't.  Be extra conservative... */
	asm volatile("pushal; int $0x10; popal"
		     : : "b" (0x0007), "c" (0x0001), "a" (0x0e00|ch));
}

void __attribute__((section(".inittext"))) puts(const char *str)
{
	int n = 0;
	while (*str) {
		putchar(*str++);
		n++;
	}
}

/*
 * Read the CMOS clock through the BIOS, and return the
 * seconds in BCD.
 */

static u8 gettime(void)
{
	u16 ax = 0x0200;
	u16 cx, dx;

	asm("int $0x1a"
	    : "+a" (ax), "=c" (cx), "=d" (dx)
	    : : "ebx", "esi", "edi");

	return dx >> 8;
}

/*
 * Read from the keyboard
 */
int getchar(void)
{
	u16 ax = 0;
	asm("int $0x16" : "+a" (ax));

	return ax & 0xff;
}

static int kbd_pending(void)
{
	u8 pending;
	asm("int $0x16; setnz %0"
	    : "=rm" (pending)
	    : "a" (0x0100));
	return pending;
}

void kbd_flush(void)
{
	for (;;) {
		if (!kbd_pending())
			break;
		getchar();
	}
}

int getchar_timeout(void)
{
	int cnt = 30;
	int t0, t1;

	t0 = gettime();

	while (cnt) {
		if (kbd_pending())
			return getchar();

		t1 = gettime();
		if (t0 != t1) {
			cnt--;
			t0 = t1;
		}
	}

	return 0;		/* Timeout! */
}
