;:ts=8 .org 0x1E00 prompt: .skip 3 putchar: .skip 3 prtstr: .skip 3 tab: .skip 3 crlf: .skip 3 printnum: .skip 3 printhex2: .skip 3 getline: .skip 3 bgetchar: .skip 3 toupper: .skip 3 skipspc: .skip 3 matchtoken: .skip 3 gethex: .skip 3 getnum: .skip 3 getnumquiet: .skip 3 getport: .skip 3 handle_event: .skip 3 setbaud: .skip 3 vconnect: .skip 3 connect: .skip 3 isconnected: .skip 3 clear: .skip 3 fixtbl: .skip 3 daysthismonth: .skip 3 checkmaint: .skip 3 maskaddr: .skip 3 setown: .skip 3 bye: .skip 3 getmaskbit: .skip 3 isnotmine: .skip 3 .equ myport,0x1f .equ brkflagtbl,0x20c5 .equ dtrcnttbl,0x20d1 .equ c_sourcetbl,0x20e4 .equ maxport,6 .flag attnflag,0x20.4 .flag queueflag,0x20.5 .flag byeflag,0x20.6 .equ tickcnt2,0x11 .flag busyflag,0x27.0 .flag timerbusyflag,0x27.1 .equ redialtimer,0x26 .equ alarmtimer,0x28 .equ modemport,2 .org 0x3C00 .equ numtimers,10 .equ maxevent,10 .equ tentrysize,8 ; last byte is # of seconds to alert. timertbl: .byte 0,0,0,0,0,0,0,0 .byte 0,0,0,0,0,0,0,0 .byte 0,0,0,0,0,0,0,0 .byte 0,0,0,0,0,0,0,0 .byte 0,0,0,0,0,0,0,0 .byte 0,0,0,0,0,0,0,0 .byte 0,0,0,0,0,0,0,0 .byte 0,0,0,0,0,0,0,0 .byte 0,0,0,0,0,0,0,0 .byte 0,0,0,0,0,0,0,0 numbuf: .byte "5260702",0 .skip 72 isr_reg_save: .skip 7 stackmax: .byte 0 dpsave: .word 0xdead dumpaddr: .word 0 redial_time: .byte 240 .org 2206h ; Stack monitor stuff checkstack: mov dptr,#stackmax movx a,@dptr clr c subb a,SP jnc cs_done mov a,SP movx @dptr,a cs_done: ret stack_cmd: mov dptr,#stackmax movx a,@dptr lcall printhex2 lcall crlf ljmp prompt alarm_cmd: mov a,alarmtimer lcall printhex2 lcall crlf ljmp prompt dump_cmd: lcall skipspc jz dump_four mov R2,#0 lcall gethex jb badflag,dump_end mov DPH,R3 mov DPL,R4 mov R2,#5 sjmp stash_dptr dump_four: mov R2,#4 dump_line: mov dptr,#dumpaddr movx a,@dptr push ACC lcall printhex2 inc dptr movx a,@dptr mov DPL,a lcall printhex2 pop DPH mov a,#':' lcall putchar mov R1,#16 dump_byte: mov a,#' ' lcall putchar movx a,@dptr inc dptr lcall printhex2 djnz R1,dump_byte lcall crlf stash_dptr: mov a,DPH push DPL mov dptr,#dumpaddr movx @dptr,a inc dptr pop ACC movx @dptr,a djnz R2,dump_line dump_end: ljmp prompt ; Enter here as a subroutine call just after a non-null ; command line is entered. decodecmd: mov dptr,#cmdtbl lcall matchtoken jnz got_cmd ret ; One of our own commands was matched. Scrap the return ; address and take over. got_cmd: dec SP dec SP mov B,#3 mul ab mov dptr,#jmptbl-3 jmp @a+dptr ; Command jump table jmptbl: ljmp dial_cmd ; User commands ljmp timer_cmd ljmp kill_cmd ljmp stack_cmd ljmp alarm_cmd ljmp dump_cmd ljmp delay_cmd cmdtbl: .byte "DIAL",0,"TIMER",0,"KILL",0,"STACK",0,"ALARM",0 .byte "DUMP",0,"DELAY",0,0 isr: mov a,tickcnt2 cjne a,#100,not1sec ; The ISR is going to process a 1-second transition. ; Stash an extra "return address" on the stack so that ; our own additional 1-second stuff is run at the end. ; Note: First clear off the saved return address, DPTR, ; PSW, and accumulator. pop 15 pop 14 pop 13 pop 12 pop 11 pop 10 mov dptr,#my1secaddr push DPL push DPH push 10 push 11 push 12 push 13 push 14 push 15 lcall checkstack not1sec: ret ; This unhooks the code so another version can be safely ; loaded over top. kill_cmd: mov a,#0x22 mov dptr,#0x2200 movx @dptr,a mov dptr,#0x2203 movx @dptr,a mov dptr,#gonestr lcall prtstr ljmp prompt gonestr: .byte "Gone.",13,10,0 delay_cmd: lcall skipspc jz delay_error mov R2,#0 lcall getnumquiet jb badflag,delay_error mov a,R1 mov dptr,#redial_time movx @dptr,a ljmp prompt delay_error: mov dptr,#destr lcall prtstr ljmp prompt destr: .byte "Error.",13,10,0 ; Code copied from the "call" command, to get the modem. dial_cmd: lcall skipspc jz getmodem mov dptr,#numbuf gnloop: lcall bgetchar movx @dptr,a inc dptr jnz gnloop getmodem: mov a,#modemport lcall isconnected jz c_port_ok mov a,#modemport mov r0,myport lcall isnotmine jz c_port_ok mov dptr,#m_busy_str lcall prtstr ljmp prompt ; Disconnect anything currently listening to the ; originating port (clears any previous call) c_port_ok: mov r5,#1 clear_loop: mov dptr,#c_sourcetbl-1 mov a,r5 movc a,@a+dptr cjne a,myport,cl_loopend mov a,r5 mov r0,myport lcall isnotmine jnz cl_loopend ; Found a port belonging to, and listening to the originator. mov r0,5 mov r1,#15 lcall vconnect cl_loopend: inc r5 cjne r5,#maxport+1,clear_loop ; Establish bidirectional link, establish ownership of both ; ends of the link, and go online. mov r0,myport mov r1,#modemport lcall vconnect mov r0,#modemport mov r1,myport lcall vconnect mov a,myport lcall setown mov a,#modemport lcall setown ; Connection to the modem is established. Set the CPU to ; listen to it. mov r0,#0 mov r1,#modemport lcall connect ; Print the "dialing" message to the caller. dialattempt: lcall sethost mov dptr,#dialstr lcall prtstr mov dptr,#numbuf lcall prtstr mov dptr,#dialstr2 lcall prtstr ; Print the dial command to the modem. Clear any garbage ; from the serial receive register first. charwait2: jnb SCON.1,charwait2 mov r0,myport mov r1,#15 lcall connect mov r0,#modemport mov r1,#0 lcall connect mov dptr,#dialcmd lcall prtstr mov dptr,#numbuf lcall prtstr mov a,#13 lcall putchar charwait3: jnb SCON.1,charwait3 clr SCON.0 ; This flag is used here to distinguish between busy and ; other dial failures. clr busyflag ; Wait for a numeric return code, processing events in ; the mean time. If any port sends break, quit. rcwait: jnb attnflag,notattn ; First check for a break from own port. The event handler ; ignores these but we want those to abort the autodialer ; too. mov a,myport mov dptr,#brkflagtbl-1 movc a,@a+dptr jnz gotbreak ; Handle other kinds of event. notmybreak: lcall handle_event jb byeflag,goodbye jnb queueflag,notattn ; Someone pressed break. Send a character to the modem ; to shut it up, print abort message and exit. gotbreak: mov a,#13 lcall putchar lcall sethost mov dptr,#abortstr lcall prtstr goodbye: ljmp bye notattn: jnb SCON.0,rcwait ; A character came from the modem. Interpret it. mov a,SBUF clr ACC.7 clr SCON.0 cjne a,#13,notcr ; CR = junk sjmp rcwait notcr: cjne a,#'7',not9 ; 7 = 'busy' ; When the QT1200 modem returns "BUSY" it is about to dial ; again. Sending a CR at this point aborts that and ; returns "NO CARRIER". mov a,#13 ; Cancel dialing lcall putchar setb busyflag ; Record 'busy' status sjmp dfail not9: cjne a,#'3',not3 ; 3 = 'no carrier' ; Dialing failed and we are on hook. dfail: lcall sethost mov dptr,#nocarstr jnb busyflag,prtfailmsg mov dptr,#busystr prtfailmsg: lcall prtstr ; Start the timer at 240 seconds. mov dptr,#redial_time movx a,@dptr mov redialtimer,a mov r7,#0 ; Wait, processing events, until the timer expires or the ; modem indicates that the phone is ringing. dialwait: jnb attnflag,notattn2 mov a,myport mov dptr,#brkflagtbl-1 movc a,@a+dptr jnz abort2 lcall handle_event jb byeflag,goodbye2 jnb queueflag,notattn2 abort2: mov dptr,#abortstr lcall prtstr goodbye2: ljmp bye notattn2: jb SCON.0,wgotchar mov a,redialtimer cjne a,7,na2_upd jnz dialwait ljmp dialattempt na2_upd: mov r7,a mov R1,#' ' mov R2,#4 lcall printnum mov a,#13 lcall putchar sjmp dialwait wgotchar: mov a,SBUF clr SCON.0 clr ACC.7 cjne a,#'2',dialwait ; Got a 'ring' return code! Abort so we don't make a call ; attempt while the phone is being used. ring_abort: mov dptr,#rangstr lcall prtstr ljmp bye not3: cjne a,#'8',not8 ; 8 = QT1200 'ringing' ljmp rcwait ; Don't know what to do with this return code. not8: cjne a,#'1',not1 ; 1 = 'connect' ; We have connected! lcall sethost mov dptr,#connstr lcall prtstr ; User is listening to controller, modem is listening to ; nothing. Make controller listen to user, user listen ; to modem. setmod: jnb SCON.1,setmod mov r0,myport mov r1,modemport lcall connect mov r0,#0 mov r1,myport lcall connect ; Clear out old junk in the serial receive register. clr SCON.0 ; Kludge: Set the DTR table entry for the modem to ; "have carrier" until it is next updated by the interrupt ; handler. mov dptr,#dtrcnttbl-1+modemport clr a movx @dptr,a ; While the modem indicates carrier, keep alerting the ; user. beeploop: mov dptr,#dtrcnttbl-1+modemport movx a,@dptr jnz lostcarrier ; Keep beeping until a key is pressed or a break comes in ; from another port, then go online. mov a,tickcnt2 clr c subb a,#50 mov P1.7,c jb SCON.0,beepbye jnb attnflag,beeploop beepbye: setb P1.7 clr SCON.0 ljmp bye lostcarrier: lcall sethost mov dptr,#lcstr lcall prtstr sjmp beepbye not1: cjne a,#'2',not2 lcall sethost sjmp ring_abort not2: push ACC mov a,#13 lcall putchar lcall sethost mov dptr,#badrcstr lcall prtstr pop ACC lcall putchar lcall crlf ljmp bye sethost: jnb SCON.1,sethost mov r0,myport mov r1,#0 lcall connect mov r0,#modemport mov r1,#15 ljmp connect dialstr: .byte "Dialing ",0 dialstr2: .byte "... ",0 dialcmd: .byte "ATE0V0W1\\V3DT",0 abortstr: .byte "Aborted.",13,10,0 badrcstr: .byte "Unknown return code: ",0 nocarstr: .byte "No carrier.",13,10,0 busystr: .byte "Busy.",13,10,0 connstr: .byte "Connected! Any key to cancel alarm.",13,10,7,0 rangstr: .byte "Aborted by incoming call.",13,10,0 m_busy_str: .byte "Modem in use.",13,10,0 lcstr: .byte 13,10,"*** Carrier lost ***",13,10,0 ;-------------------------------------------------------------------------- .equ hours,0x12 .equ minutes,0x13 .equ seconds,0x14 .equ day,0x15 .equ month,0x16 .equ year,0x25 .equ year_copy,0x17 .equ month_copy,0x18 .equ day_copy,0x19 .equ hours_copy,0x1a .equ minutes_copy,0x1b .equ seconds_copy,0x1c .flag badflag,0x20.0 .flag date_get_flag,0x20.1 ;----------------------------------------------------------------------- ; This function takes a timer entry pointed to by DPTR and compares it to ; the date in the "date_copy" variables. The carry flag is set if the ; timer entry has not yet reached the time in the "date_copy" variables. comparedates: mov R1,#year_copy cd_loop: inc dptr movx a,@dptr ; get t.year mov r0,a mov a,@R1 ; get c.year clr C subb a,r0 ; c.year - t.year jc cd_notyet ; if <0, then not yet jnz cd_now ; if >0, then yes ; Current date component is equal. Keep comparing until ; the end of the date is reached. inc R1 cjne R1,#seconds_copy+1,cd_loop ; If the loop finishes, the dates are equal. cd_now: clr C cd_notyet: ret ;--------------------------------------------------------------------------- invalidmsg: .byte "Time format invalid: ",0 ydatemsg: .byte "YY/" modatemsg: .byte "MM/" ddatemsg: .byte "DD " hdatemsg: .byte "HH:" midatemsg: .byte "MM[:SS] expected.",13,10,0 ; "Getdate" routine can be entered at each date component ; and gets components from there down. The ":SS" seconds ; component is optional. The caller must have called ; "skipspc" already. getdate_year: mov r2,#'/' lcall getnumquiet jnb badflag,gd_yearok ljmp gd_bad gd_yearok: mov a,r1 clr C subb a,#80 jnc not_2000 mov a,#100 add a,r1 mov r1,a not_2000: mov year_copy,r1 ; Get the month getdate_month: mov r2,#'/' lcall getnumquiet jb badflag,gd_bad mov a,r1 jz gd_bad mov month_copy,a clr c subb a,#13 jnc gd_bad ; Get the day, making sure it fits with the month. mov r2,#' ' lcall getnumquiet jb badflag,gd_bad mov a,r1 jz gd_bad mov a,month_copy mov r0,year_copy lcall daysthismonth sjmp getday_common ; Get the day independent of the month, therefore allowing ; any value between 1 and 31. getdate_day: mov r2,#' ' lcall getnumquiet jb badflag,gd_bad mov a,r1 jz gd_bad mov a,#31 getday_common: clr c subb a,r1 jc gd_bad mov day_copy,r1 lcall skipspc ; Get the hour getdate_hour: mov r2,#':' lcall getnumquiet jb badflag,gd_bad mov a,r1 mov hours_copy,a clr c subb a,#24 jnc gd_bad ; Get the minute getdate_minute: mov r2,#':' lcall getnumquiet mov r2,a jb badflag,gd_bad mov a,r1 mov minutes_copy,a clr c subb a,#60 jnc gd_bad ; Set the second default to zero, then check if the second ; was supplied. mov seconds_copy,#0 cjne r2,#':',gd_ok ; Get the second mov r2,#' ' lcall getnumquiet jb badflag,gd_bad mov a,r1 mov seconds_copy,a clr c subb a,#60 jnc gd_bad gd_ok: ret gd_bad: setb badflag ret ;--------------------------------------------------------------------------- ; This function gets a timer event specification. CLI input formats ; are as follows: ; ; 0 NEVER ; 1 ONCE YY/MM/DD HH:MM:SS ; 2 HOURLY MM:SS ; 3 DAILY HH:MM:SS ; 4 MONTHLY DD HH:MM:SS ; 5 YEARLY MM/DD HH:MM:SS ; 128-255 HH:MM:SS ; ; First, the necessary token table: ; timertokens: .byte "SUNDAY",0,"MONDAY",0,"TUESDAY",0,"WEDNESDAY",0 .byte "THURSDAY",0,"FRIDAY",0,"SATURDAY",0 nevertoken: .byte "NEVER",0 oncetoken: .byte "ONCE",0 hourlytoken: .byte "HOURLY",0 dailytoken: .byte "DAILY",0 monthlytoken: .byte "MONTHLY",0 yearlytoken: .byte "YEARLY",0 .byte "DURATION",0,0 ; First of all, shut off the alarm if it's going, and lock ; the interrupt routine out of the timer data structure. timer_cmd: mov alarmtimer,#0 setb P1.7 setb timerbusyflag ; Get the timer number. lcall skipspc jz timerno_absent mov R2,#' ' lcall getnumquiet jb badflag,timerno_bad mov a,R1 mov R5,a clr C subb a,#numtimers jc gettimerspec timerno_bad: mov dptr,#timerno_msg lcall prtstr clr timerbusyflag ljmp prompt timerno_absent: ljmp listtimers timerno_msg: .byte "Invalid timer number.",13,10,0 gettimerspec: lcall skipspc jz gts_showspec lcall getdate mov dptr,#timertokens lcall matchtoken jnz gottoken gts_bad: mov dptr,#btsmsg lcall prtstr clr timerbusyflag ljmp prompt gts_showspec: lcall showtimerspec lcall crlf clr timerbusyflag ljmp prompt btsmsg: .byte "Invalid time specification.",13,10,0 ; OK, got and decoded the first token, which determines ; what kind of entry it is. gottoken: cjne a,#8,notnever mov R4,#0 ljmp gts_common notnever: cjne a,#9,notonce ; Date format "ONCE". Get the full date. lcall skipspc jz once_bad lcall getdate_year jb badflag,once_bad mov R4,#1 ljmp gts_common once_bad: mov dptr,#ydatemsg push DPH push DPL mov dptr,#oncetoken abort_common: push DPH push DPL mov dptr,#invalidmsg lcall prtstr pop DPL pop DPH lcall prtstr mov a,#' ' lcall putchar pop DPL pop DPH lcall prtstr clr timerbusyflag ljmp prompt notonce: cjne a,#10,nothourly ; Date format "HOURLY". Get the minute. lcall skipspc jz hourly_bad lcall getdate_minute jb badflag,hourly_bad mov R4,#2 ljmp gts_common hourly_bad: mov dptr,#midatemsg push DPH push DPL mov dptr,#hourlytoken sjmp abort_common nothourly: cjne a,#11,notdaily ; Date format "DAILY". lcall skipspc jz daily_bad lcall getdate_hour jb badflag,daily_bad mov R4,#3 ljmp gts_common daily_bad: mov dptr,#hdatemsg push DPH push DPL mov dptr,#dailytoken sjmp abort_common notdaily: cjne a,#12,notmonthly ; Date format "MONTHLY" lcall skipspc jz monthly_bad lcall getdate_day jb badflag,monthly_bad mov R4,#4 ljmp gts_common monthly_bad: mov dptr,#ddatemsg push DPH push DPL mov dptr,#monthlytoken sjmp abort_common notmonthly: cjne a,#13,notyearly ; Date format "YEARLY" lcall skipspc jz yearly_bad lcall getdate_month jb badflag,yearly_bad mov R4,#5 ljmp gts_common yearly_bad: mov dptr,#modatemsg push DPH push DPL mov dptr,#yearlytoken ljmp abort_common notyearly: cjne a,#14,notduration ljmp gotdurtoken ; If we get this far, it's one of tokens 1-7 which are ; the weekdays. Gather more weekday name tokens and build ; up a bitmap of which ones were seen. notduration: mov r4,#0x80 mov r0,a merge_wkday: clr a setb c merge_loop: rlc a djnz r0,merge_loop orl a,r4 mov r4,a lcall skipspc jz weekly_bad mov dptr,#timertokens lcall matchtoken jz wkdays_done mov r0,a clr C subb a,#8 jc merge_wkday weekly_bad: mov dptr,#hdatemsg push DPH push DPL mov dptr,#wlstring ljmp abort_common wlstring: .byte "",0 wkdays_done: lcall getdate_hour jb badflag,weekly_bad ; Arrive here from all the branches. The "date_copy" ; variables contain the current date, with varying amounts ; overwritten starting at the low end. R4 contains the ; mode byte. The buffer pointer is just past the end of ; the timer specificiation. R5 contains the timer number. gts_common: lcall gettimeraddr lcall puttentry ; Must still check for the "event" token. gts_checkevent: lcall skipspc jz gts_stash30 mov dptr,#timertokens lcall matchtoken clr C subb a,#14 jz gotdurtoken ljmp gts_bad gotdurtoken: lcall skipspc jz gts_stash30 mov R2,#' ' lcall getnum mov a,R1 sjmp gts_stash gts_stash30: mov a,#30 gts_stash: push ACC lcall gettimeraddr mov a,#7 add a,DPL mov DPL,a jnc gts_nocar inc DPH gts_nocar: pop ACC movx @dptr,a clr timerbusyflag ljmp prompt ; Subroutine to copy a timer entry from R4 and the ; date_copy variables to the timer table entry pointed ; at by DPTR. puttentry: mov a,R4 movx @dptr,a mov R0,#6 mov R1,#year_copy settimer_loop: inc dptr mov a,@r1 movx @dptr,a inc r1 djnz r0,settimer_loop ret ; Compute the address of a timer table entry. gettimeraddr: mov dptr,#timertbl mov B,#tentrysize mov a,r5 mul ab add a,DPL mov DPL,a mov a,B addc a,DPH mov DPH,a ret ; Subroutine to copy a timer table entry from the location ; pointed to by DPTR to R4 and the date_copy variables. gettentry: movx a,@dptr mov R4,a mov R0,#6 mov R1,#year_copy show_loop1: inc dptr movx a,@dptr mov @r1,a inc r1 djnz r0,show_loop1 lcall checkstack ret ; Enter here to display a timer table entry given its ; number in R5. This should display something that ; fits in 40 characters so it can be used on both the ; command line interface and the LCD. showtimerspec: lcall gettimeraddr showts2: lcall gettentry cjne R4,#0,notnever1 mov dptr,#nevertoken ljmp prtstr ; Having taken care of the "NEVER" case, we display the ; "next activation time" as nicely as we can. notnever1: lcall prttime mov a,#' ' lcall putchar lcall compute_day lcall printwday mov a,#' ' lcall putchar lcall printdate ; Followed by the "repeat interval". cjne R4,#1,st2_notonce mov dptr,#oncetoken sjmp st2_showint st2_notonce: cjne R4,#2,st2_nothourly mov dptr,#hourlytoken sjmp st2_showint st2_nothourly: cjne R4,#3,st2_notdaily mov dptr,#dailytoken sjmp st2_showint st2_notdaily: cjne R4,#4,st2_notmonthly mov dptr,#monthlytoken sjmp st2_showint st2_notmonthly: cjne R4,#5,st2_notyearly mov dptr,#yearlytoken st2_showint: mov a,#' ' lcall putchar mov a,#'(' lcall putchar lcall prtstr st2_showdone: mov a,#')' ljmp putchar ; The weekly case is special, as usual. st2_notyearly: mov a,#' ' lcall putchar mov a,#'(' lcall putchar mov R3,4 mov R2,#7 mov dptr,#wlettbl st2_wkloop: mov a,R3 rrc a mov R3,a mov a,#'_' jnc st2w_printit movx a,@dptr st2w_printit: lcall putchar inc dptr djnz r2,st2_wkloop sjmp st2_showdone wlettbl: .byte "SMTWTFS" ;----------------------------------------------------------------------- ; This function runs at interrupt level, once a second. my1secaddr: push PSW push ACC push DPL push DPH mov PSW,#0x08 mov a,redialtimer jz rdt_done dec redialtimer rdt_done: mov a,alarmtimer jz alarm_done dec alarmtimer mov c,ACC.0 mov P1.7,c ; Now scan all timer entries, looking for ones which ; have expired. But only if the timer table is not ; being modified at the moment. alarm_done: jnb timerbusyflag,timer_doit ljmp dt_done timer_doit: mov dptr,#timertbl mov r1,#numtimers do_t_entry: mov R6,DPH mov R7,DPL movx a,@dptr jz dt_skip8 inc dptr movx a,@dptr clr C subb a,year jc dt_now jnz dt_skip7 inc dptr movx a,@dptr subb a,month jc dt_now jnz dt_skip6 inc dptr movx a,@dptr subb a,day jc dt_now jnz dt_skip5 inc dptr movx a,@dptr subb a,hours jc dt_now jnz dt_skip4 inc dptr movx a,@dptr subb a,minutes jc dt_now jnz dt_skip3 inc dptr movx a,@dptr subb a,seconds jc dt_now jnz dt_skip2 dt_now: mov dptr,#dpsave mov a,R6 movx @dptr,a inc dptr mov a,R7 movx @dptr,a mov DPH,R6 mov DPL,R7 mov a,#7 movc a,@a+dptr mov R0,a clr c subb a,alarmtimer jc dt_notalarm mov alarmtimer,R0 ; All done. It only remains to bump the timer. ; Gross: All sorts of stuff needs to be saved first. dt_notalarm: mov dptr,#isr_reg_save mov a,B movx @dptr,a push 9 ; R1 mov R0,#6 mov R1,#year_copy dtrs_loop: mov a,@r1 inc r1 inc dptr movx @dptr,a djnz r0,dtrs_loop mov DPH,R6 mov DPL,R7 ;clr a ;movx @dptr,a lcall bumptentry mov dptr,#isr_reg_save movx a,@dptr mov B,a mov R0,#6 mov R1,#year_copy dtrr_loop: inc dptr movx a,@dptr mov @r1,a inc r1 djnz r0,dtrr_loop pop 9 ; R1 mov DPH,R6 mov DPL,R7 dt_skip8: inc dptr dt_skip7: inc dptr dt_skip6: inc dptr dt_skip5: inc dptr dt_skip4: inc dptr dt_skip3: inc dptr dt_skip2: inc dptr dt_skip1: inc dptr djnz R1,do_t_entry_l dt_done: pop DPH pop DPL pop ACC pop PSW ret do_t_entry_l: ljmp do_t_entry ;----------------------------------------------------------------------- ; This function takes a timer entry, address in DPTR, and "bumps" it up ; by one iteration. For the periodic ones that means advancing it to ; the next trigger date. For the "once" one it means cancelling it. ; ; Function to bump a timer table entry up by one. bumptentry: push DPL push DPH lcall gettentry mov a,R4 jz bump_putback cjne R4,#1,b_notonce mov R4,#0 sjmp bump_putback ; ONCE --> NEVER b_notonce: cjne R4,#2,b_nothourly inc hours_copy mov a,#24 cjne a,hours_copy,bump_putback mov hours_copy,#0 sjmp bump_day2 b_nothourly: cjne R4,#3,b_notdaily bump_day2: lcall bump_day sjmp bump_putback b_notdaily: cjne R4,#4,b_notmonthly ; For the "monthly" ones we will happily bump the date up ; to an invalid one, e.g. from January 31 to February 31. ; Why? Then a monthly event scheduled for the 31st, ; rather than not happening, will simply happen on the 1st ; of the following month for months not having a 31st. ; Much preferable. lcall bump_month sjmp bump_putback b_notmonthly: cjne R4,#5,b_notyearly lcall bump_year sjmp bump_putback ; Must be weekly. This one is more complicated. b_notyearly: lcall compute_day mov R3,a b_findday: lcall bump_day inc r3 cjne r3,#7,bf_notwrap mov r3,#0 bf_notwrap: mov r2,11 ; interrupt register 3 inc r2 clr a setb c bf_floop: rlc a djnz r2,bf_floop anl a,r4 jz b_findday bump_putback: pop DPH pop DPL ljmp puttentry ; And here is the actual "bump" routine. Enter at the ; appropriate date component, and it adds that with ; carry into higher order ones. bump_hour: inc hours_copy mov a,#24 cjne a,hours_copy,bump_done mov hours_copy,#0 bump_day: inc day_copy mov a,month_copy mov r0,year lcall daysthismonth subb a,day_copy jnc bump_done mov day_copy,#1 bump_month: inc month_copy mov a,#13 cjne a,month_copy,bump_done mov month_copy,#1 bump_year: inc year_copy bump_done: ret ; Function to list the timer table. listtimers: mov R5,#0 clr badflag ltloop: lcall gettimeraddr movx a,@dptr jz ltloopbot jb badflag,haveheading mov dptr,#ltheading lcall prtstr setb badflag haveheading: mov R1,#' ' mov R2,#2 mov a,R5 lcall printnum mov a,#5 lcall tab lcall showtimerspec mov a,#40 lcall tab lcall gettimeraddr mov a,#7 add a,DPL mov DPL,a jnc lt_nocar inc DPH lt_nocar: mov a,#' ' lcall putchar movx a,@dptr mov r1,#' ' mov r2,#0 lcall printnum lcall crlf ltloopbot: inc R5 cjne R5,#numtimers,ltloop clr timerbusyflag ljmp prompt ltheading: .byte "NO. TRIGGER TIME/REPEAT SCHEDULE DURATION" .byte 13,10,13,10,0 ;----------------------------------------------------------- ; 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 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" ; Retrieve the date, and arrange it in the format used by ; these routines. getdate: setb date_get_flag w_getdate: jb date_get_flag,w_getdate mov a,year_copy xch a,hours_copy xch a,day_copy xch a,seconds_copy mov year_copy,a mov a,month_copy xch a,minutes_copy mov month_copy,a ret ; Week Day Computer ; ----------------- ; ; Number the weekdays 0 (Sunday) through 6 (Saturday) ; January 1, 1977 was a Saturday. ; Each multiple of 4 years since then added 5 days ; (modulo 7). Each single year left over added one day. compute_day: mov a,year_copy clr C subb a,#77 mov B,#4 div ab push B mov B,#5 mul ab pop B add a,B dec a mov R0,a ; Now R0 contains the day for January 1 of the year. Next, ; look up the day offset for the month and add that in. mov a,month_copy mov dptr,#motbl-1 movc a,@a+dptr add a,R0 mov R0,a ; Re-check the year and bump the day by one if we're past ; February in a leap year. mov a,year_copy anl a,#3 jnz notleap mov a,month_copy clr C subb a,#3 jc notleap inc R0 ; Finally, add in the day minus 1 and take modulo 7. notleap: mov a,day_copy dec R0 add a,R0 mov B,#7 div ab mov a,B ret motbl: .byte 0,3,3,6,1,4,6,2,5,0,3,5 ;--------------------------------------------------------------------------- ; If the download went OK, link the new code into the ; data switch firmware. .org 2203h ljmp decodecmd .org 2201h .word isr ; Interrupt address first .org 2200h .byte 0x02 ; Jump instruction next.