; ***************************************************************************** ; cowpatty.asm ; Tuesday, February 3, 1998 21:14:00 ; everything works ; 838 total bytes ; ***************************************************************************** ; trisa: 1 0 0 0 0 = 0x10 ; PORTA: RA4 RA3 RA2 RA1 RA0 = 0x03 ; busy RClk SPIClk /St EEPROM CSN=/CS ; ; PORTB: RB7 RB6 RB5 RB4 RB3 RB2 RB1 RB0 ; KB8 KB7 KB6 KB5/SPIRx KB4 KB3 KB2/SPITx KB1/MemData ; ***************************************************************************** LIST p=16F84 include "P16F84.inc" LF equ 0x0a CR equ 0x0d Rclk equ 3 ; ra3 is 74HC595 register clock St equ 1 ; ra1 is printer data strobe busy_bit equ 4 ; ra4 of PORTA is printer busy port CSN equ 0 ; ra0 is EEPROM chip select line Dout equ 1 ; rb1 is CPU SPI data output Din equ 0 ; rb0 is CPU SPI data input from peripherials SCLK equ 2 ; ra2 is shift clock to SPI peripherals ; equates for ATMEL 25040 EEPROM Write_Enable equ 0x06 ; WRITE ENABLE WRDI equ 0x04 ; WRITE DISABLE RDSR equ 0x05 ; READ STATUS REGISTER RDY equ 0 ; b0 is /READY bit in EEPROM status register WRSR equ 0x01 ; WRITE STATUS REGISTER READL equ 0x03 ; READ DATA FROM LOWER HALF OF MEMORY READH equ 0x0b ; READ DATA FROM UPPER HALF OF MEMORY WRITEL equ 0x02 ; WRITE DATA TO LOWER HALF OF MEMORY WRITEH equ 0x0a ; WRITE DATA TO UPPER HALF OF MEMORY WEN equ 1 ;T0IF equ 2 ;T0IE equ 5 prescaler equ 3 ; 7 for serial interface, 3 for parallel ;********************************************************************************* org 0x0c ; 38 variables + input_string_length tempw res 1 status_image res 1 flags res 1 irq_bit equ 7 got_one equ 6 period_bit equ 5 one2sort equ 4 hi_lo equ 3 irq_cnt res 1 bit_cnt res 1 shfts res 1 tab_index_h res 1 tab_index_l res 1 row_colm res 1 row_colm_image res 1 KeyPad res 1 LastValue res 1 Seconds res 1 Minutes res 1 lsectime res 1 ; init to zero hsectime res 1 ; init to zero BoatID res 1 ref_boat res 1 NumBoats res 1 ; init to zero spidata res 1 Acc res 1 hnumerator res 1 lnumerator res 1 hResult res 1 lResult res 1 msBCD res 1 lsBCD res 1 hStart res 1 lStart res 1 hFinish res 1 lFinish res 1 cap_ltime res 1 cap_htime res 1 lo_laptime res 1 hi_laptime res 1 lo_reftime res 1 hi_reftime res 1 input_string res 1 ; FSR reference for indirect storage of boat ID list ;********************************************************************************** org 0x00 reset goto init ;********************************************************************************** ; use 3.6864MHz crystal ; assign prescaler to timer, and program prescaler to 1:16 ; IRQ frequency is 225Hz ;********************************************************************************** ; all of the following (between multiple lines) code works org 0x04 isr movwf tempw ; this is parallel link isr bsf flags,irq_bit ; is cleared in software movf STATUS,w movwf status_image movf irq_cnt,w sublw 0xe0 ; 0xe0 - irq_cnt: if (=), Z=1 btfsc STATUS,Z goto reset_cnt incf irq_cnt,1 isr_return movf status_image,w movwf STATUS swapf tempw,1 swapf tempw,w bcf INTCON,T0IF retfie reset_cnt clrf irq_cnt incf lsectime,1 btfsc STATUS,Z incf hsectime,1 goto isr_return ;********************************************************************************** table1 addwf PCL,1 ; go there retlw 0 goto specials goto TSN retlw 0 goto TFE retlw 0 retlw 0 retlw 0 goto ZOFS ;********************************************************************************** ZOFS movf spidata,w ; retrieve row offset ; sublw 8 ; 8 - w should be >= 0, c=1 ; btfss STATUS,C ; retlw 0 ; invalid input ; movf spidata,w addwf PCL,1 retlw 0 retlw "7" retlw "4" retlw 0 retlw "1" retlw 0 retlw 0 retlw 0 retlw "0" ;********************************************************************************** TFE movf spidata,w ; sublw 4 ; 4 - w should be >= 0, c=1 ; btfss STATUS,C ; retlw 0 ; invalid input ; movf spidata,w addwf PCL,1 retlw 0 retlw "8" retlw "5" retlw 0 retlw "2" ;********************************************************************************** TSN movf spidata,w ; period already filtered out ; sublw 4 ; 4 - w should be >= 0, c=1 ; btfss STATUS,C ; retlw 0 ; invalid input ; movf spidata,w addwf PCL,1 retlw 0 retlw "9" retlw "6" retlw 0 retlw "3" ; ***************************************************************************** ; return with task vector in w specials movf spidata,w andlw 0x07 addwf PCL,1 retlw 0 ; = N/A retlw 0 ; BS retlw "C" ; comma retlw 0 ; sort/print retlw "E" ; enter ;********************************************************************************** parse_commands swapf KeyPad,w andlw 0x07 addwf PCL,1 retlw "R" goto time_numboats goto current_STATUS goto sort_print goto null_string_input retlw "R" retlw "R" retlw "R" ; this is restart command ;********************************************************************************** hexcnvrt movwf msBCD swapf msBCD,w andlw 0x0f call hex2asci movwf spidata call Print movf msBCD,w andlw 0x0f call hex2asci movwf spidata call Print return hex2asci addwf PCL,1 DT "0123456789ABCDEF" ;********************************************************************************** Mess1 movlw HIGH m1 addwf tab_index_h,w movwf PCLATH movlw LOW m1 addwf tab_index_l,w btfsc STATUS,C incf PCLATH,1 movf tab_index_l,w addwf PCL,1 m1 DT 0x0a,0x0d DT " COWPATTY PAGEANT RACE TIMER",0x0a,0x0d DT " Erase Memory? 1 = Y, 0 = N.",0x0a,0x0d,0x0a,0x00 ;********************************************************************************** Mess2 movlw HIGH m2 addwf tab_index_h,w movwf PCLATH movlw LOW m2 addwf tab_index_l,w btfsc STATUS,C incf PCLATH,1 movf tab_index_l,w addwf PCL,1 m2 DT " Ready!!",0x0d,0x0a,0x0a,0x00 ;********************************************************************************** init movlw 0x03 movwf PORTA movlw 0x10 tris PORTA movlw 0xf0 ; start this way to print startup message movwf PORTB tris PORTB movlw prescaler ; depends on whether serial or parallel interface option clrf TMR0 clrf lsectime clrf hsectime clrf NumBoats clrf flags Prep4BoatIDList movlw input_string movwf FSR ; pointer to next place to put input BoatID clrf INDF clrf BoatID bsf INTCON,T0IE bcf INTCON,T0IF bsf INTCON,GIE main call ok2write bcf PORTA,CSN ; assert CSN movlw WRSR movwf spidata call xmit clrf spidata ; no block protection call xmit bsf PORTA,CSN ; deassert CSN clrf tab_index_l clrf tab_index_h do1 call Mess1 ; erase memory or not? incf tab_index_l,1 btfsc STATUS,Z incf tab_index_h,1 andlw 0xff btfsc STATUS,Z goto ScanKeys movwf spidata call Print goto do1 ; the following gathers answer to ERASE query ScanKeys call ScanAllKeys ; returns with row_colm data in w register movwf row_colm_image call debounce call ScanAllKeys ; returns with row_colm data in w register subwf row_colm_image,w ; (row_colm_image)-(row_colm)=>w btfss STATUS,Z ; skip if same as before debounce goto ScanKeys ; not same row_colm signal movlw 0x88 ; # 0's key code subwf row_colm,w btfsc STATUS,Z goto send_ready movlw 0x48 ; # 1's key code subwf row_colm,w btfss STATUS,Z goto ScanKeys call erase_memory send_ready clrf tab_index_l clrf tab_index_h do2 call Mess2 ; print startup message incf tab_index_l,1 btfsc STATUS,Z incf tab_index_h,1 andlw 0xff btfsc STATUS,Z goto ScanPrep movwf spidata call Print goto do2 ; routine scanning of keypad starts here ; 1st scan and debounce input ScanPrep clrf KeyPad ScanKeyPad call ScanAllKeys ; returns with row_colm data in w register movwf row_colm_image ; and saves it here too for later comparison call debounce call ScanAllKeys ; returns with row_colm data in w register subwf row_colm_image,w ; (row_colm_image)-(row_colm)=>w btfss STATUS,Z ; skip if same as before debounce goto ScanKeyPad ; not same row_colm signal ; did we have a key on last pass? btfsc flags,got_one goto have_data ; we did have a key closure on last pass ; so we now have a NEW or NO key. nothing twice can get you here too. movlw 0xff andwf row_colm,w btfsc STATUS,Z goto ScanPrep ; NO KEY ; we mark the fact that we got a key closure movwf KeyPad ; data in row_colm, row_colm_image, & now KeyPad. bsf flags,got_one andlw 0x82 sublw 0x82 btfss STATUS,Z goto ScanKeyPad bsf flags,period_bit goto ScanKeyPad ; do we still have a key closure? have_data movlw 0xff andwf row_colm,w btfss STATUS,Z goto update ; key/s are still closed ; keys are open now, so we either have a legal key closure, or a command input! movf KeyPad,w btfss flags,period_bit goto scan3 call parse_commands sublw "R" btfsc STATUS,Z goto init scan1 bcf flags,period_bit scan2 bcf flags,got_one goto ScanPrep scan3 call process_entry goto scan2 update iorwf KeyPad ; update entry goto ScanKeyPad ; scan by pulling rows low against "weak pullup" in cpu ScanAllKeys movlw 0x0f tris PORTB movwf PORTB ; drive rows (rb7:4) low movf PORTB,w ; read colms (rb3:0) andlw 0x0f movwf PORTB ; now drive ports that read low, low movwf row_colm ; save colm data (rb3:0), clear row data movlw 0xf0 tris PORTB ; make colm outputs active movf PORTB,w ; read rows andlw 0xf0 iorwf row_colm,1 ; prepend row data to colm data comf row_colm,1 ; keys marked by ones movf row_colm,w return ; send data out centronix port one (1) char at a time Print call xmit StillBusy btfsc PORTA,busy_bit ; wait until printer is ready goto StillBusy bsf PORTA,Rclk ; clock data to output registers bcf PORTA,Rclk bcf PORTA,St ; now /Strobe it into printer bsf PORTA,St retlw 0 read_first movlw 8 movwf bit_cnt goto read_start read_loop bsf PORTA,SCLK bcf PORTA,SCLK read_start btfsc PORTB,Din bsf STATUS,C btfss PORTB,Din bcf STATUS,C rlf spidata,1 decfsz bit_cnt,1 goto read_loop return read_next movlw 8 movwf bit_cnt goto read_loop xmit movlw 0xfd ; rb1 is serial data output to serial-parallel converter movwf PORTB tris PORTB movlw 8 movwf bit_cnt xmitloop rlf spidata,1 btfsc STATUS,C bsf PORTB,Dout btfss STATUS,C bcf PORTB,Dout bsf PORTA,SCLK bcf PORTA,SCLK decfsz bit_cnt,1 goto xmitloop return debounce btfsc flags,irq_bit ; debounce delay bcf flags,irq_bit dlay5ms btfss flags,irq_bit goto dlay5ms bcf flags,irq_bit return ; 1st code segment fetches ASCII representation of key process_entry movf KeyPad,w andlw 0x0f sublw 8 ; 8 - w btfss STATUS,C return ; do nothing, invalid data swapf KeyPad,w ; calc andlw 0x0f ; row offset movwf spidata ; save in spidata for later movf KeyPad,w ; calc colm andlw 0x0f ; offset call table1 movwf INDF ; save ASCII input_string andlw 0xff btfsc STATUS,Z return ; if we got an Enter key, we process input_string. sublw "E" ; Enter? btfsc STATUS,Z goto got_enter incf FSR,1 clrf INDF return ; now we process input_string. ; point at beginning of string, capture time when Enter key was pressed. got_enter clrf BoatID clrf INDF movlw input_string movwf FSR movf lsectime,w movwf cap_ltime movf hsectime,w movwf cap_htime ; check to see if we are done yet. zero (0) marks end of string process_loop movf INDF,1 btfsc STATUS,Z ; test for enter marker goto process_done ; did we have a delimiter? if so use current BoatID as index into EEPROM and save time there. movf INDF,w sublw "C" ; BoatID delimiter? btfss STATUS,Z goto adjust_boat_num call store_time clrf BoatID incf FSR,1 goto process_loop ; update BoatID and examine next string character. adjust_boat_num call CalcBoatNum incf FSR,1 goto process_loop store_time call read_times call ok2write ; where to write is now the question. start or finish or none? movf hStart,1 btfss STATUS,Z goto check_finish movf lStart,1 btfsc STATUS,Z bcf flags,hi_lo ; save start time btfss STATUS,Z bsf flags,hi_lo ; save finish time goto save_it check_finish movf hFinish,1 btfss STATUS,Z goto store_again ; already did this one, could be/or is an error movf lFinish,1 btfss STATUS,Z goto store_again bsf flags,hi_lo save_it bcf PORTA,CSN movlw 0x40 subwf BoatID,w ; C = 1 if BoatID => 64, (BoatID - 64) btfsc STATUS,C movlw WRITEH btfss STATUS,C movlw WRITEL movwf spidata call xmit bcf STATUS,C rlf BoatID,w movwf spidata btfsc flags,hi_lo incf spidata,1 bcf STATUS,C rlf spidata,1 ; calc 4 byte memory record address call xmit movf cap_htime,w movwf spidata call xmit movf cap_ltime,w movwf spidata call xmit bsf PORTA,CSN btfss flags,hi_lo incf NumBoats,1 return store_again clrf BoatID return process_done call store_time clrf BoatID movlw input_string movwf FSR return CalcBoatNum movf BoatID,w movwf LastValue bcf STATUS,C rlf BoatID,1 ; 2x rlf BoatID,1 ; 4x rlf BoatID,1 ; 8x bcf STATUS,C rlf LastValue,w ; 2x addwf BoatID,1 ; 8x + 2x = 10x movf INDF,w andlw 0x0f ; no ascii please addwf BoatID,1 return ; PERIOD + MINUS = Print NumBoats and Current Lapsed Time ; print lapsed time and number of boats time_numboats movf NumBoats,w call sendBCD call spaces call DumpTime call NewLine retlw 0 ; PERIOD + COMMA = PRINT Current Race Status. ; dump BoatID, Start-time, Finish-time. current_STATUS call time_numboats call NewLine clrf BoatID ; there is no BoatID zero! next_BoatID incf BoatID,1 movlw 0x64 subwf BoatID,w ; BoatID-100 btfsc STATUS,C retlw 0 call read_times ; print unsorted list of finishers ; movf hFinish,1 ; btfss STATUS,Z ; goto dumpID ; movf lFinish,1 ; btfsc STATUS,Z ; goto next_BoatID ; non-finisher ; print all starters movf hStart,1 btfss STATUS,Z goto dumpID movf lStart,1 btfsc STATUS,Z goto next_BoatID ; non-starter dumpID movf BoatID,w call sendBCD call spaces movf hStart,w andlw 0x7f movwf hnumerator movf lStart,w movwf lnumerator call PrnTime call spaces movf hFinish,w movwf hnumerator movf lFinish,w movwf lnumerator call PrnTime call NewLine goto next_BoatID ; PERIOD + MINUS + COMMA = SORT and PRINT ; print sorted race results. BoatIDs and lapsed time. sort_print call time_numboats call NewLine ; 1st we scan to see if msb of hStart values are set indicating previous sort ; if we find them, we clear them! movlw 1 movwf BoatID purge_loop call read_times btfsc hStart,7 goto clearMSB inc_spot incf BoatID,1 movlw .100 subwf BoatID,w ; BoatID - 100 btfss STATUS,C goto purge_loop goto top_sort clearMSB bcf hStart,7 call ok2write bcf PORTA,CSN movlw 0x40 subwf BoatID,w ; C = 1 if BoatID => 64, (BoatID - 64) btfsc STATUS,C movlw WRITEH btfss STATUS,C movlw WRITEL movwf spidata call xmit bcf STATUS,C rlf BoatID,w movwf spidata bcf STATUS,C rlf spidata,1 ; calc 4 byte memory record address call xmit movf hStart,w movwf spidata call xmit bsf PORTA,CSN goto inc_spot top_sort movlw 0x01 movwf BoatID movlw 0xff movwf lo_reftime movwf hi_reftime bcf flags,one2sort ; flag indicating if we're done goto test1 new_ref movf hi_laptime,w movwf hi_reftime movf lo_laptime,w movwf lo_reftime movf BoatID,w movwf ref_boat sortlp1 incf BoatID,1 test1 movlw 0x64 ; are we done yet? subwf BoatID,w ; BoatID-100 btfsc STATUS,C goto rWeDone ; this is a question call read_times btfsc hStart,7 ; did we do him already goto sortlp1 didhefinish movf hFinish,1 ; did he finish? btfss STATUS,Z goto set_pass_flag ; he finished movf lFinish,1 btfsc STATUS,Z goto sortlp1 set_pass_flag bsf flags,one2sort ; we found one to process, not done yet! ; compute lapsed time movf lStart,w subwf lFinish,w ; lFinish - lStart movwf lo_laptime btfss STATUS,C decf hFinish,1 movf hStart,w andlw 0x7f subwf hFinish,w ; hFinish - hStart movwf hi_laptime ; was this boat faster than ref_boat? movf hi_reftime,w subwf hi_laptime,w ; hi_laptime - hi_reftime btfss STATUS,C btfsc STATUS,Z goto test_lows ; was faster! so becomes new reference goto new_ref ; was equal or slower test_lows movf lo_reftime,w subwf lo_laptime,w ; hi_laptime - hi_reftime btfss STATUS,C btfsc STATUS,Z goto sortlp1 goto new_ref rWeDone btfss flags,one2sort retlw 0 ; we are DONE!! save_marked_time movf ref_boat,w ; mark ref_boat as processed movwf BoatID call read_times try_again bsf hStart,7 call ok2write movlw 0x40 subwf ref_boat,w ; C = 1 if ref_boat => 64, (ref_boat - 64) btfsc STATUS,C movlw WRITEH btfss STATUS,C movlw WRITEL movwf spidata bcf PORTA,CSN call xmit bcf STATUS,C rlf ref_boat,w movwf spidata bcf STATUS,C rlf spidata,1 ; calc 4 byte memory record address call xmit movf hStart,w movwf spidata call xmit bsf PORTA,CSN call read_times btfss hStart,7 goto try_again movf ref_boat,w call sendBCD call spaces movf hi_reftime,w movwf hnumerator movf lo_reftime,w movwf lnumerator call PrnTime call NewLine goto top_sort ; PERIOD + ENTER = Disregard last input_string ; clear entry command null_string_input clrf KeyPad movlw input_string movwf FSR clrf INDF retlw 0 ; keys are: ; ENTER terminates a BoatId string entry sequence ; COMMA, by itself is a delimiter of BoatIds in entered string ; MINUS, by itself is a backspace key during BoatId entry ; PERIOD key is an Escape Sequence key used with other keys ; for safe transition to another state. For example, ; PERIOD + MINUS = PRINT Current Race Status, finishers only. ; PERIOD + COMMA = Print NumBoats and Lapsed Time ; PERIOD + ENTER = Disregard last input_string ; PERIOD + MINUS + COMMA = SORT and PRINT ; PERIOD + MINUS + COMMA + ENTER = RESTART ; PERIOD must be pressed before its companion command key. ; since sequence may involve 4 keys, we don't commit to a course ; of action until we see the PERIOD key released, which must be the ; first of the group to be released. ; only half (32767 count ($7fff)) of 16 bit count is useful since we use msb ; as flag indicating this boatID was sorted. ; 32767 seconds equals: 9 hours, 6 minutes, 8 seconds. ; if anyone takes that long to finish race, bury them! ; we compute minutes first by dividing seconds by 60 ; remainder (seconds) is in "Acc", minutes in "h/lresults" DumpTime movf hsectime,w movwf hnumerator movf lsectime,w movwf lnumerator PrnTime call divide movf Acc,w movwf Seconds ; now move minutes to numerator and compute hours movf hResult,w movwf hnumerator movf lResult,w movwf lnumerator call divide ; remainder (minutes) is in "Acc", Hours in "h/lresults" movf Acc,w movwf Minutes ; 16 bit seconds counter only supports 1 byte hours tabulation, so results are in lResult only movf lResult,w ; Hours call sendBCD movlw ':' movwf spidata call Print movf Minutes,w call sendBCD movlw ':' movwf spidata call Print movf Seconds,w call sendBCD return sendBCD clrf msBCD BCDloop movwf lsBCD movlw 0x0a subwf lsBCD,w ; lsBCD - 10 btfss STATUS,C goto BCDswap incf msBCD,1 goto BCDloop BCDswap movlw 0x30 iorwf lsBCD,1 iorwf msBCD,w movwf spidata call Print movf lsBCD,w movwf spidata call Print return divide movlw .16 movwf shfts clrf lResult clrf hResult clrf Acc dloop bcf STATUS,C rlf lnumerator,1 rlf hnumerator,1 rlf Acc,1 movlw .60 ; 0x3c subwf Acc,w ; Acc - 60 btfsc STATUS,C movwf Acc skip rlf lResult,1 rlf hResult,1 decfsz shfts,1 goto dloop retlw 0 stop_write movlw WRDI goto send_it enable_write movlw Write_Enable send_it bcf PORTA,CSN ; enable EEPROM movwf spidata call xmit bsf PORTA,CSN call debounce goto debounce ; return to caller from debounce ok2read movlw 0xfd movwf PORTB tris PORTB bcf PORTA,CSN movlw RDSR ; read_STATUS movwf spidata call xmit call read_first bsf PORTA,CSN btfsc spidata,RDY goto ok2read return ok2write call ok2read btfsc spidata,WEN return call enable_write goto ok2write ; test to see if BoatID is in upper or lower half of memory, ; then read start/finish times and return to caller. read_times call ok2read bcf PORTA,CSN movlw 0x40 subwf BoatID,w ; C = 1 if BoatID => 64, (BoatID - 64) btfsc STATUS,C movlw READH btfss STATUS,C movlw READL movwf spidata call xmit bcf STATUS,C rlf BoatID,w movwf spidata bcf STATUS,C rlf spidata,1 ; calc 4 byte memory record address call xmit call read_first movf spidata,w movwf hStart call read_next movf spidata,w movwf lStart call read_next movf spidata,w movwf hFinish call read_next movf spidata,w movwf lFinish bsf PORTA,CSN return NewLine movlw LF movwf spidata call Print movlw CR movwf spidata call Print return spaces movlw 3 movwf shfts sp_lp movlw 0x20 movwf spidata call Print decfsz shfts,1 goto sp_lp return ; now we clear bottom halve of mem, 8 bytes at a time erase_memory clrf Acc movlw 0x20 movwf tab_index_h mem_loop_1 movlw 8 movwf tab_index_l call ok2write bcf PORTA,CSN movlw WRITEL ; send command movwf spidata call xmit movf Acc,w ; send this groups start address movwf spidata call xmit i_loop_11 clrf spidata call xmit ; send data = 0 to clear location incf Acc,1 ; keep tabs on write address decfsz tab_index_l,1 goto i_loop_11 bsf PORTA,CSN ; initiate write process with deassert decfsz tab_index_h,1 goto mem_loop_1 ; now the top half clrf Acc movlw 0x20 movwf tab_index_h mem_loop_2 movlw 8 movwf tab_index_l call ok2write bcf PORTA,CSN movlw WRITEH movwf spidata call xmit movf Acc,w ; send this groups start address movwf spidata call xmit i_loop_21 clrf spidata call xmit ; clear location incf Acc,1 decfsz tab_index_l,1 goto i_loop_21 bsf PORTA,CSN decfsz tab_index_h,1 goto mem_loop_2 return end