1

I am a rookie in assembly programming and I am currently doing a project using a temperature sensor on MPLAB and it's values to be displayed on a LCD.

In order to convert the raw values (0 to 255 based on the output voltage) from the sensor to the LCD I need to do an affine function (8x+1000)/100. But here is the problem, since my microprocessor is in 8 bit, I cannot divide by 100.

Here is the code :

    
    MOVLW d'50'
    MOVWF CAN_Val_Brut;----------------------------------MULTIPLICATION
    clrf CAN_Val_Dec
    clrf CAN_Val_Decp1
    movlw .8
    movwf mpy_cnt
    movlw .8
    bcf STATUS,C
mpy rrf CAN_Val_Brut,f
    btfsc STATUS,C
    addwf CAN_Val_Decp1,f
    rrf CAN_Val_Decp1,f
    rrf CAN_Val_Dec,f
    decfsz mpy_cnt,f
    goto mpy        
;-------------------------------------------------------------ADDITION

;------Je met 1000 dans a et aplus1----
movlw .3;00000011
movwf aplus1
movlw .232;11101000
movwf a

movf a,w
addwf CAN_Val_Dec,f
btfsc STATUS,C
incf CAN_Val_Decp1,f
movf aplus1,w
addwf CAN_Val_Decp1,f

;-----------------------------------------------DIVISION
    movlw .8
    movwf dvy_cnt
    movlw .100
    bcf STATUS,C
dvy 
    btfsc STATUS,C
    addwf CAN_Val_Decp1,f
    rlf CAN_Val_Decp1,f
    rlf CAN_Val_Dec,f
    decfsz dvy_cnt,f
    goto dvy

If someone have a code for 2x8bits division I am glad to take a look :)

Thank you for your help !

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Auster
  • 11
  • 2
  • 1
    The same way 32-bit math is done in 16-bit CPUs or 64-bit math is done in 32-bit CPUs, you use two 8-bit registers to store the 16-bit value and add each part separately with carry, just like how you do math on paper. [How did 8 bit processors perform 16 bit arithmetic?](https://retrocomputing.stackexchange.com/q/6640/1981), [Can an 8 bit microcontrollers use a 32 bit Integers? (and the other way around)](https://stackoverflow.com/q/49093167/995714), [How does a 32 bit processor support 64 bit integers?](https://stackoverflow.com/q/23038451/995714) – phuclv Apr 08 '22 at 13:02
  • 1
    That said, you should use C or C++ code for this. Why do you even write assembly these days? Unless that part is really the hot spot in the loop and you've benchmarked it. The compiler will handle big int math for you. It's very easy to do big int additions, and a little bit difficult for multiplication, but divisions are much much harder. You can [convert division by a constant like 100 to a multiplication though](https://stackoverflow.com/q/41183935/995714), but you should still let the compiler do that – phuclv Apr 08 '22 at 13:04
  • 2
    See how the compiler generates code for (8x+1000)/100: https://godbolt.org/z/P1z4W7qjn – phuclv Apr 08 '22 at 14:03
  • 1
    @phuclv Hi, Thank you for your reply, I am using assembly for this project because our teacher want us to understand how the cpu really works. I checked the compiler you provided but unfortunately I didn't code in this language, as a major fact I don't even know what compiler I am coding in. All I know is this is Asm on a pic 16f876A and I am coding in MPLAB IDE 8.92. – Auster Apr 08 '22 at 15:05
  • If you have 256 bytes of ROM to spare, you could just make a lookup table and be done with it. That may or may not satisfy your teacher, of course. – Nate Eldredge Apr 09 '22 at 17:02

1 Answers1

3

My 1st step would be to realize that (8x + 1000)/100 is the same as 8x/100 + 1000/100, or x/12.5 + 10 or x*0.08 + 10.

Of course integer maths doesn't like 0.08. We can convert it to shifts and additions using integers. In binary, 0.08 is 0.00010100011110101110000101000111...b. This means that we can convert x*0.08 into something like x*0.0001b + x*0.000001b + x*0.0000000001 + ..., where the multiplications can be done with right shifts. This gives us something like x>>4 + x>>6 + x>>10 + x>>11 + x>>12 + x>>13 + x>>15 ... + 10.

There are 2 problems with this. The first problem is rounding - e.g. if x is an 8-bit value, then x>>10 will be zero (all of the bits in x will be shifted out), which will increase "error due to rounding". The second problem is that 0.08 in binary is an infinite number of bits, so you must choose where to stop and must have some precision loss.

For the 2nd problem, for your purposes (where x is a relatively imprecise number to begin with) 0.000101001b (or 0.0001010001111 rounded up a little) is likely to be plenty of precision. This gives us x>>4 + x>>6 + x>>9 + 10.

For the first problem, we can improve precision by doing addition before shifting. x>>9 + x>>6 + x>>4 + 10 is the same as x>>3>>6 + x>>6 + x>>4 + 10 or (x>>3 + x)>>6 + x>>4 + 10 or (x>>3 + x)>>2>>4 + x>>4 + 10 or ((x>>3 + x)>>2 + x)>>4 + 10.

For an example (and to check that the results are almost identical to the original formula) we can test it with values of x while being careful to do integer operations that truncate. For x = 255 we get (8*255 + 1000)/100 = 3040/100 = 30 and ((255>>3 + 255)>>2 + 255)>>4 + 10 = ((31 + 255)>>2 + 255)>>4 + 10 = (71 + 255)>>4 + 10 = 30 + 10 = 30.

Note that the intermediate values will require 1 more bit than x has; or if x is 8 bits then you will need 9 bits to store something like x>>3 + x. This is easily solved by using the carry flag as the extra (ninth) bit. Specifically, the formula does the addition of a pair of 8 bit values to get "carry + 8 bit" result, and then you can use an RRF instruction ("rotate right by 1 bit with carry" to reduce it back to 8 bits before using a normal shift for the rest of the bits (where normal shifts don't seem to exist in PIC assembly - I guess "shift right by N" becomes a "mask N-1 low bits, clear carry, then do N rotates with carry that's never set". You don't need any 16-bit maths (if x is 8-bit).

Brendan
  • 35,656
  • 2
  • 39
  • 66