; * * * * * * * * * * * * * * * version 2.7 * * * * * * * * * * * * * * * ; Tektronix 4170 version - TransEra Corporation ; Robert Raymond, 3707 North Canyon Road, Building 4, Provo, UT 84601 ; [31c] set default baud to 9600 ; [31b] Changes made for 4170 port hardware ; [31a] Use ansi routines from 86keri.rb ; * * * * * * * * * * * * * * * version 2.7 * * * * * * * * * * * * * * * ; [30d] Add SET PORT command, currently unimplemented. ; [30c] Isolate all machine dependencies in KERIO. ; [30a] Add keyboard DEL key alteration for APC ; 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. ; Here are the I/O routines for the TEK 4170. CSEG $ ; Clock rate *10 for timing loops ;[19g] clckrt equ 49 ;[19g] 4.9 Mhz ;[20b] ; Interrupt vector locations, in data segment 0 mnioff equ 200h ;HO_In_Int interrupt offset ;[31b] mniseg equ 202h ;HO_In_Int interrupt segment ;[31b] ; ; equates for 2661B chip -- host initialization parameters ; HO_Data equ 0e0h HO_Stat equ 0e2h HO_Mode equ 0e4h HO_Cmd equ 0e6h HOCLKRATE equ 02h ;sets async mode, 16x clock on host port HOSB2 equ 0c0h ;two stop bits HOSB1 equ 040h ;one stop bits HOEVEN equ 020h ;select even parity, not odd parity HOPENB equ 010h ;enable parity on the host HOSTART equ 037h ;RTS, DTR set, Rx & Tx enabled, reset errors HOBREAK equ 4 ; force break HO_Thre equ 01h ;one on this indicates thre, 0 is thr busy HO_DRdy equ 02h ;one indicates data ready, 0 no data outlmt EQU 1000H ;Number of times to check output status ; before giving up on send. ;[20d] ; Test if port is ready to send next char. Returns RSKP 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,HO_Stat ;[31b] begin in al,dx test al,HO_Thre ;transmit holding register empty pop ax jnz outwt2 ret outwt2: jmp rskp ;[31b] end ; Output data to port. Trashes DX and prints char in AL. outchr: mov dx,HO_Data ;[31b] begin out dx,al ; don't ask me why we do this: mov al,HO_START ;rts and start scanning out HO_Cmd,al ret ;[31b] end ; Output the character in AL, checking first to make sure the port is clear. prtout: call dopar ;[par] set parity 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 ; Test if data is available from port. instat: cmp mnchrn,0 ;Any chars in the buffer? jnz inst2 ret inst2: jmp rskp ; Input data from port. Preserves all registers 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 lea bx,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 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 lea sp, mnstk push ds ;Save all registers. push es push bp push di push si push dx push cx push bx mov ds, ax ;Get our data segment address. call mnproc ;Process the character. 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 serial port ; and puts it in the ring buffer. mnproc: mov dx,HO_Stat in al,dx ;Get the port status. [31b] start test al,HO_DRdy ;Is a character waiting? jnz mnpro2 ; Yes, go take care of it. ret ; No, just a false alarm. mnpro2: mov dx,HO_Data in al,dx ;Read the char. [31b] 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 lea bx,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 ; [31b] start prtbrk: mov dx,HO_Cmd ;break goes to command port mov al,HO_START+HO_BREAK ;add break to normal command out dx,al mov cx, 25000 ;sit for a while prtbk1: loop prtbk1 mov al,HO_START ;normal command,RTS & DTR high, Rx & Tx enabled out dx,al ;return to normal setting ret ; serini - This routine initializes all devices that need it. ; Called at the start of the program. serini: cmp mninit,0FFh ; must only do this initialization once je serin2 mov mninit,0FFh call ansmod ; switch from tek mode to ansi mode push es ;code could be added here ;to tell the interrupt controller to diable host interrupt mov ax,ds ;save data segment in cseg mov cs:mndseg,ax ; for use by the interrupt handler mov ax,0 ;point to zero page to replace mov es,ax ;the sio interrupt vector mov ax,es:.mniseg ;after first saving the current vector mov mnxseg,ax mov ax,es:.mnioff mov mnxoff,ax cli mov ax,cs mov es:.mniseg,ax mov ax,offset mnint mov es:.mnioff,ax sti call stmode ;set mode & baud to defaults call stbaud ;enable transmission of data mov al,HOSTART ;DTR high, Rx & Tx enabled, reset error out HO_Cmd,al in al,HO_Data ;dummy read to clear buffer ;code could be added here ;to tell the interrupt controller to re-enable host interrupt mov dx,0ebh mov al,24h ;turn on host read interrupt out dx,al ;[31b] end pop es serin2: ret ; serfin - this routine is used to "undo" what serini has done, called ; just before exiting back to cp/m. serfin: call clrscr ;[19b] clear screen ;[30c] cmp mninit,0FFh ;check if initialization has been done jne serfn2 ;if not, don't de-initialize mov mninit,0 push es cli ;code could be added here to assure the interrupt controller ;is restored to the state is was in when kermit started mov ax,0 mov es,ax mov ax,mnxseg ;restore sio interrupt vector mov es:.mniseg,ax mov ax,mnxoff mov es:.mnioff,ax sti pop es serfn2: ret ; 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 ; set the parity, number of data bits, and number of stop bits stmode: mov dx,HO_Cmd ;[31b] start mov al,0 ;reset out dx,al in al,dx ;reset mode1/2 sequencer mov dx,HO_Mode mov al,HOCLKRATE ;init async mode, 16x baud, no parity add al,HOSB1 ;1 stop bit add al,0ch ;8 data bits out dx,al ;[31b] end ret ; set the baud rate stbaud: mov dx,HO_Cmd ;[31b] start in al,dx ;reset mode1/2 sequencer mov dx,HO_Mode in al,dx ;get mode1 register ; can the next 4 lines be skipped? mov ah,al ;save it in al,dx ;get mode2 register to reset sequence mov al,ah ; write back old contents of mode 1 out dx,al ; next out will set mode 2 - baud rate mov al,mnbaud ;get the baud rate information cmp al,15 ;check for valid range (0-15) ja stb02 or al, 70h ;internal baud clock, 16x out dx,al ;[31b] end stb02: ret dseg $ ; Serial port default parameters mnbaud db 0dh ;9600 baud [31c] mninit db 0 ;set to 0FFh if initialization has been done mnxseg dw 0 ;system host interrupt vector mnxoff dw 0 mnchnd equ 512 ;Size of circular 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. mnchrn dw 0 ;Number of chars in the buffer. mntrg1 equ 128 ;[19a] Low trigger point for Auto XON/XOFF mntrg2 equ 384 ;[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 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 bdset: lea dx, bdtab lea bx, 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 ; This is the SET PORT subcommand (not implemented in TEK) prtset: mov ah, cmcfm call comnd ;Get a confirm. jmp $+3 ; Didn't get a confirm. lea dx, 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: lea dx, bdst ;Baud rate string. call tcrmsg mov al, mnbaud ;Print the keyword corresponding to the lea bx, bdtab ; current value of mnbaud. call tabprt ret shoprt: ret ;Port selection not implemented. DSEG $ bdtab db 16 ; 16 entries ;[31d] begin db 3,'110$' dw 0003H db 4,'1200$' dw 0008H db 3,'135$' dw 0004H db 3,'150$' dw 0005H db 4,'1800$' dw 0009H db 5,'19200$' dw 000EH db 4,'2000$' dw 000AH db 4,'2400$' dw 000BH db 3,'300$' dw 0006H db 5,'38400$' dw 000FH db 2,'45$' dw 0000H db 4,'4800$' dw 000CH db 2,'50$' dw 0001H db 3,'600$' dw 0007H db 2,'75$' dw 0002H db 4,'9600$' dw 000DH bdhlp db cr,lf,' 45 75 135 300 1200 2000 4800 19200' db cr,lf,' 50 110 150 600 1800 2400 9600 38400$' ;[31d] end ;[31a] begin -- to end of file ; 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 lea dx, anspos ;Print cursor positioning string. call tmsg ret ; CLRSCR - homes cursor and clears screen. clrscr: lea dx, anscls call tmsg ret ; CLRLIN - clears from cursor to end of line. clrlin: mov dl, cr ;Go to beginning of line call bout clreol: lea dx, ansclr ;Clear from cursor to end of line call tmsg ret ; REVON - turns on reverse video display revon: lea dx, ansron call tmsg ret ; REVOFF - turns off reverse video display revoff: lea dx, ansrof call tmsg ret ; BLDON - turns on bold (highlighted) display bldon: lea dx, ansbon call tmsg ret ; BLDOFF - turns off bold (highlighted) display bldoff: lea dx, ansbof call tmsg ret ; ANSMOD - enters ANSI mode from Tek mode ansmod: lea dx, 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,'%!1$' ;SelectCode 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 ' Tektronix 4170$'