OT: PIC16 divide and/or multiply routines

Any way you want to represent it. You have to store the numbers spread over several words of memory, and manipulate them carefully.

Double and triple precision arithmetic and floating point arithmetic are all perfectly practical (if tedious and bulky). For extra credit, try arbitrary precision arithmetic, where the numbers can get as long as they need to.

Turing machines can do almost everything, though they may take a very long time, and a great deal of memory, to do it.

--
Bill Sloman, Sydney
Reply to
bill.sloman
Loading thread data ...

You don't, PIC16 chips do 8-bit addition subtraction and logic and not much else, multiplication division etc have to be implemented as subroutines. 16-bit math sub libraries are common but never needed floating point on such a small processor, if I did I'd make up my own fixed-point format (or just store as an integer multiple). When I need true floats I use a PIC18F part like the PIC18F2525 and program it in C, the free SDCC compiler works for me.

Terry

Reply to
Terry Newton

Scaled integer fixed point is the least painful way. Even so you will need to write your own multiply and divide routines.

You will have to write your own floating point library otherwise.

If you can live with approximate values then 13/8 = 1.625

--
Regards, 
Martin Brown
Reply to
Martin Brown

You can find floating point libraries for the PIC16 family. Some use the IEEE 754 representation.

It might be less painful to write the application in C if you really need floating point. You can download a free XC8 compiler that has deliberately poor optimization.

Fractional integers (integers with a radix point somewhere appropriate) are often better speed-wise, but there is little percentage in such optimizations in 2018.

--sp

Reply to
Spehro Pefhany

In addition to the good advices given above, on should also consider, if true binary floating point representation is justified.

Especially with 4 or 8 bit data word length and primitive instruction sets and/or primitive addressing modes and only a few floating point operations required, one should consider, if a full decimal to floating point and vice versa conversion is justified.

In many 1950's and 60's "commercial" (i.e. accounting) computers used "decimal" (BCD) representation internally. The same conventions is still used in calculators.

For example 1.623 can be represented as 0.16230000E+01 which could be represented in 8 bit bytes in BCD as

0x16 0x23 0x00 0x00 (0x01)

Multiplication: Integer multiply mantissas, add exponents Division: Integer divide mantissas, subtract exponents

In fact floating point addition/subtraction is harder, since you have to deal with normalization or demoralization.

If an 8 bit processor is so primitive that it doesn't have any DAA (Decimal Adjust Accumulator) instruction. it might be easier to use base 100 (0..99) to store two decimal digits in a byte, in this case

0x10 0x17 0x00 0x00 0x01
Reply to
upsidedown

99/61 is only -.003% off.
Reply to
John S

Try dividing by 61 with just shifts and adds!

Scaling by 2^n is one way to handle modest precision floats. (easier if you know the range of input/output values)

--
Regards, 
Martin Brown
Reply to
Martin Brown

Although you *can* do this - today it would make much more sense to use an ARM core that has native floating point support!

PICs are better suited to ultra low power minor control functions.

It depends how much you know about the inputs and outputs that the system has to handle. Floating point is often used as a crutch by people who don't really know what they are doing to disguise the fact.

--
Regards, 
Martin Brown
Reply to
Martin Brown

Easy, multiply by 1074. :-)

(I once wrote a one of these for AVR, for decimal conversion. The divide-by-10 operation takes only ~30 cycles, thanks to the fast hardware (8x8) multiply, and using this trick instead of long division. Yes, quotient and remainder are produced as usual. Downside: the operation had to be a full 24 bits, to maintain exact results for all inputs. That is, for a 16 bit input, it's multiplied not by 6553, but a bit more, and shifted.)

Tim

--
Seven Transistor Labs, LLC 
Electrical Engineering Consultation and Contract Design 
Website: https://www.seventransistorlabs.com/
Reply to
Tim Williams

It's a good technique.

Reply to
Clifford Heath

On a 20MHz 8 bit AVR with a hardware multiplier a 32 bit by 32 bit IEEE

754 "float" datatype multiply IIRC takes about 6 microseconds, it's not that bad.
Reply to
bitrex

It is hard to justify using some of the cruder devices in the PIC line for new designs, but I expect this is not a new design. Who wants to redesign a board and/or retest and possibly rewrite code for a new processor?

If redesign is an option I would encourage looking at the MSP340 which is a very nice low power family as well. But yes, it is hard to want to work w ith anything other than an ARM these days. They cover a wide range of capa bility although working with an application processor is so much different than working with a CM0 that the common points seem pretty limited. So I'm not clear why that is an advantage I suppose.

Rick C.

Reply to
gnuarm.deletethisbit

On Tuesday, April 24, 2018 at 3:38:19 PM UTC-4, snipped-for-privacy@downunder.com wrot e:

ssembly for pic16, for example if i wanna save a value of 1.623 in memory, how can i save it using the 36 assembly instructions in pic16?

I recall the 8080 had a "decimal add adjust" instruction (or some similar n ame) that would correct the result of an addition for adding BCD numbers in a byte. All in all, if the inputs and outputs are going to be decimal, th en in this case using BCD arithmetic might be a useful way to go. Certainl y it will be easier to debug.

I don't understand why anyone would want to write their own arithmetic rout ines for a processor. There have to be a number of sources for such code, mostly free I expect. Doesn't Microchip have floating point libraries?

Rick C.

Reply to
gnuarm.deletethisbit

That strongly depends on the requirements for the floating point. Most of the smaller ARM devices only support single precision floats in hardware wh ich have less precision than integer arithmetic. If you need double precis ion floating point, you are back to using libraries or you need to use a mu ch larger and power hungry device.

Rick C.

Reply to
gnuarm.deletethisbit

That was only possible/sensible because the status register saved the nybble carry-out.

Clifford Heath.

All in all, if the inputs and outputs are going to be decimal, then in this case using BCD arithmetic might be a useful way to go. Certainly it will be easier to debug.

Reply to
Clifford Heath

mukltiply by the reciprocal.

X >> 6 + X >> 11 + X>> 12 + X >> 15

--
     ?
Reply to
Jasen Betts

Although true relying on double precision reals to make a bad algorithm work is not good engineering practice. Most signal processing can be done in scaled integers or fixed point if you know what you are doing.

But a PIC16's memory isn't going to hold very many double precision reals anyway so I think it is a mute point.

--
Regards, 
Martin Brown
Reply to
Martin Brown

Here is a fully tested ADD, MPY and DIV routines plus associated subroutines. see comments for which routine does what.

;MATHS.ASM ;********* CLEARTRIAL_P2 CLRF TRIAL_0 CLRF TRIAL_1 CLRF TRIAL_2 RETURN ;************* TRIAL_TO_NO1_P2 MOVF TRIAL_0,W MOVWF NO1_0 MOVF TRIAL_1,W MOVWF NO1_1 MOVF TRIAL_2,W MOVWF NO1_2 RETURN ;****************** NO2_TO_NO1_P2 MOVF NO2_0,W MOVWF NO1_0 MOVF NO2_1,W MOVWF NO1_1 MOVF NO2_2,W MOVWF NO1_2 RETURN ;***************** CLEARANSER_P2 CLRF ANSER_0 CLRF ANSER_1 CLRF ANSER_2 RETURN ;************************ CLEARNO1NO2_P2 CLRF NO1_0 CLRF NO1_1 CLRF NO1_2 CLRF NO2_0 CLRF NO2_1 CLRF NO2_2 RETURN ;************************** CLEARNO1_P2 CLRF NO1_0 CLRF NO1_1 CLRF NO1_2 RETURN ;*************************** CLEARNO2_P2 CLRF NO2_0 CLRF NO2_1 CLRF NO2_2 RETURN ;*************************** MPY24_P2 ;NO1 * NO2 TO ANSER ;24*24 BIT MPY WITH 48 BIT ANSWER CALL CLEARANSER_P2

MOVLW 24 MOVWF LOOPCOUNT SHADD_24 CLRC RLF ANSER_0,F RLF ANSER_1,F RLF ANSER_2,F

;NO1 SHIFT LEFT ONE BIT INTO CARRY CLRC RLF NO1_0,F RLF NO1_1,F RLF NO1_2,F

BNC NOADD_24 ;;;;;;;;;;;;;;; ;ADD NO2 TO ANSER MOVLW ANSER_0 MOVWF FSR

MOVF NO2_0,W CALL ADD_P2 INCF FSR,F

MOVF NO2_1,W CALL ADDWFC_P2 INCF FSR,F

MOVF NO2_2,W CALL ADDWFC_P2

NOADD_24 DECFSZ LOOPCOUNT,F GOTO SHADD_24

RETURN ;***************************************** ;NO1 / NO2 TO ANSER ;RETURNS REMAINDER IN TRIAL

;24*24 DIVIDE WITH 24 BIT RESULT DIV24_P2 CALL CLEARANSER_P2 CALL CLEARTRIAL_P2

MOVLW 24 MOVWF LOOPCOUNT D24LOOP ;ANSER*2 CLRC RLF ANSER_0,F RLF ANSER_1,F RLF ANSER_2,F

;RLF NO1 INTO TRIAL CLRC RLF NO1_0,F RLF NO1_1,F RLF NO1_2,F

RLF TRIAL_0,F RLF TRIAL_1,F RLF TRIAL_2,F

;SAVE NO2 TO TEMP MOVF NO2_0,W MOVWF TEMP_0 MOVF NO2_1,W MOVWF TEMP_1 MOVF NO2_2,W MOVWF TEMP_2

;NEG TEMP COMF TEMP_0,F ;COM COMF TEMP_1,F COMF TEMP_2,F

INCFSZ TEMP_0,F ;INC GOTO OVER INCFSZ TEMP_1,F GOTO OVER INCF TEMP_2,F OVER

;NEGATED TEMP + TRIAL MOVLW TEMP_0 MOVWF FSR

MOVF TRIAL_0,W CALL ADD_P2 INCF FSR,F

MOVF TRIAL_1,W CALL ADDWFC_P2 INCF FSR,F

MOVF TRIAL_2,W CALL ADDWFC_P2

BTFSS CARRYOUT ;WAS BTFSC GOTO JSROT24

;NEW VALUE TO TRIAL MOVF TEMP_0,W MOVWF TRIAL_0 MOVF TEMP_1,W MOVWF TRIAL_1 MOVF TEMP_2,W MOVWF TRIAL_2

BSF ANSER_0,0

JSROT24 DECFSZ LOOPCOUNT,F GOTO D24LOOP

RETURN ;************************************* ANSER_TO_NO1_P2 MOVF ANSER_0,W MOVWF NO1_0 MOVF ANSER_1,W MOVWF NO1_1 MOVF ANSER_2,W MOVWF NO1_2 RETURN ;***************************** ;ADD W TO INDF SETTING CARRYIN IF CARRY ;INPUT: W INDF ;OUTPUT: CARRYIN,INDF ADD_P2 ADDWF INDF,F

BCF CARRYIN ;CARRYOUT TO NEXT STAGE BTFSC CARRYFLAG BSF CARRYIN RETURN ;*********************************** ;INPUT: CARRYIN,INDF,W ;OUTPUT: INDF,CARRYOUT,CARRYIN (CARRYOUT = CARRYIN AFTER CALCULATION) ADDWFC_P2 BCF CARRYOUT

ADDWF INDF,F SKPNC BSF CARRYOUT

BTFSS CARRYIN GOTO NOCARRYIN_P2

INCF INDF,F SKPNZ BSF CARRYOUT NOCARRYIN_P2

;CARRYOUT TO CARRYIN FOR NEXT TIME THROUGH BCF CARRYIN BTFSC CARRYOUT BSF CARRYIN

RETURN ;****************************** ;NO1 / NO2 TO ANSER ;RETURNS REMAINDER IN TRIAL

;16*16 DIVIDE WITH 16 BIT RESULT DIV16_P2 CALL CLEARANSER_P2 CALL CLEARTRIAL_P2

MOVLW 16 MOVWF LOOPCOUNT D16LOOP ;ANSER*2 CLRC RLF ANSER_0,F RLF ANSER_1,F

;RLF NO1 INTO TRIAL CLRC RLF NO1_0,F RLF NO1_1,F

RLF TRIAL_0,F RLF TRIAL_1,F

;SAVE NO2 TO TEMP MOVF NO2_0,W MOVWF TEMP_0 MOVF NO2_1,W MOVWF TEMP_1

;NEG TEMP COMF TEMP_0,F ;COM COMF TEMP_1,F

INCFSZ TEMP_0,F ;INC IF OVERFLOW DECF TEMP_1,F INCF TEMP_1,F

;NEGATED TEMP + TRIAL MOVLW TEMP_0 MOVWF FSR

MOVF TRIAL_0,W CALL ADD_P2 INCF FSR,F

MOVF TRIAL_1,W CALL ADDWFC_P2

BTFSS CARRYOUT ;WAS BTFSC GOTO JSROT16

;NEW VALUE TO TRIAL MOVF TEMP_0,W MOVWF TRIAL_0 MOVF TEMP_1,W MOVWF TRIAL_1

BSF ANSER_0,0

JSROT16 DECFSZ LOOPCOUNT,F GOTO D16LOOP

RETURN ;************************************* ADDWFCINCFSR_P2 CALL ADDWFC_P2 INCF FSR,F RETURN ;****************************** ADDINCFSR_P2 CALL ADD_P2 INCF FSR,F RETURN ;****************************** DODIGIT_P2 MOVF DIGIT,W SKPZ BSF DIGITNOTZERO

BTFSS DIGITNOTZERO RETURN IORLW '0'

GOTO LCDSOD_P2 ;************************************ ;ADD NO1+NO2 TO ANSER ;NO1 AND NO2 ARE LEFT INTACT ADD24_P2 ;

;MOVE NO2 TO ANSER MOVF NO2_0,W MOVWF ANSER_0 MOVF NO2_1,W MOVWF ANSER_1 MOVF NO2_2,W MOVWF ANSER_2

;ADD NO1 TO ANSER MOVLW ANSER_0 MOVWF FSR

MOVF NO1_0,W CALL ADD_P2 INCF FSR,F

MOVF NO1_1,W CALL ADDWFC_P2 INCF FSR,F

MOVF NO1_2,W GOTO ADDWFC_P2 ;**********************************

Reply to
Roger_the_Dodger

For the original multiply by 1.623, I'd do:

((x

Reply to
David Brown

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.