<<< c86cmd.a86 >>> ; * * * * * * * * * * * * * * * version 2.8 * * * * * * * * * * * * * * * ; [32c] remove check for 0-length filename ; [32b] fix minor bugs ; [32a] fix prompt to show default drive and user ; RonB, 09/13/84 ; * * * * * * * * * * * * * * * version 2.7 * * * * * * * * * * * * * * * ; [fdc] Fix small glitch w/CMLEVL that issued msg spuriously sometimes. ; [] Introduced CMLEVL flag to intercept "empty" command-options ; Reset by PRSERR, SET in 86KERMIT KERMIT: ; B.E.; EIBEN at DEC-MARLBORO 2-May-84 ; [30c] Isolate ANSI escape sequences for machine independence. ; [30b] Make DEL work like BS and ^X like ^U in command input. ; RonB, 04/18/84 ; * * * * * * * * * * * * * * * version 2.6 * * * * * * * * * * * * * * * ; [28d] Improve filename special character processing ; RonB, 03/27/84 ; [25] Move logic for "seteol" and "escape" (from KERSYS) into here so those ; routines need not use internal CMD routines and variables. For this ; purpose add 2 parse routines and codes: "cmcha" and "cmnum". The point ; of this is to keep calls to CMD modular since I want to eventually ; replace the whole thing. ; R. Garland 9-Mar-1984 ; * * * * * * * * * * * * * * * version 2.1 * * * * * * * * * * * * * * * ; [9] Fix filename parsing, and add wildcard ability. ; RonB,12/26/83 ; [8] Show choices for ambiguous keywords, finish keyword on '?' ; RonB,12/26/83 ; * * * * * * * * * * * * * * * version 2.0 * * * * * * * * * * * * * * * ; This module contains all the routines and storage necessary for the ; command parser. The command parser approximates that of the Tops-20 ; COMND% JSYS. This code is adapted from the IBM PC Kermit code which ; was adapted from the CP/M-80 Kermit code. ; COMND definitions. cmcfm equ 01H cmkey equ 02H cmifi equ 03H cmofi equ 04H cmtxt equ 05H cmcha equ 06H cmnum equ 07H DSEG $ ; Resume the data segment. ; COMND storage. cmer00 db bell,'?Program error -- Invalid COMND call$' cmer01 db bell,'?Ambiguous command$' cmer02 db bell,'?Illegal input file spec$' cmer03 db bell,'?Unrecognized instruction$' cmer04 db bell,'?Invalid command or operand$' cmer05 db bell,'?Missing command-option$' cmin00 db ' Confirm with carriage return$' cmin01 db ' Input file spec (possibly wild) $' cmin02 db ' One of the following:$' ;[8] cmlevl db 0 ;0 at main-level, 1 otherwise cmstat db 0 ;What is presently being parsed. cmaflg db 0 ;Non-zero when an action char has been found. cmccnt db 0 ;Non-zero if a significant char is found. cmsflg db 0 ;Non-zero when the last char was a space. cmostp dw 0 ;Old stack pointer for reparse. cmrprs dw 0 ;Address to go to on reparse. cmprmp dw 0 ;Address of prompt. cmptab dw 0 ;Address of present keyword table. cmhlp dw 0 ;Address of present help. cmdbuf rb 80H ;Buffer for command parsing. cmfcb dw 0 ;Pointer to FCB. cmfcb2 dw 0 ;Pointer to position in FCB. cmcptr dw 0 ;Pointer for next char input. cmdptr dw 0 ;Pointer into the command buffer. cmsiz dw 0 ;Size info of user input. cmkptr dw 0 ;Pointer to keyword. cmsptr dw 0 ;Place to save a pointer. cmchr db 0 ;Save char when checking ambiguity. cmten dw 10 ;the number "10" spchar db '!#$%&()+-/@\^`|~0000' ;Valid special characters ;[8][28d] ; This set of routines provides a user oriented way of parsing ; commands. It is similar to that of the COMND JSYS in TOPS-20. CSEG $ ;Resume coding. ; This routine prints the prompt in DX and specifies the reparse ; address. prompt: pop bx ;Get the return address. push bx ;Put it on the stack again. mov cmrprs, bx ;Save as addr to go to on reparse. mov bx, 0 ;Clear out register. add bx, sp ;Get the present stack pointer. mov cmostp, bx ;Save for later restoral. mov cmprmp, dx ;Save pointer to the prompt. mov bx, offset cmdbuf mov cmcptr, bx ;Initialize the command pointer. mov cmdptr, bx mov cmaflg, 0 ;Zero the flags. mov cmlevl, 0 ;[fdc]Including the level-flag mov cmccnt, 0 mov cmsflg, 0FFH reprompt: ;[32a] begin call tcrlf repmt2: mov dx, cmprmp ;Print the prompt. call tmsg mov dl,defdrv ;Print the default drive and user add dl,'A' call bout mov al,defusr cbw or ax,ax ;Only print the user number if nonzero jz repmt3 call nout repmt3: mov dl,'>' call bout ;[32a] end ret ; This address is jumped to on reparse. repars: mov sp, cmostp ;new sp <-- old sp mov bx, offset cmdbuf mov cmdptr, bx mov cmsflg, 0FFH mov bx, cmrprs ;Get the reparse address. jmp bx ;Go there. ; This address can be jumped to on a parsing error. prserr: mov ah, cmlevl ;What level are we in? cmp ah, 0 ; jz prser1 ;skip error-message mov dx, offset cmer05 ;we're out of main-commands call tcrmsg ;and got an empty option mov cmlevl, 0 ;reset level-flag prser1: mov sp, cmostp ;Set new sp to old one. mov bx, offset cmdbuf mov cmcptr, bx ;Initialize the command pointer. mov cmdptr, bx mov cmaflg, 0 ;Zero the flags. mov cmccnt, 0 mov cmsflg, 0FFH call reprompt ;[32a] mov bx, cmrprs jmp bx ; This routine parses the specified function in AH. Any additional ; information is in DX and BX. ; Returns +1 on success ; +4 on failure (assumes a JMP follows the call) comnd: mov cmstat, ah ;Save what we are presently parsing. call cminbf ;Get chars until an action or a erase char. mov ah, cmstat ;Restore 'ah' for upcoming checks. cmp ah, cmcfm ;Parse a confirm? jz cmcfrm ;Go get one. cmp ah, cmkey ;Parse a keyword? jnz cm1 jmp cmkeyw ;Try and get one. cm1: cmp ah, cmifi ;Parse an input file spec? jnz cm2 jmp cmifil ;Go get one. cm2: cmp ah, cmofi ;Output file spec? jnz cm3 jmp cmofil ;Go get one. cm3: cmp ah, cmtxt ;Parse arbitrary text. jnz cm4 jmp cmtext cm4: cmp ah, cmcha ;[25] parse a single character? jnz cm5 ;[25] jmp cmchar ;[25] go do it. cm5: cmp ah, cmnum ;[25] parse a (decimal) number? jnz cm99 ;[25] jmp cmnumr ;[25] go do it. cm99: mov dx, offset cmer00 ;"?Unrecognized COMND call" [25] call tcrmsg ret ; This routine gets a confirm. cmcfrm: call cmgtch ;Get a char. cmp ah, 0 ;Is it negative (a terminator;a space or ;a tab will not be returned here as they ;will be seen as leading white space)? js cmcfr0 ret ;If not, return failure. cmcfr0: and ah, 7FH ;Turn off the minus bit. cmp ah, esc ;Is it an escape? jne cmcfr2 mov dl, bell ;Get a bell. call bout ;Output the char. mov cmaflg, 0 ;Turn off the action flag. mov bx, cmcptr ;Move the pointer to before the escape. dec bx mov cmcptr, bx mov cmdptr, bx dec cmccnt ;Decrement the char count. jmp cmcfrm ;Try again. cmcfr2: cmp ah, '?' ;Curious? jne cmcfr3 mov dx, offset cmin00 ;Print something useful. call tmsg call reprompt ;Reprint the prompt ;[32a] mov bx, cmdptr ;Get the pointer into the buffer. mov ah, '$' ;Put a $ there for printing. mov [bx], ah mov bx, cmcptr dec bx ;Decrement & save the buffer pointer. mov cmcptr, bx mov dx, offset cmdbuf call tmsg mov cmaflg, 0 ;Turn off the action flag. jmp repars ;Reparse everything. cmcfr3: ;[8] begin cmcfr4: jmp rskp ; This routine parses a keyword from the table pointed ; to in DX. The format of the table is as follows: ; ; addr: db n ;Where n is the # of entries in the table. ; db m ;M is the size of the keyword. ; db 'string$' ;Where string is the keyword. ; dw ab ;Where ab is data to be returned. ; ; The keywords must be in alphabetical order. cmkeyw: mov cmhlp, bx ;Save the help string. mov cmptab, dx ;Save the beginning of keyword table. mov bx, dx mov ch, [bx] ;Get number of entries in table. inc bx mov dx, cmdptr ;Save command pointer. mov cmsptr, dx ;Save pointer's here. cmky1: cmp ch, 0 ;Any commands left to check? jne cmky2 ret cmky2: dec ch mov cl, 0 ;Keep track of how many chars read in so far. call cmgtch ;Get a char. cmp ah, 0 ;Do we have a terminator? jns cmky2x jmp cmky4 ;Negative number means we do. cmky2x: inc bx ;Point to first letter of keyword. inc cl ;Read in another char. mov al, [bx] cmp ah, 'a' ;Less than a? jl cmky21 ;If so, don't capitalize. cmp ah, 'z'+1 ;More than z? jns cmky21 and ah, 137O ;Capitalize the letter. cmky21: cmp ah, al je cmky3 jg cmky2y jmp cmky41 ;Fail if ah preceeds al alphabetically. cmky2y: jmp cmky6 ;Not this keyword - try the next. cmky3: inc bx ;We match here, how 'bout next char? mov al, [bx] cmp al, '$' ;End of keyword? jne cmky3x jmp cmky7 ;Succeed. cmky3x: mov dl, al ;Save al's char here. call cmgtch inc cl ;Read in another char. mov al, dl cmp ah, 'a' jl cmky31 cmp ah, 'z'+1 jns cmky31 and ah, 137O cmky31: cmp ah, esc+80H ;Escape Recognition (escape w/minus bit on)? je cmky3y cmp ah, '?'+80H ;A question mark? je cmky3y cmp ah, ' '+80H ;A space? je cmky3y cmp ah, cr+80H ;Carriage return? je cmky3y jmp cmky38 cmky3y: mov cmkptr, bx ;Save bx here. mov cmsiz, cx ;Save size info. mov cmchr, ah ;Save char for latter. call cmambg ;See if input is ambiguous or not. jmp cmky32 ;Succeeded (not ambiguous). mov ah, cmchr cmp ah, esc+80H ;Escape? ; Display keyword choices and reparse if ambiguous ;[8] begin je cmky3a cmp ah, ' '+80H ;Space? jne cmky3b cmky3a: dec cmdptr ;If so, back up over it. cmky3b: mov dx, offset cmin02 ;'One of the following:' call tcmsgc mov bx, cmkptr ;Find beginning of current keyword mov cx, cmsiz cmky3c: dec bx ;We are 'cl' characters into it dec cl jnz cmky3c inc bx mov cmkptr, bx ;Save beginning of keyword cmky3d: mov dl, tab ;Precede each keyword with a tab call bout mov dx,cmkptr ;and display the keyword call tmsg mov bx, cmkptr ;Move to the next keyword cmky3e: inc bx cmp byte ptr [bx], '$' jnz cmky3e add bx,4 ;Bypass '$', 2-byte return value, next length mov di, cmkptr ;Get previous keyword for comparison mov cmkptr, bx ;and save beginning of this keyword mov cx, cmsiz ;Get number of characters to match dec ch ;Are we at end of table? js cmky3g ; Yes, quit displaying mov cmsiz, cx cmky3f: dec cl jz cmky3d ;This keyword also matches to 'cl' places mov ah,[bx] ;Compare this keyword to last cmp ah,[di] jne cmky3g inc di inc bx jmps cmky3f cmky3g: jmp cmky50 ;Not equal or end of table, redisplay prompt ;[8] end cmky32: mov cx, cmsiz ;Restore info. mov bx, cmkptr ;Our place in the keyword table. cmk32a: cmp cmchr, ' '+80H ;Space? ;[8] je cmky35 cmp cmchr, cr+80H ;Carriage return? je cmky35 dec cmcptr ;Pointer into buffer of input. mov dx, cmcptr cmky33: mov ah, [bx] ;Get next char in keyword. cmp ah, '$' ;Are we done yet? jz cmky34 mov di,dx mov [di], ah inc bx inc dx inc cmccnt jmp cmky33 cmky34: push bx ;Save pointer to return value ;[8] mov ah, ' ' mov di, dx mov [di], ah ;Put a blank in the buffer. inc dx mov cmdptr, dx ;[8] begin cmp cmchr, '?'+80H ;Question mark? jne cmk34a mov ah, '?' mov di,dx mov [di], ah inc dx inc cmccnt push dx mov dl, 08H ;Erase question mark from display call bout pop dx cmk34a: mov cx, cmcptr ;Remember where we were (for printing below). mov cmcptr, dx ;Update our pointers. ;[8] end mov ah, '$' mov di, dx mov [di], ah ;Add '$' for printing. mov dx, cx ;Point to beginning of filled in data. call tmsg pop bx ;Recover pointer to return value ;[8] inc bx ;Point to address we'll need. mov bx, [bx] cmp cmchr, 0BFH ;Question mark? ;[8] begin je cmk34b mov cmaflg, 0 ;If esc, turn off action flag mov cmsflg, 0FFH ; and pretend they typed a space cmk34b: jmp rskp ;[8] end cmky35: mov ah, [bx] ;Find end of keyword. inc bx cmp ah, '$' jne cmky35 mov bx, [bx] ;Address of next routine to call. jmp rskp cmky38: cmp ah, al jne cmky6 ;Go to end of keyword and try next. jmp cmky3 cmky4: and ah, 7FH ;Turn off minus bit. cmp ah, '?' ;Need help? je cmky5 cmp ah, ' ' ;Just a space - no error. je cmky51 cmp ah, cr je cmky51 cmp ah, esc ;Ignore escape? je cmky43 cmky41: mov dx, offset cmer03 call tcrmsg jmp prserr ;Parse error - give up. cmky43: mov dl, bell ;Ring a bell. call bout mov bx, cmcptr dec bx mov cmcptr, bx mov cmdptr, bx dec cmccnt ;Don't count the escape. mov cmaflg, 0 ;Reset action flag. inc ch ;Account for a previous 'dec'. jmp cmky1 ;Start over. cmky5: mov dx,cmhlp ;Print the help text. call tcmsgc cmky50: call reprompt ;Reprint the prompt ;[32a] mov bx,cmdptr ;Get pointer into buffer. mov al, '$' mov [bx], al ;Add dollar sign for printing. mov dx, offset cmdbuf call tmsg mov bx, cmdptr ;[8] begin mov cmcptr, bx mov dx, offset cmdbuf sub bx, dx mov cmccnt, bl mov cmaflg, 0 ;Turn off the action flag. jmp repars cmky51: jmp prserr cmky6: inc bx ;Find end of keyword. mov al, [bx] cmp al, '$' jne cmky6 add bx, 3 ;Beginning of next command. mov dx, cmsptr ;Get old cmdptr. mov cmdptr, dx ;Restore. mov cmsflg, 0FFH jmp cmky1 ;Keep trying. cmky7: call cmgtch ;Get char. cmp ah, 0 js cmky71 ;Ok if a terminator. dec bx jmp cmky6 ;No match - try next keyword. cmky71: mov cmchr, ah ;[8] begin jmp cmk32a ; See if keyword is ambiguous from what the user has typed in. cmambg: cmp ch, 0 ;Any keywords left to check? jne cmamb0 ret ;If not then not ambiguous. cmamb0: inc bx ;Go to end of keyword ... mov al, [bx] ;So we can check the next one. cmp al, '$' jne cmamb0 add bx, 4 ;Point to start of next keyword. dec cl ;Don't count escape. mov dx, cmsptr ;Buffer with input typed by user. cmamb1: mov ah, [bx] ;Keyword char. mov di, dx mov al, [di] ;Input char. cmp al, 'a' ;Do capitalizing. jl cmam11 cmp al, 'z'+1 jns cmam11 and al, 137O cmam11: cmp ah, al ;Keyword bigger than input (alphabetically)? jle cmamb2 ;No - keep checking. ret ;Yes - not ambiguous. cmamb2: inc bx ;Advance one char. inc dx dec cl jnz cmamb1 jmp rskp ;Fail - it's ambiguous. ; Parse an input file spec. cmifil: mov wldflg, 0 ;Set to no wildcards. ;[9] mov bx, dx ;Get the fcb address in bx. mov cmfcb, bx ;Save it. mov ch, 0 ;Initialize char count. mov ah, 0 mov [bx], ah ;Set the drive to default to current. inc bx mov cmfcb2, bx mov cl, ' ' cmifi0: mov [bx], cl ;Blank the FCB. inc bx inc ah cmp ah, 0BH ;Twelve? jl cmifi0 cmifi1: call cmgtch ;Get another char. cmp ah, 0 ;Is it an action character. jns cmifi2 and ah, 7FH ;Turn off the action bit. cmp ah, '?' ;A question mark? jne cmif12 mov cmaflg, 0 ;Blank the action flag. ; '?' is a legal character in wildcard filenames. ;[9] begin ; Make ESC take its place by giving info instead of beeping. ;[32b] mov wldflg, 0FFH ;Say we have a wildcard. inc cmdptr jmp cmifi8 ;Accept a '?' cmif12: cmp ah, esc ;An escape? jne cmif13 dec cmdptr cmf12a: mov cmaflg, 0 ;Turn off the action flag ;[9] end dec cmcptr ;Decrement the buffer pointer. dec cmccnt ;Decrement count. mov dx, offset cmin01 ;Help message. call tmsg call reprompt ;Reprint the prompt ;[32a] mov bx, cmdptr mov al, '$' mov [bx], al ;Put in dollar sign for printing. mov dx, offset cmdbuf call tmsg jmp repars cmif13: mov ah, ch ;It must be a terminator. ; The check for 0-length filenames will be performed by the ;[32c] ; caller so as to allow the file specification to be optional. cmp ah, 0DH js cmf3y jmp cmifi9 ;If too long complain. cmf3y: jmp rskp ;Otherwise we have succeeded. cmifi2: cmp ah, '.' jne cmifi3 inc ch mov ah, ch cmp ah, 1H ;Any chars yet? jnz cmf2x jmp cmifi9 ;No, give error. cmf2x: cmp ah, 0AH ;Tenth char? js cmf2y jmp cmifi9 ;Past it, give an error. cmf2y: mov dl, 9H mov dh, 0 mov bx, cmfcb add bx, dx ;Point to file type field. mov cmfcb2, bx mov ch, 9H ;Say we've gotten nine. jmp cmifi1 ;Get the next char. cmifi3: cmp ah, ':' jne cmifi4 inc ch cmp ch, 2H ;Is it in right place for a drive? je cmif3x jmp cmifi9 ;If not, complain. cmif3x: mov ch, 0 ;Reset char count. mov bx, cmfcb2 dec bx mov ah, [bx] ;Get the drive name. cmp ah,'A' ;Make sure it's in range A-P ;[9] begin jb cmif3y cmp ah,'P' jbe cmif3z cmif3y: jmp cmifi9 cmif3z: sub ah,'@' ;Get the drive number. ;[9] end mov cmfcb2, bx mov bx, cmfcb mov [bx], ah ;Put it in the fcb. jmp cmifi1 cmifi4: cmp ah, '*' jne cmifi7 mov ah, ch cmp ah, 8H ;Is this in the name or type field? jz cmifi9 ;If its where the dot should be give up. jns cmifi5 ;Type. mov cl, 8H ;Eight chars. jmp cmifi6 cmifi5: mov cl, 0CH ;Three chars. cmifi6: mov wldflg, 0FFH ;Remember we had a wildcard. mov bx, cmfcb2 ;Get a pointer into the FCB. mov ah, '?' mov [bx], ah ;Put a question mark in. inc bx mov cmfcb2, bx inc ch mov ah, ch cmp ah, cl jl cmifi6 ;Go fill in another. jmp cmifi1 ;Get the next char. cmifi7: cmif7x: cmp ah,'0' jb cmif8x cmp ah,'9' jbe cmifi8 cmp ah,'A' jb cmif8x cmp ah,'Z' jbe cmifi8 cmp ah,'a' jb cmif8x cmp ah,'z' ja cmif8x ;[9] end and ah, 137O ;Capitalize. cmifi8: mov bx, cmfcb2 ;Get the pointer into the FCB. mov [bx], ah ;Put the char there. inc bx mov cmfcb2, bx inc ch jmp cmifi1 cmif8x: push es ;Check list of special characters mov cx, ds ; which are legal in filenames mov es, cx ;Scan uses ES register. mov di, offset spchar ;Special chars. mov cx, 20 ;Twenty of them. mov al, ah ;Char is in al. repnz scasb ;Search string for input char. cmp cx, 0 ;Was it there? pop es jnz cmifi8 cmifi9: mov dx, offset cmer02 call tcrmsg ret cmofil: jmp cmifil ;For now, the same as CMIFI. ; Parse arbitrary text up to a CR. Put chars into data buffer sent to ; the host (pointed to by BX). Return updated pointer in BX and ; input size in AH. cmtext: mov cmptab, bx ;Save pointer to data buffer. mov cl, 0 ;Init the char count. cmtxt1: call cmgtch ;Get a char. cmp ah, 0 ;Terminator? jns cmtxt5 ;Nope, put into the buffer. and ah, 07FH cmp ah, esc ;An escape? jne cmtxt2 mov dl, bell ;Ring a bell. call bout mov cmaflg, 0 ;Reset action flag. dec cmcptr ;Move pointer to before the escape. dec cmdptr dec cmccnt ;Decrement count. jmp cmtxt1 ;Try again. cmtxt2: cmp ah, '?' ;Asking a question? jz cmtx2y ;[32b] cmp ah, ' ' ;Space? ;[32b] jz cmtxt3 cmp ah, ff ;Formfeed? jne cmtx2x call clrscr cmtx2x: mov ah, cl ;Return count in AH. mov bx, cmptab ;Return updated pointer. jmp rskp cmtx2y: inc cmdptr ;[32b] cmtxt3: mov cmaflg, 0 ;Reset action flag to zero. cmtxt5: inc cl ;Increment the count. mov bx, cmptab ;Pointer into destination array. mov [bx], ah ;Put char into the buffer. inc bx mov cmptab, bx jmp cmtxt1 cminbf: push dx push bx mov cx, dx ;Save value here too. mov ah, cmaflg ;Is the action char flag set? cmp ah, 0 je cminb1 jmp cminb9 ;If so get no more chars. cminb1: inc cmccnt ;Increment the char count. call bin mov ah, al ;Keep char in 'ah'. mov bx, cmcptr ;Get the pointer into the buffer. mov [bx], ah ;Put it in the buffer. inc bx mov cmcptr, bx cmp ah, 15h ;Is it a ^U? je cmnb12 ;[30b] cmp ah, 18h ; or ^X? ;[30b] jne cminb2 cmnb12: call clrlin ;[30c] call repmt2 ;Reprint the prompt (no crlf) ;[32a] mov bx, offset cmdbuf mov cmcptr, bx ;Reset the point to the start. mov cmccnt, 0 ;Zero the count. mov dx, cx ;Preserve original value of dx. jmp repars ;Go start over. cminb2: cmp ah, 08h ;Is it a backspace? ;[30b] jz cminb3 cmp ah, 7fh ; or delete? ;[30b] jne cminb4 mov dx, offset delstr call tmsg cminb3: mov ah, cmccnt ;Decrement the char count by two. dec ah dec ah cmp ah, 0 ;Have we gone too far? jns cmnb32 ;If not proceed. mov dl, bell ;Ring the bell. call bout jmp cmnb12 ;Go reprint prompt and reparse. cmnb32: mov cmccnt, ah ;Save the new char count. mov dx, offset clrspc ;Erase the character. call tmsg mov bx, cmcptr ;Get the pointer into the buffer. dec bx ;Back up in the buffer. dec bx mov cmcptr, bx jmp repars ;Go reparse everything. cminb4: cmp ah, '?' ;Is it a question mark. jz cminb6 cmp ah, esc ;Is it an escape? jz cminb6 cmp ah, cr ;Is it a carriage return? jz cminb5 cmp ah, lf ;Is it a line feed? jz cminb5 cmp ah, ff ;Is it a formfeed? jne cminb7 call clrscr cminb5: mov ah, cmccnt ;Have we parsed any chars yet? cmp ah, 1 jnz cminb6 jmp prserr ;If not, just start over. cminb6: mov cmaflg, 0FFH ;Set the action flag. jmp cminb9 cminb7: jmp cminb1 ;Get another char. cminb9: pop bx pop dx ret cmgtch: push cx push bx push dx cmgtc1: mov ah, cmaflg cmp ah, 0 ;Is it set. jne cmgt10 call cminbf ;If the action char flag is not set get more. cmgt10: mov bx, cmdptr ;Get a pointer into the buffer. mov ah, [bx] ;Get the next char. inc bx mov cmdptr, bx cmp ah, ' ' ;Is it a space? jz cmgtc2 cmp ah, tab ;Or a tab? jne cmgtc3 cmgtc2: mov ah, cmsflg ;Get the space flag. cmp ah, 0 ;Was the last char a space? jne cmgtc1 ;Yes, get another char. mov cmsflg, 0FFH ;Set the space flag. mov ah, ' ' pop dx pop bx jmp cmgtc5 cmgtc3: mov cmsflg, 0 ;Zero the space flag. pop dx pop bx cmp ah, esc jz cmgtc5 cmp ah, '?' ;Is the user curious? jz cmgtc4 cmp ah, cr jz cmgtc4 cmp ah, lf jz cmgtc6 ;[8] cmp ah, ff je cmgtc6 ;[8] pop cx ret ;Not an action char, just return. cmgtc6: mov ah, cr ;Convert lf & ff to cr ;[8] cmgtc4: dec cmdptr cmgtc5: or ah, 80H ;Make the char negative to indicate pop cx ;it is a terminator. ret ; Parse a single character ;[25] start ; this is for setting the escape character cmchar: call cmgtch ;get a char cmp ah, 0 jns cmchr1 ;go if not negative and ah, 7FH ;turn off sign bit cmp ah, '?' ;user curious? jne cmchr0 ;no - an error mov dx, bx ;help string pointer was in bx call tmsg ;print help stuff ;[32a] call reprompt ;Reprint the prompt ;[32a] mov bx, cmdptr mov al, '$' mov [bx], al ;add a "$" to what was typed mov dx, offset cmdbuf call tmsg ;type it again dec cmcptr ;but don't leave "$" .. dec cmccnt ;in buffer mov cmaflg, 0 ;turn off action flag jmp repars ;try again cmchr0: mov dx, offset erms20 call tcrmsg ;"illegal value" error ret cmchr1: mov temp, ax call cmcfrm ;get a confirm jmp cmchr0 ;or else complain mov ax, temp mov bl, ah ;return the character jmp rskp ; parse a (decimal) number. Maximum allowed value in dx cmnumr: mov temp1, 001H ;initial multiplier of 1 mov temp2, dx ;storage for maximum mov temp, 0 ;zero running sum call cmgtch ;get a char cmp ah, 0 jns cmnum1 ;go if not negative and ah, 7FH ;turn off sign bit cmp ah, '?' ;user curious? jne cmnum0 ;no - an error mov dx, bx ;help string pointer was in bx call tmsg ;print help stuff ;[32a] call reprompt ;Reprint the prompt ;[32a] mov bx, cmdptr mov al, '$' mov [bx], al ;add a "$" to what was typed mov dx, offset cmdbuf call tmsg ;type it again dec cmcptr ;but don't leave "$" .. dec cmccnt ;in buffer mov cmaflg, 0 ;turn off action flag jmp repars ;try again call cmcfrm ;get character (or confirm) jmp cmnum1 ;got a character ;fall through - too early for confirm cmnum0: mov dx, offset erms20 call tcrmsg ;"illegal value" message ret cmnum1: sub ah, 030H ;ASCII -> binary jl cmnum0 ;too small cmp ah, 09H jg cmnum0 ;too big mov bl, ah mov bh, 0 ;get number in low part of bx mov ax, temp ;get running sum mul temp1 ;multiply by decimal place value add ax, bx ;add in this digit cmp ax, temp2 ;over the maximum jg cmnum0 ;yes - error mov temp, ax ;save running sum mov ax, temp1 ;get multiplier mul cmten ;multiply multiplier by 10 mov temp1, ax ;save it call cmcfrm ;get another character jmp cmnum1 ;not terminator - process it mov bx, temp ;get value of number jmp rskp ;return success ;[25] end <<< c86fil.a86 >>> ; * * * * * * * * * * * * * * * version 2.9 * * * * * * * * * * * * * * * ; [34c] Add sorted wildcard SENDs with starting filename ; * * * * * * * * * * * * * * * version 2.8 * * * * * * * * * * * * * * * ; [32c] Allow replacement of output filename with trailer from RECEIVE. ; * * * * * * * * * * * * * * * version 2.7 * * * * * * * * * * * * * * * ; [31] Fix display of file renaming. ; RonB, 05/05/84 ; [30c] Isolate ANSI escape sequences for machine independence. ; [29g] Add 8th bit quoting ; RonB, 04/15/84 ; * * * * * * * * * * * * * * * version 2.6 * * * * * * * * * * * * * * * ; [28d] Improve input filename processing, allow valid special chars ; RonB, 03/27/84 ; [23] Modification to GTCEOF to fix ASCII mode transfer ; * * * * * * * * * * * * * * * version 2.4 * * * * * * * * * * * * * * * ; [Rg] ^X/^Z file interruption. Slight mod to GTNFIL. Rg, 2/84 ; * * * * * * * * * * * * * * * version 2.1 * * * * * * * * * * * * * * * ; [par] Added calls to set parity, strip parity on input if ; other than none parity is called for. ; JD, 2/84 ; [16] Add file-mode ASCII or BINARY processing. ; RonB,01/02/84 ; [11] Capitalize and parse filename being received. ; RonB,12/27/83 ; [9] Fix filename parsing, and add wildcard ability. ; RonB,12/26/83 ; * * * * * * * * * * * * * * * version 2.0 * * * * * * * * * * * * * * * CSEG $ ; Get the file name (including host to micro translation) gofil: cld push ds pop es mov si, offset data mov di, offset fcb mov al,0 stosb mov cx,11 mov al,' ' rep stosb mov cx,24 mov al,0 rep stosb mov di, offset fcb+1 mov ah,0 gofil1: lodsb ;Get a filename character cmp al,'.' je gofil2 cmp al,0 je gofil4 cmp ah,8 jae gofil1 call gofl20 ;Capitalize, and replace strange chars ;[11] stosb inc ah jmps gofil1 gofil2: mov di, offset fcb+9 mov ah,0 gofil3: lodsb ;Get a file type character cmp al,'.' je gofil4 cmp al,0 je gofil4 cmp ah,3 jae gofil4 call gofl20 ;Capitalize, and replace strange chars ;[11] stosb inc ah jmps gofil3 gofil4: cmp byte ptr fcb+1,' ' ;Any chars in first field? ;[32c] begin jne gofil5 ; if not, set filename to '&' mov byte ptr fcb+1,'&' gofil5: mov si, offset fcb2+11 ;Replace with RECEIVE trailer mov di, offset fcb+11 mov temp2, di mov ah,' ' gofl5b: mov al,[si] ;Get character from replacement filename cmp al,'?' je gofl5c mov [di],al ;If not wild, simply replace existing letter cmp al,' ' je gofl5c mov ah,'-' ; and replace subsequent spaces with '-' gofl5c: cmp byte ptr [di],' ' jne gofl5d ;Replace spaces in filename with filler char mov [di],ah ; either a space or a '-'. cmp ah,' ' jne gofl5d mov temp2,di ;Mark location where last space occurred gofl5d: dec di dec si cmp si,offset fcb2+8 jne gofl5e mov ah,' ' gofl5e: cmp si,offset fcb2 jae gofl5b sub temp2,di dec temp2 cmp temp2,8 jbe gofl5f mov temp2,8 gofl5f: call clrfln mov dx, offset fcb ;Print the file name. call tfile ;[32c] end cmp flwflg, 0 ;Is file warning on? jnz gofl5x jmp gofil9 ;If not, just proceed. gofl5x: mov dx, offset fcb call openf ;See if the file exists. cmp al, 0FFH ;Does it exist? jnz gofil7 jmp gofil9 ;If not create it. gofil7: mov dx, offset scrfr ;Move cursor. call poscur ;[30c] mov dx, offset infms5 ;Inform the user we are renaming the file. call tmsg mov cx, temp2 ;Get the length of the file name. ;[32c] begin mov al, 0 ;Says if first field is full. gofil8: cmp cl, 8 ;Is the first field full? jne gofl81 mov al, 0FFH ;Set a flag saying so. gofl81: mov bx, offset fcb ;Get the FCB. add bx, cx ;Add in the character number. mov ah, '&' mov [bx], ah ;Replace the char with an ampersand. push ax push bx push cx mov dx, offset fcb ;See if the file exists. call openf pop cx pop bx cmp al, 0FFH ;Does it exist? pop ax jz gofl89 ;If not create it. cmp al, 0 ;Get the flag. jz gofl83 dec cl ;Decrement the number of chars. cmp cl, 0 jz gofl88 ;If no more, die. jmp gofl81 gofl83: inc cl ;Increment the number of chars. jmp gofil8 ;[32c] end gofl88: mov dx, offset screrr call poscur ;[30c] mov dx, offset ermes4 ;Tell the user that we can't rename it. call tmsg ret gofl89: push dx mov dx, offset fcb ;Print the file name. ;[31] call tfile ;[31] pop dx gofil9: mov dx, offset fcb ;Delete the file if it exists. call delete mov dx, offset fcb ;Now create it. call create cmp al, 0FFH ;Is the disk full? je gofl9x jmp rskp gofl9x: mov dx, offset screrr ;Position cursor. call poscur ;[30c] mov dx, offset erms11 call tmsg ret ; Make sure character in al is a legal filename character: ;[11] begin ; Mask 8th bit, capitalize, and replace all illegal ; special characters with '#' gofl20: and al, 7Fh ;mask eighth bit ;[28d] cmp al, '0' ;Check for digit jb gofl21 cmp al, '9' jbe gofl23 cmp al, 'A' ;Check for uppercase letter jb gofl21 cmp al, 'Z' jbe gofl23 cmp al, 'a' ;Check for lowercase letter jb gofl21 cmp al, 'z' ja gofl21 and al, 5Fh ;Capitalize lowercase jmps gofl23 gofl21: push di mov di, offset spchar ;Special chars. mov cx, 20 ;Twenty of them. repne scasb ;Search string for input char. je gofl22 mov al, '#' ;Replace illegal characters with '#' gofl22: pop di ;[28d] end gofl23: ret ;[11] end ; Get next filename from sorted directory list. Return skip on success, ; plain return at end. getfil: cmp cxzflg, 'Z' ;[Rg] file interrupt flag set to 'Z'? ;[34c] je gtfl9 mov si,dindex ;Any more entries in list? cmp si,dircnt jae gtfl9 cld gtfl4: push ds pop es mov ds,word ptr membuf mov cl,4 shl si,cl inc si push si mov di,offset fcb2+1 mov cl,11 repe cmpsb pop si jae gtfl6 ;If above starting filename, use it push es pop ds inc dindex mov si,dindex cmp si,dircnt jb gtfl4 mov dx,offset erms28 ;No filenames below starting filename call tcrmsg jmps gtfl9 gtfl6: mov di,offset fcb+1 mov cx,11 rep movsb ;Move the name to the FCB push es pop ds inc dindex ;Point to next entry call getopn jmp rskp gtfl9: mov wldflg, 0 ;Reset wild card flag. ret ;[34c] end ; open the file for sending getopn: mov filflg, 0FFH ;Nothing in the DMA. mov eoflag, 0 ;Not the end of file. mov dx, offset fcb call openf ;Open the file. ret ; Output the chars in a packet. ptchr: mov temp1, ax ;Save the size. mov bx, offset data ;Beginning of received packet data. mov outpnt, bx ;Remember where we are. mov ch, rquote ;Quote char. ptchr1: dec temp1 ;Decrement # of chars in packet. jnl pt1 jmp rskp ;Return successfully if done. pt1: dec chrcnt ;Decrement number of chars in dta. jns ptchr2 ;Continue if space left. call outbuf ;Output it if full. jmp r ; Error return if disk is full. ptchr2: mov bx, outpnt ;Get position in packet data buffer. mov ah, [bx] ;Grab a char inc bx mov outpnt, bx ;and bump pointer. mov al, 00h ;First assume no 8th bit ;[29g] begin cmp ebquot, 'N' ;No 8th bit if we can't quote je ptch21 cmp ebquot, 'Y' ; or if we can but aren't. je ptch21 cmp ah, ebquot ;Is this the 8th bit quote character? jne ptch21 mov ah, [bx] ;Get the quoted character inc bx mov outpnt, bx dec temp1 ;Decrement # of chars in packet. mov al, 80h ;Set the 8th bit. ;[29g] end ptch21: cmp ah, ch ;Is it the quote char? jne ptchr4 ;If not proceed. mov ah, [bx] ;Get the quoted character inc bx mov outpnt, bx ;and bump pointer. dec temp1 ;Decrement # of chars in packet. mov dl, ah ;Save the parity bit in dl. ;[29g] begin and dl, 80H and ah, 7FH ;Turn off the parity bit. cmp ah, ch ;Is it the quote char? je ptchr3 ;If so just go write it out. cmp ebquot, 'N' ;No 8th bit if we can't quote je ptch22 cmp ebquot, 'Y' ; or if we can but aren't. je ptch22 cmp ah, ebquot ;Is this the 8th bit quote character? je ptchr3 ;If so, just go write it out. ptch22: add ah, 40H ;Make it a control char again. ;[29g] end and ah, 7FH ;Modulo 128. ptchr3: or ah, dl ;Or in the parity bit. ptchr4: or ah, al ;Or in the quoted 8th bit. ;[29g] mov bx, bufpnt ;Destination buffer. mov [bx], ah ;Store it. inc bx mov bufpnt, bx ;Update the pointer jmp ptchr1 ;and loop to next char. ; output the buffer, reset bufpnt and chrcnt outbuf: push bx push cx mov dx, offset fcb call soutr ;Write the record. pop cx pop bx cmp al, 0 ;Successful. jz outbf1 cmp al, 1 jz outbf0 mov dx, offset screrr call poscur ;[30c] mov dx, offset erms17 ;Record length exceeds DTA. call tmsg ret outbf0: mov dx, offset screrr call poscur ;[30c] mov dx, offset erms11 ;Disk full error. call tmsg ret outbf1: mov bx, offset dma ;Addr for beginning. mov bufpnt, bx ;Store addr for beginning. mov ax, bufsiz-1 ;Buffer size. mov chrcnt, ax ;Number of chars left. jmp rskp ; Get the chars from the file. gtchr: mov ch, squote ;Keep quote char in c. cmp filflg, 0 ;Is there anything in the DMA? jz gtchr0 ;Yup, proceed. mov cl, 0 ;No chars yet. call inbuf jmp gtceof ;No more chars, go return EOF. gtchr0: mov al, spsiz ;Get the maximum packet size. sub al, 5 ;Subtract the overhead. mov ah, 0 mov temp1, ax ;Number of chars we're to get. mov bx, offset filbuf ;Where to put the data. mov cbfptr, bx ;Remember where we are. mov cl, 0 ;No chars. gtchr1: dec temp1 ;Decrement the number of chars left. jns gtchr2 ;Go on if there is more than one left. mov al, cl ;Return the count in A. mov ah, 0 jmp rskp gtchr2: mov ax, chrcnt dec ax jl gtchr3 mov chrcnt, ax jmp gtchr4 gtchr3: call inbuf ;Get another buffer full. jmp gtceof cmp chrcnt, 0 jne gtchr4 sub cl, 2 ;Don't count controllified Z. mov al, cl mov ah, 0 jmp rskp gtchr4: mov bx, bufpnt ;Position in DMA. mov ah, [bx] ;Get a char from the file. inc bx mov bufpnt, bx cmp ebquot, 'N' ;Can we not do 8th bit quoting? ;[29g] begin je gtch41 cmp ebquot, 'Y' ;Or are we not? je gtch41 mov dh, ah and ah, 7Fh and dh, 80h ;Is the 8th bit set? je gtch41 ;If not, no need for quoting dec temp1 ;Decrement the number of characters left mov dh, ebquot ;Insert 8th bit quote char. in packet buffer mov bx, cbfptr mov [bx], dh inc cbfptr inc cl ;Count the character gtch41: mov dl, ah ;Save the char. ;[29g] end and dl, 80H ;Turn off all but parity. and ah, 7FH ;Turn off the parity. cmp ah, ' ' ;Compare to a space. jl gtchr5 ;If less then its a control char, handle it. cmp ah, del ;Is the char a delete? jz gtchr5 ;Go quote it. cmp ah, ch ;Is it the quote char? je gtch42 ;If so, insert it in the buffer ;[29g] begin cmp ebquot, 'N' ;Can we not do 8th bit quoting? je gtchr8 cmp ebquot, 'Y' ;Or are we not? je gtchr8 cmp ah, ebquot ;Is this the 8th bit quote character? jne gtchr8 ;If not, proceed gtch42: dec temp1 ;Decrement the chars remaining. ;[29g] end mov bx, cbfptr ;Position in character buffer. mov [bx], ch ;Precede char with send quote. inc cbfptr inc cl ;Increment the char count. jmp gtchr8 gtchr5: or ah, dl ;Turn on the parity bit. cmp ah, ('Z'-100O) ;Is it a ^Z? jne gtchr7 ;If not just proceed. cmp binflg, 0 ;ASCII file? ;[16] begin je gtceof ;If so, terminate cmp eoflag, 0 ;EOF flag set? ;[16] end jz gtchr6 ;If not just go on. mov bx, bufpnt mov ax, chrcnt mov dh, al ;Get number of chars left in DMA. gtch51: dec dh jns gtch52 ;Any chars left? mov chrcnt, 0 ;If not, say so. mov al, cl ;Return the count in A. mov ah, 0 jmp rskp gtch52: mov ah, [bx] ;Get the next char. inc bx ;Move the pointer. cmp ah, ('Z'-100O) ;Is it a ^Z? jz gtch51 ;If so see if they rest are. gtchr6: mov ah, ('Z'-100O) ;Restore the ^Z. gtchr7: xchg ah, al mov ah, 0 mov temp2, ax ;Save the char. dec temp1 ;Decrement char counter. mov bx, cbfptr ;Position in character buffer. mov [bx], ch ;Put the quote in the buffer. inc cbfptr inc cl ;Increment the char count. mov ax, temp2 ;Get the control char back. xchg al, ah add ah, 40H ;Make the non-control. and ah, 7FH ;Modulo 200 octal. gtchr8: or dl, dl ;Do we have parity? ;[29g] jz gtch81 ;If not, just send it. ;[29g] or ah, dl ;Or in the parity bit. cmp parflg,parnon ;[par] no parity? je gtch81 ;[par] yes, keep going and ah,7fh ;[par] else turn off parity from file ;[par]*** should probably mention that we're losing eighth bit here push ax ;[29g] begin push cx mov dx, offset scrhi ;mention that high bit is being lost call poscur ;[30c] mov dx, offset hibit call tmsg pop cx pop ax ;[29g] end gtch81: mov bx, cbfptr ;Position in character buffer. mov [bx], ah ;Put the char in the buffer. inc cbfptr inc cl ;Increment the char count. jmp gtchr1 ;Go around again. gtceof: cmp cl, 0 ;Had we gotten any data? je gteof0 ;Nope. mov filflg,0FFh ;[23] fix ASCII extra buffers at eof mov eoflag,0FFh ;[23] mov al, cl mov ah, 0 jmp rskp gteof0: mov ah, 0FFH ;Get a minus one. ret ;Input the next DMA buffer. inbuf: mov ah, eoflag ;Have we reached the end? cmp ah, 0 jz inbuf0 ret ;Return if set. inbuf0: push bx push cx mov bx, offset dma ;Set the r/w buffer pointer. mov bufpnt, bx mov dx, offset fcb call sinr cmp al, 0 ;End of file? je inbuf1 ;Still have data left. mov eoflag, 0FFH ;Set End-of-file. mov filflg, 0 ;Buffer not empty. mov chrcnt, 0 ;Say no characters. pop cx pop bx ret inbuf1: mov al, 80H ;Use as counter for number of chars read. pop cx pop bx cmp filflg, 0 ;Ever used DMS? jnz inbf21 ;Nope, then don't change count. dec al ;Fix boundary error. inbf21: mov ah, 0 ;Zero the flag (buffer not empty). mov chrcnt, ax ;Number of chars read from file. mov filflg, 0 ;Buffer not empty. jmp rskp DSEG $ temp1 dw 0 temp2 dw 0 dma rb 80H filbuf rb 60H ;Character buffer. cpfcb rb 25H ;Save FCB in case of "*". rdbuf rb 80H cnt dw 0 fcb rb 36 fcb2 rb 12 ;replacement receive filename ;[32c] chrcnt dw 0 ;Number of chars in the file buffer. filcnt dw 0 ;Number of chars left to fill. outpnt dw 0 ;Position in packet. bufpnt dw 0 ;Position in file buffer. fcbptr dw 0 ;Position in FCB. datptr dw 0 ;Position in packet data buffer. cbfptr dw 0 ;Position in character buffer. siz dw 0 ;Size of data from gtchr. filflg db 0 ;Non-zero when nothing in DMA buffer. filsiz rw 02H ;Double word for filesize (in bytes.) eoflag db 0 ;EOF flag;non-zero on EOF. binflg db 0 ;ASCII/Binary flag - 0 if ASCII file ;[16] wldflg db 0 ;Assume no "*" in fn. <<< c86ker.a86 >>> ; * * * * * * * * * * * * * * * version 2.9 * * * * * * * * * * * * * * * ; ; [35] Change file names in INCLUDE statements, fdc, 5 Jun 85 ; ; [34] ; (c) Make memory allocation global, add sorted SEND capability. ; (b) Add LOCAL TYPE command to display files on screen ; (a) Fix directory file size calculation errors ; RonB, 11/13/84 ; * * * * * * * * * * * * * * * version 2.8 * * * * * * * * * * * * * * * ; [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 ; WBC3, 10/1/84 ; [32] ; (e) Change all LEA xx,yy instructions to MOV xx,OFFSET yy ; (d) Add LOCAL and REMOTE command table entries, implementing LOCAL ; DELETE, DIRECTORY and SPACE commands (KERMIT,KERUTL) ; (c) Fix RECEIVE FILENAME to be different from GET (KERMIT,KERCMD,KERFIL) ; (b) Fix minor bugs (KERMIT,KERCMD) ; (a) Add SET option for default drive and user (KERMIT,KERCMD,KERUTL) ; RonB, 09/20/84 ;* * * * * * * * * * * * * * * version 2.7 * * * * * * * * * * * * * * * ; [31] Fix display of file rename ; RonB, 05/05/84 ;[fdc] Add help message about "?" when starting up. ; Add help message about "?" when connecting. ; Fix glitch in Bernie's command level flagging (KERCMD). ; F. da Cruz, Columbia ; ;[] Prefixed all ermes'sages with bell and changed ermes3 (secondary ; command didn't parse) from "?Not confirmed" to "?Unrecognized ; command-option". ; Prettied up (TABs between INSTR and AC's) and fixed SET command ; using RSKP convention - B.Eiben, EIBEN at DEC-Marlboro, 2-May-84 ; [30] ; (e) Recombine KERSYS and KERMIT. ; (d) Add SET PORT command, currently unimplemented (KERMIT,KERIO) ; (c) Isolate all machine dependencies in KERIO. ; (b) Make DEL work like BS and ^X like ^U in command input (KERCMD). ; (a) Add keyboard DEL key alteration for APC (KERIO). ; RonB, 04/18/84 ; [29] ; (g) Add 8th bit quoting (KERPRO,KERFIL,KERSYS). ; (f) Add QUIT command, synonymous to EXIT (KERSYS). ; (e) Move logging code to terminal module, make it a SET command, ; add quit/resume (^Q/^R) to make it more standard (KERTRM,KERSYS) ; (d) Expand receive buffer and check for packet overruns (KERPRO) ; (c) Clear FCB prior to opening or creating a file (KERUTL) ; (b) Add TAKE file processing, initially from KERMIT.INI ; (KERMIT,KERSYS,KERUTL) ; (a) Send error packet whenever a fatal error occurs (KERPRO) ; RonB, 04/08/84 ;* * * * * * * * * * * * * * * version 2.6 * * * * * * * * * * * * * * * ; [28] ; (e) Add local stack for use in interrupt handling (KERIO) ; (d) Improve input filename processing, allow valid special chars ; (KERFIL,KERCMD) ; (c) Make disk full error messages more accurate (KERSYS) ; (b) Include filename in file-not-found error message (KERPRO,KERSYS) ; (a) Clear attribute bits from filename before sending (KERPRO) ; RonB, 03/27/84 ; [27] add "Kermit-86" to interrupt messages. Rg 20-Mar-1984 ; [26] Move terminal emulation (TELNET) to a separate module: KERTRM. ; This is to "modularize" terminal emulation. Rg ; ; [25] Move logic for "seteol" and "escape" from KERSYS into KERCMD so those ; routines need not use internal CMD routines and variables. For this ; purpose add 2 parse routines and codes: "cmcha" and "cmnum". Also ; eliminate the use of some KERCMD text strings in KERPRO. The point ; of this is to keep calls to CMD modular since I want to eventually ; replace the whole thing. ; R. Garland 9-Mar-1984 ; [24] ; (a) Add terminal session logging (KERMIT,KERSYS,KERUTL) ; (b) Allow escape character to local-echo (KERMIT) ; RonB, 03/15/84 ; ; [23] Fix ASCII-mode-junk-at-end-of-file bug. (KERFIL) Rg ;* * * * * * * * * * * * * * * version 2.5 * * * * * * * * * * * * * * * ; [22] ; (a) - Cosmetics - changed FILE-Warning to Warning, parallel to CP/M ; Version and makes SET FI Binary painless ; (b) - made this version 2.5 to stop confusion -B.Eiben DEC Marlboro ; 7-March-84 ; [21] ; (a) - Add SET TIMER ON/OFF option, default is OFF (KERSYS,KERPRO) ; (b) - Change SET FILE-MODE to SET FILE-TYPE to match VAX/VMS (KERSYS) ; (c) - Move all Set/Show processing to KERSYS (KERMIT,KERSYS) ; RonB, 03/05/84 ; [20] ; (a) - Fix version & send/receive header for APC (KERSYS,KERPRO) ; (b) - Add Break processing & set clock rate for NEC (KERIO) ; (c) - Add escape character help in telnet mode (KERMIT,KERSYS) ; (d) - Add a pseudo time-out to PRTOUT so it doesn't loop forever (KERIO) ; (e) - Clean up environment better on KABORT (KERPRO,KERUTL) ; RonB, 03/02/84 ;* * * * * * * * * * * * * * * version 2.4 * * * * * * * * * * * * * * * ; [19] (let Bill's and Jeff's changes be 17 and 18) ; (a) - Add flow control for input comm port buffer. This is primarily to ; allow smooth scrolling and "Hold Screen" to work on the Rainbow. Add ; associated Set, Show, and Help for same. (KERSYS and KERIO) ; (b) - Clear screen at beginning and end of program. (KERSYS and KERIO) ; (c) - Give "bdos" mnemonic to interrupt 224. (KERUTL) ; (d) - Change telnet to check keyboard between comm port reads. (KERMIT) ; Woops - can't get this dumb simple thing to work. Save for later. ; (e) - Put in Break transmission in connect mode. (KERMIT,KERIO) ; (f) - Put in ^X/^Z file interruption. (KERSYS, KERPRO, KERFIL) ; (g) - Put in timeouts for packet receive routines. (KERPRO) ; [Rg] R. Garland, 2/84, OC.GARLAND%CU20B@COLUMBIA-20.ARPA ; Columbia Univ. OC.GARLAND@CU20B.BITNET ;* * * * * * * * * * * * * * * version 2.3 * * * * * * * * * * * * * * * ; Fill in the missing parity routines to allow this program to work with ; IBM mainframes and other systems that require parity (edit marked "[par]"). ; Include RonB's fixes for places where previous edit broke the NEC APC. ; Jeff Damens, Columbia, 6 Feb 84 ;* * * * * * * * * * * * * * * version 2.2 * * * * * * * * * * * * * * * ; Added CFIBFs (buffer clearing) where necessary, put conditional assembly ; code all in two modules (86KERIO and 86KERSYS), created new module ; (86KERSYS), added CTLPRT routine to print out control chars for the SHOW ; command, made SERINI called once at the beginning of the program and nowhere ; else and added user protocol timeouts (hitting a ). ; Bill Catchings: 12:36pm Thursday, 19 January 1984 ;* * * * * * * * * * * * * * * version 2.1 * * * * * * * * * * * * * * ; [B.E. 3-Jan-83] added DTR-code (in 86kermit and 86kerio) for RAINBO ; ; [16] Add file-mode ASCII or BINARY processing. ; [15] Clear screen lines before displaying packet debug. ; [14] Fix nout to print decimal. (KERPRO) ; [13] Use control-Z's for filler in partial sectors instead of nulls. (KERPRO) ; [12] Allow user abort from the keyboard in the send and receive routines. ; [11] Capitalize and parse filename being received. (KERFIL) ; [10] Correct missing elements in the Show display. ; [9] Fix filename parsing, and add wildcard ability. (KERCMD,KERPRO,KERFIL) ; [8] Show choices for ambiguous keywords, finish keyword on '?' (KERCMD) ; [7] Do tab expansion and suppress nulls while in telnet mode. (KERUTL) ; [6] Add support for changing baud rate. ; [5] Put OFF/ON command table in alphabetical order. ; [4] Change default escape character to '^' because NEC keyboard doesn't ; generate a control-\. ; [3] Change "esc,'[H'" to "esc,'[1;1H'" to get around bug in NEC BIOS. ; This should not affect operation on the Rainbow. ; [2] Add a de-initialization routine for the serial port, to restore ; changed interrupt vectors, etc. (KERIO) ; [1] Add I/O support for the NEC Advanced Personal Computer (KERIO) ; RonB,12/23/83 ; ; ; * * * * * * * * * * * * * * * version 2.0 * * * * * * * * * * * * * * * ; KERMIT - KL10 Error-free Reciprocal Micro Interconnect over TTY-lines ; ; Kermit Protocol Version 2 ; ; Based on the KERMIT Protocol. ; ; Copyright (C) 1983 William B. Catchings III ; This program implements the Kermit Protocol developed at Columbia ; University. This version is being written specifically for the ; DEC Rainbow 100. It will hopefully take advantage of much of what ; was learned in implementing the CP/M-80, DEC-20 and other versions. ; Things to do: ; Add new features from Kermit-80, like local CP/M functions ; Make all commands the same as those in Kermit-80 ; Now all system dependencies have been isolated in 86KERIO, which ; will be implemented as a separate module for each system. This ; eliminates source code ballooning when other systems are added ; which have (for example) non-ANSI screen controls and terminal ; emulation capabilities, as well as specific port drivers. ; Information about what routines are needed in the 86KERIO module ; is given in the 86KERMIT.HLP and 86KERIO.HLP files. ; ; So far the systems supported are: ; NEC APC in 86KERIO.APC ; DEC Rainbow in 86KERIO.RB ; ; PIP 86KERIO.A86=86KERIO.??? - will make system-dependent routines available ; ASM86 86KERMIT $PZ - will assemble without listing ; GENCMD 86KERMIT - will LOAD ; REN KERMIT.CMD=86KERMIT.CMD - will make it "real" after testing TITLE 'Kermit' TRUE EQU 1 FALSE EQU 0 ; Definitions: soh EQU 01O bell EQU 07O tab EQU 11O lf EQU 12O ff EQU 14O cr EQU 15O xon EQU 21O xoff EQU 23O esc EQU 33O del EQU 177O parevn EQU 00H ;Even parity. ;[21c] begin parmrk EQU 01H ;Mark parity. parnon EQU 02H ;No parity. parodd EQU 03H ;Odd parity. parspc EQU 04H ;Space parity. ibmpar EQU parmrk ;IBM's parity (mark). defpar EQU parnon ;Default parity (none). defeco EQU 00H ;Default is echo off ;[21a] defesc EQU '\'-100O ;Escape char is Control backslash by default. ;note: generated by INS key on the APC. deftmr EQU 00H ;Default is timer off ;[21a] deflog EQU 00H ;Default is logging off ;[24a] floxon EQU 1 ;[19a] flonon EQU 0 ;[19a] diasw EQU 00H ;Default is diagnostics off. ;[21c] end ; The actual program: CSEG ;Start coding! call getdrv ;Save initial disk and user ;[32a] begin mov cpmdrv,al mov defdrv,al call getusr mov cpmusr,al mov defusr,al ;[32a] end ; The allocation amount of 245 pages was arrived at by trial and ; error. This figure causes Concurrent CP/M on the NEC APC to only ; allocate one additional memory block. (Since the blocks are 4K ; each, one would expect 256...) mov cx,245 start1: push cx mov word ptr membuf+2, cx ;Allocate 4K (or less) bytes of memory mov dx, offset membuf ; for sorting filenames call allmem pop cx cmp al,0 loopne start1 cmp cx,0 jne reboot mov dx, offset erms25 ;Not enough memory available: call tcrmsg ; local dir, del, and type will mov word ptr membuf+2, 0 ; not be enabled, nor will wildcard ; sends. ;[34c] end reboot: pushf ;Push flags on CCP's stack. ;[12] pop ax ;flags in AX. cli ;[32b] mov bx, ds ;Set SS to DS. mov ss, bx mov sp, offset stack ;Set up the stack pointer. push ax ;Restore the flags. popf call inidma ;Set DMA base and offset ;[34c] begin call serini ;Do any necessary serial port initialization. call dspver ;Clear screen and print version header mov dx, offset hlpmsg ;[fdc] Give help message about "?" call tmsgcr ;[fdc] ; This is the main KERMIT loop. It prompts for and gets the user's commands. kermit: mov dx, offset kerm call prompt ;Prompt the user. mov dx, offset comtab mov bx, offset tophlp mov ah, cmkey call comnd jmp kermt2 ;Tell them about the error. mov cmlevl, 1 ;we're going into options call bx ;Call the routine returned. jmp kermt3 ;Tell them about the error. cmp extflg, 0 ;Check if the exit flag is set. je kermit ;If not, do it again. call serfin ;clean up serial port environment ;[2] mov dl,cpmdrv ;reset disk and user ;[32a] begin call setdrv ; to initial defaults mov dl,cpmusr call setusr ;[32a] end call haltf retf ;Just in case! ;[32b] kermt2: mov dx, offset ermes1 ;Give an error. call tcrmsg jmp kermit kermt3: mov dx, offset ermes3 ;Give an error. call tcrmsg jmp kermit ; This is the EXIT command. It leaves KERMIT and returns to CP/M. exit: mov ah, cmcfm call comnd ;Get a confirm. jmp r mov extflg, 1 ;Set the exit flag. jmp rskp ; This is the HELP command. help: mov ah, cmcfm call comnd ;Get a confirm. jmp r mov dx, offset tophlp ;Print some help. call tmsg jmp rskp ; LOCAL - parse and perform local command ;[32d] begin loccmd: mov dx, offset loctab ;Parse a keyword from the local table. mov bx, offset lochlp mov ah,cmkey call comnd jmp r call bx ;Call the specific local routine jmp r ;We got an error jmp rskp ; DIRECTORY of local files locdir: mov ah, cmifi ;Parse an input filename mov dx, offset fcb call comnd jmp r push ax ;Save length of filename mov ah, cmcfm ;Confirm it call comnd jmp r pop ax cmp ah, 0 ;Read in any chars? jne locd1 mov di, offset fcb+1 call wldfcb ;If not, set filename to all wild locd1: cmp byte ptr fcb,0 ;Replace unspecified drive with default jne locd2 mov al,defdrv inc al mov byte ptr fcb,al locd2: call dirutl ;perform directory action (in KERUTL) jmp rskp ;On error a message has already been issued jmp locs2 ;jump to display space remaining ; ERASE local files locera: mov ah, cmifi ;Parse an input filename mov dx, offset fcb call comnd jmp r push ax ;Save length of filename mov ah, cmcfm ;Confirm it call comnd jmp r pop ax cmp ah, 0 ;Read in any chars? jne loce1 mov dx, offset erms26 ;Illegal (blank) filename call tcrmsg jmp rskp loce1: call erautl ;Erase utility (in KERUTL) jmp rskp ;On error a message has already been issued jmp rskp ; TYPE local files loctyp: mov ah, cmifi ;Parse an input filename mov dx, offset fcb call comnd jmp r push ax ;Save length of filename mov ah, cmcfm ;Confirm it call comnd jmp r pop ax cmp ah, 0 ;Read in any chars? jne loct1 mov dx, offset erms26 ;Illegal (blank) filename call tcrmsg jmp rskp loct1: call typutl ;Type utility (in KERUTL) jmp rskp ;On error a message has already been issued jmp rskp ; SPACE remaining on disk locsiz: mov ah, cmifi ;Parse an input filename mov dx, offset fcb call comnd jmp r push ax ;Save length of filename mov ah, cmcfm ;Confirm it call comnd jmp r pop ax cmp ah, 0 ;Read in any chars? je locs1 mov dx, offset erms24 ;At most only a drive code should be entered call tcrmsg jmp rskp locs1: cmp fcb,0 jne locs2 mov al,defdrv inc al mov fcb,al locs2: call spcutl ;Space utility (in KERUTL) jmp rskp ;On error a message has already been issued jmp rskp ; Fill a file control block filename with ?'s wldfcb: mov ax,ds ;Fill filename with ?'s. mov es,ax mov cx,11 mov al,'?' rep stosb mov wldflg,0FFh ;Set wildcard flag ret ; REMOTE - parse and perform remote command ; (not yet implemented) remcmd: mov ah,cmtxt ;Parse arbitrary text up to a CR mov bx, offset data call comnd jmp r mov dx, offset infms6 ;Tell the user that it's not yet implemented. call tcrmsg jmp rskp ; mov dx, offset remtab ;Parse a keyword from the remote table. ; mov bx, offset remhlp ; mov ah,cmkey ; call comnd ; jmp r ; call bx ;Call the specific remote routine ; jmp r ;We got an error ; jmp rskp ;[31b] end ; FINISH - tell remote KERSRV to exit. finish: mov ah,cmcfm ;Parse a confirm. call comnd jmp r mov ah, 'F' call gensen ;Send the finish command. jmp rskp ; BYE - tell remote KERSRV to logout and then exit to CP/M. bye: mov ah,cmcfm ;Parse a confirm. call comnd jmp r mov ah, 'L' call gensen ;Send the logout command. mov extflg,1 ;Set exit flag. jmp rskp ; LOGOUT- tell remote KERSRV to logout. logout: mov ah,cmcfm ;Parse a confirm. call comnd jmp r mov ah, 'L' call gensen ;Send the logout command. jmp rskp ; RECEIVE - Receive a file or files from the remote Kermit. ;[32c] begin ; A filespec can optionally be specified to rename the received file. rec: mov ah, cmofi ;Parse an output filename mov dx, offset fcb2 call comnd jmp r push ax ;Save length of filename mov ah, cmcfm ;Confirm it call comnd jmp r pop ax cmp ah, 0 ;Read in any chars? jne rec1 mov di, offset fcb2+1 call wldfcb ;If not, set filename to all wild rec1: cmp byte ptr fcb2,0 ;Also make default drive a ?. jne rec2 mov byte ptr fcb2,'?' rec2: call read ;Do the actual protocol work. jmp rskp ;[32c] end ; GET - Get a file or files from the remote server Kermit. get: mov di, offset fcb2 mov byte ptr [di],'?' inc di call wldfcb ;Receive any filename as is ;[32c] mov ah, cmtxt ;Parse an arbitrary text string. mov bx, offset data ;Where to put the parsed text. call comnd jmp r cmp ah, 0 ;Read in any chars? jne get1 ;If not give an error. mov dx, offset ermes5 call tcrmsg jmp rskp get1: mov al, ah mov ah, 0 mov argbk1, ax ;Remember number of chars we read. mov ah, '$' ;Used for printing. mov [bx], ah call init ;Paint screen and initialize file buffers. call cfibf ;Clear any stacked NAKs. call clrfln ;Prepare to print filename. mov dx, offset data ;Print file name. call tmsg mov argblk, 0 ;Start at packet zero. mov ah, 'R' ;Receive init packet. call spack ;Send the packet. jmp r call read1 ;Join the read code. jmp rskp ; SEND - Send a file or files to the remote Kermit. sencom: mov ah, cmifi ;Parse an input file spec. mov dx, offset fcb ;Give the address for the FCB. call comnd jmp r ;Give up on bad parse. cmp ah,0 ;Check for null filename ;[32c] begin jne $+5 jmp r ;[32c] end cmp wldflg,0FFh jne sen7 mov ah, cmifi ;Parse first file to send ;[34c] begin mov dx, offset fcb2 call comnd nop nop nop mov bx,offset fcb2+1 mov cx,11 sen4: cmp byte ptr [bx],'?' ;Replace wildcards with spaces jne sen5 ; in starting filename mov byte ptr [bx],' ' sen5: inc bx loop sen4 mov wldflg,0FFh ;Show original filename as wild sen7: mov ah, cmcfm call comnd ;Get a confirm. jmp r ;Didn't get a confirm. call send jmp rskp ; SET - Set some Kermit parameter. setcom: mov dx, offset settab ;Parse a keyword from the set table. mov bx, offset sethlp mov ah,cmkey call comnd jmp r call bx ;Call the specific set routine in KERSYS. jmp r ;We got an error jmp rskp ; STATUS - Give some statistics on the connection. status: jmp show ;Make STATUS and SHOW synonymous for now ;[32b] ; TAKE - Specify file which will supply command input. ;[29b] begin take: mov ah, cmifi ;Parse an input file spec. mov dx, offset tkfcb ;Give the address for the FCB. call comnd jmp r ;Give up on bad parse. cmp ah,0 ;Check for null filename ;[32c] begin jne $+5 jmp r ;[32c] end mov ah, cmcfm call comnd ;Get a confirm. jmp r ;Didn't get a confirm. mov tkflg, 1 ;Turn on command file input mov tkptr, 0 ;Indicate file not yet open jmp rskp ;[29b] end ; TRANSMIT file(s) with no protocol ;[32e] begin txmit: mov ah,cmtxt ;Parse arbitrary text up to a CR mov bx, offset data call comnd jmp r mov dx, offset infms6 ;Tell the user that it's not yet implemented. call tcrmsg jmp rskp ;[32e] end ; Set parity for character in Register AL. dopar: cmp parflg, parnon ;No parity? je parret ;Just return cmp parflg, parevn ;Even parity? jne dopar0 and al, 7FH ;Strip parity. jpe parret ;Already even, leave it. or al, 80H ;Make it even parity. jmp parret dopar0: cmp parflg, parmrk ;Mark parity? jne dopar1 or al, 80H ;Turn on the parity bit. jmp parret dopar1: cmp parflg, parodd ;Odd parity? jne dopar2 and al, 7FH ;Strip parity. jpo parret ;Already odd, leave it. or al, 80H ;Make it odd parity. jmp parret dopar2: and al, 7FH ;Space parity - turn off parity bit. parret: ret ; The following are the SET command subroutines ;[21c] begin ; except for Baud rate and Port selection, which are ; isolated in the system dependent modules. ; Sets debugging mode on and off. debset: mov dx, offset ontab mov bx, offset onhlp 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 debug, bl ;Set the debug flag. jmp rskp ; Sets the default disk and user number for file operations ;[32a] begin ; Entry must be in one of the following forms: ; d: = go to drive d (A through P) ; u: = go to user u (0 through 15) ; du: = go to drive d and user u ; : = go to the defaults when Kermit was loaded ; Whenever a drive is specified, even if it is the same as the current ; default drive, the drive is logged in so that disks can be swapped ; without exiting Kermit to type control-C. defset: mov ah,cmtxt ;Get the du: specification mov bx, offset data call comnd jmp r mov byte ptr [bx],0 ;Mark the end of input mov ah,cmcfm ;Confirm the input call comnd jmp r mov newdrv,0FFh ;set inputs to show no entry mov newusr,0FFh mov bx, offset data ;analyze the input mov al,[bx] cmp al,'a' ;check for lower case drive specification jb defs10 cmp al,'p' jbe defs05 jmp deferr defs05: sub al,'a' jmps defs15 defs10: cmp al,'A' ;check for upper case drive specification jb defs25 cmp al,'P' ja deferr sub al,'A' defs15: mov newdrv,al ;save the new drive specification defs20: inc bx mov al,[bx] defs25: cmp al,':' ;input must terminate with required colon je defs50 cmp al,'0' ;check for user number digit jb deferr cmp al,'9' ja deferr sub al,'0' mov dl,al cmp newusr,0FFh ;is this the first digit? jne defs30 mov newusr,dl ; yes, just store the digit jmps defs20 defs30: mov al,newusr ; otherwise append digit to current value mov dh,10 mul dh add al,dl mov newusr,al jmps defs20 defs50: inc bx ;we've seen a colon, it must be followed cmp byte ptr [bx],0 ; by the null we stored earlier jne deferr mov al,newusr ;are we setting a new user number? cmp al,0FFh jne defs60 ; yes, check its value cmp al,newdrv ; otherwise if neither drive nor user jne defs70 ; was specified and yet we saw the colon, mov al,cpmdrv ; then return to the initial CP/M defaults. mov newdrv,al mov al,cpmusr defs60: cmp al,15 ;make sure user is in range ja deferr mov defusr,al ;save new user value mov dl,al call setusr defs70: mov al,newdrv ;are we setting a new drive? cmp al,0FFh je defs90 mov defdrv,al ;save new drive value call rstdsk ;reset disk system to log in drive mov dl,defdrv ; then select new default call setdrv defs90: jmp rskp deferr: mov dx, offset erms23 call tcrmsg jmp rskp ;[32a] end ; Set end-of-line character eolset: ;[25] begin mov dx, 01FH ;maximum value of number allowed. (31) mov bx, offset eolhlp ;help string for parser mov ah, cmnum ;look for a decimal number call comnd ;call the parser jmp r mov seol, bl ;set the eol character jmp rskp escape: mov bx, offset eschlp ;help string for parser mov ah, cmcha ;parser code for single character call comnd ;call parser jmp r mov escchr, bl ;set the character jmp rskp ;[25] end ; This is the SET file-type (ASCII or BINARY) command ;[16] begin fmset: mov dx, offset fmtab mov bx, offset fmhlp 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 binflg, bl ;Set the file-type flag. jmp rskp ;[16] end ; This is the SET Warning command. filwar: mov dx, offset ontab mov bx, offset onhlp 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 flwflg, bl ;Set the file warning flag. jmp rskp ; Set flow-control flag [19a] Begin setflo: mov dx, offset flotab ;set up ON/OFF response mov bx, offset flohlp mov ah, cmkey call comnd ;get response jmp r mov temp1, bx ;save response mov ah, cmcfm call comnd ;get confirm jmp r mov bx, temp1 mov floctl, bl ;set flag jmp rskp ;[19a] end ; This is the SET IBM command. ibmset: mov dx, offset ontab mov bx, offset onhlp 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 ibmflg, bl ;Set the IBM flag. cmp bl, 0 ;Turning on or off? je ibmst1 ;If off, set parity & local echo to defaults. mov parflg, ibmpar ;Set IBM parity. ;[21a] begin mov ecoflg, 1 ;Set local echo on. mov tmrflg, 1 ;Set timer on. jmps ibmst2 ibmst1: mov parflg, defpar ;Set default parity. mov ecoflg, defeco ;Set local echo to default. mov tmrflg, deftmr ;Set timer to default. ibmst2: jmp rskp ;[21a] end ; This is the LOCAL echo SET subcommand. local: mov dx, offset ontab mov bx, offset onhlp mov ah, cmkey call comnd jmp r mov temp1, bx ;Save the parsed value. mov ah, cmcfm call comnd ;Get a confirm. jmp r ;Didn't get a confirm. mov bx, temp1 mov ecoflg, bl ;Set the local echo flag. jmp rskp ; This is the LOG filename SET command log: mov ah, cmofi ;Get an output filename mov dx, offset lfcb ;For a log file call comnd jmp r cmp ah,0 ;Check for null filename ;[32c] begin jne $+5 jmp r ;[32c] end mov ah, cmcfm ;Confirm it call comnd jmp r mov logflg, 0FFh ;Turn logging on jmp rskp ; This is the SET Parity command. setpar: mov dx, offset partab mov bx, offset parhlp 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 parflg, bl ;Set the parity flag. jmp rskp ; This is the SET Timer command. ;[21a] begin tmrset: mov dx, offset ontab mov bx, offset onhlp 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 tmrflg, bl ;Set the timer flag. jmp rskp ;[21a] end ;[21c] end ; The following are display subroutines used primarily ;[21c] begin ; in the SHOW command. ;[10] begin ; This routine matches a table value and prints the corresponding ; keyword. On entry: ; al = value to match ; bx = beginning address of table tabprt: mov ah, 0 ;make it a word comparison mov ch, [bx] ;get the table size inc bx tabp1: mov dl, [bx] ;get the offset to the value inc bx ;point to beginning of the keyword mov dh, 0 mov si, dx cmp ax, 1[bx+si] ;does the value match? je tabp2 ;yes, go print the keyword at [bx]. add bx, dx ;otherwise go to next entry add bx, 3 dec ch ;any more entries jnz tabp1 ;if so, go check the next mov bx, offset erms20 ;else say value was not found tabp2: mov dx, bx call tmsg ;display the keyword ret ;[10] end ; This routine prints out the escape character in readable format. ; Call CTLPRT with any char in DL for similar output. escprt: mov dl, escchr ctlprt: cmp dl, ' ' jge escpr2 push dx mov dx, offset esctl ;Print "Control-" call tmsg pop dx add dl, 040H ;Make it printable. escpr2: call bout ret ; This routine prints "is on" if prior comparison was nonzero ; and otherwise prints "is off" ponoff: jz ponof2 ;If not say so. mov dx, offset onstr ;Say on. jmp ponof3 ponof2: mov dx, offset offstr ;Say off. ponof3: call tmsg ret ; SHOW - Show the state of the parameters available from the SET command. show: mov ah,cmcfm ;Parse a confirm. call comnd jmp r call tcrlf call shobd ;Display baud rate (system dependent) mov dx, offset debst ;Debugging string. call tcrmsg cmp debug, 0 ;Is the debugging flag on? call ponoff mov dx, offset eolst ;End of line string. ;[10] begin call tcrmsg mov dl, seol call ctlprt ;[10] end mov dx, offset escst ;Escape string. call tcrmsg call escprt ;Print the escape char. mov dx, offset fmst ;File type string. [22d] ;[16] begin call tcrmsg mov al, binflg ;Print the keyword corresponding to the mov bx, offset fmtab ;current value of binflg call tabprt ;[16] end mov dx, offset flostr ;[19a] start call tcrmsg ; mov al, floctl ;show the current mov bx, offset flotab ;setting for flow-control call tabprt ;[19a] end mov dx, offset ibmst ;IBM string. call tcrmsg cmp ibmflg, 0 ;Is the IBM flag on? call ponoff mov dx, offset locst ;Get the address of the local echo string. call tcrmsg cmp ecoflg, 0 ;Is the local echo flag on? call ponoff ;Print on or off. mov dx, offset logst ;Logging string. call tcrmsg mov dx, offset lfcb ;Print the log filename. call tfile cmp logflg, 0 ;Is the logging flag on? call ponoff mov dx, offset parst ;Parity string. call tcrmsg mov al, parflg ;Print the keyword corresponding ;[10] begin mov bx, offset partab ;to the current value of parflg call tabprt call shoprt ;Port selection (system dependent) ;[30d] mov dx, offset tmrst ;Timer on/off string ;[21a] begin call tcrmsg cmp tmrflg, 0 ;Is the timer on? call ponoff ;Print on or off. ;[21a] end mov dx, offset filst ;File warning string. call tcrmsg cmp flwflg, 0 ;Is the file warning flag on? call ponoff call tcrlf ;Print a crlf jmp rskp ;[21c] end dspver: call clrscr ;Home cursor and clear the screen ;[30c] begin mov dx, offset scrhdr ;Position to the version line. call poscur call bldon ;Turn on bold (highlighted) display mov dx, offset system ;Print the system identification call tmsg mov dx, offset versio ;Print the Kermit version header. call tmsg call bldoff ;Turn off the highlight ;[30c] end ret DSEG ;Data segment. ORG 100H ;Leave room for system page. RW 100 ;hundred word or so stack. stack RW 2 ; COMND tables comtab db 22 db 3,'BYE$' dw bye db 7,'CONNECT$' dw telnet db 6,'DELETE$' ;[32d] begin dw locera db 9,'DIRECTORY$' dw locdir db 5,'ERASE$' dw locera ;[32d] end db 4,'EXIT$' dw exit db 6,'FINISH$' dw finish db 3,'GET$' dw get db 4,'HELP$' dw help db 5,'LOCAL$' ;[32d] dw loccmd ;[32d] db 6,'LOGOUT$' dw logout db 4,'QUIT$' ;[29f] dw exit ;[29f] db 7,'RECEIVE$' dw rec db 6,'REMOTE$' ;[32d] dw remcmd ;[32d] db 4,'SEND$' dw sencom db 3,'SET$' dw setcom db 4,'SHOW$' dw show db 5,'SPACE$' ;[32d] dw locsiz ;[32d] db 6,'STATUS$' dw status db 4,'TAKE$' ;[29b] dw take ;[29b] db 8,'TRANSMIT$' ;[32e] dw txmit ;[32e] db 4,'TYPE$' ;[34b] dw loctyp ;[34b] settab db 14 db 4,'BAUD$' dw bdset ;[6] end db 9,'DEBUGGING$' dw debset db 12,'DEFAULT-DISK$' ;[32a] begin dw defset ;[32a] end db 11,'END-OF-LINE$' dw eolset db 6,'ESCAPE$' dw escape db 9,'FILE-TYPE$' ;[16] begin [21b] dw fmset ;[16] end db 13,'FLOW-CONTROL$' ;[19a] dw setflo ;[19a] db 3,'IBM$' dw ibmset db 10,'LOCAL-ECHO$' dw local db 3,'LOG$' ;[24a] dw log ;[24a] db 6,'PARITY$' dw setpar db 4,'PORT$' ;[30d] dw prtset db 5,'TIMER$' ;[21a] dw tmrset ;[21a] db 7,'WARNING$' dw filwar loctab db 5 ;[32d] begin db 6,'DELETE$' dw locera db 9,'DIRECTORY$' dw locdir db 5,'ERASE$' dw locera db 5,'SPACE$' dw locsiz ;[32d] end db 4,'TYPE$' ;[34b] dw loctyp ;[34b] ontab db 2 ;Two entries. db 3,'OFF$' ;[5] begin dw 0000H db 2,'ON$' dw 0001H ;[5] end yestab db 2 ;Two entries. db 2,'NO$' dw 0000H db 3,'YES$' dw 0001H flotab db 2 ;[19a] start db 4,'NONE$' dw flonon db 8,'XON/XOFF$' dw floxon ;[19a] end partab db 5 ;Five entries. db 4,'EVEN$' dw parevn db 4,'MARK$' dw parmrk db 4,'NONE$' dw parnon db 3,'ODD$' dw parodd db 5,'SPACE$' dw parspc fmtab db 2 ;Two entries. ;[16] begin db 5,'ASCII$' dw 00H db 6,'BINARY$' dw 01H ;[16] end hlpmsg db cr,lf,'Type ? at any point for help$' cfrmes db ' Confirm with carriage return $' filhlp db ' Input file spec (possibly wild) $' tophlp db cr,lf,' Basic Commands Other Commands' db cr,lf,'CONNECT to host as a terminal DELETE local files' db cr,lf,'RECEIVE file(s) from host DIRECTORY of local files' db cr,lf,'SEND file(s) to host HELP by giving this message' db cr,lf,'SET a Kermit parameter LOCAL generic command' db cr,lf,'SHOW the parameter values QUIT (same as EXIT)' db cr,lf,'EXIT to CP/M SPACE remaining on disk' db cr,lf,' STATUS of Kermit' db cr,lf,' Server Commands TAKE command input from file' db cr,lf,'GET file(s) from host TYPE local file on screen' db cr,lf,'SEND file(s) to host' db cr,lf,'REMOTE generic command' db cr,lf,'FINISH running Kermit on the host' db cr,lf,'LOGOUT the host' db cr,lf,'BYE to host (LOGOUT, then EXIT)' db '$' sethlp db cr,lf,'BAUD rate' ;[6] db cr,lf,'DEBUGGING displays of transferred packets' db cr,lf,'DEFAULT-DISK for file operations' ;[32a] db cr,lf,'END-OF-LINE character in packets' db cr,lf,'ESCAPE character from terminal mode' db cr,lf,'FILE-TYPE (ASCII or BINARY)' ;[16][21b] db cr,lf,'FLOW-CONTROL (NONE or XON/XOFF)' ;[19a] db cr,lf,'IBM mainframe communications mode' db cr,lf,'LOCAL-ECHO echoing (half-duplex)' db cr,lf,'LOG filename for terminal session logging' ;[24a] db cr,lf,'PARITY type' db cr,lf,'PORT to communicate on' ;[30d] db cr,lf,'TIMER for packet retransmission' ;[21a] db cr,lf,'WARNING for received filename conflicts' db '$' lochlp db cr,lf,'DELETE local files' ;[32d] begin db cr,lf,'DIRECTORY of local files' db cr,lf,'SPACE remaining on disk' db cr,lf,'TYPE local files on screen' ;[34b] db '$' ;[32d] end flohlp db cr,lf,'NONE XON/XOFF$' onhlp db cr,lf,'OFF ON$' yeshlp db cr,lf,'NO YES$' parhlp db cr,lf,'NONE MARK ODD EVEN SPACE$' fmhlp db cr,lf,'ASCII BINARY$' ;[16] eolhlp db cr,lf,'Decimal digit between 0 and 31$' eolerr db cr,lf,'Illegal end-of-line character$' timhlp db cr,lf,'Decimal digit between 1 and 94$' timerr db cr,lf,'Illegal timeout value$' esctl db 'Control-$' eschlp db cr,lf,'Enter literal value (ex: Ctrl ]) $' inthlp db 'Kermit-86: Interrupt - type' ;[27] db cr,lf,' ? to display this message' ;[20c] begin db cr,lf,' B to send a Break signal to the port' db cr,lf,' C to return to the Kermit-86> prompt' db cr,lf,' L to toggle terminal session logging' ;[24a] db cr,lf,' Q to quit terminal session logging' ;[29e] db cr,lf,' R to resume terminal session logging' ;[29e] db cr,lf,' $' inthl2 db ' to send a $' inthl3 db ' character to the port$' ;[20c] end onstr db ' is on$' offstr db ' is off$' flostr db 'Flow control: $' ;[19a] bdst db 'Baud rate: $' debst db 'Debug mode$' eolst db 'End-of-line character: $' escst db 'Escape char: $' fmst db 'File type is $' ;[16][21b] ibmst db 'IBM flag$' locst db 'Local echo$' logst db 'Logging to $' ;[24a] parst db 'Parity: $' tmrst db 'Timer$' ;[21a] timmes db 'Timeout is $' filst db 'Warning$' clrspc db ' ',10O,'$' ;Clear space. versio db ' CP/M-86 Kermit-86 - V2.9' db cr,lf,lf,'$' kerm db 'Kermit-86 $' ;[32a] pktlin db cr,'Number of packets: ' db cr,lf,'Number of retries: ' db cr,lf,'File name: $' spmes db 'Spack: $' rpmes db 'Rpack: $' hibit db 'Warning - Non-ASCII char$' ender db bell,bell,'$' inms01 db cr,lf,'[Kermit-86: Connecting to host...' ;[27][fdc] db cr,lf,' Type $' ;[fdc] inms02 db ' C to return to PC, $' ;[fdc] inms25 db ' ? for help]$' ;[fdc] inms03 db '[Kermit-86: Back at PC]$' ;[27] infms3 db 'Completed $' infms4 db 'Failed $' infms5 db 'Renaming file to $' infms6 db '%Function not implemented$' infms7 db 'Interrupted $' ;[19f] infms8 db 'File interrupt (^X) $' ;[19f] infms9 db 'File group interrupt (^Z) $' ;[19f] infm10 db ' Type: ^X to interrupt file, ^Z to ' ;[19f][30c] db 'interrupt group, ^C to "abort".$' ;[19g][30c] infm11 db '[Kermit-86: Logging to $' ;[24a][27] infm12 db ']$' ;[24a] infm13 db '[Kermit-86: Logging terminated]$' ;[24a][27] timoms db bell,'Timeout$' ermes1 db bell,'?Unrecognized command$' ermes2 db bell,'?Illegal character$' ermes3 db bell,'?Unrecognized command option$' ermes4 db bell,'?Unable to rename file$' ermes5 db bell,'?No receive file specification given$' ermes7 db bell,'?Unable to receive initiate$' ermes8 db bell,'?Unable to receive file name$' ermes9 db bell,'?Unable to receive end of file$' erms10 db bell,'?Unable to receive data$' erms11 db bell,'?Disk directory area full$' ;[28c] erms14 db bell,'?Unable to receive an acknowledgement from the host$' erms15 db bell,'?Unable to find file $' ;[28b] erms17 db bell,'?Disk data area full$' ;[28c] erms18 db bell,'?Unable to tell host that session is finished$' erms19 db bell,'?Unable to tell host to logout$' erms20 db bell,'?Illegal value$' ;[10] erms21 db '* Aborted *$' ;[12] erms22 db bell,'?Cannot open log file $' ;[24a] erms23 db bell,'?Specify new default drive and (optional) user number as DU:$' ;[32a] erms24 db bell,'?Specify drive as D:$' ;[32d] erms25 db bell,'?Not enough memory for sort buffer:' ;[34c] db cr,lf,' Local DIRECTORY, DELETE, and TYPE operations' db cr,lf,' and wildcard SENDs will not be supported.$' erms26 db bell,'?Illegal filename$' erms27 db bell,'?Memory buffer overflow$' erms28 db bell,'?No filenames above specified starting name$' ;[34c] ; Cursor addressing items (row,col) ;[19b],[30c] begin scrhdr db 3,1 ;Place for version header scrnp db 5,22 ;Place for number of packets. scrnrt db 6,22 ;Place for number of retries. scrfln db 7,12 ;Place for file name. screrr db 13,1 ;Place for error messages. scrst db 5,53 ;Place for "Complete". scrrpr db 14,1 ;Place for prompt. scrhi db 7,53 ;8th bit on in character. scrfr db 8,1 ;Rename file. ;[31] scrsp db 9,1 ;Send packet scrrp db 11,1 ;Receive scrint db 6,53 ;[19f] interrupt acknowledge scrhlp db 24,1 ;[19f] help line ; Impure storage extflg db 0 ;Exit flag;non zero to exit. ecoflg db defeco ;Echo flag; non zero for local echoing. escchr db defesc ;Escape character for the connect command. debug db diasw ;Are we in debug mode? tmrflg db deftmr ;Is the timer enabled? cpmdrv db 0 ;Initial drive and user ;[32a] begin cpmusr db 0 defdrv db 0 ;Current default drive and user defusr db 0 newdrv db 0 ;Storage for resetting drive and user newusr db 0 ;[32a] end ESEG ;Extra segment. INCLUDE C86CMD.A86 ;Get the COMND routines. INCLUDE C86FIL.A86 ;Get the file routines. ;Note, the following file does not exist. You have to copy one of the system ;dependent files like C86XRB.A86 to a file of this name. INCLUDE C86XXX.A86 ;Get the I/O routines INCLUDE C86PRO.A86 ;Get the protocol routines. INCLUDE C86TRM.A86 ;Get terminal emulation [26] INCLUDE C86UTL.A86 ;Get the utility routines. DSEG $ ;Resume data segment. DB 0 ;Is this really necessary? ;No, but without it you need to specify ;link options to GENCMD so as to make the ;load address allow for uninitialized storage. ;This bypasses that necessity by including ;all uninitialized storage in the load ;module. - RonB END <<< c86pro.a86 >>> ; C86PRO.A86 ; * * * * * * * * * * * * * * * * version 3.0 * * ** * * * * * * * * * * * * ; Allows packet echo - CGL ; * * * * * * * * * * * * * * * version 2.9 * * * * * * * * * * * * * * * ; [34c] Add sorted wildcard SENDs with initial filename ; * * * * * * * * * * * * * * * version 2.7 * * * * * * * * * * * * * * * ; [31] Fix display on file renaming. ; RonB, 05/05/84 ; [30c] Isolate ANSI escape sequences for machine independence. ; [29g] Add 8th bit quoting. ; [29d] Enlarge receive buffer and check for packet overruns. ; [29b] Add TAKE processing (close command file when aborting). ; [29a] Send error packet when aborting. ; RonB, 04/08/84 ; * * * * * * * * * * * * * * * version 2.6 * * * * * * * * * * * * * * * ; [28b] Make file-not-found error more informative (include filename). ; [28a] Clear attribute bits from filename before sending ; RonB, 03/27/84 ; [25] - make KERCMD more modular by eliminating some use of text ; strings by other modules (KERPRO, KERSYS), and moving some ; parsing into KERCMD (ESCAPE and EOLSET logic in KERSYS) ; * * * * * * * * * * * * * * * version 2.4 * * * * * * * * * * * * * * * ; [21a] Add timeout enable/disable ; RonB,03/05/84 ; [20a] Fix version & send/receive header ; [20e] Clean up environment better before rebooting in KABORT ; RonB,03/02/84 ; [19f] Add ^X/^Z file interruption - adapted from PC Kermit V1.20 ; Put in Help line for above. ; [19g] Put in timeouts. ; R. Garland 2/84 ; * * * * * * * * * * * * * * * version 2.1 * * * * * * * * * * * * * * * ; [14] Fix nout to print decimal. ; RonB,12/28/83 ; [13] Use control-Z's for filler in partial sectors instead of nulls. ; RonB,12/27/83 ; [12] Allow user abort from the keyboard in the send and receive routines. ; RonB,12/27/83 ; [9] Fix filename parsing, and add wildcard ability. ; RonB,12/26/83 ; * * * * * * * * * * * * * * * version 2.0 * * * * * * * * * * * * * * * ; This module contains the routines that actually implement the Kermit ; protocol. ; Packet definitions. maxpkt equ '~'-' '+2O ;Maximum size of a packet. maxtry equ 05O ;Default number of retries on a packet. imxtry equ 20O ;Default number of retries send initiate. drpsiz equ 5EH ;Default receive packet size. dspsiz equ 20H ;Default send packet size. dstime equ 0AH ;Default send time out interval. drtime equ 05H ;Default receive time out interval. dspad equ 00H ;Default send padding. drpad equ 00H ;Default receive padding. dspadc equ 00H ;Default send padding char. drpadc equ 00H ;Default receive padding char. dseol equ cr ;Default send EOL char. dreol equ cr ;Default receive EOL char. dsquot equ '#' ;Default send quote char. drquot equ '#' ;Default receive quote char. dqbin equ '&' ;Default 8th-bit quote char. ;[29g] bufsiz equ 80H ;Size of DMA. ; A few control characters ctlc equ 03H ;[19f] ctlx equ 18H ;[19f] ctlz equ 1AH ;[19f] DSEG $ ; Program storage. belflg db 1 ;Use bell. incmod db 0 ;Insert Character mode. hierr db 0 ;Non-ascii char (non-zero if yes). parflg db defpar ;Parity flag (default none.) flwflg db 0 ;File warning flag (default off). ibmflg db 0 ;IBM flag (default off). vmeflg db 0 ;VME flag (default off); incnt dw 0 ;Number of chars read in from port. pktptr dw 0 ;Position in receive packet. spsiz db dspsiz ;Send packet size. rpsiz db drpsiz ;Receive packet size. stime db dstime ;Send time out. rtime db drtime ;Receive time out. spad db dspad ;Send padding. rpad db drpad ;Receive padding. spadch db dspadc ;Send padding char. rpadch db drpadc ;Receive padding char. seol db dseol ;Send EOL char. reol db dreol ;Receive EOL char. squote db dsquot ;Send quote char. rquote db drquot ;Receive quote char. ebquot db 'Y' ;Send 8th-bit quote char. ;[29g] pktnum dw 0 ;Packet number. numpkt dw 0 ;Total number of packets sent. numrtr dw 0 ;Total number of retries. numtry db 0 ;Number of tries on this packet. oldtry db 0 ;Number of tries on previous packet. cxzflg db 0 ;[19f] flag for ^X/^Z file interruption state db 0 ;Present state of the automaton. packet rb 4 ;Packet (data is part of it). data rb 60H ;Data and checksum field of packet. ;[29d] recpkt rb 100H ;Receive packet storage. ;[29d] temp dw 0 argblk dw 0 ;For subroutine arguments. argbk1 dw 0 argbk2 dw 0 argbk3 dw 0 tickst dw 10*(7470/clckrt);[19g] "magic" formula for loops/tick ;[19g] CLCKRT is the system clock rate ... ;[19g] ... defined in KERIO ticklp dw 0 ;[19g] Dynamic loop count for tick tickct db dstime ;[19g] # ticks for timeout to occur tmodon db 0 ;[19g] flag for time-out message CSEG $ ; Check for a user abort or interrupt during the send or receive ;[12] begin ; modified by Rg. kabort: call dbinst ;Check if a char has been typed. jmp r ;[19f] Merrily return. call dbin ;Get the character. cmp al, ctlc ;Abort if control-C [19f] je kabrt2 cmp al, ctlx ;[19f] Is it control-x ? je kabrta ;[19f] yes cmp al, ctlz ;[19f] control-z ? jne kabrtb ;[19f] no kabrta: add al, 100O ;[19f] make into 'X' or 'Z' mov cxzflg, al ;[19f] set the flag call intmsg ;[19f] reassure the user ret ;[19f] return kabrtb: cmp al, cr ;if do a timeout ;[29a] begin jne kabrtc pop bx ;return of kabort pop bx ;receive packet pointer ;... next is return of inpkt ret ;return to timeout routine ;[29a] end kabrtc: cmp al, escchr ;or the escape character followed by 'C'. jne kabrt9 mov cx, 1000 ;Only wait just so long for the next char. kabrt1: push cx call dbin pop cx cmp al, 0 loope kabrt1 cmp al, ctlc ;Control-C also works here. [19f] je kabrt2 and al, 137O ;Capitalize it. cmp al, 'C' ;If not a 'C' then continue. jne kabrt9 kabrt2: cmp tkflg, 0 ;Close any command file in use ;[29b] begin je kabrt3 call tkcls ;[29b] end kabrt3: call binst ;Clear out extraneous input ;[29a] begin jmp kabrt4 ;If none, go abort this transmission. call bin jmps kabrt2 kabrt4: pop bx ;return from kabort pop bx ;receive packet pointer pop bx ;return from inpkt pop bx ;return from rpack ;... next is return from r??? to read mov dx, offset erms21 ;Print '* aborted *' message. jmp fatal ;[29a] end kabrt9: mov dl, bell ;beep [19f] call dbout ; [19f] ret ;return no error [19f] ; Send the generic finish command to the remote Kermit. finsen: mov ah, 'F' ;Ask for the finish command. call gensen ret ; Send the generic logout command to the remote Kermit. byesen: mov ah, 'L' ;Ask for the logout command. call gensen ret ; This procedure processes all the generic single packet sends. gensen: mov temp, ax ;Save the specific generic command. mov numtry, 0 ;Initialize count. call cfibf ;Clear out input buffer of extra NAKs. fins1: mov ah, numtry cmp ah, maxtry ;Too many times? js fins3 ;Nope, try it. fins2: mov dx, offset erms18 call tcrmsg ret fins3: inc numtry ;Increment number of tries. mov argblk, 0 ;Packet number zero. mov argbk1, 1 ;One piece of data. mov bx, offset data mov ax, temp ;Get the generic command. mov [bx], ah ;Finish running Kermit. mov ah, 'G' ;Generic command packet. call spack jmp fins2 ;Tell user and die. call rpack ;Get ACK. jmp fins1 ;Go try again. cmp ah, 'Y' ;Got an ACK? jnz fins4 ret ;Yes, then we're done. fins4: cmp ah, 'E' ;Error packet? jnz fins1 ;Try sending it again. call error1 ret ; Packet routines ; Send_Packet ; This routine assembles a packet from the arguments given and sends it ; to the host. ; ; Expects the following: ; AH - Type of packet (D,Y,N,S,R,E,F,Z,T) ; ARGBLK - Packet sequence number ; ARGBK1 - Number of data characters ; ; Returns: +1 always spack: push ax ;Save the packet type. mov bx, offset packet ;Get address of the send packet. mov ah, soh ;Get the start of header char. mov [bx], ah ;Put in the packet. inc bx ;Point to next char. mov ax, argbk1 ;Get the number of data chars. xchg ah, al add ah, ' '+3 ;Real packet character count made printable. mov [bx], ah ;Put in the packet. inc bx ;Point to next char. mov cl, ah ;Start the checksum. mov ax, argblk ;Get the packet number. xchg ah, al add ah, ' ' ;Add a space so the number is printable. mov [bx], ah ;Put in the packet. inc bx ;Point to next char. add cl, ah ;Add the packet number to the checksum. pop ax ;Get the packet type. mov [bx], ah ;Put in the packet. inc bx ;Point to next char. add cl, ah ;Add the type to the checksum. mov dx, argbk1 ;Get the packet size. spack2: cmp dx, 0 ;Are there any chars of data? jz spack3 ; No, finish up. dec dx ;Decrement the char count. mov ah, [bx] ;Get the next char. inc bx ;Point to next char. add cl, ah ;Add the char to the checksum. jmp spack2 ;Go try again. spack3: sp3x: mov ah, cl ;Get the character total. mov ch, cl ;Save here too (need 'cl' for shift). and ah, 0C0H ;Turn off all but the two high order bits. mov cl, 6 shr ah, cl ;Shift them into the low order position. mov cl, ch add ah, cl ;Add it to the old bits. and ah, 3FH ;Turn off the two high order bits. (MOD 64) add ah, ' ' ;Add a space so the number is printable. mov [bx], ah ;Put in the packet. inc bx ;Point to next char. mov ah, seol ;Get the EOL the other host wants. mov [bx], ah ;Put in the packet. inc bx ;Point to next char. mov ah, 0 ;Get a null. mov [bx], ah ;Put in the packet. cmp debug, 0 ;debug mode. je spack4 inc bx mov ah, '$' mov [bx], ah mov dx, offset scrsp ;Print string to move cursor. call poscur ;[30c] begin call clreol ;Clear current line mov dl, lf ; and next one call bout call clreol mov dx, offset scrsp ;Print string to move cursor. call poscur ;[30c] end mov dx, offset spmes call tmsg mov dx, offset packet call tmsg ;Debug end. spack4: call outpkt ;Call the system dependent routine. jmp rskp ; Write out a packet. outpkt: mov dh, spad ;Get the number of padding chars. outpk2: dec dh cmp dh, 0 jl outpk3 ;If none left proceed. mov al, spadch ;Get the padding char. call prtout ;Output it. jmp outpk2 outpk3: mov bx, offset packet ;Point to the packet. outlup: mov al, [bx] ;Get the next character. cmp al, 0 ;Is it a null? jnz outlp2 ret outlp2: call prtout ;Output the character. inc bx ;Increment the char pointer. jmp outlup ; Receive_Packet ; This routine waits for a packet arrive from the host. rpack: call inpkt ;Read up to a carriage return. jmp rptimo ; User timed us out. rpack0: call getchr ;Get a character. jmp rpack ; Hit the carriage return, go try again. cmp ah, soh ;Is the char the start of header char? jne rpack0 ; No, go until it is. rpack1: call getchr ;Get a character. jmp r ; Hit the carriage return, return bad. cmp ah, soh ;Is the char the start of header char? jz rpack1 ; Yes, then go start over. mov cl, ah ;Start the checksum. sub ah, ' '+3 ;Get the real data count. mov dh, ah ;Save it for later. mov al, ah ;Swap halves. mov ah, 0 mov argbk1, ax ;Save the data count. call getchr ;Get a character. jmp r ; Hit the carriage return, return bad. cmp ah, soh ;Is the char the start of header char? jz rpack1 ; Yes, then go start over. add cl, ah ;Add it to the checksum. sub ah, ' ' ;Get the real packet number. mov al, ah ;Swap halves. mov ah, 0 mov argblk, ax ;Save the packet number. call getchr ;Get a character. jmp r ; Hit the carriage return, return bad. cmp ah, soh ;Is the char the start of header char? jz rpack1 ; Yes, then go start over. mov temp, ax ;Save the message type. ; CGL mod for VME packet echo cmp ah, packet + 3 ; compare with the type of this received one je rpack ; if the same go back and get next ; end of CGL mod add cl, ah ;Add it to the checksum. mov bx, offset data ;Point to the data buffer. rpack2: dec dh ;Any data characters? js rpack3 ; If not go get the checksum. call getchr ;Get a character. jmp r ; Hit the carriage return, return bad. cmp ah, soh ;Is the char the start of header char? jz rpack1 ; Yes, then go start over. mov [bx], ah ;Put the char into the packet. inc bx ;Point to the next character. add cl, ah ;Add it to the checksum. jmp rpack2 ;Go get another. rpack3: call getchr ;Get a character. jmp r ; Hit the carriage return, return bad. cmp ah, soh ;Is the char the start of header char? jz rpack1 ; Yes, then go start over. sub ah, ' ' ;Turn the char back into a number. mov dh, cl ;Get the character total. and dh, 0C0H ;Turn off all but the two high order bits. mov ch, cl mov cl, 6 shr dh, cl ;Shift them into the low order position. mov cl, ch add dh, cl ;Add it to the old bits. and dh, 3FH ;Turn off the two high order bits. (MOD 64) cmp dh, ah ;Are they equal? jz rpack4 ; If so finish up. call cfibf ;Clear out any other characters on the line. ret ;And return failure. rpack4: mov ah, 0 mov [bx], ah ;Put a null at the end of the data. mov ax, temp ;Get the type. call cfibf ;Clear out any other characters on the line. jmp rskp rptimo: call cfibf ;On a time out clear out any remaining chars. ret inpkt: mov bx, offset recpkt ;Point to the beginning of the packet. mov incnt, 0 inpkt2: mov al, stime ;[19g] set up timeout loop. mov tickct, al ;[19g] mov ax, tickst ;[19g] "magic" number loops/tick mov ticklp, ax ;[19g] inpkta: cmp tmrflg, 0 ;Is timeout disabled? ;[21a] je inpktb ; If so, skip tick routine ;[21a] dec ticklp ;[19g] cmp ticklp, 0 ;[19g] done this tick? jne inpktb ;[19g] not yet mov ax, tickst ;[19g] reload loop count mov ticklp, ax ;[19g] call ticmsg ;[19g] beep for debugging dec tickct ;[19g] cmp tickct, 0 ;[19g] timed-out? jne inpktb ;[19g] not yet call tmomsg ;[19g] alert user jmp inpkt9 ;[19g] go time-out inpktb: push bx ;[19g][12] bx gets trashed by kb status check. call kabort ;[19g] Doesn't return if user aborted. pop bx ;[19g] call instat ;Any char there? jmp inpkta ; Go until there is one. call inchr ;Get the character. mov [bx], al ;Put the char in the packet. inc bx inc incnt cmp incnt, length recpkt ;Have we overrun the input buffer? ;[29d] jb inpktc jmp inpkt ;If so, just clear and restart inpktc: cmp al, reol ;Is it the EOL char? ;[29d] end jne inpkt2 ;If not loop for another. cmp incnt, 1 ;Ignore bare CR. jne inpkt6 jmp inpkt inpkt6: cmp ibmflg, 0 ;Is this the (dumb) IBM mainframe? jz inpkt4 ;If not then proceed. inpkt5: cmp state, 'S' ;Check if this is the Send-Init packet. jz inpkt4 ;If so don't wait for the XON. inpkt3: call instat ;Wait for the turn around char. jmp inpkt3 call inchr cmp al, xon ;Is it the IBM turn around character? jne inpkt3 ;If not, go until it is. inpkt4: cmp debug, 0 jz inpkt7 ;If not debugging don't print the packet. mov al, '$' ;Get a dollar sign. mov [bx], al ;Put in the packet. inc bx ;Point to next char. mov dx, offset scrrp ;Print the packet. call poscur ;[30c] begin call clreol ;Clear current line mov dl, lf ; and next one call bout call clreol mov dx, offset scrrp ;Print string to move cursor. call poscur ;[30c] end mov dx, offset rpmes call tmsg mov dx, offset recpkt call tmsg inpkt7: mov bx, offset recpkt mov pktptr, bx ;Save the packet pointer. call tmoclr ;[19g] Clear timeout message jmp rskp ;If so we are done. inpkt9: ret ;return failure on time out. getchr: push bx mov bx, pktptr ;Get the packet pointer. mov ah, [bx] ;Get the char. inc bx mov pktptr, bx pop bx ;Restore BX. cmp ah, reol ;Is it the EOL char? jne getcr2 ;If not return retskp. ret ;If so return failure. getcr2: jmp rskp ;This is where we go if we get an error packet. A call to ERROR ; positions the cursor and prints the message. A call to ERROR1 ; just prints a CRLF and then the message. error: mov state, 'A' ;Set the state to abort. mov dx, offset screrr call poscur ;[30c] jmp error2 error1: call tcrlf error2: mov bx, offset data ;Get to the string. add bx, argbk1 ;Add the length of the data. mov ah, '$' ;Put a dollar sign at the end. mov [bx], ah mov dx, offset data ;Print the error message. call tmsg ret ; Jump here if we die during a transfer. Print the error message in ; DX and abort. fatal: push dx ;Save the error message. mov dx, offset screrr call poscur ;[30c] pop dx call tmsg jmp abort ;Change the state to abort. ; Print the status message in DX, ring the bell and position of the prompt. fnstat: push dx mov dx, offset scrst ;Print string to move cursor. call poscur ;[30c] pop dx call tmsg mov dx, offset ender ;Ring them bells. call tmsg mov dx, offset scrhlp ;[19f] Cursor position call poscur ;[30c] call clreol ;[19f] Clear help line ;[30c] mov dx, offset scrrpr ;Put cursor back call poscur ;[30c] ret ; This routine sets up the data for init packet (either the ; Send_init or ACK packet). rpar: mov ah, rpsiz ;Get the receive packet size. add ah, ' ' ;Add a space to make it printable. mov [bx], ah ;Put it in the packet. mov ah, rtime ;Get the receive packet time out. add ah, ' ' ;Add a space. mov 1[bx], ah ;Put it in the packet. mov ah, rpad ;Get the number of padding chars. add ah, ' ' mov 2[bx], ah ;Put it in the packet. mov ah, rpadch ;Get the padding char. add ah, 100O ;Uncontrol it. and ah, 7FH mov 3[bx], ah ;Put it in the packet. mov ah, reol ;Get the EOL char. add ah, ' ' mov 4[bx], ah ;Put it in the packet. mov ah, rquote ;Get the quote char. mov 5[bx], ah ;Put it in the packet. mov ah, ebquot ;Get 8th-bit quote char ;[29g] begin mov 6[bx], ah ;Put it in the packet. mov ah, '1' ;Set single character check type mov 7[bx], ah ;Put it in the packet. mov ah, ' ' ;Set no repeat prefix mov 8[bx], ah ;Put it in the packet. mov ah, 0 ;Initialize capability byte cmp tmrflg, 0 ;Are we able to time out? je rpar1 ; No, leave bit set to zero or ah, 20h ; Otherwise set timeout capability flag rpar1: add ah, ' ' ;Add space to make it printable mov 9[bx], ah ;Put it in the packet. mov ah, 10 ;Ten pieces of data. ;[29g] end ret ; This routine reads in all the send_init packet information. spar: push cx ;Save CX. mov cx, ax ;Save the number of arguments. mov ah, [bx] ;Get the max packet size. sub ah, ' ' ;Subtract a space. mov spsiz, ah ;Save it. mov ax, cx ;[19g] cmp al, 2 ;[19g] Fewer than two pieces? jge spar1 ;[19g] ;[29g] jmp sparx1 ;[29g] spar1: mov ah, 1[bx] ;[19g] Get the timeout value sub ah, ' ' ;[19g] mov stime, ah ;[19g] save it. mov ax, cx cmp al, 3 ;Fewer than three pieces? jge spar2 ;[29g] jmp sparx2 ;[29g] spar2: mov ah, 2[bx] ;Get the number of padding chars. sub ah, ' ' mov spad, ah mov ax, cx cmp al, 4 ;Fewer than four pieces? jge spar3 ;[29g] jmp sparx3 ;[29g] spar3: mov ah, 3[bx] ;Get the padding char. add ah, 100O ;Re-controlify it. and ah, 7FH mov spadch, ah mov ax, cx cmp al, 5 ;Fewer than five pieces? jge spar4 ;[29g] jmp sparx4 ;[29g] spar4: mov ah, 4[bx] ;Get the EOL char. sub ah, ' ' mov seol, ah mov ax, cx cmp al, 6 ;Fewer than six pieces? jge spar5 ;[29g] jmp sparx5 ;[29g] spar5: mov ah, 5[bx] ;Get the quote char. mov squote, ah mov ax, cx ;[29g] begin cmp al, 7 ;Fewer than seven pieces? jge spar6 jmp sparx6 spar6: mov ah, 6[bx] ;Get the 8th-bit quote char. call doquo ;Set the quote character. jmp sparxx sparx1: mov stime, dstime ;Default timeout interval sparx2: mov spad, dspad ;Default number of padding chars sparx3: mov spadch, dspadc ;Default pad character sparx4: mov seol, dseol ;Default eol character sparx5: mov squote, dsquot ;Default send quote character. sparx6: mov ebquot, 'N' ;No 8th bit quoting. sparxx: pop cx ;[29g] end ret ;If so we are done. ; Set 8-bit quote character based on my capabilities ;[29g] begin ; and the other Kermit's request. doquo: cmp ebquot,'N' ;Can I do 8-bit quoting at all? je dq2 ;No - so forget it. cmp ebquot,'Y' ;Can I do it if requested? jne dq0 ;No - it's a must that I do it. mov ebquot,ah ;Do whatever he wants. jmp dq2 dq0: cmp ah,'Y' ;I need quoting - can he do it? je dq2 ;Yes - then all is settled. cmp ah,'N' ;No - then don't quote. je dq1 cmp ah,ebquot ;Both need quoting - chars must match. je dq2 mov ah,'N' dq1: mov ebquot,ah dq2: mov ah,ebquot cmp ah,rquote ;Same prefix? je dq3 ;Not allowed, so don't do quoting. cmp ah,squote ;Same prefix here? je dq3 ;This is illegal too. ret dq3: mov ebquot,'N' ;Quoting will not be done. ret ;[29g] end ; These are some utility routines. ; Increment the packet number. incpkt: inc ax ;Increment it. and ax, 3FH ;Turn off the two high order bits. mov pktnum, ax ;Save modulo 64 of the number. inc numpkt ;Increment the number of packets. ret ; Check if the packet number is the present packet. chkpeq: mov ax, argblk ;Get the packet number. cmp ax, pktnum ;Is it the right packet number? je chkpe2 ret ;No. chkpe2: jmp rskp ;Yes. ; Is the packet number one more than present. chkpom: mov ax, pktnum ;Get the present packet number. inc ax ;Increment. and ax, 03FH ;Account for wraparound. cmp ax, argblk ;Is the packet's number one more than now? jz chkpm2 ;Yes, success. ret chkpm2: jmp rskp ; Check if the packet number is the previous packet. chkpol: inc oldtry ;Save the updated number of tries. mov ax, pktnum ;Get the present packet number. cmp ax, 0 ;Had we wrapped around? jne chkpl2 mov ax, 64 chkpl2: dec ax ;Decrement. cmp ax, argblk ;Is the packet's number one less than now? je chkpl3 jmp rskp chkpl3: ret ; Abort abort: mov argblk, 0 ;packet number 0 ;[29a] begin mov argbk1, 0 ;no data mov ah, 'E' ;error type call spack jmp $ ;[29a] end mov state, 'A' ;Otherwise abort. ret ; ACK the packet. uupack: call updat ;[19f] entry which doesn't zero argbk1 jmp ack1 ;[19f] upack: call updat ;Update the number of tries. ack: mov argbk1, 0 ;No data. (Packet number already in argblk). ack1: mov ah, 'Y' ;Acknowledge packet. call spack ;Send the packet. jmp r jmp rskp updat: mov ah, numtry ;Get the number of tries. mov oldtry, ah ;Save it. mov numtry, 0 ;Reset the number of tries. ret ; Re-ACK the previous packet. reack: call nretry ;Increment and print the number of retries. mov numtry, 0 ;Reset the number of tries. mov ah, 'Y' ;Acknowledge packet. call spack ;Send the packet. jmp r jmp rskp ; NAK that packet. nak: mov ax, pktnum ;Get the packet number we're waiting for. mov argblk, ax mov argbk1, 0 mov ah, 'N' ;NAK that packet. call spack jmp abort call nretry ;Increment and print the number of retries. ret ;Go around again. ; Print the number of retries. nretry: inc numrtr ;Increment the number of retries. pretry: mov dx, offset scrnrt call poscur ;[30c] mov ax, numrtr call nout ;Write the number of retries. ret ; Print the number of packets. pnmpkt: mov dx, offset scrnp ;Print string to move cursor. call poscur ;[30c] mov ax, numpkt call nout ;Write the number of packets. ret ; This routine prints the number in AX on the screen. nout: push ax ;save all registers ;[14] begin push bx push cx push dx mov cx, 0 ;number of digits to print mov bx, 10 ;radix to use for dividing nout1: mov dx, 0 ;clear high word of dividend div bx ;divide dx:ax by 10 push dx ;push this remainder inc cx ;count it cmp ax, 0 ;anything left in the quotient? jne nout1 ; if so, keep dividing nout2: pop dx ;get a digit add dl, '0' ;make it ascii push cx ;save our count call bout ;print the digit pop cx ;repeat until all digits printed loop nout2 pop dx ;restore all registers pop cx pop bx pop ax ret ; Initialize file buffers and paint screen. init: call dspver ;Clear screen and display version header mov dx, offset scrnp ;Position to packet location call poscur mov dx, offset pktlin ;[20a] call tmsg mov dx, offset scrhlp ;[19f] Cursor position call poscur ;[19f] for help line ;[30c] call revon ;Bottom line reverse ;[30c] mov dx, offset infm10 ;[19f] Help for file transfer call tmsg ;[19f] call revoff ;[30c] init1: mov chrcnt, bufsiz ;Number of chars left. mov bufpnt, offset dma ;Addr for beginning. ret ; Clear out the old filename on the screen. clrfln: mov dx, offset scrfr ;Move cursor to file rename. ;[31] begin call poscur call clreol ;Clear to EOL. ;[31] end mov dx, offset scrfln ;Move cursor to file name position. call poscur ;[30c] call clreol ;Clear to EOL. ;[30c] ret ; acknowledge ^X/^Z with a message [19f] start intmsg: mov dx, offset scrint ;position info call poscur ;output it ;[30c] mov dx, offset infms8 ;File message cmp cxzflg, 'X' ;but first check je intm01 ;yes it was X mov dx, offset infms9 ;no it was 'Z' - file group. cmp cxzflg, 'Z' ;or was it? je intm01 ;yes - go output call clreol ;anything else - clear line ;[30c] jmps intm02 ;[30c] intm01: call tmsg ;output it intm02: ret ;goodbye [19f] end ; let the time-out clock "tick" ;[19g] start ticmsg: mov dl, bell ;output a ... call dbout ;... beep! ret ; notify of time-out tmomsg: mov dx, offset scrst ;cursor position call poscur ;[30c] mov dx, offset timoms ;timeout message call tmsg mov tmodon, 1 ;flag for message ret ; clear time-out message tmoclr: cmp tmodon, 0 ;message on screen? jne tmocl1 ;yes - go get it ret ;nothing to clear - return tmocl1: mov dx, offset scrst ;cursor position call poscur ;[30c] call clreol ;clear line ;[30c] mov tmodon, 0 ;indicate line is clear ret ;[19g] end ; RECEIVE command read: call init ;Paint the screen and initialize file buffers. call cfibf ;Clear out any stacked NAKs. read1: mov numpkt, 0 ;Set the number of packets to zero. mov numrtr, 0 ;Set the number of retries to zero. mov pktnum, 0 ;Set the packet number to zero. mov numtry, 0 ;Set the number of tries to zero. mov cxzflg, 0 ;[19f] reset ^X/^Z flag call pretry mov state, 'R' ;Set the state to receive initiate. read2: call pnmpkt mov ah, state ;Get the state. cmp ah, 'D' ;Are we in the data send state? jne read3 call rdata jmp read2 read3: cmp ah, 'F' ;Are we in the file receive state? jne read4 call rfile ;Call receive file. jmp read2 read4: cmp ah, 'R' ;Are we in the receive initiate state? jne read5 call rinit jmp read2 read5: cmp ah, 'C' ;Are we in the receive complete state? jne read6 mov dx, offset infms3 ;Plus a little cuteness. cmp cxzflg,0 ;[19f] an interruption? je read59 ;[19f] no - do normal thing mov dx, offset infms7 ;[19f] substitute 'interrupted' message. read59: call fnstat ret read6: mov dx, offset infms4 ;Plus a little cuteness. call fnstat ret ; Receive routines ; Receive init rinit: mov ah, numtry ;Get the number of tries. cmp ah, imxtry ;Have we reached the maximum number of tries? jl rinit2 mov dx, offset ermes7 ;Print this error and die. jmp fatal rinit2: inc ah ;Increment it. mov numtry, ah ;Save the updated number of tries. call rpack ;Get a packet. jmp nak ; Trashed packet: nak, retry. cmp ah, 'S' ;Is it a send initiate packet? jne rinit3 ;If not see if its an error. mov ebquot, 'Y' ;Initialize my 8th-bit quote flag ;[29g] mov ah, numtry ;Get the number of tries. mov oldtry, ah ;Save it. mov numtry, 0 ;Reset the number of tries. mov ax, argblk ;Returned packet number. (Synchronize them.) call incpkt ;Increment the packet number. mov ax, argbk1 ;Get the number of arguments received. mov bx, offset data ;Get a pointer to the data. call spar ;Get the data into the proper variables. mov bx, offset data ;Get a pointer to our data block. call rpar ;Set up the receive parameters. xchg ah, al mov ah, 0 mov argbk1, ax ;Store the returned number of arguments. call ack1 ;ACK the packet. jmp abort mov ah, 'F' ;Set the state to file send. mov state, ah ret rinit3: cmp ah, 'E' ;Is it an error packet? jne rinit4 call error rinit4: jmp abort ; Receive file rfile: cmp numtry, maxtry ;Have we reached the maximum number of tries? jl rfile1 mov dx, offset ermes8 ;Print this error and die. jmp fatal rfile1: inc numtry ;Save the updated number of tries. call rpack ;Get a packet. jmp nak ; Trashed packet: nak, retry. cmp ah, 'S' ;Is it a send initiate packet? jne rfile2 ; No, try next type. cmp oldtry, imxtry ;Have we reached the maximum number of tries? jl rfil12 ;If not proceed. mov dx, offset ermes7 ;Print this error and die. jmp fatal rfil12: call chkpol ;Check the packet number, is it right? jmp nak ;No, NAK and try again. mov bx, offset data ;Get a pointer to our data block. call rpar ;Set up the parameter information. xchg ah, al mov ah, 0 mov argbk1, ax ;Save the number of arguments. call reack ;Re-ACK the old packet. jmp abort ret rfile2: cmp ah, 'Z' ;Is it an EOF packet? jne rfile3 ; No, try next type. cmp oldtry, maxtry ;Have we reached the maximum number of tries? jl rfil21 ;If not proceed. mov dx, offset ermes9 ;Print this error and die. jmp fatal rfil21: call chkpol ;Check the packet number, is it right? jmp nak ;No, NAK and try again. mov argbk1, 0 ;No data. call reack ;Re-ACK the previous packet jmp abort ret rfile3: cmp ah, 'F' ;Start of file? jne rfile4 call chkpeq ;Packet numbers equal? jmp nak ; No, NAK it and try again. call incpkt ;Increment the number of packets. call gofil ;Get a file to write to. jmp abort call init1 ;Initialize all the file buffers. call upack ;Update counters and ACK the packet. jmp abort mov state, 'D' ;Set the state to data receive. ret rfile4: cmp ah, 'B' ;End of transmission? jne rfile5 call chkpeq ;Packet numbers equal? jmp nak ; No, NAK it and try again. call ack ;ACK the packet. jmp abort mov state, 'C' ;Set the state to complete. ret rfile5: cmp ah, 'E' ;Is it an error packet? jne rfile6 call error rfile6: jmp abort ; Receive data rdata: cmp numtry, maxtry ;Get the number of tries. jl rdata1 mov dx, offset erms10 ;Print this error and die. jmp fatal rdata1: inc numtry ;Save the updated number of tries. call rpack ;Get a packet. jmp nak ; Trashed packet: nak, retry. cmp ah, 'D' ;Is it a data packet? je rdat11 jmp rdata2 ; No, try next type. rdat11: call chkpeq ;Packet numbers equal? jmp rdat12 ; No, check if previous packet. call incpkt ;Increment the number of packets. mov ax, argbk1 ;Get the length of the data. cmp cxzflg, 0 ;[19f] interrupt requested? jne rdat1a ;[19f] yes - skip put to file. call ptchr jmp abort ; Unable to write out chars;abort. call upack ;ACK the packet. jmp abort ret rdat1a: mov bx, offset data ;[19f] data location mov ah, cxzflg ;[19f] get the ^X/^Z flag mov [bx], ah ;[19f] stick it in the packet mov argbk1, 1 ;[19f] data length is 1 call uupack ;[19f] ACK the packet (without zeroing argbk1) jmp abort ;[19f] ret ;[19f] rdat12: cmp oldtry, maxtry ;Have we reached the maximum number of tries? jl rdat13 ;If not proceed. mov dx, offset erms10 ;Print this error and die. jmp fatal rdat13: call chkpol ;Check the packet number, is it right? jmp nak ;No, NAK it and try again. mov argbk1, 0 ;No data. call reack ;Re-ACK the previous packet. jmp abort ret rdata2: cmp ah, 'F' ;Start of file? jne rdata3 ; No, try next type. cmp oldtry, maxtry ;Have we reached the maximum number of tries? jl rdat21 ;If not proceed. mov dx, offset ermes8 ;Print this error and die. jmp fatal rdat21: call chkpol ;Check the packet number, is it right? jmp nak ; No, NAK it and try again. mov argbk1, 0 ;No data. call reack ;Re-ACK the previous packet jmp abort ret rdata3: cmp ah, 'Z' ;Is it a EOF packet? je rdat32 jmp rdata4 ;Try and see if its an error. rdat32: call chkpeq ;Packet numbers equal? jmp nak ; No, NAK it and try again. call incpkt ;Increment the packet number. cmp cxzflg, 0 ;This file interrupted? [19f] start jne rdat3a ;yes jump cmp argbk1, 1 ;1 byte of data in EOF packet? jmp rdat3b ;no - finish up file mov bx, offset data ;pointer to data mov ah, [bx] ;get the data cmp ah, 'D' ;is it D as in Discard? jne rdat3b ;no - finish writing file rdat3a: mov dx, offset fcb ;get file's fcb call delete ;delete the file. cmp cxzflg, 'X' ;Kill one file or batch? jne rdat37 ;whole batch - leave flag mov cxzflg, 0 ;clear flag call intmsg ;clear message jmp rdat37 ;go to clean up. [19f] end rdat3b: mov bx, bufpnt ;Get the dma pointer. mov ax, 80H sub ax, chrcnt ;Get the number of chars left in the DMA. cmp ax, 80H jne rdat34 call outbuf ;Write out buffer if no room for ^Z. jmp abort jmp rdat36 ;Go close the file. rdat34: mov cl, 'Z'-100O ;Put in a ^Z for EOF. mov [bx], cl inc ax dec chrcnt mov cx, chrcnt mov temp, cx inc bx rdt3: inc ax cmp ax, 80H jg rdat35 ;Pad till full. mov cl, 1AH ;Use control-Z's ;[13] mov [bx], cl inc bx jmp rdt3 rdat35: call outbuf ;Output the last buffer. jmp abort ; Give up if the disk is full. rdat36: mov dx, offset fcb call closf ;Close up the file. rdat37: call upack ;ACK the packet. jmp abort mov state, 'F' ret rdata4: cmp ah, 'E' ;Is it an error packet. jne rdata5 call error rdata5: jmp abort ; Send a file. send: cmp wldflg,0FFh je send12 mov dircnt,0 ;If not wild, just use the name in the FCB mov dindex,0 call getopn ;Open the file jmps send19 send12: call getwld ;If wild, get sorted list of matching names jmp r ; on error, message already printed mov dindex,0 ;Start at first one call getfil ;Get and open first file jmp r send19: call init ;Paint the screen and initialize file buffers. call cfibf ;Clear out any stacked NAKs. mov pktnum, 0 ;Set the packet number to zero. mov numtry, 0 ;Set the number of tries to zero. mov numpkt, 0 ;Set the number of packets to zero. mov numrtr, 0 ;Set the number of retries to zero. call pretry ;Print the number of retries. mov state,'S' ;Set the state to receive initiate. send2: call pnmpkt ;Print the number of packets. cmp state, 'S' ;Are we in the send initiate state? jne send3 call sinit jmp send2 send3: cmp state, 'F' ;Are we in the file send state? jne send4 call sfile ;Call send file. jmp send2 send4: cmp state, 'D' ;Are we in the data send state? jne send5 call sdata jmp send2 send5: cmp state, 'Z' ;Are we in the EOF state? jne send6 call seof jmp send2 send6: cmp state, 'B' ;Are we in the eot state? jne send7 call seot jmp send2 send7: cmp state, 'C' ;Are we in the send complete state? jne send8 mov dx, offset infms3 ;Plus a little cuteness. cmp cxzflg, 0 ;[19f] Interrupted? je send7a ;[19f] no mov dx, offset infms7 ;[19f] substitute "interrupted" message send7a: call fnstat ret send8: mov dx, offset infms4 ;Plus a little cuteness. call fnstat ret ; Send routines ; Send initiate sinit: cmp numtry, imxtry ;Have we reached the maximum number of tries? jl sinit2 mov dx, offset erms14 jmp fatal sinit2: mov ah, 'Y' ;Reset our quote capability ;[29g] begin cmp parflg, parnon ;If we have parity, je sini21 ; send our quote preference mov ah, dqbin sini21: mov ebquot, ah ;Set our quote capability ;[29g] end inc numtry ;Save the updated number of tries. mov bx, offset data ;Get a pointer to our data block. call rpar ;Set up the parameter information. xchg ah, al mov ah, 0 mov argbk1, ax ;Save the number of arguments. mov ax, numpkt ;Get the packet number. mov argblk, ax mov ah, 'S' ;Send initiate packet. call spack ;Send the packet. jmp abort call rpack ;Get a packet. jmp r ; Trashed packet don't change state, retry. cmp ah, 'Y' ;ACK? jne sinit3 ;If not try next. call chkpeq ;Is it the right packet number? jmp nretry ;Increment the retries and go try again. call incpkt ;Increment the packet number. mov ax, argbk1 ;Get the number of pieces of data. mov bx, offset data ;Pointer to the data. call spar ;Read in the data. mov ah, numtry ;Get the number of tries. mov oldtry, ah ;Save it. mov numtry, 0 ;Reset the number of tries. mov state, 'F' ;Set the state to file send. ret sinit3: cmp ah, 'N' ;NAK? jne sinit4 ;If not see if its an error. call nretry ret sinit4: cmp ah, 'E' ;Is it an error packet? jne sinit5 call error sinit5: jmp abort ; Send file header sfile: cmp numtry, maxtry ;Have we reached the maximum number of tries? jl sfile1 mov dx, offset erms14 jmp fatal sfile1: inc numtry ;Increment it. mov cxzflg, 0 ;[19f] clear ^X/^Z flag push ds pop es mov di, offset data ;Get a pointer to our data block. mov si, offset fcb+1 ;Pointer to file name in FCB. mov cx,11 sfil11: cmp cx,3 ;Separate file type with period jne sfil12 mov al,'.' stosb sfil12: lodsb and al,7Fh ;Strip off attribute bits cmp al,' ' ;Printable, nonspace characters only jbe sfil13 cmp al,7Fh jae sfil13 stosb sfil13: loop sfil11 mov byte ptr [di],'$' ;Terminate filename for printing sub di, offset data mov argbk1, di ;Save number of characters in name call clrfln mov dx, offset data ;Print file name. call tmsg mov ax, pktnum ;Get the packet number. mov argblk, ax mov ah, 'F' ;File header packet. call spack ;Send the packet. jmp abort call rpack ;Get a packet. jmp r ; Trashed packet don't change state, retry. cmp ah, 'Y' ;ACK? jne sfile2 ;If not try next. call chkpeq ;Packet number right. jmp nretry ;Increment the retries and go try again. sfil14: call incpkt ;Increment the packet number. call updat ;Update the number of tries. sfil15: mov byte ptr fcb+20h,0 ;Set the record number to zero. mov eoflag, 0 ;Indicate not EOF. mov filflg, 0FFh ;Indicate file buffer empty. call gtchr jmp sfil16 ;Error go see if its EOF. jmp sfil18 ;Got the chars, proceed. sfil16: cmp ah, 0FFH ;Is it EOF? je sfil17 jmp abort ;If not give up. sfil17: mov state, 'Z' ;Set the state to EOF. ret sfil18: mov siz, ax mov state, 'D' ;Set the state to data send. ret sfile2: cmp ah, 'N' ;NAK? jne sfile3 ;Try if error packet. call chkpom ;Is the packet's number one more than now? jmp nretry ;Increment the retries and go try again. jmp sfil14 ;If so, join the ACK. sfile3: cmp ah, 'E' ;Is it an error packet. jne sfile4 call error sfile4: jmp abort ; Send data sdata: cmp cxzflg, 0 ;[19f] Interrupt flag on? je sdata0 ;[19f] no mov state, 'Z' ;[19f] yes - abort sending file ret sdata0: cmp numtry, maxtry ;Have we reached the maximum number of tries? jl sdata1 mov dx, offset erms14 jmp fatal sdata1: inc numtry ;Increment it. mov dx, offset data ;Get a pointer to our data block. mov datptr, dx mov dx, offset filbuf ;Pointer to chars to be sent. mov cbfptr, dx mov cx, 1 ;First char. sdat11: mov bx, cbfptr mov ah, [bx] inc cbfptr mov bx, datptr mov [bx], ah ;Put the char in the data packet. inc datptr ;Save position in data packet. inc cx ;Increment the count. cmp cx, siz ;Have we transfered that many? jle sdat11 ;If not get another. mov ax, siz ;Number of char in char buffer. mov argbk1, ax mov ax, pktnum ;Get the packet number. mov argblk, ax mov ah, 'D' ;Data packet. call spack ;Send the packet. jmp abort call rpack ;Get a packet. jmp r ; Trashed packet don't change state, retry. cmp ah, 'Y' ;ACK? jne sdata2 ;If not try next. call chkpeq ;Right packet number? jmp nretry ;Increment the retries and go try again. sdat12: call incpkt ;Increment the packet number. call updat ;Update the number of tries. cmp argbk1,1 ;1 byte of data there? [19f] start jne sdt12b ;no - go on mov bx, offset data ;pointer to data mov ah, [bx] ;get the data cmp ah, 'X' ;an 'X'? je sdt12a ;yes - go cmp ah, 'Z' ;or a 'Z' je sdt12a ;also go jmp sdt12b ;neither one - go on sdt12a: mov cxzflg, ah ;'X' or 'Z' - set the interrupt flag [19f] end ret sdt12b: call gtchr jmp sdat13 ;Error go see if its EOF. mov siz, ax ;Save the size of the data gotten. ret sdat13: cmp ah, 0FFH ;Is it EOF? je sdat14 jmp abort ;If not give up. sdat14: mov state, 'Z' ;Set the state to EOF. ret sdata2: cmp ah, 'N' ;NAK? jne sdata3 ;See if is an error packet. call chkpom ;Is the packet's number one more than now? jmp nretry ;Increment the retries and go try again. jmp sdat12 sdata3: cmp ah, 'E' ;Is it an error packet. jne sdata4 call error sdata4: jmp abort ; Send EOF seof: cmp numtry, maxtry ;Have we reached the maximum number of tries? jl seof1 mov dx, offset erms14 jmp fatal seof1: inc numtry ;Increment it. mov ax, pktnum ;Get the packet number. mov argblk, ax mov argbk1, 0 ;No data. cmp cxzflg, 0 ;[19f] interrupt flag set? je seof1a ;[19f] no - go on mov bx, offset data ;[19f] point to data mov ah, 'D' ;[19f] get 'D' as in Discard mov [bx], ah ;[19f] stuff it into packet mov argbk1, 1 ;[19f] set data length of 1 seof1a: mov ah, 'Z' ;EOF packet. call spack ;Send the packet. jmp abort call rpack ;Get a packet. jmp r ; Trashed packet don't change state, retry. cmp ah, 'Y' ;ACK? jne seof2 ;If not try next. call chkpeq ;Is it the right packet number? jmp nretry ;Increment the retries and go try again. seof12: call incpkt ;Increment the packet number. call updat ;Update the number of tries. mov dx, offset fcb ;Close the file. call closf call getfil ;Get and open the next file. jmp seof13 ; No more. mov state, 'F' ;Set the state to file send. cmp cxzflg, 'X' ;[19f] 'X' in interrupt flag? mov cxzflg, 0 ;[19f] meantime reset it jne sef13a ;[19f] no - go on call intmsg ;[19f] clear interrupt message sef13a: ret ;[19f] goodbye seof13: mov state, 'B' ;Set the state to EOT. ret seof2: cmp ah, 'N' ;NAK? jne seof3 ;Try and see if its an error packet. call chkpom ;Is the packet's number one more than now? jmp nretry ;Increment the retries and go try again. jmp seof12 seof3: cmp ah, 'E' ;Is it an error packet? jne seof4 call error seof4: jmp abort ; Send EOT seot: cmp numtry, maxtry ;Have we reached the maximum number of tries? jl seot1 mov dx, offset erms14 jmp fatal seot1: inc numtry ;Increment it. mov ax, pktnum ;Get the packet number. mov argblk, ax mov argbk1, 0 ;No data. mov ah, 'B' ;EOF packet. call spack ;Send the packet. jmp abort call rpack ;Get a packet. jmp r ;Trashed packet don't change state, retry. cmp ah, 'Y' ;ACK? jne seot2 ;If not try next. call chkpeq ;Is it the right packet number. jmp nretry ;Increment the retries and go try again. seot12: call incpkt ;Increment the packet number. call updat ;Update the number of tries. mov state, 'C' ;Set the state to file send. ret seot2: cmp ah, 'N' ;NAK? jne seot3 ;Is it error. call chkpom ;Is the packet's number one more than now? jmp nretry ;Increment the retries and go try again. jmp seot12 seot3: cmp ah, 'E' ;Is it an error packet. jne seot4 call error seot4: jmp abort <<< c86trm.a86 >>> ; * * * * * * * * * * * * * * * version 2.7 * * * * * * * * * * * * * * * * ; [fdc] Print message about how to get help during connect. ; [29e] Move terminal session logging to terminal module. ; * * * * * * * * * * * * * * * version 2.6 * * * * * * * * * * * * * * * * ; [24b revisited] Fix handling of double-escape character ; RonB, 03/22/84 ; [26] ; Move TELNET stuff from KERMIT to a this module: KERTRM. ; This is to modularize terminal emulation. ; ; integrate keyboard check in-line. This allows keyboard check ; between port checks to work without blowing the stack. ; ; to improve speed, let the port-to-screen routine loop for a certain ; number of characters before checking the keyboard. If we ; check every time, we lose a few characters at 9600 baud ; without flow control. ; ; Mar-1984. R. Garland - Columbia University. ; ; [24] ; (a) Add terminal session logging (KERMIT,KERSYS,KERUTL) ; (b) Allow escape character to local-echo (KERMIT) ; RonB, 03/15/84 ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * cseg $ ;resume code segment ; This is the CONNECT command. It makes the local Kermit act as a terminal. telnet: mov ah,cmcfm call comnd ;Get a confirm. jmp r ; Didn't get a confirm. mov dx, offset inms01 ;Confirmed, print informatory message. call tcrmsg call escprt mov dx, offset inms02 call tmsg ;[fdc] Say how to get help during connect. call escprt ;[fdc] mov dx, offset inms25 ;[fdc] call tmsgcr ;[fdc] cmp logflg, 0 ;Is logging specified? ;[24a] begin je telnt1 call logopn ;If so, open the log file jmp logerr ;[24a] end telnt1: ;Check keyboard. mov teloop, cx ;initialize loop counter call dbinst ;Any chars out there? jmp telntx ; If not, check serial port call dbin ;Yes, then get the char. cmp al, escchr ;Is it an escape char? jz intchr ;If so go process it. telnty: call dopar ;Set parity (if any). ;[24b] mov dx, ax call prtout ;Output the char to the port. cmp ecoflg, 0 ;Is the echo flag turned on? jz telnt2 ;no - go on and dl, 7FH ;Turn off the parity bit. call dbout ;Echo the character. telntx: mov cx, telnct ;load count for telnt2 loop telnt2: ;Check serial port. mov teloop, cx ;save loop count call instat ;Any characters available? jmp telnt1 ; No, check keyboard call inchr ;Get the character. and al, 7FH ;Strip off parity bit. mov dl, al ;Get the character. call dbout ;output to screen. mov cx, teloop ;get loop counter loop telnt2 ;loop a bunch of characters before ;checking keyboard - for speed. jmp telnt1 ;go back and check keyboard. intchr: call dbin ;Get a char. cmp al, 0 ;Is the char a null? jz intchr ;If so, go until we get a char. mov bl, al ;Save the actual char. and al, 137O ;Convert to upper case. ;C = close cmp al, 'C' ;Is it close? jne intch0 call logcls ;Close the log file ;[24a] mov dx, offset inms03 call tcmsgc ;return to micro message jmp rskp ;return from telnet ;B = break intch0: cmp al,'B' ;[19e] Is it "send break"? jne intch1 ;[19e] no. call prtbrk ;[19e] send a break. jmp telnt2 ;[19e] ;escape character intch1: cmp bl, escchr ;Is it the escape char? jne intch2 ;If not, go send a beep to the user. mov al, bl ;If so, uncapitalize it ;[24b] jmp telnty ;go on, we are done here. ;[24b] ;? = help intch2: cmp bl, '?' ;Is it a cry for help? ;[20c] jne intch3 mov dx, offset inthlp ;If so, display help message on screen call tcrmsg call escprt mov dx, offset inthl2 call tmsg call escprt mov dx, offset inthl3 call tmsgcr jmp intchr ;Keep looking for escape character. ;[20c] ;L = toggle logging intch3: cmp al, 'L' ;Is it the logging toggle? ;[24a] begin jne intch4 cmp logflg, 0 ;Is logging now off? jne intc32 intc31: mov logflg, 0FFh ;If so, set to 'on' value. call logopn ;Open log file. jmp logerr jmp telnt2 intc32: mov logflg, 0 call logcls ;Close the log file. jmp telnt2 ;[24a] end ;Q = quit logging ;[29e] begin intch4: cmp al, 'Q' jne intch5 cmp logflg, 0 ;If logging currently enabled, jne intc32 ; go set flag to 'off' jmp telnt2 ;R = resume logging intch5: cmp al, 'R' jne intch6 cmp logflg, 0 ;If logging currently disabled, je intc31 ; go set flag to 'on' jmp telnt2 ;[29e] end ;error = beep! intch6: mov dl, bell ;Otherwise send a beep. call dbout jmp telnt2 logerr: mov logflg, 0 ;Reset logging on file error ;[24a] begin call logcls ;Make sure log file is closed mov dx, offset erms22 ;Print an error message. call tcrmsg mov dx, offset lfcb call tfile call tcrlf jmp rskp ;And keep doing whatever we were ;[24a] end ; These are the file handling routines for terminal session logging. ; LOGOPN opens the logfile in lfcb. logopn: mov byte ptr lfcb+12,0 mov byte ptr lfcb+13,0 mov byte ptr lfcb+14,0 mov dx, offset lfcb call gtjfn ;Is file already present? cmp al, 0FFh jne logo4 ;If so, go seek to its end & open it. mov bx, offset lfcb+1 ;Don't allow wild or blank name cmp byte ptr [bx], ' ' je logo2 cmp wldflg, 0 je logo3 logo2: cld push ds pop es mov di, offset lfcb ;Make it "KERMIT.LOG" instead mov si, offset lognam mov cx, 12 rep movsb logo3: mov dx, offset lfcb call create ;Otherwise create the file. mov bx, offset lfcb mov byte ptr 32[bx], 0 ;Zero "CR" field mov bx, offset dma cmp al, 0FFh ;If no more directory space jne logo9 ret ; then return an error logo4: cld push ds pop es mov di, offset lfcb+1 ;Get the unambiguous filename mov si, offset dma+1 mov cl, 5 shl al, cl mov ah, 0 add si, ax mov cx, 11 rep movsb mov dx, offset lfcb call openf mov dx, offset lfcb ;Get existing file size call sizef mov bx, offset lfcb cmp byte ptr 35[bx], 0 je logo5 ;If file is too full ret ; then return an error logo5: cmp word ptr 33[bx], 0 ;Is the file totally empty? je logo8 ; if so, don't look for control-Z. dec word ptr 33[bx] ;Point to last record in file mov dx, offset lfcb call rinr ;Read random mov bx, offset dma-1 ;Look for the terminating control-Z. mov cx, 80h logo6: inc bx cmp byte ptr [bx], 'Z'-100O je logo9 loop logo6 mov dx, offset lfcb call soutr ;If no control-Z, then start a new buffer logo8: mov bx, offset dma logo9: mov bufpnt, bx ;Save current buffer location. mov logfil, 0FFh ;Flag that file is open mov dx, offset infm11 ;And display status on the terminal call tmsg mov dx, offset lfcb ;Including the file name call tfile mov dx, offset infm12 call tmsgcr jmp rskp ;Return success. ; LOGCLS - closes the log file, but only if it was open logcls: cmp logfil, 0 ;Is file open? je logc3 ;No, just return. cmp bufpnt, offset dma ;Do we have an empty buffer? je logc2 mov bx, bufpnt ;If not, fill remainder with control-Z's mov cx, offset DMA+80h sub cx, bx mov al, 'Z'-100O logc1: mov [bx], al inc bx loop logc1 mov dx, offset lfcb ;Write out last buffer call soutr logc2: mov dx, offset lfcb ;Close the log file call closf mov dx, offset infm13 ;Tell user so. call tcmsgc mov logfil, 0 ;Show file closed. logc3: ret ;[24a] end ; data specific to terminal emulation dseg $ telnct dw 10 ;count of characters to take from port ;for the screen before checking keyboard. teloop dw 0 ;active loop counter logflg db deflog ;Is logging enabled? ;[24a] begin logfil db 0 ;Is the log file open? lognam db 0,'KERMIT LOG' ;Default log filename lfcb db 0,'KERMIT LOG' ;Logging file control block rb 24 ;[24a] end <<< c86utl.a86 >>> ;* * * * * * * * * * * * * * * version 2.9 * * * * * * * * * * * * * * * ; [34c] Make DMA initialization and buffer allocation global ; [34b] Add LOCAL TYPE command ; [34a] Fix bugs in directory listing file sizes ;* * * * * * * * * * * * * * * version 2.8 * * * * * * * * * * * * * * * ; [32d] Add routines to get disk parameters for space calculation ; [32a] Add routines to get and set default disk and user numbers ;* * * * * * * * * * * * * * * version 2.7 * * * * * * * * * * * * * * * ; [29c] Clear FCB prior to opening or creating a file. ; [29b] Add TAKE command file processing. ; RonB, 04/08/84 ;* * * * * * * * * * * * * * * version 2.6 * * * * * * * * * * * * * * * ; [24a] Add terminal session logging ; RonB, 03/15/84 ; * * * * * * * * * * * * * * * version 2.4 * * * * * * * * * * * * * * * ; [20e] Add regular console status check, in addition to direct I/O check. ; RonB,03/02/84 ; [19c] Give "bdos" mnemonic for vector 224. ; * * * * * * * * * * * * * * * version 2.1 * * * * * * * * * * * * * * * ; [7] Do tab expansion and suppress nulls while in telnet mode. ; RonB,12/24/83 ; * * * * * * * * * * * * * * * version 2.0 * * * * * * * * * * * * * * * ; BDOS command codes bdos equ 224 reset EQU 00H conin EQU 01H conout EQU 02H rdrin EQU 03H punout EQU 04H lstout EQU 05H dconio EQU 06H gtiob EQU 07H prstr EQU 09H consta EQU 0BH resetd EQU 0DH ;[32a] stdrv EQU 0EH ;[32a] opnfil EQU 0FH clsfil EQU 10H sfirst EQU 11H snext EQU 12H delf EQU 13H readf EQU 14H writef EQU 15H makef EQU 16H gtdrv EQU 19H ;[32a] gtalv EQU 1BH ;[32d] gtrov EQU 1DH ;[32d] statr EQU 1EH gtdpb EQU 1FH ;[32d] stusr EQU 20H ;[32a] readr EQU 21H writer EQU 22H cflsz EQU 23H dmaset EQU 1AH dmabas EQU 33H allocm EQU 37H ;[32d] freem EQU 39H ;[32d] DSEG $ ;[29b] begin ; TAKE command file processing data area. tkfcb db 0,'KERMIT INI',0,0,0,0 ;FCB with default name rb 19 tkbuf rb 80h ;Input buffer tkptr dw 0 ;Buffer pointer tkflg db 1 ;Initially enable file input ; Messages and storage for directory operations dirm01 db 'Drive $' ;[32d] begin dirm02 db ', User $' dirm03 db ' K$' dirm04 db ' : $' ;directory column separator dirm05 db ' files, $' dirm06 db 'K listed$' dirm07 db ' $' ;displayed at start of each row eram01 db tab,'Delete? $' eram02 db tab,'...not deleted$' eram03 db tab,'...deleted$' eram04 db ' $' ;displayed at start of each row erahlp db cr,lf,'Respond with ''Y'' to delete this file,' db cr,lf,' ''N'' to bypass this file,' db cr,lf,' or ''ESC'' to return to the Kermit-86> prompt.$' spcm01 db 'K (out of $' spcm02 db 'K) remaining on drive $' morm01 db '--more--$' morm02 db 'Return: next line, Space: next page, ^X: next file, ^Z: quit$' membuf rb 5 ;memory control block dirbuf rb 16 dindex dw 0 ;index to first entry in row dlngth dw 0 ;length of directory list dircnt dw 0 ;files in directory list dirsiz dw 0 ;kbytes in directory list remK dw 0 ;Kbytes remaining on disk maxK dw 0 ;Kbytes available if disk was empty DSM dw 0 ;drive maximum allocation blocks KPB dw 0 ;drive Kbytes Per Block ;[32d] end CSEG $ ;Resume coding segment. tkst: cmp tkptr, 0 ;Is file open yet? jne tkst1 call tkopn jmp r ;If open failure tkst1: mov bx, tkptr ;Do we need a new record? cmp bx, offset tkbuf+80h jb tkst2 call tkread mov bx, tkptr tkst2: cmp byte ptr [bx], 1Ah ;Are we at end of file? je tkst3 jmp rskp ;If not, say we have a character. tkst3: call tkcls ; otherwise end the file jmp r tkin: call tkst ;If no file input, jmp bin ; get it from the console. mov bx, tkptr mov al, [bx] ;Get character from command file record. inc tkptr cmp al, lf ;Ignore in file je tkin push ax mov dl, al ;Echo it to the display. call bout pop ax ret tkopn: mov dx, offset tkfcb ;Open the command file call fcbzer ;zero fcb trailer mov cl, opnfil int bdos inc al jz tkopn1 mov tkptr, offset tkbuf+80h ;If success, show we need a read jmp rskp tkopn1: mov tkflg, 0 ;On failure, turn off file input flag. ret tkread: mov dx, offset tkbuf ;Set dma to our location. call setdma mov dx, offset tkfcb call sinr ;Read a record. cmp al, 0 je tkrd1 mov tkbuf, 1Ah ;If failure, load buffer with EOF. tkrd1: mov tkptr, offset tkbuf ;Reinitialize buffer pointer. call inidma ;Reset dma to normal. ret tkcls: mov dx, offset tkfcb ;Close command file. call closf mov tkflg, 0 ;Turn off file input flag. ret ;[29b] end ; Local directory operation dirutl: call getwld ;fill buffer with matching names jmp r ;on failure a message has already been printed cmp dircnt,0 jne locd3 ret locd3: mov dirsiz,0 mov ax,dircnt dec ax ;calculate number of entries in each column shr ax,1 ; = ((count-1)/4)+1 shr ax,1 inc ax mov dlngth,ax call tcrlf mov dx, offset dirm01 ;print header: Drive X, User nn: call tcrmsg mov dl,fcb ;display the drive letter add dl,'A'-1 call bout mov dx, offset dirm02 call tmsg mov al,defusr ;display the user number cbw call nout mov dl,':' call bout mov dindex,0 ;table entry in first column mov cx,dlngth locd4a: push cx mov dx, offset dirm07 ;Start new row call tcrmsg mov si,dindex mov cx,4 locd4b: push si push cx push ds mov ds,word ptr membuf mov cl,4 ;change entry number to table offset shl si,cl inc si ;skip user number mov cx,8 ;Eight characters in filename locd5b: lodsb ;get a filename character mov dl,al push cx call bout ;print it pop cx loop locd5b mov dl,'.' ;separate filename and type with a period call bout mov cx,3 ;Three characters in file type locd5c: lodsb ;get a file type character mov dl,al push cx call bout ;print it pop cx loop locd5c lodsw ;get file size pop ds add dirsiz,ax ;add filesize to listing size mov di, offset dirm03 ;message for file size (____K) mov cx,3 locd5d: mov byte ptr [di],' ' ;first blank out the last size inc di loop locd5d mov bx,10 locd5e: mov dx,0 ;fill in current size div bx add dl,'0' mov [di],dl dec di cmp ax,0 jne locd5e mov dx, offset dirm03 ;print size message call tmsg pop cx cmp cx,1 ;follow all but last column with separator je locd5f mov dx, offset dirm04 ;print column separator push cx call tmsg pop cx locd5f: pop si add si,dlngth cmp si,dircnt jae locd6 loop locd4b locd6: inc dindex pop cx dec cx jz locd8 jmp locd4a locd8: call tcrlf ;end the last row call tcrlf ;add a blank row mov ax,dircnt ;display file count call nout mov dx, offset dirm05 call tmsg mov ax,dirsiz ;display total file size call nout mov dx, offset dirm06 call tmsg jmp rskp ; Erase utility function erautl: call getwld jmp r ;On error a message has already been printed mov dindex,0 loce3: mov si,dindex cmp si,dircnt jb loce4 jmp loce9 loce4: mov cl,4 shl si,cl inc si mov di, offset fcb+1 ;FCB already has drive code push ds pop es mov ds, word ptr membuf mov cx,15 rep movsb push es pop ds mov dx, offset eram04 ;Start new row call tcrmsg mov dx, offset fcb call tfile mov dx, offset eram01 ;'Delete? ' call tmsg loce5a: call dbinst ;clear out all typeahead jmp loce5b call dbin jmps loce5a loce5b: call dbin ;get response cmp al,0 ;NUL is ignored je loce5b cmp al,cr ;CR bypasses file je loce5d cmp al,' ' ;any nonprintable char aborts jb loce8 cmp al,7Fh jae loce8 push ax mov dl,al ;echo the printable response call bout pop ax cmp al,'?' ;request for help? jne loce5c mov dx, offset erahlp ;help message call tcmsgc jmp loce3 loce5c: or al,'a'-'A' ;convert to lower case cmp al,'y' je loce6 loce5d: mov dx, offset eram02 ;' ...not deleted' call tmsg jmp loce7 loce6: cmp byte ptr fcb+14,0 ;make file read-write if necessary je loce6a mov dx, offset fcb call setatr loce6a: mov dx, offset fcb ;delete file call delete mov dx, offset eram03 ;' ...deleted' call tmsg loce7: inc dindex jmp loce3 loce8: mov dx, offset eram02 ;' ...not deleted' when aborting call tmsg loce9: call tcrlf jmp rskp ; Type utility function typutl: call getwld jmp r ;On error a message has already been printed mov dindex,0 loct3: mov si,dindex ;Get next filename in table cmp si,dircnt jb loct4 jmp loct9 loct4: mov dlngth,1 mov cl,4 shl si,cl inc si mov di, offset fcb+1 push ds pop es mov ds, word ptr membuf mov cx,15 rep movsb push es pop ds call tcrlf ;Display the filename mov dx, offset fcb call tfile call tcrlf mov dx, offset fcb call openf ;Open the file inc al jnz loct5 jmp loct6b loct5: mov dx, offset fcb ;Read a record call sinr cmp al,0 jne loct6 mov bx,offset dma loct5a: mov dl,[bx] ;Get a character cmp dl,1Ah je loct6 and dl,7Fh push bx push dx call bout ;Print the character pop dx cmp dl,lf jne loct5b inc dlngth ;Count lf characters cmp dlngth,22 ;If more than 22, then pause for input jb loct5b call more jmp loct5c loct5b: pop bx inc bx cmp bx,offset dma+128 jb loct5a jmps loct5 loct5c: pop bx jmps loct6a loct6: mov ax,dindex ;EOF in file inc ax cmp ax,dircnt jae loct6a call tcrlf call more jmp loct6a loct6a: mov dx, offset fcb ;Close file call closf call tcrlf loct6b: inc dindex ;Move on to next file jmp loct3 loct9: jmp rskp more: call revon mov dx,offset morm01 ;Show --more-- message call tmsg call revoff more1: call dbin ;Get response cmp al,0 ; ignore nulls je more1 push ax call clrlin mov dl,tab ;This is to fool CP/M so it call bout ; gets tab stops right mov dl,cr call bout pop ax and al,7Fh cmp al,'?' ;? gives help jne more2 call revon mov dx,offset morm02 ;Show help message call tmsg call revoff jmps more1 more2: cmp al,cr ;cr, lf go to next line je more4 cmp al,lf je more4 mov dlngth,0 ;Everything else resets line count cmp al,0Fh ;^O, ^X go to next file je more6 cmp al,18h je more6 cmp al,03h ;^C, ^Z, q quit je more5 cmp al,1Ah je more5 or al,'a'-'A' cmp al,'q' je more5 more4: jmp rskp ;Next line, page more5: mov ax,dircnt ;Quit mov dindex,ax more6: ret ;Next file ; Space remaining utility spcutl: call getprm ;Get the disk parameters for calculation call tcrlf mov ax,remK ;Display the number of Kbytes remaining call nout mov dx, offset spcm01 call tmsg mov ax,maxK ;And the total number on the drive. call nout mov dx, offset spcm02 call tmsg mov dl,fcb ;Finally say which drive it is. add dl,'A'-1 call bout mov dl,':' call bout call tcrlf jmp rskp ; get and save disk parameters which relate to block allocation and size getprm: mov newdrv,0 ;initialize flag mov dl,fcb ;specified drive must be the default dec dl ; which is true if drive was not specified jl gprm2 cmp dl,defdrv ; or if specified drive matches the default je gprm2 mov newdrv,0FFh ;otherwise show that we changed default push dx call getrov ;make sure the desired drive is write enabled pop dx ; or else the data may be inaccurate mov cl,dl mov ax,1 shl ax,cl and ax,bx jz gprm1 push dx call rstdsk ;if read-only, reset all drives pop dx gprm1: call setdrv ;select the new drive as default gprm2: push es call getdpb ;get address of DPB in ES:BX mov cl,es:2[bx] ;get Block Shift Factor sub cl,3 mov ax,1 shl ax,cl mov KPB,ax ;save Kbytes Per Block mov cx,es:5[bx] ;get DSM maximum block number mov DSM,cx ;save for file size and allocation calculation inc cx ;number of blocks includes block 0 mul cx mov maxK,ax ;Maximum number of Kbytes mov remK,ax ;Kbytes remaining mov ax,es:7[bx] ;subtract directory allocation from maxK mov cl,5 shr ax,cl inc ax sub maxK,ax call getalv ;get address of allocation vector in ES:BX mov ax,DSM ;compute length of vector = (DSM/8)+1 mov cl,3 shr ax,cl inc ax mov cx,ax mov ax,0 ;Count allocated blocks: gprm3: mov dl,es:[bx] ;for each byte in vector, inc bx push cx mov cx,8 ; for each bit in byte, gprm4: test dl,1 jz gprm5 inc ax ; if bit is set, then block is allocated gprm5: shr dl,1 loop gprm4 pop cx loop gprm3 mul KPB ;convert allocated blocks to Kbytes sub remK,ax ;subtract allocated Kbytes from remK pop es cmp newdrv,0 ;reset default drive if we changed it je gprm6 mov dl,defdrv call setdrv gprm6: ret ; This subroutine fills the previously allocated memory buffer with a sorted ; list of filenames matching the wild name in fcb. On failure, if there were ; no files, it prints an appropriate error message and simply returns. On ; success, it returns to the skip location with the number of entries ; in the list in dircnt. Each buffer entry is 16 bytes long and contains ; the user number at offset 0, the filename (with all attribute bits stripped) ; at offset 1-11, the allocated size in Kbytes at offset 12-13, and the ; read-only and system flags at offsets 14 and 15. getwld: mov byte ptr fcb+12,'?' ;Match any extent mov byte ptr fcb+13,'?' mov byte ptr fcb+14,'?' call getprm ;get disk parameters for size calculation mov dircnt,0 ;zero file count and total space occupied mov dx, offset fcb call gtjfn ;get first filename cmp al,0FFh jne gwld2 mov dx, offset erms15 ;unable to find file call tcrmsg mov dx, offset fcb call tfile ret gwld2: mov cl,5 ;find file directory entry shl al,cl mov ah,0 mov si, offset dma add si,ax ;pointer to filename (incl. user number) mov di, offset dirbuf push ds pop es mov ax,9[si] ;get read-only and system flags and ax,8080h ;keep only attribute bits mov 14[di],ax ;save flags at end of buffer mov cx,12 gwld2a: lodsb and al,7Fh ;get rid of all attribute bits stosb loop gwld2a add si,4 ;look at allocation area mov ax,0 ;initialize block count cmp DSM,256 ;if <256 blocks, then each takes a byte jb gwld2c mov cx,8 ;8 blocks, one word each gwld2b: cmp word ptr [si],0 je gwld2e inc si inc si inc ax loop gwld2b jmps gwld2e gwld2c: mov cx,16 ;16 blocks, one byte each gwld2d: cmp byte ptr [si],0 je gwld2e inc si inc ax loop gwld2d gwld2e: mul KPB ;convert blocks to kbytes stosw ;save this FCB's allocation mov ax,dircnt cmp ax,word ptr membuf+2 ;don't exceed buffer length jb gwld2f mov dx,offset erms27 ; if memory exceeded, print warning call tcmsgc ; and use what info we have jmp gwld4 gwld2f: mov si, offset dirbuf ;go back to start of filename mov es,word ptr membuf ;find correct location in sorted file list mov di,0 mov cx,dircnt ;number of entries already in list jcxz gwld3e gwld3a: push cx push si push di mov cx,12 repe cmpsb jb gwld3d ;table entry greater than this file, insert ja gwld3c ;table entry less than this file, keep looking lodsw ;else table entry same as file add es:[di],ax ; ...add this FCB's allocation to size gwld3b: pop di pop si pop cx jmp gwld3f ;go get next filename gwld3c: pop di ;haven't found insert location yet pop si pop cx add di,16 loop gwld3a jmp gwld3e ;if greater than all entries, insert at end gwld3d: pop di ;insertion point pop si ;filename pointer pop ax ;number of entries following insertion point push si push di mov cl,4 ;each entry occupies 16 bytes shl ax,cl add di,ax dec di mov si,di ;point to last byte of table add di,16 ;move last part down 16 bytes mov cx,ax push ds ;save filename segment push es ;make all action in table segment pop ds std ;decrement pointers after each move rep movsb cld pop ds ;restore filename segment pop di ;insertion point pop si ;filename pointer gwld3e: mov cx,16 ;insert filename in table rep movsb inc dircnt ;count new entry gwld3f: mov dx, offset fcb ;look for another matching filename call gnjfn cmp al,0FFh je gwld4 jmp gwld2 ;go process next filename gwld4: push ds pop es jmp rskp ; General output utility routines tmsgcr: call tmsg ;Print the string call tcrlf ;Print a CRLF. ret tcrmsg: push dx ;Don't trash our string. call tcrlf ;Print a CRLF. pop dx ;Restore our string. call tmsg ;Print the string ret tcmsgc: push dx ;Don't trash our string. call tcrlf ;Print a CRLF. pop dx ;Restore our string. call tmsg ;Print the string call tcrlf ;Print a CRLF. ret tcrlf: mov dl,cr ;print a crlf call bout mov dl,lf call bout ret tmsg: push bx ;Don't clobber my ACs. mov cl, prstr ;Ask BDOS for string printing. int bdos ;What a way to call the BDOS. pop bx ret tfile: mov bx, dx ;Print filename in [dx]'s FCB ;[24a] begin mov dl, [bx] ;If explicit drive number, display it. cmp dl, 0 je tfil1 add dl, 'A'-1 push bx call bout mov dl, ':' call bout pop bx tfil1: mov cx, 11 ;Now display 11 chars of filename tfil2: push cx cmp cx, 3 ;With period before file type jne tfil3 push bx mov dl, '.' call bout pop bx tfil3: inc bx mov dl, [bx] cmp dl, ' ' ;Don't include spaces je tfil4 push bx call bout pop bx tfil4: pop cx loop tfil2 ret ;[24a] end bout: mov cl, conout ;Ask BDOS for character printing. int bdos ret bin: cmp tkflg, 0 ;Check for command file input. ;[29b] begin je bin1 jmp tkin ;[29b] end bin1: mov cl, conin ;Get a char from the console. int bdos ret binst: cmp tkflg, 0 ;Check for command file input. ;[29b] begin je binst1 jmp tkst ;[29b] end binst1: mov cl, consta ;Console input status check ;[20e] begin int bdos or al, al ;Result 0 if no character ready jz bins2 jmp rskp ;Return SKIP if character ready bins2: ret ;[20e] end dbout: cmp dl, 0 ;Skip null fillers. je dbout3 call logchr ;Log the character if necessary ;[24a] call dotab ;Do any necessary tab expansion. jmp r ; No more chars to output. dbout2: mov cl, dconio ;Put a char to the console. int bdos dbout3: ret dbin: push dx mov cl, dconio ;Get a char from the console without mov dl, 0FFH ; interference. int bdos pop dx ret dbinst: push dx mov cl, dconio ;Check the console's input status. mov dl, 0FEH int bdos pop dx or al, al ;Result 0 if no character ready ;[20e] begin jz dbins2 jmp rskp ;Return SKIP if character ready dbins2: ret ;[20e] end ;Log the terminal output character logchr: cmp logfil, 0 ;Only log if file is open ;[24a] begin je logch9 mov bx, bufpnt ;Store the character in the buffer. mov [bx], dl inc bx cmp bx, offset dma+80h ;Have we filled a buffer? jb logch1 push dx ;If so, write it to file. mov dx, offset lfcb call soutr pop dx mov bx, offset dma logch1: mov bufpnt, bx logch9: ret ;Return to output routine ;[24a] end ;Halt this program. haltf: mov cl, reset ;End this program. int bdos ret ;One never knows! ;Reset the disk system to log in drives ;[32a] begin rstdsk: mov cl,resetd int bdos call inidma ;Concurrent CP/M also resets DMA address ret ;Get and set the default disk drive getdrv: mov cl,gtdrv int bdos ret ;returns drive in al (A=0 through P=15) setdrv: mov cl,stdrv ;new drive number (A=0 through P=15) in dl int bdos ret ;Get and set the default user number getusr: mov cl,stusr mov dl,0FFh int bdos ret ;returns user in al (0-15) setusr: mov cl,stusr ;new user number (0-15) in dl int bdos ret ;[32a] end ; Get the address of the disk allocation vector ;[32d] begin getalv: mov cl,gtalv int bdos ret ; Get the disk read-only vector getrov: mov cl,gtrov int bdos ret ; Set file attributes according to FCB in dx setatr: mov cl,statr int bdos ret ; Get the address of the DPB getdpb: mov cl,gtdpb int bdos ret ; Allocate a block of memory. MCB address is in dx. allmem: mov cl,allocm int bdos ret ; Free a previously allocated block of memory. MCB address is in dx. fremem: mov cl,freem int bdos ret ;[32d] end ; Get the first file in a wild card search. gtjfn: mov cl, sfirst int bdos ret ; Get the next file in a wild card search. gnjfn: mov cl, snext int bdos ret ; Close the file pointed to by the FCB in DX. closf: mov cl, clsfil int bdos ret ; Open the file pointed to by the FCB in DX. openf: call fcbzer ;clear the fcb trailer ;[29c] mov cl, opnfil int bdos ret ; Create the file pointed to by the FCB in DX. create: call fcbzer ;clear the fcb trailer ;[29c] mov cl, makef int bdos ret fcbzer: push bx ;Clear the end of the FCB ;[29c] begin push cx ; prior to opening or creating a file. mov bx, dx add bx, 12 mov ch, 0 mov cl, 23 fcbz1: mov [bx], ch inc bx loop fcbz1 pop cx pop bx ret ;[29c] end ; Write a record to the file pointed to by the FCB in DX. soutr: mov cl, writef int bdos ret ; Read a record from the file pointed to by the FCB in DX. sinr: mov cl, readf int bdos ret ; Delete the file pointed to by the FCB in DX. delete: mov cl, delf int bdos ret ; Sets dma to the default buffer. Functions that change this must call this ; function to reset it before continuing inidma: push dx mov dx, offset dma call setdma pop dx ret ; Sets the DMA to the offset pointed to in DX and the base in DS. setdma: mov cl, dmaset int bdos mov dx, ds mov cl, dmabas int bdos ret ; Do random access read, write, or file size checks, FCB is in dx rinr: mov cl, readr int bdos ret routr: mov cl, writer int bdos ret sizef: mov cl, cflsz int bdos ret ; Jumping to this location is like retskp. It assumes the instruction ; after the call is a jmp addr. rskp: pop bp add bp, 3 push bp ret ; Jumping here is the same as a ret. r: ret <<< c86xap.a86 >>> ; * * * * * * * * * * * * * * * version 2.9 * * * * * * * * * * * * * * * ; [36] Add calls to support Concurrent CP/M. ; [34] Make BREAK be correct length (250 ms). ; * * * * * * * * * * * * * * * 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 NEC APC. 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 84h ;sio interrupt offset mniseg equ 86h ;sio interrupt segment ; 8259 Interrupt controller (master) iccmd equ 20h ;interrupt command register icmask equ 22h ;interrupt mask register ; 8259 commands and masks icEOI equ 20h ;end of interrupt (command) ictmof equ 08h ;disable timer (mask) icmnof equ 02h ;disable RS232 (mask) ; 8253-5 Interval Timer tmdata equ 2bh ;baud set (chan 1) tmcmd equ 2fh ;baud timer command port ; 8253 Timer commands tmch1 equ 76h ;select & init timer channel 1 ; 8251A USART controller mndata equ 30h ; data port mnsts1 equ 32h ;in status port mnsts2 equ 34h ;in nec special status port mncmd equ 32h ;out command port mnmsk equ 34h ;out interrupt mask port mntdc equ 36h ;out transmit disable port ; 8251 status port 1 bits mninp equ 02h ;receive ready value mnout equ 01h ;send ready value mndsr equ 80h ;data set ready ; 8251 status port 2 bits mncts equ 04h ;clear to send ; 8251 initialization instructions ; command instructions ctxe equ 01h ;transmit enable cdtr equ 02h ;dtr signal high crxe equ 04h ;receive enable cbrk equ 08h ;send break cerr equ 10h ;error reset crts equ 20h ;rts signal high cmode equ 40h ;reset - go to mode instruction format chunt equ 80h ;hunt for sync characters ; mode instructions m1x equ 01h ;baud rate factor: 1x m16x equ 02h ; 16x m64x equ 03h ; 64x m5d equ 00h ;data bits: 5 m6d equ 04h ; 6 m7d equ 08h ; 7 m8d equ 0Ch ; 8 mpn equ 00h ;parity: none mpo equ 10h ; odd mpe equ 30h ; even m1s equ 40h ;stop bits: 1 m15s equ 80h ; 1.5 m2s equ 0C0h ; 2 ; 8251 interrupt mask port bits txmsk equ 01h ;disable transmit complete interrupt rxmsk equ 02h ;disable receive complete interrupt tbemsk equ 04h ;disable transmit buffer empty interrupt outlmt EQU 1000H ;Number of times to check output status ; before giving up on send. ;[20d] ; dispatch: Under Concurrent CP/M, releases the processor for other ; use when waiting for either the receive or the send status ; routines to indicate success. dispatch: push ax push bx push cx mov cl,8Eh ; P-Dispatch int 224 pop cx pop bx pop ax ret ; 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 outwt1 ;no - go on cmp xofrcv, true ;are we being held? je outwt3 ;yes - return status not ready outwt1: push ax mov dx,mnsts1 in al,dx and al,mndsr+mnout sub al,mndsr+mnout jnz outwt2 mov dx,mnsts2 in al,dx and al,mncts jnz outwt4 outwt2: pop ax outwt3: call dispatch ;let other CCPM processes have a chance ;[36] ret outwt4: pop ax jmp rskp ; Output data to port. Trashes DX and prints char in AL. outchr: mov dx,mndata out dx,al ret ; 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? jne inst2 call dispatch ;let other CCPM processes have a chance ;[36] 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 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 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 ;Get our data segment address. call mnproc ;Process the character. mov dx, iccmd mov al, icEOI ;signal end of interrupt to controller out dx, al 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,mnsts1 in al,dx ;Get the port status. and al,mninp ;Is a character waiting? jnz mnpro2 ; Yes, go take care of it. ret ; No, just a false alarm. mnpro2: 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 ; [20b] start prtbrk: ; mov dx,mncmd ;break goes to command port mov al,cbrk+crts+cerr+crxe+cdtr+ctxe ;add break to normal command out dx,al mov ax, 275 ;.. for 275 millisec's [34] call mswait ; [34] mov al,crts+cerr+crxe+cdtr+ctxe ;RTS & DTR high, Rx & Tx enabled out dx,al ;return to normal setting 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 ; 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 push es ; Make DEL key return a 7F code rather than the 18 (control-X) ;[30a] begin ; that it ordinarily does. This involves modifying a key ; conversion table in the BIOS. Version 1.107 and later ; contain a pointer to this table at 40:256C, but earlier ; versions which do not are still in common use, so I will ; try to find it with a direct search. cld mov ax, 40h ;BIOS segment mov es, ax mov di, 2500h ;Start of BIOS mov al, 0FCh ;Key gives this value mov ah, 18h ;Translated to this value mov cx, -1 serina: repnz scasb ;Look for the key value or cx, cx ;Zero if search failed jz serinb ; If so, do nothing cmp es:[di], ah ;Translation value next? jne serina ; If not, keep looking mov kbpat, di ;Save the location we're patching. mov al, 7Fh ;Insert a DEL character mov es:[di], al serinb: ;...and continue ;[30a] end mov dx,icmask in al,dx ;get current interrupt mask mov mnxmsk,al ;save it for restore ; NEC recommends that the timer be turned off during interrupt-driven ; serial I/O, but this disables the clock and keyboard repeat. I have ; not had any bad results from leaving it enabled. I will leave the ; disabling code here in case something develops. -- RonB ; or al,ictmof+icmnof;mask off timer and sio interrupts or al,icmnof ;mask off sio interrupt out dx,al 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 mov dx,mntdc mov al,00h ;enable transmission of data out dx,al mov dx,mndata ;dummy read to clear buffer in al,dx mov dx,mnmsk mov al,txmsk+tbemsk ;set interrupt mask (enable read int) out dx,al mov dx,icmask in al,dx ;enable sio interrupts and al,not icmnof out dx,al pop es serin2: ret ; serfin - this routine is used to "undo" what serini has done, called ; just before exiting back to cp/m. serfin: cmp mninit,0FFh ;check if initialization has been done jne serfn2 ;if not, don't de-initialize mov mninit,0 push es ; Unpatch the keyboard conversion table ;[30a] begin ; Restore control-X value for DEL key from our 7F value. les di, dword ptr kbpat ;Get the patch location or di, di ;Did we patch it at all? jz serfia ; If not, skip this mov kbpat, 0 ;Show no longer patched mov al, 18h ;Restore control-X value mov es:[di], al serfia: ;...and continue ;[30a] end cli mov dx,icmask mov al,mnxmsk ;restore the old interrupt mask out dx,al 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,mncmd mov al,0 ;recommended reset procedure: out dx,al ;three 0's followed by a cmode mov al,0 out dx,al mov al,0 out dx,al mov al,cmode ;enable mode setting out dx,al mov al,m1s ;1 stop, no parity, 8 data, 16x baud add al,mpn ;Note: these adds are distinct to add al,m8d ; allow the 8251 time to reset add al,m16x out dx,al mov al,crts+cerr+crxe+cdtr+ctxe ;RTS & DTR high, Rx & Tx enabled out dx,al ret ; set the baud rate stbaud: mov al,mnbaud ;get the baud rate information cmp al,12 ;check for valid range (0-12) ja stb02 mov bx,offset baudtb;get address of baud rate table add al,al ;compute word offset mov ah,0 add bx,ax mov dx,tmcmd mov al,tmch1 ;select timer channel 1 out dx,al mov dx,tmdata mov ax,[bx] ;get value out dx,al ;output low byte mov al,ah out dx,al ;output high byte stb02: ret dseg $ ; Serial port default parameters mnbaud db 6 ;300 baud ; Interval Timer values (assumes 16x baud rate mode) baudtb dw 0C00h ;50 baud 0 dw 0800h ;75 baud 1 dw 0600h ;100 baud 2 dw 0574h ;110 baud 3 dw 0400h ;150 baud 4 dw 0300h ;200 baud 5 dw 0200h ;300 baud 6 dw 0100h ;600 baud 7 dw 0080h ;1200 baud 8 dw 0040h ;2400 baud 9 dw 0020h ;4800 baud 10 dw 0010h ;9600 baud 11 dw 0008h ;19200 baud 12 mninit db 0 ;set to 0FFh if initialization has been done mnxmsk db 0 ;8259 interrupt mask storage mnxseg dw 0 ;system sio 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] kbpat dw 0000h,0040h ;patch location for DEL key ;[30a] 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: 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 ; This is the SET PORT subcommand (not implemented in APC) 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: 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 shoprt: ret ;Port selection not implemented. DSEG $ bdtab db 13 ;Thirteen entries ;[6] begin db 3,'100$' dw 0002H db 3,'110$' dw 0003H db 4,'1200$' dw 0008H db 3,'150$' dw 0004H db 5,'19200$' dw 000CH db 3,'200$' dw 0005H db 4,'2400$' dw 0009H db 3,'300$' dw 0006H db 4,'4800$' dw 000AH db 2,'50$' dw 0000H db 3,'600$' dw 0007H db 2,'75$' dw 0001H db 4,'9600$' dw 000BH ;[6] end bdhlp db cr,lf,' 50 100 150 300 1200 4800 19200' db cr,lf,' 75 110 200 600 2400 9600$' ; 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 apccls 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: ret ; BLDOFF - turns off bold (highlighted) display bldoff: ret DSEG $ anspos db esc,'[00;00H$' ;Position cursor to row and column apccls db 1Eh,1Ah,'$' ;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 ; Here tab expansion is done if necessary. If not, just return retskp. cseg $ dotab: cmp dl, tab ;A tab? je dotab1 jmp rskp ;No, just proceed. dotab1: mov curbuf, 2 ;Report cursor position command. mov dx, offset curbuf mov cl, 7 int 220 ;Special BIOS function. mov al, curbuf+2 ;Column position in binary (0-79). and al, 07h ;Number of spaces needed mov cx, 0008h ; = 8 - (col mod 8) sub cl, al dotab2: push cx mov dl, ' ' call dbout2 pop cx loop dotab2 ret dseg $ curbuf db 2,0,0 ;command, row, column of cursor position ;[7] delstr db 10O,10O,'$' ;Delete string. system db ' NEC Advanced Personal Computer$' ;[1][20a] <<< c86xfj.a86 >>> ; * * * * * * * * * * * * * * * version 2.7 * * * * * * * * * * * * * * * ; [30e] Add support for Fujitsu Micro 16s ; Chris Barker, 01/04/85 ; [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 Fujitsu Micro 16s CSEG $ ; Clock rate *10 for timing loops ;[19g] clckrt equ 80 ;[19g] 8.0 Mhz ;[30e] ; Interrupt vector locations, in data segment 0 mnioff equ 30h ;sio interrupt offset mniseg equ 32h ;sio interrupt segment ; 8259 Interrupt controller (master) iccmd equ 00h ;interrupt command register icmask equ 02h ;interrupt mask register ; 8259 commands and masks icEOI equ 20h ;end of interrupt (command) ;ictmof equ 08h ;disable timer (mask) icmnof equ 04h ;disable RS232 (mask) ; Baud Rate controller (MB 14417) bdport equ 0FD59h ;baud rate select (read/write) ; 8251A USART controller mndata equ 0FD06h ; data port mnsts1 equ 0FD07h ;in status port mncmd equ 0FD07h ;out command port ; 8251 status port 1 bits mninp equ 02h ;receive ready value mnout equ 01h ;send ready value mndsr equ 80h ;data set ready ; 8251 initialization instructions ; command instructions ctxe equ 01h ;transmit enable cdtr equ 02h ;dtr signal high crxe equ 04h ;receive enable cbrk equ 08h ;send break cerr equ 10h ;error reset crts equ 20h ;rts signal high cmode equ 40h ;reset - go to mode instruction format chunt equ 80h ;hunt for sync characters ; mode instructions m1x equ 01h ;baud rate factor: 1x m16x equ 02h ; 16x m64x equ 03h ; 64x m5d equ 00h ;data bits: 5 m6d equ 04h ; 6 m7d equ 08h ; 7 m8d equ 0Ch ; 8 mpn equ 00h ;parity: none mpo equ 10h ; odd mpe equ 30h ; even m1s equ 40h ;stop bits: 1 m15s equ 80h ; 1.5 m2s equ 0C0h ; 2 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,mnsts1 in al,dx and al,mndsr+mnout sub al,mndsr+mnout jz outwt2 outwt1: pop ax ret outwt2: pop ax jmp rskp ; Output data to port. Trashes DX and prints char in AL. outchr: mov dx,mndata out dx,al ret ; 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. mov dx, iccmd mov al, icEOI ;signal end of interrupt to 8259A out dx, al 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,mnsts1 in al,dx ;Get the port status. and al,mninp ;Is a character waiting? jnz mnpro2 ; Yes, go take care of it. ret ; No, just a false alarm. mnpro2: 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 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 ; [20b] start prtbrk: mov dx,mncmd ;break goes to command port mov al,cbrk+crts+cerr+crxe+cdtr+ctxe ;add break to normal command out dx,al mov cx, 25000 ;sit for a while prtbk1: loop prtbk1 mov al,crts+cerr+crxe+cdtr+ctxe ;RTS & DTR high, Rx & Tx enabled out dx,al ;return to normal setting ret ; [20b] end ; 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 push es mov dx,icmask in al,dx ;get current interrupt mask mov mnxmsk,al ;save it for restore ; or al,ictmof+icmnof;mask off timer and sio interrupts or al,icmnof ;mask off sio interrupt out dx,al 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 mov dx,mndata ;dummy read to clear buffer in al,dx mov dx,icmask in al,dx ;enable sio interrupts and al,not icmnof out dx,al 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 mov dx,icmask mov al,mnxmsk ;restore the old interrupt mask out dx,al 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: ;do this all in stbaud for micro16s ; set the baud rate stbaud: mov al,mnbaud ;get the baud rate information cmp al,6 ;check for valid range (0-6) ja stb03 ;j/ out of valid range mov bx,offset baudtb;get address of baud rate table mov ah,0 add bx,ax mov dx,bdport mov al,[bx] ;get value out dx,al ;output byte mov al,m64x jne stb02 ; mov al,m16x ;for 19200 bd stb02: push ax mov dx,mncmd mov al,0 ;recommended reset procedure: out dx,al ;three 0's followed by a cmode mov al,0 out dx,al mov al,0 out dx,al mov al,cmode ;enable mode setting out dx,al pop ax add al,m1s ;1 stop, no parity, 8 data, 16x baud add al,mpn ;Note: these adds are distinct to add al,m8d ; allow the 8251 time to reset out dx,al mov al,crts+cerr+crxe+cdtr+ctxe ;RTS & DTR high, Rx & Tx enabled out dx,al stb03: ret dseg $ ; Serial port default parameters mnbaud db 5 ;9600 baud ; Interval Timer values (assumes 64x baud rate mode) baudtb db 00h ;300 baud 0 db 11h ;600 baud 1 db 22h ;1200 baud 2 db 33h ;2400 baud 3 db 44h ;4800 baud 4 db 55h ;9600 baud 5 db 44h ;19200 baud 6 - requires 16x rate mode mninit db 0 ;set to 0FFh if initialization has been done mnxmsk db 0 ;8259 interrupt mask storage mnxseg dw 0 ;system sio 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 APC) 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 7 ;Thirteen entries ;[6] begin db 4,'1200$' dw 0002h db 5,'19200$' dw 0006h db 4,'2400$' dw 0003h db 3,'300$' dw 0000h db 4,'4800$' dw 0004h db 3,'600$' dw 0001h db 4,'9600$' dw 0006h ;[6] end bdhlp db cr,lf,' 300 600 1200 2400' db cr,lf,' 4800 9600 19200$' ; 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 ax, [bx] ; add ax, ' ' ;Add 20h to both row and column mov word ptr anspos+2, ax lea dx, anspos ;Print cursor positioning string. call tmsg ret ; CLRSCR - homes cursor and clears screen. clrscr: lea dx, fujcls 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 DSEG $ anspos db esc,'=00$' ;Position cursor to row and column fujcls db 1Eh,1Ah,'$' ;Home cursor and clear screen ansclr db esc,'T$' ;Clear from cursor to end of line ansron db esc,'G',0Ch,'$' ;Turn on reverse video (green) ansrof db esc,'G',04h,'$' ;Turn off reverse video (green) ansbon db esc,'G',05h,'$' ;Turn on bold (uses blue) ansbof db esc,'G',04h,'$' ;Turn off bold (back to green) ; Here tab expansion is done if necessary. If not, just return retskp. cseg $ dotab: jmp rskp ;No, just proceed. dseg $ curbuf db 2,0,0 ;command, row, column of cursor position delstr db 10O, 10O, '$' ;Delete string. system db ' FUJITSU Micro 16s $' ;[1][20a] <<< c86xfu.a86 >>> ; STARTSYSDEP ; This is so: ; ; PIP LISTING=86KERMIT.LST[WSSTARTSYSDEP^ZQENDxSYSDEP^Z] ; ; will work. ; ; ; ************************************************************************** ; ; This is the i/o support module for the Future Computers FX20/FX30 ; Running CP/M-86 or Concurrent CP/M. ; ; Tony Chabot, University of Birmingham, UK October 1985 ; (Based on the version for the Honeywell MSE by Mark Hewitt). ; ; This module checks which type of CP/M system it is running under, ; and alters its performance accordingly. No separate assembly is ; required for CP/M-86 and Concurrent. ; It is preferable to use the KERUTL from Concurrent implementations ; as that will work with both types of CP/M, whereas the standard ; version of KERUTL (for CP/M-86) will not work fully with Concurrent. ; ; This implementation allows only the modem port to be used. ; This is because the printer port does not have its rx interrupt request ; line connected to the 8259 interrupt controller. ; ; The modem port uses a 7201 Multi Protocol Serial Controller, situated ; at address C120-C123, with the relevant addresses for the modem part ; of it being C121 & C123.(Note - the FX20/FX30 manual incorrectly gives ; the addresses as C122 and C123). ; The baud rate generator is an 8253-5, timer 1. ; The range of addresses for this device is C060-C063. The generator ; has a 4MHz clock input (gleaned from a scope, so this is not too accurate, ; but a frequency meter seemed to think the frequency was 1MHz +- 10% !). ; The 7201 is programmed to divide by 16, so the baud rate divider is given ; by ; divider = 4000000/(16*baudrate) ; ; The 8259 interrupt controller is programmed (by CP/M) to base its vectors ; at 100. The relationship between the 8259 IR lines and the requesting device ; is: ; ; IR0 ? ; IR1 PIT (out0) ; IR2 7201 ; IR3 ? ; IR4 ? ; IR5 Keyboard 8251 RxRdy ; IR6 Printer 8251 TxRdy ; IR7 Pulled up. ; ; Only IR0, IR1 and IR5 have useful interrupt service routines, as well ; as the predefined vectors and CP/M system call vectors. All other ; vectors point, via some register saving code, to an illegal interrupt ; routine. It is thus simple to patch the vector for the 7201 (at 108-10B) ; to point to our ISR. ; ; ** W A R N I N G ** ; ; The 7201 also handles the LAN, so if you have lan software running, it ; is likely that this code will cause problems. ; ; ; ************************************************************************** CSEG $ ; Port base definitions bgen equ 0C060h ; Baud Rate Generator (8253-5) ictrl equ 0C000h ; Interrupt Controller (8259A) ; And the I/O ports themselves bgcmd equ bgen+3 ; Baud rate generator command port iccmd equ ictrl+0 ; Interrupt controller command port icmask equ ictrl+1 ; Interrupt controller mask register port mdmcmd equ 0C123h ; modem command port mdmbg equ bgen+1 ; Baud rate countdown value for modem port mdmio equ 0C121h ;modem data io port (Note-FX30 manual is wrong) ptrcmd equ 0C122h ; ; ; Port selection ; pmdm equ 0 ; ; Interrupt vectors in page 0 ; mdmvec equ 0108h ; Interrupt vector for modem. ; ; Interrupt masks ; immdm equ 04h ; Mask for modem port. ; ; Baud rate generator command words ; mdmbsel equ 76h ; Select modem baud rate register ; ; Interrupt controller commands ; iceoi equ 20h ; end of interrupt ; ; I/O register bits: ; ; For 7201 'Multiprotocol Serial Communications Controller' ( a name ; worthy of IBM !). ; ccreg0 equ 00h ; Control instruction - select register 0 ccreg1 equ 01h ; Control instruction - select register 1 ccreg2 equ 02h ; Control instruction - select register 2 ccreg3 equ 03h ; Control instruction - select register 3 ccreg4 equ 04h ; Control instruction - select register 4 ccreg5 equ 05h ; Control instruction - select register 5 ccreg6 equ 06h ; Control instruction - select register 6 ccreg7 equ 07h ; Control instruction - select register 7 c0null equ 00h ; Register 0 - null command c0abort equ 08h ; Register 0 - send abort c0resi equ 10h ; Register 0 - reset ext. status ints. c0chrst equ 18h ; Register 0 - channel reset c0eninc equ 20h ; Register 0 - enable int. on next character c0rpti equ 28h ; Register 0 - reset pending tx int./DMA req. c0errst equ 30h ; Register 0 - error reset c0eoi equ 38h ; Register 0 - end of interrupt c0rxcrc equ 40h ; Register 0 - reset rx CRC checker c0txcrc equ 80h ; Register 0 - reset tx CRC generator c0ricrc equ 0C0h ; Register 0 - reset idle/CRC latch c1stien equ 01h ; Register 1 - external/status int enable c1txien equ 02h ; Register 1 - transmitter interrupt enable c1cav equ 03h ; Register 1 - condition affects vector c1noi equ 00h ; Register 1 - no rx or DMA interrupts c1i1st equ 08h ; Register 1 - int. on 1st received character c1iall equ 10h ; Register 1 - int. on all received characters c1ialp equ 18h ; Register 1 - int on all rx'd chars, no parity c1wrxtx equ 20h ; Register 1 - WAIT on rx/tx c1txbcm equ 40h ; Register 1 - TX byte count mode enbable c1wten equ 80h ; Register 1 - WAIT function enable ; ; and some useful abbreviations ; c1norm equ c1ialp ; c2dma0 equ 00h ; Register 2 - No DMA c2dma1 equ 01h ; Register 2 - DMA mode 1 c2dma2 equ 02h ; Register 2 - DMA mode 2 c2dma3 equ 03h ; Register 2 - DMA mode 3 c2pri equ 04h ; Register 2 - Set DMA priority c2ack0 equ 00h ; Register 2 - Int Ack mode 0 (NV,D432) c2ack1 equ 08h ; Register 2 - Int Ack mode 1 (NV, D432) c2ack2 equ 10h ; Register 2 - Int Ack mode 2 (NV, D210) c2ack4 equ 20h ; Register 2 - Int Ack mode 4 (8085 master) c2ack5 equ 28h ; Register 2 - Int Ack mode 5 (8085 slave) c2ack6 equ 30h ; Register 2 - Int Ack mode 6 (8086) c2ack7 equ 38h ; Register 2 - Int Ack mode 7(8085/8259A slave) c2rxim equ 40h ; Register 2 - rx interrupt mask c2syncb equ 80h ; Register 2 - pin 10 ~RTSB or ~SYNCB c3rxen equ 01h ; Register 3 - receive enable c3scli equ 02h ; Register 3 - sync character load inhibit c3asm equ 04h ; Register 3 - address search mode c3rxcrc equ 08h ; Register 3 - receiver CRC enable c3hunt equ 10h ; Register 3 - enter hunt phase c3aen equ 20h ; Register 3 - auto enables on DCD/CTS c3r5bit equ 00h ; Register 3 - 5 bit data c3r6bit equ 40h ; Register 3 - 6 bit data c3r7bit equ 80h ; Register 3 - 7 bit data c3r8bit equ 0C0h ; Register 3 - 8 bit data ; ; and some useful abbreviations ; c3norm equ c3rxen+c3r8bit ; c4pen equ 01h ; Register 4 - parity enable c4ep equ 02h ; Register 4 - even parity c41stp equ 04h ; Register 4 - 1 stop bit c415stp equ 08h ; Register 4 - 1.5 stop bits c42stp equ 0C0h ; Register 4 - 2 stop bits c48syn equ 00h ; Register 4 - 8 bit internal sync (monosync) c416syn equ 10h ; Register 4 - 16 bit internal sync (bisync) c4sdlc equ 20h ; Register 4 - SDLC c4exts equ 30h ; Register 4 - External sync c41clk equ 00h ; Register 4 - 1x clock rate c416clk equ 40h ; Register 4 - 16x clock rate c432clk equ 80h ; Register 4 - 32x clock rate c464clk equ 0C0h ; Register 4 - 64x clock rate ; ; and some useful abbreviations ; c4norm equ c41stp+c416clk ; c5txcrc equ 01h ; Register 5 - transmitter CRC enable c5rts equ 02h ; Register 5 - request to send c5poly equ 04h ; Register 5 - CRC polynomial select c5txen equ 08h ; Register 5 - transmitter enable c5sbrk equ 10h ; Register 5 - send break c5t5bit equ 00h ; Register 5 - transmit 5 bit data c5t6bit equ 20h ; Register 5 - transmit 6 bit data c5t7bit equ 40h ; Register 5 - transmit 7 bit data c5t8bit equ 60h ; Register 5 - transmit 8 bit data c5dtr equ 80h ; Register 5 - data terminal ready ; ; and some useful abbreviations ; c5norm equ c5rts+c5txen+c5t8bit+c5dtr ; cs0rxr equ 01h ; Status register 0 - received char ready cs0ip equ 02h ; Status register 0 - interrupt pending cs0tbe equ 04h ; Status register 0 - tx buffer empty cs0dcd equ 08h ; Status register 0 - data carrier detect cs0sync equ 10h ; Status register 0 - sync status cs0cts equ 20h ; Status register 0 - clear to send cs0idle equ 40h ; Status register 0 - idle CRC latch status cs0brk equ 80h ; Status register 0 - break detect cs1sent equ 01h ; Status register 1 - all sent cs1sdlc equ 0Eh ; Status register 1 - SDLC residue code cs1pe equ 10h ; Status register 1 - parity error cs1oe equ 20h ; Status register 1 - overrun error cs1fe equ 40h ; Status register 1 - framing error cs1eosf equ 80h ; Status register 1 - end of SDLC frame ; System call defs for concurrent version. p_dispatch equ 8Eh ; Reschedule. f_errmode equ 2Dh ; Set BDOS error mode. ; ; Clock rate *10 for timing loops ;[19g] ; clckrt equ 80 ;[19g] 8.0 Mhz ; ; Maximum number of examinations of output port to be ready before ; rescheduling. ; outlmt equ 1000h ; ; The executable code starts here ; ; ; =========================================================================== ; ; INITIALISATION ROUTINES ; ; =========================================================================== ; ; INTERFACE ROUTINE SERINI - Initialisation code ; serini: cmp mninit, true ; Ensure that we only initialise once je serin2 mov mninit, true ; Get type of CP/M system. ; mov cl,0ch int bdos mov cpmtyp, bh ; ; Initialise the screen ; call clrscr ; Clear the screen. ; ; Disable I/O interrupts, and save the old interrupt mask. ; mov dx, icmask ; read the current interrupt mask in al, dx mov oldmsk, al ; and save it or al, immdm ; mask off i/o interrupts out dx, al ; and reprogram interrupt controller ; ; Save the system i/o interrupt vectors ; mov ax, ds ; save the data segment in code segment mov cs:mndseg, ax ; for use by interrupt handler mov ax, 0 ; point to zero page and save both the mov es, ax ; system's i/o interrupt vectors mov ax,es:.mdmvec+0 ; for the modem channel mov vscoff, ax mov ax, es:.mdmvec+2 mov vscseg, ax ; Configure the default port ; mov ax, 0 ; point to zero page and set the interrupt mov es, ax ; vector for the modem/printer channel to my ; interrupt service routine mov ax, offset isr ; set offset address mov es:.mdmvec+0, ax mov ax, cs ; set segment address mov es:.mdmvec+2, ax call setmode ; set UART mode for current port call setbaud ; set the baud rate for the current port call mnflush ; flush and enable the current port call inton ; turn interrupts on for current port ; If concurrent, set BDOS error mode. ; cmp cpmtyp, 14h jne serin2 mov cl, f_errmode mov dl, 0FEh ; Set err mode to display and return. int bdos serin2: ret ; initialisation over ; ; INTERFACE ROUTINE SERFIN - restore environment (as far as possible) ; to that which existed before we played with it ; serfin: cmp mninit, true ; only deinitialise if necessary jne serfn2 mov mninit, false ; ; Disable i/o interrupt while we reset the vectors ; mov dx, icmask ; get present interrupt mask in al, dx ; and turn off all i/o interrupts or al, immdm ; from the modem channel out dx, al ; reprogram the interrupt controller ; ; Reset the i/o interrupt vectors ; mov ax, 0 ; point at page 0 and reset the int. vectors mov es, ax mov ax, vscoff ; for the modem/printer port mov es:.mdmvec+0, ax mov ax, vscseg mov es:.mdmvec+2, ax ; ; turn interrupts back on (or off...) ; mov al, oldmsk ; restore original interrupt mask out dx, al ; ; Reset screen modes ; call clrscr ; Be tidy - clear the screen. serfn2: ret ; deinitialisation over ; ; ; INTERNAL ROUTINE SETMODE - set the operating mode for current port's UART. ; setmode: push ax push dx ; we'll need this mov dx, mdmcmd ; Command port adrs. mov al, c0chrst ; reset the port out dx, al mov al, c0resi+ccreg4 ; select register 4 out dx, al mov al, c4norm ; 16x Clock, 1 stop bit, no parity out dx, al mov al, c0resi+ccreg3 ; Select register 3 out dx, al mov al, c3norm ; 8 bits/character, RX enable out dx, al mov al, c0resi+ccreg5 ; select register 5 out dx, al mov al, c5norm ; 8 bits/character, TX enable RTS and DTR out dx, al mov al, c0resi+ccreg1 ; select register 1 out dx, al mov al, c1norm ; Interrupt enable out dx, al pop dx ; modes now set, restore regs. and return pop ax ret ; ; INTERNAL ROUTINE SETBAUD - set the baud rate of a current port. ; port number in cport. ; timer countdown table offset in cbaud. ; setbaud: push bx ; we'll be using this push dx ; and this push ax ; and this too mov al, bdtab ; check that rate is legal dec al ; pick up number of valid rates from BDTAB cmp cbaud, al ; 0 <= cbaud <= [bdtab]-1 ja setbd2 ; just return if not legal mov bx, offset bdtct; get timer value mov al, cbaud ; from timer countdown table mov ah, 0 add al, al ; word offset add bx, ax ; bx now points to correct value mov dx, bgcmd ; dx is now baud rate generator command port cmp cport, pmdm ; is it the modem port? jne setbd2 ; just return if not mov al, mdmbsel ; set baud rate for modem port out dx, al mov dx, mdmbg jmp setbd3 setbd3: mov ax, [bx] ; set the countdown value out dx, al mov al, ah out dx, al setbd2: pop ax ; baud rate set, retore regs. and return pop dx pop bx ret ; ; INTERNAL ROUTINE MNFLUSH - enable and flush current port. ; Port in cport. ; mnflush: push ax ; preserve registers push dx mov dx, mdmio ; Modem data port adrs. in al, dx ; flush the port in al, dx in al, dx mov dx, mdmcmd ; reset any pending interrupts mov al, c0errst out dx, al mov al, c0resi out dx, al pop dx ; port flushed, retore regs. and return pop ax ret ; ; INTERNAL ROUTINE INTON - enable interrupts for the selected port ; (This version simply enables the modem port!) ; Ensure that the port selected is enabled, and ; that all other ports are as the system would ; wish them! inton: push ax push dx mov dx, icmask in al,dx ; Read current interrupt mask. and al, not immdm ; Enable modem interrupts. out dx, al inton2: pop dx pop ax ; interrupts now enabled - restore regs. ret ; and return DSEG $ ; Data used by initialisation/deinitialisation mninit db false ; flag set when initialised oldmsk rb 1 ; Old interrupt mask cpmtyp db 0 ; CP/M type (0 = CP/M-86, 14h = concurrent). ; ; Current port status ; cport db pmdm ; current port number - default to modem cbaud db 8 ; current baud rate - default to 4800 ciop dw mdmio ; current i/o port - default to modem ccmdp dw mdmcmd ; current command/status port - default modem ; ; Storage for system interrupt vectors ; vscoff rw 1 ; offset for system v.24/printer int. vector vscseg rw 1 ; seg. address for system v.24/printer int. vec ; ; Baud rate timer countdown table ; (The accuracy of these is uncertain as the baud rate generator clock ; frequency was measured with a scope). ; bdtct dw 5000 ; 50 baud, code 0 dw 3333 ; 75 dw 2273 ; 110 dw 1667 ; 150 dw 833 ; 300 dw 417 ; 600 dw 208 ; 1200 dw 104 ; 2400 dw 52 ; 4800 dw 26 ; 9600 dw 13 ; 19200 CSEG $ ; =========================================================================== ; ; SET COMMANDS ; ; =========================================================================== ; ; INTERFACE ROUTINE BDSET - set baud rate for current port (cport). ; save current baud rate in cbaud. ; bdset: mov dx, offset bdtab ; table of valid baud rates mov bx, offset bdhlp ; help information for SET BAUD mov ah, cmkey ; Command parser - KEYWORD lookup call comnd jmp r ; error return mov settmp, bx ; Normal return - save value mov ah, cmcfm ; Command parser - CONFIRM call comnd jmp r mov bx, settmp mov cbaud, bl ; save the baud rate call setbaud ; and set it for the current port jmp rskp ; end of parsing SET BAUD command DSEG $ settmp rw 1 ; temporary storage for baud rate CSEG $ ; ; INTERFACE ROUTINE PRTSET - set the current port. ; prtset: mov dx, offset potab ; table of valid port names mov bx, offset pohlp ; help information for SET PORT mov ah, cmkey ; Command parser - KEYWORD lookup call comnd jmp r ; error return mov settmp, bx ; Normal return - save value mov ah, cmcfm ; Command parser - CONFIRM call comnd jmp r jmp rskp ; end of parsing SET PORT command ; ; Data required by the SET commands ; DSEG $ ; SET command data ; ; Baud rate table ; bdtab db 11 ; number of entries db 3, '110$' ; size of entry, and the keyword$ dw 02 ; value returned db 3, '150$' dw 03 db 4, '1200$' dw 06 db 5, '19200$' dw 10 db 4, '2400$' dw 07 db 3, '300$' dw 04 db 4, '4800$' dw 8 db 2, '50$' dw 00 db 3, '600$' dw 05 db 2, '75$' dw 01 db 4, '9600$' dw 09 ; ; Help table for baud rate setting ; bdhlp db cr, lf, ' 50 75 110 150 300 600' db cr, lf, ' 1200 2400 4800 9600 19200' db '$' ; ; Port table ; potab db 1 db 5, 'MODEM$' dw pmdm ; ; Help table for port selection ; pohlp db cr, lf, 'MODEM$' CSEG $ ; =========================================================================== ; ; SHOW COMMANDS ; ; =========================================================================== ; ; INTERFACE ROUTINE SHOBD - display the currently set baud rate within ; the SHOW command. ; shobd: mov dx, offset bdst ;Baud rate string. call tcrmsg mov al, cbaud ;Print the keyword corresponding to the mov bx, offset bdtab; current value of mnbaud. call tabprt ret ; ; INTERFACE ROUTINE SHOPRT - display the currently selected communication ; port within the SHOW command. ; shoprt: mov dx, offset prtst ; Port name string call tcrmsg mov al, cport ; current port code mov bx, offset potab ; and print the corresponding call tabprt ; textual description mov dx, offset prtst2 call tmsg ret DSEG $ prtst db 'Communicating via $' prtst2 db ' port$' CSEG $ ; =========================================================================== ; ; I/O ROUTINES ; ; =========================================================================== ; ; INTERNAL ROUTINE ISR - Interrupt service routine for modem port. ; isr: cli ; disable intrerupts mov cs:mnax, ax ; save ax - we will need a register mov ax, sp mov cs:mnsp, ax ; save current stack pointer mov ax, ss mov cs:mnsseg, ax ; Save current stack segment mov ax, cs:mndseg ; Switch to my stack mov ss, ax mov sp, offset mnstk push ds ; Save registers push es push bp push di push si push dx push cx push bx mov ds, ax ; set our data segment address ; ; That's the housekeeping out of the way - now we can start ; mov dx, mdmcmd ; see if char. ready at default port in al, dx test al, cs0rxr ; is there a character for us? jz iprt3 ; no - clear interrupt, and return iprt2: mov dx, mdmio ; Fetch the character in al, dx call iproc ; Process the character in AL iprt3: mov dx, iccmd ; Signal end of interrupt to mov al, iceoi ; interrupt controller out dx, al mov dx, ptrcmd ; Clear interrupt status at mov al, c0eoi ; 7201 out dx, al ; (note we use the A channel). pop bx ; Restore registers pop cx pop dx pop si pop di pop bp pop es pop ds mov ax, cs:mnsp ; restore interrupt stack mov sp, ax mov ax, cs:mnsseg ; restore original stack segment mov ss, ax mov ax, cs:mnax ; restore original AX iret ; all over - return ; ; CSEG data required by interrupt service routine ; mnax dw 0 ; temp. copy of AX mnsp dw 0 ; interrupt stack pointer mnsseg dw 0 ; interrupt stack segment mndseg dw 0 ; location of our data segment ; ; INTERNAL ROUTINE IPROC - process incoming character from Rx interrupt ; Character in AL ; iproc: cmp floctl, floxon ;are we doing flow-control ? [19a] start jne ipr2b ;no - go on cmp al, xoff ;is it an XOFF? jne ipr2a ;no - go on mov xofrcv, true ;set the flag ret ipr2a: cmp al, xon ;an XON? jne ipr2b ;no mov xofrcv, false ;clear the flag ret ; [19a] end ipr2b: cmp mnchrn,mnchnd ;Is the buffer full? je iperr ;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 ipro3 mov bx, offset mnchrs ;Yes, point to the start again. ipro3: mov mnchip,bx ;Save the pointer. mov [bx],al ;Put the character in the buffer. cmp floctl, floxon ;do flow-control? [19a] start je ipro4 ;If yes jump ret ipro4: cmp xofsnt, true ;Have we sent an XOFF jnz ipro5 ret ;return if we have ipro5: cmp mnchrn, mntrg2 ;Past the High trigger point? ja ipro6 ;yes - jump ret ipro6: mov al, xoff call prtout ;send an XOFF mov xofsnt, true ;set the flag ret ; [19a] End iperr: ret ; just return on error for now ; ; INTERFACE ROUTINE CFIBF - Clear serial port input buffer ; 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 ; ; INTERFACE ROUTINE PRTOUT - send character in AL to current port. ; prtout: call dopar ; set parity if necessary push dx push cx mov cx, outlmt prtou2: call outwait ; wait for port to be free, or timeout loop prtou2 nop call outchr ; output the character pop cx pop dx ret ; ; INTERNAL ROUTINE OUTWAIT - test if port ready for next char to be sent. ; returns RSKP if ready. ; outwait: cmp floctl, floxon jne outwt1 cmp xofrcv, true je outwt3 outwt1: push ax mov dx, mdmcmd in al, dx test al, cs0tbe jnz outwt4 pop ax outwt3: cmp cpmtyp, 14h ; Concurrent? jne outwt35 ; No. call dispatch ; Yes - redispatch the processor. outwt35: ret outwt4: pop ax jmp rskp ; ; INTERNAL ROUTINE OUTCHR - send data to a port ; outchr: mov dx, mdmio out dx, al ret ; ; INTERFACE ROUTINE INSTAT - determine if there is any data to receive. ; instat: cmp mnchrn, 0 ; any characters in buffer? jne inst2 ret inst2: jmp rskp ; ; INTERFACE ROUTINE INCHR - read a character from a port ; 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 ; ; INTERFACE ROUTINE PRTBRK - Send a BREAK sequence to the default port ; prtbrk: mov dx, mdmcmd ; Modem port cmd byte. cmp cport, pmdm ; is it modem port? jne brka ; must be an error - just return brkc: mov al, c0resi+ccreg5 ; Break to modem port. out dx, al ; Select register 5 mov al, c5norm+c5sbrk ; 8 bits, TX enable, Break, RTS & DTR out dx, al mov ax, 275 ; for 275 mS call mswait mov al, c0resi+ccreg5 ; select register 5 out dx, al mov al, c5norm ; 8 bits, TX enable, RTS & DTR out dx, al ret brka: ret DSEG $ ; ; Input character queue ; 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 ; ; a small stack for interrupt handling ; rw 64 ;Interrupt stack ;[28e] mnstk dw 0 ;bottom of stack ;[28e] CSEG $ ; =========================================================================== ; ; UTILITY ROUTINES ; ; =========================================================================== ; ; INTERNAL ROUTINE MSWAIT - Delay for AL milliseconds ; 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 ; ; INTERNAL ROUTINE DISPATCH ; ; Function Redispatch processor. ; ; Inputs None. ; ; Outputs None. ; ; Side effects Processor is redispatched. ; All registers preserved. ; dispatch: push ax push bx push cx mov cl, p_dispatch int bdos pop cx pop bx pop ax ret ; =========================================================================== ; ; SCREEN CONTROL ROUTINES ; ; =========================================================================== ; ; INTERFACE ROUTINE POSCUR - positions cursor to row and col (each 1 byte) ; pointed to by dx. ; poscur: mov bx, dx ;Do cursor positioning. mov dx, offset scrpos ;Print cursor positioning string. call tmsg mov al, [bx] ;Get row value add ax, 1Fh ;Convert to ASCII mov dl,al push bx ;(Gets clobbered by bout) call bout pop bx mov al, 1[bx] ;Do same for column value add ax, 1Fh mov dl,al call bout ret ; ; INTERFACE ROUTINE CLRSCR - homes cursor and clears screen. ; clrscr: mov dx, offset scrcls call tmsg ret ; ; INTERFACE ROUTINE CLRLIN - clears line. ; clrlin: mov dl, cr ;Go to beginning of line call bout ; ; ...FALL THROUGH ; ; INTERFACE ROUTINE CLREOL - clear to end of line ; clreol: mov dx, offset scrclr ;Clear from cursor to end of line call tmsg ret ; ; INTERFACE ROUTINE REVON - turns on reverse video display ; revon: mov dx, offset scrron call tmsg ret ; ; INTERFACE ROUTINE REVOFF - turns off reverse video display ; revoff: mov dx, offset scrrof call tmsg ret ; ; INTERFACE ROUTINE BLDON - turns on bold (highlighted) display ; bldon: mov dx, offset scrbon call tmsg ret ; ; INTERFACE ROUTINE BLDOFF - turns off bold (highlighted) display ; bldoff: mov dx, offset scrbof call tmsg ret DSEG $ scrpos db esc, 'Y$' ;Position cursor to row and column scrcls db esc, 'E$' ;Home cursor and clear screen scrclr db esc, 'K$' ;Clear from cursor to end of line scrron db esc, 'p$' ;Turn on reverse video scrrof db esc, 'q$' ;Turn off reverse video scrbon db esc, 'm$' ; Bold on (Actually underline). scrbof db esc, 'n$' ; Bold off CSEG $ ; ; INTERFACE ROUTINE DOTAB - do tab expansion if necessary ; dotab: jmp rskp ; assume h/w does it for now ; ; Assorted textual constants required as part of the machine interface ; DSEG $ delstr db esc,'D ',esc,'D$' ;Delete string. system db ' Future Computers FX20/FX30 (TC - Oct 85)$' CSEG $ ; ; ENDSYSDEP ; <<< c86xr2.a86 >>> ; * * * * * * * * * * * * * * * 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$' <<< c86xrb.a86 >>> ; * * * * * * * * * * * * * * * 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] ; 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$' <<< c86xtx.a86 >>> ; * * * * * * * * * * * * * * * 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$' <<< c86xv9.a86 >>> ; * * * * * * * * * * * * * * * 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]