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

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

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

/*
 * This file contains all the routines written for the APA-16 console driver.
 * These routines initialize the queue area, load the font into the hidden
 * font area of the APA-16, write characters to the screen with underline,
 * bold and reverse video as attributes, position the cursor, blank the screen,
 * and move the screen.
 */

#include "apasixteen.h"
#if NAPASIXTEEN > 0

#include "types.h"
extern unsigned short *apa_fontblt();

#include "sa.h"

#define APA16_FONT	2	/* apa_fontblt() id */

typedef struct {
        unsigned short *rwaddr;
} screen_stuff;

#include "apa16tty.h"
#ifdef USE_APA8_FONT
#include "apaeight.h"
#include "apaeightc.h"
#if (NAPAEIGHT <= 0) && (NAPAEIGHTC <=0)
#include "apa8tty_font.h"
#else
extern struct font_head apa8fonthead;
extern unsigned short apa8fontBits[];
extern Blt_Bitmap apa8fontBM;
#endif NAPAEIGHT
#define apa16fonthead apa8fonthead
#define apa16fontBits apa8fontBits 
#define apa16fontBM   apa8fontBM 
#else
#include "apa16tty_font.h"
#endif USE_APA8_FONT

#include "screen_conf.h"
extern struct screen_sw screen_sw[];

/*
 * X,Y coordinate for the cursor in PELS,SCANLINES.
 */
static short apa16cursorX;
static short apa16cursorY;

/*
 * If the font is a little small we shrink the size of the screen.
 * Default values for these are:
 *	firstPel = 0
 *	lastPel = SCREEN_WD
 *	firstScan = 0
 *	lastScan = SCREEN_HT
 */
static short firstPel,lastPel;
static short firstScan,lastScan;

/*
 * To make the screen look pretty about a 1/2 character border on each side
 * is defined.
 */
static short borderFPel,borderFScan,borderLPel,borderLScan;

/*
 * The amount the screen has been enlarged so that the characters are not
 * right against the edge.
 */
static short pel_pad, scan_pad;

/*
 * Underline information.
 */
#define ULINE_WD	col_wd

#ifdef APA16_SMALL 

#define ULINE_HT	1
#define PADCOL_HT	(ULINE_HT + 2)

#else APA16_SMALL

#define ULINE_HT	2

#ifdef APA16_LONG_PAD_HT
#define PADCOL_HT	(ULINE_HT + 2)
#else
#define PADCOL_HT	(ULINE_HT + 1)
#endif APA16_LONG_PAD_HT

#endif APA16_SMALL

/*
 * Font information.
 */
#define PADCOL_WD	0

#define MAX_COLUMNS	80

static 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 */

#define col_MAX screen_sw[CONS_APA16].width 
#define line_MAX screen_sw[CONS_APA16].lines
/*
 * pointer to screen print buffer
 */
char apa16_buffer[80*50];
int  apa16_buf_pointer;
/*
 * Each element in this array contains the bottom right points in the hidden
 * bitmap of the image of the character.  The ascii representation of the
 * character ('a' = 0x61) indexes this array.
 */
static struct corner {
	short x,y;
} char_corner[SIZEOF_DPLIST];

/*
 * Keep track of where in the queue each command has space allocated to it.
 */
QUEinfo QUEinstr[Q_NCOMMANDS];

/*
 * Pointer to the queue area in the hidden bitmap area of the APA-16.
 */
unsigned short *QUEptr;

/*
 * defines used to indicate whether or not to read from the source.
 * NOTE: Changing the value of MUST_PRIME may have bad side
 *	 effects because the variable "preload_src" is added to something in
 *	 calculateOffsets.
 */
#define MUST_PRIME 1
#define DONT_PRIME 0

static unsigned short prev_apa16hidden_moved;

static unsigned short apa16locator_busy = 0;

extern unsigned short *apa_fontblt();

/*
 * Keep track of whether or not the apa-16 has been initialized.
 */
static short un_init;

/*
 * Sets up the font information, the column information and the amount of
 * text that will fit on a screen.  This is not needed for the final version
 * of the driver but it is very useful for checking various fonts.
 */
apa16set_fontinfo()
{
	register extraPel, extraScan;

	/*
	 * 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 = apa16fonthead.header.maxx;
	font_ht = apa16fonthead.header.maxy;
	col_wd = font_wd + PADCOL_WD;
	col_ht = font_ht + PADCOL_HT;

	/*
	 * Figure out the size of the screen to be used.
	 */
	firstPel = 0;
	lastPel = SCREEN_WD;
	firstScan = 0;
	lastScan = SCREEN_HT;

	/*
	 * Figure out the max number of columns.  Center the columns on the
	 * screen.  
	 */
	if ((col_MAX = (SCREEN_WD/col_wd)) >= MAX_COLUMNS) {
		extraPel = SCREEN_WD - (MAX_COLUMNS * col_wd);
		firstPel += extraPel/2;
		lastPel -= (extraPel - (extraPel/2));
		col_MAX = MAX_COLUMNS;
	}
	else if ((extraPel = SCREEN_WD%col_wd) != 0) {
		firstPel += extraPel/2;
		lastPel -= (extraPel - (extraPel/2));
	}

	/*
	 * Figure out the max number of lines and center them on the screen.
	 */
	line_MAX = SCREEN_HT/col_ht;
	if ((extraScan = SCREEN_HT%col_ht) != 0) {
		firstScan += extraScan/2;
		lastScan -= (extraScan - (extraScan/2));
	}


	/*
	 * Give the screen about 1/2 a character border. (Makes it look
	 * pretty.)
	 */
	if ((firstPel - col_wd/2) < 1 || (lastPel + col_wd/2) > SCREEN_WD) {
		if ((firstPel-col_wd/3) < 1 || (lastPel+col_wd/3) > SCREEN_WD) {
			if ((firstPel - 1) < 1 || (lastPel + 1) > SCREEN_WD) {
				pel_pad = 0;
			}
			else {
				pel_pad = 1;
			}
		}
		else {
			pel_pad = col_wd/3;
		}
	}
	else {
		pel_pad = col_wd/2;
	}

	borderFPel = firstPel - pel_pad;
	borderLPel = lastPel + pel_pad;

	if ((firstScan - (col_ht/3)) < 1 ||
			 (lastScan + (col_ht/3)) > SCREEN_HT) {
		if ((firstScan - (col_ht/4)) < 1 ||
		    		(lastScan + (col_ht/4)) > SCREEN_HT) {
			if ((firstScan - 1) < 1 || (lastScan + 1) > SCREEN_HT) {
				scan_pad = 0;
			}
			else {
				scan_pad = 1;
			}
		}
		else {
			scan_pad = col_ht/4;
		}
	}
	else {
		scan_pad = col_ht/3;
	}

	borderFScan = firstScan - scan_pad;
	borderLScan = lastScan + scan_pad;
}

/*
 * For each area of this program that needs to use the queue, allocate 
 * queue space.  Each area may have some queue commands that never change.
 * If this is the case then those commands are set in here so that only those
 * areas that need to be changed, are changed.
 */
apa16init_que()
{
	QUEptr = (unsigned short *)(LAST_QUE_APA16BASE);

	/*
	 * Set up the general area where any destination only execute commands
	 * can be set up.
	 */
	QUEinstr[Q_GENBLANK].Qptr = QUEptr;
	QUEinstr[Q_GENBLANK].Qaddr = SPTR_TO_QPTR(QUEptr);
	QUEinstr[Q_GENBLANK].nwords = DSTCMDWORDS;
	/*
	 * Only one execute command. (For every execute command a counter
	 * on the APA-16 must be incremented.)
	 */
	QUEinstr[Q_GENBLANK].nexec = 1;

	/*
	 * Save space for this command.
	 */
	QUEptr -= QUEinstr[Q_GENBLANK].nwords;

	/*
	 * Set up the "put a character on the screen" command queue area. Used
	 * by apa16write_char.
	 */
	QUEinstr[Q_BLTCHAR].Qptr = QUEptr;
	QUEinstr[Q_BLTCHAR].Qaddr = SPTR_TO_QPTR(QUEptr);
	QUEinstr[Q_BLTCHAR].nwords = DSTCMDWORDS + RECTCMDWORDS;
	/*
	 * 2 execute commands. (For every execute command a counter
	 * on the APA-16 must be incremented.)
	 */
	QUEinstr[Q_BLTCHAR].nexec = 2;

	/*
	 * Set up the queue to first clear the spot where the character is
	 * to go.  Then set up to move the character into place.
	 */

	/*
	 * Load the the instructions that tell the queue command processor
	 * to load:
	 *	- The X and Y destination registers with the bottom right
	 *	  corner of the screen area where a character is to be written. 
	 *
	 * 	- The destination width and height registers with the width
	 * 	  and height of the character.
	 *
	 * Then load an execute command instruction with a CLEAR logic function
	 * and a Raster Operation Type (ROT) of write the destination only.
	 *
	 * NOTE: The X and Y destination registers will change values
	 * 	 every time a character is written and the logic function will
	 *       change from CLEAR to SET depending on whether the attribute
	 *       is NORMAL_VIDEO or REVERSE_VIDEO. The width and height
	 * 	 commands will not change at all.
	 */
	*QUEptr-- = BUILD_REGLOAD(XDST_REG,0);
	*QUEptr-- = BUILD_REGLOAD(YDST_REG,0);
	*QUEptr-- = BUILD_REGLOAD(WIDTH_REG,col_wd);
	*QUEptr-- = BUILD_REGLOAD(HEIGHT_REG,col_ht);
	*QUEptr-- = BUILD_EXCMD(DECR_QUE_COUNT,ROT_WRDST,LF_CLEAR);

	/*
	 * Load the the instructions that tell the queue command processor
	 * to load:
	 *	- The X and Y destination registers with the bottom right
	 *	  corner of the screen area where a character is to be written. 
	 *
	 *	- The X and Y source registers with the bottom right
	 *	  corner of the hidden screen area where a character is 
	 *	  located.
	 *
	 * 	- The destination width and height registers with the width
	 * 	  and height of the character.
	 *
	 * Then load an execute command instruction with a XOR logic function
	 * and a Raster Operation Type (ROT) of copy source to destination.
	 *
	 * NOTE: The X and Y source and destination registers will change
	 *       values every time a character is written. The width, height
	 *       and execute commands will not change at all.
	 */
	*QUEptr-- = BUILD_REGLOAD(XDST_REG,0);
	*QUEptr-- = BUILD_REGLOAD(RECT_YDST_REG,0);
	*QUEptr-- = BUILD_REGLOAD(XSRC_REG,0);
	*QUEptr-- = BUILD_REGLOAD(YSRC_REG,0);
	*QUEptr-- = BUILD_REGLOAD(WIDTH_REG,font_wd);
	*QUEptr-- = BUILD_REGLOAD(RECT_HEIGHT_REG,font_ht);
	*QUEptr-- = BUILD_EXCMD(DECR_QUE_COUNT,ROT_RECTCP,LF_DSTxorSRC);

	/*
	 * Set up the bold character area.
	 */
	QUEinstr[Q_BOLDCHAR].Qptr = QUEptr;
	QUEinstr[Q_BOLDCHAR].Qaddr = SPTR_TO_QPTR(QUEptr);
	QUEinstr[Q_BOLDCHAR].nwords = RECTCMDWORDS;
	/*
	 * 1 execute command. (For every execute command a counter
	 * on the APA-16 must be incremented.)
	 */
	QUEinstr[Q_BOLDCHAR].nexec = 1;

	/*
	 * Load the the instructions that tell the queue command processor
	 * to load:
	 *	- The X and Y destination registers with the bottom right
	 *	  corner of the screen area where a character is to be written. 
	 *
	 *	- The X and Y source registers with the bottom right
	 *	  corner of the character just written.
	 *
	 * 	- The destination width and height registers with the width - 1
	 * 	  and height of the character.
	 *
	 * Then load an execute command instruction with a OR SOURCE logic
	 * function and a Raster Operation Type (ROT) of copy source to
	 * destination.
	 *
	 * NOTE: The X and Y source and destination registers will change
	 *       values every time a character is written. The width, height
	 *       and execute commands will not change at all.
	 */
	*QUEptr-- = BUILD_REGLOAD(XDST_REG,0);
	*QUEptr-- = BUILD_REGLOAD(RECT_YDST_REG,0);
	*QUEptr-- = BUILD_REGLOAD(XSRC_REG,0);
	*QUEptr-- = BUILD_REGLOAD(YSRC_REG,0);
	*QUEptr-- = BUILD_REGLOAD(WIDTH_REG,font_wd-1);
	*QUEptr-- = BUILD_REGLOAD(RECT_HEIGHT_REG,font_ht);
	*QUEptr-- = BUILD_EXCMD(DECR_QUE_COUNT,ROT_RECTCP,LF_DSTorSRC);

	/*
	 * Set up the underline command queue area. Used by underline.
	 */
	QUEinstr[Q_ULINE].Qptr = QUEptr;
	QUEinstr[Q_ULINE].Qaddr = SPTR_TO_QPTR(QUEptr);
	QUEinstr[Q_ULINE].nwords = DSTCMDWORDS;
	/*
	 * 1 execute command. (For every execute command a counter
	 * on the APA-16 must be incremented.)
	 */
	QUEinstr[Q_ULINE].nexec = 1;

	/*
	 * Load the the instructions that tell the queue command processor
	 * to load:
	 *	- The X and Y destination registers with the bottom right
	 *	  corner of the screen area where an underline is to be written.
	 *
	 * 	- The destination width and height registers with the width
	 * 	  and the underline.
	 *
	 * Then load an execute command instruction with a not destination
	 * logic function and a Raster Operation Type (ROT) of write the 
	 * destination only.
	 *
	 * NOTE: The X and Y destination registers will change values
	 * 	 every time an underline is written. The width, height
	 * 	 and execute commands will not change at all.
	 */
	*QUEptr-- = BUILD_REGLOAD(XDST_REG,0);
	*QUEptr-- = BUILD_REGLOAD(YDST_REG,0);
	*QUEptr-- = BUILD_REGLOAD(WIDTH_REG,ULINE_WD);
	*QUEptr-- = BUILD_REGLOAD(HEIGHT_REG,ULINE_HT);
	*QUEptr-- = BUILD_EXCMD(DECR_QUE_COUNT,ROT_WRDST,LF_NotDST);

	/*
	 * save room for the cursor command (even though standalone doesn't
	 * use it, the debugger may have to when sharing the display with a
	 * running kernel.
	 */
	QUEptr -= 5;

	/*
	 * Set up the blank command queue area. Used by apa16_screen_blank.
	 */
	QUEinstr[Q_BLANK].Qptr = QUEptr;
	QUEinstr[Q_BLANK].Qaddr = SPTR_TO_QPTR(QUEptr);
	QUEinstr[Q_BLANK].nwords = 3 * DSTCMDWORDS;
	/*
	 * The number of execute commands cannot be filled in at this time
	 * because it will vary depending on the area to be blanked.
	 */
	QUEinstr[Q_BLANK].nexec = 0;

	QUEptr -= QUEinstr[Q_BLANK].nwords;

	/*
	 * Set up the screen move command queue area. Used by
	 * apa16_screen_move.
	 */
	QUEinstr[Q_MOVE].Qptr = QUEptr;
	QUEinstr[Q_MOVE].Qaddr = SPTR_TO_QPTR(QUEptr);
	QUEinstr[Q_MOVE].nwords = RECTCMDWORDS;
	/*
	 * One execute command.
	 */
	QUEinstr[Q_MOVE].nexec = 1;

	/*
	 * Load the the instructions that tell the queue command processor
	 * to load:
	 *	- The X and Y destination registers with the bottom right
	 *	  corner of the screen area where the destination rectangle
	 *	  resides. 
	 *
	 *	- The X and Y source registers with the bottom right
	 *	  corner of the source screen area.
	 *
	 * 	- The destination width and height registers with the width
	 * 	  and height of the rectangle to be moved.
	 *
	 * Then load an execute command instruction with a COPY SOURCE
	 * logic function and a Raster Operation Type (ROT) of copy 
	 * source to destination.
	 *
	 * NOTE: Only the Y coordinates will change.  This means the 
	 * 	 destination Y, the source Y and the height will always change.
	 */
	*QUEptr-- = BUILD_REGLOAD(XDST_REG,lastPel);
	*QUEptr-- = BUILD_REGLOAD(RECT_YDST_REG,0);
	*QUEptr-- = BUILD_REGLOAD(XSRC_REG,lastPel);
	*QUEptr-- = BUILD_REGLOAD(YSRC_REG,0);
	*QUEptr-- = BUILD_REGLOAD(WIDTH_REG,lastPel - firstPel);
	*QUEptr-- = BUILD_REGLOAD(RECT_HEIGHT_REG,0);
	*QUEptr-- = BUILD_EXCMD(DECR_QUE_COUNT,ROT_RECTCP,LF_COPYSRC);

	/*
	 * The hardware cursor is made up of an "AND" area and an "XOR" area.
	 * The AND area must be set and the XOR area must be cleared.
	 */
	QUEinstr[Q_CLEARXOR].Qptr = QUEptr;
	QUEinstr[Q_CLEARXOR].Qaddr = SPTR_TO_QPTR(QUEptr);
	QUEinstr[Q_CLEARXOR].nwords = DSTCMDWORDS;
	/*
	 * 1 execute command. (For every execute command a counter
	 * on the APA-16 must be incremented.)
	 */
	QUEinstr[Q_CLEARXOR].nexec = 1;

	/*
	 * Load the the instructions that tell the queue command processor
	 * to load:
	 *	- The X and Y destination registers with the bottom right
	 *	  corner of the hardware cursor's XOR area.
	 *
	 * 	- The destination width and height registers with the width
	 * 	  and height of the hardware cursor's XOR area.
	 *
	 * Then load an execute command instruction with a CLEAR
	 * logic function and a Raster Operation Type (ROT) of write 
	 * the destination only.
	 *
	 * NOTE: These routines never vary.
	 */
	*QUEptr-- = BUILD_REGLOAD(XDST_REG,XOR_LOCATOR_RT);
	*QUEptr-- = BUILD_REGLOAD(YDST_REG,XOR_LOCATOR_BM);
	*QUEptr-- = BUILD_REGLOAD(WIDTH_REG,HARD_LOCATOR_WD);
	*QUEptr-- = BUILD_REGLOAD(HEIGHT_REG,HARD_LOCATOR_HT);
	*QUEptr-- = BUILD_EXCMD(DECR_QUE_COUNT,ROT_WRDST,LF_CLEAR);

	QUEinstr[Q_SETAND].Qptr = QUEptr;
	QUEinstr[Q_SETAND].Qaddr = SPTR_TO_QPTR(QUEptr);
	QUEinstr[Q_SETAND].nwords = DSTCMDWORDS;
	/*
	 * 1 execute command. (For every execute command a counter
	 * on the APA-16 must be incremented.)
	 */
	QUEinstr[Q_SETAND].nexec = 1;

	/*
	 * Load the the instructions that tell the queue command processor
	 * to load:
	 *	- The X and Y destination registers with the bottom right
	 *	  corner of the hardware cursor's AND area.
	 *
	 * 	- The destination width and height registers with the width
	 * 	  and height of the hardware cursor's AND area.
	 *
	 * Then load an execute command instruction with a SET
	 * logic function and a Raster Operation Type (ROT) of write 
	 * the destination only.
	 *
	 * NOTE: These routines never vary.
	 */
	*QUEptr-- = BUILD_REGLOAD(XDST_REG,AND_LOCATOR_RT);
	*QUEptr-- = BUILD_REGLOAD(YDST_REG,AND_LOCATOR_BM);
	*QUEptr-- = BUILD_REGLOAD(WIDTH_REG,HARD_LOCATOR_WD);
	*QUEptr-- = BUILD_REGLOAD(HEIGHT_REG,HARD_LOCATOR_HT);
	*QUEptr-- = BUILD_EXCMD(DECR_QUE_COUNT,ROT_WRDST,LF_SET);


}

/*
 * 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.
 */ 
apa16init_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(SCREEN_WD);
	SETRECT(&fontblt->dst_bitmap.rect,0,0,SCREEN_WD,SCREEN_HT+HIDDEN_HT);

	/*
	 * Point the base at the beginning of the APA-16's bitmap.
	 */
	fontblt->dst_bitmap.base = (unsigned short *)APA16BASE;

	SETRECT(&fontblt->dst_rect,0,FONT_Y_COORD,font_wd,FONT_Y_COORD+font_ht);
}

/*
 * Increment the destination rect to the next spot for the next character.
 * Returns TRUE if the font will overflow into the queue area.
 */
apa16incr_dst_rect(dst_rect)
Blt_Rectangle *dst_rect;
{
	if ((dst_rect->corner_x+font_wd) > HIDDEN_WD) {
		if ((dst_rect->corner_y+font_ht) > FONT_YBASE_BOTTOM) {
			return(TRUE);
		}
		else {
			/*
			 * Go to the next line.
			 */
			SETRECT(dst_rect,
				0,
				dst_rect->origin_y+font_ht,
				font_wd,
				dst_rect->corner_y+font_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).
 */
apa16font_load(font,fontdata,fontBM)
register struct font_head *font;
register unsigned short *fontdata;
register Blt_Bitmap *fontBM;
{
	register long 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. */

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

	/*
	 * Read in each character using the dispatch information and
	 * place it in the hidden bitmap area.
	 * This loop works like this:
	 *	foreach characters 0 to 255 do
	 * 		Check and see if we have already added this character
	 * 		    to the hidden font area.
	 *		If so then
	 *			Use the information saved from adding the
	 *			    previous character.
	 *		else if the address goes beyond fontdata's size then
	 *			Mark this character as not having an image.
	 *		else
	 *			Keep this address.
	 *			Load the character into the hidden font area.
	 *			If there was a problem loading the font then
	 *				abort font loading process.
	 *			endif
	 *		endif
	 * 	endfor
	 */
	no_room = FALSE;
	for (i = 0; (i < SIZEOF_DPLIST) && (no_room == FALSE); i++) {
		/*
		 * Keep this address to compare against others.  If this
		 * address shows again then it means that character is
		 * already loaded and we don't have to load it again and
		 * waste space.
		 */
		addrused[i] = font->dplist[i].addr;

		/*
		 * Have we been to this address before??  Look at all
		 * the addresses used before this one.
		 */
		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 the other character.
	 		 */
			char_corner[i] = char_corner[j];
		}
	 	else if ((font->dplist[i].addr >= font->header.size) ||
			 (font->dplist[i].nbytes == 0)) {
			/*
			 * If there were bytes to be had, keep this bogus
			 * address (the address is bogus because its greater
			 * than the size of the bitmap.) Otherwise put an
			 * address into addrused that should not be found
			 * normally so that the if statement before this 
			 * one will function normally.
			 */
			if (font->dplist[i].nbytes == 0)
				addrused[i] = font->header.size;

			/*
	 		 * There is no character for this dispatch 
			 * area, NULLIMAGE indicates this.
	 		 */
			char_corner[i].x = NULLIMAGE;
			char_corner[i].y = NULLIMAGE;
		}
	 	else	{
			/*
			 * Set up the source rectangle which indicates where
			 * in the source bitmap the character image is.
			 */
			SETRECT(&fontblt.src_rect,
				0,
				font->dplist[i].addr/BTOW(font_wd),
				font_wd,
				(font->dplist[i].addr/BTOW(font_wd))+font_ht);

			/*
			 * Keep track of the bottom right hand corner where
			 * this character is placed.
			 */
			char_corner[i].x = fontblt.dst_rect.corner_x;
			char_corner[i].y = fontblt.dst_rect.corner_y;

			/*
			 * Call font bltter to put up the character image.
			 */
			apa_fontblt(&fontblt,APA16_FONT);

			/*
			 * Set up the destination rect for the next blt.
			 */
			no_room = apa16incr_dst_rect(&fontblt.dst_rect);
		}
	}
	if ((i != SIZEOF_DPLIST) && (no_room == TRUE))
		return(FALSE);
	else 
		return(TRUE);
}

apa16clear_hidden()
{
	register i;
	register unsigned short *scr;

	scr = (unsigned short *)HID_APA16BASE;
	for (i = (HIDDEN_HT * HIDDEN_WORD_WD) + 1; --i; )
		*scr++ = 0;
}

/*
 * If the APA-16 is out there then we return TRUE.
 */
apa16_probe(addr)
unsigned short *addr;
{
	unsigned short tmp;

	tmp = *addr;

	/*
	 * Test to see if the APA-16 is there.
	 */
	*(char *)addr = 0x0F;
	if (*(char *)addr != 0x0F) {
		return(FALSE);
	}

	/*
	 * Since we found the screen restore the spot just changed.
	 */
	*addr = tmp;
	return(TRUE);
}

apa16init_reg()
{
	unsigned short tmp;

	/*
	 * Clear the hidden area of the APA-16.  The visible area will be
	 * later.
	 */
	apa16clear_hidden();

	/*
	 * Initialize (reset) the APA-16.
	 */
	RESET_APA16(tmp);
	*(unsigned short *)MODE_R = MODER_DEFAULT;

#ifdef BLACK_ON_WHITE
	APA16_BLACK_ON_WHITE;
#else
	APA16_WHITE_ON_BLACK;
#endif

	ENABLE_VD_OUT(tmp);

	/*
	 * Set the counter register to zero.
	 */
	*QUE_COUNT_R = 0;

	/*
	 * move the locator off the screen.
	 */
	*Y_LOCATOR_R = 1023;
}

apa16clear_screen()
{
	register unsigned short *Qptr;

	/*
	 * Clear the screen.
	 */
	Qptr = QUEinstr[Q_GENBLANK].Qptr;
	QUE_CLEAR(Qptr,LF_CLEAR,
		  borderLPel,			  /* destination X */
		  borderLScan,			  /* destination Y */
		  borderLPel -borderFPel,	  /* Width of clear area */
		  borderLScan - borderFScan);     /* Height of clear area */
	/*
	 * Do the command just set up.
	 */
	DO_QUE_CMD(Q_GENBLANK);
	bzero(apa16_buffer,line_MAX * col_MAX);
}

/*
 * This routine does all essential initialization for the APA-16 screen.
 */
apa16bitmap_init()
{
	register unsigned short *Qptr;

	/*
	 * If we have not initialized the APA-16 yet then do so.
	 */
	if (un_init == TRUE) {
		/*
		 * Initialize the APA-16 registers.
		 */
		apa16init_reg();

		/*
		 * 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.)  This
		 * information is used by apa16init_que.
		 */
		apa16set_fontinfo();

		/*
		 * Initialize the APA-16 command queue.
		 */
		apa16init_que();

		/*
		 * Initialize the font. (Put it in the hidden bitmap area.)
		 */
		if (apa16font_load(&apa16fonthead,apa16fontBits,&apa16fontBM)
		    == FALSE) {
			printf("Can't create APA-16 font! abort.\r\n");
			return(FALSE);
		}
	}
	un_init = FALSE;

	/*
	 * Clear the whole visible screen.
	 */
	Qptr = QUEinstr[Q_GENBLANK].Qptr;
	QUE_CLEAR(Qptr,LF_CLEAR,
		  SCREEN_WD,	  /* destination X */
		  SCREEN_HT,	  /* destination Y */
		  SCREEN_WD,	  /* Width of clear area */
		  SCREEN_HT);     /* Height of clear area */

	/*
	 * Do the command just set up.
	 */
	DO_QUE_CMD(Q_GENBLANK);
	return(TRUE);
}

/*
 * Put a cursor in the hardware locator area.
 */
apa16load_cursor()
{
	register unsigned short *Qptr;

	/*
	 * If the APA-16 locator is busy then somebody blew it by calling
	 * apa16_screen_init.
	 */
	apa16locator_busy = FALSE;

	DO_QUE_CMD(Q_SETAND);
	DO_QUE_CMD(Q_CLEARXOR);

	/*
	 * Now put in the shape of the cursor.
	 */
	Qptr = QUEinstr[Q_GENBLANK].Qptr;
	QUE_CLEAR(Qptr,LF_SET,
		  XOR_LOCATOR_RT-HARD_LOCATOR_WD+col_wd,  /* destination X */
		  XOR_LOCATOR_BM,	  /* destination Y */
		  col_wd,	  /* Width of clear area */
		  col_ht);        /* Height of clear area */

	/*
	 * Do the command just set up.
	 */
	DO_QUE_CMD(Q_GENBLANK);

	*X_LOCATOR_R = firstPel;
	*Y_LOCATOR_R = FIRSTSCAN - 1;
}

/*
 * Initialize the APA-16 screen.  Call other init routines to set up the
 * font, which includes setting up the number of columns and rows on the
 * screen given the font size.  Then set up all pointers and increments and
 * put the cursor on the screen.
 */
apa16_screen_init()
{
	un_init = TRUE;
	if (apa16bitmap_init() == FALSE)
		return;

	/*
	 * Clear the screen.
	 */
	apa16clear_screen();

	/*
	 * set up where to start frame buffer (screen bitmap).
	 */
	apa16cursorX = FIRSTPEL;
	apa16cursorY = FIRSTSCAN;

	apa16load_cursor();
}

/*
 * Does the actual writing of the character onto the screen.
 */
apa16write_char(thecharX,thecharY,height,width,func)
short thecharX;
short thecharY;
short height,width;
short func;		/* func specifies reverse video or not */
{
	register unsigned short *Qptr;

	/*
	 * Modify the section of the queue command code to do the copy.
	 */
	Qptr = QUEinstr[Q_BLTCHAR].Qptr;

	/*
	 * Load up the x and y destination registers to blank under the
	 * cursor.
	 */
	*Qptr-- = BUILD_REGLOAD(XDST_REG,apa16cursorX);
	*Qptr-- = BUILD_REGLOAD(YDST_REG,apa16cursorY);

	/*
	 * Skip the loading of the width and height because they remain 
	 * the same.
	 */
	Qptr -= 2;

	/*
	 * Load in the execute command with either LF_SET (reverse video)
	 * or LF_CLEAR (normal video).
	 */
	*Qptr-- = BUILD_EXCMD(DECR_QUE_COUNT,ROT_WRDST,func);

	/*
	 * Load up the x and y source and destination registers for the
	 * rectangle copy instruction.
	 */
	*Qptr-- = BUILD_REGLOAD(XDST_REG,apa16cursorX-PADCOL_WD);
	*Qptr-- = BUILD_REGLOAD(RECT_YDST_REG,apa16cursorY-PADCOL_HT);
	*Qptr-- = BUILD_REGLOAD(XSRC_REG,thecharX);
	*Qptr = BUILD_REGLOAD(YSRC_REG,thecharY);

	/*
	 * Wait for the queue to be zero then do the command.
	 */
	DO_QUE_CMD(Q_BLTCHAR);
}

apa16underline()
{
	register unsigned short *Qptr;

	/*
	 * Pointer to the queue command section of the APA-16.
	 */
	Qptr = QUEinstr[Q_ULINE].Qptr;

	/*
	 * Load up the x and y destination registers for the NOT DESTINATION
	 * destination only instruction.
	 */
	*Qptr-- = BUILD_REGLOAD(XDST_REG,apa16cursorX-(col_wd - ULINE_WD));
	*Qptr = BUILD_REGLOAD(YDST_REG,apa16cursorY-(PADCOL_HT-ULINE_HT));

	/*
	 * Wait for the queue to be zero then do the command.
	 */
	DO_QUE_CMD(Q_ULINE);
}

apa16hi_intensity(attr)
register short attr;
{
	register unsigned short *Qptr;

	/*
	 * Pointer to the queue command section of the APA-16.
	 */
	Qptr = QUEinstr[Q_BOLDCHAR].Qptr;

	/*
	 * Load up the x and y source and destination registers for the
	 * rectangle copy instruction.
	 */
	*Qptr-- = BUILD_REGLOAD(XDST_REG,apa16cursorX-PADCOL_WD);
	*Qptr-- = BUILD_REGLOAD(RECT_YDST_REG,apa16cursorY-PADCOL_HT);
	*Qptr-- = BUILD_REGLOAD(XSRC_REG,apa16cursorX-PADCOL_WD-1);
	*Qptr-- = BUILD_REGLOAD(YSRC_REG,apa16cursorY-PADCOL_HT);

	/*
	 * The register load width and register load height commands need not
	 * be re-built, they will always be the same.
	 */
	Qptr -= 2;

	/*
	 * Use a different logic function it the attribute is reverse video.
	 */
	if ((attr & NORMAL_VIDEO) || (attr & UNDERLINE_VIDEO))
		*Qptr = BUILD_EXCMD(DECR_QUE_COUNT,ROT_RECTCP,LF_DSTorSRC);
	else 
		*Qptr = BUILD_EXCMD(DECR_QUE_COUNT,ROT_RECTCP,LF_DSTandSRC);

	/*
	 * Wait for the queue to be zero then do the command.
	 */
	DO_QUE_CMD(Q_BOLDCHAR);
}

/*
 * Puts the character onto the screen.
 */
apa16_screen_putc(ch,attr)
register char ch;
register int attr;
{
	register short x,y;  /* the x,y address of the characters image in the
			        hidden bitmap area */
	register short func; /* What function to be used when writing the
				char */

	x = char_corner[ch].x;
	y = char_corner[ch].y;
 
	apa16_buffer[apa16_buf_pointer]=ch;
	if (attr & BLINK) {
		TOGGLE_BACKGRND;
	}

	/*
	 * If x does not equal the null image then we assume y does not either.
	 */
	if (x != NULLIMAGE) {
		/*
		 * Check the attribute of this character.
		 */
		if ((attr & NORMAL_VIDEO) || (attr & UNDERLINE_VIDEO))
			func = LF_CLEAR;
		else 
			func = LF_SET;

		apa16write_char(x,y,font_ht,font_wd,func);

		if (attr & HI_INTENSITY) {
			apa16hi_intensity(attr);
		}

		if (attr & UNDERLINE_VIDEO) {
			apa16underline();
		}
	}
}

/*
 * Removes the cursor if necessary, updates the cursor to the column number
 * and line number given, and writes the cursor back out.
 */
apa16_pos_cursor(column_number,line_number)
register int column_number;
register int line_number;
{
	register unsigned short *Qptr;
	register unsigned short x_spot;

	apa16cursorX = (column_number * col_wd) + FIRSTPEL;
	apa16cursorY = (line_number * col_ht) + FIRSTSCAN;
	apa16_buf_pointer = (line_number * col_MAX) + column_number;

	/*
	 * If the hardware locator is busy then don't put a cursor on the
	 * screen. (NOTE: All setup work to put up characters has been done.
	 * Its sort of an invisible cursor.)
	 */
	if (apa16locator_busy)
		return;

	/*
	 * Spot where the hardware cursor is to go.
	 */
	x_spot = apa16cursorX - col_wd;

	/*
	 * Check and see if x_spot is within 48 pels of the right edge of the
	 * screen.  We check this because the APA-16 hardware cursor stuff
	 * puts garbage on the screen whenever a number larger than 1024-48
	 * is put in the X register.
	 */
	if (x_spot > (SCREEN_WD - HARD_LOCATOR_WD)) {
		prev_apa16hidden_moved = TRUE;

		/*
		 * Clear the XOR section of the hardware cursor.
		 */
		DO_QUE_CMD(Q_CLEARXOR);

		/*
		 * Now put in the shape of the cursor.
		 */
		Qptr = QUEinstr[Q_GENBLANK].Qptr;
		QUE_CLEAR(Qptr,LF_SET,
			  XOR_LOCATOR_RT - HARD_LOCATOR_WD +
			  	(col_wd + (x_spot - 
			  	(SCREEN_WD - HARD_LOCATOR_WD))), /* dest X */
			  XOR_LOCATOR_BM,	  /* dest Y */
			  col_wd,	  /* Width of clear area */
			  col_ht);        /* Height of clear area */

		/*
		 * The spot where the hardware cursor is put has changed.
		 */
		x_spot = SCREEN_WD - HARD_LOCATOR_WD;

		/*
		 * Do the command just set up.
		 */
		DO_QUE_CMD(Q_GENBLANK);
	}
	else if (prev_apa16hidden_moved) {
		prev_apa16hidden_moved = FALSE;

		/*
		 * Clear the XOR section of the hardware cursor.
		 */
		DO_QUE_CMD(Q_CLEARXOR);

		/*
		 * Now put in the shape of the cursor.
		 */
		Qptr = QUEinstr[Q_GENBLANK].Qptr;
		QUE_CLEAR(Qptr,LF_SET,
			  XOR_LOCATOR_RT-HARD_LOCATOR_WD+col_wd, /* dest X */
			  XOR_LOCATOR_BM,	  /* dest Y */
			  col_wd,	  /* Width of clear area */
			  col_ht);        /* Height of clear area */

		/*
		 * Do the command just set up.
		 */
		DO_QUE_CMD(Q_GENBLANK);
	}

	/*
	 * Set the position of the hardware cursor.
	 */
	*X_LOCATOR_R = x_spot;
	*Y_LOCATOR_R = apa16cursorY - 1;
}

/*
 * Using the attribute, (which can be either reverse video or normal video)
 * blank from the starting line and column to the ending line and
 * column.  This is done using the APA-16's command queue.  Up to 3 different
 * "queue loads" must be set up and run to clear a given area.  A queue load
 * is a set of register load instructions with an execute command.  Here is
 * an example of the most difficult case: 
 *
 * 	Apa16_screen_blank is called with sline=1,scol=1,eline = 3,ecol=2.
 *	First I set up the queue to clear from sline,scol to the end of the
 *	line.  Then I set up the queue to clear from the beginning of the
 * 	line to eline,ecol.  Then I set up the queue to clear all the lines
 * 	in between.
 *
 * All other cases, are a subset of this one case.
 */
apa16_screen_blank(attr,sline,scol,eline,ecol)
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 */
{
	register unsigned short *Qptr;
	register nQ;
	short tmp_ht,tmp_Y;
	short tmpFPel,tmpLPel;
	short func;	  /* Logic function to be used to clear the screen */

	/*
	 * Set up to build a command or set of commands on the queue to be
	 * executed. nQ keeps a count of the number of commands on the
	 * queue to be executed.
	 */
	lp_screen_blank(apa16_buffer,sline,scol,eline,ecol,CONS_APA16);
	Qptr = QUEinstr[Q_BLANK].Qptr;
	nQ = 0;

	if (attr == REVERSE_VIDEO) {
		func = LF_SET;
	}
	else {
		func = LF_CLEAR;
	}

	/*
	 * If the starting column to be blanked is not the first column
	 * then that starting line must be blanked seperately.
	 */
	if ((scol > FIRSTCOL) && (sline < eline)) {
		if (sline == 0) {
			/*
			 * Set up the queue to clear the first partial line
			 * which is from start column to the end of the line.
			 */
			QUE_CLEAR(Qptr,func,		/* Queue ptr,func */
				  borderLPel,		/* X dst right*/	
				  (sline*col_ht) + FIRSTSCAN, /* Y dst bottom */
				  borderLPel - 
				  	((scol*col_wd)+firstPel), /* width */
				  col_ht + scan_pad);	  /* height */
		}
		else {
			/*
			 * Set up the queue to clear the first partial line
			 * which is from start column to the end of the line.
			 */
			QUE_CLEAR(Qptr,func,		/* Queue ptr,func */
				  borderLPel,		 /* X dst right*/	
				  (sline*col_ht) + FIRSTSCAN, /* Y dst bottom */
				  borderLPel - 
				  	((scol*col_wd)+firstPel), /* width */
				  col_ht); 			  /* height */
		}
		/*
		 * Increment the number of commands on the queue.
		 */
		nQ += 1;

		/*
		 * Move the starting line and column down to the next
		 * set of columns to be cleared.
		 */
		sline += 1;
		scol = FIRSTCOL;
	}

	if (((ecol+1) < col_MAX) && (sline < eline)) {
		/*
		 * Set up the queue to blank the last partial line
		 * which is from the first column to the end column.
		 */
		if ((eline+1) == line_MAX) {
			QUE_CLEAR(Qptr,func,	/* Queue ptr,func */
				  (ecol*col_wd)+FIRSTPEL,	/* Xdst */
				  ((eline)*col_ht)+FIRSTSCAN+scan_pad,/* Ydst */
				  ((ecol+1)*col_wd)+firstPel,	/* width */
				  col_ht+scan_pad);		/* height */
		}
		else {
			QUE_CLEAR(Qptr,func,		/* Queue ptr,func */
				  (ecol*col_wd)+FIRSTPEL,	/* X dst */
				  (eline*col_ht)+FIRSTSCAN,	/* Y dst */
				  ((ecol+1)*col_wd)+firstPel,	/* width */
				  col_ht);			/* height */
		}

		/*
		 * Increment the number of commands on the queue.
		 */
		nQ += 1;

		/*
		 * Move the ending line and column up to the next
		 * set of columns to be cleared.
		 */
		eline -= 1;
		ecol = col_MAX - 1;
	}

	if ((sline == eline) || ((ecol+1) == col_MAX && scol == FIRSTCOL)) {
		/*
		 * If we are clearing from 0,0 to col_MAX,line_MAX then
		 * re-set the border area.
		 */

		/*
		 * If we are clearing to the last column then clear the extra
		 * area used to make the screen look good.
		 */
		if ((ecol+1) == col_MAX)
			tmpLPel = borderLPel;
		else
			tmpLPel = (ecol*col_wd)+FIRSTPEL;

		/*
		 * If we are clearing to the first column then clear the extra
		 * area used to make the screen look good.
		 */
		if (scol == FIRSTCOL)
			tmpFPel = borderFPel;
		else
			tmpFPel = ((scol-1)*col_wd)+FIRSTPEL;

		/*
		 * Calculate the height and Y coordinate for this queue command.
		 */
		tmp_ht = (eline - sline + 1) * col_ht;
		tmp_Y = (eline * col_ht) + FIRSTSCAN;

		/*
		 * If this is the top line then clear the padded area also.
		 */
		if (sline == 0)
			tmp_ht += scan_pad;
			
		/*
		 * If this is the bottom line then clear the padded area also.
		 */
		if ((eline+1) == line_MAX) {
			tmp_ht += scan_pad;
			tmp_Y += scan_pad;
		}

		/*
		 * Set up the queue to clear the all the lines
		 * between the start column and end columns.
		 */
		QUE_CLEAR(Qptr,func,		/* Queue ptr,func */
			  tmpLPel,		/* X dst */	
			  tmp_Y,		/* Y dst */
			  tmpLPel - tmpFPel,	/* width */
			  tmp_ht);		/* height */

		/*
		 * Increment the number of commands on the queue.
		 */
		nQ += 1;
	}

	/*
	 * Wait for the queue to finish its previous operation and then
	 * execute the given number of commands.
	 */
	EXECUTE_QUE_CMDS(QUEinstr[Q_BLANK].Qaddr,nQ,Q_BLANK);
}

/*
 * apa16_screen_move takes all lines from line_top to line_bottom and 
 * moves these lines to line_dest so that line_top is sitting on line_dest.
 *
 * For example, given line_top is 1, line_bottom is n and line_dest is 3,
 *
 *	Before:	1. XYZ
 *		2. ABC
 *		3. FGH
 *
 *	After scrolling 2 lines down:
 *		~
 *		~
 *		1. XYZ
 *		2. ABC
 *		3. FGH
 */
apa16_screen_move(line_top,line_bottom,line_dest)
register long line_top,line_bottom,line_dest;
{
	register unsigned short *Qptr;

	lp_screen_move(apa16_buffer,line_top,line_bottom,line_dest,CONS_APA16);

	Qptr = QUEinstr[Q_MOVE].Qptr;

	/*
	 * Subtract one because the load XDST_REG instruction is already
	 * in place.
	 */
	Qptr -= 1;
	/*
	 * Destination bottom.
	 */
	*Qptr-- = BUILD_REGLOAD(RECT_YDST_REG,
				  ((line_dest + line_bottom - line_top) *
				   col_ht) + FIRSTSCAN);

	/*
	 * Subtract one because the load XSRC_REG instruction is already
	 * in place.
	 */
	Qptr -= 1;

	/*
	 * Source bottom.
	 */
	*Qptr-- = BUILD_REGLOAD(YSRC_REG,(line_bottom * col_ht) + FIRSTSCAN);

	/*
	 * Subtract one because the load WIDTH_REG instruction is already
	 * in place.
	 */
	Qptr -= 1;

	/*
	 * Blt area height.
	 */
	*Qptr-- = BUILD_REGLOAD(RECT_HEIGHT_REG,((line_bottom-line_top+1) *
						 col_ht));
	/*
	 * Do the rectangle copy.
	 */
	DO_QUE_CMD(Q_MOVE);
}

apa16_screen_print(si,flags)
register SCREEN_INFO *si;
{
	lp_screen_print(apa16_buffer,si,flags,CONS_APA16);
}

/*
 * the folow subroutines were macros, but since space is the bigger critical
 * component of standalone, these have been converted to subroutines.
 */
QUE_CLEAR(Qptr,func,x,y,width,height)
	unsigned short *Qptr;
	unsigned short func,x,y,width,height;
{			
		*Qptr-- = BUILD_REGLOAD(XDST_REG,x);
		*Qptr-- = BUILD_REGLOAD(YDST_REG,y);
		*Qptr-- = BUILD_REGLOAD(WIDTH_REG,width);
		*Qptr-- = BUILD_REGLOAD(HEIGHT_REG,height);
		*Qptr-- = BUILD_EXCMD(DECR_QUE_COUNT,ROT_WRDST,func);
}

/*
 * Wait for the rasterop engine to stop.  If it does not stop then kill it
 * after a specified time.  The way this works is this, we poll the queue
 * counter register waiting for it to zero (which means the rasterop has
 * stopped), if it doesn't zero by QUE_TIME_OUT then assume something drastic
 * went wrong and initialize the APA-16.
 * 
 * Later we will re-write this to receive an interrupt.
 */

WAIT_QUE(index)
{
	if (*QUE_COUNT_R != 0)	{
		int c;
		c = 0;
		while (*QUE_COUNT_R != 0) {
			if (c++ == QUE_TIME_OUT) {
				c = *QUE_COUNT_R;
				apa16_screen_init();
				break;
			}
		}
	}
}

/*
 * Execute the commands set up in the queue at Qaddr but first wait for
 * the last command to finish, then set up the queue register pointer
 * and increment the count nexec number of times.
 */
EXECUTE_QUE_CMDS(Qaddr,nexec,index)
	unsigned short Qaddr;
{
		register i,tmp;
		WAIT_QUE(index);
		*QUE_PTR_R = Qaddr;
		for (i = nexec + 1; --i;)
			INCR_QUE_COUNT(tmp);
}
#endif NAPASIXTEEN
