;---------------------------------------------------------------------------; ; RS-232 Data Switch Firmware ; ; ; ; File: UTIL.ASM ; ; ; ; This file contains utility functions used by other modules in the ; ; switch firmware. ; ;---------------------------------------------------------------------------; util segment code unit rseg util $nolist $include(globals.asm) $list extrn code(handle_event) public daysthismonth public skipspc public bgetchar public matchtoken public getnumquiet public getnum public getline public prtstr public tab public crlf public putchar public toupper public printnum public getpassword public printhex2 public gethex ;--------------------------------------------------------------------------- ; Function: daysthismonth ; ; Description: ; Given a month number in A and the year number currently in the clock, ; returns the number of days in that month. Handles February 29th for ; leap years. ; ; Inputs: A - month number ; year - year number ; ; Outputs: A - number of days ; daysthismonth: cjne a,#2,not29feb jb year.0,not29feb jb year.1,not29feb mov a,#29 sjmp gotdays not29feb: mov dptr,#dayspermonth-1 movc a,@a+dptr gotdays: ret dayspermonth: db 31,28,31,30,31,30,31,31,30,31,30,31 ;--------------------------------------------------------------------------- ; Function: skipspc ; ; Description: ; Advances the command line input buffer pointer until a non-space ; character or the terminating zero is found. Does not advance the ; pointer past the termintating zero even if called more than once. ; Returns the first nonspace character found. ; ; Inputs: None ; ; Outputs: A - first nonspace character ; ; Use P2 and R0 to access the buffer, rather than ; corrupting the DPTR. skipspc: mov p2,#bufpage mov r0,bufptr skiploop: movx a,@r0 jz skipdone cjne a,#' ',skipdone inc r0 mov bufptr,r0 sjmp skiploop skipdone: ret ;--------------------------------------------------------------------------- ; Function: bgetchar ; ; Description: ; Gets one character from the command line input buffer, and advances ; the buffer pointer. Does not advance the pointer if the terminating ; zero was found. ; ; Inputs: None ; ; Outputs: A - character from the buffer ; ; Use P2 and R0 to access the buffer, rather than ; corrupting the DPTR. bgetchar: mov p2,#bufpage mov r0,bufptr movx a,@r0 jz gotzero inc bufptr gotzero: ret ;--------------------------------------------------------------------------- ; Function: matchtoken ; ; Description: ; Given the current position in the command line buffer, which must be ; on a non-space character, matches the current word against a table of ; tokens. Abbreviations are allowed. Can use a token table where each ; entry is "padded" to the same length, to allow scanning of tables in ; which the words are entered by the user (i.e. the port name table). ; ; Inputs: DPTR - pointer to token table ; ; Outputs: A - number of the token which was matched or zero if none. ; ; Initialize P2 (upper 8 bits of external memory address) ; and the token number counter. matchtoken: mov p2,#bufpage mov r1,#1 ; Outer loop: Check the first character of the token in ; the table. If zero, the end of the table has been ; reached. trynext: movx a,@dptr jz nomatch ; Point at the beginning of the word in the buffer. mov r0,bufptr ; Get a character from the buffer. If zero or space, ; we have a successful abbreviation match (it is up to ; the caller to ensure that the buffer pointer does not ; point to a space or zero to begin with; they would ; match anything. matchloop: movx a,@r0 jz gotmatch clr c subb a,#' ' jz gotmatch ; The buffer pointer is a nonblank character. Get it ; again and compare to the character from the token table. movx a,@r0 lcall toupper mov r2,a movx a,@dptr ; Loop until a mismatch occurs or the end of the word on ; the command line is reached. cjne a,2,mismatch inc dptr inc r0 sjmp matchloop ; A mismatch occurred. Skip the rest of the token in the ; token table. mismatch: inc dptr movx a,@dptr jnz mismatch ; Skip any padding (CTRL-A = 1) after the terminating zero ; of the token in the token table. skip_pad: inc dptr movx a,@dptr dec a jz skip_pad ; Increment the token counter and try to match the next token ; in the table (to which DPTR now points). inc r1 sjmp trynext ; A match was obtained. Update the buffer pointer to point ; past the matched word, and return the token number. gotmatch: mov bufptr,r0 mov a,r1 nomatch: ret ;--------------------------------------------------------------------------- ; Functions: getnum, getnumquiet ; ; Description: ; Given the current position in the command line buffer, which must be ; on a non-space character, reads a decimal number from the buffer. The ; number must be terminated by a null or the character given in R2. If ; an error is encountered, "badflag" is set. "getnum" will also print ; an error message. ; ; Inputs: R2 - character (other than 0) which may terminate the number. ; Usually set to a space. ; ; Outputs: R1 - the number which was read. ; badflag - set if an error occurred and R1 is not valid. ; getnumquiet: setb quietflag sjmp getnumcont getnum: clr quietflag ; Continue here for both functions. Initially clear the ; error flag, and set the number accumulator to zero. Then ; get the first digit. getnumcont: clr badflag mov r1,#0 lcall bgetchar ; Digit loop. Convert the current character to a binary ; value, and exit with an error if it is not valid. nextdigit: clr c subb a,#'0' jc badnum cjne a,#10,dig2 dig2: jnc badnum ; Multiply the number accumulator by 10 and add the current ; digit. Exit with an error if the result would exceed 255. xch a,r1 mov b,#10 mul ab jb ov,badnum add a,r1 jc badnum mov r1,a ; Get the next digit, and return if it is either zero or the ; value in R2. This check is done here since there must be ; at least one digit. lcall bgetchar jz numdone cjne a,2,nextdigit numdone: ret ; Error exit. badnum: setb badflag jb quietflag,numdone mov dptr,#badnumstr ljmp prtstr badnumstr: db 'Invalid number entered.',13,10,0 ;--------------------------------------------------------------------------- ; Function: printnum ; ; Description: ; This function prints the number contained in A, in decimal. It right ; justifies the result in a field of given width, using a given pad ; character. ; ; Inputs: A - the number to be printed. ; R1 - pad character. ; R2 - field width. ; ; Outputs: None ; ; Since the digits are generated in reverse order, store ; them on the stack for use later. This is OK, since only ; 3 digits, plus a terminating character, will ever be ; pushed. ; Push a -1 on the stack to terminate the character list. printnum: mov b,#0ffh push b ; Divide A by 10, and push the remainder on the stack. The ; result stays in A, and the operation is repeated until ; nothing is left. Subtract 1 from the field width for ; each digit pushed. printloop: mov b,#10 div ab push b dec r2 jnz printloop ; Check if the field width is still positive. mov a,r2 jz nopad jb acc.7,nopad ; It is, so put out however many bytes of padding required. padloop: mov a,r1 lcall putchar djnz r2,padloop ; Now pop the digits off the stack and print them. The -1 ; will generate a carry when '0' is added to it, and ; terminate the loop. nopad: pop acc add a,#'0' jc printdone lcall putchar sjmp nopad printdone: ret ;--------------------------------------------------------------------------- ; Functions: getpassword, getline ; ; Description: ; "getline" gets a command line from the controlling terminal into the ; line input buffer. "getpassword" is a special case, which does not ; echo characters as they are typed (it echoes spaces instead), and ; only allows the maximum password length. ; ; If an event occurs which indicates that the current user's session ; should be aborted, the routine sets the "byeflag" and returns with ; no line entered. ; ; Inputs: None. ; ; Outputs: byeflag - set if the command session should be aborted. ; getpassword: clr echoflag mov r0,#pw_length sjmp getline2 getline: setb echoflag ; Continue here for both functions. Initialize the buffer ; pointer (P2 and R1), and clear the "byeflag". getline2: mov p2,#bufpage mov r1,#0 clr byeflag ; Character loop. Start the idle timer at 30 seconds. gloop: mov idletimer,#30 ; Wait for one of the following to happen. gloop1: jnb attnflag,gloop2 ; (1) The attention flag became set. Handle the event. lcall handle_event jb byeflag,inputbyebye ; (2) Another user is waiting. gloop2: jnb queueflag,gloop4 mov a,waittimer jnz gloop3 ; (2A) Another user is waiting and our 30 seconds are up. mov dptr,#waitmsg sjmp abortinput gloop3: mov a,idletimer jnz gloop4 ; (2B) Another user is waiting and we have been idle for ; 30 seconds or more. mov dptr,#idlemsg abortinput: lcall prtstr setb byeflag inputbyebye: ret gloop4: jnb scon.0,gloop1 ; (3) The user typed a character. Get it from the serial ; input register. mov a,sbuf clr scon.0 clr acc.7 ; Check for backspace or rubout. cjne a,#8,notbs sjmp delchar notbs: cjne a,#127,notdel ; Delete a character, but only if the buffer is not empty. delchar: mov a,r1 jz gloop lcall do1del sjmp gloop ; Check for carriage return. If it is one, zero-terminate ; the buffer, echo a CRLF, initialize the input buffer ; pointer, and return. notdel: cjne a,#13,notcr clr a movx @r1,a mov bufptr,a ljmp crlf ; Check for CTRL-X. If it is one, delete characters until ; nothing is left of the line. notcr: cjne a,#24,notctlx cloop: mov a,r1 jz gloop lcall do1del sjmp cloop ; That's it for the control characters. If the character ; isn't printable and hasn't been handled by now, get rid ; of it. notctlx: movx @r1,a anl a,#0e0h jz gloop ; Check if the input line is at maximum length already. ; If it is, don't advance the buffer pointer and don't ; echo the character. mov a,r1 clr c subb a,r0 jnc gloop ; Advance the buffer pointer and echo the character if echo ; is enabled. Otherwise, echo a space. movx a,@r1 inc r1 jb echoflag,echoit mov a,#' ' echoit: lcall putchar sjmp gloop ; Delete one character by decrementing the buffer pointer and ; printing . do1del: mov a,#8 lcall putchar mov a,#32 lcall putchar mov a,#8 lcall putchar dec r1 ret ; Messages. waitmsg: db 13,10,'Timeout. Control transferred to next ' db 'user.',13,10,0 idlemsg: db 13,10,'Idle session aborted by pending request.' db 13,10,0 ;--------------------------------------------------------------------------- ; Function: prtstr ; ; Description: ; Prints a zero-terminated string pointed to by DPTR. ; ; Inputs: DPTR - address of string. ; ; Outputs: None. ; prtstr: movx a,@dptr jz prtdone prtwait: lcall putchar inc dptr sjmp prtstr prtdone: ret ;--------------------------------------------------------------------------- ; Function: tab ; ; Description: ; Moves the cursor to the indicated column, if not already past it. ; ; Inputs: A - desired column. ; ; Outputs: None. ; tab: clr c subb a,tabpos jc tab_exit jz tab_exit mov r0,a tabloop: mov a,#' ' lcall putchar djnz r0,tabloop tab_exit: ret ;--------------------------------------------------------------------------- ; Function: crlf ; ; Description: ; Prints and ; ; Inputs: None. ; ; Outputs: None. ; crlf: mov a,#13 lcall putchar mov a,#10 ;--------------------------------------------------------------------------- ; Function: putchar ; ; Description: ; Prints a character to the controlling terminal. Also maintains the ; "tab position" counter required by the "tab" function. ; ; Inputs: A - character to print. ; ; Outputs: None. ; putchar: jnb scon.1,putchar mov sbuf,a clr scon.1 cjne a,#13,notcr2 mov tabpos,#0 ret notcr2: clr c subb a,#' ' jc put_exit inc tabpos put_exit: ret ;--------------------------------------------------------------------------- ; Function: toupper ; ; Description: ; Converts a character to uppercase if it is a lowercase alphabetic, ; otherwise leaves it alone. ; ; Inputs: A - character. ; ; Outputs: A - character converted to uppercase. ; toupper: push acc clr c subb a,#'a' jc notlower subb a,#26 jnc notlower pop acc subb a,#1fh ret notlower: pop acc ret ;--------------------------------------------------------------------------- ; Function: printhex2 ; ; Description: ; Prints the value in A as 2 hexadecimal digits. ; ; Inputs: A - number to print ; ; Outputs: None ; printhex2: mov r0,a swap a acall prtdig mov a,r0 prtdig: anl a,#0fh cjne a,#10,prt2 prt2: jc notalpha add a,#7 notalpha: add a,#'0' ljmp putchar ;--------------------------------------------------------------------------- ; Function: gethex, gethexquiet ; ; Description: ; Evaluates a hexadecimal number of up to 4 digits from the command ; input buffer. If an error is encountered, "gethex" will print a message, ; whereas "gethexquiet" will not. Both will set the "badflag". ; ; Inputs: R2 - character other than zero which may terminate the number. ; ; Outputs: R3 - high 8 bits of number entered. ; R4 - low 8 bits of number entered. ; gethexquiet: setb quietflag sjmp gethexcont gethex: clr quietflag ; Continue here for both functions. Initialize the error ; flag and the number accumulator, then get the first digit. gethexcont: clr badflag mov r3,#0 mov r4,#0 lcall bgetchar ; Convert the character to a binary value, and exit with ; an error if it is not a valid hex digit. nexthexdigit: lcall toupper clr c subb a,#'0' jc badhex cjne a,#10,hexdig2 hexdig2: jc dodigit cjne a,#17,hexdig3 hexdig3: jc badhex clr c subb a,#7 cjne a,#16,hexdig4 hexdig4: jnc badhex ; A now contains the digit in binary form. Save it away and ; shift the number accumulator left by 4 bits. Exit with ; an error if that would overflow it. dodigit: mov r1,a mov a,r3 anl a,#0f0h jnz badhex mov a,r4 anl a,#0f0h orl a,r3 swap a mov r3,a mov a,r4 swap a anl a,#0f0h ; Finish up by adding in the digit saved earlier. orl a,r1 mov r4,a ; Get the next digit and continue unless it is one of the ; termination characters. lcall bgetchar jz hexdone cjne a,2,nexthexdigit hexdone: ret ; Error exit. badhex: mov dptr,#badhexstr lcall prtstr setb badflag ret badhexstr: db 'Invalid hexadecimal number.',13,10,0 end