home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / msk316src.zip / MSSTER.ASM < prev    next >
Assembly Source File  |  1999-04-24  |  79KB  |  2,744 lines

  1.      NAME    msster
  2. ; File MSSTER.ASM
  3.     include mssdef.h
  4. ;    Copyright (C) 1982, 1999, Trustees of Columbia University in the 
  5. ;    City of New York.  The MS-DOS Kermit software may not be, in whole 
  6. ;    or in part, licensed or sold for profit as a software product itself,
  7. ;    nor may it be included in or distributed with commercial products
  8. ;    or otherwise distributed by commercial concerns to their clients 
  9. ;    or customers without written permission of the Office of Kermit 
  10. ;    Development and Distribution, Columbia University.  This copyright 
  11. ;    notice must not be removed, altered, or obscured.
  12. ;
  13. ; Edit history
  14. ; 12 Jan 1995 version 3.14
  15. ; Last edit
  16. ; 12 Jan 1995
  17.  
  18.     public    clscpt, defkey, clscpi, ploghnd, sloghnd, tloghnd
  19.     public  dopar, shokey, cptchr, pktcpt, targ, replay, repflg
  20.     public    kbdflg, shkadr, telnet, ttyact, write, dec2di, caplft
  21.     public    cnvlin,  decout, valout, cnvstr, writeln
  22.     public    pntchr, pntflsh, pntchk, prnhand, prnopen
  23.     public    vfopen, vfread, ldiv, lmul, lgcd, atoi, atoibyte
  24.     public  domath, domath_ptr, domath_cnt, domath_msg
  25.     public    atoi_cnt, atoi_err, tod2secs
  26.  
  27. braceop    equ    7bh            ; opening curly brace
  28. bracecl    equ    7dh            ; closing curly brace
  29.  
  30. data     segment
  31.     extrn    flags:byte, trans:byte, diskio:byte, portval:word
  32.     extrn    rdbuf:byte, dosnum:word, filtst:byte, prnname:byte
  33.     extrn    comand:byte, kstatus:word, flowon:byte, flowoff:byte
  34.     extrn    cardet:byte
  35. ifndef    no_terminal
  36.     extrn    anspflg:byte
  37. endif    ; no_terminal
  38.  
  39. targ    termarg    <0,1,cptchr,0,parnon>
  40. crlf    db      cr,lf,'$'
  41. tmsg1    db    cr,lf,'(Connecting to host, type $' 
  42. tmsg3    db    ' C to return to PC)',cr,lf,cr,lf,cr,lf,'$'
  43. erms21    db    cr,lf,'?Cannot start the connection.$'
  44. erms25    db    cr,lf,'?Input must be numeric$' 
  45. erms22    db    cr,lf,'?No open logging file$'
  46. erms23    db    '*** Error writing session log, suspending capture ***$'
  47. erms24    db    cr,lf,'?Error writing Packet log$'
  48. erms26    db    ' *** PRINTER IS NOT READY ***  press R to retry'
  49.     db    ' or D to discard printing:  $'
  50. erms27    db    cr,lf
  51.     db    '? Carrier signal not detected on this serial port.'
  52.     db    cr,lf
  53.     db    '  Use the DIAL command, SET CARRIER OFF, or check your modem.$'
  54.  
  55. esctl    db    'Control-$'
  56. repflg    db    0        ; REPLAY or SET TERM REPLAY filespec flag
  57. reperr    db    cr,lf,'?File not found$'    ; for REPLAY command
  58. noterm    db    cr,lf,'Connect mode is not built into this Kermit$'
  59. vfopbad    db    cr,lf,'?Cannot open file $'    ; filename follows
  60. vfclbad    db    cr,lf,'?Cannot close file$'
  61. vfoptwice db    cr,lf,'?File is already open$'
  62. vfnofile db    cr,lf,'?File is not open$'
  63. vfrbad    db    cr,lf,'?Error while reading file$'
  64. vfwbad    db    cr,lf,'?Error while writing file$'
  65. vfrdbad    db    cr,lf,'?more parameters are needed$'
  66. vfrhandle dw    -1            ; READ FILE handle (-1 = invalid)
  67. vfwhandle dw    -1            ; WRITE FILE handle
  68.  
  69. opntab    db    3            ; OPEN FILE table
  70.     mkeyw    'Read',1
  71.     mkeyw    'Write',2
  72.     mkeyw    'Append',3
  73.  
  74. inthlp db cr,lf,' ', 22 dup ('-'),' Special keys within Connect mode '
  75.        db    22 dup ('-')
  76.        db cr,lf,'                           numeric keypad keys:'
  77.        db cr,lf,'  Pg Up/Dn  roll screen vertically   Ctrl Pg Up/Dn  roll'
  78.        db    ' screen one line '
  79.        db cr,lf,'  Home/End  roll to start/end screen Ctrl End screen dump'
  80.        db cr,lf,'  - (minus) toggle status/mode line'
  81.        db cr,lf,'                           other key combinations:'
  82.        db cr,lf,'  Alt-b  send a BREAK                Alt-n  next active'
  83.        db    ' Telnet session'
  84.        db cr,lf,'  Alt-c  start Compose sequence      Alt-x  exit emulator'
  85.        db cr,lf,'  Alt-h  show this menu              Alt-z  network session'
  86.        db    ' hold'
  87.        db cr,lf,'  Alt-= or Alt-r  reset emulator     Alt--  toggle terminal'
  88.        db    ' type'
  89.        db cr,lf,'  Ctrl Prtscrn  toggle printing'
  90.        db cr,lf,'  Additional commands are escape character '
  91. inthlpc db '  '
  92.        db    ' followed by special characters.'
  93.        db cr,lf,' ',78 dup ('-')
  94.        db cr,lf,'  7n1 = SET TERM BYTE 7, SET PARITY NONE, 1 stop bit'
  95.        db cr,lf,'  8n1 = SET TERM BYTE 8, SET PARITY NONE, 1 stop bit'
  96.        db cr,lf,'  7e1 = SET PARITY EVEN, 1 stop bit   7s1 = SET PARITY SPACE'
  97.        db    ', 1 stop bit'
  98.        db cr,lf,'  7o1 = SET PARITY ODD,  1 stop bit   7m1 = SET PARITY MARK'
  99.        db    ',  1 stop bit'
  100.        db cr,lf,'  press a key to exit this menu'
  101.        db    0
  102.  
  103. intqry db cr,lf,' ',20 dup ('-'),' Single character commands active now ' 
  104.        db    20 dup ('-')
  105.        db cr,lf,'  ?  Show this menu                  F  Dump screen to file'
  106.        db cr,lf,'  C  Close the emulator              P  Push to DOS'
  107.        db cr,lf,'  S  Status of the connection        Q  Quit logging'
  108.        db cr,lf,'  M  Toggle mode line                R  Resume logging'
  109.        db cr,lf,'  B  Send a Break                    0  Send a null'
  110.        db cr,lf,'  L  Send a long 1.8 s Break         H  Hangup phone'
  111.        db cr,lf,'  A  Send Telnet "Are You There"     I  Send Telnet' 
  112.        db    ' "Interrupt Process"' 
  113.        db cr,lf,'  Typing the escape character '
  114. intqryc db '  '                    ; where escape char glyphs go
  115.        db    ' will send it to the host'
  116.        db cr,lf,'  press space bar to exit this menu'
  117.        db 0
  118.  
  119. intprm    db    'Command> $'
  120. intclet    db    'B','C','F','H','L'    ; single letter commands
  121.     db    'M','P','Q','R','S'    ; must parallel dispatch table intcjmp
  122.     db    '?','0','A','I'
  123. numlet    equ    $ - intclet        ; number of entries
  124.     even
  125. ifndef    no_terminal
  126. intcjmp    dw    intchb,intchc,intchf,intchh,intchl
  127.     dw    intchm,intchp,intchq,intchr,intchs
  128.     dw    intchu,intchn,intayt,inttip
  129. endif    ; no_terminal
  130.  
  131. prnhand    dw    4        ; printer file handle (4 = DOS default)
  132.  
  133.     even
  134. ploghnd    dw    -1        ; packet logging handle
  135. sloghnd    dw    -1        ; session logging handle
  136. tloghnd    dw    -1        ; transaction logging handle
  137.  
  138. clotab    db    6
  139.     mkeyw    'READ-FILE',4001h
  140.     mkeyw    'WRITE-FILE',4002h
  141.     mkeyw    'All-logs',logpkt+logses+logtrn
  142.     mkeyw    'Packets',logpkt
  143.     mkeyw    'Session',logses
  144.     mkeyw    'Transactions',logtrn
  145.  
  146. clseslog db    cr,lf,' Closing Session log$'
  147. clpktlog db    cr,lf,' Closing Packet log$'
  148. cltrnlog db    cr,lf,' Closing Transaction log$'
  149.  
  150. writetab db    5            ; Write command log file types
  151.     mkeyw    'FILE',4002h        ; FILE
  152.     mkeyw    'Packet',logpkt
  153.     mkeyw    'Screen',80h        ; unused value, to say screen
  154.     mkeyw    'Session',logses
  155.     mkeyw    'Transaction',logtrn
  156.  
  157. sttmsg    db    cr,lf,'Press space to continue ...$'
  158. kbdflg    db    0            ; non-zero means char here from Term
  159. ttyact    db    1            ; Connect mode active, if non-zero
  160. shkadr    dw    0            ; offset of replacement Show Key cmd
  161. nbase    dw    10            ; currently active number base
  162. temp    dw    0
  163. temp1    dw    0
  164. tmp    db    0
  165. tmp1    db    0
  166. tmpstring db    6 dup (0)        ; local string work buffer
  167. pktlft    dw    cptsiz        ; number free bytes left
  168. caplft    dw    cptsiz        ; number free bytes left
  169.  
  170. maxdepth equ    8            ; domath, parenthesis nesting depth
  171. listlen equ    6            ; domath, outstanding numbers and ops
  172. parendepth db    0            ; domath, depth of ()'s
  173. mathkind db    0            ; domath, math operator symbol, temp
  174. operators db    '()!~^*/%&+-|#@'    ; domath, math operator list
  175. operator_len equ $ - operators        ; domath, length of the list
  176. precedence db    6,6,5,4,3, 2,2,2,2,1, 1,1,1,1 ; domath, precedence of ops
  177. fevalst    struc        ; structure of demath work stacks, all on the stack
  178.   savebp    dw    0        ; where bp is pushed
  179.   numcnt    dw    0        ; count of values in numlist
  180.   numlist    dw listlen dup (0,0)    ; list of dword values
  181.   opcnt        dw    0        ; count of operators in oplist
  182.   oplist    dw  listlen dup (0)    ; list of pending operators
  183. fevalst    ends
  184. domath_ptr dw    0            ; ds:offset of domath input text
  185. domath_cnt dw    0            ; count of bytes in input text
  186. domath_msg dw    0            ; non-zero to allow error msgs
  187. matherr dw    0            ; non-zero = offset of error msg
  188. opmsg    db    '?too many math operators or too few values $'
  189. opmsg1    db    '?math operator missing or invalid $'
  190. opmsg2    db    '?unknown math symbol: $'
  191. opmsg3 db    '?math expression error $'
  192. opmsg4    db    '?parentheses nested too deeply $'
  193. atoibyte db    0            ; non-zero to say convert one char
  194. atoi_err db    0            ; atoi return error codes
  195. atoi_cnt dw    0            ; atoi, bytes supplied to/remaining
  196. data    ends
  197.  
  198. data1    segment
  199. rephlp    db    'name of file to playback$'
  200. clohlp    db    cr,lf,' READ-FILE or WRITE-FILE, or the following log files:'
  201.     db    cr,lf,' All-logs, Packets, Session, Transaction$'
  202. msgtxt    db    'text to be written$'
  203. vfophlp    db    'Filename$'
  204. vfrdmsg    db    'name of variable  into which to read a line from file$'
  205. pktbuf    db    cptsiz dup (0)    ; packet logging buffer
  206. pktbp    dw    pktbuf        ; buffer pointer to next free byte
  207. capbuf    db    cptsiz dup (0)    ; session logging buffer
  208. capbp    dw    capbuf        ; buffer pointer to next free byte
  209. prnbuf    db    cptsiz dup (0)    ; printer buffer
  210. pntptr    dw    prnbuf        ; pointer to next free byte
  211. data1    ends
  212.  
  213. code1    segment
  214.     extrn    iseof:far, strlen:far, prtasz:far, isfile:far
  215.     assume    cs:code1
  216. code1    ends
  217.  
  218. code    segment
  219.     extrn     comnd:near, outchr:near, stat0:near, pcwait:far
  220.     extrn    beep:near, puthlp:near, serhng:near, lnout:near
  221.     extrn    serini:near, serrst:near, sendbr:near, putmod:near
  222.     extrn    fpush:near,  sendbl:near, trnmod:near, dodecom:near
  223. ifndef    no_terminal
  224.     extrn    dumpscr:near,term:near
  225. endif    ; no_terminal
  226. flnout    proc    far
  227.     call    lnout
  228.     ret
  229. flnout    endp
  230.     assume    cs:code, ds:data, es:nothing
  231.  
  232. ; the show key command
  233. shokey    proc    near
  234.     cmp    shkadr,0        ; keyboard translator present?
  235.     je    shokey1            ; e = no, use regular routines
  236.     mov    bx,shkadr        ; get offset of replacement routine
  237.     jmp    bx            ; and execute it rather than us
  238. shokey1:clc
  239.     ret
  240. shokey    endp
  241. ; enter with ax/scan code to define, si/ pointer to definition, cx/ length
  242. ; of definition.  Defines it in definition table. Obsolete.
  243. defkey    proc    near
  244.     ret
  245. defkey    endp
  246.  
  247.  
  248. ; This is the CONNECT command
  249. ifdef    no_terminal
  250. TELNET     PROC    NEAR
  251.     mov    ah,cmeol
  252.     call    comnd            ; get a confirm
  253.     jnc    teln1            ; nc = success
  254.     ret
  255. teln1:    mov    ah,prstr
  256.     mov    dx,offset noterm
  257.     int    dos
  258.     clc
  259.     ret
  260. TELNET    ENDP
  261. endif    ; no_terminal
  262.  
  263. ifndef    no_terminal
  264. TELNET     PROC    NEAR
  265.     mov    ah,cmeol
  266.     call    comnd            ; get a confirm
  267.     jnc    teln1            ; nc = success
  268.     ret
  269. teln1:    cmp    repflg,0        ; REPLAY?
  270.     jne    teln1d            ; ne = yes
  271.     call    serini            ; ensure port is inited now
  272.     jnc    teln1a            ; nc = success
  273.     test    flags.remflg,dquiet    ; quiet display mode?
  274.     jnz    teln1b            ; nz = yes. Don't write to screen
  275.     mov    dx,offset erms21    ; say cannot start connection
  276.     mov    ah,prstr
  277.     int    dos
  278. teln1b:    or    kstatus,ksgen        ; general command failure
  279.     ret
  280. teln1a:    cmp    flags.comflg,'F'    ; Fossil port?
  281.     je    teln1c            ; e = yes
  282.     cmp    flags.comflg,'4'    ; using real or Bios serial ports?
  283.     ja    teln1d            ; a = no
  284. teln1c:    cmp    flags.carrier,0        ; want CD on now?
  285.     je    teln1d            ; e = no
  286.     test    cardet,80h        ; CD on?
  287.     jnz    teln1d            ; nz = yes
  288.     mov    ah,prstr        ; complain on screen
  289.     mov    dx,offset erms27
  290.     int    dos
  291.     stc                ; fail
  292.     ret
  293. teln1d:    cmp    flags.vtflg,0        ; emulating a terminal?
  294.     jne    teln2            ; ne= yes, no wait necessary
  295.     mov    ah,prstr
  296.     mov    dx,offset crlf        ; output a crlf
  297.     int    dos
  298.     call    domsg            ; reassure user
  299.     mov    ax,2000            ; two seconds
  300.     call    pcwait            ; pause
  301. teln2:    xor    al,al            ; initial flags
  302.     mov    ttyact,1        ; say telnet is active
  303.     cmp    flags.vtflg,0        ; emulating a terminal?
  304.     je    teln3            ; e = no, say mode line is to be off
  305.     cmp    flags.modflg,0        ; mode line enabled?
  306.     jne    tel010            ; ne = yes
  307. teln3:    or    al,modoff        ; no, make sure it stays off
  308.  
  309. tel010:    test    flags.debug,logses    ; debug mode?
  310.     jz    tel0            ; z = no, keep going
  311.     or    al,trnctl        ; yes, show control chars
  312. tel0:    cmp    flags.vtflg,0        ; emulating a terminal?
  313.     je    tel1            ; e = no
  314.     or    al,emheath        ; say emulating some kind of terminal
  315. tel1:    mov    bx,portval
  316.     cmp    [bx].ecoflg,0        ; echoing?
  317.     jz    tel2            ; z = no
  318.     or    al,lclecho        ; turn on local echo
  319. tel2:    mov    targ.flgs,al        ; store flags
  320.     mov    ah,flags.comflg        ; COMs port identifier
  321.     mov    targ.prt,ah        ; Port 1 or 2, etc
  322.     mov    ah,[bx].parflg        ; parity flag
  323.     mov    targ.parity,ah
  324.     mov    ax,[bx].baud        ; baud rate identifier
  325.     mov    targ.baudb,al
  326.     xor    ah,ah
  327.     test    flags.capflg,logses    ; select session logging flag bit
  328.     jz    tel3            ; z = no logging
  329.     mov    ah,capt            ; set capture flag
  330. tel3:    or    targ.flgs,ah
  331.     jmp    short tem1
  332.  
  333. TEM:    call    serini            ; init serial port
  334.     jnc    tem1            ; nc = success
  335.     clc
  336.     ret                ; and exit Connect mode
  337.  
  338. tem1:    mov    dx,offset crlf        ; give user an indication that we are
  339.     mov    ah,prstr        ; entering terminal mode
  340.     int    dos
  341.     mov    ttyact,1        ; say telnet is active
  342.     mov    ax,offset targ        ; point to terminal arguments
  343.     call    term            ; call the main Terminal procedure
  344.     mov    al,kbdflg        ; get the char from Term, if any
  345.     mov    kbdflg,0        ; clear    the flag
  346.     or    al,al            ; was there a char from Term?
  347.     jnz    intch2            ; nz = yes, else ask for one from kbd
  348.  
  349. intchar:call    iseof            ; stdin at eof?
  350.     jnc    intch1            ; nc = not eof, get more
  351.     mov    al,'C'            ; use C when file is empty
  352.     jmp    intchc            ;  to provide an exit
  353. intch1:    cmp    al,0            ; asking for help?
  354.     jne    intch1b            ; ne = no
  355.     mov    al,' '
  356.     mov    ah,trans.escchr
  357.     cmp    ah,' '            ; printable now?
  358.     jae    intch1a            ; ae = yes
  359.     mov    al,'^'
  360.     add    ah,40h            ; make control visible
  361. intch1a:mov    word ptr intqryc,ax    ; store escape char code
  362.     mov    ax,offset intqry    ; '?' get help message
  363.     call    puthlp            ; write help msg
  364.     mov    dx,offset intprm
  365.     mov    ah,prstr        ; show prompt
  366.     int    dos
  367. intch1b:mov    ah,0ch            ; clear Bios keyboard buffer and do
  368.     mov    al,coninq        ;  read keyboard, no echo
  369.     int    dos            ; get a char
  370.     or    al,al            ; scan code indicator?
  371.     jnz    intch1c            ; nz = no, ascii
  372.     mov    ah,coninq        ; read and discard scan code
  373.     int    dos
  374.     jmp    short intch1        ; try again
  375. intch1c:cmp    al,'?'            ; want to see menu again?
  376.     jne    intch2            ; ne = no
  377.     xor    al,al            ; prep for menu display
  378.     jmp    short intchar
  379.  
  380. intch2:    mov    flags.cxzflg,0        ; prevent Control-C carryover
  381.     cmp    al,' '            ; space?
  382.     je    tem            ; e = yes, ignore it
  383.     cmp    al,cr            ; check ^M (cr) against plain ascii M
  384.     je    tem            ; exit on cr
  385.     cmp    al,trans.escchr        ; is it the escape char?
  386.     jne    intch3            ; ne = no
  387.     mov    ah,al
  388.     call    outchr
  389.     jmp    tem            ; return, we are done here
  390. intch3:    push    es
  391.     push    ds
  392.     pop    es
  393.     mov    di,offset intclet    ; command letters
  394.     mov    cx,numlet        ; quantity of them
  395.     cmp    al,' '            ; control code?
  396.     jae    intch3a            ; ae = no
  397.     or    al,40H            ; convert control chars to printable
  398. intch3a:cmp    al,96            ; lower case?
  399.     jb    intch3b            ; b = no
  400.     and    al,not (20h)        ; move to upper case
  401. intch3b:cld
  402.     repne    scasb            ; find the matching letter
  403.     pop    es
  404.     jne    intch4            ; ne = not found, beep and get another
  405.     dec    di            ; back up to letter
  406.     sub    di,offset intclet    ; get letter number
  407.     shl    di,1            ; make it a word index
  408.     jmp    intcjmp[di]        ; dispatch to it
  409. intch4:    call    beep            ; say illegal character
  410.     jmp    intchar
  411.  
  412. intayt:    mov    ah,255            ; 'I' Telnet Are You There
  413.     call    outchr            ; send IAC (255) AYT (246)
  414.     mov    ah,246
  415.     call    outchr
  416.     jmp    tem
  417.  
  418. intchb:    call    sendbr            ; 'B' send a break
  419.     jmp    tem            ; And return
  420.  
  421. intchc:    clc                ; exit Connect mode
  422.     ret
  423.  
  424. intchf:    call    dumpscr            ; 'F' dump screen, use msy routine
  425.     jmp    tem            ; and return
  426.  
  427. intchh:    call    serhng            ; 'H' hangup phone
  428.     call    serrst            ; turn off port
  429.     jmp    tem
  430.  
  431. intchl:    call    sendbl            ; 'L' send a long break
  432.     jmp    tem
  433.  
  434. inttip:    mov    ah,255            ; 'I' Telnet Interrrupt Process
  435.     call    outchr            ; send IAC (255) IP (244)
  436.     mov    ah,244
  437.     call    outchr
  438.     jmp    tem
  439.  
  440. intchm:    cmp    flags.modflg,1        ; 'M' toggle mode line, enabled?
  441.     jne    intchma            ; ne = no, leave it alone
  442.     xor    targ.flgs,modoff    ; enabled, toggle its state
  443. intchma:jmp    tem            ; and reconnect
  444.  
  445. intchp:    call    fpush            ; 'P' push to DOS
  446.     mov    dx,offset sttmsg    ; say we have returned
  447.     mov    ah,prstr
  448.     int    dos
  449.     jmp    short intchsb        ; wait for a space
  450.  
  451. intchq:    and    targ.flgs,not capt    ; 'Q' suspend session logging
  452.     jmp    tem            ; and resume
  453.  
  454. intchr:    test    flags.capflg,logses    ; 'R' resume logging. Can we capture?
  455.     jz    intchr1            ; z = no
  456.     or    targ.flgs,capt        ; turn on session logging flag
  457. intchr1:jmp    tem            ; and resume
  458.  
  459. intchs:    call    stat0            ; 'S' status, call stat0
  460.     mov    dx,offset sttmsg
  461.     mov    ah,prstr
  462.     int    dos
  463. intchsa:call    iseof            ; is stdin at eof?
  464.     jnc    intchsb            ; nc = not eof, get more
  465.     jmp    tem            ; resume if EOF
  466. intchsb:mov    ah,coninq        ; console input, no echo
  467.     int    dos
  468.     cmp    al,' '            ; space?
  469.     jne    intchsa
  470.     jmp    tem
  471.  
  472. intchu:    mov    ah,trans.escchr
  473.     cmp    ah,' '            ; printable now?
  474.     jae    intchu1            ; ae = yes
  475.     mov    al,'^'
  476.     add    ah,40h            ; make control visible
  477. intchu1:mov    word ptr inthlpc,ax    ; store escape char code
  478.     mov    ax,offset inthlp    ; help message
  479.     call    puthlp            ; write help msg
  480.     mov    ah,0ch            ; clear Bios keyboard buffer and do
  481.     mov    al,coninq        ;  read keyboard, no echo
  482.     int    dos            ; get a char
  483.     or    al,al            ; scan code indicator?
  484.     jnz    intchu2            ; nz = no, ascii
  485.     mov    ah,coninq        ; read and discard scan code
  486.     int    dos
  487. intchu2:jmp    tem            ; try again
  488.  
  489. intchn:    xor    ah,ah            ; '0' send a null
  490.     call    outchr
  491.     jmp    tem
  492. TELNET  ENDP
  493. endif    ; no_terminal
  494. code    ends
  495.  
  496. code1    segment
  497.     assume cs:code1
  498. ; Reassure user    about connection to the host. Tell him what escape sequence
  499. ; to use to return
  500.  
  501. DOMSG    PROC    FAR
  502.     mov    ah,prstr
  503.     mov    dx,offset tmsg1
  504.     int    dos
  505.     call    escprt
  506.     mov    ah,prstr
  507.     mov    dx,offset tmsg3
  508.     int    dos
  509.     ret
  510. DOMSG    ENDP
  511.  
  512. ; print    the escape character in readable format.  
  513.  
  514. ESCPRT    PROC    NEAR
  515.     mov    dl,trans.escchr
  516.     cmp    dl,' '
  517.     jge    escpr2
  518.     push    dx
  519.     mov    ah,prstr
  520.     mov    dx,offset esctl
  521.     int    dos
  522.     pop    dx
  523.     add    dl,040H        ; Make it printable
  524. escpr2:    mov    ah,conout
  525.     int    dos
  526.     ret
  527. ESCPRT    ENDP
  528. code1    ends
  529.  
  530. code    segment
  531.     assume    cs:code 
  532.  
  533. ; Set parity for character in Register AL
  534.  
  535. dopar:    push    bx
  536.     mov    bx,portval
  537.     mov    bl,[bx].parflg        ; get parity flag byte
  538.     test    bl,PARHARDWARE        ; hardware?
  539.     jnz    parret            ; nz = yes
  540.     cmp    bl,parnon        ; No parity?
  541.     je    parret            ; Just return
  542.     and    al,07FH            ; Strip parity. Same as Space parity
  543.     cmp    bl,parspc        ; Space parity?
  544.     je    parret            ; e = yes, then we are done here
  545.     cmp    bl,parevn        ; Even parity?
  546.     jne    dopar0            ; ne = no
  547.     or    al,al
  548.     jpe    parret            ; pe = even parity now
  549.     xor    al,080H            ; Make it even parity
  550.     jmp    short parret
  551. dopar0:    cmp    bl,parmrk        ; Mark parity?
  552.     jne    dopar1            ; ne = no
  553.     or    al,080H            ; Turn on the parity bit
  554.     jmp    short parret
  555. dopar1:    cmp    bl,parodd        ; Odd parity?    
  556.     or    al,al
  557.     jpo    parret            ; Already odd, leave it
  558.     xor    al,080H            ; Make it odd parity
  559. parret:    pop    bx
  560.     ret
  561.  
  562. ; REPLAY filespec  through terminal emulator
  563. replay    proc    near
  564.     mov    bx,offset rdbuf        ; place for filename
  565.     mov    dx,offset rephlp    ; help
  566.     mov    repflg,0        ; clear the replay active flag
  567.     mov    ah,cmword        ; get filename
  568.     call    comnd
  569.     jc    replay2            ; c = failure
  570.     mov    ah,cmeol        ; get an EOL confirm
  571.     call    comnd
  572.     jc    replay2            ; c = failure
  573. ifndef    no_terminal
  574.     mov    ah,open2        ; open file
  575.     xor    al,al            ; open readonly
  576.     cmp    byte ptr dosnum+1,2    ; above DOS 2?
  577.     jna    replay1            ; na = no, so no shared access
  578.     mov    al,0+40h        ; open readonly, deny none
  579. replay1:mov    dx,offset rdbuf        ; asciiz filename
  580.     int    dos
  581.     jnc    replay3            ; nc = success
  582.     mov    ah,prstr
  583.     mov    dx,offset reperr    ; Cannot open that file
  584.     int    dos
  585.     clc
  586.     ret
  587. replay3:mov    diskio.handle,ax    ; file handle
  588.     mov    repflg,1        ; set replay flag
  589.     call    telnet            ; enter Connect mode
  590.     mov    bx,diskio.handle
  591.     mov    ah,close2        ; close the file
  592.     int    dos
  593. endif    ; no_terminal
  594.     mov    repflg,0        ; clear the flag
  595.     clc
  596. replay2:ret
  597. replay    endp
  598.  
  599. cptchr    proc    near            ; session capture routine, char in al
  600.     test    flags.capflg,logses    ; session logging active now?
  601.     jz    cptch1            ; z = no
  602.     push    di
  603.     push    es
  604.     mov    di,data1        ; seg of capbp and capbuf
  605.     mov    es,di
  606.     cld
  607.     mov    di,es:capbp        ; buffer pointer
  608.     stosb
  609.     inc    es:capbp
  610.     pop    es
  611.     pop    di
  612.     dec    caplft            ; decrement chars remaining
  613.     jg    cptch1            ; more room, forget this part
  614.     call    cptdmp            ; dump the info
  615. cptch1:    ret
  616. cptchr    endp
  617.  
  618. cptdmp    proc    near            ; empty the capture buffer
  619.     push    ax
  620.     push    bx
  621.     push    cx
  622.     push    dx
  623.     mov    bx,sloghnd        ; get file handle
  624.     or    bx,bx            ; is file open?
  625.     jle    cptdm1            ; le = no, skip it
  626.     mov    cx,cptsiz        ; original buffer size
  627.     sub    cx,caplft        ; minus number remaining
  628.     jl    cptdm2            ; means error
  629.     jcxz    cptdm1            ; z = nothing to do
  630.     push    ds
  631.     mov    dx,data1        ; seg of capbuf
  632.     mov    ds,dx
  633.     mov    dx,offset capbuf    ; the capture routine buffer
  634.     mov    ah,write2        ; write with filehandle
  635.     int    dos            ; write out the block
  636.     pop    ds
  637.     jc    cptdm2            ; carry set means error
  638.     cmp    ax,cx            ; wrote all?
  639.     jne    cptdm2            ; no, an error
  640.     push    es
  641.     mov    dx,data1        ; seg of capbuf and capbp
  642.     mov    es,dx
  643.     mov    es:capbp,offset capbuf
  644.     pop    es
  645.     mov    caplft,cptsiz        ; init buffer ptr & chrs left
  646.     clc
  647.     jmp    short cptdm1
  648. cptdm2:    and    targ.flgs,not capt    ; so please stop capturing
  649.     and    flags.capflg,not logses    ; deselect session logging flag bit
  650.     mov    dx,offset erms23    ; tell user the bad news
  651.     call    putmod            ; write on mode line
  652.     stc
  653. cptdm1:    pop    dx
  654.     pop    cx
  655.     pop    bx
  656.     pop    ax
  657.     ret
  658. cptdmp    endp
  659.  
  660. pktcpt    proc    near            ; packet log routine, char in al
  661.     test    flags.capflg,logpkt    ; logging packets now?
  662.     jz    pktcp1            ; z = no
  663.     push    di
  664.     push    es
  665.     mov    di,data1        ; seg of pktbuf and pktbp
  666.     mov    es,di
  667.     mov    di,es:pktbp        ; buffer pointer
  668.     cld
  669.     stosb                ; store char in buffer
  670.     inc    es:pktbp        ; move pointer to next free byte
  671.     pop    es
  672.     pop    di
  673.     dec    pktlft            ; decrement chars remaining
  674.     jg    pktcp1            ; g = more room, forget this part
  675.     call    pktdmp            ; dump the info
  676. pktcp1:    ret
  677. pktcpt    endp
  678.  
  679. pktdmp    proc    near            ; empty the capture buffer
  680.     push    ax
  681.     push    bx
  682.     push    cx
  683.     push    dx
  684.     mov    bx,ploghnd        ; get file handle
  685.     or    bx,bx            ; is file open?
  686.     jle    cptdm1            ; le = no, skip it
  687.     mov    cx,cptsiz        ; original buffer size
  688.     sub    cx,pktlft        ; minus number remaining
  689.     jl    pktdm2            ; l means error
  690.     jcxz    pktdm1            ; z = nothing to do
  691.     push    ds
  692.     mov    dx,data1        ; seg of pktbuf
  693.     mov    ds,dx
  694.     mov    dx,offset pktbuf    ; the capture routine buffer
  695.     mov    ah,write2        ; write with filehandle
  696.     int    dos            ; write out the block
  697.     pop    ds
  698.     jc    pktdm2            ; carry set means error
  699.     cmp    ax,cx            ; wrote all?
  700.     jne    pktdm2            ; ne = no, error
  701.     push    es
  702.     mov    dx,data1        ; seg of pktbuf
  703.     mov    es,dx
  704.     mov    es:pktbp,offset pktbuf
  705.     pop    es
  706.     mov    pktlft,cptsiz        ; init buffer ptr & chrs left
  707.     jmp    short pktdm1
  708. pktdm2:    and    flags.capflg,not logpkt    ; so please stop capturing
  709.     mov    dx,offset erms24    ; tell user the bad news
  710.     mov    ah,prstr
  711.     int    dos
  712.     call    clscp4            ; close the packet log
  713. pktdm1:    pop    dx
  714.     pop    cx
  715.     pop    bx
  716.     pop    ax
  717.     ret
  718. pktdmp    endp
  719.  
  720. ; CLOSE command
  721.  
  722. clscpt    proc    near
  723.     mov    ah,cmkey        ; get kind of file to close
  724.     mov    dx,offset clotab    ; close table
  725.     mov    bx,offset clohlp    ; help
  726.     call    comnd
  727.            jc    clscp2            ; c = failure
  728.     mov    temp,bx
  729.     mov    ah,cmeol
  730.     call    comnd
  731.            jc    clscp2            ; c = failure
  732.     mov    kstatus,kssuc        ; success status thus far
  733.     mov    bx,temp
  734.     cmp    bh,40h            ; READ-FILE or WRITE-FILE?
  735.     jne    clscp0            ; ne = no
  736.     mov    ax,bx
  737.     jmp    vfclose            ; close FILE, pass kind in AX
  738. clscp0:    cmp    bx,logpkt+logses+logtrn    ; close all?
  739.     je    clscpi            ; e = yes
  740.     cmp    bx,logpkt        ; just packet?
  741.     je    clscp4
  742.     cmp    bx,logses        ; just session?
  743.     je    clscp6
  744.     cmp    bx,logtrn        ; just session?
  745.     jne    clscp1            ; ne = no
  746.     jmp    clscp8
  747. clscp1:    mov    dx,offset erms22    ; say none active
  748.     mov    ah,prstr
  749.     int    dos
  750.     clc
  751.     ret
  752. clscp2:    mov    kstatus,ksgen        ; general cmd failure status
  753.     stc
  754.     ret
  755.                     ; CLSCPI called at Kermit exit
  756. CLSCPI:    mov    bx,portval
  757.     mov    [bx].flowc,0        ; set no flow control so no sending it
  758.     call    pntflsh            ; flush PRN buffer
  759.     call    clscp4            ; close packet log
  760.     call    clscp6            ; close session log
  761.     call    clscp8            ; close transaction log
  762.     mov    al,2            ; close WRITE FILE log
  763.     call    vfclose
  764.     clc                ; return success
  765.     ret
  766.  
  767. clscp4:    push    bx            ; PACKET LOG
  768.     mov    bx,ploghnd        ; packet log handle
  769.     or    bx,bx            ; is it open?
  770.     jle    clscp5            ; e = no
  771.     call    pktdmp            ; dump buffer
  772.     mov    ah,close2
  773.     int    dos
  774.     cmp    flags.takflg,0        ; ok to echo?
  775.     je    clscp5            ; e = no
  776.     mov    ah,prstr
  777.     mov    dx,offset clpktlog    ; tell what we are doing
  778.     int    dos
  779. clscp5:    mov    ploghnd,-1        ; say handle is invalid
  780.     pop    bx
  781.     and    flags.capflg,not logpkt    ; say this log is closed
  782.     ret
  783.  
  784. clscp6:    push    bx            ; SESSION LOG
  785.     mov    bx,sloghnd        ; session log handle
  786.     or    bx,bx            ; is it open?
  787.     jle    clscp7            ; e = no
  788.     call    cptdmp            ; dump buffer
  789.     mov    ah,close2
  790.     int    dos
  791.     cmp    flags.takflg,0        ; ok to echo?
  792.     je    clscp7            ; e = no
  793.     mov    ah,prstr
  794.     mov    dx,offset clseslog    ; tell what we are doing
  795.     int    dos
  796. clscp7:    mov    sloghnd,-1        ; say handle is invalid
  797.     pop    bx
  798.     and    flags.capflg,not logses    ; say this log is closed
  799.     ret
  800.  
  801. clscp8:    push    bx            ; TRANSACTION LOG
  802.     mov    bx,tloghnd        ; transaction log handle
  803.     or    bx,bx            ; is it open?
  804.     jle    clscp9            ; e = no
  805.     mov    ah,close2
  806.     int    dos
  807.     cmp    flags.takflg,0        ; ok to echo?
  808.     je    clscp9            ; e = no
  809.     mov    ah,prstr
  810.     mov    dx,offset cltrnlog    ; tell what we are doing
  811.     int    dos
  812. clscp9:    mov    tloghnd,-1        ; say handle is invalid
  813.     pop    bx
  814.     and    flags.capflg,not logtrn    ; say this log is closed
  815.     ret
  816. clscpt    endp
  817.  
  818. ; Print on PRN the char in register al. On success return with C bit clear.
  819. ; On failure do procedure pntchk and return its C bit (typically C set).
  820. ; Uses buffer dumpbuf (screen dump).
  821. pntchr    proc    near
  822.     push    es
  823.     push    ax
  824.     mov    ax,data1        ; segment of pntptr and prnbuf
  825.     mov    es,ax
  826.     cmp    es:pntptr,offset prnbuf+cptsiz ; buffer full yet?
  827.     pop    ax
  828.     pop    es
  829.     jb    pntchr1            ; b = no
  830.     call    pntflsh            ; flush buffer now
  831.     jnc    pntchr1            ; nc = success
  832.     ret                ; c = fail, discard char
  833. pntchr1:push    es
  834.     push    bx
  835.     mov    bx,data1        ; segment of pntptr and prnbuf
  836.     mov    es,bx
  837.     mov    bx,es:pntptr        ; pointer to next open slot
  838.     mov    es:[bx],al        ; store the character
  839.     inc    bx            ; update pointer
  840.     mov    es:pntptr,bx        ; save pointer
  841.     pop    bx
  842.     pop    es
  843.     clc                ; clear carry bit
  844.     ret
  845. pntchr    endp
  846.  
  847. ; Flush printer buffer. Return carry clear if success.
  848. ; On failure do procedure pntchk and return its C bit (typically C set).
  849. ; Uses buffer dumpbuf (screen dump).
  850. pntflsh    proc    near
  851.     push    es
  852.     push    ax
  853.     mov    ax,data1        ; segment of pntptr and prnbuf
  854.     mov    es,ax
  855.     cmp    es:pntptr,offset prnbuf    ; any text in buffer?
  856.     pop    ax
  857.     pop    es
  858.     ja    pntfls1            ; a = yes
  859.     clc
  860.     ret                ; else nothing to do
  861. pntfls1:cmp    prnhand,0        ; is printer handle valid?
  862.     jg    pntfls2            ; g = yes
  863.     push    es
  864.     push    ax
  865.     mov    ax,data1        ; segment of pntptr and prnbuf
  866.     mov    es,ax
  867.     mov    es:pntptr,offset prnbuf
  868.     pop    ax
  869.     pop    es
  870.     clc                ; omit printing, quietly
  871.     ret
  872. pntfls2:push    ax
  873.     push    bx
  874.     push    cx
  875.     push    dx
  876.     push    si
  877.     push    di
  878.     push    es
  879.     mov    ah,flowoff        ; get flow control char
  880.     or    ah,ah            ; flow control active?
  881.     jz    pntfls3            ; z = no, not using xoff
  882.     call    outchr            ; output xoff (ah), no echo
  883. pntfls3:mov    bx,prnhand        ; file handle for DOS printer PRN
  884.     push    DS            ; about to change DS
  885.     mov    dx,data1        ; segment of prnbuf
  886.     mov    ds,dx            ; set DS
  887.     mov    dx,offset prnbuf    ; start of buffer
  888.     mov    cx,ds:pntptr
  889.     sub    cx,dx            ; cx = current byte count
  890. pntfls4:push    cx
  891.     push    dx
  892.     mov    cx,1
  893.     mov    ah,write2
  894.     int    dos            ; write buffer to printer
  895.     pop    dx
  896.     pop    cx
  897.     jc    pntfls5            ; c = call failed
  898.     cmp    ax,1            ; did we write it?
  899.     jne    pntfls5            ; ne = no, dos critical error
  900.     inc    dx            ; point to next char
  901.     loop    pntfls4
  902.     mov    ds:pntptr,offset prnbuf    ; reset buffer pointer
  903.     pop    DS            ; restore DS
  904.     clc                ; declare success
  905.     jmp    pntfls11
  906. pntfls5:mov    si,dx            ; address of next char to be printed
  907.     mov    di,offset prnbuf    ; start of buffer
  908.     sub    dx,di            ; dx now = number successful prints
  909.     mov    cx,ds:pntptr
  910.     sub    cx,si            ; count of chars to be printed
  911.     jle    pntfls6
  912.     mov    ax,ds
  913.     mov    es,ax
  914.     cld
  915.     rep    movsb            ; copy unwritten to start of buffer
  916. pntfls6:sub    ds:pntptr,dx        ; move back printer pointer by ok's
  917.     pop    DS            ; restore DS
  918.  
  919. pntfls7:mov    dx,offset erms26    ; printer not ready, get user action
  920.     call    putmod            ; write new mode line
  921.     call    beep            ; make a noise
  922.     mov    ah,0ch            ; clear DOS typeahead buffer
  923.     mov    al,1            ; read from DOS buffer
  924.     int    dos
  925.     or    al,al            ; Special key?
  926.     jnz    pntfls8            ; nz = no, consume
  927.     mov    al,1            ; consume scan code
  928.     int    dos
  929.     jmp    short pntfls7        ; try again
  930.  
  931. pntfls8:and    al,not 20h        ; lower to upper case quicky
  932.     cmp    al,'R'            ; Retry?
  933.     jne    pntfls8a        ; ne = no
  934.     call    trnmod            ; toggle mode line
  935.     call    trnmod            ; back to same state as before
  936.     jmp    pntfls3            ; go retry
  937. pntfls8a:cmp    al,'D'            ; Discard printing?
  938.     jne    pntfls7            ; ne = no, try again
  939.     mov    bx,prnhand
  940.     cmp    bx,4            ; stdin/stdout/stderr/stdaux/stdprn?
  941.     jbe    pntfls9            ; be = yes, always available
  942.     mov    ah,close2        ; close this file
  943.     int    dos
  944. pntfls9:mov    bx,offset prnname    ; name of printer file
  945.     mov    word ptr [bx],'UN'    ; set to NUL<0>
  946.     mov    word ptr [bx+2],'L'+0
  947.     push    es
  948.     mov    ax,data1        ; seg for pntptr
  949.     mov    es,ax
  950.     mov    es:pntptr,offset prnbuf    ; reset pointer
  951.     pop    es
  952.     mov    prnhand,-1        ; declare handle invalid
  953. ifndef    no_terminal
  954.     mov    anspflg,0        ; mszibm/msyibm print status flag off
  955. endif    ; no_terminal
  956. pntfls10:call    trnmod            ; toggle mode line
  957.     call    trnmod            ; back to same state as before
  958.     stc                ; declare failure
  959. pntfls11:pushf
  960.     mov    ah,flowon
  961.     or    ah,ah            ; flow control active?
  962.     jz    pntfls12        ; z = no, not using xon
  963.     call    outchr            ; output xon (al), no echo
  964. pntfls12:popf
  965.     pop    es
  966.     pop    di
  967.     pop    si
  968.     pop    dx
  969.     pop    cx
  970.     pop    bx
  971.     pop    ax
  972.     ret                ; nc = success
  973. pntflsh    endp
  974.  
  975. ; Check for PRN (DOS's printer) being ready. If ready, return with C clear
  976. ; Otherwise, write Not Ready msg on mode line and return with C bit set.
  977. ; N.B. DOS Critical Error will occur here if PRN is not ready.  [jrd]
  978. pntchk    proc    near
  979.     push    dx
  980.     push    cx
  981.     push    ax
  982.     mov    cx,10            ; ten retries before declaring error
  983.     cmp    prnhand,0        ; printer handle valid?
  984.     jle    pntchk2            ; le = no, invalid
  985. pntchk0:push    bx
  986.     mov    bx,prnhand        ; file handle
  987.     mov    ah,ioctl        ; get printer status, via DOS
  988.     mov    al,7            ; status for output
  989.     int    dos
  990.     pop    bx
  991.     jc    pntchk1            ; c = call failed
  992.     cmp    al,0ffh            ; code for Ready?
  993.     je    pntchk3            ; e = yes, assume printer is ready
  994. pntchk1:push    cx            ; save counter, just in case
  995.     mov    ax,100            ; wait 100 millisec
  996.     call    pcwait
  997.     pop    cx
  998.     loop    pntchk0            ; and try a few more times
  999.                     ; get here when printer is not ready
  1000. pntchk2:pop    ax
  1001.     pop    cx
  1002.     pop    dx
  1003.     stc                ; say printer not ready
  1004.     ret
  1005. pntchk3:pop    ax
  1006.     pop    cx
  1007.     pop    dx
  1008.     clc                ; say printer is ready
  1009.     ret
  1010. pntchk    endp
  1011.  
  1012.  
  1013. prnopen    proc    near
  1014.     push    ax
  1015.     push    bx
  1016.     push    cx
  1017.     push    dx
  1018.     mov    prnhand,4        ; preset default handle
  1019.     mov    dx,offset prnname    ; name of disk file, from msssho
  1020.     mov    ax,dx            ; where isfile wants name ptr
  1021.     call    isfile            ; what kind of file is this?
  1022.     jc    prnop3            ; c = no such file, create it
  1023.     test    byte ptr filtst.dta+21,1fh ; file attributes, ok to write?
  1024.     jnz    prnop2            ; nz = no
  1025.     mov    al,1            ; writing
  1026.     mov    ah,open2        ; open existing file
  1027.     int    dos
  1028.     jc    prnop2            ; c = failure
  1029.     mov    prnhand,ax        ; save file handle
  1030.     mov    bx,ax            ; handle for DOS
  1031.     mov    ah,ioctl
  1032.     mov    al,0            ; get info
  1033.     int    dos
  1034.     or    dl,20h            ; turn on binary mode
  1035.     xor    dh,dh
  1036.     mov    ah,ioctl
  1037.     mov    al,1            ; set info
  1038.     int    dos
  1039.     mov    cx,0ffffh        ; setup file pointer
  1040.     mov    dx,-1            ; and offset
  1041.     mov    al,2            ; move to eof minus one byte
  1042.     mov    ah,lseek        ; seek the end
  1043.     int    dos
  1044.     pop    dx
  1045.     pop    cx
  1046.     pop    bx
  1047.     pop    ax
  1048.     clc
  1049.     ret
  1050. prnop2:    pop    dx
  1051.     pop    cx
  1052.     pop    bx
  1053.     pop    ax
  1054.     stc
  1055.     ret
  1056. prnop3:    test    filtst.fstat,80h    ; access problem?
  1057.     jnz    prnop2            ; nz = yes
  1058.     mov    ah,creat2        ; file did not exist
  1059.     mov    cx,20h            ; attributes, archive bit
  1060.     int    dos
  1061.     mov    prnhand,ax        ; save file handle
  1062.     pop    dx
  1063.     pop    cx
  1064.     pop    bx
  1065.     pop    ax
  1066.     ret                ; may have carry set
  1067. prnopen    endp
  1068.  
  1069. ; worker: copy line from si to di, first removing trailing spaces, second
  1070. ; parsing out curly braced strings, then third converting \{b##} in strings
  1071. ; to binary numbers. Returns carry set if error; else carry clear, with byte
  1072. ; count in cx. Braces are optional but must occur in pairs.
  1073. ; Items which cannot be converted to legal numbers are copied verbatium
  1074. ; to the output string (ex: \{c}  is copied as \{c}  but \{x0d} is hex 0dh).
  1075. ; '\\' is written as '\'
  1076. ; Requires byte count in CX to handle embedded \0 bytes.
  1077. cnvlin    proc    near
  1078.     push    ax
  1079.     push    si            ; source ptr
  1080.     push    di            ; destination ptr
  1081.     push    bp            ; end of string marker
  1082.     push    temp            ; output length counter
  1083.     push    es            ; end of save regs
  1084.     push    ds            ; move ds into es
  1085.     pop    es            ; use data segment for es:di
  1086.     mov    temp,di            ; initial output position
  1087.     call    cnvstr            ; parse curly braces
  1088.     xor    dx,dx            ; initialize returned byte count
  1089.     cld
  1090.     mov    bp,si
  1091.     add    bp,cx            ; point to last byte + 1 of string
  1092. cnvln1:    cmp    si,bp            ; at end of string?
  1093.     jae    cnvlnx            ; ae = yes, exit
  1094.     mov    al,[si]            ; look at starting byte
  1095.     cmp    al,'\'            ; numeric intro?
  1096.     jne    cnvln3            ; ne = no
  1097.     mov    atoi_cnt,cx        ; length for atoi
  1098.     mov    atoibyte,1        ; convert only one character
  1099.     call    atoi            ; deal with \numbers
  1100.     jnc    cnvln4            ; nc = success, converted number
  1101. cnvln3:    lodsb                ; reread from string literally
  1102.     xor    ah,ah            ; only one byte of output
  1103. cnvln4:    cld
  1104.     stosb                ; save the char
  1105.     or    ah,ah            ; is returned number > 255?
  1106.     jz    cnvln1            ; z = no, do more chars
  1107. cnvln5:    stosb                ; save high order byte next
  1108.     jmp    short cnvln1        ; do more chars
  1109. cnvlnx:    mov    byte ptr [di],0        ; plant terminator
  1110.     clc                ; clear c bit, success
  1111.     sub    di,temp            ; current - inital output position
  1112.     mov    cx,di            ; returned byte count to CX
  1113.     pop    es            ; restore regs
  1114.     pop    temp
  1115.     pop    bp
  1116.     pop    di            ; destination ptr
  1117.     pop    si            ; source ptr
  1118.     pop    ax
  1119.     ret
  1120. cnvlin    endp
  1121.  
  1122. ; Convert string by removing surrounding curly brace delimiter pair. 
  1123. ; Converts text in place.
  1124. ; Enter with source ptr in si.
  1125. ; Preserves all registers, uses byte tmp. 9 Oct 1987 [jrd]
  1126. cnvstr    proc    near
  1127.     push    ax
  1128.     push    bx
  1129.     push    dx
  1130.     push    si            ; save start of source string
  1131.     push    di
  1132.     push    es
  1133.     xor    bx,bx             ; output count, assume none
  1134.     mov    dx,si            ; source address
  1135.     jcxz    cnvst4            ; z = nothing there
  1136.     push    ds
  1137.     pop    es            ; set es to data segment
  1138.     cld
  1139.     mov    di,si            ; set destination address to source
  1140.                     ; 2. Parse off curly brace delimiters
  1141.     mov    bx,cx            ; assume no changes to byte count
  1142.     cmp    byte ptr [si],braceop    ; opening brace?
  1143.     jne    cnvst4            ; ne = no, ignore brace-matching code
  1144.     xor    bx,bx            ; count up output bytes
  1145.     inc    si            ; skip opening brace
  1146.     mov    dl,braceop        ; opening brace (we count them up)
  1147.     mov    dh,bracecl        ; closing brace (we count them down)
  1148.     mov    tmp,1            ; we are at brace level 1
  1149. cnvst1:    cld                ; search forward
  1150.     lodsb                ; read a string char
  1151.     stosb                ; store char (skips opening brace)
  1152.     dec    cx
  1153.     inc    bx            ; count bytes read and written
  1154.     or    cx,cx            ; at end of string?
  1155.     jle    cnvst4            ; le = yes, we are done
  1156.     cmp    al,dl            ; an opening brace?
  1157.     jne    cnvst2            ; ne = no
  1158.     inc    tmp            ; yes, increment brace level
  1159.     jmp    short cnvst1        ;  and continue scanning
  1160.  
  1161. cnvst2:    cmp    al,dh            ; closing brace?
  1162.     jne    cnvst1            ; ne = no, continue scanning
  1163.     dec    tmp            ; yes, decrement brace level
  1164.     or    cx,cx            ; have we just read the last char?
  1165.     jnz    cnvst3            ; nz = no, continue scanning
  1166.     mov    tmp,0            ; yes, this is the closing brace
  1167. cnvst3:    cmp    tmp,0            ; at level 0?
  1168.     jne    cnvst1            ; ne = no, #opening > #closing braces
  1169.     dec    bx            ; don't count terminating brace
  1170.     mov    byte ptr [di-1],0    ; plant terminator on closing brace
  1171.  
  1172. cnvst4:    pop    es            ; recover original registers
  1173.     pop    di
  1174.     pop    si
  1175.     pop    dx
  1176.     mov    cx,bx            ; return count in cx
  1177.     pop    bx
  1178.     pop    ax
  1179.     ret
  1180. cnvstr    endp
  1181.  
  1182. ; OPEN { READ | WRITE | APPEND } filespec
  1183. vfopen    proc    near
  1184.     mov    ah,cmkey        ; get READ/WRITE/APPEND keyword
  1185.     mov    dx,offset opntab    ; keyword table
  1186.     xor    bx,bx            ; help
  1187.     call    comnd
  1188.     jc    vfopen1            ; c = failed
  1189.     mov    temp,bx
  1190.     mov    ah,cmword        ; read filespec
  1191.     mov    bx,offset rdbuf        ; buffer for filename
  1192.     mov    dx,offset vfophlp    ; help
  1193.     call    comnd
  1194.     jc    vfopen1            ; c = failed
  1195.     mov    ah,cmeol        ; get end of line confirmation
  1196.     call    comnd
  1197.     jnc    vfopen2
  1198.     mov    kstatus,ksgen        ; general cmd failure status
  1199. vfopen1:ret                ; error return, carry set
  1200.  
  1201. vfopen2:mov    kstatus,kssuc        ; assume success status
  1202.     mov    dx,offset rdbuf        ; filename, asiiz
  1203.     mov    bx,temp            ; kind of open
  1204.     cmp    bx,1            ; open for reading?
  1205.     jne    vfopen4            ; ne = no
  1206.                     ; OPEN READ
  1207.     cmp    vfrhandle,0        ; is it open now?
  1208.     jge    vfopen8            ; ge = yes, complain
  1209.     mov    ah,open2        ; file open
  1210.     xor    al,al            ; 0 = open readonly
  1211.     cmp    dosnum,300h        ; at or above DOS 3?
  1212.     jb    vfopen3            ; b = no, so no shared access
  1213.     or    al,40h            ; open readonly, deny none
  1214. vfopen3:int    dos
  1215.     jc    vfopen9            ; c = failed to open the file
  1216.     mov    vfrhandle,ax        ; save file handle
  1217.     clc
  1218.     ret
  1219.                     ; OPEN WRITE or APPEND
  1220. vfopen4:cmp    vfwhandle,0        ; is it open now?
  1221.     jge    vfopen8            ; ge = yes, complain
  1222.     mov    ax,dx            ; filename for isfile
  1223.     call    isfile        ; check for read-only/system/vol-label/dir
  1224.     jc    vfopen7            ; c = file does not exist
  1225.     test    byte ptr filtst.dta+21,1fh    ; the no-no file attributes
  1226.     jnz    vfopen9            ; nz = do not write over one of these
  1227. vfopen5:test    filtst.fstat,80h    ; access problem?
  1228.     jnz    vfopen9            ; nz = yes, quit here
  1229.     test    byte ptr filtst.dta+21,1bh    ; r/o, hidden, volume label?
  1230.     jnz    vfopen9            ; we won't touch these
  1231.     mov    ah,open2           ; open existing file (usually a device)
  1232.     mov    al,1+1            ; open for writing
  1233.     int    dos
  1234.     jc    vfopen9            ; carry set means can't open
  1235.     mov    vfwhandle,ax        ; remember file handle
  1236.     mov    bx,ax            ; file handle for lseek
  1237.     xor    cx,cx
  1238.     xor    dx,dx            ; cx:dx = displacment
  1239.     xor    al,al            ; 0 means from start of file
  1240.     cmp    temp,2            ; WRITE? means from start of file
  1241.     je    vfopen6            ; e = yes, else APPEND
  1242.     mov    al,2            ; move to eof
  1243. vfopen6:mov    ah,lseek        ; seek the place
  1244.     int    dos
  1245.     clc
  1246.     ret
  1247.  
  1248. vfopen7:cmp    temp,1            ; READ?
  1249.     je    vfopen9            ; e = yes, should not be here
  1250.     mov    ah,creat2        ; create file
  1251.     xor    cx,cx            ; 0 = attributes bits
  1252.     int    dos
  1253.     jc    vfopen9            ; c = failed
  1254.     mov    vfwhandle,ax        ; save file handle
  1255.     clc                ; carry clear for success
  1256.     ret
  1257.  
  1258. vfopen8:mov    dx,offset vfoptwice    ; trying to reopen a file
  1259.     mov    ah,prstr
  1260.     int    dos
  1261.     mov    kstatus,kstake        ; Take file failure status
  1262.     stc
  1263.     ret
  1264. vfopen9:mov    dx,offset vfopbad    ; can't open, complain
  1265.     mov    ah,prstr
  1266.     int    dos
  1267.     mov    dx,offset rdbuf        ; filename
  1268.     call    prtasz
  1269.     mov    kstatus,kstake        ; Take file failure status
  1270.     stc
  1271.     ret
  1272. vfopen    endp
  1273.  
  1274. ; CLOSE {READ-FILE | WRITE-FILE}
  1275. vfclose    proc    near
  1276.     mov    tmp,al            ; remember kind (1=READ, 2=WRITE)
  1277.     mov    bx,vfrhandle        ; READ FILE handle
  1278.     cmp    al,1            ; READ-FILE?
  1279.     je    vfclos1            ; e = yes
  1280.     cmp    al,2            ; WRITE-FILE?
  1281.     jne    vfclos3            ; ne = no, a mistake
  1282.     mov    bx,vfwhandle        ; write handle
  1283.     or    bx,bx
  1284.     jl    vfclos3            ; l = invalid handle
  1285.     mov    dx,offset rdbuf
  1286.     xor    cx,cx            ; zero length
  1287.     push    bx            ; save handle
  1288.     mov    ah,write2        ; set file high water mark
  1289.     int    dos
  1290.     pop    bx
  1291. vfclos1:or    bx,bx
  1292.     jl    vfclos3            ; l = invalid handle
  1293.     mov    kstatus,kssuc        ; success status thus far
  1294.     mov    ah,close2        ; close file
  1295.     int    dos
  1296.     jc    vfclos4            ; c = error
  1297.     cmp    tmp,1            ; READ?
  1298.     jne    vfclos2            ; ne = no, WRITE
  1299.     mov    vfrhandle,-1        ; declare READ handle to be invalid
  1300.     clc
  1301.     ret
  1302. vfclos2:mov    vfwhandle,-1        ; declare WRITE handle to be invalid
  1303.     clc
  1304.     ret
  1305. vfclos4:mov    ah,prstr
  1306.     mov    dx,offset vfclbad    ; complain
  1307.     int    dos
  1308. vfclos3:mov    kstatus,ksgen        ; general cmd failure status
  1309.     stc
  1310.     ret
  1311. vfclose    endp
  1312.  
  1313. ; READ-FILE variable name
  1314. vfread    proc    near
  1315.     mov    comand.cmper,1        ; do not react to '\%' in macro name
  1316.     mov    comand.cmarray,1    ; allow sub in [..] of \&<char> arrays
  1317.     mov    ah,cmword
  1318.     mov    bx,offset rdbuf+2    ; buffer for macro name
  1319.     mov    word ptr rdbuf,0
  1320.     mov    dx,offset vfrdmsg
  1321.     call    comnd            ; get macro name
  1322.     jnc    vfread1            ; nc = success
  1323.     mov    kstatus,ksgen        ; general cmd failure status
  1324.     ret                ; failure
  1325. vfread1:or    ax,ax            ; null entry?
  1326.     jnz    vfread2            ; nz = no
  1327.     mov    dx,offset vfrdbad    ; more parameters needed
  1328.     mov    ah,prstr
  1329.     int    dos
  1330.     mov    kstatus,ksgen        ; general cmd failure status
  1331.     stc
  1332.     ret
  1333.  
  1334. vfread2:push    ax
  1335.     mov    ah,cmeol        ; get command confirmation
  1336.     call    comnd
  1337.     pop    ax            ; ax is variable length
  1338.     jc    vfreadx            ; c = failed
  1339.     mov    cx,cmdblen        ; length of rdbuf
  1340.     sub    cx,ax            ; minus macro name
  1341.     dec    cx            ; minus space separator
  1342.     mov    temp,0            ; leading whitespace and comment flgs
  1343.     mov    di,offset rdbuf+2    ; destination buffer
  1344.     add    di,ax            ; plus variable name
  1345.     mov    byte ptr [di],' '    ; space separator
  1346.     inc    di            ; put definition here
  1347.     mov    bx,vfrhandle        ; READ FILE handle
  1348.     or    bx,bx            ; check for valid handle
  1349.     jge    vfread3            ; ge = valid
  1350.     mov    ah,prstr
  1351.     mov    dx,offset vfnofile    ; say no file
  1352.     int    dos
  1353. vfreadx:mov    kstatus,ksgen        ; general cmd failure status
  1354.     stc                ; failure return
  1355.     ret
  1356. vfread3:push    cx            ; read from file
  1357.     mov    kstatus,kssuc        ; assume success status
  1358.     mov    cx,1            ; read 1 char
  1359.     mov    dx,di            ; place here
  1360.     mov    byte ptr [di],0        ; insert terminator
  1361.     mov    ah,readf2
  1362.     int    dos
  1363.     pop    cx
  1364.     jc    vfreadx            ; c = read failure
  1365.     or    ax,ax            ; count of bytes read
  1366.     jz    vfread9            ; z means end of file
  1367.     mov    al,[di]            ; get the character
  1368.     cmp    flags.takflg,0        ; echo Take files?
  1369.     je    vfread3a        ; e = no
  1370.     push    ax
  1371.     mov    ah,conout
  1372.     mov    dl,al            ; echo character
  1373.     int    dos
  1374.     pop    ax
  1375. vfread3a:cmp    al,CR            ; CR?
  1376.     je    vfread7            ; e = yes, ignore it
  1377.     cmp    al,LF            ; LF?
  1378.     je    vfread8            ; e = yes, exit
  1379.     cmp    byte ptr temp,0        ; seen non-spacing char yet?
  1380.     jne    vfread6            ; ne = yes
  1381.     cmp    al,' '            ; is this a space?
  1382.     je    vfread7            ; e = yes, skip it
  1383.     cmp    al,TAB            ; or a tab?
  1384.     je    vfread7            ; e = yes, skip it
  1385.     mov    byte ptr temp,1        ; say have seen non-spacing char
  1386.  
  1387. vfread6:inc    di            ; next storage cell
  1388. vfread7:loop    vfread3            ; loop til end of line
  1389.  
  1390. vfread8:cmp    byte ptr [di-1],'-'    ; last printable is line-continuation?
  1391.     jne    vfread10        ; ne = no, use this line as-is
  1392.     dec    di            ; point to hyphen
  1393.     mov    cx,offset rdbuf+cmdblen    ; end of rdbuf
  1394.     sub    cx,di            ; minus where we are now
  1395.     jle    vfread10        ; le = no space remaining
  1396.     mov    temp,0            ; leading whitespace and comment flgs
  1397.     jmp    vfread3            ; read another line
  1398.  
  1399. vfread9:mov    kstatus,ksgen        ; EOF, general command failure status
  1400.  
  1401. vfread10:cmp    flags.takflg,0        ; echo Take files?
  1402.     je    vfread11        ; e = no
  1403.     mov    ah,prstr
  1404.     mov    dx,offset crlf        ; for last displayed line
  1405.     int    dos
  1406.  
  1407. vfread11:mov    byte ptr [di],0        ; insert final terminator
  1408.     mov    dx,offset rdbuf+2    ; start counting from here
  1409.     push    kstatus            ; preserve status from above work
  1410.     call    strlen            ; cx becomes length
  1411.     call    dodecom            ; create the variable
  1412.     pop    kstatus            ; recover reading status
  1413.     jc    vfread12        ; c = failure
  1414.     ret
  1415. vfread12:mov    kstatus,ksgen        ; general command failure status
  1416.     mov    ah,prstr
  1417.     mov    dx,offset vfrbad    ; say error while reading
  1418.     int    dos
  1419.     stc
  1420.     ret
  1421. vfread    endp
  1422.  
  1423. ; WRITELN {FILE or log} text   (adds trailing CR/LF)
  1424. writeln    proc    near
  1425.     mov    tmp,1            ; flag for trailing CR/LF
  1426.     jmp    short write0        ; common code
  1427. writeln    endp
  1428.  
  1429. ; WRITE {FILE or log} text
  1430. Write    proc    near
  1431.     mov    tmp,0            ; flag for no trailing CR/LF
  1432. write0:    mov    ah,cmkey        ; get kind of log file
  1433.     mov    dx,offset writetab    ; table of possibilities
  1434.     xor    bx,bx            ; help, when we get there
  1435.     call    comnd
  1436.     jnc    write1            ; nc = success
  1437.     mov    kstatus,ksgen        ; general cmd failure status
  1438.     ret
  1439. write1:    mov    temp,bx            ; save log file kind
  1440.     mov    comand.cmdonum,1    ; \number conversion allowed
  1441.     mov    comand.cmblen,cmdblen    ; use full length
  1442.     mov    ah,cmline        ; get string to be written
  1443.     mov    bx,offset rdbuf        ; where to put text
  1444.     mov    dx,offset msgtxt    ; help
  1445.     call    comnd
  1446.     jnc    wpkt0
  1447.     mov    kstatus,ksgen        ; general cmd failure status
  1448.     ret                ; c = failure
  1449. wpkt0:    mov    kstatus,kssuc        ; success status thus far
  1450.     mov    si,offset rdbuf        ; start of text in buffer
  1451.     cmp    tmp,0            ; add trailing CR/LF?
  1452.     je    wpkt0a            ; e = no
  1453.     mov    bx,ax            ; current length
  1454.     mov    word ptr rdbuf[bx],LF*256+CR ; add CR LF
  1455.     mov    word ptr rdbuf[bx+2],0    ; and terminator
  1456.     add    ax,2
  1457. wpkt0a:    mov    bx,temp            ; log file kind
  1458.     mov    cx,ax            ; length 
  1459.     jcxz    wpkt2            ; z = no chars to write
  1460.     cmp    bx,logpkt        ; Packet log?
  1461.     jne    wses1            ; ne = no
  1462. wpkt1:    lodsb                ; get byte to al
  1463.     call    pktcpt            ; write to packet log
  1464.     loop    wpkt1
  1465. wpkt2:     clc                ; say success
  1466.     ret
  1467.  
  1468. wses1:    cmp    bx,logses        ; Session log?
  1469.     jne    wtrn1            ; ne = no
  1470. wses2:    lodsb                ; get byte to al
  1471.     call    cptchr            ; write to log
  1472.     loop    wses2            ; do cx chars
  1473.     clc                ; say success
  1474.     ret
  1475.  
  1476. wtrn1:    cmp    bx,logtrn        ; Transaction log?
  1477.     jne    wtrn2            ; ne = no
  1478.     mov    bx,tloghnd        ; file handle
  1479.     jmp    short wtrn4
  1480.  
  1481. wtrn2:    cmp    bx,80h            ; WRITE SCREEN?
  1482.     jne    wtrn3            ; ne = no
  1483.     xor    bx,bx            ; handle is stdout
  1484.     jmp    short wtrn4
  1485.  
  1486. wtrn3:    cmp    bx,4002h        ; WRITE FILE?
  1487.     jne    wtrn5            ; ne = no, forget this
  1488.     mov    bx,vfwhandle        ; handle
  1489.  
  1490. wtrn4:    or    bx,bx            ; is handle valid?
  1491.     jl    wtrn5            ; l = no
  1492.     mov    dx,offset rdbuf
  1493.     mov    ah,write2        ; write to file handle in bx
  1494.     int    dos
  1495.     jc    wtrn4a            ; c = failure
  1496.     clc
  1497.     ret
  1498. wtrn4a:    mov    ah,prstr
  1499.     mov    dx,offset vfwbad    ; say error while writing
  1500.     int    dos
  1501.     mov    kstatus,ksgen
  1502.     stc
  1503.     ret
  1504. wtrn5:    mov    kstatus,ksgen        ; general cmd failure status
  1505.     mov    ah,prstr
  1506.     mov    dx,offset vfnofile    ; say no file
  1507.     int    dos
  1508.     stc
  1509.     ret
  1510. write    endp
  1511. code    ends
  1512.  
  1513. code1    segment
  1514.     assume    cs:code1
  1515.  
  1516. ; Convert ascii strings of the form "\{bnnn}" to a binary dword in dx:ax.
  1517. ; The braces are optional but must occur in pairs. Numeric base indicator "b"
  1518. ; is O or o or X or x or D or d or missing, for octal, hex, or decimal (def).
  1519. ; Enter with si pointing at "\".
  1520. ; Returns binary value in ax with carry clear and si to right of "}" or at
  1521. ; terminating non-numeric char if successful; otherwise, a failure,
  1522. ; return carry set with si = entry value and first read char in al.
  1523. ; Public byte  atoibyte  is used to control conversion of one character, if
  1524. ;  non-zero, or as many as can be parsed if zero. This routine always clears
  1525. ;  atoibyte upon exiting.
  1526. ; atoi_err byte values, carry bit state, meaning
  1527. ; 0 nc    successfully converted, can accept more data
  1528. ; 1 nc    successfully converted and string is terminated
  1529. ; 2 nc    successfully converted with break char pointed to by SI
  1530. ; 4 c    insufficient bytes to resolve
  1531. ; 8 c    syntax error in expression, cannot convert
  1532. atoi    proc    far
  1533.     push    temp
  1534.     push    temp1
  1535.     push    cx            ; save working reg
  1536.     push    si            ; save entry si
  1537.     push    bx
  1538.     push    ax            ; save read char
  1539.     xor    ax,ax
  1540.     mov    temp,ax
  1541.     mov    temp1,ax
  1542.     mov    atoi_err,al        ; assume success and can read more
  1543.     xor    bx,bx            ; assume no opening brace
  1544.     cld
  1545.     mov    cx,atoi_cnt        ; bytes available to examine
  1546.     or    cx,cx
  1547.     jnz    atoi1
  1548.     mov    atoi_cnt,16        ; safety
  1549. atoi1:    call    atoi_read        ; read a byte or fail
  1550.     jnc    atoi1a
  1551.     jmp    atoix            ; fail
  1552.  
  1553. atoi1a:    cmp    al,' '            ; leading space?
  1554.     je    atoi1            ; e = yes, read again, for do_math
  1555.     cmp    al,'\'            ; number introducer?
  1556.     jne    atoi1b            ; ne = no
  1557.     call    atoi_read        ; get next byte
  1558.     jnc    atoi1b
  1559.     jmp    atoix
  1560.  
  1561. atoi1b:    cmp    al,braceop        ; opening brace?
  1562.     jne    atoi1c            ; ne = no
  1563.     mov    bl,bracecl        ; remember a closing brace is needed
  1564.     call    atoi_read        ; get next byte
  1565.     jnc    atoi1c
  1566.     jmp    atoix
  1567.  
  1568. atoi1c:    call    isnumbase        ; number base introducer?
  1569.     jc    atoi1d            ; c = no
  1570.     call    atoi_read        ; get next byte
  1571.     jnc    atoi2            ; nc = got a byte
  1572.     jmp    atoix
  1573. atoi1d:    mov    nbase,10        ; assume base 10
  1574.     mov    cx,3            ; and three digits per char
  1575.  
  1576. atoi2:    call    cnvdig            ; do we have a digit now?
  1577.     jnc    atoi2a            ; nc = yes
  1578.     mov    atoi_err,8        ; syntax error, digit required
  1579.     jmp    atoix
  1580. atoi2a:    dec    si            ; back over it for read below
  1581.     inc    atoi_cnt
  1582.     mov    ax,cx            ; number base byte count
  1583.     mov    cx,atoi_cnt        ; available bytes
  1584.     cmp    atoibyte,0        ; read as many bytes as possible?
  1585.     je    atoi3            ; e = yes
  1586.     cmp    ax,cx            ; number base wants more than we have?
  1587.     ja    atoi3            ; a = yes, use available bytes
  1588.     mov    cx,ax            ; limit loop to num base requirements
  1589.     mov    atoi_err,1        ; presume successful and terminated
  1590.  
  1591. atoi3:    call    atoi_read        ; get a byte
  1592.     jc    atoix            ; c = failed
  1593.     call    cnvdig            ; convert ascii to binary digit
  1594.     jc    atoi4            ; c = cannot convert, SI-1 is break
  1595.     xor    ah,ah            ; clear high order value
  1596.     cmp    atoibyte,0        ; read as many bytes as possible?
  1597.     je    atoi3b            ; e = yes
  1598.     push    ax            ; test for fitting into byte result
  1599.     mov    ax,temp            ; current low order value
  1600.     mul    nbase            ; times number base
  1601.     add    dx,ax            ; to dx, plus any overflow
  1602.     pop    ax
  1603.     add    dx,ax            ; next result would be in dx
  1604.     or    dh,dh            ; overflow beyond one byte
  1605.     jnz    atoi4            ; nz = cannot accept this byte
  1606.     cmp    cx,1            ; reading last byte?
  1607.     ja    atoi3b            ; a = no
  1608.     
  1609. atoi3b:    inc    bh            ; say we did a successful conversion
  1610.     push    ax            ; save this byte's value
  1611.     mov    ax,temp1
  1612.     mul    nbase            ; high order
  1613.     mov    temp1,ax        ; keep low order part of product
  1614.     mov    ax,temp
  1615.     mul    nbase            ; low order
  1616.     add    temp1,dx        ; high order carry
  1617.     mov    temp,ax            ; low order
  1618.     pop    ax            ; recover binary digit
  1619.     add    temp,ax
  1620.     adc    temp1,0
  1621.     loop    atoi3            ; get more
  1622.     jmp    short atoi4a        ; out of data, don't backup
  1623.                     ; here on break char in al
  1624.  
  1625. atoi4:    dec    si            ; backup to reread terminator
  1626.     inc    atoi_cnt
  1627.     mov    atoi_err,2        ; terminated on break char
  1628. atoi4a:    or    bl,bl            ; closing brace needed?
  1629.     jz    atoi6            ; z = no, success so far
  1630. atoi5:    call    atoi_read        ; get a byte for brace
  1631.     jnc    atoi5a
  1632.     cmp    atoi_cnt,0        ; out of counts?
  1633.     je    atoix            ; e = yes
  1634. atoi5a:    cmp    al,bl            ; the closing brace?
  1635.     jne    atoi5b            ; ne = no
  1636.     mov    atoi_err,1        ; success, terminator seen (brace)
  1637.     jmp    atoi6
  1638. atoi5b:    cmp    al,' '            ; space padding?
  1639.     je    atoi5            ; e = yes, skip it
  1640. atoi5c:    mov    atoi_err,8        ; syntax error (failed to get brace)
  1641.     jmp    atoix
  1642.  
  1643. atoi6:    or    bh,bh            ; did we do any conversion?
  1644.     jz    atoix            ; z = no
  1645.     pop    ax            ; throw away old saved ax
  1646.     pop    bx            ; restore bx
  1647.     pop    ax            ; throw away starting si, keep current
  1648.     pop    cx            ; restore old cx
  1649.     mov    dx,temp1
  1650.     mov    ax,temp
  1651.     pop    temp1
  1652.     pop    temp
  1653.     mov    atoibyte,0
  1654.     mov    atoi_cnt,0
  1655.     clc                ; clear carry for success
  1656.     ret
  1657. atoix:    pop    ax            ; restore first read al
  1658.     pop    bx
  1659.     pop    si            ; restore start value
  1660.     pop    cx            ; restore old cx
  1661.     pop    temp1
  1662.     pop    temp
  1663.     xor    dx,dx
  1664.     mov    atoibyte,0
  1665.     mov    atoi_cnt,0
  1666.     stc                ; set carry for failure
  1667.     ret
  1668. atoi    endp
  1669.  
  1670. ; Examine char in AL for being a number base indicator, O, X, D, in
  1671. ; either case.
  1672. ; If matched return carry clear, number base nbase set, and cx holding
  1673. ; the qty of bytes in a single character result value.
  1674. ; If not matched return carry set.
  1675. ; AL is preserved.
  1676. isnumbase proc    near
  1677.     push    ax            ; try for number bases
  1678.     cmp    al,'a'            ; lower case?
  1679.     jb    isnum1            ; b = no
  1680.     cmp    al,'z'            ; in range of lower case?
  1681.     ja    isnum1            ; a = no
  1682.     and    al,5fh            ; map to upper case
  1683. isnum1:    cmp    al,'O'            ; octal?
  1684.     jne    isnum2            ; ne = no
  1685.     mov    nbase,8            ; set number base
  1686.     cmp    atoibyte,0        ; all bytes?
  1687.     je    isnum4            ; e = yes
  1688.     mov    cx,3            ; three octal chars per final char
  1689.     jmp    short isnum4
  1690. isnum2:    cmp    al,'X'            ; hex?
  1691.     jne    isnum3            ; ne = no
  1692.     mov    nbase,16
  1693.     cmp    atoibyte,0        ; all bytes?
  1694.     je    isnum4            ; e = yes
  1695.     mov    cx,2            ; two hex chars per final char
  1696.     jmp    short isnum4
  1697. isnum3:    cmp    al,'D'            ; decimal?
  1698.     jne    isnum5            ; ne = no, syntax error
  1699.     mov    nbase,10
  1700.     cmp    atoibyte,0        ; all bytes?
  1701.     je    isnum4            ; e = yes
  1702.     mov    cx,3
  1703. isnum4:    pop    ax            ; successful result
  1704.     clc
  1705.     ret
  1706. isnum5:    pop    ax
  1707.     stc                ; carry set for failure
  1708.     ret
  1709. isnumbase endp
  1710.  
  1711. ; Read a byte from ds:si.
  1712. ; If success decrement byte atoi_cnt, inc si, return byte in AL, carry clear.
  1713. ; If failure return carry set, no change to atoi_cnt, AL unchanged.
  1714. atoi_read proc    near
  1715.     cmp    atoi_cnt,0        ; any bytes left?
  1716.     jg    atoi_read1        ; g = yes
  1717.     mov    atoi_err,4        ; c = insufficient bytes to resolve
  1718.     stc                ; return failure
  1719.     ret
  1720. atoi_read1:
  1721.     cld
  1722.     lodsb                ; get first char
  1723.     dec    atoi_cnt        ; one less byte availble
  1724.     clc
  1725.     ret
  1726. atoi_read endp
  1727.  
  1728.                     ; worker for atoi
  1729. cnvdig    proc    near            ; convert ascii code in al to binary
  1730.     push    ax            ; return carry set if cannot
  1731.     cmp    al,'a'            ; lower case hexadecimal candidate?
  1732.     jb    cnvdig1            ; b = no
  1733.     sub    al,20h            ; lower case hex to upper
  1734. cnvdig1:cmp    al,'A'            ; uppercase hex
  1735.     jb    cnvdig2
  1736.     sub    al,'A'-10-'0'        ; 'A' becomes '10'
  1737. cnvdig2:sub    al,'0'            ; remove ASCII bias
  1738.     jc    cnvdigx            ; c = out of range
  1739.     cmp    al,byte ptr nbase    ; out of range?
  1740.     jae    cnvdigx            ; ae = yes, out of range
  1741.     add    sp,2            ; pop saved ax
  1742.     clc                ; success, binary value is in al
  1743.     ret
  1744. cnvdigx:pop    ax            ; return ax unchanged
  1745.     stc                ; c set for failure
  1746.     ret
  1747. cnvdig    endp    
  1748.  
  1749. decout    proc    far        ; display decimal number in ax
  1750.     push    ax
  1751.     push    cx
  1752.     push    dx
  1753.     mov    cx,10        ; set the numeric base
  1754.     call    mvalout        ; convert and output value
  1755.     pop    dx
  1756.     pop    cx
  1757.     pop    ax
  1758.     ret
  1759. decout    endp
  1760.  
  1761. mvalout    proc    near        ; output number in ax using base in cx
  1762.                 ; corrupts ax and dx
  1763.     xor    dx,dx        ; clear high word of numerator
  1764.     div    cx        ; (ax / cx), remainder = dx, quotient = ax
  1765.     push    dx        ; save remainder for outputting later
  1766.     or    ax,ax        ; any quotient left?
  1767.     jz    mvalout1    ; z = no
  1768.     call    mvalout        ; yes, recurse
  1769. mvalout1:pop    dx        ; get remainder
  1770.     add    dl,'0'        ; make digit printable
  1771.     cmp    dl,'9'        ; above 9?
  1772.     jbe    mvalout2    ; be = no
  1773.     add    dl,'A'-1-'9'    ; use 'A'--'F' for values above 9
  1774. mvalout2:mov    ah,conout
  1775.     int    dos
  1776.     ret
  1777. mvalout    endp
  1778.  
  1779.  
  1780. valout    proc    far        ; output number in ax using base in cx
  1781.                 ; corrupts ax and dx
  1782.     xor    dx,dx        ; clear high word of numerator
  1783.     div    cx        ; (ax / cx), remainder = dx, quotient = ax
  1784.     push    dx        ; save remainder for outputting later
  1785.     or    ax,ax        ; any quotient left?
  1786.     jz    valout1        ; z = no
  1787.     call    valout        ; yes, recurse
  1788. valout1:pop    dx        ; get remainder
  1789.     add    dl,'0'        ; make digit printable
  1790.     cmp    dl,'9'        ; above 9?
  1791.     jbe    valout2        ; be = no
  1792.     add    dl,'A'-1-'9'    ; use 'A'--'F' for values above 9
  1793. valout2:mov    ah,conout
  1794.     int    dos
  1795.     ret
  1796. valout    endp
  1797.  
  1798. ; Write binary number in AX as decimal asciiz to buffer pointer DS:DI.
  1799. dec2di    proc    far        ; output number in ax using base in cx
  1800.     push    ax
  1801.     push    cx
  1802.     push    dx
  1803.     mov    cx,10
  1804.     call    dec2di1        ; recursive worker
  1805.     pop    dx
  1806.     pop    cx
  1807.     pop    ax
  1808.     ret
  1809.  
  1810. dec2di1    proc    near        ; worker of dec2di
  1811.     xor    dx,dx        ; clear high word of numerator
  1812.     div    cx        ; (ax / cx), remainder = dx, quotient = ax
  1813.     push    dx        ; save remainder for outputting later
  1814.     or    ax,ax        ; any quotient left?
  1815.     jz    dec2di2        ; z = no
  1816.     call    dec2di1        ; yes, recurse
  1817. dec2di2:pop    dx        ; get remainder
  1818.     add    dl,'0'        ; make digit printable
  1819.     mov    [di],dl        ; store char in buffer
  1820.     inc    di
  1821.     mov    byte ptr[di],0    ; add terminator
  1822.     ret
  1823. dec2di1    endp
  1824. dec2di    endp
  1825.  
  1826. ; Perform math. Reads argptr string, returns binary value in DX:AX.
  1827. ; Expects pointer to string to be in domath_ptr and its length in domath_cnt.
  1828. ; Note that value and operator lists are stored on the stack in positive
  1829. ; order (increasing array values are at increasing addresses, not in push
  1830. ; order).
  1831. ; Returns carry set if error.
  1832. domath    proc    far
  1833.     push    bx
  1834.     push    cx
  1835.     push    si
  1836.     push    di
  1837.     mov    si,domath_ptr
  1838.     mov    di,domath_cnt
  1839.     mov    parendepth,0        ; parenthesis nesting depth
  1840.     call    domath_main
  1841.     mov    domath_msg,0        ; re-enable domath error messages
  1842.     jnc    domathx1
  1843.     mov    domath_ptr,si
  1844.     mov    domath_cnt,di
  1845. domathx1:    
  1846.     pop    di
  1847.     pop    si
  1848.     pop    cx
  1849.     pop    bx
  1850.     ret
  1851. domath    endp
  1852.  
  1853. domath_main proc near
  1854.     push    si
  1855.     push    bp
  1856.     sub    sp,size fevalst        ; counters and lists
  1857.     mov    bp,sp            ; remember base of lists
  1858.     mov    [bp].numcnt,0        ; no numbers in list
  1859.     mov    [bp].opcnt,0        ; no operators in list
  1860.     mov    [bp].numlist,0        ; returned value
  1861.     mov    [bp].numlist[2],0    ; high order returned value
  1862.     inc    parendepth        ; recursion limit counter
  1863.     cmp    parendepth,maxdepth    ; called too many times?
  1864.     jbe    domath0            ; be = no
  1865.     mov    matherr,offset opmsg4
  1866.     jmp    domath_error        ; a = too many parentheses
  1867.  
  1868. domath0:call    gettoken        ; read a token from string
  1869.     jc    domath_exit        ; c = nothing present
  1870.     cmp    bl,'O'            ; operator?
  1871.     je    domath1            ; e = yes
  1872.     cmp    [bp].numcnt,2        ; have two numbers (num num)?
  1873.     jb    domath0            ; b = no, get an operator
  1874.     mov    matherr,offset opmsg1    ; missing operator
  1875.     jmp    domath_error        ; exit with grammar error
  1876.  
  1877.     ; have initial operator in AX, decide in/pre/post-fix style
  1878. domath1:cmp    al,'!'            ; postfix? (number is on stack)
  1879.     jne    domath2            ; ne = no
  1880.     call    domath_calc1        ; do math on one arg, result on stack
  1881.     jc    domath_error        ; failure, exit
  1882.     jmp    short domath0        ; done here, get next tokens
  1883.  
  1884.                     ; prefix or infix?
  1885. domath2:call    gettoken        ; get expected following number
  1886.     jc    domath_error        ; c = eof, unexpected, error
  1887.     cmp    bl,'N'            ; got the number?
  1888.     jne    domath4            ; ne = no, operator
  1889. domath3:call    domath_la        ; call lookahead proc
  1890.     ; c clear, bl = N for use current number on stack
  1891.     ; c clear, bl = O for have higher prec operator on stack & ax, delay
  1892.     ; c clear, bl = F for read end of file, no next operator
  1893.     ; c set,   bl = F for math failure, best to exit
  1894.     jc    domath_error        ; failure on math
  1895.     cmp    bl,'F'            ; end of file?
  1896.     je    domath_end        ; e = eof, do remaining math
  1897.     cmp    bl,'O'            ; found higher prec op (delay)?
  1898.     je    domath1            ; e = yes, do newest operator
  1899.     call    domath_calc2        ; bl = 'N', number ready, do math
  1900.     jc    domath_error        ; c = failure
  1901.     cmp    [bp].opcnt,0        ; any ops backlogged?
  1902.     jne    short domath3        ; be = try again to reduce back math
  1903.     jmp    domath0            ; start over
  1904.  
  1905.             ; Have op op. Is second op a prefix operator?
  1906. domath4:cmp    al,'~'            ; prefix?
  1907.     je    domath5            ; e = yes
  1908.     cmp    al,'-'            ; prefix, overload?
  1909.     je    domath5            ; e = yes
  1910.     cmp    al,'+'            ; prefix, overload?
  1911.     jne    domath_error        ; ne = no, inconsistent syntax
  1912.         ; could be  op number postfix  so look at next token to see
  1913.         ; and if so reduce <number postfix> before applying prefix.
  1914. domath5:call    gettoken        ; get expected number following prefix
  1915.     jc    domath_end        ; c = eof, do remaining math
  1916.     cmp    bl,'O'            ; operator?
  1917.     je    domath_error        ; e = yes, that makes op op op
  1918.     call    domath_la        ; do lookahead for postfix
  1919.     jc    domath_error        ; c = failure on math
  1920.     call    domath_calc1        ; do prefix calculation now
  1921.     jc    domath_error        ; c = failure
  1922.     call    popop            ; get first operator to ax
  1923.     call    pushop            ; put it back as if just read
  1924.     mov    bl,'O'            ; mark as operator
  1925.     jmp    short domath3        ; reanalyze from new operator
  1926.  
  1927.                     ; end of data, finish up math & exit
  1928. domath_end:                ; do calcs remaining on stack
  1929.     cmp    [bp].opcnt,0        ; operators remaining to be done?
  1930.     je    domath_exit        ; e = no
  1931.     call    domath_calc2        ; finish math
  1932.     jc    domath_error        ; c = failure
  1933.     jmp    short domath_end    ; keep trying til no more operators
  1934.  
  1935. domath_error:                ; math errors seen, exit in error
  1936.     mov    cx,1            ; set carry for error
  1937.     jmp    short domath_exit1
  1938.  
  1939. domath_exit:                ; only way out, restore stack
  1940.     xor    cx,cx            ; assume success, cx = error counter
  1941. domath_exit1:
  1942.     dec    parendepth        ; nesting depth
  1943.     cmp    [bp].numcnt,1        ; only one value remaning?
  1944.     je    domath_exit2        ; e = yes, that's good
  1945.     jb    domath_exit2a        ; b = no numbers, empty arg
  1946.     jcxz    domath_exit2        ; z = no error
  1947.  
  1948.     cmp    domath_msg,0        ; msgs permitted?
  1949.     jnz    domath_exit2a        ; nz = no
  1950.     mov    matherr,offset opmsg1    ; missing operator
  1951. domath_exit2a:
  1952.     inc    cx            ; say error
  1953. domath_exit2:
  1954.     cmp    [bp].opcnt,0        ; used all operators?
  1955.     je    domath_exit3        ; e = yes
  1956.     mov    matherr,offset opmsg    ; too many operators
  1957.     inc    cx            ; say error
  1958.  
  1959. domath_exit3:                ; all returns
  1960.     mov    ax,[bp].numlist        ; returned value
  1961.     mov    dx,[bp].numlist[2]    ; high order part
  1962.     mov    sp,bp            ; base of lists
  1963.     add    sp,size fevalst        ; counters and lists
  1964.     or    cx,cx            ; any errors?
  1965.     jz    domath_exit4        ; z = no (clears carry bit)
  1966.     cmp    domath_msg,0        ; ok to complain?
  1967.     jne    domath_exit3a        ; ne = no, keep quiet
  1968.     mov    ah,prstr
  1969.     mov    dx,matherr        ; math error pointer
  1970.     or    dx,dx            ; if any
  1971.     jz    domath_exit3a        ; z = none
  1972.     push    dx
  1973.     mov    dx,offset crlf
  1974.     int    dos
  1975.     pop    dx
  1976.     int    dos            ; display error message
  1977. domath_exit3a:
  1978.     xor    ax,ax            ; return zero on errors
  1979.     xor    dx,dx
  1980.     stc                ; set carry for fail
  1981. domath_exit4:
  1982.     mov    matherr,0
  1983.     pop    bp
  1984.     pop    si
  1985.     ret                ; all done. Result in DX:AX
  1986. domath_main endp
  1987.  
  1988. ; Worker for domath. Reads argptr, inc's it, dec's arglen. Recognizes
  1989. ; numbers (with or without leading \), recognizes math operators and ().
  1990. ; Returns numbers as binary in DX:AX and BL = 'N',
  1991. ; returns operator and precedence in AL and AH with BL = 'O'. 
  1992. ; Returns Carry set and BL = 'F' if neither kind of token.
  1993. ; Tolerates leading whitespace.
  1994. ; Uses domath_ptr as source pointer, domath_cnt as source count.
  1995. ; Array domath_tmp is owned by this routine as private space.
  1996. gettoken proc    near
  1997.     xor    ax,ax            ; no operator, lowest precedence
  1998.     xor    dx,dx
  1999.  
  2000. gettok0:cmp    domath_cnt,0        ; anything to read?
  2001.     jg    gettok1            ; g = yes
  2002.     mov    bl,'F'            ; F for end of file, failure
  2003.     stc
  2004.     ret
  2005.  
  2006. gettok1:mov    si,domath_ptr
  2007.     mov    al,byte ptr [si]
  2008.     cmp    al,' '            ; leading whitepace?
  2009.     jne    gettok1a        ; ne = no
  2010.     inc    domath_ptr
  2011.     dec    domath_cnt
  2012.     jmp    short gettok0
  2013.  
  2014. gettok1a:jb    gettok1b        ; b = non-printable
  2015.     cmp    al,127
  2016.     jb    gettok2            ; b = printable
  2017. gettok1b:mov    bl,'F'            ; declare F for end of file, failure
  2018.     mov    domath_cnt,0        ; truncate string
  2019.     clc
  2020.     ret
  2021.  
  2022. gettok2:mov    cx,domath_cnt
  2023.     mov    atoi_cnt,cx
  2024.     call    atoi            ; convert DS:SI to number in DX:AX
  2025.     jc    gettok5            ; c = failed to convert number
  2026.     mov    cx,si
  2027.     sub    cx,domath_ptr        ; amount read
  2028.     sub    domath_cnt,cx        ; deduct that read
  2029.     mov    domath_ptr,si        ; break char
  2030.     call    pushval            ; push onto number stack
  2031.     mov    bl,'N'            ; say returning number
  2032.     ret                ; may have carry bit set
  2033.  
  2034. gettok5:mov    si,domath_ptr
  2035.     lodsb                ; reread non-numeric
  2036.     inc    domath_ptr
  2037.     dec    domath_cnt
  2038.     push    di            ; look for operators
  2039.     push    es
  2040.     mov    di,seg operators    ; list of math operators
  2041.     mov    es,di
  2042.     mov    di,offset operators
  2043.     mov    cx,operator_len        ; length of list
  2044.     cld
  2045.     repne    scasb            ; look for match
  2046.     pop    es
  2047.     je    gettok6            ; e = found a match
  2048.     pop    di
  2049.     cmp    domath_msg,0        ; allowed to display messages?
  2050.     jne    gettok5a        ; ne = no
  2051.     push    ax
  2052.     mov    ah,prstr
  2053.     mov    dx,offset crlf
  2054.     int    dos
  2055.     mov    dx,offset opmsg2    ; unknown symbol
  2056.     int    dos
  2057.     pop    ax
  2058.     mov    dl,al            ; unknown op
  2059.     mov    ah,conout
  2060.     int    dos
  2061.     mov    ah,prstr
  2062.     mov    dx,offset crlf
  2063.     int    dos
  2064. gettok5a:
  2065.     dec    domath_ptr        ; set to read on this symbol
  2066.     inc    domath_cnt
  2067.     mov    bl,'F'            ; say returning parse failure
  2068.     stc                ; fail
  2069.     ret
  2070. gettok6:sub    di,offset operators + 1    ; index into list
  2071.     mov    ah,precedence[di]    ; precedence to return
  2072.     pop    di
  2073.     mov    bl,'O'            ; say returning operator
  2074.     cmp    al,'('            ; start recursion?
  2075.     je    gettok7
  2076.     cmp    al,')'            ; closure?
  2077.     je    gettok9            ; e = yes, it's an EOF here
  2078.     call    pushop            ; push operator on op stack
  2079.     clc
  2080.     ret
  2081.  
  2082. gettok7:call    domath_main        ; '(', invoke new scan
  2083.     jnc    gettok8            ; nc = succeeded
  2084.     ret                ; c = return failure
  2085. gettok8:call    pushval            ; push returned DX:AX
  2086.     mov    bl,'N'            ; report number
  2087.     ret                ; may have carry bit set
  2088.  
  2089. gettok9:mov    bl,'O'
  2090.     stc                ; ')' returns EOF
  2091.     ret
  2092. gettoken endp
  2093.  
  2094. ; Lookahead processor. Have ...<op><number> on stack
  2095. ; Return bl = N for return number present on stack, carry clear
  2096. ; Return bl = O for return higher prec operator on stack, carry clear
  2097. ; Return bl = F and carry clear for end of file
  2098. ; Return bl = F and carry set for math failure (errors)
  2099.  
  2100. domath_la proc near
  2101.     call    gettoken        ; expect operator or end of file
  2102.     jnc    domath_la1        ; nc = success
  2103.     mov    bl,'F'            ; say eof
  2104.     clc                ; carry clear for eof
  2105.     ret
  2106.  
  2107. domath_la1:
  2108.     cmp    bl,'O'            ; lookahead operator?
  2109.     je    domath_la2        ; e = yes
  2110.     mov    bl,'F'            ; fail, case of number number
  2111.     stc                ; syntax error, signal quit
  2112.     ret
  2113.  
  2114. domath_la2:
  2115.     mov    si,[bp].opcnt        ; get size of op list
  2116.     dec    si            ; zero based index
  2117.     or    si,si            ; sanity check, should be > 0
  2118.     jnz    domath_la2a        ; nz = have current op available
  2119.     mov    bl,'F'            ; signal total failure
  2120.     stc
  2121.     ret
  2122. domath_la2a:
  2123.     shl    si,1            ; index words
  2124.     mov    ax,[bp].oplist[si]    ; lookahead operator
  2125.     mov    cx,[bp].oplist[si-2]    ; currently active operator
  2126.     cmp    ah,ch            ; lookahead versus current op
  2127.     ja    domath_la3        ; a = lookahead is higher, delay
  2128.     call    popop            ; remove lookahead operator from stack
  2129.     dec    domath_ptr        ; set to reread operator
  2130.     inc    domath_cnt
  2131.     mov    bl,'N'            ; signal number ready on stack
  2132.     clc
  2133.     ret
  2134.  
  2135. domath_la3:                ; delay
  2136.     cmp    al,'!'            ; postfix on number?
  2137.     jne    domath_la5        ; ne = no, another operator, delay
  2138.     call    domath_calc1        ; combine number!, report number
  2139.     jnc    domath_la4        ; nc = success, use number on stack
  2140.     mov    bl,'F'            ; report failed
  2141.     ret                ; carry set
  2142.  
  2143. domath_la4:mov    bl,'N'            ; report number is on stack
  2144.     clc                ; say success
  2145.     ret
  2146.  
  2147. domath_la5:mov    bl,'O'        ; lookahead (delayed op) is on stack and ax
  2148.     clc            ; bl = 'O', signal delay and lookahead op
  2149.     ret            ; is on the stack
  2150. domath_la endp
  2151.  
  2152. ; Calculate results of top operator and one top value.
  2153. ; Return value on top of the value stack.
  2154. ; Returns carry set if failure
  2155. ; Pops one operator, and zero (failure) or one value from stack before
  2156. ; starting.
  2157. domath_calc1    proc    near
  2158.     mov    si,[bp].opcnt        ; count of operators
  2159.     cmp    si,0            ; any?
  2160.     jg    dom_c1_1a        ; g = yes
  2161.     mov    matherr,offset opmsg3
  2162.     stc                ; fail
  2163.     ret
  2164. dom_c1_1a:
  2165.     cmp    [bp].numcnt,0        ; one or more values?
  2166.     jne    dom_c1_1b        ; ne = yes
  2167.     mov    matherr,offset opmsg    ; too few values
  2168.     stc
  2169.     ret
  2170. dom_c1_1b:
  2171.     call    popop            ; get top operator from list to AX
  2172.     mov    mathkind,al        ; operation to do
  2173.     call    popval            ; get first value from list to DX:AX
  2174.  
  2175.     cmp    mathkind,'!'        ; factorial?
  2176.     jne    dom_c1c            ; ne = no
  2177.     mov    bx,ax            ; save argument
  2178.     mov    cx,ax
  2179.     mov    ax,-1            ; default for too large an arg
  2180.     mov    dx,ax
  2181.     cmp    cx,10            ; largest within 32 bits?
  2182.     ja    dom_c1b            ; a = too large, report -1
  2183.     mov    ax,1            ; default for 0! and 1!
  2184.     xor    dx,dx
  2185.     sub    cx,ax
  2186.     jle    dom_c1b            ; le = special cases of 0 and 1
  2187.     mov    ax,bx            ; recover argument
  2188. dom_c1a:mul    cx
  2189.     loop    dom_c1a
  2190. dom_c1b:jmp    dom_c1_1x
  2191.  
  2192. dom_c1c:cmp    mathkind,'~'        ; logical not?
  2193.     jne    dom_c1d            ; ne = no
  2194.     not    ax            ; 1's complement
  2195.     not    dx
  2196.     jmp    dom_c1_1x
  2197.  
  2198. dom_c1d:cmp    mathkind,'-'        ; negate?
  2199.     jne    dom_c1e            ; ne = no
  2200.     neg    dx
  2201.     neg    ax            ; change sign
  2202.     sbb    dx,0
  2203.     jmp    dom_c1_1x
  2204.  
  2205. dom_c1e:cmp    mathkind,'+'        ; unary '+'?
  2206.     je    dom_c1_1x        ; e = yes
  2207.  
  2208. dom_c1f:stc                ; unknown operator
  2209.     mov    matherr,offset opmsg2
  2210.     ret
  2211. dom_c1_1x:
  2212.     call    pushval
  2213.     ret                ; may have carry bit set
  2214. domath_calc1 endp
  2215.  
  2216. ; Calculate results of top operator and two top values.
  2217. ; Return value on top of the value stack.
  2218. ; Returns carry set if failure
  2219. ; Pops one operator, and one (failure) or two values from stack before
  2220. ; starting.
  2221. domath_calc2 proc near
  2222.     cmp    [bp].numcnt,2        ; two or more args?
  2223.     jb    domath_calc1        ; b = no, do 1 arg math
  2224.  
  2225.     call    popop            ; get top operator from list to AX
  2226.     jnc    dom_c2_2a        ; nc = success
  2227.     mov    matherr,offset opmsg3
  2228.     ret                ; fail
  2229. dom_c2_2a:
  2230.     mov    mathkind,al        ; operation to do
  2231.     call    popval            ; get first value from list to DX:AX
  2232.     mov    bx,ax            ; right side, low order
  2233.     mov    cx,dx            ; right side, high order
  2234.     call    popval            ; first (left side) to dx:ax
  2235.     cmp    mathkind,'^'        ; raise arg1 to power of arg2?
  2236.     jne    dom_c2_6        ; ne = no
  2237.     mov    cx,bx            ; use low order of exponent
  2238.     dec    cx            ;
  2239.     cmp    cx,31            ; largest exponent we allow
  2240.     jbe    dom_c2_1        ; be = ok
  2241.     mov    ax,-1            ; report -1 as overflow result
  2242.     cwd
  2243.     jc    dom_c2_2y        ; c = overflow error
  2244. dom_c2_1:or    cl,cl            ; zero power?
  2245.     jne    dom_c2_2        ; ne = no
  2246.     mov    ax,1            ; report 1
  2247.     xor    dx,dx
  2248.     jmp    dom_c2_2x
  2249. dom_c2_2:push    si
  2250.     push    di
  2251.     mov    si,ax            ; original number
  2252.     mov    di,dx
  2253. dom_c2_3:push    cx            ; save loop counter
  2254.     mov    cx,di
  2255.     mov    bx,si            ; original number
  2256.     call    lmul            ; use long multiply
  2257.     pop    cx
  2258.     jc    dom_c2_4        ; c = overflow
  2259.     loop    dom_c2_3
  2260.     pop    di
  2261.     pop    si
  2262.     jmp    dom_c2_2x
  2263. dom_c2_4:pop    di
  2264.     pop    si
  2265.     stc
  2266.     jmp    dom_c2_2y        ; failure from overflow
  2267.  
  2268. dom_c2_6:cmp    mathkind,'*'        ; times?
  2269.     jne    dom_c2_7        ; ne = no
  2270.     call    lmul            ; do 32 bit signed multiply
  2271.     jc    dom_c2_2y        ; c = overflow error
  2272.     jmp    dom_c2_2x
  2273.  
  2274. dom_c2_7:cmp    mathkind,'/'        ; divide?
  2275.     jne    dom_c2_8        ; ne = no
  2276.     call    ldiv
  2277.     jc    dom_c2_2y        ; c = overflow error
  2278.     jmp    dom_c2_2x
  2279.  
  2280. dom_c2_8:cmp    mathkind,'%'        ; modulo?
  2281.     jne    dom_c2_9        ; ne = no
  2282.     call    ldiv            ; divide
  2283.     jc    dom_c2_2y        ; c = overflow error
  2284.     xchg    cx,dx            ; get remainder to dx:ax
  2285.     xchg    bx,ax
  2286.     jmp    dom_c2_2x
  2287.  
  2288. dom_c2_9:cmp    mathkind,'&'        ; logical AND?
  2289.     jne    dom_c2_11        ; ne = no
  2290.     and    ax,bx
  2291.     and    dx,cx
  2292.     jmp    dom_c2_2x
  2293.  
  2294. dom_c2_11:cmp    mathkind,'+'        ; addition?
  2295.     jne    dom_c2_12        ; ne = no
  2296. dom_c2_11a:
  2297.     push    si
  2298.     mov    si,dx            ; get sign bit
  2299.     xor    si,cx            ; different? if so then 1
  2300.     add    ax,bx
  2301.     adc    dx,cx
  2302.     test    si,8000h        ; check for case of same start signs
  2303.     jnz    dom_c2_11b        ; nz = different signs, no overflow
  2304.     mov    si,dx
  2305.     xor    si,cx            ; different
  2306.     test    si,8000h        ; different signs now?
  2307.     pop    si
  2308.     jnz    dom_c2_2y        ; nz = yes, overflow
  2309.     jmp    dom_c2_2x
  2310. dom_c2_11b:pop    si
  2311.     jmp    dom_c2_2x
  2312.  
  2313. dom_c2_12:cmp    mathkind,'-'        ; subtraction?
  2314.     jne    dom_c2_13        ; ne = no
  2315.     neg    cx
  2316.     neg    bx            ; change sign of right side number
  2317.     sbb    cx,0
  2318.     jmp    short dom_c2_11a    ; now do addition with bounds check
  2319.  
  2320. dom_c2_13:cmp    mathkind,'|'        ; logical OR?
  2321.     jne    dom_c2_14        ; ne = no
  2322.     or    ax,bx
  2323.     or    dx,cx
  2324.     jmp    dom_c2_2x
  2325.  
  2326. dom_c2_14:cmp    mathkind,'#'        ; exclusive OR?
  2327.     jne    dom_c2_15        ; ne = no
  2328.     xor    ax,bx
  2329.     xor    dx,cx
  2330.     jmp    dom_c2_2x
  2331.  
  2332. dom_c2_15:cmp    mathkind,'@'        ; GCD?
  2333.     jne    dom_c2_16        ; ne = no
  2334.     call    lgcd            ; call the gcd routine
  2335.     jc    dom_c2_2y        ; c = overflow error
  2336.     jmp    dom_c2_2x
  2337. dom_c2_16:stc
  2338.     ret                ; unknown operator
  2339.  
  2340. dom_c2_2x:
  2341.     call    pushval            ; store result
  2342.     ret
  2343. dom_c2_2y:
  2344.     call    pushval            ; store result (typically -1)
  2345.     mov    matherr,offset opmsg3
  2346.     stc                ; say failure of math
  2347.     ret
  2348. domath_calc2 endp
  2349.  
  2350. ; Push operator (AL) and precedence (AH) onto the operator stack.
  2351. ; Increments [bp].opcnt.
  2352. ; Return carry set if no room.
  2353. pushop    proc    near
  2354.     mov    si,[bp].opcnt        ; count of operators
  2355.     cmp    si,listlen        ; list full?
  2356.     jae    pushop1            ; ae = yes, fail
  2357.     shl    si,1            ; words
  2358.     mov    [bp].oplist[si],ax    ; save operator (AL) and preced (AH)
  2359.     inc    [bp].opcnt        ; one more operator in the list
  2360.     clc                ; success
  2361.     ret
  2362. pushop1:mov    matherr,offset opmsg1
  2363.     stc                ; fail
  2364.     ret
  2365. pushop    endp
  2366.  
  2367. ; Pop current opcode from op stack to AX. Decrements [bp].opcnt.
  2368. ; Returns carry set if no operators are available
  2369. popop    proc    near
  2370.     mov    si,[bp].opcnt        ; count of operators in list
  2371.     or    si,si            ; any?
  2372.     jz    popop1            ; z = no, exit
  2373.     dec    si            ; index from zero
  2374.     shl    si,1            ; words
  2375.     mov    ax,[bp].oplist[si]    ; get lookahead (last) operator
  2376.     dec    [bp].opcnt
  2377.     clc                ; success
  2378.     ret
  2379. popop1:    mov    matherr,offset opmsg1
  2380.     stc                ; failed to op (empty list)
  2381.     ret
  2382. popop    endp
  2383.  
  2384. ; Push value in DX:AX onto value stack. Increments [bp].numcnt.
  2385. ; Returns carry set if not enough space.
  2386. pushval    proc    near
  2387.     mov    si,[bp].numcnt
  2388.     cmp    si,listlen        ; at limit of list?
  2389.     ja    pushval1        ; a = exceeded list length, fail
  2390.     shl    si,1            ; address dwords
  2391.     shl    si,1
  2392.     mov    [bp].numlist[si],ax    ; push dx:ax onto value stack
  2393.     mov    [bp].numlist[si+2],dx    ; high order part
  2394.     inc    [bp].numcnt        ; occupancy is one greater
  2395.     clc                ; succeed
  2396.     ret
  2397. pushval1:stc                ; fail
  2398.     ret
  2399. pushval    endp
  2400.  
  2401. ; Pop top value from value stack into DX:AX. Decrements [bp].numcnt.
  2402. ; Returns carry set if no value were available.
  2403. popval    proc    near
  2404.     mov    si,[bp].numcnt        ; count of values in list
  2405.     or    si,si            ; count
  2406.     jz    popval1            ; z = none
  2407.     dec    si            ; index from 0
  2408.     shl    si,1            ; address dwords
  2409.     shl    si,1
  2410.     mov    ax,[bp].numlist[si]    ; pop top value to dx:ax
  2411.     mov    dx,[bp].numlist[si+2]    ; high order part
  2412.     dec    [bp].numcnt        ; occupancy is one less
  2413.     clc                ; success
  2414.     ret
  2415. popval1:stc                ; fail
  2416.     ret
  2417. popval    endp
  2418.                     ; end of domath procedures
  2419.  
  2420. ; Perform 32 bit division. Numerator is in DX:AX, denominator in CX:BX.
  2421. ; Returns quotient in DX:AX, remainder in CX:BX. Carry set and -1 on
  2422. ; divide by zero.
  2423. ldiv    proc    far
  2424.     push    temp
  2425.     push    temp1
  2426.     push    si
  2427.     push    di
  2428.     mov    tmp,0            ; holds final sign (0=positive)
  2429.     or    dx,dx            ; numerator is negative?
  2430.     jge    ldiv1            ; ge = no
  2431.     neg    dx
  2432.     neg    ax
  2433.     sbb    dx,0            ; change sign
  2434.     xor    tmp,1            ; remember to change sign later
  2435. ldiv1:    or    cx,cx            ; denominator is negative?
  2436.     jge    ldiv2            ; ge = no
  2437.     neg    cx
  2438.     neg    bx
  2439.     sbb    cx,0            ; change sign
  2440.     xor    tmp,1            ; remember to change sign later
  2441. ldiv2:    mov    di,cx            ; denominator, hold here
  2442.     mov    si,bx
  2443.     cmp    dx,cx            ; is numerator larger than denom?
  2444.     jne    ldiv10            ; ne = yes, and 32 bits too
  2445.     or    dx,dx            ; 32 bit number?
  2446.     jnz    ldiv10            ; nz = yes
  2447.                 ; only 16 bit numbers here
  2448.     or    bx,bx            ; denominator zero?
  2449.     jnz    ldiv3            ; nz = no
  2450.     jmp    ldivf            ; overflow, report failure
  2451.  
  2452. ldiv3:    div    bx            ; regular signed division
  2453.     mov    bx,dx            ; remainder
  2454.     xor    dx,dx            ; high quotient
  2455.     xor    cx,cx            ; high remainder
  2456.     jmp    ldivx            ; exit success
  2457.  
  2458.                 ; 32 bit numbers here
  2459. ldiv10:    mov    tmp1,0            ; shift counter
  2460.     mov    temp,0            ; shift accumulator, low part
  2461.     mov    temp1,0            ; shift accumulator, high part
  2462.     or    cx,cx            ; check for zero denominator
  2463.     jnz    ldiv11            ; nz = not zero
  2464.     or    bx,bx
  2465.     jz    ldivf            ; zero, exit failure
  2466.  
  2467. ldiv11:    cmp    dx,di            ; top vs bottom high order words
  2468.     jb    ldiv13            ; b = top smaller than bottom
  2469.     ja    ldiv12            ; a = top larger than bottom
  2470.                     ; high order words are the same
  2471.     cmp    ax,si            ; top vs bottom low order words
  2472.     jb    ldiv13            ; b = top eq or larger than bottom
  2473.     je    ldiv15            ; e = top equals bottom
  2474.                 ; top is larger than bottom
  2475. ldiv12:    or    di,di            ; can shift left further?
  2476.     js    ldiv14            ; s = no, accumulate and back down
  2477.     inc    tmp1            ; remember doing left shift
  2478.     shl    si,1            ; shift denominator left one bit
  2479.     rcl    di,1            ; include carry-out from low half
  2480.     jmp    short ldiv11        ; compare again til top < bot
  2481.  
  2482. ; either we start with top < bottom (tmp1 = 0), or we have left shifted
  2483. ; the bottom enough (tmp1 > 0) to create that condition.
  2484.  
  2485.                        ; top is smaller than or equal to bottom
  2486. ldiv13:    cmp    tmp1,0            ; any shifts remaining?
  2487.     je    ldiv16            ; e = no
  2488. ; If tmp1 is 0 then we are done, no further adjustments can be made.
  2489. ; Else back off tmp1 by one position
  2490.     dec    tmp1            ; shift right by 1 to get top >= bot
  2491.     shr    di,1
  2492.     rcr    si,1
  2493.  
  2494. ldiv14:    cmp    dx,di            ; is top less than bottom?
  2495.     jb    ldiv13            ; b = yes, shift again
  2496.     ja    ldiv15            ; a = no, greater than
  2497.     cmp    ax,si            ; low order part
  2498.     jb    ldiv13            ; b = top is less than bottom
  2499. ldiv15:    sub    ax,si            ; (top - bottom) to get remainder
  2500.     sbb    dx,di
  2501.     call    ldiv_acc        ; accumulate shifted success
  2502.     jmp    short ldiv14        ; try again til top < bot
  2503.  
  2504. ldiv16:    mov    bx,ax            ; remainder to cx:bx
  2505.     mov    cx,dx            ; quotient to dx:ax
  2506.     mov    ax,temp            ; extract accumlated shifts
  2507.     mov    dx,temp1        ; and exit success
  2508.  
  2509.                     ; successful exit, update signs
  2510. ldivx:    cmp    tmp,0            ; need to adjust signs?
  2511.     je    ldivx1            ; e = no
  2512.     neg    dx
  2513.     neg    ax            ; change sign of quotient
  2514.     sbb    dx,0
  2515.     neg    cx
  2516.     neg    bx            ; change sign of remainder
  2517.     sbb    cx,0
  2518. ldivx1:    pop    di
  2519.     pop    si
  2520.     pop    temp1
  2521.     pop    temp
  2522.     clc                ; success
  2523.     ret
  2524.  
  2525. ldivf:    mov    ax,-1            ; failure
  2526.     cwd                ; report -1
  2527.     mov    bx,ax
  2528.     mov    cx,ax
  2529.     pop    di            ; failure exit
  2530.     pop    si
  2531.     pop    temp1
  2532.     pop    temp
  2533.     stc                ; say failure
  2534.     ret
  2535.  
  2536. ldiv_acc proc    near            ; add current shift to accumulator
  2537.     push    cx
  2538.     push    ax
  2539.     push    dx
  2540.     mov    cl,tmp1            ; shift bit count
  2541.     xor    ch,ch
  2542.     mov    ax,1            ; value = 2 ** temp1
  2543.     xor    dx,dx
  2544.     jcxz    ldiv_acc2        ; z = nothing to do
  2545. ldiv_acc1:shl    ax,1
  2546.     rcl    dx,1
  2547.     loop    ldiv_acc1
  2548. ldiv_acc2:add    temp,ax            ; accumulate shifts
  2549.     adc    temp1,dx
  2550.     pop    dx
  2551.     pop    ax
  2552.     pop    cx
  2553.     ret
  2554. ldiv_acc endp
  2555. ldiv    endp
  2556.  
  2557. ; Multiplies 32 bit values in DX:AX by CX:BX and returns the result in
  2558. ; DX:AX. Overflows result in carry set and -1 as the answer. Signed
  2559. ; arithmetic is used here, beware.
  2560. lmul    proc    far
  2561.     push    si
  2562.     push    di
  2563.     push    bx
  2564.     push    cx
  2565.     push    temp
  2566.     mov    temp,0            ; zero means no sign change at end
  2567.     or    dx,dx            ; negative?
  2568.     jns    lmul1            ; ns = no
  2569.     neg    ax            ; make positive
  2570.     neg    dx
  2571.     sbb    dx,0
  2572.     xor    temp,1            ; sign change needed
  2573. lmul1:    or    cx,cx            ; ditto for cx:bx
  2574.     jns    lmul2
  2575.     neg    bx
  2576.     neg    cx
  2577.     sbb    cx,0
  2578.     xor    temp,1
  2579. lmul2:    or    cx,cx            ; check for high word in both numbers
  2580.     jz    lmul3            ; z = bottom does not have it
  2581.     or    dx,dx
  2582.     jnz    lmul4            ; nz = both have parts too large
  2583.  
  2584. lmul3:    push    dx
  2585.     push    ax
  2586.     mul    bx            ; low bottom time whole top
  2587.     mov    di,dx
  2588.     mov    si,ax            ; regular product to di:si
  2589.     pop    ax
  2590.     pop    dx            ; recover normal top
  2591.     push    dx
  2592.     push    ax
  2593.     mov    ax,dx
  2594.     mul    bx
  2595.     add    di,ax
  2596.     pop    ax
  2597.     pop    dx            ; recover normal top
  2598.     js    lmul4            ; s = overflow in high accumlator
  2599.     mul    cx            ; high bottom times regular top
  2600.     add    di,ax            ; high reg product plus new product
  2601.     jns    lmul5            ; ns = no overflow in high accumulator
  2602.  
  2603. lmul4:    mov    ax,-1            ; overflow, yield minus one as answer
  2604.     cwd                ; extend sign to dx
  2605.     mov    temp,0            ; no sign change
  2606.     stc                ; carry set for problem
  2607.     jmp    short lmul6
  2608. lmul5:    mov    dx,di            ; results to dx:ax
  2609.     mov    ax,si
  2610.     cmp    temp,0            ; sign change needed?
  2611.     je    lmul6            ; e = no
  2612.     neg    dx            ; flip output sign
  2613.     neg    ax
  2614.     sbb    dx,0
  2615.     clc                ; carry clear for no problem
  2616. lmul6:    pop    temp
  2617.     pop    cx
  2618.     pop    bx
  2619.     pop    di
  2620.     pop    si
  2621.     ret
  2622. lmul    endp
  2623.  
  2624. ; Greatest common divisor, 32 bit (removes signs). Inputs are DX:AX and
  2625. ; CX:BX. Results are in DX:AX. Carry bit set and -1 returned if error.
  2626. lgcd    proc    far
  2627.     push    bx
  2628.     push    cx
  2629.     or    dx,dx            ; negative?
  2630.     jns    lgcd1            ; ns = no
  2631.     neg    ax            ; make positive
  2632.     neg    dx
  2633.     sbb    dx,0
  2634. lgcd1:    or    cx,cx            ; negative?
  2635.     jns    lgcd2            ; ns = no
  2636.     neg    bx
  2637.     neg    cx            ; make positive
  2638.     sbb    cx,0
  2639. lgcd2:    cmp    dx,cx            ; first arg same/larger than second?
  2640.     ja    lgcd4            ; a = yes
  2641.     jb    lgcd3            ; b = smaller
  2642.     cmp    ax,bx            ; low order parts
  2643.     jae    lgcd4            ; ae = larger
  2644. lgcd3:    xchg    dx,cx            ; make largest the top number dx:ax
  2645.     xchg    ax,bx
  2646. lgcd4:    push    bx            ; preserve smaller number
  2647.     push    cx
  2648.     call    ldiv            ; large / small
  2649.     jc    lgcd6            ; c = divide by zero
  2650.     mov    dx,cx            ; temp spot
  2651.     or    dx,bx            ; remainder?
  2652.     jz    lgcd5            ; z = no, small is the answer
  2653.     pop    dx            ; new values, new top is old bot
  2654.     pop    ax            ; bottom is new remainder
  2655.     jmp    short lgcd2        ; repeat calculation
  2656. lgcd5:    pop    dx            ; recover smaller number from stack
  2657.     pop    ax            ; result in dx:ax
  2658.     pop    dx
  2659.     pop    cx
  2660.     clc
  2661.     ret                ; success
  2662. lgcd6:    pop    ax
  2663.     pop    ax            ; clean stack
  2664.     pop    dx
  2665.     pop    cx
  2666.     mov    ax,-1            ; dx:ax = -1 on error
  2667.     cwd
  2668.     stc
  2669.     ret
  2670. lgcd    endp
  2671.  
  2672. ; Convert ASCIIZ string in DS:SI of form hh:mm:ss to ASCIIZ string seconds
  2673. ; in ES:DI.
  2674. tod2secs proc    far
  2675.     mov    temp,0
  2676.     mov    temp1,0
  2677.     xor    dx,dx            ; dh=field counter, dl=read byte
  2678.     xor    bx,bx            ; three groups possible
  2679. tod2s1:    mov    dl,[si]            ; get a char
  2680.     inc    si            ; next char
  2681.     inc    dh            ; count char in field
  2682.     or    dl,dl            ; null terminator?
  2683.     jz    tod2s3            ; z = yes, wrap calculations and quit
  2684.     cmp    dl,':'            ; field separator?
  2685.     je    tod2s3            ; e = a separator, step fields
  2686.     sub    dl,'0'            ; remove ascii bias
  2687.     cmp    dl,9
  2688.     ja    short tod2s6        ; a = failure to get expected digit
  2689.     cmp    dh,2            ; more than two bytes in this field
  2690.     ja    tod2s6            ; a = yes, fail translation
  2691.     mov    al,bh            ; get sum to al
  2692.     mov    ah,10
  2693.     mul    ah            ; sum times ten
  2694.     add    al,dl            ; sum = 10 * previous + current
  2695.     mov    bh,al            ; current sum
  2696.     mov    ah,60
  2697.     or    bl,bl            ; doing hours?
  2698.     jne    tod2s2            ; ne = no, min, sec
  2699.     mov    ah,24            ; max for hours field
  2700. tod2s2:    cmp    bh,ah            ; more than legal?
  2701.     jae    tod2s6            ; ae = illegal
  2702.     jmp    short tod2s1        ; continue analysis
  2703.  
  2704. tod2s3:    mov    al,bh            ; current sum
  2705.     xor    ah,ah
  2706.     mov    cx,1
  2707.     cmp    bl,2            ; seconds?
  2708.     je    tod2s4            ; e = yes
  2709.     mov    cx,60
  2710.     or    bl,bl            ; hours?
  2711.     jne    tod2s4            ; ne = no
  2712.     mov    cx,60*60        ; seconds per hour
  2713. tod2s4:    mul    cx
  2714.     add    temp,ax
  2715.     adc    temp1,dx
  2716.     xor    bh,bh            ; bh = current field sum
  2717.     xor    dh,dh            ; dh = counter of bytes in field
  2718.     cmp    byte ptr [si-1],0    ; ended on null?
  2719.     je    tod2s5            ; e = yes, end of conversion
  2720.     inc    bl            ; point to next field
  2721.     cmp    bl,2            ; last field to use (secs)
  2722.     jbe    tod2s1            ; be = get more text
  2723. tod2s5:    mov    ax,temp
  2724.     mov    dx,temp1
  2725.     push    di
  2726.     mov    di,offset tmpstring    ; build ASCII in this buffer
  2727.     call    flnout
  2728.     pop    di
  2729.     mov    dx,offset tmpstring
  2730.     mov    si,dx
  2731.     call    strlen
  2732.     cld
  2733.     rep    movsb            ; copy string to es:di area
  2734.     mov    byte ptr es:[di],0    ; and null terminator
  2735.     clc                ; carry clear for success
  2736.     ret
  2737. tod2s6:    mov    byte ptr es:[di],0
  2738.     stc                ; carry set for illegal value    
  2739.     ret
  2740. tod2secs endp
  2741. code1    ends 
  2742.     end
  2743.