/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:ibm_emul.c 12.0$ */
/* $ACIS:ibm_emul.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/standca/RCS/ibm_emul.c,v $ */

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

/************************************************************************/
/*									*/
/*	Routines to Emulate an IBM3101 on any Screen (memory mapped).	*/
/* We implement a simple termcap for now in software.			*/
/*									*/
/************************************************************************/

#define IBM3101

#include "param.h"
#include "dir.h"
#include "user.h"
#include "proc.h"

#include "conf.h"

#include "screen_conf.h"
#include "consdefs.h"
#include "mono_tcap.h"
#include "sa.h"

int nulldev();				/* Returns False (0) */
int nodev();				/* Returns NODEV */

#include "mono.h"
#if NMONO > 0
int lp_put();
#else
#define lp_put	nodev 
#endif NMONO


/*
 * Output Emulator of a IBM3101 on IBM RT PC
 * currently only single character control characters or the single letter
 * ESCAPE sequences of the IBM3101 terminal.
 * the goal - minimal support needed for Vi - later we can support proper ANSI
 * escape sequences.
 * the following functions are supported:
 * CR
 * LF - with scrolling
 * clear screen
 * erase to end of line
 * erase to end of screen
 * inverse video
 * status line on/off
 *
 */


#if !defined(LP_LOG)
#define LP_LOG		0	/* by default no loggin on line printer */
#endif

int monopagemode;		/* non-scroll mode */
#define PAGEEOLCLR	0x02	/* if we are to clear to eol in page mode */
#define PAGEMODE	0x01	/* if in page mode */

#define NORMAL		0
#define CURSOR_Y	1
#define CURSOR_X	2
#define ESC_1		3	       /* first letter of escape sequence */

int print_log = LP_LOG;		/* log screen output */
static int old_x, old_y;
int screen_length;

/* Put status stuff */
put_status_offset = 0;
char status_line[81] = "                                                                                ";

/* Normal Screen Character attribute */
LOCAL char screen_attr = NORMAL_VIDEO;

#if !defined(LF_DELAY)
#define LF_DELAY 0                     /* by default no hard_delay required */
#endif

LOCAL lf_ibme_delay = LF_DELAY;        /* for slowing down the output */

#define b_line(line) blank_line (screen_attr, line)
#define IBME_OPEN 0x1
#define IBME_INPUTC 0x2

ibme_open(dev, tp, si, n)
int n;
dev_t dev;
register struct tty *tp;
register SCREEN_INFO *si;
{
	register EMUL_INFO *ei = &si->oute;

	if (ei->flag & IBME_OPEN)
		return (0);
	ei->flag |= IBME_OPEN;
	screen_length = SCREEN_LENGTH - 1;	/* allow for status line */
	if (n == SCREEN_SWITCH_RELOAD) 
		/* get in step with xxx_screen_init */
		screen_x = screen_y = 0;
	else if (n == SCREEN_SWITCH) 
		{
		/* save information currently on screen */
		pos_cursor(screen_x, screen_y);
		}
}

ibme_close(tp, si)
register struct tty *tp;
register SCREEN_INFO *si;
{
	register EMUL_INFO *ei = &si->oute;

	ei->flag = 0;
	ei->unit = 0;
}


ibme_putc(c, si)
register int c;
register SCREEN_INFO *si;
{
	/* Output Emulator Information */
	register EMUL_INFO *ei = &si->oute;

	static put_state;	/* internal state for cursor motion etc. */
	static cursor_base;	/* base for cursor motion */
	register int temp;
#ifdef ATR
	int   atr_display_mode;
	char *atr_display_msg;
#endif ATR

EDEBUG (0x01, aed_pr ("ibme_putc: (%c, %x) (%d)\n", c, c, put_state));
	ei->flag |= IBME_INPUTC;

	switch (put_state) {
	case CURSOR_Y:
		c -= cursor_base;      /* less the offset */
		screen_y = min(c, SCREEN_LENGTH-1);
		put_state = CURSOR_X;
		break;
	case CURSOR_X:		       /* was 1 now 0 */
		c -= cursor_base;      /* less the offset */
		screen_x = min(c, SCREEN_WIDTH-1);
		put_state = NORMAL;
		break;
#ifdef ESC_FOUND
	case ESC_1:
		put_state = NORMAL;    /* revert to normal mode */
		c |= ESC_FOUND;
		/* fall thru */
#endif
	default:		       /* NORMAL state */
		switch (c) {
		case SH_CHAR:
			if (screen_length == SCREEN_LENGTH)
				screen_length--;
			else
				screen_length++;
			b_line(SCREEN_LENGTH - 1);
			break;
#ifdef FR_GRND
#ifdef IBMRTPC
		case FR_GRND:
			screen_attr = fg_bg(screen_attr, FOREGROUND_COLOR);
			break;

		case BK_GRND:
			screen_attr = fg_bg(screen_attr, BACKGROUND_COLOR);
			break;

#else  /* ATR version */

		case FR_GRND:
			screen_attr = fg_bg(screen_attr, COLOR_FG_INC);
			break;

		case BK_GRND:
			screen_attr = fg_bg(screen_attr, COLOR_BG_INC);
			break;

		case FR_DEC:
			screen_attr = fg_bg(screen_attr, COLOR_FG_DEC);
			break;

		case BK_DEC:
			screen_attr = fg_bg(screen_attr, COLOR_BG_DEC);
			break;

		case REVERSE:
			screen_attr = fg_bg(screen_attr, REVERSE_COLOR);
			break;

		case CHG_DISPLAY_MODE:
			atr_display_mode = fg_bg(screen_attr,
						 CHANGE_DISPLAY_MODE);

			screen_length = SCREEN_LENGTH - 1;
			lf_ibme_delay = LF_DELAY;
			screen_attr   = NORMAL_VIDEO;

			/* screen is cleared to NORMAL_VIDEO, and     */
			/* the cursor is set to (0,0) in the vga and  */
			/* ibm8514 driver routines.		      */
			screen_x = screen_y = 0;

			atr_display_msg     = "Display mode is now XX        ";
			atr_display_msg[20] = atr_display_mode/10 + '0';
			atr_display_msg[21] = atr_display_mode%10 + '0';
			ibme_dump_status(si,  0, atr_display_msg);
			break;

		case BLINK_ENABLE:
			screen_attr = fg_bg(screen_attr, ENABLE_BLINK);
			break;

		case BG_HI_ENABLE:
			screen_attr = fg_bg(screen_attr, ENABLE_BG_INTENSITY);
			break;

		case BS_CHAR:
			screen_attr |= BLINK;
			break;
		case BE_CHAR:
			screen_attr &= ~BLINK;
			break;

#endif IBMRTPC
#endif FR_GRND

#ifdef SO_CHAR
		case SO_CHAR:
			screen_attr = REVERSE_VIDEO;
			break;
		case SE_CHAR:
			screen_attr = NORMAL_VIDEO;
			break;
#endif SO_CHAR
		case HO_CHAR:
			screen_x = screen_y = 0;
			break;
#ifdef US_CHAR
		case US_CHAR:
			screen_attr = UNDERLINE_VIDEO;
			break;
		case UE_CHAR:
			screen_attr = NORMAL_VIDEO;
			break;
#endif
#ifdef HI_CHAR
		case HI_CHAR:
			screen_attr |= HI_INTENSITY;
			break;
		case LO_CHAR:
			screen_attr &= ~HI_INTENSITY;
			break;
#endif HI_CHAR
		case UP_CHAR:	       /* index */
			screen_index(si);
			break;
		case 07:	       /* bell */
			beep();	       /* ring bell */
			break;
		case '\n':
			screen_lf(si);
			break;
		case '\t':
			screen_tab(si);
			return;	       /* cursor updated already */
		case '\r':
			screen_cr(si);
			break;
		case '\b':
			screen_bs(si);
			break;
#ifdef ESC_FOUND
		case 033:       /* if emulating a terminal with ESC sequences */
			put_state = ESC_1;
			break;
#endif
		case CL_CHAR:
			screen_clear(si);
			break;
		case ND_CHAR:
			if (++screen_x >= SCREEN_WIDTH) {
				screen_cr(si);
				screen_lf(si);
			}
			break;
#ifdef CM_CHAR		       /* primary cursor is directly generated */
		case CM_CHAR:
			put_state = CURSOR_Y; /* number of cursor characters */
			cursor_base = 0; /* no base value */
			break;
#endif

#ifdef CM_CHAR2			       /* secondary cursor addressing uses an offset */
		case CM_CHAR2:
			put_state = CURSOR_Y; /* number of cursor characters */
			cursor_base = CURSOR_OFFSET; /* blank is base value */
			break;
#endif

		case CD_CHAR:	       /* clear to end of screen */
			screen_blank(screen_attr, screen_y, screen_x, SCREEN_LENGTH-1, SCREEN_WIDTH-1);
			break;

		case CE_CHAR:	       /* clear to end of line */
			screen_blank(screen_attr, screen_y, screen_x, screen_y, SCREEN_WIDTH - 1);
			break;

		case 0:
			return;	       /* ignore nulls (fast) */
#ifdef SAVE_CM
		case SAVE_CM:	       /* save current cursor */
			old_x = screen_x;
			old_y = screen_y;
			break;
		case RESTORE_CM:       /* restore saved cursor */
			screen_x = old_x;
			screen_y = old_y;
			pos_cursor (screen_x, screen_y);
			break;
#endif SAVE_CM
#ifdef IGN_CHAR
		case IGN_CHAR:
			put_state = ESC_1; /* ignore it */
			break;
#endif

#ifdef AL_CHAR:
			/* 
			 * insert a line (blank) by moving the current line and
			 * following lines down the screen.
			 */
		case AL_CHAR:	       /* insert line */
			if (screen_y < screen_length - 1)
				screen_move(screen_y, screen_length - 2, screen_y + 1);
			/* move the lines down */
			b_line(screen_y); /* blank current line */
			break;
		case DL_CHAR:	       /* delete line */
			screen_move(screen_y + 1, screen_length - 1, screen_y);
			/* move lines up */
			b_line(screen_length - 1); /* blank last line */
			break;
#endif
		default:
#ifdef ESC_FOUND
			if ((c & ESC_FOUND) == 0)
#endif
			screen_out(si, c); /* normal character */
			break;
		}
		if (print_log && lp_put(c)) /* log to printer */
			print_log = 0; /* error - stop logging */
	}
	pos_cursor(screen_x, screen_y);	/* put cursor on right spot */
	if (put_status_offset) {
		register int s;
		ibme_dump_status (si, put_status_offset, &status_line[put_status_offset]);
		put_status_offset = 0;
	}

	ei->flag &= ~IBME_INPUTC;
}

LOCAL
screen_clear(si)
register SCREEN_INFO *si;
{
	screen_blank(screen_attr, 0, 0, screen_length-1, SCREEN_WIDTH-1);
	screen_x = screen_y = 0;
}


LOCAL
screen_out(si, c)
register SCREEN_INFO *si;
register c;
{
	/*
	 * output the given character at the current screen position and then
	 * update the pointer to the new position.
	 */
EDEBUG (0x01, aed_pr("screen_out (%c)\n", c));
	screen_putc (c, screen_attr);
	if (++screen_x >= SCREEN_WIDTH) {
		screen_cr(si);
		screen_lf(si);
	}
EDEBUG (0x01, aed_pr("screen_out leaving\n"));
}


/*
 * do a line feed on the screen.
 * two cases:
 * 1. we are at the bottom of the screen
 * 2. we are not
 * we will do a scroll if we are at the bottom, and then fall thru to
 * the other case.
 */
LOCAL
screen_lf(si)
register SCREEN_INFO *si;
{
EDEBUG (0x02, aed_pr("screen_lf\n"));
	if (++screen_y >= screen_length) {
		if (monopagemode & PAGEMODE)
			screen_y = 0;
		else {
			screen_y = screen_length - 1;
			/* move the lines */
			screen_move(1, screen_length - 1, 0);
			b_line(screen_length - 1);
		}
	}
	if (lf_ibme_delay)
		hard_delay(lf_ibme_delay);	/* hard_delay for readability */
EDEBUG (0x02, aed_pr("screen_lf leave\n"));
}


LOCAL
screen_index(si)
register SCREEN_INFO *si;
{
	if (--screen_y < 0) {
		screen_move(0, screen_length - 2, 1); /* move the lines */
		b_line(0);
		screen_y = 0;
	}
}


/*
 * go to start of line 
 * and do a clear to eol if we are in page mode 
 */
LOCAL
screen_cr(si)
register SCREEN_INFO *si;
{
EDEBUG (0x02, aed_pr("screen_cr\n"));
	if (monopagemode & PAGEEOLCLR)
		screen_blank(screen_attr, screen_y, screen_x, screen_y, SCREEN_WIDTH - 1);
	screen_x = 0;
EDEBUG (0x02, aed_pr("screen_cr leave\n"));
}


LOCAL
screen_bs(si)
register SCREEN_INFO *si;
{
	if (--screen_x < 0) {
		screen_x = SCREEN_WIDTH - 1;
		if (--screen_y < 0)
			screen_y = screen_length - 1;
	}
}


LOCAL
screen_tab(si)
register SCREEN_INFO *si;
{
	register int n = 8 - (screen_x & 07);

	while (--n >= 0)
		ibme_putc(ND_CHAR, si);     /* non destructive tab */
}

ibme_put_status(si, offset, string)
register SCREEN_INFO *si;
register char *string;
{
	/* Output Emulator Information */
	register EMUL_INFO *ei = &si->oute;

EDEBUG (0x40, aed_pr("ibme_put_status (%d) (%s)\n\r", offset, string));

	if (screen_length == SCREEN_LENGTH)
		return;                /* no status display */

	if (!(ei->flag & IBME_INPUTC)) {
		/* Put the status out now */
		ibme_dump_status (si, offset, string);
	} else {
		/* Mark it to put out latter */
		if ((put_status_offset == 0) || (offset < put_status_offset))
			put_status_offset = offset;
		while (*string) {
			status_line[offset++] = *string++;
		}
	}

EDEBUG (0x40, aed_pr("ibme_put_status done\n\r"));
}

ibme_dump_status(si, offset, string)
register SCREEN_INFO *si;
register char *string;
{
	register int x, y;
	x = screen_x, y = screen_y;

	if (screen_length == SCREEN_LENGTH)
		return;                /* no status display */

	while (*string) {
		pos_cursor(offset++, STATUS_LINE);
		screen_out(si, *string++);
	}
	screen_x = x, screen_y = y;
	pos_cursor(screen_x, screen_y);	/* put cursor on right spot */

}

ibme_ioctl(tp, cmd, addr, si)
register struct tty *tp;
int cmd;
caddr_t addr;
register SCREEN_INFO *si;
{
	return (-1);

}
