;: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 .flag quietflag,0x20.0 .flag badflag,0x20.1 .flag xoffflag,0x20.2 .flag freq38,0x20.3 .flag bailoutflag,0x20.4 .flag attnflag,P1.2 .org 0x28 tabpos: .skip 1 bufptr: .skip 1 keycode: .skip 1 pausecount: .skip 1 ; External memory allocation .equ bufsize,127 .equ bufpage,0x82 .org 0x100*bufpage inbuf: .skip bufsize+1 ; Define the memory range to be used by the IR buffer. ; Note that the page "irbufendpg" is not used but due ; to the simple overrun checking, may have the first four ; bytes written to. .equ irbufstartpg,0xa0 .equ irbufendpg,0xff .equ il_dptrsave,0xff04 .equ gkscode,25 .org 0x9f00 ljmp gloop ljmp gotchar gotcarrier: .skip 3 ljmp puts ljmp printhex2 .org 0x8400 ; CPU initialization ; ------------------ ; Initialize CPU registers. start: mov SP,#0x2f ; Stack at 30H mov PSW,#0 ; Registers at 0H mov TCON,#0 ; Disable timers mov IE,#0 ; Disable all interrupts mov IP,#0 mov PCON,#0 ; Initialize other registers. mov P1,#0xff mov P3,#0xff ; Serial port initialization ; -------------------------- ; Set both timers to free running, 8 bit auto reload. mov TMOD,#0x22 ; Start baud rate generator and initialize serial port. mov TH1,#0xfd setb TCON.6 mov SCON,#0x52 clr xoffflag setb freq38 setb attnflag mov keycode,#0 lcall initirtable ;--------------------------------------------------------------------------- ; Module: hostmode ; ; Description: ; This is the top level command entry point. It prints a prompt, waits ; for an input string, decodes the command, and jumps to the appropriate ; command routine. ; ; Inputs: n/a ; Outputs: n/a mov tabpos,#0 prompt: mov a,#'R' lcall putchar mov a,#'>' lcall putchar lcall getline lcall skipspc jz prompt ; Decode the first word of the input string, and print an ; error message if it does not match any of the commands. mov dptr,#cmdtbl lcall matchtoken jnz got_cmd mov dptr,#invalidcmd lcall puts sjmp prompt ; Dispatch a command by executing the appropriate entry from ; the jump table. got_cmd: mov B,#3 mul ab mov dptr,#jmptbl-3 jmp @a+dptr ; Command jump table jmptbl: ljmp help_cmd ljmp help_cmd ljmp rsend_cmd ljmp rlist_cmd ljmp dump_cmd ljmp rdel_cmd ljmp rstore_cmd ljmp freq_cmd ; Error message invalidcmd: .byte "Invalid command.",13,10,0 ; Command table cmdtbl: .byte "?",0,"HELP",0 .byte "SEND",0,"LIST",0,"DUMP",0,"DELETE",0 .byte "LEARN",0,"FREQUENCY",0,0 ;--------------------------------------------------------------------------- ; freq_cmd: lcall skipspc jz showfreq mov dptr,#f38str lcall matchtoken jnz gotfrq mov dptr,#fsyntaxstr lcall puts ljmp prompt ; A is 1 or 2. Just copy the appropriate bit into the ; 38KHz flag. gotfrq: mov C,ACC.0 mov freq38,C ljmp prompt showfreq: mov dptr,#sfstr lcall puts mov C,freq38 lcall decodefreq mov dptr,#sfstr2 lcall puts ljmp prompt sfstr: .byte "Sample/carrier frequency is ",0 sfstr2: .byte " KHz",13,10,0 fsyntaxstr: .byte "Only 38.40 and 40.07 are possible.",13,10,0 decodefreq: mov dptr,#f38str jc dfprintit mov dptr,#f40str dfprintit: ljmp puts ; Note: This does double duty as a string table for ; display, and a token match table for input. f38str: .byte "38.40",0 f40str: .byte "40.07",0 .byte 0 ;--------------------------------------------------------------------------- ; dump_cmd: lcall skipspc jz dump_syntax mov R2,#0 lcall getnum jb badflag,dump_exit mov R0,1 lcall findircode jb badflag,dump_notfound push DPH push DPL mov R0,#7 dhloop: mov dptr,#dhmsg lcall puts djnz R0,dhloop lcall crlf lcall crlf pop DPL pop DPH dhlineloop: mov R1,#14 dhentryloop: movx a,@dptr inc dptr mov R2,a movx a,@dptr inc dptr mov R3,a orl a,R2 jz dump_alldone mov a,R2 lcall printhex2 mov a,R3 lcall printhex2 mov a,#' ' lcall putchar djnz R1,dhentryloop lcall crlf sjmp dhlineloop dump_alldone: mov a,R1 clr C subb a,#14 jz dump_exit lcall crlf sjmp dump_exit dump_notfound: mov dptr,#rsumsg sjmp dump_putmsg dump_syntax: mov dptr,#rssmsg dump_putmsg: lcall puts dump_exit: ljmp prompt dhmsg: .byte "ON OFF ",0 ;--------------------------------------------------------------------------- ; Command packet routine. Gets entered when a ^A is received on the ; serial port. Gets next character and jumps to appropriate function. dopkt: lcall cgetchar jb badflag,cp_exit ; Commands start at 1 and go up to "maxcmd". jz cp_exit mov B,a clr C subb a,#maxcmd+1 jnc cp_exit mov a,#3 mul ab mov dptr,#pktjmptbl-3 jmp @a+dptr cp_exit: ret pktjmptbl: ljmp psendircode ; 1 Send IR code ljmp pstoreircode ; 2 Store IR code ljmp pdelircode ; 3 Delete IR code ljmp pgetinfo ; 4 Get IR code info ljmp pgetfree ; 5 Get free memory ljmp pgetfreq ; 6 Get sample freq. ljmp psetfreq ; 7 Set sample freq. .equ maxcmd,7 ; Get/set the 38KHz frequency flag. pgetfreq: clr A mov C,freq38 mov ACC.0,C ljmp sendreplybyte psetfreq: lcall cgetchar mov C,ACC.0 mov freq38,C ret ; Send an IR code. Get the number, then do it. psendircode: lcall cgetchar jb badflag,psi_exit mov R0,a lcall playircode ; "Badflag" is set if the IR code wasn't known, otherwise ; it is clear. We send a 1-byte reply containing ; the flag in the low bit. replybadflag: clr A mov C,badflag mov ACC.0,C lcall sendreplybyte psi_exit: ret ; Store an IR code. Very similar. pstoreircode: lcall cgetchar jb badflag,psi_exit mov R0,a lcall recordircode sjmp replybadflag ; Delete an IR code. Again very similar except that ; there is no flag. pdelircode: lcall cgetchar jb badflag,psi_exit mov R0,a lcall delircode clr badflag sjmp replybadflag ; Get information about an IR code. Takes code number, ; returns flag byte first. If "endflag" set in flag ; byte, code does not exist. Otherwise length of code ; follows in next two bytes. pgetinfo: lcall cgetchar jb badflag,pgi_exit mov R0,a lcall irgetinfo jnb badflag,pgi_found mov a,#1 ljmp sendreplybyte pgi_found: mov a,R0 lcall sendreplybyte jb badflag,pgi_exit pgi_sendsize: mov a,R3 lcall sendreplybyte jb badflag,pgi_exit mov a,R2 lcall sendreplybyte pgi_exit: ret ; Get the amount of free memory. Takes no arguments ; and returns the amount as two bytes. pgetfree: lcall irgetfree sjmp pgi_sendsize ;--------------------------------------------------------------------------- ; Routine to send reply bytes. Each reply byte must be acknowledged by ; a character coming back. That is so we don't outrun the other side, ; which may be bogged down by interrupts and event handling. sendreplybyte: jnb SCON.1,sendreplybyte mov SBUF,A clr SCON.1 ; Routine to receive command packet bytes. Does not strip high bit or ; handle XON/XOFF. Sets "badflag" if a timeout occurs. cgetchar: push 0 mov R0,#0 cget3: clr A cget4: jb SCON.0,cget2 dec A jnz cget4 djnz R0,cget3 pop 0 setb badflag ret cget2: mov a,SBUF clr SCON.0 clr badflag pop 0 ret ;--------------------------------------------------------------------------- ; Function: help_cmd ; ; Description: ; This function provides the processing for the "help" command. It ; displays a summary of all the switch commands on the terminal. ; Maintenance commands are only shown if the user is in maintenance mode. ; ; Command syntax: HELP ; ? ; ; Inputs: None ; Outputs: None ; help_cmd: mov dptr,#helptbl lcall puts ljmp prompt ; Help for user commands. helptbl: .byte 13,10,"Help goes here.",13,10,0 ;--------------------------------------------------------------------------- ; Command line interface to the IR remote control routines. rstore_cmd: lcall skipspc jz rstore_syntax mov R2,#0 lcall getnum jb badflag,rstore_exit mov R0,1 lcall recordircode jnb badflag,rstore_exit mov dptr,#rsmmsg sjmp rstore_prtmsg rstore_syntax: mov dptr,#rssmsg rstore_prtmsg: lcall puts rstore_exit: ljmp prompt rsend_cmd: lcall skipspc jz rstore_syntax mov R2,#0 lcall getnum jb badflag,rstore_exit mov R0,1 lcall playircode jnb badflag,rstore_exit mov dptr,#rsumsg sjmp rstore_prtmsg rdel_cmd: lcall skipspc jz rstore_syntax mov R2,#0 lcall getnum jb badflag,rstore_exit mov R0,1 lcall delircode ljmp prompt rssmsg: .byte "Missing code number.",13,10,0 rsmmsg: .byte "Not stored, out of memory.",13,10,0 rsumsg: .byte "Unknown code.",13,10,0 rlhdmsg: .byte "CODE LENGTH FREQ",13,10,13,10,0 rlist_cmd: mov R0,#0 lcall irlist ; If R0 already has the end flag set, skip some things. mov a,R0 jb ACC.0,rlist_end2 mov dptr,#rlhdmsg lcall puts rlist_entry: mov a,R0 jb ACC.0,rlist_end push ACC push 2 push 3 mov a,R1 mov R4,1 mov R1,#' ' mov R2,#3 lcall printnum mov a,#7 lcall tab pop ACC lcall printhex2 pop ACC lcall printhex2 mov a,#14 lcall tab pop ACC mov C,ACC.1 lcall decodefreq lcall crlf mov R0,#1 lcall irlist sjmp rlist_entry rlist_end: lcall crlf rlist_end2: mov a,R3 lcall printhex2 mov a,R2 lcall printhex2 mov dptr,#rlbfstr lcall puts ljmp prompt rlbfstr: .byte " bytes free.",13,10,0 ;--------------------------------------------------------------------------- ; 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 ;----------------------------------------------------------------------- ; Initialize IR Storage Table ; --------------------------- ; The table consists of entries of this form: ; Byte 0: Flags ; Bit 0: End marker (1 if last). ; Bit 1: 38.4KHz flag (otherwise 40.07KHz) ; Byte 1: IR code ID ; Bytes 2-end : Stored IR code in irsample/irsend format. initirtable: mov dptr,#irbufstartpg*256 mov a,#1 movx @dptr,a ret ; List IR codes ; ------------- ; First call has R0 = 0, subsequent calls R0 nonzero. ; Steps through the table entries, and for each call ; returns data about one of them. ; Data returned: ; R0 - Flags ; R1 - ID ; R3/R2 - Length of entry in bytes ; Last call returns the end flag (bit 0 set) in R0, ; and the amount of free memory in R3/R2. irlist: cjne R0,#0,il_notfirst mov dptr,#irbufstartpg*256 sjmp il_common il_notfirst: mov dptr,#il_dptrsave movx a,@dptr mov R0,a inc dptr movx a,@dptr mov DPL,a mov DPH,R0 il_common: movx a,@dptr mov R0,a jb ACC.0,il_last inc dptr movx a,@dptr mov R1,a inc dptr mov R2,#2 mov R3,#0 il_count: mov a,R2 add a,#2 mov R2,a jnc il_nocar1 inc R3 il_nocar1: movx a,@dptr mov B,a inc dptr movx a,@dptr inc dptr orl a,B jnz il_count ; Entry length is counted up in R3/R2, other registers are ; appropriately loaded, need only stash DPTR for next ; time. push DPL push DPH mov dptr,#il_dptrsave pop ACC movx @dptr,a inc dptr pop ACC movx @dptr,a ret ; Have just read the end flag, and DPTR still points to ; it. R0 is loaded. Compute the amount of free memory ; in R3/R2. il_last: inc dptr inc dptr mov a,#0 clr C subb a,DPL mov R2,a mov a,#irbufendpg subb a,DPH mov R3,a ret ; Get info about an IR code ; ------------------------- irgetinfo: lcall findircode jb badflag,gi_end mov DPH,R2 mov DPL,R1 ljmp il_common gi_end: ret ; Get the amount of free memory ; ----------------------------- irgetfree: mov R0,#0 igf_loop: lcall irlist mov a,R0 jb ACC.0,igf_done mov R0,#1 sjmp igf_loop igf_done: ret ; Find an IR code ; --------------- ; Very unsophisticated. Simply traverse the table ; until either the ID matches or the end is found. ; Input: R0 -- IR code ID. ; Output: DPTR -- pointer to first byte of IR sample ; data (first byte after ID). ; R2/R1 -- value in DPH/DPL minus 2 (pointer ; to first byte in entry). ; badflag -- set if code was not found. ; If the code is not found, DPTR must point to the ; end marker. findircode: mov dptr,#irbufstartpg*256 findloop: movx a,@dptr jnb ACC.0,fnotend setb badflag ret fnotend: mov R2,DPH mov R1,DPL inc dptr movx a,@dptr inc dptr cjne a,0,fskip clr badflag ret ; Skip the entry by reading 16-bit numbers until a 0 ; is read. fskip: movx a,@dptr inc dptr mov R1,a movx a,@dptr inc dptr orl a,R1 jnz fskip ljmp findloop ; Delete an IR code ; ----------------- ; Delete the IR code in R0 from the table by moving all ; entries above it down. ; "badflag" is set if the entry didn't exist in the ; first place. ; DPTR must point to the end marker on exit. ; First, find the code. Leaves DPTR set appropriately. delircode: lcall findircode jnb badflag,dskip ret ; Now R2/R1 points to the start of the code we want to ; delete. First we advance DPTR to the start of the ; next code. dskip: movx a,@dptr inc dptr mov R0,a movx a,@dptr inc dptr orl a,R0 jnz dskip ; Now we copy entries from @DPTR to @R2R1 until the ; end of the table is reached (the first byte of the ; entry copied has bit 0 set). copyentry: movx a,@dptr ; Get the flags byte lcall wr2r1 jnb ACC.0,c_notdone ; R2R1 points one past the copied end marker. Must ; load DPTR with the address of the end marker. mov a,R1 clr C subb a,#1 mov DPL,a mov a,R2 subb a,#0 mov DPH,a ret c_notdone: inc dptr movx a,@dptr ; Get the ID byte lcall wr2r1 inc dptr c_copyloop: movx a,@dptr inc dptr lcall wr2r1 mov R0,a movx a,@dptr inc dptr lcall wr2r1 orl a,R0 jnz c_copyloop ; We have copied a 0000 entry, so the DPTR now points ; at the start of the next entry and it's time to check ; for end of table again. sjmp copyentry ; Subroutine to write a byte at @R2R1 and increment ; the address R2R1. Note A is unmodified. wr2r1: mov P2,R2 movx @R1,a inc R1 cjne R1,#0,wr2_exit inc R2 wr2_exit: ret ; Play an IR code ; --------------- ; The "badflag" is set if the IR code is unknown. playircode: lcall findircode jnb badflag,playit ret ; Get the 38KHz flag and call the appropriate playback ; routine. playit: mov P2,R2 movx a,@R1 jb ACC.1,play38 ljmp irsend play38: ljmp irsend38 ; Add an IR code to the table ; --------------------------- recordircode: push 0 ; Scrap any previously recorded version of this code, ; and find the table end marker. lcall delircode ; Stash the start address of the to-be-added entry. mov R2,DPL mov R3,DPH ; Record the new flag byte and ID byte. clr A mov C,freq38 mov ACC.1,C movx @dptr,a inc dptr pop ACC movx @dptr,a inc dptr ; Check if we've overrun the buffer yet. cjne A,#irbufendpg,recordit ; Seems that we have overrun the buffer. ; Restore the table end marker to where it was and ; return with the "badflag" set. r_overrun: mov DPL,R2 mov DPH,R3 mov a,#1 movx @dptr,a setb badflag ret ; Record the IR code in the buffer. Note that ; no more than 4 bytes will be stored if the buffer ; is overrun. recordit: lcall irsample ; If we've overrun the buffer, scrap the IR code and exit. jb badflag,r_overrun ; Note that the IR sample routine will not set the ; badflag if all that overran the buffer is the "end ; of sample" marker. Now stash the "end of list" ; marker and then check for overrun (max. 3). mov a,#1 movx @dptr,a mov a,DPH cjne a,#irbufendpg,r_ok sjmp r_overrun r_ok: clr badflag ret ;----------------------------------------------------------------------- ; Infrared Sample Routine ; ----------------------- ; Captures an IR command. ; Input: DPTR -- address to store at. ; ; Returns: DPTR -- last address used + 1 ; badflag -- if set, IR command didn't fit ; in the buffer. ; With a clock speed of 11.0592MHz, this samples at ; 40.07 KHz (one sample every 23 instruction cycles). ; Later addition: If the flag "freq38" is set, then ; insert an extra cycle in various places to reduce the ; sampling rate to 38.4KHz. ; Initialize the "on" counter. irsample: mov R0,#0 ; 1 mov R1,#0 ; 1 ; Wait for the initial "on" transition. waitfirst: jb P1.0,waitfirst ; 2 ..2 ; Now add up 23-cycle loop iterations until the input ; goes "off". waiton: nop ; 1 ..1 nop ; 1 ..2 nop ; 1 ..3 nop ; 1 ..4 jnb freq38,skipnop1 ; 2 ..6 nop ; 1 EXTRA skipnop1: nop ; 1 ..7 nop ; 1 ..8 nop ; 1 ..9 nop ; 1 ..10 nop ; 1 ..11 nop ; 1 ..12 nop ; 1 ..13 nop ; 1 ..14 nop ; 1 ..15 mov a,R0 ; Increment count 1 ..16 add a,#1 ; 1 ..17 mov R0,a ; 1 ..18 mov a,R1 ; 1 ..19 addc a,#0 ; 1 ..20 mov R1,a ; 1 ..21 onloopbot: jnb P1.0,waiton ; Loop until off 2 ..23 ; It's off. Store the "on" count. mov a,R1 ; 1 ..1 movx @dptr,a ; 2 ..3 inc dptr ; 2 ..5 mov a,R0 ; 1 ..6 movx @dptr,a ; 2 ..8 inc dptr ; 2 ..10 ; Initialize the "off" count at 1 to account for the cycles ; we are now wasting. mov R1,#0 ; 1 ..11 mov R0,#1 ; 1 ..12 ; Waste cycles to bring us up to ..21, then jump into ; the loop just above the end as if a normal first ; iteration had happened. nop ; 1 ..13 nop ; 1 ..14 jnb freq38,skipnop2 ; 2 ..16 nop ; 1 EXTRA skipnop2: nop ; 1 ..17 nop ; 1 ..18 nop ; 1 ..19 sjmp offloopbot ; 2 ..21 ; The "wait while off" loop is just like the "wait while ; on" loop, except that we check for counter overflow. ; When the counter overflows, there have been more than ; 1.5 seconds of "off" and the code has definitely finished. waitoff: mov a,R0 ; 1 ..1 orl a,R1 ; 1 ..2 jz code_done ; 2 ..4 nop ; 1 ..5 nop ; 1 ..6 jnb freq38,skipnop3 ; 2 ..8 nop ; 1 EXTRA skipnop3: nop ; 1 ..9 nop ; 1 ..10 nop ; 1 ..11 nop ; 1 ..12 nop ; 1 ..13 nop ; 1 ..14 nop ; 1 ..15 mov a,R0 ; Increment count 1 ..16 add a,#1 ; 1 ..17 mov R0,a ; 1 ..18 mov a,R1 ; 1 ..19 addc a,#0 ; 1 ..20 mov R1,a ; 1 ..21 offloopbot: jb P1.0,waitoff ; Loop until on 2 ..23 ; It's on. Store the "off" count. mov a,R1 ; 1 ..1 movx @dptr,a ; 2 ..3 inc dptr ; 2 ..5 mov a,R0 ; 1 ..6 movx @dptr,a ; 2 ..8 inc dptr ; 2 ..10 ; Return with the "badflag" set if storing the "off" ; count has advanced the buffer pointer out of the buffer. mov a,#irbufendpg ; 1 ..11 cjne a,DPH,notoverflow ; 2 ..13 setb badflag ret ; Initialize the "on" count at 1 to account for the cycles ; we are now wasting. notoverflow: mov R1,#0 ; 1 ..14 mov R0,#1 ; 1 ..15 ; Waste cycles to bring us up to ..21, then jump into ; the loop just above the end as if a normal first ; iteration had happened. jnb freq38,skipnop4 ; 2 ..17 nop ; 1 EXTRA skipnop4: nop ; 1 ..18 nop ; 1 ..19 sjmp onloopbot ; 2 ..21 ; Finish the code by storing an "off" time of 0. code_done: clr a movx @dptr,a inc dptr movx @dptr,a inc dptr clr badflag ret ;----------------------------------------------------------------------- ; Infrared Playback Routine ; ------------------------- ; Plays back a stored IR command at a carrier frequency ; of 40.07KHz (with a 11.0592MHz processor clock). ; Input: DPTR -- pointer to the stored command. ; The remote control code is stored in external memory ; pointed to by DPTR. It consists of 16-bit numbers. ; The first one is an "on" time, the next one an "off" ; time, then another "on" time, etc. When an "off" time ; is 0, it marks the end of the code. The "on" time must ; be at least 1. ; The light/dark times are in units of 23 machine cycles. irsend: movx a,@dptr ; Fetch 'on' time. 2 ..38/15 mov R1,a ; 1 ..39/16 inc dptr ; 2 ..41/18 movx a,@dptr ; 2 ..43/20 mov R0,a ; 1 ..44/21 inc dptr ; 2 ..46/23 ; So far, we have had 10 cycles worth of "off". Now send ; out 23 * the "on" number cycles of 40KHz burst. ; Send out 256*R1+R0 cycles of IR pulses at 40KHz. ; Closest approximation with 11.0592MHz clock is ; 23 machine cycles per IR light pulse. loop40khz: clr P1.1 ; Turn light on 1 ..1 mov a,R0 ; Decrement counter 1 ..2 clr C ; 1 ..3 subb a,#1 ; 1 ..4 mov R0,a ; 1 ..5 mov a,R1 ; 1 ..6 subb a,#0 ; 1 ..7 mov R1,a ; 1 ..8 nop ; Padding 1 ..9 nop ; 1 ..10 nop ; 1 ..11 nop ; 1 ..12 setb P1.1 ; Turn light off 1 ..13 nop ; Padding 1 ..14 nop ; 1 ..15 nop ; 1 ..16 nop ; 1 ..17 nop ; 1 ..18 nop ; 1 ..19 nop ; 1 ..20 orl a,R0 ; Check counter 1 ..21 jnz loop40khz ; 2 ..23 ; Now fetch the "off" time. Trickery is required here ; to be able to loop back in time to meet the 23 cycle ; requirement if the count is 1. movx a,@dptr ; 2 ..2 jnz highset ; 2 ..4 inc dptr ; 2 ..6 movx a,@dptr ; 2 ..8 inc dptr ; 2 ..10 ; We're out 10 machine cycles of "off". If the "off" ; value is 1, we'd better loop in time to meet the 23 ; cycle requirement. dec a ; 1 ..11 jz irsend ; 2 ..13 ; OK, so the count is not 1, and we're out 13 machine ; cycles anyway. inc a ; 1 ..14 mov R0,a ; 1 ..15 mov R1,#0 ; 1 ..16 sjmp not1common ; 2 ..18 ; When we arrive from the preceeding jump, we're out 18. ; We're out 4 cycles so far. highset: inc dptr ; 2 ..6 mov R1,a ; 1 ..7 movx a,@dptr ; 2 ..9 mov R0,a ; 1 ..10 inc dptr ; 2 ..12 ; Out 12 cycles. Waste six in order to be out 18 cycles ; as if we arrived the other way. nop ; 1 ..13 nop ; 1 ..14 nop ; 1 ..15 nop ; 1 ..16 nop ; 1 ..17 nop ; 1 ..18 ; Arrive here with the low byte of the count in A, and ; R0 and R1 loaded. not1common: orl a,R1 ; 1 ..19 jz burstdone ; 2 ..21 ; The 0 and 1 cases are taken care of. Bump R1/R0 down ; by 2. This accounts for the 46 cycles of computation ; we are doing. mov a,R0 ; 1 ..22 clr C ; 1 ..23 subb a,#2 ; 1 ..24 mov R0,a ; 1 ..25 mov a,R1 ; 1 ..26 subb a,#0 ; 1 ..27 mov R1,a ; 1 ..28 ; OK, the count in R1/R0 is the number of 23 cycle units ; to waste after all the overhead, which must add up to ; 46 cycles. nop ; 1 ..29 nop ; 1 ..30 nop ; 1 ..31 orl a,R0 ; 1 ..32 jz lbottom ; 2 ..34 ; Waste 23 * (256*R1 + R0) machine cycles. Must start at ; time ..34 because the jump at the bottom takes another ; two cycles and the processing before the "on" burst takes ; ten. offloop: nop ; Waste cycles 1 nop ; 1 nop ; 1 nop ; 1 nop ; 1 nop ; 1 nop ; 1 nop ; 1 nop ; 1 nop ; 1 nop ; 1 nop ; 1 nop ; 1 mov a,R0 ; Decrement counter 1 clr C ; 1 subb a,#1 ; 1 mov R0,a ; 1 mov a,R1 ; 1 subb a,#0 ; 1 mov R1,a ; 1 orl a,R0 ; Check counter 1 jnz offloop ; 2 lbottom: ljmp irsend ; 2 ..36 burstdone: ret ; 38.4KHz Infrared Playback Routine ; --------------------------------- ; The logic in the previous routine is so convoluted that ; inserting conditional NOPs to bump the frequency down ; to 38.4KHz would have been a real pain. So here is the ; routine again, with unconditional NOPs instead. We just ; call one or the other. irsend38: movx a,@dptr ; Fetch 'on' time. 2 ..40/16 mov R1,a ; 1 ..41/17 inc dptr ; 2 ..43/19 movx a,@dptr ; 2 ..45/21 mov R0,a ; 1 ..46/22 inc dptr ; 2 ..48/24 loop38khz: clr P1.1 ; Turn light on 1 ..1 mov a,R0 ; Decrement counter 1 ..2 clr C ; 1 ..3 subb a,#1 ; 1 ..4 mov R0,a ; 1 ..5 mov a,R1 ; 1 ..6 subb a,#0 ; 1 ..7 mov R1,a ; 1 ..8 nop ; Padding 1 ..9 nop ; 1 ..10 nop ; 1 ..11 nop ; 1 ..12 setb P1.1 ; Turn light off 1 ..13 nop ; Padding 1 ..14 nop ; 1 ..15 nop ; 1 ..16 nop ; 1 ..17 nop ; 1 ..18 nop ; 1 ..19 nop ; 1 ..20 nop ; 1 ..21 orl a,R0 ; Check counter 1 ..22 jnz loop38khz ; 2 ..24 movx a,@dptr ; 2 ..2 jnz highset38 ; 2 ..4 inc dptr ; 2 ..6 movx a,@dptr ; 2 ..8 inc dptr ; 2 ..10 nop ; 1 ..11 dec a ; 1 ..12 jz irsend38 ; 2 ..14 inc a ; 1 ..15 mov R0,a ; 1 ..16 mov R1,#0 ; 1 ..17 sjmp not1common38 ; 2 ..19 highset38: inc dptr ; 2 ..6 mov R1,a ; 1 ..7 movx a,@dptr ; 2 ..9 mov R0,a ; 1 ..10 inc dptr ; 2 ..12 nop ; 1 ..13 nop ; 1 ..14 nop ; 1 ..15 nop ; 1 ..16 nop ; 1 ..17 nop ; 1 ..18 nop ; 1 ..19 not1common38: orl a,R1 ; 1 ..20 jz burstdone38 ; 2 ..22 mov a,R0 ; 1 ..23 clr C ; 1 ..24 subb a,#2 ; 1 ..25 mov R0,a ; 1 ..26 mov a,R1 ; 1 ..27 subb a,#0 ; 1 ..28 mov R1,a ; 1 ..29 nop ; 1 ..30 nop ; 1 ..31 nop ; 1 ..32 nop ; 1 ..33 orl a,R0 ; 1 ..34 jz lbottom38 ; 2 ..36 offloop38: nop ; Waste cycles 1 ..1 nop ; 1 ..2 nop ; 1 ..3 nop ; 1 ..4 nop ; 1 ..5 nop ; 1 ..6 nop ; 1 ..7 nop ; 1 ..8 nop ; 1 ..9 nop ; 1 ..10 nop ; 1 ..11 nop ; 1 ..12 nop ; 1 ..13 nop ; 1 ..14 mov a,R0 ; Decrement counter 1 ..15 clr C ; 1 ..16 subb a,#1 ; 1 ..17 mov R0,a ; 1 ..18 mov a,R1 ; 1 ..19 subb a,#0 ; 1 ..20 mov R1,a ; 1 ..21 orl a,R0 ; Check counter 1 ..22 jnz offloop38 ; 2 ..24 lbottom38: ljmp irsend38 ; 2 ..38 burstdone38: ret ;--------------------------------------------------------------------------- ; Function: puts ; ; Description: ; Prints a zero-terminated string pointed to by DPTR. ; ; Inputs: DPTR - address of string. ; ; Outputs: None. ; puts: movx A,@DPTR jz prtdone prtwait: lcall putchar inc DPTR sjmp puts 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 ljmp putchar ;--------------------------------------------------------------------------- ; 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 ;--------------------------------------------------------------------------- ; 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,2,nextdigit numdone: ret ; Error exit. badnum: setb badflag jb quietflag,numdone mov dptr,#badnumstr ljmp puts 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 ret notlower: pop ACC ret ;--------------------------------------------------------------------------- ; Function: putchar ; ; 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. ; putchar: push ACC sploop: lcall getchar 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 ;--------------------------------------------------------------------------- ; getchar: clr A jnb SCON.0,notxon mov a,SBUF clr SCON.0 clr ACC.7 ; Clear parity bit cjne a,#19,notxoff setb xoffflag sjmp getchar notxoff: cjne a,#17,notxon clr xoffflag sjmp getchar notxon: ret ;--------------------------------------------------------------------------- ; Function: getline ; ; Description: ; "getline" gets a command line from the controlling terminal into the ; line input buffer. ; ; Inputs: None. ; getline: mov P2,#bufpage mov r1,#0 ; Character loop. An incoming character ^A means a command ; packet is starting. gloop: lcall getchar jnz gotchar jb P1.0,gloop ; No character received but a remote control carrier ; detected. Pass control to the remote control receiver ; routine. ljmp gotcarrier gotchar: cjne a,#1,notpkt1 push 1 lcall dopkt pop 1 mov P2,#bufpage sjmp gloop notpkt1: cjne a,#gkscode,notpkt ; Remote control key status request. Send back the one ; response byte, and clear the attention line. mov a,keycode lcall putchar setb attnflag sjmp gloop ; Check for backspace or rubout. notpkt: 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