64-bit integer math (PASM)
ManAtWork
Posts: 2,262
I found out that sometimes it's easier and more efficient (code size) to use 64 bit math instead of trying to fit everything into 32 bits with lots of shift operations. In this thread I'd like to share my solutions for simple math routines in assembler. Suggestions for improvements/optimisations are welcome.
64 bit add and subtract are easy and are documeted in the propeller manual.
If you have to add a signed 32 bit value to a 64 bit long you can use this:
However, abs and neg are a bit more difficult.
64 bit add and subtract are easy and are documeted in the propeller manual.
' unsigned x:= x+y (or also signed if flags don't care)
add xl,yl wz,wc
addx xh,yh wz
' signed
add xl,yl wz,wc
addsx xh,yh wz
' unsigned x:= x-y
sub xl,yl wc,wz
subx xh,yh wz ' signed: use subsx
If you have to add a signed 32 bit value to a 64 bit long you can use this:
move temp,y
sar temp,#31 ' sign extend
add xl,y wc
addx xh,temp
However, abs and neg are a bit more difficult.
abs xh,xh wc,wz
if_c neg xl,xl wz
if_c_and_nz sub xh,#1
if_nc_and_z mov xl,xl wz ' test for Z-flag (optional)
neg xl,xl wz
neg xh,xh wc
if_nz sub xh,#1
if_z mov xh,xh wz ' test for Z-flag (optional)

Comments
mov count,#32 shl xl,#1 wc rcl xh,#1 divloop cmpsub xh,y wc rcl xl,#1 wc rcl xh,#1 djnz count,#divloopIt is assumed that xh[31..30] and y[31] are 0. This is no problem in my case because the input has a limited range and the sign bits are always vleared. Does anybody have a division routine without this restrictions?Cool.
One 64 bit lib that is nice to have, that can extend the use of 32 bit, is the classic scaling algorithm
R = (A *
Here A * B => 64 bit results, and then 64/32(C) applies to that result. The 64 bit value is important, but hidden from the user.
and another is Binary to BCD, see the Prop2 thread for some examples.
thanks
richard
{{ ////////////////////////////////////////////////////////////////////////////////// // 64 bit signed addition and subtraction methods // // Author : Joe Heinz // // Copyright : Joe Heinz - 2013 // // Grateful acknowledgement to kuroneko (parallax forum) // // data structure as follows : // // VAR long hostLong1, hostLong2, nodeLong1, nodeLong2 // ////////////////////////////////////////////////////////////////////////////////// }} DAT '' mailboxes pin long ' local copy of control pin hPtr long ' local copy of host pointer nPtr long ' local copy of node pointer function byte ' local copy of function cog byte ' cog PASM object is running in PUB NULL ' not a top level object PUB Start(controlPin) '' Start math object in new cog {{ ////////////////////////////////////////////////////////////////////////////////// // controlPin is pin used to indicate there is a math operation to do // // returns cog ASM engine was run in or false if no cog available // ////////////////////////////////////////////////////////////////////////////////// }} pin := controlPin ' move controlPin to pin hPtr := |< pin ' decode pin into hPtr outa[pin] := 0 ' make pin low dira[pin] := 1 ' make pin an output cog := cognew(@entry, @pin) +1 ' start asm cog return cog ' return cog PUB Stop '' Stop Math Cog if cog cogstop(cog~~ - 1) PUB Addition(hostPtr, nodePtr) '' Perform 64 bit subtraction {{ ////////////////////////////////////////////////////////////////////////////////// // hostPtr is pointer to least significant long of host time // // nodePtr is pointer to least significant long of node time // // result of addition is stored in host longs // ////////////////////////////////////////////////////////////////////////////////// }} DoMath(hostPtr, nodePtr, "a") PUB Subtraction(hostPtr, nodePtr) '' Perform 64 bit subtraction {{ ////////////////////////////////////////////////////////////////////////////////// // hostPtr is pointer to least significant long of host time, minuend // // nodePtr is pointer to least significant long of node time, subtractend // // result of subtraction is stored in host longs // ////////////////////////////////////////////////////////////////////////////////// }} DoMath(hostPtr, nodePtr, "s") PUB multiply(hostPtr, nodePtr) '' Perform 64 bit subtraction {{ ////////////////////////////////////////////////////////////////////////////////// // hostPtr is pointer to least significant long of multiplicand // // nodePtr is pointer to multiplier // // result of multiplication is stored in host longs // ////////////////////////////////////////////////////////////////////////////////// }} DoMath(hostPtr, nodePtr, "m") PUB divide(hostPtr, nodePtr) '' Perform 64 bit subtraction {{ ////////////////////////////////////////////////////////////////////////////////// // hostPtr is pointer to least significant long of dividend // // nodePtr is pointer to divisor // // result of division is stored in host longs // // remainder is stored in nodelong1 // ////////////////////////////////////////////////////////////////////////////////// }} DoMath(hostPtr, nodePtr, "d") PRI DoMath(hoPtr, noPtr, func) '' do the math operation hPtr := hoPtr ' move pointers to mailboxes nPtr := noPtr ' move pointers to mailboxes function := func ' move pointers to mailboxes outa[pin] := 1 ' make pin high repeat while function ' wait for asm cog to finish outa[pin] := 0 ' make pin low DAT org entry mov address, par ' get first address of passing method, hPtr 4 3 rdlong cpin, par ' get pin from pointer add address, #4 ' add long offset rdword host_ptr, address ' read the address to get pointer to Least Significant long of host time 8 1*2 2 add address, #4 ' add a word offset to address, points to nPtr 4 3 rdword node_ptr, address ' read the address to get pointer to Least Significant long of node time 8 1*2 3 add address, #4 ' add a word offset to address, points to function 4 3 loop waitpne zero, cpin ' wait for pin to be high rdbyte funct, address ' read the function type *add or subtract* 8 1*2 4 rdlong host_long1, host_ptr ' get least significant long of host time 8 1*2 5 add host_ptr, #4 ' add long offset to host pointer 4 3 rdlong host_long2, host_ptr ' get most significant long of host time 8 1*2 6 rdlong node_long1, node_ptr ' get least significant long of node time 8 1*2 7 add node_ptr, #4 ' add long offset to node pointer 4 3 rdlong node_long2, node_ptr ' get most significant long of node time cmp funct, #"a" wz if_z call #addi cmp funct, #"s" wz if_z call #subt cmp funct, #"m" wz if_z call #mult cmp funct, #"d" wz if_z call #div write wrlong host_long2, host_ptr ' write most significant long of result 8 1*2 9 sub host_ptr, #4 ' subtract long offset from host pointer 4 3 wrlong host_long1, host_ptr ' write least significant long of result 8 1*2 10 wrlong node_long2, node_ptr ' write most significant long of result sub node_ptr, #4 ' subtract long offset from node pointer wrlong node_long1, node_ptr ' write least significant long of result wrlong zero, address ' clear function to return to caller 8 1*2 11 jmp #loop ' div cmp node_long1, zero wz ' check to see if divisor is zero if_z mov host_long1, zero ' and zero out result if_z mov host_long2, zero ' undefined if_z jmp #write ' write to buffers ' find most significant bit shift offset of divisor < mplace = 0 to 31 > mov mplace, #0 ' prepare MSB place findm rol node_long1, #1 wc ' rotate divisor left 1 place and check for overflow if_nc add mplace, #1 ' if no overflow, add 1 to MSB place if_nc jmp #findm ' repeat loop until overflow ror node_long1, #1 ' rotate back one mov mask, #1 ' prepare mask mov tmp, mplace ' move msb shift factor to tmp for itteration count shl mask, mplace ' move mask by msb shift factor div2 cmpsub host_long2, node_long1 wc ' subtract divsor from dividend and watch for subtraction muxc d2, mask ' mux c flag to d2 at mask place cmpsub tmp, #1 wz ' subtract 1 from shift factor watching for last division if_nz shr mask, #1 ' shift mask right by 1 if_nz shr node_long1, #1 ' shift divisor right by 1 if_nz jmp #div2 ' and do loop again shr mask, #1 ' shift mask right by 1 shr node_long1, #1 ' shift divisor right by 1 cmpsub host_long2, node_long1 wc ' subtract divsor from dividend and watch for subtraction muxc d2, mask ' mux c flag to d2 at mask place sub tmp, #1 wz ' subtract 1 from shift factor watching for last division mov tmp, #1 ' move 1 to tmp for muxc shl mask, #31 ' move mask to MSB mov temp, #32 ' move 32 to temp for itteration count div1 shl host_long2, #1 ' make room for next bit from LSL shl host_long1, #1 wc ' shift next bit from LSL watching for carry muxc host_long2, tmp ' mux carry into MSL cmpsub host_long2, node_long1 wc ' subtract divisor from MSL, watch for subtraction muxc d1, mask ' mux c flag to d1 sub temp, #1 wz ' subtract 1 from itteration count if_nz shr mask, #1 ' move mask to next place if_nz jmp #div1 ' do loop again mov node_long1, host_long2 ' move remainder in node_long1 mov node_long2, #0 ' move zero to node_long2 mov host_long2, d2 ' move d2 to host_long2 mov host_long1, d1 ' move d1 to host_long1 div_ret ret mult mov multiplier, node_long1 ' get multiplier from node_long1 and place in multiplier cmp multiplier, zero wz ' check for multiply by 0 if_z mov host_long1, zero ' if multiply by zero just zero host_long1 if_z mov host_long2, zero ' and zero host_long2 if_z jmp #write ' jump to write mov node_long1, host_long1 ' else copy longs for repeated addidion mov node_long2, host_long2 ' copy the next long sub multiplier, #1 ' subtract one from multiplier :loop call #addi ' jmp to add djnz multiplier, #:loop ' and loop mult_ret ret subt sub host_long1, node_long1 wc ' do first subtraction and write c flag 4 4 subsx host_long2, node_long2 ' do second subtraction subt_ret ret addi add host_long1, node_long1 wc ' do first addition and write c flag addsx host_long2, node_long2 ' do second addition addi_ret ret zero long 0 address res host_ptr res node_ptr res host_long1 res host_long2 res node_long1 res node_long2 res funct res p_cog res multiplier res mask res tmp res temp res mplace res d2 res d1 res cpin res {{ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ TERMS OF USE: MIT License │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ │files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ │modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ │is furnished to do so, subject to the following conditions: │ │ │ │The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ │ │ │THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ │WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ │COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ │ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }}