$TITLE('iRMX II/III Console Driver for AT Platform') 
$OPTIMIZE(3)
$COMPACT( -CONST IN CODE- HAS xconsole)
$IF r_32
$LARGE( BUILDER
$EXPORTS	s120$console$data$t;
$EXPORTS	s120$video$data$t)
$ENDIF

/*****************************************************************************\
 *
 *              Copyright Intel Corporation 1987, 1988
 *                       All rights reserved
 *
 *   For Intel customers licensed for the iRMX II/III System 120 under an
 *   Intel Software License Agreement, this source code and object code
 *   derived therefrom are licensed for use on a single central processing
 *   unit for internal use only.  Necessary backup copies are permitted.
 *   Object Code derived from this source code is a Class I software product
 *   under the Intel Software License Agreement and is subject to the terms
 *   and conditions of that agreement.
 *
 *   For the right to make incorporations, or to transfer this software to
 *   third parties, contact Intel corporation.
 *
 \****************************************************************************/

/*****************************************************************************\
 *	Title:		CONSOLE.P28
 *
 *	History:
 *
 *    09/20/89  iRMX II/III System 120 device driver example  - version 1.0
 *
 \****************************************************************************/

$eject
/*
 * Abstract:
 *
 *  This driver provides an interface between the terminal support code and
 *  the system 120 keyboard and monitor.
 *
 *	This source file contains the following procedures:
 *
 *			 i120$console$init
 *			 i120$console$finish
 *			 i120$console$setup
 *			 i120$console$check
 *			 i120$console$utility
 *			 i120$console$output$task
 *			 i120$screen$on$off
 *			 i120$flush$kb$int
 *			 i120$set$cursor
 *			 i120$set$video$ram$ptr
 *			 i120$get$adapter$type
 */



xconsole : DO;


$include (:F1:comon.lit)
$include (:F1:nutyp.lit)
$include (:F0:error.lit)
$include (:F0:xtsdtn.lit)
$include (:F0:nuclus.ext)
$include (:F1:console.lit)
$include (:F1:keyboard.lit)
/*
 * The following procedure is called when the Keyboard Interrupt character is
 * detected. The object module is included in the system 120 device driver
 * library (  /rmx386/ios/xdr120.lib  )
 */

$IF r_32

	sdm_break: PROCEDURE EXTERNAL;
	END sdm_break;

$ELSE

	sdm_break: PROCEDURE (level, cdata_t, driver_ih) EXTERNAL;

	DECLARE
		level		WORD,
		cdata_t		TOKEN,
		driver_ih	SIZE$OF$OFFSET;

	END sdm_break;

$ENDIF


/*
 * Declaration for Delay Procedure. The object module is included in the
 * device driver library ( /rmx386/ios/xcmdrv.lib )
 */

g$delay:	PROCEDURE(unit, delay_factor) EXTERNAL;

	DECLARE
		unit 				WORD,
		delay_factor		WORD;

END g$delay;


$IF r_32
	DECLARE
		s120$console$data$t			SELECTOR	EXTERNAL,
		s120$video$data$t			SELECTOR	EXTERNAL;
$ENDIF

$SUBTITLE('Console Init procedure')
/*
 * Title:				 i120$console$init
 *
 * Abstract:
 *		This procedure creates and initializes the keyboard data segment,
 *		creates the output task and other driver resources. This procedure
 *		sets the status field in the TSC data structure as follows:
 *
 *		 - If initialization is successful, set status to E$OK.
 *		 - If error, set status to E$IO.
 *
 *		Note: This procedure does not initialize the display adapter.
 *			It is assumed that the display adapter is initialized
 *			by the initialization routine in the BIOS ROM after the
 *			system is powered on.
 *
 *			This procedure reads the index register address field
 *			in the shared data structure and determines the type of
 *			display adapter (EGA or MDA) is being used as a console
 *			output device.
 *
 * Calling sequence:		CALL i120$console$init(tsc$data$ptr);
 *
 * Parameters:
 *		tsc$data$ptr	POINTER to the beginning of the TSC data area.
 *
 * Calls:
 *			RQ$CREATE$SEGMENT			RQ$DELETE$SEGMENT
 *			RQE$CREATE$DESCRIPTOR
 *			RQ$CREATE$MAILBOX
 *			RQ$CREATE$TASK			 RQ$DELETE$TASK
 *			RQ$GET$TASK$TOKENS
 *			RQ$CATALOG$OBJECT			RQ$UNCATALOG$OBJECT
 *			G$DEALY					RQ$GET$PRIORITY
 *
 *			i120$set$cursor
 *
 */

$eject



i120$console$init : PROCEDURE (ddata$sel,ddata$offset) PUBLIC REENTRANT;

DECLARE
	ddata$sel			SELECTOR,
	ddata$offset		SIZE$OF$OFFSET,
	ad			BASED	ddata$sel DRIVER$DATA$STRUCT,

	ads$sel				TOKEN,
	ads			BASED	ads$sel SHARED$DATA$STRUCT,
	ads$word			WORD AT (@ads$sel);


DECLARE
		seg				TOKEN,
		kb$seg	BASED	seg KB$SEG$MODEL,

		root$job		TOKEN,
		board$type		BYTE,
		temp			BYTE;

DECLARE	mon$present		BYTE,
		mon$t			TOKEN,
		mon$info		BASED mon$t STRUCTURE (monitor		BYTE),

		NO$MONITOR		LITERALLY	'0';


/*
 * Set up pointer to the shared data structure.
 */
$IF r_32
	ads$sel = SELECTOR(@s120$console$data$t);
$ELSE
	ads$word	= ABSOLUTE$ADDR$WORD;
$ENDIF


$eject
/*
 * initialize some fields in the driver data structures
 */
	ad.buffered$device	= TRUE;
	ad.output$buffer$size = 0FFFFH;

	ad.raw$size			= DEFAULT$RAW$SIZE;
	ad.raw$in			= 0;
	ad.raw$out			= 0;

	ad.crt$len			= SCREEN$LENGTH;	 /* see console data structure */
	ad.console$state	= 0;				 /* in the TSC data area		*/
	ad.routine$index	= 0;
	ad.screen$on		= TRUE;
	ad.del$const		= 10;				 /* initial value */
	ad.int3$pending		= 0;

	ads.table$offset	= 0;				 /* normal case */
	ads.display$w		= DEFAULT$ATTRIB;	 /* default screen color is white on black */
	ads.column			= 0;
	ads.row				= 0;
	ads.kb$flag			= 0;
/*
 * Get the token for the root job
 */
	root$job = RQ$GET$TASK$TOKENS( ROOT$JOB$TOKEN, @ad.status );

/*
 * Check if debug monitor present. If there is one, assume it is SDM.
 */
	mon$t = rq$lookup$object (root$job, @(9,'RQMONITOR'), 0, @ad.status );
	IF (ad.status = E$OK) AND (mon$info.monitor <> NO$MONITOR) THEN
		mon$present = TRUE;
	ELSE
		mon$present = FALSE;

	ad.mon$present = mon$present;

/*
 * Look up for the delay constant in the Root Object Directory.
 */
	sys$info$sel = rq$lookup$object (root$job,@(9,'RQSYSINFO'),
									 0,@ad.status );
/*
 * Only use the delay constant in the sys$info structure if it is valid
 * otherwise, use the default value
 */
	IF ad.status = E$OK THEN
		ad.del$const = sys$info.delay$const;
/*
 * Create raw input buffer
 */
	ad.raw$data$p = BUILD$PTR(RQ$CREATE$SEGMENT(DEFAULT$RAW$SIZE,@ad.status),
								0);

	IF ad.status <> E$OK THEN
		GOTO INIT$ERROR$1;
/*
 * Create a segment for the keyboard translation table
 */
	seg = RQ$CREATE$SEGMENT( SIZE(kb$seg), @ad.status );

	IF ad.status <> E$OK THEN
		GOTO INIT$ERROR$2;

$eject

/*
 * Initialize the keyboard translation table
 */
	kb$seg.n$keys	= N$KEY$CODES;

	CALL MOVB( @types, @kb$seg.types, N$KEY$CODES );
	CALL MOVB( @values, @kb$seg.values, N$KEY$CODES * 10);

	ads.kb$seg$sel	= seg;
/*
 * Catalog the keyboard table segment in the root job directory
 */
	CALL RQ$CATALOG$OBJECT( root$job, seg, @(9,'PC_KB_TAB'), @ad.status );

	IF ad.status <> E$OK THEN
		GOTO INIT$ERROR$3;
/*
 * Create the output mailbox
 */
	ad.output$mbx = RQ$CREATE$MAILBOX( FIFO$Q, @ad.status );

	IF ad.status <> E$OK THEN
		GOTO INIT$ERROR$3;
/*
 * Determine the type of the display adapter is being used by the SDM, then
 * set up the video RAM pointer for the driver.
 *
 * Important:	The shared data structure is located at absolute address 400:0H
 *			 (28:0 in protected mode). The value in the index register
 *			 address field [in this structure] is evaluated to determine the
 *			 type of the display adapter that the driver should be using.
 *
 *	32 bits!!!	BLD FILE will handle placement of shared data structure -
 *				reference to it will now be via a SELECTOR named
 *				s120$console$data$t
 */

$eject

	IF mon$present AND ((ads.index$reg$addr = MONO$INDEX$REG) OR
		(ads.index$reg$addr = COLOR$INDEX$REG)) THEN
/*
 * Means that the SDM is using one of the display adapters and that the
 * shared data structure is already set up.
 */
	DO;
		IF ads.index$reg$addr = MONO$INDEX$REG THEN
			/*
			 * set video RAM pointer to the starting address of the display
			 * RAM on the monochrome display adapter.
			 */
			CALL i120$set$video$ram$ptr(ddata$sel, ads$sel, MONO$CARD, TRUE);
		ELSE
			/*
			 * set video RAM pointer to the starting address of the display
			 * RAM on the color (or EGA) card.
			 */
			CALL i120$set$video$ram$ptr(ddata$sel, ads$sel, COLOR$CARD, TRUE);
	END;

	ELSE
	/*
	 * No SDM, or SDM may have been using the serial port.
	 */
	DO;
		board$type = i120$get$adapter$type;

		DO CASE board$type;
				/* CASE 0 - there is no display adapter in the system */
			DO;
				ad.status = E$IO;
			END;
				/* CASE 1 - only the mono adapter is present */
			DO;
				CALL i120$set$video$ram$ptr(ddata$sel,ads$sel,MONO$CARD,FALSE);
			END;
				/* CASE 2 - only the color adapter (or EGA) is present */
			DO;
				CALL i120$set$video$ram$ptr(ddata$sel,ads$sel,COLOR$CARD,FALSE);
			END;
				/* CASE 3 - both color and mono display adapter are present */
			DO;
				CALL i120$set$video$ram$ptr(ddata$sel,ads$sel,COLOR$CARD,FALSE);
			END;
		END;	/* CASE board$type */
	END;

$eject

	IF ad.status <> E$OK THEN
		GOTO INIT$ERROR$3;

/*
 * Blank the display by filling the display RAM with the data pattern
 * stored in ads.display$w
 */
	CALL SETW( ads.display$w, ads.video$ram$p, ad.crt$len );
/*
 * Set the cursor to upper left corner (0,0)
 */
	CALL i120$set$cursor(ads$sel);
/*
 * Issue a reset command to start the keyboard reset and a keyboard
 * internal self test
 */
	OUTPUT(KEYB$DATA$PORT)	= KB$RESET$CMD;

/*
 * Wait for the keyboard self test to complete and do a dummy read
 */
	CALL g$delay(10, ad.del$const);			/* some delay */
	temp = INPUT(KEYB$DATA$PORT);

/*
 * get the task priority. This priority is the same as the
 * priority level assigned in the DUIB.
 */
	temp = RQ$GET$PRIORITY( THIS$TASK$PRIO, @ad.status );

	IF ad.status <> E$OK THEN
		GOTO INIT$ERROR$3;
/*
 * create an output task, use the same priority
 */
	ad.output$task	= RQ$CREATE$TASK( temp, @i120$console$output$task,
									ddata$sel, NIL, 512, 0, @ad.status );
	IF ad.status = E$OK THEN
	DO;
		ad.status = E$OK;						 /* initialization is done */
		RETURN;
	END;


INIT$ERROR$4:
	CALL RQ$DELETE$TASK( ad.output$task, @ad.status );

INIT$ERROR$3:
	seg	 = RQ$LOOKUP$OBJECT( root$job, @(9,'PC_KB_TAB'), 0, @ad.status );
	CALL RQ$UNCATALOG$OBJECT( root$job, @(9,'PC_KB_TAB'), @ad.status );
	CALL RQ$DELETE$SEGMENT( seg, @ad.status );

INIT$ERROR$2:
	CALL RQ$DELETE$SEGMENT( ads.kb$seg$sel, @ad.status );

INIT$ERROR$1:
	CALL RQ$DELETE$SEGMENT( SELECTOR$OF(ad.raw$data$p), @ad.status );

	ad.status = E$IO; /* inform the TSC that the initialization has failled */
	RETURN;

END i120$console$init;


$SUBTITLE('Console Finish procedure')
/*
 * Title:				 i120$console$finish
 *
 * Abstract:
 *		The terminal support code calls this procedure when a user detaches
 *		the console. When called, this procedure deletes the output task,
 *		the screen save task, the output mailbox, and other driver resources
 *		created by the i120$console$init procedure.
 *
 * Calling sequence:		CALL i120$console$finish(tsc$data$ptr);
 *
 * Parameters:
 *		tsc$data$ptr	POINTER to the beginning of the TSC data area.
 *
 * Calls:
 *		 RQ$DELETE$TASK
 *		 RQ$DELETE$MAILBOX
 *		 RQE$DELETE$DESCRIPTOR
 *		 RQ$DELETE$SEGMENT
 *		 RQ$GET$TASK$TOKENS
 *		 RQ$LOOKUP$OBJECT
 *		 RQ$UNCATALOG$OBJECT
 *
 */

i120$console$finish : PROCEDURE(ddata$sel,ddata$offset) PUBLIC REENTRANT;

DECLARE

	ddata$sel			SELECTOR,
	ddata$offset		SIZE$OF$OFFSET,
	ad			BASED	ddata$sel DRIVER$DATA$STRUCT,

	ads$sel				TOKEN,
	ads			BASED	ads$sel SHARED$DATA$STRUCT,
	ads$word			WORD AT (@ads$sel),

	status				WORD,
	root$job			TOKEN,
	seg					TOKEN;

$eject
/*
 * Set up pointer to the shared data structure.
 */

$IF r_32
	ads$sel = SELECTOR(@s120$console$data$t);
$ELSE
	ads$word	= ABSOLUTE$ADDR$WORD;
$ENDIF

/*
 * Delete the output task, and other resources created by the
 * initialization routine.
 */
	CALL RQ$DELETE$TASK( ad.output$task, @status );
	CALL RQ$DELETE$MAILBOX( ad.output$mbx, @status );
	CALL RQ$DELETE$SEGMENT( SELECTOR$OF(ad.raw$data$p), @status );

/*
 * Remove the descriptor for the video RAM.
 */
	CALL RQE$DELETE$DESCRIPTOR( SELECTOR$OF(ads.video$ramp), @status );

/*
 * Get the token of the keyboard data segment
 */
	root$job = RQ$GET$TASK$TOKENS( ROOT$JOB$TOKEN, @status );
	seg	 = RQ$LOOKUP$OBJECT( root$job, @(9,'PC_KB_TAB'), 0, @status );

/*
 * Uncatalog the keyboard data table, delete the keyboard data segment
 */
	CALL RQ$UNCATALOG$OBJECT( root$job, @(9,'PC_KB_TAB'), @status );
	CALL RQ$DELETE$SEGMENT( seg, @status );


END i120$console$finish;


$SUBTITLE('Console Setup procedure')
/*
 * Title:				 i120$console$setup
 *
 * Abstract:
 *		This procedure performs the attach device and detach device
 *		operations according to both the buffered$device and the
 *		buff$output$state flags (see the iRMX device driver user's
 *		guide for details).
 *
 * Calling sequence:		CALL i120$console$setup(tsc$data$ptr);
 *
 * Parameters:
 *		tsc$data$ptr	POINTER to the beginning of the TSC data area.
 *
 * Calls:
 *		 NONE
 */

i120$console$setup : PROCEDURE(ddata$sel,ddata$offset ) PUBLIC REENTRANT;

DECLARE
	ddata$sel			SELECTOR,
	ddata$offset		SIZE$OF$OFFSET,
	ad			BASED	ddata$sel DRIVER$DATA$STRUCT;

	IF NOT(ad.buffered$device) THEN
	DO;
		IF (ad.buff$output$state AND OUTPUT$SEMAPHORE) = 0 THEN
		DO;	/* case 1: Attach, recognized by Buffer$device = False
						and Output$semaphore = 0 */
		 	ad.raw$out		= ad.raw$in;
			ad.buffered$device= TRUE;
			ad.buff$output$state = ad.buff$output$state OR OUTPUT$SEMAPHORE;
	
			ad.special$modes	= 0;
			ad.high$water$mark= 0;
			ad.low$water$mark = 0;
			ad.fc$on$char		= 0;
			ad.fc$off$char	= 0;
			CALL SETB(0, @ad.special$char, 4);
		END;

		ELSE
		DO;	/* case 2: Detach, recognized by Buffer$device = False
								and Output$semaphore = 1 */
			ad.buff$output$state = ad.buff$output$state AND
										NOT(OUTPUT$SEMAPHORE);
		END;
	END;
	/* case 3: Change attributes (not relevant here);
		recognized by Buffer$device=True */

END i120$console$setup;

$SUBTITLE('Console Check procedure')
/*
 * Title:				 i120$console$check
 *
 * Abstract:
 *		This procedure handles interrupt generated by the keyboard.
 *		When called, this procedure reads the keyboard scan code.
 *		If the code read from the keyboard is not valid, then the
 *		interrupt$type field in the TSC data area is set to zero
 *		to indicate the keyboard interrupt did not occurred.
 *
 *		If the scan code is valid, translate the code into an ASCII
 *		character and copy the character into the raw input buffer and
 *		signal the Terminal Support Code to process the character.
 *		Else, process special character read from the keyboard.
 *
 * Calling sequence:		CALL i120$console$check(tsc$data$ptr);
 *
 * Parameters:
 *		tsc$data$ptr	POINTER to the beginning of the TSC data area.
 *
 * Calls:
 *			TIME
 */

i120$console$check : PROCEDURE(ddata$sel,ddata$offset) PUBLIC REENTRANT;

DECLARE
	ddata$sel			SELECTOR,
	ddata$offset		SIZE$OF$OFFSET,
	ad			BASED	ddata$sel DRIVER$DATA$STRUCT,

	ads$sel				TOKEN,
	ads			BASED	ads$sel SHARED$DATA$STRUCT,
	ads$word			WORD AT (@ads$sel),

	dinfo$p				POINTER,
	dinfo		BASED	dinfo$p DINFO$SYP120$CONSOLE,

	raw$buffer$p		POINTER,
	raw$buffer	BASED	raw$buffer$p (1) BYTE,

	seg					TOKEN,
	kb$seg		BASED	seg KB$SEG$MODEL,

$eject

	level				WORD,
	status				WORD,
	index				WORD,
	temp				WORD,
	scan$code		 	BYTE,
	function$key		BYTE,
	char				BYTE,
	raw$out				BYTE,
	raw$in				BYTE,

	old$LED$status		BYTE;


	dinfo$p = ad.dinfo$p;
	level = dinfo.interrupt$level;
/*
 * Set up pointers to the shared data structure and the keyboard
 * translation table
 */
$IF r_32
	ads$sel = SELECTOR(@s120$console$data$t);
$ELSE
	ads$word	= ABSOLUTE$ADDR$WORD;
$ENDIF

	seg		= ads.kb$seg$sel;
/*
 * if the display is OFF then turn it ON
 */
	IF NOT(ad.screen$on) THEN
		CALL i120$screen$on$off(ddata$sel, ads$sel, TURN$ON);
/*
 * Save the current keyboard LED's status 
 * ( the NumLock, CapsLock, and ScrollLock indicator ).
 */
	old$LED$status	= ads.kb$flag;
/*
 * Read the keyboard scan code
 */
	scan$code	= INPUT(KEYB$DATA$PORT);

$IF r_32
	IF ad.int3$pending THEN
	DO;
		/*
		 * Key-release interrupt for cntl-alt-break; instruct SDM to break at
		 * the CS:IP of the interrupted task
		 */
		ad.int3$pending = FALSE;
		call sdm_break;
		GOTO not$a$real$char;
	END;
$ENDIF

	IF (ad.console$state AND raw$mode) <> 0 THEN
	DO;
		char = scan$code + 20H;
		IF scan$code = CLOSE$ALT$KEY THEN
			ad.console$state= ad.console$state AND NOT raw$mode;
		GOTO char$found;
	END;

$eject



	char = scan$code AND 7FH;

	IF (char = 0) OR (char > HIGHEST$SCAN$CODE) THEN
		GOTO not$a$real$char;

/*
 * If this is a function key then set a flag to indicate that the driver will
 * have to send a NUL character to the user before the actual function code.
 */
	IF ((char >= NORM$FUNCT$MIN) AND (char <= NORM$FUNCT$MAX)) OR
		((char >= SHCT$FUNCT$MIN) AND (char <= SHCT$FUNCT$MAX)) THEN

		function$key = TRUE;
	ELSE
		function$key = FALSE;

/*
 * Index tells the meaning of the key being pressed.
 */
	index = kb$seg.types(char);

/*
 * Adjust the index if the key is released
 */
	IF scan$code >= BREAK$CONTACT THEN
		index = index + BREAK$TYPE$OFFSET;

/*
 * Get the real character from the keyboard translation table
 */
	char = kb$seg.values(char + ads.table$offset);

$eject

	DO CASE index;

			/* CASE 0 - simple char, just return value */
		DO;
			GOTO char$found;
		END;

			/* CASE 1 -	ctl	key is pressed */
		DO;
			IF (ads.kb$flag AND CTL$FLAG) = 0 AND
				(ads.kb$flag AND ALT$FLAG) = 0 THEN
			DO;
				ads.kb$flag = ads.kb$flag OR CTL$FLAG;
				ads.table$offset = 8 * N$KEY$CODES;
			END;
		END;

			/* CASE 2 -	left or right shift	key is pressed */
		DO;
			IF (ads.kb$flag AND SHIFT$FLAG) = 0 AND
				(ads.kb$flag AND ALT$FLAG) = 0	THEN
			DO;
				ads.kb$flag = ads.kb$flag OR SHIFT$FLAG;
				IF (ads.kb$flag AND CTL$FLAG) = 0 THEN
					ads.table$offset = ads.table$offset + N$KEY$CODES;
			END;
		END;

			/* CASE 3 -	ALT	key is pressed */
		DO;
			IF (ads.kb$flag AND CTL$FLAG) = 0 THEN
			DO;
				ads.kb$flag = ads.kb$flag OR ALT$FLAG;
				ads.table$offset = 9 * N$KEY$CODES;
			END;
			ELSE
				ads.kb$flag	= ads.kb$flag OR ALT$FLAG;
		END;

			/* CASE 4 -	CapsLock	key is pressed */
		DO;
			IF (ads.kb$flag AND CTL$FLAG) = 0 AND
				(ads.kb$flag AND ALT$FLAG) = 0	THEN
			DO;
				IF (ads.kb$flag AND CAPS$FLAG) = 0 THEN
				DO;	
					ads.kb$flag		= ads.kb$flag OR CAPS$FLAG;
					ads.table$offset = ads.table$offset + 2 * N$KEY$CODES;
				END;
				ELSE
				DO;
					ads.kb$flag		= ads.kb$flag AND NOT CAPS$FLAG;
					ads.table$offset = ads.table$offset - 2 * N$KEY$CODES;
				END;
			END;
		END;

$eject

			/* CASE 5 -	NumLock	key is pressed */
		DO;
			IF (ads.kb$flag AND CTL$FLAG) = 0 AND
				(ads.kb$flag AND ALT$FLAG) = 0 THEN
			DO;
				IF (ads.kb$flag AND NUM$FLAG) = 0 THEN
				DO;
					ads.kb$flag		= ads.kb$flag OR NUM$FLAG;
					ads.table$offset = ads.table$offset + 4 * N$KEY$CODES;
				END;
				ELSE
				DO;
					ads.kb$flag		= ads.kb$flag AND NOT NUM$FLAG;
					ads.table$offset = ads.table$offset - 4 * N$KEY$CODES;
				END;
			END;
		END;

			/* CASE 6 -	Scroll Lock	key is pressed */
		DO;
			IF (ads.kb$flag AND (ALT$FLAG OR CTL$FLAG)) =
				(ALT$FLAG OR CTL$FLAG) THEN
			DO;
				/*
				 * Ctl-Alt-Break key combination has been pressed. If the
				 * keyboard interrupt option is enabled, and this is the
				 * falling edge interrupt, then instruct SDM to break at
				 * the CS:IP of the interrupted task
				 */

				IF (dinfo.reset$int$en AND ad.mon$present) THEN
$IF r_32
					ad.int3$pending = TRUE;	/*will be handled on key-release*/
$ELSE
					CALL sdm_break(level, ddata$sel,
									OFFSET$OF(@i120$flush$kb$int));
$ENDIF
				GOTO not$a$real$char;
			END;

			IF (ads.kb$flag AND ALT$FLAG) = 0 THEN
			DO;
				IF (ads.kb$flag AND SCROLL$FLAG) = 0 THEN
				DO;
					ads.kb$flag = ads.kb$flag OR SCROLL$FLAG;
					char	= CONTROL$S;
				END;
				ELSE
				DO;
					ads.kb$flag = ads.kb$flag AND NOT SCROLL$FLAG;
					char	= CONTROL$Q;
				END;
			END;

			GOTO char$found;
		END;

$eject

			/* CASE 7 -	DEL	key is pressed */
		DO;
			IF (ads.kb$flag AND (ALT$FLAG OR CTL$FLAG)) =
				(ALT$FLAG OR CTL$FLAG) THEN
			DO;
				/*
				 * CTL-ALT-DEL keys combination has been pressed; if the
				 * keyboard reset option is enable then..
				 */
				IF dinfo.reset$int$en THEN
					OUTPUT(KEYB$SPECIAL$PORT)= 0FEH;

				/* then reset the CPU board */
			END;

			GOTO char$found;
		END;


			/* CASE 8 - released simple char, ignore */
		DO;
		END;

			/* CASE 9 -	ctl key is released */
		DO;
			ads.kb$flag		= ads.kb$flag AND NOT CTL$FLAG;
			ads.table$offset = 0;

			IF (ads.kb$flag AND CAPS$FLAG) <> 0 THEN
				ads.table$offset = 2 * N$KEY$CODES;

			IF (ads.kb$flag AND NUM$FLAG) <> 0 THEN
				ads.table$offset = ads.table$offset + (4 * N$KEY$CODES);

			IF (ads.kb$flag AND SHIFT$FLAG) <> 0 THEN
				ads.table$offset = ads.table$offset + N$KEY$CODES;
		END;

			/* CASE 10 -	left or right shift key is released */
		DO;
			IF (ads.kb$flag AND SHIFT$FLAG) <> 0 THEN
			DO;
				ads.kb$flag = ads.kb$flag AND NOT SHIFT$FLAG;
				IF (ads.kb$flag AND CTL$FLAG) = 0 THEN
					ads.table$offset=	ads.table$offset - N$KEY$CODES;
			END;
		END;

$eject

			/* CASE 11 -	ALT key is released */
		DO;
			ads.kb$flag		= ads.kb$flag AND NOT ALT$FLAG;
			ads.table$offset = 0;

			IF (ads.kb$flag AND CAPS$FLAG) <> 0 THEN
				ads.table$offset = 2 * N$KEY$CODES;

			IF (ads.kb$flag AND NUM$FLAG) <> 0 THEN
				ads.table$offset = ads.table$offset + (4 * N$KEY$CODES);

			IF (ads.kb$flag AND SHIFT$FLAG) <> 0 THEN
				ads.table$offset = ads.table$offset + N$KEY$CODES;
		END;


			/* CASE 12 -	CapsLock key is released */
		DO;
		END;

			/* CASE 13 -	NumLock key is released */
		DO;
		END;

			/* CASE 14 -	Scroll Lock key is released */
		DO;
		END;

			/* CASE 15 -	Del key is released */
		DO;
		END;

	END	/* CASE index */;

/*
 * Got here because the character is either invalid or it is a keyboard
 * control character
 */
not$a$real$char:

	ad.interrupt$type	 = NONE;
	ad.interrupting$unit	= 0;
	GOTO check$leds;


$eject


char$found:

	IF char <> 0 THEN
	DO;

		raw$in	= ad.raw$in;
		raw$out = ad.raw$out;

		IF raw$in <> raw$out - 1 THEN	/* If there is room in the */
		DO;								/* raw input buffer */

			IF ((ads.kb$flag AND ALT$FLAG) <> 0) OR (function$key = TRUE) THEN
			/*
			 * Must be a function key. Send a NUL before the function code
			 */
			DO;
				function$key		= FALSE;
				raw$buffer$p		= ad.raw$data$p;
				raw$buffer(raw$in) = FUNCTION$KEY$CODE;
				ad.raw$in			= raw$in + 1;
			END;
			/*
			 * Now move the character into raw input buffer
			 */
			raw$in = ad.raw$in;
			raw$out = ad.raw$out;
			IF raw$in <> raw$out - 1 THEN
			DO;
				raw$buffer$p		= ad.raw$data$p;
				raw$buffer(raw$in) = char;
				ad.raw$in			= raw$in + 1;
			END;
		END;

		/*
		 * Say that this is an input interrupt. The interrupting unit
		 * is always zero
		 */
		ad.interrupt$type	 = INPUT$INTERRUPT;
		ad.interrupting$unit	= 0;
	END;

$eject



check$leds:

	IF (old$LED$status AND INDICATOR$BITS) <>
		(ads.kb$flag AND INDICATOR$BITS) THEN
	/*
	 * Update keyboard status indicator(s) if either the Num Lock,
	 * the CapsLock, or the Scroll Lock key was pressed.
	 */
	DO;
		OUTPUT(KEYB$DATA$PORT) = TOGGLE$LED$CMD;
		CALL g$delay(10, ad.del$const);			/* some delay */
		OUTPUT(KEYB$DATA$PORT) = ads.kb$flag AND INDICATOR$BITS;
	END;

END i120$console$check;

$SUBTITLE('Console Utility procedure')
/*
 * Title:				 i120$console$utility
 *
 * Abstract:
 *			This procedure performs operation specified by the Terminal
 *			Support Code (TSC). The TSC sets up the FUNCTION$ID field in
 *			the BUFFERED$DEVICE$DATA structure to specify the function
 *			to be performed before calling this procedure. Refer to the
 *			iRMX Device Driver's Guide ( terminal utility procedure section)
 *			for a description of the FUNCTION$ID field.
 *
 * Calling sequence:		CALL i120$console$utility(tsc$data$ptr);
 *
 * Parameters:
 *		tsc$data$ptr	POINTER to the beginning of the TSC data area.
 *
 * Calls:
 *			RQ$SLEEP
 *			RQ$SEND$MESSAGE
 *
 */


i120$console$utility: PROCEDURE(ddata$sel,ddata$offset) REENTRANT PUBLIC;

DECLARE
	ddata$sel				SELECTOR,
	ddata$offset			WORD,
	ad				BASED	ddata$sel DRIVER$DATA$STRUCT,

	output$buffer$ptr		POINTER,
	output$buffer	BASED	output$buffer$ptr (1) BYTE,

	index					WORD,
	temp					WORD,
	output$count			WORD,
	status					WORD,

	seg				 		TOKEN,

	output$seg		BASED	seg STRUCTURE(
								count	 WORD,
								text	(1) BYTE);


$eject

	IF (ad.function$id >= 0) AND (ad.function$id <= MAX$FUNCT$ID) THEN

	DO CASE ad.function$id;

			/* CASE 0 OUTPUT$CALL */
		DO;
			IF (ad.buff$output$state AND OUTPUT$STOPPED) <> 0 THEN
				RETURN;

			output$count	 = ad.out$count;
			output$buffer$ptr= ad.user$buffer$p;

			/* fill output count */

			ad.out$count = 0;

			IF (ad.buff$output$state AND OUTPUT$SCROLL) <> 0 THEN
			DO;
				index = 0;
				DO WHILE (ad.output$scroll$count > 0) AND
						(index < output$count);
					temp = FINDB(@output$buffer(index), LF,
								(output$count - index));
					IF temp = 0FFFFH THEN
						index = output$count;
					ELSE
					DO;
						index = index + temp + 1;
						ad.output$scroll$count = ad.output$scroll$count - 1;
					END;
				END;

				IF index < output$count THEN
				DO;
					ad.out$count = output$count - index;
					output$count = index;
				END;
			END;	 /* if SCROLLING */


			ad.console$state = ad.console$state AND NOT cancel$output;

			IF output$count = 0 THEN
				RETURN;


$eject

		case0$again:
			seg = RQ$CREATE$SEGMENT(output$count + 2,@status);
			IF status <> 0 THEN
			DO;
				CALL RQ$SLEEP(10,@status);
				GOTO case0$again;
			END;

			output$seg.count = output$count;

			CALL MOVB(output$buffer$ptr, @output$seg.text, output$count);
			CALL RQ$SEND$MESSAGE(ad.output$mbx,seg,SELECTOR$OF(NIL),@status);

		END;
 
			/* CASE 1 ACKNOWLEDGE$INPUT */
		DO;
			ad.buff$input$state = ad.buff$input$state AND
									NOT(DOUBLE(SEND$INPUT$CMD));
			ad.in$count = 0;
		END;

			/* CASE 2 RESTART$INPUT */
		DO;
			ad.buff$input$state = ad.buff$input$state AND
				NOT(DOUBLE(INPUT$FULL));
		END;

			/* CASE 3 CHANGE$UNIT$ATTRIBUTES */
		DO;
		END;

			/* CASE 4 CHANGE$MODEM$CONTROLS */
		DO;
		END;

			/* CASE 5 GET$UNITS$AVAILABLE */
		DO;
			ad.units$available= 0FFFFH;
		END;

			/* CASE 6 cancel$output */
		DO;
			ad.console$state=	ad.console$state OR cancel$output;
		END;

			/* CASE 7 CONTROL$OUTPUT */
		DO;
		END;

$eject

			/* CASE 8 ECHO$CALL */
		DO;
			IF (ad.buff$output$state AND OUTPUT$STOPPED) <> 0 THEN
				RETURN;

			output$count = ad.echo$count;
			output$buffer$ptr = ad.echo$buffer$p;

			ad.echo$count = 0;

			IF (ad.buff$output$state AND OUTPUT$SCROLL) <> 0 THEN
			DO;
				index = 0;
				DO WHILE (ad.output$scroll$count > 0) AND
							(index < output$count);
					temp = FINDB(@output$buffer(index),LF,
								(output$count - index));
					IF temp = 0FFFFH THEN
						index = output$count;
					ELSE
					DO;
						index = index + temp + 1;
						ad.output$scroll$count = ad.output$scroll$count - 1;
					END;
				END;

				IF index < output$count	THEN
				DO;
					ad.echo$count = output$count - index;
					output$count	= index;
				END;
			END;	 /* if SCROLLING */

			ad.echo$buffer$p = @output$buffer(output$count);
			ad.console$state = ad.console$state AND NOT cancel$output;

			IF output$count = 0 THEN
				RETURN;


		case8$again:
			seg = RQ$CREATE$SEGMENT(output$count + 2,@status);

			IF status <> 0 THEN
			DO;
				CALL RQ$SLEEP(10,@status);
				GOTO case8$again;
			END;

$eject

			output$seg.count = output$count;

			CALL MOVB(output$buffer$ptr, @output$seg.text, output$count);
			CALL RQ$SEND$MESSAGE(ad.output$mbx,seg,SELECTOR$OF(NIL),@status);
		END;

			/* CASE 9 FLUSH$INPUT */
		DO;
			ad.buff$input$state = ad.buff$input$state AND NOT INPUT$FULL;
			ad.rawout = ad.rawin;
		END;

			/* CASE 10 UPDATE$RAW$IN */
		DO;
		END;

	END;	/* DO CASE */

END i120$console$utility;


$SUBTITLE('Output task')
/*
 * Title:				 i120$console$output$task
 *
 * Abstract:
 *		 This task is created by the i120$console$init procedure.
 *		 As this driver is a buffered driver, outputs must be done in
 *		 parallel with normal processing; the output task is written to
 *		 support this feature.
 *		
 *		 This task does the actual character output to the display, it
 *		 also handles screen scrolling, erase line, cursor absolute, etc.
 *		 Information to be displayed is stored in the output$segment;
 *		 when processing is completed, this task deletes the segment.
 *
 * Parameters:
 *
 * Calls:		 RQ$SLEEP		G$DELAY
 *
 */

i120$console$output$task:	PROCEDURE	REENTRANT;

DECLARE
	ddata$sel			SELECTOR,
	ad			BASED	ddata$sel DRIVER$DATA$STRUCT,

	ads$sel				TOKEN,
	ads			BASED	ads$sel SHARED$DATA$STRUCT,
	ads$word			WORD AT (@ads$sel),

	dinfo$p				POINTER,
	dinfo		BASED	dinfo$p DINFO$SYP120$CONSOLE,

	seg					TOKEN,
	output$seg	BASED	seg STRUCTURE(
									 count		WORD,
									 text	(1) BYTE),

	video$p				POINTER,
	video$ram	BASED	video$p (1) WORD,

	index				WORD,
	output$count		WORD,
	char				BYTE,
	temp				BYTE,
	timeout$value	 	WORD,
	status				WORD;

$eject
/*
 * Pick up the selector of the TSC's data segment
 */
	ddata$sel	= SELECTOR$OF(@show$ds);
/*
 * Set up pointer to the shared data structure.
 */
$IF r_32
	ads$sel = SELECTOR(@s120$console$data$t);
$ELSE
	ads$word	= ABSOLUTE$ADDR$WORD;
$ENDIF

/*
 * Set up the video RAM pointer
 */
	video$p	=	ads.video$ram$p;

	dinfo$p = ad.dinfo$p;
	IF dinfo.screen$timeout = NO$TIME$LIMIT THEN
		timeout$value = dinfo.screen$timeout;
	ELSE
		timeout$value = dinfo.screen$timeout / 2;

	DO FOREVER;
		wait$here:
		seg = RQ$RECEIVE$MESSAGE(ad.output$mbx, timeout$value, NIL, @status);
		IF status = E$TIME THEN
			DO;
				CALL i120$screen$on$off(ddata$sel, ads$sel, TURN$OFF);
				GOTO wait$here;
			END;
		ELSE
			DO;		/* if the display is being turned off then turn it on */
				IF NOT(ad.screen$on) THEN
					CALL i120$screen$on$off(ddata$sel, ads$sel, TURN$ON);
			END;

		DO index = 0 TO output$seg.count-1;

			DO WHILE (ad.buff$output$state AND OUTPUT$STOPPED) <> 0;
				CALL RQ$SLEEP(5, @status);
			END;

			IF (ad.console$state AND cancel$output) <> 0 THEN
			DO;
				ad.console$state = ad.console$state AND NOT cancel$output;
				GOTO end$of$output;
			END;

			char = output$seg.text(index);

$eject
			DO CASE ( ad.routine$index );
				/* routine$index = 0 - ordinary character */ 
			DO;
				IF (char < DISPLAYABLE$CHAR) AND
								((ad.console$state AND literal$mode) = 0) THEN
				DO CASE char;
						/* CASE 0 -	 */
					DO;
					END;

						/* CASE 1 - erase rest of screen */
					DO;
						CALL SETW(ads.display$w + BLANK$CHARACTER,
							@video$ram(ads.row * CRT$COLS + ads.column),
							crt$cols * (CRT$ROWS - ads.row-1) +
							(CRT$COLS-ads.column));
					END;

						/* CASE 2 - erase line */
					DO;
						CALL SETW(ads.display$w + BLANK$CHARACTER,
							@video$ram(ads.row * CRT$COLS), CRT$COLS);
					END;

						/* CASE 3 - erase rest of line */
					DO;
						CALL SETW(ads.display$w + BLANK$CHARACTER,
							@video$ram(ads.row * CRT$COLS + ads.column),
								CRT$COLS - ads.column);
					END;

						/* CASE 4 - cursor absolute */
					DO;
						ad.routine$index = 1;
					END;

						/* CASE 5 - line delete */
					DO;
						CALL MOVW(@video$ram((ads.row+1) * CRT$COLS),
							@video$ram(ads.row * CRT$COLS),
								(LAST$ROW - ads.row) * CRT$COLS);
						CALL SETW(ads.display$w + BLANK$CHARACTER,
								@video$ram(LAST$ROW * CRT$COLS), CRT$COLS);
					END;

						/* CASE 6 - line insert */
					DO;
						CALL MOVRW(@video$ram(ads.row * CRT$COLS),
								@video$ram((ads.row+1) * CRT$COLS),
									(LAST$ROW - ads.row) * CRT$COLS);
						CALL SETW(ads.display$w + BLANK$CHARACTER,
								@video$ram(ads.row * CRT$COLS), CRT$COLS);
					END;
$eject

						/* CASE 7 - bell */
					DO;
							/* save contents of the diagnostic register */
						char = INPUT( DIAG$REG$ADDR );
							/* disable speaker */
						OUTPUT(DIAG$REG$ADDR)= char AND 0FCH;
						CALL g$delay(2, ad.del$const);		/* some delay */
							/* set up the timer count */
						OUTPUT(TIMER$CNT$WORD$REG)=	0B6H;
						CALL g$delay(2, ad.del$const);
						OUTPUT(TIMER$CNT$DATA2)= 33H;
						CALL g$delay(2, ad.del$const);
						OUTPUT(TIMER$CNT$DATA2)= 5H;
							/* enable the speaker */
						CALL g$delay(2, ad.del$const);
						OUTPUT(DIAG$REG$ADDR )= ( char AND 0FCH ) OR	3H;
						CALL RQ$SLEEP(4,@status);		/* some delay */
							/* restore the contents of the diagnostic register */
						OUTPUT(DIAG$REG$ADDR)= char;
					END;


						/* CASE 8 - backspace */
					DO;
						IF ads.column = 0 THEN
						DO;
							IF ads.row > 0 THEN
							DO;
								ads.row = ads.row - 1;
								ads.column = LAST$COL;
							END;
						END;
						ELSE
							ads.column = ads.column - 1;
					END;

						/* CASE 9 - tab */
					DO;
					END;


$eject
						/* CASE 0A - linefeed */
					DO;
						IF ads.row >= LAST$ROW THEN
						DO;
							CALL MOVW(@video$ram(CRT$COLS),
										@video$ram(0), CRT$COLS*LAST$ROW);
							CALL SETW(ads.display$w + BLANK$CHARACTER,
									@video$ram(CRT$COLS * LAST$ROW), CRT$COLS);
						END;
						ELSE
							ads.row = ads.row + 1;
					END;

						/* CASE 0B - set color */
					DO;
						ad.routine$index = 3;
					END;

						/* CASE 0C - erase screen */
					DO;
						CALL SETW(ads.display$w + BLANK$CHARACTER,
									@video$ram(0), ad.crt$len);
						ads.row	= 0;
						ads.column = 0;
					END;

						/* CASE 0D - CR */
					DO;
						ads.column = 0;
					END;

						/* CASE 0E - literal */
					DO;
						ad.console$state = ad.console$state OR literal$mode;
					END;
	
						/* CASE 0F - set raw mode */
					DO;
						ad.console$state = ad.console$state OR raw$mode;
					END;

						/* CASE 10 - set cooked mode */
					DO;
						ad.console$state = ad.console$state AND NOT raw$mode;
					END;

						/* CASE 11 - X-ON	*/
					DO;
					END;
						/* CASE 12		 */
					DO;
					END;
						/* CASE 13 - X-OFF */
					DO;
					END;
						/* CASE 14 */
					DO;
					END;
						/* CASE 15 */
					DO;
					END;
						/* CASE 16 */
					DO;
					END;
						/* CASE 17 */
					DO;
					END;
						/* CASE 18 */
					DO;
					END;
$eject
					/* CASE 19 - cursor right */
					DO;
						IF ads.column >= LAST$COL THEN
						DO;
							IF ads.row < LAST$ROW THEN
							DO;
								ads.row = ads.row + 1;
								ads.column = 0;
							END;
						END;
						ELSE
						DO;
							ads.column = ads.column + 1;
						END;
					END;
						/* CASE 1A */
					DO;
					END;
						/* CASE 1B */
					DO;
					END;
	
					/* CASE 1C - cursor down */
					DO;
						IF ads.row <> LAST$ROW THEN
							ads.row = ads.row + 1;
					END;
						/* CASE 1D - cursor home */
					DO;
						ads.row = 0;
						ads.column = 0;
					END;
						/* CASE 1E - cursor up */
					DO;
						IF ads.row <> 0	THEN
							ads.row = ads.row - 1;
					END;
						/* CASE 1F - cursor left */
					DO;
						IF ads.column = 0 THEN
						DO;
							IF ads.row > 0 THEN
							DO;
								ads.row = ads.row - 1;
								ads.column = LAST$COL;
							END;
						END;
						ELSE
							ads.column = ads.column - 1;
					END;

				END; /* CASE char */

$eject

						 /* process normal character */
				ELSE
				DO;
					ad.console$state = ad.console$state AND NOT literal$mode;
	
					/* write the characrer to display ram at current cursor pos. */
					video$ram(ads.row * CRT$COLS + ads.column) =
								ads.display$w + char;
	
					/* update cursor position */
					ads.column = ads.column + 1;
	
					IF ads.column > LAST$COL THEN
					DO;
						ads.column = 0;
						IF ads.row >= LAST$ROW THEN
						DO;
							/* scroll the display up one line */
							CALL MOVW(@video$ram(CRT$COLS), @video$ram(0),
													CRT$COLS * LAST$ROW);
							/* erase the last line on the screen */
							CALL SETW(ads.display$w + BLANK$CHARACTER,
									@video$ram(CRT$COLS * LAST$ROW), CRT$COLS);
						END;
						ELSE
							ads.row = ads.row + 1;
					END;
				END;

			END /* of case 0	=> routine$index = 0 */;

			/* routine$index = 1 - second byte of absolute cursor position */
			DO;
				ad.routine$index = 2;
				temp = char - 20H;
				IF temp < CRT$ROWS THEN
		 			ads.row = temp;
				ELSE
					ads.row = 0;
			END;

			/* routine$index = 2 - third byte of absolute cursor position */
			DO;
				ad.routine$index = 0;
				temp = char - 20H;
				IF temp <= CRT$COLS THEN
		 			ads.column = temp;
				ELSE
					ads.column = 0;
			END;

$eject

			/* routine$index = 3 - second byte of set foreground color */
			DO;
				ad.routine$index = 0;
				ads.display$w	= char * 256;
			END;

			END;	/* CASE routine$index */

		END /* of DO index */;


end$of$output:

		CALL i120$set$cursor(ads$sel);

		CALL RQ$DELETE$SEGMENT(seg, @status);

	END	/* DO FOREVER */;

END i120$console$output$task;


$SUBTITLE('Local procedure: Set screen on/off')
/*
 * Title:				 i120$screen$on$off
 *
 * Abstract:
 *		This procedure enables or disables the CRT bases on the value of
 *		the 'flag' byte passed by the caller. It works only with the CGA
 *		or the monochrome adapter. EGA support may be added in the future.
 *
 * Calling sequence:
 *		CALL i120$screen$on$off(ddata$sel, ads$sel, flag);
 *
 * Parameters:
 *			ddata$sel		SELECTOR of the TSC data area.
 *			ads$sel			SELECTOR of the SHARED$DATA$STRUCT structure.
 *			flag			BYTE	if 0 then turn the screen OFF
 *								else	turn the screen ON
 * Calls:	None
 */

i120$screen$on$off:	PROCEDURE(ddata$sel, ads$sel, flag)	REENTRANT;

DECLARE
	ddata$sel			SELECTOR,
	ad			BASED	ddata$sel DRIVER$DATA$STRUCT,

	ads$sel				TOKEN,
	ads			BASED	ads$sel SHARED$DATA$STRUCT,

	flag				BYTE;


	IF flag THEN			/* turn the screen ON */
	DO;
		OUTPUT(ads.index$reg$addr + MODE$REG$OFFSET) = ads.video$enable;
		ad.screen$on = TRUE;
	END;

	ELSE					/* turn the screen OFF */
	DO;
		IF ad.screen$on THEN
			ad.screen$on = FALSE;
		ELSE
		DO;
			OUTPUT(ads.index$reg$addr + MODE$REG$OFFSET) = ads.video$disable;
			ad.screen$on = FALSE;
		END;
	END;
END i120$screen$on$off;

$SUBTITLE('Local procedure: Set Cursor')
/*
 * Title:				 i120$set$cursor
 *
 * Abstract:
 *		 This procedure is used to update the cursor location on the
 *		 screen. It does so by sending the cursor to the address specified
 *		 by ads.row and ads.column (see the SHARED$DATA$STRUCT structure).
 *		 This procedure does not perform any boundaries checking.
 *
 * Calling sequence:		CALL i120$set$cursor(ads$sel);
 *
 * Parameters:
 *			ads$sel		SELECTOR of the SHARED$DATA$STRUCT structure.
 *
 * Calls:				 NONE
 */

i120$set$cursor : PROCEDURE (ads$sel) REENTRANT;

DECLARE
	ads$sel			TOKEN,
	ads		BASED 	ads$sel SHARED$DATA$STRUCT,

	value			WORD;



	/* Calculate the absolute cursor address */

	value = ads.row * CRT$COLS + ads.column;

	/* Output cursor address to CRT controller */

	OUTPUT(ads.index$reg$addr) = select$R14;
	OUTPUT(ads.index$reg$addr + data$reg$offset) = HIGH(value);

	OUTPUT(ads.index$reg$addr) = select$R15;
	OUTPUT(ads.index$reg$addr + data$reg$offset) = LOW(value);


END i120$set$cursor;


$SUBTITLE('Local procedure: Set video RAM pointer')
/*
 * Title:				 i120$set$video$ram$ptr
 *
 * Abstract:
 *		 This procedure sets the video RAM pointer to the starting address
 *		 of the display adapter indicated by the adapter$type.
 *
 * Calling sequence:		CALL i120$set$video$ram$ptr(ddata$sel,
 *										ads$sel, adapter$type, mon$present);
 *
 * Parameters:
 *			ddata$sel	SELECTOR of the TSC data area.
 *			ads$sel		SELECTOR of the SHARED$DATA$STRUCT structure.
 *			adapter$type	BYTE	indicates the type of display adapter used.
 *			mon$present		BYTE	indicates if debug monitor present
 *
 * Calls:	RQE$CREATE$DESCRIPTOR
 */

i120$set$video$ram$ptr : PROCEDURE (ddata$sel,ads$sel,card$type,mon$present)
																	 REENTRANT;

DECLARE
	ddata$sel			SELECTOR,
	ddata$offset		SIZE$OF$OFFSET,
	ad			BASED 	ddata$sel DRIVER$DATA$STRUCT;

DECLARE
	ads$sel				TOKEN,
	ads			BASED	ads$sel SHARED$DATA$STRUCT,
	card$type		 	BYTE,
	mon$present			BYTE;

	IF card$type = MONO$CARD THEN
	DO;

$IF r_32
		IF (ads.index$reg$addr = MONO$INDEX$REG) AND (mon$present) THEN
		DO;
			/* SDM already set up link to the video board */

			ads.video$disable	= MON$DISABLE;
			ads.video$enable	= MON$ENABLE;
		END;
		ELSE
		DO;
$ENDIF
			/*
			 * set video RAM pointer to the starting address of the display
			 * RAM on the monochrome display adapter.
			 */
			ads.video$ram$p	=
				BUILD$PTR( RQE$CREATE$DESCRIPTOR(mono$base$addr,
											(ad.crt$len)*2, @ad.status), 0);
			ads.video$disable	= MON$DISABLE;
			ads.video$enable	= MON$ENABLE;
			ads.index$reg$addr = MONO$INDEX$REG;
$IF r_32
		END;
$ENDIF

	END;

$eject

	ELSE
	DO;
$IF r_32
		IF (ads.index$reg$addr = COLOR$INDEX$REG) AND (mon$present) THEN
		DO;
			/* SDM already set up link to the video board */

			ads.video$disable	= COL$DISABLE;
			ads.video$enable	= COL$ENABLE;
		END;
		ELSE
		DO;
$ENDIF
			/*
			 * set video RAM pointer to the starting address of the display RAM
			 * on the color display adapter.
			 */
			ads.video$ram$p	=
				BUILD$PTR( RQE$CREATE$DESCRIPTOR(color$base$addr,
											(ad.crt$len)*2, @ad.status), 0);
			ads.index$reg$addr = COLOR$INDEX$REG;
			ads.video$disable	= COL$DISABLE;
			ads.video$enable	= COL$ENABLE;
$IF r_32
		END;
$ENDIF

	END;

END i120$set$video$ram$ptr;


$SUBTITLE('Local procedure: Get adapter type')
/*
 * Title:				 i120$get$adapter$type
 *
 * Abstract:
 *		 This procedure performs a test to determine the type of display
 *		 adapter is present in the system. The meaning of the value
 *		 return to the user (board$type) is as follows:
 *
 *				0	no diaplay adapter found
 *				1	only the monochrome adapter is present
 *				2	only the color adapter is present
 *				3	both the monochrome and the color adapter are present
 *
 * Calling sequence:		board$type = i120$get$adapter$type;
 *
 * Parameters:
 *			None
 *
 * Calls:	RQ$SLEEP
 *
 */

i120$get$adapter$type : PROCEDURE BYTE REENTRANT;

DECLARE
	status			WORD,
	temp1			BOOLEAN,
	temp2			BOOLEAN,
	board$type		BYTE;


	temp1, temp2 = FALSE;
	/*
	 * Write test data to the monochrome adapter
	 */
	OUTPUT(MONO$INDEX$REG) = select$R14;
	CALL RQ$SLEEP(1, @status);							/* some delay */

	OUTPUT(MONO$INDEX$REG + DATA$REG$OFFSET) =	TEST$PATTERN;
	CALL RQ$SLEEP(1, @status);							/* some delay */
	/*
	 * Write test data to the color adapter
	 */
	OUTPUT(COLOR$INDEX$REG) = select$R14;
	CALL RQ$SLEEP(1, @status);							/* some delay */

	OUTPUT(COLOR$INDEX$REG + DATA$REG$OFFSET) =	TEST$PATTERN;
	CALL RQ$SLEEP(1, @status);							/* some delay */

$eject

	/*
	 * Zero indicates that there is no diaplay adapter present
	 */
	board$type = 0;

	/*
	 * Read test data from the display adapters
	 */
	IF (INPUT(MONO$INDEX$REG + DATA$REG$OFFSET) = TEST$PATTERN) THEN
		temp1 = TRUE;

	CALL RQ$SLEEP(1, @status);							/* some delay */

	IF (INPUT(COLOR$INDEX$REG + DATA$REG$OFFSET) = TEST$PATTERN) THEN
		temp2 = TRUE;

	IF temp1 AND temp2 THEN
		board$type = 3;				/* both adapters are present */
	ELSE
	DO;
		IF temp1 THEN
			board$type = 1;			/* monochrome adapter is present */
		ELSE
			IF temp2 THEN
				board$type = 2;		/* color adapter is present */
	END;

	RETURN(board$type);

END i120$get$adapter$type;


$SUBTITLE('Local procedure: Flush keyboard interrupt')
/*
 * Title:				 i120$flush$kb$int
 *
 * ABSTRACT:
 *		Handles the key released interrupt after ctrl-alt-brk.
 *		It is called just before jumping to SDM.
 *		This procedure reads the keyboard data port to flush the keyboard
 *		interrupt.
 *
 * CALLING SEQUENCE:	CALL i120$flush$kb$int(cdata$t);
 *
 * INTERFACE VARIABLES:
 *		cdata$t		- TOKEN for controller data segment
 *						not used by this driver
 */

i120$flush$kb$int: PROCEDURE (cdata$t) REENTRANT;

DECLARE	
	dummy				BYTE,
	cdata$t	 			SELECTOR;


	/* read the keyboard to flush keyboard interrupt */
	
	dummy = INPUT(KEYB$DATA$PORT);

END i120$flush$kb$int;


END xconsole;
