TITLE (ROM BIOS FOR IBM PERSONAL COMPUTER)
.MODEL COMPACT
OPTION M510
;-------------------
;EQUATES
;-------------------

PORT_A          EQU     60H             ;8255 PORT A ADDR
PORT_B          EQU     61H             ;8255 PORT B ADDR
PORT_C          EQU     62H             ;8255 PORT C ADDR
CMD_PORT        EQU     63H
INTA00          EQU     20H             ;8259 PORT
INTA01          EQU     21H             ;8259 PORT
EOI             EQU     20H
TIMER           EQU     40H
TIM_CTL         EQU     43H             ;8253 TIMER CONTROL PORT ADDR
TIMER0          EQU     40H             ;8253 TIMER/CNTER 0 PORT ADDR
TMINT           EQU     01              ;TIMER 0 INTR RECVD MASK
DMA08           EQU     08              ;DMA STATUS REG PORT ADDR
DMA             EQU     00              ;DMA CHANNEL 0 ADDRESS REG PORT ADDR
MAX_PERIOD      EQU     540H
MIN_PERIOD      EQU     410H
KBD_IN          EQU     60H             ;KEYBOARD DATA IN ADDR PORT
KBDINT          EQU     02              ;KEYBOARD INTR MASK
KB_DATA         EQU     60H             ;KEYBOARD SCAN CODE PORT
KB_CTL          EQU     61H             ;CONTROL BITS FOR KEYBOARD SENSE DATA

;----------------------------------
;8088 INTERRUPT LOCATIONS
;----------------------------------

ABS0    	SEGMENT
	ORG	0
STG_LOC0        LABEL   BYTE
        ORG     2*4
NMI_PTR         LABEL   WORD
        ORG     5*4
INT5_PTR        LABEL   WORD
        ORG     8*4
INT_ADDR        LABEL   WORD
INT_PTR         LABEL   DWORD
        ORG     10H*4
VIDEO_INT       LABEL   WORD
        ORG     1DH*4
PARM_PTR        LABEL   DWORD           ; POINTER TO VIDEO PARMS
        ORG     01EH*4                  ; INT 1EH
DISK_POINTER    LABEL   DWORD  
        ORG     01FH*4                  ; LOCATION OF POINTER
EXT_PTR         LABEL   DWORD           ; POINTER TO EXTENSION
        ORG     7C00H
BOOT_LOCN       LABEL   FAR
ABS0    ENDS
         
;---------------------
; STACK -- USED DURING INITIALIZATION ONLY
;---------------------         

TSTACK		SEGMENT	'STACK'			;AT 30H
	ORG	30H
        DW      128 DUP(?)
TOS     LABEL   WORD
TSTACK	ENDS
         

;-----------------------------------------
; ROM BIOS DATA AREAS
;-----------------------------------------
DATA		SEGMENT 'DATA'		; AT 40H
	ORG	40H
RS232_BASE	DW	4 DUP(?)	; ADDRESSES OF RS232 ADAPTERS
PRINTER_BASE    DW      4 DUP(?)        ; ADDRESSES OF PRINTERS
EQUIP_FLAG      DW      ?               ; INSTALLED HARDWARE
MFG_TST         DB      ?               ; INITIALIZATION FLAG
MEMORY_SIZE     DW      ?               ; MEMORY SIZE IN K BYTES
IO_RAM_SIZE     DW      ?               ; MEMORY IN I/O CHANNEL

;-----------------------------------------
; KEYBOARD DATA AREAS
;-----------------------------------------
KB_FLAG         DB      ?
         
;----- SHIFT FLAG EQUATES WITHIN KB_FLAG
         
INS_STATE       EQU     80H             ; INSERT STATE IS ACTIVE
CAPS_STATE      EQU     40H             ; CAPS LOCK STATE HAS BEEN TOGGLED
NUM_STATE       EQU     20H             ; NUM LOCK STATE HAS BEEN TOGGLED
SCROLL_STATE    EQU     10H             ; SCROLL LOCK STATE HAS BEEN TOGGLED
ALT_SHIFT       EQU     08H             ; ALTERNATE SHIFT KEY DEPRESSED
CTL_SHIFT       EQU     04H             ; CONTROL SHIFT KEY DEPRESSED
LEFT_SHIFT      EQU     02H             ; LEFT SHIFT KEY DEPRESSED
RIGHT_SHIFT     EQU     01H             ; RIGHT SHIFT KEY DEPRESSED

KB_FLAG_1       DB      ?               ; SECOND BYTE OF KEYBOARD STATUS
         
INS_SHIFT       EQU     80H             ; INSERT KEY IS DEPRESSED
CAPS_SHIFT      EQU     40H             ; CAPS LOCK KEY IS DEPRESSED
NUM_SHIFT       EQU     20H             ; NUM LOCK KEY IS DEPRESSED
SCROLL_SHIFT    EQU     10H             ; SCROLL LOCK KEY IS DEPRESSED
HOLD_STATE      EQU     08H             ; SUSPEND KEY HAS BEEN TOGGLED

ALT_INPUT       DB      ?               ; STORAGE FOR ALTERNATE KEYPAD ENTRY
BUFFER_HEAD     DW      ?               ; POINTER TO HEAD OF KEYBOARD BUFFER
BUFFER_TAIL     DW      ?               ; POINTER TO TAIL OF KEYBOARD BUFFER
KB_BUFFER       DW      16 DUP(?)       ; ROOM FOR 15 ENTRIES
KB_BUFFER_END   LABEL   WORD
       
;------ HEAD = TAIL INDICATES THAT THE BUFFER IS EMPTY
         
NUM_KEY         EQU     69              ; SCAN CODE FOR NUMBER LOCK
SCROLL_KEY      EQU     70              ; SCROLL LOCK KEY
ALT_KEY         EQU     56              ; ALTERNATE SHIFT KEY SCAN CODE
CTL_KEY         EQU     29              ; SCAN CODE FOR CONTROL KEY
CAPS_KEY        EQU     58              ; SCAN CODE FOR SHIFT LOCK
LEFT_KEY        EQU     42              ; SCAN CODE FOR LEFT SHIFT
RIGHT_KEY       EQU     54              ; SCAN CODE FOR RIGHT SHIFT
INS_KEY         EQU     82              ; SCAN CODE FOR INSERT KEY
DEL_KEY         EQU     83              ; SCAN CCDE FOR DELETE KEY

;-----------------------------------------
; DISKETTE DATA AREAS
;-----------------------------------------
         
SEEK_STATUS     DB      ?               ; DRIVE RECALIBRATION STATUS
;                                       BIT 3-0 = DRIVE 3-0 NEEDS RECAL BEFORE
;                                       NEXT SEEK IF BIT IS = 0
INT_FLAG        EQU     080H            ; INTERRUPT OCCURRENCE FLAG
MOTOR_STATUS    DB      ?               ; MOTOR STATUS
;                       	BIT 3-0 = DRIVE 3-0 IS CURRENTLY RUNNING
;                       	BIT 7 : CURRENT OPERATICN IS A WRITE, REQUIRES DELAY
MOTOR_COUNT     DB      ?               ;TIME OUT COUNTER FOR DRIVE TURN OFF
MOTOR_WAIT	EQU	37		;TWO SECONDS OF COUNTS FOR MOTOR TURN OFF
         
;         
DISKETTE_STATUS DB      ?               ; SINGLE BYTE OF RETURN CODE INFO FOR STATUS
TIME_OUT        EQU     80H             ; ATTACHMENT FAILED TO RESPOND
BAD_SEEK        EQU     40H             ; SEEK OPERATION FAILED 
BAD_NEC         EQU     20H             ; NEC CONTROLLER HAS FAILED
BAD_CRC 	EQU	10H		; BAD CRC ON DISKETTE READ
DMA_BOUNDARY    EQU     09H             ; ATTEMPT TO DMA ACROSS 64K BOUNDARY
BAD_DMA         EQU     08H             ; DMA OVERRUN ON OPERATION
RECORD_NOT_FND  EQU     04H             ; REQUESTED SECTOR NOT FOUND
WRITE_PROTECT   EQU     03H             ; WRITE ATTEMPTED ON WRITE PROT DISK
BAD_ADDR_MARK   EQU     02H             ; ADDRESS MARK NOT FOUND
BAD_CMD         EQU     01H             ; BAD COMMAND PASSED TO DISKETTE I/O
         
NEC_STATUS      DB      7 DUP(?)        ; STATUS BYTES FROM NEC

;-----------------------------------------
; VIDEO DISPLAY DATA AREA
;-----------------------------------------

CRT_MODE        DB      ?               ; CURRENT CRT MODE
CRT_COLS        DW      ?               ; NUMBER OF COLUMNS ON SCREEN
CRT_LEN         DW      ?               ; LENGTH OF REGEN IN BYTES
CRT_START       DW      ?               ; STARTING ADDRESS IN REGEN BUFFER
CURSOR_POSN     DW      8 DUP(?)        ; CURSOR FOR EACH OF UP TO 8 PAGES
CURSOR_MODE     DW      ?               ; CURRENT CURSOR MODE SETTING
ACTIVE_PAGE     DB      ?               ; CURRENT PAGE BEING DISPLAYED
ADDR_6845       DW      ?               ; BASE ADDRESS FOR ACTIVE DISPLAY CARD
CRT_MODE_SET    DB      ?               ; CURRENT SETTING OF THE 3X8 REGISTER
CRT_PALLETTE    DB      ?               ; CURRENT PALLETTE SETTING COLOR CARD
         
;-----------------------------------------
; CASSETTE DATA AREA
;-----------------------------------------
         
EDGE_CNT        DW      ?               ;TIME COUNT AT DATA EDGE
CRC_REG         DW      ?               ;CRC REGISTER
LAST_VAL        DB      ?               ;LAST INPUT VALUE
         
;-----------------------------------------
; TIMER DATA AREA
;-----------------------------------------
         
TIMER_LOW       DW      ?               ; LOW WORD OF TIMER COUNT
TIMER_HIGH	DW	?		; HIGH WORD OF TIMER COUNT
TIMER_OFL       DB      ?               ; TIMER HAS ROLLED OVER SINCE LAST READ
;COUNTS_SEC     EQU     18
;COUNTS_MIN     EQU     1092
;COUNTS_HOUR    EQU     65543
;COUNTS_DAY     EQU     1573040 = 1800B0H
         
;-----------------------------------------
; SYSTEM DATA AREA
;-----------------------------------------
        
BIOS_BREAK      DB      ?               ; BIT 7 = 1 IF BREAK KEY HAS BEEN DEPRESSED
RESET_FLAG      DW      ?               ; WORD = 1234H IF KEYBDARD RESET UNDERWAY
DATA    ENDS

;-----------------------------------------
; EXTRA DATA AREA
;-----------------------------------------

XXDATA		SEGMENT		       ; AT 50H
	ORG	50H
STATUS_BYTE     DB      ?
XXDATA  ENDS

;-----------------------------------------
; VIDEO DISPLAY BUFFER
;-----------------------------------------

VIDEO_RAM       SEGMENT			; AT 0B800H
	ORG	0B800H
REGEN   LABEL   BYTE
REGENW  LABEL   WORD
        DB      16384 DUP(?)
VIDEO_RAM       ENDS

;-----------------------------------------
; ROM RESIDENT CODE
;-----------------------------------------
CODE		SEGMENT 'CODE'		; AT 0F000H
	ORG	0E000H
;	DB	57344 DUP(?)		; FILL LOWEST 56K
        DB      '5700051 COPR. IBM 1981'; COPYRIGHT NOTICE

;=========================================
; INITIAL RELIABILITY TESTS -- PHASE 1
;-----------------------------------------
	ASSUME	CS:CODE,SS:CODE,ES:ABS0,DS:DATA
;-----------------------------------------
;        DATA DEFINITIONS
;-----------------------------------------
C1      DW      C11                     ; RETURN ADDRESS
C2      DW      C24                     ; RETURN ADDRESS FOR DUMMY STACK
         
;-----------------------------------------
;       THIS SUBROUTINE PERFORMS A READ/WRITE STORAGE TEST ON A 16K BLOCK
;       OF STORGAGE.
;ENTRY REQUIREMENTS:
;        ES = ADDRESS OF STORAGE SEGMENT BEING TESTED
;        DS = ADDRESS OF STORAGE SEGMENT BEING TESTED
;        WHEN ENTERING AT STGTST_CNT, CX MUST BE LOADED WITH THE BYTE COUNT.
;EXIT PARAMETERS:
;        ZERO FLAG = 0 IF STORAGE ERROR (DATA COMPARE OR PARITY CHECK. AL=0
;               DENOTES A PARITY CHECK. ELSE AL=XOR'ED BIT PATTERN OF THE
;               EXPECTED DATA PATTERN VS THE ACTUAL DATA READ.
;        AX,BX,CX,DX,DI, AND SI ARE ALL DESTROYED.
;-----------------------------------------
STGTST          PROC    NEAR
	MOV	CX,04000H		;SETUP CNT TO TEST A 16K BLK
STGTST_CNT:
        CLD                             ;SET DIR FLAG TO INCREMENT
        MOV     BX,CX                   ;SAVE BYTE CNT (4K FOR VIDEO OR 16K)
        MOV     AX,0FFFFH               ;GET DATA PATTERN TO WRITE
        MOV     DX,0AA55H               ;SETUP OTHER DATA PATTERNS TO USE
        SUB     DI,DI                   ;DI = OFFSET 0 RELATIVE TO ES REG
        REP     STOSB                   ;WRITE STORAGE LOCATIONS
C3:      				; STG01
	DEC	DI   			;POINT TO LAST BYTE JUST WRITTEN
	STD				;SET DIR FLAG TO GO BACKWARDS
C4:	MOV	SI,DI
	MOV	CX,BX   		;SETUP BYTE CNT
C5:	LODSB				;READ CHAR FROM STORAGE
	XOR	AL,AH   		;DATA READ AS EXPECTED?
	JNE	C7   			;NO - GO TO ERROR ROUTINE
	IN	AL,PORT_C		;DID A PARITY ERROR OCCUR?
	AND	AL,0C0H
	MOV	AL,0   			;AL=0 DATA COMPARE OK
	JNZ	C7
	CMP	AH,0   			;READING ZERO PATTERN?
	JE 	C6   			;CONTINUE READING TILL END
	MOV	AL,DL   		;GET NEXT DATA PATTERN TO WRITE
	STOSB   			;WRITE IN BYTE LOC WE JUST READ
C6:      				; WRITE_NO_MORE
	LOOP	C5   			;CONTINUE TILL 16K/4K BLOCK TESTED
	CMP	AH,0   			;ZERO PATTERN WRITTEN TO STG
	JE 	C7   			;YES - RETURN TO CALLER
	MOV	AH,AL   		;SETUP TO NEW VALUE TO COMPARE
	XCHG	DH,DL   		;MOVE ZERO DATA PATTERN TO DL
	CLD				;SET DIR FLAG TO GO FORWARD
	INC	DI   			;SET POINTER TO BEG LOCATION
	JZ 	C4   			;READ/WRITE FORWARD IN STG
	DEC	DI
	MOV	DX,1   			;SETUP 01 AND 00 PATTERNS
	JMP	SHORT C3   		;READ/WRITE BACKWARD IN STG
C7:
	RET
STGTST	ENDP
;-------------------------------------------- 
;TEST.01
;	8088 PROCESSOR TEST
;DESCRIPTION
;	VERIFY 8088 FLAGS, REGISTERS AND CONDITIONAL JUMPS
;--------------------------------------------
RESET   LABEL	NEAR
START:  CLI   				;DISABLE INTERRUPTS
	MOV	AH,0D5H 		;SET SF, CF, ZF, AND AF FLAGS ON
	SAHF
	JNC     ERR01   		;GO TO ERR ROUTINE IF CF NOT SET
	JNZ     ERR01   		;GO TO ERR ROUTINE IF ZF NOT SET
	JNP     ERR01   		;GO TO ERR ROUTINE IF PF NOT SET
	JNS     ERR01   		;GO TO ERR ROUTINE IF SF NOT SET
	LAHF                		;LOAD FLAG IMAGE TO AH
	MOV     CL,5   			;LOAD CNT REG WITH SHIFT CNT
	SHR     AH,CL  			;SHIFT AF INTO CARRY BIT POS
	JNC     ERR01   		;SO TO ERR ROUTINE IF AF NOT SET
	MOV     AL,40H          	;SET THE OF FLAG ON
	SHL     AL,1			;SETUP FOR TESTING
	JNO     ERR01   		;GO TO ERR ROUTINE IF OF NOT SET
	XOR     AH,AH                   ;SET AH = 0
	SAHF                  		;CLEAR SF, CF, ZF, AND PF
	JC      ERR01        		;GO TO ERR ROUTINE IF CF ON
	JZ      ERR01   		;GO TO ERR ROUTINE IF ZF ON
	JS      ERR01       		;GO TO ERR ROUTINE IF SF ON
	JP      ERR01        		;GO TO ERR ROUTINE IF PF ON
	LAHF                     	;LOAD FLAG IMAGE TO AH
	MOV     CL,5  			;LOAD CNT REG WITH SHIFT CNT
	SHR	AH,CL			;SHIFT AH INTO CARRY BIT POS
	JC	ERR01          		;GO TO ERR ROUTINE IF ON
	SHL	AH,1          		;CHECK THAT OF IS CLEAR
	JO	ERR01           	;GO TO ERR ROUTINE IF ON
	                          
;	READ/WRITE THE 8088 GENERAL AND SEGMENTATION REGI5TERS
;	WITH ALL ONE'S ANO ZEROES'S.
	         
	MOV	AX,0FFFFH		;SETUP ONE'S PATTERN IN AX
	STC
C8:	MOV     DS,AX			;WRITE PATTERN TO ALL REGS
	MOV     BX,DS
	MOV     ES,BX
	MOV     CX,ES
	MOV     SS,CX
	MOV     DX,SS
	MOV     SP,DX
	MOV     BP,SP
	MOV	SI,BP
	MOV	DI,SI
	JNC	C9			; TST1A
	XOR     AX,DI			;PATTERN MAKE IT THRU ALL REGS
	JNZ     ERR01			;NO - GO TO ERR ROUTINE
	CLC
	JNC	C8
C9:      				; TST1A
	OR	AX,DI			;ZERO PATTERN MAKE IT THRU?
	JZ	C10			;YES - GO TO NEXT TEST
ERR01:	HLT				;HALT SYSTEM
;--------------------------------------------
; TEST.02
;	ROS CHECKSUM TEST 1
;DESCRIPTION
;	A CHECKSUM IS DONE FOR THE 8K ROS MODULE CONTAINING POD AND BIOS.
;--------------------------------------------
C10:
	MOV    	AL,0 			;DISABLE NMI INTERRUPTS
	OUT  	0A0H,AL
	OUT   	83H,AL 			;INITIALIZE DMA PAGE REG
	MOV   	AL,99H 			;SET 8255 A,C-INPUT,B-OUTPUT
	OUT     CMD_PORT,AL 		;WRITE 8255 CMD/MODE REG
	MOV 	AL,0FCH 		;DISABLE PARITY CHECKERS AND
	OUT	PORT_B,AL 		; GATE SNS SWS,CASS MOTOR OFF
	SUB    	AL,AL
	MOV 	DX, 3D8H
	OUT    	DX,AL 			;DISABLE COLOR VIDEO
	INC	AL
	MOV	DX,3B8H
	OUT   	DX,AL 			;DISABLE B/W VIDEO,EN HIGH RES
	MOV  	AX,CODE 		;SETUP SS SEG REG
	MOV    	SS,AX
	MOV	DX,0E000H 		;SETUP STARTING ROS ADDR
	MOV     SP,OFFSET C1 		;SETUP RETURN ADDRESS
	JMP   	ROS_CHECKSUM
C11:	JNE 	ERR01			;HALT SYSTEM IF ERROR
;--------------------------------------------
;TEST.03
;	8237 DMA INITIALIZATION CHANNEL REGISTER TEST
;DESCRIPTION
;	DISABLE THE 8237 DMA CONTROLLER. VERIFY THAT TIMER 1 FUNCTIONS OK.
;	WRITE/READ THE CURRENT ADDRESS AND WORD COUNT REGISTERS FOR ALL
;	CHANNELS INITIALIZE AND START DMA FOR MEMORY REFRESH.
;------------------------------------------
;    DISABLE DMA CONTROLLER
	MOV 	AL,04  			;DISABLE DMA CONTROLLER
	OUT 	DMA08,AL

;      VERIFY THAT TIMER 1 FUNCTIONS OK
	   
	MOV   	AL,54H  		;SEL TIMER 1,LSB,MODE 2
	OUT	TIMER+3,AL
	SUB    	CX,CX			;
	MOV    	BL,CL
	MOV    	AL,CL			;SET INITIAL TIMER CNT TO 0
	OUT	TIMER+1,AL
C12:     				; TIMER1_BITS_ON
	MOV   	AL,40H  		;LATCH TIMER 1 COUNT
	OUT	TIMER+3,AL
	IN	AL,TIMER+1  		;READ TIMER 1 COUNT
	OR      BL,AL  			;ALL BITS ON IN TIMER
	CMP  	BL,0FFH  		;YES - SEE IF ALL BITS GO OFF
	JE 	C13  			; TIMER1_BITS_OFF
	LOOP    C12  			; TIMER1_BITS_ON
	JMP     SHORT ERR01  		;TIMER 1 FAILURE, HALT SYS
C13:  					; TIMER1_BITS_OFF
	MOV	AL,BL  			;SET TIMER 1 CNT
	SUB 	CX,CX
	OUT	TIMER+1,AL
C14:  					; TIMER_LOOP
	MOV   	AL,40H  		;LATCH TIMER 1 COUNT
	OUT	TIMER+3,AL
	IN	AL,TIMER+1  		;READ TIMER 1 COUNT
	AND	BL,AL
	JZ 	C15  			; WRAP_DMA_REG
	LOOP    C14  			; TIMER_LOOP
	JMP 	SHORT ERR01

;	INITIALIZE TIMER 1 TO REFRESH MEMORY

C15:					; WRAP_DMA_REG
	MOV	AL,54H  		;SEL TIM 1, LSB, MODE 2
	OUT	TIMER+3,AL  		;WRITE TIMER MODE REG
	MOV	AL,18  			;SETUP DIVISOR FOR REFRESH
	OUT   	TIMER+1,AL  		;WRITE TIMER 1 CNT REG
	OUT   	DMA+0DH,AL  		;SEND MASTER CLEAR TO DMA
	
;	WRAP DMA CHANNELS ADDRESS AND COUNT REGISTERS

	MOV	AL,0FFH 		;WRITE PATTERN FFH TO ALL REGS
C16: 	MOV 	BL,AL 			;SAVE PATTERN FOR COMPARE
	MOV	BH,AL
	MOV 	CX,8 			;SETUP LOOP CNT
	MOV     DX,DMA 			;SETUP I/O PORT ADDR OF REG
C17: 	OUT 	DX,AL 			;WRITE PATTERN TO REG, LSB
	OUT	DX,AL 			;MSB OF 16 BIT REG
	MOV  	AX,0101H 		;AX TO ANOTHER PAT BEFORE RD
	IN	AL,DX 			;READ 16-BIT DMA CH REG, LSB
	MOV	AH,AL 			;SAVE LSB OF 16-BIT REG
	IN 	AL,DX 			;READ MSB OF DMA CH REG
	CMP	BX,AX 			;PATTERN READ AS WRITTEN?
	JE   	C18 			;YES - CHECK NEXT REG
	JMP	ERR01 			;NO - HALT THE SYSTEM
C18:					; NXT_DMA_CH
	INC	DX 			;SET I/O PORT TO NEXT CH REG
	LOOP	C17 			;WRITE PATTERN TO NEXT REG
	NOT	AL 			;SET PATTERN TO ZERO
	JZ	C16 			;WRITE TO CHANNEL REGS
	
;    INITIALIZE AND START DMA FOR MEMORY REFRESH.
	
	MOV 	AL,0FFH			;SET CNT OF 64K FOR RAM REFRESH
	OUT 	DMA+1,AL
	OUT 	DMA+1,AL
	MOV 	AL,058H			;SET DMA MODE,CH 0,READ,AUOTINT
	OUT 	DMA+0BH,AL 		;WRITE DMA MODE REG
	MOV 	AL,0    		;ENABLE DMA CONTROLLER
	OUT 	DMA+8,AL		;SETUP DMA COMMAND REG
	OUT 	DMA+10,AL		;ENABLE CHANNEL 0 FOR REFRESH
	MOV 	AL,41H  		;SET MODE FOR CHANNEL 1
	OUT 	DMA+0BH,AL
	MOV 	AL,42H 			;SET MODE FOR CHANNEL 2
	OUT	DMA+0BH,AL		;
	MOV 	AL,43H 			;SET MODE FOR CHANNEL 3
	OUT 	DMA+0BH,AL
;--------------------------------------------
;TEST.04
;	BASE 16K READ/WRITE STORAGE TEST
;DESCRIPTION
;	WRITE/READ/VERIFY DATA PATTERNS FF,55,AA,01, AND 00 TO 1ST 16K OF
;	STORAGE. VERIFY STORAGE ADDRESSABILITY.
;	INITIALIZE THE 8259 INTERRUPT CONTROLLER CHIP FOR CHECKING
;	MANUFACTURING TEST 2 MODE.
;--------------------------------------------
;	DETERMINE MEMORY SIZE AND FILL MEMORY WITH DATA
	
	MOV 	AX,DATA   		;POINT DS TO DATA SEG
	MOV 	DS,AX    		;
	MOV 	BX,RESET_FLAG		;SAVE RESET_FLAG IN BX
	SUB 	AX,AX  			;SET ES AND DS TO 0
	MOV 	ES,AX			;SETUP ES SEGMENT REG
	MOV 	DS,AX
	SUB 	DI,DI
	IN 	AL,PORT_A		;DETERMINE BASE RAM SIZE
	AND 	AL,0CH    		;ISOLATE RAM SIZE SWS
	ADD 	AL,4	 		; CALCULATE MEMORY SIZE
	MOV 	CL,12
	SHL 	AX,CL
	MOV 	CX,AX
	MOV 	AH,AL
	CLD     			;SET DIR FLAG TO INCR
C19:	STOSB				;FILL BASE RAM WITH DATA
	LOOP 	C19  			; LOOP TIL ALL ZERO

;	DETERMINE IO CHANNEL RAM SIZE

	IN 	AL,PORT_C
	AND	AL,0FH
	JZ 	C21
	MOV     DX,1000H		; SEGMENT FOR I/O RAM
	MOV	AH,AL
	MOV	AL,0
C20:					; FILL_IO
	MOV	ES,DX
	MOV     CX,8000H		; FILL 32K BYTE5
	SUB 	DI,DI
	REP 	STOSB
	
	ADD	DX,800H			; NEXT SEGMENT VALUE
	DEC    	AH
	JNZ   	C20			; FILL_IO
	
;--------------------------------------------
;	INITIALIZE THE 8259 INTERRUPT CONTROLLER CHIP
;--------------------------------------------
C21:
	MOV     AL,13H 			;ICW1 - EDGE, SNGL, ICW4
	OUT     INTA00,AL
	MOV	AL,8 			;SETUP ICW2 - INT TYPE 8 (8-F)
	OUT	INTA01,AL
	MOV	AL,9 			;SETUP ICW4 - BUFFRD,8086 MODE
	OUT     INTA01,AL
	SUB     AX,AX 			;POINT DS AND ES TO BEGIN
	MOV     ES,AX 			; OF R/W STORAGE
	MOV     SI,DATA 		;POINT DS TO DATA SEG
	MOV     DS,SI 			;
	MOV	RESET_FLAG,BX		;RESTORE RESET_FLAG
	CMP	RESET_FLAG,1234H	;RESET_FLAG SET?
	JE 	C25			;YES - SKIP STG TEST
	MOV     DS,AX 			;POINT DS TO 1ST 16K OF STG
;--------------------------------------------
;	CHECK FOR MANUFACTURING TEST 2 TO LOAD TEST PROGRAMS FROM KEYBOARD.
;--------------------------------------------
	MOV	SP,3FF0H   		; ESTABLISH TEMPORARY STACK
	MOV  	SS,AX
	MOV  	DI,AX
	MOV 	BX,24H
	MOV	WORD PTR [BX],OFFSET D11   ;SET UP KB INTERRUPT
	INC	BX
	INC	BX
	MOV     [BX],CS
	CALL	KBD_RESET 		; READ IN KB RESET CODE TO BL
	CMP  	BL,065H 		; IS THIS MANUFACTURING TEST 2?
	JNZ 	C23 			; JUMP IF NOT MAN. TEST
	MOV    	DL,255 			; READ IN TEST PROGRAM
C22:	CALL 	SP_TEST
	MOV	AL,BL
	STOSB
	DEC  	DL
	JNZ 	C22 			; JUMP IF NOT DONE RET
	INT 	3EH 			;SET INTERUPT TYPE 62 ADDRESS FBH
C23:					;CONTINUE IN NORMAL MOOE
	PUSH 	CS			; PUT SS BACK
	POP	SS
	CLI				;
	MOV 	SP,OFFSET C2 		;SETUP RETURN ADDRESS
	JMP     STGTST 			;GO TO RD/WRT STG SUBROUTINE
C24:	JE 	C25  			;GO TO NEXT TEST IF OK
	JMP	ERR01
	 
;    SETUP STACK SEG AND SP

C25:
	MOV	AX,TSTACK		; GET STACK VALUE
	MOV 	SS,AX  			; SET THE STACK UP
	MOV 	SP,OFFSET TOS		; STACK IS READY TO GO

;	SETUP THE NMI INTERRUPT VECTOR POINTER
	MOV 	ES:NMI_PTR,OFFSET NMI_INT
	MOV 	ES:NMI_PTR+2,CODE
	JMP	NEAR PTR TST6		;GO TO NEXT TEST
	
ROS_CHECKSUM	PROC	NEAR		; NEXT_ROS_MODULE
	MOV	CX,8192  		;NUMBER OF BYTES TO ADD
	XOR	AL,AL
C26:
	ADD	AL,CS:[BX]
	INC  	BX			;POINT TO NEXT BYTE
	LOOP	C26  			;ADD ALL BYTES IN ROS MODULE
	OR	AL,AL  			;SUM = 0?
	RET
ROS_CHECKSUM	ENDP

;--------------------------------------
; INITIAL RELIABILITY TEST -- PHASE 2
;---------------------------------------
	ASSUME	CS:CODE,ES:ABS0

D1	DB	'PARITY CHECK 2'

D1L	EQU $-D1
D2	DB	'PARITY CHECK 1'

D2L	EQU $-D2
;---------------------------------------------
; TEST.06
; 	8259 INTERRUPT CONTROLLER TEST
;DESCRIPTION
;	READ/WRITE THE INTERRUPT MASK REGISTER (IMR) WITH ALL ONES AND ZEROES
; 	ENABLE SYSTEM INTERRUPTS.  MASK DEVICE INTERRUPTS OFF. CHECK FOR
;	HOT INTERRUPTS (UNEXPECTED).
;--------------------------------------------
TST6:
	SUB	AX,AX			;SET UP ES REG
	MOV	ES,AX
	 
;------ SET UP THE INTERRUPT 5 POINTER TO A DUMMY
	
	MOV ES:INT5_PTR,OFFSET PRINT_SCREEN 	;PRINT SCREEN
	MOV ES:INT5_PTR+2,CODE  		;
	 
;	TEST THE IMR REGISTER
	 
	CLI				;DISABLE INTERRUPTS
	MOV	AL,0 
	OUT	INTA01,AL
	IN	AL,INTA01		;READ IMR
	OR 	AL,AL 			;IMR = 0?
	JNZ	D6			;GO TO ERR ROUTINE IF NOT 0
	MOV	AL,0FFH 		;DISABLE DEVICE INTERRUPTS
	OUT	INTA01,AL 		;WRITE TO IMR
	IN	AL,INTA01 		;READ IMR
	ADD	AL,1			;ALL IMR BITS ON?
	JNZ	D6			;NO - GO TO ERR ROUTINE
	 
;	CHECK FOR HOT INTERRUPTS
	
	CLD				;SET DIR FLAG TO GO FORWARD
	MOV	CX,8			;SETUP TEMP INT RTNE IN PRT TBL
	MOV  	DI,OFFSET INT_PTR 	;GET ADDRESS OF INT PROC TABLE
D3:					; VECTBL0
	MOV	AX,OFFSET D11		;MOVE ADDR OF INTR PROC TO TBL
	STOSW
	MOV	AX,CODE 		;GET ADDR OF INTR PROC SEG
	STOSW
	ADD	BX,4			;SET BX TO POINT TO NEXT VAL
	LOOP	D3			; VECTBL0
	
;	INTERRUPTS ARE MASKED OFF. CHECK THAT NO INTERRUPTS OCCUR

	XOR	AH,AH			;CLEAR AH REG
	STI				;ENABLE EXTERNAL INTERRUPTS
	SUB 	CX,CX			;WAIT 1 SEC FOR ANY INTRS THAT
D4:	LOOP	D4			;MIGHT OCCUR
D5:	LOOP	D5
	OR	AH,AH 			;DID ANY INTERRUPTS OCCUR?
	JZ  	D7			;NO - GO TO NEXT TEST
D6:  	MOV	DX,101H			;BEEP SPEAKER IF ERROR
	CALL 	ERR_BEEP		;GO TO BEEP SUBROUTINE
	CLI
	HLT				;HALT THE SYSTEM
;-------------------------------------------- 
;TEST.7
;	8253 TIMER CHECKOUT
;DESCRIPTION
;	VERIFY THAT THE SYSTEM TIMER (0) DOESN'T COUNT TOO FAST NOR TOO
;	SLOW.
;--------------------------------------------
D7:
	MOV	AH,0  			;RESET TIMER INTR RECVD FLAG
	XOR 	CH,CH  			;CLEAR THE CH REG
	MOV  	AL,0FEH  		;MASK ALL INTRS EXCEPT LVL 0
	OUT	INTA01,AL  		;WRITE THE 8259 IMR
	MOV	AL,00010000B  		;SEL TIM 0, LSB, MODE 0, BINARY
	OUT 	TIM_CTL,AL  		;WRITE TIMER CONTROL MODE REG
	MOV  	CL,16H  		;SET PGM LOOP CNT
	MOV 	AL,CL  			;SET TIMER 0 CNT REG
	OUT	TIMER0,AL  		;WRITE TIMER 0 CNT REG
D8:	TEST	AH,0FFH			;DID TIMER 0 INTERRUPT OCCUR?
	JNZ	D9  			; YES - CHECK TIMER OP FOR SLOW TIME
	LOOP	D8  			;WAIT FOR INTR FOR SPECIFIED TIME
	JMP 	D6  			;TIMER 0 INTR DIDN'T OCCUR - ERR
D9: 	MOV  	CL,18			;SET PGM LOOP CNT
	MOV  	AL,0FFH  		;WRITE TIMER 0 CNT REG
	OUT	TIMER0,AL
	MOV	AH,0  			;RESET INTR RECEIVED FLAG
	MOV  	AL,0FEH  		;REENABLE TIMER 0 INTERRUPTS
	OUT	INTA01,AL
D10:	TEST  	AH,0FFH			;DID TIMER 0 INTERRUPT OCCUR?
	JNZ	D6  			;YES - TIMER CNTING TOO FAST, ERR
	LOOP	D10  			;WAIT FOR INTR FOR SPECIFIED TIME
	JMP	NEAR PTR TST8		;GO TO NEXT TEST ROUTINE
;-------------------------------------------- 
;	TEMPORARY INTERRUPT SERVICE ROUTINE
;--------------------------------------------
D11	PROC	NEAR
	MOV 	AH,1 
	PUSH 	AX 			;SAVE REG AX CONTENTS
	MOV 	AL,0FFH			;MASK ALL INTERRUPTS OFF
	OUT 	INTA01,AL
	MOV 	AL,EOI
	OUT 	INTA00,AL
	POP 	AX 			;RESTORE REG AX CONTENTS
	IRET
D11	ENDP
	 
NMI_INT	PROC	NEAR
	PUSH 	AX  			;SAVE ORIG CONTENTS OF AX
	IN 	AL,PORT_C
	TEST	AL,40H  		;IO CH PARITY CHECK?
	JZ  	D12  			;YES - FLAG IS SET TO 0
	MOV	SI,OFFSET D1		;ADDR OF ERROR MSG
	MOV 	CX,D1L  		;MSG LENGTH
	JMP 	SHORT D13  		;DISPLAY ERROR MSG
D12:
	TEST	AL,80H  		;PLANAR RAM P-CHECK?
	JZ  	D14  			;NO - AUX INT
	MOV	SI,OFFSET D2  		;ADDR OF ERROR MSG
	MOV 	CX, D2L  		;MSG LENGTH
D13:
	MOV	AX,0			;INIT AND SET MODE FOR VIDEO
	INT 	10H  			;CALL VIDEO_IO PROCEDURE
	CALL	P_MSG 			;PRINT ERROR MSG
	CLI
	HLT				;HALT SYSTEM
D14:
	POP	AX			;RESTORE ORIG CONTENTS OF AX
	IRET
NMI_INT ENDP

;-------------------------------------
; INITIAL RELIABILITY TEST -- PHASE 3
;--------------------------------------
	ASSUME	CS:CODE,DS:DATA

E1	DB	' 201'
E1L	EQU	$-E1
	
;	ESTABLISH BIOS SUBROUTINE CALL INTERRUPT VECTORS
	
TST8:
	CLD				;SET DIR FLAG TO GO FORWARD
	MOV	DI,OFFSET VIDEO_INT  	;SETUP ADDR TO INTR AREA
	PUSH 	CS
	POP	DS			;SETUP ADDR DF VECTOR TABLE
	MOV	SI,0FF13H		; OFFSET VECTOR_TABLE+32
	MOV	CX,20H
	REP	MOVSW			;MOVE VECTOR TABLE TO RAM
	

;	SETUP TIMER 0 TO MODE 3
	
	MOV	AL,0FFH			;DISABLE ALL DEVICE INTERRUPTS
	OUT	INTA01,AL
	MOV	AL,36H			;SEL TIM 0,LSB,MSB,MODE 3
	OUT	TIMER+3,AL		;WRITE TIMER MODE PEG
	MOV	AL,0
	OUT	TIMER,AL		;WRITE LSB TO TINER 0 REG
	OUT	TIMER,AL		;WRITE MSB TO TIMER 0 REG
	
;	SETUP TIMER 0 TO BLINK LED IF MANUFACTURING TEST MODE
	
	ASSUME	DS:DATA
	MOV 	AX,DATA 		;POINT DS TD DATA SEG
	MOV 	DS,AX
	CALL	KBD_RESET		;SEND SOFTWARE RESET TO KEYBRD
	CMP 	BL,0AAH 		;SCAN CODE 'AA' RETURNED?
	JE  	E3 			;YES - CONTINUE (NON MFG MODE)
	MOV 	AL,3CH			;EN KBD, SET KBD CLK LINE LOW
	OUT 	PORT_B,AL 		;WRITE 8255 PORT B
	NOP
	NOP
	IN  	AL,PORT_A  		;WAS A BIT CLOCKED IN?
	AND 	AL,0FFH
	JNZ 	E2 			;YES - CONTINUE (NON MFG MODE)
	INC 	MFG_TST  		;ELSE SET SW FOR MFG TEST MODE
	MOV 	ES:INT_ADDR,OFFSET BLINK_INT	;SETUP TIMER INTP TO BLINK LED
	MOV 	ES:INT_ADDR+2,CODE
	MOV 	AL,0FEH 		;ENABLE TIMER INTERRUPT
	OUT 	INTA01,AL
E2: 					; JUMPER_NOT_IN:
	MOV 	AL,0CCH			;RESET THE KEYBOARD
	OUT 	PORT_B,AL
;--------------------------------------------
;TEST.05
;	ROS CHECKSUM II
;DESCRIPTION
;	A CHECKSUM IS DONE FOR THE 4 ROS MODULES CONTAINING BASIC CODE
;--------------------------------------------
E3:
	 MOV 	DL,4			;NO. OF ROS MODULES TO CHECK
	 MOV 	BX,6000H 		;SETUP STARTING ROS ADDR
E4:					; CHECK_ROS:
	 CALL 	ROS_CHECKSUM
	 JNE 	E5			;BEEP SPEAKER IF ERROR
	 DEC 	DL			;ANY MORE TO DO?
	 JNZ 	E4 			;YES - CONTINUE
	 JMP 	NEAR PTR E6		;NO - GO TO NEXT TEST
E5:					; ROS_ERRoR:
	 MOV	DX,101H
	 CALL 	ERR_BEEP		;BEEP SPEAKER

;--------------------------------------------
;TEST.08
;	INITIALIZE AND START CRT CONTROLLER (6845)
; 	TEST VIDEO READ/WRITE STORAGE.
;DESCRIPTION
;	RESET THE VICEO ENABLE SIGNAL.
;  	SELECT ALPHANUMBERIC MODE, 40 * 25, B & W
;  	READ/WRITE DATA PATTERNS TO STG. CHECK STG ADDRESSABILITY.
;--------------------------------------------
E6:
	IN 	AL,PORT_A  		;READ SENSE SWITCHES
	MOV 	AH,0
	MOV 	EQUIP_FLAG,AX		;STORE SENSE SW INFO
	AND 	AL,30H			;ISOLATE VIDEO SW
	JNZ 	E7 			;VIDEO SWS SET TO 0?
	JMP 	E19			;SKIP VIDEO TESTS FOR BURN-IN
E7:					; TEST_VIDEO:
	XCHG	AH,AL
	CMP 	AH,30H			;B/W CARD ATTACHED?
	JE  	E8 			;YES - SET MODE FOR B/W CARD
	INC 	AL 			;SET COLOR MODE FOR COLOR CD
	CMP 	AH,20H			;80x25 MODE SELECTED?
	JNE	E8			;NO - SET MODE FOR 40X25
	MOV  	AL,3			;SET MODE FOR 80X25
E8:					; SET_MODE:
	PUSH	AX			;SAVE VIDEO MODE ON STACK
	SUB	AH,AH  			;INITIALIZE TO ALPHANUMERIC
	INT	10H 			;CALL VIDEO_IO
	POP	AX			;RESTORE VIDEO SENSE SWS IN AH
	PUSH	AX 			; RESAVE VALUE
	MOV	BX,0B000H  		;BEG VIDEO RAM ADDR B/W CD
	MOV	DX,3B8H			;MODE REG FOR B/W
	MOV	CX,4096			;RAM BYTE CNT FOR B/W CD
	MOV	AL,1			; SET MODE FOR B/W CARD
	CMP	AH,30H			;B/W VIDED CARD ATTACHED?
	JE	E9			;YES - GO TEST VIDEO STG
	MOV	BX,0B800H		;BEG VIDEO RAM ADDR COLOR CD
	MOV	DX,3D8H			;MODE REG FOR COLOR CD
	MOV	CX,4000H		;RAM BYTE CNT FOR COLOR CD
	DEC	AL			; SET MODE TO 0 FOR COLOR CD
E9:					; TEST_VIDEO_STG:
	OUT	DX,AL			;DISABLE VIDEO FOR COLOR CD
	MOV	ES,BX			;POINT ES TD VIDEO RAM STG
	MOV	AX,DATA			;POINT DS TO DATA SEGMENT
	MOV	DS,AX
	CMP	RESET_FLAG,1234H 	;POD INITIATED BY KBD RESET?
	JE	E10			;YES - SKIP VIDEO RAM TEST
	MOV	DS,BX			;POINT DS TO VIDEO RAM STG
	CALL 	STGTST_CNT		;GO TEST VIDEO R/W STG
	JE	E10			;STG OK - CONTINUE TESTING
	MOV	DX,102H			;SETUP # OF BEEPS
	CALL 	ERR_BEEP		;GO BEEP SPEAKER

;--------------------------------------------
;TEST.09
;	SETUP VIDEO DATA ON SCREEN FOR VIDEO LINE TEST.
;DESCRIPTION
; 	ENABLE VIDEO SIGNAL AND SET MODE.
;	DISPLAY A HORIZONTAL BAR ON SCREEN.
;--------------------------------------------
E10:
	POP  	AX 			;GET VIDEO SENSE SWS (AH)
	PUSH  	AX 			;SAVE IT
	MOV	AH,0 			;ENABLE VIDEO AND SET MODE
	INT 	10H 			; VIDEO
	MOV 	AX,7020H 		;WRT BLANKS IN REVERSE VIDEO
	SUB	DI,SI 			;SETUP STARTING LOC
	MOV	CX,40 			;NO. OF BLANKS TO DISPLAY
	CLD				;SET DIR FLAG TO INCREMENT
	REP	STOSW			;WRITE VIDEO STORAGE

;--------------------------------------------
;TEST.10
;	CRT INTERFACE LINES TEST
;DESCRIPTION
;	SENSE ON/OFF TRANSITION OF THE VIDEO ENABLE AND HORIZONTAL
;	SYNC LINES.
;--------------------------------------------
	POP 	AX			;GET VIDEO SENSE SW INFO
	PUSH 	AX 			;SAVE IT
	CMP 	AH,30H 			;B/W CARD ATTACHED?
	MOV	DX,03BAH		;SETUP ADDR OF BW STATUS PORT
	JE 	E11			;YES - GO TEST LINES
	MOV	DX,03DAH		;COLOR CARD IS ATTACHED
E11: 					; LINE_TST:
	MOV	AH,8
E12:					; OFLOOP_CNT:
	SUB	CX,CX
E13: 	IN 	AL,DX 			;READ CRT STATUS PORT
	AND	AL,AH			;CHECK VIDEO/HORZ LINE
	JNZ	E14			;ITS ON - CHECK IF IT GOES OFF
	LOOP	E13 			;LOOP TILL ON OR TIMEOUT
	JMP	SHORT E17		;GO PRINT ERROR MSG
E14: 	SUB	CX,CX
E15: 	IN 	AL,DX 			;READ CRT STATUS PORT
	AND	AL,AH			;CHECK VIDEO/HORZ LINE
	JZ 	E16			;ITS ON - CHECK NEXT LINE
	LOOP	E15 			;LOOP IF OFF TILL IT GOES ON
	JMP	SHORT E17
E16:					; NXT_LINE:
	MOV	CL,3 			;GET NEXT BIT TO CHECK
	SHR	AH,CL			;
	JNZ	E12			;GO CHECK HORIZONTAL LINE
	JMP	SHORT E18		;DISPLAY CURSOR ON SCREEN
E17:  					; CRT_ERR:
	MOV	DX,102H
	CALL	ERR_BEEP 		;GO BEEP SPEAKER
E18:					; DISPLAY_CURSOR:
	POP	AX 			;GET VIDEO SENSE SWS (AH)
	MOV	AH,0			;SET MODE AND DISPLAY CURSOR
	INT	10H 			;CALL VIDEO I/O PROCEDURE

;--------------------------------------------
;TEST.11
;	ADDITIONAL READ/WRITE STORAGE TEST
;DESCRIPTION
;	WRITE/READ DATA PATTERNS TO ANY READ/WRITE STORAGE AFTER THE BASIC
;	16K. STORAGE ADDRESSABILITY IS CHECKED.
;--------------------------------------------
	ASSUME	DS:DATA
E19:
	MOV	AX,DATA
	MOV	DS,AX
	 
;DETERMINE RAM SIZE ON PLANAR BOARD
	
	MOV	AH,BYTE PTR EQUIP_FLAG	;GET SENSE SWS INFO
	AND	AH,0CH			;ISOLATE RAM SIZE SWS
	MOV	AL,4
	MUL	AH
	ADD	AL,16			;ADD BASIC 16K
	MOV	DX,AX			;SAVE PLANAR RAM SIZE IN DX
	MOV	BX,AX			;  AND IN BX
	 
;  DETERMINE IO CHANNEL RAM SIZE
	
	IN 	AL,PORT_C  		;READ IO CH RAM SIZE SWS
	AND 	AL,0FH 			;ISOLATE FROM OTHER BITS
	MOV	AH,32
	MUL 	AH
	MOV 	IO_RAM_SIZE,AX 		;SAVE IO CHANNEL RAM SIZE
	CMP 	BX,40H 			;PLANAR RAM SIZE = 64K?
	JE 	E20 			;YES - ADD IO CHN RAM SIZE
	SUB 	AX,AX			;NO - DON'T AOD ANY IO RAM
E20:					; ADD_ID_SIZE:
	ADD 	AX,BX			;SUM TOTAL RAM SIZE
	MOV 	MEMORY_SIZE,AX		;SETUP MEMORY SIZE PARM
	CMP 	RESET_FLAG,1234H	;POD INITIATED BY KBD RESET?
	JE	E22			;YES - SKIP MEMORY TEST
	
;	TEST ANY OTHER READ/WRITE STORAGE AVAILABLE
	 
	MOV	BX,400H
	MOV	CX,16
E21:
	CMP	DX,CX			;ANY MORE STG TO BE TESTED?
	JBE	E23			;NO - GO TO NEXT TEST
	MOV	DS,BX			;SETUP STG ADDR IN DS AND ES
	MOV	ES,BX
	ADD	CX,16			;INCREMENT STG BYTE COUNTER
	ADD	BX,400H			;SET POINTER TD NEXT 16K BLK
	PUSH	CX			;SAVE REGS
	PUSH	BX
	PUSH	DX
	CALL	STGTST			;GO TEST A 16K BLK OF STG
	POP	DX
	POP	BX			;RESTORE REGS
	POP	CX
	JE	E21			;CHECK IF MORE STG TO TEST
	
;	PRINT FAILING ADDRESS AND XOR'ED PATTERN IF DATA COMPARE ERROR
	
	MOV	DX,DS			;CONVERT FAILING HIGH-ORDER
	MOV	CH,AL			;SAVE FAILING BIT PATTERN
	MOV	AL,DH			;GET FAILING ADDR (HIGH BYTE)
	MOV	CL,4
	SHR	AL,CL			;RIGHT-JUSTIFY HIGH BYTE
	CALL	XLAT_PRINT_CODE 	;CONVERT AND PRINT CODE
	MOV	AL,DH
	AND	AL,0FH
	CALL	XLAT_PRINT_CODE 	;CONVERT AND PRINT CODE
	MOV	AL,CH			;GET FAILING BIT PATTERN
	MOV	CL,4			; AND ISOLATE LEFTMOST NIBBLE
	SHR	AL,CL
	CALL	XLAT_PRINT_CODE 	;CONVERT AND PRINT CODE
	MOV	AL,CH			;GET FAILING BIT PATTERN AND
	AND	AL,0FH			;  ISOLATE RIGHTMOST NIBBLE
	CALL	XLAT_PRINT_CODE  	;CONVERT AND PRINT CODE
	MOV	SI,OFFSET E1 		;SETUP ADDRESS OF ERROR MSG
	MOV	CX,E1L			;GET MSG BYTE COUNT
	CALL	P_MSG			;PRINT ERROR MSG
E22:					; GO_TST12:
	JMP	NEAR PTR TST12		;GO TO NEXT TEST
E23:					; STG_TEST_DONE:
	MOV	AX,DATA 		;POINT DS TO DATA SEGMENT
	MOV	DS,AX			; CHG MADE 3/27/81
	MOV	DX,IO_RAM_SIZE  	;GET IO CHANNEL RAM SIZE
	OR 	DX,DX			;SET FLAG RESULT
	JZ 	E22			;NO IO RAM, GO TO NEXT TEST
	MOV	CX,0
	CMP	BX,1000H		;HAS IO RAM BEEN TESTED
	JA 	E22			;YES - GO TO NEXT TEST
	MOV	BX,1000H		;SETUP BEG LOC FOR IO RAM
	JMP	SHORT E21  		;GO TEST IO CHANNEL RAM
;--------------------------------------------
;	CONVERT AND PRINT ASCII CODE
;
;	AL MUST CONTAIN NUMBER TO BE CONVERTED.
;	AX AND BX DESTROYED.
;--------------------------------------------
XLAT_PRINT_CODE	PROC	NEAR
	PUSH 	DS			;SAVE DS VALUE
	PUSH	CS			;POINT DS TO CODE SEG
	POP	DS
	MOV	BX,0E4B7H		; OFFSET ASCII_TBL-XLAT TABLE
	XLATB
	MOV	AH,14
	MOV	BH,0
	INT	10H			;CALL VIDEO_IO
	POP	DS			;RESTORE ORIG VALUE IN DS
	RET
XLAT_PRINT_CODE	ENDP

;---------------------------------------
; INITIAL RELIABILITY TEST -- PHASE 4
;---------------------------------------
	ASSUME	CS:CODE,DS:DATA
F1  	DB	' 301'
F1L	EQU 	$-F1  			; KEYBDARD MESSAGE
F2  	DB  	'131'
F2L 	EQU 	$-F2			; CASSETTE MESSAGE
F3  	DB  	'601'
F3L 	EQU 	$-F3			; DISKETTE MESSAGE

F4	LABEL	WORD			; PRINTER SOURCE TABLE
	DW  3BCH
	DW  378H
	DW  278H
F4E 	LABEL	WORD
ASCII_TBL	DB	'0123456789ABCDEF'
	 
	 
;--------------------------------------------
;TEST.12
;	KEYBOARD TEST
;DESCRIPTION
;	RESET THE KEYBOARD AND CHECK THAT SCAN CODE 'AA' IS RETURNED
;	TO THE CPU.  CHECK FOR STUCK KEYS.
;--------------------------------------------
TST12:
	MOV	AX,DATA 		;POINT DS TO DATA SEG
	MOV	DS,AX
	CMP 	MFG_TST,1 		;MANUFACTURING TEST MODE?
	JE	F7 			;YES - SKIP KEYBOARD TEST
	CALL 	KBD_RESET 		;ISSUE SOFTWARE RESET TO KEYBRD
	JCXZ	F6 			;PRINT ERR MSG IF NO INTERRUPT
	MOV	AL,4DH 			;ENABLE KEYBOARD
	OUT  	PORT_B,AL
	CMP	BL,0AAH 		;SCAN CODE AS EXPECTED?
	JNE	F6 			;NO - DISPLAY ERROR MSG

;CHECK FOR STUCK KEYS

	MOV 	AL,0CCH 		;CLR KBD, SET CLK LINE HIGH
	OUT 	PORT_B,AL
	MOV 	AL,4CH			;ENABLE KBD,CLK IN NEXT BYTE
	OUT 	PORT_B,AL
	SUB 	CX,CX
F5:					; KBD_WAIT:
	LOOP 	F5			;DELAY FOR A WHILE
	IN 	AL,KBD_IN		;CHECK FOR STUCK KEYS
	CMP 	AL,0  			;SCAN CODE = 0?
	JE 	F7  			;YES - CONTINUE TESTING
	MOV 	CH,AL 			;SAVE SCAN CODE
	MOV 	CL,4
	SHR 	AL,CL 			;RIGHT-JUSTIFY HIGH BYTE
	CALL 	XLAT_PRINT_CODE		;CONVERT AND PRINT
	MOV 	AL,CH 			;RECOVER SCAN CODE
	AND 	AL,0FH			;ISOLATE LOW ORDER BYTE
	CALL 	XLAT_PRINT_CODE		;CONVERT AND PRINT
F6: 	MOV 	SI,OFFSET F1  		;GET MSG ADDR
	MOV 	CX,F1L			;GET MSG BYTE COUNT
	CALL 	P_MSG			;PRINT MSG ON SCREEN
	 
;	SETUP INTERRUPT VECTOR TABLE
F7:					; SETUP_INT_TABLE:
	SUB 	AX,AX
	MOV	ES,AX
	MOV	CX,24*2			;GET VECTOR CNT
	PUSH 	CS			;SETUP DS SEG REG
	POP 	DS
	MOV 	SI,0FEF3H		; OFFSET VECTOR_TABLE
	MOV	DI,OFFSET INT_PTR
	CLD
	REP	MOVSW

;--------------------------------------------
;TEST.13
;	CASSETTE DATA WRAP TEST
;DESCRIPTION
;	TURN CASSETTE MOTOR OFF. WRITE A BIT OUT TO THE CASSETTE DATA BUS.
;	VERIFY THAT CASSETTE DATA READ IS WITHIN A VALID RANGE.
;--------------------------------------------

;	TURN THE CASSETTE MOTOR OFF
	
	MOV 	AX,DATA 		;POINT DS REG TO DATA SEG
	MOV 	DS,AX
	MOV 	AL,04DH 		;SET TIMER 2 SPK OUT, AND CASST
	OUT 	PORT_B,AL 		;OUT BITS ON, CASSETTE MOT OFF
	
;	WRITE A BIT
	
	MOV	AL,0FFH 		;DISABLE TIMER INTERRUPTS
	OUT	INTA01,AL
	MOV	AL,0B6H 		;SEL TIM 2, LSB, MSB, MD 3
	OUT	TIMER+3,AL 		;WRITE 8253 CMD/MODE REG
	MOV	AX,1235 		;SET TIMER 2 CNT FOR 1000 USEC
	OUT	TIMER+2,AL 		;WRITE TIMER 2 COUNTER REG
	MOV	AL,AH 			;WRITE MSB
	OUT	TIMER+2,AL
	
;	READ CASSETTE INPUT
	
	IN  	AL,PORT_C 		;READ VALUE OF CASS IN BIT
	AND 	AL,10H 			;ISOUTE FROM OTHER BITS
	MOV 	LAST_VAL,AL
	CALL	READ_HALF_BIT
	CALL	READ_HALF_BIT
	JCXZ	F8 			; CAS_ERR
	CMP 	BX,MAX_PERIOD
	JNC 	F8			; CAS_ERR
	CMP 	BX,MIN_PERIOD
	JNC 	F9			;GO TO NEXT TEST IF OK
F8:					; CAS_ERR:
	MOV 	SI,OFFSET F2 		;CASSETTE WRAP FAILED
	MOV 	CX,F2L
	CALL	P_MSG			;GO PRINT ERROR MSG

;--------------------------------------------
;TEST.14
;	DISKETTE ATTACHMENT TEST
;DESCRIPTION
;	CHECK IF IPL DISKETTE DRIVE IS ATTACHED TO SYSTEM.  IF ATTACHED,
;	VERIFY STATUS OF NEC FDC AFTER A RESET. ISSUE A RECAL AND SEEK
;	CMD TO FDC AND CHECK STATUS. COMPLETE SYSTEM INITIALIZATION THEN
;	PASS CONTROL TO THE BOOT LOADER PROGRAM.
;--------------------------------------------
F9:
	MOV 	AL,0FCH 		;ENABLE TIMER AND KBD INTS
	OUT 	INTA01,AL
	MOV 	AL,BYTE PTR EQUIP_FLAG	;GET SENSE SWS INFO
	TEST	AL,01H			;IPL DISKETTE DRIVE ATTCH?
	JNZ 	F10			;YES - TEST DISKETTE CONTR
	JMP	F22			;NO - SKIP THIS TEST
F10:					; DISK-TEST:
	MOV 	AL,0BCH			;ENABLE DISKETTE, KEYBOARD,
	OUT 	INTA01,AL		; AND TIMER INTERRUPTS
	MOV 	AH,0 			;RESET NEC FDC
	INT 	13H			;VERIFY STATUS AFTER RESET
	TEST	AH,0FFH			;STATUS OK?
	JNZ 	F13			;NO - FDC FAILED
	 
;	TURN DRIVE 0 MOTOR ON
	
	MOV	DX,03F2H		;GET ADDR DF FDC CARD
	MOV	AL,1CH			;TURN MOTOR ON, EN DMA/INT
	OUT	DX,AL			;WRITE FDC CONTROL REG
	SUB	CX,CX
F11:					; MOTOR_WAIT:
	LOOP 	F11			;WAIT FOR 1 SECOND
F12:					; MOTOR_WAIT1:
	LOOP 	F12
	XOR	DX,DX			;SELECT DRIVE 0
	MOV	CH,1			;SELECT TRACK 1
	MOV	SEEK_STATUS,DL
	CALL	SEEK			;RECALIBRATE DISKETTE
	JC	F13			;GO TO ERR SUBROUTINE IF ERR
	MOV	CH,34			;SELECT TRACK 34
	CALL	SEEK			;SEEK TO TRACK 34
	JNC	F14			;OK, TURN MOTOR OFF
F13:					; DSK_ERR:
	MOV	SI,OFFSET F3		;GET ADDR OF MSG
	MOV	CX,F3L			;GET MSG BYTE COUNT
	CALL	P_MSG			;GO PRINT ERRDR MSG
	
;	TURN DRIVE 0 MOTOR OFF
F14:					; DR0_OFF:
	MOV 	AL,CH 	 		;TURN DRIVE 0 MOTOR OFF
	MOV 	DX,03F2H		; FDC CTL ADDRESS
	OUT 	DX,AL
	
;	SETUP PRINTER AND RS232 BASE ADDRESSES IF DEVICE ATTACHED

F15: 					; JMP_BOOT:
	MOV	BUFFER_HEAD,OFFSET KB_BUFFER ;SETUP KEYBOARD PARAMETERS
	MOV	BUFFER_TAIL,OFFSET KB_BUFFER
	MOV	BP,OFFSET F4		; PRT_SRC_TBL
	MOV	SI,0
F16:					; PRT_BASE:
	MOV	DX,CS:[BP]		;GET PRINTER BASE ADDR
	MOV	AL,0AAH			;WRITE DATA TO PORT A
	OUT	DX,AL
	SUB	AL,AL
	IN	AL,DX			;READ PORT A
	CMP	AL,0AAH			;DATA PATTERN SAME
	JNE	F17			;NO - CHECK NEXT PRT CD
	MOV	PRINTER_BASE[SI],DX	;YES - STORE PRT BASE ADDR
	INC	SI			; INCREMENT TO NEXT WORD
	INC	SI
F17:  					; NO_STORE:
	INC	BP			;POINT TO NEXT BASE ADDR
	INC 	BP
	CMP	BP,OFFSET F4E		;ALL POSSIBLE ADDRS CHECKED?
	JNE	F16			;PRT_BASE
	MOV	BX,0			;POINTER TO RS232 TABLE
	MOV	DX,3FAH			;CHECK IF RS232 CD 1 ATTCH?
	IN	AL,DX			;READ INTR ID REG
	TEST 	AL,0F8H
	JNZ	F18
	MOV	RS232_BASE[BX],3F8H	;SETUP RS232 CD #1 ADDR
	INC	BX
	INC 	BX
F18:	MOV	DX,2FAH			;CHECK IF RS232 CD 2 ATTCH
	IN	AL,DX			;READ INTERRUPT ID REG
	TEST	AL,0F8H
	JNZ	F19			;BASE_END
	MOV	RS232_BASE[BX],2F8H	;SETUP RS232 CD #2
	INC	BX
	INC	BX
	
;------ SET UP EQUIP FLAG TO INDICATE NUMBER OF PRINTERS AND RS232 CARDS
	
F19:					; BASE_END:
	MOV 	AX,SI			; SI HAS 2* NUMBER OF RS232
	MOV 	CL,3			; SHIFT COUNT
	ROR 	AL,CL			; ROTATE RIGHT 3 POSITIONS
	OR  	AL,BL			; OR IN THE PRINTER COUNT
	MOV 	BYTE PTR EQUIP_FLAG+1,AL	; STORE AS SECOND BYTE
	MOV 	DX,201H
	IN  	AL,DX
	TEST	AL,0FH
	JNZ 	F20			; NO_GAME_CARD
	OR  	BYTE PTR EQUIP_FLAG+1,16
F20:					; NO_GAME_CARD:
	
;  ENABLE NMI INTERRUPTS
	MOV	AL,80H			;ENABLE NMI INTERRUPTS
	OUT	0A0H,AL
	CMP 	MFG_TST,1		;MFG MODE?
	JE  	F21			; LOAD_BOOT_STRAP
	MOV 	DX,1
	CALL	ERR_BEEP		;BEEP 1 SHORT TONE
F21:					; LOAD_BOOT_STRAP:
	JMP	BOOT_STRAP		;GO TO THE BOOT LOADER
F22:					; LOOP_POD:
	CMP	MFG_TST,1		;MANUFACTURING TEST MODE?
	JNE	F23			;NO - GO TO BOOT LOADER
	JMP	START			;YES - LOOP POWER-ON-DIAGS
F23:					; GO_TO_BOOT:
	JMP	F15			; JMP_BOOT
;--------------------------------------------
; INITIAL RELIABILITY TEST -- SUBROUTINES
;--------------------------------------------
	ASSUME	CS:CODE,DS:DATA
;--------------------------------------------
;	SUBROUTINES FOR POWER ON DIAGNOSTICS
;--------------------------------------------
;	THIS PROCEDURE WILL ISSUE ONE LONG TONE (3 SECS) AND ONE OR
;	MORE SHORT TONES (1 SEC) TO INDICATE A FAILURE ON THE PLANAR
;	BOARD, A BAD RAM MODULE, OR A PROBLEM WITH THE CRT.
;ENTRY PARAMETERS:
;	DH = NUMBER OF LONG TONES TO BEEP
;	DL = NUMBER OF SHORT TONES TO BEEP.
;--------------------------------------------
ERR_BEEP	PROC	NEAR
	PUSHF				;SAVE FLAGS
	CLI 				;DISABLE SYSTEM INTERRUPTS
	PUSH	DS			;SAVE DS REG CONTENTS
	MOV 	AX,DATA 		;POINT DS TO DATA SEG
	MOV	DS,AX
	OR	DH,DH			; ANY LONG ONES TO BEEP
	JZ	G3			; NO, DO THE SHORT ONES
G1:					; LONG_BEEP:
	MOV	BL,6			; COUNTER FOR BEEPS
	CALL	BEEP			; DO THE BEEP
G2:	LOOP 	G2 			; DELAY BETWEEN BEEPS
	DEC 	DH 			; ANY MORE TO DO
	JNZ	G1			; DO IT
	CMP 	MFG_TST,1 		; MFG TEST MODE?
	JNE	G3			; YES - CONTINUE BEEPING SPEAKER 
	MOV 	AL,0CDH 		; STOP BLINKING LED
	OUT 	PORT_B,AL
	JMP 	SHORT G1
G3:					; SHORT_BEEP:
	MOV 	BL,1 			; COUNTER FOR A SHORT BEEP
	CALL	BEEP			; DO THE SOUND
G4:	LOOP	G4			; DELAY BETWEEN BEEPS
	DEC 	DL 			; DONE WITH SHORTS
	JNZ	G3  			; DO SOME MORE
G5:	LOOP	G5			; LONG DELAY BEFORE RETURN
G6:	LOOP	G6
	POP 	DS 			;RESTORE ORIG CONTENTS OF DS
	POPF				;RESTORE FLAGS TO ORIG SETTINGS
	RET 				; RETURN TO CALLER
ERR_BEEP	ENDP
	 
;	ROUTINE TO SOUND BEEPEP
	 
BEEP	PROC	NEAR
	MOV	AL,10110110B		;SEL TIM 2,LSB,MSB,BINARY
	OUT	TIMER+3,AL		;WRITE THE TIMER MODE REG
	MOV	AX,533H			;DIVISOR FOR 1000 HZ
	OUT	TIMER+2,AL		;WRITE TIMER 2 CNT - LSB
	MOV	AL,AH
	OUT	TIMER+2,AL		;WRITE TIMER 2 CNT - MSB
	IN	AL,PORT_B		;GET CURRENT SETTING OF PORT
	MOV	AH,AL			; SAVE THAT SETTING
	OR	AL,03			;TURN SPEAKER ON
	OUT	PORT_B,AL
	SUB	CX,CX			;SET SNT TO WAIT 500 MS
G7:	LOOP	G7			;DELAY BEFORE TURNING OFF
	DEC	BL			;DELAY CNT EXPIRED?
	JNZ	G7			;NO - CONTINUE BEEPING SPK
	MOV	AL,AH			; RECOVER VALUE OF PORT
	OUT	PORT_B,AL
	RET 				;RETURN TO CALLER
BEEP	ENDP
;--------------------------------------------
;	THIS PROCEDURE WILL SEND A SOFTWARE RESET TO THE KEYBOARD.
;	SCAN CDDE 'AA' SHOULD BE RETURNED TO THE CPU.
;-------------------------------------------
KBD_RESET	PROC	NEAR
	MOV	AL,0CH			;SET KBD CLK LINE LOW
	OUT	PORT_B,AL  		;WRITE 8255 PORT B
	MOV	CX,10582		;HOLD KBD CLK LOW FOR 20 MS
G8:	LOOP 	G8			;LOOP FOR 20 MS
	MOV 	AL,0CCH 		;SET CLK, ENABLE LINES HIGH
	OUT 	PORT_B,AL
SP_TEST: 				; ENTRY FOR MANUFACTURING TEST 2
	MOV 	AL,4CH			;SET KBD CLK HIGH, ENABLE LOW
	OUT 	PORT_B,AL
	MOV 	AL,0FDH			;ENABLE KEYBOARD INTERRUPTS
	OUT 	INTA01,AL 		;WRITE 8255 IMR
	STI				;ENABLE SYSTEM INTERRUPTS
	MOV 	AH,0			;RESET INTERRUPT INDICATOR
	SUB 	CX,CX  			;SETUP INTERRUPT TIMEOUT CNT
G9: 	TEST 	AH,0FFH 		;DID A KEYBOARD INTR OCCUR?
	JNZ 	G10			;YES - READ SCAN CDDE RETURNED
	LOOP 	G9 			;NO - LOOP TILL TIMEOUT
G10:	IN 	AL,PORT_A		;READ KEYBOARD SCAN CDDE
	MOV 	BL,AL  			;SAVE SCAN CODE JUST READ
	MOV 	AL,0CCH 		;CLEAR KEYBOARD
	OUT 	PORT_B,AL
	RET				;RETURN TO CALLER
KBD_RESET	ENDP
;--------------------------------------------
;	BLINK LED PROCEDURE FOR MFG BURN-IN AND RUN-IN TESTS
;	(LED WILL BLINK APPROXIMATELY .25 SECOND)
;--------------------------------------------
BLINK_INT	PROC	NEAR
	STI
	PUSH	CX			;SAVE CX REG CONTENTS
	PUSH	AX			;SAVE AX REG CONTENTS
	IN	AL,PORT_B		;READ CURRENT VAL OF PORT B
	AND	AL,0BFH
	OUT	PORT_B,AL		;BLINK LED
	SUB	CX,CX
G11:	LOOP 	G11
	OR 	AL,40H  		;STOP BLINKING LED
	OUT 	PORT_B,AL
	MOV 	AL,EOI
	OUT 	INTA00,AL
	POP 	AX			;RESTORE AX REG
	POP 	CX			;RESTORE CX REG
	IRET
BLINK_INT	ENDP
;--------------------------------------------
;	THIS SUBROUTINE WILL PRINT A MESSAGE ON THE DISPLAY
;
;ENTRY REQUIREMENTS:
;	SI = OFFSET (ADDRESS) OF MESSAGE BUFFER
;	CX = MESSAGE BYTE COUNT
;	MAXIMUM MESSAGE LENGTH IS 36 CHARACTERS
;--------------------------------------------
P_MSG	PROC	NEAR
	MOV	AX,DATA 		;POINT DS TO DATA SEG
	MOV	DS,AX
	CMP	MFG_TST,1 		;MFG TEST MODE?
	JNE	G12			;NO - DISPLAY ERROR MSG
	MOV	DH,1 			;YES - SETUP TO BEEP SPEAKER
	JMP	ERR_BEEP		;YES - BEEP SPEAKER
G12:					; WRITE_MSG:
	MOV	AL,CS:[SI]		;PUT CHAR IN AL
	INC	SI 			; POINT TO NEXT CHAR
	MOV	BH,0 			;SET PAGE # TO ZERO
	MOV	AH,14			;WRITE CHAR (TTY-INTERFACE)
	INT 	10H  			;CALL VIDEO_IO
	LOOP 	G12			;CONTINUE TILL MSG WRITTEN
	MOV	AX,0E0DH		;PDSITION CURSOR TO NEXT LINE
	INT	10H			;SEND CARRIAGE RETURN AND
	MOV	AX,0E0AH		;LINE FEED CHARS
	INT	10H
	RET
P_MSG	ENDP
;--- INT 19 -----------------------------
;BOOT STRAP LOADER
;	IF A 5 1/4" DISKETTE DRIVE IS AVAILABLE
;	ON THE SYSTEM, TRACK 0, SECTOR 1 IS READ INTO THE
;	BOOT LOCATION (SEGMENT 0, OFFSET 7C00)
;	AND CONTROL IS TRANSFERRED THERE.
;
;	IF THERE IS NO DISKETTE DRIVE, OR IF THERE IS
;	IS A HARDWARE ERROR CONTROL IS TRANSFERRED
;	TO THE CASSETTE BASIC ENTRY POINT.
;
; IPL ASSUMPTIONS
;	8255 PORT 60H BIT 0
;	= 1 IF IPL FROM DISKETTE
;-----------------------------------------
	ASSUME	CS:CODE,DS:DATA
BOOT_STRAP	PROC	NEAR

	STI				; ENABLE INTERRUPTS
	MOV	AX,DATA			; ESTABLISH ADDRESSING
	MOV	DS,AX
	MOV	AX,EQUIP_FLAG		; GET THE EQUIPMENT SWITCHES
	TEST 	AL,1			; ISOLATE IPL SENSE SWITCH
	JZ	H3			; GO TO CASSETTE BASIC ENTRY POINT
	 
;------ MUST LOAD SYSTEM FROM DISKETTE -- CX HAS RETRY COUNT
	 
	MOV	CX,4			; SET RETRY COUNT
H1:					; IPL_SYSTEM
	PUSH 	CX			; SAVE RETRY COUNT
	MOV	AH,0			; RESET THE DISKETTE SYSTEM
	INT	13H			; DISKETTE_IO
	JC	H2			; IF ERROR, TRY AGAIN
	MOV	AH,2			; READ IN THE SINGLE SECTOR
	MOV	BX,0			; TO THE BOOT LOCATION
	MOV	ES,BX
	MOV	BX,OFFSET BOOT_LOCN
	MOV 	DX,0			; DRIVE 0, HEAD 0
	MOV 	CX,1			; SECTOR 1, TRACK 0
	MOV 	AL,1			; READ ONE SECTOR
	INT 	13H 			; DISKETTE_IO
H2: 	POP 	CX  			; RECOVER RETRY COUNT
	JNC 	H4  			; CF SET BY UNSUCCESSFUL READ
	LOOP 	H1 			; DO IT FOR RETRY TIMES

;------ UNABLE TO IPL FROM THE DISKETTE

H3:					; CASSETTE_JUMP:
	INT	18H			; USE INTERRUPT VECTOR TO GET TO BASIC

;------ IPL WAS SUCCESSFUL

H4:
	JMP	BOOT_LOCN
BOOT_STRAP	ENDP
;-----INT 14---------------------------------
;RS232_IO
;	THIS ROUTINE PROVIDES BYTE STREAM I/O TO THE COMMUNICATIONS
;	PORT ACCORDING TO THE PARAMETERS:
;	(AH)=0	INITIALIZE THE COMMUNICATIONS PORT
;		(AL) HAS PARMS FOR INITIALIZATION
;
;		7	6	5 	4	3  	2	1	0
;		----- BAUD RATE --	-PARITY--     STOPBIT   --WORD LENGTH--
;
;		000 - 110		X0 - NONE	0 - 1	 10 - 7 BITS
;		001 - 150		01 - ODD	1 - 2	 11 - 8 BITS
;		010 - 300		11 - EVEN
;		011 - 600
;		100 - 1200
;		101 - 2400
;		110 - 4800
;		111 - 9600
;
;		ON RETURN, CONDITIONS SET AS IN CALL TO COMMO STATUS (AH=3)
;	(AH)=1	SEND THE CHARACTER IN (AL) OVER THE COMMO LINE
;		(AL) REGISTER IS PRESERVED
;		ON EXIT, BIT 7 OF AH IS SET IF THE ROUTINE WAS UNABLE TO
;			TO TRANSMIT THE BYTE OF DATA OVER THE LINE. THE
;			REMAINDER OF AH IS SET AS IN A STATUS REQUEST,
;			REFELECTING THE CURRENT STATUS OF THE LINE.
;	(AH)=2	RECEIVE A CHARACTER IN (AL) FROM COMMO LINE BEFORE
;			RETURNING TO CALLER
;		ON EXIT, AH HAS THE CURRENT LINE STATUS, AS SET BY THE
;			THE STATUS ROUTINE, EXCEPT THAT THE ONLY BITS
;			LEFT ON ARE THE ERROR BITS (7,4,3,2,1)
;			IN THIS CASE, THE TIHE OUT BIT INDICATES DATA SET
;			READY WAS NOT RECEIVED.
;			THUS, AH IS NON ZERO ONLY WHEN AN ERROR OCCURRED.
;	(AH)=3	RETURN THE COMMO PORT STATUS IN (AX)
;		AH CONTAINS THE LINE CONTROL STATUS
;		BIT 7 = TIME OUT
;		BIT 6 = TRANS SHIFT REGISTER EMPTY
;		BIT 5 = TRAN HOLDING REGISTER EMPTY
;	BIT 4 = BREAK DETECT
;	BIT 3 = FRAMING ERROR
;	BIT 2 = PARITY ERROR
;	BIT 1 = OVERRUN ERROR
;	BIT 0 = DATA READY
;	AL CONTAINS THE MODEM STATUS
;	BIT 7 = RECEVED LINE SIGNAL DETECT
;	BIT 6 = RING INDICATOR
;	BIT S = DATA SET READY
;	BIT 4 = CLEAR TO SEND
;	BIT 3 = DELTA RECEIVE LINE SIGNAL DETECT
;	BIT 2 = TRAILING EDGE RING DETECTOR
;	BIT 1 = DELTA DATA SET READY
;	BIT 0 = DELTA CLEAR TO SEND
;
;	(DX) = PARAMETER INDICATING WHICH RS232 CARD (0,1 ALLOWED)
; DATA AREA RS232_BASE CONTAINS THE BASE ADDRESS OF THE 8250 ON THE CARD
;	LOCATION 400H CONTAINS UP TO 4 RS232 ADDRESSES POSSIBLE
;OUTPUT
;	AX	MODIFIED ACCORDING TO PARMS OF CALL
;	ALL OTHERS UNCHANGED
;------------------------------------
	ASSUME	CS:CODE,DS:DATA
A1	LABEL	WORD
		DW	1047 		; 110 BAUD	; TABLE OF INIT VALUE
		DW 	768 		; 150
		DW 	384 		; 300
		DW 	192 		; 600
		DW 	96 		; 1200
		DW 	48 		; 2400
		DW 	24 		; 4800
		DW	12 		; 9600
		
RS232_IO	PROC	FAR
	
;------ VECTOR TO APPROPRIATE ROUTINE
	
	STI				; INTERRUPTS BACK ON
	PUSH 	DS			; SAVE SEGMENT
	PUSH 	DX
	PUSH 	SI
	PUSH 	DI
	PUSH 	CX
	MOV	SI,DX			; RS232 VALUE TO SI
	SHL	SI,1			; WORD OFFSET
	MOV	DX,DATA
	MOV	DS,DX			; SET UP OUR SEGMENT
	MOV	DX,RS232_BASE[SI] 	; GET BASE ADDRESS
	OR	DX,DX			; TEST FOR 0 BASE ADDRESS
	JZ	A3			; RETURN
	OR	AH,AH			; TEST FOR (AH)=0
	JZ	A4			; COMMUN INIT
	DEC	AH			; TEST FOR (AH)=1
	JZ	A5			; SEND AL
	DEC	AH			; TEST FOR (AH)=2
	JNZ	A2
	JMP	A12  			; RECEIVE INTO AL

A2:
        DEC     AH                      ; TEST FOR (AH)=3
        JNZ     A3
        JMP     A18                     ; COMMUNICATICN STATUS
A3:                                     ; RETURN FROM RS232
        POP     CX 
        POP     DI
        POP     SI
	POP     DX
        POP     DS
        IRET                            ; RETURN TO CALLER, NO ACTION

;------ INITIALIZE THE COMMUNICATIONS PORT

A4:
        MOV     AH,AL                   ; SAVE INIT PARMS IN AH
        ADD     DX,3                    ; POINT TO 8250 CONTROL REGISTER
        MOV     AL,80H
        OUT     DX,AL                   ; SET DLAB=1

;------ DETERMINE BAUD RATE DIVISOR

	MOV     DL,AH                   ; GET PARMS TO DL
	ROL     DL,1
	ROL     DL,1                    ; GET BAUD RATE TERM TO LOW BITS
	ROL	DL,1
        ROL     DL,1                    ; *2 FOR WORD TABLE ACCESS
        AND     DX,0EH                  ; ISOLATE THEM
        MOV     DI,OFFSET A1            ; BASE OF TABLE
        ADD     DI,DX                   ; PUT INTO INDEX REGISTER
        MOV     DX,RS232_BASE[SI]       ; POINT TO HIGH ORDER OF DIVISOR
        INC     DX
        MOV     AL,CS:[DI]+1            ; GET HIGH ORDER OF DIVISOR
        OUT     DX,AL                   ; SET MS OF DIV TO 0
        DEC     DX
        MOV     AL,CS:[DI]              ; GET LOW ORDER OF DIVISOR
        OUT     DX,AL                   ; SET LOW OF DIVISOR
        ADD     DX,3
        MOV     AL,AH                   ; GET PARMS BACK
        AND     AL,01FH                 ; STRIP OFF THE BAUD BITS
        OUT     DX,AL                   ; LINE CONTROL TO 8 BITS
        SUB     DX,2
        MOV     AL,0
        OUT     DX,AL                   ; INTERRUPT ENABLES ALL OFF
        JMP     SHORT A18               ; COM_STATUS

;------ SEND CHARACTER IN (AL) OVER COMMO LINE

A5:
        PUSH    AX                      ; SAVE CHAR TO SEND
        ADD     DX,4                    ; MODEM CONTROL REGISTER
        MOV     AL,3                    ; DTR AND RTS
        OUT     DX,AL                   ; DATA TERMINAL READY, REQUEST TO SEND
        XOR     CX,CX                   ; INITIALIZE TIME OUT COUNTER
        ADD     DX,2                    ; MODEM STATUS REGISTER
A6:                                     ; WAIT_DATA_SET_READY
        IN      AL,DX                   ; GET MODEM STATUS
        TEST    AL,20H                  ; DATA SET READY
        JNZ     A7                      ; TEST_CLEAR_TO_SEND
        LOOP    A6                      ; WAIT_DATA_SET_READY
	POP	AX
        OR      AH,80                   ; INDICATE TIME OUT
        JMP     A3                      ; RETURN
A7:                                     ; TEST_CLEAR_TO_SEND
        SUB     CX,CX
A8:                                     ; WAIT_CLEAR_TO_SEND
        IN      AL,DX                   ; GET MODEM STATUS
        TEST    AL,10H                  ; TEST CLEAR TO SEND
        JNZ     A9                      ; CLEAR_TO_SEND
        LOOP    A8                      ; WAIT_CLEAR_TO_SEND
        POP     AX                      ; TIME OUT HAS OCCURRED
        OR      AH,80H
        JMP     A3                      ; RETURN
A9:                                     ; CLEAR_TO_SEND
        DEC     DX                      ; LINE STATUS REGISTER
        SUB     CX,CX                   ; INITIALIZE WAIT COUNT
A10:                                    ; WAIT_SEND
        IN      AL,DX                   ; GET STATUS
        TEST    AL,20H                  ; IS TRANSMITTER READY
        JNZ     A11                     ; OUT_CHAR
        LOOP    A10                     ; GO BACK FOR MORE, AND TEST FOR TIME OUT
        POP     AX                      ; RECOVER ORIGINAL INPUT
        OR      AH,80H                  ; SET THE TIME OUT BIT
        JMP     NEAR PTR A3		; RETURN
A11:                                    ; OUT_CHAR
        SUB     DX,5                    ; DATA PORT
        POP     CX                      ; RECOVER IN CX TEMPORARILY
        MOV     AL,CL                   ; GET OUT CHAR TO AL FOR OUT, STATUS IN AH
        OUT     DX,AL                   ; OUTPUT CHARACTER
        JMP     NEAR PTR A3             ; RETURN

;------ RECEIVE CHARACTER FROM COMMO LINE

A12:
        AND	BIOS_BREAK,07FH		; TURN OFF BREAK BIT IN BYTE
        ADD     DX,4                    ; MODEM CONTROL REGISTER
        MOV     AL,1                    ; DATA TERMINAL READY
        OUT     DX,AL
        ADD     DX,2                    ; MODEM STATUS REGISTER
        SUB     CX,CX                   ; ESTABLISH TIME OUT COUNT
A13:                                    ; WAIT_DSR
        IN      AL,DX                   ; MODEM STATUS
        TEST    AL,20H                  ; DATA SET READY
        JNZ     A15                     ; IS IT READY YET
        LOOP    A13                     ; WAIT UNTIL IT IS
A14:                                    ; TIME_OUT_ERR
        MOV     AH,80H                  ; SET TIME OUT ERROR
        JMP     A3                      ; RETURN WITH ERROR
A15:                                    ; WAIT_DSR_END
        DEC     DX                      ; LINE STATUS REGISTER
A16:                                    ; WAIT_RECV
        IN      AL,DX                   ; GET STATUS
        TEST    AL,1                    ; RECEIVE BUFFER FULL
        JNZ     A17                     ; GET CHAR
        TEST    BIOS_BREAK,80H          ; TEST FOR BREAK KEY
        JZ      A16                     ; LOOP IF NOT
        JMP     A14                     ; SET TIME OUT ERROR
A17:                                    ; GET_CHAR
        AND     AL,00011110B            ; TEST FOR ERROR CONDITIONS ON RECV CHAR
        MOV     AH,AL                   ; SAVE THIS PART OF STATUS FOR LATER OPERATION
        MOV     DX,RS232_BASE[SI]       ; DATA PORT
        IN      AL,DX                   ; GET CHARACTER FROM LINE
        JMP     A3                      ; RETURN

;------ COMMO PORT STATUS ROUTINE

A18:
        MOV     DX,RS232_BASE[SI]
        ADD     DX,5                    ; CONTROL PORT
        IN      AL,DX                   ; GET LINE CONTROL STATUS
        MOV     AH,AL                   ; PUT IN AH FOR RETURN
        INC     DX                      ; POINT TO MODEM STATUS REGISTER
        IN      AL,DX                   ; GET MODEM CONTROL STATUS
        JMP     A3                      ; RETURN
RS232_IO        ENDP

;---- INT 16 --------------------------------
; KEYBOARD I/O
;       THESE ROUTINES PROVIDE KEYBOARD SUPPORT
; INPUT
;       (AH)=0  READ THE NEXT ASCII CHARACTER STRUCK FROM THE KEYBOARD
;               RETURN THE RESULT IN (AL), SCAN CODE IN (AH)
;       (AH)=1  SET THE Z FLAG TO INDICATE IF AN ASCII CHARACTER IS AVAILABLE
;               TO BE READ.
;               (ZF)=1 -- NO CODE AVAILABLE
;               (ZF)=0 -- CODE IS AVAILABLE
;               IF ZF = 0, THE NEXT CHARACTER IN THE BUFFER TO BE READ IS
;               IN AX, AND THE ENTRY REMAINS IN THE BUFFER
;       (AH)=2  RETURN THE CURRENT SHIFT STATUS IN AL REGISTER
;               THE BIT SETTINGS FOR THIS CODE ARE INDICATED IN THE
;               THE EQUATES FOR KB_FLAG
; OUTPUT
;       AS NOTED ABOVE, ONLY AX AND FLAGS CHANGED
;       ALL REGISTERS RETAINED
;----------------------------------------
	ASSUME  CS:CODE,DS:DATA
KEYBOARD_IO     PROC    FAR
        STI                             ; INTERRUPTS BACK ON
        PUSH    DS                      ; SAVE CURRENT DS
        PUSH    BX                      ; SAVE BX TEMPORARILY
        MOV     BX,DATA                 ;
        MOV     DS,BX                   ; ESTABLISH POINTER TO DATA REGION
        OR      AH,AH                   ; AH=0
        JZ      K1                      ; ASCII_READ
        DEC     AH                      ; AH=1
        JZ      K2                      ; ASCII_STATUS
        DEC     AH                      ; AH=2
        JZ      K3                      ; SHIFT_STATUS
        POP     BX                      ; RECOVER REGISTER
	POP	DS
        IRET                            ; INVALID COMMAND

;------ READ THE KEY TO FIGURE OUT WHAT TO DO

K1:                                     ; ASCII READ
        STI                             ; INTERRUPTS BACK ON DURING LOOP
        NOP                             ; ALLOW AN INTERRUPT TO OCCUR
        CLI                             ; INTERRUPTS BACK OFF
        MOV     BX,BUFFER_HEAD          ; GET POINTER TO HEAD OF BUFFER
        CMP     BX,BUFFER_TAIL          ; TEST END OF BUFFER
        JZ      K1                      ; LOOP UNTIL SOMETHING IN BUFFER
        MOV     AX,[BX]                 ; GET SCAN CODE AND ASCII CODE
        CALL    K4                      ; MOVE POINTER TO NEXT POSITION
        MOV     BUFFER_HEAD,BX          ; STORE VALUE IN VARIABLE
        POP     BX                      ; RECOVER REGISTER
        POP     DS                      ; RECOVER SEGMENT
        IRET                            ; RETURN TO CALLER

;------ ASCII STATUS

K2:
        CLI                             ; INTERRUPTS OFF
        MOV     BX,BUFFER_HEAD          ; GET HEAD POINTER
        CMP     BX,BUFFER_TAIL          ; IF EQUAL (Z=1) THEN NOTHING HERE
        MOV     AX,[BX] 
        STI                             ; INTERRUPTS BACK ON
        POP     BX                      ; RECOVER REGISTER
        POP     DS                      ; RECOVER SEGMENT
        RET     2                       ; THROW AWAY FLAGS

;------ SHIFT STATUS

K3:
        MOV     AL,KB_FLAG              ; GET THE SHIFT STATUS FLAGS
        POP     BX                      ; RECOVER REGISTER
        POP     DS                      ; RECOVER REGISTERS
        IRET                            ; RETURN TO CALLER
KEYBOARD_IO     ENDP

;------ INCREMENT A BUFFER POINTER

K4      PROC    NEAR
        ADD     BX,2                    ; MOVE TO NEXT WORD IN LIST
        CMP     BX,OFFSET KB_BUFFER_END ; AT END OF BUFFER?
        JNE     K5                      ; NO, CONTINUE
        MOV     BX,OFFSET KB_BUFFER     ; YES, RESET TO BUFFER BEGINNING
K5:
	RET
K4      ENDP

;------ TABLE OF SHIFT KEYS AND MASK VALUES

K6      LABEL   BYTE
        DB      INS_KEY                 ; INSERT KEY
        DB      CAPS_KEY,NUM_KEY,SCROLL_KEY,ALT_KEY,CTL_KEY
	DB	    LEFT_KEY,RIGHT_KEY
K6L     EQU     $-K6

;------ SHIFT_MASK_TABLE

K7      LABEL   BYTE
        DB      INS_SHIFT               ; INSERT MODE SHIFT
        DB      CAPS_SHIFT,NUM_SHIFT,SCROLL_SHIFT,ALT_SHIFT,CTL_SHIFT
        DB      LEFT_SHIFT,RIGHT_SHIFT

;------ SCAN CODE TABLES

K8      	DB      27,-1,0,-1,-1,-1,30,-1
        	DB      -1,-1,-1,31,-1,127,-1,17
        	DB      23,5,18,20,25,21,9,15
        	DB      16,27,29,10,-1,1,19
        	DB      4,6,7,8,10,11,12,-1,-1
        	DB      -1,-1,28,26,24,3,22,2
        	DB      14,13,-1,-1,-1,-1,-1,-1
        	DB      ' ',-1
;------ CTL TABLE SCAN
K9      LABEL   BYTE
        	DB      94,95,96,97,98,99,100,101
        	DB      102,103,-1,-1,119,-1,132,-1
        	DB      115,-1,116,-1,117,-1,118,-1
        	DB      -1
;------ LC TABLE
K10     LABEL    BYTE
        	DB      01BH,'1234567890-=',08H,09H


		DB	'quertyuiop[]',0DH,-1,'asdfghjkl;',027H


        	DB      60H,-1,5CH,'zxcvbnm,./',-1,'*',-1,' '


		DB	-1

;------ UC TABLE
K11     LABEL   BYTE
	        DB      27,'!@#$',37,05EH,'&*()_+',08H,0


		DB	'QWERTYUIOP  ',0DH,-1,'ASDFGHJKL:"'


	        DB      07EH,-1,' ZXCVBNM<>?',-1,0,-1,' ',-1

;------ UC TABLE SCAN
K12     LABEL   BYTE
	        DB      84,85,86,87,88,89,90
        	DB      91,92,93
;------ ALT TABLE SCAN
K13     LABEL   BYTE
	        DB      104,105,106,107,108
        	DB      109,110,111,112,113

;------ NUM STATE TABLE
K14     LABEL   BYTE
	        DB      '789-456+1230.'

;------ BASE CASE TABLE
K15     LABEL   BYTE
        	DB      71,72,73,-1,75,-1,77
	        DB      -1,79,80,81,82,83

;------ KEYBOARD INTERRUPT ROUTINE

KB_INT	PROC	FAR
        STI                             ; ALLOW FURTHER INTERRUPTS
	PUSH    AX
	PUSH    BX
	PUSH    CX
        PUSH    DX
	PUSH    SI
	PUSH    DI
	PUSH    DS
	PUSH    ES
        CLD                             ; FORWARD DIRECTION
	MOV     AX,DATA
        MOV     DS,AX                   ; SET UP ADDRESSING
        IN      AL,KB_DATA              ; READ IN THE CHARACTER
        PUSH    AX                      ; SAVE IT
        IN      AL,KB_CTL               ; GET THE CONTROL PORT
        MOV     AH,AL                   ; SAVE VALUE
        OR      AL,80H                  ; RESET BIT FOR KEYBOARD
        OUT     KB_CTL,AL
        XCHG    AH,AL                   ; GET BACK ORIGINAL CONTROL
        OUT     KB_CTL,AL               ; KB HAS BEEN RESET
        POP     AX                      ; RECOVER SCAN CODE
        MOV     AH,AL                   ; SAVE SCAN CODE IN AH ALSO

;------ TEST FOR OVERRUN SCAN CODE FROM KEYBOARD

        CMP     AL,0FFH                 ; IS THIS AN OVERRUN CHAR
        JNZ     K16                     ; NO, TEST FOR SHIFT KEY
        JMP     K62                     ; BUFFER_FULL_BEEP

;------ TEST FOR SHIFT KEYS

K16:                                    ; TEST_SHIFT
        AND     AL,07FH                 ; TURN OFF THE BREAK BIT
        PUSH    CS
        POP     ES                      ; ESTABLISH ADDRESS OF SHIFT TABLE
        MOV     DI,OFFSET K6            ; SHIFT KEY TABLE
        MOV     CX,K6L                  ; LENGTH
        REPNE   SCASB                   ; LOOK THROUGH THE TABLE FOR A MATCH

        MOV     AL,AH                   ; RECOVER SCAN CODE
        JE      K17                     ; JUMP IF MATCH FOUND
        JMP     K25                     ; IF NO MATCH, THEN SHIFT NOT FOUND

;------ SHIFT KEY FOUND
K17:    SUB     DI,OFFSET K6+1          ; ADJUST PTR TO SCAN CODE MTCH
        MOV     AH,CS:K7[DI]            ; GET MASK INTO AH
        TEST    AL,80H                  ; TEST FOR BREAK KEY
        JNZ     K23                     ; BREAK_SHIFT_FOUND

;------ SHIFT MAKE FOUND, DETERMINE SET OR TOGGLE

        CMP     AH,SCROLL_SHIFT
        JAE     K18                     ; IF SCROLL SHIFT OR ABOVE, TOGGLE KEY

;------ PLAIN SHIFT KEY, SET SHIFT ON

        OR      KB_FLAG,AH              ; TURN ON SHIFT BIT
        JMP     K26                     ; INTERRUPT_RETURN

;------ TOGGLED SHIFT KEY, TEST FOR 1ST MAKE OR NOT

K18:                                    ; SHIFT-TOGGLE
        TEST    KB_FLAG, CTL_SHIFT      ; CHECK CTL SHIFT STATE
        JNZ     K25                     ; JUMP IF CTL STATE
        CMP     AL, INS_KEY             ; CHECK FOR INSERT KEY
        JNZ     K22                     ; JUMP IF NOT INSERT KEY
        TEST    KB_FLAG, ALT_SHIFT      ; CHECK FOR ALTERNATE SHIFT
        JZ      K19                     ; JUMP IF NOT ALTERNATE SHIFT
        JMP     NEAR PTR K25            ; JUMP IF ALTERNATE SHIFT
K19:    TEST    KB_FLAG, NUM_STATE      ; CHECK FOR BASE STATE
        JNZ     K21                     ; JUMP IF NUM LOCK IS ON
        TEST    KB_FLAG, LEFT_SHIFT+RIGHT_SHIFT
        JZ      K22                     ; JUMP IF BASE STATE

K20:                                    ; NUMERIC ZERO, NOT INSERT KEY
        MOV     AX, 5230H               ; PUT OUT AN ASCII ZERO
        JMP     K57                     ; BUFFER_FILL
K21:                                    ; MIGHT BE NUMERIC
        TEST    KB_FLAG, LEFT_SHIFT+RIGHT_SHIFT
        JZ      K20                     ; JUMP NUMERIC, NOT INSERT

K22:                                    ; SHIFT TOGGLE KEY HIT; PROCESS IT
        TEST    AH,KB_FLAG_1            ; IS KEY ALREADY DEPRESSED
        JNZ     K26                     ; JUMP IF KEY ALREDY DEPRESSED
        OR      KB_FLAG_1,AH            ; INDICATE THAT THE KEY IS DEPRESSED
        XOR     KB_FLAG,AH              ; TOGGLE THE SHIFT STATE
        CMP     AL,INS_KEY              ; TEST FOR 1ST MAKE OF INSERT KEY
        JNE     K26                     ; JUMP IF NOT INSERT KEY

        MOV     AX,INS_KEY*256          ; SET SCAN CODE INTO AH, 0 INTO AL
        JMP     K57                     ; PUT INTO OUTPUT BUFFER

;------ BREAK SHIFT FOUND

K23:                                    ; BREAK-SHIFT-FOUND
        CMP     AH,SCROLL_SHIFT         ; IS THIS A TOGGLE KEY
        JAE     K24                     ; YES, HANDLE BREAK TOGGLE
        NOT     AH                      ; INVERT MASK
        AND     KB_FLAG,AH              ; TURN OFF SHIFT BIT
        CMP     AL,ALT_KEY+80H          ; IS THIS ALTERNATE SHIFT RELEASE
        JNE     K26                     ; INTERRUPT_RETURN

;------ ALTERNATE SHIFT KEY RELEASED, GET THE VALUE INTO BUFFER

        MOV     AL,ALT_INPUT
        MOV     AH,0                    ; SCAN CODE OF 0
        MOV     ALT_INPUT,AH            ; ZERO OUT THE FIELD
        CMP     AL,0                    ; WAS THE INPUT=0
        JE      K26                     ; INTERRUPT_RETURN
        JMP     K58                     ; IT WASN'T, SO PUT IN BUFFER

K24:                                    ; BREAK-TOGGLE
        NOT     AH                      ; INVERT MASK
        AND     KB_FLAG_1,AH            ; INDICATE NO LONGER DEPRESSED
        JMP     SHORT K26               ; INTERRUPT_RETURN

;------ TEST FOR HOLD STATE

K25:                                    ; NO-SHIFT-FOUND
        CMP     AL,80H                  ; TEST FOR BREAK KEY
        JAE     K26                     ; NOTHING FOR BREAK CHARS FROM HERE ON
        TEST    KB_FLAG_1,HOLD_STATE    ; ARE WE IN HOLD STATE
        JZ      K28                     ; BRANCH AROUND TEST IF NOT
        CMP     AL,NUM_KEY
        JE      K26                     ; CAN'T END HOLD ON NUM_LOCK
        AND     KB_FLAG_1,NOT HOLD_STATE ; TURN OFF THE HOLD STATE BIT

K26:                                    ; INTERRUPT-RETURN
        CLI                             ; TURN OFF INTERRUPTS
        MOV     AL,EOI                  ; END OF INTERRUPT COMMAND
        OUT     020H,AL                 ; SEND COMMAND TO INTERRUPT CONTROL PORT
K27:                                    ; INTERRUPT-RETURN-NO-EOI
	POP	ES
	POP	DS
	POP	DI
        POP	SI
	POP	DX
	POP	CX
	POP     BX
        POP	AX                      ; RESTORE STATE
        IRET                            ; RETURN, INTERRUPTS BACK ON WITH FLAG CHANGE

;------ NOT IN HOLD STATE, TEST FOR SPECIAL CHARS

K28:                                    ; NO-HOLD-STATE
        TEST    KB_FLAG,ALT_SHIFT       ; ARE WE IN ALTERNATE SHIFT
        JNZ     K29                     ; JUMP IF ALTERNATE SHIFT
        JMP     K38                     ; JUMP IF NOT ALTERNATE

; ------ TEST FOR RESET KEY SENTENCE (CTL ALT DEL)

K29:                                    ; TEST-RESET
        TEST    KB_FLAG,CTL_SHIFT       ; ARE WE IN CONTROL SHIFT ALSO
        JZ      K31                     ; NO_RESET
        CMP     AL,DEL_KEY              ; SHIFT STATE IS THERE, TEST KEY
        JNE     K31                     ; NO_RESET

;------ CTL-ALT-DEL HAS BEEN FOUND, DO I/O CLEANUP

        MOV     RESET_FLAG, 1234H       ; SET FLAG FOR RESET FACTION
        JMP     RESET                   ; JUMP TO POWER ON DIAGNOSTICS

;------ ALT-INPUT-TABLE
K30     LABEL   BYTE
        DB      82,79,80,81,75,76,77
        DB      71,72,73                ; 10 NUMBERS ON KEYPAD
;------ SUPER-SHIFT-TABLE
        DB      16,17,18,19,20,21,22,23 ; A-Z TYPEWRITER CHARS
        DB      24,25,30,31,32,33,34,35
        DB      36,37,38,44,45,46,47,48
        DB      49,50        

;------ IN ALTERNATE SHIFT, RESET NOT FOUND

K31:                                    ; NO-RESET
        CMP     AL,57                   ; TEST FOR SPACE KEY
        JNE     K32                     ; NOT THERE
        MOV     AL,' '                  ; SET SPACE CHAR
        JMP     K57                     ; BUFFER_FILL

;------ LOOK FOR KEY PAD ENTRY

K32:                                    ; ALT-KEY-PAD
        MOV     DI,OFFSET K30           ; ALT-INPUT-TABLE
        MOV     CX,10                   ; LOOK FOR ENTRY USING KEYPAD
        REPNE   SCASB                   ; LOOK FOR MATCH

        JNE     K33                     ; NO_ALT_KEYPAD
        SUB     DI,OFFSET K30+1         ; DI NOW HAS ENTRY VALUE
        MOV     AL,ALT_INPUT            ; GET THE CURRENT BYTE
        MOV     AH,10                   ; MULTIPLY BY 10
        MUL     AH
        ADD     AX,DI                   ; ADD IN THE LATEST ENTRY
        MOV     ALT_INPUT,AL            ; STORE IT ANAY
        JMP     K26                     ; THROW AWAY THAT KETSTROKE

; ----- LOOK FOR SUPERSHIFT ENTRY

K33:                                    ; NO-ALT-KEYPAD
        MOV     ALT_INPUT,0             ; ZERO ANY PREVIOUS ENTRY INTO INPUT
        MOV     CX,26                   ; DI, ES ALREADY POINTING
        REPNE   SCASB                   ; LOOK FOR MATCH IN ALPHABET

        JNE     K34                     ; NOT FOUND, FUNCTION KEY OR OTHER
        MOV     AL,0                    ; ASCII CODE OF ZERO
        JMP     K57                     ; PUT IT IN THE BUFFER

;------ LOOK FOR TOP ROW OF ALTERNATE SHIFT

K34:                                    ; ALT-TOP-ROW
        CMP     AL,2                    ; KEY WITH '1' ON IT
        JB      K35                     ; NOT ONE OF INTERESTING KEYS
        CMP     AL,14                   ; IS IT IN THE REGION
        JAE     K35                     ; ALT-FUNCTION
        ADD     AH,118                  ; CONVERT PSUEDO SCAN CODE TO RANGE
        MOV     AL,0                    ; INDICATE AS SUCH
        JMP     K57                     ; BUFFER_FILL

;------ TRANSLATE ALTERNATE SHIFT PSEUDO SCAN CODES

K35:                                    ; ALT-FUNCTION
        CMP     AL,59                   ; TEST FOR IN TABLE
        JAE     K37                     ; ALT-CONTINUE
K36:                                    ; CLOSE-RETURN
        JMP     K26                     ; IGNORE THE KEY
K37:                                    ; ALT-CONTINUE
        CMP     AL,71                   ; IN KEYPAD REGION
        JAE     K36                     ; IF SO, IGNORE
        MOV     BX,OFFSET K13           ; ALT SHIFT PSEUDO SCAN TABLE
        JMP     K63                     ; TRANSLATE THAT

;------ NOT IN ALTERNATE SHIFT

K38:                                    ; NOT-ALT-SHIFT
        TEST    KB_FLAG,CTL_SHIFT       ; ARE WE IN CONTROL SHIFT
        JZ      K44                     ; NOT-CTL-SHIFT

;------ CONTROL SHIFT, TEST SPECIAL CHARACTERS
;------ TEST FOR BREAK AND PAUSE KEYS

        CMP     AL,SCROLL_KEY           ; TEST FOR BREAK
        JNE     K39                     ; NO-BREAK
        MOV     BX,OFFSET KB_BUFFER     ; RESET BUFFER TO EMPTY
        MOV     BUFFER_HEAD,BX          ;
        MOV     BUFFER_TAIL,BX          ; 
        MOV     BIOS_BREAK,80H          ; TURN ON BIOS_BREAK BIT
        INT     1BH                     ; BREAK INTERRUPT VECTOR
        MOV     AX,0                    ; PUT OUT DUMMY CHARACTER
        JMP     K57                     ; BUFFER_FILL

K39:                                    ; NO-BREAK
        CMP     AL,NUM_KEY              ; LOOK FOR PAUSE KEY
        JNE     K41                     ; NO-PAUSE
        OR      KB_FLAG_1,HOLD_STATE    ; TURN ON THE HOLD FLAG
        MOV     AL,EOI                  ; END OF INTERRUPT TO CONTROL PORT
        OUT     020H,AL                 ; ALLOW FURTHER KETSTROKE INTS

;------ DURING PAUSE INTERVAL, TURN CRT BACK ON

	CMP	CRT_MODE,7		; IS THIS BLACK AND WHITE CARD
        JE      K40                     ; YES, NOTHING TO DO
        MOV     DX,03D8H                ; PORT FOR COLOR CARD
        MOV     AL,CRT_MODE_SET         ; GET THE VALUE OF THE CURRENT MODE
        OUT     DX,AL                   ; SET THE CRT MODE, SO THAT CRT IS ON
K40:                                    ; PAUSE-LOOP
        TEST    KB_FLAG_1,HOLD_STATE
        JNZ     K40                     ; LOOP UNTIL FLAG TURNED OFF
        JMP     K27                     ; INTERRUPT_RETURN_NO_EOI
K41:                                    ; NO-PAUSE

;------ TEST SPECIAL CASE KEY 55

        CMP     AL,55
        JNE     K42                     ; NOT-KEY-55
        MOV     AX,114*256              ; START/STOP PRINTING SWITCH
        JMP     K57                     ; BUFFER_FILL

;------ SET UP TO TRANSLATE CONTROL SHIFT

K42:                                    ; NOT-KEY-55
        MOV     BX,OFFSET K8            ; SET UP TO TRANSLATE CTL
        CMP     AL,59                   ; IS IT IN TABLE
        JAE     K43                     ; CTL-TABLE-TRANSLATE
        JMP     NEAR PTR K56            ; YES, GO TRANSLATE CHAR
K43:                                    ; CTL-TABLE-TRANSLATE
        MOV     BX,OFFSET K9            ; CTL TABLE SCAN
        JMP     K63                     ; TRANSLATE_SCAN

;------ NOT IN CONRTOL SHIFT

K44:                                    ; NOT-CTL-SHIFT

        CMP     AL,71                   ; TEST FOR KEYPAD REGION
        JAE     K48                     ; HANDLE KEYPAD REGION
        TEST    KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT
        JZ      K54                     ; TEST FOR SHIFT STATE

;------ UPPER CASE, HANDLE SPECIAL CASES

        CMP     AL,15                   ; BACK TAB KEY
        JNE     K45                     ; NOT-BACK-TAB
        MOV     AX,15*256               ; SET PSEUDO SCAN CODE
        JMP     NEAR PTR K57            ; BUFFER_FILL
        
K45:                                    ; NOT-BACK-TAB
        CMP     AL,55                   ; PRINT SCEEN KEY
        JNE     K46                     ; NOT-PRINT-SCREEN

;------ ISSUE INTERRUPT TO INDICATE PRINT SCREEN FUNCTION

        MOV     AL,EOI                  ; END OF CURRENT INTERRUPT
        OUT     020H,AL                 ;  SO FURTHER THINGS CAN HAPPEN
        INT     5H                      ; ISSUE PRINT SCREEN INTERRUPT
        JMP     K27                     ; G0 BACK WITHOUT EOI OCCURRING

K46:                                    ; NOT-PRINT-SCREEN
        CMP     AL,59                   ; FUNCTION KEYS
        JB      K47                     ; NOT-UPPER-FUNCTION
        MOV     BX,OFFSET K12           ; UPPER CASE PSEUDO SCAN CODES

        JMP     K63                     ; TRANSLATE_SCAN

K47:                                    ; NOT-UPPER-FUNCTION
        MOV     BX,OFFSET K11           ; POINT TO UPPER CASE TABLE
        JMP     SHORT K56               ; OK, TRANSLATE THE CHAR

;------ KEYPAD KEYS, MUST TEST NUM LOCK FOR DETERMINATION

K48:                                    ; KEYPAD-REGION
        TEST    KB_FLAG,NUM_STATE       ; ARE WE IN NUM_LOCK
        JNZ     K52                     ; TEST FOR SURE
        TEST    KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT ; ARE WE IN SHIFT STATE
        JNZ     K53                     ; IF SHIFTED, REALLY NUM STATE

;------ BASE CASE FOR KEYPAD

K49:                                    ; BASE-CASE

        CMP     AL,74                   ; SPECIAL CASE FOR A COUPLE OF KEYS
        JE      K50                     ; MINUS
        CMP     AL,78
        JE      K51
        SUB     AL,71                   ; CONVERT ORIGIN
        MOV     BX,OFFSET K15           ; BASE CASE TABLE
        JMP     SHORT K64               ; CONVERT TO PSEUDO SCAN

K50:    MOV     AX,74*256+'-'           ; MINUS
        JMP     SHORT K57               ; BUFFER_FILL

K51:    MOV     AX,78*256+'+'           ; PLUS
        JMP     SHORT K57               ; BUFFER_FILL

;------ MIGHT BE NUM LOCK, TEST SHIFT STATUS

K52:                                    ;ALMOST-NUM-STATE
        TEST    KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT
        JNZ     K49                     ; SHIFTED TEMP OUT OF NUM STATE

K53:                                    ; REALLY_NUM_STATE
        SUB     AL,70                   ; CONVERT ORIGIN
        MOV     BX,OFFSET K14           ; NUM STATE TABLE
	JMP	SHORT K56		; TRANSLATE_CHAR

;------ PLAIN OLD LOWER CASE

K54:                                    ; NOT-SHIFT
        CMP     AL,59                   ; TEST FOR FUNCTION KETS
        JB      K55                     ; NOT-LOWER-FUNCTION
        MOV     AL,0                    ; SCAN CODE IN AH ALREADY
        JMP     SHORT K57               ; BUFFER_FILL

K55:                                    ; NOT-LOWER-FUNCTION
        MOV    BX,OFFSET K10            ; LC TABLE

;------TRANSLATE THE CHARACTER

K56:                                    ; TRANSLATE-CHAR
        DEC 	AL                      ; CONVERT ORIGIN
        XLAT 	CS:K11                  ; CONVERT THE SCAN CODE TO ASCII

;------ PUT CHARACTER INTO BUFFER

K57:                                    ; BUFFER-FILL
        CMP     AL,-1                   ; IS THIS AN IGNORE CHAR
        JE      K59                     ; YES, DO NOTHING WITH IT
        CMP     AH,-1                   ; LOOK FOR -1 PSEUDO SCAN
        JE      K59                     ; NEAR_INTERRUPT_RETURN

;------ HANDLE THE CAPS LOCK PROBLEM

K58:                                    ; BUFFER-FILL-NOTEST
        TEST    KB_FLAG,CAPS_STATE      ; ARE WE IN CAPS LOCK STATE
        JZ      K61                     ; SKIP IF NOT

;------ IN CAPS LOCK STATE

        TEST    KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT    ; TEST FOR SHIFT STATE
        JZ      K60                     ; IF NOT SHIFT, CONVERT LOWER TO UPPER

;------ CONVERT ANY UPPER CASE TO LOWER CASE

        CMP     AL,'A'                  ; FIND OUT IF ALPHABETIC
        JB      K61                     ; NOT_CAPS_STATE
        CMP     AL,'Z'
        JA      K61                     ; NOT_CAPS_STATE
        ADD     AL,'a'-'A'              ; CONVERT TO LOWER CASE
        JMP     SHORT K61               ; NOT_CAPS_STATE

K59:                                    ; NEAR-INTERRUPT-RETURN
        JMP     K26                     ; INTERRUPT_RETURN

;------ CONVVERT ANY LOWER CASE TO UPPER CASE

K60:                                    ; LOWER-TO-UPPER
        CMP     AL,'a'                  ; FIND OUT IF ALPHABETIC
        JB      K61                     ; NOT_CAPS_STATE
        CMP     AL,'z'
        JA      K61                     ; NOT_CAPS_STATE
        SUB     AL,'a'-'A'              ; CONVERT TO UPPER CASE

K61:                                    ; NOT-CAPS-STATE
        MOV     BX,BUFFER_TAIL          ; GET THE END POINTER TO THE BUFFER
        MOV     SI,BX                   ; SAVE THE VALUE
        CALL    K4                      ; ADVANCE THE TAIL
        CMP     BX,BUFFER_HEAD          ; HAS THE BUFFER WRAPPED AROUND
        JE      K62                     ; BUFFER_FULL_BEEP
        MOV     [SI],AX                 ; STORE THE VALUE
        MOV     BUFFER_TAIL,BX          ; MOVE THE POINTER UP
        JMP     K26                     ; INTERRUPT_RETURN

;------ BUFFER IS FULL, SOUND THE BEEPER

K62:                                    ; BUFFER-FULL-BEEP
        CALL    ERROR_BEEP
        JMP     NEAR PTR K26            ; INTERRUPT_RETURN

;------ TRANSLATE SCAN FOR PSEUDO SCAN CODES

K63:                                    ; TRANSLATE-SCAN
        SUB     AL,59                   ; CONVERT ORIGIN TO FUNCTION KEYS
K64:                                    ; TRANSLATE-SCAN-ORGD
        XLAT    CS:K9                   ; CTL TABLE SCAN
        MOV     AH,AL                   ; PUT VALUE INTO AH
        MOV     AL,0                    ; ZERO ASCII CODE
        JMP     K57                     ; PUT IT INTO THE BUFFER
KB_INT  ENDP

ERROR_BEEP      PROC    NEAR
        PUSH    AX                      ; SAVE REGISTERS
	PUSH	BX
	PUSH	CX
        MOV     BX,0C0H                 ; NUMBER OF CYCLES FOR 1/8 SECOND TONE
        IN      AL,KB_CTL               ; GET CONTROL INFORMATION
        PUSH    AX                      ; SAVE
K65:                                    ; BEEP-CYCLE
        AND     AL,0FCH                 ; TURN OFF TIMER GATE AND SPEAKER DATA
        OUT     KB_CTL,AL               ; OUTPUT TO CONTROL
        MOV     CX,48H                  ; HALF CYCLE TIME FOR TONE
K66:    LOOP    K66                     ; SPEAKER OFF
        OR      AL,2                    ; TURN ON SPEAKER BIT
        OUT     KB_CTL,AL               ; OUTPUT TO CONTROL
        MOV     CX,48H                  ; SET UP COUNT
K67:    LOOP    K67                     ; ANOTHER HALF CYCLE
        DEC     BX                      ; TOTAL TIME COUNT
        JNZ     K65                     ; DO ANOTHER CYCLE
        POP     AX                      ; RECOVER CONTROL
        OUT     KB_CTL,AL               ; OUTPUT THE CONTROL
        POP     CX                      ; RECOVER REGISTERS
        POP     BX
	POP	AX
	RET
ERROR_BEEP      ENDP


;-- INT 13 ----------------------------------
;DISKETTE I/O
;       THIS INTERFACE PROVIDES ACCESS TO THE 5 1/4" DISKETTE DRIVES
;INPUT
;       (AH)=0  RESET DISKETTE SYSTEM
;               HARD RESET TO NEC, PREPARE COMMAND, RECAL REQD ON ALL DRIVES
;       (AH)=1  READ THE STATUS OF THE SYSTEM INTO (AL)
;               DISKETTE_STATUS FROM LAST OP'N IS USED
;       REGISTERS FOR READ/WRITE/VERIFY/FORMAT
;       (DL) - DRIVE NUMBER (0-3 ALLOWED, VALUE CHECKED)
;       (DH) - HEAD NUMBER (0-1 ALLOWED, NOT VALUE CHECKED)
;       (CH) - TRACK NUMBER (0-39, NOT VALUE CHECKED)
;       (CL) - SECTOR NUMBER (1-8, NOT VALUE CHECKED)
;       (AL) - NUMBER OF SECTORS ( MAX = 8, NOT VALUE CHECKED)
;
;       (ES:BX) - ADDRESS OF BUFFER (NOT REQUIRED FOR VERIFY)
;
;       (AH)=2 READ THE DESIRED SECTORS INTO MEMORY
;       (AH)=3 WRITE THE DESIRED SECTORS FROM MEMORY
;       (AH)=4 VERIFY THE DESIRED SECTORS
;       (AH)=5 FORMAT THE DESIRED TRACKS
;              FOR THE FORMAT OPERATION, THE BUFFER POINTER (ES,BX) MUST
;              POINT TO THE COLLECTION OF DESIRED ADDRESS FIELDS FOR THE
;              TRACK. EACH FIELD IS COMPOSED OF 4 BYTES, (C,H,R,N), WHERE
;              C = TRACK NUMBER, H=HEAD NUMBER, R = SECTOR NUMBER, N= NUMBER
;              OF BYTES PER SECTOR (00=128, 01=256, 02=512, 03=1024,)
;              THERE MUST BE ONE ENTRY FOR EVERY SECTOR ON THE TRACK.
;              THIS INFORMATION IS USED TO FIND THE REQUESTED SECTOR DURING
;              READ/WRITE ACCESS.
; DATA VARIABLE -- DISK_POINTER
; 	DOUBLE WORD POINTER TO THE CURRENT SET OF DISKETTE PARAMETERS
; OUTPUT
;       AH = STATUS OF OPERATION
;               STATUS BITS ARE DEFINED IN THE EQUATES FOR DISKETTE_STATUS
;               VARIABLE IN THE DATA SEGMENT OF THIS MODULE
;       CY = 0 SUCCESSFUL OPERATION (AH=0 ON RETURN)
;       CY = 1 FAILED OPERATION (AH HAS ERROR REASON)
;       FOR READ/WRITE/VERIFY
;               DS,BX,DX,CH,CL PRESERVED
;               AL = NUMBER OF SECTORS ACTUALLY READ
;               ***** AL MAY NOT BE CORRECT IF TIME OUT ERROR OCCURS
;       NOTE: IF AN ERROR IS REPORTED BY THE DISKETTE CODE, THE APPROPRIATE
;		ACTION IS TO RESET THE DISKETTE, THEN RETRY THE OPERATION.
;		ON READ ACCESSES, NO MOTOR START DELAY IS TAKEN, SO THAT
;		THREE RETRIES ARE REQUIRED ON READS TO ENSURE THAT THE
;		PROBLEM IS NOT DUE TO MOTOR START-UP.
;--------------------------------------------
	ASSUME	CS:CODE,DS:DATA,ES:DATA
DISKETTE_IO     PROC    FAR
	STI                             ; INTERRUPTS BACK ON
        PUSH	BX                      ; SAVE ADDRESS
	PUSH	CX
	PUSH	DS                      ; SAVE SEGMENT REGISTER VALUE
	PUSH	SI                      ; SAVE ALL REGISTERS DURING OPERATION
	PUSH	DI
	PUSH	BP
	PUSH	DX
	MOV	BP,SP                   ; SET UP POINTER TO HEAD PARM
	MOV	SI,DATA
	MOV	DS,SI                   ; SET DATA REGION
	CALL	J1                      ; CALL THE REST TO ENSURE DS RESTORED
	MOV	BX,4                    ; GET THE MOTOR WAIT PARAMETER
	CALL	GET_PARM
	MOV	MOTOR_COUNT,AH          ; SET THE TIMER COST FOR THE MOTOR
	MOV	AH,DISKETTE_STATUS      ; GET STATUS OF OPERATION
	CMP	AH,1                    ; SET THE CARRY FLAG TO INDICATE
	CMC                             ;  SUCCESS OR FAILURE
	POP	DX                      ; RESTORE ALL REGISTERS
	POP	BP
	POP	DI
	POP	SI
	POP	DS
	POP	CX
	POP	BX                      ; RECOVER ADDRESS
	RET	2                       ; THROW AWAY SAVED FLAGS
DISKETTE_IO     ENDP
J1      PROC    NEAR
        MOV     DH,AL                   ; SAVE # SECTORS IN DH
        AND     MOTOR_STATUS,07FH       ; INDICATE A READ OPERATION
        OR      AH,AH                   ; AH=0
        JZ      DISK_RESET
        DEC     AH                      ; AH=1
        JZ      DISK_STATUS
        MOV     DISKETTE_STATUS,0       ; RESET THE STATUS INDICATOR
        CMP     DL,4                    ; TEST FOR DRIVE IN 0-3 RANGE
        JAE     J3                      ; ERROR IF ABOVE
        DEC     AH                      ; AH=2
        JZ	DISK_READ
        DEC     AH                      ; AH=3
        JNZ     J2                      ; TEST_DISK_VERF

        JMP     DISK_WRITE
J2:                                     ; TEST_DISK_VERF

        DEC     AH                      ; AH=4
        JZ      DISK_VERF
        DEC     AH                      ; AH=5
        JZ      DISK_FORMAT

J3:                                     ; BAD_COMMAND
        MOV     DISKETTE_STATUS,BAD_CMD ; ERROR CODE, NO SECTORS TRANSFERRED
        RET                             ; UNDEFINED OPERATION
J1      ENDP

;------ RESET THE DISKETTE SYSTEM

DISK_RESET      PROC    NEAR
        MOV     DX,03F2H                ; ADAPTER CONTROL PORT
        CLI                             ; NO INTERRUPTS
        MOV     AL,MOTOR_STATUS         ; WHICH MOTOR IS ON
        MOV     CL,4                    ; SHIFT COUNT
        SAL     AL,CL                   ; MOVE MOTOR VALUE TO HIGH NYBBLE
        TEST    AL, 20H                 ; SELECT CORRESPONDING DRIVE
        JNZ     J5                      ; JUMP IF MOTOR ONE IS ON
        TEST    AL, 40H
        JNZ     J4                      ; JUMP IF MOTOR TWO IS ON
        TEST    AL, 80H
        JZ      J6                      ; JUMP IF MOTOR ZERO IS ON
	INC	AL
J4:     INC     AL
J5:     INC     AL
J6:     OR      AL,8                    ; TURN ON INTERRUPT ENABLE
        OUT     DX,AL                   ; RESET THE ADAPTER
        MOV     SEEK_STATUS,0           ; SET RECAL REQUIRED ON ALL DRIVES
        MOV     DISKETTE_STATUS,0       ; SET OK STATUS FOR DISKETTE
        OR      AL,4                    ; TURN OFF RESET
        OUT     DX,AL                   ; TURN OFF THE RESET
        STI                             ; REENABLE THE INTERRUPTS
        CALL    CHK_STAT_2              ; DO SENSE INTERRUPT STATUS FOLLOWING RESET
        MOV     AL,NEC_STATUS           ; IGNORE ERROR RETURN AND DO OWN RESET
        CMP     AL,0C0H                 ; TEST FOR DRIVE READY TRANSITION
        JZ      J7                      ; EVERYTHING OK
        OR      DISKETTE_STATUS,BAD_NEC ; SET ERROR CODE
        JMP     SHORT J8                ; RESET_RET

;------ SEND SPECIFY COMMAND TO NEC

J7:                                     ; DRIVE_READY
        MOV     AH,03H                  ; SPECIFY COMMAND
        CALL    NEC_OUTPUT              ; OUTPUT THE COMMAND
        MOV     BX,1                    ; FIRST BYTE PARM IN BLOCK
        CALL    GET_PARM                ;  TO THE NEC CONTROLLER
        MOV     BX,3                    ; SECOND BYTE PARM IN BLOCK
        CALL    GET_PARM                ;  TO THE NEC CONTROLLER
J8:                                     ; RESET_RET
        RET                             ; RETURN TO CALLER
DISK_RESET      ENDP

;------ DISKETTE STATUS ROUTINE
DISK_STATUS     PROC    NEAR
        MOV     AL,DISKETTE_STATUS
        RET
DISK_STATUS     ENDP

;------ DISKETTE READ

DISK_READ       PROC    NEAR
        MOV     AL,046H                 ; READ COMMAND FOR DMA
J9:                                     ; DISK_READ_CONT
        CALL    DMA_SETUP               ; SET UP THE DMA
        MOV     AH,066H                 ; SET UP READ COMMAND FOR NEC CONTROLLER
        JMP     SHORT RW_OPN            ; GO DO THE OPERATION
DISK_READ       ENDP

;------ DISKETTE VERIFY

DISK_VERF       PROC    NEAR
        MOV     AL,042H                 ; VERIFY COMMAND FOR DMA
        JMP     J9                      ; DO AS IF DISK READ
DISK_VERF       ENDP

;------ DISKETTE FORMAT

DISK_FORMAT     PROC    NEAR
        OR      MOTOR_STATUS,80H        ; INDICATE WRITE OPERATION
        MOV     AL,04AH                 ; WILL WRITE TO THE DISKETTE
        CALL    DMA_SETUP               ; SET UP THE DMA
        MOV     AH,04DH                 ; ESTABLISH THE FORMAT COMMAND
        JMP     SHORT RW_OPN            ; DO THE OPERATION
J10:                                    ; CONTINUATION OF RW_OPN FOR FMT
        MOV     BX,7                    ; GET THE
        CALL    GET_PARM                ;  BYTES/SECTOR VALUE TO NEC
        MOV     BX,9                    ; GET THE
        CALL    GET_PARM                ;  SECTORS/TRACK VALUE TO NEC
        MOV     BX,15                   ; GET THE
        CALL    GET_PARM                ;  GAP LENGTH VALUE TO NEC
        MOV     BX,17                   ; GET THE FILLER BYTE
        JMP     J16                     ;  TO THE CONTROLLER
DISK_FORMAT     ENDP

;------ DISKETTE WRITE ROUTINE

DISK_WRITE	PROC    NEAR
        OR	MOTOR_STATUS,80H        ; INDICATE WRITE OPERATION
        MOV	AL,04AH                 ; DMA WRITE COMMAND
	CALL	DMA_SETUP
        MOV	AH,045H                 ; NEC COMMAND TO WRITE TO DISKETTE
DISK_WRITE	ENDP
;----- ALLOW WRITE ROUTINE TO FALL INTO RW_OPN
;---------------------------------------
; RW_OPN
;	THIS ROUTINE PERFORMS THE READ/WRITE/VERIFY OPERATION
;---------------------------------------
RW_OPN  PROC    NEAR
        JNC     J11                     ; TEST FOR DMA ERROR
        MOV     DISKETTE_STATUS,DMA_BOUNDARY ; SET ERROR
        MOV     AL,0                    ; NO SECTORS TRANSFERRED
        RET                             ; RETURN TO MAIN ROUTINE
J11:                                    ; DO_RW_OPN
        PUSH    AX                      ; SAVE THE COMMAND

;------ TURN ON THE MOTOR AND SELECT THE DRIVE

        PUSH    CX                      ; SAVE THE T/S PARMS
        MOV     CL,DL                   ; GET DRIVE NUMBER AS SHIFT COUNT
        MOV     AL,1                    ; MASK FOR DETERMINING MOTOR BIT
        SAL     AL,CL                   ; SHIFT THE MASK BIT
        CLI                             ; NO INTERRUPTS WHILE DETERMINING MOTOR STATUS
        MOV     MOTOR_COUNT,0FFH        ; SET LARGE COUNT DURING OPERATION
        TEST    AL,MOTOR_STATUS         ; TEST THAT MOTOR FOR OPERATING
        JNZ     J14                     ; IF RUNNING, SKIP THE WAIT
        AND     MOTOR_STATUS,0F0H       ; TURN OFF ALL MOTOR BITS
        OR      MOTOR_STATUS,AL         ; TURN ON THE CURRENT MOTOR
        STI                             ; INTERRUPTS BACK ON
        MOV     AL,10H                  ; MASK BIT
        SAL     AL,CL                   ; DEVELOP BIT MASK FOR MOTOR ENABLE
        OR      AL,DL                   ; GET DRIVE SELECT BITS IN
        OR      AL,0CH                  ; NO RESET, ENABLE DMA/INT
        PUSH    DX                      ; SAVE REG
        MOV     DX,03F2H                ; CONTROL PORT ADDRESS
        OUT     DX,AL
        POP     DX                      ; RECOVER REGISTERS

;------ WAIT FOR MOTOR IF WRITE OPERATION

	TEST    MOTOR_STATUS,80H	; IS THIS A WRITE
	JZ	J14     		; NO, CONTINUE WITHOUT WAIT
	MOV	BX,20  			; GET THE MOTOR WAIT
	CALL	GET_PARM		;  PARAMETER
	OR      AH,AH   		; TEST FOR NO WAIT
J12:					; TEST_WAIT_TIME
	JZ      J14     		; EXIT WITH TIME EXPIRED
	SUB     CX,CX   		; SET UP 1/8 SECOND LOOP TIME
J13:	LOOP	J13			; WAIT FOR THE REQUIRED TIME
	DEC	AH			; DECREMENT TIME VALUE
        JMP	J12			; ARE WE DONE YET

J14:					; MOTOR_RUNNING
	STI				; INTERRUPTS BACK ON FOR BYPASS WAIT
	POP	CX

;------ DO THE SEEK OPERATION

	CALL	SEEK			; MOVE TO CORRECT TRACK
	POP	AX			; RECOVER COMMAND
	MOV	BH,AH      		; SAVE COMMAND IN BH
	MOV 	DH,0       		; SET NO SECTORS READ IN CASE OF ERROR
	JC 	J17  			; IF ERROR, THEN EXIT AFTER MOTOR OFF
	MOV	SI, OFFSET J17		; DUMMY RETURN ON STACK FOR NEC_OUTPUT
	PUSH 	SI			;  SO THAT IT WILL RETURN TO MOTOR OFF LOCATIOH

;------ SEND OUT THE PARAMETERS TO THE CONTROLLER

	CALL 	NEC_OUTPUT		; OUTPUT THE OPERATION COMMAND
        MOV 	AH,[BP+1]       	; GET THE CURRENT HEAD NUMBER
	SAL 	AH,1       		; MOVE IT TO BIT 2
	SAL 	AH,1
	AND 	AH,4       		; ISOLATE THAT BIT
	OR 	AH,DL			; OR IN THE DRIVE NUMBER
	CALL 	NEC_OUTPUT

;------ TEST FOR FORMAT COMMAND

	CMP 	BH,04DH    		; IS THIS A FORMAT OPERATION
	JNE 	J15			; NO, CONTINUE WITH R/W/V
        JMP 	J10       		; IF SO, HANDLE SPECIAL

J15:	MOV 	AH,CH			; CYLINDER NUMBER
	CALL 	NEC_OUTPUT
	MOV 	AH,[BP+1]	     	; HEAD NUMBER FROM STACK
	CALL 	NEC_OUTPUT
	MOV 	AH,CL      		; SECTOR NUMBER
	CALL 	NEC_OUTPUT
	MOV 	BX,7			; BYTES/SECTOR PARM FROM BLOCK
	CALL 	GET_PARM		;  TO THE NEC
	MOV 	BX,9			; EOT PARM FROM BLOCK
	CALL 	GET_PARM   		;  TO THE NEC
	MOV 	BX,11      		; GAP LENGTH PARM FROM BLOCK
	CALL 	GET_PARM		;  TO THE NEC
	MOV 	BX,13      		; DTL PARM FROM BLOCK
J16:					; RW_OPN FINISH
	CALL	GET_PARM		;  TO THE NEC
	POP 	SI			; CAN NOW DISCARD THAT DUMMY RETURN ADDRE5S

;------ LET THE OPERATION HAPPEN

	CALL 	WAIT_INT		; WAIT FOR THE INTERRUPT
J17:					; MOTOR_OFF
	JC 	J21			; LOOK FOR ERROR
        CALL 	RESULTS    		; GET THE NEC STATUS
        JC	J20  			; LOOK FOR ERROR

;------ CHECK THE RESULTS RETURNED BY THE CONTROLLER

	CLD 				; SET THE CORRECT DIRECTION
	MOV 	SI,OFFSET NEC_STATUS 	; POINT TO STATUS FIELD
	LODS 	NEC_STATUS 		; GET ST0
        AND 	AL,0C0H    		; TEST FOR NORMAL TERMINATION
	JZ 	J22 			; OPN_OK
	CMP 	AL,040H 		; TEST FOR ABNORMAL TERMINATION
	JNZ 	J18 			; NOT ABNORMAL, BAD NEC

;------ ABNORMAL TERMINATION, FIND OUT WHY

	LODS	NEC_STATUS		; GET ST1
	SAL	AL,1   			; TEST FOR EOT FOUND
	MOV	AH,RECORD_NOT_FND
	JC	J19			; RW_FAIL
	SAL	AL,1
	SAL	AL,1   			; TEST FOR CRC ERROR
	MOV	AH,BAD_CRC
	JC	J19			; RW_FAIL
	SAL	AL,1   			; TEST FOR DMA OVERRUN
	MOV	AH,BAD_DMA
	JC	J19			; RW_FAIL
	SAL	AL,1
	SAL	AL,1   			; TEST FOR RECORD NOT FOUND
	MOV	AH,RECORD_NOT_FND
	JC	J19			; RW_FAIL
	SAL	AL,1
	MOV	AH,WRITE_PROTECT	; TEST FOR WRITE PROTECT
	JC	J19			; RW_FAIL
	SAL	AL,1   			; TEST MISSING ADDRESS MARK
	MOV	AH,BAD_ADDR_MARK
	JC	J19			; RW_FAIL

;------ NEC MUST HAVE FAILED

J18:   					; RW_NEC_FAIL
	MOV	AH,BAD_NEC
J19:					; RW_FAIL
	OR	DISKETTE_STATUS,AH
	CALL	NUM_TRANS		; HOW MANY WERE REALLY TRANSFERRED
J20:					; RW_ERR
	RET				; RETURN TO CALLER

J21:					; RW_ERR_RES
	CALL	RESULTS			; FLUSH THE RESULTS BUFFER
	RET

;------ OPERATION WAS SUCCESSFUL

J22:					; OPN_OK
	CALL	NUM_TRANS		; HOW MANY GOT MOVED
	XOR	AH,AH  			; NO ERRORS
	RET
RW_OPN	ENDP
;--------------------------------------------
; NEC_OUTPUT
;	THIS ROUTINE SENDS A BYTE TO THE NEC CONTROLLER
;	AFTER TESTING FOR CORRECT DIRECTION AND CONTROLLER READY
;       THIS ROUTINE WILL TIME OUT IF THE BYTE IS NOT ACCEPTED
;	WITHIN A REASONABLE AMOUNT OF TIME, SETTING THE DISKETTE STATUS
;	ON COMPLETION
; INPUT
;	(AH)	BYTE TO BE OUTPUT
; OUTPUT
;	CY = 0	SUCCESS
;	CY = 1	FAILURE -- DISKETTE STATUS UPDATED
;		IF A FAILURE HAS OCCURED, THE RETURN IS MADE ONE LEVEL
;		HIGHER THAN THE CALLER OF NEC_OUTPUT
;		THIS REMOVES THE REQUIREMENT OF TESTING AFTER EVERY CALL
;		OF NEC_OUTPUT
;	(AL) DESTROYED
;--------------------------------------------
NEC_OUTPUT	PROC	NEAR
	PUSH	DX			; SAVE REGISTERS
	PUSH	CX
        MOV	DX,03F4H       		; STATUS PORT
        XOR	CX,CX  			; COUNT FOR TIME OUT
J23:
	IN	AL,DX			; GET STATUS
        TEST	AL,040H			; TEST DIRECTION BIT
	JZ	J25			; DIRECTION OK
	LOOP	J23
J24:					; TIME_ERROR
	OR	DISKETTE_STATUS,TIME_OUT
	POP	CX
	POP	DX			; SET ERROR CODE AND RESTORE REGS
	POP	AX			; DISCARD THE RETURN ADDRESS
	STC				; INDICATE ERROR TO CALLER
	RET

J25:
	XOR     CX,CX   		; RESET THE COUNT
J26:
	IN	AL,DX			; GET THE STATUS
	TEST	AL,080H			; IS IT READY
	JNZ	J27			; YES, GO OUTPUT
	LOOP	J26			; COUNT DOWN AND TRY AGAIN
	JMP	J24			; ERROR CONDITION
J27:					; OUTPUT
	MOV	AL,AH  			; GET BYTE TO OUTPUT
	MOV	DX,03F5H       		; DATA PORT
	OUT	DX,AL  			; OUTPUT THE BYTE
	POP	CX			; RECOVER REGISTERS
	POP	DX
	RET				; CY = 0 FROM TEST INSTRUCTION
NEC_OUTPUT	ENDP

;------------------------------------------
; GET_PARM
;  THIS ROUTINE FETCHES THE INDEXED POINTER FROM
;  THE DISK_BASE BLOCK POINTED AT BY THE DATA
;  VARIABLE DISK_POINTER
; A BYTE FROM THAT TABLE IS THEN MOVED INTO AH,
;  THE INDEX OF THAT BYTE BEING THE PARM IN BX
; ENTRY --
;   BX = INDEX OF BYTE TO BE FETCHED * 2
;	 IF THE LOW BIT OF BX IS ON, THE BYTE IS IMMEDIATELY
;	 OUTPUT TO THE NEC CONTROLLER
; EXIT --
;   AH = THAT BYTE FROM BLOCK
;--------------------------------------------
GET_PARM	PROC	NEAR
	PUSH	DS			; SAVE SEGMENT
	SUB	AX,AX  			; ZERO TO AX
       	MOV	DS,AX
	ASSUME	DS:ABS0
	LDS	SI,DISK_POINTER 	; POINT TO BLOCK
	SHR	BX,1			; DIVIDE BX BY 2, AND SET FLAG FOR EXIT
	MOV	AH,[SI+BX]		; GET THE WORD
	POP	DS			; RESTORE SEGMENT
	ASSUME	DS:DATA
	JC	NEC_OUTPUT		; IF FLAG SET, OUTPUT TO CONTROLLER
	RET				; RETURN TO CALLER
GET_PARM        ENDP
;--------------------------------------------
; SEEK
;	THIS ROUTINE WILL MOVE THE HEAD ON THE NAMED DRIVE
;	TO THE NAMED TRACK. IF THE DRIVE HAS NOT BEEN ACCESSED
;	SINCE THE DRIVE RESET COMMAND WAS ISSUED, THE DRIVE WILL BE
;	RECALIBRATED.
; INPUT
;	(DL) = DRIVE TO SEEK ON
;	(CH) = TRACK TO SEEK TO
; OUTPUT
;	CY = 0 SUCCESS
;	CY = 1 FAILURE -- DISKETTE_STATUS SET ACCORDINGLY
;	(AX) DESTROYED
;--------------------------------------------
SEEK	PROC	NEAR
	MOV	AL,1   			; ESTABLISH MASK FOR RECAL TEST
	PUSH	CX			; SAVE INPUT VALUES
	MOV	CL,DL			; GET DRIVE VALUE INTO CL
	ROL	AL,CL			; SHIFT IT BY THE DRIVE VALUE
	POP	CX			; RECOVER TRACK VALUE
	TEST 	AL,SEEK_STATUS     	; TEST FOR RECAL REQUIRED
	JNZ 	J28			; NO_RECAL
	OR 	SEEK_STATUS,AL		; TURN ON THE NO RECAL BIT IN FLAG
	MOV 	AH,07H     		; RECALIBRATE COMMAND
	CALL 	NEC_OUTPUT
	MOV 	AH,DL	
	CALL 	NEC_OUTPUT		; OUTPUT THE DRIVE NUMBER

	CALL	CHK_STAT_2		; GET THE INTEPUPT AN SENSE INT STATUS
	JC	J32			; SEEK_ERROR

;----- DRIVE IS IN SYNCH WITH CONTROLLER, SEEK TO TRACK

J28:
	MOV	AH,0FH 			; SEEK COMMAND TO NEC
	CALL	NEC_OUTPUT
	MOV	AH,DL  			; DRIVE NUMBER
	CALL	NEC_OUTPUT
	MOV	AH,CH  			; TRACK NUMBER
	CALL	NEC_OUTPUT
	CALL	CHK_STAT_2		; GET ENDING INTERRUPT AND SENSE STATUS

;----- WAIT FOR HEAD SETTLE

	PUSHF				; SAVE STATE FLAGS
	MOV	BX,18  			; GET HEAD SETTLE PARAMETER
	CALL	GET_PARM
	PUSH	CX      		; SAVE REGISTER
J29:					; HEAD_SETTLE
	MOV	CX,550 			; 1 MS LOOP
	OR	AH,AH  			; TEST FOR TIME EXPIRED
	JZ	J31
J30:	LOOP	J30			; DELAY FOR 1 MS
	DEC	AH			; DECREMENT THE COUNT
	JMP	J29			; DO IT SOME MORE
J31:
	POP	CX			; RECOVER STATE
	POPF
J32:					; SEEK ERROR
	RET				; RETURN TO CALLER
SEEK	ENDP

;--------------------------------------------
; DMA_SETUP
;	THIS ROUTINE SETS UP THE DMA FOR READ/WRITE/VERIFY
;	OPERATIONS.
; INPUT
;	(AL) = MODE BYTE FOR THE DMA
;	(ES:BX) = ADDRESS TO READ/WRITE THE DATA
; OUTPUT
;	(AX) DESTROYED
;--------------------------------------------
DMA_SETUP	PROC	NEAR
	PUSH	CX      		; SAVE THE REGISTER
	OUT	DMA+12,AL		; SET THE FIRST/LAST F/F
	OUT	DMA+11,AL		; OUTPUT THE MODE BYTE
	MOV	AX,ES  			; GET THE ES VALUE
	MOV	CL,4   			; SHIFT COUNT
	ROL	AX,CL   		; ROTATE LEFT
	MOV	CH,AL			; GET HIGHEST NYBLE OF ES TO CH
	AND	AL,0F0H			; ZERO THE LOW NYBBLE FROM SEGMENT
	ADD	AX,BX			; TEST FOR CARRY FROM ADDITION
	JNC	J33
	INC	CH			; CARRY MEANS HIGH 4 BITS MUST BE INC
J33:
	PUSH	AX			; SAVE START ADDRESS
	OUT	DMA+4,AL       		; OUTPUT LOW ADDRESS
	MOV	AL,AH
	OUT	DMA+4,AL       		; OUTPUT HIGH ADDRESS
	MOV	AL,CH			; GET HIGH 4 BITS
	AND	AL,0FH
	OUT	081H,AL			; OUTPUT THE HIGH 4 BITS TO PAGE REGISTER

;------ DETERMINE COUNT

	MOV	AH,DH			; NUMBER OF SECTORS
	SUB	AL,AL			;  TIMES 256 INTO AX
	SHR	AX,1			; SECTORS * 128 INTO AX
	PUSH	AX
	MOV	BX,6   			; GET THE BYTES/SECTOR PARM
	CALL	GET_PARM
	MOV	CL,AH 			; USE AS SHIFT COUNT(0=128,1=256 ETC)
	POP	AX
	SHL	AX,CL 			; MULTIPLY BY CORRECT AMOUNT
	DEC	AX 			; -1 FOR DMA VALUE
	PUSH	AX 			; SAVE COUNT VALUE
	OUT	DMA+5,AL       		; LOW BYTE OF COUNT
	MOV	AL,AH
	OUT	DMA+5,AL		; HIGH BYTE OF COUNT
	POP	CX			; RECOVER COUNT VAWE
	POP	AX			; RECOVER ADDRESS VALUE
	ADD	AX,CX   		; ADD, TEST FOR 64K OVERFLOW
	POP	CX			; RECOVER REGISTER
	MOV	AL,2			; MODE FOR 8237
	OUT     DMA+10,AL		; INIITIALIZE THE DISKETTE CHANNEL
	RET				; RETURN TO CALLER, CFL SET BY ABOVE IF ERROR
DMA_SETUP	ENDP

;---------------------------------------------
; CHK_STAT_2
;	THIS ROUTIINE HANDLES THE INTERRUPT RECEIVED AFTER
;	A RECALIBRATE, SEEK, OR RESET TO THE ADAPTER.
;	THE INTERRUPT IS WAITED FOR, THE INTERRUPT STATUS SENSED,
;	AND THE RESULT RETURNED TO THE CALLER.
; INPUT
;	NONE
; OUT~T
;	CY = 0 SUCCESS
;	CY = 1 FAILURE -- ERROR IS IN DISKETTE_STAUS
;	(AX) DESTROYED
;
CHK_STAT_2	PROC	NEAR
	CALL	WAIT_INT		; WAIT FOR THE INTERRUPT
	JC	J34			; IF ERROR, RETURN IT
	MOV	AH,08H 			; SENSE INTERRUPT STATUS COMMAND
	CALL	NEC_OUTPUT
	CALL	RESULTS			; READ IN THE RESULTS
	JC	J34			; CHK2_RETURN
	MOV	AL,NEC_STATUS  		; GET THE FIRST STATUS BYTE
	AND	AL,060H			; ISOLATE THE BITS
	CMP	AL,060H			; TEST FOR CORRECT VALUE
	JZ	J35     		; IF ERROR, GO MARK IT
	CLC				; GOOD RETURN
J34:
	RET				; RETURN TO CALLER
J35:					; CHK2_ERROR
	OR	DISKETTE_STATUS,BAD_SEEK
	STC				; ERROR RETURN CODE
	RET
CHK_STAT_2	ENDP

;------------------------------------------
; WAIT_INT
;	THIS ROUTINE WAITS FOR AN INTERRUPT TO OCCUR
;       A TIME OUT ROUTINE TAKES PLACE DURING THE WAIT, SO
;	THAT AN ERROR MAY BE RETURNED IF THE DRIVE IS NOT READY
; INPUT
;	NONE
; OUTPUT
; 	CY = 0 SUCCESS
; 	CY = 1 FAILURE -- DISKETTE_STATUS IS SET ACCORDINGLY
;	(AX) DESTROYED
;------------------------------------------
WAIT_INT	PROC	NEAR
	STI     			; TURN ON INTERRUPTS, JUST IN CASE
	PUSH	BX
	PUSH	CX			; SAYE REGISTERS
	MOV	BL,2			; CLEAR THE COUNTERS
	XOR	CX,CX  			; FOR 2 SECOND WAIT
J36:
	TEST	SEEK_STATUS,INT_FLAG	; TEST FOR INTERRUPT OCCURRING
	JNZ	J37
	LOOP	J36			; COUNT DOWN WHILE WAITING
	DEC	BL			; SECOND LEVEL COUNTER
	JNZ	J36
	OR	DISKETTE_STATUS,TIME_OUT ; NOTHING HAPPENED
	STC				; ERROR RETURN
J37:
	PUSHF   			; SAVE CURRENT CARRY
	AND	SEEK_STATUS,NOT INT_FLAG	; TURN OFF INTERRUPT FLAG
	POPF				; RECOVER CARRY
	POP	CX
	POP	BX			; RECOVER REGISTERS
	RET				; GOOD RETURN CODE COMES FROM TEST INST
WAIT_INT	ENDP

;-------------------------------------------
; DISK_INT
; 	THIS ROUTINE HANDLES THE DISKETTE INTERRUPT
; INPUT
;	NONE
; OUTPUT
; 	THE INTERRUPT FLAG IS SET IS SEEK_STATUS
;--------------------------------------------
DISK_INT	PROC	FAR
	STI				; RE ENABLE INTERRUPTS
	PUSH	DS
	PUSH	AX
	MOV	AX,DATA
	MOV	DS,AX
	OR	SEEK_STATUS,INT_FLAG
	MOV	AL,20H			; END OF INTERRUPT MARKER
	OUT	20H,AL 			; INTERRUPT CONTROL PORT
	POP	AX
	POP	DS			; RECOVER SYSTEM
	IRET				; RETURN FROM INTERRUPT
DISK_INT	ENDP

;---------------------------------------------
; RESULTS
; 	THIS ROUTINE WILL READ ANYTHING THAT THE NEC CONTROLLER
;	HAS TO SAY FOLLOWING AN INTEPRUPT.
; INPUT
;	NONE
; OUTPUT
;	CY = 0	SUCCESSFUL TRANSFER
;	CY = 1	FAILURE -- TIME OUT IN WAITING FOR STATUS
;	NEC_STATUS AREA HAS STATUS BYTE LOADED INTO IT
;	(AH)	DESTROYED
;--------------------------------------------
RESULTS PROC	NEAR
	CLD
	MOV	DI,OFFSET NEC_STATUS	; POINTER TO DATA AREA
	PUSH	CX      		; SAVE COUNTER
	PUSH	DX
	PUSH	BX
	MOV	BL,7   			; MAX STATUS BYTES

;------ WAIT FOR REQUEST FOR MASTER

J38:    				; INPUT_LOOP
        XOR     CX,CX  			; COUNTER
	MOV	DX,03F4H		; STATUS PORT
J39:    				; WAIT FOR MASTER
	IN	AL,DX			; GET STATUS
	TEST	AL,080H			; MASTER READY
	JNZ	J40A			; TEST_DIR
        LOOP    J39     		; WAIT MASTER
        OR      DISKETTE_STATUS,TIME_OUT
J40:					; RESULTS_ERROR
	STC				; SET ERROR RETURN
	POP	BX
	POP	DX
	POP	CX
	RET

;------ TEST THE DIRECTION BIT

J40A:   IN      AL,DX   		; GET STATUS REG AGAIN
        TEST    AL,040H			; TEST DIRECTION BIT
        JNZ     J42     		; OK TO READ STATUS
J41:					; NEC_FAIL
	OR	DISKETTE_STATUS,BAD_NEC
        JMP     J40     		; RESULTS ERROR

;------ READ IN THE STATUS

J42:					; INPUT STATUS
        INC     DX      		; POINT AT DATA PORT
        IN      AL,DX   		; GET THE DATA
        MOV     [DI],AL      		; STORE THE BYTE
	INC	DI			; INCREMENT THE POINTER
	MOV	CX,10			; LOOP TO KILL TIME FOR NEC

J43:
	LOOP	J43
	DEC	DX			; POINT AT STATUS PORT
	IN	AL,DX			; GET STATUS
	TEST	AL,010H			; TEST FOR NEC STILL BUSY
	JZ	J44			; RESULTS DONE
	DEC	BL			; DECREMENT THE STATUS COUNTER
	JNZ	J38			; GO BACK FOR MORE
	JMP	J41			; CHIP HAS FAILED

;------ RESULT OPERATION IS DONE

J44:
	POP	BX
	POP	DX
	POP	CX			; RECOVER REGISTERS
	RET				; GOOD RETURN CODE FROM TEST INST
;--------------------------------------------
; NUM_TRANS
;	THIS ROUTINE CALCULATES THE NUMBER OF SECTORS THAT
;       WERE ACTUALLY TRANSFERRED TO/FROM THE DISKETTE
; INPUT
;	(CH) = CYLINDER OF OPERATION
;	(CL) = START SECTOR OF OPERATION
; OUTPUT
;	(AL) = NUMBER ACTUALLY TRANSFERRED
;	N0 OTHER REGISTERS MODIFIED
;-------------------------------------------
NUM_TRANS	PROC	NEAR
	MOV	AL,NEC_STATUS+3		; GET CYLINDER ENDED UP ON
	CMP     AL,CH   		; SAME AS THE STARTED
	MOV	AL,NEC_STATUS+5		; GET ENDING SECTOR
	JZ	J45			; IF ON SAME CYL, THEN NO ADJUST
	MOV	BX,8
	CALL	GET_PARM       		; GET EOT VALUE
	MOV	AL,AH			;  INTO AL
	INC	AL			; USE EOT+1 FOR CALCULATION
J45:    SUB 	AL,CL       		; SUBTRACT START FROM END
	RET
NUM_TRANS	ENDP
RESULTS 	ENDP

;-------------------------------------------
; DISK_BASE
;  THIS IS THE SET OF PARAMETERS REQUIRED FOR
;  DISKETTE OPERATION, THEY ARE POINTED AT BY THE
;  DATA VARIABLE DISK_POINTER. TO MODIFY THE PARAMETERS,
;  BUILD ANOTHER PARAMETER BLOCK AND POINT AT IT
;-------------------------------------------

DISK_BASE	LABEL	BYTE
	DB	11001111B		; SRT=C, HD UNLOAD=0F - 1ST SPECIFY BYTE
	DB	2			; HD LOAD=1, MODE=DMA - 2ND SPECIFY BYTE
        DB      MOTOR_WAIT      	; WAIT AFTER OPN TIL MOTOR OFF
        DB      2       		; 512 BYTES/SECTOR
        DB      8       		; EOT (LAST SECTOR ON TRACK)
	DB	02AH			; GAP LENGTH
        DB      0FFH    		; DTL
	DB	050H			; GAP LENGTH FOR FORMAT
	DB	0F6H			; FILL BYTE FOR FORMAT
        DB      25      		; HEAD SETTLE TIME (MILLISECONDS)
        DB      4       		; MOTOR START TIME (1/8 SECONDS)

;--- INT 17 ---------------------------------
; PRINTER_IO
;	THIS ROUTINE PROVIDES COMMUNICATI0N WITH THE PRINTER
;       (AH)=0	PRINT THE CHARACTER IN (AL)
;		ON RETURN, AH=1 IF CHARACTER COULD NOT BE PRINTED (TIME OUT)
;		OTHER BITS SET AS ON NORMAL STATUS CALL
;	(AH)=1	INITIALIZE THE PRINTER PORT
;       	RETURNS WITH (AH) SET WITH PRINTER STATUS
;       (AH)=2	READ THE PRINTER STATUS INTO (AH)
;		7       6       5       4       3       2-1     0
;          	|       |       |       |       |        |      _TIME OUT
;		|       |       |       |       |        _UNUSED
;		|       |       |       |       _1 = I/O ERROR
;		|       |       |       _1 = SELECTED
;		|       |       _1 = OUT OF PAPER
;		|       _1 = ACKNOWLEDGE
;		_1 = BUSY
;
;       (DX) = PRINTER TO BE USED (0, 1, 2) CORRESPONDING TO ACTUAL VALUES
;		IN PRINTER_BASE AREA
; DATA AREA PRINTER BASE CONTAINS THE BASE ADDRESS OF THE PRINTER CARD(S)
;   AVAILABLE (LOCATED AT BEGINNING OF DATA SEGMENT, 408H ABSOLUTE, 3 WORDS)
;REGISTERS	AH IS MODIFIED
;		ALL OTHERS UNCHANGED
;--------------------------------------------
        ASSUME	CS:CODE,DS:DATA
PRINTER_IO	PROC	FAR
	STI				; INTERRUPTS BACK
	PUSH	DS			; SAVE SEGMENT
	PUSH	DX
	PUSH	SI
	PUSH	CX
	PUSH	BX
	MOV     SI,DATA
	MOV	DS,SI			; ESTABLISH PRINTER SEGMENT
	MOV	SI,DX			; GET PRINTER PARM
	SHL     SI,1   			; WORD OFFSET INTO TABLE
	MOV	DX,PRINTER_BASE[SI] 	; GET BASE ADDRESS FOR PRINTER CARD
	OR 	DX,DX        		; TEST DX FOR ZERO, INDICATING NO PRINTER
	JZ 	B1			; RETURN
	OR 	AH,AH			; TEST FOR (AH)=0
	JZ 	B2			; PRINT_AL
	DEC	AH  			; TEST FOR (AH)=1
	JZ 	B8			; INIT_PRT
	DEC 	AH			; TEST FOR (AH)=2
	JZ 	B5			; PRINTER STATUS
B1:     				; RETURN
	POP 	BX
	POP 	CX
	POP 	SI			; RECOVER REGISTERS
        POP 	DX  			; RECOVER REGISTERS
	POP 	DS
	IRET

;------ PRINT THE CHARACTER IN (AL)

B2 :
        PUSH 	AX 			; SAVE VALUE TO PRINT
        MOV 	BL,10      		; TIME OUT VALUE
        XOR 	CX,CX       		; ESTABLISH SHIFT COUNT
        OUT 	DX,AL       		; OUTPUT CHAR TO PORT
	INC 	DX			; POINT TO STATUS PORT
B3:     				; WAIT_BUSY
	IN 	AL,DX			; GET STATUS
        MOV 	AH,AL      		; STATUS TO AH ALSO
        TEST 	AL,80H     		; IS THE PRINTER CURRENTLY BUSY
        JNZ 	B4 			; OUT_STROBE
        LOOP 	B3 			; DECREMENT COUNT ON TIME OUT
	DEC 	BL
        JNZ 	B3 			; WAIT FOR NOT BUSY
        OR 	AH,1			; SET ERROR FLAG
        AND 	AH,0F9H  		; TURN OFF THE OTHER BITS
	JMP 	SHORT B7		; RETURN WITH ERROR FLAG SET
B4:					; OUT_STROBE
	MOV 	AL,0DH			; SET THE STROBE HIGH
	INC 	DX			; STROBE IS BIT 0 OF PORT C OF 8255
	OUT 	DX,AL
        MOV 	AL,0CH      		; SET THE STROBE LOW
	OUT 	DX,AL
        POP 	AX  			; RECOVER THE OUTPUT CHAR

;------ PRINTER STATUS

B5:
	PUSH	AX 			; SAVE AL REG
B6:
	MOV	DX,PRINTER_BASE[SI]
	INC	DX
        IN	AL,DX 			; GET PRINTER STATUS
	MOV     AH,AL
	AND	AH,0F8H 		; TURN OFF USED BITS
B7:					; STATUS_SET
	POP	DX 			; RECOVER AL REG
	MOV	AL,DL 			; GET CHARACTER INTO AL
        XOR     AH,48H 			; FLIP A COUPLE OF BITS
        JMP     B1 			; RETURN FROM ROUTINE

;------ INITIALIZE THE PRINTER PORT

B8:
	PUSH	AX 			; SAVE AL
	ADD	DX,2 			; POINT TO OUTPUT PORT
	MOV	AL,8 			; SET INIT LINE LOW
	OUT	DX,AL
	MOV	AX,1000
B9:					; INIT_LOOP
	DEC	AX			; LOOP FOR RESET TO TAKE
	JNZ	B9 			; INIT_LOOP
	MOV	AL,0CH 			; NO INTERRUPTS, NON AUTO LF, INIT HIGH
	OUT	DX,AL
        JMP	B6 			; PRT_STATUS_1
PRINTER_IO	ENDP

;--- INT 10 ---------------------------------
; VIDEO_IO
;	THESE ROUTINES PROVIDE THE CRT INTERFACE
;	THE FOLLOWING FUNCTIONS ARE PROVIDED:
;       (AH)=0  SET MODE (AL) CONTAINS MODE VALUE
;       	(AL)=0 40X25 BW (POWER ON DEFAULT)
;		(AL)=1 40X25 COLOR
;		(AL)=2 80X25 BW
;		(AL)=3 80X25 COLOR
;		GRAPHICS MODES
;		(AL)=4 320X200 COLOR
;		(AL)=5 320X200 BW
;		(AL)=6 640X200 BW
;       	CRT MODE = 7 80X25 B&W CARD (USED INTERNAL TO VIDEO ONLY)
;		*** NOTE BW MODES OPERATE SAME AS COLOR MODES, BUT COLOR
;		        BURST IS NOT ENABLED
;	(AH)=1	SET CURSOR TYPE
;		(CH) = BITS 4-0 = START LINE FOR CURSOR
;		       ** HARDWARE WILL ALWAYS CAUSE BLINK
;       	       ** SETTING BIT 5 OR 6 WILL CAUSE ERRATIC BLINKING
;		          OR NO CURSOR AT ALL
;		(CL) = BITS 4-0 = END LINE FOR CURSOR
;	(AH)=2	SET CURSOR POSITION
;       	(DH,DL) = ROW,COLUMN (0,0) IS UPPER LEFT
;      		(DH) = PAGE NUMBER (MUST BE 0 FOR GRAPHICS MODES)
;	(AH)=3	READ CURSOR POSITION
;       	(BH) = PAGE NUMBER (MUST BE 0 FOR GRAPHICS MODES)
;		ON EXIT (DH,DL) = ROW,COLUMN OF CURRENT CURSOR
;		(CH,CL) = CURSOR MODE CURRENTLY SET
;       (AH)=4  READ LIGHT PEN POSITION
;		ON EXIT:
;		(AH) = 0 -- LIGHT PEN SWITCH NOT DOWN/NOT TRIGGERED
;		(AH) = 1 -- VALID LIGHT PEN VALUE IN REGISTERS
;		        (DH,DL) = ROW, COLUMN OF CHARACTER LP POSN
;		        (CH) = RASTER LINE (0-199)
;		        (BX) = PIXEL COLUMN (0-319,639)
;	(AH)=5	SELECT ACTIVE DISPLAY PAGE (VALID ONLY FOR ALPHA MODES)
;		(AL)=NEW PAGE VALUE (0-7 FOR MODES 0&1, 0-3 FOR MODES 2&3)
;	(AH)=6	SCROLL ACTIVE PAGE UP
;		(AL) = NUMBER OF LINES, INPUT LINES BLANKED AT BOTTOM OF WINDOW
;		        AL = 0 MEANS BLANK ENTIRE WINDOW
;		(CH,CL) = ROW,COLUMN OF UPPER LEFT CORNER OF SCROLL
;		(DH,DL) = ROW,COLUMN OF LOWER RIGHT CORNER OF SCROLL
;		(BH) = ATTRIBUTE TO BE USED ON BLANK LINE
;	(AH)=7	SCROLL ACTIVE PAGE DOWN
;		(AL) = NUMBER OF LINES, INPUT LINES BLANKED AT TOP OF WINDOW
;		        AL = 0 MEANS BLANK ENTIRE WINDOW
;		(CH,CL) = ROW,COLUMN OF UPPER LEFT CORNER OF SCROLL
;		(DH,DL) = ROW,COLUMN OF LOWER RIGHT CORNER OF SCROLL
;		(BH) = ATTRIBUTE TO BE USED ON BLANK LINE
;
;	CHARACTER HANDLING ROUTINES
;
;	(AH) = 8 READ ATTRIBUTE/CHARACTER AT CURRENT CURSOR POSITION
;       	(BH) = DISPLAY PAGE (VALID FCR ALPHA MODES ONLY)
;		ON EXIT:
;       	(AL) = CHAR READ
;       	(AH) = ATTRIBUTE OF CHARACTER READ (ALPHA MODES ONLY)
;	(AH) = 9 WRITE ATTRIBUTE/CHARACTER AT CURRENT CURSOR POSITION
;       	(BH) = DISPLAY PAGE (VALID FOR ALPHA MODES ONLY)
;		(CX) = COUNT OF CHARACTERS TO WRITE
;		(AL) = CHAR TO WRITE
;       	(BL) = ATTRIBUTE OF CHARACTER (ALPHA)/COLOR OF CHAR (GRAPHICS)
;		        SEE NOTE ON WRITE DOT FOR BIT 7 OF BL = 1.
;	(AH) = 10 WRITE CHARACTER ONLY AT CURRENT CURSOR POSITION
;       	(BH) = DISPLAY PAGE (VALID FOR ALPHA MODES ONLY)
;		(CX) = COUNT OF CHARACTERS TO WRITE
;		(AL) = CHAR TO WRITE
;       FOR READ/WRITE CHARACTER INTERFACE WHILE IN GRAPHICS MODE, THE
;		CHARACTERS ARE FORMED FROM A CHARACTER GENERATOR IMAGE
;		MAINTAINED IN THE SYSTEM ROM. ONLY THE 1ST 128 CHARS
;		ARE CONTAINED THERE. TO READ/WRITE THE SECOND 128 CHARS.
;		THE USER MUST INITIALIZE THE POINTER AT INTERRUPT 1FH
;		(LOCATION 0007CH) TO POINT TO THE 1K BYTE TABLE CONTAINING
;       	THE CODE POINTS FOR THE SECOND 128 CHARS (128-255).
;	FOR WRITE CHARACTER INTERFACE IN GRAPHICS MODE, THE REPLICATION FACTOR
;       	CONTAINED IN (CX) ON ENTRY WILL PRODUCE VALID RESULTS ONLY
;		FOR CHARACTERS CONTAINED ON THE SAME ROW. CONTINUATION TO
;       	SUCCEEDING LINES WILL NOT PRODUCE CORRECTLY.
;
;       GRAPHICS INTERFACE
;      (AH) = 11 SET COLOR PALETTE
;             (DH) = PALLETTE COLOR ID BEING SET (0-127)
;	      (BL) = COLOR VALUE TO BE USED WITH THAT COLOR ID
;       	 NOTE: FOR THE CURRENT COLOR CARD, THIS ENTRY POINT HAS
;		    MEANING ONLY FOR 320X200 GRAPHICS.
;       	    COLOR ID = 0 SELECTS THE BACKGROUND COLOR (0-15)
;       	    COLOR ID = 1 SELECTS THE PALLETTE TO BE USED:
;		            0 = GREEN(1)/RED(2)/YELLOW(3)
;		            1 = CYAN(1)/MAGENTA(2)/WHITE(3)
;		    IN 40X25 OR 80X25 ALPHA MODES, THE VALUE SET FOR
;		            PALLETTE COLOR 0 INDICATES THE BORDER COLOR
;		            TO BE USED (VALUES 0-31, WHERE 16-31 SELECT THE
;		            HIGH INTENSITY BACKGROUND SET.
;	(AH) = 12 WRITE DOT
;		(DX) = ROW NUMBER
;		(CX) = COLUMN NUMBER
;		(AL) = COLOR VALUE
;		        IF BIT 7 OF AL = 1, THEN THE COLOR VALUE IS EXCLUSIVE
;		        OR'D WITH THE CURRENT CONTENTS OF THE DOT
;       (AH) = 13 READ DOT
;		(DX) = ROW NUMBER
;		(CX) = COLUMN NUMBER
;		(AL) RETURNS THE DOT READ
;
; ASCII TELETYPE ROUTINE FOR OUTPUT
;
;	(AH) = 14 WRITE TELETYPE
;		(AL) = CHAR TO WRITE
;       	(BL) = FOREGROUND COLOR IN GRAPHICS MODE
;		(BH) = DISPLAY PAGE IN ALPHA MODE
;       		NOTE -- SCREEN WIDTH IS CONTROLLED BY PREVIOUS MODE SET
;
;	(AH) = 15 CURRENT VIDEO STATE
;		RETURNS THE CURRENT VIDEO STATE
;		(AL) = MODE CURRENTLY SET (SEE AH=0 FOR EXPLANATION)
;		(AH) = NUMBER OF CHARACTER COLUMNS 0N SCREEN
;		(BH) = CURRENT ACTIVE DISPLAY PAGE
;
;	CS,SS,DS,ES,BX,CX,DX PRESERVED DURING CALL
;	ALL OTHERS DESTROYED
;-------------------------------------------- 
	ASSUME	CS:CODE,DS:DATA,ES:VIDEO_RAM

M1      LABEL	WORD			; TABLE OF ROUTINES WITHIN VIDEO I/O
	DW	OFFSET	SET_MODE
	DW	OFFSET	SET_CTYPE
	DW	OFFSET	SET_CPOS
	DW	OFFSET	READ_CURSOR
	DW	OFFSET	READ_LPEN
	DW	OFFSET	ACT_DISP_PAGE
	DW	OFFSET	SCROLL_UP
	DW	OFFSET	SCROLL_DOWN 
	DW	OFFSET	READ_AC_CURRENT
	DW	OFFSET	WRITE_AC_CURRENT
	DW	OFFSET	WRITE_C_CURRENT
	DW	OFFSET	SET_COLOR
	DW	OFFSET	WRITE_DOT
	DW	OFFSET	READ_DOT
	DW	OFFSET	WRITE_TTY
	DW	OFFSET	VIDEO_STATE
M1L	EQU	$-M1

VIDEO_IO	PROC	NEAR
	STI				; INTERRUPTS BACK ON
	CLD				; SET DIRECTION FORWARD
	PUSH	ES
	PUSH	DS			; SAVE SEGMENT REGISTERS
	PUSH	DX
	PUSH	CX
	PUSH	BX
	PUSH	SI
	PUSH	DI
	PUSH	AX			; SAVE AX VALUE
	MOV	AL,AH			; GET INTO LOW BYTE
	XOR	AH,AH			; ZERO TO HIGH BYTE
        SAL     AX,1   			; *2 FOR TABLE LOOKUP
        MOV     SI,AX			; PUT INTO SI FOR BRANCH
	CMP	AX,M1L			; TEST FOR WITHIN RANGE
	JB	M2			; BRANCH AROUND BRANCH
	POP	AX			; THROW AWAY THE PARAMETER
        JMP     VIDEO_RETURN		; DO NOTHING IF NOT IN RANGE
M2:	MOV 	AX,DATA
        MOV     DS,AX
        MOV     AX,0B800H      		; SEGMENT FOR COLOR CARD
        MOV     DI,EQUIP_FLAG  		; GET EQUIPMENT SETTING
	AND	DI,30H			; ISOLATE CRT SWITCHES
        CMP     DI,30H 			; IS SETTING FOR BW CARD?
	JNE	M3
        MOV     AX,0B000H      		; SEGMENT FOR BW CARD
M3:	MOV	ES,AX   		; SET UP TO POINT AT VIDEO RAM AREAS
	POP	AX			; RECOVER VALUE
	MOV	AH,CRT_MODE		; GET CURRENT MODE INTO AH
        JMP     WORD PTR CS:[SI+OFFSET M1]
VIDEO_IO	ENDP

;------------------------------------------
; SET_MODE
; 	THIS ROUTINE INITIALIZES THE ATTACHMENT TO
; 	THE SELECTED MODE. THE SCREEN IS BLANKED.
; INPUT
;	(AL) = MODE SELECTED (RANGE 0-9)
; OUTPUT
;	NONE
;------------------------------------------

;------ TABLES FOR USE IN SETTING OF MODE

VIDEO_PARMS	LABEL	BYTE
;------ INIT_TABLE

	DB	38H,28H,2DH,0AH,1FH,6,19H	; SET UP FOR 40X25
	DB	1CH,2,7,6,7
	DB	0,0,0,0
M4	EQU	$-VIDEO_PARMS

	DB	71H,50H,5AH,0AH,1FH,6,19H	; SET UP FOR 80X25
	DB	1CH,2,7,6,7
	DB	0,0,0,0

        DB      38H,28H,2DH,0AH,7FH,6,64H 	; SET UP FOR GRAPHICS
	DB	70H,2,1,6,7
	DB	0,0,0,0

	DB	61H,50H,52H,0FH,19H,6,19H	; SET UP FOR 80X25 B&W CARD
	DB	19H,2,0DH,0BH,0CH
	DB	0,0,0,0

M5	LABEL	WORD			; TABLE OF REGEN LENGTHS
	DW	2048			; 40X25
	DW	4096			; 80X25
	DW	16384 			; GRAPHICS
	DW	16384 			;

;------ COLUMNS
M6	LABEL	BYTE
	DB	40,40,80,80,40,40,80,80 ;column offsets

;------ C_REG_TAB
M7	LABEL	BYTE			; TABLE OF MODE SETS
	DB	2CH,28H,2DH,29H,2AH,2EH,1EH,29H ; 8 different CRT modes
	
SET_MODE	PROC	NEAR
        MOV     DX,03D4H       		; ADDRESS OF COLOR CARD
        MOV     BL,0   			; MODE SET FOR COLOR CARD
        CMP     DI,30H 			; IS BW CARD INSTALLED
	JNE	M8			; OK WITH COLOR
        MOV     AL,7   			; INDICATE BW CARD MODE
        MOV     DX,03B4H       		; ADDRESS OF BW CARD
	INC	BL			; MODE SET FOR BW CARD
M8:	MOV	AH,AL			; SAVE MODE IN AH
        MOV     CRT_MODE,AL     	; SAVE IN GLOBAL VARIABLE
	MOV	ADDR_6845,DX		; SAVE ADDRESS OF BASE
	PUSH	DS			; SAVE POINTER TO DATA SEGMENT
        PUSH    AX      		; SAVE MODE
        PUSH    DX      		; SAVE OUTPUT PORT VALUE
        ADD     DX,4    		; POINT TO CONTROL REGISTER
        MOV     AL,BL	  		; GET MODE SET FOR CARD
	OUT	DX,AL			; RESET VIDEO
	POP	DX			; BACK TO BASE REGISTER
        SUB     AX,AX  			; SET UP FOR ABS0 SEGMENT
        MOV     DS,AX   		; ESTABLISH VECTOR TABLE ADDRESSING
	ASSUME	DS:ABS0
        LDS     BX,PARM_PTR   		; GET POINTER TO VIDEO PARMS
	POP	AX			; RECOVER PARMS
        ASSUME  DS:CODE
        MOV	CX,M4  			; LENGTH OF EACH ROW OF TABLE
        CMP     AH,2   			; DETERMINE WHICH ONE TO USE
        JC      M9      		; MODE IS 0 OR 1
        ADD     BX,CX  			; MOVE TO NEXT ROW OF INIT_TABLE
        CMP     AH,4
        JC      M9      		; MODE IS 2 OR 3
	ADD	BX,CX			; MOVE TO GRAPHICS ROW OF INIT_TABLE
	CMP	AH,7
        JC      M9      		; MODE IS 4, 5, OR 6
        ADD     BX,CX  			; MOVE TO BW CARD ROW OF INIT_TABLE

;------ BX POINTS TO CORRECT ROW OF INITIALIZATION TABLE

M9:					; OUT_INIT
	PUSH	AX			; SAVE MODE IN AH
        XOR	AH,AH			; AH WILL SERVE AS REGISTER NUMBER DURING LOOP

;------ LOOP THROUGH TABLE, OUTPUTTING REG ADDRESS, THEN VALUE FROM TABLE

M10:					; INIT LOOP
        MOV     AL,AH   		; GET 6845 REGISTER NUMBER
	OUT	DX,AL
	INC	DX			; POINT TO DATA PORT
	INC	AH			; NEXT REGISTER VALUE
	MOV	AL,[BX]			; GET TABLE VALUE
	OUT 	DX,AL			; OUT TO CHIP
	INC	BX			; NEXT IN TABLE
        DEC     DX      		; BACK TO POINTER REGISTER
	LOOP	M10			; DO THE WHOLE TABLE
	POP	AX			; GET MODE BACK
	POP	DS			; RECOVER SEGMENT VALUE
	ASSUME	DS:DATA

;------ FILL REGEN AREA WITH BLANK

	XOR	DI,DI			; SET UP POINTER FOR REGEN
	MOV	CRT_START,DI		; START ADDRESS SAVED IN GLOBAL
        MOV     ACTIVE_PAGE,0  		; SET PAGE VALUE
	MOV	CX,8192			; NIMBER OF WORDS IN COLOR CARD
        CMP     AH,4   			; TEST FOR GRAPHICS
	JC	M12			; NO_GRAPHICS_INIT
        CMP     AH,7   			; TEST FOR BW CARD
	JE	M11			; BW_CARD_INIT
        XOR     AX,AX   		; FILL FOR GRAPHICS MODE
        JMP     SHORT M13       	; CLEAR_BUFFER
M11:					; BW_CARD_INIT
	MOV	CX,2048			; BUFFER SIZE ON BW CARD
M12:					; NO_GRAPHICS_INIT
        MOV     AX,' '+7*256   		; FILL CHAR FOR ALPHA
M13:					; CLEAR BUFFER
	REP	STOSW			; FILL THE REGEN BUFFER WITH BLANKS


;------ ENABLE VIDEO AND CORRECT PORT SETTING

	MOV	CURSOR_MODE,67H 	; SET CURRENT CURSOR MODE
	MOV	AL,CRT_MODE		; GET THE MODE
	XOR	AH,AH			; INTO AX REGISTER
	MOV	SI,AX			; TABLE POINTER, INDEXED BY {CRT} MODE
        MOV     DX,ADDR_6845   		; PREPARE TO OUTPUT TO VIDEO ENABLE PORT
        ADD     DX,4
	MOV	AX,CS:[SI+OFFSET M7]	; {was MOV AL...}
	OUT	DX,AL 			; SET VIDEO ENABLE PORT
        MOV     CRT_MODE_SET,AL		; SAVE THAT VALUE

;------ DETERMINE NUMBER OF COLUMNS, BOTH FOR ENTIRE DISPLAY
;------ AND THE NUMBER TO BE USED FOR TTY INTERFACE

	MOV	AX,CS:[SI+OFFSET M6]	;{was MOV AL...}
	XOR 	AH,AH
	MOV 	CRT_COLS,AX 		; NUMBER OF COLUMNS IN THIS SCREEN

;------ SET CURSOR POSITIONS

	AND	SI,0EH			; WORD OFFSET INTO CLEAR LENGTH TABLE
	MOV	CX,CS:[SI+OFFSET M5]	; LENGTH TO CLEAR
	MOV	CRT_LEN,CX 		; SAVE LENGTH OF CRT -- NOT USED FOR BW
	MOV	CX,8 			; CLEAR ALL CURSOR POSITIONS
	MOV	DI,OFFSET CURSOR_POSN
	PUSH	DS 			; ESTABLISH SEGMENT
	POP	ES 			;   ADDRESSING
        XOR     AX,AX
	REP	STOSW 			; FILL WITH ZEROES

;------ SET UP OVERSCAN REGISTER

	INC	DX			; SET OVERSCAN PORT TO A DEFAULT
	MOV	AL,30H			; VALUE OF 30H FOR ALL MODES EXCEPT 640X200
	CMP	CRT_MODE,6		; SEE IF THE MODE IS 640X200 BW
	JNZ	M14			; IF IT ISNT 640X200, THEN GOTO REGULAR
	MOV	AL,3FH			; IF IT IS 640X200, THEN PUT IN 3FH
M14: 	OUT     DX,AL   		; OUTPUT THE CORRECT VALUE TO 3D9 PORT
	MOV	CRT_PALLETTE,AL 	; SAVE THE VALUE FOR FUTURE USE

;------ NORMAL RETURN FROM ALL VIDEO RETURNS
VIDEO_RETURN:
	POP	DI
	POP	SI
	POP	BX
M15:					; VIDEO_RETURN_C
	POP	CX
	POP	DX
	POP	DS
	POP	ES			; RECOVER SEGMENTS
	IRET				; ALL DONE
SET_MODE	ENDP
;--------------------------------------------
; SET_CTYPE
; 	THIS ROUTINE SETS THE CURSOR VALUE
; INPUT
;	(CX) HAS CURSOR VALUE CH-START LINE, CL-STOP LINE
; OUTPUT
;	NONE
;--------------------------------------------
SET_CTYPE	PROC	NEAR
        MOV     AH,10			; 6845 REGISTER FOR CURSOR SET
	MOV	CURSOR_MODE,CX		; SAVE IN DATA AREA
	CALL	M16			; OUTPUT CX REG
	JMP	VIDEO_RETURN

;------ THIS ROUTINE OUTPUTS THE CX REGISTER TO THE 6845 REGS NAMED IN AH

M16:
	MOV	DX,ADDR_6845		; ADDRESS REGISTER
        MOV     AL,AH  			; GET VALUE
        OUT     DX,AL  			; REGISTER SET
        INC	DX      		; DATA REGISTER
        MOV     AL,CH  			; DATA
	OUT	DX,AL
	DEC	DX
	MOV	AL,AH
        INC     AL      		; POINT TO OTHER DATA REGISTER
        OUT     DX,AL   		; SET FOR SECOND REGISTER
	INC	DX
	MOV	AL,CL			; SECOND DATA VALUE
	OUT	DX,AL
	RET				; ALL DONE
SET_CTYPE	ENDP
;--------------------------------------------
; SET_CPOS
;	THIS ROUTINE SETS THE CURRENT CURSOR POSITION TO THE
;	NEW X-Y VALUES PASSED
; INPUT
;	DX - ROW, COLUMN OF NEW CURSOR
;	BH - DISPLAY PAGE OF CURSOR
; OUTPUT
;	CURSOR IS SET AT 6845 IF DISPLAY PAGE IS CURRENT DISPLAY
;--------------------------------------------
SET_CPOS	PROC	NEAR
	MOV	CL,BH
        XOR     CH,CH			; ESTABLISH LOOP COUNT
        SAL     CX,1   			; WORD OFFSET
	MOV	SI,CX			; USE INDEX REGISTER
        MOV 	[SI+OFFSET CURSOR_POSN],DX ; SAVE THE POINTER
	CMP 	ACTIVE_PAGE,BH
	JNZ	M17			; SET_CPOS_RETURN
        MOV     AX,DX			; GET ROW/COLUMN TO AX
        CALL    M18			; CURSOR_SET
M17:					; SET_CPOS_RETURN
        JMP     VIDEO_RETURN
SET_CPOS	ENDP

;------ SET CURSOR POSITION, AX HAS ROW/COLUMN FOR C~SOR

M18	PROC	NEAR
        CALL    POSITION		; DETERMINE LOCATION IN REGEN BUFFER
	MOV	CX,AX
        ADD     CX,CRT_START    	; ADD IN THE START ADDRESS FOR THIS PAGE
        SAR     CX,1   			; DIVIDE BY 2 FOR CHAR ONLY COUNT
        MOV     AH,14  			; REGISTER NUMBER FOR CURSOR
	CALL	M16			; OUTPUT THE VALUE TO THE 6845
	RET
M18	ENDP
;--------------------------------------------
; READ_CURSOR
;	THIS ROUTINE READS THE CURRENT CURSOR VALUE FROM THE
;	6845, FORMATS IT, AND SENDS IT BACK TO THE CALLER
; INPUT
;	BH - PAGE OF CURSOR
; OUTPUT
;	DX - ROW, COLUMN OF THE CURRENT CURSOR POSITION
;	CX - CURRENT CURSOR MODE
;-------------------------------------------
READ_CURSOR	PROC	NEAR
	MOV	BL,BH
        XOR     BH,BH
        SAL     BX,1			; WORD OFFSET
        MOV     DX,[BX+OFFSET CURSOR_POSN]
        MOV     CX,CURSOR_MODE
	POP	DI
	POP	SI
	POP	BX
        POP     AX			; DISCARD SAVED CX AND DX
	POP	AX
	POP	DS
	POP	ES
	IRET
READ_CURSOR	ENDP
;--------------------------------------------
; ACT_DISP_PAGE
; 	THIS ROUTINE SETS THE ACTIVE DISPLAY PAGE. ALLOWING
;	THE FULL USE OF THE RAM SET ASIDE FOR THE VIDEO ATTACHMENT
; INPUT
;	AL HAS THE NEW ACTIVE DISPLAY PAGE
; OUTPUT
;	THE 6845 IS RESET TO DISPLAY THAT PAGE
;--------------------------------------------
ACT_DISP_PAGE	PROC	NEAR
	MOV	ACTIVE_PAGE,AL		; SAVE ACTIVE PAGE VALUE
        MOV     CX,CRT_LEN		; GET SAVED LENGTH OF REGEN BUFFER
	CBW				; CONVERT AL TO WORD
	PUSH	AX			; SAVE PAGE VALUE
	MUL	CX			; DISPLAY PAGE TIMES REGEN LENGTH
	MOV	CRT_START,AX		; SAVE START ADDRESS FOR LATER REQUIREMENTS
	MOV	CX,AX			; START ADDRESS TO CX
        SAR     CX,1   			; DIVIDE BY 2 FOR 6845 HANDLING
        MOV     AH,12  			; 6845 REGISTER FOR START ADDRESS
	CALL	M16
        POP     BX      		; RECOVER PAGE VALUE
	SAL	BX,1			; *2 FOR WORD OFFSET
        MOV     AX,[BX+OFFSET CURSOR_POSN] ; GET CURSOR FOR THIS PAGE
	CALL	M18			; SET THE CURSOR POSITION
        JMP     VIDEO_RETURN
ACT_DISP_PAGE	ENDP
;--------------------------------------------
; SET_COLOR
;       THIS ROUTINE WILL ESTABLISH THE BACKGROUND COLOR, THE OVERSCAN COLOR,
;	AND THE FOREGROUND COLOR SET FOR MEDIUM RESOLUTION GRAPHICS
; INPUT
;	(BH) HAS COLOR ID
;       	IF BH=0, THE BACKGROUND COLOR VALUE IS SET
;       		FROM THE LOW BITS OF BL (0-31)
;       	IF BH=1, THE PALLETTE SELECTION IS MADE
;			BASED ON THE LOW BIT OF BL:
;				0 = GREEN, RED, YELLOW FOR COLORS 1,2,3
;				1 = BLUE, CYAN, MAGENTA FOR COLORS 1,2,3
;	(BL) HAS THE COLOR VALUE TO BE USED
; OUTPUT
;	THE COLOR SELECTION IS UPDATED
;--------------------------------------------
SET_COLOR	PROC	NEAR
        MOV     DX,ADDR_6845 		; I/O PORT FOR PALETTE
        ADD     DX,5 			; OVERSCAN PORT
        MOV     AL,CRT_PALLETTE 	; GET THE CURRENT PALLETTE VALUE
        OR      BH,BH			; IS THIS COLOR 0?
	JNZ	M20 			; OUTPUT COLOR 1

;------ HANDLE COLOR 0 BY SETTING THE BACKGROUND COLOR

	AND	AL,0E0H			; TURN OFF LOW 5 BITS OF CURRENT
	AND	BL,01FH			; TURN OFF HIGH 3 BITS OF INPUT YALUE
	OR	AL,BL			; PUT VALUE INTO REGISTER
M19:					; OUTPUT THE PALLETTE
        OUT     DX,AL   		; OUTPUT COLOR SELECTION TO 3D9 PORT
        MOV 	CRT_PALLETTE,AL 	; SAVE THE COLOR VALUE
	JMP 	VIDEO_RETURN

;------ HANDLE COLOR 1 BY SELECTING THE PALLETTE TO BE USED

M20:
	AND	AL,0DFH			; TURN OFF PALLETTE SELECT BIT
        SHR	BL,1   			; TEST THE LOW ORDER BIT OF BL
	JNC	M19			; ALREADY DONE
        OR      AL,20H 			; TURN ON PALLETTE SELECT BIT
	JMP	M19			; GO DO IT
SET_COLOR	ENDP
;--------------------------------------------
;VIDEO_STATE
; RETURNS THE CURRENT VIDEO STATE IN AX
; AH = NUMBER OF COLUMNS ON THE SCREEN
; AL = CURRENT VIDEO MODE
; BH = CURRENT ACTIVE PAGE
;-------------------------------------------
VIDEO_STATE	PROC	NEAR
        MOV     AH,BYTE PTR CRT_COLS	; GET NUMBER OF COLUMNS
	MOV	AL,CRT_MODE		; CURRENT MODE
	MOV	BH,ACTIVE_PAGE		; GET CURRENT ACTIVE PAGE
        POP     DI      		; RECOVER REGISTERS
	POP	SI			;
	POP	CX			; DISCARD SAVED BX
	JMP	M15			; RETURN TO CALLER
VIDEO_STATE	ENDP
;-----------------------------------
; POSITION
;	THIS SERVICE ROUTINE CALCULATES THE REGEN BUFFER ADDRESS
;	OF A CHARACTER IN THE ALPHA MOOE
; INPUT
; 	AX = ROW, COLUMN POSITION
; OUTPUT
; 	AX = OFFSET OF CHAR POSITION IN REGEN BUFFER
;------------------------------------
POSITION	PROC	NEAR
	PUSH	BX 			; SAVE REGISTER
        MOV     BX,AX
	MOV	AL,AH 			; ROWS TO AL
	MUL	BYTE PTR CRT_COLS 	; DETERMINE BYTES TO ROW
        XOR     BH,BH
        ADD     AX,BX			; ADD IN COLUMN VALUE
        SAL     AX,1   			; * 2 FOR ATTRIBUTE BYTES
	POP	BX
	RET
POSITION        ENDP
;------------------------------------------
; SCROLL_UP
;       THIS ROUTINE MOVES A BLOCK OF CHARACTERS UP
;	ON THE SCREEN
; INPUT
;	(AH) = CURRENT CRT MODE
;	(AL) = NUMBER OF ROWS TO SCROLL
;	(CX) = ROW/COLUMN OF UPPER LEFT CORNER
;       (DX) = ROW/COLUMN OF LOWER RIGHT CORNER
;	(BH) = ATTRIBUTE TO BE USED ON BLANKED LINE
;	(DS) = DATA SEGMENT
;	(ES) = REGEN BUFFER SEGNENT
; OUTPUT
;	NONE -- THE REGEN BUFFER IS MODIFIED
;-------------------------------------------
	ASSUME	CS:CODE,DS:DATA,ES:DATA
SCROLL_UP	PROC	NEAR

	MOV 	BL,AL 			; SAVE LINE COUNT IN BL
        CMP	AH,4			; TEST FOR GRAPHICS MODE
	JC	N1			; HANDLE SEPARATELY
        CMP     AH,7			; TEST FOR BW CARD
	JE	N1
	JMP	GRAPHICS_UP
N1:					; UP_CONTINUE
	PUSH	BX			; SAVE FILL ATTRIBUTE IN BH
        MOV	AX,CX  			; UPPER LEFT POSITION
	CALL 	SCROLL_POSITION		; DO SETUP FOR SCROLL
	JZ	N7			; BLANK_FIELD
        ADD     SI,AX   		; FROM ADDRESS
	MOV	AH,DH			; # ROWS IN BLOCK
	SUB	AH,BL			; # ROWS TO BE MOVED
N2:					; ROW_LOOP
	CALL	N10			; MOVE ONE ROW
	ADD	SI,BP
        ADD     DI,BP   		; POINT TO NEXT LINE IN BLOCK
	DEC	AH			; COUNT OF LINES TO MOVE
	JNZ	N2			; ROW_LOOP
N3:     				; CLEAR_ENTRY
	POP	AX			; RECOVER ATTRIBUTE IN AH
        MOV     AL,' '			; FILL WITH BLANKS
N4:					; CLEAR_LOOP
	CALL	N11			; CLEAR THE ROW
        ADD     DI,BP			; POINT TO NEXT LINE
        DEC     BL      		; COUNTER OF LINES TO SCROLL
	JNZ	N4			; CLEAR_LOOP
N5:					; SCROLL_END
        MOV     AX,DATA			; GET LOCATION
	MOV	DS,AX
        CMP     CRT_MODE,7		; IS THIS THE BLACK AND WHITE CARD
        JE      N6      		; IF SO, SKIP THE MODE RESET
	MOV	AL,CRT_MODE_SET 	; GET THE VALUE OF THE MODE SET
        MOV     DX,03D8H       		; ALWAYS SET COLOR CARD PORT
	OUT	DX,AL
N6:					; VIDEO_RET_HERE
        JMP     VIDEO_RETURN
N7:					; BLANK_FIELD
	MOV	BL,DH			; GET ROW COUNT
	JMP     N3      		; GO CLEAR THAT AREA
SCROLL_UP	ENDP

;----- HANDLE COMMON SCROLL SET UP HERE

SCROLL_POSITION	PROC	NEAR
        CMP	CRT_MODE,2 		; TEST FOR SPECIAL CASE HERE
	JB	N9			; HAVE TO HANDLE 80X25 SEPARATELY
        CMP	CRT_MODE,3
	JA	N9

;------ 80X25 COLOR CARD SCROLL

	PUSH	DX
        MOV     DX,3DAH			; GUARANTEED TO BE COLOR CARD HERE
	PUSH	AX
N8:					; WAIT_DISP_ENABLE
        IN      AL,DX   		; GET PORT
	TEST	AL,8			; WAIT FOR VERTICAL RETRACE
	JZ	N8			; WAIT_DISP_ENABLE
	MOV	AL,25H
        MOV     DX,03D8H
        OUT     DX,AL   		; TURN OFF VIDEO
	POP	AX			; DURING VERTICAL RETRACE
	POP	DX
N9:	CALL	POSITION		; CONVERT TO REGEN POINTER
        ADD     AX,CRT_START   		; OFFSET OF ACTIVE PAGE
	MOV	DI,AX			; TO ADDRESS FOR SCROLL
	MOV	SI,AX			; FROM ADDRESS FOR SCROLL
	SUB	DX,CX			; DX = #ROWS, #COLS IN BLOCK
	INC	DH
        INC     DL      		; INCREMENT FOR 0 ORIGIN
	XOR	CH,CH			; SET HIGH BYTE OF COUNT TO ZERO
	MOV	BP,CRT_COLS		; GET NUMBER OF COLUMNS IN DISPLAY
        ADD     BP,BP   		; TIMES 2 FOR ATTRIBUTE BYTE
        MOV     AL,BL			; GET LINE COUNT
	MUL 	BYTE PTR CRT_COLS 	; DETERMINE OFFSET TO FROM ADDRESS
        ADD     AX,AX   		; *2 FOR ATTRIBUTE BYTE
	PUSH 	ES			; ESTABLISH ADDRESSING TO REGEN BUFFER
        POP 	DS  			;  FOR BOTH POINTERS
        CMP 	BL,0			; 0 SCROLL MEANS BLANK FIELD
	RET				; RETURN WITH FLAGS SET
SCROLL_POSITION	ENDP

;----- MOVE_ROW
N10	PROC	NEAR
	MOV	CL,DL			; GET # OF COLS TO MOVE
	PUSH	SI
	PUSH	DI			; SAVE START ADDRESS
	REP	MOVSW			; MOVE THAT LINE ON SCREEN
	
	POP	DI
	POP	SI			; RECOVER ADDRESSES
	RET
N10	ENDP

;------ CLEAR_ROW
N11	PROC	NEAR
	MOV	CL,DL			; GET # COLUMNS TO CLEAR
	PUSH	DI
        REP     STOSW			; STORE THE FILL CHARACTER
        
	POP	DI
	RET
N11	ENDP
;-----------------------------------
; SCROLL_DOWN
;       THIS ROUTINE MOVES THE CHARACTERS WITHIN A DEFINED
;	BLOCK DOWN ON THE SCREEN, FILLING THE TOP LINES
;       WITH A DEFINED CHARACTER
; INPUT
;	(AH) = CURRENT CRT MODE
;	(AL) = NUMBER OF LINES TO SCROLL
;	(CX) = UPPER LEFT CORNER OF REGION
;	(DX) = LOWER RIGHT CORNER OF REGION
;	(BH) = FILL CHARACTER
;	(DS) = DATA SEGMENT
;	(ES) = REGEN SEGMENT
; OUTPUT
;	NONE -- SCREEN IS SCROLLED
;-----------------------------------
SCROLL_DOWN	PROC	NEAR
	STD				; DIRECTION FOR SCROLL
	MOV	BL,AL			; LINE COUNT TO BL
        CMP     AH,4    		; TEST FOR GRAPHICS
	JC	N12
        CMP     AH,7			; TEST FOR BW CARD
	JE	N12
        JMP     GRAPHICS_DOWN
N12:					; CONTINUE DOWN
	PUSH	BX			; SAVE ATTRIBUTE IN BH
	MOV	AX,DX			; LOWER RIGHT CORNER
	CALL	SCROLL_POSITION		; GET REGEN LOCATION
	JZ	N16
	SUB	SI,AX			; SI IS FROM ADDRESS
	MOV	AH,DH			; GET TOTAL # ROWS
	SUB	AH,BL			; COUNT TO MOVE IN SCROLL
N13:
	CALL	N10			; MOVE ONE ROW
	SUB	SI,BP
	SUB	DI,BP
	DEC	AH
	JNZ	N13
N14:
	POP	AX			; RECOVER ATTRIBUTE IN AH
	MOV	AL,' '
N15:
	CALL	N11			; CLEAR ONE ROW
        SUB     DI,BP   		; GO TO NEXT ROW
	DEC	BL
	JNZ	N15
	JMP	N5			; SCROLL_END
N16:
	MOV	BL,DH
	JMP	N14
SCROLL_DOWN	ENDP
; -----------------------------------------
; READ_AC_CURRENT
;	THIS ROUTINE READS THE ATTRIBUTE AND CHARACTER AT THE CURRENT
;	CURSOR POSITION AND RETURNS THEM TO THE CALLER
; INPUT
;	(AH) = CURRENT CRT NODE
;       (BH) = DISPLAY PAGE (ALPHA MODES ONLY)
;	(DS) = DATA SEGMENT
;	(ES) = REGEN SEGMENT
; OUTPUT
;	(AL) = CHAR READ
;	(AH) = ATTRIBUTE READ
;------------------------------------------
	ASSUME	CS:CODE,DS:DATA,ES:DATA
READ_AC_CURRENT	PROC	NEAR
	CMP	AH,4			; IS THIS GRAPICS
	JC	P1
        CMP     AH,7   			; IS THIS BW CARD
	JE	P1
	JMP	GRAPHICS_READ
P1:					; READ_AC_CONTINUE
	CALL	FIND_POSITION
	MOV	SI,BX			; ESTABLISH ADDRESSING IN SI

;------ WAIT FOR HORIZONTAL RETRACE

	MOV	DX,ADDR_6845		; GET BASE ADDRESS
        ADD     DX,6    		; POINT AT STATUS PORT
	PUSH	ES			;
	POP	DS			; GET SEGMENT FOR QUICK ACCESS
P2:					; WAIT FOR RETRACE LOW
        IN      AL,DX  			; GET STATUS
	TEST	AL,1			; IS HORZ RETRACE LOW
	JNZ	P2			; WAIT UNTIL IT IS
	CLI				; NO MORE INTERRUPTS
P3:					; WAIT FOR RETRACE HIGH
	IN	AL,DX			; GET STATUS
	TEST	AL,1			; IS IT HIGH
	JZ	P3			; WAIT UNTIL IT IS
        LODSW   			; GET THE CHAR/ATTR
	JMP	VIDEO_RETURN
READ_AC_CURRENT	ENDP

FIND_POSITION	PROC	NEAR
	MOV	CL,BH			; DISPLAY PAGE TO CX
        XOR     CH,CH
	MOV	SI,CX			; MOVE TO SI FOR INDEX
        SAL     SI,1    		; * 2 FOR WORD OFFSET
	MOV	AX,[SI+OFFSET CURSOR_POSN] ; GET ROW/COLUMN OF THAT PAGE
        XOR     BX,BX  			; SET START ADDRESS TO ZERO
	JCXZ	P5			; NO_PAGE
P4:					; PAGE_LOOP
        ADD     BX,CRT_LEN      	; LENGTH OF BUFFER
	LOOP	P4
P5:					; NO_PAGE
	CALL	POSITION		; DETERMINE LOCATION IN REGEN
        ADD     BX,AX   		; ADD TO START OF REGEN
	RET
FIND_POSITION	ENDP
;-----------------------------------------
; WRITE_AC_CURRENT
;	THIS ROUTINE WRITES THE ATTRIBUTE AND CHARACTER AT
;	THE CURRENT CURSOR POSITION
; INPUT
;	(AH) = CURRENT CRT MODE
;	(BH) = DISPLAY PAGE
;       (CX) = COUNT OF CHARACTERS TO WRITE
;	(AL) = CHAR TO WRITE
;	(BL) = ATTRIBUTE OF CHAR TO WRITE
;       (DS) = DATA SEGMENT
;	(ES) = REGEN SEGMENT
; OUTPUT
;	NONE
;------------------------------------------
WRITE_AC_CURRENT	PROC	NEAR
	CMP	AH,4			; IS THIS GRAPHICS
	JC	P6
        CMP     AH,7   			; IS THIS BW CARD
	JE	P6
        JMP     GRAPHICS_WRITE
P6:					; WRITE_AC_CONTINUE
        MOV     AH,BL   		; GET ATTRIBUTE TO AH
	PUSH	AX			; SAVE ON STACK
	PUSH	CX			; SAVE WRITE COUNT
	CALL	FIND_POSITION
	MOV	DI,BX			; ADDRESS TO DI RESISTER
	POP	CX			; WRITE COUNT
	POP	BX			; CHARACTER IN BX REG
P7:					; WRITE_LOOP

;------ WAIT FOR HORIZONTAL RETRACE

        MOV     DX,ADDR_6845   		; GET BASE ADDRESS
        ADD     DX,6   			; POINT AT STATUS PORT
P8:
	IN	AL,DX			; GET STATUS
        TEST    AL,1   			; IS IT LOW
	JNZ	P8			; WAIT UNTIL IT IS
	CLI				; NO MORE INTERRUPTS
P9:
	IN	AL,DX			; GET STATUS
        TEST    AL,1   			; IS IT HIGH
	JZ	P9			; WAIT UNTIL IT IS
	MOV	AX,BX			; RECOVER THE CHAR/ATTR
	STOSW				; PUT THE CHAR/ATTR
	STI				; INTERRUPTS BACK ON
	LOOP	P7 			;  AS MANY TIMES AS REQUESTED
        JMP     VIDEO_RETURN
WRITE_AC_CURRENT	ENDP
;-----------------------------------------
; WRITE_C_CURRENT
;	THIS ROUTINE WRITES THE CHARACTER AT
;	THE CURRENT CURSOR POSITION, ATTRIBUTE UNCHANGED
; INPUT
;	(AH) = CURRENT CRT MODE
;	(BH) = DISPLAY PAGE
;	(CX) = COUNT OF CHARACTERS TO WRITE
;	(AL) = CHAR TO WRITE
;	(DS) = DATA SEGMENT
;	(ES) = REGEN SEGMENT
;OUTPUT
;	NONE
;------------------------------------------
WRITE_C_CURRENT	PROC	NEAR
        CMP     AH,4   			; IS THIS GRAPHICS
	JC	P10
        CMP     AH,7   			; IS THIS BW CARD
	JE	P10
        JMP     GRAPHICS_WRITE
P10:
        PUSH    AX      		; SAVE ON STACK
	PUSH	CX			; SAVE WRITE COUNT
	CALL	FIND_POSITION
	MOV	DI,BX			; ADDRESS TO DI
        POP     CX      		; WRITE COUNT
	POP	BX			; BL HAS CHAR TO WRITE
P11:					; WRITE_LOOP

;------ WAIT FOR HORIZONTAL RETRACE

        MOV     DX,ADDR_6845    	; GET BASE ADDRESS
        ADD     DX,6   			; POINT AT STATUS PORT
P12:
        IN      AL,DX 			; GET STATUS
	TEST	AL,1			; IS IT LOW
	JNZ	P12			; WAIT UNTIL IT IS
	CLI				; NO MORE INTERRUPTS
P13:
        IN      AL,DX   		; GET STATUS
        TEST    AL,1   			; IS IT HIGH
	JZ	P13			; WAIT UNTIL IT IS
	MOV	AL,BL			; RECOVER CHAR
	STOSB				; PUT THE CHAR/ATTR
        INC     DI      		; BUMP POINTER PAST ATTRIBUTE
        LOOP    P11     		;  AS MANY TIMES AS REQUESTED
	JMP	VIDEO_RETURN
WRITE_C_CURRENT	ENDP
;--------------------------------------------
; READ DOT -- WRITE DOT
; THESE ROUTINES WILL WRITE A DOT, OR READ THE
;  DOT AT THE INDICATED LOCATION
; ENTRY --
;   DX = ROW (0-199)   (THE ACTUAL VALUE DEPENDS ON THE MODE)
;   CX = COLUMN (0-639) (THE VALUES ARE NOT RANGE CHECKED)
;   AL = DOT VALUE TO WRITE (1, 2 OR 4 BITS DEPENDING ON MODE,
;	 REQ'D FOR WRITE DOT ONLY, RIGHT JUSTIFIED)
;	 BIT 7 OF AL = 1 INDICATES XOR THE VALUE INTO THE LOCATION
;   DS = DATA SEGMENT
;   ES = REGEN SEGMENT
;
; EXIT
;       AL = DOT VALUE READ, RIGHT JUSTIFIED, READ ONLY
;--------------------------------------------
	ASSUME	CS:CODE,DS:DATA,ES:DATA
READ_DOT	PROC	NEAR
	CALL	R3      		; DETERMINE BYTE POSITION OF DOT
        MOV     AL,ES:[SI]		; GET THE BYTE
	AND	AL,AH			; MASK OFF THE OTHER BITS IN THE BYTE
	SHL	AL,CL			; LEFT JUSTIFY THE VALUE
	MOV	CL,DH			; GET NUMBER OF BITS IN RESULT
	ROL	AL,CL			; RIGHT JUSTIFY THE RESULT
        JMP     VIDEO_RETURN     	; RETURN FROM VIDEO IO
READ_DOT	ENDP

WRITE_DOT	PROC	NEAR
	PUSH	AX			; SAVE DOT VALUE
	PUSH	AX			; TWICE
	CALL	R3			; DETERMINE BYTE POSITION OF THE DOT
	SHR	AL,CL			; SHIFT TO SET UP THE BITS FOR OUTPUT
	AND	AL,AH			; STRIP OFF THE OTHER BITS
        MOV     CL,ES:[SI]  		; GET THE CURRENT BYTE
	POP	BX			; RECOVER XOR FLAG
        TEST    BL,80H 			; IS IT ON
	JNZ	R2			; YES, XOR THE DOT
	NOT	AH			; SET THE MASK TO REMOVE THE INDICATED BITS
	AND	CL,AH
	OR	AL,CL			; OR IN THE NEW VALUE OF THOSE BITS
R1:					; FINISH_DOT
        MOV     ES:[SI],AL  		; RESTORE THE BYTE IN MEMORY
	POP	AX
        JMP     VIDEO_RETURN    	; RETURN FRON VIDEO IO
R2:					; XOR_DOT
	XOR	AL,CL			; EXCLUSIVE OR THE DOTS
        JMP     R1      		; FINISH UP THE WRITING
WRITE_DOT	ENDP
;--------------------------------------------
; THIS SUBROUTINE DETERMINES THE REGEN BYTE LOCATION OF THE
; INDICATED ROW COLUMN VALUE IN GRAPHICS MODE.
; ENTRY --
;  DX = ROW VALUE (0-199)
;  CX = COLUMN VALUE (0-639)
; EXIT --
;  SI = OFFSET INTO REGEN BUFFER FOF BYTE OF INTEREST
;  AH = MASK TO STRIP OFF THE BITS OF INTEREST
;  CL = BITS TO SHIFT TO RIGHT JUSTIFY THE MASK IN AH
;  DH = BITS IN RESULT
;--------------------------------------------
R3	PROC	NEAR
	PUSH 	BX 			; SAVE BX DURING OPERATION
	PUSH 	AX 			; WILL SAVE AL DURING OPERATION

;------ DETERMINE 1ST BYTE IN INDICATED ROW BY MULTIPLTING ROW VALUE BY 40 
;------ (LOW BIT OF ROW DETERMINES EVEN/OOD, 80 BYTES/ROW

	MOV 	AL,40
	PUSH	DX			; SAVE ROW VALUE
	AND	DL,0FEH			; STRIP OFF ODD/EVEN BIT
	MUL	DL			; AX HAS ADDRESS OF 1ST BYTE OF INDICATED ROW
	POP	DX			; RECOVER IT
	TEST	DL,1			; TEST FOR EVEN/ODD
	JZ	R4			; JUMP IF EVEN ROW
        ADD     AX,2000H       		; OFFSET TO LOCATION OF ODD ROWS
R4:     				; EVEN_ROW
        MOV     SI,AX  			; MOVE POINTER TO SI
	POP	AX			; RECOVER AL VALUE
	MOV	DX,CX			; COLUMN VALUE TO DX

;------	DETERMINE GRAPHICS MODE CURRENTLY IN EFFECT

; SET UP THE REGISTERS ACCORDING TO THE MODE
;  CH = MASK FOR LOW OF COLUMN ADDRESS (7/3 FOR HIGH/MED RES)
;  CL = # OF ADDRESS BITS IN COLUMN VALUE (3/2 FOR H/M)
;  BL = MASK TO SELECT BITS FROM POINTED BYTE (80H/C0H FOR H/M)
;  BH = NUMBER OF VALID BITS IN POINTED BYTE (1/2 FOR H/M)

        MOV     BX,2C0H
        MOV     CX,302H 		; SET PARMS FOR MED RES
        CMP     CRT_MODE,6
	JC	R5			; HANDLE IF MED RES
        MOV     BX,180H
        MOV     CX,703H			; SET PARMS FOR HIGH RES

;------ DETERMINE BIT OFFSET IN BYTE FROM COLUMN MASK
R5:
	AND	CH,DL			; ADDRESS OF PEL WITHIN BYTE TO CH

;------ DETERMINE BYTE OFFSET FOR THIS LOCATION IN COLUMN

	SHR	DX,CL 			; SHIFT BY CORRECT AMOUNT
	ADD	SI,DX 			; INCREMENT THE POINTER
	MOV	DH,BH 			; GET THE # OF BITS IN RESULT TO DH

; ------ MULTIPLY BH (VALID BITS IN BYTE) BY CH (BIT OFFSET)

	SUB	CL,CL			; ZERO INTO STORAGE LOCATION
R6:
        ROR     AL,1   			; LEFT JUSTIFY THE VALUE IN AL (FOR WRITE)
        ADD     CL,CH   		; ADD IN THE BIT OFFSET VALUE
	DEC	BH			; LOOP CONTROL
	JNZ	R6			; ON EXIT, CL HAS SHIFT COUNT TO RESTORE BITS
	MOV	AH,BL			; GET MASK TO AH
	SHR	AH,CL			; MOVE THE MASK TO CORRECT LOCATION
	POP	BX			; RECOVER REG
	RET				; RETURN WITH EVERYTHING SET UP
R3	ENDP
;--------------------------------------------
; SCROLL UP
;  THIS ROUTINE SCROLLS UP THE INFORMATION ON THE CRT
; ENTRY --
;  CH,CL = UPPER LEFT CORNER OF REGION TO SCROLL
;  DH,DL = LOWER RIGHT CORNER OF REGION TO SCROLL
;   BOTH OF THE ABOVE ARE IN CHARACTER POSITIONS
;  BH = FILL VALUE FOR BLANKED LINES
;  AL = # LINES TO SCROLL (AL=0 MEANS BLANK THE ENTIRE FIELD)
;  DS = DATA SEGMENT
;  ES = REGEN SEGMENT
; EXIT --
;  NOTHING, THE SCREEN IS SCROLLED
;--------------------------------------------
GRAPHICS_UP	PROC	NEAR
	MOV 	BL,AL 			; SAVE LINE COUNT IN BL
	MOV 	AX,CX 			; GET UPPER LEFT POSITION INTO AX REG

;------ USE CHARACTER SUBROUTINE FOR POSITIONING
;------ ADDRESS RETURNED IS MULTIPUED BY 2 FROM CORRECT VALUE

	CALL	GRAPH_POSN
	MOV	DI,AX			; SAVE RESULT AS DESTINATION ADDRESS

;------ DETERMINE SIZE OF WINDOW
        SUB	DX,CX
        ADD 	DX,101H    		; ADJUST VALUES
        SAL 	DH,1       		; MULTIPLY # ROWS BY 4 SINCE 8 VERT DOTS/CHAR
        SAL 	DH,1       		;  AND EVEN/OOD ROWS

;------ DETERMINE CRT MODE

	CMP 	CRT_MODE,6		; TEST FOR MEDIUM RES
	JNC 	R7			; FIND_SOURCE

;------ MEDIUM RES UP
	SAL	DL,1			; # COLUMNS * 2, SINCE 2 BYTES/CHAR
        SAL	DI,1       		; OFFSET *2 SINCE 2 BYTES/CHAR

;------ DETERMINE THE SOURCE ADDRESS IN THE BUFFER
R7:					; FIND_SOURCE
        PUSH    ES      		; GET SEGMENTS BOTH POINTING TO REGEN
	POP	DS
	SUB	CH,CH			; ZERO TO HIGH OF COUNT REG
	SAL	BL,1 			; MULTIPLY NUMBER OF LINES BY 4
        SAL     BL,1
	JZ	R11			; IF ZERO, THEN BLANK ENTIRE FIELD
	MOV	AL,BL			; GET NUMBER OF LINES IN AL
        MOV     AH,80  			; 80 BYTES
	MUL	AH			; DETERMINE OFFSET TO SOURCE
	MOV	SI,DI			; SET UP SOURCE
        ADD     SI,AX   		;  ADD IN OFFSET TO IT
	MOV	AH,DH			; NUMBER OF ROWS IN FIELD
	SUB	AH,BL			; DETERMINE NUMBER TO MOVE

;------ LOOP THROUGH, MOVING ONE ROW AT A TIME, BOTH EVEN AND ODD FIELDS
R8:					; ROW_LOOP
	CALL	R17			; MOVE ONE ROW
	SUB	SI,2000H-80		; MOVE TO NEXT ROW
	SUB	DI,2000H-80
	DEC	AH			; NUMBER OF ROWS TO MOVE
	JNZ	R8			; CONTINUE TILL ALL MOVED

;------ FILL IN THE VACATED LINE(S)
R9:					; CLEAR_ENTRY
	MOV	AL,BH			; ATTRIBUTE TO FILL WITH
R10:
	CALL	R18			; CLEAR THAT ROW
        SUB     DI,2000H-80    		; POINT TO NEXT LINE
	DEC	BL			; NUMBER OF LINES TO FILL
	JNZ	R10			; CLEAR_LOOP
	JMP	VIDEO_RETURN		; EVERYTHING DONE

R11:					; BLANK_FIELD
	MOV	BL,DH			; SET BLANK COUNT TO EVERYTHING IN FIELD
	JMP	R9			; CLEAR THE FIELD
GRAPHICS_UP	ENDP
;--------------------------------------------
; SCROLL DOWN
;  THIS ROUTINE SCROLLS DOWN THE INFORMATION ON THE CRT
; ENTRY --
;  CH,CL = UPPER LEFT CORNER OF REGION TO SCROLL
;  DH,DL = LOWER RIGHT CORNER OF REGION TO SCROLL
;   BOTH OF THE ABOVE ARE IN CHARACTER POSITIONS
;  BH = FILL VALUE FOR BLANKED LINES
;  AL = # LINES TO SCROLL (AL=0 MEANS BLANK THE ENTIRE FIELD)
;  DS = DATA SEGMENT
;  ES = REGEN SEGMENT
; EXIT --
;  NOTHING, THE SCREEN IS SCROLLED
;---------------------------------------------

GRAPHICS_DOWN	PROC	NEAR
	STD				; SET DIRECTION
	MOV	BL,AL			; SAVE LINE COUNT IN BL
	MOV	AX,DX			; GET LOWER RIGHT POSITION INTO AX REG

;------ USE CHARACTER SUBROUTINE FOR POSITIONING
;------ ADDRESS RETURNED IS MULTIPLIED BY 2 FROM CORRECT VALUE

	CALL	GRAPH_POSN
	MOV	DI,AX 			; SAVE RESULT AS DESTINATION ADDRESS

;------ DETERMINE SIZE OF WINDOW

	SUB	DX,CX
	ADD	DX,101H			; ADJUST VALUES
        SAL     DH,1   			; MULTIPLY # ROWS BY 4 SINCE 8 VERT DOTS/CHAR
        SAL     DH,1   			;  AND EVEN/ODD ROWS

;------ DETERMINE CRT MODE

        CMP	CRT_MODE,6		; TEST FOR MEDIUM RES
	JNC	R12			; FIND_SOURCE_DOWN

;------ MEDIUM RES DOWN
        SAL	DL,1			; # COLUMNS * 2, SINCE 2 BYTES/CHAR (OFFSET OK)
        SAL	DI,1			; OFFSET *2 SINCE 2 BYTES/CHAR
	INC	DI			; POINT TO LAST BYTE

;------ DETERMINE THE SOURCE ADDRESS IN THE BUFFER
R12:					; FIND_SOURCE_DOWN
	PUSH	ES			; BOTH SEGMENTS TO REGEN
	POP	DS
        SUB     CH,CH  			; ZERO TO HIGH OF COUNT REG
        ADD     DI,240 			; POINT TO LAST ROW OF PIXELS
        SAL     BL,1   			; MULTIPLY NUMBER OF LINES BY 4
	SAL	BL,1
	JZ	R16			; IF ZERO, THEN BLANK ENTIRE FIELD
        MOV     AL,BL  			; GET NUMBER OF LINES IN AL
        MOV     AH,80  			; 80 BYTES/ROW
	MUL	AH			; DETERMINE OFFSET TO SOURCE
	MOV	SI,DI			; SET UP SOURCE
	SUB	SI,AX			;  SUBTRACT THE OFFSET
	MOV	AH,DH			; NUMBER OF ROWS IN FIELD
	SUB	AH,BL			; DETERMINE NUMBER TO MOVE

;------	LOOP THROUGH, MOVING ONE ROW AT A TIME, BOTH EVEN AND ODD FIELDS
R13:					; ROW_LOOP_DOWN
	CALL	R17			; MOVE ONE ROW
	SUB	SI,2000H+80		; MOVE TO NEXT ROW
        SUB     DI,2000H+80
	DEC	AH			; NUMBER OF ROWS TO MOVE
	JNZ	R13			; CONTINUE TILL ALL MOVED

; ------ FILL IN THE VACATED LINE(S)
R14:					; CLEAR_ENTRY_DOWN
	MOV	AL,BH			; ATTRIBUTE TO FILL WITH
R15:    				; CLEAR_LOOP_DOWN
	CALL	R18			; CLEAR A ROW
        SUB     DI,2000H+80    		; POINT TO NEXT LINE
	DEC	BL			; NUMBER OF LINES TO FILL
	JNZ	R15			; CLEAR_LOOP_DOWN
	CLD				; RESET THE DIRECTION FLAG
	JMP	VIDEO_RETURN		; EVERYTHING DONE

R16:    				; BLANK_FIELD_DOWN
	MOV	BL,DH			; SET BLANK COST TO EVERYTHING IN FIELD
	JMP	R14			; CLEAR THE FIELD
GRAPHICS_DOWN	ENDP

;------ ROUTINE TO MOVE ONE ROW OF INFORMATION

R17	PROC	NEAR
	MOV	CL,DL			; NUMBER OF BYTES IN THE ROW
	PUSH	SI
	PUSH	DI			; SAVE POINTERS
	REP	MOVSB			; MOVE THE EVEN FIELD

	POP	DI
	POP	SI
        ADD     SI,2000H
        ADD     DI,2000H       		; POINT TO THE ODD FIELD
	PUSH	SI
	PUSH	DI			; SAVE THE POINTERS
        MOV     CL,DL  			; COUNT BACK
	REP	MOVSB			; MOVE THE ODD FIELD

	POP	DI
	POP	SI			; POINTERS BACK
	RET				; RETURN TO CALLER
R17	ENDP

;------ CLEAR A SINGLE ROW

R18	PROC	NEAR
	MOV	CL,DL			; NUMBER OF BYTES IN FIELD
	PUSH	DI			; SAVE POINTER
	REP	STOSB			; STORE THE NEW VALUE

	POP	DI			; POINTER BACK
	ADD	DI,2000H		; POINT TO ODD FIELD
	PUSH	DI
	MOV	CL,DL
	REP	STOSB			; FILL THE ODD FILELD

	POP	DI
	RET				; RETURN TO CALLER
R18	ENDP
;--------------------------------------------
; GRAPHICS WRITE 
;  THIS ROUTINE WRITES THE ASCII CHARACTER TO THE CURRENT
;  POSITION ON THE SCREEN.
; ENTRY --
;  AL = CHARACTER TO WRITE
;  BL = COLOR ATTRIBUTE TO BE USED FOR FOREGROUND COLOR
;	IF BIT 7 IS SET, THE CHAR IS XOR'D INTO THE REGEN BUFFER
;	(0 IS USED FOR THE BACKGROUND COLOR)
;  CX = NUMBER OF CHARS TO WRITE
;  DS = DATA SEGMENT
;  ES = REGEN SEGMENT
; EXIT --
;  NOTHING IS RETURNED
;
; GRAPHICS READ
;   THIS ROUTINE READS THE ASCII CHARACTER AT THE CURRENT CURSOR
;   POSITION ON THE SCREEN BY MATCHING THE DOTS ON THE SCREEN TO THE
;   CHARACTER GENERATOR CODE POINTS
; ENTRY --
;  NONE (0 IS ASSUMED AS THE BACKGROUND COLOR)
; EXIT --
;  AL = CHARACTER READ AT THAT POSITION (0 RETURNED IF NONE FOUND)
;
; FOR BOTH ROUTINES, THE IMAGES USED TO FORM CHARS ARE CONTAINED IN ROM
;  FOR THE 1ST 128 CHARS. TO ACCESS CHARS IN THE SECOND HALF, THE USER
;  MUST INITIALIZE THE VECTOR AT INTERRUPT 1FH (LOCATION 0007CH) TO
;  POINT TO THE USER SUPPLIED TABLE OF GRAPHIC IMAGES (8X8 BOXES).
;  FAILURE TO DO SO WILL CAUSE IN STRANGE RESULTS
;--------------------------------------------
	ASSUME	CS:CODE,DS:DATA,ES:DATA
GRAPHICS_WRITE	PROC	NEAR 
	MOV	AH,0 			; ZERO TO HIGH OF CODE POINT 
	PUSH	AX 			; SAVE CODE POINT VALUE

;------ DETERMINE POSITION IN REGEN BUFFER TO PUT CODE POINTS

	CALL	S26			; FIND LOCATION IN REGEN BUFFER
	MOV	DI,AX			; REGEN POINTER IN DI

;------ DETERMINE REGION TO GET CODE POINTS FROM

	POP	AX			; RECOVER CODE POINT 
	CMP	AL,80H			; IS IT IN SECOND HALF
	JAE	S1			; YES

; ------ IMAGE IS IN FIRST HALF, CONTAINED IN ROM

	MOV	SI,0FA6EH 		; OFFSET CRT_CHAR_GEN-OFFSET OF IMAGES
	PUSH	CS 			; SAVE SEGMENT ON STACK
        JMP     SHORT S2		; DETERMINE_MODE

;------ IMAGE IS IN SECOND HALF, IN USER RAM

S1:					; EXTEND_CHAR
	SUB	AL,80H			; ZERO ORIGIN FOR SECOND HALF
	PUSH	DS			; SAVE DATA POINTER
        SUB     SI,SI
	MOV	DS,SI			; ESTABLISH VECTOR ADDRESSING
	ASSUME	DS:ABS0
        LDS     SI,EXT_PTR     		; GET THE OFFSET OF THE TABLE
        MOV     DX,DS  			; GET THE SEGMENT OF THE TABLE
	ASSUME	DS:DATA
        POP     DS      		; RECOVER DATA SEGMENT
	PUSH	DX			; SAVE TABLE SEGMENT ON STACK

;------ DETERMINE GRAPHICS MODE IN OPERATION

S2:					; DETERMINE_MODE
	SAL     AX,1   			; MULTIPLY CODE POINT
        SAL     AX,1   			;  VALUE BY 8
        SAL     AX,1
	ADD	SI,AX			; SI HAS OFFSET OF DESIRED CODES
        CMP     CRT_MODE,6
	POP	DS			; RECOVER TABLE POINTER SEGMENT
	JC	S7			; TEST FOR MEDIUM RESOLUTION MODE

;------ HIGH RESOLUTION MODE
S3:     				; HIGH_CHAR
        PUSH    DI      		; SAVE REGEN POINTER
        PUSH    SI      		; SAVE CODE POINTER
	MOV	DH,4			; NUMBER OF LINES THROUGH LOOP
S4:
	LODSB				; GET BYTE FROM CODE POINTS
	TEST	BL,80H			; SHOULD WE USE THE FUNCTION
	JNZ	S6			;  TO PUT CHAR IN
	STOSB				; STORE IN REGEN BUFFER
	LODSB
S5:					;
	MOV	ES:[DI+2000H-1],AL 	; STORE IN SECOND HALF
        ADD     DI,79  			; MOVE TO NEXT ROW IN REGEN
	DEC	DH			; DONE WITH LOOP
	JNZ	S4
	POP	SI
	POP	DI			; RECOVER REGEN POINTER
        INC	DI      		; POINT TO NEXT CHAR POSITION
	LOOP	S3			; MORE CHARS TO WRITE
	JMP	VIDEO_RETURN

S6:
        XOR     AL,ES:[DI]		; EXCLUSIVE OR WITH CURRENT
	STOSB				; STORE THE CODE POINT
	LODSB				; AGAIN FOR ODD FIELD
        XOR     AL,ES:[DI+2000H-1]	;
        JMP     S5			; BACK TO MAINSTREAM

;------ MEDIUM RESOLUTION WRITE
S7:					; MED_RES_WRITE
	MOV	DL,BL			; SAVE HIGH COLOR BIT
        SAL     DI,1   			; OFFSET*2 SINCE 2 BYTES/CHAR
	CALL	S19			; EXPAND BL TO FULL WORD OF COLOR
S8:					; MED_CHAR
	PUSH	DI			; SAVE REGEN POINTER
	PUSH	SI			; SAVE THE CODE POINTER
	MOV	DH,4			; NUMBER OF LOOPS
S9:
        LODSB   			; GET CODE POINT
	CALL	S21			; DOUBLE UP ALL THE BITS
        AND     AX,BX  			; CONVERT THEM TO FOREGROUND COLOR (0 BACK )
        TEST    DL,80H 			; IS THIS XOR FUNCTION
	JZ	S10			; NO, STORE IT IN AS IT IS
        XOR     AH,ES:[DI]  		; DO FUNCTICH WITH HALF
	XOR     AL,ES:[DI+1]		;  AND WITH OTHER HALF
S10:					;
	MOV	ES:[DI],AH		; STORE FIRST BYTE
	MOV	ES:[DI+1],AL		; STORE SECOND BYTE
        LODSB   			; GET CODE POINT
	CALL	S21
	AND	AX,BX 			; CONVERT TO COLOR
	TEST	DL,80H 			; AGAIN, IS THIS XOR FUNCTION
	JZ	S11 			; NO, JUST STORE THE VALUES
        XOR     AH,ES:[DI+2000H]	; FUNCTION WITH FIRST HALF
	XOR	AL,ES:[DI+2001H]	; AND WITH SECOND HALF
S11:					;
	MOV	ES:[DI+2000H],AH
	MOV	ES:[DI+2000H+1],AL	; STORE IN SECOND PORTION OF BUFFER
	ADD	DI,80 			; POINT TO NEXT LOCATION
	DEC	DH
	JNZ	S9 			; KEEP GOING
        POP     SI 			; RECOVER CODE POINTER
	POP	DI 			; RECOVER REGEN POINTER
        ADD     DI,2 			; POINT TO NEXT CHAR POSITION
	LOOP	S8 			; MORE TO WRITE
	JMP	VIDEO_RETURN
GRAPHICS_WRITE	ENDP
;-----------------------------------------------
; GRAPHICS READ
;-----------------------------------------------
GRAPHICS_READ	PROC	NEAR
	CALL	S26			; CONVERTED TO OFFSET IN REGEN
	MOV	SI,AX			; SAVE IN SI
	SUB	SP,8			; ALLOCATE SPACE TO SAVE THE READ CODE POINTER
	MOV	BP,SP			; POINTER TO SAVE AREA

;------ DETERMINE GRAPHICS MODES

        CMP	CRT_MODE,6
	PUSH	ES
	POP	DS			; POINT TO REGEN SEGMENT
	JC	S13			; MEDIUM RESOLUTION

;------ HIGH RESOLUTION READ

;------ GET VALUES FROM REGEN BUFFER AND CONVERT TO CODE POINT
	MOV	DH,4			; NUMBER OF PASSES
S12:
	MOV	AL,[SI]			; GET FIRST BYTE
	MOV	[BP],AL			; SAVE IN STORAGE AREA
	INC	BP			; NEXT LOCATION
        MOV     AL,[SI+2000H]		; GET LOWER REGION BYTE
	MOV	[BP],AL			; ADJUST AND STORE
	INC	BP
        ADD     SI,80   		; POINTER INTO REGEN
	DEC	DH			; LOOP CONTROL
        JNZ     S12    			; DO IT SOME MORE
        JMP     NEAR PTR S15    	; GO MATCH THE SAVED CODE POINTS

;------ MEDIUM RESOLUTION READ
S13:					; MED_RES_READ
        SAL     SI,1   			; OFFSET*2 SINCE 2 BYTES/CHAR
	MOV	DH,4			; NUMBER OF PASSES
S14:
	CALL	S23			; GET PAIR BYTES FROM REGEN INTO SINGLE SAVE
	ADD	SI,2000H		; GO TO LOWER REGION
        CALL    S23     		; GET THIS PAIR INTO SAVE
        SUB     SI,2000H-80    		; ADJUST POINTER BACK INTO UPPER
	DEC	DH
	JNZ	S14			; KEEP GOING UNTIL ALL 8 DONE

;-------- SAVE AREA HAS CHARACTER IN IT, MATCH IT
S15:					; FIND_CHAR
        MOV     DI,0FA6EH		; OFFSET CRT_CHAR_GEN-ESTABLISH ADDRESSING
	PUSH	CS
	POP	ES			; CODE POINTS IN CS
	SUB	BP,8			; ADJUST POINTER TO BEGINNING OF SAVE AREA
	MOV	SI,BP
	CLD				; ENSURE DIRECTION
	MOV	AL,0			; CURRENT CODE POINT BEING MATCHED
S16:
	PUSH	SS			; ESTABEISH ADDRESSING TO STACK
        POP     DS      		; FOR THE STRING COMPARE
        MOV     DX,128 			; NUMBER TO TEST AGAINST
S17:
	PUSH	SI			; SAVE SAVE AREA POINTER
	PUSH	DI			; SAVE CODE POINTER
	MOV	CX,8			; NUMBER OF BYTES TO MATCH
	REPE	CMPSB 			; COMPARE THE 8 BYTES
	
	POP	DI			; RECOVER THE POINTERS
	POP	SI
	JZ	S18			; IF ZERO FLAG SET, THEN MATCH OCCURRED
	INC	AL			; NO MATCH, MOVE ON TO NEXT
	ADD	DI,8			; NEXT CODE POINT
	DEC	DX			; LOOP CONTROL
	JNZ	S17 			; DO ALL OF THEM
    
; ------ CHAR NOT MATCHED, MIGHT BE IN USER SUPPLIED SECOND HALF
 
	CMP	AL,0    		; AL<> 0 IF ONLY 1ST HALF SCANNED
	JE	S18			; IF = 0, THEN ALL HAS BEEN SCANNED
	SUB	AX, AX
	MOV	DS,AX   		; ESTABLISH ADDRESSING TO VECTOR
	ASSUME  DS:ABS0
	LES	DI,EXT_PTR   		; GET POINTER
	MOV	AX,ES   		; SEE IF THE POINTER REALLY EXISTS
	OR	AX,DI   		; IF ALL 0, THEN DOESN'T EXIST
	JZ	S18			; NO SENSE LOOKING
	MOV	AL,128  		; ORIGIN FOR SECOND HALF
	JMP	S16			; GO BACK AND TRY FOR IT
	ASSUME	DS:DATA
    
;------ CHARACTER IS FOUND (AL=0 IF NOT FOUND)
S18:
	ADD	SP,8			; READJUST THE STACK, THROW AWAY SAVE
	JMP	NEAR PTR VIDEO_RETURN	; ALL DONE
GRAPHICS_READ	ENDP
;--------------------------------------------
; EXPAND_MED_COLOR
;  THIS ROUTINE EXPANDS THE LOW 2 BITS IN BL TO
;  FILL THE ENTIRE BX REGISTER
; ENTRY --
;  BL = COLOR TO BE USED (LOW 2 BITS)
; EXIT --
;  BX = COLOR TO BE USED (8 REPLICATIONS OF THE 2 COLOR BITS)
;--------------------------------------------
S19	PROC    NEAR
	AND 	BL,3 			; ISOLATE THE COLOR BITS
	MOV 	AL,BL			; COPY TO AL
	PUSH    CX			; SAVE REGISTER
	MOV 	CX,3 			; NUMBER OF TIMES TO DO THIS
S20:
	SAL 	AL,1
	SAL 	AL,1    		; LEFT SHIFT BY 2
	OR  	BL,AL			; ANOTHER COLOR VERSION INTO BL
	LOOP    S20			; FILL ALL OF BL
	MOV	BH,BL	 		; FILL UPPER PORTION
	POP 	CX   			; REGISTER BACK
	RET		 		; ALL DONE
S19	ENDP
;--------------------------------------------
; EXPAND_BYTE
;  THIS ROUTINE TAKES THE BYTE IN AL AND DOUBLES ALL
;  OF THE BITS, TURNING THE 8 BITS INTO 16 BITS.
;  THE RESULT IS LEFT IN AX
;--------------------------------------------
S21	PROC	NEAR
	PUSH	DX   			; SAVE REGISTERS
	PUSH	CX
	PUSH	BX
	MOV	DX,0 			; RESULT REGISTER
	MOV	CX,1 			; MASK REGISTER
S22:
	MOV	BX,AX    		; BASE INTO TEMP
	AND	BX,CX			; USE MASK TO EXTRACT A BIT
	OR	DX,BX			; PUT INTO RESULT REGISTER
	SHL	AX,1
	SHL	CX,1 			; SHIFT BASE AND MASK BY 1
	MOV	BX,AX			; BASE TO TEMP
	AND	BX,CX			; EXTRACT THE SAME BIT
	OR	DX,BX			; PUT INTO RESULT
	SHL  	CX,1 			; SHIFT ONLY MASK NOW, MOVING TO NEXT BASE
	JNC  	S22   			; USE MASK BIT COMING OUT TO TERMINATE
	MOV  	AX,DX 			; RESULT TO PARM REGISTER
	POP  	BX
	POP  	CX    			; RECOVER REGISTERS
	POP  	DX
	RET	   			; ALL DONE
S21	ENDP
;--------------------------------------------
; MED_READ_BYTE
; THIS ROUTINE WILL TAKE 2 BYTES FROM THE REGEN BUFFER,
;  COMPARE AGAINST THE CURRENT FOREGROUND COLOR, AND PLACE
;  THE CORRESPONDING ON/OFF BIT PATTERN INTO THE CURRENT
;  POSITION IN THE SAVE AREA
; ENTRY --
;  SI,DS = POINTER TO REGEN AREA OF INTEREST
;  BX = EXPANDED FOREGROUND COLOR
;  BP = POINTER TO SAVE AREA
; EXIT --
;  BP IS INCREMENT AFTER SAVE
;---------------------------------------------
S23	PROC	NEAR
	MOV	AH,[SI]			; GET FIRST BYTE
	MOV	AL,[SI+1]		; GET SECOND BYTE
	MOV	CX,0C000H		; 2 BIT MASK TO TEST THE ENTRIES
	MOV	DL,0			; RESULT REGISTER
S24:
	TEST    AX,CX			; IS THIS SECTION BACKGROUND?
	CLC				; CLEAR CARRY IN HOPES THAT IT IS
	JZ	S25			; IF ZERO, IT IS BACKGROUND
	STC				; WASN'T, SO SET CARRY
S25:	RCL	DL,1    		; MOVE THAT BIT INTO THE RESULT
	SHR	CX,1
	SHR	CX,1			; MOVE THE MASK TO THE RIGHT BY 2 BITS
	JNC	S24			; DO IT AGAIN IF MASK DIDN'T FALL OUT
	MOV	[BP],DL			; STORE RESULT IN SAVE AREA
	INC	BP   			; ADJUST POINTER
	RET			 	; ALL DONE
S23	ENDP
;-----------------------------------------
; V4_POSITION
;  THIS ROUTINE TAKES THE CURSOR POSITION CONTAINED IN
;  THE MEMORY LOCATION, AND CONVERTS IT INTO AN OFFSET
;  INTO THE REGEN BUFFER, ASSUMING ONE BYTE/CHAR.
;  FOR MEDIUM RESOLUTION GRAPHICS, THE NUMBER MUST
;  BE DOUBLED.
; ENTRY -- NO REGISTERS,MEMORY LOCATION CURSOR_POSN IS USED
; EXIT--
;  AX CONTAINS OFFSET INTO REGEN BUFFER
;-----------------------------------------
S26	PROC	NEAR
	MOV	AX,CURSOR_POSN		; GET CURRENT CURSOR
GRAPH_POSN	LABEL	NEAR
	PUSH	BX   			; SAVE REGISTER
	MOV	BX,AX			; SAVE A COPY OF CURRENT CURSOR
	MOV	AL,AH			; GET ROWS TO AL
	MUL	BYTE PTR CRT_COLS	; MULTIPLY BY BYTES/COLUMN
	SHL	AX,1			; MULTIPLY * 4 SINCE 4 ROWS/BYTE
	SHL	AX,1
	SUB	BH,BH			; ISOLATE COLUMN VALUE
	ADD	AX,BX			; DETERMINE OFFSET
	POP	BX 			; RECOVER POINTER
	RET			 	; ALL DONE
S26	ENDP
;--------------------------------------------
; WRITE_TTY
; THIS INTERFACE PROVIDES A TELETYPE LIKE INTERFACE TO THE
;  VIDEO CARD. THE INPUT CHARACTER IS WRITTEN TO THE CURRENT
;  CURSOR POSITION, AND THE CURSOR IS MOVED TO THE NEXT POSITION.
;  IF THE CURSOR LEAVES THE LAST COLUMN OF THE FIELD, THE COLUMN
;  IS SET TO ZERO, AND THE ROW VALUE IS INCREMNTED. IF THE ROW
;  ROW VALUE LEAVES THE FIELD, THE CURSOR IS PLACED ON THE LAST ROW,
;  FIRST COLUMN, AND THE ENTIRE SCREEN IS SCROLLED UP ONE LINE.
;  WHEN THE SCREEN IS SCROLLED UP, THE ATTRIBUTE FOR FILLING THE
;  NEWLY BLANKED LINE IS READ FROM THE CURSOR POSITION ON THE PREVIOUS
;  LINE BEFORE THE SCROLL, IN CHARACTER MODE. IN GRAPHICS MODE,
;  THE 0 COLOR IS USED.
; ENTRY --
;   (AH) = CURRENT CRT MODE
;   (AL) = CHARACTER TO BE WRITTEN
;	   NOTE THAT BACK SPACE, CAR RET, BELL AND LINE FEED ARE HANDLED
;	   AS COMMANDS RATHER THAN AS DISPLAYABLE GRAPHICS
;   (BL) = FOREGROUND COLOR FOR CHAR WRITE IF CURRENTLY IN A GRAPHICS MODE
; EXIT --
;   ALL REGISTERS SAVED
;---------------------------------------------
	ASSUME	CS:CODE,DS:DATA
WRITE_TTY	PROC	NEAR
  	PUSH    AX			; SAVE REGISTERS
  	PUSH    AX			; SAVE CHAR TO WRITE
  	MOV 	AH,3
  	INT	10H			; READ THE CURRENT CURSOR POSITION
  	POP 	AX			; RECOVER CHAR
 
;------ DX NOW HAS THE CURRENT CURSOR POSITION
 
	CMP	AL,8    		; IS IT A BACKSPACE
	JE	U8	 		; BACK_SPACE
	CMP	AL,0DH  		; IS IT CARRIAGE RETURN
	JE	U9	 		; CAR_RET
	CMP	AL,0AH  		; IS IT A LINE FEED
	JE	U10			; LINE_FEED
	CMP	AL,07H  		; IS IT A BELL
	JE	U11			; BELL
	
;------ WRITE THE CHAR TO THE SCREEN

	MOV 	BH,ACTIVE_PAGE 		; GET THE CURRENT ACTIVE PAGE
	MOV 	AH,10 			; WRITE CHAR ONLY
	MOV 	CX,1 			; ONLY ONE CHAR
	INT 	10H 			; WRITE THE CHAR

;------ POSITION THE CURSOR FOR NEXT CHAR

	INC  	DL
	CMP 	DL,BYTE PTR CRT_COLS 	; TEST FOR COLUMN OVERFLOW
	JNZ	U7		 	; SET CURSDR
	MOV	DL,0	    		; COLUMN FOR CURSOR
	CMP	DH,24
	JNZ	U6			; SET_CURSOR_INC
    
;------ SCROLL REQUIRED
U1:

	MOV 	AH,2
	MOV 	BH,0
	INT 	10H			; SET THE CURSOR
    
;------ DETERMINE VALUE TO FILL WITH DURING SCROLL
 
	MOV 	AL,CRT_MODE		; GET THE CURRENT MODE
	CMP 	AL,4
	JC  	U2		  	; READ_CURSOR
	CMP 	AL,7
	MOV 	BH,0    		; FILL WITH BACKGROUND
	JNE 	U3		    	; SCROLL_UP
	
U2:					; READ_CURSOR
	MOV 	AH,8
	INT 	10H			; READ CHAR/ATTR AT CURRENT CURSOR
	MOV 	BH,AH	   		; STORE IN BH

U3:					; SCROLL_UP
	MOV 	AX,601H  		; SCROLL ONE LINE
	MOV 	CX,0    		; UPPER LEFT CORNER
	MOV 	DH,24   		; LOWER RIGHT ROW
	MOV 	DL,BYTE PTR CRT_COLS 	; LOWER RIGHT COLUMN
	DEC 	DL
U4:					; VIDEO_CALL_RETURN
	INT 	10H			; SCROLL UP THE SCREEN
U5:					; TTY-RETURN
	POP 	AX	 		; RESTORE THE CHARACTER

	JMP	VIDEO_RETURN		; RETURN TO CALLER

U6:					; SET_CURSOR_INC
	INC	DH			; NEXT ROW
U7:					; SET_CURSOR
	MOV	AH,2
	JMP	U4			; ESTABLISH THE NEW CURSOR
   
;------ BACK SPACE FOUND
   
U8:
	CMP	DL,0			; ALREADY AT END OF LINE
	JE	U7			; SET_CURSOR
	DEC	DL			; NO -- JUST MOVE IT BACK
	JMP	U7			; SET_CURSOR
   
;------ CARRIAGE RETURN FOUND
   
U9:
	MOV	DL,0			; MOVE TO FIRST COLUMN
	JMP	U7		   	; SET_CURSOR

;------ LINE FEED FOUND

U10:
	CMP	DH,24			; BOTTOM OF SCREEN
	JNE	U6			; YES, SCROLL THE SCREEN
	JMP	U1			; NO, JUST SET THE CURSOR
   
;------ BELL FOUND
   
U11:
	MOV	BL,2  			; SET UP COUNT FOR BEEP
	CALL	BEEP  			; SOUND THE POD BELL
	JMP	U5			; TTY_RETURN
WRITE_TTY	ENDP
;-------------------------------------------
; LIGHT PEN
;	THIS ROUTINE TESTS THE LIGHT PEN SWITCH AND THE LIGHT
;	  PEN TRIGGER. IF BOTH ARE SET, THE LOCATION OF THE LIGHT
;	  PEN IS DETERMINED. OTHERWISE, A RETURN WITH NO INFORMATION
;	  IS MADE.
;	ON EXIT:
;	(AH) = 0 IF NO LIGHT PEN INFORMATION IS AVAILABLE
;		BX,CX,DX ARE DESTROYED
;	(AH) = 1 IF LIGHT PEN IS AVAILABLE
;		(DH,DL) = ROW,COLUMN OF CURRENT LIGHT PEN POSITION
;		(CH) = RASTER POSITION
;		(BX) = BEST GUESS AT PIXEL HORIZONTAL POSITION
;--------------------------------------------
	ASSUME	CS:CODE,DS:DATA
;------ SUBTRACT_TABLE
V1	LABEL	BYTE
	DB	3,3,5,5,3,3,3,4	;
READ_LPEN	PROC	NEAR
  
;------ WAIT FOR LIGHT PEN TO BE DEPRESSED

	MOV 	AH,0			; SET NO LIGHT PEN RETURN CODE
	MOV 	DX,ADDR_6845		; GET BASE ADDRESS OF 6845
	ADD 	DX,6    		; POINT TO STATUS REGISTER
	IN  	AL,DX   		; GET STATUS REGISTER
	TEST   	AL,4 			; TEST LIGHT PEN SWITCH
	JNZ	V6			; NOT SET, RETURN

;------ NOW TEST FOR LIGHT PEN TRIGGER

	TEST	AL,2			; TEST LIGHT PEN TRIGGER
	JZ	V7			; RETURN WITHOUT RESETTING TRIGGER

;------ TRIGGER HAS BEEN SET, READ THE VALUE IN

	MOV	AH,16			; LIGHT PEN REGISTERS IN 6845

;------ INPUT REGS POINTED TO BY AH, AND CONVERT TO ROW COLUMN IN DX

	MOV	DX,ADDR_6845		; ADDRESS REGISTER FOR 6845
	MOV	AL,AH			; REGISTER TO READ
	OUT	DX,AL			; SET IT UP
	INC	DX			; DATA REGISTER
	IN	AL,DX			; GET THE VALUE
	MOV	CH,AL			; SAVE IN CX
	DEC	DX			; ADDRESS REGISTER
	INC	AH
	MOV	AL,AH			; SECOND DATA REGISTER
	OUT	DX,AL
	INC	DX			; POINT TO DATA REGISTER
	IN	AL,DX			; GET SECOND DATA VALUE
	MOV	AH,CH			; AX HAS INPUT VALUE
    
;------ AX HAS THE VALUE READ IN FROM THE 6845

	MOV	BL,CRT_MODE
	SUB	BH,BH			; MODE VALUE TO BX
	MOV	BL,CS:V1[BX]		; DETERMINE AMOUNT TO SUBTRACT
	SUB	AX,BX			; TAKE IT AWAY
	SUB	AX,CRT_START		; CONVERT TO CORRECT PAGE ORIGIN
	JNS	V2			; IF POSITIVE, DETERMINE MODE
	MOV	AX,0			; <0 PLAYS AS 0
    
;------ DETERMINE MODE OF OPERATION

V2:		    			; DETERMINE_MODE
	MOV	CL,3    		; SET *8 SHIFT COUNT
	CMP	CRT_MODE,4		; DETERMINE IF GRAPHICS OR ALPHA
	JB 	V4	 		; ALPHA_PEN
	CMP	CRT_MODE,7
	JE 	V4	 		; ALPHA_PEH

;------ GRAPHICS MODE

	MOV	DL,40			; DIVISOR FCR GRAPHICS
	DIV	DL			; DETERMINE ROW(AL) AND COLUMN(AH)
					; AL RANGE 0-99, AH RANGE 0-39
;------ DETERMINE GRAPHIC ROW POSITION

	MOV	CH,AL			; SAVE ROW VALUE IN CH
	ADD	CH,CH			; *2 FOR EVEN/ODD FIELD
	MOV	BL,AH			; COLUMN VALUE TO BX
	SUB	BH,BH			; MULTIPLY BY 8 FOR MEDIUM RES
	CMP	CRT_MODE,6		; DETERMINE MEDIUM OR HIGH RES
	JNE	V3   			; NOT_HIGH_RES
	MOV	CL,4			; SHIFT VALUE FOR HIGH RES
	SAL	AH,1			; COLUMN VALUE TIMES 2 FOR HIGH RES
V3:		   			; NOT_HIGH_RES
	SHL	BX,CL			; MULTIPLY *16 FOR HIGH RES

;------ DETERMINE ALPHA CHAR POSITION

	MOV	DL,AH			; COLUMN VALUE FOR RETURN
	MOV	DH,AL			; ROW VALUE
	SHR	DH,1			; DIVIDE BY 4
	SHR	DH,1			;  FOR VALUE IN 0-24 RANGE
	JMP	SHORT V5		; LIGHT_PEN_RETURN_SET
    
;------ ALPHA MODE ON LIGHT PEN

V4:	    				; ALPHA_PEN
	DIV 	BYTE PTR CRT_COLS 	; DETERMINE ROW,COLUMN VALUE
	MOV	DH,AL 			; ROWS TO DH
	MOV	DL,AH 			; COLS TO DL
	SAL	AL,CL			; MULTIPLY ROWS *8
	MOV	CH,AL			; GET RASTER VALUE TO RETURN REG
	MOV	BL,AH			; COLUMN VALUE
	XOR	BH,BH			; TO BX
	SAL	BX,CL
V5:	    				; LIGHT_PEN_RETURN_SET
	MOV	AH,1			; INDICATE EVERTHING SET
V6:	    				; LIGHT_PEN_RETURN
	PUSH    DX			; SAVE RETURN VALUE (IN CASE)
	MOV	DX,ADDR_6845		; GET BASE ADDRESS
	ADD	DX,7			; POINT TO RESET PARM
	OUT	DX,AL			; ADDRESS, NOT DATA, IS IMPORTANT
	POP	DX			; RECOVER VALUE
V7:	    				; RETURN_NO_RESET
	POP	DI
	POP	SI
	POP	DS			; DISCARD SAVED BX,CX,DX
	POP	DS
	POP	DS
	POP	DS
	POP	ES
	IRET
READ_LPEN	ENDP
;--- INT 12 ---------------------------------
; MEMORY_SIZE_DETERMINE
;	THIS ROUTINE DETERMINES THE AMOUNT OF MEMORY IN THE SYSTEM
;	AS REPRESENTED BY THE SWITCHES ON THE PLANAR. NOTE THAT
;	THE SYSTEM MAY NOT BE ABLE TO USE I/O MEMORY UNLESS THERE
;	IS A FULL COMPLEMENT OF 64K BYTES ON THE PLANAR.
; INPUT
;	NO REGISTERS
;	THE MEMOEY_SIZE VARIABLE IS SET DURING POWER ON DIAGNOSTICS
;	 ACCORDING TO THE FOLLOWING HARDWARE ASSUMPTIONS:
;	PORT 60 BITS 3,2 = 00 - 16K BASE RAM
;			   01 - 32K BASE RAM
;			   10 - 48K BASE RAM
;			   11 - 64K BASE RAM
;	PORT 62 BITS 3-0 INDICATE AMOUNT OF I/O RAM IN 32K INCREMENTS
;		E.G., 0000 - NO RAM IN I/O CHANNEL
; 		      0010 - 64K RAM IN I/O CHANNEL, ETC.
; OUTPUT
; 	(AX) = NUMBER OF CONTIGUOUS 1K BLOCKS OF MEMORY
;--------------------------------------------
	ASSUME	CS:CODE,DS:DATA
MEMORY_SIZE_DETERMINE	PROC	FAR
	STI		  		; INTERRUPTS BACK ON
	PUSH	DS			; SAVE SEGMENT
	MOV	AX,DATA			; ESTABLISH ADDRESSING
	MOV	DS,AX
	MOV	AX,MEMORY_SIZE    	; GET VALUE
	POP	DS			; RECOVER SEGMENT
	IRET	  			; RETURN TO CALLER
MEMORY_SIZE_DETERMINE	ENDP
;--- INT 11 -----------------------------------
; EQUIPMENT DETERMINATION
;	THIS ROUTINE ATTEMPTS TO DETERMINE WHAT OPTIONAL
;	DEVICES ARE ATTACHED TO THE SYSTEM.
; INPUT
;	NO REGISTERS
;	THE EQUIP_FLAG VARIABLE IS SET DURING THE POWER ON DIAGNOSTICS
;	 USING THE FOLLOWING HARDWARE ASSUMPTIONS:
;	PORT 60 = LOW ORDER BYTE OF EQUIPMENT
;	PORT 3FA = INTERRUPT ID REGISTER OF 8250
;		BITS 7-3 ARE ALWAYS 0
;	PORT 378 = OUTPUT PORT OF PRINTER -- 8255 PORT THAT
;		CAN BE READ AS WELL AS WRITTEN
; OUTPUT
;	(AX) IS SET, BIT SIGNIFICANT, TO INDICATE ATTACHED I/O
;	BIT 15,14 = NUMBER OF PRINTERS ATTACHED
;	BIT 13 NOT USED
;	BIT 12 = GAME I/O ATTACHED
;	BIT 11,10,9 = NUMBER OF RS232 CARDS ATTACHED
;	BIT 8 UNUSED
;	BIT 7,6 = NUMBER OF DISKETTE DRIVES
;		00=1, 01=2, 10-3, 11=4 ONLY IF BIT 0 = 1
;	BIT 5,4 = INITIAL VIDEO MODE
;		00 - UNUSED
;		01 - 40x25 BW USING COLOR CARD
;		10 - 80X25 BW USING COLOR CARD
;		11 - 80x25 BW USING BW CARD
;	BIT 3,2 = PLANAR RAM SIZE (00=16K,01=32K,10=48K,11=64K)
;	BIT 1 NOT USED
;	BIT 0 = IPL FROM DISKETTE -- THIS BIT INDICATES THAT THERE ARE DISKETTE
;		DRIVES ON THE SYSTEM
;
;	NO OTHER REGISTERS AFFECTED
;---------------------------------------------
	ASSUME	CS:CODE,DS:DATA

 EQUIPMENT	PROC	FAR
	STI				; INTERRUPTS BACK ON
	PUSH    DS			; SAVE SEGMENT REGISTER
	MOV 	AX,DATA			; ESTABLISH ADDRESSING
	MOV 	DS,AX
	MOV	AX,EQUIP_FLAG		; GET THE CURRENT SETTINGS
	POP	DS			; RECOVER SEGMENT
	IRET				; RETURN TO CALLER
EQUIPMENT	ENDP
;--- INT 15 --------------------------------
; CASSETTE I/O
;	(AH) = 0 TURN CASSETTE MOTOR ON
;	(AH) = 1 TURN CASSETTE MOTOR OFF
;	(AH) = 2 READ 1 OR MORE 256 BYTE BLOCKS FROM CASSETTE
;		(ES,BX) = POINTER TO DATA BUFFER
;		(CX) = COUNT OF BYTES TO READ
;		ON EXIT:
;  		(ES,BX) = POINTER TO LAST BYTE READ + 1
;  		(DX) = COUNT OF BYTES ACTUALLY READ
;  		(CY) = 0 IF NO ERROR OCCURRED
;  		     = 1 IF ERROR OCCURRED
;  		(AH) = ERROR RETURN IF (CY)= 1
;  			= 01 IF CRC ERROR WAS DETECTED
;  			= 02 IF DATA TRANSITIONS ARE LOST
;  			= 04 IF NO DATA WAS FOUND
;  	(AH) = 3 WRITE 1 OR MORE 256 BYTE BLOCKS TO CASSETTE
;  		(ES,BX) = POINTER TO DATA BUFFER
;  		(CX) = COUNT OF BYTES TO WRITE
;  		ON EXIT:
;		(EX,BX) = POINTER TO LAST BYTE WRITTEN + 1
;		(CX) = 0
;	(AH) = ANY OTHER THAN ABOVE VALUES CAUSES (CY)= 1
;		AND (AH)= 80 TO BE RETURNED (INVALID COMMAND).
;--------------------------------------------
	ASSUME	DS:DATA,ES:NOTHING,SS:NOTHING,CS:CODE
CASSETTE_IO	PROC	FAR
	STI				; INTERRUPTS BACK ON
	PUSH   DS			; ESTABLISH ADDRESSING TO DATA
	PUSH   AX
	MOV    AX,DATA
	MOV    DS,AX
	AND    BIOS_BREAK, 7FH		; MAKE SURE BREAK FLAG IS OFF
	POP    AX
	CALL   W1			; CASSETTE_IO_CONT
	POP    DS
	RET    2			; INTERRUPT RETURN
CASSETTE_IO	ENDP
W1	PROC	NEAR
;--------------------------------------------
; PURPOSE:
;  TO CALL APPROPRIATE ROUTINE DEPENDING ON REG AH
;
;  AH		ROUTINE
;--------------------------------------------
;  0   		MOTOR ON
;  1    	MOTOR OFF
;  2		READ CASSETTE BLOCK
;  3		WRITE CASSETTE BLOCK
;--------------------------------------------

	OR  	AH,AH   		;TURN ON MOTOR?
	JZ  	MOTOR_ON    		;YES, DO IT
	DEC 	AH	 		;TURN OFF MOTOR?
	JZ  	MOTOR_OFF   		;YES, DO IT
	DEC 	AH	 		;READ CASSETTE BLOCK?
	JZ  	READ_BLOCK  		;YES, DO IT
	DEC 	AH	 		;WRITE CASSETTE BLOCK?
	JNZ 	W2	 		; NOT_DEFINED
	JMP 	WRITE_BLOCK 		;YES, DO IT

W2:					;COMMAND NOT DEFINED
	MOV	AH,080H 		;ERROR, UNDEFINED OPERATION
	STC	    			;ERROR FLAG
	RET
W1	ENDP

MOTOR_ON	PROC	NEAR
;--------------------------------
; PURPOSE:
;  TO TURN ON CASSETTE MOTOR
;---------------------------------
	IN	AL,PORT_B		;READ CASSETTE OUTPUT
	AND	AL,NOT 08H		; CLEAR BIT TO TURN ON MOTOR
W3:	OUT	PORT_B,AL		;WRITE IT OUT
	SUB	AH,AH			;CLEAR AH
	RET
MOTOR_ON	ENDP

MOTOR_OFF	PROC	NEAR
;----------------------------------
; PURPOSE:
; TO TURN CASSETTE MOTOR OFF
;-----------------------------------
	IN	AL,PORT_B		;READ CASSETTE OUTPUT
	OR	AL,08H			; SET BIT TO TURN OFF
	JMP	W3			;WRITE IT, CLEAR ERROR, RETURN
MOTOR_OFF	ENDP
READ_BLOCK	PROC	NEAR
;--------------------------------------------
; PURPOSE:
;  TO READ 1 OR MORE 256 BYTE BLOCKS FROM CASSETTE
;
; ON ENTRY:
;  ES IS SEGMENT FOR MEMORY BUFFER (FOR COMPACT CODE)
;  BX POINTS TO START OF MEMORY BUFFER
;  CX CONTAINS NUMBER OF BYTES TO READ
; ON EXIT:
;  BX POINTS 1 BYTE PAST LAST BYTE PUT IN MEM
;  CX CONTAINS DECREMENTED BYTE COUNT
;  DX CONTAINS NUMBER OF BYTES ACTUALLY READ
;
;  CARRY FLAG IS CLEAR IF NO ERROR DETECTED
;  CARRY FLAG IS SET IF CRC ERROR DETECTED
;--------------------------------------------
	PUSH	BX			;SAVE BX
	PUSH	CX			;SAVE CX
	PUSH	SI			; SAVE SI
	MOV	SI,7			; SET UP RETRY COUNT FOR LEADER
	CALL	BEGIN_OP		; BEGIN BY STARTING MOTOR
W4:					; SEARCH FOR LEADER
	IN	AL,PORT_C		;GET INTIAL VALUE
	AND	AL,010H			;MASK OFF EXTRANEOUS BITS
	MOV	LAST_VAL,AL		;SAVE IN LOC LAST_VAL
	MOV	DX,16250		; # OF TRANSITIONS TO LOOK FOR

W5: 					; WAIT_FOR_EDGE
	TEST	BIOS_BREAK,80H		; CHECK FOR BREAK KEY
	JZ	W6			; JUMP IF NO BREAK KEY
	JMP	W17			; JUMP IF BREAK KEY HIT

W6:	DEC	DX
	JNZ	W7			; JUMP IF BEGINNING OF LEADER
	JMP	W17			; JUMP IF NO LEADER FOUND
	
W7:	CALL	READ_HALF_BIT		;IGNORE FIRST EDGE
	JCXZ	W5			; JUMP IF NO EDGE DETECTED
	MOV	DX,0378H		; CHECK FOR HALF BITS
	MOV	CX,200H			;MUST HAVE AT LEAST THIS MANY ONE SIZE
					;PULSES BEFORE CHECKNG FOR SYNC BIT (0)
	IN	AL,021H			; INTERRUPT MASK REGISTER
	OR	AL,1			; DISABLE TIMER INTERRUPTS
	OUT	021H,AL
W8:					; SEARCH_LDR
	TEST	BIOS_BREAK,80H		; CHECK FOR BREAK KEY
	JNZ	W17			; JUMP IF BREAK KEY HIT
	PUSH	CX			;SAVE REG CX
	CALL	READ_HALF_BIT		;GET PULSE WIDTH
	OR	CX,CX			; CHECK FOR TRANSITION
	POP	CX			;RESTORE ONE BIT COUNTER
	JZ	W4			; JUMP IF NO TRANSITION
	CMP	DX,BX			;CHECK PULSE WIDTH
	JCXZ	W9			;IF CX=0 THEN WE CAN LOOK
			 		;FOR SYNC BIT (0)
	JNC	W4			; JUMP IF ZERO BIT (NOT GOOD LEADER)
	LOOP	W8			;DEC CX AND READ ANOTHER HALF ONE BIT
W9:				   	; FIND_SYNC
	JC	W8			; JUMP IF ONE BIT (STILL LEADER)

; A SYNCH BIT HAS BEEN FOUND. READ SYN CHARACTER:

	CALL	READ_HALF_BIT		;SKIP OTHER HALF OF SYNC BIT (0)
	CALL	READ_BYTE		; READ SYN BYTE
	CMP	AL,16H			; SYNCHRONIZATION CHARACTER
	JNE	W16			; JUMP IF BAD LEADER FOUND.

;------ GOOD CRC SO READ DATA BLOCK(S)
	POP	SI			; RESTORE REGS
	POP	CX
	POP	BX
;--------------------------------------------
;  READ 1 OR MORE 256 BYTE BLOCKS FROM CASSETTE
;
; ON ENTRY:
;  ES IS SEGMENT FOR MEMORY BUFFER (FOR COMPACT CODE)
;  BX POINTS TO START OF MEMORY BUFFER
;  CX CONTAINS NUMBER OF BYTES TO READ
; ON EXIT:
;  BX POINTS 1 BYTE PAST LAST BYTE PUT IN MEM
;  CX CONTAINS DECREMENTED BYTE COUNT
;  DX CONTAINS NUMBER OF BYTES ACTUALLY READ
;--------------------------------------------
	PUSH	CX			;SAVE BYTE COUNT
W10:					;COME HERE BEFORE EACH
					;256 BYTE BLOCK IS READ
	MOV	CRC_REG,0FFFFH		;INIT CRC REG
	MOV	DX,256			;SET CX TO DATA BLOCK SIZE
W11:					; RD_BLK
 	TEST	BIOS_BREAK,80H		; CHECK FOR BREAK KEY
 	JNZ	W13			; JUMP IF BREAK KEY HIT
 	CALL	READ_BYTE		;READ BYTE FROM CASSETTE
 	JC	W13			;CY SET INDICATES NO DATA TRANSITIONS
 	JCXZ	W12			;IF WE'VE ALREADY REACHED
		   			;END OF MEMORY BUFFER
		    			;SKIP REST OF BLOCK
 	MOV	ES:[BX],AL		;STORE DATA BYTE AT BYTE PTR
 	INC	BX   			;INC BUFFER PTR
 	DEC	CX 			;DEC BYTE CCUNTER
W12:		  			; LOOP UNTIL DATA BLOCK HAS BEEN READ FROM CASSETTE.
 	DEC	DX   			;DEC BLOCK CNT
 	JG	W11			; RD_BLK
	CALL    READ_BYTE   		;NOW READ TWO CRC BYTES
	CALL    READ_BYTE
	SUB 	AH,AH		 	;CLEAR AN
	CMP 	CRC_REG,1D0FH   	;IS THE CRC CORRECT
	JNE	W14 			;IF NOT EQUAL CRC IS BAD
	JCXZ    W15	    		;IF BYTE COUNT IS ZERO
					;THEN WE HAVE READ ENOUGH
 					;SO WE WILL EXIT
	JMP 	W10 			;STILL MORE, SO READ ANOTHER BLOCK
W13:					;MISSING_DATA
 					;NO DATA TRANSITIONS SO
	MOV 	AH,01H			;SET AH=02 TO INDICATE
 					;DATA TIMEOUT
W14:					; BAD_CRC
	INC 	AH  			;EXIT EARLY ON ERROR
					;SET AH=01 TO INDICATE CRC ERROR
W15:					; RD_BLK_EX
	POP 	DX  			;CALCULATE COUNT OF
	SUB 	DX,CX		 	;DATA BYTES ACTUALLY READ
 					;RETURN COUNT IN REG DX
	PUSH    AX			;SAVE AX (RET CODE)
	TEST    AH,03H			; CHECK FOR ERRORS
	JNZ 	W18 			; JUMP IF ERROR DETECTED
	CALL    READ_BYTE   		;READ TRAILER
	JMP 	SHORT W18	  	;SKIP TO TURN OFF MOTOR
W16:					; BAD_LEADER
	DEC 	SI  			; CHECK RETRIES
	JZ	W17 			; JUMP IF TOO MANY RETRIES
	JMP	W4 			; JUMP IF NOT TOO MANY RETRIES
W17: 					;NO VALID DATA FOUND
;------ NO DATA FROM CASSETTE ERROR, I.E. TIMEOUT

	POP	SI			; RESTORE REGS
	POP	CX			;RESTORE REGS
	POP	BX
	SUB	DX,DX			;ZERO NUMBER OF BYTES READ
	MOV	AH,04H			;TIME OUT ERROR (NO LEADER)
	PUSH	AX
W18:					; MOT_OFF
	IN	AL,021H			; RE_ENABLE INTERRUPTS
	AND	AL,0FFH-1
	OUT	021H,AL
	CALL	MOTOR_OFF		;TURN OFF MOTOR
	POP	AX			;RESTORE RETURN CODE
	CMP	AH,01H			;SET CARRY IF ERROR (AH>0)
	CMC
	RET				;FINISHED
READ_BLOCK	ENDP
;------------------------------------------
READ_BYTE	PROC	NEAR
; PURPOSE:
;  TO READ A BYTE FROM CASSETTE
;
; ON EXIT REG AL CONTAINS READ DATA BYTE
;-------------------------------------------
	PUSH	BX 			;SAVE REGS BX,CX
	PUSH	CX
	MOV	CL,8H			; SET BIT COUNTER FOR 8 BITS
W19:		  			; BYTE_ASM
	PUSH	CX 			; SAVE CX
;-------------------------------------------
;  READ DATA BIT FROM CASSETTE
;-------------------------------------------
	CALL	READ_HALF_BIT	  	;READ ONE PULSE
	JCXZ	W21			;IF CX=0 THEN TIMEOUT
			  		;BECAUSE OF NO DATA TRANSITIONS
	PUSH	BX			;SAVE 1ST HALF BIT'S
			  		;PULSE WIDTH (IN BX)
	CALL	READ_HALF_BIT	  	;READ COMPLEMENTARY PULSE
	POP 	AX		   	;COMPUTE DATA BIT
	JCXZ	W21			;IF CX=0 THEN TIMEOUT DUE TO
			  		;NO DATA TRANSITIONS
	ADD 	BX,AX			;PERIOD
	CMP 	BX,06F0H   		; CHECK FOR ZERO BIT
	CMC		   		; CARRY IS SET IF ONE BIT
	LAHF				;SAVE CARRY IN AH
	POP 	CX			;RESTORE CX
					;NOTE:
			  		; MS BIT OF BYTE IS READ FIRST.
			  		; REG CH IS SHIFTED LEFT WITH
			  		; CARRY BEING INSERTED INTO LS
					;   BIT OF CH.
			  		; AFTER ALL 8 BITS HAVE BEEN
			  		;   READ, THE MS BIT QF THE DATA BYTE
			  		;   WILL BE IN THE MS BIT OF REG CH
 	RCL 	CH,1	  		;ROTATE REG CH LEFT WITH CARRY TO
					;  LS BIT OF REG CH
	SAHF				;RESTORE CARRY FOR CRC ROUTINE
	CALL	CRC_GEN		   	;GENERATE CRC FOR BIT
	DEC 	CL			;LOOP TILL ALL 8 BITS OF DATA
			  		;ASSEMBLED IN REG CH
	JNZ 	W19			; BYTE_ASM
	MOV 	AL,CH	  		;RETURN DATA BYTE IN REG AL
	CLC
W20:					; RD_BYT_EX
	POP 	CX		 	;RESTORE REGS CX,BX
	POP 	BX
	RET				;FINISHED
W21:					; NO_DATA
	POP 	CX			;RESTORE CX
	STC			   	;INCICATE ERROR
	JMP	W20			; RD_BYT_EX
READ_BYTE	ENDP
;--------------------------------------------
READ_HALF_BIT	PROC	NEAR
; PURPOSE
;  TO COMPUTE TIME TILL NEXT DATA
;  TRANSITION (EDGE)
;		
; ON ENTRY:
;  EDGE_CNT CONTAINS LAST EDGE COUNT
;
; ON EXIT:
;  AX CONTAINS OLD LAST EDGE COUNT
;  BX CONTAINS PULSE WIDTH (HALF BIT)
;----------------------------------------
	MOV 	CX,100   		; SET TIME TO WAIT FOR BIT
	MOV 	AH,LAST_VAL		;GET PRESENT INPUT VALUE
W22:					; RD_H_BIT
	IN  	AL,PORT_C		;INPUT DATA BIT
	AND 	AL,010H   		;MASK OFF EXTRANEOUS BITS
	CMP 	AL,AH			;SAME AS BEFORE?
	LOOPE	W22			;LOOP TILL IT CHANGES
	MOV 	LAST_VAL,AL		;UPDATE LAST_VAL WITH NEW VALUE
	MOV 	AL,0	 		;READ TIMER'S COUNTER COMMAND
	OUT 	TIM_CTL,AL		;LATCH COUNTER
	IN 	AL,TIMER0		;GET LS BYTE
	MOV 	AH,AL			;SAVE IN AH
	IN  	AL,TIMER0		;GET MS BYTE
	XCHG	AL,AH			;XCHG AL,AH
	MOV 	BX,EDGE_CNT		;BX GETS LAST EDGE COUNT
	SUB 	BX,AX			;SET BX EQUAL TO HALF BIT PERIOD
	MOV 	EDGE_CNT,AX		;UPDATE EDGE COUNT
	RET
READ_HALF_BIT	ENDP
;-----------------------------------------
WRITE_BLOCK	PROC	NEAR
;
; WRITE 1 OR MORE 256 BYTE BLOCKS TO CASSETTE.
;	THE DATA IS PADDED TO FILL OUT THE LAST 256 BYTE BLOCK.
;
;  ON ENTRY:
;   BX POINTS TO MEMORY BUFFER ADDRESS
;   CX CONTAINS NUMBER OF BYTES TO WRITE
;
;  ON EXIT:
;   BX POINTS 1 BYTE PAST LAST BYTE WRITTEN TO CASSETTE
;   CX IS ZERO
;--------------------------------------------
	PUSH	BX
	PUSH	CX
	IN	AL,PORT_B		;DISABLE SPEAKER
	AND	AL,NOT 02H
	OR	AL,01H			; ENABLE TIMER
	OUT	PORT_B,AL
	MOV	AL,0B6H			; SET UP TIMER -- MODE 3 SOUARE WAVE
	OUT	TIM_CTL,AL
	CALL	BEGIN_OP		; START MOTOR AND DELAY
	MOV	AX,1184			; SET NORMAL BIT SIZE
	CALL	W31	 		; SET_TIMER
	MOV	CX,0800H		;SET CX FOR LEADER BYTE COUNT
W23:					; WRITE LEADER
	STC		   		; WRITE ONE BITS
	CALL	WRITE_BIT		;
	LOOP	W23			; LOOP 'TIL LEADER IS WRITTEN
	CLC				;WRITE SYNC BIT (0)
	CALL	WRITE_BIT
	POP	CX			;RESTORE REGS CX,BX
	POP	BX
	MOV	AL,16H			; WRITE SYN CHARACTER
	CALL	WRITE_BYTE	    	;

;-------------------------------------------
;  WRITE 1 OR MORE 256 BYTE BLOCKS TO CASSETTE
;
;  ON ENTRY:
;   BX POINTS TO MEMORY BUFFER ADDRESS
;   CX CONTAINS NUMBER OF BYTES TO WRITE
;
;  ON EXIT:
;   BX POINTS 1 BYTE PAST LAST BYTE WRITTEN TO CASSETTE
;   CX IS ZERO
;------------------------------------------
WR_BLOCK:
	MOV	CRC_REG,0FFFFH	 	;INIT CRC
	MOV	DX,256 			;FOR 256 BYTES
W24:					; WR_BLK
	MOV	AL,ES:[BX] 		;READ BYTE FROM MEM
	CALL	WRITE_BYTE 		;WRITE IT TO CASSETTE
	JCXZ	W25 			;UNLESS CX=0, ADVANCE PTRS & DEC COUNT
	INC	BX 			;INC BUFFER POINTER
	DEC	CX 			;DEC BYTE COUNTER
W25:				   	; SKIP_ADV
	DEC	DX 			;DEC BLOCK CNT
	JG	W24 			;LOOP TILL 256 BYTE BLOCK
			  		; IS WRITTEN TO TAPE
;------------------ WRITE CRC --------------
;  WRITE 1'S COMPLEMENT OF CRC REG TO CASSETTE
;  WHICH IS CHECKEC FOR CORRECTNESS WHEN THE BLOCK IS READ
;
;  REG AX IS MODIFIED
;-----------------------------------------
	MOV	AX,CRC_REG		;WRITE THE ONE'S COMPLEMENT OF THE
			  		;  TWO BYTE CRC TO TAPE
	NOT	AX			;FOR 1'S COMPLEMENT
	PUSH	AX	   		;SAVE IT
	XCHG	AH,AL			;WRITE MS BYTE FIRST
	CALL	WRITE_BYTE		;WRITE IT
	POP	AX    			;GET IT BACK
	CALL	WRITE_BYTE		;NOW WRITE LS BYTE
	OR	CX,CX			;IS BYTE COUNT EXHAUSTED?
	JNZ	WR_BLOCK		;JUMP IF NOT DONE YET
	PUSH	CX    			;SAVE REG CX
	MOV	CX,32			;WRITE OUT TRAILER BITS
W26:					; TRAIL_LOOP
	STC
	CALL	WRITE_BIT
	LOOP	W26			; WRITE UNTIL TRAILER WRITTEN
	POP	CX 			;RESTORE REG CX
	MOV	AL,0B0H			; TURN TIMER2 OFF
	OUT	TIM_CTL,AL
	MOV	AX,1
	CALL	W31    			; SET_TIMER
	CALL	MOTOR_OFF		;TURN MOTOR OFF
	SUB	AX,AX			;NO ERRORS REPORTED ON WRITE OP
	RET			 	;FINISHED
WRITE_BLOCK	ENDP
;------------------------------------------
WRITE_BYTE	PROC	NEAR
;  WRITE A BYTE TO CASSETTE.
;  BYTE TO WRITE IS IN REG AL.
;--------------------------------------------
	PUSH	CX			;SAVE REGS CX,AX
	PUSH	AX
	MOV 	CH,AL	 		;AL=BYTE TO WRITE.
			  		;   (MS BIT WRITTEN FIRST)
	MOV 	CL,8	   		;FOR 8 DATA BITS IN BYTE.
			  		;   NOTE: TWO EDGES PER BIT
W27:					; DISASSEMBLE THE DATA BIT
	RCL 	CH,1	   		;ROTATE MS BIT INTO CARRY
	PUSHF				;SAVE FLAGS.
			 		;   NOTE: DATA BIT IS IN CARRY
	CALL	WRITE_BIT		;WRITE DATA BIT
	POPF		  		;RESTORE CARRY FOR CRC CALC
	CALL	CRC_GEN			;COMPUTE CRC ON DATA BIT
	DEC 	CL			;LOOP TILL ALL 8 BITS DONE
	JNZ 	W27	    		; JUMP IF NOT DONE YET
	POP 	AX			;RESTORE REGS AX,CX
	POP 	CX
	RET				;WE ARE FINISHED
WRITE_BYTE	ENDP
;-------------------------------------
WRITE_BIT	PROC	NEAR
; PURPOSE:
;
;  TO WRITE A DATA BIT TO CASSETTE
;  CARRY FLAG CONTAINS DATA BIT
;  I.E. IF SET   DATA BIT IS A ONE
;	IF CLEAR DATA BIT IS A ZERO
;
;  NOTE: TWO EDGES ARE WRITTEN PER BIT
;	 ONE BIT HAS 500 USEC BETWEEN EDGES
;	     FOR A 1000 USEC PERIOD (1 MILLISEC)
;
;	 ZERO BIT HAS 250 USEC BETWEEN EDGES
;	     FOR A 500 USEC PERIOD (.5 MILLISEC)
; CARRY FLAG IS DATA BIT
;------------------------------------------
	 				;ASSUME IT'S A '1'
	MOV	AX,1184			; SET AX TO NOMINAL ONE SIZE
	JC	W28			; JUMP IF ONE BIT
	MOV	AX,592			; NO, SET TO NOMINAL ZERO SIZE
W28:					; WRITE_BIT_AX
	PUSH	AX			;WRITE BIT WITH PERIOD EQ TO VALUE AX
W29:
	IN	AL,PORT_C		;INPUT TIMER_0 OUTPUT
	AND	AL,020H
	JZ	W29 			;LOOP TILL HIGH
W30:
	IN	AL,PORT_C		;NOW WAIT TILL TIMER'S OUTPUT IS LOW
	AND	AL,020H
	JNZ	W30
		    			;RELOAD TIMER WITH PERIOD
			 		;FOR NEXT DATA BIT
	POP	AX			;RESTORE PERIOD COUNT
W31:					; SET TIMER
	OUT	042H,AL			; SET LOW BYTE OF TIMER 2
	MOV	AL,AH
	OUT	042H,AL			; SET HIGH BYTE OF TIMER 2
	RET
WRITE_BIT	ENDP
;----------------------------------------
CRC_GEN		PROC	NEAR
;  UPDATE CRC REGISTER WITH NEXT DATA BIT
;
;  CRC IS USED TO DETECT READ ERRORS
;
;  ASSUMES DATA BIT IS IN CARRY
;
;  REG AX IS MODIFIED
;  FLAGS ARE MODIFIED
;----------------------------------------
	MOV	AX,CRC_REG
					;THE FOLLOWING INSTUCTIONS
					;WILL SET THE OVERFLOW FLAG
					;IF CARRY AND MS BIT OF CRC
					;ARE UNEQUAL
	RCR	AX,1
	RCL	AX,1
	CLC				;CLEAR CARRY
    	JNO 	W32			;SKIP IF NO OVERFLOW
					;IF DATA BIT XORED WITH
					; CRC REG BIT 15 IS ONE
	XOR	AX,0810H		;THEN XOR CRC REG WITH
					; 0810H
	STC				;SET CARRY
W32:
	RCL	AX,1			;ROTATE CARRY (DATA BIT)
					; INTO CRC REG
	MOV	CRC_REG,AX		;UPDATE CRC_REG
	RET				;FINISHED
CRC_GEN		ENDP
;--------------------------------------------
BEGIN_OP	PROC	NEAR		; START TAPE AND DELAY
;
;--------------------------------------------
	CALL	MOTOR_ON		;TURN ON MOTOR
	MOV	BL,42H			;DELAY FOR TAPE DRIVE
					;TO GET UP TO SPEED (1/2 SEC)
W33:
  	MOV	CX,700H			;INNER LOOP= APPROX. 10 MILLISEC
W34:	LOOP	W34
	DEC	BL
	JNZ	W33
	RET
BEGIN_OP	ENDP
;----------------------------------------
; CHARACTER GENERATOR GRAPHICS FOR 320X200 AND 640X200 GRAPHICS
;----------------------------------------
CRT_CHAR_GEN	LABEL	BYTE

	DB 000H,000H,000H,000H,000H,000H,000H,000H ; D_00
	DB 07EH,081H,0A5H,081H,0BDH,099H,081H,07EH ; D_01
	DB 07EH,0FFH,0DBH,0FFH,0C3H,0E7H,0FFH,07EH ; D_02
	DB 06CH,0FEH,0FEH,0FEH,07CH,038H,010H,000H ; D_03
	DB 010H,038H,07CH,0FEH,07CH,038H,010H,008H ; D_04
	DB 038H,07CH,038H,0FEH,0FEH,07CH,038H,07CH ; D_05
	DB 010H,010H,038H,07CH,0FEH,07CH,038H,07CH ; D_06
	DB 000H,000H,018H,03CH,03CH,018H,000H,000H ; D_07
	DB 0FFH,0FFH,0E7H,0C3H,0C3H,0E7H,0FFH,0FFH ; D_08
	DB 000H,03CH,066H,042H,042H,066H,03CH,000H ; D_09
	DB 0FFH,0C3H,099H,0BDH,0BDH,099H,0C3H,0FFH ; D_0A
	DB 00FH,007H,00FH,07DH,0CCH,0CCH,0CCH,078H ; D_08
	DB 03CH,066H,066H,066H,03CH,018H,07EH,018H ; D_0C
	DB 03FH,033H,03FH,030H,030H,070H,0F0H,0E0H ; D_0D
	DB 07FH,063H,07FH,063H,063H,067H,0E6H,0C0H ; D_0E
	DB 099H,05AH,03CH,0E7H,0E7H,03CH,05AH,099H ; D_0F

	DB 080H,0E0H,0F8H,0FEH,0F8H,0E0H,080H,000H ; D_10
	DB 002H,00EH,03EH,0FEH,03EH,00EH,002H,000H ; D_11
	DB 018H,03CH,07EH,018H,018H,07EH,03CH,018H ; D_12
	DB 066H,066H,066H,066H,066H,000H,066H,000H ; D_13
	DB 07FH,0DBH,0DBH,07BH,01BH,01BH,01BH,000H ; D_14
	DB 03EH,063H,038H,06CH,06CH,038H,0CCH,078H ; D_15
	DB 000H,000H,000H,000H,07EH,07EH,07EH,000H ; D_16
	DB 018H,03CH,07EH,018H,07EH,03CH,018H,0FFH ; D_17
	DB 018H,03CH,07EH,018H,018H,018H,018H,000H ; D_18
	DB 018H,018H,018H,018H,07EH,03CH,018H,000H ; D_19
	DB 000H,018H,00CH,0FEH,00CH,018H,000H,000H ; D_1A
	DB 000H,030H,060H,0FEH,060H,030H,000H,000H ; D_1B
	DB 000H,000H,0C0H,0C0H,0C0H,0FEH,000H,000H ; D_1C
	DB 000H,024H,066H,0FFH,066H,024H,000H,000H ; D_1D
	DB 000H,018H,03CH,07EH,0FFH,0FFH,000H,000H ; D_1E
	DB 000H,0FFH,0FFH,07EH,03CH,018H,000H,000H ; D_1F

	DB 000H,000H,000H,000H,000H,000H,000H,000H ; SP D_20
	DB 030H,078H,078H,030H,030H,000H,030H,000H ; ! D_21
	DB 06CH,06CH,06CH,000H,000H,000H,000H,000H ; " D_22
	DB 06CH,06CH,0FEH,06CH,0FEH,06CH,06CH,000H ; # D_23
	DB 030H,07CH,0C0H,078H,00CH,0F8H,030H,000H ; $ D_24
	DB 000H,0C6H,0CCH,018H,030H,066H,0C6H,000H ; PER CENT D_25
	DB 038H,06CH,038H,076H,0DCH,0CCH,076H,000H ; & D_26
	DB 060H,060H,0C0H,000H,000H,000H,000H,000H ; ' D_27
	DB 018H,030H,060H,060H,060H,030H,018H,000H ; ( D_28
	DB 060H,030H,018H,018H,018H,030H,060H,000H ; ) D_29
	DB 000H,066H,03CH,0FFH,03CH,066H,000H,000H ; * D_2A
	DB 000H,030H,030H,0FCH,030H,030H,000H,000H ; + D_2B
	DB 000H,000H,000H,000H,000H,030H,030H,060H ; , D_2C
	DB 000H,000H,000H,0FCH,000H,000H,000H,000H ; - D_2D
	DB 000H,000H,000H,000H,000H,030H,030H,000H ; . D_2E
	DB 006H,00CH,018H,030H,060H,0C0H,080H,000H ; / D_2F

	DB 07CH,0C6H,0CEH,0DEH,0F6H,0E6H,07CH,000H ; 0 D_30
	DB 030H,070H,030H,030H,030H,030H,0FCH,000H ; 1 D_31
	DB 078H,0CCH,00CH,038H,060H,0CCH,0FCH,000H ; 2 D_32
	DB 078H,0CCH,00CH,038H,00CH,0CCH,078H,000H ; 3 D_33
	DB 01CH,03CH,06CH,0CCH,0FEH,00CH,01EH,000H ; 4 D_34
	DB 0FCH,0C0H,0F8H,00CH,00CH,0CCH,078H,000H ; 5 D_35
	DB 038H,060H,0C0H,0F8H,0CCH,0CCH,078H,000H ; 6 D_36
	DB 0FCH,0CCH,00CH,018H,030H,030H,030H,000H ; 7 D_37
	DB 078H,0CCH,0CCH,078H,0CCH,0CCH,078H,000H ; 8 D_38
	DB 078H,0CCH,0CCH,07CH,00CH,018H,070H,000H ; 9 D_39
	DB 000H,030H,030H,000H,000H,030H,030H,000H ; : D_3A
 	DB 000H,030H,030H,000H,000H,030H,030H,060H ; ; D_3B
	DB 018H,030H,060H,0C0H,060H,030H,018H,000H ; < D_3C
	DB 000H,000H,0FCH,000H,000H,0FCH,000H,000H ; = D_3D
	DB 060H,030H,018H,00CH,018H,030H,060H,000H ; > D_3E
	DB 078H,0CCH,00CH,018H,030H,000H,030H,000H ; ? D_3F

	DB 07CH,0C6H,0DEH,0DEH,0DEH,0C0H,078H,000H ; @ D_40
	DB 030H,078H,0CCH,0CCH,0FCH,0CCH,0CCH,000H ; A D_41
	DB 0FCH,066H,066H,07CH,066H,066H,0FCH,000H ; B D_42
	DB 03CH,066H,0C0H,0C0H,0C0H,066H,03CH,000H ; C D_43
	DB 0F8H,06CH,066H,066H,066H,06CH,0F8H,000H ; D D_44
	DB 0FEH,062H,068H,078H,068H,062H,0FEH,000H ; E D_45
	DB 0FEH,062H,068H,078H,068H,060H,0F0H,000H ; F D_46
	DB 03CH,066H,0C0H,0C0H,0CEH,066H,03EH,000H ; G D_47
	DB 0CCH,0CCH,0CCH,0FCH,0CCH,0CCH,0CCH,000H ; H D_48
	DB 078H,030H,030H,030H,030H,030H,078H,000H ; I D_49
	DB 01EH,00CH,00CH,00CH,0CCH,0CCH,078H,000H ; J D_4A
	DB 0E6H,066H,06CH,078H,06CH,066H,0E6H,000H ; K D_4B
	DB 0F0H,060H,060H,060H,062H,066H,0FEH,000H ; L D_4C
	DB 0C6H,0EEH,0FEH,0FEH,0D6H,0C6H,0C6H,000H ; M D_4C
	DB 0C6H,0E6H,0F6H,0DEH,0CEH,0C6H,0C6H,000H ; N D_4E
	DB 038H,06CH,0C6H,0C6H,0C6H,06CH,038H,000H ; O D_4F

	DB 0FCH,066H,066H,07CH,060H,060H,0F0H,000H ; P D_50
	DB 078H,0CCH,0CCH,0CCH,0DCH,078H,01CH,000H ; Q D_51
	DB 0FCH,066H,066H,07CH,06CH,066H,0E6H,000H ; R D_52
	DB 078H,0CCH,0E0H,070H,01CH,0CCH,078H,000H ; S D_53
	DB 0FCH,0B4H,030H,030H,030H,030H,078H,000H ; T D_54
	DB 0CCH,0CCH,0CCH,0CCH,0CCH,0CCH,0FCH,000H ; U D_55
	DB 0CCH,0CCH,0CCH,0CCH,0CCH,078H,030H,000H ; V D_56
	DB 0C6H,0C6H,0C6H,0D6H,0FEH,0EEH,0C6H,000H ; W D_57
	DB 0C6H,0C6H,06CH,038H,038H,06CH,0C6H,000H ; X D_58
	DB 0CCH,0CCH,0CCH,078H,030H,030H,078H,000H ; Y D_59
	DB 0FEH,0C6H,08CH,018H,032H,066H,0FEH,000H ; Z D_5A
	DB 078H,060H,060H,060H,060H,060H,078H,000H ; [ D_5B
	DB 0C0H,060H,030H,018H,00CH,006H,002H,000H ; BACKSLASH D_5C
	DB 078H,018H,018H,018H,018H,018H,078H,000H ; ] D_5D
	DB 010H,038H,06CH,0C6H,000H,000H,000H,000H ; CIRCUMFLEX	D_5E
	DB 000H,000H,000H,000H,000H,000H,000H,0FFH ; _ D_5F
    
 	DB 030H,030H,018H,000H,000H,000H,000H,000H ;   D_60
 	DB 000H,000H,078H,00CH,07CH,0CCH,076H,000H ; LOWER CASE	A D_61
 	DB 0E0H,060H,060H,07CH,066H,066H,0DCH,000H ; L.C. B D_62
 	DB 000H,000H,078H,0CCH,0C0H,0CCH,078H,000H ; L.C. C D_63
 	DB 01CH,00CH,00CH,07CH,0CCH,0CCH,076H,000H ; L.C. C D_64
 	DB 000H,000H,078H,0CCH,0FCH,0C0H,078H,000H ; L.C. E D_65
 	DB 038H,06CH,060H,0F0H,060H,060H,0F0H,000H ; L.C. F D_66
	DB 000H,000H,076H,0CCH,0CCH,07CH,00CH,0F8H ; L.C. G D_67
 	DB 0E0H,060H,06CH,076H,066H,066H,0E6H,000H ; L.C. H D_68
	DB 030H,000H,070H,030H,030H,030H,078H,000H ; L.C. I D_69
 	DB 00CH,000H,00CH,00CH,00CH,0CCH,0CCH,078H ; L.C. J D_6A
	DB 0E0H,060H,066H,06CH,078H,06CH,0E6H,000H ; L.C. K D_6B
	DB 070H,030H,030H,030H,030H,030H,078H,000H ; L.C. L D_6C
	DB 000H,000H,0CCH,0FEH,0FEH,0D6H,0C6H,000H ; L.C. M D_6D
	DB 000H,000H,0F8H,0CCH,0CCH,0CCH,0CCH,000H ; L.C. N D_6E
	DB 000H,000H,078H,0CCH,0CCH,0CCH,078H,000H ; L.C. O D_6F

	DB 000H,000H,0DCH,066H,066H,07CH,060H,0F0H ; L.C. P D_70
	DB 000H,000H,076H,0CCH,0CCH,07CH,00CH,01EH ; L.C. Q D_71
	DB 000H,000H,0DCH,076H,066H,060H,0F0H,000H ; L.C. R D_72
	DB 000H,000H,07CH,0C0H,078H,00CH,0F8H,000H ; L.C. S D_73
	DB 010H,030H,07CH,030H,030H,034H,018H,000H ; L.C. T D_74
 	DB 000H,000H,0CCH,0CCH,0CCH,0CCH,076H,000H ; L.C. U D_75
 	DB 000H,000H,0CCH,0CCH,0CCH,078H,030H,000H ; L.C. V D_76
 	DB 000H,000H,0C6H,0D6H,0FEH,0FEH,06CH,000H ; L.C. W D_77
 	DB 000H,000H,0C6H,06CH,038H,06CH,0C6H,000H ; L.C. X D_78
 	DB 000H,000H,0CCH,0CCH,0CCH,07CH,00CH,0F8H ; L.C. Y D_79
 	DB 000H,000H,0FCH,098H,030H,064H,0FCH,000H ; L.C. Z D_7A
    	DB 01CH,030H,030H,0E0H,030H,030H,01CH,000H ;   D_7B
	DB 018H,018H,018H,000H,018H,018H,018H,000H ;   D_7C
	DB 0E0H,030H,030H,01CH,030H,030H,0E0H,000H ;   D_7D
	DB 076H,0DCH,000H,000H,000H,000H,000H,000H ;   D_7E
	DB 000H,010H,038H,06CH,0C6H,0C6H,0FEH,000H ; DELTA D_7F

;--- INT 1A -------------------------------
; TIME_OF_DAY
;  THIS ROUTINE ALLOWS THE CLOCK TO BE SET/READ
;
; INPUT
;   (AH) = 0	READ THE CURRENT CLOCK SETTING
;		RETURNS CX = HIGH PORTION OF COUNT
;			DX = LOW PORTION OF COUNT
;			AL = 0 IF TIMER HAS NOT PASSED 24 HOURS SINCE LAST READ
;			   <>0 IF ON ANOTHER DAY
;   (AH) = 1	SET THE CURRENT CLOCK
;	CX = HIGH PORTION OF COUNT
;	DX = LOW PORTION OF COUNT
; NOTE: COUNTS OCCUR AT THE RATE OF 1193180/65536 COUNTS/SEC
;	(OR ABOUT 18.2 PER SECOND -- SEE EQUATES BELOW)
;--------------------------------------------
	ASSUME	CS:CODE,DS:DATA
TIME_OF_DAY	PROC	FAR
	STI		 		; INTERRUPTS BACK ON
	PUSH	DS	 		; SAVE SEGMENT
	PUSH	AX	 		; SAVE PARM
	MOV	AX,DATA
	MOV	DS,AX			; ESTABLISH ADDRESSING TO VALUES
	POP	AX			; GET BACK INPUT PARM
	OR	AH,AH	   		; AH=0
	JZ	T2			; READ_TIME
	DEC	AH		 	; AH=1
	JZ	T3	  		; SET_TIME
T1:			 		; TOD_RETURN
	STI				; INTERRUPTS BACK ON
	POP	DS			; RECOVER SEGMENT
	IRET				; RETURN TO CALLER
	
T2:			 		; READ_TIME
	CLI				; NO TIMER INTERRUPTS WHILE READING
	MOV	AL,TIMER_OFL
	MOV	TIMER_OFL,0		; GET OVERFLOW, AND RESET THE FLAG
	MOV	CX,TIMER_HIGH
	MOV	DX,TIMER_LOW
	JMP	T1			; TOD_RETURN
	
T3:			   		; SET_TIME
	CLI				; NO INTERRUPTS WHILE HRITING
	MOV	TIMER_LOW,DX
	MOV	TIMER_HIGH,CX		; SET THE TIME
	MOV	TIMER_OFL,0		; RESET OVERFLOW
	JMP	T1			; TOD_RETURN
TIME_OF_DAY	ENDP
;--------------------------------------------
; THIS ROUTINE HANDLES THE TIMER INTERRUPT FROM
; CHANNEL 0 OF THE 8253 TIMER. INPUT FREQUENCY IS 1.19318 MHZ
; AND THE DIVISOR IS 65536, RESULTING IN APPROX. 18.2 INTERRUPTS
; EVERY SECOND.
;
; THE INTERRUPT HANDLER MAINTAINS A COUNT OF INTERRUPTS SINCE POWER
;  ON TIME, WHICH MAY BE USED TO ESTABLISH TIME OF DAY.
; THE INTERRUPT HANDLER ALSO DECREMENTS THE MOTOR CONTROL COUNT
;  OF THE DISKETTE, AND WHEN IT EXPIRES, WILL TURN OFF THE DISKETTE
;  MOTOR, 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	PROC	FAR
	STI				; INTERRUPTS BACK ON
	PUSH	DS
	PUSH	AX
	PUSH	DX   			; SAVE MACHINE STATE
	MOV	AX,DATA
	MOV	DS,AX			; ESTABLISH ADDRESSABILITY
	INC	TIMER_LOW		; INCREMENT TIME
	JNZ	T4    			; TEST_DAY
	INC	TIMER_HIGH		; INCREMENT HIGH WORD OF TIME
T4:					; TEST_DAY
	CMP	TIMER_HIGH,018H 	; TEST FOR COUNT EQUALLING 24 HOURS
	JNZ 	T5    			; DISKETTE_CTL
	CMP 	TIMER_LOW,0B0H
	JNZ 	T5    			; DISKETTE_CTL
   
;------ TIMER HAS GONE 24 HOURS

	MOV	TIMER_HIGH,0
	MOV	TIMER_LOW,0
	MOV	TIMER_OFL,1
   
;------ TEST FOR DISKETTE TIME OUT

T5:					; DISKETTE_CTL
	DEC	MOTOR_COUNT
	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_RET:
	INT	1CH 			; TRANSFER CONTROL TO A USER ROUTINE
	MOV	AL,EOI
	OUT	020H,AL 		; END OF INTERRUPT TO 8259
	POP	DX
    	POP	AX
    	POP	DS 			; RESET MACHINE STATE
	IRET		 		; RETURN FROM INTERRUPT
TIMER_INT	 ENDP
;--------------------------------------------
; THESE ARE THE VECTORS WHICH ARE MOVED INTO
; THE 8086 INTEPRUPT AREA DURING POWER ON
;--------------------------------------------
VECTOR_TABLE	LABEL	WORD		; VECTOR TABLE FOR MOVE TO INTERRUPTS

	DW	OFFSET TIMER_INT 	; INTERRUPT 8
	DW	CODE

	DW	OFFSET KB_INT 		; INTERRUPT 9
	DW	CODE

	DD    	0	  		; INTERRUPT A
	DD    	0	  		; INTERRUPT B
	DD    	0	  		; INTERRUPT C
	DD    	0	  		; INTERRUPT D
   
	DW	OFFSET DISK_INT		; INTERRUPT E
	DW	CODE
	
	DD	0			; INTERRUPT F
	
	DW	OFFSET VIDEO_IO		; INTERRUPT 10H
	DW	CODE

	DW	OFFSET EQUIPMENT 	; INTERRUPT 11H
	DW	CODE
   
	DW	OFFSET MEMORY_SIZE_DETERMINE ; INT 12H
	DW	CODE
   
	DW	OFFSET DISKETTE_IO  	; INTERRUPT 13H
	DW	CODE
	
	DW	OFFSET RS232_IO		; INTERRUPT 14H
	DW	CODE

	DW	OFFSET CASSETTE_IO  	; INTERRUPT 15H
	DW	CODE
	
	DW	OFFSET KEYBOARD_IO	; INTERRUPT 16H
	DW	CODE
	
	DW	OFFSET PRINTER_IO   	; INTERRUPT 17H
	DW	CODE

   	DW	00000H			; INTERRUPT 18H
	DW	0F600H			;  ROM BASIC ENTRY POINT

	DW	OFFSET BOOT_STRAP	; INTERRUPT 19H
	DW	CODE

	DW	TIME_OF_DAY		; INTERRUPT 1AH -- TIME OF DAY
	DW	CODE

	DW	DUMMY_RETURN		; INTERRUPT 1BH -- KEYBOARD BREAK ADDR
	DW	CODE

	DW	DUMMY_RETURN		; INTERRUPT 1CH -- TIMER BREAK ADDR
	Dw	CODE

	DW	VIDEO_PARMS		; INTERRUPT 1DH -- VIDEO PARAMETERS
	DW	CODE

	DW	OFFSET DISK_BASE	; INTERRUPT 1EH -- DISK PARMS
	DW	CODE

	DD	0			; INTERRUPT 1FH -- POINTER TO VIDEO EXT

DUMMY_RETURN:
	IRET				; DUMMY RETURN FOR BREAK FROM KEYBOARD
;-- INT 5 ----------------------------------
;	THIS LOGIC WILL BE INVOKED BY INTERRUPT 05H TO FRINT
;	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 DURING THE
;	TIME THIS ROUTINE IS PRINTING IT WILL BE IGNORED.
;	ADDRESS 50:0 CONTAINS THE STATUS OF THE PRINT SCREEN:
;
;	50:0	=0	EITHER PRINT SCREEN HAS NOT BEEN CALLED
;			OR UPON RETURN FROM A CALL THIS INDICATES
;			A SUCCESSFUL OPERATION.
;
;		=1	PRINT SCREEN IS IN PROGRESS
;
;		=377	ERROR ENCOUNTERED DURING PRINTING
;--------------------------------------------
	ASSUME	CS:CODE,DS:XXDATA
    
PRINT_SCREEN	PROC	FAR
	STI				;MUST RUN WITH INTERRUPTS ENABLED
	PUSH	DS			;MUST USE 50:0 FOR DATA AREA STORAGE
	PUSH	AX
	PUSH	BX
	PUSH	CX			;WILL USE THIS LATER FOR CURSOR LIMITS
	PUSH	DX			;WILL HOLD CURRENT CURSOR POSITION
  	MOV	AX,XXDATA		;HEX 50
  	MOV	DS,AX
  	CMP	STATUS_BYTE,1		;SEE IF PRINT ALREADY IN PROGRESS
  	JZ 	EXIT    		; JUMP IF PRINT ALREADY IN PROGRESS
  	MOV	STATUS_BYTE,1	    	;INDICATE PRINT NOW IN PROGRESS
  	MOV	AH,15			;WILL REQUEST THE CURRENT SCREEN MODE
  	INT	10H			;	[AL]=MODE
  					;	[AH]=NUMBER COLUMNS/LINE
					;	[BH]=VISUAL PAGE
	;********************************************
	;	AT THIS POINT WE KNOW THE COLUMNS/LINE ARE IN
	;	[AX] AND THE PAGE IF APPLICABLE IS IN [BH]. THE STACK
	;	HAS DS,AX,BX,CX,DX PUSHED. [AL] HAS VIDEO MODE
	;
	;********************************************
	MOV	CL,AH			;WILL MAKE USE OF [CX] REGISTER TO
	MOV	CH,25		   	;CONTROL ROW & COLUMNS
	CALL	CRLF		    	;CARRIAGE RETURN LINE FEED ROUTINE
	PUSH	CX			;SAVE SCREEN BOUNDS
	MOV	AH,3		    	;WILL NOW READ THE CURSOR.
	INT	10H			;AND PRESERVE THE POSITION
	POP	CX			;RECALL SCREEN BOUNDS
	PUSH	DX			;RECALL [BH]=VISUAL PAGE
	XOR	DX,DX			;WILL SET CURSOR POSITION TO [0,0]

;********************************************
;	THE LOOP FROM PRI10 TO THE INSTRUCTION PRIOR TO PRI20
;	IS THE LOOP TO READ EACH CURSOR POSITION FROM THE SCREEN
;	AND PRINT.
;********************************************
PRI10:	MOV	AH,2			;TO INDICATE CURSOR SET REQUEST
	INT 	10H			;NEW CURSOR POSITION ESTABLISHED
	MOV 	AH,8    		;TO INDICATE READ CHARACTER
	INT 	10H			;CHARACTER NOW IN [AL]
	OR  	AL,AL   		;SEE IF VALID CHAR
	JNZ 	PRI15   		;JUMP IF VALID CHAR
	MOV 	AL,' '			;MAKE A BLANK
PRI15:
	PUSH	DX			;SAVE CURSOR POSITION
	XOR 	DX,DX   		;INDICATE PRINTER 1
	XOR 	AH,AH   		;TO INDICATE PRINT CHAR IN [AL]
	INT	17H			;PRINT THE CHARACTER
	POP 	DX	 		;RECALL CURSOR POSITION
	TEST	AH,25H			; TEST FOR PRINTER ERROR
	JNZ 	ERR10   		; JUMP IF ERROR DETECTED
	INC 	DL	 		;ADVANCE TO NEXT COLUMN
	CMP 	CL,DL   		;SEE IF AT END OF LINE
	JNZ 	PRI10   		;IF NOT PROCEED
	XOR 	DL,DL   		;BACK TO COLUMN 0
	MOV 	AH,DL   		;[AH]=0
	PUSH	DX			;SAVE NEW CURSOR POSITION
	CALL	CRLF			; LINE FEED CARRIAGE RETURN
	POP 	DX	 		;RECALL CURSOR PCSITION
	INC	DH			;ADVANCE TO NEXT LINE
	CMP 	CH,DH   		;FINISHED?
	JNZ 	PRI10   		;IF NOT CONTINUE
PRI20:	POP	DX			;RECALL CURSOR POSITION
	MOV 	AH,2    		;TO INDICATE CURSOR SET REQUEST
	INT 	10H			;CURSOR POSITION RESTORED
	MOV 	STATUS_BYTE,0		;INDICATE FINISHED
	JMP 	SHORT EXIT		;EXIT THE ROUTINE
ERR10:	POP	DX			;GET CURSOR POSITION
	MOV 	AH,2    		;TO REQUEST CURSOR SET
	INT 	10H			;CURSOR POSITION RESTORED
ERR20:	MOV	STATUS_BYTE,0FFH 	;INDICATE ERROR

EXIT: 	POP	DX			;RESTORE ALL THE REGISTERS USED
	POP 	CX
	POP 	BX
	POP 	AX
	POP 	DS
	IRET
PRINT_SCREEN	ENDP

;------ CARRIAGE RETURN, LINE FEED SUBROUTINE
 
CRLF	PROC	NEAR
	XOR	DX,DX   		;PRINTER 0
	XOR	AH,AH   		;WILL NOW SEND INITIAL LF,CR TO PRINTER
	MOV	AL,12Q  		;LF
	INT	17H			;SEND THE LINE FEED
	XOR	AH,AH   		;NOW FOR THE CR
	MOV	AL,15Q  		;CR
	INT	17H			;SEND THE CARRIAGE RETURN
	RET
CRLF   ENDP
CODE	ENDS				;segment move 11/4/97

;----------------------------------
; POWER ON RESET VECTOR
;----------------------------------
VECTOR	SEGMENT 'CODE'			;AT 0FFFFH
	ORG	0FFF0H

;------ POWER ON RESET
POR:
	JMP	FAR PTR RESET

	DB	'04/24/81'		; RELEASE MARKER
	NOP				; PLACEHOLDER (11/4/97)
	NOP				; COMPUTER_TYPE (11/4/97)
	NOP				; CHECKSUM BYTE (11/4/97)
    
VECTOR	ENDS
CODE	ENDS
END POR
