/*
 *  TEX Device Driver  ver 2.02-
 *  copyright(c) 1988,89 by TSG, 1990-93 SHIMA
 *
 *  PREVIEWER ver 2.02-
 *  device.c : previewer module
 *             4th edition
 *
 *	very slightly modified against warnings by Oh-Yeah? 25 May 1992
 *  support EMS by SHIMA, Dec 1992
 *
 *  Of course, this module is device-dependent.
 */

#define	TITLE_COMMENT	""

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define _DEF_STDIO_H_
#include <dos.h>
#include <mem.h>
#include <alloc.h>
#include <conio.h>
#define _DEF_DOS_H_
#include "dd.h"

#define ONEBYTE     8
#define G_TOP       0xa8000000L
#define	G_SEG		0xa800
#define	G_ID		0xafff0008L
#define	T_ID		0xa1000008L
#define G_WIDTH_B   80
#define G_HEIGHT    400
#define HIGH_MONO   2
#define ON          1
#define OFF         0
#define ESC         0x1b
#define	V_SHIFT		1

#define	gvram0()	outp( 0xa6, 0 )	/* write GVRAM0 */
#define	gvram1()	outp( 0xa6, 1 )	/* write GVRAM1 */

#define	new_width	((f_use_new_size_option)?dim->text_width:s_max_width)
#define	new_height	((f_use_new_size_option)?dim->text_height:s_max_height)

#define GRAM_BUF    uchar far

int f_machine_unique;
const char *const title_comment = TITLE_COMMENT;
extern int resume_info;
extern int unit_pages;
extern int slow_fact;
extern int slow_fact2;
extern int slow_page;
extern int v_shift0;
extern int fore_color;
extern int back_color;
extern BOOL normal_16color;
extern BOOL f_reverse;
extern BOOL f_resume;
extern BOOL f_use_new_size_option;
static int v_shift1 = 1;

static int GDC5M;

#define	MAX_VIEW	16

extern int num_view_buf;
extern int num_view;
#ifndef	NEMS
int view_EMS_top;
int view_EMS_unit;
extern int system_EMS_end;
int set_ems( int );
#endif
struct VIEW {
	int current;	/* current screen to be saved	*/
	int total;	/* total screen saved			*/
	int page[MAX_VIEW];	/* page numbers for screens		*/
};
static struct VIEW view;

struct SCROLL {
	/* output parameter. send to scroll-functions and other device-output-
     * functions
     */
	uint offset;
	/* skipped bytes due to horizontal scroll */
	int r_line;
	/* number of lines of the second part of screen */
	int x;
	int y;
	/* the current positioin in bitmap. */
	int gw_act, gh_act;
	/* width and height to output on screen */
	BOOL x_lock, y_lock;
	/* permition to scroll horizontal and vertical direction. */
	HUGE_BUF *mptr;
	/* the current position of bitmap pointer */
	int h_spc, v_spc;
	/* h_spc and v_spc is to put at the center of the screen */

};

typedef enum {
	BackSpace, SpaceKey,	/* PreviousPart, NextPart   */
	Key_P, Key_N,	/* PreviousPage, NextPage   */
	CaridgeReturn,	/* NextPageDirection        */
	EscapeKey,	/* EscapeQuit               */
	Key_Q,	/* Same as above            */
	Key_B,	/* Another Previous Page	*/
	Key_V,	/* View outline				*/
	Key_C,	/* Comressed View			*/
	Key_R,	/* Reverse					*/
	Key_H,	/* HELP 					*/
	HELP,	/* Help key					*/
	Key_M,	/* Move to a page			*/
	Key_G,	/* Same as above			*/
	ArrowDown, ArrowUp, ArrowLeft, ArrowRight,
	LoopEnd, ShiftKey,
	ShiftDown, ShiftUp, ShiftLeft, ShiftRight
} KeyInput;

static unsigned int key_data[] =
{
	0x0140, 0x0610,
	0x0302, 0x0540,
	0x0310,
	0x0001,
	0x0201,
	0x0520,
	0x0510,
	0x0508,
	0x0208,
	0x0404,
	0x0780,
	0x0580,
	0x0402,
	0x0720, 0x0704, 0x0708, 0x0710,
	0x0000, 0x0e01
};

int s_max_width = 118 * 8;
int s_max_height = 118 * 11;
int f_center;

static HUGE_BUF *map_top_ptr;

/* pointer of bitmap buffer */
static PIXEL vmax, hmax, hmaxb;

/* size to output. hmaxb is byte-size. */
static GRAM_BUF *g_top_ptr;

/* head pointer of graphic vram */
static int f_div;
static int f_div2;
static int f_box;
static int v_x_shift;
static int v_x_shift2;
static int v_y_shift;
static int v_y_shift2;

/* static int	view_page; */
static BUFFER *v_top;
static BUFFER *v_top2;
static HUGE_BUF *view_buf;
long view_buf_size = (long)G_WIDTH_B * (long)G_HEIGHT / 2;	/* 0x3E80L */

int x_shift;
int y_shift;

/* Proto-types */

/*	buffer.c */
void clear_buf(BUF_INFO *);
ulong leftbuffer(void);

void device_init(DIMENSION *);
void device_end(void);

/* (in fact, DIMENSION information is given) */
void device_cont(void);
void device_pause(void);
void device_clear(OUTPUT_INFO *);
void pr_new_page(void);
NEXT_ACTION device_out(OUTPUT_INFO *, DIMENSION *);

/* (in fact, OUTPUT_INFO information is given also to this) */
static KeyInput extra_inkey(void);
static void first_draw(struct SCROLL *, int, int);
static void draw_screen(struct SCROLL *);
static void move_screen(struct SCROLL *, KeyInput);
static void scroll_up(struct SCROLL *);
static void scroll_down(struct SCROLL *);
static void scroll_right(struct SCROLL *);
static void scroll_left(struct SCROLL *);
static void flskey(void);
static void clear_text_screen(void);
static void program_screen_mode(int);
static void cursor_mode(int);

/* static void	line1to2( struct SCROLL *, int ); */
/* static void	line2to1( struct SCROLL *, int ); */
static void s_up(struct SCROLL *, int);
static void s_down(struct SCROLL *, int);
static void s_left(struct SCROLL *);
static void s_right(struct SCROLL *);
static void gdc_scroll(struct SCROLL *);
static void gshift(uint, uint, uint);
static void o_port(int, int);
static void cls(int);
static void pallet(int);
static void s_view4(void);
static void s_view8(void);
static void v_box(BUFFER *, int, int, int, int);
static void msg_help(void);
static int  get_view(int, int);
static void reversescreen(int);

/*
 * ２つに分けたＧＶＲＡＭの行の移動：使っていない
 *
static void	line1to2( struct SCROLL *s_dat, int line )
{
	s_dat->offset -= line*G_WIDTH_B;
	s_dat->r_line += line;
	movedata( G_SEG, s_dat->offset + G_HEIGHT*G_WIDTH_B,
		      G_SEG, s_dat->offset,
		      line*G_WIDTH_B );
}

static void	line2to1( struct SCROLL *s_dat, int line )
{
	movedata( G_SEG, s_dat->offset,
		      G_SEG, s_dat->offset + G_HEIGHT*G_WIDTH_B,
		      line*G_WIDTH_B );
	s_dat->offset += line*G_WIDTH_B;
	s_dat->r_line -= line;
}

*/

static void s_up(struct SCROLL *s_dat, int v_shift)
	/* スムース・スクロール・アップ by v_shift dots */
{
	int line1, i, add;
	HUGE_BUF *mptr_tmp;
	GRAM_BUF *gptr_tmp;

	line1 = G_HEIGHT - s_dat->r_line;
	mptr_tmp = s_dat->mptr + (ulong)hmaxb *(G_HEIGHT - v_shift);

	gptr_tmp = g_top_ptr + s_dat->offset + G_WIDTH_B * s_dat->r_line;

	if (line1 > v_shift) {
		add = v_shift;
		s_dat->r_line += v_shift;
	}
	else {
		s_dat->r_line = v_shift - (add = line1);
	}

	for (i = 0; i < add; i++) {
		movedata(FP_SEG(mptr_tmp), FP_OFF(mptr_tmp),
				 FP_SEG(gptr_tmp), FP_OFF(gptr_tmp), s_dat->gw_act);
		mptr_tmp += hmaxb;
		gptr_tmp += G_WIDTH_B;
	}

	gptr_tmp = g_top_ptr + s_dat->offset;
	for (i = v_shift - add; i > 0; i--) {
		movedata(FP_SEG(mptr_tmp), FP_OFF(mptr_tmp),
				 FP_SEG(gptr_tmp), FP_OFF(gptr_tmp), s_dat->gw_act);
		mptr_tmp += hmaxb;
		gptr_tmp += G_WIDTH_B;
	}
	gdc_scroll(s_dat);
}

static void s_down(struct SCROLL *s_dat, int v_shift)
	/* スムース・スクロール・ダウン by v_shift dots */
{
	int ins, i;
	HUGE_BUF *mptr_tmp;
	GRAM_BUF *gptr_tmp;

	mptr_tmp = s_dat->mptr;
	if (s_dat->r_line >= v_shift) {
		ins = v_shift;
		s_dat->r_line -= v_shift;
		gptr_tmp = g_top_ptr + s_dat->offset + G_WIDTH_B * s_dat->r_line;
	}
	else {
		ins = s_dat->r_line;
		s_dat->r_line = G_HEIGHT + s_dat->r_line - v_shift;
		gptr_tmp = g_top_ptr + s_dat->offset + G_WIDTH_B * s_dat->r_line;
		for (i = v_shift - ins; i > 0; i--) {
			movedata(FP_SEG(mptr_tmp), FP_OFF(mptr_tmp),
					 FP_SEG(gptr_tmp), FP_OFF(gptr_tmp), s_dat->gw_act);
			mptr_tmp += hmaxb;
			gptr_tmp += G_WIDTH_B;
		}
		gptr_tmp = g_top_ptr + s_dat->offset;
	}

	for (i = 0; i < ins; i++) {
		movedata(FP_SEG(mptr_tmp), FP_OFF(mptr_tmp),
				 FP_SEG(gptr_tmp), FP_OFF(gptr_tmp), s_dat->gw_act);
		mptr_tmp += hmaxb;
		gptr_tmp += G_WIDTH_B;
	}
	gdc_scroll(s_dat);
}

static void s_right(struct SCROLL *s_dat)
	/* 左方向　スムース・スクロール by 16 dots */
{
	HUGE_BUF *mptr_tmp;
	GRAM_BUF *gptr_tmp;
	int i, count;

	/*	if( s_dat->offset <= 0 )	line2to1( s_dat, 1 ); */
	s_dat->offset -= 2;

	mptr_tmp = s_dat->mptr;
	gptr_tmp = (GRAM_BUF *) G_TOP + s_dat->offset + G_WIDTH_B * s_dat->r_line;
	count = 0;
	gdc_scroll(s_dat);
	for (i = G_HEIGHT - s_dat->r_line; i > 0; i--) {
		if (count++ < s_dat->v_spc || count > s_dat->v_spc + s_dat->gh_act)
			*((int far *)gptr_tmp) = 0;
		else {
			*((int far *)gptr_tmp) = *((int far *)mptr_tmp);
			mptr_tmp += hmaxb;
		}
		gptr_tmp += G_WIDTH_B;
	}
	gptr_tmp = (GRAM_BUF *) G_TOP + s_dat->offset;
	for (i = s_dat->r_line; i > 0; i--) {
		/*		if( count++ < s_dat->v_spc || count > s_dat->v_spc + s_dat->gh_act )
			*((int far*)gptr_tmp) = 0;
		else{														*/
		*((int far *)gptr_tmp) = *((int far *)mptr_tmp);
		mptr_tmp += hmaxb;
		/*		}															*/
		gptr_tmp += G_WIDTH_B;
	}
}

static void s_left(struct SCROLL *s_dat)
	/* 右方向　スムース・スクロール by 16 dots */
{
	HUGE_BUF *mptr_tmp;
	GRAM_BUF *gptr_tmp;
	int i, count;

	s_dat->offset += 2;

	/*	if( ++(s_dat->offset) >= G_WIDTH_B ) line1to2( s_dat, 1 ); */

	mptr_tmp = s_dat->mptr + (G_WIDTH_B - 2);
	gptr_tmp = (GRAM_BUF *) G_TOP + s_dat->offset
		+ G_WIDTH_B * s_dat->r_line + (G_WIDTH_B - 2);
	count = 0;
	for (i = G_HEIGHT - s_dat->r_line; i > 0; i--) {
		if (count++ < s_dat->v_spc || count > s_dat->v_spc + s_dat->gh_act)
			*((int far *)gptr_tmp) = 0;
		else {
			*((int far *)gptr_tmp) = *((int far *)mptr_tmp);
			mptr_tmp += hmaxb;
		}
		gptr_tmp += G_WIDTH_B;
	}

	gdc_scroll(s_dat);
	gptr_tmp = (GRAM_BUF *) G_TOP + s_dat->offset + (G_WIDTH_B - 2);
	for (i = s_dat->r_line; i > 0; i--) {
		/*		if( count++ < s_dat->v_spc || count > s_dat->v_spc + s_dat->gh_act )
			*((int far*)gptr_tmp) = 0;
		else{															*/
		*((int far *)gptr_tmp) = *((int far *)mptr_tmp);
		mptr_tmp += hmaxb;
		/*		}																*/
		gptr_tmp += G_WIDTH_B;
	}
}

static void gdc_scroll(struct SCROLL *s_dat)
	/* ２分割のＧＶＲＡＭの表示 */
{
	gshift((s_dat->offset + G_WIDTH_B * s_dat->r_line) >> 1,
		   s_dat->offset >> 1, G_HEIGHT - s_dat->r_line);
}

static void gshift(uint adr_1, uint adr_2, uint ln)
	/* ＧＤＣ　スクロール・コマンド */
{
	o_port(0xa2, 0x70);
	o_port(0xa0, adr_1 & 0xff);
	o_port(0xa0, (adr_1 >> 8) & 0xff);
	o_port(0xa0, (ln & 0xf) << 4);
	o_port(0xa0, (ln >> 4) | GDC5M);
	o_port(0xa0, adr_2 & 0xff);
	o_port(0xa0, adr_2 >> 8);
	o_port(0xa0, ((400 - ln) & 0xf) << 4);
	o_port(0xa0, ((400 - ln) >> 4) | GDC5M);
}

static void o_port(int out_p, int data)
	/* ＧＤＣ へ、コマンドやパラメータを送る */
{
	while ((inp(0xa0) & 0x02) != 0);
	(void)outp(out_p, data);
}

void pr_new_page()
{
}

static KeyInput
    extra_inkey(void)
	/* realtime key scanning. waiting until one of KeyInput is pressed.
     * and if the key is a cursor-key, I check at the same time whether Shift-
     * key is hit or not.
     */
{
#if 1
	const int intno = 0x18;
	int num = 0;
	int f_num = 0;
	union REGS inregs, inregs2, outregs;
	KeyInput key_d;
	static char code[] = "\b pn\r\x1b" "qbvcrh mg";
	char *p;

	inregs.h.ah = 0x04;
	inregs2.h.ah = 0x00;
	while (TRUE) {
		for (key_d = ArrowDown; key_d != LoopEnd; (int)key_d++) {
			inregs.h.al = (uchar)(key_data[(int)key_d] >> 8);
			int86(intno, &inregs, &outregs);
			if (outregs.h.ah & (uchar)key_data[(int)key_d]) {
				inregs.h.al = (uchar)(key_data[(int)ShiftKey] >> 8);
				int86(intno, &inregs, &outregs);
				if (outregs.h.ah & (uchar)key_data[(int)ShiftKey])
					key_d = (KeyInput) ((int)key_d - (int)ArrowDown +
										(int)ShiftDown);
				return key_d;
			}
		}
		flskey();
		int86(intno, &inregs2, &outregs);
		if (outregs.h.al >= '0' && outregs.h.al <= '9') {
			f_num = 1;
			num -= (num / 100) * 100;
			num = num * 10 + outregs.h.al - '0';
			if (num >= 1)
				unit_pages = num;
		} else {
			p = strchr(code, tolower(outregs.h.al));
			if (p && *p) {
				key_d = (KeyInput)(p - code);
				if (key_d == Key_G)
					key_d = Key_M;
				else if (key_d == Key_Q)
					key_d = EscapeKey;
				else if (key_d == Key_B)
					key_d = Key_P;
				else if (key_d == Key_M && f_num == 0)
					continue;
				return key_d;
			} else if (outregs.h.ah == 0x3f) {	/* scan code == HELP key */
				return HELP;
			}
			f_num = num = 0;
		}
	}
#else
	const int intno = 0x18;
	int num = 0;
	int f_num = 0;
	union REGS inregs, inregs2, outregs;
	KeyInput key_d;

	inregs.h.ah = 0x04;
	inregs2.h.ah = 0x01;
	key_d = (KeyInput) 0;

resume:
	while (TRUE) {
		int86(intno, &inregs2, &outregs);
		if (outregs.h.bh > 0) {
			if (outregs.h.al >= '0' && outregs.h.al <= '9') {
				f_num = 1;
				num -= (num / 100) * 100;
				num = num * 10 + outregs.h.al - '0';
				if (num >= 1)
					unit_pages = num;
			}
			flskey();
		}
		inregs.h.al = (uchar)(key_data[(int)key_d] >> 8);
		int86(intno, &inregs, &outregs);
		if (outregs.h.ah & (uchar)key_data[(int)key_d])
			break;
		if ((KeyInput) (((int)key_d)++) == LoopEnd)
			key_d = (KeyInput) 0;
	}
	/* check Shift+CursorKey ... */
	if ((int)key_d >= (int)ArrowDown && (int)key_d <= (int)ArrowRight) {
		inregs.h.al = (uchar)(key_data[(int)ShiftKey] >> 8);
		int86(intno, &inregs, &outregs);
		if (outregs.h.ah & (uchar)key_data[(int)ShiftKey])
			key_d = (KeyInput) ((int)key_d - (int)ArrowDown +
								(int)ShiftDown);
	}
	else {
		if (key_d == Key_G)
			key_d = Key_M;
		else if (key_d == Key_B)
			key_d = Key_P;
		else if (key_d == Key_Q)
			key_d = EscapeKey;
		else if (key_d == Key_M && f_num == 0)
			goto resume;
	}
	return (key_d);
#endif
}

void device_pause()
{
	union REGS regs;

	regs.h.ah = 0x41;
	int86(0x18, &regs, &regs);	/* graphic OFF */
}

void device_cont()
{
	union REGS regs;

	regs.h.ah = 0x40;
	int86(0x18, &regs, &regs);	/* graphic ON */
}

void device_init(DIMENSION *dim)
	/* initialize graphic screen */
{
	union REGS regs;
	uint far *gvram_last = (uint far *)G_ID;
	uint x, x2, y, y2;

	if (v_shift0 < 0 ){
		if((v_shift1 = - v_shift0) <= 1) v_shift1 = 1;
		if(v_shift1 > 16) v_shift1 = 16;
		v_shift0 = 0;
	}
	if (f_resume) {
		if (*gvram_last-- == (((uint)'P') << 8) + 'D'
		  && *(gvram_last-1) == resume_info) {
			dim->start_page = *gvram_last;
		}
		else {
			if (*((uint far *)(T_ID + 2)) == (((uint)'O') << 8) + 'D'
		 	  && *((uint far *)(T_ID - 2)) == resume_info) {
				dim->start_page = *((uint far *)T_ID);
			}
		}
	}
	GDC5M = ((inp(0x31) & 0x80) == 0) ? 0x40 : 0;

	if (num_view == -0x7fff) {			/* check 2nd GVRAM */
		x = *((uint far *)G_TOP);
		(void)gvram1();
		*((uint far *)G_TOP) = x + 1;
		(void)gvram0();
		if (x != *((uint far *)G_TOP))
			num_view = 0;
	}
	cls(0);
	(void)outp(0xa4, 0);				/* display GVRAM0 */
	(void)outp(0x6a, (normal_16color)?1:0); /* 16:8 color mode */
	regs.h.ah = 0x42;
	/*	regs.h.ch = 0xe0; */
	regs.h.ch = 0xc0;
	int86(0x18, &regs, &regs);	/* 400 W/B line mode */

	reversescreen(f_reverse);	/* 1-st screen */
	device_cont();

	if (dim->split == 1) {
		x = x_shift + dim->text_width;
		if (x < s_max_width)
			x = s_max_width;
		if (x_shift < 0)
			x -= x_shift;

		y = y_shift + dim->text_height;
		if (y < s_max_height)
			y = s_max_height;
		if (y_shift < 0)
			y -= y_shift;
	}
	else
		return;

	num_view_buf = (int)((leftbuffer() - 0x80L) / view_buf_size);

	if (x < (G_WIDTH_B - 12) * 32 && y < (G_HEIGHT - 4) * 4) {
		f_div = 4;
	}
	else if (x < (G_WIDTH_B - 12) * 64 && y < (G_HEIGHT - 4) * 8) {
		f_div = 8;
	}
	if (f_div > 0 && x > (G_WIDTH_B - 6) * 4 * f_div) {
		if (f_div == 4)
			f_div2 = 8;
	}
	else
		f_div2 = f_div;

	if (!f_use_new_size_option && s_max_height > 0 && s_max_width > 0)
		f_box = 1;

	if (num_view_buf > MAX_VIEW)
		num_view_buf = MAX_VIEW;
	if( num_view == 0 ) num_view = num_view_buf;
	if (f_div) {
		v_x_shift = x_shift >> 2;
		v_x_shift2 = x_shift >> 3;
		v_y_shift = y_shift >> 2;
		v_y_shift2 = y_shift >> 3;
		x = x2 = 5;
		y = y2 = 2;
		if (v_x_shift > 0) {
			x += (v_x_shift >> 3);
			x2 += (v_x_shift2 >> 3);
		}
		if (v_y_shift > 0) {
			y += v_y_shift;
			y2 += v_y_shift2;
		}
		v_top = (BUFFER *)G_TOP + x + (G_WIDTH_B * y);
		v_top2 = (BUFFER *)G_TOP + x2 + (G_WIDTH_B * y2);
		if (!f_div2) goto nv;
#ifndef	NEMS
		if (num_view < 0 && num_view >= -MAX_VIEW){
			num_view_buf = -num_view;
			view_EMS_top = system_EMS_end;
			max_user_page -= view_EMS_unit*num_view_buf;
			system_EMS_end += view_EMS_unit*num_view_buf;
			view_buf = pf_addr;
			return;
		}
#endif
		if (num_view_buf >= 6 || (num_view_buf > 0 && num_view > 0)) {
			view_buf =
				(HUGE_BUF *)farmalloc(view_buf_size * (num_view = num_view_buf));
		}
		else if (num_view < 0) {
			view_buf = (HUGE_BUF *)G_TOP;
			num_view_buf = 6;
		}
		else if (num_view == 0)
			f_div = 0;
	}
	else
nv:		num_view_buf = num_view = 0;
}

static void cls(int mode)
{
	static BUF_INFO vram =
	{
		(BUFFER *)0xa8000000L,
		(BUFFER *)0xa8000000L,
		(BUFFER *)0xa8007fffL,
		0x7ff0L, 0
	};
	static BUF_INFO vram1 =
	{
		(BUFFER *)0xb0000000L,
		(BUFFER *)0xb0000000L,
		(BUFFER *)0xb000ffffL,
		0xfffeL, 0
	};

	clear_buf(&vram);
	if (mode != 0) {
		clear_buf(&vram1);
		if (num_view >= -MAX_VIEW ) {
			(void)gvram1();
			clear_buf(&vram);
			clear_buf(&vram1);
			(void)gvram0();
		}
	}
}

static void pallet(int mode)
{
	union REGS regs;

	static char preg[24] =
	{0, 0, 0, 0, 0x07, 0x07, 0x07, 0x07,
	 0, 0, 0, 0, 0x70, 0x70, 0x70, 0x70,
	 0, 0, 0, 0, 0x67, 0x45, 0x23, 0x01};

	regs.h.ah = 0x43;
	regs.x.bx = FP_OFF(&(preg[mode]));
	int86(0x18, &regs, &regs);	/* 1-st screen */
}

static void a_palette(uint num, uint color)
{
	(void)outp (0xa8, (uchar)num);
	(void)outp (0xaa, (uchar)(color >> 8));
	(void)outp (0xac, (uchar)((color & 0xf0) >> 4));
	(void)outp (0xae, (uchar)(color & 0xf));
}
static void init_a_palette()
{
	int i;
	static uint apreg[] =
	{0x000, 0x008, 0x080, 0x088, 0x800, 0x808, 0x880, 0x888,
	0x000, 0x00f, 0x0f0, 0x0ff, 0xf00, 0xf0f, 0xff0, 0xfff};
	for(i=0; i<16; i++)
		a_palette(i, apreg[i]);
}
static void set_a_palette_m(uint plane, uint fc, uint bc)
/* plane : 0〜3, fc/bc : color */
{
	int i;
	plane = (1 << plane);
	for(i=0; i<16; i++)
		a_palette( i, (i & plane) ? fc : bc);
}

static void reversescreen(int mode)
{
	uint fc, bc;

	if (normal_16color) {
		fc = (mode) ? fore_color : 0xfff;
		bc = (mode) ? back_color : 0x0;
		set_a_palette_m(0, fc, bc);
	} else {
		pallet((mode == TRUE) ? 8 : 0);
	}
}

void device_end()
	/* erase graphic screen */
{
	union REGS regs;

	*(((uchar far *)(G_ID)) + 1) = 'P';

	device_pause();
	if (normal_16color) {
		reversescreen(0);
	} else {
		pallet(16);
	}

	if (f_machine_unique == 8) {
		(void)outp(0x6a, 0);              /* 8 color mode */
	} else if (f_machine_unique == 16 || (inp(0x42) & 8) == 0) {
		(void)outp(0x6a, 1);              /* 16 color mode */
	}
	regs.h.ah = 0x42;
	regs.h.ch = 0xc0;
	int86(0x18, &regs, &regs);	/* 400 Color line mode */

	cls(1);
	gshift(0, 0, 400);
	clear_text_screen();
	program_screen_mode(OFF);
	cursor_mode(ON);
}

void device_clear(OUTPUT_INFO *out)
	/* clear screen and initialize variables */
{
	/* intialize variables */
	map_top_ptr = out->bitmap_ptr;
	hmax = out->width;
	vmax = out->height;
	hmaxb = out->byte_width;

	/* clear screen */
	if (out->split > 1)
		cls(0);
	program_screen_mode(ON);
	clear_text_screen();
	cursor_mode(OFF);
}

static void s_view8(void)
{
	int i, j, vm, v;
	uint ch;
	HUGE_BUF *ptr0;
	BUFFER *ptr1;
	BUFFER *ptr;
	BUFFER *ptrg0;
	BUFFER *ptrg;

	ptrg0 = v_top2;
	ptr0 = map_top_ptr;

	for (ch = v = 0; v < vmax; v += 8) {
		vm = ((vmax - v) < 8) ? vmax - v : 8;
		ptr1 = (BUFFER *)ptr0;
		ptr0 += (hmaxb << 3);
		ptrg = ptrg0;
		ptrg0 += G_WIDTH_B;
		for (i = 0; i < hmaxb; i++) {
			ptr = ptr1++;
			for (j = vm; j != 0; j--) {
				if (*ptr != 0) {
					ch |= 1;
					break;
				}
				ptr += hmaxb;
			}
			if ((i & 7) == 7)
				*ptrg++ = ch;
			ch += ch;
		}
		if ((i = (hmaxb & 7)) != 0)
			*ptrg++ = (ch << (7 - i));
	}
#if	1
	if (f_box)
		v_box(v_top2, -v_x_shift2, -v_y_shift2,
				  s_max_width / 8, s_max_height / 8);
#else
	if (f_box)
		v_box(v_top2, -v_x_shift2, -v_y_shift2,
				  new_width / 8, new_height / 8);
#endif
}

static void s_view4(void)
{
	int i, j, vm, v;
	uint ch;
	HUGE_BUF *ptr0;
	BUFFER *ptr1;
	BUFFER *ptr;
	BUFFER *ptrg0;
	BUFFER *ptrg;

	ptrg0 = v_top;
	ptr0 = map_top_ptr;

	for (ch = v = 0; v < vmax; v += 4) {
		vm = ((vmax - v) < 4) ? vmax - v : 4;
		ptr1 = (BUFFER *)ptr0;
		ptr0 += (hmaxb << 2);
		ptrg = ptrg0;
		ptrg0 += G_WIDTH_B;
		for (i = 0; i < hmaxb; i++) {
			ptr = ptr1++;
			for (j = vm; j != 0; j--) {
				if ((*ptr & 0xf0) != 0) {
					ch |= 2;
				}
				if ((*ptr & 0x0f) != 0) {
					ch |= 1;
				}
				if ((ch & 3) == 3)
					break;
				ptr += hmaxb;
			}
			if ((i & 3) == 3)
				*ptrg++ = ch;
			ch <<= 2;
		}
		if ((i = (hmaxb & 3)) != 0)
			*ptrg++ = (ch << ((3 - i) * 2));
	}
#if	1
	if (f_box)
		v_box(v_top, -v_x_shift, -v_y_shift,
			  s_max_width / 4, s_max_height / 4);
#else
	if (f_box)
		v_box(v_top, -v_x_shift, -v_y_shift,
			  new_width / 4, new_height / 4);
#endif
}

static void v_box(BUFFER *orig, int x, int y, int w, int h)
{
	BUFFER *ptr;
	int i, b, ch, dif;

	orig += (x + G_WIDTH_B * 8) / 8 + G_WIDTH_B * (y - 1);
	b = (x & 7);

	ch = (1 << (7 - b));
	ptr = orig;
	for (i = h; i > 0; ptr += G_WIDTH_B, i--)
		*ptr |= ch;

	dif = G_WIDTH_B * (h - 1);
	ch = (0xff >> b);
	ptr = orig;
	*(ptr + dif) |= ch;
	*ptr |= ch;

	ptr++;
	for (i = w + b - 8; i > 8; ptr++, i -= 8) {
		*ptr |= 0xff;
		*(ptr + dif) |= 0xff;
	}
	ch = (0xff << (8 - i));
	*ptr |= ch;
	*(ptr + dif) |= ch;

	ch = (1 << (8 - i));
	for (i = h; i > 0; ptr += G_WIDTH_B, i--)
		*ptr |= ch;
}

static void msg_help()
{
	cls(0);

	if (f_reverse) {
		reversescreen(0);
	}
	fprintf(stderr,
			"\x1b" "[5;H"
			"\t\t\t\t<<< Help >>>\n\n"
			"\t(SHIFT + ) → ← ↑ ↓\t: Scroll in a page\n"
			"\tN\t\t\t: Next page\n"
			"\tP, B\t\t\t: Previous page\n"
			"\tG, M\t\t\t: Move to a page\n"
			"\tnumbers\t\t\t: pages (skipped) by next B, G, M, N, P\n"
			"\tSpace\t\t\t: Next part\n"
			"\tBack Space\t\t: Previous part\n"
			"\tC, V\t\t\t: (Compressed) View outline\n"
			"\tR\t\t\t: Reverse Screen(toggle)\n"
			"\tH, Help\t\t\t: Help message\n"
			"\tReturn\t\t\t: Next block or quit\n"
			"\tQ, Escape\t\t: Quit\n"
		);

	getch();
	if (f_reverse) {
		reversescreen(1);
	}
	fprintf(stderr, "\x1b" "[5;H" "\x1b" "[0J" "\x1e");

}

void slow(int fact)
{
	int	  i, j;

	i = (fact<6)?5:fact;
	while(fact-- > 0){
		for(j = 0; j < 6*i; j++) slow( 0 );
	}
}

NEXT_ACTION device_out(OUTPUT_INFO *out, DIMENSION *dim)
{
	static struct SCROLL s_dat;
	KeyInput key;
	int i, j;

	s_dat.mptr = map_top_ptr;

	first_draw(&s_dat, out->page, dim->split);
	slow(slow_page);

	for (;;) {
		switch (key = extra_inkey()) {
		  case Key_V:
		  case Key_C:
			  if (!f_div)
				  break;
			  cls(0);
			  gshift(0, 0, G_HEIGHT);
			  s_dat.r_line = 0;
			  i = s_dat.offset;
			  s_dat.offset = 0;
			  if (f_div == 4 && key != Key_C) {
				  s_view4();
				  j = get_view(out->page, 4);
			  }
			  else {
				  s_view8();
				  j = get_view(out->page, 8);
			  }
			  s_dat.offset = i;
			  cls(0);
			  if (j) goto disp_m;
disp_0:		  draw_screen(&s_dat);
			  slow(slow_page);
			  break;
		  case Key_R:
			  f_reverse = (f_reverse == TRUE) ? FALSE : TRUE;
			  reversescreen(f_reverse);
			  while (kbhit())
				  getch();
			  goto disp2;
		  case Key_H:
		  case HELP:
			  msg_help();
			  goto disp_0;
		  case Key_M:
disp_m:		  unit_pages -= out->page;
			  return ((NEXT_ACTION)Key_N);
		  case ArrowDown:
			  scroll_up(&s_dat);
			  goto sl0;
		  case ArrowLeft:
			  scroll_right(&s_dat);
			  goto sl2;
		  case ArrowRight:
			  scroll_left(&s_dat);
sl2:		  slow(slow_fact2);
			  break;
		  case ArrowUp:
			  scroll_down(&s_dat);
sl0:		  slow(v_shift0);
			  break;
		  case ShiftUp:
		  case ShiftDown:
		  case ShiftLeft:
		  case ShiftRight:
			  move_screen(&s_dat, key);
disp2:		  slow(slow_fact);
			  break;
		  default:
			  return ((NEXT_ACTION)key);
		}
	}
}

static void first_draw(struct SCROLL *s_dat, int page, int split)
	/* at first, set variables and put to screen */
{
	uint far *gvram_last = (uint far *)(G_ID - 4);

	*gvram_last++ = resume_info;
	*gvram_last++ = page;
	*gvram_last++ = (((uint)'O') << 8) + 'D';	/* ID	   */
	*gvram_last++ = 0x239;		/* Version */
	*gvram_last++ = FP_OFF(s_dat);
	*gvram_last = FP_SEG(s_dat);

	if (f_resume) {
		*((uint far *)(T_ID - 2)) = resume_info;
		*((uint far *)T_ID) = page;
		*((uint far *)(T_ID + 2)) = (((uint)'O') << 8) + 'D';	/* ID	   */
	}
	s_dat->x_lock = s_dat->y_lock = FALSE;
	s_dat->offset = 0;

	if (hmaxb < G_WIDTH_B) {
		/* can't scroll horizontally */
		s_dat->gw_act = hmaxb;
		s_dat->x_lock = TRUE;
	}
	else
		s_dat->gw_act = G_WIDTH_B;

	if (vmax < G_HEIGHT) {
		/* can't scroll vertically */
		s_dat->gh_act = vmax;
		s_dat->y_lock = TRUE;
	}
	else
		s_dat->gh_act = G_HEIGHT;

	s_dat->h_spc = (G_WIDTH_B - s_dat->gw_act) / 2;
	s_dat->v_spc = (split==1)?(G_HEIGHT - s_dat->gh_act) / 2:0;
	g_top_ptr = (GRAM_BUF *) G_TOP + s_dat->h_spc + s_dat->v_spc * G_WIDTH_B;

	s_dat->x = -s_dat->h_spc;
	s_dat->y = -s_dat->v_spc;

	draw_screen(s_dat);
}

static void draw_screen(struct SCROLL *s_dat)
	/* put bitmap-image to screen */
{
	int i, j;
	HUGE_BUF *mptr_tmp;
	GRAM_BUF *gptr_tmp;

	gptr_tmp = g_top_ptr;
	if (s_dat->y_lock == TRUE && s_dat->x_lock == FALSE) {
		gptr_tmp = g_top_ptr;
		for (i = s_dat->offset; --i >= 0;)
			gptr_tmp[i] = 0;
		gptr_tmp += (G_WIDTH_B * s_dat->gh_act + s_dat->gw_act);
		j = hmaxb - s_dat->offset - G_WIDTH_B;
		for (i = 0; i < j;)
			gptr_tmp[i++] = 0;
	}
	s_dat->r_line = 0;
	gdc_scroll(s_dat);
	mptr_tmp = s_dat->mptr;
	gptr_tmp = g_top_ptr + s_dat->offset;
	for (i = 0; i < s_dat->gh_act; i++) {
		movedata(FP_SEG(mptr_tmp), FP_OFF(mptr_tmp),
				 FP_SEG(gptr_tmp), FP_OFF(gptr_tmp), s_dat->gw_act);
		gptr_tmp += G_WIDTH_B;
		mptr_tmp += (ulong)hmaxb;
	}
}

static int get_view(page, div)
{
	int i, pt, pg;
	uchar tmp[G_WIDTH_B / 2];
	GRAM_BUF *gptr_tmp;
	HUGE_BUF *buf_tmp;

	pt = view.current;
	pg = -1;
	while (TRUE) {
		if (div != f_div2)
			goto skip;
		if (--pt < 0)
			pt = view.total - 1;
		if (pt >= 0) {
#ifndef	NEMS
			if( view_EMS_unit ){
				buf_tmp = view_buf;
				set_ems( view_EMS_top + view_EMS_unit * pt );
			}
			else
#endif
				buf_tmp = view_buf + view_buf_size * pt;
			gptr_tmp = (GRAM_BUF *) (G_TOP + G_WIDTH_B / 2);
			for (i = G_HEIGHT; i > 0; i--) {
				if (num_view >= -MAX_VIEW) {
					movedata(FP_SEG(buf_tmp), FP_OFF(buf_tmp),
						 FP_SEG(gptr_tmp), FP_OFF(gptr_tmp), G_WIDTH_B / 2);
				}
				else {
					(void)gvram1();
					movedata(FP_SEG(buf_tmp), FP_OFF(buf_tmp),
							 FP_SEG(tmp), FP_OFF(tmp), G_WIDTH_B / 2);
					(void)gvram0();
					movedata(FP_SEG(tmp), FP_OFF(tmp),
						 FP_SEG(gptr_tmp), FP_OFF(gptr_tmp), G_WIDTH_B / 2);
				}
				buf_tmp += G_WIDTH_B / 2;
				gptr_tmp += G_WIDTH_B;
			}
			fprintf(stderr, "\x1b" "[;41H[%d]\t\t",
					pg = view.page[pt]);
		}
skip:	i = (uchar)getch();
		fprintf(stderr, "\x1b" "*[%d]" "\x1b" "[H", page);
		if (i != 'm' && i != 'M' && i != 'ﾓ')
			pg = -1;
		if (i != 'v' && i != 'V' && i != 'ﾋ')
			break;
	}
	fprintf(stderr, "\x1a" "[%d]", page);
	if (div != f_div2)
		return 0;
	for (pt = view.total; pt > 0;) {
		if (view.page[--pt] == page) {
			view.current = pt + 1;
			goto nxt;
		}
	}
	if (view.total < num_view_buf)
		view.current = view.total++;
	if (view.current >= num_view_buf)
		view.current = 0;
	view.page[view.current] = page;

	gptr_tmp = (GRAM_BUF *) G_TOP;
#ifndef	NEMS
	if( view_EMS_unit ){
		buf_tmp = view_buf;	
		set_ems( view_EMS_top + view_EMS_unit*view.current );
	}else
#endif
		buf_tmp = view_buf + view_buf_size * view.current;
	view.current++;
	for (i = G_HEIGHT; i > 0; i--) {
		if (num_view >= -MAX_VIEW ) {
			movedata(FP_SEG(gptr_tmp), FP_OFF(gptr_tmp),
					 FP_SEG(buf_tmp), FP_OFF(buf_tmp), G_WIDTH_B / 2);
		}
		else {
			movedata(FP_SEG(gptr_tmp), FP_OFF(gptr_tmp),
					 FP_SEG(tmp), FP_OFF(tmp), G_WIDTH_B / 2);
			(void)gvram1();
			movedata(FP_SEG(tmp), FP_OFF(tmp),
					 FP_SEG(buf_tmp), FP_OFF(buf_tmp), G_WIDTH_B / 2);
			(void)gvram0();
		}
		buf_tmp += G_WIDTH_B / 2;
		gptr_tmp += G_WIDTH_B;
	}
nxt:
	if (pg >= 0)
		unit_pages = pg;
	return pg+1;
}

static void move_screen(struct SCROLL *s_dat, KeyInput direct)
{
	int x_step, y_step;

	x_step = y_step = 0;
	switch (direct) {
	  case ShiftDown:
		  y_step = (s_dat->gh_act) >> 1;
		  break;
	  case ShiftUp:
		  y_step = -((s_dat->gh_act) >> 1);
		  break;
	  case ShiftLeft:
		  x_step = -(s_dat->gw_act) >> 1;
		  break;
	  case ShiftRight:
		  x_step = (s_dat->gw_act) >> 1;
		  break;
	}

	if (x_step != 0 && !s_dat->x_lock) {
		if (s_dat->x + x_step <= 0)
			x_step = -s_dat->x;
		if (s_dat->x + x_step + G_WIDTH_B >= hmaxb)
			x_step = hmaxb - (s_dat->x + G_WIDTH_B);
		x_step &= 0xfffe;
		s_dat->x += x_step;
		s_dat->mptr += x_step;
		s_dat->offset = s_dat->h_spc + s_dat->x;
		draw_screen(s_dat);
	}
	else if (y_step != 0 && !s_dat->y_lock) {
		if (s_dat->y + y_step <= 0)
			y_step = -s_dat->y;
		if (s_dat->y + y_step + G_HEIGHT >= vmax)
			y_step = vmax - (s_dat->y + G_HEIGHT);
		s_dat->y += y_step;
		s_dat->mptr += (long)y_step *hmaxb;

		draw_screen(s_dat);
	}
}

static void scroll_up(struct SCROLL *s_dat)
{
	int shift;

	if ((shift = vmax - s_dat->y - G_HEIGHT) > 0) {
		if (shift > v_shift1)
			shift = v_shift1;
		s_dat->mptr += hmaxb * shift;
		s_dat->y += shift;
		s_up(s_dat, shift);
	}
}

static void scroll_down(struct SCROLL *s_dat)
{
	int shift;

	if ((shift = s_dat->y) > 0) {
		if (shift > v_shift1)
			shift = v_shift1;
		s_dat->mptr -= hmaxb * shift;
		s_dat->y -= shift;
		s_down(s_dat, shift);
	}
}

static void scroll_right(struct SCROLL *s_dat)
{
	if (s_dat->x > 0) {
		s_dat->mptr -= 2;
		s_dat->x -= 2;
		s_right(s_dat);
	}
}

static void scroll_left(struct SCROLL *s_dat)
{
	if (s_dat->x + G_WIDTH_B < hmaxb) {
		s_dat->mptr += 2;
		s_dat->x += 2;
		s_left(s_dat);
	}
}

static void flskey(void)
	/*  flush key-buffer. by calling dos function */
{
	bdos(12, 0, 0);
}

static void clear_text_screen()
	/* clear text screen */
{
	fprintf(stderr, "\x1b" "*");
}

static void program_screen_mode(int mode)
	/* to use the bottom line for user */
{
	fprintf(stderr, mode ? "\x1b" "[>1h" : "\x1b" "[>1l");
}

static void cursor_mode(int flg)
	/* Cursor on or off */
{
	fprintf(stderr, flg ? "\x1b" "[>5l" : "\x1b" "[>5h");
}

/* end of file : device.c */
