; GetShow.asm
;  A package of 2 assembler procedures for performing numeric IO
;  with the keyboard and a DOS window.  This file must be INCLUDEd
;  in any source file that uses the subroutines:
;
;      INCLUDE getshow.asm
;
;  Original by Alan Pinck; modified by Ian Allen idallen@ncf.ca
;
;    getNum accepts keyboard input and assuming
;       decimal characters representing a number
;       in the range 0 to 9999, converts the input
;       into a binary number which is returned in
;       the AX register.  Leading whitespace is skipped.
;       getNum returns with the Carry flag off if
;       it was successful and with the Carry flag on
;       if it was not (if the keyboard input did not
;       start with something that could be interpretted
;       as a number, or if the number exceeds 4 digits)
;
;    showNum displays the contents of AX (assumed to be
;       a number in 2's complement form) as a decimal value
;
;  No newlines or carriage returns are added.  You will need to
;  add them yourself to format your output nicely.


;--- data which is local to this package ---
diolastc db     00
dionewl  db     0Dh,0Ah,"$"
dionum   dw     ?
; ===========================================


; showNum displays the 2's complement number
;  in AX as a decimal number

showNum proc    near
        push    ax     ;save working registers
        push    bx
        push    cx
        push    dx

;       push    ax
;       mov     dx,offset dionewl ;ensure display is on a newline
;       mov     ah,09
;       int     21h
;       pop     ax
        
        cmp     ax,0    ;test for negative
        jge     positive;skip ahead if positive
        mov     ah,02h  ;display character code
        mov     dl,'-'  ;  minus sign
        int     21h
        mov     bx,ax
        sub     ax,bx   ;ax now zero
        sub     ax,bx   ;ax now positive

positive:        
        mov     bx,10   ;divide by 10 (decimal!) to convert to decimal
        mov     cl,0    ;count of number of digits produced
div_loop:
        mov     dx,0    ;setup to divide dx|ax by bx
        idiv    bx      ;unsigned division: 
                        ;quotient in ax, remainder in dx
        push    dx      ;save the digit (remainder)
        inc     cl      ;keep track of number of digits
        cmp     ax,0    ;until nothing left to divide
        jnz     div_loop;  loop back

show_digit:
        pop     dx
        add     dl,30h  ;convert digit to ASCII code
        mov     ah,02h  ;display character
        int     21h
        dec     cl      ;loop until all digits shown
        jnz     show_digit

        pop     dx      ;restore working registers
        pop     cx
        pop     bx
        pop     ax
        ret
showNum endp

;==========================================

; getNum returns number input (converted to
;   binary) in ax
; input is assumed to be a positive, decimal
;   value with a maximum of 4 digits, terminated
;   with an ASCII non-digit code
; Carry flag is set (on) if an input error is
;   detected (Carry flag is clear (off) if the
;   input is valid

getNum  proc    near
        push    bx     ;save working registers
        push    cx
        push    dx
        
        mov     dionum,0
        mov     bh,'9'  ;setup for valid range testing
        mov     bl,'0'
        mov     cl,4    ;maximum number of digits acceptable
        
        mov     al,diolastc
        cmp     al,00   ;check if we no previous char available
        jne     gotchar ; skip input if one already here
skipwhite:
        mov     ah,01h  ;input a char
        int     21h
gotchar:
        mov     diolastc,al ;skip over leading blanks and 
        cmp     al,20h      
        je      skipwhite
        cmp     al,0Dh      ;carriage returns
        je      skipwhite
        cmp     al,0Ah      ;and newlines
        je      skipwhite

notwhite:
; check initial character to ensure it is a digit
        cmp     bh,al     ; check that it's < '9'
        jc      bad_digit ; error if its not
        sub     al,bl     ; subtract '0' from it
        jc      bad_digit ; error if its < '0'

get_loop:
        mov     ah,0
        push    ax
        mov     ax,dionum
        mov     dx,10      ; decimal based system
        imul    dx         ; multiply ax by dx results in dx|ax
        mov     dionum,ax  ; value of prior digits times 10
        pop     ax         ; add this digit's value
        add     dionum,ax

        mov     ah,01   ;input a character
        int     21h
        mov     diolastc,al ;save it incase we're finished digits
        cmp     bh,al       ;exit getting digits if not a digit
        jc      get_exit
        sub     al,bl
        jc      get_exit
        
        dec     cl          ;if this is not more than
        jnz     get_loop    ;  4 digits add this digit to num
        sub     cl,1        ;otherwise exit with carry set
        jmp     bad_digit
        
get_exit:
        mov     ax,dionum  ; "good" number
        cmp     ax,ax   ; clear carry
bad_digit:  ;-- if we get here something is wrong
            ;-- by careful coding we have ensured that
            ;-- the carry flag will be set
            ;-- this is the "error" return indicator
        pop     dx  ;restore working registers
        pop     cx
        pop     bx
        ret

getNum  endp


