TITLE BIOS2 ---- 06/10/85 BIOS INTERRUPT ROUTINES
.286C
.LIST
CODE	SEGMENT BYTE PUBLIC

	PUBLIC	PRINT_SCREEN_1
	PUBLIC	RTC_INT
	PUBLIC	TIME_OF_DAY_1
	PUBLIC 	TIMER_INT_1

	FXTRN	CMOS_READ:NEAR	; READ CMOS LOCATION ROUTINE
	EXTRN	CMOS_WRITE:NEAR	; WRITE CMOS LOCATION ROUTINE
	EXTRN	DDS:NEAR	; LOAD (DS) WITH DATA SEGMENT SELECTOR

;--- INT  1A H -- (TIME OF DAY) -----------------------------------------------
;       THIS BIOS ROUTINE ALLOWS THE CLOCKS TO BE SET OR READ			:
;									:
; PARAMETERS:								:
;     (AH) = 00H  READ THE CURRENT SETTING AND RETURN WITH,				:
;                      (CX) = HIGH PORTION OF COUNT					:
;                      (DX) = LOW PORTION OF COUNT					:
;                      (AL) = 0 TIMER HAS NOT PASSED 24 HOURS SINCE LAST READ	:
;                             1 IF ON ANOTHER DAY. (RESET TO ZERO AFTER READ)	:
;									:
;     (AH) = 01H  SET THE CURRENT CLOCK USING,						:
;		     (CX) = HIGH PORTION OF COUNT					:
;		     (DX) = LOW PORTION OF COUNT.					:
;									:
;               NOTE: COUNTS OCCUR AT THE RATE OF 1193180/65536 COUNTS/SECOND	:
;                            (OR ABOUT 18.2 PER SECOND -- SEE EQUATES)		:
;									:
;     (AH) = 02H  READ THE REAL TIME CLOCK AND RETURN WITH,				:
;                      (CH) = HOURS IN BCD (00-23)					:
;                      (CL) = MINUTES IN BCD (00-59)					:
;                      (DH) = SECONDS IN BCD (00-59)					:
;                      (DL) = DAYLIGHT SAVINGS ENABLE (00-01)			:
;									:
;     (AH) = 03H  SET THE REAL TIME CLOCK USING,					:
;                     (CH) = HOURS IN BCD (00-23)					:
;                     (CL) = MINUTES IN BCD (00-59)					:
;                     (DH) = SECONDS IN BCD (00-59)					:
;                     (DL) = 01 IF DAYLIGHT SAVINGS ENABLE OPTION, ELSE 00.	:
;									:
;             NOTE: (DL) = 00 IF DAYLIGHT SAVINGS TIME ENABLE IS NOT ENABLED.	:
;                   (DL) = 01 ENABLES TWO	SPECIAL UPDATES THE LAST SUNDAY IN	:
;	           APRIL   (1:59:59 --> 3:00:00 AM) AND THE LAST SUNDAY IN	:
;                   OCTOBER (1:59:59 --> 1:00:00 AM) THE FIRST TIME.		:
;									:
;     (AH) = 04H  READ THE DATE FROM THE REAL TIME CLOCK AND RETURN WITH,	:
;                      (CH) = CENTURY IN BCD (19 OR 20)				:
;                      (CL) = YEAR IN BCD (00-99)					:
;                      (DH) = MONTH IN BCD (01-12)					:
;                      (DL) = DAY IN BCD (01-31).					:
;									:
;     (AH) = 05H  SET THE DATE INTO THE REAL TIME CLOCK USING,			:
;                     (CH) = CENTURY IN BCD (19 OR 20)				:
;                     (CL) = YEAR IN BCD (00-99)					:
;                     (DH) = MONTH IN BCD (01-12)					:
;                     (DL) = DAY IN BCD (01-31).					:
;									:
;     (AH) = 06H  SET THE ALARM TO INTERRUPT AT SPECIFIED TIME,			:
;                     (CH) = HOURS IN BCD (00-23 (OR FFH))				:
;                     (CL) = MINUTES IN BCD (00-59 (OR FFH))			:
;                     (DH) = SECONDS IN BCD (00-59 (OR FFH))			:
;									:
;     (AH) = 07H  RESET THE ALARM INTERRUPT FUNCTION.					:
;									:
; NOTES: FOR ALL RETURNS CY= 0 FOR SUCCESSFUL OPERATION.				:
;        FOR (AH)= 2, 4, 6 - CARRY FLAG SET IF REAL TIME CLOCK NOT OPERATING.	:
;        FOR (AH)= 6 - CARRY FLAG SET IF ALARM ALREADY ENABLED. 			:
;        FOR THE ALARM FUNCTION (AH = 6) THE USER MUST SUPPLY A ROUTINE AND	:
;         INTERCEPT THE CORRECT ADDRESS IN THE VECTOR TABLE FOR INTERRUPT 4AH.:
;         USE 0FFH FOR ANY "DO NOT CARE" POSITION FOR INTERVAL INTERRUPTS.	:
;        INTERRUPTS ARE DISABLED DURING DATA MODIFICATION. 				:
;        AH & AL ARE RETURNED MODIFIED AND NOT DEFINED EXCEPT WHERE INDICATED.:
;------------------------------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA

TIME_OF_DAY_1	PROC  FAR
	STI				; INTERRUPTS BACK ON
	CMP	AH,(RTC_TBE-RTC_TB)/2	; CHECK IF COMMAND IN VALID RANGE (0-7)
	CMC				; COMPLEMENT CARRY FOR ERROR EXIT
	JC	TIME_9			; EXIT WITH CARRY = 1 IF NOT VALID

	PUSH	DS			; SAVE USERS (DS) SEGMENT
	CALL	DDS			; GET DATA SEGMENT SELECTOR
	PUSH	SI			; SAVE WORK REGISTER
	SHR	AX,8			; CONVERT FUNCTION TO BYTE OFFSET
	ADD	AX,AX			; CONVERT FUNCTION TO WORD OFFSET (CY=0)
	MOV	SI,AX			; PLACE INTO ADDRESSING REGISTER
	CLI				; NO INTERRUPTS DURING TIME FUNCTIONS
	CALL	CS:[SI]+OFFSET RTC_TB	; VECTOR TO FUNCTION REQUESTED WITH CY=0
					;  RETURN WITH CARRY FLAG SET FOR RESULT
	STI				; INTERRUPTS BACK ON
	MOV	AH,0			; CLEAR (AH) TO ZERO
	POP	SI			; RECOVER USERS REGISTER
	POP	DS			; RECOVER USERS SEGMENT SELECTOR
TIME_9:					; RETURN WITH CY= 0 IF NO ERROR
	RET	2
					; 	  ROUTINE VECTOR TABLE (AH)=
RTC_TB	DW	RTC_00			; 0 = READ CURRENT CLOCK COUNT
	DW	RTC_10			; 1 = SET CLOCK COUNT
	DW	RTC_20			; 2 = READ THE REAL TIME CLOCK TIME
	DW	RTC_30			; 3 = SET REAL TIME CLOCK TIME
	DW	RTC_40			; 4 = READ THE REAL TIME CLOCK DATE
	DW	RTC_50			; 5 = SET REAL TIME CLOCK DATE
	DW	RTC_60			; 6 = SET THE REAL TIME CLOCK ALARM
	DW	RTC_70			; 7 = RESET ALARM
RTC_TBE	EQU	$

TIME_OF_DAY_1 	ENDP


RTC_00	PROC	NEAR			; 	  READ TIME COUNT
	MOV	AL,@TIMER_OFL		; GET THE OVERFLOW FLAG
	MOV	@TIMER_OFL,0		; AND THEN RESET THE OVERFLOW FLAG
	MOV	CX,@TIMER_HIGH		; GET COUNT OF TIME HIGH WORD
	MOV	DX,@TIMER_LOW		; GET COUNT OF TIME LOW WORD
	RET				; RETURN WITH NO CARRY

RTC_10:					; 	  SET TIME COUNT
	MOV	@TIMER_LOW,DX		; SET TIME COUNT LOW WORD
	MOV	@TIMER_HIGH.CX		; SET THE TIME COUNT HIGH WORD
	MOV	@TIMER_OFL,0		; RESET OVERFLOW FLAG
	RET				; RETURN WITH NO CARRY

RTC_20:					; 	  GET RTC TIME
	CALL	UPD_IPR			; CHECK FOR UPDATE IN PROCESS
	JC	RTC_29			; EXIT IF ERROR (CY= 1)

	MOV	AL,CMOS_SECONDS		; SET ADDRESS OF SECONDS
	CALL	CMOS_READ		; GET SECONDS
	MOV	DH,AL			; SAVE
	MOV	AL,CMOS_REG_B		; ADDRESS ALARM REGISTER
	CALL	CMOS_READ		; READ CURRENT VALUE OF DSE BIT
	AND	AL,00000001B		; MASK FOR VALID DSE BIT
	MOV	DL,AL			; SET [DL] TO ZERO FOR NO DSE BIT
	MOV	AL,CMOS_MINUTES		; SET ADDRESS OF MINUTES
	CALL	CMOS_READ		; GET MINUTES
	MOV	CL,AL			; SAVE
	MOV	AL,CM0S_HOURS		; SET ADDRESS OF HOURS
	CALL	CMOS_READ		; GET HOURS
	MOV	CH,AL			; SAVE
	CLC				; SET CY= 0
RTC_29:
	RET				; RETURN WITH RESULT IN CARRY FLAG

RTC_30:					; 	  SET RTC TIME
	CALL	UPD_IPR			; CHECK FOR UPDATE IN PROCESS
	JNC	RTC_35			; GO AROUND IF CLOCK OPERATING
	CALL	RTC_STA			; ELSE TRY INITIALIZING CLOCK
RTC_35:
	MOV	AH,DH			; GET TIME BYTE - SECONDS
	MOV	AL,CMOS_SECONDS		; ADDRESS SECONDS
	CALL	CMOS_WRITE		; UPDATE SECONDS
	MOV	AH,CL			; GET TIME BYTE - MINUTES
	MOV	AL,CMOS_MINUTES		; ADDRESS MINUTES
	CALL	CMOS_WRITE		; UPDATE MINUTES
	MOV	AH,CH			; GET TIME BYTE - HOURS
	MOV	AL,CMOS_HOURS		; ADDRESS HOURS
	CALL	CMOS_WRITE		; UPDATE ADDRESS
	MOV	AX,X*CMOS_REG_B		; ADDRESS ALARM REGISTER
	CALL	CMOS_READ		; READ CURRENT TIME
	AND	AL,01100010B		; MASK FOR VALID BIT POSITIONS
	OR	AL,00000010B		; TURN ON 24 HOUR MODE
	AND	DL,00000001B8		; USE ONLY THE DSE BIT
	OR	AL,DL			; GET DAY LIGHT SAVINGS TIME BIT (OSE)
	XCHG	AH,AL			; PLACE IN WORK REGISTER AND GET ADDRESS
	CALL	CMOS_WRITE		; SET NEW ALARM SITS
	CLC				; SET CY= 0
	RET				; RETURN WITH CY= 0

RTC_40:					; 	  GET RTC DATE
	CALL	UPD_IPR			; CHECK FOR UPDATE IN PROCESS
	JC	RTC_49			; EXIT IF ERROR (CY= 1)

	MOV	AL,CMOS_DAY_MONTH	; ADDRESS DAY OF MONTH
	CALL	CMOS_READ		; READ DAY OF MONTH
	MOV	DL,AL			; SAVE
	MOV	AL,CMOS_MONTH		; ADDRESS MONTH
	CALL	CMOS_READ		; READ MONTH
	MOV	DH,AL			; SAVE
	MOV	AL,CMOS_YEAR		; ADDRESS YEAR
	CALL	CMOS_READ		; READ YEAR
	MOV	CL,AL			; SAVE
	MOV	AL,CMOS_CENTURY		; ADDRESS CENTURY LOCATION
	CALL	CMOS_READ		; GET CENTURY BYTE
	MOV	CH,AL			; SAVE
	CLC				; SET CY=0
RTC_49:
	RET				; RETURN WITH RESULTS IN CARRY FLAG

RTC_50:					; 	  SET RTC DATE
	CALL	UPD_IPR			; CHECK FOR UPDATE IN PROCESS
	JNC	RTC_55			; GO AROUND	IF NO ERROR
	CALL	RTC_STA			; ELSE INITIALIZE CLOCK
RTC_55:
	MOV	AX,CMOS_DAY_WEEK	; ADDRESS OF DAY OF WEEK BYTE
	CALL	CMOS_WRITE		; LOAD ZEROS TO DAY OF WEEK
	MOV	AH,DL			; GET	DAY OF MONTH BYTE
	MOV	AL,CMOS_DAY_MONTH	; ADDRESS DAY OF MONTH BYTE
	CALL	CMOS_WRITE		; WRITE OF DAY OF MONTH REGISTER
	MOV	AH,DH			; GET MONTH
	MOV	AL,CMOS_MONTH		; ADDRESS MONTH BYTE
	CALL	CMOS_WRITE		; WRITE MONTH REGISTER
	MOV	AH,CL			; GET YEAR BYTE
	MOV	AL,CMOS_YEAR		; ADDRESS YEAR REGISTER
	CALL	CMOS_WRITE		; WRITE YEAR REGISTER
	MOV	AH,CH			; GET CENTURY BYTE
	MOV	AL,CMOS_CENTURY		; ADDRESS CENTURY BYTE
	CALL	CMOS_WRITE		; WRITE CENTURY LOCATION
	MOV	AX,X*CMOS_REG_B		; ADDRESS ALARM REGISTER
	CALL	CMOS_READ		; READ WIRRENT SETTINGS
	AND	AL,07FH			; CLEAR 'SET BIT'
	XCHG	AH,AL			; MOVE TO WORK REGISTER
	CALL	CMOS_WRITE		; AND START CLOCK UPDATING
	CLC				; SET CY= 0
	RET				; RETURN CY=0

RTC_60:					;	  SET RTC ALARM
	MOV	AL,CMOS_REG_B		; ADDRESS ALARM
	CALL	CMOS_READ		; READ ALARM REGISTER
	TEST	AL,20H			; CHECK FOR ALARM ALREADY ENABLED
	STC				; SET CARRY IN CASE OF ERROR
	JNZ	RTC_69			; ERROR EXIT IF ALARM SET
	CALL	UPD_IPR			; CHECK FOR UPDATE IN PROCESS
	JNC	RTC_65			; SKIP INITIALIZATION IF NO ERROR
	CALL	RTC_STA			; ELSE INITIALIZE CLOCK

RTC_65:	MOV	AH,DH			; GET SECONDS BYTE
	MOV	AL,CMOS_SEC_ALARM	; ADDRESS THE SECONDS ALARM REGISTER
	CALL	CMOS_WRITE		; INSERT SECONDS
	MOV	AH,CL			; GET MINUTES PARAMETER
	MOV	AL,CMOS_MIN_ALARM	; ADDRESS MINUTES ALARM REGISTER
	CALL	CMOS_WRITE		; INSERT MINUTES
	MOV	AH,CH			; GET HOURS PARAMETER
	MOV	AL,CMOS_HR_ALARM	; ADDRESS HOUR ALARM REGISTER
	CALL	CMOS_WRITE		; INSERT HOURS
	IN	AL,INTB01		; READ SECOND INTERRUPT MASK REGISTER
	AND	AL,0FEH			; ENABLE ALARM TIMER BIT (CY= 0)
	OUT	INTB01,AL		; WRITE UPDATED MASK
	MOV	AX,X*CMOS_REG_B		; ADDRESS ALARM REGISTER
	CALL	CMOS_READ		; READ CURRENT ALARM REGISTER
	AND	AL,07FH			; ENSURE SET BIT TURNED OFF
	OR	AL,20H			; TURN ON ALARM ENABLE
	XCHG	AH,AL			; MOVE MASK TO OUTPUT REGISTER
	CALL	CMOS_WRITE		; WRITE NEW ALARM MASK
	CLC				; SET CY= 0
RTC_69:
	MOV	AX,0			; CLEAR AX REGISTER
	RET				; RETURN WITH RESULTS IN CARRY FLAC

RTC_70:					; 	  RESET ALARM
	MOV	AX,X*CMOS_REG_B		; ADDRESS ALARM REGISTER (TO BOTH AH,AL)
	CALL	CMOS_READ		; READ ALARM REGISTER
	AND	AL,57H			; TURN OFF ALARM ENABLE
	XCHG	AH,AL			; SAVE DATA AND RECOVER ADDRESS
	CALL	CMOS_WRITE		; RESTORE NEW VALUE
	CLC				; SET CY= 0
	RET				; RETURN WITH NO CARRY
RTC_00	ENDP

RTC_STA	PROC	NEAR			; 	  INITIALIZE REAL TIME CLOCK
	MOV	AX,26H*H+CMOS_REG_A	; ADDRESS REGISTER A AND LOAD DATA MASK
	CALL	CMOS_WRITE		; INITIALIZE STATUS REGISTER A
	MOV	AX,82H*H+CMOS_REG_B	; SET "SET BIT" FOR CLOCK INITIALIZATION
	CALL	CMOS_WRITE		; AND 24 HOUR MODE TO REGISTER B
	MOV	AL,CMOS_REG_C		; ADDRESS REGISTER C
	CALL	CMOS_READ		; READ REGISTER C TO INITIALIZE
	MOV	AL,CMOS_REG_D		; ADDRESS REGISTER D
	CALL	CMOS_READ		; READ REGISTER D TO INITIALIZE
	RET
RTC_STA	ENDP

UPD_IPR	PROC	NEAR			; 	  WAIT TILL UPDATE NOT IN PROGRESS
	PUSH	CX			; SAVE CALLERS REGISTER
	MOV	CX,800			; SET TIMEOUT LOOP COUNT
UPD_10:
	MOV	AL,CMOS_REG_A		; ADDRESS STATUS REGISTER A
	CLI				; NO TIMER INTERRUPTS DURING UPDATES
	CALL	CMOS_READ		; READ UPDATE IN PROCESS FLAG
	TEST	AL,80H			; IF UIP BIT IS ON ( CANNOT READ TIME )
	JZ	UPD_90			; EXIT WITH CY= 0 IF CAN READ CLOCK NOW
	STI				; ALLOW INTERRUPTS WHILE WAITING
	LOOP	UPD_10			; LOOP TILL READY OR TIMEOUT
	XOR	AX,AX			; CLEAR RESULTS IF ERROR
	STC				; SET CARRY FOR ERROR
UPD_90:
	POP	CX			; RESTORE CALLERS REGISTER
	CLI				; INTERRUPTS OFF DURING SET
	RET				; RETURN WITH CY FLAG SET

UPD_IPR	ENDP


;--- HARDWARE INT  70 H -- ( IRQ LEVEL  8) ------------------------------------
; ALARM INTERRUPT HANDLER (RTC)		:
;       THIS ROUTINE HANDLES THE PERIODIC AND ALARM INTERRUPTS FROM THE CMOS	:
;       TIMER. INPUT FREQUENCY IS 1.024 KHZ OR APPROXIMATELY 1024 INTERRUPTS	:
;       EVERY SECOND FOR THE PERIODIC INTERRUPT. FOR THE ALARM FUNCTION,	:
;       THE INTERRUPT WILL OCCUR AT THE DESIGNATED TIME.	:
;				:
;       INTERRUPTS ARE ENABLED WHEN THE EVENT OR ALARM FUNCTION IS ACTIVATED.	:
;       FOR THE EVENT INTERRUPT, THE HANDLER WILL DECREMENT THE WAIT COUNTER	:
;       AND WHEN IT EXPIRES WILL SET THE DESIGNATED LOCATION TO 80H. FOR	:
;       THE ALARM INTERRUPT. THE USER MUST PROVIDE A ROUTINE TO INTERCEPT	:
;       THE CORRECT ADDRESS FROM THE VECTOR TABLE INVOKED BY INTERRUPT 4AH	:
;       PRIOR TO SETTING THE REAL TIME CLOCK ALARM (INT 1AH, AH= 06H).	:
;------------------------------------------------------------------------------

RTC_INT	PROC	FAR			; 	  ALARM INTERRUPT
	PUSH	DS			; LEAVE INTERRUPTS DISABLED
	PUSH	AX			; SAVE REGISTERS
	PUSH	DI

RTC_I_1:				; 	  CHECK FOR SECOND INTERRUPT
	MOV	AX,(CMOS_REG_B+NMI)*H+CMOS_REG_C+NMI ; ALARM AND STATUS
	OUT	CMOS_PORT,AL		; WRITE ALARM FLAG MASK ADDRESS
	NOP				; I/O DELAY
	IN	AL,CMOS_DATA		; READ AND RESET INTERRUPT REQUEST FLAGS
	TEST	AL,01100000B		; CHECK FOR EITHER INTERRUPT PENDING
	JZ	RTC_I_9			; EXIT IF NOT A VALID RTC INTERRUPT

	XCHG	AH,AL			; SAVE FLAGS AND GET ENABLE ADDRESS
	OUT	CMOS_PORT,AL		; WRITE ALARM ENABLE MASK ADDRESS
	NOP				; I/O DELAY
	IN	AL,CMOS_DATA		; READ CURRENT ALARM ENABLE MASK
	AND	AL,AH			; ALLOW ONLY SOURCES THAT ARE ENABLED
	TEST	AL,01000000B		; CHECK FOR PERIODIC INTERRUPT
	JZ	RTC_I_5			; SKIP IF NOT A PERIODIC INTERRUPT

;-----	DECREMENT WAIT COUNT BY INTERRUPT INTERVAL

	CALL	DDS			; ESTABLISH DATA SEGMENT ADDRESSABILITY
	SUB	@RTC_LOW,0976		; DECREMENT COUNT LOW BY 1/1024
	SBB	@RTC_HIGH,0		; ADJUST HIGH WORD FOR LOW.WORD BORROW
	JNC	RTC_I_5			; SKIP TILL 32 BIT WORD LESS THAN ZERO

;-----	TURN OFF PERIODIC INTERRUPT ENABLE

	PUSH	AX			; 	  SAVE INTERRUPT FLAG MASK
	MOV	AX,X*(CMOS_REG_B+NMI)	; INTERRUPT ENABLE REGISTER
	OUT	CMOS_PORT,AL		; WRITE ADDRESS TO CMOS CLOCK
	NOP				; I/O DELAY
	IN	AL,CMOS_DATA		; READ CURRENT ENABLES
	AND	AL,0BFH			; TURN OFF PIE
	XCHG	AL,AH			; GET CMOS ADDRESS AND SAVE VALUE
	OUT	CMOS_PORT,AL		; ADDRESS REGISTER B
	XCHG	AL,AH			; GET NEW INTERRUPT ENABLE MASK
	OUT	CMOS_DATA,AL		; SET MASK IN INTERRUPT ENABLE REGISTER
	MOV	@RTC_WAIT_FLAG,0	; SET FUNCTION ACTIVE FLAG OFF
	LDS	DL,DWORD PTR @USER_FLAG	; SET UP (DS:DI) TO POINT TO USER FLAG
	MOV	BYTE PTR [DI],80H	; TURN ON USERS FLAG
	POP	AX			; GET INTERRUPT SOURCE BACK
RTC_I_5:
	TEST	AL,00100000B		; TEST FOR ALARM INTERRUPT
	JZ	RTC_I_7			; SKIP USER INTERRUPT CALL IF NOT ALARM

	MOV	AL,CMOS_REG_D		; POINT TO DEFAULT READ ONLY REGISTER
	OUT	CMOS_PORT,AL		; ENABLE NMI AND CMOS ADDRESS TO DEFAULT
	STI				; INTERRUPTS BACK ON NOW
	PUSH	DX
	INT	4AH			; TRANSFER TO USER ROUTINE
	POP	DX
	CLI				; BLOCK INTERRUPT FOR RETRY
RTC_I_7:				; RESTART ROUTINE TO HANDLE DELAYED
	JMP	RTC_I_1			;  ENTRY AND SECOND EVENT BEFORE DONE


RTC_I_9:				; 	  EXIT - NO PENDING INTERRUPTS
	MOV	AL,CMOS_REG_D		; POINT TO DEFAULT READ ONLY REGISTER
	OUT	CMOS_PORT,AL		; ENABLE NMI AND CMOS ADDRESS TO DEFAULT
	MOV	AL,EOI			; END OF INTERRUPT MASK TO 8259 - 2
	OUT	INTB00,AL		; TO 8259 - 2
	OUT	INTA00,AL		; TO 8259 - 1
	POP	DI			; RESTORE REGISTERS
	POP	AX
	POP	DS
	IRET				; END OF INTERRUPT

RTC_INT	ENDP


;--- INT 05 H -----------------------------------------------------------------
; PRINT_SCREEN			:
;       THIS LOGIC WILL BE INVOKED BY INTERRUPT 05H TO PRINT THE SCREEN.	:
;       THE CURSOR POSITION AT THE TIME THIS ROUTINE IS INVOKED WILL BE	:
;       SAVED AND RESTORED UPON COMPLETION.  THE ROUTINE IS INTENDED TO	:
;       RUN WITH INTERRUPTS ENABLED.   IF A SUBSEQUENT PRINT SCREEN KEY	:
;       IS DEPRESSED WHILE THIS ROUTINE IS PRINTING IT WILL BE IGNORED.	:
;       THE BASE PRINTERS STATUS IS CHECKED FOR NOT BUSY AND NOT OUT OF	:
;       PAPER.  AN INITIAL STATUS ERROR WILL ABEND THE PRINT REQUEST.	:
;       ADDRESS  0050:0000  CONTAINS THE STATUS OF THE PRINT SCREEN:	:
;       50:0    = 0     PRINT SCREEN HAS NOT BEEN CALLED OR UPON RETURN	:
;                        FROM A CALL THIS INDICATES A SUCCESSFUL OPERATION.	:
;               = 1     PRINT SCREEN IS IN PROGRESS - IGNORE THIS REQUEST.	:
;               = 255   ERROR ENCOUNTERED DURING PRINTING.	:
;------------------------------------------------------------------------------

PRINT_SCREEN_I	PROC  FAR
					; DELAY INTERRUPT ENABLE TILL FLAG SET
	PUSH	DS
	PUSH	AX			; SAVE WORK REGISTERS
	PUSH	BX
	PUSH	CX
	PUSH	DX			; USE 0040:0100 FOR STATUS AREA STORAGE
	CALL	DDS			; GET STATUS_BYTE DATA SEGMENT
	CMP	@STATUS_BYTE,1		; SEE IF PRINT ALREADY IN PROGRESS
	JE	PR190			; EXIT IF PRINT ALREADY IN PROGRESS
	MOV	@STATUS_BYTE.1		; INDICATE PRINT NOW IN PROGRESS
	STI				; MUST RUN WITH INTERRUPTS ENABLED
	MOV	AH.0FH			; WILL REQUEST THE CURRENT SCREEN MODE
	INT	10H			;          (AL)= MODE
					;          (AH)= NUMBER COLUMNS/LINE
					;          (BH)= VISUAL PAGE
	MOV	CL,AH			; WILL MAKE USE OF (CX) REGISTER TO
	MOV	CH,@ROWS		;  CONTROL ROWS ON SCREEN & COLUMNS
	INC	CH			; ADJUST ROWS ON DISPLAY COUNT
					;          (CL)= NUMBER COLUMNS/LINE
					;          (CH)= NUMBER OF ROWS ON DISPLAY
	;--------------------------------------------------------------------
	;	AT THIS POINT WE KNOW THE COLUMNS/LINE COUNT IS IN (CL)     :
	;	AND THE NUMBER OF ROWS ON THE DISPLAY IS IN (CH).           :
	;	THE PACE IF APPLICABLE IS IN ~8141. THE STACK HAS           :
	;	(DS),(AX),(BX),(CX),(DX) PUSHED.                            :
	;--------------------------------------------------------------------
	XOR	DX,DX			; FIRST PRINTER
	MOV	AH,02H			; SET PRINTER STATUS REQUEST COMMAND
	INT	17H			; REQUEST CURRENT PRINTER STATUS
	XOR	AH,080H			; CHECK FOR PRINTER BUSY (NOT CONNECTED)
	TEST	AH,0A0H			;  OR OUT OF PAPER
	JNZ	PRI80			; ERROR EXIT IF PRINTER STATUS ERROR

	CALL	CRLF			; CARRIAGE RETURN LINE FEED TO PRINTER

	PUSH	CX			; SAVE SCREEN BOUNDS
	MOV	AH,03H			; NOW READ THE CURRENT CURSOR POSITION
	INT	10H			; AND RESTORE AT END OF ROUTINE
	POP	CX			; RECALL SCREEN BOUNDS
	PUSH	DX			; PRESERVE THE ORIGINAL POSITION
	XOR	DX,DX			; INITIAL CURSOR (0,0) AND FIRST PRINTER
	;----------------------------------------------------------------
	;	THIS LOOP IS TO READ EACH CURSOR POSITION FROM THE	 :
	;	SCREEN AND PRINT IT. (BH)= VISUAL PAGE (CH)= ROWS		 :
	;----------------------------------------------------------------

PRI10:
	MOV	AH,02H			; INDICATE CURSOR SET REQUEST
	INT	10H			; NEW CURSOR POSITION ESTABLISHED
	MOV	AH,08H			; INDICATE READ CHARACTER FROM DISPLAY
	INT	10H			; CHARACTER NOW IN (AL)
	OR	AL,AL			; SEE IF VALID CHAR
	JNZ	PRI20			; JUMP IF VALID CHAR
	MOV	AL,' '			; ELSE MAKE IT A BLANK
PRI20:
	PUSH	DX			; SAVE CURSOR POSITION
	XOR	DX,DX			; INDICATE FIRST PRINTER (DX= 0)
	XOR	AH,AH			; INDICATE PRINT CHARACTER IN (AL)
	INT	17H			; PRINT THE CHARACTER
	POP	DX			; RECALL CURSOR POSITION
	TEST	AH,29H			; TEST FOR PRINTER ERROR
	JNZ	PRI70			; EXIT IF ERROR DETECTED
	INC	DL			; ADVANCE TO NEXT COLUMN
	CMP	CL,DL			; SEE IF AT END OF LINE
	JNZ	PRI10			; IF NOT LOOP FOR NEXT COLUMN
	XOR	DL,DL			; BACK TO COLUMN 0
	MOV	AH,DL			; (AH)= O
	PUSH	DX			; SAVE NEW CURSOR POSITION
	CALL	CRLF			; LINE FEED CARRIAGE RETURN
	POP	DX			; RECALL CURSOR POSITION
	INC	DH			; ADVANCE TO NEXT LINE
	CMP	CH,DH			; FINISHED?
	JNZ	PRI10			; IF NOT LOOP FOR NEXT LINE

	POP	DX			; GET CURSOR POSITION
	MOV	AH,02H			; INDICATE REQUEST CURSOR SET
	INT	10H			; CURSOR POSITION RESTORED
	CLI				; BLOCK INTERRUPTS TILL STACK CLEARED
	MOV	@STATUS_BYTE,0		; MOVE OK RESULTS FLAG TO STATUS_BYTE
	JMP	SHORT PRI90		; EXIT PRINTER ROUTINE

PRI70:					; ERROR EXIT
	POP	DX			; GET CURSOR POSITION
	MOV	AH,02H			; INDICATE REQUEST CURSOR SET
	INT	10H			; CURSOR POSITION RESTORED
PRI80:
	CLI				; BLOCK INTERRUPTS TILL STACK CLEARED
	MOV	@STATUS_BYTE,0FFH	; SET ERROR FLAG
PRI90:
	POP	DX			; EXIT ROUTINE
	POP	CX			; RESTORE ALL THE REGISTERS USED
	POP	BX
	POP	AX
	POP	DS
	IRET				; RETURN WITH INITIAL INTERRUPT MASK
PRINT_SCREEN_I	ENDP


;-----	CARRIAGE RETURN. LINE FEED SUBROUTINE
CRLF	PROC	NEAR
					; 	  SEND CR,LF TO FIRST PRINTER
	XOR	DX,DX			; ASSUME FIRST PRINTER (DX= 0)
	MOV	AX,CR			; GET THE PRINT CHARACTER COMMAND AND
	INT	17H			;   THE CARRIAGE RETURN CHARACTER
	MOV	AX,LF			; NOW GET THE LINE FEED AND
	INT	17H			;   SEND IT TO THE BIOS PRINTER ROUTINE
	RET
CRLF	ENDP



;-- HARDWARE INT  08 H - ( IRQ LEVEL 0 ) --------------------------------------
;       THIS ROUTINE HANDLES THE TIMER INTERRUPT FROM CHANNEL 0 OF 	:
;       THE 8254 TIMER. INPUT FREQUENCY IS 1.19314 MHZ AND THE DIVISOR 	:
;       IS 65536, RESULTING IN APPROXIMATELY 18.2 INTERRUPTS EVERY SECOND.	:
;				:
;       THE INTERRUPT HANDLER MAINTAINS A COUNT (40:6C) OF INTERRUPTS SINCE	:
;       POWER ON TIME, WHICH MAY BE USED TO ESTABLISH TIME OF DAY.	:
;       THE INTERRUPT HANDLER ALSO DECREMENTS THE MOTOR CONTROL COUNT (40:40)	:
;       OF THE DISKETTE, AND WHEN IT EXPIRES, WILL TURN OFF THE	:
;       DISKETTE MOTOR(s), AND RESET THE MOTOR RUNNING FLAGS.	:
;       THE INTERRUPT HANDLER WILL ALSO INVOKE A USER ROUTINE THROUGH	:
;       INTERRUPT 1CH AT EVERY TIME TICK. THE USER MUST CODE A	:
;       ROUTINE AND PLACE THE CORRECT ADDRESS IN THE VECTOR TABLE.	:
; -----------------------------------------------------------------------------

TIMER_INT_1	PROC  FAR
	STI				; INTERRUPTS BACK ON
	PUSH	DS
	PUSH	AX
	PUSH	DX			; SAVE MACHINE STATE
	CALL	DDS			; ESTABLISH ADDRESSABILITY
	INC	@TIMER_LOW		; INCREMENT TIME
	JNZ	T4			; GO TO TEST DAY
	INC	@TIMER_HICH		; INCREMENT HIGH WORD OF TIME
T4:					; TEST DAY
	CMP	@TIMER_HIGH,018H	; TEST FOR COUNT EQUALING 24 HOURS
	JNZ	T5			; GO TO DISKETTE_CTL
	CMP	@TIMER_LOW,0B0H
	JNZ	T5			; GO TO DISKETTE_CTL

;-----	TIMER HAS GONE 24 HOURS
	SUB	AX,AX
	MOV	@TIMER_HIGH,AX
	MOV	@TIMER_LOW,AX
	MOV	@TIMER_OFL,1

;-----	TEST FOR DISKETTE TIME OUT
T5:
	DEC	@MOTOR_COUNT		; DECREMENT DISKETTE MOTOR CONTROL
	JNZ	T6			; RETURN IF COUNT NOT OUT
	AND	@MOTOR_STATUS,0F0H	; TURN OFF MOTOR RUNNING BITS
	MOV	AL,0CH
	MOV	DX,03F2H		; FDC CTL PORT
	OUT	DX,AL			; TURN OFF THE MOTOR
T6:					; TIMER TICK INTERRUPT
	INT	1CH			; TRANSFER CONTROL TO A USER ROUTINE

	POP	DX			; RESTORE (DX)
	MOV	AL,EOI			; GET END OF INTERRUPT MASK
	CLI				; DISABLE INTERRUPTS TILL STACK CLEARED
	OUT	INTA00,AL		; END OF INTERRUPT TO 8259 - 1
	POP	AX
	POP	DS			; RESET MACHINE STATE
	IRET				; RETURN FROM INTERRUPT

TIMER_INT_1	ENDP

CODE	ENDS
	END




















BIOS25-181
