;       ROMAN80 - VERSION 0.2
;
;       ROMAN CONVERSION PROGRAM
;
;       DATE: 10-OCT-78
;       BY: M PEDDER
;
;
;       DEFINITIONS
;
       ORG     100H            ;ALLOCATE
START:  JMP     ROMAN
CR:     EQU     0DH
LF:     EQU     0AH
CONSTA: EQU     1H              ;Console status port
CONDAT: EQU     0H              ;Console data port
B$KDR:  EQU     02H             ;Data available mask
STACK:  EQU     400H
B$VDR:  EQU     01H             ;Tx ready mask
;
;       MESSAGES
;
STM     DB      'COPYRIGHT (C) 1978 M PEDDER',CR,LF
       DB      '"ROMAN" ACCEPTS DECIMAL NUMBERS IN THE RANGE 1 - 3999'
       DB      CR,LF
       DB      'AND CONVERTS TO ROMAN NUMERALS, CHECKING VALIDITY.'
       DB      CR,LF,LF
       DB      'ENTER DATA FOLLOWING "READY =", ENDING WITH "CR"'
       DB      CR,LF,'$'
RDM     DB      CR,LF,'READY =  $'
TOOL:   DB      '  TOO LARGE$'
INV:    DB      '  INVALID CHARACTER$'
LIST:   DB      0,0,'IVXLCDM'
;
;       WORKSPACE:
;
DBUF:   DS      5               ;DECIMAL BUFFER
RBUF:   DS      16              ;ROMAN BUFFER
DCNT:   DS      1               ;DECIMAL COUNT
STK:    DS      2               ;SP HOLDER
;
;       SUPERVISORY:
;
ROMAN:  LXI     H,0
       DAD     SP
       SHLD    STK
       LXI     SP,STACK        ;PREPARE STACK
       LXI     H,STM           ;POINT TO START MESSAGE
       CALL    PRINS           ;AND OUTPUT IT
RDY:    CALL    RDYM            ;PRODUCE READY MESSAGE, AND SET UP
       CALL    INPUT           ;GET DATA
       JC      RDY             ;IF IT IS VALID
       CALL    ENC             ;ENCODE AND OUTPUT IT
       JMP     RDY
       PAGE
;
;       READY MESSAGE AND SETTING UP:
;
RDYM:   LXI     H,RDM           ;POINT TO READY MESSAGE
       CALL    PRINS           ;AND OUTPUT IT
       LXI     B,DBUF          ;PREPARE DECIMAL POINTER
       LXI     H,DCNT          ;AND COUNT POINTER
       MVI     M,00H           ;CLEAR COUNTER
       RET
;
;       GET INPUT FROM CONSOLE INTO DBUF, CHECKING VALIDITY:
;
INPUT:  IN      CONSTA          ;IF CHARACTER
       ANI     B$KDR           ;IS AVAILABLE
       JZ      INPUT
       IN      CONDAT          ;GET IT
       ANI     7FH             ;STRIP PARITY
       CPI     03H             ;IF IT IS EXIT CMD
       JZ      EXIT            ;THEN GO
       CPI     0DH             ;IF IT IS 'CR'
       JNZ     ECHO            ;THEN
       MVI     A,20H           ;LOAD 'SPACE'
       CALL    PRINC           ;AND OUTPUT IT
       CALL    PRINC           ;TWICE
       MVI     A,'$'           ;MARK END
       STAX    B               ;OF DATA
       ORA     A               ;CLEAR FLAG
       RET
;
EXIT:   LHLD    STK
       SPHL
       RET
;
ECHO:   CALL    PRINC           ;ECHO CHARACTER
       CPI     '0'             ;IF IT IS ZERO OR MORE
       JM      INVCH           ;AND
       CPI     ':'             ;IF IT IS NINE OR LESS
       JP      INVCH           ;THEN IT IS VALID CHARACTER
       INR     M               ;SO COUNT IT
       STAX    B               ;AND STORE IT
       INX     B               ;ADVANCE POINTER
       MOV     A,M             ;CHECK COUNT
       CPI     05H             ;IF IT IS LESS THAN FIVE
       JM      INPUT           ;THEN CONTINUE
       LXI     H,TOOL          ;ELSE POINT TO 'TOO LARGE'
       JMP     MES
;
INVCH:  LXI     H,INV           ;POINT TO 'INVALID CHARACTER'
MES:    CALL    PRINS           ;AND OUTPUT IT
       STC                     ;SET ERROR FLAG
       RET
;
;
       PAGE
;
;       PREPARE TO ENCODE:
;
ENC:    LXI     B,DBUF          ;PREPARE DECIMAL POINTER
       LXI     D,LIST          ;PREPARE LIST POINTER
       LXI     H,DCNT          ;PREPARE COUNT POINTER
       MOV     A,M             ;GET COUNT
       CPI     04H             ;IF IT IS NOT FOUR
       JNZ     CONT            ;CONTINUE
       LDAX    B               ;ELSE GET FIRST CHARACTER
       CPI     34H             ;IF IT IS NOT FOUR
       JM      CONT            ;CONTINUE
       LXI     H,TOOL          ;ELSE LOAD 'TOO LARGE'
       CALL    PRINS           ;AND OUTPUT IT
       RET
;
CONT:   MOV     L,M             ;GET COUNT
       MVI     H,00H           ;INTO HL
       DAD     H               ;DOUBLE IT
       DAD     D               ;ADD TO LIST
       XCHG                    ;AND RESTORE LIST
       LXI     H,RBUF          ;PREPARE ROMAN POINTER
;
;       ENCODE CHARACTER STREAM IN DBUF:
;
ENCA:   LDAX    B               ;GET A CHARACTER
       INX     B               ;UPDATE THE POINTER
       CPI     '$'             ;IF IT IS NOT "END MARKER"
       RZ                      ;THEN
       CPI     '9'             ;IF IT IS NOT A NINE
       JZ      NINE            ;THEN
       CPI     '5'             ;IF IT IS NOT FIVE OR MORE
       JP      FIVE            ;THEN
       CPI     '4'             ;IF IT IS NOT FOUR
       JZ      FOUR            ;THEN
ETA:    CPI     '0'             ;IF IT IS ZERO
       JNZ     ONE             ;THEN
ETB:    DCX     D               ;MODIFY ROMAN POINTER
       DCX     D               ;TWICE
       LDA     DCNT            ;REDUCE COUNT
       DCR     A               ;AND
       STA     DCNT            ;IF IT IS NOT ZERO
       JNZ     ENCA            ;THEN LOOP
       MVI     A,'$'           ;ELSE MARK END
       CALL    STOR            ;AND STORE IT
       JMP     ROUT            ;ENCODE COMPLETED
;
       PAGE
;
;       ITS A 1=I, 10=X, 100=C, 1000=M OR MORE:
;
ONE:    PUSH    PSW             ;SAVE DATA
       LDAX    D               ;LOAD ROMAN CHARACTER
       CALL    STOR            ;AND STORE IT
       POP     PSW             ;UNSAVE DATA
       DCR     A               ;SUBTRACT ONE
       JMP     ETA             ;AND TRY AGAIN
;
;       ITS A 4=IV, 40=XL, 400=CD:
;
FOUR:   LDAX    D               ;LOAD ROMAN CHARACTER I, X OR C
       CALL    STOR            ;AND STORE IT
       INX     D               ;GET NEXT
       LDAX    D               ;ROMAN CHARACTER V, L OR D
       CALL    STOR            ;AND STORE THAT
       DCX     D               ;RESTORE POINTER
       JMP     ETB             ;AND EXIT
;
;       ITS A 5=V, 50=L, 500=D OR MORE:
;
FIVE:   PUSH    PSW             ;SAVE DATA
       INX     D               ;PREPARE POINTER
       LDAX    D               ;GET ROMAN CHARACTER V, L OR D
       CALL    STOR            ;AND STORE IT
       DCX     D               ;RESTORE POINTER
       POP     PSW             ;AND DATA
       SUI     05H             ;SUBTRACT FIVE
       JMP     ETA             ;AND TRY AGAIN
;
;       ITS A 9=IX, 90=XC, OR 900=CM:
;
NINE:   LDAX    D               ;GET ROMAN CHARACTER I, X OR C
       CALL    STOR            ;AND STORE IT
       INX     D               ;MOVE
       INX     D               ;POINTER
       LDAX    D               ;GET ROMAN CHARACTER X, C OR M
       CALL    STOR            ;AND STORE THAT
       DCX     D               ;RESTORE
       DCX     D               ;POINTER
       JMP     ETB             ;AND EXIT
;
       PAGE
;
;       STORE ROMAN CHARACTER IN RBUF FOR OUTPUT:
;
STOR:   MOV     M,A             ;STORE DATA IN BUFFER
       INX     H               ;AND MOVE POINTER
       RET
;
;       ROMAN OUTPUT TO CONSOLE:
;
ROUT:   LXI     H,RBUF          ;POINT TO ROMAN BUFFER
       CALL    PRINS           ;AND OUTPUT IT
       RET
;
;       OUTPUT A STRING OF CHARACTERS:
;
PRX:    CALL    PRINC           ;OUTPUT IT
PRINS:  MOV     A,M             ;GET CHARACTER
       INX     H               ;POINT TO NEXT
       CPI     '$'             ;IF IT IS NOT END
       JNZ     PRX             ;THEN GO
       RET
;
;       OUTPUT A CHARACTER TO CONSOLE:
;
PRINC:  PUSH    PSW             ;SAVE DATA
PRA:    IN      CONSTA          ;IF CHARACTER
       ANI     B$VDR           ;CAN BE TAKEN
       JZ      PRA
       POP     PSW             ;UNSAVE CHARACTER
       OUT     CONDAT          ;AND OUTPUT IT
       RET
;
       END