/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */

/* $Header:keyboard.c 12.2$ */
/* $ACIS:keyboard.c 12.2$ */
/* $Source: /ibm/acis/usr/sys/standatr/RCS/keyboard.c,v $ */

#ifndef lint
static char *rcsid = "$Header:keyboard.c 12.2$";
#endif

/*
 * 6152 Keyboard input routine
 */

#include "sa.h"
#include "keyboard.h"
#include "../h/param.h"
#include "../machine/cpu.h"
#include "screen_conf.h"
#include "../ca_atr/pcif.h"		/* need cbcb to get data from keyboard */
#include "../pc_code/vga.h"		/* for VGA_SAVE */


#include "codes.h"


struct kbdata *kbdata;			/* will point to kbd data area in PC memory  */ 
long	kb_pc_cb;			/* where keyboard structure is */

int _init_kbd = 0;

getchar()
/*
 * getchar for standalone use: maps CR into NL
 * and echos each character. use _getchar to avoid these
 * effects.
 */
{

	register int c;

	c  = _getchar();
	if (c == '\r')
		c = '\n';
	putchar(c);
	return (c);
}

static int next_char;		       /* next character to return */

int esc_char = ESC1_CHAR;	       /* the first escape character */

#ifdef ESC2_CHAR
int esc2_char = ESC2_CHAR;
#else
int esc2_char = 0;
#endif


_getchar()
{
	register int c, ch;
	int iid;

	if (c = next_char) {
		if (c & ESC_MARK) {
			next_char = c - ESC_MARK;
			return (esc2_char);
		}
		next_char = 0;
		return (c);
	}
	if (!_init_kbd)
		init_kbd(); 

	for (;;) {
		ch = get_kbd(&iid, getchar_timeout);
		if (iid == KBD_TIMEOUT && (c = getchar_char))
			break;
		if (iid == KBD_ASCII) {
			c = ch;		/* already in ASCII */
			break;
		}
		if (iid == KBD_HOT_KEY) {
			/* 
			 * if we see a restore hotkey,
			 * reload the screen. It is
			 * always "saved".
			 */
			if (ch == VGA_RESTORE)
				screen_redraw();
			{
				unsigned old_window = get_512_window();
				unsigned vga_win = get_pc_cb(BIOSENT);
				struct vga_params *vga = (struct vga_params *)
						(set_512_window(vga_win)+pcif_512_fw);

				/* clear the opcode to tell the pc_code we're done */
				vga->pc_opcode = 0;
				set_512_window(old_window);
				
			}
			continue;
		}
		if ((c = key_scan(ch)) != NONE)
			break;
	}
	getchar_timeout = 0;
	if (c & ESC_MARK) {
		if (esc2_char)
			next_char = c; /* leave the mark */
		else
			next_char = c - ESC_MARK; /* remove mark */
		c = esc_char;	       /* send escape character 1 */
	}
	return (c);
}


/* following allows us to get rid of bell and keyboard clicks easily */
#ifndef NOBELL
#define NOBELL 0
#endif

int nobell = NOBELL;

/*
 *	This version of init_kbd came from ATR full-kernel version,
 *	and includes many fixes since original stand-alone version.
 */
init_kbd()
{
	register int i;
	int old_window;

	_init_kbd++;

	old_window = get_512_window();	/* save the current window */
	kb_pc_cb = get_pc_cb(KBENT);
	kbdata = (struct kbdata *)(set_512_window(kb_pc_cb) + pcif_512_fw);
	set_512_window(old_window);
	TRACEF(("init init_kbd\n"));

}


/*
 * wait for something to appear at the keyboard, then return it
 * and the associated status byte.
 */
get_kbd(iidptr, timeout)
	register int *iidptr;
	register int timeout;
{
	register int c, iid;

	TRACEF(("waiting for keyboard\n"));

	/*
	 *  If the PC is handling the keyboard then we must get
	 *  the PC address of the kbdata structure from the cbcb.
	 */
	register int old_window;

	old_window = get_512_window();	/* save the current window */
	kb_pc_cb = get_pc_cb(KBENT);
	kbdata = (struct kbdata *)(set_512_window(kb_pc_cb) + pcif_512_fw);

	/* check if new scan code is present */
	while ((iid = ((int)kbdata->kbstatus)) == 0) {
		if (timeout) {
			if(--timeout == 0) {
			if (iidptr != 0)
				*iidptr = KBD_TIMEOUT;
			TRACEF(("Timeout: kbdata kbstatus register: %x\n", iid));
			set_512_window (old_window);	/* restore window */
			return (0);
			}
		} else
			delay(1);	/* wait 1 ms before polling again */
	}
	c = (int)kbdata->kbsc;		/* read scan code */
	kbdata->kbstatus = 0;		/* inform PC that scan code was read */
	set_512_window (old_window);	/* restore window */


	TRACEF(("get_kbd: got %x (IID=%x) from keyboard\n", c, iid));
	if (iidptr != 0)
		*iidptr = iid & IID_MASK; /* return it if requested */
	return (c);
}


clear_kbd(n)
	register int n;
{
	register int i;
	int old_window;

	old_window = get_512_window();	/* save the current window */
	kbdata = (struct kbdata *)(set_512_window(kb_pc_cb) + pcif_512_fw);
	for(i=0;i<n;i++) {	/* clear pending scan codes */
		kbdata->kbstatus = 0;
		delay(1);
	}
	set_512_window(old_window);
}


/* flag bits for state variable */
#define CAPS_MODE	01	       /* must be 1. caps mode */
#define ALT_MODE	02	       /* alt mode */
#define CTL_MODE	04	       /* control mode */
#define NUM_MODE	010		/* numeric keypad mode */
#define SCROLL_MODE	020		/* scroll stop */

#ifdef CLICK_CHAR
#define CLICK_FREQ 0x1392	       /* click frequency */
#define CLICK_TIME 0x02		       /* minimal time */
int click_freq = CLICK_FREQ;
int click_time = CLICK_TIME;
#endif CLICK_CHAR

/*
 *	The following key_scan taken from ORIGINAL 3/31 standalone
 *	keyboard driver. ORIGINAL "codes.h" can be used for Enhanced
 *	AT keyboard, NEW codes_atr.h necessary for AT keyboard.
 */
static	int shift_lock_down = 0;

key_scan(code)
	register int code;
{
	register int n = (state & CAPS_MODE) + (code << 1); /* CAPS_MODE == 1 */
	register int c = NONE;
	static int click = 1;
	

	if (make_break) {
		register int old_state = state;

		make_break = 0;
		switch (code) {
		case SHIFT_LOCK:       /* lock key let go */
			shift_lock_down = 0;
			break;
		case ALT1:
		case ALT2:
			state &= ~ALT_MODE; /* alt key released */
			break;
		case SHIFT1:
		case SHIFT2:
			state &= ~CAPS_MODE; /* shift key released */
			set_kbd_led(~CAPS_LED);	/* turn off cap lock led */
			break;
		case CNTRL:
			state &= ~CTL_MODE; /* control key released */
			break;
		}
		if (state != old_state)
			kbd_status();  /* reflect state change */
	} else if (code >= MAX_CODES || (c = codes[n]) == NONE) {
		switch (code) {
		case SHIFT_LOCK:       /* caps lock key pressed */
			if (shift_lock_down == 0)
			{
				state ^= CAPS_MODE; /* invert caps state each time */
				set_kbd_led(state & CAPS_MODE ? CAPS_LED : ~CAPS_LED);
				shift_lock_down = 1;
				kbd_status();
			} 
			break;
		case ALT1:
		case ALT2:
			state |= ALT_MODE; /* key held down */
			kbd_status();
			break;
		case SHIFT1:
		case SHIFT2:
			state |= CAPS_MODE; /* key held down */
			kbd_status();
			break;
		case CNTRL:
			state |= CTL_MODE; /* key held down */
			kbd_status();
			break;
#ifdef DEBUG_CHAR
		case DEBUG_CHAR:
			if (state == 0)
				debug = !debug;
			else if (state == ALT_MODE)
				cnhangup();
			TRACEF(("DEBUG_CHAR detected!\n"));
			kbd_status();
			break;
#endif
		case BREAK:
			make_break = 1;	/* released a make/break key */
			break;
#ifdef SWITCH_CHAR
		case SWITCH_CHAR:
			switch_screen((state&CTL_MODE) ? SCREEN_SWITCH_RELOAD : SCREEN_SWITCH ); /* to next screen */
			break;
#endif SWITCH_CHAR

#ifdef PRINT_CHAR
		case PRINT_CHAR:
			print_screen(state & CTL_MODE);	/* print it */
			break;
#endif PRINT_CHAR

#ifdef CLICK_CHAR
		case CLICK_CHAR:
			{
				/* invert click flag */
				click = !click;
			}
			break;

#endif CLICK_CHAR
		}
	} else {
		if (state == (ALT_MODE | CTL_MODE) && c == 0177)
		TRACEF(("REBOOT not implemented!\n"));
/*			_reboot();      start execution again */
		if (state & CTL_MODE)
			c &= 037;
		if (c & ESC_FLAG)
			c ^= ESC_FLAG | ESC_MARK; /* turn on extra bit for ESC */
		if (state & ALT_MODE)
			c |= ALT_MARK; /* flag as alt hit */
	}
	TRACEF(("code = %x state=%x ==>%x\n", code, state, c));
#ifdef CLICK_CHAR
	if (c != NONE && (click) )
		speaker(click_freq, click_time);
#endif CLICK_CHAR

	return (c);
}


static kbd_status()
{
	static int xdebug = -1;

	if (xdebug != debug) {
debug_loop:
		put_status(60, debug ? "DEBUG" : "     ");
		xdebug = debug;
	}
	put_status(68, (state & ALT_MODE) ? "ALT" : "   ");
	put_status(71, (state & CAPS_MODE) ? "CAPS" : "    ");
	put_status(75, (state & CTL_MODE) ? "CTRL" : "    ");
}



static
set_kbd_led(leds)
	char leds;
{
}

int beep_freq = BEEP_FREQ;
int beep_time = BEEP_TIME;

beep()
{
	speaker(beep_freq, beep_time);
}


/*	Following had to be implemented off-keyboard on the AT	*/

speaker(beep_freq, beep_time)
register int beep_freq, beep_time;
{
}

static cnhangup()
{
	beep();
}
