Short bio

Colin Raffel is an associate professor at the University of Toronto, an associate research director at the Vector Institute, and a faculty researcher at Hugging Face. His lab works on decentralizing, democratizing, and de-risking large-scale AI.

Longer, more effusive bio

Colin Raffel is an associate professor of computer science at the University of Toronto, an associate research director and faculty member at the Vector Institute, and a faculty researcher at Hugging Face. Prior to joining the University of Toronto, he was an assistant professor at the University of North Carolina and a research scientist at Google Brain. He received a PhD in electrical engineering and a Master's in philosophy from Columbia University, supervised by Daniel P. W. Ellis; a Master's in music, science, and technology from Stanford University's Center for Computer Research in Music and Acoustics, supervised by Julius O. Smith III; and a Bachelor's in mathematics from Oberlin College, supervised by Robert Young. His lab works on decentralizing, democratizing, and de-risking large-scale AI. His work has received, or has been a runner-up for, a TMLR outstanding paper certification, the ICBS Frontiers of Science Award, a NeurIPS outstanding paper award, the Caspar Bowden Award for Outstanding Research in Privacy Enhancing Technologies, a CACM and SIGPLAN research highlight, and a best paper award at ISMIR.

Bio of rejections

The first time he applied for PhDs, Colin was rejected from every school he applied to. On the second round, Colin got rejected from every school except for one (Columbia, where he ultimately did his PhD). When he applied to faculty positions the first time, he was also rejected by every school except for one. He was rejected by Google three times before he started working there. Probably half of his papers were rejected at least once before being published. He has also applied to many more funding opportunities than he has received, and was rejected from every award he has ever applied for.

Bio of non-professional achievements

Colin Raffel is a dad of two living in Toronto. He is a decent cook and enjoys building and riding bikes. Once, he DJed a wedding where Girl Talk was a groomsman. He also built a guitar pedal that is now owned by Adrian Utley of Portishead. The hardest outdoor bouldering route he climbed was a V6 (he once climbed a V9 indoors, but such achievements are debatable).

Bio hallucinated by a state-of-the-art LLM (generated 7 April 2025; falsehoods underlined)

Colin Raffel is a leading researcher in machine learning, specializing in natural language processing, deep learning, and transfer learning. He earned his B.Sc. in Computer Science from the University of British Columbia (2010), followed by an M.Sc. (2012) and Ph.D. (2016) in Computer Science from the University of Waterloo, advised by Jimmy Lin. After his doctorate, Raffel was a postdoctoral researcher at Google Brain (2016–2017) before joining the University of North Carolina at Chapel Hill as an Assistant Professor (2017–2019). He then returned to Google Brain as a Research Scientist (2019–2023) before moving to Hugging Face in 2023 as a Principal Investigator, while also serving as an Adjunct Assistant Professor at UNC Chapel Hill. Raffel has received several notable awards, including a Google PhD Fellowship (2014) and a Spotlight Paper Award at NeurIPS (2020) for his work on large-scale transfer learning. He is also a key contributor to major open-source projects, including TensorFlow and Hugging Face’s Transformers library, shaping modern NLP research and applications.

BIOS (source)

$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