/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * 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/cacons/RCS/ibm_emul.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
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.			*/
/*									*/
/************************************************************************/

#include "mono.h"
#include "apaeight.h"
#include "apaeightc.h"
#include "apasixteen.h"
#include "ega.h"
#include "mpel.h"
#ifdef ATR
#include "vga.h"
#include "ibmvbarmmmdxiv.h"
#include "pcbios.h"
#else ATR
#define NVGA 0
#define NPCBIOS 0
#define NIBMVBARMMMDXIV 0
#endif ATR
#if (NAPAEIGHT > 0) || (NAPAEIGHTC > 0) || (NAPASIXTEEN > 0) ||       \
    (NMONO > 0) || (NEGA > 0) || (NVGA > 0) ||     \
    (NIBMVBARMMMDXIV > 0) || (NMPEL > 0)

#define IBM3101

#include "../h/param.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/ioctl.h"
#include "../h/tty.h"

#include "../machinecons/screen_conf.h"
#include "../machinecons/consdefs.h"
#include "../machinecons/ibm_emul.h"
#include "../machine/io.h"


#ifndef MINIROOT
int lp_put();
#endif /* !MINIROOT */
extern struct tty cons[];

/*
 * Output Emulator of a IBM3101 on SAILBOAT
 * 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
 *
 */

/* ESCAPE sequences added to change foreground or background color
 * on the EGA when in an enhanced of color emulation mode.
 */


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


#define b_line(line,fg,bg) blank_line (NORMAL_VIDEO, line, fg, bg)

char status_line[79] = "                                                                              ";

ibme_open(dev, tp, si)
dev_t dev;
register struct tty *tp;
register SCREEN_INFO *si;
{
	register EMUL_INFO *ei = &si->oute;
	register struct ibme_softc *ibmes = &ibme_softc[dev];

	if (ei->flag & IBME_OPEN)
		return (0);
	ei->flag |= IBME_OPEN;

	ibmes->screen_length = SCREEN_LENGTH - 1; /* allow for status line */
	ibmes->lf_ibme_delay = LF_DELAY;	  /* for slowing down output */
	/*ibmes->monopagemode = PAGEEOLCLR;*/

	/* Normal Screen Character attribute */
	ibmes->screen_attr = NORMAL_VIDEO;
	ibmes->save_fg = ibmes->fg = screen_sw[WS].color_entries-1;
	ibmes->save_bg = ibmes->bg = BACKGROUND_COLOR;
	ibmes->red = (screen_sw[WS].color_entries <= SCREEN_RED ?
							ibmes->fg : SCREEN_RED);

	if ((ibmes->screen_x > SCREEN_WIDTH) || (ibmes->screen_y > SCREEN_LENGTH)) {
		screen_clear(si);
	}
	pos_cursor(ibmes->screen_x, ibmes->screen_y); /* reset cursor */
}

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_write(tp, uio, si)
register struct tty *tp;
struct uio *uio;
register SCREEN_INFO *si;
{
	return ((*linesw[tp->t_line].l_write)(tp, uio));
}

/* set the color table */
ibme_color_table(entry,red,green,blue,flags,si)
	unsigned entry,red,green,blue;
	int	flags;
	SCREEN_INFO *si;
{
	screen_color_table(entry,red,green,blue,flags);
}

ibme_putc(c, si, kern_flag)
register int c;
register SCREEN_INFO *si;
register int	kern_flag;
{
	/* Output Emulator Information */
	register EMUL_INFO *ei = &si->oute;
	register struct ibme_softc *ibmes = &ibme_softc[WS];
	int   display_mode;
	char *display_msg;

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

	switch (ibmes->put_state) {
	case CURSOR_Y:
		c -= ibmes->cursor_base;      /* less the offset */
		if (c >= 0)			/* ignore bad data */
			ibmes->screen_y = min(c, SCREEN_LENGTH-1);
		ibmes->put_state = CURSOR_X;
		break;
	case CURSOR_X:		       /* was 1 now 0 */
		c -= ibmes->cursor_base;      /* less the offset */
		if (c >= 0)			/* ignore bad data */
			ibmes->screen_x = min(c, SCREEN_WIDTH-1);
		ibmes->put_state = NORMAL;
		break;
	case COLOR_TABLE1:
	case COLOR_TAB_RED:
	case COLOR_TAB_GREEN:
	case COLOR_TAB_BLUE:
	case COLOR_FG:
	case COLOR_BG:
		/* look for terminations first */
		if (c == ';') {
			switch (ibmes->put_state) {
				/* color table index found, get next color */
				case COLOR_TABLE1:
					ibmes->put_state = COLOR_TAB_RED;
					ibmes->color_tab = ibmes->color;
					ibmes->color = 0;
					ibmes->color_count = 0;
					break;
				/* red found, normalize color */
				case COLOR_TAB_RED:
					ibmes->put_state = COLOR_TAB_GREEN;
					ibmes->color_red=NORMALIZE_COLOR(
					 ibmes->color,ibmes->color_count);
					ibmes->color = 0;
					ibmes->color_count = 0;
					break;
				case COLOR_TAB_GREEN:
					ibmes->put_state = COLOR_TAB_BLUE;
					ibmes->color_green=NORMALIZE_COLOR(
					 ibmes->color,ibmes->color_count);
					ibmes->color = 0;
					ibmes->color_count = 0;
					break;
				case COLOR_TAB_BLUE:
					ibmes->put_state = NORMAL;
					ibmes->color_blue=NORMALIZE_COLOR(
					 ibmes->color,ibmes->color_count);
					screen_color_table(ibmes->color_tab,
					 ibmes->color_red, ibmes->color_green,
					 ibmes->color_blue, COLOR_SET);
					break;
				case COLOR_FG:
					ibmes->put_state = NORMAL;
					ibmes->fg = ibmes->color;
					break;
				case COLOR_BG:
					ibmes->put_state = NORMAL;
					ibmes->bg = ibmes->color;
					break;
			}
			break;
		}
		/* check for overflows */
		if (ibmes->color_count == MAX_COLOR_COUNT) {
			ibmes->put_state = NORMAL;
			break;
		}
		ibmes->color_count++;
		if (( c >= '0') && (c <= '9')) {
			ibmes->color = (ibmes->color*16)+(c-'0');
			break;
		}
		if (( c >= 'a') && (c <= 'f')) {
			ibmes->color = (ibmes->color*16)+(c-'a')+0xa;
			break;
		}
		if (( c >= 'A') && (c <= 'F')) {
			ibmes->color = (ibmes->color*16)+(c-'A')+0xa;
			break;
		}
		/* illegal character for this escape squence */
		ibmes->put_state = NORMAL;
		break;
#ifdef ESC_FOUND
	case ESC_1:
		ibmes->put_state = NORMAL;    /* revert to normal mode */
		c |= ESC_FOUND;
		/* fall thru */
#endif
	default:		       /* NORMAL state */
		switch (c) {
		case SH_CHAR:
			{
			struct	tty	*tp = &cons[ WS ];

			if (ibmes->screen_length == SCREEN_LENGTH) {
				ibmes->screen_length--;
			} else {
				ibmes->screen_length++;
			}
			tp->t_winsize.ws_row = ibmes->screen_length;
			gsignal( tp->t_pgrp, SIGWINCH);
			b_line(SCREEN_LENGTH - 1,ibmes->fg,ibmes->bg);
			break;
			}

#ifdef FR_GRND
		case FR_GRND:
			screen_color_table(ibmes->fg, 0, 0, 0, COLOR_FG_INC);
			break;

		case BK_GRND:
			screen_color_table(ibmes->bg, 0, 0, 0, COLOR_BG_INC);
			break;
#endif FR_GRND
#ifdef FR_DEC
		case FR_DEC:
			screen_color_table(ibmes->fg, 0, 0, 0, COLOR_FG_DEC);
			break;

		case BK_DEC:
			screen_color_table(ibmes->bg, 0, 0, 0, COLOR_BG_DEC);
			break;
#endif FR_DEC
#ifdef REVERSE
		case REVERSE:
			screen_color_table(ibmes->fg, ibmes->bg,
					   0, 0, REVERSE_COLOR);
			break;
#endif
#ifdef CHG_DISPLAY_MODE
		case CHG_DISPLAY_MODE:
		     display_mode =
		       screen_color_table(0, 0, 0, 0, CHANGE_DISPLAY_MODE);
	
		     if (display_mode < 0) {
			break;
		     }

		     ibmes->screen_length = SCREEN_LENGTH - 1;
		     ibmes->lf_ibme_delay = LF_DELAY;
		     ibmes->screen_attr = NORMAL_VIDEO;
		     ibmes->save_fg = ibmes->fg = screen_sw[WS].color_entries-1;
		     ibmes->save_bg = ibmes->bg = BACKGROUND_COLOR;
		     ibmes->red = (screen_sw[WS].color_entries <= SCREEN_RED ?
						     ibmes->fg : SCREEN_RED);

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

		     display_msg	 = "Display mode is now 0xXX      ";
		     display_msg[22] = (display_mode > 0x9f ? 
			(display_mode >> 4) + 'a' : (display_mode >> 4) +'0');
		     display_mode &= 0xf;
		     display_msg[23] = (display_mode > 0x9 ?
			(display_mode) + 'a' - 10: (display_mode) +'0');
		     ibme_dump_status(si,  0, display_msg);
		     break;
#endif CHG_DISPLAY_MODE
#ifdef BLINK_ENABLE
		case BLINK_ENABLE:
			screen_color_table(0, 0, 0, 0, ENABLE_BLINK);
			break;

		case BG_HI_ENABLE:
			screen_color_table(0, 0, 0, 0, ENABLE_BG_INTENSITY);
			break;
#endif BLINK_ENABLE
#ifdef BS_CHAR
		case BS_CHAR:
			ibmes->screen_attr |= BLINK;
			break;
		case BE_CHAR:
			ibmes->screen_attr &= ~BLINK;
			break;
#endif BS_CHAR
#ifdef CL_SAVE
		case CL_SAVE:
			ibmes->save_fg = ibmes->fg;
			ibmes->save_bg = ibmes->bg;
			break;
		case CL_RSTR:
			ibmes->fg = ibmes->save_fg;
			ibmes->bg = ibmes->save_bg;
			break;
#endif CL_SAVE
#ifdef CT_CHAR
		case CT_CHAR:
			ibmes->put_state = COLOR_TABLE1;
			ibmes->color = 0;
			ibmes->color_count = 0;
			break;
		case FG_CHAR:
			ibmes->put_state = COLOR_FG;
			ibmes->color = 0;
			ibmes->color_count = 0;
			break;
		case BG_CHAR:
			ibmes->put_state = COLOR_BG;
			ibmes->color = 0;
			ibmes->color_count = 0;
			break;
#endif CT_CHAR
#ifdef SO_CHAR
		case SO_CHAR:
			ibmes->screen_attr |= REVERSE_VIDEO;
			break;
		case SE_CHAR:
			ibmes->screen_attr &= ~REVERSE_VIDEO;
			break;
#endif SO_CHAR
		case HO_CHAR:
			ibmes->screen_x = ibmes->screen_y = 0;
			break;
#ifdef US_CHAR
		case US_CHAR:
			ibmes->screen_attr |= UNDERLINE_VIDEO;
			break;
		case UE_CHAR:
			ibmes->screen_attr &= ~UNDERLINE_VIDEO;
			break;
#endif
#ifdef HI_CHAR
		case HI_CHAR:
			ibmes->screen_attr |= HI_INTENSITY;
			break;
		case LO_CHAR:
			ibmes->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 */
			ibmes->put_state = ESC_1;
			break;
#endif
		case CL_CHAR:
			screen_clear(si);
			break;
		case ND_CHAR:
			if (++ibmes->screen_x >= SCREEN_WIDTH) {
				screen_cr(si);
				screen_lf(si);
			}
			break;
#ifdef CM_CHAR		       /* primary cursor is directly generated */
		case CM_CHAR:
			ibmes->put_state = CURSOR_Y; /* number of cursor characters */
			ibmes->cursor_base = 0; /* no base value */
			break;
#endif

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

		case CD_CHAR:	       /* clear to end of screen */
			screen_blank(NORMAL_VIDEO, ibmes->screen_y,
			 ibmes->screen_x, SCREEN_LENGTH-1, SCREEN_WIDTH-1,
			 ibmes->fg,ibmes->bg);
			break;

		case CE_CHAR:	       /* clear to end of line */
			screen_blank(NORMAL_VIDEO, ibmes->screen_y,
			 ibmes->screen_x, ibmes->screen_y, SCREEN_WIDTH - 1,
			 ibmes->fg,ibmes->bg);
			break;

		case 0:
			return;        /* ignore nulls (fast) */
#ifdef SAVE_CM
		case SAVE_CM:	       /* save current cursor */
			ibmes->old_x = ibmes->screen_x;
			ibmes->old_y = ibmes->screen_y;
			break;
		case RESTORE_CM:       /* restore saved cursor */
			ibmes->screen_x = ibmes->old_x;
			ibmes->screen_y = ibmes->old_y;
			pos_cursor (ibmes->screen_x, ibmes->screen_y);
			break;
#endif SAVE_CM
#ifdef IGN_CHAR
		case IGN_CHAR:
			ibmes->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 */
			/* move the lines down */
			if (ibmes->screen_y < ibmes->screen_length - 1)
				screen_move(ibmes->screen_y,
					ibmes->screen_length - 2,
					ibmes->screen_y + 1);
			/* blank current line */
			b_line(ibmes->screen_y,ibmes->fg,ibmes->bg);
			break;
		case DL_CHAR:	       /* delete line */
			/* move lines up */
			screen_move(ibmes->screen_y + 1,
				ibmes->screen_length - 1, ibmes->screen_y);
			/* blank last line */
			b_line(ibmes->screen_length - 1, ibmes->fg, ibmes->bg);
			break;
#endif
		default:
#ifdef ESC_FOUND
			if ((c & ESC_FOUND) == 0)
#endif
			screen_out(si, c, kern_flag); /* normal character */
			break;
		}
#ifndef MINIROOT
		/* log to printer */
		if ((screen_sw[WS].flags & CONSDEV_LOG) && lp_put(si,(c== ND_CHAR ? ' ':c)))
			screen_sw[WS].flags &= ~CONSDEV_LOG; /* error - stop logging */
#endif /* !MINIROOT */
	}
	pos_cursor(ibmes->screen_x, ibmes->screen_y);	/* put cursor on right spot */
	if (ibmes->put_status_offset) {
		register int s;
#ifdef ATR 
		s = spl5();
#else
		s = spl1();
#endif ATR
		ibme_dump_status (si, ibmes->put_status_offset, &status_line[ibmes->put_status_offset]);
		ibmes->put_status_offset = 0;
		splx(s);
	}

	ei->flag &= ~IBME_INPUTC;
}

LOCAL
screen_clear(si)
register SCREEN_INFO *si;
{
	register struct ibme_softc *ibmes = &ibme_softc[WS];

	screen_blank(NORMAL_VIDEO, 0, 0, ibmes->screen_length-1,
	 SCREEN_WIDTH-1,ibmes->fg,ibmes->bg);
	ibmes->screen_x = ibmes->screen_y = 0;
}


LOCAL
screen_out(si, c, kern_flag)
register SCREEN_INFO *si;
register c;
register kern_flag;
{
	register struct ibme_softc *ibmes = &ibme_softc[WS];

	/*
	 * 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, ibmes->screen_attr,((kern_flag) ? ibmes->red: ibmes->fg)
								 , ibmes->bg);
	if (++ibmes->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;
{
	register struct ibme_softc *ibmes = &ibme_softc[WS];

EDEBUG (0x02, aed_pr("screen_lf\n"));
	if (++ibmes->screen_y >= ibmes->screen_length) {
		if (ibmes->monopagemode & PAGEMODE)
			ibmes->screen_y = 0;
		else {
			ibmes->screen_y = ibmes->screen_length - 1;
			/* move the lines */
			screen_move(1, ibmes->screen_length - 1, 0);
			b_line(ibmes->screen_length - 1, ibmes->fg, ibmes->bg);
		}
	}
	if (ibmes->lf_ibme_delay)
		delay(ibmes->lf_ibme_delay);	/* hard_delay for readability */
EDEBUG (0x02, aed_pr("screen_lf leave\n"));
}


LOCAL
screen_index(si)
register SCREEN_INFO *si;
{
	register struct ibme_softc *ibmes = &ibme_softc[WS];

	if (--ibmes->screen_y < 0) {
		screen_move(0, ibmes->screen_length - 2, 1); /* move the lines */
		b_line(0, ibmes->fg, ibmes->bg);
		ibmes->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;
{
	register struct ibme_softc *ibmes = &ibme_softc[WS];

EDEBUG (0x02, aed_pr("screen_cr\n"));
	if (ibmes->monopagemode & PAGEEOLCLR)
		screen_blank(NORMAL_VIDEO, ibmes->screen_y, ibmes->screen_x,			 ibmes->screen_y, SCREEN_WIDTH - 1, ibmes->fg, ibmes->bg);
	ibmes->screen_x = 0;
EDEBUG (0x02, aed_pr("screen_cr leave\n"));
}


LOCAL
screen_bs(si)
register SCREEN_INFO *si;
{
	register struct ibme_softc *ibmes = &ibme_softc[WS];

	if (--ibmes->screen_x < 0) {
		ibmes->screen_x = SCREEN_WIDTH - 1;
		if (--ibmes->screen_y < 0)
			ibmes->screen_y = ibmes->screen_length - 1;
	}
}


LOCAL
screen_tab(si)
register SCREEN_INFO *si;
{
	register struct ibme_softc *ibmes = &ibme_softc[WS];

	register int n = 8 - (ibmes->screen_x & 07);

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

ibme_put_status(si, offset, string)
register SCREEN_INFO *si;
register char *string;
{
	register struct ibme_softc *ibmes = &ibme_softc[WS];

	/* Output Emulator Information */
	register EMUL_INFO *ei = &si->oute;

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

	if (ibmes->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 ((ibmes->put_status_offset == 0) || (offset < ibmes->put_status_offset))
			ibmes->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 struct ibme_softc *ibmes = &ibme_softc[WS];
	register int x, y;
	x = ibmes->screen_x, y = ibmes->screen_y;

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

	ibmes->screen_y = STATUS_LINE;
	ibmes->screen_x = offset;
	while (*string) {
		if (ibmes->screen_x >= SCREEN_WIDTH - 1)
			break;
		pos_cursor(offset++, STATUS_LINE);
		screen_out(si, *string++,0);
	}
	ibmes->screen_x = x, ibmes->screen_y = y;
	pos_cursor(ibmes->screen_x, ibmes->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);

}

ibme_save(tp,si)
register struct tty *tp;
register SCREEN_INFO *si;
{
	return(screen_save());
}

ibme_restore(tp,si)
register struct tty *tp;
register SCREEN_INFO *si;
{
	return(screen_restore());
}
#endif NEED IBMEMUL
