;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; final.asm ; ; By Adriaan Van Nuffel and Robin Reusens ; afvnuffe@vub.ac.be, rreusens@vub.ac.be ; ; 20 may 2005 ; ; The goal of the game is getting as many points as possible, by shooting ; the cowboys that appear behind the windows. ; ; These cowboys have to carry these LDRS: ; Mr.1: RB0 - RD2 ; Mr.2: RB1 - RD3 ; Mr.3: RB2 - RD4 ; Mr.4: RB3 - RD5 ; Mr.5: RB4 - RD6 ; Mr.6: RB5 - RD7 ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LIST P=PIC16F877A #INCLUDE P16F877A.INC __CONFIG _CP_OFF & _DEBUG_OFF & _LVP_OFF & _PWRTE_ON & _WDT_OFF & _BODEN_OFF & _XT_OSC ORG 0 ; *** Giving names to the used registers *** ; DELAY routine variables TIMER1 EQU H'20' ; For the Delay routine TIMER2 EQU H'21' ; INSERTDIGITS routine variables SH_REG1 EQU H'22' ; Used for accessing the shift registers (SH_REG1 controls SH_REG2 EQU H'23' ; the left digit, SH_REG2 the right one) INSDIG_COUNTER EQU H'24' ; Used as counter in the INSERDIGITS subroutine ; INCSCORE and SHOWSCORE routines variables SCORE_DEC1 EQU H'25' ; Stores the first decimal of the score SCORE_DEC2 EQU H'26' ; Stores the second decimal (= x 10) ; CONVERTSCORE routine variables A_SH_REG EQU H'27' ; Used for the conversion to a decimal number of the score A_DEC EQU H'28' ; Idem. ; NOT USED SO FAR RESET_BIT EQU H'29' ; To check if the game is ended. ; CHECK_LDR routine variables LDRS EQU H'2A' ; Used to check the status of the LDRs ; Time-management variables (used in main loop, TRIGGER_COWBOY and INCSCORE) CLOCK EQU H'2B' ; When CLOCK is being reset, the value it will reset to DA_CLOCK_NUMBAH EQU H'2E' ; is stored in DA_CLOCK_NUMBAH, which decreases every time a cowboy is hit CLOCK2 EQU H'34' TIMELEFT EQU H'35' ; This one decreases each time a cowboy appears. When it's zero: game over!! ; Cowboy-management variables (ised in ) COWBOY_STATS EQU H'2C' ; 1 = visible / 0 = withdrawn COWBOY_AGE EQU H'2D' ; 1 = time to go, 0 = he can stay a little longer... RANDOM EQU H'30' ; For the random sequence generator DECIDE EQU H'31' ; To decide which cowboy should be triggered... BIT_0 EQU H'32' ; For the PRBS random method BIT_5 EQU H'33' DIG_0 EQU H'70' ; The digits DIG_1 EQU H'71' DIG_2 EQU H'72' DIG_3 EQU H'73' DIG_4 EQU H'74' DIG_5 EQU H'75' DIG_6 EQU H'76' DIG_7 EQU H'77' DIG_8 EQU H'78' DIG_9 EQU H'79' ; *** Configure all pins *** RESET ; Outputs: ; ; RB5-RB0 (Actuation of the relays) ; RA0 -> DS Shift Register 1 (Data Send) ; RA1 -> DS Sh Reg 2 ; RA2 -> OE Sh Reg 1&2 (Output Enable, active low) ; RA3 -> ST_CP Sh Reg 1&2 (Storage register clock pulse) ; RA4 -> SH_CP Sh Reg 1&2 (Shift register clock pulse) ; ; Inputs: ; ; RD2-RD7 (From comparators) ; RD1 -> start game button ; ; Not used: ; ; RA5, RE0-RE2, RC0-RC3, RC4-RC7, RD0 (= 13 pins) ; RB6 & RB7 are only connected to the programming pins ; BCF STATUS, RP0 ; Go to Bank 0 BCF STATUS, RP1 CLRF PORTA ; Initialize Port A (See example datasheet p 41) CLRF PORTB ; " " B CLRF PORTD ; " " D (I'm not sure if all this is necessary...) BSF STATUS, RP0 ; Go to Bank 1 MOVLW B'00000110' ; Adjust the settings in ADCON1, to make all the pins MOVWF ADCON1 ; of port A digital I/O. See datasheet ('ds', from now on) p.128 MOVLW B'11000000' ; All pins of A are outputs, except RA7 and RA6, which MOVWF TRISA ; are non-existant CLRF TRISB ; All pins of port B are outputs. BCF TRISE, PSPMODE ; Port D pins are digital I/O. (ds p. 50) MOVLW B'11111111' ; And they are all inputs, except RD0 and RD1, which MOVWF TRISD ; are not used. ; *** Defining the digits *** BCF STATUS, RP0 ; Go to Bank 0 MOVLW B'01111011' MOVWF DIG_0 MOVLW B'01100000' MOVWF DIG_1 MOVLW B'00110111' MOVWF DIG_2 MOVLW B'01110110' MOVWF DIG_3 MOVLW B'01101100' MOVWF DIG_4 MOVLW B'01011110' MOVWF DIG_5 MOVLW B'01011111' MOVWF DIG_6 MOVLW B'01110000' MOVWF DIG_7 MOVLW B'01111111' MOVWF DIG_8 MOVLW B'01111110' MOVWF DIG_9 CLRF SCORE_DEC1 CLRF SCORE_DEC2 MOVLW B'10100111' ; The random start number... (arbitrary chosen) MOVWF RANDOM ; *** Main part *** ; There are two loops: 'preloop' in case you're not playing, and 'mainloop' while playing ; after the game, we go back to 'preloop' preloop MOVLW H'00' MOVWF RESET_BIT MOVLW H'FF' MOVWF CLOCK MOVWF CLOCK2 MOVWF DA_CLOCK_NUMBAH BTFSC RANDOM, H'0' ; The random routine gets 'warmed up' BSF BIT_0, H'0' BTFSC RANDOM, H'5' BSF BIT_5, H'0' MOVF BIT_0, 0 XORWF BIT_5, 1 BCF STATUS, C BTFSC BIT_5, H'0' BSF STATUS, C RRF RANDOM, 1 BCF BIT_0, H'0' BCF BIT_5, H'0' MOVLW B'01111101' ; = 'A' MOVWF SH_REG1 MOVF DIG_3, 0 ; = '3' MOVWF SH_REG2 CALL INSERTDIGITS CALL DELAY CALL DELAY BSF PORTA,H'2' ; Output disable: lettin' it blink a bit... CALL DELAY BCF PORTA,H'2' ; Output enable again... CALL DELAY CALL DELAY BSF PORTA,H'2' ; Output disable: lettin' it blink a bit... CALL DELAY BCF PORTA,H'2' ; Output enable again... MOVLW B'00000000' ; = '' MOVWF SH_REG1 MOVLW B'00000000' ; = '' MOVWF SH_REG2 CALL INSERTDIGITS CALL DELAY CALL DELAY MOVLW B'00000000' ; = '' MOVWF SH_REG1 MOVLW B'00000101' ; = 'r' MOVWF SH_REG2 CALL INSERTDIGITS CALL DELAY CALL DELAY MOVLW B'00000101' ; = 'r' MOVWF SH_REG1 MOVLW B'01000111' ; = 'o' MOVWF SH_REG2 CALL INSERTDIGITS CALL DELAY CALL DELAY MOVLW B'01000111' ; = 'o' MOVWF SH_REG1 MOVLW B'01001111' ; = 'b' MOVWF SH_REG2 CALL INSERTDIGITS CALL DELAY CALL DELAY MOVLW B'01001111' ; = 'b' MOVWF SH_REG1 MOVLW B'00000001' ; = 'i' MOVWF SH_REG2 CALL INSERTDIGITS CALL DELAY CALL DELAY MOVLW B'00000001' ; = 'i' MOVWF SH_REG1 MOVLW B'01000101' ; = 'n' MOVWF SH_REG2 CALL INSERTDIGITS CALL DELAY CALL DELAY MOVLW B'01000101' ; = 'n' MOVWF SH_REG1 MOVLW B'00000000' ; = '' MOVWF SH_REG2 CALL INSERTDIGITS CALL DELAY CALL DELAY MOVLW B'00000000' ; = '' MOVWF SH_REG1 MOVLW B'00000000' ; = '' MOVWF SH_REG2 CALL INSERTDIGITS CALL DELAY CALL DELAY ; BTFSC PORTD, H'1' ; Test start button, if pressed: start game. GOTO mainloop GOTO preloop mainloop MOVF PORTD, 0 ; The number zero is substracted from Port D, and if SUBLW B'00000000' ; the result is 0 (zero-bit = 1), we know none of the LDRs BTFSS STATUS, Z ; was hit. So we can return to the start of the loop, and CALL CHECK_LDRS ; check things again. Otherwise, we have to go check which LDR was hit. DECF CLOCK2, 1 BTFSC STATUS, Z DECF CLOCK, 1 ; Decrease the clock, until it has reached 00000000 BTFSC STATUS, Z ; (we know cos we check the zero-bit) CALL TRIGGER_COWBOY ; which means that a certain amount of time has passed. Time for action! BTFSC RESET_BIT, H'0' GOTO preloop GOTO mainloop ; *** TRIGGER_COWBOY ; This routine decides which cowboys have to appear, and which have to disseappear...! TRIGGER_COWBOY MOVF DA_CLOCK_NUMBAH, 0 ; reset the clock to the right time! MOVWF CLOCK2 BTFSC RANDOM, H'0' BSF BIT_0, H'0' BTFSC RANDOM, H'5' BSF BIT_5, H'0' MOVF BIT_0, 0 XORWF BIT_5, 1 BCF STATUS, C BTFSC BIT_5, H'0' BSF STATUS, C RRF RANDOM, 1 BCF BIT_0, H'0' BCF BIT_5, H'0' BTFSC RANDOM, H'0' BSF BIT_0, H'0' BTFSC RANDOM, H'5' BSF BIT_5, H'0' MOVF BIT_0, 0 XORWF BIT_5, 1 BCF STATUS, C BTFSC BIT_5, H'0' BSF STATUS, C RRF RANDOM, 1 BCF BIT_0, H'0' BCF BIT_5, H'0' BTFSC RANDOM, H'0' BSF BIT_0, H'0' BTFSC RANDOM, H'5' BSF BIT_5, H'0' MOVF BIT_0, 0 XORWF BIT_5, 1 BCF STATUS, C BTFSC BIT_5, H'0' BSF STATUS, C RRF RANDOM, 1 BCF BIT_0, H'0' BCF BIT_5, H'0' MOVF RANDOM, 0 MOVWF DECIDE MOVLW B'00000111' ANDWF DECIDE, 1 ; Now we're gonna check which cowboy's time has passed... ; The first time a cowboy is checked: he is not removed, but ; the second time, he'll be removed. To achieve this, the ; first time, his 'age' (COWBOY_AGE) bit is set. ; When he's removed, his age bit goes down again. ; check_mr1 BTFSS COWBOY_STATS, H'0' GOTO check_mr2 BTFSS COWBOY_AGE, H'0' GOTO its_time_to_go_1 BSF COWBOY_AGE, H'0' GOTO check_mr2 its_time_to_go_1 CALL DELAY CALL DELAY BCF COWBOY_STATS, H'0' BCF COWBOY_AGE, H'0' check_mr2 BTFSS COWBOY_STATS, H'1' GOTO check_mr3 BTFSS COWBOY_AGE, H'1' GOTO its_time_to_go_2 BSF COWBOY_AGE, H'1' GOTO check_mr3 its_time_to_go_2 BCF COWBOY_STATS, H'1' BCF COWBOY_AGE, H'1' check_mr3 BTFSS COWBOY_STATS, H'2' GOTO check_mr4 BTFSS COWBOY_AGE, H'2' GOTO its_time_to_go_3 BSF COWBOY_AGE, H'2' GOTO check_mr4 its_time_to_go_3 BCF COWBOY_STATS, H'2' BCF COWBOY_AGE, H'2' check_mr4 BTFSS COWBOY_STATS, H'3' GOTO check_mr5 BTFSS COWBOY_AGE, H'3' GOTO its_time_to_go_4 BSF COWBOY_AGE, H'3' GOTO check_mr5 its_time_to_go_4 BCF COWBOY_STATS, H'3' BCF COWBOY_AGE, H'3' check_mr5 BTFSS COWBOY_STATS, H'4' GOTO check_mr6 BTFSS COWBOY_AGE, H'4' GOTO its_time_to_go_5 BSF COWBOY_AGE, H'4' GOTO check_mr6 its_time_to_go_5 BCF COWBOY_STATS, H'4' BCF COWBOY_AGE, H'4' check_mr6 BTFSS COWBOY_STATS, H'5' GOTO end_agecheck BTFSS COWBOY_AGE, H'5' GOTO its_time_to_go_6 BSF COWBOY_AGE, H'5' GOTO end_agecheck its_time_to_go_6 BCF COWBOY_STATS, H'5' BCF COWBOY_AGE, H'5' end_agecheck test_combination_0 MOVLW B'00000000' ; In case the last three bits of RANDOM are 000, SUBWF DECIDE, 0 ; cowboy 1 will become visible. BTFSS STATUS, Z GOTO test_combination_1 BSF COWBOY_STATS, H'00' GOTO end_combinations_tests test_combination_1 MOVLW B'00000001' ; In case the last three bits of RANDOM are 001, SUBWF DECIDE, 0 ; cowboy 2 will become visible. BTFSS STATUS, Z GOTO test_combination_2 BSF COWBOY_STATS, H'01' GOTO end_combinations_tests test_combination_2 MOVLW B'00000010' ; In case the last three bits of RANDOM are 010, SUBWF DECIDE, 0 ; cowboy 3 will become visible. BTFSS STATUS, Z GOTO test_combination_3 BSF COWBOY_STATS, H'02' GOTO end_combinations_tests test_combination_3 MOVLW B'00000011' ; In case the last three bits of RANDOM are 011, SUBWF DECIDE, 0 ; cowboy 4 will become visible. BTFSS STATUS, Z GOTO test_combination_4 BSF COWBOY_STATS, H'03' GOTO end_combinations_tests test_combination_4 MOVLW B'00000100' ; In case the last three bits of RANDOM are 100, SUBWF DECIDE, 0 ; cowboy 5 will become visible. BTFSS STATUS, Z GOTO test_combination_5 BSF COWBOY_STATS, H'04' GOTO end_combinations_tests test_combination_5 MOVLW B'00000101' ; In case the last three bits of RANDOM are 101, SUBWF DECIDE, 0 ; cowboy 6 will become visible. BTFSS STATUS, Z GOTO end_combinations_tests ; !!! BSF COWBOY_STATS, H'05' end_combinations_tests ; Indeed, in case the last three bits were 110 or 111, nothing is changed. CALL REFRESH_COWBOYS RETURN ; End of TRIGGER_COWBOY subroutine ; *** REFRESH_COWBOYS REFRESH_COWBOYS CLRF PORTB MOVF COWBOY_STATS, 0 MOVWF PORTB RETURN ; *** CHECK_LDRS ; This routine tests which LDR is hit, increases the score, and shows it. CHECK_LDRS MOVWF LDRS BTFSS LDRS, H'2' ; Is there a hit on Mr.1? GOTO test_LDR_port_RD3 BTFSS COWBOY_STATS, H'0' ; ... and is he visible?? GOTO end_without_inc ; If not: you hit a non-active cowboy. This does not count. BCF COWBOY_STATS, H'0' ; In that case: 'withdraw' Mr. 1 ! BCF COWBOY_AGE, H'0' GOTO end_check_ldrs ; We assume that you can't hit two LDRs simultaneously test_LDR_port_RD3 BTFSS LDRS, H'3' ; Is there a hit on Mr.2? GOTO test_LDR_port_RD4 BTFSS COWBOY_STATS, H'1' ; ... and is he visible?? GOTO end_without_inc ; If not: you hit a non-active cowboy. This does not count. BCF COWBOY_STATS, H'1' BCF COWBOY_AGE, H'1' GOTO end_check_ldrs test_LDR_port_RD4 BTFSS LDRS, H'4' ; Mr. 3? GOTO test_LDR_port_RD5 BTFSS COWBOY_STATS, H'2' ; ... etcetera. GOTO end_without_inc BCF COWBOY_STATS, H'2' BCF COWBOY_AGE, H'2' GOTO end_check_ldrs test_LDR_port_RD5 BTFSS LDRS, H'5' ; Mrs. 4 GOTO test_LDR_port_RD6 BTFSS COWBOY_STATS, H'3' GOTO end_without_inc BCF COWBOY_STATS, H'3' BCF COWBOY_AGE, H'3' GOTO end_check_ldrs test_LDR_port_RD6 BTFSS LDRS, H'6' ; Mr. 5? GOTO test_LDR_port_RD7 BTFSS COWBOY_STATS, H'4' GOTO end_without_inc BCF COWBOY_STATS, H'4' BCF COWBOY_AGE, H'4' GOTO end_check_ldrs test_LDR_port_RD7 ; BTFSS LDRS, H'7' ; Mr. 6? ; GOTO end_check_ldrs ; There is no need to check the bit: BTFSS COWBOY_STATS, H'5' GOTO end_without_inc BCF COWBOY_STATS, H'5' ; if it wasn't 1, 2, 3, 4 or 5, it must be 6! BCF COWBOY_AGE, H'5' end_check_ldrs CALL INCSCORE ; And add one point! Yahoo!! CALL REFRESH_COWBOYS end_without_inc CALL SHOWSCORE RETURN ; *** INCSCORE: This subroutine increments the score. ; The two decimals of the score are calculated seperately, to make ; the conversion to decimal easier. SCORE_DEC1 is the right decimal, ; SCORE_DEC2 the left one. (This is the opposite of the SH_REG's, just ; to make it easier.) ; Each time you make a hit, the clock gets 'faster' ; When the score has reached 99, the game is over. INCSCORE MOVLW H'02' SUBWF DA_CLOCK_NUMBAH, 1 ; Tighten up the clock INCF SCORE_DEC1, 1 MOVF SCORE_DEC1, 0 SUBLW H'0A' ; When the first decimal of the score has become 10, BTFSS STATUS, Z ; the Z-bit will be set. GOTO ietsverder CLRF SCORE_DEC1 ; We have to put the first decimal then back to 0, INCF SCORE_DEC2, 1 ; and increment the second decimal. MOVLW H'09' ; When score = 99, the game should stop. SUBWF SCORE_DEC2, 0 ; To do this, first check the last decimal. BTFSC STATUS, Z GOTO check_dec1 ; If it's nine: go and check decimal 1 GOTO ietsverder ; else go a bit further. check_dec1 MOVLW H'09' ; Now we can check the first decimal. SUBWF SCORE_DEC1, 0 BTFSC STATUS, Z BSF RESET_BIT, H'0' ; First decimal = 9? Then score is 99 and we must reset. ietsverder RETURN ; *** SHOWSCORE: This subroutine calls CONVERTSCORE twice to check for each ; score_decimal which digit should be displayed. Finally, they get displayed ; by calling INSERTDIGITS (automatically) SHOWSCORE MOVF SCORE_DEC1,0 MOVWF A_DEC CALL CONVERTSCORE MOVF A_SH_REG, 0 MOVWF SH_REG2 MOVF SCORE_DEC2, 0 MOVWF A_DEC CALL CONVERTSCORE MOVF A_SH_REG, 0 MOVWF SH_REG1 CALL INSERTDIGITS RETURN ; *** CONVERTSCORE: This subroutine takes care of the conversion to decimal of the score. ; We simply check for both decimals with which DIG_... they correspond. CONVERTSCORE test_nul MOVLW H'00' SUBWF A_DEC, 0 BTFSS STATUS, Z GOTO test_een MOVF DIG_0, 0 MOVWF A_SH_REG GOTO einde_van_testen test_een MOVLW H'01' SUBWF A_DEC, 0 BTFSS STATUS, Z GOTO test_twee MOVF DIG_1, 0 MOVWF A_SH_REG GOTO einde_van_testen test_twee MOVLW H'02' SUBWF A_DEC, 0 BTFSS STATUS, Z GOTO test_drie MOVF DIG_2, 0 MOVWF A_SH_REG GOTO einde_van_testen test_drie MOVLW H'03' SUBWF A_DEC, 0 BTFSS STATUS, Z GOTO test_vier MOVF DIG_3, 0 MOVWF A_SH_REG GOTO einde_van_testen test_vier MOVLW H'04' SUBWF A_DEC, 0 BTFSS STATUS, Z GOTO test_vijf MOVF DIG_4, 0 MOVWF A_SH_REG GOTO einde_van_testen test_vijf MOVLW H'05' SUBWF A_DEC, 0 BTFSS STATUS, Z GOTO test_zes MOVF DIG_5, 0 MOVWF A_SH_REG GOTO einde_van_testen test_zes MOVLW H'06' SUBWF A_DEC, 0 BTFSS STATUS, Z GOTO test_zeven MOVF DIG_6, 0 MOVWF A_SH_REG GOTO einde_van_testen test_zeven MOVLW H'07' SUBWF A_DEC, 0 BTFSS STATUS, Z GOTO test_acht MOVF DIG_7, 0 MOVWF A_SH_REG GOTO einde_van_testen test_acht MOVLW H'08' SUBWF A_DEC, 0 BTFSS STATUS, Z GOTO test_negen MOVF DIG_8, 0 MOVWF A_SH_REG GOTO einde_van_testen test_negen MOVLW H'09' SUBWF A_DEC, 0 BTFSS STATUS, Z GOTO einde_van_testen MOVF DIG_9, 0 MOVWF A_SH_REG GOTO einde_van_testen einde_van_testen RETURN ; *** INSERTDIGITS: This subroutine 'inserts' the correct bits in the shift registers *** INSERTDIGITS BCF STATUS, RP0 ; Go to Bank 0 (in case we weren't there yet...) BCF STATUS, RP1 BSF INSDIG_COUNTER, H'3' ; 8 is the value, and we stick it in a counter! insdigloop CLRF PORTA ; (Remark: OE is active low, so clearing port A will enable the output) RLF SH_REG1,1 ; Check the next bit BTFSC STATUS, C ; and if it's set, then set the RA0 BSF PORTA, H'0' ; (which is connected to the DS of the first shift register). RLF SH_REG2,1 ; Now we do the same for the second shift register. BTFSC STATUS, C BSF PORTA, H'1' ; RA1 is connected to the DS of the second shift register. BSF PORTA, H'4' ; Then we give a pulse on the clock of the shift registers (SH_CP) DECFSZ INSDIG_COUNTER, 1 GOTO insdigloop BSF PORTA, H'3' ; Now we give a pulse on the clock of the storage registers (ST_CP) to show the result. RETURN ; End of subroutine ; *** Delay routine *** DELAY CLRWDT MOVLW D'255' MOVWF TIMER1 DELAY2 MOVLW D'255' MOVWF TIMER2 DECFSZ TIMER2,F GOTO $-1 DECFSZ TIMER1,F GOTO DELAY2 RETLW 0 END ; End of source code.