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

/* $Header:apa8tty.c 12.0$ */
/* $ACIS:apa8tty.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/cacons/RCS/apa8tty.c,v $ */

#define LOCAL

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:apa8tty.c 12.0$";
#endif

#include "apaeight.h"
#if NAPAEIGHT > 0
 
extern int beep();
extern unsigned short *apa_fontblt();

#include "../h/types.h"
 
#include "../machinecons/screen_conf.h"
 
#include "../machinecons/apa8tty.h"
#include "../machinecons/apa8tty_font.h"

/*
 * Define macros to check, set and reset the cursor bit.
 */
LOCAL short apa8cursorflag;
#define CURSOR_OFF (apa8cursorflag == FALSE)
#define CURSOR_ON (apa8cursorflag == TRUE)
#define SET_CURSOR  apa8cursorflag = TRUE
#define RESET_CURSOR apa8cursorflag = FALSE
LOCAL unsigned apa8_color;
/*
 * Pointer to the bitmap cursor spot.
 */
LOCAL unsigned short *apa8scr;

/*
 * This is an array of masks to be used when trying to determine where the
 * character is in a 16 bit word on the bitmap.  Note it covers all 9 bits.
 */
LOCAL unsigned short char_mask[] = {
	0x007f,		/* 0 */ 
	0xfe00,		/* 7 */
	0xfc01,		/* 6 */
	0xf803,		/* 5 */
	0xf007,		/* 4 */
	0xe00f,		/* 3 */
	0xc01f,		/* 2 */
	0x803f,		/* 1 */
};

/*
 * This is an array of masks to be used when rotating a character and
 * writing in the y direction. (Thats why byte 1 and byte 2 in the mask
 * are the same.)
 */
LOCAL unsigned short y1_rotatemasks[] = {
	0x0000,		/* 8 */ 
	0xfefe,		/* 1 */
	0xfcfc,		/* 2 */
	0xf8f8,		/* 3 */
	0xf0f0,		/* 4 */
	0xe0e0,		/* 5 */
	0xc0c0,		/* 6 */
	0x8080,		/* 7 */
};

/*
 * This array of rotate masks is used on the second pass of writing a
 * character in apa8write_char. The problem is that we want to mask out
 * the "9th" bit (characters are 8 bits, the 9th bit is the space between
 * characters which may contain garbage.) Note each of these values is 
 * equal to:
 * 		((~y1_rotatemasks[ii]) ^ (1 << ((ii - 1) & 0x7)))
 */
LOCAL unsigned short y2_rotatemasks[] = {
	0x7f7f,		/* 1 */ 
	0x0000,		/* 0 */
	0x0101,		/* 7 */
	0x0303,		/* 6 */
	0x0707,		/* 5 */
	0x0f0f,		/* 4 */
	0x1f1f,		/* 3 */
	0x3f3f,		/* 2 */
};

/*
 * This is an array of data masks to be used on the second pass of writing
 * a character on the screen.  Even though we allow the 9th bit to be written,
 * we must make it blank.
 */
LOCAL unsigned short y_datamasks[] = {
	0x007f,		/* no bit 7 */
	0x00fe,		/* no bit 0 */
	0x00fd,		/* no bit 1 */
	0x00fb,		/* no bit 2 */
	0x00f7,		/* no bit 3 */
	0x00ef,		/* no bit 4 */
	0x00df,		/* no bit 5 */
	0x00bf,		/* no bit 6 */
};

/*
 * This is an array of masks to be used to pick out the character from 
 * the rotated bytes in apa8write_char.
 */
LOCAL unsigned short x_rotatemasks[] = {
	0x00ff,		/* 0 */ 
	0xfe01,		/* 7 */
	0xfc03,		/* 6 */
	0xf807,		/* 5 */
	0xf00f,		/* 4 */
	0xe01f,		/* 3 */
	0xc03f,		/* 2 */
	0x807f,		/* 1 */
};


/*
 * The amount to rotate a character where the cursor is.
 */
LOCAL unsigned short rotate_amt;

/*
 * Font information.
 */
#define PADCOL_WD	1
#define PADCOL_HT	2

#define MAX_COLUMNS	80
#define MAX_LINES	35

LOCAL unsigned short  font_wd,	/* Maximum width of the given font */
		       font_ht,	/* Maximum height of the given font */
		       col_wd,	/* Width of the spot in which a character
				   is placed */
		       col_ht,	/* Height of the spot in which a character
				   is placed */
		       col_MAX,	/* Number of the last column in one line.
				   Equals the number of characters on one
				   line minus one */
		       line_MAX;/* Number of the last line.  Equals the 
				   number of lines on the screen minus one */

/*
 * save area for screen print
 */
#ifndef MINIROOT
char	apa8_save_screen[MAX_COLUMNS*MAX_LINES];
int	apa8_save_pos;
#endif /* !MINIROOT */

/*
 * A general unrolled loop. A good optimization is to make this loop equal
 * to col_ht. (Which will always be the same once we pick a font.)
 */
#define GEN_LOOP(cnt,s) {						\
	for (; cnt > 15; cnt -= 16) {					\
		s;s;s;s;s;s;s;s;s;s;s;s;s;s;s;s;s;s;			\
	}								\
	if (cnt > 0) {							\
		switch (cnt) {						\
			case 15: s; case 14: s; case 13: s; case 12: s;	\
			case 11: s; case 10: s; case  9: s; case  8: s;	\
			case  7: s; case  6: s; case  5: s; case  4: s;	\
			case  3: s; case  2: s; case  1: s; 		\
		 }							\
	}								\
}

/*
 * NOTE: the value of "reg" does not matter because all we want to do is
 *       call the to[] address.
 */
#define CHAR_BLANK_LOOP(to,reg,incr) {	\
	to[incr*0] = to[incr*1] = to[incr*2] = to[incr*3] =	\
	to[incr*4] = to[incr*5] = to[incr*6] = to[incr*7] = reg;\
}
#define CHAR_LOOP(to,from,incr) {	\
	to[(incr*0)] = from[(incr*0)];	\
	to[(incr*1)] = from[(incr*1)];	\
	to[(incr*2)] = from[(incr*2)];	\
	to[(incr*3)] = from[(incr*3)];	\
	to[(incr*4)] = from[(incr*4)];	\
	to[(incr*5)] = from[(incr*5)];	\
	to[(incr*6)] = from[(incr*6)];	\
	to[(incr*7)] = from[(incr*7)];	\
	to[(incr*8)] = from[(incr*8)];	\
	to[(incr*9)] = from[(incr*9)];	\
	to[(incr*10)] = from[(incr*10)];	\
	to[(incr*11)] = from[(incr*11)];	\
	to[(incr*12)] = from[(incr*12)];	\
	to[(incr*13)] = from[(incr*13)];	\
	to[(incr*14)] = from[(incr*14)];	\
	to[(incr*15)] = from[(incr*15)];	\
}

#define BOLD_CHAR_LOOP(to,from,incr,shift) {	\
		register int tmp;	\
		tmp = from[(incr*0)];	\
		to[(incr*0)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*1)];	\
		to[(incr*1)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*2)];	\
		to[(incr*2)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*3)];	\
		to[(incr*3)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*4)];	\
		to[(incr*4)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*5)];	\
		to[(incr*5)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*6)];	\
		to[(incr*6)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*7)];	\
		to[(incr*7)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*8)];	\
		to[(incr*8)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*9)];	\
		to[(incr*9)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*10)];	\
		to[(incr*10)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*11)];	\
		to[(incr*11)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*12)];	\
		to[(incr*12)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*13)];	\
		to[(incr*13)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*14)];	\
		to[(incr*14)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
		tmp = from[(incr*15)];	\
		to[(incr*15)] = (tmp << shift) | ((tmp >> (16-shift)) &  0xffff); \
	}

/*
 * Unrolled loop for across the APA-8 visible screen.
 */
#define SCREEN_LOOP(to,from,incr) {	\
	to[(incr*0)] = from[(incr*0)];	\
	to[(incr*1)] = from[(incr*1)];	\
	to[(incr*2)] = from[(incr*2)];	\
	to[(incr*3)] = from[(incr*3)];	\
	to[(incr*4)] = from[(incr*4)];	\
	to[(incr*5)] = from[(incr*5)];	\
	to[(incr*6)] = from[(incr*6)];	\
	to[(incr*7)] = from[(incr*7)];	\
	to[(incr*8)] = from[(incr*8)];	\
	to[(incr*9)] = from[(incr*9)];	\
	to[(incr*10)] = from[(incr*10)];	\
	to[(incr*11)] = from[(incr*11)];	\
	to[(incr*12)] = from[(incr*12)];	\
	to[(incr*13)] = from[(incr*13)];	\
	to[(incr*14)] = from[(incr*14)];	\
	to[(incr*15)] = from[(incr*15)];	\
	to[(incr*16)] = from[(incr*16)];	\
	to[(incr*17)] = from[(incr*17)];	\
	to[(incr*18)] = from[(incr*18)];	\
	to[(incr*19)] = from[(incr*19)];	\
	to[(incr*20)] = from[(incr*20)];	\
	to[(incr*21)] = from[(incr*21)];	\
	to[(incr*22)] = from[(incr*22)];	\
	to[(incr*23)] = from[(incr*23)];	\
	to[(incr*24)] = from[(incr*24)];	\
	to[(incr*25)] = from[(incr*25)];	\
	to[(incr*26)] = from[(incr*26)];	\
	to[(incr*27)] = from[(incr*27)];	\
	to[(incr*28)] = from[(incr*28)];	\
	to[(incr*29)] = from[(incr*29)];	\
	to[(incr*30)] = from[(incr*30)];	\
	to[(incr*31)] = from[(incr*31)];	\
	to[(incr*32)] = from[(incr*32)];	\
	to[(incr*33)] = from[(incr*33)];	\
	to[(incr*34)] = from[(incr*34)];	\
	to[(incr*35)] = from[(incr*35)];	\
	to[(incr*36)] = from[(incr*36)];	\
	to[(incr*37)] = from[(incr*37)];	\
	to[(incr*38)] = from[(incr*38)];	\
	to[(incr*39)] = from[(incr*39)];	\
	to[(incr*40)] = from[(incr*40)];	\
	to[(incr*41)] = from[(incr*41)];	\
	to[(incr*42)] = from[(incr*42)];	\
	to[(incr*43)] = from[(incr*43)];	\
	to[(incr*44)] = from[(incr*44)];	\
}

/* #define ABC does not work faster yet ... but should!!?? */
#ifdef ABC
#define BLANK_LOOP(to,reg,incr) {	\
	to[incr*0] = to[incr*1] = to[incr*2] = to[incr*3] =	\
	to[incr*4] = to[incr*5] = to[incr*6] = to[incr*7] =	\
	to[incr*8] = to[incr*9] = to[incr*10] = to[incr*11] =	\
	to[incr*12] = to[incr*13] = to[incr*14] = to[incr*15] = \
	to[incr*16] = to[incr*17] = to[incr*18] = to[incr*19] = \
	to[incr*20] = to[incr*21] = to[incr*22] = to[incr*23] = \
	to[incr*24] = to[incr*25] = to[incr*26] = to[incr*27] = \
	to[incr*28] = to[incr*29] = to[incr*30] = to[incr*31] = \
	to[incr*32] = to[incr*33] = to[incr*34] = to[incr*35] = \
	to[incr*36] = to[incr*37] = to[incr*38] = to[incr*39] = \
	to[incr*40] = to[incr*41] = to[incr*42] = to[incr*43] = \
	to[incr*44] = reg;	\
}
#else
/*
 * NOTE: the value of "reg" does not matter because all we want to do is
 *       call the to[] address.
 */
#define BLANK_LOOP(to,reg,incr) {	\
	to[incr*0] = reg;	\
	to[incr*1] = reg;	\
	to[incr*2] = reg;	\
	to[incr*3] = reg;	\
	to[incr*4] = reg;	\
	to[incr*5] = reg;	\
	to[incr*6] = reg;	\
	to[incr*7] = reg;	\
	to[incr*8] = reg;	\
	to[incr*9] = reg;	\
	to[incr*10] = reg;	\
	to[incr*11] = reg;	\
	to[incr*12] = reg;	\
	to[incr*13] = reg;	\
	to[incr*14] = reg;	\
	to[incr*15] = reg;	\
	to[incr*16] = reg;	\
	to[incr*17] = reg;	\
	to[incr*18] = reg;	\
	to[incr*19] = reg;	\
	to[incr*20] = reg;	\
	to[incr*21] = reg;	\
	to[incr*22] = reg;	\
	to[incr*23] = reg;	\
	to[incr*24] = reg;	\
	to[incr*25] = reg;	\
	to[incr*26] = reg;	\
	to[incr*27] = reg;	\
	to[incr*28] = reg;	\
	to[incr*29] = reg;	\
	to[incr*30] = reg;	\
	to[incr*31] = reg;	\
	to[incr*32] = reg;	\
	to[incr*33] = reg;	\
	to[incr*34] = reg;	\
	to[incr*35] = reg;	\
	to[incr*36] = reg;	\
	to[incr*37] = reg;	\
	to[incr*38] = reg;	\
	to[incr*39] = reg;	\
	to[incr*40] = reg;	\
	to[incr*41] = reg;	\
	to[incr*42] = reg;	\
	to[incr*43] = reg;	\
	to[incr*44] = reg;	\
}
#endif

/*
 * Array of short masks used to compute the masks to mask out the edges
 * of the destination that should not be affected.  A one means that bit
 * in the destination can be changed.  The values in left_mask_values are
 * set up to be used with the left edge and the values in right_mask_values
 * are to be used in right edge. 
 */
LOCAL unsigned short left_mask_values[] = {
		0xffff,0x7fff,0x3fff,0x1fff,0x0fff,0x07ff,0x03ff,0x01ff,
		0x00ff,0x007f,0x003f,0x001f,0x000f,0x0007,0x0003,0x0001,
		0x0000
};

LOCAL unsigned short right_mask_values[] = {
		0xffff,0x8000,0xc000,0xe000,0xf000,0xf800,0xfc00,0xfe00,
		0xff00,0xff80,0xffc0,0xffe0,0xfff0,0xfff8,0xfffc,0xfffe,
		0x0000
};

#define FONT_WD 8

#define NULL ((unsigned short *)0)

#define XOR_CURSOR(mask,apa8scr) {				\
	register unsigned short *scr_ptr = apa8scr;		\
	SET_WRITEMASK(mask);					\
	SET_DATAMASK(DEFAULT_DM);				\
	SET_FC(DCR_VEN | DCR_SEN | DCR_ADWR | DCR_X | DCR_FCPNB);\
	scr_ptr[0] = scr_ptr[0];				\
	scr_ptr[NEXT_SL] = scr_ptr[NEXT_SL];			\
	scr_ptr[NEXT_SL*2] = scr_ptr[NEXT_SL*2];		\
	scr_ptr[NEXT_SL*3] = scr_ptr[NEXT_SL*3];		\
	scr_ptr[NEXT_SL*4] = scr_ptr[NEXT_SL*4];		\
	scr_ptr[NEXT_SL*5] = scr_ptr[NEXT_SL*5];		\
	scr_ptr[NEXT_SL*6] = scr_ptr[NEXT_SL*6];		\
	scr_ptr[NEXT_SL*7] = scr_ptr[NEXT_SL*7];		\
	scr_ptr[NEXT_SL*8] = scr_ptr[NEXT_SL*8];		\
	scr_ptr[NEXT_SL*9] = scr_ptr[NEXT_SL*9];		\
	scr_ptr[NEXT_SL*10] = scr_ptr[NEXT_SL*10];		\
	scr_ptr[NEXT_SL*11] = scr_ptr[NEXT_SL*11];		\
	scr_ptr[NEXT_SL*12] = scr_ptr[NEXT_SL*12];		\
	scr_ptr[NEXT_SL*13] = scr_ptr[NEXT_SL*13];		\
	scr_ptr[NEXT_SL*14] = scr_ptr[NEXT_SL*14];		\
	scr_ptr[NEXT_SL*15] = scr_ptr[NEXT_SL*15];		\
}

/*
 * Removing and writing the cursor functions are now macros.
 */
#define APA8REMOVE_CURSOR() 	{					\
	if (CURSOR_ON) {						\
		RESET_CURSOR;						\
		XOR_CURSOR(char_mask[rotate_amt],apa8scr);		\
	}								\
}
#define APA8WRITE_CURSOR()	{					\
	if (CURSOR_OFF) {						\
		SET_CURSOR;						\
		XOR_CURSOR(char_mask[rotate_amt],apa8scr);		\
	}								\
}

/*
 * Each element points to the bitmap address of the character used to index
 * the array.
 */
LOCAL unsigned short *bitmap_addr[SIZEOF_DPLIST];
LOCAL unsigned long line_addr[MAX_LINES+1];
LOCAL unsigned long col_addr[MAX_COLUMNS+1];

apa8set_fontinfo()
{
	register int i;

	/*
	 * Set up the variables for the font width and height and the width
	 * and height for each space (or "cell") where the character will be
	 * placed.
	 */
	font_wd = apa8fonthead.header.maxx;
	font_ht = apa8fonthead.header.maxy;
	col_wd = font_wd + PADCOL_WD;

	/*
	 * An odd col_ht does not work in this driver because everything has
	 * been optimized for a 9x16 bit font.
	 */
	if ((col_ht = font_ht + PADCOL_HT) & 0x1)
		printf("WARNING: THE COLUMN HEIGHT IS ODD!!\n");

	/*
	 * Figure out the max number of columns.  Center the columns on the
	 * screen.  
	 */
	if ((col_MAX = (SCREEN_WD/col_wd) - 1) >= MAX_COLUMNS) {
		col_MAX = MAX_COLUMNS - 1;
	}

	/*
	 * Figure out the max number of lines and center them on the screen.
	 */
	if ((line_MAX = (SCREEN_HT/col_ht) - 1) >= MAX_LINES) {
		line_MAX = MAX_LINES - 1;
	}

	/*
	 * Foreach line on the screen calculate its starting bus address.
	 */
	for (i = 0; i <= (line_MAX + 1); i++) {
		line_addr[i] = APA8BASE + SL_TO_BUS(LINE_TO_SL(i));
	}

	for (i = 0; i <= (col_MAX + 1); i++) {
		col_addr[i] = BM_TO_BUS(COL_TO_BM(i));
	}

	/*
	 * Set the screen structure which keeps track of the max number of
	 * lines and columns.
	 */
	screen_sw[CONS_APA8].lines = line_MAX + 1;
	screen_sw[CONS_APA8].width = col_MAX + 1;
}

/*
 * Set up the fontbltstruct so that it contains the font bitmap as the source,
 * the screen as the destination, the combination rule as source copy, a
 * clipping rectangle as the whole screen and the destination rectangle
 * as the first spot for the font.
 */ 
apa8init_fontblt(fontblt,fontBM)
register fontbltstruct *fontblt;
register Blt_Bitmap *fontBM;
{
	/*
	 * The source bitmap is the font bitmap.
	 */
	fontblt->src_bitmap = *fontBM;

	/*
	 * Set up the screen as the destination bitmap.
	 */
	fontblt->dst_bitmap.nshorts = DIV_BPW(BITMAP_WD);
	SETRECT(&fontblt->dst_bitmap.rect,0,0,BITMAP_WD,SCREEN_HT);

	/*
	 * Point the base at the beginning of the APA-8's bitmap.
	 */
	fontblt->dst_bitmap.base = (unsigned short *)
				   screen_sw[CONS_APA8].rwaddr;

	SETRECT(&fontblt->dst_rect,SCREEN_WD,0,SCREEN_WD+FONT_WD,font_ht);
}

/*
 * Increment the destination rectangle to the next spot for the next character.
 * Returns TRUE if the font will overflow into the queue area.
 */
apa8incr_dst_rect(dst_rect)
Blt_Rectangle *dst_rect;
{
	if ((dst_rect->corner_x + (2* FONT_WD)) > BITMAP_WD) {
		if ((dst_rect->corner_y+col_ht) > SCREEN_HT) {
			return(TRUE);
		}
		else {
			/*
			 * Go to the next line.
			 */
			SETRECT(dst_rect,
				SCREEN_WD,
				dst_rect->origin_y + col_ht,
				SCREEN_WD + FONT_WD,
				dst_rect->corner_y + col_ht);
		}
	}
	else {
		dst_rect->corner_x += FONT_WD;
		dst_rect->origin_x += FONT_WD;
	}
	return(FALSE);
}

/*
 * Loads the given font into the hidden screen area.  Keeps track of where
 * the font is put into the font area of the hidden bitmap.
 * If the font cannot be read then it returns FALSE (0) otherwise it
 * returns TRUE (1).
 */
apa8font_load(font,fontdata,fontBM)
register struct font_head *font;
register unsigned short *fontdata;
register Blt_Bitmap *fontBM;
{
	register i,j;
	fontbltstruct fontblt;
	int no_room;            /* indicates whether or not there is room
				   for the next character in the font area */
	unsigned short addrused[SIZEOF_DPLIST]; /* Used to keep track of the
						  addresses visited when
						  adding font characters */

	/*
	 * Set up the write and data mask.
	 */
	SET_WRITEMASK(NOMASK);
	SET_DATAMASK(HIDDEN_DM);

	/*
	 * Set up the APA-8 control register to use its system read/write
	 * calls in the X direction.
	 */
	SET_FC(DCR_VEN | DCR_SEN | DCR_SWR | DCR_X | HIDDEN_FUNC);

	/*
	 * Initialize the structure that will be passed to fontblt.
	 */
	apa8init_fontblt(&fontblt,fontBM);

	/*
	 * Read in each character using the dispatch information and
	 * place it in the hidden bitmap area.
	 */
	no_room = FALSE;
	for (i = 0;(i < SIZEOF_DPLIST) && (no_room == FALSE); i++) {
		/*
		 * Keep this address to indicate that we have already dealt
		 * with the character image at this address.  This address
		 * will be compared with later addresses if a match is found
		 * then that later character will just take the information
		 * generated from the earlier character and the character
		 * will not have to be loaded into the hidden area of the
		 * bitmap.  This saves hidden bitmap space.
		 */
		addrused[i] = font->dplist[i].addr;

		/*
		 * Have we been to this address before??  If so don't
		 * load the character.
		 */
		for (j=0;  j < i && font->dplist[i].addr != addrused[j]; j++);
	
		if (j < i && font->dplist[i].addr == addrused[j]) {
			/*
			 * we've already loaded this font into the 
			 * bitmap go get the info from this other character.
			 */
			bitmap_addr[i] = bitmap_addr[j];
		}
		else if (font->dplist[i].addr >= font->header.size || 
			 (font->dplist[i].nbytes == 0)) {
			/*
			 * There is no character for this dispatch 
			 * area, NULL indicates this.
			 */
			if (font->dplist[i].nbytes == 0)
				addrused[i] = font->header.size;
			bitmap_addr[i] = NULL;
		}
		else	{
			/*
			 * Set up the source rectangle to encompass the
			 * characters bit image in the source bitmap.
			 */
			SETRECT(&fontblt.src_rect,
				0,
				font->dplist[i].addr/BTOW(font_wd),
				font_wd,
				(font->dplist[i].addr/BTOW(font_wd))+font_ht);

			/*
			 * Call font bltter to put up the character image.
			 */
			bitmap_addr[i] = apa_fontblt(&fontblt,CONS_APA8);
			fontblt.dst_rect.origin_x += FONT_WD;
			fontblt.dst_rect.corner_x += FONT_WD;

			/*
			 * Put the same font image out there again.
			 */
			apa_fontblt(&fontblt,CONS_APA8);

			/*
			 * Set up the destination rectangle for the next blt.
			 */
			no_room = apa8incr_dst_rect(&fontblt.dst_rect);
		}
	}

	if ((i != SIZEOF_DPLIST) && (no_room == TRUE))
		return(FALSE);
	else
		return(TRUE);
}

/*
 * Clear all of the APA-8. (Including the hidden area.)
 */
apa8clear()
{
	register unsigned short *apa8scr;
	register i,j;

	apa8scr = (unsigned short *)screen_sw[CONS_APA8].rwaddr;
	SET_DATAMASK(CLEAR_DM);
	SET_WRITEMASK(NOMASK);

	/*
	 * Set up for adaptive write in x direction.
	 */
#ifdef BLACK_ON_WHITE
	SET_FC(DCR_VEN | DCR_SEN | DCR_ADWR | DCR_X | DEFAULT_FUNC);
#else
	SET_FC(DCR_VEN | DCR_SEN | DCR_ADWR | DCR_X | NOTFC(DEFAULT_FUNC));
#endif

	/*
	 * Clear the visible screen.
	 */
	for (i = SCREEN_HT + 1; --i ; apa8scr += MUL_2(HIDDEN_WORD_WD))
		for (j = SCREEN_WORD_WD + 1; --j; apa8scr += 2)
			*apa8scr = 0x0000;

	/*
	 * Clear hidden bitmap area.  NOTE: DEFAULT_FUNC may not be DCR_FCPB.
	 */
	apa8scr = (unsigned short *)screen_sw[CONS_APA8].rwaddr +
				    MUL_2(SCREEN_WORD_WD);
	SET_FC(DCR_VEN | DCR_SEN | DCR_ADWR | DCR_X | DCR_FCPB);

	for (i = SCREEN_HT + 1; --i ; apa8scr += MUL_2(SCREEN_WORD_WD))
		for (j = HIDDEN_WORD_WD + 1; --j; apa8scr +=2 )
			*apa8scr = 0x0000;
#ifndef MINIROOT
	bzero(apa8_save_screen,sizeof(apa8_save_screen));
#endif /* !MINIROOT */
}

/*
 * Reverse all of the APA-8 visible screen. 
 */
apa8_reverse_screen()
{
	register unsigned short *scraddr;
	register i,j;

	APA8REMOVE_CURSOR();

	scraddr = (unsigned short *)screen_sw[CONS_APA8].rwaddr;
	SET_DATAMASK(DEFAULT_DM);
	SET_WRITEMASK(NOMASK);

	/*
	 * Set up for adaptive write in x direction.
	 */
	SET_FC(DCR_VEN | DCR_SEN | DCR_ADWR | DCR_X | DEFAULT_FUNC);

	/*
	 * Clear the visible screen.
	 */
	for (i = SCREEN_HT + 1; --i ; scraddr += MUL_2(HIDDEN_WORD_WD))
		for (j = SCREEN_WORD_WD + 1; --j; scraddr +=2)
			*scraddr = *scraddr;

	APA8WRITE_CURSOR();

}

apa8_init_hdwr()
{
	/*
	 * Set up the write and data mask.
	 */
	SET_WRITEMASK(NOMASK);
	SET_DATAMASK(HIDDEN_DM);

	/*
	 * Set up the APA-8 control register to use its system read/write
	 * calls in the X direction.
	 */
	SET_FC(DCR_VEN | DCR_SEN | DCR_SWR | DCR_X | HIDDEN_FUNC);
}

/*
 * Returns TRUE (1) if the APA-8 screen is found.
 */
apa8_probe(addr)
unsigned short *addr;
{
	apa8_init_hdwr();

	/*
	 * Test for the APA-8 screen.
	 */
	*(unsigned short *)addr = 0x0F0F;
	if (*(unsigned short *)addr != 0x0F0F) {
		return(FALSE);
	}

	return(TRUE);
}

/*
 * Initializes the APA-8 screen.
 */
apa8_screen_init()
{
	RESET_CURSOR;

	/*
	 * Initialize the APA-8.
	 */
	apa8_init_hdwr();

	/*
	 * Clear the visible screen and the hidden memory area.
	 */
	apa8clear();

	/*
	 * Calculate the number of columns and lines on the screen based on
	 * the maximum width and height of this font.  (NOTE: Assume that
	 * the font is fixed width.)
	 */
	apa8set_fontinfo();

	/*
	 * Initialize the font. (Put it in the hidden bitmap area.)
	 */
	if (apa8font_load(&apa8fonthead,apa8fontBits,&apa8fontBM) == FALSE) {
		printf("Can't create APA-8 font! Abort.\r\n");
		return;
	}

	/*
	 * set up where to start frame buffer (screen bitmap).
	 */
	apa8scr = (unsigned short *)screen_sw[CONS_APA8].rwaddr;
	rotate_amt = 0;
#ifdef BLACK_ON_WHITE
	apa8_color = FOREGROUND_COLOR;	/* specify which color is black */
#else
	apa8_color = BACKGROUND_COLOR;	/* specify which color is black */
#endif
#ifndef MINIROOT
	apa8_save_pos = 0;
#endif /* !MINIROOT */

	APA8WRITE_CURSOR();
}

/*
 * NOTE: The font_ht MUST be an even number.
 */
apa8write_char(fromapa8scr,toapa8scr,func,attribute)
register unsigned short *fromapa8scr, *toapa8scr;
register unsigned short func;  /* function used to write the character
				  to the screen. */
register int attribute;
{
	/*
	 * Set up the writemask and datamask.
	 */
	SET_WRITEMASK(x_rotatemasks[rotate_amt]);

	/*
	 * Write out the character.
	 */
	if (attribute & HI_INTENSITY) {
		register int rot=rotate_amt+1;
		SET_DATAMASK(ALL_DM);
		SET_FC(DCR_VEN | DCR_SEN | DCR_SWR | DCR_X | 
			(attribute & REVERSE_VIDEO ? DCR_FCOR : DCR_FCNOR)
			 | rotate_amt);
		BOLD_CHAR_LOOP(toapa8scr,fromapa8scr,NEXT_SL,rot);
	} else {
		SET_FC(DCR_VEN | DCR_SEN | DCR_ADWR | DCR_X | func);
		SET_DATAMASK(DEFAULT_DM);
		CHAR_LOOP(toapa8scr,fromapa8scr,NEXT_SL);
	}
	if (attribute & UNDERLINE_VIDEO) {
		SET_DATAMASK(RDWR_DM);
		SET_FC(DCR_VEN | DCR_SEN | DCR_SWR | DCR_X | RDWR_FUNC);
		toapa8scr[NEXT_SL*13] = (unsigned short)
			(attribute & REVERSE_VIDEO ? 0xffff : 0x0000);
	}
}

apa8_screen_putc(ch,attr,fgcolor,bgcolor)
register char ch;
register int attr;
unsigned fgcolor,bgcolor;
{
	register unsigned short *addr;
	register unsigned short func;
	int	s;

	/* only two entries are valid */
	fgcolor &= 0x1;
	bgcolor &= 0x1;
	/* if they're equal, no character printed */
	if (fgcolor == bgcolor) {
		attr &= ~ UNDERLINE_VIDEO;
		ch = ' ';
	}
	/* if bgcolor == black, reverse the sence of REV_VID */
	if (bgcolor == apa8_color) { 
		attr ^= REVERSE_VIDEO;
	}

	addr = bitmap_addr[ch];

#ifndef MINIROOT
	apa8_save_screen[apa8_save_pos] = ch;
#endif /* !MINIROOT */
	if (addr != NULL) {
		/*
		 * Put the character onto the screen where the cursor is.
		 */
		APA8REMOVE_CURSOR();

		if (attr & REVERSE_VIDEO) {
			register unsigned short *scr = (apa8scr + 1);

			/*
			 * Clear the 9th bit.
			 * 
			 * Set up the writemask and datamask.
			 */
			SET_WRITEMASK(y2_rotatemasks[rotate_amt]);
			SET_DATAMASK(CLEAR_DM);

			/*
			 * Set up the control register to do adaptive read
			 * and write in Y direction.
			 */
			func = NOTFC(DEFAULT_FUNC) | rotate_amt;
			SET_FC(DCR_VEN | DCR_SEN | DCR_ADWR | func);

			CHAR_BLANK_LOOP(scr,func,MUL_2(NEXT_SL));
		} else {
			register unsigned short *scr = (apa8scr + 1);

			/*
			 * Set the 9th bit.
			 * 
			 * Set up the writemask and datamask.
			 */
			SET_WRITEMASK(y2_rotatemasks[rotate_amt]);
			SET_DATAMASK(CLEAR_DM);

			/*
			 * Set up the control register to do adaptive read
			 * and write in Y direction.
			 */
			func = DEFAULT_FUNC | rotate_amt;
			SET_FC(DCR_VEN | DCR_SEN | DCR_ADWR | func);

			CHAR_BLANK_LOOP(scr,func,MUL_2(NEXT_SL));
		}

		apa8write_char(addr,apa8scr,func,attr);

		/*
		 * Don't bother writing the cursor back out.  The emulator
		 * must call pos_cursor anyway.
		 * APA8WRITE_CURSOR();
		 */
	}
}

apa8_pos_cursor(column_number,line_number)
register int column_number;
register int line_number;
{
	APA8REMOVE_CURSOR();
	apa8scr = (unsigned short *)(LINE_TO_BUS(line_number) +
						 COL_TO_BUS(column_number));
#ifndef MINIROOT
	apa8_save_pos = lp_pos_cursor(column_number,line_number,CONS_APA8);
#endif /* !MINIROOT */

	rotate_amt = COL_TO_ROTATE(column_number);

	/*
	 * removed the cursor now write it back out.
	 */
	APA8WRITE_CURSOR();
}

/*
 * Emulate a 2 entry color table for the aed.
 * Note: This does allow the two color table entries to differ
 */
apa8_color_table(table_entry,red,green,blue,flags)
	unsigned table_entry,red,green,blue;
	int	flags;
{
	int	s;
	int	current,wanted,one_count;

	/* there's only two table entries */
	if (table_entry > 1) {
		return(-1);
	}

	switch(flags) {
	/* monochrome displays flip colors in response to most flags */
	case	COLOR_FG_INC:
	case	COLOR_BG_INC:
	case	COLOR_FG_DEC:
	case	COLOR_BG_DEC:
	case	REVERSE_COLOR:
		apa8_color ^= 1;
		apa8_reverse_screen();
		return(0);

	case	COLOR_SET:
		/* now see if we need to change anything */
		one_count = 0;
		current = apa8_color ^ table_entry;

		/* map the RGB color to a white or black */
		if (red & SCREEN_HIGH_BIT) one_count++;
		if (blue & SCREEN_HIGH_BIT) one_count++;
		if (green & SCREEN_HIGH_BIT) one_count++;
		wanted = (one_count >= 2);

		/* if a change needs to be made, make it */
		if (current != wanted) {
			apa8_color ^= 1;
			apa8_reverse_screen();
		}
		return(0);
	/* can't handle other flags */
	default:
		return(-1);
	}
}

/*
 * On a given line (logical line usually numbering 0 to 28) clear all the
 * scan lines between the first and last bytes (using func) inclusive.
 */
apa8clearline(first,last,line,func)
register int first;	/* first byte to be cleared */
int last;	 	/* last byte to be cleared */
int line;		/* line at which to start */
unsigned short func; /* the function used to clear the screen */
{
	register unsigned short *tmp_apa8scr;
	register i,j,width;

	tmp_apa8scr = (unsigned short *)(LINE_TO_BUS(line) + BM_TO_BUS(first));
	width = last - first + 1;

	/*
	 * Set up the control register to do adaptive write in Y direction.
	 * Because we are going in the Y direction, we want to move 2
	 * scanlines down (hence we use MUL_2).
	 */
	SET_FC(DCR_VEN | DCR_SEN | DCR_ADWR | func);
	for (i = DIV_2(col_ht) + 1; --i; ) {
		for (j = width + 1; --j; tmp_apa8scr++ ) {
			*tmp_apa8scr = i;
		}
		tmp_apa8scr += (MUL_2(NEXT_SL) - width);
	}
}


apa8_screen_blank(attr,sline,scol,eline,ecol,fgcolor,bgcolor)
int attr;   /* attribute of how to clear the screen (i.e. reverse video, etc) */int sline,scol; /* The starting line and column */
int eline,ecol; /* The ending line and column */
unsigned fgcolor,bgcolor;
{
	register unsigned short *tmp_apa8scr;
	register i,s;
	register sbyte,ebyte;
	unsigned short func;

#ifndef MINIROOT
	lp_screen_blank(apa8_save_screen,sline,scol,eline,ecol,CONS_APA8);
#endif /* !MINIROOT */
	/* only two entries are valid */
	fgcolor &= 0x1;
	bgcolor &= 0x1;
	/* if they're equal, no character printed */
	if (fgcolor == bgcolor) {
		attr &= ~ UNDERLINE_VIDEO;
	}
	/* if bgcolor == black, reverse the sence of REV_VID */
	if (bgcolor == apa8_color) { 
		attr ^= REVERSE_VIDEO;
	}
	APA8REMOVE_CURSOR();

	if (attr & REVERSE_VIDEO) {
		func = NOTFC(DEFAULT_FUNC);
	} else
		func = DEFAULT_FUNC;

	/*
	 * Set up the datamask to clear parts of the screen.
	 */
	SET_DATAMASK(CLEAR_DM);

	/*
	 * Special case the easy loop where scol is the first column and
	 * ecol is the last column.
	 */
	if (scol == FIRSTCOL && ecol == col_MAX) {
		SET_WRITEMASK(NOMASK);
		/*
		 * Clear from the start line to the end line.
		 */
		tmp_apa8scr = (unsigned short *)LINE_TO_BUS(sline);

		/*
		 * Set up the control register to do adaptive write 
		 * in X direction.
		 */
		SET_FC(DCR_VEN | DCR_SEN | DCR_ADWR | DCR_X | func);
		for (i = ((eline - sline + 1) * col_ht) + 1; --i; ) {
			BLANK_LOOP(tmp_apa8scr,i,2);
			tmp_apa8scr += NEXT_SL;
		}
		return;
	}

	/*
	 * Right now we are dealing with logical columns which will span
	 * 2 bytes.  Clear the first partial byte and the last partial byte
	 * in the first and last line.  Then deal with clearing the screen
	 * in terms of bytes.
	 */

	/*
	 * Calculate the starting byte.
	 */
	sbyte = COL_TO_BM(scol);

	/*
	 * If the starting column is not the first column and the start column
	 * does not fall on a byte boundary then clear part of of the first
	 * byte.
	 */
	if (scol > FIRSTCOL && MOD_BPB(scol*col_wd)) {
		/*
		 * Set up the writemask with the mask for the first half of
		 * the character.
		 */
		SET_WRITEMASK(y1_rotatemasks[COL_TO_ROTATE(scol)]);
		tmp_apa8scr = (unsigned short *)(LINE_TO_BUS(sline) +
						 COL_TO_BUS(scol));

		/*
		 * Set up the control register to do adaptive write 
		 * in Y direction.  Because we are going in the Y direction,
		 * we want to move 2 scanlines down (hence we use MUL_2).
		 */
		SET_FC(DCR_VEN | DCR_SEN | DCR_ADWR | func);
		for (i = (col_ht >> 1) + 1; --i ; ) {
			*tmp_apa8scr = i;
			tmp_apa8scr += MUL_2(NEXT_SL);
		}

		/*
		 * Finished with this byte move forward to the next one.
		 */
		sbyte += 1;
	}

	/*
	 * Calculate the byte boundrary for the last bit in the end column.
	 */
	ebyte = COL_TO_BM(ecol) + 1;

	/*
	 * If the end columns given is not the last column and the next
	 * column after the end column doesn't start on a NEW byte then
	 * only part of that byte should be cleared so we clear it here.
	 */
	if (ecol < col_MAX && MOD_BPB((ecol+1)*col_wd)) {
		/*
		 * Set up the writemask with the mask for the second half of
		 * the character. (The first half will be cleared below.)
		 */
		SET_WRITEMASK(y2_rotatemasks[COL_TO_ROTATE(ecol)]);
		tmp_apa8scr = (unsigned short *)(LINE_TO_BUS(eline) +
						 COL_TO_BUS(ecol) + 2);

		/*
		 * Set up the control register to do adaptive write 
		 * in Y direction.  Because we are going in the Y direction,
		 * we want to move 2 scanlines down (hence we use MUL_2).
		 */
		SET_FC(DCR_VEN | DCR_SEN | DCR_ADWR | func);
		for (i = (col_ht >> 1) + 1; --i ; ) {
			*tmp_apa8scr = i;
			tmp_apa8scr += MUL_2(NEXT_SL);
		}

		/*
		 * Finished with this byte move back.
		 */
		ebyte -= 1;
	}

	SET_WRITEMASK(NOMASK);

	if ((scol > FIRSTCOL) && (sline < eline)) {
		/*
		 * Clear the first partial line which is from start 
		 * byte figured above, to the end of the line.
		 */
		apa8clearline(sbyte,SCREEN_BYTE_WD,sline,func);

		/*
		 * Cleared this line.
		 */
		sbyte = 0;
		scol = FIRSTCOL;
		sline += 1;
	}

	if ((ecol < col_MAX) && (sline < eline)) {
		/*
		 * clear from first byte in a scanline to the ebyte figured
		 * above.
		 */
		apa8clearline(0,ebyte,eline,func);

		/*
		 * Cleared this line.
		 */
		ebyte = SCREEN_BYTE_WD;
		ecol = col_MAX;
		eline -= 1;
	}

	/*
	 * Clear the area inbetween sline and eline.
	 */
	if (sline == eline) {
		if (ebyte >= sbyte)
			apa8clearline(sbyte,ebyte,sline,func);
	}
	else if (ecol == col_MAX && scol == FIRSTCOL) {
		/*
		 * Clear from the start line to the end line.
		 */
		tmp_apa8scr = (unsigned short *)LINE_TO_BUS(sline);

		/*
		 * Set up the control register to do adaptive write 
		 * in X direction.
		 */
		SET_FC(DCR_VEN | DCR_SEN | DCR_ADWR | DCR_X | func);
		for (i = ((eline - sline + 1) * col_ht) + 1; --i; ) {
			BLANK_LOOP(tmp_apa8scr,i,2);
			tmp_apa8scr += NEXT_SL;
		}
	}


	/*
	 * Don't bother writing the cursor out because the emulator will be
	 * calling pos_cursor anyway.
	 * APA8WRITE_CURSOR();
	 */
}

/*
 * scroll_down takes the block of lines starting at start_line and ending
 * at the bottom of the screen minus n_scrolls number of lines, and moves them 
 * down to the end of the screen.
 * For example, 
 *	Before:	1. XYZ
 *		2. ABC
 *		3. FGH
 *
 *	After scrolling 2 lines down:
 *		~
 *		~
 *		1. XYZ
 */

apa8scroll_down(src_line,dst_line,nlines)
int src_line;	/* Line number to copy from */
int dst_line;	/* Line number to copy to */
int nlines;	/* Number of lines to be copied */
{
	register unsigned short *tmp_to;   /* bitmap pointer to destination. */
	register unsigned short *tmp_from; /* bitmap pointer to source. */
	register i,s;	   /* loop counter for scanlines and bytes per
			      scanline */

	/*
	 * Note the difference between SCAN lines (0 - 512)
	 * and lines. (0 - LINE_MAX)
	 */
	tmp_from = (unsigned short *)(LINE_TO_BUS(src_line + nlines) - 
				      MUL_2(NEXT_SL));

	/*
	 * Set up the destination bitmap pointer to be at the last line,
	 * col_ht number of SCAN lines down.
	 */
	tmp_to = (unsigned short *)(LINE_TO_BUS(dst_line + nlines) -
				    MUL_2(NEXT_SL));

	/*
	 * Set up for the number of scanlines to be moved. (Plus 2 because:
	 * the while loop immediately subtracts 1 and tmp_from INCLUDES the
	 * the line to be copied.)
	 */
	i = (nlines * col_ht) + 1;


	SET_WRITEMASK(NOMASK);
	SET_DATAMASK(DEFAULT_DM);

	/*
	 * set up the control register to adaptive read/write in the X
	 * direction using the default function so as not to change anything.
	 */
	SET_FC(DCR_VEN | DCR_SEN | DCR_X | DCR_ADWR | DCR_FCPB); 


	/*
	 * foreach scan line do...
	 */
	while (--i) {
		/*
		 * foreach word in the width of the visible screen do...
		 */ 
		/*
		 * SCREEN_LOOP is 45 of these instructions in a row, a
		 * totally unrolled loop.
		 */
		SCREEN_LOOP(tmp_to,tmp_from,2);

		/*
		 * Go to the next scan line above.
		 */
		tmp_to -= NEXT_SL;
		tmp_from -= NEXT_SL;
	}
}

/*
 * scroll_up takes the block of lines starting n_scrolls number of lines below
 * start_line and ending at the bottom of the screen, and moves them up to
 * start_line.
 * For example, 
 *	Before:	1. XYZ
 *		2. ABC
 *		3. FGH
 *
 *	After scrolling 2 lines up:
 *		3. FGH
 *		~
 *		~
 */

apa8scroll_up(src_line,dst_line,nlines)
int src_line;	/* Line number to copy from */
int dst_line;	/* Line number to copy to */
int nlines;	/* Number of lines to be copied */
{
	register unsigned short *tmp_to;   /* bitmap pointer to destination. */
	register unsigned short *tmp_from; /* bitmap pointer to source. */
	register i,s;	   /* loop counter for scanlines and bytes per
			      scanline */

	/*
	 * Set up the destination bitmap pointer to be the starting scanline.
	 */
	tmp_to = (unsigned short *)LINE_TO_BUS(dst_line);

	/*
	 * Set up the source bitmap pointer to be n_scrolls LINEs in front
	 * of the  destination bitmap pointer.
	 * Note:  The difference between SCAN lines (0 - 512) and LINEs.
	 * (0 - line_MAX)
	 */
	tmp_from = (unsigned short *)LINE_TO_BUS(src_line);

	/*
	 * Set up for the number of scanlines to be moved.
	 * NOTE: +1 because the while loop immediately subtracts 1.
	 */
	i =  (nlines * col_ht) + 1;


	/*
	 * Set up the data and writemask.
	 */
	SET_WRITEMASK(NOMASK);
	SET_DATAMASK(DEFAULT_DM);

	/*
	 * set up the control register to adaptive read/write in the X
	 * direction using the default function so as not to change anything.
	 */
	SET_FC(DCR_VEN | DCR_SEN | DCR_X | DCR_ADWR | DCR_FCPB); 

	/*
	 * foreach scan line do...
	 */
	while (--i) {
		/*
		 * foreach word in each scanline do...
		 */
		/*
		 * SCREEN_LOOP is 45 of these instructions in a row, a
		 * totally unrolled loop.
		 */
		SCREEN_LOOP(tmp_to,tmp_from,2);
		/*
		 * Go to the next scan line below.
		 */
		tmp_to += NEXT_SL;
		tmp_from += NEXT_SL;
	}
}

apa8_screen_move(line_top,line_bottom,line_dest)
long line_top,line_bottom,line_dest;
{
#ifndef MINIROOT
	lp_screen_move(apa8_save_screen,line_top,line_bottom,line_dest,CONS_APA8);
#endif /* !MINIROOT */
	/*
	 * Remove the cursor before doing any scrolling.
	 */
	APA8REMOVE_CURSOR();

	/*
	 * Decide which way we are scrolling.
	 */
	if (line_top < line_dest) {
		apa8scroll_down(line_top,line_dest,line_bottom-line_top+1);
	}
	else if (line_top > line_dest) {
		apa8scroll_up(line_top,line_dest,line_bottom-line_top+1);
	}

	/*
	 * Don't bother writing the cursor out because the emulator will
	 * be calling pos_cursor anyway.
	 * APA8WRITE_CURSOR();
	 */
}

#ifndef MINIROOT
apa8_screen_print(si,flag)
register SCREEN_INFO *si;
{
	lp_screen_print(apa8_save_screen,si,flag,CONS_APA8);
}
#endif /* !MINIROOT */
#endif NAPAETTY
