Assembly language Part 4 (PIC Microcontroller)

Once the program has been successfully been linked it may be simulated. Here the PC models the PIC’s instruction set and I/O ports and allows the user to reset the (simulated) PIC, set break points, single step or run continuously. During this process user-selected file registers or the whole of Data memory can be monitored, as can execution time. Of course simulated execution time by the PC will be several orders of magnitude slower than a real PIC.

MPLAB window showing files selected=

Fig. 8.6 MPLAB window showing files selected to assemble, link and simulate Program 8.4.

Figure 8.7 shows the end result of a simulation of our example. In the Watch_1 window are shown the initial values for NUM_1 and NUM_2 of 05h and 08h. Values of variables in this window can be set up by the programmer by double-clicking on the variable address. The outcome tmp936_thumb[2][2]_thumb(to the nearest integer) is seen in the Watch window as the value of RMS. The Watch window is set from the Window menu. Just under this window is the Stop-watch window, which shows that the program took 292 cycles to execute with the given data, which for a 4 MHz crystal is 292 /is in real time. After resetting under the Debug menu, abreakpoint is set up at the last instruction in main.asm. This movwf RMS instruction is shown in the screen snapshot greyed out. The program can be ‘run’ by clicking on the Green Traffic Light icon m in the Simulation tool bar (second icon from the left) or from the Debug menu. The Red equivalent t?J icon next left can be used to pause a run at any time. The t=» icon is used to single step one instruction at a time.


MPLAB screen shot showing the programs selected=

Fig. 8.7 MPLAB screen shot showing the programs selected in Fig. 8.6 being simulated.

Simulation will not catch all problems, especially those involving complex hardware/software interaction. However, over 95% of problems are caused by purely software design faults and simulation is good technique for testing and debugging such code.

For example, our code will fail if the total NUM_12 + NUM_22 > 65, 535, as SUM is only double-byte – see SAQ 8.5. Debugging should always at a first iteration try largest and smallest values of variables. However, correct operation is by no means guaranteed by this test for all possible combinations and sequences of input.

Finally, we review some general information specific to Microchip-compatible assemblers as an aid to reading programs in the rest of the topic:

The latter is the prefix used in the C language to denote this number base.

The assembler normally defaults to this base so some programs show no hexadecimal indicators. However, it is better not to rely on the default behavior.

- Binary: Denoted by a leading b with a quote delimited number; eg. b’01000001′.

- Decimal: Denoted by a leading d with a quote delineated number; eg. d’65′ or a leading period prefix; eg. .65.

- ASCII: Denoted by a quote delimited character; eg. ‘A’.

• Label arithmetic.

- Current position: $; eg. goto $+2.

- Addition: +; eg. goto LOOP+6.

- Subtraction: -; eg. goto LOOP-8.

- Multiplication: 1; eg. subwf LAST*2.

- Division: /; eg. subwf LAST/2.

- Current position: $; eg. goto $+2.

• Directives.

- org: Places the following code in Program memory starting from the specified address; eg. org 0100h. Defaults to 000h. Can only be used for absolute assembly.

- code: Counterpart to org for relocatable assembly. The actual address of the code stream is defined in the linker’s command file. More than one code stream may be defined in the command file and in this case its name appears in the label field; eg. SUBROUTINES code.

- equ: Associates a value with a symbol; eg. PORTB equ 06. The #define directive may be used instead; #define PORTB 06.

- cblock – endc: Used in absolute assembly to allocate program variables in Data memory; eg.

tmp938_thumb[2][2][2]

The address is optional after the first cblock use. udata: Counterpart to cblock for relocatable assembler. The start address for this Data memory stream is in the linker’s script file. There may be more than one Data stream defined in this script file in which case its name is published in the label field; eg.

tmp939_thumb[2][2][2]

- udata _ovr: OVeRlay Uninitialized DATA is similar to udata but the linker tries to reuse File registers for the specified named variables.

- res: Used with udata to REServe one or more bytes for a variable in the Data stream.

- extern: Publishes the named variables as defined outside the file, to be subsequently resolved by the linker.

- global: Publishes the named variables that have been defined (that is space reserved) in the file and that are to be made visible to the linker.

- macro – endm: Used to allow the specified enclosed sequence of instructions to be replaced by a new macro instruction; eg.

tmp940_thumb[2][2][2]

adds the literal N to the specified register file file. For example, to add five to File 20h the programmer can use the invocation Addf 5,20h

- include: Used to include the specified file at this point; for example include “myfile.asm”.

- end: Normally the last line of an assembly-level source file. Tells the assembler to ignore anything following.

Examples

Example 8.1

The following routine effectively exchanges the byte contents of W and a file register F without needing an additional intermediate file register.

tmp941_thumb[2][2][2]

where ” denotes eXclusive-OR.

Wrap the given code within a macro to generate a new instruction Exgwf F where F is the designated file register.

Solution

tmp942_thumb[2][2][2]

Note that this macro instruction will alter the C flag according to the outcome of the last instruction.

Example 8.2

The PIC18CXXX family have an instruction bnc (Branch if No Carry) which transfers the program execution to the specified destination if the Carry flag is zero. Devise a macro instruction to simulate this for the 12-and 14-bit core families.

Solution

The code fragment below follows the macrobut with the C flag replacing the Z flag.

tmp943_thumb[2][2][2]

Example 8.3

Macros may be nested, that is a macro may use other macros in its definition. For example, consider a macro to create a countdown process that initialises a given GPR and then decrements to zero. Assuming that the macro Movlf has already been defined:.

Solution

One possible solution is:

tmp944_thumb[2][2][2]tmp945_thumb[2][2][2]

The specified file register is firstly initialized to literal using the macro Movlf. The actual countdown uses the decfsz instruction to both decrement the contents of count_file and break out of the loop on zero.

Thus the invocation Countdown d’100′,40h will initialize File 40h to decimal 100 and decrement to zero.

Note that both the Working register and STATUS are altered by this macro as well as the target GPR. Side effects are a hazard in using macro instructions, especially if the macro has been designed by someone else and hidden in an include file. At the very least assume that W and STATUS are altered unless known otherwise. Altering banks in a macro is also potentially hazardous.

The macro label is qualified with the local directive to ensure that each time a macro is used it does not inject C_LOOP into the assembler’s symbol table. If not so qualified then an Address label duplicated assembler error will occur.

Example 8.4

The PIC16F84 is unusual in that its GPRs are mirrored across both data banks – see Fig. 4.6.More commonly, different banks hold unique GPRs. For example, the PIC16C74 has 96 GPRs in BankO spanning File 20h- 7Fh and another 96 in Bank 1 spanning File 80h- FFh – see topic B. These two banks are not images; thus for instance, File 60h in BankO is not the same as File E0h.

In order to select a file register in Bank 1, the RP0 bit must be set to 1 as described. For example; to copy W into File E0h we have:

tmp946_thumb[2][2][2]

When using a relocatable assembler, the programmer will not necessarily know which bank the linker has placed a variable. Furthermore, as the suite and mix of component source files changes, the bank may switch back and forth as different phases of the project evolve!

To get around this problem, Microchip-compatible assemblers provide the banksel directive. This automatically keeps track of the location of the named variable and issues code to make the appropriate change-over. Show how this directive should be used when storing the decimal literals 1, 10, 100 in three GPRs called var_0, var_1 and var_2 respectively.

Solution

A possible sequence of instructions is shown below. The directive issues either bsf STATUS,RP0 or bcf STATUS,RP0 instructions as appropriate before the following instruction. For PICs with four File register banks,11 both RP1:RP0 bits will be altered by this directive.

tmp947_thumb[2][2][2]

Where Indirect addressing is used for 4-bank PICs the IRP bit in STATUS must be 0 for Banks0:1 and 0 for Banks2:3.This can be implemented using the bankisel directive in a similar manner to banksel.

Self-assessment questions

8.1 Design macros to simulate the PIC18XXX family relative conditional Branch instructions bc (Branch on Carry) and bz (Branch if Zero).

8.2 Design a macro of the form Mul XPLIER,XCAND,PRODUCT that will implement the function PRODUCT:2 = VAR1 x VAR2. Hint: Check Program 6.5.What do you think are the advantages and disadvantages of using a macro instead of a subroutine in a long implementation like this?

8.3 The goto and call instruction op-codes use an 11-bit address suitable to transfer anywhere within a 2 Kbyte Program store; as illustrated in Fig. 5.4.As shown in this diagram, the 13-bit Program Counter is overwritten by the instruction’s 11-bit address together with PCLATH[4:3] (PCLATch High byte) File 0Ah to give a 13bit destination address.

Some mid-range PICs have a 4 or 8 Kbyte Program store, such as the PIC16C74 and PIC16F876 respectively.The programmer needs to manipulate these bits before the goto.For instance, for the PIC16C74 to call a subroutine beginning at FRED which is at address 0B00h we have:

tmp948_thumb[2][2][2]

To allow the assembler to alter the PCLATH[4:3] bits as appropriate, Microchip compatible assemblers have a directive pagesel which must precede any goto or call instruction; rather in the manner of the banksel directive of Example 8.4. Show how you could use this to support a series of calls to subroutines named SUB_0, SUB_1 and SUB_2.

8.4 The banksel approach to selecting a bank is inefficient in that an extra instruction is issued even if the PIC is already in the correct bank. Consider how in a time- or space-critical subroutine this inefficiency can be avoided.

8.5 To be safe, determine a maximum value that NUM_1 and NUM_2 should not exceed to guarantee correct working for our program to calculate the root mean square of the two variables.

8.6 Rewrite the routine main.asm of Program 8.2 and the subroutine root2.asm of Program 8.4 to allow for all values of NUM_1 and NUM_2. This will require a 3-byte sum and square root function.

8.7 The following routine based on the macro instruction Movlf of Example 8.3 does not work as intended. COUNT is altered seemingly at random and not consistently with the desired literal 32. Why is this?

tmp949_thumb[2][2][2]

8.8 A programmer with expertise in the Motorola 68HC05 MCU has been converted to the PIC family and wishes to design macros to simulate, amongst others, the following 68HC05 instructions. Note that the Accumulator register in the 68HC05 family is the equivalent to the Working register of the PIC. Ida memory

LoaD Accumulator with data from memory. Ida #data

LoaD Accumulator with literal data. sta memory

STore Accumulator data into memory.

tst memory

TeST memory for zero

tsta

TeST Accumulator for zero

Code suitable macros. Why do you think this approach might not be such a good idea?

Next post:

Previous post: