PAGE 118,121
TITLE BIOS1 ---- 06/10/85 INTERRUPT 15H BIOS ROUTINES
.286C
.XLIST
INCLUDE IAPX286.INC
INCLUDE DSEG.INC
INCLUDE POSTEQU.INC
INCLUDE SYSDATA.INC
.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		       :
;									       :
PAGE
;	  (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,080H 		; BASE ON 0
	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,0FEH
	OUT	INTB01,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:
	CLI				; DISABLE INTERRUPTS
	TEST	@RTC_WAIT_FLAG,02H	; CHECK FOR "WAIT" IN PROGRESS
	JZ	EVENT_WAIT_4		; SKIP TO CANCEL CURRENT "EVENT WAIT"

	STI				; ENABLE INTERRUPTS
	POP	DS			; CLEAR STACK
	STC				; AND SET CARRY FLAG FOR ERROR REQUEST
	JMP	C1_F			; RETURN

EVENT_WAIT_4:
	PUSH	AX			; SAVE
	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
	CLI				; NO INTERRUPTS ALLOWED
	TEST	@RTC_WAIT_FLAG,01	; TEST FOR FUNCTION ACTIVE
	JZ	WAIT_1
	STI				; ENABLE INTERRUPTS PRIOR TO RETURN
	POP	DS
	STC				; SET ERROR
	JMP	C1_F			; RETURN
WAIT_1:
	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,03	; SET ON "WAIT" FUNCTION ACTIVE SWITCHES
	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
PAGE
;--- 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 GDT 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 GDT 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 GDT 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
	LIDT	[BP]			; REGISTER FROM THIS AREA

	LGDT	[SI].CGDT_LOC		; LOAD GLOBAL DESCRIPTOR TABLE REGISTER


;-----	SWITCH TO VIRTUAL MODE

	MOV	AX,VIRTUAL_ENABLE	; MACHINE STATUS WORD NEEDED TO
	LMSW	AX			;  SWITCH TO VIRTUAL MODE
	 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_SHUTDOWN		; 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,DISABLE_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_SHUT_DOWN	; 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,CMOS_DATA		; OPEN CMOS STANDBY LATCH

	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	BIOS_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	BIOS_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	BIOS_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	BIOS_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
PAGE
;-------------------------------------------------------------------------------
; 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

;-----	8042 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
PAGE
;--- 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 286 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		       :
;	    SELECTOR 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.	       :
;-------------------------------------------------------------------------------
PAGE
;
;	THE FOLLOWING DIAGRAM DEPICTS THE ORGANIZATION
;	OF GDT.
;-------------------------------------------------------------------------------
;			     G D T					       :
;									       :
;			       .-------------.				       :
;			       v	     |				       :
;     (ES:SI)-->>  +00 .----------------.    |				       :
;		       |     DUMMY	|    |				       :
;		       |		|    |				       :
;		   +08 |----------------|    |				       :
;		       |      GDT	| ---'                                 :
;		       |		|				       :
;		   +10 |----------------|				       :
;		       |      IDT	|				       :
;		       |		|				       :
;		   +18 |----------------|				       :
;		       |      DS	|				       :
;		       |		|				       :
;		   +20 |----------------|				       :
;		       |      ES	|				       :
;		       |		|				       :
;		   +28 |----------------|				       :
;		       |      SS	|				       :
;		       |		|				       :
;		   +30 |----------------|				       :
;		       |      CS	|				       :
;		       |		|				       :
;		   +38 |----------------|				       :
;		       |   TEMP BIOS	|				       :
;		       |      CS	|				       :
;		       |----------------|				       :
;									       :
;-------------------------------------------------------------------------------

;-------------------------------------------------------------------------------
;     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	?			; IDT 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
	STC				; 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-ICW1
	OUT	INTA00,AL		; EDGE,INTERVAL-8,MASTER,ICW4 NEEDED
	JMP	$+2
	MOV	AL,BH			; HARDWARE INT'S START AT INT # (BH)
	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
PAGE
;----------------------------------------
; SETUP BIOS CODE SEGMENT DESCRIPTOR	:
;----------------------------------------

	MOV	[SI].BIO_CS.SEG_LIMIT,MAX_SEG_LEN	; SET LENGTH
	MOV	[SI].BIO_CS.BASE_HI_BYTE,CSEG@_HI	; SET HIGH BYTE OF CS=0F
	MOV	[SI].BIO_CS.BASE_LO_WORD,CSEG@_LO	; SET LOW WORD 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
	LIDT	[SI].IDTPTR		; INTERRUPT DESCRIPTOR TABLE REGISTER

	MOV	AX,VIRTUAL_ENABLE	; MACHINE STATUS WORD NEEDED TO
	LMSW	AX			;  SWITCH TO VIRTUAL MODE
	 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	BX
	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
