// Math32.c // Multi-bit math // Written for PCM C compiler by Custom Computer Services // Tab size = 2 // Copyright 1995-2004 by Canzona Technologies #ifndef __MATH32_C #define __MATH32_C /*********************************************************************** #define StackDataSize as size of stack parameters in bytes MathPtr must be externally initialized to lowest address of stack xxxPtr() parameters passed in MathSrcP, MathDstP, result in MathDstP xxxStk() parameters removed from stack, result left on stack MulPtr(), DivPtr() use scratch register NegPtr() uses 2 stack levels including call MulPtr(), DivPtr(), SLPtr(), SRPtr() use 2 stack levels including call MulsPtr(), DivsPtr(), use 3 stack levels including call Other xxxPtr() functions use 1 stack level including call xxxStk() functions use 1 stack level + xxxPtr() stack requirements No error checking for stack ************************************************************************/ // Numeric parameter size in bytes #ifndef StackDataSize #define StackDataSize 4 #endif // Carry status for shifts, add/sub result // Carry set for overflow (ADDWF result), or no underflow (SUBWF result) bit MathCarry; // Double precision products and dividends if set #ifdef dMathDouble bit MathDouble = 0; #endif // Math stack byte *MathPtr; byte *MathDstP; // xxxPtr() destination pointer byte *MathSrcP; // xxxPtr() source pointer #ifndef dMathTPtr #define dMathTPtr MathPtr #endif // Next stack address is scratch register for intermediate calculations #byte MulTPtr = dMathTPtr // Multiplication scratch register #byte DivTPtr = dMathTPtr // Division scratch register #inline // Save carry in MathCarry void SaveCarry() { MathCarry = 0; // Assume no carry if (status_c) MathCarry = 1; } #inline // Restore carry from MathCarry void RestoreCarry() { status_c = 0; if (MathCarry) status_c = 1; } void PushStk(byte *ptr) { byte MathCnt; // Byte count for calculations for (MathCnt = StackDataSize; MathCnt != 0; MathCnt--) { *MathPtr++ = *ptr++; } } void PushStk32(byte b1, byte b2, byte b3, byte b4) { *MathPtr++ = b1; *MathPtr++ = b2; *MathPtr++ = b3; *MathPtr++ = b4; } #define dPushStk32(v32) PushStk32(v32, (v32)>>8, (v32)>>16, (v32)>>24); // Push unsigned 16-bit value void PushStk16(byte low, byte hi) { byte MathCnt; // Byte count for calculations *MathPtr++ = low; *MathPtr++ = hi; #if StackDataSize > 2 for (MathCnt = StackDataSize-2; MathCnt != 0; MathCnt--) { *MathPtr++ = 0; } #endif } // Push unsigned long void PushStkLong(long val) { byte MathCnt; // Byte count for calculations *MathPtr++ = val & 0xff; *MathPtr++ = val >> 8; #if StackDataSize > 2 for (MathCnt = StackDataSize-2; MathCnt != 0; MathCnt--) { *MathPtr++ = 0; } #endif } // Push signed 8-bit value void PushStkS8(byte val) { byte MathCnt; // Byte count for calculations byte temp; *MathPtr++ = val; #if StackDataSize > 1 temp = 0; if (BIT_TEST(val, 7)) temp--; for (MathCnt = StackDataSize-1; MathCnt != 0; MathCnt--) { *MathPtr++ = temp; } #endif } // Push signed 16-bit value void PushStkS16(byte low, byte hi) { byte MathCnt; // Byte count for calculations byte temp; *MathPtr++ = low; *MathPtr++ = hi; #if StackDataSize > 2 temp = 0; if (BIT_TEST(hi, 7)) temp--; for (MathCnt = StackDataSize-2; MathCnt != 0; MathCnt--) { *MathPtr++ = temp; } #endif } // Push signed long void PushStkSlong(long val) { byte MathCnt; // Byte count for calculations byte temp; *MathPtr++ = val & 0xff; *MathPtr++ = val >> 8; #if StackDataSize > 2 temp = 0; if (BIT_TEST(val, 15)) temp--; for (MathCnt = StackDataSize-2; MathCnt != 0; MathCnt--) { *MathPtr++ = temp; } #endif } #inline void DropStk() { MathPtr -= StackDataSize; } long PopLong() { DropStk(); return *((long*)MathPtr); } // MathCarry = sign of *MathDstP void signPtr() { MathCarry = 0; if (BIT_TEST((*(MathDstP+StackDataSize-1)),7)) MathCarry = 1; } // Check sign of 1st on stack void SignStk() { MathDstP = MathPtr - StackDataSize; signPtr(); } void PopStk(byte *ptr) { byte MathCnt; // Byte count for calculations ptr += StackDataSize; for (MathCnt = StackDataSize; MathCnt != 0; MathCnt--) { *--ptr = *--MathPtr; } } void PopStk16(byte *ptr) { ptr++; MathPtr -= (StackDataSize-2); *ptr-- = *--MathPtr; *ptr = *--MathPtr; } // Swap *MathDstP and *MathSrcP void SwapPtr() { byte MathCnt; // Byte count for calculations byte SwapDstP; // Swap scratch register for (MathCnt = StackDataSize; MathCnt != 0; MathCnt--) { SwapDstP = *MathDstP; *MathDstP++ = *MathSrcP; *MathSrcP++ = SwapDstP; } MathDstP -= StackDataSize; MathSrcP -= StackDataSize; } // Swap 1st and 2nd on stack void SwapStk() { MathDstP = MathPtr - StackDataSize; MathSrcP = MathPtr - 2*StackDataSize; SwapPtr(); } // *MathDstP = *MathSrcP void dupPtr() { byte MathCnt; // Byte count for calculations for (MathCnt = StackDataSize; MathCnt != 0; MathCnt--) { *MathDstP++ = *MathSrcP++; } MathDstP -= StackDataSize; MathSrcP -= StackDataSize; } // Duplicate 1st on stack void DupStk() { MathPtr += StackDataSize; MathDstP = MathPtr - StackDataSize; MathSrcP = MathPtr - 2*StackDataSize; dupPtr(); } // Copy 2nd on stack to next stack entry void OverStk() { MathPtr += StackDataSize; MathDstP = MathPtr - StackDataSize; MathSrcP = MathPtr - 3*StackDataSize; dupPtr(); } // Delete 2nd value on stack void Del2ndStk() { MathDstP = MathPtr - 2*StackDataSize; MathSrcP = MathPtr - StackDataSize; dupPtr(); // Copy 1st entry over 2nd entry DropStk(); } // Restore last value popped void RestoreStk() { MathPtr += StackDataSize; } // *MathDstP = ~*MathDstP void comPtr() { byte MathCnt; // Byte count for calculations MathCnt = StackDataSize; FSR = MathDstP; #asm com32a: comf INDF,f incf FSR,f decfsz MathCnt,f goto com32a #endasm } // Complement 1st on stack void ComStk() { MathDstP = MathPtr - StackDataSize; comPtr(); } // *MathDstP << 1 // bit 0 = MathCarry // MathCarry set according to high bit on exit void RLPtr() { byte MathCnt; // Byte count for calculations MathCnt = StackDataSize; FSR = MathDstP; RestoreCarry(); #asm rl32a: rlf INDF,f incf FSR,f decfsz MathCnt,f goto rl32a #endasm SaveCarry(); } // Rotate right 1st on stack // bit 0 = MathCarry // MathCarry set according to high bit on exit void RLStk() { MathDstP = MathPtr - StackDataSize; RLPtr(); } // *MathDstP << "shft" // bit 0 = 0 // MathCarry set according to high bit on exit void SLPtr(byte shft) { while (shft) { MathCarry = 0; RLPtr(); shft--; } } // Shift left 2nd on stack for count in 1st on stack // bit 0 = 0 // MathCarry set according to high bit on exit void SLStk() { DropStk(); MathDstP = MathPtr - StackDataSize; SLPtr(*MathPtr); } // *MathDstP >> 1 // High bit = MathCarry // MathCarry set from bit 0 on exit void RRPtr() { byte MathCnt; // Byte count for calculations MathCnt = StackDataSize; FSR = MathDstP+StackDataSize-1; // Start with uppermost byte RestoreCarry(); #asm rr32a: rrf INDF,f decf FSR,f decfsz MathCnt,f goto rr32a #endasm SaveCarry(); } // Rotate right 1st on stack // High bit = MathCarry // MathCarry set from bit 0 on exit void RRStk() { MathDstP = MathPtr - StackDataSize; RRPtr(); } // *MathDstP >> "shft" // High bit = MathCarry // MathCarry set from bit 0 on exit void SRPtr(byte shft) { while (shft) { if (MathCarry) { RRPtr(); MathCarry = 1; } else { RRPtr(); MathCarry = 0; } shft--; } } // Shift right 2nd on stack for count in 1st on stack // High bit = MathCarry // MathCarry set from bit 0 on exit void SRStk() { DropStk(); MathDstP = MathPtr - StackDataSize; SRPtr(*MathPtr); } // *MathDstP = *MathDstP + *MathSrcP // MathCarry set on exit if overflow void addPtr() { byte AddDstP; // Addition scratch register byte MathCnt; // Byte count for calculations byte SaveDstP, SaveSrcP; SaveDstP = MathDstP; SaveSrcP = MathSrcP; MathCnt = StackDataSize; #asm bcf STATUS,C // No overflow yet add32a: movf SaveSrcP,w incf SaveSrcP,f movwf FSR movf INDF,w movwf AddDstP // AddDstP = *SaveSrcP++ movf SaveDstP,w incf SaveDstP,f movwf FSR // FSR = SaveDstP++ movf AddDstP,w btfsc STATUS,C goto add32c // Carry clear add32b: addwf indf,f goto add32e add32c: // Carry set incfsz AddDstP,w // Add one more for carry goto add32b // No overflow from carry, just add // W = 0x100 - overflow from carry addwf indf,f bsf STATUS,C // Overflow from carry add32e: decfsz MathCnt,f goto add32a #endasm SaveCarry(); } // 2nd on stack += 1st on stack // 1st on stack dropped void AddStk() { MathDstP = MathPtr - 2*StackDataSize; MathSrcP = MathPtr - StackDataSize; addPtr(); DropStk(); } // *MathDstP = *MathDstP - *MathSrcP // MathCarry set on exit if no underflow void subPtr() { byte MathCnt; // Byte count for calculations byte SubDstP; // Subtraction scratch register byte SaveDstP, SaveSrcP; SaveDstP = MathDstP; SaveSrcP = MathSrcP; MathCnt = StackDataSize; status_c = 1; // No underflow yet #asm sub32a: movf SaveSrcP,w incf SaveSrcP,f movwf FSR movf INDF,w movwf SubDstP // SubDstP = *SaveSrcP++ movf SaveDstP,w incf SaveDstP,f movwf FSR // FSR = SaveDstP++ movf SubDstP,w btfss STATUS,C goto sub32c // Carry set - no underflow sub32b: subwf indf,f goto sub32d sub32c: // Carry clear - underflow from last time incfsz SubDstP,w // Subtract one more for carry goto sub32b // No underflow from carry, just subtract // W = 0x100 - underflow from carry subwf indf,f bcf STATUS,C // Underflow from carry sub32d: decfsz MathCnt,f goto sub32a #endasm SaveCarry(); } // *MathDstP = *MathDstP compared with *MathSrcP // MathCarry set on exit if no underflow void cmpPtr() { byte MathCnt; // Byte count for calculations byte SubDstP; // Subtraction scratch register byte SaveDstP, SaveSrcP; SaveDstP = MathDstP; SaveSrcP = MathSrcP; MathCnt = StackDataSize; status_c = 1; // No underflow yet #asm cmp32a: movf SaveSrcP,w incf SaveSrcP,f movwf FSR movf INDF,w movwf SubDstP // SubDstP = *SaveSrcP++ movf SaveDstP,w incf SaveDstP,f movwf FSR // FSR = SaveDstP++ movf SubDstP,w btfss STATUS,C goto cmp32c // Carry set - no underflow cmp32b: subwf indf,w goto cmp32d cmp32c: // Carry clear - underflow from last time incfsz SubDstP,w // Subtract one more for carry goto cmp32b // No underflow from carry, just subtract // W = 0x100 - underflow from carry subwf indf,w bcf STATUS,C // Underflow from carry cmp32d: decfsz MathCnt,f goto cmp32a #endasm SaveCarry(); } // 2nd on stack -= 1st on stack // 1st on stack dropped void SubStk() { MathDstP = MathPtr - 2*StackDataSize; MathSrcP = MathPtr - StackDataSize; subPtr(); DropStk(); } // 2nd on stack - 1st on stack // 2nd on stack not altered // 1st on stack dropped void CmpStk() { MathDstP = MathPtr - 2*StackDataSize; MathSrcP = MathPtr - StackDataSize; cmpPtr(); DropStk(); } // *MathDstP &= *MathSrcP void andPtr() { byte MathCnt; // Byte count for calculations byte AndDstP; // AND scratch register byte SaveDstP, SaveSrcP; SaveDstP = MathDstP; SaveSrcP = MathSrcP; MathCnt = StackDataSize; #asm and32a: movf SaveSrcP,w incf SaveSrcP,f movwf FSR movf INDF,w movwf AndDstP // AndDstP = *SaveSrcP++ movf SaveDstP,w incf SaveDstP,f movwf FSR // FSR = SaveDstP++ movf AndDstP,w andwf indf,f decfsz MathCnt,f goto and32a #endasm } // 2nd on stack &= 1st on stack // 1st on stack dropped void AndStk() { MathDstP = MathPtr - 2*StackDataSize; MathSrcP = MathPtr - StackDataSize; andPtr(); DropStk(); } // *MathDstP ++ void incPtr() { byte MathCnt; // Byte count for calculations MathCnt = StackDataSize; FSR = MathDstP; #asm inc32a: incf INDF,f // Overflow if incremented to 0 btfss STATUS,Z goto inc32b // Done if no overflow incf FSR,f decfsz MathCnt,f goto inc32a inc32b: #endasm } // Increment 1st on stack // MathCarry set according to result void IncStk() { MathDstP = MathPtr - StackDataSize; incPtr(); } // *MathDstP -- void decPtr() { byte MathCnt; // Byte count for calculations MathCnt = StackDataSize; FSR = MathDstP; #asm dec32a: decf INDF,f incf INDF,w // Underflow if original value was 0 btfss STATUS,Z goto dec32b // Done if no underflow (not Z) incf FSR,f decfsz MathCnt,f goto dec32a dec32b: #endasm } // Decrement 1st on stack // MathCarry set according to result void DecStk() { MathDstP = MathPtr - StackDataSize; decPtr(); } // *MathDstP = -(*MathDstP) void negPtr() { byte MathCnt; // Byte count for calculations MathCnt = StackDataSize; FSR = MathDstP; #asm com32a: comf INDF,f incf FSR,f decfsz MathCnt,f goto com32a #endasm FSR = MathDstP; #asm inc32a: incf INDF,f // Overflow if incremented to 0 btfss STATUS,Z goto inc32b // Done if no overflow incf FSR,f decfsz MathCnt,f goto inc32a inc32b: #endasm } // Negate 1st on stack void NegStk() { MathDstP = MathPtr - StackDataSize; negPtr(); } // Absolute value of *MathDstP // MathCarry set according to sign of original value void AbsPtr() { SignPtr(); if (MathCarry) { // Negative number negPtr(); } } // Absolute value of 1st on stack // MathCarry set according to sign of original value void AbsStk() { MathDstP = MathPtr - StackDataSize; AbsPtr(); } // Force value on stack to fit into 16 bits void StkMake16() { SignStk(); PushStkSLong(0x7fff); // Largest positive value // Look for 2nd on stack <= 1st on stack MathDstP = MathPtr - 2*StackDataSize; MathSrcP = MathPtr - StackDataSize; if (MathCarry) { // Smallest negative value comStk(); // Look for 1st on stack <= 2nd on stack MathSrcP = MathPtr - 2*StackDataSize; MathDstP = MathPtr - StackDataSize; } CmpPtr(); if (MathCarry) { // No underflow SwapStk(); } DropStk(); } // *MathDstP/1 = (*MathDstP) * (*MathSrcP) (unsigned, MSB of product in *MathSrcP) void mulPtr() { byte MulDstP; // Multiplication scratch register #1 byte MulSrcP; // Multiplication scratch register #2 byte MathCnt; // Byte count for calculations byte MulCnt; // Multiplication count register MulDstP = MathDstP; MulSrcP = MathSrcP; MathSrcP = MathDstP; MathDstP = MulTPtr; DupPtr(); // Copy 1st entry to scratch area MathDstP = MulDstP; for (MathCnt = StackDataSize; MathCnt != 0; MathCnt--) { *MathDstP++ = 0; } for (MulCnt = 8*StackDataSize; MulCnt != 0; MulCnt--) { MathDstP = MulDstP; MathCarry = 0; RLPtr(); MathDstP = MulSrcP; RLPtr(); if (MathCarry) { MathDstP = MulDstP; MathSrcP = MulTPtr; AddPtr(); if (MathCarry) { MathSrcP = MulDstP; incPtr(); } } } MathDstP = MulDstP; MathSrcP = MulSrcP; } // *MathDstP/1 = (*MathDstP) * (*MathSrcP) (signed, MSB of product in *MathSrcP) void mulsPtr() { byte MulDstP; // Multiplication scratch register #1 byte MulSrcP; // Multiplication scratch register #2 bit sign; // Sign of product MulDstP = MathDstP; MulSrcP = MathSrcP; signPtr(); sign = MathCarry; if (MathCarry) negPtr(); MathDstP = MulSrcP; signPtr(); if (MathCarry) { sign = !sign; negPtr(); } MathSrcP = MulSrcP; MathDstP = MulDstP; mulPtr(); if (sign) { MathDstP = MulDstP; comPtr(); MathDstP = MulSrcP; comPtr(); MathDstP = MulDstP; incPtr(); if (MathCarry) { MathDstP = MulSrcP; incPtr(); } } MathDstP = MulDstP; MathSrcP = MulSrcP; } void MulStk() { MathDstP = MathPtr - 2*StackDataSize; MathSrcP = MathPtr - StackDataSize; mulPtr(); #ifdef dMathDouble if (!MathDouble) #endif DropStk(); // Drop high order portion if single-precision } void MulsStk() { MathDstP = MathPtr - 2*StackDataSize; MathSrcP = MathPtr - StackDataSize; mulsPtr(); #ifdef dMathDouble if (!MathDouble) #endif DropStk(); // Drop high order portion if single-precision } // *MathDstP = (*MathDstP) / (*MathSrcP) (unsigned) // *MathSrcP = (*MathDstP) % (*MathSrcP) (unsigned) // Dividend and quotient are double-precision if MathDouble set void divPtr() { byte MathCnt; // Byte count for calculations byte DivDstP; // Division scratch register #1 byte DivSrcP; // Division scratch register #2 byte DivCnt; // Division count register DivDstP = MathDstP; DivSrcP = MathSrcP; MathDstP = DivTPtr; DupPtr(); // Copy divisor to scratch area MathDstP = DivSrcP; for (MathCnt = StackDataSize; MathCnt != 0; MathCnt--) { *MathDstP++ = 0; // Remainder } for ( DivCnt = #ifdef dMathDouble MathDouble ? 8*2*StackDataSize : #endif 8 * StackDataSize; DivCnt != 0; DivCnt-- ) { MathDstP = DivDstP; MathCarry = 0; RLPtr(); #ifdef dMathDouble if (MathDouble) { MathDstP = DivDstP+StackDataSize; RLPtr(); } #endif MathDstP = DivSrcP; RLPtr(); // Rotate dividend through remainder MathDstP = DivSrcP; MathSrcP = DivTPtr; if (!MathCarry) { // No overflow from shift, compare first cmpPtr(); MathDstP = DivSrcP; MathSrcP = DivTPtr; // Restore registers } if (MathCarry) { subPtr(); *DivDstP |= 1; } } MathDstP = DivDstP; MathSrcP = DivSrcP; } // *MathDstP = (*MathDstP) / (*MathSrcP) (signed) // *MathSrcP = (*MathDstP) % (*MathSrcP) (signed) // Dividend and quotient are double-precision if MathDouble set void divsPtr() { byte MathCnt; // Byte count for calculations byte DivDstP; // Division scratch register #1 byte DivSrcP; // Division scratch register #2 bit signq; // Sign of quotient bit signr; // Sign of remainder DivDstP = MathDstP; DivSrcP = MathSrcP; #ifdef dMathDouble if (MathDouble) MathDstP += StackDataSize; #endif signPtr(); signq = MathCarry; signr = MathCarry; if (MathCarry) { MathDstP = DivDstP; comPtr(); #ifdef dMathDouble if (MathDouble) { MathDstP = DivDstP+StackDataSize; comPtr(); } #endif MathDstP = DivDstP; incPtr(); #ifdef dMathDouble if (MathDouble) { if (MathCarry) { MathDstP = DivDstP+StackDataSize; incPtr(); } } #endif } MathDstP = DivSrcP; signPtr(); if (MathCarry) { signq = !signq; negPtr(); } MathSrcP = DivSrcP; MathDstP = DivDstP; divPtr(); if (signq) { MathDstP = DivDstP; comPtr(); #ifdef dMathDouble if (MathDouble) { MathDstP = DivDstP+StackDataSize; comPtr(); } #endif MathDstP = DivDstP; incPtr(); #ifdef dMathDouble if (MathDouble) { if (MathCarry) { MathDstP = DivDstP+StackDataSize; incPtr(); } } #endif } if (signr) { // Sign of remainder is sign of dividend MathDstP = DivSrcP; negPtr(); } MathDstP = DivDstP; MathSrcP = DivSrcP; } // 1st = 2nd on stack / 1st on stack (unsigned) void DivStk() { MathDstP = #ifdef dMathDouble MathDouble ? MathPtr-3*StackDataSize : #endif MathPtr-2*StackDataSize; MathSrcP = MathPtr - StackDataSize; divPtr(); DropStk(); } // 1st on stack = 2nd on stack / 1st on stack (signed) void DivSStk() { MathDstP = #ifdef dMathDouble MathDouble ? MathPtr-3*StackDataSize : #endif MathPtr-2*StackDataSize; MathSrcP = MathPtr - StackDataSize; divsPtr(); DropStk(); } // 2nd on stack = 2nd on stack / 1st on stack (unsigned) // 1st on stack = 2nd on stack % 1st on stack (unsigned) void DivRStk() { MathDstP = #ifdef dMathDouble MathDouble ? MathPtr-3*StackDataSize : #endif MathPtr-2*StackDataSize; MathSrcP = MathPtr - StackDataSize; divPtr(); } // 2nd on stack = 2nd on stack / 1st on stack (signed) // 1st on stack = 2nd on stack % 1st on stack (signed) void DivSRStk() { MathDstP = #ifdef dMathDouble MathDouble ? MathPtr-3*StackDataSize : #endif MathPtr-2*StackDataSize; MathSrcP = MathPtr - StackDataSize; divsPtr(); } #endif //#ifndef __MATH32_C