;
;	CONVERSI.ASM ver 1.2
;     Base conversion program
;
;From Heathkit course on assembly language
;
;Converts between ASCII, Binary, Decimal,
;Hexadecimal, Octal, and Split Octal.
;
VERN	EQU	1
REVN	EQU	2
;
; v1.2  Added upper case conversion to input. Added
;	version display in message. Since <cr> ends input
;	the Ascii conversion won't show a 'return' key and
;	I can't figure a good way around it.
;	09/26/84 Dick Mead WB6NGC
;
; v1.1	Entered by  Ben Miller	WB8LGH
;	on 02/20/80, with routines for CP/M
;	I/O. Added prompt for input info.
;	Added lower case print on ASCII output.
;
CR	EQU	0DH	;CARRIAGE RETURN

LF	EQU	0AH	;LINE FEED
BDOS	EQU	0005H	;CP/M ENTRY POINT
;
	ORG	100H	;ORIGIN OF OBJECT CODE
;
BEGIN	LXI	SP,STACK;DEFINE TOP OF STACK
	LXI	H,MESS6	;SET H-L TO MESSAGE #6
	CALL	PRINT	;GO PRINT MESSAGE
;
BEGIN1	LXI	H,MESS1	;SET H-L TO MESSAGE #1
	CALL	PRINT	;GO PRINT MESSAGE
	LXI	D,0000H ;LOAD D-E REGISTERS WITH 0
	CALL	INPUT	;GET BASE OF ENTRY
	CPI	'a'	;..lower to
	JC	BEG2	;..upper case
	CPI	'z'+1	;..conversion
	JNC	BEG2	;..for
	XRI	20H	;..commands
BEG2:	CPI	'D'	;IS IT DECIMAL?
	JZ	DECIN	;IF DECIMAL GO DECIN
	CPI	'H'	;IS IT HEXADECIMAL?
	JZ	HEXIN	;IF HEXADECIMAL GO HEXIN
	CPI	'O'	;IS IT OCTAL?
	JZ	OCTIN	;IF OCTAL GO OCTIN
	CPI	'S'	;IS IT SPLIT OCTAL
	JZ	SOCTN	;IF SPLIT OCTAL GO SOCTN
	CPI	'B'	;IS IT BINARY
	JZ	BININ	;IF BINARY GO BININ
	CPI	'A'	;IS IT ASCII?
	JZ	ASCIN	;IF ASCII GO ASCIN
	CPI	'X'	;ARE YOU DONE?
	JZ	0000H	;DO A WARM BOOT
	LXI	H,MESS2	;NONE OF THE ABOVE? -MUST BE AN ERROR
	CALL	PRINT	;GO PRINT ERROR MESSAGE
	JMP	BEGIN	;START AGAIN TURKEY INPUT IN ERROR
;
PRINT	MOV	A,M	;GET MESSAGE CHARACTER
	ORA	A	;CHECK FOR DELIMITER
	RZ		;RETURN IF DONE
	CALL	OUTPT	;DISPLAY CHARACTER
	INX	H	;SET H-L TO NEXT CHARACTER
	JMP	PRINT	;DO ANOTHER CHARACTER
;
INPUT	PUSH	B	;SAVE REGISTERS
	PUSH	D
	PUSH	H
	MVI	C,1	;READ CONSOLE KBD
	CALL	BDOS	;CALL CP/M ENTRY
	POP	H	;RESTORE REGISTERS
	POP	D
	POP	B
	RET		;RETURN TO MAIN PROGRAM
;
OUTPT	PUSH	PSW	;SAVE REGISTERS
	PUSH	B
	PUSH	D
	PUSH	H
	MOV	E,A	;GET CHARACTER FOR CP/M
	MVI	C,2	;WRITE CHARACTER TO CRT
	CALL	BDOS	;CALL CP/M ENTRY
	POP	H	;RESTORE REGISTERS
	POP	D
	POP	B
	POP	PSW
	RET		;RETURN TO MAIN PROGRAM
;
DECIN	LXI	H,DECIMAL;POINT TO ASCII FOR DECIMAL
	CALL	VALIN	;SHOW QUESTIONS WITH PROPER BASE
;
NEXT1	CALL	GETIN	;GET CHARACTER AND ECHO
	JZ	CNVRT	;GO CONVERT TO ALL BASES IF SO
	SUI	30H	;SUBTRACT 30 HEX FROM ASCII
	JC	ERROR	;NO GOOD IF CARRY
	CPI	10	;HIGHER THAN "9"?
	JNC	ERROR	;NO GOOD IF SO
	LXI	H,0000H ;CLEAR H-L FOR MULTIPLICATION
	DAD	D	;H-L=D-E TIMES ONE
	DAD	H	;H-L=D-E TIMES TWO
	DAD	H	;H-L=D-E TIMES FOUR
	DAD	D	;H-L=D-E TIMES FIVE
	DAD	H	;H-L=D-E TIMES TEN
	MOV	E,A	;NEW KEY VALUE TO E
	MVI	D,00H	;CLEAR D
	DAD	D	;ADD UNIT VALUE TO D-E
	XCHG		;NEW TOTAL TO D-E
	JMP	NEXT1	;LOOP FOR NEXT CHARACTER
;
OCTIN	LXI	H,OCTAL	;POINT TO ASCII FOR OCTAL
	CALL	VALIN	;SHOWS QUESTION WITH PROPER BASE
;
NEXT2	CALL	GETIN	;GET CHARACTER AND ECHO IT
	JZ	CNVRT	;GO CONVERT TO ALL BASES IF SO
	SUI	30H	;SUBTRACT 30 HEX FROM ASCII
	JC	ERROR	;NO GOOD IF CARRY
	CPI	8	;HIGHER THAN "7"?
	JNC	ERROR	;NO GOOD IF SO
	LXI	H,0000H ;CLEAR H-L FOR MULTIPLICATION
	DAD	D	;H-L=D-E TIMES ONE
	DAD	H	;H-L=D-E TIMES TWO
	DAD	H	;H-L=D-E TIMES FOUR
	DAD	H	;H-L=D-E TIMES EIGHT
	MOV	E,A	;NEW KEY VALUE TO E
	MVI	D,00H	;CLEAR D
	DAD	D	;ADD UNIT VALUE TO TOTAL
	XCHG		;NEW TOTAL TO D-E
	JMP	NEXT2	;LOOP FOR NEXT DIGIT
;
HEXIN	LXI	H,HEXAD	;POINT TO ASCII FOR HEXADECIMAL
	CALL	VALIN	;SHOWS QUESTION WITH PROPER BASE
;
NEXT3	CALL	GETIN	;GET CHARACTER AND ECHO IT
	JZ	CNVRT	;GO CONVERT TO ALL BASES IF DONE
	CPI	'a'	;force

	JC	HEX4	;..lower
	CPI	'z'+1	;...to
	JNC	HEX4	;...upper
	XRI	20H	;..case
HEX4	SUI	30H	;SUBTRACT 30 HEX ASCII
	JC	ERROR	;NO GOOD IF CARRY
	CPI	10	;IS IT 0 THROUGH 9?
	JC	HEXOK	;IT'S OK IF SO
	SUI	7	;SUBTRACT 7 MORE FOR LETTERS
	CPI	10	;LOWER THAN 10?
	JC	ERROR	;NO GOOD IF SO
	CPI	16	;HIGHER THAN 16?
	JNC	ERROR	;NO GOOD IF SO
;
HEXOK	LXI	H,0000H ;CLEAR H-L FOR MULTIPLICATION
	DAD	D	;H-L=D-E TIMES ONE
	DAD	H	;H-L=D-E TIMES TWO
	DAD	H	;H-L=D-E TIMES FOUR
	DAD	H	;H-L=D-E TIMES EIGHT
	DAD	H	;H-L=D-E TIMES SIXTEEN
	MOV	E,A	;NEW KEY VALUE TO E
	MVI	D,00H	;CLEAR D
	DAD	D	;ADD UNIT VALUE TO TOTAL
	XCHG		;NEW TOTAL TO D-E
	JMP	NEXT3	;LOOP FOR NEXT DIGIT
;
BININ	LXI	H,BINRY	;SHOWS ASCII FOR BINARY
	CALL	VALIN	;SHOWS QUESTION WITH PROPER BASE

;
NEXT4	CALL	GETIN	;GET CHARACTER AND ECHO IT
	JZ	CNVRT	;GO CONVERT IF SO
	SUI	30H	;SUBTRACT 30H FROM ASCII
	JC	ERROR	;NO GOOD IF CARRY
	CPI	2	;IF HIGHER THAN "1"?
	JNC	ERROR	;NO GOOD IF SO
	LXI	H,0000H ;CLEAR H-L FOR MULTIPLICATION
	DAD	D	;H-L=D-E TIMES ONE
	DAD	H	;H-L=D-E TIMES TWO
	MOV	E,A	;NEW KEY VALUE TO E
	MVI	D,00H	;CLEAR D
	DAD	D	;ADD UNIT VALUE TO TOTAL
	XCHG		;NEW TOTAL TO D-E
	JMP	NEXT4	;LOOP FOR NEXT DIGIT
;
SOCTN	LXI	H,SPLIT	;POINT TO ASCII FOR SPLIT OCTAL
	CALL	VALIN	;SHOWS QUESTION WITH PROPER BASE
;
NEXT5	CALL	GETIN	;GET CHARACTER AND ECHO IT
	JZ	CNVRT	;GO CONVERT TO ALL BASES IF SO
	CPI	'/'	;IS CHARACTER A SLASH?
	JNZ	NODOT	;NO-CONTINUE
	MOV	D,E	;YES - TOTAL IN E BECOMES HIGH BYTE
	MVI	E,00H	;CLEAR E
	JMP	NEXT5	;GO GET NEXT CHARACTER
;
NODOT	SUI	30H	;SUBTRACT30H FROM ASCII
	JC	ERROR	;NO GOOD IF CARRY
	CPI	8	;HIGHER THAN "7"?
	JNC	ERROR	;NO GOOD IF SO
	MOV	L,A	;SAVE NEW KEY VALUE IN L
	MOV	A,E	;PREVIOUS TOTAL TO A
	ADD	A	;PREVIOUS TOTAL TIMES TWO
	ADD	A	;PREVIOUS TOTAL TIMES FOUR
	ADD	A	;PREVIOUS TOTAL TIMES EIGHT
	ADD	L	;ADD NEW KEY TO MULTIPLIED TOTAL
	MOV	E,A	;NEWTOTAL BACK TO E
	JMP	NEXT5	;LOOP FOR NEXT DIGIT
;
ASCIN	LXI	H,ASCII	;POINT TO ASCII FOR ASCII
	CALL	VALIN	;SHOWS QUESTION WITH PROPER BASE
;
NEXT6	CALL	GETIN	;GET CHARACTER AND ECHO IT
	JZ	CNVRT	;GO CONVERT TO ALL BASES IF SO
	MOV	D,E	;LAST CHARACTER MOVES TO D

	MOV	E,A	;NEW CHARACTER GOES IN E
	JMP	NEXT6	;LOOP FOR NEXT CHARACTER
;
CNVRT	LXI	H,MESS5	;SET H-L TO BEGINNING OF MESSAGE
	CALL	PRINT	;DISPLAY MESSAGE
;
DOUT	XCHG		;SWAP UNKNOWN INTO H-L
	PUSH	H	;SAVE UNKNOWN ON STACK
	LXI	B,10000D;LOAD B-C WITH 10000 DECIMAL
	CALL	SUBTR	;SUBTRACT AND DISPLAY
	LXI	B,1000D	;LOAD B-C WITH 1000 DECIMAL
	CALL	SUBTR	;SUBTRACT AND DISPLAY
	LXI	B,100D	;LOAD B-C WITH 100 DECIMAL
	CALL	SUBTR	;SUBTRACT AND DISPLAY
	LXI	B,10D	;LOAD B-C WITH 10 DECIMAL
	CALL	SUBTR	;SUBTRACT AND DISPLAY
	MOV	A,L	;WHAT'S LEFT IS UNITS ONLY
	ADI	30H	;MAKE IT ASCII
	CALL	OUTPT	;DISPLAY UNITS
	POP	D	;GET ORIGINAL BACK
	CALL	SPACES	;OUTPUT TWO SPACES
;
HOUT	MOV	A,D	;HIGH BYTE OF UNKNOWN TO A
	CALL	DOHEX	;CONVERT HEX AND DISPLAY
	MOV	A,E	;LOW BYTE OF UNKNOWN TO A
	CALL	DOHEX	;CONVERT AND DISPLAY
	CALL	SPACES	;OUTPUT TWO SPACES
;
OOUT	MOV	A,D	;HIGH BYTE OF UNKNOWN TO A
	RAL		;ROTATE LEFT MOST BIT
	RAL		; INTO RIGHTMOST POSITION
	PUSH	PSW	;SAVE NEW BYTE ON STACK
	ANI	01H	;ZER0 ALL BUT RIGHT BIT
	CALL	OCTOUT	;MAKE ASCII AND DISPLAY ON CRT
	POP	PSW	;GET MODIFIED BYTE BACK
	CALL	ROTES	;GO NEXT 3 BYTES 2 TIMES
	MOV	A,E	;LOW BYTE OF UNKNOWN TO A
	CALL	ROTES	;DO LEFTOVER OF HIGH AND 5 OF LOW
	CALL	ROTER	;DO LAST THREE BYETE
	CALL	SPACES	;OUTPUT TWO SPACES
;
SOOUT	MOV	A,D	;HIGH BYTE OF UNKNOWN TO A
	CALL	SOCT	;CONVERT TO OCTAL DISPLAY
	MVI	A,'/'	;SET UP A '/' TO DISPLAY
	CALL	OUTPT	;DISPLAY ON CRT
	MOV	A,E	;LOW BYTE OF UNKNOWN TO A
	CALL	SOCT	;CONVERT TO OCTAL AND DISPLAY
	CALL	SPACES	;OUTPUT TWO SPACES
;
BOUT	MOV	A,D	;HIGH BYTE OF UNKNOWN TO A
	CALL	BINOUT	;DISPLAY AS 0'S AND 1'S
	MOV	A,E	;LOW BYTE OF UNKNOWN TO A
	CALL	BINOUT	;DISPLAY AS O'S AND 1'S
	CALL	SPACES	;OUTPUT TWO SPACES
;
AOUT	MOV	A,D	;HIGH BYTE OF UNKNOWN TO A
	CALL	ATEST	;TEST FOR LEGAL AND DISPLAY
	MVI	A,' '	;NEED A SPACE TO SEPERATE
	CALL	OUTPT	;DISPLAY THE SPACE
	MOV	A,E	;LOW BYTE OF UNKNOWN TO A
	CALL	ATEST	;TEST FOR LEGAL AND DISPLAY
	CALL	CRLF	;DO A CAR RET AND LINE FEED
	CALL	CRLF	;ONE MORE MAKES IT NICE
	JMP	BEGIN1	;START ALL OVER AGAIN???
;
CRLF	MVI	A,0DH	;SETUP A CARRIAGE RETURN
	CALL	OUTPT	;DISPLAY IT
	MVI	A,0AH	;SET UP A LINE FEED
	JMP	OUTPT	;DISPLAY IT AND RETURN TO WHEREVER
;
ATEST	CPI	' '	;LOWER THAN A SPACE?
	JC	DODOT	;DISPLAY A '.' IF SO
	CPI	7FH	;HIGHER THAN A RUBOUT?
	JC	OUTPT	;DISPLAY A CHARACTER IF SO
;
DODOT	MVI	A,'.'	;SETUP A DOT TO DISPLAY
	JMP	OUTPT	;DISPLAY THE DOT ON THE CRT

BINOUT	MVI	B,08H	;8 BITS TO DO - SETUP COUNTER
;
BLOOP	RAL		;ROTATE A BIT INTO CARRY
	PUSH	PSW	;SAVE THE MODIFIED BYTE
	MVI	A,'1'	;ASSUME BIT LOGIC IS 1
	JC	BINOK	;DISPLAY IT IF A-OK
	MVI	A,'0'	;OOPS  CHANGED MY MIND  FICKLE  
;
BINOK	CALL	OUTPT	;DISPLAY WHATEVER
	POP	PSW	;GET MODIFIED BYTE BACK
	DCR	B	;COUNT ONE BIT COMPLETED
	JNZ	BLOOP	;DO ANOTHER UNTILL ALL 8 ARE DONE
	RET		;THEN GO BACK TO WHEREVER
;
SOCT	RAL		;ROTATE TWO LEFTMOST BITS
	RAL		; INTO TWO RIGHTMOST
	RAL		; POSITIONS
	PUSH	PSW	;SAVE MODIFIED BYTE ON STACK
	ANI	03H	;ZERO ALL BUT TWO RIGHTMOST BITS
	CALL	SPOUT	;MAKE ASCII AND DISPLAY ON CRT
	POP	PSW	;GET MODIFIED BYTE BACK
	CALL	SPINR	;DO 3 BITS AND FALL INTO 3 MORE
;
SPINR	RAL		;ROTATE TWO LEFTMOST BITS
	RAL		; INOT TWO RIGHTMOST
	RAL		; POSITIONS
;
SPOUT	PUSH	PSW	;SAVE MODIFIED BYTE ON STACK
	ANI	07H	;ZERO ALL BUT THREE RIGHT BITS
	ADI	30H	;ADD ASCII OFFSET
	CALL	OUTPT	;DISPLAY CHARACTER ON CRT
	POP	PSW	;GET MODIFIED BYTE BACK
	RET		;GO BACK TO WHERE YOU CAME FROM
;
ROTES	CALL	ROTER	;DO 3 BITS AND FALL INTO 3 MORE
;
ROTER	RAL		;ROTATE CARRY PLUS LEFTMOST
	RAL		; TWO BITS OF DATA BYTE INTO
	RAL		; THREE RIGHTMOST BITS
;
OCTOUT	PUSH	PSW	;SAVE MODIFIED BYTE ON STACK
	ANI	07H	;ZERO ALL BUT RIGHT THREE BITS
	ADI	30H	;ADD IN ASCII OFFSET
	CALL	OUTPT	;DISPLAY CHARACTER ON CRT
	POP	PSW	;GET MODIFIEY BYTE BACK
	RET		;RETURN TO MAIN PROGRAM
;
DOHEX	PUSH	PSW	;SAVE BYTE ON STACK
	RRC		;ROTATE
	RRC		;LEFT
	RRC		; FOUR BITS
	RRC		; INTO PLACE
	CALL	HEXOUT	;MAKE ASCII AND DISPLAY
 	POP	PSW	;GET BYTE BACK
;
HEXOUT	ANI	0FH	;KEEP ONLY RIGHT FOUR BITS
	ADI	30H	;ADD ASCII OFFSET
	CPI	':'	;IS IT A NUMBER
	JC	OUTPT	;DISPLAY IT ON CRT IF SO
	ADI	07H	;MAKE IT A LETTER
	JMP	OUTPT	;AND DISPLAY ON CRT
;
SUBTR	MVI	D,0FFH	;D REGISTER TO MINUS ONE
;
LOOP5	MOV 	A,L	;LOW BYTE OF UNKNOWN TO A
	SUB	C	;SUBTRACT LOW BYTE OF DIVISOR
	MOV	L,A	;DIFFERENCE BACK TO L
	MOV	A,H	;HIGHT BYTE OF UNKNOWN TO A
	SBB	B	;SUBTRACT B AND BORROW IF ANY
	MOV	H,A	;DIFFERENCE BACK TO H
	INR	D	;COUNT ONE SUBTRACTION
	JNC	LOOP5	;SUBTRACT AGAIN IF NO BORROW
	DAD	B	;ADD DIVISOR BACK TO UNKNOWN
	MOV	A,D	;COUNT TO A
	ADI	30H	;MAKE IT ASCII
	JMP	OUTPT	;DISPLAY ON CRT
;
ERROR	LXI	H,MESS4	;SET H-L TO BEGINNING OF MESSAGE
	CALL	PRINT	;GO PRINT IT
	JMP	BEGIN	;START OVER
;
SPACES	MVI	A,' '	;PUT A SPACE IN REGISTER A
	CALL	OUTPT	;PRINT IT
	JMP	OUTPT	;OUTPUT ANOTHER SPACE THEN RETURN
;
GETIN	CALL	INPUT	;GET CHARACTER FROM KEYBOARD
	CPI	0DH	;ARE THEY DONE ENTERING?
	RNZ		;BACK TO WHOEVER IF NOT
	JMP	CNVRT	;JMP TO CONVERT ALL BASES
;
VALIN	PUSH	H	;SAVE BASE POINTER
	LXI	H,WHAT	;POINT TO QUESTION
	CALL	PRINT	;SHOWS THE QUESTION
	POP	H	;GET THE BASE NAME POINTER BACK
	CALL	PRINT	;PRINT IT
	LXI	H,VALUE	;POINT TO  VALUE
	JMP	PRINT	;PRINT IT THEN JUMP WHEREVER
;
WHAT	DB	CR,LF,'What''s your ',0
VALUE	DB	' Value? ',0
DECIMAL	DB	'Decimal',0
HEXAD	DB	'Hexadecimal',0
OCTAL	DB	'Octal',0
SPLIT	DB	'Split Octal',0
BINRY	DB	'Binary',0
ASCII	DB	'ASCII',0
MESS1	DB	CR,LF,'What base is your entry? ',0
MESS2	DB	CR,LF,'Sorry that is not a base I can work with. ',0
MESS4	DB	CR,LF
	DB	'Input character wrong for base specified, '
	DB	're-enter',0
MESS5	DB	CR,LF,' Decimal Hex   Octal   S/Octal  Binary'
	DB	'           ASCII',CR,LF,'  ',0
MESS6	DB	CR,LF,'Base Converter Ver. ',(VERN MOD 10)+'0','.'
	DB	REVN/10+'0',(REVN MOD 10)+'0'
	DB	CR,LF,'Possible choices are: ',CR,LF
	DB	CR,LF,'    A = ASCII '
	DB	CR,LF,'    B = Binary '
	DB	CR,LF,'    D = Decimal '
	DB	CR,LF,'    H = Hexadecimal'
	DB	CR,LF,'    O = Octal '
	DB	CR,LF,'    S = Split Octal '
	DB	CR,LF,'    X = Exit '
	DB	CR,LF,0
;
	DS	60	;RESERVE SPACE FOR STACK
STACK	EQU	$	;TOP OF STACK
;
	END


