; * * * * * * * * * * * * * * * version 2.9 * * * * * * * * * * * * * * * ; [35] Modify the Rainbow 100 version to handle the Victor 9000/Sirius ; Eric Zurcher, Utah State University, Logan, Utah (REHABIV@USU.BITNET) ; 07/07/86 ; * * * * * * * * * * * * * * * 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 Victor 9000. CSEG $ ; Clock rate *10 for timing loops ;[19g] clckrt equ 48 ;[19g] 4.8 Mhz ;[35a] begin ; Offsets of memory-mapped Victor "ports" within segment 0E000H mdmseg EQU 0E000H ;Segement for memory-mapped 7201 of Victor mnstat EQU 042H ;Status port A. mndata EQU 040H ;Data port A. mnctl1 EQU 000H ;Interrupt controller. mnctl2 EQU 001H ;Interrupt controller. mnclk1 EQU 023H ;For setting baud rate mnclk2 EQU 020H ; " ;[35a] end ; Interrupt vector locations. These are all in data segment 0. mnoff EQU 84H ;Main data port interrupt routine offset. [35] mnseg EQU 86H ;Main data port interrupt routine segment. [35] 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. ;[35b] begin outchr: push es push si mov dx, mdmseg mov es, dx mov si, mndata mov es:[si], al pop si pop es ret ;[35b] end ; 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 ;[35c] begin push es push si mov dx, mdmseg mov es, dx mov si, mnstat mov al, es:[si] test al, output pop si pop es ;[35c] end 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 mov dx, mdmseg ;[35] mov es, dx ;[35] call mnproc ;Process the character. ;[35d] begin mov si, mnctl1 ;Point to the interrupt controller mov al, 061H mov es:[si], al ;Re-enable interrupts mov si, mnstat ;Get the status port. mov al, 38H mov es:[si], al ;Tell the port we finished with the interrupt. ;[35d] end 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 si, mnstat ;[35] Check port status mov al, es:[si] ;[35] 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. ;[35e] begin ;Unlike the Rainbow, Victor CP/M does not normally handle these interrupts. ;Let's just return, lest we wreak havoc... ; pushf ; Save flags, like an int. ; callf dword ptr mnoldo ; Call CPM's routine. ;[35e] end ret ; Now back to MNINT. ;[33] End addition ;[35f] begin mnpro2: mov al, 1 ;Point to RR1. mov es:[si], al mov al, es:[si] ;Read RR1. mov ah, al ;Save it. mov al, 30H ;Reset any errors. mov es:[si], al mov si, mndata mov al, es:[si] ;Read the char. ;[35f] end 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: ; ;[35g] begin push es ;save registers push si mov dx, mdmseg ;Point to "port" segment mov es, dx mov si, mnstat ;status reg. address for port mov al, 15H ;select reg. 5 mov es:[si], al mov al, 0FAH ;8 bits, TX, Break, RTS, & DTR mov es:[si], al ;Turn Break on mov ax, 275 ;.. for 275 millisec's [34] call mswait ; [34] mov al, 15H ;select reg. 5 mov es:[si], al mov al, 0EAH ;same as above without Break mov es:[si], al ;turn it off pop si pop es ret ; [19e] end ;[35g] 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][35] removed cmp mninit, 0FFH je serin2 mov mninit, 0FFH 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. pop ds ;Restore data segment. mov mnoldo, bx ;[33] Stash original serial interrupt offset. mov mnolds, es ;[33] Stash original segment. ;[35h] begin push es ;Save registers push si mov dx, mdmseg ;Get "port" segment mov es, dx mov si, mnstat ;Point to status port. mov al, 18H ;Reset the port. mov es:[si], al mov al, 14H ;Select register 4. mov es:[si], al mov al, 48H ;16X clock, 1.5 stop bits, no parity. mov es:[si], al mov al, 13H ;Select register 3. mov es:[si], al mov al, 0C1H ;8 bits/char, RX enable. mov es:[si], al mov al, 15H ;Select register 5. mov es:[si], al mov al, 0EAH ;8 bits/char, TX enable, RTS and DTR. mov es:[si], al mov al, 11H ;Select register 1. mov es:[si], al mov al, 18H ;Enable interrupt processing on this port. mov es:[si], al mov si, mnctl2 ;point to 8259 interrput controller mov al,es:[si] and al, 0FDH ;Enable interrupt processing mov es:[si], al mov si, mnctl1 mov al, 061H mov es:[si], al ;Clears outstanding requests sti ;Restore interrupts. pop si pop es call stbaud serin2: ret serfin: call clrscr ;[19b] clear screen ;[30c] cmp mninit, 0FFH jne serfn2 mov mninit, 0 cli push es push si mov dx, mdmseg ;Victor mov es, dx ;Victor mov si, mnctl2 ;point to 8259 interrput controller mov al,es:[si] ;Enable interrupt processing or al, 02H mov es:[si], al mov si, mnstat mov al, 01 mov es:[si], al mov al, 00 mov es:[si], al mov ax, mnoldo ;[33] Get original interrupt offset. mov es, mnolds ;[33] Get original interrupt segment. push ds ;Save the data segment. mov dx, 0 mov ds, dx ;We want DSEG = 0. mov .mnoff, ax ;Put in the main port interrupt offset addr. mov .mnseg, es ;Put in the main port interrupt segment addr. pop ds ;Restore data segment. pop si pop es sti serfn2: ret ;[35h] end ; 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. mninit DB 0 ;set to 0FFH if initialization has been done 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. ;[35i] begin ; This is the SET BAUD rate subcommand ; set the baud rate stbaud: mov al,mnbaud ;get the baud rate information cmp al,15 ;check for valid range (0-15) ja stb02 mov bx,offset baudtb;get address of baud rate table add al,al ;compute word offset mov ah,0 add bx,ax push es push si mov ax, mdmseg mov es, ax mov si, mnclk1 mov al, 036H mov es:[si], al mov si, mnclk2 mov ax, [bx] mov es:[si], al mov es:[si], ah pop si pop es stb02: ret dseg $ ; Serial port default parameters mnbaud db 13 ;9600 baud ; Interval Timer values (assumes 16x baud rate mode) baudtb dw 061Bh ;50 baud 0 dw 0412h ;75 baud 1 dw 02C6h ;110 baud 2 dw 0245h ;135 baud 3 dw 0209h ;150 baud 4 dw 0104h ;300 baud 5 dw 0082h ;600 baud 6 dw 0041h ;1200 baud 7 dw 002Bh ;1800 baud 8 dw 0027h ;2000 baud 9 dw 0021h ;2400 baud 10 dw 0016h ;3600 baud 11 dw 0010h ;4800 baud 12 dw 0008h ;9600 baud 13 dw 0004h ;19200 baud 14 dw 0002h ;38400 baud 15 CSEG $ bdset: mov dx, offset bdtab mov bx, offset bdhlp mov ah, cmkey call comnd jmp r mov temp1, bx mov ah, cmcfm call comnd ;Get a confirm. jmp r ; Didn't get a confirm. mov bx, temp1 mov mnbaud, bl ;Set the baud rate table index. call stbaud jmp rskp ;[35i] end ;This is the SET PORT subcommand (not implemented in Victor at this time) 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. ;[35j] begin shobd: mov dx, offset bdst ;Baud rate string. call tcrmsg mov al, mnbaud ;Print the keyword corresponding to the mov bx, offset bdtab ; current value of mnbaud. call tabprt ret DSEG $ bdtab db 16 ;Thirteen entries ;[6] begin db 3,'110$' dw 0002H db 4,'1200$' dw 0007H db 3,'135$' dw 0003H db 3,'150$' dw 0004H db 4,'1800$' dw 0008H db 5,'19200$' dw 000EH db 4,'2000$' dw 0009H db 4,'2400$' dw 000AH db 3,'300$' dw 0005H db 4,'3600$' dw 000BH db 5,'38400$' dw 000FH db 4,'4800$' dw 000CH db 2,'50$' dw 0000H db 3,'600$' dw 0006H db 2,'75$' dw 0001H db 4,'9600$' dw 000DH ;[6] end bdhlp: db cr,lf,' 50 110 150 600 1800 2400 4800 19200' db cr,lf,' 75 135 300 1200 2000 3600 9600 38400$' CSEG $ ;[35j] 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. ;[35k] begin poscur: mov si, dx mov dx, offset anspos ; move prefix string mov cl, prstr int bdos xor dx,dx mov dl, [si] add dl,01FH ; this is the row mov cl,dconio ; no checking please int bdos mov dl, 1[si] add dl,01FH ; this is the column mov cl,dconio int bdos ret ;[35k] end ; 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 ;[35l] begin ; Just keep Victor in VT52 (actually Heath-19) mode ansmod: ;mov dx, offset ansion ;call tmsg ret DSEG $ anspos db esc,'Y$' ;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,'p$' ;Turn on reverse video ansrof db esc,'q$' ;Turn off reverse video ansbon db esc,'($' ;Turn on bold (highlight) display ansbof db esc,')$' ;Turn off bold display ansion db '$' ;Enter ANSI mode ;[35l] end ; 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 ' Victor 9000$' ;[35]