
;****************************************************************
;*								*
;*			I/O LIBRARY				*
;*								*
;****************************************************************

;			R. DAVID POGGE
;			4 SEPTEMBER 1980

			ORG 0500H

	; GLOBAL ADDRESSES

	ABORT	EQU	0008H
	INPUT	EQU	0046H
	OUTPUT	EQU	0043H
	PHEX	EQU	0426H
	PHLREG	EQU	042EH
	PLNIB	EQU	0417H
	QUOTE	EQU	0400H

	; VARIABLES

	A1	EQU	8000H
	A2	EQU	8002H
	A3	EQU	8004H
	A4	EQU	8006H
	BUFF	EQU	8032H
	SIGN	EQU	802DH






	; PRINT D/E STRING SUBROUTINE

	; THIS SUBROUTINE PRINTS THE STRING POINTED TO BY THE D/E REGISTER PAIR.

PDESTR:	LDAX D		; FETCH ASCII DATA USING POINTER D/E
	INX D		; INCREMENT POINTER FOR NEXT ASCII DATA
	CPI 0H		; CHECK FOR NULL BYTE
	RZ		; IF SO, RETURN
	CALL OUTPUT	; IF NOT, PRINT THE BYTE
	JMP PDESTR	; AND FETCH NEXT BYTE








	; INPUT STRING SUBROUTINE

	; THIS SUBROUTINE TAKES INPUT CHARACTERS FROM THE TELETYPE AND
	; STORES THEM IN THE BUFFER.

ISTRNG:	LXI H,BUFF	; LET H/L POINT TO BUFFER BEGINNING
	LXI D,BUFF	; LET D/E=H/L

GETTXT:	CALL INPUT	; GET TEXT FROM KEYBOARD
	PUSH PSW	; SAVE ASCII CHARACTER
	CALL OUTPUT	; ECHO IT TO THE TTY
	POP PSW		; RECALL ASCII CHARACTER
	CPI 0DH		; IS IT A CARRIAGE RETURN?
	JZ TERM		; IF SO, JUMP TO "TERM"
	CPI 7FH		; IS IT A RUBOUT?
	JZ RUBOUT	; IF SO, JUMP TO "RUBOUT"
	MOV M,A		; OTHERWISE, STORE IT IN THE BUFFER
	INX H		; AND INCREMENT THE POINTER H/L
	JMP GETTXT	; THEN GET NEXT TEXT CHARACTER

RUBOUT:	MOV A,L		; IF H/L=D/E THE POINTER IS AT THE BEGINNING
	SUB E		; OF THE BUFFER, SO DON'T DO ANYTHING
	JNZ RUB		; AND RETURN TO GET MORE TEXT
	MOV A,H
	SBB D
	JNZ RUB
	CALL QUOTE	; IF H/L=D/E, THEN GIVE NEW PROMPT CHARACTER
	DB 0DH		; <CR>
	DB 0AH		; <LF>
	DB 3EH		; >
	DB 00H
	JMP ISTRNG

RUB:	DCX H		; OTHERWISE, DECREMENT POINTER H/L
	MVI A,3CH
	CALL OUTPUT	; PRINT "<"
	JMP GETTXT	; AND RETURN TO GET MORE TEXT

TERM:	MVI A,0H	; TERMINATE TEXT STRING WITH A NULL BYTE
	MOV M,A
	MVI A,0AH
	CALL OUTPUT	; APPEND LINE FEED TO PREVIOUSLY PRINTED CARRIAGE RETURN

	RET		; AND RETURN TO MAIN PROGRAM











	; MULT-ADD SUBROUTINE

	; ** THIS SUBROUTINE ADDS REGISTER D/E TO
	; REGISTERS A1 AND A2 A TOTAL OF "C" TIMES
	; WHERE "C" IS THE NUMBER IN REGISTER C.
	; THE NUMBERS ARE CONSIDERED TO BE BCD **

MLTADD:	LXI H,A1
	MOV A,M		; ADD REG E TO A1 LOB
	ADD E
	DAA		; AND DECIMAL ADJUST THE RESULT
	MOV M,A
	INX H
	MOV A,M		; ADD REGISTER D + CARRY TO A1 HOB
	ADC D
	DAA		; AND DECIMAL ADJUST THE RESULT
	MOV M,A
	INX H
	MOV A,M		; ADD CARRY TO A2 LOB
	ACI 0
	DAA		; AND DECIMAL ADJUST THE RESULT
	MOV M,A
	DCR C		; DECREMENT COUNTER C
	JNZ MLTADD	; IF NOT DONE, DO IT AGAIN
	RET		; OTHERWISE, RETURN





	; BINARY TO BCD CONVERSION SUBROUTINE

	; ** THE BINARY NUMBER IN REGISTER A4 IS CONVERTED
	; TO A BCD NUMBER IN REGISTERS A1 AND A2.
	; A1LOB CONTAINS THE 1'S AND 10'S DIGITS,
	; A1 HOB CONTAINS THE 100'S AND 1000'S DIGITS,
	; AND A2LOB CONTAINS THE 10,000'S DIGIT (AND 100,000
	; DIGIT IS ALWAYS ZERO). **

B2BCD:	LXI H,0
	SHLD A1		; CLEAR A1
	SHLD A2		; CLEAR A2
	LDA A4+1	; GET MOST SIGNIFICANT HEX DIGIT
	RRC		; FROM THE TOP NIBBLE OF A4 HOB
	RRC
	RRC
	RRC
	ANI 0FH
	JZ SKIP1	; AND IF IT IS NOT ZERO
	MOV C,A		; PUT IT IN REGISTER C
	LXI D,4096H
	CALL MLTADD	; USE MULT-ADD TO ADD 4096 TO A1 & A2 "C" TIMES



SKIP1:	LDA A4+1	; GET NEXT MOST SIGNIFICANT HEX DIGIT
	ANI 0FH		; FROM BOTTOM NIBBLE OF A4 HOB
	JZ SKIP2	; AND IF IT IS NOT ZERO
	MOV C,A		; PUT IT IN REGISTER C
	LXI D,256H
	CALL MLTADD	; USE MULT-ADD TO ADD 256 TO A1 & A2 "C" TIMES

SKIP2:	LDA A4		; GET NEXT HEX DIGIT FROM TOP NIBBLE OF A4LOB
	RRC
	RRC
	RRC
	RRC
	ANI 0FH
	JZ SKIP3	; AND IF IT IS NOT ZERO
	MOV C,A		; PUT IT IN REGISTER C
	LXI D,16H
	CALL MLTADD	; USE MULT-ADD TO ADD 16 TO A1 & A2 "C" TIMES

SKIP3:	LDA A4		; GET LAST HEX DIGIT FROM A4LOB
	ANI 0FH
	RZ		; AND RETURN IF IT IS ZERO
	MOV C,A		; OTHERWISE, MOV IT INTO REGISTER C
	LXI D,1H	; AND CALL MULT-ADD TO ADD "C" TO A1 & A2
	JMP MLTADD


	; PRINT DECIMAL SUBROUTINE

	; ** THIS SUBROUTINE PRINTS THE BCD NUMBER IN A1 & A2 **

PDEC:	LDA A2
	CALL PLNIB	; PRINT 10,000'S DIGIT
	LHLD A1
	JMP PHLREG	; PRINT 4 LOW DIGITS


	; OUTPUT A4 SUBROUTINE

	; ** THIS SUBROUTINE PRINTS THE DECIMAL EQUIVALENT
	; OF THE NUMBER IN REGISTER A4. REGISTERS A1 & A2
	; ARE DESTROYED IN THE PROCESS. THE SUBROUTINE USES
	; REGISTERS C, D/E, AND H/L. **

OUTA4:	CALL B2BCD	; CONVERT A4 FROM BINARY TO BCD
	JMP PDEC	; AND PRINT IT

	; ASCII TO BCD SUBROUTINE

A2BCD:	SUI 30H		; SUBTRACT #30
	CC ABORT	; IF BORROW, THEN ABORT
	CPI 0AH		; COMPARE TO #0A
	CNC ABORT	; IF NO BORROW, THEN ABORT
	RET		; OTHERWISE, RETURN


	; ASCII TO BINARY SUBROUTINE

A2BIN:	XRA A		; CLEAR A,D,E, SIGN, & A4 REGISTERS
	STA SIGN
	STA A4
	STA A4+1
	MOV D,A
	MOV E,A
	LXI H,BUFF	; SET POINTER H/L TO BUFFER BEGINNING
	PUSH H		; AND STORE ON STACK
FCHBCD:	POP H		; LOAD H/L POINTER
	MOV A,M		; FETCH BCD DIGIT (OR - SIGN, OR TERMINATOR)
	CPI 0H
	JNZ MINSCK	; IF NOT TERMINATOR, GO TO "MINUS CHECK"
	LXI H,SIGN	; IF SIGN REGISTER IS ZERO
	ADD M
	RZ		; THEN RETURN
	LDA A4+1	; OTHERWISE, CHECK A4>32,767?
	SBI 80H		; IF IT IS
	CNC ABORT	; THEN ABORT
	XRA A		; OTHERWISE, NEGATE A4 BY SUBTRACTING FROM ZERO
	LXI H,A4
	SUB M
	MOV M,A
	MVI A,0
	INX H
	SBB M
	MOV M,A
	RET		; THEN RETURN
MINSCK:	CPI 2DH		; IF ASCII CHARACTER WAS NOT A "-" SIGN
	JNZ NUMBER	; THEN GO TO "NUMBER"
	STA SIGN	; OTHERWISE MAKE SIGN REGISTER NONZERO
	INX H		; INCREMENT H/L POINTER
	PUSH H		; STORE POINTER ON STACK
	JMP FCHBCD	; AND GET NEXT DIGIT
NUMBER:	PUSH PSW	; SAVE ASCII NUMBER ON STACK
	MVI B,9H	; ADD A4 TO ITSELF 9 TIMES (D/E=A4)
MLT10:	LDA A4
	ADD E
	STA A4
	LDA A4+1
	ADC D
	STA A4+1
	CC ABORT	; (CHECK FOR OVERFLOW)
	DCR B
	JNZ MLT10
	POP PSW		; RECALL ASCII NUMBER FROM STACK
	INX H		; INCREMENT POINTER H/L
	PUSH H		; AND STORE ON THE STACK
	CALL A2BCD	; CONVERT ASCII TO BCD NUMBER





	LXI H,A4	; SET POINTER H/L TO REGISTER A4
	ADD M		; ADD BCD ENTRY TO A4
	MOV M,A		; AND STORE ALSO IN D/E
	MOV E,A
	INX H
	MVI A,0
	ADC M
	MOV M,A
	MOV D,A
	CC ABORT	; (CHECK FOR OVERFLOW)
	JMP FCHBCD	; GET NEXT DIGIT






	; GET A4 SUBROUTINE

GETA4:	MVI A,23H
	CALL OUTPUT	; PROMPT WITH "#"
	CALL ISTRNG	; GET ASCII STRING OF BCD CHARACTERS
	JMP A2BIN	; AND CONVERT TO 16 BIT BINARY IN REGISTER A4






	; INPUT A4 SUBROUTINE

INA4:	CALL GETA4
	LDA SIGN	; IF A4 IS POSITIVE
	CPI 0H
	RZ		; THEN RETURN

NEG:	CALL QUOTE	; BUT IF IT IS NEGATIVE
	DB 4EH		; N
	DB 45H		; E
	DB 47H		; G
	DB 3FH		; ?
	DB 0DH		; <CR>
	DB 0AH		; <LF>
	DB 00H
	JMP INA4	; PRINT "NEG?" AND PROMPT AGAIN









	; SIGNED OUTPUT A4 SUBROUTINE

SOUTA4:	LDA A4+1	; IF A4 IS POSITIVE
	ANA A
	JP OUTA4	; THEN OUTPUT A4
	XRA A		; OTHERWISE, NEGATE IT BY
	LXI H,A4	; SUBTRACTING IT FROM ZERO
	SUB M
	MOV M,A
	MVI A,0
	INX H
	SBB M
	MOV M,A
	CALL QUOTE	; USE QUOTE TO PRINT A
	DB 2DH		; MINUS SIGN
	DB 00H
	JMP OUTA4	; THEN OUTPUT A4




	; SIGNED INPUT A4 SUBROUTINE

SINA4:	CALL GETA4	; GET A4
	LDA A4+1	; IF IT IS A POSITIVE NUMBER
	SUI 80H
	RC		; THEN RETURN
	LDA SIGN	; BUT IF THE SIGN BIT IS SET
	CPI 0H		; THEN CHECK THE SIGN REGISTER TO MAKE
	RNZ		; THAT THE NUMBER WAS REALLY NEGATIVE
	CALL ABORT	; AND IF IT WASN'T, THEN ABORT




	; PRINT FLOAT(A3) SUBROUTINE

PFLOAT:	LDA A3		; USE PRINT HEXT TO PRINT SIGNIFICANT BITS
	CALL PHEX
	CALL QUOTE
	DB 2AH		; *
	DB 32H		; 2
	DB 5EH		; ^
	DB 00H
	LDA A3+1	; USE PRINT HEX TO PRINT POWER OF 2
	CALL PHEX
	RET		; AND RETURN







	; LET A3=A4 SUBROUTINE (FIXED TO FLOATING POINT)

A3EQA4:	LHLD A4		; LET H/L=A4
	MVI B,0FH	; SET POWER OF 2 EQUAL TO 15 (DECIMAL)
	SUB A
	ADD H		; IF SIGNIFICANT BITS = 1.XXXXXXX
	JM MOVA3	; THEN STORE RESULT IN A3 AND RETURN
DOUBLE: DCR B		; OTHERWISE, DECREMENT THE POWER OF 2
	MOV A,L		; AND DOUBLE THE VALUE IN H/L
	ADD L
	MOV L,A
	MOV A,H
	ADC H
	MOV H,A		; IF SIGNIFICANT BITS ARE 1.XXXXXXX
	JM MOVA3	; THEN STORE RESULT IN A3 AND RETURN
	MOV A,B
	CPI 0H		; CHECK THE POWER OF 2 FOR ZERO
	JNZ DOUBLE	; AND IF NOT ZERO, DOUBLE THE SIG. BITS AGAIN
MOVA3:	XCHG		; MOVE SIGNIFICANT BITS TO D/E
	LXI H,A3
	MOV M,D		; MOVE SIGNIFICANT BITS TO A3LOB
	INX H
	MOV M,B		; MOVE POWER OF 2 TO A3HOB
	RET




	; LET A4=A3 SUBROUTINE (FLOATING TO FIXED POINT)

A4EQA3:	MVI E,0		; CLEAR REGISTER E
	LXI H,A3	; MOVE SIGNIFICANT BITS TO REGISTER D
	SUB A		; AND SET THE FLAGS IN THE PROCESS
	ADD M
	MOV D,M
	JZ MOVA4	; IF SIG. BITS ARE ZERO, GO TO "MOVA4"
	SUB A
	INX H
	ADD M		; PUT POWER OF 2 IN ACCUMULATOR (SET FLAGS)
	JM SMALL	; IF POWER OF 2 < 0, GO TO "SMALL"
	SUI 10H		; SUBTRACT 16 (DECIMAL) FROM POWER OF 2
	CNC ABORT	; AND IF NO BORROW, THEN ABORT
	MOV B,A		; LET B=POWER OF 2 MINUS 16
	INR B		; IF POWER OF 2 WAS 15
	JZ MOVA4	; THEN GO TO "MOVA4"
HALVE:	STC		; LSR REGISTER D/E
	CMC
	MOV A,D
	RAR
	MOV D,A
	MOV A,E
	RAR
	MOV E,A
	INR B		; AND INCREMENT POWER OF 2
	JNZ HALVE	; IF NOT DONE, HALVE REGISTER D/E AGAIN

MOVA4:	XCHG
	SHLD A4		; MOVE D/E TO REGISTER A4
	RET		; AND RETURN
SMALL:	SUB A		; NEGATIVE POWER OF 2, SO
	MOV D,A		; CLEAR SIG. BITS IN REGISTER D
	JMP MOVA4	; AND STORE ZERO IN REGISTER A4




	; NORM(A3) SUBROUTINE

NORMA3:	LDA A3		; MOVE SIG. BITS TO ACCUMULATOR
	SUI 0H		; SET FLAGS
	RM		; RETURN IF ALREADY NORMALIZED (MSB SET)
	RZ		; RETURN IF SIG. BITS ARE ALL ZERO
NORM:	LDA A3+1	; DECREMENT POWER OF 2
	DCR A
	CPI 7FH		; CHECK FOR UNDERFLOW
	JZ UFLOW
	STA A3+1	; AND STORE NEW POWER OF 2
	LDA A3
	ADD A
	STA A3		; DOUBLE SIGNIFICANT BITS
	JP NORM		; IF NOT NORMALIZED YET, TRY AGAIN
	RET		; OTHERWISE, RETURN
UFLOW:	SUB A		; LET A3=0
	STA A3
	STA A3+1
	RET		; AND RETURN

	END
 A		; NEGATIVE POWER OF 2,