Файл: The quintessential PIC microcontroller (S. Katzen, 2000).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 15.06.2025

Просмотров: 5313

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

6. Subroutines and Modules 151

(b)IF Carry bit is one THEN and Multiplicand to subproduct.

(c)Shift Multiplicand right once.

(d)Repeat WHILE product not zero.

4.End with 16-bit Product.

Program 6.5 declares the variables that are passed to and from the subroutine at the of the main program. Keeping all these global declarations in one part of the program and using a di erent file register for each overall global variable reduces the possibility of interaction but at the expense of rather extravagant use of scarce Data memory resources. Temporary local storage is declared within each subroutine as its need will be ‘thrown away’ after the subroutine is terminated. However, interaction can still occur in local storage where nested subroutine structures are used.

The coding follows the task list closely. The decision whether to add the left-shifted multiplicand to the subproduct is dependent on the state of the Carry flag when the multiplier is shifted right. This implements the conditional addition

product = product + (multiplicand<<n) × bit n

Rather than implementing this shift and conditional add process eights times, the summation loop is terminated whenever the multiplier residue is zero. This means that the execution time of the subroutine is variable, depending on the bit pattern of the multiplier. The worst-case scenario is when the multiplier is 255 (11111111b). This takes 142 cycles including the two cycle call.

In order to use this subroutine, the caller copies the multiplicand into File 20h and multiplier into File 21h. On return, the 16-bit product can be read at File 2E:Fh. As an example, consider that the bytes located at File 42h and File 46h are to be multiplied.

movf

42h,w

; Get Number 1

movwf

20h

; and copy into

MULTIPLIER

movf

46h,w

; Get Number 2

movwf

21h

; and copy into

MULTIPLICAND

call

MULT

; Go to it!

; On return the

product is now in File 2E:Fh

Most MPU/MCUs have software stacks which in addition to saving subroutine return addresses allow the programmer to push and pull data to and from memory to pass information between caller and subroutine. As the stack is a dynamic storage entity, growing where necessary to accommodate these passed and temporary variables and shrinking again when the subroutine terminates, this clearly is an e cient method of memory allocation. Furthermore, each call outwards in a nested structure opens a new stack frame for this dynamic storage as an extension to the


152 The Quintessential PIC Microcontroller

Program 6.5 The byte multiplication subroutine.

; Global declarations

STATUS

equ

3

; Status register is File 3

C

equ

0

; Carry flag is bit0

Z

equ

2

; and the Zero flag is bit2

MULTIPLIER

equ

20h

; Multiplier byte

MULTIPLICAND

equ

21h

; Multiplicand byte

PRODUCT_L

equ

2Eh

; Low byte of the product

PRODUCT_H

equ

2Fh

; High byte of the product

;The MULT subroutine

;************************************************************

; * FUNCTION:

Multiplies two bytes to give a 2-byte

product

*

; * EXAMPLE :

MULTIPLICAND = 10h, MULTIPLIER = FFh

*

; * EXAMPLE

:

PRODUCT_H:PRODUCT_L = 0FF0h (16 x 255

= 4080d

*

; * ENTRY

:

MULTIPLIER = File 20h, MULTIPLICAND =

File 21h *

; * EXIT

:

PRODUCT_H = File 2Eh, PRODUCT_L = 2Fh

*

; * EXIT

:

MULTIPLIER, MULTIPLICAND altered

*

; * EXIT

:

W, Status and MULTIPLICAND_H = File 30h altered*

;************************************************************

;Local declarations

MULTIPLICAND_H equ 30h

; Task 1: Zero double-byte product MUL clrf PRODUCT_L

clrf PRODUCT_H

; Task 2: Extend multiplicand to 16 bits clrf MULTIPLICAND_H

;Task 3: DO

;Task 3A: Shift multiplier right once

MUL_LOOP bcf

STATUS,C

;

Clear carry

rrf

MULTIPLIER,f

; Task 3B:

IF Carry == 1 THEN

add multiplicand to product

btfss

STATUS,C

;

IF C == 1 THEN do addition

goto

MUL_CONT

;

ELSE skip this task

movf

MULTIPLICAND,w

;

DO addition

addwf

PRODUCT_L,f

;

First the low bytes

btfsc

STATUS,C

;

IF no carry THEN do high bytes

incf

PRODUCT_H,f

;

ELSE add carry

movf

MULTIPLICAND_H,w ;

Next the high bytes

addwf

PRODUCT_H,f

; Task 3C:

Shift multiplicand

right once

MUL_CONT bcf

STATUS,C

;

Zero Carry-in

rlf

MULTIPLICAND,f

rlf

MULTIPLICAND_H,f

; WHILE multiplier not zero

movf MULTIPLIER,f

;

Test multiplier for zero

btfss

STATUS,Z

goto

MUL_LOOP

;

IF not THEN go again

return

;

ELSE finished


6. Subroutines and Modules 153

stack. in this way the possibility of overlap between variable storage when using nested subroutines is virtually eliminated.

High-level languages, such as C (see Chapter 9) are based around this stack model, which allows the creation and passing of variables only restricted by the amount of data memory that can be allocated to this stack.

The downside to this approach is the extra CPU resources necessary to support the creation and maintenance of the stack. One or more dedicated address registers or stack pointers are normally provided and address modes that facilitate access to variables in these stack frames are needed for e cient working. Even then, the outcome is normally slower and coding is longer than models based on fixed memory allocations.

The PIC CPU does not explicitly support a software stack. However, it is possible to simulate such a structure using Indirect addressing with the File Select Register (FSR = File 4) and INDirect File (INDF = File 0) – see page 109. As there is no stack pointer register per se, in the code fragment below the main routine has allocated File 0Ch as a Pseudo Stack Pointer, which we call PSP.

; Global declarations

PSP

equ

0Ch

; Holds the Pseudo Stack Pointer

TOS

equ

2Fh

; File 2Fh is the initial Top Of Stack

INDF

equ

0

; INDirect File

FSR

equ

04

; File Select Register

STATUS

equ

3

; Status register is File 3

C

equ

0

; Carry flag is bit0

Z

equ

2

; and the Zero flag is bit2

MULTIPLIER

equ

46h

; Multiplier byte

MULTIPLICAND equ

42h

; Multiplicand byte

MAIN

; In the beginning

set up Top Of Stack

movlw

TOS

;movwf

PSP

; which is stored in File 0Ch

;

; Sometime later when ready to call subroutine

movf

PSP,w

; Point FSR to top of stack frame

movwf

FSR

; which is held in the PSP

movf

MULTIPLICAND,w

; Push multiplicand out into stack

movwf

INDF

decf

FSR,f

movf

MULTIPLIER,w

; Likewise for the multiplier

movwf

INDF

decf

FSR,f

call

MUL

; Go to it

; Continue on with

the product available at FSR-3:FSR-4

The programmer also has to set aside a block of Data memory to hold the various stack frames. Here we are specifying that the Top Of Stack


154 The Quintessential PIC Microcontroller

(TOS) address is File 2Fh. If the range File 2fh–0Dh is kept clear of absolute allocations then a total of 35 bytes is available for the stack. As the hardware stack holds the subroutine return address, all locations in the simulated software stack can be used for variable passing and local storage. However, if an 8-deep subroutine nest is going to be implemented then a bigger slice of available storage may well be necessary. The software stack is initialized by moving the literal 2Fh, named TOS, into the file holding the Pseudo Stack Pointer.

As an example, consider a stack-oriented version of the multiplication subroutine of Program 6.5. A view of the software stack from the perspective of this new coding is shown in Fig. 6.8. Based on this diagram, in order to call up this subroutine the following procedure has to be implemented:

1.Push the Multiplicand and then Multiplier into the stack frame and call the subroutine.

2.Push zero into the next byte in the frame, which is being used for local storage.

3.Push zero out twice more to create an initialized hole for the two bytes to return the product.

The following code fragment shows how item 1 above is coded.

(a)Transfer the contents of the Pseudo Stack Register to the FSR. This means that the FSR now points to the top of the new stack frame. If the subroutine is a first-level call (that is not nested from another subroutine) then this will be 2Fh in our example.

Entry PSP

Top Of Frame

1: Before call

FSR

2: After call

FSR

3: Core of subroutine

FSR

4: New PSP

Multiplicand

Multiplier

Multiplicand_H

Product_L

Product_H

TOF

TOF-1

TOF-2

TOF-3

TOF-4

Fig. 6.8 The stack frame viewed from the perspective of subroutine MUL_S.

6. Subroutines and Modules 155

(b)Copy the Multiplicand from memory (we assume it is at File 46h as in our last example) into W and then indirectly into the frame using INDF as the target. Decrementing the FSR completes the push action.

(c)In a similar manner, the Multiplicand is pushed into the stack.

(d)Call the subroutine.

; (a)

movf

PSP,w

; Copy current top of stack frame address

movwf

FSR

; into the File Select register

; (b)

movf

MULTIPLICAND,w

; Push the Multiplicand into the stack

movwf

INDF

; by copying the datum out

decf

FSR,f

; and decrementing the FSR

; (c)

movf

MULTIPLIER,w

; Push the Multiplier into the stack

movwf

INDF

; by copying the datum out

decf

FSR,f

; and decrementing the FSR

; (d)

call

MUL_S

; Call the subroutine

Coding of the subroutine MUL_S is given in Program 6.6. This implements items 2–4 of Fig. 6.8. Firstly, MULTIPLICAND_H, the temporary storage of the multiplicand overflow, is zeroed and then zero is pushed into the next two locations to create the initial value for the product. In item 4 the Pseudo Stack Pointer is reset to point to the next free byte below the frame. In this way, should the subroutine wish to call another, then there will be a new frame available for that next-level storage with the new TOF beginning just below the old frame. These two instructions may be omitted in this case as there are no further nested call outs, although Task 3C will have to be altered. This is done in Example 6.6.

The core of the subroutine, that is Task 3, is similar to Program 6.5 but the FSR has to be moved up and down the frame to access the appropriate level. The only non-obvious use of the FSR is at Task 3C. As there are two ways into this routine, depending on whether the shifted multiplicand is added to the product or not, the state of the FSR is unknown. It can however be reset from the PSP which is pointing to just below the frame at this point. By adding five to this PSR value, the FSR will always point to MULTIPLICAND.

Finally the subroutine ‘cleans up’ the stack by updating the Pseudo Stack Pointer to its previous value. In this case this is done by adding five, but in general by adding the frame depth n.

Program 6.6 requires 45 instructions as compared to 20 in Program 6.5. Its worst-case execution time of 274 cycles also compares unfavorably


156 The Quintessential PIC Microcontroller

with 142 cycles. Thus in all respects except reusability and robustness this stack-based model is clearly inferior. It may be more economical in

Program 6.6 Implementing a byte multiply using a stack model. (continued next page).

; ************************************************************

; * FUNCTION:

Multiplies two

bytes to give a

2-byte product

*

; * EXAMPLE :

MULTIPLICAND =

10h, MULTIPLIER

= FFh

*

; * EXAMPLE

:

PRODUCT_H:PRODUCT_L = 0FF0h (16 x 255 = 4080d

*

; * ENTRY

:

MULTIPLICAND =

PSP, MULTIPLIER = PSP-1

*

; * ENTRY

:

FSR points to one below MULTIPLIER

*

; * EXIT

:

PRODUCT_H = PSP-3, PRODUCT_L = PSP-4

*

; * EXIT

:

W, Status

*

;************************************************************

;Tasks 1 & 2: Extend multiplicand and zero double-byte product

MUL

clrf

0

decf

FSR,f

; FSR ---> PRODUCT_L

clrf

0

decf

FSR,f

; FSR ---> PRODUCT_H

clrf

0

decf

FSR,w

; Now reset Pseudo Stack Pointer

movwf

PSP

; to Bottom Of Frame

;Task 3: DO

;Task 3A: Shift multiplier right once

incf

FSR,f

incf

FSR,f

incf

FSR,f

; FSR ---> MULTIPLIER

MUL_LOOP bcf

STATUS,C

; Clear carry

rrf

0,f

; Task 3B: IF Carry == 1 THEN add multiplicand to product btfss STATUS,C ; IF C == 1 THEN do addition

goto MUL_CONT ; ELSE skip this task

incf

FSR,f

; FSR ---> MULTIPLICAND

movf

0,w

; DO addition

decf

FSR,f

decf

FSR,f

decf

FSR,f

; FSR ---> PRODUCT_L

addwf

0,f

; First the low bytes

decf

FSR,f

; FSR ---> PRODUCT_H

btfsc

STATUS,C

; IF no carry THEN do high bytes

incf

0,f

; ELSE add carry

incf

FSR,f

incf

FSR,f

; FSR ---> MULTIPLICAND_H

movf

0,w

; Next the high bytes

decf

FSR,f

decf

FSR,f

; FSR ---> PRODUCT_H

addwf

0,f