;: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 .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 sersave: .skip 1 rcommtimer: .skip 1 .equ msgtbl,0x2400 .equ nummsgs,6 .org 0x2100 ljmp tick_isr .org 0x2300 initlcd: .skip 3 lputs: .skip 3 wrtlcd: .skip 3 lputchar: .skip 3 kgetchar: .skip 3 daysthismonth: .skip 3 getdate: .skip 3 setdate: .skip 3 putchar: .skip 3 puts: .skip 3 printnum: .skip 3 printdate: .skip 3 prttime: .skip 3 lcd_input: .skip 3 lcd_parsenum: .skip 3 inputdate: .skip 3 crlf: .skip 3 tab: .skip 3 sputs: .skip 3 skipspc: .skip 3 bgetchar: .skip 3 matchtoken: .skip 3 getnum: .skip 3 getnumquiet: .skip 3 toupper: .skip 3 sputchar: .skip 3 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 prtspc: .skip 3 cancelflash: .skip 3 password_cmd: .skip 3 localmode: .skip 3 localmenu: .skip 3 lsetpass: .skip 3 lbrowsemsgs: .skip 3 rings_cmd: .skip 3 pickup_cmd: .skip 3 lsetrings: .skip 3 lsetpick: .skip 3 gettone: .skip 3 gettone2: .skip 3 hangup: .skip 3 tdelay: .skip 3 lsetclock: .skip 3 date_cmd: .skip 3 keydispatch: .skip 3 sgetchar: .skip 3 getline: .skip 3 sersel: .skip 3 speak: .skip 3 saynum: .skip 3 compute_day: .skip 3 printwday: .skip 3 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 prt2dig: .skip 3 timer_cmd: .skip 3 inittimertbl: .skip 3 llisttimers: .skip 3 gettimeraddr: .skip 3 bumptentry: .skip 3 comparedates: .skip 3 .org 0x5000 ;----------------------------------------------------------- ; Keypad Scan Routine ; ; Returns the ASCII code for the key that is being pressed, ; or 0 if no key down or if several are pressed. ; ; Trashes R0,R1,DPTR. Returns result in R0. scankey: lcall stackcheck mov R0,#0 ; no key found yet clr P1.2 ; select row 0 setb P1.3 rowloop: mov DPTR,#0xc400 ; get column bits movx A,@DPTR mov R1,#7 ; loop 7 times colloop: rrc A ; shift out rightmost bit jc nobit ; if bit is set push ACC mov A,#-1 ; bogus key code cjne R0,#0,loadr0 ; if first key seen mov A,R1 ; A gets 7..1 mov C,P1.2 mov ACC.3,C ; A gets 1..7, 9...15 mov DPTR,#keytab-1 movc A,@A+DPTR ; fetch entry from table loadr0: mov R0,A ; save it pop ACC nobit: djnz R1,colloop ; endloop jb P1.2,scandone setb P1.2 clr P1.3 ajmp rowloop scandone: setb P1.3 cjne R0,#0xff,notinv inc R0 notinv: ret keytab: .byte "4653210",-1,-1,-1,-1,".987" ;----------------------------------------------------------- ; Interrupt service routine ; tick_isr: djnz tickcnt1,isr_exit ; Reload the counter and save context. mov tickcnt1,#36 push PSW push ACC push DPL push DPH mov dptr,#scan_entry push DPL push DPH lcall stackcheck isr_exit: reti ; This is executed 100 times per second. Select register ; bank 1 (bank 0 will be restored by the "pop psw" at exit). scan_entry: mov PSW,#08h ; Keypad scan processing ; ---------------------- acall scankey mov A,R0 cjne A,keycode,newkey mov A,keytimer jz scan_exit djnz keytimer,scan_exit ; We have a debounced key code. jz scan_exit mov keyout,R0 ajmp scan_exit newkey: mov keytimer,#5 mov keycode,R0 ; Telephone answering stuff ; ------------------------- ; Increment the ring timer. scan_exit: mov dptr,#ringtimer movx a,@dptr inc a movx @dptr,a ; Check ring detect. mov dptr,#0xc400 movx a,@dptr jb ACC.7,noring ; Ring detect is asserted. Deal with each of the three ; states. jnb inring,nir1 ; Inside a ring and still ringing. Wait until 1/2 second ; of ringing has elapsed. mov dptr,#ringtimer movx a,@dptr clr C subb a,#20 jc rd_exit ; Have 1/2 second of ringing. Count the ring, and go ; to "finished with ring" state. mov dptr,#ringcount movx a,@dptr inc a movx @dptr,a setb ringflag clr inring sjmp rd_exit ; Ring detect asserted outside "in ring" state. If in ; "waiting for pause" state, keep waiting. nir1: jnb validpause,rd_exit ; Ring detect asserted in pause state. Reset the ring ; timer and go into "in ring" state. mov dptr,#ringtimer clr a movx @dptr,a clr validpause setb inring ; Common exit. 0 ticks of silence so far. rd_exit: clr a mov dptr,#silenttimer movx @dptr,a inc dptr movx @dptr,a sjmp ring_exit ; Ring detect is not asserted. First, increment the silent ; timer. noring: mov dptr,#silenttimer movx a,@dptr add a,#1 movx @dptr,a jnc nr1 inc dptr movx a,@dptr inc a movx @dptr,a ; Just incremented the high byte. If incremented to 4, then ; no ringing for 1024 ticks or 10.24 seconds which means it ; has stopped ringing. cjne a,#4,nr1 mov dptr,#ringcount clr a movx @dptr,a ; If in valid pause state, that's all. nr1: jb validpause,ring_exit ; If no ring for 1/2 second, enter valid pause state. Note ; that if we aren't in valid pause state, no carry into the ; high byte of the silence counter could have occurred and ; therefore the low byte is still in A. clr C subb a,#50 jc notyetvalid clr inring setb validpause sjmp ring_exit ; The only work left to do is in ring state. notyetvalid: jnb inring,ring_exit ; If 50 ticks of silence, the previous bit already took us ; to pause state. If less than 5 ticks of silence, all OK. ; But if more than 5 ticks of silence, the current ring ; had too big a gap in it and the 1/2 second timeout must ; be restarted. mov dptr,#silenttimer movx a,@dptr clr C subb a,#5 jc ring_exit mov dptr,#ringtimer clr a movx @dptr,a ; DTMF Decoder Stuff ; ------------------ ring_exit: mov dptr,#0xc800 movx a,@dptr jnb ACC.4,notone jb seentone,dtmf_exit jb toneflag,dtmf_exit setb seentone setb toneflag anl a,#0x0f mov dptr,#lasttone movx @dptr,a sjmp dtmf_exit notone: clr seentone dtmf_exit: mov a,ticktimer1 jz t1_exit dec ticktimer1 t1_exit: mov dptr,#flashtimer movx a,@dptr jz t2_exit dec a movx @dptr,a t2_exit: djnz tickcnt2,int_exit sjmp do1sec ; Restore context and exit. int_exit: pop DPH pop DPL pop ACC pop PSW ret ; The following is executed once per second. First, reload ; the counter. do1sec: mov tickcnt2,#100 ; Decrement 1-second timers as required. mov dptr,#listentimer movx a,@dptr jz notlt dec a movx @dptr,a notlt: mov dptr,#tonetimer movx a,@dptr jz nottt dec a movx @dptr,a nottt: mov dptr,#rcommtimer movx a,@dptr jz notrct dec a movx @dptr,a ; Signal a "time has changed" event. notrct: setb secflag ; Update clock ; ------------ ; Add 1 second to the current time. wait_idle: mov A,#60 inc seconds cjne A,seconds,date_done mov seconds,#0 inc minutes cjne A,minutes,date_done mov minutes,#0 mov A,#24 inc hours cjne A,hours,date_done mov hours,#0 ; Add 1 day to the date. mov dptr,#weekday movx a,@dptr inc a cjne a,#7,storit clr a storit: movx @dptr,a mov A,month mov R0,year lcall daysthismonth cjne A,day,notlastday mov day,#0 mov A,#13 inc month cjne A,month,notlastday mov month,#1 inc year notlastday: inc day ; End of interrupt processing. date_done: ljmp int_exit