;Ulam
ORG 100h
SECTION .TEXT

   CALL GetInt

   CALL Ulam


   ;exit to DOS
   MOV  AH,4Ch
   INT  21h


;read digit chars, convert to int, until any non-digit char entered
;only working up to couple of thousand, then "Divide Overflow"
GetInt:
    PUSH BX
    PUSH CX
    PUSH DX
    PUSH DI
    MOV  BX,0      ;accumulating total number
;get first digit:
    MOV  AH,01     ;read char from stdin, with echo.  into AL
    INT  21h
    CMP  AL,30h    ;char-'0'
    JL   doneNum
    CMP  AL,39h    ;char-'9'
    JG   doneNum
    SUB  AL,30h    ;convert from ascii digit to number
    MOV  BL,AL     ;keep total in BX

;get subsequent digits
    MOV  CH,0      ;clear hi byte of CX
nextD:
    MOV  AH,01     ;read char from stdin, with echo.  into AL
    INT  21h
    CMP  AL,30h    ;char-'0'
    JL   doneNum
    CMP  AL,39h    ;char-'9'
    JG   doneNum
    SUB  AL,30h    ;convert from ascii digit to number
    MOV  CL,AL     ;save to CL.
    MOV  AX,BX     ;current total
    MOV  DI,10     ;multiplier
    MUL  DI        ;multiply by 10. DX,AX=AX*DI
    ADD  AX,CX     ;add digit to total
    MOV  BX,AX     ;back to BX
    JMP  nextD
doneNum:
    MOV  AX,BX
    POP  DI
    POP  DX
    POP  CX
    POP  BX
    RET


;Ulam number
; in: AX is N
Ulam:
    PUSH BX
    PUSH DX

topUlam:
    CMP  AX,1
    JLE  doneUlam
    MOV  BX,AX     ;save N in BX

    CALL OutStars
    ;    CALL OutNum
    ;    MOV  AH,02
    ;MOV  DL,10     ;LF
    ;INT  21h

    MOV  AX,BX     ;saved copy of N back to AX

    AND  BX,1      ;is bit 0 on.    N%2
    JNZ  odd       ;N%2==1
even:
    SHR  AX,1      ;N = N/2
    JMP  endif
odd:
    MOV  BX,AX     ;N
    SHL  AX,1      ;2N
    SHL  AX,1      ;4N
    SUB  AX,BX     ;4N-N=3N
    INC  AX        ;3n+1
endif:
    JMP  topUlam
doneUlam:
    POP DX
    POP BX
    RET


;output AX asterisks on a row
OutStars:
    PUSH AX
    PUSH BX
    PUSH CX
    PUSH DX

    MOV  CX,AX
    MOV  AH,02
    MOV  DL,2Ah     ;'*'
another:
    INT  21h
    LOOP another

    MOV  DL,10     ;LF
    INT  21h

    POP DX
    POP CX
    POP BX
    POP AX
    RET



;convert AX to decimal and output the digits
OutNum:
    PUSH  AX
    PUSH  BX
    PUSH  DX
    PUSH  SI

    MOV   DX,10    ;div and mod by 10 each iteration
    MOV   BX,digitBuf  ;address of buffer
    MOV   SI,4     ;buffer index.  five iterations for the 5 digits
NextDig:
    DIV   DL          ;AL=AX/10  AH=AX%10
    MOV   [BX+SI],AH  ;remainder (last digit) to buffer
    MOV   AH,0        ;clear high byte.  AL has quotient
    DEC   SI
    JNS   NextDig     ;until negative

    MOV   SI,0        ;index into buffer
NextOut:
    MOV   DL,[BX+SI]  ;char param to display
    ADD   DL,30h      ;offset to ASCII digit char.
    MOV   AH,02       ;write char to screen INT
    INT   21h
    INC   SI
    CMP   SI,5
    JNZ   NextOut

    POP   SI
    POP   DX
    POP   BX
    POP   AX
    RET


SECTION .DATA
digitBuf: DB  "     "   ;five bytes
