/*
 *  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
 *             8 June 1992 : modified for hires PC-98 by Y.OKI
 *             4th June 1992 : modified for hires PC-98 by Y.OKI
 *  		   Support EMS by SHIMA, Dec 1992
 *
 *  Of course, this module is device-dependent.
 */

#define	TITLE_COMMENT	""

#include <stdio.h>
#include <string.h>
#define _DEF_STDIO_H_
#include <dos.h>
#include <mem.h>
#include <alloc.h>
#include <conio.h>
#include <ctype.h>
#define _DEF_DOS_H_
#include "dd.h"
#include "err.h"

#define ONEBYTE     8
/* FOR NORMAL RESOUTION */
#define G_TOP_N		0xa8000000L
#define	G_SEG_N		0xa800
#define	G_ID_N		0xafff0008L
#define	T_ID_N		0xa1000008L
#define G_WIDTH_B_N	80
#define G_HEIGHT_N	400
/* FOR HIGH RESOUTION */
#define G_TOP_H		0xc0000000L
#define	G_SEG_H		0xc000
#define	G_ID_H		0xdfff0008L
#define	T_ID_H		0xe1000008L
#define G_WIDTH_B_H	140
#define G_HEIGHT_H	750

#define HIGH_MONO   2
#define ON          1
#define OFF         0
#define ESC         0x1b
#define	V_SHIFT		1
#define DEFAULT (0xff)	/* for HIRESO bios */
#define	gvram0()	(void)outp( 0xa6, 0 )	/* write GVRAM0 on NORMRESO*/
#define	gvram1()	(void)outp( 0xa6, 1 )	/* write GVRAM1 on NORMRESO*/

#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;
extern BOOL hireso;
static int v_shift1 = 2;

static int GDC5M;	/* GDCのクロック 5/2.5MHz */
static int INCADR;	/* GDC Scroll のアドレスインクリメント量 */

#define	MAX_VIEW	16

extern int num_view_buf;
extern int num_view;
#ifndef	NEMS
extern int system_EMS_end;
int view_EMS_top;
int view_EMS_unit;
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;
static unsigned void far *ucw = NULL;
static unsigned int ucw_seg;
static unsigned char far *ucw_para;
static unsigned int far *ucw_paraw;
static unsigned long G_top = G_TOP_N;
static unsigned int G_seg = G_SEG_N;
static unsigned long G_id = G_ID_N;
static unsigned long T_id = T_ID_N;
static unsigned int G_width_b = G_WIDTH_B_N;
static unsigned int G_height = G_HEIGHT_N;
static BOOL hireso_device_inited = FALSE;
static unsigned long gptr_step, gptr_halfstep, mptr_step;
/*
   ハイレゾモードでdevice_initが実行済みかどうかを示すフラグ
   bios 使用に初期設定が不可欠なので hireso_device_inited==FALSE で
   bios を使用するとハングするのでその対策
*/

struct SCROLL {
	/* output parameter. send to scroll-functions and other device-output-
     * functions
     */
	int 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_shift2;
static int v_x_shift4;
static int v_x_shift8;
static int v_y_shift2;
static int v_y_shift4;
static int v_y_shift8;
static int lr_step = 2;

/* static int view_page; */
static BUFFER *v_top2;
static BUFFER *v_top4;
static BUFFER *v_top8;
static HUGE_BUF *view_buf;
long view_buf_size = (long)G_WIDTH_B_N * (long)G_HEIGHT_N / 2;	/* 0x3E80L */

int x_shift;
int y_shift;

/* Proto-types */

/*	buffer.c */
void clear_buf(BUF_INFO *);
ulong leftbuffer(void);

void set_disp_reso(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);	/* NORMRESO 用 */
static void s_view2(void);
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 wait_out(void);

/* 以下はあらたに加わった関数 */
static void reversescreen(int);
static void accessvram(unsigned int number);
#ifdef USE_HIRESBIOS
static void dispvram(unsigned int number);
static void setcolormode(unsigned int color);
static void far dummy(void);
static int bioscall(unsigned char code);
#endif
/*
 * ２つに分けたＧＶＲＡＭの行の移動：使っていない
 *
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;
	BUFFER *mptr_tmp;
	BUFFER *gptr_tmp;
	unsigned long work;

	line1 = G_height - s_dat->r_line;
	work = (ulong)hmaxb * (ulong)(G_height - v_shift);
	mptr_tmp = (BUFFER *)((((work & 0xffff0L) << 12) | (work & 0xfL))
							+ (unsigned long)s_dat->mptr);

	work = (ulong)(s_dat->offset) + (ulong)(s_dat->r_line) * (ulong)G_width_b;
	gptr_tmp = (BUFFER *)((((work & 0xffff0L) << 12) | (work & 0xfL))
							+ (unsigned long)g_top_ptr);
	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);
		(ulong)mptr_tmp += mptr_step;
		(ulong)gptr_tmp += gptr_step;
	}

	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);
		(ulong)mptr_tmp += mptr_step;
		(ulong)gptr_tmp += gptr_step;
	}
	gdc_scroll(s_dat);

}

static void s_down(struct SCROLL *s_dat, int v_shift)
	/* スムース・スクロール・ダウン by v_shift dots */
{
	int ins, i;
	BUFFER *mptr_tmp;
	BUFFER *gptr_tmp;
	unsigned long work;

	mptr_tmp = (BUFFER *)s_dat->mptr;
	if (s_dat->r_line >= v_shift) {
		ins = v_shift;
		s_dat->r_line -= v_shift;
		work = (ulong)(s_dat->offset)
			+ (ulong)(s_dat->r_line) * (ulong)G_width_b;
		gptr_tmp = (BUFFER *)((((work & 0xffff0L) << 12) | (work & 0xfL))
			+ (unsigned long)g_top_ptr);
	}
	else {
		ins = s_dat->r_line;
		s_dat->r_line = G_height + s_dat->r_line - v_shift;

		work = (ulong)(s_dat->offset)
			+ (ulong)(s_dat->r_line) * (ulong)G_width_b;
		gptr_tmp = (BUFFER *)((((work & 0xffff0L) << 12) | (work & 0xfL))
			+ (unsigned long)g_top_ptr);
		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);
			(ulong)mptr_tmp += mptr_step;
			(ulong)gptr_tmp += gptr_step;
		}
		work = s_dat->offset;
		gptr_tmp = (BUFFER *)((((work & 0xffff0L) << 12) | (work & 0xfL))
			+ (ulong)g_top_ptr);
	}

	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);
		(ulong)mptr_tmp += mptr_step;
		(ulong)gptr_tmp += gptr_step;
	}
	gdc_scroll(s_dat);
}

static void s_right(struct SCROLL *s_dat)
	/* 左方向　スムース・スクロール by 32 dots */
{
	BUFFER *mptr_tmp;
	BUFFER *gptr_tmp;
	int i, count;
	unsigned long work;

	/*	if( s_dat->offset <= 0 )	line2to1( s_dat, 1 ); */
	s_dat->offset -= (hireso == 0) ? 2 : 4;
	if (s_dat->offset < 0) s_dat->offset = 0;

	work = (ulong)(s_dat->mptr);
	mptr_tmp = (BUFFER *)((work & 0xffff0000L) + ((work & 0xfff0L) << 12)
		+ (work & 0xfL));
	work = (ulong)(s_dat->offset) + (ulong)s_dat->r_line * (ulong)G_width_b;
	gptr_tmp = (BUFFER *)((((work & 0xffff0L) << 12) | (work & 0xfL)) + G_top);

	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){
			if (hireso == 0) {
				*((int far *)gptr_tmp) = 0;
			}
			else {
				*((long far *)gptr_tmp) = 0L;
			}
		}
		else {
			if (hireso == 0) {
				*((int far *)gptr_tmp) = *((int far *)mptr_tmp);
			}
			else {
				*((long far *)gptr_tmp) = *((long far *)mptr_tmp);
			}
			(ulong)mptr_tmp += mptr_step;
		}
		(ulong)gptr_tmp += gptr_step;
	}
	work = s_dat->offset;
	gptr_tmp = (BUFFER *)((((work & 0xffff0L) << 12) | (work & 0xfL)) + G_top);

	for (i = s_dat->r_line; i > 0; i--) {
		if (hireso == 0) {
			*((int far *)gptr_tmp) = *((int far *)mptr_tmp);
		}
		else {
			*((long far *)gptr_tmp) = *((long far *)mptr_tmp);
		}
		(ulong)mptr_tmp += mptr_step;
		(ulong)gptr_tmp += gptr_step;
	}
}

static void s_left(struct SCROLL *s_dat)
	/* 右方向　スムース・スクロール by 32 dots */
{
	BUFFER *mptr_tmp;
	BUFFER *gptr_tmp;
	int i, count, step;
	unsigned long work;

	step = (hireso) ? 4 : 2;

	s_dat->offset += step;

	/*	if( ++(s_dat->offset) >= G_width_b ) line1to2( s_dat, 1 ); */

	work = (ulong)(s_dat->mptr) + (G_width_b - step);
	mptr_tmp = (BUFFER *)((work & 0xffff0000L) + ((work & 0xfff0L) << 12)
		+ (work & 0xfL));
	work = (ulong)(s_dat->offset) + (G_width_b - step)
		+ (ulong)(s_dat->r_line) * (ulong)G_width_b;
	gptr_tmp = (BUFFER *)((((work & 0xffff0L) << 12) | (work & 0xfL))
							+ G_top);
	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)
			if (hireso == 0) {
				*((int far *)gptr_tmp) = 0;
			}
			else {
				*((long far *)gptr_tmp) = 0L;
			}
		else {
			if (hireso == 0) {
				*((int far *)gptr_tmp) = *((int far *)mptr_tmp);
			}
			else {
				*((long far *)gptr_tmp) = *((long far *)mptr_tmp);
			}
			(ulong)mptr_tmp += mptr_step;
		}
		(ulong)gptr_tmp += gptr_step;
	}

	gdc_scroll(s_dat);
	work = s_dat->offset + (G_width_b - step);
	gptr_tmp = (BUFFER *)((((work & 0xffff0L) << 12) | (work & 0xfL)) + G_top);
	for (i = s_dat->r_line; i > 0; i--) {
#if 0
		if( count++ < s_dat->v_spc || count > s_dat->v_spc + s_dat->gh_act ){
			if (hireso==0){
				*((int far*)gptr_tmp) = 0;
			}
			else{
				*((long far*)gptr_tmp) = 0L;
			}
		}
		else
#endif
		{
			if (hireso == 0) {
				*((int far *)gptr_tmp) = *((int far *)mptr_tmp);
			}
			else {
				*((long far *)gptr_tmp) = *((long far *)mptr_tmp);
			}
			(ulong)mptr_tmp += mptr_step;
		}
		(ulong)gptr_tmp += gptr_step;
	}
}

static void gdc_scroll(struct SCROLL *s_dat)
	/* ２分割のＧＶＲＡＭの表示 */
{
	uint adr;

	adr = (((long)(s_dat->r_line))* G_width_b + s_dat->offset) >> 1;
	gshift( adr, s_dat->offset >> 1, G_height - s_dat->r_line );
}

static void wait_out(void)
{
	(void)outp(0x5f, 0);
}

static void gshift(uint adr_1, uint adr_2, uint ln)

	/* ＧＤＣ　グラフィック画面表示シフトコマンド
	 * adr_1 : 画面左上に持って来たい位置のVRAMｱﾄﾞﾚｽ
	 * ln : adr_1からln*2またはlnライン分を表示（残りは再びトップアドレスから）
	 * adr_2 : トップアドレス
	 *
     * adr_1 ┌──────────┐┐
     *       │←     70word     →││
     *       │      (40word)      ││hireso&interlace -> ln
     *       │                    ││other -> ln*2
     *       │                    ││
     *       └──────────┘┘
     * adr2  ┌──────────┐┐
     *       │                    ││ln2
     *       │                    ││
     *       └──────────┘┘
     */

{
	uint ln2;

	ln2 = G_height - ln;
	if (hireso == 1) {			/* インターレースモードの配慮 */
		ln = ln >> 1;
		ln2 = ln2 >> 1;
	}
	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 | INCADR);
	o_port(0xa0, adr_2 & 0xff);
	o_port(0xa0, adr_2 >> 8);
	o_port(0xa0, (ln2 & 0xf) << 4);
	o_port(0xa0, (ln2 >> 4) | GDC5M | INCADR);
}

static void o_port(int out_p, int data)
	/* ＧＤＣへコマンドやパラメータを送る */
{
	while ((inp(0xa0) & 0x02) != 0);
	if (hireso)	wait_out();
	(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()
{
#ifdef USE_HIRESBIOS
	/* err.c でいきなり device_cont() をコールしているが，その段階で
	   device_init() が未実行であるとハングする．フラグを使って対策した*/
	if (hireso == 0 || hireso_device_inited == TRUE) {
		bioscall((hireso) ? 0x12 : 0x41);	/* graphic OFF */
	}
#else
	int i;
	do{
		wait_out();
		i=inp(0xa0);
		wait_out();
	}while ( i & 2 );
	wait_out();
	(void)outp(0xa2, (hireso)? 0x05 : 0x0c);
	wait_out();
#endif
}

void device_cont()
{
#ifdef USE_HIRESBIOS
	/* err.c でいきなり device_cont() をコールしているが，その段階で
	   device_init() が未実行であるとハングする．フラグを使って対策した*/
	if (hireso == 0 || hireso_device_inited == TRUE) {
		bioscall((hireso) ? 0x11 : 0x40);	/* graphic ON */
	}
#else
	int i;
	do{
		wait_out();
		i= inp(0xa0);
		wait_out();
	}while ( (i & 0x22) != 0x20 );
	do{
		wait_out();
		i=inp(0xa0);
		wait_out();
	}while ( i & 2 );
	wait_out();
	(void)outp(0xa2, 0x0d);
	wait_out();
#endif
}

void set_disp_reso(void)
{
	if(hireso){
		lr_step = 4;
		G_top = G_TOP_H;
		G_seg = G_SEG_H;
		G_id = G_ID_H;
		T_id = T_ID_H;
		G_width_b = G_WIDTH_B_H;
		G_height = G_HEIGHT_H;
		view_buf_size = (long)G_width_b *(long)G_height / 2;
	}
}

void device_init(DIMENSION *dim)
	/* initialize graphic screen */
{
	union REGS regs;
	uint far *gvram_last = (uint far *)G_ID_N;
	uint x, y;

	if (hireso) {
		gvram_last = (uint far *)G_id;
#ifdef USE_HIRESBIOS
		/* ハイレゾＢＩＯＳをコールするのに必要な設定 */
		/* 0x380 バイトのワークを確保する但しオフセット０である必要あり*/
		if ((ucw = farmalloc(0x390L)) == NULL) {
			error(NO_MEMORY,
#ifdef	JAPANESE
				  "ハイレゾ用ワークエリアの不足"
#else
				  "Cannot get Memory for Hiresolution 9801."
#endif
				);
		}
		ucw_seg = FP_SEG(ucw) + (FP_OFF(ucw) >> 4) + 1;
		ucw_paraw = (unsigned int far *)(((long)ucw_seg + 0x10L) << 16);
		ucw_para = (unsigned char far *)ucw_paraw;
		ucw_paraw[0] = FP_OFF(dummy);
		ucw_paraw[1] = FP_SEG(dummy);
		bioscall(0);
		setcolormode(1);
		accessvram(0);
		hireso_device_inited = TRUE;
#else
		wait_out();
		(void)outp(0xa4, 0x0e);	/* plane 0 : Read & Write */
		wait_out();
		reversescreen(0);
#endif
	}
	/* 左右のスムーススクロール等で使用する変数 */
	gptr_step = ((((long)G_width_b & 0xffff0L) << 12) 
				| (G_width_b & 0xfL));
	gptr_halfstep = ((((long)(G_width_b / 2) & 0xffff0L) << 12) 
				| ((G_width_b / 2) & 0xfL));

	if (v_shift0 < 0 ){
		if((v_shift1 = - v_shift0) <= 1) v_shift1 = 2;
		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 = (hireso == 0 && (inp(0x31) & 0x80) == 0) ? 0x40 : 0;
	INCADR = (hireso == 2) ? 0x80 : 0;
	if (num_view == -0x7fff) {			/* check 2nd GVRAM */
		x = *((uint far *)G_top);
		accessvram(1);
		*((uint far *)G_top) = x + 1;
		accessvram(0);
		if (x != *((uint far *)G_top))
			num_view = 0;
	}
	/* num_view==-0x7fff : 2nd GVRAM exist. num_view=0 : not exist*/
	cls(0);

	if (!hireso) {
		(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);	/* 8 color 400 line mode */
	}
#ifdef USE_HIRESBIOS
	else {
		dispvram(1);			/* display GVRAM0 */
	}
#endif
	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) * 16 && y < (G_height - 4) * 2) {
		f_div = 2;
	}
	else 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 == (hireso) ? 2 : 4)
			f_div2 = f_div * 2;
	}
	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) {
		uint x2, x4, x8, y2, y4, y8;

		v_x_shift2 = x_shift >> 1;
		v_x_shift4 = x_shift >> 2;
		v_x_shift8 = x_shift >> 3;
		v_y_shift2 = y_shift >> 1;
		v_y_shift4 = y_shift >> 2;
		v_y_shift8 = y_shift >> 3;
		x2 = x4 = x8 = 5;
		y2 = y4 = y8 = 2;
		if (v_x_shift2 > 0) {
			x2 += v_x_shift2 >> 3;
			x4 += v_x_shift4 >> 3;
			x8 += v_x_shift8 >> 3;
		}
		if (v_y_shift2 > 0) {
			y2 += v_y_shift2;
			y4 += v_y_shift4;
			y8 += v_y_shift8;
		}
		v_top2 = (BUFFER *)((HUGE_BUF *)G_top + (x2 + (G_width_b * y2)));
		v_top4 = (BUFFER *)((HUGE_BUF *)G_top + (x4 + (G_width_b * y4)));
		v_top8 = (BUFFER *)((HUGE_BUF *)G_top + (x8 + (G_width_b * y8)));
		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 num_view = f_div = 0;
	}
	else
nv:		num_view_buf = num_view = 0;
}

static void cls(int mode)
{
	static BUF_INFO vram[3] =
	{	{
			(BUFFER *)0xa8000000L,
			(BUFFER *)0xa8000000L,
			(BUFFER *)0xa8007fffL,
			0x7ff0L, 0
		},
		{
			(BUFFER *)0xb0000000L,
			(BUFFER *)0xb0000000L,
			(BUFFER *)0xb000ffffL,
			0xfffeL, 0
		},
		{
			(BUFFER *)0xc0000000L,
			(BUFFER *)0xc0000000L,
			(BUFFER *)0xd0009fffL,
			0x1a000L, 0
		}
	};

	accessvram(0);
	clear_buf( (hireso)?&vram[2]:&vram[0] );
	if (mode != 0) {
		if (hireso) {
#ifdef USE_HIRESBIOS
			accessvram(0);
			clear_buf(&vram[2]);
			accessvram(1);
			clear_buf(&vram[2]);
			accessvram(2);
			clear_buf(&vram[2]);
			accessvram(3);
			clear_buf(&vram[2]);
			accessvram(0);
#else
			wait_out();
			(void)outp(0xa4,0x01);	/* Read 0 plane, Write 1-3plane */
			wait_out();
			clear_buf(&vram[2]);
			wait_out();
			(void)outp(0xa4,0x0e);	/* Read 0 plane, Write 0plane */
			wait_out();
#endif
		}
		else {
			accessvram(0);
			clear_buf(&vram[0]);
			clear_buf(&vram[1]);
			if (num_view >= -MAX_VIEW) {
				accessvram(1);
				clear_buf(&vram[0]);
				clear_buf(&vram[1]);
				accessvram(0);
			}
		}
	}
}

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);
	wait_out();
	wait_out();
	(void)outp(0xaa, (uchar)(color >> 8));
	wait_out();
	wait_out();
	(void)outp(0xac, (uchar)((color & 0xf0) >> 4));
	wait_out();
	wait_out();
	(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;
#ifdef USE_HIRESBIOS
	if (hireso) {
		ucw_para[0] = 0x80;
		ucw_para[1] = (mode) ? (back_color & 0xff) : 0;
		ucw_para[2] = (mode) ? (back_color >> 8) : 0;
		bioscall(4);
		ucw_para[0] = 0x81;
		ucw_para[1] = (mode) ? (fore_color & 0xff) : 0xff;
		ucw_para[2] = (mode) ? (fore_color >> 8) : 0xff;
		bioscall(4);
	}
#else
	if (hireso){
		fc = (mode) ? fore_color : 0xfff;
		bc = (mode) ? back_color : 0x0;
		set_a_palette_m(0, fc, bc);
	}
#endif
	else {
		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;

	if (!hireso) {
		*(((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, G_height);
	}
	else {
#ifdef USE_HIRESBIOS
		if (hireso_device_inited){
#endif
			*(((uchar far *)(G_id)) + 1) = 'P';
			device_pause();
			gshift(0, 0, G_height);
#ifdef USE_HIRESBIOS
			setcolormode(0);	/* 16 Color mode */
#else
			init_a_palette();
#endif
			cls(1);
#ifdef USE_HIRESBIOS
			bioscall(0x13);		/* terminate graphic */
		}
		farfree(ucw);
#endif
	}
	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; /* map_top_ptr : huge pointer */
	hmax = out->width;
	vmax = out->height;
	hmaxb = out->byte_width;
	mptr_step = ((((unsigned long)hmaxb & 0xffff0L) << 12) 
				| (hmaxb & 0xfL));

	/* 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;
	BUFFER *ptr0;
	BUFFER *ptr1;
	BUFFER *ptr;
	BUFFER *ptrg0;
	BUFFER *ptrg;
	ulong mptr_step_;

	ptrg0 = v_top8;
	ptr0 = (BUFFER*)map_top_ptr;
	i = hmaxb << 3;
	mptr_step_ = ((((ulong)i & 0xfff0L) << 12) | ((ulong)i & 0xfL));

	for (ch = v = 0; v < vmax; v += 8) {
		vm = ((vmax - v) < 8) ? vmax - v: 8;
		ptr1 = (BUFFER *)ptr0;
		(ulong)ptr0 += mptr_step_;
		ptrg = ptrg0;
		(ulong)ptrg0 += gptr_step;
		for (i = 0; i < hmaxb; i++) {
			ptr = ptr1++;
			for (j = vm; j != 0; j--) {
				if (*ptr != 0) {
					ch |= 1;
					break;
				}
				(ulong)ptr += mptr_step;
			}
			if ((i & 7) == 7){
				*ptrg++ = ch;
			}
			ch += ch;
		}
		if ((i = (hmaxb & 7)) != 0)
			*ptrg++ = (ch << (7 - i));
	}
	if (f_box)
		v_box(v_top8, -v_x_shift8, -v_y_shift8,
			  s_max_width / 8, s_max_height / 8);
}

static void s_view4(void)
{
	int i, j, vm, v;
	uint ch;
	BUFFER *ptr0;
	BUFFER *ptr1;
	BUFFER *ptr;
	BUFFER *ptrg0;
	BUFFER *ptrg;
	ulong mptr_step_;

	ptrg0 = v_top4;
	ptr0 = (BUFFER*)map_top_ptr;
	i = hmaxb << 2;
	mptr_step_ = ((((ulong)i & 0xfff0L) << 12) | ((ulong)i & 0xfL));

	for (ch = v = 0; v < vmax; v += 4) {
		vm = ((vmax - v) < 4) ? vmax - v : 4;
		ptr1 = (BUFFER *)ptr0;
		(ulong)ptr0 += mptr_step_;
		ptrg = ptrg0;
		(ulong)ptrg0 += gptr_step;
		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;
				(ulong)ptr += mptr_step;
			}
			if ((i & 3) == 3)
				*ptrg++ = ch;
			ch <<= 2;
		}
		if ((i = (hmaxb & 3)) != 0)
			*ptrg++ = (ch << ((3 - i) * 2));
	}
	if (f_box)
		v_box(v_top4, -v_x_shift4, -v_y_shift4,
			  s_max_width / 4, s_max_height / 4);
}

static void s_view2(void)
{
	int i, j, vm, v;
	uint ch;
	BUFFER *ptr0;
	BUFFER *ptr1;
	BUFFER *ptr;
	BUFFER *ptrg0;
	BUFFER *ptrg;
	ulong mptr_step_;

	ptrg0 = v_top2;
	ptr0 = (BUFFER*)map_top_ptr;
	i = hmaxb << 1;
	mptr_step_ = ((((ulong)i & 0xfff0L) << 12) | ((ulong)i & 0xfL));

	for (ch = v = 0; v < vmax; v += 2) {
		vm = ((vmax - v) < 2) ? vmax - v : 2;
		ptr1 = (BUFFER *)ptr0;
		(ulong)ptr0 += mptr_step_;
		ptrg = ptrg0;
		(ulong)ptrg0 += gptr_step;
		for (i = 0; i < hmaxb; i++) {
			ptr = ptr1++;
			for (j = vm; j != 0; j--) {
				if ((*ptr & 0xc0) != 0) {
					ch |= 8;
				}
				if ((*ptr & 0x30) != 0) {
					ch |= 4;
				}
				if ((*ptr & 0x0c) != 0) {
					ch |= 2;
				}
				if ((*ptr & 0x03) != 0) {
					ch |= 1;
				}
				if ((ch & 15) == 15)
					break;
				(ulong)ptr += mptr_step;
			}
			if ((i & 1) == 1)
				*ptrg++ = ch;
			ch <<= 4;
		}
		if ((i = (hmaxb & 1)) != 0)
			*ptrg++ = ch;
	}
	if (f_box)
		v_box(v_top2, -v_x_shift2, -v_y_shift2,
			  s_max_width / 2, s_max_height / 2);
}

static void v_box(BUFFER *orig, int x, int y, int w, int h)
{
	BUFFER *ptr;
	BUFFER *ptr2;
	int i, b, ch;

	(HUGE_BUF *)orig += (ulong)(x + G_width_b * 8) / 8
		+ (ulong)G_width_b *(y - 1);
	b = (x & 7);
	ch = (1 << (7 - b));
	ptr = orig;
	for (i = h; i > 0; (ulong)ptr += gptr_step, i--)
		*ptr |= ch;

	ch = (0xff >> b);
	ptr = ptr2 = orig;
	for (i = h - 1; i > 0; (ulong)ptr2 += gptr_step, i--);
	*ptr2 |= ch;
	*ptr |= ch;

	ptr++;
	ptr2++;
	for (i = w + b - 8; i > 8; ptr++, ptr2++, i -= 8) {
		*ptr |= 0xff;
		*ptr2 |= 0xff;
	}
	ch = (0xff << (8 - i));
	*ptr |= ch;
	*ptr2 |= ch;

	ch = (1 << (8 - i));
	for (i = h; i > 0; (ulong)ptr += gptr_step, 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;
			  j = s_dat.offset = 0;
			  if (f_div == ((hireso) ? 2 : 4) && key != Key_C) {
				  if (hireso)
					  s_view2();
				  else
					  s_view4();
				  if (num_view)
					  j = get_view(out->page, (hireso) ? 2 : 4);
				  else
					  getch();
			  }
			  else {
				  if (hireso)
					  s_view4();
				  else
					  s_view8();
				  if (num_view)
					  j = get_view(out->page, (hireso) ? 4 : 8);
				  else
					  getch();
			  }
			  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 && hireso == FALSE) {
		*((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;
	(HUGE_BUF *)g_top_ptr = (HUGE_BUF *)G_top
		+ ((ulong)s_dat->h_spc + ((ulong)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;
	BUFFER *mptr_tmp;
	BUFFER *gptr_tmp;
	static BOOL first_drawing = TRUE;

	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;
		(HUGE_BUF *)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;
	if (!first_drawing) {
		gdc_scroll(s_dat);
	}
	else {
		first_drawing = FALSE;
	}
	mptr_tmp = (BUFFER *)(s_dat->mptr);
	(HUGE_BUF *) gptr_tmp = (HUGE_BUF *)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);
		(ulong)gptr_tmp += gptr_step;
		(ulong)mptr_tmp += mptr_step;
	}
}

static int get_view(page, div)
{
	int i, pt, vram_mode, pg;
	uchar tmp[G_WIDTH_B_N / 2];	/* normalreso の時のみ使用 */
	BUFFER *gptr_tmp;
	BUFFER *buf_tmp;
	ulong work;

	pt = view.current;
	pg = -1;
	while (TRUE) {
		if (div != f_div2)
			goto skip;
		if (--pt < 0)
			pt = view.total - 1;
		if (pt >= 0) {
			if (hireso) {
				if (num_view >= -MAX_VIEW ) {
					work = view_buf_size * pt;
					vram_mode = 0x0e;	/* read plane 0, write plane 0 */
				}
				else {
					work = view_buf_size * (pt % 2);
					vram_mode = (0x0e + (pt / 2 + 1) * 0x10);	/* read(pt/2+1), write0 */
				}
				wait_out();
				(void)outp(0xa4, vram_mode);	/* set plane access mode */
				wait_out();
			}
			else {
				work = view_buf_size * pt;
			}
#ifndef	NEMS
			if( view_EMS_unit ){
				buf_tmp = (BUFFER*)view_buf;
				set_ems( view_EMS_top + view_EMS_unit*pt );
			}
			else
#endif
			(HUGE_BUF*)buf_tmp = (HUGE_BUF *)view_buf + work;
			gptr_tmp = (BUFFER *)G_top + G_width_b / 2;
			for (i = G_height; i > 0; i--) {
				if (hireso) {
					movedata(FP_SEG(buf_tmp), FP_OFF(buf_tmp),
						 FP_SEG(gptr_tmp), FP_OFF(gptr_tmp), G_width_b / 2);
				}
				else {
					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 {
						gvram1();
						movedata(FP_SEG(buf_tmp), FP_OFF(buf_tmp),
								 FP_SEG(tmp), FP_OFF(tmp), G_width_b / 2);
						gvram0();
						movedata(FP_SEG(tmp), FP_OFF(tmp),
						 FP_SEG(gptr_tmp), FP_OFF(gptr_tmp), G_width_b / 2);
					}
				}
				(ulong)buf_tmp += gptr_halfstep;
				(ulong)gptr_tmp += gptr_step;
			}
			if (hireso){
				wait_out();
				(void)outp(0xa4, 0x0e);	/* read plane0 write plane0 */
				wait_out();
			}
			accessvram(0);
			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 = (BUFFER *)G_top;
	if (hireso) {
		if (num_view >= -MAX_VIEW) {
			work = view_buf_size * view.current;
			vram_mode = 0x0e;	/* read plane0 write plane0 */
		}
		else {
			work = view_buf_size * (view.current % 2);
			vram_mode = (0x0f - (1 << (view.current / 2 + 1)));
			/*read 0 write(view.current/2+1) */
		}
		wait_out();
		(void)outp(0xa4, vram_mode);
		wait_out();
	}
	else {
		work = view_buf_size * view.current;
	}
#ifndef	NEMS
	if( view_EMS_unit ){
		buf_tmp = (BUFFER *)view_buf;	
		set_ems( view_EMS_top + view_EMS_unit*view.current );
	}else
#endif
	(HUGE_BUF *)buf_tmp = (HUGE_BUF *)view_buf + work;
	view.current++;
	for (i = G_height; i > 0; i--) {
		if (hireso) {
			movedata(FP_SEG(gptr_tmp), FP_OFF(gptr_tmp),
					 FP_SEG(buf_tmp), FP_OFF(buf_tmp), G_width_b / 2);
		}
		else {
			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);
				gvram1();		/* accessvram(1) */
				movedata(FP_SEG(tmp), FP_OFF(tmp),
						 FP_SEG(buf_tmp), FP_OFF(buf_tmp), G_width_b / 2);
				gvram0();		/* accessvram(0) */
			}
		}
		(ulong)buf_tmp += gptr_halfstep;
		(ulong)gptr_tmp += gptr_step;
	}
	if (hireso) {
		wait_out();
		(void)outp(0xa4, 0x0e);		/* read plane0 write plane0 */
		wait_out();
		accessvram(0);
#ifdef USE_HIRESBIOS
		dispvram(1);
#endif
	}
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, step;
	ulong work;

	/*
   ハイレゾノンインターレースモードでは gdc scroll offset は DWORD 単位
   でなければならない．混乱を避けるため，deviceh.c ではすべてのモードで
   DWORD単位スクロールを行なっている．その結果，move_screen の横方向は
   DWORDステップである．bitmap の横幅は必ず DWORD の倍数になっているので
   問題はない(device.h参照)
	*/
	step = (s_dat->gw_act) >> 1;
	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 = -step;
		  break;
	  case ShiftRight:
		  x_step = step;
		  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 = (x_step / lr_step) * lr_step;
		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) {
		ulong work;
		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 += (ulong)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;
		if (hireso)
			shift = (shift / 2) * 2;
		s_dat->mptr += shift * hmaxb;
		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;
		if (hireso)
			shift = (shift / 2) * 2;
		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 -= lr_step;
		s_dat->x -= lr_step;
		s_right(s_dat);
	}
}

static void scroll_left(struct SCROLL *s_dat)
{
	if (s_dat->x + G_width_b < hmaxb) {
		s_dat->mptr += lr_step;
		s_dat->x += lr_step;
		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");
}

#ifdef USE_HIRESBIOS
static int bioscall(unsigned char code)
	/* ノーマル・ハイレゾモードのＢＩＯＳコール */
{
	union REGS inregs, outregs;
	struct SREGS segregs;

	inregs.h.ah = code;
	if (hireso) {
		segregs.ds = ucw_seg;
		int86x(0x1d, &inregs, &outregs, &segregs);
	}
	else {
		int86(0x18, &inregs, &outregs);
	}
	return outregs.h.ah;
}
static void far dummy(void)
{
	return;
}
#endif

static void accessvram(unsigned int number)
{
	if (hireso) {
#ifdef USE_HIRESBIOS
		ucw_para[0] = DEFAULT;
		ucw_para[1] = DEFAULT;
		ucw_para[2] = number;
		ucw_para[3] = DEFAULT;
		bioscall(1);
#else
		uchar work;
		
		number &= 3;
		work = (number << 4)+((~(1 << number)) & 0xf);
		wait_out();
		(void)outp(0xa4, work);
		wait_out();
#endif
	}
	else if (number == 0) {
		gvram0();
	}
	else {
		gvram1();
	}
}
#ifdef USE_HIRESBIOS
static void dispvram(unsigned int number)
{
	ucw_para[0] = DEFAULT;
	ucw_para[1] = DEFAULT;
	ucw_para[2] = DEFAULT;
	ucw_para[3] = number;
	bioscall(1);
}
static void setcolormode(unsigned int color)
/* color : 0 = 16 color, 1 = monochro */
{
	ucw_para[0] = color;
	ucw_para[1] = DEFAULT;
	ucw_para[2] = DEFAULT;
	ucw_para[3] = DEFAULT;
	bioscall(1);
}
#endif
/* end of file : device.c */
