;:ts=8 ; Internal memory allocation ; 00-07: Register bank 0 -- used for non-interrupt code ; 08-0F: Register bank 1 -- used for interrupt code ; 10-17: Register bank 2 \ ; 18-1F: Register bank 3 > misc. variables go here ; 20-2F: Bit addressable space / ; 30-7F: Generic memory -- used for stack .equ ireg0, 8 .org 0x10 year: .skip 1 month: .skip 1 day: .skip 1 hours: .skip 1 minutes: .skip 1 seconds: .skip 1 year_copy: .skip 1 month_copy: .skip 1 day_copy: .skip 1 hours_copy: .skip 1 minutes_copy: .skip 1 seconds_copy: .skip 1 tickcnt1: .skip 1 tickcnt2: .skip 1 keycode: .skip 1 keyout: .skip 1 .flag cdflag,0x20.0 .flag secflag,0x20.1 .flag ovflag,0x20.2 .flag ioflag,0x20.3 ; 0: keypad/LCD 1: host .flag quietflag,0x20.4 .flag badflag,0x20.5 .flag menuflag,0x20.6 .flag xoffflag,0x20.7 .flag eventiosave,0x21.0 .flag showtime,0x21.1 .flag inring,0x21.2 .flag validpause,0x21.3 .flag rflag,0x21.4 .flag lflag,0x21.5 .flag ringflag,0x21.6 .flag toneflag,0x21.7 .flag seentone,0x22.0 .flag hungupflag,0x22.1 .flag flashflag,0x22.2 .flag flashphase,0x22.3 .org 0x28 keytimer: .skip 1 tabpos: .skip 1 bufptr: .skip 1 ticktimer1: .skip 1 ; External memory allocation .equ bufsize,127 .equ bufpage,0x22 .org 0x100*bufpage inbuf: .skip bufsize+1 c4save: .skip 1 silenttimer: .skip 2 ringtimer: .skip 1 ringcount: .skip 1 pickupcount: .skip 1 listentimer: .skip 1 lasttone: .skip 1 tonetimer: .skip 1 pickuptone: .skip 1 flashnum: .skip 1 flashtimer: .skip 1 .equ pwlength,10 password: .skip pwlength+1 weekday: .skip 1 c8save: .skip 1 .org 0x2300 ljmp initlcd ljmp lputs ljmp wrtlcd ljmp lputchar kgetchar: .skip 3 ljmp daysthismonth ljmp getdate ljmp setdate ljmp putchar ljmp puts ljmp printnum ljmp printdate ljmp prttime ljmp lcd_input ljmp lcd_parsenum ljmp inputdate ljmp crlf ljmp tab ljmp sputs ljmp skipspc ljmp bgetchar ljmp matchtoken ljmp getnum ljmp getnumquiet ljmp toupper ljmp sputchar handle_event: .skip 3 initmsgtbl: .skip 3 msg_cmd: .skip 3 prompt: .skip 3 flash_cmd: .skip 3 handle_flash: .skip 3 clearline2: .skip 3 initflash: .skip 3 ljmp prtspc cancelflash: .skip 3 password_cmd: .skip 3 localmode: .skip 3 localmenu: .skip 3 lsetpass: .skip 3 lbrowsemsgs: .skip 3 ljmp rings_cmd ljmp pickup_cmd ljmp lsetrings ljmp lsetpick ljmp gettone ljmp gettone2 ljmp hangup ljmp tdelay ljmp lsetclock date_cmd: .skip 3 ljmp keydispatch ljmp sgetchar ljmp getline ljmp sersel ljmp speak ljmp saynum compute_day: .skip 3 ljmp printwday irmenu: .skip 3 lcd_select: .skip 3 printhex2: .skip 3 phonemenu: .skip 3 pickup: .skip 3 rsendcmd: .skip 3 rputchar: .skip 3 rreceivereply: .skip 3 rreceivechar: .skip 3 rfixserial: .skip 3 stackcheck: .skip 3 ljmp prt2dig timer_cmd: .skip 3 inittimertbl: .skip 3 llisttimers: .skip 3 gettimeraddr: .skip 3 bumptentry: .skip 3 comparedates: .skip 3 .org 0x4000 ;--------------------------------------------------------------------------- ; Function: sputs ; ; Description: ; Prints a zero-terminated string pointed to by DPTR. ; ; Inputs: DPTR - address of string. ; ; Outputs: None. ; sputs: movx A,@DPTR jz prtdone prtwait: lcall sputchar inc DPTR sjmp sputs prtdone: ret ;--------------------------------------------------------------------------- ; Function: tab ; ; Description: ; Moves the cursor to the indicated column, if not already past it. ; ; Inputs: A - desired column. ; ; Outputs: None. ; tab: lcall stackcheck 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: lcall stackcheck mov A,#13 lcall putchar mov A,#10 ljmp putchar ;----------------------------------------------------------- ; LCD Initialization ; ; Sets up the LCD for 2 line display, screen clear, cursor ; off, current address at home position. ; initlcd: mov DPTR,#initstr sjmp lputs initstr: .byte 31,0x38,31,0x0c,31,0x06,31,0x01,0 ;----------------------------------------------------------- ; LCD Output Functions ; ; Write characters and strings to the LCD. ; ; lputs ignores CR and LF characters. This allows printing ; strings originally intended for terminal display. lputs: movx A,@DPTR jz lpdone setb cdflag cjne a,#13,notcr1 sjmp lskipchar notcr1: cjne a,#10,notlf sjmp lskipchar notlf: cjne A,#31,normchar inc DPTR movx A,@DPTR jz lpdone clr cdflag normchar: lcall wrtlcd lskipchar: inc DPTR sjmp lputs lpdone: ret lputchar: setb cdflag ; (fall through to below) ;----------------------------------------------------------- ; LCD Interface Routine ; ; Writes a byte to the LCD, waiting for it to become ready ; first if necessary. ; ; Stack: 5 ; Trashes: ACC ; Inputs: cdflag (0 = cmd, 1 = data) ; ACC byte to write to LCD wrtlcd: push DPL push DPH push ACC mov dptr,#c4save movx a,@dptr setb ACC.2 clr ACC.3 movx @dptr,a mov dptr,#0xc400 movx @dptr,a wrtwait: lcall lcdcycle ; read busy flag jb ACC.7,wrtwait ; loop until zero mov dptr,#c4save movx a,@dptr mov C,cdflag mov ACC.3,C clr ACC.2 ; write movx @dptr,a mov dptr,#0xc400 movx @dptr,a pop ACC lcall lcdcycle pop DPH pop DPL ret lcdcycle: mov DPTR,#0xc000 clr IE.7 movx @DPTR,A movx A,@DPTR setb IE.7 lcall stackcheck ret ;--------------------------------------------------------------------------- ; Function: daysthismonth ; ; Description: ; Given a month number in A and the year number currently in R0, ; returns the number of days in that month. Handles February 29th for ; leap years. ; ; Inputs: A - month number ; R0 - year number ; ; Outputs: A - number of days ; daysthismonth: lcall stackcheck cjne A,#2,notfeb mov A,R0 anl A,#3 jnz not29feb mov A,#29 ret not29feb: mov A,#28 ret notfeb: mov DPTR,#dayspermonth-1 movc A,@A+DPTR gotdays: ret dayspermonth: .byte 31,28,31,30,31,30,31,31,30,31,30,31 ;----------------------------------------------------------- ; Routines to set/get the time ; getdate: clr IE.7 mov hours_copy,hours mov minutes_copy,minutes mov seconds_copy,seconds mov day_copy,day mov month_copy,month mov year_copy,year setb IE.7 lcall stackcheck ret setdate: lcall compute_day mov dptr,#weekday clr IE.7 movx @dptr,a mov hours,hours_copy mov minutes,minutes_copy mov seconds,seconds_copy mov day,day_copy mov month,month_copy mov year,year_copy mov tickcnt1,#36 mov tickcnt2,#100 setb secflag setb IE.7 lcall stackcheck ret ;----------------------------------------------------------- ; Standard output routines ; ; putchar Print character in A, clobber A ; puts Print null-terminated string pointed at ; by DPTR, clobber A and DPTR putchar: jb ioflag,notlc1 ljmp lputchar notlc1: ljmp sputchar puts: jb ioflag,notlc2 ljmp lputs notlc2: ljmp sputs ;--------------------------------------------------------------------------- ; 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: lcall stackcheck pop ACC add A,#'0' jc printdone lcall putchar sjmp nopad printdone: ret ;----------------------------------------------------------- ; Print the date and time ; Nice date format for looking at printdate: mov a,month_copy mov B,#3 mul AB mov R3,#3 mov DPTR,#monthtab-3 ploop: push ACC movc A,@A+DPTR lcall putchar pop ACC inc A djnz R3,ploop mov a,#' ' lcall putchar mov a,day_copy lcall prt2dig mov a,#' ' lcall putchar mov a,year_copy mov B,#100 div AB add a,#19 push B lcall prt2dig pop ACC ljmp prt2dig prttime: mov a,hours_copy lcall prt2dig ; Print ":", and the minute. mov a,#':' lcall putchar mov a,minutes_copy lcall prt2dig ; Print ":", the second, and exit. mov a,#':' lcall putchar mov a,seconds_copy lcall prt2dig ret monthtab: .byte "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC" ; Subroutine to print a number as 2 digits. prt2dig: mov r1,#'0' mov r2,#2 lcall stackcheck ljmp printnum printwday: mov B,#3 mul AB mov R3,#3 mov DPTR,#wdaytab ploop2: push ACC movc A,@A+DPTR lcall putchar pop ACC inc A djnz R3,ploop2 ret wdaytab: .byte "SUNMONTUEWEDTHUFRISAT" ;----------------------------------------------------------- ; LCD/keypad input routine. Start with the allowed input ; width in "_" characters, and rotate digits in from the ; right until "." is pressed. Null-terminated string is ; left in the input buffer. ; ; Inputs: R3 -- width. ; R4 -- LCD position command. lcd_input: clr hungupflag ; Build the initial string in the buffer mov R0,3 mov DPTR,#inbuf mov A,#'_' floop: movx @DPTR,A inc DPTR djnz R0,floop clr A movx @DPTR,A ; Display the current string li_display: mov A,R4 clr cdflag lcall wrtlcd mov DPTR,#inbuf lcall lputs ; Back up by one character mov a,#0x10 clr cdflag lcall wrtlcd ; Turn the blinking cursor on mov a,#0x0d lcall wrtlcd ; Wait for a character, exit if "." or if timeout. li_wchar: mov dptr,#tonetimer mov a,#60 movx @dptr,a li_wchar1: lcall kgetchar jnz li_gotchar mov dptr,#tonetimer movx a,@dptr jnz li_wchar1 setb hungupflag ret ; Clear the blinking cursor again li_gotchar: push ACC mov a,#0x0c clr cdflag lcall wrtlcd pop ACC cjne a,#'.',notdot ret ; Rotate the character into the string from the right. notdot: push ACC lcall stackcheck mov DPTR,#inbuf shiftloop: mov A,#1 movc A,@A+DPTR jz shiftdone movx @DPTR,A inc DPTR sjmp shiftloop shiftdone: pop ACC movx @DPTR,A sjmp li_display ;----------------------------------------------------------- ; Parse an 8-bit number out of the input buffer. Starts at ; the first numeric digit, finishes at the first nondigit. ; ; Output: R1 -- the number. ; cdflag -- 0: no number was input, A is invalid. ; ovflag -- 1: number did not fit in 8 bits. lcd_parsenum: clr cdflag clr ovflag mov R1,#0 mov DPTR,#inbuf lcall stackcheck piloop: movx A,@DPTR jz lnpdone clr C subb A,#'0' jc notdig cjne A,#10,dig2 dig2: jnc notdig setb cdflag ; 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,overflow add A,R1 jc overflow mov R1,A nextdig: inc DPTR sjmp piloop notdig: jnb cdflag,nextdig ret overflow: setb ovflag lnpdone: ret ;----------------------------------------------------------- ; Common part to all parts of setting the date: Print the ; date at the home position, print "Enter" on the second row, ; print a user supplied string, then finish the second row ; with "then press '.'". Then input a 2-digit number at the ; position supplied in R4 and return. sd_common: push DPL push DPH lcall clearline2 clr cdflag mov A,#0x80 lcall wrtlcd lcall prttime mov a,#' ' lcall putchar lcall printdate mov DPTR,#estring lcall stackcheck lcall puts pop DPH pop DPL lcall puts mov DPTR,#tpdstring lcall puts mov R3,#2 lcall lcd_input jb hungupflag,ldi_abort ljmp lcd_parsenum ; A timeout occurred. Pop one return address off the ; stack and return to the caller of "inputdate" with the ; hungupflag still set. ldi_abort: pop ACC pop ACC ret estring: .byte 31,0xc0,"Enter ",0 tpdstring: .byte ", then press '.'",0 yearstring: .byte 31,0x90,"XX",31,0xc6,"year (80-99/00-79)",0 monthstring: .byte 31,0x89," ",31,0xc6,"month",0 daystring: .byte "day",0 hourstring: .byte "hour (0-23)",0 minstring: .byte "minutes",0 secstring: .byte "seconds",0 ;----------------------------------------------------------- ; Input a date. Returns with the "date copy" registers set, ; caller must actually update the clock. inputdate: mov DPTR,#yearstring mov R4,#0x92 lcall sd_common jnb cdflag,domonth mov A,R1 clr C subb A,#80 jnc not_2000_1 mov A,#100 add A,R1 mov R1,A not_2000_1: mov year_copy,R1 domonth: mov DPTR,#monthstring mov R4,#0x8a lcall sd_common jnb cdflag,doday mov A,R1 jz domonth mov month_copy,A clr C subb A,#13 jnc domonth doday: mov DPTR,#daystring mov R4,#0x8d lcall sd_common jb cdflag,gotday mov R1,day_copy gotday: mov A,R1 jz doday mov A,month_copy mov R0,year_copy lcall daysthismonth clr C subb A,R1 jc doday mov day_copy,R1 dohour: mov DPTR,#hourstring mov R4,#0x80 lcall sd_common jnb cdflag,dominute mov A,R1 mov hours_copy,A clr C subb A,#24 jnc dohour dominute: mov DPTR,#minstring mov R4,#0x83 lcall sd_common jnb cdflag,dosecond mov A,R1 mov minutes_copy,A clr C subb A,#60 jnc dominute dosecond: mov DPTR,#secstring mov R4,#0x86 lcall sd_common jb cdflag,gotsec mov R1,#0 gotsec: mov A,R1 mov seconds_copy,A clr C subb A,#60 jnc dosecond ret ;--------------------------------------------------------------------------- ; 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,dig3 dig3: 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,#' ',checkr2 sjmp numdone checkr2: cjne a,2,nextdigit numdone: ret ; Error exit. badnum: setb badflag jb quietflag,numdone mov dptr,#badnumstr ljmp sputs badnumstr: .byte "Invalid number entered.",13,10,0 ;--------------------------------------------------------------------------- ; 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 lcall stackcheck ret notlower: pop ACC ret ;--------------------------------------------------------------------------- ; Phone answer maintenance commands rings_cmd: lcall skipspc jnz setrings lcall showrings ljmp prompt setrings: mov r2,#0 lcall getnum jb badflag,setrings_exit mov dptr,#pickupcount mov a,R1 movx @dptr,a setrings_exit: ljmp prompt showrings: mov dptr,#pickupcount movx a,@dptr mov dptr,#ansstr3 jz rings_never push ACC mov dptr,#ansstr lcall puts pop ACC mov R1,#' ' mov R2,#0 lcall printnum mov dptr,#ansstr2 rings_never: ljmp puts lsetrings: clr cdflag mov a,#1 lcall wrtlcd lcall showrings mov dptr,#lsrstring lcall lputs mov R3,#3 mov R4,#0xca lcall lcd_input jb hungupflag,ls_timeout lcall lcd_parsenum jb ovflag,lsetrings jnb cdflag,lsr_done mov dptr,#pickupcount mov a,R1 movx @dptr,a lsr_done: ljmp phonemenu ls_timeout: ljmp localmode ansstr: .byte "Answer after ",0 lsrstring: .byte 31,0xc0,"Change to ___ (0 = never)",0 ansstr2: .byte " rings.",13,10,0 ansstr3: .byte "Never answer.",13,10,0 pickup_cmd: lcall skipspc jnz h_set_pickup lcall show_pickup ljmp prompt h_set_pickup: mov r2,#0 lcall getnum jb badflag,show_p_exit lcall set_pickup jnb badflag,hs_bye mov dptr,#badtonestr lcall puts hs_bye: ljmp prompt ; LCD version lsetpick: clr cdflag mov a,#1 lcall wrtlcd lcall show_pickup mov dptr,#lsetpickstr lcall lputs mov R3,#2 mov R4,#0x9e lcall lcd_input jb hungupflag,lsp_timeout lcall lcd_parsenum jb ovflag,lsetpick jnb cdflag,lsp_done mov a,R1 lcall set_pickup jb badflag,lsetpick lsp_done: ljmp phonemenu lsp_timeout: ljmp localmode set_pickup: clr badflag cjne R1,#0,notp0 mov R1,#10 notp0: mov a,R1 clr C subb a,#13 jnc setp_bad mov dptr,#pickuptone mov a,R1 movx @dptr,a ret setp_bad: setb badflag ret show_pickup: mov dptr,#pickstr lcall puts mov dptr,#pickuptone movx a,@dptr mov R0,a clr C subb a,#10 jc show_p1 mov dptr,#picktbl movc a,@a+dptr sjmp show_printit show_p1: mov a,R0 add a,#'0' show_printit: lcall putchar mov dptr,#pickstr2 lcall puts show_p_exit: ret pickstr: .byte "Pick up on '",0 pickstr2: .byte "'.",13,10,0 picktbl: .byte "0*#" badtonestr: .byte "Bad DTMF code.",13,10,0 lsetpickstr: .byte 31,0x94,"Change to __" .byte 31,0xc0,"Enter 0-9, 11 = '*', 12 = '#'",0 ;--------------------------------------------------------------------------- ; Clear part of the LCD display. Start position command supplied in A, ; number of bytes to clear in R1. prtspc: clr cdflag lcall wrtlcd psloop: mov a,#' ' lcall lputchar djnz R1,psloop lcall stackcheck ret ;--------------------------------------------------------------------------- ; Get a DTMF tone. If no tone received for 30 seconds from the time this ; is called, say "timeout" and hang up. gettone: mov dptr,#tonetimer mov a,#30 movx @dptr,a gettone2: jb toneflag,gottone lcall handle_event mov dptr,#tonetimer movx a,@dptr jnz gettone2 ; Timeout. Say so, stay online long enough for the ; voice to finish saying it, then hang up. mov dptr,#timeoutstr lcall speak mov ticktimer1,#150 lcall tdelay setb hungupflag ljmp hangup ; Got a tone. gottone: mov dptr,#lasttone movx a,@dptr clr toneflag ret timeoutstr: .byte "time-out",0 ;--------------------------------------------------------------------------- ; Hang up the phone. Be nice and say "good bye" first. hangup: mov dptr,#byestring lcall speak mov ticktimer1,#100 lcall tdelay mov dptr,#c4save movx a,@dptr clr ACC.4 clr ACC.5 movx @dptr,a mov dptr,#0xc400 movx @dptr,a ret byestring: .byte "good bye",0 ;--------------------------------------------------------------------------- tdelay: mov a,ticktimer1 jz d1done lcall handle_event sjmp tdelay d1done: lcall stackcheck ret ;--------------------------------------------------------------------------- ; Local set clock function lsetclock: clr showtime mov dptr,#setclockstr lcall lputs lcall getdate lcall inputdate jb hungupflag,ls_abort lcall clearline2 mov dptr,#lsetstring lcall lputs lcall prttime mov a,#' ' lcall lputchar lcall printdate mov dptr,#lsettbl ljmp keydispatch lsettbl: .byte '.' ljmp lsetit .byte 0 lsetit: lcall setdate ljmp localmenu ls_abort: ljmp localmode lsetstring: .byte 31,0xc0,"Press '.' to start the clock" .byte 31,0x80,0 setclockstr: .byte 31,1,31,0x9f,"CLOCK SET",0 ;--------------------------------------------------------------------------- ; LCD menu dispatcher. Gets a character, then traverses a table of ; ASCII codes and jump instructions, and executes the one following the ; character that was input. ; ; NOTE: ASSUMES THAT IT RUNS AT THE SAME STACK DEPTH AS THE MAIN LCD ; "READY" FUNCTION. JUMPS DIRECTLY THERE IF A TIMEOUT OCCURS. keydispatch: mov r1,DPH mov r2,DPL mov dptr,#tonetimer mov a,#60 movx @dptr,a kdtop: lcall kgetchar jnz gotkey mov dptr,#tonetimer movx a,@dptr jnz kdtop ljmp localmode gotkey: mov DPH,r1 mov DPL,r2 mov r0,a gkloop: movx a,@dptr inc dptr jz kdtop cjne a,0,trynext1 clr a jmp @a+dptr trynext1: inc dptr inc dptr inc dptr sjmp gkloop ;--------------------------------------------------------------------------- ; Function: sputchar ; ; Description: ; Prints a character to the controlling terminal. Also maintains the ; "tab position" counter required by the "tab" function. ; ; Absorbs characters coming in from the serial port (since we don't ; do typeahead). This allows XON/XOFF output control. ; ; Inputs: A - character to print. ; ; Outputs: None. ; sputchar: push ACC lcall stackcheck sploop: lcall sgetchar jb xoffflag,sploop jnb SCON.1,sploop pop ACC 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 ;--------------------------------------------------------------------------- ; sgetchar: clr A lcall stackcheck lcall handle_event jnb SCON.0,notxon mov a,SBUF clr SCON.0 clr ACC.7 ; Clear parity bit cjne a,#19,notxoff setb xoffflag sjmp sgetchar notxoff: cjne a,#17,notxon clr xoffflag sjmp sgetchar notxon: ret ;--------------------------------------------------------------------------- ; Function: getline ; ; Description: ; "getline" gets a command line from the controlling terminal into the ; line input buffer. ; ; Borrows the timer and "hungupflag" used by the DTMF routines to time out ; after 60 seconds of no keystrokes so that we don't get stuck in host mode. ; ; Inputs: None. ; getline: mov P2,#bufpage mov r1,#0 clr hungupflag ; Character loop. gloop: mov a,#60 mov dptr,#tonetimer movx @dptr,a gloop1: lcall sgetchar jnz gotchar mov dptr,#tonetimer movx a,@dptr jnz gloop1 mov dptr,#g_timeoutstr lcall sputs setb hungupflag ret ; Check for backspace or rubout. gotchar: 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,#bufsize jnc gloop ; Advance the buffer pointer and echo the character if echo ; is enabled. Otherwise, echo a space. movx a,@r1 inc r1 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 g_timeoutstr: .byte 13,10,"TIMEOUT",13,10,0 ;--------------------------------------------------------------------------- ; This routine switches serial routings by writing the configuration number ; in A into the serial router register. Serial configurations should only ; be switched when nobody is sending on the current connection, or the ; character currently being sent will be trashed. ; First wait for the 8031 to finish sending the last ; character. sersel: jnb SCON.1,sersel mov dptr,#c8save movx @dptr,a mov dptr,#0xc800 movx @dptr,a clr xoffflag ; If we switched routings in the middle of an incoming ; character, it will be trashed. So delay for a bit and ; then clear out whatever was received. mov ticktimer1,#2 ssdelayloop: mov a,ticktimer1 jnz ssdelayloop clr SCON.0 ret ;--------------------------------------------------------------------------- ; Before starting to speak, make sure the DTMF receiver ; indicates silence. DTMF tones tend to drown out the ; voice. speak: push DPL push DPH lcall stackcheck mov dptr,#0xc800 speak1: movx a,@dptr jnb ACC.4,speak2 lcall handle_event sjmp speak1 speak2: mov dptr,#c8save movx a,@dptr anl a,#0x03 mov r0,a mov a,#1 lcall sersel pop DPH pop DPL lcall sputs mov a,#13 lcall sputchar mov a,r0 ljmp sersel saynum: mov P2,#bufpage mov R1,#0 mov B,#100 div ab push B jz nothundred lcall say2dig mov dptr,#str100 lcall tcloop pop ACC jz sndone push ACC mov dptr,#strand lcall tcloop nothundred: pop ACC lcall say2dig sndone: clr a movx @r1,a mov dptr,#inbuf ljmp speak ; Translate a number between 0 and 99. say2dig: mov B,a clr c subb a,#10 jnc not1dig mov a,#'0' add a,B movx @r1,a inc r1 ret not1dig: subb a,#10 jnc twenty add a,#10 mov dptr,#tentbl sjmp tblcopy twenty: mov a,#10 xch a,B div ab mov dptr,#tenstbl-4 lcall tblcopy mov a,#'-' movx @r1,a inc r1 mov a,B jz tcdone sjmp say2dig tblcopy: rl A mov R0,A movc a,@a+dptr xch A,R0 inc dptr movc a,@a+dptr mov DPL,a mov DPH,R0 tcloop: movx a,@dptr jz tcdone movx @r1,a inc dptr inc r1 sjmp tcloop tcdone: ret tentbl: .word str10,str11,str12,str13,str14,str15,str16,str17 .word str18,str19 tenstbl: .word str20,str30,str40,str50,str60,str70,str80,str90 str10: .byte "ten",0 str11: .byte "eleven",0 str12: .byte "twelve",0 str13: .byte "thirteen",0 str14: .byte "4teen",0 str15: .byte "fifteen",0 str16: .byte "6teen",0 str17: .byte "7teen",0 str18: .byte "8een",0 str19: .byte "9teen",0 str20: .byte "twenty",0 str30: .byte "thirty",0 str40: .byte "4te",0 str50: .byte "fifty",0 str60: .byte "6te",0 str70: .byte "7te",0 str80: .byte "8e",0 str90: .byte "9te",0 str100: .byte " hun-dred",0 strand: .byte ",,and ",0