Файл: 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 |
|