NAME MSXRMX ; Generic MS DOS Kermit module $INCLUDE(msdefs.h86) public serini, serrst, clrbuf, outchr, coms, vts, dodel, bddat, inbaud public ctlu, cmblnk, locate, lclini, prtchr, dobaud, clearl, dstflg public dodisk, getbaud, beep, rdbaud, dstmbx, srcptr, source public count, xofsnt, puthlp, putmod, clrmod, poscur, savsi, portatt public sendbr, term, machnam, setktab, setkhlp, showkey, hangup false equ 0 true equ 1 ; external variables used: ; drives - # of disk drives on system ; flags - global flags as per flginfo structure defined in pcdefs ; trans - global transmission parameters, trinfo struct defined in pcdefs ; portval - pointer to current portinfo structure (currently either port1 ; or port2) ; port1, port2 - portinfo structures for the corresponding ports ; global variables defined in this module: ; xofsnt, xofrcv - tell whether we saw or sent an xoff. datas segment public 'datas' public prttab, sintok, soutok extrn drives:byte, flags:byte, trans:byte, citok:word, cotok:word extrn portval:word, port1:byte, port2:byte, packet:byte extrn mbox:word, tmbox:word, sigpair:word, sematok:word machnam db 21 dup(' ') ; filled in by rmxkerm erms40 db cr,lf,'?Warning: Unrecognized baud rate',cr,lf,'$' erms60 db cr,lf,':COM2: not found' db cr,lf,'use RUN ATTACH. . .AS COM2. . .$' db cr,lf,'& try again$' badbd db cr,lf,'Unimplemented baud rate$' noimp db cr,lf,'Command not implemented.$' shkmsg db 'Not implemented.' shklen equ $-shkmsg setktab db 0 setkhlp db 0 crlf db cr,lf,'$' delstr db BS,' ',BS,'$' ; Delete string. [21d] clrlin db cr ; Clear line (just the cr part). clreol db ESC,'[0K' ; Clear line. clscr db ESC,'[2J' ; clear screen cursor db ESC,'[' ; position cursor row dw ? ; part db ';' ; of col dw ? ; above db 'H' ; last of above mode db ESC,'[24;1H',ESC,'[0K' ; put cursor on mode line & clear it model db 80 dup(?) ; mode line contents huosc db ESC,']M:H',ESC,'\' ; hangup OSC temp db ? ; buffer for term xofsnt db 0 ; Say if we sent an XOFF. xofrcv db 0 ; Say if we received an XOFF. com2 db 6,':COM2:' ; Entries for choosing communications port. [19b] comptab db 04H db 01H,'1$' dw 01H db 01H,'2$' dw 00H db 04H,'COM1$' dw 01H db 04H,'COM2$' dw 00H ourarg termarg <> even count dw 0ffffh ; set to keep MSCOMM out of the XON/XOFF act savsi dw source ; points to next char in buffer srcptr dw source ; points to next quadrant of buffer prttab dw com2,0 ; RMXKERM fills in 2nd word sintok dw 0 soutok dw 0 nucom2 dw 0 portatt dw 4,4,0ffh,119h ; serial I/O port attributes for rqaspecial inbaud dw 0,1 ; part of above termatt dw 2,2,2 dup (?) ; terminal port attributes for special getptat dw 3,3,2 dup(?) ; structure to read baud rate getinbd dw ? ; part of above dbufno dw 0 ; serial output buffer: 0=dest1, 2=dest2 dstptr dw dest1,dest2 ; indexed by dbufno dstmbx dw 2 dup (?) ; indexed by dbufno. filled in by RMXKERM dstflg dw 2 dup (0) ; indexed by dbufno. 0 means don't waitio dest1 db 256 dup(?) ; serial output buffer dest2 db 256 dup(?) ; serial output buffer dest db 2 dup (?) ; outchr's buffer ; This table is indexed by portinfo.baud. Unsupported baud rates contain 0. bddat label word dw 0 ; 45.5 baud N/A dw 50 ; 50 baud dw 75 ; 75 baud dw 110 ; 110 baud dw 0 ; 134.5 baud N/A dw 150 ; 150 baud dw 300 ; 300 baud dw 600 ; 600 baud dw 1200 ; 1200 baud dw 1800 ; 1800 baud dw 2000 ; 2000 baud dw 2400 ; 2400 baud dw 4800 ; 4800 baud dw 9600 ; 9600 baud dw 19200 ; 19200 baud dw 38400 ; 38400 baud source db bufsiz dup(0) ; serial input buffer datas ends code segment public 'code' extrn comnd:near, dopar:near, prserr:near extrn delcon:near, crfile:near, opfile:near, reader:near, writer:near extrn aspcl:near, prstr_:near, outpkt:near, awrite:near extrn waitio:near, sendms:near, aopen:near, special:near assume cs:code,ds:datas PATCH: mov bx,offset outpkt_ ; used as data by lclini to replace jmp bx ; MSCOMM's outpkt with our's ; This is the HANGUP command. It drops DTR momentarily, if the serial board ; and its driver support modem control. HANGUP PROC call serini ; ensure port's initialized mov ax,soutok ; enable OSC's on output mov bx,offset portatt and word ptr[bx+4],0bfh mov cx,5 assume cs:cgroup call aspcl mov ax,soutok ; send hangup OSC mov bx,offset huosc mov byte ptr[bx+4],'H' mov cx,size huosc mov dx,tmbox call awrite mov ax,soutok ; wait for it mov bx,tmbox call waitio sub cx,cx mov ax,soutok mov bx,offset huosc HU1: mov byte ptr[bx+4],'A' loop HU1 ; kill time mov cx,size huosc ; send answer OSC mov dx,tmbox call awrite mov ax,soutok ; wait for it mov bx,tmbox call waitio mov ax,soutok ; disable OSC's on output mov bx,offset portatt or word ptr[bx+4],40h mov cx,5 call aspcl assume cs:code call serrst ; reset port jmp RSKP HANGUP ENDP ; this is called by Kermit initialization. It checks the ; number of disks on the system, sets the drives variable ; appropriately. Returns normally. DODISK PROC NEAR mov drives,1 ; 1 "disk". It's $, which can be reattachfiled ret DODISK ENDP ; Clear the input buffer. This throws away all the characters in the ; serial interrupt buffer. This is particularly important when ; talking to servers, since NAKs can accumulate in the buffer. ; Do nothing since we are not interrupt driven. Returns normally. CLRBUF PROC NEAR push es mov ax,ds mov es,ax cli cld mov di,savsi ; scan from current ptr mov cx,offset source+size source ; to end of buffer for NUL sub cx,di sub al,al repne scasb je CLRB1 ; jump if found mov di,offset source ; scan from the beginning, if not mov cx,size source repne scasb CLRB1: sti dec di mov savsi,di ; cuurent ptr points to NUL pop es ret CLRBUF ENDP ; Put the char in AH to the serial port. This assumes the ; port has been initialized. Should honor xon/xoff. Skip returns on ; success, returns normally if the character cannot be written. outchr proc near push bx mov al,ah call dopar ; set parity, as req'd mov dest,al mov bx,offset dest ; write it mov cx,1 sub dx,dx assume cs:cgroup call awrite assume cs:code pop bx jcxz OUTCH1 ; OK return ret ; NG return OUTCH1: jmp rskp outchr endp ; Set the baud rate for the current port, based on the value ; in the portinfo structure. Returns normally. DOBAUD PROC NEAR mov bx,portval ; see if new baud rate is OK mov si,[bx].baud shl si,1 mov cx,bddat[si] jcxz DOBD1 ; nope call serrst ; yeah, force serini to set it ret DOBD1: mov [bx].baud,ax ; restore previous baud rate mov ah,prstr ; send bad baud msg mov dx,offset badbd int dos ret DOBAUD ENDP ; Get the current baud rate from the serial card and set it ; in the potinfo structure for the current port. Returns normally. ; This is used during initialization. GETBAUD PROC NEAR ret ; It's all taken care of, so what's to do? GETBAUD ENDP ; Use for DOS 2.0 and above. Check the port status. If no data, skip ; return. Else, read in a char and return. PRTCHR PROC NEAR cmp xofsnt,true ; if XOF has been sent, je PRTCH4 ; see if there's room in buffer for XON PRTCH1: mov si,savsi ; current serial input buffer ptr cld lodsb test al,al ; got one? jz PRTCH6 ; nope PRTCH2: cmp si,offset source+size source ; edge of the universe? jl PRTCH3 ; no mov si,offset source ; yeah, point to beginning PRTCH3: mov savsi,si ; update current ptr ret ; got one return PRTCH4: mov ax,savsi sub ax,srcptr ; if next quadrant is above current ptr, js PRTCH5 ; there's room to XON cmp ax,bufsiz/4 ; if there's not a clear quadrant below, jl PRTCH1 ; there isn't PRTCH5: mov bx,portval ; send XON mov ax,[bx].flowc call outchr nop nop nop mov xofsnt,false ; reset jmp PRTCH1 PRTCH6: jmp RSKP ; no data return PRTCHR ENDP ; Send a break out the current serial port. Returns normally. SENDBR PROC NEAR mov ah,prstr ; send intel's appologies mov dx,offset noimp ; for not having OS support int dos ; for their hardware's capabilities ret SENDBR ENDP ; Clear to the end of the current line. Returns normally. CLEARL PROC NEAR mov bx,offset clreol ; send ESC[0K SCRO1: mov cx,4 ; to clear to end of line SCRO2: mov ax,cotok push es push si assume cs:cgroup call writer assume cs:code pop si pop es ret CLEARL ENDP ; This routine blanks the screen. Returns normally. CMBLNK PROC NEAR mov bx,offset clscr ; send ESC[2J for clear screen jmp SCRO1 CMBLNK ENDP ; Position the cursor according to contents of DX: ; DH contains row, DL contains column. Returns normally. POSCUR PROC NEAR add dx,101h ; KERMIT starts w/0, RMX w/1 mov al,dh ; convert row to decimal sub ah,ah mov cl,10 div cl add ax,'00' ; covert to ASCII mov row,ax ; stash in row portion of escape sequence mov al,dl ; convert column to decimal sub ah,ah div cl add ax,'00' ; convert to ASCII mov col,ax ; to column portion of escape sequence mov bx,offset cursor mov cx,8 ; send ESC[row;colH to position cursor jmp SCRO2 POSCUR ENDP ; Move the cursor to the left margin, then clear to end of line. ; Returns normally. CTLU PROC NEAR mov bx,offset clrlin ; send CR,ESC[0K to clear entire line mov cx,5 jmp SCRO2 CTLU ENDP ; Homes the cursor. Returns normally. LOCATE PROC NEAR mov dx,0 ; Go to top left corner of screen. jmp poscur LOCATE ENDP ; Write a line at the bottom of the screen... ; the line is passed in dx, terminated by a $. Returns normally. putmod proc near mov bx,es ; save es mov ax,ds mov es,ax mov di,dx mov cx,0ffffh ; compute # bytes in line mov al,'$' repne scasb neg cx add cx,0fffeh mov si,dx mov di,offset model mov dx,cx ; save # bytes rep movsb ; move line after mode line escape sequence mov cx,length mode ; mode line escape sequence length add cx,dx ; plus line length mov es,bx ; restore es mov bx,offset mode ; write line prefixed w/escape sequence jmp SCRO2 putmod endp ; clear the mode line written by putmod. Returns normally. clrmod proc near mov bx,offset mode ; mode line escape sequence mov cx,length mode ; ESC[24;1HESC[0K jmp SCRO2 ; clears mode line clrmod endp ; Put a help message on the screen. ; Pass the message in ax, terminated by a null. Returns normally. puthlp proc near push ax ; preserve this mov ah,prstr mov dx,offset crlf int dos pop si ; point to string again puthl3: lodsb ; get a byte cmp al,0 ; end of string? je puthl4 ; yes, stop mov dl,al mov ah,dconio int dos ; else write to screen jmp puthl3 ; and keep going puthl4: mov ah,prstr mov dx,offset crlf int dos ret puthlp endp ; Delete a character from the terminal. This works by printing ; backspaces and spaces. Returns normally. DODEL PROC NEAR mov ah,prstr mov dx,offset delstr ; Erase weird character. int dos ret DODEL ENDP ; Set the current port. COMS PROC NEAR call serrst ; reset the old port mov dx,offset comptab mov bx,0 mov ah,cmkey call comnd ; parse for the new one jmp r push bx ; save port # mov ah,cmcfm call comnd ; Get a confirm. jmp short comx ; Didn't get a confirm. nop pop cx ; get port # mov ax,nucom2 or ax,cx ; is it COM2 and the first time jnz COMS2 ; neither mov di,prttab ; yes, create it assume cs:cgroup call crfile assume cs:code jcxz COMS1 ; OK mov dx,offset erms60 ; no luck, bitch mov ah,prstr int dos ret COMS1: mov sintok,ax ; open COM2 mov bx,1 assume cs:cgroup call aopen assume cs:code call rdbaud ; read it's baud rate mov port2.baud,cx ; memorize it call serrst ; reset COM2 inc nucom2 ; set flag to prevent deja vu sub cx,cx ; 0 means COM2 COMS2: mov flags.comflg,cl ; remember port # mov ax,offset port2 jcxz COMS3 ; is it COM2 mov ax,offset port1 ; nope, COM1 COMS3: mov portval,ax ; set portinfo ptr ret COMX: pop bx ret COMS ENDP ; Reads serial port's baud rate. Returns baud rate index in cx if recognizable ; by KERMIT, if not, cx=0ffffh. rdbaud proc near push es mov ax,sintok ; use aspecial function 4 to read baud rate mov bx,offset getptat mov cx,4 assume cs:cgroup call aspcl mov ax,ds mov es,ax mov ax,getinbd ; search bddat table for it mov cx,baudsiz mov di,offset bddat+2*(baudsiz-1) std repne scasw cld je RDB1 ; found it, cx=index mov dx,offset erms40 call prstr_ ; not found, say so mov cx,0ffffh ; return unrecognizable RDB1: pop es assume cs:code ret rdbaud endp ; Set heath emulation on/off. VTS PROC NEAR jmp notimp VTS ENDP notimp: mov ah,prstr mov dx,offset noimp int dos jmp prserr ; Initialize variables to values used by the generic MS DOS version. lclini: mov flags.vtflg,0 ; Don't do terminal emulation. mov ax,word ptr patch ; Substitute our outpkt for MSCOMM's mov word ptr outpkt,ax mov ax,word ptr patch+2 mov word ptr outpkt+2,ax mov ax,word ptr patch+4 mov word ptr outpkt+4,ax ret showkey: mov ax,offset shkmsg mov cx,shklen ret ; Initialization for using serial port. Returns normally. SERINI PROC NEAR cmp sintok,0 ; is this trip necessary? jne SIN1 ; no assume cs:cgroup push es mov bl,flags.comflg ; get port # sub bh,bh shl bx,1 ; convert to port name table index mov di,prttab[bx] ; create it call crfile mov soutok,ax ; set serial output token mov bx,2 ; open for writing call aopen mov ax,soutok ; set baud rate & attributes mov bx,offset portatt mov cx,5 call aspcl mov ax,mbox ; when the message sent to mbox mov bx,ax ; is the mbox token itself call sendms ; it tells the serial input task pop es ; to initialize & start input assume cs:code ; burma-shave SIN1: ret SERINI ENDP ; Reset the serial port. This is the opposite of serini. Calling ; this twice without intervening calls to serini should be harmless. ; Returns normally. SERRST PROC NEAR assume cs:cgroup mov bx,sintok ; serial input token test bx,bx jz SERST1 ; unecessary mov sintok,0 ; MUST be done 1st, cuz siotsk is gonna get ints call delcon ; before we get back from delete connection SERST1: mov bx,soutok ; serial output token test bx,bx jz SERST2 ; don't bother mov soutok,0 ; see $-5 lines call delcon ; ditto SERST2: ret assume cs:code SERRST ENDP ; Bell ringer BEEP PROC NEAR mov dl,bell mov ah,dconio int dos ret BEEP ENDP ; Dumb terminal emulator. Doesn't work too well above 1200 baud (and ; even at 1200 baud you sometimes lose the first one or two characters ; on a line). term proc near mov si,ax ; this is source mov di,offset ourarg ; place to store arguments mov ax,ds mov es,ax ; address destination segment mov cx,size termarg rep movsb ; copy into our arg blk mov ax,cotok ; save terminal attributes mov bx,offset termatt mov cx,4 assume cs:cgroup call special push termatt+6 ; save and termatt+6,0fdffh ; turn off translation push termatt+4 ; save or termatt+4,0e0h ; disable OSC's mov ax,cotok ; make it transparent as all get out mov bx,offset termatt ; during terminal emulation mov cx,5 call special pop termatt+4 ; restore pop termatt+6 ; restore sub ax,ax ; turn off control C trap mov sigpair,ax mov ax,citok mov bx,offset sigpair mov cx,6 call special assume cs:code term1: call prtchr jmp short term2 ; have a char... nop nop jmp short term3 ; no char, go on term2: push ax and al,7fh ; mask off parity for terminal mov temp,al mov ax,cotok mov bx,offset temp mov cx,1 assume cs:cgroup call writer ; go print it assume cs:code pop ax test ourarg.flgs,capt ; capturing output? jz short term3 ; no, forget it call ourarg.captr ; else call the routine term3: mov ax,citok ; read 1 from keyboard mov bx,offset temp mov cx,1 assume cs:cgroup call reader assume cs:code test ax,ax jz term1 ; no character, go on mov al,temp cmp al,ourarg.escc ; escape char? je short term4 ; yes, exit mov ah,al call outchr ; output the character nop nop nop test ourarg.flgs,lclecho ; echoing? jz term1 ; no, continue loop mov ax,cotok ; write it to CRT mov bx,offset temp mov cx,1 assume cs:cgroup call writer assume cs:code jmp term1 ; else echo and keep going term4: mov ax,cotok ; restore terminal attributes mov bx,offset termatt mov cx,5 assume cs:cgroup call special mov ax,sematok ; restore control C trap mov sigpair,ax mov ax,citok mov bx,offset sigpair mov cx,6 call special assume cs:code ret term endp ; MSCOMM's outpkt routine rewritten to eliminate character-at-a-time output outpkt_ proc near assume cs:cgroup mov ax,ds mov es,ax push bp ; save mov bp,dbufno ; output buffer number cmp dstflg[bp],0 ; has it been written je OUTPK1 ; no mov bx,dstmbx[bp] ; yes, wait for it to complete mov ax,soutok call waitio OUTPK1: mov di,dstptr[bp] ; output buffer ptr mov dstflg[bp],di ; flag as written mov cl,trans.spad ; number of pad chars sub ch,ch mov bx,cx ; save # pads cld jcxz OUTPK2 mov al,trans.spadch ; pad character rep stosb ; put in buffer OUTPK2: mov dx,di ; save buffer ptr mov di,offset packet mov cx,0ffffh ; compute # bytes in packet sub al,al repne scasb neg cx add cx,0fffeh add bx,cx ; add to # pad chars mov si,offset packet mov di,dx rep movsb ; move packet to buffer mov cx,bx ; write pads & packet mov bx,dstptr[bp] mov dx,dstmbx[bp] call awrite xor bp,2 ; ping-pong buffer mov dbufno,bp ; for next time pop bp ; restore assume cs:code jcxz OUTPK3 ; OK if no awrite complaints ret ; else NG OUTPK3: jmp rskp outpkt_ endp ; Jumping to this location is like retskp. It assumes the instruction ; after the call is a jmp addr. RSKP PROC NEAR pop bp add bp,3 push bp ret RSKP ENDP ; Jumping here is the same as a ret. R PROC NEAR ret R ENDP code ends end