$TITLE(ROM BIOS FOR IBM PERSONAL COMPUTER)
;------------------------
; 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 AT 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			; INTERRUPT 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
;----------------------
STACK	SEGMENT AT 30H
	DW	128 DUP(?)
TOS	LABEL	WORD
STACK	ENDS

;--------------------------------------------
; ROM BIOS DATA AREAS
;--------------------------------------------
DATA	SEGMENT AT 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 CODE 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 OPERATION 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 KEYBOARD RESET UNDERWAY
DATA	ENDS

;--------------------------------------------
; EXTRA DATA AREA
;--------------------------------------------
XXDATA	SEGMENT AT 50H
STATUS_BYTE	DB	?
XXDATA	ENDS

;--------------------------------------------
; VIDEO DISPLAY BUFFER
;--------------------------------------------
VIDEO_RAM	SEGMENT AT 0B800H
REGEN	LABEL	BYTE
REGENW	LABEL	WORD
	DB	16384 DUP(?)
VIDEO_RAM	ENDS
;--------------------------------------------
; ROM RESIDENT CODE
;--------------------------------------------
CODE	SEGMENT AT 0F000H
	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 STORAGE.
;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 REGISTERS
;	   WITH ALL ONE'S AND 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	BX,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,AUTOINT
	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 BYTES
	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 F8H
C23:					;CONTINUE IN NORMAL MODE
	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,STACK			; 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	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	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 OF 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 TIMER 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 TO 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 INTR 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	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 VIDEO ENABLE SIGNAL.
;	SELECT ALPHANUMERIC 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 VIDEO 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 TO 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,DI				;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 ADD 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 TO 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	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		; KEYBOARD 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				;ISOLATE 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 OF 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 ERROR MSG

;	TURN DRIVE 0 MOTOR OFF

F14:						; DR0_OFF:
	MOV	AL,0CH				;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 BEEPER

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 CNT 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 CODE '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 CODE RETURNED
	LOOP	G9				;NO - LOOP TILL TIMEOUT
G10:	IN	AL,PORT_A			;READ KEYBOARD SCAN CODE
	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			;POSITION 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 TIME 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 5 = 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		; COMMUNICATION 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	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	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	'qwertyuiop[]',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	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 ALREADY 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 AWAY
	JMP	K26			; THROW AWAY THAT KEYSTROKE

;------ 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 KEYSTROKE 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	K56			; YES, GO TRANSLATE CHAR
K43:					; CTL-TABLE-TRANSLATE
	MOV	BX,OFFSET K9		; CTL TABLE SCAN
	JMP	K63			; TRANSLATE_SCAN

;------ NOT IN CONTROL 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	K57			; BUFFER_FILL

K45:					; NOT-BACK-TAB
	CMP	AL,55			; PRINT SCREEN 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			; GO 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 KEYS
	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

;------ CONVERT 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	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 LOCATION

;------ 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 ADDRESS

;------ 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 OCCURRED, 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 INTERUPT 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 VALUE
	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	; INITIALIZE THE DISKETTE CHANNEL
	RET			; RETURN TO CALLER, CFL SET BY ABOVE IF ERROR
DMA_SETUP	ENDP
;---------------------------------------------
; CHK_STAT_2
;	THIS ROUTINE 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
; OUTPUT
;	CY = 0 SUCCESS
;	CY = 1 FAILURE -- ERROR IS IN DISKETTE_STATUS
;	(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			; SAVE 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 INTERRUPT.
; 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
;	NO 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 COMMUNICATION 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 FOR 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
;		(BH) = 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 ON 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

;------ C_REG_TAB
M7	LABEL	BYTE			; TABLE OF MODE SETS
	DB	2CH,28H,2DH,29H,2AH,2EH,1EH,29H

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 	; NUMBER 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 MODE
	MOV	DX,ADDR_6845	; PREPARE TO OUTPUT TO VIDEO ENABLE PORT
	ADD	DX,4
	MOV	AL,CS:[SI+OFFSET M7]
	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	AL,CS:[SI + OFFSET M6]
	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 CURSOR

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 VALUE
	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 MODE
; 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 SEGMENT
; 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 MODE
;	(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 GRAPHICS
	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 REGISTER
	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 FROM 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 FOR 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 MULTIPLYING ROW VALUE BY 40
;------ ( LOW BIT OF ROW DETERMINES EVEN/ODD, 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 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	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 FIELD
	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 FUNCTION 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	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		; ESTABLISH 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	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 INCREMENTED.  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 CURSOR
	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_PEN

;------ GRAPHICS MODE

	MOV	DL,40		; DIVISOR FOR 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 MEMORY_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 COUNTER
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 OF 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				;INDICATE 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 SQUARE 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 CHECKED 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. D 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 WRITING
	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 INTERRUPT 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 PRINT
;	THE SCREEN. THE CURSOR POSITION AT THE TIME THIS ROUTINE
;	IS INVOKED WILL BE SAVED AND RESTORED UPON COMPLETION. THE
;	ROUTINE IS INTENDED TO RUN WITH INTERRUPTS ENABLED.
;	IF A SUBSEQUENT 'PRINT SCREEN KEY IS DEPRESSED 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 POSITION
	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

;----------------------------------
; POWER ON RESET VECTOR
;----------------------------------
VECTOR	SEGMENT AT 0FFFFH

;------ POWER ON RESET

	JMP	RESET

	DB	'04/24/81'              ; RELEASE MARKER
VECTOR	ENDS
	END
