home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
kermit.columbia.edu
/
kermit.columbia.edu.tar
/
kermit.columbia.edu
/
cpm86
/
c86ker.src
< prev
next >
Wrap
Text File
|
1991-04-17
|
279KB
|
10,129 lines
<<< 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 "<esc-char>?" 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 <CR>).
; 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 <cr> 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 <lf> 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 ;'<tab>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 <Oc.Garland%CU20B@Columbia-20>
; * * * * * * * * * * * * * * * version 2.3 * * * * * * * * * * * * * * *
; [par] Added calls to set parity, strip parity on input if
; other than none parity is called for.
; JD, 2/84
; * * * * * * * * * * * * * * * version 2.2 * * * * * * * * * * * * * * *
; [2] Add a de-initialization routine for the serial port, to restore
; changed interrupt vectors, etc.
; RonB,12/23/83
; [1] Add I/O support for the NEC Advanced Personal Computer
; RonB,12/23/83
; * * * * * * * * * * * * * * * version 2.0 * * * * * * * * * * * * * * *
; This module contains all the low level communications port I/O
; routines.
; 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 <Oc.Garland%CU20B@Columbia-20>
; * * * * * * * * * * * * * * * version 2.3 * * * * * * * * * * * * * * *
; [par] Added calls to set parity, strip parity on input if
; other than none parity is called for.
; JD, 2/84
; * * * * * * * * * * * * * * * version 2.2 * * * * * * * * * * * * * * *
; [2] Add a de-initialization routine for the serial port, to restore
; changed interrupt vectors, etc.
; RonB,12/23/83
; [1] Add I/O support for the NEC Advanced Personal Computer
; RonB,12/23/83
; * * * * * * * * * * * * * * * version 2.0 * * * * * * * * * * * * * * *
; This module contains all the low level communications port I/O
; routines.
; 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 <Oc.Garland%CU20B@Columbia-20>
; * * * * * * * * * * * * * * * version 2.3 * * * * * * * * * * * * * * *
; [par] Added calls to set parity, strip parity on input if
; other than none parity is called for.
; JD, 2/84
; * * * * * * * * * * * * * * * version 2.2 * * * * * * * * * * * * * * *
; [2] Add a de-initialization routine for the serial port, to restore
; changed interrupt vectors, etc.
; RonB,12/23/83
; [1] Add I/O support for the NEC Advanced Personal Computer
; RonB,12/23/83
; * * * * * * * * * * * * * * * version 2.0 * * * * * * * * * * * * * * *
; This module contains all the low level communications port I/O
; routines.
; The following is the I/O code for the DEC Rainbow.
CSEG $
; Clock rate *10 for timing loops ;[19g]
clckrt equ 48 ;[19g] 4.8 Mhz
; Interrupt vector locations, in data segment
mnstat EQU 042H ;Status port.
mndata EQU 040H ;Data port.
mnctrl EQU 002H ;Control port.
; Interrupt vector locations. These are all in data segment 0.
mnoff EQU 90H ;Main data port interrupt routine offset.
mnseg EQU 92H ;Main data port interrupt routine segment.
output EQU 04H ;Bit for output ready.
input EQU 01H ;Bit for input ready.
outlmt EQU 1000H ;Number of times to check output status
; before giving up on send. ;[20d]
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 <Oc.Garland%CU20B@Columbia-20>
; * * * * * * * * * * * * * * * version 2.3 * * * * * * * * * * * * * * *
; [par] Added calls to set parity, strip parity on input if
; other than none parity is called for.
; JD, 2/84
; * * * * * * * * * * * * * * * version 2.2 * * * * * * * * * * * * * * *
; [2] Add a de-initialization routine for the serial port, to restore
; changed interrupt vectors, etc.
; RonB,12/23/83
; [1] Add I/O support for the NEC Advanced Personal Computer
; RonB,12/23/83
; * * * * * * * * * * * * * * * version 2.0 * * * * * * * * * * * * * * *
; This module contains all the low level communications port I/O
; routines.
; The following is the I/O code for the DEC Rainbow.
CSEG $
; Clock rate *10 for timing loops ;[19g]
clckrt equ 48 ;[19g] 4.8 Mhz
; Interrupt vector locations, in data segment
mnstat EQU 042H ;Status port.
mndata EQU 040H ;Data port.
mnctrl EQU 002H ;Control port.
; Interrupt vector locations. These are all in data segment 0.
mnoff EQU 90H ;Main data port interrupt routine offset.
mnseg EQU 92H ;Main data port interrupt routine segment.
output EQU 04H ;Bit for output ready.
input EQU 01H ;Bit for input ready.
outlmt EQU 1000H ;Number of times to check output status
; before giving up on send. ;[20d]
; Input data from port. Preserves all ACs and returns char in
; AL. Gets the char from the ring buffer. Assumes a char is
; already there.
inchr: push bx
cli ;Disable interrupts while were are playing.
dec mnchrn ;Decrement the number of chars in the buffer.
mov bx, mnchop ;Get the pointer into the buffer.
inc bx ;Increment to the next char.
cmp bx, offset mnchrs+mnchnd ;Past the end?
jb inchr2
mov bx, offset mnchrs ;If so wrap around to the start.
inchr2: mov mnchop, bx ;Save the updated pointer.
mov al, [bx] ;Get the character.
sti ;All done, we can restore interrupts.
pop bx
cmp parflg,parnon ;[par] no parity?
je inchr3 ;[par] yup, don't bother stripping
and al,7fh ;[par] checking parity, strip off
inchr3: cmp floctl, floxon ; do flow control? [19a] start
je inchr4 ;If yes jump
ret
inchr4: cmp xofsnt, true ;Have we sent an XOFF
je inchr5 ;Jump if yes
ret
inchr5: cmp mnchrn, mntrg1 ;Under the low trigger point?
jb inchr6 ;yes - jump
ret
inchr6: push ax ;save current character
mov al, xon
call prtout ;send an XON
mov xofsnt, false ;turn off the flag
pop ax ;get back character
ret ; [19a] end
; Output data to port. Trashes DX and prints char in AL.
outchr: mov dx, mndata
out dx, al
ret
; Test if data is available from port.
instat: cmp mnchrn, 0 ;Any chars in the buffer?
jnz inst2
ret
inst2: jmp rskp
; Test if port is ready to send next char. Returns RETSKP if ready.
; Trashes dx.
outwt: cmp floctl, floxon ;are we doing flow-control? [19a] start
jne outwta ;no - go on
cmp xofrcv, true ;are we being held?
jne outwta ;no - ok go on
ret ;held - say we're busy. [19a] end
outwta: push ax
mov dx, mnstat
in al, dx
test al, output
pop ax
jnz outwt2
ret
outwt2: jmp rskp
; Output the character, checking first to make sure the port is clear.
prtout: call dopar ;[par]
push dx
push cx ;[20d] begin
mov cx,outlmt
prtou2: call outwt ;Wait until the port is ready
loop prtou2 ; or too much time has passed.
nop
call outchr ;Output it.
pop cx ;[20d] end
pop dx
ret
mnax dw 0 ;Storage in CSEG ;[28e] begin
mnsp dw 0 ; for use by interrupt handler
mnsseg dw 0
mndseg dw 0
; This routine handles the interrupts on input.
mnint: cli
mov cs:mnax, ax ;Save interrupt stack location.
mov ax, sp
mov cs:mnsp, ax
mov ax, ss
mov cs:mnsseg, ax
mov ax, cs:mndseg ;Switch to our internal stack.
mov ss, ax
mov sp, offset mnstk
push ds ;Save all registers.
push es
push bp
push di
push si
push dx
push cx
push bx
mov ds, ax
call mnproc ;Process the character.
mov dx, mnstat ;Get the status port.
mov al, 38H
out dx, al ;Tell the port we finished with the interrupt.
pop bx ;Restore all registers.
pop cx
pop dx
pop si
pop di
pop bp
pop es
pop ds
mov ax, cs:mnsp ;Restore the original stack.
mov sp, ax
mov ax, cs:mnsseg
mov ss, ax
mov ax, cs:mnax
iret ;Return from the interrupt. ;[28e] end
; This routine (called by MNINT) gets a char from the main port
; and puts it in the infamous circular buffer.
mnproc: mov dx, mnstat
in al, dx ;Get the port status.
test al, input ;Any there?
jnz mnpro2 ;Yup, go take care of it.
;[33] Begin addition
; If not a received character, simulate an interrupt transferring
; control to the CPM routine. Let it handle worrisome things like
; someone turning on the printer.
pushf ; Save flags, like an int.
callf dword ptr mnoldo ; Call CPM's routine.
ret ; Now back to MNINT.
;[33] End addition
mnpro2: mov al, 1 ;Point to RR1.
out dx, al
in al, dx ;Read RR1.
mov ah, al ;Save it.
mov al, 30H ;Reset any errors.
out dx, al
mov dx, mndata
in al, dx ;Read the char.
cmp floctl, floxon ;are we doing flow-control ? [19a] start
jne mnpr2b ;no - go on
cmp al, xoff ;is it an XOFF?
jne mnpr2a ;no - go on
mov xofrcv, true ;set the flag
ret
mnpr2a: cmp al, xon ;an XON?
jne mnpr2b ;no
mov xofrcv, false ;clear the flag
ret ; [19a] end
mnpr2b: cmp mnchrn, mnchnd ;Is the buffer full?
je mnperr ;If so, take care of the error.
inc mnchrn ;Increment the character count.
mov bx, mnchip ;Get the buffer input pointer.
inc bx ;Increment it.
cmp bx, offset mnchrs+mnchnd ;Past the end?
jb mnpro3
mov bx, offset mnchrs ;Yes, point to the start again.
mnpro3: mov mnchip, bx ;Save the pointer.
mov [bx], al ;Put the character in the buffer.
cmp floctl, floxon ;do flow control? [19a] start
je mnpro4 ;If yes jump
ret
mnpro4: cmp xofsnt, true ;Have we sent an XOFF
jnz mnpro5
ret ;return if we have
mnpro5: cmp mnchrn, mntrg2 ;Past the High trigger point?
ja mnpro6 ;yes - jump
ret
mnpro6: mov al, xoff
call prtout ;send an XOFF
mov xofsnt, true ;set the flag
ret ; [19a] End
mnperr: ret ;Just return on an error for now.
; prtbrk - send a break ; [19e] start
prtbrk: ;
mov dx, mnstat ;status reg. address for port
mov al, 15H ;select reg. 5
out dx, al ;
mov al, 0FAH ;8 bits, TX, Break, RTS, & DTR
out dx, al ;Turn Break on
mov ax, 275 ;.. for 275 millisec's [34]
call mswait ; [34]
mov al, 15H ;select reg. 5
out dx, al ;
mov al, 0EAH ;same as above without Break
out dx, al ;turn it off
ret ; [19e] end
mswait: ; [34] start
mov cx,5*clckrt ; inner loop count for 1 millisec.
mswai1:
sub cx,1 ;** inner loop takes 20 clock cycles
jnz mswai1 ;**
dec ax ; outer loop counter
jnz mswait ; wait another millisecond
ret ; [34] end
;
; Init the 7201 for 8 bits, no parity, and 1 stop bit.
serini: call ansmod ;Switch from VT52 to ANSI mode ;[30c]
mov ax, ds
mov cs:mndseg, ax ;Save the data segment somewhere in CSEG.
push ds ;Save the data segment.
mov ax, 0
mov ds, ax ;We want DSEG = 0.
cli ;Turn off interrupts.
mov bx, .mnoff ;[33] Get original interrupt offset.
mov es, .mnseg ;[33] Get original interrupt segment.
mov ax, offset mnint;Point to the interrupt routine offset.
mov .mnoff, ax ;Put in the main port interrupt offset addr.
mov ax, cs ;Get our code segment.
mov .mnseg, ax ;Put in the main port interrupt segment addr.
sti ;Restore interrupts.
pop ds ;Restore data segment.
mov mnoldo, bx ;[33] Stash original serial interrupt offset.
mov mnolds, es ;[33] Stash original segment.
mov dx, mnstat ;Point to status port.
mov al, 18H
out dx, al ;Reset the port.
mov al, 14H
out dx, al ;Select register 4.
mov al, 44H ;16X clock, 1 stop bit, no parity.
out dx, al
mov al, 13H
out dx, al ;Select register 3.
mov al, 0C1H ;8 bits/char, RX enable.
out dx, al
mov al, 15H
out dx, al ;Select register 5.
mov al, 0EAH ;8 bits/char, TX enable, RTS and DTR.
out dx, al
mov al, 11H
out dx, al ;Select register 1.
mov al, 18H
out dx, al ;Enable interrupt processing on this port.
mov dx, mnctrl ;point to comm control port
mov al, 0F0H ;set RTS & DTR high
out dx, al
ret
serfin:
call clrscr ;[19b] clear screen ;[30c]
ret ;Nothing to deinitialize on Rainbow.
; This routine clears the serial port input buffer. It is called to
; clear out excess NAKs that can result from server mode operation.
cfibf: mov mnchrn, 0 ;Say no characters in the buffer.
mov mnchip, offset mnchrs-1+mnchnd ;Reset input pointer.
mov mnchop, offset mnchrs-1+mnchnd ;Reset output pointer.
ret
DSEG $
mnchnd equ 256 ;[19a] Size of circular buffer.
mntrg1 equ 64 ;[19a] Low trigger point for Auto XON/XOFF
mntrg2 equ 192 ;[19a] High trigger point for Auto XON/XOFF
floctl db 1 ;[19a] If floctl=floxon do Auto XON/XOFF logic
xofsnt db 0 ;[19a] set if XOFF was sent
xofrcv db 0 ;[19a] set if XOFF was recieved
mnchrn DW 0 ;[19a] Number of chars in the buffer.
mnchrs RB mnchnd ;Circular character buffer for input.
mnchip DW mnchrs-1+mnchnd ;Input pointer into character buffer.
mnchop DW mnchrs-1+mnchnd ;Output pointer into character buffer.
mnoldo RW 1 ;[33] CPM's 7201 interrupt vector offset
mnolds RW 1 ;[33] and segment.
rw 32 ;Interrupt stack ;[28e]
mnstk dw 0 ;bottom of stack ;[28e]
CSEG $
; The following routines do the SET and SHOW for the machine dependent
; features of Kermit. At present there are only two: baud rate setting
; and port selection.
; This is the SET BAUD rate subcommand (not implemented in Rainbow)
bdset: mov ah, cmcfm
call comnd ;Get a confirm.
jmp $+3 ; Didn't get a confirm.
mov dx, offset infms6 ;Tell user it's not implemented
call tcrmsg
jmp rskp
; This is the SET PORT subcommand (not implemented in Rainbow)
prtset: mov ah, cmcfm
call comnd ;Get a confirm.
jmp $+3 ; Didn't get a confirm.
mov dx, offset infms6 ;Tell user it's not implemented
call tcrmsg
jmp rskp
; The following procedures implement the SHOW command for the system
; dependent features of baud rate and port selection.
shobd: ret ;Baud rate selection not implemented.
shoprt: ret ;Port selection not implemented.
; The following routines do screen control. These are isolated here because
; the screen control sequences are likely to vary from system to system, even
; though the Rainbow and APC (the only systems implemented to date) both use
; ANSI sequences for this purpose.
CSEG $
; POSCUR - positions cursor to row and col (each 1 byte) pointed to by dx.
poscur: mov bx, dx ;Do ANSI cursor positioning.
mov cl, 10
mov al, [bx] ;Get row value
sub ah, ah
div cl ;units digit in ah, tens digit in al
add ax, '00' ;Convert both to ASCII
mov word ptr anspos+2, ax ;Save reversed (al,ah)
mov al, 1[bx] ;Do same for column value
sub ah, ah
div cl
add ax, '00'
mov word ptr anspos+5, ax
mov dx, offset anspos ;Print cursor positioning string.
call tmsg
ret
; CLRSCR - homes cursor and clears screen.
clrscr: mov dx, offset anscls
call tmsg
ret
; CLRLIN - clears from cursor to end of line.
clrlin: mov dl, cr ;Go to beginning of line
call bout
clreol: mov dx, offset ansclr ;Clear from cursor to end of line
call tmsg
ret
; REVON - turns on reverse video display
revon: mov dx, offset ansron
call tmsg
ret
; REVOFF - turns off reverse video display
revoff: mov dx, offset ansrof
call tmsg
ret
; BLDON - turns on bold (highlighted) display
bldon: mov dx, offset ansbon
call tmsg
ret
; BLDOFF - turns off bold (highlighted) display
bldoff: mov dx, offset ansbof
call tmsg
ret
; ANSMOD - enters ANSI mode from VT52 mode
ansmod: mov dx, offset ansion
call tmsg
ret
DSEG $
anspos db esc,'[00;00H$' ;Position cursor to row and column
anscls db esc,'[H',esc,'[J$' ;Home cursor and clear screen
ansclr db esc,'[K$' ;Clear from cursor to end of line
ansron db esc,'[7m$' ;Turn on reverse video
ansrof db esc,'[m$' ;Turn off reverse video
ansbon db esc,'[1m$' ;Turn on bold (highlight) display
ansbof db esc,'[m$' ;Turn off bold display
ansion db esc,'<$' ;Enter ANSI mode
; Here tab expansion is done if necessary. If not, just return retskp.
CSEG $
dotab: jmp rskp
DSEG $
delstr db ' ',10O,10O,'$' ;Delete string.
system db ' DEC Rainbow-100$'
<<< 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 <Oc.Garland%CU20B@Columbia-20>
; * * * * * * * * * * * * * * * version 2.3 * * * * * * * * * * * * * * *
; [par] Added calls to set parity, strip parity on input if
; other than none parity is called for.
; JD, 2/84
; * * * * * * * * * * * * * * * version 2.2 * * * * * * * * * * * * * * *
; [2] Add a de-initialization routine for the serial port, to restore
; changed interrupt vectors, etc.
; RonB,12/23/83
; [1] Add I/O support for the NEC Advanced Personal Computer
; RonB,12/23/83
; * * * * * * * * * * * * * * * version 2.0 * * * * * * * * * * * * * * *
; This module contains all the low level communications port I/O
; routines.
; 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 <Oc.Garland%CU20B@Columbia-20>
; * * * * * * * * * * * * * * * version 2.3 * * * * * * * * * * * * * * *
; [par] Added calls to set parity, strip parity on input if
; other than none parity is called for.
; JD, 2/84
; * * * * * * * * * * * * * * * version 2.2 * * * * * * * * * * * * * * *
; [2] Add a de-initialization routine for the serial port, to restore
; changed interrupt vectors, etc.
; RonB,12/23/83
; [1] Add I/O support for the NEC Advanced Personal Computer
; RonB,12/23/83
; * * * * * * * * * * * * * * * version 2.0 * * * * * * * * * * * * * * *
; This module contains all the low level communications port I/O
; routines.
; The following is the I/O code for the 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]