        NAME    msster
; File MSSTER.ASM
; Last edit: 1 Jan 1988
; 1 Jan 1988 version 2.30
; 24 Dec 1987 Speed up Status display. [jrd]
; 9 Oct 1987 Add cnvstr routine for curly braced strings. [jrd]
; 2 Oct 1987 Make TARG structure global, clean out old Set Key material. [jrd]
; 15 July 1987 Fix xon/xoff bug when Pushing to DOS. Tnx to Mark Zinzow. [jrd]
; 7 July 1986 Remove prkey routine. [jrd]
; 2 July 1987 Slight modification to cnvlin permitting \0 in text.
;  Remove Control-Z at end of capture file. [jrd]
; 22 June 1987 Add XOFF host when Pushing to DOS, from Paul Amaranth. [jrdd
; 13 June 1987 Use decimal printing for showing Macros. Move many number
;  conversion routines here from mssset.asm so latter will assemble under
;  MASM 1.25. [jrd]
; 19 April 1987 Add global byte pcnet for support of local area nets. [jrd]
;  Add global byte ttyact to indicate, by non-zero, that Connect mode
;  is active; mostly for system dependent i/o modules. [jrd]
; 5 April 1987 Keep Domsg message on screen for 1 second minimum.
;  Add L (Long Break) and H (Hangup) to keyboard operations [jrd]
; 24 March 1987 Add logging details for separate packet and session. [jrd]
; 9 Jan 1987 Add hooks for keyboard translator in system dependent modules.
;  Byte kbdflg, defined here, receives a char from the sys dep routines
;  instead of from keyboard during breaks from Connect mode. Word Shkadr
;  is defined here as zero. If it is set to the address of a replacement
;  Show Key procedure then the regular code here is bypassed. [jrd]
; 11 Dec 1986 Turn of serial port when Pushing to DOS. Add char transfer
; from keyboard translator. [jrd]
; 19 Oct 1986 Output cr/lf before status display. [rjd]
; 1 Oct 1986 Version 2.29a
; [2.29] code frozen on 6 May 1986 [jrd]


        Public  clscpt, defkey, inicpt, clscpi
        public  dopar, shokey, cptchr, targ
        public  kbdflg, shkadr, telnet, pcnet, ttyact
        public cnvlin, katoi, decout, valout, atoi, cnvstr
        include mssdef.h

braceop equ     7bh                     ; opening curly brace
bracecl equ     7dh                     ; closing curly brace

datas   segment public 'datas'
        extrn   flags:byte, trans:byte, buff:byte, portval:word
        extrn   cptio:byte

targ    termarg <0,1,80,24,cptchr,2dch,0,scntab,deftab,0,,parnon>
crlf    db      cr,lf,'$'
tmsg1   db      cr,lf,'(Connecting to host, type $'
tmsg3   db      ' C to return to PC)',cr,lf,cr,lf,cr,lf,'$'
erms25  db      cr,lf,'?Input must be numeric$'
erms22  db      cr,lf,'?No capture file open$' ;[jd]
erms23  db      cr,lf,'?Error writing capture file, suspending capture'
        db      cr,lf,'$'
esctl   db      'Control-$'

inthlp db cr,lf,'  ?  This message                    F  Dump screen to file'
       db cr,lf,'  C  Close the connection            P  Push to DOS'
       db cr,lf,'  S  Status of the connection        Q  Quit logging'
       db cr,lf,'  M  Toggle mode line                R  Resume logging'
       db cr,lf,'  B  Send a Break                    0  Send a null'
       db cr,lf,'  L  Send a long 1.8 s Break         H  Hangup phone'
       db cr,lf,'  Typing the escape character will send it to the host'
       db 0               ; this short-form obscures less screen area [jrd]

intprm  db      'Command> $'

CAPBUF  DB      cptsiz DUP (?)
CAPBP   DW      ?
CAPLFT  dw      ?

scntab  dw      0
deftab  dw      0
sttmsg  db      cr,lf,'Type space to continue ...$'
shkmsg  db      cr,lf,'Press key: $'
kbdflg  db      0               ; non-zero means char here from Term
pcnet   db      0               ; Network is functioning properly
ttyact  db      0               ; Connect mode active, if non-zero
shkadr  dw      0               ; offset of replacement Show Key command
ten     dw      10                      ; multiplier for setatoi
nbase   dw      ?                       ; currently active number base
numset  db      '0123456789ABCDEF'      ; number conversion alphabet
tmp     db      ?
datas   ends

code    segment public 'code'
        extrn   comnd:near, outchr:near, stat0:near
        extrn   clrbuf:near, term:near, strlen:near
        extrn   prtchr:near, ihostr:near
        extrn   beep:near, puthlp:near, getbaud:near, serhng:near
        extrn   serini:near,serrst:near, sendbr:near, showkey:near
        extrn   fpush:near, dumpscr:near, pcwait:near, sendbl:near
        assume  cs:code, ds:datas

; the show key command.

shokey  proc    near
        cmp     shkadr,0                ; keyboard translator present?
        je      shokey1                 ; e = no, use regular routines
        mov     bx,shkadr               ; get offset of replacement routine
        jmp     bx                      ; and execute it rather than us
shokey1:
;       mov     ah,cmcfm                ; confirm with carriage return
;       call    comnd
;        jmp    r                       ; uh oh...
;       mov     dx,offset shkmsg
;       mov     ah,prstr
;       int     dos                     ; print a prompt for it
;       mov     ax,offset targ          ; give it terminal arg block.
;       call    showkey                 ; show them the key definition
        jmp     rskp                    ; and return
shokey  endp

;       This is the CONNECT command.

TELNET  PROC    NEAR
        mov     ah,cmcfm
        call    comnd                   ; Get a confirm.
         jmp    r                       ;  Didn't get a confirm.
        mov     ah,prstr                ; Output
        mov     dx,offset crlf          ; a crlf.
        int     dos
        cmp     flags.vtflg,0           ; emulating a terminal?
        jne     teln1                   ; ne= yes, skip flashing message
        call    domsg                   ; Reassure user
teln1:  mov     al,0                    ; initial flags
        mov     ttyact,1                ; say telnet is active
        cmp     flags.modflg,0          ; mode line enabled?
        jne     tel010                  ; yes, go on
        or      al,modoff               ; no, make sure it stays off

tel010: or      al,havtt                ; defaults (!)
        test    flags.debug,logses      ; debug mode?
        jz      tel0                    ; z = no, keep going
        or      al,trnctl               ; yes, show control chars
tel0:   cmp     flags.wrpflg,0          ; wrap mode?
        jz      tel011                  ; no, keep going
        or      al,lnwrap
tel011: cmp     flags.vtflg,0           ; emulation a terminal?
        jz      tel1
        or      al,emheath              ; say emulating some kind of terminal
tel1:   mov     bx,portval
        cmp     [bx].ecoflg,0           ; echoing?
        jz      tel2
        or      al,lclecho
tel2:   call    getbaud                 ; pickup current baud rate for port
        mov     targ.flgs,al            ; store flags
        mov     ah,flags.comflg         ; COMs port identifier
        mov     targ.prt,ah             ; Port 1 or 2
        mov     ah,trans.escchr         ; escape character
        mov     targ.escc,ah
        mov     ah,[bx].parflg          ; parity flag
        mov     targ.parity,ah
        mov     ax,[bx].baud            ; baud rate identifier
        mov     targ.baudb,al
        xor     ah,ah
        test    flags.capflg,logses     ; select session logging flag bit
        jz      tel3                    ; z = no logging
        mov     ah,capt                 ; set capture flag
tel3:   or      targ.flgs,ah
tem:    call    serini                  ; init serial port
        cmp     flags.comflg,'N'        ; Network port?
        jne     tem0                    ; ne = no
        cmp     pcnet,1                 ; did net intialize?
        ja      tem0            ; a=yes (0=no net,1=net/no session,2=session)
        mov     kbdflg,'C'              ; no, say Close the connection
        jmp     tem2                    ; do it
tem0:
        mov     dx,offset crlf          ; give user an indication that we are
        mov     ah,prstr                ; entering terminal mode
        int     dos
        mov     ax,offset targ          ; Point to terminal arguments
        call    term                    ; Call the main Terminal procedure
        or      targ.flgs,scrsam        ; assume screen is the same.
tem1:   mov     flags.wrpflg,0          ; assume not wrap mode
        test    targ.flgs,lnwrap        ; in wrap mode?
        jz      tem2                    ; no, continue
        mov     flags.wrpflg,1
tem2:
        mov     al,kbdflg               ; get the char from Term, if any
        mov     kbdflg,0                ; clear the flag
        cmp     al,0                    ; was there a char from Term?
        jne     intch0                  ; ne = yes, else ask for one from kbd
intchr: mov     ah,dconio               ; Direct console I/O.
        mov     dl,0FFH                 ; Input.
        int     dos                     ; Get a char.
        jz      intchr                  ; no char, keep looking
intch0:
        mov     ah,al
        cmp     ah,' '                  ; space - ignore it
        je      tem
        mov     bh,ah                   ; Save the actual char.
        and     ah,not ('a'-'A')        ; Convert to upper case.
        or      ah,40H                  ; convert ctl-char to actual char.
        cmp     ah,'C'                  ; Is it close?
        jne     intch1
        call    serrst                  ; reset serial port
        mov     ttyact,0                ; say we are no longer active
        jmp     rskp                    ; and return

intch1: cmp     ah,'S'                  ; Is it status?
        jne     intch2                  ; ne = no
        call    stat0                   ; If so, call stat0.
        call    puthlp                  ; put help on screen
        mov     dx,offset sttmsg
        mov     ah,prstr
        int     dos
intch1a:mov     ah,coninq               ; console input, no echo
        int     dos
        cmp     al,' '                  ; space?
        jne     intch1a
        and     targ.flgs,not scrsam    ; remember screen changed.
        jmp     tem
intch2: cmp     ah,'B'                  ; Send a break?
        jne     intch3                  ; No
        call    sendbr                  ; Yes, so send a break
        jmp     tem                     ; And return
intch3: cmp     ah,'M'                  ; mode line?
        jne     intch4
        cmp     bh,cr                   ; check ^M (cr) against plain ascii M
        je      intch3a                 ; exit on cr
        cmp     flags.modflg,1          ; mode line enabled?
        jne     intch3a                 ; ne = no, leave it alone
        xor     targ.flgs,modoff        ; enabled, toggle its state
intch3a:jmp     tem                     ; and reconnect
intch4: cmp     ah,'F'                  ; Dump screen to filespec?
        jne     intch4a                 ; ne = no
        call    dumpscr                 ; call global proc in MSX or MSY
        jmp     tem                     ; and return
intch4a:cmp     bh,'?'                  ; Is it help?
        jne     intch5                  ; If not, go to the next check.
        mov     ax,offset inthlp        ; get the address of the help message.
        call    puthlp                  ; write help msg
        mov     dx,offset intprm
        mov     ah,prstr                ; Print it.
        int     dos
        and     targ.flgs,not scrsam    ; remember screen changed
        jmp     intchr                  ; Get another char.
intch5: cmp     bh,trans.escchr         ; Is it the escape char?
        jne     intch7                  ; ne = no
intch6: mov     ah,al
        call    outchr
        nop
        nop
        nop
        jmp     tem                     ; Return, we are done here.
intch7: cmp     ah,'Q'                  ; 'Q' maybe want to stop logging?
        jne     intch8
        test    targ.flgs,capt          ; not capturing, can't do this
        jz      intc11
        and     targ.flgs,not capt      ; stop capturing
        jmp     tem                     ; and resume
intch8: cmp     ah,'R'                  ; maybe resume?
        jne     intch9                  ; no, keep going
        test    flags.capflg,logses     ; can we capture?
        jz      intc11                  ; no, forget it
        test    targ.flgs,capt          ; already capturing?
        jnz     intc11                  ; yes, can't toggle back on then
        or      targ.flgs,capt          ; else turn flag on
        jmp     tem                     ; and resume
intch9: cmp     bh,'0'          ; perhaps want a null (note original chr in bh)
        jne     intc10
        mov     ah,0
        call    outchr
        nop
        nop
        nop
        jmp     tem
intc10: cmp     ah,'P'                  ; maybe want to push?
        jne     intc11                  ; no, go on
        push    bx                      ; send XOFF, if possible, to hold host
        mov     bx,portval              ;  from Paul Amaranth
        mov     ah,byte ptr [bx].flowc  ; get XOFF char, or null
        pop     bx
        or      ah,ah                   ; check for null (no flow control)
        jz      intch10a                ; z = null, don't send one
        call    outchr                  ; send XOFF to host while we are away
         nop
         nop
         nop
intch10a:call   serrst                  ; turn  off serial interrupts
        call    fpush                   ; try pushing
         nop
         nop
         nop
        mov     dx,offset sttmsg
        mov     ah,prstr
        int     dos
        call    serini                  ; init serial port again
        call    ihostr                  ; XON the host (if using flow control)
        jmp     intch1a                 ; wait for space
intc11: cmp     ah,'L'                  ; 'L', send a long Break?
        jne     intc12                  ; ne = no
        call    sendbl                  ; send a long one
        jmp     tem
intc12: cmp     ah,'H'                  ; 'H', hangup the phone
        jne     intc13                  ; ne = no
        call    serhng
        call    serrst                  ; turn off port
        jmp     tem
intc13: call    beep                    ; say illegal character
        jmp     tem
TELNET  ENDP
; Reassure user about connection to the host. Tell him what escape sequence
; to use to return and the communications port and baud; rate being used.

DOMSG   PROC    NEAR
        mov     ah,prstr
        mov     dx,offset tmsg1
        int     dos
        call    escprt
        mov     ah,prstr
        mov     dx,offset tmsg3
        int     dos
        ret
DOMSG   ENDP

; print the escape character in readable format.

ESCPRT  PROC    NEAR
        mov     dl,trans.escchr
        cmp     dl,' '
        jge     escpr2
        push    dx
        mov     ah,prstr
        mov     dx,offset esctl
        int     dos
        pop     dx
        add     dl,040H         ; Make it printable.
escpr2: mov     ah,conout
        int     dos
        ret
ESCPRT  ENDP


; Set parity for character in Register AL.

dopar:  push    bx
        mov     bx,portval
        mov     bl,[bx].parflg          ; get parity flag byte
        cmp     bl,parnon               ; No parity?
        je      parret                  ; Just return
        and     al,07FH                 ; Strip parity. Same as Space parity
        cmp     bl,parspc               ; Space parity?
        je      parret                  ; e = yes, then we are done here
        cmp     bl,parevn               ; Even parity?
        jne     dopar0                  ; ne = no
        or      al,al
        jpe     parret                  ; pe = even parity now
        xor     al,080H                 ; Make it even parity.
        jmp     short parret
dopar0: cmp     bl,parmrk               ; Mark parity?
        jne     dopar1                  ; ne = no
        or      al,080H                 ; Turn on the parity bit.
        jmp     short parret
dopar1: cmp     bl,parodd               ; Odd parity?
        or      al,al
        jpo     parret                  ; Already odd, leave it.
        xor     al,080H                 ; Make it odd parity.
parret: pop     bx
        ret

inicpt  proc    near
        mov     capbp,offset capbuf
        mov     caplft,cptsiz           ; init buffer ptr & chrs left
        ret                             ; and return
inicpt  endp


cptchr  proc    near                    ; capture routine, char in al
        push    di
        mov     di,capbp
        mov     byte ptr [di],al
        inc     capbp
        pop     di
        dec     caplft                  ; decrement chars remaining
        jg      cptch1                  ; more room, forget this part
        call    cptdmp                  ; dump the info
        call    inicpt                  ; re-init ptrs.
cptch1: ret                             ; and return
cptchr  endp

cptdmp  proc    near                    ; empty the capture buffer
        push    ax
        push    bx
        push    cx
        push    dx
        mov     dx,offset capbuf        ; the capture routine buffer
        mov     bx,cptio.handle         ; get file handle
        mov     cx,cptsiz               ; original buffer size
        sub     cx,caplft               ; minus number remaining
        jl      cptdm2                  ; means error
        mov     ah,write2               ; write with filehandle
        int     dos                     ; write out the block
        jnc     cptdm1                  ; carry set means error
cptdm2: and     targ.flgs,not capt      ; so please stop capturing
        mov     dx,offset erms23        ; tell user the bad news
        mov     ah,prstr
        int     dos
cptdm1: pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
cptdmp  endp

clscpt  proc    near
        test    flags.capflg,0FFH       ; doing capture
        jnz     clscp1                  ; yes, go ahead
        mov     dx,offset erms22
        mov     ah,prstr
        int     dos
        jmp     rskp
clscp1: mov     ah,cmcfm
        call    comnd
         jmp    r
clscpi: mov     ax,caplft
        cmp     ax,cptsiz               ; is buffer empty?
        je      clscp2                  ; yes, forget this stuff
        call    cptdmp                  ; dump buffer (preserves registers)
clscp2: mov     ah,close2               ; DOS 2.0 close
        push    bx
        mov     bx,cptio.handle         ; file handle
        int     dos                     ; close up file
        pop     bx
        mov     flags.capflg,logoff     ; no longer capturing...
        jmp     rskp                    ; and return
clscpt  endp

; enter with ax/scan code to define, si/ pointer to definition, cx/ length
; of definition.  Defines it in definition table.
defkey  proc    near
        ret
defkey  endp

; worker: copy line from si to di, first removing trailing spaces, second
; parsing out curly braced strings, then third converting \{b##} in strings
; to binary numbers. Returns carry set if error; else carry clear, with byte
; count in cx. Braces are optional but must occur in pairs.
; Items which cannot be converted to legal numbers are copied verbatium
; to the output string (ex: \{c}  is copied as \{c}  but \{x0d} is hex 0dh).
cnvlin  proc    near
        push    ax
        push    si                      ; source ptr
        push    di                      ; destination ptr
        push    es                      ; end of save regs
        push    ds                      ; move ds into es
        pop     es                      ; use datas segment for es:di
        call    cnvstr                  ; trim trailing, parse curly braces
        xor     cx,cx                   ; initialize returned byte count
cnvln1: cmp     byte ptr [si],0         ; at end of string?
        je      cnvln2                  ; e = yes, exit
        call    katoi                   ; read char, convert ascii to binary
        cld
        stosb                           ; save the char
        inc     cx                      ; and count it
        or      ah,ah                   ; is returned number > 255?
        jz      cnvln1                  ; z = no, do more chars
        push    ax
        stosb                           ; save high order byte next
        pop     ax
        inc     cx
        jmp     short cnvln1            ; do more chars
cnvln2: mov     byte ptr [di],0         ; plant terminator
        clc                             ; clear c bit, success
cnvlnx: pop     es                      ; restore regs
        pop     di                      ; destination ptr
        pop     si                      ; source ptr
        pop     ax
        ret
cnvlin  endp

; Convert string by first remove trailing spaces and then removing surrounding
; curly brace delimiter pair. Converts text in place.
; Enter with source ptr in si.
; Preserves all registers, uses byte tmp. 9 Oct 1987 [jrd]
;
cnvstr  proc    near
        push    ax
        push    cx
        push    dx
        push    si                      ; save start of source string
        push    di
        push    es
                                        ; 1. Trim trailing spaces
        mov     dx,si                   ; source address
        call    strlen                  ; get current length to cx
        jcxz    cnvst4                  ; z = nothing there
        mov     di,si                   ; set di to source address
        add     di,cx                   ; start at end of string
        dec     di                      ; ignore terminator
        mov     al,spc                  ; scan while spaces
        push    ds
        pop     es                      ; set es to datas segment
        std                             ; search backward
        repe    scasb                   ; scan off trailing spaces
        mov     byte ptr [di+2],0       ; terminate string after last text
        cld
        mov     di,si                   ; set destination address to source
                                        ; 2. Parse off curly brace delimiters
        cmp     byte ptr [si],braceop   ; opening brace?
        jne     cnvst4                  ; ne = no, ignore brace-matching code
        inc     si                      ; skip opening brace
        mov     dl,braceop              ; opening brace (we count them up)
        mov     dh,bracecl              ; closing brace (we count them down)
        mov     tmp,1                   ; we are at brace level 1
cnvst1: cld                             ; search forward
        lodsb                           ; read a string char
        stosb                           ; store char (skips opening brace)
        cmp     al,0                    ; at end of string?
        je      cnvst4                  ; e = yes, we are done
        cmp     al,dl                   ; an opening brace?
        jne     cnvst2                  ; ne = no
        inc     tmp                     ; yes, increment brace level
        jmp     short cnvst1            ;  and continue scanning

cnvst2: cmp     al,dh                   ; closing brace?
        jne     cnvst1                  ; ne = no, continue scanning
        dec     tmp                     ; yes, decrement brace level
        cmp     byte ptr [si],0         ; have we just read the last char?
        jne     cnvst3                  ; no, continue scanning
        mov     tmp,0                   ; yes, this is the closing brace
cnvst3: cmp     tmp,0                   ; at level 0?
        jne     cnvst1                  ; ne = no, #opening > #closing braces
        mov     byte ptr [di-1],0       ; plant terminator on closing brace

cnvst4: pop     es                      ; recover original registers
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     ax
        ret
cnvstr  endp


; Convert ascii strings of the form "\{bnnn}" to a binary word in ax.
; The braces are optional but must occur in pairs. Numeric base indicator "b"
; is O or o or X or x or D or d or missing, for octal, hex, or decimal (def).
; Enter with si pointing at "\".
; Returns binary value in ax with carry clear and si to right of "}" or at
; terminating non-numeric char if successful; otherwise, a failure,
; return carry set with si = entry value + 1 and first read char in al.

katoi   proc    near
        cld
        lodsb                           ; get first char
        xor     ah,ah                   ; clear high order field
        push    cx                      ; save working reg
        push    si                      ; save entry si+1
        push    bx
        push    ax                      ; save read char
katoi1: cmp     al,0                    ; end of text?
        je      katoi1a                 ; e = yes, exit failure
        cmp     al,'\'                  ; escape char?
        je      katoi1b                 ; e = yes
katoi1a:jmp     katoix                  ; common jump point to exit failure
katoi1b:lodsb                           ; get next char, maybe brace
        cmp     al,0                    ; premature end?
        je      katoi1a                 ; e = yes, exit failure
        xor     bx,bx                   ; no conv yet, assume no opening brace
        cmp     al,braceop              ; opening brace?
        jne     katoi2                  ; ne = no, have number or base
        mov     bl,bracecl              ; remember a closing brace is needed
        lodsb                           ; get number base, if any
katoi2: xor     cx,cx                   ; temporary place for binary value
        mov     nbase,10                ; assume decimal numbers
        cmp     al,0                    ; premature end?
        je      katoix                  ; e = yes, exit failure
        cmp     al,'a'                  ; lower case?
        jb      katoi3                  ; b = no
        cmp     al,'z'                  ; in range of lower case?
        ja      katoi3                  ; a = no
        and     al,5fh                  ; map to upper case
katoi3: cmp     al,'O'                  ; octal?
        jne     katoi4                  ; ne = no
        mov     nbase,8                 ; set number base
        jmp     short katoi6
katoi4: cmp     al,'X'                  ; hex?
        jne     katoi5                  ; ne = no
        mov     nbase,16
        jmp     short katoi6
katoi5: cmp     al,'D'                  ; decimal?
        jne     katoi7                  ; ne = no base char, assume decimal
        mov     nbase,10
katoi6: lodsb                           ; get a digit
katoi7: cmp     al,0                    ; premature end?
        je      katoi8a                 ; e = yes, use it as a normal end
        cmp     al,bl                   ; closing brace?
        je      katoi9                  ; e = yes
        call    cnvdig                  ; convert ascii to binary digit
        jc      katoi8                  ; c = cannot convert
        inc     bh                      ; say we did a successful conversion
        xor     ah,ah                   ; clear high order value
        push    ax                      ; save this byte's value
        xchg    ax,cx                   ; put binary summation in ax
        mul     nbase                   ; scale up current sum
        xchg    ax,cx                   ; put binary back in cx
        pop     ax                      ; recover binary digit
        add     cx,ax                   ; form running sum
        jmp     short katoi6            ; get more

katoi8: cmp     bl,0                    ; closing brace needed?
        jne     katoix                  ; ne = yes, but not found
katoi8a:dec     si                      ; backup to reread terminator
katoi9: cmp     bh,0                    ; did we do any conversion?
        je      katoix                  ; e = no, exit failure
        pop     ax                      ; throw away old saved ax
        pop     bx                      ; restore bx
        pop     ax                      ; throw away starting si, keep current
        mov     ax,cx                   ; return final value in ax
        pop     cx                      ; restore old cx
        clc                             ; clear carry for success
        ret
katoix: pop     ax                      ; restore first read al
        pop     bx
        pop     si                      ; restore start value + 1
        pop     cx                      ; restore old cx
        stc                             ; set carry for failure
        ret
katoi   endp

cnvdig  proc    near                    ; convert ascii code in al to binary
        push    cx                      ; return carry set if cannot.
        push    es                      ; nbase has numeric base.
        push    di
        push    ax
        cmp     al,'a'                  ; lower case?
        jb      cnvdig1                 ; b = no
        cmp     al,'f'                  ; highest hex digit
        ja      cnvdigx                 ; a = illegal symbol
        sub     al,'a'-'A'              ; convert 'a' to 'f' to upper case
cnvdig1:mov     di,offset numset        ; set of legal number symbols
        mov     cx,nbase                ; number of legal symbols in this base
        cmp     cx,cx                   ; preset z flag
        push    ds
        pop     es                      ; point es at data segment
        cld                             ; scan forward
        repne   scasb                   ; find character in set
        jne     cnvdigx                 ; ne = not found
cnvdig2:inc     cx                      ; offset auto-dec of repne scasb above
        sub     cx,nbase                ; counted off minus length
        neg     cx                      ; two's complement = final value
        pop     ax                      ; saved ax
        mov     ax,cx                   ; return binary in al
        clc                             ; c clear for success
        jmp     short cnvdixx           ; exit
cnvdigx:stc                             ; c set for failure
        pop     ax
cnvdixx:pop     di
        pop     es
        pop     cx
        ret
cnvdig  endp

decout  proc    near            ; display decimal number in ax
        push    ax
        push    cx
        push    dx
        mov     cx,10           ; set the numeric base
        call    valout          ; convert and output value
        pop     dx
        pop     cx
        pop     ax
        ret
decout  endp

valout  proc    near            ; output number in ax using base in cx
                                ; corrupts ax and dx
        xor     dx,dx           ; clear high word of numerator
        div     cx              ; (ax / cx), remainder = dx, quotient = ax
        push    dx              ; save remainder for outputting later
        or      ax,ax           ; any quotient left?
        jz      valout1         ; z = no
        call    valout          ; yes, recurse
valout1:pop     dx              ; get remainder
        add     dl,'0'          ; make digit printable
        cmp     dl,'9'          ; above 9?
        jbe     valout2         ; be = no
        add     dl,'A'-1-'9'    ; use 'A'--'F' for values above 9
valout2:mov     ah,conout
        int     dos
        ret
valout  endp

; Convert input in buffer pointed to by SI to real number which is returned
; in AX.  Enter with string size in AX.
; Return on failure, return skip on success.      Revised by [jrd]
ATOI    PROC    NEAR
        mov     bx,0            ; high order of this stays 0
        mov     tmp,0           ; No input yet
        mov     cl,ah           ; Number of chars of input.
        mov     ch,0            ; size of string
        mov     ax,0            ; init sum
        cld
atoi0:  jcxz    atoi4           ; Fail on no input.
        lodsb                   ; get an input char
        dec     cx              ; count number remaining
        cmp     al,';'          ; start of comment?
        jne     atoi0a          ; ne = no
        dec     si              ; backup to semicolon
        mov     byte ptr [si],0 ; plant terminator
        xor     cx,cx           ; clear bytes remaining field
        jmp     atoi4           ; and exit
atoi0a: cmp     al,'0'          ; got punctuation?
        jb      atoi0           ; e = yes, keep scanning
        dec     si              ; back up source pointer for reread below.
        inc     cx              ; and readjust byte counter
        mov     ax,0            ; clear sum
atoi1:  push    ax              ; save sum
        lodsb                   ; read a byte into al
        mov     bl,al           ; put it into bl
        pop     ax              ; regain sum
        cmp     bl,'9'          ; check range for '0' to '9'
        ja      atoi2           ; above '9'
        cmp     bl,'0'
        jb      atoi2           ; below '0'
        sub     bl,'0'          ; take away ascii bias
        mul     ten             ; sum * 10. dx = high, ax = low
        add     ax,bx           ; add current value
        mov     tmp,1           ; say have sum being computed
        loop    atoi1
        inc     si              ; inc for dec below
atoi2:  dec     si              ; point at terminator
        cmp     tmp,0           ; were any digits discovered?
        je      atoi4           ; No so fail
        jmp     rskp            ; success exit, sum is in ax
atoi4:  mov     dx,offset erms25 ; Input must be numeric
        ret
ATOI    ENDP


; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.

RSKP    PROC    NEAR
        pop     bp
        add     bp,3
        push    bp
        ret
RSKP    ENDP

; Jumping here is the same as a ret.

R       PROC    NEAR
        ret
R       ENDP

code    ends
        end
