Файл: The quintessential PIC microcontroller (S. Katzen, 2000).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.06.2025
Просмотров: 5358
Скачиваний: 0
162 The Quintessential PIC Microcontroller
Example 6.5
Write a subroutine to evaluate the square root of a 16-bit integer located in File 26:7h. The 8-bit outcome is to be returned in the Working register.
Solution
The crudest way of doing this is to try every possible integer k from 1 upwards, generating k2 by multiplication and checking that the outcome is no more than n. A slightly more sophisticated approach is based on the relationship:
k
k2 = (2 × i) + 1
i=0
On this basis a possible structure for this function is:
1.Zero the loop count
2.Set variable I (the magic number) to 1
3.DO forever:
(a)Take I from Number
(b)IF the outcome is under zero THEN BREAK out
(c)ELSE increment the loop count
(d) Add 2 to I |
√Number |
4. Return loop count as |
That is sequentially subtract the series 1, 3, 5, 7, 9, 11…from Number until underflow occurs; with the tally of successful passes being the square
number |
SQRT |
count = 0
65 |
count = 0 |
i = 0 |
|
- |
1 |
||
64 |
count = 1 |
LOOP: |
|
- |
3 |
61count = 2
-5
56 |
count = 3 |
count = count + 1 |
number = number - i |
|||
- |
7 |
|||||
49 |
count = 4 |
|||||
- |
9 |
|||||
40 |
count = 5 |
|||||
-11 |
no |
|||||
29 |
count = 6 |
i = i + 2 |
<0? |
|||
-13 |
||||||
16 |
count = 7 |
yes |
||||
-15 |
||||||
1count = 8
-17 |
Return |
count |
||
-16 |
Terminate with square root = 8 |
|||
(a) An example |
(b) Flowchart of the process |
|||
Fig. 6.9 Finding the square root of an integer.
6. Subroutines and Modules 163
root. An example giving √65 = 8 is given in Fig. 6.9(a) using this series approach. A flowchart visualizing the task list is also given in Fig. 6.9(b).
Program 6.11 Coding the square root subroutine.
; Global declarations |
|||
STATUS |
equ |
3 |
; Status register is File 3 |
C |
equ |
0 |
; Carry flag is bit0 |
NB |
equ |
0 |
; Alternative name Not Borrow |
NUM_H |
equ |
26h |
; Number low byte |
NUM_L |
equ |
27h |
; Number high byte |
;************************************************************
;* FUNCTION: Calculates the square root of a 16-bit integer *
; * |
EXAMPLE |
: Number |
= FFFFh |
(65,535d), Root = FFh (255d) |
* |
|
; |
* |
ENTRY |
: Number |
in File |
26:7h |
* |
; |
* |
EXIT |
: Root in W. Files 26:7h and 35:6:7h altered |
* |
||
;************************************************************
;Local declarations
COUNT |
equ |
35h |
; |
The loop count |
I_H |
equ |
36h |
; |
Magic number high |
I_L |
equ |
37h |
; |
Magic number low |
; Task 1: Zero loop |
count |
|||
SQR_ROOT |
clrf |
COUNT |
||
; Task 2: Set magic |
number |
I to one |
||
clrf |
I_L |
|||
clrf |
I_H |
|||
incf |
I_L,f |
|||
;Task 3: DO
;Task 3(a): Number - I
SQR_LOOP movf |
I_L,w |
; |
Get low byte magic number |
subwf |
NUM_L,f |
; |
Subtract from low byte Number |
movf |
I_H,w |
; |
Get high byte magic number |
btfss |
STATUS,NB |
; |
Skip if No Borrow out |
addlw |
1 |
; |
Return borrow |
subwf |
NUM_H,f |
; Subtract high bytes |
|
; Task 3(b): IF |
underflow |
THEN exit |
|
btfss |
STATUS,NB |
; IF No Borrow THEN continue |
|
goto |
SQR_END |
; ELSE the process is complete |
|
; Task 3(c): ELSE increment loop count |
|||
incf |
COUNT,f |
||
; Task 3(d): Add two to the magic number |
|||
movf |
I_L,w |
||
addlw |
2 |
; IF no carry THEN done |
|
btfsc |
STATUS,C |
||
incf |
I_H,f |
; ELSE add carry to upper byte I |
|
movwf |
I_L |
||
goto |
SQR_LOOP |
||
; Task 4: Return loop count as the square root
SQR_END movf COUNT,w ; Copy into W return
164 The Quintessential PIC Microcontroller
The coding in Program 6.11 follows the task list closely. The maximum value of the loop count is FFh, as √65535 = 255. Thus a single byte at File 35h is reserved for this local variable. Similarly the maximum possible value of the magic number is 511 (1FFh) and so the two registers File 36:7h are reserved for this local variable. This of course means that Task 3(a) entails a double-byte subtraction. The coding is somewhat simplified as the high byte of I, that is I_H, is never more than 01h and so a borrow from the lower byte can be added to a copy of I_H before the high-byte subtract to return the borrow without overflow. If a borrow is generated from this high-byte subtraction the outcome is under zero and the loop is exited. Otherwise COUNT is incremented and I augmented by two. Actually the latter is always twice COUNT plus one, so COUNT is not needed. Instead, on return the 16-bit value I can be shifted once right. This divides by 2 and by throwing away the one that pops out into the carry, e ectively subtracts by one – I is always odd and so its least significant bit is always 1. Try coding this alternative arrangement.
Example 6.6
Repeat Example 5.5, which multiplies a byte by ten, but using a software stack for data storage and parameter passing. You may assume that the multiplicand byte is in memory at File 46h.
Solution
The global declarations for the subroutine of Program 6.12 and calling procedure is:
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 |
XCAND |
equ |
46h |
; Multiplicand byte |
STATUS |
equ |
3 |
; Status register is File 3 |
C |
equ |
0 |
; Carry flag is bit0 |
; The main routine |
sets up the Pseudo stack pointer (PSP) |
||
MAIN |
movlw TOS |
; Set up the PSP |
|
movwf PSP |
; to the initial Top Of Stack |
||
;..........................and so on
;Get ready to call up the X10 subroutine
movf PSP,w ; 1st point to current stack position
movwf FSR |
the stack |
|||
; Now copy multiplicand onto |
||||
movf |
XCAND,w |
; Copy multiplicand into W |
||
movwf |
INDF |
; |
and onto the stack |
|
decf |
FSR,w |
; |
Point |
down one |
call X10 ; Now call subroutine
;On return PSP is returned to original position
;and product is at PSP+3:PSP+2
NEXT_MAIN ..... ... ; Continuation of main routine
6. Subroutines and Modules 165
Program 6.12 uses the same technique as the original routine. First the multiplicand is shifted left once to multiply by two and then two further shifts multiplies by eight. The two resulting 16-bit data are then added to give the product. In the same manner as Program 6.6, the File Select Register is moved up and down to point the the appropriate datum as the
Program 6.12 Using a software stack to pass parameters and to provide a workspace. (continued next page).
;************************************************************
;* FUNCTION: Xs byte XCAND by 10 giving double-byte product *
; * |
EXAMPLE |
: 64h x 0Ah = 3E8h (100d x 10d = 1000d) |
* |
||
; |
* |
ENTRY |
: Multiplicand |
pushed into software stack at PSP * |
|
; |
* |
EXIT |
: Product_H:_L |
in PSP-3:PSP-2 |
* |
; ************************************************************
X10 |
movf |
PSP,w |
; Point FSR at current stack position |
|
movwf |
FSR |
|||
clrf |
INDF |
; Zero XCAND overflow |
||
; Now multiply by two by shifting |
16-bit XCAND left once |
|||
bcf |
STATUS,C |
; Clear |
Carry-in |
|
incf |
FSR,f |
; Point |
to the XCAND LSB |
|
rlf |
INDF,f |
; Shift |
left LSB |
|
decf |
FSR,f |
; Point |
to MSB |
|
rlf |
INDF,f |
; Shift left MSB |
||
; Add to 16-bit |
subproduct |
|||
incf |
FSR,f |
; Point to XCANDx2_L |
||
movf |
INDF,w |
; Get it |
||
decf |
FSR,f |
; Point at PROD_L |
||
decf |
FSR,f |
|||
movwf |
INDF |
; Update it with XCANDx2_L |
||
incf |
FSR,f |
; Point to XCANDx2_H |
||
movf |
INDF,w |
; Get it |
||
decf |
FSR,f |
; Point at PROD_H |
||
decf |
FSR,f |
|||
movwf |
INDF |
; Update it with XCANDx2_H |
||
; Now shift left twice more to give x8 |
||||
incf |
FSR,f |
; Point to XCANDx2_L |
||
incf |
FSR,f |
|||
incf |
FSR,f |
|||
bcf |
STATUS,C |
; Clear Carry-in |
||
rlf |
INDF,f |
; Shift left LSB |
||
decf |
FSR,f |
; Point to MSB |
||
rlf |
INDF,f |
; Shift left MSB |
||
incf |
FSR,f |
|||
rlf |
INDF,f |
; Shift left LSB |
||
decf |
FSR,f |
; Point to MSB |
||
rlf |
INDF,f |
; Shift left MSB |
||
166 The Quintessential PIC Microcontroller
Program 6.12 (continued.) Using a software stack to pass parameters and to provide a workspace.
; Add to 16-bit |
subproduct |
|
incf |
FSR,f |
; Point to XCANDx8_L |
movf |
INDF,w |
; Get it |
decf |
FSR,f |
; Point at PROD_L |
decf |
FSR,f |
|
addwf |
INDF,f |
; Update it with XCANDx8_L |
incf |
FSR,f |
; Point to XCANDx8_H |
btfsc |
STATUS,C |
; IF Carry set THEN inc XCANDx8_H |
incf |
INDF,f |
|
movf |
INDF,w |
; ELSE get it |
decf |
FSR,f |
; Point at PROD_H |
decf |
FSR,f |
|
addwf |
INDF,f |
; Update it with XCANDx8_H |
return
; ************************************************************
program progresses. The double-byte product can be accessed relative to the Pseudo Stack Pointer by the caller. Unlike Program 6.6 this PSP is not altered when pushing out the multiplicand nor in the subroutine. This is because the subroutine is a dead end in that it can never call another subroutine. Thus a new stack frame need not be formed.
Example 6.7
In order to ensure that the 7-segment decoder subroutine of Program 6.4 does not cause the PCL register to overflow when the o set is added, a programmer has used the directive org (ORiGin – see page 200) to tell the assembler to locate the subroutine at the absolute instruction address 700h – as shown in Program 6.13. When the subroutine is tested by calling from another part of the program in the store somewhere lower than 700h the system fails and performs unpredictably. What has gone wrong?
Program 6.13 The software 7-segment decoder revisited.
org |
700h |
; Start the subroutine at 700h |
SVN_SEG addwf |
PCL,f |
; Add N to PCL giving PC + N |
retlw |
b’11000000’ |
; Code for 0 |
retlw |
b’11111001’ |
; Code for 1 |
retlw |
b’10100100’ |
; Code for 2 |
retlw |
b’10110000’ |
; Code for 3 |
retlw |
b’10011001’ |
; Code for 4 |
retlw |
b’10010010’ |
; Code for 5 |
retlw |
b’10000010’ |
; Code for 6 |
retlw |
b’11111000’ |
; Code for 7 |
retlw |
b’10000000’ |
; Code for 8 |
retlw |
b’10010000’ |
; Code for 9 |