NAME msster ; File MSSTER.ASM include mssdef.h ; Copyright (C) 1982, 1997, Trustees of Columbia University in the ; City of New York. The MS-DOS Kermit software may not be, in whole ; or in part, licensed or sold for profit as a software product itself, ; nor may it be included in or distributed with commercial products ; or otherwise distributed by commercial concerns to their clients ; or customers without written permission of the Office of Kermit ; Development and Distribution, Columbia University. This copyright ; notice must not be removed, altered, or obscured. ; ; Edit history ; 12 Jan 1995 version 3.14 ; Last edit ; 12 Jan 1995 public clscpt, defkey, clscpi, ploghnd, sloghnd, tloghnd public dopar, shokey, cptchr, pktcpt, targ, replay, repflg public kbdflg, shkadr, telnet, ttyact, write, dec2di, caplft public cnvlin, decout, valout, cnvstr public pntchr, pntflsh, pntchk, prnhand, prnopen public vfopen, vfread, ldiv, lmul, lgcd, atoi, atoibyte public domath, domath_ptr, domath_cnt, domath_msg public atoi_cnt, atoi_err, tod2secs braceop equ 7bh ; opening curly brace bracecl equ 7dh ; closing curly brace data segment extrn flags:byte, trans:byte, diskio:byte, portval:word extrn rdbuf:byte, dosnum:word, filtst:byte, prnname:byte extrn comand:byte, kstatus:word, flowon:byte, flowoff:byte extrn cardet:byte ifndef no_terminal extrn anspflg:byte endif ; no_terminal targ termarg <0,1,cptchr,0,parnon> crlf db cr,lf,'$' tmsg1 db cr,lf,'(Connecting to host, type $' tmsg3 db ' C to return to PC)',cr,lf,cr,lf,cr,lf,'$' erms21 db cr,lf,'?Cannot start the connection.$' erms25 db cr,lf,'?Input must be numeric$' erms22 db cr,lf,'?No open logging file$' erms23 db '*** Error writing session log, suspending capture ***$' erms24 db cr,lf,'?Error writing Packet log$' erms26 db ' *** PRINTER IS NOT READY *** press R to retry' db ' or D to discard printing: $' erms27 db cr,lf db '? Carrier signal not detected on this serial port.' db cr,lf db ' Use the DIAL command, SET CARRIER OFF, or check your modem.$' esctl db 'Control-$' repflg db 0 ; REPLAY or SET TERM REPLAY filespec flag reperr db cr,lf,'?File not found$' ; for REPLAY command noterm db cr,lf,'Connect mode is not built into this Kermit$' vfopbad db cr,lf,'?Cannot open file $' ; filename follows vfclbad db cr,lf,'?Cannot close file$' vfoptwice db cr,lf,'?File is already open$' vfnofile db cr,lf,'?File is not open$' vfrbad db cr,lf,'?Error while reading file$' vfwbad db cr,lf,'?Error while writing file$' vfrdbad db cr,lf,'?more parameters are needed$' vfrhandle dw -1 ; READ FILE handle (-1 = invalid) vfwhandle dw -1 ; WRITE FILE handle opntab db 3 ; OPEN FILE table mkeyw 'Read',1 mkeyw 'Write',2 mkeyw 'Append',3 inthlp db cr,lf,' ', 22 dup ('-'),' Special keys within Connect mode ' db 22 dup ('-') db cr,lf,' numeric keypad keys:' db cr,lf,' Pg Up/Dn roll screen vertically Ctrl Pg Up/Dn roll' db ' screen one line ' db cr,lf,' Home/End roll to start/end screen Ctrl End screen dump' db cr,lf,' - (minus) toggle status/mode line' db cr,lf,' other key combinations:' db cr,lf,' Alt-b send a BREAK Alt-n next active' db ' Telnet session' db cr,lf,' Alt-c start Compose sequence Alt-x exit emulator' db cr,lf,' Alt-h show this menu Alt-z network session' db ' hold' db cr,lf,' Alt-= or Alt-r reset emulator Alt-- toggle terminal' db ' type' db cr,lf,' Ctrl Prtscrn toggle printing' db cr,lf,' Additional commands are escape character ' inthlpc db ' ' db ' followed by special characters.' db cr,lf,' ',78 dup ('-') db cr,lf,' 7n1 = SET TERM BYTE 7, SET PARITY NONE, 1 stop bit' db cr,lf,' 8n1 = SET TERM BYTE 8, SET PARITY NONE, 1 stop bit' db cr,lf,' 7e1 = SET PARITY EVEN, 1 stop bit 7s1 = SET PARITY SPACE' db ', 1 stop bit' db cr,lf,' 7o1 = SET PARITY ODD, 1 stop bit 7m1 = SET PARITY MARK' db ', 1 stop bit' db cr,lf,' press a key to exit this menu' db 0 intqry db cr,lf,' ',20 dup ('-'),' Single character commands active now ' db 20 dup ('-') db cr,lf,' ? Show this menu F Dump screen to file' db cr,lf,' C Close the emulator P Push to DOS' db cr,lf,' S Status of the connection Q Quit logging' db cr,lf,' M Toggle mode line R Resume logging' db cr,lf,' B Send a Break 0 Send a null' db cr,lf,' L Send a long 1.8 s Break H Hangup phone' db cr,lf,' A Send Telnet "Are You There" I Send Telnet' db ' "Interrupt Process"' db cr,lf,' Typing the escape character ' intqryc db ' ' ; where escape char glyphs go db ' will send it to the host' db cr,lf,' press space bar to exit this menu' db 0 intprm db 'Command> $' intclet db 'B','C','F','H','L' ; single letter commands db 'M','P','Q','R','S' ; must parallel dispatch table intcjmp db '?','0','A','I' numlet equ $ - intclet ; number of entries even ifndef no_terminal intcjmp dw intchb,intchc,intchf,intchh,intchl dw intchm,intchp,intchq,intchr,intchs dw intchu,intchn,intayt,inttip endif ; no_terminal prnhand dw 4 ; printer file handle (4 = DOS default) even ploghnd dw -1 ; packet logging handle sloghnd dw -1 ; session logging handle tloghnd dw -1 ; transaction logging handle clotab db 6 mkeyw 'READ-FILE',4001h mkeyw 'WRITE-FILE',4002h mkeyw 'All-logs',logpkt+logses+logtrn mkeyw 'Packets',logpkt mkeyw 'Session',logses mkeyw 'Transaction',logtrn clseslog db cr,lf,' Closing Session log$' clpktlog db cr,lf,' Closing Packet log$' cltrnlog db cr,lf,' Closing Transaction log$' writetab db 5 ; Write command log file types mkeyw 'FILE',4002h ; FILE mkeyw 'Packet',logpkt mkeyw 'Screen',80h ; unused value, to say screen mkeyw 'Session',logses mkeyw 'Transaction',logtrn sttmsg db cr,lf,'Press space to continue ...$' kbdflg db 0 ; non-zero means char here from Term ttyact db 0 ; Connect mode active, if non-zero shkadr dw 0 ; offset of replacement Show Key cmd nbase dw 10 ; currently active number base temp dw 0 temp1 dw 0 tmp db 0 tmp1 db 0 tmpstring db 6 dup (0) ; local string work buffer pktlft dw cptsiz ; number free bytes left caplft dw cptsiz ; number free bytes left maxdepth equ 8 ; domath, parenthesis nesting depth listlen equ 6 ; domath, outstanding numbers and ops parendepth db 0 ; domath, depth of ()'s mathkind db 0 ; domath, math operator symbol, temp operators db '()!~^*/%&+-|#@' ; domath, math operator list operator_len equ $ - operators ; domath, length of the list precedence db 6,6,5,4,3, 2,2,2,2,1, 1,1,1,1 ; domath, precedence of ops fevalst struc ; structure of demath work stacks, all on the stack savebp dw 0 ; where bp is pushed numcnt dw 0 ; count of values in numlist numlist dw listlen dup (0,0) ; list of dword values opcnt dw 0 ; count of operators in oplist oplist dw listlen dup (0) ; list of pending operators fevalst ends domath_ptr dw 0 ; ds:offset of domath input text domath_cnt dw 0 ; count of bytes in input text domath_msg dw 0 ; non-zero to allow error msgs matherr dw 0 ; non-zero = offset of error msg opmsg db '?too many math operators or too few values $' opmsg1 db '?math operator missing or invalid $' opmsg2 db '?unknown math symbol: $' opmsg3 db '?math expression error $' opmsg4 db '?parentheses nested too deeply $' atoibyte db 0 ; non-zero to say convert one char atoi_err db 0 ; atoi return error codes atoi_cnt dw 0 ; atoi, bytes supplied to/remaining data ends data1 segment rephlp db 'name of file to playback$' clohlp db cr,lf,' READ-FILE or WRITE-FILE, or the following log files:' db cr,lf,' All-logs, Packets, Session, Transaction$' msgtxt db 'text to be written$' vfophlp db 'Filename$' vfrdmsg db 'name of variable into which to read a line from file$' pktbuf db cptsiz dup (0) ; packet logging buffer pktbp dw pktbuf ; buffer pointer to next free byte capbuf db cptsiz dup (0) ; session logging buffer capbp dw capbuf ; buffer pointer to next free byte prnbuf db cptsiz dup (0) ; printer buffer pntptr dw prnbuf ; pointer to next free byte data1 ends code1 segment extrn iseof:far, strlen:far, prtasz:far, isfile:far assume cs:code1 code1 ends code segment extrn comnd:near, outchr:near, stat0:near, pcwait:far extrn beep:near, puthlp:near, serhng:near, lnout:near extrn serini:near, serrst:near, sendbr:near, putmod:near extrn fpush:near, sendbl:near, trnmod:near, dodecom:near ifndef no_terminal extrn dumpscr:near,term:near endif ; no_terminal flnout proc far call lnout ret flnout endp assume cs:code, ds:data, es:nothing ; the show key command shokey proc near cmp shkadr,0 ; keyboard translator present? je shokey1 ; e = no, use regular routines mov bx,shkadr ; get offset of replacement routine jmp bx ; and execute it rather than us shokey1:clc ret shokey endp ; enter with ax/scan code to define, si/ pointer to definition, cx/ length ; of definition. Defines it in definition table. Obsolete. defkey proc near ret defkey endp ; This is the CONNECT command ifdef no_terminal TELNET PROC NEAR mov ah,cmeol call comnd ; get a confirm jnc teln1 ; nc = success ret teln1: mov ah,prstr mov dx,offset noterm int dos clc ret TELNET ENDP endif ; no_terminal ifndef no_terminal TELNET PROC NEAR mov ah,cmeol call comnd ; get a confirm jnc teln1 ; nc = success ret teln1: cmp repflg,0 ; REPLAY? jne teln1d ; ne = yes call serini ; ensure port is inited now jnc teln1a ; nc = success test flags.remflg,dquiet ; quiet display mode? jnz teln1b ; nz = yes. Don't write to screen mov dx,offset erms21 ; say cannot start connection mov ah,prstr int dos teln1b: or kstatus,ksgen ; general command failure ret teln1a: cmp flags.comflg,'F' ; Fossil port? je teln1c ; e = yes cmp flags.comflg,'4' ; using real or Bios serial ports? ja teln1d ; a = no teln1c: cmp flags.carrier,0 ; want CD on now? je teln1d ; e = no test cardet,80h ; CD on? jnz teln1d ; nz = yes mov ah,prstr ; complain on screen mov dx,offset erms27 int dos stc ; fail ret teln1d: cmp flags.vtflg,0 ; emulating a terminal? jne teln2 ; ne= yes, no wait necessary mov ah,prstr mov dx,offset crlf ; output a crlf int dos call domsg ; reassure user mov ax,2000 ; two seconds call pcwait ; pause teln2: xor al,al ; initial flags mov ttyact,1 ; say telnet is active cmp flags.vtflg,0 ; emulating a terminal? je teln3 ; e = no, say mode line is to be off cmp flags.modflg,0 ; mode line enabled? jne tel010 ; ne = yes teln3: or al,modoff ; no, make sure it stays off tel010: test flags.debug,logses ; debug mode? jz tel0 ; z = no, keep going or al,trnctl ; yes, show control chars tel0: cmp flags.vtflg,0 ; emulating a terminal? je tel1 ; e = no or al,emheath ; say emulating some kind of terminal tel1: mov bx,portval cmp [bx].ecoflg,0 ; echoing? jz tel2 ; z = no or al,lclecho ; turn on local echo tel2: mov targ.flgs,al ; store flags mov ah,flags.comflg ; COMs port identifier mov targ.prt,ah ; Port 1 or 2, etc mov ah,[bx].parflg ; parity flag mov targ.parity,ah mov ax,[bx].baud ; baud rate identifier mov targ.baudb,al xor ah,ah test flags.capflg,logses ; select session logging flag bit jz tel3 ; z = no logging mov ah,capt ; set capture flag tel3: or targ.flgs,ah jmp short tem1 TEM: call serini ; init serial port jnc tem1 ; nc = success mov ttyact,0 ; say we are no longer active clc ret ; and exit Connect mode tem1: mov dx,offset crlf ; give user an indication that we are mov ah,prstr ; entering terminal mode int dos mov ax,offset targ ; point to terminal arguments call term ; call the main Terminal procedure mov al,kbdflg ; get the char from Term, if any mov kbdflg,0 ; clear the flag or al,al ; was there a char from Term? jnz intch2 ; nz = yes, else ask for one from kbd intchar:call iseof ; stdin at eof? jnc intch1 ; nc = not eof, get more mov al,'C' ; use C when file is empty jmp intchc ; to provide an exit intch1: cmp al,0 ; asking for help? jne intch1b ; ne = no mov al,' ' mov ah,trans.escchr cmp ah,' ' ; printable now? jae intch1a ; ae = yes mov al,'^' add ah,40h ; make control visible intch1a:mov word ptr intqryc,ax ; store escape char code mov ax,offset intqry ; '?' get help message call puthlp ; write help msg mov dx,offset intprm mov ah,prstr ; show prompt int dos intch1b:mov ah,0ch ; clear Bios keyboard buffer and do mov al,coninq ; read keyboard, no echo int dos ; get a char or al,al ; scan code indicator? jnz intch1c ; nz = no, ascii mov ah,coninq ; read and discard scan code int dos jmp short intch1 ; try again intch1c:cmp al,'?' ; want to see menu again? jne intch2 ; ne = no xor al,al ; prep for menu display jmp short intchar intch2: mov flags.cxzflg,0 ; prevent Control-C carryover cmp al,' ' ; space? je tem ; e = yes, ignore it cmp al,cr ; check ^M (cr) against plain ascii M je tem ; exit on cr cmp al,trans.escchr ; is it the escape char? jne intch3 ; ne = no mov ah,al call outchr jmp tem ; return, we are done here intch3: push es push ds pop es mov di,offset intclet ; command letters mov cx,numlet ; quantity of them cmp al,' ' ; control code? jae intch3a ; ae = no or al,40H ; convert control chars to printable intch3a:cmp al,96 ; lower case? jb intch3b ; b = no and al,not (20h) ; move to upper case intch3b:cld repne scasb ; find the matching letter pop es jne intch4 ; ne = not found, beep and get another dec di ; back up to letter sub di,offset intclet ; get letter number shl di,1 ; make it a word index jmp intcjmp[di] ; dispatch to it intch4: call beep ; say illegal character jmp intchar intayt: mov ah,255 ; 'I' Telnet Are You There call outchr ; send IAC (255) AYT (246) mov ah,246 call outchr jmp tem intchb: call sendbr ; 'B' send a break jmp tem ; And return intchc: mov ttyact,0 ; 'C' say we are no longer active clc ; and exit Connect mode ret intchf: call dumpscr ; 'F' dump screen, use msy routine jmp tem ; and return intchh: call serhng ; 'H' hangup phone call serrst ; turn off port jmp tem intchl: call sendbl ; 'L' send a long break jmp tem inttip: mov ah,255 ; 'I' Telnet Interrrupt Process call outchr ; send IAC (255) IP (244) mov ah,244 call outchr jmp tem intchm: cmp flags.modflg,1 ; 'M' toggle mode line, enabled? jne intchma ; ne = no, leave it alone xor targ.flgs,modoff ; enabled, toggle its state intchma:jmp tem ; and reconnect intchp: call fpush ; 'P' push to DOS mov dx,offset sttmsg ; say we have returned mov ah,prstr int dos jmp short intchsb ; wait for a space intchq: and targ.flgs,not capt ; 'Q' suspend session logging jmp tem ; and resume intchr: test flags.capflg,logses ; 'R' resume logging. Can we capture? jz intchr1 ; z = no or targ.flgs,capt ; turn on session logging flag intchr1:jmp tem ; and resume intchs: call stat0 ; 'S' status, call stat0 mov dx,offset sttmsg mov ah,prstr int dos intchsa:call iseof ; is stdin at eof? jnc intchsb ; nc = not eof, get more jmp tem ; resume if EOF intchsb:mov ah,coninq ; console input, no echo int dos cmp al,' ' ; space? jne intchsa jmp tem intchu: mov ah,trans.escchr cmp ah,' ' ; printable now? jae intchu1 ; ae = yes mov al,'^' add ah,40h ; make control visible intchu1:mov word ptr inthlpc,ax ; store escape char code mov ax,offset inthlp ; help message call puthlp ; write help msg mov ah,0ch ; clear Bios keyboard buffer and do mov al,coninq ; read keyboard, no echo int dos ; get a char or al,al ; scan code indicator? jnz intchu2 ; nz = no, ascii mov ah,coninq ; read and discard scan code int dos intchu2:jmp tem ; try again intchn: xor ah,ah ; '0' send a null call outchr jmp tem TELNET ENDP endif ; no_terminal code ends code1 segment assume cs:code1 ; Reassure user about connection to the host. Tell him what escape sequence ; to use to return DOMSG PROC FAR mov ah,prstr mov dx,offset tmsg1 int dos call escprt mov ah,prstr mov dx,offset tmsg3 int dos ret DOMSG ENDP ; print the escape character in readable format. ESCPRT PROC NEAR mov dl,trans.escchr cmp dl,' ' jge escpr2 push dx mov ah,prstr mov dx,offset esctl int dos pop dx add dl,040H ; Make it printable escpr2: mov ah,conout int dos ret ESCPRT ENDP code1 ends code segment assume cs:code ; Set parity for character in Register AL dopar: push bx mov bx,portval mov bl,[bx].parflg ; get parity flag byte test bl,PARHARDWARE ; hardware? jnz parret ; nz = yes cmp bl,parnon ; No parity? je parret ; Just return and al,07FH ; Strip parity. Same as Space parity cmp bl,parspc ; Space parity? je parret ; e = yes, then we are done here cmp bl,parevn ; Even parity? jne dopar0 ; ne = no or al,al jpe parret ; pe = even parity now xor al,080H ; Make it even parity jmp short parret dopar0: cmp bl,parmrk ; Mark parity? jne dopar1 ; ne = no or al,080H ; Turn on the parity bit jmp short parret dopar1: cmp bl,parodd ; Odd parity? or al,al jpo parret ; Already odd, leave it xor al,080H ; Make it odd parity parret: pop bx ret ; REPLAY filespec through terminal emulator replay proc near mov bx,offset rdbuf ; place for filename mov dx,offset rephlp ; help mov repflg,0 ; clear the replay active flag mov ah,cmword ; get filename call comnd jc replay2 ; c = failure mov ah,cmeol ; get an EOL confirm call comnd jc replay2 ; c = failure ifndef no_terminal mov ah,open2 ; open file xor al,al ; open readonly cmp byte ptr dosnum+1,2 ; above DOS 2? jna replay1 ; na = no, so no shared access mov al,0+40h ; open readonly, deny none replay1:mov dx,offset rdbuf ; asciiz filename int dos jnc replay3 ; nc = success mov ah,prstr mov dx,offset reperr ; Cannot open that file int dos clc ret replay3:mov diskio.handle,ax ; file handle mov repflg,1 ; set replay flag call telnet ; enter Connect mode mov bx,diskio.handle mov ah,close2 ; close the file int dos endif ; no_terminal mov repflg,0 ; clear the flag clc replay2:ret replay endp cptchr proc near ; session capture routine, char in al test flags.capflg,logses ; session logging active now? jz cptch1 ; z = no push di push es mov di,data1 ; seg of capbp and capbuf mov es,di cld mov di,es:capbp ; buffer pointer stosb inc es:capbp pop es pop di dec caplft ; decrement chars remaining jg cptch1 ; more room, forget this part call cptdmp ; dump the info cptch1: ret cptchr endp cptdmp proc near ; empty the capture buffer push ax push bx push cx push dx mov bx,sloghnd ; get file handle or bx,bx ; is file open? jle cptdm1 ; le = no, skip it mov cx,cptsiz ; original buffer size sub cx,caplft ; minus number remaining jl cptdm2 ; means error jcxz cptdm1 ; z = nothing to do push ds mov dx,data1 ; seg of capbuf mov ds,dx mov dx,offset capbuf ; the capture routine buffer mov ah,write2 ; write with filehandle int dos ; write out the block pop ds jc cptdm2 ; carry set means error cmp ax,cx ; wrote all? jne cptdm2 ; no, an error push es mov dx,data1 ; seg of capbuf and capbp mov es,dx mov es:capbp,offset capbuf pop es mov caplft,cptsiz ; init buffer ptr & chrs left clc jmp short cptdm1 cptdm2: and targ.flgs,not capt ; so please stop capturing and flags.capflg,not logses ; deselect session logging flag bit mov dx,offset erms23 ; tell user the bad news call putmod ; write on mode line stc cptdm1: pop dx pop cx pop bx pop ax ret cptdmp endp pktcpt proc near ; packet log routine, char in al test flags.capflg,logpkt ; logging packets now? jz pktcp1 ; z = no push di push es mov di,data1 ; seg of pktbuf and pktbp mov es,di mov di,es:pktbp ; buffer pointer cld stosb ; store char in buffer inc es:pktbp ; move pointer to next free byte pop es pop di dec pktlft ; decrement chars remaining jg pktcp1 ; g = more room, forget this part call pktdmp ; dump the info pktcp1: ret pktcpt endp pktdmp proc near ; empty the capture buffer push ax push bx push cx push dx mov bx,ploghnd ; get file handle or bx,bx ; is file open? jle cptdm1 ; le = no, skip it mov cx,cptsiz ; original buffer size sub cx,pktlft ; minus number remaining jl pktdm2 ; l means error jcxz pktdm1 ; z = nothing to do push ds mov dx,data1 ; seg of pktbuf mov ds,dx mov dx,offset pktbuf ; the capture routine buffer mov ah,write2 ; write with filehandle int dos ; write out the block pop ds jc pktdm2 ; carry set means error cmp ax,cx ; wrote all? jne pktdm2 ; ne = no, error push es mov dx,data1 ; seg of pktbuf mov es,dx mov es:pktbp,offset pktbuf pop es mov pktlft,cptsiz ; init buffer ptr & chrs left jmp short pktdm1 pktdm2: and flags.capflg,not logpkt ; so please stop capturing mov dx,offset erms24 ; tell user the bad news mov ah,prstr int dos call clscp4 ; close the packet log pktdm1: pop dx pop cx pop bx pop ax ret pktdmp endp ; CLOSE command clscpt proc near mov ah,cmkey ; get kind of file to close mov dx,offset clotab ; close table mov bx,offset clohlp ; help call comnd jc clscp2 ; c = failure mov temp,bx mov ah,cmeol call comnd jc clscp2 ; c = failure mov kstatus,kssuc ; success status thus far mov bx,temp cmp bh,40h ; READ-FILE or WRITE-FILE? jne clscp0 ; ne = no mov ax,bx jmp vfclose ; close FILE, pass kind in AX clscp0: cmp bx,logpkt+logses+logtrn ; close all? je clscpi ; e = yes cmp bx,logpkt ; just packet? je clscp4 cmp bx,logses ; just session? je clscp6 cmp bx,logtrn ; just session? jne clscp1 ; ne = no jmp clscp8 clscp1: mov dx,offset erms22 ; say none active mov ah,prstr int dos clc ret clscp2: mov kstatus,ksgen ; general cmd failure status stc ret ; CLSCPI called at Kermit exit CLSCPI: mov bx,portval mov [bx].flowc,0 ; set no flow control so no sending it call pntflsh ; flush PRN buffer call clscp4 ; close packet log call clscp6 ; close session log call clscp8 ; close transaction log mov al,2 ; close WRITE FILE log call vfclose clc ; return success ret clscp4: push bx ; PACKET LOG mov bx,ploghnd ; packet log handle or bx,bx ; is it open? jle clscp5 ; e = no call pktdmp ; dump buffer mov ah,close2 int dos cmp flags.takflg,0 ; ok to echo? je clscp5 ; e = no mov ah,prstr mov dx,offset clpktlog ; tell what we are doing int dos clscp5: mov ploghnd,-1 ; say handle is invalid pop bx and flags.capflg,not logpkt ; say this log is closed ret clscp6: push bx ; SESSION LOG mov bx,sloghnd ; session log handle or bx,bx ; is it open? jle clscp7 ; e = no call cptdmp ; dump buffer mov ah,close2 int dos cmp flags.takflg,0 ; ok to echo? je clscp7 ; e = no mov ah,prstr mov dx,offset clseslog ; tell what we are doing int dos clscp7: mov sloghnd,-1 ; say handle is invalid pop bx and flags.capflg,not logses ; say this log is closed ret clscp8: push bx ; TRANSACTION LOG mov bx,tloghnd ; transaction log handle or bx,bx ; is it open? jle clscp9 ; e = no mov ah,close2 int dos cmp flags.takflg,0 ; ok to echo? je clscp9 ; e = no mov ah,prstr mov dx,offset cltrnlog ; tell what we are doing int dos clscp9: mov tloghnd,-1 ; say handle is invalid pop bx and flags.capflg,not logtrn ; say this log is closed ret clscpt endp ; Print on PRN the char in register al. On success return with C bit clear. ; On failure do procedure pntchk and return its C bit (typically C set). ; Uses buffer dumpbuf (screen dump). pntchr proc near push es push ax mov ax,data1 ; segment of pntptr and prnbuf mov es,ax cmp es:pntptr,offset prnbuf+cptsiz ; buffer full yet? pop ax pop es jb pntchr1 ; b = no call pntflsh ; flush buffer now jnc pntchr1 ; nc = success ret ; c = fail, discard char pntchr1:push es push bx mov bx,data1 ; segment of pntptr and prnbuf mov es,bx mov bx,es:pntptr ; pointer to next open slot mov es:[bx],al ; store the character inc bx ; update pointer mov es:pntptr,bx ; save pointer pop bx pop es clc ; clear carry bit ret pntchr endp ; Flush printer buffer. Return carry clear if success. ; On failure do procedure pntchk and return its C bit (typically C set). ; Uses buffer dumpbuf (screen dump). pntflsh proc near push es push ax mov ax,data1 ; segment of pntptr and prnbuf mov es,ax cmp es:pntptr,offset prnbuf ; any text in buffer? pop ax pop es ja pntfls1 ; a = yes clc ret ; else nothing to do pntfls1:cmp prnhand,0 ; is printer handle valid? jg pntfls2 ; g = yes push es push ax mov ax,data1 ; segment of pntptr and prnbuf mov es,ax mov es:pntptr,offset prnbuf pop ax pop es clc ; omit printing, quietly ret pntfls2:push ax push bx push cx push dx push si push di push es mov ah,flowoff ; get flow control char or ah,ah ; flow control active? jz pntfls3 ; z = no, not using xoff call outchr ; output xoff (ah), no echo pntfls3:mov bx,prnhand ; file handle for DOS printer PRN push DS ; about to change DS mov dx,data1 ; segment of prnbuf mov ds,dx ; set DS mov dx,offset prnbuf ; start of buffer mov cx,ds:pntptr sub cx,dx ; cx = current byte count pntfls4:push cx push dx mov cx,1 mov ah,write2 int dos ; write buffer to printer pop dx pop cx jc pntfls5 ; c = call failed cmp ax,1 ; did we write it? jne pntfls5 ; ne = no, dos critical error inc dx ; point to next char loop pntfls4 mov ds:pntptr,offset prnbuf ; reset buffer pointer pop DS ; restore DS clc ; declare success jmp pntfls11 pntfls5:mov si,dx ; address of next char to be printed mov di,offset prnbuf ; start of buffer sub dx,di ; dx now = number successful prints mov cx,ds:pntptr sub cx,si ; count of chars to be printed jle pntfls6 mov ax,ds mov es,ax cld rep movsb ; copy unwritten to start of buffer pntfls6:sub ds:pntptr,dx ; move back printer pointer by ok's pop DS ; restore DS pntfls7:mov dx,offset erms26 ; printer not ready, get user action call putmod ; write new mode line call beep ; make a noise mov ah,0ch ; clear DOS typeahead buffer mov al,1 ; read from DOS buffer int dos or al,al ; Special key? jnz pntfls8 ; nz = no, consume mov al,1 ; consume scan code int dos jmp short pntfls7 ; try again pntfls8:and al,not 20h ; lower to upper case quicky cmp al,'R' ; Retry? jne pntfls8a ; ne = no call trnmod ; toggle mode line call trnmod ; back to same state as before jmp pntfls3 ; go retry pntfls8a:cmp al,'D' ; Discard printing? jne pntfls7 ; ne = no, try again mov bx,prnhand cmp bx,4 ; stdin/stdout/stderr/stdaux/stdprn? jbe pntfls9 ; be = yes, always available mov ah,close2 ; close this file int dos pntfls9:mov bx,offset prnname ; name of printer file mov word ptr [bx],'UN' ; set to NUL<0> mov word ptr [bx+2],'L'+0 push es mov ax,data1 ; seg for pntptr mov es,ax mov es:pntptr,offset prnbuf ; reset pointer pop es mov prnhand,-1 ; declare handle invalid ifndef no_terminal mov anspflg,0 ; mszibm/msyibm print status flag off endif ; no_terminal pntfls10:call trnmod ; toggle mode line call trnmod ; back to same state as before stc ; declare failure pntfls11:pushf mov ah,flowon or ah,ah ; flow control active? jz pntfls12 ; z = no, not using xon call outchr ; output xon (al), no echo pntfls12:popf pop es pop di pop si pop dx pop cx pop bx pop ax ret ; nc = success pntflsh endp ; Check for PRN (DOS's printer) being ready. If ready, return with C clear ; Otherwise, write Not Ready msg on mode line and return with C bit set. ; N.B. DOS Critical Error will occur here if PRN is not ready. [jrd] pntchk proc near push dx push cx push ax mov cx,10 ; ten retries before declaring error cmp prnhand,0 ; printer handle valid? jle pntchk2 ; le = no, invalid pntchk0:push bx mov bx,prnhand ; file handle mov ah,ioctl ; get printer status, via DOS mov al,7 ; status for output int dos pop bx jc pntchk1 ; c = call failed cmp al,0ffh ; code for Ready? je pntchk3 ; e = yes, assume printer is ready pntchk1:push cx ; save counter, just in case mov ax,100 ; wait 100 millisec call pcwait pop cx loop pntchk0 ; and try a few more times ; get here when printer is not ready pntchk2:pop ax pop cx pop dx stc ; say printer not ready ret pntchk3:pop ax pop cx pop dx clc ; say printer is ready ret pntchk endp prnopen proc near push ax push bx push cx push dx mov prnhand,4 ; preset default handle mov dx,offset prnname ; name of disk file, from msssho mov ax,dx ; where isfile wants name ptr call isfile ; what kind of file is this? jc prnop3 ; c = no such file, create it test byte ptr filtst.dta+21,1fh ; file attributes, ok to write? jnz prnop2 ; nz = no mov al,1 ; writing mov ah,open2 ; open existing file int dos jc prnop2 ; c = failure mov prnhand,ax ; save file handle mov bx,ax ; handle for DOS mov ah,ioctl mov al,0 ; get info int dos or dl,20h ; turn on binary mode xor dh,dh mov ah,ioctl mov al,1 ; set info int dos mov cx,0ffffh ; setup file pointer mov dx,-1 ; and offset mov al,2 ; move to eof minus one byte mov ah,lseek ; seek the end int dos pop dx pop cx pop bx pop ax clc ret prnop2: pop dx pop cx pop bx pop ax stc ret prnop3: test filtst.fstat,80h ; access problem? jnz prnop2 ; nz = yes mov ah,creat2 ; file did not exist mov cx,20h ; attributes, archive bit int dos mov prnhand,ax ; save file handle pop dx pop cx pop bx pop ax ret ; may have carry set prnopen endp ; worker: copy line from si to di, first removing trailing spaces, second ; parsing out curly braced strings, then third converting \{b##} in strings ; to binary numbers. Returns carry set if error; else carry clear, with byte ; count in cx. Braces are optional but must occur in pairs. ; Items which cannot be converted to legal numbers are copied verbatium ; to the output string (ex: \{c} is copied as \{c} but \{x0d} is hex 0dh). ; '\\' is written as '\' ; Requires byte count in CX to handle embedded \0 bytes. cnvlin proc near push ax push si ; source ptr push di ; destination ptr push bp ; end of string marker push temp ; output length counter push es ; end of save regs push ds ; move ds into es pop es ; use data segment for es:di mov temp,di ; initial output position call cnvstr ; parse curly braces xor dx,dx ; initialize returned byte count cld mov bp,si add bp,cx ; point to last byte + 1 of string cnvln1: cmp si,bp ; at end of string? jae cnvlnx ; ae = yes, exit mov al,[si] ; look at starting byte cmp al,'\' ; numeric intro? jne cnvln3 ; ne = no mov atoi_cnt,cx ; length for atoi mov atoibyte,1 ; convert only one character call atoi ; deal with \numbers jnc cnvln4 ; nc = success, converted number cnvln3: lodsb ; reread from string literally xor ah,ah ; only one byte of output cnvln4: cld stosb ; save the char or ah,ah ; is returned number > 255? jz cnvln1 ; z = no, do more chars cnvln5: stosb ; save high order byte next jmp short cnvln1 ; do more chars cnvlnx: mov byte ptr [di],0 ; plant terminator clc ; clear c bit, success sub di,temp ; current - inital output position mov cx,di ; returned byte count to CX pop es ; restore regs pop temp pop bp pop di ; destination ptr pop si ; source ptr pop ax ret cnvlin endp ; Convert string by removing surrounding curly brace delimiter pair. ; Converts text in place. ; Enter with source ptr in si. ; Preserves all registers, uses byte tmp. 9 Oct 1987 [jrd] ; cnvstr proc near push ax push bx push dx push si ; save start of source string push di push es xor bx,bx ; output count, assume none mov dx,si ; source address jcxz cnvst4 ; z = nothing there push ds pop es ; set es to data segment cld mov di,si ; set destination address to source ; 2. Parse off curly brace delimiters mov bx,cx ; assume no changes to byte count cmp byte ptr [si],braceop ; opening brace? jne cnvst4 ; ne = no, ignore brace-matching code xor bx,bx ; count up output bytes inc si ; skip opening brace mov dl,braceop ; opening brace (we count them up) mov dh,bracecl ; closing brace (we count them down) mov tmp,1 ; we are at brace level 1 cnvst1: cld ; search forward lodsb ; read a string char stosb ; store char (skips opening brace) dec cx inc bx ; count bytes read and written or cx,cx ; at end of string? jle cnvst4 ; le = yes, we are done cmp al,dl ; an opening brace? jne cnvst2 ; ne = no inc tmp ; yes, increment brace level jmp short cnvst1 ; and continue scanning cnvst2: cmp al,dh ; closing brace? jne cnvst1 ; ne = no, continue scanning dec tmp ; yes, decrement brace level or cx,cx ; have we just read the last char? jnz cnvst3 ; nz = no, continue scanning mov tmp,0 ; yes, this is the closing brace cnvst3: cmp tmp,0 ; at level 0? jne cnvst1 ; ne = no, #opening > #closing braces dec bx ; don't count terminating brace mov byte ptr [di-1],0 ; plant terminator on closing brace cnvst4: pop es ; recover original registers pop di pop si pop dx mov cx,bx ; return count in cx pop bx pop ax ret cnvstr endp ; OPEN { READ | WRITE | APPEND } filespec vfopen proc near mov ah,cmkey ; get READ/WRITE/APPEND keyword mov dx,offset opntab ; keyword table xor bx,bx ; help call comnd jc vfopen1 ; c = failed mov temp,bx mov ah,cmword ; read filespec mov bx,offset rdbuf ; buffer for filename mov dx,offset vfophlp ; help call comnd jc vfopen1 ; c = failed mov ah,cmeol ; get end of line confirmation call comnd jnc vfopen2 mov kstatus,ksgen ; general cmd failure status vfopen1:ret ; error return, carry set vfopen2:mov kstatus,kssuc ; assume success status mov dx,offset rdbuf ; filename, asiiz mov bx,temp ; kind of open cmp bx,1 ; open for reading? jne vfopen4 ; ne = no ; OPEN READ cmp vfrhandle,0 ; is it open now? jge vfopen8 ; ge = yes, complain mov ah,open2 ; file open xor al,al ; 0 = open readonly cmp dosnum,300h ; at or above DOS 3? jb vfopen3 ; b = no, so no shared access or al,40h ; open readonly, deny none vfopen3:int dos jc vfopen9 ; c = failed to open the file mov vfrhandle,ax ; save file handle clc ret ; OPEN WRITE or APPEND vfopen4:cmp vfwhandle,0 ; is it open now? jge vfopen8 ; ge = yes, complain mov ax,dx ; filename for isfile call isfile ; check for read-only/system/vol-label/dir jc vfopen7 ; c = file does not exist test byte ptr filtst.dta+21,1fh ; the no-no file attributes jnz vfopen9 ; nz = do not write over one of these vfopen5:test filtst.fstat,80h ; access problem? jnz vfopen9 ; nz = yes, quit here test byte ptr filtst.dta+21,1bh ; r/o, hidden, volume label? jnz vfopen9 ; we won't touch these mov ah,open2 ; open existing file (usually a device) mov al,1+1 ; open for writing int dos jc vfopen9 ; carry set means can't open mov vfwhandle,ax ; remember file handle mov bx,ax ; file handle for lseek xor cx,cx xor dx,dx ; cx:dx = displacment xor al,al ; 0 means from start of file cmp temp,2 ; WRITE? means from start of file je vfopen6 ; e = yes, else APPEND mov al,2 ; move to eof vfopen6:mov ah,lseek ; seek the place int dos clc ret vfopen7:cmp temp,1 ; READ? je vfopen9 ; e = yes, should not be here mov ah,creat2 ; create file xor cx,cx ; 0 = attributes bits int dos jc vfopen9 ; c = failed mov vfwhandle,ax ; save file handle clc ; carry clear for success ret vfopen8:mov dx,offset vfoptwice ; trying to reopen a file mov ah,prstr int dos mov kstatus,kstake ; Take file failure status stc ret vfopen9:mov dx,offset vfopbad ; can't open, complain mov ah,prstr int dos mov dx,offset rdbuf ; filename call prtasz mov kstatus,kstake ; Take file failure status stc ret vfopen endp ; CLOSE {READ-FILE | WRITE-FILE} vfclose proc near mov tmp,al ; remember kind (1=READ, 2=WRITE) mov bx,vfrhandle ; READ FILE handle cmp al,1 ; READ-FILE? je vfclos1 ; e = yes cmp al,2 ; WRITE-FILE? jne vfclos3 ; ne = no, a mistake mov bx,vfwhandle ; write handle or bx,bx jl vfclos3 ; l = invalid handle mov dx,offset rdbuf xor cx,cx ; zero length push bx ; save handle mov ah,write2 ; set file high water mark int dos pop bx vfclos1:or bx,bx jl vfclos3 ; l = invalid handle mov kstatus,kssuc ; success status thus far mov ah,close2 ; close file int dos jc vfclos4 ; c = error cmp tmp,1 ; READ? jne vfclos2 ; ne = no, WRITE mov vfrhandle,-1 ; declare READ handle to be invalid clc ret vfclos2:mov vfwhandle,-1 ; declare WRITE handle to be invalid clc ret vfclos4:mov ah,prstr mov dx,offset vfclbad ; complain int dos vfclos3:mov kstatus,ksgen ; general cmd failure status stc ret vfclose endp ; READ-FILE variable name vfread proc near mov comand.cmper,1 ; do not react to '\%' in macro name mov comand.cmarray,1 ; allow sub in [..] of \& arrays mov ah,cmword mov bx,offset rdbuf+2 ; buffer for macro name mov word ptr rdbuf,0 mov dx,offset vfrdmsg call comnd ; get macro name jnc vfread1 ; nc = success mov kstatus,ksgen ; general cmd failure status ret ; failure vfread1:or ax,ax ; null entry? jnz vfread2 ; nz = no mov dx,offset vfrdbad ; more parameters needed mov ah,prstr int dos mov kstatus,ksgen ; general cmd failure status stc ret vfread2:push ax mov ah,cmeol ; get command confirmation call comnd pop ax ; ax is variable length jc vfreadx ; c = failed mov cx,cmdblen ; length of rdbuf sub cx,ax ; minus macro name dec cx ; minus space separator mov temp,0 ; leading whitespace and comment flgs mov di,offset rdbuf+2 ; destination buffer add di,ax ; plus variable name mov byte ptr [di],' ' ; space separator inc di ; put definition here mov bx,vfrhandle ; READ FILE handle or bx,bx ; check for valid handle jge vfread3 ; ge = valid mov ah,prstr mov dx,offset vfnofile ; say no file int dos vfreadx:mov kstatus,ksgen ; general cmd failure status stc ; failure return ret vfread3:push cx ; read from file mov kstatus,kssuc ; assume success status mov cx,1 ; read 1 char mov dx,di ; place here mov byte ptr [di],0 ; insert terminator mov ah,readf2 int dos pop cx jc vfreadx ; c = read failure or ax,ax ; count of bytes read jz vfread9 ; z means end of file mov al,[di] ; get the character cmp flags.takflg,0 ; echo Take files? je vfread3a ; e = no push ax mov ah,conout mov dl,al ; echo character int dos pop ax vfread3a:cmp al,CR ; CR? je vfread7 ; e = yes, ignore it cmp al,LF ; LF? je vfread8 ; e = yes, exit cmp byte ptr temp,0 ; seen non-spacing char yet? jne vfread6 ; ne = yes cmp al,' ' ; is this a space? je vfread7 ; e = yes, skip it cmp al,TAB ; or a tab? je vfread7 ; e = yes, skip it mov byte ptr temp,1 ; say have seen non-spacing char vfread6:inc di ; next storage cell vfread7:loop vfread3 ; loop til end of line vfread8:cmp byte ptr [di-1],'-' ; last printable is line-continuation? jne vfread10 ; ne = no, use this line as-is dec di ; point to hyphen mov cx,offset rdbuf+cmdblen ; end of rdbuf sub cx,di ; minus where we are now jle vfread10 ; le = no space remaining mov temp,0 ; leading whitespace and comment flgs jmp vfread3 ; read another line vfread9:mov kstatus,ksgen ; EOF, general command failure status vfread10:cmp flags.takflg,0 ; echo Take files? je vfread11 ; e = no mov ah,prstr mov dx,offset crlf ; for last displayed line int dos vfread11:mov byte ptr [di],0 ; insert final terminator mov dx,offset rdbuf+2 ; start counting from here push kstatus ; preserve status from above work call strlen ; cx becomes length call dodecom ; create the variable pop kstatus ; recover reading status jc vfread12 ; c = failure ret vfread12:mov kstatus,ksgen ; general command failure status mov ah,prstr mov dx,offset vfrbad ; say error while reading int dos stc ret vfread endp ; WRITE {FILE or log} text Write proc near mov ah,cmkey ; get kind of log file mov dx,offset writetab ; table of possibilities xor bx,bx ; help, when we get there call comnd jnc write1 ; nc = success mov kstatus,ksgen ; general cmd failure status ret write1: mov temp,bx ; save log file kind mov comand.cmdonum,1 ; \number conversion allowed mov comand.cmblen,cmdblen ; use full length mov ah,cmline ; get string to be written mov bx,offset rdbuf ; where to put text mov dx,offset msgtxt ; help call comnd jnc wpkt0 mov kstatus,ksgen ; general cmd failure status ret ; c = failure wpkt0: mov kstatus,kssuc ; success status thus far mov si,offset rdbuf ; start of text in buffer mov di,si ; convert in-place, to asciiz mov cx,ax ; length for cnvlin call cnvlin mov bx,temp ; log file kind jcxz wpkt2 ; z = no chars to write mov si,offset rdbuf cmp bx,logpkt ; Packet log? jne wses1 ; ne = no wpkt1: lodsb ; get byte to al call pktcpt ; write to packet log loop wpkt1 wpkt2: clc ; say success ret wses1: cmp bx,logses ; Session log? jne wtrn1 ; ne = no wses2: lodsb ; get byte to al call cptchr ; write to log loop wses2 ; do cx chars clc ; say success ret wtrn1: cmp bx,logtrn ; Transaction log? jne wtrn2 ; ne = no mov bx,tloghnd ; file handle jmp short wtrn4 wtrn2: cmp bx,80h ; WRITE SCREEN? jne wtrn3 ; ne = no xor bx,bx ; handle is stdout jmp short wtrn4 wtrn3: cmp bx,4002h ; WRITE FILE? jne wtrn5 ; ne = no, forget this mov bx,vfwhandle ; handle wtrn4: or bx,bx ; is handle valid? jl wtrn5 ; l = no mov dx,offset rdbuf mov ah,write2 ; write to file handle in bx int dos jc wtrn4a ; c = failure ret wtrn4a: mov ah,prstr mov dx,offset vfwbad ; say error while writing int dos mov kstatus,ksgen stc ret wtrn5: mov kstatus,ksgen ; general cmd failure status mov ah,prstr mov dx,offset vfnofile ; say no file int dos stc ret write endp code ends code1 segment assume cs:code1 ; Convert ascii strings of the form "\{bnnn}" to a binary dword in dx:ax. ; The braces are optional but must occur in pairs. Numeric base indicator "b" ; is O or o or X or x or D or d or missing, for octal, hex, or decimal (def). ; Enter with si pointing at "\". ; Returns binary value in ax with carry clear and si to right of "}" or at ; terminating non-numeric char if successful; otherwise, a failure, ; return carry set with si = entry value and first read char in al. ; Public byte atoibyte is used to control conversion of one character, if ; non-zero, or as many as can be parsed if zero. This routine always clears ; atoibyte upon exiting. ; atoi_err byte values, carry bit state, meaning ; 0 nc successfully converted, can accept more data ; 1 nc successfully converted and string is terminated ; 2 nc successfully converted with break char pointed to by SI ; 4 c insufficient bytes to resolve ; 8 c syntax error in expression, cannot convert atoi proc far push temp push temp1 push cx ; save working reg push si ; save entry si push bx push ax ; save read char xor ax,ax mov temp,ax mov temp1,ax mov atoi_err,al ; assume success and can read more xor bx,bx ; assume no opening brace cld mov cx,atoi_cnt ; bytes available to examine or cx,cx jnz atoi1 mov atoi_cnt,16 ; safety atoi1: call atoi_read ; read a byte or fail jnc atoi1a jmp atoix ; fail atoi1a: cmp al,' ' ; leading space? je atoi1 ; e = yes, read again, for do_math cmp al,'\' ; number introducer? jne atoi1b ; ne = no call atoi_read ; get next byte jnc atoi1b jmp atoix atoi1b: cmp al,braceop ; opening brace? jne atoi1c ; ne = no mov bl,bracecl ; remember a closing brace is needed call atoi_read ; get next byte jnc atoi1c jmp atoix atoi1c: call isnumbase ; number base introducer? jc atoi1d ; c = no call atoi_read ; get next byte jnc atoi2 ; nc = got a byte jmp atoix atoi1d: mov nbase,10 ; assume base 10 mov cx,3 ; and three digits per char atoi2: call cnvdig ; do we have a digit now? jnc atoi2a ; nc = yes mov atoi_err,8 ; syntax error, digit required jmp atoix atoi2a: dec si ; back over it for read below inc atoi_cnt mov ax,cx ; number base byte count mov cx,atoi_cnt ; available bytes cmp atoibyte,0 ; read as many bytes as possible? je atoi3 ; e = yes cmp ax,cx ; number base wants more than we have? ja atoi3 ; a = yes, use available bytes mov cx,ax ; limit loop to num base requirements mov atoi_err,1 ; presume successful and terminated atoi3: call atoi_read ; get a byte jc atoix ; c = failed call cnvdig ; convert ascii to binary digit jc atoi4 ; c = cannot convert, SI-1 is break xor ah,ah ; clear high order value cmp atoibyte,0 ; read as many bytes as possible? je atoi3b ; e = yes push ax ; test for fitting into byte result mov ax,temp ; current low order value mul nbase ; times number base add dx,ax ; to dx, plus any overflow pop ax add dx,ax ; next result would be in dx or dh,dh ; overflow beyond one byte jnz atoi4 ; nz = cannot accept this byte cmp cx,1 ; reading last byte? ja atoi3b ; a = no atoi3b: inc bh ; say we did a successful conversion push ax ; save this byte's value mov ax,temp1 mul nbase ; high order mov temp1,ax ; keep low order part of product mov ax,temp mul nbase ; low order add temp1,dx ; high order carry mov temp,ax ; low order pop ax ; recover binary digit add temp,ax adc temp1,0 loop atoi3 ; get more jmp short atoi4a ; out of data, don't backup ; here on break char in al atoi4: dec si ; backup to reread terminator inc atoi_cnt mov atoi_err,2 ; terminated on break char atoi4a: or bl,bl ; closing brace needed? jz atoi6 ; z = no, success so far atoi5: call atoi_read ; get a byte for brace jnc atoi5a cmp atoi_cnt,0 ; out of counts? je atoix ; e = yes atoi5a: cmp al,bl ; the closing brace? jne atoi5b ; ne = no mov atoi_err,1 ; success, terminator seen (brace) jmp atoi6 atoi5b: cmp al,' ' ; space padding? je atoi5 ; e = yes, skip it atoi5c: mov atoi_err,8 ; syntax error (failed to get brace) jmp atoix atoi6: or bh,bh ; did we do any conversion? jz atoix ; z = no pop ax ; throw away old saved ax pop bx ; restore bx pop ax ; throw away starting si, keep current pop cx ; restore old cx mov dx,temp1 mov ax,temp pop temp1 pop temp mov atoibyte,0 mov atoi_cnt,0 clc ; clear carry for success ret atoix: pop ax ; restore first read al pop bx pop si ; restore start value pop cx ; restore old cx pop temp1 pop temp xor dx,dx mov atoibyte,0 mov atoi_cnt,0 stc ; set carry for failure ret atoi endp ; Examine char in AL for being a number base indicator, O, X, D, in ; either case. ; If matched return carry clear, number base nbase set, and cx holding ; the qty of bytes in a single character result value. ; If not matched return carry set. ; AL is preserved. isnumbase proc near push ax ; try for number bases cmp al,'a' ; lower case? jb isnum1 ; b = no cmp al,'z' ; in range of lower case? ja isnum1 ; a = no and al,5fh ; map to upper case isnum1: cmp al,'O' ; octal? jne isnum2 ; ne = no mov nbase,8 ; set number base cmp atoibyte,0 ; all bytes? je isnum4 ; e = yes mov cx,3 ; three octal chars per final char jmp short isnum4 isnum2: cmp al,'X' ; hex? jne isnum3 ; ne = no mov nbase,16 cmp atoibyte,0 ; all bytes? je isnum4 ; e = yes mov cx,2 ; two hex chars per final char jmp short isnum4 isnum3: cmp al,'D' ; decimal? jne isnum5 ; ne = no, syntax error mov nbase,10 cmp atoibyte,0 ; all bytes? je isnum4 ; e = yes mov cx,3 isnum4: pop ax ; successful result clc ret isnum5: pop ax stc ; carry set for failure ret isnumbase endp ; Read a byte from ds:si. ; If success decrement byte atoi_cnt, inc si, return byte in AL, carry clear. ; If failure return carry set, no change to atoi_cnt, AL unchanged. atoi_read proc near cmp atoi_cnt,0 ; any bytes left? jg atoi_read1 ; g = yes mov atoi_err,4 ; c = insufficient bytes to resolve stc ; return failure ret atoi_read1: cld lodsb ; get first char dec atoi_cnt ; one less byte availble clc ret atoi_read endp ; worker for atoi cnvdig proc near ; convert ascii code in al to binary push ax ; return carry set if cannot cmp al,'a' ; lower case hexadecimal candidate? jb cnvdig1 ; b = no sub al,20h ; lower case hex to upper cnvdig1:cmp al,'A' ; uppercase hex jb cnvdig2 sub al,'A'-10-'0' ; 'A' becomes '10' cnvdig2:sub al,'0' ; remove ASCII bias jc cnvdigx ; c = out of range cmp al,byte ptr nbase ; out of range? jae cnvdigx ; ae = yes, out of range add sp,2 ; pop saved ax clc ; success, binary value is in al ret cnvdigx:pop ax ; return ax unchanged stc ; c set for failure ret cnvdig endp decout proc far ; display decimal number in ax push ax push cx push dx mov cx,10 ; set the numeric base call mvalout ; convert and output value pop dx pop cx pop ax ret decout endp mvalout proc near ; output number in ax using base in cx ; corrupts ax and dx xor dx,dx ; clear high word of numerator div cx ; (ax / cx), remainder = dx, quotient = ax push dx ; save remainder for outputting later or ax,ax ; any quotient left? jz mvalout1 ; z = no call mvalout ; yes, recurse mvalout1:pop dx ; get remainder add dl,'0' ; make digit printable cmp dl,'9' ; above 9? jbe mvalout2 ; be = no add dl,'A'-1-'9' ; use 'A'--'F' for values above 9 mvalout2:mov ah,conout int dos ret mvalout endp valout proc far ; output number in ax using base in cx ; corrupts ax and dx xor dx,dx ; clear high word of numerator div cx ; (ax / cx), remainder = dx, quotient = ax push dx ; save remainder for outputting later or ax,ax ; any quotient left? jz valout1 ; z = no call valout ; yes, recurse valout1:pop dx ; get remainder add dl,'0' ; make digit printable cmp dl,'9' ; above 9? jbe valout2 ; be = no add dl,'A'-1-'9' ; use 'A'--'F' for values above 9 valout2:mov ah,conout int dos ret valout endp ; Write binary number in AX as decimal asciiz to buffer pointer DS:DI. dec2di proc far ; output number in ax using base in cx push ax push cx push dx mov cx,10 call dec2di1 ; recursive worker pop dx pop cx pop ax ret dec2di1 proc near ; worker of dec2di xor dx,dx ; clear high word of numerator div cx ; (ax / cx), remainder = dx, quotient = ax push dx ; save remainder for outputting later or ax,ax ; any quotient left? jz dec2di2 ; z = no call dec2di1 ; yes, recurse dec2di2:pop dx ; get remainder add dl,'0' ; make digit printable mov [di],dl ; store char in buffer inc di mov byte ptr[di],0 ; add terminator ret dec2di1 endp dec2di endp ; Perform math. Reads argptr string, returns binary value in DX:AX. ; Expects pointer to string to be in domath_ptr and its length in domath_cnt. ; Note that value and operator lists are stored on the stack in positive ; order (increasing array values are at increasing addresses, not in push ; order). ; Returns carry set if error. domath proc far push bx push cx push si push di mov si,domath_ptr mov di,domath_cnt mov parendepth,0 ; parenthesis nesting depth call domath_main mov domath_msg,0 ; re-enable domath error messages jnc domathx1 mov domath_ptr,si mov domath_cnt,di domathx1: pop di pop si pop cx pop bx ret domath endp domath_main proc near push si push bp sub sp,size fevalst ; counters and lists mov bp,sp ; remember base of lists mov [bp].numcnt,0 ; no numbers in list mov [bp].opcnt,0 ; no operators in list mov [bp].numlist,0 ; returned value mov [bp].numlist[2],0 ; high order returned value inc parendepth ; recursion limit counter cmp parendepth,maxdepth ; called too many times? jbe domath0 ; be = no mov matherr,offset opmsg4 jmp domath_error ; a = too many parentheses domath0:call gettoken ; read a token from string jc domath_exit ; c = nothing present cmp bl,'O' ; operator? je domath1 ; e = yes cmp [bp].numcnt,2 ; have two numbers (num num)? jb domath0 ; b = no, get an operator mov matherr,offset opmsg1 ; missing operator jmp domath_error ; exit with grammar error ; have initial operator in AX, decide in/pre/post-fix style domath1:cmp al,'!' ; postfix? (number is on stack) jne domath2 ; ne = no call domath_calc1 ; do math on one arg, result on stack jc domath_error ; failure, exit jmp short domath0 ; done here, get next tokens ; prefix or infix? domath2:call gettoken ; get expected following number jc domath_error ; c = eof, unexpected, error cmp bl,'N' ; got the number? jne domath4 ; ne = no, operator domath3:call domath_la ; call lookahead proc ; c clear, bl = N for use current number on stack ; c clear, bl = O for have higher prec operator on stack & ax, delay ; c clear, bl = F for read end of file, no next operator ; c set, bl = F for math failure, best to exit jc domath_error ; failure on math cmp bl,'F' ; end of file? je domath_end ; e = eof, do remaining math cmp bl,'O' ; found higher prec op (delay)? je domath1 ; e = yes, do newest operator call domath_calc2 ; bl = 'N', number ready, do math jc domath_error ; c = failure cmp [bp].opcnt,0 ; any ops backlogged? jne short domath3 ; be = try again to reduce back math jmp domath0 ; start over ; Have op op. Is second op a prefix operator? domath4:cmp al,'~' ; prefix? je domath5 ; e = yes cmp al,'-' ; prefix, overload? je domath5 ; e = yes cmp al,'+' ; prefix, overload? jne domath_error ; ne = no, inconsistent syntax ; could be op number postfix so look at next token to see ; and if so reduce before applying prefix. domath5:call gettoken ; get expected number following prefix jc domath_end ; c = eof, do remaining math cmp bl,'O' ; operator? je domath_error ; e = yes, that makes op op op call domath_la ; do lookahead for postfix jc domath_error ; c = failure on math call domath_calc1 ; do prefix calculation now jc domath_error ; c = failure call popop ; get first operator to ax call pushop ; put it back as if just read mov bl,'O' ; mark as operator jmp short domath3 ; reanalyze from new operator ; end of data, finish up math & exit domath_end: ; do calcs remaining on stack cmp [bp].opcnt,0 ; operators remaining to be done? je domath_exit ; e = no call domath_calc2 ; finish math jc domath_error ; c = failure jmp short domath_end ; keep trying til no more operators domath_error: ; math errors seen, exit in error mov cx,1 ; set carry for error jmp short domath_exit1 domath_exit: ; only way out, restore stack xor cx,cx ; assume success, cx = error counter domath_exit1: dec parendepth ; nesting depth cmp [bp].numcnt,1 ; only one value remaning? je domath_exit2 ; e = yes, that's good jb domath_exit2a ; b = no numbers, empty arg jcxz domath_exit2 ; z = no error cmp domath_msg,0 ; msgs permitted? jnz domath_exit2a ; nz = no mov matherr,offset opmsg1 ; missing operator domath_exit2a: inc cx ; say error domath_exit2: cmp [bp].opcnt,0 ; used all operators? je domath_exit3 ; e = yes mov matherr,offset opmsg ; too many operators inc cx ; say error domath_exit3: ; all returns mov ax,[bp].numlist ; returned value mov dx,[bp].numlist[2] ; high order part mov sp,bp ; base of lists add sp,size fevalst ; counters and lists or cx,cx ; any errors? jz domath_exit4 ; z = no (clears carry bit) cmp domath_msg,0 ; ok to complain? jne domath_exit3a ; ne = no, keep quiet mov ah,prstr mov dx,matherr ; math error pointer or dx,dx ; if any jz domath_exit3a ; z = none push dx mov dx,offset crlf int dos pop dx int dos ; display error message domath_exit3a: xor ax,ax ; return zero on errors xor dx,dx stc ; set carry for fail domath_exit4: mov matherr,0 pop bp pop si ret ; all done. Result in DX:AX domath_main endp ; Worker for domath. Reads argptr, inc's it, dec's arglen. Recognizes ; numbers (with or without leading \), recognizes math operators and (). ; Returns numbers as binary in DX:AX and BL = 'N', ; returns operator and precedence in AL and AH with BL = 'O'. ; Returns Carry set and BL = 'F' if neither kind of token. ; Tolerates leading whitespace. ; Uses domath_ptr as source pointer, domath_cnt as source count. ; Array domath_tmp is owned by this routine as private space. gettoken proc near xor ax,ax ; no operator, lowest precedence xor dx,dx gettok0:cmp domath_cnt,0 ; anything to read? jg gettok1 ; g = yes mov bl,'F' ; F for end of file, failure stc ret gettok1:mov si,domath_ptr mov al,byte ptr [si] cmp al,' ' ; leading whitepace? jne gettok1a ; ne = no inc domath_ptr dec domath_cnt jmp short gettok0 gettok1a:jb gettok1b ; b = non-printable cmp al,127 jb gettok2 ; b = printable gettok1b:mov bl,'F' ; declare F for end of file, failure mov domath_cnt,0 ; truncate string clc ret gettok2:mov cx,domath_cnt mov atoi_cnt,cx call atoi ; convert DS:SI to number in DX:AX jc gettok5 ; c = failed to convert number mov cx,si sub cx,domath_ptr ; amount read sub domath_cnt,cx ; deduct that read mov domath_ptr,si ; break char call pushval ; push onto number stack mov bl,'N' ; say returning number ret ; may have carry bit set gettok5:mov si,domath_ptr lodsb ; reread non-numeric inc domath_ptr dec domath_cnt push di ; look for operators push es mov di,seg operators ; list of math operators mov es,di mov di,offset operators mov cx,operator_len ; length of list cld repne scasb ; look for match pop es je gettok6 ; e = found a match pop di cmp domath_msg,0 ; allowed to display messages? jne gettok5a ; ne = no push ax mov ah,prstr mov dx,offset crlf int dos mov dx,offset opmsg2 ; unknown symbol int dos pop ax mov dl,al ; unknown op mov ah,conout int dos mov ah,prstr mov dx,offset crlf int dos gettok5a: dec domath_ptr ; set to read on this symbol inc domath_cnt mov bl,'F' ; say returning parse failure stc ; fail ret gettok6:sub di,offset operators + 1 ; index into list mov ah,precedence[di] ; precedence to return pop di mov bl,'O' ; say returning operator cmp al,'(' ; start recursion? je gettok7 cmp al,')' ; closure? je gettok9 ; e = yes, it's an EOF here call pushop ; push operator on op stack clc ret gettok7:call domath_main ; '(', invoke new scan jnc gettok8 ; nc = succeeded ret ; c = return failure gettok8:call pushval ; push returned DX:AX mov bl,'N' ; report number ret ; may have carry bit set gettok9:mov bl,'O' stc ; ')' returns EOF ret gettoken endp ; Lookahead processor. Have ... on stack ; Return bl = N for return number present on stack, carry clear ; Return bl = O for return higher prec operator on stack, carry clear ; Return bl = F and carry clear for end of file ; Return bl = F and carry set for math failure (errors) domath_la proc near call gettoken ; expect operator or end of file jnc domath_la1 ; nc = success mov bl,'F' ; say eof clc ; carry clear for eof ret domath_la1: cmp bl,'O' ; lookahead operator? je domath_la2 ; e = yes mov bl,'F' ; fail, case of number number stc ; syntax error, signal quit ret domath_la2: mov si,[bp].opcnt ; get size of op list dec si ; zero based index or si,si ; sanity check, should be > 0 jnz domath_la2a ; nz = have current op available mov bl,'F' ; signal total failure stc ret domath_la2a: shl si,1 ; index words mov ax,[bp].oplist[si] ; lookahead operator mov cx,[bp].oplist[si-2] ; currently active operator cmp ah,ch ; lookahead versus current op ja domath_la3 ; a = lookahead is higher, delay call popop ; remove lookahead operator from stack dec domath_ptr ; set to reread operator inc domath_cnt mov bl,'N' ; signal number ready on stack clc ret domath_la3: ; delay cmp al,'!' ; postfix on number? jne domath_la5 ; ne = no, another operator, delay call domath_calc1 ; combine number!, report number jnc domath_la4 ; nc = success, use number on stack mov bl,'F' ; report failed ret ; carry set domath_la4:mov bl,'N' ; report number is on stack clc ; say success ret domath_la5:mov bl,'O' ; lookahead (delayed op) is on stack and ax clc ; bl = 'O', signal delay and lookahead op ret ; is on the stack domath_la endp ; Calculate results of top operator and one top value. ; Return value on top of the value stack. ; Returns carry set if failure ; Pops one operator, and zero (failure) or one value from stack before ; starting. domath_calc1 proc near mov si,[bp].opcnt ; count of operators cmp si,0 ; any? jg dom_c1_1a ; g = yes mov matherr,offset opmsg3 stc ; fail ret dom_c1_1a: cmp [bp].numcnt,0 ; one or more values? jne dom_c1_1b ; ne = yes mov matherr,offset opmsg ; too few values stc ret dom_c1_1b: call popop ; get top operator from list to AX mov mathkind,al ; operation to do call popval ; get first value from list to DX:AX cmp mathkind,'!' ; factorial? jne dom_c1c ; ne = no mov bx,ax ; save argument mov cx,ax mov ax,-1 ; default for too large an arg mov dx,ax cmp cx,10 ; largest within 32 bits? ja dom_c1b ; a = too large, report -1 mov ax,1 ; default for 0! and 1! xor dx,dx sub cx,ax jle dom_c1b ; le = special cases of 0 and 1 mov ax,bx ; recover argument dom_c1a:mul cx loop dom_c1a dom_c1b:jmp dom_c1_1x dom_c1c:cmp mathkind,'~' ; logical not? jne dom_c1d ; ne = no not ax ; 1's complement not dx jmp dom_c1_1x dom_c1d:cmp mathkind,'-' ; negate? jne dom_c1e ; ne = no neg dx neg ax ; change sign sbb dx,0 jmp dom_c1_1x dom_c1e:cmp mathkind,'+' ; unary '+'? je dom_c1_1x ; e = yes dom_c1f:stc ; unknown operator mov matherr,offset opmsg2 ret dom_c1_1x: call pushval ret ; may have carry bit set domath_calc1 endp ; Calculate results of top operator and two top values. ; Return value on top of the value stack. ; Returns carry set if failure ; Pops one operator, and one (failure) or two values from stack before ; starting. domath_calc2 proc near cmp [bp].numcnt,2 ; two or more args? jb domath_calc1 ; b = no, do 1 arg math call popop ; get top operator from list to AX jnc dom_c2_2a ; nc = success mov matherr,offset opmsg3 ret ; fail dom_c2_2a: mov mathkind,al ; operation to do call popval ; get first value from list to DX:AX mov bx,ax ; right side, low order mov cx,dx ; right side, high order call popval ; first (left side) to dx:ax cmp mathkind,'^' ; raise arg1 to power of arg2? jne dom_c2_6 ; ne = no mov cx,bx ; use low order of exponent dec cx ; cmp cx,31 ; largest exponent we allow jbe dom_c2_1 ; be = ok mov ax,-1 ; report -1 as overflow result cwd jc dom_c2_2y ; c = overflow error dom_c2_1:or cl,cl ; zero power? jne dom_c2_2 ; ne = no mov ax,1 ; report 1 xor dx,dx jmp dom_c2_2x dom_c2_2:push si push di mov si,ax ; original number mov di,dx dom_c2_3:push cx ; save loop counter mov cx,di mov bx,si ; original number call lmul ; use long multiply pop cx jc dom_c2_4 ; c = overflow loop dom_c2_3 pop di pop si jmp dom_c2_2x dom_c2_4:pop di pop si stc jmp dom_c2_2y ; failure from overflow dom_c2_6:cmp mathkind,'*' ; times? jne dom_c2_7 ; ne = no call lmul ; do 32 bit signed multiply jc dom_c2_2y ; c = overflow error jmp dom_c2_2x dom_c2_7:cmp mathkind,'/' ; divide? jne dom_c2_8 ; ne = no call ldiv jc dom_c2_2y ; c = overflow error jmp dom_c2_2x dom_c2_8:cmp mathkind,'%' ; modulo? jne dom_c2_9 ; ne = no call ldiv ; divide jc dom_c2_2y ; c = overflow error xchg cx,dx ; get remainder to dx:ax xchg bx,ax jmp dom_c2_2x dom_c2_9:cmp mathkind,'&' ; logical AND? jne dom_c2_11 ; ne = no and ax,bx and dx,cx jmp dom_c2_2x dom_c2_11:cmp mathkind,'+' ; addition? jne dom_c2_12 ; ne = no dom_c2_11a: push si mov si,dx ; get sign bit xor si,cx ; different? if so then 1 add ax,bx adc dx,cx test si,8000h ; check for case of same start signs jnz dom_c2_11b ; nz = different signs, no overflow mov si,dx xor si,cx ; different test si,8000h ; different signs now? pop si jnz dom_c2_2y ; nz = yes, overflow jmp dom_c2_2x dom_c2_11b:pop si jmp dom_c2_2x dom_c2_12:cmp mathkind,'-' ; subtraction? jne dom_c2_13 ; ne = no neg cx neg bx ; change sign of right side number sbb cx,0 jmp short dom_c2_11a ; now do addition with bounds check dom_c2_13:cmp mathkind,'|' ; logical OR? jne dom_c2_14 ; ne = no or ax,bx or dx,cx jmp dom_c2_2x dom_c2_14:cmp mathkind,'#' ; exclusive OR? jne dom_c2_15 ; ne = no xor ax,bx xor dx,cx jmp dom_c2_2x dom_c2_15:cmp mathkind,'@' ; GCD? jne dom_c2_16 ; ne = no call lgcd ; call the gcd routine jc dom_c2_2y ; c = overflow error jmp dom_c2_2x dom_c2_16:stc ret ; unknown operator dom_c2_2x: call pushval ; store result ret dom_c2_2y: call pushval ; store result (typically -1) mov matherr,offset opmsg3 stc ; say failure of math ret domath_calc2 endp ; Push operator (AL) and precedence (AH) onto the operator stack. ; Increments [bp].opcnt. ; Return carry set if no room. pushop proc near mov si,[bp].opcnt ; count of operators cmp si,listlen ; list full? jae pushop1 ; ae = yes, fail shl si,1 ; words mov [bp].oplist[si],ax ; save operator (AL) and preced (AH) inc [bp].opcnt ; one more operator in the list clc ; success ret pushop1:mov matherr,offset opmsg1 stc ; fail ret pushop endp ; Pop current opcode from op stack to AX. Decrements [bp].opcnt. ; Returns carry set if no operators are available popop proc near mov si,[bp].opcnt ; count of operators in list or si,si ; any? jz popop1 ; z = no, exit dec si ; index from zero shl si,1 ; words mov ax,[bp].oplist[si] ; get lookahead (last) operator dec [bp].opcnt clc ; success ret popop1: mov matherr,offset opmsg1 stc ; failed to op (empty list) ret popop endp ; Push value in DX:AX onto value stack. Increments [bp].numcnt. ; Returns carry set if not enough space. pushval proc near mov si,[bp].numcnt cmp si,listlen ; at limit of list? ja pushval1 ; a = exceeded list length, fail shl si,1 ; address dwords shl si,1 mov [bp].numlist[si],ax ; push dx:ax onto value stack mov [bp].numlist[si+2],dx ; high order part inc [bp].numcnt ; occupancy is one greater clc ; succeed ret pushval1:stc ; fail ret pushval endp ; Pop top value from value stack into DX:AX. Decrements [bp].numcnt. ; Returns carry set if no value were available. popval proc near mov si,[bp].numcnt ; count of values in list or si,si ; count jz popval1 ; z = none dec si ; index from 0 shl si,1 ; address dwords shl si,1 mov ax,[bp].numlist[si] ; pop top value to dx:ax mov dx,[bp].numlist[si+2] ; high order part dec [bp].numcnt ; occupancy is one less clc ; success ret popval1:stc ; fail ret popval endp ; end of domath procedures ; Perform 32 bit division. Numerator is in DX:AX, denominator in CX:BX. ; Returns quotient in DX:AX, remainder in CX:BX. Carry set and -1 on ; divide by zero. ldiv proc far push temp push temp1 push si push di mov tmp,0 ; holds final sign (0=positive) or dx,dx ; numerator is negative? jge ldiv1 ; ge = no neg dx neg ax sbb dx,0 ; change sign xor tmp,1 ; remember to change sign later ldiv1: or cx,cx ; denominator is negative? jge ldiv2 ; ge = no neg cx neg bx sbb cx,0 ; change sign xor tmp,1 ; remember to change sign later ldiv2: mov di,cx ; denominator, hold here mov si,bx cmp dx,cx ; is numerator larger than denom? jne ldiv10 ; ne = yes, and 32 bits too or dx,dx ; 32 bit number? jnz ldiv10 ; nz = yes ; only 16 bit numbers here or bx,bx ; denominator zero? jnz ldiv3 ; nz = no jmp ldivf ; overflow, report failure ldiv3: div bx ; regular signed division mov bx,dx ; remainder xor dx,dx ; high quotient xor cx,cx ; high remainder jmp ldivx ; exit success ; 32 bit numbers here ldiv10: mov tmp1,0 ; shift counter mov temp,0 ; shift accumulator, low part mov temp1,0 ; shift accumulator, high part or cx,cx ; check for zero denominator jnz ldiv11 ; nz = not zero or bx,bx jz ldivf ; zero, exit failure ldiv11: cmp dx,di ; top vs bottom high order words jb ldiv13 ; b = top smaller than bottom ja ldiv12 ; a = top larger than bottom ; high order words are the same cmp ax,si ; top vs bottom low order words jb ldiv13 ; b = top eq or larger than bottom je ldiv15 ; e = top equals bottom ; top is larger than bottom ldiv12: or di,di ; can shift left further? js ldiv14 ; s = no, accumulate and back down inc tmp1 ; remember doing left shift shl si,1 ; shift denominator left one bit rcl di,1 ; include carry-out from low half jmp short ldiv11 ; compare again til top < bot ; either we start with top < bottom (tmp1 = 0), or we have left shifted ; the bottom enough (tmp1 > 0) to create that condition. ; top is smaller than or equal to bottom ldiv13: cmp tmp1,0 ; any shifts remaining? je ldiv16 ; e = no ; If tmp1 is 0 then we are done, no further adjustments can be made. ; Else back off tmp1 by one position dec tmp1 ; shift right by 1 to get top >= bot shr di,1 rcr si,1 ldiv14: cmp dx,di ; is top less than bottom? jb ldiv13 ; b = yes, shift again ja ldiv15 ; a = no, greater than cmp ax,si ; low order part jb ldiv13 ; b = top is less than bottom ldiv15: sub ax,si ; (top - bottom) to get remainder sbb dx,di call ldiv_acc ; accumulate shifted success jmp short ldiv14 ; try again til top < bot ldiv16: mov bx,ax ; remainder to cx:bx mov cx,dx ; quotient to dx:ax mov ax,temp ; extract accumlated shifts mov dx,temp1 ; and exit success ; successful exit, update signs ldivx: cmp tmp,0 ; need to adjust signs? je ldivx1 ; e = no neg dx neg ax ; change sign of quotient sbb dx,0 neg cx neg bx ; change sign of remainder sbb cx,0 ldivx1: pop di pop si pop temp1 pop temp clc ; success ret ldivf: mov ax,-1 ; failure cwd ; report -1 mov bx,ax mov cx,ax pop di ; failure exit pop si pop temp1 pop temp stc ; say failure ret ldiv_acc proc near ; add current shift to accumulator push cx push ax push dx mov cl,tmp1 ; shift bit count xor ch,ch mov ax,1 ; value = 2 ** temp1 xor dx,dx jcxz ldiv_acc2 ; z = nothing to do ldiv_acc1:shl ax,1 rcl dx,1 loop ldiv_acc1 ldiv_acc2:add temp,ax ; accumulate shifts adc temp1,dx pop dx pop ax pop cx ret ldiv_acc endp ldiv endp ; Multiplies 32 bit values in DX:AX by CX:BX and returns the result in ; DX:AX. Overflows result in carry set and -1 as the answer. Signed ; arithmetic is used here, beware. lmul proc far push si push di push bx push cx push temp mov temp,0 ; zero means no sign change at end or dx,dx ; negative? jns lmul1 ; ns = no neg ax ; make positive neg dx sbb dx,0 xor temp,1 ; sign change needed lmul1: or cx,cx ; ditto for cx:bx jns lmul2 neg bx neg cx sbb cx,0 xor temp,1 lmul2: or cx,cx ; check for high word in both numbers jz lmul3 ; z = bottom does not have it or dx,dx jnz lmul4 ; nz = both have parts too large lmul3: push dx push ax mul bx ; low bottom time whole top mov di,dx mov si,ax ; regular product to di:si pop ax pop dx ; recover normal top push dx push ax mov ax,dx mul bx add di,ax pop ax pop dx ; recover normal top js lmul4 ; s = overflow in high accumlator mul cx ; high bottom times regular top add di,ax ; high reg product plus new product jns lmul5 ; ns = no overflow in high accumulator lmul4: mov ax,-1 ; overflow, yield minus one as answer cwd ; extend sign to dx mov temp,0 ; no sign change stc ; carry set for problem jmp short lmul6 lmul5: mov dx,di ; results to dx:ax mov ax,si cmp temp,0 ; sign change needed? je lmul6 ; e = no neg dx ; flip output sign neg ax sbb dx,0 clc ; carry clear for no problem lmul6: pop temp pop cx pop bx pop di pop si ret lmul endp ; Greatest common divisor, 32 bit (removes signs). Inputs are DX:AX and ; CX:BX. Results are in DX:AX. Carry bit set and -1 returned if error. lgcd proc far push bx push cx or dx,dx ; negative? jns lgcd1 ; ns = no neg ax ; make positive neg dx sbb dx,0 lgcd1: or cx,cx ; negative? jns lgcd2 ; ns = no neg bx neg cx ; make positive sbb cx,0 lgcd2: cmp dx,cx ; first arg same/larger than second? ja lgcd4 ; a = yes jb lgcd3 ; b = smaller cmp ax,bx ; low order parts jae lgcd4 ; ae = larger lgcd3: xchg dx,cx ; make largest the top number dx:ax xchg ax,bx lgcd4: push bx ; preserve smaller number push cx call ldiv ; large / small jc lgcd6 ; c = divide by zero mov dx,cx ; temp spot or dx,bx ; remainder? jz lgcd5 ; z = no, small is the answer pop dx ; new values, new top is old bot pop ax ; bottom is new remainder jmp short lgcd2 ; repeat calculation lgcd5: pop dx ; recover smaller number from stack pop ax ; result in dx:ax pop dx pop cx clc ret ; success lgcd6: pop ax pop ax ; clean stack pop dx pop cx mov ax,-1 ; dx:ax = -1 on error cwd stc ret lgcd endp ; Convert ASCIIZ string in DS:SI of form hh:mm:ss to ASCIIZ string seconds ; in ES:DI. tod2secs proc far mov temp,0 mov temp1,0 xor dx,dx ; dh=field counter, dl=read byte xor bx,bx ; three groups possible tod2s1: mov dl,[si] ; get a char inc si ; next char inc dh ; count char in field or dl,dl ; null terminator? jz tod2s3 ; z = yes, wrap calculations and quit cmp dl,':' ; field separator? je tod2s3 ; e = a separator, step fields sub dl,'0' ; remove ascii bias cmp dl,9 ja short tod2s6 ; a = failure to get expected digit cmp dh,2 ; more than two bytes in this field ja tod2s6 ; a = yes, fail translation mov al,bh ; get sum to al mov ah,10 mul ah ; sum times ten add al,dl ; sum = 10 * previous + current mov bh,al ; current sum mov ah,60 or bl,bl ; doing hours? jne tod2s2 ; ne = no, min, sec mov ah,24 ; max for hours field tod2s2: cmp bh,ah ; more than legal? jae tod2s6 ; ae = illegal jmp short tod2s1 ; continue analysis tod2s3: mov al,bh ; current sum xor ah,ah mov cx,1 cmp bl,2 ; seconds? je tod2s4 ; e = yes mov cx,60 or bl,bl ; hours? jne tod2s4 ; ne = no mov cx,60*60 ; seconds per hour tod2s4: mul cx add temp,ax adc temp1,dx xor bh,bh ; bh = current field sum xor dh,dh ; dh = counter of bytes in field cmp byte ptr [si-1],0 ; ended on null? je tod2s5 ; e = yes, end of conversion inc bl ; point to next field cmp bl,2 ; last field to use (secs) jbe tod2s1 ; be = get more text tod2s5: mov ax,temp mov dx,temp1 push di mov di,offset tmpstring ; build ASCII in this buffer call flnout pop di mov dx,offset tmpstring mov si,dx call strlen cld rep movsb ; copy string to es:di area mov byte ptr es:[di],0 ; and null terminator clc ; carry clear for success ret tod2s6: mov byte ptr es:[di],0 stc ; carry set for illegal value ret tod2secs endp code1 ends end