Intro Mechanical stuff Electronics Software Who we are? + useful links

      Getting numbers on the display  
      Showing or not showing anything on the display  
      Showing the correct numbers on the display  
      Score handling  
      Random numbers?  
      How to show the cowboys at random?  
      The MAIN LOOP  
      Checking the light sensors  
      Real cowboys die young...  
      Making the game faster  

Software

Several programs were written to test all the different parts of the set-up:

The different parts were combined (and adapted), resulting in our final program:

Now we'll take a closer look at all the parts of the final program.


Getting numbers on the display

This is done with the INSERTDIGITS routine. In this routine, we communicate with the two shift registers (74HC595). They are connected with the pins of port A:

To show a number, the correct bit combination has to be shifted in the register. These combinations are stored in SH_REG1 and SH_REG2 (somewhere in the program). The INSERTDIGITS routine is then responsible for shifting SH_REG1 and SH_REG2. This goes as follows:

First, port A is cleared. This ensures that the SH_CP and ST_CP (the clock pulses) are low. Then we place the first bits of SH_REG1 and SH_REG2 on the corresponding output pins (respectively RA0 and RA1). To do this, they are "right rotated", which copies the first bit to the carry bit (STATUS, C). By right rotating them, we also ensure that in the next loop cycle, the second bit will become the first bit, and so on... After setting RA0 and RA1 to the right value, a pulse is given on the SH_CP, that way, the bits are shifted in the register.

This is repeated 8 times.

After that, a pulse is given on the ST_CP. This puts the bits that were shifted inside the shift registers on the output pins of the shift register, which are connected to the 7-segment displays. (See Electronics for more information.)

go back to top


Showing or not showing anything on the display

This is quite easy to do: if you put a high on the OE of the shift registers (connected with RA2), the output pins of the shift registers are cleared (put in high-impedance state). (However, the shift register still contains its data!) If these outputs are high-impedance, the displays won't work. If you put a low on the OE, the displays will show the data that is in the shift registers.

In assembler:

    BCF     PORTA,H'2'     ; Output enable
    BSF     PORTA,H'2'     ; Output disable

go back to top


Showing the correct numbers on the display

To get the display to show what we want him to show, we simply have to check how we connected it. Well... this is how:

These are the 'names' of the leds in a 7-segment display.

 

This is how the leds are connected to the shift register.

So you can see: to display number 4, a high is needed on connections f, g, b and c. This means  we need to load the combination  01101100 in the shift register. Similar, we can define for each of the numbers 0... 9 which combination needs to be loaded in the shift register. This is done in the beginning of the program:

    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

These DIGs will be used in the CONVERTSCORE routine. To see how this happens... read on!

go back to top


Score handling

Score handling is done with three routines: INCSCORE, SHOWSCORE, CONVERTSCORE.

How INCSCORE works:

Three things are done: increasing the score, checking if the score hasn't reached 99 yet, and making the game run a bit faster. (Each time a cowboy is hit, the game should get a bit faster).

We keep track of the score in two decimals: SCORE_DEC2 for the multitudes of 10 (= left number), SCORE_DEC1 for the multitudes of 1(= right number). When a hit on a cowboy is detected, the score should increase, so we increase SCORE_DEC1. If SCORE_DEC1 is 10, it's set back to zero, and SCORE_DEC2 is increased.

Doing it this way ensures that we can easily convert the score from binary to decimal system, to load it in the display.

How CONVERTSCORE works:

We compare one of the two decimals (SCORE_DEC1 or SCORE_DEC2) with binary numbers from 00000000 to 00001001. When they're the same, the corresponding 'DIG' is loaded in SH_REG1 or SH_REG2. Because we don't want to write this routine twice, we simply run it twice, using general names (A_SCORE_DEC and A_SH_REG). CONVERTSCORE is only called from the SHOWSCORE routine.

How SHOWSCORE works:

Showscore first puts SCORE_DEC1 in A_SCORE_DEC and SH_REG_1 in A_SH_REG. Then it calls CONVERTSCORE, which converts the decimal score in an appropriate 'DIG' to show. Then the same is done for the SCORE_DEC2 and SH_REG2.

This way, in both the SH_REG variables, the right digit is stored, and ready for displaying. Then we display them by calling INSERTDIGITS.

go back to top


Random numbers?

To make the game harder, the cowboys appear randomly. To achieve this, we used the PRBS method (Pseudo Random Binary Sequence). In this method, two bits of a random sequence are EXORed (= XOR ), the whole sequence is shifted to the right, and the result of the EXOR is added on the left.

(For those who didn't remember:

Bit 1 Bit 2 Bit 1 EXOR Bit 2
0 0 0
1 1 0
0 1 1
1 0 1

)

BUT... this method generates a random series of 2n - 1 bits (with n being the number of bits in the sequence, we used 8 bits).

BUT... this method does not work with every start number and with every combination of bits. We used as a start number 10100111, and we EXORed bits zero and five (as shown in the image). To test possible combinations and start numbers, we wrote a simulation in MATLAB: behold prbs.m!!

To get it working in assembler:

    RANDOM    EQU H'30'
    BIT_0     EQU H'32'
    BIT_5     EQU H'33'
...
    MOVLW     B'10100111' ; The random start number... (arbitrary chosen)
    MOVWF     RANDOM
...
    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'

A short explanation: bit zero and bit five of the register RANDOM are copied in the 'bit zeros' of register BIT_0 and register BIT_5. Then these two registers are EXORed, and the result is placed back in BIT_5. Then bit zero of the result is placed in the carry bit (STATUS, C), and the RANDOM register is shifted one place to the right.

More information on PBRS can be found in the book 'The Art Of Control Engineering', p. 418.

go back to top


How to show the cowboys at random?

(This is done in the TRIGGER_COWBOYS routine. It is quite a long routine, so I'm going to explain all the parts separately.)

We can now generate a series of random 1's and 0's, but how do we get a random choice between the cowboys? This works as follows: the random method runs three times. Now the three first bits of RANDOM are 'refreshed'. Since it's random, there are 8 different possible combinations for these three bits. Here's what we are going to do, for each combination:

1 000 Show cowboy 1
2 001 Show cowboy 2
3 010 Show cowboy 3
4 011 Show cowboy 4
5 100 Show cowboy 5
6 101 Show cowboy 6
7 110 Do nothing
8 111 Do nothing

Now, how do we get the job done in assembler?

    MOVF      RANDOM, 0
    MOVWF     DECIDE
    MOVLW     B'00000111'
    ANDWF     DECIDE, 1

go back to top


The MAIN LOOP

If you are reading this document top-to-bottom, the time has come to discuss the main loop, so you can understand the stuff below a bit better. (If you are reading this document bottom-to-top, you will now understand what you just read below.).

The main loop is separated in two loops: preloop and mainloop. Before starting the game, preloop shows our names: 'A3' and 'robin' on the display. The original idea was to add a huge start-button, which you could press to start the game. Until the button was pressed, the preloop loop should loop. However, the huge start-button is not yet implemented, so now the program just shows the preloop with our names once, and then jump over to the mainloop. Here's a diagram of the mainloop.

As you can see, there two clocks that decrease, in a construction similar to the DELAY routine (not explained here). This causes the call to TRIGGER_COWBOYS to happen only once in a while. Also, the LDRs (light sensors) are only checked when there is a 1 on port D somewhere. Why all that trouble? Why not simply add a call to the DELAY routine? The answer is that we must constantly check if a cowboy is hit. If you would hit a cowboy during a DELAY, the signal would appear, but the pic would not read it because it's busy doing nothing.

Checking the reset bit is done to see if the game has ended yet and if we have to go back to the preloop.

Each time the TRIGGER_COWBOYS routine is called, the CLOCK2 is being reset (using DA_CLOCK_NUMBAH). For more info on this: see 'Making the game faster' (below).

go back to top


Checking the light sensors

In the main loop, the routine CHECK_LDRS is called. This routine performs a check on the 6 cowboys if he/she was hit. Here's how it works for one cowboy:

For the last cowboy, we don't have to check the first line: if none of the other cowboys was hit, and the CHECK_LDRS routine was called, it must be that the last one was hit. In the end of CHECK_LDRS, the SHOWSCORE is called to put the (increased) score in the displays.

go back to top


Real cowboys die young...

Then there is one last aspect that needs discussing: the COWBOY_AGE register. We want to show the cowboys a certain time, but not too long. That's why, the first time the TRIGGER_COWBOY routine is called, for the visible cowboys, we set the correct bit in COWBOY_AGE. (Visible cowboys are cowboys that have their corresponding COWBOY_STATS bit set.)

Then, the second time TRIGGER_COWBOY is called, we check if their COWBOY_AGE-bit is set. If so, we clear it, and also clear their COWBOY_STATS bit. This means the cowboys withdraw (without being shot by the player).

Since there are 6 cowboys, this has to be done 6 times each time the TRIGGER_COWBOY routine runs.

Here's an example how it can be accomplished in assembler...

...
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
...

Et voila....

go back to top


Making the game faster

We want to make the game faster as the player hits more cowboys, to create some sort of 'tetris'-effect. That's why, each time a cowboy is hit, and INCSCORE is called, DA_CLOCK_NUMBAH is decreased a bit. Then, when CLOCK2 is being reset (in the TRIGGER_COWBOYS), using DA_CLOCK_NUMBAH, it will be a bit smaller, so the time until the next TRIGGER_COWBOYS will decrease also.

go back to top


Well, I guess that's about all there is to say about the software part of the project...