; * * * * * * * * * * * * * * * version 2.9 * * * * * * * * * * * * * * * ; [35a] Kermit now sets a default baud rate. ; [35b] Implemented the SET BAUD command. ; [35c] Implemented the baud rate function to the SHOW command. ; The above was done to make operation compatable with CPM-80 version ; and simplify use by non computer orientated users. ; Mark Woollard, 4/12/85, Animal and Grassland Research Institute ; * * * * * * * * * * * * * * * version 2.8 * * * * * * * * * * * * * * * ; [34] Insert milli-second wait-loop for Break-timing - label MSWAIT: ; [33] Fix printer on hanging system problem by letting CP/M handle the ; interrupts from the 7201 that we don't care about. Thanks to ; Paul Ford, U. of Chicago Graduate School of Business ; * * * * * * * * * * * * * * * version 2.7 * * * * * * * * * * * * * * * ; [30d] Add SET PORT command, currently unimplemented. ; [30c] Isolate all machine dependencies in KERIO. ; RonB, 04/18/84 ; * * * * * * * * * * * * * * * version 2.6 * * * * * * * * * * * * * * * ; [28e] Switch to local stack on interrupts. ; RonB, 03/28/84 ; * * * * * * * * * * * * * * * version 2.4 * * * * * * * * * * * * * * * ; [20b] Add PRTBRK to send break & set correct clock rate for NEC. ; [20d] Add a pseudo time-out to PRTOUT so it doesn't loop forever. ; RonB,03/02/84 ; [19a] Add XON/XOFF type flow control ; [19b] Clear screen and beginning and end of program. ; [19e] Add PRTBRK to send break to port (Rainbow only) ; [19g] Put in EQU for clock rate for timing loops. ; Rg, 2/84 ; * * * * * * * * * * * * * * * version 2.3 * * * * * * * * * * * * * * * ; [par] Added calls to set parity, strip parity on input if ; other than none parity is called for. ; JD, 2/84 ; * * * * * * * * * * * * * * * version 2.2 * * * * * * * * * * * * * * * ; [2] Add a de-initialization routine for the serial port, to restore ; changed interrupt vectors, etc. ; RonB,12/23/83 ; [1] Add I/O support for the NEC Advanced Personal Computer ; RonB,12/23/83 ; * * * * * * * * * * * * * * * version 2.0 * * * * * * * * * * * * * * * ; This module contains all the low level communications port I/O ; routines. ; The following is the I/O code for the DEC Rainbow. CSEG $ ; Clock rate *10 for timing loops ;[19g] clckrt equ 48 ;[19g] 4.8 Mhz ; Interrupt vector locations, in data segment mnstat EQU 042H ;Status port. mndata EQU 040H ;Data port. mnctrl EQU 002H ;Control port. ; Interrupt vector locations. These are all in data segment 0. mnoff EQU 90H ;Main data port interrupt routine offset. mnseg EQU 92H ;Main data port interrupt routine segment. output EQU 04H ;Bit for output ready. input EQU 01H ;Bit for input ready. outlmt EQU 1000H ;Number of times to check output status ; before giving up on send. ;[20d] defbd EQU 0BH ; [35a] Default baud rate, 0=50 baud, 1=75, ; 2=110, 3=134.5, 4=150, 5=200, 6=300, 7=600 ; 8=1200, 9=1800, 10=2000, 11=2400, 12=3600 ; 13=4800, 14=9600 and 15=19200 ; Input data from port. Preserves all ACs and returns char in ; AL. Gets the char from the ring buffer. Assumes a char is ; already there. inchr: push bx cli ;Disable interrupts while were are playing. dec mnchrn ;Decrement the number of chars in the buffer. mov bx, mnchop ;Get the pointer into the buffer. inc bx ;Increment to the next char. cmp bx, offset mnchrs+mnchnd ;Past the end? jb inchr2 mov bx, offset mnchrs ;If so wrap around to the start. inchr2: mov mnchop, bx ;Save the updated pointer. mov al, [bx] ;Get the character. sti ;All done, we can restore interrupts. pop bx cmp parflg,parnon ;[par] no parity? je inchr3 ;[par] yup, don't bother stripping and al,7fh ;[par] checking parity, strip off inchr3: cmp floctl, floxon ; do flow control? [19a] start je inchr4 ;If yes jump ret inchr4: cmp xofsnt, true ;Have we sent an XOFF je inchr5 ;Jump if yes ret inchr5: cmp mnchrn, mntrg1 ;Under the low trigger point? jb inchr6 ;yes - jump ret inchr6: push ax ;save current character mov al, xon call prtout ;send an XON mov xofsnt, false ;turn off the flag pop ax ;get back character ret ; [19a] end ; Output data to port. Trashes DX and prints char in AL. outchr: mov dx, mndata out dx, al ret ; Test if data is available from port. instat: cmp mnchrn, 0 ;Any chars in the buffer? jnz inst2 ret inst2: jmp rskp ; Test if port is ready to send next char. Returns RETSKP if ready. ; Trashes dx. outwt: cmp floctl, floxon ;are we doing flow-control? [19a] start jne outwta ;no - go on cmp xofrcv, true ;are we being held? jne outwta ;no - ok go on ret ;held - say we're busy. [19a] end outwta: push ax mov dx, mnstat in al, dx test al, output pop ax jnz outwt2 ret outwt2: jmp rskp ; Output the character, checking first to make sure the port is clear. prtout: call dopar ;[par] push dx push cx ;[20d] begin mov cx,outlmt prtou2: call outwt ;Wait until the port is ready loop prtou2 ; or too much time has passed. nop call outchr ;Output it. pop cx ;[20d] end pop dx ret mnax dw 0 ;Storage in CSEG ;[28e] begin mnsp dw 0 ; for use by interrupt handler mnsseg dw 0 mndseg dw 0 ; This routine handles the interrupts on input. mnint: cli mov cs:mnax, ax ;Save interrupt stack location. mov ax, sp mov cs:mnsp, ax mov ax, ss mov cs:mnsseg, ax mov ax, cs:mndseg ;Switch to our internal stack. mov ss, ax mov sp, offset mnstk push ds ;Save all registers. push es push bp push di push si push dx push cx push bx mov ds, ax call mnproc ;Process the character. mov dx, mnstat ;Get the status port. mov al, 38H out dx, al ;Tell the port we finished with the interrupt. pop bx ;Restore all registers. pop cx pop dx pop si pop di pop bp pop es pop ds mov ax, cs:mnsp ;Restore the original stack. mov sp, ax mov ax, cs:mnsseg mov ss, ax mov ax, cs:mnax iret ;Return from the interrupt. ;[28e] end ; This routine (called by MNINT) gets a char from the main port ; and puts it in the infamous circular buffer. mnproc: mov dx, mnstat in al, dx ;Get the port status. test al, input ;Any there? jnz mnpro2 ;Yup, go take care of it. ;[33] Begin addition ; If not a received character, simulate an interrupt transferring ; control to the CPM routine. Let it handle worrisome things like ; someone turning on the printer. pushf ; Save flags, like an int. callf dword ptr mnoldo ; Call CPM's routine. ret ; Now back to MNINT. ;[33] End addition mnpro2: mov al, 1 ;Point to RR1. out dx, al in al, dx ;Read RR1. mov ah, al ;Save it. mov al, 30H ;Reset any errors. out dx, al mov dx, mndata in al, dx ;Read the char. cmp floctl, floxon ;are we doing flow-control ? [19a] start jne mnpr2b ;no - go on cmp al, xoff ;is it an XOFF? jne mnpr2a ;no - go on mov xofrcv, true ;set the flag ret mnpr2a: cmp al, xon ;an XON? jne mnpr2b ;no mov xofrcv, false ;clear the flag ret ; [19a] end mnpr2b: cmp mnchrn, mnchnd ;Is the buffer full? je mnperr ;If so, take care of the error. inc mnchrn ;Increment the character count. mov bx, mnchip ;Get the buffer input pointer. inc bx ;Increment it. cmp bx, offset mnchrs+mnchnd ;Past the end? jb mnpro3 mov bx, offset mnchrs ;Yes, point to the start again. mnpro3: mov mnchip, bx ;Save the pointer. mov [bx], al ;Put the character in the buffer. cmp floctl, floxon ;do flow control? [19a] start je mnpro4 ;If yes jump ret mnpro4: cmp xofsnt, true ;Have we sent an XOFF jnz mnpro5 ret ;return if we have mnpro5: cmp mnchrn, mntrg2 ;Past the High trigger point? ja mnpro6 ;yes - jump ret mnpro6: mov al, xoff call prtout ;send an XOFF mov xofsnt, true ;set the flag ret ; [19a] End mnperr: ret ;Just return on an error for now. ; prtbrk - send a break ; [19e] start prtbrk: ; mov dx, mnstat ;status reg. address for port mov al, 15H ;select reg. 5 out dx, al ; mov al, 0FAH ;8 bits, TX, Break, RTS, & DTR out dx, al ;Turn Break on mov ax, 275 ;.. for 275 millisec's [34] call mswait ; [34] mov al, 15H ;select reg. 5 out dx, al ; mov al, 0EAH ;same as above without Break out dx, al ;turn it off ret ; [19e] end mswait: ; [34] start mov cx,5*clckrt ; inner loop count for 1 millisec. mswai1: sub cx,1 ;** inner loop takes 20 clock cycles jnz mswai1 ;** dec ax ; outer loop counter jnz mswait ; wait another millisecond ret ; [34] end ; ; Init the 7201 for 8 bits, no parity, and 1 stop bit. serini: call ansmod ;Switch from VT52 to ANSI mode ;[30c] mov ax, ds mov cs:mndseg, ax ;Save the data segment somewhere in CSEG. push ds ;Save the data segment. mov ax, 0 mov ds, ax ;We want DSEG = 0. cli ;Turn off interrupts. mov bx, .mnoff ;[33] Get original interrupt offset. mov es, .mnseg ;[33] Get original interrupt segment. mov ax, offset mnint;Point to the interrupt routine offset. mov .mnoff, ax ;Put in the main port interrupt offset addr. mov ax, cs ;Get our code segment. mov .mnseg, ax ;Put in the main port interrupt segment addr. sti ;Restore interrupts. pop ds ;Restore data segment. mov mnoldo, bx ;[33] Stash original serial interrupt offset. mov mnolds, es ;[33] Stash original segment. mov dx, mnstat ;Point to status port. mov al, 18H out dx, al ;Reset the port. mov al, 14H out dx, al ;Select register 4. mov al, 44H ;16X clock, 1 stop bit, no parity. out dx, al mov al, 13H out dx, al ;Select register 3. mov al, 0C1H ;8 bits/char, RX enable. out dx, al mov al, 15H out dx, al ;Select register 5. mov al, 0EAH ;8 bits/char, TX enable, RTS and DTR. out dx, al mov al, 11H out dx, al ;Select register 1. mov al, 18H out dx, al ;Enable interrupt processing on this port. mov dx, mnctrl ;point to comm control port mov al, 0F0H ;set RTS & DTR high out dx, al mov al,defbd ; [35a] Get default baud mov dl,al ; Save it mov cl,4 ; Move low nibble into high shl al,cl ; or al,dl ; Replace low nibble out 6,al ; Write to baud control port mov baudrt,al ; Save it for show baud ;[35a] end ret serfin: call clrscr ;[19b] clear screen ;[30c] ret ;Nothing to deinitialize on Rainbow. ; This routine clears the serial port input buffer. It is called to ; clear out excess NAKs that can result from server mode operation. cfibf: mov mnchrn, 0 ;Say no characters in the buffer. mov mnchip, offset mnchrs-1+mnchnd ;Reset input pointer. mov mnchop, offset mnchrs-1+mnchnd ;Reset output pointer. ret DSEG $ mnchnd equ 256 ;[19a] Size of circular buffer. mntrg1 equ 64 ;[19a] Low trigger point for Auto XON/XOFF mntrg2 equ 192 ;[19a] High trigger point for Auto XON/XOFF floctl db 1 ;[19a] If floctl=floxon do Auto XON/XOFF logic xofsnt db 0 ;[19a] set if XOFF was sent xofrcv db 0 ;[19a] set if XOFF was recieved mnchrn DW 0 ;[19a] Number of chars in the buffer. mnchrs RB mnchnd ;Circular character buffer for input. mnchip DW mnchrs-1+mnchnd ;Input pointer into character buffer. mnchop DW mnchrs-1+mnchnd ;Output pointer into character buffer. mnoldo RW 1 ;[33] CPM's 7201 interrupt vector offset mnolds RW 1 ;[33] and segment. rw 32 ;Interrupt stack ;[28e] mnstk dw 0 ;bottom of stack ;[28e] baudrt rb 1 ;Current baud rate CSEG $ ; The following routines do the SET and SHOW for the machine dependent ; features of Kermit. At present there are only two: baud rate setting ; and port selection. ; This is the SET BAUD rate subcommand (not implemented in Rainbow) bdset: mov ah, cmcfm call comnd ;Get a confirm. jmp bdset1 ; Didn't get a confirm. jmp bdset2 ; [35b] Go and ask for baud bdset1: mov dx, offset ermes3 ; Tell user unknown command option call tcrmsg jmp rskp bdset2: mov dx, offset bdlst ; Print list of baud speeds call tmsg bdstrt: mov cl,1 ; Get a response from keyboard int 224 and al,5fH ; Force upper case cmp al,'A' jnb bdset3 ; AL >= 'A'... jmp bdset4 ; Response too low bdset3: cmp al,'P' jle bdset5 ; AL <= 'P'... bdset4: mov dx,offset rtrmss ; Incorrect response so delete char call tmsg ; typed and make keyboard beep jmp bdstrt ; Go and try again... bdset5: sub al,'A' ; Convert to internal baud rate code mov dl,al ; Save AL mov cl,4 ; Move lower nibble into upper shl al,cl or al,dl ; replace lower nibble out 6,al ; Write to baud port mov baudrt,al ; Save for SHOW BAUD ;[35b] end jmp rskp ; This is the SET PORT subcommand (not implemented in Rainbow) prtset: mov ah, cmcfm call comnd ;Get a confirm. jmp $+3 ; Didn't get a confirm. mov dx, offset infms6 ;Tell user it's not implemented call tcrmsg jmp rskp ; The following procedures implement the SHOW command for the system ; dependent features of baud rate and port selection. ; SHOW BAUD command shobd: mov dx, offset bdst ;[35c] Print 'Baud rate:' call tcrmsg mov al,baudrt ; Get current baud rate and al,0FH ; Mask off high nibble mov cl,6 ; Multiply by 6 mul cl add ax, offset bdmss2 ; Set up DX as an offset into mov dx,ax ; the list of baud strings call tmsg ; Print the string ret ; ;[35c] end shoprt: ret ;Port selection not implemented. ; The following routines do screen control. These are isolated here because ; the screen control sequences are likely to vary from system to system, even ; though the Rainbow and APC (the only systems implemented to date) both use ; ANSI sequences for this purpose. CSEG $ ; POSCUR - positions cursor to row and col (each 1 byte) pointed to by dx. poscur: mov bx, dx ;Do ANSI cursor positioning. mov cl, 10 mov al, [bx] ;Get row value sub ah, ah div cl ;units digit in ah, tens digit in al add ax, '00' ;Convert both to ASCII mov word ptr anspos+2, ax ;Save reversed (al,ah) mov al, 1[bx] ;Do same for column value sub ah, ah div cl add ax, '00' mov word ptr anspos+5, ax mov dx, offset anspos ;Print cursor positioning string. call tmsg ret ; CLRSCR - homes cursor and clears screen. clrscr: mov dx, offset anscls call tmsg ret ; CLRLIN - clears from cursor to end of line. clrlin: mov dl, cr ;Go to beginning of line call bout clreol: mov dx, offset ansclr ;Clear from cursor to end of line call tmsg ret ; REVON - turns on reverse video display revon: mov dx, offset ansron call tmsg ret ; REVOFF - turns off reverse video display revoff: mov dx, offset ansrof call tmsg ret ; BLDON - turns on bold (highlighted) display bldon: mov dx, offset ansbon call tmsg ret ; BLDOFF - turns off bold (highlighted) display bldoff: mov dx, offset ansbof call tmsg ret ; ANSMOD - enters ANSI mode from VT52 mode ansmod: mov dx, offset ansion call tmsg ret DSEG $ anspos db esc,'[00;00H$' ;Position cursor to row and column anscls db esc,'[H',esc,'[J$' ;Home cursor and clear screen ansclr db esc,'[K$' ;Clear from cursor to end of line ansron db esc,'[7m$' ;Turn on reverse video ansrof db esc,'[m$' ;Turn off reverse video ansbon db esc,'[1m$' ;Turn on bold (highlight) display ansbof db esc,'[m$' ;Turn off bold display ansion db esc,'<$' ;Enter ANSI mode ; Here tab expansion is done if necessary. If not, just return retskp. CSEG $ dotab: jmp rskp DSEG $ delstr db ' ',10O,10O,'$' ;Delete string. system db ' DEC Rainbow-100$' bdlst db esc,'[H',esc,'[J','Baud rates available :' ;[35b] Baud rate list db cr,lf,cr,lf,' A) 50' ;for SET BAUD db cr,lf,' B) 75' db cr,lf,' C) 110' db cr,lf,' D) 134.5' db cr,lf,' E) 150' db cr,lf,' F) 200' db cr,lf,' G) 300' db cr,lf,' H) 600' db cr,lf,' I) 1200' db cr,lf,' J) 1800' db cr,lf,' K) 2000' db cr,lf,' L) 2400' db cr,lf,' M) 3600' db cr,lf,' N) 4800' db cr,lf,' O) 9600' db cr,lf,' P) 19200' db cr,lf,cr,lf,'Enter choice > $' rtrmss db 8,' ',8,7,'$' ;[35b] Delete char and beep string bdmss2 db '50 $' ;[35c] Used by SHOW BAUD option db '75 $' db '110 $' db '134.5$' db '150 $' db '200 $' db '300 $' db '600 $' db '1200 $' db '1800 $' db '2000 $' db '2400 $' db '3600 $' db '4800 $' db '9600 $' db '19200$'