The Instruction Set Part 3 (PIC Microcontroller)

The PIC 12/14-bit cores have two instructions which can shift a datum byte in a file register one place left or right. Both rlf and rrf are known as Circular or Rotate instructions. These shift left or right respectively with the incoming bit injected in from the Carry flag and outgoing bit popped out into the same C flag. This circular action is emphasized in Fig. 3.7.

tmp18331_thumb[2]_thumb

Two possible scenarios are:

tmp18332_thumb[2]_thumb

One use of the shifting operation is to bitwise examine a datum. Suppose you want to determine the position of the leftmost logic 1 bit in File 20h, with this information being put in the Working register. For example, if the pattern is:

tmp18333_thumb[2]_thumb


This can be realised by continually shifting the pattern under investigation right, counting the number of times until the residue is zero. The coding given in Program 5.4 uses the Working register as a counter. The data byte in File 20h is successively shifted right and the count incremented. As the Carry flag is cleared each time before the shift, logic 0s are brought in from the left.7 Eventually the residue will become all zeros

tmp18334_thumb[2]_thumb

Program 5.4 Shifting to find the highest set bit.

tmp18335_thumb[2]_thumb

Shifting right pops out the rightmost bit into the Carry flag. Replacing btfsc STATUS,Z by btfsc STATUS,C would determine the position of the rightmost bit. In many situations repetitively shifting into the Carry flag can be used to examine the data on a bit by bit basis. For instance, we could modify our program to totalize the number of set bits in the byte, as in Program 5.5.

Program 5.4 did not distinguish between no bits set (00000000b) and bit0 set (00000001 b). How could you modify the program to do so?

The Rotate instructions can be used for multiple-precision shifting operations. Remembering that a Rotate takes in the Carry bit and in turn saves its ejected bit in C, consider as an example a 24-bit word stored in the data store attmp18336_thumb[2]_thumb

which can be shifted right once by the sequence:

tmp18337_thumb[2]_thumb

Consider that we wish to count the number of bits set to 1 in this triple-byte datum. One solution is shown in Program 5.5. Here the 24-bit word is shifted right (it could equally well have been left) until the word is all zero with the state of the Carry flag controlling the incrementation of the Working register. The multiple-precision zero test is implemented by testing each byte in turn for zero and going back to the top of the loop if any byte test gives a non-zero outcome.

Program 5.5 Triple-precision shifting to find the number of set bits.

Program 5.5 Triple-precision shifting to find the number of set bits.

Shifting can be used to multiply and divide data by powers of two. For example, to divide by eight shift left three times:

tmp18339_thumb[2]_thumb

where we are assuming that the Carry flag is cleared before each shift. In general shifting n places right gives a 2n division and similarly to the left gives multiplication by the same factor.

As an example consider that the byte in File 22h (called MULTIPLICAND) is to be multiplied by 3 to give a 2-byte product in File 24:5h (PRODUCT_H and PRODUCT_L respectively).

The implementation of Program 5.6 relies on factoring x3as x2 + x1. The former is implemented by shifting a 16-bit extension of the byte multipicand (upper byte zeroed) once left. The single byte multiplicand is then added to the 2-byte subproduct to give the desired outcome. Example 5.5 gives the more complex case of multiplying by ten. Program Counter instructions

The instructions listed in Table 5.4 modify in some way the setting of the Program Counter. The most elementary of these is nop. No Operation does not alter the state of the system in any way, but the PC will increment as a consequence of the instruction code being fetched from the Instruction store. Thus its sole outcome is [PC] <- [PC] + 1. This takes one bus cycle, so its main use is to implement a short delay, 1 ^s for a 4 MHz clock rate. For example, to pulse Port A’s pin low for 2 ^s and then high we have:

Program 5.6 Multiplying by three.

Program 5.6 Multiplying by three.

tmp18341_thumb[2]_thumb

with the assumption that bit 0 of Port A has been set up as an output and that bit0 (pin RA0) was high before entering the routine.

The goto instruction is an absolute jump instruction allowing the program to transfer to the specified instruction anywhere in the Program store. The process has been described in Fig. 5.4.

The remaining four instructions can skip over the following command if some condition is met. The pair decfsz (DECrement File and Skip on Zero) and incfsz (INCrement File and Skip on Zero) augment the specified file contents and then if the outcome is zero the PC is further incremented. Strangely, the Z flag is not affected by these instructions.

A typical use for these instructions is to count the number of passes through a loop. For example, suppose it is necessary to pulse Port A pin RA0 low 20 times.

Table 5.4: Program Counter instructions.

: Program Counter instructions.tmp18343_thumb[2]_thumb

Notice the assembler notation d’20′ for decimal 20. This is equivalent to 14h but more readily understood by the programmer.

The btfsc (Bit Test File and Skip if Clear) and btfss (Bit Test File and Skip if Set) instructions have been used extensively in programs both here and in topic 3. Besides their obvious use in changing the program flow based on the state of a specified bit in any register file, they allow decisions to be make on the state of the various flags in the Status register. Thus in Program 5.5 the series of btfss STATUS,Z (or the more unreadable btfss 3,2) instructions enable the program loop to be exited when the Z flag is set, that is on a series of zero outcomes. Similarly btfsc STATUS,C allows the count action to be skipped over when the C flag is clear. Neither instruction affects the flags.

All four skip instructions take two cycles to execute when the skip occurs but only one when the condition is not met. As described in Example 4.1,the former situation needs to flush the instruction pipeline. This is to reflect the fact that the pre-fetched instruction code sitting in stage one of the pipeline is not going to be the next instruction executed. This accounts for the additional bus cycle execution time as the true destination instruction has now to be fetched.

Examples

Example 5.1

Code a program to decrement a 2-byte number at File 26:27h ordered as high:low byte, remembering that decf does not alter the Carry/Borrow flag.

Solution

The task list to implement this job is:

1. IF the least significant byte in File 27h is zero THEN decrement the most significant byte.

2. Always decrement the least significant byte.

Program 5.7 gives one possible implementation based directly on this algorithm. Extension to an n-byte word is obvious.

Program 5.7 Double-precision decrement.

Program 5.7 Double-precision decrement.

Example 5.2

Some early computers used a bi-quinary code to represent BCD digits. This is a 7-bit code with only two bits set to one for any combination:

tmp18345_thumb[2][2]

Although this is highly inefficient (with only ten out of a possible 128 code combinations being used as compared to the 16 combinations of the 4-bit natural code) it does have the advantage that it is very easy to determine when an error has occurred. Determine an error-detection routine to check the byte in File 20h. Assume that the most-significant bit is zero. If an error occurs then the Working register is to be set to FFh, otherwise zero.

Solution

All we need to do here is to determine when there are more or less than two bits set to one. Based on this approach we have the task list:

1. Count the number of ones in the bi-quinary byte.

2. ZeroW.

3. IF count is not two THEN make FFh to signal an error.

Program 5.8 shows a possible coding implementing this algorithm. Here the loop continually shifts the bi-quinary byte left until the residue is zero. When the carry bit is set, the bit count is incremented. On exit from the loop, two is subtracted from the bit tally after moving into W. If it is zero then the routine is completed and the 00h setting of W shows a correct outcome. Otherwise FFh is placed in W to show an error situation. This is equivalent to decimal -1 and is traditionally used to note an error situation. As there are only ten legal combinations out of 128 possibilities used in this code the likelihood of an undetected error is rather small.

Next post:

Previous post: