; * * * * * * * * * * * * * * * 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 <Oc.Garland%CU20B@Columbia-20> ; * * * * * * * * * * * * * * * 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] ; 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 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] 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 $+3 ; Didn't get a confirm. mov dx, offset infms6 ;Tell user it's not implemented call tcrmsg 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. shobd: ret ;Baud rate selection not implemented. 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$'