PAGE 118,121
TITLE BIOS1 ---- 06/10/85 INTERRUPT 15H BIOS ROUTINES
.286C
.LIST
CODE	SEGMENT BYTE PUBLIC

	PUBLIC	CASSETTE_IO_1
	PUBLIC	GATE_A20
	PUBLIC	SHUT9

	EXTRN	CMOS_READ:NEAR		; READ CMOS LOCATION ROUTINE
	EXTRN	CMOS_WRITE:NEAR		; WRITE CMOS LOCATION ROUTINE
	EXTRN	CONF_TBL:NEAR		; SYSTEM/BIOS CONFIGURATION TABLE
	EXTRN	DDS:NEAR		; LOAD (DS) WITH DATA SEGMENT SELECTOR
	EXTRN	PROC_SHUTDOWN:NEAR	; 80286 HARDWARE RESET ROUTINE

;--- INT 15 H --------------------------------------------------------------------
;    INPUT - CASSETTE I/O FUNCTIONS		:
;				:
;	  (AH) = 00H		:
;	  (AH) = 01H		:
;	  (AH) = 02H		:
;	  (AH) = 03H		:
;	RETURNS FOR THESE FUNCTIONS ALWAYS (AH) = 86H, CY = 1)	:
;	IF CASSETTE PORT NOT PRESENT		:
;--------------------------------------------------------------------------------:
;    INPUT - UNUSED FUNCTIONS		:
;	  (AH) = 04H THROUGH 7FH		:
;	RETURNS FOR THESE FUNCTIONS ALWAYS (AH) = 86H, CY = 1)	:
;		(UNLESS INTERCEPTED BY SYSTEM HANDLERS)	:
;		NOTE: THE KEYBOARD INTERRUPT HANDLER INTERRUPTS WITH AH=4FH	:
;--------------------------------------------------------------------------------:
; EXTENSIONS			:
;	(AH) = 80H	DEVICE OPEN		:
;			(BX) = DEVICE ID	:
;			(CX) = PROCESS ID	:
;					:
;	(AH) = 81H	DEVICE CLOSE		:
;			(BX) = DEVICE ID	:
;			(CX) = PROCESS ID	:
;					:
;	(AH) = 82H	PROGRAM TERMINATION	:
;			(BX) = DEVICE ID	:
;					:
;	(AH) = 83H	EVENT WAIT		:
;					:
;			(AL) = 00H SET INTERVAL	:
;			(ES:BX) POINTER TO A BYTE IN CALLERS MEMORY	:
;		           THAT WILL HAVE THE HIGH ORDER BIT SET	:
;			         AS SOON AS POSSIBLE AFTER THE INTERVAL	:
;			         EXPIRES.	:
;		   	(CX,DX) NUMBER OF MICROSECONDS TO ELAPSE BEFORE	:
;			         POSTING.	:
;			(AL) = 01H CANCEL	:
;					:
;	          RETURNS: CARRY IF AL NOT = 00H OR 01H	:
;		    OR IF FUNCTION AL=0 ALREADY BUSY	:
;					:
;	(AH) = 84H	JOYSTICK SUPPORT		:
;			(DX) = 00H - READ THE CURRENT SWITCH SETTINGS	:
;			         RETURNS AL = SWITCH SETTINGS (BITS 7-4)	:
;			(DX) = 01H - READ THE RESISTIVE INPUTS	:
;				RETURNS	AX = A(x) VALUE	:
;					BX = A(y) VALUE	:
;					CX = B(x) VALUE	:
;					DX = B(y) VALUE	:
;						:
;	(AH) = 85H	SYSTEM REQUEST KEY PRESSED	:
;			(AL) = 00H MAKE OF KEY	:
;			(AL) = 01H BREAK OF KEY	:
;						:
;	(AH) = 86H	WAIT			:
;			(CX,DX)	NUMBER OF MICROSECONDS TO ELAPSE BEFORE	:
;				RETURN TO CALLER	:
;						:
;	(AH) = 87H	MOVE BLOCK			:
;			(CX)	NUMBER OF WORDS TO MOVE	:
;			(ES:SI)	POINTER TO DESCRIPTOR TABLE	:
;						:
;	(AH) = 88H	EXTENDED MEMORY SIZE DETERMINE	:
;						:
;	(AH) = 89H	PROCESSOR TO VIRTUAL MODE	:
;						:
;	(AH) = 90H	DEVICE BUSY LOOP		:
;			(AL)	SEE TYPE CODE	:
;						:
;	(AH) = 91H	INTERRUPT COMPLETE FLAG SET	:
;			(AL)	TYPE CODE	:
;			00H -->	7FH		:
;				 SERIALLY REUSABLE DEVICES	:
;				 OPERATING SYSTEM MUST SERIALIZE ACCESS	:
;			80H -->	BFH		:
;				 REENTRANT DEVICES; ES:BX IS USED TO	:
;				 DISTINGUISH DIFFERENT CALLS (MULTIPLE I/O	:
;				 CALLS ARE ALLOWED SIMULTANEOUSLY)	:
;			C0H -->	FFH		:
;				 WAIT ONLY CALLS -- THERE IS NO	:
;				 COMPLEMENTARY 'POST' FOR THESE WAITS.	:
;				 THESE ARE TIMEOUT ONLY. TIMES ARE	:
;				 FUNCTION NUMBER DEPENDENT.	:
;						:
;			TYPE	DESCRIPTION 		TIMEOUT	:
;			00H = DISK			YES	:
;			01H = DISKETTE			YES	:
;			02H = KEYBOARD			NO	:
;			80H = NETWORK			NO	:
;				ES:BX --> NCB		:
;			FDH = DISKETTE MOTOR START	YES	:
;			FEH = PRINTER 			YES	:
;						:
;	(AH) = C0H	RETURN CONFIGURATION PARAMETERS POINTER	:
;			RETURNS			:
;			 (AH) = 00H AND CY= 0 (IF PRESENT ELSE 86 AND CY= 1)	:
;			 (ES:BX) = PARAMETER TABLE ADDRESS POINTER	:
;				WHERE:		:
;						:
;				DW	8		LENGTH OF FOLLOWING TABLE	:
;				DB	MODEL_BYTE	SYSTEM MODEL BYTE	:
;				DB	TYPE_BYTE	SYSTEM MODEL TYPE BYTE	:
;				DB	BIOS_LEVEL	BIOS REVISION LEVEL	:
;				DB	?	10000000 = DMA CHANNEL 3 USE BY BIOS	:
;						01000000 = CASCADED INTERRUPT LEVEL 2	:
;						00100000 = REAL TIME CLOCK AVAILABLE	:
;						00010000 = KEYBOARD SCAN CODE HOOK 1AH	:
;				DB	0		RESERVED	:
;				DB	0		RESERVED	:
;				DB	0		RESERVED	:
;				DB	0		RESERVED	:
;					:
;---------------------------------------------------------------------------------
	ASSUME	CS:CODE

CASSETTE_IO_1	PROC	FAR
	STI				; ENABLE INTERRUPTS
	CMP	AH,080H			; CHECK FOR RANGE
	JB	C1			; RETURN IF 00-7FH
	CMP	AH,0C0H			; CHECK FOR CONFIGURATION PARAMETERS
	JE	CONF_PARMS
	SUB	AH,080M			; BASE ON 0
	OR	AH,AH
	JZ	DEV_OPEN		; DEVICE OPEN
	DEC	AH
	JZ	DEV_CLOSE		; DEVICE CLOSE
	DEC	AH
	JZ	PROG_TERM		; PROGRAM TERMINATION
	DEC	AH
	JZ	EVENT_WAIT		; EVENT WAIT
	DEC	AH
	JNZ	NOT_JOYSTICK
	JMP	JOY_STICK		; JOYSTICK BIOS
NOT_JOYSTICK:
	DEC	AH
	JZ	SYS_REQ			; SYSTEM REQUEST KEY
	DEC	AH
	JZ	C1_A			; WAIT
	DEC	AH
	JNZ	C1_B
	JMP	BLOCKMOVE		; MOVE BLOCK

C1_A:	JMP	WAIT			; WAIT

C1_B:	DEC	AH

	JNZ	C1_C
	JMP	EXT_MEMORY		; GO GET THE EXTENDED MEMORY

C1_C:	DEC	AH
	JNZ	C1,D			; CHECK FOR FUNCTION 89H
	JMP	SET_VMODE		; SWAP TO VIRTUAL MODE

C1_D:	SUB	AH,7			; CHECK FOR FUNCTION 90H
	JNZ	C1_E			; GO IF NOT
	JMP	DEVICE_BUSY

C1_E:	DEC	AH			; CHECK FOR FUNCTION 8BH
	JNZ	C1			; GO IF NOT
	JMP	INT_COMPLETE

C1:	MOV	AH,86H			; SET BAD COMMAND
	STC				; SET CARRY FLAG ON
C1_F:	
	RET	2			; FAR RETURN EXIT FROM ROUTINES

DEV_OPEN:				; NULL HANDLERS

DEV_CLOSE:

PROG_TERM:

SYS_REQ:
	JMP	C1_F			; RETURN
CASSETTE_IO_1	ENDP

CONF_PARMS	PROC	NEAR
	PUSH	CS			; GET CODE SEGMENT
	POP	ES			; PLACE IN SELECTOR POINTER
	MOV	BX,OFFSET CONF_TBL	; GET OFFSET OF PARAMETER TABLE
	XOR	AH,AH			; CLEAR AH AND SET CARRY OFF
	JMP	C1_F			; EXIT THROUGH COMMON RETURN
CONF_PARMS	ENDP

EVENT_WAIT	PROC	NEAR
	ASSUME	DS:DATA
	PUSH	DS			; SAVE
	CALL	DDS	
	OR	AL,AL
	JZ	EVENT_WAIT_2		; GO IF ZERO
	DEC	AL			; CHECK IF 1
	JZ	EVENT_WAIT_3
	POP	DS			; RESTORE DATA SEGMENT
	STC				; SET CARRY
	JMP	C1_F			; EXIT

EVENT_WAIT_2:
	CLI				; NO INTERRUPTS ALLOWED
	TEST	@RTC_WAIT_FLAG,01	; CHECK FOR FUNCTION ACTIVE
	JZ	EVENT_WAIT_1
	STI				; ENABLE INTERRUPTS
	POP	DS
	STC				; SET ERROR
	JMP	C1_F			; RETURN

EVENT_WAIT_1:
	IN	AL,INTB01		; ENSURE INTERRUPT UNMASKED
	JMP	$+2
	AND	AL,DFEH
	OUT	INTBOI,AL
	MOV	@USER_FLAG_SEG,ES	; SET UP TRANSFER TABLE
	MOV	@USER_FLAG,BX
	MOV	@RTC_HIGH,CX
	MOV	@RTC_LOW,DX
	MOV	@RTC_WAIT_FLAG,01	; SET ON FUNCTION ACTIVE SWITCH
	MOV	AL,CMOS_REG_B		; ENABLE PIE
	CALL	CMOS_READ		; READ CMOS LOCATION
	AND	AL,07FH			; CLEAR SET
	OR	AL,040H			; ENABLE PIE
	PUSH	AX			; SAVE AH
	MOV	AH,AL			; PLACE DATA INTO DATA REGISTER
	MOV	AL,CMOS_REG_B		; ADDRESS ALARM REGISTER
	CALL	CMOS_WRITE		; PLACE DATA IN AH INTO ALARM REGISTER
	POP	AX			; RESTORE AH
	POP	DS
	STI				; ENABLE INTERRUPTS
	CLC				; CLEAR CARRY
	JMP	C1_F

;-----	CANCEL

EVENT_WAIT_3:
	PUSH	AX			; SAVE
	CLI				; DISABLE INTERRUPTS
	MOV	AX,X*CMOS REG_B		; TURN OFF PIE
	CALL	CMOS_READ		; GET ALARM REGISTER
	AND	AL,0BFH			; CLEAR PIE
	XCHG	AH,AL			; PLACE INTO WRITE REGISTER
	CALL	CMOS_WRITE		; WRITE BACK TO ALARM REGISTER
	POP	AX			; RESTORE AH
	MOV	@RTC_WAIT_FLAG,0	; SET FUNCTION ACTIVE FLAG OFF
	STI				; ENABLE INTERRUPTS
	POP	DS			; RESTORE DATA SEGMENT
	CLC				; SET CARRY OFF
	JMP	C1_F			; RETURN
EVENT WAIT	ENDP

;--- JOY_STICK ----------------------------------------------------
;	THIS ROUTINE WILL READ THE JOYSTICK PORT	:
;				:
;	INPUT			:
;	(DX)=0	READ THE CURRENT SWITCHES		:
;		RETURNS (AL)= SWITCH SETTINGS IN BITS 7-4	:
;				;
;	(DX)=1	READ THE RESISTIVE INPUTS		:
;		RETURNS (AX)=A(x) VALUE		:
;		        (BX)=A(y) VALUE		:
;		        (CX)=B(x) VALUE		:
;		        (DX)=B(y) VALUE		:
;				:
;	CY FLAG ON IF NO ADAPTER CARD OR INVALID CALL	:
;------------------------------------------------------------------

JOY_STICK	PROC	NEAR
	STI				; INTERRUPTS BACK ON
	MOV	AX,DX			; GET SUB FUNCTION CODE
	MOV	DX,201H			; ADDRESS OF PORT
	OR	AL,AL
	JZ	JOY_2			; READ SWITCHES
	DEC	AL
	JZ	JOY_3			; READ RESISTIVE INPUTS
	JMP	C1			; GO TO ERROR RETURN
JOY_1:		
	STI
	JMP	C1_F			; GO TO COMMON RETURN

JOY_2:
	IN	AL,DX
	AND	AL,0F0H			; STRIP UNWANTED BITS OFF
	JMP	JOY_1			; FINISHED

JOY_3:
	MOV	BL,1
	CALL	TEST_CORD
	PUSH	CX			; SAVE A(X) VALUE
	MOV	BL,2
	CALL	TEST_CORD
	PUSH	CX			; SAVE A(Y) VALUE
	MOV	BL,4
	CALL	TEST_CORD
	PUSH	CX			; SAVE B(X) VALUE
	MOV	BL,8
	CALL	TEST_CORD
	MOV	DX,CX			; SAVE B(Y) VALUE
	POP	CX			; GET B(X) VALUE
	POP	BX			; GET A(Y) VALUE
	POP	AX			; GET A(X) VALUE
	JMP	JOY_1			; FINISHED - RETURN

TEST_CORD	PROC	NEAR
	PUSH	DX			; SAVE
	CLI				; BLOCK INTERRUPTS WHILE READING
	MOV	AL,0			; SET UP TO LATCH TIMER 0
	OUT	TIMER+3,AL
	JMP	$+2
	IN	AL,TIMER		; READ LOW BYTE OF TIMER 0
	JMP	$+2
	MOV	AH,AL
	IN	AL,TIMER		; READ HIGH BYTE OF TIMER 0
	XCHG	AH,AL			; REARRANGE TO HIGH,LOW
	PUSH	AX			; SAVE
	MOV	CX,4FFH			; SET COUNT
	OUT	DX,AL			; FIRE TIMER
	JMP	$+2
TEST_CORD_1:
	IN	AL,DX			; READ VALUES
	TEST	AL,BL			; HAS PULSE ENDED?
	LOOPNZ	TEST_CORD_1
	CMP	CX,0
	POP	CX			; ORIGINAL COUNT
	JNZ	SHORT TEST_CORD_2
	SUB	CX,CX			; SET 0 COUNT FOR RETURN
	JMP	SHORT TEST_CORD_3	; EXIT WITH COUNT = 0
TEST_CORD_2:
	MOV	AL,0			; SET UP TO LATCH TIMER 0
	OUT	TIMER+3,AL
	JMP	$+2
	IN	AL,TIMER		; READ LOW BYTE OF TIMER 0
	MOV	AH,AL
	JMP	$+2
	IN	AL,TIMER		; READ HIGH BYTE OF TIMER 0
	XCHG	AH,AL			; REARRANGE TO HIGH,LOW

	CMP	CX,AX			; CHECK FOR COUNTER WRAP
	JAE	TEST_CORD_4		; GO IF NO
	PUSH	DX
	MOV	DX,-1

	SUB	DX,AX			; ADJUST FOR WRAP
	ADD	CX,DX
	POP	DX
	JMP	SHORT TEST_CORD_5

TEST_CORD_4:
	SUB	CX,AX
TEST_CORD_5:
	AND	CX,1FF0H		; ADJUST
	SHR	CX,4

TEST_CORD_3:
	STI				; INTERRUPTS BACK ON
	MOV	DX,201H			; FLUSH OTHER INPUTS
	PUSH	CX
	PUSH	AX
	MOV	CX,4FFH			; COUNT
TEST_CORD_6:
	IN	AL,DX
	TEST	AL,0FH
	LOOPNZ	TEST_CORD_6

	POP	AX
	POP	CX
	POP	DX			; SET COUNT
	RET				; RETURN

TEST_CORD	ENDP
JOY_STICK	ENDP

WAIT	PROC	NEAR
	PUSH	DS			; SAVE
	CALL	DDS
	TEST	@RTC_WAIT_FLAG,01	; TEST FOR FUNCTION ACTIVE
	JZ	WAIT_1
	POP	DS
	STC				; SET ERROR
	JMP	C1_F			; RETURN
WAIT_1:
	CLI				; NO INTERRUPTS ALLOWED
	IN	AL,INTB01		; ENSURE INTERRUPT UNMASKED
	JMP	$+2
	AND	AL,0FEH
	OUT	INTB01,AL
	MOV	@USER_FLAG_SEG,DS	; SET UP TRANSFER TABLE
	MOV	@USER_FLAG,OFFSET @RTC_WAIT_FLAG
	MOV	@RTC_HIGH,CX
	MOV	@RTC_LOW,DX
	MOV	@RTC_WAIT_FLAG,01	; SET ON FUNCTION ACTIVE SWITCH
	PUSH	AX			; SAVE (AH)
	MOV	AX,X*CMOS_REG_B		; ENABLE PIE
	CALL	CMOS_READ		; READ ALARM BYTE
	AND	AL,07FH			; CLEAR SIT BIT
	OR	AL,040H			; ENABLE PIE BIT
	XCHG	AH,AL			; DATA TO WORK REGISTER
	CALL	CMOS_WRITE		; WRITE NEW ALARM BYTE
	POP	AX			; RESTORE (AH)

;-----	WAIT TILL RTC TIMEOUT POSTED  (WITH ERROR TIMEOUT)

	STI				; ENABLE INTERRUPTS
	PUSH	CX
	PUSH	DX			; SAVE CALLERS PARAMETERS
	XCHG	DX,CX			; SWAP COUNT WORK REGISTERS
WAIT_2:
	TEST	@RTC_WAIT_FLAG,080H	; CHECK FOR END OF WAIT - CLEAR CARRY
	LOOPZ	WAIT_2			; DECREMENT TIMEOUT DELAY TILL WAIT END
	JNZ	WAIT_9			; EXIT IF RTC TIMER WAIT ENDED FLAG SET
	SUB	DX,1			; DECREMENT ERROR TIMEOUT COUNTER
	JNC	WAIT_2			; LOOP TILL COUNTERS TIMEOUT
WAIT_9:
	MOV	@RTC_WAIT_FLAG,0	; SET FUNCTION INACTIVE
	POP	DX
	POP	CX			; RESTORE CALLERS PARAMETERS
	POP	DS
	CLC				; CLEAR CARRY FLAG
	JMP	C1_F

WAIT	ENDP

;--- INT 15 H - ( FUNCTION 87 H - BLOCK MOVE ) -----------------------------------
;				:
;	THIS BIOS FUNCTION PROVIDES A MEANS FOR A REAL MODE PROGRAM OR SYSTEM	:
;	TO TRANSFER A BLOCK OF STORAGE TO AND FROM STORAGE ABOVE THE 1 MEG	:
;	ADDRESS RANGE IN PROTECTED MODE SPACE BY SWITCHING TO PROTECTED MODE.	:
;				:
; ENTRY:				:
;	(AH) =	87 H (FUNCTION CALL) - BLOCK MOVE.	:
;	(CX) =	WORD COUNT OF STORAGE BLOCK TO BE MOVED.	:
;		NOTE: MAX COUNT = 8000H FOR 32K WORDS (65K BYTES) 	:
;	ES:SI = LOCATION OF A GOT TABLE BUILT BY ROUTINE USING THIS FUNCTION.	:
;				:
;	(ES:SI) POINTS TO A DESCRIPTOR TABLE (GDT) BUILT BEFORE INTERRUPTING	:
;	TO THIS FUNCTION. THE DESCRIPTORS ARE USE TO PERFORM THE BLOCK	:
;	MOVE IN THE PROTECTED MODE. THE SOURCE AND TARGET DESCRIPTORS	:
;	BUILT BY THE USER MUST HAVE A SEGMENT LENGTH = 2 * CX-1 OR GREATER.	:
;	THE DATA ACCESS RIGHTS BYTE MUST BE SET TO CPL0-R/W (93H). THE	:
;	24 BIT ADDRESS (BYTE HI, WORD LOW) MUST BE SET TO THE TARGET/SOURCE.	:
;				:
;	***  NO INTERRUPTS ARE ALLOWED DURING TRANSFER. LARGE BLOCK MOVES	:
;	     MAY CAUSE LOST INTERRUPTS.		:
;				:
; EXIT:				:
;	(AH) = 00H   IF SUCCESSFUL		:
;	(AH) = 01H   IF MEMORY PARITY (PARITY ERROR REGISTERS ARE CLEARED)	:
;	(AH) = 02H   IF ANY OTHER EXCEPTION INTERRUPT ERROR OCCURRED	:
;	(AH) = 03H   IF GATE ADDRESS LINE 20 FAILED	:
;		   ALL REGISTERS ARE RESTORED EXCEPT (AH).	:
;				:
;	IF SUCCESSFUL - CARRY FLAG = 0		:
;	IF ERROR ------ CARRY FLAG = 1		:
;				:
; DESCRIPTION:			:
;				:
;	1.  SAVE ENTRY REGISTERS AND SETUP FOR SHUTDOWN EXIT.	:
;	2.  THE REQUIRED ENTRIES ARE BUILT IN THE GDT AT (ES:SI).	:
;	3.  GATE ADDRESS LINE 20 ACTIVE, CLI AND SET SHUTDOWN CODES.	:
;	4.  THE IDTR IS LOADED AND POINTS TO A ROM RESIDENT TABLE.	:
;	5.  THE GDTR IS LOADED FROM THE OFFSET POINTER (ES:SI).	:
;	6.  THE PROCESSOR IS PUT INTO PROTECTED MODE.	:
;	7.  LOAD (DS) AND (ES) WITH SELECTORS FOR THE SOURCE AND TARGET.	:
;	8.  DS:SI (SOURCE)  (ES:DI)  (TARGET) REP MOVSW IS EXECUTED.	:
;	9.  CHECK MADE FOR PARITY ERRORS.	:
;       10.  REAL MODE RESTORED WHEN SHUTDOWN 09H IS EXECUTED.	:
;       11.  ERRORS ARE CHECKED FOR AND RETURN CODES ARE SET FOR (AH).	:
;       12.  ADDRESS LINE 20 GATE IS DISABLED.	:
;       13.  RETURN WITH REGISTERS RESTORED AND STATUS RETURN CODE.	:
;		(FOR PC-AT COMPATIBILITY ZF=1 IF SUCCESSFUL, ZF=0 IF ERROR.)	:
;---------------------------------------------------------------------------------
;
;	THE FOLLOWING DIAGRAM DEPICTS THE ORGANIZATION OF A BLOCK MOVE GDT.
;---------------------------------------------------------------------------------
;               G D T		:
; (ES:SI)			:
;    |            .-----------.		:
;    |            V           |		:
;    +00  .---------------.   |		:
;         |     DUMMY     |   |	1. THE FIRST DESCRIPTOR IS THE REQUIRED DUMMY.	:
;         |               |   |	   (USER INITIALIZED TO 0)	:
;    +08  |---------------|   |		:
;         |    GDT LOC    | --.	2. THE SECOND DESCRIPTOR POINTS TO THE GDT	:
;         |               |	   TABLE AS A DATA SEGMENT.	:
;    +10  |---------------|	   (USER INITIALIZED TO 0 - MODIFIED BY BIOS)	:
;         |    SOURCE     |	3. THE THIRD DESCRIPTOR POINTS TO THE SOURCE	:
;         |     GDT       |	   TO BE MOVED. (FROM)	:
;    +18  |---------------|	   (USER INITIALIZED)	:
;         |    TARGET     |	4. THE FOURTH DESCRIPTOR POINTS TO THE	:
;         |     GDT       |	   DESTINATION SEGMENT. (TO)	:
;    +20  |---------------|	   (USER INITIALIZED)	:
;         |     BIOS      |	5. THE FIFTH IS A DESCRIPTOR THAT BIOS USES	:
;         |     (CS)      |	   TO CREATE THE PROTECTED MODE CODE SEGMENT.	:
;    +28  |---------------|	   (USER INITIALIZED TO 0 - MODIFIED BY BIOS)	:
;         |     (SS)      |	6. THE SIXTH DESCRIPTOR IS USED BY BIOS TO	:
;         |               |	   CREATE A PROTECTED MODE STACK SEGMENT.	:
;         '---------------'	   (USER INITIALIZED TO 0 - MODIFIED BY BIOS)	:
;			   (POINTS TO USERS STACK)	:
;				:
;				:
;		SAMPLE  OF  SOURCE  OR  TARGET  DESCRIPTOR	:
;				:
;	SOURCE_TARGET_DEF	STRUC	:
;						:
;	SEG_LIMIT 	DW	?	; SEGMENT LIMIT (1-65536 BYTES)	:
;	LO_WORD		DW	?	; 24 BIT SEGMENT PHYSICAL	:
;	HI_BYTE		DB	?	;    ADDRESS (0 TO (16M-1))	:
;	DATA_ACC_RIGHTS	DB	93H	; ACCESS RIGHTS BYTE (CPL0-R/W)	:
;	RESERVED	DW	0	; RESERVED WORD (MUST BE ZERO)	:
;						:
;	SOURCE_TARGET_DEF	ENDS			:
;---------------------------------------------------------------------------------
;
;	THE GLOBAL DESCRIPTOR TABLE (ACTUAL LOCATION POINTED TO BY ES:SI)

BLOCKMOVE_GDT_DEF	STRUC
		DQ	?		; FIRST DESCRIPTOR NOT ACCESSIBLE
CGDT_LOC	DQ	?		; LOCATION OF CALLING ROUTINE GDT
SOURCE		DQ 	?		; SOURCE DESCRIPTOR
TARGET		DQ	?		; TARGET DESCRIPTOR
BIOS_CS		DQ	?		; BIOS CODE DESCRIPTOR
TEMP_SS		DQ	?		; STACK DESCRIPTOR
BLOCKMOVE_GDT_DEF	ENDS

BLOCKMOVE	PROC	NEAR

	CLD				; SET DIRECTION FORWARD
	PUSHA				; SAVE GENERAL PURPOSE REGISTERS
	PUSH	ES			; SAVE USERS EXTRA SEGMENT
	PUSH	DS			; SAVE USERS DATA SEGMENT

;-----	SAVE THE CALLING ROUTINE'S STACK

	CALL	DDS			; SET DS TO DATA AREA
	MOV	@IO_ROM_SEG,SS		; SAVE USERS STACK SEGMENT
	MOV	@IO_ROM_INIT,SP		; SAVE USERS STACK POINTER

;=====	SET UP THE PROTECTED MODE DEFINITIONS  =====
;-----	MAKE A 24 BIT ADDRESS OUT OF THE ES:SI	FOR THE GDT POINTER

	ASSUME	DS:NOTHING		; POINT (DS) TO USERS CONTROL BLOCK
	MOV	AX,ES			; GET THE GDT DATA SEGMENT
	MOV	DS,AX			; MOVE THE GOT SEGMENT POINTER TO (DS)
	MOV	DH,AH			; BUILD HIGH BYTE OF THE 24 BIT ADDRESS
	SHR	DH,4			; USE ONLY HIGH NIBBLE SHIFT - RIGHT 4
	SHL	AX,4			; STRIP HIGH NIBBLE FROM (AX)
	ADD	AX,SI			; ADD THE GOT OFFSET TO DEVELOP LOW WORD
	ADC	DH,0			; ADJUST HIGH BYTE IF CARRY FROM LOW

;-----	SET THE GDT_LOC

	MOV	[SI].CGDT_LOC.SEG_LIMIT,MAX_SEG_LEN
	MOV	[SI].CGDT_LOC.BASE_LO_WORD,AX	; SET THE LOW WORD
	MOV	[SI].CGDT_LOC.BASE_HI_BYTE,DH	; SET THE HIGH BYTE
	MOV	[SI].CGDT_LOC.DATA_RESERVED,0	; RESERVED

;-----	SET UP THE CODE SEGMENT DESCRIPTOR
	MOV	[SI].BIOS_CS.SEG_LIMIT,MAX_SEG_LEN
	MOV	[SI].BIOS_CS.BASE_LO_WORD,CSEG@_LO	; LOW WORD OF (CS)= 0
	MOV	[SI].BIOS_CS.BASE_HI_BYTE,CSEG@_HI	; HIGH BYTE OF (CS)= 0FH
	MOV	[SI].BIOS_CS.DATA_ACC_RIGHTS,CPL0_CODE_ACCESS
	MOV	[SI].BIOS_CS.DATA_RESERVED,0		; RESERVED

;-----	MAKE A 24 BIT ADDRESS OUT OF THE (SS) - ( (SP) REMAINS USER (SP) )

	MOV	AX,SS			; GET THE CURRENT STACK SEGMENT
	MOV	DH,AH			; FORM HIGH BYTE OF 24 BIT ADDRESS
	SHR	DH,4			; FORM HIGH BYTE - SHIFT RIGHT 4
	SHL	AX,4			; STRIP HIGH NIBBLE FROM (AX)

;-----	SS IS NOW IN POSITION FOR A 24 BIT ADDRESS --> SETUP THE (SS) DESCRIPTOR

	MOV	[SI].TEMP_SS.SEG_LIMIT,MAX_SEG_LEN	; SET THE SS SEGMENT LIMIT
	MOV	[SI].TEMP_SS.BASE_LO_WORD,AX		; SET THE LOW WORD
	MOV	[SI].TEMP_SS.BASE_HI_BYTE,DH		; SET THE HIGH BYTE
	MOV	[SI].TEMP_SS.DATA_ACC_RIGHTS,CPL0_DATA_ACCESS	; SET CPL 0

;-----	GATE ADDRESS BIT 20 ON (DISABLE INTERRUPTS)

	MOV	AH,ENABLE_BIT20		; GET ENABLE MASK
	CALL	GATE_A20		; ENABLE A20 AND CLEAR INTERRUPTS
	CMP	AL,0			; WAS THE COMMAND ACCEPTED?
	JZ	BL4			; GO IF YES

	MOV	AL,03H			; SET THE ERROR FLAG IF NOT
	OUT	MFG_PORT,AL
	JMP	SHORT SHUT9		; EARLY ERROR EXIT

;-----	SET SHUTDOWN RETURN ADDRESS AND DISABLE NMI 
BL4:
	MOV	AX,9*H+CMOS_SHUT_DOWN+NMI		; SET THE SHUTDOWN BYTE LOCATION
	CALL	CMOS_WRITE		; TO SHUT DOWN 9 AND DISABLE NMI

;-----	CLEAR EXCEPTION ERROR FLAG

	SUB	AL,AL
	OUT	MFG_PORT,AL		; SET ERROR FLAG LOCATION TO 0

;-----	LOAD THE IDT AND GDT

	MOV	BP,OFFSET ROM_IDT_LOC
	SEGOV	CS			; LOAD THE IDT
	DB	02EH
	LIDT	[BP]			; REGISTER FROM THIS AREA
	DB	00FH
??0001	LABEL	BYTE
	MOV	BX,WORD PTR [BP]
??0002	LABEL	BYTE
	ORG	OFFSET CS:??0001
	DB	001H
	ORG	OFFSET CS:??0002

	LGDT	[SI].CGDT_LOC		; LOAD GLOBAL DESCRIPTOR TABLE REGISTER
	DB	00FH
??0003	LABEL	BYTE
	MOV	DX,WORD PTR [SI].CGDT_LOC
??0004	LABEL	BYTE
	ORG	OFFSET CS:??0003
	DB	001H
	ORG	OFFSET CS:??0004

;-----	SWITCH TO VIRTUAL MODE

	MOV	AX,VIRTUAL_ENABLE	; MACHINE STATUS WORD NEEDED TO
	LMSW	AX			;  SWITCH TO VIRTUAL MODE
	DB	00FH,001H,0F0H
	DB	0EAH			; PURGE PRE-FETCH QUEUE WITH FAR JUMP
	DW	OFFSET VIRT		;  - TO OFFSET
	DW	BIOS_CS			;  - IN SEGMENT -PROTECTED MODE SELECTOR
VIRT:

;-----	IN PROTECTED MODE - SETUP STACK SELECTOR AND SOURCE/TARGET SELECTORS

	MOV	AX,TEMP_SS		; USER'S SS-SP IS NOT A DESCRIPTOR
	MOV	SS,AX			; LOAD STACK SELECTOR
	MOV	AX,SOURCE		; GET THE SOURCE ENTRY
	MOV	DS,AX			; LOAD SOURCE SELECTOR
	MOV	AX,TARGET		; GET THE TARGET ENTRY
	MOV	ES,AX			; LOAD TARGET SELECTOR
	SUB	SI,SI			; SET SOURCE INDEX REGISTER TO ZERO
	SUB	DI,DI			; SET TARGET INDEX REGISTER TO ZERO

	REP	MOVSW			; MOVE THE BLOCK COUNT PASSED IN (CX)

;-----	CHECK FOR MEMORY PARITY BEFORE SHUTDOWN
	IN	AL,PORT_B		; GET THE PARITY LATCHES
	AND	AL,PARITY_ERR		; STRIP UNWANTED BITS
	JZ	DONE1			; GO IF NO PARITY ERROR

;-----	CLEAR PARITY BEFORE SHUTDOWN

	MOV	AX,DS:[DI]		; FETCH CURRENT SOURCE DATA
	MOV	DS:[DI],AX		; WRITE IT BACK
	MOV	AL,01			; SET PARITY CHECK ERROR = 01
	OUT	MFG_PORT,AL
	IN	AL,PORT_B
	OR	AL,RAM_PAR_OFF		; TOGGLE PARITY CHECK LATCHES
	OUT	PORT_B,AL		;  TO CLEAR THE PENDING ERROR
	AND	AL,RAM_PAR_ON		;  AND ENABLE CHECKING
	OUT	PORT_B,AL

;-----	CAUSE A	SHUTDOWN
DONE1:
	JMP	PROC_SHUTOOWN		; GO RESET PROCESSOR AND SHUTDOWN

	;======================
;-----	  RETURN FROM SHUTDOWN
	;======================
SHUT9:					;	  RESTORE USERS STACK
	ASSUME	DS:DATA
	MOV	AX,DATA			; SET DS TO DATA AREA
	MOV	DS,AX
	MOV	SS,@IO_ROM_SEG		; GET USER STACK SEGMENT
	MOV	SP,@IO_ROM_INIT		; GET USER STACK POINTER

;-----	GATE ADDRESS BIT 20 OFF

	MOV	AH,DISASLE_BIT20	; DISABLE MASK
	CALL	GATE_A20		; GATE ADDRESS 20 LINE OFF
	CMP	AL,0			; COMMAND ACCEPTED?
	JZ	DONE3			; GO IF YES

	IN	AL,MFG_PORT		; CHECK FOR ANY OTHER ERROR FIRST
	CMP	AL,0			; WAS THERE AN ERROR?
	JNZ	DONE3			; REPORT FIRST ERROR IF YES
	MOV	AL,03H			; ELSE SET GATE A20 ERROR FLAG
	OUT	MFG_PORT,AL

;-----	RESTORE	THE USERS REGISTERS AND SET RETURN CODES
DONE3:
	MOV	AX,CMOS_REG_D		; CLEAR (AH) TO ZERO AND (AL) TO DEFAULT
	OUT	CMOS_PORT,AL		; ENABLE NMI INTERRUPTS

	POP	DS			; RESTORE USER DATA SEGMENT
	POP	ES			; RESTORE USER EXTRA SEGMENT
	IN	AL,MFG_PORT		; GET THE ENDING STATUS RETURN CODE
	MOV	BP,SP			; POINT TO REGISTERS IN THE STACK
	MOV	[BP+15],AL		; PLACE ERROR CODE INTO STACK AT (AH)
	CMP	AH,AL			; SET THE ZF & CY FLAGS WITH RETURN CODE
	POPA				; RESTORE THE GENERAL PURPOSE REGISTERS
	STI				; TURN INTERRUPTS ON
DONE4	PROC	FAR
	RET	2			; RETURN WITH FLAGS SET --  (AH)= CODE
DONE4	ENDP				;  (CY=0,ZF=1)= OK   (CY=1,ZF=0)= ERROR

;-----	BLOCK MOVE EXCEPTION INTERRUPT HANDLER

EX_INT:
	MOV	AL,02H			; GET EXCEPTION ERROR CODE
	OUT	MFG_PORT,AL		; SET EXCEPTION INTERRUPT OCCURRED FLA
	JMP	PROC_SHUTDOWN		; CAUSE A EARLY SHUTDOWN

;-----	ROM IDT LOCATION

ROM_IDT_LOC:
	DW	ROM_IDT_END-ROM_IDT	; LENGTH OF ROM IDT TABLE
	DW	ROM_IDT			; LOW WORD OF BASE ADDRESS
	DB	CSEG@_HI		; HIGH BYTE OF BASE ADDRESS
	DB	0			; RESERVED

;-----	THE ROM	EXCEPTION INTERRUPT VECTOR GATES FOR BLOCK MOVE
ROM_IDT:				;	  EXCEPTION 00
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 01
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 02
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 03
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 04
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 05
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 06
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 07
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 08
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 09
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 10
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 11
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 12
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 13
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 14
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 15
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 16
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 17
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 18
	DW	EX_ INT			; DESTINATION OFFSET
	DW	BIO_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 19
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 20
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 21
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 22
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 23
	DW	EX_ INT			; DESTINATION OFFSET
	DW	BIO_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 24
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 25
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 26
	DW	EX_ INT			; DESTINATION OFFSET
	DW	BIO_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 27
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 28
	DW	EX_ INT			; DESTINATION OFFSET
	DW	BIO_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 29
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 30
	DW	EX_INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED
					;	  EXCEPTION 31
	DW	EX_ INT			; DESTINATION OFFSET
	DW	BIOS_CS			; DESTINATION SEGMENT SELECTOR
	DB	0			; WORD COPY COUNT
	DB	TRAP_GATE		; GATE TYPE - ACCESS RIGHTS BYTE
	DW	0			; RESERVED

ROM_IDT_END:
BLOCKMOVE 	ENDP


;---------------------------------------------------------------------------------
; GATE_A20			:
;	THIS ROUTINE CONTROLS A SIGNAL WHICH GATES ADDRESS BIT 20.	:
;	THE GATE A20 SIGNAL IS AN OUTPUT OF THE 8042 SLAVE PROCESSOR.	:
;	ADDRESS BIT 20 SHOULD BE GATED ON BEFORE ENTERING PROTECTED MODE.	:
;	IT SHOULD BE GATED OFF AFTER ENTERING REAL MODE FROM PROTECTED	:
;	MODE.	INTERRUPTS ARE LEFT DISABLED ON EXIT.	:
; INPUT				:
;	(AH)= DDH  ADDRESS BIT 20 GATE OFF. (A20 ALWAYS ZERO)	:
;	(AH)= DFH  ADDRESS BIT 20 GATE ON. (A20 CONTROLLED BY 80286)	:
; OUTPUT				:
;	(AL)= 00H  OPERATION SUCCESSFUL. 8042 HAS ACCEPTED COMMAND.	:
;	(AL)= 02H  FAILURE--8042 UNABLE TO ACCEPT COMMAND.	;
;---------------------------------------------------------------------------------
GATE_A20		PROC
	PUSH	CX			; SAVE USERS (CX)
	CLI				; DISABLE INTERRUPTS WHILE USING 8042
	CALL	EMPTY_8042		; INSURE 8042 INPUT BUFFER EMPTY
	JNZ	GATE_A20_RETURN		; EXIT IF 8042 UNABLE TO ACCEPT COMMAND
	MOV	AL,0D1H			; 8042 COMMAND TO WRITE OUTPUT PORT
	OUT	STATUS_PORT,AL		; OUTPUT COMMAND TO 8042
	CALL	EMPTY_8042		; WAIT FOR 8042 TO ACCEPT COMMAND
	JNZ	GATE_A20_RETURN		; EXIT IF 8042 UNABLE TO ACCEPT COMMAND
	MOV	AL,AH			; 8042 PORT DATA
	OUT	PORT_A,AL		; OUTPUT PORT DATA TO 8042
	CALL	EMPTY_8042		; WAIT FOR 8042 TO ACCEPT PORT DATA

;-----	6042 OUTPUT WILL SWITCH WITHIN 20 MICRO SECONDS OF ACCEPTING PORT DATA

GATE_A20_RETURN:
	POP	CX			; RESTORE USERS (CX)
	RET

;--------------------------------------------------------------------------------- 
; EMPTY_8042			;
;	THIS ROUTINE WAITS FOR THE 8042 INPUT BUFFER TO EMPTY.	:
; INPUT				:
;	NONE			:
; OUTPUT				:
;	(AL)= 00H  8042 INPUT BUFFER EMPTY (ZERO FLAG SET)	:
;	(AL)= 02H  TIME OUT, 8042 INPUT BUFFER FULL (NON-ZERO FLAG SET)	:
;	(CX)       - MODIFIED		:
;---------------------------------------------------------------------------------
EMPTY_8042:
	SUB	CX,CX			; (CX)=0, WILL BE USED AS TIME OUT VALUE
EMPTY_L:
	IN	AL,STATUS_PORT		; READ 8042 STATUS PORT
	AND	AL,INPT_BUF_FULL	; TEST INPUT BUFFER FULL FLAG (BIT 1)
	LOOPNZ	EMPTY_L			; LOOP UNTIL BUFFER EMPTY OR TIME OUT
	RET
GATE_A20		ENDP

;--- INT 15 H - ( FUNCTION 88 H - I/O MEMORY SIZE DETERMINE ) --------------------
; EXT_MEMORY			:
;	THIS ROUTINE RETURNS THE AMOUNT OF MEMORY IN THE SYSTEM THAT IS	:
;	LOCATED STARTING AT THE 1024K ADDRESSING RANGE, AS DETERMINED BY	:
;	THE POST ROUTINES.		:
;	NOTE THAT THE SYSTEM MAY NOT BE ABLE TO USE I/O MEMORY UNLESS THERE	:
;	IS A FULL COMPLEMENT OF 512K OR 640 BYTES ON THE PLANAR. THIS SIZE	:
;	SIZE IS STORED IN CMOS AT ADDRESS LOCATIONS 30H AND 31H.	:
; INPUT				:
;	AH = 88H		:
;				:
;	THE I/O MEMORY SIZE VARIABLE IS SET DURING POWER ON	:
;	DIAGNOSTICS ACCORDING TO THE FOLLOWING ASSUMPTIONS:	:
;				:
;	1. ALL INSTALLED MEMORY IS FUNCTIONAL.	:
;	2. ALL MEMORY FROM 0 TO 640K MUST BE CONTIGUOUS.	:
;				:
; OUTPUT 			:
;	(AX) = NUMBER OF CONTIGUOUS 1K BLOCKS OF MEMORY A 	:
;	       AVAILABLE STARTING AT ADDRESS 1024K.	:
;				:
;---------------------------------------------------------------------------------

EXT_MEMORY	PROC
	MOV	AX,CMOS_U_M_S_LO*H+CMOS_U_M_S_HI	; ADDRESS HIGH/LOW BYTES
	CALL	CMOS_READ		; GET THE HIGH BYTE OF I/O MEMORY
	XCHG	AL,AH			; PUT HIGH BYTE IN POSITION (AH)
	CALL	CMOS_READ		; GET THE LOW BYTE OF I/O MEMORY
	IRET				; RETURN TO USER
EXT_MEMORY	ENDP

;--- INT 15 H ( FUNCTION 89 H ) --------------------------------------------------
; PURPOSE:			:
;	THIS BIOS FUNCTION PROVIDES A MEANS TO THE USER TO SWITCH INTO	:
;	VIRTUAL (PROTECTED) MODE. UPON COMPLETION OF THIS FUNCTION THE	:
;	PROCESSOR WILL BE IN VIRTUAL (PROTECTED) MODE AND CONTROL WILL	:
;	BE TRANSFERRED TO THE CODE SEGMENT THAT WAS SPECIFIED BY THE USER.	:
;				:
; ENTRY REQUIREMENTS:		:
;				:
;	(ES:SI) POINTS TO A DESCRIPTOR TABLE (GDT) BUILT BEFORE INTERRUPTING	:
;	TO THIS FUNCTION. THESE DESCRIPTORS ARE USED BY THIS FUNCTION TO	:
;	INITIALIZE THE IDTR, THE GDTR AND THE STACK SEGMENT SELECTOR. THE	:
;	DATA SEGMENT (DS) SELECTOR AND THE EXTRA SEGMENT (ES) SELECTOR WILL	:
;	BE INITIALIZE TO DESCRIPTORS BUILT BY THE ROUTINE USING THIS FUNCTION.	:
;	BH - OFFSET INTO THE INTERRUPT DESCRIPTOR TABLE STATING WHERE THE	:
;	     FIRST EIGHT HARDWARE INTERRUPTS WILL BEGIN. ( INTERRUPT LEVEL 1 )	:
;	BL - OFFSET INTO THE INTERRUPT DESCRIPTOR TABLE STATING WHERE THE	:
;	     SECOND EIGHT HARDWARE INTERRUPTS BEGIN. ( INTERRUPT LEVEL 2 )	:
;				:
; THE DESCRIPTORS ARE DEFINED AS FOLLOWS:		:
;				:
;	1.	THE FIRST DESCRIPTOR IS THE REQUIRED DUMMY.	:
;		(USER INITIALIZED TO 0)		:
;	2.	THE SECOND DESCRIPTOR POINTS TO THE GDT TABLE AS	:
;		A DATA SEGMENT.		:
;		(USER INITIALIZED)		:
;	3.	THE THIRD DESCRIPTOR POINTS TO THE USER DEFINED	:
;		INTERRUPT DESCRIPTOR TABLE (IDT).	:
;		(USER INITIALIZED)		:
;	4.	THE FORTH DESCRIPTOR POINTS TO THE USER'S DATA	:
;		SEGMENT (DS).		:
;		(USER INITIALIZED)		:
;	5.	THE FIFTH DESCRIPTOR POINTS TO THE USER'S EXTRA	:
;		SEGMENT (ES).		:
;		(USER INITIALIZED)		:
;	6.	THE SIXTH DESCRIPTOR POINTS TO THE USER'S STACK	:
;		SEGMENT (SS).		:
;		(USER INITIALIZED)		:
;	7.	THE SEVENTH DESCRIPTOR POINTS TO THE CODE SEGMENT	:
;		THAT THIS FUNCTION WILL RETURN TO.	:
;		(USER INITIALIZED TO THE USER'S CODE SEGMENT.)	:
;	8.	THE EIGHTH DESCRIPTOR IS USED BY THIS FUNCTION TO 	:
;		ESTABLISH A CODE SEGMENT FOR ITSELF. THIS IS	:
;		NEEDED SO THAT THIS FUNCTION CAN COMPLETE IT'S 	:
;		EXECUTION WHILE IN PROTECTED MODE. WHEN CONTROL 	:
;		GETS PASSED TO THE USER'S CODE THIS DESCRIPTOR CAN 	:
;		BE USED BY HIM IN ANY WAY HE CHOOSES.	:
;				:
;    NOTE - EACH DESCRIPTOR MUST CONTAIN ALL THE NECESSARY DATA	:
;	   I.E. THE LIMIT, BASE ADDRESS AND THE ACCESS RIGHTS BYTE.	:
;				:
;	AH= 89H	  (FUNCTION CALL)		:
;	ES:SI = LOCATION OF THE GDT TABLE BUILD BY ROUTINE	:
;	USING THIS FUNCTION.		:
;				:
; EXIT PARAMETERS:		:
;				:
;	AH = 0 IF SUCCESSFUL		:
;	ALL SEGMENT REGISTERS ARE CHANGED, (AX) AND (BP) DESTROYED	:
;				:
; CONSIDERATIONS:		:
;				:
;	1. 	NO BIOS AVAILABLE TO USER. USER MUST HANDLE ALL 	:
;		I/O COMMANDS.		:
;	2.	INTERRUPTS - INTERRUPT VECTOR LOCATIONS MUST BE	:
;		MOVED. DUE TO THE 2B6 RESERVED AREAS, THE	:
;		HARDWARE INTERRUPT CONTROLLERS MUST BE REINITIALIZED	:
;		TO DEFINE LOCATIONS THAT DO NOT RESIDE IN THE 286	:
;		RESERVED AREAS.		:
;	3.	EXCEPTION INTERRUPT TABLE AND HANDLER MUST BE 	:
;		INITIALIZED BY THE USER.		:
;	4.	THE INTERRUPT DESCRIPTOR TABLE MUST NOT OVERLAP 	:
;		THE REAL MODE BIOS INTERRUPT DESCRIPTOR TABLE.	:
;	5.	THE FOLLOWING GIVES AN IDEA OF WHAT THE USER CODE	:
;		SHOULD LOOK LIKE WHEN INVOKING THIS FUNCTION.	:
;				:
;	REAL MODE --->	"USER CODE"		:
;		"	MOV	AX,GDT SEGMENT	:
;		"	MOV	ES,AX	:
;		"	MOV	SI,GDT OFFSET	:
;		"	MOV	BH,HARDWARE INT LEVEL 1 OFFSET	:
;		"	MOV	BL,HARDWARE INT LEVEL 2 OFFSET	:
;		"	MOV	AH,89H	:
;		"	INT	15H	:
;	VIRTUAL MODE --->	"USER CODE"	:
;				:
; DESCRIPTION:			:
;				:
;	1. 	CLI (NO INTERRUPTS ALLOWED) WHILE THIS FUNCTION IS EXECUTING.	:
;	2.	ADDRESS LINE 20 IS GATED ACTIVE.	:
;	3.	THE CURRENT USER STACK SEGMENT DESCRIPTOR IS INITIALIZED.	:
;	4.	THE GDTR IS LOADED WITH THE GDT BASE ADDRESS.	:
;	5.	THE IDTR IS LOADED WITH THE IDT BASE ADDRESS.	:
;	6.	THE 8259 IS REINITIALIZED WITH THE NEW INTERRUPT OFFSETS.	:
;	7.	THE PROCESSOR IS PUT IN VIRTUAL MODE WITH THE CODE	:
;		SEGMENT DESIGNATED FOR THIS FUNCTION.	:
;	8.	DATA SEGMENT IS LOADED WITH THE USER DEFINED 	:
;		ELECTOR FOR THE DS REGISTER.	:
;	9.	EXTRA SEGMENT IS LOADED WITH THE USER DEFINED 	:
;		SELECTOR FOR THE ES REGISTER.	:
;       10.	STACK SEGMENT IS LOADED WITH THE USER DEFINED	:
;		SELECTOR FOR THE SS REGISTER.	:
;       11.	CODE SEGMENT DESCRIPTOR SELECTOR VALUE IS 	:
;		SUBSTITUTED ON THE STACK FOR RETURN TO USER.	:
;       12.	WE TRANSFER CONTROL TO THE USER WITH INTERRUPTS DISABLED.	:
;---------------------------------------------------------------------------------
;
;	THE FOLLOWING DIAGRAM DEPICTS THE ORGANIZATION
;	OF GDT.
;---------------------------------------------------------------------------------
;		              G D T
;				:
;		         		.-------------.	:
;		                	v             |	:
;     (ES:SI)-->>	+00	.----------------.    |	:
;				|     DUMMY      |    |	:
;				|                |    |	:
;			+08	|----------------|    |	:
;				|      GDT       |  --'	:
;				|                |		:
;			+10	|----------------|		:
;				|      IDT       |		:
;				|                |		:
;			+18	|----------------|		:
;				|      IDT       |		:
;				|                |		:
;			+18	|----------------|		:
;				|      IDT       |		:
;				|                |		:
;			+18	|----------------|		:
;				|      IDT       |		:
;				|                |		:
;			+18	|----------------|		:
;				|      IDT       |		:
;				|                |		:
;			+18	|----------------|		:
;				|      IDT       |		:
;				|                |		:
;			+18	|----------------|		:
;					:
;---------------------------------------------------------------------------------

;---------------------------------------------------------------------------------
;	THE GLOBAL DESCRIPTOR TABLE (ACTUAL LOCATION POINTED TO BY ES:SI)	:
;---------------------------------------------------------------------------------

VIRTUAL_ENABLE_GDT_DEF	STRUC
	DQ	?	; FIRST DESCRIPTOR NOT ACCESSIBLE
GDTPTR	DQ	?	; GDT DESCRIPTOR
IDTPTR	DQ	?	; GDT DESCRIPTOR
USER_DS	DQ	?	; USER DATA SEGMENT DESCRIPTOR
USER_ES	DQ	?	; USER EXTRA SEGMENT DESCRIPTOR
USER_SS	DQ	?	; USER STACK SEGMENT DESCRIPTOR
USER_CS	DQ	?	; USER CODE SEGMENT DESCRIPTOR
BIO_CS	DQ	?	; TEMPORARY BIOS DESCRIPTOR
VIRTUAL_ENABLE_GDT_DEF	ENDS

	ASSUME	DS:DATA

X_VIRTUAL	PROC	FAR
SET_VMODE:

;-----	ENABLE ADDRESS LATCH BIT 20

	CLI				; NO INTERRUPTS ALLOWED
	MOV	AH,ENABLE_BIT20		; ENABLE BIT 20 FOR ADDRESS GATE
	CALL	GATE_A20
	CMP	AL,0			; WAS THE COMMAND ACCEPTED?
	JZ	BIT20_ON			; GO IF YES
	MOV	AH,0FFH			; SET THE ERROR FLAG
	ITC				; SET CARRY
	IRET				; EARLY EXIT
BIT20_ON:
	PUSH	ES			; MOVE SEGMENT POINTER
	POP	DS			; TO THE DATA SEGMENT

;---------------------------------------------------------------------------------
; REINITIALIZE THE 8259 INTERRUPT CONTROLLER #1 TO THE USER SPECIFIED OFFSET	:
;---------------------------------------------------------------------------------
	
	MOV	AL,11H		; START INITIALIZATION SEQUENCE-ICWI
	OUT	INTA00,AL	; EDGE,INTERVAL-8,MASTER,ICW4 NEEDED
	JMP	$+2
	MOV	AL,BH		; HARDWARE INT'S START AT INT # (BL)
	OUT	INTA01,AL	; SEND ICW2
	JMP	$+2
	MOV	AL,04H		; SEND ICW3 - MASTER LEVEL 2
	OUT	INTA01,AL
	JMP	$+2
	MOV	AL,01H		; SEND ICW4 - MASTER,8086 MODE
	OUT	INTA01,AL
	JMP	$+2
	MOV	AL,0FFH		; MASK OFF ALL INTERRUPTS
	OUT	INTA01,AL

;---------------------------------------------------------------------------------
; REINITIALIZE THE 8259 INTERRUPT CONTROLLER #2 TO THE USER SPECIFIED OFFSET	:
;---------------------------------------------------------------------------------

	MOV	AL,11H		; INITIALIZE SEQUENCE-ICW1 FOR SLAVE
	OUT	INTB00,AL	; EDGE,INTERVAL-8,MASTER,ICW4	NEEDED
	JMP	$+2
	MOV	AL,BL		; HARDWARE INT'S START AT INT # (BL)
	OUT	INTB01,AL	; SEND ICW2
	MOV	AL,02H
	JMP	$+2
	OUT	INTB01,AL	; SEND ICW3 - SLAVE LEVEL 2
	JMP	$+2
	MOV	AL,01H
	OUT	INTB01,AL	; SEND ICW4 - SLAVE,8086 MODE
	JMP	$+2
	MOV	AL,0FFH
	OUT	INTB01,AL	; MASK OFF ALL INTERRUPTS

;------------------------------------------
; SETUP BIOS CODE SEGMENT DESCRIPTOR	:
;------------------------------------------

	MOV	[SI].BIO_CS.SEG_LIMIT,MAX_SEG_LEN	; SET LENGTH
	MOV	[SI].BIO_CS.BASE_HI_WORD,CSEG@_HI	; HIGH WORD OF (CS)= 0F
	MOV	[SI].BIO_CS.BASE_LO_BYTE,CSEG@_LO	; LOW BYTE OF (CS)= 0
	MOV	[SI].BIO_CS.DATA_ACC_RIGHTS,CPL0_CODE_ACCESS
	MOV	[SI].BIO_CS.DATA_RESERVED,0		; ZERO RESERVED AREA

;------------------------------------------
;	ENABLE PROTECTED MODE 	:
;------------------------------------------

	LGDT	[SI],GDTPTR	; LOAD GLOBAL DESCRIPTOR TABLE REGISTER
	DB	00FH
??0005	LABEL	BYTE
	MOV	DX,WORD PTR [SI].GDTPTR
??0006	LABEL 	BYTE
	ORG	OFFSET CS:??0005
	DB	001H
	ORG	OFFSET CS:??0006
	LIDT	[SI].IDTPTR		; INTERRUPT DESCRIPTOR TABLE REGISTER
	DB	00FH
??0007	LABEL	BYTE
	MOV	BX,WORD PTA [SI].IDTPTR
??0008	LABEL 	BYTE
	ORG	OFFSET CS:??0007
	DB	001H
	ORG	OFFSET CS:??0008

	MOV	AX,VIRTUAL_ENABLE	; MACHINE STATUS WORD NEEDED TO
	LMSW	AX			;  SWITCH TO VIRTUAL MODE
	DB	00FH,001H,0F0H
	DB	0EAH			; PURGE PRE-FETCH QUEUE WITH FAR JUMP
	DW	OFFSET VMODE		;  - TO OFFSET
	DW	BIO_CS			;  - IN SEGMENT -PROTECTED MODE SELECTOR

VMODE:
;------------------------------------------
;	SETUP USER SEGMENT REGISTERS 	:
;------------------------------------------

	MOV	AX,USER_DS	; SETUP USER'S DATA SEGMENT
	MOV	DS,AX		; TO PROTECTED MODE SELECTORS
	MOV	AX,USER_ES	; SETUP USER'S EXTRA SEGMENT
	MOV	ES,AX
	MOV	AX,USER_SS	; SETUP USER'S STACK SEGMENT
	MOV	SS,AX
;------------------------------------------
;	PUT TRANSFER ADDRESS ON STACK 	:
;	AND RETURN TO THE USER	:
;------------------------------------------
	POP	BX	; GET RETURN IP FROM THE STACK
	ADD	SP,4	; NORMALIZE STACK POINTER
	PUSH	USER_CS	; SET STACK FOR A RETURN FAR
	PUSH	AX
	RET		; RETURN TO USER IN VIRTUAL MODE
X_VIRTUAL	ENDP

;--- DEVICE BUSY AND INTERRUPT COMPLETE --------------------------
;				:
;	THIS ROUTINE IS A TEMPORARY HANDLER FOR DEVICE BUSY	:
;	AND INTERRUPT COMPLETE		:
;				:
;	INPUT	- SEE PROLOGUE		:
;-----------------------------------------------------------------

DEVICE_BUSY	PROC	NEAR	
	CLC			; TURN CARRY OFF
	JMP	C1_F		; RETURN WITH CARRY FLAG
DEVICE_BUSY	ENDP

INT_COMPLETE	PROC	NEAR
	IRET			; RETURN
INT_COMPLETE	ENDP

CODE	ENDS
	END

