;
;       CONVERSI.ASM ver 1.1
;     Base conversion program
;
;From Heathkit course on assembly language
;
;Converts between ASCII, Binary, Decimal,
;Hexadecimal, Octal, and Split Octal.
;
;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     '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
       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,'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