name msxv90 ; File MSXV90.ASM include mssdef.h ; Copyright (C) 1982,1991, Trustees of Columbia University in the ; City of New York. Permission is granted to any individual or ; institution to use, copy, or redistribute this software as long as ; it is not sold for profit and this copyright notice is retained. ; Kermit system dependent module for VICTOR 9000/SIRIUS ; Edit History ; 2 March 1991 version 3.10 ; Last edit: 17 Dec 1990 MS-DOS Kermit v3.02 editing. [jrd] ; 12 June 1988 Add error recovery if serial port fails to initialize. [jrd] ; 9 March 1988 Add procedure getmodem and global byte mdmhand for use by ; scripts in sensing modem status. Add: ignore received XOFF if we have sent ; an XOFF already (avoids lockouts from echoes). Add user/buffer xon/xoff ; sensing. [jrd] ; 3 March 1988 Add shomodem routine to show status of DSR, CD, CTS lines ; [bgp] ; 7 Sept 1987 Remove keep_delete, pass null, del chars to terminal section. ; [jrd] ; 29 August 1987 Add capability for sending long break [bgp] ; 9 February 1987 Add flag for ignoring delete chars in SERINT - they must ; be retained when emulating a Tektronix (graphics mode) - this is done ; using global symbol keep_delete. ; 6 November 1986 Fix receiver overrun detection and miscellaneous minor ; fixes ; 30 Sept 1986 Reject DEL char at serial port reception level to avoid ; problems when DEL is used as a filler char (be Emacs). [jrd] ; 16 Sept 1986 Revise serial port routines prtchr, outchr, serini, serint ; to use more efficient code from IBM version. [jrd] ; 4 Sept 1986 Add Bob Goeke's change to move comms port table to a system ; dependent module (typ msx---) to allow 3+ ports and localized idents. [jrd] ; 26 August 1986 Use parity mask when testing for nulls & Xon/Xoff in serial ; port interrupt routine. [jrd] ; 16 August 1986 Use observed screen attributes for mode line. [jrd] ; 9 August 1986 Revise SERINT to insert control-G for overrun chars, give ; faster return of interrupts to system, remove use of BP in code. [jrd] ; Original version, BGP, 23 November 1985 ; Add global entry point vtstat for use by Status routine in mssset. ; Cleared terminal emulation flag, flags.vtflg, in procedure lclini. ; Add register save/restore in procedure getbaud. ; Joe R. Doupnik 12 March 1986 ; Add some register save/restores here and there. ; Add global procedures ihosts and ihostr to handle host initialization ; when packets are to be sent or received by us,resp. 24 March 1986 ; Add global procedure dtrlow to force DTR and RTS low in support of Kermit ; command Hangup. B.G.Peterson 10 April 1986 ; Add support of serial port settings through use of IOCTL DOS function ; code from Andreas Stumpf (ZRZS@DS0RUS1I), merged by B.G.Peterson 10 April 86 ; Moved VTS and VTSTAT routines to MSYxxx.ASM where the terminal emulation ; is done anyway. B.G.Peterson 10 April 1986 ; Last update 28 April 1986 ; 30 July 1986 Corrected IHOSTS and IHOSTR to prevent sending null byte if ; no flow control. ; Modified SERHNG so it turns DTR and RTS off for 3 seconds and then back ; on again so a new connection can be made easily. ; Modified SERRST to wait until transmitter is empty before turning off ; port. ; Modified port controller access so that if opening it using the standard ; Victor drivers fails, it will just go direct to the hardware. [bgp] public serini, serrst, clrbuf, outchr, coms, dodel public ctlu, cmblnk, locate, lclini, prtchr, dobaud public clearl, getbaud, beep, puthlp, putmod public clrmod, poscur, sendbr, showkey, sendbl, pcwait public xofsnt, machnam, setktab, setkhlp, count public ihosts, ihostr, dtrlow, comptab, serhng, mdmhand public shomodem, getmodem, baudst, bdtab, portval public parmsk, flowon, flowoff, peekcom FALSE equ 0 off equ 0 bufon equ 1 ; buffer level xon/xoff on-state control flag usron equ 2 ; user level xon/xoff on-state control flag MNTRGH EQU BUFSIZ*3/4 ; High point = 3/4 of buffer full. MNTRGL EQU BUFSIZ/4 ; Low point = 1/4 of buffer full. DEF_BAUD EQU 8 ; Default to 1200 baud ; constants used by serial port handler SEG_7201 EQU 0E004H ; Segment for 7201 serial controller DATAA_7201 EQU 0 ; DATA A offset STATA_7201 EQU 2 ; STATUS A offset DATAB_7201 EQU 1 ; DATA B offset STATB_7201 EQU 3 ; STATUS B offset ; no interrupts, no waits REG1_7201 EQU 0 ; 7201 Register 1 value ENABLE_INT EQU 18H ; Mask to turn on interrupts (OR) ; non-DMA, non-vectored interrupts, priority type 1 (Ra>Rb>Ta>Tb) REG2_7201 EQU 14H ; 7201 Register 2 value ; 8 bits/char, no CRC, receiver enabled REG3_7201 EQU 0C1H ; 7201 Register 3 value ; clock/16, 1.5 stop bit, no parity REG4_7201 EQU 48H ; 7201 Register 4 value ; DTR low (active), 8 bits/char, transmitter enabled, RTS low, no CRC REG5_7201 EQU 0EAH ; 7201 Register 5 value BREAK_ON EQU 10H ; Mask to turn on break bit (OR) DTR_RTS_OFF EQU 7DH ; Mask to turn off DTR and RTS (AND) MDMINP EQU 1 ; Input ready bit. [jrd] MDMOVER EQU 32 ; Receiver overrun bit. [bgp] SEG_8253 EQU 0E002H ; Segment for 8253 timer SETA_8253 EQU 0 ; Speed for port A SETB_8253 EQU 1 ; Speed for port B CTRL_8253 EQU 3 ; Control for 8253 timer SEG_8259 EQU 0E000H ; Segment for 8259 int. controller CW1_8259 EQU 0 ; Offset for 8259 register 1 CW2_8259 EQU 1 ; Offset for 8259 register 2 ; 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. ; setktab - keyword table for redefining keys (should contain a 0 if ; not implemented) ; setkhlp - help for setktab. data segment extrn flags:byte, trans:byte setktab db 0 setkhlp db CR,LF,'Set Key not supported on VICTOR/SIRIUS Kermit$' sh_key_str db 'Set Key not supported on VICTOR/SIRIUS Kermit' sh_key_len dw 45 machnam db 'VICTOR/SIRIUS$' clrlin db CR ; clears full line with next line... clreol db ESCAPE,'K$' ; clears from cursor to end of line home db ESCAPE,'H$' ; homes cursor clrscr db ESCAPE,'E$' ; clears screen and homes cursor delstr db BS,BS,' ',BS,BS,'$' ; Delete string clr_25 db ESCAPE,'j',ESCAPE,'x1',ESCAPE,'Y8 ',ESCAPE,'l',ESCAPE,'k' db ESCAPE,'y1$' ;clr_25 does the entire operation of clearing the mode line start_25 db ESCAPE,'j',ESCAPE,'x1',ESCAPE,'Y8 ',ESCAPE,'p$' ; start_25 enables line 25 and moves to the start in reverse video end_25 db ESCAPE,'q',ESCAPE,'k',ESCAPE,'y1$' ; end_25 turns off reverse video and line 25 and returns the cursor mov_pfx db ESCAPE,'Y$' ; prefix for moves mdminfo struc mddat dw 0 ; data register mdiir dw 0 ; interrupt identification register mdstat dw 0 ; line status register mdcom dw 0 mden db 0 mddis db 0 mdmeoi db 0 mdintv dw 0 mdminfo ends modem mdminfo port1 prtinfo <0FFFH,0,defpar,1,0,defhand,floxon,0> ; UART ports port2 prtinfo <0FFFH,0,defpar,1,0,defhand,floxon,0> portval dw port1 ; Default is to use port 1 savsci dw ? ; Save for serial port interrupt vector. savscs dw ? ; Ditto. portin db FALSE ; Has comm port been initialized. xofsnt db FALSE ; Say if we sent an XOFF. xofrcv db FALSE ; Say if we received an XOFF. parmsk db ? ; parity mask, 0ffh for no parity, 07fh with. flowoff db ? ; flow-off char, Xoff or null (if no flow) flowon db ? ; flow-on char, Xon or null overrun db ? ; holds status of receiver overrun mdmhand db 0 ; Modem status register, current. erms20 db CR,LF,'?Warning: System has no disk drives$' badbd db CR,LF,'Unimplemented baud rate$' hngmsg db CR,LF,' The phone should have hung up.',CR,LF,'$' ; [jrd] hnghlp db CR,LF,' The modem control lines DTR and RTS for the current' db ' port are forced low (off)' db CR,LF,' to hang up the phone. Normally, Kermit leaves them' db ' high (on) when it exits.' db CR,LF,' They will return high after about 3 seconds.' db CR,LF,'$' ; [jrd] shmdm0 db CR,LF,' Modem is not ready: DSR is off$' shmdm1 db CR,LF,' Modem is ready: DSR is on$' ; [bgp] shmdm2 db CR,LF,' no Carrier Detect: CD is off$' shmdm3 db CR,LF,' Carrier Detect: CD is on$' shmdm4 db CR,LF,' no Clear to Send: CTS is off$' shmdm5 db CR,LF,' Clear to Send: CTS is on$' ; [bgp] tmp db ?,'$' temp1 dw ? ; Temporary storage. ontab db 02H ; Two entries. mkeyw 'OFF',00H mkeyw 'ON',01H comptab db 0AH mkeyw '1',01H mkeyw '2',00H mkeyw 'A',01H mkeyw 'B',00H mkeyw 'COM1',01H mkeyw 'COM2',00H mkeyw 'SERIALA',01H mkeyw 'SERIALB',00H mkeyw 'TTY',01H mkeyw 'UL1',00H bdtab db 16 ; Baud rate table mkeyw '45.5',0 mkeyw '50',1 mkeyw '75',2 mkeyw '110',3 mkeyw '134.5',4 mkeyw '150',5 mkeyw '300',6 mkeyw '600',7 mkeyw '1200',8 mkeyw '1800',9 mkeyw '2000',10 mkeyw '2400',11 mkeyw '4800',12 mkeyw '9600',13 mkeyw '19200',14 mkeyw '38400',15 ; This table is indexed by the baud rate definitions given above. ; Unsupported baud rates should contain FF. ; This number is determined by 78125/(baud rate) (decimal values) bddat label word dw 6B4H ; 45.5 baud dw 61AH ; 50 baud dw 411H ; 75 baud dw 2C6H ; 110 baud dw 244H ; 134.5 baud dw 208H ; 150 baud dw 104H ; 300 baud dw 82H ; 600 baud dw 41H ; 1200 baud dw 2BH ; 1800 baud dw 26H ; 2000 baud dw 20H ; 2400 baud dw 10H ; 4800 baud dw 8H ; 9600 baud dw 4H ; 19200 baud dw 2H ; 38400 baud ; variables for serial interrupt handler source db BUFSIZ DUP(?) ; Buffer for data from port. srcpnt dw 0 ; Pointer in buffer (DI). count dw 0 ; Number of chars in int buffer. ; variables for accessing portinfo from serial drivers using IOCTL function ; ; Structure is defined according to "Systems Programmers Toolkit II", ; Appendix A. Structure contains baud rate and values of control registers ; 0 through 7. pval struc stype dw 11H ; port access status dw (?) blocktype dw 0 ; serial baudr dw (?) ; baud rate to set or get CR0 db (?) CR1 db (?) CR2A db (?) CR2B db (?) CR3 db (?) CR4 db (?) CR5 db (?) CR6 db (?) CR7 db (?) pval ends erms41 db CR,LF,'?Warning: Cannot open com port' db CR,LF,' Going direct to serial controller hardware...$' ; [bgp] rdbuf db 20 dup (?) ; input buffer plength equ 17 ; length of pval structure oldpval pval <,,,41H,,,,,,,,,> ; default to 1200 baud newpval pval <,,,41H,,,,,,,,,> ; value comes from bdtab above... [bgp] prttab dw com2,com1 ; 0=com2, 1=com1 in flags.comflg com1 db 'SERIALA',0 ; name string for device com2 db 'SERIALB',0 IOread equ 2 ; read status block IOwrite equ 3 ; write status block prthnd dw 0 ; handle for accessing port data ends code segment extrn comnd:near, dopar:near, defkey:near extrn lclyini:near assume cs:code,ds:data,es:nothing ; local initialization LCLINI proc near mov flags.vtflg,TTHEATH ; BIOS does HEATH, use as default cmp flags.comflg,1 ; using port 1? jne lclini2 ; no... mov portval,offset port1 mov modem.mddat,DATAA_7201 ; set COM1 values mov modem.mdstat,STATA_7201 mov modem.mdcom,SETA_8253 jmp lclini0 lclini2: ; using port2 mov portval,offset port2 mov modem.mddat,DATAB_7201 ; set COM2 values mov modem.mdstat,STATB_7201 mov modem.mdcom,SETB_8253 lclini0:call opnprt ; get file handle and init port call lclyini ; init term part too ret LCLINI endp ; procedure to get a file handle for the port. if it fails ask the user ; for some predefined handle (3 is the usual value) (Andreas Stumpf) ; 30 July 1986 If it fails, just warn the user and go direct to the ; hardware [bgp] OPNPRT proc near cmp prthnd,0 ; is one open? jle opnprta ; no... mov bx,prthnd ; better close this mov ah,CLOSE2 ; to be sure they don't accumulate int DOS mov prthnd,0 ; done... opnprta: mov al,flags.comflg mov ah,0 mov si,ax shl si,1 ; double index mov dx,prttab[si] mov ah,OPEN2 mov al,2 int DOS ; open port on handle jnc opnprt2 mov ah,PRSTR ; it didn't like the string... mov dx,offset erms41 int DOS mov prthnd,-1 ; no port is open! [bgp] push es ; better save this mov bx,SEG_7201 ; point at controller mov es,bx mov bx,modem.mdstat mov byte ptr es:[bx],1 mov byte ptr es:[bx],18H ; Software reset of current port push ax ; kill time for four 2.5 MHz cylces pop ax ; 8 processor cycles mov bx,STATA_7201 ; First one is always port A mov byte ptr es:[bx],2 ; must be first one set mov byte ptr es:[bx],REG2_7201 mov bx,modem.mdstat mov byte ptr es:[bx],4 ; must be second mov byte ptr es:[bx],REG4_7201 mov byte ptr es:[bx],1 ; rest any order mov byte ptr es:[bx],REG1_7201 mov byte ptr es:[bx],3 mov byte ptr es:[bx],REG3_7201 mov byte ptr es:[bx],5 mov byte ptr es:[bx],REG5_7201 pop es call getbaud ; use this to be sure right value is used call dobaud ; better set baud rate to the correct value too jmp opnprt2a ; all init done... [bgp] opnprt2: mov prthnd,ax ; call succeeded - save handle mov bx,ax mov ah,IOCTL mov al,IOread ; get old values mov cx,plength mov dx,offset oldpval ; place for old values int DOS mov ah,IOCTL mov al,IOread mov dx,offset newpval ; one to work on int DOS ; set registers to something neat cli ; avoid interrupts here mov bx,offset newpval mov [bx].CR1,REG1_7201 mov [bx].CR2A,REG2_7201 mov [bx].CR3,REG3_7201 mov [bx].CR4,REG4_7201 mov [bx].CR5,REG5_7201 mov bx,prthnd ; get handle mov ah,IOCTL mov al,IOwrite ; set new values int DOS opnprt2a: push es ; save this for a flash mov bx,SEG_7201 mov es,bx mov bx,modem.mdstat mov byte ptr es:[bx],10H ; clear external/status interrupts mov byte ptr es:[bx],30H ; clear special receive cond. int. mov byte ptr es:[bx],38H ; set to end of interupt pop es ; back again sti ; interrupts are okay again ret OPNPRT endp ; Show the definition of a key. Since it isn't really necessary to redefine ; keys for the VICTOR/SIRIUS (assuming that KEYGEN is available), this isn't ; implemented, and the string returned to the calling sequence merely says so. ; Returns a string to print in AX, length of same in CX. ; Returns normally. SHOWKEY proc near mov ax,offset sh_key_str mov cx,sh_key_len ret SHOWKEY 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. ; Returns normally. CLRBUF proc near cli mov ax,offset source mov srcpnt,ax mov count,0 sti ret CLRBUF endp ; Clear to the end of the current line. Returns normally. CLEARL proc near mov dx,offset clreol mov ah,PRSTR int DOS ret CLEARL endp ; Put the char in AH to the serial port. This assumes the ; port has been initialized. Skip returns on success, returns ret if the ; character cannot be written. ; Add entry point OUTCH2 for non-flow controlled sending to ; prevent confusion of flow control logic at top of outchr; used by receiver ; buffer high/low water mark flow control code. [jrd] OUTCHR proc near cmp flowoff,0 ; Are we doing flow control. je outch2 ; No, just continue. cmp ah,flowoff ; sending xoff? jne outch1 ; ne = no mov xofsnt,usron ; indicate user level xoff being sent jmp outch1b outch1: cmp ah,flowon ; user sending xon? jne outch1b ; ne = no mov xofsnt,off ; say an xon has been sent (cancels xoff) outch1b:cmp xofrcv,off ; Are we being held (xoff received)? je outch2 ; e = no - it's OK to go on. cmp flags.timflg,0 ; is timer off? je outch2 ; e = yes, no timeout period push cx ; save reg mov ch,trans.rtime ; receive timeout interval (sec) mov cl,0 ; convert to 4 millsec increments jcxz outch1c ; z = no timeout wanted. outch1a:cmp xofrcv,off ; Are we being held (xoff received)? je outch1c ; e = no - it's OK to go on. push ax mov ax,4 ; 4 millisec wait loop call pcwait pop ax loop outch1a ; and try it again mov xofrcv,off ; timed out, force it off and fall thru outch1c:pop cx ; end of flow control section ; OUTCH2 is entry point for sending without flow control OUTCH2: mov al,ah ; Parity routine works on AL. call dopar ; Set parity appropriately. mov ah,al ; Don't overwrite character with status push bx push cx push es xor cx,cx mov bx,SEG_7201 ; point at 7201 mov es,bx mov bx,modem.mdstat outch3: mov al,es:[bx] test al,4 ; ready? jnz outch4 ; yes loop outch3 jmp outch5 ; Timeout outch4: mov al,ah ; Now send it out mov bx,modem.mddat mov es:[bx],al pop es pop cx pop bx ret outch5: pop es pop cx pop bx ret OUTCHR endp ; This routine blanks the screen. Returns normally. CMBLNK proc near mov dx,offset clrscr mov ah,PRSTR int DOS ret CMBLNK endp ; Locate: homes the cursor. Returns normally. LOCATE proc near mov dx,offset home mov ah,PRSTR int DOS ret LOCATE endp ; write a line in inverse video at the bottom of the screen... ; the line is passed in dx, terminated by a dollar sign. Returns normally. PUTMOD proc near push si ; better save this push dx ; preserve message mov dx,offset start_25 ; to set up for write to 25 mov ah,PRSTR int DOS mov ah,DCONIO ; output a char at a time pop si ; get back message cld ; better increment putmod1: lodsb ; get byte cmp al,'$' ; is it end of string? je putmod2 mov dl,al int DOS jmp putmod1 putmod2: mov dx,offset end_25 ; back to normal mov ah,PRSTR int DOS pop si ; and restore it ret PUTMOD endp ; clear the mode line written by putmod. Returns normally. CLRMOD proc near mov dx,offset clr_25 ; to clear line 25 mov ah,PRSTR int DOS ret CLRMOD endp ; put a help message on the screen. This one uses reverse video... ; pass the message in ax, terminated by a null. Returns normally. PUTHLP proc near push si mov si,ax mov ah,DCONIO ; don't check anything... cld ; better increment on strings puthlp1: lodsb ; get byte cmp al,0 ; is it null (null-terminated string) je puthlp2 mov dl,al int DOS jmp puthlp1 puthlp2: mov dl,13 ; want a crlf int DOS mov dl,10 int DOS pop si ret PUTHLP endp ; Set the baud rate for the current port, based on the value ; in the portinfo structure. Returns carry clear. BAUDST PROC NEAR mov dx,offset bdtab ; baud rate table, ascii xor bx,bx ; help is the table itself mov ah,cmkey ; get keyword call comnd jc baudst1 ; c = failure push bx ; save result mov ah,cmeol ; get confirmation call comnd pop bx jc baudst1 ; c = failure mov si,portval mov ax,[si].baud ; remember original value mov [si].baud,bx ; set the baud rate call dobaud ; use common code clc baudst1:ret BAUDST ENDP ; Set the baud rate for the current port, based on the value ; in the portinfo structure. Returns normally. Method of setting using ; IOCTL thanks to Andreas Stumpf DOBAUD proc near push ax ; these too [jrd] push bx push cx push dx mov bx,portval mov bx,[bx].baud cmp bx,0 jl unk_baud ; out of range cmp bl,bdtab ; number of table entries jge unk_baud ; out of range shl bx,1 ; get index into table mov ax,offset bddat ; start of table add bx,ax mov ax,[bx] ; get divider cmp ax,0FFH ; unimplemented baud rate? je unk_baud ; that's right mov bx,offset newpval mov [bx].baudr,ax ; set it in structure no matter what [bgp] cmp prthnd,0 ; anything open? jge dobaud0 ; yes, do it nice push es ; better save this... push ax ; save divider mov bx,SEG_8253 ; point at timer mov es,bx mov bx,CTRL_8253 ; set up function first mov ax,modem.mdcom ; set up control byte ror al,1 ; need port number in high bits ror al,1 and al,0C0H ; keep only top 2 bits add al,36H ; set both, Mode 3, binary mov es:[bx],al mov bx,modem.mdcom ; Where to write the rate pop ax ; get divider back mov es:[bx],al mov es:[bx],ah ; done pop es jmp dobaud1 dobaud0: ; [bgp] mov ah,IOCTL mov al,IOwrite mov bx,prthnd ; set the poor thing mov cx,plength mov dx,offset newpval int DOS dobaud1: pop dx pop cx pop bx pop ax ret unk_baud: mov ah,PRSTR mov dx,offset badbd ; Give an error message. int DOS pop dx ; restore regs [jrd] pop cx pop bx pop ax ret DOBAUD endp ; Get the current baud rate from the serial card and set it ; in the portinfo structure for the current port. Returns normally. ; This is used during initialization. The method of getting the baud ; rate directly from the port handler is thanks to Andreas Stumpf. ; Note that this assumes that the thing has a defined baud rate to ; start with from the initialization and the opnprt has been called on ; the current port so that the values in newpval are defined. GETBAUD proc near push ax push bx push cx push dx cmp prthnd,0 ; opened? jne go_gb ; yes, get the rate call opnprt ; no, open it go_gb: mov bx,offset newpval mov ax,[bx].baudr xor ch,ch mov cl,bdtab ; entries in baudtable mov bx,offset bddat-2 add bx,cx add bx,cx loop_gb: cmp ax,[bx] je have_gb dec bx dec bx dec cx jnz loop_gb have_gb: dec cx ; value is one greater than desired mov bx,portval ; cx=-1 means unrecognized (dropped off bottom) mov [bx].baud,cx pop dx pop cx pop bx pop ax ret GETBAUD endp ; Skip returns if no character available at port, ; otherwise returns with char in al, # of chars in buffer in dx. ; Revised 22 May 1986, and again slightly 2 August 1986 by [jrd] ; Direct copy from msxibm.asm [jrd] PRTCHR proc near call chkxon ; see if we need to xon cmp count,0 ; any characters available? jnz prtch1 ; nz = yes, get one xor dx,dx ; return count of zero stc ret ; No data prtch1: push si ; save si cli ; interrupts off, to keep srcpnt & count consistent mov si,srcpnt ; address of next available slot in buffer sub si,count ; minus number of unread chars in buffer cmp si,offset source ; located before start of buffer (wrapped)? jae prtch2 ; ae = no add si,BUFSIZ ; else do arithmetic module bufsiz prtch2: mov al,byte ptr [si] ; get a character into si dec count ; one less unread char now sti ; interrupts back on now. pop si mov dx,count ; return # of chars in bufer clc ret PRTCHR endp ; Examine incoming communications stream for a packet SOP character. ; Return CX= count of bytes starting at the SOP character (includes SOP) ; and carry clear. Return CX = 0 and carry set if SOP is not present. ; Destroys AL. peekcom proc far mov cx,count ; qty in circular buffer cmp cx,6 ; basic NAK jb peekc4 ; b = two few chars, get more push bx cli ; interrupts off, to keep srcpnt & count consistent mov bx,srcpnt ; address of next available slot in buffer sub bx,cx ; minus number of unread chars in buffer cmp bx,offset source ; located before start of buf? jae peekc1 ; ae = no add bx,bufsiz ; else do arithmetic modulo bufsiz peekc1: mov al,[bx] cmp al,trans.rsoh ; packet receive SOP? je peekc3 ; e = yes inc bx cmp bx,offset source+bufsiz ; beyond end of buffer? jb peekc2 ; b = no mov bx,offset source ; wrap around peekc2: loop peekc1 ; keep looking sti pop bx stc ; set carry for no SOP ret peekc3: sti ; interrupts back on now pop bx inc cx ; include SOP in count clc ; say SOP found ret ; CX has count remaining peekc4: xor cx,cx ; return count of zero stc ; say no data ret peekcom endp ; local routine to see if we have to transmit an xon chkxon proc near cmp flowon,0 ; doing flow control? je chkxo1 ; no, skip all this test xofsnt,usron ; did user send an xoff? jnz chkxo1 ; nz = yes, don't contradict it here test xofsnt,bufon ; have we sent a buffer level xoff? jz chkxo1 ; z = no, forget it cmp count,mntrgl ; below (low water mark) trigger? jae chkxo1 ; no, forget it mov ah,flowon ; ah gets xon and xofsnt,off ; remember we've sent the xon. call outch2 ; send via non-flow controlled entry point chkxo1: ret chkxon endp ; IHOSTS - Initialize the host by sending XON, or equivalent, and enter the ; cycle of clear input buffer, wait 1 second, test if buffer empty then exit ; else repeat cycle. Requires that the port be initialized before hand. ; Ihosts is used by the local send-file routine just after initializing ; the serial port. ; 22 March 1986 [jrd] ; 30 July 1986 Avoid sending nulls if no flow control [bgp] IHOSTS proc near push ax ; save the registers push cx push dx mov xofrcv,off ; clear old xoff received flag mov xofsnt,off ; and old xoff sent flag mov ah,flowon ; put Go-ahead flow control char in ah or ah,ah ; check for null char jz ihosts1 ; z=null, don't send it call outchr ; send it (release Host's output queue) ihosts1:call clrbuf ; clear out interrupt buffer pop dx ; empty buffer. we are done here. pop cx pop ax ret IHOSTS endp ; IHOSTR - initialize the remote host for our reception of a file by ; sending the flow-on character (XON typically) to release any held ; data. Called by receive-file code just after initializing the serial ; port. 22 March 1986 [jrd] ; 30 July 1986 Avoid sending null if no flow control [bgp] IHOSTR proc near push ax ; save reg mov xofrcv,off ; clear old xoff received flag mov xofsnt,off ; and old xoff sent flag mov ah,flowon ; put Go-ahead flow control char in ah or ah,ah ; check for null char jz ihostr1 ; z=null, don't send it call outchr ; send it (release Host's output queue) ihostr1:pop ax ret IHOSTR endp ; Global proc to hang up the phone by making DTR and RTS low. DTRLOW proc near mov ah,cmline ; allow text to be able to display help mov bx,offset rdbuf ; dummy buffer mov dx,offset hnghlp ; help message call comnd ; get a confirm jnc dtrlow1 ; nc = success ret ; carry set = failure dtrlow1:call serhng ; drop DTR and RTS mov ah,PRSTR ; give a nice message mov dx,offset hngmsg int dos clc ret DTRLOW endp ; SERHNG us used to hang up the phone. This resets the port (by calling ; serrst), and then forces DTR and RTS low to terminate the connection. ; 12 April 1986 [bgp] ; 30 July 1986 Turn them back on again after 3 seconds so new connection can ; be made [bgp] SERHNG proc near call serrst ; reset the port to be sure push dx push cx push bx push ax cmp prthnd,0 ; nice method open? [bgp] jge serhng0 ; yes push es ; better save this mov bx,SEG_7201 ; point at controller mov es,bx mov bx,modem.mdstat cli ; no interrupts please mov byte ptr es:[bx],5 ; register 5 mov byte ptr es:[bx],REG5_7201 and DTR_RTS_OFF sti ; interrupts ok again mov ax,3000 ; sleep for 3 seconds call pcwait mov bx,SEG_7201 mov es,bx mov bx,modem.mdstat cli ; no interrupts please mov byte ptr es:[bx],5 mov byte ptr es:[bx],REG5_7201 sti ; interrupts ok again pop es jmp short serhng1 ; [bgp] serhng0: mov bx,offset newpval mov [bx].CR5,REG5_7201 and DTR_RTS_OFF mov dx,bx ; where the stuff is mov cx,plength ; how long it is mov bx,prthnd ; get handle mov ah,IOCTL mov al,IOwrite ; set new values int DOS mov ax,3000 ; sleep for 3 seconds call pcwait mov bx,offset newpval ; turn them back on again... mov [bx].CR5,REG5_7201 mov dx,bx mov cx,plength mov bx,prthnd mov ah,IOCTL mov al,IOwrite int DOS serhng1: pop ax pop bx pop cx pop dx ret SERHNG endp ; SHOW MODEM, displays current status of lines DSR, CD, and CTS. ; Uses global byte mdmhand, the modem line status register. [jrd] SHOMODEM proc near mov ah,cmeol call comnd jc shomodem4 ; c = failure mov dx,offset shmdm0 ; assume DSR is not set test mdmhand,20h ; is bit set? jnz shomodem1 ; z = no mov dx,offset shmdm1 ; DSR is asserted (line active) shomodem1: mov ah,PRSTR int DOS test mdmhand,80h ; test for CD jz shomodem2 ; z = off mov dx,offset shmdm3 ; actually have CD shomodem2: int DOS mov dx,offset shmdm4 ; assume no CTS test mdmhand,10H ; test for CTS jz shomodem3 mov dx,offset shmdm5 ; actually have CTS shomodem3: int DOS clc shomodem4:ret SHOMODEM endp ; Get modem status and set global byte mdmhand. Preserve all registers. ; Check the status of the return control lines from the serial port. ; Since this function doesn't care if the port has been properly ; initialized or anything else we will always go directly to the ; hardware. This is actually quite reasonable since the values ; returned in IOCTL from earlier will have no relation to the current ; state of reality. [bgp] ; Note that the method of getting DSR is a little weird. There are ; no connections on the 7201 for DSR so we have to get it from one ; of the 6522s where there are a few free lines (specifically, the ; one that controls the keyboard interface and the CRT brightness/ ; contrast. This one is at E8040-E804F with line PA3 for DSRA and ; PA5 for DSRB. Note that a zero is registered for an active line ; on the DSR sense. getmodem proc near ; gets modem status upon request mov mdmhand,0 ; assume nothing is on push bx push es ; better save these mov bx,0E804H ; segment of 6522 mov es,bx mov bl,8 ; assume looking at port A cmp flags.comflg,1 je getmod1 ; e = it is A mov bl,20H ; actually looking at B getmod1:test es:[1],bl ; is DSR bit set? jnz getmod2 ; nz = no or mdmhand,20h ; DSR is asserted (line active) getmod2:mov bx,SEG_7201 ; point at 7201 mov es,bx mov bx,modem.mdstat test byte ptr es:[bx],8 ; test for CD jz getmod3 ; z = off or mdmhand,80h ; actually have CD getmod3:test byte ptr es:[bx],20H ; test for CTS jz getmodx ; z = off or mdmhand,10h ; actually have CTS getmodx:pop es pop bx getmodem endp ; Send a break out the current serial port. Returns normally. ; Changed to use IOCTL function 12 April 1986 [bgp] ; 30 July 1986 Direct to hardware if not opened right [bgp] SENDBR proc near push es push dx push cx push bx push ax mov ax,250 ; break length in msec push ax ; for later use jmp sendbrz sendbl: push es push dx push cx push bx push ax mov ax,3500 ; break length in msec push ax ; for later use sendbrz: cmp prthnd,0 ; open ok? [bgp] jge sendbr0 ; yes, do it nice mov bx,SEG_7201 ; point at 7201 mov es,bx mov bx,modem.mdstat cli ; no interrupts please mov byte ptr es:[bx],1 ; register 1 mov byte ptr es:[bx],REG1_7201 mov byte ptr es:[bx],5 mov byte ptr es:[bx],REG5_7201 or BREAK_ON sti ; interrupts ok again pop ax call wait_msec cli ; no interrupts mov byte ptr es:[bx],5 mov byte ptr es:[bx],REG5_7201 mov byte ptr es:[bx],1 mov byte ptr es:[bx],REG1_7201 or ENABLE_INT sti ; interrupts ok again jmp sendbr1 ; [bgp] sendbr0: mov bx,offset newpval mov [bx].CR1,REG1_7201 ; to disable interrupts mov [bx].CR5,REG5_7201 or BREAK_ON mov dx,bx ; where the stuff is mov cx,plength ; how much there is mov bx,prthnd ; get handle mov ah,IOCTL mov al,IOwrite ; set new values cli ; avoid interrupts here int DOS mov bx,SEG_7201 ; have to explicitly do register 1 mov es,bx ; IOCTL doesn't seem to touch it mov bx,modem.mdstat mov byte ptr es:[bx],1 ; Register 1 mov byte ptr es:[bx],REG1_7201 sti ; interrupts back on pop ax call wait_msec ; kill time mov bx,offset newpval mov [bx].CR5,REG5_7201 mov bx,prthnd mov ah,IOCTL mov al,IOwrite cli ; no interrupts please int DOS mov bx,SEG_7201 ; Point at 7201 serial controller mov es,bx mov bx,modem.mdstat mov byte ptr es:[bx],1 ; Register 1 must be done explicitly mov byte ptr es:[bx],REG1_7201 or ENABLE_INT sti ; interrupts are okay again sendbr1: pop ax pop bx pop cx pop dx pop es clc ; need to stay connected ret ; And return. SENDBR endp ; Wait for the # of milliseconds in ax. The delay is set for a 5 MHz ; clock rate. Actual delay for ax=1 is 1.007 msec, plus 1.005 msec ; for each increment in ax. WAIT_MSEC proc near pcwait: ; entry point for the outside world... push cx ; 10 cycles mov cx,ax ; 2 cycles wait_msec1: push cx ; 10 cycles mov cx,294 ; 4 cycles wait_msec2: loop wait_msec2 ; 5+17*(CX-1) cycles pop cx ; 8 cycles loop wait_msec1 ; 17 cycles if jump, 5 cycles if no jump pop cx ; 8 cycles ret WAIT_MSEC endp ; Position the cursor according to contents of DX: ; DH contains row, DL contains column. Returns normally. POSCUR proc near push ax push dx ; save this mov dx,offset mov_pfx ; move prefix string mov ah,PRSTR int DOS pop dx push dx mov dl,dh add dl,' ' ; this is the row mov ah,DCONIO ; no checking please int DOS pop dx push dx add dl,' ' ; this is the column int DOS pop dx pop ax ret POSCUR endp ; Delete a character from the terminal. This works by printing ; backspaces and spaces. Returns normally. DODEL proc near mov dx,offset delstr ; Erase character. mov ah,PRSTR int DOS ret DODEL endp ; Move the cursor to the left margin, then clear to end of line. ; Returns normally. CTLU proc near mov dx,offset clrlin mov ah,PRSTR int DOS ret CTLU endp ; set the current port. COMS proc near mov dx,offset comptab mov bx,0 mov ah,CMKEY call comnd jnc coms1 ; nc = success ret coms1: push bx mov ah,cmeol call comnd ; Get a confirm pop bx jnc coms2 ret coms2: call serrst ; reset current port mov flags.comflg,bl ; Set the comm port flag. cmp flags.comflg,1 ; Using Com 1? jne coms0 ; Nope. mov ax,offset port1 mov portval,ax mov modem.mddat,DATAA_7201 ; Set COM1 defaults. mov modem.mdstat,STATA_7201 mov modem.mdcom,SETA_8253 call opnprt ; open the handle ret coms0: mov ax,offset port2 mov portval,ax mov modem.mddat,DATAB_7201 ; Set COM2 defaults. mov modem.mdstat,STATB_7201 mov modem.mdcom,SETB_8253 call opnprt ret COMS endp ; initialization for using serial port. This routine performs ; any initialization necessary for using the serial port, including ; setting up interrupt routines, setting buffer pointers, etc. ; Doing this twice in a row should be harmless (this version checks ; a flag and returns if initialization has already been done). ; SERRST below should restore any interrupt vectors that this changes. ; Returns normally. Modified to IOCTL function 12 April 1986 [bgp] SERINI proc near push es push dx push cx push bx push ax cmp portin,FALSE ; Did we initialize port already? je serin0 jmp serin2 ; Yes, just leave serin0: cli ; Disable interrupts xor ax,ax ; Address low memory mov es,ax mov bx,modem.mdintv mov ax,es:[bx] mov savsci,ax mov ax,offset serint ; Point at our routine mov es:[bx],ax add bx,2 ; Now for CS value mov ax,es:[bx] mov savscs,ax mov es:[bx],cs mov portin,1 ; Note that we are initialized mov ax,offset source mov srcpnt,ax mov count,0 mov ax,SEG_8259 ; Point at 8259 interrupt controller mov es,ax mov bx,CW2_8259 ; Control word 2 mov al,es:[bx] and al,modem.mden ; Enable INT1 (all from 7201) mov es:[bx],al ; Save it mov bx,CW1_8259 ; Control word 1 mov al,modem.mdmeoi ; Clear any outstanding requests mov es:[bx],al ; Note that access to the serial controller here is only to register 1 which ; must be done explicitly anyway, so there is no reason to care about ; whether a port is open [bgp] mov bx,SEG_7201 ; Point at 7201 serial controller mov es,bx mov bx,modem.mdstat mov byte ptr es:[bx],1 ; Register 1 must be done explicitly mov byte ptr es:[bx],REG1_7201 or ENABLE_INT mov byte ptr es:[bx],10H ; Clear external/status interrupts mov byte ptr es:[bx],30H ; Clear special receive cond. int. mov byte ptr es:[bx],38H ; Set to end of interrupt sti ; Allow interrupts mov bx,portval ; get port [jrd] mov parmsk,0FFH ; parity mask, assume parity is None. [jrd] cmp [bx].parflg,PARNON ; is it None? je serin1 ; e = yes mov parmsk,07FH ; no, pass lower 7 bits as data serin1: mov bx,[bx].flowc ; get flow control chars mov flowoff,bl ; xoff or null mov flowon,bh ; xon or null serin2: pop ax pop bx pop cx pop dx pop es clc ; carry clear for success 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. Modified to use IOCTL function 12 April 1986 [bgp] ; 30 July 1986 Make it go direct to hardware if handle open failed [bgp] ; Also, don't reset the port until the transmit buffer is empty SERRST proc near push es ; preserve this push dx push cx push bx push ax cmp portin,FALSE ; Reset already? je srst0 ; Yes, just leave. mov bx,SEG_7201 ; point at 7201 [bgp] mov es,bx mov bx,modem.mdstat sersta: cli mov byte ptr es:[bx],1 ; want status register 1 mov al,es:[bx] ; get status sti test al,1 ; all sent? (transmitter and shift reg. empty) jz sersta ; no, wait... cli ; Disable interrupts mov bx,SEG_8259 ; Point at 8259 interrupt controller mov es,bx mov bx,CW2_8259 mov al,es:[bx] or al,modem.mddis ; Turn off INT1 mov es:[bx],al xor bx,bx ; Address low memory mov es,bx mov bx,modem.mdintv ; Restore the serial card int vector mov ax,savsci mov es:[bx],ax add bx,2 ; Restore CS too. mov ax,savscs mov es:[bx],ax ; As in SERINI, the only access to the serial controller is to register 1 ; which must be done explicitly anyway, so we don't care about the handle [bgp] mov bx,SEG_7201 ; Point at 7201 serial controller mov es,bx mov bx,modem.mdstat mov byte ptr es:[bx],1 ; Register 1 has to be done explicitly mov byte ptr es:[bx],REG1_7201 mov portin,FALSE ; Reset flag. sti srst0: pop ax pop bx pop cx pop dx pop es ; All done. ret ; All done. SERRST endp ; serial port interrupt routine. This is not accessible outside this ; module, handles serial port receiver interrupts. ; Revised on 22 May 1986, again 2 August 1986 to run at 38.4kb on PC's. [jrd] ; Srcpnt holds offset, within buffer Source, where next rcv'd char goes. ; Count is number of chars now in buffer, and oldest char is srcpnt-count ; done modulo size of Source. All pointer management is handled here. ; Control-G char substituted for char(s) lost in overrun condition. [jrd] ; Adapted from msxibm.asm code. [jrd] ; Fixed test for receiver overrun. [bgp] SERINT proc near push ax ; [jrd] push ds ; do ds and ax first [jrd] push es push bx push dx mov ax,seg data mov ds,ax ; address data segment mov bx,SEG_7201 ; point at 7201 mov es,bx mov bx,modem.mdstat mov al,es:[bx] ; get status (register 0) mov byte ptr es:[bx],1 ; want status register 1 [bgp] mov ah,es:[bx] ; get status [bgp] mov bx,STATA_7201 ; this one is always channel A mov byte ptr es:[bx],38H ; Notify 7201 of end of interrupt mov bx,SEG_8259 ; point at 8259 mov es,bx mov bx,CW1_8259 mov dl,modem.mdmeoi mov es:[bx],dl ; Clear interrupt test al,MDMINP ; anything there (status register 0)? jnz srint0 ; nz = yes jmp retint ; and exit now (common jump point) srint0: mov bx,SEG_7201 mov es,bx and ah,MDMOVER ; select overrun bit (status register 1)[jrd] mov overrun,ah ; save it for later [jrd] jz srint0a ; no overrun mov bx,modem.mdstat mov byte ptr es:[bx],30H ; Clear overrun error status srint0a: mov bx,modem.mddat mov al,es:[bx] ; get data byte cmp flowoff,0 ; flow control active? je srint4 ; e = no mov ah,al ; ah = working copy. Check flow cntl. and ah,parmsk ; strip parity temporarily, if any. [jrd] cmp ah,flowoff ; acting on Xoff? jne srint3 ; ne = Nope, go on cmp xofsnt,0 ; have we sent an outstanding XOFF? [jrd] jne srint1 ; ne = yes, ignore (possible echo) mov xofrcv,bufon ; Set the flag saying XOFF received srint1: jmp retint ; and exit srint3: cmp ah,flowon ; acting on Xon? jne srint4 ; ne = Nope, go on mov xofrcv,off ; Clear the XOFF received flag. jmp retint ; and exit srint4: mov ah,overrun ; get overrun flag [jrd] or ah,ah ; overrun? jz srint5 ; z = no mov ah,al ; yes, save present char mov al,BELL ; insert control-G for missing character srint5: mov bx,srcpnt ; address of buffer storage slot mov byte ptr [bx],al ; store the new char in buffer "source" inc srcpnt ; point to next slot inc bx cmp bx,offset source+BUFSIZ ; beyond end of buffer? jb srint6 ; b = not past end mov srcpnt,offset source ; wrap buffer arount srint6: cmp count,BUFSIZ ; filled already? jae srint7 ; ae = yes inc count ; no, add a char srint7: or ah,ah ; anything in overrun storage? jz srint8 ; z = no mov al,ah ; recover any recent char from overrun xor ah,ah ; clear overrun storage jmp srint5 ; yes, go store real second char srint8: sti ; ok to allow interrupts now, not before cmp count,MNTRGH ; past the high trigger point? jbe retint ; be = no, we're within our limit test xofsnt,bufon ; Has an XOFF been sent by buffer control? jnz retint ; nz = Yes. mov al,flowoff ; get the flow off char (Xoff or null) or al,al ; don't send null chars jz retint ; z = null, nothing to send call dopar ; set parity appropriately. mov ah,al ; Don't overwrite character with status push cx ; save reg [jrd] xor cx,cx ; loop counter [jrd] mov bx,SEG_7201 ; point at 7201 mov es,bx mov bx,modem.mdstat srint9: mov al,es:[bx] test al,4 ; ready? jnz srint10 ; yes loop srint9 jmp srint11 ; Timeout srint10: mov al,ah ; now send it out mov bx,modem.mddat mov es:[bx],al mov xofsnt,bufon ; Remember we sent an XOFF at buffer level srint11: pop cx ; [jrd] retint: sti ; be sure that this made it on pop dx pop bx pop es pop ds pop ax ; [jrd] iret SERINT endp ; Produce a beep. Returns normally. BEEP proc near mov dl,BELL mov ah,DCONIO ; No checks, just do it int DOS ret BEEP endp CODE ends end