.Title UCL - User Command Language Package .sbttl . ************************* .sbttl . * Issued by Sandia * .sbttl . * National Laboratories,* .sbttl . * a prime contractor * .sbttl . ******** to the * .sbttl . * United States * .sbttl . * Department * .sbttl . * of * .sbttl . * Energy * .sbttl . ********************* ---NOTICE--- ********************* .sbttl . * This software was prepared as an account of work * .sbttl . * sponsored by the United States Government. Neither * .sbttl . * the United States nor the United States Department of * .sbttl . * Energy, nor any of their employees, * .sbttl . * nor any of their contractors, subcontractors, or their * .sbttl . * employees, makes any warranty, express or implied, or * .sbttl . * assumes any legal liability or responsibility for the * .sbttl . * ********** accuracy, ********** * .sbttl . * * * completeness * * * .sbttl . * * * or usefulness * * * .sbttl . * * * of any * * * .sbttl . * * * information, * * * .sbttl . * * * apparatus, * * * .sbttl . * **** * product * **** * .sbttl . * * * or process * * * .sbttl . * * * disclosed, * * * .sbttl . * * * or represents * * * .sbttl . * * ** that its ** * * .sbttl . * * ** use would not ** * * .sbttl . ********* ** infringe ** ********* .sbttl . ** privately ** .sbttl . ** owned ** .sbttl . ** rights. ** .sbttl . ** ** .sbttl . ** ** .sbttl . ** ** .sbttl . ******************** .sbttl . .sbttl . ******************** .sbttl . ** ** .sbttl . ** UNCLASSIFIED ** .sbttl . ** ** .sbttl . ******************** .page .sbttl Author ; Author: William M. Davidson ; Division 5238 ; Sandia National Laboratories ; P.O. Box 5800 ; Albuquerque, NM 87185 ; (505) 844-7486 ; FTS 844-7486 .page .sbttl Description ; This program takes a command line from KMON and processes it like TSX+. ;It assumes that the first argument is the file name of a command file or ;an executable image. The rest of the arguments are placed in the command ;file in place of the argument indicators, i.e.: ^1, ^2, ^3, etc. If the ;file name is a .sav, image the string is passed back to KMON with a 'RU ' ;placed in front. The program removes ^), ^(, ^<, ^>, and ^! special ;character sequences that are used by TSX+ because RT-11 cannot process ;them. A '^' in front of any letter will create the same character as if ;the 'ctrl' key was depressed with the letter. Finally, ^$ will create the ;'escape' character. ; In addition, words passed with back-slashes around them, i.e., ;\word1 word2 word3 word4\ are treated as one argument. After a comment ;identifier is found '!', the rest of the line is ignored. This allows ;command files to be heavily commented and none of the comments will be passed ;to KMON. Comments are allowed on the same line with executable commands. ;Also, the command is truncated to 6 letters before attempting to find a file ;with that name. If an argument is omitted or specified with the character ;'&', the default argument is used. The default argument is specified either ;at the beginning of the command file or right after the argument indicator. ;Example: if you want all ^1 to have a default, argument use the following at ;the beginning of the command file. ;!^1&This is the default argument for all ^1& ;If you want one ^1 to have a default argument, use the following at the ;location in the command file it is to be used. ; ^1&this is the default argument for this ^1 only& ;The order of precedence is as follows. It uses the argument on the command ;line if there is one, otherwise it uses the global default value if there ;is one, otherwise it uses the single default value if there is one. If there ;are no arguments, the argument identifier is removed. ;Standard tty input buffer length. .iif ndf ttyin,ttyin=134. ;Max size of input line ;If a SYSGEN is done changing the length of the buffer, assemble UCL ;with the SYSGEN conditional file. .page .sbttl Memory allocations .psect code,i .psect data,d .dsable gbl .nlist bex,cnd ;Constants maxarg =-1 ;max number of arguments buflen =512. ;length of read buffer top =50 ;location of top address of program name =<10.*3>+2 ;offset to first input file name after .csispc cmdlen =510 ;place to put length of command passed to KMON cmd =512 ;start of KMON command area cmdend =777 ;last byte for KMON command maxcmd =cmdend-cmd+1 ;maximum length of command for KMON chain =4040 ;chain bits for jsw jsw =44 ;job status word dollar =44 ;ascii '$' cr =15 ;carriage return lf =12 ;line feed esc =33 ;escape character emterr =52 ;emt error code location userrb =53 ;user error code location space =40 ;space character tab =11 ;tab character bslash =134 ;back slash character '\' defchr ='& ;default value indicator $F ='F ;to indicate fatal error $W ='W ;to indicate warning condition wild =132500 ;wild flag indicator .page .asect .word debug ;easy way to find address of 'debug' using sipp .psect data ;General purpose EMT area area: .blkw 6. ;Default CSI file extensions defext: .rad50 'COM' ;default extension for input files .blkw 3 ;no defaults for the others ;File block dev: .blkw 3 ;4 word file specs for input file ext: .blkw 1 .sbttl Buffers ;All buffers are dynamically allocated, start and end addresses are saved here cmdlin: .word 0 ;start address of command line cmlend: .word 0 ;end address of command line rdlin: .word 0 ;start address of read line rdend: .word 0 ;end address of read line rdbuf: .word 0 ;start address of read buffer rdbend: .word 0 ;end address of read buffer klin: .word 0 ;start address of KMON line kend: .word 0 ;end address of KMON line kbuff: .word 0 ;start address of KMON buffer kbend: .word 0 ;end address of KMON buffer .sbttl Variables readpt: .word 0 ;read buffer pointer kptr: .word 0 ;KMON buffer pointer rdcnt: .word 0 ;counts the number of blocks read table: .word 0 ;table of starting address of arguments char: .word 0 ;first character of next argument expdev: .word 0 ;indicates explicit device given expext: .word 0 ;indicates explicit extension given ptr: .word 0 ;place to put psuedo arg. string pointer debug: .word 0 ;turns on debug .page .sbttl Tables ;Special characters that follow a '^' spchr: .asciz '()<>!' radtab: .word <50*50>,50,1 radasc: .ascii ' ABCDEFGHIJKLMNOPQRSTUVWXYZ$.*0123456789' .sbttl KMON commands run: .ascii 'RU '<200> .sbttl Messages prompt: .ascii '*'<200> vers: .asciz 'WMDUCL 84f02a' prefix: .ascii '?UCL-' sev: .ascii 'X-'<200> err1: .asciz 'Invalid command' err2: .ascii 'Invalid device ' $dev: .asciz 'XXX:' err3: .ascii 'File not found ' $file: .asciz 'XXX:XXXXXX.XXX' err4: .asciz 'Read error' err5: .asciz 'Not enough memory' err6: .asciz 'Command(s) too long for KMON buffer' err7: .asciz 'Too many arguments' err8: .asciz 'Default argument number too large' err9: .asciz 'Argument called itself' .even end:: .page .sbttl Macros ;System macros .mcall .sreset ;software reset .mcall .csispc ;convert ascii to rad50 .mcall .settop ;change top address of program .mcall .print ;print message to terminal .mcall .exit ;exit to monitor .mcall .dstat ;get device status .mcall .readw ;read from device .mcall .lookup ;open an existing file .mcall .fetch ;load device handler into memory .mcall .purge ;throw away an open channel .mcall .gtlin ;get string from terminal .mcall .serr ;intercept ?mon-f errors .mcall .herr ;let mon handle ?mon-f errors ;Error macro .macro error num,sev jsr r5,error ;error routine .word $'sev ;pass error severity and .word err'num ; error number .endm error ;Recursive call macro .macro exline arg1 mov r1 ,-(sp) ;save current r1 mov (arg1) ,r1 ;load new pointer mov arg1 ,-(sp) ;save table pointer neg (arg1) ;mark in use call exline ;call routine mov (sp)+ ,arg1 ;get table pointer neg (arg1) ;indicate not in use mov (sp)+ ,r1 ;get value off stack .endm exline .page .sbttl Main control ;This routine controls the whole program. It basically does five operations. ;It first initializes the buffers and then opens the command file to be read. ;Then it fills an argument table so each argument on the command line can be ;quickly located. ;Fourth, it reads in the command file and substitutes the argument identifiers ;with arguments. Finally, the expanded command file is transferred to the ;KMON buffer area and KMON is told there are commands to be executed with the ;'.exit' system call. .psect code .enable lsb start:: .sreset ;doesn't hurt anything call init ;initialize the process bcs 50$ ;branch if error call opnfil ;find command file or .SAV file bcs 50$ ;branch if error cmp #^rSAV ,ext ;.SAV file? bne 20$ ;no 10$: tstb -(r5) ;yes, find end of first argument bne 10$ ;keep looking movb #space ,(r5) ;found, restore null byte with a space mov #run ,r1 ;get 'RU' command call fillbf ;fill buffer mov cmdlin ,r1 ;get command line call fillbf ;fill buffer br 30$ ;nothing else to do 20$: call ldtab ;parse command line and load arg table bcs 50$ ;branch if error call expand ;expand the command file bcs 50$ ;branch if error 30$: mov kbuff ,sp ;move stack pointer mov kbuff ,r0 ;KMON buffer area mov kptr ,r1 ;end of KMON buffer area sub r0 ,r1 ;length of command(s) to be passed mov #cmdlen ,r2 ;start of KMON command area mov r1 ,(r2)+ ;save byte count 40$: movb (r0)+ ,(r2)+ ;transfer a byte dec r1 ;bump counter bne 40$ ;go until zero bis #chain ,@#jsw ;set chain bit in job status word clr r0 ;make sure r0 is zero 50$: .exit ;return to monitor .dsable lsb .page .sbttl Initialization ;This routine initializes everything that needs initializing. It first ;allocates all the dynamic buffers in 'initbf'. Then it determines if ;a command line was passed through the KMON command area. If there is ;no command line, a prompt is printed and it waits for a command line to ;be entered. The command line is then copied to its buffer. The first ;word of the command line is identified and used as a filename to locate ;a command file on sy: or dk: or a .sav image on dk:. Finally, if a command ;file was located, the file is opened for reading. ;On exit, R5 contains the pointer to the first argument in the command line. ;If error, carry set on return .enable lsb init: call initbf ;initialize buffers bcs 60$ ;error tst @#cmdlen ;anything in KMON line? bne 20$ ;yes 10$: .gtlin cmdlin,#prompt ;no, input line thru terminal tstb @cmdlin ;anything entered? bne 50$ ;yes, go process it .print #vers ;no, just print version br 10$ ;and try again 20$: mov #cmd ,r0 ;start of KMON line mov cmdlin ,r1 ;start of command line buffer 30$: movb (r0) ,(r1)+ ;move command line to local buffer cmp r1 ,cmlend ;buffer full? bhis 40$ ;yes, forget the rest tstb (r0)+ ;at end? bne 30$ ;no, go again 40$: clrb -(r1) ;mark end of command 50$: mov cmdlin ,r5 ;start of command line call parse ;mark end of first arg, r5 -> next arg call filchk ;check for legal file name bcs 60$ ;error call chkdev ;check for valid device 60$: return .dsable lsb .page .sbttl Parse command line and load argument table ;This routine looks for words on the command line that are delimited by spaces, ;tabs, or '\' by calling 'parse'. The address that each word starts is saved ;in a table. If a default identifier is found, the space for that argument is ;left blank. The end of each word is identified by a null byte. ;On entry, R5 must contain the pointer to the first argument in the command line ;If error, carry set on return .enable lsb ldtab:: mov table ,r1 ;start of argument table clr r2 ;argument counter 10$: tstb (r5) ;end of command line? beq 40$ ;yes inc r2 ;no, bump argument counter cmp r2 ,#maxarg ;exceeded argument limit? blos 20$ ;no error 7,F ;yes, ?Too many arguments br 40$ 20$: cmpb #defchr ,(r5) ;default indicator? beq 30$ ;yes, skip to next arg mov r5 ,(r1) ;no, save in table 30$: tst (r1)+ ;bump pointer call parse ;find next argument br 10$ ;go again 40$: return .dsabl lsb .page .sbttl Expand command file ;This routine reads the command file and substitutes arguments in the command ;line for argument identifiers in the command file. It first reads one line ;from the command file. It then checks if the line is a comment line or a ;default argument identification line. If it is a comment line, the entire ;line is ignored. If it is a default argument identification line, the ;default value is inserted into the table with 'define'. Finally, if none ;of the above tests are true, the line is expanded with 'exline' and the new ;line is put in the command buffer. This loop continues until the end of ;the command file is encountered. .enable lsb expand::call readln ;read one line -> rdlin bcs 20$ ;error beq 20$ ;eof mov rdlin ,r1 ;read line pointer mov klin ,r2 ;KMON line pointer cmpb #'! ,(r1) ;first character comment identifier? bne 10$ ;no inc r1 ;go to next character cmpb #'^ ,(r1) ;argument identifier? bne expand ;no call define ;yes, get default definition br expand 10$: call exline ;expand line bcs 20$ ;error mov klin ,r1 ;get KMON line pointer call fillbf ;write to KMON buffer bcc expand ;go again if no error 20$: return .dsable lsb .page .sbttl Define default argument ;This routine sets up the default value for an argument if the value has ;not been determined with the command line. It first determines the ;argument number and checks if it is already defined. If it is not defined, ;the length of the default value is determined and a new buffer is allocated ;for the default value putting the start address of the buffer into the ;argument table. The default value is then copied to the new buffer. ;On entry, R1 must be the pointer to the default argument identification ;line. ;If error, carry set on return .enable lsb define::call deciml ;convert number to binary bcs 30$ ;error cmp r0 ,#maxarg ;too large? ble 10$ ;no error 8,W ;yes, ?default arg number too large br 30$ ;get out 10$: tst r0 ;zero or negative? ble 30$ ;yes, don't process mov r0 ,r5 ;make a copy dec r5 ;calculate asl r5 ; offset add table ,r5 ;pointer into argument table tst (r5) ;anything there? bne 30$ ;yes, don't use default mov r1 ,r4 ;start of default string inc r4 ;go past ^ call skpdef ;find end of string and mark it sub r4 ,r1 ;length of string beq 30$ ;nothing there mov r1 ,r0 ;see if there call setop ; is room for string bcs 30$ ;no mov r0 ,(r5) ;save start of string 20$: movb (r4) ,(r0)+ ;copy byte inlin -> string tstb (r4)+ ;end of string? bne 20$ ;no 30$: return .page .sbttl Expand line from command file ;This routine expands one line from the command file substituting argument ;identifiers in the command file with arguments supplied with the command line, ;default identification line, or the argument identifiers. It first looks ;for a comment identifier. If one is found, the output pointer is backed up ;removing all space and tab characters until a non-space or non-tab character ;is found, at which time the end of the output string is marked. If a '^' ;character is found, the 'deciml' routine is called which determines if the ;sequence of characters make up an argument identifier. If it is not an ;argument identifier, the character following the '^' is converted to a ;control character. If the argument number is larger than the maximum number ;of arguments, the sequence of characters is ignored. If it is a valid ;argument number, the address into the table is calculated. If there is an ;argument in the table, the routine is called recursively passing the start ;address of the argument. A similar procedure is performed for default values ;that are given after the argument identifier. This procedure is performed ;until the output line is full or the end of the input line is reached. ;On entry, R1 must point to the input line and R2 must point to the output ;line. ;If error, an .exit is performed so the recursion doesn't have to be unwound .enable lsb exline::cmpb #'! ,(r1) ;comment character? bne 10$ ;no call backup ;yes, back up to end of command br 90$ ;skip rest of line 10$: cmpb #'^ ,(r1) ;argument indicator? bne 30$ ;no call deciml ;yes, convert number to binary bcc 40$ ;no error cmpb #dollar ,(r1) ;^$ sequence? bne 20$ ;no movb #esc ,(r1) ;yes, make into 'esc' character br 30$ ;go put in wrtlin 20$: call letter ;determine if a letter bcs 30$ ;not a letter bicb #340 ,(r1) ;make into control character 30$: movb (r1)+ ,(r2)+ ;move one byte rdlin -> wrtlin br 70$ ;eol check 40$: cmp r0 ,#maxarg ;arg number within range? bhi 80$ ;no dec r0 ;reduce arg number by 1 asl r0 ;make into a pointer add table ,r0 ;add table base tst (r0) ;pointer larger than zero? bgt 60$ ;yes beq 50$ ;no, zero error 9,F ;negative, ?Argument called itself .exit ;leave now 50$: cmpb #defchr ,(r1) ;arg defined on line? bne 80$ ;no, forget there was an arg id mov #ptr ,r0 ;psuedo pointer mov r1 ,(r0) ;put start of default arg inc (r0) ;skip past 'defchr' 60$: call skpdef ;move pointer past default, if any exline r0 ;call this routine recursively 70$: cmp r2 ,kend ;KMON line full? bhis 90$ ;yes 80$: tstb (r1) ;end of input line? bne exline ;no 90$: clrb (r2) ;mark end of line return .dsable lsb .page .sbttl Back up to end of command of current line ;This routine backs up the pointer (r2) to the first occurence of a non- ;space or non-tab character and then bumps the pointer to where the end of ;line indicator should go. ;On entry, R2 must point to the current end of the output line ;On exit, R2 will point to new end of line. EOL must be inserted at this point. .enable lsb backup::cmpb #space ,-(r2) ;is previous character a space? beq backup ;yes cmpb #tab ,(r2) ;is previous character a tab? beq backup ;yes inc r2 ;bump by one return .dsable lsb .page .sbttl Determine if character is a letter ;This routine determines if the character that the pointer is currently on ;is a letter, either upper or lower case. If it is a letter, the carry ;bit is cleared on return. If the character is anything else, the carry bit ;is set on return and the pointer points to the byte before the one tested. ;On entry, R1 must point to the character to be tested .enable lsb letter::cmpb (r1) ,#'A ;larger than capatal A? blo 10$ ;no, not a letter cmpb #'Z ,(r1) ;larger than capatal Z? bhis 20$ ;no, a letter cmpb (r1) ,#'a ;larger than small A? blo 10$ ;no, not a letter cmpb #'z ,(r1) ;larger than small Z? bhis 20$ ;no, a letter 10$: dec r1 ;back up pointer one sec ;set carry 20$: return .dsable lsb .page .sbttl Move input pointer past default values, if any ;This routine moves the pointer (r1) past the default value of an argument ;that was placed immediately following an argument identifier. It first ;determines whether or not there is a default value. If there is no default ;value, the routine returns with r1 untouched. Otherwise, r1 is moved forward ;until a matching default character is found or the end of line is detected. ;If another default character is found, the end of default value substring is ;marked with a null byte. ;On entry, R1 must contain the pointer to the current position of the input ;string. ;On exit, R1 will contain the pointer to the remainer of the input string. .enable lsb skpdef::cmpb #defchr ,(r1) ;default character? bne 30$ ;no, leave pointer where is 10$: inc r1 ;bump pointer cmpb #defchr ,(r1) ;look for second one beq 20$ ;found tstb (r1) ;end of line? beq 30$ ;yes br 10$ ;no 20$: clrb (r1)+ ;mark end of sub-string 30$: return .page .sbttl Initialize buffers ;This routine initializes all of the dynamically allocated buffers. ;For each buffer, it stores the first address of the buffer and the first ;address of the buffer that follows so it can be determined when a ;pointer has exceeded its buffer. It also clears all values in the ;default table. .enable lsb initbf::mov #<3*ttyin>,r0 ;size of line buffers call setop ;allow room for it bcs 20$ ;no room mov r0 ,cmdlin ;save start of command buffer add #ttyin ,r0 ;end of comm buff/start of rdlin buff mov r0 ,cmlend ;end of command buffer mov r0 ,rdlin ;start of rdlin buffer add #ttyin ,r0 ;end of rdlin buff/start of klin buff mov r0 ,rdend ;end of read line buffer mov r0 ,klin ;start of klin buffer add #ttyin ,r0 ;end of klin buffer mov r0 ,kend ;save it mov #buflen ,r0 ;size of read buffer call setop ;allow room for it bcs 20$ ;no room mov r0 ,rdbuf ;save start of read buffer add #buflen ,r0 ;calculate end of read buffer mov r0 ,rdbend ;save it mov #maxcmd ,r0 ;size of KMON buffer call setop ;allow room for it bcs 20$ ;no room mov r0 ,kbuff ;save it add #maxcmd ,r0 ;calculate end of KMON buffer mov r0 ,kbend ;save it mov #<2*maxarg>,r0 ;size of argument table call setop ;allow room for it bcs 20$ ;no room mov r0 ,table ;save start of table mov r0 ,r1 ;make a copy add #<2*maxarg>,r1 ;stopping point 10$: clr (r0)+ ;clear table cmp r0 ,r1 ;done? blo 10$ ;no 20$: return .dsable lsb .page .sbttl Parse command line ;This routine determines the end of the current word that is in the string ;pointed to by r5 and the beginning of the next word. Words are delimited ;by spaces, tabs, or '\'. If the end of line is reached, r5 will point to ;a null byte on exit. The end of the current word is marked with a null ;byte ;On entry, R5 must contain the pointer to a word in the string ;On exit, R5 will contain the pointer to the word following the word pointed ;to on entry. .enabl lsb parse:: cmpb #bslash ,char ;did argument start with '\'? bne 20$ ;no 10$: cmpb #bslash ,(r5) ;yes, find matching one beq 30$ ;found tstb (r5)+ ;go to next character bne 10$ ;no at end of line dec r5 ;keep eol marker br 70$ 20$: cmpb #space ,(r5) ;find a space beq 30$ cmpb #tab ,(r5) ; or tab character beq 30$ tstb (r5)+ ;go to next character bne 20$ ;not at end of line dec r5 ;end of line, keep marker br 70$ 30$: clrb (r5)+ ;mark end of argument 40$: tstb (r5) ;end of line? beq 70$ ;yes cmpb #space ,(r5) ;no, now look for a non-space beq 50$ ; or cmpb #tab ,(r5) ; non-tab character bne 60$ 50$: inc r5 ;continue until br 40$ ;found 60$: movb (r5) ,char ;save first character in char cmpb #bslash ,(r5) ;next argument start with '\'? bne 70$ ;no inc r5 ;point to next character 70$: return .dsable lsb .page .sbttl Set new top of program ;This routine changes the top of the program. It attempts to increase the ;size of the program by the number of bytes passed in r0. ;On entry, R0 must contain the size(bytes) of the new buffer. ;On exit, R0 will contain the first address of the new buffer ;If error, carry set on return .enable lsb setop:: mov r1 ,-(sp) ;save r1 inc r0 ;make requested size bic #1 ,r0 ; at a word boundary mov r0 ,r1 ;get requested size mov @#top ,r0 ;get current top add r0 ,r1 ;present top + additional room tst (r0)+ ;calculate start of buffer mov r0 ,-(sp) ;save on stack .settop r1 ;see if there is room cmp r0 ,r1 ;did we get the space? beq 10$ ;yes error 5,F ;no, ?Not enough memory 10$: mov (sp)+ ,r0 ;first address of buffer mov (sp)+ ,r1 ;restore r1 return .dsabl lsb .page .sbttl Error routine ;This routine handles all errors. If the error is determined to be fatal ;or a warning, the appropriate status bits in the user error byte are set. ;It then prints out a prefix message and the actual error message. The ;error severity and the address of the error message are passed with the ;help of r5. ;Calling sequence must be: jsr r5,error ;On entry, R5 must point to the error severity followed by the address ;of the error message ;On exit, the carry bit is set .enable lsb error:: movb (r5) ,sev ;set severity cmpb #$F ,(r5) ;fatal error? bne 10$ ;no bisb #20 ,@#userrb ;yes, set fatal bit 10$: cmpb #$W ,(r5) ;warning? bne 20$ ;no bisb #2 ,(r5) ;yes, set warning bit 20$: .print #prefix ;print prefix tst (r5)+ ;bump pointer .print (r5)+ ;print message sec ;set carry rts r5 .dsabl lsb .page .sbttl Check for valid file name ;This routine determines if the first word in the command line is a valid ;file name. If the first word is longer than 6 characters it first truncates ;the first word to 6 letters. If an explicit device or file-extension is ;found in the first word, the appropriate flag is set. The first word is then ;passed to the csi to convert to RAD50. If the word is a valid file name, ;the file name in RAD50 is saved for the 'opnfil' routine. ;On entry and exit, R5 contains the pointer to the unprocessed portion of the ;command line. It must not be changed. ;If error, carry set on return .enable lsb filchk::mov cmdlin ,r0 ;command line pointer 10$: mov #7 ,r1 ;character counter 20$: tstb (r0) ;end of command? beq 50$ ;yes cmpb #': ,(r0) ;device delimiter? bne 30$ ;no inc r0 ;yes, bump pointer mov sp ,expdev ;set explicit device flag br 10$ ;go reset character counter 30$: cmpb #'. ,(r0)+ ;extension delimiter? bne 40$ ;no mov sp ,expext ;yes, set explicit extension flag br 50$ ;use file string as-is 40$: dec r1 ;bump counter bne 20$ ;go again until zero clrb -(r0) ;mark end of file name 50$: mov sp ,r4 ;save stack pointer .csispc rdbuf,#defext,cmdlin ;convert first argument to rad50 bcs 60$ ;error mov rdbuf ,r0 ;rad50 pointer tst (r0) ;any output specs given? bne 60$ ;yes, error tst name(r0) ;any input specs given? beq 60$ ;no, error tst name+6(r0) ;more than one input file? bne 60$ ;yes, error tst (sp)+ ;any options given? beq 70$ ;no 60$: error 1,F ;?Invalid command mov r4 ,sp ;restore stack br 80$ ;and leave 70$: add #name-2 ,r0 ;r0 -> 4 word file specs mov #dev ,r1 ;file block mov (r0)+ ,(r1)+ ;save device name mov (r0)+ ,(r1)+ ;save file mov (r0)+ ,(r1)+ ; name mov (r0) ,(r1) ;save extension 80$: return .dsabl lsb .page .sbttl Check for valid device ;This routine determines if the DK: device or the specified device is ;a valid device from which to read a command file. If it is a valid device, ;the handler is fetched if it isn't already loaded. ;On entry and exit, R5 contains the pointer to the unprocessed portion of the ;command line. It must not be changed. ;If error, carry set on return .enable lsb chkdev::.dstat #area,#dev ;device in tables? bcs 10$ ;no bit #100000 ,area ;random access device? beq 10$ ;no tst area+4 ;handler loaded? bne 20$ ;yes mov area+2 ,r0 ;no, size of handler call setop ;room? bcs 20$ ;no mov r0 ,r1 ;yes, put size in r1 .fetch r1,#dev ;load handler bcc 20$ ;no error 10$: mov #dev ,r1 ;rad50 device name pointer mov #$dev ,r0 ;device name error string call radcvt ;convert rad50 -> ascii movb #': ,(r0)+ ;mark end of device clrb (r0) ;mark end of string error 2,F ;?Invalid device XXX: 20$: return .dsabl lsb .page .sbttl Open necessary files ;This routine opens command files for reading or determines the existence of ;.SAV files on DK:. It first looks for the file using DK: and .COM as defaults. ;If it can't be found, it looks for the file on SY: with the same extension. ;Finally, it tries to find the file on DK: with .SAV extension. If a device or ;extension is given on the command line, this routine will try to locate the ;file without changing any given information. ;On entry and exit, R5 contains the pointer to the unprocessed portion of the ;command line. It must not be changed. ;If error, carry set on return .enable lsb opnfil::.serr ;intercept ?mon-f errors .lookup #area,#0,#dev ;try file name as is bcc 60$ ;found .purge #0 ;not found, release channel movb @#emterr,r1 ;save error status tst expdev ;explicit device? beq 20$ ;no tstb r1 ;?mon error? bmi 10$ ;yes, give error tst expext ;explicit extension? bne 50$ ;yes, give error mov #^rSAV ,ext ;no, try .sav .lookup #area ;can it be found? bcc 60$ ;yes 10$: mov #wild ,ext ;no, put wild flag in extension br 50$ ;?file not found xxx:xxxxxx.* 20$: mov #^rSY ,dev ;try SY: .lookup #area ;can it be found? bcc 60$ ;yes .purge #0 ;release channel tst expext ;explicit extension? beq 30$ ;no mov #wild ,dev ;yes, put wild flag in device br 50$ ;?file not found *:xxxxxx.xxx 30$: tst r1 ;?mon error? bmi 40$ ;yes, don't try dk:xxxxxx.sav mov #^rdk ,dev ;try dk: mov #^rsav ,ext ; .sav .lookup #area ;can it be found? bcc 60$ ;yes 40$: mov #wild ,dev ;put wild-flag in dev mov #wild ,ext ; and extension 50$: call filcvt ;convert file name to ascii .herr ;let monitor handle ?mon-f errors error 3,F ;?File not found br 70$ ;keep carry set 60$: .herr ;let monitor handle ?mon-f errors 70$: return .dsabl lsb .page .sbttl Convert rad50 filename to ascii string ;This routine converts the four word RAD50 filespec starting at 'dev' to ;an ascii string with the proper device and extension delimiters. .enable lsb filcvt: mov #dev ,r1 ;rad50 pointer mov #$file ,r0 ;ascii pointer call radcvt ;convert rad50 to ascii movb #': ,(r0)+ ;end of device call radcvt ;rad50 filename to call radcvt ; ascii movb #'. ,(r0)+ ;end of file name call radcvt ;rad50 ext to ascii clrb (r0) ;mark end of string return .dsable lsb .page .sbttl Read a line from input file ;This routine returns one line of text from the input file. It firsts reads ;512. words from the file and transfers a line of text to the 'rdlin' buffer. ;Lines of text are delimited by line feeds with carriage returns being ;ignored. On successive calls, data is transferred to the 'rdlin' buffer and ;reads to the file occur only when the end of the 512. byte buffer is reached. ;If a line of text is longer than the 'rdlin' buffer, the remainder of the ;line is truncated. The end of the 'rdlin' buffer is indicated by a null ;byte. ;If error, carry set on return .enable lsb readln::mov rdlin ,r2 ;read line pointer mov readpt ,r1 ;read buffer pointer beq 20$ ;read a block if zero 10$: cmp r1 ,rdbend ;time to read again? blo 40$ ;no 20$: .readw #area,#0,rdbuf,#256.,rdcnt ;read a block bcc 30$ ;no error tstb @#emterr ;eof? beq 80$ ;branch if eof error 4,F ;no, ?Read error br 80$ 30$: inc rdcnt ;bump read block counter mov rdbuf ,r1 ;reset read buffer pointer 40$: tstb (r1) ;end of text file? beq 80$ ;branch if eof cmpb #cr ,(r1) ;carriage return? bne 50$ ;no inc r1 ;yes br 10$ ;skip it 50$: cmpb #lf ,(r1) ;line feed? beq 70$ ;yes movb (r1)+ ,(r2)+ ;transfer a byte cmp r2 ,rdend ;end of rdlin buffer? blo 10$ ;no, go until a lf is reached 60$: cmpb #lf ,(r1)+ ;yes, find the lf bne 60$ ;not yet 70$: clrb (r2) ;mark end of line inc r1 ;go to next character mov r1 ,readpt ;save read buffer pointer 80$: return .dsabl lsb .page .sbttl Convert ASCII decimal to binary ;This routine converts the one or two decimal ascii characters pointed to by ;r1 to binary. This routine assumes the numerals occur after a '^' character ;so r1 could be in the middle of a special TSX command file sequence. If r1 ;points to a character that is in the 'spchr' table, the entire sequence is ;ignored by updating r1 past the sequence and returning a very large argument ;number. Finally, the binary value of the decimal characters is returned ;in r0. ;On entry, R1 must point to the character following a '^' character ;On exit, R0 contains the binary value of the decimal characters and ;R1 will point to the character immediately following the numerals ;or special sequence or remains untouched if an error occurs. ;If at least the first character is not a numeral, carry bit set on return .enabl lsb deciml::inc r1 ;go to next character after '^' mov #spchr ,r0 ;special character pointer 10$: cmpb (r0)+ ,(r1) ;is the character in the table? beq 20$ ;yes tstb (r0) ;end of table? bne 10$ ;no, keep looking br 30$ ;yes, not in table 20$: inc r1 ;go to next character mov #-1 ,r0 ;very large argument number br 80$ ; and leave 30$: mov #2 ,r3 ;max. number of numeral chr allowed 40$: cmpb (r1) ,#'0 ;is character a number? blo 50$ ;no cmpb (r1) ,#'9 ;try other end bhi 50$ ;no inc r1 ;next character dec r3 ;update numeral counter bne 40$ ;see if there is another 50$: cmp r3 ,#1 ;at least one numeral? blos 60$ ;yes sec ;no, set carry br 80$ ; and leave 60$: clr r0 ;clear accumulator tst r3 ;one or two numerals? bne 70$ ;one movb -2(r1) ,r3 ;two, get ms digit bic #177760 ,r3 ;make into binary beq 70$ ;branch if zero mov r3 ,r0 ;make a copy asl r0 ;mult. asl r0 ; by four add r3 ,r0 ; then by five asl r0 ;finally by ten 70$: movb -1(r1) ,r3 ;get ls digit bic #177760 ,r3 ;make into binary add r3 ,r0 ;add to ms 80$: return .dsabl lsb .page .sbttl Fill command buffer for exit ;This routine copies the string that is pointed to by r1 to the KMON ;command buffer. The string must be terminated by a null byte. If the ;end of the KMON command buffer is ever reached, the process is stopped ;and an error condition is indicated. If 'debug' is turned on (non-zero), ;the string is also printed to the terminal. ;On entry, R1 must point to the string to be transferred to the KMON buffer ;If error, carry set on return .enable lsb fillbf::tst debug ;debug turned on? beq 10$ ;no .print r1 ;yes, print input string 10$: mov kptr ,r2 ;get KMON buffer pointer bne 20$ ;if zero mov kbuff ,r2 ; set up pointer 20$: movb (r1)+ ,(r2)+ ;move one byte beq 40$ ;branch if end of line bmi 30$ ;eol, remove eol indicator cmp r2 ,kbend ;end of KMON buffer? blo 20$ ;no error 6,F ;yes, ?Command too long for KMON br 50$ ;return 30$: dec r2 ;back up one 40$: mov r2 ,kptr ;save KMON buffer pointer clc ;make sure carry is clear 50$: return .dsable lsb .page .sbttl rad50 to ascii convert ;This routine converts a rad50 word pointed to by r1 to three ascii characters ;and puts the ascii characters into a string pointed to by r0. ;On entry, R1 must point to the RAD50 word to be converted and R0 must point ;to a 3 byte string to place the ascii characters. ;On exit, R1 is incremented by two and R0 is incremented by three .enable lsb radcvt::mov #radtab ,r3 ;rad50 power table mov (r1)+ ,r2 ;get rad50 number 10$: clr r4 ;clear counter 20$: sub (r3) ,r2 ;reduce by rad50 power bcs 30$ ;too much? inc r4 ;no, bump counter br 20$ ;go again 30$: add (r3) ,r2 ;restore number tst r4 ;counter zero? beq 40$ ;yes, skip it movb radasc(r4),(r0)+ ;output character 40$: cmp #1 ,(r3)+ ;last power? bne 10$ ;no, go again return .dsabl lsb .end start